전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]

티스토리 뷰

Jenkins with Kubernetes방식 이해

아래는 Jenkins with kubernetes방식으로 CI/CD Pipeline이 구동되는 모습입니다. 

이 pipeline의 단계(Stage)는 아래와 같이 4개 입니다. (번호가 붙어 있는게 Stage입니다.)

각 Stage에서 하는 일은 아래와 같습니다.

1) CI

- Get source: gitlab에서 소스와 CI/CD정의파일을 가져옴

- Build Microservice Image: Image를 build하고 docker hub로 푸시함

2) CD

- Cleanup Existing Deployments: 기존에 배포한 POD를 삭제함

- Deploy to Cluster: docker hub registry에서 Image를 가져와 worker nodes에 배포함

Get source단계 이후의 작업을 Jenkins slave POD안의 container들이 한다는 것을 주의깊게 봐주십시오.

Jenkins with kubernetes방식Jenkins (master) POD가 동적으로 Jenkins slave POD를 만들고, 그 안에 필요한 container를 생성합니다. 그리고 모든 작업이 끝나면 slave POD를 삭제합니다.

이렇게 하는 이유는 필요한 리소스(docker, kubectl 등)를 container로 독립적으로 관리하기 위함입니다.

그렇게 해야 리소스 간에 충돌이 없고, 작업량에 따라 자동으로 Slave Pod를 Scale Out/In할 수 있기 때문입니다. 

실제로 아래 명령을 실행하고 Pipeline을 build하면 새로운 jenkins slave POD를 확인할 수 있습니다.

 

CI/CD 정의 파일 만들기

eclipse에서 개발한 hellonode프로젝트에 CI/CD정의 파일을 만들고, gitlab으로 push합니다.

CI/CD정의 파일은 아래와 같은 종류가 있습니다.

- Dockerfile: 배포Image build 정의 파일

- deploy.yaml : POD배포 정의 파일

- svc.yaml: Service 정의 파일

- ing.yaml: Ingress 정의 파일

- Jenkinsfile : Pipeline 정의 파일

- pipeline.properties: Jenkinsfile에서 참조하는 환경변수 정의 파일

먼저, deployment라는 폴더를 만듭니다.

1) Dockerfile

- git에는 .gitignore에 정의한대로 node_modules(필요 라이브러리)가 없습니다. 따라서 image 생성 시 npm install명령으로 설치를 합니다. npm install 전에 package.json파일이 먼저 복사되야 합니다. 그 안에 라이브러리가 정의되어 있기 때문입니다.

- container 외부에 8081포트를 노출시켰습니다. 나중에 deployment 정의 파일에도 동일 port로 정의해야 합니다.

FROM node:carbon
WORKDIR /home/hellonode/
COPY package.json .
RUN [ "npm", "install" ]
COPY . .
EXPOSE 8081
CMD [ "npm", "start" ]

 

2) deployment 정의 파일: deploy.yaml

Image의 이름에서 'happykube'는 본인의 hub docker의 userid 또는 Organization으로 바꾸셔야 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellonode
  labels:
    app: hellonode
spec:
  selector:
    matchLabels:
      app: hellonode
  replicas: 2
  template:
    metadata:
      name: hellonode
      labels:
        app: hellonode
    spec:
      containers:
        - name: hellonode
          image: happykube/hellonode
          imagePullPolicy: Always
          env:
            - name: PORT
              value: "8081"
          ports:
            - name: port1
              containerPort: 8081
          resources:
            requests:
              cpu: 500m
              memory: 1024Mi
            limits:
              cpu: 1000m
              memory: 2048Mi

 

3) Service 정의파일: svc.yaml

 

apiVersion: v1
kind: Service
metadata:
  name: hellonode
spec:
  type: NodePort
  selector:
    app: hellonode
  ports:
    - name: hellonode
      port: 8090
      targetPort: 8081

 

4) Ingress정의파일: ing.yaml

host명은 본인의 k8s maste나 worker node의 IP로 변경해야 합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hellonode
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
    - host: hellonode.169.56.84.35.nip.io
      http:
        paths:
          - path: /(.*)
            pathType: Prefix
            backend:
              service:
                name: hellonode
                port: 
                  number: 8090

 

5) Pipeline 정의 파일: Jenkjnsfile

Jenkinsfile은 TAB으로 들여쓰기를 합니다.

