Jenkins构建自由风格项目和Maven这边不在赘述

1、Pipeline简介

  1. 概念 Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点 的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。
  2. 使用Pipeline有以下好处(来自翻译自官方文档): 代码:Pipeline以代码的形式实现,通常被检入源代码控制,使团队能够编辑,审查和迭代其传送流 程。 持久:无论是计划内的还是计划外的服务器重启,Pipeline都是可恢复的。 可停止:Pipeline可接 收交互式输入,以确定是否继续执行Pipeline。 多功能:Pipeline支持现实世界中复杂的持续交付要 求。它支持fork/join、循环执行,并行执行任务的功能。 可扩展:Pipeline插件支持其DSL的自定义扩 展 ,以及与其他插件集成的多个选项。
  3. 如何创建 Jenkins Pipeline呢? Pipeline 脚本是由 Groovy 语言实现的,但是我们没必要单独去学习 Groovy Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法 Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一 个 Jenkinsfile 脚本文件放入项目源码库中(一般我们都推荐在 Jenkins 中直接从源代码控制(SCM) 中直接载入 Jenkinsfile Pipeline 这种方法)。

2、 Pipeline语法快速入门

1、 Declarative声明式-Pipeline

流水线->选择HelloWorld模板
image.png

  1. pipeline {
  2. agent any
  3. stages {
  4. stage('Hello') {
  5. steps {
  6. echo 'Hello World'
  7. }
  8. }
  9. }
  10. }

stages:代表整个流水线的所有执行阶段。通常stages只有1个,里面包含多个stage
stage:代表流水线中的某个阶段,可能出现n个。一般分为拉取代码,编译构建,部署等阶段。
steps:代表一个阶段内需要执行的逻辑。steps里面是shell脚本,git拉取代码,ssh远程发布等任意内 容。

编写一个简单声明式Pipeline:

  1. pipeline {
  2. agent any
  3. stages {
  4. stage('拉取代码') {
  5. steps {
  6. echo '拉取代码'
  7. }
  8. }
  9. stage('编译构建') {
  10. steps {
  11. echo '编译构建'
  12. }
  13. }
  14. stage('项目部署') {
  15. steps {
  16. echo '项目部署'
  17. }
  18. }
  19. }
  20. }

2、Scripted Pipeline脚本式-Pipeline

这次选择”Scripted Pipeline” image.png

  1. node {
  2. def mvnHome
  3. stage('Preparation') { // for display purposes
  4. }
  5. stage('Build') {
  6. }
  7. stage('Results') {
  8. }
  9. }
  • Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行 环境,后续讲到Jenkins的Master-Slave架构的时候用到。
  • Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如: Build、Test、Deploy,Stage 是一个逻辑分组的概念。
  • Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像, 由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令 一样。

    编写一个简单的脚本式Pipeline
    ```groovy node { def mvnHome stage(‘拉取代码’) { // for display purposes

    1. echo '拉取代码'

    } stage(‘编译构建’) {

    1. echo '编译构建'

    } stage(‘项目部署’) {

    1. echo '项目部署'

    } }

  1. <a name="PyOsY"></a>
  2. ## 3、Pipeline的一些模板语法
  3. <a name="C2mt9"></a>
  4. ### 1、 拉取代码
  5. ```groovy
  6. pipeline {
  7. agent any
  8. stages {
  9. stage('拉取代码') {
  10. steps {
  11. checkout([$class: 'GitSCM', branches: [[name: '*/master']],
  12. doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
  13. userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url:
  14. 'git@192.168.66.100:itheima_group/web_demo.git']]])
  15. }
  16. }
  17. }
  18. }

2、 编译打包

  1. pipeline {
  2. agent any
  3. stages {
  4. stage('拉取代码') {
  5. steps {
  6. checkout([$class: 'GitSCM', branches: [[name: '*/master']],
  7. doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
  8. userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url:
  9. 'git@192.168.66.100:itheima_group/web_demo.git']]])
  10. }
  11. }
  12. stage('编译构建') {
  13. steps {
  14. sh label: '', script: 'mvn clean package'
  15. }
  16. }
  17. }
  18. }

