快轉到主要內容

Docker 筆記

·1176 字·6 分鐘
Luca
作者
Luca

Docker 介紹
#

Docker 是一個開源的容器化(Containerization)平台,主要用途是將應用程式及其相依環境(如程式庫、設定檔)打包成一個可攜式的單位,稱為 Container

透過 Docker,開發者可以:

  • 輕鬆部署:一次打包,到處執行,減少「在我電腦可以跑」的問題。
  • 快速啟動:Container 啟動速度通常在秒級,比虛擬機器快很多。
  • 資源高效:共用宿主機(Host OS)的核心,不需要像 VM 一樣整個作業系統。

容器 vs 虛擬機器
#

特性容器 Container虛擬機器 Virtual Machine (VM)
架構共用宿主機 OS 核心,執行獨立進程模擬完整硬體與作業系統
啟動速度快速(秒級)較慢(數十秒至數分鐘)
資源使用較輕量,執行多個 Container 不需額外 OS 開銷每個 VM 都有完整 OS,較占記憶體與磁碟空間
隔離性過程隔離,核心共用,隔離程度較低完整作業系統隔離,安全性與穩定性較高
移植性高,打包後可在各平台執行中等,受限於 Hypervisor 相容性
使用場景微服務、快速部署、DevOps執行異質 OS、強隔離、模擬不同平台

名詞介紹
#

名詞英文簡單說明
映像檔Image底層模板,包含了運行特定應用程式所需的一切。
容器Container建構在映像檔之上,Docker利用容器來執行應用。
倉庫Repository倉庫是集中存放映像檔檔案的場所,docker Image版的github
卷/體積Volumes建構在容器之上,類似於docker版的隨身碟。

Docker 底層邏輯
#

OverlayFS 圖片

圖片來源: docker docs

OverlayFS 的關鍵概念
#

OverlayFS 是一種 Linux 檔案系統,它允許將多個檔案系統層(layer)疊加在一起,形成一個統一的視圖。這種技術常用於容器技術(如 Docker)或嵌入式系統中,因為它能實現高效的儲存空間利用和快速的檔案系統操作。

Lower Layer (對應docker image):

  • 底層目錄,通常是只讀的,用來存放基礎映像檔的內容。

Upper Layer (對應docker container):

  • 上層目錄,通常是可寫的,用於儲存對底層檔案的改動。

Merged View (同一層插入獨立且持久的區塊 docker volume):

  • 用戶最終看到的統一檔案系統視圖,它是通過將多個底層檔案系統合併後的結果。

Whiteout Files (對於被刪除的檔案):

  • 底層刪除檔案時,OverlayFS 會在上層建立一個「白色檔案」隱藏底層的對應檔案。
# 查看是否有docker群組
id
# 使用者aipe-tester 新增 docker群組
gpasswd -a aipe-tester docker
# 完成後需要登出登入
logout & login
# 查看是否有docker群組
id

Docker image
#

Docker Image 是一種唯讀的執行環境範本,內含應用程式執行所需的所有檔案、程式庫、依賴設定及執行指令。可以把 Image 想像成是 Container 的「藍圖」或「快照」,開發者先利用 Dockerfile 定義 Image 的內容與建構方式,然後使用 docker build 產生 Image,之後再透過 docker run 依據 Image 建立並啟動一個或多個獨立的 Container。由於 Image 是層疊式結構(Layered),具有高效能的快取與重用特性,因此能大幅提升應用部署的一致性與彈性。

常用指令
#

# 列出目前所有映像檔資訊
docker image ls -a

# 安裝本地映像檔 (解壓縮/解包 寫入docker images)
docker image load -i 2023_0811_2253_hello-world_latest.tgz

# 打包ubuntu:jammy 儲存成ubuntu_jammy.tar
docker image save -o ubuntu_jammy.tar ubuntu:jammy

# 壓縮ubuntu:jammy 儲存成ubuntu_jammy.gzip
docker image save ubuntu:jammy | gzip -9 -c > ubuntu_jammy.gzip

# 更改標籤與名稱(可以將最新的版本標籤改成latest)
docker tag 舊名稱:舊標籤 新名稱:新標籤

docker run
#

docker run 是 Docker 中最常用的指令,用來根據指定的 Image 建立並啟動一個新的 Container。透過 docker run,使用者可以同時設定執行模式(前景或背景)、掛載 Volume、指定埠口對映(Port Mapping)、設定環境變數,以及自訂 Container 名稱等。這條指令讓開發者可以在一個簡單的步驟中,快速從 Image 啟動出獨立且隔離的執行環境,是 Docker 自動化部署與持續整合流程中的核心操作之一。

