나의 공부기록

[Kubernetes] 18. Jenkins를 통한 CI/CD 본문

CS/Kubernetes

[Kubernetes] 18. Jenkins를 통한 CI/CD

나의 개발자 2025. 5. 10. 16:27

Jenkins를 통한 CI/CD

✅ 사전 세팅

더보기

1. Cluster 생성

export PRI_SUBNET1_ID=subnet-0b0e6
export PRI_SUBNET2_ID=subnet-09a7c
export CLUSTER_NAME=pri1-cluster
export ACCOUNT_ID=7981
export VPC_ID=vpc-0350
export REGION=ap-northeast-2

eksctl create cluster --vpc-private-subnets $PRI_SUBNET1_ID,$PRI_SUBNET2_ID --name pri-cluster --region ap-northeast-2 --version 1.32 --nodegroup-name pricng --node-type t3.small --nodes 3 --nodes-min 2 --nodes-max 5 --node-private-networking

 

2. LB Controller 생성

aws sts get-caller-identity

eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve

eksctl create iamserviceaccount   --cluster=$CLUSTER_NAME   --namespace=kube-system   --name=aws-load-balancer-controller   --role-name AmazonEKSLoadBalancerControllerRole   --attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy   --override-existing-serviceaccounts   --approve

kubectl get sa -n kube-system | grep -i load

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=$CLUSTER_NAME \
--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 \
--set region=ap-northeast-2 \
--set vpcId=$VPC_ID

Jenkins - OpenSource CI 도구

  • 오픈소스 CI 도구인 Jenkins의 경우, 로컬 환경(private network)에 설치하면 내가 네트워크 관리자여서 Port Forwarding을 제어할 수 있는 상황이 아니라면 webhook이 불가능
    ➡️ 해결 방법 : 클라우드에 인스턴스를 생성하여 Jenkins를 구성할 예정

 

EKS가 설치된 VPC의 Public Subnet에 Jenkins 설치

더보기

1. EC2 생성

  • ⚠️주의 - 인스턴스 유형은 최소 small
  • 어떤 포트를 사용하는지 모르기 때문에, 일단은 모든 포트 허용

 

2. 생성한 EC2 인스턴스 접속 & 업데이트

ubuntu@ip-10-20-1-144:~$ sudo apt update -y
ubuntu@ip-10-20-1-144:~$ sudo -i
ubuntu@ip-10-20-1-144:~#

 

3. Jenkins 설치하기 위한 레포지토리 추가

# Jenkins 키 가져오기
wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null

# Jenkins 저장소 추가
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

 

4. Java 설치

  • Jenkins를 실행하려면 Java가 설치되어 있어야 함
  • Java가 설치되어 있지 않으면 오류가 발생할 수 있음
sudo apt update
sudo apt install openjdk-17-jdk -y

 

5. Jenkins 재시작

sudo systemctl start jenkins
sudo systemctl enable jenkins

 

 6. Jenkins 로그인

 

7. Gradle, AWS 플러그인 설치 & 사용자 생성

  • Jenkins는 플러그인을 통해 원하는 환경을 구성 가능
  • Plugin 설치 시, 오류가 발생하면 인스턴스 유형(small⬆️)이나 포트 의심(보안그룹)
  • 계정명 : admin / 암호 : test123

 

✅ Jenkins에서 주로 볼 곳

 

8. Plugin 설치 - Docker & SSH & Slack

  • Docker & Docker Pipeline : 이미지 빌드 & PUSH 필요
  • Publish Over SSH : Github Push할 때 필요
  • Slack Notification : 완료 시, 알람을 위해
  • Maven : App 빌드를 위해 필요
     ➡️ 추후에 설치 & 설정할 예정

 

 9. Docker Hub 로그인

  • username : etoile0320 ➡️ 나중에 등록할 예정

 

10. 플러그인 설치 완료 ➡️ Jenkins 재시작 : 로그인  

  • ⚠️주의 : 나중에 Jenkins 빌드를 많이 하면, 디스크 용량이 부족한 경우가 발생
    ➡️ 그럴 때는 EBS를 늘리면 됨

 

 11. Plugin 설치 확인

 

12. Credential 생성

  • Docker 자격증명 설정

 

13. Github Fork - 간단한 스프링 앱

 

