跳到正文Seminar Week 4 Summary · Jimmy Huang's Blog类比:足球(Soccer)
可以把编排器理解成球队里的 教练 / manager:
球员不会自己随便站位;
教练会统一安排位置、配合和整体战术;
同样,编排器也不是只负责“启动容器”,而是负责:把容器放到合适的节点;
让服务之间可以通信;
失败时自动补位;
负载高时做扩容。
什么是容器编排工具
容器编排技术提供了一个在 大规模环境 下集成和管理容器的框架。
例子
常见功能
可扩展性对比
Docker Swarm
Docker Swarm 是什么
Swarm 由一个或多个 Docker 节点组成,例如:
物理服务器
虚拟机(VMs)
Raspberry Pi
云实例(cloud instances)
Docker 主机的两种模式
Docker Swarm Service
在 Swarm 中,应用编排时最基本的调度单位是 service,不是单个裸容器。
Service 是什么
一个比容器更高层的对象;
它把一些高级特性包装在容器外面。
非 Leader Manager 如何处理命令
Docker client 先把命令发给某个 manager;
如果这个 manager 不是 leader,它会把命令 proxy(代理转发) 给 leader;
leader 再真正决定把任务交给哪个 worker 执行。
你不一定非要直接连到 leader 才能下命令;
但真正做调度决策的,通常还是 leader。
Raft 一致性算法(Raft consensus algorithm)
Swarm 的 managers 原生支持 高可用(HA, High Availability)。
Swarm 用 Raft 一致性算法 来实现 manager 层面的高可用。
什么是一致性算法
让一组机器即使有一部分出故障,也仍然能对“系统当前真实状态”达成一致。
把一个状态机的状态分布到多个节点;
并保证整个集群对同一系列状态变化保持一致。
Raft 可视化资源
服务器状态:1 个 Leader,N-1 个 Followers
节点启动后先是 follower;
一段时间没收到 leader 心跳,就会开始选举,变成 candidate;
candidate 拿到多数票后,成为 leader;
如果发现别人的任期(term)更高,就会退回 follower。
Terms(任期)
从一次选举开始;
如果选举成功,会有一个 leader 管理这个 term;
有些选举会失败,这个 term 可能没有 leader;
不同服务器观察到 term 切换的时间,可能并不完全一致。
Leader 选举流程
基本步骤
服务器启动时先作为 followers
如果 follower 在一段时间内没有收到通信(election timeout),它会认为 leader 可能挂了,于是开始选举
接下来会发生什么
最先超时的 follower 开始发起新的选举
它把当前 term 加 1
切换成 candidate
先给自己投票
再向其他服务器发送 RequestVote RPC
a) 拿到多数票
成为 leader
向其他服务器发送 AppendEntry 心跳
b) 收到合法 leader 的 RPC
c) 没有人赢得选举
一开始所有节点都是 follower;
某个节点先超时,变成 candidate,并请求投票;
另一个节点也可能几乎同时超时,于是出现竞争;
如果票被分散,就会出现 split vote(票数分裂);
这时没有 leader 产生,系统会进入新的 term;
再次发起选举后,某个节点最终拿到多数票,成为新的 leader;
其他节点退回 follower。
Docker Swarm 常用命令
创建与加入 Swarm
创建一个新的 swarm;
执行该命令的节点会成为第一个 manager;
同时切换到 swarm mode。
查看节点
查看 swarm 中的所有节点;
包括哪些是 managers;
哪个是当前 leader。
创建与查看 Service
docker service create:创建新 service
docker service ls:列出当前运行中的 services
docker service ps <service>:查看某个 service 下各个副本 / task 的详细情况
docker service inspect --pretty <service>:查看 service 的详细信息
扩缩容、更新、日志、删除
scale:调整副本数
update:更新 service 的属性
logs:查看 service 日志
Docker Networking
Docker 在容器里运行应用,而这些应用往往要跨很多不同网络通信;
所以 Docker 必须具备比较强的网络能力;
在 swarm 中,各节点不一定在同一个 LAN;
Docker 使用 VXLAN 提供虚拟 overlay network(覆盖网络)。
你可以把 overlay network 理解成:
容器与持久化数据
在容器里保存数据,推荐方式是使用 volumes(卷)。
基本思路
创建一个 volume
创建一个 container
把 volume 挂载到容器里的某个目录
以后写到这个目录里的数据,实际上就会写进 volume
Volume 相关命令
docker volume create创建新 volume
默认使用本地驱动(local driver)
也可以用 d 指定其他驱动
docker volume inspect查看 volume 的详细信息
包括它在宿主机文件系统中的位置
Kubernetes
Kubernetes 是什么
Kubernetes 这个词来自希腊语,可以理解成:
governor(治理者)
helmsman of a ship(掌舵者)
captain(船长)
一个 开源容器编排系统
最初由 Google 设计
现在由 CNCF 维护
deployment(部署)
scaling(扩缩容)
operations(运维操作)
一个用来运行应用的 cluster(集群)
一个用来管理云原生微服务应用的 orchestrator(编排器)
Kubernetes 的基本思路
在应用世界里,这种统一安排、统一管理的事情,就叫 orchestration(编排)。
先把应用打包好;
再把它交给 Kubernetes 集群;
然后由集群来决定如何运行它。
masters / control plane
workers / nodes
Masters(控制平面)
Kubernetes 的 master 其实是一组系统服务,它们一起构成了集群的 control plane(控制平面)。
主要组件
API Server
集群里几乎所有组件之间的通信都要经过它;
它本质上是一个 RESTful 接口;
默认端口是 443。
Cluster Store
Controller Manager
Scheduler
监听新的工作任务;
并把任务分配给合适且健康的节点。
Nodes(工作节点 / 数据平面)
Nodes 是 Kubernetes 集群里的 workers(工作节点)。
监听 API Server,查看是否有新的工作任务;
执行这些任务;
把结果和状态回报给 control plane。
节点上的关键组件
Kubelet
负责节点注册;
监听 API Server 的工作分配;
并向 master 报告状态。
Container Runtime
Kube-proxy
应用如何运行在 Kubernetes 集群中
一个应用想在 Kubernetes 上运行,通常需要满足几个条件:
被打包成 container
被包裹在 Pod 中
通过 declarative manifest file(声明式清单文件) 部署
一般步骤
把应用写成多个小而独立的服务
每个服务各自打包成一个容器
定义一个 Kubernetes Pod 来运行容器化服务 再通过更高层的控制器来部署 Pods,例如:Deployments
DaemonSets
StatefulSets
CronJobs
把这份“期望状态”提交给集群
剩下的由 Kubernetes 负责实现
Pod
Pod 是什么
Pod 是 Kubernetes 里的 原子级调度单位(atomic unit of scheduling)。
为什么叫 Pod
Docker 的 logo 是鲸鱼;
一群鲸鱼可以叫 pod;
所以一组需要一起工作的容器,就被叫做 Pod。
Pod 的特点
Pod 适合承载一组必须始终待在一起的、紧密协作的容器。
它是一种 all-or-nothing(要么一起成功,要么一起失败) 的思路。
多容器 Pod 的例子
Pod(补充)
同一个 Pod 里的多个容器会共享相同环境,例如:
shared memory(共享内存)
volumes(共享卷)
network stack(共享网络栈)
Pod 是扩缩容的基本单位
创建多个 Pod;
每个 Pod 里各自运行一个 nginx 容器。
扩缩容的基本单位是 Pod
不是单个 container
Pod 的原子性(atomic operations)
一个 Pod 要么被完整部署;
要么根本没有部署成功。
一个 Pod 只能被调度到 一台 node 上;
不能把同一个 Pod 拆到多台机器上运行。
Pod 生命周期(Pod lifecycle)
虽然新的 Pod 看起来很像旧的 Pod,但它其实是:
一个全新的 Pod
有新的 ID
也有新的 IP 地址
Deployment
Deployment 是一个比 Pod 更高层的 Kubernetes 对象。
Deployment 文件里通常会定义什么
使用哪个 image
部署多少个 replicas
使用哪些 labels 和 selector
两种使用方式
1. Declarative(声明式)
2. Imperative(命令式)
一个 Deployment YAML 示例
声明式模型与期望状态(desired state)
你告诉它“我想要什么状态”
它会不断检查“现在是不是这个状态”
如果不是,它就自动把系统拉回去
这就是 watch loop(观察循环) 的意义。
课件里的两个例子
例子 1:副本数不够
例子 2:版本升级
Desired State:希望镜像版本从 v2.00 更新到 v2.01
Current State:现在还在用 v2.00
Kubernetes 会做什么:把所有副本更新到新版本
Service
为什么需要 Service
Pod 会死;
Pod 会被替换;
替换后的 Pod IP 会变;
扩缩容时,Pod 的数量也会变化。
Service 的例子
然后它把请求负载均衡到后面的多个 renderer Pods 上。
Service 的前端与后端
前端(front-end)
后端(back-end)
Service 的后端是一组动态变化的 Pods:
Pods 可以增加
Pods 可以减少
Pods 可以被替换
但 Service 会继续帮你把流量转发到正确的 Pods 上。
Service 的本质
Service 提供的是一个稳定的网络抽象点,它能:
在 TCP / UDP 层面做负载均衡;
面对一组动态变化的 Pods 仍然保持稳定入口。
Service 工作在 TCP / UDP 层
它没有应用层智能
所以它不能提供应用层路由
Pod 如何连接到 Service
Service 不是随便挑 Pod 来转发流量的。
工作方式
那么只有同时满足这些标签的 Pods 才会被选中。
那它就不会被这个 Service 选中,因为 zone 不匹配。
Summary
Summary
前几周你学的是“怎么把应用装进容器”,这周开始学的是:
当容器变多、机器变多之后,系统怎么自动安排它们、维护它们、替换它们、扩展它们,并且让它们始终能稳定地对外提供服务。
Pod x Deployment x Service
Case 1
今天安排几个柜员上班
某个柜员请假了补人
新系统上线时安排大家逐步换新流程
Case 2
它负责确保“应该有几个 Pod、版本是什么、挂了怎么补”。
Kubernetes使用流程
1. 写应用
比如 Flask / Node.js / Java 服务
2. 容器化
3. 准备 Kubernetes 环境
4. 写 Deployment
告诉 Kubernetes 跑几个 Pod,用什么镜像
5. 写 Service
6. apply 配置
7. Kubernetes 自动完成运行和维护
docker swarm join-token worker
docker swarm join-token manager
docker service create
docker service ls
docker service ps <service>
docker service inspect --pretty <service>
docker service scale <service>=<replicas>
docker service update <service>
docker service logs <service>
docker service rm <service>
docker volume create myvol
docker volume ls
docker container run -dit --name voltainer \
--mount source=myvol,target=/vol \
alpine
docker volume create
docker volume ls
docker volume inspect
docker volume prune
docker volume rm
kubectl apply -f deployment.yaml
kubectl run nginx --image=nginx:1.15.8 --replicas=2 --port=80
apiVersion: apps/v1 # 使用的 Kubernetes API 版本;Deployment 属于 apps/v1 这一组
kind: Deployment # 对象类型,这里表示要创建的是一个 Deployment
metadata:
labels:
run: nginx # 给这个 Deployment 打标签;方便分类、筛选和管理
name: nginx # 这个 Deployment 的名字
spec:
replicas: 2 # 希望保持 2 个 Pod 副本同时运行
selector:
matchLabels:
run: nginx # Deployment 用这个标签选择它要管理的 Pod
# 也就是说,带有 run: nginx 标签的 Pod 会被它认作“自己管的”
template: # Pod 模板:Deployment 以后创建出来的 Pod,都按这个模板生成
metadata:
labels:
run: nginx # 给新创建的 Pod 打标签
# 注意:这里的标签要和上面 selector.matchLabels 对上,
# 不然 Deployment 找不到自己创建的 Pod
spec:
containers: # Pod 里要运行的容器列表;这里定义了 1 个容器
- image: nginx:1.15.8 # 容器使用的镜像:nginx,版本 1.15.8
name: nginx # 容器名字
ports:
- containerPort: 80 # 容器内部监听的端口是 80
# 表示 nginx 在容器里对外提供 Web 服务的端口
用户/前端
|
v
Service(统一入口)
|
v
Pod A Pod B Pod C
\ | /
\ | /
Deployment(负责维持这些 Pod)