Docker進階(含springcloud的docker-compose的簡單實例)

Docker進階

一. 數據卷(volume)

1.1 爲什麼需要數據卷

​ docker鏡像是由多個文件系統(只讀層)疊加而成,當我們啓動一個容器的時候,docker的服務端會加載鏡像的只讀層,並在最頂層創建一個可讀寫層。當運行的容器修改現有的文件,該文件會從只讀層拷貝到讀寫層,其實並沒有影響到鏡像本身,依然存在於鏡像中。當我們刪除掉容器,容器運行時的數據都會丟失,當我們通過鏡像重新run一個容器,該容器還是會回到最初的狀態。那麼問題來了,我們該如何保存我們運行中的數據了?這個問題有兩種解決方法:a. 我們可以定期的將我們的容器通過commit的方式生成一個鏡像;b.通過數據捲來實現。很明顯我們不可能不停的commit來生成鏡像,有些朋友可能會說你這麼說的意思是通過數據捲來實現唄,可我都不知道什麼是數據卷,怎麼知道它好用了?那麼接下來我們就開始來介紹數據卷。

1.2 什麼是數據卷

​ 數據卷就是容器內部的數據目錄直接映射到宿主機上的目錄上,無論在宿主機還是容器內對數據的修改在另外一方都是可見的。

1.3 數據卷的使用

1.3.1 命令的使用

​通過前面篇章中使用的centos鏡像來講解,其實也就一個命令,我們的工作也就是圍繞着該命令來講解:docker run -it -v /dataVolumn:/containerDataVolumn centos

在這裏插入圖片描述

在這裏插入圖片描述

​ 至於其他的命令在這裏不作過多的解釋,只解釋一下 -v /dataVolumn:/containerDataVolumn 這個命令的意思,-v是綁定將容器中的目錄掛載到宿主機的目錄上,/dataVolumn:/containerDataVolumn中冒號前的路徑是指自動在宿主機上創建的目錄名(不用我們手動去創建),冒號後的路徑是指在容器中自動創建的目錄名。

1.3.2數據卷的創建與測試
我們前面提到過“論在宿主機還是容器內對數據的修改在另外一方都是可見的”,那麼本小節我們回來測試這個問題。

A.我們在容器中 /containerDataVolumn 目錄下創建一個container.txt文件,並寫入內容,命令:echo “hello world” > container.txt

在這裏插入圖片描述

​ 在宿主機的 /dataVolumn目錄下會查看到有container.txt文件,並查看內容,如下圖所示:

[外鏈圖片轉存失敗(img-8JF8QWq8-1564634033999)(high-level\4.png)]

B.在宿主機的 /dataVolumn目錄下新建 host.txt文件,並寫入內容,命令爲:echo “welcome” > host.txt

[外鏈圖片轉存失敗(img-gTIvrn91-1564634034000)(high-level\5.png)]

​ 在容器的/containerDataVolumn目錄下會看到host.txt文件,並查看內容,如下圖所示:

[外鏈圖片轉存失敗(img-VmQhxEsr-1564634034001)(high-level\6.png)]

C.刪除掉容器,查看宿主機 /dataVolumn目錄,文件並沒有丟失

[外鏈圖片轉存失敗(img-nESWjJZW-1564634034001)(high-level\7.png)]

D.我們可以通過 docker inspect 容器ID 命令查看容器的信息,其中hostConfig.binds可以查看到綁定信息,如下圖所示:

[外鏈圖片轉存失敗(img-vFSGlhmZ-1564634034002)(high-level\8.png)]

1.3.3 數據卷的其他創建方式

​ 通過上面的方式創建數據卷的時候,我們每次在運行鏡像的時候都需要去指定宿主機目錄和容器目錄,不便於維護與遷移,給大家舉個例子:例如我們的日誌文件是存放在容器中的 /cloud-project/logs目錄下,而且在項目的配置文件中也是指定到該目錄下,對應到我們的宿主機是/mycloud-project/logs目錄,如果說由於項目發佈啓動的時候,運維人員寫錯了目錄名,那將是很大的問題。所以我們在生成鏡像文件的時候就指定數據卷的目錄豈不是更好。

具體操作是,我們根據Dockerfile目錄中通過VOLUMN指定數據卷的位置,至於什麼是Dockerfile,在後面的篇章中會講解。

A. 新建一個空的目錄:mkdir my-dockerfile

B. 新建Dockerfile文件

[外鏈圖片轉存失敗(img-TLE2cRtd-1564634034003)(high-level\9.png)]

C.在Dockerfile中添加如下內容:

FROM centos
VOLUMN ["/containerDataVolumn"]
CMD /bin/bash

D.執行命令 docker build -t mycentos:me . 生成名爲mycentos,tag爲me的新的鏡像文件。注意:最後的一個點不能省略,它不是結束的句號(我在這裏栽了很大的跟頭)!!!

[外鏈圖片轉存失敗(img-SJVsvLI8-1564634034004)(high-level\10.png)]

E. 根據mycentos:me這個鏡像啓動一個容器,觀察根目錄下會生成 containerDataVolumn文件夾,如下圖所示:

[外鏈圖片轉存失敗(img-moMugS75-1564634034005)(high-level\11.png)]

F. 那麼如何查看,容器中的數據卷目錄對應的宿主機的目錄呢?上一小節我們講過,可以通過 docker inspect 容器ID 命令查看,結果如下圖所示:

[外鏈圖片轉存失敗(img-69Il5eMg-1564634034006)(high-level\12.png)]


二. Dockerfile

2.1 Dockerfile是什麼

​ Dockerfile是docker中鏡像文件的的描述文件,說的直白點就是鏡像文件到底是由什麼東西一步步構成的。例如我們在淘寶上買了一件商品,但是這個商品需要組裝才能使用,於是賣家就給了你一張圖紙,你就按照圖紙一步一步的組裝起來,然後就成了你所需要的樣子。那麼Dockerfile就是這張圖紙,鏡像文件就是你需要的商品。Dockerfile名字可以隨便命名,但是不建議你這做,還是按照規範來使用,首字母要大寫。下面給出我們前幾個章節使用到的ubuntu爲例,來看看它的Dockerfile是怎麼樣的,如下圖所示:


在這裏插入圖片描述

2.2 Dockerfile、鏡像、容器

​ Dockerfile:是鏡像的構建文件,描述的鏡像是一步步怎麼來的。

鏡像:是通過Dockerfile做出來的,包含操作系統基礎文件和軟件運行環境,它使用分層的存儲方式。

容器:是運行起來的鏡像,簡單理解,Docker鏡像相當於程序,容器相當於進程。

2.3 Dockerfile的基本語法及執行流程

2.3.1 Dockerfile的基本語法

​ a.每個保留字必須放在每一行的開頭,可以大寫也可以小寫,但是建議大寫;

b.指令按照順序,從上往下依次執行;

c.#表示註解;

d.每條執行指令都會創建一個新的鏡像,並且執行提交。

2.3.2 Dockerfile的執行流程

​ a.docker從基礎鏡像中執行一個容器;

b.執行一條指令對鏡像進行修改;

c.執行docker commit命令,提交新的鏡像;

d.在基於剛剛提交的鏡像運行一個新的容器;

e.執行Dockerfile中的下一條指令,按照b、c、d依次循環下去,知道所有的指令執行完畢。

2.3.3 演示講解
FROM centos                              #指定要生成的鏡像的基礎鏡像,開頭第一句話必須也只能是FROM
MAINTAINER  [email protected]         #指定作者是誰
RUN yum install -y vim                   #執行 yum install -y vim 命令,安裝vim
RUN yum install -y net-tools             #執行 yum install -y net-tools, 安裝net-tools工具
WORKDIR /dev/                            #啓動容器後,如果啓動交互模式,直接進入到哪個目錄
CMD ["/bin/bash"]                        #啓動容器的時候,進入到/bin/bash這種命令行

​ 如上代碼所示,FROM、MAINTAINER、RUN、WORKDIR、CMD均爲關鍵字,按照標準的規範需要大寫;FROM centos表示我們的基礎鏡像是centos,然後會啓動centos這個容器,執行第二行代碼又會生成新的鏡像文件,然後提交,周而復始。

2.4 Dockerfile關鍵字

關鍵字 作用
FROM 指定基礎鏡像
MAINTAINER 作者的信息
RUN 執行什麼命令
EXPOSE 容器對外暴露的端口
WORKDIR 進入到容器後進入到哪個目錄
ENV 配置環境變量
ADD 將文件拷貝到鏡像中並解壓
COPY 將文件拷貝到鏡像中
VOLUME 配置數據卷
CMD 容器啓動時候執行的命令
ENTRYPOINT 容器啓動時候執行的命令

A.ADD指令,我們現在定義這樣一個Dockerfile,代碼如下所示:

FROM ubuntu:18.04
MAINTAINER  [email protected]
RUN mkdir /datas
ADD jdk-8u60-linux-x64.tar.gz /datas/
WORKDIR /datas/
CMD ["/bin/bash"]

首先必須將jdk-8u60-linux-x64.tar.gz文件拷貝到Dockerfile同級目錄下,如下圖所示:

[外鏈圖片轉存失敗(img-Z2iimRP6-1564634034006)(high-level\15.png)]

執行命令:docker build -t myubuntu . 構建myubuntu鏡像,如下圖所示:

[外鏈圖片轉存失敗(img-7dbLxqI1-1564634034007)(high-level\16.png)]

然後啓動容器,進入到 /datas/目錄下,會發現 jdk1.8.0_60 目錄,表示add命令還執行了解壓命令,如下圖所示:

[外鏈圖片轉存失敗(img-dGbPIvuJ-1564634034008)(high-level\17.png)]

B.COPY命令,我們定義一個Dockerfile, 代碼如下:

FROM centos
MAINTAINER  [email protected]
RUN mkdir /datas
ADD jdk-8u60-linux-x64.tar.gz /datas/
WORKDIR /datas/
CMD ["/bin/bash"]

執行命令:docker build -t myubuntu . 構建myubuntu鏡像。

然後啓動容器,進入到 /datas/目錄下,會看到jdk-8u60-linux-x64.tar.gz文件,並沒有解壓,如下圖所示:

[外鏈圖片轉存失敗(img-Q5ytK1C5-1564634034009)(high-level\18.png)]

C.ENV命令,配置環境變量,我們還是用上面提到的jdk爲例,做過Java開發的朋友都知道,jdk需要配置環境變量,Dockerfile的內容如下所示:

FROM ubuntu                      
MAINTAINER  [email protected]   
RUN mkdir -p /datas/ 
ADD jdk-8u60-linux-x64.tar.gz /datas/
ENV JAVA_HOME=/datas/jdk1.8.0_60                    #配置JAVA_HOME
ENV PATH=$JAVA_HOME/bin:$PATH                       #配置PATH
CMD ["/bin/bash"]

​ 當我們容器啓動後,我們輸入:java -version命令,就能看到我們熟悉的內容了。

D.CMD關鍵字,在鏡像構建階段不執行,在容器啓動階段執行(而我們的RUN關鍵字定義的指令在容器構建階段就會執行,請記住它與CMD的區別)。如果一個Dockerfile中有多個CMD命令,後面的會覆蓋前面的,說白了只有最後一個生效,如下代碼和註釋:

............省略............
CMD echo "<<<<<<<<<<<nice to meet you>>>>>>>>>>"
CMD /bin/bash
CMD echo "==========How are you?============="    #當容器啓動的時候只有該行代碼會執行,會將前兩行代碼覆蓋

​ CMD還有一個問題,就是當我們使用 docker run命令的時候,我們可以在整個docker命令的最後加上其他額外的命令,那麼額外的命令會覆蓋Dockerfile中所有的CMD命令,例如我們執行如下命令:docker run -it centos ls / -l,最終的結果如下圖所示:

[外鏈圖片轉存失敗(img-EuORqWVd-1564634034009)(high-level\19.png)]

E. ENTRYPOINT指令,和CMD命令差不多,如果一個Dockerfile中有多個ENTRYPOINT,只有最後一個生效。但是他們還是有區別的,如果ENTRYPOINT後面有CMD,當以exec的方式運行的時候,CMD的值會作爲參數給ENTRYPOINT,可能很多人看到這句話不大理解,那麼我們來兩個例子:

例一:

FROM centos                      
MAINTAINER  [email protected]   
RUN mkdir -p /datas/ 
ENTRYPOINT ["echo", "hello"]
CMD ["world"]                           #會將world作爲echo hello的參數,最後的命令其實爲echo hello world 

[外鏈圖片轉存失敗(img-IubXDBmq-1564634034010)(high-level\20.png)]

例二:

FROM centos                      
MAINTAINER  [email protected]   
RUN mkdir -p /datas/ 
ENTRYPOINT ["echo", "hello"]

[外鏈圖片轉存失敗(img-8Yl9xLbB-1564634034011)(high-level\21.png)]

總結:當我們理解了CMD和ENTRYPOINT兩個命令的區別後,以後在使用的過程中就不會出現各種問題了。

三. docker-compose

​ 在開發一個應用的時候,我們需要其他的很多東西,例如數據庫,nginx,網站應用等很多的環境,而docker又推崇的是每一個容器只運行一個進程,那麼我們勢必得很多的容器,那麼我們得通過docker build命令一個個的構建鏡像,然後在通過docker run命令啓動一個個容器,那麼當我們修改了應用後,我們又得去重複上面的操作。而且容器與容器之間存在着很多的依賴,我們在操作的時候還得去考慮先啓動哪個容器,在啓動另外一個容器,這些操作和步驟都得花上大量的精力。那麼docker-compose的出現就是爲了解決這個問題。

​ docker-compose是一種容器編排技術,我們可以編寫一個docker-compose.yml文件,在文件中編排好我們的服務,只用通過一個命令即可搞定所有的工作。

3.1 安裝docker-compose

A. 運行如下命令獲取最新版的docker-compose

curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

B. 給二進制文件加上執行權限

chmod +x /usr/local/bin/docker-compose

如果docker-compose命令無效,可以給這個文件創建一個在 /usr/bin 目錄下的一個軟連接,如下所示:

ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

C. 驗證docker-compose是否安裝成功

docker-compose --version

3.2 docker-compose.yml的編寫

version: "3.7"

services:
  mysql: 
    image: mysql:5.7.26
    ports:
      - "3306:3306"
    environment: 
      - MYSQL_ROOT_PASSWORD=miller
    network_mode: "host"
    volumes:
      - "/datas/db:/var/lib/mysql"
      - "/docker-compose/mysql/my.cnf:/etc/my.cnf"
  eureka-server:
    build:
      context: ./eureka-server
      dockerfile: Dockerfile
    ports:
      - "8761:8761"
    network_mode: "host"
  provider:
    build:
      context: ./provider
      dockerfile: Dockerfile
    ports:
      - "6666:6666"
    network_mode: "host"
    depends_on:
      - "mysql"
    command: ["./wait-for-it.sh", "mysql:3306"]
  consumer:
    container_name: consumer
    build:
      context: ./consumer
    network_mode: "host"
    ports:
      - "8080:8080"

version

指定docker-compose文件的版本,對應的版本信息如下:

