Docker
Dockerfile
Example
FROM ubuntu
RUN apt-get update
RUN apt-get install -y python-is-python3
RUN apt-get install -y python3-pip
RUN pip3 install flask
WORKDIR /opt/source-code
COPY app.py /opt/source-code
EXPOSE 5000
ENTRYPOINT ["flask","run","--host=0.0.0.0"]FROM
指定基礎映像檔,所有建置都必須從這裡開始。
FROM ubuntu:20.04
FROM golang:1.21-alpineCOPY
從主機複製檔案到容器中。
# 複製單一檔案
COPY app.go /app/
# 複製整個目錄
COPY src/ /app/src/ADD
與 COPY 類似,但有額外功能:可以解壓縮檔案、從 URL 下載。
# 自動解壓縮
ADD app.tar.gz /app/
# 從 URL 下載
ADD https://example.com/file.txt /app/RUN
執行命令並建立新的 Layer。
# 安裝套件
RUN apt-get update && apt-get install -y curl
# 執行多行命令
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
python3 \
python3-pipCMD
容器啟動時的預設命令。
# 執行形式
CMD ["executable", "param1", "param2"]
# Shell 形式
CMD echo "Hello World"ENTRYPOINT
設定容器啟動時的主要命令,不容易被覆蓋。
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# 與 CMD 組合使用
ENTRYPOINT ["echo"]
# 可以被 docker run 的參數覆蓋
CMD ["Hello World"]CMD vs ENTRYPOINT
# hard code
FROM ubuntu
CMD ["sleep","5"]
# flex
FROM ubuntu
CMD ["sleep"]
# combined
FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["5"]LABEL
為映像檔加入 metadata。
LABEL version="1.0"
LABEL description="This is my app" \
maintainer="user@example.com"EXPOSE
宣告容器使用的網路埠。
EXPOSE 80
EXPOSE 80/tcp
EXPOSE 80/udpENV
設定環境變數。
ENV APP_HOME /app
ENV VERSION=1.0 DEBUG=trueVOLUME
建立掛載點。
VOLUME /data
VOLUME ["/data", "/logs"]USER
指定執行命令的使用者。
# 使用使用者名稱
USER nginx
# 使用 UID
USER 1000WORKDIR
設定工作目錄。
WORKDIR /app
WORKDIR /app/srcARG
定義建置時的變數。
ARG VERSION=latest
ARG BUILD_DATE
FROM ubuntu:${VERSION}ONBUILD
定義子映像檔建置時的觸發指令。
ONBUILD COPY . /app/src
ONBUILD RUN /app/src/build.shSTOPSIGNAL
設定停止容器時發送的系統信號。
STOPSIGNAL SIGTERM
STOPSIGNAL 9SHELL
# Windows 容器範例
SHELL ["powershell", "-Command"]
# Linux 容器範例
SHELL ["/bin/bash", "-c"]Multi-Stage Builds
多階段建置範例,用於減少最終映像檔大小。
# 建置階段
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# 最終階段
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]Examples
Go
# 建置階段
FROM golang:1.21-alpine AS builder
# 安裝建置工具
RUN apk add --no-cache git
# 設定工作目錄
WORKDIR /app
# 複製 go mod 檔案
COPY go.mod go.sum ./
# 下載相依套件
RUN go mod download
# 複製源碼
COPY . .
# 建置應用程式
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 最終階段
FROM alpine:latest
# 安裝基本工具
RUN apk --no-cache add ca-certificates tzdata
# 建立非 root 使用者
RUN adduser -D appuser
# 設定工作目錄
WORKDIR /app
# 從建置階段複製執行檔
COPY --from=builder /app/main .
# 變更擁有者
RUN chown -R appuser:appuser /app
# 切換到非 root 使用者
USER appuser
# 暴露埠口
EXPOSE 8080
# 執行應用程式
CMD ["./main"]Java
# 建置階段
FROM maven:3.8.4-openjdk-17 AS builder
# 設定工作目錄
WORKDIR /app
# 複製 pom.xml
COPY pom.xml .
# 下載相依套件(利用 Docker 層級快取)
RUN mvn dependency:go-offline
# 複製源碼
COPY src ./src
# 建置應用程式
RUN mvn clean package -DskipTests
# 最終階段
FROM eclipse-temurin:17-jre-alpine
# 設定工作目錄
WORKDIR /app
# 建立非 root 使用者
RUN addgroup -S spring && adduser -S spring -G spring
# 設定 Java 選項
ENV JAVA_OPTS="-Xmx512m -Xms256m"
# 設定時區
ENV TZ=Asia/Taipei
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 複製建置產物(.jar檔案)
COPY --from=builder /app/target/*.jar app.jar
# 變更擁有者
RUN chown spring:spring /app
# 切換到非 root 使用者
USER spring
# 暴露埠口
EXPOSE 8080
# 啟動應用程式
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]Architecture
flowchart LR C((Client)) Di((Docker image)) C --->|REST API| Di
Layer Architecture
FROM
RUN
RUN
COPY
ENTRYPOINTBuild image:
docker build Dockerfile -t lexyu/my-custom-appRun container (COPY-ON-WRITE Mechanism):
docker run lexyu/my-custom-app
Image
映像檔作為容器的基礎,用於創建和運行容器。是一種輕量級、不可變的軟體包,包含了執行應用所需的所有內容:
- 精簡版的作業系統(Cut-down OS)
- 必需的運行環境(執行環境)
- 應用程式檔案(Application files)
- 依賴的第三方庫(Third-party libraries)
- 環境變數(Environment variables)
Engine

Containerization
Each Container:
flowchart TD
Ns((Namespace))
PID(Process ID)
UT(Unix Timesharing)
M(Mount)
IP(InterProcess)
Nw(Network)
Ns <--> PID
Ns <--> UT
Ns <--> M
Ns <--> IP
Ns <--> Nw
Namespace - PID

cgroups

Network
Default Networks

User-defined Networks

Built-in DNS

Multiple Port Mapping

Volume
允許資料共享:
- 主機與容器之間
- 容器與容器之間

docker run -v /data/mysql:/var/lib/mysql mysql
使用 --mount 取代 -v:
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
Add Volume into Container From Host
docker run --name website -v ($pwd):/usr/share/nginx/html:ro -d -p 9000:80 nginx
Share Volumes from Specific Container
docker run --name website-copy --volumes-from website -d -p 9001:80 nginx
Docker Registry
Central Repository of all Docker Images:
[Registry]/[User/Account]/[Image/Repository]gcr.io/kubernetes-e2e-test-images/dnsutilsdocker.io/nginx/nginx
Docker Storage
File System when install Docker in Linux:
./var/lib/docker
|_ aufs
|_ containers
|_ image
|_ volumesContainer vs Virtual Machines
Container 容器
用於執行應用程式的隔離環境,允許多個應用程式在隔離環境中運行:
- 輕量化
- 使用主機的作業系統
- 啟動速度快
- 需要較少的硬體資源

Virtual Machines 虛擬機
機器(實體硬體)的抽象化。問題:
- 每個虛擬機需要完整的作業系統
- 啟動速度慢
- 資源密集

虛擬機管理程式(Hypervisor):負責管理虛擬機的程式。
Docker on Windows
