1
2
AWS EKS Workshop Study (=AEWS)는 EKS Workshop 실습 스터디입니다.
CloudNet@ Gasida(가시다)님이 진행하시며,공개된 AWS EKS Workshop을 기반으로 진행하고 있습니다.

스터디 마지막 시간에는 EKS Automation 을 주제로 진행하였다. 여기서 Automation 단어는 인프라 프로비저닝 및 코드 배포를 포함하여 소프트웨어 제공 수명 주기의 일부를 자동화를 의미한다. 스터디에서는 모임장님께서 AWS 서비스 기반의 Automation(ACK)를 소개해주셨는데 실무에 도입하기에는 아직 보완할 서비스라 느껴, 다른 주제(gitops ci / cd 파이프라인 구성)를 선정하여 블로그를 작성한다.

사실 지난 스터디에서 다룬 주제였지만, 내부 트러블슈팅으로 절반(CD 파이프라인 구성) 정도 밖에 구성하지 못하였다. 다시 스터디를 통해 계기가 생긴 만큼 이번 글에서는 CI 파이프라인 구성 확장 및 CD 연결까지 다룰 예정이다. 사용한 코드들은 필자의 레파지토리에서 확인이 가능하다.

CI / CD

코드가 변경될 때마다 자동적으로 코드 통합(CI)와 배포(CD)를 수행하는 소프트웨어 개발 방식이다. 이를 통해 애플리케이션을 보다 짧은 주기로 서비스를 제공할 수 있어 비즈니스 이점이 큰 기술로 최근 devops 핵심 기술이라 할 수 있다.

<a href="https://server-engineer.tistory.com/800">https://server-engineer.tistory.com/800</a>

https://server-engineer.tistory.com/800

Gitops는 CI / CD일까?

엄밀히 말하면 다르다. GitOps는 이런 CI/CD 프로세스에 git를 사용하여 인프라와 애플리케이션의 설정을 관리하는 방법이다. 달리 말하면, CI/CD는 코드 변경을 효과적으로 관리하고 배포하는 프로세스이며, GitOps는 이런 프로세스를 git을 통해 조정하고 관리하는 방식이라고 할 수 있다. 이 둘은 상호 보완적인 개념으로, GitOps는 CI/CD 파이프라인을 통해 애플리케이션을 배포하고 관리하는 데 사용될 수 있다.

<a href="https://blogs.vmware.com/cloud/2021/02/24/gitops-cloud-operating-model/">https://blogs.vmware.com/cloud/2021/02/24/gitops-cloud-operating-model/</a>

https://blogs.vmware.com/cloud/2021/02/24/gitops-cloud-operating-model/

이번 시간에는 EKS 환경에서 CI / CD 구성을 목표로 애플리케이션을 코드에서 배포까지 자동화하는 작업을 진행하고자 한다. CD 구성 같은 경우 지난 블로그 글을 통해 진행하였고, 이어서 CI 구성과 gitops ci/cd 까지 확장하여 다룰 것이다. CI 구성에 사용한 도구는 보편적으로 가장 많이 사용하는 젠킨스(jenkins)를 사용핬고 gitops ci cd 구성을 위해 CD(Argocd), container registry(harbor), gitops(gitlab) 을 이용하였다. 구성 아키텍처는 다음과 같다.

gitlab-archi.png

복잡해보이지만, 각 도구들을 나눠서 확인하고 도구별 연동 방법에 대해 소개하겠다.

젠킨스(Jenkins)

CI 빌드 도구가 많지만, 젠킨스를 선택한 이유는 확장성과 커뮤니티 활성도가 압도적으로 높아서다. 젠킨스는 지속적 통합을 위한 오픈 소스 자동화 도구이다. 파이프라인 중 도커 이미지빌드 및 테스트, git에 업데이트(통합)까지의 과정을 구성해주는 도구이다. 젠킨스는 다양한 플러그인이 제공되어 기능 확장이 쉽게 가능하다. 대표적으로 쿠버네티스 플러그인을 통해서 쿠버네티스 APi 서버와 통신하여 코드 테스트(unit test)을 담당하는 Slave 파드를 동적으로 생성하고 관리할 수 있다. 이를 통해서 얻을 수 있는 이점은 다음과 같다.

  1. 동적 스케일링: 쿠버네티스는 Jenkins 내 유닛 테스트(Jenkins Slaves)를 파드로 실행할 수 있다. 이렇게 하면 빌드/테스트 워크로드에 따라 파드를 동적으로 스케일링 할 수 있다. 즉, 추가적인 빌드 요구 사항이 있을 때마다 쿠버네티스는 새로운 유닛 테스트 파드를 생성하고, 작업이 완료되면 이를 제거할 수 있고 요청량에 따라 파드를 조절할 수 있다. 이를 통해 리소스를 효율적으로 사용할 수 있다.
  2. 고가용성: 쿠버네티스의 내장된 고가용성 기능 덕분에, Jenkins 마스터의 다운타임을 크게 줄일 수 있다. 이는 Jenkins 마스터가 다운되더라도 쿠버네티스가 자동으로 새 인스턴스를 시작하여 서비스 중단 시간을 최소화할 수 있다는 것을 의미한다.

