Github Actions + ALB + ArgoCD + EKS + HPA +Helm + Docker Hub + Slack을 활용한 무중단 자동 배포 환경 구축기(with Rolling)

이상훈·2023년 9월 14일
1

Project

목록 보기
3/6
post-thumbnail

쿠버네티스를 도입하게 된 이유

 CICD 및 인프라 구축과 함께 내가 맡은 도메인 개발이 끝나고 프론트엔드 작업 마무리까지 여유가 생겼다. 그래서 확장성을 고려하여 아키텍처를 개선해보려고 한다.

1. 기존 아키텍처

 EC2 서버 하나에 Blue 컨테이너 1개, Green 컨테이너 1개를 두고 Nginx로 로드밸런싱하였다. 보통 사이드 프로젝트에서 무중단 배포와 도커를 사용했다면 대부분 아래와 같은 아키텍처일 만큼 보편화된 구조다. 하지만 서비스 규모가 커져서 확장한다고 했을 때는 아래 아키텍처에서 수정을 많이 해야 한다. 먼저 Scale Up 방식을 생각해보자

참고 : Github Actions +S3 + CodeDeploy + Docker + Nginx + Slack을 활용한 CICD 구축(with Blue-Green 무중단 배포)


2. Scale up

 기존 EC2에 용량을 상황에 따라 t2.small -> t2.medium 이런 식으로 늘려주면 된다. 그리고 docker-compose scale을 통해 컨테이너 개수를 늘려주면 된다. 이 방식은 아래와 같은 장, 단점이 있다.

장점
1. 간편하다.

단점
1. EC2가 죽으면 전체 서비스가 다운된다.
2. 성능 향상에 한계가 있다.

하지만 아마 위와 같은 아키텍처를 실제 사용하는 팀은 없을 거다..단점이 너무나 크다. 말이 안 되는 아키텍처... 아키텍처를 설계할 때는 확장성을 고려해서 설계해야 하며 위와 같은 Scale up 방식보다는 Scale out 방식을 고려해야 한다.


3. Scale out

 AWS에서 지원하는 로드밸런서를 통해 Blue Group과 Green Group을 연결해준다. 그리고 각각의 그룹에 AutoScaling을 적용해 트래픽에 따라 EC2 수를 조절해주면 된다.

장점
1. 확장에 용이하다.
2. AutoScaling 덕분에 트래픽에 대한 대처가 유연하다.

단점
1. Blue-Green 무중단 배포를 사용해야 한다.

Blue-Green 배포 방식은 서버 자원을 2배로 사용하기 때문에 서버가 늘어나면 늘어날수록 비용이 커지게 된다. 사실 롤링 배포나 카나리 배포 관련해서 알아봤으나 레퍼런스가 거의 없고 컨테이너 오케스트레이션 도구 없이는 수동으로 관리를 해줘야 해서 까다롭다고 한다.


4. Kubernetes

 쿠버네티스는 컨테이너화된 애플리케이션을 배포, 관리, 확장할 때 수반되는 다수의 수동 프로세스를 자동화하는 오픈소스 컨테이너 오케스트레이션 플랫폼이다. 컨테이너 오케스트레이션 플랫폼으로 쿠버네티스, Docker Swarm, Apache Mesos 등이 있지만 단연 쿠버네티스가 가장 인기가 많다고 한다.

장점
1. 애플리케이션 배포 단순화 : Blue/Green 배포 외에도 Rolling, Canary 배포 방식을 손쉽게 가동할 수 있다.
2. 지속적인 상태 확인과 셀프 힐링 : 쿠버네티스는 배포된 애플리케이션의 구성 요소들을 지속적으로 모니터링해서 문제가 발생하면 자동으로 재구동 시켜줄 수 있다.
3. 오토스케일링 : 오토스케일링 기능을 지원해서 급격한 부하 증가에 따른 장애를 방지할 수 있다.

