歡迎光臨
每天分享高質量文章

Docker 配置與實踐清單

文章對來官方文件及 Docker Links[1] 中鏈接內容進行歸檔整理,包含了日常工作中常用的 Docker 概念與命令,如果對於 Linux 常用操作尚不熟悉的可以參考 Linux Commands CheatSheet[2]。
Docker 是一個開源的應用容器引擎,基於 Go 語言 並遵從 Apache2.0 協議開源。Docker 可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中,然後發佈到任何流行的 Linux 機器上。
虛擬機最大的瓶頸在於其需要特殊硬體虛擬化技術支持,並且攜帶完整的操作系統;而 Docker 沒有硬體虛擬化,可以運行在物理機、虛擬機,甚至嵌套運行在 Docker 容器內,並且其不攜帶操作系統的,會輕巧很多。在呼叫宿主機的記憶體、CPU、磁盤等等資源時,虛擬機是利用 Hypervisor 去虛擬化記憶體,整個呼叫過程是虛擬記憶體->虛擬物理記憶體->真正物理記憶體,但是 Docker 是利用 Docker Engine 去呼叫宿主的的資源,這時候過程是虛擬記憶體->真正物理記憶體。
Docker 綜合運用了 Cgroup,Linux Namespace,Secomp capability,Selinux 等機制,在 Docker Internals CheatSheet 中我們會有詳細的討論,或者前往 Backend Boilerplate/Docker[3] 瀏覽常見服務/應用的 Docker 配置案例。
安裝與配置

Docker CE
這裡我們使用科大的 Docker CE 源[4]進行安裝:
# 更改 Ubuntu 預設源地址
$ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

# 安裝必備的系統命令
$ sudo apt-get install -y python-software-properties

$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

$ sudo add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

$ sudo apt-get update

$ apt-cache policy docker-ce # 列舉 docker-ce 版本

$ apt-get install docker-ce=17.03.2-ce....

Daemon Configuration
# 配置開機自啟動
$ sudo systemctl enable docker

#
 取消開機自啟動
$ sudo systemctl disable docker

我們還需要修改儲存路徑,指定鏡像儲存地址,允許遠程訪問;此時我們可以修改 systemd 中的配置檔案,也可以修改 /etc/docker/daemon.json,此處以修改服務為例:
# 使用 systemctl 命令列修改
$ sudo systemctl edit docker.service

# 或者查找配置地址並使用 Vim 修改
$ systemctl status docker

# 修改檔案內容
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock --insecure-registry 10.196.108.176:5000 --dns 114.114.114.114 --dns 8.8.8.8 --dns 8.8.4.4 -g /mnt

然後重啟服務:
# 重新載入服務配置
$ sudo systemctl daemon-reload

# 重啟 Docker
$ sudo systemctl restart docker.service

# 判斷是否配置成功
$ sudo netstat -lntp | grep dockerd

Docker Swarm
# 在主節點啟動 Swarm
$ docker swarm init

# 查看 Swarm 密鑰
$ docker swarm join-token -q worker

# 在主節點啟動 Procontainer
$ docker run -it -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

# 在主節點啟動 Registry
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2

# 將子節點加入到 Swarm
$ docker swarm join \
--token ${TOKEN} \
10.196.108.176:2377
代理設置
鑒於 gcr 域名的不可用,我們需要利用 ss-privoxy 等工具搭建 Docker 源代理,也可以參考這裡手動配置客戶端:
$ docker run -i -t -e SERVER_ADDR=ss.server.ip -e SERVER_PORT=port -e PASSWORD=123456 bluebu/shadowsocks-privoxy

如果需要手動安裝,需要先安裝 sslocal 命令:

$ apt install python3-pip
$ pip3 install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U

寫入你的配置檔案到例如 config.json:
{
    "server""...",
    "server_port": ...,
    "local_port"1080,
    "password""..."
    "method""chacha20-ietf-poly1305",
    "timeout"600
}

啟動:
$ sslocal -c config.json

這時一個 socks5 代理在你本機就啟動了。下麵安裝配置 privoxy 把他轉成 http/https 代理。安裝略。修改/添加兩個 privoxy 的配置(對於 ubuntu,在 /etc/privoxy/config):
listen-address 0.0.0.0:8118        # 所有 interface 上監聽流量
forward-socks5 / 127.0.0.1:1080 .  # 流量導向本機上的 ss 代理

這時可以訪問一下不存在的網站測試一下:
HTTP_PROXY=127.0.0.1:8118 HTTPS_PROXY=127.0.0.1:8118 curl https://www.google.com

下麵修改各台機器的 Docker 配置(假定我們的 master 內網地址 1.1.1.2,其他兩台機器地址為 1.1.1.3 和 1.1.1.4):
[Environment]
Environment="HTTP_PROXY=127.0.0.1:8118" "HTTPS_PROXY=127.0.0.1:8118" "NO_PROXY=localhost,127.0.0.1,1.1.1.2,1.1.1.3,1.1.1.4"

