云原生技术范畴
- 不可变基础设施 目前实现: 容器镜像
- 云应用编排理论 目前实现: 容器设计模式
avatar
什么是容器?
容器,是一个视图隔离,资源可限制,独立文件系统的进程集合
- 视图隔离 - 如能看到部分进程,独立主机名等等;
- 控制资源使用率 - 如 2G 内存大小,CPU 使用个数等等
什么是镜像?
运行容器所需要的所有文件集合 - 容器镜像 Dockerfile - 描述镜像构建步骤 构建步骤所产生出文件系统的变化- changeset 类似 disk snapshot 提高分发效率,减少磁盘压力
summary
容器 - 和系统其它部分隔离开的进程集合 镜像 - 容器所需要的所有文件集合 - Build once,Run anywhere
容器运行时的生命周期
-
单进程模型
- Init 进程生命周期 = 容器生命周期
- 运行期间可运行 exec 执行运维操作
-
数据持久化
- 独立于容器的生命周期
- 数据卷 - docker volume vs bind
moby 容器引擎架构
-
containerd
- 容器运行时管理引擎,独立于 moby daemon
- containerd-shim 管理容器生命周期,可被 containerd 动态接管
-
容器运行时
- 容器徐计划技术方案
- runC kata gVisor
moby daemon containerd shim shim shim container container container runC runC runC
容器和VM之间的差异
Kubernetes
自动化的容器编排平台
- 部署
- 弹性
- 管理 核心功能
- 服务发现与负载均衡
- 容器自动装箱
- 存储编排
- 自动容器恢复
- 自动发布与回滚
- 配置与密文管理
- 批量执行
- 水平伸缩
Kubernetes 架构
Kubernetes 核心概念与API
- Pod
- 最小的调度以及资源单元
- 由一个或多个容器组成
- 定义容器运行的方式(Command 或者环境变量等)
- 提供给容器共享的运行环境(网络、进程空间)
- Volume
- 声明 Pod 中的容器可访问的文件目录
- 可以被挂载在 Pod 中一个(或者多个) 容器的指定路径下
- 支持多种后端存储的抽象
- 本地存储、分布式存储、云存储。。
- Deployment
- 定义一组 Pod 的副本数目、版本等
- 通过控制器(COntroller) 维持Pod的数目
- 自动恢复失败的 Pod
- 通过控制器以指定的策略控制版本
- 滚动升级、重新生成、回滚等
- Service
- 提供访问一个或多个 Pod 实例的稳定访问地址
- 支持多种访问方式实现
- ClusterIP
- NodePort
- LoadBalancer
- Namespaces
- 一个集群内部的逻辑隔离机制(鉴权,资源额度)
- 每个资源都属于一个Namspace
- 同一个Namspace 中的资源命名
- 不同Namspace 中的资源可命名
API 基础知识
HTTP + JSON/YAML
- kubectl
- UI
- curl
为何需要 Pod
容器的本质
-
一个视图被隔离、资源受限的进程
-
容器里PID = 1 的进程就是应用本身
-
管理虚拟机 = 管理基础设施;管理容器 - 直接管理应用本身 Kubernetes 是云时代的的操作系统,以此类推,容器镜像其实就是: 这个操作系统的软件安装包
-
Kubernetes = 操作系统(例如: Linux)
-
容器= 进程(Linux 线程)
-
Pod = 进程组(Linux 线程组)
进程组
helloworld程序由4个进程组成,这些进程之间共享某些文件
helloworld程序如何用容器跑起来?
解法1: 在一个Docker容器中,启动这4个进程
疑问: 容器 PID=1 的进程就是应用本身,蔽日 main 进程,那么谁来负责管理剩余的3个进程?
容器是单进程模型,除非,应用进程本身具备"进程管理"能力(这意味着: helloworld 程序需要具备 systemd 的能力),或者,容器的 PID=1 进程改成 systemd,这会导致: 管理容器 = 管理 systemd != 直接管理应用本身
Pod = 进程组
Pod 是一个逻辑单元,多个容器的组合,Kubernetes 的原子调度单位。
Google 工程师发现,在 Borg 项目部署的应用,往往都存在着类似于"进程和进程组"的关系,更具体说,就是这些应用之间有着密切的协作关系,使得它们必须部署在同一台机器上并且共享这些信息。
为什么 Pod 必须是原子调度单位?
example: 容器间协作
- App: 业务容器,写日志文件
- LogCollector: 转发日志文件到Elasticsearch中
- 内存要求:
- App: 1G
- LogCollector: 0.5G
- 当前可用内存:
- Node_A: 1.25G
- Node_B: 2G 此时如果 App 先被调度到了Node_A上,会怎么样? Node_A资源不足,App 将无法运行
Task Scheduling 问题
- Mesos: 资源囤积(resource hoarding)
- 所有设置了Affinity 约束的任务都到达时,才开始统一进行调度
- 调度效率损失和死锁
- Google Omega: 乐观调度处理冲突
- 先不管冲突,通过回滚机制在出现冲突之后解决问题
- 复杂
- Kubernetes: Pod
亲密关系 - 调度解决
- 两个应用需要运行在同一个宿主机上
超亲密关系 - Pod 解决
- 会发生直接的文件交换
- 使用 localhost 或者 Socket 文件进行本地通信
- 会发生非常频繁的RPC调用
- 会共享某些 Linux Namespace(比如,一个容器要加入另一个容器额Network Namespace)
Pod 要解决的问题
如何让一个 Pod 里多个容器之间最高效的共享某些资源和数据? 容器之间原本是被 Linux Namespace 和 cgroups 隔离开
共享网络
容器 A 和 B
- 通过 Infra Contanier 方式共享同一个Network Namespace
- 镜像: k8s.gcr.io/pause;汇编语言便携的,永远处于暂停,大小 100-200kb
- 直接使用 localhost 通信
- 看到的网络设备和Infra容器看到的一样
- 一个 Pod 只有一个 ip 地址,也就是这个 Pod 的Network Namespace 对应的 ip 地址
- 所有网络资源,都是一个 Pod 一份,并且被该 Pod 中的所有容器共享
- 整个 Pod 的生命周期和 Infra 容器一一致,而与容器 A 和 B 无关
共享存储
容器设计模式
war 包 + Tomcat 容器化
- 方法-: 把war包和Tomcat打包进一个镜像
- 无论是war包还是tomcat更新都需要重新制作镜像
- 方法二: 镜像只打包 tomcat,使用数据卷(hostpath)从宿主机上将 war 包挂载进 tomcat 容器
- 需要维护一套分布式存储系统
InitContainer
容器设计模式: Sidebar
通过在 Pod 里定义专门容器,来执行主业务容器的辅助工作
- 原本需要ssh进行执行的脚本
- 日志收集
- debug应用
- 应用监控 优势在于,将辅助功能和主业务容器解耦,实现独立发布和能力重用
Sidebar: 应用于日志收集
Sidebar: 代理容器
Sidebar: 适配器容器
应用编排与管理: 核心原理
Kubernetes 资源对象
- Spec: 期望的状态
- Status: 观测到的状态
- Metadata:
- Labels
- Annotations
- OwnerReference
Labels
- 标识型的Key:Value 元数据
- 作用
- 用于筛选资源
- 唯一的组合资源的方法
- 可以使用 Selector 来查询
- 类似于 SQL ‘select * where …’ 例子:
environment: production
release: stable
app.kubernetes.io/version: 5.7.21
failure-domain.beta.kubernetes.io/region: cn-hangzhou
Selector
annotations
Ownereference
控制循环
sensor
controller
控制循环例子 - 扩容
两种 API 设计方法
- 命令式(和孩子交流)
- 吃饭
- 刷牙
- 睡觉
- 唱一首歌
- 新扩一个 pod
- 删除一个 pod
0 - 声明式(和员工交流)
- 市场占有率达到 80%
- 稳定性达到 99.99%
- 做一个身高体重正常的孩子
- 副本数保持在 3 个
命令式和声明式对比
命令式
- 命令未响应怎么办?
- 反复重试
- 需要记录当前的操作 复杂
- 如果多重试了怎么办?
- 巡检做修正 - 额外工作,危险
- 如果多方并发访问怎么办
- 需要加锁,复杂低效
声明式
- 天然地记录了状态
- 幂等操作,可在任意时刻反复操作
- 正常操作即巡检
- 可合并多个变更
控制器模式总结