본문 바로가기
Today I Learned 2024. 8. 13.

(24.08.13)[18주차] GitHub Actions 환경에서의 CI 테스트코드 이슈 트러블슈팅

프로젝트 작성하면서 테스트코드를 작성하면서 검증을 계속 진행하고 있었지만,

기획단이 없이 개발자들끼리 진행한 프로젝트로 인해서,

계속 기능에 대해 비지니스 로직의 업데이트 및 수정으로 검증 테스트코드 과정이 조금 딜레이가 된 상태였다.

 

따라서, GitHub Repository와 Develop 브랜치와 Main 브랜치에 Push 시, GitHub Actions 를 통한 테스트코드 실행의 CI 검증을 구축하려고 하면서 테스트코드에서의 예기치못한 예외 발생 이슈가 발생을 했고,

 

개인적으로 지금까지의 Java Spring Boot환경에서의 이슈와 다른 결이므로

트러블슈팅을 기록

 


 

GitHub Actions에서 CI 중에서의 테스트코드 예외발생

이슈

GitHub Actions에서 Gradle Build Step에서의 에외발생으로 인한 FAIL

  • 해당 관련해서 GitHub Actions 중 Step중에서 Gradle에서의 Test 코드 실행을 하는 과정에서 DefaultCacheAwareContextLoaderDelegate 로인한 IllegalStateException, IOException 발생
  • 단, 팀원에서 각 Local 개발을 위해 테스트코드를 진행해도 정상적으로 PASS 된 상태이지만, GitHub Actions 에서만 해당 부분에서 발생을 한 것

원인분석

예외분석

  • IOException
    • GitHub Actions 시스템 내에서 어떠한 명령어를 입력하는 데에 있어서 발생하는 프로세스 중에서의 에외
  • IllegalStateException
    • Spring 환경에서 특정 Bean을 생성하는 과정에서 발생하는 예외

-> 즉, 테스트코드를 실행하는 과정에서 시스템에 직접적으로 명령어를 입력하여 Bean을 등록하는 어떠한 과정에서 예오가 순차적으로발생

 

원인예측

@Profile("test")
@Configuration
public class EmbeddedRedisConfig {

    @Value("${spring.data.redis.port}")
    int port;
    private RedisServer redisServer;

    @PostConstruct
    public void startRedis() throws IOException {
   		...
    }

    @PreDestroy
    public void stopRedis() {
        this.redisServer.stop();
    }
    
    private boolean isRedisRunning() throws IOException {
        return isRunning(executeGrepProcessCommand(port));
    }
    
    private Process executeGrepProcessCommand(int redisPort) throws IOException {
        String command = String.format("netstat -an | findstr LISTENING | findstr :%d", redisPort);
        String[] shell = {"cmd.exe", "/c", command};

        return Runtime.getRuntime().exec(shell);

    }
    
		...
  • Redis를 사용해서 실제 서비스에서 많은 양의 캐시 즉 데이터가 전송되기 때문에 직접적으로 Redis를 사용할 수는 없었기 때문에 Test 환경에서 임의로 EmbeddedRedis를 사용하기 위해 EmbeddedRedisConfig를 작성해서 test Profile을 사용을 하고 있던 상황
    • Redis 구축 담당자가 고안한 로직과 환경구축
  • 위에서의 로직에서는 executeGrepProcessCommend 메서드내에서 EmbeddedRedis를 사용할 때 Port충돌을 방지하기 위해 현재 사용하고 있는 EmbeddedRedis 포트를 확인하는 기능
    • Commmand를 사용해서 cmd.exe에 직접적으로 커멘트를 입력해서 그 결과에서 확인하고 있는 EmbbededRedis 를 확인하는 로직

-> 따라서, 이러한 로직에서의 Command 를 입력하는 과정에서 테스트코드에서의 예외가 발생한 것

 

원인분석

