-
Github Actions CI 구성Project 2023. 8. 22. 22:51
이번 글에서는 초기 프로젝트 세팅을 하며 진행한 Github Actions의 CI 구성 작업 과정에 대한 내용을 작성하겠습니다.
이번에 세팅을 진행하며 가장 신경 쓴 부분이 바로 github의 secrets를 이용하여 application-prod.yaml 파일의 변수들을 환경변수로 주입시키는 부분이었습니다.
예전 프로젝트에서는 방법을 찾지 못하여 미리 작성한 application-prod.yaml 파일을 base64로 인코딩 시킨 값을 직접 github의 secrets으로 등록하여 진행했는데 이러한 과정에서 application-prod.yaml 파일이 변경이 생기는 경우 매번 인코딩과 secrets 수정을 진행하는 부분이 불편하다고 느꼈기 때문에 이번에는 직접 환경변수를 넣을 수 있는 방법으로 진행하게 되었습니다.
아래는 예시로 작성한 application-prod.yaml 파일입니다.
spring: kafka: bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS} producer: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer server: port: ${CRAWLER_PORT} custom: kafka: producer: is-enabled: true
위 파일에서 bootstrap-servers 변수와 port 변수의 값 주입을 환경변수를 통해 받도록 설정하였습니다.
앞으로 설명할 예시가 크롤러 어플리케이션이기 때문에 repository의 environments에 crawler라는 environment를 만들고 해당 부분에 필요한 secrets 들을 작성하였습니다.
다음으로는 Dockerfile입니다. 컨테이너 형태로 배포를 진행할 예정이기 때문에 미리 해당 어플리케이션에 맞는 Dockerfile을 작성하였습니다.
FROM openjdk:17-jdk WORKDIR /app ARG KAFKA_BOOTSTRAP_SERVERS ARG CRAWLER_PORT ENV active_profile=prod \\ KAFKA_BOOTSTRAP_SERVERS=$KAFKA_BOOTSTRAP_SERVERS \\ CRAWLER_PORT=$CRAWLER_PORT RUN chmod a+x /app COPY build/libs/*.jar crawler.jar ENTRYPOINT ["java", "-jar", "/app/crawler.jar", "--spring.profiles.active=${active_profile}"]
Dockerfile에서 가장 유심히 봐야하는 부분이 바로 ARG와 ENV 입니다. 각각의 키워드는 서로 사용할 수 있는 부분이 다릅니다.
아래 사진에서 볼 수 있듯이 ARG 로 정의한 변수는 이미지 build까지 사용가능하며 ENV 변수의 경우 build 뿐만 아니라 컨테이너 런타임에도 사용할 수 있습니다.
ARG 변수는 build 시 --build-arg 옵션을 통해 주입할 수 있으며 ENV 변수는 build 뿐만 아니라 image를 run 할때 -e 옵션을 통해 전달할 수 있습니다.
좀 더 자세한 내용은 아래 블로그를 참고하면 좋을것 같습니다.
Pass Docker Environment Variables During The Image Build
Pass Docker Environment Variables During The Image Build
Looking for a way to pass environment variables during your Docker image build? Here are multiple ways you can get environment variables into your images, and to your future Docker containers.
vsupalov.com
결국 위 블로그의 핵심은 ENV 변수는 build 시에 직접 세팅할 수 없기 때문에 ARG 변수를 통해 build 시에 변수를 github의 secrets를 이용해 세팅하고 설정한 ARG 변수를 ENV 변수로 매핑하여 run타임에서 사용하도록 하는 것입니다.
따라서 환경변수로 세팅했기 때문에 이후에 실제 만들어진 이미지를 실행시키고 컨테이너 내부에서 환경변수를 확인해보면 설정이 제대로 된 것을 알 수 있습니다.
위 방법을 토대로 github actions workflow 파일을 작성한 결과는 아래와 같습니다.
여러 steps 중에 Docker image build 부분을 보면 --build-arg 옵션을 통해 원하는 값들을 전달한 것을 확인할 수 있습니다.
name: LiveFeedCrawler Module CI on: push: branches: - main paths: - "LiveFeedCrawler/**" jobs: test-and-build: runs-on: ubuntu-latest environment: crawler env: REGISTRY: ${{ secrets.REGISTRY }} IMAGE_NAME: ${{ secrets.IMAGE_NAME }} USERNAME: ${{ secrets.USERNAME }} permissions: contents: read packages: write steps: - name: Checkout branch uses: actions/checkout@v3.5.2 - name: Set up JDK 17 uses: actions/setup-java@v3.11.0 with: distribution: 'zulu' java-version: '17' - name: Gradle Test run: ./gradlew :LiveFeedCrawler:test - name: Extract version variable run: | echo "APP_VERSION=$(./gradlew :LiveFeedCrawler:properties -q | grep version | awk -F ' ' '{ print $2 }')" >> $GITHUB_ENV - name: Execute Gradle build run: ./gradlew :LiveFeedCrawler:clean :LiveFeedCrawler:bootJar - name: Docker image build run: | cd ./LiveFeedCrawler docker build \ --build-arg KAFKA_BOOTSTRAP_SERVERS=${{ secrets.KAFKA_BOOTSTRAP_SERVERS }} \ --build-arg CRAWLER_PORT=${{ secrets.CRAWLER_PORT }} \ -t $REGISTRY/$IMAGE_NAME:$APP_VERSION . - name: Login to Container registry uses: docker/login-action@v2.1.0 with: registry: ${{ env.REGISTRY }} username: ${{ env.USERNAME }} password: ${{ secrets.PERSONAL_GITHUB_TOKEN }} - name: Docker image push to container registry run : docker push $REGISTRY/$IMAGE_NAME:$APP_VERSION
추가로 살펴볼 내용은 on.push.paths 부분입니다.
해당 프로젝트가 mono-repo / multi-module로 구성된 프로젝트기 때문에 브랜치가 병합될 때 모든 모듈이 빌드되면 낭비가 발생할거라 예상할 수 있기 때문에 특정 모듈에 변경이 일어났다면 해당 모듈만 github actions 과정을 거치도록 설정하였습니다.
마지막으로 살펴볼 내용이 위 yaml 파일에서 Extract version variable 파트입니다. 해당 부분의 역할은 build.gradle에 작성된 version 변수를 가져와 github actions 에서 사용할 수 있도록 환경변수로 만들어주는 역할을 합니다.
이후에 지속적으로 어플리케이션 버전이 올라갈텐데 그때마다 동일한 Docker image 태그를 사용하고 싶지 않고 build.gradle의 version 변수로 어플리케이션 이미지의 버전을 관리하고 싶었기 때문에 해당 단계에서 환경변수로 세팅하는 것을 확인할 수 있습니다.
참고로 ./gradlew properties 명령어를 통해 설정된 변수들을 추출할 수 있습니다. 이때 멀티 모듈 프로젝트였기 때문에 위에서 보듯이 LiveFeedCrawler 모듈의 properties 들만 가져오도록 설정했습니다.
지금까지 이번 프로젝트를 세팅하면서 어떻게 github actions를 통해 CI를 구성하였는지에 대한 과정이었습니다. 전문적으로 DevOps를 하는 개발자가 아니기 때문에 부정확한 내용이 있을수 있으니 참고로 보시면 좋을것 같습니다.
마지막으로 위의 방법을 통해 만들어진 도커 이미지를 만약 위에처럼 github의 private한 container registry가 아니라 docker hub와 같은 오픈된 곳으로 push 하게 되면 다른 사용자가 해당 이미지를 받아서 컨테이너를 실행시킨후 내부로 들어가 환경 변수를 전부 볼 수 있는 위험이 있습니다.
이번 프로젝트는 private 공간에 이미지를 push 했기 때문에 괜찮지만 만약 오픈된 곳으로 push한다면 위 방법보다는 다른 방법을 이용하는 것이 좋을것 같습니다.
개인적으로는 실제 배포하는 서버에 application-prod.yaml 파일을 작성하고 volume 설정으로 적용하는 방법도 있을것 같습니다.
하지만 해본게 아니라 추측이기 때문에, 이는 추후에 해볼일이 생긴다면 시도해보겠습니다.
이번 글에서는 CI에 관한 부분만 작성을 하였는데 CD도 구현하게 된다면 해당 부분도 추가로 글을 작성하도록 하겠습니다.
혹시나 더 좋은 방법이 있다면 언제든지 댓글 남겨주시면 감사하겠습니다.
'Project' 카테고리의 다른 글
mono-repo / multi-module 프로젝트에서 spring 자동 설정 이용하기 (0) 2023.08.22 AWS S3 이미지 저장 및 삭제와 DB로직 트랜잭션 분리 (0) 2023.07.27 사이드 프로젝트 백엔드 TB jenkins CI/CD 구축 (0) 2023.03.03