Skip to main content

[Note] Docker and Kubernetes: The Complete Guide @ Udemy 學習筆記

此篇為筆記之整理,非原創內容,資料來源主要來自:Docker and Kubernetes: The Complete Guide @ Udemy

TL;DR#

$ docker run [image-id] # create and start an container by specific image
$ docker stop [container-id]
$ docker ps [--all]
$ docker system prune # 把所有 docker 中當前沒用到的 container 清空
# 將 container 包成 image
$ docker build -t pjchender/project_name .
# 針對某一運行中的 container 下指令
$ docker exec -it [container-id] sh

重要概念#

  • Image(映像檔):有所有執行程式時所需要相依的內容、設定、指令和執行檔。當在執行 container 時,它會使用一個獨立的檔案系統,而這個檔案系統即是透過 image 來提供。
  • Container(容器):image 的 instance,真正執行的程式,它是獨立於本機所有 process 以外的一個 process。
  • Docker Client(Docker CLI):輸入 docker 指令的地方,可以透過安裝 Docker Desktop 取得
  • Docker Server(daemon):建立映像檔、執行容器的地方

Kernel 和 Container#

Kernel 介於硬體和軟體的中間,當應用程式在執行時,會透過 system call 告知 kernel 要執行的項目(例如,寫檔),kernel 收到指令後會再對硬體進行操作:

Imgur

  • name spacing:可以將單一 process(或 group of processes)使用到的資源加以隔離
  • control groups(cgroups):可以限制每個 process 可以使用到的資源量

Imgur

Container 的概念就如同下圖中紅色框線的位置:

Imgur

Image 和 Container#

  • 當我們在講 image 的時候,實際上是在講一個檔案系統的快照(FS Snapshot)

Imgur

Docker CLI#

# Creating and Running a Container from an Image
$ docker run hello-world
$ docker run -it ubuntu bash
# 對 container 執行某一指令
# -i 讓 STDIN 保持開啟
# -t 讓 Terminal 介面比較好看(formatted)
# -it 讓開發者可以輸入內容到 container
$ docker exec <container-id> <command>
$ docker exec -it <container-id> bash # 執行 container 的 bash shell
# Create a Container
$ docker create <image-name> # 會取得 container id
# Start a Container
# -a:attached to the contained 並將 output 顯示在終端機上
$ docker start <container-id>
# 取得某一 Container 的 logs
$ docker logs <container-id>
# 停止某個 container
$ docker stop <container-id>
# 刪除某個 container
$ docker kill <container-id>
# 列出 containers
$ docker ps # 列出所有正在執行的 container
$ docker ps --all # 列出所有被建立過的 container
# 清除 container
$ docker system prune # 把所有 docker 中當前沒用到的 container 清空

docker run:執行某個 image#

# Creating and Running a Container from an Image
# docker run <image-name> <override-command>
$ docker run hello-world
# 執行 ubuntu 的 bash
$ docker run -it ubuntu bash
  • docker run = docker create + docker start
  • 執行 CLI 時會透過 docker client 告知 docker server(docker daemon) 要執行的任務,接著 docker server 會在本地的 image cache 中找尋有無 hello-world 這個 image,找不到的話就會到 docker hub 上去找,找到後便把這個 image 拉到本機的 image cache 中,接著 docker server 會根據這個 image 建立 container 以執行程序。

docker create & docker start#

# Create a Container
$ docker create <image name> # 會取得 container id
# Start a Container
# -a:attached to the contained 並將 output 顯示在終端機上
$ docker start <container id>
  • docker create 的動作會把 image 中的 FS Snapshot 複製一份到 container 中
  • docker start 會真正去執行啟動的指令(startup command)

docker stop & docker kill#

  • 使用 docker stop 會向 container 發送 SIGTERM 的指令(Signal Terminate),如此程式有機會執行 graceful shutdown 的過程
  • 使用 docker kill 會向 container 發送 SIGKILL 的指令
  • 如果使用 docker stop 後十秒鐘 container 仍沒有被停止,則 docker 會自動執行 kill 指令

docker ps:列出所有執行中的 container#

$ docker ps # 列出所有正在執行的 container
$ docker ps --all # 列出所有被建立過的 container

建立 Dockerfile#

Dockerfile 建立的流程如下:

  1. 選定一個 base image
  2. 執行一些指令來安裝其他的程式
  3. 定義一些指令來啟動該 container

Dockerfile#

The Build Process in Detail @ Docker and Kubernetes: The Complete Guide @ Udemy

# Dockerfile
# Use an existing docker image as a base
FROM alpine
# Download and install a dependency
RUN apk add --update redis
# Tell the image what to do when it starts as a container
CMD ["redis-server"]

