[CI/CD] EKS 에서 Jenkins, ArgoCD 를 활용한 파이프라인 구성

728x90

CI/CD 미니 프로젝트

목차

I. 과제 및 배경 소개

 

아키텍쳐

환경 구성

프로젝트 진행 일정

프로젝트 GitHub 주소

EKS와 K8S의 차이점

 

II. 구현 절차

 

1. EC2 인스턴스 구성

    1-1. EC2 인스턴스에 부여 IAM 역할 부여

    1-2. 보안 그룹 생성

    1-3. 인스턴스에 접속해 도구 설치

 

2. Repository의 manifest로 eksctl 클러스터 생성

    2-1. EC2 인스턴스 공개키 git 계정에 등록

    2-2. git clone으로 manifest 가져오기

    2-3. 파일 기반 eksctl 클러스터 생성

 

3. CI—Jenkins

    3-1. 기본 설정: 환경변수, 플러그인, 크레덴셜, 슬랙 알림

          1) Global Tool Configuration에서 환경변수 설정

          2) Plugin 설치

          3) Credentials 등록

          4) Slack 알림을 위한 젠킨스 CI 도구 설치

    3-2. 파이프라인 생성

          1) Jenkins Job 구성

          2) Jenkinsfile의 내용

          3) Jenkins CI-Slack Notification 실행 확인

 

4. CD—ArgoCD

    4-1. ArgoCD 설치

          1) aws-load-balancer-controller 설치

          2) ArgoCD 설치 확인 및 웹 UI 접속

    4-2. Git 저장소에서 바로 애플리케이션 배포하기

          1) Repository 연결

          2) ArgoCD Application 생성

          3) ArgoCD CLI

 

5.CI/CD 연동 확인—Jenkins & ArgoCD

 

6. 추가구성

    6-1. metrics-server

    6-2. Cluster Autoscaler

          1) 수동 스케일링

          2) 자동 스케일링

    6-3. CloudWatch Container Insight

 

7. Troubleshooting


I. 과제 및 배경 소개


본 프로젝트의 목표는 클라우드 인프라 오케스트레이션의 구현이다. AWS EKS 상에 클러스터를 구성하고, CI/CD 도구인 Jenkins와 ArgoCD를 주로 사용하여 개발부터 배포까지 자동적이고 유기적인 방식으로 이루어지도록 구성한다. 인프라 구성은 아키텍처에서 상술한다.

아키텍처

  1. 개발자가 깃허브에 코드를 push 한다.
  2. Jenkins를 GitHub에 연결하고, Jenkins는 GitHub repository에 commit 된 내용을 감지한다.
  3. Pipeline을 생성하고 GitHub에 위치한 Jenkinsfile 스크립트를 찾아 자동 실행되게 설정한다.(checkout → maven build)
  4. Dockerfile을 참조해 image를 빌드한다.
  5. Docker image를 Docker Hub에 push 한다.
  6. Jenkins 에서 manifest를 업데이트해 image tag를 수정한다.
  7. ArgoCD 에서 image tag 수정을 감지하고 싱크 한다.
  8. ArgoCD에서 docker image pull을 진행한다.
  9. ArgoCD에서 소스 코드에서 변경된 부분을 반영하여 업데이트하거나 파드를 재생성한다.

환경 구성

환경 구성은 위에서 보이는 아키텍쳐와 같다. 자세한 설명은 아래 순서를 참고하여 실행하며 알아보도록 하자.

프로젝트 GitHub 주소

https://github.com/seongwoo-choi/mini-cicd-project.git

EKS와 K8S의 차이점

EKS(Elastic Kubernetes Service)는 AWS에서 제공하는 '관리형 쿠버네티스' 이다.

쿠버네티스를 따로 구축하고 관리할 때는 마스터 노드 및 etcd를 이중화, 삼중화 하는것이 매우 중요한데, EKS에서는 마스터노드와 etcd 노드를 EKS가 관리해주고 Private Link로 연결되어 내부망 통신으로 안전하게 워커노드와 통신 할 수 있다.

1. AWS Managed

k8s는 Master Node와 Worker Node 둘다 관리해야 하지만 EKS를 사용하면 Master Node와 Worker Node 둘 다 AWS Managed에서 한번에 관리가 가능하다.

2. 클러스터 업그레이드 용이성

k8s에서 클러스터를 업그레이드 하려고 하면 매뉴얼과 절차대로 진행해야 한다.

반면 EKS를 사용하게 되면 AWS에서 클러스터 업그레이드를 클릭 한번으로 업데이트를 할 수 있다.

3. 네트워크 할당

쿠버네티스에서 파드를 생성하면 해당 파드는 클러스터 내에서만 볼 수 있게 가상 네트워크를 부여받는다.

네트워크 플러그인으로는 AWS CNI라는 플러그인이 사용되는데 해당 플러그인은 일반적인 CNI(Flannel, Callico)와 차이점이 있다.클러스터의 IP 대역을 VPC로 할당 받기 때문에 파드를 생성하면 VPC와 동일한 네트워크를 공유하게 된다.

즉, 파드는 클러스터를 생성하는 데 사용되는 서브넷의 IP를 사용하게 되고 Worker Node는 여러 네트워크 인터페이스(=ENI)로 파드(=오브젝트)의 IP를 관리한다.

4. 인증방식

EKS와 k8s와 다르게 EKS는 AWS IAM을 인증방식으로 사용해 사용자에게 역할을 할당시킨다.


II. 구현 절차


1. EC2 인스턴스 구성

Repository로 Docker Hub 또는 ECR 사용 가능, 본 프로젝트에서는 Docker Hub를 선택했다.

1-1. EC2 인스턴스에 IAM 역할 부여

EC2 인스턴스에서 eksctl 을 사용하여 클러스터를 생성하기 때문에 AdministratorAccess 정책을 부여한 역할이 필요하다.

AdministratorAccess 정책으로 EKS용 역할 생성하기

  • IAM 접속 → 액세스 관리 → 역할 → 역할 만들기

EC2 인스턴스에 역할을 부여할 것이므로 EC2 선택

AWS 관리에서 모든 권한을 줄 수 있는 AdministratorAccess 정책을 추가한다.

권한 추가 후 역할 생성 버튼 클릭하여 역할을 생성한다.

인스턴스의 IAM 역할 수정

  • 인스턴스 선택 → 작업 → 보안 → IAM 역할 수정

EC2 인스턴스에서 IAM 역할 수정 화면

