(三)Docker高級應(yīng)用

Docker中的文件和數(shù)據(jù)

Docker鏡像通過UnionFS進(jìn)行分層存儲(可以通過docker history查看),一個Docker鏡像本質(zhì)上是一組文件,默認(rèn)存儲在/var/lib/docker/<storage-driver>中。

而Docker 容器其實(shí)是在鏡像的最上層加了一層讀寫層,通常也稱為容器層。在運(yùn)行中的容器里所做的改動,如寫新文件、修改已有文件、刪除文件等操作其實(shí)都寫到了容器層。容器層刪除了,最上層的讀寫層跟著也刪除了,改動自然也丟失了。

若要持久化這些改動,一是通過 docker commit <containerId> [repository[:tag]]將當(dāng)前容器保存成為一個新鏡像。若想將持久化數(shù)據(jù),或是多個容器間共享數(shù)據(jù),需將數(shù)據(jù)存儲主機(jī)的文件系統(tǒng)中。

types-of-mounts.png

數(shù)據(jù)卷(Volume) - 為什么是特殊的目錄?

  • 處于UFS(Union File System)之外
  • 主機(jī)文件系統(tǒng)中的普通目錄
  • 在卷上的I/O性能應(yīng)與主機(jī)上的完全相同
  • 卷的內(nèi)容不包含在Docker鏡像中
  • 任何對卷內(nèi)容的修改不是鏡像的一部分
  • 可被多個容器共享和重用
  • 持久化數(shù)據(jù)(即使容器已被刪除)

如何初始化卷?

volume-data-container.png
  • 從主機(jī)上掛載卷: 與容器共享主機(jī)目錄

    • Docker卷允許在主機(jī)操作系統(tǒng)和容器之間共享任意目錄/文件
    • 需要確保在主機(jī)操作系統(tǒng)上外部配置文件是可訪問的
    • 動態(tài)伸縮的約束
  • 從其他容器掛載卷(數(shù)據(jù)容器):數(shù)據(jù)容器會作為一個接口, 向其他容器提供訪問卷的途徑

    • 容器可處理一組卷
    • 能限制訪問權(quán)限,如只讀或讀寫: docker run --volume-from some-container
    • 在多個容器間共享卷
    • 卷內(nèi)容會保持同步
    • 底層使用相同的目錄

使用數(shù)據(jù)容器的場景?

  • 存儲持久化數(shù)據(jù)庫

    • 啟動數(shù)據(jù)容器
    • 啟動基于數(shù)據(jù)庫鏡像的數(shù)據(jù)庫容器
    • 外部化數(shù)據(jù)目錄 - 通過從數(shù)據(jù)容器掛載卷
    • 安排數(shù)據(jù)容器的備份計劃
  • 配置文件

  • 數(shù)據(jù)文件

實(shí)踐操作

  1. 訪問Docker Host虛擬機(jī)

因?yàn)樵贛acOS上的Docker實(shí)際是運(yùn)行在一個虛擬環(huán)境下的,所以/var/lib/docker路徑無法在Mac上找到。我們需要連接到Docker Host虛擬機(jī),才能查看到Docker的默認(rèn)目錄。

$ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty
linuxkit-025000000001:~# ls /var/lib/docker/
builder     containers  network     plugins     swarm       trust
containerd  image       overlay2    runtimes    tmp         volumes
linuxkit-025000000001:~# ls /var/lib/docker/volumes/
metadata.db
  1. 在"主機(jī)"上掛載一個卷
# 創(chuàng)建匿名卷
$ docker volume create
4997a11f62aff64766a2ce490debf1770a0c3f29dae57326579fefd0c3668673
# 創(chuàng)建卷
$ docker volume create my-volume
my-volume

這樣創(chuàng)建的卷會默認(rèn)在/var/lib/docker/volumes/下,可以使用docker volume進(jìn)行統(tǒng)一管理

  1. 使用卷
