持续集成CI&CD之CI的完整版最佳实践

Posted 老梅mqm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了持续集成CI&CD之CI的完整版最佳实践相关的知识,希望对你有一定的参考价值。

上一章:​​持续集成CI&CD之配置管理最佳实践​

下一章:​​持续集成CI&CD之CD的完整版最佳实践​

本CI实践主要对于有持续集成需求,适用于联调开发环境,可以大大加快开发人员的联调验证速度。主要采用jenkins pipeline脚本实现。

jenkins pipeline

​参考文档​​:https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile

​编写语法参考 ​​:http://groovy-lang.org/semantics.html

概念

       pipeline是用户定义的一个CD流水线模型 。pipeline的代码定义了整个的构建过程, 他通常包括构建, 测试和交付应用程序的阶段 。本质上,Jenkins 是一个自动化引擎,它支持许多自动模式。 pipeline向Jenkins中添加了一组强大的工具, 支持用例 简单的持续集成到全面的CD流水线。通过对一系列的相关任务进行建模, 用户可以利用pipeline的很多特性。

声明式

Jenkinsfile (Declarative Pipeline)

pipeline 
    agent any  // (1)
    stages 
        stage(Build)   // (2)
            steps 
                // (3)
            
        
        stage(Test)   // (4)
            steps 
                // (5)
            
        
        stage(Deploy)  // (6)
            steps 
                // (7)
            
        
    

(1)在任何可用的代理上,执行流水线或它的任何阶段。

(2)定义 "Build" 阶段。

(3)执行与 "Build" 阶段相关的步骤。

(4)定义"Test" 阶段。

(5)执行与"Test" 阶段相关的步骤。

(6)定义 "Deploy" 阶段。 (7)执行与 "Deploy" 阶段相关的步骤

脚本化

Jenkinsfile (Scripted Pipeline)

node   // (1)
    stage(Build)  // (2)
        // (3)
    
    stage(Test)  // (4)
        // (5)
    
    stage(Deploy)  // (6)
        // (7)
    

(1)在任何可用的代理上,执行流水线或它的任何阶段。

(2)定义 "Build" 阶段。 stage 块 在脚本化流水线语法中是可选的。 然而, 在脚本化流水线中实现 stage 块 ,可以清楚的显示Jenkins UI中的每个 stage 的任务子集。

(3)执行与 "Build" 阶段相关的步骤。

(4)定义 "Test" 阶段。

(5)执行与 "Test" 阶段相关的步骤。

(6)定义 "Deploy" 阶段。 (7)执行与 "Deploy" 阶段相关的步骤

CI集成流程

CI流程架构

持续集成CI&CD之CI的完整版最佳实践_java

(1)开发人员代码提交 (2)开发人员点击jenkins构建 (3)开发人员使用运维工具查看服务情况

代码部署文件

开发环境部署以k8s为例

spring-cloud-nacos

本案例是将spring-cloud-nacos工程通过jenkins编译打包发布的开发环境,所有部署相关的文件都在docker文件夹中。

采用jenkins-pipeline的声明式部署,所有maven打包、镜像编译方式,部署都使用容器化,不依赖主机。相关的文件都使用jenkins系统变量、凭证配置。

工程结构

父工程
|--- 子模块1  # java子模块
|------ docker文件夹 # 存放部署文件
|--------- Dockerfile
|--------- Jenkinsfile
|--------- k8s.yaml
|------ src
...
|--- 子模块2
|------ docker文件夹
|--------- Dockerfile
|--------- Jenkinsfile
|--------- k8s.yaml
|------ src

部署文件-Dockerfile

用于容器镜像制作。

FROM openjdk:8-jre
MAINTAINER QiMing Mei <meiqiming@talkweb.com.cn>

ARG JAR_FILE
ENV JAVA_OPTS="-server -Xms1024m -Xmx1024m" JAR_FILE="$JAR_FILE"

ADD target/$JAR_FILE /usr/share/service/$JAR_FILE

