|
文章声明:本文系油管上的一个系列(.NET Microservices – Full Course)教程的学习记录的第五章。不涉及营销,有兴趣看可以原视频[1]。本文分成四部分:持久化卷申领、Kubernetes 密钥配置、部署SQL Server到Kubernetes、更新平台服务。 Kubetnetes持久化
Persistent Volumes Claim xxx
Persistent Volumn xxx
Storage Class xxx
创建持久卷申领
配置yaml文件
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mssql-claim
spec:
resources:
requests:
storage: 100Mi
accessModes:
- ReadWriteOnce然后基于yaml文件创建申领卷
>>kubectl apply -f local.pvc.yaml检查结果

默认创建的申领持久卷在xxx
Kubenetes密钥创建
此处将为之后的SQL Server创建账号以及密码。
>>kubectl create secret generic mssql --from-literal=SA_PASSWORD="pa55wOrd!"这里的要点是
- Secret的名称是mssql
- Key是SA_PASSWORD
- Key的Value是pa55w0rd!
在创建SQL Server的yaml文件时会运用到以上的信息
在Kubenetes中创建SQL Server
首先来看下加入SQL Server之后整个微服务的架构

考虑SQL Server之后的整体架构
创建SQL Server的yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: mssql-depl
spec:
replicas: 1
selector:
matchLabels:
app: mssql
template:
metadata:
labels:
app: mssql
spec:
containers:
- name: mssql
image: mcr.microsoft.com/mssql/server:2017-latest
ports:
- containerPort: 1433
env:
- name: MSSQL_PID
value: "Express"
- name: ACCEPT_EULA
value: "Y"
- name: SA_PASSWORD
valueFrom:
secretKeyRef:
name: mssql
key: SA_PASSWORD
volumeMounts:
- mountPath: /var/opt/mssql/data
name: mssqldb
volumes:
- name: mssqldb
persistentVolumeClaim:
claimName: mssql-claim注意事项:
- deployment的名字时mssql-depl,之后通过kubectl get deployments查看到的就是这个名字
- 整个deployment会去选择名为mssql的模板(selector app),同时创建了名为mssql的模板(template pp)
- 名为mssql的模板将创建名为mssql的容器(container name),该容器的镜像地址为http://mcr.microsoft.com/mssql/server:2017-latest,并且配有标签(2017-latest)
- 容器对外暴露的端口号是1433
- 容器的环境变量(配置)如下。MSSQL_PID的值为Express,ACCEPT_EULA的值为Y,SA_PASSWORD的值取自名为mssql的secret中的key为SA_PASSWORD的值
- SQL Server的卷挂载地址为容器所在环境内的地址。这里SQL Server将运行在Linux系统中,所以配置了一个Linux地址。
- SQL Server使用的持久卷名为mssqldb、和挂载的名字一致(实际上,应该是先设置好持久卷名,然后配置容器的持久卷挂载地址、并确定挂载的卷名)。持久卷申领的名字为mssql-claim、即为之前申领的名字
为SQL Server配置Cluster IP
在加入SQL Server后,为了让其它服务能够访问到SQL Server,需要配置一个Cluster IP。
apiVersion: v1
kind: Service
metadata:
name: mssql-clusterip-svc
spec:
type: ClusterIP
selector:
app: mssql
ports:
- name: mssql
protocol: TCP
port: 1433
targetPort: 1433注意事项:
- 这里选择的app是mssql、即要创建的mssql容器。
- Port和Target Port都和容器端口一致。
为SQL Server配置负载均衡
负载均衡 xxx
配置的yaml脚本
apiVersion: v1
kind: Service
metadata:
name: mssql-loadbalancer
spec:
type: LoadBalancer
selector:
app: mssql
ports:
- name: mssql
protocol: TCP
port: 1433
targetPort: 1433部署SQL Server
执行命令
>>kubectl apply -f mssql-depl.yaml

部署SQL Server及关联服务

检查SQL Server关联服务

检查Pods

Docker中也显示SQL Server已经部署完成(app名为mssql,部署名为mssql-depl)
此时,可以通过数据库管理软件(如SSMS)进行登录查看

