天天看點

window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收

  • 0x00 前言簡述
  • 0x01 基礎安裝
    • 1) 基礎指令
    • 2) 基礎配置
    • 3) 生産執行個體
  • 0x02 Registry 目錄結構
  • 0x03 Registry API
    • API 一覽
    • 實際示例
  • 0x04 Registry GC
    • Shell 腳本
  • 0x05 配置檔案解析
    • config.yaml 檔案一覽
  • 0x06 入坑解決

0x00 前言簡述

描述:本來我想直接寫Harbor的Docker鏡像倉庫搭建配置與使用,但是覺得還是應該從基礎的Docker的Registry鏡像講起從安全建構到GC回收同時加深學習映像;

官網介紹:Registry是一個無狀态的,高度可擴充的伺服器端應用程式,存儲,讓你分發圖像。它是開源的根據許可Apache許可證。

此時就不在詳細講解Registry的介紹了,有興趣的朋友可以參見官網文檔或者前面我筆記中的介紹;

以下是一些知識點複習:

  • (1) Registry是一個幾種存放image并對外提供上傳下載下傳以及一系列API的服務,Registry與鏡像的關系可以想象成自己機器上的源碼和遠端SVN或者Git服務的關系,可以很容易和本地源代碼以及遠端Git服務的關系相對應。
  • (2) Registry開源常用于建構私有鏡像倉庫;

Q:為什麼不直接采用Docker官網Hub作為存儲鏡像的地方?

答:我認為主要是以下幾個方面的影響

1.存儲空間有限

2.上傳/拉取速度有限

3.企業内部敏感開發項目(如果是您肯定不會上傳到别人的伺服器中)

4.免費開源

反之使用Registey好處:

  • 鏡像存儲位置由您掌握
  • 全面管理控制自己的鏡像
  • 鏡像存儲和配置設定緊密內建到您的内部開發流程

Registry 版本說明:

  • Docker Registry 1.0版本(hub/docker.io等公共的鏡像倉庫還支援,安全性以及相容性不如V2.0)
  • Docker Registry 2.0版本在安全性和性能上做了諸多優化,并重新設計了鏡像的存儲的格式;(

    Docker目前1.6之後支援V2

    )

名詞解釋:

  • 1.repository name(存儲庫名詞) 存儲庫指在庫中存儲的鏡像。

    /project/redis:latest

    • 經典存儲庫名稱由2級路徑構成,每級路徑小于30個字元,V2的api不強制要求這樣的格式。
    • 每級路徑名至少有一個小寫字母或者數字,使用句号,破折号和下劃線分割。更嚴格來說,它必須符合正規表達式:

      [a-z0-9]+[._-][a-z0-9]+)

    • 多級路徑用/分隔
    • 存儲庫名稱總長度(包括/)不能超過256個字元
  • 2.digest(摘要) 摘要是鏡像每個層的唯一标示。雖然算法允許使用任意算法,但是為了相容性應該使用sha256。
例如 - sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b# 用一個簡單的例子,在僞代碼來示範摘要計算let C = 'a small string'let B = sha256(C)let D = 'sha256:' + EncodeHex(B)let ID(C) = D# python僞代碼import hashlib
C = 'a small string'
B = hashlib.sha256(C)
D = 'sha256:' + B.hexdigest()
           

0x01 基礎安裝

測試環境:

# OS
CentOS Linux release 7.8.2003 (Core)# Linux
Server Version: 19.03.9
Storage Driver: overlay2
           
1) 基礎指令

基礎執行個體:

# 1.Start your registry
docker run -d -p 5000:5000 --restart=always --name registry registry:2# 2.拉取鏡像并修改
docker pull ubuntu
docker run --name ubuntu-test -d ubuntu 
docker commit -a "Weiyigeek" -m "鏡像描述" ubuntu-test ubuntu-custom:1.0# 3.Tag the image so that it points to your registry
docker image tag ubuntu-custom:1.0 127.0.0.1:5000/ubuntu-custom:1.0# 4.從鏡像倉儲上傳與下載下傳鏡像
docker push localhost:5000/ubuntu-custom:1.0
docker pull localhost:5000/ubuntu-custom:1.0# 5.删除本地/遠端的ubuntu-custom:1.0
docker image remove ubuntu-custom:1.0
docker image remove localhost:5000/ubuntu-custom:1.0# 6.registry倉庫删除與清除卷;
docker container stop registry && docker container rm -v registry
           
2) 基礎配置

Registry 鏡像環境變量:

# Environment variable# 自定義Registry倉庫鏡像存放的實體位址
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/registry# 啟用DELETE操作否則不能執行 -X DELETE
REGISTRY_STORAGE_DELETE_ENABLED=true# 綁定Registry位址與端口
REGISTRY_HTTP_ADDR=0.0.0.0:5000# 證書Certificate設定
REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt
REGISTRY_HTTP_TLS_KEY=/certs/domain.key# 本地基礎認證
REGISTRY_AUTH=htpasswd
REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
           

指定環境變量運作Registry倉庫:

# (1) docker run 時指定環境變量
$ docker run -d \
  --restart=always \
  --name registry \
  -v "$(pwd)"/auth:/auth \
  -v /opt/docker/registry:/var/lib/registry \
  -v "$(pwd)"/certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -e REGISTRY_STORAGE_DELETE_ENABLED=true \
  -p 443:443 \
  registry:2
           
# (2) Yaml 配置
docker run -d -p 5000:5000 --restart=always --name registry \
  -v `pwd`/config.yml:/etc/docker/registry/config.yml \
  registry:2# 例如:開發配置的confi.yaml
version: 0.1
log:
  level: debug
storage:
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: localhost:5000
  host: https://registry.weiyigeek.top:5000
  secret: asecretforlocaldevelopment
  debug:
    addr: localhost:5001
auth:
  htpasswd:
    realm: basic-realm
    path: /path/to/htpasswd
           

參考位址:https://github.com/docker/distribution/blob/master/cmd/registry/config-example.yml

# (3) docker-compose 建構鏡像倉庫# docker-compose.yml 
registry:
  restart: always
  image: registry:2
  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
    REGISTRY_STORAGE_DELETE_ENABLED=true
  volumes:
    - /opt/docker/registry:/var/lib/registry
    - /opt/docker/certs:/certs
    - /opt/docker/auth:/auth
           
3) 生産執行個體

描述:此處以實際生産環境為例進行

Docker Registry

私有倉庫搭建;

3.1) 伺服器拉取Docker Registry鏡像并建立Autho認證的.htpasswd檔案;

$ docker pull registry:2# Digest: sha256:8be26f81ffea54106bae012c6f349df70f4d5e7e2ec01b143c46e2c03b9e551d# Status: Downloaded newer image for registry:2# docker.io/library/registry:2
$ mkdir -vp /opt/registry/ && cd $_
htpasswd -Bbn weiyigeek 123456                  # -n 不更新檔案輸入到終端标準輸出
htpasswd -bB -c auth.htpasswd weiyigeek 123456  # -c 建立存儲認證字元串,-b 強制加密密碼,-B 在指令行中接收密碼
           

Tips:在Push或者Delete鏡像是通過HTTP請求Registry的API完成的,每個請求都需要一個Token才能完成操作,而此Token需要使用auth檔案(

明文使用者/密碼編碼

)來進行鑒權;

3.2) Docker Registry 自簽證書

描述:如果Registry倉庫使用TLS認證時必須帶有證書,當外部通路該Registry倉庫時候提供安全通道,我們可以在認證機構購買簽名證書或者自簽證書也可以;

使用 OpenSSL 來生成 CA (證書授權中心,certificate authority)、 中級 CA(intermediate CA) 和末端證書(end certificate)。包括 OCSP、CRL 和 CA頒發者Issuer資訊、具體頒發和失效日期。

# 方式1:互動式證書生成$openssl req -newkey rsa:4096 -nodes -sha256 -keyout ./certs/domain.key -x509 -days 365 -out ./certs/domain.crt# 方式2:配置檔案方式生成cat >ca.conf <<EOF
[ req ]
default_bits  = 2048
distinguished_name = req_distinguished_name
prompt   = no
encrypt_key  = no
x509_extensions  = v3_ca
[ req_distinguished_name ]
CN = localhost
[ CA_default ]
copy_extensions = copy
[ alternate_names ]
DNS.2=localhost
[ v3_ca ]
[email protected]_names
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = critical,CA:true
keyUsage=keyCertSign,cRLSign,digitalSignature,keyEncipherment,nonRepudiation
EOF
openssl req -days 365 -x509 -config ca.conf -new -keyout certs/domain.key -out certs/domain.crt
           

