iT邦幫忙

2023 iThome 鐵人賽

DAY 30
0
DevOps

從0開始學習DevOps,並部署CICD至Java專案中系列 第 30

把Kubernetes整合到Jenkins pipeline中,完成CI/CD。與完賽心得

  • 分享至 

  • xImage
  •  

準備使用Kubernetes部署所需要的yml文件

我這邊命名文件為「goal-life-deployment.yml」

apiVersion: apps/v1
kind: Deployment
metadata:
  name: goal-life-deployment
  namespace: build
  labels:
    app: goal-life
spec:
  replicas: 2
  selector:
    matchLabels:
      app: goal-life
  template:
    metadata:
      labels:
        app: goal-life
    spec:
      containers:
      - name: goal-life
        image: 192.168.2.60:80/goal-life/goal-life:0.1.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
      imagePullSecrets:
      - name: harbor-secret
---
apiVersion: v1
kind: Service
metadata:
  namespace: build
  name: goal-life-service
  labels:
    app: goal-life
spec:
  selector:
    app: goal-life
  ports:
  - port: 8082
    targetPort: 8081
  type: NodePort
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: build
  name: goal-life-ingress
spec:
  ingressClassName: ingress
  rules:
  - host: tw.goal-life.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: goal-life-service
            port:
              number: 8082

Step 1:請先建立Harbor登入資訊,與使用k8s執行、確認部署文件能正常運行

新增一個Harbor登入資訊在Kubernetes中
kubectl create secret docker-registry <自定義sectet_name>
接著就是填寫你拜訪Harbor頁面的IP:端口號、登入的帳號與密碼

kubectl create secret docker-registry harbor-secret \
  --docker-server=192.168.2.60:80 \
  --docker-username=admin \
  --docker-password=Harbor12345

在dashboard就會看到已新增一個Secrets

https://ithelp.ithome.com.tw/upload/images/20231015/20162058Kjyy8dVcaf.png

留意在harbor這個區塊,image填寫你要從Harbor拉取下來的image,填寫的內容就同我們前面幾則使用 docker pull <ip>:<port>/<倉庫名稱>/<image名稱>:<tag>

而我們這邊不用寫上“docker pull”,會直接為我們從私有的harbor倉庫都拉取image下來。

另外一定要加上 imagePullSecrets 這個部分,帶入我們剛剛加上的harbor登入資訊,name就是我們自定義的secret名稱。
https://ithelp.ithome.com.tw/upload/images/20231015/201620586iXArsdoXY.png

先嘗試在console使用k8s執行看看是否能建立成功

kubectl apply -f goal-life-deployment.yml

https://ithelp.ithome.com.tw/upload/images/20231015/20162058LKCOGQj26C.png

從dashboard就會看到已成功拉取image

https://ithelp.ithome.com.tw/upload/images/20231015/20162058pSjqp3Jo33.png

再使用docker images 確認是否成功拉取image

https://ithelp.ithome.com.tw/upload/images/20231015/201620580JeeHHXX3T.png

Step 2:在Jenkins下載 Kubernetes 插件,配置Kubernetes Cloud

https://ithelp.ithome.com.tw/upload/images/20231015/20162058fKjZ5gbcXk.png

配置Jenkins Cloud Kubernetes,需要先產生Kubernetes認證的文件

kubectl config view --minify --flatten > /usr/local/build/k8s/kubeconfig.yaml

以下配置參考來源
複製這段文字 certificate-authority-data,轉為base64 文件

echo <certificate-authority-data> | base64 -d > ./ca.crt

https://ithelp.ithome.com.tw/upload/images/20231015/20162058VcmieR93qq.png

把轉換後的文字貼上
本地URL 使用:https://host.docker.internal:6443
本地環境運行,請記得勾選:Disable https certificate check

https://ithelp.ithome.com.tw/upload/images/20231015/20162058OROn5PzgZn.png
再把以下client-certificate-data / client-key-data 轉為文件

echo <client-certificate-data> |base64 -d > ./client.crt
echo <client-key-data> |base64 -d > ./client.key

再生成一個PKC認證文件 (結合上方三個文件)這邊產生文件會需要自定義一個密碼

openssl pkcs12 -export -out ./cert.pfx -inkey ./client.key -in ./client.crt -certfile ./ca.crt

點選新增認證、上傳檔案、輸入自定義的文件密碼,之後點選Test Connection
https://ithelp.ithome.com.tw/upload/images/20231015/20162058QiPypcuRjr.png

https://ithelp.ithome.com.tw/upload/images/20231015/20162058cIhtUZ8fYD.png

再往下方Jenkins URL 填寫本地宿主機的:

本地環境也可以直接寫 http://host.docker.internal:8080/

https://ithelp.ithome.com.tw/upload/images/20231015/2016205803uT98F8Vq.png

新增完成

https://ithelp.ithome.com.tw/upload/images/20231015/20162058GDsFvje1Ul.png

Step 3:調整pipeline,把之前CI的script加上Deploy stage

我這邊總共做了幾件事

  1. 使用agent動態代理連接 kubernetes cloud,這樣才可以連接到k8s叢集中,並在構建時一併執行yaml,新增一個container運行,沒有多加一個controller的話,動態代理很快就會結束連接。(這邊的image用什麼都可以,本來用kubectl是想要讓pod中可以執行kubectl命令,但下載的位置不是可執行命令的位置,可能還需要花時間研究如何調整,但不影響後續執行)

  2. 構建好之後,我從github另外開一個repo管理我的部署檔案,從上面拉取部署檔案到k8s構建的pod中。(因為這邊的agent跟外面不同,以及這裡的容器是在k8s中構建的pod,所以第一步驟pull code那邊取得的東西,這個pod不會有)

  3. install kubectl:這邊直接下載文檔到pod中,這樣才可以在pod中執行kubectl命令。

    (這邊下載的位置會是在

  4. 最後執行部署文件,完成在k8s中部署專案

    sh './kubectl apply -f /home/jenkins/agent/workspace/goal-life/deploy/goal-life-deployment.yml'
    
    • 運行第四個步驟時,如果你指定的namespace不是在default,而是其他特別指定的命名空間的話,這邊會跳出錯誤,沒有權限可以執行。

      我這邊是在console直接設定這個使用者開通所有的權限,如果有要特別指定權限,可以再參考下方連結了解權限配置方式。

      開通權限後,再運行就可以正常了。

      可參考來源

      kubectl create clusterrolebinding serviceaccounts-cluster-admin \
        --clusterrole=cluster-admin \
        --group=system:serviceaccounts
      

完成CI/CD了!!!

這邊需要留意的部分是,部署上去的專案,資料庫連線需要使用宿主機ip,並且port要開放外部連接,才可以運行成功。
不然就是要在k8s中,建立DB的容器與服務,並在部署檔配置讓他可以連接到k8s中啟動的DB。
https://ithelp.ithome.com.tw/upload/images/20231015/20162058aAfMLdTdPF.png

在Jenkins pipeline 建置專案 CI/CD 完整的script如下

pipeline{
  agent any

  tools {
    jdk 'jdk17'
    maven 'maven'
  }
  
  environment {
      harborUser = 'admin'
      harborPwd = 'Harbor12345'
      harborUrl = '192.168.2.60:80'
      harborRepo = 'goal-life'
  }

  stages {
    stage('pull code') {
      steps {
        echo '---start pull code from git-hub---'
        checkout scmGit(branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[credentialsId: 'luluchen622', url: 'https://github.com/luluchen622/goal-life.git']])
        echo '---pull code from git-hub success---'
      }
    }
    stage('compile') {
      steps {
          sh 'mvn clean compile'
          echo '---compile success---'
        }
    }
    stage('通過sonarqube做代碼檢測') {
      steps {
        withSonarQubeEnv('sonarqube') {
            sh '/var/jenkins_home/tools/hudson.plugins.sonar.SonarRunnerInstallation/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./impl/target/'
        }
        echo '---sonarqube success---'
      }
    }

    stage('通過Docker構建image') {
      steps {
        sh 'mvn clean package -Dmaven.test.skip=true'
		sh 'docker build -t ${JOB_NAME}:${tag} .'
        echo '通過Docker構建image - SUCCESS'
      }
    }
    stage('將image推送到harbor') {
      steps {
          sh '''docker login -u ${harborUser} -p  ${harborPwd} ${harborUrl}
                docker tag ${JOB_NAME}:${tag} ${harborUrl}/${harborRepo}/${JOB_NAME}:${tag}
                docker push ${harborUrl}/${harborRepo}/${JOB_NAME}:${tag}'''
        echo '將image推送到harbor - SUCCESS'
      }
}
    stage('Deploy') {
      agent {
        kubernetes {
          yaml """
apiVersion: v1
kind: Pod
metadata:
  name: kubectl-apply
spec:
  containers:
  - name: kubectl
    image: bitnami/kubectl:latest
    command: ['sleep']
    args: ['infinity']
"""
                }
             }
        steps {
            script {
                echo '---start pull code from git-hub---'
                checkout scmGit(branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[credentialsId: 'luluchen622', url: 'https://github.com/luluchen622/goal-life-deployment.git']])
                echo '---pull code from git-hub success---'
                
                echo '---install kubectl---'
                sh 'curl -LO "https://storage.googleapis.com/kubernetes-release/release/v1.20.5/bin/linux/amd64/kubectl"'
                sh 'chmod u+x ./kubectl'
                
                echo '---deploy goal-life---'
                sh './kubectl apply -f /home/jenkins/agent/workspace/goal-life/deploy/goal-life-deployment.yml'
                echo '---deploy success---'
            }
        }
    }
  }
}

