前言
使用harbor过程中,一直想使用harbor的api实现镜像的上传功能,但是实际上harbor是直接调用了docker registry的api,harbor层只是做了一个透传的功能,这个可以参考《harbor权威指南》这本书,参考官网接口以及网上大佬的思想,实现了一个Java版本,主要是实现了docker daemon上传的逻辑。
docker镜像tar包结构
实现上传首先需要将镜像的tar包解压,读取目录结构,一个典型的docker镜像包(使用docker save命令)结构如下:
.
├── 1ecf8bc84a7c3d60c0a6bbdd294f12a6b0e17a8269616fc9bdbedd926f74f50c
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── 6f4ec1f3d7ea33646d491a705f94442f5a706e9ac9acbca22fa9b117094eb720.json
├── aaac5bde2c2bcb6cc28b1e6d3f29fe13efce6d6b669300cc2c6bfab96b942af4
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── b63363f0d2ac8b3dca6f903bb5a7301bf497b1e5be8dc4f57a14e4dc649ef9bb
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── c453224a84b8318b0a09a83052314dd876899d3a1a1cf2379e74bba410415059
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── dd8ef1d42fbcccc87927eee94e57519c401b84437b98fcf35505fb6b7267a375
│ ├── VERSION
│ ├── json
│ └── layer.tar
├── manifest.json
└── repositories
清单文件manifet.json结构
[
{
"Config":"6f4ec1f3d7ea33646d491a705f94442f5a706e9ac9acbca22fa9b117094eb720.json",
"RepoTags":[
"alpine:filebeat-6.8.7-arm64"
],
"Layers":[
"aaac5bde2c2bcb6cc28b1e6d3f29fe13efce6d6b669300cc2c6bfab96b942af4/layer.tar",
"dd8ef1d42fbcccc87927eee94e57519c401b84437b98fcf35505fb6b7267a375/layer.tar",
"c453224a84b8318b0a09a83052314dd876899d3a1a1cf2379e74bba410415059/layer.tar",
"b63363f0d2ac8b3dca6f903bb5a7301bf497b1e5be8dc4f57a14e4dc649ef9bb/layer.tar",
"1ecf8bc84a7c3d60c0a6bbdd294f12a6b0e17a8269616fc9bdbedd926f74f50c/layer.tar"
]
}
]
manifest.json 包含了对这个tar包的描述信息,比如image config文件地址,tags说明,镜像layer信息,在解析的时候也是根据这个文件去获取关联的文件
上传流程
- 获取鉴权信息
- 检查layer.tar是否已经存在
- 上传layer.tar
- 上传image config
- 上传manifest(非包中的manifest.json而是Manifest struct)
-
核心实现类DockerImageUploadBiz.java
/** * @author Administrator */ @Service @Slf4j public class DockerImageUploadBiz { //远程仓库 @Value("${docker.remote.repo}") private String targetRepoAddress; //本地解压路径 @Value("${docker.upload.extractPath}") private String tarPath; //harbor用户名 @Value("${docker.harbor.userName}") private String userName; //密码 @Value("${docker.harbor.password}") private String password; /** * @param sourceTar 上传的镜像文件 * @param project 项目 */ public void push(File sourceTar, String project) { if (!sourceTar.exists()) { log.warn("Error!file is not exist!path:{}", sourceTar); return; } try { String unTarPath = FileUtil.doUnArchiver(sourceTar, tarPath); String manifest = FileUtil.readJsonFile(unTarPath + File.separator + "manifest.json"); JSONArray jsonArray = JSONObject.parseArray(manifest); if (Objects.isNull(jsonArray)) { log.warn("manifest convert error!path:{},content:{}", unTarPath + File.separator + "manifest.json", manifest); return; } for (Object arr : jsonArray) { JSONObject jsonObject = (JSONObject) arr; JSONArray repoTags = jsonObject.getJSONArray("RepoTags"); for (Object repoTag : repoTags) { String repo = repoTag.toString(); String substring = repo.substring(repo.lastIndexOf('/') + 1); String[] split = substring.split(":"); String imageName = split[0]; String tag = split[1]; log.info("imageName:{},tag:{}", imageName, tag); JSONArray layers = jsonObject.getJSONArray("Layers"); //1.上传layer log.info("========================STEP:1/3==============================="); log.info("PUSHING LAYERS STARTING..."); List<String> layerPathList = new ArrayList<>(layers.size()); int i = 1; for (Object layer : layers) { String layerPath = unTarPath + File.separator + layer.toString(); log.info("PUSHING LAYER:{}-{} ...", i, layers.size()); layerPathList.add(layerPath); pushLayer(project, layerPath, imageName); i++; } log.info("PUSHING LAYERS ENDED..."); log.info("========================STEP:2/3==============================="); //2.上传config log.info("PUSHING CONFIG STARTING...");

最低0.47元/天 解锁文章
888

被折叠的 条评论
为什么被折叠?



