Ryan Wang's Blog

Docker 环境变量的运用

2018.12.29

前段时间发布了 Halo 的 Docker Compose 部署教程,该方法可以自动完成 Nginx 配置以及 SSL 证书的配置,实现了 Halo 最便捷的部署方式,可最近确发现了一个问题,就是部署的时候不太方便修改内置数据库的用户名和密码,使用的是默认的admin和123456,当初考虑到安全问题,就禁用了 h2数据库的在线控制台。可有时候还是需要修改一些数据啥的,虽然使用频率并不高,但是非要使用的时候还是挺头疼的,于是乎就搜索了相关资料,并完美的解决了修改用户名和密码的问题,所以水文记录一下。

原理

Dockerfile 在构建镜像的时候其实是提供了一个参数可供配置的,那就是 ENV,在 Dockerfile 里面配置之后是可以在构建和创建容器的时候调用的,正好,Spring Boot 打包之后的 Jar 包,在启动的时候也是可以加参数以替代默认配置文件里面的配置项的,所以利用这个特性,就可以完成我们想要的效果啦。

Dockerfile 解析

FROM maven:3
LABEL maintainer="Ryan Wang<i@ryanc.cc>"

WORKDIR /opt/halo
ADD . /tmp
ENV TZ=Asia/Shanghai \
DB_USER="admin" \
DB_PASSWORD="123456"

RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone

RUN cd /tmp && mvn package -Pci && mv target/dist/halo/* /opt/halo/ \
    && rm -rf /tmp/* && rm -rf ~/.m2

EXPOSE 8090

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/opt/halo/halo-latest.jar","--spring.datasource.username=${DB_USER}","--spring.datasource.password=${DB_PASSWORD}"]

如上代码,我们可以看到,我们在 ENV 参数里面一共配置了三个属性, TZ DB_USER DB_PASSWORD ,这三个分别是:时区,数据库用户名和数据库密码。时区我们很好理解,就是指定一下容器内的时区而已,保证容器和宿主机的时区一致,防止出现时区不同步的问题。

重点我们看到 DB_USERDB_PASSWORD ,这两个都是配置有默认值的,如果说在创建容器不指定这两个环境变量的话就会使用这两个默认值。环境变量写好了,我们在看到最后一行 ENTRYPOINT 这个是在创建容器之后,容器内启动应用所执行的命令,可以用脚本代替(写上脚本文件名就行),也可以直接写命令,在这里我们组合起来就是:

java -jar /opt/halo/halo-latest.jar --spring.datasource.username=${DB_USER} --spring.datasource.password=${DB_PASSWORD}

又因为我们在上面指定了 DB_USERDB_PASSWORD 这两个环境变量,所以实际执行的命令就是

java -jar /opt/halo/halo-latest.jar --spring.datasource.username=admin --spring.datasource.password=admin

所以这样启动之后,数据库用户名和密码就被应用获取到,然后创建DataSource了。如果大家还不理解为啥是 spring.datasource.usernamespring.datasource.password 这两个参数的话,看看 Spring Boot 的配置文件就知道了。

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    # H2database 配置
    driver-class-name: org.h2.Driver
    url: jdbc:h2:file:~/halo/halo
    username: admin
    password: 123456

创建容器指定环境变量

上面我们讲解到在 Dockerfile 里面指定环境变量,其实我们在使用 Docker 镜像创建容器的时候也是可以手动指定环境变量的值的,不然从 Docker Hub 拉取下来的镜像怎么修改配置丫。

在创建容器的时候,是有个参数的,那就是 -e ,这个e应该就代表 ENV 吧,在 -e 后面就可以指定环境变量名和值啦,具体写法:-e 环境变量名=值 我们只需要在创建容器的时候加上这个参数就行啦,完整命令如下(假设创建 Halo 的应用容器):

docker run -d --name halo -p 8090:8090 -v ~/halo:/root/halo -e DB_USER=root -e DB_PASSWORD=67890 ruibaby/halo

这样执行之后,用户名就变成了 root ,密码就变成了 67890。这样就可以愉快的使用 H2数据库控制台啦。

Docker Compose 指定环境变量

Halo 也提供了 Docker Compose 的创建文件,如今我们支持了手动传参修改数据库用户名和密码后,在 Docker Compose 文件里我们也是可以自己配置的,如下:

  halo:
    restart: always
    image: ruibaby/halo
    container_name: halo
    ports:
      - 8090:8090
    environment:
      - VIRTUAL_PORT=8090
      - VIRTUAL_HOST=localhost  # 监听的地址(务必修改)
      - LETSENCRYPT_HOST=localhost # 证书的域名 (务必修改)
      - LETSENCRYPT_EMAIL=i@example.com # 证书所有者的邮箱,快过期时会提醒(务必修改)
      - DB_USER=admin # h2数据库用户名,自定义(务必修改)
      - DB_PASSWORD=123456 # h2数据库密码,自定义(务必修改)
    volumes:
      - ~/halo:/root/halo

其中在 environment 节点就是所有需要的环境变量,按需修改即可,修改完成之后,执行 docker-compose up -d 就可以完美部署好应用了。

小疑问

有人知道如果在 Docker Hub 上的镜像有更新,我们需要更新自己已创建的容器咋整呢?就是说根据新的 Docker 镜像更新容器内的内容以平滑升级应用。这个问题已经困惑我好久了,搜了很多相关资料都没解惑,不可能手动销毁容器然后用新镜像创建容器吧?如果有大佬知道如果整,欢迎在下面留言。