  • 팀원 전체의 개발 환경이 Redis가 설치가 되어있고 이에 따라 EmbeddedRedis를 활용하는 환경은 Windows OS이기 때문에 cmd.exe 를 사용해서 현재 사용하는 Redis의 Port를 확인할 수 있었지만, GitHub Actions가 작동하는 환경은 Windows OS 가 아니라는 점을 확인
  • GitHub Actions가 작동되는 환경은 Docker Container 기반의 ubuntu Linux에서 실행되고 있기 때문에, 이를 감안하여 ubuntu Linux 를 통해 Command와 이를 입력할 환경을 GitHub Actions 에 맞게 변경이 필요

 

해결

private Process executeGrepProcessCommand(int redisPort) throws IOException {
      String command = String.format("netstat -nat | grep LISTEN|grep %d", redisPort);
      String[] shell = {"/bin/sh", "-c", command};
      
      return Runtime.getRuntime().exec(shell);
    }
  • 위와 같이 Unix-like 종류의 ubuntu(Linux)언어의 shell언어를 직접 사용할 수 있도록 하기 위와 같이 command와 shell을 바꾸도록 변경

-> 그렇다면, Windows환경에서의 테스트코드 작동을 확인할 때마다 Command와 Shell을 바꿔서 exec 해야하는 문제가 발생

 

작동환경에 따른 Command, Shell 변경 로직

 private Process executeGrepProcessCommand(int redisPort) throws IOException {

    String githubActions = System.getenv("GITHUB_ACTIONS");

    if ("true".equals(githubActions)) {
      String command = String.format("netstat -nat | grep LISTEN|grep %d", redisPort);
      String[] shell = {"/bin/sh", "-c", command};
      return Runtime.getRuntime().exec(shell);

    } else {
      String command = String.format("netstat -an | findstr LISTENING | findstr :%d", redisPort);
      String[] shell = {"cmd.exe", "/c", command};
      return Runtime.getRuntime().exec(shell);
    }
  }
  • System의 getenv  메서드를 사용해서 해당 코드가 실행되는 System 환경에서의 env 환경변수를 Map<String, String> 형태로 반환이 되어 가져올 수 있음
    •  따라서 위의 getenv를 통해 가지고 오는 env 환경변수에서 "GITHUB_ACTIONS" 환경변수가 코드가 실행되는 환경이 GitHub Actions일 때, true 인점을 활용

-> 즉, GitHub Actions환경일 때만,  unix Command를 활용하는 코드가 작동하면서 CI 자동화 파이프라인에서 테스트코드 정상적으로 작동

 

정상적으로 Test가 Gradle에서 실행이 되고, Run Tests 스텝에서 정상적으로 Task가 수행한 GitHub Actions

 

GitHub Actions가 Docker Container로 작동하는 것으로 알고 있었으나,

EC2 인스턴스를 세팅을 할 때만 utbuntu Linux 기반의 Command를 활용을 했지, GitHub Actions 컨테이너 환경에서 맞춰서 작동될 수 있도록 코드를 짜야하는 것을 잊고 있었던 점이 있다.

 

동시에, unix 기반 OS 환경이 아닌 환경(사실상 Windows) 일경우 작동하는 것을 System.getenv를 통해서 조건문을 제작할 수 있었지만,

더 나아가서 그렇다면 Windows가 아닌 MacOS 환경이나 또다른 코드가 작동하는 환경일 경우 전부 로직을 늘릴 수 밖에 없을 것이다. 

 

그랬기 때문에 로컬 개발 환경에서도 Docker Container 에서 Redis, MySQL DB을 구축해서 개발을 해야, 이에 맞춰서 GitHub Actions와 환경통일, 더 나아가서 AWS EC2 인스턴스를 활용한 웹서버 배포에서도 호환성 있게 개발시 작성한 코드를 활용할 수 있었지만,

이를 인지못하고 프로젝트 시작시 로컬 환경에 세팅을 한 것이 아쉬운 결정으로 생각된다.