Docker 1.12實踐:Docker Service、Stack與分布式應用捆綁包

2021-12-18 優雲數智

在本文中小數將帶大家了解如何利用Docker Compose創建一套分布式應用捆綁包,並將其作為Docker Stack在Docker Swarm Mode中進行部署。

PS,7月23日「與大神相約暑期檔,漂洋過海對話Docker&Mesos」活動火熱報名中,大家快點擊下方「閱讀原文」報名吧!

Docker 1.12的首套候選發行版於三周之前公布,而近期又有更多新功能計劃被添加至該版本當中。

下面首先來看各項新的功能特性:

內置編排機制:通常來講,應用利用一個Docker Compose文件進行定義。此定義由多個被部署在不同主機上的容器共同構成。這種作法除了能夠避免單點故障(簡稱SPOF)之外,也能夠讓應用具備彈性。目前包括Docker Swarm、Kubernetes以及Mesos在內的多種編排框架都允許大家對此類應用進行編排。不過現在我們又有了新的選擇——Docker Engine如今迎來了內置編排機制。更多細節內容將在後文中進行說明。

Service:現在大家可以利用docker service create 命令輕鬆創建一項複製且分布式的負載均衡服務。該應用可實現「理想狀態」,例如運行三套Couchbase容器,並具備自我修復能力。Docker引擎能夠確保必要容器數量始終運行於集群當中。如果某容器發生故障,那麼另一容器將旋即啟動。如果某臺節點發生故障,則該節點上的容器會在另一節點上啟動。稍後我們將詳細說明其作用。

零配置安全性: Docker 1.12採用相互驗證TLS,能夠對swarm當中各節點間的通信內容進行驗證、授權與加密。更多詳盡內容將在後文中進行討論。

Docker Stack與分布式應用捆綁包:分布式應用捆綁包,或者簡稱DAB,是一種多服務可分發鏡像格式。在後文中我們會進一步討論。

截至目前,大家已經可以選定一個Dockerfile,並利用docker build命令由此創建鏡像。使用docker run命令則可啟動容器。這條命令亦能夠輕鬆同時啟動多套容器。另外,大家也可以使用Docker Compose文件並利用docker-compose scale命令對容器進行規模擴展。



鏡像屬於單一容器的一種可攜式格式。而Docker 1.12當中新推出的分布式應用捆綁包,或者簡稱DAB,則屬於一種新的概念,其專門面向多套容器的遷移需求。每個捆綁包都可作為stack在運行時中進行部署。


感興趣的朋友可以前往docker.com/dab了解更多與DAB相關的內容。為了簡單起見,在這裡我們利用類比來進行說明:

Dockerfile -> 鏡像 -> 容器

Docker Compose -> 分布式應用捆綁包 -> Docker Stack

下面我們使用一個Docker Compose文件來創建DAB,並將其作為Docker Stack加以部署。

需要強調的是,這項實驗性功能僅存在於1.12-RC2版本當中。

利用Docker Compose創建一個分布式應用捆綁包

Docker Compose CLI添加了一條新的bundle命令。下面來看其具體說明:

docker-compose bundle --help
Generate a Docker bundle from the Compose file.

Local images will be pushed to a Docker registry, and remote images
will be pulled to fetch an image digest.

Usage: bundle [options]

Options:
   -o, --output PATH          Path to write the bundle file to.
                              Defaults to "<project name>.dsb".

現在,讓我們選取一條Docker Compose定義並以此為基礎創建DAB。以下為我們的Docker Compose定義內容:

version: "2"
services:
 db:
   container_name: "db"
   image: arungupta/oreilly-couchbase:latest
   ports:
     - 8091:8091
     - 8092:8092
     - 8093:8093
     - 11210:11210
 web:
   image: arungupta/oreilly-wildfly:latest
   depends_on:
     - db
   environment:
     - COUCHBASE_URI=db
   ports:
     - 8080:8080

此Compose文件會啟動WildFly與Couchbase伺服器。其中WildFly伺服器中已經預部署了一款Java EE應用,且接入Couchbase伺服器並允許利用REST API執行CRUD操作。該文件的原始碼來自:github.com/arun-gupta/oreilly-docker-book/blob/master/hello-javaee/docker-compose.yml。利用它生成一個應用捆綁包:

docker-compose bundle
WARNING: Unsupported key 'depends_on' in services.web - ignoring
WARNING: Unsupported key 'container_name' in services.db - ignoring
Wrote bundle to hellojavaee.dsb

