02-Kubernetes篇
概念篇
Kubernetes概述
什么是Kubernetes
kubernetes,简称 K8s,是用 8 代替 8 个字符“ubernete”而成的缩写。
kubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,目标是让部署容器化的应用简单并且高效。
**Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。**在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。在 Kubernetes 中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。
Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。 由于 Kubernetes 是在容器级别运行,而非在硬件级别,它提供了 PaaS 产品共有的一些普遍适用的功能, 例如部署、扩展、负载均衡,允许用户集成他们的日志记录、监控和警报方案。 但是,Kubernetes 不是单体式(monolithic)系统,那些默认解决方案都是可选、可插拔的。 Kubernetes 为构建开发人员平台提供了基础,但是在重要的地方保留了用户选择权,能有更高的灵活性。
Kubernetes 发展史
传统部署时代
在物理服务器上运行应用程序。 由于无法限制在物理服务器中运行的应用程序资源使用,因此会导致资源分配问题。
例如,如果在同一台物理服务器上运行多个应用程序, 则可能会出现一个应用程序占用大部分资源的情况,而导致其他应用程序的性能下降。
一种解决方案是将每个应用程序都运行在不同的物理服务器上, 但是当某个应用程式资源利用率不高时,剩余资源无法被分配给其他应用程式, 而且维护许多物理服务器的成本很高。
虚拟化部署时代
因此,引入了虚拟化技术。虚拟化技术允许在单个物理服务器的 CPU 上运行多台虚拟机(VM)。 虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性, 因为一个应用程序的信息不能被另一应用程序随意访问。
虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序, 而因此可以具有更高的可扩缩性,以及降低硬件成本等等的好处。 通过虚拟化,可以将一组物理资源呈现为可丢弃的虚拟机集群。
每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。
但是明显的缺点是,虚拟层冗余,导致资源浪费和性能下降。
容器部署时代
容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。 因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。
容器的优势:
① 敏捷性:敏捷应用程序的创建和部署;和使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
② 及时性:持续开发、集成和部署;通过快速简单的回滚(由于镜像的不可变性),支持可靠且频繁的容器镜像的构建和部署。
③ 解耦性:关注开发和运维的分离;在构建、发布时创建应用程序的容器镜像,而不是在部署的时候,从而将应用程序和基础架构分离。
④ 可观测性:不仅可以显示操作系统级别的信息和指标,还可以显示应用程序的运行状况和其它指标信号。
⑤ 跨平台:跨开发、测试和生产的环境一致性;在便捷式的计算机上和在云上相同的运行。
⑥ 可移植:跨云和 Linux 发行版本的可移植性;可以在 Ubuntu、CentOS、RedHat、本地、Google Kubernetes Engine 和其他任何地方运行。
⑦ 简易性:以应用程序为中心的管理;提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
⑧ 大分布式:松散耦合、分布式、弹性、解放的微服务;应用程序被分解成较小的独立部分,并且可以动态的部署和管理 --- 而不是在一台大型单机上整体运行(垂直扩展是有上限的)。
⑨ 隔离性:资源隔离;可预测应用程序性能。
⑩ 高效性:资源利用;高效率和高密度。
为什么使用 Kubernetes
容器是打包和运行应用程序的最佳方式,在生产环境中,需要管理运行应用程序的容器,并且确保这些容器不会停机。如果一个容器发生了故障,则需要手动启动另一个容器,太麻烦了;如果有一个系统能够帮助处理这些行为,是不是会很方便?
Kubernetes 就能解决上面提出的一系列的问题。Kubernetes 提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足提出的扩展要求、故障转移的应用、提供部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary (金丝雀) 部署。
Kubernetes 功能
服务发现和负载均衡:Kubernetes 可以使用 DNS 名称或自己的 IP 地址来暴露容器。 如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
存储编排:Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。
自动部署和回滚:你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态更改为期望状态。 例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。
自动完成装箱计算:你为 Kubernetes 提供许多节点组成的集群,在这个集群上运行容器化的任务。 你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。 Kubernetes 可以将这些容器按实际情况调度到你的节点上,以最佳方式利用你的资源。
自我修复:Kubernetes 将重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端。
密钥与配置管理:Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
Kubernetes 概念
Pod
Pod 是 K8S 中最重要也是最基本的概念,Pod 是最小的部署单元,是一组容器的集合。每个 Pod 都由一个特殊的根容器 Pause 容器,以及一个或多个紧密相关的用户业务容器组成。
Pause 容器作为 Pod 的根容器,以它的状态代表整个容器组的状态。K8S 为每个 Pod 都分配了唯一的 IP 地址,称之为 Pod IP。Pod 里的多个业务容器共享 Pause 容器的IP,共享 Pause 容器挂载的 Volume。
Controller
控制器,通过它来实现对pod的管理,比如启动pod、停止pod、伸缩pod的数量等等。
Service
Service 定义了一个服务的访问入口,通过 Label Selector 与 Pod 副本集群之间“无缝对接”,定义了一组 Pod 的访问策略,防止 Pod 失联。
创建 Service 时,K8S会自动为它分配一个全局唯一的虚拟 IP 地址,即 Cluster IP。服务发现就是通过 Service 的 Name 和 Service 的 ClusterIP 地址做一个 DNS 域名映射来解决的。
Label
标签,附加到某个资源上,用于关联对象、查询和筛选。一个 Label 是一个 key=value 的键值对,key 与 value 由用户自己指定。Label 可以附加到各种资源上,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源上。
我们可以通过给指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等工作。
K8S 通过 Label Selector(标签选择器)来查询和筛选拥有某些 Label 的资源对象。Label Selector 有基于等式( name=label1 )和基于集合( name in (label1, label2) )的两种方式。
NameSpace
命名空间,Namespace 多用于实现多租户的资源隔离。Namespace 通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组。
K8S 集群在启动后,会创建一个名为 default 的 Namespace,如果不特别指明 Namespace,创建的 Pod、RC、Service 都将被创建到 default 下。
当我们给每个租户创建一个 Namespace 来实现多租户的资源隔离时,还可以结合 K8S 的资源配额管理,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等。
ReplicaSet(RC)
ReplicaSet 用来确保预期的 Pod 副本数量,如果有过多的 Pod 副本在运行,系统就会停掉一些 Pod,否则系统就会再自动创建一些 Pod。
我们很少单独使用 ReplicaSet,它主要被 Deployment 这个更高层的资源对象使用,从而形成一整套 Pod 创建、删除、更新的编排机制。
Deployment
Deployment 用于部署无状态应用,Deployment 为 Pod 和 ReplicaSet 提供声明式更新,只需要在Deployment 描述想要的目标状态,Deployment 就会将 Pod 和 ReplicaSet 的实际状态改变到目标状态。
Horizontal Pod Autoscaler(HPA)
HPA 为 Pod 横向自动扩容,也是 K8S 的一种资源对象。HPA 通过追踪分析 RC 的所有目标 Pod 的负载变化情况,来确定是否需要针对性调整目标 Pod 的副本数量。
Kubernetes 集群架构与组件
工作原理
Kubernetes 集群架构以及相关的核心组件如下图所示:一个 Kubernetes 集群一般包含一个 Master 节点和多个 Node 节点,一个节点可以看成是一台物理机或虚拟机。

一个kubernetes集群主要是由控制节点(master)、**工作节点(node)**构成,每个节点上都会安装不同的组件。
kubernetes中的工作机器称为 节点, 会运行容器化应用程序。每个集群至少有一个工作节点。工作节点会托管 Pod ,而 Pod 就是作为应用负载的组件。 控制平面管理集群中的工作节点和 Pod。 在生产环境中,控制平面通常跨多台计算机运行, 一个集群通常运行多个节点,提供容错性和高可用性。
master:集群的控制平面,负责集群的决策 ( 管理 )
ApiServer : 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
Scheduler : 负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
ControllerManager : 负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
Etcd :负责存储集群中各种资源对象的信息,键值数据库,类似于 Redis。
node:集群的数据平面,负责为容器提供运行环境 ( 干活,worker工作节点 )
Kubelet : 负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器。每一个 Node 节点上必须安装的组件,负责交互 master 的 api-server 以及当前机器的应用启停等。
KubeProxy : 负责提供集群内部的服务发现和负载均衡
Docker : 负责节点上容器的各种操作
Pod:Pod 是 K8S 中最重要也是最基本的概念,Pod 是最小的部署单元,是一组容器的集合。每个 Pod 都由一个特殊的根容器 Pause 容器,以及一个或多个紧密相关的用户业务容器组成。
docker run 启动的是一个 container(容器),容器是 Docker 的基本单位,一个应用就是一个容器。
kubectl run 启动的是一个应用称为一个 Pod ,Pod 是 Kubernetes 的基本单位。
Pod 是对容器的再一次封装。
Pod 类似于 Java 日志体系中的 Slf4j ,而 Docker 中的容器类似于 Java 日志体系中的 Logback 等日志实现。
一个容器往往代表不了一个基本应用,如:博客系统(WordPress,PHP + MySQL);但是一个 Pod 可以包含多个 Container,一个 Pod 可以代表一个基本的应用。
组件交互原理

通过Kubernetes 部署是一个 Tomcat 应用,了解组件交互原理。
0:开机默认所有节点的 kubelet 、master 节点的scheduler(调度器)、controller-manager(控制管理器)一直监听 master 的 api-server 发来的事件变化。
1:程序员使用命令行工具: kubectl ; kubectl create deploy tomcat --image=tomcat8(告诉 master 让集群使用 tomcat8 镜像,部署一个 tomcat 应用)。
2:kubectl 命令行内容发给 api-server,api-server 保存此次创建信息到 etcd 。
3:etcd 给 api-server 上报事件,说刚才有人给我里面保存一个信息。(部署Tomcat[deploy])
4:controller-manager 监听到 api-server 的事件,是 (部署Tomcat[deploy])。
5:controller-manager 处理这个 (部署Tomcat[deploy])的事件。controller-manager 会生成 Pod 的部署信息【pod信息】。
6:controller-manager 把 Pod 的信息交给 api-server ,再保存到 etcd 。
7:etcd 上报事件【pod信息】给 api-server 。
8:scheduler 专门监听 【pod信息】 ,拿到 【pod信息】的内容,计算,看哪个节点合适部署这个 Pod【pod 调度过后的信息(node: node-02)】。
9:scheduler 把 【pod 调度过后的信息(node: node-02)】交给 api-server 保存给 etcd 。
10:etcd 上报事件【pod调度过后的信息(node: node-02)】,给 api-server 。
11:其他节点的 kubelet 专门监听 【pod 调度过后的信息(node: node-02)】 事件,集群所有节点 kubelet 从 api-server 就拿到了 【pod调度过后的信息(node: node-02)】 事件。
12:每个节点的 kubelet 判断是否属于自己的事情;node-02 的 kubelet 发现是它的事情。
13:node-02 的 kubelet 启动这个 pod。汇报给 master 当前启动好的所有信息。
基础篇
环境部署
安装方式
Kubernetes 有多种部署方式,目前主流的方式有 kubeadm 、minikube 、二进制包。
① minikube:一个用于快速搭建单节点的 Kubernetes 工具。
② kubeadm:一个用于快速搭建Kubernetes 集群的工具(可以用于生产环境)。
③ 二进制包:从官网上下载每个组件的二进制包,依次去安装(建议生产环境使用)。
平台规划
生产环境 K8S 平台规划
K8S 环境有两种架构方式,单 Master 集群和多 Master 集群,将先搭建起单 Master 集群,再扩展为多 Master 集群。开发、测试环境可以部署单 Master 集群,生产环境为了保证高可用需部署多 Master 集群。
① 单 Master 集群架构
单 Master 集群架构相比于多 Master 集群架构无法保证集群的高可用,因为 master 节点一旦宕机就无法进行集群的管理工作了。单 master 集群主要包含一台 Master 节点,及多个 Node 工作节点、多个 Etcd 数据库节点。
Etcd 是 K8S 集群的数据库,可以安装在任何地方,也可以与 Master 节点在同一台机器上,只要 K8S 能连通 Etcd。

② 多 Master 集群架构
多 Master 集群能保证集群的高可用,相比单 Master 架构,需要一个额外的负载均衡器来负载多个 Master 节点,Node 节点从连接 Master 改成连接 LB 负载均衡器。

③ 集群规划
为了测试,我在本地使用 VMware 创建了几个虚拟机(可通过克隆快速创建虚拟机),一个虚拟机代表一台独立的服务器。
K8S 集群规划如下:生产环境建议至少两台 Master 节点,LB 主备各一个节点;至少两台以上 Node 节点,根据实际运行的容器数量调整;Etcd 数据库可直接部署在 Master 和 Node 的节点,机器比较充足的话,可以部署在单独的节点上。
k8s-master-1
10.39.177.34
master-1
kube-apiserver kube-cpntroller-manager kube-scheduler ectd
k8s-master-2
10.39.177.67
master-2
kube-apiserver kube-cpntroller-manager kube-scheduler
k8s-node-1
10.39.177.50
node-1
kubelet kube-proxy docker ectd
k8s-node-2
10.39.177.189
node-2
kubelet kube-proxy docker ectd
Load Balancer(Master)
10.39.177.46
lb-master
Nginx
Load Balancer(Backup)
10.39.177.67
lb-backup
Nginx
④ 服务器硬件配置推荐
测试环境与生产环境服务器配置推荐如下,本地虚拟机的配置将按照本地测试环境的配置来创建虚拟机。

操作系统初始化
接下来将基于二进制包的方式,手动部署每个组件,来组成 K8S 高可用集群。通过手动部署每个组件,一步步熟悉每个组件的配置、组件之间的通信等,深层次的理解和掌握 K8S。
首先做的是每台服务器的配置初始化,依次按如下步骤初始化。
关闭防火墙
关闭selinux
关闭swap
添加 hosts
添加内容如下:
同步系统时间
各个节点之间需保持时间一致,因为自签证书是根据时间校验证书有效性,如果时间不一致,将校验不通过。
联网情况可使用如下命令
如果不能联外网可使用 date 命令设置时间
部署Etcd集群
自签证书
K8S 集群安装配置过程中,会使用各种证书,目的是为了加强集群安全性。K8S 提供了基于 CA 签名的双向数字证书认证方式和简单的基于 http base 或 token 的认证方式,其中 CA 证书方式的安全性最高。每个K8S集群都有一个集群根证书颁发机构(CA),集群中的组件通常使用CA来验证API server的证书,由API服务器验证kubelet客户端证书等。
证书生成操作可以在master节点上执行,证书只需要创建一次,以后在向集群中添加新节点时只要将证书拷贝到新节点上,并做一定的配置即可。下面就在 master-1 节点上来创建证书,详细的介绍也可以参考官方文档:分发自签名-CA-证书
① K8S 证书
如下是 K8S 各个组件需要使用的证书

