docker是目前使用很广泛的容器技术,它不同于虚拟机,docker使用了Linux内核提供的命名空间和cgroups等技术,利用沙箱机制,将不同容器的运行环境进行了隔离,保证了软件系统在部署上的封装和安全。
docker在提供部署安全性的同时,也在改变着我们开发的方式。在以前的系统中,为了方便对系统的配置进行修改,我们常常将系统可配置的参数放置在配置文件或者数据库中。但是,有些配置,比如数据库的主机,用户名及密码等信息,由于每个开发者开发环境搭建不同,可能需要每次检出代码后对配置文件进行必要的设置。即便开发团队统一了开发使用的环境,如果我们需要将代码交给其他公司使用或者进行开源时,还是需要隐藏自己代码中的一些配置信息,避免一些不安全的事件发生。
使用docker之后,我们可以使用docker的环境变量来处理这个问题,而结合上k8s后,我们还可以使用ConfigMap进一步对配置进行封装。例如MySQL的docker就引出了多个环境变量,我们在创建容器的时候按照需求对环境变量进行配置,就可以对数据库的root用户密码以及端口等多个信息进行配置,真正将系统的配置延迟到部署时进行。
docker对环境变量的定义和初始化在Dockerfile中进行定义,也就是说,在制作docker镜像时进行定义,我编写了一个简单的Dockerfile,内容如下:
FROM alpine:latest
ENV MY_PROGRAM_NAME ""
ENV MY_PROGRAM_VERSION ""
WORKDIR /
COPY main /
ENTRYPOINT [ "/main" ]
我们基于alpine的最新版本镜像进行新镜像的配置。定义了两个环境变量,初始值为空,一个是MY_PROGRAM_NAME,代表程序名称,一个叫MY_PROGRAM_VERSION,代表程序版本。之后设定了工作目录为根目录,将一个main可执行文件复制到镜像的根目录,将镜像的Entrypoint设置为了main。main是用golang编写的一个简单程序:
package main
import (
"fmt"
"os"
)
func main() {
name := os.Getenv("MY_PROGRAM_NAME")
version := os.Getenv("MY_PROGRAM_VERSION")
fmt.Println("Name: ", name)
fmt.Println("Version: ", version)
}
使用go build编译之后,将生成的main可执行文件复制到Dockerfile所在的目录下即可。docker添加的环境变量会被写入镜像的环境变量中,所以我们的Go程序只需要读取系统的环境变量就可以取到docker运行容器时动态传入的环境变量。接下来进入Dockerfile所在目录,运行下面命令制作镜像:
docker build -t yjp/env .
我们制作了一个名为yjp/env的镜像。下面我们来试一下,运行如下命令启动容器:
docker run --rm yjp/env
由于我们没有传递环境变量,容器运行的输出为:
Name:
Version:
然后我们再次运行,这次通过docker run的-e选项传递环境变量:
docker run --rm -e MY_PROGRAM_NAME main -e MY_PROGRAM_VERSION 1.0 yjp/env
这次容器的运行结果为:
Name: main
Version: 1.0
这样,就实现了通过运行容器时动态传入环境变量并在程序中使用环境变量。使用这种方式,我们可以为不同的容器使用不同的环境变量,对我们的程序进行配置,而不必担心暴露系统的配置信息。