我這邊命名文件為「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
新增一個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
留意在harbor這個區塊,image填寫你要從Harbor拉取下來的image,填寫的內容就同我們前面幾則使用 docker pull <ip>:<port>/<倉庫名稱>/<image名稱>:<tag>
而我們這邊不用寫上“docker pull”,會直接為我們從私有的harbor倉庫都拉取image下來。
另外一定要加上 imagePullSecrets 這個部分,帶入我們剛剛加上的harbor登入資訊,name就是我們自定義的secret名稱。
kubectl apply -f goal-life-deployment.yml
從dashboard就會看到已成功拉取image
再使用docker images 確認是否成功拉取image
配置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
把轉換後的文字貼上
本地URL 使用:https://host.docker.internal:6443
本地環境運行,請記得勾選:Disable https certificate check
再把以下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
再往下方Jenkins URL 填寫本地宿主機的:
本地環境也可以直接寫 http://host.docker.internal:8080/
新增完成
我這邊總共做了幾件事
使用agent動態代理連接 kubernetes cloud,這樣才可以連接到k8s叢集中,並在構建時一併執行yaml,新增一個container運行,沒有多加一個controller的話,動態代理很快就會結束連接。(這邊的image用什麼都可以,本來用kubectl是想要讓pod中可以執行kubectl命令,但下載的位置不是可執行命令的位置,可能還需要花時間研究如何調整,但不影響後續執行)
構建好之後,我從github另外開一個repo管理我的部署檔案,從上面拉取部署檔案到k8s構建的pod中。(因為這邊的agent跟外面不同,以及這裡的容器是在k8s中構建的pod,所以第一步驟pull code那邊取得的東西,這個pod不會有)
install kubectl:這邊直接下載文檔到pod中,這樣才可以在pod中執行kubectl命令。
(這邊下載的位置會是在
最後執行部署文件,完成在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
這邊需要留意的部分是,部署上去的專案,資料庫連線需要使用宿主機ip,並且port要開放外部連接,才可以運行成功。
不然就是要在k8s中,建立DB的容器與服務,並在部署檔配置讓他可以連接到k8s中啟動的DB。
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是一門博大精深的學問,可以使用的工具非常多,而每個工具又有各自的深度與廣度,實在是很難透過區區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部署上線到雲端上使用。
若你瀏覽到這系列文章,發現哪些說得不夠精確,或者有其他建議與想法,也歡迎與我聯繫,謝謝。