我最近在我的一個(gè)業(yè)余項(xiàng)目中遇到了挑戰(zhàn)乐埠。我需要將在一臺機(jī)器上構(gòu)建的docker容器轉(zhuǎn)移到另一臺可以通過SSH訪問的機(jī)器上。我不想將我的容器推送到公共docker注冊表,也不想設(shè)置自己的私有注冊表。
使用內(nèi)置工具
很快就在stack overflow找到了答案(https://stackoverflow.com/a/26226261/272958)
docker save <image> | bzip2 | \
ssh user@host 'bunzip2 | docker load'
讓我們分解一下:
-
docker save <image>
獲取所有圖像數(shù)據(jù)姨蟋,并將其及其標(biāo)簽序列化為二進(jìn)制數(shù)據(jù)流。 -
docker load
接收二進(jìn)制數(shù)據(jù)流并將其反序列化為帶有標(biāo)簽的圖像立帖。 -
bzip2
壓縮流并bunzip2
解壓縮流眼溶。 -
ssh user@host 'some command'
ssh進(jìn)入遠(yuǎn)程主機(jī)并運(yùn)行指定的命令。
事實(shí)證明晓勇,docker load
它能夠自動解壓縮bzip
'd內(nèi)容堂飞,因此您可以將命令簡化為:
docker save <image> | bzip2 | \
ssh user@host 'docker load'
您可以刪除灌旧,bzip2
但是docker映像通常很大,從進(jìn)行壓縮bzip2
可以節(jié)省大量帶寬酝静。
我仍然有一個(gè)問題节榜。我通過慢速的3G Internet連接進(jìn)行所有操作,并且遠(yuǎn)程主機(jī)已經(jīng)具有要推送的映像中的大多數(shù)層别智,我只需要推送包含我的應(yīng)用程序邏輯的微小新層即可宗苍。
Pushing With Layers
經(jīng)過更多研究,我發(fā)現(xiàn)docker-push-ssh薄榛。使用此命令讳窟,您可以執(zhí)行以下操作:
docker-push-ssh user@host <image>
它僅傳輸所需的層。為此:
- 在本地計(jì)算機(jī)上設(shè)置一個(gè)臨時(shí)Docker注冊表-這很容易做到敞恋,因?yàn)樵诿麨閐ocker的鏡像中有一個(gè)docker注冊表
registry:2
- 難道一個(gè)
docker push
到本地注冊表丽啡。因?yàn)樗榧氨镜赜?jì)算機(jī)的網(wǎng)絡(luò),所以速度很快硬猫。 - 使用SSH代理遠(yuǎn)程服務(wù)器上的端口补箍,以便它連接到本地計(jì)算機(jī)上的代理。
- 難道一個(gè)
docker pull
遠(yuǎn)程服務(wù)器上啸蜜。該拉取在SSH隧道上運(yùn)行坑雅,但是docker pull非常聰明,僅拉取它尚不具備的層衬横。
我自己的解決方案
這是一個(gè)好主意裹粤,但是我遇到了三個(gè)問題:
- 它是用Python編寫的,并且需要Python 2.7蜂林,我不想依賴于已安裝遥诉。
- 它已經(jīng)有一段時(shí)間沒有更新了,通常不會有什么問題噪叙,但是考慮到它依賴于舊版本的Python矮锈,這有點(diǎn)令人擔(dān)憂。
- 目前尚不清楚遠(yuǎn)程計(jì)算機(jī)上所需的特權(quán)級別睁蕾。我希望能夠?qū)⑹虑殒i定下來苞笨,以便進(jìn)行推送的用戶只能將特定的已命名docker映像推送到遠(yuǎn)程計(jì)算機(jī),而不能做其他任何事情惫霸。
為了解決所有這些問題,我決定在Node.js中創(chuàng)建一個(gè)名為docker-over-ssh的CLI 葱弟。盡管名稱如此壹店,但docker-over-ssh實(shí)際上完全與傳輸無關(guān)。它僅需要一種通過stdin和stdout與自身的遠(yuǎn)程實(shí)例進(jìn)行通信的方法芝加。要使用它硅卢,請docker-over-ssh
同時(shí)在本地和遠(yuǎn)程計(jì)算機(jī)上安裝射窒。然后,您可以運(yùn)行:
docker-over-ssh push <image> \
ssh user@host "docker-over-ssh pull <image>"
該圖看起來與先前的解決方案完全相同将塑。
該docker-over-ssh push
命令將啟動本地docker注冊表脉顿,將映像推送到其中,然后運(yùn)行“子命令”(在本示例中為ssh user@host "docker-over-ssh pull <image>"
)点寥,并將tcp流量從該子命令的stdio代理到本地docker注冊表艾疟。
該docker-over-ssh pull <image>
命令啟動一個(gè)本地TCP代理(用幾行node.js代碼編寫),并將該代理連接到stdio敢辩,以便它可以與本地docker注冊表通信蔽莱。然后,它docker pull
指向本地注冊表運(yùn)行戚长。僅傳輸新的層盗冷,從而使所有工作保持高效。
用戶唯一需要的許可是docker-over-ssh pull <image>
在遠(yuǎn)程計(jì)算機(jī)上運(yùn)行的能力同廉,而無需任何其他操作仪糖。
與CircleCI一起使用
在這一點(diǎn)上,我有一個(gè)可行的解決方案迫肖,但是我想使它自動化锅劝,以便可以從CircleCI進(jìn)行部署。向CircleCI添加SSH密鑰非常容易咒程。面臨的挑戰(zhàn)是如何通過其復(fù)雜的docker網(wǎng)絡(luò)設(shè)置來完成這項(xiàng)工作:
- 您的本地代碼(即終止代理的node.js代碼)無法與使用運(yùn)行的容器對話
docker run
鸠天。 - 該
docker
守護(hù)進(jìn)程(用于運(yùn)行docker push
)不能跟跑為主要CircleCI工作服務(wù)容器。
我找不到直接解決這兩個(gè)問題的任何實(shí)用方法帐姻,但是我發(fā)現(xiàn)NGROK可以很容易地創(chuàng)建一個(gè)可以訪問本地服務(wù)的Internet訪問地址稠集。有了這個(gè),我能夠告訴CircleCI啟動Docker注冊表作為構(gòu)建服務(wù)饥瓷,然后使用ngrok啟動一個(gè)臨時(shí)代理以將其公開給docker
守護(hù)程序剥纷。它甚至支持使用用戶名和密碼(由我自動生成)來保護(hù)它,以確保一切安全呢铆。
最后晦鞋,我要做的就是更新CircleCI配置:
docker:
- image: circleci/node:12
environment:
LOCAL_DOCKER_REGISTRY_PORT: '5000'
- image: registry:2
并將DOCKER_REGISTRY_NGROK
環(huán)境變量設(shè)置為我的ngrok API密鑰,您可以免費(fèi)獲取棺克。
然后悠垛,我向docker-over-ssh添加了一些代碼來處理這兩個(gè)環(huán)境變量。
結(jié)論
我做所有這些事情的原因是嘗試安裝一個(gè)dokku服務(wù)器娜谊,我可以在其中運(yùn)行很多輔助項(xiàng)目确买,并避免在heroku上花費(fèi)很少的金錢來處理很少使用的東西。我對這個(gè)問題的解決方案感到非常滿意纱皆,但是由于Digital Ocean擁有托管的Kubernetes服務(wù)湾趾,而且價(jià)格似乎非常實(shí)惠芭商,因此我現(xiàn)在決定研究Kubernetes 。這將需要我弄清楚運(yùn)行具有某種身份驗(yàn)證/授權(quán)的持久性Docker注冊表搀缠。