1-2. 보안 그룹 생성

  • ssh 와 jenkins 에 접속하기 위한 8080 포트 추가
  • 접속 가능한 ip주소는 현재 인스턴스를 생성하고 실행할 컴퓨터인 나의 ip 주소를 사용

1-3. 인스턴스에 접속해 도구 설치

  • awscli, aws-iam-authenticator, kubectl, eksctl
  • Jenkins, Docker, Helm, ArgoCD 등
sudo hostname jenkins
sudo su - jenkins

awscli 설치

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

AWS configure

AWS 명령줄 인터페이스(AWS CLI)는 명령줄 셸에서 명령을 사용하여 AWS 서비스와 상호 작용할 수 있는 오픈 소스 도구다. AWS CLI를 사용함으로써 터미널의 명령 프롬프트에서 브라우저 기반인 aws 콘솔에서 제공하는 것과 동일한 기능을 구현하는 명령 실행을 시작할 수 있게 된다.

aws configure 명령어를 사용해 IAM에서 미리 생성해 둔 사용자로 aws 계정에 로그인이 가능하다.

  • IAM 사용자 생성: IAM → 액세스 관리 → 사용자 → 사용자 추가

사용자 이름 지정 후 자격 증명 유형을 선택한다. 여기서는 액세스 키를 이용하여 로그인을 할 것이기 때문에 액세스 키 로그인 방식만 선택한다.

AdministratorAccess 권한을 부여하여 인스턴스에서 작업 수행이 가능하도록 한다.

태그는 생략 후 IAM을 최종으로 생성하면 아래와 같은 메세지가 출력된다. “.csv 다운로드” 후 파일 안에 있는 액세스 키 ID와 비밀 액세스 키를 복사하여 aws configure 로그인 시 아래와 같이 입력한다.

순서대로 액세스 키 ID, 비밀 키, 사용할 리전, 그리고 아웃풋 포맷(보통 JSON으로 설정)을 입력한다.

aws sts get-caller-identity 명령어를 통해 로그인 된 ID 정보를 확인할 수 있다.

aws-load-balancer-controller

공식 문서: https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/network-load-balancing.html, https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/alb-ingress.html, https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/aws-load-balancer-controller.html

helm repo add eks <https://aws.github.io/eks-charts>
helm repo update
aws sts get-caller-identity # account 확인

--set image.repository=dkr.ecr.region-code.amazonaws.com/amazon/aws-load-balancer-controller

helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=***myeks-custom*** --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller --set image.repository=***602401143452***.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-load-balancer-controller

aws-iam-authentication 설치

curl -o aws-iam-authenticator https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/aws-iam-authenticator
chmod +x ./aws-iam-authenticator
mkdir -p $HOME/bin && cp ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator && export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc

aws-iam-authenticator help

앞에서 설정한 것 처럼, IAM 권한을 사용하여 AWS 서비스 및 리소스에 대한 액세스 권한을 지정하고 제어할 수 있다. IAM 역할에 권한을 부여하려면 액세스 유형, 수행할 수 있는 작업, 작업을 수행할 수 있는 리소스를 지정하는 정책을 연결하면 된다. IAM 정책을 사용하여 특정 AWS 서비스 API 및 리소스에 대한 액세스 권한을 부여할 수 있다. (여기서는 ec2 인스턴스에 권한을 부여하기 위해 설치하였다.)

curl 명령어로 aws-iam-authentication의 파일을 다운로드하고, **chmod**를 사용해 모두에게 실행 권한을 부여한다. 그리고 환경 변수를 설정하고 배쉬 설정 파일안에 세팅해준다. 마지막으로 help 명령어를 실행시켜 정상적으로 작동 여부를 확인 할 수 있다.

kubectl 1.22.8 설치

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
apt-cache madison kubectl | grep 1.22.8
sudo apt-get install kubectl=1.22.8-00 -y
sudo apt-mark hold kubectl

쿠버네티스 커맨드 라인 도구인 kubectl을 사용하면 쿠버네티스 클러스터에 대해 명령을 실행할 수 있다. kubectl 을 사용하여 애플리케이션을 배포하고, 클러스터 리소스를 검사 및 관리하고, 로그를 볼 수 있다.

여기서 사용할 버전은 1.22.8 버전이며 apt-get curl 명령어를 사용해 설치한다.

eksctl 설치

curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
eksctl version

eksctl은 Amazon EKS에서 Kubernetes 클러스터를 생성 및 관리하기 위한 명령줄 유틸리티다. eksctl은 Amazon EKS용 노드가 있는 새 클러스터를 가장 쉽고 간편하게 생성하는 방법이다.

eksctl 을 설치 수 있는 깃허브 파일을 curl 명령어로 다운받고, 파일의 위치를 /usr/local/bin 밑으로 이동시킨다. 설치 후 eksctl version 명령어를 사용해 버전 확인이 가능하다.

Jenkins 설치

sudo apt update

sudo apt-get install default-jdk -y
java -version

sudo apt install maven -y
mvn --version

wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list
sudo apt update
sudo apt install jenkins -y

sudo cat /var/lib/jenkins/secrets/initialAdminPassword # 젠킨스 접속시 사용되는 초기 암호 확인

Jenkins는 빌드와 테스트 등의 워크플로우에서 지속적인 통합(CI, Continous Integration) 서비스를 제공하는 툴이다. 반복되는 업무를 자동화 하기 위하여 구성된 도구로 한번의 설정으로 빌드를 자동화 시킬 수 있다.

Docker 설치

sudo apt install docker.io -y
sudo usermod -aG docker $USER
sudo systemctl start docker
sudo systemctl enable docker
sudo systemctl status docker

sudo usermod -aG docker jenkins
sudo service jenkins restart
sudo systemctl daemon-reload
sudo service docker stop
sudo service docker start

Docker 는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다. Docker는 소프트웨어를 컨테이너라는 표준화된 유닛으로 패키징하며, 이 컨테이너에는 라이브러리, 시스템 도구, 코드, 런타임 등 소프트웨어를 실행하는 데 필요한 모든 것이 포함되어 있다. 이 프로젝트 내에서는 이미지를 빌드하는 역할을 수행한다.

Helm CLI 설치

curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

Helm 은 Kubernetes 클러스터에서 좀 더 쉽게 애플리케이션을 설치하고 관리하는 프로그램이다. 로컬 시스템에서 Helm CLI를 사용하여 차트를 설치하고 관리할 수 있다. 여기서는 aws의 AWS Load Balancer Controller를 설치하기 위해 Helm Chart를 설치하였다.

ArgoCD CLI 설치

