Docker基礎入門
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils # 注意:此處是大坑,可以自行換成阿里的源 sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo # 我們用阿里的 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 進行安裝(如果不換源,速度會很慢) sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls
一、安裝
環境:Centos7
1、解除安裝舊版本
較舊的Docker版本稱為docker
或docker-engine
。如果已安裝這些程式,請解除安裝它們以及相關的依賴項。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2、使用儲存庫安裝
注意:官方給了三種安裝方式,這裡我們選擇最常用的儲存庫安裝
在新主機上首次安裝Docker Engine之前,需要設定Docker儲存庫。之後可以從儲存庫安裝和更新Docker。
sudo yum install -y yum-utils
# 注意:此處是大坑,可以自行換成阿里的源
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 我們用阿里的
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 進行安裝(如果不換源,速度會很慢)
sudo yum install docker-ce docker-ce-cli containerd.io
3、啟動以及驗證
systemctl start docker # 啟動
systemctl status docker # 檢視狀態
docker version # 檢視版本
docker run hello-world # 測試
4、設定加速器(阿里雲)
注意:裡面的地址每個人都有專屬的地址。
開啟阿里雲官網->控制檯->搜尋容器映象服務
->右下角找到映象加速器
sudo mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://isch1uhg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker run hello-world
5、解除安裝
# 解除安裝Docker Engine,CLI和Containerd軟體包
sudo yum remove docker-ce docker-ce-cli containerd.io
# 主機上的映像,容器,卷或自定義配置檔案不會自動刪除。要刪除所有影象,容器和卷
sudo rm -rf /var/lib/docker
二、Docker三要素
1、倉庫(Repository)
倉庫:集中存放映象的場所。
注意:倉庫(Repository)和倉庫註冊伺服器(Registry)是有區別的,倉庫註冊伺服器往往存放著多個倉庫,每個倉庫中又包含多個映象,每個映象有不同的標籤(tag)
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub(https://hub.docker.com),存放了數量龐大的映象供使用者下載,國內的公開倉庫包括阿里雲、網易雲等。
2、映象(Image)
一個只讀模板,用來建立Docker容器,一個映象可以建立很多個容器。
容器與映象的關係類似於面向物件程式設計中的物件和類
Docker | 面向物件 |
---|---|
容器 | 物件 |
映象 | 類 |
3、容器 (Container)
獨立執行的一個或一組應用。
容器使用映象建立的執行例項。
它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的,保證安全的平臺。
可以把容器看作是一個簡易版的Linux環境(包括root使用者許可權,程序空間,使用者空間和網路空間等等)和執行在啟動的應用程式。
容器的定義和映象幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
三、簡單瞭解底層原理
1、Docker是怎麼工作的
Docker是一個Client-Server結構的系統,Docker守護程序執行在主機上,然後通過Socket連線從客戶端訪問,守護程序從客戶端接收命令並管理執行在主機上的容器。
2、Docker為什麼比VM快?
(1)Docker有著比虛擬機器更少的抽象層,由於Docker不需要Hypervisor實現硬體資源虛擬化,執行在Docker容器上的程式直接使用的都是實際物理機的硬體資源,因此在CPU、記憶體利用率上Docker將會在效率上有明顯的優勢。
(2)Docker利用的是宿主機的核心,而不需要Guest OS。因此當新建一個容器時,Docker不需要和虛擬機器一樣重疊載入一個作業系統核心,從而避免引尋、載入作業系統核心,返回比較費時費資源的過程。當新建一個虛擬機器時,虛擬機器軟體需要載入Guest OS,這個新建過程是分鐘級別的,而Docker由於直接利用宿主機的作業系統,因此新建一個Docker容器只需幾秒鐘。
Docker容器 | 虛擬機器(VM) | |
---|---|---|
作業系統 | 與宿主機共享OS | 宿主機OS上執行虛擬機器OS |
儲存大小 | 映象小,便於儲存與傳輸 | 映象龐大(vmdk,vdi等) |
執行效能 | 幾乎無額外效能損失 | 作業系統額外的CPU、記憶體消耗 |
移植性 | 輕便、靈活,適應於Linux | 笨重,與虛擬化技術耦合度高 |
硬體親和性 | 面向軟體開發者 | 面向硬體運維者 |
四、相關命令
1、幫助命令
docker version # 檢視docker版本資訊
docker info # 詳細說明
docker --help # 幫助命令
2、映象命令
(1)列出本地映象
docker images [OPTIONS] [REPOSITORY[:TAG]]
# OPTIONS說明:
-a: 列出本地所有的映象(含中間映像層)
-q: 只顯示映象ID
--digests: 顯示映象的摘要資訊
--no-trunc: 顯示完整的映象資訊
# 各個選項說明:
# REPOSITORY:表示映象的倉庫源
# TAG:映象的標籤
# IMAGE ID:映象ID
# CREATED:映象建立時間
# SIZE:映象大小
同一倉庫源可以有多個TAG,代表這個倉庫源的不同版本,我們使用REPOSITORY:TAG來定義不同的映象,如果不指定一個映象的版本標籤,例如我們只使用ubuntu,docker將預設使用ubuntu:latest映象。
(2)查詢映象
docker search [options] 某個xxx映象名字 # 會在https://hub.docker.com上去查詢
docker search mysql --filter=STARS=3000 # 搜尋星數大於等於3000的映象
# OPTIONS說明:
# --no-trunc: 顯示完整的映象描述
# -s:列出點贊數不小於指定值的映象
# --automated: 只列出automated build型別的映象
(3)獲取映象
docker pull 映象名字[:TAG] # 如果不寫TAG,則預設獲取latest,此時從我們配置的阿里雲上獲取映象
docker pull mysql # 獲取最新版本的mysql
Using default tag: latest
latest: Pulling from library/mysql
852e50cd189d: Pull complete # 分卷下載
29969ddb0ffb: Pull complete
a43f41a44c48: Pull complete
5cdd802543a3: Pull complete
b79b040de953: Pull complete
938c64119969: Pull complete
7689ec51a0d9: Pull complete
a880ba7c411f: Pull complete
984f656ec6ca: Pull complete
9f497bce458a: Pull complete
b9940f97694b: Pull complete
2f069358dc96: Pull complete
Digest: sha256:4bb2e81a40e9d0d59bd8e3dc2ba5e1f2197696f6de39a91e90798dd27299b093
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
# docker pull mysql 等價於docker pull docker.io/library/mysql:latest
docker pull mysql:5.7 # 下載指定版本的映象
5.7: Pulling from library/mysql
852e50cd189d: Already exists # 聯合檔案系統,已經存在的不會去重複下載
29969ddb0ffb: Already exists
a43f41a44c48: Already exists
5cdd802543a3: Already exists
b79b040de953: Already exists
938c64119969: Already exists
7689ec51a0d9: Already exists
36bd6224d58f: Pull complete
cab9d3fa4c8c: Pull complete
1b741e1c47de: Pull complete
aac9d11987ac: Pull complete
Digest: sha256:8e2004f9fe43df06c3030090f593021a5f283d028b5ed5765cc24236c2c4d88e
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
(4)刪除映象
# 刪除單個映象
docker rmi -f 映象名/ID # 如果是映象名,後面不帶TAG,則預設刪除latest
# 刪除多個映象
docker rmi -f 映象名1 映象名2 ...
docker rmi -f id1 id2 ... // 注意:兩種方式不能混用
# 刪除全部映象
docker rmi -f $(docker images -aq)
3、容器命令
有映象才能建立容器,這是根本前提(下載一個Centos映象演示)
(1) 新建並啟動容器(互動式)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run -it --name mycentos centos // 如果不指定別名,系統會自動分配
# OPTIONS說明:
# --name 容器新名字:為容器指定一個名稱
# -d:後臺執行容器,並返回容器ID,即啟動守護式容器
# -i: 以互動模式執行容器,通常與-t同時使用
# -t: 為容器重新分配一個偽輸入終端,通常與-i同時使用
# -P: 隨機埠對映
# -p: 指定埠對映有以下四種格式
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort
containerPort
# 測試
[root@iz2zeaj5c9isqt1zj9elpbz ~]# docker run -it centos /bin/bash # 啟動並進入容器
[root@783cb2f26230 /]# ls # 在容器內檢視
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@783cb2f26230 /]# exit # 退出容器
exit
[root@iz2zeaj5c9isqt1zj9elpbz ~]#
(2)列出當前所有正在執行的容器
docker ps [OPTIONS] # 不帶OPTIONS,則只列出正在執行的容器
# OPTIONS說明(常用):
# -a:列出當前所有正在執行的容器以及歷史上執行過的
# -l:顯示最近建立的容器
# -n: 顯示最近n個建立的容器
# -q: 靜默模式,只顯示容器編號
# --no-trunc:不截斷輸出
(3)退出容器
exit // 直接關閉並退出容器
重新開啟一個終端,執行docker ps -l
,會返回剛才我們建立的容器資訊,並且STATUS
會提示已經退出。
那麼可不可以在互動式,不關閉容器的情況下,暫時退出,一會又可以回來呢?
Ctrl + P + Q
# 執行後,我們將退出容器,回到宿主機,使用docker ps -l,我們會發現這個剛才退出的容器STATUS是Up狀態。
(4)啟動容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
# 同時可以啟動多個容器,容器名和ID可以混用
# OPTION說明(常用):
# -i : 進入互動式,此時只能進入一個容器
進入上面我們退出但是依然存活的容器
docker start -i 186ae928f07c
(5)重啟容器
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死容器之前等待停止的時間(預設為10)
(6) 停止容器
docker stop [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -t :在殺死前等待停止的時間(預設為10)
docker kill [OPTIONS] CONTAINER [CONTAINER...] // 強制關閉(相當於拔電源)
# OPTIONS說明:
# -s: 傳送到容器的訊號(預設為“KILL”)
(7)刪除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# OPTIONS說明:
# -f :強制刪除,不管是否在執行
# 上面的命令可以刪除一個或者多個,但是想全部刪除所有的容器,那麼我們要把全部的容器名或者ID都寫一遍嗎?
docker rm -f $(docker ps -aq) # 刪除所有
docker ps -aq | xargs docker rm -f
(8) 啟動守護式容器
docker run -d 容器名/容器ID
說明:我們使用docker ps -a
命令檢視,會發現剛才啟動的容器以及退出了,這是為什麼呢?
很重要的要說明一點:Docker容器後臺執行,必須要有一個前臺程序,容器執行的命令如果不是那些一直掛起的命令(比如執行top,tail),就會自動退出。
這就是Docker的機制問題,比如我們現在執行WEB容器,以Nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service
即可,例如service nginx start
,但是這樣做,Nginx為後臺程序模式執行,導致Docker前臺沒有執行的應用。這樣的容器後臺啟動後,會立即自殺因為它覺得它沒事可做。所以最佳的解決方案是將要執行的程式以前臺程序的形式執行。
那麼如何讓守護式容器不自動退出呢?我們可以執行一直掛起的命令。
docker run -d centos /bin/sh -c "while true;do echo hello Negan;sleep 2;done"
(9) 檢視容器日誌
docker logs [OPTIONS] CONTAINER
# OPTION說明:
# -t 加入時間戳
# -f 跟隨最新的日誌列印
# --tail 顯示最後多少條
(10) 檢視容器內的程序
docker top CONTAINER
(11) 檢視容器內部細節
docker inspect CONTAINER
(12) 進入正在執行的容器並以命令列互動
exec
在容器中開啟新的終端,並且可以啟動新的程序
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker exec -it CONTAINER /bin/bash // 進入容器內部並互動
# 隔山打牛
docker exec -it 3d00a0a2877e ls -l /tmp
# 3d00a0a2877e是我上面跑的守護式容器
# 進入容器,執行ls -l /tmp 命令後將結果返回給我宿主機,而使用者的介面還是停留在宿主機,沒有進入容器內部
attach
直接進入容器啟動命令的終端,不會啟動新的程序
docker attach CONTAINER
# 注意,進入上面那個守護式容器後,我們會看到還是每隔兩秒列印hello Negan,而且不能退出,只能重新開啟一個終端,執行docker kill 命令
(13)容器與主機間的檔案拷貝
docker cp CONTAINER:SRC_PATH DEST_PATH # 把容器內的檔案拷貝到宿主機
docker cp 90bd03598dd4:123.txt ~
docker cp SRC_PATH CONTAINER:DEST_PATH # 把宿主機上的檔案拷貝到容器
docker cp 12345.txt 90bd03598dd4:~
練習
練習一:Docker部署Nginx
# 查詢一個nginx映象
docker search nginx
# 下載映象
docker pull nginx
# 啟動
docker run -d --name nginx01 -p 3344:80 nginx # -p 3344(宿主機):80(容器埠)
# 測試(瀏覽器訪問)
123.56.243.64:3344
五、視覺化
1、portainer
Docker圖形介面管理工具,提供一個後臺面板供我們操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true protainer/portainer
六、Docker映象
1、映象是什麼
映象是一種輕量級、可執行的獨立軟體包,用來打包軟體執行環境和基於執行環境開發的軟體,它包含執行某個軟體所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。
所有應用直接打包docker映象,就可以直接跑起來。
如何獲取映象
- 從遠端操作下載
- 朋友拷貝
- 自己製作一個映象DockerFile
2、映象載入原理
(1)聯合檔案系統
UnionFS(聯合檔案系統):Union檔案系統是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層一層疊加,同時可以將不同目錄掛在到同一個虛擬檔案系統下。聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象。
特性:一次同時載入多個檔案系統,但從外面看起來,只能看到一個檔案系統,聯合載入會把各層檔案系統疊加起來,這樣最終的檔案系統會包含所有底層的檔案和目錄。
(2)Docker映象載入原理
Docker的映象實際上是由一層一層的檔案系統組成,也就是上面所說的聯合檔案系統。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引導載入kernel,Linux剛啟動時會載入bootfs檔案系統,在Docker映象最底層是bootfs。這一層與我們典型的Linux/Unix系統是一樣的,包含boot載入器和核心。當boot載入完成之後整個核心就都在記憶體中,此時記憶體的使用權已由bootfs轉交給核心,此時系統也會解除安裝bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系統中的/dev,/proc,/bin,/etc等標準目錄和檔案。rootfs就是各種不同的作業系統發行版,比如Ubuntu,CentOS等等。
對於一個精簡的OS,rootfs可以很小,只需要包含最近本的命令,工具和程式庫就可以,因為底層直接用Host的kernel,自己只需要提供rootfs就可以,由此可見對於不同的Linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3、分層理解
(1)分層的映象
我們下載一個映象,注意觀察下載的日誌輸出,可以看到是一層一層在下載。
為什麼Docker映象採用這種分層的結構?
最大的好處,我覺得莫過於資源共享了,比如有多個映象從相同的Base映象構建而來,那麼宿主機只需要在磁碟上保留一份Base映象,同時記憶體中也只需要載入一份Base映象,這樣就可以為所有的容器服務了,而且映象每一層都可以被共享。
(2)理解
所有的Docker映象都始於一個基礎映象層,當進行修改或者增加新內容時,就會在當前映象層之上,建立新的映象層。假如基於Ubuntu Linux 16.04建立一個新的映象,這就是新映象的第一層;如果在該映象中新增Python包,就會在基礎映象之上建立第二個映象層,如果繼續新增一個安全補丁,就會建立第三個映象層。
再新增額外的映象層同時,映象始終保持是當前所有映象的組合。
注意:Docker映象都是隻讀的,當容器啟動時,一個新的可寫層被載入到映象的頂部。
這一層就是我們通常說的容器層,容器之下的都叫映象層。
4、commit
docker commit 提交容器成為一個新的映象
docker commit -m="提交的描述資訊" -a="作者" 容器id 目標映象名:[TAG]
七、容器資料卷
如果資料在容器中,那麼我們容器刪除,資料就會丟失!
需求:資料需要持久化。
MySQL,容器刪了,刪庫跑路?Docker容器中產生的資料,需要同步到本地。
這就是卷技術,目錄的掛載,將我們容器內的目錄,掛載到Linux上。
1、使用資料卷
- 直接使用命令來掛載
docker run -it -v 主機目錄:容器內目錄 # 雙向繫結,一方改變另一方自動改變
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect d2343e9d338a # 進行檢視
/*
...
"Mounts": [ # 掛載 -v
{
"Type": "bind",
"Source": "/home/ceshi", # 主機內的路徑
"Destination": "/home", # 容器內的路徑
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
*/
2、實戰:安裝MySQL
思考:MySQL的資料持久化問題
docker run -d -p 13306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、具名掛載和匿名掛載
(1) 、匿名掛載
docker run -d -P --name nginx01 -v /etc/nginx nginx # 只指定了容器內的路徑
docker volume ls # 檢視所有volume的情況
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a # 匿名掛載
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
# 這種就是匿名掛載,我們在-v只寫了容器內的路徑,沒有寫容器外的路徑。
(2)、具名掛載
docker run -d -P -v juming:/etc/nginx --name nginx02 nginx # 具名掛載
docker volume ls
DRIVER VOLUME NAME
local 55050407d8fd052403bbf6ee349aa6268c2ec5c1054dafa678ac5dd31c59217a
local fd074ffbcea60b7fe65025ebe146e0903e90d9df5122c1e8874130ced5311049
local juming # 我們上面起的名字
# 通過-v 卷名:容器內路徑
# 所有的docker容器內的卷,沒有指定目錄的情況下,都在/var/lib/docker/volumes目錄下。
docker inspect juming
[
{
"CreatedAt": "2020-12-09T00:22:54+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming/_data",
"Name": "juming",
"Options": null,
"Scope": "local"
}
]
我們通過具名掛載可以方便的找到我們掛載的卷,大多情況下我們都會使用具名掛載。
(3)、拓展
-v 容器內路徑 # 匿名掛載
-v 卷名:容器內路徑 # 具名掛載
-v /宿主機路徑:容器內路徑 # 指定路徑掛載
# 通過 -V 容器內路徑:ro rw改變讀寫許可權
ro readonly # 只讀
rw readwrite # 可讀可寫
# 一旦設定了只讀,容器對我們掛載出來的內容就有限定了。檔案只能通過宿主機來操作,容器無法操作。
docker run -d -P --name nginx01 -v juming:/etc/nginx:ro nginx
4、資料卷容器
容器間的資訊同步。
docker run -it -v /home --name c1 centos /bin/bash # 啟動c1,作為一個父容器(指定容器內掛載目錄)
docker run -it --name c2 --volumes-from c1 centos /bin/bash # 啟動c2,掛載c1,此時在兩個容器中/home中的操作就會同步
# 下面的容器是我們自己建立的,可以參考下面dockerfile進行構建
docker run -it --name docker01 negan/centos # 啟動一個容器作為父容器(被掛載的容器)
docker run -it --name docker02 --volumes-from docker01 negan/centos #啟動容器2,掛載容器1
docker run -it --name docker03 --volumes-from docker02 negan/centos #啟動容器3,掛載容器2
# 上面的容器3也可以直接掛載到容器1上,然後我們進入任意一個容器,進入掛載卷volume01/volume02,進行操作,容器間的資料會自動同步。
結論:
容器之間配置資訊的傳遞,資料卷容器的宣告週期一直持續到沒有容器使用為止。
但是一旦持久到了本地,本地資料不會被刪除。
八、DockerFile
1、初識DockerFile
DockerFile就是用來構造docker映象的構建檔案,命令引數指令碼。通過這個指令碼,可以生成映象。映象是一層一層的,指令碼是一個個命令,每個命令就是一層。
# dockerfile1內容,所有命令都是大寫
FROM centos # 基於centos
VOLUME ["volume01","volume02"] # 掛載資料卷(匿名掛載)
CMD echo "----end-----"
CMD /bin/bash
# 構建
docker build -f dockerfile1 -t negan/centos . # -f 指定路徑 -t指定名字,不加tag預設最新
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 0cfe6b5be6bf
Removing intermediate container 0cfe6b5be6bf
---> 396a4a7cfe15
Step 3/4 : CMD echo "----end-----"
---> Running in fa535b5581fa
Removing intermediate container fa535b5581fa
---> 110d9f93f827
Step 4/4 : CMD /bin/bash
---> Running in 557a2bb87d97
Removing intermediate container 557a2bb87d97
---> c2c9b92d50ad
Successfully built c2c9b92d50ad
Successfully tagged negan/centos:latest
docker images # 檢視
2、DokcerFile構建過程
(1)、基礎知識
每個保留關鍵字(指令)都必須是大寫字母
執行從上到下順序執行
#
表示註釋每一個指令都會建立提交一個新的映象層,並提交
DockerFile是面向開發的,我們以後要釋出專案,做映象,就需要編寫dockerfile檔案。
Docker映象逐漸成為企業交付的標準。
(2)、基本命令
FROM #這個映象的媽媽是誰?(基礎映象,一切從這裡開始構建)
MAINTAINER # 告訴別人誰負責養他?(映象是誰寫的,指定維護者資訊,姓名+郵箱)
RUN # 你想讓他幹啥?(映象構建的時候需要執行的命令)
ADD # 給他點創業資金(複製檔案,會自動解壓)
WORKDIR # 映象的工作目錄
VOLUME # 給他一個存放行李的地方(設定卷,容器內掛載到主機的目錄,匿名掛載)
EXPOSE # 門牌號是多少?(指定對外埠)
CMD # 指定這個容器啟動的時候要執行的命令,只有最後一個會生效,可被替代
ENTRYPOINT # 指定這個容器啟動時候要執行的命令,可以追加命令
ONBUILD # 當構建一個被繼承DockerFile,這時就會執行ONBUILD指令
COPY # 類似ADD,將我們檔案拷貝到映象中
ENV # 構建的時候設定環境變數
3、實戰操作
(1)建立一個自己的CentOS
# vim Dockerfile
FROM centos
MAINTAINER Negan<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 構建
docker build -f Dockerfile -t negan/centos .
# 測試
docker run -it negan/centos
[root@ffae1f9eb97e local]# pwd
/usr/local # 進入的是我們在dockerfile設定的工作目錄
# 檢視映象構建過程
docker history 映象名/ID
(2)CMD與ENTRYPOINT區別
兩者都是容器啟動的時候要執行的命令,CMD命令只有最後一個會生效,後面不支援追加命令,會被替代。ENTRYPOINT不會被替代,可以追加命令。
CMD
# vim cmd
FROM centos
CMD ["ls","-a"]
# 構建
docker build -f cmd -t cmd_test .
# 執行,發現我們的ls -a命令生效
docker run cmd_test
.
..
.dockerenv
bin
dev
etc
......
# 追加命令執行
docker run cmd_test -l
# 丟擲錯誤,不能追加命令,原來的ls -a命令被-l替換,但是-l不是一個有效的命令
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 追加完整的命令
docker run cmd_test ls -al
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:36 .
drwxr-xr-x 1 root root 4096 Dec 10 14:36 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:36 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:36 dev
......
ENTRYPOINT
# vim entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
# 構建
docker build -f entrypoint -t entrypoint_test .
# 執行
docker run entrypoint_test
.
..
.dockerenv
bin
dev
etc
# 追加命令執行
docker run entrypoint_test -l
total 56
drwxr-xr-x 1 root root 4096 Dec 10 14:41 .
drwxr-xr-x 1 root root 4096 Dec 10 14:41 ..
-rwxr-xr-x 1 root root 0 Dec 10 14:41 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 10 14:41 dev
drwxr-xr-x 1 root root 4096 Dec 10 14:41 etc
......
4、實戰構建tomcat
(1)環境準備
ll
total 166472
-rw-r--r-- 1 root root 11437266 Dec 9 16:22 apache-tomcat-9.0.40.tar.gz
-rw-r--r-- 1 root root 641 Dec 10 23:26 Dockerfile
-rw-r--r-- 1 root root 159019376 Dec 9 17:39 jdk-8u11-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Dec 10 22:48 readme.txt
(2)構建映象
# vim Dockerfile (Dockerfile是官方建議命名)
FROM centos
MAINTAINER Negan<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u271-linux-aarch64.tar.gz /usr/local/
ADD apache-tomcat-9.0.40.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.40
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.40
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.40/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.40/bin/logs/catalina.out
# 構建
docker -t tomcat .
(3)啟動容器
docker run -d -P --name tomcat01 -v /home/Negan/tomcat/test:/usr/local/apache-tomcat-9.0.40/webapps/test -v /home/Negan/tomcat/logs:/usr/local/apache-tomcat-9.0.40/logs tomcat
九、釋出自己的映象
1、docker hub
首先需要在DockerHub上註冊自己的賬號,並且確定這個賬號可以登入。
在我們的伺服器上進行登入,登入成功後提交我們的映象。
# 登入
docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
# 登入成功後推送我們的映象
docker push [OPTIONS] NAME[:TAG]
Push an image or a repository to a registry
Options:
--disable-content-trust Skip image signing (default true)
docker tag tomcat huiyichanmian/tomcat # 需要改名,推送改名後的(前面加自己的使用者名稱)
docker push huiyichanmian/tomcat
2、阿里雲
登入阿里雲,找到容器映象服務,使用映象倉庫。建立名稱空間,建立映象倉庫。選擇本地倉庫。
具體阿里雲上有特別詳細的步驟,這裡不再贅述了。
十、Docker網路
1、理解docker0
(1)檢視本機網絡卡資訊
ip addr
# 本地迴環地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里雲內網地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286793195sec preferred_lft 286793195sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
(2)檢視容器網絡卡資訊
我們獲取一個tomcat映象,用來測試。
docker run -d -P --name t1 tomcat
docker exec -it t1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 我們發現容器啟動時候會得到一個eth0@ifxxx的網絡卡,而且ip地址和上面的docker0裡的是在同一網段。
233: eth0@if234: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(3)、再次檢視本地網絡卡資訊
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:0c:7b:cb brd ff:ff:ff:ff:ff:ff
inet 172.24.14.32/18 brd 172.24.63.255 scope global dynamic eth0
valid_lft 286792020sec preferred_lft 286792020sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:5e:2b:4c:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# 我們發現多了一條網絡卡資訊,而且與容器裡面的網絡卡有某種對應關係。(233,234)
234: veth284c2d9@if233: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
我們重複上面的操作,不難發現我們只要安裝了docker,本地網卡里就會多一個docker0,而且我們每啟動一個容器,docker就會給容器分配一個網絡卡,而且在本地也會多出一個網絡卡資訊,與容器內的相對應。這就是veth_pair技術,就是一對虛擬裝置介面,它們都是成對出現,一段連著協議,一段彼此相連。正因為有了這個特性,我們通常veth_pair充當了一個橋樑,連線各種虛擬網路裝置。
所有的容器不指定網路的情況下,都是由docker0路由的,docker會給我們的容器分配一個預設的ip。
Docker使用的是Linux的橋接,宿主機中docker0是一個Docker容器的網橋。所有的網路介面都是虛擬的。
只要容器刪除,對應的網橋也響應的被刪除。
2、--link
問題:我們每次重啟容器,容器的ip地址會變,而我們專案中一些配置使用的固定ip,這時候也需要做相應的改變,那麼我們可不可以直接設定服務名呢,下次重啟時候,配置直接找服務名。
我們啟動兩個tomcat,進行測試,互相ping其對應的名字,看是否能通?
docker exec -it t1 ping t2
ping: t2: Name or service not known
# 答案是肯定的,不能識別t2,如何解決?
# 我們使用--link進行連線
docker run -d -P --name t3 --link t2 tomcat
# 我們嘗試使用t3來ping t2
docker exec -it t3 ping t2
# 我們發現竟然通了
PING t2 (172.17.0.3) 56(84) bytes of data.
64 bytes from t2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from t2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.066 ms
......
那麼--link做了什麼呢?
docker exec -it t3 cat /etc/hosts # 我們來檢視t3的hosts檔案
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.3 t2 6bf3c12674c8 # 原因在這了,這裡對t2做了標記,當ping t2時候,自動轉到172.17.0.3
172.17.0.4 b6dae0572f93
3、自定義網路
(1)檢視docker網路
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
afc0c673386f none null local
# bridge 橋接(docker預設)
# host 與主機共享
# none 不配置
(2)容器啟動時的預設網路
docker0是我們預設的網路,不支援域名訪問,可以使用--link打通。
# 一般我們啟動容器是這樣子,使用的是預設網路,而這個預設網路就是bridge,所以下面兩條命令是相同的
docker run -d -P --name t1 tomcat
docker run -d -P --name t1 --net bridge tomcat
(3)建立網路
# --driver bridge 預設,可以不寫
# --subnet 192.168.0.0/16 子網掩碼
# --gateway 192.168.0.1 預設閘道器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
10684d1bfac9 bridge bridge local
19f4854793d7 host host local
0e98462f3e8e mynet bridge local # 我們自己建立的網路
afc0c673386f none null local
ip addr
.....
# 我們自己建立的網路
239: br-0e98462f3e8e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b6:a7:b1:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-0e98462f3e8e
valid_lft forever preferred_lft forever
.....
啟動兩個容器,使用我們自己建立的網路
docker run -P -d --name t1 --net mynet tomcat
docker run -P -d --name t2 --net mynet tomcat
# 檢視我們自己建立的網路資訊
docker network mynet inspect
# 我們發現我們剛啟動的兩個容器使用的是我們剛建立的網路
......
"Containers": {
"1993703e0d0234006e1f95e964344d5ce01c90fe114f58addbd426255f686382": {
"Name": "t2",
"EndpointID": "f814ccc94232e5bbc4aaed35022dde879743ad9ac3f370600fb1845a862ed3b0",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"8283df6e894eeee8742ca6341bf928df53bee482ab8a6de0a34db8c73fb2a5fb": {
"Name": "t1",
"EndpointID": "e462941f0103b99f696ebe2ab93c1bb7d1edfbf6d799aeaf9a32b4f0f2f08e01",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
.......
那麼使用自己建立的網路有什麼好處呢?
我們回到我們以前的問題,就是域名ping不通的問題上。
docker exec -it t1 ping t2
PING t2 (192.168.0.3) 56(84) bytes of data.
64 bytes from t2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.063 ms
......
docker exec -it t2 ping t1
PING t1 (192.168.0.2) 56(84) bytes of data.
64 bytes from t1.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.045 ms
......
我們發現域名可以ping通了,那麼說明我們自定義的網路已經幫我們維護好了對應關係。
這樣不同的叢集使用不同的網路,也能保證叢集的安全和健康。
4、網路連通
現在有這樣一個需求,t1,t2使用的是我們自己定義的網路,t3,t4使用的預設的docker0網路。那麼現在t3和t1或者t2能否通訊呢?
我們知道,docker0的預設閘道器是172.17.0.1,mynet預設閘道器是192.168.0.1,他們直接屬於不同的網段,不能進行通訊。那麼現在如何解決上面的問題呢?
那麼mynet能不能給t3分配一個ip地址呢?如果能分配,那麼問題就應該可以得到解決。
docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 一個容器兩個ip地址
docker network connect mynet t3 # 將t3加入到mynet網路中
# 檢視mynet資訊
docker network inspect mynet
"Containers": {
......
"d8ecec77f7c1e6d26ad0fcf9107cf31bed4b6dd553321b737d14eb2b497794e0": {
"Name": "t3", # 我們發現了t3
"EndpointID": "8796d63c1dd1969549a2d1d46808981a2b0ad725745d794bd3b824f278cec28c",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
......
這時候,t3就能和t1,t2進行通訊了。
5、部署Redis叢集
# 建立網路
docker network create redis --subnet 172.38.0.0/16
# 通過指令碼建立六個redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 啟動容器
vim redis.py
import os
for i in range(1, 7):
str = "docker run -p 637{}:6379 -p 1637{}:16379 --name redis-{} \
-v /mydata/redis/node-{}/data:/data \
-v /mydata/redis/node-{}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1{} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf".format(i,i,i,i,i,i)
os.system(str)
python reidis.py
# 建立叢集
docker exec -it redis-1 /bash/sh # 進入redis-1容器
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 建立叢集。。。。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: a89026d4ea211d36ee04f2f3762c6e3cd9692a28 172.38.0.14:6379
slots: (0 slots) slave
replicates d63e90423a034f9c42e72cc562706919fd9fc418
S: 53d6196c160385181ff23b15e7bda7d4387b2b17 172.38.0.16:6379
slots: (0 slots) slave
replicates 9d1d33301aea7e4cc9eb41ec5404e2199258e94e
M: 9d1d33301aea7e4cc9eb41ec5404e2199258e94e 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: bee27443cd5eb6f031115f19968625eb86c8440b 172.38.0.15:6379
slots: (0 slots) slave
replicates 875f0a7c696fcd584c4f5a7fd5cc38b343acbc49
M: d63e90423a034f9c42e72cc562706919fd9fc418 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 進行測試
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:160
cluster_stats_messages_pong_sent:164
cluster_stats_messages_sent:324
cluster_stats_messages_ping_received:159
cluster_stats_messages_pong_received:160
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:324
十一、Docker Compose
1、介紹
Compose是Docker官方開源的專案,需要安裝。
Compose是用於定義和執行多容器Docker應用程式的工具。通過Compose,可以使用YAML檔案來配置應用程式的服務。然後,使用一個命令,就可以從配置中建立並啟動所有服務。
使用Compose基本上是一個三步過程:
- Dockerfile保證我們的專案在任何地方執行
- docker-compose檔案
- 啟動
2、快速開始
(1)安裝
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 安裝成功
# docker-compose version 1.27.4, build 40524192
(2)使用
# 為專案建立目錄
mkdir composetest
cd composetest
# 編寫一段flask程式
vim app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # 這裡主機名直接用“redis”,而不是ip地址
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
# 編寫requirements.txt檔案(不指定版本,下載最新)
flask
redis
# 編寫Dockerfile檔案
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 編寫docker-compose.yml檔案
# 檔案中定義了兩個服務,web和redis,web是從Dockerfile開始構建,redis使用的是公共映象
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
# 執行
docker-compose up
3、搭建部落格
# 建立並進入目錄
mkdir wordpress && cd wordpress
# 編寫docker-compose.yml檔案來啟動,並建立了一個單獨Mysql例項,具有用於資料永續性的卷掛載
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
W0RDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 啟動執行
docker-compose up -d
十二、Docker Swarm
1、環境準備
準備四臺伺服器。安裝docker。
2、swarm叢集搭建
docker swarm COMMAND
Commands:
ca Display and rotate the root CA
init Initialize a swarm # 初始化一個節點(管理節點)
join Join a swarm as a node and/or manager # 加入節點
join-token Manage join tokens # 通過節點token加入
leave Leave the swarm # 離開節點
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm #更新
# 首先我們初始化一個節點
docker swarm init --advertise-addr + 自己的ip(這裡用內網地址,主要是省錢)
docker swarm init --advertise-addr 172.27.0.4
# 提示我們節點建立成功
Swarm initialized: current node (yamss133bil4gb59fyangtdmm) is now a manager.
To add a worker to this swarm, run the following command:
# 在其他機器上執行,加入這個節點
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 生成一個管理節點的token,
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# 我們在機器2上行加入節點的命令
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-4yyba7377uz9mfabak6pwu4ci 172.27.0.4:2377
# 我們在機器1上檢視節點資訊
docker node ls
# 我們發現一個管理節點,一個工作節點,狀態都是就緒狀態
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Leader 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Ready Active 20.10.0
# 現在將機器3也加入,同樣是work節點
# 到此為止,現在只有機器4沒有加入,這時候我們想把它設定成管理節點。
# 在機器1上執行生成管理節點的命令,在機器4上執行
docker swarm join-token manager
# 生成的命令在機器4上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 此時機器4也是管理節點了
This node joined a swarm as a manager.
# 至此我們可以在機器1或者機器4上進行其他操作了。(只能在管理節點上操作)
3、Raft協議
在前面的步驟,我們已經完成了雙主雙從叢集的搭建。
Raft協議:保證大多數節點存活才能使用。必須大於1,叢集至少大於3臺。
實驗1
將機器1上的docker停止,宕機,現在叢集中只有一個管理節點了。叢集是否可用。
#我們在機器四上檢視節點資訊
docker node ls
# 發現我們的叢集已經不能用了
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
# 我們將機器1上的docker重啟,發現叢集又能用了,但是此時機器1上的docker已經不是leader了,leader自動轉給機器4了
實驗2
我們將工作節點離開叢集,檢視叢集資訊
# 我們在機器2上執行
docker swarm leave
# 在機器一上檢視節點資訊
docker node ls
# 我們發現機器2狀態是Down
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm * VM-0-4-centos Ready Active Reachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
實驗3
現在我們將機器2也設定成管理節點(此時叢集有三個管理節點),隨機down掉一個管理節點,叢集是否正常執行。
# 在機器2上執行
docker swarm join --token SWMTKN-1-3f8p9pq2gp36s6ei0bs9pepqya24n274msin701j9kdt7h3v2z-82pamju7b37aq8e1dcf1xmmng 172.27.0.4:2377
# 將機器1的docker宕機
systemctl stop docker
# 在機器二上檢視節點資訊
docker node ls
# 叢集正常,且可以看到機器1宕機了
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yamss133bil4gb59fyangtdmm VM-0-4-centos Ready Active Unreachable 20.10.0
mfxdgj1pobj0idbl9cesm2xnp VM-0-7-centos Down Active 20.10.0
vdwcwr3v6qrn6da40zdrjkwmy * VM-0-7-centos Ready Active Reachable 20.10.0
u3rlqynazrdiz6oaubnuuyqod VM-0-11-centos Ready Active 20.10.0
im6kk7qd2a3s9g98lydni6udi VM-0-13-centos Ready Active Leader 20.10.0
4、彈性建立服務
docker service COMMAND
Commands:
create Create a new service # 建立一個服務
inspect Display detailed information on one or more services # 檢視服務資訊
logs Fetch the logs of a service or task # 日誌
ls List services # 列表
ps List the tasks of one or more services # 檢視我們的服務
rm Remove one or more services # 刪除服務
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services # 動態擴縮容
update Update a service # 更新
docker service create -p 8888:80 --name n1 nginx # 建立一個服務,在叢集中隨機分配
kj0xokbxvf5uw91bswgp1cukf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# 給我們的服務增加三個副本
docker service update --replicas 3 n1
# 動態擴縮容
docker service scale n1=10 # 和上面updata一樣,這個方便
docker ps # 檢視
服務,叢集中任意的節點都可以訪問,服務可以有多個副本動態擴縮容實現高可用。
5、使用Docker stack部署部落格
我們現在需要將上面的部落格跑在我們的叢集中,並且要開十個副本。
# 編輯docker-compose.yml檔案
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
deploy:
replicas: 10
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
# 進行啟動
docker stack deploy -c docker-compose.yml wordpress
# 檢視
docker service ls