1. 前 言
在推送镜像文件到镜像仓库时,我们往往是在镜像文件所在的那个主机上,以 root 用户的权限,执行 docker push 命令,完成镜像推送的工作。
但有这么一种令人匪夷所思的人,他直接打一个离线的镜像包(docker save tomcat:latest > tomcat-892148dsadg-v1.tar)出来,比如 tomcat-892148dsadg-v1.tar ,然后通过邮件或者其它通讯工具发给你,让你推送到镜像仓库。
这时,你怎么搞?直接通过 shell 指令操作,可行吗?当然可以,只要在有 docker 环境的服务器上,
以 root 权限的用户执行下面 5 步就能完成:
- rz 命令,上传 tar 包到服务器上
- docker load -i xxxx.tar ,加载镜像文件
- docker login harbor仓库地址 -u 用户名 -p 密码 ,登录 harbor 仓库
- docker tag xxxx.tar harbor仓库地址/命名空间/xxxxx.tar,重新打标签
- docker push harbor仓库地址/命名空间/xxxxx.tar,推送镜像到 harbor 仓库
上面的方式,看着是很清晰地解决了问题,但一般来说,开发人员很少能直接操控服务器,更别提拿到 root 用户,在一些权限管控严格的企业,这种方式想想就好了,或者让运维来。
那还有其它方式吗?当然!那就直接用 java 操作 docker 命令实现离线的 tar包 推送至 harbor 仓库吧!
2. 编写工具类
2.1 引入依赖包
- 在 pom.xml 文件中引入下面的两个依赖
<dependency> <groupid>com.github.docker-java</groupid> <artifactid>docker-java</artifactid> <version>3.2.13</version> </dependency> <dependency> <groupid>com.github.docker-java</groupid> <artifactid>docker-java-transport-httpclient5</artifactid> <version>3.2.13</version> </dependency>
2.2 使用当前服务器的docker环境推送镜像
- application.properties 配置文件
harbor.username=harbor harbor.password=harbor123!
- 工具类
import com.github.dockerjava.api.dockerclient; import com.github.dockerjava.api.model.authconfig; import com.github.dockerjava.api.model.image; import com.github.dockerjava.core.defaultdockerclientconfig; import com.github.dockerjava.core.dockerclientimpl; import com.github.dockerjava.httpclient5.apachedockerhttpclient; import com.github.dockerjava.transport.dockerhttpclient; import lombok.extern.slf4j.slf4j; import org.springframework.beans.factory.annotation.value; import org.springframework.stereotype.service; import java.io.file; import java.io.fileinputstream; import java.io.ioexception; import java.io.inputstream; import java.time.duration; import java.util.list; /** * @author yuhuofei * @version 1.0 * @description 镜像上传工具类 * @date 2022/6/13 21:19 */ @service @slf4j public class dockerutils { @value("${harbor.username}") private string harborusername; @value("${harbor.password}") private string harborpassword; /** * 获取dockerclient对象 */ public dockerclient getdockerclient() { //创建defaultdockerclientconfig(当前服务器的默认配置) defaultdockerclientconfig config = defaultdockerclientconfig.createdefaultconfigbuilder().build(); //创建dockerhttpclient dockerhttpclient httpclient = new apachedockerhttpclient.builder() .dockerhost(config.getdockerhost()) .maxconnections(100) .connectiontimeout(duration.ofseconds(30)) .responsetimeout(duration.ofseconds(45)) .build(); //创建dockerclient return dockerclientimpl.getinstance(config, httpclient); } /** * @param file 镜像文件 * @param imagename 镜像名称,格式最好是:镜像名-镜像id-版本号.tar * @param harborloginurl harbor仓库地址 * @param imageid 镜像id * @param tag 版本号 */ public void uploadimage(file file, string imagename, string harborloginurl, string imageid, string tag) { log.info("上传镜像文件时,传递的参数:{},{},{},{},{}", file.getabsolutepath(), imagename, harborloginurl, imageid, tag); try (inputstream inputstream = new fileinputstream(file)) { dockerclient dockerclient = getdockerclient(); //harbor登录信息 authconfig autoconfig = new authconfig().withregistryaddress(harborloginurl).withusername(harborusername).withpassword(harborpassword); //加载镜像 log.info("====开始加载镜像====,{}", inputstream); dockerclient.loadimagecmd(inputstream).exec(); //获取加载镜像的名称,如果根据镜像名称去匹配,有可能重复,所以用镜像id去匹配 string uploadimagename = ""; list<image> list = dockerclient.listimagescmd().exec(); for (image image : list) { if (image.getid().contains(imageid)) { uploadimagename = image.getrepotags()[0]; } } //给镜像打上tag log.info("原始镜像名:{},要修改的镜像名:{}", uploadimagename, imagename); dockerclient.tagimagecmd(uploadimagename, imagename, tag).exec(); //推送镜像至镜像仓库 dockerclient.pushimagecmd(imagename).withauthconfig(autoconfig).start().awaitcompletion(); //push成功后,删除本地加载的镜像 dockerclient.removeimagecmd(imagename).exec(); dockerclient.removeimagecmd(uploadimagename).exec(); } catch (interruptedexception exception) { throw new baseexception("推送镜像到镜像仓库异常:{}", exception); } catch (ioexception e) { throw new baseexception("镜像文件流处理异常"); } } }
2.2 使用指定服务器的docker环境推送镜像
- application.properties 文件的配置
docker.server.url=tcp://xxxxxx:2376 docker.cert.path=/home/user/.docker/certs api.version=1.41 registry.url=https://index.docker.io/v1/ registry.username=docker registry.password=docker123! registry.email=xxxxxxx@qq.com harbor.username=harbor harbor.password=harbor123!
- 工具类
import com.github.dockerjava.api.dockerclient; import com.github.dockerjava.api.model.authconfig; import com.github.dockerjava.api.model.image; import com.github.dockerjava.core.defaultdockerclientconfig; import com.github.dockerjava.core.dockerclientimpl; import com.github.dockerjava.httpclient5.apachedockerhttpclient; import com.github.dockerjava.transport.dockerhttpclient; import lombok.extern.slf4j.slf4j; import org.springframework.beans.factory.annotation.value; import org.springframework.stereotype.service; import java.io.file; import java.io.fileinputstream; import java.io.ioexception; import java.io.inputstream; import java.time.duration; import java.util.list; /** * @author yuhuofei * @version 1.0 * @description 镜像上传工具类 * @date 2022/6/13 21:19 */ @service @slf4j public class dockerutils { @value("${docker.server.url}") private string dockerserverurl; @value("${docker.cert.path}") private string dcokercertpath; @value("${registry.user}") private string registryuser; @value("${registry.pass}") private string registrypass; @value("${registry.mail}") private string registrymail; @value("${registry.url}") private string registryurl; //harbor仓库登录用户名 @value("${harbor.username}") private string harborusername; //harbor仓库登录密码 @value("${harbor.password}") private string harborpassword; /** * 获取dockerclient对象 */ public dockerclient getdockerclient() { //创建defaultdockerclientconfig(指定docker服务器的配置) dockerclientconfig config = defaultdockerclientconfig.createdefaultconfigbuilder() .withdockerhost(dockerserverurl) .withdockertlsverify(true) .withdockercertpath(dcokercertpath) .withregistryusername(registryuser) .withregistrypassword(registrypass) .withregistryemail(registrymail) .withregistryurl(registryurl) .build(); //创建dockerhttpclient dockerhttpclient httpclient = new apachedockerhttpclient.builder() .dockerhost(config.getdockerhost()) .maxconnections(100) .connectiontimeout(duration.ofseconds(30)) .responsetimeout(duration.ofseconds(45)) .build(); //创建dockerclient return dockerclientimpl.getinstance(config, httpclient); } /** * @param file 镜像文件 * @param imagename 镜像名称,格式最好是:镜像名-镜像id-版本号.tar * @param harborloginurl harbor仓库地址 * @param imageid 镜像id * @param tag 版本号 */ public void uploadimage(file file, string imagename, string harborloginurl, string imageid, string tag) { log.info("上传镜像文件时,传递的参数:{},{},{},{},{}", file.getabsolutepath(), imagename, harborloginurl, imageid, tag); try (inputstream inputstream = new fileinputstream(file)) { dockerclient dockerclient = getdockerclient(); //harbor登录信息 authconfig autoconfig = new authconfig().withregistryaddress(harborloginurl).withusername(harborusername).withpassword(harborpassword); //加载镜像 log.info("====开始加载镜像====,{}", inputstream); dockerclient.loadimagecmd(inputstream).exec(); //获取加载镜像的名称,如果根据镜像名称去匹配,有可能重复,所以用镜像id去匹配 string uploadimagename = ""; list<image> list = dockerclient.listimagescmd().exec(); for (image image : list) { if (image.getid().contains(imageid)) { uploadimagename = image.getrepotags()[0]; } } //给镜像打上tag log.info("原始镜像名:{},要修改的镜像名:{}", uploadimagename, imagename); dockerclient.tagimagecmd(uploadimagename, imagename, tag).exec(); //推送镜像至镜像仓库 dockerclient.pushimagecmd(imagename).withauthconfig(autoconfig).start().awaitcompletion(); //push成功后,删除本地加载的镜像 dockerclient.removeimagecmd(imagename).exec(); dockerclient.removeimagecmd(uploadimagename).exec(); } catch (interruptedexception exception) { throw new exception("推送镜像到镜像仓库异常:{}", exception); } catch (ioexception e) { throw new exception("镜像文件流处理异常"); } } }
3. 说 明
- 上述的 java 服务,不能部署在 docker 环境的容器内,需要部署在 docker 主机上,不然在容器中运行的 java 服务,是没法操作到主机上的 docker 命令的
- 上传 tar 包文件的前端内容省略
- 这里上传的 tar 包命名格式最好是:镜像名-镜像id-版本号.tar
- 打出来的 tar 包,大小最好不要超过 20 g
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论