단점
1. 높은 러닝 커브
2. 비용(돈, 시간)

 위에 적어둔 거 말고도 쿠버네티스의 장점은 정말 많다.. 문제는 높은 러닝 커브와 비용인데 상대적으로 쿠버네티스를 공부할 시간적 여유가 있었고 금액 같은 경우에는 대략 달에 10만원 정도로 예상되긴 하지만 부담할 수 있었다. 무엇보다도 쿠버네티스가 너무나도 궁금했으며 강의만 듣기보다는 프로젝트에서 직접 구축해 운영해보는게 체화가 더 쉽고 효과적이라 생각했다. 그리고 요새 트랜드인 만큼 배워두면 훗날 MSA를 공부하거나 혹은 다른 개발자, 인프라 담당자분들과 소통하는데 이점이 있을 거라 판단했다. 이런 이유로 다른 팀원분들에게는 추후에 CICD 및 쿠버네티스 환경에 대해 설명 및 문서화를 약속드리고 도입하기로 했다.


EKS 환경 구축하기

쿠버네티스를 구축하는 방법은 주로 3가지로 요약할 수 있다.

  1. 자체 서버 환경에서 쿠버네티스 설치
  2. 클라우드 서버 인프라 위에 쿠버네티스 설치
  3. AKS(Azure), EKS(AWS), GKE(GCP)등의 클라우드의 Managed 쿠버네티스 사용

 노트북 사양이 Ram 8GB라 1번 방식은 불가능하고 처음 쿠버네티스에 입문하므로 1, 2번 방식은 설정에 다소 어려움이 예상되어 Managed 쿠버네티스 중 익숙한 AWS 환경의 EKS를 사용하기로 했다. Amazon에서 지원하는 EKS 관리형 서비스는 컨트롤 플레인을 직접 구성하지 않고서 k8s를 손쉽게 사용할 수 있도록 편리함을 제공한다. AWS에서 제공하는 VPC, ELB, IAM 등 특정 기능들을 같이 활용하고자 할 때 유용하다. 이제 EKS를 구축해보자.

1. EC2 인스턴스에 필요한 툴 설치하기

  1. EC2 인스턴스 하나를 생성하자(for EKS 관리)

  2. 생성한 EC2 인스턴스에 접속하자.

  3. EC2에 AWS CLI 관리 툴 설치하자.

    $ sudo apt-get install -y unzip
    $ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    $ unzip awscliv2.zip
    $ sudo ./aws/install

  4. EC2에 EKS 관리 툴 eksctl 설치하자.

    $ ARCH=amd64
    $ PLATFORM=$(uname -s)_$ARCH
    $ curl -sLO "https://github.com/eksctlio/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz"
    $ curl -sL "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_checksums.txt" | grep $PLATFORM | sha256sum --check
    $ tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz_
    $ sudo mv /tmp/eksctl /usr/local/bin

  5. EC2에 k8s 관리 툴 kubectl 설치하자

    $ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.27.1/2023-04-19/bin/linux/amd64/kubectl
    $ chmod +x ./kubectl
    $ mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$HOME/bin:$PATH
    $ echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc


2. IAM 생성 및 EC2에 등록

  1. AWS 사용자에서 "사용자 생성" 클릭하자.

  2. 사용자 이름 설정하자.

  3. 아래와 같이 AdministratorAccess 정책을 선택하고 "사용자 생성"을 클릭하자.

  4. access key, secret key 확인하자.

  5. EC2에 접속하자.

  6. AWS 사용자 계정을 등록하자.

    $ aws configure
    $ AWS Access Key ID [None]: access key
    $ AWS Secret Access Key [None]: secret key
    $ Default region name [None]: ap-northeast-2
    $ Default output format [None]: Enter

  7. AWS 사용자 계정 연결 성공했는지 확인하자.



