EKS CI/CD
April 2024 (1976 Words, 11 Minutes)
CI/CD
이번주에 사용하는 클러스터는 기존과 큰 차이가 없습니다.
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/**eks-oneclick6.yaml**
해당 파일을 기준으로 프로비저닝합니다.
Docker Hub
public 이미지 레포인 도커허브의 테스트를 진행해봅니다.
# ubuntu 이미지 다운로드
docker pull ubuntu:20.04
docker images
# 실습을 위한 디렉터리 생성 및 이동
mkdir -p /root/myweb && cd /root/myweb
# Dockerfile 파일 생성
cat > Dockerfile <<EOF
FROM ubuntu:20.04
ENV TZ=Asia/Seoul VERSION=1.0.0 NICK=montkim
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone && \
sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apache2 figlet && \
echo "\$NICK Web Server \$VERSION<br>" > /var/www/html/index.html && \
echo "<pre>" >> /var/www/html/index.html && \
figlet AEWS Study >> /var/www/html/index.html && \
echo "</pre>" >> /var/www/html/index.html
EXPOSE 80
CMD ["usr/sbin/apache2ctl", "-DFOREGROUND"]
EOF
# 이미지 빌드
cat Dockerfile
docker build -t myweb:v1.0.0 .
docker images
docker image history myweb:v1.0.0
docker image inspect myweb:v1.0.0 | jq
# 컨테이너 실행
docker run -d -p 80:80 --rm --name myweb myweb:v1.0.0
docker ps
curl localhost
# 웹 접속 확인
curl -s ipinfo.io/ip | awk '{ print "myweb = http://"$1"" }'
myweb 이라는 이름의 컨테이너를 직접 빌드하여 테스트를 진행해봅니다.
docker
개인 계정에 빌드/ 푸시 테스트를 마저 진행해봅니다.
#
DHUB=idoyo7
docker tag myweb:v1.0.0 $DHUB/myweb:v1.0.0
docker images
docker push $DHUB/myweb:v1.0.0
개인 계정에 테스트된걸 잘 확인했네요.
사실 Docker hub 테스트는 엄청 오랜만에 해본거같은데
일반적으론 private docker registry 혹은 ECR을 사용하는 경우가 더 많으니 오랜만에 보는게 꽤 생소하네요
Jenkins
Jenkins는 Java 로 제작된 오픈소스 CI/CD 툴로
손쉬운 설치, 구성 및 수많은 확장이 가능한 도구입니다.
개발자들이 소스 코드 변경사항을 중앙 서버에 자주 커밋하면, Jenkins가 이러한 변경사항을 자동으로 검출하여 통합, 테스트, 배포하는 과정을 도와주는 것을 목적으로 합니다.
그중 CI/CD 워크플로를 구성하는 구조는다음과 같이 구분이 가능하겠네요.
CI (Continuous Integration)
- 자동화 테스트: Jenkins는 코드 저장소에 새로운 코드 변경사항이 푸시될 때마다 자동으로 빌드 및 테스트를 실행하여, 코드가 주어진 요구사항을 만족하는지 확인합니다. 이는 버그를 빠르게 발견하고 수정할 수 있게 해줍니다.
- 빌드 스케줄링: Jenkins를 사용하면 빌드를 예약하거나 특정 조건에 따라 자동으로 실행되도록 설정할 수 있습니다.
CD (Continuous Delivery/Deployment)
- 자동화 배포: Jenkins는 빌드 및 테스트가 성공적으로 완료되면, 코드를 자동으로 스테이징(준비) 환경 또는 생산 환경에 배포할 수 있습니다. Continuous Delivery는 스테이징 환경에 자동 배포하는 것을 의미하며, Continuous Deployment는 추가 승인 없이 직접 생산 환경에 배포하는 것을 의미합니다.
- 환경 관리: Jenkins는 다양한 환경(개발, 테스트, 스테이징, 생산 등)에서의 배포를 관리하고 자동화할 수 있는 기능을 제공합니다.
설치
# 실습 편리를 위해서 root 계정 전환
sudo su -
# Add required dependencies for the jenkins package
# https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/amazon-linux-install.html
sudo yum install fontconfig java-17-amazon-corretto -y
java -version
alternatives --display java
JAVA_HOME=/usr/lib/jvm/java-17-amazon-corretto.x86_64
echo $JAVA_HOME
# 젠킨스 설치
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo yum upgrade
sudo yum install jenkins -y
sudo systemctl daemon-reload
sudo systemctl enable jenkins && sudo systemctl start jenkins
sudo systemctl status jenkins
# 초기 암호 확인
sudo systemctl status jenkins
cat /var/lib/jenkins/secrets/initialAdminPassword
# 접속 주소 확인
curl -s ipinfo.io/ip | awk '{ print "Jenkins = http://"$1":8080" }'
Jenkins = http://43.202.2.88:8080
패키지 형태로 Jenkins 설치를 진행합니다.
해당 서버에 접속하면, 필수 addon들 설치를 진행합니다.
저는 무슨문제인지 addon 설치들이 진행이되지않아 기본 패키지에서 Jenkins를 다루었습니다.
해당 주소로 접속하여 admin 계정설정등 초기 설정후 신규 Jenkins 실행을 진행해봅니다.
Prerequsite : JDK 설정
Jenkins 관리 → Tools 진입 항목
-
JDK installations : jdk-17 , /usr/lib/jvm/java-17-amazon-corretto.x86_64 를 입력 후 저장
이후 첫번째 Item(프로젝트) 생성을 진행합니다.
- 새로운 Item → Name : First-Project , Freestyle project
- Build Step → Add Build Step → Execute Shell
- echo “AWS Workshop Study”
- java version
- whoami
- touch hello.txt
각각 추가를 해보며 빌드 한 결과들을 첨부해봤습니다.
이제 로컬 환경에서 해당 빌드가 이루어진 디렉토리를 검사해봅니다
#
find / -name First-Project
/var/lib/jenkins/jobs/First-Project
/var/lib/jenkins/workspace/First-Project
# 프로젝트(job, item) 별 작업 공간 확인
tree /var/lib/jenkins/workspace/First-Project
docker build 환경 준비하기
# jenkins 유저로 docker 사용 가능하게 설정
grep -i jenkins /etc/passwd
usermod -s /bin/bash jenkins
grep -i jenkins /etc/passwd
# jenkins 유저 전환
su - jenkins
whoami
pwd
docker info
exit
#
chmod 666 /var/run/docker.sock
usermod -aG docker jenkins
# Jeknins 유저로 확인
su - jenkins
docker info
# Dockerhub로 로그인 하기
docker login
Username: <자신의 계정명>
Password: <자신의 암호>
# myweb:v2.0.0 컨테이너 이미지 생성을 위한 Dockerfile 준비
# 실습을 위한 디렉터리 생성 및 이동
mkdir -p ~/myweb2 && cd ~/myweb2
# Dockerfile 파일 생성
cat > Dockerfile <<EOF
FROM ubuntu:20.04
ENV TZ=Asia/Seoul VERSION=2.0.0 NICK=montkim
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \
sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
apt-get update && apt-get install -y apache2 figlet && \
echo "$NICK Web Server $VERSION<br>" > /var/www/html/index.html && \
echo "<pre>" >> /var/www/html/index.html && \
figlet AEWS Study >> /var/www/html/index.html && \
echo "</pre>" >> /var/www/html/index.html
EXPOSE 80
CMD ["usr/sbin/apache2ctl", "-DFOREGROUND"]
EOF
jenkins또한 docker로 실행이 가능하지만, 해당 실행환경 내에서 Docker 를 호출하는 과정은 꽤 까다롭기때문에 패키지 형태로 설치를 진행했고
Jenkins User에서도 docker 데몬에 접속할 수 있게 설정을 변경한것들이 보입니다.
빌드 파이프라인이나, 사전설정에 별도로 dockerfile 등에 대한 설정이 현재 과정에서는 조금 번거로워, 로컬환경에 직접 Dockerfile을 지정해둔 후, pipeline에만 빌드하는 과정을 담아보겠습니다.
# execute shell
cd /var/lib/jenkins/myweb2
docker build -t myweb:v2.0.0 .
# add step
docker run -d -p 80:80 --rm --name myweb myweb:v2.0.0
파라미터와 Git 기반으로도 Triggering 및 파이프라인 변수 처리가 가능합니다.
근데 Jenkins 설정과정에서… 플러그인 설치가 안되어 “소스코드 관리” 항목의 진행이 안되어… 넘어가겠습니다.
Jenkins에서 Kubernets 적용하기
# jenkins 사용자에서 아래 작업 진행
whoami
mkdir ~/.kube
# root 계정에서 아래 복사 실행
cp ~/.kube/config /var/lib/jenkins/.kube/config
chown jenkins:jenkins /var/lib/jenkins/.kube/config
# jenkins 사용자에서 aws eks 사용(sts 호출 등)을 위한 자격증명 설정
su jenkins
aws configure
AWS Access Key ID [None]: AKIA5ILF2###
AWS Secret Access Key [None]: ###
Default region name [None]: ap-northeast-2
# jenkins 사용자에서 kubectl 명령어 사용 확인
kubectl get pods -A
jenkins 사용자 이름으로 kube config와 aws configure를 진행하여 user가 kuberentes cli가 사용가능한지 확인합니다.
add item에서 PIPELINE을 선택해 해당 내용을 넣어줍니다.
- 해당 과정은 Jenkins 설치시 애드온설정이 불가능하여 Text로 진행합니다.
https://github.com/idoyo7/aews-cicd
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 2
selector:
matchLabels:
app: mywebs
template:
metadata:
name: myweb
labels:
app: mywebs
spec:
containers:
- name: myweb
image: idoyo7/myweb:v1.0.0
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
ports:
- name: webport
port: 8080
targetPort: 80
selector:
app: mywebs
type: ClusterIP
---
코드를 일부 수정해 다음 파라미터로 설정된 git 을 반영했습니다.
파이프라인에서 사용할 파라미터들은 “이 빌드는 매개변수가 있습니다” 항목에서 변수로 추가가 가능합니다.
pipeline {
agent any
tools {
jdk 'jdk-17'
}
environment {
DOCKERHUB_USERNAME = 'idoyo7'
GITHUB_URL = 'https://github.com/idoyo7/aews-cicd.git'
DIR_NUM = '3'
}
stages {
stage('Container Build') {
steps {
// 릴리즈파일 체크아웃
checkout scmGit(branches: [[name: '*/main']],
extensions: [[$class: 'SparseCheckoutPaths',
sparseCheckoutPaths: [[path: "/${DIR_NUM}"]]]],
userRemoteConfigs: [[url: "${GITHUB_URL}"]])
// 컨테이너 빌드 및 업로드
sh "docker build -t ${DOCKERHUB_USERNAME}/myweb:v1.0.0 ./${DIR_NUM}"
sh "docker push ${DOCKERHUB_USERNAME}/myweb:v1.0.0"
}
}
stage('K8S Deploy') {
steps {
sh "kubectl apply -f ./${DIR_NUM}/deploy/deployment-svc.yaml"
}
}
}
}
pipeline의 변수는 별도로 관리되어 다른 사용자가 함부로 볼 수 없게 설정이 가능합니다.
별도로 Gitops를 완성시키는데 있어 jenkins 설정 내의 pipeline 파일이 아닌, jenkinsfile을 선언적으로 관리해 git안에 파이프라인의 정보를 직접 설정할수도 있습니다.
그 과정에서도 역시 변수들에 대한 설정은 프로젝트에서 관리를 하기때문에 보다 더 안전하게 가져갈수있다는 장점이 있습니다.
선언적으로 파이프라인을 관리하는것은 좋지만
Groovy 문법 기반으로 작성이 되는데, 이해하기 어렵지는 않지만 기존에 사용하던 형태와는 조금 다른부분들이 있어 조금은 번거로운 과정이 될 수 있을것 같습니다.
ArgoCD
설치
# helm 설치
cat <<EOT > argocd-values.yaml
global:
domain: argocd.$MyDomain
configs:
params:
server.insecure: true
controller:
metrics:
enabled: true
serviceMonitor:
enabled: true
server:
ingress:
enabled: true
controller: aws
ingressClassName: alb
hostname: "argocd.$MyDomain"
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/backend-protocol: HTTP
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
#alb.ingress.kubernetes.io/success-codes: 200-399
#alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
#alb.ingress.kubernetes.io/group.name: study
alb.ingress.kubernetes.io/ssl-redirect: '443'
aws:
serviceType: ClusterIP
backendProtocolVersion: GRPC
metrics:
enabled: true
serviceMonitor:
enabled: true
repoServer:
metrics:
enabled: true
serviceMonitor:
enabled: true
applicationSet:
metrics:
enabled: true
serviceMonitor:
enabled: true
notifications:
metrics:
enabled: true
serviceMonitor:
enabled: true
EOT
kubectl create ns argocd
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 6.7.11 -f argocd-values.yaml --namespace argocd
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
해당 설정으로 프로비저닝후 로그인을 진행합니다
admin / secret value
설정으로 로그인후 어플리케이션 등록을 진행합니다.
- Application Name : first-myweb
- Project Name : default
- SYNC POLICY : Manual
- AUTO-CREATE NAMESPACE : 클러스터에 네임스페이스가 없을 시 argocd에 입력한 이름으로 자동 생성
- APPLY OUT OF SYNC ONLY : 현재 동기화 상태가 아닌 리소스만 배포
- PRUNE PROPAGATION POLICY
- foreground : 부모(소유자, ex. deployment) 자원을 먼저 삭제함
- background : 자식(종속자, ex. pod) 자원을 먼저 삭제함
- orphan : 고아(소유자는 삭제됐지만, 종속자가 삭제되지 않은 경우) 자원을 삭제함
- [체크] AUTO-CREATE-NAMESPACE
- SOURCE
- Repository URL : https://github.com/gasida/aews-cicd.git
- Revision : main
- Path : 3/deploy
- DESTINATION
- Cluster URL : https://kubernetes.default.svc
- Namespace : first
- [선택] Directory ← 소스를 보고 자동으로 유형 선택됨
- 화면 상단 [CREATE] 클릭
- 배포하기 - [SYNC] 클릭 > [SYNCHRONIZE] 클릭
- PRUNE : GIt에서 자원 삭제 후 배포시 K8S에서는 삭제되지 않으나, 해당 옵션을 선택하면 삭제시킴
- FORCE : –force 옵션으로 리소스 삭제
- APPLY ONLY : ArgoCD의 Pre/Post Hook은 사용 안함 (리소스만 배포)
- DRY RUN : 테스트 배포 (배포에 에러가 있는지 한번 확인해 볼때 사용)
해당 설정들을 통해 배포를 진행해봅니다.
ArgoCD CLI 설치하기
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm -f argocd-linux-amd64
# CLI 버전 확인
argocd version
# 로그인
argocd login argocd.$MyDomain
<로그인하기>
kubectl config get-contexts -o name
# KOPS@myeks.ap-northeast-2.eksctl.io
argocd cluster add admin@myeks.ap-northeast-2.eksctl.io
y # 입력
#argocd app 조회하기
argocd app list
실제로 Argocd를 쓰면서 Web위주로 확인을 하여 CLI를 사용해본건 이번이 처음이였습니다.
CLI를 통한 Applliation 생성
#
kubectl config set-context --current --namespace=argocd
argocd app create guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-server https://kubernetes.default.svc --dest-namespace default
#
argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
argocd/guestbook https://kubernetes.default.svc default default OutOfSync Missing <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook
web을 통해서 설정했던것들 또한 cli를 통해 파라미터로 동일하게 설정이 가능합니다.
Sync with CLI
# arogcd app 조회
argocd app get guestbook
...
# 모니터링
watch -d kubectl get pod,svc,ep
# sync 진행
argocd app sync guestbook
sync 완료된것을 확인했습니다.
이제 반대로 위에 생성했던 first-myweb 프로젝트의 소스중
service 를 ClusterIP → LoadBalancer로 설정했습니다.
sync 가 이루어지지않아 OutOfSync 과정이 되어있는것을 확인할수있는데요
argocd app sync first-myweb
명령어를 통해 Sync를 진행하고 LB의 생성을 지켜봅니다
live manifest에서도 CLB이긴하지만, loadbalancer에 관한 정보를 확인할 수 있었습니다.
- 근데 LB는 K8S의 Desired state가 아니기때문에 별도로 LB자체의 정보가 보이진않네요
Argo Rollout
Argo Rollouts는 Kubernetes 환경에서 진보된 배포 전략을 제공하는 오픈 소스 도구로 Kubernetes 배포 기능을 확장하여, 특히 블루/그린(blue/green) 또는 카나리(canary) 배포와 같은 고급 배포 전략을 구현할 수 있도록 설계되었습니다.
- 카나리 배포: 점진적으로 새 버전을 소수의 사용자에게 먼저 제공하고, 이를 모니터링하면서 점차적으로 더 많은 트래픽으로 확장합니다. 문제 발생 시 즉시 롤백할 수 있습니다.
- 블루/그린 배포: 새로운 버전(그린)과 이전 버전(블루)을 동시에 운영하면서, 테스트 후 문제가 없을 경우 트래픽을 새 버전으로 완전히 전환합니다.
- 무중단 배포: 사용자의 서비스 중단 없이 새로운 버전으로의 전환을 보장합니다.
- 롤백 기능: 배포에 문제가 생길 경우 이전 상태로 쉽게 되돌릴 수 있는 기능을 제공합니다.
https://argoproj.github.io/argo-rollouts/architecture/
Argo Rollout 설치를 진행합니다
#
cat <<EOT > argorollouts-values.yaml
dashboard:
enabled: true
ingress:
enabled: true
ingressClassName: alb
hosts:
- argorollouts.$MyDomain
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/backend-protocol: HTTP
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/ssl-redirect: '443'
EOT
kubectl create ns argo-rollouts
helm install argo-rollouts argo/argo-rollouts --version 2.35.1 -f argorollouts-values.yaml --namespace argo-rollouts
# 확인
kubectl get all -n argo-rollouts
kubectl get crd | grep argo
Rollout 에 사용할 컴포넌트 배포
# Run the following command to deploy the initial Rollout and Service:
kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml
롤아웃 전략은 다음과같이 설정되어있어 점진적인 배포전략을 세울 수 있습니다.
Argo Rollout CLI 설치
#
curl -LO https://github.com/argoproj/argo-rollouts/releases/download/v1.6.4/kubectl-argo-rollouts-linux-amd64
chmod +x ./kubectl-argo-rollouts-linux-amd64
mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
# 설치 확인
kubectl argo rollouts version
실제로 배포했던 컴포넌트들은
CLI로 Canary Update
# 이미지 변경
kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow
# 확인
kubectl argo rollouts get rollout rollouts-demo
웹상에서 Promote 여부를 설정하여 롤아웃 전략대로 업데이트를 진행 할 수 있습니다.
이 역시 CLI 기반으로도 가능하며 다음과 같습니다
# promote
kubectl argo rollouts promote rollouts-demo
# 확인
kubectl argo rollouts get rollout rollouts-demo
웹에서도 테스트 가능한 과정인데, CLI로만 진행해보니 또 감회가 새롭네요
promote 말고 abort 도 가능합니다
kubectl argo rollouts abort rollouts-demo