shell
shell
复制代码
mvn clean package -U -DskipTests=true
JAR_NAME=hotel-directsales-data-web.jar
JAR_DIR=hotel-directsales-data-web
SHORT_COMMIT_ID=${GIT_COMMIT:0:7}
Image_Name=hotel-directsales-data-sync
Image_Repository=aliyun-regirest-vpc.cn-shanghai.personal.cr.aliyuncs.com/tengrong-pro/xinlimei
short_branch=$(echo "$GIT_BRANCH" | awk -F'/' '{print $2}')
Image=$Image_Repository:$Image_Name-$short_branch-$SHORT_COMMIT_ID
Deployment=hotel-directsales-data-sync
NameSpace=pro
PODNUM=5
PodPort=8080
SKYWALKING_URL="aliyun-skywalking:8000"
SKYWALKING_AUTH="aliyun-skywalking-secret"
APOLLO_URL="http://apollo.com.cn "
EurekaUrl="k8s-eureka.com.cn:8761"
cpuLimit=4
memoryLimit=10241Mi
cpuRequest=1
memoryRequest=6144Mi
HEAP="-Xms4g -Xmx4g \
-Dserver.port=8080 \
-Deureka.client.serviceUrl.defaultZone=http://k8s-eureka.com.cn:8761/eureka/ \
-Dxxl.job.admin.addresses=http://k8s-xxljob.com.cn/ \
-Drocketmq.name-server=rocketmq-namesrv.com.cn:19876 \
-Dspring.activemq.broker-url=failover:(tcp://activemq.com.cn:61616) "
ENVIROMENT="pro"
#cd到项目目录下
cd $JAR_DIR
#拷贝Dockerfile文件
cp -rf /data/Dockerfile-pro/Dockerfile-xlm ./Dockerfile
#构建镜像
docker build \
--build-arg APOLLO_URL="$APOLLO_URL" \
--build-arg SKYWALKING_URL="$SKYWALKING_URL" \
--build-arg SKYWALKING_AUTH="$SKYWALKING_AUTH" \
--build-arg JAVA_NAME_ARG="$JAR_NAME" \
--build-arg SERVICE_NAME="$Deployment" \
--build-arg HEAP="$HEAP" \
--build-arg ENVIROMENT="$ENVIROMENT" \
-t $Image .
#替换资源模版变量
cp /data/Dockerfile-pro/xinlimei-template-k8s-memory.yml ./application.yml
sed -i "s/ProjectName/$Deployment/g" application.yml
sed -i "s#imageName#$Image#g" application.yml
sed -i "s/NameSpace/$NameSpace/g" application.yml
sed -i "s/PODNUM/$PODNUM/g" application.yml
sed -i "s/PodPort/$PodPort/g" application.yml
sed -i "s/EurekaUrl/$EurekaUrl/g" application.yml
sed -i "s/cpuLimit/$cpuLimit/g" application.yml
sed -i "s/memoryLimit/$memoryLimit/g" application.yml
sed -i "s/cpuRequest/$cpuRequest/g" application.yml
sed -i "s/memoryRequest/$memoryRequest/g" application.yml
cat application.yml
#推送镜像
docker push $Image
#滚动更新
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config -n $NameSpace apply -f application.yml
#健康检查
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config -n $NameSpace rollout status --timeout=480s deployment/$Deployment
pipline
pipline
复制代码
pipeline {
// 使用任意可用的agent执行
agent any
// 定义环境变量,这些变量将在整个Pipeline中使用
environment {
// ========== 项目配置 ==========
// JAR包名称
JAR_NAME = 'hotel-directsales-data-web.jar'
// 项目目录名
JAR_DIR = 'hotel-directsales-data-web'
// 镜像基础名称
Image_Name = 'hotel-directsales-data-sync'
// 阿里云镜像仓库地址
Image_Repository = 'aliyun-regirest-vpc.cn-shanghai.personal.cr.aliyuncs.com/tengrong-pro/xinlimei'
// Kubernetes部署名称
Deployment = 'hotel-directsales-data-sync'
// Kubernetes命名空间
NameSpace = 'pro'
// Pod副本数量
PODNUM = '5'
// 容器端口
PodPort = '8080'
// ========== SkyWalking APM配置 ==========
// SkyWalking服务地址,用于链路追踪
SKYWALKING_URL = 'aliyun-skywalking:8000'
// SkyWalking认证信息(secret名称)
SKYWALKING_AUTH = 'aliyun-skywalking-secret'
// ========== 中间件配置 ==========
// Apollo配置中心地址
APOLLO_URL = 'http://apollo.com.cn'
// Eureka注册中心地址
EurekaUrl = 'k8s-eureka.com.cn:8761'
// ========== K8s资源限制配置 ==========
// CPU限制(最大可用)
cpuLimit = '4'
// 内存限制(最大可用)
memoryLimit = '10241Mi'
// CPU请求(最小保证)
cpuRequest = '1'
// 内存请求(最小保证)
memoryRequest = '6144Mi'
// ========== JVM及Java应用参数 ==========
// Java堆内存设置及服务配置
HEAP = '-Xms4g -Xmx4g -Dserver.port=8080 -Deureka.client.serviceUrl.defaultZone=http://k8s-eureka.com.cn:8761/eureka/ -Dxxl.job.admin.addresses=http://k8s-xxljob.com.cn/ -Drocketmq.name-server=rocketmq-namesrv.com.cn:19876 -Dspring.activemq.broker-url=failover:(tcp://activemq.com.cn:61616)'
// 运行环境标识
ENVIROMENT = 'pro'
// ========== 动态生成的变量(将在Pipeline中赋值) ==========
// 短提交ID(Git commit前7位)
SHORT_COMMIT_ID = ''
// 短分支名(去掉origin/前缀)
short_branch = ''
// 完整镜像标签
Image = ''
}
// 选项配置
options {
// 构建历史保留策略:保留最近10个构建
buildDiscarder(logRotator(numToKeepStr: '10'))
// 禁止并发构建,防止资源冲突
disableConcurrentBuilds()
// 控制台输出带时间戳
timestamps()
// Pipeline超时设置为30分钟
timeout(time: 30, unit: 'MINUTES')
}
// 定义构建触发器(可选,根据需要启用)
triggers {
// 每15分钟检查一次代码变更(轮询SCM)
// pollSCM('H/15 * * * *')
}
// 构建阶段定义
stages {
// ========== 阶段1:初始化与变量准备 ==========
stage('初始化环境') {
steps {
script {
// 获取Git提交ID前7位作为版本标识
env.SHORT_COMMIT_ID = env.GIT_COMMIT ? env.GIT_COMMIT.take(7) : "unknown"
// 提取短分支名:去掉origin/前缀,只保留分支名
// 例如:origin/release/1.0 -> release/1.0
if (env.GIT_BRANCH) {
def branchParts = env.GIT_BRANCH.split('/')
if (branchParts.length > 1) {
// 去掉第一个元素(origin),用剩下的部分重新组合
env.short_branch = branchParts[1..-1].join('/')
} else {
env.short_branch = env.GIT_BRANCH
}
} else {
env.short_branch = "unknown"
}
// 组合完整的镜像地址:仓库/命名空间:镜像名-分支-提交ID
env.Image = "${env.Image_Repository}:${env.Image_Name}-${env.short_branch}-${env.SHORT_COMMIT_ID}"
// 打印关键变量用于调试
echo "=========================================="
echo "Git分支: ${env.GIT_BRANCH}"
echo "短分支名: ${env.short_branch}"
echo "提交ID: ${env.SHORT_COMMIT_ID}"
echo "构建镜像: ${env.Image}"
echo "=========================================="
}
}
}
// ========== 阶段2:Maven编译打包 ==========
stage('Maven构建') {
steps {
// 进入项目子目录执行构建
dir("${env.JAR_DIR}") {
// 使用Maven构建
// clean: 清理目标目录
// package: 打包
// -U: 强制更新依赖快照
// -DskipTests=true: 跳过测试加速构建(生产环境建议开启测试)
sh '''
mvn clean package -U -DskipTests=true
'''
}
}
}
// ========== 阶段3:Docker镜像构建 ==========
stage('构建Docker镜像') {
steps {
dir("${env.JAR_DIR}") {
script {
// 拷贝生产环境Dockerfile到项目目录
sh 'cp -rf /data/Dockerfile-pro/Dockerfile-xlm ./Dockerfile'
// 构建Docker镜像,通过build-arg传递配置参数
// 这些参数将在Dockerfile中用于配置Java应用
sh """
docker build \\
--build-arg APOLLO_URL="${env.APOLLO_URL}" \\
--build-arg SKYWALKING_URL="${env.SKYWALKING_URL}" \\
--build-arg SKYWALKING_AUTH="${env.SKYWALKING_AUTH}" \\
--build-arg JAVA_NAME_ARG="${env.JAR_NAME}" \\
--build-arg SERVICE_NAME="${env.Deployment}" \\
--build-arg HEAP="${env.HEAP}" \\
--build-arg ENVIROMENT="${env.ENVIROMENT}" \\
-t ${env.Image} .
"""
}
}
}
}
// ========== 阶段4:K8s部署模板处理 ==========
stage('生成K8s部署配置') {
steps {
dir("${env.JAR_DIR}") {
script {
// 拷贝K8s模板文件到项目目录
sh 'cp /data/Dockerfile-pro/xinlimei-template-k8s-memory.yml ./application.yml'
// 使用sed替换模板中的占位符变量
// 注意:使用#作为分隔符避免URL中的/冲突
sh """
sed -i "s/ProjectName/${env.Deployment}/g" application.yml
sed -i "s#imageName#${env.Image}#g" application.yml
sed -i "s/NameSpace/${env.NameSpace}/g" application.yml
sed -i "s/PODNUM/${env.PODNUM}/g" application.yml
sed -i "s/PodPort/${env.PodPort}/g" application.yml
sed -i "s/EurekaUrl/${env.EurekaUrl}/g" application.yml
sed -i "s/cpuLimit/${env.cpuLimit}/g" application.yml
sed -i "s/memoryLimit/${env.memoryLimit}/g" application.yml
sed -i "s/cpuRequest/${env.cpuRequest}/g" application.yml
sed -i "s/memoryRequest/${env.memoryRequest}/g" application.yml
"""
// 打印生成的K8s配置用于审查
echo "========== 生成的K8s部署配置 =========="
sh 'cat application.yml'
echo "======================================="
}
}
}
}
// ========== 阶段5:镜像推送 ==========
stage('推送镜像到仓库') {
steps {
script {
// 将构建好的镜像推送到阿里云私有镜像仓库
// 需要确保Jenkins节点已配置镜像仓库登录凭证(docker login)
sh "docker push ${env.Image}"
// 可选:清理本地镜像释放空间
// sh "docker rmi ${env.Image} || true"
}
}
}
// ========== 阶段6:Kubernetes部署 ==========
stage('部署到K8s集群') {
steps {
dir("${env.JAR_DIR}") {
script {
// 使用kubectl apply部署到Kubernetes集群
// 指定kubeconfig文件路径和命名空间
sh """
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config \\
-n ${env.NameSpace} \\
apply -f application.yml
"""
}
}
}
}
// ========== 阶段7:部署验证与健康检查 ==========
stage('健康检查') {
steps {
script {
// 使用kubectl rollout status检查部署状态
// 等待Deployment滚动更新完成,超时时间为480秒(8分钟)
// 会检查Pod是否成功创建并达到就绪状态
sh """
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config \\
-n ${env.NameSpace} \\
rollout status \\
--timeout=480s \\
deployment/${env.Deployment}
"""
echo "✅ 部署成功!应用已在Kubernetes集群中正常运行。"
}
}
}
}
// ========== 后置处理 ==========
post {
// 无论构建成功与否都执行
always {
script {
// 清理工作区(可选,根据磁盘空间需求决定)
// cleanWs()
echo "Pipeline执行完成,构建结果: ${currentBuild.currentResult}"
}
}
// 构建成功时执行
success {
script {
echo "🎉 构建部署成功!"
echo "部署镜像: ${env.Image}"
echo "K8s部署: ${env.Deployment}"
echo "命名空间: ${env.NameSpace}"
// 可以在这里添加通知,如钉钉/企业微信/邮件等
// dingtalk(
// robot: 'jenkins-robot',
// type: 'MARKDOWN',
// title: '部署成功通知',
// text: "${env.Deployment} 部署成功\n镜像: ${env.Image}"
// )
}
}
// 构建失败时执行
failure {
script {
echo "❌ 构建失败,请检查日志定位问题"
// 可以在这里添加失败通知
// 钉钉/企业微信/Slack通知等
}
}
// 构建不稳定时执行(如测试失败但构建成功)
unstable {
script {
echo "⚠️ 构建状态不稳定"
}
}
// 构建被中止时执行
aborted {
script {
echo "🛑 构建被手动中止"
}
}
}
}
deployment优雅上下线(nacos)
yaml
复制代码
[root@jenkins Dockerfile-pro]# cat template-k8s.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ProjectName
name: ProjectName
namespace: NameSpace
spec:
progressDeadlineSeconds: 600
replicas: PODNUM
revisionHistoryLimit: 10
selector:
matchLabels:
app: ProjectName
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: ProjectName
annotations:
alibabacloud.com/burst-resource: eci
spec:
containers:
- env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PODNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_PORT
value: 'PodPort'
- name: POD_NAME
value: Nacos_AppName
- name: NAMESPACE_ID
value: NAMESPACEID
- name: NACOS_URL
value: 'NacosUrl'
name: ProjectName
ports:
- containerPort: PodPort
name: app-port-PodPort
protocol: TCP
image: imageName
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /bin/sh
- '-c'
- >-
curl -X PUT
"${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
& sleep 5
curl -X PUT
"${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
& sleep 5
curl -X PUT
"${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
& sleep 35
livenessProbe:
failureThreshold: 10
initialDelaySeconds: 50
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: PodPort
timeoutSeconds: 1
readinessProbe:
failureThreshold: 10
initialDelaySeconds: 50
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: PodPort
timeoutSeconds: 1
resources:
limits:
cpu: 'cpuLimit'
memory: memoryLimit
requests:
cpu: 'cpuRequest'
memory: memoryRequest
volumeMounts:
- name: localtime
mountPath: /etc/localtime
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: aliyun-pro-register
restartPolicy: Always
terminationGracePeriodSeconds: 120
volumes:
- hostPath:
path: /etc/localtime
type: ''
name: localtime
deployment优雅上下线(eureka)
yaml
复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ProjectName
name: ProjectName
namespace: NameSpace
spec:
progressDeadlineSeconds: 600
replicas: PODNUM
revisionHistoryLimit: 10
selector:
matchLabels:
app: ProjectName
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: ProjectName
annotations:
alibabacloud.com/burst-resource: eci
spec:
containers:
- env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PODNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_PORT
value: 'PodPort'
- name: POD_NAME
value: ProjectName
- name: EUREKA_URL
value: 'EurekaUrl'
name: ProjectName
ports:
- containerPort: PodPort
name: app-port-PodPort
protocol: TCP
image: imageName
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /bin/sh
- '-c'
- |
curl -X PUT "${EUREKA_URL}/eureka/apps/${POD_NAME}/${POD_IP}:${POD_PORT}/status?value=OUT_OF_SERVICE"
sleep 45
sleep 15
livenessProbe:
failureThreshold: 10
initialDelaySeconds: 50
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: PodPort
timeoutSeconds: 1
readinessProbe:
failureThreshold: 10
initialDelaySeconds: 50
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: PodPort
timeoutSeconds: 1
resources:
limits:
cpu: 'cpuLimit'
memory: memoryLimit
requests:
cpu: 'cpuRequest'
memory: memoryRequest
volumeMounts:
- mountPath: /etc/localtime
name: volume-localtime
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: aliyun-pro-register
restartPolicy: Always
terminationGracePeriodSeconds: 120
volumes:
- hostPath:
path: /etc/localtime
type: ''
name: volume-localtime