邊學、邊實作、寫撰寫文章的DevOps完賽心得

在開始學習與實作後,我更驚覺DevOps是一門博大精深的學問,可以使用的工具非常多,而每個工具又有各自的深度與廣度,實在是很難透過區區30天就能完成些什麼!

在這過程中,我努力把要為專案建置CI/CD流程所需具備的工具,由淺入深邊學邊做,也希望自己的文章能夠幫助其他像我一樣,要從0到1學習與實作CI/CD的人,所以有時候學會了一個小項目,譬如:如何使用Dockerfile? 就會一併想要更多的為什麼? 以及可以怎麼變化、怎麼運用,能更加方便?跟如何透過更白話的方式敘述與表達,能夠讓跟我一樣從0開始的初學者能夠較低門檻的理解?

這是我第一次參加鐵人賽挑戰,我就選擇對我來說最具挑戰的項目「DevOps」,從去年底到開賽前,我花了10個月的時間,自學了Vue3與如何寫後端測試,並且寫完一個前後端及測試的SideProject,其實也很猶豫要不要就輕鬆一點選這兩個題目,但因為接下來就是要為自己的SideProject加上CI/CD的配置,於是就決定參加這個對我來說,很具備挑戰的項目!

9/5~10/15中午,準備天數39.5天,扣除已安排好的活動,實際大約剩下30天,總花費時數150小時左右,下班/週末/連假都是沒日沒夜的在學習與實作,有時候一個問題也卡個3天,持續重新調整自己的時程安排,所幸我順利完賽啦!

但其實還有非常多地方可以做得更好,這30天僅是0到1的過程,還有很長一段路要前進!

接下來我會繼續再深入了解Kubernetes、以及Nexus處理其他服務打包後的jar檔、GCP、AWS、以及自動化CI/CD,文章會更新在https://medium.com/@luluchen622 ,但步調會調整的比較緩慢一些,因為過程中還要學習Spring Security 建置在專案中,讓我自己的[計畫目標系統 goal-life]side-project,可以結合CI/CD部署上線到雲端上使用。

若你瀏覽到這系列文章,發現哪些說得不夠精確,或者有其他建議與想法,也歡迎與我聯繫,謝謝。


上一篇
Kubernetes的組成與相關指令操作、新增Ingress並取得連線
系列文
從0開始學習DevOps,並部署CICD至Java專案中30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言