前几天开始接触Docker,在了解到它的隔离性与安全性后,我准备把博客转移到Docker容器中。
为什么要自己构建镜像
Docker线上仓库提供了许多应用镜像,它们几乎涵盖了所有服务器需求。在仓库中,大到一个操作系统,小到一个MC服务器,用户都能直接拉取运行。但是,如果只使用现成镜像的话,就不能称之为学习Docker。另外在一些情况下,这些已有的镜像并不能满足需求。
我的博客程序是WordPress,它运行在Nginx+PHP7+MySQL的环境中,使用FTP进行文件管理。通过一段时间的思考,我决定将PHP与MySQL运行在两个单独的Docker容器中,而将Nginx与PureFTPd运行在同一个Docker容器中。
Nginx与PureFTPd放在一起容易配置权限,并且避免了很多坑。在现有的仓库中,我并没有找到一个合适的镜像,于是决定自己搞一个。
Dockerfile几个常用的命令
Dockerfile是构建镜像的命令合集,某种程度上类似于shell文件。Docker在构建镜像时会按照Dockerfile中的命令顺序执行。
1.FROM
FROM alpine:3.9
FROM引入了一个已经存在的镜像作为我们的基础镜像。举个栗子,如果我们打算安装QQ For Windows,那么我们必须有一个Windows环境,这个环境就是基础镜像。
Nginx与PureFTPd都可以运行在alpine Linux中,所以我使用alpineLinux作为基础镜像。可以看到alpine后面跟了一个版本号3.9,它也可以不填写。当省略版本号时,它将获取最新的(lasted)镜像版本。
2.ENV
ENV定义了一些环境变量值,可以在Dockerfile中使用。在DockerNginx官方脚本中,可以见到一行环境变量定义:
ENV NGINX_VERSION 1.15.8
在后面的编译过程中,这个环境变量被使用。
3.RUN
RUN用于指定在构建过程中要执行的命令,经常用于安装软件包。举个栗子,通过RUN可以执行SHELL命令:
# RUN <command> the commanddefault is /bin/sh -c on Linux or cmd /S /C on Windows)
RUN apk update \
&&apk add build-base \
&&echo "finish!"
# RUN ["/bin/sh","-c","apk update"]
当前RUN执行的结果会被添加到镜像中,作为下面命令的基础镜像。假如在这里写上安装QQ的命令,那么在生成镜像时该步骤就会为我们装好QQ。
RUN的两种书写方式Shell 和 Exec 一看就明白,在此不做讨论。
4.COPY && ADD
COPY nginx.conf /var/www/nginx/nginx.conf
COPY命令把一个本地文件复制到容器中的指定位置,常用来将配置文件复制到镜像中。
ADD功能类似,但是ADD的第一个参数可以是本地文件、URL等。
5.CMD
cmd命令是容器启动后运行的命令,可以看作开机启动项。他的写法和RUN类似,但它并不在生成镜像时执行,而是在容器每次“开机”时执行。
如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。
CMD 能够被 docker run 后面跟的命令行参数替换。
6.ENTRYPOINT
# ENTRYPOINT ["executable", "param1", "param2"]
# ENTRYPOINT command param1 param2 #shell格式会忽略CMD或docker run参数
容器启动后的入口,如果跟CMD同时存在,则CMD的内容会变成ENRTYPOINT的参数。
ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。
编写Dockerfile的过程
脚本流程与shell安装软件的流程基本一致,只不过这些命令需要让Docker自动调用,而不是我们去shell手写回车。我编写的文件就是将Nginx的Docker安装流程与PureFTPd的安装流程写在一起,并加入了一些配置文件而已,没什么复杂的内容。
需要注意的是,如果启动后容器中没有前台程序,那么Docker会认为”你没事儿干了”而把这个容器关闭。使用”daemon off;”参数让Nginx在前台运行,避免容器被Docker停掉。
完整的Dockerfile
后记
作为一个Docker初学者,我的理解与使用可能并不正确。我不是搞运维的专业人员,所以目前这些东西够用了。