wget https://github.com/argoproj/argo-cd/releases/download/v2.2.5/argocd-linux-amd64 -O argocd
chmod +x argocd
sudo mv argocd /usr/local/bin

argocd는 Kubernetes를 위한 CD(Continuous Delivery)툴로, GitOps방식으로 관리되는 Manifest 파일의 변경사항을 감시하며, 현재 배포된 환경의 상태와 Git에 정의된 Manifest 상태를 동일하게 유지하는 역할을 수행 한다. 쉽게 말해 Jenkins 에서 빌드한 파일들을 자동으로 배포할 수 있게 만들고, 그 과정을 GUI 방식으로 볼 수 있도록 설계된 툴이다. 현재 프로젝트 내에서는 Jenkins를 CI로, argocd를 CD툴로 사용하였다.


2. Repository의 manifest로 eksctl 클러스터 생성

2-1. EC2 인스턴스 공개키 git 계정에 등록

sudo su - jenkins
ssh-keygen
cat .ssh/id_rsa.pub

  • github: Settings → SSH and GPG keys → New SSH Key → Key에 공개키(id_rsa.pub) 내용 입력

2-2. git clone으로 manifest 가져오기

깃 계정에 현재 인스턴스의 jenkins 호스트 공개키를 등록했기 때문에 ssh 방식으로 깃 클론이 가능하다. 아래와 같이 소스를 가져와 eksctl 명령어를 통해 클러스터를 생성한다.

$ sudo su - jenkins
$ git clone git@github.com:XXXXXX.git

2-3. 파일 기반 eksctl 클러스터 생성

$ cd mini-cicd-project
$ eksctl create cluster -f <파일명> 
# 15~20 분 정도 소요됨
$ kubectl get nodes

아래는 eksctl 클러스터를 생성할 때 사용한 파일의 각 부분이다.

우선 Cluster 환경을 설정하는 yaml 파일에 EKS 이름, 리전, k8s 버전, AZ 를 설정한다.

# project_myeks.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: project-myeks
  region: ap-northeast-2
  version: "1.22"

# AZ
availabilityZones: ["ap-northeast-2a", "ap-northeast-2b",  "ap-northeast-2c"]

아래 IAM 부분은 EKS 에서 사용할 애드온들에 대한 serviceAccounts 계정을 생성 하는 부분으로 IAM역할을 serviceAccounts 계정에 적절히 바인딩한다. eks 입장에서 AWS IAM 은 외부 인증 서버이기 때문에 OIDC 를 true 로 설정해야 한다. 자주 사용되는 애드온들의 경우 IAM역할들이 wellKnownPolicies 에 정의되어 있다. 각각의 애드온들은 alb, nlb 를 사용할 수 있게 하는 aws-load-balancer-controller, ebs 크기 변경 및 스냅샷을 찍을 수 있게 하는 ebs-csi-controller-sa, 클러스터 오토 스케일링이 가능하게 하는 cluster-autoscaler 이다.

# IAM OIDC & Service Account
iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: aws-load-balancer-controller
        namespace: kube-system
      wellKnownPolicies:
        awsLoadBalancerController: true
    - metadata:
        name: ebs-csi-controller-sa
        namespace: kube-system
      wellKnownPolicies:
        ebsCSIController: true
    - metadata:
        name: cluster-autoscaler
        namespace: kube-system
      wellKnownPolicies:
        autoScaler: true

다음은 워커 노드 그룹을 설정하는 부분으로 Production 용 노드만 만들었고 가용성을 높이기 위해 오토 스케일링을 사용한다. 또한 deploy 에서 생성되는 파드들이 해당 production 노드에 스케줄링되게 하기 위해서 labels: { role: production } 을 붙여주었으며, 노드 즉 EC2 인스턴스들이 모두 private subnet 에 배치되도록 설정했다. ssh 접속을 하기 위해 미리 키를 생성하여 인스턴스에 등록했다. 위에서 설정한 IAM 애드온들을 사용할 수 있도록 애드온 정책들을 true 로 설정했다.

# Managed Node Groups
managedNodeGroups:
  # On-Demand Instance
  - name: production
    instanceType: t2.medium
    minSize: 1
    desiredCapacity: 2
    labels: { role: production }
    maxSize: 3
    privateNetworking: true
    ssh:
      allow: true
      publicKeyPath: ./keypair/myssh.pub
    availabilityZones: ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
    iam:
      withAddonPolicies:
        autoScaler: true
        albIngress: true
        cloudWatch: true
        ebs: true

EKS 로 생성되는 클러스터의 경우 컨트롤 플레인을 AWS 에서 자체적으로 관리해주기 때문에 어떤 작업이 이뤄졌는지 로그를 확인하기 어렵다. 그래서 아래와 같이 CloudWatch 에서 로그를 확인할 수 있도록 cloudWatch를 설정한다.

# CloudWatch Logging
cloudWatch:
  clusterLogging:
    enableTypes: ["*"]
  • 전체 코드는 아래와 같다.
  • # project_myeks.yaml
    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig
    
    metadata:
      name: project-myeks
      region: ap-northeast-2
      version: "1.22"
    
    # AZ
    availabilityZones: ["ap-northeast-2a", "ap-northeast-2b",  "ap-northeast-2c"]
    
    # IAM OIDC & Service Account
    iam:
      withOIDC: true
      serviceAccounts:
        - metadata:
            name: aws-load-balancer-controller
            namespace: kube-system
          wellKnownPolicies:
            awsLoadBalancerController: true
        - metadata:
            name: ebs-csi-controller-sa
            namespace: kube-system
          wellKnownPolicies:
            ebsCSIController: true
        - metadata:
            name: cluster-autoscaler
            namespace: kube-system
          wellKnownPolicies:
            autoScaler: true
    
    # Managed Node Groups
    managedNodeGroups:
      # On-Demand Instance
      - name: production
        instanceType: t2.medium
        minSize: 1
        desiredCapacity: 2
        labels: { role: production }
        maxSize: 3
        privateNetworking: true
        ssh:
          allow: true
          publicKeyPath: ./keypair/myssh.pub
        availabilityZones: ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
        iam:
          withAddonPolicies:
            autoScaler: true
            albIngress: true
            cloudWatch: true
            ebs: true
    
    # CloudWatch Logging
    cloudWatch:
      clusterLogging:
        enableTypes: ["*"]

3. CI—Jenkins

웹 브라우저에서 인스턴스의 IP:8080 으로 접속한다. 앞서 젠킨스를 설치하고 난 뒤 sudo cat /var/lib/jenkins/secrets/initialAdminPassword 로 확인한 초기 암호를 입력한다.

