Kubernetes学习日记

本篇日记记录 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 由下图所示的三部分组成:![截屏2024-01-16 11.36.51](/Users/Mixficsol/Desktop/截屏2024-01-16 11.36.51.png)

  • 通过结合使用 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:

    1. Secret:把 Pod 想要访问的加密数据,存放到 Etcd 中
    2. ConfigMap:保持不需要加密的,应用所需的配置信息
    3. Downward API:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息
    4. 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 对应的存储介质,一定是一块额外挂载在宿主机的磁盘