Docker 之 远程连接

摘要

远程连接Docker有如下三种方式

方式一:开启 TCP(不带 TLS,仅用于内网调试)

  • 在docker服务端编辑 /etc/docker/daemon.json,加上:

1
2
3
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
}
  • 此时通过sudo systemctl restart docker重启 Docker会启动失败,原因是systemd会在docker启动命令中添加 -H fd://,其含义是从 systemd 传递进来的 socket 文件描述符监听 API 请求,当 Docker 被 systemd 启动并启用 socket activation(套接字激活)时,systemd 会预先创建 socket(比如 /var/run/docker.sock),然后再启动 dockerd,并通过文件描述符(fd)把这个 socket 传递给 dockerd。此时你在 dockerd 中看到的 -H fd:// 意思是:“不用自己打开 socket,去 systemd 那里拿吧。”

1
2
3
4
5
6
7
8
9
10
11
12
# 通过该命令可以获取 docker 的启动命令文件是 /usr/lib/systemd/system/docker.service
sudo systemctl status docker
# 查看 /usr/lib/systemd/system/docker.service,可以看到 docker的启动命令如下,可以看到 -H fd://
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
# 另外可以在与 docker.service 同目录下找到 docker.socket 文件,可以看到如下内容
ListenStream=/run/docker.sock # /run 目录是被软连接到 /var/run/

# 总结:通过systemd 启动 docker时,如果配置了 -H fd://,则 docker 会监听 /run/docker.sock 文件,实际上也就是 /var/run/docker.sock
# 而我们在/etc/docker/daemon.json中加上的"unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"实际上就是改写docker的启动参数,这就与 systemd 启动 docker 的 `-H fd://` 参数冲突了
# 此时我们可以不使用systemd 启动 docker,而是使用 docker daemon 启动 docker
sudo dockerd --containerd=/run/containerd/containerd.sock
# 但这样不利于 docker 的管理,因此最好的方式是禁用 `-H fd://`
  • 禁用 -H fd://(systemd 与 daemon.json 冲突的根源)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 方式一: 去掉 `-H fd://` 参数
sudo vi /usr/lib/systemd/system/docker.service

# 方式二(推荐): 创建 override.conf 文件,其作用是 覆盖 systemd 默认配置文件,只会覆盖指定的参数
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo vi /etc/systemd/system/docker.service.d/override.conf
# 填入
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock
# 这里顺便说一下,`--containerd=/run/containerd/containerd.sock` 是 docker 默认参数,
# 告诉 Docker 守护进程去连接已有的 containerd 实例,而不是自己启动一个新的。
# Docker 默认内部使用 containerd 来管理容器运行时,所以这条参数是明确指定要使用哪个 containerd 服务。
  • 重启 docker daemon

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker
  • 在客户端测试连接

1
docker -H tcp://远程IP:2375 ps
  • 在客户端加入环境变量后就不需要每次都加上 -H 参数了

1
2
3
4
5
# zsh 就换成 ~/.zshrc
echo "export DOCKER_HOST=tcp://远程IP:2375" >> ~/.bashrc
source ~/.bashrc
# 测试
docker ps
  • ❗不要开启无认证的 tcp://0.0.0.0:2375 在公网,这是裸奔的安全风险,任何人都能控制你的 Docker。

  • ✅ 推荐方式是:

    • 使用 tcp://0.0.0.0:2376 + --tlsverify
    • 或通过 ssh:// 隧道 访问 Docker

小贴士

  • 通过上面的介绍你应该搞明白一件事,就是我们可以不用在 /etc/docker/daemon.json 中配置远程连接,而是通过 systemd 来配置,即在 /usr/lib/systemd/system/docker.service 或者 /etc/systemd/system/docker.service.d/override.conf中配置。
1
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock
  • 那么问题来了:既然 systemd 就能搞定一切,那还要 /etc/docker/daemon.json 有什么用?答案是:可读性、可维护性、工具兼容性更强。
项目 daemon.json systemd ExecStart
语法 JSON Shell 命令行
适合设置 Hosts、日志、registry、镜像驱动等 启动命令、资源限制等
可读性 👍 结构化 👎 较长、容易出错
自动化支持 👍 工具友好 👎 需要 patch systemd

