Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Distribution Registry 的設置與維護

在這篇文章中,我們將會聊一聊如何設置 Docker 私有影像倉庫,包括標籤命名、SSL 配置及垃圾回收等操作,以確保 Docker Image 的有效管理。

Docker Tag 命名

一般來講,同一個docker image會提供多個不同的版本,每個版本會附予不同的tag,以作標識。但以docker image的維護者來講,它的tag通常代表的是自己程式的版本號。不過這個版本號卻存在很多變數,就讓筆者好好地逐一說明。

程式的版本號

在沒有Docker的年代,其實所有軟件在發佈時,都會標示版本號,方便使用方明確追蹤問題,自行選擇升級、降級以解決相容性問題。大家要重現問題,也能清清楚地重現。所以docker image的tag,在某程度,都是代表發佈自己的程式版本號。但以前的年代,軟件底層的依賴,例如OS層面的共享程式庫,則不在發佈的管控中,所以過去的程式,在跨電腦安裝時,都會出現缺少某些共享庫的問題。而使用了Docker後,image以內的共享庫的都會在打包的那一刻固定和發佈,就不會有漏的問題。

庫更新,怎麼辦

上面說到image可以打包共享庫,但問題是共享庫也會有安全性更新問題,那麼對docker image的維護者來講,它自己的tag又該如何命名?

因為庫的量可大可少,所以一般來說,都不可能完全把各個庫的版本號寫在自己的tag上。退而求其次,就是用"版本號+日期",庫的細版本號,就存在原始碼當中。Ubuntu 就是這樣的例子。

不過"版本號+日期"的命名方式真的方便嗎?每次下遊用戶想更新去最近版本,都要自己找一次最近的日期。這樣對很多用戶來講都不夠方便。所以docker又提供了一個重tag的功能。例如ubuntu:noble,在早些時候指著noble-20240904.1,然後過幾天,又指向更新的noble-20241009。更常見的是latest,每次image都預設會存在,docker也希望大家會定期更新這個tag,讓大家可以更易地找到最新版本。

註: 這跟git tag有所不同,git tag並不預期會變的。當協作者收到tag後,那怕上遊刻意更新tag指針,協作者沒有刪除原tag之前,都不會知道tag更新去了哪裏。

我們該如何選

在發佈方和引用方來講,引用時可以明確使用唯一的"版本號+日期",對穩定性來講是有意義的。不過多多少少,會產生額外的時間成本。發佈方來說,就是多用了一些儲存空間,方便引用方可以隨時找到舊(庫)版本。而引用方,就要手動修改引用號,作為驗收依據,自動更新的難度比較大。

但對於自動更新要求比較大的情況下,可能就是使用latest或者會隨時更新的share tag(共用tag)比較實際。但我們也依然要定一些方式去版本更新記錄,例如:同時使用

  • beta
  • latest
  • archive

每日自動更新beta,只有所有測試都通過時,才把archive指向現在的latest,再把latest指向現在的beta。這樣做的好處是,核心的docker stack檔案改變的機會較少,也可以免除docker swarm做太細緻的權限管理。

Private Registry 私有影像倉庫

Distribution ,原本是由 docker 自己發行的私有影像倉庫 ,原名就為 “regsitry”。

如果 server 群沒有互聯網,又或對私隱很有要求,需要自建一個最簡單的倉庫,可以用這個。當然,那台機第一次必需經互聯網。架起後就可以斷網,並由其他 client 提送新的 registry image更新。

Registry Server 起動方式

最簡單的起動方式,但什麼都不設定。

docker run -d -p 5000:5000 --name registry registry:3

若想要加入 SSL,讓你的 client 不會認為它是不安全的 registry ,最簡易可以寫成 docker compose, 由 docker compose up -d 執行。

# docker-compose.yml
registry:
  restart: always
  image: registry:3
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
  volumes:
    - /path/data:/var/lib/registry
    - /path/certs:/certs

上述的 environment 中,有條件的話,還請設定需要登入才能訪問限制。最簡單,可以使用 apache http header 驗證方式。

# docker-compose.yml
registry:
  restart: always
  image: registry:3
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
+   REGISTRY_AUTH: htpasswd
+   REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
+   REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /path/data:/var/lib/registry
    - /path/certs:/certs
+   - /path/auth:/auth

REGISTRY_AUTH, REGISTRY_AUTH_HTPASSWD_PATH, REGISTRY_AUTH_HTPASSWD_REALM 的值照抄就好,然後/path/auth/htpasswd 就需要以 htpasswd 的格式提供內容 apache password_encryptions。即是以下那個樣子

USERNAME_1:BCRYPT_HASH_1
USERNAME_2:BCRYPT_HASH_2
USERNAME_3:BCRYPT_HASH_3

Client 連線方式

一切都設定好後,在 client 端,就可以登入並推送你的 image,(題外話,cli登入的都是以明文的方式存在電腦中,所以不要隨便在公開的地方存入自己的帳號)

# login
docker login YOUR_DOMAIN:5000
# try re-upload image
docker image tag registry:3 YOUR_DOMAIN:5000/registry:3
docker image push YOUR_DOMAIN:5000/registry:3

如果 server 端沒有提供SSL,那麼 client 就只能設定 http 的不安全連線。 https://distribution.github.io/distribution/about/insecure/

修改 client 端的 /etc/docker/daemon.json (Windows Docker Desktop請經 Gui修改),然後重啟 client 端的 docker

{
  "insecure-registries" : ["YOUR_DOMAIN:5000"]
}

Registry Server 維護 - Garbage collection 垃圾回收

當我們設立了自己的 Registry 倉庫之後,少不免就是要維護硬碟的用量。很多過期的 Image ,沒有需要,那就手動刪除,然後進行 Garbage collection (垃圾回收)。另一種情況,就如前述教學中,大家使用統一版本號,例如 latest ,表面上看似只有一個 tag ,但其實底下可能已經藏有多個不同的版本,也需要經過Garbage collection來清理空間。

因為回收過程比較危險,所以官方並不建議自動做,以下就簡單講講為了做刪除和回收,設定檔要怎樣改。為方便改設定,我們更新 docker compose yaml 檔,把 server config 都帶到 container 外面。

registry:
  restart: always
  image: registry:3
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /path/data:/var/lib/registry
    - /path/certs:/certs
    - /path/auth:/auth
+   - /path/config.yml:/etc/distribution/config.yml

config.yml 就如下所示,為了提供 API 刪除 image 的可能,storage.delete.enbled 要為 true,又為著之後進行回收時,可以避免有人於回收中途上載,所以預先加入 storage.maintenance.readonly.enabled 的控制項。回收之前要把readonly改為true,回收後再調為false。 每次修改完,記得重啟一下 docker service 。

storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
  maintenance:
    readonly:
      enabled: false

Garbage collection 指令

# inside container
# bin/registry garbage-collect [--dry-run] [--delete-untagged] [--quiet] /path/to/config.yml
bin/registry garbage-collect --delete-untagged=true /etc/docker/registry/config.yml

# outside container, at host level
docker exec -it YOUR_CONATINER_NAME bin/registry garbage-collect --delete-untagged=true /etc/docker/registry/config.yml

draft

some image keep using same tag to keep check latest version; but older blob is not able to be deleted according to normal manifest garbage collection. Must use --delete-untagged flag to delete unused blob

quote from offical web site https://distribution.github.io/distribution/about/garbage-collection/ The --delete-untagged option can be used to delete manifests that are not currently referenced by a tag.

Reference