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

博观而约取,厚积而薄发。这篇文章主要讲述持续集成CI&CD之配置管理最佳实践相关的知识,希望能为你提供帮助。
上一章:??持续集成CI& CD之工具快速搭建??
下一章:??持续集成CI& CD之CI的完整版最佳实践??
选用阿里的nacos配置管理进行最佳实践
Nacos Config 配置方案设计业务配置的一些场景
在实际的业务场景中应用和共享配置间的关系可能如下图所示:



  • 从单个应用的角度来看: 应用可能会有多套(develop/beta/product)发布环境,多套发布环境之间有不同的基础配置,例如数据库。
  • 从多个应用的角度来看:多个应用间可能会有一些共享通用的配置,比如多个应用之间共用一套zookeeper集群。
极简配置方案设计
在实际开发、运维过程中,如果一个或多个应用使用了多个配置文件,特别是共享的配置文件,很容易出现误解,搞混配置【需要区别多个配置文件的优先级】;建议设计2层配置文件即可,手动配置的全局共享配置只能有一个,并且不支持动态刷新;每一个应用除了有一个全局配置文件后,还应该默认有一个专属的配置(默认可以不存在),支持动态刷新;如下图



配置的优化级别应该是:本地配置 < 全局共享配置< 专属配置不同的环境配置,应该使用空间进行隔离
实现案例nacos配置的优先级
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
  • A: 通过??spring.cloud.nacos.config.shared-configs[n].data-id?? 支持多个共享 Data Id 的配置
  • B: 通过??spring.cloud.nacos.config.extension-configs[n].data-id?? 的方式支持多个扩展 Data Id 的配置
  • C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置
当三种方式共同使用时,他们的一个优先级关系是:A < B < C
nacos配置的使用
以spring-cloud为例基于nacos配置优先级,以及设计方案的最简原则,建议spring-cloud项目使用以下bootstrap.yml文件配置
server:
    port:  $CONTAINER_PORT:8080  #所有类似$CONTAINER_PORT:8080写法的  CONTAINER_PORT不能改动,默认值根据业务修改
    servlet:
        context-path:  /      #工程访问路径业务自己定义
#  https://github.com/spring-projects
spring:
    main:
        allow-bean-definition-overriding:  true  #  允许定义bean的覆盖
    application:
        name:  $APP_NAME:talkweb-demo  #默认值一般修改为与工程名或子模块名相同
    cloud:
        ###nacos配置###
        nacos:
            server-addr:  $NACOS_URL:192.168.141.203:8848
            username:  $NACOS_USR:nacos-usr
            password:  $NACOS_PWD:nacos-pwd
            discovery:
                namespace:  $NACOS_NAMESPACE:namespace-dev
                group:  $NACOS_GROUP:DEFAULT_GROUP
            config:
                namespace:  $NACOS_NAMESPACE:namespace-dev
                file-extension:  properties
                extension-configs:
                    #  全局配置不支持动态刷新
                    -  data-id:  $GLOBAL_COMM:global-comm.properties
                        group:  $NACOS_GROUP:DEFAULT_GROUP


??全局配置文件??:global-comm.properties,(手动设置data-id,全局的公共配置)
??空间ID??:namespace-dev(设置空间ID时,建议手动输入可读的ID,不要系统自动生成)
??应用专属配置文件??:$spring.application.name.properties,(系统默认设置的data-id,主要使用场景是,”单个服务调优“或者“在公共配置中会影响其他服务的特别的配置“)
为保证代码完整性、可阅读性,所有的代码配置都必须在本地的配置中体现,并设置默认值[$环境:默认值]。所以建议nacos中的配置文件以key=vue 的形式的属性文件存在,用于覆盖本地的默认值。
扩展有些管理系统的配置的一些开关,可以结合nacos的配置实现,如下增加??系统全局配置??
spring:
    application:
        name:  $APP_NAME:talkweb-demo  #默认值一般修改为与工程名或子模块名相同
    cloud:
        ###nacos配置###
        nacos:
            server-addr:  $NACOS_URL:192.168.141.203:8848
            username:  $NACOS_USR:nacos-usr
            password:  $NACOS_PWD:nacos-pwd
            discovery:
                namespace:  $NACOS_NAMESPACE:namespace-dev
                group:  $NACOS_GROUP:DEFAULT_GROUP
            config:
                namespace:  $NACOS_NAMESPACE:namespace-dev
                file-extension:  properties
                extension-configs:
                    #  全局配置不支持动态刷新
                    -  data-id:  $GLOBAL_COMM:global-comm.properties
                        group:  $NACOS_GROUP:DEFAULT_GROUP
                    #  系统全局配置支持动态刷新[该配置由系统默认创建,不能人为操作]
                    -  data-id:  $SYS_GLOBAL_CONFIG:sys-global-config.properties
                        group:  $NACOS_GROUP:DEFAULT_GROUP
                        refresh:  true


