使用环境变量、arg参数,config声明式配置docker服务

在k8s中,我们可以使用deployment文件,hashmap文件等声明是的配置微服务。我们也希望利用docker的一些功能来做相同的事情。


一, arg 、env、env_file
arg 可以在docker文件中声明参数,被声明的参数可以被用在声明后的语句中。
arg是编译时变量,仅用于编译阶段;如果一个docker文件中由多个编译阶段,每个阶段应该由自己的arg参数。
声明arg的时候如果没有设置默认值,就需要使用--build-arg <varname>=<value>方式在build的时候提供值。
arg是build阶段的变量,Image创建后将不会存在,也就是说不能用于容器的运行。但是,可以方便的通过“docker history” 方便的查看build时使用的arg.
env 与arg对比,它是运行时变量,env变量在其声明以后就可以使用,并且在build的时候会保存下来;在容器运行的时候可以继续访问。
evn可以在声明时赋值,也可以在运行时赋值,在运行时使用-e参数赋值,例如:
$ docker run -e "env_var_name=another_value"。
Arg与ENV在命令行赋值时有这样显著的区别:

  1. 如果没有显示的声明一个arg, 在build时传递这个arg会出错。但是Env不需要再docker文件中显示的声明,可以在docker run命令中声明任意新的env变量。
  2. 编译时声明arg需要指定值,负责出错。但是env不需要,如果没有给一个env变量赋值,该变量会被认为是来自当前host的变量。

下图就描述了环境变量与arg变量的区别。


image.png

ARG和ENV可以被联合使用,因为env在build的时候可以被保存下来,所以通过联合使用ARG和ENV, 可以在build的时候传递参数,并在运行时仍旧可用。

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

ENV参数的来源有三种方式,上面提了两种,还有地中方式,总结如下:

  1. 显示传参, docker run -e env-key=env-vule, 或者被用于docker-compose中:
version: '3'
services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name=another_value
  1. 获取宿主机的环境变量,通过不提供赋值:docker run -e env_var_name,或者被用于docker-compose中:
version: '3'
services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name
  1. 使用env_file文件批量导入环境变量。
    除了手动书写环境变量,docker也支持把环境变量卸载配置文件中,批量导入。这个工作是由env_file来完成,
//docker run方式
docker run --env-file=env_file_name alpine env
//docker compose 方式
version: '3'
services:
  plex:
    image: linuxserver/plex
    env_file: env_file_name

env_file支持设置多个环境变量文件,例如下面的形式也是可以的:

nv_file:
  - ./a.env
  - ./b.env

上面个的两个环境变量是以相对路径的方式来表明文件,相对路径的起点为docer-compose文件所在的位置。
另外, Env声明的环境变量比在文件中声明的优先级高,所以手写的env环境变量总是试图替换文件中的同名变量。
env_file环境文件的格式时键值对方式,如下, #用于表明注释; 如果只设置key,表示空值,如果只设置=val 将被忽略。

# Set Rails/Rack environment
RACK_ENV=development
VAR="quoted"

注意
ARG主要用于build阶段,当成成image后,可以使用docker history查看Image中的arg信息。
ENV主要用于运行时,当启动容器后,可以使登录容器查看系统环境变量,也可以启动前,使用ocker inspect查看启动时的环境变量及设置。

二,环境变量替换
环境变量(用ENV声明),可以被作为参数用于部分的dockerfile 指令中,通常以variable_name或者{variable_name}表示,带括号的写法也可以用于{foo}_bar类似的变量替换场景。 变量的写法也支持部分bash的变量写法:{variable:-word} 如果变量未被设置,使用word作为期值。
{variable:+word} 如果设置了变量,word是变量结果,否则为空。 转移符号"\"对变量起作用,可以用来将变量变成普通文字。
以下是一个变量替换的例子:

ROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar
ADD . $FOO       # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

在dockerfile中环境变量替换支持以下的指令:
. ADD
. COPY
. ENV
. EXPOSE
. FROM
. LABEL
. STOPSIGNAL
. USER
. VOLUME
. WORKDIR
. ONBUILD
从上面arg和env配合使用得例子,可以看到arg也可以作为参数在docker文件中使用。