...

環境變數 NO_PROXY 顧名思義,它不支持 CIDR 應該,所以需要你列舉一下集群主機地址。
鏡像

鏡像描述了 Docker 容器運行的初始檔案系統,包含運行應用所需的所有依賴。即可以是一個完整的操作系統,也可以僅包含應用所需的最小 bin/lib 檔案集合。Docker 鏡像和容器採用分層檔案系統結構,每個容器包含一層薄薄的可寫層,只讀部分是共享的,這種機制保證了資源的可復用性,減少了鏡像與容器的空間占用。Docker 鏡像儲存引擎有 AUFS,DeviceMapper,Overlay 等多種實現。
構建與拉取
編寫完成 Dockerfile 之後,可以通過 docker build 命令來創建鏡像;關於 Dockfile 的具體語法,可以查看下文。Dockfile 基本的格式為 docker build [ 選項 ] 路徑,該命令將讀取指定路徑下(包括子目錄)的 Dockerfile,並將該路徑下所有內容發送給 Docker 服務端,由服務端來創建鏡像。因此一般建議放置 Dockerfile 的目錄為空目錄。也可以通過 .dockerignore 檔案(每一行添加一條匹配樣式)來讓 Docker 忽略路徑下的目錄和檔案。
鏡像的完整 tag 不僅包含鏡像名字,還指明瞭鏡像從哪裡來,要到哪裡去,就像一個 URL。可以通過 -t 選項指定鏡像的標簽信息,譬如:
$ sudo docker build -t myrepo/myapp /tmp/test1/

$ docker build -t username/image_name:tag_name .

$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Docker 支持從 Registry 拉取鏡像,或者將某個容器儲存為鏡像:
# 拉取鏡像
$ docker pull image_name

# 將某個容器儲存為鏡像
$ docker commit -m “commit message” -a “author”  container_name username/image_name:tag
複製代碼Docker 支持將鏡像儲存為檔案,以方便鏡像的匯出與加載:
# 儲存鏡像
$ docker save --output saved-image.tar my-image:1.0.0
$ docker save my-image:1.0.0 > saved-image.tar
$ docker save my_image:my_tag | gzip > my_image.tar.gz

# 匯入鏡像
$ docker load --input saved-image.tar
$ docker load 
鏡像管理
docker images 命令會列舉出全部的鏡像:
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mynewimage          latest              4d2eab1c0b9a        5 minutes ago       278.1 MB
ubuntu              14.04               ad892dd21d60        11 days ago         275.5 MB
<none>              <none>              6b0a59aa7c48        11 days ago         169.4 MB
<none>              <none>              6cfa4d1f33fb        7 weeks ago         0 B

Docker 中鏡像主要分為三種狀態:
  • 已使用鏡像(used image): 指所有已被容器(包括已停止的)關聯的鏡像。即 docker ps -a 看到的所有容器使用的鏡像。

  • 未取用鏡像(unreferenced image):沒有被分配或使用在容器中的鏡像,但它有 Tag 信息。

  • 懸空鏡像(dangling image):未配置任何 Tag (也就無法被取用)的鏡像,所以懸空。這通常是由於鏡像 build 的時候沒有指定 -t 引數配置 Tag 導致的。


# 列舉未使用的
$ docker images --filter "dangling=true"

# 刪除所有無用的鏡像
$ docker rmi $(docker images -q -f dangling=true)
Dockerfile
Dockerfile 由一行行命令陳述句組成,並且支持以 # 開頭的註釋行。一般的,Dockerfile 分為四部分:基礎鏡像信息、維護者信息、鏡像操作指令和容器啟動時執行指令;指令的一般格式為 INSTRUCTION arguments,指令包括 FROM、MAINTAINER、RUN 等。例如:
#
# MongoDB Dockerfile
#
# https://github.com/dockerfile/mongodb
#

# Pull base image.
FROM dockerfile/ubuntu

ENV SOURCE http://downloads-distro.mongodb.org/repo/ubuntu-upstart