ENTRYPOINT java $JAVA_OPTS -jar /usr/share/service/$JAR_FILE  --server.port=$CONTAINER_PORT \\
--spring.cloud.nacos.discovery.ip=$DOMAIN_NAME --spring.cloud.nacos.server-addr=$NACOS_URL

部署文件-k8s.yaml

用于k8s环境部署。

apiVersion: v1
kind: Namespace
metadata:
  name: __NAME_SPACE__
  labels:
    name: __NAME_SPACE__
---
apiVersion: v1
kind: Service
metadata:
  name: __DOMAIN_NAME__
  namespace: __NAME_SPACE__
spec:
  ports:
  - name: app-port
    port: __CONTAINER_PORT__
    targetPort: __CONTAINER_PORT__
    protocol: TCP
  # 当开启管理端口时,外部服务需要访问时打开此注释
  # - name: manage-port
  #   port: __MANAGE_PORT__
  #   targetPort: __MANAGE_PORT__
  #   protocol: TCP
  selector:
    app: __DOMAIN_NAME__
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: __DOMAIN_NAME__
  namespace: __NAME_SPACE__
spec:
  selector:
    matchLabels:
      app: __DOMAIN_NAME__
  replicas: __REPLICAS_NUM__
  template:
    metadata:
      labels:
        app: __DOMAIN_NAME__
    spec:
      initContainers:
      - name: init-agent-sidecar
        image: ming19871211/skywalking-agent:__SKYWALKING_VESION__
        command:
        - sh
        - -c
        - set -ex;cp -r /skywalking/agent/* /usr/skywalking/agent;
        volumeMounts:
        - name: agent
          mountPath: /usr/skywalking/agent
      containers:
      - name: __DOMAIN_NAME__
        image: __DOCKER_IMAGE__
        imagePullPolicy: IfNotPresent #本地存在就不到远程拉取镜像
        env: #环境变量设置
        - name: TZ
          value: Asia/Shanghai
        - name: NACOS_NAMESPACE
          value: __NACOS_NAMESPACE__
        - name: DEPLOY_ENV  # 兼容性配置,后续版本建议删除
          value: __NACOS_NAMESPACE__
        - name: NACOS_GROUP
          value: __NACOS_GROUP__
        - name: JAVA_OPTS
          value: "__JAVA_OPTS__"
        - name: CONTAINER_PORT
          value: "__CONTAINER_PORT__"
        - name: MANAGE_PORT
          value: "__MANAGE_PORT__"
        - name: NACOS_URL
          value: __NACOS_URL__
        - name: DOMAIN_NAME
          value: __DOMAIN_NAME__.__NAME_SPACE__
        # - name: DOMAIN_NAME
        #   valueFrom:
        #     fieldRef:
        #       apiVersion: v1
        #       fieldPath: status.podIP
        envFrom:
        - secretRef:
            name: __NACOS_AUTH__
        resources: #资源限制
          requests:
            memory: "128Mi"
            cpu: "100m" #最低需要 0.1个cpu
          limits:
            memory: "__LIMIT_MEMORY__Mi"
            cpu: "1000m"
        ports:
        - containerPort: __CONTAINER_PORT__
          name: app-port
          protocol: TCP
        - containerPort: __MANAGE_PORT__
          name: manage-port
          protocol: TCP
        readinessProbe: #就绪探针
          httpGet:
          #  path: __APP_MANAGE_PATH__/actuator/health/readiness
          #  port: __MANAGE_PORT__
          tcpSocket:
            port: __CONTAINER_PORT__
          initialDelaySeconds: 60
          periodSeconds: 15
          timeoutSeconds: 5
        livenessProbe: #健康检查
        #  httpGet:
        #    path: __APP_MANAGE_PATH__/actuator/health/liveness
        #    port: __MANAGE_PORT__
          tcpSocket:
            port: __CONTAINER_PORT__
          initialDelaySeconds: 60
          periodSeconds: 15
          timeoutSeconds: 5
        volumeMounts:
        - name: time-config
          mountPath: /etc/localtime
          readOnly: true
        - name: agent
          mountPath: /usr/skywalking/agent
        # 增加挂载
        # - name: [PVC_NAME_ALIAS] # pod挂载别称
        #   mountPath: [POD_MOUNT_PATH] # pods内挂载路径
        #   subPath: [PVC_MOUNT_SUBPATH] #  pvc挂载盘中的子路径,注释这一项标识挂载根路径下
      imagePullSecrets:
      - name: __DOCKER_REGISTRY_SECRET__
      nodeSelector:
        isDev: "true"
      volumes:
      - name: time-config
        hostPath:
          path: /etc/localtime
      - name: agent
        emptyDir: 
      # 增加挂载
      # - name: [PVC_NAME_ALIAS] # pod挂载别称
      #   persistentVolumeClaim:
      #     claimName: [PVC_NAME] # 对应指定的pvc名称

部署文件-Jenkinsfile

jenkins部署脚本文件,分为三个步骤进行部署(maven编译、docker镜像制作、k8s部署),整个部署运行的节点只需要有docker环境即可部署。

def docker_image = "" //定义全局的镜像变量
pipeline 
    /*
    1.配置maven相关变量 credentials-[Secret file]类型: jenkins-maven-setting[maven setting.xml文件]
    2.配置docker仓库相关变量 系统环境变量:DOCKER_REGISTRY_ADDR [docker仓库地址]
    credentials-[Username with password]类型:jenkins-docker-registry-creds[docker仓库账号/docker仓库密码]
    3.配置nacos相关变量 定义账号密码对应下面的NACOS_URL
    credentials-[Username with password]类型:jenkins-nacos-creds[nacos登录账号/nacos登录密码]
    4.配置k8s相关变量 credentials-[Secret file]类型: jenkins-k8s-config[k8s集群访问凭证文件]
    5.若需要启动skywalking链路跟踪,需要配置环境变量IS_SKYWALKING=true, SKYWALKING_VESION=[skywalking的版本号]

    合计:1个docker仓库地址环境变量,4个credentials为必须配置;2个skywalking环境变量根据需求配置;
    `参考文档`:https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile
    `编写语法参考 `:http://groovy-lang.org/semantics.html
     */
    // 如果指定具体的节点执行,请 agent  label docker-slave 
    agent any
    options 
        //超时一小时
        timeout(time: 1, unit: HOURS)
        //不允许同时执行
        disableConcurrentBuilds()
    
