Post

MSA Deploy (3) - CI/CD & Harbor, Jenkins

MSA Deploy (3) - CI/CD & Harbor, Jenkins

5. CI/CD 환경

virtualbox에 vagrant로 프로비저닝한 환경에 harbor와 jenkins를 구성하기엔 리소스가 모자랄 것으로 예상해 로컬 PC 기반 CI/CD + k8s 배포 파이프라인 구축

1
2
3
4
5
6
7
8
9
[개발 코드]
   ↓
[Jenkins (빌드)]
   ↓
[Docker 이미지 생성]
   ↓
[Harbor (이미지 저장소)]
   ↓
[Kubernetes (이미지 pull → 배포)]

5-1. 작업 순서

  1. Harbor 구축 (이미지 저장소)
    • Docker Registry 역할
    • k8s가 여기서 이미지 pull
  2. Jenkins 구축 (CI/CD 서버)
    • 코드 빌드
    • Docker 이미지 생성
    • Harbor로 push
  3. Docker ↔ Harbor 연결
    • docker login
    • 이미지 push 테스트
  4. Kubernetes ↔ Harbor 연결
    • imagePullSecret 설정
    • Pod에서 Harbor 이미지 pull 가능하게
  5. Jenkins Pipeline 구성 (Jenkinsfile)
    • git pull
    • build
    • docker build
    • docker push
    • kubectl apply

5-2. harbor

5-2-1. 설치

harbor는 nginx(gateway), harbor-core(API), harbor-db(postgres), redis, registry로 구성된 마이크로 서비스 구조
여러 컨테이너를 묶어서 실행하기 위해 docker compose로 설치

1
wsl --install -d Ubuntu

Docker Desktop WSL integration 기능이 Ubuntu를 Docker CLI를 사용 가능한 상태로 연결해 Ubuntu WSL에서 작업한 내용이 docker-desktop에 전달됨

1
2
3
4
5
6
7
[Ubuntu WSL]  ← 작업하는 환경
      ↓
[docker CLI]
      ↓
[docker-desktop WSL (Docker Engine)]
      ↓
[컨테이너 실행]

docker-desktop WLS 특징

  • 도커를 돌리기 위한 내부 OS
  • 실제 Docker 서버(엔진)
  • Docker 엔진 전용 내부 환경
  • 최소 구성 (busybox 수준)
  • 패키지 설치 거의 불가
  • sudo, apt, systemctl 없음
  • Harbor 같은 복잡한 서비스 설치 불가

Ubuntu WSL이 필요한 이유

  • 실제 서버처럼 사용하는 리눅스
  • 완전한 리눅스 환경
  • apt 사용 가능
  • sudo 가능
  • 패키지 설치 가능
  • Harbor / Jenkins 설치 가능

Ubuntu WSL 초기설정

wsl.exe -d Ubuntu
1
2
3
4
5
6
7
8
9
# harbor 다운로드
wget https://github.com/goharbor/harbor/releases/download/v2.15.0/harbor-online-installer-v2.15.0.tgz

# 압축 해제 
tar xzvf harbor-online-installer-v2.15.0.tgz

# 설정 파일 생성
cd harbor
cp harbor.yml.tmpl harbor.yml

마스터, 워커 노드에서 접근할 수 있도록 harbor.yml에서 hostname을 수정하고 https를 비활성화

1
2
3
4
5
6
7
8
vim harbor.yml

hostname: localhost
...
# https:
#   port: 443
#   certificate: /your/cert/path
#   private_key: /your/key/path

설치 후 harbor 위치 이동

1
mv /mnt/c/harbor ~/

설치 성공 확인