3.3) 挂載安全認證與自簽證書并啟動registry容器

docker run -d -p 0.0.0.0:8443:5000 --name registry-net \
    -v /var/lib/registry-net:/var/lib/registry \
    -v /opt/registry/certs:/certs \
    -v /opt/registry/auth.htpasswd:/etc/docker/registry/auth.htpasswd \
    -e REGISTRY_AUTH="{htpasswd: {realm: localhost, path: /etc/docker/registry/auth.htpasswd}}" \
    -e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
    -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
    -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
    -e REGISTRY_STORAGE_DELETE_ENABLED=true \
    registry# 檢視容器(隻能本地通路)$docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                     NAMES
86bc983e8183        registry            "/entrypoint.sh /etc…"   About a minute ago   Up About a minute   127.0.0.1:443->5000/tcp   registry
           

3.4) 驗證registry是否可登入,登入後賬号密碼将會base64存儲在 /root/.docker/config.json 之中,為後續使用skopeo的時候做準備;

$docker login localhost -u weiyigeek -p 123456# WARNING! Using --password via the CLI is insecure. Use --password-stdin.# WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Login Succeededcat /root/.docker/config.json{"auths": {"https://index.docker.io/v1/": { "auth": "d2VpxOQ=="},"localhost": {"auth": "d2VpeWlnZWVrOjEyMzQ1Ng==" }},"HttpHeaders": {"User-Agent": "Docker-Client/19.03.9 (linux)"}}# base64 編碼$echo "d2VpeWlnZWVrOjEyMzQ1Ng==" | base64 -d
weiyigeek:123456[
           

3.5) 上傳一個鏡像到registry之中

$docker tag alpine localhost/alpine:latest$docker images
localhost/alpine    latest              a24bb4013296        2 months ago        5.57MB$docker push localhost/alpine:latest# The push refers to repository [localhost/alpine]# 50644c29ef5a: Pushed# latest: digest: sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65 size: 528
           

3.6)registry 檢視上傳的鏡像

# (1) 檢視registry中存儲的鏡像倉庫名稱 (注意--cacert參數如果證書未導入到系統中則必須加上)$curl --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -X GET https://localhost/v2/_catalog{"repositories":["alpine"]}
           

3.7) 利用skopeo轉儲鏡像到registry之中和操作鏡像:

# (1) 首先信任CA憑證,根據不同的發行版選擇相應的路徑和指令行即可,為後面skopeo指令使用做準備。# CentOS
update-ca-trust force-enablecp certs/domain.crt /etc/pki/ca-trust/source/anchors/localhost.crt
update-ca-trust# Ubuntucp certs/domain.crt /usr/local/share/ca-certificates/localhost.crt
$ update-ca-certificates# Debiancp certs/domain.crt /usr/share/ca-certificates/localhost.crtecho localhost.crt >> /etc/ca-certificates.conf
update-ca-certificates# (2) COPY 鏡像到 registry
skopeo inspect docker://docker.io/alpine# 以 localhost/library/alpine:3.10 為例# localhost   就是該 registry 的域名或者 URL # library     就是項目名稱 project# alpine:3.12 就是鏡像名和鏡像的 tag
skopeo copy docker://alpine:3.12 docker://localhost/library/alpine:3.12# Getting image source signatures# Copying blob df20fa9351a1 done# Copying config a24bb40132 done# Writing manifest to image destination# Storing signatures# (3) 删除registry倉庫中的鏡像(删除後并不徹底)
skopeo delete docker://localhost/alpine# (4) 檢視倉庫裡的鏡像curl --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -X GET https://localhost/v2/_catalog{"repositories":["library/alpine"]}
skopeo inspect docker://localhost/library/alpine:3.12{"Name": "localhost/library/alpine","Digest": "sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65","RepoTags": ["3.12"],"Created": "2020-05-29T21:19:46.363518345Z","DockerVersion": "18.09.7","Labels": null,"Architecture": "amd64","Os": "linux","Layers": ["sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c"],"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]}
           

注意事項:

  • 1.證書頒發者可以用中間證書提供給您。在這種情況下,你必須用中間證書串連您的證書形成的證書捆綁。
cat domain.crt intermediate-certificates.pem > certs/domain.crt
           
  • 2.Let’s Encrypt:https://letsencrypt.org/how-it-works/

0x02 Registry 目錄結構

描述: 此時以上傳的library/alpine:3.12鏡像為例檢視registry目錄中檔案變化;

registry 倉庫結構目錄如下:

# registry 持久化位置$ls /var/lib/registry/docker/registry/v2/
blobs  repositories# registry 樹形結構
tree -h /var/lib/registry/docker/registry/v2/
/var/lib/registry/docker/registry/v2/
├── [  20]  blobs
│   └── [  36]  sha256
│       ├── [  78]  a1
│       │   └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65  # 鏡像data manifest (存儲了data config與data layer 的 Digest摘要資訊)
│       │       └── [ 528]  data
│       ├── [  78]  a2
│       │   └── [  18]  a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e  # 鏡像data config
│       │       └── [1.5K]  data #  "mediaType": "application/vnd.docker.container.image.v1+json",
│       └── [  78]  df
│           └── [  18]  df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c  # 鏡像data layer (通常體積最大)
│               └── [2.7M]  data # "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
└── [  21]  repositories
    └── [  20]  library
        └── [  55]  alpine
            ├── [  20]  _layers
            │   └── [ 150]  sha256
            │       ├── [  18]  a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e   # 指向 > blobs/sha256/a24bb401......6b63d83e
            │       │   └── [  71]  link
            │       └── [  18]  df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c   # 指向 > blobs/sha256/df20fa9.......9a752eb4c
            │           └── [  71]  link
            ├── [  35]  _manifests
            │   ├── [  20]  revisions  # 修訂記錄
            │   │   └── [  78]  sha256  
            │   │       └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65  # 修訂指向 manifest  >  blobs/sha256/a15790640......c90fd65
            │   │           └── [  71]  link
            │   └── [  18]  tags
            │       └── [  34]  3.12
            │           ├── [  18]  current
            │           │   └── [  71]  link  # 指向 blobs 中 data manifest digest a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
            │           └── [  20]  index
            │               └── [  78]  sha256
            │                   └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65 
            │                       └── [  71]  link  # 同上
            └── [   6]  _uploads  # 鏡像上傳過程中的臨時目錄
26 directories, 8 files
           

Q: 那 registry 存儲目錄到底長什麼樣? 🤔

答: 結合下面這張圖可以看見,registry 存儲目錄下隻有兩種檔案名的檔案

即data與link檔案

(1) link 檔案: 是普通的文本檔案存放在 repositories 目錄下,其内容是指向 data 檔案的 sha256 digest值, 從字面意義上就很好了解它;

(2) data 檔案: 存放在 blobs 目錄下檔案且分為三個檔案(即鏡像的

layer/config/manifest

等檔案)
# (1) Repositories$ls /var/lib/registry/docker/registry/v2/repositories/alpine/
_layers  _manifests  _uploads# - _layers/sha256 目錄下的檔案夾名是鏡像的Layer和Config的Digest,通該目錄下的link檔案找到對應 blobs 目錄下的 data 檔案,實際上當我們 pull 一個鏡像的 layer 時,是通過 link 檔案找到 layer 在 registry 中實際的存儲位置的。# - _manifests 檔案夾下的 tags 和 revisions 目錄下的 link 檔案則指向該鏡像的 manifest 檔案,儲存在所有曆史鏡像tag的manifest檔案的link。當删除一個鏡像時,隻會删除該鏡像最新的 tag 的 link 檔案。# - revisions 目錄記錄了鏡像修訂版本$ls /var/lib/registry/docker/registry/v2/repositories/alpine(鏡像名稱)/_manifests/revisions/sha256/
  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65# - tags 目錄下的檔案夾名例如3.10就是鏡像的Tag,在它的子目錄下的 current/link 檔案則記錄了目前 tag 指向的 manifest 檔案的位置;# 比如我們的 alpine:latest 鏡像,每次 push 新的 latest 鏡像時current/link 都會更新成指向最新鏡像的 manifest 檔案。
  $ ls /var/lib/registry/docker/registry/v2/repositories/alpine(鏡像名稱)/_manifests/tags/3.12(鏡像标記)/
  current  index  # 目前都指向 a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65 即 鏡像 data manifest# (2) Blobs$ls /var/lib/registry/docker/registry/v2/blobs/sha256/
