当前位置: 首页 > backend >正文

Docker学习笔记(二):镜像与容器管理

Docker

镜像

最小的镜像

hello-world 是 Docker 官方提供的一个镜像,通常用来验证 Docker 是否安装成功。

先通过 docker pull 从 Docker Hub 下载它。

[root@docker ~]# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
Digest: sha256:a0dfb02aac212703bfcb339d77d47ec32c8706ff250850ecc0e19c8737b18567
Status: Image is up to date for hello-world:latest
docker.io/library/hello-world:latest

用 docker images 命令查看镜像的信息。

[root@docker ~]# docker images hello-world
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
hello-world   latest    1b44b5a3e06a   3 weeks ago   10.1kB

通过 docker run 运行。

[root@docker ~]# docker run hello-worldHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:1. The Docker client contacted the Docker daemon.2. The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)3. The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare images, automate workflows, and more with a free Docker ID:https://hub.docker.com/For more examples and ideas, visit:https://docs.docker.com/get-started/

base镜像

base 镜像有两层含义:

  • 不依赖其他镜像,从 scratch 构建。

  • 其他镜像可以之为基础进行扩展。

所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。

在这里插入图片描述

以 CentOS 为例考察 base 镜像包含哪些内容。

下载镜像:

docker pull centos:7

[root@docker ~]# docker pull centos:7
7: Pulling from library/centos
2d473b07cdd5: Pull complete
Digest: sha256:be65f488b7764ad3638f236b7b515b3678369a5124c47b8d32916d6487418ea4
Status: Downloaded newer image for centos:7
docker.io/library/centos:7

查看镜像信息:

[root@docker ~]# docker images centos:7
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       7         eeb6ee3f44bd   3 years ago   204MB

Linux 操作系统由内核空间和用户空间组成。如下图所示:

在这里插入图片描述

rootfs

内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。

用户空间的文件系统是 rootfs,包含我们熟悉的 /dev, /proc, /bin 等目录。

对于 base 镜像来说,底层直接用 Host 的 kernel,自己只需要提供 rootfs 就行了。

而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 已经算臃肿的了,alpine 还不到 10MB。

我们平时安装的 CentOS 除了 rootfs 还会选装很多软件、服务、图形桌面等,需要好几个 GB 就不足为 奇了。

base 镜像提供的是最小安装的 Linux 发行版。

支持运行多种 Linux OS

不同 Linux 发行版的区别主要就是 rootfs。

比如 Ubuntu 14.04 使用 upstart 管理服务,apt 管理软件包;而 CentOS 7 使用 systemd 和 yum。这 些都是用户空间上的区别,Linux kernel 差别不大。

所以 Docker 可以同时支持多种 Linux 镜像,模拟出多种操作系统环境。

在这里插入图片描述

上图 Debian 和 BusyBox(一种嵌入式 Linux)上层提供各自的 rootfs,底层共用 Docker Host 的 kernel。

额外1
  • base 镜像只是在用户空间与发行版一致,kernel 版本与发型版是不同的。 例如 ubuntu使用 3.x.x 的 kernel,如果 Docker Host 是 CentOS Stream 8(比如我们的实验环 境),那么在 CentOS 容器中使用的实际是是 Host 4.18.0 的 kernel。
[root@docker ~]# uname -r
4.18.0-553.6.1.el8.x86_64
#Host os kernel 为 4.18.0

启动一个ubuntu,ubuntu内核正常与host os(centos stream 8)不一致

[root@docker ~]# docker run -it ubuntu
root@65a155bed367:/# uname -r
4.18.0-553.6.1.el8.x86_64

启动一个centos:7,centos:7内核正常为3.10

[root@docker ~]# docker run -it centos:7
[root@1068e82c836b /]# uname -r
4.18.0-553.6.1.el8.x86_64
  • 容器只能使用 Host 的 kernel,并且不能修改。 所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。如果容器对 kernel 版本有要求 (比如应用只能在某个 kernel 版本下运行),则不建议用容器,这种场景虚拟机可能更合适。
额外2
# 查看内核版本
[root@docker ~]# uname -r
4.18.0-553.6.1.el8.x86_64# 查看操作系统版本
[root@docker ~]# cat /etc/os-release
NAME="CentOS Stream"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Stream 8"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://centos.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream"

镜像的分层结构

Docker 支持通过扩展现有镜像,创建新的镜像。

构建过程如下图所示:

在这里插入图片描述

可写的容器层

当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。

在这里插入图片描述

只有容器层是可写的,容器层下面的所有镜像层都是只读的。