depends_on只負責創建兩項服務之間的依賴性,並以特定順序對二者進行啟動。這能確保Docker容器首先啟動,而運行在其中的應用則需要更長時間才能啟動完成。因此,此屬性只在一定程度上解決了這一問題。

container_name能夠為該容器提供一個特定名稱。對特定容器名稱的依賴性為緊密耦合,且不允許我們對該容器進行規模伸縮。因此這裡我們暫時忽略這兩條警告。此命令會利用Compose項目名(也就是其目錄名稱)生成一個文件。因此在本示例中,生成的文件名為hellojavaee.dsb。此文件的擴展名在RC3中則為.dab。此生成的應用捆綁包內容如下所示:

{
 "services": {
   "db": {
     "Image": "arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c",
     "Networks": [
       "default"
     ],
     "Ports": [
       {
         "Port": 8091,
         "Protocol": "tcp"
       },
       {
         "Port": 8092,
         "Protocol": "tcp"
       },
       {
         "Port": 8093,
         "Protocol": "tcp"
       },
       {
         "Port": 11210,
         "Protocol": "tcp"
       }
     ]
   },
   "web": {
     "Env": [
       "COUCHBASE_URI=db"
     ],
     "Image": "arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914",
     "Networks": [
       "default"
     ],
     "Ports": [
       {
         "Port": 8080,
         "Protocol": "tcp"
       }
     ]
   }
 },
 "version": "0.1"
}

此文件為包含在應用內的各項服務提供完整的描述。當然,未來我們應該可以使用其它容器格式,例如Rkt或者VM等形式。不過就目前來講,其還僅支持Docker這一種格式。

在Docker中進行Swarm Mode初始化

正如之前所提到,目前「理想狀態」由Docker Swarm負責保持。而其現在已經被納入Docker Engine當中。在本篇文章中,我們使用新增的一條命令,即docker swarm:

docker swarm --help

Usage: docker swarm COMMAND

Manage Docker Swarm

Options:
     --help   Print usage

Commands:
 init        Initialize a Swarm
 join        Join a Swarm as a node and/or manager
 update      Update the Swarm
 leave       Leave a Swarm
 inspect     Inspect the Swarm

Run 'docker swarm COMMAND --help' for more information on a command.

在Docker Engine中對一個Swarm節點(作為工作節點)進行初始化:

docker swarm init
Swarm initialized: current node (ek9p1k8r8ox7iiua5c247skci) is now a manager.

關於該節點的更多細節信息可利用docker swarm inspect命令進行查看。

docker swarm inspect
[
   {
       "ID": "1rcvu7m9mv2c8hiaijr7an9zk",
       "Version": {
           "Index": 1895
       },
       "CreatedAt": "2016-07-01T23:52:38.074748177Z",
       "UpdatedAt": "2016-07-02T04:54:32.79093117Z",
       "Spec": {
           "Name": "default",
           "AcceptancePolicy":{
               "Policies": [
                   {
                       "Role": "worker",
                       "Autoaccept": true
                   },
                   {
                       "Role": "manager",
                       "Autoaccept":false
                   }
               ]
           },
           "Orchestration": {
               "TaskHistoryRetentionLimit":10
           },
           "Raft": {
               "SnapshotInterval": 10000,
               "LogEntriesForSlowFollowers":500,
               "HeartbeatTick":1,
               "ElectionTick":3
           },
           "Dispatcher": {
               "HeartbeatPeriod": 5000000000
           },
           "CAConfig": {
               "NodeCertExpiry": 7776000000000000
           }
       }
   }
]

從輸出結果中可以看到,該節點只屬於工作節點而非管理節點。如果在單節點集群當中,這樣的設置並無不妥。不過在多節點集群當中,則應至少存在一個管理節點。

部署Docker Stack


利用docker deploy命令創建一個stack:

docker deploy -f hellojavaee.dsb hellojavaee
Loading bundle from hellojavaee.dsb
Creating network hellojavaee_default
Creating service hellojavaee_db
Creating service hellojavaee_web

下面來看各服務列表:

docker service ls
ID            NAME             REPLICAS  IMAGE                 COMMAND
2g8kmrimztes  hellojavaee_web  1/1       arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914    
46xhlb15cc60  hellojavaee_db   1/1       arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c

