简介
- 本文基于
K8S
官方基础教程,从现实运维使用场景出发,结合上述新手入门困境,基于基本组件的定义、功能和使用角度进行讲解,帮助新手在最小化复杂度的前提下上手K8S
。
环境
目标
- 在
K8S
集群上部署应用程序。 - 对应用程序进行扩容缩容(多实例)。
- 对应用程序进行版本更新。
- 使用
K8S
工具排查部署问题。
集群
- 首先,让我们对
K8S
集群有1
个整体的掌握:
- 上图描述的是
1
个Master
节点和6
个Node
节点的K8S
集群。
Master
节点负责管理集群,负责协调集群中的所有活动,例如调度应用程序、维护应用程序的状态、扩展和更新应用程序。Node
节点负责实际承载容器运行的工作节点,每个Node
节点都有1
个Kubelet
。Kubelet
管理Node
节点并负责与Master
节点通信。Node
节点还具有处理容器操作的工具,例如:Docker
和rkt
。
概念
Deployment
- 上图是在之前的基础上,添加了
Deployment
、Pod
和Container
。 Deployment
: 在K8S
中通过发布Deployment
,可以创建应用程序(image
)的实例(container
),这个实例会被包含在Pod
中。- 其中
Deployment
位于Master
节点上,通过发布Deployment
,Master
节点会选择合适的Node
节点创建Container
。 - 其中
Container
会被包含在Pod
中,Pod
是K8S
中最小可管理单元。 - 创建应用程序实例后,
Kubernetes Deployment Controller
会持续监控这些实例。若运行实例的Node
节点关机或被删除,则Kubernetes Deployment Controller
将在集群中重新选择合适的Node
节点,创建新的实例。通过这样的方式提供了1
种自我修复机制来解决机器故障或维护问题。 - 在容器编排之前的时代,各种安装脚本通常用于启动应用程序,但是不能够使应用程序从机器故障中恢复。通过创建应用程序实例并确保它们在集群节点中的运行实例个数,
Kubernetes Deployment
提供了1
种完全不同的方式来管理应用程序。
Pod
- 在
K8S
中创建了1
个Deployment
之后,K8S
会创建1
个Pod
(容器组)来放置Container
(容器)。
Pod
:用于存放1
组Container
(可包含1
个或多个Container
,即图上的正方体),以及这些Container
的共享资源:- 共享存储:
Volumes
(卷),即图上的紫色圆柱体。 - 网络:每个
Pod
在集群中有个唯一的IP
,Pod
中的Container
共享该IP
地址。 - 容器的基本信息:容器的镜像版本、对外暴露的端口等。
- 共享存储:
Pod
中的容器共享IP
地址和端口空间,同1
个Pod
中的不同容器的端口不能冲突,同1
个Pod
中的容器可以使用localhost + Port
互相访问。Pod
是K8S
集群上的最基本的单元。当在K8S
上创建Deployment
时,会在集群上创建包含容器的Pod
(而非直接创建容器)。- 每个
Pod
都与运行它的Node
节点绑定,并保持在那里直到终止或被删除。若节点发生故障,则会在集群中的其他可用节点上运行相同的Pod
(从相同的镜像创建容器,使用相同的配置,不同的IP
地址,不同的Pod
名称)。 - 若多个容器紧密耦合并且需要共享磁盘等资源,则他们应该被部署在同
1
个Pod
中。
Node
Pod
总是在Node
节点上运行,Node
节点是K8S
集群中的工作节点。每个Node
节点都由Master
节点管理。1
个Node
节点可以有多个Pod
,Master
节点会根据每个Node
节点上可用资源的情况,自动调度Pod
到最佳的Node
节点上。Node
节点所需的基础服务:Kubelet
:负责Master
节点和Node
节点之间通信的进程,管理Pod
和Pod
内运行的Container
。- 容器运行环境:
Docker
或rkt
,负责下载镜像、创建和运行容器等。
Service
Pod
有自己的生命周期,当Node
节点故障时,节点上运行的Pod
也会销毁。然后Deployment
可以通过创建新的Pod
来动态地将群集调整回原来的状态,以使应用程序保持运行,K8S
通过Service
服务的方式为前端屏蔽了后端Pod
在销毁、创建过程中所带来IP
地址的变化。Service
通过LabelSelector
选择1
组的Pod
,把这些Pod
的指定端口公布到到集群外部并支持负载均衡和服务发现。Service
使Pod
之间的相互依赖解耦,互相访问时不再需要知道对方的IP
。- 在创建
Service
的时,通过设置配置文件中的spec.type
字段的值,可以以不同方式对外暴露服务:ClusterIP
:默认,在集群中的内部IP
上公布服务,这种方式下,服务只能在集群内部可以访问到。NodePort
:使用NAT
方式在集群中每个节点的同1
个端口上公布服务。这种方式下,可以通过访问集群中任意节点加端口号的方式访问服务<NodeIP>:<NodePort>
。此时ClusterIP
的访问方式仍然可用。LoadBalancer
:在云环境中(需要云服务商支持)创建1
个集群外部的负载均衡器,并使用此负载均衡器的IP
地址作为服务的访问地址。此时ClusterIP
和NodePort
的访问方式仍然可用。
- 图上有
Service A
和Service B
,Service A
将请求转发到IP
为10.10.10.1
的Pod
上,Service B
将请求转发到IP
为10.10.10.2
、10.10.10.3
、10.10.10.4
的Pod
上。 Service
将外部请求路由到1
组Pod
中,提供了1
个抽象层,使得K8S
可以在不影响服务调用者的情况下,动态调度容器组,维持Deployment
对应Pod
的数量。Service
使用Labels
(标签)、LabelSelector
(标签选择器)匹配1
组Pod
。Labels
是附加到K8S
对象的键/值对,其用途有多种:- 将
K8S
对象(Node
、Deployment
、Pod
、Service
等)指派用于开发环境、测试环境或生产环境。 - 嵌入版本标签,使用标签区别不同应用软件版本。
- 使用标签对
K8S
对象进行分类。
- 将
- 上图体现了
Labels
(标签)和LabelSelector
(标签选择器)之间的关联关系:Deployment B
含有LabelSelector
为app=B
,通过此方式声明与含有app=B
标签的Pod
关联。- 通过
Deployment B
创建的Pod
包含标签为app=B
。 Service B
通过标签选择器app=B
选择可以路由的Pod
。
Labels
(标签)可以在创建K8S
对象时附加上去,也可以在创建之后再附加上去。任何时候都可以修改1
个K8S
对象的Labels
。
操作
创建服务
- 创建
deployment
:
1 | vim deployment.yaml |
1 | apiVersion: apps/v1 |
apiVersion
:声明API
的版本,可通过kubectl api-versions
查看集群所支持的版本。kind
:声明资源类型,可通过kubectl api-resources
查看集群所支持的资源。metadata
:元数据,即Deployment
的一些基本属性和信息。name
:Deployment
的名称。labels
:Deployment
的标签,允许为1
个或多个。
spec
:Deployment
的描述信息,期望Deployment
在K8S
集群中的状态。replicas
:副本数。selector
:标签选择器,与metadata
中labels
一起工作。matchLabels
:选择匹配的标签,允许为1
个或多个。
template
:选择或创建Pod
的模板。metadata
:Pod
的元数据。labels
:Pod
的标签,与selector
中的matchLabels
一起工作,允许为1
个或多个。
spec
:Pod
的描述信息,期望Pod
在K8S
集群中的状态。containers
:生成容器。name
:container
的名称。image
:container
的镜像。
应用
yaml
文件:
1 | kubectl apply -f deployment.yaml |
服务信息
- 获取资源:
1 | # 命令格式 |
- 获取指定命名空间的资源:
1 | # 命令空间:所有空间 |
- 获取指定资源的详细信息:
1 | # 命令格式 |
- 获取指定
Pod
中容器的日志:
1 | # 命令格式 |
- 在指定
Pod
的容器中执行命令:
1 | # 命令格式 |
发布服务
- 创建
service
:
1 | vim service.yaml |
1 | apiVersion: v1 |
apiVersion
:声明API
的版本,可通过kubectl api-versions
查看集群所支持的版本。kind
:声明资源类型,可通过kubectl api-resources
查看集群所支持的资源。metadata
:元数据,即Service
的一些基本属性和信息。name
:Service
的名称。labels
:Service
的标签,允许为1
个或多个。
spec
:Service
的描述信息,期望Service
在K8S
集群中的状态,如何选择Pod
,如何对外提供服务。selector
:标签选择器,选择包含以下标签的Pod
。ports
:端口信息。name
:端口名称。protocol
:端口协议,可选TCP/UDP
。port
:集群内其他Pod
可通过此端口访问Service
。nodePort
:通过任意节点的指定端口访问Service
。targetPort
:将请求转发到匹配Pod
的端口。
type
:Service
的类型,可选ClusterIP/NodePort/LoaderBalancer
。
应用
yaml
文件:
1 | kubectl apply -f service.yaml |
伸缩服务
- 在之前的操作中,我们创建了
Deployment
仅包含1
个Pod
,当流量增加时,我们可以对应用程序进行伸缩操作以满足服务的性能要求。 - 伸缩的实现可以通过更改
deployment.yaml
文件中部署的replicas
(副本数)来完成。
- 上图中,
Service A
只将访问流量转发到IP
为10.0.0.5
的Pod
上。
修改了
Deployment
的replicas
为4
后,K8S
又为此Deployment
创建了3
个新的Pod
,这4
个Pod
有相同的标签。因此Service A
通过标签选择器与新的Pod
建立了对应关系,将访问流量通过负载均衡在4
个Pod
之间进行转发。更新
deployment
:
1 | vim deployment.yaml |
1 | apiVersion: apps/v1 |
- 应用
yaml
文件:
1 | kubectl apply -f deployment.yaml |
- 获取资源:
1 | # 资源类型:Pod |
滚动更新
- 用户期望服务始终可用,为此开发者/运维者在更新应用程序时要分多次完成。在
K8S
中,这是通过Rolling Update
滚动更新完成的。 - 滚动更新通过使用新版本的
Pod
逐步替代旧版本的Pod
来实现Deployment
的更新,从而实现零停机。新的Pod
将在具有可用资源的Node
(节点)上进行调度。 - 我们将应用程序扩容为多个实例,这是执行更新而不影响应用程序可用性的前提。
- 默认情况下,滚动更新过程中,
K8S
会逐个使用新版本Pod
替换旧版本Pod
,以便使服务一直处于可用状态。 - 最大不可用
Pod
数为1
、最大新建Pod
数为1
。这两个参数可以配置为数字或百分比。在K8S
中,更新是版本化的,任何部署更新都可以恢复为以前的稳定版本。
- 环境中
Service A
将流量负载均衡到4
个旧版本的Pod
(绿色)上。
- 更新部署文件中的镜像版本后,
Master
节点选择了1
个Node
节点,并根据新的镜像版本创建Pod
(紫色)。新Pod
拥有唯一的新的IP
。同时,Master
节点会选择1
个旧版本的Pod
将其移除。
- 重复上一步骤,再创建
1
个新的Pod
替换1
个原有的Pod
。
执行滚动更新,直到所有旧版本
Pod
均移除,新版本Pod
也达到Deployment
部署文件中定义的副本数,则滚动更新完成。更新
deployment
:
1 | vim deployment.yaml |
1 | apiVersion: apps/v1 |
- 应用
yaml
文件:
1 | kubectl apply -f deployment.yaml |