Add more practices

This commit is contained in:
Baohua Yang
2026-01-30 17:10:28 -08:00
parent fec2e506d9
commit cbfe75fe9b
6 changed files with 563 additions and 128 deletions

View File

@@ -28,6 +28,7 @@
* [利用 commit 理解镜像构成](image/commit.md) * [利用 commit 理解镜像构成](image/commit.md)
* [使用 Dockerfile 定制镜像](image/build.md) * [使用 Dockerfile 定制镜像](image/build.md)
* [Dockerfile 指令详解](image/dockerfile/README.md) * [Dockerfile 指令详解](image/dockerfile/README.md)
* [RUN 执行命令](image/dockerfile/run.md)
* [COPY 复制文件](image/dockerfile/copy.md) * [COPY 复制文件](image/dockerfile/copy.md)
* [ADD 更高级的复制文件](image/dockerfile/add.md) * [ADD 更高级的复制文件](image/dockerfile/add.md)
* [CMD 容器启动命令](image/dockerfile/cmd.md) * [CMD 容器启动命令](image/dockerfile/cmd.md)

181
image/dockerfile/run.md Normal file
View File

@@ -0,0 +1,181 @@
# RUN 执行命令
## 基本语法
```docker
RUN <command>
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)

View File

@@ -1,20 +1,83 @@
# 简介 # Kubernetes 简介
![](../.gitbook/assets/kubernetes_logo.png) ![](../.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 个副本镜像版本为 v1K8s 会持续确保当前状态符合期望状态
### 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/):官方文档

View File

@@ -1,44 +1,114 @@
# 配置 DNS # 配置 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 ```bash
$ mount # 1. 创建自定义网络
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ... $ docker network create mynet
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
tmpfs on /etc/resolv.conf type tmpfs ... # 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 ```json
{ {
"dns" : [ "dns": [
"114.114.114.114", "114.114.114.114",
"8.8.8.8" "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)外部访问

View File

@@ -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 ```bash
$ sudo iptables -t nat -nL # 将宿主机的 8080 端口映射到容器的 80 端口
... $ docker run -d -p 8080:80 nginx
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16
...
``` ```
其中上述规则将所有源地址在 `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 ```bash
$ iptables -t nat -nL $ docker run -d -P nginx
...
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
``` ```
使用 `-p 80:80` 查看映射结果
```bash ```bash
$ iptables -t nat -nL $ docker ps
Chain DOCKER (2 references) CONTAINER ID PORTS
target prot opt source destination abc123456 0.0.0.0:49153->80/tcp
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
``` ```
注意 此时 Nginx 被映射到了宿主机的 49153 端口
* 这里的规则映射了 `0.0.0.0`意味着将接受主机来自所有接口的流量用户可以通过 `-p IP:host_port:container_port` `-p IP::port` 来指定允许访问容器的主机上的 IP接口等以制定更严格的规则 ---
* 如果希望永久绑定到某个固定的 IP 地址可以在 Docker 配置文件 `/etc/docker/daemon.json` 中添加如下内容 ## 查看端口映射
```json ### docker port
{
"ip": "0.0.0.0" ```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 模式不需要端口映射

View File

@@ -1,98 +1,126 @@
# Docker Hub # 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 ```bash
$ docker search centos $ docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED NAME DESCRIPTION STARS OFFICIAL
centos The official build of CentOS. 6449 [OK] centos The official build of CentOS. 7000+ [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]
``` ```
可以看到返回了很多包含关键字的镜像其中包括镜像名字描述收藏数表示该镜像的受关注程度是否官方创建`OFFICIAL`是否自动构建 `AUTOMATED` > **技巧**始终优先使用 `OFFICIAL` 标记为 `[OK]` 的镜像安全性更有保障
根据是否是官方提供可将镜像分为两类 ### 2. 拉取镜像
一种是类似 `centos` 这样的镜像被称为基础镜像或根镜像这些基础镜像由 Docker 公司创建验证支持提供这样的镜像往往使用单个单词作为名字
还有一种类型比如 `ansible/centos7-ansible` 镜像它是由 Docker Hub 的注册用户创建并维护的往往带有用户名称前缀可以通过前缀 `username/` 来指定使用某个用户提供的镜像比如 ansible 用户
另外在查找的时候通过 `--filter=stars=N` 参数可以指定仅显示收藏数量为 `N` 以上的镜像
下载官方 `centos` 镜像到本地
```bash ```bash
$ docker pull centos $ docker pull nginx:alpine
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
``` ```
## 推送镜像 ### 3. 推送镜像
用户也可以在登录后通过 `docker push` 命令来将自己的镜像推送到 Docker Hub 需要先登录
以下命令中的 `username` 请替换为你的 Docker 账号用户名
```bash ```bash
$ docker tag ubuntu:24.04 username/ubuntu:24.04 $ docker login
# 输入用户名和密码
$ 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
``` ```
## 自动构建 打标签并推送
> 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)加速下载