一、docker commit 定制镜像

不要使用 docker commit 定制镜像,定制镜像应该使用 Dockerfile 来完成

在之前的例子中,我们所使用的都是来自于 Docker Hub 的镜像。直接使用这些镜像是可以满足一定的需求,但有时候我们就需要定制自己的镜像。

$ docker run --name webserver -d -p 80:80 nginx

$ docker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit

// 查看变动
$ docker diff webserver

现在我们定制好了变化,我们希望能将其保存下来形成镜像。

而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

$ docker commit webserver lihaixing/docker-nginx:v1
sha256:405bcabb583a2f06fece0c69543eac1fa2e94d84b2c2bf1858ff00cb74b5aec4

// 镜像还在本地,看一下
$ docker image ls lihaixing/docker-nginx

$ docker run --name webserver2 -d -p 81:80 lihaixing/docker-nginx:v1

此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 黑箱镜像

就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。这种黑箱镜像的维护工作是非常痛苦的。

二、Dockerfile定制镜像

从刚才的 docker commit 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。

如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

Dockerfile 指令介绍 .....

1. FROM 指定基础镜像

指定 基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令,如果不依赖任何镜像

// 这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
...

在 Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;

也有一些方便开发、构建、运行各种语言应用的镜像,如 node、openjdk、python、ruby、golang 等。

2. run 命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一

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

    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

    3. CMD 容器启动命令

  • shell 格式:CMD <命令>
  • exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
  • 参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。

4. ENTRYPOINT

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param

5. ENV 设置环境变量

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

ENV NODE_VERSION 7.2.0
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
  && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
  && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
  && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs

6. ARG 构建参数

构建参数和 ENV 的效果一样,都是设置环境变量。

所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。

Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。

灵活的使用 ARG 指令,能够在不修改 Dockerfile 的情况下,构建出不同的镜像。

ARG 指令有生效范围,如果在 FROM 指令之前指定,那么只能用于 FROM 指令中。


ARG <参数名>[=<默认值>]

ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine

RUN set -x ; echo ${DOCKER_USERNAME} // 无效

3. docker build 输出镜像

docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

docker build -t mynginx . 

docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world

docker build http://server/context.tar.gz

docker build - < Dockerfile
// 后者
cat Dockerfile | docker build -

docker build - < context.tar.gz

lihaixing
463 声望719 粉丝

前端就爱瞎折腾