基于 K8S 构建 Jenkins 微服务发布平台

实现汇总:

  1. 发布流程设计讲解
  2. 准备基础环境
    1. K8s环境(部署Ingress Controller,CoreDNS,Calico/Flannel)
    2. 部署代码版本仓库Gitlab
    3. 配置本地Git上传测试代码,创建项目到Gitlab
    4. 部署pinpoint 全链路监控系统(提前修改Dockerfile,打包镜像上传)
    5. 部署镜像仓库Harbor(开启helm仓库)
    6. master节点部署helm应用包管理器(配置本地helm仓库,上传helm包)
    7. 部署K8S 存储(nfs、ceph),master节点提供pv自动供给
    8. 部署MySQL集群(导入微服务数据库)
    9. 部署EFK日志采集(追加)
    10. 部署prometheus监控系统(追加)
  3. 在Kubernetes中部署Jenkins
  4. Jenkins Pipeline 及参数化构建
  5. Jenkins在K8S中动态创建代理
  6. 自定义构建Jenkins-Slave镜像
  7. 基于Kubernetes构建Jenkins CI系统
  8. Pipeline 集成 Helm 发布微服务项目

发布流程设计讲解

机器环境

当前环境部署主要是实现微服务自动发布和推送,具体实现的功能细节主要实现在下述几大软件上面。其实自动发布和推送有很多种方式,如有不足,请留言补充。

IP地址 主机名 服务配置
192.168.25.223 k8s-master01 Kubernetes-Master节点+Jenkins
192.168.25.225 k8s-node01 Kubernetes-Node节点
192.168.25.226 k8s-node02 Kubernetes-Node节点
192.168.25.227 gitlab-nfs Gitlab,NFS,Git
192.168.25.228 harbor harbor,mysql,docker,pinpoint

准备基础环境

K8s环境(部署Ingress Controller,CoreDNS,Calico/Flannel)

部署命令

单Master版:

ansible-playbook -i hosts single-master-deploy.yml -uroot -k

多Master版:

ansible-playbook -i hosts multi-master-deploy.yml -uroot -k

部署控制

如果安装某个阶段失败,可针对性测试.

例如:只运行部署插件

ansible-playbook -i hosts single-master-deploy.yml -uroot -k --tags addons

示例参考:https://github.com/ansible/ansible-examples


部署代码版本仓库Gitlab

部署docker

Uninstall old versions
$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
SET UP THE REPOSITORY
$ sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
$ sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
INSTALL DOCKER ENGINE
$ sudo yum install docker-ce docker-ce-cli containerd.io -y
$ sudo systemctl start docker && sudo systemctl enable docker
$ sudo docker run hello-world

部署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 \
passzhang/gitlab-ce-zh:latest

访问地址:http://IP:9999

初次会先设置管理员密码 ,然后登陆,默认管理员用户名root,密码就是刚设置的。

配置本地Git上传测试代码,创建项目到Gitlab

https://github.com/passzhang/simple-microservice

代码分支说明:

  • dev1 交付代码

  • dev2 编写Dockerfile构建镜像

  • dev3 K8S资源编排

  • dev4 增加微服务链路监控

  • master 最终上线

拉取master分支,推送到私有代码仓库:

git clone https://github.com/PassZhang/simple-microservice.git

