.NetCore应用部署至K8S,实际上就四个问题需要解决:
- 日志等文件的持久化
- 配置appsettings.json
- 服务的健康检查
- 编写项目的yaml和dockerfile
我们一步一步来。
日志等文件的持久化
你的项目可能内含一些程序运行期间生成的文件,比如日志或用户上传的文件等,你肯定不想让其随容器的销毁而丢失,那么就要解决应用在k8s内的文件持久化问题。
这个问题的解决方法非常简单,就是在k8s内申请pvc,然后将应用对应的目录挂载到数据卷上就可以了,我们在此先创建对应的pvc资源,如何使用storageClass动态分配pv请参考这篇文章:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hulu-project-api-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
设置服务的配置文件
.NetCore应用的配置文件是appsettings,一般来说你肯定不会希望每次改变appsettings就要重新打包一次docker镜像重新进行部署,所以appsettings应该是独立可配置的,对于k8s来说,我们可以将其配置在configMap资源内,关于configMap的使用可以参考这两篇文章,讲解的非常详细,这里不再赘述:
现在我们来创建appsettings的configMap,在data内设置[key:value]的键值对格式可以了,value为你appsettings.json内的配置内容:
apiVersion: v1
kind: ConfigMap
metadata:
name: hulu-project-api-config
namespace: default
data:
appsettings.json: |-
{
"AppSettings": {
"EnablePrintingLog": true,
"InjectSpecificationDocument": true,
"AesKey": "xxxx"
},
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "/app/log/",
"restrictedToMinimumLevel": 2,
"rollingInterval": 3,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}"
}
},
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:5341"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithEnvironmentUserName",
"WithProcessId",
"WithHttpRequestId",
"WithCorrelationId",
"WithExceptionDetails",
"WithDemystifiedStackTraces",
"WithThreadId",
"WithClientIp",
"WithClientAgent"
]
},
"AllowedHosts": "*",
"Connection": [
{
"DbType": "MySql",
"DbKey": "HuLuMysql",
"ConnStr": "Database=hulu_project;Data Source=xxx;Port=3306;User Id=root;Password=xxx;Charset=utf8;Convert Zero Datetime=True;"
}
],
"Redis": {
"ConnectionString": "127.0.0.1:6379",
"InstanceName": "HuLuProject_"
},
"JWTSettings": {
"ValidateIssuerSigningKey": true,
"IssuerSigningKey": "xxx",
"ValidateIssuer": true,
"ValidIssuer": "HuLu",
"ValidateAudience": true,
"ValidAudience": "Abc",
"ValidateLifetime": true,
"ExpiredTime": 60,
"ClockSkew": 5,
"Algorithm": "HS256"
},
"CorsAccessorSettings": {
"PolicyName": "HuluPolicy",
"WithExposedHeaders": [
"X-Pagination",
"access-token",
"x-access-token"
],
"WithOrigins": [
"https://wfd.hafuhafu.cn"
]
}
}
服务的健康检查
如果需要容器内服务在出现异常时能够自动重启以持续不断的提供服务的话,那么服务的健康检查是必不可少的,关于k8s健康检查探针可以参考这篇文章,如果要使用健康检查,那么你的服务需要以tcp、http或其他的方式暴露出一个访问接口,比如http的话就是一个无参数调用返回200 OK的空接口,我们这里先定义一个liveness的探针,在稍后编写项目yaml的时候会用到:
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 15 # 第一次探测前等待15秒,给程序启动的时间
periodSeconds: 10 # 每10秒探测一次
timeoutSeconds: 10 # 探测超时时间10秒
successThreshold: 1 # 探测失败后,最少连续探测成功多少次才被认定为成功,默认是1,对于liveness必须是1,最小值是1
failureThreshold: 3 # 探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1
编写项目的yaml和dockerfile
既然要部署到k8s内,那dockerfile和yaml是必不可少的,dockerfile这块就不详细说了,因为大多数情况下vs2019内,右键项目“添加docker支持”,会自动生成一个dockerfile,这个dockerfile已经足以应对大部分情况了,我们主要说下yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hulu-project-api
namespace: default
labels:
app: project-api
spec:
replicas: 1
selector:
matchLabels:
app: project-api
template:
metadata:
labels:
app: project-api
spec:
volumes:
- name: hulu-project-api-config
configMap:
name: hulu-project-api-config # 与前文配置的configMap名称一致
- name: hulu-project-api-pvc
persistentVolumeClaim:
claimName: hulu-project-api-pvc # 与前文配置的pvc名称一致
containers:
- name: hulu-project-api
image: registry-vpc.cn-shanghai.aliyuncs.com/hulu0811/huluproject-api:457d9b28 # 镜像替换为你的应用的镜像
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: hulu-project-api-config
readOnly: true
mountPath: /app/appsettings.json # 此处为你应用的appsettings在容器内的位置
subPath: appsettings.json # 与前文配置的configMap的Key的名称一致,configMap的appsettings会替换掉镜像内原有的配置
- name: hulu-project-api-pvc
mountPath: /app/log # 此处为你项目运行时生成的需要持久化的文件的位置,比如log
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 3
之后还需要编写项目的service资源,以对外提供服务,如果你需要使用域名对外提供服务的话,还需要创建对应的ingress资源,关于ingress的安装与使用可以参考这两篇文章:
之后我们来创建对应的service和ingress:
# service
---
apiVersion: v1
kind: Service
metadata:
name: hulu-project-api
namespace: default
labels:
app: project-api
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: project-api
type: ClusterIP
# ingress
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hulu-project-api
namespace: default
labels:
app: project-api
annotations:
kubernetes.io/ingress.class: traefik # 这里使用了traefik作为ingress实现
traefik.ingress.kubernetes.io/router.entrypoints: 'web,websecure'
spec:
rules:
- host: api.hafuhafu.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hulu-project-api
port:
number: 80
创建好后,将全部yaml资源文件按顺序kubectl apply -f <filename>执行一遍,至此,.NetCore应用就成功部署在K8S内了。