diff --git a/SUMMARY.md b/SUMMARY.md index f9b2e77..1e38119 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -28,6 +28,7 @@ * [利用 commit 理解镜像构成](image/commit.md) * [使用 Dockerfile 定制镜像](image/build.md) * [Dockerfile 指令详解](image/dockerfile/README.md) + * [RUN 执行命令](image/dockerfile/run.md) * [COPY 复制文件](image/dockerfile/copy.md) * [ADD 更高级的复制文件](image/dockerfile/add.md) * [CMD 容器启动命令](image/dockerfile/cmd.md) diff --git a/image/dockerfile/run.md b/image/dockerfile/run.md new file mode 100644 index 0000000..0a652e7 --- /dev/null +++ b/image/dockerfile/run.md @@ -0,0 +1,181 @@ +# RUN 执行命令 + +## 基本语法 + +```docker +RUN +RUN ["executable", "param1", "param2"] +``` + +`RUN` 指令是 Dockerfile 中最常用的指令之一。它在**当前镜像层**之上创建一个新层,执行指定的命令,并提交结果。 + +--- + +## 两种格式对比 + +### 1. Shell 格式 + +```docker +RUN apt-get update +``` + +- **特点**:默认通过 `/bin/sh -c` 执行。 +- **优势**:可以使用环境变量、管道、重定向等 Shell 特性。 +- **示例**: + ```docker + RUN echo "Hello" > /test.txt + ``` + +### 2. Exec 格式 + +```docker +RUN ["apt-get", "update"] +``` + +- **特点**:直接调用可执行文件,不经过 Shell。 +- **优势**:避免 Shell 字符串解析问题,适用于参数中包含特殊字符的情况。 +- **注意**:无法使用 `$VAR` 环境变量替换(除非显式调用 shell)。 + +--- + +## 常见最佳实践 + +### 1. 组合命令(减少层数) + +每一个 `RUN` 指令都会新建一层镜像。为了减少镜像体积和层数,应使用 `&&` 连接命令。 + +**❌ 糟糕的写法**(创建 3 层): + +```docker +RUN apt-get update +RUN apt-get install -y nginx +RUN rm -rf /var/lib/apt/lists/* +``` + +**✅ 推荐写法**(创建 1 层): + +```docker +RUN apt-get update && \ + apt-get install -y nginx && \ + rm -rf /var/lib/apt/lists/* +``` + +### 2. 清理缓存 + +在安装完软件后,立即清除缓存,可以显著减小镜像体积。 + +- **Debian/Ubuntu**: + ```docker + RUN apt-get update && apt-get install -y package-bar \ + && rm -rf /var/lib/apt/lists/* + ``` +- **Alpine**: + ```docker + RUN apk add --no-cache package-bar + ``` + +### 3. 使用 `set -e` 和 `pipefail` + +默认情况下,管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功,整个 `RUN` 就视为成功。 + +**❌ 隐蔽的错误**: + +```docker +# 如果下载失败,gzip 可能会报错,但如果不影响后续,构建可能继续 +RUN wget http://error-url | gzip -d > file +``` + +**✅ 推荐写法**: + +```docker +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +RUN wget http://url | gzip -d > file +``` + +--- + +## 常见问题 + +### Q: 为什么 `RUN cd /app` 不生效? + +```docker +RUN cd /app +RUN touch hello.txt +``` + +**结果**:`hello.txt` 会出现在根目录 `/`,而不是 `/app`。 + +**原因**:每个 `RUN` 都在一个新的 Shell/容器 环境中执行。`cd` 只影响当前 `RUN` 的环境。 + +**解决**:使用 `WORKDIR` 指令。 + +```docker +WORKDIR /app +RUN touch hello.txt +``` + +### Q: 环境变量不生效? + +```docker +RUN export MY_VAR=hello +RUN echo $MY_VAR +``` + +**结果**:输出为空。 + +**原因**:同上,环境变量只在当前 `RUN` 有效。 + +**解决**:使用 `ENV` 指令,或在同一行 `RUN` 中导出。 + +```docker +ENV MY_VAR=hello +RUN echo $MY_VAR +``` + +--- + +## 高级技巧 + +### 1. 使用 BuildKit 的挂载缓存 + +BuildKit 支持在 `RUN` 指令中使用 `--mount` 挂载缓存,加速构建。 + +```docker +# 缓存 apt 包 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && apt-get install -y gcc +``` + +```docker +# 缓存 Go 模块 +RUN --mount=type=cache,target=/go/pkg/mod \ + go build -o app +``` + +### 2. 挂载密钥 + +安全地使用 SSH 密钥或 Token,而不将其记录在镜像中。 + +```docker +RUN --mount=type=secret,id=mysecret \ + cat /run/secrets/mysecret +``` + +--- + +## 本章小结 + +| 要点 | 说明 | +|------|------| +| **作用** | 在新层执行命令 | +| **原则** | 合并命令,清理缓存 | +| **格式** | Shell (常用) vs Exec | +| **陷阱** | `cd` 不持久,环境变量不持久 | +| **进阶** | 使用 Cache Mount 加速构建 | + +## 延伸阅读 + +- [CMD 容器启动命令](cmd.md):容器启动时的命令 +- [WORKDIR 指定工作目录](workdir.md):改变目录 +- [Dockerfile 最佳实践](../../appendix/best_practices.md) diff --git a/kubernetes/intro.md b/kubernetes/intro.md index 5d37b28..c570305 100644 --- a/kubernetes/intro.md +++ b/kubernetes/intro.md @@ -1,20 +1,83 @@ -# 简介 +# Kubernetes 简介 ![](../.gitbook/assets/kubernetes_logo.png) -Kubernetes 是 Google 团队发起的开源项目,它的目标是管理跨多个主机的容器,提供基本的部署,维护以及应用伸缩,主要实现语言为 Go 语言。Kubernetes 是: +## 什么是 Kubernetes -* 易学:轻量级,简单,容易理解 -* 便携:支持公有云,私有云,混合云,以及多种云平台 -* 可拓展:模块化,可插拔,支持钩子,可任意组合 -* 自修复:自动重调度,自动重启,自动复制 +Kubernetes(常简称为 K8s)是 Google 开源的容器编排引擎。如果说 Docker 解决了"如何打包和运送集装箱"的问题,那么 Kubernetes 解决的就是"如何管理海量集装箱的调度、运行和维护"的问题。 -Kubernetes 构建于 Google 数十年经验,一大半来源于 Google 生产环境规模的经验。结合了社区最佳的想法和实践。 +它不仅仅是一个编排系统,更是一个**云原生应用操作系统**。 -在分布式系统中,部署,调度,伸缩一直是最为重要的也最为基础的功能。Kubernetes 就是希望解决这一序列问题的。 +> **名字由来**:Kubernetes 在希腊语中意为"舵手"或"飞行员"。K8s 是因为 k 和 s 之间有 8 个字母。 -Kubernetes 目前在[GitHub](https://github.com/kubernetes/kubernetes)进行维护。 +--- -### Kubernetes 能够运行在任何地方! +## 为什么需要 Kubernetes -虽然 Kubernetes 最初是为 GCE 定制的,但是在后续版本中陆续增加了其他云平台的支持,以及本地数据中心的支持。 +当我们在单机运行几个容器时,Docker Compose 就足够了。但在生产环境中,我们需要面对: + +- **多主机调度**:容器应该运行在哪台机器上? +- **自动恢复**:容器崩溃了怎么办?节点挂了怎么办? +- **服务发现**:容器 IP 变了,其他服务怎么找到它? +- **负载均衡**:流量大了,如何分发给多个副本? +- **滚动更新**:如何不中断服务升级应用? + +Kubernetes 完美解决了这些问题。 + +--- + +## 核心概念 + +### Pod (豆荚) +Kubernetes 的最小调度单位。一个 Pod 可以包含一个或多个紧密协作的容器(共享网络和存储)。就像豌豆荚里的豌豆一样。 + +### Node (节点) +运行 Pod 的物理机或虚拟机。 + +### Deployment (部署) +定义应用的期望状态(如:需要 3 个副本,镜像版本为 v1)。K8s 会持续确保当前状态符合期望状态。 + +### Service (服务) +定义一组 Pod 的访问策略。提供稳定的 Cluster IP 和 DNS 名称,负责负载均衡。 + +### Namespace (命名空间) +用于多租户资源隔离。 + +--- + +## Docker 用户如何过渡 + +如果你已经熟悉 Docker,学习 K8s 会很容易: + +| Docker 概念 | Kubernetes 概念 | 说明 | +|------------|----------------|------| +| Container | Pod | K8s 增加了一层 Pod 包装 | +| Volume | PersistentVolume | K8s 的存储更加抽象和强大 | +| Network | Service/Ingress| K8s 的网络模型更扁平 | +| Compose | Deployment + Service | 声明式配置的理念是一致的 | + +--- + +## 架构 + +Kubernetes 也是 C/S 架构,由 **Master (控制平面)** 和 **Worker (工作节点)** 组成: + +- **Control Plane**:负责决策(API Server, Scheduler, Controller Manager, etcd) +- **Worker Node**:负责干活(Kubelet, Kube-proxy, Container Runtime) + +--- + +## 学习建议 + +Kubernetes 的学习曲线较陡峭。建议的学习路径: +1. **理解基本概念**:Pod, Deployment, Service +2. **动手实践**:使用 Minikube 或 Kind 在本地搭建集群 +3. **部署应用**:编写 YAML 部署一个无状态应用 +4. **深入原理**:网络模型、存储机制、调度算法 + +--- + +## 延伸阅读 + +- [Minikube 安装](../kubernetes/setup/README.md):本地体验 K8s +- [Kubernetes 官网](https://kubernetes.io/):官方文档 diff --git a/network/dns.md b/network/dns.md index 1b6c4f9..20110e8 100644 --- a/network/dns.md +++ b/network/dns.md @@ -1,44 +1,114 @@ # 配置 DNS -如何自定义配置容器的主机名和 DNS 呢?秘诀就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件。 +## 容器的 DNS 机制 -在容器中使用 `mount` 命令可以看到挂载信息: +Docker 容器的 DNS 配置有两种情况: + +1. **默认 Bridge 网络**:继承宿主机的 DNS 配置(`/etc/resolv.conf`)。 +2. **自定义网络**(推荐):使用 Docker 嵌入式 DNS 服务器 (Embedded DNS),支持通过**容器名**进行服务发现。 + +--- + +## 嵌入式 DNS (Embedded DNS) + +这是 Docker 网络最强大的功能之一。在自定义网络中,容器可以通过"名字"找到彼此,而不需要知道对方的 IP(因为 IP 可能会变)。 ```bash -$ mount -/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ... -/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ... -tmpfs on /etc/resolv.conf type tmpfs ... +# 1. 创建自定义网络 +$ docker network create mynet + +# 2. 启动容器 web 并加入网络 +$ docker run -d --name web --network mynet nginx + +# 3. 启动容器 client 并尝试 ping web +$ docker run -it --rm --network mynet alpine ping web +PING web (172.18.0.2): 56 data bytes +64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms ``` -这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 `/etc/resolv.conf` 文件立刻得到更新。 +**原理**: +Docker 守护进程在 `127.0.0.11` 运行了一个 DNS 服务器。容器内的 DNS 请求会被转发到这里。如果是容器名,解析为容器 IP;如果是外部域名(如 google.com),转发给上游 DNS。 -配置全部容器的 DNS ,也可以在 `/etc/docker/daemon.json` 文件中增加以下内容来设置。 +--- + +## 配置 DNS 参数 + +如果你需要手动配置容器的 DNS(例如使用内网 DNS 服务器),可以在 `docker run` 中使用以下参数: + +### 1. --dns + +指定 DNS 服务器 IP。 + +```bash +$ docker run -it --dns=114.114.114.114 ubuntu cat /etc/resolv.conf +nameserver 114.114.114.114 +``` + +### 2. --dns-search + +指定 DNS 搜索域。例如设置为 `example.com`,则 `ping host` 会尝试解析 `host.example.com`。 + +```bash +$ docker run --dns-search=example.com myapp +``` + +### 3. --hostname (-h) + +设置容器的主机名。 + +```bash +$ docker run -h myweb nginx +``` + +--- + +## 全局 DNS 配置 + +如果希望所有容器都使用特定的 DNS 服务器(而不是继承宿主机),可以修改 `/etc/docker/daemon.json`: ```json { - "dns" : [ + "dns": [ "114.114.114.114", "8.8.8.8" ] } ``` -这样每次启动的容器 DNS 自动配置为 `114.114.114.114` 和 `8.8.8.8`。使用以下命令来证明其已经生效。 +修改后需要重启 Docker 服务:`systemctl restart docker`。 -```bash -$ docker run -it --rm ubuntu:24.04 cat etc/resolv.conf +--- -nameserver 114.114.114.114 -nameserver 8.8.8.8 -``` +## 常见问题 -如果用户想要手动指定容器的配置,可以在使用 `docker run` 命令启动容器时加入如下参数: +### Q: 容器无法解析域名 -`-h HOSTNAME` 或者 `--hostname=HOSTNAME` 设定容器的主机名,它会被写到容器内的 `/etc/hostname` 和 `/etc/hosts`。但它在容器外部看不到,既不会在 `docker container ls` 中显示,也不会在其他的容器的 `/etc/hosts` 看到。 +**现象**:`ping www.baidu.com` 失败,但 `ping 8.8.8.8` 成功。 -`--dns=IP_ADDRESS` 添加 DNS 服务器到容器的 `/etc/resolv.conf` 中,让容器用这个服务器来解析所有不在 `/etc/hosts` 中的主机名。 +**解决**: +1. 宿主机的 `/etc/resolv.conf` 可能有问题(例如使用了本地回环地址 127.0.0.53,特别是 Ubuntu 系统)。Docker 可能会尝试修复,但有时会失败。 +2. 尝试手动指定 DNS:`docker run --dns 8.8.8.8 ...` +3. 检查防火墙是否拦截了 UDP 53 端口。 -`--dns-search=DOMAIN` 设定容器的搜索域,当设定搜索域为 `.example.com` 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 `host.example.com`。 +### Q: 无法通过容器名通信 ->注意:如果在容器启动时没有指定最后两个参数,Docker 会默认用主机上的 `/etc/resolv.conf` 来配置容器。 +**现象**:`ping db` 提示 `bad address 'db'`。 + +**原因**: +- 你可能在使用**默认的 bridge 网络**。默认 bridge 网络**不支持**通过容器名进行 DNS 解析(这是一个历史遗留设计)。 +- **解决**:使用自定义网络 (`docker network create ...`)。 + +--- + +## 本章小结 + +| 场景 | DNS 行为 | 备注 | +|------|----------|------| +| **默认网络** | 继承宿主机 | 不支持容器名解析 | +| **自定义网络** | Docker 嵌入式 DNS | ✅ 支持容器名解析 | +| **手动指定** | 使用 `--dns` | 覆盖默认配置 | + +## 延伸阅读 + +- [网络模式](README.md):Docker 网络概览 +- [端口映射](port_mapping.md):外部访问 diff --git a/network/port_mapping.md b/network/port_mapping.md index dc6f92d..fe15be2 100644 --- a/network/port_mapping.md +++ b/network/port_mapping.md @@ -1,57 +1,149 @@ -# 映射容器端口到宿主主机的实现 +# 外部访问容器 -默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。 +## 为什么要映射端口 -## 容器访问外部实现 +容器运行在自己的隔离网络环境中(通常是 Bridge 模式)。这意味着: +- **容器之间**:可以通过 IP 或容器名(自定义网络)互通。 +- **宿主机访问容器**:可以通过容器 IP 访问。 +- **外部网络访问容器**:❌ 默认无法直接访问。 -容器所有到外部网络的连接,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 `iptables` 的源地址伪装操作实现的。 +为了让外部(如你的浏览器、其他局域网机器)访问容器内的服务,我们需要将容器的端口**映射**到宿主机的端口。 -查看主机的 NAT 规则。 +``` + 外部用户 (Browser) + │ + ▼ + 宿主机 (localhost:8080) + │ + ┌────┴────┐ 端口映射 + │ Docker │ (8080 -> 80) + │ Proxy │ + └────┬────┘ + │ + ▼ + 容器 (Class B: 80) +``` + +--- + +## 端口映射方式 + +### 1. 指定映射 (-p) + +使用 `-p <宿主机端口>:<容器端口>` 格式。 ```bash -$ sudo iptables -t nat -nL -... -Chain POSTROUTING (policy ACCEPT) -target prot opt source destination -MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 -... +# 将宿主机的 8080 端口映射到容器的 80 端口 +$ docker run -d -p 8080:80 nginx ``` -其中,上述规则将所有源地址在 `172.17.0.0/16` 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。 +此时访问 `http://localhost:8080` 即可看到 Nginx 页面。 -## 外部访问容器实现 +**多种格式**: -容器允许外部访问,可以在 `docker run` 时候通过 `-p` 或 `-P` 参数来启用。 +| 格式 | 含义 | 示例 | +|------|------|------| +| `ip:hostPort:containerPort` | 绑定指定 IP 的特定端口 | `-p 127.0.0.1:8080:80` (仅本机访问) | +| `ip::containerPort` | 绑定指定 IP 的随机端口 | `-p 127.0.0.1::80` | +| `hostPort:containerPort` | 绑定所有 IP (0.0.0.0) 的特定端口 | `-p 8080:80` (默认) | +| `containerPort` | 绑定所有 IP 的随机端口 | `-p 80` | -不管用那种办法,其实也是在本地的 `iptable` 的 nat 表中添加相应的规则。 +### 2. 随机映射 (-P) -使用 `-P` 时: +使用 `-P` (大写) 参数,Docker 会随机映射 Dockerfile 中 `EXPOSE` 指令暴露的所有端口到宿主机的高端口(49000-49900)。 ```bash -$ iptables -t nat -nL -... -Chain DOCKER (2 references) -target prot opt source destination -DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80 +$ docker run -d -P nginx ``` -使用 `-p 80:80` 时: +查看映射结果: ```bash -$ iptables -t nat -nL -Chain DOCKER (2 references) -target prot opt source destination -DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 +$ docker ps +CONTAINER ID PORTS +abc123456 0.0.0.0:49153->80/tcp ``` -注意: +此时 Nginx 被映射到了宿主机的 49153 端口。 -* 这里的规则映射了 `0.0.0.0`,意味着将接受主机来自所有接口的流量。用户可以通过 `-p IP:host_port:container_port` 或 `-p IP::port` 来指定允许访问容器的主机上的 IP、接口等,以制定更严格的规则。 +--- -* 如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 `/etc/docker/daemon.json` 中添加如下内容。 +## 查看端口映射 -```json -{ - "ip": "0.0.0.0" -} +### docker port + +```bash +$ docker port mycontainer +80/tcp -> 0.0.0.0:8080 +80/tcp -> [::]:8080 ``` + +### docker ps + +```bash +$ docker ps +CONTAINER ID IMAGE PORTS NAMES +abc123456 nginx 0.0.0.0:8080->80/tcp web +``` + +--- + +## 最佳实践与安全 + +### 1. 限制监听 IP + +默认情况下,`-p 8080:80` 会监听 `0.0.0.0:8080`,这意味着任何人只要能连接你的宿主机 IP,就能访问该服务。 + +如果不希望对外暴露(例如数据库服务),应绑定到 `127.0.0.1`: + +```bash +# 仅允许本机访问 +$ docker run -d -p 127.0.0.1:3306:3306 mysql +``` + +### 2. 避免端口冲突 + +如果宿主机 8080 已经被占用了,容器将无法启动。 + +**解决**: +- 更换宿主机端口:`-p 8081:80` +- 让 Docker 自动分配:`-p 80` + +### 3. UDP 映射 + +默认是 TCP 协议。如果要映射 UDP 服务(如 DNS, Syslog): + +```bash +$ docker run -d -p 53:53/udp dns-server +``` + +--- + +## 实现原理 + +Docker 使用 `docker-proxy` 进程(用户态)或 `iptables` DNAT 规则(内核态)来实现端口转发。 + +当流量到达宿主机端口时,iptables 规则将其目标地址修改为容器 IP 并转发: + +```bash +# 简化的 iptables 逻辑 +iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80 +``` + +这也是为什么你在容器内部看到的访问来源 IP 通常是网关 IP(如 172.17.0.1),而不是真实的外部 Client IP(除非使用 host 网络模式)。 + +--- + +## 本章小结 + +| 要点 | 说明 | +|------|------| +| **-p** | 指定端口映射(常用),如 `8080:80` | +| **-P** | 随机映射所有 EXPOSE 的端口 | +| **安全性** | 默认监听所有 IP,敏感服务应绑定 `127.0.0.1` | +| **查看** | 使用 `docker port` 或 `docker ps` | + +## 延伸阅读 + +- [EXPOSE 指令](../image/dockerfile/expose.md):在 Dockerfile 中声明端口 +- [网络模式](README.md):Host 模式不需要端口映射 diff --git a/repository/dockerhub.md b/repository/dockerhub.md index 258eb5a..18203b4 100644 --- a/repository/dockerhub.md +++ b/repository/dockerhub.md @@ -1,98 +1,126 @@ # Docker Hub -目前 Docker 官方维护了一个公共仓库 [Docker Hub](https://hub.docker.com/),其中已经包括了数量超过 [10,000,000](https://hub.docker.com/search/?type=image) 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。 +## 什么是 Docker Hub -## 注册 +[Docker Hub](https://hub.docker.com/) 是 Docker 官方维护的公共镜像仓库,也是全球最大的容器镜像库。 -你可以在 https://hub.docker.com 免费注册一个 Docker 账号。 +它提供了: +- **官方镜像**:由 Docker 官方和软件厂商(如 Nginx, MySQL, Node.js)维护的高质量镜像。 +- **个人/组织仓库**:用户可以上传自己的镜像。 +- **自动构建**:与 GitHub/Bitbucket 集成(需付费)。 +- **Webhooks**:镜像更新时触发回调。 -## 登录 +--- -可以通过执行 `docker login` 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。 +## 核心功能 -你可以通过 `docker logout` 退出登录。 +### 1. 搜索镜像 -## 拉取镜像 - -你可以通过 `docker search` 命令来查找官方仓库中的镜像,并利用 `docker pull` 命令来将它下载到本地。 - -例如以 `centos` 为关键词进行搜索: +除了网页搜索,也可以使用命令行: ```bash $ docker search centos -NAME DESCRIPTION STARS OFFICIAL AUTOMATED -centos The official build of CentOS. 6449 [OK] -ansible/centos7-ansible Ansible on Centos7 132 [OK] -consol/centos-xfce-vnc Centos container with "headless" VNC session… 126 [OK] -jdeathe/centos-ssh OpenSSH / Supervisor / EPEL/IUS/SCL Repos - … 117 [OK] -centos/systemd systemd enabled base container. 96 [OK] +NAME DESCRIPTION STARS OFFICIAL +centos The official build of CentOS. 7000+ [OK] ``` -可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建(`OFFICIAL`)、是否自动构建 (`AUTOMATED`)。 +> **技巧**:始终优先使用 `OFFICIAL` 标记为 `[OK]` 的镜像,安全性更有保障。 -根据是否是官方提供,可将镜像分为两类。 - -一种是类似 `centos` 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。 - -还有一种类型,比如 `ansible/centos7-ansible` 镜像,它是由 Docker Hub 的注册用户创建并维护的,往往带有用户名称前缀。可以通过前缀 `username/` 来指定使用某个用户提供的镜像,比如 ansible 用户。 - -另外,在查找的时候通过 `--filter=stars=N` 参数可以指定仅显示收藏数量为 `N` 以上的镜像。 - -下载官方 `centos` 镜像到本地。 +### 2. 拉取镜像 ```bash -$ docker pull centos -Using default tag: latest -latest: Pulling from library/centos -7a0437f04f83: Pull complete -Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1 -Status: Downloaded newer image for centos:latest -docker.io/library/centos:latest +$ docker pull nginx:alpine ``` -## 推送镜像 +### 3. 推送镜像 -用户也可以在登录后通过 `docker push` 命令来将自己的镜像推送到 Docker Hub。 - -以下命令中的 `username` 请替换为你的 Docker 账号用户名。 +需要先登录: ```bash -$ docker tag ubuntu:24.04 username/ubuntu:24.04 - -$ docker image ls - -REPOSITORY TAG IMAGE ID CREATED SIZE -ubuntu 24.04 5a81c4b8502e 6 days ago 78.3MB -username/ubuntu 24.04 5a81c4b8502e 6 days ago 78.3MB - -$ docker push username/ubuntu:24.04 - -$ docker search username - -NAME DESCRIPTION STARS OFFICIAL AUTOMATED -username/ubuntu +$ docker login +# 输入用户名和密码 ``` -## 自动构建 +打标签并推送: -> 2021 年 7 月 26 日之后,该项功能仅限[付费用户](https://www.docker.com/blog/changes-to-docker-hub-autobuilds/)使用。 +```bash +# 1. 标记镜像 +$ docker tag myapp:v1 username/myapp:v1 -自动构建(`Automated Builds`)可以自动触发构建镜像,方便升级镜像。 +# 2. 推送 +$ docker push username/myapp:v1 +``` -有时候,用户构建了镜像,安装了某个软件,当软件发布新版本则需要手动更新镜像。 +--- -而自动构建允许用户通过 Docker Hub 指定跟踪一个目标网站(支持 [GitHub](https://github.com) 或 [BitBucket](https://bitbucket.org))上的项目,一旦项目发生新的提交 (`commit`)或者创建了新的标签(`tag`),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。 +## 限制与配额(重要) -要配置自动构建,包括如下的步骤: +### 镜像拉取限制 (Rate Limiting) -* 登录 Docker Hub; +自 2020 年 11 月起,Docker Hub 对匿名和免费用户实施了拉取速率限制: -* 在 Docker Hub 点击右上角头像,在账号设置(`Account Settings`)中关联(`Linked Accounts`)目标网站; +| 用户类型 | 限制 | +|---------|------| +| **匿名用户** (未登录) | 每 6 小时 100 次请求 | +| **免费账户** (已登录) | 每 6 小时 200 次请求 | +| **Pro/Team 账户** | 无限制 | -* 在 Docker Hub 中新建或选择已有的仓库,在 `Builds` 选项卡中选择 `Configure Automated Builds`; +> **提示**:如果在 CI/CD 环境中遇到 `toomanyrequests` 错误,建议: +> 1. 在 CI 中配置 `docker login` +> 2. 使用国内镜像加速器 +> 3. 搭建私有仓库代理 -* 选取一个目标网站中的项目(需要含 `Dockerfile`)和分支; +--- -* 指定 `Dockerfile` 的位置,并保存。 +## 安全最佳实践 -之后,可以在 Docker Hub 的仓库页面的 `Timeline` 选项卡中查看每次构建的状态。 +### 1. 启用 2FA (双因素认证) + +在 Account Settings -> Security 中启用 2FA,保护账号安全。启用后,CLI 登录需要使用 **Access Token** 而非密码。 + +### 2. 使用 Access Token + +不要在脚本或 CI/CD 中直接使用登录密码。 +1. 在 Docker Hub -> Account Settings -> Security -> Access Tokens 创建 Token。 +2. 使用 Token 作为密码登录: + +```bash +$ docker login -u username -p dckr_pat_xxxxxxx +``` + +### 3. 关注镜像漏洞 + +Docker Hub 会对官方镜像和付费用户的镜像进行安全扫描。在镜像标签页可以看到漏洞扫描结果。 + +--- + +## Webhooks + +当镜像被推送时,可以自动触发 HTTP 回调(例如通知 CI 系统部署)。 + +**配置方法**: +仓库页面 -> Webhooks -> Create Webhook。 + +--- + +## 自动构建 (Automated Builds) + +> ⚠️ 目前仅限付费用户 (Pro/Team) 使用。 + +链接 GitHub/Bitbucket 仓库后,当代码有提交或打标签时,Docker Hub 会自动运行构建。这保证了镜像总是与代码同步,且由可信的官方环境构建。 + +--- + +## 本章小结 + +| 功能 | 说明 | +|------|------| +| **官方镜像** | 优先使用的基础镜像 | +| **拉取限制** | 匿名 100次/6h,登录 200次/6h | +| **安全** | 推荐开启 2FA 并使用 Access Token | +| **自动化** | 支持 Webhooks 和自动构建 | + +## 延伸阅读 + +- [私有仓库](registry.md):搭建自己的 Registry +- [镜像加速器](../install/mirror.md):加速下载