1.Overview
Application 개발 환경을 셋업하면서 CI/CD 툴에 대한 고려가 나왔습니다. Jenkins는 가장 유명한 CI/CD 툴 중 하나로 Jenkins를 이용해 이미지 빌드 부터 Kubernetes에 배포까지 할 수 있습니다. Docker, Kubernetes를 위한 버전인 Jenkins X 가 있지만 기본 Jenkins로도 배포는 가능합니다. 그 외에 CD 툴로 Argo CD 를 많이 이용합니다. 쿠버네티스 배포를 위해서 매니페스트를 작성하는데 이 또한 버전 관리가 필요하기 때문에 이를 github 와 연결해 사용합니다. 쿠버네티스와 같은 CNCF에서 관리 되기 때문에 kubernetes config 관리, monitoring 등 호환이 잘 되는 것으로 알려져있습니다. 이번 글에서는 Jenkins를 이용해 이미지 빌드 부터 쿠버네티스 배포까지 진행해보겠습니다. 대략적인 flow는 다음과 같습니다.
_2. Jenkins 설치 _
우선 Jenkins를 설치하기 위한 이미지가 필요합니다. 공식 이미지는 update가 중단되었기 때문에 사용에 필요한 기능을 추가하기 위해 이미지 빌드를 진행합니다.
Docker를 사용해야 하기 때문에 docker-cli, docker-compose를 설치합니다. 도커는 client-server 구조이기 때문에 docker-cli만 설치 후 호스트에 설치된 docker.socket을 컨테이너로 마운트 해 사용 하도록 합니다. (docker-cli 또한 host로 마운트 해서 별도로 빌드없이 사용할 수 있습니다)
#Dockerfile
FROM jenkins/jenkins:lts
USER root
RUN apt-get update
# docker install
RUN curl -fsSL https://get.docker.com/ | sh
# docker-compose install
RUN curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
chmod +x /usr/local/bin/docker-compose
도커파일을 바탕으로 생성 된 이미지로 컨테이너를 실행합니다. 50000번 포트는 slave aget에 연결하기 위한 포트입니다. 볼륨은 Jenkins 데이터를 보존하기 위한 볼륨과 host의 docker socket을 사용하기 위해 설정합니다.
docker run -d --privileged --name jenkins -p 8080:8080 -p 50000:50000 \
-v $(pwd)/jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock my_jenkins:1.0
컨테이너가 정상적으로 실행 되었다면 8080포트로 jenkins에 접속합니다. docker logs jenkins
로 초기 비밀번호를 확인 후 플러그인을 설치합니다.
추가로 docker를 사용하기 위해 docker, docker pipeline 플러그인을 설치하면 Jenkins 설치는 완료됩니다.
3. Image build & Push
첫번 째 단계로 Git으로 관리되는 코드를 가져와 Image build를 후 Nexus로 업로드 하는 과정입니다. 저는 flask로 간단한 api Server를 실행하는 코드와 이를 빌드하기 위한 Dockerfile을 github에 Private repository로 만들어 놓았습니다.
#app.py
from flask import Flask
from flask_restx import Api, Resource
app = Flask(__name__)
api = Api(app)
@api.route('/hello')
class HelloWorld(Resource):
def get(self):
return {"hello": "world!"}
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5000)
#Dockerfile
FROM python
WORKDIR /app
RUN pip install flask-restx
COPY ./app.py /app
ENTRYPOINT [ "python3" ]
CMD [ "app.py" ]
우선 Nexus와 Git에 접속하기 위한 Credential을 생성합니다. jenkins 관리-> Manage Credential -> Add Credential 에서 생성합니다. (github는 패스워드가 아닌 Personal Token으로 입력해야합니다)
)
이후 작업을 위한 파이프라인을 생성합니다.
다른 설정없이 바로 pipeline script를 작성합니다. Pipeline 항목 아래에 있는 pipeline Systax에 들어가 git repository checkout명령어를 생성합니다.
복사한 코드를 가지고 파이프라인 스크립트를 생성합니다.
pipeline {
agent any
environment {
imageName = "flaskapp"
#Nexus Credential ID 입력
registryCredentials = "nexus"
registry = "Nexus 주소 입력"
dockerImage = ''
}
stages {
stage('Code checkout') {
steps {
# 복사한 코드 입력
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'seokbin_github', url: 'https://github.com/Goseokbin/jenkins-example.git']]]) }
}
// Building Docker images
stage('Building image') {
steps{
script {
dockerImage = docker.build imageName
}
}
}
// Uploading Docker images into Nexus Registry
stage('Uploading to Nexus') {
steps{
script {
docker.withRegistry( 'http://'+registry, registryCredentials ) {
dockerImage.push('latest')
}
}
}
}
stage('Docker Run') {
steps{
script {
sh 'docker run -d -p 5000:5000 --rm --name myflaskapp ' + registry + imageName
}
}
}
}
}
이후 저장 한 후 Now Build를 통해 테스트합니다. 아래와 같이 모든 stage가 정상적으로 실행 된 것을 확인 할 수 있습니다.
또한 Nexus에 접속해 이미지가 정상적으로 업로드 되고 docker container도 실행 된 것을 확인 할 수 있습니다.
)
4. Kubernetes 배포
Nexus 까지 이미지가 정상적으로 올라가는 것을 확인 한 후 Kubernetes를 배포하기 위해 Kubernetes CD Plugin을 설치합니다.
플러그인 설치 후 cluster와 연결을 위해 Credential을 생성해 Kube config(~/.kube/config
) 파일의 내용을 입력합니다.
해당 이미지를 바탕으로 쿠버네티스에 배포하기 위한 매니페스트를 작성해 Github에 Push 합니다. 저는 Deployment와 이를 노출하기 위한 Service를 만들었습니다.
#jenkins_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: jenkins-flask
name: jenkins-flask
spec:
replicas: 1
selector:
matchLabels:
app: jenkins-flask
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: jenkins-flask
spec:
containers:
- image: [nexus 주소]/flaskapp
imagePullPolicy: Always
name: flaskapp
ports:
- containerPort: 5000
resources: {}
imagePullSecrets:
- name: regcred
status: {}
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: jenkins-flask
name: jenkins-flask
spec:
ports:
- port: 5000
protocol: TCP
targetPort: 5000
selector:
app: jenkins-flask
type: NodePort
status:
loadBalancer: {}
이를 바탕으로 기존에 작성한 pipeline script에서 kubernetes stage를 추가합니다.
stage('Deploy to kubernetes'){
steps{
script{
kubernetesDeploy(configs: "jenkins_deploy.yaml", kubeconfigId: "kubernetes")
}
}
}
이후 build 를 실행했을 때 쿠버네티스 배포 스테이지에서 다음과 같은 에러가 발생하였습니다. 해당 에러를 구글링 했을 때 플러그인 버전 호환 문제이기 때문에 다운그레이드를 해야했습니다. 이를 위해 기존의 설치된 플러그인을 삭제하고 kubernetes Plugin 1.0 버전을 다운받아서 설치합니다.
플러그인 설치 → 고급 → 플러그인 올리기 에서 다운 받은 파일을 추가해 설치합니다.
설치 후 재실행 하였을 때 파이프라인이 정상적으로 실행되고 kubernetes에 배포 된 것을 확인 할 수 있습니다
.
매니페스트가 여러 파일로 이루어진다면 manifests 폴더를 만들고 configs:'manifests/
와 같이 폴더명을 입력하면 됩니다. 생성되는 리소스의 순서가 중요하기 때문에 이는 파일 명을 통해 순서를 정해줍니다.
파이프라인 스크립트의 전체 코드는 다음과 같습니다.
pipeline {
agent any
environment {
imageName = "flaskapp"
registryCredentials = "nexus_credential"
registry = "172.16.156.21:5000/"
dockerImage = ''
}
stages {
stage('Code checkout') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'seokbin_github', url: 'https://github.com/Goseokbin/jenkins-example.git']]]) }
}
// Building Docker images
stage('Building image') {
steps{
script {
dockerImage = docker.build imageName
}
}
}
// Uploading Docker images into Nexus Registry
stage('Uploading to Nexus') {
steps{
script {
docker.withRegistry( 'http://'+registry, registryCredentials ) {
dockerImage.push('latest')
}
}
}
}
stage('Deploy to kubernetes'){
steps{
script{
kubernetesDeploy(configs: "jenkins_deploy.yaml", kubeconfigId: "kubernetes")
}
}
}
}
}
5. 마무리
이렇게 간단한 어플리케이션을 Jenkins를 통해 CI/CD 테스트를 진행해보았습니다. CI/CD에 대한 부분도 세부적으로 설정해야 할 부분은 많습니다. 카나리 배포, 블루/그린 배포 등의 배포 방법, 환경에 따른 변경 사항 등 다양한 고려사항이 있습니다만 현재 저희 프로젝트는 사용자가 많지 않은 적은 규모의 클러스터이기 때문에 CI/CD에 과도하게 집중할 필요는 없습니다. 하지만 쿠버네티스에서 공식적으로 추천하는 Argo CD 또한 궁금하기 때문에 향후 기회가 된다면 테스트해 보겠습니다.
'Kubernetes' 카테고리의 다른 글
Kubernetes에서 학습용 Job 생성하기 (0) | 2023.10.12 |
---|---|
Kubeflow JupyterNoteBook (0) | 2023.10.12 |
Kubernetes Offline 환경 설치하기 (0) | 2023.10.12 |
Kubeflow V1.4 설치 및 초기 설정(User 추가, CORS, dex DB 분리) (0) | 2023.10.12 |
Kubernetes API server OOM 장애기록 (0) | 2023.10.12 |