学习Docker(2017-10-2)

本节目录

  1. Dockerfile 指令详解
    1. COPY 复制文件
    2. ADD 更高级的复制文件
    3. CMD 容器启动命令
    4. ENTRYPOINT 入口点

Dockerfile 指令详解

COPY 复制文件

格式:
COPY <源路径>…<目标路径>
COPY [“<源路径>”,…”<目标路径>”]
和 RUN指令一样,也有两种格式,一种命令行,一种函数调用的方式

COPY 指令将从构建上下文目录中 <源路径>的文件 / 目录 复制到新的一层镜像内的 <目标路径> 位置。 比如:

1
COPY package.json /usr/src/app/

<源路径>可以是多个,可以使用通配符,规则要满足 Go 的 filepath.Match 规则
<目标路径>可以是容器内的绝对路径,也可以是相对于工作目录的相对路径,目标目录不需要事先创建,当目录不存在会在复制文件前创建确实目录

使用 COPY 指令,源文件的各种元数据都会保留。比如 读、写、执行权限、文件变更时间等

ADD 更高级的复制文件

ADD 指令更 COPY 的格式和性质基本一致。但是在 COPY 的基础上增加了一些功能。

  1. <源路径> 可以是一个 URL ,这种情况下, Docker 引擎会尝试去下载这个链接的文件。下载后文件权限自动设置为600,当需要修改权限,还需要额外的一层 RUN 进行权限调整,如果 URL 是一个压缩文件,需要解压缩,同样也需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令然后使用工具处理权限,解压缩,然后清理无用的文件 —-扯了那么多,就是这个功能不实用??

  2. <源路径>是一个 tar 压缩文件的话,压缩格式为 gzip , bzip2 以及 xz 的情况下, ADD 指令将会自动解压缩这个压缩文件到 <目标路径>

ADD 最适合的场景就是自动解压缩的时候,并且需要注意 ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得缓慢? 所以在 COPY 和 ADD 指令中,当文件复制使用 COPY 指令,当涉及到自动解压缩的场合使用 ADD

CMD 容器启动命令

格式:

  1. shell 格式
1
CMD <命令>
  1. exec 格式
    1
    CMD ["可执行文件","参数1","参数2"]

容器是进程,当启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是注定默认的容器的启动命令的

比如,ubuntu 镜像默认CMD 是 /bin/bash ,如果我们在 运行时 指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。就是用 cat /etc/os-release 命令替换默认命令,输出系统信息。(注意cat后面跟空格)

在指令格式上,一般推荐 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要是使用双引号 “”。如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行

1
CMD echo $HOME

实际执行

1
CMD ["sh","-c","echo $HOME"]

使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理

ENTRYPOINT 入口点

ENTRYPOINT 的目的和 CMD 一样,就是在指定容器启动程序及参数,ENTRYPOINT 在运行时也可以替代,需要通过 docker run 的参数 –entrypoint 来指定。当entrypoint指定后,CMD 的含义就发生了改变。将 CMD 的内容作为参数传给 ENTRYPOINT 指令 。实际执行时,变成了:

1
<ENTRYPOINT> "CMD"

第一次看的这种解释有点怪。

场景一:让镜像变成像命令一样使用

假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

1
2
3
4
5
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD ["curl","-s","http://ip.cn"]

还记得前一节讲的如何通过 Dockerfile 构建镜像? 将上述信息写入 Dockerfile 文件中 运行 docker build -t myip . 创建镜像。加入我们需要查询当前公网 IP ,只需要执行:

1
docker run myip

这样看来我们把镜像当做命令使用了,不过命令总有参数,那么如果我们希望显示 HTTP 头信息,就需要添加 -i 参数,直接添加对吗?

我们之前说过,跟在镜像后面的是命令,会替换原来的 CMD ,而不是添加在curl -s http://ip.cn 后面,替换命令为 -i 不是命令,他只是参数,所以报出错误找不到文件。

如何解决这个问题?

  1. 如果我们希望加入 -i 这参数,就必须重写新的完整输入

    1
    docker run myip curl -s http://ip.cn -i
  2. 使用 ENTRYPOINT 就可以解决这个问题。(如果不记得ENTRYPOINT是什么翻到上面再看一下概念)
    Dockerfile文件中

1
2
3
4
5
FROM ubuntu:14.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["curl","-s","http://ip.cn"]

docker run 设置的命令参数或者 CMD 指令的命令,都将作为ENTRYPOINT指令的命令参数,追加到ENTRYPOINT指令的命令之后。其中上述 的 -i 作为参数天交到 curl 之后。

关于 apt-get

关于 curl

问题:如何删除镜像?

1
2
3
4
5
6
docker ps -l (列出正在运行的详情)
docker stop <containerid>
docker rm <containerid>
docker rmi <imageid>
docker rmi $(docker images -q)(删除全部镜像)
docker rm $(docker ps -a -q)(删除所有的容器)

场景二:应用运行前的准备工作
启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。而这些准备工作是跟容器 CMD 无关的,无论 CMD 为神马,都需要事先进行一个预处理的工作。比如:

  1. mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。
  2. 可能希望避免使用 root 用户启动服务,从而提高安全性,而在启动服务前还需要以 root 身份执行,方便调试

这些情况,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 CMD)作为命令,在脚本最后执行。因为对 shell 脚本不熟悉 所以用照片记录脚本的使用。

shell 教程 | 菜鸟教程