14. Jenkins 상의 프로젝트 생성 - Pipeline

  • SCM(Source Code Management) - Git에 CI/CD 스크립트를 저장
  • Credential 생성 - Github
  • Github 레포지토리의 블랜치 명과 일치해야 함❗
  • 스크립트 파일 경로 : /Jenkins

15. Jenkins 프로젝트 생성 확인  

 

16. Plugin 설치 - Maven

 

17. Tool - Maven이라는 도구 정의

 

18. 지금 빌드 ➡️ 실패 : Git 레포는 확인했지만, jenkinsfile을 찾지 못함

 

 

💡 Jenkinsfile이라는 스크립트에서 해야 할 것

1️⃣ 소스코드에 변경사항이 생겼을 때, git clone
2️⃣ App 빌드 (Maven) ➡️ target/springbootApp.jar 파일 생성
3️⃣ target/springbootApp.jar을 Docker build
4️⃣ 컨테이너 레지스트리(Docker Hub)에 Push
5️⃣ GitHub 레포지토리를 소스 레포지토리에서 deploy 레포로 변경 후, git clone
6️⃣ deployment.yml 파일의 이미지 태그 수정 후, Push

 

19. github - webhook 환경 구성

✅ webhook 설정 완료

 

20. Git 레포지토리에 Jenkinsfile 추가

pipeline {
    agent any
   
    stages {
        stage('start') {
            steps {
                sh "echo hello jenkins!!!"
            }
            post {
                failure {
                    sh "echo failed"
                }
                success {
                    sh "echo success"
                }
            }
        }
    }
}

 

21. 지금 빌드 ➡️ 성공 확인

 

  • webhook 확인을 위해 소스코드 변경

 

22. webhook으로 Jenkins Build가 자동으로 수행되는지 확인 = webhook 성공

 

  • 위에서 정리한 절차대로 스크립트를 짜면 된다.
  • VSCode 접속 - Github 열기

 

23. Jenkinsfile - 환경변수 설정

pipeline {
    agent any

# ✅ 추가된 부분
    tools {
    	# Jenkins에서 생성한 Mavne명
        maven 'my_maven'
    }
    environment {
        GITNAME = 'BoHyun-Choi-0320'            
        GITEMAIL = '320fleur@gmail.com' 
        GITWEBADD = 'https://github.com/BoHyun-Choi-0320/simple_sb.git'
        GITSSHADD = 'git@github.com:BoHyun-Choi-0320/simple_sb.git'
        GITCREDENTIAL = 'git_cre'
        
        DOCKERHUB = 'etoile0320/spring'
        DOCKERHUBCREDENTIAL = 'docker_cre'
    }


    stages {
        stage('Checkout Github') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [],
                userRemoteConfigs: [[credentialsId: GITCREDENTIAL, url: GITWEBADD]]])

            }
            post {
                failure {
                    sh "echo clone failed"
                }
                success {
                    sh "echo clone success"
                }
            }
        }
        stage('springboot app build') {
            steps {
                sh "mvn clean package"
            }
            post {
                failure {
                    sh "echo mvn packaging failed"
                }
                success {
                    sh "echo mvn packaging success"
                }
            }
        }
        stage('docker image build') {
            steps {
                sh "docker build -t ${DOCKERHUB}:${currentBuild.number} ."
                sh "docker build -t ${DOCKERHUB}:latest ."
                // currentBuild.number 젠킨스가 제공하는 빌드넘버 변수
                // oolralra/fast:<빌드넘버> 와 같은 이미지가 만들어질 예정.
               
            }
            post {
                failure {
                    sh "echo image build failed"
                }
                success {
                    sh "echo image build success"
                }
            }
        }
        stage('docker image push') {
            steps {
                withDockerRegistry(credentialsId: DOCKERHUBCREDENTIAL, url: '') {
                    sh "docker push ${DOCKERHUB}:${currentBuild.number}"
                    sh "docker push ${DOCKERHUB}:latest"
                }
            }
            post {
                failure {
                    sh "docker image rm -f ${DOCKERHUB}:${currentBuild.number}"
                    sh "docker image rm -f ${DOCKERHUB}:latest"
                    sh "echo push failed"
                    // 성공하든 실패하든 로컬에 있는 도커이미지는 삭제
                }
                success {
                    sh "docker image rm -f ${DOCKERHUB}:${currentBuild.number}"
                    sh "docker image rm -f ${DOCKERHUB}:latest"
                    sh "echo push success"
                    // 성공하든 실패하든 로컬에 있는 도커이미지는 삭제
                }
            }
        }
        stage('EKS manifest file update') {
            steps {
                git credentialsId: GITCREDENTIAL, url: GITSSHADD, branch: 'main'
                sh "git config --global user.email ${GITEMAIL}"
                sh "git config --global user.name ${GITNAME}"
                sh "sed -i 's@${DOCKERHUB}:.*@${DOCKERHUB}:${currentBuild.number}@g' fast.yml"

                sh "git add ."
                sh "git branch -M main"
                sh "git commit -m 'fixed tag ${currentBuild.number}'"
                sh "git remote remove origin"
                sh "git remote add origin ${GITSSHADD}"
                sh "git push origin main"
            }
            post {
                failure {
                    sh "echo manifest update failed"
                }
                success {
                    sh "echo manifest update success"
                }
            }
        }
       

    }
    
}

 

