本篇日记记录 Mixficsol 学习 Kubernetes 过程
名词
- PaaS
- Saas
- Laas
- AWS
- OpenStack
- 云计算
- 开源基础设施
- 服务器端技术
- Docker Daemon
- Swarm
- 大数据离线数据业务
- Hadoop,Spark
- 容器运行时
- 有状态无状态
- CRI
- Hypervisor
- 文件系统
- /proc 文件系统
- Mount Namespace
- 网卡
- grpc
- protobuf
- CNI,CRI,CSI
- Deployment
- NFS
- ENTRYPOINT
- ElasticSearch
- 微服务
- 灰度发布
- 热插拔
- GC
- 交叉编译
- 混部
-
问题
- 为什么容器里只能跑”一个进程”?
- 为什么 Kubernetes 就不能固定 IP 地址?
- 容器网络连不通怎么去 Debug ?
- Kubernetes 中 StatefulSet 和 Operator 有什么区别?
- PV 和 PVC 这些概念怎么用?
- Docker 项目默认会为容器启用哪些 Namespace ?
- 如何修复容器中的 top 指令以及 /proc 文件系统中的信息?
- 什么是操作系统的文件系统?
- rootfs文件系统(容器镜像)只包含了操作系统所含的文件、配置和目录,并未包含操作系统内核,容器镜像里面并不会包含操作系统内核?
- 容器里进程新建的文件,怎么才能让宿主机获取到?
- 宿主机上的文件和目录,怎么才能让容器里面的进程访问到?
- 在实际使用 Kubernetes 的过程中,相比于编写一个单独的 Pod 的 YAML 文件,我一定会推荐你使用一个 replicas=1 的 Deployment。请问,这两者有什么区别呢?
- 为什么 Inofrmer 和编写的控制循环之间,一定要使用一个工作队列进行协作?
命令
- docker build “我的镜像”
- docker run “我的镜像”
- docker run -d -v /test helloworld
- kubectl create -f nginx-deployment.yaml
- kubectl describe pod test-liveness-exec
- kubectl get pod website -o yaml
- kubectl scale deployment nginx-deployment –replicas=4
- kubectl get deployments
- kubectl get rs
- kubectl rollout status
- kubectl set image deployment/nginx-deployment nginx=nginx:1.91
- kubectl rollout undo deployment/nginx-deployment
- kubectl rollout history deployment/nginx-deployment
- kubectl rollout pause deployment/nginx-deployment
- kubectl get service nginx
- kubectl get statefulset web
- kubectl exec web-0 – sh -c ‘hostname’
- kubectl get pod -w -l app=nginx
- nslookup web-0.nginx
- kubectl get pvc -l app=nginx
总结
容器
Docker 项目解决了应用打包和发布这一困扰运维人员多年的技术难题
容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个”边界”
Cgroups 技术是用来制造约束的主要手段,而 Namespace 技术则是用来修改进程视图的主要方法
Docker 在创建容器进程时,指定了这个进程所需要启用的一组 Namespace 参数。这样,容器就只能“看”到当前 Namespace 所限定的资源、文件、设备、状态,或者配置。而对于宿主机以及其他不相关的程序,它就完全看不到了
Linux Cgroups 是 Linux 内核中用来为进制设置资源限制的一个重要功能,限制一个进制组能够使用的资源上限,CPU,内存,磁盘,网络带宽,对进程进行优先级设置,审计,以及将进程挂起和恢复等操作
一个正在运行的 Docker 容器,其实就是一个启动了多个 Linux Namespace 的应用进程,而这个进程能够使用的资源量,则受 Cgroups 配置的限制
Mount Namespace 修改的,是容器进程对文件系统“挂载点”的认知。但是,这也就意味着,只有在“挂载”这个操作发生之后,进程的视图才会被改变。而在此之前,新创建的容器会直接继承宿主机的各个挂载点
挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)
容器的 rootfs 由下图所示的三部分组成:
通过结合使用 Mount Namespace 和 rootfs,容器就能够为进程构建出一个完善的文件系统隔离环境。当然,这个功能的实现还必须感谢 chroot 和 pivot_root 这两个系统调用切换进程根目录的能力
Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作
容器云
浅谈 Kubernet
Kubernet 项目的架构,跟它的原型项目 Borg 非常类似,都由 Master 和 Node 两种节点组成,而这两种角色分别对应着控制节点和计算节点,其中控制节点,即 Master 节点,由三个紧密协作的独立组件组合而成,它们分别是负责 API 服务的 kube-apiserver、负责调度的 kube-scheduler,以及负责容器编排的 kube-controller-manager。整个集群的持久化数据,则由 kube-apiserver 处理后保存在 Etcd 中
在计算节点上最核心的部分,是一个叫作 kubelet 的组件,kubelet 主要负责同容器运行时打交道,这个交互所依赖的,是一个称作 CRI 的远程调用接口,这个接口定义了容器运行时的各个核心操作,通过 OCI 这个容器运行时规范同底层的 Linux 操作系统进行交互,把 CRI 请求翻译成对 Linux 操作系统的调用,kubelet 还通过 gRPC 协议同一个叫做 Device Plugin 的插件进行交互,此外 kubelet 可以调用网络插件和存储插件为容器配置网络和持久化存储,分别是 CNI 和 CSI
Kubernetes 集群搭建与实践
kubeadm 这个部署工具可以快速部署 Kubernetes 集群,但是 kubeadm 目前最欠缺的是,一键部署一个高可用的 Kubernetes 集群,即:Etcd、Master 组件都应该是多节点集群,而不是现在这样的单点。
Deployment,是一个定义多副本应用 (即多个副本 Pod) 的对象,负责在 Poed 定义发生变化时,对每个副本进行滚动更新,Deployment 扮演的正是 Pod 的控制器的角色
一个 Kubernetes 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。前者存放的是这个对象的元数据,后者存放的,则是属于这个对象独有的定义,用来描述它所要表达的功能
Kubernetes 里”最小”的API对象是 Pod. Pod 可以等价为一个应用,所以,Pod 可以由多个紧密协作的容器组成
容器想要使用的数据卷,也就是 Volume, 正是 Pod 的 Spec 字段的一部分。而 Pod 里的每个容器,则需要显式的声明自己要挂载哪个 Volume
容器编排与Kubernetes作业管理
容器的”单进程模型”,并不是指容器里只能运行”一个”进程,而是指容器没有管理多个进程的能力
Pod 只是一个逻辑概念,Kubernetes 真正处理的,还是宿主机操作系统上 Linux 容器的 Namespace 和 Cgroups,而并不存在一个所谓的 Pod 的边界或者隔离环境
Pod,其实是一组共享了某些资源的容器,Pod 里的所有容器,共享的是同一个 Network Namespace, 并且可以声明共享同一个 Volume
在 Kubernetes 中,Pod 的实现需要使用一个中间容器,这个容器叫作 infra 容器,在这个 Pod 中,infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,通过 Join Network Namesapce 的方式,与 infra 容器关联在一起
sidecar 指的就是我们可以在一个 Pod 中,启动一个辅助容器,来完成一些独立于主进程之外的工作
凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义
Kubernetes 支持的 Projected Volume:
- Secret:把 Pod 想要访问的加密数据,存放到 Etcd 中
- ConfigMap:保持不需要加密的,应用所需的配置信息
- Downward API:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息
- ServiceAccountToken
把 Kubernetes 客户端以容器的方式运行在集群里,然后使用 default Service Account 自动授权的方式,被称作“InClusterConfig”,是最推荐的进行 Kubernetes API 编程的授权方式
在 Kubernetes 中,可以为 Pod 里的容器定义一个健康检查”探针”(Probe),这样 kubelet 就会根据这个 Probe 的返回值觉得这个容器的状态,而不是直接以容器镜像是否运行作为依据。这种机制,是生产环境中保证应用健康存活的重要手段
Kubernetes 里的 Pod 恢复机制,也叫 restartPolicy. 它是 Pod 的 Spec 部分的一个标准字段,默认值是 Always,任何时候这个容器发生了异常,它一定会被重新创建
只要 Pod 的 restartPolicy 指定的策略允许重启异常的容器(比如:Always),那么这个 Pod 就会保持 Running 状态,并进行容器重启
对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入 Failed 状态
PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身上,而不会影响任何 Pod 的控制器的定义
Kubernetes “一切皆对象”的设计思想:比如应用是 Pod 对象,应用的配置是 ConfigMap 对象,应用要访问的密码则是 Secret 对象
类似 Deployment 这样的一个控制器,实际上都是由上半部分的控制器定义 (包括期望状态),加上下半部分的被控制对象的模版组成的
跟 Deployment 相似,这些控制循环最后的执行结果,要么就是创建,更新一些 Pod (或者其他的 API 对象,资源),要么就是删除一些已经存在的 Pod (或者其他的 API 对象,资源)
Deployment 实现了 Kubernetes 项目中一个非常重要的功能:Pod 的”水平扩展/收缩”,一个 ReplicaSet 对象,其实就是由副本数目的定义和一个 Pod 模版组成的,它的定义其实是 Deployment 的一个子集,Deployment 控制器实际操纵的,正是这样的 ReplicaSet 对象,而不是 Pod 对象
Deployment 与 ReplicaSet 以及 Pod 的关系类似于下面这样:
将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是 “滚动更新”
Deployment 实际上是一个两层控制器,首先,它通过 ReplicaSet 的个数来描述应用的版本,然后,它再通过 ReplicaSet 的属性 (比如 replicas 的值),来保证 Pod 的副本数量,Deployment 控制 ReplicaSet(版本),ReplicaSet 控制 Pod(副本数)
实例之间有不对等关系,以及实例外部数据有依赖关系的应用,就被称为”有状态应用”
容器技术诞生后,大家很快发现,它用来封装”无状态应用”,尤其是 Web 服务,非常好用。但是,一旦想用容器运行”有状态应用”,其困难程度就会直线上升
Kubernetes 项目很早就在 Deployment 的基础上,扩展出了对 “有状态应用”的初步支持,这个编排功能,就是:StatefulSet
StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态
Service 是 Kubernetes 项目中用来将一组 Pod 暴露给外界访问的一种机制,比如,一个 Deployment 有 3 个 Pod,那么就可以定义一个 Service。然后,用户只要能访问到这个 Service,它就能访问到某个具体的 Pod
Service 是以 Service 的 VIP 方式和以 Service 的 DNS 方式被访问,在 Service DNS 的方式下,有两种处理方法:第一种是 Normal Service,第二种是 Headless Service,区别在于,Headless Service 不需要分配一个 VIP,而是可以直接以 DNS 记录的方式解析出被代理 Pod 的 IP 地址
Kubernetes 将 Pod 的拓扑状态 (比如:哪个节点先启动,哪个节点后启动),按照 Pod 的 “名字 + 编号”的方式固定了下来,还为每一个 Pod 提供了一个固定并且唯一的访问入口,即:这个 Pod 对应的 DNS 记录
对于 “有状态应用”实例的访问,你必须使用 DNS 记录或者 hostname 的方式,而绝不应该直接访问这些 Pod 的 IP 地址
StatefulSet 这个控制器的主要作用之一,就是使用 Pod 模板创建 Pod 的时候,对它们进行编号,并且按照编号顺序逐一完成创建工作。而当 StatefulSet 的“控制循环”发现 Pod 的“实际状态”与“期望状态”不一致,需要新建或者删除 Pod 进行“调谐”的时候,它会严格按照这些 Pod 编号的顺序,逐一完成这些操作,所以,StatefulSet 其实可以认为是对 Deployment 的改良,与此同时,通过 Headless Service 的方式,StatefulSet 为每个 Pod 创建了一个固定并且稳定的 DNS 记录,来作为它的访问入口
Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象,大大降低了用户声明和使用持久化 Volume 的门槛
PVC 其实就是一种特殊的 Volume
StatefulSet 的控制器直接管理的是 Pod,Kubernetes 通过 Headless Service,为这些有编号的 Pod,在 DNS 服务器中生成带有同样编号的 DNS 记录,StatefulSet 为每一个 Pod 分配并创建一个同样编号的 PVC
DaemonSet 的主要作用,是在 Kubernetes 集群里,运行一个 Daemon Pod
我们的 DaemonSet Controller 会在创建 Pod 的时候,自动在这个 Pod 的 API 对象里,加上这样一个 nodeAffinity 定义
通过一个 Toleration(污点),调度器在调度这个 Pod 的时候,就会忽略当前节点上的”污点”,从而成功地将网络插件的 Agent 组件调度到这台机器上启动起来
DaemonSet 只管理 Pod 对象,然后通过 nodeAffinity 和 Toleration 这两个调度器的小功能,保证了每个节点上有且只有一个 Pod
CronJob 是一个 Job 对象的控制器
kube-apiserver 在响应命令式请求(比如,kubectl replace)的时候,一次只能处理一个写请求,否则会有产生冲突的可能。而对于声明式请求(比如,kubectl apply),一次能处理多个写操作,并且具备 Merge 能力
Kubernetes 项目为我们额外提供了一种“热插拔”式的 Admission 机制,它就是 Dynamic Admission Control,也叫作:Initializer
在 Kubernetes 项目中,一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)、Version(API 版本)和 Resource(API 资源类型)三个部分组成的
CRD 的全称是 Custom Resource Definition。顾名思义,它指的就是,允许用户在 Kubernetes 中添加一个跟 Pod、Node 类似的、新的 API 资源类型,即:自定义 API 资源
在 Kubernetes 项目中,负责完成授权(Authorization)工作的机制,就是 RBAC:基于角色的访问控制
Operator 的工作原理,实际上是利用了 Kubernetes 的自定义 API 资源(CRD),来描述我们想要部署的“有状态应用”;然后在自定义控制器里,根据自定义 API 对象的变化,来完成具体的部署和运维工作
自定义控制器的工作流程示意图s
Informer 的第一个职责是同步本地缓存工作,第二就是根据这些事件的类型,触发事先注册号的 ResourceEventHandler
所谓 Informer,其实就是一个带有本地缓存和索引机制的,可以注册 EventHandler 的 client,它是自定义控制器跟 APIServer 进行数据同步的重要组件
在 Kubernetes 项目中,负责完成授权工作的机制,就是 RBAC:基于角色的访问控制
Role,其实就是一组权限规则列表。我们分配这些权限的方式,是通过创建 RoleBinding 对象,将被作用者和权限列表进行绑定
ClusterRole 和 ClusterRoleBinding,则是 Kubernetes 集群级别的 Role 和 RoleBinding,它们的作用范围不受 Namespace 限制
权限的被作用者可以有很多种 (User,Group),在我们平常的使用中,最普遍的用法还是 ServiceAccount
在 Kubernetes 生态中,还有一个相对更加灵活和编程友好的管理”有状态应用”的解决方案:Operator
Kubernetes容器持久化存储
- PV 描述的是持久化存储数据卷,这个 API 对象主要定义的是一个持久化存储在宿主机上的目录,比如一个 NFS(网络文件系统) 的挂载目录,而 PVC 描述的,则是 Pod 所希望使用的持久化存储的属性,比如,Volume 存储的大小,可读写权限
- 容器的 Volume,其实就是将一个宿主机上的目录,跟一个容器里的目录绑定挂载在了一起,而所谓的 “持久化 Volume”,指的就是这个宿主机上的目录,具备 ”持久性“
- 持久化 Volume 的实现,往往依赖于一个远程存储服务,可能是远程文件存储(NFS),远程块存储(公有云提供的远程磁盘)
- 持久化存储的步骤:第一为虚拟机挂载远程磁盘的操作,第二然后将磁盘设备格式化并挂在到 Volume 宿主机目录
- 绝不可以把一个宿主机上的目录当做 PV 使用,因为本地目录的存储行为完全不可控,它所在的磁盘随时都可以被应用写满,甚至造成整个宿主机宕机,而且,不同的本地目录之间也缺乏哪怕最基础的 I/O 隔离机智
- 所以一个 Local Persistent Volume 对应的存储介质,一定是一块额外挂载在宿主机的磁盘
-