三、Docker核心必备之自定义镜像实战
3.1 简介
Docker 自定义镜像是指用户可以基于现有的 Docker 镜像,添加自己的应用程序、配置文件等内容,从而构建出符合自己需求的镜像。
Docker 自定义镜像可以帮助用户快速构建自己所需要的环境,简化了部署和管理的过程,同时也提高了应用程序的可移植性。
构建镜像的两种方法:
- 继续dockerfile制作镜像(主流)
- 基于DockerCommit 制作镜像
3.2 Commit构建自定义镜像
使用 docker commit 命令将当前容器的状态保存为一个新的镜像。该命令可以将容器中的所有修改(包括新增、删除、修改文件等)保存到一个新的镜像中,并为该镜像分配一个唯一的 ID。
docker commit 命令的基本语法如下:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
其中,CONTAINER 是要保存为镜像的容器的名称或 ID;REPOSITORY 是新镜像的仓库名称;TAG 是新镜像的标签。
例如,以下命令将名称为 mycontainer 的容器保存为名为 myimage 的镜像:
docker commit mycontainer myimage
使用 docker run 命令启动新的容器时,可以使用新的镜像名称和标签来指定使用自定义镜像:
docker run -it myimage bash
docker commit 的常见参数 OPTIONS
-a:标注作者
-m:说明注释
例如:
docker commit -a "01" -m "addFile" mycontainer myimage:V1
使用 docker commit 命令构建镜像的方法相对简单,但是这种方式比较适合用于快速的测试和试验场景。在实际生产环境中,建议使用 Dockerfile 文件来构建自定义镜像,以确保镜像的可重复性和可管理性。
3.3 Dockerfile构建镜像实战
Dockerfile 是一种用于定义 Docker 镜像构建过程的文件格式,可以将构建镜像的各个步骤编写到 Dockerfile 中,并使用 Docker 命令进行构建。通过 Dockerfile 构建镜像的好处是可以将构建过程进行版本控制,以便于追踪镜像的变化,也可以自动化构建镜像的过程,从而提高构建镜像的效率。
Dockerfile 由一系列指令组成,每个指令代表着构建过程中的一个步骤。以下是一个基本的 Dockerfile 示例:
# 基础镜像
FROM ubuntu:18.04
# 作者信息
MAINTAINER My Name <myemail@example.com>
# 安装必要的软件包
RUN apt-get update && \
apt-get install -y software-properties-common && \
add-apt-repository -y ppa:openjdk-r/ppa && \
apt-get update && \
apt-get install -y openjdk-8-jdk
# 设置工作目录
WORKDIR /app
# 将本地文件复制到镜像中
COPY . /app
# 暴露端口
EXPOSE 8080
# 容器启动命令
CMD ["java", "-jar", "myapp.jar"]
上面的 Dockerfile 中,首先通过 FROM 指令指定了一个基础镜像,然后通过 RUN 指令安装了必要的软件包,通过 WORKDIR 指令设置了工作目录,通过 COPY 指令将本地文件复制到镜像中,通过 EXPOSE 指令暴露了容器的端口,最后通过 CMD 指令设置了容器启动命令。
在构建镜像时,只需要执行如下命令:
docker build -t myimage:1.0 .
其中,-t 参数指定了构建的镜像名称和版本号,. 表示 Dockerfile 文件所在的当前目录。
3.4 Docker容器镜像分层结构
Docker 容器是在 Docker 镜像的基础上创建的运行实例。每个 Docker 镜像都可以由多个层(Layer)构成,而容器层也是 Docker 镜像的一部分,是在镜像层之上的一个可写层。
当我们启动一个 Docker 容器时,Docker 会创建一个可写的容器层,这个层上的任何更改都只会影响这个容器实例,而不会影响原始的 Docker 镜像。这个容器层可以看做是一个用于保存容器运行状态的磁盘映像,包含了该容器运行时所做的任何更改、新增或删除的文件和目录。
容器层和镜像层是相互关联的,一个容器层基于一个镜像层,而一个镜像层也可以作为多个容器层的基础。因为 Docker 镜像的多层结构,每个容器都可以在其基础镜像的基础上添加自己的可写层,从而实现镜像的共享和压缩的效果。这也是 Docker 容器具有轻量级、易于共享和部署的优势之一。
3.5 DockerFile常用指令
Dockerfile 是一种文本文件,它包含了构建 Docker 镜像所需要的所有指令和命令,可以通过 Docker build 命令自动构建一个 Docker 镜像。下面是常用的 Dockerfile 指令:
- FROM:指定基础镜像,例如 FROM ubuntu:20.04,表示从 Ubuntu 20.04 镜像开始构建新镜像。
- RUN:在容器中运行命令,例如 RUN apt-get update && apt-get install -y nginx,表示在容器中安装 Nginx。
- CMD:设置容器启动时要运行的命令,例如 CMD [“nginx”, “-g”, “daemon off;”],表示容器启动时运行 Nginx,并设置 Nginx 的守护进程模式。
- ENTRYPOINT:指定容器启动时要运行的命令或脚本。它允许您指定容器启动时要运行的命令或脚本,并且这个命令或脚本不能被 docker run 命令中的其他参数所覆盖。这使得 ENTRYPOINT 指令特别适合用于构建可重用的容器镜像。
- COPY:将本地文件复制到容器中,例如 COPY index.html /usr/share/nginx/html/,表示将本地 index.html 文件复制到容器的 /usr/share/nginx/html/ 目录下。
- WORKDIR:设置工作目录,例如 WORKDIR /app,表示将容器的工作目录设置为 /app 目录。
- EXPOSE:暴露容器的端口,例如 EXPOSE 80,表示将容器的 80 端口暴露出来。
- ENV:设置环境变量,例如 ENV TZ=Asia/Shanghai,表示设置容器的时区为上海。
- ARG:定义构建时的参数,例如 ARG VERSION=latest,表示在构建镜像时可以使用 --build-arg VERSION=2.0 来指定版本号。
- LABEL:为镜像设置元数据,例如 LABEL maintainer=“John Doe”,表示设置镜像的维护者为 John Doe。
- ADD:将本地文件或远程文件添加到容器中,和 COPY 类似,但 ADD 支持更多的特性,例如自动解压缩。
- MAINTAINER:指定 Dockerfile 的作者信息。然而,从 Docker 1.13 版本开始,推荐使用 LABEL 代替 MAINTAINER 指令来指定作者信息,因为 MAINTAINER 指令已经被废弃了。
这些指令可以根据需要灵活组合使用,以构建出符合自己需求的 Docker 镜像。
RUN、ENTRYPOINT 和 CMD 指令区别
在 Dockerfile 中,RUN、ENTRYPOINT 和 CMD 是三个常用的指令。它们之间的区别如下:
- RUN:用于在容器内部执行命令并提交结果,以便后续指令使用。RUN 指令执行的命令会在构建镜像过程中运行,生成新的镜像层。
- ENTRYPOINT:指定容器启动时要运行的命令或脚本。ENTRYPOINT 指令在运行容器时被执行。
- CMD:为容器指定默认的执行命令。如果在运行容器时没有指定其他命令,那么默认情况下会执行 CMD 指令所指定的命令。
RUN 用于在构建过程中执行命令生成新的镜像层,而 ENTRYPOINT 和 CMD 则是在容器运行时指定容器启动时要运行的命令或脚本。如果在 docker run 命令中指定了要运行的命令,则它会覆盖 ENTRYPOINT 和 CMD 指令指定的命令或脚本。
例如:
我们有一个 Dockerfile 文件,内容如下:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl
CMD ["echo", "Hello, world!"]
ENTRYPOINT ["curl", "-s", "https://ipinfo.io/ip"]
在这个 Dockerfile 中,我们定义了三个指令:FROM、RUN 和 CMD。在构建镜像时,这些指令将按照顺序依次执行。其中,RUN 指令将安装 curl 工具,CMD 指令将打印 “Hello, world!”,ENTRYPOINT 指令将获取当前容器的 IP 地址。
如果我们运行以下命令构建镜像:
docker build -t myimage .
那么在构建过程中,Docker 将依次执行这些指令,并为每个指令创建一个新的镜像层。最终生成的镜像将包含三个层:基础镜像层、安装 curl 工具的层和打印 “Hello, world!” 的层。
当我们运行容器时,如果不指定命令,Docker 将使用 CMD 指令定义的默认命令。因此,如果我们运行以下命令启动容器:
docker run myimage
那么容器将输出 “Hello, world!”。如果我们在运行容器时指定了命令,那么这个命令将覆盖 CMD 指令定义的默认命令。例如,如果我们运行以下命令启动容器:
docker run myimage curl -s https://www.baidu.com
那么容器将输出百度首页的 HTML 代码,而不是 “Hello, world!”。因此,CMD 和 ENTRYPOINT 指令定义的命令会被覆盖。
RUN作用在镜像层,CMD 或 ENTRYPOINT 作用在容器层,容器层会覆盖,因此如果您在 Dockerfile 中定义多个 CMD 或 ENTRYPOINT 指令,只有最后一个指令会生效;定义多个RUN指令,每个RUN指令都会生效。
例如,以下 Dockerfile 文件:
FROM ubuntu
RUN apt-get update && apt-get install -y curl
CMD ["curl", "https://www.google.com"]
CMD ["echo", "Hello, World!"]
该文件中先执行 RUN 指令安装了 curl,然后指定了两个 CMD。在启动容器时,会执行最后一个 CMD,即输出 Hello, World!。
ENTRYPOINT指令和CMD指令都是用来指定容器启动时要执行的命令或脚本。如果同时指定了ENTRYPOINT和CMD,那么CMD指定的命令或脚本会作为ENTRYPOINT命令或脚本的默认参数。
举个例子,如果在Dockerfile中指定了以下指令:
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]
那么当我们运行容器时,命令为:
$ docker run myimage
Hello world
这里,ENTRYPOINT指令指定了echo命令作为容器启动时要执行的命令或脚本,并将"Hello"作为参数传递给echo命令;而CMD指令指定了"world"作为默认参数,如果我们在运行容器时指定了其他参数,那么CMD指定的默认参数就会被覆盖。
总结:
RUN 指令用于在镜像构建过程中执行命令,并创建一个新的镜像层;ENTRYPOINT 指令指定的命令或脚本会在容器启动时执行,并作为容器的主要命令或应用程序;CMD 指令可以为 Docker 镜像设置默认的应用程序参数。
使用 RUN 指令可以在镜像构建期间执行命令,创建新的镜像层,并在这些层上进行其他操作。而 ENTRYPOINT 指令指定的命令或脚本会在容器启动时执行,作为容器的主要命令或应用程序。它可以让容器以应用程序的方式运行,而不仅仅是一个可以执行一些命令的临时容器。
CMD 指令可以为 Docker 镜像设置默认的应用程序参数。它可以被 docker run 命令行中传递的参数所覆盖,允许镜像的使用者在启动容器时更改应用程序的默认行为。
在 Dockerfile 中,RUN 指令可以出现多次,每次执行命令会在当前镜像层上创建一个新的镜像层;ENTRYPOINT 指令可以出现多次,但是每次出现都会覆盖之前的指令,它指定的命令或脚本会成为容器的主要命令或应用程序;CMD 指令可以出现多次,但只有最后一个指令会生效。
总之,RUN 指令是用于在构建镜像时运行命令的,ENTRYPOINT 指令是在容器运行时指定主要命令或应用程序的,CMD 指令用于为镜像设置默认参数。