② 准备 cfssl 工具
使用 cfssl 工具来生成证书,首先下载 cfssl 工具。依次执行如下命令:
注意,如果证书认证缺失,所以可以考虑请求里面关闭ssl证书认证,命令行中加上-k即可。
注意,如果下载不成功,就多打开几个窗口
自签 Etcd SSL 证书
首先为 etcd 签发一套SSL证书,通过如下命令创建几个目录,/k8s/etcd/ssl 用于存放 etcd 自签证书,/k8s/etcd/cfg 用于存放 etcd 配置文件,/k8s/etcd/bin 用于存放 etcd 执行程序。
进入 etcd 目录:
① 创建 CA 配置文件:ca-config.json
执行如下命令创建 ca-config.json
说明:
signing:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;
profiles:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;
expiry:证书过期时间
server auth:表示client可以用该 CA 对server提供的证书进行验证;
client auth:表示server可以用该CA对client提供的证书进行验证;
② 创建 CA 证书签名请求文件:ca-csr.json
执行如下命令创建 ca-csr.json:
说明:
CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;
key:加密算法
C:国家
ST:地区
L:城市
O:组织,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
OU:组织单位
③ 生成 CA 证书和私钥

说明:
ca-key.pem:CA 私钥
ca.pem:CA 数字证书
④ 创建证书签名请求文件:etcd-csr.json
执行如下命令创建 etcd-csr.json:
说明:
hosts:需要指定授权使用该证书的 IP 或域名列表,这里配置所有 etcd 的IP地址。
key:加密算法及长度
⑤ 为 etcd 生成证书和私钥

说明:
-ca:指定 CA 数字证书
-ca-key:指定 CA 私钥
-config:CA 配置文件
-profile:指定环境
-bare:指定证书名前缀
证书生成完成,后面部署 Etcd 时主要会用到如下几个证书:

Etcd 数据库集群部署
etcd 集群采用主从架构模式(一主多从)部署,集群通过选举产生 leader,因此需要部署奇数个节点(3/5/7)才能正常工作。etcd使用raft一致性算法保证每个节点的一致性。

① 下载etcd
从 github 上下载合适版本的 etcd
解压并复制
② 创建 etcd 配置文件:etcd.conf
说明:
ETCD_NAME:etcd在集群中的唯一名称
ETCD_DATA_DIR:etcd数据存放目录
ETCD_LISTEN_PEER_URLS:etcd集群间通讯的地址,设置为本机IP
ETCD_LISTEN_CLIENT_URLS:客户端访问的地址,设置为本机IP
ETCD_INITIAL_ADVERTISE_PEER_URLS:初始集群通告地址,集群内部通讯地址,设置为本机IP
ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址,设置为本机IP
ETCD_INITIAL_CLUSTER:集群节点地址,以 key=value 的形式添加各个 etcd 的地址
ETCD_INITIAL_CLUSTER_TOKEN:集群令牌,用于集群间做简单的认证
ETCD_INITIAL_CLUSTER_STATE:集群状态
ETCD_CERT_FILE:客户端 etcd 数字证书路径
ETCD_KEY_FILE:客户端 etcd 私钥路径
ETCD_TRUSTED_CA_FILE:客户端 CA 证书路径
ETCD_PEER_CERT_FILE:集群间通讯etcd数字证书路径
ETCD_PEER_KEY_FILE:集群间通讯etcd私钥路径
ETCD_PEER_TRUSTED_CA_FILE:集群间通讯CA证书路径
③ 创建 etcd 服务:etcd.service
通过EnvironmentFile指定 etcd.conf 作为环境配置文件。
etcd.service 更多的配置以及说明可以通过如下命令查看:
④ 将 etcd 目录拷贝到另外两个节点
⑤ 修改两个节点配置文件
修改 node-1 节点的 /k8s/etcd/cfg/etcd.conf
修改 node-2 节点的 /k8s/etcd/cfg/etcd.conf
⑥ 启动 etcd 服务
首先在三个节点将 etcd.service 拷贝到 /usr/lib/systemd/system/ 下
在三个节点启动 etcd 服务
设置开机启动
查看 etcd 的日志

注意:如果日志中出现连接异常信息,请确认所有节点防火墙是否开放2379,2380端口,或者直接关闭防火墙。
查看 etcd 集群状态

基础入门(一)
资源管理方式
概述
命令式对象管理:直接通过命令去操作 Kubernetes 的资源。
命令式对象配置:通过命令配置和配置文件去操作 Kubernetes 的资源。
声明式对象配置:通过 apply 命令和配置文件去操作 Kubernetes 的资源。
命令式对象管理
对象
测试
简单
命令式对象配置
文件
开发
可以审计、跟踪
声明式对象配置
目录
开发
支持目录操作
创建和更新资源使用声明式对象配置:kubectl apply -f xxx.yaml。
删除资源使用命令式对象配置:kubectl delete -f xxx.yaml。
查询资源使用命令式对象管理:kubectl get(describe) 资源名称。
命令式对象管理
kubectl 命令
kubectl 是 Kubernetes 集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装和部署。
kubectl 命令的语法如下:
参数
command:指定要对资源执行的操作,如:create、get 、delete 等。
type:指定资源的类型,如:deployment 、pod 、service 等。
name:指定资源的名称,名称大小写敏感。
flags:指定额外的可选参数。
示例:查看所有的 Pod
示例:以 yaml 格式查看某个 Pod
操作(command)
Kubernetes 允许对资源进行多种操作,可以通过 --help 查看详细的操作命令
经常使用的操作如下所示:
基本命令
create
创建
创建一个资源
edit
编辑
编辑一个资源
get
获取
获取一个资源
patch
更新
更新一个资源
delete
删除
删除一个资源
explain
解释
展示资源文档
运行和调试
run
运行
在集群中运行一个指定的镜像
expose
暴露
暴露资源为 Service
describe
描述
显示资源内部信息
logs
日志
输出容器在 Pod 中的日志
attach
缠绕
进入运行中的容器
exec
执行
执行容器中的一个命令
cp
复制
在 Pod 内外复制文件
rollout
首次展示
管理资源的发布
scale
规模
扩(缩)容 Pod 的数量
autoscale
自动调整
自动调整 Pod 的数量
高级命令
apply
应用
通过文件对资源进行配置
label
标签
更新资源上的标签
其他命令
cluster-info
集群信息
显示集群信息
version
版本
显示当前 Client 和 Server 的版本
资源类型(type)
Kubernetes 中所有的内容都抽象为资源,可以通过下面的命令进行查看
经常使用的资源如下所示:
集群级别资源
nodes
no
集群组成部分
namespaces
ns
隔离 Pod
Pod资源
Pods
po
装载容器
Pod资源
replicationcontrollers
rc
控制 Pod 资源
replicasets
rs
控制 Pod 资源
deployments
deploy
控制 Pod 资源
daemonsets
ds
控制 Pod 资源
jobs
控制 Pod 资源
cronjobs
cj
控制 Pod 资源
horizontalpodautoscalers
hpa
控制 Pod 资源
statefulsets
sts
控制 Pod 资源
服务发现资源
services
svc
统一 Pod 对外接口
ingress
ing
统一 Pod 对外接口
存储资源
volumeattachments
存储
persistentvolumes
pv
存储
persistentvolumeclaims
pvc
存储
配置资源
configmaps
cm
配置
secrets
配置
示例:查询命名空间
示例:创建命名空间
示例:删除命名空间
命令式对象配置
命令式对象配置就是通过命令配置和配置文件去操作 Kubernetes 的资源。
命令式对象配置的方式操作资源,可以简单的认为:命令 + yaml 配置文件(里面是命令需要的各种参数)。
通过如下示例,进行展示:
创建一个 nginxpod.yaml 文件,内容如下
执行 create 命令,创建资源
执行 get 命令,查看资源
执行 delete 命令,删除资源
声明式对象配置
声明式对象配置:通过 apply 命令和配置文件去操作 Kubernetes 的资源。
声明式对象配置和命令式对象配置类似,只不过它只有一个 apply 命令。
apply 命令相当于 create 命令和 patch 命令。
通过如下示例,进行展示:
创建一个 nginx-pod.yaml 文件,内容如下
执行 apply 命令
故障排除
显示资源列表
语法命令
示例:获取类型为 Deployment 的资源列表
示例:获取类型为 Pod 的资源列表
示例:获取类型为 Node 的资源列表
示例:显示所有名称空间下的 Deployment
或者
示例:查看 kube-system 名称空间下的 Deployment
示例:查看在名称空间下的资源对象
示例:查询不在名称空间下的资源对象(理解:就是受 Kubernetes 集群统一调度,而不受到具体名称空间的约束)
显示有关资源的详细信息
语法命令
示例:查询名称为 nginx-pod 的 Pod 的信息
示例:显示名称为 nginx 的 Deployment 的信息
查询 Pod 中的容器的打印日志
命令:类似于 Docker 的
docker logs -f xxx
示例:查询名称为 nginx-pod 的 Pod 日志
在 Pod 中的容器环境内执行命令
命令:类似于 Docker 的
docker exec -it xxx /bin/bash
示例:在名称为 nginx-pod 的容器中执行命令
部署(Deployment)一个应用

在 Kubernetes 中,通过创建 Deployment ,可以创建应用程序(如同 Docker 的 image)的实例(如同 Docker 的容器),这个实例被包含在 Pod 的概念中,Pod 是 Kubernetes 中的最小管理单元。
在 Kubernetes 集群中创建 Deployment 之后,Deployment 将指示 Kubernetes 集群如何创建和更新应用程序的实例,Master 节点将应用程序实例调度到集群中的具体的节点上。
创建应用程序实例之后,Kubernetes 的 Deployment Controller 将会持续监控这些实例。如果运行这些实例的 Node 节点关机或者被删除,则 Kubernetes 的 Deployment Controller 将在集群中的资源最优的另一个 Node 节点上重新创建一个新的实例,这就提供了一种自我修复机制 来解决机器故障或维护问题。
在容器编排之前的时代,各种安装脚本通常用于启动应用程序,但是却不能使得应用程序从机器故障中恢复。通过创建应用程序实例并确保它们在集群节点中的运行实例个数,Kubernetes 的 Deployment 提供了一种完全不同的方式来管理应用程序。
语法命令
示例
原理

应用程序探索
概述
创建 Deployment 之后,Kubernetes 会创建一个 Pod(容器组)来放置应用程序实例(container 容器)。

Pod
Pod(容器组)是一个 Kubernetes 中的一个抽象概念,用于存放一组 Container(可以包含一个或多个 Container 容器,如:上图中的小正方体)以及这些 Container(容器)的一些共享资源。这些资源包括:
共享存储,称为卷(Volume),如:上图中的紫色圆柱体。
网络,每个 Pod(容器组)在集群中有一个唯一的 IP,Pod(容器组)中的 Container(容器)共享该 IP 地址。
Container(容器)的基本新,如:容器的镜像版本、对外暴露的端口等。
Pod(容器组)是 Kubernetes 集群上的最基本的单元。当我们在 Kubernetes 集群上创建 Deployment 的时候,会在 Kubernetes 集群上创建包含容器的 Pod (而不是直接创建容器)。每个 Pod 都和运行它的 Node 节点绑定,并保持在哪里直到终止或被删除。如果 Node 节点发生了故障,则会在集群中的其他可用的 Node 节点上运行相同的 Pod(从同样的镜像创建 Container,使用同样的配置,IP 地址不同,Pod 名称不同)。
Pod 是一组容器(可包含一个或多个应用程序容器),以及共享存储(卷 Volumes)、IP 地址和有关如何运行容器的信息。
如果多个容器紧密耦合并且需要共享磁盘等资源,则他们应该被部署在同一个Pod(容器组)中。
Node

Pod(容器组)总是在 Node(节点)上运行。Node(节点)是 Kubernetes 集群中的计算机,可以是虚拟机或物理机。每个 Node(节点)都由 Master 管理。一个 Node(节点)可以有多个Pod(容器组),kubernetes 的 Master 会根据每个 Node(节点)上可用资源的情况,自动调度 Pod(容器组)到最佳的 Node(节点)上。
每个 Kubernetes 的 Node(节点)至少运行:
Kubelet,负责 Master节点和 Node 节点之间通信的进程;管理 Pod(容器组)和 Pod(容器组)内运行的 Container(容器)。
kube-proxy,负责进行流量转发。
容器运行环境(如:Docker)负责下载镜像、创建和运行容器等。
应用程序外部可见(Service)
概述
Kubernetes 中的 Pod 是转瞬即逝,Pod 有自己的 生命周期 ,当一个工作的 Node 挂掉后,在 Node 上运行的 Pod 也会随之消亡。Deployment(Deployment 其实是驱动 ReplicaSet) 会自动的创建新的 Pod 驱动集群回到目标状态,以保证应用程序正常运行。
Kubernetes 的 Service 是一个抽象层,它定义了一组 Pod 的逻辑集,并为这些 Pod 支持外部流量暴露、负载均衡和服务发现。
Service 使从属 Pod 之间的松耦合成为可能。 和其他 Kubernetes 对象一样,Service 用 YAML (更推荐) 或者 JSON 来定义。Service 下的一组 Pod 通常由 LabelSelector (请参阅下面的说明为什么您可能想要一个 spec 中不包含selector的服务)来标记。
尽管每个 Pod 都有一个唯一的 IP 地址,但是如果没有 Service ,这些 IP 不会暴露在群集外部。Service 允许您的应用程序接收流量。Service 也可以用在 ServiceSpec 标记type的方式暴露。
ClusterIP(默认) :在集群的内部 IP 上公开 Service 。这种类型使得 Service 只能从集群内访问。
NodePort:使用 NAT 在集群中每个选定 Node 的相同端口上公开 Service 。使用
<NodeIP>:<NodePort>从集群外部访问 Service。是 ClusterIP 的超集。LoadBalancer:在当前云中创建一个外部负载均衡器(如果支持的话),并为 Service 分配一个固定的外部 IP 。是 NodePort 的超集。
ExternalName:通过返回带有该名称的 CNAME 记录,使用任意名称(由 spec 中的
externalName指定)公开 Service 。不使用代理。这种类型需要kube-dns的 v1.7 或更高版本。
Service 和 Label

Service 通过一组 Pod 路由通信。Service 是一种抽象,它允许 Pod 死亡并在 Kubernetes 中复制,而不会影响应用程序。在依赖的 Pod (如:应用程序中的前端和后端组件)之间进行发现和路由是由 Kubernetes Service 处理的。
Service 匹配一组 Pod 是使用 标签(Label)和选择器(Selector), 它们是允许对 Kubernetes 中的对象进行逻辑操作的一种分组原语。标签( Label )是附加在对象上的键/值对,可以以多种方式使用:
指定用于开发,测试和生产的对象
嵌入版本标签
使用 Label 将对象进行分类

