#云原生征文# 持续集成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="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: {}
【本文正在参加云原生有奖征文活动】,活动链接:https://ost.51cto.com/posts/12598
贼拉到位
原生k8s专家
很不错,赞起!