// #######################需要修改的区域 开始#################################
    /*** 一般情况下只需要修改environment与parameters区域的 $SERVER_NAME $MODULE_NAME  $NAME_SPACE
     * $NACOS_NAME_SPACE  没有多层子项目的情况下SERVER_NAME与MODULE_NAME一般是相同的
     * 教育云认证服务 eg: SERVER_NAME=MODULE_NAME=ssop-auth-service, NACOS_NAME_SPACE=NAME_SPACE=talkweb-school-dev
     **/
    environment 
        // 服务的域名,一般与项目名称相同
        DOMAIN_NAME=$SERVER_NAME
        // docker容器内部端口,不建议修改
        CONTAINER_PORT=80
        // spring boot中设置了管理端口 eg: management.server.port:$MANAGE_PORT:18085 必须设置 MANAGE_PORT=XXX, 建议直接打开下面注释
        // MANAGE_PORT=90
        // spring boot中没有设置管理端口,但是设置了server.servlet.context-path,并且在非根目录时需要增加环境变量APP_MANAGE_PATH其值与server.servlet.context-path相同
        // APP_MANAGE_PATH="/"
        // 服务的的实例数量
        REPLICAS_NUM=1
        // 表示限制内存大小,单位为M,只能为数字;
        LIMIT_MEMORY=2048
        // maven配置文件setting路径
        MAVEN_SETTING=credentials(jenkins-maven-setting)
        // docker仓库地址,一般与系统环境变量相同,若相同时手动修改
        DOCKER_REGISTRY_ADDR="$env.DOCKER_REGISTRY_ADDR"
        // docker仓库账号密码--对应系统环境变量DOCKER_REGISTRY_ADDR
        DOCKER_REGISTRY=credentials(jenkins-docker-registry-creds)
        // 注册中心域名,默认是nacos 一般不需要修改
        NACOS_URL=nacos-headless.talkweb:8848
        // nacos凭证-对应NACOS_URL
        NACOS_CREDS=credentials(jenkins-nacos-creds)
        // k8s版本号
        K8S_VERSION=v1.19.16
        // K8s配置文件路径
        K8S_CONFIG=credentials(jenkins-k8s-config)
        // k8s中nacos账号密码存储的secret名称
        K8S_NACOS_AUTH="nacos-auth"
        // k8s中docker仓库信息存储的secret名称
        K8S_DOCKER_REGISTRY="hub-local"
        // 根据需求,xms、xmm已经自动添加 此处不用添加
        //JAVA_OPTS="-Dspring.XXX=XXX "
    
    parameters 
        // 修改部署的域名空间
        string(name:name_space, defaultValue: "$NAME_SPACE", description: 发布的命名空间)
        // 修改为子项目名称 多层情况,合同产品eg: soeasy-clm-message/soeasy-clm-message-server
        string(name:module, defaultValue: "$MODULE_NAME", description: 发布的子项目)
        // nacos空间
        choice(name: nacos_namespace, choices: [$NACOS_NAME_SPACE], description: nacos命名空间)
        // nacos组
        choice(name: nacos_group, choices: [DEFAULT_GROUP], description: nacos组名称)
        // 配置Tag的git参数,参数类型 设置为 分支或标签
        // 配置git地址:在流水线 -> 定义[Pipeline script from SCM] -> SCM[git] -> Repository URL[git地址]
        // -> Credentials[git账号] -> 指定分支(为空时代表any)[$Tag] --> 脚本路径[Jenkinsfile文件的工程路径]
        // -> 轻量级检出[去掉√ ]
    
