最近看 OJ 项目的远程开发阶段,然后踩坑踩了 2 天😂
Docker
版本:在 CentOS
安装 sudo yum install docker-ce-20.10.9 docker-ce-cli-20.10.9 containerd.io
Client: Docker Engine - Community
Version: 20.10.9
API version: 1.41
Go version: go1.16.8
Git commit: c2ea9bc
Built: Mon Oct 4 16:08:25 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.9
API version: 1.41 (minimum version 1.12)
Go version: go1.16.8
Git commit: 79ea9d3
Built: Mon Oct 4 16:06:48 2021
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.31
GitCommit: e377cd56a71523140ca6ae87e30244719194a521
runc:
Version: 1.1.12
GitCommit: v1.1.12-0-g51d5e94
docker-init:
Version: 0.19.0
GitCommit: de40ad0
SpringBoot
版本: 2.7.14
Java-Docker
依赖
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java-transport-httpclient5 -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>3.3.4</version>
</dependency>
不设置 SSL 认证
vim /usr/lib/systemd/system/docker.service
主要添加 -H tcp://0.0.0.0:2376
进去,只需要修改这个位置就好了
1
然后重启 Docker 服务
systemctl daemon-reload
systemctl restart docker
然后查看是否守护线程启动成功
systemctl status docker.service
Java 调用代码
// 获取默认的 Docker Client
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost("tcp://服务器IP:2376")
.build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofSeconds(45))
.build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
PingCmd pingCmd = dockerClient.pingCmd();
pingCmd.exec();
配置 SSL 验证
官方文档
需要注意一点 官方文档写的 TLS 应该在 2376
端口,这里之前踩了大坑(之前一直使用 2375 一直不行)
当然我们需要开启服务器的2376 端口的防火墙(和宝塔如果有的话)
!!!
将以下示例中 $HOST 的所有实例替换为 Docker 守护程序主机(服务器 IP)
首先创建存储相关信息的文件夹
mkdir -p /usr/local/certs.d/dockerd/ca
cd /usr/local/certs.d/dockerd/ca
让后在这个文件夹下面执行对应的命令
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
..............................................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code): CN
State or Province Name (full name)[]: Beijing
Locality Name (eg, city) []: Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]: China
Organizational Unit Name (eg, section) []: developer
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []: 填写邮箱
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................++
.................................................................................................++
e is 65537 (0x10001)
$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
接下来,我们将使用我们的 CA 签署公钥:
这里官方文档使用了 DNS 我们可以不写
$ echo subjectAltName = IP:$HOST,IP:127.0.0.1 >> extfile.cnf
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth > extfile-client.cnf
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out cert.pem -extfile extfile-client.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:
删除一些不重要的文件
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
修改文件的权限
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
最后
vi /etc/docker/daemon.json
填写相关内容
{
"registry-mirrors": ["https://mirror.ccs.tencentyun.com"],
"tls": true,
"tlscacert": "/usr/local/certs.d/dockerd/ca/ca.pem",
"tlscert": "/usr/local/certs.d/dockerd/ca/server-cert.pem",
"tlskey": "/usr/local/certs.d/dockerd/ca/server-key.pem"
}
然后重启 Docker 服务
systemctl daemon-reload
systemctl restart docker
然后查看是否守护线程启动成功
systemctl status docker.service
测试连通性
我们需要在服务器上下载下面三个文件 ca.pem
、 key.pem
、cert.pem
就在 /usr/local/certs.d/dockerd/ca
目录下
PS 执行这个命令的时候我们需要在 ssl 目录里面放入这三个文件
curl https://服务器IP:2376/version --cert cert.pem --key key.pem --cacert ca.pem
Java 调用代码
─src
│ ├─main
│ │ ├─java
│ │ └─resources
│ │ ├─ca 文件放在这个目录下
而且文件的名称需要下面这三个名称即 ca.pem
、 key.pem
、cert.pem
URL url = ClassLoaderUtil.getClassLoader().getResource("ca");
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost("tcp://服务器 IP :2376")
.withDockerTlsVerify(true)
// 下面三个参数不加也能运行成功
// .withRegistryPassword("密码")
// .withRegistryUsername("用户名")
// .withRegistryEmail("邮箱")
// 这里如果不截取的话会报错 url.getPath()结果是 /E:/yuoj-code-sandbox-master/target/classes/ca
// 多一个 / 所以要截取,截取之后才不会报错
.withDockerCertPath(url.getPath().substring(1))
.build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
// 这里不要忘了啊,如果不加的话就会是 Status 400: Client sent an HTTP request to an HTTPS server
.sslConfig(config.getSSLConfig())
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofSeconds(45))
.build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
PingCmd pingCmd = dockerClient.pingCmd();
pingCmd.exec();
报错小结
1、这里如果 Docker 版本过高会报一下错误 {“message”:“client version 1.23 is too old. Minimum supported API version is 1.24, please upgrade your client to a newer version”}
解决办法:降低服务器 Docker 版本
2、还有一个报错 Cannot pull images when logged in to Docker Desktop (Status 500: unauthorized: incorrect username or password)
这个报错解决方法是,在 Docker 上登录一个账号就行了
3、报错信息 Status 400: Client sent an HTTP request to an HTTPS server
解决办法:首先确定 .sslConfig(config.getSSLConfig()) 写了没有,其次在确认一下公钥私钥证书是否放到了对应的目录下面