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

@@ -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)外部访问

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
$ 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 模式不需要端口映射