前言
鸽了一个多月,更文虽迟但到
近两个月的打工人时光,让人变秃了,也变懒了(试图解释
以及忙着忙着,就模糊了工作和个人生活的界限,不似996,胜似996,有点心累
希望接下来能好好调整
话不多说,进入正题
容器编排
容器本身没有价值,有价值的是“容器编排”
容器编排简介
容器编排,就是对容器的管理,包括:
- 应用部署:将应用装进容器,部署到某一物理节点上
- 水平扩展:可动态调整容器副本数量,业务请求压力大的时候,就可以多开几个应用副本来处理
- 服务发现:一个应用对应一种服务,当你把应用跑起来后,容器编排工具就能发现这一服务,这样其他服务就可以调用当前服务了
- 负载均衡:容器编排工具能将请求调度到一个服务的不同容器副本上,从而降低各容器的压力
- 故障自愈:物理节点宕机后,其上的容器会重新部署到另一个节点
- 滚动更新:对一个服务对应的全部应用进行批量更新
- 版本回退:可以回退应用版本
目前比较主流的容器编排工具之一就是k8s
k8s简介
k8s
,全称为Kubernetes
,K8s是谷歌和redhat主导开发的容器编排工具。
Kubernetes
这个单词源于希腊语,译为“舵手”
下图是个形象的比喻,将k8s类比成舵手,统筹调度着船上的一个个集装箱(容器
):
K8s架构
简单来说就是:master节点管理node节点,node节点上跑着容器
用户通过UI界面或者CLI(命令行)与master节点交互
而不同节点上跑着不同的k8s组件
K8s组件
Master节点上的组件:
- API server:顾名思义,包括了k8s所有的api,是集群的统一入口。Kubernetes 中所有的组件都会和 API Server 进行连接,组件与组件之间一般不进行独立的连接,都依赖于 API Server 进行消息的传送;
- scheduler:调度器,当你用k8s跑一个应用时,它会根据应用对 CPU、memory 的请求大小,找一台合适的节点,进行放置
- etcd:存储系统,保存集群的数据。比如容器部署到了哪些节点上、容器的信息等等
- controller:控制器,可以用来控制应用的副本数量,实现了服务编排里说的故障自愈和水平伸缩的功能
Node节点上的组件:
- kubelet:用来管理Node节点,可以理解为master节点派到Node节点的代表
- kube-proxy:提供网络代理,提供负载均衡等功能
- docker:运行容器
部署应用的过程分解
- 用户可以通过 UI 或者 CLI 提交一个 Pod(简单说就是一组容器,后边会介绍) 给 Kubernetes 进行部署
- 这个 Pod 请求首先会提交给 Kubernetes API Server
- 下一步 API Server 会把这个信息写入到它的存储系统 etcd
- 之后 Scheduler 会通过 API Server 的 watch 或者叫做 notification 机制得到这个信息:有一个 Pod 需要被调度。
- 这个时候 Scheduler 会根据Pod对硬件的要进行一次调度(选出一个合适的节点),在完成这次调度之后,它会向 API Server 反馈 说:“OK!这个 Pod 需要被调度到xx节点上。”
- 这个时候 API Server 接收到这次操作之后,会把这次的结果再次写到 etcd 中
- 然后 API Server 会通知相应的节点进行这次 Pod 真正的执行启动
- 相应节点的 kubelet 会得到这个通知
- kubelet 就会去调 Container runtime 来真正去启动配置这个容器和这个容器的运行环境,去调度 Storage Plugin 来去配置存储,network Plugin 去配置网络
Pod
Pod,是一组容器。
这一组容器装在一个叫infra的容器里(俄罗斯套娃),共享同一个 Network Namespace,可以共享同一个Volume(可简单理解为一个文件夹)。
为什么pod要设计成为一组容器呢?
其实这个概念是类比进程和进程组的关系。
上一篇文章已经说过,一个容器里边一般只跑一个进程,即应用进程;那么一个pod对应一组容器,自然也就对应一个进程组
Google 公司的工程师们发现,他们部署的应用,往往都存在着类似于“进程和进程组”的关系。更具体地说,就是这些应用之间有着密切的协作关系,使得它们必须部署在同一台机器上。
因此,Pod是 k8s项目中的最小编排单位。
Deployment和Service
在k8s中,我们对一组pod的部署对应着一个Deployment
,可以用一个yaml文件来描述:
通过kubectl apply -f xxx.yaml
启动deployment,从而来保证pod副本数稳定
一组pod跑起来自然是要对外提供服务的,k8s对此也做了抽象,用一个yaml文件描述Service
,eg:
apiVersion: v1
kind: Service
metadata:
name: demo #服务名
namespace: default
labels:
app: demo
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: demo
version: v1
......
这些yaml文件也被称为资源清单文件
,描述的对象被称为资源
,比如Deployment
、Service
,每个资源可以设置对应的label
和selector
通过给selector绑定不同的label,建立deployment和pod、service与deployment的映射关系
蓝绿部署
场景:
版本更新不允许两个版本代码同时运行。
方法:
启动一个新的Deployment,给这个deployment一个新的标签
配置service的标签选择(service原先选择老的deployment的标签,后来改成选择新deployment的标签),将流量导向新的Deployment
之前的service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: demo #服务名
namespace: default
labels:
app: demo
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
# 注意这里我们匹配 app 和 version 标签,当要切换流量的时候,我们更新 version 标签的值,比如:v2
selector:
app: demo
version: v1
v1-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1-deployment
namespace: default
labels:
app: demo
version: v1 #deployment的版本:v1
........
v2-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo1-deployment
namespace: default
labels:
app: demo
version: v2 #deployment的版本:v2
........
通过命令启动service和两个deployment:
kubectl apply -f service.yaml -f v1-deployment.yaml -f v2-deployment.yaml
动态修改service的标签选择器,实现流量从v1版本应用切到v2版本应用
kubectl patch service demo -p '{"spec":{"selector":{"version":"v2"}}}'