方式二:开启 TCP + TLS 安全访问(推荐用于公网)

  • 创建 TLS 证书,通过一个脚本实现,脚本名称 generate-docker-certs.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash

set -e

SERVER_IP=166.189.9.114 # 🚨 修改为你的 Docker 服务器 IP 或域名

echo "[+] 创建 CA..."
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 \
-key ca-key.pem -subj "/CN=docker-ca" -out ca.pem

echo "[+] 创建服务器私钥..."
openssl genrsa -out server-key.pem 4096

echo "[+] 创建 OpenSSL 配置文件..."
cat > extfile.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = ${SERVER_IP}

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
IP.1 = ${SERVER_IP}
EOF

echo "[+] 创建服务器证书签名请求 (CSR)..."
openssl req -new -key server-key.pem \
-out server.csr -config extfile.cnf

echo "[+] 签发服务器证书..."
openssl x509 -req -days 365 -in server.csr \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem -extensions v3_req -extfile extfile.cnf

echo "[+] 创建客户端私钥..."
openssl genrsa -out key.pem 4096

echo "[+] 创建客户端 CSR..."
openssl req -new -key key.pem -subj "/CN=client" -out client.csr

echo "[+] 签发客户端证书..."
cat > client-ext.cnf <<EOF
extendedKeyUsage = clientAuth
EOF

openssl x509 -req -days 365 -in client.csr \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out cert.pem -extfile client-ext.cnf

echo "[✓] 所有证书生成完成!"
echo
echo " 服务器证书: server-cert.pem, server-key.pem"
echo " 客户端证书: cert.pem, key.pem"
echo " CA根证书: ca.pem"
  • 执行脚本

1
2
chmod +x generate-docker-certs.sh
./generate-docker-certs.sh
  • 将以下证书文件部署到docker服务端

1
2
3
4
# 服务端:server-cert.pem, server-key.pem, ca.pem
cp server-cert.pem /etc/docker/server-cert.pem
cp server-key.pem /etc/docker/server-key.pem
cp ca.pem /etc/docker/ca.pem
  • 将以下证书文件部署到docker客户端

1
cert.pem, key.pem, ca.pem
  • 在docker服务端编辑 /etc/docker/daemon.json

1
2
3
4
5
6
7
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],
"tlsverify": true,
"tlscacert": "/etc/docker/ca.pem",
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem"
}
  • 禁用 -H fd://(systemd 与 daemon.json 冲突的根源)

1
2
3
4
5
6
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo vi /etc/systemd/system/docker.service.d/override.conf
# 填入
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock
  • 重启 docker daemon

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker
  • 在客户端测试连接

1
2
3
4
5
6
7
docker \
--tlsverify \
--tlscacert=ca.pem \
--tlscert=cert.pem \
--tlskey=key.pem \
-H tcp://166.189.9.114:2376 \
ps
  • 在客户端加入环境变量后就不需要每次都加上 -H 参数 和证书参数了

1
2
3
4
5
6
7
8
9
10
11
cat <<EOT >> ~/.bashrc
# 远程Docker IP和端口
export DOCKER_HOST=tcp://166.189.9.114:2376
# 用于通过 TLS(SSL)安全连接远程 Docker 守护进程,类似于 docker --tlsverify
export DOCKER_TLS_VERIFY=1
# 这个目录下要有这些证书文件:ca.pem, cert.pem, key.pem
export DOCKER_CERT_PATH=/path/to/certs
EOT
source ~/.bashrc
# 测试
docker ps

方式三:ssh:// 隧道访问

  • 最简单的方式就是使用 ssh 远程执行命令的方式,这种方式不需要任何配置,支持密码和证书认证,也支持指定端口,但这不是标准的远程连接docker方式,适用于偶尔访问的情况。

1
ssh -i ~/.ssh/lexing-test.pem -p22 centos@166.189.9.114 "docker ps"
1
2
3
# docker -H 远程访问 不支持在命令行直接输入密码或密钥文件,必须配置免密登录才可以,而且此时端口必须是默认的22
# 要求登录用户必须拥有docker的运行权限(加入 docker group)
docker -H ssh://centos@166.189.9.114 ps
  • 基于config配置(推荐)

    • 这种方式的优点是可以配置证书和端口
    • 在 ~/.ssh/config 中指定具体的密钥文件和端口,登录用户必须拥有docker的运行权限(加入 docker group)
    1
    2
    3
    4
    5
    Host mydocker
    HostName 166.189.9.114
    User centos
    Port 22
    IdentityFile ~/.ssh/my_docker_key
    • 测试
    1
    docker -H ssh://mydocker ps
  • 添加环境变量避免每次都加上 -H 参数

