mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-10 20:04:36 +00:00
Add more practices
This commit is contained in:
110
network/dns.md
110
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):外部访问
|
||||
|
||||
@@ -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 模式不需要端口映射
|
||||
|
||||
Reference in New Issue
Block a user