目录:
(1)k8s指南-概述
(2)k8s指南-架构
(3)k8s指南-工作负载(1)
(4)k8s指南-工作负载(2)
(5)k8s指南-工作负载(3)
(6)k8s指南-工作负载(4)
(7)k8s指南-Service
(8)k8s指南-Ingress
(9)k8s指南-DNS与服务发现
(10)K8S指南-平滑升级与自动扩缩容
Kubernetes是一个可移植的,可扩展的开源平台,用于管理容器化的工作负载和服务,为声明式配置和自动化带来了巨大便利。它拥有着巨大而快速增长的生态系统,其相关的服务和工具得到了广泛的应用。
一个kuberbetes集群由一个控制平面组件和一系列节点组成。
Going back in time
传统部署时代
早期的应用程序一般都部署在物理节点上,这带来的问题是没办法界定物理资源的边界,容易出现机器资源分配问题。比如说,当有多个应用运行在同一台物理机上时,如果某个应用占用了机器的大部分资源,就会导致其他应用无法运行。解决办法是让不同的应用跑在不同的机器上,但这又会导致机器资源利用不充分,而且会产生高昂的成本。
虚拟化部署时代
为了解决传统部署方式中存在的问题,虚拟化技术出现了。虚拟化部署允许用户在一个物理机cpu上跑多个虚拟机,虚拟机中的应用程序是相互隔离的。一个应用不能随意地访问另外一个应用,这在某种程度上提供了一定的安全性保证。
虚拟化技术能够很好地利用物理服务器上的资源,并且可以很方便地添加或者更新应用程序,从而实现更好的伸缩性,降低硬件成本。每一个虚拟机就是一台完整的计算器,在虚拟化硬件的基础上运行所有组件,包括操作系统。
容器化部署时代
容器与虚拟机有点类似,但它们有着更为宽松的隔离性,以便能够共享操作系统,也因此更为轻量级。与虚拟机类似的是,容器拥有自己的文件系统,cpu,内存,进程空间等。由于容器与底层基础设施是解藕的,所以可以方便地在云和云之间,操作系统和操作系统之间进行移植。
容器之所以变得如此受欢迎,是因为它具有很多的优点,比如:
kubernetes能做什么
容器是打包和运行应用程序的好方式。在生产环境中,你需要管理运行应用程序的容器,并确保不会停机。例如,如果一个容器发生故障,则需要启动另一个容器。如果由系统来处理这种情况,会不会变得更容易?这就是Kubernetes解决此类问题的方法。k8s提供了一个可弹性运行分布式系统的框架,能够满足你的扩展、故障转移、部署模式等需求。
Kubernetes为你提供:
k8s可以使用DNS名称或者自己的ip地址公开容器,如果进入容器的流量很大,k8s可以负载均衡并分配网络流量,从而使部署稳定。
k8s允许你自动挂载你选择的存储系统,例如本地存储,公有云存储等。
你可以使用k8s描述已部署容器的所需状态,k8s会以受控的速率将实际状态更改为期望状态。例如,你可以让k8s为你的部署创建新容器,删除现有容器并将它们所有的资源用于新容器。
Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。 当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。
重新启动失败的容器,在节点不可用时,替换和重新调度节点上的容器,对用户定义的健康检查不响应的容器会被中止,并且在容器准备好服务之前不会将其向客户端广播。
k8s允许存储和管理敏感信息,例如密码、OAuth令牌和ssh密钥。你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置。
当你部署完 Kubernetes, 即拥有了一个完整的集群。
一个 Kubernetes 集群由一组被称作节点的机器组成,Kubernetes 所管理的容器化应用就在这些节点上运行。每个集群具有至少一个工作节点。
工作负载组件,也就是pods,托管在集群中的工作节点上,而控制平面负责管理所有的工作节点和pods。通常在生产环境中,控制平面和集群是分布式的,跨主机和跨节点的,以此来实现故障转移和高可用。
控制平台组件负责为集群做全局决策,比如任务调度,同时还能检测和响应集群事件,例如当pod数量不满足replicas指定的数量时,负责启动新的pod。
控制平面组件可以在集群中的任何节点上运行。但是为了简单起见,设置脚本通常会选择一台机器来启动所有的控制平面组件,并且这台机器不会跑任何用户容器。
kube-apiserver
API Server是控制平面中用于对外暴露k8s API接口的,它是控制平面的前端。
API Server的主要实现是kube-apiserver,是可以水平伸缩的。也就是说,它可通过部署多个实例进行伸缩。你可以运行kube-apiserver的多个实例,并在这些实例之间做负载均衡。
etcd
etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。通常来说,etcd数据库需要有备份计划。
kube-scheduler
kube-scheduler负责监控新创建但还没有指定运行节点的pods,然后选择合适的节点让pod运行。
调度决策考虑的因素有很多,包括单个pod和pod集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。
kube-controller-manager
kube-controller-manager是控制器进程管理器。
从逻辑上来讲,每个控制器都是一个单独的进程,但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。
这些控制器包括:
cloud-controller-manager
云控制器管理器是指嵌入特定云的控制逻辑的控制平面组件。通过云控制器管理器,你可以将你的集群连接到云提供商的API, 并将云平台交互组件和你的集群交互组件隔离开来。
注意,cloud-controller-manager 仅作用在特定的云平台上,如果你在自己的环境中运行Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器。
节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。
kubelet
kebelet是运行在每个节点上的代理,保证容器都运行在pod中。
kubelet 接收一组通过各类机制提供给它的PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且是健康的。 kubelet 只管理由 Kubernetes 创建的容器。
kube-proxy
kube-proxy是运行在每个节点上的网络代理,负责维护节点上的网络规则。这些网络规则允许从集群内部或外部发起的网络会话与 Pod 进行网络通信。
如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发本身的流量。
container runtime
容器运行时负责容器运行的环境。
Kubernetes 支持多个容器运行环境: Docker、 containerd、CRI-O 以及 Kubernetes CRI (容器运行环境接口)的其他任何实现。
插件使用 Kubernetes 资源(DaemonSet、 Deployment等)实现集群功能,插件中命名空间域的资源属于 kube-system 命名空间。下面介绍几种插件。
DNS
事实上k8s中除了DNS之外,其他插件都不是必须插件。集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。
Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。
在上文中介绍过,k8s控制平面包括诸多组件,其中一个很核心的组件就是API Server。它负责提供HTTP API,以供用户、集群中的不同部分以及集群外的组件相互通信。
通过API Server,你可以查询和操作k8s中的对象,如Pod,Namespace,ConfigMap和Event等。大部分操作都可以通过kubectl命令行接口或者类似kubeadm这类命令行工具来执行,其背后实际调用的就是API。
如果你需要在程序中访问k8s的API,可以考虑使用客户端库。
k8s对象是指k8s系统中的持久化实体,k8s通过这些实体来描述集群状态。
k8s对象是“目标性记录”–一旦创建对象,k8s系统将持续工作以确保对象存在。通过创建对象来告知k8s系统,所需要的集群工作负载是什么样的,这就是k8s集群的期望状态。
操作k8s对象,无论是创建、修改或者删除,都需要使用k8s API。比如,当使用kubectl命令行接口时,CLI会执行必要的k8s API调用,当然,也可以直接在程序代码中使用客户端库直接调用API。
对象规约与状态
几乎每个k8s对象都包含两个嵌套的对象字段:spec和status,用于管理对象的配置。对于具有spec的对象,必须在创建对象时设置其内容,描述其期望状态。
status描述了对象的当前状态,它是由k8s系统和组件设置并更新的。k8s控制平面全程负责管理对象的实际状态,以使之与期望状态相匹配。
例如,k8s中的Deployment对象能够表示运行在集群中的应用。当创建Deployment时,可能需要设置Deployment的spec,以指定该应用的副本数量。k8s系统读取Deployment的spec,启动指定数量的实例–更新状态与规约相匹配。如果这些实例中有失败的,k8s会执行修正操作,来尝试达到期望值–这意味着它会启动一个新的实例来替换失败的实例。
描述k8s对象
创建k8s对象时,必须提供对象的规约,用来描述对象的期望状态,以及对象的一些基本信息。当使用k8s API创建对象时(直接创建或基于kubectl),API请求必须在请求体中包含json格式的信息。大多数情况下,需要在.yaml
文件中为kubectl提供这些信息,kubectl在发起API请求时,将这些信息转换成JSON格式。下面是一个示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
在kubectl命令行接口中使用apply命令,将.yaml
文件作为参数,调用API接口生成对象:
kubectl apply -f https://k8s.io/examples/application/deployment.yaml --record
输出类似如下这样:
deployment.apps/nginx-deployment created
必需字段
在对象参数对应的.yaml
文件中,需要配置如下字段:
name
,UID
和namespace
等。对象spec的精确格式对每个k8s对象来说都是不同的,包含了该对象特定的字段。Kubenetes API 参考 中说明了不同对象的规约格式。
kubectl命令行工具支持多种不同的方式来创建和管理k8s对象。
应该只使用一种技术来管理k8s对象,不同的技术混合在一起作用在同一对象上将导致未定义行为。
不同方式的特性如下:
管理技术 | 作用范围 | 建议的环境 | 支持的写入者数 | 学习难度 |
---|---|---|---|---|
指令式命令 | 活跃对象 | 开发项目 | 1+ | 低 |
指令式对象配置 | 单个文件 | 生产项目 | 1 | 中等 |
声明式对象配置 | 文件目录 | 生产项目 | 1+ | 高 |
指令式命令
使用指令式命令时,用户可以在集群中的活动对象上进行操作。这种方式将用户操作传给kubectl命令作为参数或标志。
指令式命令是在集群中运行一次性任务的推荐方式。因为是直接在活跃的对象上操作,所以它不提供以前配置的历史记录。
下面是一个指令式命令的例子:
kubectl create deployment nginx --image nginx
指令式对象配置
在指令式对象配置中,kubectl命令需要指定操作,可选标志和至少一个文件名。指定的文件必须包含yaml或json格式的对象的完整定义。
下面是一个例子:
kubectl create -f nginx.yaml
删除两个配置文件中定义的对象:
kubectl delete -f nginx.yaml -f redis.yaml
通过覆盖活动配置来更新配置文件中定义的对象:
kubectl replace -f nginx.yaml
声明式对象配置
使用声明式对象配置时,用户对本地存储的对象配置进行操作,但是不定义要对该文件操作的类型。kubectl会自动检测每个文件的创建、更新和删除操作。这使得配置可以在目录上工作,根据目录中配置文件,对不同的对象执行不同的操作。
声明式对象配置保留了其他编写者所做的修改,即使这些更改并未合并到对象配置文件中。可以用
patch
API操作仅写入差异点,而不是使用replace
API操作来替换整个对象。
下面是一个声明式对象的例子。
处理configs
目录中的所有对象配置文件,创建并更新活跃对象。可以首先使用diff子命令查看将要进行的修改,然后再进行应用:
kubectl diff -f configs/
kubectl apply -f configs/
递归处理目录:
kubectl diff -R -f configs/
kubectl apply -R -f configs/
三种对象操作方式的主要优缺点如下:
指令式命令 | 指令式对象配置 | 声明式对象配置 | |
---|---|---|---|
优点 | 命令简单易学,仅需一步即可对集群进行更改 | 可以将变更通过文件形式存储在版本控制系统如git中;可以与流程集成,如在推送和审计之前检查更新 | 对活动对象的更改即使未合并到配置文件中,也会被保留下来(增量更新);支持对目录进行操作 |
缺点 | 无法审核和跟踪,不提供记录源,不提供创建新对象的模板 | 不适合对目录进行操作;对活动对象的更新必须反映在配置文件中,否则会在下一次替换时丢失(全量更新) | 难于调试,出现异常结果时难以理解;diff产生的部分更新会创建复杂的合并和补丁操作 |
每个对象都有一个名字,用来标识在同类资源中的唯一性。此时同时,每个对象还有一个UID,在整个集群中都是唯一的。
举个例子,在同一个命名空间内,只能有一个叫做myapp-1234
的pod,但是,还可以同时存在一个也叫这个名字的deployment对象。这是因为他们的对象类型是不同的。
对于用户提供的非唯一属性,Kubernetes提供了labels
和annotation
机制。
客户端提供的字符串,用来引用资源中的对象,如/api/v1/pods/some name
。
以下是四种常见的资源命名约束:
DNS子域名
很多资源类型需要可以用作DNS子域名的名称。DNS子域名的定义见RFC 1123规范。需要满足如下规则:
标签名
某些资源类型需要其名称遵循DNS标签标准,规则如下:
分段路径名
某些资源类型的名称需要作为分段路径名来使用,不能为“.”或者“…”,也不能包含“/”和“%”。下面是一个示例:
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
注意: 某些资源类型有额外的命名限制
Kubernetes UIDs 是全局唯一标识符(也叫 UUIDs),可以用来标识一个唯一的对象。 UUIDs 是标准化的,见 ISO/IEC 9834-8 和 ITU-T X.667。在 Kubernetes 集群的整个生命周期中创建的每个对象都有一个不同的 uid,它旨在区分类似的实体。
在 Kubernetes 中,“名字空间(Namespace)”提供一种机制,将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一,但跨名字空间时没有这个要求。 名字空间作用域仅针对带有名字空间的对象,例如 Deployment、Service 等, 这种作用域对集群访问的对象不适用,例如 StorageClass、Node、PersistentVolume 等。
避免使用前缀 kube- 创建名字空间,因为它是为 Kubernetes 系统名字空间保留的。
查看命名空间
你可以使用以下命令列出集群中现存的名字空间:
kubectl get namespace
Kubernetes 会创建四个初始名字空间:
为请求设置名字空间
要为当前请求设置名字空间,请使用--namespace
参数。
例如:
kubectl run nginx --image=nginx --namespace=<名字空间名称>
kubectl get pods --namespace=<名字空间名称>
设置命名空间偏好
你可以永久保存命名空间,以用于对应上下文中所有后续 kubectl 命令。
kubectl config set-context --current --namespace=<名字空间名称>
# 验证
kubectl config view | grep namespace:
[1]. https://kubernetes.io/zh/docs/concepts/overview/
[2]. https://blog.csdn.net/weichuangxxb/article/details/103754021