<a href="https://devopscube.com/jenkins-architecture-explained/">https://devopscube.com/jenkins-architecture-explained/</a>

https://devopscube.com/jenkins-architecture-explained/

아키텍처도 간단하다. jekins 배포시 마스터 파드가 배포되며 해당 파드에서 jenkins https 서버(UI) 실행 및 빌드 스케쥴러, 유저, 플러그인, 보안 정보들을 관리한다.

Jenkins 배포

헬름으로 젠킨스를 배포하면서 구성 아키텍처를 확인하겠다. 먼저, 차트를 가져오자.

1
2
3
helm repo add jenkins https://charts.jenkins.io
helm repo update
helm fetch jenkins/jenkins --untar --version 4.3.9

차트에는 마스터(컨트롤러), Agent(노드), 플러그인 설정, PV 및 백업 설정, 보안적 요소가 들어가있다. 또한, 젠킨스 차트에는 VALUES_SUMMERY.md 이 포함되는데 전체 파라미터에 대한 설명이 알기 쉽게 들어가있다.

구성 차트는 다음과 같이 수정했다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# values-jenkins.yaml
controller:
  adminUser: "admin"
  adminPassword: "admin1234"
  jenkinsUriPrefix: "/jenkins"
  installPlugins:
  - kubernetes
  - workflow-aggregator
  - git
  - configuration-as-code
  - pipeline-stage-view
  - gitlab

  ingress: 
    enabled: true
    apiVersion: "networking.k8s.io/v1" 
    annotations: 
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: <cert ARN 입력>
      alb.ingress.kubernetes.io/healthcheck-path: /login
      kubernetes.io/ingress.class: alb
      ingressClassName: alb
    hostName: <host domain 입력> 
1
2
kubectl create ns jenkins
helm install jenkins jenkins/jenkins -f values-jenkins.yaml --namespace jenkins --version 4.3.9

배포 후 jekins 페이지에 접속이 잘되는 지 확인해보자. 위 차트에서 확인했듯이 admin 아이디와 비밀번호는 admin / admin1234 이다.

jenkins1.png

Jenkins & 쿠버네티스 연동

젠킨스 파이프라인 작업을 쿠버네티스에서 작업시키려면 쿠버네티스와 연동이 필요하다. 다음 그림과 같이 젠킨스에서 쿠버네티스 주소를 입력하고, 쿠버네티스 접근 정보를 입력하자.

jenkins 관리 → 노드 관리 → Configure Clouds 접근 후 쿠버네티스 연동을 진행하자, 연동을 위해 kubernetes URL과 접근 정보가 필요하다. kubernetes URL과 접근 정보는 베스천 서버에 접근 파일에서 확인할 수 있다. 이를 복사해서 젠킨스 마스터 서버에 등록하자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 베스천 서버 
vi ~/.kube/config 
---
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <auth-data> 
    server: https://api.hanhorang.link 
  name: hanhorang.link
contexts:

add-auth.png

쿠버네티스 URL 입력 후 접근 파일(config)을 등록하자.

kube-auth.png

Test Connection 버튼을 통해 연동 테스트를 진행하자.

test.png

그림과 같이 Connected to kubernetes가 나오면 연동이 완료된 것이다. Save를 통해 Configure Clouds 정보를 저장시키자.

CI & CD 구성 전 사전 작업

사전 작업으로 추가 구성이 필요합니다. 아래 블로그 글을 참고하여 필요 툴을 설치해주세요. 컨테이너 이미지 레지스트리(harbor) : https://hanhorang31.github.io/post/tech-private-docker/ gitlab & argocd : https://hanhorang31.github.io/post/pkos2-3-gitops/

구성전 도구 연동 [Gitlab-Jenkins-Harbor] 을 위해 사전 작업이 필요하다.

