Skip to content

linux

Docker基礎介紹與實戰

  • Ops

TOC

image

Docker簡介

  • Docker是2013年由DotCloud開發的開源專案,因為軟體的成功,公司之後也改名為Docker.Inc
  • 其實Docker的前身是象龜(@gordonTheTurtle),之後改成鯨魚

Docker的重要性

  • 容器將會是未來最有影響力的基礎架構
  • 不管你是開發、維運、系統管理、部屬都會須要學一下
  • 成長最快的雲端技術
  • Infrastructure as code

Docker的優點

  • 過去底層的基礎架構都是為sysops或sysadmins設計的,但Docker的解決方案考慮了developer的使用情境
  • 就是快:develop faster, build faster, test faster, deploy faster, update faster, recover faster
  • 使用容器來減少不同應用程式、不同系統、不同相依性的複雜度
  • 大部分現存的軟體開發得少維護得多,Docker可以減輕我們維護的難度,增加開發的時間

相關工具

學會了Docker你可能需要多了解其他工具

安裝前要注意的

在不同環境下的安裝:

  • Linux: 直接安裝,不過有分版本
  • Windows: Win10不直接支援,需要透過virtual machine來安裝,在安裝的流程中會設定到;因為Windows Containers的出現,新的Windows Server 2016之後會開始直接支援Docker,不用再透過Linux Container
  • Mac: 也是要透過vm,據說不要用brew來裝
  • Cloud: Docker for GCP/AWS/Azure

看更多有關windows container:

Stable vs Edge(beta)

開源版本的Edge每個月會更新一次,Stable大概四個月更新一次,付費的企業版本會有更穩定的支援

安裝

Windows 10 Pro / Enterprise

  • 使用Pro跟Enterprice的用戶算是比較吃香,會有比較好的體驗
  • 請在此下載安裝Docker for Windows
  • 命令界面(CLI)會建議使用PowerShell
  • 如果你的系統已經有在使用VirtualBox或VMware,Hyper-V啟用可能會發生資源互搶的問題

Windows 7, 8, or 10 Home Edition

  • Win7, 8與Pro/Enterprise的主要差別是Hyper-V過舊不支援,Win10 Home則是沒有Hyper-V,因此要額外安裝Docker Toolbox,然後透過VirtualBox安裝Linux VM(Linux Container),也就是 docker-machine 使用網路位址轉換(NAT)存取網路
  • 需要把Toolbox的 http://localhost 改成 http://192.168.99.100

Mac

  • OSX Yosemite 10.10.3以下版本的需要裝Toolbox,以上版本安裝Docker for Mac

Linux

  • 不要用系統預設的packages直接下指令安裝,例如 apt install docker.io,你可能會裝到過舊的版本
  • 可以經由Docker automated script來安裝相依的程式,例如 curl -ssl https://get.docker.com/ | sh
  • 或者先看過官網上的要求來手動下載安裝檔

Docker會動用到系統核心的功能,需要root權限來操作 你可以將使用者加入Docker group,註:有些版本的linux如Red Hat, Fedora沒有此選項,每個指令都要透過 sudo

Docker Machine & Docker Compose

Windows和Mac會自動幫你安裝好,Linux系統則要自己安裝這兩個項目

Docker compose

使用官方文件提供的指令可能會安裝到非最新的Docker compose版本,你可以到github/docker/compose去安裝最新的版本

版本格式

現今版本的格式為 YY.MM,如 18.06.0 就是2018年6月出的版本的第一個release

其他選項

你可以試試Play with Docker,無須安裝任何環境就可以透過瀏覽器體驗Docker的強大,可以參考以下文章:

指令格式

舊的指令格式為:docker <command> (options),例如 docker run,而新的Docker指令的格式已改成:

例如 docker run 現在要寫成 docker container run,不過舊的指令格式目前還能使用

安裝完成你可以試著執行以下指令:

  • docker: 檢視所有管理選項
  • docker version: 確認你可以跟docker engine溝通,和檢查你的版本號
  • docker info: 檢視docker engine的各項設定值
  • docker image ls: 檢視本機所有映像檔
  • docker pull <-- IMAGE -->: 取得映像檔

容器(Containers)

什麼是容器

容器是基於特定映像檔所創造出來的程序

