Docker 遠端連線 -- dockerd 命令詳解
配置 TLS
實現安全的 Docker 遠端連線。
GitHub:https://github.com/khs1994-docker/dockerd-tls
本機:macOS
遠端機:使用 VirtualBox
虛擬 CoreOS
(IP 192.168.57.110
)
目標:能在 macOS
遠端操作 CoreOS
。(注意不是 SSH 遠端登入)。dockerd
命令僅能在 Linux
下使用。
官方文件:https://docs.docker.com/edge/engine/reference/commandline/dockerd/
官方文件:https://docs.docker.com/engine/admin/
本文適合有一定 Linux 基礎的讀者閱讀,如果出現錯誤,請將各種操作之前修改過的檔案恢復原狀並刪除新增的環境變數。
我們已經知道 Docker 是客戶端/服務端架構。一般情況下我們使用的 Docker 客戶端/服務端都在本機(macOS、Windows 實際上是在本機啟動了一個虛擬機器,這裡指 Linux)。本文所指的情況是 Docker 客戶端與服務端不在同一主機上。
Dokcer 架構
Typically, you start Docker using operating system utilities. For debugging purposes, you can start Docker manually using the dockerd
command. You may need to use sudo
非安全的連線方式
先介紹 非安全
的連線方式。
服務端配置
CoreOS
請使用第二種方法,其他 Linux 系統配置時選擇以下兩種方法之一
通常的配置方法
docker.service
中 dockerd
的 -H
引數不能與 daemon.json
中的 hosts
鍵值對衝突。(其他引數同理)
新建 /etc/systemd/system/docker.service.d/docker.conf
檔案。
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
在 /etc/docker/daemon.json
(下文統一簡稱 daemon.json
)中寫入以下內容
{
"hosts":[
"unix:///var/run/docker.sock",
"tcp://0.0.0.0:2375"
]
}
該檔案必須符合 json 規範寫法,否則 Docker 將不能啟動。
重新啟動 Docker。
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
CoreOS 官方文件提供的方法
官方文件:https://coreos.com/os/docs/latest/customizing-docker.html
新建 /etc/systemd/system/docker-tcp.socket
檔案
[Unit]
Description=Docker Socket for the API
[Socket]
# ListenStream=127.0.0.1:2375
ListenStream=2375
BindIPv6Only=both
Service=docker.service
[Install]
WantedBy=sockets.target
重新啟動服務
$ sudo systemctl daemon-reload
$ sudo systemctl enable docker-tcp.socket
$ sudo systemctl stop docker
$ sudo systemctl start docker-tcp.socket
$ sudo systemctl start docker
注意:這種方法必須先啟動 docker-tcp.socket,再啟動 Docker,一定要注意啟動順序!
systemd socket 詳情請檢視:http://www.jinbuguo.com/systemd/systemd.socket.html
在客戶端測試連線
在 macOS
(下文中 macOS
一律代指 Docker 客戶端)上使用以下命令
$ docker -H 192.168.57.110:2375 info
成功輸出資訊,證明客戶端可以成功連線到遠端的服務端。
在 macOS
上遠端操作 CoreOS
上的 Docker
每次執行命令時必須加上 -H
引數(這樣太麻煩,我們可以通過將 Docker 命令 引數
配置成 環境變數
來簡化命令)。
在 macOS
上執行如下命令。
$ export DOCKER_HOST="tcp://0.0.0.0:2375"
$ docker info
這裡寫入的變數是臨時生效的,重新登入環境變數就消失了(下文同理,之後不再贅述),讓環境變數永久生效請寫入 ~/.bashrc
。
fish shell
本人 macOS
上使用的 shell 是 fish,這裡記錄一下 fish 中的操作,使用 bash 的讀者請忽略 fish 相關內容。
$ set -Ux DOCKER_HOST "tcp://0.0.0.0:2375"
# 以上命令寫入的環境變數是永久存在的,通過以下命令刪除環境變數
$ set -Ue DOCKER_HOST
配置安全連線
官方文件:https://docs.docker.com/engine/security/https/
上面我們配置的遠端連線是不安全的,只能用於測試環境中。在生產環境中需要配置 TLS
安全連線,只有擁有金鑰的客戶端,才能連線到遠端的服務端。
服務端配置
只能使用 Linux 下的 openssl
生成金鑰,macOS 下的不可以。在 CoreOS
下執行以下操作
手動執行命令生成證書(不推薦)
這一步較複雜,你可以跳過這一方法,使用容器生成證書。此方法來自 Docker 官方文件 https://docs.docker.com/engine/security/https/。
檔案總覽
├── ca-key.pem # 妥善保管,連線時用不到
├── ca.pem # clent & server
├── ca.srl # 用不到
├── cert.pem # client
├── client.csr # 請求檔案
├── extfile.cnf # 配置檔案
├── key.pem # client
├── server-cert.pem # server
├── server.csr # 請求檔案
└── server-key.pem # server
# 生成 CA 私鑰
$ openssl genrsa -aes256 -out ca-key.pem 4096
# 需要輸入兩次密碼(自定義)
# 生成 CA 公鑰
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# 輸入上一步中設定的密碼,然後需要填寫一些資訊
# 下面是伺服器證書生成
# 生成伺服器私鑰
$ openssl genrsa -out server-key.pem 4096
# 用私鑰生成證書請求檔案
$ openssl req -subj "/CN=localhost" -sha256 -new -key server-key.pem -out server.csr
$ echo subjectAltName = DNS:localhost,DNS:www.khs1994.com,DNS:tencent,IP:192.168.199.100,IP:192.168.57.110,IP:127.0.0.1 >> extfile.cnf
# 允許服務端哪些 IP 或 host 能被客戶端連線,下文會進行測試。
# DNS 我也不是很理解,這裡配置 localhost ,公共 DNS 解析的域名,/etc/hosts 檔案中的列表進行測試。
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
# 用 CA 來簽署證書
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem
-CAcreateserial -out server-cert.pem -extfile extfile.cnf
# 再次輸入第一步設定的密碼
# 下面是客戶端證書檔案生成
# 生成客戶端私鑰
$ openssl genrsa -out key.pem 4096
# 用私鑰生成證書請求檔案
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth >> extfile.cnf
# 用 CA 來簽署證書
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem
-CAcreateserial -out cert.pem -extfile extfile.cnf
# 再次輸入第一步設定的密碼
# 刪除檔案,更改檔案許可權
$ rm -v client.csr server.csr
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
把 ca.pem
server-cert.pem
server-key.pem
三個檔案移動到 /etc/docker/
資料夾中。
使用容器生成證書(推薦)
GitHub:https://github.com/khs1994-docker/dockerd-tls
方法來自 CoreOS
官方文件:https://coreos.com/os/docs/latest/generate-self-signed-certificates.html
既然使用容器那就可以在任何系統執行,只要把生成的證書檔案對應的放到 Docker 客戶端和服務端即可。
$ git clone --depth=1 https://github.com/khs1994-docker/dockerd-tls.git
$ cd dockerd-tls
在 ./cfssl/*.json
中配置好 CN
hosts
。
$ docker-compose up cfssl
命令執行完畢之後在 ./cfssl/cert
資料夾中可以看到證書檔案,修改檔案許可權。
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
把 ca.pem
server-cert.pem
server-key.pem
三個檔案移動到服務端 /etc/docker/
資料夾中。
CoreOS
請使用第二種方法,其他 Linux 系統根據上文選擇的方法,這裡選擇對應的方法
通常的配置方法
修改 daemon.json
檔案。
注意:非安全連線使用的是
2375
埠,安全連線使用的是2376
埠。當然這是推薦的埠配置,你可以配置任何埠!
{
"tlsverify": true,
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem",
"tlscacert": "/etc/docker/ca.pem",
"hosts":[
"unix:///var/run/docker.sock",
"tcp://0.0.0.0:2376"
]
}
重新啟動 Docker
$ sudo systemctl restart docker
CoreOS 官方文件的方法
首先需要修改 /etc/systemd/system/docker-tcp.socket
檔案內容
ListenStream=2375
# 修改為
ListenStream=2376
修改 CoreOS
上的 daemon.json
檔案。
{
"tlsverify": true,
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem",
"tlscacert": "/etc/docker/ca.pem"
}
重新啟動服務。
$ sudo systemctl daemon-reload
$ sudo systemctl stop docker
$ sudo systemctl restart docker-tcp.socket
$ sudo systemctl restart docker
上文已經提到了啟動順序,這裡提示一下,不再贅述。
客戶端遠端安全連線
將 ca.pem
cert.pem
key.pem
三個檔案通過 scp
下載到 macOS
。
在 macOS
執行以下命令,金鑰路徑請根據實際情況填寫。
$ docker --tlsverify
--tlscacert=/Users/khs1994/test/ca.pem
--tlscert=/Users/khs1994/test/cert.pem
--tlskey=/Users/khs1994/test/key.pem
-H=192.168.57.110:2376
info
把金鑰放入 ~/.docker
資料夾中
每次操作需要跟那麼多引數,太麻煩了。我們可以把 ca.pem
cert.pem
key.pem
三個檔案放入客戶端 ~/.docker
中,然後配置環境變數就可以簡化命令了。
$ export DOCKER_HOST=tcp://192.168.57.110:2376 DOCKER_TLS_VERIFY=1
$ docker info
你也可以選擇其他路徑,請通過環境變數
DOCKER_CERT_PATH
指定。
報錯詳情
不使用安全連線
$ docker -H 192.168.57.110:2376 info
Get http://192.168.57.110:2376/v1.34/containers/json?all=1: malformed HTTP response "x15x03x01x00x02x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?
在非允許列表 IP 中登入
假如遠端伺服器還有一個 IP 10.141.20.83
,現在我們嘗試使用這個 IP 作為服務端地址,看看客戶端能否連線到。
$ docker -H 10.141.20.83:2376 info
error during connect: Get https://10.141.20.83:2376/v1.34/info: x509: certificate is valid for 192.168.57.110, 192.168.199.100, 127.0.0.1, not 10.141.20.83
在非允許列表 Host 中登入
$ docker -H localhost:2376 info
error during connect: Get https://localhost:2375/v1.34/info: x509: certificate is valid for coreos1, not localhost
fish shell
$ set -Ux DOCKER_HOST tcp://192.168.57.110:2376
$ set -Ux DOCKER_TLS_VERIFY 1
# 以上命令寫入環境變數是永久存在的,通過以下命令刪除環境變數
$ set -Ue DOCKER_HOST ; set -Ue DOCKER_TLS_VERIFY
服務端驗證模式
-
tlsverify
,tlscacert
,tlscert
,tlskey
set: Authenticate clients -
tls
,tlscert
,tlskey
: Do not authenticate clients
客戶端驗證模式
-
tls
: Authenticate server based on public/default CA pool -
tlsverify
,tlscacert
: Authenticate server based on given CA -
tls
,tlscert
,tlskey
: Authenticate with client certificate, do not authenticate server based on given CA -
tlsverify
,tlscacert
,tlscert
,tlskey
: Authenticate with client certificate and authenticate server based on given CA
測試遠端構建 Docker 映象
在 macOS
新建 demo 資料夾並進入。
我們首先建一個文字檔案 test.txt
hello!
然後新建一個簡單的 Dockerfile
檔案
FROM busybox
COPY ./test.txt /
CMD cat /test.txt
按照前面的方法設定好環境變數,這裡不再贅述。
$ docker -H 192.168.57.110:2375 --tlsverify build -t khs1994/busybox .
在遠端服務端檢視
SSH 登入到 CoreOS
(這裡為了便於理解,SSH 到遠端伺服器操作)。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
khs1994/busybox latest 368d23df8500 10 seconds ago 1.13MB
我們已經檢視到了映象。
$ docker run -it --rm khs1994/busybox
hello!
執行成功。
客戶端恢復原狀
你如果想在 macOS
操作本地的服務端,請將上面配置的環境變數刪除,這裡不再贅述。