1
2
3
4
5
6
7
8
9
10
11
gram@DESKTOP-TQV062L:~/harbor$ docker ps
CONTAINER ID   IMAGE                                 COMMAND                  CREATED          STATUS                             PORTS                       NAMES
11379e03240b   goharbor/harbor-jobservice:v2.15.0    "/harbor/entrypoint.…"   15 seconds ago   Up 9 seconds (health: starting)                                harbor-jobservice
b2111739189c   goharbor/nginx-photon:v2.15.0         "nginx -g 'daemon of…"   15 seconds ago   Up 12 seconds (health: starting)   0.0.0.0:80->8080/tcp        nginx
09257b053a54   goharbor/harbor-core:v2.15.0          "/harbor/entrypoint.…"   15 seconds ago   Up 13 seconds (health: starting)                               harbor-core
b8a8112c071a   goharbor/harbor-registryctl:v2.15.0   "/home/harbor/start.…"   15 seconds ago   Up 13 seconds (health: starting)                               registryctl
82c7b42d56bd   goharbor/registry-photon:v2.15.0      "/home/harbor/entryp…"   15 seconds ago   Up 13 seconds (health: starting)                               registry
a8a3a86ebcab   goharbor/harbor-portal:v2.15.0        "nginx -g 'daemon of…"   15 seconds ago   Up 13 seconds (health: starting)                               harbor-portal
00a9c89521b9   goharbor/redis-photon:v2.15.0         "redis-server /etc/r…"   15 seconds ago   Up 13 seconds (health: starting)                               redis
f048b958637b   goharbor/harbor-db:v2.15.0            "/docker-entrypoint.…"   15 seconds ago   Up 13 seconds (health: starting)                               harbor-db
1c504cd1c846   goharbor/harbor-log:v2.15.0           "/bin/sh -c /usr/loc…"   15 seconds ago   Up 14 seconds (health: starting)   127.0.0.1:1514->10514/tcp   harbor-log

로컬PC에서 harbor 접속 확인

harbor 접속

Harbor 설치 환경

  • Windows + WSL2(Ubuntu)
  • Docker Desktop
  • Harbor v2.15.0
  • Kubernetes v1.18.4
  • Docker runtime 18.09.9

5-2-2. Member 서비스 이미지 Push/Pull

MSA 환경에서 Docker 이미지를 중앙 저장소로 관리하기 위해 Harbor를 구축하고,
Vagrant 기반 Kubernetes Cluster에서 Harbor 이미지를 Pull 하도록 구성했다.

구성 환경은 아래와 같다.

1
2
3
4
5
6
7
8
9
[ Windows PC ]
 └─ Docker Desktop
     └─ Harbor (192.168.56.1:8080)

[ Vagrant Kubernetes Cluster ]
 ├─ m-k8s
 ├─ w1-k8s
 ├─ w2-k8s
 └─ w3-k8s
  1. Harbor API 확인
1
2
3
curl http://192.168.56.1:8080/api/v2.0/ping
# 정상 응답:
Pong
  1. Harbor UI에 접속해 프로젝트 생성

프로젝트 생성

  1. Docker insecure registry 설정

Harbor를 HTTP로 구성했기 때문에 Docker에서 insecure registry 설정이 필요하다. 적용 후 Docker Desktop을 재시작 해야한다.

http 설정

docker 설정

  1. Docker 이미지 Push
    • 로컬에서 member-service 이미지 확인

docker 이미지

  • Harbor 용 태그 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS C:\WINDOWS\system32> docker tag member-service:latest 192.168.56.1:8080/deploy-test-member/member-service:latest
PS C:\WINDOWS\system32> docker images
REPOSITORY                                            TAG       IMAGE ID       CREATED         SIZE
product-service                                       latest    0004eb27476e   3 weeks ago     433MB
ordering-service                                      latest    46f21531d0c3   3 weeks ago     434MB
192.168.56.1:8080/deploy-test-member/member-service   latest    fcaee27bcf5f   4 weeks ago     421MB
member-service                                        latest    fcaee27bcf5f   4 weeks ago     421MB
goharbor/redis-photon                                 v2.15.0   4ed296911e8b   7 weeks ago     173MB
goharbor/harbor-registryctl                           v2.15.0   4b2b3294a46c   7 weeks ago     166MB
goharbor/registry-photon                              v2.15.0   5b02673a2fa2   7 weeks ago     87.7MB
goharbor/harbor-log                                   v2.15.0   9e71d0bc1e73   7 weeks ago     170MB
goharbor/harbor-jobservice                            v2.15.0   2fe35692621f   7 weeks ago     185MB
goharbor/harbor-core                                  v2.15.0   87a862e24da8   7 weeks ago     210MB
goharbor/harbor-portal                                v2.15.0   b3939ccdd07f   7 weeks ago     166MB
goharbor/harbor-db                                    v2.15.0   e66097841983   7 weeks ago     274MB
goharbor/prepare                                      v2.15.0   029f75920a4b   7 weeks ago     199MB
goharbor/nginx-photon                                 v2.15.0   77728976f2e8   7 weeks ago     158MB
redis                                                 7.4       65750d044ac8   16 months ago   117MB
apache/kafka                                          3.8.0     b610bd8a193a   21 months ago   382MB
mysql                                                 8.0.38    6c54cbcf775a   22 months ago   572MB
  • Harbor push
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS C:\WINDOWS\system32> docker login 192.168.56.1:8080
Username: admin

