一、環境準備
1.1 代碼準備
- gitlab準備相關代碼,目前三個分支。該代碼用來部署
- 因爲使用的容器部署,所以每個項目下一個目錄有一個Dockerfile文件,如下
保證服務名和jar包名稱固定,jar包和服務有一樣的名字,pom.xml可以設置finalName屬性
FROM lizhenliang/java:8-jdk-alpine
LABEL maintainer pengjunjie
ENV JAVA_ARGS="-Dfile.encoding=UTF8 -Duser.timezone=GMT+08"
COPY ./target/owinfo-bjkj-exchange-management.jar ./
EXPOSE 5006
CMD java -jar $JAVA_ARGS $JAVA_OPTS /owinfo-bjkj-exchange-management.jar
- 保證在父pom.xml目錄能夠成功編譯打包
1.2 harbor準備
- 準備好部署服務的 chart模板放在template項目下,用helm進行部署會使用該模板,如下:
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.image.name }}
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
project: {{ .Values.project }}
service: {{ .Values.image.name }}
template:
metadata:
labels:
project: {{ .Values.project}}
service: {{ .Values.image.name }}
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: {{ .Values.image.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: Always
ports:
- protocol: TCP
containerPort: {{ .Values.service.targetPort }}
env:
- name: JAVA_OPTS
value: "-Xmx2g"
resources:
requests:
cpu: 0.5
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
readinessProbe:
tcpSocket:
port: {{ .Values.service.targetPort }}
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
tcpSocket:
port: {{ .Values.service.targetPort }}
initialDelaySeconds: 60
periodSeconds: 10
1.2 jenkins準備
- 準備jenkins-slave鏡像,保證有jdk、maven、docker、helm、kubectl等環境,保證正確的時區設置
FROM centos:7
LABEL maintainer pengjunjie
RUN yum install -y java-1.8.0-openjdk maven curl git libtool-ltdl-devel && \
yum clean all && \
rm -rf /var/cache/yum/* && \
mkdir -p /usr/share/jenkins
COPY slave.jar /usr/share/jenkins/slave.jar
COPY jenkins-slave /usr/bin/jenkins-slave
COPY settings.xml /etc/maven/settings.xml
RUN chmod +x /usr/bin/jenkins-slave
COPY helm kubectl /usr/bin/
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENTRYPOINT ["jenkins-slave"]
- jenkins確保下面插件已經安裝
kubernetes、pipeline、git、git parameter、Extended Choice Parameter、Pipeline unit steps、config file provider等插件
- 創建jenkins連接gitlab、harbor的認證憑據
- 創建jenkins-slave中 kubectl連接k8s集羣的認證文件
admin.kubeconfig生成方式如下
保證目錄下面有下面4個文件
ca.pem,ca-key.pem,ca-config.json,admin.json
ca-config.json 定義新的ca的過期時間等屬性,如下
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
admin.json 定義了ca的基本屬性,如下
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing",
"O": "system:masters",
"OU": "System"
}
]
}
生成admin證書
# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
生成admin用戶並綁定集羣權限
# export KUBE_APISERVER="https://192.168.217.140:6443"
# 設置集羣參數
# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=admin.kubeconfig
# 設置客戶端認證參數
# kubectl config set-credentials admin --client-certificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=admin.kubeconfig
# 設置上下文參數
# kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=admin.kubeconfig
# 設置默認上下文
# kubectl config use-context kubernetes --kubeconfig=admin.kubeconfig
最後把admin.kubeconfig的內容複製到jenkins裏面,生成認證id即可
# ls
admin.csr admin.json admin-key.pem admin.kubeconfig admin.pem ca-config.json ca-key.pem ca.pem
二 pipeline + CICD實戰
2.1 pipeline微服務模板
jenkins創建一個pipeline項目,pipeline文件如下
// 1、更改git_url項目地址
// 2、更改要發佈的服務列表
// 3、通過Namespace隔離,更改Namespace列表,這裏需要創建harbor 倉庫的名字爲${Namespace}, k8s集羣中也需要創建${Namespace}
// ${Namespace} 規定取包含項目和分支含義且唯一的名稱,比如 owinfo-bjkj, owinfo-bjkj-test, owinfo-bjkj-dev
// 4、保證http://${registry}/chartrepo/template下面有服務部署需要的chart模板
// 項目gitlab地址、gitlab認證ID
def git_url = "http://192.168.217.141:880/root/owinfo-bjkj.git"
def git_authId = "2b69269f-b299-42c4-8b7e-e2c6b2ded2c9"
// 項目harbor倉庫地址、項目harbor中的項目倉庫名稱、harbor認證ID
def registry = "192.168.217.142"
def harbor_authId = "9a8ef8f6-3be6-4958-b0fd-9bd53c285c7f"
// jenkins slave中kubectl認證
def k8s_authId="2f9e4da0-dca4-4f52-b464-15e723f4233a"
// image_pull_secret
def image_pull_secret="registry-pull-secret"
pipeline {
agent {
kubernetes {
label 'jenkins-slave'
yaml """
apiVersion: v1
kind: Pod
metadata:
labels:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "${registry}/library/jenkins-slave:jdk1.8"
imagePullPolicy: IfNotPresent
volumeMounts:
- name: docker-cmd
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: maven-cache
mountPath: /root/.m2
volumes:
- name: docker-cmd
hostPath:
path: /usr/bin/docker
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: maven-cache
hostPath:
path: /root/.m2
"""
}
}
parameters {
gitParameter branch: '',
branchFilter: '.*',
defaultValue: '',
description: '發佈的分支',
name: 'Branch',
quickFilterEnabled: false,
selectedValue: 'NONE',
sortMode: 'NONE',
tagFilter: '*',
type: 'PT_BRANCH'
extendedChoice defaultValue: 'none', description: '選擇發佈的微服務', \
multiSelectDelimiter: ',', name: 'Service', type: 'PT_CHECKBOX', \
value: 'owinfo-bjkj-exchange-management:5006,owinfo-bjkj-exchange-statistic:5007,owinfo-bjkj-exchange-monitor:5008'
choice (choices: ['microsoft-service'], description: '部署模板', name: 'Template')
choice (choices: ['owinfo-bjkj', 'owinfo-bjkj-test', 'owinfo-bjkj-dev', 'owinfo-4A'], description: '命名空間', name: 'Namespace')
choice (choices: ['1', '3', '5', '7'], description: '副本數目', name: 'ReplicaCount')
}
stages {
stage('拉取代碼') {
steps {
checkout([$class: 'GitSCM',
branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
gitTool: 'Default',
submoduleCfg: [],
userRemoteConfigs: [[url: "${git_url}",credentialsId: "${git_authId}"]]
])
}
}
stage('代碼編譯') {
steps {
sh """
mvn clean package -Dmaven.test.skip=true
"""
}
}
stage('構建鏡像') {
steps {
withCredentials([usernamePassword(credentialsId: "${harbor_authId}", passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {
sh """
docker login -u ${dockerUser} -p '${dockerPassword}' ${registry}
for service in \$(echo ${Service} |sed 's/,/ /g');do
service_name=\${service%:*}
image_name=${registry}/${Namespace}/\${service_name}:${BUILD_NUMBER}
cd \${service_name}
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
"""
configFileProvider([configFile(fileId: "${k8s_authId}",
targetLocation: "admin.kubeconfig")]) {
sh """
# 創建imagePullSecret、helm拉取鏡像時使用
kubectl create secret docker-registry ${image_pull_secret} \
--docker-username=${dockerUser} --docker-password=${dockerPassword} \
--docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
# 創建chart倉庫地址名稱爲template,該空間下面可以定義多個模板化的chart
helm repo add --username ${dockerUser} --password ${dockerPassword} template http://${registry}/chartrepo/template
"""
}
}
}
}
stage('Helm部署到K8S') {
steps {
sh """
common_args="-n ${Namespace} --kubeconfig admin.kubeconfig"
for service in \$(echo ${Service} |sed 's/,/ /g');do
service_name=\${service%:*}
service_port=\${service#*:}
image=${registry}/${Namespace}/\${service_name}
tag=${BUILD_NUMBER}
helm_args="\${service_name} --set image.repository=\${image} --set
image.tag=\${tag} --set replicaCount=${ReplicaCount} --set
image.name=\${service_name} --set project=${Namespace} --set
imagePullSecrets[0].name=${image_pull_secret} --set
service.targetPort=\${service_port} template/${Template}"
# 判斷是否爲新部署
if helm history \${service_name} \${common_args} &>/dev/null;then
action=upgrade
else
action=install
fi
helm \${action} \${helm_args} \${common_args}
done
# 查看pod狀態
sleep 30
kubectl get pods -o wide \${common_args}
"""
}
}
}
}
上面的模板是統一部署服務的模板,可以重複使用,需要注意下面事項:
-
保證構建速度,CICD跳過單元測試,將單元測試和代碼檢查的工作剝離出來
-
涉及到的數據庫、緩存、隊列環境未在集羣中部署,因此需要現有的環境
-
構建前,上面需要修改的地址必須要修改好
-
必須保證harbor和k8s事先有這個${Namespace}
-
必須保證參數化構建時,代碼的url和namespace對應上,比如test分支,我們部署到owinfo-bjkj-test
目前chart只使用deployment,服務部署完成後,需要選擇Ingress-controller、istio、或者Kong等網關暴露服務。同樣項目容器的日誌可以統一按照 /服務名/xxx/*.log的方式掛載到nfs,通過filebeat進行統一收集。
harbor作爲鏡像倉庫,如下:
2.2 還需要什麼?
- 統一掛載服務日誌到nfs,基於文件的方式用filebeat收集並存儲到es,kibana展示。動態jenkins-slave pod中的maven緩存掛載到的本地/root/.m2,同樣可以使用nfs掛載
- k8s部署Kong網關,進行服務暴露
- 剝離單元測試、代碼檢查,利用其它辦法集成sonarqube工具
- 利用prometheus進行k8s集羣內部監控,實現HPA等功能,grafana展示