Docker 的部署方式
在使用docker run
命令啟動 Docker 容器時,如果需要進行埠對映、目錄掛載、網路資訊等配置,整條命令將變得非常長,並且由於是一條 shell 命令,修改和複用也不方便。我們在大規模部署容器的時候不可能手動去輸入眾多的命令,所以需要一些工具來輔助我們實現docker run
命令的編寫,同時實現簡單快捷的大規模部署。
docker-compose 部署
docker-compose 是一個讀取特定格式的 yaml 檔案並將其轉換為docker run
命令的工具,它有效的規避了上述的問題,並且它也是docker swarm
、docker stack
等技術的基石。docker-compose 需要一份 yaml 格式的指令碼,如果在使用命令時不想指定具體的指令碼名稱,那就需要將指令碼命名為docker-compose.yml
。下面是一份啟動 MySQL 容器的 docker-compose 指令碼。
version: "3" services: mysql: container_name: login_db image: mysql:5.7 ports: - "3306:3306" environment: - MYSQL_ROOT_PASSWORD=123456 volumes: - "mysql-data:/var/lib/mysql" networks: - my-bridge volumes: mysql-data: networks: my-bridge: driver: bridge 複製程式碼
全域性配置
version
指定的是 docker-compose 的版本,由於v2
和v3
在語法上存在一些不同,所以需要明確告訴 docker-compose 當前指令碼所使用的語法版本是多少。
services
表示服務定義。一份指令碼中可以定義多個服務,docker-compose 會一併啟動。
volumes
下的名稱列表就是服務啟動時要建立的所有資料卷的名稱,只有先建立了資料卷才能在下面的服務定義中的volumes
配置中進行掛載使用。注意區分全域性的 volumes 和服務定義下的 volumes 配置
。
networks
下配置的是服務啟動時要建立的網路及其驅動型別。這裡建立了一個驅動為bridge
、名稱為my-bridge
的橋接網路。只有先建立了網路才能在下面的服務定義中的networks
配置中進行註冊使用。注意區分全域性的 networks 和服務定義下的 networks 配置
。
服務配置
mysql
表示定義一個名叫 mysql 的服務。
container_name
表示服務要啟動的容器的名稱,如果這個服務要動態擴充套件多個容器,則不可以指定容器名稱,否則由於容器名稱衝突將導致無法擴充套件。
image
表示服務要使用的映象。
ports
表示容器與宿主機的埠對映關係。凡是指定了埠對映關係的服務都不能動態擴充套件容器數量,因為會導致埠衝突。
environment
表示要設定到容器中的環境變數。這裡設定的環境變數將被放置到容器的全域性環境變數中,你可以在容器中讀取並操作。
volumes
表示要掛載的目錄或者資料卷。這裡和docker run
命令中的-v
引數作用是一致的,即可以掛載資料卷,也可以掛載目錄。這裡使用到的資料卷必須在全域性配置
中先指定,如果是掛載目錄則需要先手動建立。
networks
表示這個服務要註冊到哪些網路上去,這裡使用到的網路必須在全域性配置
中先指定。註冊到同一個網路上的容器之間可以使用服務名進行通訊。
使用 docker-compose
通過 docker-compose 可以對服務進行啟動、停止、刪除、擴容等操作。docker-compose 的操作必須依賴指令碼,如果指令碼存在於當前目錄下且名為docker-compose.yml
則不需要額外指定,否則需要使用-f
引數進行指定。
啟動服務
啟動服務使用命令docker-compose -f /path/to/script.yml up -d
,up
表示啟動服務,-d
表示後臺執行。這個命令會讀取指令碼檔案並首先建立申明的資料卷和網路,然後再啟動服務。
停止服務
停止服務使用命令docker-compose -f /path/to/script.yml stop
。這個命令會將指令碼中的所有服務的所有容器都停止,但不會刪除容器。
刪除服務
刪除服務使用命令docker-compose -f /path/to/script.yml rm
。這個命令會要求二次確認刪除,並且無法刪除未停止的服務。刪除服務時不會關聯刪除服務啟動時建立的網路和資料卷。
強行刪除服務
強行刪除服務使用命令docker-compose -f /path/to/script.yml down
。這個命令會將所有服務的所有容器都停止並刪除,同時刪除服務啟動時建立的網路,但不會刪除資料卷
。
擴容服務
隨著業務量的上升,我們可能需要將服務從一個容器擴充套件到多個容器以提高服務能力,這時候就可以使用 docker-compose 來直接擴容服務。
docker-compose -f /path/to/script.yml up --scale orderService=3 -d 複製程式碼
這個命令表示將名為orderService
的服務擴容至三個容器並後臺啟動。如果原本的容器數量大於 3 個,那麼這個命令就是縮減容器數量操作。需要注意的是,支援服務擴容的要求是非常苛刻的,需要滿足以下三點要求:
- 不能夠指定 container_name,即容器名;
- 不能夠指定埠對映關係;
- 不能夠指定掛載資料卷或目錄。
如果在指令碼中指定了要掛在的目錄或者資料卷,那麼擴容後多個容器將共用一個數據卷和目錄,這樣就會導致資料出現混亂。解決方法是不指定任何資料卷和目錄進行掛載,由 Docker 自行建立隨機名稱的資料卷。
Docker Swarm 部署
上文講到的 docker-compose 部署方式還停留在單機部署上,但在實際生產環境中不同系統的 Docker 容器都是垮宿主機部署,這時候就需要將這些宿主機形成叢集統一管理,Docker Swarm 就是 Docker 原生提供的一種叢集管理模式。除了 Docker Swarm 外,還有 Mesos、Kubernates 等不同的叢集管理模式。
在 Docker Swarm 叢集模式下,當多個宿主機形成集群后,我們就可以在管理節點(Manager Node)上通過管理命令將不同服務的容器部署到叢集內不同的宿主機上,同一個服務的多個容器也可以分佈到叢集內不同的宿主機上以實現負載均衡。
建立 Swarm 叢集
建立叢集之前需要先規劃好叢集內的節點角色,選擇其中一臺宿主機作為管理節點開始建立叢集,執行如下命令:
docker swarm init --advertise-addr=本機IP 複製程式碼
--advertise-addr
用於指定管理節點所在宿主機的eth0
網絡卡的 IP 地址,如果存在多個網絡卡,這個引數一定要指定,否則可能造成管理節點和工作節點之間無法通訊。命令執行完畢後會輸出如下的提示資訊:
Swarm initialized: current node (gmdscjfdlubanwl7i75z5cc85) is now a manager. To add a worker to this swarm, run the following command: docker swarm join \ --token SWMTKN-1-6djatxtetutac68xd1u8v1icnyv6t0pcplhaph2irqqxqo1m2b-8w6lq2kpw6j1chqpu4vlf2cx3 \ 管理節點IP:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. 複製程式碼
根據輸出提示,我們需要在其他宿主機上執行docker swarm join …
操作以使其加入到叢集作為工作節點。叢集節點加入完成後在管理節點
執行docker node ls
可以看到所有的節點狀態資訊。
IDHOSTNAMESTATUSAVAILABILITYMANAGER STATUS gmdscjfdlubanwl7i75z5cc85 *docker-1ReadyActiveLeader xna7a0h6a0xhct95kh7v6p9pldocker-3ReadyActive 複製程式碼
使用標籤標記節點
Docker Swarm 會根據自己的負載均衡演算法將服務分散部署到不同的叢集節點上,但有時候我們也希望能夠指定服務部署到指定的叢集節點上,這時候就需要通過標籤來指定具體的節點了。下面的名稱嘗試為 HOSTNAME 是docker-3
的節點增加一個標籤:
docker node update --label-add mytag=db xna7a0h6a0xhct95kh7v6p9pl 複製程式碼
這條命令為 ID 為xna7a0h6a0xhct95kh7v6p9pl
的節點增加了一個標籤,標籤名稱為mytag
,標籤值為db
。標籤的使用會在下文 Docker Stack 部署服務時講解。
在 Swarm 叢集中部署服務
有了叢集就可以進行服務部署,下面嘗試在叢集中部署一個 MySQL 服務,在管理節點 執行命令如下:
docker service create --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 複製程式碼
命令的引數和docker run
命令的引數是基本一致的,只是命令換成了docker service create
。命令docker service ls
用於檢視所有已部署的服務;命令docker service ps 服務名稱
用於檢視該服務容器的部署節點和狀態:
IDNAMEIMAGENODEESIRED STATECURRENT STATEERRORPORTS 0m5erytxi6samysql.1mysql:5.7docker-1RunningRunning 3 minutes ago 複製程式碼
上面的資訊表示 MySQL 服務下共啟動了 1 個容器,其部署在名為docker-1
的節點上,容器ID 為0m5erytxi6sa
,當前狀態是正在執行,並且與期望的狀態一致。
擴容服務
與使用 docker-compose 部署服務類似,docker service
命令也可以讓指定的服務進行容量增減,下面的命令嘗試將 MySQL 服務擴容至 3 個容器:
docker service scale mysql=3 複製程式碼
再次執行docker service ps mysql
檢視容器部署情況:
IDNAMEIMAGENODEDESIRED STATECURRENT STATEERRORPORTS 0m5erytxi6samysql.1mysql:5.7docker-1RunningRunning 13 minutes ago idgvqymekwammysql.2mysql:5.7docker-3RunningRunning 3 minutes ago fldlrega7p40mysql.3mysql:5.7docker-3RunningRunning 3 minutes ago 複製程式碼
可以看到容器數量已經擴大至 3 個,其中兩個部署在docker-3
節點上。
更新服務
在服務使用多容器部署的情況下,可以使用服務更新來發布新的版本或調整服務部署引數,這樣可以避免先刪除服務再啟動服務造成的服務中斷。如下命令嘗試使用新版本映象來更新服務:
docker service update --image mysql:5.8 mysql 複製程式碼
這個命令表示對名為mysql
的服務使用使用mysql:5.8
版本的映象進行更新。需要注意的是,如果服務只部署了一個容器,那麼更新過程中,對外服務是肯定會中斷的。
刪除服務
使用命令docker service rm 服務名稱
可以將已部署的服務刪除。刪除服務時服務下所有的容器都會被刪除且不可恢復。
使用 Docker Stack 管理多個服務
Stack
在 Swarm 模式下,使用docker service create
命令可以建立服務進行部署,當多個服務組合到一起時,我們就把它們看作是一個 Stack。簡單來說,**Stack 就是多個 Service 的組合。在 Swarm 模式下可以使用 Docker Stack 可以實現服務的批量部署。
Docker Stack 指令碼
Docker Stack 並不是新的技術點,它同樣是使用 docker-compose 指令碼來管理服務建立指令碼,並且語法沒有任何區別。下面的示例展示了怎麼使用 Docker Stack 來部署一個 WordPress 和 MySQL 服務,其中 WordPress 服務需要引用 MySQL 服務進行資料讀寫:
version: '3' services: wordpress: image: wordpress ports: - 80:80 environment: - WORDPRESS_DB_HOST=mysql - WORDPRESS_DB_PASSWORD=123456 networks: - my-network depends_on: - mysql deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 1 delay: 10s mysql: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORD=123456 - MYSQL_DATABASE=wordpress volumes: - mysql-data:/var/lib/mysql networks: - my-network deploy: mode: global placement: constraints: [node.labels.mytag == db] volumes: mysql-data: networks: my-network: driver: overlay 複製程式碼
在wordpress
服務的environment
中配置的環境變數WORDPRESS_DB_HOST
的值是mysql
,這個mysql
表示的是下面的mysql
服務。由於wordpress
服務和mysql
服務都註冊到了my-network
網路上,所以他們彼此可以通過服務名稱進行通訊,而不需要指定容器 IP。
depends_on
用於配置依賴關係。這裡的配置表示wordpress
服務必須等mysql
服務啟動後才開始啟動,通過這個指令可以組織服務的啟動順序。
deploy
指令下配置的是部署相關的策略資訊。
-
mode
表示部署模式是多副本部署(replicated
)還是全域性單節點部署(global
)。 -
replicas
表示多副本部署時需要多少個副本,即該服務需要啟動多少個容器。 -
restart_policy
用於指定服務下容器的重啟策略,重啟觸發條件由condition
定義,delay
表示每次重啟的間隔時間,max_attempts
表示最大嘗試重啟次數。 -
update_config
用於指定服務更新的策略。parallelism
表示每次更新多少個容器,delay
表示兩次更新的時間間隔。
placement
用於配置節點的部署位置。constraints
用於指定部署位置的判斷條件,上文中的node.labels.mytag == db
表示將mysql
服務部署到標籤mytag
的值為db
的節點上。
部署使用的網路my-network
的驅動是overlay
,這是一種跨主機通訊的網路,在 Swarm 模式下為了讓分佈在叢集內不同節點上的容器能夠互相通訊,他們就都必須註冊到overlay
驅動的網路上。我們也可以預先手動建立一個overlay
驅動的網路:
docker network create -d overlay my-network 複製程式碼
這樣我們在 docker-compose 指令碼中宣告網路時就需要進行如下變更:
networks: my-network: external: true 複製程式碼
external: true
這裡的my-network
網路引用的是預先建立好的同名網路。
部署 Stack
編寫好 docker-compose 指令碼後就可以使用docker stack
命令進行服務部署了,假設上文的指令碼命名為myweb.yml
, 下面的命令使用上文的指令碼進行服務部署:
docker stack deploy -c myweb.yml myweb 複製程式碼
-c
引數是--compose-file
的縮寫,用於指定部署指令碼的位置,最後的myweb
是這個 Stack 的名字,可以任意設定,通常建議和指令碼名稱保持一致,便於管理。命令docker stack ls
可以檢視部署的所有 Stack;命令docker stack ps Stack名稱
可以檢視該 Stack 下所有服務的容器部署情況;命令docker stack services Stack 名稱
可以檢視該 Stack 下所有服務的部署情況,如部署模式、副本數量等資訊。
刪除 Stack
命令docker stack rm Stack名稱
可以刪除一個指定的 Stack。這個命令會刪除該 Stack 下所有的服務及其所有的容器,無法恢復。
規劃 Stack
上文提到刪除 Stack 時,其下所有服務都會被刪除,那麼規劃 Stack 就變得尤為重要。如果在規劃時將所有服務都放到一個 Stack 下,那麼當需要重新部署某個服務時就不得不將其他所有服務也全部重新部署。通常建議將高耦合的服務放到一個 Stack中,每個中介軟體叢集作為一個 Stack,這樣可以避免重新部署時發生雪崩。總之,規劃 Stack 與編寫程式碼類似,都要減少相互的耦合,儘量避免出現雪崩。