// ########################需要修改的区域 结束################################
    stages 
        stage(Run maven) 
            agent 
                docker 
                    image maven:3.5-jdk-8-alpine
                    args -v $HOME/.m2:$HOME/.m2 
                    reuseNode true
                
            
            environment 
                // 设置maven本地仓库地址
                MAVEN_OPTS=" -Dmaven.repo.local=$HOME/.m2/repository "
            
            steps 
                sh "mvn -version && mvn clean package -pl $params.module -am   -Dmaven.test.skip=true -s $MAVEN_SETTING"
            
        
        stage(Build And Push Docker Image)
            steps 
                script 
                    // 获取标签版本或者分支的commitId
                    if (!params.Tag || params.Tag.contains(origin/)) 
                        sh "git rev-parse --short HEAD > commit-id"
                        tag = readFile(commit-id).replace("\\n", "").replace("\\r", "")
                     else 
                        tag = "$params.Tag"
                    
                    // 如果符合条件的tag指向最新提交则只是显示tag的名字,否则会有相关的后缀来描述该tag之后有多少次提交以及最新的提交commit-id
                    //git_tag = sh(returnStdout: true,script: git describe --tags --always).trim()
                    // 获取jar的名称
                    target_jar_name = sh(returnStdout: true,script: "target_jar_name=\\$(ls $params.module/target/*.jar) && echo \\$target_jar_name##*/").trim()
                    module_std = sh(returnStdout: true,script: "module_std=$target_jar_name.replace(.jar,) && echo \\$module_std%-[0-9].[0-9]*").trim()
                    // 解决打包名称不按正规方式命名, 保证模块代号相同
                    project_prefix=env.DOMAIN_NAME.split("-")[0]
                    module_std = module_std.contains("$project_prefix") ? module_std : params.module.split("/")[-1]
                    module_std = module_std.contains("$project_prefix") ? module_std : "$project_prefix-$module_std"
                    // 赋值镜像名称
                    docker_image = "$env.DOCKER_REGISTRY_ADDR/$params.name_space/$module_std:$tag"

                    // 编译镜像
                    sh "rm -rf $params.module/docker/target && mv $params.module/target  $params.module/docker"
                    sh "docker build -t $docker_image --build-arg JAR_FILE=$target_jar_name  $params.module/docker"
                
                echo "完成DOCKER镜像打包......"
                sh "docker login $DOCKER_REGISTRY_ADDR --username=$DOCKER_REGISTRY_USR --password=$DOCKER_REGISTRY_PSW"
                sh "docker push $docker_image"
                sh "docker rmi $docker_image"
            
        
        stage(Deploy to k8s)
            agent 
                docker 
                    image "ming19871211/kubectl:$K8S_VERSION"
                    reuseNode true
                
            
            environment 
                // 定义k8s访问凭证的环境变量
                KUBECONFIG=.kube/config
            
            steps
                sh "mkdir -p .kube && cp $K8S_CONFIG .kube/config"
                script 
                    java_opts = env.JAVA_OPTS ? env.JAVA_OPTS : ""
                    java_opts="$java_opts -server -Xms$LIMIT_MEMORY.toInteger() / 2 m -Xmx$LIMIT_MEMORY.toInteger() / 2m"
                    // 若有大并发随机数场景 java_opts增加 -Djava.security.egd=file:/dev/./urandom 参数
                    if (env.IS_SKYWALKING && env.IS_SKYWALKING == "true") 
                        java_opts="$java_opts -javaagent:/usr/skywalking/agent/skywalking-agent.jar=collector.backend_service=$SKYWALKING_BACKEND_SERVICE,agent.namespace=$params.name_space,agent.service_name=$DOMAIN_NAME"
                    
                    skywalking_vesion = env.SKYWALKING_VESION ? env.SKYWALKING_VESION : "7.0.0"
                    //判断k8s部署空间是否存在,若不存在,则直接创建
                    def k8s_namespaces = sh(script: "kubectl get namespaces $params.name_space", returnStatus: true)
                    if (k8s_namespaces != 0)
                        sh "kubectl create namespace $params.name_space"
                    
                    //查看nacos认证密钥是否存在,若不存在则创建
                    def nacos_auth_name = sh(script: "kubectl -n $params.name_space get secret $K8S_NACOS_AUTH ", returnStatus: true)
                    if (nacos_auth_name != 0)
                        sh "kubectl -n $params.name_space create secret generic $K8S_NACOS_AUTH \\
                        --from-literal=NACOS_USR=$NACOS_CREDS_USR --from-literal=NACOS_PWD=$NACOS_CREDS_PSW"
                    
                    //查看docker仓库认证密钥是否存在,若不存在则创建
                    def docker_registry_name = sh(script: "kubectl -n $params.name_space get secret $K8S_DOCKER_REGISTRY ", returnStatus: true)
                    if (docker_registry_name != 0)
                        sh "kubectl -n $params.name_space create secret docker-registry $K8S_DOCKER_REGISTRY \\
                        --docker-server=$DOCKER_REGISTRY_ADDR \\
                        --docker-username=$DOCKER_REGISTRY_USR --docker-password=$DOCKER_REGISTRY_PSW "
                    
                    //管理端口
                    //manage_port = env.MANAGE_PORT ?  env.MANAGE_PORT : "$CONTAINER_PORT.toInteger() + 10"
                    manage_port = env.MANAGE_PORT ?  env.MANAGE_PORT : env.CONTAINER_PORT
                    // 应用上下文件访问根路径
                    app_manage_path = ( env.APP_MANAGE_PATH && env.APP_MANAGE_PATH != "/" ) ? env.APP_MANAGE_PATH : ""
                    //部署服务
                    sh "sed -e s#__DOCKER_IMAGE__#$docker_image#  \\
                    -e s#__DOMAIN_NAME__#$env.DOMAIN_NAME# \\
                    -e s#__NAME_SPACE__#$params.name_space# \\
                    -e s#__REPLICAS_NUM__#$env.REPLICAS_NUM# \\
                    -e s#__CONTAINER_PORT__#$env.CONTAINER_PORT# \\
                    -e s#__MANAGE_PORT__#$manage_port# \\
                    -e s#__APP_MANAGE_PATH__#$app_manage_path# \\
                    -e s#__DOCKER_REGISTRY_SECRET__#$env.K8S_DOCKER_REGISTRY# \\
                    -e s#__LIMIT_MEMORY__#$env.LIMIT_MEMORY# \\
                    -e \\"s#__JAVA_OPTS__#$java_opts#\\" \\
                    -e s#__NACOS_NAMESPACE__#$params.nacos_namespace# \\
                    -e s#__NACOS_GROUP__#$params.nacos_group# \\
                    -e s#__NACOS_URL__#$env.NACOS_URL# \\
                    -e s#__NACOS_AUTH__#$env.K8S_NACOS_AUTH# \\
                    -e s#__SKYWALKING_VESION__#$skywalking_vesion# \\
                    $params.module/docker/k8s.yaml | kubectl apply -f -"

                    //查看部署服务的状态
                    timeout(time: 30, unit: SECONDS) 
                        for (int i = 0; i < 3; i++) 
                            sleep 5
                            sh "kubectl  -n $params.name_space  get pods |grep ^$env.DOMAIN_NAME "
                            if ( i == 2 )
                                no_run_num= sh(returnStdout: true,script: "kubectl get pods -n $params.name_space |grep ^$env.DOMAIN_NAME |grep -v Running |wc -l").trim()
                                echo "还未部署完成pods数目: $no_run_num"
                            
                        
                    
                
            
        
    
    post 
        always 
            echo 执行完成。
            sh rm -rf .kube 
        
        success 
            echo 恭喜你,发布成功了!
        
        unstable 
            echo 发布不稳定哦...
        
        failure 
            echo 发布失败啦,请查明原因哦!
        
        changed 
            echo 与之前信息有所不同哦...
        
    