若逐步執行這個 Dockerfile 的過程會像是(通常不會這麼做,單純示範用):

# 從 container 建立一個 image
$ docker run -it alpine sh
$ / apk add --update redis
# 開啟另一個 Terminal
$ docker commit -c 'CMD ["redis-server"]' <container-id> # 會回傳 image-id
$ docker run <image-id>

docker build#

在建立好 Dockerfile 後,可以執行 docker build 指令:

# 建立好 Docker file 後
$ docker build . # 執行建立好的 Dockerfile 後,會得到 image 的 id
$ docker run b9124cbfdc30 # 透過 docker run 可以執行

docker build 會做以下行為:

  • 每一個 Step 都會啟動一個 container 執行對應的行為,完成後會把它存成一個暫時的 image。
  • 接著,在下一個 Step 開始時,會去使用上一個 Step 留下的 Image,並以此再次啟動一個 Container,執行該 Step 對應的指令,該指令執行完後,同樣的會再把最終完成的結果存在一個 Image,以這樣的流程重複完成每個 Step。
  • 待所有 Step 都執行完後,最終會有一個 Image 可以讓使用者執行 docker run <image-id>

tagging a image#

每次都要輸入 image-id 非常麻煩,透過 image tag 可以為 image 建立標籤。慣例來說,標籤的命名會是 dockerId/projectName:version,例如 pjchender/redis:latest

# tagging docker image
$ docker build -t pjchender/redis-server:latest .
$ docker run pjchender/redis-server # 預設會使用 latest version

這整個為 image 建立標籤的流程稱作 tagging a image,但就技術上來說,只有標籤名稱最後的版本號是真正的 tag,前面單純只是 dockerId 和 projectName。

Rebuild with cache#

  • 每一次 Dockerfile 被執行後,Docker 都會將該次執行的內容快取下來,只要 Dockerfile 沒有改變(添加或變更順序),下次重複執行 docker build 指令時,會直接取用 cache 中的內容,在終端機中會看到 Using Cache 的說明文字。

Dockerize an App#

將 App Dockerize 的過程包含以下步驟:

  1. 建立 App
  2. 建立 Dockerfile
  3. 根據 Dockerfile 來 build image
  4. 將 image 以 container 方式執行
  5. 從瀏覽器連到該 Web App

1. 建立 App#

const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hi there');
});
app.listen(8080, () => {
console.log('Listening on port 8080');
});

2. 建立 Dockerfile#

  • 其中 COPY ./ ./ 的目的是要把專案資料夾中的所有檔案都複製一份到 docker container 的根目錄(root directory)
  • 由於只使用 COPY ././ 會把本機專案的所有檔案都複製到 container FS 的根目錄中,這麼做並不是很好,因此可以使用 WORKDIR <path> 這個指令來指定工作目錄,如此,所有後續的指令都會在這個工作資料夾中被執行
# Specify a base image
FROM node:alpine
# 定義 container 中的工作資料夾
WORKDIR /usr/app
# 將本機資料夾的檔案複製到工作資料夾中
COPY ./ ./
# Install some dependencies
RUN npm install
# Default command
CMD ["npm", "start"]

這樣的寫法有一個問題,每次只要專案的中任何檔案有變更時,就算套件沒有變更,都會重新執行一次 npm install(即package.json 的內容沒有改變),因此可以改成只有在 package.json 的檔案有變更時,在執行 npm install 這個指令,我們可以把 Dockerfile 改成:

# Specify a base image
FROM node:alpine
# 定義 container 中的工作資料夾
WORKDIR /usr/app
# 先將 package.json 複製到 container 的工作資料夾中
# 只有當 package.json 有改變時,才會執行 npm install 的指令
COPY ./package.json ./
RUN npm install
# 將本機資料夾的檔案複製到工作資料夾中
COPY ./ ./
# Default command
CMD ["npm", "start"]

3. 根據 Dockerfile 來 build image#

# -t 表示為該 image 建立 tag
$ docker build -t pjchender/docker-node-sandbox .

4. 將 image 以 container 方式執行#

$ docker run pjchender/docker-node-sandbox

5. 從瀏覽器連進該 Web App#

  • 透過 port mapping 的方式將 localhost 的 port 和 container 的 port 相對應
# docker run with port mapping
# -p [localhost-port]:[container-port]
$ docker run -p 8080:8080 <image-id>
# 在本機瀏覽器輸入 localhost:5000 將會連到 container 的 8080 port
$ docker run -p 5000:8080 pjchender/docker-node-sandbox

當 container 已經在執行時,希望能夠對同一個 container 執行某一些指令時,可以使用 docker exec 的指令:

$ docker exec -t <container-id> sh

課程資源#

Last updated on