文章目錄
- 1. 運行redis容器
- 2. 運行web容器
- 3. 編排鏡像
- 4. 構建node.js鏡像
- 4.1 dockerfile基礎定義
- 4.2 npm install
- 4.3 應用配置
- 4.4 構建并運行鏡像
- 4.5 運行添加環境變數
- 5. OnBuild優化Dockerfile
- 6. 忽略檔案`.dockerignore`
- 6.1 Docker Ignore
- 6.2 Docker 構建安全背景關系
- 6.3 優化構建
- 7. 容器持久化資料
- 8. 容器之間的交流
- 9. Docker 網路
- 9.1 創建網路
- 9.2 連接網路
- 9.3 連接兩個容器
- 9.4 Create Aliases
- 9.5 斷開容器連接
- 10. 使用卷持久化存盤
- 10.1 --volumes,-v
- 10.2 --volumes-from
- 10.3 只讀卷
- 11. 管理日志
- 12. 容器運行策略
- 13. 容器元資料與標簽
- 14. 負載平衡的容器
- 14.1 NGINX Proxy
- 14.2 單機
- 14.2 集群
相關閱讀:
- docker 快速學習手冊
- docker 的冷門高效玩法
- docker 命令使用大全
參考資料:
- https://www.katacoda.com/courses/docker
1. 運行redis容器
第一個任務是識別配置為運行Redis的Docker映像的名稱,使用Docker,所有容器都是基于Docker映像啟動的,這些影像包含啟動流程所需的所有內容;主機不需要任何配置或依賴項,
$ docker search redis
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 9971 [OK]
sameersbn/redis 83 [OK]
grokzen/redis-cluster Redis cluster 3.0, 3.2, 4.0, 5.0, 6.0, 6.2 79
rediscommander/redis-commander Alpine image for redis-commander - Redis man… 66 [OK]
redislabs/redisearch Redis With the RedisSearch module pre-loaded… 39
redislabs/redisinsight RedisInsight - The GUI for Redis 35
kubeguide/redis-master redis-master with "Hello World!" 33
redislabs/redis Clustered in-memory database engine compatib… 31
oliver006/redis_exporter Prometheus Exporter for Redis Metrics. Supp… 30
redislabs/rejson RedisJSON - Enhanced JSON data type processi… 27
arm32v7/redis Redis is an open source key-value store that… 24
redislabs/redisgraph A graph database module for Redis 16 [OK]
arm64v8/redis Redis is an open source key-value store that… 15
redislabs/redismod An automated build of redismod - latest Redi… 15 [OK]
redislabs/rebloom A probablistic datatypes module for Redis 14 [OK]
webhippie/redis Docker image for redis 11 [OK]
insready/redis-stat Docker image for the real-time Redis monitor… 10 [OK]
s7anley/redis-sentinel-docker Redis Sentinel 10 [OK]
redislabs/redistimeseries A time series database module for Redis 10
goodsmileduck/redis-cli redis-cli on alpine 9 [OK]
centos/redis-32-centos7 Redis in-memory data structure store, used a… 5
clearlinux/redis Redis key-value data structure server with t… 3
wodby/redis Redis container image with orchestration 1 [OK]
tiredofit/redis Redis Server w/ Zabbix monitoring and S6 Ove… 1 [OK]
xetamus/redis-resource forked redis-resource 0 [OK]
$
使用搜索命令,Jane已經確定Redis Docker Image被稱為Redis,并希望運行最新版本,因為Redis是一個資料庫,Jane想在她繼續作業的時候把它作為后臺服務運行,
要完成這一步,在后臺啟動一個容器,運行一個基于官方影像的Redis實體,
Docker CLI有一個名為run的命令,它將基于Docker映像啟動一個容器,結構是docker運行<options> <image-name>
默認情況下,Docker將在前臺運行一個命令,要在后臺運行,需要指定選項-d,
$ docker run -d redis
66a23eb0c3fd7ce1099f0eef043303eb286084a87e6047e851057d0ecc634ee0
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66a23eb0c3fd redis "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp zen_archimedes
`docker inspect <friendly-name|container-id>`命令提供了運行容器的詳細資訊,如IP地址等,
`docker logs <friendly-name|container-id>`將顯示容器寫入標準錯誤或標準輸出的訊息,
#靜態映射埠
$ docker run -d --name redisHostPort -p 6379:6379 redis:latest
00d11bf6c9217aa43646c32779a29d62d854e90ae3cfe70e72e61016db49fb7c
#動態映射埠
$ docker run -d --name redisDynamic -p 6379 redis:latest
56a6612f70b1f35097da220339cc1ec4c3ae84137757e803aa59e41c61523a11
$ docker port redisDynamic 6379
0.0.0.0:32768
#已經啟動了多個redis實體
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
56a6612f70b1 redis:latest "docker-entrypoint.s…" 58 seconds ago Up 57 seconds 0.0.0.0:32768->6379/tcp redisDynamic
00d11bf6c921 redis:latest "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6379->6379/tcp redisHostPort
66a23eb0c3fd redis "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 6379/tcp zen_archimedes
預設情況下,主機埠映射為0.0.0.0,即所有IP地址,在定義埠映射時,可以指定一個特定的IP地址,例如-p 127.0.0.1:6379:6379
默認情況下,Docker將運行可用的最新版本,如果需要一個特定的版本,它可以被指定為一個標記,例如,version 3.2將被docker run -d redis:3.2,
由于這是Jane第一次使用Redis映像,它將被下載到Docker Host機器上,
讓資料持久存盤
$ docker run -d --name redisMapped -v /opt/docker/data/redis:/data redis
71e3cfca3344c9eaa4102761fad59d135c3233b56e960eebd5b650d72996936e
Docker允許您使用$PWD作為當前目錄的占位符,
2. 運行web容器
Docker映像從一個基本映像開始,基本映像應該包括應用程式所需的平臺依賴項,例如,安裝JVM或CLR,
這個基本映像定義為Dockerfile中的一條指令,Docker映像是基于Dockerfile的內容構建的,Dockerfile是描述如何部署應用程式的說明串列,
在這個例子中,我們的基礎影像是Nginx的Alpine版本,這提供了Linux Alpine發行版上配置的web服務器,
寫一個網頁
$ vim index.html
<h1>Hello World</h1>
創建dockerfile
FROM nginx:alpine
COPY . /usr/share/nginx/html
構建鏡像
$ docker build -t webserver-image:v1 .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM nginx:alpine
---> 513f9a9d8748
Step 2/2 : COPY . /usr/share/nginx/html
---> ae7287f132f3
Successfully built ae7287f132f3
Successfully tagged webserver-image:v1
$ docker build -t webserver-image:v1 .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM nginx:alpine
---> 513f9a9d8748
Step 2/2 : COPY . /usr/share/nginx/html
---> Using cache
---> ae7287f132f3
Successfully built ae7287f132f3
Successfully tagged webserver-image:v1
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver-image v1 ae7287f132f3 15 seconds ago 22.9MB
通過鏡像運行容器
$ docker run -d -p 80:80 webserver-image:v1
測驗web
$ curl ip
<h1>Hello World</h1>
3. 編排鏡像
所有Docker映像都從一個基本映像開始,基本映像是來自Docker 官方倉庫用于啟動容器的相同映像,除了影像名稱,我們還可以包含影像標簽,以指示我們想要的特定版本,默認情況下,這是最新的版本,
這些基本映像用作運行應用程式所需的附加更改的基礎,例如,在這個場景中,在部署靜態HTML檔案之前,我們需要在系統上配置并運行NGINX,因此,我們想使用NGINX作為我們的基本映像,
Dockerfile是簡單的文本檔案
FROM nginx:1.11-alpine
定義了基本映像之后,我們需要運行各種命令來配置映像,有很多命令可以幫助實作這一點,主要的兩個命令是COPY和RUN,
RUN <command>允許您像在命令提示符中那樣執行任何命令,例如安裝不同的應用程式包或運行構建命令,RUN的結果會持久化到映像中,因此不要在磁盤上留下任何不必要的或臨時的檔案,這一點很重要,因為這些檔案將包含在映像中,
COPY <src> <dest>允許您將檔案從包含Dockerfile的目錄復制,
已經為您創建了一個新的index.html檔案,我們想從我們的容器中提供該檔案,在FROM命令后面的下一行,使用COPY命令將index.html復制到/usr/share/nginx/html目錄中,
$ vim index.html
<h1>Hello World</h1>
FROM nginx:1.11-alpine
COPY index.html /usr/share/nginx/html/index.html
將我們的檔案復制到映像中并下載了所有依賴項后,您需要定義需要訪問哪個埠應用程式,
使用EXPOSE <port>命令可以告訴Docker應該打開哪些埠,可以系結到哪些埠,您可以在一條命令中定義多個埠,例如EXPOSE 80433或EXPOSE 7000-8000
FROM nginx:1.11-alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
配置好Docker映像并定義了我們想要訪問的埠后,現在我們需要定義啟動應用程式的命令,
Dockerfile中的CMD行定義了啟動容器時要運行的默認命令,如果命令需要引數,那么建議使用一個陣列,例如["cmd", "-a", "arga value", "-b", "argb-value"],這將被組合在一起,命令cmd -a" arga value" -b argb-value將被運行,
運行NGINX的命令為NGINX -g daemon off;將此設定為Dockerfile中的默認命令,
CMD的另一種方法是ENTRYPOINT,雖然CMD可以在容器啟動時被重寫,但ENTRYPOINT定義了一個命令,在容器啟動時可以將引數傳遞給它,
在這個例子中,NGINX將是關閉-g守護行程的入口點;默認的命令,
FROM nginx:1.11-alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
寫完Dockerfile后,你需要使用docker構建將它轉換成一個映像,build命令接受一個包含Dockerfile的目錄,執行步驟并將映像存盤在本地Docker引擎中,如果由于錯誤而失敗,則構建將停止,
docker build -t my-nginx-image:latest .
docker images
使用來自build命令的ID結果或您分配給它的友好名稱啟動新構建映像的實體,
NGINX被設計為后臺服務,所以你應該包含選項-d,要使web服務器可訪問,使用-p 80:80將其系結到埠80
$ docker run -d -p 80:80 my-nginx-image:latest
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e0b44c067f my-nginx-image:latest "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, 443/tcp gracious_ride
$ curl -i http://ip
HTTP/1.1 200 OK
Server: nginx/1.11.13
Date: Tue, 28 Sep 2021 08:17:44 GMT
Content-Type: text/html
Content-Length: 21
Last-Modified: Tue, 28 Sep 2021 08:11:49 GMT
Connection: keep-alive
ETag: "6152ce45-15"
Accept-Ranges: bytes
<h1>Hello World</h1>
4. 構建node.js鏡像
4.1 dockerfile基礎定義
正如我們在前一個場景中所描述的,所有映像都從一個基本映像開始,理想情況下,該映像盡可能接近您所需的配置,Node.js為每個發布版本提供了帶有標簽的預構建映像,
Node:0.0的影像為Node:10-alpine,這是一個基于 Alpine-based的構建,比官方形象更小和更流線型,
除了基本映像,我們還需要創建應用程式運行的基本目錄,使用RUN <命令>,我們可以像從命令shell中運行一樣執行命令,通過使用mkdir,我們可以創建目錄.
我們可以使用WORKDIR <目錄>定義一個作業目錄,以確保所有未來的命令都是從相對于我們的應用程式的目錄執行的,
在單獨的行上設定FROM <image>:<tag>、RUN <command>和WORKDIR <directory>,以配置用于部署應用程式的基本環境,
Dockerfile內容:
FROM node:10-alpine
RUN mkdir -p /src/app
WORKDIR /src/app
4.2 npm install
在前面的集合中,我們配置了配置的基礎以及希望如何部署應用程式,下一個階段是安裝運行應用程式所需的依賴項,對于Node.js,這意味著運行NPM install,
為了將構建時間保持在最小,Docker將在Dockerfile中快取一行代碼的執行結果,以便在將來的構建中使用,如果發生了更改,Docker將使當前行和以下所有行無效,以確保所有行都是最新的,
對于NPM,我們只希望在包中有東西時重新運行NPM install,Json檔案已經改變,如果沒有任何改變,那么我們可以使用快取版本來加速部署,使用COPY包,我們可以使RUN npm install命令失效,如果包,Json檔案已經改變,如果檔案沒有更改,那么快取將不會失效,并且將使用npm install命令的快取結果,
Dockerfile更新內容:
FROM node:10-alpine
RUN mkdir -p /src/app
WORKDIR /src/app
COPY package.json /src/app/package.json
RUN npm install
如果你不想使用快取作為構建的一部分,那么設定選項--no-cache=true作為docker構建命令的一部分,
4.3 應用配置
在安裝了依賴項之后,我們希望復制應用程式的其余源代碼,拆分依賴項的安裝并復制源代碼使我們能夠在需要時使用快取,
如果我們在運行npm install之前復制我們的代碼,那么它每次都會運行,因為我們的代碼會發生變化,通過復制只是包,Json,我們可以確保快取是無效的,只有當我們的包內容已經改變,
在Dockerfile中創建所需的步驟,以完成應用程式的部署,
我們可以使用copy復制Dockerfile所在的整個目錄<dest dir>,
復制源代碼之后,使用EXPOSE <port>定義應用程式需要訪問的埠,
最后,需要啟動應用程式,使用Node.js的一個巧妙技巧是使用npm start命令,這看起來在包里,Json檔案,了解如何啟動保存重復命令的應用程式,
FROM node:10-alpine
RUN mkdir -p /src/app
WORKDIR /src/app
COPY package.json /src/app/package.json
RUN npm install
COPY . /src/app
EXPOSE 3000
CMD [ "npm", "start" ]
4.4 構建并運行鏡像
$ docker build -t my-nodejs-app .
$ docker run -d --name my-running-app -p 3000:3000 my-nodejs-app
您可以使用curl測驗容器是否可訪問,如果應用程式回應,那么您就知道一切都已正確啟動,
$ curl http://docker:3000
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
4.5 運行添加環境變數
Docker映像應該設計成可以從一個環境傳輸到另一個環境,而不需要做任何更改或重新構建,通過遵循這個模式,您可以確信,如果它在一個環境(如登臺)中作業,那么它也將在另一個環境(如生產環境)中作業,
使用Docker,可以在啟動容器時定義環境變數,例如,對于Node.js應用程式,您應該在生產環境中運行時為NODE_ENV定義一個環境變數,
使用-e選項,可以將名稱和值設定為-e NODE_ENV=production
$ docker run -d --name my-production-running-app -e NODE_ENV=production -p 3000:3000 my-nodejs-app
5. OnBuild優化Dockerfile
雖然Dockerfile是按從上到下的順序執行的,但當該映像用作另一個映像的基礎時,您可以觸發一條指令在稍后的時間執行,
ONBUILD指令可以為鏡像添加觸發器,
當我們在一個Dockerfile檔案中加上ONBUILD指令,該指令對利用該Dockerfile構建鏡像(比如為A鏡像)不會產生實質性影響,
但是當我們撰寫一個新的Dockerfile檔案來基于A鏡像構建一個鏡像(比如為B鏡像)時,這時構造A鏡像的Dockerfile檔案中的ONBUILD指令就生效了,在構建B鏡像的程序中,首先會執行ONBUILD指令指定的指令,然后才會執行其它指令,
需要注意的是,如果是再利用B鏡像構造新的鏡像時,那個ONBUILD指令就無效了,也就是說只能再構建子鏡像中執行,對孫子鏡像構建無效,其實想想是合理的,因為在構建子鏡像中已經執行了,如果孫子鏡像構建還要執行,相當于重復執行,這就有問題了,
利用ONBUILD指令,實際上就是相當于創建一個模板鏡像,后續可以根據該模板鏡像創建特定的子鏡像,需要在子鏡像構建程序中執行的一些通用操作就可以在模板鏡像對應的dockerfile檔案中用ONBUILD指令指定, 從而減少dockerfile檔案的重復內容撰寫,
下面是Node.js的OnBuild Dockerfile,與前面的場景不同,應用程式指定命令以ONBUILD作為前綴,
Dockerfile1:
FROM node:7
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ONBUILD COPY package.json /usr/src/app/
ONBUILD RUN npm install
ONBUILD COPY . /usr/src/app
CMD [ "npm", "start" ]
docker build -t node:7-onbuild -f Dockerfile1
結果是,我們可以構建這個映像,但在將構建的映像用作基本映像之前,不會執行應用程式特定的命令,然后,它們將作為基本映像構建的一部分執行,
有了復制代碼、安裝依賴項和啟動應用程式的所有邏輯后,在應用程式級別上需要定義的唯一方面就是要公開哪個埠,
創建OnBuild映像的好處是,我們的Dockerfile現在更簡單,可以輕松地跨多個專案重用,而不必重新運行相同的步驟,以提高構建時間,
dockerfile2:
FROM node:7-onbuild
EXPOSE 3000
已經為您創建了上一步中的Dockerfile,基于OnBuild docker檔案構建映像與之前相同,OnBuild命令將像在基礎Dockerfile中一樣執行,
總之,唯有 ONBUILD 是為了幫助別人定制自己而準備的,而不是為了構建當前鏡像的,
docker build -t my-nodejs-app -f dockerfile2
docker run -d --name my-running-app -p 3000:3000 my-nodejs-app
curl http://ip:3000
6. 忽略檔案.dockerignore
6.1 Docker Ignore
為了防止敏感檔案或目錄被錯誤地包含在映像中,您可以添加一個名為.dockerignore的檔案,
Dockerfile將作業目錄復制到Docker映像中,因此,這將包括潛在的敏感資訊,如我們希望在映像外部管理的密碼檔案,
$ ls
Dockerfile cmd.sh passwords.txt
$ cat Dockerfile
FROM alpine
ADD . /app
COPY cmd.sh /cmd.sh
CMD ["sh", "-c", "/cmd.sh"]
$ cat cmd.sh
echo "Hello World"
$ cat passwords.txt
admin:admin
$ docker build -t password .
Sending build context to Docker daemon 4.096kB
Step 1/4 : FROM alpine
---> 3fd9065eaf02
Step 2/4 : ADD . /app
---> 8e7bc5dac978
Step 3/4 : COPY cmd.sh /cmd.sh
---> ec486638d561
Step 4/4 : CMD ["sh", "-c", "/cmd.sh"]
---> Running in fe4cba7a87b2
Removing intermediate container fe4cba7a87b2
---> 4c270e87d27c
Successfully built 4c270e87d27c
Successfully tagged password:latest
$ docker run password ls /app
Dockerfile
cmd.sh
passwords.txt
這將包括密碼檔案,
下面的命令將在.dockerignore檔案中包含password .txt,并確保它不會意外地出現在容器中,dockerignore檔案將存盤在源代碼管理中,并與團隊共享,以確保每個人都是一致的,
echo passwords.txt >> .dockerignore
.dockerignore檔案支持目錄和正則運算式來定義限制,非常類似于.gitignore,這個檔案還可以用來提高構建時間,我們將在下一步研究這個問題,
構建映像,因為Docker Ignore檔案不應該包括密碼檔案,
$ docker build -t nopassword .
Sending build context to Docker daemon 4.096kB
Step 1/4 : FROM alpine
---> 3fd9065eaf02
Step 2/4 : ADD . /app
---> 36ee8b3bc4ee
Step 3/4 : COPY cmd.sh /cmd.sh
---> a4c8fc953352
Step 4/4 : CMD ["sh", "-c", "/cmd.sh"]
---> Running in 5b7774763eca
Removing intermediate container 5b7774763eca
---> b6b6eac92cce
Successfully built b6b6eac92cce
Successfully tagged nopassword:latest
$ docker run nopassword ls /app
Dockerfile
cmd.sh
如果您需要使用密碼作為RUN命令的一部分,那么您需要復制、執行和洗掉檔案作為單個RUN命令的一部分,只有Docker容器的最終狀態被持久化到映像中,
6.2 Docker 構建安全背景關系
dockerignore檔案可以確保Docker映像中不包含敏感細節,但是,它們也可以用來提高映像的構建時間,
在環境中,已經創建了100M的臨時檔案,Dockerfile永遠不會使用這個檔案,當您執行構建命令時,Docker將整個路徑內容發送給引擎,以便它計算要包含哪些檔案,因此,發送100M檔案是不需要的,并創建了一個較慢的構建,
您可以通過執行該命令看到100M的影響,
$ docker build -t large-file-context .
Sending build context to Docker daemon 104.9MB
Step 1/4 : FROM alpine
---> 3fd9065eaf02
Step 2/4 : ADD . /app
---> cb1e74c524af
Step 3/4 : COPY cmd.sh /cmd.sh
---> e3dbbbd57ddf
Step 4/4 : CMD ["sh", "-c", "/cmd.sh"]
---> Running in 5fcb5e771266
Removing intermediate container 5fcb5e771266
---> 7e398a079fb0
Successfully built 7e398a079fb0
Successfully tagged large-file-context:latest
在下一步中,我們將演示如何提高構建的性能,
明智的做法是忽略.git目錄以及在映像中下載/構建的依賴項,比如node_modules,在Docker容器中運行的應用程式永遠不會使用它們,只會增加構建程序的開銷,
6.3 優化構建
以同樣的方式,我們使用.dockerignore檔案來排除敏感檔案,我們可以使用它來排除我們不想在構建期間發送到Docker構建背景關系的檔案,
要加快構建速度,只需在忽略檔案中包含大檔案的檔案名,
echo big-temp-file.img >> .dockerignore
當我們重建影像時,它將會快得多,因為它不需要復制100M文件,
$ docker build -t no-large-file-context .
Sending build context to Docker daemon 4.096kB
Step 1/4 : FROM alpine
---> 3fd9065eaf02
Step 2/4 : ADD . /app
---> Using cache
---> 4a1be3423c29
Step 3/4 : COPY cmd.sh /cmd.sh
---> Using cache
---> e30db2162cca
Step 4/4 : CMD ["sh", "-c", "/cmd.sh"]
---> Using cache
---> 4d4964ddbb00
Successfully built 4d4964ddbb00
Successfully tagged no-large-file-context:latest
當忽略.git這樣的大目錄時,這種優化會產生更大的影響,
7. 容器持久化資料
資料容器是唯一負責存盤/管理資料的容器,
與其他容器一樣,它們由主機系統管理,然而,當您執行docker ps命令時,它們不會運行,
要創建資料容器,我們首先要創建一個具有知名名稱的容器以供將來參考,我們使用busybox作為基礎,因為它體積小,重量輕,以防我們想要探索和移動容器到另一個主機,
在創建容器時,我們還提供了一個-v選項來定義其他容器讀取/保存資料的位置,
$ docker create -v /config --name dataContainer busybox
容器就緒后,我們現在可以將檔案從本地客戶端目錄復制到容器中,
下面的命令將config.conf檔案復制到dataContainer和config.conf目錄中,
$ docker cp config.conf dataContainer:/config/
現在我們的Data Container有了配置,我們可以在啟動需要組態檔的依賴容器時參考該容器,
使用——volumes-from <container>選項,我們可以使用正在啟動的容器中來自其他容器的掛載卷,在這種情況下,我們將啟動一個Ubuntu容器,它參考了我們的資料容器,當我們列出config目錄時,它將顯示來自附加容器的檔案,
$ docker run --volumes-from dataContainer ubuntu ls /config
config.conf
如果/config目錄已經存在,那么volumes-from將被覆寫并成為所使用的目錄,可以將多個卷映射到一個容器,
如果我們想將Data Container移動到另一臺機器,那么我們可以將其匯出到.tar檔案,
$ docker export dataContainer > dataContainer.tar
命令docker import dataContainer.tar會將資料容器匯入到docker中,
8. 容器之間的交流
連接到容器最常見的場景是應用程式連接到資料存盤,創建鏈接時的關鍵方面是容器的名稱,所有容器都有名稱,但為了在處理鏈接時更容易一些,為所連接的源容器定義一個友好的名稱是很重要的,
運行一個友好的名稱為redis-server的redis服務器,我們將在下一步連接它,這將是源容器,
$ docker run -d --name redis-server redis
Redis是一個快速的、開源的鍵值資料存盤,
要連接到源容器,在啟動新容器時使用--link <container-name|id>:<alias>選項,容器名參考上一步中定義的源容器,而別名定義主機的友好名稱,
通過設定別名,我們可以將應用程式的配置方式與基礎設施的呼叫方式分開,這意味著應用程式配置在連接到其他環境時不需要更改,
在這個例子中,我們打開一個鏈接到redis-server的Alpine容器,我們已經將別名定義為redis,當一個鏈接被創建時,Docker將做兩件事,
首先,Docker將基于鏈接到容器的環境變數設定一些環境變數,這些環境變數為您提供了一種通過已知名稱參考埠和IP地址等資訊的方法,
可以使用env命令輸出所有環境變數,例如:
$ docker run --link redis-server:redis alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=bae49bf11c01
REDIS_PORT=tcp://172.18.0.2:6379
REDIS_PORT_6379_TCP=tcp://172.18.0.2:6379
REDIS_PORT_6379_TCP_ADDR=172.18.0.2
REDIS_PORT_6379_TCP_PORT=6379
REDIS_PORT_6379_TCP_PROTO=tcp
REDIS_NAME=/angry_franklin/redis
REDIS_ENV_GOSU_VERSION=1.12
REDIS_ENV_REDIS_VERSION=6.2.5
REDIS_ENV_REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.5.tar.gz
REDIS_ENV_REDIS_DOWNLOAD_SHA=4b9a75709a1b74b3785e20a6c158cab94cf52298aa381eea947a678a60d551ae
HOME=/root
通過創建鏈接,您可以以與在您的網路中運行的服務器相同的方式ping源容器,
$ docker run --link redis-server:redis alpine ping -c 1 redis
PING redis (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.202 ms
--- redis ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.202/0.202/0.202 ms
通過創建鏈接,應用程式可以以通常的方式與源容器進行連接和通信,而無需考慮兩個服務都運行在容器中這一事實,
這是一個簡單的node.js應用程式,它使用主機名redis連接到redis,
$ docker run -d -p 3000:3000 --link redis-server:redis katacoda/redis-node-docker-example
發送一個HTTP請求到應用程式將存盤請求在Redis和回傳一個計數,如果發出多個請求,就會看到計數器的遞增,因為條目被持久化了,
$ curl ip:3000
This page was generated after talking to redis.
Application Build: 1
Total requests: 1
IP count:
::ffff:172.17.0.33: 1
$ curl ip:3000
This page was generated after talking to redis.
Application Build: 1
Total requests: 2
IP count:
::ffff:172.17.0.33: 2
以同樣的方式,您可以將源容器連接到應用程式,也可以將它們連接到自己的CLI工具,
下面的命令將啟動一個redis -cli工具的實體,并通過它的別名連接到redis服務器,
$ docker run -it --link redis-server:redis redis redis-cli -h redis
redis:6379> info
KEYS *命令將輸出當前存盤在源redis容器中的內容,
9. Docker 網路
9.1 創建網路
第一步是使用CLI創建網路,這個網路將允許我們附加多個容器,這些容器將能夠發現彼此,在本例中,我們將從創建一個后端網路開始,所有連接到我們后端的容器都將在這個網路上,
$ docker network create backend-network
89109e8de51aee15171ac6ec7257af040aecc66906777acfbbe88a715dcdb9d4
當我們啟動新的容器時,我們可以使用--net屬性來分配它們應該連接到哪個網路,
$ docker run -d --name=redis --net=backend-network redis
9.2 連接網路
與使用鏈接不同,docker網路的行為類似于傳統網路,節點可以附加/分離,
首先你會注意到Docker不再分配環境變數或更新容器的hosts檔案,使用下面的兩個命令,您會注意到它不再提到其他容器,
$ docker run --net=backend-network alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d566ff9c9a14
HOME=/root
$ docker run --net=backend-network alpine cat /etc/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.19.0.3 97c3a236a7e6
相反,容器可以通過Docker中的嵌入式DNS服務器進行通信,這個DNS服務器通過IP 127.0.0.11分配給所有容器,并在resolv.conf檔案中設定,
$ docker run --net=backend-network alpine cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
當容器試圖通過眾所周知的名稱(如Redis)訪問其他容器時,DNS服務器將回傳正確的容器的IP地址,在這種情況下,Redis的完全限定名將是Redis .backend-network,
$ docker run --net=backend-network alpine ping -c1 redis
PING redis (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.324 ms
--- redis ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.324/0.324/0.324 ms
9.3 連接兩個容器
Docker支持多個網路和容器同時連接到多個網路,
例如,讓我們用Node.js應用程式創建一個單獨的網路,它與我們現有的Redis實體通信,
第一個任務是以同樣的方式創建一個新的網路,
$ docker network create frontend-network
37e9702dd8f695f515b988beddd1cf4d4f7b38447a4e4177d98fcf96231321b2
當使用connect命令時,可以將現有容器附加到網路上,
$ docker network connect frontend-network redis
當我們啟動web服務器時,考慮到它連接到同一個網路,它將能夠與我們的Redis實體通信,
$ docker run -d -p 3000:3000 --net=frontend-network katacoda/redis-node-docker-example
$ curl ping:3000
This page was generated after talking to redis.
Application Build: 1
Total requests: 1
IP count:
::ffff:172.17.0.51: 1
$ curl ping:3000
This page was generated after talking to redis.
Application Build: 1
Total requests: 2
IP count:
::ffff:172.17.0.51: 2
9.4 Create Aliases
使用docker網路時仍然支持鏈接,并提供了一種方法來定義容器名的別名,這將為容器提供一個額外的DNS條目名稱和被發現的方式,當使用--link時,嵌入式DNS將保證本地化查找結果只在使用--link的容器上,
另一種方法是在將容器連接到網路時提供別名,
下面的命令將用db的別名將我們的Redis實體連接到前端網路,
docker network create frontend-network2
docker network connect --alias db frontend-network2 redis
當容器試圖通過名稱db訪問服務時,他們將得到我們的Redis容器的IP地址,
$ docker run --net=frontend-network2 alpine ping -c1 db
PING db (172.21.0.2): 56 data bytes
64 bytes from 172.21.0.2: seq=0 ttl=64 time=0.170 ms
--- db ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.170/0.170/0.170 ms
9.5 斷開容器連接
創建好網路后,我們可以使用CLI來探索細節,下面的命令將列出我們主機上的所有網路,
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
89109e8de51a backend-network bridge local
6fe697227a58 bridge bridge local
37e9702dd8f6 frontend-network bridge local
b0a9dbbb0bab frontend-network2 bridge local
fa054a9af353 host host local
f50397115ef2 none null local
然后,我們可以探索網路,查看連接的容器及其IP地址,
$ docker network inspect frontend-network
[
{
"Name": "frontend-network",
"Id": "37e9702dd8f695f515b988beddd1cf4d4f7b38447a4e4177d98fcf96231321b2",
"Created": "2021-09-28T12:52:03.799072129Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/16",
"Gateway": "172.20.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"80bb046b3ac46cd0efa7b66640e0eaf297f65d39ad080193758f6d19d10c6d3e": {
"Name": "redis",
"EndpointID": "a8fb389c6672f0ab173c39b273064922ea252d3fce5094d1864fb2b36cdfa25d",
"MacAddress": "02:42:ac:14:00:02",
"IPv4Address": "172.20.0.2/16",
"IPv6Address": ""
},
"b28cbfb9b69e68d070f8fef5ddd2bbabb6e410ebeb0905b917dfd8eb103d85a3": {
"Name": "inspiring_archimedes",
"EndpointID": "80afb91170ab08def612373bf78be60609dbb173bb3d57b3abda7b578ff001cc",
"MacAddress": "02:42:ac:14:00:03",
"IPv4Address": "172.20.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
下面的命令斷開redis容器與前端網路的連接,
$ docker network disconnect frontend-network redis
10. 使用卷持久化存盤
10.1 --volumes,-v
在啟動容器時創建和分配Docker卷,資料卷允許將主機目錄映射到容器,以便共享資料,
這種映射是雙向的,它允許從容器內部訪問存盤在主機上的資料,這還意味著行程在容器內保存的資料會持久化到主機上,
這個例子將使用Redis作為一種持久化資料的方法,在下面啟動一個Redis容器,并使用-v引數創建一個資料卷,它指定容器中保存到/data目錄中的任何資料都應該持久化到主機的/docker/redis-data目錄中,
docker run -v /docker/redis-data:/data --name r1 -d redis redis-server --appendonly yes
我們可以使用下面的命令將資料輸送到Redis實體中,
$ cat data | docker exec -i r1 redis-cli --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1
Redis會將這些資料保存到磁盤,在主機上,我們可以調查應該包含Redis資料檔案的映射直接,
$ ls /docker/redis-data
appendonly.aof
這個目錄可以掛載到第二個容器,一種用法是讓Docker容器對資料執行備份操作,
$ docker run -v /docker/redis-data:/backup ubuntu ls /backup
appendonly.aof
10.2 --volumes-from
資料卷映射給主機有利于資料持久化,然而,要從另一個容器訪問它們,您需要知道容易出錯的確切路徑,
另一種方法是使用-volumes-from,該引數將映射卷從源容器映射到正在啟動的容器,
在這個例子中,我們將Redis容器的卷映射到Ubuntu容器,/data目錄只存在于我們的Redis容器中,然而,因為-volumes-from,Ubuntu容器可以訪問資料,
$ docker run --volumes-from r1 -it ubuntu ls /data
appendonly.aof
這允許我們訪問來自其他容器的卷,而不必關心它們是如何在主機上持久化的,
10.3 只讀卷
掛載卷使容器對目錄具有完全的讀和寫訪問權限,通過對掛載目錄添加“ro”權限,可以對該目錄設定只讀權限,如果容器試圖修改目錄中的資料,則會出錯,
$ docker run -v /docker/redis-data:/data:ro -it ubuntu rm -rf /data
rm: cannot remove '/data/appendonly.aof': Read-only file system
11. 管理日志
當你啟動一個容器時,Docker將跟蹤行程的Standard Out和Standard Error輸出,并通過客戶端使它們可用,
在后臺,有一個名為Redis -server的Redis實體運行,通過使用Docker客戶端,我們可以訪問標準輸出和標準錯誤輸出
$ docker logs redis-server
默認情況下,Docker日志使用JSON -file記錄器輸出,這意味著輸出存盤在主機上的JSON檔案中,這可能會導致大檔案填滿磁盤,因此,您可以更改日志驅動程式以移動到不同的目的地,
Syslog日志驅動程式將所有容器日志寫到主機的中央Syslog日志中,syslog是一種廣泛使用的訊息記錄標準,它允許生成訊息的軟體、存盤訊息的系統以及報告和分析訊息的軟體分離,
此日志驅動程式設計用于外部系統收集和聚合syslog日志,下面的命令將redis日志重定向到syslog
$ docker run -d --name redis-syslog --log-driver=syslog redis
如果你試圖使用客戶端查看日志,你會收到錯誤FATA[0000] "logs"命令只支持"json-file"日志驅動程式,
相反,您需要通過syslog流訪問它們,
第三個選項是禁用容器上的日志記錄,這對于在日志記錄中非常冗長的容器特別有用,
當容器啟動時,只需將log-driver設定為none,不會記錄任何輸出,
docker run -d --name redis-none --log-driver=none redis
inspect命令允許您識別特定容器的日志記錄配置,下面的命令將為每個容器輸出LogConfig部分,
$ docker inspect --format '{{ .HostConfig.LogConfig }}' redis-server
{json-file map[]}
$ docker inspect --format '{{ .HostConfig.LogConfig }}' redis-syslog
{syslog map[]}
$ docker inspect --format '{{ .HostConfig.LogConfig }}' redis-none
{none map[]}
12. 容器運行策略
Docker認為任何帶有非零退出代碼的容器都崩潰了,默認情況下,崩潰的容器將保持停止狀態,
我們已經創建了一個特殊的容器,它輸出一條訊息,然后使用代碼1退出,以模擬崩潰,
docker run -d --name restart-default scrapbook/docker-restart-example
如果列出所有的容器,包括stopped,您將看到容器已經崩潰
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
39ef0a052236 scrapbook/docker-restart-example "/bin/sh -c ./launch…" 5 seconds ago Exited (1) 2 seconds ago restart-default
而日志將輸出我們的訊息,這在現實生活中可能會提供幫助我們診斷問題的資訊,
$ docker logs restart-default
Tue Sep 28 13:41:39 UTC 2021 Booting up...
根據您的場景,重新啟動失敗的行程可能會糾正這個問題,Docker可以在停止嘗試之前自動重試啟動Docker特定次數,
他的選項--restart=on-failure:#允許你說Docker應該重試多少次,在下面的示例中,Docker將在停止之前重新啟動容器三次,
$ docker run -d --name restart-3 --restart=on-failure:3 scrapbook/docker-restart-example
$ docker logs restart-3
Tue Sep 28 13:42:44 UTC 2021 Booting up...
Tue Sep 28 13:42:46 UTC 2021 Booting up...
Tue Sep 28 13:42:50 UTC 2021 Booting up...
Tue Sep 28 13:42:53 UTC 2021 Booting up...
最后,Docker總是可以重新啟動失敗的容器,在這種情況下,Docker將一直嘗試,直到容器被明確告知停止,
例如,當容器崩潰時,使用always標志自動重新啟動容器
$ docker run -d --name restart-always --restart=always scrapbook/docker-restart-example
$ docker logs restart-always
13. 容器元資料與標簽
當容器通過docker運行啟動時,標簽可以被附加到容器上,一個容器可以在任何時候有多個標簽,
注意,在這個例子中,因為我們使用的標簽是用于CLI,而不是一個自動化的工具,所以我們沒有使用DNS表示法格式,
要添加單個標簽,可以使用l =<value>選項,下面的示例為容器分配了一個名為user的帶有ID的標簽,這將允許我們查詢與該特定用戶相關的所有正在運行的容器,
docker run -l user=12345 -d redis
如果您正在添加多個標簽,那么這些標簽可以來自外部檔案,檔案的每一行都需要有一個標簽,然后這些標簽將被附加到正在運行的容器上,
這一行在檔案中創建了兩個標簽,一個用于用戶,另一個用于分配角色,
echo 'user=123461' >> labels && echo 'role=cache' >> labels
The --label-file=<filename> option will create a label for each line in the file.
docker run --label-file=labels -d redis
標簽鏡像的作業方式與容器相同,但在構建鏡像時在Dockerfile中設定,當容器啟動時,鏡像的標簽將應用到容器實體,
在一個Dockerfile中,你可以使用label指令分配一個標簽,在標簽下面創建名為“剪貼簿”的供應商,
LABEL vendor=Katacoda
如果我們想要分配多個標簽,可以使用下面的格式,每行都有一個標簽,使用反斜杠("")連接,注意,我們使用的是與第三方工具相關的DNS標記格式,
LABEL vendor=Katacoda \
com.katacoda.version=0.0.5 \
com.katacoda.build-date=2016-07-01T10:47:29Z \
com.katacoda.course=Docker
標簽和元資料只有在您可以稍后查看/查詢它們時才有用,查看特定容器或鏡像的所有標簽的第一種方法是使用docker inspect,
環境已經為您創建了一個名為rd的容器和一個名為katacoda-label-example的映像,
通過提供運行容器的友好名稱或哈希id,您可以查詢它的所有元資料,
docker inspect rd
使用-f選項,您可以過濾JSON回應,只針對我們感興趣的標簽部分,
$ docker inspect -f "{{json .Config.Labels }}" rd
{"com.katacoda.created":"automatically","com.katacoda.private-msg":"magic","user":"scrapbook"}
檢查鏡像的方法是一樣的,但是JSON格式略有不同,將其命名為ContainerConfig而不是Config,
$ docker inspect -f "{{json .ContainerConfig.Labels }}" katacoda-label-example
{"com.katacoda.build-date":"2015-07-01T10:47:29Z","com.katacoda.course":"Docker","com.katacoda.private-msg":"HelloWorld","com.katacoda.version":"0.0.5","vendor":"Katacoda"}
這些標簽將保留,即使鏡像已被取消標記,當鏡像未被標記時,它的名稱將為<none>.
雖然檢查單個容器和映像可以為您提供更多的背景關系,但在運行數千個容器的生產中,限制對您感興趣的容器的回應是很有用的,
docker ps命令允許您根據標簽名稱和值指定一個過濾器,例如,下面的查詢將回傳所有具有值katacoda的用戶標簽鍵的容器,
$ docker ps --filter "label=user=scrapbook"
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c0d9b5b90ed redis "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 6379/tcp rd
基于構建鏡像時使用的標簽,可以對鏡像應用相同的過濾方法,
$ docker images --filter "label=vendor=Katacoda"
REPOSITORY TAG IMAGE ID CREATED SIZE
katacoda-label-example latest 33a1689f8704 8 minutes ago 112MB
標簽不僅適用于影像和容器,也適用于Docker Daemon本身,當您啟動守護行程的實體時,可以為它分配標簽,以幫助確定應該如何使用它,例如,它是開發服務器還是生產服務器,或者它是否更適合運行資料庫等特定角色,
docker -d \
-H unix:///var/run/docker.sock \
--label com.katacoda.environment="production" \
--label com.katacoda.storage="ssd"
14. 負載平衡的容器
14.1 NGINX Proxy
在這個場景中,我們希望運行一個NGINX服務,它可以在加載新容器時動態發現和更新它的負載平衡配置,我們已經創建了nginx-proxy,
Nginx-proxy接受HTTP請求,并根據請求主機名將請求代理到相應的容器,這對用戶是透明的,不會產生任何額外的性能開銷,
在啟動代理容器時,需要配置三個鍵屬性,
- 第一種方法是使用
-p 80:80將容器系結到主機上的80埠,這確保了所有HTTP請求都由代理處理, - 第二步是掛載
docker.sock檔案,這是一個與運行在主機上的Docker守護行程的連接,允許容器通過API訪問它的元資料,NGINX-proxy使用這個來監聽事件,然后根據容器的IP地址更新NGINX配置,掛載檔案的作業方式與使用-v /var/run/docker.sock:/tmp/docker.sock:ro的目錄相同,指定:ro將訪問限制為只讀, - 最后,我們可以設定一個可選的
-e DEFAULTHOST=<domain>,如果傳入的請求沒有生成任何指定的主機,則該容器將處理請求,這使您能夠在一臺機器上運行多個具有不同域的網站,并可回傳到一個已知的網站,
使用下面的命令啟動nginx-proxy,
docker run -d -p 80:80 -e DEFAULT_HOST=proxy.example -v /var/run/docker.sock:/tmp/docker.sock:ro --name nginx jwilder/nginx-proxy
:因為我們使用的是DEFAULT_HOST,所以任何傳入的請求都將被定向到已經分配了HOST代理的容器,
您可以使用curl http://ip向web服務器發出請求,由于我們沒有容器,它將回傳一個503錯誤,
$ curl http://ip
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>
14.2 單機
Nginx-proxy現在正在監聽Docker在啟動/停止時引發的事件,一個名為katacoda/docker-http-server的示例網站已經創建,它將回傳運行它的機器名,這允許我們測驗我們的代理是否按照預期作業,它的內部是一個PHP和Apache2應用程式,偵聽埠80,
為了讓Nginx-proxy開始向容器發送請求,你需要指定VIRTUAL_HOST環境變數,這個變數定義了請求來自的域,并且應該由容器處理,
在這個場景中,我們將把我們的HOST設定為與DEFAULT_HOST匹配,這樣它將接受所有請求,
docker run -d -p 80 -e VIRTUAL_HOST=proxy.example katacoda/docker-http-server
有時,NGINX需要幾秒鐘的時間來重新加載,但如果我們使用curl http://ip執行一個請求到我們的代理,那么請求將由我們的容器處理,
$ curl http://ip
<h1>This request was processed by host: a6b180c03df3</h1>
14.2 集群
現在,我們已經成功地創建了一個容器來處理HTTP請求,如果我們用相同的VIRTUAL_HOST啟動第二個容器,那么nginx-proxy將在一個回圈負載平衡的場景中配置系統,這意味著第一個請求將發送到一個容器,第二個請求將發送到第二個容器,然后回圈重復,您可以運行的節點數量沒有限制,
使用與前面相同的命令啟動第二個容器或者第三個
docker run -d -p 80 -e VIRTUAL_HOST=proxy.example katacoda/docker-http-server
docker run -d -p 80 -e VIRTUAL_HOST=proxy.example katacoda/docker-http-server
如果使用curl http://ip執行對代理的請求,則請求將由第一個容器處理,第二個HTTP請求將回傳不同的機器名,這意味著它是由第二個容器處理的,
$ curl http://ip
<h1>This request was processed by host: ac2c0ef08655</h1>
$ curl http://ip
<h1>This request was processed by host: a6b180c03df3</h1>
當NGINX -proxy為我們自動創建和配置NGINX時,如果你對最終的配置感興趣,你可以使用docker exec輸出完整的組態檔,如下所示,
docker exec nginx cat /etc/nginx/conf.d/default.conf
關于何時重新加載配置的附加資訊可以在使用
docker logs nginx
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/304393.html
標籤:其他
上一篇:Redis新舊復制
下一篇:MySQL高性能索引策略