# 啟動容器,掛載my-volume卷到容器的/data;
$ docker run -dit --name c-my-volume -v my-volume:/data busybox
a9ee3a9c98009a64865eb98da92f7bdd052634e44861f59d42051fe7015dbcd0
# 查看是否掛載成功;
$ docker exec c-my-volume ls | grep data
data
# 啟動容器,自動生成匿名卷并掛載到容器的/data;
$ docker run -dit --name c-anonymous -v :/data busybox
ede60b7d7c514ef3b8d5f4d94cfa994b19da7680f13a5539ac64948fe65deb0e
# 查看匿名卷對應(yīng)的目錄;
$ docker inspect -f {{.Mounts}} c-anonymous
  1. 向卷中寫入數(shù)據(jù)
  • 宿主機(jī)(Docker Host)向卷寫入數(shù)據(jù)
linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# touch test
linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# echo "Hello" > test
  • 容器卷中檢查修改文件
$ docker exec c-my-volume cat /data/test
Hello
$ docker exec -it c-my-volume sh
/ # echo "Hi" > /data/test
  • 宿主機(jī)(Docker Host)檢查
linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# cat test
Hi
  1. 使用數(shù)據(jù)容器
  • Dockerfile
FROM busybox
VOLUME /config # 聲明卷的掛載點(diǎn)
CMD ls /config
  • Shell
$ docker build -t data-container -f Dockerfile-2
# 創(chuàng)建數(shù)據(jù)容器
$ docker create --name data-container data-container
610dc7f0df53adec4a8ee02e7f8f2a4856a29f5be3007c1fb69e9863d7960af1
$ docker inspect -f {{.Mounts}} data-container
[{volume e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa /var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data /config local  true }]
# 啟動服務(wù)容器,引用數(shù)據(jù)容器
$ docker run -dit --name service-container --volumes-from data-container busybox
# 文件系統(tǒng)中修改
linuxkit-025000000001:/var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data# touch test
# 查看服務(wù)容器
$ docker exec service-container ls /config
  1. 運(yùn)行時添加文件
# 復(fù)制文件到docker容器中
$ touch test-2
$ docker cp test-2 service-container:/config/
# 文件系統(tǒng)中查看
linuxkit-025000000001:/var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data# ls
test    test-2

Mount掛載

文件掛載有多種方式,Volume卷只是提供了統(tǒng)一的管理方式。我們可以使用mount進(jìn)行自定義配置,包括綁定任意文件目錄,設(shè)置讀寫規(guī)則等。

  • --mount:由多個鍵值對組成,由逗號分隔,每一個由 <key>=<value> 元祖組成。鍵值對沒有順序。
    • type,可以是 bind,volume,tmpfs。
    • source,主機(jī)上的文件或目錄的路徑。可能用 src,source 指定。
    • destination,容器中的文件或目錄的路徑。可能用 destination,dst,target 指定。
    • readonly,如果存在,將更改 Propagation,可以是一個 rprivate。
    • consistency,如果存在,可以是 consistent,delegated 或 cached,只在 Mac 版有效。
    • --mount 標(biāo)志不支持 z 或 Z 修改 selinux。
  • -v 絕對路徑:容器內(nèi)路徑:默認(rèn)使用bind模式掛載目錄
  1. 直接掛載主機(jī)目錄
# 啟動容器,掛載本機(jī)目錄(絕對路徑)到容器的/data;
$ docker run -dit --name c-temp-path -v /temp:/data busybox
a9ee3a9c98009a64865eb98da92f7bdd052634e44861f59d42051fe7015dbcd0
# 查看是否掛載成功;
$ docker exec c-temp-path ls /data
Shared
username
$ docker inspect c-temp-path
...
"Mounts": [
    {
        "Type": "bind",
        "Source": "/Users",
        "Destination": "/data",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],
...

可以掛載任意路徑,但是無法用volume進(jìn)行管理。

卷插件

卷插件允許第三方數(shù)據(jù)管理解決方案接入
在不修改應(yīng)用的情況下,可以用另一個插件替換當(dāng)前插件

  • Flocker
  • Convoy
  • Blockbridge
  • GlusterFS
  • Netshare
  • Openstorage

卷的應(yīng)用場景

配置文件: 一次構(gòu)建,多處部署

  • 將配置文件植入到容器
    • 在 Dockerfile 中使用 'COPY' 指令
    • 在鏡像構(gòu)建時使用 'RUN' 指令修改配置
    • 最簡方案(靜態(tài)配置的情況下)
    • 任何對配置文件的修改都需要重新構(gòu)建鏡像
  • 使用環(huán)境變量, 動態(tài)傳入到容器
    • 當(dāng)啟動容器時傳入環(huán)境變量: docker run -e USERNAME=zzz PASSWORD=...
    • 簡單配置情況下運(yùn)行良好
  • 查詢鍵-值存儲
    • 利用鍵-值存儲獲取配置信息
    • 很多可選項(xiàng),如 consul, etcd, zookeeper
    • 使得配置更加動態(tài)化
    • 引入外部依賴,可用性更加重要
  • 從主機(jī)上掛載卷
  • 從其他容器掛載卷

Tips

  • Volumes!=Persistance(卷并不意味著持久化)
  • 卷并不會被垃圾回收
  • 針對有狀態(tài)的容器(如數(shù)據(jù)庫)和相應(yīng)的數(shù)據(jù)容器,可以使用相同的鏡像
  • 為數(shù)據(jù)容器制定備份計劃
  • 將帶有敏感數(shù)據(jù)的容器放在的當(dāng)前主機(jī)上
  • 盡可能緩存鏡像,因?yàn)橄螺d很耗時

