GitLab CI와 Springboot를 연동하여 테스트 및 SonarQube를 통한 코드 분석 자동화 파이프라인 구성 시 발생한 트러블 슈팅에 관련한 내용입니다.
해당 트러블 슈팅 기록은 Springboot와 GitLab에 한정하여 발생하는 트러블이므로 GitHub 및 기타 프로그래밍 언어, 프레임워크에 대한 해당사항은 없습니다.
GitLab CI와 Springboot 연동에 대한 정보가 없는 경우 이해가 힘들 수 있으니 여기를 먼저 확인해주시기 바랍니다.
git push
이벤트가 발생하여 파이프라인이 동작하게되면 위 아키텍처에 따라 동작합니다. Springboot에서 단위 테스트를 할 경우 일반적으로 애플리케이션 설정 정보가 불필요하지만 DB 등 외부 인스턴스와 통신을 하게되면 통합 테스트로 작성하게 되어 애플리케이션 설정 정보가 필요하게됩니다.
이 상황에서 ubuntu
에 구성된 GitLab Runner
가 설정 정보인 application.yaml
을 인식하지 못해 아래와 같은 에러가 발생하게 되었습니다.
DemoApplicationTest > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143
Caused by: org.springframework.boot.context.config.ConfigDataResourceNotFoundException at ConfigDataResourceNotFoundException.java:97
application.yaml
을 제대로 로드하지 못하는 경우는 여러가지가 있지만 GitLab CI
연동 시 발생할 수 있는 원인은 아래와 같습니다.
@Value(“${env.test}“)
형식의 환경 변수가 GItLab CI 환경에 존재하지 않는 경우spring.profiles
가 불일치하는 경우spring.server.port
가 중복되는 경우이런 원인들은 GitLab
뿐만 아니라 GitHub
에서도 발생 할 수 있는 에러의 원인이기 때문에 반드시 확인을 해주셔야합니다.
이번 트러블 슈팅에는 위 원인과는 다른 GitLab
환경에서만 발생할 수 있는 원인인 환경변수에 설정된 application.yaml
파일이 정상적인 경로로 인식되지 않는 경우에 대해서 설명드리겠습니다.
먼저 GitLab
환경이 어떤 상태여서 이런 에러가 발생했는지 설명드리겠습니다.
ubuntu
이며, GitLab
, SonarQube
, other spring application
등이 구동 중spring application의 application.yaml
은 GitLab CI/CD > Variables
항목에 등록other spring application
은 개발 환경에서 .jar
로 빌드를 완료하고, GitLab
이 설치된 환경에서 환경 변수로 application.yaml
을 로드하여 실행되도록 구성etc/profile
내부에 SPRING_CONFIG_LOCATION=file://path/...
와 같이 설정 위치에 대한 환경 변수 등록위와 같은 상태가 GitLab Runner
가 동작할 때 어떤 영향을 받는지 알아보겠습니다.
GitLab Runner
는 기본적으로 시스템 환경 변수를 상속SPRING_CONFIG_LOCATION
이 상속된 환경 변수에 포함GitLab CI/CD > Variables
에 등록된 환경 변수를 무시하고, 시스템 환경 변수를 사용결론적으로 시스템 환경 변수 상속으로 인해 실제 애플리케이션의 설정 파일이 아닌 별도 서비스로 등록된 애플리케이션의 설정 파일을 로드하는 문제가 발생했습니다.
해결에서 시간이 지체되었던 가장 큰 이유는 로그를 너무 늦게 확인했던 점입니다. GitLab CI
가 반환하는 로그가 아닌 애플리케이션 실행 로그를 빠르게 확인했더라면 문제가 신속히 해결되었을겁니다.
# .gitlab-ci.yml
...생략
script:
- ./gradlew test -i
./gradlew test -i
명령어를 사용하면 애플리케이션이 구동되는 동안 출력되는 로그를 GitLab Pipline
에서 모두 확인할 수 있습니다.
환경 변수의 명칭이 GitLab Runner
가 인식할 수 있는 예약어를 사용하였기 때문에 이 부분을 애플리케이션 특성에 맞는 변수명으로 변경했습니다.
sudo vim /etc/profile
# 변경 전
export SPARING_CONFIG_LOCATION=...
# 변경 후
export [애플리케이션명]_CONFIG_LOCATION=...
:wq
# 적용
source /etc/profile
애플리케이션의 서비스 파일에서도 해당 내용을 수정해줍니다.
sudo vim /etc/systemd/system/[애플리케이션명].service
...생략
[Service]
User=...
Environment="[애플리케이션명]_CONFIG_LOCATION=..."
:wq
# 적용
sudo systemctl daemon-reload
시스템 환경 변수에 대한 설정을 모두 완료하였기 때문에 GitLab CI/CD > Variables
에 SPRING_APPLICATION_YML
등의 이름으로 설정 정보를 등록해줍니다.
환경 변수로 등록한 설정 파일을 .gitlab-ci.yml
파일에 동적으로 생성되도록 스크립트를 수정합니다.
... 생략
script:
- echo "$SPRING_APPLICATION_YML" > src/main/resources/application.yaml
- chmod +x ./gradlew
- ./gradlew clean test jacocoTestReport sonar
IDE
에서 인식하는 classpath
와 GitLab Runner
환경에서 인식하는 classpath
가 다를 수 있기에 위 방법으로도 해결되지 않는다면 classpath
에 대한 정보를 확인하는 것이 필요합니다.
스크립트에 아래 명령을 추가하시면 됩니다.
- echo "Classpath debug:"
- ./gradlew printClasspath
.gitlab-ci.yml
에 다음과 같이 해당 환경 변수 등록을 통해 GitLab Runner
가 인식하도록 설정합니다.
# .gitlab-ci.yml
variables:
[환경변수명]: [값]
또는 application.yaml
에 환경 변수가 없는 경우에 대비한 기본값을 추가합니다. 이 방식은 SqEL(Spring Expression Language)
와 환경 변수 대체 기능이 결합된 형식입니다.
# application.yaml
env:
test: ${ENV_TEST:[기본값]}
소스 코드에서 기본 값을 명시하고 방식은 위에서 설정한 방법과 동일하게 SqEL
을 사용합니다.
@Value("${[환경변수명]:[기본값]}"")
private String envValue;
또는 GitLab CI/CD > Variables
에 환경 변수를 미리 추가합니다.
.gitlab-ci.yml
파일에 프로파일에 대한 정보를 명시적으로 설정합니다.
# .gitlab-ci.yml
variables:
SPRING_PROFILES_ACTIVE: "test"
또는 gradle
을 통해 실행할 때 프로파일 정보를 지정해줍니다.
./gradlew test -Dspring.profiles.active=test
그 외 DB 접속 정보 불일치와 포트 중복의 경우는 별도로 해결 방법을 작성하지 않겠습니다.
이번 트러블 슈팅을 통해 얻은 교훈은 다음과 같습니다.
GitLab Runner
가 구성된 환경과 동작 방식에 대한 이해가 부족했다.