使用 docker-client 对docker Engine 进行调用

使用 docker-client 对docker Engine 进行调用

前提:一段时间以来,使用程序操作docker都是通过shell脚本进行操作,这种方法有很大的弊端(个人理解分为以下几点:)

1、对并发处理不友好

1.1、如果生成shell脚本,脚本前面路径加上UUID,只要脚本中的变量(例如路径)都是唯一的,这种做法是行的通的。(Linux 多用户,多任务)

举例Java程序执行Java代码调用脚本:

    public synchronized static String execCommand(String[] command){
        log.info("将要执行命令{}", JSONObject.toJSONString(command));
        StringBuilder result = new StringBuilder();
        try {
//            String image = "nginx-xi1:latest";
//            String[] command = new String[]{"docker","build","-t",image,"/root/xiqingsong/docker/642B426072C773F3BFCB6B408932594E"};
            Process ps = Runtime.getRuntime().exec(command);
            ps.waitFor();
            BufferedReader bufrIn = new BufferedReader(new InputStreamReader(ps.getInputStream(), "UTF-8"));
            BufferedReader bufrError = new BufferedReader(new InputStreamReader(ps.getErrorStream(), "UTF-8"));
            // 读取输出 result是shell中的输出
//            StringBuilder result = new StringBuilder();
            String line = null;
            while ((line = bufrIn.readLine()) != null || (line = bufrError.readLine()) != null) {
                result.append(line).append('\n');
            }
            log.info(result.toString());
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

通过上面代码示例发现一个方法 Process.waitFor() 先看看方法的说明:

Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated. This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked until the subprocess exits.
导致当前线程在必要时等待,直到该进程对象所表示的进程终止。如果子进程已经终止,则该方法立即返回。如果子进程尚未终止,则调用线程将被阻塞,直到子进程退出。

没错,当程序执行Linux命令的时候,该线程必须要等待完成,而且通过执行结果也很难判断出是否成功。当然shell脚本的话可能通过是不是 “exit 1” 进行判断。
同时 还需要判断执行时间,为程序设置等待超时时间。(不到实在没有解决方案的时候,慎用)

1.2 、对事务管理不友好

1.3、异常处理比较复杂

**没有调查没有发言权,1.2 1.3 这个我没有验证,公司大佬告诉我这个问题,我先写上

2、选择合适的SDK

官方地址:https://docs.docker.com/engine/api/sdk/examples/

2.1 、官方只给出了两种语言操作docker Engine的示例(go 、 Python)

在这里插入图片描述

当然除了这两种语言之外,官方下面推荐以下SDK

在这里插入图片描述

在这里插入图片描述

3、正文 目前Java用于操作docker 常用的工具有两种:

在这里插入图片描述

3.1、操作docker 需要配置开放远程调用的端口,和证书的配置

有兴趣的同学可以去看官网上的介绍和操作步骤:https://docs.docker.com/engine/security/https/

下面是我借鉴其他博客实测过后生成证书的脚本:

#创建 Docker TLS 证书
#!/bin/bash

#相关配置信息
SERVER="192.168.80.136"
PASSWORD="123123"
COUNTRY="CN"
STATE="北京市"
CITY="北京市"
ORGANIZATION="本地测试"
ORGANIZATIONAL_UNIT="Dev"
EMAIL="[email protected]"

###开始生成文件###
echo "开始生成文件"

#切换到生产密钥的目录
cd /etc/docker   
#生成ca私钥(使用aes256加密)
openssl genrsa -aes256 -passout pass:$PASSWORD  -out ca-key.pem 2048
#生成ca证书,填写配置信息
openssl req -new -x509 -passin "pass:$PASSWORD" -days 3650 -key ca-key.pem -sha256 -out ca.pem -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/OU=$ORGANIZATIONAL_UNIT/CN=$SERVER/emailAddress=$EMAIL"

#生成server证书私钥文件
openssl genrsa -out server-key.pem 2048
#生成server证书请求文件
openssl req -subj "/CN=$SERVER" -new -key server-key.pem -out server.csr
#使用CA证书及CA密钥以及上面的server证书请求文件进行签发,生成server自签证书
openssl x509 -req -days 3650 -in server.csr -CA ca.pem -CAkey ca-key.pem -passin "pass:$PASSWORD" -CAcreateserial  -out server-cert.pem

#生成client证书RSA私钥文件
openssl genrsa -out key.pem 2048
#生成client证书请求文件
openssl req -subj '/CN=client' -new -key key.pem -out client.csr

sh -c 'echo "extendedKeyUsage=clientAuth" > extfile.cnf'
#生成client自签证书(根据上面的client私钥文件、client证书请求文件生成)
openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca-key.pem  -passin "pass:$PASSWORD" -CAcreateserial -out cert.pem  -extfile extfile.cnf

#更改密钥权限
chmod 0400 ca-key.pem key.pem server-key.pem
#更改密钥权限
chmod 0444 ca.pem server-cert.pem cert.pem
#删除无用文件
rm client.csr server.csr

echo "生成文件完成"
###生成结束###

脚本来源:https://blog.csdn.net/qq_21187515/article/details/90268345

3.2 、 配置docker 添加证书

1> 获取docker 启动的时候加载的配置文件(由于环境安装的都不一定一样,所以这个路径可能会变)

在这里插入图片描述

2> 编辑文件 添加证书

--tlsverify \
--tlscacert=/etc/docker/ca.pem \ 
--tlscert=/etc/docker/server-cert.pem \          
--tlskey=/etc/docker/server-key.pem \     
-H tcp://0.0.0.0:2376 \    
-H unix:///var/run/docker.sock 

3.3 、重启docker

在这里插入图片描述

可以看到端口已经监听了

3.4 、验证配置是否成功

在这里插入图片描述

3.5 、问题解决

Get http://192.168.80.136:2376/v1.26/images/json: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS? 

在这里插入图片描述

如果你出现这个问题,不要着急,下面是解决方案

在这里插入图片描述

原因分析:解决方法来源:http://tinylab.org/use-docker-without-sudo/

  • - 因为 /var/run/docker.sock 所属 docker 组具有 setuid 权限
    - 1. $ sudo ls -l /var/run/docker.sock
      2. srw-rw---- 1 root docker 0 May  1 21:35 /var/run/docker.sock
    

4、程序调用

maven添加:

<dependency>
    <groupId>com.spotify</groupId>
    <artifactId>docker-client</artifactId>
    <version>8.16.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>2.27</version>
</dependency>

代码实现:

package com.qyos.docker.utils;

import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerCertificates;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.exceptions.DockerCertificateException;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.Image;
import com.spotify.docker.client.messages.RegistryAuth;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.nio.file.Paths;
import java.util.List;

/**
 * @Author xiqingsong
 * @CreateTime 2020/4/28 15:46
 * docker 客户端统一处理docker操作
 **/
@Slf4j
public class DockerUtils {
    static DockerClient docker;

    static {
        try {
            docker = DefaultDockerClient.builder()
                    .uri(URI.create("https://192.168.80.136:2376"))
                    .dockerCertificates(new DockerCertificates(Paths.get("/etc/docker/")))  //jar 部署位置所需docker服务端的证书
                    .build();
        } catch (DockerCertificateException e) {
            e.printStackTrace();
        }
    }


    public static boolean getImageList(){
        try {
            List<Image> quxImages = docker.listImages();
            log.info(quxImages.toString());
            return true;
        } catch (DockerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

}

5 、docker-client 操作手册

官方文档: https://github.com/spotify/docker-client/blob/master/docs/user_manual.md

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章