# Install MongoDB.
RUN \
  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
  echo 'deb $SOURCE dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
  apt-get update && \
  apt-get install -y mongodb-org && \
  rm -rf /var/lib/apt/lists/*

ENV PATH /usr/local/mongo/bin:$PATH

# Define mountable directories.
VOLUME ["/data/db"]

# Define working directory.
WORKDIR /data

# Define default command.
CMD ["mongod"]

# Expose ports.
#   - 27017: process
#   - 28017: http
EXPOSE 27017
EXPOSE 28017

其中,一開始必須指明所基於的鏡像名稱,接下來推薦說明維護者信息。後面則是鏡像操作指令,例如 RUN 指令,RUN 指令將對鏡像執行跟隨的命令。每運行一條 RUN 指令,鏡像添加新的一層,並提交。最後是 CMD 指令,來指定運行容器時的操作命令。
指令名 格式 描述 備註
FROM 格式為 FROM FROM : 第一條指令必須為 FROM 指令。 如果在同一個 Dockerfile 中創建多個鏡像時,可以使用多個 FROM 指令(每個鏡像一次)
MAINTAINER 格式為 MAINTAINER 指定維護者信息。
RUN RUN RUN ["executable", "param1", "param2"] 前者將在 shell 終端中運行命令,即 /bin/sh -c;後者則使用 exec 執行。指定使用其它終端可以通過第二種方式實現,例如 RUN ["/bin/bash", "-c", "echo hello"] 每條 RUN 指令將在當前鏡像基礎上執行指定命令,並提交為新的鏡像。當命令較長時可以使用 \ 來換行。
CMD 支持三種格式,CMD ["executable","param1","param2"] 使用 exec 執行,推薦方式;CMD command param1 param2/bin/sh 中執行,提供給需要交互的應用;CMD ["param1","param2"] 提供給 ENTRYPOINT 的預設引數; 指定啟動容器時執行的命令,每個 Dockerfile 只能有一條 CMD 命令。如果指定了多條命令,只有最後一條會被執行。如果用戶啟動容器時候指定了運行的命令,則會改寫掉 CMD 指定的命令。
EXPOSE EXPOSE [...] 告訴 Docker 服務端容器暴露的端口號,供互聯繫統使用 在啟動容器時需要通過 -p 來指定端口映射,Docker 主機會自動分配一個端口轉發到指定的端口
ENV ENV。指定一個環境變數,會被後續 RUN 指令使用,併在容器運行時保持
ADD ADD 該命令將複製指定的 到容器中的 可以是 Dockerfile 所在目錄的一個相對路徑;也可以是一個 URL;還可以是一個 tar 檔案(自動解壓為目錄)
COPY COPY 複製本地主機的 (為 Dockerfile 所在目錄的相對路徑)到容器中的 dest 當使用本地目錄為源目錄時,推薦使用 COPY
ENTRYPOINT ENTRYPOINT ["executable", "param1", "param2"],使用指定可執行檔案執行;ENTRYPOINT command param1 param2,會在 Shell 中執行 配置容器啟動後執行的命令,並且不可被 docker run 提供的引數改寫。每個 Dockerfile 中只能有一個 ENTRYPOINT,當指定多個時,只有最後一個起效。
VOLUME VOLUME ["/data"] 創建一個可以從本地主機或其他容器掛載的掛載點,一般用來存放資料庫和需要保持的資料等
USER USER daemon 指定運行容器時的用戶名或 UID,後續的 RUN 也會使用指定用戶
WORKDIR WORKDIR /path/to/workdir 為後續的 RUNCMDENTRYPOINT 指令配置工作目錄 可以使用多個 WORKDIR 指令,後續命令如果引數是相對路徑,則會基於之前命令指定的路徑
當服務不需要管理員權限時,可以通過該命令指定運行用戶。並且可以在之前創建所需要的用戶,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres;要臨時獲取管理員權限可以使用 gosu,而不推薦 sudo。
RUN、CMD 和 ENTRYPOINT 這三個 Dockerfile 指令看上去很類似,很容易混淆。RUN 執行命令並創建新的鏡像層,RUN 經常用於安裝軟體包。CMD 設置容器啟動後預設執行的命令及其引數,但 CMD 能夠被 docker run 後面跟的命令列引數替換。ENTRYPOINT 配置容器啟動時運行的命令。我們經常可以使用 ENTRYPOINT 指定固定命令,使用 CMD 動態傳入引數。
ENTRYPOINT ["/bin/echo""Hello"]
CMD ["world"]

# docker run -it 
# Hello world
# docker run -it  John
# Hello John

Docker 推薦的是單個容器執行單個任務的關註點分離策略,容器的主行程會負責管理所有的子行程。如果我們未在自定義初始化腳本中考慮太多子行程生命周期管理的操作,那麼可以使用 –init 引數來允許 Docker 自動註入 init 行程作為主行程,其會在容器關閉時候自動處理所有的派生的子行程,並且相對於完整的 sysvinit 或者 systemd 更為輕量級。如果我們希望在單個容器中運行多個行程,則可以使用 supervisord 或者自定義腳本[5]:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]
Registry
Docker 允許我們建立私有的 Registry 來存放於管理鏡像,直接運行如下命令即可創建私有 Registry:
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
參考上文描述我們可知,鏡像名的前綴即表示該鏡像所屬的 Registry 地址,因此我們可以通過 tag 方式將某個鏡像推送到私有倉庫:
# 拉取公共鏡像
$ docker pull ubuntu:16.04

# 為鏡像添加 Registry 信息
$ docker tag ubuntu:16.04 custom-domain:5000/my-ubuntu

# 將其推送到私有鏡像庫
$ docker push custom-domain:5000/my-ubuntu

# 從私有鏡像庫中拉取鏡像
$ docker pull custom-domain:5000/my-ubuntu

我們也可以指定鏡像庫的存放地址:
-v /mnt/registry:/var/lib/registry

很多情況下我們的內部倉庫並不會配置 HTTPS,如果希望以 HTTP 方式訪問,那麼需要在任何需要推送/拉取鏡像的機器上配置非安全域名:
"insecure-registries": ["myregistry.example.com:5000"] }

有時候我們也需要為私有倉庫配置權限認證,那麼首先需要添加 TLS 支持,並且配置認證檔案:
$ mkdir auth
$ docker run \
  --entrypoint htpasswd \
  registry:2 -Bbn cscan cscancscan > ~/auth/htpasswd

$ openssl req -new -newkey rsa:4096 -days 365 \
                -subj "/CN=localhost" \
                -nodes -x509  \
                -keyout ~/certs/domain.key \
                -out ~/certs/domain.crt

然後可以使用 Compose 檔案來描述所需要的 TLS 以及 AUTH 引數:
registry-srv:
  restart: always
  image: registry:2
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /opt/registry:/var/lib/registry
    - ~/certs:/certs
    - ~/auth:/auth

接下來使用 Docker Compose 命令啟動服務:
$ docker-compose up -d

# 登錄到鏡像服務器
$ docker login myregistrydomain.com:5000

容器

Docker 中鏡像是只讀的,創建容器時只是在鏡像上面新建一個可寫層,不需要複製整個檔案系統,因而可以實現毫秒級創建。
啟停控制
  • docker create:創建一個容器但是不啟動。

  • docker rename:允許重命名容器。

  • docker run:在同一個操作中創建並啟動一個容器。

  • docker rm:刪除容器。

  • docker update:更新容器的資源限制。

容器會在結束命令之後自動退出,使用以下的命令選項可以將容器保持在激活狀態:
  • -i 即使在沒有附著的情況下依然保持 STDIN 處於開啟。單純使用 -i 命令是不會出現 root@689d580b6416:/ 這種前綴

  • -t 分配一個偽 TTY 控制台

# 創建,並且啟動某個容器以執行某個命令
$ docker run -ti --name container_name image_name command

#
 創建,啟動容器執行某個命令然後刪除該容器
$ docker run --rm -ti image_name command

#
 創建,啟動容器,並且映射捲與端口,同時設置環境變數
$ docker run -it --rm -p 8080:8080 -v /path/to/agent.jar:/agent.jar -e JAVA_OPTS=”-javaagent:/agent.jar” tomcat:8.0.29-jre8

#
 創建容器,指定網絡
$ docker run --network=

預設情況下,創建容器時,它不會將其任何端口發佈到外部世界。要使端口可用於 Docker 之外的服務或未連接到容器網絡的 Docker 容器,請使用 –publish 或 -p 標誌。這會創建一個防火牆規則,將容器端口映射到 Docker 主機上的端口。
標誌值 描述
-p 8080:80 將容器的 80 端口映射到 Docker 主機的 8080 端口(TCP)
-p 8080:80/udp 將容器的 80 端口映射到 Docker 主機的 8080 端口(UDP)
-p 8080:80/tcp -p 8080:80/udp 將容器的 80 端口映射到 Docker 主機的 8080 端口(TCP 和 UDP)
# 啟動/停止某個容器
$ docker [start|stop] container_name

#
 在某個容器內執行某條命令
$ docker exec -ti container_name command.sh

#
 查看某個容器的輸出日誌
$ docker logs -ft container_name
狀態查詢
  • docker ps 查看運行中的所有容器。

  • docker logs 從容器中獲取日誌。(你也可以使用自定義日誌驅動,不過在 1.10 中,它只支持 json-file 和 journald)

  • docker inspect 查看某個容器的所有信息(包括 IP 地址)。

  • docker events 從容器中獲取事件(events)。

  • docker port 查看容器的公開端口。

  • docker top 查看容器中活動行程。

  • docker stats 查看容器的資源使用情況統計信息。

  • docker diff 查看容器的 FS 中有變化檔案信息。

# 根據條件過濾查詢
$ docker ps --filter "name=nostalgic"

 # 顯示正在運行的容器串列
$ docker stats --all
管理配置
創建容器時也可以容器的重啟策略,即是當容器出錯退出或者宿主機重啟時候,容器的應對策略;重啟策略同樣會保證相關聯的容器以正確的順序重啟,避免意外的錯誤。
  • no:不進行重啟

  • on-failure:當容器以非零狀態碼退出時重啟容器

  • unless-stopped:當某個容器被顯性關閉或者 Docker 本身關閉或重啟時重啟

  • always:無論出現任何情況都重啟容器


# 設置重啟策略
# Off, On-failure, Unless-stopped, Always
$ docker run -dit — restart unless-stopped [CONTAINER]
可以通過多種過濾條件來進行容器的移除:
# 關閉所有正在運行的容器
$ docker kill $(docker ps -q)

# 移除所有停止的容器
$ docker rm $(docker ps -a -q)

# 根據狀態移除
$ docker rm $(docker ps -q -f 'status=exited')

# 根據標簽移除
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')

$ docker rm $(docker ps -a | grep "46 hours ago")

我們也可以對容器中的檔案進行匯入匯出操作:
  • docker cp 在容器和本地檔案系統之間複製檔案或檔案夾。

  • docker export 將容器的檔案系統切換為壓縮包(tarball archive stream)輸出到 STDOUT。

資源配額
我們可以使用 docker stats 命令來查看 Docker 容器的性能狀態與資源占用:
$ docker stats redis1 redis2

CONTAINER           CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O
redis1              0.07%               796 KB / 64 MB        1.21%               788 B / 648 B       3.568 MB / 512 KB

Memory
$ docker run -it -m 300M ubuntu:14.04 /bin/bash
CPU
在線上環境中,我們即希望能夠儘量避免 CPU 空間時間片,最大化資源利用率,也要保障重點業務的資源占用,避免因某個異常容器占用或不合理使用整機 CPU 資源,造成宿主機上大量容器異常;此時單容器的 CPU 資源約束及上限將變得非常重要,既不能限得太死,又不能限不住,將通過內核和調度系統的限制機制來保障資源穩定性。Docker 允許使用 cpus,cpuset-cpus,cpu-shares 等來限制容器的計算資源占用:
# 指定容器可占用的 CPU 核編號,0-3 表示占用四個核,1、3 表示占用兩個核
$ docker run -it --cpuset-cpus="1,3" ubuntu /bin/bash

# 最多允許占用單 CPU 50% 的計算資源,如果雙核 CPU,則可以設置為 1.5 等
$ docker run -it --cpus=".5" ubuntu /bin/bash

# 不同的值能夠指定不同的容器權重,用於動態分配 CPU 資源
$ docker run -it --cpu-shares="512" ubuntu /bin/bash

CPU Set 保障了容器的 CPU 核數,安全性較強,但是整體資源利用率低。如果容器實際並不需要如此多核的 CPU 資源來處理任務則會造成資源浪費,並且導致其他容器上的任務無法利用該容器上的 CPU 空閑時間片,人為阻斷了 CPU 閑時復用的能力。
CPU Share 允許通過共享的方式獲得 CPU 資源,不同的容器共享一定總量的 CPU 計算能力,每個容器都系結全量核,而每個容器獲取一定份額的 CPU 計算力。該樣式下,每個容器的 CPU 資源分配不再以整核分配,而是可精細到 CPU 時間片份額的粒度,並且是連續的 CPU 核能力值。當整機閑時,可以讓較為繁忙的業務獲得整機空閑。採用 CPU 資源共享的機制,其資源隔離性沒有 set 樣式強,對於極個別 CPU 資源敏感型業務,有可能出現偶爾等待 CPU 時間片的情況,而影響業務穩定性。對於極少數的這類業務,我們容許繼續使用 CPU set 樣式。
Storage
如果使用 Device Mapper 作為底層儲存驅動,則可以通過 Docker daemon 的如下引數來全域性限制單個容器占用空間的大小:
# 限制單個容器最多占用 20G 空間,將應用於任何新建容器。
$ --storage-opt dm.basesize=20G

如果是 btrfs 儲存驅動,可使用其提供的 subvolume 功能來實現。一個容器會對應一個 subvolume。針對容器對應的 subvolume 啟用並配置 quota 即可限制其磁盤空間:
$ btrfs qgroup limit -e 50G /var/lib/docker/btrfs/subvolumes/<CONTAINER_ID>

授予對單個設備訪問權限:

docker run -it --device=/dev/ttyUSB0 debian bash

授予所有設備訪問權限:
docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash

資源配置

Volume | 資料捲
容器運行時應該儘量保持容器儲存層不發生寫操作,對於資料庫類需要儲存動態資料的應用,其資料庫檔案應該儲存於捲(Volume)中。為了防止運行時用戶忘記將動態檔案所儲存目錄掛載為捲,在 Dockerfile 中,我們可以事先指定某些目錄掛載為匿名捲,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器儲存層寫入大量資料。
資料捲是一個可供一個或多個容器使用的特殊目錄,它繞過 UFS,可以提供很多有用的特性:
  • 資料捲可以在容器之間共享和重用

  • 對資料捲的修改會立馬生效

  • 對資料捲的更新,不會影響鏡像

  • 捲會一直存在,直到沒有容器使用

  • 資料捲的使用,類似於 Linux 下對目錄或檔案進行 mount

For example:
# the following creates a tmpfs volume called foo with a size of 100 megabyte and uid of 1000.
$ docker volume create --driver local \
    --opt type=tmpfs \
    --opt device=tmpfs \
    --opt o=size=100m,uid=1000 \
    foo

nother example that uses nfs to mount the /path/to/dir in rw mode from 192.168.1.1:
$ docker volume create --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.1,rw \
    --opt device=:/path/to/dir \
    foo
$ docker run -d \
  -it \
  --name devtest \
  -v myvol2:/app \
  nginx:latest
"Mounts": [
    {
        "Type""volume",
        "Name""myvol2",
        "Source""/var/lib/docker/volumes/myvol2/_data",
        "Destination""/app",
        "Driver""local",
        "Mode""",
        "RW"true,
        "Propagation"""
    }
],
Docker -v 標記也可以指定掛載一個本地主機的目錄 / 檔案到容器中去:
# 掛載目錄
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

# 掛載檔案
$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

# Docker 掛載資料捲的預設權限是讀寫,用戶也可以通過 `:ro` 指定為只讀。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py

註意:Dockerfile 中不支持這種用法,這是因為 Dockerfile 是為了移植和分享用的。然而,不同操作系統的路徑格式不一樣,所以目前還不能支持。
VOLUME /data
Network | 網絡
Linux 在網絡棧中引入網絡命名空間,將獨立的網絡協議棧隔離到不同的命令空間中,彼此間無法通信;Docker 利用這一特性,實現不容器間的網絡隔離,並且引入 Veth 設備對來實現在不同網絡命名空間的通信。Linux 系統包含一個完整的路由功能,當 IP 層在處理資料發送或轉發的時候,會使用路由表來決定發往哪裡。Netfilter 負責在內核中執行各種掛接的規則(過濾、修改、丟棄等),運行在內核樣式中;Iptables 樣式是在用戶樣式下運行的行程,負責協助維護內核中 Netfilter 的各種規則表;通過二者的配合來實現整個 Linux 網絡協議棧中靈活的資料包處理機制。Docker 的網絡子系統採用了基於驅動的可插拔機制,其預設包含瞭如下驅動樣式:
  • bridge:預設的網絡驅動,常用於多個應用運行與獨立容器中並且需要相互通訊的時候。

  • host:移除容器與 Docker 主機之間的網絡隔離,直接使用宿主機所在的網絡。底層與宿主機共用一個 Network Namespace,容器將不會虛擬出自己的網卡,配置自己的 IP 等,而是使用宿主機的 IP 和端口。

  • Overlay:Overlay 網絡用語連接多個 Docker Daemon,保證 Docker Swarm 服務的正常運行;獨立的容器與 Swarm 服務,或者不同宿主機上的容器同樣能夠通過 Overlay 進行通信。

  • none:對於指定容器禁止所有的網絡通信。

  • Macvlan:Macvlan 網絡會允許直接為容器分配 MAC 地址,使其作為真正的物理設備接入到宿主機所在的網絡中。

我們使用 network 命令可以查看到預設的網絡:
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f707aa0ef50d        bridge              bridge              local
97dd7a032d96        host                host                local
d5a1bed0b12d        none                null                local

橋接樣式下,當 Docker 啟動時,會自動在主機上創建一個 docker0 虛擬網橋,即軟體交換機,在掛載到它的網口之間進行轉發。同時,Docker 隨機分配一個本地未占用的私有網段(在 RFC1918 中定義)中的一個地址給 docker0 接口。比如典型的 172.17.42.1 ,掩碼為 255.255.0.0 。此後啟動的容器內的網口也會自動分配一個同一網段(172.17.0.0/16)的地址。
橋接樣式下,創建一個 Docker 容器的時候,同時會創建了一對 veth pair 接口(當資料包發送到一個接口時,另外一個接口也可以收到相同的資料包)。這對接口一端在容器內,即 eth0 ;另一端在本地並被掛載到 docker0 網橋,名稱以 veth 開頭(例如 vethAQI2QT)。通過這種方式,主機可以跟容器通信,容器之間也可以相互通信。Docker 就創建了在主機和所有容器之間一個虛擬共享網絡。
# 創建新的網絡
$ docker network create --driver bridge isolated

# 指定網段,宿主機會作為預設網關
$ docker network create --driver=bridge --subnet=192.168.2.0/24 --gateway=192.168.2.10 new_subnet

# 創建時將某個容器連接到網絡
$ docker run --network=isolated -itd --name=docker-nginx nginx

# 將某個運行中容器連接到某個網絡
$ docker network connect multi-host-network container1
DNS
預設情況下,容器從 Docker 守護行程繼承 DNS 設置,包括 /etc/hosts 和 /etc/resolv.conf。可以基於每個容器改寫這些設置。
  • -h HOSTNAME or –hostname=HOSTNAME 設定容器的主機名,它會被寫到容器內的 /etc/hostname 和 /etc/hosts 。但它在容器外部看不到,既不會在 docker ps 中顯示,也不會在其他的容器的 /etc/hosts 看到。

  • –link=CONTAINER_NAME:ALIAS 選項會在創建容器的時候,添加一個其他容器的主機名到 /etc/hosts 檔案中,讓新容器的行程可以使用主機名 ALIAS 就可以連接它。

  • –dns=IP_ADDRESS 添加 DNS 服務器到容器的 /etc/resolv.conf 中,讓容器用這個服務器來解析所有不在 /etc/hosts 中的主機名。

  • –dns-search=DOMAIN 設定容器的搜索域,當設定搜索域為 .example.com 時,在搜索一個名為 host 的 主機時,DNS 不僅搜索 host,還會搜索 host.example.com 。 註意:如果沒有上述最後 2 個選項, Docker 會預設用主機上的 /etc/resolv.conf 來配置容器。


空間清理
Docker 使用過程中,可能會發現宿主節點的磁盤容量持續增長,譬如 volume 或者 overlay2 目錄占用了大量的空間;如果任其發展,可能將磁盤空間耗盡進而引發宿主機異常,進而對業務造成影響。Docker 的內置 df 指令可用於查詢鏡像(Images)、容器(Containers)和本地捲(Local Volumes)等空間使用大戶的空間占用情況。而容器的占用的總空間,包含其最頂層的讀寫層(writable layer)和底部的只讀鏡像層(base image layer,read-only),我們可以使用 ps -s 引數來顯示二者的空間占用情況:
# 查看當前目錄下的檔案空間占用
$ du -h --max-depth=1 | sort

# 空間占用總體分析
$ docker system df

# 輸出空間占用細節
$ docker system df -v

# 輸出容器的空間占用
$ docker ps -s

docker system prune 指令能夠進行自動地空間清理,其預設會清除已停止的容器、未被任何容器所使用的捲、未被任何容器所關聯的網絡、所有懸空鏡像:
# 一併清除所有未使用的鏡像和懸空鏡像
$ docker system prune --all

#
 列舉懸空鏡像
$ docker images -f dangling=true

#
 刪除全部懸空鏡像
$ docker image prune

#
 刪除所有未被使用的鏡像
$ docker image prune -a

#
 刪除指定樣式的鏡像
$ docker images -a | grep "pattern" | awk '{print $3}' | xargs docker rmi

#
 刪除全部鏡像
$ docker rmi $(docker images -a -q)

#
 刪除全部停止的容器
$ docker rm $(docker ps -a -f status=exited -q)

#
 根據指定樣式刪除容器
$ docker rm $(docker ps -a -f status=exited -f status=created -q)
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')

#
 刪除全部容器
$ docker stop $(docker ps -a -q)
$ docker rm $(docker ps -a -q)

#
 列舉並刪除未被使用的捲
$ docker volume ls -f dangling=true
$ docker volume prune

#
 根據指定的樣式刪除捲
$ docker volume prune --filter "label!=keep"

#
 刪除未被關聯的網絡
$ docker network prune
$ docker network prune --filter "until=24h"
複製代碼我們也可以手動指定日誌檔案的尺寸或者清空日誌檔案:
# 設置日誌檔案最大尺寸
$ dockerd ... --log-opt max-size=10m --log-opt max-file=3

#
 清空當前日誌檔案
truncate -s 0 /var/lib/docker/containers/*/*-json.log