3. EKS 클러스터 생성하기

  1. EC2에 접속해서 아래 명령어를 실행하자.(EKS 설치)

    필요에 따라 ec2 인스턴스 유형, worker 노드 수 등을 조절할 수 있다.

    $ eksctl create cluster \
    --name [클러스터 이름] \
    --region ap-northeast-2 \
    --with-oidc \
    --ssh-access \
    --ssh-public-key [EC2 pem 키 이름] \
    --nodes 2 \
    --node-type t2.small \
    --node-volume-size=20 \
    --managed

  2. 아래와 같은 화면이 나오고 대략 20분 정도 기다리면 EKS 설치가 완료된다.

  3. AWS EC2에 들어가 보면 아래와 같이 worker node가 2개 추가로 생성되었음을 확인할 수 있다.

  1. AWS EKS에 들어가 보면 클러스터 하나가 생성되었음을 확인할 수 있다.

  2. 편의를 위해 CLI 명령어 자동 완성기능을 추가하자.

    $ source <(kubectl completion bash)
    $ echo "source <(kubectl completion bash)" >> ~/.bashrc


ALB ingress controller 설치하기

 쿠버네티스 ingress 리소스를 사용해서 외부와 통신할 수 있도록 해보자. 나는 EKS 즉 AWS 환경에서 클러스터를 구축했으므로 편의상 ALB ingress controller를 사용하겠다. EKS 환경에서 ALB ingress controller를 설치하는 과정은 공식 문서를 참고하자

참고 : AWS Load Balancer Controller 추가 기능 설치

다음으로 ingress.yaml을 작성하자.

ingress-seniors.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "ingress-seniors"
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/load-balancer-name: seniors-load-balancer
    alb.ingress.kubernetes.io/target-type: instance
    alb.ingress.kubernetes.io/class: alb 
spec:
  ingressClassName: alb
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: "service-seniors"
            port:
              number: 80

ingress 리소스를 실행하면 aws에 로드밸런서가 자동으로 생성된다.


ArgoCD 설치하기

 ArgoCD는 GitOps 방식으로 관리되는 Manifest(yaml) 파일의 변경 사항을 감시하며, 현재 배포된 환경의 상태와 Github Manifest 파일에 정의된 상태를 동일하게 유지하는 역할을 수행한다.

"ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes."
즉 한마디로 ArgoCD는 쿠버네티스를 위한 CD(Continuous Delivery) 툴이라고 할 수 있다. 쿠버네티스의 구성 요소들을 관리 및 배포하기 위해서는 Manifest 파일을 구성하여 실행해야 하며, 이러한 파일들은 계속해서 변경되기 때문에 지속적인 관리가 필요하다. 이를 간편하게 Git으로 관리하는 방식이 바로 GitOps이고 GitOps를 실현하며 쿠버네티스에 배포까지 해주는 툴이 바로 ArgoCD이다.

ArgoCD를 설치해보자.

  1. 먼저 ArgoCD namespace를 생성하고 kubectl 명령을 통해 ArgoCD를 k8s cluster에 배포하자.

    $ kubectl create namespace argocd
    $ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

  2. ArgoCD CLI를 설치하자.

    $ sudo curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
    $ sudo chmod +x /usr/local/bin/argocd

  3. ArgoCD Server를 외부에서 접속할 수 있도록 Service의 type을 LoadBalancer로 변경

    kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

  4. External-IP 주소를 확인해서 브라우저로 접속하자.

    kubectl get svc -n argocd argocd-server

  5. ArgoCD의 PW를 아래 명령어로 확인하자. 참고로 ID는 admin이다.

    kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

  6. 로그인하면 아래와 같은 화면을 확인할 수 있다.


ArgoCD + Helm chart release하기

 Helm은 k8s에서 애플리케이션을 배포하기 위해 사용되는 대표적인 패키징 툴이다. 여기서 말하는 패키지는 k8s 리소스를 하나로 묶은 helm chart다. Helm chart는 yaml 파일의 묶음으로 이 묶음을 public 혹은 private registry에 push 해두고 helm 명령어를 활용해 helm chart를 설치하여 k8s 리소스를 배포할 수 있다. Helm 외에도 Kustomize같은 도구들이 있지만 나는 helm을 이용해서 애플리케이션을 배포 및 관리하겠다.

