Docker 使用dockerfile构建镜像(三) | 青训营笔记

109 阅读7分钟

这是我参与「第五届青训营」笔记创作活动的第18天

人生没有白走的路,每一步它都算数—考研政治老师孔昱力 定制docker镜像的方式有两种:

  • 手动修改容器内容,导出新的镜像
  • 基于Dockerfile自行编写指令,基于指令流程创建镜像。

在学习使用dockerfile构建镜像之前,先复习一下镜像的分层结构。

流程图 (33).jpg

最底层是共用宿主机的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

同样是容器启动时才调用的命令,但是和命令有区别。

ENTRYPOINTCMD共存时,会将CMD后的命令追加到ENTRYPOINT后。docker run后的命令覆盖掉CMD命令后,同样会追加到ENTRYPOINT。即便是dockerfile中没有写CMDdocker 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"]

参考&感谢