可能是Asp.net Core On host、 docker、kubernetes(K8s) 配置读取的最佳实践
写在前面
为了不违反广告法,我竭尽全力,不过“最佳实践”确是标题党无疑,如果硬要说的话 只能是个人最佳实践。
问题引出
可能很多新手都会遇到同样的问题:我要我的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: 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
是不是看起来简单清晰了很多,其实我现在越来越喜欢用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-apigateway
namespace: dotnetcore
data:
appsettings.yml: |
Logging:
LogLevel:
Default: Information
Microsoft: Warning
Microsoft.Hosting.Lifetime: Information
AllowedHosts: '*'
AddAdministration:
Path: /administration
IdentityServer:
Authority: 'http://172.16.1.30:31100' #这里的授权中心可以配置你自己的
ApiName: ocelot
RequireHttpsMetadata: false
ApiSecret: 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-apigateway
namespace: dotnetcore
spec:
replicas: 1
selector:
matchLabels:
app: hei-ocelot-apigateway
template:
metadata:
labels:
app: hei-ocelot-apigateway
spec:
containers:
- name: hei-ocelot-apigateway
image: gebiwangshushu/hei-ocelot-apigateway:1.1
ports:
- containerPort: 80
volumeMounts:
- name: hei-ocelot-apigateway
mountPath: "/app/config"
readOnly: true
volumes:
- name: hei-ocelot-apigateway
configMap:
name: hei-ocelot-apigateway
可以看到我们在k8s下面也是用volumes的方式使用我们的configMap的,其中挂载目录volumeMounts:mountPath是"/app/config",我们进入运行中pod看下配置:
kubectl exec -it hei-ocelot-apigateway-795495f7c8-vpmhb sh -n dotnetcore
cd /app/config
我们可以看到我们的pod里面的/app/config ,确确实实有我们要的配置;
这里因为我们是volumes 的方式的,大家可以试着改下上面的configMap-- hei-ocelot-config.yml 再重新apply 一下,会看到这里的配置是几乎是即时更新的(有一点点延迟);
PS:有一个问题有些在startup使用的配置,即时更新了也需要重启下应用,这个我暂时还没想到什么办法好办法,各位老哥有什么思路的可以直接甩我一脸~
总结
其实写完我觉得也有点怪怪,说新手引导吧,不够保姆式、说经验分享,不够精简,下次我定好好想,认真写好点;
然后我的主题,其实思考过同样问题的读者,全文就一句:volumes挂载配置做到各种环境下的配置统一;
最后,我抛出了一个问题:On K8s的时候, 程序启动使用的配置,如何在配置文件更新的情况后重启程序应用新配置(或者叫热加载配置?当然这里不是指配置文件的reloadOnChange=true);
源码
https://github.com/gebiWangshushu/Hei.Ocelot.ApiGateway
参考
龙哥Edison Zhouk8s 系列博客:https://www.cnblogs.com/edisonchou/p/aspnet_core_on_k8s_foundation_artcles_index.html
- 分布式消息队列 RocketMQ 源码分析 —— Message 顺序发送与消费
- 深入解析快速排序算法的原理及其Go语言版实现
- Hadoop3.0: YARN Resource配置说明
- GO语言利用K近邻算法实现小说鉴黄
- Why Spring Boot
- 如何使用HammerDB进行MySQL基准测试
- 一个不可思议的MySQL慢查分析与解决
- Apache Spark 2.2中基于成本的优化器(CBO)
- golang的HTTP基本认证机制实例详解
- hdu----(3065)病毒侵袭持续中(AC自动机)
- Spark机器学习库(MLlib)指南之简介及基础统计
- 监控利器之 Prometheus
- C线程同步/异步
- Spring-Security-入门(一):登录与退出
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Jetpack新成员,App Startup一篇就懂
- python之利用魔术方法实现自己定义的二维向量
- 【猫狗数据集】使用top1和top5准确率衡量模型
- django-URL反向解析Reverse(九)
- spring之如何在web应用中使用?
- 【猫狗数据集】使用预训练的resnet18模型
- django-URL应用命名空间(十)
- 【colab pytorch】模型权重初始化
- 【colab pytorch】提取模型中的某一层
- django-URL实例命名空间(十一)
- 【colab pytorch】数据预处理
- django-URL默认参数传递
- springmvc之自定义视图
- 【colab pytorch】训练和测试常用模板代码
- django-VIews之HttpResponse(一)