1. Helm chart 만들기

Helm chart를 하나 만들자.

$ helm create [name]

만든 helm chart 구조를 보자.

$ helm [name]/

위 파일들은 manifest file 들이다. 참고로 pod나 service에 대한 설정 들을 쿠버네티스에서는 manifest라고 하며 이를 적은 파일을 manifest file이라 부른다. 주로 yaml이나 json 형식으로 기재한다. 편의상 NOTES.txt, _helpers.tpl, hpa.yaml 같은 파일들을 전부 지워주자. 그리고 아래 manifest file 들로 내용을 수정해주자.

  • service-seniors.yaml
apiVersion: v1
kind: Service
metadata:
  name: "service-{{ .Release.Name }}"
spec:
  type: NodePort
  selector:
    name: "{{ .Chart.Name }}"
  ports:
  - port: 80
    targetPort: 8080
  • deployment-seniors.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: "deployment-{{ .Release.Name }}"
spec:
  strategy:
  replicas: "{{ .Values.replicaCount }}"
  selector:
    matchLabels:
      name: "{{ .Chart.Name }}"
  template:
    metadata:
      labels:
        name: "{{ .Chart.Name }}"
    spec:
      containers:
      - name: "{{ .Chart.Name }}"
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: "{{ .Values.image.pullPolicy }}"
  • ingress-seniors.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "ingress-{{ .Release.Name }}"
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/load-balancer-name: seniors-load-balancer
    alb.ingress.kubernetes.io/target-type: instance
    alb.ingress.kubernetes.io/class: alb 
spec:
  ingressClassName: alb
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: "service-{{ .Release.Name }}"
            port:
              number: 80
  • values.yaml
replicaCount: 2
image:
  repository: strangehoon2/seniors
  pullPolicy: Always
  tag: "sha-3a7c8a0"
service:
  type: NodePort

다시 chart 구조를 보자.

이제 helm chart 저장을 위한 git repository를 하나 만들자. 편의상 config repository라고 많이들 부르는 거 같다. 그리고 ubuntu 환경에 git을 설치하고 만든 github repository에 helm chart를 push 하자.


2. ArgoCD 배포하기

2.1 Github repository 연결하기

 먼저 ArgoCD에 접속해서 "setting" -> "repository" -> "connect repo"를 클릭하자. ArgoCD에서 helm 차트 release하는 방법은 2가지다.

  • helm 차트 저장소 직접 이용
  • git에 있는 helm 차트 이용

나는 2번째 방법을 선택했다. 따라서 TYPE을 git으로 지정하고 앞서 만든 helm 차트를 위한 git repository의 url을 입력하자.


2.2 애플리케이션 배포하기

 Github repository를 연결했으니 이제 애플리케이션을 배포해보자. ArgoCD Applications의 "NEW APP"를 클릭하자. 나는 아래와 같이 설정했다. "Sync policy"를 manual로 설정하면 앞서 연결한 config github repository의 manifest 파일들에 변경 사항이 발생해도 수동으로 Refresh, Sync를 통해 동기화해줘야 한다. 반면 Automatic으로 설정하면 일정 주기마다 config repository를 감시하다 변경 사항이 발생하면 자동으로 동기화해준다. 나는 편의상 "Sync policy"를 Automatic으로 설정했다.

Create 버튼을 클릭하면 새로운 app이 하나 만들어지고 app을 클릭하면 아래와 같이 release 현황들을 볼 수 있다.

이제 앞서 alb ingress controller를 설치하면서 자동으로 생성된 aws load balancer로 접속 테스트해보자. ingress 설정과 함께 argoCD 배포가 성공적으로 수행되었음을 확인할 수 있다.


ArgoCD Slack 알림 연동하기

