使用Docker封裝java應用

本文詳細介紹瞭如何使用docker封裝一個java應用(名字叫cspj,這個java應用涉及數據持久化以及RMI調用),包括:html

  • docker安裝
  • 鏡像製做
  • 容器運行
  • 數據文件處理
  • 私有倉庫
  • swarm集羣,包括service、node、stack、task等相關概念
  • 使用docker-machine快速建立docker虛擬機

1. docker介紹

docker是一種容器技術,對操做系統、文件系統、網絡等進行了封裝,使其中的進程能夠完整運行。java

docker和虛擬機是不一樣的技術。虛擬機虛擬了一套硬件環境,須要在這個硬件環境之上安裝完整的操做系統、jdk等相關軟件,才能運行一個java應用,虛擬機和宿主機在操做系統層面就是相互隔離的。而docker則不一樣,以Linux下的docker爲例,它使用的依舊是宿主機中的Linux內核,只不過在宿主機的用戶層上虛擬了一個容器,這個容器中的Linux只是操做系統中的一小部分,使用的依舊是宿主機的Linux內核。好比宿主機是Ubuntu,docker虛擬的操做系統能夠是alpine,這只是虛擬了alpine和Ubuntu不一樣的部分。node

一個docker容器中通常只運行一個應用,這和虛擬機也是不一樣的。好比咱們的一個應用有java應用,有數據庫mysql,那麼java應用運行在一個容器裏,mySql運行在另外一個容器裏。他們之間能夠經過docker虛擬的網絡進行交互。mysql

docker中的容器就是運行中的進程。它是經過鏡像進行啓動的。docker中的鏡像就至關於一個模板,啓動一個容器就至關於經過模板建立一個可執行的應用。所以,只要鏡像不變,全部經過這個鏡像建立的容器都是一摸同樣的。又由於docker進行了操做系統、文件系統、網絡等方面的封裝,因此這個鏡像就能夠在各類不一樣的環境上運行,從而保證一致的執行效果。linux

容器運行以後,在其中會有一個可讀寫層,這是用來臨時保存容器中應用在運行中產生的數據的。當這個容器被銷燬以後,所保存的數據也就消失了。使用原有的鏡像從新運行一個新的容器,就又是一個全新的應用了。git

因此,若是咱們須要對容器中的數據進行持久化,就須要用到volume或者bind mounts技術。好比咱們的java應用中有一個內置文件數據庫Derby,若是須要保留對這個文件數據庫的修改,同時又不想改變鏡像文件,就能夠把這個文件數據庫使用volume或bind mounts技術保存到宿主機的文件系統中。這樣,即便容器被銷燬,容器中所修改的文件數據庫也會被保留下來。github

還有一種方法保存容器中的臨時數據,就是使用commit命令把容器可讀寫層中的臨時數據也一塊兒生成一個新的鏡像。之後經過這個新鏡像運行的容器,就都保留了這部分數據,這部分數據也就成了新鏡像的一層,並且沒法被修改。經過這個新鏡像運行的容器,會生成一個新的可讀寫層,用來臨時保存這次運行中生成的數據。若是一直使用commit保存數據,新的鏡像就會愈來愈大。docker官方不推薦使用這種方法保存數據。sql

在詳細說一下docker的鏡像。docker的鏡像是使用Dockerfile製做的。Dockerfile是一個腳本,docker build命令會讀取這個腳本,按照其指令構造鏡像。docker的鏡像是一層一層的。每個Dockerfile指令,都會生成鏡像中的一層。docker

咱們本身製做的docker鏡像一般不會從最底層開始構建。好比咱們要製做一個java應用的鏡像,咱們就要依賴於openjdk:8-alpine的官方鏡像。在這個基礎之上,再製做咱們的java應用鏡像層。而官方的openjdk:8-alpine則是基於alpine操做系統製做的鏡像,在這個操做系統之上,它爲咱們設置好了各類環境變量,咱們在這個鏡像之上就能夠直接製做咱們本身的java應用鏡像,而沒必要關心jdk的設置了。數據庫

aphine 是一個特別簡潔的官方的Linux操做系統系統容器鏡像,只有5M大小。從中也能夠看出docker和虛擬機的區別,虛擬機中運行的操做系統必定是完整的操做系統,一般都會有幾個G的大小。

2. 環境準備

實驗電腦爲Intel-Core-i7 CPU, 安裝Windows10操做系統,使用VirtualBox安裝了CentOS-7虛擬機。咱們將在CentOS-7虛擬機上安裝Docker。關於如何在安裝設置虛擬機,請參看這裏

若是要執行8.2節中的實例,必須使用VMWare虛擬機安裝CentOS-7系統,由於VMWare支持nested vm。還須要設置vmware虛擬機的處理器中,選擇「虛擬化Intel VT-x/EPT或AMD-V/RVI(V)。

若是使用AMD處理器,則可使用VirtualBox安裝CentOS-7,由於最新的VirtualBox-6支持在AMD系統上打開netstad vm。

VirtualBox中安裝的CentOS-7系統的IP地址是192.168.56.104.

3. 安裝Docker

Docker分爲社區版和企業版,咱們使用社區版便可。

  • 卸載舊docker
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
  • 安裝docker
# 安裝依賴
$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

# 設置國內鏡像源
$ sudo yum-config-manager \
    --add-repo \
    https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

# 安裝最新版本
$ sudo yum install docker-ce docker-ce-cli containerd.io

# 啓動
$ sudo systemctl start docker

# 驗證,或從docker官方下載hello-world鏡像並根據鏡像運行容器。這個鏡像只有不到2K
$ sudo docker run hello-world

# 把用戶添加到docker組中,這樣執行docker命令時就沒必要使用sudo了
$ sudo usermod -aG docker your-user

設置鏡像加速器能夠加速從Docker Hub獲取鏡像的速度。在/etc/docker/daemon.json文件中(如不存在請新建)添加以下內容:

{
  "registry-mirrors": [
    "https://dockerhub.azk8s.cn",
    "https://reg-mirror.qiniu.com"
  ]
}

以後啓動服務:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

更詳細的安裝方法請參看Get Docker Engine - Community for CentOS安裝 Docker