i Info → A Personal Access Token (PAT) can be used instead.
          To create a PAT, visit https://app.docker.com/settings


Password:

Login Succeeded
PS C:\WINDOWS\system32> docker push 192.168.56.1:8080/deploy-test-member/member-service:latest
The push refers to repository [192.168.56.1:8080/deploy-test-member/member-service]
d06ca5ee8d0b: Layer already exists
d24210e52761: Layer already exists
f7409d4c66cc: Layer already exists
08e98c779fb9: Layer already exists
0a828e8088fe: Layer already exists
344ae0b6479e: Layer already exists
989e799e6349: Layer already exists
latest: digest: sha256:cf421bb1ce160dc0b08b5f24118c47e3e565b05dc22facb1c57b1471276c7b48 size: 1786
  • 이미지 push 확인

docker 이미지

  1. Deployment에서 이미지 Pull

member 서비스의 Deployment, Service를 Harbor에서 이미지를 pull 하도록 아래와 같이 수정

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
63
64
65
66
67
68
# depl_svc.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: member-depl
  namespace: deploy-test

spec:
  replicas: 1

  selector:
    matchLabels:
      app: member

  template:
    metadata:
      labels:
        app: member

    spec:
      imagePullSecrets:
      - name: harbor-secret

      containers:
      - name: member-container
        image: 192.168.56.1:8080/deploy-test-member/member-service:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

        resources:
          limits:
            cpu: "250m"
            memory: "500Mi"
          requests:
            cpu: "100m"
            memory: "250Mi"

        env:
        - name: DB_HOST
          value: "mysql-master-0.mysql-master.deploy-test-data.svc.cluster.local"
        - name: DB_PW
          value: "rootpass"

        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10

---

apiVersion: v1
kind: Service

metadata:
  name: member-service
  namespace: deploy-test

spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: member

kubectl apply -f depl_svc.yaml로 적용 후 pod 배포 확인

이미지 Pull

5-2-3. ordering, product 서비스 이미지 Push/Pull

  1. ordering 서비스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS C:\WINDOWS\system32> docker login 192.168.56.1:8080
Authenticating with existing credentials... [Username: admin]

i Info → To login with a different account, run 'docker logout' followed by 'docker login'


Login Succeeded
PS C:\WINDOWS\system32> docker tag ordering-service:latest 192.168.56.1:8080/deploy-test-ordering/ordering-service:latest
PS C:\WINDOWS\system32> docker push 192.168.56.1:8080/deploy-test-ordering/ordering-service:latest
The push refers to repository [192.168.56.1:8080/deploy-test-ordering/ordering-service]
7fa3ab0f89c0: Pushed
d24210e52761: Mounted from deploy-test-member/member-service
f7409d4c66cc: Mounted from deploy-test-member/member-service
08e98c779fb9: Mounted from deploy-test-member/member-service
0a828e8088fe: Mounted from deploy-test-member/member-service
344ae0b6479e: Mounted from deploy-test-member/member-service
989e799e6349: Mounted from deploy-test-member/member-service
latest: digest: sha256:7ce6d68cb67b39f8767632e569074d4161ddfcdb87b07eee59cbd4de1ba3366f size: 1786
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
# depl_svc.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ordering-depl
  namespace: deploy-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ordering
  template:
    metadata:
      labels:
        app: ordering
    spec:
      imagePullSecrets:
      - name: harbor-secret

      containers:
      - name: ordering-container
        image: 192.168.56.1:8080/deploy-test-ordering/ordering-service:latest
        imagePullPolicy: Always

        ports:
        - containerPort: 8080
        
        resources:
          limits:
            cpu: "250m"
            memory: "500Mi"
          requests:
            cpu: "100m"
            memory: "250Mi"
        env:
        - name: DB_HOST
          value: "mysql-master-0.mysql-master.deploy-test-data.svc.cluster.local"
        - name: DB_PW
          value: "rootpass"
        
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: ordering-service
  namespace: deploy-test
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: ordering
  1. product 서비스
