当前位置:   article > 正文

java集成Docker-java实现远程镜像、容器创建,服务发布

docker-java

1、集成目的

       目前项目中需要实现水利机理模型的容器化部署,使用docker实现模型容器化部署操作,互相隔离,就是一个不错的方案。

2、需要docker-java 实现

  • 实现docker远程连接、远程安全连接
  • 构建镜像、加载镜像、删除镜像、拉取镜像、创建容器、启动容器、停止容器、删除容器。
  • 执行模型运行命令
  • 实现路径挂载。
  • 实现宿主机模型包复制

3、使用docker-java

3.1创建远程连接

3.1.1修改docker配置信息(普通连接)

  1. 打开docker.service文件
  2. sudo vi /lib/systemd/system/docker.service
  3. 找到ExecStart 开头的配置,注释原配置 进行备份
  4. 插入以下内容
  5. ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
  6. 保存退出
  7. sudo systemctl daemon-reload
  8. sudo systemctl restart docker

注意:

1、我使用的是Ubutun系统,需要使用sudo进行操作。防火墙如果启动,需要开发2375端口,不然无法访问到docker。

2、浏览器输入:http://IP:2375/version 如果响应正常,则配置生效

3.1.2添加maven依赖

  1. <dependency>
  2. <groupId>com.github.docker-java</groupId>
  3. <artifactId>docker-java</artifactId>
  4. <version>3.2.13</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.docker-java</groupId>
  8. <artifactId>docker-java-transport-httpclient5</artifactId>
  9. <version>3.2.13</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.alibaba</groupId>
  13. <artifactId>fastjson</artifactId>
  14. <version>1.2.83</version>
  15. </dependency>

3.1.3创建普通连接

  1. package com.sf.engines.config;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.github.dockerjava.api.DockerClient;
  4. import com.github.dockerjava.api.model.Info;
  5. import com.github.dockerjava.core.DefaultDockerClientConfig;
  6. import com.github.dockerjava.core.DockerClientImpl;
  7. import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
  8. import com.github.dockerjava.transport.DockerHttpClient;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.context.annotation.Bean;
  11. import java.time.Duration;
  12. @Configuration
  13. public class DockerConfig {
  14. /**
  15. * 连接docker服务器
  16. * @return
  17. */
  18. @Bean("dockerClient")
  19. public DockerClient connectDocker(){
  20. DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
  21. .withDockerTlsVerify(false)
  22. // 这里填最上面填的ip端口号,ip换成服务器ip
  23. .withDockerHost("tcp://192.168.10.8:2375")
  24. // docker API版本号,可以用docker version查看
  25. .withApiVersion("1.41")
  26. // 默认
  27. .withRegistryUrl("https://index.docker.io/v1/").build();
  28. DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
  29. .dockerHost(config.getDockerHost())
  30. .sslConfig(config.getSSLConfig())
  31. .maxConnections(100)
  32. .connectionTimeout(Duration.ofSeconds(30))
  33. .responseTimeout(Duration.ofSeconds(45))
  34. .build();
  35. DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
  36. Info info = dockerClient.infoCmd().exec();
  37. String infoStr = JSONObject.toJSONString(info);
  38. System.out.println("docker的环境信息如下:=================");
  39. System.out.println(infoStr);
  40. return dockerClient;
  41. }
  42. }

控制台打印了docker信息,说明已经连上docker。

注意:

  • DOCKER_HOST Docker的地址,比如: tcp://localhost:2376 或者unix:///var/run/docker.sock
  • DOCKER_TLS_VERIFY 是否开启 TLS 验证 (http 和 https 之间切换)
  • DOCKER_CERT_PATH TLS 验证的证书路径
  • DOCKER_CONFIG 其他docker配置文件的路径 (比如 .dockercfg)
  • api.version API version版本
  • registry.url 下载源地址(docker镜像存放的地址)
  • registry.username 登陆用户名 (推送镜像到docker云仓库时需要)
  • registry.password 登陆用户密码(推送镜像到docker云仓库时需要)
  • registry.email 登陆账户的邮箱(推送镜像到docker云仓库时需要)