a1/ a2/ df/ $find /var/lib/registry/docker/registry/v2/ -name "data" -exec ls -sh {} \;# image layer 檔案是 gzip 格式的 tar 包,是鏡像層真實内容的 tar.gzip 格式存儲形式。
2.7M ./blobs/sha256/df/df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c/data# image config 檔案是 json 格式的,它是在建構時候生成的根據DockerFile和主控端的一些資訊; (記錄了"rootfs"."diff_ids")
4.0K ./blobs/sha256/a2/a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e/data# image manifest 檔案json 檔案格式的,存放該鏡像 layer 和 image config 檔案的索引。(鏡像拉取首先需拉取此檔案)
4.0K ./blobs/sha256/a1/a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65/data
           
window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收

此時我們可以再往Registry中COPY一個鏡像友善後面進行對比分析:

$skopeo copy docker://debian:buster-slim docker://localhost/library/debian:buster-slim$curl --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -X GET https://localhost/v2/_catalog{"repositories":["library/alpine","library/debian"]}# 對比分析 registry 中 alpine:3.12 和 debian:buster-slim 兩個基礎鏡像,此時在registry 存儲目錄的結構如下:$tree -h /var/lib/registry/docker/registry/v2/
/var/lib/registry/docker/registry/v2/
├── [  20]  blobs
│   └── [  66]  sha256
│       ├── [  78]  a1
│       │   └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
│       │       └── [ 528]  data
│       ├── [  78]  a2
│       │   └── [  18]  a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
│       │       └── [1.5K]  data
│       ├── [  78]  bf
│       │   └── [  18]  bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb  # debian:buster-slim 的 Data Layer
│       │       └── [ 26M]  data
│       ├── [  78]  c7
│       │   └── [  18]  c7346dd7f20ef06fd3c58446fab0c3edf22e78131d374775f5f947849537b773  # debian:buster-slim 的 Data Config
│       │       └── [1.5K]  data
│       ├── [  78]  df
│       │   └── [  18]  df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
│       │       └── [2.7M]  data
│       └── [  78]  e0
│           └── [  18]  e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02  # debian:buster-slim 的 Data Manifest
│               └── [ 529]  data
└── [  21]  repositories
    └── [  34]  library
        ├── [  55]  alpine  # 此處不做過多說明
        │   ├── [  20]  _layers
        │   │   └── [ 150]  sha256
        │   │       ├── [  18]  a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
        │   │       │   └── [  71]  link
        │   │       └── [  18]  df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
        │   │           └── [  71]  link
        │   ├── [  35]  _manifests
        │   │   ├── [  20]  revisions
        │   │   │   └── [  78]  sha256
        │   │   │       └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
        │   │   │           └── [  71]  link
        │   │   └── [  18]  tags
        │   │       └── [  34]  3.12
        │   │           ├── [  18]  current
        │   │           │   └── [  71]  link
        │   │           └── [  20]  index
        │   │               └── [  78]  sha256
        │   │                   └── [  18]  a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
        │   │                       └── [  71]  link
        │   └── [   6]  _uploads
        └── [  55]  debian
            ├── [  20]  _layers
            │   └── [ 150]  sha256
            │       ├── [  18]  bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb
            │       │   └── [  71]  link  # 摘要指向 blobs 中debian:buster-slim 的 Data Layer 
            │       └── [  18]  c7346dd7f20ef06fd3c58446fab0c3edf22e78131d374775f5f947849537b773
            │           └── [  71]  link  # 摘要指向 blobs 中debian:buster-slim 的 Data Config
            ├── [  35]  _manifests
            │   ├── [  20]  revisions
            │   │   └── [  78]  sha256
            │   │       └── [  18]  e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
            │   │           └── [  71]  link # 摘要指向 blobs 中 debian:buster-slim 的 Data Manifest
            │   └── [  25]  tags
            │       └── [  34]  buster-slim # 以下摘要都是指向 blobs 中 debian:buster-slim 的 Data Manifest
            │           ├── [  18]  current
            │           │   └── [  71]  link
            │           └── [  20]  index
            │               └── [  78]  sha256
            │                   └── [  18]  e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
            │                       └── [  71]  link
            └── [   6]  _uploads
48 directories, 16 files
           

然後我們再采用skopeo删除我們上傳到Registry倉庫中的鏡像,再進行目錄的對比:

$skopeo delete docker://localhost/library/alpine:3.12 --debug# 列出變化的目錄結構部分
repositories
    └── library
        ├── alpine
        │   ├── _layers
        │   │   └── sha256
        │   │       ├── a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
        │   │       │   └── link
        │   │       └── df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
        │   │           └── link
        │   ├── _manifests
        │   │   ├── revisions
        │   │   │   └── sha256
        │   │   │       └── a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
        │   │   └── tags  # tags 下面的目錄與檔案被删除
        │   └── _uploads
           

總結上述:

  • 1.上述可以看見當

    skopeo delete

    删除一個鏡像時,隻是對_manifests下的檔案

    revisions/sha256/a15790640a6690...ka9f2b0d7cc90fd65/link

    tags即其子目錄檔案

    進行删除;實際上兩者删除的是同一個内容,即對記錄了該鏡像 manifests 檔案 digest摘要 的 link 檔案。
  • 2.運作後從--debug參數中得到

    DEBU[0000] DELETE https://localhost/v2/library/alpine/manifests/sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65

    可以得出通過 registry API 來 DELETE 一個鏡像實質上是删除 repositories 中繼資料檔案夾下的 tag 名檔案夾和該 tag 的 revisions 下的 link 檔案。
# 我們也可以采用Registry API 進行操作達到同樣的效率
我們定義摘要字元串比對以下文法:
digest      := algorithm ":" hex
algorithm   := /[A-Fa-f0-9_+.-]+/
hex         := /[A-Fa-f0-9]+/# digest = sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b# (1) 擷取到鏡像 Docker-Content-Digest:$curl -I -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/library/alpine/manifests/3.12# HTTP/1.1 200 OK# Content-Length: 528# Content-Type: application/vnd.docker.distribution.manifest.v2+json# Docker-Content-Digest: sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65# Docker-Distribution-Api-Version: registry/2.0# Etag: "sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65"# X-Content-Type-Options: nosniff# Date: Fri, 21 Aug 2020 02:30:06 GMT# (2) 删除registry倉庫中的指定Docker-Content-Digest的鏡像tags 與 links 檔案$curl -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X DELETE https://localhost/v2/library/alpine/manifests/sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
           

可以看出删除倉庫中的鏡像,隻是把在registry倉庫中鏡像的tags子目錄與revisions/sha256/.../link檔案進行删除,而blobs中的鏡像 data 與 repositories 中鏡像目錄library/alpine/并未被删除,這樣導緻的後果是registry倉庫的伺服器将會占用一部分存儲資源導緻資源的浪費,那如何解決這個問題就是我們下面提到的

Registry GC

 機制;

0x03 Registry API

描述:可以通過registry API操作管理鏡像或者擷取鏡像manifest相關資訊;

官方參考位址: https://docs.docker.com/registry/spec/api/

API 一覽

描述:通過API遇到的錯誤代碼如下表所示: https://docs.docker.com/registry/spec/api/#errors-2

API方法和URI清單涵蓋如下表:

Method Path Entity Description
GET

/v2/

Base Check that the endpoint implements Docker Registry API V2.
GET

/v2/_catalog

/v2/_catalog?n=

Catalog 檢索注冊中心中可用的存儲庫的排序json清單
GET

/v2//tags/list

Tags 擷取存儲庫下由“name”辨別的标記。
GET

/v2/manifests/

Manifest 擷取由“name”和“reference”辨別的清單,其中“reference”可以是标記或摘要。還可以向這個端點發出一個' HEAD '請求,在不接收所有資料的情況下擷取資源資訊。
PUT

/v2/manifests/

Manifest 把由“name”和“reference”辨別的清單放在“reference”可以是标簽或摘要的地方。
DELETE

/v2/manifests/

