Container Image 影分身之術 — 失敗篇

smalltown
Starbugs Weekly 星巴哥技術專欄
9 min readAug 3, 2021

Background

一直有一個小問題存在於目前的架構中,雖然不是很嚴重,但遇到的時候就會覺得有點煩躁,那就是 Kubernetes 下載 Container Image 時遇到 Failed to pull image …,遇到這個問題通常兩種可能:

  • Container Image 不存在:整個不存在,或是該 Tag 不存在
  • Container Image 不能載:K8s 沒有權限從該 Registry 下載

而自己遇到這個問題時通常不是該 Container Image 真的不存在,或是沒有權限去下載,而是該 Container Image 沒有存放於正確的 Container Registry 供 Kubernetes 去抓取,這是什麼意思?因為在組職內部的 Container Registry 可能會有很多個或是很多種,例如可以分成 Public,Private,Development, Production…等,這就可能會導致 Container Image 存放在某個 Kubernetes Cluster 碰觸不到的 Registry,這時候或許就有人想問,為什麼要那麼麻煩,分那麼多個?

The Reason Why

Public vs Private Registry

Container Image 都透過同一個 Public Container Registry 上傳或是下載就好啦!例如 Docker Hub,Quay.io…等,這樣會有什麼問題嗎?

  • High Availability: Public Registry 的 Container Image 是不是有可能會突然存取不到?例如 Registry Service 掛點,或是 Image Owner 把它砍掉…等,這時候應用服務的 K8s Pod 因為某些原因被分配到某一台沒有暫存過該 Container Image 的 Node,該應用服務就會無法正常運行,假如自己有準備其他的 Private Container Registry 就可以避免
  • Security: 有些公司的政策並不允許內部應用服務直接連線到 Internet 抓取檔案,因為 1) 會無法管控應用服務下載的 Container Image,無法得知該 Image 是否含有風險性高的 Vulnerability 或是會不會導致 Supply Chain Attack,2) 假如有惡意人士擁有可以操縱 K8s 的權限,他也可以連到 Internet 直接下載有問題的 Container Image 回來使用
  • Speed & Efficiency: Container Image 可以用越快的速度下載到 K8s Node 中,Pod 就可以用越快的速度開起來,自己就近準備的 Private Container Registry 的下載速度一定要比 Public 來得快才對

Development vs Production Registry

再來就是開發環境的 Container Image 其實要經過一些測試階段,確定沒有問題才能夠部署到 Production 環境,所以理論上 Production 環境的 K8s 是不被允許直接使用 Development 環境的 Container Image,有人會透過 Image Tag 來做區分,但假如可以從 Registry 就直接一刀兩斷,那就不會有不預期的現象發生

Requirements

看完上面的分析就會覺得人生好難 T_T 而得到的結論就是不太可能只使用一個 Container Registry 就解決所有的問題,一定會有多個 Container Registry 需要同時使用,所以才在一開始提到自己會遇到 Container Image 沒有存放在正確的 Registry 中,這時候就會開始幻想,要是有個工具可以幫忙同步這些 Container Image 該有多好啊!自己希望至少要有底下兩個功能

  • 支援 Image Wildcard Tag:這樣就可以只同步某一些 Image,不需要浪費時間和頻寬去同步不需要的 Container Image
  • 由 K8s CRD 控制同步組態:因為要同步的東西一定不少,透過 K8s CRD 的話會比較好管理組態

自己稍微研究了一下,只有找到滿足第一點的開源工具 Dregsy,底下就來試用看看這個 Container Registry 同步工具

Demonstrate

假設我們想要使用 golang 這個存放於 Docker Hub 的 Container Image,但為了預防萬一,想要將它備份到 quay.iosmalltown/golang ,避免某一天因為什麼原因在 Docker Hub 的 Container Image 突然無法下載了 !

先證明 quay.io 這邊的 Container Registry 是空的XD 避免有作弊的嫌疑