3.1.4创建安全连接 

  1. 首先修改docker.service
  1. sudo vi /lib/systemd/system/docker.service
  2. 找到ExecStart 开头的配置,注释原配置 进行备份
  3. 插入以下内容
  4. ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -D --tlsverify --tlscert=/etc/docker/certs.d/server-cert-docker.pem --tlskey=/etc/docker/certs.d/server-key-docker.pem --tlscacert=/etc/docker/certs.d/ca-docker.pem
  5. 保存退出
  6. sudo systemctl daemon-reload
  7. sudo service docker restart

   2. 证书脚本编写

  1. #!/bin/bash
  2. #
  3. # -------------------------------------------------------------
  4. # 自动创建 Docker TLS 证书
  5. # -------------------------------------------------------------
  6. # 以下是配置信息
  7. # --[BEGIN]------------------------------
  8. CODE="docker"
  9. IP="192.168.1.1"
  10. PASSWORD="123456"
  11. COUNTRY="CN"
  12. STATE="SHANDONG"
  13. CITY="JINAN"
  14. ORGANIZATION="thyc"
  15. ORGANIZATIONAL_UNIT="Dev"
  16. COMMON_NAME="$IP"
  17. EMAIL="zjbang.ok@163.com"
  18. # --[END]--
  19. # Generate CA key
  20. openssl genrsa -aes256 -passout "pass:$PASSWORD" -out "ca-key-$CODE.pem" 4096
  21. # Generate CA
  22. openssl req -new -x509 -days 365 -key "ca-key-$CODE.pem" -sha256 -out "ca-$CODE.pem" -passin "pass:$PASSWORD" -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/OU=$ORGANIZATIONAL_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
  23. # Generate Server key
  24. openssl genrsa -out "server-key-$CODE.pem" 4096
  25. # Generate Server Certs.
  26. openssl req -subj "/CN=$COMMON_NAME" -sha256 -new -key "server-key-$CODE.pem" -out server.csr
  27. echo "subjectAltName = IP:$IP,IP:127.0.0.1" >> extfile.cnf
  28. echo "extendedKeyUsage = serverAuth" >> extfile.cnf
  29. openssl x509 -req -days 365 -sha256 -in server.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "server-cert-$CODE.pem" -extfile extfile.cnf
  30. # Generate Client Certs.
  31. rm -f extfile.cnf
  32. openssl genrsa -out "key-$CODE.pem" 4096
  33. openssl req -subj '/CN=client' -new -key "key-$CODE.pem" -out client.csr
  34. echo extendedKeyUsage = clientAuth >> extfile.cnf
  35. openssl x509 -req -days 365 -sha256 -in client.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "cert-$CODE.pem" -extfile extfile.cnf
  36. rm -vf client.csr server.csr
  37. chmod -v 0400 "ca-key-$CODE.pem" "key-$CODE.pem" "server-key-$CODE.pem"
  38. chmod -v 0444 "ca-$CODE.pem" "server-cert-$CODE.pem" "cert-$CODE.pem"
  39. # 打包客户端证书
  40. mkdir -p "tls-client-certs-$CODE"
  41. cp -f "ca-$CODE.pem" "cert-$CODE.pem" "key-$CODE.pem" "tls-client-certs-$CODE/"
  42. cd "tls-client-certs-$CODE"
  43. tar zcf "tls-client-certs-$CODE.tar.gz" *
  44. mv "tls-client-certs-$CODE.tar.gz" ../
  45. cd ..
  46. rm -rf "tls-client-certs-$CODE"
  47. # 拷贝服务端证书
  48. mkdir -p /etc/docker/certs.d
  49. cp "ca-$CODE.pem" "server-cert-$CODE.pem" "server-key-$CODE.pem" /etc/docker/certs.d/

脚本是网上找的,可以自动生成证书并完成拷贝的脚本。脚本命名自由:sudo vi auto_gen_docker.sh 

3.执行脚本

  1. chmod a+x auto_gen_docker.sh
  2. sh auto_gen_docker.sh

3.2镜像管理

3.2.1从dockerfile构建镜像

  1. /**
  2. * 从Dockerfile构建镜像
  3. * @return
  4. * @throws URISyntaxException
  5. */
  6. public String buildImage(String imageName, String imageTag, String dockerFile) throws URISyntaxException {
  7. ImmutableSet<String> tag = ImmutableSet.of(imageName + ":" + imageTag);
  8. String imageId = dockerClient.buildImageCmd(new File(dockerFile))
  9. .withTags(tag)
  10. .start()
  11. .awaitImageId();
  12. return imageId;
  13. }

