Docker 的核心概念之一是 Docker 镜像,它是一个轻量级、独立的、可执行的包,包含了运行软件所需的一切。
1. 什么是 Docker 镜像?
可以把 Docker 镜像理解成一种"容器模板"或"静态蓝图"。它包含了操作系统、依赖库、应用程序代码和配置等,是一种静态且不可更改的打包文件,采用分层结构,每一层都是在镜像构建过程中添加的某个组件或步骤。这种分层结构不仅提高了构建和运行的效率,还能优化存储空间。
镜像构建通常从一个基础操作系统镜像(例如 Ubuntu、Alpine)开始,接着按需添加依赖库、应用代码等内容,最终生成一个用于运行容器的可复用"快照"。想象一下,镜像就像是一个系统的备份,一旦构建完成,它就可以用来启动一个容器,而容器是镜像的运行实例。
镜像的构建一般是通过编写 Dockerfile 来实现。Dockerfile 是一组命令集合,用于定义镜像构建的每个步骤。例如,FROM 指令定义基础镜像,RUN 指令安装依赖,COPY 指令添加文件,最后 CMD 或 ENTRYPOINT 定义容器的启动命令。构建过程可以总结为以下步骤:
- 创建基础层:镜像通常从一个操作系统镜像开始,例如 ubuntu 或 alpine。
- 安装依赖:使用 RUN 指令安装所需的依赖。
- 添加文件:使用 COPY 或 ADD 指令将应用代码复制到镜像中。
- 设置启动命令:通过 CMD 或 ENTRYPOINT 指令定义容器启动时运行的命令。
每一条指令都会创建一层,最终生成的镜像即包含了所有指令执行后的累积结果。
Docker 镜像是容器的模板,容器是镜像的运行实例。每当你启动一个容器时,Docker 会基于镜像创建一个可写层,称为"容器层",并在该层上运行应用。容器层用于存储容器运行过程中产生的文件和数据,而镜像本身保持不变。
2. Docker 镜像的工作原理
Docker 镜像是分层构建的,每一层代表构建镜像过程中的一个步骤。每一层都被缓存,如果一个镜像的某一层已经存在,Docker 会复用它,而不是重新创建。这种机制显著提高了构建速度。
例如,如果您只更新了应用程序代码,而没有更新基础操作系统或依赖项,Docker 将只重建发生变化的层。这是 Docker 高效的关键。
不同的镜像可以共享相同的基础层,这样可以减少存储空间的使用。例如,多个应用都可能基于相同的 Ubuntu 层,Docker 只需存储一个该层的副本。
以下是层次工作的简单分解:
- 基础层:这通常是轻量级的 Linux 发行版,如 Alpine 或 Ubuntu。
- 依赖层:包含应用所需的依赖库。
- 应用层:包括您的应用程序所需的库和依赖项。
- 配置层:您的应用程序需要的任何特定配置、环境变量或额外工具。
Docker 使用分层存储驱动(如 AUFS、OverlayFS、Btrfs 等)来管理这些层。在 Docker 启动时,它会把这些层"叠加"起来形成一个完整的文件系统供容器使用。分层存储驱动的关键功能如下:
- 叠加机制:Docker 会将不同的只读层与容器的读写层叠加起来,提供一个统一的文件系统。对于容器内部的操作来说,它看起来像一个完整的文件系统。
- 写时复制:容器在运行时会有一个单独的可写层,所有对文件的修改都会存储在这个可写层中,而不会影响只读的基础层。这样不仅可以保护镜像文件的完整性,还能实现资源的高效利用。
例如,如果容器修改了某个文件,Docker 不会直接在只读层中更改该文件,而是将其复制到容器的写层中并在写层上修改。
3. Docker 镜像与容器
一个常见的困惑点是 Docker 镜像和容器之间的区别。为了使概念更清晰,您可以将 Docker 镜像和容器视为类似于面向对象编程概念:
- Docker 镜像 :这就像编程中的一个 类。类定义了结构和行为(方法和属性),但在您创建实例之前并不实际执行任何操作。类似地,Docker 镜像是一个静态蓝图,包含了运行应用程序的指令和依赖项,但它本身并不主动运行。
示例 :一个 Car
类可能定义了 color
、model
和 speed
等属性,但在您创建实例之前它并不是一辆真正的汽车。
- Docker 容器 :这就像类的一个 实例。当您实例化(运行)一个类时,您会得到一个可以与世界互动的活对象。类似地,当您运行一个 Docker 镜像时,它就变成了一个活的 Docker 容器,在隔离环境中执行应用程序。
简单说来,我们可以将 Docker 镜像看成是 Docker 容器的静态时,也可将 Docker 容器看成是 Docker 镜像的运行时。
从 Docker 的官方文档来看,Docker 容器的定义和 Docker 镜像的定义几乎是相同,Docker 容器和 Docker 镜像的区别主要在于 docker 容器多出了一个可写层。
容器中的进程就运行在这个可写层,这个可写层有两个状态,即运行态和退出态。当我们 docker run 运行容器后,docker 容器就进入了运行态,当我们停止正在运行中的容器时,docker 容器就进入了退出态。
4. 从 Docker Hub 拉取和运行镜像
Docker Hub 是一个公共注册表,开发者可以在这里共享镜像。您可以轻松地从 Docker Hub 拉取现有镜像并在您的环境运行。
例如,要运行一个 Nginx Web 服务器,您可以使用几个命令拉取并运行官方的 Nginx 镜像:
# 从 Docker Hub 拉取 Nginx 镜像
docker pull nginx
# 将 Nginx 镜像作为容器运行
docker run -d -p 8080:80 nginx
在这个例子中:
docker pull nginx
:从 Docker Hub 下载最新的 Nginx 镜像。docker run -d -p 8080:80 nginx
:以分离模式(-d
)运行容器,将容器的 80 端口映射到主机的 8080 端口。
现在,如果您访问 http://localhost:8080
,您应该可以看到 Nginx 的欢迎页面。
Docker Hub于2024年9月7日晚间恢复了中国大陆地区的正常访问和镜像拉取功能,可以正常访问和使用Docker Hub,速度也表现良好。
在此之前,由于某些原因,Docker Hub在国内无法正常访问和拉取镜像,导致许多用户无法使用Docker Hub服务。为了应对这一问题,国内用户可以采取以下替代方案:
- 使用镜像加速:配置国内Docker镜像加速地址,通过加速节点下载Docker Hub的内容。推荐使用阿里云的镜像加速服务,用户需要注册并登录阿里云,找到容器镜像服务的加速器地址,并在Docker配置文件中添加该地址。
- 使用国内镜像网站:例如AtomHub可信镜像中心(https://hub.atomgit.com/),这是一个国内的Docker镜像网站,用户可以在这里拉取镜像。
5. 创建您自己的 Docker 镜像
虽然从 Docker Hub 拉取镜像很有用,但开发者经常需要创建自定义镜像。这就是 Dockerfile 的用武之地。Dockerfile 是一个脚本,包含了一系列构建 Docker 镜像的指令。
以下是一个基本 Go 应用程序的示例 Dockerfile:
# 从基础镜像开始
FROM golang:1.18
# 设置容器内的工作目录
WORKDIR /app
# 复制 Go 模块文件
COPY go.mod ./
COPY go.sum ./
# 下载依赖项
RUN go mod download
# 复制源代码
COPY . .
# 构建应用程序
RUN go build -o myapp
# 运行应用程序的命令
CMD ["./myapp"]
构建 Docker 镜像
一旦您的 Dockerfile 准备好,您可以使用以下命令构建您的 Docker 镜像:
docker build -t my-go-app .
这个命令告诉 Docker 从当前目录(.
)的 Dockerfile 构建镜像,并将其标记为 my-go-app
。
运行 Docker 镜像
要将您刚刚构建的镜像作为容器运行:
docker run -d -p 8080:8080 my-go-app
这个命令从 my-go-app
镜像启动一个容器,并将容器的 8080 端口映射到主机的 8080 端口。
6. 管理 Docker 镜像
Docker 提供了几个命令来帮助管理您的镜像。
-
列出镜像:要查看系统上的所有 Docker 镜像列表,请运行:
docker images
-
删除镜像:如果您不再需要一个镜像,您可以使用以下命令删除它:
docker rmi <image-id>
-
清理未使用的镜像:Docker 还提供了一种清理未使用镜像的方法:
docker image prune
这个命令将删除悬挂镜像(没有标记或与任何容器关联的镜像)。
7. 结论
Docker 镜像是 Docker 生态中的核心要素,作为容器的蓝图,它确保了应用程序在不同环境中的一致性和可移植性。无论是从 Docker Hub 拉取镜像,还是使用 Dockerfile 构建自定义镜像,Docker 镜像都让应用交付变得更加高效。掌握 Docker 镜像的创建和管理,您将更好地控制容器化应用程序的运行环境,让应用能够高效、可靠地部署到任何环境中。
本文由mdnice多平台发布