映像檔(image)和容器(container)的差別

  • 映像檔是我們想執行的應用程式的模板,一個映像檔可以包含一個完整的作業系統環境,裡面安裝了需要的應用程式,同一個映像檔可以建立多個容器
  • 容器是從映像檔建立的執行實例,可以被啟動、開始、停止、刪除
  • 大部分的映像檔都可以從Docker Hub下載並取用

基本指令

  1. 從Docker Hub下載nginx映像檔
  2. 建立一個映像檔實例(容器),命名為webhost1並執行
  3. Port Forwarding: 啟用主機的80 port並將容器內部使用的80 port映射到主機上

註:

  • nginx server預設的對外端口是80 port
  • 你可以更改要映射到主機的哪個port,例如 8888:80,然後用 localhost:8888 訪問

加入detach參數只在背景執行,並回傳容器的id

顯示目前所有容器

關閉正在運行的容器

顯示webhost2的log

檢視webhost2的程序,或檢查是否有正在運行的webhost2

強制關閉運行中的容器

以上這些指令發生了什麼事

docker container run

  1. 尋找特定的映像檔快取,找不到該映像檔的話會從遠端倉庫(remote image repo)尋找(預設是Docker Hub,若沒提供映像檔版本將下載最新的版本)
  2. 新增映像檔實例(容器)
  3. Docker引擎會給這個容器一個實體IP(virtual ip)
  4. 啟用host的特定埠號將容器的特定埠號映射到host
  5. 以映像檔的Dockerfile CMD來執行容器

容器(container)和虛擬機(virtual machine)的差別

很多介紹容器的文章把容器拿來和vm比較,雖然它們相似的地方很多,但事實上它們是完全不同的概念,因為:

  • 容器只是程序(processes)
  • 容器可用的資源是受限的
  • 容器關閉=程序停止

舉個例子來說:

檢視此容器是否正在運行

檢視所有運行中的docker容器,註:docker ps 會對象是host,如果系統是mac或windows,需要先連到docker vm

檢視系統上所有程序,可以清楚的看到容器是一個系統上正在執行的程序

練習:開啟多個容器

  1. 執行一個nginx實例,在背景執行且聽80port
  2. 執行一個httpd(apche)實例,在背景執行且聽8080port
  3. 執行一個mysql實例,在背景執行、密碼設定為自動產生,聽3306port

檢視這三個容器的狀態

檢視mysql密碼

基本指令:監控執行中的容器

檢視容器的設定(metadata),會回傳json陣列

檢視所有容器的即時狀態(live performance)

基本指令:在容器中使用終端機

  • run -t: Allocate a pseudo-TTY
  • run -i: Keep STDIN open even if not attached

根據格式 docker container run [OPTIONS] IMAGE [COMMAND] [ARG...],在指令後面可以再帶入 [COMMAND] 及參數 [ARG...],以nginx為例,預設程式(default program)是 nginx,參數是 -g'daemon off;'

加入bash參數來改變預設程式,進入bash shell之後exit,可以看到容器隨之停止:

這邊可以看出預設的程式變成bash:

  • start -a: Attach STDOUT/STDERR and forward signals
  • start -i: Attach container’s STDIN

重啟容器,注意這裡一樣會開啟bash shell因為我們建立容器時就把command改成bash了:

如果容器正在執行中,我們如何透過殼程式操作呢(這很常用,在容器執行時debug或設定參數):

  • exec: 在執行中的容器上執行額外的程序
  • exec -t: Allocate a pseudo-TTY
  • exec -i: Keep STDIN open even if not attached

另外要注意的是,並非所有映像檔都有bash程式,例如Linux的超迷你分支alpine[ 延伸閱讀 ]:

要使用bash要先透過alpine內建的殼程式sh來安裝

Docker背後的網路運作

docker讓你可以建立虛擬網路(virtual network),並將container加到網路內,建立起屬於你自己應用程式的網路拓墣 [來源]

docker daemon運作的時候,會建立三個網路

當你建立容器時,使用的ip跟主機並不同,例如以下例子:我的主機內部ip是 192.168.43.63,而我的nginx容器的內部ip則是 172.17.0.2

它們之間透過docker birdge network的模式(預設),此模式透過一個叫docker0的NAT server來掌管容器的網路連線 image [圖片來源]

透過以下指令可以看出bridge網路裡有哪些容器

新增一個網路(預設是bridge driver),然後建立新的容器