vue-nacos

工程结构(vue)

|--- docker文件夹 # 存放部署文件
|------ Dockerfile
|------ Jenkinsfile
|------ k8s.yaml
|------ default.conf
|------ entrypoint.sh
|------ init.js
|--- public
|--- src
|--- vue相关文件
...
|--- .gitlab-ci.yml

配置文件-init.js

类似结构

/* 全局静态初始化环境配置 */
var ENV_API = 
  AUTH2_URL: "$AUTH2_URL",
  SERVICE_API_URL: "$SERVICE_API_URL"
;

替换变量脚本-entrypoint.sh

该脚本主要用于初始化配置文件占($ENV_XX)位参数的替换

#!/bin/bash
#配置文件路径,默认是static/js/config.js
__config_file="/usr/share/nginx/html/static/js/config.js"
if [ -n "$VUE_CONFIG_FILE_PATH" ]; then
  __config_file="/usr/share/nginx/html/$VUE_CONFIG_FILE_PATH"
fi;

if [  -f "/init-env/env.conf" ]; then
grep -v "^#" /init-env/env.conf |grep -v ^$  |while read LINE
do
  A=`echo $LINE |awk -F "=" print $1`
  B=`echo $LINE |awk -F "$A=" print $2`
  result=$(echo $A | grep "\\.")
  if [[ "$result" != "" ]]; then
    echo "#$A=\\"$B\\"" >>  /init-env/env1.conf
  elif [[ -z "$B" ]]; then
    echo "空行,不需转换舍弃";
  else
    B=$B//\\"/\\\\\\"; #处理特殊字符
    echo "$A=\\"$B/\\`/\\\\\\`\\"" >>  /init-env/env1.conf
  fi
