K8S pipeline CICD實戰

一、環境準備

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展示
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章