GItlab 설정

  1. 사용자, 그룹 생성 & 접근 토큰 발급 (사용자 : han / 토큰 glpat-yznZ6zHSNN7UaoSHZ-rr)

    1. admin→ users→ 계정생성 비밀번호는 user 생성 후 edit 에서 설정할 수 있다.

      gitlab-user-token.png

      gitlab-admin-user.png

    2. users→ 사용자 → Impersonation Token을 통해 토큰 발급

      gitops-token.png

  2. 프로젝트 생성 (test) 및 옵션 설정

    1. 프로젝트 → Setting → CI / CD → Devops 파이프라인 설정

      gitlab1.png

    2. Repository 접근 제어 설정

      프로젝트 → Setting → Repository 접근 아래와 같이 설정

      gitops2.png

  3. 빌드 코드 작성(예제 코드) 및 Repo에 Push

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    git config --global user.email han@gmail.com
    git config --global user.name han
    git add . 
    git commit -m "update code"
    [main 01f9418] update code
     3 files changed, 29 insertions(+)
     create mode 100644 Dockerfile
     create mode 100644 app.py
     create mode 100644 requirements.txt
    
    git push origin main
    Username for 'https://gitlab.hanhorang.link': han  
    Password for 'https://han@gitlab.hanhorang.link': 
    Enumerating objects: 6, done.
    Counting objects: 100% (6/6), done.
    Delta compression using up to 2 threads
    Compressing objects: 100% (4/4), done.
    Writing objects: 100% (5/5), 824 bytes | 824.00 KiB/s, done.
    Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
    To https://gitlab.hanhorang.link/gitlab-instance-0d144142/test.git
       347dcae..01f9418  main -> main
    

    giops4.png

Harbor 설정

  1. 사용자 생성 (ID : han / PW)

  2. 버킷 생성 (bucket name : aews) 및 버킷 접근 제어 설정(han에 권한 부여)

    automation1.png

Jenkins 설정

Credentials 추가 ( Gitlab, Harbor ID & PWD)

auto2.png

auto3.png

Jenkins & gitlab webhook 연동

webhook.png

gitlab 에서 이벤트 발생(코드 변경)시 jenkins에 알려 코드 파이프라인이 바로 실행 될 수 있도록 webhook 을 구성하겠다. 다음과 같이 configure system → gitalb 에서 gitlab 연결을 테스트하여 등록한다.

webhook1.png

webhook2.png

  • 해당 메뉴가 보이지 않을 경우 플러그인 gitlab 을 설치하자

이어서 gitlab 과 jenkins 파이프라인을 연결하자. 우선 아래 메뉴에서 파이프라인을 생성하자.

webhook10.png

그 다음 general에서 아래와 같이 설정한 후, [빌드 유발]에서 Secret token 토큰을 발급받고 webhook URL을 복사하자, 복사한 정보는 gitlab webhook 등록에 사용할 것이다.

webhook3.png

webhook6.png

webhook7.png

확인한 정보값을 gitlab project → setting → webhook 에 입력하여 연동을 진행하자.

webhook8.png

CI & CD 구성

1. CI 파이프라인 구성

ci11.png

위 단계에서 생성한 파이프라인에서 스크립트를 다음과 같이 작성하자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
podTemplate(yaml: '''
              kind: Pod
              metadata:
                name: kaniko-image-build-pod
              spec:
                containers:
                - name: yq
                  image: linuxserver/yq:amd64-latest
                  imagePullPolicy: Always
                  tty : true
                  command:
                  - sleep
                  args:
                  - 99d
                - name: kaniko
                  image: gcr.io/kaniko-project/executor:v1.6.0-debug
                  imagePullPolicy: Always
                  command:
                  - sleep
                  args:
                  - 99d
                  volumeMounts:
                    - name: docker-config
                      mountPath: /root/.docker
                  tty: true
                volumes:
                    - name: docker-config
                      secret:
                        secretName: regcred
                        items:
                        - key: .dockerconfigjson
                          path: config.json
'''
  ) {

  node(POD_LABEL) {
    stage('Build with Kaniko') {

      //git tag를 가져오기 위한 clone 
      git branch: 'main',
        credentialsId: 'gitlab',
        url: 'https://gitlab.hanhorang.link/gitlab-instance-f7cee683/aews.git'

      script(){
          GIT_TAG = sh (
            script: 'git describe --always',
            returnStdout: true
          ).trim()
      }
	  //Image build 
      container('kaniko') {
        //kaniko 에서 빌드하기 위해 소스코드 clone
        git branch: 'main',
          credentialsId: 'gitlab',
          url: 'https://gitlab.hanhorang.link/gitlab-instance-f7cee683/aews.git'
        // kaniko 실행
        sh '/kaniko/executor -f Dockerfile -c `pwd` --insecure --skip-tls-verify --cache=true --destination=harbor.hanhorang.link/aews/test:' + GIT_TAG 
      }

    }
  }
}