示例
伸缩应用程序-扩缩容
当创建了一个 Deployment,然通过 Service 提供访问 Pod 的方式,但是当流量增加的时候,需要对应用程序进行伸缩操作以满足系统性能的需求。

语法命令
示例
执行滚动升级
滚动升级允许通过使用新的示例逐步更新 Pod 实例,从而实现 Deployment 更新,0 停机。
与应用程序扩展类似,如果暴露了 Deployment,服务(Service)将在更新期间仅对可用的 pod 进行负载均衡,可用 Pod 是应用程序可用的实例。
滚动更新允许以下操作:
将应用程序从一个环境提升到另一个环境(通过容器镜像更新)
回滚到以前的版本
持续集成和持续交付应用程序,无需停机
相应语法命令操作如下:
镜像升级
查看历史记录
默认回滚到上一个版本
默认回滚到第一个版本
示例展示:
使用声明式对象配置模拟上述效果
部署一个 Deployment
编辑yaml文件
yaml文件内容
创建deploy
暴露应用
编辑yaml文件
yaml文件内容
创建service
安装 DashBoard
下载 yaml 文件(网速不行,请点这里📎recommended.yaml),并安装
将
type: ClusterIP改成NodePort,便于通过Node端口访问
查看 Dashboard 暴露外网端口

访问dashboard:https://192.168.183.101:32568/
注意,使用https,而不是http

创建账户
主要内容是创建admin-user账户,并授予集群管理权限。
获取账户token

将token复制到web界面上进行输入即可,进入网页内部。

基础入门(二)
Namespace
Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。
默认创建的namespace
default
Active
45h
所有未指定Namespace的对象都会被分配在default命名空间
kube-node-lease
Active
45h
集群节点之间的心跳维护,v1.13开始引入
kube-public
Active
45h
此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system
Active
45h
所有由Kubernetes系统创建的资源都处于这个命名空间
Namespace相关命令
查看所有的ns
查看指定的ns
指定输出格式
kubernetes支持的格式有很多,比较常见的是wide、json、yaml。
-o 格式参数
查看ns详情
创建&删除
YAML配置
Pod
Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。
Pod相关命令
kubernetes没有提供单独运行Pod的命令,都是通过Pod控制器来实现的。
看指定名称空间
创建并运行
这种方式创建了控制器,想要删除还需要删除deployment。
查看Pod基本信息
查看Pod的详细信息
获取pod信息
访问POD
删除指定Pod
查询一下当前namespace下的Pod控制器
YAML配置
Label
两种Label Selector
常用的Label 示例如下:
版本标签:"version":"release", "version":"stable"......
环境标签:"environment":"dev","environment":"test","environment":"pro"
架构标签:"tier":"frontend","tier":"backend"
当前有两种Label Selector:
基于等式的Label Selector
name = slave选择所有包含Label中key="name"且value="slave"的对象env != production选择所有包括Label中的key="env"且value不等于"production"的对象
基于集合的Label Selector
name in (master, slave)选择所有包含Label中的key="name"且value="master"或"slave"的对象name not in (frontend)选择所有包含Label中的key="name"且value不等于"frontend"的对象
标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用逗号","进行分隔即可。例如:
Label相关命令
为pod资源打标签
为pod资源更新标签
查看标签
筛选标签
删除标签
YAML配置
Deployment
在kubernetes中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod**,一般都是通过Pod控制器来完成的**。Pod控制器用于pod的管理,确保pod资源符合预期的状态,当pod的资源出现故障时,会尝试进行重启或重建pod。
Pod控制器其中的一种是 Deployment。
Deployment相关命令
创建Deployment
--image:指定pod的镜像--port:指定端口--replicas:指定创建pod数量--namespace:指定namespace
查看创建的Pod
查看deployment的信息
UP-TO-DATE:成功升级的副本数量
AVAILABLE:可用副本的数量
查看deployment的详细信息
删除
YAML配置
Service
Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。
Service相关命令
操作一:创建集群内部可访问的Service
操作二:创建集群外部也可访问的Service
YAML配置
基础入门(三)
Kubernetes 对象(Kubernetes Objects)
Kubernetes 里面操作的资源实体,就是 Kubernetes 的对象,可以使用 yaml 来声明,然后让 Kubernetes 根据 yaml 的声明创建出这个对象。
操作 Kubernetes 对象,无论是创建、修改还是删除,都需要使用 Kubernetes 的 API 。如:当使用 kubectl 命令行的时候,CLI 会执行必要的 Kubernetes API 调用。
Kubernetes 对象指的是 Kubernetes 系统的持久化实体,所有的这些 Kubernetes 对象,就代表了当前集群中的实际情况。常规的应用中,我们将应用程序产生的数据存放在数据库中;同理,Kubernetes 将其数据以 Kubernetes 对象的形式通过 api-server 存储在 etcd 中。具体来说,这些数据(Kubernetes 对象)描述了:
集群中运行了那些容器化应用程序,以及在哪个节点上运行。
集群中应用程序可用的资源,如:网络、存储等。
应用程序相关的策略定义,如:重启策略、升级策略和容错策略等。
其他 Kubernetes 管理应用程序时所需要的信息。
每一个 Kubernetes 对象都包含了两个重要字段:
spec:需要由我们来提供,描述了我们对该对象所期望的 目标状态 。
status:只能由 Kubernetes 系统来修改,描述了该对象在 Kubernetes 系统中的实际状态。
Kubernetes 通过对应的控制器(如:Deployment 等),不断的使得实际状态趋向于我们期望的目标状态。
示例:查看 Deployment 时在系统底层运行的 yaml
【问】:Kubernetes 是如何保证最终一致的?
etcd 保存的创建资源期望的状态以及最终这个资源的状态,这两种状态需要最终一致,即:spec 和 status 要最终一致。
当输入 kubectl create deployment my-nginx --image=nginx 命令的时候,api-server 保存到 etcd ,controller-manager 会解析数据,知道集群中需要 my-nginx ,保存到 etcd 中。
kubelet 在底层不停的死循环使得 spec 状态和 status 最终一致,如果 spec.replicas != status.replicas ,就启动 Pod。
描述 Kubernetes 对象
当在 Kubernetes 集群中创建一个对象的时候,必须提供:
该对象的 spec 字段,通过该字段描述我们期望的目标状态。
该对象的一些基本信息,如:名字等。
可以使用 kubectl 命令行创建对象,也可以使用 yaml 格式的文件进行创建。
使用
kubectl apply命令进行部署
使用
kubectl delete命令进行删除
Kubernetes 对象的 yaml 格式

在上述的 yaml 文件中,如下的字段是必须填写的:
apiVersion:用来创建对象时所需要的 Kubernetes API 的版本,可以通过 kubectl api-resources 查询。
kind:创建对象的类型,可以通过 kubectl api-resources 查询。
metadata:用来唯一确定该对象的元数据,包括 name 、namespace 等,如果 namespace 为空,则默认值为 default 。
spec:翻译为 规格 ,表示我们对该对象的期望状态。
status 不需要编写,那是 Kubernetes 实际运行过程中将变化的记录保存在此字段中。
不同类型的 Kubernetes ,其 spec 对象的格式不同(含有不同的内嵌字段),通过 API 手册可以查看 Kubernetes 对象的字段和描述。 或者,也可以通过此地址来参照。
实际中如何创建 Kubernetes 对象的 yaml
如果 Kubernetes 集群中已经存在了要创建的对象,那么可以使用 kubectl get 直接输出 yaml ,然后去除 status 即可
如果 Kubernetes 集群中不存在了要创建的对象,那么可以使用类似
kubectl run xxx --dry-run=client输出 yaml
对象名称
概述
Kubernetes REST API 中,所有的对象都是通过 name 和 UID 唯一性的确定。
可以通过 namespace + name 唯一性的确定一个 RESTful 对象,如:
Name
在同一名称空间下,同一个类型的对象,可以通过 name 来确定唯一性。如果删除该对象之后,可以再重新创建一个同名对象。
根据命名规则,Kubernetes 对象的名称应该是:
最长不超过 253 个字符。
必须由小写字母、数字、减号 - 、小数点 . 组成。
某些资源类型有更具体的要求。
示例:下面的配置文件定义了一个 name 为 nginx-demo 的 Pod,该 Pod 包含一个 name 为 nginx 的 容器
UID
UID 是由 Kubernetes 系统生成的,唯一标识某个 Kubernetes 对象的字符串。
Kubernetes集群中,每创建一个对象,都有一个唯一的 UID 。用于区分多次创建的同名对象(如前面所述,按照名字删除对象后,重新再创建同名对象时,两次创建的对象 name 相同,但是 UID 不同。)
名称空间(命名空间)
概述
在 Kubernetes 中名称空间是用来对象资源进行隔离的。 默认情况下,Kubernetes 会初始化四个名称空间:
default:所有没有指定 namespace 的对象都会被分配到此名称空间中。
kube-node-lease:Kubernetes 集群节点之间的心跳维护,V 1.13 开始引入。
kube-system:Kubernetes 系统创建的对象放在此名称空间中。
kube-public:此名称空间是 Kubernetes 集群安装时自动创建的,并且所有的用户都可以访问(包括未认证的用户),主要是为集群预留的,如:在某些情况中,某些 Kubernetes 对象应用应该能被所有集群用户访问到。
名称空间在实际开发中如何划分
基于环境隔离,如:dev(开发)、test(测试)、prod(生产)等。
基于产品线隔离,如:前端、后端、中间件、大数据、Android、iOS、小程序等。
基于团队隔离,如:企业发展事业部、技术工程事业部、云平台事业部等。
名称空间的特点
名称空间资源隔离、网络不隔离,如:配置文件不可以跨名称空间访问,但是网络访问可以跨名称空间访问。
默认情况下,安装 Kubernetes 集群的时候,会初始化一个
default名称空间,用来承载那些没有指定名称空间的 Pod 、Service 、Deployment 等对象。
名称空间的命名规则
不能带小数点(
.)。不能带下划线(
_)。使用数字、小写字母或减号(
-)组成的字符串。
名称空间的操作
示例:创建和删除名称空间(yaml )
示例:创建和删除名称空间(命令行 )
示例:创建 Pod 的同时,指定自定义的名称空间(yaml)
Service 与 Pod 的 DNS
当创建一个 Service 的时候,Kubernetes 会创建一个相应的 DNS 条目。
该条目的形式是
<service-name>.<namespace-name>.svc.cluster.local,这意味着如果容器中只使用<服务名称>,它将被解析到本地名称空间的服务器。这对于跨多个名字空间(如开发、测试和生产) 使用相同的配置非常有用。如果你希望跨名字空间访问,则需要使用完全限定域名(FQDN)。
注意事项
大多数的 Kubernetes 资源(如:Pod、Service、副本控制器等)都位于某些名称空间中,但是名称空间本身并不在名称空间中,而且底层资源(如:node 和持久化卷)不属于任何命名空间。
查看在名称空间中的资源:
查看不在名称空间中的资源:
标签和选择器
概述
标签(Label)是附件在 Kubernetes 对象上的一组键值对,其意图是按照对用户有意义的方式来标识 Kubernetes 镀锡,同时,又不对 Kubernetes 的核心逻辑产生影响。标签可以用来组织和选择一组 Kubernetes 对象。我们可以在创建 Kubernetes 对象的时候为其添加标签,也可以在创建以后为其添加标签。每个 Kubernetes 对象可以有多个标签,同一个对象的标签的 key 必须是唯一的,如:
使用标签(Label)可以高效的查询和监听 Kubernetes 操作,在 Kubernetes 界面工具(如:Kubernetes DashBoard)和 kubectl 中,标签使用的非常普遍。而那些非标识性的信息应该记录在 注解(Annotation) 中。
为什么要使用标签
使用标签,用户可以按照自己期望的形式组织 Kubernetes 对象之间的结构,而无需对 Kubernetes 有任何修改。
应用程序的部署或者批处理程序的部署通常是多维度的(如:多个高可用分区、多个程序版本、多个微服务分层)。管理这些对象的时候,很多时候要针对某一个维护的条件做整体操作,如:将某个版本的程序整体删除。这种情况下,如果用户能够事先规划好标签的使用,再通过标签进行选择,就非常的便捷。
标签的例子有:
release: stable、release: canary。
environment: dev、environment: qa、environment: production。
tier: frontend、tier: backend、tier: cache。
partition: customerA、partition: customerB。
track: daily、track: weekly。
上面只是一些使用比较普遍的标签,也可以根据自己的情况建立合适的标签。
标签的语法
标签是一组键值对(key/value),标签的 key 有两个部分:可选的前缀和标签名,通过 / 分隔。
标签前缀:
标签前缀部分是可选的。
如果指定,必须是一个 DNS 的子域名,如:k8s.eip.work 。
不能多于 253 个字符。
使用 / 和标签名分隔。
标签名:
标签名部分是必须的。
不能多余 63 个字符。
必须由字母、数字开始和结尾。
可以包含字母、数字、减号(-)、下划线(_)、小数点(.)。
如果省略标签前缀,则标签的 key 就被认为是专属于用户的。Kubernetes 的系统组件(如:kube-scheduler、kube-controller-manager、kube-apiserver、kubectl 或其他第三方组件)向可以的 Kubernetes 对象添加标签的时候,必须指定一个前缀。
kubernetes.io/和k8s.io/这两个前缀是 Kubernetes 核心组件预留的。
标签的 value :
不能多于 63 个字符。
可以为空字符串。
如果不为空,则必须由字母、数字开始和结尾。
如果不为空,可以包含字母、数字、减号(-)、下划线(_)、小数点(.)。
示例:
标签选择器
通常来讲,会有多个 Kubernetes 对象包含相同的标签。通过使用标签选择器(label selector),用户/客户端可以选择一组对象。标签选择器是 Kubernetes 中最主要的分类和筛选手段。
Kubernetes 的 api-server 支持两种形式的标签选择器,equality-based 基于等式的和 set-based 基于集合的。标签选择器可以包含多个条件,并使用逗号进行分隔,此时只要满足所有条件的 Kubernetes对象才会被选中。
基于等式的标签选择器,可以使用三种操作符 = 、== 、!=。前两个操作符含义是一样的,都代表相等;后一个操作符代表不相等。
示例:选择了标签名为
environment且 标签值为production的Kubernetes对象
示例:选择了标签名为
tier且标签值不等于frontend的对象,以及不包含标签tier的对象
示例:选择所有包含
partition标签的对象
示例:选择所有不包含
partition标签的对象
基于集合标签选择器,可以根据标签名的一组值进行筛选。支持的操作符有三种:in、notin、exists。
示例:选择所有的包含
environment标签且值为production或qa的对象
选择所有的
tier标签不为frontend和backend的对象,或不含tier标签的对象
选择包含
partition标签(不检查标签值)且environment不是qa的对象
示例:Job、Deployment、ReplicaSet 和 DaemonSet 同时支持基于等式的选择方式和基于集合的选择方式。
matchLabels 是由 {key,value} 对组成的映射。 matchLabels 映射中的单个 {key,value} 等同于 matchExpressions 的元素, 其 key 字段为 "key",operator 为 "In",而 values 数组仅包含 "value"。
matchExpressions 是 Pod 选择算符需求的列表。 有效的运算符包括 In、NotIn、Exists 和 DoesNotExist。 在 In 和 NotIn 的情况下,设置的值必须是非空的。 来自 matchLabels 和 matchExpressions 的所有要求都按逻辑与的关系组合到一起 -- 它们必须都满足才能匹配。
标签的操作
示例:添加标签
示例:更新标签
示例:删除标签
注解 annotations
注解(annotation) 可以用来向 Kubernetes 对象的 meta.annotations 字段添加任意的信息。Kubernetes 的客户端或者自动化工具可以存取这些信息以实现自定义的逻辑。
VScode 插件
推荐在 Vscode 中安装如下的插件,可以加快编写 Kubernetes 的 yaml 的速度:


重新认识 kubectl 和 kubelet
核心文件夹:
/etc/kubernetes。kubelet 额外参数配置:
/etc/sysconfig/kubelet。kubelet配置位置:
/var/lib/kubelet/config.yaml。
万物基础 -- 容器
镜像
概述
平时使用 Docker 的镜像非常之简单,类似于下面的命令:
其实,完整的写法应该是这样:
如果使用自己搭建过 Docker 的私有镜像仓库,可以发现会出现如下的命令:
说明:
192.168.65.100:镜像仓库的地址。
5000:镜像仓库的端口。
xudaxian:镜像仓库的名称。
ubuntu:镜像的名称。
1.0:镜像的版本,如果不写,默认就是 latest 。
当然,如果拉取的是 hub.docker.com 中的镜像,那么镜像仓库地址以及端口都可以省略。
Kubernetes 中的镜像
在 Kubernetes 的 Pod 定义容器的时候,必须指定容器所使用的镜像,容器中的 image 字段支持的语法和 docker 命令是一样的,包括私有镜像仓库和标签,如:
注意:在生产环境中,建议锁定镜像的版本。
示例:
Kubernetes 中的镜像拉取策略:
IfNotPresent(默认) :只有当镜像在本地不存在时才会拉取。
Always :每当 kubelet 启动一个容器时,kubelet 会查询容器的镜像仓库, 将名称解析为一个镜像摘要。 如果 kubelet 有一个容器镜像,并且对应的摘要已在本地缓存,kubelet 就会使用其缓存的镜像; 否则,kubelet 就会使用解析后的摘要拉取镜像,并使用该镜像来启动容器。
Never :Kubelet 不会尝试获取镜像。如果镜像已经以某种方式存在本地, kubelet 会尝试启动容器;否则,会启动失败。
示例:
下载私有仓库的镜像
使用阿里云容器镜像的私有仓库,阿里云要求进行登录,如果是 docker 拉取镜像,那么只需要 docker login 之类的就可以了;但是,如果使用 Kubernetes 该怎么办?
创建 secret
在 yaml 中拉取镜像的时候设置镜像拉取的密钥(secret)
注意:需要将 secret 的用户名和密码设置为自己的,而且在拉取阿里云私有镜像的时候设置为自己的镜像。
环境变量
使用 env 用来给 Pod 中的容器设置环境变量,相当于 docker run -e xxx=xxx 中的 -e 参数。
示例:
启动命令
Docker 的镜像拥有存储镜像信息的相关元数据,如果不设置生命周期命令和参数,容器运行时会运行镜像制作时提供的默认的命令和参数,Docker 原生定义这两个字段为 ENTRYPOINT 和 CMD 。
如果在创建工作负载时填写了容器的运行命令和参数,将会覆盖镜像构建时的默认命令 Entrypoint 、CMD,规则如下:
[touch]
[/root/test]
未设置
未设置
[touch /root/test]
[touch]
[/root/test]
[mkdir]
未设置
[mkdir]
[touch]
[/root/test]
未设置
[/opt/test]
[touch /opt/test]
[touch]
[/root/test]
[mkdir]
[/opt/test]
[mkdir /opt/test]
换言之,如果在 Kubernetes 的 yaml 中定义了 comand 和 args ,那么就会覆盖 Dockerfile 中的 ENTRPOINT 和 CMD 。
示例:启动 MySQL
资源限额
容器中的程序要运行,肯定会占用一定的资源,比如 CPU 和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量的资源,导致其他的容器无法运行。
针对上面的情况,Kubernetes 提供了对内存和 CPU 的资源进行配额的机制,这种机制主要通过 resources 选项实现,它有两个子选项:
limits:用于限制运行的容器的最大占用资源,当容器占用资源超过 limits 时会被终止,并进行重启。
requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
cpu:core 数,可以为整数或小数,1 == 1000m。
memory:内存大小,可以使用 Gi、 Mi、G、M 等形式。
示例:
工作负载
概述
工作负载是运行的 Kubernetes 上的一个应用程序。
一个应用很复杂,可能由单个组件或者多个组件共同完成。我们可以用一组 Pod 来描述一个应用,也就是一个工作负载,而 Pod 是一组容器。
换言之,工作负载控制一组 Pod ,Pod 控制一组容器(如:Deployment【工作负载】部署 3 个副本的 nginx-pod ,每个 nginx-pod 里面是真正的 nginx 容器)。

工作负载能让 Pod 拥有自愈能力。我们主要研究不同的工作负载如何控制 Pod 的行为。
Pod
Pod介绍
什么是Pod
Pod 是一组(一个多多个)容器的集合,这些容器共享存储、网络等。在实际开发中,一般不直接创建 Pod ,而是创建一个工作负载(如:Deployment等),让它们来创建 Pod 。
Pod 的特点:
Pod 对容器有自愈能力(自我恢复能力),Pod 可以自动重启失败的容器。
Pod 自己不能恢复自己,Pod 被删除了就没有了,但是有时我们希望如果 Node 节点宕机了,这些 Pod 还可以在其他节点重新启动。
Pod 中可以有一个容器,也可以有多个容器。
如果 Pod 中有多个容器协同工作(通常而言,都是有关系了),我们将提供服务的称为主容器,另外的容器称为 SideCar 。
Pod 天生为其成员容器提供了两种共享资源:网络 和 存储。
一个 Pod 由一个 Pause 容器设置整个 Pod 里面的所有容器的网络、名称空间等信息。
Pod结构
每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类:
用户程序所在的容器,数量可多可少
Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
可以以它为依据,评估整个Pod的健康状态
可以在根容器上设置Ip地址,其它容器都共享此IP(Pod的IP),以实现Pod内部的网路通信
Pod定义
在kubernetes中基本所有资源的一级属性都是一样的,主要包含5部分:
apiVersion 版本,由kubernetes内部定义,版本号可以用
kubectl api-versions查询到kind 类型,由kubernetes内部定义,版本号可以用
kubectl api-resources查询到metadata 元数据,主要是资源标识和说明,常用的有name、namespace、labels等
status 状态信息,里面的内容不需要定义,由kubernetes自动生成
spec 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
在上面的属性中,spec是接下来研究的重点,常见子属性:
containers <[]Object> 容器列表,用于定义容器的详细信息
nodeName 根据nodeName的值将pod调度到指定的Node节点上
nodeSelector <map[]> 根据NodeSelector中定义的信息选择将该Pod调度到包含这些label的Node 上
hostNetwork 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes <[]Object> 存储卷,用于定义Pod上面挂在的存储信息
restartPolicy 重启策略,表示Pod在遇到故障的时候的处理策略
查看pod
Pod的使用
可以使用 Deployment 等各种工作负载的 yaml 文件创建 Pod ,也可以直接创建 Pod 。
Pod 的模板如下:
示例:多容器协同

温馨提示:如果想进多容器协同的 Pod 中查看指定容器,使用这样的命令:
kubectl exec -it Pod的名称 -c Pod中的容器名 -- COMMAND。
Pod的初始化容器
初始化容器是在 Pod 的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
初始化容器 必须运行完成直至结束 ,如果某个初始化容器运行失败,那么 Kubernetes 需要重启它直至成功完成。
初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行。
初始化容器有很多的应用场景,下面列出的是最常见的几个:
提供主容器镜像中不具备的工具程序或自定义代码。
初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足。

示例:
Pod的临时容器
概述
临时容器是一种特殊的容器,该容器可以在现有的 Pod 中临时运行,以便完成我们发起的操作,比如故障排查。我们应该使用临时容器来检查服务,而不是用临时容器来构建应用程序。
Pod 是 Kubernetes 集群进行管理的最小单元,由于 Pod 是一次性且可以替换的,因此 Pod 一旦被创建,就无法将容器加入到Pod 中。而且,我们通常使用 Deployment 来删除并替换Pod。但是,有的时候我们需要检查现有 Pod 的状态,比如对难以复现的故障进行排查。在这些场景中,可以在现有 Pod 中运行临时容器来检查其状态并运行任意命令。
临时容器在目前的 Kubernetes 版本是 beta 。
什么是临时容器
临时容器和其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启,因此不适合用来构建应用程序。临时容器使用和常规容器相同的 ContainerSpec 来描述,但是许多字段是不兼容或者不允许的。
临时容器没有端口配置,因此像 ports 、livenessProbe、readinessProbe这样的字段是没有的。
Pod 的资源分配是不可变的,因此 resources 这样的配置临时容器也是没有的。
临时容器是使用 ephemeralcontainers 来进行创建的,而不是直接添加到 pod.spec 中,所以是无法使用 kubectl edit 来添加一个临时容器。
和常规容器一样,将临时容器添加到Pod后,不能更改或删除临时容器。
临时容器的用途
当由于容器奔溃或容器镜像不包含调试工具而导致 kubectl exec 无用的时候,临时容器对于交互式故障排查非常有用。
比如,像 distroless 镜像 允许用户部署最小的容器镜像,从而减少攻击面并减少故障和漏洞的暴露。由于 distroless 镜像 不包含 Shell 或任何的调试工具,因此很难单独使用 kubectl exec 命令进行故障排查。
使用临时容器的时候,启用 进程名字空间共享 很有帮助,可以查看其他容器中的进程。
开启临时容器
查询临时容器是否开启

默认是关闭的。
在 Master 节点(192.168.183.101)和 Node 节点(192.168.183.102、192.168.183.103)修改 kubectl 的参数
加载配置文件以便重启 kubelet
在 Master 节点(192.168.183.101)修改 kube-apiserver.yaml 和 kube-scheduler.yaml
使用临时容器在线debug
创建一个 Pod
创建临时容器
查看nginx这个pod是否已经有临时容器
或者通过如下
创建 ec.json 文件,内容如下
注意:json 文件目前是没有注释的。
应用临时容器
进入到临时容器上去
重启策略
在容器探测中,一旦容器探测出现了问题,Kubernetes 就会对容器所在的 Pod 进行重启,其实这是由 Pod 的重启策略决定的,Pod 的重启策略有 3 种,分别如下:
Always:容器失效时,自动重启该容器(默认值)。
OnFailure:容器终止运行且退出码不为 0 时重启。
Never:不论状态如何,都不重启该容器。
重启策略适用于 Pod 对象中的所有容器,首次需要重启的容器,将在其需要的时候立即进行重启,随后再次重启的操作将由 kubelet 延迟一段时间后进行,且反复的重启操作的延迟时长以此为 10s 、20s 、40s 、80s 、160s 和 300s ,300s 是最大的延迟时长。
示例:
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
Kubernetes 在主容器启动之后和停止之前提供了两个钩子函数:
postStart:容器创建之后执行,如果失败会重启容器。
preStop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。
钩子处理器支持使用下面的三种方式定义动作:
exec 命令:在容器内执行一次命令。
tcpSocket:在当前容器尝试访问指定的 socket 。
httpGet:在当前容器中向某 url 发起 HTTP 请求(应用程序使用此种方式最多)。
示例:
Probe 探针机制(健康检查机制)
概述
probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。
针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:
startupProbe(启动探针):
kubelet 使用启动探针,来检测应用是否已经启动。如果启动就可以进行后续的探测检查。慢容器一定指定启动探针。一直在等待启动。
启动探针一旦成功就不在执行(只会成功执行一次),存活探针和就绪探针将接着持续运行。
livenessProbe(存活探针):
kubelet 使用存活探针,来检测容器是否正常存活。(有些容器可能产生死锁【应用程序在运行,但是无法继续执行后面的步骤】),如果检测失败就会重新启动这个容器。
readinessProbe(就绪探针):
kubelet 使用就绪探针,来检测容器是否准备好了可以接收流量。当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。
用途:Service后端负载均衡多个Pod,如果某个Pod还没就绪,就会从service负载均衡里面剔除。
Probe 的配置项:
initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
periodSeconds:执行探测的时间间隔(单位是秒),默认是 10 秒,最小值是 1。
successThreshold:探测器在失败后,被视为成功的最小连续成功数,默认值是 1。存活和启动探针的这个值必须是 1。
failureThreshold:当探测失败时,Kubernetes 的重试次数,存活探测情况下的放弃就意味着重新启动容器, 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。
timeoutSeconds:探测的超时后等待多少秒,默认值是 1 秒,最小值是 1。
Probe 的探测方式:exec 、httpGet(应用程序使用此种方式最多)、tcpSocket。
应用示例
示例:
微服务
SpringBoot(2.3+) 已经支持了 Kubernetes 的探针机制,只需要添加 spring-boot-starter-actuator 依赖,并在 application.yml 中配置如下内容:
存活探针的路径是:
/actuator/health/liveness。就绪探针的路径是:
/actuator/health/readiness。
Pod调度
Pod调度方式
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度【强制策略】
NodeName
这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。
亲和性调度
上述介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了它的使用场景。
基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
NodeAffinity
关系符的使用说明
NodeAffinity规则设置的注意事项:
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功
如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化
PodAffinity
首先来看一下PodAffinity的可配置项:
topologyKey 用于指定调度时作用域,例如:
如果指定为
kubernetes.io/hostname,那就是以Node节点为区分范围如果指定为
beta.kubernetes.io/os,则以Node节点的操作系统类型来区分
PodAntiAffinity
PodAntiAffinity主要实现以运行的Pod为参照,新创建的Pod跟参照pod不在一个区域中的功能。与上面的PodAffinity是一种相反策略。
污点(Taints)
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否允许Pod调度过来。
Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可调度
NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod
NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离

污点API
接下来,演示下污点的效果:
为node设置污点(PreferNoSchedule)
创建pod1
查看pod,运行成功
取消污点PreferNoSchedule,创建NoSchedule污点
创建pod2
为node设置污点(职消NoSchedule,设置NoExecute)
创建pod3
小提示: 使用kubeadn搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上
容忍 (Toleration)
污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,Pod通过容忍忽略拒绝。
查看相应配置
ReplicaSet(rs)
概述

