如何部署 Docker Registry 服務

發表於2015-04-28

本文闡釋了怎樣部署私有的 Docker Registry 服務 —— 或為公司私用,或公開給其他使用者使用。例如,你公司可能需要私人的 Registry 來支援持續整合(CI)。又或,你的公司可能有大量映象方式的產品或服務,你想以公司品牌的方式來整體提供和呈現。

Docker 公共的 Registry 裡維護有一個預設的 registry 映象可以用以協助該部署過程。該 Registry 映象對本地測試足矣,但不能用於生產環境。對於生產環境,應以 docker/distribution 為基礎,自行配置並構建自定義 Registry 映象。

注意:本文中的例子在 Ubuntu 14.04 下編寫及測試。如果你在不同的作業系統中執行
Docker,你或許需要“翻譯”這些命令,以適合你執行環境的需要。

官方映象下的簡單示例

本節中,將建立一個 Container 來執行 Docker 的官方 Registry 映象。你將推送(Push)一個映象到這個 Registry 伺服器,然後再從該 Registry 中拉取(Pull)同一個映象。

這是個很好的練習,有助於理解客戶端與本地 Registry 的基本互動。

1、安裝 Docker。

2、從 Docker 公共 Registry 中執行 hello-world 映象。

$ docker run hello-world

run 命令自動從 Docker 的官方映象庫中將 hello-world 映象 pull 下來。

3、在 localhost 上啟動 Registry 服務。

$ docker run -p 5000:5000 registry:2.0

這將在 DOCKER_HOST 上啟動一個 Registry 服務,並在 5000 埠監聽。
4、列出映象。

$ docker images
REPOSITORY     TAG     IMAGE ID      CREATED       VIRTUAL SIZE
registry       2.0     bbf0b6ffe923  3 days ago    545.1 MB
golang         1.4     121a93c90463  5 days ago    514.9 MB
hello-world    latest  e45a5af57b00  3 months ago  910 B

這個列表應當包括一個由先前執行而得來的 hello-world 映象。

5、為本地 repoistory 重新標記 hello-world 映象。

$ docker tag hello-world:latest localhost:5000/hello-mine:latest

此命令使用 [REGISTRYHOST/]NAME[:TAG] 格式為 hello-world:latest 重新打標。REGISTRYHOST在此例中是 localhost。在 Mac OSX 環境中,得把 localhost 換成 $(boot2docker ip):5000

6、列出新映象。

$ docker images
REPOSITORY                  TAG          IMAGE ID      CREATED       VIRTUAL SIZE
registry                    2.0     bbf0b6ffe923  3 days ago    545.1 MB
golang                      1.4     121a93c90463  5 days ago    514.9 MB
hello-world                 latest  e45a5af57b00  3 months ago  910 B
localhost:5000/hello-mine   latest  ef5a5gf57b01  3 months ago  910 B

可以看到,新映象已經出現在列表中。

7、推送新映象到本地 Registry 中。

$ docker push localhost:5000/hello-mine:latest
The push refers to a repository [localhost:5000/hello-mine] (len: 1)
e45a5af57b00: Image already exists
31cbccb51277: Image successfully pushed
511136ea3c5a: Image already exists
Digest: sha256:a1b13bc01783882434593119198938b9b9ef2bd32a0a246f16ac99b01383ef7a

8、使用 curl 命令及 Docker Registry 服務 API v2 列出 Registry 中的映象:

$ curl -v -X GET http://localhost:5000/v2/hello-mine/tags/list
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /v2/hello-mine/tags/list HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Docker-Distribution-Api-Version: registry/2.0
< Date: Sun, 12 Apr 2015 01:29:47 GMT
< Content-Length: 40
<
{"name":"hello-mine","tags":["latest"]}
* Connection #0 to host localhost left intact

也可以透過在瀏覽器中訪問以下地址來獲取這些資訊:http://localhost:5000/v2/hello-mine/tags/list

9、從你的本地環境中移除所有未使用的映象:

$ docker rmi -f $(docker images -q -a )

此命令僅用於說明目的;移除映象強制 run 從 Registry 而不是從本地快取拉取。如果在這之後執行docker images,在你的映象列表中,應該看不到任何 hello-world 或 hello-mine 的例項。