3、 部署 / 远程Tomcat

  1. pipeline {
  2. agent any
  3. stages {
  4. stage('拉取代码') {
  5. steps {
  6. checkout([$class: 'GitSCM', branches: [[name: '*/master']],
  7. doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
  8. userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url:
  9. 'git@192.168.66.100:itheima_group/web_demo.git']]])
  10. }
  11. }
  12. stage('编译构建') {
  13. steps {
  14. sh label: '', script: 'mvn clean package'
  15. }
  16. }
  17. stage('项目部署') {
  18. steps {
  19. deploy adapters: [tomcat8(credentialsId: 'afc43e5e-4a4e-4de6-984fb1d5a254e434', path: '', url: 'http://192.168.66.102:8080')], contextPath: null,
  20. war: 'target/*.war'
  21. }
  22. }
  23. }
  24. }

4、 Pipeline Script from SCM / Pipeline脚本语法远程维护

刚才我们都是直接在Jenkins的UI界面编写Pipeline代码,这样不方便脚本维护,建议把Pipeline脚本放 在项目中(一起进行版本控制)

1、 在项目根目录建立Jenkinsfile文件,把内容复制到该文件中

image.png
把Jenkinsfile上传到Gitlab

2、在项目中引用该文件

image.png

5、 Jenkins的参数化构建

有时在项目构建的过程中,我们需要根据用户的输入动态传入一些参数,从而影响整个构建结果,这时 我们可以使用参数化构建。 Jenkins支持非常丰富的参数类型
image.png

接下来演示通过输入gitlab项目的分支名称来部署不同分支项目。

image.png

改动pipeline流水线代码

image.png
image.png

点击Build with Parameters

image.png

6、企业级流水线模板