接著準備組態來讓 Dregsy 知道要把 Container Registry 同步到哪邊去,這邊有幾個地方想要在特別說明一下

  • Relay 可以選擇使用 skopeo 或是 docker,選擇 skopeo 的話可以不用把 container image 拉到本地之後再推到遠端去,是比較方便的做法
  • source 跟 target 分別就是要將哪個來源的 Container Registry 複製到哪個目標 Container Registry 去,其中 auth 是將 username 和 password json 格式的字串進行 base64 encode,例如 將 {“username”: “smalltown”, “password”: “Change_Me_Please”} 做完 base64 encode 會變成 eyJ1c2VybmFtZSI6ICJzbWFsbHRvd24iLCAicGFzc3dvcmQiOiAiQ2hhbmdlX01lX1BsZWFzZSJ9Cg==
  • mappings 就是主要重點,這邊希望它把 golang 官方使用 alpine 3.14 加上 golang 1.16 建構出來的 container image 同步到 smalltown/golang,所以使用了正規表示式來描述 Image Tag
relay: skopeo
tasks:
- name: smalltown_gloang
interval: 60
verbose: true
source:
registry: registry.hub.docker.com
target:
registry: quay.io
auth: ******
mappings:
- from: library/golang
to: smalltown/golang
tags:
- 'regex: 1\.16.*-alpine3\.14'

最後開始試著同步看看

~$ docker run --rm -v #{PATH}/config.yaml:/config.yaml xelalex/dregsytime="2021-08-01T11:31:44Z" level=info msg="dregsy 0.4.1"
time="2021-08-01T11:31:44Z" level=info msg="skopeo version 1.3.1 commit: 1bb50ac33996b2fbf512d82f48d7ca2931bd0eb4\n"
time="2021-08-01T11:31:44Z" level=info msg="relay ready" relay=skopeo
time="2021-08-01T11:31:44Z" level=info msg="waiting for next sync task..."
time="2021-08-01T11:31:44Z" level=info msg="syncing task" source=registry.hub.docker.com target=quay.io task=smalltown_
time="2021-08-01T11:31:44Z" level=info msg=mapping from=/library/golang to=/smalltown/golang
time="2021-08-01T11:31:44Z" level=info msg="refreshing credentials" registry=registry.hub.docker.com
time="2021-08-01T11:31:44Z" level=info msg="refreshing credentials" registry=quay.io
time="2021-08-01T11:31:48Z" level=info msg="syncing tag" tag=1.16-alpine3.14
time="2021-08-01T11:32:00Z" level=info msg="syncing tag" tag=1.16.5-alpine3.14
time="2021-08-01T11:33:51Z" level=info msg="syncing tag" tag=1.16.6-alpine3.14
time="2021-08-01T11:34:03Z" level=info msg="waiting for next sync task..."

看 Log 顯示的滿正常的,不過實際到 quay.io 查看時卻發現除了 1.16 相關的之外,連其他版本的 Tag 也被同步過來了,這實在是有點囧,重看一下 README.md ,發現作者有提到 regex 功能還在 Alpha 階段XD 我把組態中的正規表示式改成特定版本再重試一次後,看來結果是正常的,所以正規表示式功能看來還不穩定

Conclusion

用完覺得勉強堪用,不過假如扣掉 Image Tag 使用正規表示式功能的話,其實就跟另外兩個找到的工具 plexsystems/sinkerAliyunContainerService/image-syncer 差不多了,看來目前在這塊領域還是沒有相對完美的解決方案,看到這邊的讀者有推薦的工具或是其他作法的話,歡迎提出來一起討論 m(_ _)m 自己則是想要發幾個 PR 來把正規表示式跟利用 K8s CRD 控制同步組態的功能補上,然後再另外寫個成功篇XD

--

--

smalltown
Starbugs Weekly 星巴哥技術專欄

原來只是一介草 QA,但開始研究自動化維運雲端服務後,便一頭栽進 DevOps 的世界裏,熱愛鑽研各種可以提升雲端服務品質及增進團隊開發效率的開源技術