|
|
@@ -6,6 +6,7 @@ pipeline {
|
|
|
string(name: 'NAMESPACE', defaultValue: 'portal-frontend', description: 'K8s 命名空间')
|
|
|
string(name: 'DOMAIN', defaultValue: 'radar-power.asia', description: 'Ingress 域名(留空则不创建 Ingress)')
|
|
|
string(name: 'TLS_SECRET', defaultValue: 'portal-tls', description: 'TLS Secret 名称(仅在 DOMAIN 非空时使用)')
|
|
|
+ booleanParam(name: 'FORCE_UPDATE_CONFIG', defaultValue: false, description: '强制更新配置(包括 Deployment 和 Ingress)')
|
|
|
}
|
|
|
|
|
|
environment {
|
|
|
@@ -28,8 +29,8 @@ pipeline {
|
|
|
echo ">>> 环境:${params.env}, Harbor项目:${env.HARBOR_PROJECT}, K8s命名空间:${params.NAMESPACE}"
|
|
|
if (params.DOMAIN?.trim()) {
|
|
|
echo ">>> 域名:${params.DOMAIN}, TLS Secret:${params.TLS_SECRET}"
|
|
|
- echo ">>> 注意:TLS Secret 应该已经存在,请确保命名空间一致"
|
|
|
}
|
|
|
+ echo ">>> 强制更新配置:${params.FORCE_UPDATE_CONFIG}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -41,54 +42,54 @@ pipeline {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- stage('🔧 构建 Docker 镜像') {
|
|
|
+ stage('�� 验证 TLS Secret') {
|
|
|
+ when {
|
|
|
+ expression { params.DOMAIN?.trim() }
|
|
|
+ }
|
|
|
steps {
|
|
|
script {
|
|
|
sh """
|
|
|
- docker login -u ${HARBOR_USER} -p ${HARBOR_PASS} ${HARBOR_HOST}
|
|
|
- docker build --build-arg ENV=${params.env} -t ${IMAGE_TAG} .
|
|
|
+ export KUBECONFIG=${KUBECONFIG_PATH}
|
|
|
+
|
|
|
+ # 验证 TLS Secret 是否存在
|
|
|
+ if ! kubectl get secret ${params.TLS_SECRET} -n ${params.NAMESPACE} >/dev/null 2>&1; then
|
|
|
+ echo "❌ TLS Secret '${params.TLS_SECRET}' 在命名空间 '${params.NAMESPACE}' 中不存在!"
|
|
|
+ echo "请先创建 TLS Secret:"
|
|
|
+ echo "kubectl create secret tls ${params.TLS_SECRET} --cert=fullchain.pem --key=privkey.pem -n ${params.NAMESPACE}"
|
|
|
+ exit 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo "✅ TLS Secret '${params.TLS_SECRET}' 验证成功"
|
|
|
"""
|
|
|
- echo "✅ 镜像构建成功:${IMAGE_TAG}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- stage('🚀 推送镜像到 Harbor') {
|
|
|
+ stage('🔧 构建 Docker 镜像') {
|
|
|
steps {
|
|
|
script {
|
|
|
sh """
|
|
|
- docker push ${IMAGE_TAG}
|
|
|
- docker rmi ${IMAGE_TAG}
|
|
|
+ docker login -u ${HARBOR_USER} -p ${HARBOR_PASS} ${HARBOR_HOST}
|
|
|
+ docker build --build-arg ENV=${params.env} -t ${IMAGE_TAG} .
|
|
|
"""
|
|
|
- echo "✅ 镜像推送并本地清理完成"
|
|
|
+ echo "✅ 镜像构建成功:${IMAGE_TAG}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- stage('�� 验证 TLS Secret') {
|
|
|
- when {
|
|
|
- expression { params.DOMAIN?.trim() }
|
|
|
- }
|
|
|
+ stage('🚀 推送镜像到 Harbor') {
|
|
|
steps {
|
|
|
script {
|
|
|
sh """
|
|
|
- export KUBECONFIG=${KUBECONFIG_PATH}
|
|
|
-
|
|
|
- # 验证 TLS Secret 是否存在
|
|
|
- if ! kubectl get secret ${params.TLS_SECRET} -n ${params.NAMESPACE} >/dev/null 2>&1; then
|
|
|
- echo "❌ TLS Secret '${params.TLS_SECRET}' 在命名空间 '${params.NAMESPACE}' 中不存在!"
|
|
|
- echo "请先创建 TLS Secret:"
|
|
|
- echo "kubectl create secret tls ${params.TLS_SECRET} --cert=fullchain.pem --key=privkey.pem -n ${params.NAMESPACE}"
|
|
|
- exit 1
|
|
|
- fi
|
|
|
-
|
|
|
- echo "✅ TLS Secret '${params.TLS_SECRET}' 验证成功"
|
|
|
+ docker push ${IMAGE_TAG}
|
|
|
+ docker rmi ${IMAGE_TAG}
|
|
|
"""
|
|
|
+ echo "✅ 镜像推送并本地清理完成"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- stage('�� Kubernetes 部署') {
|
|
|
+ stage(' Kubernetes 部署') {
|
|
|
steps {
|
|
|
script {
|
|
|
def domain = params.DOMAIN?.trim()
|
|
|
@@ -98,49 +99,17 @@ pipeline {
|
|
|
# 确保命名空间存在
|
|
|
kubectl get ns ${params.NAMESPACE} >/dev/null 2>&1 || kubectl create ns ${params.NAMESPACE}
|
|
|
|
|
|
- # 如果 Deployment 存在,更新镜像
|
|
|
- if kubectl get deployment ${PROJECT_NAME} -n ${params.NAMESPACE} >/dev/null 2>&1; then
|
|
|
- echo ">>> Deployment 已存在,更新镜像..."
|
|
|
- kubectl set image deployment/${PROJECT_NAME} ${PROJECT_NAME}=${IMAGE_TAG} -n ${params.NAMESPACE}
|
|
|
- kubectl rollout status deployment/${PROJECT_NAME} -n ${params.NAMESPACE} --timeout=120s
|
|
|
+ # 如果强制更新配置或 Deployment 不存在,则重新创建
|
|
|
+ if [ "${params.FORCE_UPDATE_CONFIG}" = "true" ] || ! kubectl get deployment ${PROJECT_NAME} -n ${params.NAMESPACE} >/dev/null 2>&1; then
|
|
|
|
|
|
- # 更新 Ingress(如果提供了域名)
|
|
|
- if [ -n "${domain}" ]; then
|
|
|
- echo ">>> 更新 Ingress 配置..."
|
|
|
- kubectl apply -n ${params.NAMESPACE} -f - <<EOF
|
|
|
-apiVersion: networking.k8s.io/v1
|
|
|
-kind: Ingress
|
|
|
-metadata:
|
|
|
- name: ${PROJECT_NAME}-ingress
|
|
|
- annotations:
|
|
|
- kubernetes.io/ingress.class: nginx
|
|
|
- nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|
|
- nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
|
|
- nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
|
|
|
- nginx.ingress.kubernetes.io/ssl-passthrough: "false"
|
|
|
- nginx.ingress.kubernetes.io/proxy-body-size: "8m"
|
|
|
- nginx.ingress.kubernetes.io/rewrite-target: /
|
|
|
-spec:
|
|
|
- tls:
|
|
|
- - hosts:
|
|
|
- - ${domain}
|
|
|
- secretName: ${params.TLS_SECRET}
|
|
|
- rules:
|
|
|
- - host: ${domain}
|
|
|
- http:
|
|
|
- paths:
|
|
|
- - path: /
|
|
|
- pathType: Prefix
|
|
|
- backend:
|
|
|
- service:
|
|
|
- name: ${PROJECT_NAME}
|
|
|
- port:
|
|
|
- number: 80
|
|
|
-EOF
|
|
|
+ if [ "${params.FORCE_UPDATE_CONFIG}" = "true" ]; then
|
|
|
+ echo ">>> 强制更新配置,删除现有资源..."
|
|
|
+ kubectl delete deployment ${PROJECT_NAME} -n ${params.NAMESPACE} --ignore-not-found=true
|
|
|
+ kubectl delete svc ${PROJECT_NAME} -n ${params.NAMESPACE} --ignore-not-found=true
|
|
|
+ kubectl delete ingress ${PROJECT_NAME}-ingress -n ${params.NAMESPACE} --ignore-not-found=true
|
|
|
fi
|
|
|
- else
|
|
|
- # 创建新的 Deployment 和 Service
|
|
|
- echo ">>> 创建新的 Deployment 和 Service..."
|
|
|
+
|
|
|
+ echo ">>> 创建 Deployment 和 Service..."
|
|
|
kubectl apply -n ${params.NAMESPACE} -f - <<EOF
|
|
|
apiVersion: apps/v1
|
|
|
kind: Deployment
|
|
|
@@ -161,6 +130,25 @@ spec:
|
|
|
image: ${IMAGE_TAG}
|
|
|
ports:
|
|
|
- containerPort: 80
|
|
|
+ resources:
|
|
|
+ requests:
|
|
|
+ memory: "128Mi"
|
|
|
+ cpu: "100m"
|
|
|
+ limits:
|
|
|
+ memory: "256Mi"
|
|
|
+ cpu: "200m"
|
|
|
+ livenessProbe:
|
|
|
+ httpGet:
|
|
|
+ path: /
|
|
|
+ port: 80
|
|
|
+ initialDelaySeconds: 30
|
|
|
+ periodSeconds: 10
|
|
|
+ readinessProbe:
|
|
|
+ httpGet:
|
|
|
+ path: /
|
|
|
+ port: 80
|
|
|
+ initialDelaySeconds: 5
|
|
|
+ periodSeconds: 5
|
|
|
---
|
|
|
apiVersion: v1
|
|
|
kind: Service
|
|
|
@@ -173,11 +161,12 @@ spec:
|
|
|
ports:
|
|
|
- port: 80
|
|
|
targetPort: 80
|
|
|
+ protocol: TCP
|
|
|
EOF
|
|
|
|
|
|
# 创建 Ingress(如果提供了域名)
|
|
|
if [ -n "${domain}" ]; then
|
|
|
- echo ">>> 创建新的 Ingress..."
|
|
|
+ echo ">>> 创建 Ingress..."
|
|
|
kubectl apply -n ${params.NAMESPACE} -f - <<EOF
|
|
|
apiVersion: networking.k8s.io/v1
|
|
|
kind: Ingress
|
|
|
@@ -191,6 +180,8 @@ metadata:
|
|
|
nginx.ingress.kubernetes.io/ssl-passthrough: "false"
|
|
|
nginx.ingress.kubernetes.io/proxy-body-size: "8m"
|
|
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
|
|
+ nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384"
|
|
|
+ nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3"
|
|
|
spec:
|
|
|
tls:
|
|
|
- hosts:
|
|
|
@@ -209,10 +200,16 @@ spec:
|
|
|
number: 80
|
|
|
EOF
|
|
|
fi
|
|
|
+
|
|
|
+ else
|
|
|
+ # 只更新镜像
|
|
|
+ echo ">>> Deployment 已存在,仅更新镜像..."
|
|
|
+ kubectl set image deployment/${PROJECT_NAME} ${PROJECT_NAME}=${IMAGE_TAG} -n ${params.NAMESPACE}
|
|
|
+ echo "✅ 镜像更新完成,Kubernetes 将自动处理滚动更新"
|
|
|
fi
|
|
|
|
|
|
- # 等待部署完成
|
|
|
- echo ">>> 等待部署完成..."
|
|
|
+ # 等待 Deployment 就绪
|
|
|
+ echo ">>> 等待 Deployment 就绪..."
|
|
|
kubectl wait --for=condition=available --timeout=300s deployment/${PROJECT_NAME} -n ${params.NAMESPACE}
|
|
|
|
|
|
# 显示部署状态
|
|
|
@@ -222,18 +219,48 @@ EOF
|
|
|
|
|
|
# 如果配置了域名,显示访问信息
|
|
|
if [ -n "${domain}" ]; then
|
|
|
- echo ">>> 应用部署完成!"
|
|
|
- echo ">>> 访问地址:https://${domain}"
|
|
|
- echo ">>> 注意:确保域名 ${domain} 已正确解析到集群公网IP"
|
|
|
+ echo "✅ 应用部署完成!"
|
|
|
+ echo "🌐 访问地址:https://${domain}"
|
|
|
+ echo "�� 注意:确保域名 ${domain} 已正确解析到集群公网IP"
|
|
|
else
|
|
|
- echo ">>> 应用部署完成!"
|
|
|
- echo ">>> 注意:未配置域名,请手动配置 Ingress 或使用 NodePort 访问"
|
|
|
+ echo "✅ 应用部署完成!"
|
|
|
+ echo "📝 注意:未配置域名,请手动配置 Ingress 或使用 NodePort 访问"
|
|
|
fi
|
|
|
"""
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ stage('🔍 部署验证') {
|
|
|
+ when {
|
|
|
+ expression { params.DOMAIN?.trim() }
|
|
|
+ }
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ def domain = params.DOMAIN?.trim()
|
|
|
+ sh """
|
|
|
+ export KUBECONFIG=${KUBECONFIG_PATH}
|
|
|
+
|
|
|
+ echo ">>> 验证部署状态..."
|
|
|
+
|
|
|
+ # 检查 Pod 状态
|
|
|
+ kubectl get pods -n ${params.NAMESPACE} -l app=${PROJECT_NAME}
|
|
|
+
|
|
|
+ # 检查 Service 状态
|
|
|
+ kubectl get svc -n ${params.NAMESPACE} ${PROJECT_NAME}
|
|
|
+
|
|
|
+ # 检查 Ingress 状态
|
|
|
+ kubectl get ingress -n ${params.NAMESPACE} ${PROJECT_NAME}-ingress
|
|
|
+
|
|
|
+ # 检查 TLS Secret
|
|
|
+ kubectl get secret -n ${params.NAMESPACE} ${params.TLS_SECRET}
|
|
|
+
|
|
|
+ echo "✅ 部署验证完成"
|
|
|
+ """
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
stage('🧹 清理本地旧镜像(保留最新3个)') {
|
|
|
steps {
|
|
|
script {
|