Manifest 删除由“name”和“reference”辨別的清單。注意,一個清單隻能被“摘要”删除。
GET

/v2/blobs/

Blob 從由“摘要”辨別的系統資料庫中檢索blob。還可以向這個端點發出一個' HEAD '請求,在不接收所有資料的情況下擷取資源資訊。
DELETE

/v2/blobs/

Blob 删除由“name”和“digest”辨別的blob
POST

/v2/blobs/uploads/

Initiate Blob Upload 如果成功,将提供一個上傳位置來完成上傳。可選地,如果“digest”參數存在,請求主體将用于在單個請求中完成上傳。
GET

/v2/blobs/uploads/

Blob Upload 此端點的主要目的是解決可恢複上傳的目前狀态利用uuid。
PATCH

/v2/blobs/uploads/

Blob Upload 上傳指定上傳的資料塊。
PUT

/v2/blobs/uploads/

Blob Upload 完成' uuid '指定的上傳,可選附加主體作為最後塊
DELETE

/v2/blobs/uploads/

Blob Upload 取消未完成的上傳程序,釋放相關資源。如果沒有調用此操作,未完成的上傳最終将逾時。

(Important)結合registry倉庫解釋鏡像PULL與PUSH過程:

  • (1) PULL 鏡像: 鏡像由一個json清單和層疊檔案組成,pull鏡像的過程就是檢索這兩個元件的過程。
- 第一步就是擷取清單,清單由下面幾個字段組成:
registry:5000/v2/redis/manifests/latest(擷取redis:latest清單檔案)# 字段	描述# name	鏡像名稱# tag	鏡像目前版本的tag# fsLayers	層描述清單(包括摘要)# signature	一個JWS簽名,用來驗證清單内容
- 第二步當擷取清單之後,用戶端需要驗證前面(signature),以確定名稱和fsLayers層是有效的。确認後用戶端可以使用digest去下載下傳各個fs層。在V2api中層存儲在blobs中已digest作為鍵值.
1.首先拉取鏡像清單(pulling an Image Manifest)
  $ HEAD /v2/<image/manifests/<reference>#檢查鏡像清單是否存在
  $ GET /v2/<image>/manifests/<reference>#拉取鏡像清單
  提示:reference可是是tag或者是digest  
2.開始拉取每個層(pulling a Layer)
  $ GET /v2/<image>/blobs/<digest>
  提示:digest是鏡像每個fsLayer層的唯一辨別,存在于清單的fsLayers裡面。
           
  • (2) PUSH 鏡像: 推送鏡像和拉取鏡像過程相反,先推各個層到registry倉庫,然後上傳清單.(Pushing a Layer(上傳層)分為2步)
# 2.1) 使用post請求在registry倉庫啟動上傳服務,傳回一個url這個url用來上傳資料和檢查狀态。# 首先Existing Layers(檢查層是否存在),若傳回200 OK 則表示存在,不用上傳
$ HEAD /v2/image/blobs/<digest># 開始上傳服務(Starting An Upload),如果post請求傳回202 accepted,一個url會在location字段傳回.
$ POST /v2/image/blobs/uploads/# 202 Accepted# Location: /v2/\/blobs/uploads/\# Range: bytes=0-# Content-Length: 0# Docker-Upload-UUID:  # 可以用來檢視上傳狀态和實作斷點續傳# 2.2) 開始上傳層(Uploging the Layer)> PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest> 
Content-Length: <size of layer>>
Content-Type: application/octet-stream# 上傳進度(Upload Progress)
$ GET /v2/<image>/blobs/uploads/<uuid># 204 No Content# Location: /v2//blobs/uploads/# Range: bytes=0-# Docker-Upload-UUID: # 重點-整塊上傳(Monolithic Upload)> PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest> > Content-Length: <size of layer>> Content-Type: application/octet-stream<Layer Binary Data># 重點-分塊上傳(Chunked Upload)> PATCH /v2/\<name>/blobs/uploads/\<uuid>> Content-Length: \<size of chunk>> Content-Range: \<start of range>-\<end of range>> Content-Type: application/octet-stream
\<Layer Chunk Binary Data># 如果伺服器不接受這個塊,則傳回:#   416 Requested Range Not Satisfiable#   Location: /v2//blobs/uploads/#   Range: 0-#   Content-Length: 0#   Docker-Upload-UUID: # 成功則傳回:#   202 Accepted#   Location: /v2//blobs/uploads/#   Range: bytes=0-#   Content-Length: 0#   Docker-Upload-UUID: # 重點-交叉上傳(Cross Repository Blob Mount)可以把用戶端有通路權限的已有存儲庫中的層挂載到目前存儲庫中
POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<repository name>
Content-Length: 0# 成功傳回:#   201 Created#   Location: /v2//blobs/#   Content-Length: 0#   Docker-Content-Digest: # 失敗傳回:#   202 Accepted#   Location: /v2//blobs/uploads/#   Range: bytes=0-#   Content-Length: 0#   Docker-Upload-UUID: # 3.3) 上傳完成(Completed Upload),但是注意分塊上傳在最後一塊上傳完畢後,需要送出一個上傳完成的請求> PUT /v2/<name>/blob/uploads/<uuid>?digest=<digest>> Content-Length: <size of chunk>> Content-Range: <start of range>-<end of range>> Content-Type: application/octet-stream   <Last Layer Chunk Binary Data># 成功傳回:# 201 Created# Location: /v2//blobs/# Content-Length: 0# Docker-Content-Digest: 
           
實際示例

Tips: 後續不再加

--cacert /opt/registry/certs/domain.crt

參數,預設大家都已經把證書導入帶系統本地;

  • 0.Registry V2協定及其認證請求驗證
# Registry 倉庫協定$curl -I -u 'weiyigeek:123456' -X GET https://localhost/v2/
HTTP/1.1 200 OK# 采用一個RFC7235相容的授權頭進行認證$curl -I -H 'Authorization: Basic d2VpeWlnZWVrOjEyMzQ1Ng==' -X GET https://localhost/v2/
HTTP/1.1 200 OK
           
  • 1.檢視Registry倉庫中有那些鏡像(不精确-當通過delete删除鏡像時候此處并未删除需要手動到repositories檔案夾中删除)
# Registry 倉庫中所有鏡像curl --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -X GET https://localhost/v2/_catalog{"repositories":["library/alpine","library/debian"]}# 傳回倉庫中指定條目的鏡像(通過-v 參數可看出last的不同)curl --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -X GET "https://localhost/v2/_catalog?n=1&last=a"
           
  • 2.擷取某個鏡像的标簽清單 (注意加或者未加Project的差別)
curl -u 'weiyigeek:123456' -X GET https://localhost/v2/alpine/tags/list# {"name":"alpine","tags":["latest"]}curl -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/tags/list  # 'library/alpine'# {"name":"library/alpine","tags":["3.12"]}# 列出鏡像部分tags(Pagination)curl -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/tags/list?n=<integer>
           
  • 3.拉取Registry 倉庫鏡像中Manifests(清單)檔案