ArgoCD에 정의한 애플리케이션이 어떤 상태에 있는지 전달받을 수 있도록 알림을 설정해보겠다. ArgoCD는 Email, GitHub, Slack, Grafana, Webhook, Telegram, Teams 등의 다양한 알림 채널을 지원하고 있으며 나는 Slack을 이용하겠다.

  1. 이전에 생성한 slack 애플리케이션의 Bot User OAuth Token을 확인하자.
  1. ArgoCD Notification 관련 패키지를 설치한다.

    $ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/release-1.0/manifests/install.yaml

  2. ArgoCD Trigger 설정을 설치한다. ArgoCD에서 관리하는 애플리케이션이 어떤 상황에서 어떤 메시지를 보내야 하는지를 정의하는 설정이다.

    $ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-notifications/release-1.0/catalog/install.yaml

  3. 다음 yaml 파일을 작성하고 실행해주자.

    $ kubectl apply -n argocd -f argocd-notifications-secret.yaml

    apiVersion: v1
    kind: Secret
    metadata:
     name: argocd-notifications-secret
    stringData:
     slack-token: [slack token]
  4. 이번엔 argocd-notifications-cm을 수정해주자.

    $ kubectl edit -n argocd cm argocd-notifications-cm

    아래와 같이 slack token을 넣어주자.

  5. 애플리케이션에 Notification Trigger를 설정해주자.

    • Sync가 성공했을 때 알림

      $ kubectl patch app [app name] -n argocd --type merge -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-succeeded.slack":"'$SLACK_CHANNEL_NAME'"}}}'

    • Sync가 실패했을 때 알림

      $ kubectl patch app [app name] -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-failed.slack":"$SLACK_CHANNEL_NAME"}}}' --type merge

    • Sync가 진행중일 때 알림

      $ kubectl patch app [app name] -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-running.slack":"$SLACK_CHANNEL_NAME"}}}' --type merge

    • Sync 상태가 Unknown일 때 알림

      kubectl patch app [app name] -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-status-unknown.slack":"$SLACK_CHANNEL_NAME"}}}' --type merge

    • Health가 Degrade 되었을 때 알림

      kubectl patch app [app name] -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-health-degraded.slack":"$SLACK_CHANNEL_NAME"}}}' --type merge

    • Deploy 되었을 때 알림

      kubectl patch app [app name] -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-deployed.slack":"$SLACK_CHANNEL_NAME"}}}' --type merge


도메인 + HTTPS 설정하기

 이전까지는 ALB의 Dns 주소를 통해서 서비스에 접속할 수 있었다. 이번에는 가비아에서 구입한 도메인을 연결하고 Https 설정을 해보겠다.

  1. 가비아 도메인을 AWS Route 53에서 호스팅하고 ACM 인증서를 발급받자.

    참고 : EC2 HTTPS로 연결하기 (1) - 도메인 구매하고 ACM 인증서 발급하기

  1. Route 53에서 "레코드 생성"을 클릭해 ALB 관련한 레코드 하나를 생성하자.

  1. ingress.yaml 파일에 아래 코드를 추가하자.
 alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
 alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
 alb.ingress.kubernetes.io/certificate-arn: [acm 인증서 arn]
   .
   .
   .
 spec:
  rules:
  - host: [도메인 이름]
  1. 현재 가동 중인 ingress를 삭제하고 다시 실행하자.

  2. url로 접속 테스트해보자.

    도메인 주소와 함께 Https 설정이 성공적으로 완료되었음을 확인할 수 있다.


Rolling 무중단 배포 구현하기

 Rolling 업데이트란 새 버전을 배포하면서, 새 버전 인스턴스를 하나씩 늘려가고 기존 버전의 인스턴스를 하나씩 줄여나가는 방식이다. 이러면 새 버전의 인스턴스로 트래픽이 이전되기 전까지 이전 버전과 새 버전의 인스턴스가 동시에 존재할 수 있다는 단점이 있지만, 많은 서버 자원을 확보하지 않아도 된다는 장점이 있다. deployment.yaml 파일에 아래 코드를 추가하자.

spec:
  strategy:
    rollingUpdate:
      maxSurge: 50%
      maxUnavailable: 50%
    type: RollingUpdate