$ docker images
REPOSITORY      TAG      IMAGE ID      CREATED       VIRTUAL SIZE
registry         2.0     bbf0b6ffe923  3 days ago    545.1 MB
golang           1.4     121a93c90463  5 days ago    514.9 MB

10、試執行 hello-mine

$ docker run hello-mine
Unable to find image 'hello-mine:latest' locally
Pulling repository hello-mine
FATA[0001] Error: image library/hello-mine:latest not found

命令 run 執行失敗,因為你的新映象在 Docker 公共 Registry 中是不存在的。
11、現在,嘗試指定映象的 Registry 來執行映象:

$ docker run localhost:5000/hello-mine

如果你在這之後執行 docker images, 你會發現裡面多了一個 hello-mine 的例項。

使 Docker 官方 Registry 映象做好生產環境準備

Docker 的官方映象只為簡單的測試或除錯準備。其配置對多數生產環境來講都不適用。例如,任何能訪問伺服器 IP 的客戶端,都能推送及拉取映象。參看下一節,獲取使該映象做好生產環境準備的資訊。

理解生產環境的部署

當部署一個用於生產環境釋出的 Registry 時,須考慮如下因素:

  • BACKEND STORAGE 應在何處儲存映象?
  • ACCESS AND/OR AUTHENTICATION 使用者是否應擁有全部或受控的訪問許可權?這取決於你為公眾提供映象服務,還是隻為公司內部提供。
  • DEBUGGING 當問題或狀況發生時,是否有解決這些它們的方法。日誌由於可以看到問題動向,這使其很有用。
  • CACHING 快速提取映象可能至關重要,如果依賴映象進行測試、構建,或有其他自動化系統的話。

我們可以配置 Registry 功能特性,用以調整適配這些因素。可以在命令列裡指定選項來幹這個,或者更通常地,用一個 Registry 配置檔案來完成此事。配置檔案是 YAML 格式的。

Docker 的官方 Repository 映象用以下配置檔案做了預置:

version: 0.1
log:
level: debug
fields:
service: registry
environment: development
storage:
cache:
  layerinfo: inmemory
filesystem:
  rootdirectory: /tmp/registry-dev
http:
addr: :5000
secret: asecretforlocaldevelopment
debug:
  addr: localhost:5001
redis:
addr: localhost:6379
pool:
maxidle: 16
maxactive: 64
idletimeout: 300s
dialtimeout: 10ms
readtimeout: 10ms
writetimeout: 10ms
notifications:
endpoints:
  - name: local-8082
    url: http://localhost:5003/callback
    headers:
       Authorization: [Bearer <an example token>]
    timeout: 1s
    threshold: 10
    backoff: 1s
    disabled: true
  - name: local-8083
    url: http://localhost:8083/callback
    timeout: 1s
    threshold: 10
    backoff: 1s
    disabled: true

這個配置非常基本,可以看到這在生產環境下會有一些問題。例如,http 區塊詳述了執行 Registry 的主機的 HTTP 伺服器配置,但伺服器沒有使用甚至是最低要求的傳輸層安全性(TLS)配置。接下來我們將配置這些東西。

在 Registry 伺服器上配置 TLS

在本節中,將在伺服器上配置 TLS,使能透過 https 協議來通訊。在伺服器上啟用 TLS 是在企業防火牆內執行 Registry 推薦的最低安全配置。達成這一目標的方法之一就是構建你自己的 Registry 映象。

下載 source 並生成 certificates

1、下載 Registry 原始碼

當然,也可以使用 git clone 命令。

2、把下載的包解壓到本地目錄。

解壓後建立 distribution 目錄。

3、進入 distribution 目錄。

$ cd distribution

4、新建 certs 子目錄。

$ mkdir certs

5、使用 SSL 生成自簽名證書。

$ openssl req \
     -newkey rsa:2048 -nodes -keyout certs/domain.key \
     -x509 -days 365 -out certs/domain.crt

此命令將提示你回答一些基本的資訊,用於建立證書。

6、列出 certs 目錄的內容。

$ ls certs
domain.crt domain.key

當你構建這個 Container 時,certs 目錄及其內容也會自動被複制。

