用餐厅经营来讲解什么是微服务,K8S,消息队列

本文最后更新于:2022年7月16日 晚上

前言

虽然之前就介绍过微服务,但当时自己也是似懂非懂的状态,缺乏实战经验。近期实战后对微服务相关的概念更加深刻,因此写下这篇文章,希望能够帮助到更多正在学习中的人。
本文会提到微服务和k8s的相关概念,包括k8s中的pods,nodes,services等。

概念

单线程

佟掌柜开了一家同福客栈,经营着餐饮和住宿。刚开始,店铺里只有一个店员“吕秀才”,他的月薪是5k。当他接到一个外卖订单时,他立马跑到厨房做菜。此时其他外卖订单也来了,但他却无法处理,一旦超过三分钟没有接单,客户就会取消订单。效率很低,肥肠难受。

多线程

此时,佟掌柜又雇了一个店员“李大嘴”,两个店员每个月需要花费10k。当一个店员做菜时,另一个店员也可以接单并且跑到厨房做菜。但是这家店只能同时处理两份订单,同时处理的订单数量等于员工的数量,效率依旧很低,肥肠难受。

微服务

于是佟掌柜将厨师和前台的岗位分开。厨师李大嘴月薪3k只负责做菜,前台吕秀才月薪2k只负责接单。这样,在相同的成本下,即使有更多的订单进来,前台也可以接单。但这时候问题来了,厨师只有一个,虽然前台将单子接了,但是厨师时按顺序做菜的。就会导致后面客户需要等很长的时间,产生不满。
于是老板又雇了一个厨师郭芙蓉,此时餐厅的人工成本为2个厨师6k,一个前台2k。通过区分岗位为厨师和前台,只需要8k就可以完成之前10k的事。

注册中心

将程序设计成微服务后,就会面临一个问题“如何实现微服务直接的调度?”. 最简单的方法就是,部署好“厨师”微服务后,拿到这个微服务的IP地址(厨师名字),比如:李大嘴。 并且将这个IP地址直接编码在程序里,也就是告诉“前台”,如果要炒菜的话,就喊一声“李大嘴,炒一份番茄炒蛋”。这个方法有一个弊端,李大嘴去了趟西方,取了个洋名,叫作“库克”。但是前台并不知道,依旧在喊着“李大嘴,炒一份番茄炒蛋”,但此时厨师却没有任何回应。
注册中心就是解决这个问题的,常见的有’nacos’等。
当李大嘴上岗的时候,就会在小簿子上记录“厨师-李大嘴”。当郭芙蓉上岗时,也会在小簿子上写“厨师-郭芙蓉”。
当吕秀才接到订单的时候,就会去小簿子上看所有的厨师,然后随便找一个厨师,喊一声“xxx,炒一份番茄炒蛋”。
注册中心同时也有一个心跳机制,每过一段时间喊一声“xxx,你还在不在”。如果回复在的话,则保持记录,如果没有回复的话,就会将这个名字从簿子上划去。

K8S里的Pod是什么

POD是由1-n个容器组成的,为什么说k8s能控制的最小单位是POD,而不是容器?举个例子,厨师是一个容器,锅是一个容器,铲子是一个容器。如果只增加厨师的数量,而不增加锅的铲子的数量,即使雇来了厨师,也无法工作。因此,餐厅将厨师、锅、铲子定义为一个POD,要么同时增加三种容器的数量,要么都不增加。

K8S里的Node是什么

Node就是主机,如果佟掌柜在街对面开了家连锁店,那么可以说有两个Node。厨师可以分配在任意一个分店,吕秀才接到订单后,可以让任意一家店的厨师做菜。

K8S里的Service是什么

Service有点类似上面提到的注册中心。当佟掌柜雇了好多厨师,但是发现不好管理。于是就雇佣了一个厨师长”白展堂”。白展堂不做菜,只负责下命令。当吕秀才接到订单的时候,只需要告诉白展堂,做一份番茄炒蛋。于是白展堂就会找一个厨师做。吕秀才不需要关心到底是哪个厨师做的。

用K8S有什么优势?

一个优势就是可以随时新增、删除厨师Pod的数量。比如同福客栈一个月只有5天是高峰期,需要5个厨师。其他25天都是低峰期,只要2个厨师。如果按照传统的方式,每个月厨师的花费就要5*3k=15k。但是通过K8S,可以在高峰期的时候招日结零时工,一天100元。这样每个月厨师的花费就是2*3k+0.1k*3*5=7.5k。肥肠好用。

消息队列是什么

上面提到,吕秀才收到订单后,会让白展堂安排厨师炒菜。如果这时所有厨师都在忙,白展堂就无法回复给吕秀才。吕秀才这个书呆子,没有得到回复,就会一直问白展堂“安排上了吗,安排上了吗”。此时,吕秀才就无法接其他的单子。
于是佟掌柜买了一本便利贴。吕秀才接单后,就写一张便利贴,然后放旁边就好了。白展堂发现有空闲的厨师,就去看看有没有便利贴还没有处理,有的话就拿一张出来,安排个厨师。

实战

在实战中,往往需要自备多个主机以实现多node,但是通过minikube,可以只在一台主机上就可以模拟多个node。

通过命令初始化minikube

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
minikube start

