容器启动后是可写的,所有写操作都保存在顶部的可写层中。可以通过docker commit命令将现有的容器进行提交来生成新的镜像。
具体的实现原理是通过对可写层的修改生成新的镜像,因为联合文件系统所允许的层数是有限的,建议通过 Dockerfile构建镜像。
docker commit命令用于从容器中创建一个新的镜像,其语法
docker commit [选项]容器[仓库[:标签]]
-a选项指定提交的镜像作者;-c选项表示使用Dockerfile指令来创建镜像;-p选项表示在执行提交命令commit时将容器暂停。
Dockerfile 可以定义镜像内容,其是由一系列指令和参数构成的脚本,每一条指令构建一层,因此每一条指令的内容就是描述该层应当如何构建,一个Dockerfile包含了构建镜像的完整指令。Docker 通过读取一系列Dockerfile指令自动构建镜像。
镜像的定制实际上就是定制每一层所添加的配置文件。将每一层修改、安装、构建、操作的命令都写入一个Dockerfile脚本,使用该脚本构建、定制镜像,可以解决基于容器生成镜像无法重复、构建缺乏透明性和体积偏大的问题。创建Dockerfile之后,当需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成镜像即可。
使用docker build命令,其基本语法
docker commit [选项]容器[仓库[:标签]]
- # 使用当前目录作为构建上下文的简单构建命令
- docker build .
- Sending build context to Docker daemon 6.51 MB
- ...
大多数情况下,最好将Dockerfile 和所需文件复制到一个空的目录中,再以这个目录为构建上下文进行构建。
一定要注意不要将多余的文件放到构建上下文中,特别是不要把/、/usr路径作为构建上下文,否则构建过程会相当缓慢甚至失败。
按照习惯,将Dockerfile文件直接命名为“Dockerfile”,并置于构建上下文的根位置。否则,执行镜像构建时就需要使用-f选项指定Dockerfile文件的具体位置。
- docker build -f Dockerfile 文件路径 .
- # 点号(.)表示当前路径
-
- # 通过-t(--taq)选项指定构建的新镜像的仓库名和标签
- docker build -t shykes/myapp .
-
- # 在执行build命令时添加多个-t选项(带参数)将镜像标记为多个仓库
- docker build -t shykes/myapp:1.0.2-tshykes/myapp:latest .
Docker 守护进程逐一执行Dockerfile中的指令。
Docker 将重用过程中的中间镜像(缓存),以加速构建过程。构建缓存仅会使用本地生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from 选项指定缓存。如果通过--no-cache选项禁用缓存,则将不再使用本地生成的镜像链,而是从镜像仓库中下载。
构建成功后,可以将所生成的镜像推送到Docker注册中心。
# 注释
指令 参数
- # escape=\
- 或者
- # escape=`
将转义字符设置为反引号()在Windows系统中特别有用,默认转义字符“\”是目录路径分隔符。
要提高构建性能,可通过将.dockerignore文件添加到构建上下文中来定义要排除的文件和目录。
- #注释
- */temp*
- */*/temp*
- temp?
除了以#开头的注释行,其他3 行分别表示在根的任何直接子目录中排除名称以 temp 开头的文件和目录、从根目录下两级的任何子目录中排除以 temp 开头的文件和目录、排除根目录中名称为 temp 的单字符扩展名的文件和目录。
Docker 还支持一个特殊的通配符字符串“**”,它匹配任何数量的目录(包括零)。例如,*/.go将排除所有目录中以.go结尾的所有文件,包括构建上下文的根。
FROM指令可以使用以下3种格式。
FROM <镜像> [AS<名称>]
FROM <镜像>[:<标签>][AS<名称]
FROM <镜像>[@<摘要值>][AS<名称>]
RUN指令可以使用以下两种格式。
RUN <命令>
RUN ["可执行程序","参数1","参数2"]
第2种是exec格式,不会启动shell环境。
RUN指令将在当前镜像顶部创建新的层,在其中执行所定义的命令并提交结果。提交结果产生的镜像将用于Dockerfile的下一步处理。
分层的RUN指令和生成的提交结果符合Docker的核心理念。
exec格式可以避免shell字符串转换,能够使用不包含指定shell可执行文件的基本镜像来运行RUN命令。
在shell格式中,可以使用反斜杠“\”将单个RUN指令延续到下一行
- RUN /bin/bash -c 'source $HOME/.bashrc; \
- echo $HOME'
-
- # 也可以将这两行指令并到一行中
- RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
- # 如果不使用/bin/sh,改用其他shell,则需要使用exec格式并以参数形式传入所要使用的shell
- RUN ["/bin/bash", "-c","echo hello"]
CMD指令可以使用以下3种格式。
CMD ["可执行程序","参数1","参数2"] # exec格式
CMD ["参数1","参数2"] # 提供ENTRYPOINT指令的默认参数
CMD 命令 参数 1 参数 2 # shell格式
一个Dockerfile文件中只能有一个CMD指令,如果列出多个CMD指令,则只有最后一个CMD指令有效。
CMD的主要作用是为运行中的容器提供默认值。
CMD一般是整个Dockerfile的最后一条指令,当Dockerfile完成了所有环境的安装和配置后,使用CMD指示docker run命令运行镜像时要执行的命令。
CMD指令使用shell 或exec格式设置运行镜像时要执行的命令。
- # shell格式
- FROM ubuntu
- CMD echo "This is a test."| wc -
-
- # JSON数组表示命令
- FROM ubuntu
- CMD ["/usr/bin/wc","--help"
如果希望容器每次运行同一可执行文件,则应考虑组合使用ENTRYPOINT和CMD指令
如果用户执行dockerrun命令时指定了参数,则该参数会覆盖CMD指令中的默认定义。
RUN实际执行命令并提交结果;CMD 在构建镜像时不执行任何命令,只是为镜像定义想要执行的命令。
LABEL <键>=<值> <键>=<值> <键>=<值>...
- LABEL "com.example.vendor"="ACME Incorporated"
- LABEL com.example.label-with-valie="foo"
- LABEL version="1.0"
- LABEL description=”这段文本表明 \
- 标记可以使用多行“
一个镜像可以有多个标记。要指定多个标记,Docker建议尽可能将它们合并到单个LABEL指令中。
EXPOSE <端口>[<端口>...]
EXPOSE指令通知容器在运行时监听指定的网络端口。可以指定TCP或UDP端口,默认是TCP端口。
EXPOSE不会发布该端口,只是起到声明作用。要发布端口,必须在运行容器时使用-p选项以发布一个或多个端口,或者使用-P选项发布所有暴露的端口。
ENV <键> <值> # 将单个变量设置为一个值
ENV <键>=<值> ... # 允许一次设置多个变量
ENV 指令以键值对的形式定义环境变量。该值会存在于构建镜像阶段的所有后续指令环境中,也可以在运行时被指定的环境变量替换。
COPY [--chown=<用户>:<组>]<源>…<目的>
COPY [--chown=<用户>:<组>]["<源>",...,"<目的>"]
--chown选项只能用于构建Linux容器,而不能在Windows 容器上工作。因为用户和组的所有权概念不能在Linux和Windows之间转换,所以对于路径中包含空白字符的情形,必须采用第2种格式。
COPY指令将指定源路径的文件或目录复制到容器文件系统指定的目的路径中。
COPY指令可以指定多个源路径,但文件和目录的路径将被视为相对于构建上下文的源路径。
- COPY hom* /mydir/ # 添加所有以"hom"开头的文件
- COPY hom?.txt /mydir/ #?用于替换任何单字符,如"home.txt"
目的路径可以是绝对路径,也可以是相对于工作目录
- COPY test relativeDir/ # 将“test”添加到相对路径'WORKDIR'/relativeDir/
- COPY test /absoluteDird # 将“test”添加到绝对路径/absoluteDir/
COPY指令遵守以下复制规则。
复制过来的源文件在容器中作为新文件和目录,它们都以 UID 和GID 为 0的用户和组账号的身份被创建,除非使用--chown选项明确指定用户名、组名或UID/GID组合。
还可以使用-from=
ADD [--chown=<用户>:<组>] <源>...<目的>
ADD [--chown=<用户>:<组>]["<源>",..."<目的>"]
ADD指令的可以使用URL地址指定
ADD指令的归档文件在复制过程中能够被自动解压缩。
ENTRYPOINT ["可执行文件","参数1","参数2"] # exec格式(首选)
ENTRYPOINT 命令 参数1 参数2 # shell格式
- # 将使用Nginx 镜像的默认内容启动nginx监听端口80
- docker run -i -t --rm -p 80:80 nginx
docker run <镜像>的命令行参数将附加在exec格式的ENTRYPOINT指令所定义的所有元素之后,并将覆盖使用 CMD 指令所指定的所有元素。这种方式允许参数被传递给入口点,即 docker run <镜像>-d 命令将-d 参数传递给入口点。用户可以使用 docker run--entrypoint 命令覆盖ENTRYPOINT指令。
shell格式的ENTRYPOINT 指令防止使用任何CMD或run命令行参数,其缺点是ENTRYPOINT指令将作为/bin/sh -c 的子命令启动,不传递任何其他信息。这就意味着可执行文件将不是容器的第 1个进程(PID1),并且不会接收UNIX信号,因此可执行文件将不会从docker stop<容器>命令中接收到SIGTERM(中止信号)。
在Dockerfile中只有最后一个ENTRYPOINT 指令会起作用。
VOLUME ["挂载点路径”]
VOLUME 指令创建具有指定名称的挂载点,并将其标记为从本地主机或其他容器可访问的外部挂载。挂载点路径可以是JSON数组VOLUME ["/var/log/"]或具有多个参数的纯字符串,如VOLUME/var/log 或VOLUME/war/log/var/db。
WORKDIR 工作目录路径
WORKDIR指令为Dockerfile中的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令设置工作目录,如果该目录不存在,则将被自动创建,即使它没有在任何后续的Dockerfile指令中被使用。可以在一个Dockerfile文件中多次使用WORKDIR指令。如果提供了相对路径,则该路径将相对于前面WORKDIR指令的路径。
- WORKDIR /a
- WORKDIR b
- WORKDIRc
- RUN pwd
-
- # 在此Dockerfile中,最终pwd命令的输出是/a/b/c。
USER 指令设置运行镜像时使用的用户名(或UID)和可选的用户组(或GID),Dockerfile 中的任何RUN、CMD和ENTRYPOINT指令也会使用这个被设置的身份。
ARG指令定义一个变量,用户可以在使用--build-arg
SHELL指令用于指定shell格式以覆盖默认的shell。
这种方法的基本步骤是:运行容器-修改容器-将容器保存为新的镜像。
1、以交互方式启动CentOS容器
- [root@docker-a ~]# docker run -it centos /bin/bash
- Unable to find image 'centos:latest' locally
- latest: Pulling from library/centos
- a1d0c7532777: Pull complete
- Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
- Status: Downloaded newer image for centos:latest
- [root@39314c4bf9c7 /]#
2、在容器中执行以下命令,编辑用于安装Nginx软件包的yum源定义文件
- # 配置nginx软件包的yum源定义文件,将原来的yum文件打包或全部删除(防止影响),下面使用全部删除
- [root@4d31f1ee0762 ~]# rm -f ./CentOS-Linux-*
- [root@4d31f1ee0762 ~]# vi /etc/yum.repos.d/nginx.repo
- [root@4d31f1ee0762 /]# cat /etc/yum.repos.d/nginx.repo
- [nginx-stable]
- name=nginx stable repo
- baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
- gpgcheck=0
- enabled=1
- # 安装nginx
- [root@4d31f1ee0762 ~]# yum install nginx
3、基于该容器生成新的镜像
- # 退出容器
- [root@4d31f1ee0762 ~]# exit
- exit
-
- # 查看容器ID
- [root@docker-a ~]# docker ps -a
- CONTAINER ID IMAGE ... STATUS PORTS NAMES
- 4d31f1ee0762 centos ... Exited (0) 13 seconds ago musing_engelbart
-
- # 执行docker commit命令,将该容器提交并在本地生成新的镜像
- [root@docker-a ~]# docker commit 4d31f1ee0762 centos-with-nginx
- sha256:00e543806f6a7705fc39c5b41a679dcd4da674b52636399be6a79d0bd38676c1
-
- # 查看该镜像的基本信息
- [root@docker-a ~]# docker images centos-with-nginx
- REPOSITORY TAG IMAGE ID CREATED SIZE
- centos-with-nginx latest 00e543806f6a 20 seconds ago 248MB
-
- # 通过docker history命令进一步验证镜像的构建过程和镜像的分层结构
- [root@docker-a ~]# docker history centos-with-nginx
- IMAGE CREATED CREATED BY SIZE COMMENT
- 00e543806f6a 35 seconds ago /bin/bash 16.7MB
- 5d0da3dc9764 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
2 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B 2 years ago /bin/sh -c #(nop) ADD file:805cb5e15fb6e0bb0… 231MB
4、基于新的镜像启动新容器
- # 基于新的镜像启动一个容器
- [root@docker-a ~]# docker run -it centos-with-nginx /bin/bash
- [root@670d26e56e72 /]# nginx # 启动Nginx
- [root@670d26e56e72 /]# ps -aux # 查看相关进程
- USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
- root 1 0.0 0.0 12052 3336 pts/0 Ss 05:20 0:00 /bin/bash
- root 15 0.0 0.0 27076 876 ? Ss 05:20 0:00 nginx: master process nginx
- nginx 16 0.0 0.0 45792 4472 ? S 05:20 0:00 nginx: worker process
- root 17 0.0 0.0 47588 3564 pts/0 R+ 05:20 0:00 ps -aux
5、根据需要将镜像推送到Docker Hub或其他注册中心
可前往Docker Hub网站或其他网站参考官方镜像的Dockerfile。
基本步骤是:准备构建上下文--编写Dockerfile---构建镜像。多数情况下使基于一个已有的基础镜像构建新的镜像。
下面以在CentOS镜像的基础上安装Nginx服务器软件构建新的镜像为例
1、准备构建上下文
- # 建立一个目录,用作构建上下文,并准备所需的文件
- [root@docker-a ~]# mkdir dockerfile-test && cd dockerfile-test
- [root@docker-a dockerfile-test]# touch nginx.repo
- [root@docker-a dockerfile-test]# touch Dockerfile
- [root@docker-a dockerfile-test]# vi nginx.repo
- [root@docker-a dockerfile-test]# cat nginx.repo
- [nginx-stable]
- name=nginx stable repo
- baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
- gpgcheck=0
- enabled=1
2、编写Dockerfile文件
- # 第一层镜像:基于centos
- FROM centos:latest
-
- LABEL auther xxx
-
- # 定义环境变量
- ENV pkg nginx
- # 定义工作的环境路径
- WORKDIR /etc/yum.repos.d
-
- # 第二层:配置centos的软件仓库源
- RUN rm -f ./CentOS-Linux-*
-
- # 第三层:将docker主机上的文件拷贝到镜像第三层文件系统中
- # COPY ./nginx.repo /etc/yum.repos.d/
- RUN curl -o ./CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
-
- # 第四层
- RUN yum clean all && yum makecache
-
- # 第五层:安装nginx-注意,所有操作都免交互
- RUN yum install $pkg -y
-
-
- # 第六层:修改Dockerfile,添加一个修改首页内容的信息:
- RUN echo "Hello! This is nginx server" > /usr/share/nginx/html/index.html
-
- # 声明这个镜像使用的是80端口
- EXPOSE 80
-
- # 第七层 启动nginx,容器启动时执行的命令,如果有多个CMD,在容器启动时就只执行最后一个
- CMD nginx -g "daemon off;" # 这句不会退出容器
- CMD echo "123456" # 若加上这句,容器在执行完后会自动退出
- # 或者:
- # ENTRYPOINT 跟CMD是同一个意思,如果同时用,CMD就变成ENTRYPOINT的一个参数
- # ENTRYPOINT ["ls", "-s", "-l"]
3、使用docker build命令构建镜像
- [root@docker-a dockerfile-test]# docker build -t centos-with-nginx:1.0 . # 最后的点号表示构建上下文为当前目录
- ...
- Building 0.0s (10/10) FINISHED
- ...
-
- # 查看刚构建的镜像信息
- [root@docker-a dockerfile-test]# docker images centos-with-nginx:1.0
- REPOSITORY TAG IMAGE ID CREATED SIZE
- centos-with-nginx 1.0 cac33bbb93a5 26 minutes ago 343MB
4、基于该镜像启动容器进行测试
- [root@docker-a dockerfile-test]# docker run --rm -d -p 8000:80 --name my-nginx centos-with-nginx:1.0
- 6744c52a9e0859b7542cc60be808c6dfffd73e20bbe45c3f01b3d00b3434e685
-
- # 列出正在运行的容器来验证该容器
- [root@docker-a dockerfile-test]# docker ps
- CONTAINER ID IMAGE ... STATUS PORTS NAMES
- ceb0c4687474 centos-with-... ... Up 2 seconds 0.0.0.0:8000->... my-nginx
-
- # 访问Nginx官网进行测试
- [root@docker-a dockerfile-test]# curl 127.0.0.1:8000
- ...
- # 还可以使用浏览器访问进行实际测试
5、实验完毕,停止该容器,该容器会被自动删除
- [root@docker-s dockerfile-test]# docker stop my-nginx
- my-nginx
- [root@docker-s dockerfile-test]# docker ps -a
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7.4、构建缓存测试
在构建过程中,每次生成一层新的镜像时这个进行就会被缓存。即使是后面的某个步骤构建失败,再次构建时也会从失败的那层镜像的前一条指令继续往下执行。
- # 修改Dockerfile,修改首页内容的信息:
- Hello Pleace test the nginx server
-
- [root@docker-adockerfile-test]# docker build -t centos-with-nginx:2.0 .
- ... # 此处执行使用了缓存,没有重新执行命令,而是从修改处执行
- => [7/7] RUN echo "Hello Pleace test the nginx server" > /usr/share/nginx/html/index.html 0.4s
- => exporting to image 0.1s
- => => exporting layers `0.1s
- => => writing image sha256:3eea5ebd876ec132bbb7b0c1ca7dc8cb5fc68c3f6b5
- ...
-
- # 同上、基于该镜像启动容器,访问Nginx首页,可以发现输出了Hello Pleace test the nginx server
如果不想使用这种缓存功能,可以在执行构建命令时加上--no-cache选项
docker build --no-cache -t centos-with-nginx:2.0 .