관리자 설정과 권장 플러그인 설치를 끝내면 젠킨스의 메인 화면이 나타난다.

3-1. 기본 설정: 환경변수, 플러그인, 크레덴셜, 슬랙 알림

1) Global Tool Configuration에서 환경변수 설정

Maven을 다음과 같이 설정한다.

  • Name 에 지정한 값은 앞으로 젠킨스에서 구성할 작업에서 변수로 사용된다.
  • MAVEN_HOME의 위치: /usr/share/maven

maven 세팅 화면

2) Plugin 설치

  • Jenkins 관리 → 플러그인 관리 → 설치 가능 → Search
  • Docker, Docker pipeline, kubernetes CLI, Slack Notification

일부 플러그인은 Restart가 필요하다.

3) Credentials 등록

도커허브, 깃허브, 슬랙에 대한 3개의 Credentials 를 설정한다.

  • Jenkins 관리 → Manage Credentials → Domains (global) → Global credentials → Add Credentials

  1. GitHub 계정 설정하기
    • Kind: Username with Password
    • Username: GitHub 계정명
    • Password: GitHub에서 발급받은 토큰 값(Settings-Developer settings-Personal access tokens)
    • ID: 젠킨스 내에서 해당 크레덴셜을 가리키기 위해 사용할 변수 이름을 지정한다.
  2. Docker Hub Registry 계정 설정하기
    1. dockerhub: Account Settings → Security → New Access Token → Generate
    2. 아래와 같은 화면에서 생성된 토큰을 복사해 젠킨스 크레덴셜에 입력한다. Kind는 깃헙과 동일하게 “Username with Password”로 선택한다.
  3. Slack 통합 토큰 자격 증명 ID 설정하기

이 부분은 다음 단계인 Slack 알림 설정 과정에서 서술한다.

4) Slack 알림을 위한 젠킨스 CI 앱 추가

참조: https://hello-startup.tistory.com/19

CI/CD의 각 단계가 순조롭게 진행되고 있는지를 실시간으로 확인하는 방법에는 여러가지가 있으나, 본 프로젝트에서는 Slack 메시지를 받는 방법을 선택했다.

우선 메시지를 받아볼 Slack 워크스페이스를 개설하고, Jenkins 에서 알람이 올 채널을 하나 개설하고, 슬랙 앱 메뉴에서 아래처럼 Jenkins CI를 검색한다.

아래 화면에서 Jenkins CI를 추가하면 링크가 열리고 Slack에 추가할 수 있다.

알림을 포스트할 채널을 고른 뒤, 통합 앱을 추가하면 설정 지침 페이지로 이동하며, 이곳에서 기타 젠킨스에서 앱을 설정하는 방법을 상세히 설명한다. 설정 지침에서 다음과 같이 팀 하위 도메인, 통합 토큰 자격 증명 ID 값이 부여되었음을 확인할 수 있다. 이 두 가지 값을 복사한다.

젠킨스로 돌아와, Jenkins 관리 → 시스템 설정 맨 하단 Slack 설정 부분을 찾는다. (Slack Notification plugin이 설치되어 있지 않으면 보이지 않음) Workspace에는 팀 하위 도메인 값을 붙여넣고, Default channel 에는 Slack 에서 생성한 채널 이름을 넣는다. Credential에는 열쇠 아이콘 Add 버튼을 눌러 Jenkins Credentials Provider를 선택한다.

Credential 추가를 위해 아래와 같이 Kind는 토큰 ID 값만을 사용하기 때문에 Secret text를 선택하고, Secret에 앞서 Jenkins CI 도구에서 복사해둔 통합 토큰 자격 증명 ID 값을 입력한다. ID는 임의 입력해도 무방하다.

추가한 크레덴셜의 ID를 드롭다운 메뉴에서 고르고 저장한다.

3-2. 파이프라인 생성

1) Jenkins Job 구성

  • 새로운 item → Pipeline 선택 → Pipeline Tab(맨 하단 Pipeline으로 이동) → Definition에서 Pipeline script from SCM 선택

아래 그림과 같이 해당 Repository URL 에 Jenkins Credential 을 통해 인증하고 main 브랜치로 이동하여 Jenkinsfile 의 파이프라인 스크립트를 실행하도록 설정했다.

2) Jenkinsfile의 내용

위에서 Script Path에 설정한 바와 같이 Jenkinsfile은 GitHub repository 최상위 디렉토리에 위치한다.

전체 스크립트는 pipeline { } 안에 작성되며 이하 각 부분 별로 스크립트를 설명한다.

  • tools 안에는 Global Tool Configuration 에서 설정한 Maven3 를 사용할 수 있도록 maven 이라는 명칭을 부여했다.
  • environment 안에는 Jenkinsfile 스크립트에서 사용할 로컬 변수들을 설정했다. 도커 허브 레지스트리, 도커 허브 계정, 깃 허브 계정, 이메일, 이름 등을 변수로 설정해주었다.
  // Global Tool Configuration 에서 설정한 maven 의 명칭
  tools {
    maven 'Maven3' 
  }

  // 해당 스크립트 내에서 사용할 로컬 변수들 설정
  // 레포지토리가 없으면 생성됨
  // Credential들에는 젠킨스 크레덴셜에서 설정한 ID를 사용
  environment {
    dockerHubRegistry = 'how0326/tomcat' 
    dockerHubRegistryCredential = 'docker' 
    githubCredential = 'git_hub'
    gitEmail = 'how0326@gmail.com'
    gitName = 'seongwoo-choi'
  }

이하는 stages { } 안에 작성되며 그 하위 요소인 stage { } 에서 단계별 작업을 지정한다.

  • steps { } 내의 스크립트 실행이 끝난 후에 post { }안의 스크립트가 실행되며, failure { } 내부의 스크립트는 steps 가 실패할 경우, success { } 는 steps 가 성공할 경우에 실행된다.
  • 우선 checkout 을 사용하여 깃허브에서 레포지토리를 클론해온다. 만약 프라이빗 레포지토리일 경우에는 environment 에서 지정한 깃허브 계정으로 클론한다.
  • 두 번째 stage 는 tools 에서 지정한 maven 도구를 사용하여 Jar 파일을 생성하는 단계이다.
    // 깃허브 계정으로 레포지토리를 클론한다.
    stage('Checkout Application Git Branch') {
      steps {
        checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: githubCredential, url: 'https://github.com/mini-cicd-project/mini-cicd-project.git']]])
      }
      // steps 가 끝날 경우 실행한다.
      // steps 가 실패할 경우에는 failure 를 실행하고 성공할 경우에는 success 를 실행한다.
      post {
        failure {
          echo 'Repository clone failure' 
        }
        success {
          echo 'Repository clone success' 
        }
      }
    }

    // Maven 을 사용하여 Jar 파일 생성    
    stage('Maven Jar Build') {
      steps {
        sh 'mvn clean install'  
      }
      post {
        failure {
          echo 'Maven war build failure' 
        }
        success {
          echo 'Maven war build success'
        }
      }
    }