对容器增删改差操作如下:

操作具体执行
创建 文件新文件只能被添加在容器层中。
删除 文件依据容器分层结构由上往下依次查找。找到后,在容器层中记录该删除操作。 具体实现 是,UnionFS会在容器层创建一个”whiteout”文件,将被删除的文件“遮挡”起来。
修改 文件依据容器分层结构由上往下依次查找。找到后,将镜像层中的数据复制到容器层进行修 改,修改后的数据保存在容器层中。(copy-on-write)
读取 文件依据容器分层结构由上往下依次查找。

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化 的部分,不会对镜像本身进行任何修改。

构建镜像

Docker 提供了两种构建镜像的方法:
  • docker commit 命令

  • Dockerfile 构建文件

docker commit

docker commit 命令是创建新镜像最直观的方法,其过程包含三个步骤:

运行容器–修改容器 --将容器保存为新的镜像

示例:

运行容器
[root@docker ~]# docker run -it ubuntu
root@dfcf8c139cb1:/#
安装 vim

本身不带vim

root@dfcf8c139cb1:/# vim
bash: vim: command not found
root@dfcf8c139cb1:/# apt-get update
Get:1 http://archive.ubuntu.com/ubuntu noble InRelease [256 kB]
Get:2 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Get:3 http://security.ubuntu.com/ubuntu noble-security/main amd64 Packages [1408 kB]
Get:4 http://archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
Get:5 http://archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB]
Get:6 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages [19.3 MB]
Get:7 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Packages [2159 kB]
Get:8 http://security.ubuntu.com/ubuntu noble-security/multiverse amd64 Packages [23.0 kB]
Get:9 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Packages [1135 kB]
Get:10 http://archive.ubuntu.com/ubuntu noble/main amd64 Packages [1808 kB]
Get:11 http://archive.ubuntu.com/ubuntu noble/restricted amd64 Packages [117 kB]
Get:12 http://archive.ubuntu.com/ubuntu noble/multiverse amd64 Packages [331 kB]
Get:13 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages [1760 kB]
Get:14 http://archive.ubuntu.com/ubuntu noble-updates/restricted amd64 Packages [2269 kB]
Get:15 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 Packages [1918 kB]
Get:16 http://archive.ubuntu.com/ubuntu noble-updates/multiverse amd64 Packages [45.2 kB]
Get:17 http://archive.ubuntu.com/ubuntu noble-backports/main amd64 Packages [48.8 kB]
Get:18 http://archive.ubuntu.com/ubuntu noble-backports/universe amd64 Packages [35.6 kB]
Fetched 33.0 MB in 37s (896 kB/s)
Reading package lists... Doneroot@dfcf8c139cb1:/# apt-get install -y vim
5
69
保存为新镜像

一定要新开一个窗口查看!!!

