
CI 란? 개발자를 위한 자동화 프로세스인 지속적인 통합 ( Continuous Integration )
어플리케이션의 새로운 코드 변경 사항이 정기적으로 빌드/ 테스트 되어 공유 Repository에
(ex. git,github) 통합하는것을 의미한다. 다수의 개발자가 작업할 경우 레포지토리에 쌓이는
commit들이 충돌하는 것을 자동화된 빌드와 테스트로 방지할수 있다.
CD 란? 지속적인 배포 ( Continuous Deployment )“수동적"으로 배포하는것을 지속적인 제
공이라 하는데, 이것을“자동화”하는것이지속적인 배포 ( Continuous Deployment ) 이다.
어플리케이션 개발 단계 부터 배포 때 까지 모든 단계들을 자동화를 통해서 사용자에게 배
포 할수있도록 만드는 것 즉, CI/CD란 각각의 개발자들이 개발을 하는 개발환경을 사용자가
사용 가능한 서비스로 전달하는 모든 과정을 지속 가능한 형태로 또 가능하다면 자동으로 해
서 개발자와 사용자 사이의 격차를 없애는 것이다. 이러한 과정에는 코드를 빌드하고, 테스트
하고 배포하는 활동이 있다.
가장 많이 사용되는 도구 5가지
GCP,Jenkins,ArgoCD를 사용하여 CI/CD 체계를 구축을 해보자
- K8S(Google Kubernetes Engine(GKE))
- Jenkins-Pipeline
- ArcoCD
- GitHub
- DockerHub
- Spring boot

CI: GCP VM에 Jenkins 설치 -> github에 Jenkinsfile 생성 (단계별 코드 통합 과정 진행) -> Jenkins-Pipeline구축 -> 새로운 이미지 생성 및 태깅 -> Dockerhub Push -> 별도의 manifest repo안 deployment.yaml을 새로운 이미지 태그로 업데이트 -> github webhook를 jenkins에 연동
CD: GKE 활성화 및 클러스터 생성 -> ArgoCD 설치 -> ArgoCD 페이지에서 Repository 생성 -> ArgoCD 페이지
에서 Application 생성 -> 배포 자동화 확인

- API 사용 확인
[v] Google Kubernetes Engine API(컴퓨팅 > Kubernetes Engine API)- GKE 클러스 만들기
-> 컴퓨팅 > Kubernetes Engine > 클러스터 > 만들기 > GKE Standard > 구성

-> 주소 복사후 CLOUD SHEEL 붙여넣기
$ kubectl get nodes

-> kubectl 활성화 확인

로컬에서 ssh연결을 해서 들어간다.

Jenkins설치를 하면 8080포트를 통해 로컬에서 들어가야하기 때문에 방화벽을 미리 설정해준다.

도커를 os에 맞게 설치하고 jenkins를 설치해준다.
도커 다운로드 설명 링크
docker run -d -p 8080:8080 --name jenkins -v /home/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -u root jenkins/jenkins:lt
docker exec jenkins apt update
docker exec jenkins apt install -y docker.io

로컬 PC에서 VM의 외부 IP로 URL 접속 후 jenkins 설치 비밀번호 입력해준다.
jenkins 비밀번호를 까먹으신분은 jenkins docker log를 다시확인하여 비밀번호를 알 수 있다.
docker logs (jenkin-container-name)

-Jenkins Pipeline을 통해 이미지를 빌드할 docker-hub repository를 생성해준다.
- 추후 사용하게 될 Jenkinsfile에 토큰이 필요하니 dockerhub token을 생성하고 잘 기록해둔다.

- 새로운 item -> 제목 입력후 -> 파이프라인 선택
- github porject -> jenkinfile을 만들어둔 자신의 Git Repository 입력
- GitHub hook trigger for GITScm polling 체크
-> 추후에 github webhook연동을 해야하므로 체크한다.

- Repository URL에 자신의 깃허브 주소를 입력한다
- 자신이 설정한 Branch로 변경해준다.
- *Add를 통해 깃허브 credentials를 입력해준다.

- Kind: git
- username: Git ID
- Git Password: Git Token
- ID: Jenkinsfile에 작성할 ID 작성

- Jenkins 관리 -> plugins -> Available plugins -> Docker Pipeline설치
- Jenkins Pipeline을 이용해 Docker hub에 이미지를 빌드하기 위해선 설치해야한다.


- 도커허브에 이미지가 푸쉬하기 위해 credential발급
- Dashboard -> Jenkins 관리 -> Credentials -> Global credentials -> add
- Username: Docker ID 입력
- Password: Docker Secret Token 값 입력
- ID: Jenkinsfile에 작성할 ID 작성