1
2
3
4
5
6
7
8
9
10
11
PS C:\WINDOWS\system32> docker tag product-service:latest 192.168.56.1:8080/deploy-test-product/product-service:latest
PS C:\WINDOWS\system32> docker push 192.168.56.1:8080/deploy-test-product/product-service:latest
The push refers to repository [192.168.56.1:8080/deploy-test-product/product-service]
b67528f7fc65: Pushed
d24210e52761: Pushed
f7409d4c66cc: Pushed
08e98c779fb9: Pushed
0a828e8088fe: Pushed
344ae0b6479e: Pushed
989e799e6349: Pushed
latest: digest: sha256:9f8260d717ed01dd380bebe4d5e78b34ff0bb023c0aabb0a30c8754d8738cc60 size: 1786
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
# depl_svc.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-depl
  namespace: deploy-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: product
  template:
    metadata:
      labels:
        app: product
    spec:
      imagePullSecrets:
      - name: harbor-secret

      containers:
      - name: product-container
        image: 192.168.56.1:8080/deploy-test-product/product-service:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: "250m"
            memory: "500Mi"        
          requests:
            cpu: "100m"
            memory: "250Mi"
        env:
        - name: DB_HOST
          value: "mysql-master-0.mysql-master.deploy-test-data.svc.cluster.local"
        - name: DB_PW
          value: "rootpass"        
        readinessProbe:
          httpGet:            
            path: /health
            port: 8080          
          initialDelaySeconds: 10          
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: product-service
  namespace: deploy-test
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: product

kubectl apply -f depl_svc.yaml로 적용 후 pod 배포 확인

이미지 Pull

5-3. jenkins

Harbor에 Jenkins를 추가해 CI/CD 환경을 구축한다.

1
2
3
4
5
Git Push
 → Jenkins Build
 → Docker Image Build
 → Harbor Push
 → Kubernetes Deployment
flowchart LR

A[Git Repository]
 --> B[Jenkins]

B --> C[Gradle Build]

C --> D[Docker Build]

D --> E[Harbor Push]

E --> F[Kubernetes Pull]

F --> G[Deployment]

구성 환경은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
[ Windows PC ]
 ├─ Docker Desktop
 │   ├─ Harbor(:8080)
 │   └─ Jenkins(:8081)
 │
 └─ WSL2 Ubuntu

[ Vagrant Kubernetes Cluster ]
 ├─ m-k8s
 ├─ w1-k8s
 ├─ w2-k8s
 └─ w3-k8s

5-3-1. Jenkins 설치

  1. Jenkins Docker 실행
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
PS C:\WINDOWS\system32> docker run -d --name jenkins -p 8081:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home -v //var/run/docker.sock:/var/run/docker.sock jenkins/jenkins:lts
Unable to find image 'jenkins/jenkins:lts' locally
lts: Pulling from jenkins/jenkins
a7730063fcfe: Pull complete
beb35d8d9493: Pull complete
a576649e9376: Pull complete
32ad68c1bba8: Pull complete
6e001067f512: Pull complete
f015f18f3c7a: Pull complete
0454b3406328: Pull complete
6e428cebe944: Pull complete
1aac2fe67fcc: Pull complete
b850d1c40542: Pull complete
32ebd389acde: Pull complete
e714309bcf67: Pull complete
Digest: sha256:7004d07dbcdc5439fdad8853acdb029c5e3ab7a3d8190184fbf89bec66786c02
Status: Downloaded newer image for jenkins/jenkins:lts
aba5d2c3669bd8d6864452056d008a10e0e484ba8aae63adae4dc5cd7a0379cb
PS C:\WINDOWS\system32> docker ps
CONTAINER ID   IMAGE                                 COMMAND                   CREATED          STATUS                  PORTS                                              NAMES
aba5d2c3669b   jenkins/jenkins:lts                   "/usr/bin/tini -- /u…"   33 seconds ago   Up 33 seconds           0.0.0.0:50000->50000/tcp, 0.0.0.0:8081->8080/tcp   jenkins
69f2b3db8863   goharbor/nginx-photon:v2.15.0         "nginx -g 'daemon of…"   19 hours ago     Up 19 hours (healthy)   0.0.0.0:8080->8080/tcp                             nginx
7f2521b15b61   goharbor/harbor-jobservice:v2.15.0    "/harbor/entrypoint.…"   19 hours ago     Up 19 hours (healthy)                                                      harbor-jobservice
98fd6931f30e   goharbor/harbor-core:v2.15.0          "/harbor/entrypoint.…"   19 hours ago     Up 19 hours (healthy)                                                      harbor-core
91f563ca1cdc   goharbor/harbor-registryctl:v2.15.0   "/home/harbor/start.…"   19 hours ago     Up 19 hours (healthy)                                                      registryctl
52c2e44c992d   goharbor/harbor-portal:v2.15.0        "nginx -g 'daemon of…"   19 hours ago     Up 19 hours (healthy)                                                      harbor-portal
cea0f2cba842   goharbor/harbor-db:v2.15.0            "/docker-entrypoint.…"   19 hours ago     Up 19 hours (healthy)                                                      harbor-db
334279cfc2fa   goharbor/registry-photon:v2.15.0      "/home/harbor/entryp…"   19 hours ago     Up 19 hours (healthy)                                                      registry
d42af998eddd   goharbor/redis-photon:v2.15.0         "redis-server /etc/r…"   19 hours ago     Up 19 hours (healthy)                                                      redis
58db501b6900   goharbor/harbor-log:v2.15.0           "/bin/sh -c /usr/loc…"   19 hours ago     Up 19 hours (healthy)   127.0.0.1:1514->10514/tcp                          harbor-log
  1. Jenkins 초기 비밀번호 확인, 브라우저 접속
