前情交代:
- 在开发环境已经打好了镜像包,并且次镜像包在test环境已经测试通过,现在开发生产环境实现流水线的k8s部署
- k8s 应部署完毕,环境ok
- jenkins部署完毕
- 工作重点在jenkins的部署流程配置和通知k8s拉取镜像。
1. 原有的微服务代码一个字符都不用变
那么问题来了怎么让镜像在prod环境使用nacos上的*-prod.yaml文件呢?归根到底是要通知k8s设置spring-profile-action=prod.
怎么实现这个功能嗯?
- 先看一下现有的dockerfile文件:
FROM 10.1.8.151:8082/blade/jdk1.8-skywalking:latest
MAINTAINER Fengkai
RUN mkdir -p /wy/user
WORKDIR /wy/user
EXPOSE 8310
ADD ./target/user.jar ./app.jar
#设置环境变量
ENV SPRING_PROFILES_ACTIVE=dev APP_NAME=wy-user BACKEND_ADDER=10.1.33.131:11800
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE -Dserver.port=8310 -Duser.timezone=GMT+8 -server -Xms512m -Xmx512m -javaagent:/usr/local/skywalking/skywalking-agent.jar -Dskywalking.agent.service_name=$APP_NAME-$SPRING_PROFILES_ACTIVE -Dskywalking.collector.backend_service=$BACKEND_ADDER -jar app.jar
关注点在这里,怎么在k8s的yaml中可以替换这个地方的值,结论是可以的,解决方案语法如下:
spec: # 规格详情
minReadySeconds: 20
replicas: ${REPLICAS}
selector:
matchLabels:
app: ${deploymentName} #匹配的就是lab中定义的
template:
metadata:
labels:
app: ${deploymentName}
spec:
containers:
- name: ${deploymentName} #容器的名字 docker run --name 后面的名字
image: ${imageName} # harbor中获取,可能是环境变量的值
env:
- name: SPRING_PROFILES_ACTIVE
value: ${SPRING_PROFILES_ACTIVE}
ports:
- containerPort: ${EXPOSEPORT} # 每一个容器都是默认$servicePort启动,因为在容器内不没有问题的。
其他的问题应该都不存在,扫清了这个障碍,我们可以开始需求的开发了。
这里以一个微服务为例展开,最后会总总结,让其他30个微服务复用这一套流程即可,记住,对原来的代码一定不能有入侵性,即要解耦,要不然你有1000000个微服务的时候,你会烦死的。
2. jenkins配置
jenkins的配置值方式这里有三种方式:
- 自由风格
- pom风格
- 流水线
这里采用自由风格。为什么呢?因为本公司均采用的是这种自由风,不过后期我想修改下这个风格使用流水线的风格。
这里特别说明一下哦,这是三条路,每一条均能到达彼岸,所以不要纠结啊。知道语法后都是很简单的。解决问题才是关键。
所有不要纠结号码???嘿嘿嘿!~~
3. jenkins devops服务器脚本书写
3.1 新建一个item
没有个微服务创建一个item便于管理
3.2 配置参数
3.3 书写脚本
# docker暴露的端口 EXPOSE 7002
EXPOSEPORT='8310'
# k8s 编排service 端口
NODEPORT='30021'
# blade/blade-demo:12
echo ${DOCKER_IMAGE}
# 先跳到工作目录下:/data/jenkins/workspace/zz
# cd ${WORKSPACE}
# 再次切换到当前项目的路径下,主要是为了那yaml文件
# cd blade-ops/blade-admin/
#仓库地址
registryUrl='10.1.8.151:8082'
#项目名:
projectName=wy
#服务名
serviceName=user
#环境标签
SPRING_PROFILES_ACTIVE=prod
#pod的名字,如:blade-saber-prod
deploymentName=$projectName-$serviceName-$SPRING_PROFILES_ACTIVE
# 容器名字, 如blade-saber-prod
containerName=$projectName-$serviceName-$SPRING_PROFILES_ACTIVE
#镜像名字,如:10.1.8.151:8082/blade/blade-demo:12
imageName=$registryUrl/${DOCKER_IMAGE}
echo '============通知k8s拉取镜像开始==============='
#k8s master ip
MASTER='10.1.33.61'
# k8s 服务器上用于存放配置的目录 推动deploy_k8s.sh和*.yaml去的目录需要推送到k8smaster上
CONF_DIR='/data/kube-conf'
# 登录k8s username
K8S_USERNAME='root'
# jenkins服务器用于存放和k8s相关文件的目录
LOCAL_CON_DIR="/data/k8s"
SH_NAME='deploy_k8s.sh'
# file name of yaml 如:k8s-prod-deploy.yaml
YAML_NAME='deploy_k8s.yaml'
SH_NAME='deploy_k8s.sh'
# transfer yaml & sh to k8s
scp ${LOCAL_CON_DIR}/${YAML_NAME} ${MASTER}:${CONF_DIR} #远程传送yaml
scp ${LOCAL_CON_DIR}/${SH_NAME} ${MASTER}:${CONF_DIR} #远程传送sh
# run k8s script
echo "+x----->"
# k8s 服务器上/data/kube-conf/deploy_k8s.给执行权限
ssh -p '22' ${K8S_USERNAME}@${MASTER} chmod +x ${CONF_DIR}/${SH_NAME}
echo "sh----->"
# k8s 服务器上/data/kube-conf/deploy_k8s.执行
ssh -p '22' ${K8S_USERNAME}@${MASTER} sh ${CONF_DIR}/${SH_NAME} ${deploymentName} ${imageName} ${REPLICAS} ${NAMESPACE} ${YAML_NAME} ${SPRING_PROFILES_ACTIVE} ${EXPOSEPORT} ${NODEPORT}
echo '============通知k8s拉取镜像结束==============='
echo '============ THE END ==============='
脚本注释,解释的已经很清楚了吧。
总之一句话,就是把harbor上的镜像名字拿到手,然后传给下面的脚本去执行:在此服务器上有两个文件,把这两个文件上传的k8s上去拉取镜像,并且启动镜像的过程
3.4 deploy_k8s.sh 内容:
#!/bin/bash
CONF_DIR="/data/kube-conf" # k8s 服务器上用于存放配置的目录
imageName=$2 # like wy/h5:2
deploymentName=$1 #添加一个-prod表示生产环境 wy-h5-prod
imageName=$2 # like wy/h5:2
REPLICAS=$3 # like 3
NAMESPACE=$4 #like wy
YAML_NAME=$5 # file name of yaml like wy-h5-prod_deploy_k8s.yaml
SPRING_PROFILES_ACTIVE=$6
EXPOSEPORT=$7 # 7002
NODEPORT=$8 # 30003
# Pod running process
RUN=$(kubectl get po -n wy | grep -w ${deploymentName}|wc -l)
echo ${RUN}
if [ $RUN -eq 0 ];then
echo " deploy------>"
# apply pod service
export deploymentName=${deploymentName}
export imageName=${imageName}
export REPLICAS=${REPLICAS}
export NAMESPACE=${NAMESPACE}
export SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
export EXPOSEPORT=${EXPOSEPORT}
export NODEPORT=${NODEPORT}
envsubst < ${CONF_DIR}/${YAML_NAME}| kubectl apply -f -
else
# Delete pod service
echo " delete first----->"
#传参数进yaml文件
export deploymentName=${deploymentName}
export imageName=${imageName}
export REPLICAS=${REPLICAS}
export NAMESPACE=${NAMESPACE}
export SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
export EXPOSEPORT=${EXPOSEPORT}
export NODEPORT=${NODEPORT}
envsubst < ${CONF_DIR}/${YAML_NAME}| kubectl delete -f -
echo " sleep 20s to waitting delete old pods service----->"
sleep 20 # sleep 5 s to wait
echo " deply second----->"
# apply pod service
export deploymentName=${deploymentName}
export imageName=${imageName}
export REPLICAS=${REPLICAS}
export NAMESPACE=${NAMESPACE}
export SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
export EXPOSEPORT=${EXPOSEPORT}
export NODEPORT=${NODEPORT}
envsubst < ${CONF_DIR}/${YAML_NAME}| kubectl apply -f -
fi
exit
3.5 deploy_k8s.yaml内容
这个文件就是k8s执行的yaml文件,如果你对k8s一无所知,建议先去学习下。
${**}是传参过来的额,在deploy_k8s.sh中传递过来的。最初是由jenkins的脚本传递过来的。
kind: Deployment # 一次部署
apiVersion: apps/v1 # 版本号
metadata:
name: ${deploymentName} # 微服务服务的名字
namespace: ${NAMESPACE} # k8s中项目的名字
labels:
app: ${deploymentName} # id一样,唯一标识
spec: # 规格详情
minReadySeconds: 20
replicas: ${REPLICAS}
selector:
matchLabels:
app: ${deploymentName} #匹配的就是lab中定义的
template:
metadata:
labels:
app: ${deploymentName}
spec:
containers:
- name: ${deploymentName} #容器的名字 docker run --name 后面的名字
image: ${imageName} # harbor中获取,可能是环境变量的值
env:
- name: SPRING_PROFILES_ACTIVE
value: ${SPRING_PROFILES_ACTIVE}
ports:
- containerPort: ${EXPOSEPORT} # 每一个容器都是默认$servicePort启动,因为在容器内不没有问题的。
protocol: TCP
imagePullPolicy: IfNotPresent #Always 总是拉取,IfNotPresent如果不存在才拉取
imagePullSecrets: # 使用密码登录
- name: harbor-login-registry
restartPolicy: Always # 如果镜像出问题,就自己重新启动
terminationGracePeriodSeconds: 10 #优雅停机的秒数
strategy:
type: RollingUpdate #k8s滚动跟新,1.0,2.0, 保证高可用
rollingUpdate:
maxUnavailable: 25% # 最大不可用4 台每次关一个。
maxSurge: 25%
---
kind: Service
apiVersion: v1
metadata:
name: ${deploymentName}
namespace: ${NAMESPACE}
labels:
app: ${deploymentName}
spec:
ports:
- name: http
protocol: TCP
port: ${EXPOSEPORT} # 暴露出来的端口,可以让客户端访问的端口
targetPort: ${EXPOSEPORT} # 容器内部的端口,可以均设置为$servicePort
nodePort: ${NODEPORT} # 代理的端口。 要规划好,修改K8S中NodePort方式暴露服务的端口的默认范围(30000-32767)
selector:
app: ${deploymentName}
type: NodePort
sessionAffinity: None # 无须 会话亲和
特别注意:
env:
- name: SPRING_PROFILES_ACTIVE
value: ${SPRING_PROFILES_ACTIVE}
设置的不同的环境,可以通过传参的方式传递过来。
4. 测试
整个开发工作完成了。但感觉还是碎片化的,我们来实战一波。
4.1 构建项目
4.2 选择适当的值
关于 脚本,谁负责的微服务在dev环境中打的那个版本应该很清楚,这个要沟通好,别打错了。
副本集的个数,也可以根据业务情况选择哦:
4.3 点击开始构建
点击开始构建就会开始
4.4 查看构建日志
Success控制台输出
Started by user admin
Running as SYSTEM
Building on master in workspace /data/jenkins/workspace/wy-user-prod
[wy-user-prod] $ /bin/sh -xe /tmp/jenkins2172421775510506537.sh
+ EXPOSEPORT=8310
+ NODEPORT=30021
+ echo wy/user:16
wy/user:16
+ registryUrl=10.1.8.151:8082
+ projectName=wy
+ serviceName=user
+ SPRING_PROFILES_ACTIVE=prod
+ deploymentName=wy-user-prod
+ containerName=wy-user-prod
+ imageName=10.1.8.151:8082/wy/user:16
+ echo ============通知k8s拉取镜像开始===============
============通知k8s拉取镜像开始===============
+ MASTER=10.1.33.61
+ CONF_DIR=/data/kube-conf
+ K8S_USERNAME=root
+ LOCAL_CON_DIR=/data/k8s
+ SH_NAME=deploy_k8s.sh
+ YAML_NAME=deploy_k8s.yaml
+ SH_NAME=deploy_k8s.sh
+ scp /data/k8s/deploy_k8s.yaml 10.1.33.61:/data/kube-conf
+ scp /data/k8s/deploy_k8s.sh 10.1.33.61:/data/kube-conf
+ echo '+x----->'
+x----->
+ ssh -p 22 root@10.1.33.61 chmod +x /data/kube-conf/deploy_k8s.sh
+ echo 'sh----->'
sh----->
+ ssh -p 22 root@10.1.33.61 sh /data/kube-conf/deploy_k8s.sh wy-user-prod 10.1.8.151:8082/wy/user:16 3 wy deploy_k8s.yaml prod 8310 30021
3
delete first----->
deployment.apps "wy-user-prod" deleted
service "wy-user-prod" deleted
sleep 20s to waitting delete old pods service----->
deply second----->
deployment.apps/wy-user-prod created
service/wy-user-prod created
+ echo ============通知k8s拉取镜像结束===============
============通知k8s拉取镜像结束===============
+ echo '============ THE END ==============='
============ THE END ===============
Finished: SUCCESS
提示构建完成。
4.5 去k8s看下是否完成了发布
发布完成。
同理我们可以吧我们其他的微服务也创建一个items,做一些配置,做各自的发版工作,最后的结果如下:
整个部署就完成了。
5 总结,流程解释
5.1 说明
- 各个微服务使用的部署脚本deploy_k8s.sh deploy_k8s.yaml是一模一样的,没有变化
- jenkins中的脚本也仅有此处不同,如果你想在优化,可以他这些也写在一个脚本中,我此处觉得挺好的,不再优化了:嘿嘿嘿~~
5.2 流程说明
- jenkens发起构建流程
- 需要执行的脚本会传输到k8s服务器上
- 执行传输的k8s服务器上的脚本去harbor拉取镜像,使用
kubectl apply -f deploy_k8s.yaml
执行拉取镜像和启动镜像的任务。整个流程完成。
下一篇:jenkins使用自定义方式结合k8s实现ci/cd-前端代码实战https://blog.csdn.net/fsjwin/article/details/110739581