服务部署之k8s

 
微服务个人觉得是个非常复杂又庞大的体系,比如要有完备的监控平台、分布式日志收集系统、权限控制、服务治理,各服务应该高度自治、服务注册于发现、节点动态扩缩容、熔断降级,限流等等。标准的微服务架构涉及到这么多的技术点,如果没有巨人的肩膀依靠,我相信一般的公司都很难实施,好在google这个巨人帮我们实现了,它就是k8s。官网介绍,k8s源自Google15年生产环境的运维经验提供的最佳实践,可见一斑。概念性的东西就不介绍了(主要是我懂的也不多,太庞大),优势就是上面说到的标准微服务架构涉及到的技术点,这哥们本身就基于标准微服务架构实现的。我这里简单说下它和docker的关系。k8s容器集群管理系统,当然docker社区也提供类似的集群管理系统,docker三剑客之一docker-swarm,这个哥们设计有缺陷(大起大落上不了规模),所以后面就不了了之了。说到三剑客我们还接触到一个容器编排docker-compose,三剑客现在还在用的应该就只有它了,集成测试环境的编排系统,它只能单机,同一个docker-compose里面的容器在一个docker网络里面(当然可以通过其他手段实现),它跟docker的关系,个人觉得就是套娃的关系。闲聊就到这。下面看下整个服务部署的实验环境。
 
通信主机所有通信主机都是基于vm虚拟机实现,看图
简单解说一下:
1.所有虚拟主机基于ubuntu-server18.4这个版本,好像最新版是20.4;
2.截图里面有4台虚拟主机,其中org是对安装k8s准备的公共系统环境,比如配置网络、安装k8s工具、docker等;
3.master和node节点都是基于org完整克隆出来的,这样可以减少一定的工作量吧;
4.搭建k8s集群最少需要10个g的内存,这里还不包括master高可用,master2g,node4g,这是常规练手部署,也可以不按套路出牌,master和slave交叉部署,也是可以的;
下面我们通过命令看看node信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get node
2 NAME                STATUS   ROLES    AGE     VERSION
3 kubernetes-master   Ready    master   3d15h   v1.18.2
4 kubernetes-node01   Ready    <none>   3d15h   v1.18.2
5 kubernetes-node02   Ready    <none>   3d15h   v1.18.2
6 root@kubernetes-master:/usr/local/k8s-test01/product#
 
服务部署结构图:
简单介绍下,
1.在最前端的是网关也是代理(ingress-nginx-controller),它是k8s的网关入口实现,除了ingress-nginx还有很多,可以参考 https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/ ,它的高可用可通过HA或者keepalived实现,在这个实验里面我没有去实现,其实配置起来很简单,通过shell脚本检查nginx是否可用,对外提供vip地址,基于vrrp路由冗余协议实现,当然这个网关也是在k8s这个集群里面的,图没有显示层级出来。
2.网关后面就是我们的service服务,我这里部署的是产品服务ProductService,整个service的网络模型我部署成了ClusterIp类型,这是k8s对内访问的三种模型之一。这个模型有个特点外面的服务不能直接访问里面pod之上的容器,所以我们可以把它理解为一个封闭的网络,由ingress-nginx代理,而它们的网络是在k8s这个大型子网里面,通过coredns(k8s里面的组件)组件解析(当然这里面还有CNI的实现者网桥的介入,甚至还有docker网桥)。下面我们直接部署吧。
 
部署
我们着重看ProductService的部署吧,其他的k8s集群环境准备就不赘述了,分两种,adm镜像和二进制,adm比较简单。我简单说下,部署productservice我的一个思路,先是通过工程dockerfile构建镜像,当然这些可以通过自动构建和部署,然后通过deployment资源管理器创建pod资源,最后部署service代理。这里简单画个图理解一下。
这里简单介绍下这里面涉及到的几个资源:
1.在k8s集群里面master主要负责控制和管理node节点,node为工作负载节点,干活就是它,也就是容器的执行是在它里面的pod之上。
2.deployment是资源控制管理器,比如动态扩缩容,滚动更新,回滚等等操作,在k8s里面有多种资源控制器,比如rs、rc、ds、job等,rc已经被rs替代,然而deployment的功能又包含rs,所以我这里用dep。
3.service服务代理,代理谁?pod,通过label标签匹配,为什么需要它?如果没有它的话,pod暴露的是一个个ip,如果中间一个pod挂了,dep为了满足我们的期望值,会重新创建一个pod,这时候出问题了,刚好service就是为了解决这个问题而诞生的,它通过标签匹配到集群里面对应的标签pod,监听它们的状态,然后把pod信息同步到service里面,提供外部服务。service代理模式分三种iptables、ipvs等。
图片解说到这,看代码。
1.构建镜像 productservice

 

 1 FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
 2 WORKDIR /app
 3 EXPOSE 80
 4 EXPOSE 443
 5 
 6 FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
 7 WORKDIR /src
 8 
 9 COPY "EventBus/EventBus/EventBus.csproj" "EventBus/EventBus/EventBus.csproj"