# cd 进入simple-microservice目录
# 修改.git/config文件,将地址上传地址配置成本地gitlab既可以
vim /root/simple-microservice/.git/config
...
[remote "origin"]
url = http://192.168.25.227:9999/root/simple-microservice.git
fetch = +refs/heads/*:refs/remotes/origin/*
... # 下载之后,还需修改连接数据库配置(xxx-service/src/main/resources/application-fat.yml),本次测试我将数据库地址修改成192.168.25.228::3306.
# 修改好数据库地址后,才可以上传文件。 cd microservice
git config --global user.email "passzhang@example.com"
git config --global user.name "passzhang"
git add .
git commit -m 'all'
git push origin master

部署pinpoint 全链路监控系统(提前修改Dockerfile,打包镜像上传)


部署镜像仓库Harbor(开启helm仓库)

安装docker与docker-compose

# wget http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# yum install docker-ce -y
# systemctl start docker && systemctl enable docker
curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

2.2 解压离线包部署

# tar zxvf harbor-offline-installer-v1.9.1.tgz
# cd harbor
-----------
# vi harbor.yml
hostname: 192.168.25.228
http: 8088
-----------
# ./prepare
# ./install.sh --with-chartmuseum --with-clair
# docker-compose ps

--with-chartmuseum 参数表示启用Charts存储功能。

配置Docker可信任

由于habor未配置https,还需要在docker配置可信任。

# cat /etc/docker/daemon.json
{"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["192.168.25.228:8088"]
}
# systemctl restart docker
#这边配置好仓库之后,也要保证K8S的master节点和docker节点都能同时连接上。需要修改dameon.json文件。

master节点部署helm应用包管理器(配置本地helm仓库,上传helm包)

安装Helm工具

# wget https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz
# tar zxvf helm-v3.0.0-linux-amd64.tar.gz
# mv linux-amd64/helm /usr/bin/

配置国内Chart仓库

# helm repo add stable http://mirror.azure.cn/kubernetes/charts
# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# helm repo list

安装push插件

# helm plugin install https://github.com/chartmuseum/helm-push

如果网络下载不了,也可以直接解压课件里包:

# tar zxvf helm-push_0.7.1_linux_amd64.tar.gz
# mkdir -p /root/.local/share/helm/plugins/helm-push
# chmod +x bin/*
# mv bin plugin.yaml /root/.local/share/helm/plugins/helm-push

添加repo

# helm repo add  --username admin --password Harbor12345 myrepo http://192.168.25.228:8088/chartrepo/ms

推送与安装Chart

# helm push ms-0.1.0.tgz --username=admin --password=Harbor12345 http://192.168.25.228:8088/chartrepo/ms
# helm install --username=admin --password=Harbor12345 --version 0.1.0 http://192.168.25.228:8088/chartrepo/library/ms

部署K8S 存储(nfs、ceph),master节点提供pv自动供给

先准备一台NFS服务器为K8S提供存储支持。

# yum install nfs-utils -y
# vi /etc/exports
/ifs/kubernetes * (rw,no_root_squash)
# mkdir -p /ifs/kubernetes
# systemctl start nfs
# systemctl enable nfs

并且要在每个Node上安装nfs-utils包,用于mount挂载时用。

由于K8S不支持NFS动态供给,还需要先安装上图中的nfs-client-provisioner插件:

具体配置文件如下:

[root@k8s-master1 nfs-storage-class]# tree
.
├── class.yaml
├── deployment.yaml
└── rbac.yaml 0 directories, 3 files

rbac.yaml

[root@k8s-master1 nfs-storage-class]# cat rbac.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
name: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io

class.yaml

[root@k8s-master1 nfs-storage-class]# cat class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "true"

deployment.yaml

[root@k8s-master1 nfs-storage-class]# cat deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 192.168.25.227
- name: NFS_PATH
value: /ifs/kubernetes
volumes:
- name: nfs-client-root
nfs:
server: 192.168.25.227
path: /ifs/kubernetes # 部署时不要忘记将server地址修改成新的nfs地址。
# cd nfs-client
# vi deployment.yaml # 修改里面NFS地址和共享目录为你的
# kubectl apply -f .
# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-df88f57df-bv8h7 1/1 Running 0 49m

部署MySQL集群(导入微服务数据库)

# yum install mariadb-server -y
# systemctl start mariadb.service
# mysqladmin -uroot password '123456'

或者docker创建

docker run -d --name db -p 3306:3306 -v /opt/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 --character-set-server=utf8

最后将微服务数据库导入。

[root@cephnode03 db]# pwd
/root/simple-microservice/db
[root@cephnode03 db]# ls
order.sql product.sql stock.sql
[root@cephnode03 db]# mysql -uroot -p123456 <order.sql
[root@cephnode03 db]# mysql -uroot -p123456 <product.sql
[root@cephnode03 db]# mysql -uroot -p123456 <stock.sql # 配置好之后需要修改数据库授权
GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.25.%' IDENTIFIED BY '123456';

部署EFK日志采集(追加)


部署prometheus监控系统(追加)


在Kubernetes中部署Jenkins

参考:https://github.com/jenkinsci/kubernetes-plugin/tree/fc40c869edfd9e3904a9a56b0f80c5a25e988fa1/src/main/kubernetes

当前我们直接在kubernetes中部署Jenkins程序,部署之前需要提前准备好存储,前面已经部署了nfs 存储,也可以使用其他存储方案,例如ceph等。接下来我们开始部署吧。

Jenkins yaml文件汇总

[root@k8s-master1 jenkins]# tree
.
├── deployment.yml
├── ingress.yml
├── rbac.yml
├── service-account.yml
└── service.yml 0 directories, 5 files

rbac.yml

[root@k8s-master1 jenkins]# cat rbac.yml
---
# 创建名为jenkins的ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins ---
# 创建名为jenkins的Role,授予允许管理API组的资源Pod
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"] ---
# 将名为jenkins的Role绑定到名为jenkins的ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins

service-account.yml

[root@k8s-master1 jenkins]# cat service-account.yml
# In GKE need to get RBAC permissions first with
# kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin [--user=<user-name>|--group=<group-name>] ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins ---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"] ---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins

**ingress.yml **

[root@k8s-master1 jenkins]# cat ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: jenkins
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: 100m
spec:
rules:
- host: jenkins.test.com
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 80

service.yml

[root@k8s-master1 jenkins]# cat service.yml
apiVersion: v1
kind: Service
metadata:
name: jenkins
spec:
selector:
name: jenkins
type: NodePort
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
nodePort: 30006
- name: agent
port: 50000
protocol: TCP

deployment.yml

[root@k8s-master1 jenkins]# cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
labels:
name: jenkins
spec:
replicas: 1
selector:
matchLabels:
name: jenkins
template:
metadata:
name: jenkins
labels:
name: jenkins
spec:
terminationGracePeriodSeconds: 10
serviceAccountName: jenkins
containers:
- name: jenkins
image: jenkins/jenkins:lts
imagePullPolicy: Always
ports:
- containerPort: 8080
- containerPort: 50000
resources:
limits:
cpu: 1
memory: 1Gi
requests:
cpu: 0.5
memory: 500Mi
env:
- name: LIMITS_MEMORY
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- name: JAVA_OPTS
value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85 -Duser.timezone=Asia/Shanghai
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
securityContext:
fsGroup: 1000
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-home
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-home
spec:
storageClassName: "managed-nfs-storage"
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi

登录地址:直接输入ingress配置的域名:http://jenkins.test.com

修改插件地址:

由于默认插件源在国外服务器,大多数网络无法顺利下载,需修改国内插件源地址:

cd jenkins_home/updates
sed -i 's/http:\/\/updates.jenkins-ci.org\/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

Jenkins Pipeline 及参数化构建

Jenkins参数化构建流程图

Jenkins Pipeline是一套插件,支持在Jenkins中实现集成和持续交付管道;

  • Pipeline通过特定语法对简单到复杂的传输管道进行建模;

    1. 声明式:遵循与Groovy相同语法。pipeline { }
    2. 脚本式:支持Groovy大部分功能,也是非常表达和灵活的工具。node { }
  • Jenkins Pipeline的定义被写入一个文本文件,称为Jenkinsfile。

参考:https://jenkins.io/doc/book/pipeline/syntax/

当前环境中我们需要配置pipeline脚本,我们可以先来创建一个Jenkins-pipeline脚本测试一下

安装pipeline插件 : Jenkins 首页 ------ >系统管理 ------ > 插件管理 ------> 可选插件 ------> 过滤输入pipeline, 安装pipeline插件既可以使用。

流水线中输入以下脚本进行测试

pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building'
}
}
stage('Test') {
steps {
echo 'Testing'
}
}
stage('Deploy') {
steps {
echo 'Deploying'
}
}
}
}

测试结果如下:

日志如下:

控制台输出
Started by user admin
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/pipeline-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Build)
[Pipeline] echo
Building
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] echo
Testing
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] echo
Deploying
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

输出SUCCESS即成功测试

Jenkins在K8S中动态创建代理

前面我们已经完成了pipeline脚本的测试,但是考虑到Jenkins 主机性能有限,如果我们要运行大批量的任务,Jenkins 主机可能会崩溃,这时我们采用Jenkins-slave的方式,给Jenkins主机增加小弟,由Jenkins主机来部署任务,具体任务和编译则留给小弟去做。

传统的Jenkins Master/Slave架构

K8S中Jenkins Master/Slave架构

添加Kubernetes插件

Kubernetes插件:Jenkins在Kubernetes集群中运行动态代理.

插件介绍:https://github.com/jenkinsci/kubernetes-plugin

新增一个kubernetes 云

当前环境中我们需要将Jenkins和kubernetes 进行关联,让Jenkins可以连通kubernetes 并且自动在kubernetes 中 进行命令操作,需要添加kubernetes 云,操作步骤如下:

Jenkins 首页 ------ > 系统管理 ------ > 系统配置 ------ > 云 ------ > 新增一个云 ------ > Kubernetes

配置一下kubernetes 云,当前我们部署的Jenkins是在kubernetes 中直接部署的pod,Jenkins可以直接通过service 读取到kubernetes的地址,所以我们这个地方输入kubernetes的DNS地址(https://kubernetes.default)就可以了,输入完之后不要忘记点击链接测试哦。

Jenkins地址我们也直接输入DNS地址既可以,地址为(http://jenkins.default),这样我们就新增了一个kubernetes云。

自定义构建Jenkins-Slave镜像推送到镜像仓库

配置所需文件:

[root@k8s-master1 jenkins-slave]# tree
.
├── Dockerfile #构建Jenkins-slave所需
├── helm #helm 命令:用于在Jenkins-slave pod 工作时,执行helm 操作安装helm chart库。
├── jenkins-slave #jenkins-slave所需脚本
├── kubectl #kebectl 命令:用于在Jenkins-slave pod 工作中,执行pod 创建命令和查询pod 运行结果等。
├── settings.xml #Jenkins-slave 所需文件
└── slave.jar #Jenkins-slave jar包 0 directories, 6 files

Jenkins-slave 所需 Dockerfile文件

FROM centos:7
LABEL maintainer passzhang
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/
ENTRYPOINT ["jenkins-slave"]

参考:https://github.com/jenkinsci/docker-jnlp-slave

参考:https://plugins.jenkins.io/kubernetes

推送Jenkins-slave 镜像到harbor仓库

[root@k8s-master1 jenkins-slave]#
docker build -t jenkins-slave:jdk-1.8 . docker tag jenkins-slave:jdk-1.8 192.168.25.228:8088/library/jenkins-slave:jdk-1.8 docker login 192.168.25.228:8088 #登录私有仓库
docker push 192.168.25.228:8088/library/jenkins-slave:jdk-1.8 #推送镜像到私有仓库

配置好之后,需要使用pipeline 流水线测试一下是否可以直接调用Jenkins-slave ,查看Jenkins-slave 是否正常工作。

测试pipeline脚本:

pipeline {
agent {
kubernetes {
label "jenkins-slave"
yaml """
apiVersion: v1
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: 192.168.25.228:8088/library/jenkins-slave:jdk-1.8
"""
}
}
stages {
stage('Build') {
steps {
echo 'Building'
}
}
stage('Test') {
steps {
echo 'Testing'
}
}
stage('Deploy') {
steps {
echo 'Deploying'
}
}
}
}

部署截图如下:

Pipeline 集成 Helm 发布微服务项目

部署步骤:

拉取代码 ——> 代码编译 ——> 单元测试 ——> 构建镜像 ——> Helm部署到K8S 测试

创建新的Jenkins任务k8s-deploy-spring-cloud

增加pipeline脚本:

#!/usr/bin/env groovy
// 所需插件: Git Parameter/Git/Pipeline/Config File Provider/kubernetes/Extended Choice Parameter
// 公共
def registry = "192.168.25.228:8088"
// 项目
def project = "ms"
def git_url = "http://192.168.25.227:9999/root/simple-microservice.git"
def gateway_domain_name = "gateway.test.com"
def portal_domain_name = "portal.test.com"
// 认证
def image_pull_secret = "registry-pull-secret"
def harbor_registry_auth = "9d5822e8-b1a1-473d-a372-a59b20f9b721"
def git_auth = "2abc54af-dd98-4fa7-8ac0-8b5711a54c4a"
// ConfigFileProvider ID
def k8s_auth = "f1a38eba-4864-43df-87f7-1e8a523baa35" 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: '', description: '选择发布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
extendedChoice defaultValue: 'none', description: '选择发布的微服务', \
multiSelectDelimiter: ',', name: 'Service', type: 'PT_CHECKBOX', \
value: 'gateway-service:9999,portal-service:8080,product-service:8010,order-service:8020,stock-service:8030'
choice (choices: ['ms', 'demo'], description: '部署模板', name: 'Template')
choice (choices: ['1', '3', '5', '7', '9'], description: '副本数', name: 'ReplicaCount')
choice (choices: ['ms'], description: '命名空间', name: 'Namespace')
}
stages {
stage('拉取代码'){
steps {
checkout([$class: 'GitSCM',
branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]
])
}
}
stage('代码编译') {
// 编译指定服务
steps {
sh """
mvn clean package -Dmaven.test.skip=true
"""
}
}
stage('构建镜像') {
steps {
withCredentials([usernamePassword(credentialsId: "${harbor_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
docker login -u ${username} -p '${password}' ${registry}
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*}
image_name=${registry}/${project}/\${service_name}:${BUILD_NUMBER}
cd \${service_name}
if ls |grep biz &>/dev/null; then
cd \${service_name}-biz
fi
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
"""
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
sh """
# 添加镜像拉取认证
kubectl create secret docker-registry ${image_pull_secret} --docker-username=${username} --docker-password=${password} --docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
# 添加私有chart仓库
helm repo add --username ${username} --password ${password} myrepo http://${registry}/chartrepo/${project}
"""
}
}
}
}
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}/${project}/\${service_name}
tag=${BUILD_NUMBER}
helm_args="\${service_name} --set image.repository=\${image} --set image.tag=\${tag} --set replicaCount=${replicaCount} --set imagePullSecrets[0].name=${image_pull_secret} --set service.targetPort=\${service_port} myrepo/${Template}" # 判断是否为新部署
if helm history \${service_name} \${common_args} &>/dev/null;then
action=upgrade
else
action=install
fi # 针对服务启用ingress
if [ \${service_name} == "gateway-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${gateway_domain_name} \
\${common_args}
elif [ \${service_name} == "portal-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${portal_domain_name} \
\${common_args}
else
helm \${action} \${helm_args} \${common_args}
fi
done
# 查看Pod状态
sleep 10
kubectl get pods \${common_args}
"""
}
}
}
}

执行结果如下:

当前直接点击构建,构建时前面几次可能会失败,多构建一次,打印出所有参数,既可以直接执行成功。

点击发布gateway-service pod 查看日志结果

+ kubectl get pods -n ms --kubeconfig admin.kubeconfig
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 3h11m
eureka-1 1/1 Running 0 3h10m
eureka-2 1/1 Running 0 3h9m
ms-gateway-service-66d695c486-9x9mc 0/1 Running 0 10s
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS # 执行成功之后,会打印出来pod 信息

发布剩下的服务,并查看结果:

+ kubectl get pods -n ms --kubeconfig admin.kubeconfig
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 3h14m
eureka-1 1/1 Running 0 3h13m
eureka-2 1/1 Running 0 3h12m
ms-gateway-service-66d695c486-9x9mc 1/1 Running 0 3m1s
ms-order-service-7465c47d79-lbxgd 0/1 Running 0 10s
ms-portal-service-7fd6c57955-jkgkk 0/1 Running 0 11s
ms-product-service-68dbf5b57-jwpv9 0/1 Running 0 10s
ms-stock-service-b8b9895d6-cb72b 0/1 Running 0 10s
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS

查看eureka结果:

可以看到所有的服务模块都已经注册到eureka中了。

访问一下前端页面:

可以看到有商品查询出来,代表已经连接数据库,同时业务可以正常运行。大功告成了!

总结环境所需插件

  • 使用Jenkins的插件

    • Git & gitParameter
    • Kubernetes
    • Pipeline
    • Kubernetes Continuous Deploy
    • Config File Provider
    • Extended Choice Parameter
  • CI/CD环境特点
    • Slave弹性伸缩
    • 基于镜像隔离构建环境
    • 流水线发布,易维护
  • Jenkins参数化构建可帮助你完成更复杂环境CI/CD

Jenkins-k8s-helm-harbor-githab-mysql-nfs微服务发布平台实战的更多相关文章

  1. Jenkins-k8s-helm-eureka-harbor-githab-mysql-nfs微服务发布平台实战

    基于 K8S 构建 Jenkins 微服务发布平台 实现汇总: 发布流程设计讲解 准备基础环境 K8s环境(部署Ingress Controller,CoreDNS,Calico/Flannel) 部 ...

  2. 宜信开源|微服务任务调度平台SIA-TASK入手实践

    引言 最近宜信开源微服务任务调度平台SIA-TASK,SIA-TASK属于分布式的任务调度平台,使用起来简单方便,非常容易入手,部署搭建好SIA-TASK任务调度平台之后,编写TASK后配置JOB进行 ...

  3. 宜信开源微服务任务调度平台(SIA-TASK)

    背景 无论是互联网应用或者企业级应用,都充斥着大量的批处理任务.常常需要一些任务调度系统帮助开发者解决问题.随着微服务化架构的逐步演进,单体架构逐渐演变为分布式.微服务架构.在此的背景下,很多原先的任 ...

  4. 微服务管理平台nacos虚拟ip负载均衡集群模式搭建

    一.Nacos简介 Nacos是用于微服务管理的平台,其核心功能是服务注册与发现.服务配置管理. Nacos作为服务注册发现组件,可以替换Spring Cloud应用中传统的服务注册于发现组件,如:E ...

  5. php+mysql的微信文章发布平台

    如何在微信上发表丰富图文的文章? 最近在新浪云平台上做了一个php+mysql的微信文章发布平台,丫丫说. 在线编辑文章,扫一扫即可分享到微信,发到朋友圈,非常简单! http://yayashuo. ...

  6. 微服务治理平台的RPC方案实现

    导读:本文主要探讨了rpc框架在微服务化中所处的位置,需要解决的问题.同时介绍了用友云微服务治理平台的rpc解决方案,为什么选择该方案.该方案提供的好处是什么.同时也会介绍用友RPC框架的基本结构以及 ...

  7. 企业级分布式应用服务EDAS _Dubbo商业版_微服务PaaS平台 【EDAS Serverless 运维 创业】

    企业级分布式应用服务EDAS _Dubbo商业版_微服务PaaS平台_分布式框架 - 阿里云https://www.aliyun.com/product/edas?source_type=yqzb_e ...

  8. mPass多租户系统微服务开发平台

    目录 项目总体架构图 基于SpringBoot2.x.SpringCloud并采用前后端分离的企业级微服务,多租户系统架构微服务开发平台 mPaaS(Microservice PaaS)为租户业务开发 ...

  9. 字节跳动内部微服务架构-Docker实战学习笔记分享 真香

    前言 基于 Spring Cloud 的微服务设计和开发,已经越来越多地得到了更多企业的推广和应用,而 Spring Cloud 社区也在不断的迅速发展壮大之中,近几年时间,Spring Cloud ...

随机推荐

  1. Postman:非专业的并发测试

    Postman是开发中常用的接口测试工具,也可以用来进行并发测试. 使用方法如下: 1. 直接输入url 选择GET方法,点击Send. 结果打印一个"test",如下: 2. 使 ...

  2. js实现new

    function New(fn,...args){ let obj={} obj.__proto__=fn.prototype let result=fn.apply(obj,args) if(typ ...

  3. Vue学习笔记-基本语法

    插值文本(输出文本):{{ }}或v-textHtml(输出Html):v-html 监听属性常用于表单输入时要进行的动作watch : { dataValue:function(val) { }} ...

  4. 解决win10台式机插入耳机没有声音或者音量不大

    没有声音 如果是插入机箱的前面板,尝试切换到后面板,因为有些组装机箱的前面板是不能用的 有声音,单音量不大(注意:并不一定适用于所有情况,但是可以尝试一下) 原因:缺少驱动,可以确定下控制面板是否有这 ...

  5. FreeNOS学习3——了解目录结构和Main函数

    下载源码的文件夹,看到里面有以下内容: 解释一下里面都有啥 lib -> 共享代码 1.libfs 文件系统(磁盘管理) 2.liballoc 内存分配和虚拟内存映射(内存管理) 3.libex ...

  6. Oracle 11g R2 for Win10(64位)的安装注意点

    一般我们在win10系统安装oracle11g或者10g及更低版本的oracle客户端时,都是无法安装,一般安装的时候会闪退.这是什么原因呢?其实很简单,win10出的时间比较晚,在oracle11g ...

  7. 博客同步到CSDN客户端

    同步本人博客到CSDN客户端 http://blog.csdn.net/johnnyz1234

  8. JDBC 详解笔记

    # JDBC ## 1:What? 通过Java代码来操作数据库的. 数据库的种类很多,导致不同的数据库的操作方式是不同. 通过JDBC的标准完成.通过java语言完成对于数据库的CRUD. ## 2 ...

  9. c++简单实现循环队列

    栈的数据结构是先进后出,而队列的数据结构就是 一个出口一个入口入口只能入队,出口只能出队 实现的代码如下: /* 循环静态队列实现 2017年8月5日07:50:58 */ #ifndef __QUE ...

  10. 【题解】CF1056F Write the Contest(三分+贪心+DP)

    [题解]CF1056F Write the Contest(三分+贪心+DP) 最优化问题的三个解决方法都套在一个题里了,真牛逼 最优解应该是怎样的,一定存在一种最优解是先完成了耗时长的任务再干别的( ...