ReplicaSet 的主要作用是保证一定数量的 Pod 能够正常运行,它会持续监听这些 Pod 的运行状态,一旦 Pod 发生故障,就会重启或重建。同时它还支持对 Pod 数量的扩缩容。
注意:ReplicaSet 没有镜像升级的功能,如果要模拟这一功能,需要手动将 rs 删除,然后修改 yaml 文件,再创建新的 Pod 。

ReplicaSet 的资源清单文件:
创建 ReplicaSet
创建 ReplicaSet
扩缩容
方式一:使用
kubectl edit命令,修改spec:replicas:n即可
方式二:使用
scale命令使用扩缩容,后面加上--replicas=n直接指定目标数量即可
方式三: 修改 yaml 文件中 spec.replicas 字段,随后使用
kubectl apply -f xxx.yaml命令即可
模拟镜像升级
先删除 rs ,更新 rs 中 Pod 的模板,再重新创建 rs
注意:此种方式在实际开发中并不可取,它的原理是先删除所有的 Pod ,然后创建新的 Pod ,如果在 k8s-rs.yaml 文件中的 Pod 的镜像写错,那么之前的 Pod 已经没了,不满足滚动更新的原则(新建一批 Pod ,删除一批 Pod ,直到新建的所有 Pod 替换原有的所有 Pod ),所以推荐使用 Deployment 代替 ReplicaSet 。
删除 ReplicaSet
方式一:通过
kubectl delete命令
方式二:通过 yaml 文件
Deployment(deploy)
概述

为了更好的解决服务编排的问题,Kubernetes 在v1.2版本开始,引入了 Deployment 控制器。值得一提的是,Deployment 控制器并不直接管理 Pod,而是通过管理 ReplicaSet 来间接管理 Pod ,即:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod 。所以 Deployment 的功能比 ReplicaSet 强大。

Deployment 的资源清单:
Deployment的更新原理
仅当 Deployment 的 Pod 模板(spec.template)发生改变的时候(如:模板的标签或容器镜像被更新),才会触发 Deployment 上线,其他更新(如:Deployment 的扩缩容等操作)均不会触发上线动作。
上线动作:创建新的 ReplicaSet,准备就绪后,替换旧的 ReplicaSet(此时不会删除,因为 revisionHistoryLimit 指定了保留几个版本)。
创建Deployment
创建 Deployment
扩缩容
方式一:使用
kubectl edit命令,修改spec:replicas:n即可
方式二:使用
scale命令实现扩缩容
方式三:修改 yaml 文件中 spec.replicas 字段,随后使用
kubectl apply -f xxx.yaml命令即可
镜像升级
概述
Deployment 支持两种镜像更新策略:重建更新和滚动更新,可以通过 strategy 字段进行配置:
strategy:指定新的Pod替代旧的Pod的策略,支持两个属性type:指定策略类型,支持两种策略Recreate:在创建出新的Pod之前会先杀掉所有已经存在的Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
rollingUpdate:当type为RollingUpdate的时候生效,用于为rollingUpdate设置参数,支持两个属性maxUnavailable: 用来指定在升级过程中不可用的Pod的最大数量,默认为25%。
maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。
Deployment 的默认的镜像更新策略是 RollingUpdate(滚动更新),实际开发的时候,使用默认镜像更新策略即可。
重建更新
设置镜像更新策略为重建更新
方式一:使用
kubectl set image命令
方式二:修改 yaml 文件,使用
kubectl apply -f xxx.yaml命令即可
滚动更新
设置镜像更新策略为滚动更新
方式一:使用
kubectl set image命令
方式二:修改 yaml 文件,使用
kubectl apply -f xxx.yaml命令即可
版本回退
Deployment 支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能
创建 Deployment
查看版本升级历史记录
镜像升级
查看版本升级历史记录
版本回退
Deployment 之所以能够实现版本的回退,就是通过记录下历史的 ReplicaSet 来实现的,一旦想回滚到那个版本,只需要将当前版本的 Pod 数量降为 0 ,然后将回退版本的 Pod 提升为目标数量即可。
金丝雀发布
概述
滚动更新也是有缺点的:滚动更新短时间就直接结束了,不能直接控制新老版本的存活时间;而金丝雀发布却可以实现这样的功能。

金丝雀的发布流程如下:
将 service 的标签设置为
app=nginx,这就意味着集群中的所有标签是app=nginx的 Pod 都会加入负载均衡网络。使用
Deployment v=v1 app=nginx去筛选标签是app=nginx以及v=v1的 所有 Pod。同理,使用
Deployment v=v2 app=nginx去筛选标签是app=nginx以及v=v2的所有 Pod 。逐渐加大
Deployment v=v2 app=nginx控制的 Pod 的数量,根据轮询负载均衡网络的特点,必定会使得此 Deployment 控制的 Pod 的流量增大。当测试完成后,删除
Deployment v=v1 app=nginx即可。
金丝雀发布
准备 v1 版本的 Deployment
创建 v2 版本的 Deployment
创建 Service
测试:
实际开发中,还需要结合 Jenkins 的 Pipeline

删除Deployment
方式一:通过 kubectl delete 命令
方式二:通过 yaml 文件
HPA
概述
目前已经可以通过手动执行 kubectl scale 命令实现Pod的扩缩容,但是这显然不符合 Kubernetes 的定位目标–自动化和智能化。Kubernetes 期望可以通过监测Pod的使用情况,实现 Pod 数量的自动调整,于是就产生了 HPA 这种控制器。
HPA 可以获取每个 Pod 的利用率,然后和 HPA 中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现 Pod 的数量的调整。其实 HPA 和之前的 Deployment 一样,也属于一种 Kubernetes 资源对象,它通过追踪分析目标Pod的负载变化情况,来确定是否需要针对性的调整目标 Pod 的副本数。

安装metrics-server
获取 metrics-server v0.6.1(网速不行,请点这里📎components.yaml)
修改 components.yaml
安装
查看pod运行情况
使用kubectl top node 查看资源使用情况
HPA 监控CPU使用率
创建 Deployment 和 Service
创建 HPA
测试
HPA 目前也可以监控内存等指标
DaemonSet
概述
DaemonSet 控制器确保所有(或一部分)的节点都运行了一个指定的 Pod 副本,通常用于实现系统级后台任务。比如ELK服务。
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上。
当节点从集群中移除时,Pod 也就被垃圾回收了。
删除一个 DaemonSet 可以清理所有由其创建的 Pod。

DaemonSet 的典型应用场景:
在每个节点上运行集群的存储守护进程,如:glusterd、ceph。
在每个节点上运行日志收集守护进程,如:fluentd、logstash。
在每个节点上运行监控守护进程,如:Prometheus Node Exporter、Sysdig Agent、collectd、Dynatrace OneAgent、APPDynamics Agent、Datadog agent、New Relic agent、Ganglia gmond、Instana Agent 等
DaemonSet 的资源清单:
部署DaemonSet
创建 DaemonSet
删除
更新策略
1.6版本开始支持滚动更新。如如下两种策略
OnDelete : 默认升级策略,在创建好新的DaemonSet配置之后,新的Pod不会被自动创建,用户需要手动删除旧版本的Pod,才触发新建操作。
RollingUpdate: 旧版本的POD 将被自动杀掉,然后自动创建新版的DaemonSet Pod。与Deployment 不同为不支持查看和管理DaemonSet的更新记录;回滚操作是通过再次提交旧版本配置而不是 rollback命令实现
StatefulSet(sts)
概述
Stateful 翻译为 有状态的 。
Deployment 部署的应用称为无状态应用,StatefulSet 部署的应用称为有状态应用。
无状态应用:网络可能会变(IP 地址)、存储可能会变(卷)、顺序可能会变(启动的顺序)。应用场景:业务代码,如:使用 SpringBoot 开发的商城应用的业务代码。
有状态应用:网络不变、存储不变、顺序不变。应用场景:中间件,如:MySQL 集群、Zookeeper 集群、Redis 集群、MQ 集群。
StatefulSet使用场景和限制
对于有如何要求的应用程序,StatefulSet 非常适合。
稳定、唯一的网络标识(dnsname),必须配置 HeadlessService(无头服务):StatefulSet 通过和其相关的无头服务为每个 Pod 提供 DNS 解析条目。
假设无头服务的 DNS 条目为
$(service name).$(namespace).svc.cluster.local,那么 Pod 的解析条目就是$(pod name).$(service name).$(namespace).svc.cluster.local,并且每个 Pod 都是唯一的。

稳定、持久的存储:每个 Pod 始终对应各自的存储路径(PersistantVolumeClaimTemplate)。
有序的、优雅的部署和缩放:按顺序地增加副本、减少副本,并在减少副本时执行清理。
有序的、自动的滚动更新:按顺序自动地执行滚动更新。
限制:
给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
删除或者收缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
StatefulSet 当前需要无头服务来负责 Pod 的网络标识。我们需要负责创建此服务。
当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
在默认Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要人工干预才能修复的损坏状态。
部署StatefulSet
创建 StatefulSet
查看当前创建的pod
新建一个 Pod ,在 Pod 中访问 sts 创建的 Pod 以及无头服务
删除 StatefulSet
Pod的管理策略
StatefulSet 的 Pod 的管理策略(podManagementPolicy)分为:OrderedReady(有序启动,默认值) 和 Parallel(并发一起启动)。
示例
分区更新
StatefulSet 的更新策略:
OnDelete:默认升级策略,在创建好新的DaemonSet配置之后,新的Pod不会被自动创建,用户需要手动删除旧版本的Pod,才触发新建操作。
RollingUpdate:StatefulSet 控制器将删除并重新创建 StatefulSet 中的每个 Pod。它将按照与 Pod 终止相同的顺序进行(从最大的序数到最小的),依次更新每个 Pod。Kubernetes 控制平面会等到更新的 Pod 运行并准备好,然后再更新其前任。如果已设置
.spec.minReadySeconds(请参阅“最小就绪秒数”),则控制平面会在 Pod 准备就绪后额外等待该时间,然后再继续。partitioned:分区滚动更新。如果指定了分区,则在更新 StatefulSet 时,将更新序数大于或等于该分区的所有
Pod.spec.template。所有序号小于分区的 Pod 都不会更新,即使删除了,也会在之前的版本中重新创建。如果 StatefulSet 的.spec.updateStrategy.rollingUpdate.partition大于其.spec.replicas,则对其的更新.spec.template将不会传播到其 Pod。在大多数情况下,您不需要使用分区,但如果想要暂存更新、roll out Canary 或执行分阶段roll out,它们会很有用。
回滚更新: 一旦更新配置有误,即使修改了配置,k8s也不会继续更新,而是等待异常POD恢复至正常。这时需要手动删除异常POD
示例
Job
概述
Job 主要用于负责批量处理短暂的一次性任务。
Job 的特点:
当 Job 创建的 Pod 执行成功结束时,Job 将记录成功结束的 Pod 数量。
当成功结束的 Pod 达到指定的数量时,Job 将完成执行。
删除 Job 对象时,将清理掉由 Job 创建的 Pod 。
Job 可以保证指定数量的 Pod 执行完成。

Job 的资源清单:
关于模板中的重启策略的说明:
如果设置为OnFailure,则Job会在Pod出现故障的时候重启容器,而不是创建Pod,failed次数不变。
如果设置为Never,则Job会在Pod出现故障的时候创建新的Pod,并且故障Pod不会消失,也不会重启,failed次数+1。
如果指定为Always的话,就意味着一直重启,意味着Pod任务会重复执行,这和Job的定义冲突,所以不能设置为Always。
注意:
所有的 Job 类型的 Pod 不需要阻塞式镜像,如:nginx 等。
Job 类型的 Pod 需要运行完成后就停止的镜像,如:alpine、busybox 等。
Deployment 类型的 Pod 需要阻塞式镜像。
部署Job
新建 Job
删除 Job
CronJob
概述
CronJob 控制器以 Job 控制器为其管控对象,并借助它管理 Pod 资源对象,Job 控制器定义的作业任务在其控制器资源创建之后便会立即执行,但 CronJob 可以以类似 Linux 操作系统的周期性任务作业计划的方式控制器运行时间点及重复运行的方式,换言之,CronJob 可以在特定的时间点反复去执行 Job 任务。

CronJob 的资源清单:
schedule:cron表达式,用于指定任务的执行时间。
*/1 * * * *:表示分钟 小时 日 月份 星期。分钟的值从 0 到 59 。
小时的值从 0 到 23 。
日的值从 1 到 31 。
月的值从 1 到 12 。
星期的值从 0 到 6,0 表示星期日。
多个时间可以用逗号隔开,范围可以用连字符给出:* 可以作为通配符,/ 表示每
...
concurrencyPolicy:并发执行策略
Allow:运行 Job 并发运行(默认)。
Forbid:禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行。
Replace:替换,取消当前正在运行的作业并使用新作业替换它。
部署CronJob
新建 CronJob
删除 CronJob
网络
kubernetes网络
概述
Kubernetes 网络解决四个方面的问题:
一个 Pod 中容器之间通过本地回路(loopback)通信。
集群网络在不同 Pod 之间提供通信;换言之,Pod 和 Pod 之间能互相通信(通过 calico 网络插件实现 Pod 之间网络的扁平化;当然,Node 节点之间的通信也是通过 calico 网络插件)。
Service 资源允许我们对外暴露 Pod 中运行的应用程序,以支持来自集群之外的访问;换言之,Service 和 Pod 之间能互相通信。
可以使用 Service 来发布仅供集群内部使用的服务。

kubernetes网络架构图
架构图
http
访问流程

Service
概述
在 Kubernetes 中,Pod 是应用程序的载体,我们可以通过 Pod 的 IP 来访问应用程序,但是 Pod 的 IP 地址不是固定的,这就意味着不方便直接采用 Pod 的 IP 对服务进行访问。
为了解决这个问题,Kubernetes 提供了 Service 资源,Service 会对提供同一个服务的多个 Pod 进行聚合,并且提供一个统一的入口地址,通过访问 Service 的入口地址就能访问到后面的 Pod 服务。

Service 在很多情况下只是一个概念,真正起作用的其实是 kube-proxy 服务进程,每个 Node 节点上都运行了一个 kube-proxy 的服务进程。当创建 Service 的时候会通过 API Server 向 etcd 写入创建的 Service 的信息,而 kube-proxy 会基于监听的机制发现这种 Service 的变化,然后它会将最新的 Service 信息转换为对应的访问规则(其实,就是 EndPoint)。

