这个pipeline是专门部署Java后端的
configMap
apiVersion: v1
kind: ConfigMap
metadata:
name: maven-settings
namespace: jenkins
data:
settings.xml: |
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>/root/.m2/repository</localRepository>
<!-- 1. 私有Nexus认证(你的账号密码) -->
<servers>
<server>
<id>maven-public</id>
<username>admin</username>
<password>123456</password>
</server>
</servers>
<!-- 2. 核心:解除HTTP拦截 + 公私包分流 -->
<mirrors>
<!-- 放行内网HTTP私有仓库,解除Maven拦截(关键!) -->
<mirror>
<id>maven-public</id>
<name>私有Nexus仓库</name>
<url>http://nexus-nexus-repository-manager.nexus.svc.cluster.local:8081/repository/maven-public/</url>
<mirrorOf>maven-public</mirrorOf>
</mirror>
<!-- 公有依赖走阿里云,速度最快 -->
<mirror>
<id>aliyunmaven</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central,spring,plugins</mirrorOf>
</mirror>
</mirrors>
<!-- 3. 私有仓库配置 -->
<profiles>
<profile>
<id>nexus-profile</id>
<repositories>
<repository>
<id>maven-public</id>
<url>http://nexus-nexus-repository-manager.nexus.svc.cluster.local:8081/repository/maven-public/</url>
<releases><enabled>true</enabled></releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven-public</id>
<url>http://nexus-nexus-repository-manager.nexus.svc.cluster.local:8081/repository/maven-public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>nexus-profile</activeProfile>
</activeProfiles>
</settings>
❯ kubectl apply -f maven-settings-cm.yaml -n jenkins
configmap/maven-settings created
❯ pwd
/home/cy/workspace/k8s-helm/jenkins/configMap
❯ ls
docker-config-cm.yaml maven-settings-cm.yaml
~/workspace/k8s-helm/jenkins/configMap ❯
# docker-config-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-insecure-registry
namespace: jenkins
data:
daemon.json: |
{
"insecure-registries": ["harbor-core.harbor.svc.cluster.local:80"]
}
❯ kubectl apply -f docker-config-cm.yaml -n jenkins
configmap/docker-insecure-registry created
~/workspace/k8s-helm/jenkins/configMap ❯
docker推送基础镜像到harbor
# 修改宿主机的配置,让docker可以处理http请求
❯ cat /etc/docker/daemon.json
{
"data-root": "/home/cy/docker",
"insecure-registries": ["harbor.cyan.com","harbor-core.harbor.svc.cluster.local"]
}
在jenkins构建中直接使用宿主机的docker.socket
# 拉镜像
docker pull eclipse-temurin:21-jdk-alpine
# 1. 给镜像打标签(关键:格式 = harbor域名/项目名/镜像名:版本)
docker tag eclipse-temurin:21-jdk-alpine harbor.cyan.com/library/eclipse-temurin:21-jdk-alpine-3.23
# 2. 登录 Harbor(无需加 http!)
docker login harbor.cyan.com -u admin -p 123456
# 3. 推送镜像到 Harbor
docker push harbor.cyan.com/library/eclipse-temurin:21-jdk-alpine-3.23
kankio
Kaniko 是 Google 开源的、专为 Kubernetes 设计的无守护进程容器镜像构建工具,核心价值是在容器内安全构建镜像、无需 Docker Daemon、无需特权权限。
❯ cat kankio-secret.sh
kubectl delete secret harbor-regcred -n jenkins --ignore-not-found=true
kubectl create secret docker-registry harbor-regcred \
--namespace=jenkins \
--docker-server=harbor.cyan.com \
--docker-username=admin \
--docker-password=123456
~/workspace/k8s-helm/jenkins/secret ❯
clusterrolebinding
ClusterRoleBinding = 把 “集群级权限” 绑定给 “用户 / ServiceAccount / 用户组” 让某个账号能在整个 Kubernetes 集群里干活,而不是只在某个 namespace 里。
❯ cat clusterrolebinding.sh
kubectl create clusterrolebinding jenkins-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=jenkins:jenkins
~/workspace/k8s-helm/jenkins/secret ❯
pipeline
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
metadata:
name: dev-template
namespace: jenkins
spec:
serviceAccountName: jenkins
hostAliases:
- ip: "10.0.0.2"
hostnames:
- "harbor.cyan.com"
containers:
- name: maven
image: maven:3.9.12-eclipse-temurin-21
command: ['cat']
tty: true
volumeMounts:
- mountPath: /root/.m2/settings.xml
name: maven-settings
subPath: settings.xml
- mountPath: /root/.m2/repository
name: m2-cache
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command: ['cat']
tty: true
volumeMounts:
- mountPath: /kaniko/.docker
name: docker-config
- name: jnlp
image: jenkins/inbound-agent:3355.v388858a_47b_33-2-jdk21
args: ["$(JENKINS_SECRET)", "$(JENKINS_AGENT_NAME)"]
- name: k8s-tools
image: gcr.io/cloud-builders/kubectl
command: ['cat']
tty: true
securityContext:
runAsUser: 1000
volumes:
- name: maven-settings
configMap:
name: maven-settings
items:
- key: settings.xml
path: settings.xml
- name: m2-cache
hostPath:
path: /home/cy/workspace/k8s-helm/jenkins/data
type: DirectoryOrCreate
- name: docker-config
projected:
sources:
- secret:
name: harbor-regcred
items:
- key: .dockerconfigjson
path: config.json
"""
}
}
parameters {
// 选择环境
choice(name: 'SPRING_PROFILE',choices: ['prod', 'dev'], description: '选择要执行的环境')
// 这里会自动被脚本填充分支列表
string(name: 'BRANCH', defaultValue: '1.0.0', description: 'GIT 分支')
}
environment {
APP_NAME = "${JOB_NAME}"
// 全程只用 harbor.cyan.com
HARBOR_ADDR = "harbor.cyan.com"
HARBOR_USER = "admin"
HARBOR_PWD = "123456"
HARBOR_PROJECT = "library"
// 基础镜像
JAVA_IMAGE = "${HARBOR_ADDR}/${HARBOR_PROJECT}/eclipse-temurin:21-jdk-alpine-3.23"
// 构建产物
IMAGE_NAME = "${JOB_NAME}"
IMAGE_TAG = "${BUILD_NUMBER}"
LATEST_TAG = "latest"
FULL_IMAGE = "${HARBOR_ADDR}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}"
FULL_IMAGE_LATEST = "${HARBOR_ADDR}/${HARBOR_PROJECT}/${IMAGE_NAME}:${LATEST_TAG}"
GIT_URL = "https://github.com/cyan-daimao/${JOB_NAME}.git"
JAR_MODULE = "${JOB_NAME}-application"
K8S_NAMESPACE = "${params.SPRING_PROFILE}"
WORKSPACE = "/home/jenkins/agent/workspace/${JOB_NAME}"
}
stages {
stage('拉取代码') {
steps {
container('maven') {
script {
sh """
git config --global --add safe.directory ${WORKSPACE}
rm -rf ${WORKSPACE}/*
git clone -b ${params.BRANCH} ${GIT_URL} ${WORKSPACE}
cd ${WORKSPACE}
echo "CODE_CHANGED=true" > ${WORKSPACE}/build-flag
git fetch --all
"""
// 核心修复:转成 List,Jenkins 就不报错了
def branchList = sh(
script: """
cd ${WORKSPACE}
git branch -r | grep -v HEAD | sed 's/origin\///g' | sort -u
""",
returnStdout: true
).trim().split('\n').toList() // 这里加 toList()
// 更新参数下拉框
properties([
parameters([
choice(name: 'SPRING_PROFILE', choices: ['prod', 'dev'], description: '环境'),
choice(name: 'BRANCH', choices: branchList, description: 'Git分支')
])
])
}
}
}
}
stage('Maven打包') {
when {
expression {
def flag = readFile("${WORKSPACE}/build-flag").trim()
return flag == "CODE_CHANGED=true"
}
}
steps {
container('maven') {
sh """
echo "=== 开始Maven打包 ==="
cd ${WORKSPACE}
mvn clean package -DskipTests
echo "=== 验证打包产物 ==="
ls -l ./${JAR_MODULE}/target/*.jar
JAR_FILE=$(ls ./${JAR_MODULE}/target/*.jar | grep -v "sources" | grep -v "test")
if [ -z "${JAR_FILE}" ]; then
echo "❌ 未找到有效的JAR包!"
exit 1
fi
echo "找到JAR包:${JAR_FILE}"
"""
}
}
}
stage('构建并推送镜像') {
when {
expression {
def flag = readFile("${WORKSPACE}/build-flag").trim()
return flag == "CODE_CHANGED=true"
}
}
steps {
container('kaniko') {
sh """
echo "=== 编写Dockerfile ==="
cd ${WORKSPACE}
JAR_FILE=$(ls ./${JAR_MODULE}/target/*.jar | grep -v "sources" | grep -v "test")
JAR_NAME=$(basename ${JAR_FILE})
cat > Dockerfile << EOF
FROM ${JAVA_IMAGE}
WORKDIR /app
COPY ./${JAR_MODULE}/target/${JAR_NAME} /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=${params.SPRING_PROFILE}", "/app/app.jar"]
EOF
echo "=== 查看Dockerfile ==="
cat Dockerfile
echo "=== Kaniko 构建并推送镜像 ==="
/kaniko/executor \
--context ${WORKSPACE} \
--dockerfile ${WORKSPACE}/Dockerfile \
--destination ${FULL_IMAGE} \
--destination ${FULL_IMAGE_LATEST} \
--insecure \
--insecure-pull
"""
}
}
}
stage('部署到k3s') {
steps {
container('k8s-tools') {
sh """
set -e
set -x
echo "=== 创建dev命名空间(如果不存在) ==="
kubectl create namespace ${K8S_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
echo "=== 创建镜像拉取密钥(用 harbor.cyan.com) ==="
kubectl create secret docker-registry harbor-regcred \
--namespace=${K8S_NAMESPACE} \
--docker-server=${HARBOR_ADDR} \
--docker-username=${HARBOR_USER} \
--docker-password=${HARBOR_PWD} \
--dry-run=client -o yaml | kubectl apply -f -
echo "=== 生成部署配置并应用 ==="
cd ${WORKSPACE}
cat > deploy.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${APP_NAME}
namespace: ${K8S_NAMESPACE}
spec:
replicas: 1
selector:
matchLabels:
app: ${APP_NAME}
template:
metadata:
labels:
app: ${APP_NAME}
annotations:
build-number: "${BUILD_NUMBER}"
spec:
containers:
- name: ${APP_NAME}
image: ${FULL_IMAGE}
imagePullPolicy: Always
ports:
- containerPort: 8080
imagePullSecrets:
- name: harbor-regcred
---
apiVersion: v1
kind: Service
metadata:
name: ${APP_NAME}-svc
namespace: ${K8S_NAMESPACE}
spec:
selector:
app: ${APP_NAME}
ports:
- port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${APP_NAME}-ingress
namespace: ${K8S_NAMESPACE}
spec:
ingressClassName: nginx
rules:
- host: ${APP_NAME}-${K8S_NAMESPACE}.cyan.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ${APP_NAME}-svc
port:
number: 8080
EOF
kubectl apply -f deploy.yaml -n ${K8S_NAMESPACE}
kubectl rollout restart deployment/${APP_NAME} -n ${K8S_NAMESPACE}
kubectl rollout status deployment/${APP_NAME} -n ${K8S_NAMESPACE}
echo "✅ 部署成功!应用访问地址:http://${APP_NAME}-${K8S_NAMESPACE}.cyan.com"
"""
}
}
}
}
post {
success {
echo "🎉 部署成功!"
}
failure {
echo "❌ 构建失败,请检查日志!"
}
}
}