Docker 入門 第五部分:Stacks
先決條件
- 安裝 Docker 1.13 或更高版本
- 按照第三部分中的描述安裝Docker Compose。
- 按照第四部分中描述安裝了Docker Machine,
- 閱讀第一部分
- 在第二部分中學習如何創建容器
- 確保你已經將創建的
friendlyhello
鏡像發布到registry。我們會在這里用到這個鏡像 - 確保你的鏡像已經作為容器被部署運行,運行這個命令,在你的信息中加入
username
,repo
, 和tag
:docker run -p 80:80 username/repo:tag
, 然后查看http://localhost/
。 - 從第三部分復制一份
docker-compose.yml
- 確保你在第四部分中設置的機器已經運行並准備就緒。運行
docker-machine ls
來驗證。如果機器已經停止運行,運行docker-machine start myvm1
啟動管理器,然后運行docker-machine start myvm2
來啟動worker。 - 運行第四部分中的swarm並讓其准備就緒,可以運行
docker-machine ssh myvm1 "docker node ls"
來驗證。如果swarm已經運行,那兩個節點都會報告一個ready
狀態,如果沒有,按照第四部分中的部署swarm中的內容重新初始化swarm並加入worker。
介紹
在第四部分,我們學習了如何部署一個swarm(swarm是一個運行Docker的機器集群),部署一個應用到swarm,其中容器在多台機器上運行。
在本節,你會了解到分布式程序層次結構的頂層:Stack。Stack一組相互關聯的服務,他們共享依賴關系,並可以在一起被編排和擴展。單個Stack可以定義和協調整個應用程序的功能(雖然復雜度很高的應用可能需要使用多個Stack)。
好消息是,在第三部分中當你創建Compose 文件並使用docker stack deploy
時,你就已經開始使用Stack技術。但是那只是運行在一台單獨的主機上的一個單個的Stack,通常在生產環境里不會這么使用。在這里,你可以根據所學內容,使多個服務彼此關聯,並運行在多台機器上。
添加一個新的服務並重新部署
在docker-compose.yml
文件中添加一個服務很簡單。首先,我們先添加一個可視化服務,看看swarm是如何調度容器的。
1.編輯docker-compose.yml
文件,使用下面的內容替換原內容。確保使用你的鏡像信息替換username/repo:tag
:
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
這里唯一改變的地方是新增了和web同級的visualizer
服務。有兩個事情需要注意: volumes
密鑰讓可視化工具可以攔截到Docker的主機套接字文件;placement
密鑰,確保該服務只運行在swarm管理器上,而不是worker上。這是因為這個容器是由Docker創建的開源項目構建的,使用圖示的方式顯示swarm上運行的docker服務。
我們現在就來看看placement約束和volumes。
2.確保你的shell已被配置為可與myvm1進行通信。(完整的例子點這里)
- 運行
docker-machine ls
列出機器,並確保你已經連接到myvm1. - 如果有必要,重新運行
docker-machine env myvm1
,然后運行指定的命令來配置shell。
Mac 和 Linux上的命令:
eval $(docker-machine env myvm1)
Windows上的命令:
& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression
3.在管理器上重新運行docker stack deploy
,並更新需要更新的服務。
$ docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: angi1bf5e4to03qu9f93trnxm)
Creating service getstartedlab_visualizer (id: l9mnwkeq2jiononb5ihz9u7a4)
4.在可視化工具中查看
從Compose文件中可以看出visualizer
運行在8080端口。運行docker-machine ls
獲取其中一個節點
的IP地址。訪問該IP地址的8080端口就可以看到正在運行的visualizer:

visualizer
的其中一個副本正如我們預期的一樣運行在管理器上。我們可以運行docker stack ps <stack>
來確認這個
docker stack ps getstartedlab
該可視化工具是一個獨立的服務,可以在stack里任何包含它的app上運行。它不依賴任何其他內容。現在我們創建一個具有依賴關系的服務:提供瀏覽器計數器的Redis服務。
保存數據
我們可以通過相同的步驟來添加一個Redis數據庫來存儲應用數據。
1.在docker-compose.yml文件末尾點擊一個Redis服務,並保存這個新文件。確保使用你的鏡像信息替換username/repo:tag
。
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
redis:
image: redis
ports:
- "6379:6379"
volumes:
- "/home/docker/data:/data"
deploy:
placement:
constraints: [node.role == manager]
command: redis-server --appendonly yes
networks:
- webnet
networks:
webnet:
Docker庫中有一個Redis的官方鏡像,使用了一個很短的鏡像名字:redis
,因此這里沒有使用username/repo
來表示。Redis的6379端口已經被預設為從容器暴露給主機,在我們的compose文件中,我們將其從主機暴露給公網,因此,如果你選擇這樣做,你就可以在任何節點連接Redis桌面管理器並管理該redis節點。
最重要的是,在該stack上部署redis期間,redis規范中有一些內容可以使數據保持不變。
- redis總是運行在管理器上,所有它總是使用相同的文件系統
- redis訪問主機上文件系統的任意目錄將其作為容器內的
/data
目錄,用來存儲redis數據。
總之,這會在主機的物理文件系統上為Redis數據創建一個”真實數據源(官方叫“source of truth”)“。如果沒有這個,Redis會把數據保存在容器內的/data
目錄,如果容器被重新部署,那么數據就會被刪除。
真實的數據源有兩部分組成:
- Redis服務有位置約束,確保它總是使用相同的主機。
- 創建的卷讓容器可以將
./data
(主機上的)作為/data
(redis容器上)來進行訪問。當容器創建刪除時,在指定主機上的。/data
下存儲的文件會一直存在,從而實現連續性。
到這里,我們就做好了部署Redis stack的准備。
2.在管理器上創建./data
目錄
docker-machine ssh myvm1 "mkdir ./data"
3.確保你的shell已被配置為可與myvm1通信(點此查看完成例子)
- 運行
docker-machine ls
,列出機器,確保你已經鏈接上myvm1
,如下條所示。 - 如有必要,重新運行
docker-machine env myvm1
,然后運行給定的命令來配置shell。
# Mac 或 Linux上的命令:
eval $(docker-machine env myvm1)
# Winidows上的命令:
& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression
4.再次運行docker stack deploy
$ docker stack deploy -c docker-compose.yml getstartedlab
5.運行docker service ls
來驗證三個服務是否按照預期運行。
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
x7uij6xb4foj getstartedlab_redis replicated 1/1 redis:latest *:6379->6379/tcp
n5rvhm52ykq7 getstartedlab_visualizer replicated 1/1 dockersamples/visualizer:stable *:8080->8080/tcp
mifd433bti1d getstartedlab_web replicated 5/5 gordon/getstarted:latest *:80->80/tcp
6.在其中一個檢點上檢查web頁面,例如http://192.168.99.101
,查看瀏覽計數器的結果,該計數器已存在並將信息存儲在redis上。

另外,檢查任意節點IP地址上的8080端口的可視化工具,注意redis
服務與web服務和visualizer服務都在運行,

