今天開始,會說明 Docker 更多的細節。未來如果需要維運 container 或用到 container 調度系統(如 K8S),則接下來十天的內容,將有可能幫上一點忙。
首先第一天要介紹的是 Volume 的進階用法。說是進階用法,其實只是比一開始提到的同步程式操作複雜了點而已。
原本同步程式的做法很單純,只是把本機的某個位置綁在 container 的某個路徑上,如:
docker run -d -it -v $PWD:/usr/local/apache2/htdocs -p 8080:80 httpd
這樣會把 host 下指令的路徑綁在 container 的 /usr/local/apache2/htdocs
上。
上面的方法達成了 host 共享資料給 container,但有時候會是 container 之間需要共用資料。可以試著執行下面的指令來了解如何做到這件事:
# 建立 volume,並命名為 code
docker volume create --name code
# 執行 BusyBox container 綁定 volume 在 source,並查看裡面的內容,並新增檔案
docker run --rm -it -v code:/source -w /source busybox sh
ls -al && echo Hi volume > /source/volume.html && exit
# 執行新的 BusyBox container 查看 volume 的內容
docker run --rm -it -v code:/source busybox ls -l /source
# 執行 Nginx container 綁定到 html 目錄裡
docker run -d -v code:/usr/share/nginx/html -p 8080:80 --name my-web nginx:alpine
# 查看 html
curl http://localhost:8080/volume.html
# 執行新的 Nginx container,並把 my-web 容器綁定的 volume 綁到這個容器上
docker run -d --volumes-from my-web -p 8081:80 --name my-web2 nginx:alpine
# 查看 html
curl http://localhost:8081/volume.html
這段指令很長,仔細說明如下:
code
然後掛載到 BusyBox 的 /source
下,並產生程式碼。--rm
參數,所以每次 container 都會移除,但 code
裡面的資料不會消失my-web
把 code
拿來掛載到,且可以正常使用my-web2
把 my-web
的掛載設定拿來用,且可以正常使用-v
參數掛載這次掛載的參數使用方法如下:
-v [VOLUME_NAME]:[CONTAINER_PATH]
VOLUME_NAME
若不存在,會建立一個同名 volume(背後執行 docker volume create --name VOLUME_NAME
);若存在,則會做掛載。
Volume 的名字有限定不能有斜線 /
:
$ docker volume create --name a/b
Error response from daemon: create a/b: "a/b" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path
所以有斜線,且是絕對路徑的話,就代表的是 bind mount,沒有斜線代表的是 volume。
-v
也可以不指定 VOLUME_NAME
如下:
-v [CONTAINER_PATH]
Docker 會使用 docker volume create
建立 volume,名稱會是一個隨機亂數,並掛載到 CONTAINER_PATH
。
--volumes-from
參數有時候會需要跟其他 container 共同相同的 volume 設定,比方像上例,同樣的 web 服務,都會有相同的設定。
--volumes-from
這時候就會非常好用,它後面要接的參數是 CONTAINER_ID
。它會把該 container 的 volume 設定原封不動的複製過去,包括 bind mount。
可以想像,在 Docker 的世界裡,container 跟 volume 是兩個不同且獨立元件,然後可以使用 docker run 組裝起來。
以上面這個圖來說,兩個 Web container 共同使用 Code volume,而 DB container 使用 Data volume。因 volume 是獨立的元件,所以我們可以執行其他 container,把對應的 volume 拿來讀取做其他應用。
筆者不會 MySQL,就當 MySQL 要停止才能備份好了。
# 啟動 MySQL
docker run -d -e MYSQL_ROOT_PASSWORD=password --name db mysql
# 停止 MySQL
docker stop db
# 使用 BusyBox container 做備份
docker run --rm -it --volumes-from db -v $PWD:/backup busybox tar cvf /backup/backup.tar /var/lib/mysql
這是個簡單的範例,裡面有兩個重點:
-v /var/lib/mysql
參數-v /var/lib/mysql
複製過來,因此 tar 指令後面要接 /var/lib/mysql
路徑MySQL 內帶 volume 是由 Dockerfile 指令設定的:
VOLUME /var/lib/mysql
其他 DB 像 Redis 也有類似的設定:
VOLUME /data
主要是因為這類需要 persistent 資料的服務,都會設定 VOLUME
讓其他 container 可以共用並做其他處理,如備份。
除了 DB 會用到 volume 外,筆者也遇過這個情境需要用到 volume 設定。主要是因為 Nginx 需要對應路徑要真的有 .php 檔案,它才會往 FPM 送出請求。Docker Compose 檔範例如下:
web:
image: nginx
working_dir: /usr/share/nginx/html
volumes_from:
- php
php:
image: php-fpm
working_dir: /usr/share/nginx/html
volumes:
- .:/usr/share/nginx/html
- /usr/share/nginx/html
Container 共享檔案雖然很方便,但有時候會希望限制權限。比方說回頭看第一個範例,假設我們只希望 BusyBox 才能有寫入權限,Nginx 只有唯讀權限,則可以加上屬性參數如下:
# BusyBox 不變
docker run --rm -it -v code:/source -w /source busybox sh
# Nginx volume 設定加上 :ro
docker run -d -v code:/usr/share/nginx/html:ro -p 8080:80 --name my-web nginx:alpine
屬性設定格式如下:
-v [VOLUME_NAME]:[CONTAINER_PATH]:[PROPERTIES]
此外 Mac 的效能問題也可以調整屬性設定解決。
以上都是以 container 共用檔案的前提在說明如何運用 volume。事實上,volume 獨立元件設計,更好用的地方在於,它可以使用不同的 driver 來替換實體的儲存位置。比方說,直接透過 volume driver 掛載 SSH 上的某個目錄--sshfs。
這部分筆者僅知道概念,但沒有經驗,有興趣可以參考官網說明。
docker volume create
建立 volume
--name
指定 volume 名稱docker run
-v|--volume
掛載 volume 到 container 裡面的某個目錄--volumes-from
新的 container 會共享舊的 container 的 volume 設定。知道 volume 的設定方法,雖然在開發上沒有什麼影響,但在維運應用上就能有更多變化。
比方說,假設有一群 container cluster,而 container 不知道會在哪台機器啟動的時候,volume 設定就變很重要。另外架一台 storage 然後讓 container 機器設定 volume driver 存取 storage,這樣的設計就會非常有彈性。