系统默认全局配置:sys-global-config.properties (手动设置data-id,支持动态刷新,业务系统自动生成,不能人为操作,主要用于管理后台配置的参数,eg:邮箱配置、短信配置、系统开关等等)
前端使用nacos配置
为了使一套代码能再不同的环境中,前端也需要使用配置中心,最好能与后端使用一套配置。设计方案:前端工程(vue)在项目中使用公共的一个配置文件;部署时利用shell脚本,从nacos中获取配置后, 自动替换前端配置文件中的配置项。
以k8s部署为例1.在docker编译时,将??默认的配置文件???替换为??带有占位符配置文件??。(占位符,只是将具体值由nacos中key取代,eg: $key)
FROM  nginx:stable
MAINTAINER  Qiming  Mei  < meiqiming@talkweb.com.cn>

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"]


2.利用启动entrypoint.sh配置文件中??替换占位符??
#!/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;


3.利用k8s的初始化容器下载nacos配置文件 ??__XXX__??为占位符,部署时会替换为具体的值
---
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="https://www.songbingjia.com/android/username=$NACOS_USR& password=$NACOS_PWD"  -S  "$NACOS_URL/nacos/v1/auth/users/login"  -O  login-token;
                    access_token=$(grep    -Eo  "accessToken":"([^"]*)"  login-token  |awk  -F  \\":\\"  print  $2);
                    access_token=$access_token/\\"/;
                    rm  -f  /init-env/env.conf;
                    rm  -f  /init-env/env-sys.conf;
                    wget  "$NACOS_URL/nacos/v1/cs/configs?dataId=$CONFIG_FILE& group=$NACOS_GROUP& tenant=$NACOS_NAMESPACE& accessToken=$access_token"  -O    /init-env/env.conf;
                    wget  "$NACOS_URL/nacos/v1/cs/configs?dataId=$SYS_GLOBAL_CONFIG& group=$NACOS_GROUP& tenant=$NACOS_NAMESPACE& accessToken=$access_token"  -O    /init-env/env-sys.conf  ||  return  0;
                    if  [    $?  -eq  0  -a  -f  "/init-env/env-sys.conf"  ];   then
                        echo  -e    "\\n"  > >     /init-env/env.conf;
                        cat  /init-env/env-sys.conf  > >   /init-env/env.conf;
                    fi
                env:  #环境变量设置
                -  name:  NACOS_NAMESPACE
                    value:  __NACOS_NAMESPACE__
                -  name:  NACOS_GROUP
                    value:  __NACOS_GROUP__
                -  name:  SYS_GLOBAL_CONFIG
                    value:  __SYS_GLOBAL_CONFIG__
                -  name:  CONFIG_FILE
                    value:  __CONFIG_FILE__
                -  name:  NACOS_URL
                    value:  __NACOS_URL__
                envFrom:
                -  secretRef:
                        name:  __NACOS_AUTH__
                volumeMounts:
                -  name:  init-env
                    mountPath:  /init-env/
            containers:
            -  name:  __DOMAIN_NAME__
                image:  __DOCKER_IMAGE__
                imagePullPolicy:  IfNotPresent  #本地存在就不到远程拉取镜像
                env:  #环境变量设置
                -  name:  TZ
                    value:  Asia/Shanghai
                -  name:  DOMAIN_NAME
                    value:  __DOMAIN_NAME__.__NAME_SPACE__
                resources:  #资源限制
                    requests:
                        memory:  "128Mi"
                        cpu:  "100m"  #最低需要  0.1个cpu
                    limits:
                        memory:  "__LIMIT_MEMORY__Mi"
                        cpu:  "1000m"
                ports:
                -  containerPort:  80
                readinessProbe:  #就绪探针
                #    httpGet:
                #        path:  /index.html
                #        port:  80
                    tcpSocket:
                        port:  80
                    initialDelaySeconds:  30
                    periodSeconds:  15
                    timeoutSeconds:  5
                livenessProbe:  #健康检查
                #    httpGet:
                #        path:  /index.html
                #        port:  80
                    tcpSocket:
                        port:  80
                    initialDelaySeconds:  30
                    periodSeconds:  15
                    timeoutSeconds:  5
                volumeMounts:
                -  name:  time-config
                    mountPath:  /etc/localtime
                    readOnly:  true
                -  name:  init-env
                    mountPath:  /init-env/
            imagePullSecrets:
            -  name:  __DOCKER_REGISTRY_SECRET__
            nodeSelector:
                isDev:  "true"
            volumes:
            -  name:  time-config
                hostPath:
                    path:  /etc/localtime
            -  name:  init-env
                emptyDir: 


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


    推荐阅读