服務治理

Docker Compose
Docker Compose 是用於定義和運行複雜 Docker 應用的工具。你可以在一個檔案中定義一個多容器的應用,然後使用一條命令來啟動你的應用,然後所有相關的操作都會被自動完成;簡單的 Compose 檔案定義如下:
# 指定 Docker Compose 檔案版本
version: '3'
services:
  web:
    # 指定從本地目錄進行編譯
    build: .

    # 指定匯出端口
    ports:
      - '5000:5000'

    # 替換預設的 CMD 命令
    command: python app.py

    # 將本地目錄系結到容器內目錄
    volumes:
      - .:/code

  redis:
    # 鏡像的 ID
    image: 'redis:alpine'

這裡用到的 Python Web 應用的 Dockerfile 如下:
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python""app.py"]

值得註意的是,我們在代碼中直接使用服務名作為連接地址,即可訪問到 Redis 資料庫:
cache = redis.Redis(host='redis', port=6379)

然後使用 docker-compose 命令啟動:
# 交互式啟動
$ docker-compose up

# 守護行程式啟動
$ docker-compose up -d

# 查看運行情況
$ docker-compose ps

# 關閉
$ docker-compose stop

# 移除內部捲
$ docker-compose down --volumes

在涉及到資料儲存的場景下,我們同樣可以指定 docker-compose 創建命名資料捲,並將其掛載到容器中:
version: "3.2"
services:
  web:
    image: nginx:alpine
    volumes:
      - type: volume
        source: mydata
        target: /data
        volume:
          nocopy: true
      - typebind
        source: ./static
        target: /opt/app/static

  db:
    image: postgres:latest
    volumes:
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
      - "dbdata:/var/lib/postgresql/data"

