ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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도 구현하게 된다면 해당 부분도 추가로 글을 작성하도록 하겠습니다.

    혹시나 더 좋은 방법이 있다면 언제든지 댓글 남겨주시면 감사하겠습니다.

     

     

     

     

    댓글

Designed by Tistory.