TIP) Jenkinsfile은 yaml과 다르게 space로 들여쓰기를 할 필요가 없습니다. java, js와 같은 언어와 같이 space와 tab을 섞어 써도 됩니다.

def label = "hellonode-${UUID.randomUUID().toString()}"


podTemplate(
	label: label, 
	containers: [
		//container image는 docker search 명령 이용
		containerTemplate(name: "docker", image: "docker:latest", ttyEnabled: true, command: "cat"),
		containerTemplate(name: "kubectl", image: "lachlanevenson/k8s-kubectl", command: "cat", ttyEnabled: true)
	],
	//volume mount
	volumes: [
		hostPathVolume(hostPath: "/var/run/docker.sock", mountPath: "/var/run/docker.sock")
	]
) 
{
	node(label) {
		stage("Get Source") {
			//git url:"https://github.com/happykube/hellonode.git", branch: "main", credentialsId: "git_credential"
		    git branch: 'main', url: 'https://github.com/happykube/hellonode.git'
        } 

		//-- 환경변수 파일 읽어서 변수값 셋팅 
		def props = readProperties  file:"deployment/pipeline.properties"
		def tag = props["version"]
		def dockerRegistry = props["dockerRegistry"]
		def credential_registry=props["credential_registry"]
		def image = props["image"]
		def deployment = props["deployment"]
		def service = props["service"]
		def ingress = props["ingress"]
		def selector_key = props["selector_key"]
		def selector_val = props["selector_val"]
		def namespace = props["namespace"]

		try {
			stage("Build Microservice image") {
				container("docker") {
					docker.withRegistry("${dockerRegistry}", "${credential_registry}") {
						sh "docker build -f ./deployment/Dockerfile -t ${image}:${tag} ."
						sh "docker push ${image}:${tag}"
						sh "docker tag ${image}:${tag} ${image}:latest"
						sh "docker push ${image}:latest"
					}
				}
			}
			stage( "Clean Up Existing Deployments" ) {
				container("kubectl") {
					sh "kubectl delete deployments -n ${namespace} --selector=${selector_key}=${selector_val}"
				}
			}

			stage( "Deploy to Cluster" ) {
				container("kubectl") {
					sh "kubectl apply -n ${namespace} -f ${deployment}"
					sh "sleep 5"
					sh "kubectl apply -n ${namespace} -f ${service}"
					sh "kubectl apply -n ${namespace} -f ${ingress}"
				}
			}

		} catch(e) {
			currentBuild.result = "FAILED"
		}
	}
}

 

TIP)

containerTemplate에서 정의한 image이름은 아래와 같이 docker search로 찾을 수 있습니다.

OFFICIAL image를 사용하는 것이 좋고, 없으면 가장 인기가 많은 image를 사용하시면 됩니다.

 

6) Pipeline 환경설정 파일: pipeline.properties

- image, version은 deploy.yaml에 지정한 container imge와 동일해야 합니다. 

image변수의 'happykube'는 docker hub registry의 본인 userid나 Organization으로 바꿔야 합니다.

version=0.1.1
namespace=hklee
dockerRegistry=https://index.docker.io/v1/
credential_registry=registry_credential
image=happykube/hellonode
deployment=deployment/deploy.yaml
service=deployment/svc.yaml
ingress=deployment/ing.yaml
selector_key=app
selector_val=hellonode

 

docker image registry에 대한 인증 credential 'registry_credential'을 만듭니다. 

 

 

 


Pipeline 수행

1) git에 Push하기

git에 먼저 Push합니다.

2) Jenkins 에서 Pipeline Item 만들기

 

git repository주소, branch, Jenkinsfile의 경로를 정확히 입력하십시오. 

 

3) Pipeline 실행하기

 

주요 진행상황은 아래와 같습니다.

- jenkins slave POD 생성

- gitlab에서 소스와 CI/CD 정의 파일들 가져오기

 

- build docker image

 

- 기존 POD 삭제

 

- POD, Service, Ingress 배포

 

- Pipeline 수행 종료

 

아래와 같이 정상적으로 모든 단계가 완료된 것을 확인할 수 있습니다.

 


앱 실행 확인

이제 실제로 앱이 정상적으로 배포되었는지 확인합니다.

1) POD, Service, Ingress 확인

$ kubectl get all 
$ kubectl get ing

2) 웹브라우저로 접근

 

위와 같이 페이지가 정상적으로 나오면 성공입니다.

댓글

전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]