Docker 的核心概念之一是 Docker 镜像,它是一个轻量级、独立的、可执行的包,包含了运行软件所需的一切。

1. 什么是 Docker 镜像?

可以把 Docker 镜像理解成一种“容器模板”或“静态蓝图”。它包含了操作系统、依赖库、应用程序代码和配置等,是一种静态且不可更改的打包文件,采用分层结构,每一层都是在镜像构建过程中添加的某个组件或步骤。这种分层结构不仅提高了构建和运行的效率,还能优化存储空间。

镜像构建通常从一个基础操作系统镜像(例如 Ubuntu、Alpine)开始,接着按需添加依赖库、应用代码等内容,最终生成一个用于运行容器的可复用“快照”。想象一下,镜像就像是一个系统的备份,一旦构建完成,它就可以用来启动一个容器,而容器是镜像的运行实例。

镜像的构建一般是通过编写 Dockerfile 来实现。Dockerfile 是一组命令集合,用于定义镜像构建的每个步骤。例如,FROM 指令定义基础镜像,RUN 指令安装依赖,COPY 指令添加文件,最后 CMD 或 ENTRYPOINT 定义容器的启动命令。构建过程可以总结为以下步骤:

  1. 创建基础层:镜像通常从一个操作系统镜像开始,例如 ubuntu 或 alpine。
  2. 安装依赖:使用 RUN 指令安装所需的依赖。
  3. 添加文件:使用 COPY 或 ADD 指令将应用代码复制到镜像中。
  4. 设置启动命令:通过 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 类可能定义了 colormodelspeed 等属性,但在您创建实例之前它并不是一辆真正的汽车。

  • 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多平台发布


Miniwa
29 声望1 粉丝