連結容器至多個網路

DNS設置

容器隨著不同設置會改變其狀態,容器間用ip位址來連線是不可靠的,因此我們會需要DNS server

先來看看新網路的DNS能不能運作

同樣在nginx容器安裝ping套件後,測試能不能雙向溝通

註:如果我以上的測試在預設的bridge network做的話會出現錯誤訊息 Name or service not known,原因是預設的bridge網路並沒有內建的DNS server,容器要連線必須手動使用 --link 指令為容器設定連線到bridge網路。建議建立新的網路來省去這一步驟

練習:快速更新Linux分支的CLI套件

  1. 分別檢查容器不同分支的Linux上的curl版本
  2. 分別在centos:7和ubuntu:14.04的容器中開啟終端機

分別取得各版本的映像檔

建立ubuntu映像檔實例,開啟終端機並檢查curl版本

container run --rm: exit之後預期容器被移除

建立centos映像檔實例,開啟終端機並檢查curl版本

練習:輪替式DNS(DNS Round Robin aka poor man’s load balancer)

  1. 建立一個network包含兩個elasticsearch:2的映像檔實例
  2. 將兩個容器的網路拓墣別名(network-alias)都命名為search

透過一個內網centos容器來測試連線

curl -s: Silent mode. Don’t output anything

映像檔(image)

什麼是映像檔?根據官方定義跟我隨翻:

An image is an ordered collections of root filesystem changes and the corresponding execution parameters for use within a container runtime

映像檔 = 檔案系統變動的有序集合 + 執行一個實例時相對應的執行參數

  • 不是作業系統、沒有內核(kernal)、沒有核心模組(kernal module)[ 延伸閱讀 ]
  • 體積非常輕量,小則幾KB(golang static binary),大則幾GB(ubuntu distro + apache + php)

Docker Hub

目前最多人用的映像檔的集散地(image registry),映像檔repo分成 officialnon-official,映像檔會用 tag 做區別(正確來說tag不算版本或分支),舉例來說,官方的nginx repo:

  • 1.15.5, mainline, 1, 1.15, latest (mainline/stretch/Dockerfile)
  • 1.15.5-perl, mainline-perl, 1-perl, 1.15-perl, perl (mainline/stretch-perl/Dockerfile)
  • 1.15.5-alpine, mainline-alpine, 1-alpine, 1.15-alpine, alpine (mainline/alpine/Dockerfile)

分別pull下來,可以看出同一個映像檔(相同image id)可以有多個tags

Docker Hub的操作跟Github很類似,可以fork其他repo或是上傳新的repo,你所需要的資訊也都能在detail page或github上找到。

  • 映像檔可以從Github或Bitbucket的repo來建置,從首頁點選 Create Automated Build 連結帳戶即可
    • 可以選擇你的映像檔要從哪個分支來build
    • 對於dockerfile的 FROM 指令,Docker Hub可以設定Repository Links,設定連結後,來源映像檔如果有偵測到更新,你的映像檔就會跟著rebuild
    • 自定義Rebuild的Trigger

Union檔案系統(Union file system) && 映像檔的資料層(Image layers)

映像檔的形成並非一個大的資料區塊,例如當我pull nginx:alpine時,可以看到有些資料層我已經有了

透過 image history 來檢視映像檔的資料更動紀錄:

註:IMAGE ID為 <missing> 只是為了區別——這些資料層不完整代表這個映像檔,只是被這個映像檔所用

所有映像檔都是繼承於特定基礎映像檔(blank layer, scratch),再往上繼承堆疊,不同映像檔之間可以共享基礎的檔案系統層,提升儲存效率 [ 延伸閱讀 ]

image [ 圖片來源 ]

image [ 圖片來源 ]

透過 image inspect 來檢視映像檔的metadata,可以看到這個映像檔開了哪些port、有哪些環境變數以及建立的時候會執行的指令等

fork一份映像檔到自己的repo

註:

  • 在遠端機器使用 docker login 的時候,操作完要登出才會移除機器上儲存的密碼
  • 欲上傳private映像檔要先建立一個私人的repo

Dockerfile基本指令