回顧
下面是本節內容的回顧:
bash-3.2$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 - virtualbox Running tcp://192.168.99.104:2376 v17.04.0-ce
myvm2 - virtualbox Running tcp://192.168.99.105:2376 v17.04.0-ce
bash-3.2$ docker-machine ssh myvm1 "docker node ls"
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
fvenziy5o4xmziyzqxianzd65 myvm2 Ready Active
x500bs7lrweto9chkg6xq2ybd * myvm1 Ready Active Leader
bash-3.2$ docker-machine ssh myvm1 "docker stack ps getstartedlab"
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kjeymj6rp0y8 getstartedlab_web.1 johndmulhausen/get-started:part1 myvm2 Running Running 15 minutes ago
dehkjrmu0fxn getstartedlab_web.2 johndmulhausen/get-started:part1 myvm1 Running Running 15 minutes ago
acnejfyy1cmg getstartedlab_web.3 johndmulhausen/get-started:part1 myvm2 Running Running 15 minutes ago
36lpsek707gj getstartedlab_web.4 johndmulhausen/get-started:part1 myvm1 Running Running 15 minutes ago
q5yb5uj97ef1 getstartedlab_web.5 johndmulhausen/get-started:part1 myvm2 Running Running 15 minutes ago
bash-3.2$ ls
docker-compose.yml
bash-3.2$ more docker-compose.yml
version: "3"
services:
web:
image: johndmulhausen/get-started:part1
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
bash-3.2$ docker-machine scp docker-compose.yml myvm1:~
docker-compose.yml
bash-3.2$ docker-machine ssh myvm1 "docker stack deploy -c docker-compose.yml getstartedlab"
Creating service getstartedlab_visualizer
Updating service getstartedlab_web (id: a3mhoq23ydyut4uje16slqum2)
bash-3.2$ docker-machine ssh myvm1 "docker stack ps getstartedlab"
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ts3ud4mdf9qi getstartedlab_visualizer.1 dockersamples/visualizer:stable myvm1 Running Running 34 seconds ago
kjeymj6rp0y8 getstartedlab_web.1 johndmulhausen/get-started:part1 myvm2 Running Running 20 minutes ago
dehkjrmu0fxn getstartedlab_web.2 johndmulhausen/get-started:part1 myvm1 Running Running 19 minutes ago
acnejfyy1cmg getstartedlab_web.3 johndmulhausen/get-started:part1 myvm2 Running Running 20 minutes ago
36lpsek707gj getstartedlab_web.4 johndmulhausen/get-started:part1 myvm1 Running Running 19 minutes ago
q5yb5uj97ef1 getstartedlab_web.5 johndmulhausen/get-started:part1 myvm2
bash-3.2$ more docker-compose-WITH-REDIS.yml
version: "3"
services:
web:
image: johndmulhausen/get-started:part1
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
redis:
image: redis
ports:
- "6379:6739"
volumes:
- ./data:/data
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
bash-3.2$ docker-machine scp docker-compose-WITH-REDIS.yml myvm1:~
docker-compose-WITH-REDIS.yml
bash-3.2$ docker-machine ssh myvm1 "docker stack deploy -c docker-compose-WITH-REDIS.yml getstartedlab"
Updating service getstartedlab_web (id: a3mhoq23ydyut4uje16slqum2)
Updating service getstartedlab_visualizer (id: uwd8tja3fpcpsy8kumk19orr8)
Creating service getstartedlab_redis
bash-3.2$ docker-machine ssh myvm1 "docker stack ps getstartedlab"
PORTS
o2xct1jotx55 getstartedlab_redis.1 redis:latest myvm1 Ready Rejected 1 second ago "invalid mount config for type…
"
tf1s212hfh9c \_ getstartedlab_redis.1 redis:latest myvm1 Shutdown Rejected 6 seconds ago "invalid mount config for type…
"
sizyxk5f0a5h \_ getstartedlab_redis.1 redis:latest myvm1 Shutdown Rejected 11 seconds ago "invalid mount config for type…
"
xd8s1ljsj9oq \_ getstartedlab_redis.1 redis:latest myvm1 Shutdown Rejected 16 seconds ago "invalid mount config for type…
"
6h4wfo8nophs \_ getstartedlab_redis.1 redis:latest myvm1 Shutdown Rejected 21 seconds ago "invalid mount config for type…
"
ts3ud4mdf9qi getstartedlab_visualizer.1 dockersamples/visualizer:stable myvm1 Running Running 4 minutes ago
kjeymj6rp0y8 getstartedlab_web.1 johndmulhausen/get-started:part1 myvm2 Running Running 24 minutes ago
dehkjrmu0fxn getstartedlab_web.2 johndmulhausen/get-started:part1 myvm1 Running Running 23 minutes ago
acnejfyy1cmg getstartedlab_web.3 johndmulhausen/get-started:part1 myvm2 Running Running 24 minutes ago
36lpsek707gj getstartedlab_web.4 johndmulhausen/get-started:part1 myvm1 Running Running 23 minutes ago
q5yb5uj97ef1 getstartedlab_web.5 johndmulhausen/get-started:part1 myvm2
bash-3.2$ docker-machine ssh myvm1 "mkdir ./data"
bash-3.2$ docker-machine ssh myvm1 "docker stack deploy -c docker-compose-WITH-REDIS.yml getstartedlab"
Updating service getstartedlab_web (id: a3mhoq23ydyut4uje16slqum2)
Updating service getstartedlab_visualizer (id: uwd8tja3fpcpsy8kumk19orr8)
Updating service getstartedlab_redis (id: oxlqkgq0vcmlzc5k1gk0rthdf)
bash-3.2$ docker-machine ssh myvm1 "docker stack ps getstartedlab"
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xl9qttm6cg7v getstartedlab_redis.1 redis:latest myvm1 Running Running 32 seconds ago
ihuhl093b492 getstartedlab_visualizer.1 dockersamples/visualizer:stable myvm1 Running Running 33 seconds ago
3jpjjz6hdmex getstartedlab_web.1 johndmulhausen/get-started:part1 myvm2 Running Running 31 seconds ago
q9w4v6g14bxm getstartedlab_web.2 johndmulhausen/get-started:part1 myvm1 Running Running 34 seconds ago
w8z6vyae3cyb getstartedlab_web.3 johndmulhausen/get-started:part1 myvm2 Running Running 31 seconds ago
xgpfmyhp2uub getstartedlab_web.4 johndmulhausen/get-started:part1 myvm1 Running Running 34 seconds ago
jdkgknm7wlip getstartedlab_web.5 johndmulhausen/get-started:part1 myvm2 Running Running 31 seconds ago
bash-3.2$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 - virtualbox Running tcp://192.168.99.104:2376 v17.04.0-ce
myvm2 - virtualbox Running tcp://192.168.99.105:2376 v17.04.0-ce
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> cfc0b7af2d99<br/><b>Visits:</b> 1
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> 8900768882f9<br/><b>Visits:</b> 2
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> 3adb6b451c2e<br/><b>Visits:</b> 3
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> 8398387efdb7<br/><b>Visits:</b> 4
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> 67e3e4c066ee<br/><b>Visits:</b> 5
bash-3.2$ curl http://192.168.99.105/
<h3>Hello World!</h3><b>Hostname:</b> cfc0b7af2d99<br/><b>Visits:</b> 6
本節了解到stack是全部一致運行的相互關聯的服務,並且,從第三節就開始使用stack。還學習了想stakc中添加服務,將它們加氟Compose文件中。最后,學習了通過位置約束和卷組的組合,你可以創建一個永久的源來保存數據,一般在銷毀並重部署容器時,app的數據仍會存在。