在一个大型 Java 项目中, 通常整个项目会被划分为很多个小模块, 正常的 Jenkins 流水线无法针对某一个模块改动进行针对性的部署。
以下配置文件为动态感知部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: D_NAME
namespace: jzdata-<namespace>
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
app: D_NAME
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: D_NAME
spec:
containers:
- env:
- name: version
value: "D_TAG"
- name: TZ
value: Asia/Shanghai
envFrom:
- configMapRef:
name: D_NAME
image: D_HARBOR/D_IMG/D_NAME:D_TAG
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
tcpSocket:
port: web
timeoutSeconds: 1
name: D_NAME
ports:
- containerPort: D_PORT
name: web
protocol: TCP
readinessProbe:
failureThreshold: 3
tcpSocket:
port: web
timeoutSeconds: 1
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
resources:
limits:
cpu: "1"
memory: 2Gi
requests:
cpu: 125m
memory: 2Gi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /opt/log/stash
name: log-volume
- mountPath: /var/tmp
name: tmp-volume
- mountPath: /tmp
name: tmp-volume
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: jz-registry
restartPolicy: Always
schedulerName: default-scheduler
volumes:
- hostPath:
path: /opt/log/stash
type: DirectoryOrCreate
name: log-volume
- hostPath:
path: /opt/tmp
type: DirectoryOrCreate
name: tmp-volume
FROM registry.9zdata.cn/common/centos-jdk:1.8.1
WORKDIR /opt/
# P_JAR: jar 的名字(含.jar)
# P_SNAME: 服务名称
ENV WAR=P_JAR \
ENV=dev \
SERVICE_NAME=P_SNAME
# COPY libs /opt/libs/
COPY start.sh* P_PATH/target/${WAR} /opt/
ENV LC_ALL zh_CN.UTF-8
RUN chmod +x start.sh
CMD ["./start.sh"]
pipeline {
agent any
environment {
// 获取提交代码的用户
author = sh(returnStdout: true, script: 'git log --pretty=format:"%an" -1').trim()
// harbor 的地址
HARBOR_URL = 'registry.9zdata.cn'
// 镜像存在 harbor 的哪个 project 中
HARBOR_PROJECT = 'ninestone'
// 这个微服务需要部署的所有子服务名称
PROJECT_NAME_0 = "ninestone-common"
PROJECT_NAME_1 = 'ninestone-dynamic-page-service'
PROJECT_NAME_2 = 'ninestone-goods-service'
PROJECT_NAME_3 = 'ninestone-platform-service'
PROJECT_NAME_4 = 'ninestone-point-service'
PROJECT_NAME_5 = 'ninestone-tenant-service'
PROJECT_NAME_6 = 'ninestone-user-service'
PROJECT_NAME_7 = 'ninestone-web-api'
// 公共的 k8s-deployment 文件
K8S_DEPLOY_NAME = 'kubernetes-deployment.yaml'
// 需要打包的服务
ORIGIN_BUILD_PROJECT = ''
}
triggers {
// 配置 gitlab 的 webhook 信息
gitlab(triggerOnPush: true, triggerOnMergeRequest: false, branchFilterType: 'All', secretToken:"bf4c7a2bc847cc4e8438af7c2f4a3520")
}
stages {
stage('Prepare') {
steps {
script {
sh "echo 开始构建, 当前分支 : ${env.BRANCH_NAME}"
// 只允许 master、dev、sit 分支进行构建
if(env.BRANCH_NAME != 'master' && env.BRANCH_NAME != 'dev' && env.BRANCH_NAME != 'sit') {
error("自动构建只能是 master、dev、sit 分支")
}
// 获取本次的 commitId
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
// 根据不同分支发布到不同的 k8s 的 namespace
branch = ""
if (env.BRANCH_NAME == 'master') {
branch = "uat"
}
if (env.BRANCH_NAME == 'dev') {
branch = "dev"
}
if (env.BRANCH_NAME == 'sit') {
branch = "sit"
}
tag = tag_input ?: build_tag
echo "tag: ${tag}"
}
}
}
stage('env explain') {
steps {
script{
def diffFiles = sh(returnStdout: true, script: "git show HEAD | grep diff | awk '{print \$3}' | awk '{print substr(\$0,3)}' | tr '\\n' ','").trim()
def splits = diffFiles.split(',')
def modulePaths = new HashSet<String>();
for(def filePath in splits){
int index = filePath.indexOf('/')
if(index >= 0 && !filePath.contains('k8s') && !filePath.contains('sql')){
modulePaths.add(filePath.substring(0, index))
}
}
println 'modulePaths:' + modulePaths
ORIGIN_BUILD_PROJECT = modulePaths
}
}
}
stage('mvn package') {
steps {
sh '''
source /home/.bashrc
mvn clean && mvn package -Dmaven.test.skip=true
'''
}
}
stage('make image & kube apply') {
parallel {
stage('ninestone-dynamic-page-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-dynamic-page-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_1}')
kubeApply('${PROJECT_NAME_1}', '19010')
}
}
stage('ninestone-goods-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-goods-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_2}')
kubeApply('${PROJECT_NAME_2}', '19015')
}
}
stage('ninestone-platform-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-platform-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_3}')
kubeApply('${PROJECT_NAME_1}', '19003')
}
}
stage('ninestone-point-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-point-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_4}')
kubeApply('${PROJECT_NAME_1}', '19014')
}
}
stage('ninestone-tenant-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-tenant-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_5}')
kubeApply('${PROJECT_NAME_1}', '19004')
}
}
stage('ninestone-user-service') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-user-service') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_6}')
kubeApply('${PROJECT_NAME_1}', '19005')
}
}
stage('ninestone-web-api') {
when {
expression {
return ORIGIN_BUILD_PROJECT.contains('ninestone-web-api') || ORIGIN_BUILD_PROJECT.contains('ninestone-common')
}
}
steps {
buildImages('${PROJECT_NAME_7}')
kubeApply('${PROJECT_NAME_1}', '19012')
}
}
}
}
}
parameters {
string(name: 'tag_input', defaultValue: '', description: '部署的服务的镜像标签')
booleanParam(name: 'origin_build_all', defaultValue: false, description: '是否打包整个服务')
}
post {
always {
dingtalk(
robot: '通知机器人',
type: 'ACTION_CARD',
title: '你有新的消息, 请注意查收',
text: [
"# [钉钉通知工具人~](${env.JOB_URL}) ",
"---",
"- 项目: ${JOB_NAME}",
"- 分支: ${currentBuild.projectName}",
"- 编号: [#${currentBuild.number}](${env.BUILD_URL})",
"- 状态: ${currentBuild.currentResult}",
"- 持续时间: ${currentBuild.durationString}",
"- 提交人: ${author}",
"- 消息: 你的任务已经部署, 请注意",
"- 部署服务: ${ORIGIN_BUILD_PROJECT}"
]
)
}
}
}
def buildImages(server_name) {
sh """
rm -f ./Dockerfile_tmp_${server_name}
cp ./Dockerfile ./Dockerfile_tmp_${server_name}
sed -i \"s/P_JAR/${server_name}.jar/g\" ./Dockerfile_tmp_${server_name}
sed -i \"s/P_SNAME/${server_name}/g\" ./Dockerfile_tmp_${server_name}
sed -i \"s#P_PATH#${server_name}#g\" ./Dockerfile_tmp_${server_name}
cat ./Dockerfile_tmp_${server_name}
docker build -t ${HARBOR_URL}/${HARBOR_PROJECT}/${server_name}:${tag} -f Dockerfile_tmp_${server_name} .
docker push ${HARBOR_URL}/${HARBOR_PROJECT}/${server_name}:${tag}
docker rmi ${HARBOR_URL}/${HARBOR_PROJECT}/${server_name}:${tag}
"""
}
def kubeApply(server_name, port) {
sh """
cp ./k8s/${K8S_DEPLOY_NAME} ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/D_NAME/${server_name}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/<namespace>/${branch}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/D_HARBOR/${HARBOR_URL}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/D_IMG/${HARBOR_PROJECT}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/D_TAG/${tag}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
sed -i \"s/D_PORT/${port}/g\" ./k8s/${server_name}/${K8S_DEPLOY_NAME}
cat ./k8s/${server_name}/${K8S_DEPLOY_NAME}
kubectl apply -f ./k8s/${server_name}/${K8S_DEPLOY_NAME}
"""
}