# 判斷指定鏡像與tags的Manifests(清單)是否存在curl -I -u 'weiyigeek:123456' -X HEAD https://localhost/v2/library/alpine/manifests/3.12# HTTP/1.1 200 OK# Content-Length: 2783# Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws# 倉庫中`Manifests`清單
$ curl -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/manifests/3.12# {#    "schemaVersion": 1,#    "name": "library/alpine",#    "tag": "3.12",#    "architecture": "amd64",#    "fsLayers": [{#          "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"},{#          "blobSum": "sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c"#       }#    ],#    "history": [#       {#          "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:64771e4514cb653a0fe68e1ceed5bd16640ebf3bd859dc3333efe87dc4709a5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container\":\"ce1874fa1fc1eb128516899352f185645f492c443b5a80d9a3fae8b09d1b6b16\",\"container_config\":{\"Hostname\":\"ce1874fa1fc1\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"/bin/sh\\\"]\"],\"ArgsEscaped\":true,\"Image\":\"sha256:64771e4514cb653a0fe68e1ceed5bd16640ebf3bd859dc3333efe87dc4709a5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2020-05-29T21:19:46.363518345Z\",\"docker_version\":\"18.09.7\",\"id\":\"4a28aef4f8a9e13b1df98eaf8e651db2857161cea27134dad697ad0c7a7de12d\",\"os\":\"linux\",\"parent\":\"a5213fa3ad8fa7a42f88213945845ef49dcf11328d51576b8f076142ce75bdf8\",\"throwaway\":true}"#       },#       {#          "v1Compatibility": "{\"id\":\"a5213fa3ad8fa7a42f88213945845ef49dcf11328d51576b8f076142ce75bdf8\",\"created\":\"2020-05-29T21:19:46.192045972Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:c92c248239f8c7b9b3c067650954815f391b7bcb09023f984972c082ace2a8d0 in / \"]}}"#       }#    ],#    "signatures": [#       {#          "header": {#             "jwk": {#                "crv": "P-256",#                "kid": "P6TV:UOU3:V564:FNEL:DQG2:WQX5:6Z5P:NQF6:XZOR:JTMI:Q2QI:AQZ3",#                "kty": "EC",#                "x": "n70C5idlCOFB4ubdg5K6MCvRBIH6d5YzhTRumV1i6D8",#                "y": "OmZn6AyifVg3kZ67ICPViHTHBXvMui8fPwqXzbTnWw0"#             },#             "alg": "ES256"#          },#          "signature": "S4Tvfqx0nA7hULgyKdKKdoYpgMsTqxlbQ6JDeQv1HXZie1zMCsafNZfLI59kivzHb7IV8hwEnvxehL0cKuoZ4w",#          "protected": "eyJmb3JtYXRMZW5ndGgiOjIxMzYsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMC0wOC0yMlQwNzoyNDozM1oifQ"#       }#    ]# }
           
  • 4.擷取倉庫鏡像的manifests内容 (go-hello:scratch)
curl -v -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/go-hello/manifests/scratch # {#    "schemaVersion": 2,#    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",#    "config": {#       "mediaType": "application/vnd.docker.container.image.v1+json",#       "size": 1472,#       "digest": "sha256:cb05b87d001253772ae9a212200de5eb8304ab9691c61589332a2f57e7059209"#    },#    "layers": [#       {#          "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",#          "size": 1106793,#          "digest": "sha256:5395625ce01dee311e2f7c879b0b148ac7525de7aad5080a518d7f7e5a99d368"#       }#    ]# }
           
  • 5.擷取(鏡像:版本)辨別的data manifests的 digest
curl -I --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/go-hello/manifests/scratch# HTTP/1.1 200 OK# Content-Length: 528# Content-Type: application/vnd.docker.distribution.manifest.v2+json# 注意Docker-Content-Digest中的内容: 在registry2.3或更高版本删除清單時,必須在HEAD或GET擷取清單以擷取要删除的正确digest攜帶以下頭;# Docker-Content-Digest: sha256:8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e# Docker-Distribution-Api-Version: registry/2.0# Etag: "sha256:8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e"# X-Content-Type-Options: nosniff# Date: Fri, 21 Aug 2020 02:30:06 GMT# 【簡約版本:直接提取Docker-Content-Digest頭内容】curl -Is -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/library/alpine/manifests/3.12 | grep "Docker-Content-Digest:" | cut -f 2 -d " "
sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65# 【如不指定Accept則預設為 application/vnd.docker.distribution.manifest.v1+prettyjws 】curl -I -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/manifests/3.12                                     HTTP/1.1 200 OK# Content-Length: 2783# Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws# Docker-Content-Digest: sha256:cae82a43ba96214acd380f3d4ed043445f56f80f0fc99f3f927d5e6eaee40791# Docker-Distribution-Api-Version: registry/2.0# Etag: "sha256:cae82a43ba96214acd380f3d4ed043445f56f80f0fc99f3f927d5e6eaee40791"# X-Content-Type-Options: nosniff# Date: Sat, 22 Aug 2020 03:01:16 GMT
           
  • 6.删除倉庫中的鏡像即删除(repositories下面的 _manifests 中的Tags 與 revisions 下的link)
# 加入 -v 參數 檢視請求傳回流程  $curl -v --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X DELETE https://localhost/v2/go-hello/manifests/sha256:8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e#  202 Accepted#  Content-Length: None# 失敗 傳回404錯誤
           

注意:預設情況下,registry不允許删除鏡像操作,需要在啟動registry時指定環境變量

REGISTRY_STORAGE_DELETE_ENABLED=true

,或者修改其配置檔案即可。reference必須是digest,否則删除将失敗。在registry2.3或更高版本删除清單時,必須在HEAD或GET擷取清單以擷取要删除的正确digest攜帶以下頭:

Accept: application/vnd.docker.distribution.manifest.v2+json

  • 7.拉取鏡像,由于層被存儲在系統資料庫中的blobs中是以是需要通過一個标準的HTTP請求來進行拉取一個層的資訊
# (1) 先檢視鏡像 data 相關的 Digest 碼curl -s -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/library/alpine/manifests/3.12 | jq '"Data Config - " + .config.digest','"Data Layer - " + .layers[0].digest'# "Data Config - sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e"# "Data Layer - sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c"# (2) 擷取拉取鏡像的data config 與 data layer 檔案curl -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/blobs/sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e | jq curl -u 'weiyigeek:123456' -X GET https://localhost/v2/library/alpine/blobs/sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c -o /tmp/alpine-3.12.tar.gz[[email protected] tmp]$ ls -lh alpine-3.12.tar.gz
-rw-r--r--.  1 root root 2.7M 8月  22 16:27 alpine-3.12.tar.gz[[email protected] tmp]$ tar -zxvf alpine-3.12.tar.gz  # 實際上該壓縮檔案中存放的是rootfs檔案系統# bin/# bin/arch# bin/ash# bin/base64# bin/bbconfig
           
  • 8.鏡像通過Registry API上傳到倉庫中
# 所有層上傳使用兩個步驟來管理上傳過程。
* 第一步開始在系統資料庫中的服務上傳,傳回一個URL來進行第二步。
* 第二步使用上載URL傳遞的實際資料。上傳都開始傳回,可用于将資料推和檢查上傳狀态URL的POST請求。# Location頭将用于每個請求後進行通信的上載位置。雖然它不會在本技術規格改變,客戶應使用API傳回的最新值。# (1) 開始上載一個POST請求 # POST /v2//blobs/uploads/curl -I -u 'weiyigeek:123456' -X POST https://localhost/v2/test-images/blobs/uploads/# 如果POST請求是成功的它将傳回 202響應 将與Location頭上傳的URL傳回:# HTTP/1.1 202 Accepted# Content-Length: 0# Docker-Distribution-Api-Version: registry/2.0# Docker-Upload-Uuid: 9efa5ca7-d009-4532-88de-695c1f945e59  # 必須額# Location: https://localhost/v2/test-images/blobs/uploads/9efa5ca7-d009-4532-88de-695c1f945e59?_state=XqlcahxfSzts1a43SgEs_MQ9-GAczgadg-Ra3vayoh57Ik5hbWUiOiJ0ZXN0LWltYWdlcyIsIlVVSUQiOiI5ZWZhNWNhNy1kMDA5LTQ1MzItODhkZS02OTVjMWY5NDVlNTkiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjAtMDgtMjJUMDg6NDA6NDguMDkwNTk0NzQ4WiJ9  # 指定了位置标頭# Range: 0-0# X-Content-Type-Options: nosniff# Date: Sat, 22 Aug 2020 08:40:48 GMT# (2) 通過HEAD請求到BLOB存儲API進行檢查鏡像相關層是否存在(可用傳回200 OK)# HEAD /v2//blobs/curl -I -u 'weiyigeek:123456' -X HEAD https://localhost/v2/test-images/blobs/sha256:a14....jk5# (3) 上傳進度檢視此時需要第一步中的Docker-Upload-Uuid之進行請求# GET /v2//blobs/uploads/curl -I -u 'weiyigeek:123456' -X GET https://localhost/v2/test-images/blobs/uploads/9efa5ca7-d009-4532-88de-695c1f945e59# (4) Monolithic Upload 簡單的單塊上傳,并可以通過想避免分塊的複雜性的# PUT /v2//blobs/uploads/?digest=# Content-Length: # Content-Type: application/octet-stream# # (5) Chunked Upload 進行組塊的上載,該客戶機可以指定一個範圍報頭和僅包括層檔案的一部分:# PATCH /v2//blobs/uploads/# Content-Length: # Content-Range: -# Content-Type: application/octet-stream# # (6) 跨存儲庫Blob挂載,可以從客戶機具有讀通路權的另一個存儲庫挂載blob,進而不需要将已知的blob上傳到注冊中心,要發出一個blob挂載而不是一個upload, POST請求應該以以下格式發出(成功将傳回202 Accepted):
POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<repository name>
Content-Length: 0# (7) Completed Upload 當鏡像上傳完畢必須進行以下請求否則倉庫不認為鏡像個層全部上傳,當接收到最後一個塊和層已被驗證時候将傳回201 Create 并且傳回該鏡像的Docker-Content-Digest值;# PUT /v2//blobs/uploads/?digest=# Content-Length: # Content-Range: -# Content-Type: application/octet-stream# # (8) 取消上傳鏡像到倉庫,可通過發出DELETE請求到registry之中;# DELETE /v2//blobs/uploads/# (9) 删除層(Deleting a Layer)# DELETE /v2//blobs/# 成功傳回:# 202 Accepted# Content-Length: None# (10) 上傳鏡像清單(Pushing an Image Manifest),我們上傳完鏡像層之後,就開始上傳鏡像清單# PUT /v2//manifests/# Content-Type: # {# "name": ,# "tag": ,# "fsLayers": [#   {#     "blobSum": #   },#   ...# ]# ],# "history": ,# "signature": ,# ...# }# 如果清單中有層("blobSum":)是未知的,則傳回# {  "errors:" [{          "code": "BLOB_UNKNOWN",          "message": "blob unknown to registry",          "detail": {              "digest": #         }#     },#     ...#   ]# }# ```
           

