1. 项目概述在Linux环境下部署.NET应用一直是个技术痛点特别是当需要快速迭代和隔离环境时。我最近在Ubuntu 22.04 LTS上完整走通了Docker部署.NET API的流程实测这套方案比传统部署方式效率提升至少3倍。Docker的轻量化特性让.NET应用在Linux上的运行像在Windows上一样顺畅而Ubuntu作为最流行的Linux发行版其稳定性又为生产环境提供了保障。这个方案特别适合以下场景需要快速搭建.NET开发/测试环境的团队希望实现开发环境与生产环境一致的DevOps流程在Linux服务器上部署.NET Core/5/6/7/8应用需要多版本.NET并行运行的场景整个过程涉及几个关键技术点Ubuntu系统的基础配置包括用户权限、网络等Docker引擎的安装与调优.NET SDK镜像的选择与定制容器化部署的权限控制生产环境下的性能优化重要提示虽然本文以Ubuntu 22.04为例但同样适用于20.04/24.04等LTS版本。对于WSL2下的Ubuntu环境只需注意磁盘挂载路径的差异即可。2. 环境准备与Docker安装2.1 Ubuntu系统基础配置在开始安装Docker前建议先完成这些系统级配置# 更新软件源并升级现有包 sudo apt update sudo apt upgrade -y # 安装基础工具集 sudo apt install -y ca-certificates curl gnupg lsb-release # 添加当前用户到docker组后续操作可免sudo sudo groupadd docker sudo usermod -aG docker $USER newgrp docker # 立即生效组权限变更这些操作解决了三个核心问题确保系统安全补丁最新准备后续安装所需的工具链避免每次docker命令都要sudo的麻烦常见坑点如果在WSL中操作需要先确认Windows端的Docker Desktop已正确配置WSL集成。可通过wsl -l -v查看WSL版本建议使用WSL2。2.2 Docker引擎安装详解官方推荐通过仓库安装而非deb包这样可以获得自动更新# 添加Docker官方GPG密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 设置稳定版仓库 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装Docker引擎 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin安装后必须进行的调优配置# 创建daemon配置文件 sudo tee /etc/docker/daemon.json -EOF { exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m }, storage-driver: overlay2, registry-mirrors: [https://registry.cn-hangzhou.aliyuncs.com] } EOF # 重启服务使配置生效 sudo systemctl enable docker sudo systemctl restart docker这个配置实现了使用systemd作为cgroup驱动兼容性更好日志轮转防止磁盘爆满国内镜像加速下载overlay2存储驱动性能最优验证安装成功的终极测试docker run --rm hello-world如果看到欢迎信息说明Docker已正确安装。如果遇到permission denied错误请检查用户组配置并确认已执行newgrp docker。3. .NET环境部署实战3.1 选择合适的基础镜像微软官方提供了多个.NET镜像变体主要区别在于mcr.microsoft.com/dotnet/sdk包含编译工具开发用mcr.microsoft.com/dotnet/aspnet仅运行时生产用标签差异7.0、6.0等指定版本-alpine为轻量版对于API项目推荐使用多阶段构建# 第一阶段构建 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY . . RUN dotnet publish -c Release -o /app # 第二阶段运行 FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app COPY --frombuild /app . ENTRYPOINT [dotnet, YourApi.dll]这种构建方式可以保持最终镜像最小化仅300MB左右避免源代码泄露风险分离构建依赖与运行依赖3.2 典型Dockerfile深度解析以一个实际的ASP.NET Core API项目为例完整Dockerfile应包含这些关键要素# 使用Alpine版进一步减小镜像体积 FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build # 安装必要的构建工具如git用于私有nuget包 RUN apk add --no-cache git # 优化nuget包恢复层 WORKDIR /src COPY [YourApi/YourApi.csproj, YourApi/] RUN dotnet restore YourApi/YourApi.csproj # 复制源码并构建 COPY . . RUN dotnet build -c Release --no-restore RUN dotnet publish -c Release -o /app --no-build # 运行时镜像 FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine WORKDIR /app # 设置时区中国时区 RUN apk add --no-cache tzdata \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone # 从构建阶段复制结果 COPY --frombuild /app . # 健康检查 HEALTHCHECK --interval30s --timeout3s \ CMD curl -f http://localhost:5000/health || exit 1 # 设置非root用户运行 RUN adduser -D appuser chown -R appuser /app USER appuser ENTRYPOINT [dotnet, YourApi.dll]这个配置解决了生产环境的几个关键问题镜像体积从1GB优化到约150MB正确处理中国时区符合安全最佳实践非root运行内置健康检查便于监控分层构建加速后续编译3.3 容器网络与端口配置.NET Core默认监听5000(HTTP)和5001(HTTPS)端口在Docker中需要正确映射# 开发环境运行端口映射挂载源码 docker run -d -p 8080:80 -p 4430:443 \ -v $(pwd):/app \ -e ASPNETCORE_ENVIRONMENTDevelopment \ --name myapi your-api-image # 生产环境运行只读文件系统 docker run -d -p 80:80 -p 443:443 \ --read-only \ -v /app/logs \ -e ASPNETCORE_ENVIRONMENTProduction \ --name myapi-prod your-api-image关键参数说明-p主机端口:容器端口映射-v挂载卷开发时挂载源码生产时挂载日志--read-only增强安全性需配合tmpfs或volume-e设置环境变量对于HTTPS支持需要在Dockerfile中额外配置# 在运行时镜像中添加证书 FROM mcr.microsoft.com/dotnet/aspnet:7.0 COPY --frombuild /app /app COPY ./certificates /usr/local/share/ca-certificates/ RUN update-ca-certificates4. 生产环境优化指南4.1 性能调优参数在docker run时添加这些参数可以显著提升.NET性能docker run -d \ --cpus2 \ # 限制CPU核心数 --memory1g \ # 限制内存 --pids-limit200 \ # 防止fork炸弹 --ulimit nofile1024:4096 \ # 文件描述符限制 -e COMPlus_ReadyToRun1 \ # 启用AOT编译 -e COMPlus_TieredCompilation1 \ # 分层编译 -e DOTNET_SYSTEM_GLOBALIZATION_INVARIANT1 \ # 禁用全球化 your-api-image这些环境变量的作用ReadyToRun提前编译减少JIT时间TieredCompilation优化长期运行的性能GlobalizationInvariant节省约30MB内存如果不需要本地化4.2 日志与监控方案推荐使用以下组合实现生产级监控标准输出日志 ELK// Program.cs中配置 builder.Logging.AddConsole();健康检查端点app.MapHealthChecks(/health);Prometheus监控// 安装nuget包: prometheus-net.AspNetCore app.UseMetricServer(); app.UseHttpMetrics();对应的Docker Compose配置示例version: 3.8 services: api: image: your-api-image ports: - 80:80 deploy: resources: limits: cpus: 2 memory: 1G healthcheck: test: [CMD, curl, -f, http://localhost/health] interval: 30s timeout: 5s retries: 3 prometheus: image: prom/prometheus ports: - 9090:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml grafana: image: grafana/grafana ports: - 3000:30004.3 常见问题排错手册问题1容器启动后立即退出检查项docker logs container_id docker inspect container_id | grep -i status解决方案确保Dockerfile中有ENTRYPOINT或CMD检查应用是否监听正确端口默认5000问题2数据库连接失败典型错误Connection refused 或 Unable to connect to SQL Server排查步骤确认容器网络模式docker network ls docker inspect container_id | grep -i network如果是bridge模式确保使用宿主IP而非localhost检查防火墙规则sudo ufw status问题3性能突然下降诊断命令docker stats container_id docker exec -it container_id top可能原因内存泄漏检查.NET GC相关指标线程死锁添加DOTNET_DiagnosticPorts环境变量磁盘IO瓶颈考虑使用tmpfs挂载问题4时区不正确解决方案# 在Dockerfile中添加 RUN apk add --no-cache tzdata ENV TZAsia/Shanghai验证方法docker exec -it container_id date5. 进阶技巧与安全加固5.1 多阶段构建优化对于大型解决方案可以采用更精细的多阶段构建# 阶段1还原nuget包 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS restore WORKDIR /src COPY [YourApi/YourApi.csproj, YourApi/] COPY [YourLib/YourLib.csproj, YourLib/] RUN dotnet restore YourApi/YourApi.csproj # 阶段2构建 FROM restore AS build COPY . . RUN dotnet build -c Release --no-restore # 阶段3测试 FROM build AS test RUN dotnet test -c Release --no-build # 阶段4发布 FROM build AS publish RUN dotnet publish -c Release -o /app --no-build # 阶段5运行时 FROM mcr.microsoft.com/dotnet/aspnet:7.0 COPY --frompublish /app . ENTRYPOINT [dotnet, YourApi.dll]这种结构允许独立缓存nuget还原层最大程度利用Docker缓存在CI/CD管道中复用中间阶段分离构建与测试环境5.2 安全加固措施生产环境必须实施的安全配置非root用户运行RUN adduser -D appuser --uid 1000 \ chown -R appuser /app USER appuser只读文件系统docker run --read-only -v /tmp -v /app/logs ...资源限制# docker-compose.yml中 deploy: resources: limits: cpus: 0.5 memory: 512M漏洞扫描# 安装trivy sudo apt install -y wget apt-transport-https gnupg lsb-release wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list sudo apt update sudo apt install -y trivy # 扫描镜像 trivy image your-api-image5.3 CI/CD集成示例GitHub Actions自动化部署脚本name: Build and Deploy on: push: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Login to Docker Hub uses: docker/login-actionv2 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Build and push uses: docker/build-push-actionv4 with: push: true tags: yourusername/your-api:latest cache-from: typegha cache-to: typegha,modemax deploy: needs: build runs-on: ubuntu-latest steps: - name: SSH and deploy uses: appleboy/ssh-actionmaster with: host: ${{ secrets.PRODUCTION_HOST }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | docker pull yourusername/your-api:latest docker stop live-api || true docker rm live-api || true docker run -d \ --name live-api \ -p 80:80 \ -p 443:443 \ --restart unless-stopped \ yourusername/your-api:latest这个流水线实现了自动构建Docker镜像并推送到仓库使用GitHub Actions缓存加速构建通过SSH自动部署到生产服务器零停机更新策略6. 性能对比与实测数据为了验证容器化部署的实际效果我在i7-1165G7/16GB的笔记本上进行了对比测试场景启动时间内存占用并发能力(req/s)原生运行1.2s120MB3,200Docker默认1.5s140MB2,900调优后容器1.3s125MB3,150测试使用的API是一个包含JWT认证、EF Core数据库访问和Redis缓存的典型WebAPI。关键发现容器化带来的性能损耗5%经过优化后内存开销主要来自额外的安全隔离层启动时间差异主要来自镜像层加载对于CPU密集型运算如加密操作容器性能几乎与原生一致。网络IO方面使用host网络模式可以获得最佳性能docker run --network host your-api-image但要注意这会降低隔离性仅推荐在受控环境使用。