1. 项目概述
最近在帮团队把一个.NET Core项目部署到Ubuntu 22.04虚拟机的Docker环境中,整个过程踩了不少坑,也积累了一些实战经验。不同于Windows环境下的部署,Linux+Docker的组合需要特别注意运行时兼容性、权限管理和网络配置等问题。这篇文章将详细记录从环境准备到最终部署的全过程,特别适合那些需要将.NET应用容器化部署到Linux环境的开发者参考。
.NET Core的跨平台特性让我们可以在Linux服务器上运行原本基于Windows开发的应用程序,而Docker则进一步简化了部署和扩展的复杂度。Ubuntu 22.04作为长期支持版本,提供了稳定的基础环境。这种组合在中小型项目中特别实用,既能利用Linux服务器的性能优势,又能保持开发环境的灵活性。
2. 环境准备
2.1 虚拟机基础配置
首先需要准备Ubuntu 22.04的虚拟机环境。我使用的是VMware Workstation 17 Pro,分配了4核CPU、8GB内存和50GB磁盘空间。安装时选择最小化安装(Minimal Installation),这样可以减少不必要的软件包,保持系统干净。
安装完成后,有几个基础配置需要立即进行:
# 更新软件包列表 sudo apt update && sudo apt upgrade -y # 安装基础工具 sudo apt install -y curl wget git net-tools注意:在虚拟机中建议启用SSH服务,方便后续通过终端连接操作:
sudo apt install -y openssh-server sudo systemctl enable --now ssh
2.2 Docker环境安装
Ubuntu 22.04官方仓库中的Docker版本可能不是最新的,建议使用Docker官方提供的安装方式:
# 卸载旧版本(如果有) sudo apt remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt install -y ca-certificates curl gnupg lsb-release # 添加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 # 验证安装 sudo docker run hello-world安装完成后,建议将当前用户加入docker组,避免每次都要使用sudo:
sudo usermod -aG docker $USER newgrp docker # 立即生效3. 项目容器化
3.1 准备.NET Core项目
假设我们有一个ASP.NET Core Web API项目,项目结构如下:
MyApiProject/ ├── Controllers/ ├── Models/ ├── Program.cs ├── appsettings.json └── MyApiProject.csproj首先需要在本地开发环境发布项目:
dotnet publish -c Release -o ./publish这会在项目目录下生成一个publish文件夹,包含所有运行时需要的文件。
3.2 编写Dockerfile
在项目根目录创建Dockerfile,内容如下:
# 使用官方.NET运行时镜像作为基础 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 # 使用SDK镜像构建项目 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyApiProject/MyApiProject.csproj", "MyApiProject/"] RUN dotnet restore "MyApiProject/MyApiProject.csproj" COPY . . WORKDIR "/src/MyApiProject" RUN dotnet build "MyApiProject.csproj" -c Release -o /app/build # 发布项目 FROM build AS publish RUN dotnet publish "MyApiProject.csproj" -c Release -o /app/publish # 最终阶段 FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyApiProject.dll"]这个Dockerfile采用了多阶段构建,可以显著减小最终镜像的体积。关键点说明:
- 使用官方.NET镜像,确保运行时环境一致
- 分阶段构建:SDK阶段用于编译,运行时阶段只包含必要组件
- 暴露80和443端口,对应HTTP和HTTPS
- ENTRYPOINT指定容器启动时运行的命令
3.3 构建Docker镜像
将项目文件(包括Dockerfile)上传到Ubuntu虚拟机后,在项目根目录执行:
docker build -t myapi:1.0 .构建过程可能会花费几分钟时间,取决于项目复杂度和网络速度。构建完成后,可以用以下命令查看镜像:
docker images4. 运行容器
4.1 基本运行
最简单的运行方式是直接启动容器:
docker run -d -p 8080:80 --name myapi_container myapi:1.0参数说明:
-d: 后台运行-p 8080:80: 将容器内部的80端口映射到主机的8080端口--name: 为容器指定一个名称
4.2 生产环境配置
对于生产环境,建议添加以下配置:
docker run -d \ -p 80:80 \ -p 443:443 \ --name myapi_prod \ --restart unless-stopped \ -e ASPNETCORE_ENVIRONMENT=Production \ -e ASPNETCORE_URLS=http://+:80 \ -v /path/to/appsettings.Production.json:/app/appsettings.Production.json \ myapi:1.0关键配置项:
--restart unless-stopped: 容器意外退出时自动重启-e: 设置环境变量-v: 挂载配置文件,方便修改配置而无需重建镜像
5. 网络与存储配置
5.1 自定义网络
为了提高安全性和可管理性,建议为容器创建自定义网络:
docker network create myapi_network然后运行容器时指定网络:
docker run -d \ --network myapi_network \ --name myapi \ myapi:1.05.2 数据持久化
如果应用需要保存数据,可以使用Docker卷:
# 创建卷 docker volume create myapi_data # 使用卷 docker run -d \ -v myapi_data:/app/data \ myapi:1.06. 常见问题与解决方案
6.1 权限问题
在Linux上运行.NET Core应用可能会遇到文件权限问题,特别是在尝试写入文件时。解决方法:
- 确保Dockerfile中设置了正确的用户:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 USER app WORKDIR /app- 或者在运行时指定用户:
docker run -u 1000:1000 myapi:1.06.2 时区设置
容器内默认使用UTC时间,如果需要使用本地时区:
ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone6.3 性能调优
对于高负载应用,可以调整以下参数:
docker run -d \ --cpus 2 \ --memory 2g \ --memory-swap 4g \ myapi:1.07. 监控与日志
7.1 查看日志
查看容器日志:
docker logs -f myapi_container7.2 资源监控
查看容器资源使用情况:
docker stats myapi_container7.3 健康检查
在Dockerfile中添加健康检查:
HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost/health || exit 18. 使用Docker Compose
对于更复杂的部署,建议使用Docker Compose。创建docker-compose.yml文件:
version: '3.8' services: myapi: image: myapi:1.0 build: context: . dockerfile: Dockerfile ports: - "80:80" - "443:443" environment: - ASPNETCORE_ENVIRONMENT=Production networks: - myapi_network volumes: - myapi_data:/app/data restart: unless-stopped volumes: myapi_data: networks: myapi_network: driver: bridge启动服务:
docker-compose up -d9. CI/CD集成
可以将这个流程集成到CI/CD管道中。以下是GitHub Actions的示例:
name: Build and Deploy on: push: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v3 with: context: . push: true tags: yourusername/myapi:latest10. 安全最佳实践
- 定期更新基础镜像:
FROM mcr.microsoft.com/dotnet/aspnet:6.0@sha256:具体sha值使用非root用户运行容器
扫描镜像中的漏洞:
docker scan myapi:1.0- 限制容器权限:
docker run --read-only --security-opt=no-new-privileges myapi:1.0在实际部署中,我发现这种组合特别适合中小型项目的部署。它结合了.NET Core的开发效率、Linux的运行效率和Docker的部署便利性。最大的优势是一旦配置完成,后续的部署和扩展就变得非常简单。