(24.08.13)[18주차] GitHub Actions 환경에서의 CI 테스트코드 이슈 트러블슈팅
프로젝트 작성하면서 테스트코드를 작성하면서 검증을 계속 진행하고 있었지만,
기획단이 없이 개발자들끼리 진행한 프로젝트로 인해서,
계속 기능에 대해 비지니스 로직의 업데이트 및 수정으로 검증 테스트코드 과정이 조금 딜레이가 된 상태였다.
따라서, GitHub Repository와 Develop 브랜치와 Main 브랜치에 Push 시, GitHub Actions 를 통한 테스트코드 실행의 CI 검증을 구축하려고 하면서 테스트코드에서의 예기치못한 예외 발생 이슈가 발생을 했고,
개인적으로 지금까지의 Java Spring Boot환경에서의 이슈와 다른 결이므로
트러블슈팅을 기록
GitHub Actions에서 CI 중에서의 테스트코드 예외발생
이슈
- 해당 관련해서 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 자동화 파이프라인에서 테스트코드 정상적으로 작동
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 인스턴스를 활용한 웹서버 배포에서도 호환성 있게 개발시 작성한 코드를 활용할 수 있었지만,
이를 인지못하고 프로젝트 시작시 로컬 환경에 세팅을 한 것이 아쉬운 결정으로 생각된다.
'Today I Learned' 카테고리의 다른 글
(24.08.28) 코딩테스트 알고리즘 정리(구현:누적합,유클리드,에라토스테네스의체, 구간탐색:이분탐색,슬라이딩윈도우, Greedy:동전교환 알고리즘) (4) | 2024.08.28 |
---|---|
(24.08.26) 코딩테스트 - 알고리즘 준비 (0) | 2024.08.26 |
(24.08.12)[18주차] AWS 의 CloudWatch를 통한 Monitoring & Logging (0) | 2024.08.12 |
(24.08.09)[17주차] WebSocket과 실시간 메시지 구조 (0) | 2024.08.09 |
(24.08.07)[17주차] Spring Batch와 스케쥴링 - 활용 및 정리 (0) | 2024.08.07 |