volumes:
  mydata:
  dbdata:
Docker Swarm
Swarm 是 Docker 公司在 2014 年 12 月初發佈的一套較為簡單的工具,用來管理 Docker 集群,它將一群 Docker 宿主機變成一個單一的,虛擬的主機。Swarm 使用標準的 Docker API 接口作為其前端訪問入口,換言之,各種形式的 Docker Client(dockerclient in go,docker_py,docker 等)均可以直接與 Swarm 通信。
Swarm Deamon 只是一個調度器(Scheduler)和路由器(Router),Swarm 自己不運行容器,它只是接受 Docker 客戶端發送過來的請求,調度適合的節點來運行容器,這意味著,即使 Swarm 由於某些原因掛掉了,集群中的節點也會照常運行,當 Swarm 重新恢復運行之後,它會收集重建集群信息。並且 Swarm 提供的路由匹配(服務發現、負載均衡、跨容器通訊)非常可靠。在單個端口上運行一個服務,Swarm 節點的任意主機都可以訪問,負載均衡完全在後臺實現。
Swarm 在 Scheduler 節點運行容器的時候,會根據指定的策略來計算最適合運行容器的節點,目前支持的策略有:Random,Binpack,Spread。
Random 顧名思義,就是隨機選擇一個 Node 來運行容器,一般用作除錯用。Spread 和 Binpack 策略會根據各個節點的可用的 CPU,RAM 以及正在運行的容器的數量來計算應該運行容器的節點。在同等條件下,Spread 策略會選擇運行容器最少的那台節點來運行新的容器,binpack 策略會選擇運行容器最集中的那台機器來運行新的節點。
使用 Spread 策略會使得容器會均衡的分佈在集群中的各個節點上運行,一旦一個節點掛掉了只會損失少部分的容器。Binpack 策略最大化的避免容器碎片化,就是說 Binpack 策略盡可能的把還未使用的節點留給需要更大空間的容器運行,盡可能的把容器運行在一個節點上面。
# 創建一個新的服務
$ docker service create \
--image nginx \
--replicas 2 \
nginx