將 TLS 加入配置

distribution 軟體庫在 cmd 子目錄中包含有示例 Registry 配置。在本節中,您可以編輯這些配置之一,以新增 TLS 支援。

1、編輯 ./cmd/registry/config.yml 檔案。

$ vi ./cmd/registry/config.yml

2、定位到 http 區塊。

http:
    addr: :5000
    secret: asecretforlocaldevelopment
    debug:
            addr: localhost:500

3、給伺服器的自簽名證書新增一個 tls 區塊:

http:
    addr: :5000
    secret: asecretforlocaldevelopment
    debug:
            addr: localhost:5001
    tls:
        certificate: /go/src/github.com/docker/distribution/certs/domain.crt
        key: /go/src/github.com/docker/distribution/certs/domain.key

提供 Container 內到證書的路徑。如果你希望跨 Layer 使用兩步認證,那麼你可以增加一個 clientcas 區塊選項。

4、儲存並關閉該檔案。

構建並執行你的 Registry 映象

1、構建你的 Registry 映象。

$ docker build -t secure_registry .

2、執行你的新映象。

$ docker run -p 5000:5000 registry_local:latest
time="2015-04-12T03:06:18.616502588Z" level=info msg="endpoint local-8082 disabled, skipping" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
time="2015-04-12T03:06:18.617012948Z" level=info msg="endpoint local-8083 disabled, skipping" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
time="2015-04-12T03:06:18.617190113Z" level=info msg="using inmemory layerinfo cache" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
time="2015-04-12T03:06:18.617349067Z" level=info msg="listening on :5000, tls" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
time="2015-04-12T03:06:18.628589577Z" level=info msg="debug server listening localhost:5001"
2015/04/12 03:06:28 http: TLS handshake error from 172.17.42.1:44261: remote error: unknown certificate authority

觀察啟動時的資訊。你應該可以看到 tls 在執行。

3、使用 curl 驗證可以透過 https 連線。

$ curl -v https://localhost:5000
* Rebuilt URL to: https://localhost:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
* successfully set certificate verify locations:
*   CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: http://curl.haxx.se/docs/sslcerts.html

配置適合 v1 及 v2 版本 Registry 的 Nginx

本節介紹瞭如何使用 docker-compose ,在 nginx 代理背後執行版本 1 和 2 並存的 Registry 服務。並存的 Registry 服務都用 localhost:5000 訪問。如果 docker 客戶端版本小於 1.6,那麼 Nginx 將其請求路由到 1.0 版本的 Registry 服務。從更新版本客戶端發來的請求,將路由到 2.0 版本的 Registry 服務。

此過程使用您在上面最後一個過程中建立的 distribution 目錄。該目錄包含有一個 compose 配置示例。

安裝 Docker Compose

1、在你 distribution 目錄所在主機上開啟一個新的終端視窗。

2、獲取 docker-compose 二進位制可執行檔案。

