想了解JavaAPI操作Docker示例的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于java调用docker中的api的相关问题,此外,我们还将为您介绍关于DockerClient(an
想了解Java API 操作Docker示例的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于java调用docker中的api的相关问题,此外,我们还将为您介绍关于Docker Client (another java docker client api)、Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock、Docker Java API 开发、Docker 学习笔记之 docker-save vs docker-export vs docker-commit的新知识。
本文目录一览:- Java API 操作Docker示例(java调用docker中的api)
- Docker Client (another java docker client api)
- Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock
- Docker Java API 开发
- Docker 学习笔记之 docker-save vs docker-export vs docker-commit
Java API 操作Docker示例(java调用docker中的api)
大家好,我是邵奈一,一个不务正业的程序猿、正儿八经的斜杠青年。
1、世人称我为:被代码耽误的诗人、没天赋的书法家、五音不全的歌手、专业跑龙套演员、不合格的运动员…
2、这几年,我整理了很多IT技术相关的教程给大家,爱生活、爱分享。
3、如果您觉得文章有用,请收藏,转发,评论,并关注我,谢谢!
博客导航跳转(请收藏):邵奈一的技术博客导航
| 公众号 | 微信 | 微博 | CSDN | 简书 |
教程目录
- 0x00 教程内容
- 0x01 安装并配置Docker
- 1. 安装Docker
- 2. 配置Docker开放2375端口
- 0x02 Java API 操作Docker
- 1. 引入 docker-java 项目的两种方式
- 2. 新建项目并引入依赖
- 3. 编写代码
- 0x03 检验
- 1. 拉取镜像
- 2. 运行创建容器代码
- 0xFF 总结
- 安装并配置Docker
- Java API 操作Docker
- 检验
说明:本教程最好需要有点Docker基础与编程基础,可以参考本博客的其他内容进行学习。
0x01 安装并配置Docker1. 安装Docker
(1)不清楚的可以看我另一篇教程=> D001.5 Docker入门(超级详细基础篇)
安装位置
直达=> 传送门
2. 配置Docker开放2375端口
默认情况下,Docker通过守护进程Unix socket(/var/run/docker.sock)
来进行本地进程
通信,但此进程只能在本地使用Docker客户端
或者Docker API
方式进行操作。如果想在其他主机上操作Docker主机,就需要让Docker守护进程打开一个HTTP Socket
,以实现远程的通信。
vim /usr/lib/systemd/system/docker.service
加上相应的内容:
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
注意:如果有防火墙,也要放开2375端口。
systemctl daemon-reload
systemctl restart docker
以下命令可以查看是否配置好,并且可以看到2375端口已经被监听了:
1. 引入 docker-java 项目的两种方式
(1)方式一
直接在新建的Maven项目中,添加Maven依赖即可:
<!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.1.5</version>
</dependency>
(2)方式二
将 docker-java
安装到本地Maven中,操作如下:
1、下载 docker-java
的 github 源码(需要安装好 Git
)
git clone https://github.com/docker-java/docker-java.git
2、安装到本地Maven中
cd docker-java/
mvn install -Dmaven.test.skip=true
2. 新建项目并引入依赖
(1)此处我们使用第一种方式,直接在pom.xml
中引入依赖的方式
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.1.5</version>
</dependency>
3. 编写代码
(1)新建一个工具类 DockerClientUtils
:
package com.shaonaiyi.utils;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.core.DockerClientBuilder;
/**
* @Auther: shaonaiyi@163.com
* @Date: 2021/1/10 15:37
* @Description: Java API实现创建Docker容器
*/
public class DockerClientUtils {
/**
* 连接Docker服务器
* @return
*/
public DockerClient connectDocker(String dockerInstance){
DockerClient dockerClient = DockerClientBuilder.getInstance(dockerInstance).build();
dockerClient.infoCmd().exec();
return dockerClient;
}
/**
* 创建容器
* @param client
* @return
*/
public CreateContainerResponse createContainers(DockerClient client, String containerName, String imageName){
CreateContainerResponse container = client.createContainerCmd(imageName)
.withName(containerName)
.exec();
return container;
}
/**
* 启动容器
* @param client
* @param containerId
*/
public void startContainer(DockerClient client,String containerId){
client.startContainerCmd(containerId).exec();
}
/**
* 启动容器
* @param client
* @param containerId
*/
public void stopContainer(DockerClient client,String containerId){
client.stopContainerCmd(containerId).exec();
}
/**
* 删除容器
* @param client
* @param containerId
*/
public void removeContainer(DockerClient client,String containerId){
client.removeContainerCmd(containerId).exec();
}
}
(2)编程代码来测试(可以直接在DockerClientUtils
里直接写main方法):
public static void main(String[] args){
DockerClientUtils dockerClientUtils =new DockerClientUtils();
//连接Docker服务器
DockerClient client = dockerClientUtils.connectDocker("tcp://192.168.128.100:2375");
//创建容器
CreateContainerResponse container = dockerClientUtils.createContainers(client,"sny_hello","hello-world");
//启动容器
dockerClientUtils.startContainer(client,container.getId());
}
注意:192.168.128.100
需要修改成自己的 Docker 服务器的ip地址
!
1. 拉取镜像
(1)因为本例子是测试新建容器的API,所以,先得有镜像,我们使用hello-world
的镜像(如果已经存在则不需要操作了):
docker run hello-world
2. 运行创建容器代码
(1)运行代码,则可以看到IDEA显示没有报错:
PS:如果重复测试的话,需要先删除容器,命令如下:
docker rm sny_hello
0xFF 总结
- 更多参考资料
https://docs.docker.com/engine/api/sdk/
https://github.com/docker-java/docker-java
https://copyfuture.com/blogs-details/202001231456248957t5rdb0yjnpby3c
邵奈一 原创不易,如转载请标明出处,教育是一生的事业。
Docker Client (another java docker client api)
前一篇提到了docker-java,这里介绍另一个docker client 库,Docker Client
版本兼容
兼容17.03.1~ce - 17.12.1~ce (点 [here][1]查看).
下载jar包
点击 [via Maven][maven-search]搜索和下载最新的jar包.
pom.xml配置如下:
<dependency>
<groupId>com.spotify</groupId>
<artifactId>docker-client</artifactId>
<version>LATEST-VERSION</version>
</dependency>
当前最新的是8.15.0
<dependency>
<groupId>com.spotify</groupId>
<artifactId>docker-client</artifactId>
<version>8.15.0</version>
</dependency>
使用举例
// Create a client based on DOCKER_HOST and DOCKER_CERT_PATH env vars
final DockerClient docker = DefaultDockerClient.fromEnv().build();
// Pull an image
docker.pull("busybox");
// Bind container ports to host ports
final String[] ports = {"80", "22"};
final Map<String, List<PortBinding>> portBindings = new HashMap<>();
for (String port : ports) {
List<PortBinding> hostPorts = new ArrayList<>();
hostPorts.add(PortBinding.of("0.0.0.0", port));
portBindings.put(port, hostPorts);
}
// Bind container port 443 to an automatically allocated available host port.
List<PortBinding> randomPort = new ArrayList<>();
randomPort.add(PortBinding.randomPort("0.0.0.0"));
portBindings.put("443", randomPort);
final HostConfig hostConfig = HostConfig.builder().portBindings(portBindings).build();
// Create container with exposed ports
final ContainerConfig containerConfig = ContainerConfig.builder()
.hostConfig(hostConfig)
.image("busybox").exposedPorts(ports)
.cmd("sh", "-c", "while :; do sleep 1; done")
.build();
final ContainerCreation creation = docker.createContainer(containerConfig);
final String id = creation.id();
// Inspect container
final ContainerInfo info = docker.inspectContainer(id);
// Start container
docker.startContainer(id);
// Exec command inside running container with attached STDOUT and STDERR
final String[] command = {"sh", "-c", "ls"};
final ExecCreation execCreation = docker.execCreate(
id, command, DockerClient.ExecCreateParam.attachStdout(),
DockerClient.ExecCreateParam.attachStderr());
final LogStream output = docker.execStart(execCreation.id());
final String execOutput = output.readFully();
// Kill container
docker.killContainer(id);
// Remove container
docker.removeContainer(id);
// Close the docker client
docker.close();
Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock
在 Docker 容器里面使用 docker run
/docker build
?
Docker 容器技术目前是微服务/持续集成/持续交付领域的第一选择。而在 DevOps 中,我们需要将各种后端/前端的测试/构建环境打包成 Docker 镜像,然后在需要的时候,Jenkins 会使用这些镜像启动容器以执行 Jenkins 任务。
为了方便维护,我们的 CI 系统如 Jenkins,也会使用 Docker 方式部署。 Jenkins 任务中有些任务需要将微服务构建成 Docker 镜像,然后推送到 Harbor 私有仓库中。 或者我们所有的 Jenkins Master 镜像和 Jenkins Slave 镜像本身都不包含任何额外的构建环境,执行任务时都需要启动包含对应环境的镜像来执行任务。
我们的 Jenkins Master、Jenkins Slaves 都是跑在容器里面的,该如何在这些容器里面调用 docker run
命令启动包含 CI 环境的镜像呢? 在这些 CI 镜像里面,我们从源码编译完成后,又如何通过 docker build
将编译结果打包成 Docker 镜像,然后推送到内网仓库呢?
答案下面揭晓。
一、原理说明:/var/run/docker.sock
Docker 采取的是 Client/Server 架构,我们常用的 docker xxx
命令工具,只是 docker 的 client,我们通过该命令行执行命令时,实际上是在通过 client 与 docker engine 通信。
我们通过 apt/yum 安装 docker-ce 时,会自动生成一个 systemd 的 service,所以安装完成后,需要通过 sudo systemctl enable docker.service
来启用该服务。 这个 Docker 服务启动的,就是 docker engine,查看 /usr/lib/systemd/system/docker.service
,能看到有这样一条语句:
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
默认情况下,Docker守护进程会生成一个 socket(/var/run/docker.sock
)文件来进行本地进程通信,因此只能在本地使用 docker 客户端或者使用 Docker API 进行操作。 sock 文件是 UNIX 域套接字,它可以通过文件系统(而非网络地址)进行寻址和访问。
因此只要以数据卷的形式将 docker 客户端和上述 socket 套接字挂载到容器内部,就能实现 "Docker in Docker",在容器内使用 docker 命令了。具体的命令见后面的「示例」部分。
要记住的是,真正执行我们的 docker 命令的是 docker engine,而这个 engine 跑在宿主机上。所以这并不是真正的 "Docker in Docker".
二、示例
在容器内部使用宿主机的 docker,方法有二:
- 命令行方式:将
/usr/bin/docker
映射进容器内部,然后直接在容器内部使用这个命令行工具docker
- 需要的时候,也可以将
/etc/docker
文件夹映射到容器内,这样容器内的docker
命令行工具也会使用与宿主机同样的配置。
- 需要的时候,也可以将
- 编程方式:在容器内部以编程的方式使用 docker
- 通过 python 使用 docker: 在 Dockerfile 中通过
pip install docker
将 docker client 安装到镜像中来使用
- 通过 python 使用 docker: 在 Dockerfile 中通过
容器的启动方式也有两种,如下:
1. 直接通过 docker 命令启动
示例命令如下:
docker run --name <name> \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
--user root \
<image-name>:<tag>
**必须以 root 用户启动!(或者其他有权限读写 /var/run/docker.sock
的用户)**然后,在容器内就能正常使用 docker 命令,或者访问宿主机的 docker api 了。
2. 使用 docker-compose 启动
docker-compose.yml 文件内容如下:
version: ''3.3''
services:
jenkins-master:
image: jenkinsci/blueocean:latest
container_name: jenkins-master
environment:
- TZ=Asia/Shanghai # 时区
ports:
- "8080:8080"
- "50000:50000"
volumes:
- ./jenkins_home:/var/jenkins_home # 将容器中的数据映射到宿主机
- /usr/bin/docker:/usr/bin/docker # 为容器内部提供 docker 命令行工具(这个随意)
- /var/run/docker.sock:/var/run/docker.sock # 容器内部通过 unix socket 使用宿主机 docker engine
user: root # 必须确保容器以 root 用户启动!(这样它才有权限读写 docker.socket)
restart: always
然后通过 docker-compose up -d
即可后台启动容器。
Docker 中的 uid 与 gid
通过上面的操作,我们在容器内执行 docker ps
时,还是很可能会遇到一个问题:权限问题。
如果你容器的默认用户是 root,那么你不会遇到这个问题,因为 /var/run/docker.sock
的 onwer 就是 root.
但是一般来说,为了限制用户的权限,容器的默认用户一般都是 uid 和 gid 都是 1000 的普通用户。这样我们就没有权限访问 /var/run/docker.sock
了。
解决办法:
方法一(不一定有效):在构建镜像时,最后一层添加如下内容:
# docker 用户组的 id,通常都是 999
RUN groupadd -g 999 docker \
&& usermod -aG docker <your_user_name>
这样我们的默认用户,就能使用 docker 命令了。
P.S.
999
不一定是 docker 用户组,所以上述方法某些情况下可能失效。这时还是老老实实通过docker run -u root
启动容器吧。(或者在docker-compose.yml
中添加user: root
属性)
参考
- Docker in Docker - 王柏元
Docker Java API 开发
因为工作原因需要使用Java调用装有docker环境的机器完成打镜像 上传镜像库的操作,进过调查,发现了两个比较常用的Java API工具,分别是 docker-java 和 spotify-docker-client,Github地址分别如下:
docker-java:https://github.com/docker-java/docker-java
spotify-docker-client:https://github.com/spotify/docker-client
两者做个简单的对比:
Star/Fork(2018/12/19) | 文档丰富度 | API功能覆盖度 | 使用复杂度 | |
docker-java | 1188/638 | 相对完善 | 基本功能 | 简单 |
spotify-docker-client | 1101/450 | 相对完善 | 功能实现较多 | 相对复杂 |
1.升级docker版本(可选)
#删除旧的版本的docker
yum remove docker docker-common docker-selinux docker-engine
yum erase docker docker-common docker-client docker-compose
#设置docker yum源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
#update
yum update
#要先安装docker-ce-selinux-17.03.2.ce,否则安装docker-ce会报错
yum install https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm
#以查看所有仓库中所有docker版本,并选择特定版本安装
yum list docker-ce --showduplicates | sort -r
#由于repo中默认只开启stable仓库,故这里安装的是最新稳18.03.0.ce-1.el7.centos
yum install docker-ce
#安装指定的版本 例如: yum install docker-ce-17.12.0.ce-1.el7.centos
sudo yum install <FQPN>
#启动
systemctl start docker
#查看版本
docker version
2. 开启docker远程访问
默认情况下,Docker守护进程Unix socket(/var/run/docker.sock)来进行本地进程通信,而不会监听任何端口,因此只能在本地使用docker客户端或者使用Docker API进行操作。如果想在其他主机上操作Docker主机,就需要让Docker守护进程打开一个HTTP Socket,这样才能实现远程通信。
方法一:
vi /lib/systemd/system/docker.service
找到Execstart=/usr/bin/dockerd后加上-H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock 退出并且保存
方法二:
Centos 7.X docker-ce:
修改/etc/sysconfig/docker文件,在最后增加一行DOCKER_OPTS
# docker-latest daemon can be used by starting the docker-latest unitfile.
# To use docker-latest client, uncomment below lines
#DOCKERBINARY=/usr/bin/docker-latest
#DOCKERDBINARY=/usr/bin/dockerd-latest
#DOCKER_CONTAINERD_BINARY=/usr/bin/docker-containerd-latest
#DOCKER_CONTAINERD_SHIM_BINARY=/usr/bin/docker-containerd-shim-latest
DOCKER_OPTS="-H unix:///var/run/docker.sock -H 0.0.0.0:2375"
方法一二都需要使配置生效:
#docker.service changed on disk. Run ''systemctl daemon-reload'' to reload units.
systemctl daemon-reload
systemctl restart docker.service
Centos 6.x docker 1.X.X
$ sudo vi /etc/sysconfig/docker
other_args="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock"
$ sudo service docker restart
# centos docker的其它操作方式
$ sudo service docker start
$ sudo service docker stop
$ /bin/systemctl start docker.service
3.引入spotify-docker-client依赖(以标准maven工程为例)
错误分析:
1.systemctl start docker 报错,内容:
Dec 19 19:13:09 VM_0_4_centos dockerd[13361]: time="2018-12-19T19:13:09.334930508+08:00" level=warning msg="[!] DON''T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON''T KNOW WHA
Dec 19 19:13:09 VM_0_4_centos dockerd[13361]: time="2018-12-19T19:13:09.339318789+08:00" level=info msg="libcontainerd: new containerd process, pid: 13364"
Dec 19 19:13:10 VM_0_4_centos dockerd[13361]: time="2018-12-19T19:13:10.343022632+08:00" level=error msg="[graphdriver] prior storage driver overlay2 failed: driver not supported"
Dec 19 19:13:10 VM_0_4_centos dockerd[13361]: Error starting daemon: error initializing graphdriver: driver not supported
Dec 19 19:13:10 VM_0_4_centos systemd[1]: docker.service: main process exited, code=exited, status=1/FAILURE
Dec 19 19:13:10 VM_0_4_centos systemd[1]: Failed to start Docker Application Container Engine.
-- Subject: Unit docker.service has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit docker.service has failed.
--
-- The result is failed.
原因:/var/lib/docker 目录有旧的container 文件,版本变更后需要删除
解决办法:
mv /var/lib/docker /var/lib/docker.old
service docker start
rm -rf /var/lib/docker.old
Docker 学习笔记之 docker-save vs docker-export vs docker-commit
之前对这几个 command 是忘了记,记了混~所以写下笔记以巩固之。
1.docker save
docker save -h
Usage: docker save [OPTIONS] IMAGE [IMAGE...]
Save one or more images to a tar archive (streamed to STDOUT by default)
--help Print usage
-o, --output Write to a file, instead of STDOUT
从接的参数就可以猜到,直接接 image,不太可能导出单纯的文件系统(因为镜像本身就是分层存储的)
简单测试一下
docker save -o busybox.tar busybox && mkdir busybox && tar xf busybox.tar -C busybox
tree busybox
busybox
├── 2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749.json
├── 374004614a75c2c4afd41a3050b5217e282155eb1eb7b4ce8f22aa9f4b17ee57
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── manifest.json
└── repositories
docker load 与之匹配,将其(带历史地)导入到 docker images 中
docker load -i busybox.tar
2.docker export
docker export -h
Usage: docker export [OPTIONS] CONTAINER
Export a container''s filesystem as a tar archive
--help Print usage
-o, --output Write to a file, instead of STDOUT
从接的参数猜测,直接接 container,多半就是 dump rootfs 了
栗子测试一下:
docker run --name container -d busybox
docker export -o busybox.tar container && mkdir busybox && tar xf busybox.tar -C busybox
tree busybox -L 1
busybox
├── bin
├── dev
├── etc
├── home
├── proc
├── root
├── sys
├── tmp
├── usr
└── var
docker import 与之匹配
docker import busybox.tar my-busybox:1.0
docker images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# my-busybox 1.0 5bfea374dd5c 3 seconds ago 1.093 MB
注意:docker import 后面接的是 docker export 导出的文件,也就是一个文件系统,所以导入的镜像是不带历史的
使用 docker history $image_name 查看镜像,只有一层
3.docker commit
docker commit -h /tmp/pkg_debian (debian) choldrim-pc
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Create a new image from a container''s changes
-a, --author Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change=[] Apply Dockerfile instruction to the created image
--help Print usage
-m, --message Commit message
-p, --pause=true Pause container during commit
commit 是合并了 save、load、export、import 这几个特性的一个综合性的命令,它主要做了:
- 将 container 当前的读写层保存下来,保存成一个新层
- 和镜像的历史层一起合并成一个新的镜像
如果原本的镜像有 3 层,commit 之后就会有 4 层,最新的一层为从镜像运行到 commit 之间对文件系统的修改
docker commit container my-commit-image
docker history my-commit-image
IMAGE CREATED CREATED BY SIZE COMMENT
e86539128c67 5 seconds ago sh 0 B
2b8fd9751c4c 9 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0 B
<missing> 9 weeks ago /bin/sh -c #(nop) ADD file:9ca60502d646bdd815 1.093 MB
参考
关于Java API 操作Docker示例和java调用docker中的api的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Docker Client (another java docker client api)、Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock、Docker Java API 开发、Docker 学习笔记之 docker-save vs docker-export vs docker-commit的相关知识,请在本站寻找。
本文标签: