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 底層邏輯#
圖片來源: 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 | 交互式執行並進入 shell | docker 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 | 設定容器的 hostname | docker run -h myhost ubuntu |
docker run --privileged | 以特權模式執行(擴大容器權限) | docker run --privileged ubuntu |
docker run --cpus | 限制CPU | docker run --cpus="1.0" ubuntu |
docker run --memory | 限制記憶體 | docker run --memory="512m" ubuntu |
docker run --gpus | 限制GPU | docker 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 | 建立新的 volume | docker volume create my_volume |
docker volume ls | 列出所有 volume | docker volume ls |
docker volume rm | 刪除一個或多個 volume | docker volume rm my_volume |
docker volume prune | 刪除未使用的 volume | docker 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 中;是最常用的檔案複製指令,單純無副作用。 |
ADD | 與 COPY 類似,也可複製本機目錄,但可額外自動解壓 .tar 檔案,且可接受 URL 來源自動下載(但較少建議使用)。ZIP 不會自動解壓。 |
CMD | 設定 Container 執行階段的預設執行命令,僅能設定一條(若多條僅最後一條有效),若在 docker run 時指定命令會覆蓋掉 CMD 。 |
USER | 以 USER <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 | 停止並移除所有服務、網路,並一併移除 volume | docker compose down -v |
docker compose ls -a | 查看 Compose 管理中的服務狀態 | docker compose ls -a |
docker compose build | 依照 Compose 檔案建構 Image | docker compose build |
docker compose stop | 停止服務但不移除容器 | docker compose stop |
docker compose start | 啟動已存在但停止的容器 | docker compose start |
docker compose restart | 重新啟動服務 | docker compose restart |
docker compose exec | 在服務內執行指令(等同 docker exec ) | docker 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