範例1

  • FROM: 開頭一定要加的,指定一個 Base Image 來初始化,通常是取用較輕量的分支如alpine
  • ENV: 設定環境變數
  • RUN: 每行指令都會往上建築新的 layer(new layer on top),上面的範例用 && 來連結每一行指令是常見的作法
  • EXPOSE: 容器沒有任何預設開啟的TCP/UDP埠號,而加了也不代表這些埠號會自動打開,要開啟埠號還是要透過 container run -p
  • CMD: 容器執行或停止的時候都會執行的指令 註:docker能自動幫我們處理logging,上面的範例將log送到stdout跟stderr,讓docker可以捕捉到這些log然後做後續處理

在dockerfile所在的目錄下build image

稍微更改一下dockerfile再安裝一次,可以看出union file system怎麼運作的,dockfile頂端指令會動到的資料幾乎沒什麼變動:Using cache,而越接近後頭的指令,會有較多的變動

範例2

  • 如果你能從更末端的映像檔來build,例如 FROM 官方的nginx再做一些客製化,在維護dockerfiles時就會更加容易。如範例A到範例B
  • WORKDIR: 意同 RUN cd /some/path,不過使用 WORKDIR 會更好
  • COPY: 這個範例用本地的index.html複寫原本nginx的index.html
  • 這個範例並沒有改寫 CMD,這個指令會繼承 nginx:latest 的command

建立一個容器,訪問主頁面時預期會看到客製後的index.html

練習:建立Dockerfile(alpine + node.js + tini)

發布到Docker Hub

持久化數據(Persisting Data)

容器的設計理念有兩個特性:

  1. immutable intrastucture: 當你需要更改設定,皆是透過重新建立新的容器
  2. ephemeral:無狀態,代表容器可以被關閉、銷毀或取代

延伸閱讀: Pets vs Cattle Analogy

容器的特性帶來可靠性(iability)和一致性(consistency),不過相對地帶來一個問題,即如何維持持久化數據(如資料庫),針對這種關注點分離(Seperation of concerns, SOC)的架構設計問題,Docker提供了幾個方案:

  1. Volumes: 由Docker管理,存儲在宿主機的某個地方(在linux上是/var/lib/docker/volumes/)。非Docker應用程序不能改動這一位置的數據。Volumes是Docker最好的數據持久化方法
  2. Bind mounts: 數據可以存放在宿主機的任何地方。數據甚至可以是重要的系統文件或目錄。非Docker應用程序可以改變這些數據;適用於local 開發測試
  3. tmpfs mounts: 數據只存儲在宿主機的內存中,不會寫入到宿主機的文件系統 [ 來源 ]

image

Volumes

先來看一下mysql:8 Dockerfile的 VOLUME 指令

意即當mysql的容器建立的時候,docker會在本機上新增一個 volume location 然後跟容器裡的數據目錄互通,這兩個路徑指向host同一個位址,資料只有靠手動方式才能移除,不會隨著容器被移除而消失

建立一個mysql容器然後看metadata

可以看到data儲存在主機 /var/lib/docker/volumes/c7eaf7ddfa3ad5d90abbe0372d628f97ddcc2384dae45a35d60c74bfdac37416/_data 的位置

而這在使用上會比較不友善:沒辦法從volume看出哪個容器是連結到自身

這裡稍微改善的方式是用參數 -v 為volume命名(以專案來命名之類的)

source的path也變得比較乾淨易讀

另外Volume也可以是匿名的(anonymous volume),會分配一個隨機的名字,在同一個主機中不會重覆[ 官方文件 ]

Bind Mounting

  • host優先於container
  • 不能在Dockerfile裡使用,只能透過 container run
  • 通常是用 -v 指令,或 --mount,格式為 /path/host:/path/container,如

範例

新增一個nginx容器,把 /home/ssivart/桌面目錄mount至容器的 /usr/share/nginx/html

接著我在 /home/ssivart/桌面新增一個index.html,內容為 <h1>Hello World! Bind Mount</h1>

預期在localhost:80會看到我更改過的首頁內容

-v--mount 的差別[ 文件 ]

現在這兩個指令差別僅在於如果host上目錄不存在,使用 -v 會幫你建立新的目錄,使用 --mount 會顯示錯誤

練習:postgres版本更新用Volumes保持數據

練習:使用Bind Mount架設ruby + jekyll[ 來源 ]

Docker Compose[ 官方文件 ]

  • 應用程式常常需要結合多個容器如SQL、proxy、網頁和後端排程等,docker compose便是用來設置容器之間的關係
  • 一鍵完成
  • 可以透過Docker Swarm 1.13以上版本部屬compose file

