Github Actions + Linode를 이용한 컨테이너 CI/CD 파이프라인 구성

백준호·2024년 3월 1일

Linode에는 AWS CodeSeries와 같은 매니지드 CI, CD 서비스가 존재하지 않는다. 따라서 CI/CD 환경을 구성하기 위해서는 Github Actions, Jenkins 등의 도구를 이용해서 직접 CI/CD 파이프라인을 구성해야한다. 이 글에서는 Github Actions를 이용하여 컨테이너 애플리케이션 CI/CD 파이프라인을 구성한다.

CI/CD 플로우

  • 개발자는 대상 레포지토리에 변경된 코드를 푸시한다.
  • push 이벤트를 통해 트리거된 Github Actions workflow가 실행된다.
  • 첫번째로 docker 이미지를 빌드하고 docker hub에 이미지를 푸시한다.
  • 이후 SSH 통신을 이용하여 Linode 가상머신에 접속한 후 컨테이너 이미지를 다운받고 실행한다.

배포할 클라우드 인프라 구성

컨테이너를 배포할 클라우드 인프라 구조는 위와 같으며 CI/CD 구성에 집중하기 위해 자세한 인프라 구조에 대한 설명은 생략한다.

terraform 인프라 코드

Github Workflows 파일

name: cicd
on:
  push:
    branches: ['master']
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v3.3.0

      - name: set up QEMU
        uses: docker/setup-qemu-action@v1

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_IMAGE_NAME }}
          build-args: |
            db_host=${{ secrets.DB_HOST }}
            db_name=${{ secrets.DB_NAME }}
            db_user=${{ secrets.DB_USERNAME }}
            db_password=${{ secrets.DB_PASSWORD }}
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3.3.0

      - name: execute remote ssh
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_SSH_HOSTS }}
          username: root
          password: ${{ secrets.REMOTE_SSH_PASSWORD }}
          port: 22
          script: |
            docker pull ${{ secrets.DOCKER_IMAGE_NAME }}
            docker stop $${{ secrets.DOCKER_IMAGE_NAME }} || true && docker rm $${{ secrets.DOCKER_IMAGE_NAME }} || true
            docker run -d -p 3000:9090 ${{ secrets.DOCKER_IMAGE_NAME }} --name $${{ secrets.DOCKER_IMAGE_NAME }}
            docker image prune -a -f

CI/CD 파이프라인 구성을 위한 Github Actions 파일이다. 크게 build, deploy stage로 나눈다.

Build Stage

  • docker hub에 이미지를 푸시하기 위하여 로그인을 진행한다.
  • 로그인 후 도커 이미지를 빌드하고 docker hub에 푸시한다.

Deploy Stage

  • Linode 가상머신에 SSH 통신을 이용하여 도커 이미지를 pull 받는다.
  • pull 받은 이미지를 실행한다.

Github Secrets

  • DOCKER_USERNAME, DOCKER_PASSWORD: docker hub 로그인을 위한 계정 정보
  • DOCKER_IMAGE_NAME: docker hub에 등록된 이미지 이름. 편의를 위해 컨테이너 이름도 이미지 이름으로 설정한다.
  • DB_HOST, DB_NAME, DB_USERNAME, DB_PASSWORD → 컨테이너 애플리케이션에서 필요한 환경변수 정보. 컨테이너 애플리케이션에서 데이터베이스 연결까지 테스트하기 위해 DB 접속 정보를 추가하였다.

Dockerfile

FROM openjdk:17-jdk-slim as builder

COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN ./gradlew bootJar

FROM openjdk:17-jdk-slim

WORKDIR /app

COPY --from=builder build/libs/*.jar app.jar

ARG db_host
ARG db_name
ARG db_user
ARG db_password

ENV DB_HOST=$db_host
ENV DB_NAME=$db_name
ENV DB_USERNAME=$db_user
ENV DB_PASSWORD=$db_password

EXPOSE 9090
ENTRYPOINT java -jar app.jar
  • Spring Boot 애플리케이션을 이용하여 컨테이너를 배포한다.
  • 이미지 용량을 줄이기 위해 multi stage build를 사용한다.
  • build stage에서 애플리케이션 빌드 후 최종 stage에서는 실행에 필요한 파일만 layer에 두고 이미지를 구성하기 때문에 빌드-실행 단일 stage를 사용할 때보다 이미지 용량이 줄어든다.
  • 위에서 등록한 환경변수를 컨테이너 내부에 등록한다.

전체 코드

https://github.com/junho100/cicd-test-template-server

실행 결과


코드를 푸시하자 자동으로 Github Actions workflow가 실행되고 컨테이너가 가상머신에 실행됨을 확인할 수 있다.

확장성 문제

간단한 Spring 애플리케이션 CI/CD 구성을 하는 것에는 문제가 없었지만 위 구성은 개발을 진행하고 운영을 하는데에 확장성 문제가 있다.

환경변수 관리 문제

백엔드 서버를 개발하다보면 환경변수로 관리가 필요한 민감한 정보가 존재한다(디비 정보, 토큰 secret key 등).

이러한 환경변수 정보를 Github Actions에서 등록하고 있는데, 환경변수를 추가할 때마다 Github Actions 설정 파일도 매번 바꿔줘야한다. 환경변수는 개발을 하며 많아질 수 있고 이는 Github Actions 설정파일 가독성을 떨어뜨릴 수 있다. 또한 환경변수 추가 후 설정파일 수정을 하지 않는 등의 휴먼 에러가 발생할 수 있다.

이를 해결하기 위해 application.yml 파일 자체를 gitignore한 후 private repository에 존재하는 application.yml을 불러와 배포하는 방법이 있다.

가상머신 확장성 문제

가상머신에 SSH 접속을 하는 부분을 보면 SSH 호스트 ip 리스트를 직접 지정해주고 있다. 배포를 하고 서비스를 운영하다보면 트래픽이 늘어 가상 머신 개수를 늘리거나 비용 최적화를 위해 가상머신 개수를 줄이는 등 호스트 ip는 변할 수 있다. 호스트 ip 리스트가 변경될 때마다 Github Actions 코드를 변경해야하는 불편함이 존재한다.

이를 해결하기 위해서 클라우드 매니지드 서비스를 사용할 수 있다. AWS CodeDeploy는 auto scaling group을 지정하거나 이름 필터를 기반으로 배포할 수 있기 때문에 가상머신 변경에 자유롭다.

다른 방법은 아예 CI/CD 도구가 가상머신을 모르게 하는 방법이 있다. ECS, Kubernetes 등 컨테이너 오케이스트레이션 도구를 이용하여 컨테이너 배포 명령을 내리고 컨테이너가 배치될 가상머신은 컨트롤 플레인에서 임의로 지정하는 방식이다.

관련 자료

ssh github actions

[Docker] Dockerfile ENV 명령어(환경변수 설정)

docker-build-pass-environment-variables

profile
회고하는 개발자

0개의 댓글