基于jenkins构建持续集成ci平台
1994字约7分钟
2024-09-29
发布流程

使用 Gitlab 作为代码仓库 & 使用 Harbor 作为镜像仓库
1.部署gitlab
mkdir gitlab
cd gitlab
docker run -d \
  --name gitlab \
  -p 8443:443 \
  -p 9999:80 \
  -p 9998:22 \
  -v $PWD/config:/etc/gitlab \
  -v $PWD/logs:/var/log/gitlab \
  -v $PWD/data:/var/opt/gitlab \
  -v /etc/localtime:/etc/localtime \
  --restart=always \
  lizhenliang/gitlab-ce-zh:latest访问地址:http://IP:9999
初次会先设置管理员密码 ,然后登陆,默认管理员用户名root,密码就是刚设置的。
创建项目,提交测试代码
进入后先创建项目,提交代码,以便后面测试。
unzip tomcat-java-demo-master.zip
cd tomcat-java-demo-master
git init
git remote add origin http://192.168.0.13:9999/root/java-demo.git
git add .
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git commit -m 'all'
git push origin master2、部署Harbor镜像仓库
2.1 安装docker与docker-compose
2.2 解压离线包部署
# tar zxvf harbor-offline-installer-v2.0.0.tgz
# cd harbor
# cp harbor.yml.tmpl harbor.yml
# vi harbor.yml
hostname: reg.ctnrs.com
https:   # 先注释https相关配置
harbor_admin_password: Harbor12345
# ./prepare
# ./install.sh2.3 在Jenkins主机配置Docker可信任,如果是HTTPS需要拷贝证书
由于habor未配置https,还需要在docker配置可信任。
# cat /etc/docker/daemon.json 
{"registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "insecure-registries": ["192.168.0.12"]
}
# systemctl restart docker在Kubernetes平台部署Jenkins

部署nfs-pv自动供给
#安装nfs安装包(每个k8s节点都要安装)
yum install nfs-utils
#创建nfs共享目录
mkdir -p /nfs/kubernetes
#修改nfs配置文件
vim /etc/exports
/nfs/kubernetes *(rw,no_root_squash)
#启动nfs并加入开机自启
systemctl start nfs
systemctl enable nfs
#部署NFS实现自动创建PV插件:
git clone https://github.com/kubernetes-incubator/external-storage 
cd nfs-client/deploy 
kubectl apply -f rbac.yaml # 授权访问apiserver 
kubectl apply -f deployment.yaml # 部署插件,需修改里面NFS服务器地址与共享目录 
kubectl apply -f class.yaml # 创建存储类
kubectl get sc  # 查看存储类3.部署Jenkins
需要提前准备好PV自动供给,为Jenkins持久化数据。
cd jenkins 
kubectl apply -f jenkins.yaml 
kubectl get pods,svc -n ops -o wide访问地址:http://NodePort:30008
第一次部署会进行初始化:


点无,不安装任何插件


4.安装插件
默认从国外网络下载插件,会比较慢,建议修改国内源:
# 进入到nfs共享目录
cd /nfs/kubernetes/ops-jenkins-pvc-0b76f611-9e06-433d-a666-8e7d0e9f1138/updates
sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json 
sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
# 删除pod重建,pod名称改成你实际的
kubectl delete pod jenkins-d58f4db66-9cthj -n opshttp://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
管理Jenkins->系统配置-->管理插件-->分别搜索Git Parameter/Git/Pipeline/kubernetes/Config File Provider,选中点击安装。
- Git Parameter:Git参数化构建 
- Git:拉取代码 
- Pipeline:流水线 
- kubernetes:连接Kubernetes动态创建Slave代理 
- Config File Provider:存储kubectl用于连接k8s集群的kubeconfig配置文件 
5、添加kubernetes集群
管理Jenkins->Manage Nodes and Clouds->configureClouds->Add



构建Jenkins-Slave镜像

6.构建Slave镜像
cd jenkins-slave课件目录里涉及四个文件:
- Dockerfile:构建镜像 
- jenkins-slave:shell脚本启动slave.jar 
- settings.xml:修改maven官方源为阿里云源 
- slave.jar:agent程序,接受master下发的任务 
构建并推送到镜像仓库:
docker build -t 192.168.0.12/library/jenkins-slave-jdk:1.8 .
docker push 192.168.0.12/library/jenkins-slave-jdk:1.8Jenkins在K8S中动态创建代理


Kubernetes插件:Jenkins在Kubernetes集群中运行动态代理
插件介绍:https://github.com/jenkinsci/kubernetes-plugin

Jenkins Pipeline 介绍
Jenkins Pipeline是一套运行工作流框架,将原本独立运行单个或者多个节点的任 务链接起来,实现单个任务难以完成的复杂流程编排和可视化。
- Jenkins Pipeline是一套插件,支持在Jenkins中实现持续集成和持续交付; 
- Pipeline通过特定语法对简单到复杂的传输管道进行建模; 
- Jenkins Pipeline的定义被写入一个文本文件,称为Jenkinsfile。 


- Stages 是 Pipeline 中最主要的组成部分,Jenkins 将会按照 Stages 中描述的顺序 从上往下的执行。 
- Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作, 比如:Build、Test、Deploy 
- Steps:步骤,Steps 是最基本的操作单元,可以是打印一句话,也可以是构建一 个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh ‘mvn',就相当于我 们平时 shell 终端中执行 mvn命令一样。 

