【09】按部就班學 docker:docker swarm

寫在前面的話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 內容。

相關文章
相關標籤/搜索