在輸出結果中,我們可以看到正在運行的兩項服務,分別為WildFly與Couchbase。 Service概念同樣新增於Docker 1.12版本,其負責為我們提供「理想狀態」,而具體實現則由Docker Engine負責。使用docker ps命令顯示當前正在運行的容器列表:

CONTAINER ID        IMAGE                                                                                                 COMMAND                  CREATED             STATUS              PORTS                                                        NAMES
622756277f40        arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c   "/entrypoint.sh /opt/"   3 seconds ago       Up 1 seconds        8091-8093/tcp, 11207/tcp, 11210-11211/tcp, 18091-18092/tcp   hellojavaee_db.1.19enwdt6i5m853m5675tx3z29
abf8703ed713        arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914     "/opt/jboss/wildfly/b"   3 seconds ago       Up 1 seconds        8080/tcp                                                     hellojavaee_web.1.70piloz6j4zt06co8htzisgyl

WildFly容器會在Couchbase容器啟動並運行之前先行啟動。這意味著Java EE應用會嘗試接入Couchbase伺服器但發生失敗。因此,該應用將永遠無法成功完成引導。

自我修復Docker Service

Docker Service負責保持應用的「理想狀態」。在本示例中,我們的理想狀態是確保特定服務有且只有一套容器與之對應且持續運行。如果我們移除該容器,而非服務,則該服務會自動重啟容器。使用以下命令移除容器:

docker rm -f abf8703ed713

請注意,這裡之所以要使用-f,是因為該容器已經處於運行狀態。Docker 1.12自我修復機制會介入並自動重啟此容器。現在再次打開運行容器列表:

CONTAINER ID        IMAGE                                                                                                 COMMAND                  CREATED             STATUS                  PORTS                                                        NAMES
db483ac27e41        arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914     "/opt/jboss/wildfly/b"   1 seconds ago       Up Less than a second   8080/tcp                                                     hellojavaee_web.1.ddvwdmojjysf46d4n3x4g8uv4
622756277f40        arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c   "/entrypoint.sh /opt/"   26 seconds ago      Up 25 seconds           8091-8093/tcp, 11207/tcp, 11210-11211/tcp, 18091-18092/tcp   hellojavaee_db.1.19enwdt6i5m853m5675tx3z29

結果顯示新容器已經啟動完成。檢查WildFly服務:

docker service inspect hellojavaee_web
[
   {
       "ID": "54otfi6dc9bis7z6gc6ubynwc",
       "Version": {
           "Index": 328
       },
       "CreatedAt": "2016-07-02T01:36:35.735767569Z",
       "UpdatedAt": "2016-07-02T01:36:35.739240775Z",
       "Spec": {
           "Name": "hellojavaee_web",
           "Labels": {
               "com.docker.stack.namespace": "hellojavaee"
           },
           "TaskTemplate": {
               "ContainerSpec": {
                   "Image": "arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914",
                   "Env": [
                       "COUCHBASE_URI=db"
                   ]
               }
           },
           "Mode": {
               "Replicated": {
                   "Replicas": 1
               }
           },
           "Networks": [
               {
                   "Target": "epw57lz7txtfchmbf6u0cimis",
                   "Aliases": [
                       "web"
                   ]
               }
           ],
           "EndpointSpec": {
               "Mode": "vip",
               "Ports": [
                   {
                       "Protocol": "tcp",
                       "TargetPort": 8080
                   }
               ]
           }
       },
       "Endpoint": {
           "Spec": {},
           "Ports": [
               {
                   "Protocol": "tcp",
                   "TargetPort": 8080,
                   "PublishedPort": 30004
               }
           ],
           "VirtualIPs": [
               {
                   "NetworkID": "9lpz688ir3pzexubkcb828ikg",
                   "Addr": "10.255.0.5/16"
               },
               {
                   "NetworkID": "epw57lz7txtfchmbf6u0cimis",
                   "Addr": "10.0.0.4/24"
               }
           ]
       }
   }
]

Swarm會將隨機埠分配給該服務,我們也可以利用docker service update命令進行手動更新。在本示例中,容器的埠8080被映射至主機上的埠30004。

進行應用驗證

下面檢查該應用是否已經成功部署:

curl http://localhost:30004/books/resources/book
[{"books":0}]

為該應用添加新的book:

curl -v \
> -H "Content-Type: application/json" \
> -X POST -d '{
>   "isbn": "978-1-4919-1889-0",
>   "name": "Minecraft Modding with Forge",
>   "cost": 29.99
> }' \
> http://localhost:30004/books/resources/book
*   Trying ::1...
* Connected to localhost (::1) port 30004 (#0)
> POST /books/resources/book HTTP/1.1
> Host: localhost:30004
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 92
>
* upload completely sent off: 92 out of 92 bytes
< HTTP/1.1 200 OK
< Connection: keep-alive
< X-Powered-By: Undertow/1
< Server: WildFly/10
< Content-Type: application/octet-stream
< Content-Length: 88
< Date: Sat, 02 Jul 2016 01:39:49 GMT
<
* Connection #0 to host localhost left intact
{"name":"Minecraft Mhttp://localhost:30004/books/resources/book-1-4919-1889-0"}

再次驗證該book:

curl http://localhost:30004/books/resources/book
[{"books":{"name":"Minecraft Modding with Forge","cost":29.99,"id":"1","isbn":"978-1-4919-1889-0"}}, {"books":1}]

欲了解更多與此Java應用相關的信息,請訪問github.com/arun-gupta/oreilly-docker-book/tree/master/hello-javaee。


活動預告:

沒有西遊記的暑期不完美,

沒有重量級嘉賓的 Meetup 不是好沙龍!

顛倒時差與遠在美國的 Timothy Chen 約了好久,

終於敲定7月23日漂洋過海到北京,

與幾位大神級人物來一次 Docker&Mesos 的對話。

快點擊「閱讀原文」報名與他們來一次面對面的交流吧!


相關焦點

  • CoreOS實踐指南(七):Docker容器管理服務
    至今在伺服器系統上流行的安裝軟體方式依然是編譯原始碼、手工的安裝包或各種包管理工具,雖然包管理工具的出現解決了應用軟體安裝、卸載以及自身依賴等諸多問題,卻無法很好的解決軟體之間的依賴衝突。而早在Docker誕生以前,「沙盒」的概念已經被普遍使用在Android、iOS等主流的手機系統中了。
  • 雲計算核心技術Docker教程:Docker Swarm 使用
    docker@swarm-manager:~$ docker service create --replicas 1 --name helloworld alpine ping docker.com查看 helloworld 服務運行在哪個節點上,可以看到目前是在 swarm-worker1 節點:
  • docker下高並發和高可用之docker swarm使用
    準備條件:1.To add a worker to this swarm, run the following command:docker swarm join --token SWMTKN-1-2fl56eq1fov04ftmzsaayrybz92mrc2c90ja5sc8ni4ur5ze54-8lta9gj7pot0s9fhhxsbca032
  • Docker再體驗之Docker Compose,及它與Kubernetes的區別
    sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-composesudo chmod +x /usr
  • Docker 入門教程
    (1)為了降低成本, 進行伺服器整合(2)將一個整體式的應用拆分成低耦合的單個服務(微服務架構)5.整合伺服器Docker隔離應用的能力使得Docker可以整合多個伺服器以降低成本.(1)啟動快容器裡面的應用,直接就是底層系統的一個進程,而不是虛擬機內部的進程。所以,啟動容器相當於啟動本機的一個進程,而不是啟動一個作業系統,速度就快很多。
  • 萬字長文:編寫Dockerfiles最佳實踐
    此功能要求在運行dockerbuild命令的主機上安裝git。要排除與構建無關的文件(不重構源倉庫),請使用.dockerignore文件。 此文件支持類似於.gitignore文件的排除模式。有關創建的信息,請參閱.dockerignore文件。多階段構建允許您大幅減小最終鏡像的大小,而無需減少中間層和文件的數量。
  • 【GITC】搜狐雲臺陳偉:基於docker的自動化運維管理架構與實踐
    十分依賴經驗豐富的運維人員  ● Docker的優勢和問題  陳偉總結docker的優勢有:  1. Docker的先進理念:「build、ship、run」。把應用的構建、分發、運行的流程標準化、規範化。  2. 私有的registry天然就是一個分發倉庫,名稱+tag的方式也很有利於版本管理,回滾等。
  • 編寫Dockerfile
    考慮這個 Dockerfile:# syntax=docker/dockerfile:1FROM ubuntu:18.04COPY .用 .dockerignore 提高速度要排除與構建無關的文件,使用 .dockerignore 文件。此文件支持類似於.gitignore文件的排除模式。不要安裝不必要的軟體包為了減少複雜性、依賴關係、文件大小和構建時間,請避免安裝額外或不必要的軟體包。
  • 使用 Python 優雅地編排 Dockerfile​
    ('always')service.hostname('www.netkiller.cn')service.extra_hosts(['db.netkiller.cn:127.0.0.1','cache.netkiller.cn:127.0.0.1','api.netkiller.cn:127.0.0.1'])service.environment(['TZ=Asia/Shanghai
  • Docker Registry V1 與 V2 的區別解析以及靈雀雲的實時同步遷移實踐
    這裡選擇官方的 nginx dockerfile 來 build 鏡像docker build -t nginx:dockerfile .新模型客戶端只需要和 authorization service 進行一次交互獲得對應 token 即可和 registry 進行交互,減少了複雜的流程。同時 registry 和 authorization service 一一對應的方式也降低了被攻擊的可能。
  • 30分鐘帶你了解Web工程師必知的Docker知識
    首先筆者先來介紹一下Docker:Docker 是一個基於 Go 語言開發的開源應用容器引擎, 可以讓我們把我們的應用和包打包到一個輕量級、可移植的容器中,然後發布到任何流行的 Linux 機器上,並且可以實現虛擬化。所謂容器,就是完全使用沙箱機制,相互之間沒有任何接口,並且性能開銷極低。
  • 推薦給IT新手的11個Docker免費上手項目
    Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的鏡像中,然後發布到任何流行的 Linux或Windows 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何接口。
  • SpringBoot+GitLab+Docker+Jenkins實現持續集成上
    3.1.Docker安裝步驟(1)yum 包更新到最新sudo yum update(2)安裝需要的軟體包, yum-util 提供yum-config-manager功能,另外兩個是devicemapper驅動依賴的sudo yum
  • Docker常用命令就該這麼學!
    Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的鏡像中,然後發布到任何流行的 Linux或Windows 機器上,也可以實現虛擬化。
  • Docker Hello World之初識篇
    來到官網,找到windows版本的下載包,大概500多兆,很快就下載好了。注意,win10的系統比較好安裝,win7系統需要安裝toolbox,有點麻煩,略。    下載下來安裝包,就是一頓點點點,隨後安裝完畢,是使用的過程中,一般是第一次啟動Socker desktop,會提示你更新windows的WSL2的包,就跟著它來到windows的網頁,跟著操作就對了。然後下載一個wsl_update_x64.msi的安裝包,安裝它,然後用命令啟動它,就好了。後面的那些複雜的操作,我不懂,也沒有跟著它操作,似乎也沒啥用的樣子?
  • Docker Hub 官方倉庫發布程式語言包
    Docker 容器發展迅速,正在成為構建分布式應用的平臺。而 Docker 項目組的目標就是不斷的提升用戶快速編碼和構建項目的容易程度。而 Docker 的官方倉庫 正是通過用戶反饋以及一些最佳實踐所提供的免費資源庫。
  • 不用Docker也能構建容器的4種方法
    Docker CLI 不僅用於構建/發布/運行鏡像,多年來它還背負了太多的東西,現在還與 Docker Swarm 和 Docker EE 特性捆綁在一起。在所有的選項中,我最喜歡 k3c,但它使用起來比較繁瑣,它把所有東西都捆綁在一個二進位文件中,這很可能會與其他軟體發生衝突。它運行的是自己的嵌入式 containerd 和 buildkit 二進位文件。
  • 【Docker系列】手把手教你寫Dockerfile
    #開頭的表示注釋行,說明dockerfile中的指令MAINTAINER:Dockerfile作者信息,一般寫的是聯繫方式EXPOSE:指定鏡像容器監聽埠號;發布服務使用ADD:對壓縮文件進行解壓縮;將數據移動到指定的目錄ONBUILD:創建鏡像,作為其他鏡像的基礎鏡像運行操作指令ENTRYPOINT:指定運行容器啟動過程執行命令,覆蓋CMD參數
  • 雲計算核心技術Docker教程:Docker容器使用
    $ docker export <容器 ID> > ubuntu.tar可以使用 docker import 從容器快照文件中再導入為鏡像,以下實例將快照文件 ubuntu.tar 導入到鏡像 test/ubuntu:v1命令如下:$ cat docker/ubuntu.tar
  • 雲計算核心技術Docker教程:Docker多階段構建
    實際上,通常只有一個Dockerfile用於開發(包含構建應用程式所需的一切),而精簡的Dockerfile用於生產時,僅包含您的應用程式以及運行該應用程式所需的內容。這被稱為「構建器模式」。維護兩個Dockerfile是不理想的。