Service 的资源清单:
spec.type 的说明:
ClusterIP(默认值):通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。
NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求
<节点 IP>:<节点端口>,我们可以从集群的外部访问一个 NodePort 服务。LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
如果想要在内网环境中,使用
type=LoadBalancer就需要部署MetalLB组件。
ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(如:foo.bar.example.com)。 无需创建任何类型代理。
OSI 网络模型和 TCP/IP 协议:

注意:
Service 会基于 Pod 的探针机制(readinessProbe,就绪探针)完成 Pod 的自动剔除(没有就绪的 Pod)和上线工作。
Service 即使是无头服务(Headless Service),其他的 Kubernetes 应用资源(如:Pod 等)不能通过 IP 访问,但是可以将 Service 名字当成域名来访问;非无头服务,更是如此了。
Service 默认使用的协议是 TCP 协议,对应的是 OSI 网络模型中的第四层传输层,所以也有人称 Service 为四层网络负载均衡。
selector 只有在 Service 的 spec.type 是 ClusterIP、NodePort、LoadBalancer 才有效。
准备工作
在使用 Service 之前,首先利用 Deployment 创建出 3 个Pod 。
为了方便测试,修改 Pod 中 Nginx 的 index.html(都修改)
ClusterIP类型的Service
创建Service
新建 k8s-svc.yaml
创建 Service
访问
原理

注意:
curl 10.96.12.53:80是可以访问到 Pod 中的容器。curl 10.96.12.53:6379不可以访问到 Pod 中的容器。将 Kubernetes 中能分配 IP 地址的都看做是一个小型的、轻巧的、虚拟的 Linux 主机,
10.96.12.53:6379是因为我们没有在 service 中开启端口
查看Service的详细信息
语法
示例

EndPoint(端点)
Endpoint 是 Kubernetes 中的一个资源对象,存储在etcd 中,用来记录一个 Service 对应的所有 Pod 的访问地址,它是根据Service配置文件中的 Selector 描述产生的(当然,也可以自定义端点)。
一个 Service 由一组 Pod 组成,这些 Pod 通过 Endpoints 暴露出来,Endpoints 是实现实际服务的端点集合。换言之,Service 和 Pod 之间的联系是通过 Endpoints 实现的。

示例:查询端点信息
示例:删除一个 Pod ,Deployment 会将 Pod 重建,Endpoints 的内容随着 Pod 发生变化。
示例:创建自定义的 EndPoints

鉴于有些人认为像 MySQL 之类的不应该使用 Docker 、k8s 部署,而是应该直接部署在物理机上,那么就可以使用此种方式,将 MySQL 的地址配置在 EndPoint 中,对外暴露一个 Service ,这样 Kubernetes 中的资源(如:Pod)就可以直接使用暴露的 Service 名称来访问,这样可以实时剔除 EndPoint 的信息。换言之,反向代理集群外的服务。
域名解析
进入其中 Pod ,使用 nslookup 命令查询 DNS

总结:网络层次

会话保持技术
基于客户端 IP 地址的会话保持模式,即来自同一个客户端发起的所有请求都尽最大可能转发到固定的一个 Pod 上,只需要在 spec 中添加 sessionAffinity: ClientIP 选项。
示例:
删除Service
命令
HeadLiness类型的Service
所谓的 HeadLiness 类型的 Service ,即所谓的无头服务,即不希望 Kubernetes 集群分配 IP ,针对这种情况,Kubernetes 提供了 HeadLinesss Service,这类 Service 不会分配 Cluster IP,如果想要访问 Service ,只能通过 Service 的域名进行查询,一般配合 StatefulSet 使用。
语法
示例:
NodePort类型的Service
概述
在之前的案例中,创建的 Service 的 IP 地址只能在集群内部才可以访问,如果希望 Service 暴露给集群外部使用,那么就需要使用到另外一种类型的 Service,称为 NodePort 类型的 Service。NodePort 的工作原理就是将 Service 的端口映射到 Node 的一个端口上,然后就可以通过 NodeIP:NodePort 来访问 Service 了。
语法

部署Service
创建 Service
LoadBalancer类型的Service
LoadBalancer 和 NodePort 很相似,目的都是向外部暴露一个端口,区别在于 LoadBalancer 会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境的支持,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。这个功能需要云平台厂商的支持;换言之,加钱吧。

示例:执行也没用
ExternalName类型的Service
ExternalName 类型的 Service 用于引入集群外部的服务,它通过 externalName 属性指定一个服务的地址,然后在集群内部访问此 Service 就可以访问到外部的服务了,但是需要注意目标服务的跨域问题。

示例
使用ExternalName将外部服务映射到内部服务。
Pod指定自己的主机名
Pod 中还可以设置 hostname 和 subdomain 字段,需要注意的是,一旦设置了 hostname ,那么该 Pod 的主机名就被设置为 hostname 的值,而 subdomain 需要和 svc 中的 name 相同。
示例
如果 Pod 和 Pod 之间的访问:Pod 的 hostname.Pod 的 subdomain.名称空间.svc.cluster.local;换言之,nginx1 访问 nginx2 只需要这样:nginx2.default-subdomain.default.svc.cluster.local 即可(默认的名称空间可以省略,也可以这样 nginx2.default-subdomain)。
那么 Pod 和 Service 的访问:Pod 的 subdomain.名称空间.svc.cluster.local。
Ingress
为什么需要Ingress
Service 可以使用 NodePort 暴露集群外访问端口,但是性能低、不安全并且端口的范围有限。
Service 缺少七层(OSI 网络模型)的统一访问入口,负载均衡能力很低,不能做限流、验证非法用户、链路追踪等等。
Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。流量路由由 Ingress 资源上定义的规则控制。
使用 Ingress 作为整个集群统一的入口,配置 Ingress 规则转发到对应的 Service 。
Ingress nginx和nginx ingress
nginx Ingress(不用)
nginx Ingress是 Nginx 官网适配 k8s 的,分为开源版和 nginx plus 版(收费)。
官网地址。

ingress nginx
ingress nginx 是 k8s 官方出品的,会及时更新一些特性,性能很高,被广泛采用。
官网地址。
注意:以后如果不特别说明,ingress = ingress nginx 。
Ingress 安装
自建集群采用裸金属安装方式。
下载
需要做如下修改:
修改
k8s.gcr.io/ingress-nginx/controller:v1.1.2镜像为registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.1.2。修改
k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1镜像为registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1。修改 Service 为 ClusterIP,无需 NodePort 模式。
修改 Deployment 为 DaemonSet 。
修改 Container 使用主机网络,直接在主机上开辟 80 、443 端口,无需中间解析,速度更快,所有各个节点的 80 和 443 端口不能被其它进程占用。

image-20230419230521662 containers 使用主机网络,对应的 dnsPolicy 策略也需要改为主机网络的。
修改 DaemonSet 的
nodeSelector: ingress-node=true。这样只需要给 node 节点打上ingress-node=true标签,即可快速的加入或剔除 ingress-controller的数量。

image-20230419230623625 创建文件
deploy.yaml 修改的内容如下
给 Node 节点打标签
当然,也可以给 Master 节点打标签,但是 kubeadm 安装 k8s 集群的时候,会给 Master 节点打上污点,即使打上标签,也不会进行 Pod 的调度;换言之,Ingress 也不会在 Master 节点上安装。
安装 Ingress (需要关闭 Node 节点的 80 和 443 端口,不能有其他进程占用)
验证是否安装成功:只需要在部署了 ingress 的主机,执行如下的命令

卸载
Ingress 原理

工作原理:
用户编写 Ingress 规则,说明哪个域名对应 k8s 集群中的哪个 Service。
Ingress 控制器动态感知 Ingress 服务规则的变化,然后生成一段对应的 Nginx 的反向代理配置。
Ingress 控制器会将生成的 Nginx 配置写入到一个运行着的 Nginx 服务中,并动态更新。
准备工作(一)
为了后面的实验比较方便,创建如下图所示的模型

创建实验所需的 Service 以及 Pod 等
准备工作(二)
基本配置
语法
pathType 说明:
Prefix:基于以
/分隔的 URL 路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由/分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。Exact:精确匹配 URL 路径,且区分大小写。
ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者与 Prefix 或 Exact 类型作相同处理
注意:ingress 规则会生效到所有安装了 IngressController 的机器的 nginx 配置。
示例
如果出现x509: certificate signed by unknown authority错误,则需要先执行kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission。
示例的示意图

测试示例(后面有更简单的测试方法):
第一种方案:
在 win 中的 hosts(C:\Windows\System32\drivers\etc\hosts) 文件中添加如下的信息
在 win 中使用 curl 命令

第二种方案:
重新建立一个虚拟机(安装有 CentOS7 系统,并且带有桌面),在 hosts (
/etc/hosts)文件中添加如下的内容:
通过浏览器访问:nginx.angle.com

证明:ingress 规则会生效到所有安装了 IngressController 的机器的 nginx 配置
首先进入到容器nginx容器内部,然后执行如下:

默认后端
示例
效果:
tomcat.com 域名的 非
/abc开头的请求,都会转到 defaultBackend 。非 tomcat.com 域名下的所有请求,也会转到 defaultBackend 。
Ingress中的nginx的全局配置
官方文档。
方式一:在安装的时候,添加配置。
方式二:编辑 cm
限流
官方文档。
示例:
路径重写
官方文档。
路径重写,经常用于前后端分离。

示例
$n代表/分割的第n个path。
基于Cookie的会话保持技术
Service 只能基于 ClientIP,但是 ingress 是七层负载均衡,可以基于 Cookie 实现会话保持。
示例
配置SSL
官方地址。
生成证书语法
示例:生成证书
示例
测试

注意:实际开发的时候,需要自己购买证书。
Ingress 金丝雀发布
概述
以前使用 Kubernetes 的 Service 配合 Deployment 进行金丝雀的部署,原理如下所示:

但是,也是有缺点的,就是不能自定义灰度逻辑,如:指定用户进行灰度(新功能只让 VIP 先体验一个月,一个月之后再将新功能解锁,让所有用户都体验到)。
Ingress 金丝雀发布的原理如下所示:

准备工作
部署 Service 和 Deployment
部署普通的 ingress
测试 ingress
基于 Header 的流量切分
示例
测试
基于 Cookie 的流量切分
示例
基于服务权重的流量切分
示例
Network Policy(网络策略)
概述
网络策略指的是 Pod 间的网络隔离策略,默认情况下是互通的。
Pod 之间的互通是通过如下三个标识符的组合来辨识的:
其他被允许的 Pod(例外:Pod 无法阻塞对自身的访问)。
被允许的名称空间。
IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)。

Pod 隔离和非隔离
默认情况下,Pod 都是非隔离的(non-isolated),可以接受来自任何请求方的网络请求。
如果一个 NetworkPolicy 的标签选择器选中了某个 Pod,则该 Pod 将变成隔离的(isolated),并将拒绝任何不被 NetworkPolicy 许可的网络连接。
示例
场景
官方文档。
配置与存储
概述
简介
容器的生命周期可能很短,会被频繁的创建和销毁。那么容器在销毁的时候,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器中的数据,Kubernetes 引入了 Volume 的概念。和 Docker 中的卷管理(匿名卷、具名卷、自定义挂载目录,都是挂载在本机,功能非常有限)不同的是,Kubernetes 天生就是集群,所以为了方便管理,Kubernetes 将 卷 抽取为一个对象资源,这样可以更方便的管理和存储数据。
Volume 是 Pod 中能够被多个容器访问的共享目录,它被定义在 Pod 上,然后被一个 Pod 里面的多个容器挂载到具体的文件目录下,Kubernetes 通过 Volume 实现同一个 Pod 中不同容器之间的数据共享以及数据的持久化存储。Volume 的生命周期不和 Pod 中的单个容器的生命周期有关,当容器终止或者重启的时候,Volume 中的数据也不会丢失。

Kubernetes 支持的 Volume 类型
简版
Kubernetes 的 Volume 支持多种类型,如下图所示

细分类型
Kubernetes 目前支持多达 28 种数据卷类型(其中大部分特定于具体的云环境如 GCE/AWS/Azure 等)
非持久性存储:
emptyDir
HostPath
网络连接性存储:
SAN:iSCSI、ScaleIO Volumes、FC (Fibre Channel)
NFS:nfs,cfs
分布式存储
Glusterfs
RBD (Ceph Block Device)
CephFS
Portworx Volumes
Quobyte Volumes
云端存储
GCEPersistentDisk
AWSElasticBlockStore
AzureFile
AzureDisk
Cinder (OpenStack block storage)
VsphereVolume
StorageOS
自定义存储:FlexVolume
扩展阅读:
配置
配置最佳实战
云原生应用的 12 要素中,提出了配置分离。
在推送到集群之前,配置文件应该存储在版本控制系统中。这将允许我们在必要的时候快速回滚配置更改,它有助于集群重新创建和恢复。
使用 YAML 而不是 JSON 编写配置文件,虽然这些格式几乎可以在所有场景中互换使用,但是 YAML 往往更加用户友好。
建议相关对象分组到一个文件,一个文件通常比几个文件更容易管理。请参阅 guestbook-all-in-one.yaml 文件作为此语法的示例。
除非必要,否则不指定默认值(简单的最小配置会降低错误的可能性)。
将对象描述放在注释中,以便更好的内省。
Secret
概述
Secret 对象类型用来保存敏感信息,如:密码、OAuth2 令牌以及 SSH 密钥等。将这些信息放到 Secret 中比放在 Pod 的定义或者容器镜像中更加安全和灵活。
由于创建 Secret 可以独立于使用它们的 Pod, 因此在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险较小。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施,如:避免将机密数据写入非易失性存储。
Secret 类似于 ConfigMap 但专门用于保存机密数据。
注意:Secret 和 ConfigMap 是保存在 etcd 中。
Secret的种类
命令行
细分类型
Opaque
用户定义的任意数据
kubernetes.io/service-account-token
服务账号令牌
kubernetes.io/dockercfg
~/.dockercfg 文件的序列化形式
kubernetes.io/dockerconfigjson
~/.docker/config.json 文件的序列化形式
kubernetes.io/basic-auth
用于基本身份认证的凭据
kubernetes.io/ssh-auth
用于 SSH 身份认证的凭据
kubernetes.io/tls
用于 TLS 客户端或者服务器端的数据
bootstrap.kubernetes.io/token
启动引导令牌数据
Pod 如何引用
要使用 Secret,Pod 需要引用 Secret。 Pod 可以用三种方式之一来使用 Secret :
作为容器的环境变量(envFrom字段引用)
由 kubelet 在为 Pod 拉取镜像时使用(此时Secret是docker-registry类型的)
Secret 对象的名称必须是合法的 DNS 子域名。 在为创建 Secret 编写配置文件时,你可以设置 data 与/或 stringData 字段。 data 和 stringData 字段都是可选的。data 字段中所有键值都必须是 base64 编码的字符串。如果不希望执行这种 base64 字符串的转换操作,你可以选择设置 stringData 字段,其中可以使用任何字符串作为其取值。
创建 Secret
命令行创建 Secret