提示:我在使用buileImageCmd时,无法正确构建镜像。不得已采取比较笨的办法,将dockerfile文件上传到服务器目标文件夹中,通过ssh远程调用的方式,进行远程镜像构建操作。具体实现代码如下:

  1. //远程构建镜像
  2. private String buildImage(EnginesModelImageEntity image) {
  3. String tag = image.getImageName() + ":" + image.getImageTag();
  4. //模型解压命令
  5. StringBuffer buildBuffer = new StringBuffer("");
  6. buildBuffer.append(ModelConstant.CD_STR)
  7. .append(image.getDockerFileUrl())
  8. .append(ModelConstant.AND_STR)
  9. .append(ModelConstant.DOCKER_BUILD)
  10. .append(tag)
  11. .append(ModelConstant.DOCKER_BUILD_END);
  12. //调用远程命令进行模型解压
  13. Connection unzipCoon = SshUtil.login(ip,userName,password);
  14. String result = SshUtil.execute(unzipCoon, buildBuffer.toString());
  15. return result;
  16. }

注意:远程连接的代码在以前的文章里有,可以去查阅。

1、首先通过CD名称进入到存放dockerfile的文件夹,然后执行镜像build命令: docker build -t imageName:imageTag . 

2、构建命令最后的. 一定不能省略。

3.2.2获取镜像ID

        构建镜像有时会没有那么快,可以通过接口获取镜像ID:

  1. public R getImageID(EnginesModelImageEntity image) {
  2. if(ObjectUtils.isEmpty(dockerClient)){
  3. initDockerClient();
  4. }
  5. String tag = image.getImageName() + ":" + image.getImageTag();
  6. List<Image> images = dockerClient.listImagesCmd().withImageNameFilter(image.getImageName()).exec();
  7. if(images.isEmpty()){
  8. return R.fail("镜像构建中,请稍后再试!");
  9. }
  10. String imageId = "";
  11. for(Image i : images){
  12. if(Arrays.asList(i.getRepoTags()).contains(tag)){
  13. String id = i.getId();
  14. imageId = id.substring(id.indexOf(":")+1,id.indexOf(":")+13);
  15. }
  16. }
  17. if(ObjectUtils.isNotEmpty(imageId)){
  18. image.setImageId(imageId);
  19. image.setImageStatus("2");
  20. baseMapper.updateById(image);
  21. return R.success("获取镜像ID成功!");
  22. }
  23. return R.success("未查询到镜像ID,请稍后再试!");
  24. }

注意:如果使用同一个dockerfile文件构建镜像,则镜像ID是一致的,会存在多个镜像TAG,可以根据repoTags中是否包含自己的镜像标签来判断,镜像是否构建完成。

3.2.3删除镜像

docker-java删除镜像是通过镜像ID进行删除操作的:

  1. public void removeImage(DockerClient client,String imageId){
  2. dockerClient.removeImageCmd(imageId).exec();
  3. }

但是会把同一个dockerfile构造的镜像全部删除,我是通过镜像tag进行镜像删除操作的:

  1. public boolean removeImage(Long id) {
  2. EnginesModelImageEntity image = baseMapper.selectById(id);
  3. if(ObjectUtils.isEmpty(dockerClient)){
  4. initDockerClient();
  5. }
  6. image.setImageStatus("3");
  7. if(baseMapper.updateById(image)>=0){
  8. String tag = image.getImageName() + ":" + image.getImageTag();
  9. //模型解压命令
  10. StringBuffer removeBuffer = new StringBuffer("");
  11. removeBuffer.append(ModelConstant.DOCKER_REMOVE)
  12. .append(tag);
  13. //调用远程命令进行模型解压
  14. Connection unzipCoon = SshUtil.login(ip,userName,password);
  15. SshUtil.execute(unzipCoon, removeBuffer.toString());
  16. return true;
  17. }
  18. return false;
  19. }

注意:通过删除镜像命令:docker rmi imageName:imageTag进行镜像删除操作。

3.3容器管理