網(wǎng)絡(luò)管理

docker-networks.png

Docker 為我們提供了四種不同的網(wǎng)絡(luò)模式

  • bridge:未指定網(wǎng)絡(luò)時, 容器默認(rèn)在bridge網(wǎng)絡(luò)下
  • container:CONTAINER_NAME:重用某容器的網(wǎng)絡(luò)配置(ip/mac),等同于在同一容器內(nèi)
  • host:(唯一)共享了宿主機(jī)的網(wǎng)絡(luò), 使用相同的ip
  • none:(唯一)關(guān)閉所有網(wǎng)絡(luò)連接

docker network ls中包含了默認(rèn)的host、none和一個bridge網(wǎng)絡(luò),當(dāng)啟動的容器沒有指定網(wǎng)絡(luò)時,會默認(rèn)加入到bridge網(wǎng)絡(luò)中

Bridge 網(wǎng)橋模式

net-bridge.png
  • 啟動Docker時會在主機(jī)上創(chuàng)建一個名為docker0的虛擬網(wǎng)橋接口
  • docker0 會為每一個容器分配一個新的 IP 地址并將 docker0 的 IP 地址設(shè)置為默認(rèn)的網(wǎng)關(guān)
  • docker0 通過 iptables 中的配置與宿主機(jī)器上的網(wǎng)卡相連,所有符合條件的請求都會通過 iptables 轉(zhuǎn)發(fā)到 docker0 并由網(wǎng)橋分發(fā)給對應(yīng)的機(jī)器
  • 端口映射通過修改 iptables 再將對端口的訪問重定向到 docker0,實(shí)現(xiàn)對容器的訪問


    network-bridge-forward.png
# 啟動容器并映射端口
$ docker run -dit --name bridge-container -p 5000 busybox
cb52033e0b8f9c5a8e4c18e5a1fe0ac6d01809393575c1c85b3cdd5aacea437f
# 查看容器服務(wù)的端口
$ docker port bridge-container 
5000/tcp -> 0.0.0.0:32771
# 查看容器的IP地址
$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' bridge-container
172.17.0.2
# 查看默認(rèn)網(wǎng)絡(luò)配置和其中的容器
$ docker network inspect bridge
...
"IPAM": {
    ...
    "Config": [
        {
            "Subnet": "172.17.0.0/16",
            "Gateway": "172.17.0.1"
        }
    ]
},
"Containers": {
    "cb52033e0b8f9c5a8e4c18e5a1fe0ac6d01809393575c1c85b3cdd5aacea437f": {
        "Name": "bridge-container",
        "EndpointID": "b4af63f885a783563aeabdb70806983cb519a497835209686954a599fb81bc7a",
        "MacAddress": "02:42:ac:11:00:02",
        "IPv4Address": "172.17.0.2/16",
        "IPv6Address": ""
    }
},
...

網(wǎng)絡(luò)和Links

  • docker run --link <target container> <image>
    • Links只能對同一主機(jī)上的容器生效
    • 容器在重新部署時會斷開與其他容器的連接
    • 容器在創(chuàng)建后才能被相互Link

