# 如何高效迁移容器镜像

2024突然新增了几个PoC，需要在客户现场私有化部署(我的快速部署方案成了不二之选)。但是客户现场情况复杂：

* 客户所提供部署用主机为内网机器，不允许访问公网；
    
* 客户只提供一个跳板机可供我们远程连接；
    
* 跳板机可访问公网；
    

## 历史做法

使用[seafile](https://www.seafile.com/home/)

> Seafile 是一款开源的企业云盘，注重可靠性和性能。支持 Windows, Mac, Linux, iOS, Android 平台。支持文件同步或者直接挂载到本地访问。

PoC所需要的容器镜像加起来有50G左右，这里以nginx镜像为例：

1. ```bash
    docker save nginx -o nginx.tar
    ```
    
2. 将`nginx.tar` 上传到seafile上
    
3. 远程连接到客户的跳板机，从seafile上下载nginx.tar包
    
4. 解压并上传镜像到客户的镜像中心
    
    ```bash
    docker load -i nginx.tar
    docker tag nginx yourharbor.com/dev/nginx
    docker push theharbor.com/dev/nginx
    ```
    

### 问题

因为网速的原因，光是上传下载tar包，就相当费时间，整个过程相当地不友好，这种低效会让人非常难受，得想想办法了。

分析确定的现状：

1. 不能用公网的镜像中心
    
2. 只能用seafile  
    

## 解决办法

### 压缩

在这种情况下只能想办法减少镜像包大小， 我最先想到是使用高压缩比，比如xz

> xz使用LZMA算法进行压缩,压缩率可以达到gzip的2~3倍,是目前Linux系统中压缩率最高的压缩程序。但压缩速度非常慢。xz压缩文件的扩展名为.xz

nginx.tar: 271M

压缩后

```bash
tar -cJf nginx.tar.xz nginx.tar
```

nginx.tar.xz: 64M，压缩体积相当出色

```bash
tar -cJf nginx.tar.xz nginx.tar  118.46s user 1.92s system 95% cpu 2:06.01 total
tar -xf nginx.tar.xz  5.00s user 0.22s system 94% cpu 5.499 total
```

正如前面提到xz的缺点，压缩需2min左右，解压超过5s，但，高压缩比带来收益还是很可观的，从271M-&gt;64M，网速相对稳定的情况下，数据从seafile上传时间减少到原先的1/4，不能不说很优秀了。但是，xz对于大文件压缩时间感人，略加测试后，所以果断弃坑。那，还有没有其它的办法呢？

### 终极大招

前面提到nginx.tar文件大小为271M，但是使用docker images查看镜像大小为141M

```bash
nginx        latest    605c77e624dd   2 years ago   141MB
```

而在Harbor上看到的大小为仅为`54.09M` 而已，这个体积甚至低于nginx.tar.xz的64M，这已经不能用优秀来形容了，but why?

* harbor上显示的是压缩后的镜像体积；
    
* docker images看到的是镜像下载到本地后展开的体积（可以试着下载一个大一点的镜像，可以观察到`Downloading`、`Extracting`）过程，可以印证这一点。
    
* docker save: 因为docker镜像采用的是分层存储方式,每一层只存储与上一层的差异内容。save命令将整个镜像一起保存,导致重复内容也会被重复保存,增加了文件的大小; save命令保存的是完整可运行的镜像,包含所有的所有历史版本、元数据、环境变量和配置
    

经过以上分析可知，docker镜像中心对镜像的存储优化已经做到极致了(仅限于笔者的认知)，所以解决方案也就呼之欲出了: 使用镜像中心 harbor? 必须是registry

> Distribution implementation for storing and distributing of container images and artifacts

当然不是，轻量级本地registry即可，将镜像数据挂载到本地的 ***image\_data*** 目录

```bash
docker run -d -p 12345:5000 --restart=always --name registry \
  -v `pwd`/image_data:/var/lib/registry \
  registry:2
```

registry体积小，占用资源少，在这个场景下无疑是比harbor更好的选择

重新打tag

```bash
docker tag nginx localhost:12345/dev/nginx
```

推送

```bash
docker push localhost:12345/dev/nginx
```

看一下image\_data目录大小

```bash
 55M	image_data
```

xz压缩后的体积

```bash
 53M	image_data.tar.xz
```

压缩后仅减少了2M，足以可见docker对容器镜像存储的压缩技术有多优秀了。所以，xz级别的压缩就显得没那么必要了，普通tar包即可

```bash
 54M	image_data.tar
```

将 image\_data.tar 上传到seafile，客户现场启用本地registry，剩下就是一些体力活了，如果客户使用的是harbor，还可以直接使用harbor的**复制管理**功能，稍加配置即可完成镜像从本地registry到客户的harbor这一步，好了，打完收工。

## 总结

念念不忘，必有回响。对于低效的不满是因，想到registry是灵光一现，也是果。高压缩比方式是最容易想到的，但是细细整理下来，才发现docker生态对于镜像存储优化下了很大功夫，实在是让人佩服。这让我想起2020年还在网银时，当时老大亮哥给我分一任务，好像是梳理镜像下载的瓶颈，现在我只记得当时使用了sisdig各种工具测试，最后得出的结论是网络是瓶颈，但是后来又发现本地解压也很费时间，CPU为陡然变高，很可惜当时没有将过程记录下来，还是要多写哇。然后本篇我也是先做完再梳理的，也会有新的收获，最重要的是将数据对比记录并展现出来了。感叹当年**Solomon Hykes**做docker的魄力，也对“docker”背后公司现在的处境不胜唏嘘，最后祝愿**Solomon Hykes** 的新项目[Dagger](https://dagger.io/)越来越好。

参考：

* [镜像和容器的基本操作](https://www.qikqiak.com/k8s-book/docs/3.%E9%95%9C%E5%83%8F%E5%92%8C%E5%AE%B9%E5%99%A8%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C.html)
    
* [https://hub.docker.com/\_/registry](https://hub.docker.com/_/registry)