1、部署SpringBoot微服务脚本\deploy.yaml

  1. pipeline {
  2. parameters {
  3. string(name: 'BRANCH_NAME', defaultValue: 'dev', description: '')
  4. }
  5. agent {
  6. node {
  7. label 'maven'
  8. }
  9. }
  10. stages {
  11. stage('拉取代码') {
  12. agent none
  13. steps {
  14. container('maven') {
  15. git(url: "$APP_REPO", credentialsId: 'gitlab-id', branch: "$BRANCH_NAME", changelog: true, poll: false)
  16. }
  17. }
  18. }
  19. stage('项目编译') {
  20. agent none
  21. steps {
  22. container('maven') {
  23. sh 'ls -al'
  24. sh 'cd afterloan-batch && mvn clean install -Dmaven.test.skip=true'
  25. }
  26. }
  27. }
  28. stage('构建镜像') {
  29. agent none
  30. steps {
  31. container('maven') {
  32. sh 'ls $JAR_PATH/target'
  33. sh "cd $JAR_PATH && docker build -t $APP_NAME:latest -f ./Dockerfile ."
  34. }
  35. }
  36. }
  37. stage('推送镜像') {
  38. agent none
  39. steps {
  40. container('maven') {
  41. withCredentials([usernamePassword(credentialsId : 'harbor-id' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
  42. sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
  43. sh "docker tag $APP_NAME:latest $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_NAME-$BUILD_NUMBER"
  44. sh "docker push $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_NAME-$BUILD_NUMBER"
  45. }
  46. }
  47. }
  48. }
  49. stage('部署') {
  50. agent none
  51. steps {
  52. container ("maven") {
  53. // 这种方式启k8s是官方推荐的
  54. sh "envsubst < $JAR_PATH/k8s/deploy.yml | kubectl apply -f -"
  55. }
  56. }
  57. }
  58. }
  59. environment {
  60. // 固定的变量
  61. DOCKER_CREDENTIAL_ID = 'dockerhub'
  62. KUBECONFIG_CREDENTIAL_ID = 'kubeconfig'
  63. REGISTRY = 'k8s-habor:30002'
  64. DOCKERHUB_NAMESPACE = 'afterloan'
  65. SONAR_CREDENTIAL_ID = 'sonar-token'
  66. // JOB_BASE_NAME,这个变量,是jenkins自带的变量,会用到
  67. // 值要做更改的变量
  68. APP_NAME = 'afterloan-batch'
  69. JAR_PATH = 'afterloan-batch/afterloan-start'
  70. APP_REPO = 'http://192.168.50.11:9091/after_load/yusp-afterloan-batch.git'
  71. K8S_NAMESPACE = "afterloan-project"
  72. }
  73. }
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. labels:
  5. app: $APP_NAME
  6. name: $APP_NAME
  7. namespace: ${K8S_NAMESPACE} #一定要写名称空间
  8. spec:
  9. progressDeadlineSeconds: 600
  10. replicas: 1
  11. selector:
  12. matchLabels:
  13. app: $APP_NAME
  14. strategy:
  15. rollingUpdate:
  16. maxSurge: 50%
  17. maxUnavailable: 50%
  18. type: RollingUpdate
  19. template:
  20. metadata:
  21. labels:
  22. app: $APP_NAME
  23. spec:
  24. imagePullSecrets:
  25. - name: harbor-id #提前在项目下配置访问阿里云的账号密码
  26. containers:
  27. - image: $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_NAME-$BUILD_NUMBER
  28. # readinessProbe:
  29. # httpGet:
  30. # path: /actuator/health
  31. # port: 8080
  32. # timeoutSeconds: 10
  33. # failureThreshold: 30
  34. # periodSeconds: 5
  35. imagePullPolicy: Always
  36. name: app
  37. ports:
  38. - containerPort: 8080
  39. protocol: TCP
  40. resources:
  41. requests:
  42. cpu: 100m
  43. memory: 512Mi
  44. limits:
  45. cpu: 1000m
  46. memory: 1024Mi
  47. terminationMessagePath: /dev/termination-log
  48. terminationMessagePolicy: File
  49. dnsPolicy: ClusterFirst
  50. restartPolicy: Always
  51. terminationGracePeriodSeconds: 30
  52. ---
  53. apiVersion: v1
  54. kind: Service
  55. metadata:
  56. labels:
  57. app: $APP_NAME
  58. name: $APP_NAME
  59. namespace: ${K8S_NAMESPACE}
  60. spec:
  61. ports:
  62. - name: http
  63. port: 8080
  64. protocol: TCP
  65. targetPort: 8080
  66. # nodePort: 32608
  67. selector:
  68. app: $APP_NAME
  69. sessionAffinity: None
  70. type: NodePort
  1. FROM pig4cloud/java:8-jre
  2. ENV JAR_NAME="jenkins-one"
  3. ENV TZ=Asia/Shanghai
  4. ENV JAVA_OPTS="-Xms512m -Xmx512m -Djava.security.egd=file:/dev/./urandom"
  5. RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
  6. && echo $TZ > /etc/timezone \
  7. && mkdir -p /jenkins-one
  8. WORKDIR /$JAR_NAME
  9. #EXPOSE 9000
  10. ADD ./target/$JAR_NAME.jar ./
  11. CMD java $JAVA_OPTS -jar $JAR_NAME.jar

2、部署前端npm项目脚本\deploy.yaml

  1. FROM nginx
  2. #将dist目录内容复制到nginx容器html内部
  3. COPY dist /usr/share/nginx/html/
  4. COPY default.conf /etc/nginx/conf.d/
  5. COPY dns.sh /docker-entrypoint.d/dns.sh
  6. RUN chmod +x /docker-entrypoint.d/dns.sh
  7. EXPOSE 80
  1. #!/usr/bin/env bash
  2. export NAME_SERVER=`awk '/^nameserver/{print $2}' /etc/resolv.conf`
  3. sed -i "s#{NAME_SERVER}#${NAME_SERVER}#g" /etc/nginx/conf.d/default.conf
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. labels:
  5. app: $APP_NAME
  6. name: $APP_NAME
  7. namespace: $K8S_NAMESPACE #一定要写名称空间
  8. spec:
  9. progressDeadlineSeconds: 600
  10. replicas: 1
  11. selector:
  12. matchLabels:
  13. app: $APP_NAME
  14. strategy:
  15. rollingUpdate:
  16. maxSurge: 50%
  17. maxUnavailable: 50%
  18. type: RollingUpdate
  19. template:
  20. metadata:
  21. labels:
  22. app: $APP_NAME
  23. spec:
  24. imagePullSecrets:
  25. - name: harbor-id #提前在项目下配置harbor私服的账号密码
  26. containers:
  27. - image: $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_TAG_NAME-$BUILD_NUMBER
  28. # readinessProbe:
  29. # httpGet:
  30. # path: /actuator/health
  31. # port: 8080
  32. # timeoutSeconds: 10
  33. # failureThreshold: 30
  34. # periodSeconds: 5
  35. imagePullPolicy: Always
  36. name: app
  37. ports:
  38. - containerPort: 80
  39. protocol: TCP
  40. resources:
  41. limits:
  42. cpu: 512m
  43. memory: 512Mi
  44. terminationMessagePath: /dev/termination-log
  45. terminationMessagePolicy: File
  46. dnsPolicy: ClusterFirst
  47. restartPolicy: Always
  48. terminationGracePeriodSeconds: 30
  49. ---
  50. apiVersion: v1
  51. kind: Service
  52. metadata:
  53. labels:
  54. app: $APP_NAME
  55. name: $APP_NAME
  56. namespace: $K8S_NAMESPACE
  57. spec:
  58. ports:
  59. - name: http
  60. port: 80
  61. protocol: TCP
  62. targetPort: 80
  63. # nodePort: 32609
  64. selector:
  65. app: $APP_NAME
  66. sessionAffinity: None
  67. type: NodePort
  1. server {
  2. listen 80;
  3. listen [::]:80;
  4. server_name localhost;
  5. resolver {NAME_SERVER} valid=10s;
  6. resolver_timeout 3s;
  7. client_max_body_size 0;
  8. set $proxy_url "http://afterloan-service.afterloan-project.svc.cluster.local:8080";
  9. #gzip配置
  10. gzip on;
  11. gzip_min_length 1k;
  12. gzip_buffers 4 16k;
  13. gzip_http_version 1.1;
  14. gzip_comp_level 5;
  15. gzip_types text/xml text/plain text/css text/javascript application/x-javascript application/javascript application/css application/xml application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
  16. gzip_vary on;
  17. gzip_disable "MSIE [1-6]\.";
  18. location / {
  19. root /usr/share/nginx/html;
  20. index index.html index.htm;
  21. }
  22. location ~ ^/(api|oauth|business) {
  23. proxy_pass $proxy_url;
  24. proxy_set_header Host $host;
  25. proxy_set_header X-Real-IP $remote_addr;
  26. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  27. }
  28. error_page 500 502 503 504 /50x.html;
  29. location = /50x.html {
  30. root /usr/share/nginx/html;
  31. }
  32. }
  1. pipeline {
  2. parameters {
  3. string(name: 'BRANCH_NAME', defaultValue: 'dev-二批次', description: '')
  4. string(name: 'BRANCH_TAG_NAME', defaultValue: 'dev-batch2', description: '解决镜像标签无法使用中文的问题')
  5. }
  6. agent {
  7. node {
  8. label 'nodejs'
  9. }
  10. }
  11. stages {
  12. stage('拉取代码') {
  13. agent none
  14. steps {
  15. container('nodejs') {
  16. git(url: 'http://192.168.50.11:9091/after_load/yusp-afterloan-web.git', credentialsId: 'gitlab-id', branch: "$BRANCH_NAME", changelog: true, poll: false)
  17. }
  18. }
  19. }
  20. stage('构建') {
  21. agent none
  22. steps {
  23. container('nodejs') {
  24. script {
  25. sh 'npm config delete proxy'
  26. sh 'yarn'
  27. sh 'yarn build'
  28. }
  29. }
  30. }
  31. }
  32. // stage("代码质量扫描") {
  33. // agent none
  34. // steps {
  35. // container('nodejs') {
  36. // script {
  37. // withCredentials([string(credentialsId: 'sonar-token', variable: 'SONAR_TOKEN',)]) {
  38. // withSonarQubeEnv('sonar') {
  39. // sh 'ls -al'
  40. // sh "sonar-scanner -Dsonar.projectName=$JOB_BASE_NAME -Dsonar.projectKey=$JOB_BASE_NAME -Dsonar.projectBaseDir=./$JOB_BASE_NAME -Dsonar.exclusions=**/node_modules/**,**/tests/**,**/html/**,**/public/**,**/mocks/** -Dsonar.host.url=http://192.168.35.42:9000/ -Dsonar.login=$SONAR_TOKEN"
  41. // }
  42. // }
  43. // timeout(unit: 'MINUTES', activity: true, time: 5) {
  44. // waitForQualityGate 'false'
  45. // }
  46. // }
  47. // }
  48. // }
  49. // }
  50. stage('构建镜像') {
  51. agent none
  52. steps {
  53. container('nodejs') {
  54. sh 'pwd && ls'
  55. sh "docker build -t $APP_NAME:latest -f ./Dockerfile ."
  56. }
  57. }
  58. }
  59. stage('推送镜像') {
  60. agent none
  61. steps {
  62. container('nodejs') {
  63. withCredentials([usernamePassword(credentialsId : 'harbor-id' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
  64. sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
  65. sh "docker tag $APP_NAME:latest $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_TAG_NAME-$BUILD_NUMBER"
  66. sh 'docker push $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BRANCH_TAG_NAME-$BUILD_NUMBER'
  67. }
  68. }
  69. }
  70. }
  71. stage('部署') {
  72. agent none
  73. steps {
  74. kubernetesDeploy(configs: 'deploy.yml', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
  75. }
  76. }
  77. }
  78. environment {
  79. DOCKER_CREDENTIAL_ID = 'dockerhub'
  80. KUBECONFIG_CREDENTIAL_ID = 'kubeconfig'
  81. REGISTRY = 'k8s-habor:30002'
  82. DOCKERHUB_NAMESPACE = 'afterloan'
  83. APP_NAME = 'afterloan-web'
  84. SONAR_CREDENTIAL_ID = 'sonar-token'
  85. K8S_NAMESPACE='afterloan-project'
  86. }
  87. }
  1. FROM nginx
  2. COPY ./dist /data
  3. RUN rm /etc/nginx/conf.d/default.conf
  4. ADD student-eva.conf /etc/nginx/conf.d/default.conf
  5. RUN rm /etc/nginx/nginx.conf
  6. ADD nginx.conf /etc/nginx/nginx.conf
  7. RUN /bin/bash -c 'echo init ok'
  1. user nginx;
  2. worker_processes 1;
  3. error_log /var/log/nginx/error.log warn;
  4. pid /var/run/nginx.pid;
  5. events {
  6. worker_connections 1024;
  7. }
  8. http {
  9. include /etc/nginx/mime.types;
  10. default_type application/octet-stream;
  11. client_max_body_size 200M;
  12. log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  13. '$status $body_bytes_sent "$http_referer" '
  14. '"$http_user_agent" "$http_x_forwarded_for"';
  15. access_log /var/log/nginx/access.log main;
  16. add_header Cache-Control "no-cache";
  17. sendfile on;
  18. #tcp_nopush on;
  19. keepalive_timeout 65;
  20. #gzip on;
  21. include /etc/nginx/conf.d/*.conf;
  22. }
  1. server {
  2. listen 80;
  3. server_name localhost;
  4. gzip on;
  5. gzip_static on; # 需要http_gzip_static_module 模块
  6. gzip_min_length 1k;
  7. gzip_comp_level 4;
  8. gzip_proxied any;
  9. gzip_types text/plain text/xml text/css;
  10. gzip_vary on;
  11. gzip_disable "MSIE [1-6]\.(?!.*SV1)";
  12. # 前端打包好的dist目录文件
  13. root /data/;
  14. # 若新增后端路由前缀注意在此处添加(|新增)
  15. location ~* ^/(code|auth|admin|course|arranging|dict|resource|exercise|study|tpi|activity|lesson|studentEva) {
  16. proxy_pass http://course-gateway:9999;
  17. proxy_connect_timeout 15s;
  18. proxy_send_timeout 15s;
  19. proxy_read_timeout 15s;
  20. proxy_set_header X-Real-IP $remote_addr;
  21. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  22. proxy_set_header X-Forwarded-Proto http;
  23. }
  24. }