세 번째 stage는 도커를 사용해 이미지를 빌드하는 단계이다.

이 단계부터는 성공, 실패 시에 슬랙에 알람이 오도록 설정했다.

    stage('Docker Image Build') {
      steps {
        // 도커 이미지를 빌드하며 빌드한 횟수에 따라 순차적으로 증가하는 젠킨스 자체 변수를 태그로 자동 지정한다.
        sh "docker build . -t ${dockerHubRegistry}:${currentBuild.number}"
        sh "docker build . -t ${dockerHubRegistry}:latest"
      }
      // 성공, 실패 시 슬랙에 알람오도록 설정
      post {
        failure {
          echo 'Docker image build failure'
          slackSend (color: '#FF0000', message: "FAILED: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
        success {
          echo 'Docker image build success'
          slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
      }
    }  

네번째 스테이지는 도커에 이미지를 푸시하는 단계이다.

  • 로그인 부분은 젠킨스의 “Pipeline Syntax” 기능을 활용하여 도커 허브에 로그인하는 샘플 스크립트를 생성, 수정해 사용했다. 이후 도커 명령어로 도커 허브에 앞선 단계에서 생성한 이미지 두 개를 푸시한다.
  • 이미지를 푸시한 이후에 10 초 쉰 후 후속작업을 실행하도록 했으며, 푸시가 성공하든 실패하든 현재 빌드된 이미지를 삭제하도록 했다. 이는 불필요한 스토리지 사용량을 줄이며, 다음 이미지 빌드 시에 캐시 사용으로 변경사항 미적용 오류 등을 방지하기 위한 방법이다.
    stage('Docker Image Push') {
      steps {
        // 젠킨스에 등록한 크레덴셜로 도커 허브에 이미지 푸시
        withDockerRegistry(credentialsId: dockerHubRegistryCredential, url: '') {
          sh "docker push ${dockerHubRegistry}:${currentBuild.number}"
          sh "docker push ${dockerHubRegistry}:latest"
          // 10초 쉰 후에 다음 작업을 이어나가도록 함
          sleep 10
        } 
      }

      post {
        failure {
          echo 'Docker Image Push failure'
          sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
          sh "docker rmi ${dockerHubRegistry}:latest"
          slackSend (color: '#FF0000', message: "FAILED: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
        success {
          echo 'Docker Image Push success'
          sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
          sh "docker rmi ${dockerHubRegistry}:latest"
          slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
      }
    }

마지막 스테이지는 deploy/production.yaml 의 이미지 태그를 변경하여 깃허브에 푸시하는 단계이다.

  • Pipeline Syntax sample을 사용하여 깃허브에 로그인하는 스크립트를 작성하고, sed 명령어를 사용하여 deploy/production.yaml 의 이미지 태그를 변경한 뒤 메인 브랜치에 푸시하도록 했다.
    stage('K8S Manifest Update') {
      steps {
        // git 계정 로그인, 해당 레포지토리의 main 브랜치에서 클론
        git credentialsId: githubCredential,
            url: 'https://github.com/mini-cicd-project/mini-cicd-project.git',
            branch: 'main'  

        // 이미지 태그 변경 후 메인 브랜치에 푸시
        sh "git config --global user.email ${gitEmail}"
        sh "git config --global user.name ${gitName}"
        sh "sed -i 's/tomcat:.*/tomcat:${currentBuild.number}/g' deploy/production.yaml"
        sh "git add ."
        sh "git commit -m 'fix:${dockerHubRegistry} ${currentBuild.number} image versioning'"
        sh "git branch -M main"
        sh "git remote remove origin"
        sh "git remote add origin git@github.com:mini-cicd-project/mini-cicd-project.git"
        sh "git push -u origin main"
      }
      post {
        failure {
          echo 'K8S Manifest Update failure'
          slackSend (color: '#FF0000', message: "FAILED: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
        success {
          echo 'K8s Manifest Update success'
          slackSend (color: '#0AC9FF', message: "SUCCESS: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
      }
    }
  • 전체 코드는 아래와 같다.
  • pipeline {
      agent any
      // Global Tool Configuration 에서 설정한 Name
      tools {
        maven 'Maven3' 
      }
    
      // 해당 스크립트 내에서 사용할 로컬 변수들 설정
      // 레포지토리가 없으면 생성됨
      // Credential들에는 젠킨스 크레덴셜에서 설정한 ID를 사용
      environment {
        dockerHubRegistry = 'how0326/tomcat' 
        dockerHubRegistryCredential = 'docker' 
        githubCredential = 'git_hub'
        gitEmail = 'how0326@gmail.com'
        gitName = 'seongwoo-choi'
      }
    
      stages {
    
        // 깃허브 계정으로 레포지토리를 클론한다.
        stage('Checkout Application Git Branch') {
          steps {
            checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: githubCredential, url: 'https://github.com/mini-cicd-project/mini-cicd-project.git']]])
          }
          // steps 가 끝날 경우 실행한다.
          // steps 가 실패할 경우에는 failure 를 실행하고 성공할 경우에는 success 를 실행한다.
          post {
            failure {
              echo 'Repository clone failure' 
            }
            success {
              echo 'Repository clone success' 
            }
          }
        }
    
        // Maven 을 사용하여 Jar 파일 생성    
        stage('Maven Jar Build') {
          steps {
            sh 'mvn clean install'  
          }
          post {
            failure {
              echo 'Maven war build failure' 
            }
            success {
              echo 'Maven war build success'
            }
          }
        }
        stage('Docker Image Build') {
          steps {
            // 도커 이미지 빌드
            sh "docker build . -t ${dockerHubRegistry}:${currentBuild.number}"
            sh "docker build . -t ${dockerHubRegistry}:latest"
          }
          // 성공, 실패 시 슬랙에 알람오도록 설정
          post {
            failure {
              echo 'Docker image build failure'
              slackSend (color: '#FF0000', message: "FAILED: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
            success {
              echo 'Docker image build success'
              slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
          }
        }  
    
        stage('Docker Image Push') {
          steps {
            // 젠킨스에 등록한 계정으로 도커 허브에 이미지 푸시
            withDockerRegistry(credentialsId: dockerHubRegistryCredential, url: '') {
              sh "docker push ${dockerHubRegistry}:${currentBuild.number}"
              sh "docker push ${dockerHubRegistry}:latest"
              // 10초 쉰 후에 다음 작업 이어나가도록 함
              sleep 10
            } 
          }
    
          post {
            failure {
              echo 'Docker Image Push failure'
              sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
              sh "docker rmi ${dockerHubRegistry}:latest"
              slackSend (color: '#FF0000', message: "FAILED: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
            success {
              echo 'Docker Image Push success'
              sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
              sh "docker rmi ${dockerHubRegistry}:latest"
              slackSend (color: '#0AC9FF', message: "SUCCESS: Docker Image Push '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
          }
        }
    
        stage('K8S Manifest Update') {
          steps {
            // git 계정 로그인, 해당 레포지토리의 main 브랜치에서 클론
            git credentialsId: githubCredential,
                url: 'https://github.com/mini-cicd-project/mini-cicd-project.git',
                branch: 'main'  
    
            // 이미지 태그 변경 후 메인 브랜치에 푸시
            sh "git config --global user.email ${gitEmail}"
            sh "git config --global user.name ${gitName}"
            sh "sed -i 's/tomcat:.*/tomcat:${currentBuild.number}/g' deploy/production.yaml"
            sh "git add ."
            sh "git commit -m 'fix:${dockerHubRegistry} ${currentBuild.number} image versioning'"
            sh "git branch -M main"
            sh "git remote remove origin"
            sh "git remote add origin git@github.com:mini-cicd-project/mini-cicd-project.git"
            sh "git push -u origin main"
          }
          post {
            failure {
              echo 'K8S Manifest Update failure'
              slackSend (color: '#FF0000', message: "FAILED: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
            success {
              echo 'K8s Manifest Update success'
              slackSend (color: '#0AC9FF', message: "SUCCESS: K8S Manifest Update '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
            }
          }
        }
    
      }
    }

3) Jenkins CI-Slack Notification 실행 확인

  • Pipeline 구성을 마치고 Save/Apply → Build Now

빌드가 시작되며 깃허브 레포지토리에서 Jenkinsfile 을 가져와 스크립트를 실행한다.

위와 같이 Stage View를 통해 오류 없이 Jenkinsfile의 실행이 성공한 것을 확인할 수 있다. slackSend가 설정된 각 스테이지의 실행이 성공(또는 실패)할 때마다 슬랙으로 알림이 전송된다. 아래 이미지는 도커 이미지 빌드, 푸시, 깃허브에 푸시가 성공했음을 알리는 슬랙 메시지이다.

실제로 아래와 같이 도커허브에 새로운 빌드 번호를 tag로 단 이미지가 푸시된 것을 확인할 수 있다.


4. CD—ArgoCD

4-1. ArgoCD 설치

참조: https://argo-cd.readthedocs.io/en/stable/getting_started/

1) aws-load-balancer-controller 설치

helm repo add eks https://aws.github.io/eks-charts
helm repo list
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=project-myeks --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller --set image.repository=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-load-balancer-controller

2) ArgoCD 설치 확인 및 웹 UI 접속

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

설치가 끝나면 argocd-server 서비스의 타입을 로드 밸런서로 변경한 후, 해당 로드 밸런서의 DNS 이름 주소로 접속할 수 있게 한다.

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

kubectl -n argocd get all로 생성된 리소스들을 확인하고 service/argocd-server의 주소를 확인한다. 서버는 위에서 설정한 대로 LoadBalancer 타입이며 external-ip 에 도메인 주소가 부여된 것을 확인할 수 있다.

jenkins@jenkins:~$ kubectl -n argocd get all
NAME                                                    READY   STATUS    RESTARTS   AGE
pod/argocd-application-controller-0                     1/1     Running   0          3h58m
pod/argocd-applicationset-controller-66689cbf4b-xpm7g   1/1     Running   0          3h58m
pod/argocd-dex-server-66fc6c99cc-nlrlx                  1/1     Running   0          3h58m
pod/argocd-notifications-controller-8f8f46bd6-lrm5d     1/1     Running   0          3h58m
pod/argocd-redis-d486999b7-bh2f5                        1/1     Running   0          3h58m
pod/argocd-repo-server-7db4cc4b45-2b2dd                 1/1     Running   0          3h58m
pod/argocd-server-79d86bbb47-64w7s                      1/1     Running   0          3h58m

NAME                                              TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)                      AGE
service/argocd-applicationset-controller          ClusterIP      10.100.204.233   <none>                                                                        7000/TCP                     3h58m
service/argocd-dex-server                         ClusterIP      10.100.68.12     <none>                                                                        5556/TCP,5557/TCP,5558/TCP   3h58m
service/argocd-metrics                            ClusterIP      10.100.113.208   <none>                                                                        8082/TCP                     3h58m
service/argocd-notifications-controller-metrics   ClusterIP      10.100.153.105   <none>                                                                        9001/TCP                     3h58m
service/argocd-redis                              ClusterIP      10.100.110.65    <none>                                                                        6379/TCP                     3h58m
service/argocd-repo-server                        ClusterIP      10.100.161.179   <none>                                                                        8081/TCP,8084/TCP            3h58m
service/argocd-server                             LoadBalancer   10.100.0.145     ad7af453685d843d8a0a210edf174fba-610535311.ap-northeast-2.elb.amazonaws.com   80:32388/TCP,443:30461/TCP   3h58m
service/argocd-server-metrics                     ClusterIP      10.100.61.142    <none>                                                                        8083/TCP                     3h58m

NAME                                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argocd-applicationset-controller   1/1     1            1           3h58m
deployment.apps/argocd-dex-server                  1/1     1            1           3h58m
deployment.apps/argocd-notifications-controller    1/1     1            1           3h58m
deployment.apps/argocd-redis                       1/1     1            1           3h58m
deployment.apps/argocd-repo-server                 1/1     1            1           3h58m
deployment.apps/argocd-server                      1/1     1            1           3h58m

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/argocd-applicationset-controller-66689cbf4b   1         1         1       3h58m
replicaset.apps/argocd-dex-server-66fc6c99cc                  1         1         1       3h58m
replicaset.apps/argocd-notifications-controller-8f8f46bd6     1         1         1       3h58m
replicaset.apps/argocd-redis-d486999b7                        1         1         1       3h58m
replicaset.apps/argocd-repo-server-7db4cc4b45                 1         1         1       3h58m
replicaset.apps/argocd-server-79d86bbb47                      1         1         1       3h58m

NAME                                             READY   AGE
statefulset.apps/argocd-application-controller   1/1     3h58m                                                                  

확인한 주소로 브라우저에서 ArgoCD의 웹 UI에 접속한다.

admin 계정에 대한 암호는 아르고CD의 시작문서에서 안내하는 대로 다음 명령어를 통해 구할 수 있다.

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

4-2. Git 저장소에서 바로 애플리케이션 배포하기

1) Repository 연결

private repository 와 연동하기 위해 Repositories 설정 메뉴에서 아래 화면처럼 “Connect repo using HTTPS”를 선택한다.

  • Repository의 type은 git을 선택
  • ArgoCD가 바라볼 git 주소를 Repository URL에 입력
  • private repository 이므로 git hub ID와 Password(GitHub에서 발급받은 토큰 값) 입력

2) ArgoCD Application 생성

메인 화면 가운데의 “CREATE APPLICATION”으로 아래와 같은 앱 구성 화면을 연다.

  • General 섹션: 생성할 Application 이름을 지정하고 Sync 정책을 설정한다. git 저장소의 변경 사항을 자동으로 운영에 반영하기 위해 Automatic으로 설정하였다.

  • Source 섹션: repository URL에서 미리 등록한 git repository를 선택한 다음, 배포할 application의 위치(git repository에서의 위치)를 Path에 입력한다.

  • Destnation 섹션: kubernetes의 어느 cluster에 배포할지(아래 그림에서는 기본선택), 그리고 어떤 namespace에 배포할지를 결정한다. 마지막으로, 구성화면 최상단의 create를 눌러 앱을 생성한다.

아래는 생성된 애플리케이션을 확인하는 화면이다. 구성 시에 설정한 automatic sync policy에 따라 생성 직후 “Synced” 상태임을 확인할 수 있다.

아래는 해당 애플리케이션의 배포 및 연결 상태가 시각적으로 표시되는 화면이다.

배포된 App에 접속하려면 위 화면에서 myweb-ing(ALB)의 링크 셰어 아이콘 :을 누르거나, App Details에서 URLs를 찾는다.

아래는 배포된 앱에 접속했을 때 볼 수 있는 화면이다.

3) ArgoCD CLI

한편 ArgoCD CLI에서는 배포된 앱의 정보를 다음과 같이 확인할 수 있다.

  • 아르고 CD 서버에 로그인하기
$ argocd login --insecure ac...32-882088216.ap-northeast-2.elb.amazonaws.com:80
Username: admin
Password:
'admin:login' logged in successfully
Context 'ac...16.ap-northeast-2.elb.amazonaws.com:80' updated
  • 앱 정보 보기
$ argocd app get mini-project-cicd
Name:               mini-project-cicd
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://ac...16.ap-northeast-2.elb.amazonaws.com:80/applications/mini-project-cicd
Repo:               https://github.com/xxxx/xxxx.git
Target:             HEAD
Path:               deploy/
SyncWindow:         Sync Allowed
Sync Policy:        Automated
Sync Status:        Synced to HEAD (ee6b1a4)
Health Status:      Healthy

GROUP              KIND        NAMESPACE  NAME               STATUS  HEALTH   HOOK  MESSAGE
                   Service     default    production-svc-lb  Synced  Healthy        service/production-svc-lb unchanged
apps               Deployment  default    production-deploy  Synced  Healthy        deployment.apps/production-deploy configured
networking.k8s.io  Ingress     default    myweb-ing          Synced  Healthy        ingress.networking.k8s.io/myweb-ing unchanged
  • Sync Policy가 Automated 가 아닌 경우, 싱크를 실행하는 명령어는 다음과 같다.
argocd app sync <application>
  • kubectl로도 애플리케이션 Ingress 및 내부 Load Balancer의 DNS 주소를 볼 수 있다.
jenkins@jenkins:~$ kubectl get ing,svc -n default
NAME                                  CLASS    HOSTS   ADDRESSPORTS                                                                     AGE
ingress.networking.k8s.io/myweb-ing   <none>   *       k8s-default-mywebing-c51c82d215-597039061.ap-northeast-2.elb.amazonaws.com:80    42m

NAME                        TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes          ClusterIP      10.100.0.1      <none>        443/TCP        13h
service/production-svc-lb   LoadBalancer   10.100.165.79   k8s-default-producti-7b54e179b8-1cecdb688046eb5a.elb.ap-northeast-2.amazonaws.com  80:30458/TCP   42m

5. CI/CD 연동 확인—Jenkins & ArgoCD

Jenkins와 ArgoCD 동기화 확인을 위해 jenkins에서 build를 실행한다.

정상적으로 build가 완료되었다.

ArgoCD에서 Refresh를 해주거나 약간 기다리면 변경 사항을 반영하여 자동으로 Synchronized가 진행되는 것을 볼 수 있다. 아래는 Sync Status를 통해 Jenkins로 인한 변동 사항이 ArgoCD에 정상적으로 동기화되었음을 확인하는 화면이다.

History and Rollback 을 통해서 아래치럼 배포 이력을 확인하고, 우측 상단의 버튼을 통해 Re-deploy 또는 Rollback 시킬 수 있다.

Sync Status를 통해 Jenkins로 인한 변동 사항이 ArgoCD에 정상적으로 동기화되었음을 확인할 수 있다.


6. 추가 구성

6-1. metrics-server

공식 문서: https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/metrics-server.html

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

6-2. Cluster Autoscaler

1) 수동 스케일링

eksctl scale nodegroup --name myeks-ng1 --cluster project-myeks --help
eksctl scale nodegroup --name myeks-ng1 --cluster project-myeks --nodes 2

2) 자동 스케일링

공식 문서: https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/autoscaling.html

curl -o cluster-autoscaler-autodiscover.yaml https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml

YAML 파일을 수정하고 **을 클러스터 이름으로 바꾼다.

                        - --expander=least-waste
            - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/project-myeks
          volumeMounts:
            - name: ssl-certs
kubectl patch deployment cluster-autoscaler \
  -n kube-system \
  -p '{"spec":{"template":{"metadata":{"annotations":{"cluster-autoscaler.kubernetes.io/safe-to-evict": "false"}}}}}'
kubectl -n kube-system edit deployment.apps/cluster-autoscaler

48         - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/***project-myeks***
49         - ***--balance-similar-node-groups***
50         - ***--skip-nodes-with-system-pods=false***

현재 노드의 kubernetes 버전과 image 버전을 맞춰야 한다. 현재 kubernetes 버전은 1.22.6 이지만 오토스케일러 이미지는 1.22.2 가 제일 최신 버전이기 때문에 1.22.2 로 설정한다.

kubectl get nodes
NAME                                                 STATUS   ROLES    AGE   VERSION
ip-192-168-117-21.ap-northeast-2.compute.internal    Ready    <none>   50m   v1.22.6-eks-7d68063
ip-192-168-149-72.ap-northeast-2.compute.internal    Ready    <none>   50m   v1.22.6-eks-7d68063
ip-192-168-180-249.ap-northeast-2.compute.internal   Ready    <none>   50m   v1.22.6-eks-7d68063
kubectl set image deployment cluster-autoscaler \
  -n kube-system \
  cluster-autoscaler=k8s.gcr.io/autoscaling/cluster-autoscaler:v1.22.2

다음 명령어로 Cluster Autoscaler 로그를 확인한다.

kubectl -n kube-system logs -f deployment.apps/cluster-autoscaler

6-3. CloudWatch Container Insight

공식 문서: https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/Container-Insights-setup-EKS-quickstart.html

ClusterName='project-myeks'
LogRegion='ap-northeast-2'
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On'
curl https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${ClusterName}'/;s/{{region_name}}/'${LogRegion}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f -

윈도우는 쉘 스크립트를 실행하지 못해서 git bash 를 다운받아서 실행해야 한다.

❯ kubectl get po -A
NAMESPACE           NAME                                            READY   STATUS    RESTARTS   AGE
amazon-cloudwatch   cloudwatch-agent-8khfv                          1/1     Running   0          74s
amazon-cloudwatch   cloudwatch-agent-l9w7j                          1/1     Running   0          74s
amazon-cloudwatch   fluent-bit-lrzx2                                1/1     Running   0          74s
amazon-cloudwatch   fluent-bit-vlc9x                                1/1     Running   0          74s

fluent-bit 가 로그를 수집하고 cloudwatch-agent 에게 로그를 보내주면 cloudwatch 에 로그가 쌓인다.

kubectl get nodes --show-labels
NAME                                                 STATUS   ROLES    AGE   VERSION               LABELS
ip-192-168-149-72.ap-northeast-2.compute.internal    Ready    <none>   77m   v1.22.6-eks-7d68063   alpha.eksctl.io/cluster-name=project-myeks,alpha.eksctl.io/nodegroup-name=myeks-ng1,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t3.medium,beta.kubernetes.io/os=linux,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup-image=ami-06512ccb913a9d11d,eks.amazonaws.com/nodegroup=myeks-ng1,eks.amazonaws.com/sourceLaunchTemplateId=lt-018ac7a7f7853aa04,eks.amazonaws.com/sourceLaunchTemplateVersion=1,failure-domain.beta.kubernetes.io/region=ap-northeast-2,failure-domain.beta.kubernetes.io/zone=ap-northeast-2b,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-192-168-149-72.ap-northeast-2.compute.internal,kubernetes.io/os=linux,node.kubernetes.io/instance-type=t3.medium,topology.ebs.csi.aws.com/zone=ap-northeast-2b,topology.kubernetes.io/region=ap-northeast-2,topology.kubernetes.io/zone=ap-northeast-2b
alpha.eksctl.io/cluster-name=project-myeks
alpha.eksctl.io/nodegroup-name=myeks-ng1
beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t3.medium,beta.kubernetes.io/os=linux
eks.amazonaws.com/capacityType=ON_DEMAND
eks.amazonaws.com/nodegroup-image=ami-06512ccb913a9d11d
eks.amazonaws.com/nodegroup=myeks-ng1
eks.amazonaws.com/sourceLaunchTemplateId=lt-018ac7a7f7853aa04
eks.amazonaws.com/sourceLaunchTemplateVersion=1
failure-domain.beta.kubernetes.io/region=ap-northeast-2
failure-domain.beta.kubernetes.io/zone=ap-northeast-2b
kubernetes.io/arch=amd64
kubernetes.io/hostname=ip-192-168-149-72.ap-northeast-2.compute.internal
kubernetes.io/os=linux,node.kubernetes.io/instance-type=t3.medium
topology.ebs.csi.aws.com/zone=ap-northeast-2b
topology.kubernetes.io/region=ap-northeast-2
topology.kubernetes.io/zone=ap-northeast-2b

7. Troubleshooting

테라폼으로 VPC를 직접 생성하고 클러스터 생성에 활용 시 오류 발생

  • 문제: 테라폼으로 VPC를 생성한 후 해당 VPC와 서브넷 주소를 할당해서 클러스터를 생성했을 때, LoadBalancer 가 VPC 에 할당되지 않았다.
  • 해결: eksctl 로 VPC 가 자동으로 생성되도록 했다.

Jenkins Helm chart 생성시 오류 발생

  • 문제: 헬름 차트로 젠킨스를 생성했을 시, ALB, NLB 가 제대로 연결이 되지 않는다.
  • 추정 이유: LB가 타겟 그룹에 제대로 연결되지 않아 생기는 것으로 짐작된다.
  • 해결: ALB를 하나만 생성했다.

도커의 사용

  • 문제: EKS 클러스터에서 도커를 사용할 수 없기 때문에 Jenkins 에서 도커를 사용할 수 없다.
  • 해결: 마스터 슬레이브 구조로 구축하는 것이 최선이나 시간적인 여유가 없어 EC2 인스턴스에 젠킨스를 설치하는 방식으로 우회하였다.

AWS Load Balancer Controller 설치 오류

  • 문제: AWS Load Balancer Controller 설치를 시도했을 때 다음과 같은 메시지가 출력되며 설치가 진행되지 않는다.

Error: INSTALLATION FAILED: Kubernetes cluster unreachable: exec plugin: invalid apiVersion "client.authentication.k8s.io/v1alpha1"

  • 해결: 해결하지 못함. EC2를 새로 만들고 처음부터 재설치 진행.

EKS 클러스터의 완전 삭제

  • 문제: 실습을 반복하면서 EKSCTL 로 깨끗하게 지워지지 않은 클러스터가 남아 새 클러스터가 정상적으로 생성되지 않는다.
  • 해결: AWS 웹콘솔의 CloudFormation에서 직접 스택을 삭제하고 연결된 리소스를 직접 정리한다.

 

728x90