24. webhook으로 자동 빌드 ➡ 실패 : Docker 미설치

  • docker 미설치 - Jenkins build 오류 발생 

 

  • EC2 인스턴스에서 Docker 설치
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

 

25. 다시 빌드 ➡ 실패 : Jenkins의 권한 부족

  • Jenkins는 명령을 치는 주체가 jenkins라는 리눅스 사용자이기 때문에
    ➡️ jenkins라는 리눅스 사용자에게 권한을 부여해야 함
  • jenkins라는 사용자를 docker라는 그룹에 add해주는 명령어
usermod -aG docker jenkins

# 변경사항 발생했으므로 jenkins 재시작
systemctl restart jenkins

 

26. 다시 빌드 ➡ 실패 : Gihub와 SSH 통신 문제 발생

  • Docker 이미지 PUSH는 성공된 것을 확인 가능
  • Github와 SSH 통신 문제
  •  Jenkins 파일 수정

 

✅ SSH 통신을 위한 인증 방법

1. ID/PW 방식
2. public key 방식(key pair)
➡️ 클라이언트(Jenkins) = private key / 서버(Github) = public key

 

  • EC2 인스턴스에서 Key Pair 생성
# EC2 관리자 권한
ubuntu@ip-10-20-1-144:~$ sudo -i

# jenkins라는 사용자 전환
root@ip-10-20-1-144:~# su jenkins
jenkins@ip-10-20-1-144:/root$ cd ~
jenkins@ip-10-20-1-144:~$ pwd
/var/lib/jenkins #  ➡️  jenkins라는 사용자의 home 디렉토리

# jenkins에서 Key Pair 생성
jenkins@ip-10-20-1-144:~$ ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -N '' -q

# Key Pair 확인 : id_rsa ➡️ Priave / id_rsa.pub ➡️ Public
jenkins@ip-10-20-1-144:~$ ls ~/.ssh
id_rsa	id_rsa.pub

 

  • Github에 Public Key 등록
    • Github의 모든 레포지토리에 SSH Key 등록
  • Jenkins에 Gihub 사용자 추가
    • Github 사용자 : git
  •  내 known_hosts에 등록하기 위해 github 서버에 ssh 접속을 1회 실시
jenkins@ip-10-20-1-144:~$ ssh git@github.com

 

  • 중간에 fail이 떠도 상관없음

 

27. Jenkinsfile 수정 : fast.yml ➡️ deploy.yml

 

  • dep/deploy.yml 수정 - Jenkinsfile에 맞게 이미지 수정

 

28. 다시 빌드 ➡ 성공

  • Jenkins에서 빌드가 성공해서 deploy.yml의 이미지 태그가 수정됨을 확인 가능

 

ArgoCD 설치해서 앱 배포가 잘 되게 해 보세요.

29. ArgoCD 설치

kubectl create ns argocd

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

eksctl scale nodegroup --cluster pri1-cluster --nodes 5 --nodes-max 7

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

 

30. 소스코드 수정

 

31. webhook 자동 빌드 ➡ 성공

32. ArgoCD 설정

  • Github 레포지토리 연동
  • App 배포

 

33. 결과 확인