😄 Darwin 12.4 (arm64) 上的 minikube v1.26.0
✨ 自动选择 docker 驱动。其他选项:parallels, ssh
📌 Using Docker Desktop driver with root privileges
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
🔥 Creating docker container (CPUs=2, Memory=4000MB) ...
🐳 正在 Docker 20.10.17 中准备 Kubernetes v1.24.1…
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

初始化minikube后,可以查看当前的node

1
2
3
4
kubectl get node

NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 93s v1.24.1

通过minikube新建一个node

1
2
3
4
5
6
7
8
9
10
minikube node add

😄 添加节点 m02 至集群 minikube
❗ Cluster was created without any CNI, adding a node to it might cause broken networking.
👍 Starting worker node minikube-m02 in cluster minikube
🚜 Pulling base image ...
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
🐳 正在 Docker 20.10.17 中准备 Kubernetes v1.24.1…
🔎 Verifying Kubernetes components...
🏄 Successfully added m02 to minikube!

新建node后,查看当前node

1
2
3
4
5
kubectl get node

NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 3m48s v1.24.1
minikube-m02 Ready <none> 50s v1.24.1

可以看到,这里多出了一个名为’minikube-m02’的node。

通过docker查看当前容器

1
2
3
4
5
docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a52a7710e0dc gcr.io/k8s-minikube/kicbase:v0.0.32 "/usr/local/bin/entr…" About a minute ago Up About a minute 0.0.0.0:49926->22/tcp, 0.0.0.0:49927->2376/tcp, 0.0.0.0:49929->5000/tcp, 0.0.0.0:49930->8443/tcp, 0.0.0.0:49928->32443/tcp minikube-m02
53ebe4a2e5f2 gcr.io/k8s-minikube/kicbase:v0.0.32 "/usr/local/bin/entr…" 4 minutes ago Up 4 minutes 0.0.0.0:49616->22/tcp, 0.0.0.0:49617->2376/tcp, 0.0.0.0:49614->5000/tcp, 0.0.0.0:49615->8443/tcp, 0.0.0.0:49613->32443/tcp minikube

可以看到,minikube通过创建两个docker容器为我们模拟出了两个nodes。

部署一个K8S应用

需要注意的是,部署的时候需要注意镜像的架构,不然可能会导致创建失败。
本章会部署一个kubia应用,参考(https://zhuanlan.zhihu.com/p/381759916)

先创建一个deployment

1
kubectl create deployment kubia-deploy --image=nuptaxin/kubia --port=8080

查看deployment

1
2
3
4
kubectl get deployments

NAME READY UP-TO-DATE AVAILABLE AGE
kubia-deploy 1/1 1 1 6s

查看Pods

1
2
3
4
kubectl get pods

NAME READY STATUS RESTARTS AGE
kubia-deploy-7c9744674c-dz6rl 1/1 Running 0 9s

创建deployment后默认只开启一个pod

将Pods创建为一个Service

1
2
3
kubectl expose deployment kubia-deploy --type=LoadBalancer --name kubia-deploy-http

service/kubia-deploy-http exposed

查看Services

1
2
3
4
5
kubectl get service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25m
kubia-deploy-http LoadBalancer 10.97.77.113 <pending> 8080:30555/TCP 6s

kubia service映射8080端口到pods的30555端口。因为这里使用minikube的原因,所以external-ip是pending。

查看Pods详细

1
2
3
4
kubectl get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubia-deploy-7c9744674c-dz6rl 1/1 Running 0 84s 172.17.0.2 minikube-m02 <none> <none>

可以看到,这里唯一的一个pod被部署到了minikube-m02这个node上。

增加Pods的数量

1
2
3
kubectl scale deployment/kubia-deploy --replicas=5

deployment.apps/kubia-deploy scaled

再次查看Pods的详细

1
2
3
4
5
6
7
8
kubectl get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubia-deploy-7c9744674c-6vppm 1/1 Running 0 55s 172.17.0.3 minikube-m02 <none> <none>
kubia-deploy-7c9744674c-dbbf2 1/1 Running 0 55s 172.17.0.3 minikube <none> <none>
kubia-deploy-7c9744674c-dz6rl 1/1 Running 0 2m37s 172.17.0.2 minikube-m02 <none> <none>
kubia-deploy-7c9744674c-ls6rt 1/1 Running 0 55s 172.17.0.4 minikube <none> <none>
kubia-deploy-7c9744674c-pbw8n 1/1 Running 0 55s 172.17.0.4 minikube-m02 <none> <none>

可以看到现在有了5个pods,而且被部署到了不同的Node上。

访问K8S应用

因为这里是通过minikube创建的集群,所以无法直接通过service暴露的8080端口访问。需要通过minikube转发一趟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
minikube service kubia-deploy-http

|-----------|-------------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-------------------|-------------|---------------------------|
| default | kubia-deploy-http | 8080 | http://192.168.49.2:30555 |
|-----------|-------------------|-------------|---------------------------|
🏃 Starting tunnel for service kubia-deploy-http.
|-----------|-------------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-------------------|-------------|------------------------|
| default | kubia-deploy-http | | http://127.0.0.1:55421 |
|-----------|-------------------|-------------|------------------------|
🎉 正通过默认浏览器打开服务 default/kubia-deploy-http...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

执行完命令后,可以看到,minikube将55421端口转发到了service的8080上。service的8080转发到了pods的30555上。于是这里就可以通过 http://127.0.0.1:55421 访问service,service接受到请求后会转发到其中的一个pod上进行处理。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!