使用 base64 对数据进行编码
使用 yaml 格式创建 Secret
有的时候,觉得手动将数据编码,再配置到 yaml 文件的方式太繁琐,那么也可以将数据编码交给 Kubernetes
注意:如果同时使用 data 和 stringData ,那么 data 会被忽略。
根据文件创建 Secret
注意:密钥 的 Key 默认是文件名的名称。
根据文件创建 Secret(自定义密钥 的 Key )
查看 Secret
示例:以 JOSN 的形式提取 data
示例:以 yaml 的格式
使用 Secret——环境变量引用
示例
注意:环境变量引用的方式不会被自动更新。
使用 Secret——卷挂载
示例
ConfigMap
概述
ConfigMap 和 Secret 非常类似,只不过 Secret 会将信息进行 base64 编码和解码,而 ConfigMap 却不会。
创建 ConfigMap、环境变量引用、卷挂载
示例
注意:
ConfigMap 和 Secret 一样,环境变量引用不会热更新,而卷挂载是可以热更新的。
最新版本的 ConfigMap 和 Secret 提供了不可更改的功能,即禁止热更新,只需要在 Secret 或 ConfigMap 中设置
immutable = true。
ConfigMap 结合 SpringBoot 做到生产配置无感知

临时存储
概述
emptyDir、configMap、downwardAPI、secret 是作为 本地临时存储 提供的。它们由各个节点上的 kubelet 管理。
CSI 临时卷 必须 由第三方 CSI 存储驱动程序提供。
通用临时卷 可以 由第三方 CSI 存储驱动程序提供,也可以由支持动态配置的任何其他存储驱动程序提供。 一些专门为 CSI 临时卷编写的 CSI 驱动程序,不支持动态供应:因此这些驱动程序不能用于通用临时卷。
使用第三方驱动程序的优势在于,它们可以提供 Kubernetes 本身不支持的功能, 例如,与 kubelet 管理的磁盘具有不同运行特征的存储,或者用来注入不同的数据。
emptyDir
Pod 分派到某个 Node 上时,emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。
就像其名称表示的那样,卷最初是空的。
尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。
当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。
注意: 容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。
emptyDir的一些用途:临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
emptyDir卷存储在该节点的磁盘或内存中,如果设置emptyDir.medium = Memory,那么就告诉 Kubernetes 将数据保存在内存中,并且在 Pod 被重启或删除前,所写入的所有文件都会计入容器的内存消耗,受到容器内存限制约束。

示例
hostPath
emptyDir 中的数据不会被持久化,它会随着 Pod 的结束而销毁,如果想要简单的将数据持久化到主机中,可以选择 hostPath 。
hostPath 就是将 Node 主机中的一个实际目录挂载到 Pod 中,以供容器使用,这样的设计就可以保证 Pod 销毁了,但是数据依旧可以保存在 Node 主机上。
注意:hostPath 之所以被归为临时存储,是因为实际开发中,一般都是通过 Deployment 部署 Pod 的,一旦 Pod 被 Kubernetes 杀死或重启拉起等,并不一定会部署到原来的 Node 节点中。
除了必需的
path属性之外,用户可以选择性地为hostPath卷指定type。
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate
如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory
在给定路径上必须存在的目录。
FileOrCreate
如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File
在给定路径上必须存在的文件。
Socket
在给定路径上必须存在的 UNIX 套接字。
CharDevice
在给定路径上必须存在的字符设备。
BlockDevice
在给定路径上必须存在的块设备。
hostPath 的典型应用就是时间同步:通常而言,Node 节点的时间是同步的(云厂商提供的云服务器的时间都是同步的),但是,Pod 中的容器的时间就不一定了,有 UTC 、CST 等;同一个 Pod ,如果部署到中国,就必须设置为 CST 了。
示例
持久化存储
VOLUME
基础
Kubernetes 支持很多类型的卷。Pod 可以同时使用任意数目的卷类型。
临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。
当 Pod 不再存在时,Kubernetes 也会销毁临时卷。
Kubernetes 不会销毁 持久卷 。
对于给定 Pod 中 任何类型的卷 ,在容器重启期间数据都不会丢失。
使用卷时,在
.spec.volumes字段中设置为 Pod 提供的卷,并在.spec.containers[*].volumeMounts字段中声明卷在容器中的挂载位置。
subPath
有时,在单个 Pod 中共享卷以供多方使用是很有用的。 volumeMounts.subPath 属性可用于指定所引用的卷内的子路径,而不是其根路径。
注意:ConfigMap 和 Secret 使用子路径挂载是无法热更新的。
安装NFS
NFS 的简介:网络文件系统,英文Network File System(NFS),是由 SUN 公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样。

注意:实际开发中,不建议使用 NFS 作为 Kubernetes 集群持久化的驱动。
本次以 Master (192.168.183.101)节点作为 NFS 服务端
在 Master(192.168.183.101)节点创建 /etc/exports 文件
在 Master(192.168.183.101)节点创建
/nfs/data/(共享目录)目录,并设置权限
在 Master(192.168.183.101)节点启动 NFS
在 Master(192.168.183.101)节点加载配置
在 Master(192.168.183.101)节点检查配置是否生效

在 Node(192.168.183.102、192.168.183.103)节点安装 nfs-utils
在 Node(192.168.183.102、192.168.183.103)节点,执行以下命令检查 nfs 服务器端是否有设置共享目录

在 Node(192.168.183.102、192.168.183.103)节点,执行以下命令挂载 nfs 服务器上的共享目录到本机路径
/root/nd
在 Node (192.168.183.102)节点写入一个测试文件
在 Master(192.168.183.101)节点验证文件是否写入成功
示例
PV和PVC
概述
前面已经学习了使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 NFS,这就带来的一些问题:
开发人员对 Pod 很熟悉,非常清楚 Pod 中的容器那些位置适合挂载出去。但是,由于 Kubernetes 支持的存储系统非常之多,开发人员并不清楚底层的存储系统,而且要求开发人员全部熟悉,不太可能(术业有专攻,运维人员比较熟悉存储系统)。
在 yaml 中配置存储系统,就意味着将存储系统的配置信息暴露,非常不安全(容易造成泄露)。
为了能够屏蔽底层存储实现的细节,方便用户使用,Kubernetes 引入了 PV 和 PVC 两种资源对象。

PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 Kubernetes 管理员进行创建和配置,它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接。
PVC(Persistent Volume Claim)是持久化卷声明的意思,是用户对于存储需求的一种声明。换言之,PVC 其实就是用户向Kubernetes 系统发出的一种资源需求申请。
PV 的缺点:
需要运维事先准备好 PV 池。
资源浪费:没有办法预估合适的 PV,假设运维向 k8s 申请了 20m 、50m、10G 的 PV,而开发人员申请 2G 的 PVC ,那么就会匹配到 10G 的PV ,这样会造成 8G 的空间浪费。 也有人称 PV 为静态供应。
PVC和PV的基本演示
创建 PV (一般是运维人员操作)
创建 PVC (一般是开发人员)
注意:
pv 和 pvc 的 accessModes 和 storageClassName 必须一致。
pvc 申请的空间大小不大于 pv 的空间大小。
storageClassName 就相当于分组的组名,通过 storageClassName 可以区分不同类型的存储驱动,主要是为了方便管理。

