最近看 OJ 项目的远程开发阶段,然后踩坑踩了 2 天😂
Docker
版本:在 CentOS
安装 sudo yum install docker-ce-20.10.9 docker-ce-cli-20.10.9 containerd.io
Client: Docker Engine - CommunityVersion: 20.10.9API version: 1.41Go version: go1.16.8Git commit: c2ea9bcBuilt: Mon Oct 4 16:08:25 2021OS/Arch: linux/amd64Context: defaultExperimental: trueServer: Docker Engine - CommunityEngine:Version: 20.10.9API version: 1.41 (minimum version 1.12)Go version: go1.16.8Git commit: 79ea9d3Built: Mon Oct 4 16:06:48 2021OS/Arch: linux/amd64Experimental: falsecontainerd:Version: 1.6.31GitCommit: e377cd56a71523140ca6ae87e30244719194a521runc:Version: 1.1.12GitCommit: v1.1.12-0-g51d5e94docker-init:Version: 0.19.0GitCommit: 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 ClientDockerClientConfig 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()) 写了没有,其次在确认一下公钥私钥证书是否放到了对应的目录下面