파이프라인에서 중요하게 볼 점은 도커 빌드를 위한 도구로 kaniko 를 사용한 점이다. kaniko 는 Google Cloud에서 개발한 오픈 소스 도구로 Docker 데몬이 없는 환경에서 Docker 이미지를 빌드할 수 있다. 이 Docker 데몬이 없는 환경 라는 단어가 쿠버네티스에서는 특히 중요하다.

  • 호환성 제공 : eks 1.24 이상 버전에서 docker 가 deprecated 되어 더 이상 docker 이미지 빌드를 할 수 없다는 점(커스텀 노드면 docker 설치해서 가능하긴 하다)에서
  • 보안 이슈 제거 : Docker 데몬은 루트 권한으로 실행되기 때문에, 악의적인 이미지가 Docker 데몬에 액세스하게 되면 시스템 전체를 위험에 빠뜨릴 수 있다. 이와 반면 Kaniko는 사용자 공간(user-space)에서 실행되므로 이러한 보안 문제를 완화한다.
  • 효율성 : Kaniko는 이미지 빌드 과정을 Kubernetes 팟 내에서 직접 수행하므로, 별도의 빌드 서버를 유지할 필요가 없다. 이는 관리 오버헤드를 줄이고 리소스 사용을 최적화할 수 있어 효율적이다.

이러한 점에서 필자는 kaniko를 사용하여 이미지를 빌드하였다. kaniko에서는 또한 docker login 기능을 제공한다. 베스천 서버에서 harbor 도커레지스트리에 접근하기 위한 시크릿 정보를 생성하고 파이프라인을 빌드하자. 빌드가 정상적으로 처리되면 jenkins에서의 결과 화면과 harbor에서 푸쉬된 이미지를 확인할 수 있다!

1
2
3
4
5
# 시크릿 생성
kubectl create secret docker-registry regcred -n jenkins \
--docker-username=admin \
--docker-password=Harbor12345 \
--docker-server=harbor.hanhorang.link

auto11.png

harbor1.png

2. ArgoCD 를 통한 CD 구성

cd10.png

CI 구성은 이미지 푸쉬가 끝이 아니다. 이미지 푸쉬가 완료되고 배포 차트에 이미지 태그 업데이트까지 시켜줘야 한다. 차트의 이미지 태그를 업데이트해야 Argocd를 통해서 자동으로 쿠버네티스에 배포되기 때문이다. 이를 위해 깃랩에서 새로운 프로젝트를 생성해서 배포 차트를 구성하자. 차트 구성은 필자의 깃 레파지토리를 확인하자. 차트를 확인하면 kustomize 를 통해 차트를 구성한 것을 확인할 수 있다. Kustomize는 기존 YAML 파일을 수정하지 않고, 오버레이와 패치를 통해 구성을 수정하거나 확장하는 기능을 제공하는 도구로서 추후 dev,ops 환경에서 배포를 고려하여 도입하였다.

1
2
# 설치 
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"  | bash
1
2
3
4
5
6
7
8
9
# kustomize 차트 구성
├── base
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── service.yaml
├── overlays
│   └── dev
│       └── kustomization.yaml
└── README.md
1
2
3
4
# 배포 
# overlays/dev 
kubectl apply -k . 
--

애플리케이션 동작을 확인했으면 argocd를 통해 sync 작업을 진행하겠다.

ArgoCD 에 접속하여 gialab 접근을 위한 시크릿 정보를 입력하겠다. Argocd → Setting → Repo 에서 다음의 사진처럼 k8s-chart 레파지토리와 gitlab 접근 토큰을 입력하자.

auto13.png

연결이 정상적이면 Succeesful로 표 될 것이다. 이어서 APP sync 를 진행하자. 왼쪽 Application에서 다음과 같이 정보를 입력하자.

auto12.png

  • Source : 차트 타켓이 되는 정보를 입력한다. 중요한 것은 Path 인데 실행 경로로 overlays/dev 로 설정하자.
  • Destination : 배포 클러스터 정보의 입력란이다. 다음과 같이 클러스터 서비스 이름과 네임스페이스를 입력하자.

입력이 완료되면 다음과 같이 확인할 수 있다.

auto14.png

3. CI & CD 연동

