토이 프로젝트를 개발하고 배포할 때, JAR 파일(혹은 Node.js)을 빌드하고 Docker Image를 Docker Hub에 Push하고, Kubernetes Cluster에서 Docker Hub에서 Docker Image를 Pull 받아 Deployment를 생성하는 모든 작업을 수작업으로 진행했다.
./gradlew clean build
docker build --push --platform linux/amd64 -t [이미지 이름] .
kubectl apply -f [배포.yml]
등 등 ...
결론적으로 코드의 변경이 있으면, 운영 서버에 반영되기 까지 모든 작업은 수작업이고 MSA로 구성된 많은 Application를 관리하는 것은 귀찮고, 실수할 가능성도 존재했다.
나와 친구 개발자는 CI/CD 파이프라인을 구축해서 코드 통합, 배포 작업을 자동화하는 것을 목표로 했다.
대표적인 CI/CD Tool을 떠올리면 Jenkins, GitHub Actions가 있다.
필자는 GitHub Actions를 선택했는데, 이유는 Jenkins를 AWS Ubuntu 서버에서 사용하기 위해서는 4GB RAM 권장이 있어서 AWS 비용 절감을 목표로 했기 때문이다. (추가적으로 직관적인 workflows 구성도 한 몫 했다.)
Jenkins, GitHub Actions로 CD까지 가능하다.
하지만, Kubernetes를 사용하는 많이 개발자들은 Argo CD를 많이 사용하는 것을 찾아볼 수 있다.
그 이유가 궁금했다.
주요한 이유 중 하나는 Argo CD 공식문서를 통해 확인할 수 있었다.
What is Argo CD?
Argo CD is a declarative GitOps continous delivery tool for Kubernetes.
Why Argo CD?
Application definitions, configurations, and environments should be declarative and version controlled.
Application deployment and lifecycle management should be automated, auditable, and easy to understand
핵심은 Argo CD는 Kubernetes에 특화된 도구로 Desired State와 실제로 Kubernetes Cluster에 적용된 State를 모니터링하고 Sync를 맞춰주는 것이다. (+ 쉬운 작업)
환경 : Ubuntu 20.04 LTS, K3s(단일 노드)
CI : GitHub Actions
CD : Argo CD
위 CI/CD 도구로 파이프라인을 구축하는 것을 목표로 작업을 진행했다.
Spring application.yml 파일도 등록했다.
name: CI
on:
push:
branches:
- back-gateway # 트리거 브랜치
env:
IMAGE_NAME: gallery-gateway
jobs:
docker-build-and-deploy:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: ./backend/gateway
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: "adopt"
java-version: "11"
- name: Retrieve application properties
env:
APPLICATION_PROPERTIES: ${{ secrets.GATEWAY_APPLICATION_PROPERTIES }} # 시크릿에 등록시킨 application.yml 불러오기
run: |
mkdir ./src/main/resources
touch ./src/main/resources/application.yml
echo "${APPLICATION_PROPERTIES}" > ./src/main/resources/application.yml
- name: Build Spring Boot application
run: ./gradlew clean build -x test # JAR Build
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.BACK_DOCKER_USERNAME }} # 도커 허브 username
password: ${{ secrets.BACK_DOCKER_TOKEN }} # 도커 허브에서 생성한 토큰
- name: Build Docker image
run: docker build -t ${{ secrets.BACK_DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ github.sha }} . # Docker Image Build
- name: Push Docker image
run: docker push ${{ secrets.BACK_DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ github.sha }} # Docker Image Push
kustomize-update:
runs-on: ubuntu-latest
needs: docker-build-and-deploy
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Switch to gateway-argo branch
run: |
git fetch
git checkout gateway-argo
- name: Set Kustomize
uses: yokawasa/action-setup-kube-tools@v0.9.2
with:
kustomize: "3.7.0"
- name: Update kubernetes manifest
run: |
cd config/deploy/gateway/
kustomize edit set image ${{ secrets.BACK_DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
git config user.name "GitHub Actions"
git config user.email "<>"
git add .
git commit -m 'k8s manifest for ${{ github.sha }}'
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }} # 자동으로 생성되도록 함
branch: gateway-argo
Argo CD
Getting started를 그대로 따라하면 된다.
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
: 필자는 UI로 Argo CD를 작업하기 위해 core install이 아닌 stable manifests를 install 했다.
또한 외부에서 접속하기 위해서는 Load Balancer
를 사용하거나 Port-fowarding
이 필요하다.
필자는 바로 적용해보기 위해 후자를 선택했다.
kubectl port-forward --address=0.0.0.0 svc/argocd-server -n argocd 8080:80
--address
옵션을 지정하지 않으면, localhost 혹은 127.0.0.1 과 같은 기본적인 로컬 주소가 사용된다. 이 경우에는 포트 포워딩이 로컬 머신의 루프백 인터페이스에만 바인딩된다고 한다.
gateway.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway-dp
spec:
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
containers:
- image: chancehee/gallery-gateway
name: gateway
env:
- name: GALLERY_MEMBER_HOST
value: "member-svc:8001"
- name: REDIS_HOST
value: "redis-svc"
- name: REDIS_PORT
value: "6379"
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: gallery-secret
key: REDIS_PASSWORD
- name: GALLERY_GALLERY_HOST
value: "gallery-svc:8002"
- name: GALLERY_POST_HOST
value: "post-svc:8003"
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: gateway-svc
spec:
ports:
- port: 8000
selector:
app: gateway
kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gateway.yml # 위에서 정의한 파일 이름
images:
- name: chancehee/gallery-gateway
newTag: 6919d288ad46de5a0129f0883fb9bf3ed2b8c4a5 # GitHub Action에서 정의한 임의의 값 (SHA)
gateway 뿐만 아니라 여러 개의 파이프라인을 구축했다.
Desired State와 Kubernetes Cluster State의 Sync가 일치하는 것을 확인할 수 있다.