啟動指令\選項
#

docker run 參數功能描述範例示範
docker run建立並啟動一個新的容器docker run -it ubuntu
docker run -d以背景(detached)模式執行容器docker run -d nginx
docker run -it交互式執行並進入 shelldocker run -it ubuntu
docker run --name指定容器名稱docker run --name my_container -it ubuntu
docker run -p對應主機與容器的埠口(port mapping)docker run -p 8080:80 nginx
docker run -v掛載主機目錄到容器(Bind mount)docker run -v /host/path:/container/path ubuntu
docker run -v掛載volume 到容器(Volume)docker run -v volume_name:/container/path ubuntu
docker run --rm容器停止後自動刪除docker run --rm ubuntu
docker run -e設定環境變數docker run -e MY_VAR=value ubuntu
docker run --network指定容器使用的網路docker run --network my_network ubuntu
docker run --restart設定自動重啟策略docker run --restart=always nginx
docker run -h設定容器的 hostnamedocker run -h myhost ubuntu
docker run --privileged以特權模式執行(擴大容器權限)docker run --privileged ubuntu
docker run --cpus限制CPUdocker run --cpus="1.0" ubuntu
docker run --memory限制記憶體docker run --memory="512m" ubuntu
docker run --gpus限制GPUdocker run --gpus=all ubuntu

範例:
#

# 這是用來建置 ollama 和 open-webui
docker run -d -p 3000:8080 --gpus=all -v ollama:/root/.ollama -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:ollama

Docker container
#

Docker Container 是一種輕量級、可攜式的執行環境,能將應用程式及其所有相依環境(如程式庫、設定檔)打包在一起,確保在不同平台上都能一致執行。每個 Container 彼此隔離,並與宿主作業系統共用核心,這讓它比傳統虛擬機(Virtual Machine)更快速、佔用資源更少。透過 Container,開發者可以輕鬆部署、擴充與管理應用,實現真正的「一次建置,到處執行(Build Once, Run Anywhere)」

container 操作
#

docker container 指令功能描述範例示範
docker container ls -a列出所有容器docker container ls -a
docker container start啟動已存在但已停止的容器docker container start <container_id>
docker container stop停止一個正在執行的容器docker container stop <container_id>
docker container restart重新啟動一個容器docker container restart <container_id>
docker container rm刪除一個容器docker container rm <container_id>
docker container exec在執行中的容器內執行指令(如 bash)docker container exec -it <container_id> bash
docker container logs查看容器的日誌輸出docker container logs <container_id>
docker container inspect查看容器詳細設定與狀態docker container inspect <container_id>
docker container pause暫停容器中的所有進程docker container pause <container_id>
docker container unpause恢復被暫停的容器進程docker container unpause <container_id>
docker container prune清理所有已停止的容器docker container prune
docker container cp複製容器內檔案到主機docker cp <container_id>:<容器內路徑> <主機路徑>
docker container cp複製主機檔案到容器內docker cp <主機路徑> <container_id>:<容器內路徑>

Docker volume
#

Docker Volume 是 Docker 提供的一種持久化儲存機制,用來將資料獨立於 Container 之外保存,避免因 Container 停止或刪除而導致資料遺失。Volume 可以被多個 Container 共用,常用於儲存資料庫檔案、應用設定或上傳檔案等需要長期保留的資料。使用者可以透過 docker volume create 建立 Volume,並在執行 docker run 時使用 -v--mount 將 Volume 掛載到 Container 的檔案系統中。這樣的設計讓容器化應用同時具備可移植性與資料持久性,是建構穩定可維護系統的關鍵之一。

volume 操作
#

指令用途範例
docker volume create建立新的 volumedocker volume create my_volume
docker volume ls列出所有 volumedocker volume ls
docker volume rm刪除一個或多個 volumedocker volume rm my_volume
docker volume prune刪除未使用的 volumedocker volume prune

Docker file
#

Dockerfile 是一個用來自動化建立 Docker Image 的文字檔,裡面以指令的形式逐步描述如何安裝應用程式、複製檔案、設定環境變數及定義 Container 啟動時要執行的動作。透過撰寫 Dockerfile,開發者可以將應用的建置流程標準化並版本化,確保每次使用 docker build 生成的 Image 都一致且可重現。這種基於程式碼描述環境的方式,讓專案部署更容易跨平台搬移與擴充,是實現 Infrastructure as Code (IaC) 的核心工具之一。

