Docker 是一个强大的容器化平台,它可以将应用程序及其所有依赖项打包到一个独立的容器中。这确保了应用在任何环境中都能以相同的方式运行。本指南将带你了解 Docker 的核心概念,并教你如何使用 Dockerfile 和 Docker Compose 来构建和管理你的应用。


核心概念

在开始之前,先了解几个核心概念:

  • 镜像 (Image):一个只读的模板,包含了应用程序运行所需的所有文件、代码、运行时和库。你可以把它看作是应用程序的“蓝图”。

  • 容器 (Container):镜像的一个可运行实例。你可以启动、停止、删除容器,容器内的操作不会影响镜像本身。

  • Dockerfile:一个纯文本文件,包含了一系列用于构建 Docker 镜像的指令。

  • Docker Compose:一个用于定义和运行多容器应用的工具。它允许你用一个 YAML 文件来配置应用的所有服务,并用一个命令来管理它们。


第一步:使用 Dockerfile 构建镜像

Dockerfile 是构建 Docker 镜像的“说明书”。它通过一系列指令来定义如何从一个基础镜像逐步构建出你的应用镜像。

Dockerfile

# 阶段1: 构建阶段(使用 .NET SDK 镜像)
# AS build 是给这个阶段命名,方便后续引用
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# COPY 指令会从构建上下文(通常是本地项目根目录)复制文件
# 这里只复制项目文件,以利用 Docker 的构建缓存
COPY ["WebApp/WebApp.csproj", "WebApp/"]
# 还原项目依赖,如果项目文件没有改变,这一步会被缓存
RUN dotnet restore "./WebApp/WebApp.csproj"

# 复制所有源代码到工作目录
COPY . .
WORKDIR "/src/WebApp"

# 构建项目,将编译结果输出到 /app/build 目录
RUN dotnet build "./WebApp.csproj" -c Release -o /app/build

# 阶段2: 发布阶段
# 从之前的 build 阶段继承,用于生成最终的发布文件
FROM build AS publish
# 发布项目,并指定不包含 .NET 运行时
RUN dotnet publish "./WebApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

# 阶段3: 最终镜像阶段(使用轻量级 ASP.NET 运行时镜像)
# 生产环境通常使用轻量级镜像以减小体积
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
# 暴露容器端口,这只是一个文档声明,并不会自动发布端口
EXPOSE 8080

# 从 publish 阶段复制最终的发布文件
# --from= 是多阶段构建的关键,它只复制需要的文件,避免了多余的 SDK 文件
COPY --from=publish /app/publish .

# ENTRYPOINT 定义容器启动时执行的默认命令
ENTRYPOINT ["dotnet", "WebApp.dll"]

# CMD(补充): CMD可以作为ENTRYPOINT的参数,或单独定义容器启动的命令
# 如果同时定义 ENTRYPOINT 和 CMD,CMD会被作为 ENTRYPOINT 的参数
# 例如:ENTRYPOINT ["dotnet"]  CMD ["WebApp.dll"]

Dockerfile 关键指令(补充)

  • USER: 指定容器内的用户。为了安全,生产环境通常不使用 root 用户。

  • LABEL: 为镜像添加元数据,如作者、版本等。

  • ENV: 设置环境变量,用于配置应用。

  • ARG: 定义构建时的变量,在 docker build 命令中传递。


第二步:使用 Docker Compose 管理多容器应用

docker-compose.yml 文件让你可以用一个文件来定义所有服务、网络和卷,并用一个命令来管理它们。

YAML

version: '3.8'

services:
  web-api:
    image: webapi:latest # 使用已构建好的镜像
    depends_on:
      - database # 定义服务依赖,确保数据库服务先启动
    ports:
      - "8084:8080" # 将主机端口8084映射到容器端口8080
    environment: # 设置环境变量,如数据库连接字符串
      - ConnectionStrings__DefaultConnection=Server=database;...
    networks:
      - my_network # 将服务加入自定义网络
  
  database:
    image: postgres:15
    ports:
      - "5432:5432"
    volumes: # 卷,用于持久化数据库数据
      - db_data:/var/lib/postgresql/data

# 定义网络和卷,用于服务间的通信和数据持久化
networks: 
  my_network:
    driver: bridge

volumes:
  db_data: # 定义命名卷

Docker Compose 关键参数(补充)

  • volumes: 用于持久化数据或同步本地文件到容器。

    • - ./src:/app/src绑定挂载,用于开发时同步代码,方便热重载。

    • - db_data:/var/lib/postgresql/data命名卷,用于持久化数据库数据,即使容器被移除,数据依然存在。

  • networks: 定义自定义网络,提供服务间的隔离和通信。driver: bridge 是默认且最常用的网络类型。

  • container_name: 为容器指定一个固定的名称,而非 Docker Compose 默认生成的名称。


第三步:常用 Docker 命令

掌握这些命令,你就能轻松管理你的容器应用。

  • docker build -t app-image .

    • docker build: 构建镜像。

    • -t app-image: 为镜像指定标签(名称和版本)。

    • .: 构建上下文,告诉 Docker 从当前目录寻找 Dockerfile。

  • docker-compose up -d --build

    • docker-compose up: 启动 docker-compose.yml 中定义的所有服务。

    • -d: 在后台运行容器。

    • --build: 在启动前强制重新构建所有服务。

  • docker-compose down: 停止并移除所有服务和默认网络。

  • docker-compose ps: 列出所有正在运行的服务。

  • docker-compose logs [服务名]: 查看某个服务的日志。

  • docker-compose exec [服务名] [命令]: 在运行中的容器里执行命令,例如 docker-compose exec web-api bash

现在你已经掌握了 Dockerfile 和 Docker Compose 的核心用法。通过实践,你将能更好地理解这些概念,并构建出高效、可移植的应用程序。