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


컨테이너를 배포할 클라우드 인프라 구조는 위와 같으며 CI/CD 구성에 집중하기 위해 자세한 인프라 구조에 대한 설명은 생략한다.
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로 나눈다.
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
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 등 컨테이너 오케이스트레이션 도구를 이용하여 컨테이너 배포 명령을 내리고 컨테이너가 배치될 가상머신은 컨트롤 플레인에서 임의로 지정하는 방식이다.