# 첫 번째 단계: 애플리케이션 빌드
FROM openjdk:11-jdk AS builder
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN ./gradlew bootJar
# 두 번째 단계: 최종 실행 이미지 생성
FROM openjdk:11-slim
# 첫 번째 단계에서 생성된 JAR 파일을 최종 이미지로 복사
COPY --from=builder build/libs/*.jar springboot-sample-app.jar
VOLUME /tmp
EXPOSE 8080
# 애플리케이션 실행 명령 설정
ENTRYPOINT ["java", "-jar", "/springboot-sample-app.jar"]
위 Dockerfile은 Spring Boot 애플리케이션을 컨테이너화하고 배포하기 위한 파일이다.
pipeline{
agent any
environment {
dockerHubRegistry = 'dongjukim123/docker' // dockerHub에 repository 명
dockerHubRegistryCredential = 'docker-hub' // Jenkins에서 생성한 dockerhub-credential-ID값
githubCredential = 'github' // Jenkins에서 생성한 github-credential-ID값
}
// 1. git repository 가 체크되는지 확인, 제대로 연동이 안될 경우, 이 단계(stage) 에서 fail 발생
stages {
stage('check out application git branch'){
steps {
checkout scm
}
post {
failure {
echo 'repository checkout failure'
}
success {
echo 'repository checkout success'
}
}
}
// 2. gradle 빌드 (springboot web 생성을 위한 선행과정)
stage('build gradle') {
steps {
sh './gradlew build'
sh 'ls -al ./build'
}
post {
success {
echo 'gradle build success'
}
failure {
echo 'gradle build failed'
}
}
}
// 3. Dockefile build
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 !'
}
success {
echo 'Docker image build success !'
}
}
}
// 4. 빌드된 이미지 push
stage('Docker Image Push') {
steps {
withDockerRegistry([ credentialsId: dockerHubRegistryCredential, url: "" ]) {
sh "docker push ${dockerHubRegistry}:${currentBuild.number}"
sh "docker push ${dockerHubRegistry}:latest"
sleep 10 /* Wait uploading */
}
}
post {
failure {
echo 'Docker Image Push failure !'
sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
success {
echo 'Docker image push success !'
sh "docker rmi ${dockerHubRegistry}:${currentBuild.number}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
}
}
// 5. 쿠버네티스 배포 작업
stage('K8S Manifest Update') {
steps {
sh "ls"
sh 'mkdir -p gitOpsRepo'
dir("gitOpsRepo")
{
git branch: "main",
credentialsId: githubCredential,
url: 'https://github.com/dongjucloud/kube-manifests.git'
sh "git config --global user.email dongju08@naver.com"
sh "git config --global user.name dongjucloud"
// 배포될 때 마다 버전이 올라야 하므로 deployment.yaml 에서 ksw7734/docker:버전 을 sed
-i 로 ${currentBuild.number} 변수를 이용해 변경
sh "sed -i 's/docker:.*\$/docker:${currentBuild.number}/' deployment.yaml"
sh "git add deployment.yaml"
sh "git commit -m '[UPDATE] k8s ${currentBuild.number} image versioning'"
withCredentials([gitUsernamePassword(credentialsId: githubCredential,
gitToolName: 'git-tool')]) {
sh "git remote set-url origin https://github.com/dongjucloud/kube-manifests"
sh "git push -u origin main"
}
}
}
post {
failure {
echo 'K8S Manifest Update failure !'
}
success {
echo 'K8S Manifest Update success !'
}
}
}
}
}
이 파이프라인을 통해 새로 푸쉬된 이미지 태그를 갈아끼운다 -> yaml수정
배포할 manifests 작성

kubernetes 배포 작업을 위한 deplyment.yaml, service.yaml 파일 작성
service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: k8s
name: k8s
spec:
type: LoadBalancer
selector:
app: k8s
ports:
- port: 8080
targetPort: 8080
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: k8s
name: k8s
spec:
replicas: 3
selector:
matchLabels:
app: k8s
template:
metadata:
labels:
app: k8s
spec:
containers:
- name: k8s
image: dongjukim123/docker:25
ports:
- containerPort: 8080


- git repository -> setting -> webhook
- Payload URL : http://(jenkins IP:Jenkins port)/github-webhook/?job=(파이프라인)
- Content Type : application/json
- Just the push event

jenkinsfile에서 일부러 오류를 내보았더니 webhook이 변경된 소스를 인식해 자동으로 파이프라인이 작동하였다.
-> 잘못된 jenkinsfile 입력으로 실패한 모슴

- 새로운 이미지 태그로 변경된 manifest를 통해 ArgoCD로 배포한다.
$ kubectl create namespace argocd
-> argocd 생성 및 적용을 위한 네임스페이스 생성
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
-> 외부에서 접근을 위해 argo server service의 ClusterIP를 LoadBalancer로 변경해준다.
$ kubectl get pod,svc –n argocd
-> argocd 가 위와 같이 구성 확인

Username = admin
Password = ***
콘솔에서 패스워드 확인
$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}"
| base64 –d

- 연결방식을 HTTPS로 설정
- Type: git
- Project: default
- Reopsitory URL: jenkinfile이 있는 자신의 github url
- Password: 자신의 Github token

Connection Status 가 Successful 이면 성공

- Application Name: 배포할 Application명 입력
- Project Name: default
- SYNC 정책은 자동설정

Repository URL: Gitgub Manifests URL
- Branch:maini
Path: manifests가 존재하는 경로 URL로 접속시, Manifests가 존재한다면 현재경로(".")- namespcae이름은 미리 cloudshell에서 새로 만들어 주도록 하자(springboot-ns 생성)


- 소스를 변경시키면 deplyment.yaml파일을 통해 자동으로 배포가 된다.
- 외부 IP접속을통해 배포가 잘되었는지 확인해주자.