done
sed -i s/\\r// /init-env/env1.conf
#sed -e s#\\&#\\\\&#g env.conf > env1.conf
eval "$(cat /init-env/env1.conf)"
rm -f /init-env/env1.conf
else
    echo "/init-env/env.conf文件不存在,注意需要引入外部环境变量哦!";
fi;

#备份配置文件,存在就不备份,不存在就备份
if [ ! -f  "$__config_file.env" ]; then
  cp $__config_file $__config_file.env
fi

# 变量替换方式直接会将$的变量替换掉
if [ -f "/init-env/env.conf" -o "$IS_ENV_REPLACE" = "true" ]; then
  #替换$中的环境变量
eval "cat <<EOF
$(<$__config_file.env)
EOF
" > $__config_file
  #删除环境变量文件
  rm -rf /init-env/env.conf
  #赋予可读权限
  chmod -R +r /usr/share/nginx/html
fi

nginx -g daemon off;

部署文件-default.conf

容器内nginx的配置

# 隐藏nginx版本
server_tokens off;
# 在某些浏览器上禁用内容类型嗅探。
add_header X-Content-Type-Options nosniff;
# 此标头启用跨站点脚本 (XSS) 过滤器
add_header X-XSS-Protection "1; mode=block";
# X-Frame-Options 是为了防止clickJacking
# DENY:不允许在 frame 中展示#
# SAMEORIGIN:允许在相同域名下frame展示
# ALLOW-FROM https://example.com/ :指定来源的 frame 中展示
# add_header X-Frame-Options SAMEORIGIN;
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 5;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\\.";
# 设置压缩所需要的缓冲区大小
gzip_buffers 32 4k;
# 设置gzip压缩针对的HTTP协议版本
gzip_http_version 1.0;
server 
    listen       80;
    server_name  localhost;
    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;
    # 带前缀方式的将 $SERVER_NAME替换为对应前缀名称即可,若访问直接在根目录下去掉$SERVER_NAME,去掉多余的/
    location /$SERVER_NAME 
        alias /usr/share/nginx/html;
        try_files $uri $uri/ /$SERVER_NAME/index.html;#根据官网这规则配置
        expires 5m;
    
    # 若访问直接在根目录下,注释下面三行
    location = / 
        return 301 $scheme://$host/$SERVER_NAME;
    
    error_page   500 502 503 504  /50x.html;
    location = /50x.html 
        root   /usr/share/nginx/html;
    