1
2
3
4
5
6
PS C:\WINDOWS\system32> docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassworddocker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
cat: /var/jenkins_home/secrets/initialAdminPassworddocker: No such file or directory
cat: exec: No such file or directory
cat: jenkins: No such file or directory
cat: cat: No such file or directory
cf0980bdaca649828a67d6f4dcfabfbd

Jenkins 접속

  1. Jenkins 내부 Docker 설치

Jenkins에서 Docker 사용이 가능한지 확인한다.

1
2
3
gram@DESKTOP-TQV062L:~/harbor$ docker exec -it -u root jenkins bash
root@aba5d2c3669b:/# docker version
bash: docker: command not found

Jenkins container 안에 Docker CLI가 없어 기존 Jenkins를 삭제하고 jenkins + docker cli 커스텀 이미지를 만들어서 설치한다.

1
2
3
4
PS C:\WINDOWS\system32> docker stop jenkins
jenkins
PS C:\WINDOWS\system32> docker rm jenkins
jenkins
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
gram@DESKTOP-TQV062L:~/harbor$ mkdir ~/jenkins-docker
gram@DESKTOP-TQV062L:~/harbor$ cd ~/jenkins-docker/
gram@DESKTOP-TQV062L:~/jenkins-docker$ vim Dockerfile
gram@DESKTOP-TQV062L:~/jenkins-docker$ docker build -t my-jenkins .
[+] Building 17.3s (6/6) FINISHED                                                                                                                       docker:default
 => [internal] load build definition from Dockerfile                                                                                                              0.1s
 => => transferring dockerfile: 139B                                                                                                                              0.0s
 => [internal] load metadata for docker.io/jenkins/jenkins:lts                                                                                                    0.0s
 => [internal] load .dockerignore                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                   0.0s
 => [1/2] FROM docker.io/jenkins/jenkins:lts                                                                                                                      0.2s
 => [2/2] RUN apt-get update && apt-get install -y docker.io                                                                                                     16.2s
 => exporting to image                                                                                                                                            0.7s
 => => exporting layers                                                                                                                                           0.7s
 => => writing image sha256:2982b24efd31963c44816a3726c565131471a6b57118c08838fccc8e6329d07a                                                                      0.0s
 => => naming to docker.io/library/my-jenkins                                                                                                                     0.0s
gram@DESKTOP-TQV062L:~/jenkins-docker$

# Dockerfile
FROM jenkins/jenkins:lts

USER root

RUN apt-get update && apt-get install -y docker.io

USER jenkins

새 jenkins를 실행한다.

1
2
PS C:\WINDOWS\system32> docker run -d --name jenkins -p 8081:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home -v //var/run/docker.sock:/var/run/docker.sock my-jenkins
0fadb941d9dc79bc4f43aebac1d0d0d681a4f83f215bc447346a38afd65a6cf0

Jenkins에서 Docker가 정상적으로 실행된 것을 확인한다.

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
gram@DESKTOP-TQV062L:~/jenkins-docker$ docker exec -it -u root jenkins bash
root@0fadb941d9dc:/# docker version
Client:
 Version:           26.1.5+dfsg1
 API version:       1.45
 Go version:        go1.24.4
 Git commit:        a72d7cd
 Built:             Sun Mar  8 15:28:39 2026
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Desktop 4.40.0 (187762)
 Engine:
  Version:          28.0.4
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.7
  Git commit:       6430e49
  Built:            Tue Mar 25 15:07:22 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.26
  GitCommit:        753481ec61c7c8955a23d6ff7bc8e4daed455734
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
  1. Harbor 테스트