Jenkins在Kubernetes中持续部署
自动部署应用(yaml):
将kubectl工具封装到Slave镜像中,并通过Config File Provider插件存储连接K8s集群的kubeconfig认证文件,然后 挂载到Slave容器中,这样就能用kubectl apply deploy.yaml --kubeconfig=config
注:为提高安全性,kubeconfig文件应分配权限
除了上述方式,还可以使用Kubernetes Continuous Deploy插件,将资源配置(YAML)部署到Kubernetes,这种 不是很灵活性
7.编写Pipeline脚本




创建命名空间
kubectl create ns dev
kubectl create ns test
kubectl create ns prod将镜像仓库认证凭据保存在K8s Secret中
kubectl create secret docker-registry registry-pull-secret --docker-username=admin --docker-password=Harbor12345 --docker-server=192.168.0.12 -n dev
kubectl create secret docker-registry registry-pull-secret --docker-username=admin --docker-password=Harbor12345 --docker-server=192.168.0.12 -n test
kubectl create secret docker-registry registry-pull-secret --docker-username=admin --docker-password=Harbor12345 --docker-server=192.168.0.12 -n prod部署文件:deploy.yaml (将部署文件跟git仓库文件放在一起)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: REPLICAS
  selector:
    matchLabels:
      app: java
  template:
    metadata:
      labels:
        app: java
    spec:
      imagePullSecrets:
      - name: SECRET_NAME
      containers:
      - image: IMAGE_NAME
        name: java-demo
        resources:
          requests:
            cpu: 0.5
            memory: 500Mi
          limits:
            cpu: 1
            memory: 1Gi
        livenessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 40
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 40
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: java-demo
spec:
  selector:
    app: java
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: NodePort创建项目->流水线->Pipeline脚本如下:
// 公共
def registry = "192.168.0.12"
// 项目
def operation = "demo"
def app_name = "java-demo"
def image_name = "${registry}/${operation}/${app_name}:${BUILD_NUMBER}"
def git_address = "http://192.168.0.13:9999/root/java-demo.git"
// 认证
def secret_name = "registry-pull-secret"
def docker_registry_auth = "e2c15e7c-766e-4961-bddb-2160e6e1859b"
def git_auth = "d17ea255-ade3-4623-b2ce-0b40b87e7164"
def k8s_auth = "6f488843-b5e2-49ca-949b-eccb75b54f6c"
pipeline {
  agent {
    kubernetes {
        label "jenkins-slave"
        yaml """
kind: Pod
metadata:
  name: jenkins-slave
spec:
  containers:
  - name: jnlp
    image: "${registry}/library/jenkins-slave-jdk:1.8"
    imagePullPolicy: Always
    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: /tmp/m2
"""
        }
      
      }
    parameters {    
        gitParameter branch: '', branchFilter: '.*', defaultValue: 'master', description: '选择发布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
        choice (choices: ['1', '3', '5', '7'], description: '副本数', name: 'ReplicaCount')
        choice (choices: ['dev','test','prod'], description: '命名空间', name: 'Namespace')
    }
    stages {
        stage('拉取代码'){
            steps {
                checkout([$class: 'GitSCM', 
                branches: [[name: "${params.Branch}"]], 
                doGenerateSubmoduleConfigurations: false, 
                extensions: [], submoduleCfg: [], 
                userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]
                ])
            }
        }
        stage('代码编译'){
           steps {
             sh """
                mvn clean package -Dmaven.test.skip=true
                """ 
           }
        }
        stage('构建镜像'){
           steps {
                withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
                sh """
                  echo '
                    FROM lizhenliang/tomcat
                    LABEL maitainer lizhenliang
                    RUN rm -rf /usr/local/tomcat/webapps/*
                    ADD target/*.war /usr/local/tomcat/webapps/ROOT.war
                  ' > Dockerfile
                  docker build -t ${image_name} .
                  docker login -u ${username} -p '${password}' ${registry}
                  docker push ${image_name}
                """
                }
           } 
        }
        stage('部署到K8S平台'){
          steps {
              configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
                sh """
                  sed -i 's#IMAGE_NAME#${image_name}#' deploy.yaml
                  sed -i 's#SECRET_NAME#${secret_name}#' deploy.yaml
                  sed -i 's#REPLICAS#${ReplicaCount}#' deploy.yaml
                  kubectl apply -f deploy.yaml -n ${Namespace} --kubeconfig=admin.kubeconfig
                """
              }
          }
        }
    }
}上述脚本中,registry变量值改成你的镜像仓库地址。
将之前部署该项目涉及的yaml合并到一个名为deploy.yaml文件中,并提交到该项目代码仓库,与 pom.xml同级目录,用于上述脚本自动部署使用。
1、将harbor认证和gitlab认证保存到Jenkins凭据
管理Jenkins->安全-->管理凭据->Jnekins->添加凭据->Username with password
- Username:用户名 
- Password:密码 ID:留空 
- Description:描述 
分别添加连接git和harbor凭据,并修改上面脚本docker_registry_auth 和git_auth变量的值为Jenkins 凭据ID。
2、将kubeconfig存储在Jenkins,用于slave镜像里kubectl连接k8s集群
管理Jenkins-> Managed files->Add->Custom file ->Content字段内容是kubeconfig(默认路径在 master节点/root/.kube/config),然后复制ID替换上述脚本中k8s_auth变量的值。
8.构建测试


流水线脚本与源代码一起版本管理
Jenkinsfile文件建议与源代码一起版本管理,实现流水线即代码(Pipeline as Code)。 这样做的好处:
- 自动为所有分支创建流水线脚本 
- 方便流水线代码复查、追踪、迭代 
- 可被项目成员查看和编辑 


参考链接:https://www.cnblogs.com/u1s1/p/14231196.html