10 COPY "EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
11 COPY "EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
12 COPY "Services/ProductService/Api/Product.Api.csproj" "Services/ProductService/Api/Product.Api.csproj"
13 COPY "Services/ProductService/Application/Product.Application.csproj" "Services/ProductService/Application/Product.Application.csproj"
14 COPY "Services/ProductService/Domain/Product.Domain.csproj" "Services/ProductService/Domain/Product.Domain.csproj"
15 COPY "Services/ProductService/Infrastructure/Product.Infrastructure.csproj" "Services/ProductService/Infrastructure/Product.Infrastructure.csproj"
16 
17 COPY "NuGet.config" "NuGet.config"
18 WORKDIR /src/Services/ProductService/Api
19 RUN dotnet restore "Product.Api.csproj"
20 WORKDIR /src
21 COPY . .
22 WORKDIR /src/Services/ProductService/Api
23 RUN dotnet publish --no-restore -c Release -o /app
24 
25 FROM build AS publish
26 
27 FROM base AS final
28 WORKDIR /app
29 COPY --from=publish /app .
30 COPY --from=build /src/Services/ProductService/Proto /app/Proto
31 
32 ENTRYPOINT ["dotnet", "Product.Api.dll"]

 

先通过dockerfire构建一份镜像,记得打标签。我这里把编译也做了,当然你也可以先在vs里面发布好,然后再打包镜像,会快一些,docker镜像下载很蛋疼,一般有三种处理方式吧。1.配置加速地址 2.搭建镜像私服 3.docker save和load命令。
2.通过deployment创建pod资源,今天这篇文章我不打算用helm包管理器来做,这样里面的细节会多一些,后续下次k8s部署会全部基于helm来做,看代码
 1 apiVersion: apps/v1
 2 kind: Deployment # 资源类型
 3 metadata:
 4   name: product-server # 名称
 5 spec:
 6   replicas: 2 # pod实例数
 7   selector:
 8     matchLabels:
 9       app: product-server 标签
10   template: # 模板
11     metadata:
12       labels:
13         app: product-server 标签 上下两个标签需要对应,表示template里面创建的这个pod属于这个dep资源里面的
14     spec: #详细信息
15       containers:  # 容器
16       - name: product-server
17         image: myproduct-test-01:2.0 # image镜像
18         imagePullPolicy: IfNotPresent # image拉取策略,如果本地有就不拉取,这里注意啊,这个本地不光是master节点,node节点也都需要有这个镜像
19         ports:
20         - containerPort: 80 # 容器端口
21           name: site
22           protocol: TCP
23         - containerPort: 81
24           name: grpc
25           protocol: TCP
26  
27         env: # 环境变量,也可以通过configmap实现,其实就是配置文件
28         - name: ASPNETCORE_ENVIRONMENT
29           value: "Development"
30         - name: ConnectionString
31           value: "Server=mssql;Database=product_db;User Id=sa;Password=Pass@word"

 

执行命令 kubectl create -f 文件名称.yaml,接着我们通过查看命令,看看deployment信息, 在实际操作的时候,可能会因为pull镜像导致状态有问题,我一般是先把镜像处理好,再在本地拉取镜像。这里需要注意一点小知识点,pod的生命周期和探针,探针分两种,就绪和存活探测两种,这里我为什么要提这个呢?因为在微服务里面,个人觉得健康检查很重要。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get deployment
2 NAME             READY   UP-TO-DATE   AVAILABLE   AGE
3 mssql            1/1     1            1           27h
4 product-server   2/2     2            2           6h13m
5 rabmq            2/2     2            2           13h
6 root@kubernetes-master:/usr/local/k8s-test01/product#

 

3.部署service代理
 1 apiVersion: v1
 2 kind: Service # 资源类型
 3 metadata:
 4   name: product-server # 名称,这个名称类似dockercompose里面定义的服务名
 5 spec:
 6   ports:
 7   - name: psvc
 8     port: 80 # 服务端口,提供给集群内部访问的端口,外部访问不了
 9     targetPort: 80 #  容器端口
