命名空间创建
在开始之前,请先创建一个专用命名空间(示例名为 your-namespace
):
kubectl create namespace your-namespace
安装 NGINX Ingress Controller ☸️
若尚未安装 Ingress Controller,可使用 Helm 进行安装(示例使用 ingress-nginx
):
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx
安装完成后,可通过以下命令确认 Controller 已就绪:
kubectl get pods -n ingress-nginx
前提环境
your-registry/abp-app
,并使用标签 blue
与 green
做版本区分your-namespace
部署,若需修改,请将 namespace: your-namespace
替换为实际环境命名空间目标一:多阶段 Docker 镜像构建
使用 .NET 8.0 SDK 构建并生成最小的 ASP.NET Core 运行时镜像。
目标二:蓝绿发布策略
在 Kubernetes 中使用两套 Deployment(Blue 与 Green)并通过 Service Selector 切换流量,实现零停机发布。
目标三:配置管理
使用 ConfigMap 挂载 appsettings.json
,并通过 reloadOnChange: true
实现热重载;敏感信息放入 Secret,遵循最小权限原则。
目标四:健康检查
在 ABP 应用中配置健康检查端点 /healthz
,并在 Deployment 中配置 ReadinessProbe 与 LivenessProbe,保障版本切换的无缝。
目标五:安全与监控
提示最小权限运行、非 root 用户、资源限额、日志采集与指标监控推荐,帮助构建生产级方案。
在项目根目录下,创建 Dockerfile
并填写以下内容。示例假设以下目录结构:
.
├── MySolution.sln
└── src
└── YourProjectName
├── YourProjectName.csproj
└── (其余源文件)
# ---------------- BUILD STAGE ----------------
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# 复制解决方案文件及项目文件,执行 restore
COPY *.sln ./
COPY src/YourProjectName/YourProjectName.csproj ./src/YourProjectName/
RUN dotnet restore
# 复制所有源代码并发布到 /app/publish
COPY . .
RUN dotnet publish src/YourProjectName/YourProjectName.csproj -c Release -o /app/publish
# ---------------- RUNTIME STAGE ----------------
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
# 创建非 root 用户以提高安全性
RUN adduser --disabled-password --gecos "" appuser \
&& chown -R appuser /app
USER appuser
# 复制发布产物
COPY --from=build /app/publish .
# 设置监听端口与环境
ENV ASPNETCORE_URLS=http://+:80
ENV DOTNET_ENVIRONMENT=Production
EXPOSE 80
ENTRYPOINT ["dotnet", "YourProjectName.dll"]
**/bin
**/obj
**/.vs
**/*.user
**/*.suo
✅ 说明
.dockerignore
文件可显著加速 Docker 构建并减少镜像体积。- 若您的项目结构与示例不同,请相应调整
COPY
路径。ENTRYPOINT
中的YourProjectName.dll
必须与发布输出一致,若有所不同,请替换为实际可执行文件名。
apiVersion: v1
kind: Namespace
metadata:
name: your-namespace
apiVersion: v1
kind: ConfigMap
metadata:
name: abp-config
namespace: your-namespace
data:
appsettings.json: |
{
"App": {
"Name": "ABP in K8s",
"HotReload": true
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}
挂载示例(在 Deployment 中使用):
volumeMounts: - name: config-volume mountPath: /app/appsettings.json subPath: appsettings.json volumes: - name: config-volume configMap: name: abp-config
apiVersion: v1
kind: Secret
metadata:
name: abp-secrets
namespace: your-namespace
type: Opaque
data:
# 请将真实值进行 base64 编码后填入
ConnectionStrings__Default: -encoded-db-connection-string>
ConnectionStrings__Redis: -encoded-redis-connection-string>
Jwt__Secret: -encoded-jwt-secret>
创建命令示例(以 Linux/macOS 为例):
echo -n "Server=mssql;Database=YourDb;User Id=sa;Password=YourPassword;" | base64 # 输出类似:U2VydmVyPW1zc3FsO0RhdGFiYXNlPVlvdXJEYjtVc2VyIElkPXNhO1Bhc3N3b3JkPVlvdXJQYXNzd29yZDs= kubectl -n your-namespace create secret generic abp-secrets \ --from-literal=ConnectionStrings__Default="Server=mssql;Database=YourDb;User Id=sa;Password=YourPassword;" \ --from-literal=ConnectionStrings__Redis="Server=redis;Password=YourRedisPassword;" \ --from-literal=Jwt__Secret="YourJwtSecret"
安全提示
- 生产环境切勿直接将数据库连接串等明文写入 ConfigMap。
- 应使用 Kubernetes Secret 存储敏感信息,并结合 RBAC 限制访问权限。
- 如需更高安全性,可使用 Vault 或 Kubernetes CSI Secrets Store 将 Secret 以加密方式挂载到 Pod。
ℹ️ 说明
- ConfigMap 将配置文件挂载到 Pod 内特定路径,应用通过
AddJsonFile("appsettings.json", reloadOnChange: true)
热加载。- Secret 中的密钥通过环境变量注入到 Pod,应用可以用
builder.Configuration.GetConnectionString("Redis")
等方式读取。
apiVersion: apps/v1
kind: Deployment
metadata:
name: abp-app-blue
namespace: your-namespace
labels:
app: abp
version: blue
spec:
replicas: 2
selector:
matchLabels:
app: abp
version: blue
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app: abp
version: blue
spec:
containers:
- name: abp-container
image: your-registry/abp-app:blue
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: abp-config
- secretRef:
name: abp-secrets
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 20
periodSeconds: 20
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
volumeMounts:
- name: config-volume
mountPath: /app/appsettings.json
subPath: appsettings.json
volumes:
- name: config-volume
configMap:
name: abp-config
apiVersion: apps/v1
kind: Deployment
metadata:
name: abp-app-green
namespace: your-namespace
labels:
app: abp
version: green
spec:
replicas: 2
selector:
matchLabels:
app: abp
version: green
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app: abp
version: green
spec:
containers:
- name: abp-container
image: your-registry/abp-app:green
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: abp-config
- secretRef:
name: abp-secrets
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 20
periodSeconds: 20
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
volumeMounts:
- name: config-volume
mountPath: /app/appsettings.json
subPath: appsettings.json
volumes:
- name: config-volume
configMap:
name: abp-config
✅ 说明
- 初始部署时,仅需创建 Blue Deployment;如需先验证 Blue 无误,再创建 Green Deployment,保持 Service 指向 Blue。
rollingUpdate
策略可保证滚动更新时 Pod 始终保持可用,并且按需扩容。若只需纯粹蓝绿切换,可将strategy.type
改为Recreate
。- 确保项目的工作目录与挂载路径保持一致,例如本示例假设可执行文件与配置文件位于容器内部
/app
下。
apiVersion: v1
kind: Service
metadata:
name: abp-service
namespace: your-namespace
spec:
type: ClusterIP
selector:
app: abp
version: blue # 初始指向 Blue 版本
ports:
- port: 80
targetPort: 80
蓝绿切换命令
- 将流量切换到 Green:
kubectl -n your-namespace patch svc abp-service \ -p '{"spec":{"selector":{"app":"abp","version":"green"}}}'
- 回滚至 Blue:
kubectl -n your-namespace patch svc abp-service \ -p '{"spec":{"selector":{"app":"abp","version":"blue"}}}'
- ⚠️ 注意:同时运行 Blue/Green 会占用双倍资源,请确保集群容量充足;切换稳定后可删除不再使用的一侧 Deployment。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: abp-ingress
namespace: your-namespace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- abp.yourdomain.com
secretName: abp-tls
rules:
- host: abp.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: abp-service
port:
number: 80
TLS Secret 创建示例
kubectl -n your-namespace create secret tls abp-tls \ --cert=/path/to/tls.crt \ --key=/path/to/tls.key
ℹ️ 说明
ingressClassName: nginx
假定已安装并就绪 NGINX Ingress Controller,且IngressClass
名称为nginx
。rewrite-target: /
会将所有外部路径重写到/
,适合后端全路径托管场景。若需保留原始路径,可移除该注解,或使用正则重写。- 如需精确匹配根路径,可将
pathType: Prefix
改为Exact
。
为了让 Kubernetes 的探针(Probe)与 ABP 应用的健康检查端点对齐,需要在应用内部添加健康检查服务。
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
var builder = WebApplication.CreateBuilder(args);
// 配置 appsettings.json 并开启热重载
builder.Host.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables(prefix: "ASPNETCORE_");
});
// 注册健康检查服务
builder.Services.AddHealthChecks()
.AddCheck("Self", () => HealthCheckResult.Healthy());
// 可根据需要添加更多检查项,例如:
// .AddSqlServer(builder.Configuration.GetConnectionString("Default"), name: "SQL Server", failureStatus: HealthStatus.Unhealthy)
// .AddRedis(builder.Configuration.GetConnectionString("Redis"), name: "Redis", failureStatus: HealthStatus.Degraded);
builder.Services.AddControllers(); // 注册控制器
var app = builder.Build();
app.UseRouting();
// 映射健康检查端点
app.MapHealthChecks("/healthz");
// 映射 API 控制器
app.MapControllers();
app.Run();
✅ 说明
.AddHealthChecks()
默认会注册一个自检项,使/healthz
始终返回 200。- 若要做更细粒度检查,可启用
.AddSqlServer()
或.AddRedis()
等扩展方法,此时需引用对应 NuGet 包。reloadOnChange: true
可在 ConfigMap 更新后自动加载新的配置值,无需重启应用。
ℹ️ 说明
- 应用启动后,注册健康检查并映射路由。
- Kubernetes 发起 ReadinessProbe 和 LivenessProbe 请求到
/healthz
,判断 Pod 是否就绪或存活。- 当探针返回
200 OK
时,Pod 被认为健康;否则 K8s 会执行重启或不就绪操作。
详情参考:健康检查:在 .NET 微服务模板中优雅配置 Health Checks
以下流程展示在 Kubernetes 中从 Blue 到 Green 的切换细节,并提供回滚步骤。
部署 Blue 版本
kubectl apply -f namespace.yaml
kubectl -n your-namespace apply -f configmap.yaml
kubectl -n your-namespace apply -f secret.yaml
docker build -t your-registry/abp-app:blue .
docker push your-registry/abp-app:blue
kubectl -n your-namespace apply -f deployment-blue.yaml
kubectl -n your-namespace apply -f service.yaml
kubectl -n your-namespace get pods -l version=blue
kubectl -n your-namespace exec -it <blue-pod> -- curl -f http://localhost/healthz
部署 Green 版本(保留 Blue)
docker build -t your-registry/abp-app:green .
docker push your-registry/abp-app:green
kubectl -n your-namespace apply -f deployment-green.yaml
kubectl -n your-namespace get pods -l version=green
kubectl -n your-namespace exec -it <green-pod> -- curl -f http://localhost/healthz
切换 Service Selector 至 Green
kubectl -n your-namespace patch svc abp-service \
-p '{"spec":{"selector":{"app":"abp","version":"green"}}}'
监控 Green 稳定性
kubectl -n your-namespace get pods -l version=green
kubectl -n your-namespace logs -l version=green --tail=50
回滚至 Blue(如有必要) ⚠️
如果在监控过程中发现 Green 不稳定,可立即执行:
kubectl -n your-namespace patch svc abp-service \
-p '{"spec":{"selector":{"app":"abp","version":"blue"}}}'
删除 Blue 版本 ️
当 Green 完全稳定后,可按以下命令删除 Blue Deployment 及其关联资源:
kubectl -n your-namespace delete deployment abp-app-blue
kubectl -n your-namespace get rs -l version=blue
kubectl -n your-namespace delete rs -l version=blue
日志采集
指标监控
/metrics
端点。prometheus.io/scrape: "true"
的 Pod 并采集指标。告警与自动扩缩容
最小权限原则
镜像安全
网络策略
审计与日志保留
数据库兼容性
零停机发布 ✅
配置热重载
appsettings.json
并启用 reloadOnChange: true
,无需重启容器即可动态更新配置。安全可控
高可用高性能 ⚡
可观测体系
Microsoft Docs
Docker Docs
Prometheus & Grafana
Trivy