容器網(wǎng)絡(luò)模型 - 組件

container-network-model.png
  • 沙箱
    • 包含容器網(wǎng)絡(luò)堆棧配置信息:容器的接口、路由表和 DNS 設(shè)置
    • 可能包含多網(wǎng)絡(luò)的多個端點(diǎn):連接的每一個網(wǎng)絡(luò)都有一個不同的端點(diǎn)
  • 網(wǎng)絡(luò)
    • 一組端點(diǎn)之間能夠相互直接交流
    • 實(shí)現(xiàn)可以是Linux網(wǎng)橋或重疊網(wǎng)(Overlay)
  • 容器
    • 容器能夠作為任意一個或多個網(wǎng)絡(luò)的一部分
    • 能夠同時對接橋接網(wǎng)絡(luò)和重疊網(wǎng)絡(luò)
  • 在某個特定網(wǎng)絡(luò)下的所有容器都能夠自由地互相通信
  • 多個網(wǎng)絡(luò)有助于分散容器之間的流量傳輸
  • 多個端點(diǎn)允許一個容器加入到多個網(wǎng)絡(luò)中

用戶可創(chuàng)建的網(wǎng)絡(luò)有2種類型:

網(wǎng)絡(luò)實(shí)戰(zhàn)

  1. 默認(rèn)的bridge網(wǎng)絡(luò)也是網(wǎng)橋網(wǎng)絡(luò),應(yīng)用場景相同。
  1. 創(chuàng)建新的Bridge網(wǎng)絡(luò)
# 創(chuàng)建2個Bridge網(wǎng)絡(luò)并創(chuàng)建容器連接網(wǎng)絡(luò)
$ docker network create my-net1
$ docker run -itd --net=my-net1 --name service0 busybox
$ docker run -itd --net=my-net1 --name service1 busybox
$ docker network create my-net2
$ docker run -itd --net=my-net2 --name service2 busybox
# 查看網(wǎng)絡(luò), 獲取容器ip地址等信息
$ docker network ls
$ docker network inspect my-net1
$ docker network inspect my-net2
  1. 容器進(jìn)行網(wǎng)絡(luò)訪問
# 同網(wǎng)絡(luò)容器Ping:成功
$ docker exec service0 ping service1 # 172.18.0.2
# 不同網(wǎng)絡(luò)容器Ping:失敗
$ docker exec service1 ping service2
$ docker exec service1 ping 172.19.0.2 # 無連接
  1. 為容器配置新的網(wǎng)絡(luò)
# 將容器連接到另一個網(wǎng)絡(luò)
$ docker network connect my-net2 service1
$ docker network inspect my-net2
# 此時service1在2個網(wǎng)絡(luò)各自有一個ip
$ docker exec service0 ping service1  # 172.18.0.3
$ docker exec service1 ping service0  # 172.18.0.2
$ docker exec service1 ping service2  # 172.19.0.2
$ docker exec service2 ping service1  # 172.19.0.3

此時,service1容器中的沙箱就具有2個Endpoints,對應(yīng)不同的網(wǎng)絡(luò)。訪問時會根據(jù)網(wǎng)絡(luò)配置決定應(yīng)該走哪一個網(wǎng)絡(luò)進(jìn)行傳輸。

  1. 使用Host網(wǎng)絡(luò)
$ docker run -itd --net=host --name service-host busybox
$ docker inspect service-host

這時候容器就和本機(jī)運(yùn)行的進(jìn)程相同了,只要暴露了端口就能通過localhost:port進(jìn)行訪問。

  1. 使用None網(wǎng)絡(luò):即關(guān)閉所有網(wǎng)絡(luò)連接

網(wǎng)絡(luò)插件

  • 允許第三方的容器網(wǎng)絡(luò)方案連接到容器網(wǎng)絡(luò)中
  • 降低了不同類型主機(jī)上容器通信的難度
  • 擴(kuò)展由Docker提供的核心網(wǎng)絡(luò)功能

可用的網(wǎng)絡(luò)插件來自于:

  • Weave
  • Project Calico
  • Nuage Networks
  • Cisco
  • VMware
  • Microsoft
  • Midokura

容器安全