3.3.1创建容器

  1. private String createContainers(EnginesModelContainerEntity entity){
  2. EnginesModelImageEntity image = imageMapper.selectById(entity.getImageId());
  3. String result = "";
  4. for(int i=0;i<entity.getContainerNumber();i++){
  5. EnginesModelContainerChildEntity childEntity = new EnginesModelContainerChildEntity();
  6. childEntity.setModelId(entity.getModelId());
  7. String containerName = entity.getContainerName().replace(" ","") + ModelConstant.UNDERLINE +i;
  8. childEntity.setContainerName(containerName);
  9. childEntity.setContainerStatus("2");
  10. childEntity.setParentId(entity.getId());
  11. childEntity.setStatus(0);
  12. childEntity.setIsDeleted(0);
  13. childEntity.setCreateTime(new Date());
  14. childEntity.setCreateUser(entity.getCreateUser());
  15. String buffer = ModelConstant.DOCKER_RUN + containerName + ModelConstant.SPACE + image.getImageName() + ModelConstant.COLON + image.getImageTag();
  16. Connection pullCoon = SshUtil.login(ip,userName,password);
  17. result = SshUtil.execute(pullCoon, buffer);
  18. if(ObjectUtils.isNotEmpty(result)){
  19. childMapper.insert(childEntity);
  20. }
  21. }
  22. return result;
  23. }

使用的远程ssh方式创建容器,容器启动命令:

docker run --gpus all -itd --name= containerName imageName:imageTag

4、远程服务器docker容器中写文件

  1. public static void main(String[] args) {
  2. String containerId = "61cf0a2e4c4a0ae00ad8aa1193f3abfbdd9a48e6236f427d2c37fae23b84ae6f"; // Replace with your container ID
  3. String filePath = "/opt/model/twoHydrodynamic/ProcessData/Inflow_3.json";
  4. String fileContent = "Create JSON FILE";
  5. DockerClientService.initDockerClient("ip","docker端口");
  6. // 创建一个tar归档,将内容写入tar文件
  7. ByteArrayInputStream tarStream = null;
  8. try {
  9. tarStream = TarUtils.createTarArchive(filePath, fileContent);
  10. } catch (IOException e) {
  11. throw new RuntimeException(e);
  12. }
  13. ExecCreateCmdResponse response = dockerClient.execCreateCmd(containerId)
  14. .withCmd("tar", "-C", "/", "-x")
  15. .withAttachStdout(true)
  16. .withAttachStderr(true)
  17. .exec();
  18. ExecStartCmd startCmd = dockerClient.execStartCmd(response.getId())
  19. .withDetach(false)
  20. .withTty(true);
  21. ExecStartResultCallback callback = new ExecStartResultCallback();
  22. startCmd.exec(callback);
  23. // 发送tar归档数据
  24. dockerClient.copyArchiveToContainerCmd(containerId)
  25. .withRemotePath("/")
  26. .withTarInputStream(tarStream)
  27. .exec();
  28. // 关闭tarStream
  29. try {
  30. tarStream.close();
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }
  34. }

5、远程服务器docker容器中读文件

  1. public static void main(String[] args) {
  2. String containerId = "61cf0a2e4c4a0ae00ad8aa1193f3abfbdd9a48e6236f427d2c37fae23b84ae6f"; // Replace with your container ID
  3. String filePath = "/opt/model/twoHydrodynamic/OutputData/X2DProcess.dat";
  4. DockerClientService.initDockerClient("IP","docker端口");
  5. String taskId = UUID.randomUUID().toString();
  6. String basePath = "/Users/admin/Documents/shuke/modelFile/depth/"+taskId;
  7. File folder = new File(basePath);
  8. //如果文件夹不存在,则创建文件夹
  9. if (!folder.exists()) {
  10. folder.mkdirs();
  11. }
  12. String outPath = basePath + "/X2DProcess.dat";
  13. try {
  14. InputStream inputStream = dockerClient.copyArchiveFromContainerCmd(containerId, filePath).exec();
  15. try (OutputStream outputStream = new FileOutputStream(outPath)) {
  16. byte[] buffer = new byte[1024];
  17. int bytesRead;
  18. while ((bytesRead = inputStream.read(buffer)) != -1) {
  19. outputStream.write(buffer, 0, bytesRead);
  20. }
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. } finally {
  24. try {
  25. inputStream.close();
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. contentGet(folder,outPath,taskId);
  31. } catch (NotFoundException e) {
  32. System.err.println("Container not found: " + e.getMessage());
  33. } catch (FileNotFoundException e) {
  34. throw new RuntimeException(e);
  35. }
  36. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/411847
推荐阅读
相关标签
  

闽ICP备14008679号