maxSurge : 업데이트 중에 동시에 최대로 추가할 수 있는 파드의 수(새로운 버전의 파드를 미리 생성해야 하므로)
maxUnavailable : 업데이트 과정 중에서 동시에 사용 불가능한 상태가 될 수 있는 파드의 수

maxSurge와 maxUnavailable 둘 다 정수나 % 단위로 지정할 수 있지만, 둘 다 0이 될 수는 없다. 값을 정수로 지정하게 될 경우 실제로 업데이트되는 파드의 수가 지정되고 백분율(%)로 지정하게 되면 원하는 개수의 파드의 비율이 반올림되어 지정된다. 나는 인스턴스를 2개 배포할 예정이므로 값을 50%로 설정했다. 이럴 경우 아래와 같이 동작한다.

  1. 초기 상태 : 현재 실행 중인 파드(Pod)가 2개다.

  2. 배포 시작 : maxSurge 설정에 따라 pod 수의 50%, 즉 1개의 pod가 생성된다. 현재 기존 파드 2개와 새로운 파드 1개 총 3개의 pod가 실행 중이다.

  3. 교체 : 새로운 버전의 파드가 실행됐으니까 이전 버전의 파드가 종료된다. maxUnavailable 설정에 따라 최대 50% 즉 1개의 파드가 종료된다. 이전 버전의 파드가 종료될 때마다 새로운 버전의 파드가 실행된다.

이러한 과정을 반복하여 이전 버전의 파드가 모두 종료되고, 새로운 버전의 파드가 모두 실행 중인 상태가 된다.


HPA Autoscaling 운영하기

 쿠버네티스는 CPU 사용률 등을 체크하여 Pod의 개수를 Scaling 하는 기능이 있다. 이 기능을 HorizontalPodAutoscaler(HPA)라고 하며 지정한 메트릭을 Controller가 체크하여, 부하에 따라 필요한 Pod의 Replica 수가 되도록 자동으로 Pod 수를 늘리거나 줄여준다.

먼저 HPA Autoscaling을 운영하려면 Metrics-Server를 설치해야 한다.

$ git clone https://github.com/237summit/kubernetes-metrics-server.git
$ cd kubernetes-metrics-server
$ kubectl get pods -A

아래 명령어로 설치가 잘 되었는지 확인하자. Pod에 대한 리소스 사용량을 보여주는 명령어다.

$ kubectl top pods -A

이제 hpa.yaml을 작성하자.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: "hpa-{{ .Release.Name }}"
spec:
  maxReplicas: {{ .Values.maxReplicas }}
  minReplicas: {{ .Values.minReplicas }}
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: "deployment-{{ .Release.Name }}"
  targetCPUUtilizationPercentage: {{ .Values.targetCPUUtilizationPercentage }}

나는 프로젝트에서 maxReplicas = 5, minReplicas = 2, targetCPUUtilizationPercentage = 50으로 설정했다. 따라서 Pod의 CPU 사용률이 50%가 넘어가면 최소 2개에서 최대 5개로 Pod의 수를 Autoscaling 해준다.


github actions workflow 설정하기

 이제 github action의 workflow를 작성한다. 나는 develop 브랜치에 push시 build가 되고 main 브랜치에 push시 build & deploy가 되도록 workflow를 분리했다. 앞서 argoCD와 함께 CICD 플로우를 설명하자면 아래와 같다.

  1. application repository의 main 브랜치에 push 이벤트가 발생한다.
  2. dockerhub에 새로운 image를 푸시한다.
  3. config repository의 values.yaml 파일의 image tag 값이 변경된다.
  4. argoCD가 변경 사항을 추적하여 배포 작업(동기화)을 수행한다.
  5. rolling update가 수행된다.

build.yaml

name: Java CI with Gradle

