使用 Docker Configs 存储配置信息
在使用 configs 之前,在编排容器时,对于容器运行所需要的参数我都是通过环境变量的方式去配置的;所以在 compose.yml 中可以看到大量代码片段
这种方式虽然目前没有太大的问题,在工程实践上也没有太大的困难;但似乎仍有可以改进的地方,于是我把所有的环境变量配置,从 compose.yml 抽离成一个个独立的 envfile,使用 env_file 进行管理,于是可以该为这样的方式
但这样还是有些弊端,比如我们现在要修改配置的话,修改完配置文件,还需要重新执行下容器编排文件。因为容器编排文件里是对所有容器的定义,这就会导致一些不涉及修改配置的其他服务也要跟着重启。虽然我们可以把每个服务单独维护一个编排文件,但这样就又失去了编排文件的意义。 那么,既然我们使用的是 Docker Swarm 方式来编排容器。从 Docker 的视角,如何管理配置呢?
简单的说,什么是 Docker Configs
Configs 允许存储一些非敏感信息,比如在容器运行之外的一些配置文件。这种方式可以保证 docker 镜像更加通用,而不需要将配置文件或者是环境变量绑定到容器。对于敏感信息,比如数据库账号之类的,建议使用secrets与 secrets 不同之处在于,configs 并没有对数据进行加密,而是直接把配置文件挂载到容器的文件系统中。同时允许任何时刻删除或者添加 configs,也可以共享 configs;为了支持更高灵活性,configs 内容可以是普通的字符串或者是二进制内容,当然二进制要保证大小在 512kb 之内
Docker 是如何管理 Configs
当你添加一个 config 到 swarm 时,Docker 会通过把 config 发送到 swarm manager;这个配置被加密存储在 Raft log 中,同时 swarm 集群中的各个 manager node 是共享这个 configs 信息,以确保所有的 manager node 都是同样的 configs 信息,即便在某个 manager node 被重置后,获取到的 configs 信息依旧如此
如果我们授权某个正在运行的 service 访问 config,config 信息会被以文件的形式挂载到容器里,请注意这里要与 volumes 区分。volumes 是把宿主机文件系统中的文件,挂载到容器中;而 config 相当于是把 swarm 的配置信息以文件的形式存放在容器中,两者本质上的差异还是比较明显的。默认情况下,挂载到容器里的 configs 信息,会存储在容器路径 /<config-name> 下;
一个 node 是否可以使用 configs,取决于这个 node 是否为 swarm manager 角色,或者有被授权访问 configs 的 service 运行在该 node 上。当容器任务停止运行时,共享给它的配置将从该容器的内存文件系统中卸载,并从节点的内存中刷新。
如果 node 与 swarm 失去了连接,已经访问 configs 的 service 仍然可以访问,只是不能够接受 configs 的更新,直到 node 恢复了与 swarm 的连接
如果要更新 stack,一定要更新 compose-file,然后重新执行 docker stack deploy -c <new-compose-file> <stack-name> 如果在 compose-file 中使用了一个新的 config,service 则会使用最新的 config,请注意 config 是不可以修改的,所以不能修改已经创建的 config,而是应该创建一个 config
运用到 Springboot
如一开始我介绍的,通过注入环境变量的方式,动态改变 springboot 程序运行状态的方式,是很常见的方式;一般来说,如果把 springboot 容器化后,我们考虑的便是如何能够通过参数的方式切换 springboot 运行的 profile 比如:
如果像我一样,好奇 springboot 如何映射环境变量,可以参考链接:
但上述方式,且不说优雅与否;从我的经验来看,要求管理 docker 的人或多或少的了解些要运行的 springboot 程序定义了那些 profiles,乃至知道关键配置属性是什么,才知道如何去声明环境变量,以注入到容器的环境中,这样就显得 docker 并不是那么灵活。
我们已经知道可以通过 docker config 创建配置信息,这些配置信息本身存储在 Docker Swarm 中(可以假设 Docker Swarm 中也有一个类似 k8s 的 etcd,是否如此我还不清楚),当容器引用某个 config 时,config 会以文件的形式存在于容器内的文件系统中,默认情况下 Linux 环境存在于容器内路径 /<config-name>
所以,我们可以通过环境变量的方式改变 springboot 启动时读取配置文件的路径,按照我们所指定的路径去读取配置文件,然后启动;这样我们就可以将 springboot 容器与配置完全分离,也便于我们版本管理配置;
如何更新 Docker Config
在使用 Docker Config 的方式成功部署后,遇到一个最迫切的问题就是:
我该如何让服务使用更新后的配置文件
Docker Config 被设计为版本化的。这是 Docker 设计时就决定了的。假设有个服务正在使用 myconfig 在运行,同时有另外一个服务也在使用 myconfig 在运行,这时候如果我们因为任意一个服务需要去修改配置,在没有强制版本控制的情况下,我们很可能是直接更新 myconfig 这个配置。这样的话,服务间就会因为共用同一个 config 而产生耦合。糟糕的话,我们本来是想修改服务 1 的,结果不但服务没有正确按照新配置启动,反而导致另外一个服务也没法正在运行了。参考:Let me try explaining why I think versioning is important;
如何操作
方式一,创建新的 docker config
很好理解,就是创建一个新的 docker config,配置文件内容修改后,就使用修改后的文件创建一个新的 docker config
2. 既然已经创建了一个新的 docker config,那么只需要让服务使用这个 config,并且重新启动就可以了
方式二,动态 docker config name
如果说更新服务的方式是 docker stack deploy ,或许有时候真的是要把整个 stack 更新一下呢。如果使用的 docker config 名称是固定的,像下面这样
重复执行 docker stack deploy 可能会报错,错误信息可能是 :
Error response from daemon: rpc error: code = InvalidArgument desc = only updates to Labels are allowed
既然如此,那就让 docker config 的名字,随着 docker stack deploy 的执行而变化吧。我们把编排文件改成下面这个样子
deploy 命令改为
References
版权声明: 本文为 InfoQ 作者【yombo】的原创文章。
原文链接:【http://xie.infoq.cn/article/b4a190f8f0cd5b0beb33d06c8】。文章转载请联系作者。
评论