在容器中的root權(quán)限對應(yīng)宿主機(jī)的一個普通用戶, 但當(dāng)容器開啟--privileged特權(quán)后, 就可以使用root對宿主機(jī)進(jìn)行操作

# 重要文件映射到容器中
$ docker run -v /:/hostfs busybox cat /hostfs/etc/paths 
        # 部分文件不可見
# 容器中修改主機(jī)重要文件
$ docker run -it -v /:/hostfs busybox touch /hostfs/threat-on-the-way
        # 無法創(chuàng)建

安全最佳實(shí)踐

  • 主機(jī):
    • 保持內(nèi)核及時更新
    • 增強(qiáng)主機(jī)保護(hù)
    • 保持Docker及時更新
  • Docker守護(hù)進(jìn)程:
    • 只允許受信用戶控制Docker守護(hù)進(jìn)程
    • 不使用不受信的鏡像倉庫
    • 必要時請為Docker守護(hù)進(jìn)程應(yīng)用TLS認(rèn)證
    • 限制容器之間的網(wǎng)絡(luò)通信
  • 鏡像:
    • 在Dockerfile中為容器創(chuàng)建一個非root用戶
    • 以非root用戶運(yùn)行容器進(jìn)程
    • 只使用受信的基礎(chǔ)鏡像
    • 僅安裝必要的包
    • 重新構(gòu)建鏡像時需要包含安全補(bǔ)丁
  • 容器運(yùn)行時
    • 限制容器使用Linux內(nèi)核能力
    • 不要使用privileged容器
    • 限制容器上的資源使用
    • 指定容器重啟策略為on-failure
    • 使用AppArmor/SELinux保證額外的安全層
  • 其他
    • 為Docker掛載點(diǎn)創(chuàng)建單獨(dú)的分區(qū)
    • 不要到產(chǎn)品環(huán)境中使用任何開發(fā)者工具(boot2docker, kinematic)
    • 建立本地倉庫鏡像
    • 使用供應(yīng)商最支持的存儲驅(qū)動程序
    • aufs是唯一的允許容器共享執(zhí)行文件的存儲驅(qū)動,但可能會導(dǎo)致嚴(yán)重的內(nèi)核崩潰
    • 為Docker守護(hù)進(jìn)程設(shè)置受限的控制資源權(quán)限(ulimit)
    • 由最小基礎(chǔ)鏡像開始(Busybox, Alpine)

多主機(jī)部署和管理

Docker本身只關(guān)注單主機(jī)(Docker Host),對鏡像、容器進(jìn)行管理。在多主機(jī)部署時,主要是利用其它組件、工具進(jìn)行服務(wù)發(fā)現(xiàn)、服務(wù)注冊、網(wǎng)絡(luò)傳輸。只是由外部程序來接管多主機(jī)的協(xié)調(diào)工作,對于Docker來說是透明的。例如:

  • 將Docker容器放在Host網(wǎng)絡(luò)/映射Docker容器的端口到Host上
  • 利用服務(wù)發(fā)現(xiàn)(Consul/Eureka)自動發(fā)布、獲取IP:PORT
  • 利用Http通信
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自 http://blog.opskumu.com/docker.html 一、Docker 簡介 Docke...
    極客圈閱讀 10,760評論 0 120
  • Docker — 云時代的程序分發(fā)方式 要說最近一年云計算業(yè)界有什么大事件?Google Compute Engi...
    ahohoho閱讀 15,866評論 15 147
  • 五、Docker 端口映射 無論如何,這些 ip 是基于本地系統(tǒng)的并且容器的端口非本地主機(jī)是訪問不到的。此外,除了...
    R_X閱讀 1,969評論 0 7
  • 但我們的努力可以讓我們做一個厲害的普通人。 2017年7月30日 星期天 小雨 夢想還是要有的,萬一有一天實(shí)現(xiàn)了呢...
    肥狗輝閱讀 1,437評論 2 4
  • 兩個美女周日下午經(jīng)過了一次教師資格證的考試洗禮,感覺成熟了許多。我感覺我們的教師成長隊伍中又多了一個梯隊,但...
    泥腿子劉艷閱讀 221評論 0 1

友情鏈接更多精彩內(nèi)容