Docker-Java 使用小记
- 前置知识
- Java 语言
- Springboot 框架
- docker 基础知识
- dockerfile 的使用
作者在最近项目中用 docker-java 动态部署 docker 。总结了一些经验。
0x01 依赖项
0x01x01 Maven
正常使用就两个依赖项,官方文档有写。
版本号以 maven 官方仓库的最新版本为准。
1 | <!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java --> |
0x01x02 java.lang.NoClassDefFoundError
Maven 配置不当 / 抽风的情况下, 偶尔 docker-java 本身的依赖不会自动加载进来。导致调用后续使用中马上会报错。
1 | java.lang.NoClassDefFoundError |
这两者有一定区别,不过这里是同时报出来的。看堆栈的话 NoClassDefFoundError 在先。
自行查看报错堆栈,导入没有的包即可。
作者当时是少了这个:
1 | <dependency> |
比较坑,因为 httpcore5 是存在的,这里报找不到类导致作者一直在乱找问题,翻了好久才发现其下没有 http2 的类,要导入 httpcore-h2 才好。
0x02 初始化
首先要连接到本地的 docker 服务上。
0x02x01 docker-java.properties
Docker-java 官方文档中介绍了多种方式修改 docker 的默认属性。
使用配置文件方式的示例:
1 | # -docker version 查看 API version |
项目上线时 tls 这部分可能很重要, docker 服务本身没有身份验证机制,公网上 2375 端口暴露会使黑客甚至任何人都能够恶意操作 docker 。
故而如果不可避免地要暴露 docker 服务,不能使用 2375 端口。
目前作者的项目体量小,服务全在本地,封闭 2375 不让外界访问即可,故此处不展开。
0x02x02 设置 DockerClient
基本使用官方文档中的示例即可。
如果使用 docker-java.properties , createDefaultConfigBuilder() 创建的默认 config 中,属性即为你配置文件中所写的属性。如果代码中需要改动,也可以在 build 前使用 with 方法注入。
1 | DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); |
DockerClient 创建完成后,接下来就可以相对优雅地调用 dockerAPI 了。
0x03 操作 docker (基于 dockerfile )
docker-java 提供的方法其实非常全面,基本建立了 dockerClient 就能涵盖对 docker 服务的所有操作。这里拣最基本最重要的介绍一下。
作者主要用到的是自定义镜像和容器管理,未涉及镜像仓库管理( pull&push )。
0x03x01 dockerClient 用法
dockerClient 中方法的使用基本都一致。
1 | XxxxxxCmd cmd = dockerClient.xxxxxxCmd().withxxx().withxxx(); // 创建命令 |
.exec() 方法这里分为同步的和异步的。
开发者大概是考虑到部分 docker 操作工程量比较大,耗时长,故把这些操作的原型统一为异步抽象类。与同步操作的区别就是这里异步操作的 .exec() 方法需要指定回调。
翻了一下源码,异步操作比较少,但有几个还是很常用的。
![23-10-21-Docker-Java 使用 1](.._pics\23-10-21-Docker-Java 使用 1.png)
异步操作的 .exec() 方法是有参的。
1 | .exec(callback) |
故而要先实例化一个对应的 callback 对象。
1 | XxxxxxCallback callback = new XxxxxxCallback(); |
也可以重写函数体输出一些调试信息之类的,当然正常使用也行。
1 | XxxxxxResultCallback callback = new XxxxxxResultCallback(){ |
0x03x02 创建镜像
1 | String imageName; // 镜像名 |
docker-java 没有直接调用 docker run 的接口 ,这是因为 run 本质上就是 build + create 两步操作依次进行。故而这里首先介绍一下 build 。
示例:
1 | BuildImageResultCallback callback=new BuildImageResultCallback(); // 创建 buildImage 的回调 |
这里 withTag(String tag) 会报过时,代码建议是换用withTags(Set<String> tags) (因为 docker 一个镜像可以打很多 tag )。想用就用,影响不大。
0x03x03 创建容器
示例:
1 | // 创建容器 |
参数不一一解释了,挺直观的。
0x03x04 启动容器
示例:
1 | // 启动容器 |
*** 什么获取容器信息之前要 sleep ? ***
0x03x05 获取端口映射
注意到创建容器的时候建立了一个端口映射:
.withPortBindings(PortBinding.parse(“80:”))
意为将容器的 80 端口映射到宿主机的随机端口。
端口映射:
1 | 80: #将容器 80 端口映射到宿主机随机(高阶)端口 |
由于随机端口映射是在容器启动时确定的,故而容器启动后才能 get 到。
回到上一小节启动的容器:
1 | Container container = dockerClient.listContainersCmd().withShowAll(true).exec().get(0); |
可以直接从 container 中获得端口信息:
1 | Integer port = container.getPorts()[0].getPublicPort(); |
回到刚才的问题,启动容器和获取信息过程之间为什么要 sleep ?
经过我的测试,刚启动的 container 的端口映射是不稳定的,需要经历很短时间的分发过程。当你在创建完端口之后立即得到 Container 对象,其端口信息与后来在 docker 服务里看到的端口信息不符。故而要 sleep ,等待端口映射稳定。如此这般。
0x03x06 关闭&删除容器
示例:
1 | // 关闭容器 |
0x03x07 其他操作
pull、push 等,其调用大同小异,甚至不用翻源码,看代码提示即可。命令构造器构造命令,根据需求适当通过 withXxx 方法加参数,最后调用 .exec() 方法执行之。(异步方法有参,传入实例化的对应 XxxxxxCallback )
0x04 总结
大概这些。后面要开一下 TLS 。