Jenkinsfile 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. pipeline {
  2. agent any
  3. parameters {
  4. choice(name: 'env', choices: ['dev', 'test', 'prod'], description: '部署环境')
  5. string(name: 'NAMESPACE', defaultValue: 'portal-frontend', description: 'K8s 命名空间')
  6. string(name: 'DOMAIN', defaultValue: '', description: 'Ingress 域名(留空则不创建 Ingress)')
  7. }
  8. environment {
  9. PROJECT_NAME = 'portal-service-frontend'
  10. BUILD_DIR = 'dist'
  11. NODE_ENV = 'production'
  12. HARBOR_HOST = '8.130.28.21:81'
  13. KUBECONFIG_PATH = '/root/.kube/config'
  14. NODE1_IP = '172.27.73.147'
  15. NODE2_IP = '172.27.73.146'
  16. HARBOR_USER = 'admin'
  17. HARBOR_PASS = 'Hfln@1024'
  18. HARBOR_RETENTION_ID = '1'
  19. }
  20. stages {
  21. stage('🧬 初始化环境') {
  22. steps {
  23. script {
  24. env.HARBOR_PROJECT = params.env
  25. env.IMAGE_TAG = "${env.HARBOR_HOST}/${env.HARBOR_PROJECT}/${env.PROJECT_NAME}:${BUILD_NUMBER}"
  26. echo ">>> 环境:${params.env}, Harbor项目:${env.HARBOR_PROJECT}, K8s命名空间:${params.NAMESPACE}"
  27. echo ">>> IMAGE_TAG = ${env.IMAGE_TAG}"
  28. }
  29. }
  30. }
  31. stage('📥 拉取代码') {
  32. steps {
  33. checkout scm
  34. echo "✅ 代码拉取成功"
  35. }
  36. }
  37. stage('🔧 构建 Docker 镜像') {
  38. steps {
  39. script {
  40. sh """
  41. docker login -u ${env.HARBOR_USER} -p ${env.HARBOR_PASS} ${env.HARBOR_HOST}
  42. docker build --build-arg ENV=${params.env} -t ${env.IMAGE_TAG} .
  43. """
  44. echo "✅ 镜像构建成功:${env.IMAGE_TAG}"
  45. }
  46. }
  47. }
  48. stage('🚀 推送镜像到 Harbor') {
  49. steps {
  50. script {
  51. sh """
  52. docker push ${env.IMAGE_TAG}
  53. docker rmi ${env.IMAGE_TAG} || true
  54. """
  55. echo "✅ 镜像推送并本地清理完成"
  56. }
  57. }
  58. }
  59. stage('🔍 测试节点能否拉取镜像') {
  60. steps {
  61. script {
  62. echo ">>> 测试节点能否拉取镜像..."
  63. // 注意:确保 Jenkins 节点能免密 SSH 到这些节点,或将这一步改为跳过
  64. sh """
  65. ssh root@${env.NODE1_IP} docker login -u ${env.HARBOR_USER} -p ${env.HARBOR_PASS} ${env.HARBOR_HOST} && docker pull ${env.IMAGE_TAG} || echo '[❌ 节点 ${env.NODE1_IP} 拉取失败]'
  66. ssh root@${env.NODE2_IP} docker login -u ${env.HARBOR_USER} -p ${env.HARBOR_PASS} ${env.HARBOR_HOST} && docker pull ${env.IMAGE_TAG} || echo '[❌ 节点 ${env.NODE2_IP} 拉取失败]'
  67. """
  68. }
  69. }
  70. }
  71. stage('📦 部署到 Kubernetes') {
  72. steps {
  73. script {
  74. def domain = params.DOMAIN?.trim()
  75. // 组装 ingress 块(如果 domain 非空)
  76. def ingressBlock = ''
  77. if (domain) {
  78. ingressBlock = """---
  79. apiVersion: networking.k8s.io/v1
  80. kind: Ingress
  81. metadata:
  82. name: ${env.PROJECT_NAME}
  83. namespace: ${params.NAMESPACE}
  84. annotations:
  85. nginx.ingress.kubernetes.io/rewrite-target: /
  86. spec:
  87. tls:
  88. - hosts:
  89. - ${domain}
  90. secretName: portal-tls
  91. rules:
  92. - host: ${domain}
  93. http:
  94. paths:
  95. - path: /
  96. pathType: Prefix
  97. backend:
  98. service:
  99. name: ${env.PROJECT_NAME}
  100. port:
  101. number: 80
  102. """
  103. }
  104. // 生成完整 deploy.yaml 内容
  105. def deployYaml = """apiVersion: apps/v1
  106. kind: Deployment
  107. metadata:
  108. name: ${env.PROJECT_NAME}
  109. namespace: ${params.NAMESPACE}
  110. spec:
  111. replicas: 2
  112. selector:
  113. matchLabels:
  114. app: ${env.PROJECT_NAME}
  115. template:
  116. metadata:
  117. labels:
  118. app: ${env.PROJECT_NAME}
  119. spec:
  120. containers:
  121. - name: ${env.PROJECT_NAME}
  122. image: ${env.IMAGE_TAG}
  123. ports:
  124. - containerPort: 80
  125. env:
  126. - name: NODE_ENV
  127. value: "${params.env}"
  128. ---
  129. apiVersion: v1
  130. kind: Service
  131. metadata:
  132. name: ${env.PROJECT_NAME}
  133. namespace: ${params.NAMESPACE}
  134. spec:
  135. type: NodePort
  136. selector:
  137. app: ${env.PROJECT_NAME}
  138. ports:
  139. - port: 80
  140. targetPort: 80
  141. nodePort: 30088
  142. ${ingressBlock}
  143. """
  144. writeFile file: 'deploy.yaml', text: deployYaml
  145. sh """
  146. export KUBECONFIG=${env.KUBECONFIG_PATH}
  147. kubectl get ns ${params.NAMESPACE} >/dev/null 2>&1 || kubectl create ns ${params.NAMESPACE}
  148. kubectl apply -f deploy.yaml
  149. kubectl rollout status deployment/${env.PROJECT_NAME} -n ${params.NAMESPACE} --timeout=120s || echo '[rollout timeout or incomplete]'
  150. """
  151. if (domain) {
  152. echo ">>> ✅ 部署完成(含 Ingress),访问地址:https://${domain}/ (请确保 DNS 已指向 Ingress 公网 IP 且 secret portal-tls 已创建)"
  153. } else {
  154. echo ">>> ✅ 部署完成(NodePort),访问地址:http://${env.NODE1_IP}:30088/ 或 http://${env.NODE2_IP}:30088/"
  155. }
  156. }
  157. }
  158. }
  159. stage('🧹 清理本地旧镜像(保留最新3个)') {
  160. steps {
  161. script {
  162. def baseImage = "${env.HARBOR_HOST}/${env.HARBOR_PROJECT}/${env.PROJECT_NAME}"
  163. sh """
  164. docker images ${baseImage} --format "{{.Repository}}:{{.Tag}}" \\
  165. | grep -v latest \\
  166. | sort -r -t ':' -k2 \\
  167. | tail -n +4 \\
  168. | xargs -r docker rmi || true
  169. """
  170. echo "✅ 本地旧镜像清理完成"
  171. }
  172. }
  173. }
  174. stage('🧼 清理 dangling 镜像') {
  175. steps {
  176. script {
  177. sh """
  178. docker images -f "dangling=true" -q | xargs -r docker rmi || true
  179. """
  180. echo "✅ 悬空镜像(<none>)清理完成"
  181. }
  182. }
  183. }
  184. stage('🔁 触发 Harbor 镜像保留策略(可选)') {
  185. steps {
  186. script {
  187. sh """
  188. curl -u ${env.HARBOR_USER}:${env.HARBOR_PASS} -X POST "http://${env.HARBOR_HOST}/api/v2.0/retentions/${env.HARBOR_RETENTION_ID}/executions" || echo '[retention trigger failed]'
  189. """
  190. echo "✅ Harbor 镜像保留策略已触发(若配置)"
  191. }
  192. }
  193. }
  194. }
  195. post {
  196. success {
  197. echo "✅ 构建 & 部署成功 🎉"
  198. }
  199. failure {
  200. echo "❌ 构建或部署失败,请检查日志"
  201. }
  202. always {
  203. cleanWs()
  204. }
  205. }
  206. }