Jenkins Container 안에서 Harbor 접속 테스트를 한다.

1
2
3
4
5
6
7
8
9
10
# docker exec -it -u root jenkins bash 로 Jenkins Container 접속

root@0fadb941d9dc:/# docker login 192.168.56.1:8080
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

5-3-2. Github 연동

  1. Github SSH 연동

Jenkins에서 Git Clone을 하기 위해 Github SSH Key를 생성하고 Github Public key에 등록한다.
그리고 Jenkins 브라우저에서 Github SSH Key로 Credential 설정을 한다.

1
2
3
4
5
# Jenkins 전용 SSH Key 생성
ssh-keygen -t ed25519 -C "jenkins"
# 저장 위치
~/.ssh/jenkins_key
# 주의 사항: CI/CD 자동화를 위해 Passphrase 없이 생성함
  1. Gihub Clone 테스트

Freestype Project 생성 후 Git Clone 테스트를 수행한다.
Repository는 https://github.com/5-SH/deploy-test-member.git로 설정함.

Jenkins 접속

5-3-3. Jenkins Pipeline 생성

Jenkins Pipeline을 사용해 Spring Boot 애플리케이션을 자동 빌드하고 Harbor Registry에 이미지를 Push한 뒤 Kubernetes에 자동 배포한다.

  1. Jenkins Pipeline 구성
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    Git Clone
    ↓
    Gradle Build
    ↓
    Docker Image Build
    ↓
    Harbor Login
    ↓
    Harbor Registry Push
    ↓
    Kubernetes Deployment
    
  2. Jenkinsfile 구성

Harbor(192.168.56.1:8080)에서 이미지를 Pull 하고 k8s/depl_svc.yml을 사용해 k8s에 배포한다.
rollout restart 명령으로 변경사항에 관계 없이 k8s에서 서비스를 재배포 한다.

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
pipeline {

    agent any

    environment {

        IMAGE_NAME = "192.168.56.1:8080/deploy-test-member/member-service"
        IMAGE_TAG = "latest"
    }

    stages {

        stage('Git Clone') {

            steps {

                git branch: 'main',
                    credentialsId: 'github-ssh',
                    url: 'https://github.com/5-SH/deploy-test-member.git'
            }
        }

        stage('Gradle Build') {

            steps {

                sh '''
                chmod +x gradlew
                ./gradlew clean build
                '''
            }
        }

        stage('Docker Build') {

            steps {

                sh '''
                docker build -t $IMAGE_NAME:$IMAGE_TAG .
                '''
            }
        }

        stage('Harbor Login') {

            steps {

                withCredentials([
                    usernamePassword(
                        credentialsId: 'harbor-account',
                        usernameVariable: 'HARBOR_USER',
                        passwordVariable: 'HARBOR_PASS'
                    )
                ]) {

                    sh '''
                    docker login 192.168.56.1:8080 \
                    -u $HARBOR_USER \
                    -p $HARBOR_PASS
                    '''
                }
            }
        }

        stage('Harbor Push') {

            steps {

                sh '''
                docker push $IMAGE_NAME:$IMAGE_TAG
                '''
            }
        }

        stage('Kubernetes Deploy') {

            steps {

                withCredentials([
                    file(
                        credentialsId: 'kubeconfig',
                        variable: 'KUBECONFIG'
                    )
                ]) {

                    sh '''
                    kubectl apply -f k8s/depl_svc.yml
                    kubectl rollout restart deployment/member-depl -n deploy-test
                    '''
                }
            }
        }
    }
}
  1. Jenkins Docker 이미지 커스터마이징 초기 Jenkins Container에 Docker CLI, kubectl, OpenJDK 17이 없어 빌드와 배포에 실패를 했다.
    Jenkins Dockerfile을 아래와 같이 커스터마이징 해 도구들을 추가했다.
    그리고 kubectl 설치, docker.sock 접근 권한, docker 명령 실행 실패로 root 유저를 사용했다.
1
2
3
4
5
6
7
8
9
# Dockerfile

FROM jenkins/jenkins:lts-jdk17