docker實際上是C/S模式的,咱們在Linux終端輸入的docker命令實際上是客戶端,後臺還有一個服務端在運行。客戶端和服務端能夠不運行在同一個機器上。

4. 製做鏡像

4.1 準備鏡像文件

新建一個空目錄,把java應用程序放入到這個目錄中,並新建Dockerfile文件。這裏咱們先不考慮臨數據庫持久化的問題,直接把全部應用程序進行打包:

[eric@centos7min2 cspj-server]$ ll
total 4
drwxrwxr-x. 2 eric eric 206 Sep 28 21:53 bin
drwxrwxr-x. 2 eric eric 207 Sep 27 16:23 conf
drwxrwxr-x. 4 eric eric  92 Sep 29 14:03 database
-rw-rw-r--. 1 eric eric  93 Sep 29 14:19 Dockerfile
drwxr-xr-x. 3 eric eric 278 Sep 27 16:12 lib

這個java應用程序的啓動腳本是bin/startServer.sh,這個腳本中啓動命令最後有&符號,須要去掉。由於容器中運行的程序都是在前臺運行的,若是加上&符號,這個在前臺運行的startServer.sh腳本就執行完畢,這個容器也就當即中止了。

bin/setEnv.sh中設定了一些RMI參數,爲了能夠進行RMI鏈接,設置其內容以下:

#!/bin/sh

export IP=`awk 'END{print $1}' /etc/hosts`
echo "$IP cspj-host" >> /etc/hosts
cat /etc/hosts