[外鏈圖片轉存失敗(img-7CtHuUhu-1564634034012)(C:\Users\Administrator\Desktop\課程資料\Docker\high-level\22.png)]

services

定義服務。

image

指定基礎鏡像。

ports

指定對外開放的端口。

environment

配置環境變量。

network_mode

網絡模式,默認爲bridge(橋接)模式。

volumes

指定數據卷。

context

要構建的鏡像的上下文,說白了就是相對於docker-compose.yml文件的位置。

dockerfile

指定Dockerfile文件的名字,如果名字爲Dockerfile的話,不用指定。

depends_on

指定容器所依賴的另外一個容器的服務名,但是並不會等待所依賴的容器啓動纔去啓動這個容器。

3.3 啓動

docker-compose up: 如果沒有構建過鏡像,首先會構建鏡像,然後啓動容器。

docker-compose up --build: 無論鏡像是否存在,首先會構建鏡像,然後啓動容器。

docker-compose start [service…]: 啓動已經存在的容器。

3.4 wait-for-it.sh使用

地址:https://github.com/vishnubob/wait-for-it

./wait-for-it.sh www.google.com:80 -- echo "google is up"

如果連接上谷歌的服務器,輸出“google is up”

四. Docker的工程化實踐

A. 創建spring-boot的工程。

​ 在主類上加上@SpringBootApplication, 來標註該類爲一個Spring-boot項目的啓動類。

B. 編寫代碼。

C. 將spring-boot項目打包後的jar包上傳到ubuntu的/spring-boot/目錄下。

D. 執行:docker run -it -v /spring-boot:/jarDir -p 8088:8080 mcr.microsoft.com/java/jre:8u192-zulu-alpine /bin/sh -c “java -jar /jarDir/spring-boot-1.0-SNAPSHOT.jar” 啓動我們的docker的jre容器,然後運行我們java程序。

E. docker run -it -e MYSQL_ROOT_PASSWORD=123456 -p 3305:3306 mysql:5.7.26,啓動mysql的容器。

F. 將mysql容器的數據通過數據卷的方式映射到宿主的目錄下。

五.springcloud 容器部署例子

1.Eureka Dockerfile
#基於哪個鏡像
FROM lwieske/java-8
#將本地文件夾掛載到當前容器
VOLUME  /tmp
ADD eureka-0.0.1-SNAPSHOT.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
#聲明暴露的端口
EXPOSE 8761
#配置容器啓動後執行的命令
  ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
2.Provider Dockerfile
#基於哪個鏡像
FROM lwieske/java-8
#將本地文件夾掛載到當前容器
VOLUME  /tmp
#賦值文件到容器
ADD my-cloud-provider-1.0-SNAPSHOT.jar app.jar
COPY wait-for-it.sh /
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
#聲明暴露的端口
EXPOSE 6002
#配置容器啓動後執行的命令
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
3.其它模塊類似
4.docker-compose.yml文件的編寫(容器編排)
version: "3.7"
services:
  eureka:
    build:
      context: ./eureka
    ports:
      - "8761:8761"
    network_mode: "host"
  provider:
    build:
      context: ./provider
    ports:
      - "6002:6002"
    network_mode: "host"
    depends_on:
      - "mysql"
    command: ["./wait-for-it.sh","mysql:3306"]
  mysql:
    image: mysql:5.7.26
    ports:
      - "3305:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    volumes:
      - "/mydir/mysqldate/mysql:/var/lib/mysql"
  consumer:
    build:
      context: ./consumer
    ports:
      - "8080:8080"
    network_mode: "host"

network_mode: “host”:讓不同容器間可以相互通信。

5.服務器結構圖如下

[外鏈圖片轉存失敗(img-pQh9S7Md-1564634034013)(/images/docker製作-文件目錄.png)]
​ (注:provider 目錄下還有一個wait-for-it.sh文件)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章