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가 구성된 환경과 동작 방식에 대한 이해가 부족했다.