vault backup: 2026-11-07 16:33:44
This commit is contained in:
@@ -0,0 +1,426 @@
|
||||
很多人对 Docker 有个误解,觉得容器就是轻量级虚拟机。这个想法很危险。虚拟机有自己独立的内核,而容器是共享宿主机内核的,区别大了去了。
|
||||
|
||||
Docker 的隔离主要靠两样东西:**[Namespace](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=Namespace&zhida_source=entity)** 和 **[Cgroup](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=Cgroup&zhida_source=entity)**。
|
||||
|
||||
Namespace 做的是资源视角的隔离,让容器里的进程觉得自己是系统唯一的主人。Linux 目前提供了 6 个主要的 Namespace:
|
||||
|
||||
- **PID Namespace**:容器里的进程看不到宿主机和其他容器的进程
|
||||
- **Mount Namespace**:容器有自己独立的文件系统挂载点
|
||||
- **Network Namespace**:容器有独立的网络栈、IP地址、端口
|
||||
- **IPC Namespace**:进程间通信隔离
|
||||
- **UTS Namespace**:主机名隔离
|
||||
- **[User Namespace](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=User+Namespace&zhida_source=entity)**:用户和用户组隔离
|
||||
|
||||
Cgroup 做的是资源使用的限制,防止一个容器把宿主机的 CPU、内存、磁盘 IO 全吃光。
|
||||
|
||||
听着挺完善的对吧?但问题在于,这些隔离都是"软隔离",不是"硬隔离"。共享内核意味着,一旦内核有漏洞,或者你的配置有疏漏,容器里的恶意代码就有可能突破这些限制,逃到宿主机上。
|
||||
|
||||
我之前看过一个案例,某在线编程平台因为配置不当,用户通过 Python 的 `os.system()` 直接在宿主机上执行了命令,把整台服务器搞崩了。所以,做代码沙箱,安全这根弦必须绷紧。
|
||||
|
||||
## **第一道锁:资源限制,别让恶意代码把机器吃干抹净**
|
||||
|
||||
做代码沙箱,最基本的就是限制资源。你想想,要是用户提交一个死循环,或者疯狂 fork 进程的 fork 炸弹,不限制的话宿主机直接就废了。
|
||||
|
||||
我一般会在 `docker run` 的时候加上这些参数:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--memory="512m" \
|
||||
--memory-swap="512m" \
|
||||
--cpus="1" \
|
||||
--pids-limit="50" \
|
||||
--cpu-shares=512 \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
逐个解释下:
|
||||
|
||||
- `--memory="512m"`:限制容器最多使用 512MB 内存,超过就直接 OOM Kill,不给任何商量余地
|
||||
- `--memory-swap="512m"`:这个和 memory 设成一样的值,意思就是禁用 swap。别小看这个,有些恶意代码会利用 swap 来绕过内存限制
|
||||
- `--cpus="1"`:最多使用 1 个 CPU 核心的算力
|
||||
- `--pids-limit="50"`:限制容器内最多 50 个进程。这个是防 fork 炸弹的关键,`:(){ :|:& };:` 这种东西直接就废了
|
||||
- `--cpu-shares=512`:CPU 时间的权重分配,多个容器竞争 CPU 时按这个比例来
|
||||
|
||||
还有一点容易忽略的,磁盘 IO 也得限制:
|
||||
|
||||
```bash
|
||||
# 创建限速的 cgroup
|
||||
mkdir /sys/fs/cgroup/blkio/sandbox
|
||||
echo "8:0 1048576" > /sys/fs/cgroup/blkio/sandbox/blkio.throttle.read_bps_device
|
||||
```
|
||||
|
||||
不过说实话,磁盘 IO 限制在实际操作中比较麻烦,我更倾向于直接把容器的文件系统设成只读,后面会讲。
|
||||
|
||||
## **第二道锁:Capability 裁剪,别给容器不需要的权限**
|
||||
|
||||
Linux 把 root 的权限拆分成了几十个 Capability,Docker 默认只保留了容器运行所需的一小部分。但即便如此,默认配置下容器还是有一些不必要的权限。
|
||||
|
||||
你可以用这个命令看看默认情况下容器有哪些 Capability:
|
||||
|
||||
```bash
|
||||
docker run --rm alpine capsh --print
|
||||
```
|
||||
|
||||
我这边输出大概有这些:`CAP_CHOWN`, `CAP_DAC_OVERRIDE`, `CAP_FSETID`, `CAP_FOWNER`, `CAP_MKNOD`, `CAP_NET_RAW`, `CAP_SETGID`, `CAP_SETUID`, `CAP_SETFCAP`, `CAP_SETPCAP`, `CAP_NET_BIND_SERVICE`, `CAP_SYS_CHROOT`, `CAP_KILL`, `CAP_AUDIT_WRITE`。
|
||||
|
||||
对于代码沙箱来说,这里面很多都是多余的。我的做法是把所有 Capability 都去掉,只加回必须的:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--cap-drop=ALL \
|
||||
--cap-add=NET_BIND_SERVICE \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
`--cap-drop=ALL` 先把所有权限都干掉,然后 `--cap-add=NET_BIND_SERVICE` 只加回绑定 1024 以下端口的权限(如果你的沙箱不需要网络,这个也省了)。
|
||||
|
||||
千万别用 `--privileged` 参数!这个参数等于把所有安全机制全关了,容器里的 root 和宿主机的 root 基本没区别。我见过有人在生产环境用 `--privileged` 跑容器,被领导骂了三天。
|
||||
|
||||
## **第三道锁:[Seccomp](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=Seccomp&zhida_source=entity) 系统调用过滤,把攻击面缩到最小**
|
||||
|
||||
Seccomp 是我最喜欢的一个安全机制。它可以在内核层面限制容器进程能调用哪些系统调用,相当于给容器加了一层"系统调用防火墙"。
|
||||
|
||||
Docker 默认就启用了 Seccomp,使用一个白名单配置文件,默认会屏蔽大约 44 个危险或不常用的系统调用。你可以通过 `docker info` 确认:
|
||||
|
||||
```text
|
||||
Security Options:
|
||||
seccomp
|
||||
Profile: default
|
||||
```
|
||||
|
||||
但是,默认配置对于代码沙箱来说还是太宽松了。我一般会写一个自定义的 Seccomp Profile,只允许代码执行真正需要的系统调用。
|
||||
|
||||
举个实际的例子,如果沙箱只用来运行 Python 代码,我可以通过 `strace` 先抓取 Python 运行时需要哪些系统调用:
|
||||
|
||||
```bash
|
||||
strace -c -f python3 /tmp/test.py 2>&1 | head -50
|
||||
```
|
||||
|
||||
然后根据抓取结果编写 Seccomp Profile。一个精简版的 Profile 大概长这样:
|
||||
|
||||
```json
|
||||
{
|
||||
"defaultAction": "SCMP_ACT_ERRNO",
|
||||
"architectures": ["SCMP_ARCH_X86_64"],
|
||||
"syscalls": [
|
||||
{
|
||||
"names": [
|
||||
"read", "write", "open", "close", "fstat", "lseek",
|
||||
"mmap", "mprotect", "munmap", "brk", "rt_sigaction",
|
||||
"rt_sigprocmask", "ioctl", "access", "pipe", "select",
|
||||
"poll", "mremap", "nanosleep", "clock_gettime",
|
||||
"clone", "fork", "vfork", "execve", "exit", "wait4",
|
||||
"uname", "fcntl", "flock", "fsync", "dup", "dup2",
|
||||
"getpid", "getppid", "getuid", "getgid", "geteuid",
|
||||
"getegid", "getrlimit", "gettimeofday", "arch_prctl",
|
||||
"set_tid_address", "set_robust_list", "futex",
|
||||
"clock_getres", "clock_nanosleep"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
关键点在于 `defaultAction` 设成了 `SCMP_ACT_ERRNO`,意思就是默认拒绝所有系统调用,只在白名单里的才放行。这比默认配置安全得多。
|
||||
|
||||
使用自定义 Profile 启动容器:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--security-opt seccomp=/path/to/seccomp-profile.json \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
有个真实的案例很有说服力:CVE-2017-16995 这个内核漏洞,攻击者可以通过 bpf 系统调用实现提权。但 Docker 默认的 Seccomp 配置屏蔽了 bpf 系统调用,所以这个漏洞在 Docker 容器里根本没法利用。这就是 Seccomp 的价值。
|
||||
|
||||
不过写 Seccomp Profile 是个苦力活,不同语言运行时需要的系统调用不一样,你得一个一个测。我建议先用 `SCMP_ACT_LOG` 模式跑一段时间收集日志,确认没问题再切换到 `SCMP_ACT_ERRNO`。
|
||||
|
||||
## **第四道锁:[AppArmor](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=AppArmor&zhida_source=entity) / SELinux,强制访问控制再加一道门**
|
||||
|
||||
Seccomp 管的是系统调用,AppArmor 管的是资源访问。它可以在文件读写、网络访问、Capability 等层面做更细粒度的限制。
|
||||
|
||||
Ubuntu 默认使用 AppArmor,CentOS/RHEL 默认使用 SELinux。Docker 在 Ubuntu 上默认会给容器加载一个 AppArmor Profile,叫做 `docker-default`。
|
||||
|
||||
对于代码沙箱,我一般会写一个更严格的自定义 AppArmor Profile:
|
||||
|
||||
```text
|
||||
#include <tunables/global>
|
||||
|
||||
profile sandbox-profile flags=(attach_disconnected,mediate_deleted) {
|
||||
#include <abstractions/base>
|
||||
|
||||
# 只允许读 /usr 和 /lib
|
||||
/usr/** r,
|
||||
/lib/** r,
|
||||
/lib64/** r,
|
||||
|
||||
# 允许写临时目录
|
||||
/tmp/** rw,
|
||||
/dev/null rw,
|
||||
/dev/urandom r,
|
||||
|
||||
# 禁止网络访问(除了已建立的连接)
|
||||
deny network,
|
||||
deny network inet,
|
||||
deny network inet6,
|
||||
|
||||
# 禁止 ptrace(防止调试其他进程)
|
||||
deny ptrace,
|
||||
|
||||
# 禁止 mount
|
||||
deny mount,
|
||||
|
||||
# 禁止加载内核模块
|
||||
deny /sys/module/** rw,
|
||||
}
|
||||
```
|
||||
|
||||
加载并使用这个 Profile:
|
||||
|
||||
```bash
|
||||
# 编译并加载 Profile
|
||||
apparmor_parser -r /etc/apparmor.d/sandbox-profile
|
||||
|
||||
# 使用 Profile 启动容器
|
||||
docker run \
|
||||
--security-opt apparmor=sandbox-profile \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
AppArmor 还有个好处是它的日志很好用。当容器进程尝试访问被禁止的资源时,`/var/log/syslog` 或 `/var/log/audit/audit.log` 里会有详细记录,方便你排查问题。
|
||||
|
||||
如果你的系统是 CentOS,那就用 SELinux,思路是一样的,只是配置方式不同。SELinux 用标签和策略来管理访问控制,比 AppArmor 更严格但也更复杂。不过对于代码沙箱场景,AppArmor 够用了。
|
||||
|
||||
## **第五道锁:User Namespace,让容器里的 root 变成宿主机的 nobody**
|
||||
|
||||
这是个很容易被忽略但非常重要的安全机制。默认情况下,Docker 容器里的 root 用户(UID 0)和宿主机的 root 用户在文件权限上是等价的。也就是说,一旦容器逃逸,攻击者拿到的是宿主机的 root 权限,想想都后怕。
|
||||
|
||||
User Namespace 的作用是把容器内的 UID 映射到宿主机的非特权 UID。比如容器内的 root(UID 0)在宿主机上实际是 UID 100000,这样就算逃逸了,攻击者在宿主机上也没有任何特权。
|
||||
|
||||
Docker 启用 User Namespace 的方式:
|
||||
|
||||
```bash
|
||||
# 先在 /etc/docker/daemon.json 中配置
|
||||
{
|
||||
"userns-remap": "default"
|
||||
}
|
||||
|
||||
# 重启 Docker
|
||||
systemctl restart docker
|
||||
```
|
||||
|
||||
Docker 会自动创建一个叫 `dockremap` 的用户,并配置 subuid 和 subgid 映射。你可以验证一下:
|
||||
|
||||
```bash
|
||||
cat /etc/subuid
|
||||
# dockremap:100000:65536
|
||||
|
||||
cat /etc/subgid
|
||||
# dockremap:100000:65536
|
||||
```
|
||||
|
||||
这意味着容器内的 UID 0-65535 会映射到宿主机的 UID 100000-165535。
|
||||
|
||||
启用 User Namespace 之后有个副作用:一些需要特权的操作(比如挂载 NFS、使用 `--net=host`)就不能用了。但对于代码沙箱来说,这些功能本来就不需要,所以影响不大。
|
||||
|
||||
## **第六道锁:只读文件系统 + 临时存储,不给恶意代码留后门**
|
||||
|
||||
代码沙箱最怕的就是恶意代码往文件系统里写东西,比如写个后门脚本、改个配置文件什么的。我的做法是把容器的根文件系统设成只读:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--read-only \
|
||||
--tmpfs /tmp:size=100m,mode=1777 \
|
||||
--tmpfs /run:size=10m,mode=0755 \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
`--read-only` 把整个根文件系统挂载为只读,`--tmpfs` 给需要写入的目录单独挂载内存文件系统。`/tmp` 给 100MB,`/run` 给 10MB,够用了。
|
||||
|
||||
有些运行时还需要 `/dev/shm`,也可以加上:
|
||||
|
||||
```bash
|
||||
--tmpfs /dev/shm:size=64m,mode=1777
|
||||
```
|
||||
|
||||
这样恶意代码就算想写东西,也只能写到 `/tmp` 里,而且容器一销毁就没了,啥也留不下。
|
||||
|
||||
还有一点,别把宿主机的目录挂载到容器里,尤其是敏感目录。我见过有人把 `/var/run/docker.sock` 挂进容器的,这等于把宿主机的 Docker 控制权拱手让人,攻击者可以直接创建一个特权容器然后逃逸。
|
||||
|
||||
## **第七道锁:网络隔离,让恶意代码连不上外网**
|
||||
|
||||
代码沙箱里的程序一般不需要网络访问。就算需要,也应该严格限制。
|
||||
|
||||
最简单粗暴的方式就是直接禁用网络:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--network=none \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
这样容器里连 `ping` 都用不了,恶意代码想外传数据?没门。
|
||||
|
||||
如果你的沙箱确实需要网络(比如代码里要请求某个 API),那可以用自定义网络 + iptables 规则来做白名单:
|
||||
|
||||
```bash
|
||||
# 创建自定义网络
|
||||
docker network create --driver bridge sandbox-net
|
||||
|
||||
# 启动容器
|
||||
docker run \
|
||||
--network=sandbox-net \
|
||||
sandbox-runner
|
||||
|
||||
# 在宿主机上用 iptables 限制容器只能访问特定 IP
|
||||
iptables -I FORWARD -s 172.20.0.0/16 -d 允许的IP -j ACCEPT
|
||||
iptables -I FORWARD -s 172.20.0.0/16 -j DROP
|
||||
```
|
||||
|
||||
这样容器只能访问你允许的 IP,其他一概不通。
|
||||
|
||||
## **第八道锁:超时机制,别让代码跑到天荒地老**
|
||||
|
||||
恶意代码不一定要搞破坏,它也可以就是跑个死循环,占着你的计算资源不放。所以超时机制必须有。
|
||||
|
||||
我一般这样处理:
|
||||
|
||||
```bash
|
||||
timeout 10 docker run \
|
||||
--rm \
|
||||
--memory="512m" \
|
||||
--cpus="1" \
|
||||
sandbox-runner
|
||||
```
|
||||
|
||||
`timeout 10` 表示 10 秒后强制终止。`--rm` 表示容器结束后自动删除,不留痕迹。
|
||||
|
||||
但在生产环境中,我更倾向于在代码层面控制超时,因为 `timeout` 命令发的是 SIGTERM,有些进程可能捕获信号后不退出。更可靠的做法是在容器内部用进程管理器来控制:
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import signal
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["python3", "/tmp/user_code.py"],
|
||||
timeout=10,
|
||||
capture_output=True,
|
||||
preexec_fn=lambda: signal.signal(signal.SIGALRM, lambda *_: (_ for _ in ()).throw(TimeoutError()))
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
# 超时处理
|
||||
pass
|
||||
```
|
||||
|
||||
## **终极大招:gVisor / [Kata Containers](https://zhida.zhihu.com/search?content_id=784190073&content_type=Answer&match_order=1&q=Kata+Containers&zhida_source=entity),真正的沙箱容器**
|
||||
|
||||
前面说的那些加固措施,都是基于传统 Docker 容器的,本质上还是共享内核。如果你的安全要求特别高(比如多租户环境、运行完全不可信的代码),那可以考虑使用沙箱容器运行时。
|
||||
|
||||
**gVisor** 是 Google 开源的一个项目,它实现了一个用户态内核叫 Sentry,拦截容器进程的所有系统调用,在用户态处理后再转发给宿主机内核。这样容器进程和宿主机内核之间就多了一层防护,攻击面大幅缩小。
|
||||
|
||||
gVisor 和宿主机内核的通信只用了不到 20 个系统调用,而传统容器可以用 300 多个。这差距,安全程度可想而知。
|
||||
|
||||
安装和使用 gVisor 也比较简单:
|
||||
|
||||
```bash
|
||||
# 安装 runsc
|
||||
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list
|
||||
sudo apt-get update && sudo apt-get install runsc
|
||||
|
||||
# 配置 Docker 使用 runsc
|
||||
# 在 /etc/docker/daemon.json 中添加
|
||||
{
|
||||
"runtimes": {
|
||||
"runsc": {
|
||||
"path": "/usr/bin/runsc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 重启 Docker
|
||||
systemctl restart docker
|
||||
|
||||
# 使用 gVisor 运行容器
|
||||
docker run --runtime=runsc sandbox-runner
|
||||
```
|
||||
|
||||
gVisor 的缺点也很明显:系统调用开销导致性能有损耗,不适合系统调用密集型的应用;不支持 GPU 直通;没有实现全部的 Linux 系统调用,有些应用可能跑不起来。
|
||||
|
||||
**Kata Containers** 是另一种方案,它本质上是在轻量级虚拟机里跑容器。每个容器(或 Pod)都有自己独立的内核,隔离性接近虚拟机,但使用体验和普通容器一样。
|
||||
|
||||
Kata 的安全边界比 gVisor 更强,但资源开销也更大,每个容器至少需要几十 MB 的额外内存。对于在线编程平台这种需要同时运行成百上千个沙箱的场景,gVisor 可能更合适。
|
||||
|
||||
## **我的最终方案:层层叠加,纵深防御**
|
||||
|
||||
说了这么多,最后晒一下我在生产环境中实际使用的完整配置。我把它封装成了一个脚本:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
docker run \
|
||||
--rm \
|
||||
--runtime=runsc \
|
||||
--network=none \
|
||||
--read-only \
|
||||
--memory="512m" \
|
||||
--memory-swap="512m" \
|
||||
--cpus="1" \
|
||||
--pids-limit="50" \
|
||||
--cap-drop=ALL \
|
||||
--security-opt seccomp=/etc/docker/seccomp/sandbox.json \
|
||||
--security-opt apparmor=sandbox-profile \
|
||||
--tmpfs /tmp:size=100m,mode=1777 \
|
||||
--tmpfs /run:size=10m,mode=0755 \
|
||||
--user=1000:1000 \
|
||||
sandbox-runner \
|
||||
timeout 10 python3 /tmp/user_code.py
|
||||
```
|
||||
|
||||
这里有几个值得注意的点:
|
||||
|
||||
1. `--runtime=runsc`:使用 gVisor 运行时,从内核层面加强隔离
|
||||
2. `--network=none`:完全禁用网络
|
||||
3. `--read-only`:只读文件系统
|
||||
4. `--cap-drop=ALL`:去掉所有 Linux Capabilities
|
||||
5. `--security-opt seccomp=...`:自定义 Seccomp Profile
|
||||
6. `--security-opt apparmor=...`:自定义 AppArmor Profile
|
||||
7. `--user=1000:1000`:以非 root 用户运行
|
||||
8. `timeout 10`:10 秒超时
|
||||
|
||||
这套配置不是一次性就定下来的,是在实际运行中不断调整的。刚开始的时候,因为 Seccomp 太严格导致很多正常代码跑不了,我就把 `SCMP_ACT_ERRNO` 改成 `SCMP_ACT_LOG` 先观察了一周,收集了所有被拒绝的系统调用,确认哪些是必须的后才逐步放开。
|
||||
|
||||
还有个经验:每次更新语言运行时版本后,都要重新测试 Seccomp Profile,因为新版本可能会用到之前不需要的系统调用。我就吃过这个亏,升级 Python 版本后用户代码全报错,排查了半天才发现是 Seccomp 屏蔽了一个新加的系统调用。
|
||||
|
||||
## **一些血的教训**
|
||||
|
||||
最后分享几个我在实战中遇到的真实问题:
|
||||
|
||||
**1. docker.sock 千万别挂进去**
|
||||
|
||||
这个前面提过了,但还是要强调。我见过不止一次,有人为了在容器里操作 Docker,把 `/var/run/docker.sock` 挂进容器。这就等于把宿主机的 root 权限交出去了。
|
||||
|
||||
**2. 别用 latest 标签跑生产环境**
|
||||
|
||||
镜像版本要固定,用 digest 更好。恶意镜像替换这种攻击虽然少见,但不是不可能。
|
||||
|
||||
**3. 定期更新内核**
|
||||
|
||||
Docker 的安全很大程度上依赖内核。脏牛漏洞(CVE-2016-5195)可以直接从容器里逃逸,就是因为内核的问题。保持内核更新是最基本的安全措施。
|
||||
|
||||
**4. CVE-2019-5736 的启示**
|
||||
|
||||
这个漏洞非常经典。攻击者通过覆写宿主机上的 runc 二进制文件,实现了容器逃逸。根本原因是 runc 进入容器命名空间时,`/proc/self/exe` 指向了宿主机的 runc 程序,而容器内的 root 用户可以写入这个文件。这个漏洞的修复方式是在 runc 进入容器前先克隆一份自身的二进制文件。这个案例告诉我们,不要低估攻击者的创造力,即使看起来很小的疏漏也可能被利用。
|
||||
|
||||
**5. 监控和日志不能少**
|
||||
|
||||
再安全的沙箱也需要监控。我会在宿主机上用 auditd 记录所有和容器相关的系统调用,用 Falco 做运行时安全检测。一旦发现异常行为(比如容器进程尝试访问 `/proc` 或 `/sys` 下的敏感文件),立即告警。
|
||||
|
||||
## **总结**
|
||||
|
||||
做 Docker 代码沙箱,没有银弹,只有纵深防御。单靠任何一层安全机制都不够可靠,必须把 Namespace、Cgroup、Capability、Seccomp、AppArmor、User Namespace、只读文件系统、网络隔离这些手段叠加起来,再加上 gVisor 这类沙箱运行时,才能把风险降到可接受的水平。
|
||||
|
||||
记住一个原则:**最小权限**。容器只需要能完成任务的最低权限,多给一点都不行。安全这事儿,宁可过度,不能不足。
|
||||
|
||||
还有就是,安全是一个持续的过程,不是一次性的配置。新的漏洞会不断出现,你的防御措施也要跟着更新。保持关注 Docker 和 Linux 内核的安全公告,及时打补丁,这是最基本的运维素养。
|
||||
@@ -0,0 +1,40 @@
|
||||
Podman是一个开源的容器管理工具,语法兼容 Docker,但是某些方面比原生 Docker 命令更好用,比如能够以非root用户的身份去运行容器,而且不需要守护进程。
|
||||
|
||||
Podman引入了一个注册表的概念,注册表就是包括 [docker.io](http://docker.io/) 在内的多个容器镜像源,默认的有 redhat docker fedora centos quay。
|
||||
|
||||
国内直接用 podman pull 拉取镜像会很慢,所以需要配置国内镜像源来加速访问。国内的镜像源有以下:
|
||||
|
||||
- docker官方中国区: https://registry.docker-cn.com
|
||||
- 网易: http://hub-mirror.c.163.com
|
||||
- USTC: http://docker.mirrors.ustc.edu.cn
|
||||
- 阿里云: http://<你的ID>.mirror.aliyuncs.com
|
||||
|
||||
|
||||
|
||||
#### 1.修改Podman的注册表配置文件
|
||||
|
||||
```vim /etc/containers/registries.conf
|
||||
|
||||
|
||||
unqualified-search-registries = ["docker.io"]
|
||||
|
||||
|
||||
[[registry]]
|
||||
prefix = "docker.io"
|
||||
location="d1234.mirror.aliyuncs.com"
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
注:此处location的值只需要填写 https:// 之后的内容就行了
|
||||
|
||||
#### 3.测试是否可以拉取对应的镜像
|
||||
|
||||
podman pull hello-world
|
||||
|
||||
|
||||

|
||||
|
||||
**注意:由于podman目前还不是很完善,好些镜像还是无法下载到(可以通过docker下载完镜像后导入到podman安装的那台机器上也是可以正常使用的)**
|
||||
@@ -0,0 +1,165 @@
|
||||
---
|
||||
page-title: Ubuntu 安装 Docker
|
||||
url: https://docker_practice.gitee.io/install/ubuntu.html
|
||||
date: 2024-04-07 14:50:23
|
||||
---
|
||||
|
||||
## Ubuntu 安装 Docker
|
||||
|
||||
> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.
|
||||
|
||||
## 准备工作
|
||||
|
||||
### 系统要求
|
||||
|
||||
Docker 支持以下版本的 [Ubuntu](https://ubuntu.com/server) 操作系统:
|
||||
|
||||
- Focal 20.04 (LTS)
|
||||
- Bionic 18.04 (LTS)
|
||||
- Xenial 16.04 (LTS)
|
||||
|
||||
Docker 可以安装在 64 位的 x86 平台或 ARM 平台上。Ubuntu 发行版中,LTS(Long-Term-Support)长期支持版本,会获得 5 年的升级维护支持,这样的版本会更稳定,因此在生产环境中推荐使用 LTS 版本。
|
||||
|
||||
### 卸载旧版本
|
||||
|
||||
旧版本的 Docker 称为 `docker` 或者 `docker-engine`,使用以下命令卸载旧版本:
|
||||
|
||||
```
|
||||
$ sudo apt-get remove docker \
|
||||
docker-engine \
|
||||
docker.io
|
||||
```
|
||||
|
||||
## 使用 APT 安装
|
||||
|
||||
由于 `apt` 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。
|
||||
|
||||
```
|
||||
$ sudo apt-get update
|
||||
|
||||
$ sudo apt-get install \
|
||||
apt-transport-https \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg-agent \
|
||||
software-properties-common
|
||||
```
|
||||
|
||||
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
|
||||
|
||||
为了确认所下载软件包的合法性,需要添加软件源的 `GPG` 密钥。
|
||||
|
||||
```
|
||||
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
|
||||
|
||||
|
||||
# 官方源
|
||||
# $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
```
|
||||
|
||||
然后,我们需要向 `sources.list` 中添加 Docker 软件源
|
||||
|
||||
```
|
||||
$ sudo add-apt-repository \
|
||||
"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
|
||||
$(lsb_release -cs) \
|
||||
stable"
|
||||
|
||||
|
||||
# 官方源
|
||||
# $ sudo add-apt-repository \
|
||||
# "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
|
||||
# $(lsb_release -cs) \
|
||||
# stable"
|
||||
```
|
||||
|
||||
> 以上命令会添加稳定版本的 Docker APT 镜像源,如果需要测试版本的 Docker 请将 stable 改为 test。
|
||||
|
||||
### 安装 Docker
|
||||
|
||||
更新 apt 软件包缓存,并安装 `docker-ce`:
|
||||
|
||||
```
|
||||
$ sudo apt-get update
|
||||
|
||||
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
|
||||
```
|
||||
|
||||
## 使用脚本自动安装
|
||||
|
||||
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统上可以使用这套脚本安装,另外可以通过 `--mirror` 选项使用国内源进行安装:
|
||||
|
||||
> 若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本
|
||||
|
||||
```
|
||||
# $ curl -fsSL test.docker.com -o get-docker.sh
|
||||
$ curl -fsSL get.docker.com -o get-docker.sh
|
||||
$ sudo sh get-docker.sh --mirror Aliyun
|
||||
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
|
||||
```
|
||||
|
||||
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定(stable)版本安装在系统中。
|
||||
|
||||
## 启动 Docker
|
||||
|
||||
```
|
||||
$ sudo systemctl enable docker
|
||||
$ sudo systemctl start docker
|
||||
```
|
||||
|
||||
## 建立 docker 用户组
|
||||
|
||||
默认情况下,`docker` 命令会使用 [Unix socket](https://en.wikipedia.org/wiki/Unix_domain_socket) 与 Docker 引擎通讯。而只有 `root` 用户和 `docker` 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 `root` 用户。因此,更好地做法是将需要使用 `docker` 的用户加入 `docker` 用户组。
|
||||
|
||||
建立 `docker` 组:
|
||||
|
||||
将当前用户加入 `docker` 组:
|
||||
|
||||
```
|
||||
$ sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
退出当前终端并重新登录,进行如下测试。
|
||||
|
||||
## 测试 Docker 是否安装正确
|
||||
|
||||
```
|
||||
$ docker run hello-world
|
||||
|
||||
Unable to find image 'hello-world:latest' locally
|
||||
latest: Pulling from library/hello-world
|
||||
d1725b59e92d: Pull complete
|
||||
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
|
||||
Status: Downloaded newer image for hello-world:latest
|
||||
|
||||
Hello from Docker!
|
||||
This message shows that your installation appears to be working correctly.
|
||||
|
||||
To generate this message, Docker took the following steps:
|
||||
1. The Docker client contacted the Docker daemon.
|
||||
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
|
||||
(amd64)
|
||||
3. The Docker daemon created a new container from that image which runs the
|
||||
executable that produces the output you are currently reading.
|
||||
4. The Docker daemon streamed that output to the Docker client, which sent it
|
||||
to your terminal.
|
||||
|
||||
To try something more ambitious, you can run an Ubuntu container with:
|
||||
$ docker run -it ubuntu bash
|
||||
|
||||
Share images, automate workflows, and more with a free Docker ID:
|
||||
https://hub.docker.com/
|
||||
|
||||
For more examples and ideas, visit:
|
||||
https://docs.docker.com/get-started/
|
||||
```
|
||||
|
||||
若能正常输出以上信息,则说明安装成功。
|
||||
|
||||
## 镜像加速
|
||||
|
||||
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](https://docker_practice.gitee.io/install/mirror.html)。
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [Docker 官方 Ubuntu 安装文档](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
|
||||
@@ -0,0 +1,156 @@
|
||||
### 准备在Ubuntu上安装Docker
|
||||
|
||||
你可以在Ubuntu 18.04、20.04、21.10和22.04版本上安装最新的Docker版本。支持x64_64/AMD64、ARMhf、ARM64和S390x架构。
|
||||
|
||||
有几种方法可以安装Docker,你的选择将取决于你需要的功能和你喜欢的软件包管理方法。你将在本指南中了解所有可能的选项。
|
||||
|
||||
#### 选择一个Docker Flavor
|
||||
|
||||
Linux上的Docker传统上是只用终端的。一个正常的安装包括Docker引擎,一个运行容器的守护程序,以及与守护程序互动的 `docker` 命令行界面(CLI)。
|
||||
|
||||
[Docker桌面](https://www.wbolt.com/go?_=df5dc7c8d0aHR0cHM6Ly93d3cuZG9ja2VyLmNvbS9wcm9kdWN0cy9kb2NrZXItZGVza3RvcA%3D%3D)是使用Docker的另一种方式。历史上只在Windows和Mac上可用,它在2022年5月推出了Linux版。Ubuntu上的Docker Desktop需要一个AMD64处理器和Ubuntu 21.10或22.04。它提供了一个管理容器的图形界面,一个集成的Kubernetes集群,以及对第三方扩展的支持。Docker Desktop还为你提供了 `docker` 和Docker Compose CLI。
|
||||
|
||||
Docker Desktop与Docker Engine不同,它使用虚拟机来运行你的容器(这就是为什么Windows和MacOS用户需要Docker Desktop)。尽管在Ubuntu上可以并排使用这两个版本,但你可能会发现 `docker` CLI版本之间的冲突。最好是选择一个并坚持使用。如果你对你的终端很满意,并且希望得到最好的支持和性能,那么就选择Docker Engine。Docker Desktop的虚拟机使其略显沉重,但如果你想要一个集成的用户界面或计划使用Kubernetes,则是更好的选择。
|
||||
|
||||
### 在Ubuntu上安装Docker引擎
|
||||
|
||||
Docker Engine是在Ubuntu上运行Docker的标准方式。它适用于所有支持的版本和环境,从本地的Ubuntu桌面机到你的Ubuntu服务器云主机。在你继续之前,请确保你运行的Ubuntu版本是18.04或更新的。
|
||||
|
||||
Docker引擎有3种不同的安装方法:
|
||||
|
||||
1. 一个官方的Ubuntu `apt` 资源库
|
||||
|
||||
2. 一个独立的Debian软件包(`.deb`)
|
||||
|
||||
3. 一个自动安装脚本
|
||||
|
||||
选项1,即Ubuntu `apt` 软件库,是最常用的。它将Docker添加到你的软件包列表中,这样你就可以通过运行 `apt upgrade` 轻松安装未来的更新。选项2,不支持自动更新,但非常适合在有空气过滤的离线系统上安装Docker。自动安装脚本是最简单的入门方法,但[其配置只适合开发使用](https://www.wbolt.com/go?_=1dc9a57d32aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZW5naW5lL2luc3RhbGwvdWJ1bnR1LyNpbnN0YWxsLXVzaW5nLXRoZS1jb252ZW5pZW5jZS1zY3JpcHQ%3D)。Docker建议不要在生产环境中使用它。
|
||||
|
||||
当你与生产系统一起工作,想要轻松更新,或需要在所有环境中使用相同的机制时,使用 `apt` 仓库。
|
||||
|
||||
如果你想在自己的机器上试验Docker时获得快速的安装体验,请使用自动安装脚本。
|
||||
|
||||
#### 1. 使用apt仓库安装Docker
|
||||
|
||||
Docker publishes packages to its own `apt` repository. You’ll need to add this repository to your `apt` sources list, otherwise, your `apt` installation won’t be able to find the Docker packages.
|
||||
|
||||
Docker将软件包发布到自己的 `apt` 仓库。你需要将这个仓库添加到你的 `apt` 源列表中,否则,你的 `apt` 安装将无法找到Docker包。
|
||||
|
||||
首先,更新你现有的软件包列表,并为下面的步骤安装一些依赖项。 `ca-certificates`, `curl`, `gnupg` 和 `lsb_release` 工具将被用来下载正确的Docker `apt` 仓库细节和你系统的签名密钥。尽管你可能已经有了这些软件包,但确保它们是可用的也无妨。
|
||||
|
||||
$ sudo apt update
|
||||
|
||||
$ sudo apt install ca-certificates curl gnupg lsb-release
|
||||
|
||||
接下来,在apt上注册Docker的GPG密钥圈。这将让 `apt` 验证你安装的Docker包。
|
||||
|
||||
$ sudo mkdir -p /etc/apt/keyrings
|
||||
|
||||
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
|
||||
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
|
||||
`curl` 命令为Ubuntu下载Docker的GPG密钥,将其转换为标准的OpenGPG编码,并将其保存到apt的keyring目录中。 `chmod` 用来设置keyring文件的权限,以便 `apt` 能够可靠地检测到它。
|
||||
|
||||
现在你可以把Docker包的源代码添加到你的系统中。运行下面的命令:
|
||||
|
||||
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
这使用shell替换来自动检测你的系统架构,如AMD64或ARM64,并下载相应的软件包列表。它将使用之前添加的GPG密钥进行验证。该软件库被作为一个新的软件包列表添加到 `apt /etc/apt/sources.list.d directory.`中。
|
||||
|
||||
再次更新你的软件包列表,让 `apt` 知道Docker软件包的存在:
|
||||
|
||||
$ sudo apt update
|
||||
|
||||
现在你可以使用 `apt install` 命令将Docker的组件添加到你的系统中。你将会安装最新版本的Docker社区版(CE)。有三个软件包需要安装:
|
||||
|
||||
- `docker-ce`: Docker引擎守护程序。
|
||||
|
||||
- `docker-ce-cli`: 你将与之互动的Docker CLI。
|
||||
|
||||
- `containerd.io`: 被[称为containerd](https://www.wbolt.com/go?_=11e6738913aHR0cHM6Ly9jb250YWluZXJkLmlvLw%3D%3D)的容器运行时间,用于启动和运行你的容器。
|
||||
|
||||
$ sudo apt install docker-ce docker-ce-cli containerd.io
|
||||
|
||||
#### 2. 使用Debian Packagers安装Docker
|
||||
|
||||
官方的 `.deb` 包设置起来很简单,但你必须手动下载和安装每个新版本。软件包被托管在一个[简单的文件服务器](https://www.wbolt.com/go?_=f703ea2518aHR0cHM6Ly9kb3dubG9hZC5kb2NrZXIuY29tL2xpbnV4L3VidW50dS9kaXN0cw%3D%3D)上。首先浏览Docker的[发布说明页面](https://www.wbolt.com/go?_=42b1de2189aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZW5naW5lL3JlbGVhc2Utbm90ZXM%3D),找到最新的可用版本号,比如20.10.20。接下来,[前往containerd的GitHub页面](https://www.wbolt.com/go?_=ef964bbef7aHR0cHM6Ly9naXRodWIuY29tL2NvbnRhaW5lcmQvY29udGFpbmVyZC9yZWxlYXNlcw%3D%3D),也可以查看其版本号。它将看起来像1.6.8。
|
||||
|
||||
现在你可以运行下面的命令序列,为你的平台下载正确的软件包。将`<DOCKER_VERSION>` 和 `<CONTAINERD_VERSION>` 分别替换为当前Docker和containerd的版本号。
|
||||
|
||||
$ curl https://download.docker.com/linux/ubuntu/dists/$(lsb_release --codename | cut -f2)/pool/stable/$(dpkg --print-architecture)/docker-ce_<DOCKER_VERSION>~3-0~ubuntu-focal_amd64.deb -o docker-ce.deb
|
||||
|
||||
$ curl https://download.docker.com/linux/ubuntu/dists/$(lsb_release --codename | cut -f2)/pool/stable/$(dpkg --print-architecture)/docker-ce-cli_<DOCKER_VERSION>~3-0~ubuntu-focal_amd64.deb -o docker-ce-cli.deb
|
||||
|
||||
$ curl https://download.docker.com/linux/ubuntu/dists/$(lsb_release --codename | cut -f2)/pool/stable/$(dpkg --print-architecture)/containerd.io_<CONTAINERD_VERISON>-1_amd64.deb -o containerd.deb
|
||||
|
||||
你也可以通过在浏览器中访问你的Ubuntu版本的[目录列表](https://www.wbolt.com/go?_=f703ea2518aHR0cHM6Ly9kb3dubG9hZC5kb2NrZXIuY29tL2xpbnV4L3VidW50dS9kaXN0cw%3D%3D)来下载文件并检查所有可用的版本。
|
||||
|
||||
一旦你下载了Docker CE、CLI和containerd软件包,使用以下命令来安装它们:
|
||||
|
||||
$ sudo apt install ./docker-ce.deb ./docker-ce-cli.deb ./containerd.deb
|
||||
|
||||
如果你没有使用上面提供的脚本而手动下载软件包,请调整文件名。
|
||||
|
||||
#### 3. 使用安装脚本安装Docker
|
||||
|
||||
Docker的[便利脚本](https://www.wbolt.com/go?_=1dc9a57d32aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZW5naW5lL2luc3RhbGwvdWJ1bnR1LyNpbnN0YWxsLXVzaW5nLXRoZS1jb252ZW5pZW5jZS1zY3JpcHQ%3D)可以自动安装所有Docker组件。它是非交互式的,所以你可以坐下来等待,而你的系统已经为你准备好了。
|
||||
|
||||
首先下载该脚本:
|
||||
|
||||
$ curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
|
||||
现在以root权限运行该脚本:
|
||||
|
||||
$ sudo sh get-docker.sh
|
||||
|
||||
Executing docker install script
|
||||
|
||||
...
|
||||
|
||||
等到脚本达到完成。你应该看到一条确认信息,显示Docker已经安装。
|
||||
|
||||
这个脚本是为了提供一个通用的解决方案。如果不直接修改脚本的源代码,你就无法定制它的功能。它也不是为执行Docker更新而设计的,因为它不会将依赖关系更新到最新版本。
|
||||
|
||||
#### 确保Docker引擎在Ubuntu上运行
|
||||
|
||||
无论你使用哪种安装方法,你都应该通过启动一个容器来确保一切正常:
|
||||
|
||||
$ sudo docker run hello-world
|
||||
|
||||
你会看到Docker客户端将 `hello-world:image` 从Docker Hub仓库拉到你的机器上:
|
||||
|
||||
Unable to find image 'hello-world:latest' locally
|
||||
|
||||
latest: Pulling from library/hello-world
|
||||
|
||||
2db29710123e: Pull complete
|
||||
|
||||
Digest: sha256:7d246653d0511db2a6b2e0436cfd0e52ac8c066000264b3ce63331ac66dca625
|
||||
|
||||
Status: Downloaded newer image for hello-world:latest
|
||||
|
||||
然后,一个新的容器将自动启动。hello-world “的启动镜像被配置为运行一个简单的命令,输出一些关于Docker的基本信息,然后立即退出:
|
||||
|
||||
Hello from Docker!
|
||||
|
||||
This message shows that your installation appears to be working correctly.
|
||||
|
||||
To generate this message, Docker took the following steps:
|
||||
|
||||
1. The Docker client contacted the Docker daemon.
|
||||
|
||||
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
|
||||
|
||||
(amd64)
|
||||
|
||||
3. The Docker daemon created a new container from that image which runs the
|
||||
|
||||
executable that produces the output you are currently reading.
|
||||
|
||||
4. The Docker daemon streamed that output to the Docker client, which sent it
|
||||
|
||||
to your terminal.
|
||||
|
||||
看到上面显示的输出意味着Docker已经可以使用了。守护进程成功地从Docker Hub拉出了[hello-world:latest image](https://www.wbolt.com/go?_=2712683cf7aHR0cHM6Ly9odWIuZG9ja2VyLmNvbS9fL2hlbGxvLXdvcmxk),并使用它启动了一个容器。从 “Hello from Docker “一行开始的所有内容都是由容器内的进程发出的。
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
--iptables=false
|
||||
|
||||
[https://docs.docker.com/engine/reference/commandline/dockerd/](https://docs.docker.com/engine/reference/commandline/dockerd/)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
curl -x http://172.1.40.147:7897 https://registry-1.docker.io/v2/
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
## 检查 Docker 是否安装[](https://amprobe.amuluze.com/doc/views/docker.html#%E6%A3%80%E6%9F%A5-docker-%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ docker -v
|
||||
Docker version 25.0.3, build 4debf41
|
||||
```
|
||||
|
||||
如果输出以上信息,则表示 Docker 已经安装。
|
||||
|
||||
## 安装并启动 Docker[](https://amprobe.amuluze.com/doc/views/docker.html#%E5%AE%89%E8%A3%85%E5%B9%B6%E5%90%AF%E5%8A%A8-docker)
|
||||
|
||||
> 如果已经安装 Docker,则跳过该步骤,下面 `Ubuntu 22.04` 安装 Docker 的步骤
|
||||
|
||||
### 安装[](https://amprobe.amuluze.com/doc/views/docker.html#%E5%AE%89%E8%A3%85)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo apt update
|
||||
```
|
||||
|
||||
### 安装依赖[](https://amprobe.amuluze.com/doc/views/docker.html#%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
|
||||
```
|
||||
|
||||
**添加 `docker` 的官方 `GPG` 密钥**
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
```
|
||||
|
||||
**添加 `docker` 官方库**
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
```
|
||||
|
||||
**更新 `ubuntu` 源列表**
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo apt update
|
||||
```
|
||||
|
||||
**安装最新 `docker-ce`**
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 检查[](https://amprobe.amuluze.com/doc/views/docker.html#%E6%A3%80%E6%9F%A5)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo systemctl status docker
|
||||
● docker.service - Docker Application Container Engine
|
||||
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Thu 2024-01-11 21:37:25 CST; 17s ago
|
||||
TriggeredBy: ● docker.socket
|
||||
Docs: https://docs.docker.com
|
||||
Main PID: 51116 (dockerd)
|
||||
Tasks: 8
|
||||
Memory: 25.8M
|
||||
CPU: 213ms
|
||||
CGroup: /system.slice/docker.service
|
||||
└─51116 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
|
||||
|
||||
Jan 11 21:37:24 hecs-398409 systemd[1]: Starting Docker Application Container Engine...
|
||||
Jan 11 21:37:24 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:24.527450424+08:00" level=info msg="Starting up"
|
||||
Jan 11 21:37:24 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:24.530213574+08:00" level=info msg="detected 127.0.0.>
|
||||
Jan 11 21:37:24 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:24.625114442+08:00" level=info msg="Loading container>
|
||||
Jan 11 21:37:25 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:25.269789956+08:00" level=info msg="Loading container>
|
||||
Jan 11 21:37:25 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:25.300116824+08:00" level=info msg="Docker daemon" co>
|
||||
Jan 11 21:37:25 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:25.300207349+08:00" level=info msg="Daemon has comple>
|
||||
Jan 11 21:37:25 hecs-398409 dockerd[51116]: time="2024-01-11T21:37:25.331215962+08:00" level=info msg="API listen on /ru>
|
||||
Jan 11 21:37:25 hecs-398409 systemd[1]: Started Docker Application Container Engine.
|
||||
```
|
||||
|
||||
### 卸载[](https://amprobe.amuluze.com/doc/views/docker.html#%E5%8D%B8%E8%BD%BD)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo apt-get purge docker.io
|
||||
$ sudo rm -rf /var/lib/docker
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
```Plain Text
|
||||
sudo mkdir -p /etc/docker
|
||||
sudo tee /etc/docker/daemon.json <<-'EOF'
|
||||
{
|
||||
"registry-mirrors": ["https://dqf81q1k.mirror.aliyuncs.com"]
|
||||
}
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
```Plain Text
|
||||
sudo mkdir -p /etc/docker
|
||||
sudo tee /etc/docker/daemon.json <<-'EOF'
|
||||
{
|
||||
"registry-mirrors": ["https://dqf81q1k.mirror.aliyuncs.com"]
|
||||
}
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
## 检查 `docker-compose` 是否安装[](https://amprobe.amuluze.com/doc/views/docker-compose.html#%E6%A3%80%E6%9F%A5-docker-compose-%E6%98%AF%E5%90%A6%E5%AE%89%E8%A3%85)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ docker-compose -v
|
||||
docker-compose version 1.29.2, build unknown
|
||||
```
|
||||
|
||||
如果输出以上信息,则表示 `docker-compose` 已经安装。
|
||||
|
||||
## 安装 `docker-compose`[](https://amprobe.amuluze.com/doc/views/docker-compose.html#%E5%AE%89%E8%A3%85-docker-compose)
|
||||
|
||||
> 已经安装 `docker-compose` 跳过该步骤。
|
||||
|
||||
> 前提:已经安装 `docker`。
|
||||
|
||||
下载 `docker-compose`程序包:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ curl -L https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
添加可执行权限:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ sudo chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
检查安装结果:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
# 命令
|
||||
$ sudo docker-compose -v
|
||||
|
||||
# 结果
|
||||
$ docker-compose -v
|
||||
docker-compose version 1.28.5, build c4eb3a1f
|
||||
```
|
||||
|
||||
## 安装 `amprobe`[](https://amprobe.amuluze.com/doc/views/docker-compose.html#%E5%AE%89%E8%A3%85-amprobe)
|
||||
|
||||
在 `/data` 目录下新建 `amprobe` 目录,目录结构如下:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ mkdir -p /data/amprobe
|
||||
$ cd /data/amprobe
|
||||
$ tree
|
||||
.
|
||||
└── docker-compose.yml
|
||||
```
|
||||
|
||||
在 `/data/amprobe` 目录下新建 `docker-compose.yml` 文件,输入如下内容:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
version: '3'
|
||||
services:
|
||||
amprobe:
|
||||
image: amuluze/amprobe:v1.3.3
|
||||
container_name: amprobe
|
||||
network_mode: "host"
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /:rootfs:ro
|
||||
```
|
||||
|
||||
### 启动服务[](https://amprobe.amuluze.com/doc/views/docker-compose.html#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
$ cd /data/amprobe
|
||||
$ docker-compose up -d
|
||||
docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
308e422b4a96 amuluze/amprobe:v1.3.3 "/usr/bin/supervisor…" 6 minutes ago Up 6 mi
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
shell k8s.gcr.io/coredns/coredns => m.daocloud.io/k8s.gcr.io/coredns/coredns
|
||||
|
||||
|
||||
https://mirrors.kb.cx/
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
## 同步设备
|
||||
- [x] 同步 jar
|
||||
- [x] 同步 vue
|
||||
- [x] 同步 www
|
||||
- [x] docker 参数 ipc
|
||||
- [x] application-limit.yml 下的权限参数
|
||||
- [x] lib
|
||||
- [x] libsyd.so
|
||||
- [x] libsyd1118.so
|
||||
- [x] libsyd1308.so
|
||||
- [x] 数据库参数
|
||||
|
||||
|
||||
## 问题
|
||||
1. 服务端:销毁主密钥,液晶卡死。看不到主密钥异常状态。
|
||||
2. 密码卡:主密钥生成后,调用统一接口 backupLMK 概率性出错。
|
||||
3. 高压检测值为 0 ,导致主密钥销毁。
|
||||
4. %% hsm 开机未自动启动。8886 端口没有。 %%
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
### 什么是 cgroup
|
||||
|
||||
Cgroup 是一个 Linux 内核特性,对一组进程的资源使用(CPU、内存、磁盘 I/O 和网络等)进行限制、审计和隔离。
|
||||
|
||||
cgroups(Control Groups) 是 linux 内核提供的一种机制,这种机制可以根据需求把一系列系统任务及其子任务整合 (或分隔) 到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。简单说,cgroups 可以限制、记录任务组所使用的物理资源。本质上来说,cgroups 是内核附加在程序上的一系列钩子 (hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。
|
||||
### 什么是 cgroupfs
|
||||
|
||||
docker 默认的 Cgroup Driver 是 cgroupfs
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
$ docker info | grep cgroup
|
||||
Cgroup Driver: cgroupfs
|
||||
```
|
||||
|
||||
Cgroup 提供了一个原生接口并通过 cgroupfs 提供(从这句话我们可以知道 cgroupfs 就是 Cgroup 的一个接口的封装)。类似于 procfs 和 sysfs,是一种虚拟文件系统。并且 cgroupfs 是可以挂载的,默认情况下挂载在 /sys/fs/cgroup 目录。
|
||||
|
||||
什么是 Systemd?
|
||||
|
||||
Systemd 也是对于 Cgroup 接口的一个封装。systemd 以 PID1 的形式在系统启动的时候运行,并提供了一套系统管理守护程序、库和实用程序,用来控制、管理 Linux 计算机操作系统资源。
|
||||
|
||||
### 为什么使用 systemd 而不是 croupfs
|
||||
|
||||
这里引用以下 kubernetes 官方的原话[1]:
|
||||
|
||||
❝
|
||||
|
||||
当某个 Linux 系统发行版使用 systemd[2] 作为其初始化系统时,初始化进程会生成并使用一个 root 控制组(cgroup),并充当 cgroup 管理器。Systemd 与 cgroup 集成紧密,并将为每个 systemd 单元分配一个 cgroup。你也可以配置容器运行时和 kubelet 使用 cgroupfs。连同 systemd 一起使用 cgroupfs 意味着将有两个不同的 cgroup 管理器。
|
||||
|
||||
单个 cgroup 管理器将简化分配资源的视图,并且默认情况下将对可用资源和使用 中的资源具有更一致的视图。 当有两个管理器共存于一个系统中时,最终将对这些资源产生两种视图。在此领域人们已经报告过一些案例,某些节点配置让 kubelet 和 docker 使用 cgroupfs,而节点上运行的其余进程则使用 systemd; 这类节点在资源压力下 会变得不稳定。
|
||||
|
||||
ubuntu 系统,debian 系统,centos7 系统,都是使用 systemd 初始化系统的。systemd 这边已经有一套 cgroup 管理器了,如果容器运行时和 kubelet 使用 cgroupfs,此时就会存在 cgroups 和 systemd 两种 cgroup 管理器。也就意味着操作系统里面存在两种资源分配的视图,当操作系统上存在 CPU,内存等等资源不足的时候,操作系统上的进程会变得不稳定。
|
||||
|
||||
我们可以简单得理解为一山不要容二虎,一个国家只能有一个国王。
|
||||
|
||||
注意事项: 不要尝试修改集群里面某个节点的 cgroup 驱动,如果有需要,最好移除该节点重新加入。
|
||||
|
||||
### 如何修改 docker 默认的 cgroup 驱动
|
||||
|
||||
增加 "exec-opts": ["native.cgroupdriver=systemd"] 配置 , 重启 docker 即可
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
$ cat /etc/docker/daemon.json
|
||||
{
|
||||
"exec-opts": ["native.cgroupdriver=systemd"],
|
||||
"registry-mirrors": [
|
||||
"https://docker.mirrors.ustc.edu.cn",
|
||||
"http://hub-mirror.c.163.com"
|
||||
],
|
||||
"max-concurrent-downloads": 10,
|
||||
"log-driver": "json-file",
|
||||
"log-level": "warn",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "3"
|
||||
},
|
||||
"data-root": "/var/lib/docker"
|
||||
}
|
||||
```
|
||||
|
||||
### kubelet 配置 cgroup 驱动
|
||||
|
||||
参考官方[3]
|
||||
|
||||
❝
|
||||
|
||||
说明: 在版本 1.22 中,如果用户没有在 KubeletConfiguration 中设置 cgroupDriver 字段, kubeadm init 会将它设置为默认值 systemd。
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
# kubeadm-config.yaml
|
||||
kind: ClusterConfiguration
|
||||
apiVersion: kubeadm.k8s.io/v1beta3
|
||||
kubernetesVersion: v1.21.0
|
||||
---
|
||||
kind: KubeletConfiguration
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
cgroupDriver: systemd
|
||||
```
|
||||
|
||||
然后使用 kubeadm 初始化
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
$ kubeadm init --config kubeadm-config.yaml
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
ps axf | grep docker | grep -v grep | awk '{print "kill -9 " $1}' | sh
|
||||
|
||||
rm -rf /var/lib/docker
|
||||
|
||||
rm -rf /var/lib/containerd
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||

|
||||
|
||||
写 Docker 的朋友大概都经历过一个阶段:在单机上玩得飞起 docker run 用得滚瓜烂熟。但当业务扩展,需要两台服务器上的容器直接“对话”时,问题就来了。
|
||||
|
||||
默认的 bridge 网络只能管自家一亩三分地,跨主机通信怎么办?难道只能靠繁琐的端口映射和复杂的 iptables 转发?
|
||||
|
||||
当然不是。Docker 早已为我们准备了三把利剑:**Overlay、Macvlan 和 IPvlan**。
|
||||
|
||||

|
||||
|
||||
Docker官网的几种网络模式
|
||||
|
||||
今天这篇文章,我们就从原理到实战,彻底把这三者讲清楚,顺便带你避开那些文档里不常说的“坑”。
|
||||
|
||||
## 一、 为什么我们需要跨主机通信?
|
||||
|
||||
在默认的 bridge 模式下,Docker 容器通过 NAT(网络地址转换)访问外部。外部要访问容器,必须通过**端口映射**。
|
||||
|
||||
**痛点很明显:**
|
||||
|
||||
1. **端口管理地狱**:几十个微服务,端口号记不住,还容易冲突。
|
||||
2. **性能损耗**:NAT 转换在大流量下是 CPU 杀手。
|
||||
3. **IP 不可见**:容器没有真实局域网 IP,传统的监控、服务发现工具无法直接通过 IP 管理容器。
|
||||
|
||||
我们想要的是:**容器像虚拟机一样,拥有局域网内的独立 IP,不仅跨主机能通,还能和物理机直接互联。**
|
||||
|
||||
## 二、 方案一:Overlay 网络 (Docker Swarm 的默认选择)
|
||||
|
||||
如果你用过 Docker Swarm 或 K8s (Flannel/Calico),对 Overlay 应该不陌生。
|
||||
|
||||
### 1. 原理核心:VXLAN 隧道
|
||||
|
||||
Overlay 网络是在现有的物理网络(Underlay)之上,构建了一个虚拟网络。
|
||||
|
||||
- **封装**:当主机 A 的容器发包给主机 B 的容器时,Docker 会把原始数据包封装在一个 **VXLAN** 包(UDP 4789端口)里。
|
||||
- **传输**:这个 UDP 包像普通数据一样在物理网络传输。
|
||||
- **解封**:到达主机 B 后,Docker 拆开 UDP 包,拿出原始数据,塞给目标容器。
|
||||
|
||||
> **通俗理解:** 就像寄快递。原本的信件(容器数据)被装进了一个快递盒(VXLAN 封装),快递员(物理网络)只看快递单(物理 IP),不管盒子里装啥。到了目的地,拆盒取信。
|
||||
|
||||
### 2. 实战测试
|
||||
|
||||
Overlay 需要依赖键值存储(KV Store)来同步网络信息,最简单的办法是使用 Docker Swarm 模式(无需安装额外组件),早期的Docker版本(17.03前)配置中也可使用外部组件如consul、etcd等同步信息
|
||||
|
||||
**环境准备:**
|
||||
|
||||
- Node 1 (Manager): 172.21.14.5
|
||||
- Node 2 (Worker): 172.21.14.6
|
||||
- Docker环境 (我的 Docker version 26.1.5+dfsg1, build a72d7cd , 带docker swarm)
|
||||
|
||||
**核心端口要求(Swarm 集群必需)**
|
||||
|
||||
- 2377/tcp:Swarm 集群管理通信
|
||||
- 7946/tcp/udp:节点间通信(覆盖网络发现)
|
||||
- 4789/udp:Overlay 网络数据传输(VXLAN)
|
||||
|
||||
**Step 1: 初始化 Swarm (node1) , 加入Swarm集群(node2)**
|
||||
|
||||
```bash
|
||||
# node1上 Manager
|
||||
docker swarm init --advertise-addr 172.21.14.5
|
||||
# 执行后会给出一个 token 命令,复制该命令到 Node 2 执行即可加入集群 ,也会打开了相应的端口
|
||||
|
||||
# node2上 Worker
|
||||
docker swarm join --token SWMTKN-1-028glf3487mka3134oot1z2rxfmbisqj7jaqan9gayvmwjjcpl-e5hpu73a6fvegdm5xi10otl15 172.21.14.5:2377
|
||||
|
||||
# node1上 Manager
|
||||
docker node ls
|
||||
```
|
||||
|
||||

|
||||
|
||||
运行docker swarm init后
|
||||
|
||||

|
||||
|
||||
执行完后已查看到一个节点
|
||||
|
||||

|
||||
|
||||
加入docker
|
||||
|
||||

|
||||
|
||||
已经有两个节点了
|
||||
|
||||
|
||||
|
||||
**Step 2: 创建 Overlay 网络,**Swarm 会自动同步到node2 工作节点
|
||||
|
||||
```bash
|
||||
docker network create -d overlay --attachable my-overlay
|
||||
```
|
||||
|
||||
_注意:加上 **--attachable** 才能让独立的容器(非 Swarm Service)也能使用这个网络。_
|
||||
|
||||
|
||||
|
||||
**Step 3: 跨主机验证** 在 Node 1 运行容器:
|
||||
|
||||
```bash
|
||||
docker run -itd --name app1 --network my-overlay alpine
|
||||
```
|
||||
|
||||
在 Node 2 运行容器:
|
||||
|
||||
```bash
|
||||
docker run -itd --name app2 --network my-overlay alpine
|
||||
```
|
||||
|
||||
进入 Node 1 的容器 ping Node 2 的容器:
|
||||
|
||||
```bash
|
||||
docker exec -it app1 ping app2
|
||||
```
|
||||
|
||||

|
||||
|
||||
node1容器
|
||||
|
||||

|
||||
|
||||
node2容器测试
|
||||
|
||||
**结果:** 跨主机 Ping 通!且支持 DNS 解析(直接 Ping 容器名)。
|
||||
|
||||
**Step 4: 查看验证通信原理(vxlan)**
|
||||
|
||||
```bash
|
||||
# 通过查看详情检查是vxlan
|
||||
docker network inspect my-overlay
|
||||
|
||||
# 主机tcpdump抓包通信过程,之前说到的4789端口
|
||||
tcpdump -i eth0 port 4789 -c 10 -nn
|
||||
```
|
||||
|
||||

|
||||
|
||||
docker network inspect查看
|
||||
|
||||

|
||||
|
||||
tcpdump 抓取vxlan 4789端口数据包
|
||||
|
||||
**Step 5: 使用service进行验证**
|
||||
|
||||
```bash
|
||||
# node1上创建service1 并进行验证,并调度到node1节点
|
||||
docker service create --name service1 --network my-overlay --replicas 3 --constraint node.hostname==iZt4n4moivyl2vcl14obxcZ nginx:alpine
|
||||
|
||||
# 查看service
|
||||
docker service ls
|
||||
|
||||
# 查看容器
|
||||
docker ps
|
||||
|
||||
# node1上创建service2 并进行验证,并调度到node2节点
|
||||
docker service create --name service2 --network my-overlay --replicas 3 --constraint node.hostname==iZt4n4moivyl2vcl14obxdZ nginx:alpine
|
||||
|
||||
# 查看service
|
||||
docker service ls
|
||||
|
||||
# 在node2上查看容器,看调度情况
|
||||
docker ps
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
创建两个service进行验证
|
||||
|
||||

|
||||
|
||||
在node1上验证service2的连通性和nginx服务
|
||||
|
||||
## 三、 方案二:Macvlan (性能之王)
|
||||
|
||||
在验证了overlay后,看到官方还有IPvlan 、Macvlan,也想着来验证下。(**云服务器测试可能不通**)
|
||||
|
||||

|
||||
|
||||
官方network
|
||||
|
||||
Overlay 虽然方便,但 VXLAN 封包解包有 CPU 损耗。如果你的应用对网络延迟极度敏感(如高频交易、VoIP),Macvlan 是更好的选择。
|
||||
|
||||
### 1. 原理核心:子接口与独立 MAC
|
||||
|
||||
Macvlan 允许你在物理网卡(如 eth0)上创建多个“子接口”,每个子接口都有**独立的 MAC 地址**和 IP 地址。
|
||||
|
||||
- 从交换机来看,你的服务器仿佛变身成了接了 N 台设备的“交换机”。
|
||||
- 容器直接通过 Layer 2(数据链路层)通信,**无需 NAT,性能几乎等同于物理机。**
|
||||
|
||||
### 2. 实战测试
|
||||
|
||||
**前提:** 你的物理网卡需要支持(大部分都支持),且交换机开启混杂模式(Promiscuous Mode,如果在 VMware 虚拟机里测试,记得在 vSwitch 安全设置里开启“混杂模式”)。
|
||||
|
||||
**Step 1: 创建 Macvlan 网络 (两台主机都要执行)** 假设物理网段是 △192.168.1.0/24△,网关 △192.168.1.1△,我们划出一部分 IP 给容器用。
|
||||
|
||||
```bash
|
||||
# 创建macvlan网络
|
||||
docker network create -d macvlan \
|
||||
--subnet=172.18.200.0/24 \
|
||||
--gateway=172.18.200.254 \
|
||||
-o parent=enp3s0 macvlan-net
|
||||
9460256b68801cf46f5c21dff59c7a79d9a757010c5d6cc0414e5a1474d5433b
|
||||
```
|
||||
|
||||
**Step 2: 运行容器**
|
||||
|
||||
```bash
|
||||
# 创建一个172.18.200.222的容器
|
||||
docker run -d --name app \
|
||||
--network macvlan-net \
|
||||
--ip=172.18.200.222 \
|
||||
nginx:1.23-alpine
|
||||
```
|
||||
|
||||

|
||||
|
||||
运行macvlan网络和容器
|
||||
|
||||
**Step 3: 验证** 容器互 Ping、容器 Ping 外部物理机均可通, **这个过程可能需要一点儿时间。**
|
||||
|
||||

|
||||
|
||||
局域网访问macvlan ip
|
||||
|
||||

|
||||
|
||||
在物理机上进行验证
|
||||
|
||||
## 四、 方案三:IPvlan (云环境救星)
|
||||
|
||||
Macvlan 有个缺点:每个容器都要一个新的 MAC 地址。很多公有云厂商(AWS, 阿里云等)的虚拟交换机有限制,如果不认识这个 MAC 地址,直接丢弃数据包。这就导致 Macvlan 在云上往往跑不通。
|
||||
|
||||
**IPvlan** 应运而生。
|
||||
|
||||
### 1. 原理核心:共享 MAC
|
||||
|
||||
IPvlan 也有 L2 和 L3 两种模式,最常用的是 L2。
|
||||
|
||||
- 所有容器**共享宿主机物理网卡的 MAC 地址**。
|
||||
- 虽然 MAC 一样,但 IP 不同,Linux 内核根据 IP 地址进行流量分发。
|
||||
|
||||
### 2. 实战测试 (L2 模式)
|
||||
|
||||
该命令需要 Linux 内核 >= 4.2。
|
||||
|
||||
**Step 1: 创建 IPvlan 网络** 语法几乎和 Macvlan 一模一样,只是驱动变了。我仍然在
|
||||
|
||||
```bash
|
||||
# 创建一个ipvlan网络,创建之前要先删除之前的Macvlan(因为网络相同冲突了)
|
||||
docker network create -d ipvlan \
|
||||
--subnet=172.18.200.0/24 \
|
||||
--gateway=172.18.200.254 \
|
||||
-o parent=enp3s0 \
|
||||
-o ipvlan_mode=l2 \
|
||||
ipvlan-net
|
||||
```
|
||||
|
||||
**Step 2: 运行与验证**
|
||||
|
||||
```bash
|
||||
# 运行一个ipvlan-net 容器
|
||||
docker run -d --name app \
|
||||
--network ipvlan-net \
|
||||
--ip=172.18.200.222 nginx:1.23-alpine
|
||||
```
|
||||
|
||||

|
||||
|
||||
本机验证ip及网络
|
||||
|
||||
|
||||
|
||||
## 五、 总结:我该选哪个?
|
||||
|
||||
为了方便大家决策,我做了一张对比表:
|
||||
|
||||
|特性|Overlay|Macvlan|IPvlan (L2)|
|
||||
|---|---|---|---|
|
||||
|应用场景|容器编排 (Swarm/K8s)|传统应用迁移、高性能需求|公有云环境、MAC受限环境|
|
||||
|配置难度|简单 (Swarm原生)|中等 (需规划网段)|中等|
|
||||
|性能|有损耗 (VXLAN封装)|极高 (接近物理机)|极高|
|
||||
|网络层级|纯虚拟网络|物理网络二层打通|物理网络二层打通|
|
||||
|MAC地址|虚拟 MAC|每个容器独立 MAC|共享宿主机 MAC|
|
||||
|最大缺点|只有 Docker 内部互通|容易耗尽交换机 MAC 表|旧内核不支持|
|
||||
|
||||
**极简决策树:**
|
||||
|
||||
1. 如果在云服务器上跑,且不能申请辅助网卡 -> 选 **Overlay**。
|
||||
2. 如果是自建机房,追求极致性能 -> 选 **Macvlan**。
|
||||
3. 如果是自建机房,但交换机限制了 MAC 地址数量 -> 选 **IPvlan**。
|
||||
|
||||
|
||||
|
||||
**最后一步:**
|
||||
|
||||
看完原理和实验,建议你找两台虚拟机(VirtualBox 或 VMWare),按照文章里的命令敲一遍。网络这东西,**纸上得来终觉浅,绝知此事要 Ping 通!**
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
*先说结论:可以安装,但只能安装到 1.7 版本,如果镜像所需 Docker 版本或 Linux 内核版本更高的话,就没办法(比如 SQL Server),需要先升级内核**
|
||||
|
||||
1、先确认 yum 源的可用性
|
||||
|
||||
```Plain Text
|
||||
yum makecache
|
||||
```
|
||||
|
||||
如果不可用,则先更换源地址,如阿里云:https://developer.aliyun.com/mirror/
|
||||
|
||||
```Plain Text
|
||||
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-6.10.repo
|
||||
```
|
||||
|
||||
2、如果可以的话,先进行一下系统组件的升级
|
||||
|
||||
```Plain Text
|
||||
yum update
|
||||
```
|
||||
|
||||
3、安装 epel 源(貌似没什么用)
|
||||
|
||||
```Plain Text
|
||||
yum install epel-release
|
||||
```
|
||||
|
||||
如果安装后执行 yum makecache 出错:
|
||||
|
||||
Error: Cannot retrieve repository metadata (repomd.xml) for repository: epel. Please verify its path and try again
|
||||
|
||||
则需要打开 /etc/yum.repos.d/epel.repo 文件,将其中所有 mirrorlist=https://... 中的 https 改为 http 即可。
|
||||
|
||||
其实好像是因为系统里的证书过期或者什么原因,对 https 解析失败导致的,这只是个临时处理办法。
|
||||
|
||||
参考:https://www.cnblogs.com/z-books/p/9037432.html
|
||||
|
||||
4、安装一些基础环境
|
||||
|
||||
```Plain Text
|
||||
yum install -y yum-utils device-mapper-persistent-data lvm2
|
||||
```
|
||||
|
||||
5、下载并安装 docker 包:
|
||||
|
||||
其实按说安装 epel 源后,直接执行 yum install docker-io 就可以安装 docker 了
|
||||
|
||||
但应该是 docker 已经停止了对 6 版本的维护,这样直接安装是搜不到的:No package docker-io available
|
||||
|
||||
所以目前只能通过连接下载安装:
|
||||
|
||||
```Plain Text
|
||||
yum install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm
|
||||
rpm -ivhf docker-engine-1.7.1-1.el6.x86_64.rpm
|
||||
```
|
||||
|
||||
参考:https://blog.csdn.net/qq_41216743/article/details/93532921
|
||||
|
||||
https://www.cnblogs.com/jianshuai520/p/11898851.html
|
||||
|
||||
https://stackoverflow.com/questions/55134196/installing-docker-on-centos-6-after-removal-of-docker-io
|
||||
|
||||
6、安装完成后,就可以运行了:
|
||||
|
||||
```Plain Text
|
||||
service docker start
|
||||
```
|
||||
|
||||
7、设为开机自启:
|
||||
|
||||
```Plain Text
|
||||
chkconfig docker on
|
||||
```
|
||||
|
||||
参考:https://www.liquidweb.com/kb/how-to-install-docker-on-centos-6/
|
||||
|
||||
8、测试镜像:
|
||||
|
||||
可参考:https://www.cnblogs.com/xwgli/p/13571355.html
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
## **一、这套脚本解决什么问题?**
|
||||
|
||||
这份脚本不是只做“安装命令拼接”,而是覆盖完整链路:
|
||||
|
||||
1. **环境检测**:系统版本、架构、网络、磁盘空间
|
||||
2. **版本管理**:默认稳定版本 + 支持手动指定版本
|
||||
3. **安装执行**:自动配置官方源、安装 Engine / Containerd / Compose
|
||||
4. **配置优化**:自动生成 `daemon.json`(镜像加速、日志轮转等)
|
||||
5. **权限处理**:自动把用户加入 `docker` 组
|
||||
6. **结果验证**:命令可用、服务状态、测试容器、Compose 检查
|
||||
7. **失败排障**:安装日志和服务日志可追踪
|
||||
|
||||
一句话:
|
||||
**不只“装上 Docker”,而是“装好并可用”。**
|
||||
|
||||
## **二、支持范围(脚本兼容性)**
|
||||
|
||||
### **支持发行版**
|
||||
|
||||
- Ubuntu 20.04 / 22.04 / 24.04
|
||||
- Debian 11 / 12
|
||||
- CentOS 7 / 8 / 9
|
||||
- RHEL 8 / 9
|
||||
- Rocky Linux 8 / 9
|
||||
- AlmaLinux 8 / 9
|
||||
- Fedora 38 / 39
|
||||
|
||||
### **支持架构**
|
||||
|
||||
- `x86_64`(主流云服务器)
|
||||
- `aarch64` / ARM64(ARM 服务器、部分开发板)
|
||||
- ARMv7(旧款 ARM 设备)
|
||||
|
||||
### **基础要求**
|
||||
|
||||
- 需要 root 权限执行
|
||||
- 需要网络可访问软件源
|
||||
- 建议至少 5GB 可用磁盘(推荐 20GB+)
|
||||
|
||||
## **三、怎么使用(一键安装 + 参数控制)**
|
||||
|
||||
## **1)标准用法(推荐)**
|
||||
|
||||
```text
|
||||
# 下载脚本
|
||||
wget https://gitee.com/haonuli/itinstall-script/raw/main/install-docker-linux.sh
|
||||
|
||||
# 赋予执行权限
|
||||
chmod +x install-docker-linux.sh
|
||||
|
||||
# 运行(交互式)
|
||||
sudo bash install-docker-linux.sh
|
||||
```
|
||||
|
||||
## **2)常用参数**
|
||||
|
||||
```text
|
||||
sudo bash install-docker-linux.sh [选项]
|
||||
```
|
||||
|
||||
- `--version <版本号>`:安装指定 Docker 版本
|
||||
- `--no-compose`:不安装 Docker Compose
|
||||
- `--no-mirror`:不配置镜像加速(海外网络可用)
|
||||
- `--no-autostart`:不设置 Docker 开机自启
|
||||
- `--help`:查看帮助
|
||||
|
||||
### **示例**
|
||||
|
||||
```text
|
||||
# 默认安装(交互式)
|
||||
sudo bash install-docker-linux.sh
|
||||
|
||||
# 指定版本
|
||||
sudo bash install-docker-linux.sh --version 24.0.7
|
||||
|
||||
# 海外机器,不用国内镜像
|
||||
sudo bash install-docker-linux.sh --no-mirror
|
||||
|
||||
# 只要 Engine,不装 Compose
|
||||
sudo bash install-docker-linux.sh --no-compose
|
||||
```
|
||||
|
||||
## **四、脚本执行时,核心流程是什么?**
|
||||
|
||||
这套脚本按照固定顺序执行,保证结果稳定:
|
||||
|
||||
1. 检查 root 权限
|
||||
2. 检查网络联通性(国内/国际)
|
||||
3. 检查磁盘可用空间
|
||||
4. 识别发行版、版本、CPU 架构
|
||||
5. 检测本机是否已安装 Docker
|
||||
6. 选择默认版本或指定版本
|
||||
7. 更新包索引,安装依赖
|
||||
8. 添加 Docker 官方 GPG 密钥与仓库
|
||||
9. 安装 Docker Engine + Containerd
|
||||
10. 写入 `daemon.json`
|
||||
11. 配置用户组权限
|
||||
12. 启动服务并设置自启
|
||||
13. 执行安装后验证
|
||||
14. 输出安装摘要与日志位置
|
||||
|
||||
如果中途任意步骤失败,会立即退出并提示错误点,避免出现“半安装状态”。
|
||||
|
||||
## **五、如果机器上已经有 Docker,怎么处理?**
|
||||
|
||||
脚本会自动检测并给出选择:
|
||||
|
||||
1. 卸载后重装
|
||||
2. 保持现有版本不变
|
||||
3. 升级到指定版本
|
||||
4. 退出脚本
|
||||
|
||||
如果你选择“卸载后重装”,还会继续问你:
|
||||
|
||||
- 是否保留 `/var/lib/docker` 数据(镜像、容器、卷)
|
||||
|
||||
这一步很关键,建议生产环境谨慎确认。
|
||||
|
||||
## **六、为什么我会自动写 `daemon.json`?**
|
||||
|
||||
因为很多线上问题都不是“装不上”,而是“默认配置不适合实际场景”。
|
||||
|
||||
我在脚本里默认做了这些配置方向:
|
||||
|
||||
- 配置多个镜像加速源(提升拉镜像速度与可用性)
|
||||
- 限制容器日志文件大小(防止磁盘被日志打满)
|
||||
- 使用 `overlay2` 存储驱动
|
||||
- 开启 `live-restore`
|
||||
- 设置较合理的并发下载与文件句柄上限
|
||||
|
||||
你也可以按自己的场景再细调:
|
||||
|
||||
```text
|
||||
sudo nano /etc/docker/daemon.json
|
||||
sudo systemctl restart docker
|
||||
docker info
|
||||
```
|
||||
|
||||
## **七、安装完成后,我建议你做这 4 个验证**
|
||||
|
||||
```text
|
||||
docker --version
|
||||
docker info
|
||||
docker compose version
|
||||
docker run --rm hello-world
|
||||
```
|
||||
|
||||
如果这 4 项都通过,基础可用性就没问题了。
|
||||
|
||||
另外,若当前用户还需要 `sudo` 才能运行 Docker,一般是组权限没刷新:
|
||||
|
||||
```text
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
或者重新登录终端会话。
|
||||
|
||||
## **八、常见报错,优先这么排查**
|
||||
|
||||
### **1)权限报错**
|
||||
|
||||
- 现象:提示必须 root
|
||||
- 处理:确保使用 `sudo bash install-docker-linux.sh`
|
||||
|
||||
### **2)网络/GPG 相关报错**
|
||||
|
||||
- 先验证 DNS 和外网访问
|
||||
- 内网环境可配置代理后重试
|
||||
|
||||
### **3)Docker 服务启动失败**
|
||||
|
||||
优先看这条日志:
|
||||
|
||||
```text
|
||||
sudo journalctl -xeu docker.service --no-pager | tail -50
|
||||
```
|
||||
|
||||
其次检查 `daemon.json` 是否合法 JSON。
|
||||
|
||||
### **4)镜像拉取慢/失败**
|
||||
|
||||
- 检查镜像加速源是否生效:
|
||||
|
||||
```text
|
||||
docker info | grep -A 10 "Registry Mirrors"
|
||||
```
|
||||
|
||||
## **九、什么时候建议完全卸载 Docker?**
|
||||
|
||||
以下情况建议完整清理后重装:
|
||||
|
||||
- 多轮升级后环境状态混乱
|
||||
- 配置文件长期手工改动、难以追溯
|
||||
- 引擎版本和依赖出现兼容性问题
|
||||
|
||||
但请注意:
|
||||
**完整卸载前,务必确认是否保留 `/var/lib/docker` 数据。**
|
||||
|
||||
## **十、写在最后:脚本化的价值,不是“偷懒”,是“稳定可复制”**
|
||||
|
||||
我做这份脚本的核心目标是:
|
||||
|
||||
- 新机器快速初始化
|
||||
- 多环境配置保持一致
|
||||
- 出问题时可定位、可回滚
|
||||
|
||||
如果你也在做 Linux 下 Docker 标准化落地,希望这篇能帮你少踩一些坑。
|
||||
@@ -0,0 +1,136 @@
|
||||
|
||||
## 1.下载docker包
|
||||
|
||||
地址:[Index of linux/static/stable/aarch64/](https://download.docker.com/linux/static/stable/aarch64/)
|
||||
|
||||
选择自己合适的版本,我下载的是20.10.7
|
||||
|
||||
选择自己放置docker包的路径进行解压:
|
||||
|
||||
cd /home/huawei/zq
|
||||
|
||||
tar -zxvf docker-20.10.7.tgz
|
||||
|
||||

|
||||
|
||||
## 2. 解压docker并赋执行权限
|
||||
|
||||
chmod +x ./docker/*
|
||||
|
||||

|
||||
|
||||
将docker文件夹下的所有可执行文件复制到/usr/bin/下面:
|
||||
|
||||
cp /home/zq/docker/* /usr/bin/
|
||||
|
||||
## 3. 编辑docker的系统服务文件
|
||||
|
||||
vi /usr/lib/systemd/system/docker.service
|
||||
|
||||
将下面的内容复制到刚创建的docker.service文件中
|
||||
|
||||
[Unit]
|
||||
|
||||
Description=Docker Application Container Engine
|
||||
|
||||
Documentation=https://docs.docker.com
|
||||
|
||||
After=network-online.target firewalld.service
|
||||
|
||||
Wants=network-online.target
|
||||
|
||||
|
||||
|
||||
[Service]
|
||||
|
||||
Type=notify
|
||||
|
||||
ExecStart=/usr/bin/dockerd
|
||||
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
|
||||
LimitNOFILE=infinity
|
||||
|
||||
LimitNPROC=infinity
|
||||
|
||||
TimeoutStartSec=0
|
||||
|
||||
Delegate=yes
|
||||
|
||||
KillMode=process
|
||||
|
||||
Restart=on-failure
|
||||
|
||||
StartLimitBurst=3
|
||||
|
||||
StartLimitInterval=60s
|
||||
|
||||
|
||||
|
||||
[Install]
|
||||
|
||||
WantedBy=multi-user.target
|
||||
|
||||
## 4. 为docker.service添加执行权限
|
||||
|
||||
chmod +x /usr/lib/systemd/system/docker.service
|
||||
|
||||
并:
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
## 5. 启动dockerd
|
||||
|
||||
cd /usr/bin
|
||||
|
||||
执行:
|
||||
|
||||
dockerd
|
||||
|
||||
## 6. 编辑daemon.json
|
||||
|
||||
vi /etc/docker/daemon.json
|
||||
|
||||
并添加以下内容:
|
||||
|
||||
{
|
||||
|
||||
"registry-mirrors": ["https://registry.docker-cn.com"],
|
||||
"exec-opts": ["native.cgroupdriver=systemd"]
|
||||
}
|
||||
|
||||
并执行:
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
ps:联网的话建议将仓库镜像的地址换成国内阿里云的仓库地址,官方地址在联网情况下基本无法访问。
|
||||
|
||||
启动docker:
|
||||
|
||||
systemctl start docker
|
||||
|
||||
开机自启动:
|
||||
|
||||
systemctl enable docker
|
||||
|
||||
## 7. 验证docker
|
||||
|
||||
查看docker 版本:
|
||||
|
||||
docker -v
|
||||
|
||||

|
||||
|
||||
查看docker详细版本信息:
|
||||
|
||||
docker version
|
||||
|
||||

|
||||
|
||||
查看docker相关信息:
|
||||
|
||||
docker info
|
||||
|
||||

|
||||
|
||||
8. 至此,docker二进制包就完成了,可以进行下一步操作啦,下一篇完成[k8s](https://so.csdn.net/so/search?q=k8s&spm=1001.2101.3001.7020)的离线部署。
|
||||
Reference in New Issue
Block a user