部署文件-Dockerfile(vue)

用于容器镜像制作

FROM nginx:stable
MAINTAINER Qiming Mei <meiqiming@talkweb.com.cn>

ADD default.conf /etc/nginx/conf.d/default.conf
COPY dist/ /usr/share/nginx/html/
COPY init.js /usr/share/nginx/html/static/js/config.js
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
#CMD ["/bin/bash","-c", "/entrypoint.sh" ]
ENTRYPOINT ["/entrypoint.sh"]

部署文件-k8s.yaml(vue)

apiVersion: v1
kind: Namespace
metadata:
  name: __NAME_SPACE__
  labels:
    name: __NAME_SPACE__
---
apiVersion: v1
kind: Service
metadata:
  name: __DOMAIN_NAME__
  namespace: __NAME_SPACE__
spec:
  ports:
  - name: app-port
    port: 80
    targetPort: 80
    protocol: TCP
  selector:
    app: __DOMAIN_NAME__
---
apiVersion: apps/v1  
kind: Deployment
metadata:
  name: __DOMAIN_NAME__
  namespace: __NAME_SPACE__
spec:
  selector:
    matchLabels:
      app: __DOMAIN_NAME__
  replicas: __REPLICAS_NUM__ 
  template: 
    metadata:
      labels:
        app: __DOMAIN_NAME__
    spec:
      initContainers:
      - name: init-env-sidecar
        image: busybox:latest
        command: [ "sh", "-c"]
        args:
        - set -ex;
          CONFIG_FILE=$CONFIG_FILE:-"vue-comm.properties";
          SYS_GLOBAL_CONFIG=$SYS_GLOBAL_CONFIG:-"sys-global-config.properties";
          wget --post-data="username=$NACOS_USR&password=$NACOS_PWD以上是关于持续集成CI&CD之CI的完整版最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

持续集成CI&CD之配置管理最佳实践

持续集成:什么是持续集成(CI)持续交付(CD)和持续部署(CD)

CI/CD

CI/CD

新Jenkins实践-第1章 开篇-为什么要做CI/CD?

每周一书《OpenStack最佳实践——测试与CI/CD》留言送书