1
2
3
4
5
6
## 基于免密认证
echo "export DOCKER_HOST=ssh://user@host" >> ~/.bashrc
## 或者基于config的方式
echo "export DOCKER_HOST=ssh://mydocker" >> ~/.bashrc
source ~/.bashrc
docker ps

docker context: 同时连接多个远程docker

  • 上面介绍的三种方式,为了简化连接,都加上了DOCKER_HOST环境变量,但是DOCKER_HOST只能配置一个,如果我们要同时连接多个远程docker服务呢,每次切换DOCKER_HOST环境变量太过繁琐,可以通过下面的方式为每个远程docker服务创建一个 context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# TCP
docker context create remote-tcp \
--docker "host=tcp://166.189.9.114:2375"

# TCP + TLS
docker context create remote-tls \
--docker "host=tcp://166.189.9.114:2376,ca=/path/ca.pem,cert=/path/cert.pem,key=/path/key.pem"

# ssh隧道
docker context create remote-ssh --docker "host=ssh://mydocker"

# 使用
docker context ls # 列出所有context
docker context use remote-ssh # 切换到指定的context
docker ps # 查看容器
  • docker context 是 Docker CLI 的一个强大功能,它让你可以轻松地在 多个 Docker 后端环境之间切换,比如:

    • 本地 Docker(默认)
    • 远程主机的 Docker(通过 SSH 或 TCP/TLS)
    • Docker Desktop
    • Docker Swarm 或 Kubernetes(部分支持)
  • 就像你用 kubectl config use-context 切换 Kubernetes 集群,docker context 也允许你在多个 Docker 后端之间切换,而无需反复设置 DOCKER_HOST 环境变量或写繁琐的 SSH 隧道命令。

  • 常用命令一览

命令 说明
docker context ls 列出所有上下文
docker context use <name> 切换到指定 context
docker context create <name> --docker "Docker endpoint config" 创建一个新的 context
docker context rm <name> 删除 context
docker context inspect <name> 查看 context 详情
docker context update <name> --docker "Docker endpoint config" 更新 context 参数(Docker 24+ 支持)
docker context show 显示当前 context
docker context export <name> <file.tar> 导出 context 到tar文件
docker context import <name> <file.tar> 导入 context 从tar文件
  • Docker endpoint config

参数名 中文描述
from 复制指定 context 名称 的 Docker 端点配置
host 要连接的 Docker 端点地址
ca CA 签名的证书的路径
cert TLS 证书文件的路径
key TLS 密钥文件的路径
skip-tls-verify 跳过 TLS 证书验证(⚠️ 不建议用于生产环境)

三种远程连接 Docker 的方式及其优缺点总结

连接方式 优点 缺点 适用场景
1. SSH 方式 (ssh://) - 配置简单,无需额外开启 Docker TCP 端口和 TLS
- 安全性高,基于 SSH 加密和认证
- 不用开放额外端口,防火墙友好
- 易于用 SSH 代理和密钥管理
- 需要远程用户有 Docker 权限(如属于 docker 组)
- 连接速度可能受 SSH 连接影响
- 需要在本地安装并配置 SSH
开发环境、内网管理、小规模远程操作
2. TCP + TLS 方式 - 标准的远程 Docker API 访问
- 支持证书认证,安全性高
- 可以配置多个客户端和权限控制
- 适合自动化脚本、CI/CD 访问
- 配置较复杂,需要生成和管理 CA、服务器和客户端证书
- 需要开放 TCP 端口(如 2376),增加安全风险
- 证书配置错误容易导致连接失败
生产环境、自动化集成、需要高安全认证
3. TCP 明文访问(无 TLS) - 配置最简单,只需监听 TCP 端口
- 方便快速测试和调试
- 极度不安全,数据明文传输
- 任何人都可访问 Docker API,极易被攻击
- 生产环境严重不建议使用
仅限局域网内测试或极简环境