注意:
Pod 的删除,并不会影响 PVC;换言之,PVC 可以独立于 Pod 存在PVC 也,是 K8s 的系统资源。不过,推荐将 PVC 和 Pod 也在一个 yaml 文件中。
PVC 删除会不会影响到 PV,要根据 PV 的回收策略决定。
集群中的Volume Controller发现这个PVC后,就会主动在集群中寻找合适的PV,来和PVC绑定。只有和PV绑定了的PVC,才能被pod正常挂载使用。Volume Controller寻找PV的条件主要是:
(1)PVC和PV的spec字段中指定的规格,例如存储(storage)的大小;
(2)PVC和PV的storageClassName必须一样。(这里的storage)
PV的回收策略
目前的回收策略有:
Retain:手动回收(默认)。
Recycle:基本擦除 (
rm -rf /thevolume/*)。Delete:诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除。
目前,仅 NFS 和 HostPath 支持回收(Recycle)。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete)。
示例:演示 Retain
示例:演示 Recycle
PV的声明周期
一个 PV 的生命周期,可能会处于 4 种不同的阶段:
Available(可用):表示可用状态,还未被任何 PVC 绑定。
Bound(已绑定):表示 PV 已经被 PVC 绑定。
Released(已释放):表示 PVC 被删除,但是资源还没有被集群重新释放。
Failed(失败):表示该 PV 的自动回收失败。
PV的访问模式
访问模式(accessModes):用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载。
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
生命周期

PVC 和 PV 是一一对应的,PV 和 PVC 之间的相互作用遵循如下的生命周期:
资源供应:管理员手动创建底层存储和 PV。
资源绑定:
用户创建 PVC ,Kubernetes 负责根据 PVC 声明去寻找 PV ,并绑定在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在以存在的 PV 中选择一个满足条件的。
一旦找到,就将该 PV 和用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了。
如果找不到,PVC 就会无限期的处于 Pending 状态,直到系统管理员创建一个符合其要求的 PV 。
PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再和其他的 PVC 进行绑定了。
资源使用:用户可以在 Pod 中像 Volume 一样使用 PVC ,Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用。
资源释放:
用户删除 PVC 来释放 PV 。
当存储资源使用完毕后,用户可以删除 PVC,和该 PVC 绑定的 PV 将会标记为 已释放 ,但是还不能立刻和其他的 PVC 进行绑定。通过之前 PVC 写入的数据可能还留在存储设备上,只有在清除之后该 PV 才能再次使用。
资源回收:
Kubernetes 根据 PV 设置的回收策略进行资源的回收。
对于 PV,管理员可以设定回收策略,用于设置与之绑定的 PVC 释放资源之后如何处理遗留数据的问题。只有 PV 的存储空间完成回收,才能供新的 PVC 绑定和使用。
动态供应
概述
静态供应:集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息,并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
动态供应:集群自动根据 PVC 创建出对应 PV 进行使用。

动态供应的完整流程

集群管理员预先创建存储类(StorageClass)。
用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim)。
存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume)。
系统读取存储类的信息。
系统基于存储类的信息,在后台自动创建 PVC 需要的 PV 。
用户创建一个使用 PVC 的 Pod 。
Pod 中的应用通过 PVC 进行数据的持久化。
PVC 使用 PV 进行数据的最终持久化处理。
设置NFS动态供应
注意:不一定需要设置 NFS 动态供应,可以直接使用云厂商提供的 StorageClass 。
官网地址。
部署 NFS 动态供应
示例:测试动态供应
设置SC为默认驱动
命令
设置 SC 为默认驱动
示例:测试默认驱动
展望
目前,只需要运维人员部署好各种 storageclass,开发人员在使用的时候,创建 PVC 即可;但是,存储系统太多太多,运维人员也未必会一一掌握,此时就需要 Rook 来统一管理了。

调度原理
ResourceQuotas(资源配额)
简介
当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量 。
资源配额是帮助管理员解决这一问题的工具。 资源配额,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命令空间中的 Pod 可以使用的计算资源的总上限。
资源配额的工作方式如下:
不同的团队可以在不同的命名空间下工作,目前这是非约束性的,在未来的版本中可能会通过 ACL (Access Control List 访问控制列表) 来实现强制性约束。
集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况,以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
如果命名空间下的计算资源 (如 cpu 和 memory)的配额被启用,则用户必须为 这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示:可使用 LimitRanger 准入控制器来为没有设置计算资源需求的 Pod 设置默认值。
计算资源配额
用户可以对给定命名空间下的可被请求的 计算资源 总量进行限制。
limits.cpu
所有非终止状态的 Pod,其 CPU 限额总量不能超过该值。
limits.memory
所有非终止状态的 Pod,其内存限额总量不能超过该值。
requests.cpu
所有非终止状态的 Pod,其 CPU 需求总量不能超过该值。
requests.memory
所有非终止状态的 Pod,其内存需求总量不能超过该值。
hugepages-<size>
对于所有非终止状态的 Pod,针对指定尺寸的巨页请求总数不能超过此值。
cpu
与 requests.cpu 相同。
memory
与 requests.memory 相同。
示例
扩展资源的资源配额
除上述资源外,在 Kubernetes 1.10 版本中,还添加了对 扩展资源 的支持。
由于扩展资源不可超量分配,因此没有必要在配额中为同一扩展资源同时指定 requests 和 limits。 对于扩展资源而言,目前仅允许使用前缀为 requests. 的配额项。
以 GPU 拓展资源为例,如果资源名称为 nvidia.com/gpu,并且要将命名空间中请求的 GPU 资源总数限制为 4,则可以如下定义配额:
有关更多详细信息,请参阅查看和设置配额。
存储资源配额
用户可以对给定命名空间下的 存储资源 总量进行限制。
此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。
requests.storage
所有 PVC,存储资源的需求总量不能超过该值。
persistentvolumeclaims
在该命名空间中所允许的 PVC 总量。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage
在所有与 <storage-class-name> 相关的持久卷申领中,存储请求的总和不能超过该值。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims
在与 storage-class-name 相关的所有持久卷申领中,命名空间中可以存在的持久卷申领总数。
例如,如果一个操作人员针对 gold 存储类型与 bronze 存储类型设置配额, 操作人员可以定义如下配额:
gold.storageclass.storage.k8s.io/requests.storage: 500Gibronze.storageclass.storage.k8s.io/requests.storage: 100Gi
在 Kubernetes 1.8 版本中,本地临时存储的配额支持已经是 Alpha 功能:
requests.ephemeral-storage
在命名空间的所有 Pod 中,本地临时存储请求的总和不能超过此值。
limits.ephemeral-storage
在命名空间的所有 Pod 中,本地临时存储限制值的总和不能超过此值。
ephemeral-storage
与 requests.ephemeral-storage 相同。
注意:如果所使用的是 CRI 容器运行时,容器日志会被计入临时存储配额。 这可能会导致存储配额耗尽的 Pods 被意外地驱逐出节点。 参考日志架构 了解详细信息。
对象数量配额
你可以使用以下语法对所有标准的、命名空间域的资源类型进行配额设置:
count/<resource>.<group>:用于非核心(core)组的资源。count/<resource>:用于核心组的资源。
这是用户可能希望利用对象计数配额来管理的一组资源示例。
count/persistentvolumeclaims
count/services
count/secrets
count/configmaps
count/replicationcontrollers
count/deployments.apps
count/replicasets.apps
count/statefulsets.apps
count/jobs.batch
count/cronjobs.batch
相同语法也可用于自定义资源。 例如,要对 example.com API 组中的自定义资源 widgets 设置配额,请使用 count/widgets.example.com。
当使用 count/* 资源配额时,如果对象存在于服务器存储中,则会根据配额管理资源。 这些类型的配额有助于防止存储资源耗尽。例如,用户可能想根据服务器的存储能力来对服务器中 Secret 的数量进行配额限制。 集群中存在过多的 Secret 实际上会导致服务器和控制器无法启动。 用户可以选择对 Job 进行配额管理,以防止配置不当的 CronJob 在某命名空间中创建太多 Job 而导致集群拒绝服务。
对有限的一组资源上实施一般性的对象数量配额也是可能的。 此外,还可以进一步按资源的类型设置其配额。
configmaps
在该命名空间中允许存在的 ConfigMap 总数上限。
persistentvolumeclaims
在该命名空间中允许存在的 PVC 的总数上限。
pods
在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。
replicationcontrollers
在该命名空间中允许存在的 ReplicationController 总数上限。
resourcequotas
在该命名空间中允许存在的 ResourceQuota 总数上限。
services
在该命名空间中允许存在的 Service 总数上限。
services.loadbalancers
在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。
services.nodeports
在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。
secrets
在该命名空间中允许存在的 Secret 总数上限。
例如,pods 配额统计某个命名空间中所创建的、非终止状态的 Pod 个数并确保其不超过某上限值。 用户可能希望在某命名空间中设置 pods 配额,以避免有用户创建很多小的 Pod, 从而耗尽集群所能提供的 Pod IP 地址。
示例
基于优先级类(PriorityClass)来设置资源配额
Pod 可以创建为特定的优先级。 通过使用配额规约中的 scopeSelector 字段,用户可以根据 Pod 的优先级控制其系统资源消耗。
仅当配额规范中的 scopeSelector 字段选择到某 Pod 时,配额机制才会匹配和计量 Pod 的资源消耗。
如果配额对象通过 scopeSelector 字段设置其作用域为优先级类,则配额对象只能跟踪以下资源:
pods
cpu
memory
ephemeral-storage
limits.cpu
limits.memory
limits.ephemeral-storage
requests.cpu
requests.memory
requests.ephemeral-storage
示例:创建一个配额对象,并将其与具有特定优先级的 Pod 进行匹配
LimitRange(限制范围)
简介
默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。
使用资源配额,集群管理员可以以名字空间为单位,限制其资源的使用与创建。
在命名空间中,一个 Pod 或 Container 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。
有人担心,一个 Pod 或 Container 会垄断所有可用的资源。 LimitRange 是在命名空间内限制资源分配(给多个 Pod 或 Container)的策略对象,例如:资源额度 ResourceQuotas 设置为 requests.cpu 为 1 ,requests.memory 为 1Gi ,但是有人的 Pod 直接设置为 requests.cpu 为 1,requests.memory 为 1Gi,那么其他人就不能再创建 Pod 了,所以需要使用 LimitRange 对资源配额设置区间(最小和最大)。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
实战
示例:为命名空间配置 CPU 最小和最大约束
示例:配置命名空间的最小和最大内存约束
示例:为命名空间配置默认的 CPU 请求和限制
示例:为命名空间配置默认的内存请求和限制
示例:限制存储消耗
调度原理
Pod调度方式
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度【强制策略】
NodeName
这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。
亲和性调度
上述介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了它的使用场景。
基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
NodeAffinity
关系符的使用说明
NodeAffinity规则设置的注意事项:
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功
如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化
PodAffinity
首先来看一下PodAffinity的可配置项:
topologyKey 用于指定调度时作用域,例如:
如果指定为
kubernetes.io/hostname,那就是以Node节点为区分范围如果指定为
beta.kubernetes.io/os,则以Node节点的操作系统类型来区分
PodAntiAffinity
PodAntiAffinity主要实现以运行的Pod为参照,新创建的Pod跟参照pod不在一个区域中的功能。与上面的PodAffinity是一种相反策略。
污点(Taints)
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否允许Pod调度过来。
Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可调度
NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod
NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离

污点API
接下来,演示下污点的效果:
为node设置污点(PreferNoSchedule)
创建pod1
查看pod,运行成功
取消污点PreferNoSchedule,创建NoSchedule污点
创建pod2
为node设置污点(职消NoSchedule,设置NoExecute)
创建pod3
小提示: 使用kubeadn搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上
容忍 (Toleration)
污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,Pod通过容忍忽略拒绝。
查看相应配置
安全
访问控制概述
概述
用户使用 kubectl、客户端库或构造 REST 请求来访问 Kubernetes API。 人类用户和 Kubernetes 服务账户都可以被鉴权访问 API。 当请求到达 API 时,它会经历多个阶段,如下图所示

客户端
在 Kubernetes 集群中,客户端通常由两类:
User Account:一般是独立于 Kubernetes 之外的其他服务管理的用户账号。
Service Account:Kubernetes 管理的账号,用于为Pod的服务进程在访问 Kubernetes 时提供身份标识。
认证、授权和准入控制
api-server 是访问和管理资源对象的唯一入口。任何一个请求访问 api-server,都要经过下面的三个流程:
Authentication(认证):身份鉴别,只有正确的账号才能通过认证。
Authorization(授权):判断用户是否有权限对访问的资源执行特定的动作。
Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。
权限流程控制
用户携带令牌或者证书给 Kubernetes 的 api-server 发送请求,要求修改集群资源。
Kubernetes 开始认证。
Kubernetes 认证通过之后,会查询用户的授权(有哪些权限)。
用户执行操作的过程中(操作 CPU、内存、硬盘、网络……),利用准入控制来判断是否可以执行这样的操作。
认证管理
Kubernetes 的客户端身份认证方式
Kubernetes 集群安全的关键点在于如何识别并认证客户端身份,它提供了 3 种客户端身份认证方式:
HTTP Base 认证:
通过 用户名+密码 的方式进行认证。
这种方式是把 用户名:密码 用 BASE64 算法进行编码后的字符串放在 HTTP 请求中的 Header 的 Authorization 域里面发送给服务端。服务端收到后进行解码,获取用户名和密码,然后进行用户身份认证的过程。
HTTP Token 认证:
通过一个 Token 来识别合法用户。
这种认证方式是用一个很长的难以被模仿的字符串--Token 来表明客户端身份的一种方式。每个 Token 对应一个用户名,当客户端发起 API 调用请求的时候,需要在 HTTP 的 Header 中放入 Token,API Server 接受到 Token 后会和服务器中保存的 Token 进行比对,然后进行用户身份认证的过程。
HTTPS 证书认证:
基于 CA 根证书签名的双向数字证书认证方式。
这种认证方式是安全性最高的一种方式,但是同时也是操作起来最麻烦的一种方式。
高可用集群
进阶篇
基于kubeadm安装k8s
环境规划
Master
192.168.183.101
CentOS 7.9,基础设施服务器
2核CPU,4G内存,50G硬盘
k8s-master
Node1
192.168.183.102
CentOS 7.9,基础设施服务器
2核CPU,2G内存,50G硬盘
k8s-node1
Node2
192.168.183.103
CentOS 7.9,基础设施服务器
2核CPU,2G内存,50G硬盘
k8s-node2
搭建流程
准备 3 台机器,要求网络互通(如果是云服务器,要求私网互通;如果是虚拟机,要求网络互通)。
在 3 台机器上安装 Docker 容器化环境。
安装 Kubernetes :
3 台机器安装核心组件:kubeadm(创建集群的引导工具)、kubelet 、kubectl(程序员使用的命令行)。
kubelet 可以直接通过容器化的方式创建出 Kubernetes 的核心组件,如:Controller Manager、Scheduler 等。
由 kubeadm 引导创建 Kubernetes 集群。
Kubernetes 和 Docker 之间的版本对应关系
对应关系:Docker 的版本是 v20.10 ,对应的 Kubernetes 的版本是 v1.21 。
准备工作
关闭&禁用防火墙
升级系统内核
查看当前系统的版本
看当前系统的内核
查看启动顺序
查看可用内核版本及启动顺序
在 CentOS 7.x 上启用 ELRepo 仓库
提升下载速度,换源
sed -i "s/mirrorlist=/#mirrorlist=/g" /etc/yum.repos.d/elrepo.repo
sed -i "s#elrepo.org/linux#mirrors.tuna.tsinghua.edu.cn/elrepo#g" /etc/yum.repos.d/elrepo.repo
查看可用的系统内核相关包
安装最新主线内核版本
设置默认的内核版本
或者使用如下命令
重新创建内核配置
重启系统
查看当前系统的内核
设置主机名
主机名解析
为了方便后面集群节点间的直接调用,需要配置一下主机名解析。
时间同步
Kubernetes 要求集群中的节点时间必须精确一致,所以在每个节点上添加时间同步
关闭 SELinux
查看 SELinux 是否开启

永久关闭 SELinux ,需要重启
关闭当前会话的 SELinux ,重启之后无效
关闭 swap 分区
永久关闭 swap ,需要重启
关闭当前会话的 swap ,重启之后无效
将桥接的 IPv4 流量传递到 iptables 的链
修改 /etc/sysctl.conf 文件
可能没有,追加
加载 br_netfilter 模块
持久化修改(保留配置包本地文件,重启系统或服务进程仍然有效)
开启 ipvs
在 Kubernetes 中 service 有两种代理模型,一种是基于 iptables ,另一种是基于 ipvs 的。ipvs 的性能要高于 iptables 的,但是如果要使用它,需要手动载入 ipvs 模块。
在三台机器安装 ipset 和 ipvsadm
在三台机器执行如下脚本
授权、运行、检查是否加载
重启
重启三台机器
Docker 安装
三台机器上都安装 Docker 。
卸载旧版本
yum 安装 gcc 相关
安装所需要的软件包
设置 stable 镜像仓库
更新 yum 软件包索引
查看存储库中 Docker 的版本
安装指定版本的 Docker(v20.10)
启动 Docker
设置为开机自启动
验证 Docker 是否安装成功
阿里云镜像加速
添加阿里云的 Kubernetes 的 YUM 源
由于 Kubernetes 的镜像源在国外,非常慢,这里切换成国内的阿里云镜像源(三台机器均需执行下面命令)
安装 kubelet 、kubeadm 和 kubectl
安装组件
为了实现 Docker 使用的 cgroup drvier 和 kubelet 使用的 cgroup drver 一致,建议修改 /etc/sysconfig/kubelet 文件的内容
注意,docker的cgroup驱动程序默认设置为system。默认情况下Kubernetes cgroup为systemd,我们需要更改Docker cgroup驱动。
设置为开机自启动即可,由于没有生成配置文件,集群初始化后自动启动
查看 Kubernetes 安装所需镜像
查看 Kubernetes 安装所需镜像

下载 Kubernetes 安装所需镜像
三台机器下载镜像
给 coredns 镜像重新打 tag
也可以使用如下的命令快速安装
部署 Kubernetes 的 Master 节点
在 192.168.183.101 机器上部署 Kubernetes 的 Master 节点
注意:
apiserver-advertise-address 一定要是主机的 IP 地址。
apiserver-advertise-address 、service-cidr 和 pod-network-cidr 不能在同一个网络范围内。
不要使用 172.17.0.1/16 网段范围,因为这是 Docker 默认使用的。
等待运行完毕,会出现如下日志:
根据日志提示操作,在 192.168.183.101 执行如下命令:
如果是 root 用户,还可以执行如下命令
默认的 token 有效期为 24 小时,当过期之后,该 token 就不能用了,这时可以使用如下的命令创建 token
或者生成一个永不过期的token
打印日志如下:
部署 Kubernetes 的 Node节点
根据日志提示操作,在 192.168.183.102 和 192.168.183.103 执行如下命令

部署网络插件
Kubernetes 支持多种网络插件,比如 flannel、calico、canal 等,任选一种即可,本次选择 calico(在 192.168.183.101 节点上执行,网络不行,请点这里 📎calico.yaml。
查看部署 CNI 网络插件进度
查看节点状态
在 Master(192.168.183.101)节点上查看节点状态

设置 kube-proxy 的 ipvs 模式
在 Master(192.168.183.101)节点设置 kube-proxy 的 ipvs 模式
删除 kube-proxy ,让 Kubernetes 集群自动创建新的 kube-proxy

让 Node 节点也能使用 kubectl 命令
默认情况下,只有 Master 节点才有 kubectl 命令,但是有些时候也希望在 Node 节点上执行 kubectl 命令
Kubernetes 安装 Nginx
部署 Nginx

暴露接口
查看服务状态

自动补全
常见操作
新增节点
删除节点
集群迁移
备份数据:在进行任何迁移操作之前,请先备份您的 Kubernetes 集群的数据。这些数据包括 etcd 数据库、ConfigMaps、Secrets 等。您可以使用工具如 Velero 或 kubeadm-dump 等对 Kubernetes 集群数据进行备份。
升级版本:如果源和目标 Kubernetes 版本不同,则需要将源集群升级到与目标集群相同的版本。请注意,升级 Kubernetes 版本可能会涉及到更改 API 和对象规范,因此在升级之前,请务必仔细查看 Kubernetes 的文档。
准备新集群:在目标环境中创建一个新的 Kubernetes 集群,并确保节点和网络配置与原始集群相同。您可以使用工具如 kubeadm 或 Rancher 等创建新的 Kubernetes 集群。
迁移工作负载:使用 kubectl 工具将工作负载从源集群迁移到目标集群。您可以通过运行
kubectl get命令获取所有工作负载的清单,然后使用kubectl apply命令将它们应用到目标集群上。例如,您可以运行以下命令来将 nginx-deployment 工作负载从源集群迁移到目标集群:迁移配置:将 ConfigMaps 和 Secrets 等配置对象从备份中还原,并使用
kubectl apply命令将它们应用到目标集群上。例如,您可以运行以下命令来将名为 configmap-nginx 的 ConfigMap 从备份中还原并应用到目标集群上:迁移存储:如果使用了持久化存储卷,需要将其迁移到目标环境,并确保它们能够在新集群中正常访问。您可以使用工具如 Velero 或 Rook 等来进行存储卷迁移。
测试和验证:在完成迁移后,请对新集群进行测试和验证,以确保工作负载能够正常运行。您可以使用工具如 Kubernetes Dashboard 或 kubectl 等来监视工作负载的运行状态,并确保它们没有任何问题。
切换 DNS:将 DNS 记录指向新集群中的节点,以确保应用程序能够访问工作负载。您可以在 DNS 服务器上更改 A 记录或 CNAME 记录,以将应用程序重定向到新集群中的节点。
请注意,在执行此类操作之前,请确保您已经备份了所有关键数据,并且测试过程是必不可少的。如果您不确定如何执行这些步骤,建议您寻求专业人员的帮助。
etcd篇
Helm篇
Istio篇
Kubernetes生态链路整合
最后更新于
这有帮助吗?