指令說明
FROM建立 Image 的基底 Image 名稱,可指定版本號,可多次使用(用於多階段建構),也能搭配 AS 為階段命名。
ARG可為空值,可用 --build-arg 傳入,可用於 FROM 前(例如指定 Base Image 參數);只在建構階段有效,不會保留在 Image 內。
ENV不能為空值,用來設定 Container 執行階段的環境變數,且會保存在 Image 中,可供後續指令與執行時使用。
WORKDIR設定後續指令執行時的工作目錄,若目錄不存在會自動建立,等同於 cd + mkdir 的效果。
RUN在建構(build)階段執行的指令,執行結果會寫進 Image 的對應層,常用於安裝套件(如 apt-get install -y vim)。
EXPOSE聲明對外可能使用的 Ports,僅為 Metadata,真正開放埠口需在 docker run 時以 -p--publish 顯式對映。
COPY將本機的檔案或目錄複製到 Image 中;是最常用的檔案複製指令,單純無副作用。
ADDCOPY 類似,也可複製本機目錄,但可額外自動解壓 .tar 檔案,且可接受 URL 來源自動下載(但較少建議使用)。ZIP 不會自動解壓。
CMD設定 Container 執行階段的預設執行命令,僅能設定一條(若多條僅最後一條有效),若在 docker run 時指定命令會覆蓋掉 CMD
USERUSER <UID>[:<GID>] 格式切換執行後續指令的使用者,通常用於提高安全性(避免以 root 執行)。

範例:
#

# 使用 Ubuntu 24.04
FROM ubuntu:24.04

# 設定環境變數,避免互動式輸入
ENV DEBIAN_FRONTEND=noninteractive

