构建的docker容器转移到另一台可以通过SSH访问的机器上

我最近在我的一个业余项目中遇到了挑战。我需要将在一台机器上构建的docker容器转移到另一台可以通过SSH访问的机器上。我不想将我的容器推送到公共docker注册表,也不想设置自己的私有注册表。

使用内置工具

很快就在stack overflow找到了答案()

docker save <image> | bzip2 | \ ssh user@host bunzip2 | docker load

让我们分解一下:

docker save <image> 获取所有图像数据,并将其及其标签序列化为二进制数据流。docker load 接收二进制数据流并将其反序列化为带有标签的图像。bzip2压缩流并bunzip2解压缩流。ssh user@host some command ssh进入远程主机并运行指定的命令。

事实证明,docker load它能够自动解压缩bzipd内容,因此您可以将命令简化为:

docker save <image> | bzip2 | \ ssh user@host docker load

您可以删除,bzip2但是docker映像通常很大,从进行压缩bzip2可以节省大量带宽。

我仍然有一个问题。我通过慢速的3G Internet连接进行所有操作,并且远程主机已经具有要推送的映像中的大多数层,我只需要推送包含我的应用程序逻辑的微小新层即可。

Pushing With Layers

经过更多研究,我发现docker-push-ssh。使用此命令,您可以执行以下操作:

docker-push-ssh user@host <image>

它仅传输所需的层。为此:

在本地计算机上设置一个临时Docker注册表-这很容易做到,因为在名为docker的镜像中有一个docker注册表 registry:2难道一个docker push到本地注册表。因为它遍及本地计算机的网络,所以速度很快。使用SSH代理远程服务器上的端口,以便它连接到本地计算机上的代理。难道一个docker pull远程服务器上。该拉取在SSH隧道上运行,但是docker pull非常聪明,仅拉取它尚不具备的层。

我自己的解决方案

这是一个好主意,但是我遇到了三个问题:

它是用Python编写的,并且需要Python 2.7,我不想依赖于已安装。它已经有一段时间没有更新了,通常不会有什么问题,但是考虑到它依赖于旧版本的Python,这有点令人担忧。目前尚不清楚远程计算机上所需的特权级别。我希望能够将事情锁定下来,以便进行推送的用户只能将特定的已命名docker映像推送到远程计算机,而不能做其他任何事情。

为了解决所有这些问题,我决定在Node.js中创建一个名为docker-over-ssh的CLI 。尽管名称如此,但docker-over-ssh实际上完全与传输无关。它仅需要一种通过stdin和stdout与自身的远程实例进行通信的方法。要使用它,请docker-over-ssh同时在本地和远程计算机上安装。然后,您可以运行:

docker-over-ssh push <image> \ ssh user@host "docker-over-ssh pull <image>"

该图看起来与先前的解决方案完全相同。

该docker-over-ssh push命令将启动本地docker注册表,将映像推送到其中,然后运行“子命令”(在本示例中为ssh user@host "docker-over-ssh pull <image>"),并将tcp流量从该子命令的stdio代理到本地docker注册表。

该docker-over-ssh pull <image>命令启动一个本地TCP代理(用几行node.js代码编写),并将该代理连接到stdio,以便它可以与本地docker注册表通信。然后,它docker pull指向本地注册表运行。仅传输新的层,从而使所有工作保持高效。

用户唯一需要的许可是docker-over-ssh pull <image>在远程计算机上运行的能力,而无需任何其他操作。

与CircleCI一起使用

在这一点上,我有一个可行的解决方案,但是我想使它自动化,以便可以从CircleCI进行部署。向CircleCI添加SSH密钥非常容易。面临的挑战是如何通过其复杂的docker网络设置来完成这项工作:

您的本地代码(即终止代理的node.js代码)无法与使用运行的容器对话docker run。该docker守护进程(用于运行docker push)不能跟跑为主要CircleCI工作服务容器。

我找不到直接解决这两个问题的任何实用方法,但是我发现NGROK可以很容易地创建一个可以访问本地服务的Internet访问地址。有了这个,我能够告诉CircleCI启动Docker注册表作为构建服务,然后使用ngrok启动一个临时代理以将其公开给docker守护程序。它甚至支持使用用户名和密码(由我自动生成)来保护它,以确保一切安全。

最后,我要做的就是更新CircleCI配置:

docker: - image: circleci/node:12 environment: LOCAL_DOCKER_REGISTRY_PORT: 5000 - image: registry:2

并将DOCKER_REGISTRY_NGROK环境变量设置为我的ngrok API密钥,您可以免费获取。

然后,我向docker-over-ssh添加了一些代码来处理这两个环境变量。

结论

我做所有这些事情的原因是尝试安装一个dokku服务器,我可以在其中运行很多辅助项目,并避免在heroku上花费很少的金钱来处理很少使用的东西。我对这个问题的解决方案感到非常满意,但是由于Digital Ocean拥有托管的Kubernetes服务,而且价格似乎非常实惠,因此我现在决定研究Kubernetes 。这将需要我弄清楚运行具有某种身份验证/授权的持久性Docker注册表。

翻译自: