基于.net框架的云原生TodoList Demo 项目,验证云原生核心特性
这是一个基于.NET 8和ASP.NET Core的云原生TodoList项目,展示了完整的云开发生命周期。核心功能包括: 1)使用EF Core实现CRUD操作 2)PostgreSQL数据库持久化 3)Docker容器化部署 4)Kubernetes集群编排 5)CI/CD自动化流程 6)Prometheus+Grafana监控体系 项目亮点: 多阶段Docker构建优化镜像 K8s Conf
·
以下是一个基于 .NET 8 + ASP.NET Core 的云原生 TodoList Demo 项目,涵盖 容器化、Kubernetes 编排、CI/CD、可观测性、弹性扩缩容 等核心云原生特性,代码简洁且附详细操作指南,适合入门学习。

项目概览
- 目标:实现一个支持增删改查(CRUD)的 TodoList 后端服务,通过云原生技术栈部署,展示完整的云原生实践流程。
- 技术栈:
- 后端:.NET 8 + ASP.NET Core(轻量、高性能、内置依赖注入)
- 数据库:PostgreSQL(云原生持久化存储)
- ORM:Entity Framework Core(EF Core,支持 PostgreSQL)
- 容器化:Docker(多阶段构建减小镜像体积)
- 编排:Kubernetes(K8s,声明式管理)
- CI/CD:GitHub Actions(自动化构建、测试、部署)
- 可观测性:Prometheus(监控指标) + Grafana(可视化) + Loki(日志聚合)
- 配置管理:K8s ConfigMap/Secret(外部化配置)

一、项目结构
todolist-cloudnative/
├── src/ # 核心业务代码
│ ├── TodoApi/ # ASP.NET Core Web API 项目
│ │ ├── Controllers/ # API 控制器
│ │ ├── Models/ # 数据库模型与 DTO
│ │ ├── Services/ # 业务逻辑层
│ │ ├── Data/ # 数据库上下文(EF Core)
│ │ ├── Program.cs # 应用入口(配置、中间件、指标)
│ │ └── appsettings.json # 本地配置(开发环境)
│ └── TodoApi.Tests/ # 单元测试(xUnit)
├── docker/ # Docker 配置
│ └── Dockerfile # 多阶段构建脚本(.NET 8 镜像)
├── k8s/ # K8s 部署配置
│ ├── todo-deployment.yaml # 应用 Deployment
│ ├── todo-service.yaml # 应用 Service
│ ├── postgres-statefulset.yaml # PostgreSQL StatefulSet
│ ├── postgres-service.yaml # PostgreSQL Service
│ ├── configmap.yaml # 非敏感配置(K8s 注入)
│ └── secret.yaml # 敏感配置(数据库密码,K8s Secret)
├── prometheus/ # Prometheus 监控配置
│ └── todolist.yml # 抓取规则(.NET 指标路径)
├── scripts/ # 辅助脚本(可选)
│ └── init-db.sql # 初始化数据库表(SQL 脚本)
├── .github/ # GitHub Actions 工作流
│ └── workflows/
│ └── deploy.yml # CI/CD 自动化流程(构建、部署)
└── todolist.sln # 解决方案文件

二、核心功能实现(.NET 8 + ASP.NET Core)
1. 初始化项目(.NET 8 Web API)
dotnet new webapi -n TodoApi
cd TodoApi
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL # PostgreSQL 驱动
dotnet add package Prometheus-net.AspNetCore # Prometheus 指标
dotnet add package AutoMapper # 对象映射(可选)
2. 数据库模型与上下文(src/TodoApi/Data/TodoDbContext.cs)
使用 EF Core 定义模型和数据库上下文,支持迁移。
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Data
{
public class TodoDbContext : DbContext
{
public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options) { }
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
}
public class TodoItem
{
public int Id { get; set; }
public required string Title { get; set; }
public string? Description { get; set; }
public bool IsCompleted { get; set; } = false;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
}
3. 业务逻辑与控制器(src/TodoApi/Controllers/TodoController.cs)
实现 CRUD 接口,集成 Prometheus 指标统计。
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using TodoApi.Data;
using TodoApi.Models;
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
private readonly TodoDbContext _context;
private readonly IMapper _mapper;
private static readonly Counter TodoCounter = Metrics.CreateCounter(
"todo_operations_total",
"Total number of Todo operations",
"operation");
public TodoController(TodoDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
// 创建待办事项
[HttpPost]
public async Task<ActionResult<TodoItem>> CreateTodo(TodoItemCreateDto createDto)
{
var todo = _mapper.Map<TodoItem>(createDto);
_context.TodoItems.Add(todo);
await _context.SaveChangesAsync();
TodoCounter.WithLabels("create").Inc(); // 统计创建操作
return CreatedAtAction(nameof(GetTodo), new { id = todo.Id }, todo);
}
// 获取所有待办事项
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodos()
{
TodoCounter.WithLabels("list").Inc(); // 统计列表操作
return await _context.TodoItems.ToListAsync();
}
// 其他接口(GetById、Update、Delete)类似...
}
// DTO(数据传输对象)
public class TodoItemCreateDto
{
public required string Title { get; set; }
public string? Description { get; set; }
}
}
4. 配置与指标暴露(src/TodoApi/Program.cs)
集成配置、中间件、Prometheus 指标端点。
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using TodoApi.Data;
using TodoApi.Profiles;
var builder = WebApplication.CreateBuilder(args);
// 配置数据库(从 K8s Secret/ConfigMap 注入)
builder.Services.AddDbContext<TodoDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// 配置 AutoMapper
builder.Services.AddAutoMapper(typeof(TodoProfile));
// 集成 Prometheus 指标
builder.Services.AddMetrics()
.AddRuntimeInstrumentation()
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
var app = builder.Build();
// 中间件
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// 暴露 Prometheus 指标端点
app.MapMetrics();
// 健康检查(K8s 存活/就绪探针)
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
var result = new
{
status = report.Status.ToString(),
checks = report.Entries.Select(e => new { e.Name, e.Status, e.Duration })
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(result);
}
});
app.Run();
5. 本地配置(src/TodoApi/appsettings.json)
开发环境配置(生产环境通过 K8s 注入)。
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=tododb;Username=admin;Password=mypassword"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

三、容器化(Docker)
docker/Dockerfile(多阶段构建,最小化镜像体积)
# 阶段 1:构建 .NET 8 应用
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /app
# 缓存依赖(仅当 csproj 或 packages.lock.json 变化时重新下载)
COPY TodoApi/TodoApi.csproj TodoApi/
COPY TodoApi/TodoApi.Tests/TodoApi.Tests.csproj TodoApi.Tests/
RUN dotnet restore TodoApi/TodoApi.csproj
# 复制代码并构建
COPY . .
RUN dotnet publish TodoApi/TodoApi.csproj -c Release -o out
# 阶段 2:运行轻量级镜像(仅包含运行时)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
# 从构建阶段复制发布文件
COPY --from=builder /app/out .
# 暴露端口(.NET API 默认端口 5000/5001,K8s Service 映射到 80)
EXPOSE 80
# 启动命令(使用 Kestrel 服务器)
ENTRYPOINT ["dotnet", "TodoApi.dll"]

四、Kubernetes 编排(云原生核心)
1. 配置管理(k8s/configmap.yaml 和 k8s/secret.yaml)
非敏感配置(ConfigMap,注入数据库连接字符串等):
apiVersion: v1
kind: ConfigMap
metadata:
name: todo-config
data:
ConnectionStrings__DefaultConnection: "Host=postgres-service;Database=tododb;Username=admin;Password=mypassword"
敏感配置(Secret,注入数据库密码,需 base64 编码):
# 生成 base64 编码(实际生产建议用 HashiCorp Vault 等工具)
echo -n "admin" | base64 # 用户名
echo -n "mypassword" | base64 # 密码
apiVersion: v1
kind: Secret
metadata:
name: todo-secret
type: Opaque
data:
ConnectionStrings__DefaultConnection: "Host=postgres-service;Database=tododb;Username=YWRtaW4=;Password=bXlwYXNzd29yZA=="
2. PostgreSQL 部署(k8s/postgres-statefulset.yaml)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-service
replicas: 1 # 生产环境建议 3 副本 + 主从复制(如 Patroni)
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_USER
value: "admin"
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: todo-secret
key: POSTGRES_PASSWORD # 假设 Secret 中单独存储密码
- name: POSTGRES_DB
value: "tododb"
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates: # 持久化存储(云原生存储卷,如 AWS EBS、阿里云盘)
- metadata:
name: postgres-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
3. Todo 服务部署(k8s/todo-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-deployment
spec:
replicas: 2 # 初始副本数(弹性扩缩容基础)
selector:
matchLabels:
app: todo
template:
metadata:
labels:
app: todo
spec:
containers:
- name: todo
image: your-docker-username/todolist:v1 # 替换为实际镜像地址
ports:
- containerPort: 80
env:
- name: ConnectionStrings__DefaultConnection
valueFrom:
configMapKeyRef:
name: todo-config
key: ConnectionStrings__DefaultConnection
livenessProbe: # 存活探针(自动重启异常实例)
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe: # 就绪探针(流量路由前检查)
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
resources: # 资源限制(K8s 调度依据)
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
4. 服务暴露(k8s/todo-service.yaml 和 k8s/todo-ingress.yaml)
ClusterIP Service(内部访问):
apiVersion: v1
kind: Service
metadata:
name: todo-service
spec:
type: ClusterIP
selector:
app: todo
ports:
- protocol: TCP
port: 80
targetPort: 80
Ingress(外部访问,需安装 NGINX Ingress Controller):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: todo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /todos
pathType: Prefix
backend:
service:
name: todo-service
port:
number: 80


五、CI/CD 自动化(GitHub Actions)
.github/workflows/deploy.yml
name: Deploy to Kubernetes
on:
push:
branches: [ "main" ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore TodoApi/TodoApi.csproj
- name: Build project
run: dotnet build TodoApi/TodoApi.csproj -c Release --no-restore
- name: Publish project
run: dotnet publish TodoApi/TodoApi.csproj -c Release -o ./publish
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
push: true
tags: your-docker-username/todolist:latest,your-docker-username/todolist:${{ github.sha }}
deploy-to-k8s:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Deploy to Kubernetes cluster
run: |
# 替换镜像标签为最新提交 SHA
sed -i "s|your-docker-username/todolist:v1|your-docker-username/todolist:${{ github.sha }}|g" k8s/todo-deployment.yaml
# 应用 K8s 配置
kubectl apply -f k8s/
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }} # 从 GitHub Secrets 读取集群配置
六、可观测性配置
1. Prometheus 监控(prometheus/todolist.yml)
scrape_configs:
- job_name: "todo_service"
scrape_interval: 15s
metrics_path: "/metrics"
static_configs:
- targets: ["todo-service:80"] # K8s Service 域名(集群内部可解析)
2. 日志集成(Loki)
- 在
appsettings.json中配置日志输出为 JSON 格式:"Logging": { "Console": { "FormatterName": "json", "FormatterOptions": { "IncludeScopes": false, "TimestampFormat": "o" } } } - 部署 Promtail 收集日志并发送到 Loki(需额外配置)。
3. Grafana 仪表盘(示例查询)
- 请求速率:
rate(todo_operations_total{job="todo_service"}[5m]) - 平均延迟:
rate(http_request_duration_seconds_sum{job="todo_service"}[5m]) / rate(http_request_duration_seconds_count{job="todo_service"}[5m])
七、云原生特性验证
1. 容器化
- 本地构建镜像:
docker build -t todolist:v1 -f docker/Dockerfile . - 运行测试:
docker run -p 80:80 todolist:v1,访问http://localhost:80/api/todos验证接口。
2. Kubernetes 部署
- 本地搭建 K8s 集群(Minikube 或 Kind):
minikube start - 应用配置:
kubectl apply -f k8s/ - 查看状态:
kubectl get pods,svc,ingress。
3. 弹性扩缩容
- 手动扩缩容:
kubectl scale deployment/todo-deployment --replicas=3 - 自动扩缩容(HPA):
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: todo-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: todo-deployment minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # CPU 使用率超 70% 自动扩容
4. 故障自愈
- 手动删除 Pod:
kubectl delete pod <pod-name>,观察 K8s 自动重建新 Pod(状态变为Running)。
5. 持续交付
- 推送代码到 GitHub
main分支,触发 GitHub Actions 自动构建、测试、部署。
八、总结
通过这个 TodoList Demo,你可以完整体验云原生应用的核心流程:
- 容器化:用 Docker 封装应用,确保环境一致性。
- Kubernetes 编排:通过 Deployment、Service 等资源实现自动化管理。
- CI/CD:GitHub Actions 实现代码提交到生产的全自动化。
- 可观测性:Prometheus + Grafana 监控性能,Loki 收集日志。
- 弹性扩缩容:HPA 根据负载自动调整资源,保障高可用。
后续可扩展方向:
- 微服务拆分(如用户服务、待办服务),用 Istio 实现服务网格。
- 引入数据库读写分离(主从复制)。
- 添加认证鉴权(OAuth2、JWT)。
- 使用 Helm 打包 K8s 配置,简化多环境部署。
更多推荐
所有评论(0)