容器化早已不是 Linux 平台的专属能力,Windows Server 从 2016 版本开始就原生支持容器运行。配合 Docker Compose 之后,原本散落在多条命令行里的端口映射、卷挂载、环境变量等参数,可以被收敛到一份 YAML 文件中,一条命令即可拉起整套服务,销毁时同样干净利落。对于运行 .NET Framework 老应用、依赖 IIS 的站点,或者短期内无法迁移到 Linux 的业务系统来说,这套组合提供了一条风险可控的渐进式改造路径。

一、Windows 容器的运行原理与镜像选择
与不少人的直觉相反,Windows 容器和 Linux 容器在隔离机制上走的是同一条路线:容器与宿主机共享同一个内核。区别在于 Windows 容器共享的是 Windows 内核,这带来一个硬性约束——镜像内打包的操作系统版本必须与宿主机兼容。在一台 Windows Server 2019 上强行运行 ltsc2022 标签的镜像,容器会在启动后立刻退出,日志中会明确写出操作系统不匹配的提示。宿主机的内部版本号可以从注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion 中读取,先拿到 BuildNumber 再去对照镜像标签,能省去大量无意义的试错。
Windows 提供两种隔离模式。进程隔离开销小、启动速度快,但要求容器系统版本与宿主机严格对齐;Hyper-V 隔离则让每个容器运行在独立的轻量虚拟机中,允许版本存在差异,代价是更高的内存占用和更慢的启动速度。在 compose 文件里通过 isolation 字段切换,写成 isolation: process 或 isolation: hyperv 即可。如果宿主机与镜像版本能够对齐,生产环境优先选择进程隔离,资源利用率明显更好。
镜像来源也需要特别注意。Docker Hub 上绝大多数热门镜像只有 Linux 版本,Windows 生态的官方镜像集中在 mcr.microsoft.com 仓库中,例如 IIS 镜像 mcr.microsoft.com/windows/servercore/iis,.NET Framework 运行时镜像 mcr.microsoft.com/dotnet/framework/aspnet。拉取时务必核对标签中的 ltsc 版本号,Windows 基础镜像体积普遍在 GB 级别,磁盘空间也要提前规划。
# 读取宿主机系统内部版本号,用于对照镜像标签 (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuildNumber # 检查容器功能是否已安装 Get-WindowsFeature -Name Containers
二、环境准备:启用容器功能并安装 Docker 与 Compose
环境准备分三步走:启用容器功能、安装 Docker Engine、安装 Compose 插件。Docker Engine 在 Windows Server 上通过 PowerShell 的包提供程序安装,整个过程不需要图形界面,装完重启一次机器即可生效。
# 安装 Docker 提供程序与引擎 Install-Module -Name DockerMsftProvider -Repository PSGallery -Force Install-Package -Name docker -ProviderName DockerMsftProvider -Force Restart-Computer -Force
重启后执行 docker version 确认客户端与服务端都已就绪。引擎的全局配置文件位于 C:\ProgramData\Docker\config\daemon.json,该文件默认不存在,需要手工创建目录和文件。常见配置项包括镜像加速地址,以及开启实验特性以支持在同一台机器上切换运行 Linux 容器。每次修改配置后,执行 Restart-Service docker 让其生效。
{
"registry-mirrors": ["https://mirror.ipipp.com"],
"experimental": true
}
Compose 这边建议直接上 V2 版本。V1 的 docker-compose.exe 是独立程序,官方已停止维护;V2 以插件形式并入 docker 命令,调用方式从 docker-compose 变成了 docker compose,参数语义基本一致。安装 V2 只需把官方发布的可执行文件放进 C:\Program Files\Docker\Docker\cli-plugins\ 目录并命名为 docker-compose.exe。
# 创建插件目录并下载 Compose V2 New-Item -ItemType Directory -Force -Path "$env:ProgramFiles\Docker\cli-plugins" Invoke-WebRequest -Uri "https://github.com/docker/compose/releases/latest/download/docker-compose-windows-x86_64.exe" -OutFile "$env:ProgramFiles\Docker\cli-plugins\docker-compose.exe" # 验证插件是否就位 docker compose version
三、用一份 YAML 文件描述整套服务
假设要在 C:\apps\order-system 目录下编排一个小型系统:一个 IIS 静态门户加一个 .NET Framework 接口服务。目录结构很简单,site 子目录存放门户静态文件,api 子目录存放 Dockerfile 与发布产物,根目录放置 docker-compose.yml。所有服务、网络、卷都在这份文件里声明。
version: "3.8"
services:
web:
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
container_name: portal-web
isolation: process
ports:
- "8080:80"
volumes:
- C:\apps\order-system\site:C:\inetpub\wwwroot
restart: unless-stopped
networks:
- frontend
api:
build:
context: C:\apps\order-system\api
dockerfile: Dockerfile
container_name: order-api
isolation: process
environment:
- ASPNETCORE_ENVIRONMENT=Production
ports:
- "9000:80"
volumes:
- C:\apps\order-system\logs:C:\app\logs
depends_on:
- web
restart: unless-stopped
networks:
- frontend
networks:
frontend:
driver: nat
几个字段值得展开说明。volumes 在 Windows 上的写法是盘符路径对盘符路径,冒号两侧都必须是绝对路径,写成相对路径或者指向不存在的目录都会导致容器创建失败;ports 的宿主机端口与容器端口映射格式与 Linux 完全一致;restart: unless-stopped 保证容器异常退出或宿主机重启后自动拉起,前提是 Docker 服务本身被设置为自动启动。depends_on 只控制启动顺序,并不等待上游服务真正就绪,如果接口依赖门户完成初始化,要么给上游服务加 healthcheck 配合 condition 写法,要么在应用层做重试,后者实现成本更低。
api 服务的镜像通过 Dockerfile 构建,基础镜像选 .NET Framework 4.8 的 servercore 版本,把发布产物复制进 IIS 默认站点目录即可。
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019 COPY ./publish/ /inetpub/wwwroot EXPOSE 80
在 docker-compose.yml 所在目录执行 docker compose up -d --build,Compose 会先构建 api 镜像,再按依赖顺序创建并启动两个容器。首次运行需要从远端仓库拉取 servercore 基础镜像,耗时可能达到几分钟,属于正常现象。启动完成后用 docker compose ps 查看状态,浏览器访问宿主机 8080 端口即可看到门户页面。
四、生命周期管理与常用命令
日常运维基本围绕下面这组命令展开,掌握它们就能覆盖绝大多数场景。
| 命令 | 作用 |
|---|---|
docker compose up -d | 后台启动全部服务 |
docker compose ps | 查看各服务运行状态 |
docker compose logs -f api | 持续跟踪指定服务日志 |
docker compose exec api cmd | 进入容器内部排查问题 |
docker compose restart api | 重启单个服务 |
docker compose down | 停止并移除容器与网络 |
docker compose down -v | 连命名卷一并删除 |
有两点容易被忽视。其一,down 与 down -v 的区别在于后者会把命名卷一起删掉,如果数据库数据放在命名卷里,执行前务必确认。其二,修改 compose 文件后再次执行 up -d,Compose 会做差量更新,只重建配置发生变化的服务,未受影响的服务原样保留。这正是它比逐条执行 docker run 的脚本优雅得多的地方——配置文件天然成了可版本化管理的基础设施声明。
五、高频故障排查思路
排查故障时优先怀疑三类问题。第一类是 YAML 文件本身:缩进必须用空格而不能用 Tab;用记事本编辑容易保存出带 BOM 的 UTF-8 文件,解析时会报出莫名其妙的错误,建议用 VS Code 编辑并确认编码为无 BOM 的 UTF-8。
第二类是容器启动即退出。最常见的原因是镜像版本与宿主机不匹配,docker compose logs 里能看到操作系统不匹配的提示,解决办法是更换镜像标签或者把 isolation 改成 hyperv。另一个高频原因是端口冲突,宿主机上已有进程占用了映射端口时,容器同样无法启动。
# 定位占用 8080 端口的进程 netstat -ano | findstr :8080 # 按进程号强制结束 taskkill /PID 4520 /F # 查看接口服务最近两百行日志 docker compose logs --tail 200 api
第三类是网络与挂载问题。卷挂载的源路径必须真实存在且以盘符开头;防火墙也常被忽略,宿主机本机能访问不代表外部机器能访问,需要用 New-NetFirewallRule 放行对应端口。把这几类问题按顺序过一遍,绝大多数 Compose 编排故障都能在十分钟内定位到根因。
Docker ComposeWindows Server容器编排修改时间:2026-10-03 18:58:04