[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
dfcf8c139cb1   ubuntu    "/bin/bash"   3 minutes ago   Up 3 minutes             distracted_lehmann

dfcf8c139cb1 是新创建容器的ID

distracted_lehmann 是 Docker 为我们的容器随机分配的名字。

将容器保存为镜像
[root@docker ~]# docker commit distracted_lehmann ubuntu-with-vim
sha256:ba23f0e4a93f623c832ff90087f23979680e1b42e5c381b49978c29849ee9c04

新镜像的命名为ubuntu-with-vim

查看新镜像的属性
[root@docker ~]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED          SIZE
ubuntu-with-vim   latest    ba23f0e4a93f   58 seconds ago   203MB
ubuntu            latest    802541663949   2 weeks ago      78.1MB
hello-world       latest    1b44b5a3e06a   3 weeks ago      10.1kB
httpd             latest    199e3a035264   3 weeks ago      117MB
centos            7         eeb6ee3f44bd   3 years ago      204MB
使用新镜像
[root@docker ~]# docker run -it ubuntu-with-vim
root@03907d15a3e0:/# which vim
/usr/bin/vim
root@03907d15a3e0:/# vim file1
root@03907d15a3e0:/# cat file1
123

Dockerfile构建镜像

Dockerfile 是一个文本文件,记录了镜像构建的所有步骤。

Dockerfile内容基础知识:

  • 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  • 指令按照从上到下,顺序执行
  • #表示注释
  • 每条指令都会创建一个新的镜像层并对镜像进行提交

常用参数:

docker build -f [Dockerfile路径] [构建上下文路径]
参数作用
-f 或 --file标志符,声明要使用自定义 Dockerfile
[Dockerfile路径]绝对路径或相对于构建上下文的路径(如 subdir/Dockerfile.dev )
[构建上下文路径]Docker 打包发送给守护进程的目录(通常用 . 表示当前目录)
第一个 Dockerfile

用 Dockerfile 创建上节的 ubuntu-with-vim

[root@docker ~]# cd /root/
[root@docker ~]# ls
anaconda-ks.cfg
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y vim
[root@docker ~]# docker build -t ubuntu-with-vim-dockerfile .

在这里插入图片描述

查看镜像分层结构 ubuntu-with-vim-dockerfile 是通过在 base 镜像的顶部添加一个新的镜像层而得到的。

在这里插入图片描述

[root@docker ~]# docker images
REPOSITORY                   TAG       IMAGE ID       CREATED              SIZE
ubuntu-with-vim-dockerfile   latest    e22fcd50ee9e   About a minute ago   203MB
ubuntu-with-vim              latest    ba23f0e4a93f   52 minutes ago       203MB
ubuntu                       latest    802541663949   2 weeks ago          78.1MB
hello-world                  latest    1b44b5a3e06a   3 weeks ago          10.1kB
httpd                        latest    199e3a035264   3 weeks ago          117MB
centos                       7         eeb6ee3f44bd   3 years ago          204MB[root@docker ~]# docker history ubuntu
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
802541663949   2 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      2 weeks ago   /bin/sh -c #(nop) ADD file:e67907c77897d2719…   78.1MB
<missing>      2 weeks ago   /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      2 weeks ago   /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      2 weeks ago   /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
<missing>      2 weeks ago   /bin/sh -c #(nop)  ARG RELEASE                  0B
[root@docker ~]# docker history ubuntu-with-vim-dockerfile:latest
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
e22fcd50ee9e   2 minutes ago   RUN /bin/sh -c apt-get update && apt-get ins…   125MB     buildkit.dockerfile.v0
<missing>      2 weeks ago     /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      2 weeks ago     /bin/sh -c #(nop) ADD file:e67907c77897d2719…   78.1MB
<missing>      2 weeks ago     /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      2 weeks ago     /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
<missing>      2 weeks ago     /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
<missing>      2 weeks ago     /bin/sh -c #(nop)  ARG RELEASE                  0B

镜像的缓存特性

Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。

示例1:

在前面的 Dockerfile 中添加一点新内容,往镜像中复制一个文件:

[root@docker ~]# cd /root
[root@docker ~]# pwd
/root
[root@docker ~]# ls
anaconda-ks.cfg  Dockerfile
[root@docker ~]# touch testfile
[root@docker ~]# ls
anaconda-ks.cfg  Dockerfile  testfile
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y vim
COPY testfile /
[root@docker ~]# docker build -t ubuntu-with-vim-dockerfile-2 .

在这里插入图片描述

在 ubuntu-with-vi-dockerfile 镜像上直接添加一层就得到了新的镜像 ubuntu-with-vim-dockerfile-2。

在这里插入图片描述

示例2:

如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效。

交换前面 RUN 和 COPY 的顺序:

[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM ubuntu
COPY testfile /
RUN apt-get update && apt-get install -y vim
[root@docker ~]# docker build -t ubuntu-with-vim-dockerfile-3 .#虽然在逻辑上这种改动对镜像的内容没有影响,但由于分层的结构特性,Docker 必须重建受影响的镜像层。

在这里插入图片描述

上图中看到[2/3],[3/3]都没有使用缓存,最后生成了新的镜像层,缓存已经失效。

调试Dockerfile

如果Dockerfile出现错误该如何解决?

示例:

先pull busybox

[root@docker ~]# ls
anaconda-ks.cfg  Dockerfile  testfile
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c "echo continue to build..."
COPY testfile /[root@docker ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
80bfbb8a41a2: Pull complete
Digest: sha256:ab33eacc8251e3807b85bb6dba570e4698c3998eca6f0fc2ccb60575a563ea74
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest
[root@docker ~]# docker images
REPOSITORY                     TAG       IMAGE ID       CREATED          SIZE
ubuntu-with-vim-dockerfile-3   latest    f95f014aa80f   10 minutes ago   203MB
ubuntu-with-vim-dockerfile-2   latest    7de0cc8476dd   17 minutes ago   203MB
ubuntu-with-vim-dockerfile     latest    e22fcd50ee9e   2 hours ago      203MB
ubuntu-with-vim                latest    ba23f0e4a93f   3 hours ago      203MB
ubuntu                         latest    802541663949   2 weeks ago      78.1MB
hello-world                    latest    1b44b5a3e06a   3 weeks ago      10.1kB
httpd                          latest    199e3a035264   3 weeks ago      117MB
busybox                        latest    0ed463b26dae   11 months ago    4.43MB
centos                         7         eeb6ee3f44bd   3 years ago      204MB#基于刚才写的Dockerfile构建镜像image-debug
[root@docker ~]# docker build -t image-debug .

在这里插入图片描述

Dockerfile 在执行第三步 RUN 指令时失败。我们可以利用busybox的镜像进行调试,方式是通过 docker run -it 启动镜像的一个容器。

在这里插入图片描述

错误原因就是镜像中没有bash,busybox用的是sh,改错就是把Dockerfile中的bash改成sh就可以了

在这里插入图片描述

Dockerfile常用指令

FROM:镜像构建的 “地基”

这是 Dockerfile 里必须放在第一行的指令,作用是指定一个基础镜像(base 镜像)。后续所有指令都会基于这个基础镜像来叠加操作,相当于给镜像构建找了个 “起点”。比如想基于 Ubuntu 系统构建镜像,就可以写FROM ubuntu:22.04(22.04 是具体的版本标签,避免用 latest 以防版本变动)。

MAINTAINER:标注镜像 “作者信息”

用来设置镜像的作者,内容可以是任意字符串,比如个人姓名、邮箱,或者团队名称,主要作用是方便后续维护时识别镜像归属。例如MAINTAINER Zhang San <zhangsan@example.com>,不过现在更推荐用LABEL maintainer="..."(LABEL 能承载更多元的元数据),但 MAINTAINER 作为传统指令仍可使用。

COPY:简单的 “文件复制工具”

功能是把 Docker 构建上下文(build context,也就是执行docker build时指定的目录下的文件)里的文件或目录,复制到镜像内部的指定路径。
它支持两种写法:

  • 简洁版:COPY 源路径 目标路径(比如COPY app.jar /opt/
  • 数组版:COPY ["源路径", "目标路径"](当路径包含空格或特殊字符时用,比如COPY ["my app", "/opt/my app"]
    关键注意点:源路径只能是构建上下文里的文件 / 目录,不能写上下文之外的路径(比如../file.txt这种上级目录路径是无效的)。
ADD:带 “自动解压” 的复制指令

和 COPY 的核心功能一致(从构建上下文复制到镜像),但多了一个特殊能力:如果源文件是常见的归档格式(比如 tar、zip、tgz、xz 等),ADD 会自动把文件解压到镜像的目标路径下,不用额外写解压命令。
比如ADD app.tar.gz /opt/,会直接把 app.tar.gz 里的所有文件解压到 /opt/ 目录下;但如果只是想单纯复制归档文件(不想解压),就用 COPY 更合适,避免 ADD 的 “自动解压” 功能干扰。

ENV:给镜像设置 “环境变量”

用来定义环境变量,这些变量不仅在构建镜像时(后续的指令中)可以使用,容器运行时也能生效。
比如先定义ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk,之后的 RUN 指令就可以直接用这个变量:RUN echo $JAVA_HOME,容器启动后也能通过echo $JAVA_HOME看到这个路径,避免硬编码路径导致的维护麻烦。

EXPOSE:声明容器 “监听端口”

用来告诉 Docker,这个镜像构建出的容器,内部进程会监听某个或某些端口(比如 Web 服务监听 80 端口)。
比如EXPOSE 80 443,表示容器内会监听 80(HTTP)和 443(HTTPS)端口。
注意:EXPOSE 只是 “声明”,不会自动把端口映射到主机;要实现主机和容器的端口映射,需要在docker run时用-p 主机端口:容器端口(比如-p 8080:80)来手动配置,这部分后续在容器网络章节会详细讲。

VOLUME:声明镜像的 “数据卷”

把镜像中的某个文件或目录声明为 “数据卷”(volume),数据卷的作用是让容器的数据脱离容器本身存储,方便数据持久化(比如数据库的数据目录),或者实现多个容器间共享数据。
比如VOLUME /var/lib/mysql,表示把容器内的 /var/lib/mysql 目录(MySQL 的数据目录)设为数据卷,后续容器运行时,这个目录的数据会存在宿主机的指定位置,不会随着容器删除而丢失。具体的使用细节会在容器存储章节展开。

WORKDIR:设置后续指令的 “工作目录”

为 Dockerfile 中后面的 RUN、CMD、ENTRYPOINT、ADD、COPY 指令设置 “当前工作目录”,相当于在镜像内部切换到指定目录,后续这些指令的操作都会基于这个目录进行,不用每次都写完整路径。
比如先写WORKDIR /opt/app,之后的COPY app.jar .就相当于COPY app.jar /opt/app/RUN java -jar app.jar也会在 /opt/app 目录下执行,让指令更简洁。如果多次使用 WORKDIR,后续的指令会基于前一次的目录叠加(比如先WORKDIR /opt,再WORKDIR app,最终工作目录是 /opt/app)。

RUN:构建时 “执行命令”

镜像构建过程中(也就是docker build的时候),在容器环境里执行指定的命令,执行结果会被保存到镜像的分层中。
比如需要安装软件时,就用RUN apt update && apt install -y nginx(把多个命令用 && 连起来,减少镜像分层);或者创建目录RUN mkdir -p /opt/app。简单说,RUN 是 “构建镜像时要做的操作”。

CMD:容器 “启动时执行的命令”

定义容器启动时要运行的命令,比如启动 Web 服务、数据库服务等。
有几个关键特性:

  • Dockerfile 里可以写多个 CMD 指令,但只有最后一个会生效(前面的都会被覆盖);
  • CMD 指定的命令可以被docker run后面的参数 “替换”。比如 Dockerfile 里写CMD ["nginx", "-g", "daemon off;"],如果执行docker run 镜像名 /bin/bash,就会用/bin/bash替换原来的 CMD 命令,容器启动后会进入 bash 交互环境,而不是启动 nginx。
ENTRYPOINT:容器 “固定启动命令”

和 CMD 类似,也是设置容器启动时运行的命令,但它的核心特点是 “不可被轻易替换”,更适合作为容器的 “固定入口程序”。
关键特性:

  • 同样支持多个指令,但只有最后一个生效
  • CMD 的内容,或者docker run后面跟的参数,会被当作 “参数” 传递给 ENTRYPOINT 指定的命令。比如 Dockerfile 里写ENTRYPOINT ["echo", "Hello"],再写CMD ["World"],容器启动后会执行echo Hello World;如果执行docker run 镜像名 Docker,就会执行echo Hello Docker(用 “Docker” 替换了 CMD 的 “World” 作为参数)。
    简单说,ENTRYPOINT 定义 “要做什么”,CMD 或docker run参数定义 “做这件事的参数”,适合需要固定核心程序、灵活调整参数的场景(比如工具类镜像)。
综合示例:
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
# my dockerfile
FROM busybox     				#从bustbox开始创建
MAINTAINER 123456@qq.com		#声明作者信息
WORKDIR /testdir				#设置工作目录
RUN touch tmpfile1				#在新镜像中创建
COPY ["tmpfile2","."]			#将Dockerfile文件所在目录中的tmpfile2文件拷贝到新镜像中
ADD ["passwd.tar.gz","."]		#将Dockerfile文件所在目录中的passwd.tar.gz文件拷贝到新镜像中并压缩
ENV WELCOME "You are in my container,welcome! hahahahaha"					  #设置环境变量WELCOME# 构建镜像:# tmpfile2 用touch命令产生
[root@docker ~]# touch tmpfile2# passwd.tar.gz 用tar命令产生
[root@docker ~]# cp /etc/passwd .
[root@docker ~]# tar -cvzf passwd.tar.gz passwd
passwd
[root@docker ~]# pwd
/root
[root@docker ~]# rm passwd
[root@docker ~]# ls
anaconda-ks.cfg  Dockerfile  passwd.tar.gz  testfile  tmpfile2#构建新镜像my-image
[root@docker ~]# docker build -t my-image .

验证:

[root@docker ~]# docker run -it my-image
/testdir # pwd
/testdir
/testdir # ls
passwd    tmpfile1  tmpfile2
/testdir # echo $WELCOME
You are in my container,welcome! hahahahaha# 进入容器,当前目录即为 WORKDIR
# WORKDIR 中保存了我们希望的文件和目录
# ENV 指令定义的环境变量已经生效

RUN vs CMD vs ENTRYPOINT

Shell 格式
<instruction> <command>

例如

RUN apt-get install python3  
CMD echo "Hello world"  
ENTRYPOINT echo "Hello world" 

当指令执行时,shell 格式底层会调用 /bin/sh -c 。

示例:

[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
ENV name gqd
ENTRYPOINT echo "Hello, $name"[root@docker ~]# docker build -t dockerfile1 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 97B                                                  0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:3dc1f7ae4dd8e013ea883e40fd6af1eec21184e015ebfc68a83e00b  0.0s=> => naming to docker.io/library/dockerfile1                                       0.0s
[root@docker ~]# docker run dockerfile1
Hello, gqd# 环境变量 name 已经被值 gqd 替换。
Exec 格式
<instruction> ["executable", "param1", "param2", ...]

例如

RUN ["apt-get", "install", "python3"]  
CMD ["/bin/echo", "Hello world"]  
ENTRYPOINT ["/bin/echo", "Hello world"]

示例

[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
ENV name yuxb
ENTRYPOINT ["/bin/echo", "Hello, $name"][root@docker ~]# docker build -t dockerfile2 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 107B                                                 0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:d5d961b3b298e94273bcf4f50b903d2403549027d7baa757db3c6b7  0.0s=> => naming to docker.io/library/dockerfile2                                       0.0s
[root@docker ~]# docker run dockerfile2
Hello, $name# 环境变量“name”没有被替换。

如果想替换,如下:

FROM busybox
ENV name yuxb  
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
ENV name yuxb
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"][root@docker ~]# docker build -t dockerfile3 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 116B                                                 0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:cb5385aa427e4ce20cf11448cc7710404040b99cc45f8a7112ac5cc  0.0s=> => naming to docker.io/library/dockerfile3                                       0.0s
[root@docker ~]# docker run dockerfile3
Hello, yuxb
小结

CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可 以。

RUN

RUN 指令通常用于安装应用和软件包。

RUN 在当前镜像的顶部执行命令,并通过创建新的镜像层。Dockerfile 中常常包含多个 RUN 指令。

RUN 有两种格式:

  • Shell 格式:RUN

  • Exec 格式:RUN [“executable”, “param1”, “param2”]

示例:

使用 RUN 安装多个包

FROM ubuntu
RUN apt-get update && apt-get install -y bzr cvs git mercurial subversion
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y bzr cvs git mercurial subversion
[root@docker ~]# docker build -t dockerfile4 .
[+] Building 236.5s (6/6) FINISHED                                         docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 123B                                                 0.0s=> [internal] load metadata for docker.io/library/ubuntu:latest                     0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/2] FROM docker.io/library/ubuntu:latest                                0.0s=> [2/2] RUN apt-get update && apt-get install -y bzr cvs git mercurial subversi  235.3s=> exporting to image                                                               1.2s=> => exporting layers                                                              1.1s=> => writing image sha256:f5d2b0a3ad23038c70cc1a18294f558c9bd74ae1a3bccb995a27946  0.0s=> => naming to docker.io/library/dockerfile4                                       0.0s

验证

[root@docker ~]# docker run -it dockerfile4
root@d3cd686296e4:/# apt list install brz cvs git mercurial subversion
Listing... Done
brz/noble,now 3.3.5-6build2 amd64 [installed,automatic]
cvs/noble,now 2:1.12.13+real-30build1 amd64 [installed]
git/noble-updates,noble-security,now 1:2.43.0-1ubuntu7.3 amd64 [installed]
mercurial/noble-updates,now 6.7.2-1ubuntu2.2 amd64 [installed]
subversion/noble,now 1.14.3-1build4 amd64 [installed]
root@d3cd686296e4:/#
CMD

CMD 指令允许用户指定容器的默认执行的命令。

此命令会在容器启动且 docker run 没有指定其他命令时运行。

  • 如果 docker run 指定了其他命令,CMD 指定的默认命令将被忽略。

  • 如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。

CMD 有三种格式:

  • Exec 格式:CMD [“executable”,“param1”,“param2”] 这是 CMD 的推荐格式。

  • CMD [“param1”,“param2”] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。

  • Shell 格式:CMD command param1 param2

第二种格式 CMD [“param1”,“param2”] 要与 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是为 ENTRYPOINT 设置默认的参数。在后面讨论 ENTRYPOINT 时举例说明。

下面看看 CMD 是如何工作的。Dockerfile 如下:

FROM busybox
CMD echo "Hello,world"
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
CMD echo "Hello,world"
[root@docker ~]# docker build -t dockerfile5 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 73B                                                  0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:e43571cba5b2433e067af8e01a3c850a4f93219a89b25744643c8a3  0.0s=> => naming to docker.io/library/dockerfile5                                       0.0s
[root@docker ~]# docker run -it dockerfile5
Hello,world[root@docker ~]# docker run -it dockerfile5 echo 666
666
[root@docker ~]# docker run -it dockerfile5 /bin/sh
/ #
ENTRYPOINT

ENTRYPOINT 指令的核心作用是让容器能以应用程序或服务的形式运行,它和 CMD 有相似之处 —— 都能指定要执行的命令及参数,但两者的关键区别在于:ENTRYPOINT 指定的命令不会被忽略,无论运行docker run时是否额外指定了其他命令,它都会被执行。

ENTRYPOINT 有两种使用格式,选择时要特别注意,因为不同格式的实际效果差异很大:

  1. Exec 格式(推荐)
    写法是ENTRYPOINT ["可执行文件", "参数1", "参数2"],比如ENTRYPOINT ["nginx", "-g", "daemon off;"]。这种格式会直接调用指定的可执行文件,能够正确接收后续的参数(包括 CMD 里的参数或docker run时附加的参数)。
  2. Shell 格式
    写法是ENTRYPOINT 命令 参数1 参数2,比如ENTRYPOINT nginx -g "daemon off;"。这种格式会在 shell 中执行命令,此时docker run附加的参数无法传递给 ENTRYPOINT,实际使用中灵活性较低,不如 Exec 格式常用。
Exec 格式

ENTRYPOINT 的 Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。

ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。

示例Dockerfile

FROM busybox
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
[root@docker ~]# docker build -t dockerfile6 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 98B                                                  0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:066e84f20225466825115eda2b0a68331ddb8d7d628844f37994c1a  0.0s=> => naming to docker.io/library/dockerfile6                                       0.0s
[root@docker ~]# docker run -it dockerfile6
Hello world[root@docker ~]# docker run -it dockerfile6 666
Hello 666
Shell 格式

ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。

示例Dockerfile

FROM busybox
ENTRYPOINT echo "Hello,"
CMD ["world"]
[root@docker ~]# vim Dockerfile
[root@docker ~]# cat Dockerfile
FROM busybox
ENTRYPOINT echo "Hello,"
CMD ["world"][root@docker ~]# docker build -t dockerfile7 .
[+] Building 0.1s (5/5) FINISHED                                           docker:default=> [internal] load build definition from Dockerfile                                 0.0s=> => transferring dockerfile: 89B                                                  0.0s=> [internal] load metadata for docker.io/library/busybox:latest                    0.0s=> [internal] load .dockerignore                                                    0.0s=> => transferring context: 2B                                                      0.0s=> CACHED [1/1] FROM docker.io/library/busybox:latest                               0.0s=> exporting to image                                                               0.0s=> => exporting layers                                                              0.0s=> => writing image sha256:452cb29c2d513ff74ac8744c4c99a9e521c2da78edeee10829035e9  0.0s=> => naming to docker.io/library/dockerfile7                                       0.0s
[root@docker ~]# docker run -it dockerfile7
Hello,[root@docker ~]# docker run -it dockerfile7 666
Hello,
最佳实践
  • 使用 RUN 指令安装应用和软件包,构建镜像。

  • 如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式 的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命 令行替换默认参数。

  • 如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认 命令。

Dockerfile案例:配置SSH镜像

先创建Dockerfile

[root@docker ~]# vim centos.ssh.dockerfile
[root@docker ~]# cat centos.ssh.dockerfile
FROM centos:8.4.2105
MAINTAINER yuxb
RUN minorver=8.4.2105 && \
sed -e "s|^mirrorlist=|#mirrorlist=|g" \
-e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors                                                                                                                    .aliyun.com/centos-vault/$minorver|g" \
-i.bak \
/etc/yum.repos.d/CentOS-*.repo
RUN yum install -y openssh-server
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN echo "root:123" | chpasswd
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]

构建镜像

[root@docker ~]# docker build -t centos:ssh -f centos.ssh.dockerfile .# docker build:Docker 的构建命令,用于根据 Dockerfile 构建镜像
# -t centos:ssh:指定构建出的镜像的标签(tag),格式为名称:标签,这里表示镜像名为centos,标签为ssh
# -f centos.ssh.dockerfile:指定要使用的 Dockerfile 文件名为centos.ssh.dockerfile(默认情况下会寻找名为Dockerfile的文件,这里用-f参数指定了自定义文件名)
# .:表示构建上下文的路径为当前目录,Docker 会将该目录下的文件发送给 Docker 引擎用于构建镜像

在这里插入图片描述

查看现象

# docker history centos:ssh 这条命令的作用是查看 centos:ssh 这个 Docker 镜像的构建历史,即该镜像是由哪些步骤(层)组成的。
[root@docker ~]# docker history centos:ssh
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
0572296974cc   About a minute ago   CMD ["/usr/sbin/sshd" "-D"]                     0B        buildkit.dockerfile.v0
<missing>      About a minute ago   EXPOSE map[22/tcp:{}]                           0B        buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c echo "root:123" | chpasswd # …   1.77kB    buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c ssh-keygen -t ecdsa -f /etc/s…   695B      buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c ssh-keygen -t rsa -f /etc/ssh…   3.18kB    buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c yum install -y openssh-server…   51.9MB    buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c minorver=8.4.2105 && sed -e "…   17.6kB    buildkit.dockerfile.v0
<missing>      About a minute ago   MAINTAINER yuxb                                 0B        buildkit.dockerfile.v0
<missing>      3 years ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B    
<missing>      3 years ago          /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B    
<missing>      3 years ago          /bin/sh -c #(nop) ADD file:805cb5e15fb6e0bb0…   231MB 

测试

#基于刚才dockerfile创建的镜像centos:ssh创建容器sshtest
[root@docker ~]# docker run -d -p 2022:22 --name sshtest centos:ssh
8f20df0046a8aecddd618265261b4d0db17f2e6f4f3605d5e1ef247b36b7518d
# -p 2022:22:端口映射,将容器内部的 22 端口(SSH 默认端口)映射到主机的 2022 端口。这意味着可以通过主机的 2022 端口访问容器内的 SSH 服务
# --name sshtest:为容器指定一个名称 sshtest,方便后续通过名称管理容器(如停止、删除等操作)
# centos:ssh:指定使用的镜像,即之前构建的带有 SSH 服务的 CentOS 镜像#查看创建出来的容器
[root@docker ~]# docker ps
CONTAINER ID   IMAGE        COMMAND               CREATED          STATUS          PORTS                                   NAMES
8f20df0046a8   centos:ssh   "/usr/sbin/sshd -D"   14 seconds ago   Up 13 seconds   0.0.0.0:2022->22/tcp, :::2022->22/tcp   sshtest# ssh登录容器测试ssh,能够成功登录
# 通过 SSH 协议 连接到容器内部。
[root@docker ~]# ssh root@localhost -p 2022# 第二种登陆方法
# 通过 Docker 自身的 exec 命令 直接进入容器内部的交互终端。
[root@docker ~]# docker exec -it sshtest bash

在这里插入图片描述

在这里插入图片描述

ssh直连192.168.108.30,端口号输入2022也可以登录

在这里插入图片描述

http://www.xdnf.cn/news/19960.html

相关文章:

  • 基于STM32的智能家居环境监控系统设计
  • 如何看懂GPU架构?万云智算一分钟带你了解GPU参数指标
  • Matter安全实现
  • Deathnote: 1靶场渗透
  • RTC实时时钟RX8025SA国产替代FRTC8025S
  • 2025打磨机器人品牌及自动化打磨抛光设备技术新版分析
  • 为何三折叠手机只有华为可以?看华为Mate XTs非凡大师就知道
  • 【CouponHub项目开发】EasyExcel解析Excel并使用线程池异步执行和延时队列兜底
  • Java GcExcel V8.2 新版本:效率升级与功能突破
  • 5.7 点云公开数据集——3D形状分类/部件分割
  • 企业发完年终奖后,是员工跳槽的高峰期?
  • 《嵌入式硬件(二):中断》
  • 数据可视化大屏精选开源项目
  • 【SuperSocket 】SuperSocket 中自定义 Session
  • [光学原理与应用-402]:设计 - 深紫外皮秒脉冲激光器 - 元件 - AOM零级光与一级光:深紫外皮秒激光器中的核心光学特性与系统应用
  • 决策树算法详解:从原理到实战
  • RabbitMq如何实现幂等性
  • 力扣字符串刷题-六道题记录-1
  • ECMAScript (5)ES6前端开发核心:国际化与格式化、内存管理与性能
  • Lucene 8.7.0 版本的索引文件格式
  • uniapp vue页面传参到webview.nvue页面的html或者另一vue中
  • 架构-亿级流量性能调优实践
  • 【ICCV 2025 顶会论文】,新突破!卷积化自注意力 ConvAttn 模块,即插即用,显著降低计算量和内存开销。
  • 阿里云轻量应用服务器部署-WooCommerce
  • 剧本杀APP系统开发:引领娱乐行业新潮流的科技力量
  • 【RNN-LSTM-GRU】第三篇 LSTM门控机制详解:告别梯度消失,让神经网络拥有长期记忆
  • 【已更新文章+代码】2025数学建模国赛A题思路代码文章高教社杯全国大学生数学建模-烟幕干扰弹的投放策略
  • 达梦数据库-字典缓冲区 (二)-v2
  • void*指针类型转换笔记
  • C++ const以及相关关键字