我最近在我的一个业余项目中遇到了挑战。我需要将在一台机器上构建的docker容器转移到另一台可以通过SSH访问的机器上。我不想将我的容器推送到公共docker注册表,也不想设置自己的私有注册表。
使用内置工具
很快就在stack overflow找到了答案(https://stackoverflow.com/a/26226261/272958)
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
它能够自动解压缩bzip
'd内容,因此您可以将命令简化为:
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注册表。
网友评论