关注墨瑾轩,带你探索编程的奥秘!
超萌技术攻略,轻松晋级编程高手
技术宝库已备好,就等你来挖掘
订阅墨瑾轩,智趣学习不孤单
即刻启航,编程之旅更有趣
“容器化?不就是把代码扔进Docker镜像里吗?”
如果你这样想,那说明你还没见过把应用打包成"俄罗斯套娃"的快乐!
在云原生的世界里,容器就像乐高积木:
但手动操作?那简直是程序员的"自虐行为"!今天我们就用C#代码+Kubernetes,把应用变成"会自己组装的宇宙飞船"!
“Dockerfile?这和写作业有什么区别?”
# Dockerfile
# 基础镜像:轻量级的Alpine Linux
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
# 构建镜像阶段(多阶段构建)
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["MyApp/MyApp.csproj", "MyApp/"]
RUN dotnet restore "MyApp/MyApp.csproj"
COPY . .
WORKDIR "/src/MyApp"
RUN dotnet build -c Release -o /app/build
# 发布阶段
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
# 最终运行阶段
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
# 注释解释:
# 1. 使用多阶段构建减少镜像体积
# 2. 最终镜像只包含运行时依赖
# 3. ENTRYPOINT确保容器启动时自动运行应用
“镜像体积2GB?这和拖着整个Windows系统跑马拉松有什么区别?”
# 优化版Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base
WORKDIR /app
# ... 其他步骤保持不变 ...
“Kubernetes?这名字听着像某种外星语言”
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3 # 启动3个副本,像"三体文明"一样冗余
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:latest # 替换为你的镜像地址
ports:
- containerPort: 80
# 资源限制:CPU/内存像"预算"一样严格管理
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
“应用在容器里,但用户怎么访问?”
这时候你需要一个"跨维传送门"——Service
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer # 云端用LoadBalancer,本地用NodePort
ports:
- port: 80
targetPort: 80
selector:
app: myapp
“环境变量硬编码?这和把密码写在Post-it上有什么区别?”
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
ConnectionString: "Server=mydb;Database=mydb;User=myuser;Password=mypass;"
MaxConnections: "100"
// Program.cs
public class Program
{
public static void Main(string[] args)
{
var configMap = Environment.GetEnvironmentVariable("ConnectionString");
// ... 使用配置连接数据库
CreateHostBuilder(args).Build().Run();
}
}
“数据随容器消失?这和用纸条记密码有什么区别?”
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: myapp-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/myapp"
# deployment.yaml(修改版)
...
spec:
containers:
- name: myapp
volumeMounts:
- name: myapp-storage
mountPath: "/app/data"
volumes:
- name: myapp-storage
persistentVolumeClaim:
claimName: myapp-pvc
“升级应用需要停机?这和换衣服要脱光再穿有什么区别?”
# deployment.yaml(修改版)
...
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多启动1个Pod
maxUnavailable: 0 # 至少保持所有Pod可用
“CPU飙到100%才发现?这和等急诊室有什么区别?”
// MetricsMiddleware.cs
public class MetricsMiddleware
{
private readonly RequestDelegate _next;
public MetricsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 记录请求计数
Metrics.Counter("http_requests_total").Add(1);
// 记录响应时间
var stopwatch = Stopwatch.StartNew();
await _next(context);
stopwatch.Stop();
Metrics.Histogram("http_request_duration_seconds").Observe(stopwatch.Elapsed.TotalSeconds);
}
}
“每次改YAML太麻烦?这和手写乐高说明书有什么区别?”
myapp-chart/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl
└── README.md
replicaCount: 3
image:
repository: myregistry/myapp
tag: latest
pullPolicy: IfNotPresent
“手动推送镜像?这和用邮局寄包裹有什么区别?”
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push image to registry
run: docker push myregistry/myapp:${{ github.sha }}
- name: Deploy to Kubernetes
uses: azure/k8s-set-context@v1
with:
name: my-cluster
server: my-k8s-server
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
- name: Apply Helm chart
run: helm upgrade --install myapp ./myapp-chart --set image.tag=${{ github.sha }}
“现在你已经掌握了云原生容器化的’九阳真经’”
通过今天的修炼,你已经能:
最后彩蛋:
// 在Program.cs里加个"防老板突击检查"的彩蛋
Console.WriteLine("容器化应用已启动!请老板放心:您的服务比我的代码更健壮!");
标题备选方案: