Docker 学习笔记 - Day2

  LeungJZ

Dockerfile

虽然我们确实能快速搭建了一个 lnmp 的环境,但是每次切换宿主机时,都要执行多个步骤:

  1. 启动 mysql 服务器
  2. 启动 php 服务器
  3. 进入 php 容器修改配置
  4. 启动 nginx 服务器
    怎么说都还是麻烦。如果能一键启动的话,那就更好了。

Dockerfile 就可以解决这样的问题。

要使用 Dockerfile ,先要了解一下这到底是个什么东西。

什么是 Dockerfile?

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

Dockerfile是一个文本文档,其中包含用户可以在命令行上调用以组合镜像的所有命令。使用docker构建用户可以创建一个自动构建,连续执行几个命令行指令。

Dockerfile 常用命令

FROM

语法: FROM <image> [AS <name>] or FROM <image>[:<tag>][AS ] or FROM <image>[@<digest>][AS ]

注意:FROM 命令必须是 Dockerfile 的首个命令,但可以在一个 Dockerfile 中出现多次。

说明: 该命令定义了使用哪个基础镜像启动构建流程。基础镜像可以为任意镜像。

MAINTAINER

语法: MAINTAINER <NAME>

说明: 设置生成镜像的作者名字。

RUN

语法:

  • RUN <command> (shell形式,/bin/sh -c 或者是 Windows 的 cmd /S /C )
  • RUN ["<executable>", "<param1>", "<param2>"] (exec 形式)

说明:

RUN命令将在当前image中执行任意合法命令并提交执行结果。

RUN 指令缓存不会在下个命令执行时自动失效。

RUN命令用于创建镜像(在之前commit的层之上形成新的层)。

ADD

语法:

  • ADD <src> [<src> ...] <dest>
  • ADD ["<src>", ... "<dest>"] (路径中包含空格的必须使用该形式)

注意:ADD只有在build镜像的时候运行一次,后面运行container的时候不会再重新加载了。

说明: 从 src 拷贝新的文件或文件夹或远程文件 URL 到镜像的文件系统的路径 dest 中。

  • 所有拷贝到 container 中的文件和文件夹权限为 0755 , uid 和 gid 为0。
  • 如果文件是可识别的压缩格式,则 docker 会自动解压缩。
  • 如果 src 为本地文件或文件夹,则必须相对于 docker build <PATH> 中的 <PATH> 目录中。
  • dest 是一个绝对路径,或者相对于 WORKDIR ,如果 <dest> 不存在则会自动创建。

COPY

  • COPY <src> [<src> ...] <dest>
  • COPY ["<src>", ... "<dest>"] (路径中包含空格的必须使用该形式)

COPY 的语法和使用说明和 ADD 基本一致,但是在遇见压缩包时, COPY 很直接的拷贝到容器当中,而不会解压。 Docker 团队建议大多数情况下使用 COPY。

CMD

语法:

  • CMD ["<executable>","<param1>","<param2>"] exec 形式,比较好的形式。
  • CMD ["<param1>","<param2>"] 作为 ENTRYPOINT 的默认参数。
  • CMD <command> <param1> <param2> shell 形式。

注意:Dockerfile 中只能有一个 CMD 指令,如果有多个则只有最后一个生效。

说明:

CMD 命令最主要的目的是提供一个默认的执行容器。

和RUN命令相似,CMD可以用于执行特定的命令。和RUN不同的是,这些命令不是在镜像构建的过程中执行的,而是在用镜像构建容器后被调用。

如果 CMD 用于为 ENTRYPOINT 提供默认参数时, CMD 和 ENTRYPOINT 指令应该规定为 JSON 数组格式。

docker run command 的命令匹配到 CMD command 时,会替换CMD执行的命令,即 docker run命令如果指定了参数会把CMD里的参数覆盖: (这里说明一下,如:docker run -it ubuntu /bin/bash 命令的参数是指/bin/bash 而非 -it ,-it只是docker 的参数,而不是容器的参数,以下所说参数均如此。)

ENTERYPOINT

语法:

  • ENTRYPOINT ["<executable>", "<param1>", "<param2>"] (exec 形式,推荐)
  • ENTRYPOINT <command> <param1> <param2> (shell 形式)

注意:Dockerfile 中只能有一个 ENTRYPOINT 指令,如果有多个则只有最后一个生效。

说明:

配置容器启动后执行的命令,并且参数不可被 docker run 提供的参数覆盖。

如果结合 CMD 使用时,要使用 exec 的形式,即:

ENTRYPOINT ["echo"]
CMD ["HELLO_WORLD"]

这是 CMD 命令的内容则不是一个完整的指令,而是为 ENTRYPOINT 命令提供默认参数,该参数可以被 docker run 后的参数所覆盖。

EXPOSE

语法: EXPOSE <port> [<port> ...]

说明: 通知 Docker 在运行时监听指定的端口。主机上要用还得在启动container时,做host-container的端口映射。

ENV

语法:

  • ENV <key> <value>
  • ENV <key>=<value> [<key>=<value> ...]

说明:

该指令将环境变量的 key 设置为 value ,这些值都可以在 Dockerfile 后续的命令中用上,并可以被修改。

VOLUME

语法:

  • VOLUME ["<path>", ...]
  • VOLUME <path> [<path> ...]

说明: 创建一个有具体名称的挂载点,并将其标记为从本机或者其他容器外部的挂载卷。

USER

语法: USER <username | UID>

说明: 指定某个用户运行容器。

WORKDIR

语法: WORKDIR </path/to/workdir>