export JAVA_EXECUTE=java
export CSPJ_LIBPATH=../lib/*.jar
export CSPJ_LIBPATH_OPT=../lib/opt/*.jar
export CSPJ_CLASSPATH=../conf/
export JVM_OPTARGS="-Xmx1024m -Xms1024m"
export CSPJ_OPTARGS="-Dcspj.home=$PWD/../ -Dfile.encoding=UTF-8"
export CSPJ_JMXARGS="-Djava.rmi.server.hostname=cspj-host -Dcom.sun.management.jmxremote.port=9998 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

echo $CSPJ_JMXARGS

其中IP是容器運行時動態獲取容器的IP地址,把這個地址寫入到/etc/hosts中時爲了後續進行RMI鏈接,設置-Djava.rmi.server.hostname=cspj-host也是爲了後續的RMI鏈接

4.2 編寫Dockerfile

Dockerfile中內容爲:

FROM openjdk:8-alpine
COPY . /cspj-server/
WORKDIR /cspj-server/bin
CMD ["./startServer.sh"]
  • FROM指令表示咱們的鏡像所基於的基礎鏡像。這裏咱們使用的是openjdk:8-alpine。官方鏡像均可以從docker hub中搜索。好比搜索openjdk,在openjdk界面點擊TAGS頁面,輸入一些過濾信息,就能夠找到對應版本。好比咱們還可使用8-jre-alpine進行過濾,只使用jre
  • COPY指令表示把當前目錄下的全部文件(Dockerfile除外)複製到鏡像中的/cspj-server/目錄。注意必定要有最後的「/」符號,表示複製到這個目錄中。因爲上一條指令咱們基於的基礎鏡像openjdk:8-alpine就已經包含了一個基礎的Linux文件系統,因此執行COPY命令時,就是在鏡像中的文件系統中進行操做。COPY命令會自動在這個文件系統中建立不存在的/cspj-server/文件夾
  • WORKDIR指令指定鏡像的工做目錄。至關於在鏡像的文件系統中進入這個目錄,並把這個目錄設置爲工做目錄
  • CMD指令指定經過這個鏡像啓動容器時須要執行什麼命令。咱們這裏把應用程序的啓動腳本做爲執行命令

在構建鏡像時,docker中每條指令都會構建一層,因此若是有RUN命令時,通常把多個操做都寫在一行裏。

4.3 構建鏡像

在剛纔的目錄中,執行 docker build -t ws3495/cspj-server:v1.0.0 . 命令,構建鏡像。:v1.0.0能夠省略,此時默認是:latest。注意不要丟掉最後的「.」,它以宿主機的一個文件夾做爲"context",Dockerfile中的指令就是基於這個「context」進行構建的。好比這個docker build命令指定了當前路徑(/home/eric/dockertest/forbuildimage/cspj-server)爲「context」,那麼在Dockerfile中,COPY . /cspj-server/ 指令中的「.」指的就是宿主機的/home/eric/dockertest/forbuildimage/cspj-server目錄。關於docker build指令能夠參考docker build,關於上下文能夠參考這裏

經過執行剛纔的命令,其執行過程爲:

[eric@centos7min2 cspj-server]$ docker build -t ws3495/cspj-server:v1.0.0 .
Sending build context to Docker daemon   32.8MB
Step 1/4 : FROM openjdk:8-alpine
 ---> a3562aa0b991
Step 2/4 : COPY . /cspj-server/
 ---> 27361ab40a65
Step 3/4 : WORKDIR /cspj-server/bin
 ---> Running in aef7152e561a
Removing intermediate container aef7152e561a
 ---> b0fcdabdde69
Step 4/4 : CMD ["./startServer.sh"]
 ---> Running in 7a11c32dccae
Removing intermediate container 7a11c32dccae
 ---> 4e56f3b72f1d
Successfully built 4e56f3b72f1d
Successfully tagged ws3495/cspj-server:v1.0.0

經過這個執行過程當中每一個step,能夠看出docker構建鏡像時的操做:

  • Sending build context to Docker daemon: docker客戶端把「context」發送到docker服務端
  • Step 1/4: 根據基礎鏡像openjdk:8-alpine構建第一層。若是這是第一次執行,會從docker hub上拉取這個鏡像,緩存在本地。之後再次執行這個指令時,就會直接從本地獲取這個鏡像了。
  • Step 2/4: 構建第2層,把相對「context」路徑「.」下全部文件複製到鏡像的/cspj-server/文件夾
  • Step 3/4: 構建第3層。先啓動一個容器aef7152e561a,執行了所要求的指令,隨後刪除了這個臨時的容器,並把執行結果進行了提交,就生成了鏡像的一層。
  • Step 4/4: 設置容器啓動命令。
  • 最後打上一個tag

經過執行docker image ls命令,能夠看到剛纔構建的鏡像:

[eric@centos7min2 cspj-server]$ docker image ls
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
ws3495/cspj-server        v1.0.0              4e56f3b72f1d        12 minutes ago      137MB
ws3495/cspj-server        v1.0.1              e5868fe7c123        4 hours ago         132MB
openjdk                   8-alpine            a3562aa0b991        4 months ago        105MB
registry                  latest              f32a97de94e1        6 months ago        25.8MB
hello-world               latest              fce289e99eb9        9 months ago        1.84kB
prakhar1989/static-site   latest              f01030e1dcf3        3 years ago         134MB

這個命令輸出一個相似表格的結構,第一行是表頭。第二行就是咱們剛構建的ws3495/cspj-server:v1.0.0,第3行是咱們從docker hub上拉取的openjdk:8-alpine鏡像。

5. 啓動應用

5.1 啓動容器

執行命令docker run -d -p 27449:27449 -p 27450:27450 ws3495/cspj-server:v1.0.0,依據剛纔製做的鏡像,啓動一個容器:

[eric@centos7min2 cspj-server]$ docker run -d \
>   -p 27449:27449 -p 27450:27450 \
>   ws3495/cspj-server:v1.0.0
47ed8277b0e0bfbb90a798a8b5499a0ee693499fd2342615388248ad72e932ab

命令執行結束後,返給咱們一個字符串,這個串就是這個剛剛啓動的容器的ID。因爲咱們使用了-d參數,因此這個容器在後臺運行。

命令中的-p <host port>:<container port>參數把容器中的端口和宿主機中的端口進行了映射。外部訪問host port的鏈接就會被轉發到這個容器的container port上。

使用docker logs id指令能夠查看容器的日誌,id僅需前幾位便可:

[eric@centos7min2 cspj-server]$ docker logs 47ed827
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 47ed8277b0e0
172.17.0.2 cspj-host
-Djava.rmi.server.hostname=cspj-host -Dcom.sun.management.jmxremote.port=9998 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

Starting CSPJ Server...
2019-09-29_10:35:33.263[0000]CSPJ Server Process ID:10
2019-09-29_10:35:33.275[0000]CSPJ Server Version:CSPJ_V1.3.7.1 Build:2019-08-03 10:54:51
2019-09-29_10:35:33.276[0000]系統日誌初始化成功[/cspj-server/log/syslog.trace]
2019-09-29_10:35:33.277[0000]平臺主目錄:/cspj-server
2019-09-29_10:35:33.277[0000]平臺配置信息主目錄:/cspj-server/conf
...
2019-09-29_10:35:37.587[0000]終端[RMI Registry]監聽端口[27449]數據端口[27450]
2019-09-29_10:35:37.587[0000]終端守護進程啓動成功
2019-09-29_10:35:37.587[0000]初始化交易主控入口……
2019-09-29_10:35:37.589[0000]交易主控入口初始化成功[DefaultTransactionInvoker]
2019-09-29_10:35:37.589[0000]啓動通信口岸……
2019-09-29_10:35:37.599[0000]啓動通信口岸:SocketPortal[NIO]
2019-09-29_10:35:37.599[0000]通信口岸啓動成功
2019-09-29_10:35:37.649[0000]CSPJ Server 啓動成功

使用命令docker ps能夠查看正在運行的容器:

[eric@centos7min2 cspj-server]$ docker ps
CONTAINER ID        IMAGE                       COMMAND              CREATED             STATUS              PORTS                                  NAMES
47ed8277b0e0        ws3495/cspj-server:v1.0.0   "./startServer.sh"   53 minutes ago      Up 53 minutes       0.0.0.0:27449-27450->27449-27450/tcp   jolly_shockley

對於這個運行中的容器,可使用命令docker exec -it id sh,進入這個容器進行查看和修改:

[eric@centos7min2 cspj-server]$ docker exec -it 47ed827 sh
/cspj-server/bin # ls
SecInputKey.sh      derby.log           jmxremote.password  serverStatus.sh     startServer.sh      transform.sh
codeinfo.sh         ij.sh               pwdgen.sh           setEnv.sh           stopServer.sh
/cspj-server/bin # cd ../log
/cspj-server/log # ls
error.trace   root.trace    syslog.trace  trace
/cspj-server/bin #

此時就進入到了容器裏,在裏面對容器中的內容進行修改,就會寫入到容器的可讀寫層(應該是最上層)。以後再執行docker restart <id>時,這個容器的修改不會消失。只有在使用docker rm <id>命令刪除這個容器時,全部臨時存儲的文件就會消失。或者再使用docker run ... ws3495/cspj-server:v1.0.0命令運行一個新容器時,這個容器中所做的修改也不會被新容器知道。

5.2 從外部RMI鏈接到容器的27449端口

在咱們的windows 10系統上,修改C:\Windows\System32\drivers\etc\hosts文件,添加一行192.168.56.104 cspj-host,其中192.168.56.104是CentOS-7虛擬機的IP。就可使用IDE(RMI鏈接的客戶端工具)鏈接了:

ide鏈接cspj-server

其原理是:

如今這個集羣共有3個IP地址:

  • IP1: 是windows 10操做系統所在的地址,也是RMI客戶端所在的地址
  • IP2: 192.168.56.104,是CentOS-7系統(在win10中運行的VirtualBox虛擬機中)所在的地址
  • IP3: 是docker容器運行的地址,即RMI服務端所在的地址

因爲docker啓動時設置了-p 27449:27449 -p 27450:27450參數,全部發送到IP2:27449和IP2:27450的信息都會被轉發到IP3:27449和IP3:27450。這兩個端口是咱們設置的RMI提供服務的端口。

在docker容器中啓動的java應用(RMI服務端,IP3)在啓動時設置了-Djava.rmi.server.hostname=cspj-host選項,當客戶端使用rmi方式鏈接到docker容器中的進程時,容器中的進程會向客戶端返回一個本機的cspj-host參數標定服務端所在的地址(已在docker中的/etc/hosts中設置了IP3 cspj-host(參看5.1節中的docker logs指令的輸出結果))。客戶端會從本機的hosts文件中查找cspj-host所在的地址。

客戶端須要以RMI方式鏈接到IP3上時,須要經過IP2進行中轉,因此在RMI客戶端所在的機器上,須要在hosts文件中設置IP2 cspj-host,使客戶端去IP2:27449獲取RMI服務。又因爲IP2會把全部27449端口的數據包都轉發到IP3:27449,因此就會最終找到真正的RMI服務。

當有更多的IP對數據包進行轉發時,也是同樣的,客戶端須要設置hosts中cspj-host爲IP2所在的地址。

注意:鏈接過程當中可能會碰到NoSuchObject的異常,須要多試幾回。或者在IP2上啓動一個cspj,IDE鏈接上以後,再關閉IP2上的cspj,而後再試

6. 修改數據文件

6.1 使用commit生成新的鏡像

經過5.2節圖中的界面,咱們能夠修改Derby數據庫文件。主要修改內容是在容器中開放18000端口,全部向這個端口發送的數據,都會收到一個返回信息,信息中標明這個容器的IP地址。修改以後,使用docker commit <id> ws3495/cspj-server:tmp命令,把這個容器存爲一個新鏡像ws3495/cspj-server:tmp

[eric@centos7min2 cspj-server]$ docker commit 47ed8277b0e0 ws3495/cspj-server:tmp
sha256:ae4f6b8ecf435b714c331372d93c96bbb56460469bb9dd06e4e0f93faa8659a8
[eric@centos7min2 cspj-server]$ docker image ls
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
ws3495/cspj-server        tmp                 ae4f6b8ecf43        9 seconds ago       141MB
ws3495/cspj-server        v1.0.0              4e56f3b72f1d        About an hour ago   137MB
ws3495/cspj-server        v1.0.1              e5868fe7c123        5 hours ago         132MB
openjdk                   8-alpine            a3562aa0b991        4 months ago        105MB
registry                  latest              f32a97de94e1        6 months ago        25.8MB
hello-world               latest              fce289e99eb9        9 months ago        1.84kB
prakhar1989/static-site   latest              f01030e1dcf3        3 years ago         134MB

能夠看出,commit生成的鏡像tmp比原始鏡像v1.0.0鏡像大了很多。

使用命令docker stop命令停掉如今這個容器,再使用docker ps -a命令,能夠看到其狀態爲Exited(stop的容器可使用docker start命令再啓動,其修改不會丟失):

[eric@centos7min2 cspj-server]$ docker stop 47ed8277b0e0
47ed8277b0e0
[eric@centos7min2 cspj-server]$ docker ps -a
CONTAINER ID        IMAGE                       COMMAND              CREATED             STATUS                        PORTS               NAMES
47ed8277b0e0        ws3495/cspj-server:v1.0.0   "./startServer.sh"   58 minutes ago      Exited (137) 31 seconds ago                       jolly_shockley

咱們根據剛纔commit的鏡像,啓動一個新容器,此次把18000端口也映射到宿主機:

[eric@centos7min2 cspj-server]$ docker run -d \
>   -p 27449:27449 -p 27450:27450 -p 18000:18000 \
>   ws3495/cspj-server:tmp
f211f0844f78dc38460260c34b7e9cb7f9ae8245c7d246e35e7be7cf4d3ba23c

新運行的容器和剛纔那個容器的ID是不同的。使用IDE鏈接到這個新容器上,能夠看到剛纔在那個容器中所做的修改,這裏都存在。

在windows 10上,使用telnet鏈接到CentOS-7的18000端口,發送一段數據,能夠看到返回信息:

response info

其中的ip addr is 172.17.0.2即容器中java應用返回信息。

6.2 更好的方法

v1.0.0鏡像啓動了一個容器47ed8277b0e0,可不能夠在這個運行的容器上再用相似-p的參數映射出一個端口呢?根據How do I assign a port mapping to an existing Docker container?這個答案,須要修改docker守護進程的配置文件,不是一個很好的解決方案。

commit會使鏡像不斷增大。可使用bind mounts技術,在docker run命令中,使用--mount type=bind,source=<host dir>,target=/cspj-server/database參數,把宿主機上的一個文件夾掛載到容器中。咱們的java應用全部數據庫修改都是修改/cspj-server/database目錄,這樣對數據庫的修改就能夠保存下來,即便容器被刪除了,對數據庫的修改也不會消失。不過要注意,<host dir>中因該包含咱們java應用中database目錄所需的一些基礎文件:

[eric@centos7min2 cspj-server]$ ll database/
total 20
drwxrwxr-x. 2 eric eric   97 Sep 29 14:03 log
-rw-rw-r--. 1 eric eric  608 Sep 29 14:03 README_DO_NOT_TOUCH_FILES.txt
drwxrwxr-x. 2 eric eric 8192 Sep 29 14:03 seg0
-rw-rw-r--. 1 eric eric 1003 Sep 29 14:03 service.properties

或者使用外置的數據庫,不要和java應用集成在一塊兒。例如鏈接到外部的oracle數據庫;或者啓動一個mySQL的docker container,使用docker-compose工具把他們關聯到一塊兒。詳情請參看「容器集羣」一節

7. 私有倉庫registry

若是沒法鏈接Docker Hub,咱們能夠搭建私有倉庫。使用官方鏡像registry便可搭建:

# 搭建本地的registry,默認在/etc/lib/registry中
[eric@centos7min2 cspj-server]$ docker run -d -p 5000:5000 --restart=always --name registry registry
821497a1688646027389b8c3547ab3e321e8df1c1fa442987506b3b5784de52e

# 查看本地registry上存在的鏡像
[eric@centos7min2 cspj-server]$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":[]}

# 使用docker tag標記一個到127.0.0.1:5000的鏡像
[eric@centos7min2 cspj-server]$ docker tag ws3495/cspj-server:tmp 127.0.0.1:5000/ws3495/cspj-server:tmp
[eric@centos7min2 cspj-server]$ docker image ls
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
127.0.0.1:5000/ws3495/cspj-server   tmp                 ae4f6b8ecf43        About an hour ago   141MB
ws3495/cspj-server                  tmp                 ae4f6b8ecf43        About an hour ago   141MB
ws3495/cspj-server                  v1.0.0              4e56f3b72f1d        3 hours ago         137MB
ws3495/cspj-server                  v1.0.1              e5868fe7c123        6 hours ago         132MB
openjdk                             8-alpine            a3562aa0b991        4 months ago        105MB
registry                            latest              f32a97de94e1        6 months ago        25.8MB
hello-world                         latest              fce289e99eb9        9 months ago        1.84kB
prakhar1989/static-site             latest              f01030e1dcf3        3 years ago         134MB

# 推送這個鏡像
[eric@centos7min2 cspj-server]$ docker push 127.0.0.1:5000/ws3495/cspj-server:tmp
The push refers to repository [127.0.0.1:5000/ws3495/cspj-server]
e907982060bf: Pushed 
4c70c37a74c9: Pushed 
ceaf9e1ebef5: Pushed 
9b9b7f3d56a0: Pushed 
f1b5933fe4b5: Pushed 
tmp: digest: sha256:588c372c92e3909fb280311d273be512dfc5641eb66e0514b9c90c9db314dcb4 size: 1369

# 查看結果
[eric@centos7min2 cspj-server]$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ws3495/cspj-server"]}

# 從新pull
[eric@centos7min2 cspj-server]$ docker image rm 127.0.0.1:5000/ws3495/cspj-server:tmp
Untagged: 127.0.0.1:5000/ws3495/cspj-server:tmp
Untagged: 127.0.0.1:5000/ws3495/cspj-server@sha256:588c372c92e3909fb280311d273be512dfc5641eb66e0514b9c90c9db314dcb4

[eric@centos7min2 cspj-server]$ docker pull 127.0.0.1:5000/ws3495/cspj-server:tmp
tmp: Pulling from ws3495/cspj-server
Digest: sha256:588c372c92e3909fb280311d273be512dfc5641eb66e0514b9c90c9db314dcb4
Status: Downloaded newer image for 127.0.0.1:5000/ws3495/cspj-server:tmp
127.0.0.1:5000/ws3495/cspj-server:tmp

[eric@centos7min2 cspj-server]$ docker image ls
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
ws3495/cspj-server                  tmp                 ae4f6b8ecf43        About an hour ago   141MB
127.0.0.1:5000/ws3495/cspj-server   tmp                 ae4f6b8ecf43        About an hour ago   141MB
ws3495/cspj-server                  v1.0.0              4e56f3b72f1d        3 hours ago         137MB
ws3495/cspj-server                  v1.0.1              e5868fe7c123        6 hours ago         132MB
openjdk                             8-alpine            a3562aa0b991        4 months ago        105MB
registry                            latest              f32a97de94e1        6 months ago        25.8MB
hello-world                         latest              fce289e99eb9        9 months ago        1.84kB
prakhar1989/static-site             latest              f01030e1dcf3        3 years ago         134MB

更多搭建私有倉庫的方法請參看私有倉庫

8. 容器集羣

一個container(容器)只作一件事情,一個container中只運行一個程序。若是咱們的應用由好幾個部分組成,應該如何把它們組織到一塊兒呢?好比一個應用能夠分爲server(邏輯處理)、db(數據庫操做)、monitor(監控)等幾部分,通常會把這幾部分分別製做成鏡像,啓動到不一樣的container中。怎麼把它們組成一個完整的能夠對外提供服務的應用呢?若是須要多個應用,如何進行負載均衡呢?

這裏就要用到集羣管理。Docker自帶一個集羣管理工具Swarm(蜂羣),使用它能夠解決咱們剛纔提出的問題。

使用Swarm,先要明確與之相關的一些概念:

  • node: swarm集羣中每個運行了docker服務的機器(虛擬機或物理機),都被稱爲node。參看第3節
  • container: 這個咱們已經知道了,就是容器,是經過鏡像(image)運行起來的。swarm以外運行的container隨時能夠加入到swarm中,成爲一個task
  • task: 簡單來講,在swarm集羣中,每一個運行中的container就是一個task
  • service: 一個應用的不一樣部分被稱爲service。service中能夠有多個task正在運行
  • stack: 一些相互關聯的service一塊兒對外提供服務,就構成了一個stack。例如頁面service和數據庫service組合在一塊兒才能讓用戶使用,這就是一個stack

docker命令中的nodeservicestack指令,都必須在swarm集羣環境下使用。

8.1 負載均衡

咱們前面建立了ws3495/cspj-server:tmp鏡像。它對外提供一個服務,對任意發送到18000端口的請求,返回一個容器的IP地址。IDE工具能夠用RMI方式鏈接到這個容器,對容器的數據進行操做。

如今須要實現對這個服務的負載均衡。咱們須要根據這個鏡像啓動多個容器,讓它們共同對外提供更加穩定可靠的服務。

8.1.1 建立compose文件

在任意位置建立一個compose文件 docker-compose.yml:

version: "3"
services:
  server:
    image: ws3495/cspj-server:tmp
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "0.5"
          memory: 1024M 
      restart_policy:
        condition: on-failure
    ports:
      # <host port> : <container port>
      - "27449:27449"
      - "27450:27450"
      - "18000:18000"
    networks:
      - cspjnet
networks:
  cspjnet:
  • services: services下的每一項都是一個service
  • server: 咱們把cspj程序提供的service叫作server
  • image: 根據哪一個鏡像啓動容器
  • replicas: 啓動2個容器。啓動後,每一個容器都是一個task,它們共同組成了一個service(名字叫作server)
  • limits: 限制每一個容器的CPU和內存使用率
  • restart_policy: 失敗後當即重啓
  • ports: 端口映射,左側是宿主機的端口,右側是容器的端口
  • networks: 爲這幾個容器新建的虛擬網絡(默認設置,即load-balanced overlay network)

8.1.2 初始化swarm

swarm中的節點(node)分爲manager和worker。咱們須要初始化一個manager,而後把其它節點做爲worker加入進來。這裏咱們只有一個機器(CentOS-7),因此咱們只建立一個manager節點。

執行命令docker swarm init --advertise-addr 192.168.56.104建立manager節點:

[eric@centos7min2 swarm]$ docker swarm init --advertise-addr 192.168.56.104
Swarm initialized: current node (eby778btfq9hvpzn62gnnv5ux) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4famuxyeqheaiiip2rp2vyk7fuq5v4csvn4432w2czbm7ctov2-6yjw4idxlrdy4vjbknd4d1gdg 192.168.56.104:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

這個命令會建立幾個network,用於集羣管理。ingress和none就是swarm在這個節點上建立的,docker_gwbridge可能也是:

[eric@centos7min2 swarm]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
015532cb540e        bridge              bridge              local
a4025c27d82d        docker_gwbridge     bridge              local
20ce1819213b        host                host                local
5fnz5sfhkka7        ingress             overlay             swarm
ec1e8f535e07        none                null                local

能夠執行一些檢查,看一看這個節點如今的狀態:

# swarm中只有一個節點,而且是manager
[eric@centos7min2 swarm]$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
eby778btfq9hvpzn62gnnv5ux *   centos7min2         Ready               Active              Leader              19.03.2

# 尚未stack
[eric@centos7min2 swarm]$ docker stack ls
NAME                SERVICES            ORCHESTRATOR

# 也沒有service
[eric@centos7min2 swarm]$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS

8.1.3 部署並啓動應用

執行命令docker stack deploy -c docker-compose.yml cspj,swarm就能夠根據docker-compose.yml文件中的配置,自動部署並啓動應用。咱們給這個stack命名爲cspj。

# 部署應用。會根據yml文件中的設置,建立虛擬網絡cspj_cspjnet,使用ws3495/cspj-server:tmp鏡像啓動一個服務cspj_server
[eric@centos7min2 swarm]$ docker stack deploy -c docker-compose.yml cspj
Creating network cspj_cspjnet
Creating service cspj_server

# swarm又新建了一個cspj_cspjnet的虛擬網絡
[eric@centos7min2 swarm]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
015532cb540e        bridge              bridge              local
d5a7fdih7h92        cspj_cspjnet        overlay             swarm
a4025c27d82d        docker_gwbridge     bridge              local
20ce1819213b        host                host                local
5fnz5sfhkka7        ingress             overlay             swarm
ec1e8f535e07        none                null                local

# 查看全部stack。如今只有1個,名字叫cspj,裏面有1個service
[eric@centos7min2 swarm]$ docker stack ls
NAME                SERVICES            ORCHESTRATOR
cspj                1                   Swarm

# 查看全部service。輸出信息代表cspj_server這個service中有兩個task(REPLICAS),而且都已經啓動了
[eric@centos7min2 swarm]$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                    PORTS
uq82928el14d        cspj_server         replicated          2/2                 ws3495/cspj-server:tmp   *:18000->18000/tcp, *:27449-27450->27449-27450/tcp

# 查看cspj_server這個service中的task。swarm自動爲每一個task進行了編號
[eric@centos7min2 swarm]$ docker service ps cspj_server
ID                  NAME                IMAGE                    NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
j7kjtj81re1o        cspj_server.1       ws3495/cspj-server:tmp   centos7min2         Running             Running 5 minutes ago                       
1elk64sfezfo        cspj_server.2       ws3495/cspj-server:tmp   centos7min2         Running             Running 5 minutes ago                       

# 按照普通方式查看container,發現ID和service ps中顯示的ID不同。爲何?
[eric@centos7min2 swarm]$ docker ps 
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                    NAMES
8d9f67c15cb6        ws3495/cspj-server:tmp   "./startServer.sh"       21 minutes ago      Up 21 minutes       27449-27450/tcp          cspj_server.2.1elk64sfezfoql0ex1njwjlnp
dee88b746baf        ws3495/cspj-server:tmp   "./startServer.sh"       21 minutes ago      Up 21 minutes       27449-27450/tcp          cspj_server.1.j7kjtj81re1o1ge5anwnlmzka
821497a16886        registry                 "/entrypoint.sh /etc…"   24 hours ago        Up 24 hours         0.0.0.0:5000->5000/tcp   registry

在Windows 10上,向CentOS-7的18000端口發送數據,能夠看到負載均衡的效果:

send message and receive ip

IDE也能夠正常鏈接。

8.1.4 關閉應用並恢復

使用docker stack rm cspj關閉並移除cspj這個task

# 關閉stack
[eric@centos7min2 swarm]$ docker stack rm cspj
Removing service cspj_server
Removing network cspj_cspjnet

# stack已被移除
[eric@centos7min2 swarm]$ docker stack ls
NAME                SERVICES            ORCHESTRATOR

# service已被移除
[eric@centos7min2 swarm]$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS

# cspj_cspjnet已被移除
[eric@centos7min2 swarm]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
015532cb540e        bridge              bridge              local
a4025c27d82d        docker_gwbridge     bridge              local
20ce1819213b        host                host                local
5fnz5sfhkka7        ingress             overlay             swarm
ec1e8f535e07        none                null                local

# task已被關閉並移除
[eric@centos7min2 swarm]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
821497a16886        registry            "/entrypoint.sh /etc…"   24 hours ago        Up 24 hours         0.0.0.0:5000->5000/tcp   registry

使用命令docker swarm leave --force移除swarm集羣(即把最後一個node從swarm集羣中移除掉)。以後,docker node, docker stack, docker service這些命令就不可用了:

# 關閉swarm
[eric@centos7min2 compose]$ docker swarm leave --force
Node left the swarm.

8.2 多個node多個servcie

本節使用docker-machine工具建立多個虛擬docker node,並把它們都用swarm組織成一個集羣。同時,咱們再在docker-compose.yml文件中添加一個service,使多個service共同工做。操做平臺是CentOS-7。docker-machine能夠徹底獨立使用,沒必要同時安裝docker,docker-machine建立的虛擬機中就有docker服務。

因爲docker-machine建立虛擬機須要先安裝virtualbox,而目前版本的virtualbox(6.0)僅能在AMD的CPU上支持嵌套的虛擬機,因此在本節中咱們使用vmware workstation pro 15(有30天免費試用期,或者使用vmware workstation palyer),在這個虛擬機上安裝CentOS-7,而後再在CentOS-7上安裝docker-machine,docker-machine再建立基於virtualbox的虛擬機。

新搭建的CentOS-7系統的IP地址是192.168.154.100.

8.2.1 安裝docker-machine

docker-machine能夠快速部署帶有docker服務的虛擬機。

在CentOS-7上使用docker-machine須要先安裝virtual-box。新建/etc/yum.repos.d/virtualbox.repo文件,內容以下:

[virtualbox]
name=Oracle Linux / RHEL / CentOS-$releasever / $basearch - VirtualBox
baseurl=http://download.virtualbox.org/virtualbox/rpm/el/$releasever/$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://www.virtualbox.org/download/oracle_vbox.asc

而後執行以下命令安裝virtualbox。安裝成功以後,執行sudo systemctl status vboxdrv,能夠查看virtualbox的狀態:

  • sudo yum update,會更新全部軟件,能夠不執行。若是執行,需在執行後重啓系統
  • yum install -y kernel-devel kernel-headers gcc make perl,以後完後可能須要重啓系統
  • sudo yum install VirtualBox-6.0
[eric@vmwmin1 ~]$ sudo systemctl status vboxdrv
● vboxdrv.service - VirtualBox Linux kernel module
   Loaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: disabled)
   Active: active (exited) since Wed 2019-10-02 00:23:22 CST; 23min ago
  Process: 822 ExecStart=/usr/lib/virtualbox/vboxdrv.sh start (code=exited, status=0/SUCCESS)

Oct 02 00:19:51 vmwmin1 systemd[1]: Starting VirtualBox Linux kernel module...
Oct 02 00:19:54 vmwmin1 vboxdrv.sh[822]: vboxdrv.sh: Starting VirtualBox services.
Oct 02 00:19:54 vmwmin1 vboxdrv.sh[855]: Starting VirtualBox services.
Oct 02 00:19:54 vmwmin1 vboxdrv.sh[822]: vboxdrv.sh: Building VirtualBox kernel modules.
Oct 02 00:19:54 vmwmin1 vboxdrv.sh[860]: Building VirtualBox kernel modules.
Oct 02 00:23:22 vmwmin1 systemd[1]: Started VirtualBox Linux kernel module.

安裝docker-machine:

$ base=https://github.com/docker/machine/releases/download/v0.16.0 &&
  curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine &&
  sudo mv /tmp/docker-machine /usr/local/bin/docker-machine &&
  chmod +x /usr/local/bin/docker-machine

安裝成功後,執行docker-machine ls,能夠看到還不存在由docker-machine建立的虛擬機。

8.2.2 建立虛擬機(兩個node)

使用docker-machine create --driver virtualbox <vm-name>能夠直接建立帶有docker服務的虛擬機,沒必要事先安裝docker。這裏咱們建立兩個虛擬機,分別爲myvm1myvm2:

# 建立myvm1,因爲是第一次執行,會從github上下載一些文件
[eric@vmwmin1 ~]$ docker-machine create --driver virtualbox myvm1
Running pre-create checks...
(myvm1) Image cache directory does not exist, creating it at /home/eric/.docker/machine/cache...
(myvm1) No default Boot2Docker ISO found locally, downloading the latest release...
(myvm1) Latest release for github.com/boot2docker/boot2docker is v18.09.9
(myvm1) Downloading /home/eric/.docker/machine/cache/boot2docker.iso from https://github.com/boot2docker/boot2docker/releases/download/v18.09.9/boot2docker.iso...
(myvm1) 0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Creating machine...
(myvm1) Copying /home/eric/.docker/machine/cache/boot2docker.iso to /home/eric/.docker/machine/machines/myvm1/boot2docker.iso...
(myvm1) Creating VirtualBox VM...
(myvm1) Creating SSH key...
(myvm1) Starting the VM...
(myvm1) Check network to re-create if needed...
(myvm1) Found a new host-only adapter: "vboxnet0"
(myvm1) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env myvm1

# 建立myvm2,省略了一些輸出內容
[eric@vmwmin1 ~]$ docker-machine create --driver virtualbox myvm2
...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env myvm2

# 查看這兩個虛擬機
[eric@vmwmin1 ~]$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v18.09.9   
myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v18.09.9

8.2.3 配置http方式訪問私有倉庫

本文開始時咱們使用的CentOS-7系統的地址是192.168.56.104,咱們在這個CentOS-7系統上使用docker搭建個了一個私有倉庫。如今咱們又使用vmware新搭建了一個CentOS-7系統,其地址是192.168.154.100,在這個系統上使用docker-machine建立了兩個虛擬機myvm1和myvm2。咱們須要使myvm1和myvm2能夠訪問這個私有倉庫,因此須要對myvm1和myvm2進行一些配置,使其能夠以不安全的方式訪問私有倉庫。

經過執行命令docker-machine scp <filename> <your-machine-name>:<path>會把文件拷貝到對應的虛擬機中。

經過執行命令docker-machine ssh <your-machine-name> "<your-docker-command>"能夠直接在虛擬機中執行命令。若是省略""中的內容,就能夠以ssh方式鏈接到虛擬機中。

在docker-machine所在的CentOS-7系統上,在任意位置新建一個文件daemon.json,內容爲:

{
  "insecure-registries": [
    "192.168.56.104:5000"
  ]
}

192.168.56.104是私有倉庫所在的地址。

把這個文件拷貝到myvm1和myvm2的/etc/docker/目錄下:

# 拷貝文件
$ docker-machine scp daemon.json myvm1:~
$ docker-machine scp daemon.json myvm2:~
$ docker-machine ssh myvm1 "sudo mv daemon.json /etc/docker/"
$ docker-machine ssh myvm2 "sudo mv daemon.json /etc/docker/"

# 重啓
[eric@vmwmin1 ~]$ docker-machine restart myvm1 myvm2
Restarting "myvm2"...
Restarting "myvm1"...
(myvm2) Check network to re-create if needed...
(myvm2) Waiting for an IP...
Waiting for SSH to be available...
(myvm1) Check network to re-create if needed...
(myvm1) Waiting for an IP...
Waiting for SSH to be available...
Detecting the provisioner...
Detecting the provisioner...
Restarted machines may have new IP addresses. You may need to re-run the `docker-machine env` command.

# 查看重啓後狀態
[eric@vmwmin1 ~]$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
myvm1   -        virtualbox   Running   tcp://192.168.99.102:2376           v18.09.9   
myvm2   -        virtualbox   Running   tcp://192.168.99.103:2376           v18.09.9

配置好daemon.json以後,myvm1和myvm2就能夠以不安全的方式(HTTP)訪問私有倉庫了。

8.2.4 配置swarm集羣

使用swarm命令,把這兩個節點加入到swarm集羣中:

# 初始化myvm1,會自動設置myvm1爲manager
[eric@vmwmin1 ~]$ docker-machine ssh myvm1 "docker swarm init --advertise-addr 192.168.99.102"
Swarm initialized: current node (tjpk0hxlhij9v77yh30ehnzkg) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-11sad8xx5hp9tyt9oed3gdgzx9ma7lfkk2chm0l8hi3mc0we2s-0bjuixv1bpsvryjsizppfr7bz 192.168.99.102:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

# 根據上一個輸出的提示,把myvm2加入到swarm中
[eric@vmwmin1 ~]$ docker-machine ssh myvm2 "docker swarm join --token SWMTKN-1-11sad8xx5hp9tyt9oed3gdgzx9ma7lfkk2chm0l8hi3mc0we2s-0bjuixv1bpsvryjsizppfr7bz 192.168.99.102:2377"
This node joined a swarm as a worker.

# 查看swarm中的節點,*標記的myvm1是manager
[eric@vmwmin1 ~]$ docker-machine ssh myvm1 "docker node ls"
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
tjpk0hxlhij9v77yh30ehnzkg *   myvm1               Ready               Active              Leader              18.09.9
w46api0hvap58ghhe3spdd9i7     myvm2               Ready               Active                                  18.09.9

8.2.5 兩個service

新建一個docker-compose2.yml,其內容爲:

version: "3"
services:
  server:
    image: 192.168.56.104:5000/ws3495/cspj-server:tmp
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "0.5"
          memory: 1024M 
      restart_policy:
        condition: on-failure
    ports:
      # <host port> : <container port>
      - "27449:27449"
      - "27450:27450"
      - "18000:18000"
    networks:
      - cspjnet
  visualizer:
    image: 192.168.56.104:5000/dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
    networks:
      - cspjnet
networks:
  cspjnet:

這個compose文件中有兩個service,一個是咱們的java應用server;另外一個是visualizer,這是一個能夠經過瀏覽器觀察swarm節點狀態的鏡像。咱們已經提早把它們push到私有倉庫了(visualizer鏡像也能夠直接從Docker Hub中獲取)。

把這個compose文件拷貝到myvm1上(必須是manager節點,不能是worker節點),就能夠部署了:

# 把docker-compose2.yml拷貝到myvm1的~目錄下
[eric@vmwmin1 ~]$ docker-machine scp docker-compose2.yml myvm1:~
docker-compose2.yml                             100%  693   405.5KB/s   00:00    

# 向myvm1虛擬機發送指令,進行service部署
[eric@vmwmin1 ~]$ docker-machine ssh myvm1 "docker stack deploy -c docker-compose2.yml cspj"
Creating network cspj_cspjnet
Creating service cspj_server
Creating service cspj_visualizer

# 查看新部署的stack
[eric@vmwmin1 ~]$ docker-machine ssh myvm1 "docker stack ls"
NAME                SERVICES            ORCHESTRATOR
cspj                2                   Swarm

# 查看新部署的service,能夠看到如今是部署並啓動了2個service,其中cspj_server啓動了2份,cspj_visualizer啓動了1份
[eric@vmwmin1 ~]$ docker-machine ssh myvm1 "docker service ls"
ID                  NAME                MODE                REPLICAS            IMAGE                                                 PORTS
lo86uqnj2unu        cspj_server         replicated          2/2                 192.168.56.104:5000/ws3495/cspj-server:tmp            *:18000->18000/tcp, *:27449-27450->27449-27450/tcp
89d5hype8ert        cspj_visualizer     replicated          1/1                 192.168.56.104:5000/dockersamples/visualizer:stable   *:8080->8080/tcp

# CentOS-7系統上(192.168.154.100),全部發送到8080端口的數據包都會被轉發到myvm1(192.168.99.102)的8080端口
[eric@vmwmin1 ~]$ sudo firewall-cmd --list-forward-ports
port=8080:proto=tcp:toport=:toaddr=192.168.99.102
port=18000:proto=tcp:toport=:toaddr=192.168.99.102
port=27449:proto=tcp:toport=:toaddr=192.168.99.102
port=27450:proto=tcp:toport=:toaddr=192.168.99.102

使用瀏覽器鏈接http://192.168.154.100:8080/,能夠看到swarm中節點和service的狀態:

visualizer

8.2.6 清理

經過使用命令docker-machine ssh myvm1 "docker stack rm cspj"關閉並刪除cspj這個stack,會同時中止並刪除server和visualizer這兩個service,會同時中止並刪除cspj_server.1, cspj_server.2, cspj_visualizer這3個docker容器。

經過使用命令docker-machine stop myvm1 myvm2docker-machine rm myvm1 myvm2中止並刪除這兩個虛擬機。

8.2.7 簡化指令

執行docker-machine env myvm1,按照其輸出結果的提示,執行eval $(docker-machine env myvm1),能夠把myvm1設置爲active。此時能夠在CentOS-7系統上直接執行docker命令即會向myvm1發送執行,而沒必要經過docker-machine ssh myvm1 "<command>"向myvm1發送指令了。有興趣能夠自行嘗試。

9. 總結

docker爲應用部署提供了極大方便,鏡像設置好以後,能夠在任何地方快速部署,保證同樣的執行效果。docker的鏡像儘可能把每一個功能拆分出來,使多個鏡像組成stack共同對外提供服務。若是須要數據持久化,可使用volume功能把數據存儲在宿主機上。volume功能也能夠在多個service之間共享存儲數據。

docker-machine提供了便捷搭建虛擬機,便捷管理虛擬機的能力,使得集羣的管理更加方便。

docker中還有不少地方值得探索,好比如何使用config設置配置文件,如何搭建HTTPS方式的私有倉庫,kubernetes和swarm的比較,docker的底層工做機制是如何實現的。後續會進一步對這些內容進行分析。

參考文檔

相關文章
相關標籤/搜索