概念
海 → VPS
鯨魚 → Docker
貨櫃 → Container,來自單個或數個 Image
installation
方法 1
Docker (docker-ce) installation for Ubuntu 16.04 and Ubuntu 18.04
將 docker_installer.sh 檔案放到 VPS 上,執行:
1 | bash docker_installer.sh |
方法 2
下載安裝腳本 sh install-docker.sh # 執行安裝腳本
1 | curl https://get.docker.com/ |
Hello World
將 Docker Hub 用來練習的 Hello World 映像檔拉進 VPS
1 | docker run hello-world |
背後運作
- 在本機端(VPS)尋找是否有 Hello World 映像檔,若有直接將該 image 運行成一個 container
- 若沒有,從 Docker Hub 上拉取對應的 image 並運行
- 若 Docker Hub 也沒有,拋出錯誤訊息
Docker 基本指令
幫助命令
版本查詢
1 | docker version |
查看 Docker 資訊
1 | docker info |
常用指令
1 | docker --help |
查看特定指令細節說明
1 | docker help COMMAND |
image 相關命令
列出現有的 images
1 | docker images [OPTIONS] |
REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
---|---|---|---|---|
倉庫源(唯一) | 版本 | ID(唯一) | 創建日期 | 檔案大小 |
同一個倉庫源可以有個 tag,代表不同版本(e.g 3.2),使用
REPOSITORY:TAG
來定義不同的 image,如果不指定版本標籤預設為 lastest (e.g ubuntu:latest)
options
-a (列出本地所有images,包含中間層)
-q (只顯示image ID)
--digests (顯示image的摘要訊息)
--no-trunc (顯示完整的image訊息)
查詢遠端倉庫(docker hub)的 image
1 | docker search [OPTIONS] REPONAME |
options
-s [number] (列出 stars 數不小於[number]的 repo)
--automated (只列出 automated build 類型的 images)
--no-trunc (顯示完整的image訊息)
下載遠端倉庫(docker hub)的 image
1 | docker pull REPONAME[:TAG] |
若不加上
:TAG
參數,預設下載最新版本(latest),若需指定版本
e.gdocker pull nginx:3.2
刪除 image
1 | docker rmi [OPTIONS] IMAGE[:TAG] |
- 若不加上
:TAG
參數,預設刪除最新版本(latest)- 可刪除多個,只需在 imageName 後加空白,接續著要刪除的 imageName
options
-f (強制刪除,即使在運行中)
刪除所有 images
1 | docker rmi -f $(docker images -q) |
$()
可以解釋為一個子命令,docker images -q
如上面說明,可羅列出宿主機器所有 image 的 id,搭配起來即可刪同時刪除所有 id 被羅列的 image。
1 | docker images -q | xargs docker rmi -f |
將 | 前指令產生的字串作為參數,傳入 | 後的指令中 (Linux 原生指令)
列出 image 歷史
1 | docker history IMAGE |
Container 相關命令
將 image 實例化為 container 並執行
1 | docker run [OPTIONS] IMAGE |
options
--name (為該容器命名)
-d (啟動守護式容器(常用於後台應用) -> 不需與 container 終端交互)
-i (啟動互動式容器(常用於前台應用) -> 登入 container 時可與終端交互,通常與 -t 同時使用)
-t (自動打開該 container 終端介面)
-P (隨機 port 映射)
-p [vps port]:[container port] (指定 port 映射)
運行交互式容器
1 | docker run -it --name mycentos centos /bin/bash |
以 centos 映像開啟一個名為 mycentos 的交互式 container。使用 -it
選項會直接進入該 container 的終端介面(紅框處),該紅框處的 SHA 值則對應此 container 的 ID
運行守護式容器
1 | docker run -d --name mycentos centos |
開啟守護式容器成功後,系統會返回一個 conatainer ID,證明已經開啟成功。
不過有個需注意的地方,在成功開啟後,再以 docker ps 查詢運行中容器時,卻發現沒有發現該容器(如上圖)。
原因是在該容器成功實例化並執行後,由於沒有任何前台進程,所以很快又就被 docker 自動清除掉了。參考
上述問題是由於 docker 機制所導致,docker 後台容器要運作,就必須有一個前台進程
如何讓前台有進程執行?
1 | docker run -d centos bin/sh -c "while true;do echo hello;sleep 2;done" |
該指令每兩秒會在終端機 log 一次 ‘hello’ 字串,如此一來有進程正在執行,docker 就不會自動清除該容器。
列出 docker 所有運行中的 container
1 | docker ps [OPTIONS] |
options
-a (列出正在運行及歷史上運行過的容器)
-l (列出最近一個執行的容器)
-n [number] (列出最近[number]個運行的容器)
-q (只顯示容器編號)
停止 container
在 container
環境中的終端執行
- container 停止並退出
1 | exit |
- 重啟”已關閉(exited)”容器
docker ps -l
-> 列出上一個被操作的容器,記下 ID SHA 值 或 容器名稱docker start NAME|ID
-> 重啟- 重啟並進入 container 交互終端
docker start NAME|ID -i
- container 不停止並退出
1 | ctrl + P + Q |
- 如何證明容器沒有被關閉?
docker ps
查看該 container 的 status 是否為 up- 可透過
attach
指令重新進到”不停止退出”的容器,下面會有該指令介紹。
在 宿主機
環境中的終端執行
- 正常停止,需等待 (建議使用)
1 | docker stop NAME|ID |
- 強制停止,立即關閉
1 | docker kill NAME|ID |
刪除已停止運行的 container
- 刪除單個
1 | docker rm NAME|ID |
- 刪除全部
1 | docker rm $(docker ps -qa) |
- 已刪除的 container 會從快取中清除,使用
docker ps
指令查詢不到- 刪除未停止的容器需加上
-f
參數進行強制刪除
重啟正在運行的 container
1 | docker restart NAME|ID |
重啟後輸入指令
docker ps NAME|ID
,查看該容器的 status,開啟時間會變為幾秒前,證明該容器確實已被重新啟動
查看容器日誌 logs
1 | docker logs [OPTIONS] NAME|ID |
options
-t (顯示各 log 時間)
-f (即時更新)
--tail [number] (只看最後 [number] 行)
查看容器內執行中的進程
1 | docker top [OPTIONS] NAME|ID |
查看容器內部細節
1 | docker inspect [OPTIONS] NAME|ID |
進入正在執行的容器並以終端機交互
指令
attach | exec |
---|---|
進入容器開啟終端,且不開啟新的進程。 | 啟動容器中新的終端,並開啟新進程 |
attach
1 | docker attach [OPTIONS] CONTAINER |
範例:
1 | docker attach d2ea08ddc42c |
在容器內使用 ctrl + P + Q
退出後,輸入此指令可以回到容器終端介面。
exec
1 | docker exec [OPTIONS] CONTAINER [script] |
範例 1:
1 | docker exec -it d2ea08ddc42c /bin/bash |
此指令效果相當於上面的 docker attach d2ea08ddc42c
,原理實際上是啟動了容器內的 /bin/bash,此時你就可以通過 bash shell 與容器內交互了,就像遠端連接了 SSH 一樣。
範例 2:
1 | docker exec -it d2ea08ddc42c ls /tmp |
此指令不會進入 d2ea08ddc42c
容器,並且在該指定容器中的 /tmp
目錄下操作 ls
指令,並將結果返回宿主環境。
從容器內複製文件到宿主環境
1 | docker cp [OPTION] d2ea08ddc42c:容器內路徑 宿主環境路徑 |
範例:
1 | docker cp d2ea08ddc42c:/tmp/test.txt /tmp |
將容器內的 /tmp/test.txt
檔案複製一份到宿主環境的 /tmp
路徑
修改並提交現有容器成為一個新的 image
1 | docker commit [OPTION] CONTAINER [REPOSITORY[:TAG]] |
options
-a="" (作者)
-m="" (提交訊息)
-c (將 Dockerfile 指令應用於創建的映像)
-p (再提交時暫停容器運作,預設為true)
範例:
1 | docker commit -a="dylam" -m="customCentOS" d2ea08ddc42c dylan/centOS:1.1.0 |
Data Volumes 資料卷
概述
在 Docker 容器中產生的資料,若不透過 commit 將資料成為 image 的一部分的話,在關閉容器時資料就會消失,為了解決這個問題,可以 透過 Data Volumes 解決。
特性
- 將容器資料持久化儲存
- 容器間繼承、共享資料
- Data Volumes 在容器中修改會直接生效
- Data Volumes 的更改不會包含在 image 的更新中
- 直到沒有任何容器使用,他的生命週期才正式結束
容器內添加
以指令添加
1 | docker run -it -v /宿主機絕對路徑資料夾:/容器資料夾 IMAGE |
可支援添加多個 Data Volume
1 | docker run -it -v /host-a:/container-a -v /host-b:/container-b IMAGE |
範例:
1 | docker run -it -v /hostDataVolume:/containerDataVolume dylan/centos:1.0.0 |
此指令將會以 dylan/centos:1.0.0 映像,開啟一個交互式容器,同時建立 Data Volumes,hostDataVolume 資料夾建立在宿主機根路徑下,containerDataVolume 則建立在容器根路徑下。兩個目錄具有共享資料的功能及上述所有特性。
- 訪問錯誤
Docker 掛載主機目錄 Docker 訪問若出現cannot open directory .: permission denied
解決方法,添加 privileged 參數:
1 | docker run -it -v /宿主機絕對路徑資料夾:/容器資料夾 --privileged=true IMAGE |
- 自訂權限
1 | docker run -it -v /宿主機絕對路徑資料夾:/容器資料夾:ro IMAGE |
or 表示
read-only
,加上此參數後,容器內的 Volume 不可修改。
以 Dockerfile 添加
在根目錄新增資料夾,並且添加一個 dockerfile 檔案
1 | cd / && mkdir mydocker && cd mydocker && touch dockerfile && vim dockerfile |
編寫基礎 dockerfile
1 | FROM centos |
FROM | VOLUME | CMD |
---|---|---|
基礎環境 | 映像生成後,自動建立的容器內 Data Volume 資料夾路徑(支援生成多個目錄),以 Dockerfile 描述的 volume 無法指定對應的宿主機資料夾位置,而是由 Docker 自動產生 |
執行 shell 指令 |
docker build
透過 dockerfile 生成 image
1 | docker build -f ~/mydocker/Dockerfile -t dylan/centos:1.0.0 . |
若已在 ~/mydocker 目錄下,則也不須加
-f ~/mydocker/Dockerfile
,預設會讀取當下目錄的 Dockerfile(D 大寫) 檔案
options
-f (dockerfile 路徑)
-t (生成的 image 名字)
查看是否掛載成功
使用指令 docker inspect CONTAINER
查詢容器細節
將會發現此資料:
- 以指令添加時
1 | "HostConfig": { |
- 以 Dockerfile 添加時
Destination 為容器 data volume 位置,Source 為宿主機 data volume 位置(docker 自動產生)
1 | "Mounts": [ |
繼承其他容器的 Data Volume
假設現在已經有一個正在運行的容器,名為 dc01,並且已有設定 Data Volume,此時如果想要在新建一個容器,並且共享 dc01 的 Data Volume,可以怎麼做呢?
1 | docker run -it --name dc02 --volumes-from dc01 IMAGE |
此時新生成一個 dc02 容器,並且與 dc01 共享 Data Volume,在內部所有資料將會共享並同步,即使父容器 dc01 被刪除,dc02 繼承自 dc01 的 Data Volume 依然會存在。
Dockerfile
什麼是 Dockerfile?
- Dockerfile 是用來構建 Docker image 的文件,是由一系列指令和參數構成的腳本。
- 構建三步驟
- 編寫 Dockerfile
- docker build -> 執行 dockerfile,建構成 image
- docker run -> 執行生成的映像成為一個運行中容器
Dockerfile 的解析過程
基礎知識
- 每條保留字都必須全大寫,且至少要有一個參數
- 指令從上到下依序執行
#
表示註解- 每條指令都會創建一個
映像層
,並對映像進行提交
Docker 執行 Dockerfile 的大致流程
- Docker 從基礎映像運行一個容器 (FROM 後的參數)
- 執行一條指令並對容器作出修改
- 執行類似 docker commit 的操作,提交一個新的映像層
- 再基於剛提交的映像運行一個新容器
- 執行下一條 Dockerfile 指令,直到所有指令執行完成
Dockerfile 保留字指令
FORM
基礎 IMAGE,當前新映像是基於哪個映像的
MAINTAINER
作者資訊
1 | MAINTAINER The CentOS Project <cloud-ops@centos.org> |
RUN
容器構建時需要運行的額外 Linux 命令
EXPOSE
當前容器對外暴露的 port 號
WORKDIR
指定在創建容器後,終端機預設的工作目錄
ENV
在構建映像的過程中,設定環境變數
這個變數可以在後續任何RUN
指令中引用,就如同在命令指令了環境變數前綴一樣;也可以在其他指令中直接使用這些變數。
1 | ENV MY_PATH /usr/test |
ADD
將宿主機資料夾下的文件複製進映像且預設自動處理 URL 和解壓縮 tar
COPY
類似 ADD 指令,拷貝文件和資料夾到映像中。
從宿主機上下文(dockerfile 所在的資料夾)中的文件或資料夾,複製到映像內的<目標路徑>位置
支援兩種格式寫法,前者為 <源路徑> 後者為 <目標路徑>
1 | COPY src dest |
VOLUME
定義容器內的 Data Volume 資料夾的路徑
CMD
指定容器啟動時要運行的指令
注意
- Dockerfile 中可以有多個 CMD 指令
- 但只有最後一個生效
- CMD 會被 docker run 之後的參數覆蓋。
1 | FROM node:10.15.3-alpine |
以這個簡單的 dockerfile 為例,將這個 dockerfile 透過 docker build 指令產生 image 後,使用 docker run 指令生成容器:
1 | docker run -p 3000:3000 -it myNodeApp |
在執行的最後,終端會執行最後一個 dockerfile 的 CMD 指令,也是就是 node index.js
(將 node app 啟動)。但如果在下 docker run 時,在指令最後加上了其他 command 指令,則這個指令會覆蓋掉 node index.js
1 | docker run -p 3000:3000 -it myNodeApp ls -l |
可以想像 dockerfile 在你加了其他指令後變成:
1 | FROM node:10.15.3-alpine |
如上述 1、2 點所敘述,CMD 指令可以有多個,但只有最後一個會起作用。
格式
CMD 指令格式和 RUN 相似,也是兩種格式
1 | # shell 格式 |
1 | # exex 格式 |
1 | # 參數列表格式,在指定了 ENTRYPOINT 指令後,用 CMD 指定具體的參數 |
ENTRYPOINT
指定容器啟動時要運行的指令,ENTRYPOINT 和 CMD 一樣,都是在指定容器啟動程序及參數
注意
- 和 CMD 不同的是,ENTRYPOINT 後這指令不會被 docker run 之後的參數覆蓋,而是為該指令追加參數。
- docker run 之後的參數會被當作參數傳遞給 ENTRYPOINT 指定的指令,形成新的指令組合,但 dockerfile 內必須使用
ENTRYPOINT [""]
格式,否則追加參數不會起作用。
ONBUILD
當構建一個被繼承的 Dockerfile 時運行該指令,父映像在被子映像繼承後,父映像的 ONBUILD 會被觸發。
案例
父映像 ~/mydocker/parentdockerfile
1 | FROM centos |
透過父映像 dockerfile 生成 image
1 | docker run build -f ~/mydocker/parentdockerfile -t parent/image . |
子映像 ~/mydocker/childdockerfile
1 | FROM parent/image |
執行
1 | docker build -f ~/mydocker/childdockerfile -t child/image . |
此時因為父映像被繼承(FROM),因此 ONBUILD RUN echo "Hello!"
會被觸發,並且在終端機輸出執行結果。
案例
Base 映像(scratch)
下面是 centOS 6.8 官方 Dockerfile,Docker Hub 中 99% 的映像都是在 scratch 映像中安裝和配置需要的軟體而構建出來的,你可以把它當作 docker 映像的基底,大部分映像的映像層最底部都是它。
當你編寫的 Dockerfile 包含指令FROM centos
即代表該 Dockerfile 描述的映像的內部映像層也包含了 scratch 映像。
1 | FROM scratch |
自訂義映像
編寫 Dockerfile 案例 1
Docker hub 中官方的 centos 是一個精簡版的 Linux 系統,為的是盡可能壓縮大小,因此有許多功能是不支援的。另外某些行為已經被官方的 Dockerfile 所定義,如下:
- 執行
docker run -it centos
時,預設進入的目錄路徑為/
。 - 不支援 ifconfig 指令查找 ip 資訊
- 不支援 vim 編輯器
現在嘗試使用官方的 centos 映像為基底,編寫一個新的 Dockerfile,讓上述的功能可以被支援,且進入容器時的預設目錄為 /tmp
。
1 | # 路徑: ~/mydocker/dockerfile |
將 Dockerfile 編譯成 image
1 | docker build -f ~/mydocker/dockerfile -t dylan/mycentos . |
查看 image 歷史。
1 | docker history dylan/mycentos |
有一點值得注意,Docker 映像的概念就是一層一層映像層的嵌套,透過 image history 這個指令可以證實,每一個 Docker 保留字的指令都會生成一個新的映像層(參考上述的
Docker 執行 Dockerfile 的大致流程
部分),每層都有擁有一個 ID,而最後一個指令產生的映像層 ID,將會成為這個映像最終對外暴露的 ID。
執行 image 成 container
1 | docker run -it --name mycentos dylan/mycentos |
接著就可以發現這個容器的變化了
編寫 Dockerfile 案例 2
此案例是使用 centos 為基底,並包含 tomcat 環境,並且此案例中會涵蓋幾乎所有的 Dockerfile 保留字指令。
1 | FROM centos |
執行 Dockerfile 形成 image
若 Dockerfile 在宿主機當前路徑下,可以不需指定 Dockerfile 位置
-f ~/mydocker/Dockerfile
1 | docker build -t custom/centos . |
執行 image 成 container
1 | docker run -d -p 9999:8080 --name mycentos -v /dylan/tomcat9/volume:/usr/local/apache-tomcat-9.0.9/webapps/volume -v /dylan/tomcat0/logs/:/usr/local/apache-tomcat-9.0.8/logs --privileged=true custom/centos |
看起來有點長,不過都是上面所提到的東西綜合運用而已,斷行後一個一個看看:
1 | # 以後台方式執行容器,不進入容器終端、不印出任何執行log |
上傳 image 到 Docker Hub
Build image from Dockerfile
假設已經 commit 了一個新的映像,或者用自己的 Dockerfile 產生了一個映像,叫做 dylan237/centos
,執行此指令產生 image
/ 前必須與 Docker Hub 帳號一致
1 | docker build -t dylan237/centos . |
登入
在執行 docker push
指令前需要先登入,輸入自己的 Docker Hub 帳號密碼,只要登入過 Docker 會把 credential 保存,往後不需要再登入
1 | docker login |
Push
將 image 上傳
補充: Docker image 大小動輒數百 MB,要上傳的話其實算蠻肥的,還記得上面提過映像是由多個映像層組成的概念嗎?假設今天這個例子的基底是 centos,在 commit 的時候 Docker 並不會把完整的映像層都提交到遠端,而是將你新增的幾個映像層做提交而已,未來 pull 下來會連同你的基底 image 和你提交的映像層合併成你自己的 image 讓你使用。
1 | docker push dylan237/centos |
更新版本
1 | docker push dylan237/centos:tagname |
透過資料形式保存 image
如果在客戶端環境不能使用網路,這時候就能利用 docker save
將 image 轉成檔案,之後只要有這支檔案,就透過 docker load
指令將檔案轉還原成 image,幫助我們在無網路的環境中,跨平台部署專案。
假設想輸出成檔案的映像叫 dylan/test,並且希望輸出後的檔案位置在當下目錄 ./myImageInFileForm
1 | docker save -o ./myImageInFileForm dylan/test |
在當下目錄輸入指令 ls -l
應該就可以看到輸出完成的檔案 myImageInFileForm,接著可以透過任何形式保存這支檔案。
將檔案還原成 image
1 | docker load -i myImageInFileForm.bin |