# 更新系統並安裝必要工具
RUN apt update && apt upgrade -y && \
    apt install -y software-properties-common curl sudo && \
    add-apt-repository ppa:deadsnakes/ppa && \
    apt update && apt install -y python3.12 python3.12-venv python3-pip && \
    apt clean && rm -rf /var/lib/apt/lists/*

# 建立使用者 Lucaca
RUN useradd -m -s /bin/bash Lucaca && echo "Lucaca ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# 切換使用者
USER Lucaca
WORKDIR /home/Lucaca

# (可選)建立虛擬環境,並使用 venv 的 python/pip
RUN python3.12 -m venv .venv
RUN .venv/bin/pip install --upgrade pip && .venv/bin/pip install jupyter

# 開放 port
EXPOSE 8888

# 使用 venv 的 python 啟動 Jupyter Notebook,並且設定初始路徑,此路徑啟動容器時Bind mount外部可以看的到內容
CMD ["/home/Lucaca/.venv/bin/jupyter", "notebook", "--ip=0.0.0.0", "--no-browser", "--port=8888", "--notebook-dir=/home/Lucaca/notebooks"]

使用 Dockerfile 建立 image
#

docker會在當前目錄下找尋檔案名稱是Dockerfile(沒有副檔名)的檔案,並自行建構

docker build -t jupyter-image .

啟動容器
#

# 設定Bind mount外部可以看的到內容
docker run -it \\
  -p 8888:8888 \\
  -v ~/jupyter-notebooks:/home/Lucaca/notebooks \\
  jupyter-image

Docker compose
#

Docker Compose 是一個用來定義與管理多個 Docker Container 的工具,透過一個 docker-compose.yml 檔案就能一次描述整個應用程式需要的服務、網路與 Volume,並用簡單指令快速啟動、停止或重建,適合在開發與測試環境中協調多容器架構。

compose 操作
#

子指令作用範例
docker compose up啟動並建立 Compose 內定義的所有服務docker compose up
docker compose up -d在背景執行所有服務docker compose up -d
docker compose down停止並移除所有服務、網路、掛載(不包含 volume)docker compose down
docker compose down -v停止並移除所有服務、網路,並一併移除 volumedocker compose down -v
docker compose ls -a查看 Compose 管理中的服務狀態docker compose ls -a
docker compose build依照 Compose 檔案建構 Imagedocker compose build
docker compose stop停止服務但不移除容器docker compose stop
docker compose start啟動已存在但停止的容器docker compose start
docker compose restart重新啟動服務docker compose restart
docker compose exec在服務內執行指令(等同 docker execdocker compose exec web bash
docker compose rm移除停止狀態的服務容器docker compose rm
docker compose config驗證並輸出合併後的 Compose 設定docker compose config

範例:
#

# 指定 docker-compose.yml 檔案格式的版本。版本 "3.8" 是較新的版本,支援許多現代 Docker 功能。
version: "3.8"

# "services" 是最核心的部分,用來定義組成應用程式的各個容器(服務)。
services:
  # 定義第一個服務,名稱為 "mysql"。
  mysql:
    # 指定此服務使用的 Docker 映像檔。這裡是使用官方的 MySQL 8.0 版本。
    image: mysql:8.0
    # 為此服務的容器設定一個固定的、好記的名稱。
    container_name: mysql
    # 設定容器的重啟策略。 "always" 表示無論容器因何故停止,Docker 都會自動嘗試重啟它。
    restart: always
    # 設定容器內的環境變數。
    environment:
      # 設定 MySQL root 使用者的密碼。這是 MySQL 映像檔要求的必要變數。
      MYSQL_ROOT_PASSWORD: rootpass
    # 設定磁碟區掛載 (volumes),用來持久化儲存資料或掛載設定檔。
    volumes:
      # 建立一個名為 "db_data" 的具名磁碟區 (named volume),並將其掛載到容器內的 /var/lib/mysql 目錄。
      # 這樣可以確保即使容器被刪除,MySQL 的資料庫檔案也能被保存下來。
      - db_data:/var/lib/mysql
      # 將主機當前目錄下的 init.sql 檔案掛載到容器的 /docker-entrypoint-initdb.d/init.sql。
      # MySQL 映像檔在第一次啟動時,會自動執行這個目錄下的所有 .sql 檔案,常用於初始化資料庫和資料表。
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    # 將此服務連接到指定的網路。
    networks:
      - backend

  # 定義第二個服務,名稱為 "flask"。
  flask:
    # "build: ." 表示 Docker Compose 會在當前目錄下尋找 Dockerfile,並使用它來建構此服務的映像檔。
    build: .
    # 為此服務的容器設定一個固定的名稱。
    container_name: flask
    # 同樣設定重啟策略為 "always"。
    restart: always
    # 設定 Flask 應用程式需要的環境變數。
    environment:
      # 設定 Flask 的運行環境為開發模式,這會啟用除錯模式等功能。
      FLASK_ENV: development
      # 資料庫主機名稱。這裡直接使用服務名稱 "mysql",因為它們在同一個 Docker 網路上,可以透過服務名稱互相訪問。
      DB_HOST: mysql
      # 連接資料庫所使用的使用者名稱。
      DB_USER: root
      # 連接資料庫所使用的密碼,必須與 mysql 服務中設定的 MYSQL_ROOT_PASSWORD 一致。
      DB_PASSWORD: rootpass
      # 要連接的資料庫名稱。這個資料庫應該由 init.sql 腳本建立。
      DB_NAME: flaskdb
    # 設定服務之間的依賴關係。這會確保 "mysql" 服務先於 "flask" 服務啟動。
    depends_on:
      - mysql
    # 掛載磁碟區。
    volumes:
      # 將主機上的 "D:/iSpan_docker_homework" 目錄掛載到容器內的 "/app" 目錄。
      # 這使得你在主機上修改程式碼時,容器內的程式碼也會同步更新,方便開發和除錯,不需重新建構映像檔。
      - D:/iSpan_docker_homework:/app
    # 將此服務也連接到 "backend" 網路。
    networks:
      - backend
    # 設定連接埠映射 (port mapping)。
    ports:
      # 將主機的 5000 連接埠映射到容器的 5000 連接埠。
      # 這樣你就可以透過瀏覽器訪問 http://localhost:5000 來存取 Flask 應用程式。
      - "5000:5000"

# 在頂層定義磁碟區 (volumes)。
volumes:
  # 宣告一個名為 "db_data" 的具名磁碟區。具體設定由 Docker 管理。
  db_data:

# 在頂層定義網路 (networks)。
networks:
  # 宣告一個名為 "backend" 的自訂網路。
  backend:
    # 指定網路的驅動程式為 "bridge"。這是最常見的網路類型。
    driver: bridge

參考資料
#

https://github.com/twtrubiks/docker-tutorial

https://github.com/testdrivenio/django-on-docker