三, .Env 文件
.env 文件仅试用于docker-compose 或docker-stack, 首先它不是env_file文件,除非你把它在env_file文件下声明。其次,它是作为默认的环境变量替换与ocker-compose 必须存在于相同的补录。
也就是说,它只跟docker-compose.yaml文件有关系,它运行docker-compose.yaml时,胡主动从.env找寻需要替换的环境变量。它的格式也是key=value格式。如下,它们被用于替换comose文件以开头的变量,例如VARIABLE_NAME.

//.env文件
VARIABLE_NAME=some value
OTHER_VARIABLE_NAME=some other value, like 5

//compose文件中的引用替换
version: '3'
services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name=${VARIABLE_NAME} # here it is

注意
对于.env文件,我们可以在不启动docker-compose的情况下,使用 docker-compose config来查看最终替换后的效果,这个非常的方便。

四, config 文件
环境变量文件外(.env, env-file),docker swarm 支持在image镜像以外定义配置属性,这些配置属性被保存到config文件中,它可以由docker config 命令来创建。
config文件类似于k8s得configMap, 它得内容不加密(不同于secret),通常被用来直接mount到容器磁盘文件系统里,它可以任意时候通过docker config添加,也可以任意时候移除。而且,config文件可以被服务之间共享。
config文件可以是string类型或者二级制类型。config文件默认在容器中mount到/<config-name>。挂在config文件得时候,可一个给文件设置权限,如果没有设置,默认权限为容器启动时得运行用户,并且所有用户可读。
config文件在运行时mount给容器,在容器关闭的时候在从容器的文件系统unmount。除了容器服务可以访问外, 作为swarm manager的node也可以访问,其它节点是不能访问config文件的。
另外一个config如果正在被服务使用,是不能用命令删除的,所以最好使用版本号、替换更新的技巧来替换和删除旧config文件。
docker config的子命令包括以下:

docker config create
docker config create [OPTIONS] CONFIG file|-
通过一个文件或者STDIN创建 config。这个命令只能在swarm得manager节点执行,需要特别注意。
options支持两个变量:
--label , -l : 给config创建一个lable
--template-driver: 将config内的模板内容最终转化为config内容的驱动。
示例1,通过STDIN输入创建config

$ printf <config> | docker config create my_config -

onakdyv307se2tl7nl20anokv

$ docker config ls

ID                          NAME                CREATED             UPDATED
onakdyv307se2tl7nl20anokv   my_config           6 seconds ago       6 seconds ago

示例2, 通过文件来创建config

$ docker config create my_config ./config.json

dg426haahpi5ezmkkj5kyl3sn

$ docker config ls

ID                          NAME                CREATED             UPDATED
dg426haahpi5ezmkkj5kyl3sn   my_config           7 seconds ago       7 seconds ago

可以看到,我们可以使用任何格式的文件来生成config,这个比k8s的configmap方便。

示例3,使用golang作为模板引擎,生成config
index.html.tmpl:
<html lang="en">
<head><title>Hello Docker</title></head>
<body>
<p>Hello {{ env "HELLO" }}! I'm service {{ .Service.Name }}.</p>
</body>
</html>
创建config

$ docker config create --template-driver golang homepage index.html.tmpl

运行docker 并设置变量

$ docker service create \
     --name hello-template \
     --env HELLO="Docker" \
     --config source=homepage,target=/usr/share/nginx/html/index.html \
     --publish published=3000,target=80 \
     nginx:alpine

最终访问容器,查看到的config文件

$ curl http://0.0.0.0:3000

<html lang="en">
  <head><title>Hello Docker</title></head>
  <body>
    <p>Hello Docker! I'm service hello-template.</p>
  </body>
</html>

总结: 可以看到使用.env , env-file 可以让环境变量的设置独立维护,并在compose文件中共享环境变量。
使用config可以向在k8s中创建configMap一样,闯将相应的配置文件,并指定mount的地址。
所以在sarming集群中我们可以利用这些元素来创建声明式的配置文件。


参考:

  1. The Compose Specification
  2. ocker ARG, ENV and .env - a Complete Guide
  3. Dockerfile reference
  4. Store configuration data using Docker Configs

推荐阅读更多精彩内容