Docker compose包含兩個部份:

  1. docker-compose.yml: YAML格式的文件來設定容器、網路、Volumes的hierarchy
    • 有區分版本,如1, 2, 2.1
    • -f 來讀取特定檔案,預設讀取的檔名是 docker-compose.yml
  2. docker-compose: 命令列(CLI)工具用來測試compose file

以下是compose file範例:

一個簡單的proxy network設定如下

docker compose CLI

  • 請參考上方提到的安裝流程
  • 僅用於開發測試端,非正式部屬使用

常用指令

  • docker-compose up: 設定volumes/networks,執行所有容器,使用 -d 讓程序於背景執行
  • docker-compose down: 停止並移除所有容器,常用 -v 來移除所有volumes/networks

練習: drupal + postgres架站

使用compose來建置(build)客製化映像檔

  • 使用 docker-compose up,如果找不到該映像檔的cache,會在當下建置
  • re-build使用 docker-compose up --builddocker-compose build

先來看以下compose範例:

當proxy容器執行時,會先在cache找nginx-custom這個映像檔,找不到會build,這邊示範了在 context 提供的目錄下用 dockerfile 來建置映像檔

執行完 down,docker並不會主動移除客製化映像檔,針對要移除上面範例經過命名的映像檔,down 指令加上 --rmi all 參數,如果是不提供映像檔名稱,docker會以 <bulid directory>_<container name> 的規則來命名映像檔,可以透過 --rmi local 移除

composefile配置

這個部份官方還在持續更新 往後針對部署端的compose還會有許多變動(swarm、stack、secrets…),之後會持續更新

docker-compose會自動辨識.override.yml 的檔案,.test.yml.prod.yml 或其他自訂的compose file則需要手動透過指令 -f 來操作

本地開發端:docker-compose.yml + docker-compose.override.yml

CI測試:docker-compose.yml + docker-compose.test.yml

部署通常會用 config 輸出成一個完整的compose file:docker-compose.yml + docker-compose.prod.yml

Swarm Mode

  • Swarm用來做(叢集)架構管理
  • Swarm內建於Docker
  • 多個Docker host組成1個Swarm mode
  • Swarm程式獨立於Docker,在Docker環境下透過swarmkit運行(須先啟用)

Node

Swarm中的Docker的實例(Docker host)

image

  • Worker:
    • 負責容器的執行
  • Manager:
    • 在Swarm中透過Raft Database的設定進行協調與同步
    • 負責管理Worker與協調容器的部署工作
    • 也可以當Worker,Manager可以想像成有Swarm控制權限的Worker

兩個角色可以互換,node之間透過雙向TLS(mutual Transport Layer Security, 前身是SSL)協定溝通

image

Service and Task

  • Task: Container + Command(怎麼run這個容器)
  • Service: Task A + Task B + Task C(任務的堆疊),基於 docker run 的再封裝

Swarm會確保services持續運作

在新的處事方式上,服務器被編好號,就像牛在牛群中。比如,www001到www100。當一個服務器宕機了,它將會被取出替換上線 —— Randy Bias

image

Swarm運作

image [ 圖片來源 ]

基本指令

docker swarm init 可以新增一個swarm,其中完成這些動作[ 文件 ]

  1. 初始公開金鑰基礎架構(public key infrastructure, PKI)
    • Docker先扮演第一個Manager node(root manager)
    • 產生一組root certificate authority(.ca)
    • 產生token: worker token + manager token
    • 其他node可以用這組token加入(join)swarm
  2. 初始Raft database
    • 儲存憑證
    • 在control plane讓Manager之間共享log,透過TLS
    • 儲存config data[ 文件 ]

初始後可以看到第一個manager node

建立一個service,指定task的容器執行的指令及參數(docker run)

檢視service的task,可以看到一個執行中的容器

更新service的參數,提高task數量(replicas)來sacle up

手動停止其中一個容器,service會再add一個task到service queue取代之,確保同時會有3個正常運作的task

要終止task必須移除整個service,同時會把當中所有的程序清理掉

更新services

指令範例

service更新其中的tasks都會重建,task建立時docker會挑負載較小的node做較多分配,所以對service做 force update 可以平衡node之間的負載

CLI更新

