 
概念
海 → 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 |