大家好!我是大聪明-PLUS!
Docker 早已不再仅仅是一个将应用程序打包到容器中的工具。如今,它是一个完整的生态系统,拥有丰富的功能,而许多开发人员甚至 DevOps 专家都对其只是略知皮毛。
在本文中,我将介绍一些 Docker 功能,这些功能通常不会在教程或官方指南中涉及,但它们可以简化开发人员的工作,并帮助他们更好地理解 Docker。在整篇文章中,这些功能将按从知名到鲜为人知的顺序排列(类似于曾经流行的冰山一角)。
编译期间忽略文件
运行镜像构建时,Docker 客户端会在上下文 root 中查找文件 .dockerignore。如果找到,则所有符合其模式的文件和目录都会在传递给构建器之前从上下文中排除。
如果您有多个 Dockerfile,则可以为每个 Dockerfile 指定一个单独的忽略文件。为此,请将忽略文件放在与 Dockerfile 本身相同的文件夹中,并且其名称以 Dockerfile 名称开头。以下是此方法的一个示例:
├──` `index`.`ts`
`├──` `src/`
`├──` `docker`
`│` `├──` `build`.`Dockerfile`
`│` `├──` `build`.`Dockerfile`.`dockerignore`
`│` `├──` `lint`.`Dockerfile`
`│` `├──` `lint`.`Dockerfile`.`dockerignore`
`│` `├──` `test`.`Dockerfile`
`│` `└──` `test`.`Dockerfile`.`dockerignore`
`├──` `package`.`json`
`└──` `package-lock`.`json`
`
多阶段构建
许多人都知道可以 FROM 在单个 Dockerfile 中使用多个 Dockerfile,但并非所有人都了解这个工具的实用性。想象一下,你有一个 Go 或 C++ 应用程序,需要先编译,然后在容器中运行。如果使用单个容器执行整个过程,生成的镜像会变得很重:它包含编译器、开发库等等。
多阶段功能允许您在单个 Dockerfile 中将构建阶段(包含编译器和开发依赖项)与运行时阶段分离。这样,最终镜像将仅包含必要的二进制文件和库,从而节省空间并加快启动速度。
多阶段功能允许您将构建和运行时分离到一个 Dockerfile 中。第一个容器包含 FROM带有编译器的镜像(golang:1.22.2),第二个容器包含最小运行时(alpine, distroless),最终的可执行文件会被复制到其中。因此,最终镜像只有几兆字节。
`FROM golang:1.24 AS build
WORKDIR /app
COPY . .
RUN go build -o /app/bin/app
FROM alpine:3.20
COPY --from=build /app/bin/app /usr/local/bin/app
CMD ["app"]
`
简单模板(Go):
`FROM golang:1.24 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/bin/app ./cmd/app
FROM alpine:3.20
RUN adduser -D appuser
COPY --from=build /app/bin/app /usr/local/bin/app
USER appuser
CMD ["/usr/local/bin/app"]
`
AS build 给出一个命名阶段,然后从中 COPY --from=build 获取文件。
使用明确的图像标签(golang:1.24, alpine:3.20)来确保可重复性;请勿 latest 在生产中使用。
请务必使用 <img src> .dockerignore以避免将大型/不必要的目录复制到上下文中(例如 node_modules、.git、tmp)。这可以加快构建速度并减少机密泄露的可能性。
仅复制您需要的文件,如果您只需要二进制文件,请不要复制整个文件夹。
安装类型
BuildKit 允许您 RUN 在说明中使用其他挂载点,以简化镜像构建。挂载点类型:
| 类型 | 目的 |
|---|---|
bind |
将目录或文件从主机文件系统加载到容器中以供使用 RUN。 |
cache |
缓存程序集之间的目录,加速依赖项的安装、编译等。 |
ssh |
提供对 SSH 代理或密钥的访问,以克隆私有存储库或其他 SSH 操作。 |
secret |
安全地传输机密信息(API 密钥、密码等),而不将其包含在最终图像中。 |
有关每种类型的更多详细信息
1. 绑定(type=bind)
用途:
在运行时将文件或目录挂载到容器中 RUN,而不会将其复制到镜像。这意味着命令可以访问这些内容,但 在命令完成后 不会保存 RUN,除非命令明确将它们复制到其他地方。
运行以下命令使用绑定挂载在 ubuntu 容器中运行 bash。
例子:
`# syntax=docker/dockerfile:1.3
FROM node:16
RUN --mount=type=bind,source=./tools,target=/app/tools \
./tools/setup.sh
`
选项:
-
target/dst--- 容器内部的安装路径。 -
source/src是 中的路径from。默认值是 根目录from。 -
from--- 构建阶段、上下文或图像名称以安装根source。 -
rw/readwrite--- 允许写入挂载。写入的数据不会被保存。
例子:
`docker run `-it` `--mount` `type=`bind`,src="$(pwd)",target=`/src ubuntu `bash
或者
`docker run `-it` \
`-v` `"$(pwd):/src"` \
ubuntu `bash
当您在主机上修改并保存源代码时,这些更改会立即反映在容器中已挂载的文件夹或文件中。只要您保存文件,容器就会立即看到您对代码所做的更改。
这样,您就可以在容器中运行监视和响应文件系统更改的进程。
2. 缓存(type=cache)
目的:
允许在构建之间累积结果,无需重新运行整个作业。常用于包管理器、编译器、apt/dpkg、pip、npm、go、ccache 等。
持久缓存有助于加快构建阶段。即使您重建某个层,也只需下载新的或修改过的包。
例子:
`# syntax=docker/dockerfile:1.3
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
COPY . .
RUN python setup.py install
`
选项:
-
target--- 缓存将位于容器内的位置。 -
id--- 一个缓存标识符,用于在需要时分隔不同的缓存。默认为 pathtarget。 -
sharing--- 如何使用缓存:shared,,private。locked-
shared--- 不同的构建团队(编写者)可以同时编写。 -
private- 每个写入器都会收到"他的"缓存挂载。 -
locked--- 第二个写入器等待第一个写入器释放挂载。
-
-
from--- 创建阶段、上下文或图像名称,用作缓存安装基址。默认为空目录。 -
ro/readonly- 如果您只需要从缓存中读取。 -
source--- 中的路径from。默认值为 rootfrom。 -
mode--- 八进制格式的新缓存目录访问权限。默认值:0755。 -
uid--- 新缓存目录的用户 ID。默认值:0。 -
gid--- 新缓存目录的组 ID。默认值:0。
3. SSH(类型=ssh)
如果您要在构建中使用的凭据是套接字或 SSH 密钥,则可以使用 SSH 挂载,而不是密钥挂载。克隆私有 Git 仓库是 SSH 挂载的常见用例。
SSH 密钥/代理将被临时转移,不会保留在镜像中。
选项:
-
id--- ssh 标识符。 -
target--- 在容器中挂载 ssh 套接字/密钥的位置。
例子:
`# syntax=docker/dockerfile:1.4
FROM ubuntu:22.04
RUN --mount=type=ssh git clone git@github.com:myorg/private.git /src/private
`
4. 秘密(type=secret)
用途:
构建过程中需要敏感数据:API 密钥、令牌、凭证、私有配置。这些数据应仅在构建过程中可用 RUN, 不应保留在最终镜像中。
在构建阶段,您需要通过命令传递秘密 docker build
```````bash
docker build --secret id=mysecret,src=/local/path/to/secret.txt .
```
````
在 Dockerfile 中
```````dockerfile
RUN --mount=type=secret,id=mysecret \
some-command-that-needs-secret /run/secrets/mysecret
```
````
选项:
-
id--- 秘密的名称。 -
target- 安装位置(如果不是默认/run/secrets/)。 -
required=true/false- 如果需要秘密,并且如果缺少秘密,构建就会失败。 -
mode,uid,gid--- 文件访问权限。
容器健康:HEALTHCHECK
有时,某个服务虽然正式运行,但其中某个进程却卡住了或出现了故障。Docker 允许你在镜像中定义一个命令来检查容器的状态。
此功能有助于检测 Web 服务器陷入无限循环且无法处理新连接但服务器进程正在运行的情况。
HEALTHCHECK Dockerfile 中的指示 Docker 定期检查容器的状态;状态 :starting,, healthy。 unhealthy
`HEALTHCHECK [OPTIONS] CMD command`
Web 服务示例:
`HEALTHCHECK `--interval=`30s `--timeout=`10s `--start-period=`15s `--retries=3` \
CMD `curl` `-f` http://localhost:8080/ || `exit` `1
-
--timeout--- 如果在此时间内未收到对请求的响应,则该请求被视为不成功。 -
--start-period- 在开始检查之前给服务启动时间。 -
--interval--- 检查之间的时间。 -
--retries--- 转至 之前尝试失败的次数unhealthy。
命令退出状态指示容器的健康状况。可能的值:
0:成功 --- 容器健康且可以使用。
1:不健康 --- 容器未正常工作。
命令应该轻量、确定,并且在成功时返回 0。避免在内部进行繁重的端到端测试 HEALTHCHECK ------这会给容器带来负担。
一个 Dockerfile 只能包含一条 HEALTHCHECK 指令。如果指定了多条指令,则只有最后一条指令会被执行。
现在docker ps 显示 (如果添加了检查)。
此外,Docker Swarm 或 Kubernetes 等编排器可以使用此信息来重启容器。编排器(Swarm、ECS、Kubernetes)和其他工具可以使用此状态来重启或路由流量。
日志驱动:日志管理
默认情况下,Docker 将日志保存为 JSON 文件。然而,这并不总是方便。为了与监控系统集成,您可以选择其他驱动程序。
Docker 支持多种日志驱动程序(默认为 json-file syslog、、、 等 journald) fluentd``gelf
使用 syslog 运行容器的示例:
`docker run `--log-driver=`syslog nginx:1.27`
或者为所有容器设置默认值 /etc/docker/daemon.json:
`{
"log-driver": `"local"`,
"log-opts": {
"max-size": `"10m"`,
"max-file": `"3"`
}
}
`
Docker 支持多种内置驱动程序:
-
json-file 是标准选项,将日志写入本地 JSON 文件。
-
本地 - 类似于
json-file,但使用更紧凑的二进制存储并且扩展性更好。 -
syslog --- 将日志发送到系统日志。
-
journald - 与 systemd 和 集成
journalctl。 -
fluentd --- 将日志转发到 Fluentd 代理。
-
gelf --- 通过 GELF 协议向 Graylog 或 Logstash 发送消息。
-
awslogs 、 splunk 、 datadog 、 gcplogs 、 logentries --- 将日志发送到云或商业系统。
每个驱动程序都有自己的特性、参数和要求。例如, fluentd 它要求在主机上安装并运行 Fluentd 代理,并且 syslog syslog 守护进程可用。
Docker 登录
当您输入 docker login 并看到警告时
WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning.
--- Docker 诚实地说,默认情况下它会将您的凭据保存到 未加密 ~/.docker/config.json 格式的 文件中 (仅使用 base64 编码)。
这意味着任何人 base64 --decode 都会回来 user:password
什么是凭证助手以及为什么需要它?
Credential helper 是一个小程序,可以隐藏/检索平台存储(macOS Keychain、Windows Credential Manager、GNOME Keyring/SecretService、pass(密码存储)等)的密码。
Docker 本身不加密,但它可以将存储委托给这些助手:然后在 config.json [密码] 的位置 auth 将有一个指向助手的链接,并且密码本身存储在安全的操作系统存储中。
那里写有详细的安装说明。
文件中指定了凭证存储, $HOME/.docker/config.json用于告知 Docker Engine 使用它。参数值credsStore应该是正在使用的程序的后缀(即 之后的部分 docker-credential-)。
配置示例
macOS(钥匙串)。在 Mac 上最简单的方法是使用 osxkeychain。 ~/.docker/config.json添加:
{ "credsStore": "osxkeychain" }
此后, docker login 密码将被放入钥匙串中,并且 config.json 不会再有明确的密码。
在Linux中,经常使用pass。
使用示例pass:
`
`sudo` apt-get update
`sudo` apt-get install `-y` pass gpg
gpg `--full-generate-key`
gpg `--list-keys`
pass init `"ваш pub GPG_KEY_ID"`
`curl` `-L` `-o` /usr/local/bin/docker-credential-pass \
https://github.com/docker/docker-credential-helpers/releases/download/v0.9.3/docker-credential-pass-v0.9.3.linux-amd64
`chmod` `+`x /usr/local/bin/docker-credential-pass
`mkdir` `-p` ~/.docker
`cat` > ~/.docker/config.json <<`'EOF'`
{ `"credsStore"`: `"pass"` }
EOF
`
此后, docker login 它将数据保存到 pass,而不是 config.json。(请参阅存储库中的帮助版本 - 撰写本文时的当前版本为 v0.9.3)
在 Windows 上,Docker Desktop 默认与 Windows Credential Manager / Docker Desktop 存储集成(credsStore 通常 = desktop 或 wincred 取决于构建)。
在 Windows 上,这些设置可以在 Docker Desktop GUI 中使用 - "安全存储 docker 登录"选项;或者手动 config.json
docker build 许多人只使用像和 这样的 基本命令 docker run,而没有意识到其中隐藏的强大功能。在过去的 12 年里,Docker 的功能变得如此丰富,以至于文档仿佛无边无际。
所以,如果你仍在"最低限度"地使用 Docker,不妨尝试深入挖掘一下。这些小改进很快就会带来回报------构建速度更快,容器更简洁,整个系统也更加可靠和易于理解。
您的反馈将影响第二部分的发布。期待您的评论 :)