这是我参与「第五届青训营」笔记创作活动的第18天
人生没有白走的路,每一步它都算数—考研政治老师孔昱力 定制docker镜像的方式有两种:
- 手动修改容器内容,导出新的镜像
- 基于Dockerfile自行编写指令,基于指令流程创建镜像。
在学习使用dockerfile构建镜像之前,先复习一下镜像的分层结构。
最底层是共用宿主机的Linux内核,这个我们无需指定。Rootfs层需要指定,然后依次向上手动添加不同的层依赖,最后在构建时自动添加最上层的可写层。
接下来正式学习dockerfile脚本文件指令。
Dockerfile 指令
| 指令 | 解释 |
|---|---|
| FROM | 指定基础镜像,必须为第一个指令。即基于什么东西,继续开发的 |
| MAINTAINER | 指定维护者信息,可以没有 |
| RUN | 在容器内执行一些命令 |
| ADD | 添加文件到镜像内,会自动解压。可以添加网络文件,会自动下载,但是不会自动解压 |
| COPY | 添加文件到镜像内,不会自动解压。不支持网络文件 |
| WORKDIR | 切换工作目录,可以理解为cd命令 |
| VOLUME | 详见使用dockerfile构建镜像 |
| EXPOSE | 指定容器对外开放端口 |
| CMD | 容器启动时才调用的命令 |
| ENTRYPOINT | 容器启动时才调用的命令,但是和CMD有区别。 |
| ENV | 设置环境变量 |
FROM
指定基础镜像,必须为第一个指令,即基于什么东西,继续开发的。
格式:
FROM <image>
示例:
FROM centos
MAINTAINER
指定维护者信息,可以没有
格式:
FROM <message>
示例:
MAINTAINER Anthony_4926
RUN
在容器内执行一些命令,有两种命令形式。
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
格式:
RUN <command>
示例:
RUN yum install vim -y
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'
格式:
RUN ["executable", "param1", "param2"]
示例:
RUN ["/bin/bash", "-c", "echo hello"]
ADD
添加文件到镜像内,会自动解压。可以添加网络文件,会自动下载,但是不会自动解压
格式:
ADD <src>... <dest>
示例:
ADD myfile /mydir/ # 添加宿主机myfile文件到容器内的/mydir下
ADD hom?.txt /mydir/ # 支持通配符的路径匹配
ADD https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz / # 网络文件
COPY
添加文件到镜像内,不会自动解压。不支持网络文件。其余同ADD
格式:
COPY <src>... <dest>
示例:
COPY myfile /mydir/ # 复制宿主机myfile文件到容器内的/mydir下
COPY hom?.txt /mydir/ # 支持通配符的路径匹配
WORKDIR
切换工作目录,可以理解为cd命令
格式:
WORKDIR <path>
示例:
WORKDIR /a
WORKDIR b
VOLUME
了解docker volume,需要知道docker的文件系统是如何工作的。docker镜像由多个文件系统(只读层)叠加而成,当我们启动一个容器时,docker会加载只读层镜像并在其上(即镜像栈顶部)添加一个读写层。如果已经运行的容器修改了现有的文件,那么会从读写层下面的只读层复制到读写层,该文件只读层依然存在,只是已经被读写层中该文件的复制副本所隐藏。当删除docker容器,或重新启动时,之前的修改将丢失。在docker中,只读层及在顶部的读写层组合被称为Union File System(联合文件系统)
为了能够保存(持久化)数据以及共享容器之间的数据,docker提出Volume的概念。简单来说,Volume就是目录或文件,它可以绕过默认的联合文件系统,而以正常的文件或目录的形式存在于宿主机。
理论总结:Volume可以将容器以及a8f容器产生的新数据分离开来,这样使用docker rm 容器删除容器时,不会影响相关数据。
格式:
VOLUME dir
示例:
VOLUME /myvol
挂载后的目录可使用docker inspect 容器ID查看如下结构中的"Source"值,该值就是容器中/myval目录在宿主机中的目录映射。
"Mounts": [
{
"Type": "volume",
"Name": "02df6a4484e86c45b7f019a6f89c1acd3e889741f817a3c63c1a7ef98b3bc6af",
"Source": "/var/lib/docker/volumes/02df6a4484e86c45b7f019a6f89c1acd3e889741f817a3c63c1a7ef98b3bc6af/_data",
"Destination": "/myvol",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
EXPOSE
指定容器对外开放的端口。
容器通过对外暴露这个端口,容器内的应用监听这个端口。需要注意的是,这个仅仅是指定容器对外开放哪些端口,此时外部仍然无法访问容器内部应用。需要在容器启动时,使用-p命令将容器暴露的端口与宿主机的端口进行关联。这时才能通过访问宿主机的端口,访问到容器的端口,进而访问容器内部的应用。
如果没有容器没有暴露端口,后期也可以通过-p 80:80命令的形式,暴露端口。但是无法使用-P来暴露端口。
格式:
EXPOSE <port> [<port>...]
示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
CMD
容器启动时才调用的命令。有三种形式,下边给出两种。如果docker run命令后缀有命令,其将会覆盖CMD后的命令。
格式:
CMD command param1 param2
示例:
CMD ping baidu.com > a.txt # 容器启动时,执行 ping baidu.com > a.txt
格式:
CMD ["param1","param2"] # 作为ENTRYPOINT的默认参数
ENTRYPOINT
同样是容器启动时才调用的命令,但是和命令有区别。
当ENTRYPOINT和CMD共存时,会将CMD后的命令追加到ENTRYPOINT后。docker run后的命令覆盖掉CMD命令后,同样会追加到ENTRYPOINT。即便是dockerfile中没有写CMD,docker run后的命令同样会覆盖CMD命令,然后追加到ENTRYPOINT后。
ENV
设置环境变量。有两种格式,第一种格式一次只能设置一个环境变量;第二种一次可设置多个。
格式:
ENV <key> <value>
示例:
ENV GOPATH /gopath
可以设置多个变量,每个变量为一个"="的键值对,如果中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
格式:
ENV key=value ...
示例:
ENV EMAIL="xxx@163.com" NAME=Anthony_4926
ONBUILD
用于设置镜像触发器。当当前的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被动触发。
格式:
ONBUILD [INSTRUCTION]
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
dockerfile小战一下
需求,通过dockerfile,构建nginx镜像,且运行容器后,生成的页面,是"学习docker”
# 1. 创建Dockerfile文件,注意文件名,必须是这个。文件内写入如下内容
FROM nginx
RUN echo '<meta charset=utf8> 学习docker' > /usr/share/nginx/html/index.html
# 2. 构建镜像
docker build .
文件中写了两行指令,构建时候也就分了两步。最后输出Successfully built c67ce8a9a109
然后我们查看本地,看看是不是已经有了ID为c67ce8a9a109的镜像。下图中看到了该镜像,但是它没有名字,没有版本
我们现在用docker tag命令改一下该ID镜像的名字
docker tag c67ce8a9a109 my_nginx
然后运行该镜像,再访问一下这个nginx
docker run -d -p 80:80 my_nginx
dockerfile再战一下
源码编译制作nginx镜像。
# This my second nginx Dockerfile
# Version 1.0
# Base images 基础镜像
FROM centos
#MAINTAINER 维护者信息
MAINTAINER bertwu
#ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
#ADD 文件放在当前目录下,拷过去会自动解压
ADD nginx-1.8.0.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/
#RUN 执行以下命令
RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm
RUN yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all
RUN useradd -s /sbin/nologin -M www
#WORKDIR 相当于cd
WORKDIR /usr/local/nginx-1.8.0
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install
RUN echo "daemon off;" >> /etc/nginx.conf
#EXPOSE 映射端口
EXPOSE 80
#CMD 运行以下命令
CMD ["nginx"]