on:
  push:
    branches: [ "develop" ]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v3

      # JDK를 17 버전으로 세팅
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      # Gradle 캐싱-> 빌드 속도 UP
      - name: Gradle caching
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # application.yml 파일 생성
      - name: make application.yaml
        run: |
          cd ./src/main/resources
          touch ./application.yml
          echo "${{ secrets.APPLICATION }}" >> ./application.yml
        shell: bash

      # Gradle로 빌드 실행
      - name: Build with Gradle
        run: ./gradlew bootJar

      # image로부터 Metadata 추출
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}
          tags: |
            type=sha

      # image 빌드 및 도커허브에 push
      - name: web docker build and push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_ACCESS_TOKEN }}
          docker build -t ${{ secrets.DOCKER_REPO }}/seniors:${{ steps.meta.outputs.version }} .
          docker push ${{ secrets.DOCKER_REPO }}/seniors:${{ steps.meta.outputs.version }}

      # 빌드 결과 Slack 알람 전송
      - name: Send Slack Alarms
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_CHANNEL: general
          SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
          SLACK_ICON: https://github.com/rtCamp.png?size=48
          SLACK_MESSAGE: 빌드 결과 => ${{ job.status }}
          SLACK_TITLE: 빌드 결과 알람
          SLACK_USERNAME: Notification-Bot
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        if: always()

deploy.yaml

name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v3

      # JDK를 17 버전으로 세팅
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      # Gradle 캐싱-> 빌드 속도 UP
      - name: Gradle caching
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # application.yml 파일 생성
      - name: make application.yaml
        run: |
          cd ./src/main/resources
          touch ./application.yml
          echo "${{ secrets.APPLICATION }}" > ./application.yml
        shell: bash

      # Gradle로 빌드 실행
      - name: Build with Gradle
        run: ./gradlew bootJar

      # image로부터 Metadata 추출
      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}
          tags: |
            type=sha

      # image 빌드 및 도커허브에 push
      - name: web docker build and push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_ACCESS_TOKEN }}
          docker build -t ${{ secrets.DOCKER_REPO }}/seniors:${{ steps.meta.outputs.version }} .
          docker push ${{ secrets.DOCKER_REPO }}/seniors:${{ steps.meta.outputs.version }}

      # manifest file 존재하는 Repository clone and checkout
      - name: Clone and Checkout to config Repository
        uses: actions/checkout@v3
        with:
          repository: ""/""
          token: ${{ secrets.HELMREPO }}

      # yq 사용해 yaml file edit
      - name: Change config repo values.yaml
        uses: mikefarah/yq@master
        with:
          cmd: yq -i '.image.tag = "${{ steps.meta.outputs.version }}"' seniors/values.yaml

      # config repo의 image version update
      - name: Pushes to config repository
        uses: cpina/github-action-push-to-another-repository@main
        env:
          API_TOKEN_GITHUB: ${{ secrets.HELMREPO }}
        with:
          source-directory: "."
          destination-github-username: " "
          destination-repository-name: " "
          user-email: " "
          target-branch: main
          commit-message: "Update Image version to ${{ steps.meta.outputs.version  }}"

아키텍쳐

CICD Workflow

  • Developer Flow

    • App Repository에 code 변경 사항 push
    • Github Actions가 변경 감지
    • 빌드 후, Docker Hub에 이미지 push
    • Slack에 빌드 성공 여부 알림 전송
    • Config Repository의 values.yaml의 image.tag 값 변경
    • ArgoCD가 Config Repository Sync
    • EKS에 배포
    • Slack에 배포 성공 여부 알림 전송
  • Operator Flow

    • Config Repository에 code 변경 사항 push
    • ArgoCD가 Config Repository Sync
    • EKS에 배포
    • Slack에 배포 성공 여부 알림 전송

EKS


참고

EKS 설치하기 : [따배클] AWS (Amazon Web Service) EKS를 이용하여 Kubernetes를 구축해보자! - 2
ArgoCD + Slack 연동 : [DevOps] ArgoCD Slack Notification 설정하기

profile
Problem Solving과 기술적 의사결정을 중요시합니다.

0개의 댓글