$ sudo wget https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` -O /usr/local/bin/docker-compose

此命令將二進位制可執行檔案安裝到 /usr/local/bin 目錄。

3、新增可執行許可權到二進位制檔案。

$ sudo chmod +x /usr/local/bin/docker-compose

做一些清理

1、移除早先的映象。

$ docker rmi -f $(docker images -q -a )

該步驟是一個內部管理步驟。這可以防止你在這個例子裡錯誤地選取了舊的映象。

2、編輯 distribution/cmd/registry/config.yml 檔案,並移除 tls 區塊。

如果沿用了前面例子裡的東西,你就會有一個 tls 區塊。

3、儲存變更並關閉檔案。

配置 SSL

1、進入 distribution/contrib/compose/nginx 目錄。

該目錄包含了 Nginx 及 Registry 的配置檔案。

2、使用 SSL 生成自簽名證書。

$ openssl req \
     -newkey rsa:2048 -nodes -keyout domain.key \
     -x509 -days 365 -out domain.crt

此命令將提示你回答一些問題,供證書建立使用。

3、編輯 Dockerfile 並新增以下行。

COPY domain.crt /etc/nginx/domain.crt
COPY domain.key /etc/nginx/domain.key

當你全部搞完的時候,這個檔案看上去像下面。

FROM nginx:1.7

COPY nginx.conf /etc/nginx/nginx.conf
COPY registry.conf /etc/nginx/conf.d/registry.conf
COPY docker-registry.conf /etc/nginx/docker-registry.conf
COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
COPY domain.crt /etc/nginx/domain.crt
COPY domain.key /etc/nginx/domain.key

4、儲存並關閉 Dockerfile 檔案。

5、編輯 registry.conf 檔案並增加以下配置。

ssl on;
ssl_certificate /etc/nginx/domain.crt;
ssl_certificate_key /etc/nginx/domain.key;

這是一個 nginx 配置檔案。

6、儲存並關閉 registry.conf 檔案。

構建並執行

1、進到 distribution/contrib/compose 目錄

此目錄包含單一個 docker-compose.yml 配置。

nginx:
build: "nginx"
ports:
    - "5000:5000"
links:
    - registryv1:registryv1
    - registryv2:registryv2
registryv1:
image: registry
ports:
    - "5000"
registryv2:
build: "../../"
ports:
    - "5000"

此配置按 nginx/Dockerfile 所指定,構建一個新的 nginx 映象。1.0 版本的 Registry 來自 Docker 的官方公開映象。Registry 2.0 映象將從你前面用到的 distribution/Dockerfile 來構建。

2、獲取 Registry 1.0 映象。

$ docker pull registry:0.9.1

Compose 的配置是在本地尋找此映象。如果你不做這步,那後面的步驟會失敗。

3、構建 nginx,Registry 2.0 映象,以及

$ docker-compose build
registryv1 uses an image, skipping
Building registryv2...
Step 0 : FROM golang:1.4

...

Removing intermediate container 9f5f5068c3f3
Step 4 : COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
---> 74acc70fa106
Removing intermediate container edb84c2b40cb
Successfully built 74acc70fa106

此命令將輸出其執行過程,直到執行結束。

4、啟動使用了 Compose 的配置。

$ docker-compose up
Recreating compose_registryv1_1...
Recreating compose_registryv2_1...
Recreating compose_nginx_1...
Attaching to compose_registryv1_1, compose_registryv2_1, compose_nginx_1
...

5、在另一個終端,顯示執行中的配置。

$ docker ps
CONTAINER ID        IMAGE                       COMMAND                CREATED             STATUS              PORTS                                     NAMES
a81ad2557702        compose_nginx:latest        "nginx -g 'daemon of   8 minutes ago       Up 8 minutes        80/tcp, 443/tcp, 0.0.0.0:5000->5000/tcp   compose_nginx_1
0618437450dd        compose_registryv2:latest   "registry cmd/regist   8 minutes ago       Up 8 minutes        0.0.0.0:32777->5000/tcp                   compose_registryv2_1
aa82b1ed8e61        registry:latest             "docker-registry"      8 minutes ago       Up 8 minutes        0.0.0.0:32776->5000/tcp                   compose_registryv1_1

瀏覽一下

1、檢查一下你 nginx 伺服器上的 TLS。

$ curl -v https://localhost:5000
* Rebuilt URL to: https://localhost:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
* successfully set certificate verify locations:
*   CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: http://curl.haxx.se/docs/sslcerts.html

2、標記 v1 registry 映象。

$ docker tag registry:latest localhost:5000/registry_one:latest

3、將其推送到 localhost

$ docker push localhost:5000/registry_one:latest

如果你在使用 1.6 版本的 Docker 客戶端,這將推送映象到 v2 registry。

4、使用 curl 來列出 Registry 中的映象。

$ curl -v -X GET http://localhost:32777/v2/registry1/tags/list
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 32777 (#0)
> GET /v2/registry1/tags/list HTTP/1.1
> User-Agent: curl/7.36.0
> Host: localhost:32777
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Docker-Distribution-Api-Version: registry/2.0
< Date: Tue, 14 Apr 2015 22:34:13 GMT
< Content-Length: 39
<
{"name":"registry1","tags":["latest"]}
* Connection #0 to host localhost left intact

本例參照引用了分配給 2.0 Registry 服務的特定埠。早些時候,在使用 docker ps 命令顯示正在執行的容器時,你應該看到過這個埠。

相關文章