cd11.png

드디어 마지막 단계이다! 앞에서 구성한 파이프라인의 stage 단계를 추가하여 CD 구성 차트의 이미지 태그를 업데이트하는 부분을 추가할 것이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Jekins pipeline
.
.
.
stage('Update k8s chart') {
      withCredentials([usernamePassword(credentialsId: 'gitlab', usernameVariable: 'GITLAB_USERNAME', passwordVariable: 'GITLAB_ACCESS_TOKEN')]) {
        dir("~/") {
          git branch: 'main',
            credentialsId: 'gitlab',
            url: 'https://gitlab.hanhorang.link/han/k8s-chart.git'
          sh 'echo $GITLAB_ACCESS_TOKEN $GITLAB_USERNAME'
          sh 'git config --global credential.helper store'
          sh 'git config --global user.name "han"'
          sh 'git config --global user.email "han@gmail.com"'
          sh 'git remote set-url origin https://han:glpat-yznZ6zHSNN7UaoSHZ-rr@gitlab.hanhorang.link/han/k8s-chart.git'
          sh 'sed -i "s|newTag:.*|newTag: ' + GIT_TAG + '|" overlays/dev/kustomization.yaml'
          sh 'git add .'
          sh 'git commit -m "updated the image tag to ' + GIT_TAG + '" || true'
          sh 'git push origin main'
        }
      }
    }

트러블슈팅

해당 단계에서 깃 푸쉬가 안되어 많은 시행 착오를 겪었다. 케이스별로 묶어 정리하니 참고하자.

  • git not

    Console Output 에서 로그가 다음과 같이 git을 인식하지 못하는 문제이다.

    1
    2
    3
    
    .
    .
    /home/jenkins/agent/workspace/aews-pipeline-test/~@tmp/durable-bda4026d/script.sh: line 1: git: not
    

    근본적인 원인은 git이 stage container 에 설치되지 않았기 때문이다. 커맨드로 git 설치하는 방법도 있지만, 필자의 경우 리소스 오버헤드를 고려하여 jenkins 옵션에서 git을 활성화하였다.

    auto17.png

  • URL using bad/illegal format or missing URL , fatal: could not read Username for 'https://gitlab.hanhorang.link': No such device or address

    gitlab 원격저장소를 잘못 설정하여 생긴 문제이다. 원인을 찾는 것은 쉬웠지만, 어디서 잘못되었는 지 테스트에 오래걸렸다. 필자의 경우 깃랩 토큰 노출을 고려하여 withCredentials 를 사용하였는데 다음과 같이 **** 로 표시로 원격저장소를 인식하지 못하였다.

    auto21.png

    해결 방법으로 하드 코딩이나 환경 변수로 설정하는 것을 추천한다.

Gitops 기반 CI & CD 테스트

앞 단계에서 필자는 CI & CD 구성을 완료하였다. 이제 본격적인 운영 시나리오로 들어가서 GItops 기반에서 CI & CD 프로세스가 정상적으로 작동되는 지 확인해보겠다. 밑의 아키텍처처럼 소스코드 레파지토리에 코드를 수정하여 푸쉬시 jenkins를 통해서 이미지가 새로 푸쉬되고, k8s-chart 의 이미지 태그가 업데이트되어 최종적으로 쿠버네티스 애플리케이션이 수정되는 것을 확인해보겠다.

gitlab-archi.png

  1. Commit Change

    예제 파일 구성시 플라스트 실행 파일에 대해 수정이 필요하여 파일을 수정하고 Commit을 진행하였다.

    ci1.png

  2. Jenkins 파이프라인 확인

    Commit 진행시, Webhook를 통해 다음과 같이 자동으로 설정한 파이프라인이 실행된다.

    ci2.png

    각 파이프라인의 로그를 확인하면 레파지토리와 k8s-chart에 대해 작업을 수행한 것을 확인할 수 있다.

    ci6.png

  3. ArgoCD Sync 확인

    ArgoCD 의 설정한 Sync 옵션에 따라 자동으로 k8s에 배포된 것을 확인할 수 있다.

    ci7.png

마치며

이번 주에는 CI / CD 에 대한 본격적인 맛보기로 예제를 들어 gitops ci & cd를 구성하였다. 이것을 기반으로 확장하여 애플리케이션 트래픽 관리를 위한 argo rollout 적용, 개발 & 운영 환경에 대한 관리, 이미지 태그 관리 등등.. 확장 기능들이 많다. 또 나중에 기회가 된다면 정리해서 공유하도록 하겠다!