service createservice update 在不同版本上的參數變更:

  1. 17.05前:必須都要透過 service lsservice ps 來檢查是否正常執行
  2. 17.05後:新增了 --detach 參數,預設為true
  3. 17.10後:--detach 參數,預設改為false 結論是17.12版本後要透過shell scripts或automation來建立service的話,記得設定 --detach=true

使用GCP Compute Engine Instance Group來試作多節點Swarm

worker node沒有swarm的控制權

提昇host的worker成manager,node狀態變成Reachable

Overley Network

跨Node間的網路拓樸,基於Routing Mesh

psql在node1,drupal在node2,node2可以透過dns name:psql來存取node1的資料庫,用80 port連到node1也能看到drupal的歡迎頁面

Routing Mesh

  • 為service底下的task提供路由(routes ingress packets)
    • container-to-container use Virtual IP
    • external traffic incomming to publish ports(all nodes listen)
  • 為services提供負載平衡
    • stateless load balancer,如果需要指向特定container的話,須另外設定(如cookie、session)
    • 使用linux既有的IPVS(IP Virtual Server)實現load balancing
    • load balancer表現於TCP層的(OSI第3層),針對一個swarm一個對外port的web架構,還會需要Nginx、HAProxy來做DNS層(第4層)的load balancer
  1. 無論訪問網路中的哪個節點,即使該節點上沒有運行該service的副本,最終都能訪問到該service 舉例來說,如果後端資料庫有3個副本,當前端web server要取資料時,並非直接訪問某個資料庫副本的ip,而是透過swarm為所有service搭建的Virtual IP(VIP)
  2. 外部流量導向所有節點共同監聽的public port

image

image

練習:voting app[ dockersamples ]

image

首先先建立前後端的網路,跨node要用overlay模式

Stacks

  • dockerfile版本3以上
  • 適用於部署端的compose(services + volumes + overlay networks + secrets…)
  • composefile指令跟local端的差異:開發端忽略 deploy 指令,部署端忽略 build
  • 部署不透過docker-compose
  • 針對已經存在的service,再部署會update這些service

上述的範例只要透過一個Composefile就能完成所有部署

swarm的可視化工具visualizer

image

Secrets

  • 儲存:
    • 使用者帳密
    • TLS憑證、金鑰
    • SSH金鑰
    • 自訂設定檔
  • 支援動態字串或二進位的內容
  • 儲存在Raft database,只存在於各個manager node的硬碟空間
  • secrets產生後會先存在swarm再分配到特定service,只有特定的container能訪問
  • 乍看之下是個實體檔案,但實際上是ramfs files system透過記憶體儲存
  • secrets只適用於swarm,而swarm只適用於部署端(production),但docker讓docker-compose也能使用,僅提供開發測試,不具真的功能(fake secure)

secrets無法透過指令檢視內容(廢話),容器要存取secret得先經過指定

但這樣還少了怎麼配置這些secret,docker提供一個方便的查找方式,前提是映像檔要有這些變數

終端機進容器檢視密碼

針對已經建立的service可以透過 update --secret-rmupdate --secret-add 來更新secret,但這樣做會導致整個service重新部署

secrets透過stack部署

  • dockerfile版本3.1以上

透過實體檔案儲存密碼的方式是有風險的,記得在部署完成後移除這些檔案

開發端測試secrets

  • docker-compose CLI版本11以上適用
  • 開發端是使用bind mount的方式把secrets mount到本機目錄,只是用於開發並無secret實際功能
  • 不適用內含external用法,如果部署是用 external:true,可以另外維護一個開發用的compose file,secret的部份改用file來寫入

Healthchecks

  • 回傳0(OK)或1(Error)
  • 支援Dockerfile、Compose、docker run 及swarm services
  • 容器有三種健康狀態: starting, healthy, unhealthy

container

services

Docker Registry

push之後映像檔的實體檔案儲存在/var/lib/registry/

Registry in Swarm mode

  • 主要問題是解決nodes之間要訪問同一份映像檔
  • 如果只在其中一個manager node build image,其他node是沒辦法取得該image
  • 解法:
    • 使用Docker hub、AWS、Quay來儲存管理映像檔
    • 借助Routing Mesh,所有node都能藉由127.0.0.1:5000訪問

以下示範如何使用Routing Mesh建立registry

將映像檔push到registry後,所有nodes都能從127.0.0.1:5000取得映像檔