删除鏡像Manifests與鏡像層資訊:

curl -Is -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/library/alpine/manifests/3.12 | grep "Docker-Content-Digest:" | cut -f 2 -d " "
sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65curl -s -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X GET https://localhost/v2/library/alpine/manifests/3.12 | grep "digest""digest": "sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e""digest": "sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c"# 删除 鏡像  _Manifests Tagscurl -v --cacert /opt/registry/certs/domain.crt -u 'weiyigeek:123456' -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X DELETE https://localhost/v2/library/alpine/manifests/sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65# 删除 鏡像 _Layercurl -v -u 'weiyigeek:123456' -X DELETE https://localhost/v2/library/alpine/blobs/sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83ecurl -v -u 'weiyigeek:123456' -X DELETE https://localhost/v2/library/alpine/blobs/sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4ccurl -v -u 'weiyigeek:123456' -X DELETE "https://localhost/v2/library/alpine/blobs/sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"curl -v -u 'weiyigeek:123456' -X DELETE https://localhost/v2/library/alpine/blobs/sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65# 清空後效果
repositories|
   library
    │   ├── alpine
    │   │   ├── _layers
    │   │   │   └── sha256
    │   │   │       ├── a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
    │   │   │       ├── a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
    │   │   │       └── df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
    │   │   ├── _manifests
    │   │   │   ├── revisions
    │   │   │   │   └── sha256
    │   │   │   │       └── a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
    │   │   │   └── tags
    │   │   └── _uploads
           

簡單粗暴清空 Registry 倉庫:

$tree /var/lib/registry/docker/registry/v2/blobs/sha256/
/var/lib/registry/docker/registry/v2/blobs/sha256/
├── 0e
├── 32
├── 3f
├── 53
├── 59
├── 8d
├── a1
├── a2
├── b7
├── b9
├── cb
├── df
└── e7rm -rf /var/lib/registry/docker/registry/v2/blobs/sha256/*rm -rf /var/lib/registry/docker/registry/v2/repositories/*
           

0x04 Registry GC

描述:在上一章節中我們闡述了為什麼要引入Registry GC機制,再說其目的用處前我們先對其進行一個簡單的介紹;

Q: 什麼是Registry GC ?

答 :GC英文全稱

Garbage collection

就是垃圾回收的意思,從 docker 官方文檔關于GC偷來的 example 來解釋一下吧。

官網文檔:https://docs.docker.com/registry/garbage-collection/

#假如鏡像 A 和鏡像 B 它們分别引用了layer a,b和 a,c。
-  A  -|     |
a  -  b  -  c |           |
----  B ----# 通過 registry API 删除鏡像 B 之後,layer c 并沒有删掉,隻是删掉了對它的引用是以 c 是多餘的(就是我們上訴提到那種清情況)。
-  A  -|     |
a  -  b  -  c #此時通過GC機制之後 Layer C 被删除掉了即沒有被引用的層将被徹底删除;
-  A  -|     |
a  -  b 
           

此處可以借鑒registry GC 的源碼檔案 garbagecollect.go 可以看到 GC 的主要分兩個階段:

  • (1) marking 階段:根據上文我們提到的 link 檔案,通過掃描所有鏡像 tags 目錄下的 link 檔案就可以得到這些鏡像的 manifest,在 manifest 中儲存在該鏡像所有的 layer 和 config 檔案的 digest 值,把這些值标記為不能清除。
markSet := make(map[digest.Digest]struct{})
manifestArr := make([]ManifestDel, 0)
err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error {emit(repoName)var err error
	named, err := reference.WithName(repoName)if err != nil {return fmt.Errorf("failed to parse repo name %s: %v", repoName, err)}
	repository, err := registry.Repository(ctx, named)if err != nil {return fmt.Errorf("failed to construct repository: %v", err)}
	manifestService, err := repository.Manifests(ctx)if err != nil {return fmt.Errorf("failed to construct manifest service: %v", err)}
	manifestEnumerator, ok := manifestService.(distribution.ManifestEnumerator)if !ok {return fmt.Errorf("unable to convert ManifestService into ManifestEnumerator")}
           
  • (2) sweep 階段:删除操作當marking完成之後沒有标記blobs(layer 和 config)就會被清理掉;
// sweep
vacuum := NewVacuum(ctx, storageDriver)if !opts.DryRun {for _, obj := range manifestArr {
		err = vacuum.RemoveManifest(obj.Name, obj.Digest, obj.Tags)if err != nil {return fmt.Errorf("failed to delete manifest %s: %v", obj.Digest, err)}}}
blobService := registry.Blobs()
deleteSet := make(map[digest.Digest]struct{})
err = blobService.Enumerate(ctx, func(dgst digest.Digest) error {// check if digest is in markSet. If not, delete it!if _, ok := markSet[dgst]; !ok {
		deleteSet[dgst] = struct{}{}}return nil})
           
window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收

Q:那 GC 都幹了啥?

答: 我們可以利用registry容器中的

registry garbage-collect

指令進行GC回收操作;
$ docker exec -it registry sh -c "/bin/registry garbage-collect -m --delete-untagged=true /etc/docker/registry/config.yml"
/ # cat /etc/docker/registry/config.yml 
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry  # 關鍵點
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3# marking 階段
library/alpine # Registry中的鏡像未标記将會被删除 
library/debian 
library/debian: marking manifest sha256:e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
library/debian: marking blob sha256:c7346dd7f20ef06fd3c58446fab0c3edf22e78131d374775f5f947849537b773
library/debian: marking blob sha256:bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb# sweep 階段
3 blobs marked, 3 blobs and 0 manifests eligible for deletion
blob eligible for deletion: sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/a1/a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65  go.version=go1.11.2 instance.id=fe470a94-4f51-4764-93e5-0f1d5d6172bf service=registry
blob eligible for deletion: sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/a2/a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e  go.version=go1.11.2 instance.id=fe470a94-4f51-4764-93e5-0f1d5d6172bf service=registry
blob eligible for deletion: sha256:df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/df/df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c  go.version=go1.11.2 instance.id=fe470a94-4f51-4764-93e5-0f1d5d6172bf service=registry
           

此時經過GC之後的Registry 存儲目錄長什麼樣子?

$ tree /var/lib/registry/docker/registry/v2
/var/lib/registry/docker/registry/v2
├── blobs
│   └── sha256
│       ├── a1
│       ├── a2
│       ├── bf
│       │   └── bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb
│       │       └── data
│       ├── c7
│       │   └── c7346dd7f20ef06fd3c58446fab0c3edf22e78131d374775f5f947849537b773
│       │       └── data
│       ├── df
│       └── e0
│           └── e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
│               └── data
└── repositories
    └── library
        ├── alpine  # 可以看見鏡像名稱并未删除
        │   ├── _layers
        │   │   └── sha256
        │   │       ├── a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e
        │   │       │   └── link
        │   │       └── df20fa9351a15782c64e6dddb2d4a6f50bf6d3688060a34c4014b0d9a752eb4c
        │   │           └── link
        │   ├── _manifests
        │   │   ├── revisions
        │   │   │   └── sha256
        │   │   │       └── a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65
        │   │   └── tags
        │   └── _uploads
        └── debian
            ├── _layers
            │   └── sha256
            │       ├── bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb
            │       │   └── link
            │       └── c7346dd7f20ef06fd3c58446fab0c3edf22e78131d374775f5f947849537b773
            │           └── link
            ├── _manifests
            │   ├── revisions
            │   │   └── sha256
            │   │       └── e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
            │   │           └── link
            │   └── tags
            │       └── buster-slim
            │           ├── current
            │           │   └── link
            │           └── index
            │               └── sha256
            │                   └── e0a33348ac8cace6b4294885e6e0bb57ecdfe4b6e415f1a7f4c5da5fe3116e02
            │                       └── link
            └── _uploads
40 directories, 10 files
           
Shell 腳本

示例1:檢視Docker官方鏡像倉庫中鏡像的所有标簽

  • 方式1:
#!/bin/sh# set -xe # 其實實作方法就是通過鏡像倉庫的 restful API,來查詢,然後把傳回的 json 結果簡單處理一下,然後列印出來。
image_name=$1
repo_url=https://registry.hub.docker.com/v1/repositoriescurl -s ${repo_url}/${image_name}/tags | jq | grep name | awk '{print $2}' | sed -e 's/"//g'
           
  • 方式2:一條指令搞定
skopeo inspect docker://docker.io/alpine | jq ".RepoTags"
           

示例2.registry資訊檢視腳本與RegistryGC回收腳本

#!/bin/bash# Description:檢視Registry倉庫中的鏡像資訊并從倉庫中删除指定鏡像,然後進行垃圾回收# Author:WeiyiGeek# createTime:2020年8月23日 16:55:57set -x# [+ Defined]
PARM=$1
IMAGE_NAME=${2}
ACTION=${PARM:="NONE"}
REGISTRY_URL="https://localhost/v2"
REGISTRY_NAME="registry"
REGISTRY_HOME="/var/lib/registry/docker/registry/v2"
MANIFESTS_DIGEST=""
AUTH="Authorization: Basic d2VpeWlnZWVrOjEyMzQ1Ng=="function Usage(){echo -e "\e[32mUsage: $0 {view} \e[0m"echo -e "\e[32m       $0 {tags}  \e[0m"echo -e "\e[32m       $0 {gc}  \e[0m"echo -e "\e[32m       $0 {delete}  \e[0m"echo -e "\e[32m #檢視倉庫中的鏡像資訊并從倉庫中删除指定鏡像,然後進行垃圾回收 \e[0m"exit;}# [+ 顯示倉庫中的鏡像]function ViewRegistry(){curl -s -H "${AUTH}" "${REGISTRY_URL}/_catalog" | jq ".repositories"}# [+ 顯示倉庫中鏡像标記]function ViewTags(){
  local FLAG=0
  local IMAGE_NAME=$1curl -s -H "${AUTH}" "${REGISTRY_URL}/_catalog" | jq ".repositories" > registry.reposed -i "s#\[##g;s#]##g;s# ##g;s#\"##g;s#,##g;/^\s*$/d" registry.repofor i in $(cat registry.repo)doif [[ "$i" == "${IMAGE_NAME}" ]];then
      FLAG=1breakfidoneif [[ $FLAG -eq 1 ]];thencurl -s -H "${AUTH}" "${REGISTRY_URL}/${IMAGE_NAME}/tags/list" | jq ".tags"elseecho -e "\e[31m[ERROR]: Registry 不存在 ${IMAGE_NAME} 該鏡像\e[0m"exitfi}# [+ 倉庫廢棄鏡像回收] function GcRegistry(){
  docker exec -it $1 sh -c "/bin/registry garbage-collect -m --delete-untagged=true /etc/docker/registry/config.yml"if [[ $? -ne 0 ]];thenecho -e "\e[31m[ERROR]:GC Failed! \e[0m"exitfi# 删除 blobs/sha256中的空目錄for i in $(find ${REGISTRY_HOME}/blobs/sha256/ | grep -v "data");do if [[ $(ls -A $i|wc -c) -eq 0 ]];then echo -e "[info]delete empty directory : ${i}"rm -rf ${i}fidoneecho -e "[+ Registry restart ....]"
  docker restart $1}# [+ 删除倉庫中的鏡像]function Del() {
  local IMAGE_NAME=$1
  local TAGS=$2if [[ "$TAGS" != "" ]];then# 驗證删除的鏡像是否存在curl -s -H "${AUTH}" -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' "${REGISTRY_URL}/${IMAGE_NAME}/manifests/${TAGS}" > images.mainfests
    err_flag=$(grep -c '"errors"' images.mainfests)if [[ $err_flag -ne 0 ]];thenecho -e "\e[31m[ERROR]:$(cat images.mainfests) \e[0m"exitfi# 擷取要删除鏡像的digest摘要
    MANIFESTS_DIGEST=$(curl -s -H "${AUTH}" -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' "${REGISTRY_URL}/${IMAGE_NAME}/manifests/${TAGS}" | grep "Docker-Content-Digest:" | cut -f 2 -d " ")grep "digest" images.mainfests | sed 's# ##g;s#"##g;s#digest:##g' > images.digestecho ${MANIFESTS_DIGEST} >> images.digest# 删除 鏡像 _Manifests目錄中的Tags相關目錄curl -v -H "${AUTH}" -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -X DELETE "${REGISTRY_URL}/${IMAGE_NAME}/manifests/${MANIFESTS_DIGEST}"# 删除 鏡像 _Layer 目錄下的linkfor digest in $(cat images.digest);docurl -v -H "${AUTH}" -X DELETE "${REGISTRY_URL}/${IMAGE_NAME}/blobs/${digest}"donefi# GC 回收(注意參數為容器鏡像名稱)
  GcRegistry ${REGISTRY_NAME}# 判斷 鏡像 是否存在其它 tags 不存在時候直接删除其目錄$flag_tags=$(curl -s -H "${AUTH}" "${REGISTRY_URL}/${IMAGE_NAME}/tags/list" | jq ".tags") if [[ -z $flag_tags ]];thenrm -rf "${REGISTRY_HOME}/repositories/${IMAGE_NAME}"fi# 删除 _layers 目錄下的digest檔案空的目錄for i in $(find ${REGISTRY_HOME}/repositories/${IMAGE_NAME}/_layers/sha256/ | grep -v "link");do if [[ $(ls -A $i|wc -c) -eq 0 ]];then echo -e "[info]delete empty directory : ${i}"rm -rf ${i}fidone# 删除 manifests 目錄下的digest檔案空的目錄for i in $(find ${REGISTRY_HOME}/repositories/${IMAGE_NAME}/_manifests/revisions/sha256/ | grep -v "link");do if [[ $(ls -A $i|wc -c) -eq 0 ]];then echo -e "[info]delete empty directory : ${i}"rm -rf ${i}fidone}# [Main]if [[ "$ACTION" = "NONE" ]];then
  Usageelif [[ "$ACTION" = "view" ]];then
  ViewRegistryelif  [[ "$ACTION" = "tags" ]];then
  ViewTags $2elif  [[ "$ACTION" = "delete" ]];then
  Del $2 $3elif  [[ "$ACTION" = "gc" ]];then
  GcRegistry $2else
  Usagefi
EOF
           

章節總結:

  • (1) 在GC之後registry存儲目錄我們可以看到,原本blobs目錄下有6個data檔案現在已經變成3個,而alpine:3.12鏡像相關的

    Layer、Config、Manifest

    檔案已經被GC清理掉了; 但是在 repositories 目錄下,該鏡像的 _layers 下的 link 檔案依舊存在🤔。
  • (2) 總結以上,用下面這三張圖檔就能直覺地了解這些過程啦
    • 2.1 delete 鏡像之前的 registry 存儲目錄結構
      window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收
    • 2.2 delete 鏡像之後的 registry 存儲目錄結構
      window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收
    • 2.3 GC 之後的 registry 存儲目錄結構
      window docker 檢視鏡像倉庫_10.Docker容器鏡像Registry倉庫安全建構與GC回收
  • (3) GC 之後一定要重新開機,因為 registry 容器緩存了鏡像 layer 的資訊當删除掉一個鏡像 A 後邊 GC 掉該鏡像的 layer 之後,如果不重新開機 registry 容器,

    當重新 PUSH 鏡像 A 的時候就會提示鏡像 layer 已經存在,不會重新上傳 layer 但實際上已經被 GC 掉了,最終會導緻鏡像 A 不完整無法 pull 到該鏡像

  • (4) GC 不是事務性操作,是以在進行 GC 的時候最好暫停 PUSH 鏡像,以免把正在上傳的鏡像 layer 給 GC 掉。

0x05 配置檔案解析

config.yaml 檔案一覽
# 配置版本version: 0.1# 日志配置日志系統的行為log:# 通路日志系統的ACCESSLOG配置行為accesslog:disabled: true # 日志輸出的格式預設info.Permitted values are error, warn, info, debuglevel: debug # 日志輸出的格式預設 text , json, and logstashformatter: text# 字段名稱的地圖fields:service: registryenvironment: staging# 配置日志記錄挂鈎的行為hooks:- type: maildisabled: truelevels:- panicoptions:smtp:addr: mail.example.com:25username: mailuserpassword: passwordinsecure: truefrom: [email protected]:- [email protected]# 日志等級(error, warn, info, debug):deprecated: use "log"loglevel: debug# 存儲後端配置必須storage:# 使用本地磁盤存儲系統資料庫檔案filesystem:rootdirectory: /var/lib/registrymaxthreads: 100azure:accountname: accountnameaccountkey: base64encodedaccountkeycontainer: containername# Google Cloud Storagegcs:bucket: bucketnamekeyfile: /path/to/keyfilecredentials:type: service_accountproject_id: project_id_stringprivate_key_id: private_key_id_stringprivate_key: private_key_stringclient_email: [email protected]_id: client_id_stringauth_uri: http://example.com/auth_uritoken_uri: http://example.com/token_uriauth_provider_x509_cert_url: http://example.com/provider_cert_urlclient_x509_cert_url: http://example.com/client_cert_urlrootdirectory: /gcs/object/name/prefixchunksize: 5242880# Amazon Simple Storage Service (S3)s3:accesskey: awsaccesskeysecretkey: awssecretkeyregion: us-west-1regionendpoint: http://myobjects.localbucket: bucketnameencrypt: truekeyid: mykeyidsecure: truev4auth: truechunksize: 5242880multipartcopychunksize: 33554432multipartcopymaxconcurrency: 100multipartcopythresholdsize: 33554432rootdirectory: /s3/object/name/prefix# Openstack Swift object storage. swift:username: usernamepassword: passwordauthurl: https://storage.myprovider.com/auth/v1.0 or https://storage.myprovider.com/v2.0 or https://storage.myprovider.com/v3/authtenant: tenantnametenantid: tenantiddomain: domain name for Openstack Identity v3 APIdomainid: domain id for Openstack Identity v3 APIinsecureskipverify: trueregion: frcontainer: containernamerootdirectory: /swift/object/name/prefix# Aliyun OSS for object storageoss:accesskeyid: accesskeyidaccesskeysecret: accesskeysecretregion: OSS region nameendpoint: optional endpointsinternal: optional internal endpointbucket: OSS bucketencrypt: optional data encryption settingsecure: optional ssl settingchunksize: optional size valyerootdirectory: optional root directoryinmemory:  # This driver takes no parameters# 使用delete結構允許通過摘要删除映像blob和清單delete:enabled: falseredirect:disable: false# 使用高速緩存結構,以便能夠在存儲後端通路的資料緩存。目前唯一可用的高速緩存提供對層的中繼資料,它使用blobdescriptor字段如果配置的快速通路。cache:# 如果設定為Redis的,一個Redis的緩存池中繼資料層。如果設定為inmemory,一個inmemory地圖緩存層的中繼資料。blobdescriptor: redis# 維護維修maintenance:# 上傳清除uploadpurging:enabled: trueage: 168hinterval: 24hdryrun: false# 如果在維護隻讀部分,使設定為true,用戶端将不會被允許寫入系統資料庫。readonly:enabled: false# 認證相關(隻能配置一個身份驗證提供者。)auth:silly:# 使用範圍realm: silly-realmservice: silly-service# 令牌的認證您可以從registry中分離認證系統token:autoredirect: truerealm: token-realmservice: token-serviceissuer: registry-token-issuerrootcertbundle: /root/certs/bundle# 支援htpasswd的認證允許您使用的是Apache的htpasswd檔案來配置基本身份驗證htpasswd:realm: basic-realm# 唯一支援的密碼格式是bcrypt。path: /path/to/htpasswdmiddleware:registry:- name: ARegistryMiddlewareoptions:foo: barrepository:- name: ARepositoryMiddlewareoptions:foo: barstorage:- name: cloudfrontoptions:baseurl: https://my.cloudfronted.domain.com/privatekey: /path/to/pemkeypairid: cloudfrontkeypairidduration: 3000sipfilteredby: awsregionawsregion: us-east-1, use-east-2updatefrenquency: 12hiprangesurl: https://ip-ranges.amazonaws.com/ip-ranges.jsonstorage:- name: redirectoptions:baseurl: https://example.com/# 報表reporting:bugsnag:apikey: bugsnagapikeyreleasestage: bugsnagreleasestageendpoint: bugsnagendpointnewrelic:licensekey: newreliclicensekeyname: newrelicnameverbose: true# HTTP伺服器主機上的系統資料庫中的配置http:addr: localhost:5000prefix: /my/nested/registry/host: https://myregistryaddress.org:5000secret: asecretforlocaldevelopmentrelativeurls: falsedraintimeout: 60stls:certificate: /path/to/x509/publickey: /path/to/x509/privateclientcas:- /path/to/ca.pem- /path/to/another/ca.pemletsencrypt:cachefile: /path/to/cache-fileemail: [email protected]: [myregistryaddress.org]debug:addr: localhost:5001# 監控prometheus:enabled: truepath: /metrics# 它來指定報頭的HTTP伺服器應該在響應;headers:X-Content-Type-Options: [nosniff]http2:disabled: false# 通知公告notifications:events:includereferences: true# 可以接受的事件通知的清單endpoints:- name: alistenerdisabled: falseurl: https://my.listener.com/eventheaders: >timeout: 1sthreshold: 10backoff: 1signoredmediatypes:- application/octet-streamignore:mediatypes:- application/octet-streamactions:- pull# redis 相關配置redis:addr: localhost:6379password: asecretdb: 0dialtimeout: 10msreadtimeout: 10mswritetimeout: 10mspool:maxidle: 16maxactive: 64idletimeout: 300s# 健康檢查health:storagedriver:enabled: trueinterval: 10sthreshold: 3# 檔案結構包括要定期檢查的路徑的清單為一個檔案的\存在。如果檔案存在于指定的路徑,健康檢查将失敗。您可以使用這一機制通過建立一個檔案,使系統資料庫進行旋轉。file:- file: /path/to/checked/fileinterval: 10shttp:- uri: http://server.to.check/must/return/200headers:Authorization: [Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==]statuscode: 200timeout: 3sinterval: 10sthreshold: 3tcp:- addr: redis-server.domain.com:6379timeout: 3sinterval: 10sthreshold: 3proxy:remoteurl: https://registry-1.docker.iousername: [username]password: [password]compatibility:schema1:signingkeyfile: /etc/registry/key.jsonenabled: true# 驗證validation:manifests:urls:allow:- ^https?://([^/]+\.)*example\.com/deny:- ^https?://www\.example\.com/
           

0x06 入坑解決

問題1: Error in deleting repository in a private registry V2 #1573;

問題原因:在啟動registry時候沒有啟用

REGISTRY_STORAGE_DELETE_ENABLED=true

環境變量;

ISSUE: https://github.com/docker/distribution/issues/1573

解決辦法: 啟動docker registry 加入環境變量;

-e "REGISTRY_STORAGE_DELETE_ENABLED=true"
           

往期文章:

1.Docker學習之基礎知識

2.Docker學習之基礎使用

3.Docker學習之Dockerfile

4.Docker常用指令參數一覽表

5.Docker安裝運作異常解決記錄總結

個人首頁:

- 部落格:https://blog.weiyigeek.top/about/

- 知乎:WeiyiGeek - https://www.zhihu.com/people/weiyiSec

- 微信公衆号:WeiyiGeek