10   - name: grpc
11     port: 81
12     targetPort: 81
13   selector:
14     app: product-server  # 标签,与之对应的是deployment里面的pod标签,它们是多对多的关系
15   type: ClusterIP # 内部网络

 

执行命令 kubectl create -f 文件名称.yaml,接下来我们可以查看service信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get service
2 NAME             TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                          AGE
3 kubernetes       ClusterIP      10.96.0.1       <none>        443/TCP                          3d15h
4 mssql            LoadBalancer   10.104.37.195   <pending>     1433:31227/TCP                   26h
5 product-server   ClusterIP      10.107.6.57     <none>        80/TCP,81/TCP                    6h10m
6 rabmq            NodePort       10.102.48.159   <none>        15672:31567/TCP,5672:30567/TCP   13h
7 root@kubernetes-master:/usr/local/k8s-test01/product#
 
deployment和service配置文件可以放在一个yaml文件里面,通过---分开就行,这里分开是为了看起来有层次。
好了,productservice部署完毕了,我们的服务是部署完毕了,但是我们在外部访问不了,需要入口网关,这里我们先使用ingress-nginx-controller。
部署ingress
1.安装ingress-nginx,这里我用的是最新版0.30.0,很是郁闷,下载地址被墙了,最后在github开源代码里面找到安装资源,其实就是一份yaml文件。
 1 # 其他...
 2 apiVersion: apps/v1
 3 kind: Deployment
 4 metadata:
 5 name: nginx-ingress-controller
 6 namespace: ingress-nginx
 7 labels:
 8 app.kubernetes.io/name: ingress-nginx
 9 app.kubernetes.io/part-of: ingress-nginx
10 spec:
11 replicas: 1
12 selector:
13 matchLabels:
14 app.kubernetes.io/name: ingress-nginx
15 app.kubernetes.io/part-of: ingress-nginx
16 template:
17 metadata:
18 labels:
19 app.kubernetes.io/name: ingress-nginx
20 app.kubernetes.io/part-of: ingress-nginx
21 annotations:
22 prometheus.io/port: "10254"
23 prometheus.io/scrape: "true"
24 spec:
25 # wait up to five minutes for the drain of connections
26 terminationGracePeriodSeconds: 300
27 serviceAccountName: nginx-ingress-serviceaccount
28 nodeSelector:
29 kubernetes.io/os: linux
30 containers:
31 - name: nginx-ingress-controller
32 image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
33 args:
34 - /nginx-ingress-controller
35 - --configmap=$(POD_NAMESPACE)/nginx-configuration
36 - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
37 - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
38 - --publish-service=$(POD_NAMESPACE)/ingress-nginx
39 - --annotations-prefix=nginx.ingress.kubernetes.io
40 # ......

 

安装ingress-nginx ,执行命令 kubectl create -f ingress-nginx.yaml,随后我们通过命令查看。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods -n ingress-nginx
2 NAME                                        READY   STATUS    RESTARTS   AGE
3 nginx-ingress-controller-77db54fc46-tx6pf   1/1     Running   5          40h
4 root@kubernetes-master:/usr/local/k8s-test01/product#

 

接下来配置并发布ingress-nginx网关服务。
 1 apiVersion: networking.k8s.io/v1beta1
 2 kind: Ingress
 3 metadata:
 4   name: nginx-web
 5   annotations: # 扩展信息,这里可以配置鉴权、认证信息
 6     nginx.ingress.kubernetes.io/rewrite-target: /
 7 spec:
 8   # 路由规则
 9   rules:
10   # 主机名,只能是域名,需要在宿主机配置hosts映射
11   - host: product.com
12     http:
13       paths:
14       - path: /
15         backend:
16           # 后台部署的 Service Name,与上面部署的service对应
17           serviceName: product-server
18           # 后台部署的 Service Port,与上面部署的service对应
19           servicePort: 80
20       - path: /grpc
21         backend:
22           # 后台部署的 Service Name,与上面部署的service对应
23           serviceName: product-server
24           # 后台部署的 Service Port,与上面部署的service对应
25           servicePort: 81
 
执行kubectl create -f productservice-ingress.yaml部署。接下来我们查看网关服务信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get ingress
2 NAME        CLASS    HOSTS         ADDRESS   PORTS   AGE
3 nginx-web   <none>   product.com             80      8h
 
mssql和rabbitmq的部署这里就贴代码了,接下来我们访问product.com这个域名,看看是否部署成功。
 
 
我们测试一个get接口试试,看看能不能通,看图。
 