说明: 设定指令 CMD , RUN , ENTRYPOINT , COPYADD 的工作目录。可以在一个 Dockerfile 中出现多次,如果提供了一个相对路径,那么它将相对于前一个 WORKDIR 指令的路径。

ARG

语法: ARG <name>[=default value>]

说明:

  • 定义一个变量,用户可以在建立的时候通过 docker build 命令使用 —build-arg <varname>=<value> 指定。
  • 多条 ARG 指令可以定义指定的变量,但是在构建成功后取消。
  • 相同名字的环境变量会被 ENV 指令的覆盖。
  • Docker 有一组预定义的 ARG 变量,可以在 Dockerfile 中使用而不需要 ARG 指令。

ONBUILD

语法: ONBUILD <Dockerfile INSTRUCTION>

说明:

  • 当镜像作为另一个镜像构建的基础时,添加一个被延时执行的触发指令。就类似于在子镜像的 Dockerfile 的 FROM 指令下插入了一条命令。
  • ONBUILD 指定的命令在构建镜像时不执行,只在它的子镜像中执行。
  • 任何构建指令都可以被注册为触发器,但 ONBUILD 指令不一定触发 FROM , MAINTAINER 或者 ONBUILD 指令。

Dockerfile 构建镜像

现在将先前搭建 lnmp 的具体步骤一步步写进 Dockerfile 中。

MySQL

FROM mysql

MAINTAINER LeungJZ

ENV MYSQL_ROOT_PASSWORD 123456

EXPOSE 3306

没啥太多的配置,就是简单的配置ROOT密码和暴露3306端口。

构建镜像:

leung@ubuntu:~/docker$ docker build ./mysql/ -t test-mysql
Sending build context to Docker daemon   2.56kB
Step 1/4 : FROM mysql
 ---> 141d24fea983
Step 2/4 : MAINTAINER LeungJZ
 ---> Using cache
 ---> aa6665261e02
Step 3/4 : ENV MYSQL_ROOT_PASSWORD 123456
 ---> Using cache
 ---> 4d482f48a09d
Step 4/4 : EXPOSE 3306
 ---> Using cache
 ---> 385bb10cd49a
Successfully built 385bb10cd49a
Successfully tagged test-mysql:latest

瞬间构建成功。再启动一个容器 docker run --name mysql -p 3306:3306 -d test-mysql ,和以前基本一致。

Php7.1-fpm

FROM php:7.1-fpm

MAINTAINER LeungJZ

COPY php.ini /usr/local/etc/php/conf.d/php.ini

RUN /usr/local/bin/docker-php-ext-install pdo_mysql

EXPOSE 9000

CMD ["php-fpm"]

指定了 php-fpm 的版本为 7.1,并覆盖了我们自定义的 php.ini ,并且安装 pdo_mysql 拓展。

leung@ubuntu:~/docker$ docker build ./php/ -t test-php7
Sending build context to Docker daemon  70.14kB
Step 1/6 : FROM php:7.1-fpm
 ---> 9b44e8b4c8b6
Step 2/6 : MAINTAINER LeungJZ
 ---> Using cache
 ---> 1038ce686af9
Step 3/6 : COPY php.ini /usr/local/etc/php/conf.d/php.ini
 ---> Using cache
 ---> cdbdece75628
Step 4/6 : RUN /usr/local/bin/docker-php-ext-install pdo_mysql
 ---> Using cache
 ---> 8a8cbd9c8ac9
Step 5/6 : EXPOSE 9000
 ---> Using cache
 ---> 1fd3881b6769
Step 6/6 : CMD php-fpm
 ---> Using cache
 ---> 00d178a9351b
Successfully built 00d178a9351b
Successfully tagged test-php7:latest

因为之前构建过一次,所以全都是从缓存中获取,直接就构建完成。

启动: docker run --name php7-fpm --link mysql:mysql -p 9000:9000 -v $PWD/project:/usr/share/nginx/html -d test-php7

nginx

FROM nginx:1.13

MAINTAINER LeungJZ

COPY default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

ENTRYPOINT ["nginx", "-g", "daemon off;"]

配置就是基础镜像为 1.13 版本的 nginx ,覆盖自定义的 default.conf 配置文件,暴露80端口,并且不以守护进程模式启动。

leung@ubuntu:~/docker$ docker build ./nginx/ -t test-nginx
Sending build context to Docker daemon  4.608kB
Step 1/5 : FROM nginx:1.13
 ---> da5939581ac8
Step 2/5 : MAINTAINER LeungJZ
 ---> Using cache
 ---> 3076338acc32
Step 3/5 : COPY default.conf /etc/nginx/conf.d/default.conf
 ---> Using cache
 ---> bb7ab780e5d4
Step 4/5 : EXPOSE 80
 ---> Using cache
 ---> 38dda9a94ae6
Step 5/5 : ENTRYPOINT nginx -g daemon off;
 ---> Using cache
 ---> 7864bf277ac0
Successfully built 7864bf277ac0
Successfully tagged test-nginx:latest

启动: docker run --name nginx --link php7-fpm:php7-fpm -v $PWD/project:/usr/share/nginx/html -p 80:80 -d test-nginx

访问 http://10.211.55.9/i.php 依旧可以看到熟悉的 phpinfo 的界面。

小结

Dockerfile 可以帮我们减轻了很多配置方面的麻烦,但是启动时,依旧需要绑定很多变量,如挂载卷,映射端口等。虽然只需 run 一次,但是这也是麻烦的。

当然,肯定有解决的办法,就是接下来的 docker-compose 。