USER root

RUN apt-get update && apt-get install -y docker.io curl

RUN curl -LO "https://dl.k8s.io/release/v1.28.15/bin/linux/amd64/kubectl" && install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && rm -f kubectl

Jenkins의 실행과 종료는 아래 명령어를 사용한다.

1
2
3
4
5
docker stop jenkins

docker rm jenkins

docker run -d --name jenkins -u root -p 8081:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock my-jenkins

5-4. 최종 CI/CD 흐름

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Git Push
   ↓
Jenkins Trigger
   ↓
Gradle Build
   ↓
Docker Build
   ↓
Harbor Push
   ↓
kubectl Apply
   ↓
Kubernetes Rolling Update
   ↓
신규 Pod 생성
   ↓
서비스 반영

member 서비스 CI/CD 성공

5-5. ordering, product 서비스 CI/CD

  1. ordering 서비스
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
pipeline {

    agent any

    environment {

        IMAGE_NAME = "192.168.56.1:8080/deploy-test-ordering/ordering-service"
        IMAGE_TAG = "latest"
    }

    stages {

        stage('Git Clone') {

            steps {

                git branch: 'main',
                    credentialsId: 'github-ssh',
                    url: 'https://github.com/5-SH/deploy-test-ordering.git'
            }
        }

        stage('Gradle Build') {

            steps {

                sh '''
                chmod +x gradlew
                ./gradlew clean build
                '''
            }
        }

        stage('Docker Build') {

            steps {

                sh '''
                docker build -t $IMAGE_NAME:$IMAGE_TAG .
                '''
            }
        }

        stage('Harbor Login') {

            steps {

                withCredentials([
                    usernamePassword(
                        credentialsId: 'harbor-account',
                        usernameVariable: 'HARBOR_USER',
                        passwordVariable: 'HARBOR_PASS'
                    )
                ]) {

                    sh '''
                    docker login 192.168.56.1:8080 \
                    -u $HARBOR_USER \
                    -p $HARBOR_PASS
                    '''
                }
            }
        }

        stage('Harbor Push') {

            steps {

                sh '''
                docker push $IMAGE_NAME:$IMAGE_TAG
                '''
            }
        }

        stage('Kubernetes Deploy') {

            steps {

                withCredentials([
                    file(
                        credentialsId: 'kubeconfig',
                        variable: 'KUBECONFIG'
                    )
                ]) {

                    sh '''
                    kubectl apply -f k8s/depl_svc.yml
                    kubectl rollout restart deployment/ordering-depl -n deploy-test
                    '''
                }
            }
        }
    }
}

ordering 서비스 CI/CD 성공

  1. product 서비스
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
pipeline {

    agent any

    environment {

        IMAGE_NAME = "192.168.56.1:8080/deploy-test-product/product-service"
        IMAGE_TAG = "latest"
    }

    stages {

        stage('Git Clone') {

            steps {

                git branch: 'main',
                    credentialsId: 'github-ssh',
                    url: 'https://github.com/5-SH/deploy-test-product.git'
            }
        }

        stage('Gradle Build') {

            steps {

                sh '''
                chmod +x gradlew
                ./gradlew clean build
                '''
            }
        }

        stage('Docker Build') {

            steps {

                sh '''
                docker build -t $IMAGE_NAME:$IMAGE_TAG .
                '''
            }
        }

        stage('Harbor Login') {

            steps {

                withCredentials([
                    usernamePassword(
                        credentialsId: 'harbor-account',
                        usernameVariable: 'HARBOR_USER',
                        passwordVariable: 'HARBOR_PASS'
                    )
                ]) {

                    sh '''
                    docker login 192.168.56.1:8080 \
                    -u $HARBOR_USER \
                    -p $HARBOR_PASS
                    '''
                }
            }
        }

        stage('Harbor Push') {

            steps {

                sh '''
                docker push $IMAGE_NAME:$IMAGE_TAG
                '''
            }
        }

        stage('Kubernetes Deploy') {

            steps {

                withCredentials([
                    file(
                        credentialsId: 'kubeconfig',
                        variable: 'KUBECONFIG'
                    )
                ]) {

                    sh '''
                    kubectl apply -f k8s/depl_svc.yml
                    kubectl rollout restart deployment/product-depl -n deploy-test
                    '''
                }
            }
        }
    }
}

product 서비스 CI/CD 성공

This post is licensed under CC BY 4.0 by the author.