服务部署就到这,接下来我们简单总结一下
1.这是测试环境所以master没做高可用,ingress也没做高可用,有时间再做顺便补充一下;
2.外部网络请求到ingress-nginx域名,线上环境这个域名肯定是公网地址,ingress做认证鉴权,合法的请求通过path路由到对应的后台service,如果其中一台ingress挂掉了,keepalived会把vip游离到其他slave节点,这样就完成了ingress的高可用;
3.service代理会把请求随机转发到标签匹配的pod里面的容器处理,如果其中一台node挂了或者pod异常退出了(也就是返回值不等于0),deployment会重新启动一个pod,我们下面做个实验,删掉其中一个pod,看看效果怎么样。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods
2 NAME                              READY   STATUS    RESTARTS   AGE
3 mssql-59bd4dc6df-xzxc2            1/1     Running   5          27h
4 product-server-599cfd85cc-2q7zx   1/1     Running   0          6s
5 product-server-599cfd85cc-ppmhx   1/1     Running   0          6h51m
6 rabmq-7c9748f876-9msjg            1/1     Running   0          14h
7 rabmq-7c9748f876-lggh6            1/1     Running   0          14h
 
我们先通过命令显示所有default命名空间下面的所有pod,然后delete一个pod看看,它会不会重新启动。执行删除命令
1 kubectl delete pods product-server-599cfd85cc-ppmhx

 

接着马上查看pods信息,要快知道吗?
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods
2 NAME                              READY   STATUS        RESTARTS   AGE
3 mssql-59bd4dc6df-xzxc2            1/1     Running       5          27h
4 product-server-599cfd85cc-2q7zx   1/1     Running       0          2m8s
5 product-server-599cfd85cc-9s497   1/1     Running       0          13s
6 product-server-599cfd85cc-ppmhx   0/1     Terminating   0          6h53m
7 rabmq-7c9748f876-9msjg            1/1     Running       0          14h
8 rabmq-7c9748f876-lggh6            1/1     Running       0          14h
 
看到没?ppmhx这个pod正在终止,马上就创建了新的pod。牛逼吧,强大吧。这只是它的冰山一角,我们还可以通过HPA实现动态扩缩容,比如cpu达到多少负载,水平扩展pod,或者删除pod。
 
最后提一下,碰到坑如何处理?
我一般是先通过命令查看大概信息
 1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl describe pods product-server-599cfd85cc-2q7zx
 2 Name:         product-server-599cfd85cc-2q7zx
 3 Namespace:    default
 4 Priority:     0
 5 Node:         kubernetes-node01/192.168.44.210
 6 Start Time:   Fri, 08 May 2020 00:31:31 +0800
 7 Labels:       app=product-server
 8               pod-template-hash=599cfd85cc
 9 Annotations:  cni.projectcalico.org/podIP: 10.244.185.145/32
10               cni.projectcalico.org/podIPs: 10.244.185.145/32
11 Status:       Running
12 IP:           10.244.185.145
13 IPs:
14   IP:           10.244.185.145
15 Controlled By:  ReplicaSet/product-server-599cfd85cc
16 Containers:
17   product-server:
18     Container ID:   docker://bfc6ab23a5e228b85960322d3ea57321ed4c51cd4ad413a3db1a8452e4f52bea
19     Image:          myproduct-test-01:2.0
20     Image ID:       docker://sha256:eab1c9f80b5f64adb368f1e3d3f2955597f90c0badd2ba81fc714a6774a0e473
21     Ports:          80/TCP, 81/TCP
22     Host Ports:     0/TCP, 0/TCP
23     State:          Running
24       Started:      Fri, 08 May 2020 00:31:33 +0800
25     Ready:          True
26     Restart Count:  0
27 其他 ....

 

这里面描述了很多信息,如果遇到错误,也会有大概的信息提示,如果通过这里的信息提示不能排错,我一般就会去看容器的日志信息,通过命令kubectl logs pod名称,这个就不演示。
最后简单总结一下吧,关于k8s站在我们应用的角度来说,不是太难,但是非运维人员要把它玩好,我觉得有点勉强,不是我们接受能力差,还是我们开发人员没时间玩它。太庞大,知识面太多,比如你要完k8s你先的熟悉一种容器化技术吧,网络,等等,还有坑太多,还可能要FQ啥的,最让我吐槽的是各种镜像下载超时,有时候被墙了,还要各种想办法拿资源,这里哪位朋友方便介绍给我一个FQ软件,最好是收费的。谢谢,就这样把。下次把监控、ELK、PVC等补上。
 
 
 
posted @ 2020-05-08 01:17  小菜 。  阅读(10958)  评论(1编辑  收藏  举报