# 更新服務
$ docker service update \
--image nginx:alpine \
nginx

# 刪除服務
$ docker service rm nginx

# 縮容,而不是直接刪除服務
$ docker service scale nginx=0

# 擴容
$ docker service scale nginx=5

# 列出所有的服務
$ docker service ls

# 列出一個服務的所有實體(包括服務的健康狀況)
$ docker service ps nginx

# 服務的詳細信息
$ docker service inspect nginx

我們也可以使用 Docker Compose 的腳本來進行批次部署:
$ docker stack deploy application
version: '3'
services:
  web:
    image: registry.gitlab.com/example/example # you need to use external image
    command: npm run prod
    ports:
      - 80:80
    deploy:
      replicas: 6
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

相關鏈接:
  1. https://github.com/wxyyxc1992/Awesome-Links/blob/master/Infrastructure/Virtualization/Container/Docker/Docker-Links.md

  2. https://github.com/wxyyxc1992/Awesome-CheatSheet/blob/master/Infrastructure/OS/Linux/Linux-Commands-CheatSheet.md

  3. https://github.com/wxyyxc1992/Backend-Boilerplate/tree/master/infra/docker

  4. https://mirrors.ustc.edu.cn/help/docker-ce.html

  5. https://docs.docker.com/config/containers/multi-service_container/

原文鏈接:https://juejin.im/post/5ba11f635188255c8473a134

Kubernetes實戰培訓

Kubernetes應用實戰培訓將於2018年10月12日在深圳開課,3天時間帶你系統學習Kubernetes本次培訓包括:容器基礎、Docker基礎、Docker進階、Kubernetes架構及部署、Kubernetes常用物件、Kubernetes網絡、儲存、服務發現、Kubernetes的調度和服務質量保證、監控和日誌、Helm、專案實踐等,點擊下方圖片查看詳情。

赞(0)

分享創造快樂