寫在前面的話node
至此,docker 的基礎知識已經瞭解的差很少了,接下來就來談談對於 docker 容器,咱們如何來管理它。python
docker swarmmysql
在學習 docker swarm 以前,得先知道容器編排工具的做用:提供了基於容器調度和集羣的技術,並編排容器之間如何進行交互。web
docker swarm 不是惟一的容器編排工具,但倒是 docker 自帶的容器編排工具。redis
能夠看看它的主要結構(圖片來自互聯網):sql
docker swarm 以集羣的形式運行,包括兩個角色:Manager 和 worker。docker
Manager 是 swarm 的集羣的核心,因此通常會有多個,並且建議是奇數個,避免出現腦裂的狀況。數據庫
多個 Manager 之間經過 Raft 協議進行信息同步。flask
Worker 就是幹活的節點,節點之間經過 GOSS 網絡進行信息同步。api
Service 和 Replicas:每個應用就是一個 Service,而每一次該應用的運行就是 Replicas。
建立 docker swarm
準備 3 臺虛擬機:
IP | 主機名 | 說明 |
---|---|---|
192.168.100.100 | docker-node1 | Manager |
192.168.100.101 | docker-node2 | Worker |
192.168.100.102 | docker-node3 | Worker |
初始化 Manager 節點並將兩個 Worker 節點加入:
node1 執行:
docker swarm init --advertise-addr=192.168.100.100
結果如圖:
複製紅色部分到另外兩個節點執行!
node2 和 node3 執行:
docker swarm join --token SWMTKN-1-4423bymu0axfzp7cr1df6jjq36d5errbhetzlg3zz722lzt2j5-4nopnics4j267mh1gidf8z7nm 192.168.100.100:2377
結果如圖:
在 node1 節點上查看 Swarm 信息:
docker node ls
結果如圖:
相似相關集羣的命令在 Worker 節點上執行通常都會報錯:
This node is not a swarm manager
常見錯誤:
1. 初始化或者加錯了節點想退出:
docker swarm leave --force
2. node2 和 node3 加入 swarm 時候報錯:
Error response from daemon: error while validating Root CA Certificate: x509: certificate has expired or is not yet valid
這種問題通常是幾臺服務器的時間不一致,同步下時間通常就行了:
yum -y install ntp ntpdate -u ntp.api.bz
3. node2 和 node3 加入 swam 好久沒反應,報錯:
connect: no route to host
這種問題通常是防火牆緣由,關閉服務器的防火牆便可:
systemctl stop firewalld.service
systemctl disable firewalld.service
操做 docker swarm
在 docker swarm 中,命令都是基於 service 運行,並且全部服務都是運行在 Manager 節點。
【1】建立運行容器:
docker service create --name demo busybox sh -c "while true;do sleep 300; done"
【2】查看運行信息:
docker service ls
結果如圖:
【3】查看該容器運行在哪一個節點:
docker service ps demo
結果如圖:
經過 create 時候的名字查找,但這個名字並非容器名字。
【4】將容器運行多個節點:
docker service scale demo=3
查看:
docker service ps demo
結果如圖:
能夠看到,3個容器被平均分配到了各個節點。
【5】刪除容器:
docker container rm -f a98aad768cb9
先隨便刪除一個容器,再查看,結果如圖:
能夠發現,容器在短期內會少掉一個節點,可是隨即再度恢復成 3 個節點。
docker service ps demo
查看詳細信息,結果如圖:
能夠看到一個被 Shutdown 的容器。值得注意的是,這個容器並不存在了已經。
【6】刪除整個 Service:
docker service rm demo
結果如圖,啥都不剩:
實戰 - docker swarm 部署 wordpress
注意:在作這個實戰以前,最好將 3 個節點的 docker 都重啓一下,確保每一個節點都存在:docker_gwbridge 和 ingress 網絡。
【1】在 Manager 節點建立 overlay 網絡和 MySQL:
network create -d overlay demo
結果如圖:
該建立只存在於 Manager 節點。
docker service create --name mysql --env MYSQL_ROOT_PASSWORD=123456 --env MYSQL_DATABASE=wordpress --network demo --mount type=volume,source=mysql_data,destination=/var/lib/mysql mysql:5.7
和直接 docker run 有着明顯的不一樣,-e 參數變成了 --env,-v 參數變成了 --mount。
查看建立結果:
docker service ls
docker service ps mysql
結果如圖:
【2】建立 wordpress:
docker service create --name wordpress -p 8888:80 --network demo --env WORDPRESS_DB_PASSWORD=123456 --env WORDPRESS_DB_HOST=mysql wordpress
查看結果:
【3】訪問測試:
這裏使用 Manager 節點的 IP 進行訪問的,你也可使用 Worker 節點的 IP 訪問,三個 IP 都是沒問題的。基於這一點,就能夠實現不少騷操做。
一樣,能夠啓動多個節點:
docker service scale wordpress=3
結果如圖:
Routing Mesh
爲啥同一 overlay 網絡中的多個容器之間可以互相通訊,切訪問的時候每一個 IP 都能訪問?
這就是 Routing Mesh,其主要的兩種體現:Internal 和 Ingress。
Internal:容器與容器之間訪問經過 overlay 網絡
【1】經過建立兩個容器進行測試,分別是 busybox 和 jwilder/whoami:
# 建立測試 overlay 網絡 docker network create -d overlay ov-demo1 # 建立兩個容器 docker service create --name busybox --network ov-demo1 busybox sh -c "while true;do sleep 3000;done" docker service create --name whoami --network ov-demo1 -p 8000:8000 -d jwilder/whoami # 將 whoami 建立成多個節點 docker service scale whoami=3
【2】進入 busybox 測試:
docker exec -it 1e68d97b6cda sh
結果如圖:
記住上面這個 IP。而後去查看幾個 whoami 容器的 IP,都不是這個 IP。
那這個 IP 是啥?
學過 LVS 的就知道有個東西叫作 VIP,也就是虛擬 IP,而這裏的原理就是 LVS 實現,而這個 IP 就是 VIP,全部纔會有訪問哪一個 IP 都行的結果,並且負載均衡。
Ingress: 若是服務有綁定端口,則能夠經過任意節點的這個端口訪問。
原理在於:當訪問指定端口的時候,請求被 gwbridge 轉發帶 ingress-sbox 的 namespace 中,而後讓 LVS 調度給了任意一臺機器。
docker swarm 中的 docker-compose.yml
上面學習的 docker swarm 雖然很爽,可是仍是沒有擺脫手動寫很長的命令的束縛。因此有了接下來的專門用於 docker swarm 的 docker-compose.yml。
具體能夠參照官方文檔:
https://docs.docker.com/compose/compose-file/
這裏以部署 wordpress 爲例的 docker-compose.yml:
version: '3'
services:
web:
image: wordpress
ports:
- 8888:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_PASSWORD: 123456
networks:
- ov-test
depends_on:
- db
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 2
delay: 10s
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- mysql_test_data:/var/lib/mysql
networks:
- ov-test
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes:
mysql_test_data:
networks:
ov-test:
driver: overlay
相比於以前 docker-comse 時寫的文件,這裏主要多了 deploy 項,具體能夠根據官方文檔學習。
刪除掉以前創建的全部容器,運行該配置文件,啓動服務:
docker stack deploy wordpess --compose-file=docker-compose.yml
結果如圖:
能夠發現,在 docker swarm 中管理 docker compose 服務,不是使用 docker service 命令,而是換成了 docker stack 命令進行管理。
查看容器的相關信息:
# 查看 docker stack 運行狀況 docker stack ls # 查看運行狀態 docker stack services wordpess # 也能夠用 service 查看 docker service ls # 查看更詳細的信息 docker stack ps wordpess
結果如圖:
訪問測試:
刪除服務:
docker stack rm wordpess
實戰 - docker swarm 中的複雜項目實戰
這是一個投票系統,工做原理以下圖:
docker-compose 文件:
version: '3' services: redis: image: redis:alpine ports: - "6379" networks: - frontend deploy: replicas: 2 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure db: image: postgres:9.4 volumes: - db-data:/var/lib/postgresql/data networks: - backend deploy: placement: constraints: [node.role == manager] vote: image: dockersamples/exampleenvotingapp_vote:before ports: - 5000:80 networks: - frontend depends_on: - redis deploy: replicas: 2 update_config: parallelism: 2 restart_policy: condition: on-failure result: image: dockersamples/exampleenvotingapp_result:before ports: - 5001:80 networks: - backend depends_on: - db deploy: replicas: 1 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure worker: image: dockersamples/exampleenvotingapp_worker networks: - frontend - backend labels: [APP=VOTING] deploy: placement: constraints: [node.role == manager] restart_policy: condition: on-failure delay: 10s max_attempts: 3 visualizer: image: dockersamples/visualizer:stable ports: - 8080:8080 stop_grace_period: 1m30s volumes: - "/var/run/docker.sock:/var/run/docker.sock" deploy: placement: constraints: [node.role == manager] networks: frontend: backend: volumes: db-data:
因爲這個項目會去下載幾個鏡像,特別慢,有興趣的能夠本身嘗試一下。
docker 中的密碼管理
在實際生產中,不少時候數據庫的密碼不適合直接以明文的形式寫出來,好比寫在 docker-compose.yml 文件中,因此這就須要對密碼進行處理。
對於 docker 中的密碼,有兩點須要明確:
1. 密碼存在 Manager 節點的 Raft database 中,可以同步到其它節點。
2. Secert 在容器內部是以文件的形式保存的。
【1】建立 secert:
方法 1:從文件建立,建立一個 passwd 的文件,內容是密碼:admin123
# 建立密碼 docker secret create pass-demo passwd # 查看 docker secret ls
結果如圖:
方法 2:從用戶輸入建立:
echo "admin123" | docker secret create pass-demo1 -
注意最後的 - 符號,結果如圖:
【2】刪除密碼:
docker secret rm pass-demo1
【3】給建立的容器指定密碼:
docker service create --name demo --secret pass-demo busybox sh -c "while true;do sleep 300;done"
找到容器的節點,進入容器查看密碼:
docker exec -it e80ae4cfdf15 sh
結果如圖:
【4】如何使用這個文件,以 MySQL 爲例:
docker service create --name mysql --secret pass-demo -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/pass-demo -d mysql
MySQL 鏡像有個環境變量是能夠指定密碼文件的,可是這個特性是 MySQL 8 才具備的。結果如圖:
【5】如何在 docker-compose.yml 中使用:
version: '3.7' services: web: image: wordpress ports: - 8888:80 secrets: - my-password environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-password networks: - demo depends_on: - mysql deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 2 delay: 10s mysql: image: mysql:5.7 secrets: - my-password environment: MYSQL_ROOT_PASSWORD_FILE: /run/secret/my-password MYSQL_DATABASE: wordpress volumes: - mysql-data:/var/lib/mysql networks: - demo deploy: mode: global placement: constraints: - node.role == manager volumes: mysql-data: networks: demo: driver: overlay secrets: my-password: file: ./password
有幾個地方值得注意:
1. 配置密碼的時候指定的是文件。
2. 多了一個頂層配置,secerts
3. docker-compose.yml 版本是當前最新的,不然可能不支持該語法。
服務更新
服務搭建起來了,那如何在不中止服務的狀況下灰度更新它?docker swarm 提供了本身方法。
這裏以 Flask 項目爲例:
【1】在三個節點的新建目錄,app.py 和 Dockerfil:
mkdir docker-update cd docker-update
app.py:
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello Flask version 1.0\n" if __name__=="__main__": app.run(host="0.0.0.0", port=5000)
Dockerfile:
FROM python:2.7 COPY app.py /app/ RUN pip install flask WORKDIR /app/ EXPOSE 5000 CMD ["python", "app.py"]
【2】將 3 個節點構建成鏡像:
docker build -t dylan/flask-test:v1.0 .
【3】將 3 個節點 app.py 裏面的 1.0 換成 2.0,從新構建鏡像:
docker build -t dylan/flask-test:v2.0 .
【4】部署 1.0 版本:
docker service create --name flask --publish 9999:5000 --network demo dylan/flask-test:v1.0
【5】找到對應的節點訪問測試:
sh -c "while true;do curl 127.0.0.1:9999&&sleep 1;done"
結果如圖:
【6】在線升級:
# 擴展節點 docker service scale flask=3 # 更新 docker service update --image dylan/flask-test:v2.0 flask
結果如圖:
再看看訪問的效果:
能夠看出在升級過程當中,有段時間是雙版本並行的,更新完成後都是新的了。這就實現了業務的不中斷。
寫到這裏,docker swarm 的知識差很少了,若是想更深刻的學習,能夠本身再去研究。
小結
docker swarm 給咱們打開了容器編排的大門,可是最終目標仍是 K8S,若是感興趣,能夠關注後面的 K8S 內容。