可以在其中创建一个数据库、并向从中添加一些数据。如果此时删除容器,再打开数据库,数据仍然存在。这是因为数据持久化在主机中、不会被容器或者Pods的生命周期影响。
更新平台服务、连接SQL Server
配置数据库连接字符串
在appsettings.Production.json中,配置数据库的连接字符串
{
"CommandService": "http://commands-clusterip-svc:80",
"ConnectionStrings": {
"PlatformsConn": "Server=mssql-clusterip-svc,1433;Initial Catalog=platformsdb;User ID=sa;Password=pa55w0rd!"
}
}这里的Server地址就是Cluster IP——使用这个地址是因为一旦平台服务部署好之后,它会同SQL Server一样都在Kubernetes的Node里面。
注入并使用SQL Server
之前平台服务统一使用内存数据库,现在将更新配置,使得在生产环境下使用SQL Server。
首先要引入环境变量,区分开发与生产环境
using Microsoft.Extensions.Hosting;
public Startup(
IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
private readonly IWebHostEnvironment _env;
然后根据环境注入不同的数据库
public void ConfigureServices(IServiceCollection services)
{
if (_env.IsDevelopment())
{
Console.WriteLine(">>>Using InMem Db");
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseInMemoryDatabase(&#34;InMemory&#34;));
}
else if (_env.IsProduction())
{
Console.WriteLine(&#34;>>>Using SQL Server&#34;);
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString(&#34;PlatformsConn&#34;)));
}
}
这里会从配置文件中读取数据库连接字符串PlatformConn
生成初始化的迁移脚本
一般来说,可以直接调用EF Core的脚本命令(Visual Studio)进行初始化
add-migration InitCreation但是因为在开发环境(默认环境)下使用内存数据库,migration会发生错误。因此,一方面需要在appsettings.Development.json中,配置数据库的连接字符串,另一方面需要在代码中做出临时的修改。
首先是配置文件
{
&#34;Logging&#34;: {
&#34;LogLevel&#34;: {
&#34;Default&#34;: &#34;Information&#34;,
&#34;Microsoft&#34;: &#34;Warning&#34;,
&#34;Microsoft.Hosting.Lifetime&#34;: &#34;Information&#34;
}
},
&#34;CommandService&#34;: &#34;https://localhost:44345&#34;,
&#34;ConnectionStrings&#34;: {
&#34;PlatformsConn&#34;: &#34;Server=localhost,1433;Initial Catalog=platformsdb;User ID=sa;Password=pa55w0rd!&#34;
}
}这里不同于生产环境的配置,因为对数据库的访问是主机访问Kubernetes,因此要使用localhost作为IP地址。
再是在代码中的临时修改
//if (_env.IsDevelopment())
//{
// Console.WriteLine(&#34;>>>Using InMem Db&#34;);
// services.AddDbContext<ApplicationDbContext>(opt =>
// opt.UseInMemoryDatabase(&#34;InMemory&#34;));
//}
//else if (_env.IsProduction())
//{
// Console.WriteLine(&#34;>>>Using SQL Server&#34;);
// services.AddDbContext<ApplicationDbContext>(opt =>
// opt.UseSqlServer(Configuration.GetConnectionString(&#34;PlatformsConn&#34;)));
//}
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString(&#34;PlatformsConn&#34;)));
这样就可以进行migration脚本的生成。
但是还遇到了其它的麻烦,报错内容为
Add-Migration : 无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。 该问题的解决思路可以参考这两篇文章[2][3]
完成之后,命令行截图如下

可以看到对于之前平台服务创建的模型[4]有对应的migration。
namespace PlatformService.Migrations
{
public partial class InitialCreation : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: &#34;Platforms&#34;,
columns: table => new
{
PlatformId = table.Column<int>(type: &#34;int&#34;, nullable: false)
.Annotation(&#34;SqlServer:Identity&#34;, &#34;1, 1&#34;),
Name = table.Column<string>(type: &#34;nvarchar(max)&#34;, nullable: false),
Publisher = table.Column<string>(type: &#34;nvarchar(max)&#34;, nullable: false),
Cost = table.Column<string>(type: &#34;nvarchar(max)&#34;, nullable: false)
},
constraints: table =>
{
table.PrimaryKey(&#34;PK_Platforms&#34;, x => x.PlatformId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: &#34;Platforms&#34;);
}
}
}
一旦生成migration脚本之后,就可以取消之前的临时修改。
初始化数据库
在之前的环境中,是对内存数据库进行初始化的。现在同样也要对这部分进行环境的区分,使得在生产环境下,可以初始化SQL Server数据库。
传入环境变量
public static void MockPopulation(IApplicationBuilder app, bool isProduction)
{
using (var serviceScope = app.ApplicationServices.CreateScope())
{
SeedData(serviceScope.ServiceProvider.GetService<ApplicationDbContext>(), isProduction);
}
}
private static void SeedData(ApplicationDbContext context, bool isProduction)
{ }
外部调用代码为
MockInMemoryDatabase.MockPopulation(app, _env.IsProduction());
然后在SeedData中,针对环境进行初始化区分
if (isProduction)
{
Console.WriteLine(&#34;>>>Attempting to apply migration...&#34;);
try
{
context.Database.Migrate();
}
catch (Exception ex)
{
Console.WriteLine($&#34;>>>Cannot run migration: {ex.Message}&#34;);
}
}
这里无须担心,每次初始化平台服务时,都会进行重复进行migration。这是因为migration时会用现有的migrate脚本名字和数据库(连接字符串对应的数据库)中一张名为__EFMigrationsHistory的表中的migrate脚本名字进行比对,如果数据库中不存在现有的migrate脚本,则将进行migration;反之则不会进行。
重新构建平台服务的镜像,并重新部署
该部分可以参考之前的内容[5]
测试平台服务与SQL Server的连接
查看初始化数据

然后在通过创建接口,插入一条新数据

查看全部数据,发现一共有4条数据读取到(中间为了演示,去除了两条数据、使得插入的数据自增长主键跳过4、5,直接为6)

参考
- ^Youtube视频链接 https://www.youtube.com/watch?v=DgVjEo3OGBI&list=PLpSmZmoBaROZm0ucoQchgBJJ_SyTZWbC0&index=3&t=12301s
- ^权限配置 https://www.cnblogs.com/pari-Zhong/p/5339028.html
- ^EFCore模块注入 https://www.cnblogs.com/lbonet/p/9143686.html
- ^创建平台服务 https://zhuanlan.zhihu.com/p/507220056
- ^重新部署平台服务 https://zhuanlan.zhihu.com/p/508808719
|
|