写在前面
为了不违反广告法,我竭尽全力,不过“最佳实践”确是标题党无疑,如果硬要说的话 只能是个人最佳实践。
问题引出
可能很多新手都会遇到同样的问题:我要我的Asp.net Core 应用传统方式直接部署(host),docker部署(docker-compose),kubernetes(以下称k8s)下部署,都用统一的方式读取配置,怎么实现呢?。
大家知道,我们默认平时配置文件以appsettings.json
、appsettings.{EnvironmentName}.json
形式存在,这样在host方式下面没有问题,但在docker下,如果直接把配置打包到镜像,那每次改一下下配置就需要重新打包,那成本太大了。另外在k8s下面又有Secret、ConfigMap等多种方式管理配置,如何把多种配置存储和读取,有机结合、同一份代码统一管理使用,是我们今天的主题。
下面我用一个Api网关Ocelot作为示例(demo),讲讲我处理的方式,希望能给大家带来一定启发。
一、先把配置文件改成Yaml格式
注:
其实不改为yml也可以的!!
主要考虑到后面在docker、k8s等里面,更好管理,比如yaml的注释和json的注释语法不一致等等问题;
比如我原来的appsettings.json长这样:
{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","AddAdministration": {"Path": "/administration","IdentityServer": {"Authority": "http://172.16.3.117:5100","ApiName": "ocelot","RequireHttpsMetadata": false,"ApiSecret": "secret"}}
}
改成 appsettings.yml
Logging:LogLevel:Default: InformationMicrosoft: WarningMicrosoft.Hosting.Lifetime: Information
AllowedHosts: '*'
AddAdministration:Path: /administrationIdentityServer:Authority: 'http://172.16.3.117:5100'ApiName: ocelotRequireHttpsMetadata: falseApiSecret: secret
是不是看起来简单清晰了很多,其实我现在越来越喜欢用yml了
既然配置源的格式变了,那读取配置的方法也肯定变了,起码config.AddJsonFile(“xx.json”) 要改为 config.AddYamlFile(“xx.yml”)
新增引用的扩展:NetEscapades.Configuration.Yaml
加载配置文件改写为:
.AddYamlFile("appsettings.yml", optional: false, reloadOnChange: true)
.AddYamlFile($"appsettings.{env.EnvironmentName}.yml", optional: true, reloadOnChange: true)
二、Docker使用
“但在docker下,如果直接把配置打包到镜像,那每次改一下下配置就需要重新打包,那成本太大了”
我前面提出了这个问题,如想不重新打包,Volume(挂载)就好了。
把你的配置文件放到/home/heidemo/config
目录后,比如我们什么的示例配置文件:appsettings.yml
docker run --rm=true -v /home/heidemo/config:/config gebiwangshushu/hei-ocelot-apigateway:1.0
这样就可以随性更新/home/heidemo/config下的配置信息而不需要每次都重新build镜像了,这样是支持热更新的,当然如果你修改的那个配置是需要重启程序才可以加载的,那还是要用docker-compose 重启下对应服务的;
三、docker-compose使用
我们知道 Docker是 官方编排(Orchestration)项目之一,如果我们在Docker环境下挂载配置的话,那在docker-compose下面的配置也是挂载的,我们来看下我们掐头去尾后的 docker-compose.yml
:
version: '3.4'services:hei.ocelot.apigateway:...volumes:- /home/heidemo/config:/app/config...
没错,docker-compose 额挂载就这么定义,这样可以实现跟Docker一样的挂载效果;
大家可以用以上配置 clone我的demo,然后 docker-compose up
一下,看看效果;
四、k8s使用
前面的docker、docker-compose 的方式还是非常容易理解的,就是挂载;那我们在k8s下面运行的时候,它的容器实例是动态的运行到集群的各台机器上的,那如果我们我们只用文件挂载很明显就不满足要求了,我们来看看怎么实现。
先准备一个configMap,hei-ocelot-config.yml
apiVersion: v1
kind: ConfigMap
metadata:name: hei-ocelot-apigatewaynamespace: dotnetcore
data:appsettings.yml: |Logging:LogLevel:Default: InformationMicrosoft: WarningMicrosoft.Hosting.Lifetime: InformationAllowedHosts: '*'AddAdministration:Path: /administrationIdentityServer:Authority: 'http://172.16.1.30:31100' #这里的授权中心可以配置你自己的ApiName: ocelotRequireHttpsMetadata: falseApiSecret: secret
完整请看这里
大家可以看到,我们的data节点是跟我们程序里面的appsettings.json
一样一样的,这也是我们比较喜欢不再用json的原因。
创建configMap:
kubectl apply -f hei-ocelot-config.yml
查看configMap:
kubectl describe configmaps hei-ocelot-apigateway -n dotnetcore
使用configMap:
这里是使用示例,在我的demo根目录下面完整配置deploy.yml 是可以直接部署的。
apiVersion: apps/v1
kind: Deployment
metadata:name: hei-ocelot-apigatewaynamespace: dotnetcore
spec:replicas: 1selector:matchLabels:app: hei-ocelot-apigatewaytemplate:metadata:labels:app: hei-ocelot-apigatewayspec:containers:- name: hei-ocelot-apigatewayimage: gebiwangshushu/hei-ocelot-apigateway:1.1ports:- containerPort: 80volumeMounts:- name: hei-ocelot-apigatewaymountPath: "/app/config"readOnly: truevolumes:- name: hei-ocelot-apigatewayconfigMap:name: hei-ocelot-apigateway
可以看到我们在k8s下面也是用volumes的方式使用我们的configMap的,其中挂载目录volumeMounts:mountPath是”/app/config”,我们进入运行中pod看下配置:
kubectl exec -it hei-ocelot-apigateway-795495f7c8-vpmhb sh -n dotnetcorecd /app/config
我们可以看到我们的pod里面的/app/config ,确确实实有我们要的配置;
这里因为我们是volumes 的方式的,大家可以试着改下上面的configMap— hei-ocelot-config.yml 再重新apply 一下,会看到这里的配置是几乎是即时更新的(有一点点延迟);
PS:有一个问题有些在startup使用的配置,即时更新了也需要重启下应用,这个我暂时还没想到什么办法好办法,各位老哥有什么思路的可以直接甩我一脸~
总结
其实写完我觉得也有点怪怪,说新手引导吧,不够保姆式、说经验分享,不够精简,下次我定好好想,认真写好点;
然后我的主题,其实思考过同样问题的读者,全文就一句:volumes挂载配置做到各种环境下的配置统一;
最后,我提出了一个问题:On K8s的时候, 程序启动使用的配置,如何在配置文件更新的情况后重启程序应用新配置(或者叫热加载配置?当然这里不是指配置文件的reloadOnChange=true);
本文示例源码
github:https://github.com/gebiWangshushu/Hei.Ocelot.ApiGateway
文章博客园地址请点击“阅读原文”