Kubernetes学习笔记(1)-简介与基本使用
Kubernetes简介
背景介绍
应用程序的部署方式主要经历了三个时代。传统部署将应用程序部署在物理机上,这种方式实现简单,但是很难合理地分配计算资源,容易导致资源浪费以及程序之间相互影响。而后发展到虚拟化部署,即可以在一台物理机上运行多个虚拟机,每个虚拟机都是一个独立的环境,每个程序部署在一台虚拟机上,这种方式程序之间不会相互影响,但是增加了操作系统的虚拟,同样会浪费部分资源。部署方式继续发展,到容器化部署。容器化与虚拟化部署类似,但是容器之间共享操作系统,降低了资源的浪费。
在容器化部署的相关过程中,一个非常重要的课题就是容器编排。容器管理的相关问题统称为容器编排问题。例如当某个容 器故障停机之后,如何让另外一个容器立刻启动去替补停机的容器;或者如何根据并发访问量的多少来动态地调整容器的数量。
以下许多软件来解决容器编排问题,而我们主要学习的是其中最受欢迎,使用最为广泛的Kubernetes。
Swarm
:Docker自己的容器编排工具Mesos
:Apache的一个资源统一管控的工具,需要和Marathon结合使用Kubernetes
:Google开源的的容器编排工具
kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能:
- 自我修复:一旦某一个容器崩溃,能够在1秒左右迅速启动新的容器
- 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
- 服务发现:服务可以通过自动发现的形式找到它所依赖的服务
- 负载均衡:如果一个服务启动了多个容器,能够自动实现请求的负载均衡
- 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
- 存储编排:可以根据容器自身的需求自动创建存储卷
Kubernetes的环境搭建,可以参考官方文档Getting Started|Kubernetes。
另外,如果在Mac或者Windows上安装过Docker Desktop,可以在设置中开启Kubernetes的支持,就可以在本地进行Kubernetes的使用,可以参考相关博客Kubernetes基础: 在MacOS上安装Kubernetes。
组件与相关概念
Kubernetes属于主从架构,集群中的角色分为控制节点Master以及工作节点Node,在不同的节点上会安装不同的组件。
Master节点是整个集群的控制平面,负责集群的管理与决策,其中包含如下组件:
ApiServer
:资源操作的唯一入口,用于接收用户输入的命令,提供认证、授权、API注册和发现等机制Scheduler
:负责集群资源调度,按照预定的调度策略将Pod调度到相应的Node节点上ControllerManager
:负责维护集群的状态,例如程序部署安排、故障检测、自动扩展、滚动更新等Etcd
:负责存储集群中各种资源对象的信息
Node节点是集群的数据平面,负责为容器提供运行环境,用于实际运行容器,其中包含如下组件:
Kubelet
:负责维护容器的生命周期,通过控制Docker,来创建、更新销毁容器。KubeProxy
:负责提供集群内部的服务发现和负载均衡Docker
:负责节点上容器的各种操作
现在假设我们需要在Kubernetes上部署一个nginx服务,那么需要经过如下的步骤:
- 首先启动Kubernetes环境,Master和Node将自身的信息注册到etcd数据库中
- nginx服务到安装请求通过kubectl发送到Master节点上的ApiServer组件
- ApiServer组件调用Scheduler组件来决定把这个服务安装到哪个Node节点上。这个过程需要从etcd中读取各个Node节点的信息,然后按照调度算法进行选择,并将选择结果告知ApiServer
- ApiServer调用ControllerManager来调度Node节点安装nginx服务
- Kubelet接收到指令之后,通知Docker,由Docker来启动一个nginx的Pod。这里的Pod是Kubernetes中的最小操作单元,Container容器必须运行在某个Pod当中
- 至此,一个nginx服务就运行成功了,如果外界需要访问该服务,则需要通过KubeProxy来令Pod产生可访问的代理,之后外界用户就可以访问集群中的nginx服务
下面统一介绍Kubernetes中的相关概念:
Master
:集群控制节点,每个集群至少需要一个Master节点来负责集群的管理和控制Node
:工作负载节点,由Master分配容器到这些Node工作节点上,然后Node节点中的Docker负责容器的运行Pod
:Kubernetes中的最小控制单元,容器都是运行在Pod中的。一个Pod中可以有一个或者多个容器Controller
:控制器,可以通过控制器来实现对Pod的管理,例如启动Pod、停止Pod、伸缩Pod的数量等Service
:Pod对外服务的统一入口,其中可以维护同一类的多个PodLabel
:标签,用于对资源进行分类,同一类资源会拥有相同的标签Namespace
:命名空间,用于隔离Pod的运行环境
资源管理
介绍
在Kubernetes中,所有内容都被抽象为资源,用户需要通过操作资源来管理Kubernetes。
Kubernetes的本质上就是一个集群系统,用户可以在集群中部署各种服务。所谓的部署服务,其实就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中。Kubernetes的最小管理单元是Pod而不是容器,所以只能将容器放在Pod
中,而Kubernetes一般也不会直接管理Pod,而是通过Pod控制器
来管理Pod的。Pod可以提供服务之后,就要考虑如何访问Pod中服务,Kubernetes提供了Service
资源实现这个功能。当然,如果Pod中程序的数据需要持久化,Kubernetes还提供了各种存储
系统。
学习Kubernetes的核心,就是学习如何对集群上的Pod、Pod控制器、Service、存储等各种资源进行操作。
kubectl
Kubernetes提供了一个命令行工具kubectl,该命令行工具能够对集群本身进行管理,并且能够在集群上进行容器化应用的安装部署。kubectl命令的语法如下:
1 |
|
command
:指定需要对资源执行的操作,例如create、get、deletetype
:指定资源的类型,例如deployment、pod、servicename
:指定资源的名称,注意是大小写敏感的params
:指定额外的可选参数
下面是一些命令的举例:
1 |
|
Tips:在Node节点上运行kubectl
kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行kubectl,需要将master上的.kube文件复制到node节点上,可以通过在master节点上执行scp命令来完成文件的复制。
资源类型
在Kubernetes中所有的内容都被抽象为resource,可以通过下面的命令查看:
1 |
|
其中常用的resource类型有:
不必过于在意resource名称的单复数,kubectl都能够支持
- 集群资源:属于集群组成的一部份
resource名称 | 缩写 |
---|---|
nodes | no |
- namespace:命名空间,用于隔离Pod
resource名称 | 缩写 |
---|---|
namespace | ns |
- pod:用于装载容器
resource名称 | 缩写 |
---|---|
pods | pd |
- pod controller:用于控制Pod资源
resource名称 | 缩写 |
---|---|
replicationcontrollers | rc |
replicasets | rs |
deployments | deploy |
daemonsets | ds |
jobs | |
cronjobs | cj |
horizontalpodautoscalers | hpa |
statefulsets | sts |
- 服务发现:Pod的统一对外服务接口
resource名称 | 缩写 |
---|---|
services | svc |
ingress | ing |
- 存储资源:支持存储相关功能
resource名称 | 缩写 |
---|---|
volumeattachements | |
persistentvolumes | pv |
persistentvolumeclaims | pvc |
- 配置资源:支持配置相关功能
resource名称 | 缩写 |
---|---|
configmaps | cm |
secrets |
操作类型
Kubernetes允许对resource进行多种操作,可以通过--help
来查看详细的操作命令:
1 |
|
其中常用的操作类型有:
- 基本命令:
操作 | 功能 |
---|---|
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 | 显示版本信息,包括Server以及Client |
管理方式
Kubernetes共有三种资源管理方式,分别是命令式对象管理、命令式对象配置以及声明式对象配置。
- 命令式对象管理:指的是直接使用命令去操作Kubernetes资源
- 命令式对象配置:通过命令和配置文件去操作Kubernetes资源,命令包括create/patch
- 声明式对象配置:通过apply命令和配置文件操作Kubernetes资源,其中apply用于创建和更新资源,无法删除资源
类型 | 操作对象 | 适用环境 | 优点 | 缺点 |
---|---|---|---|---|
命令式对象管理 | 对象 | 测试 | 简单 | 只能操作活动对象,无法审计、跟踪 |
命令式对象配置 | 文件 | 开发 | 可以审计、跟踪 | 项目大时,配置文件多,操作麻烦 |
声明式对象配置 | 目录 | 开发 | 支持目录操作 | 意外情况下难以调试 |
命令式对象管理
命令式对象管理指的是直接使用命令去操作Kubernetes资源,如下所示:
1 |
|
命令式对象配置
命令式对象配置就是使用命令配合配置文件一起来操作Kubernetes Resource。 这种方式可以简单的认为是命令 + yaml配置文件(里面是命令需要的各种参数)
例如我们可以创建一个配置文件nginx-pod.yaml
,这个配置文件基本描述了Pod运行的环境以及相关的属性,内容如下:
1 |
|
该配置文件完成了两个资源的定义。首先定义了一个namespace,名称为dev
。然后定义了一个pod,该pod的名称为nginxpod
,运行在dev
namespace中,同时在详细信息spec
中指定了容器的名称以及对应的镜像。
之后执行create命令进行资源创建,提示创建了namespace dev 以及pod nginxpod,可以通过相关get命令查看到创建出的资源。
1 |
|
同时也可以使用删除命令,同时删除这两个资源:
1 |
|
声明式对象配置
声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。
同样使用上面的实例配置文件,首先第一次执行apply命令,发现创建了资源。之后我们可以继续执行,发现资源没有变动。
1 |
|
实际上,声明式对象配置使用yaml配置文件来描述资源的最终状态,然后使用apply命令来将资源调整为描述的状态,如果没有该资源就进行创建,如果有修改就进行调整,没有变化就什么也不做。
使用apply操作资源,如果资源不存在,就创建,相当于 kubectl create;如果资源已存在,就更新,相当于 kubectl patch。
推荐使用方式:
- 创建/更新资源 使用声明式对象配置 kubectl apply -f xxx.yaml
- 删除资源 使用命令式对象配置 kubectl delete -f xxx.yaml
- 查询资源 使用命令式对象管理 kubectl get(describe) 资源名称
Getting Started
接下来我们通过在Kubernetes集群中部署一个可供外界访问的nginx服务来初步学习Kubernetes的使用以及相关概念。
Namespace
namespace是Kubernetes系统中非常重要的resource,它的主要作用是实现多套环境的资源隔离或者多租户的资源隔离。
在默认情况下,Kubernetes集群中的所有Pod都是可以相互访问的,但是在实际中,我们可能不想让两个Pod之间进行相互访问,那么此时就可以将两个Pod划分到不同的namespace下。
Kubernetes通过namespace机制来划分逻辑上的组,方便对不同组的资源进行隔离和管理。同时通过Kubernetes的授权机制,可以将不同的namespace交给不同的租户来管理,实现多租户的资源隔离。此外,结合Kuberentes的资源配额机制,限定不同租户能够占用的资源,例如CPU使用量、内存使用量等,来实现租户可用资源的管理。
Kubernetes集群在启动之后,会默认创建如下的namespace:
default
:所有未指定namespace的对象都会被分配在default命名空间kube-node-lease
:用于维护集群节点之间的心跳信息kube-public
:此命名空间下的resource可以被所有人访问,包括未认证用户kube-system
:所有由Kubernetes系统创建的resource都处于这个命名空间
在Getting
Started中,我们创建一个名为dev的namespace,可以使用之前提到的三种资源管理方式中的任意一种来进行。通过kubectl get ns dev -o yaml
命令可以以yaml形式查看namespace的详细信息。
- 如果需要使用yaml配置文件来创建,可以参考:
1 |
|
Pod
Pod是Kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。Kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的,例如可以通过kubectl get pod -n kube-system
查看集群系统相关Pod。
我们可以通过kubectl run
命令来指定在集群中运行Pod,例如:
1 |
|
同样也可以使用delete删除对应的pod。kubectl delete pod nginx -n dev
。
注意:在较老版本的kubectl中,run命令会生成deplyment来控制Pod,而在最新的kubectl中,run命令则是直接创建Pod。
- 参考yaml配置文件:
1 |
|
Deployment
在Kubernetes中,Pod是最小的控制单元,但是Kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障时,会尝试进行重启或重建Pod。
Kubernetes中Pod控制器的种类有很多,在Getting Started中我们先介绍其中的Deployment。
可以通过下面的命令创建一个Deployment:
1 |
|
之后可以通过kubectl get deploy -n dev
查看到相关Deployment的信息。创建Deployment的同时也会创建对应的Pod,也可以通过kubectl get pod -n dev
来查看相关Pod信息。
此时如果我们手动使用delete命令删除Pod,该Pod会被删除,但是由于Deployment的存在,它会新建一个Pod来顶替被删除的Pod。
当前Pod是由Pod控制器创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建。如果要完全删除Pod的话,则需要先删除Pod控制器。
- 参考yaml配置文件:
1 |
|
Service
Deployment可以通过创建一组Pod来提供具有高可用性的服务,其中每个Pod都会分配一个单独的Pod IP。但是还是存在两个问题:
- Pod IP 会随着Pod的重建产生变化
- Pod IP 仅仅是集群内可见的虚拟IP,外部无法访问
为了能够从外部访问服务以及固定IP,Kubernetes提供了Service。Service可以看作是一组同类Pod提供的对外访问接口,借助Service,应用可以很方便地实现服务发现以及负载均衡。
Service分为两类,分别对应两种type,ClusterIP
以及NodePort
。其中ClusterIP表示集群内部可访问的Service,主要解决了IP变化的问题,而NodePort表示集群内外都可以访问的Service。
我们可以通过如下命令创建一个ClusterIP
类型的Service。expose命令可以将resource暴露为新的Kubernetes
Service(暴露resource,不是创建新resource,除了新得到的Service)
1 |
|
这里我们将名为nginx的deployment暴露为了一个ClusterIP类型的Service,可以通过kubectl get svc
命令查看,可以发现其中有一个CLUSTER-IP
字段,这就是Service的IP。在Service的生命周期中,这个地址是不会变动的,可以通过访问这个IP来访问当前Service对应的Pod。
同样我们也可以将nginx
deployment暴露为一个NodePort
类型的Service,只需要指定type为NodePort即可。
1 |
|
创建完成之后,同样可以查看svc详情,使用命令kubectl get svc svc-nginx2 -n dev -o wide
。发现其中的端口映射如下:
1 |
|
这表示将宿主机的31727端口和Deployment对应的80端口进行映射,此时可以使用浏览器访问http://localhost:31727
,可以看见nginx的欢迎页面。这里的浏览器就是从集群之外进行访问的。
- 参考配置yaml文件:
1 |
|
Label
Label是Kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
- 一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等等
- 一个资源对象可以定义任意数量的Label ,同一个Label也可以被添加到任意数量的资源对象上去
- Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
可以通过Label实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。
一些常用的Label 示例如下:
- 版本标签:"version":"release", "version":"stable"......
- 环境标签:"environment":"dev","environment":"test","environment":"pro"
- 架构标签:"tier":"frontend","tier":"backend"
可以通过label命令来为Resource进行标签相关操作:
1 |
|
基于标签,我们可以进行标签的选择。Kubernetes提供了Label Selector来完成这一操作。Label用于给某个资源对象定义标识,而Label Selector用于查询和筛选拥有某些标签的资源对象。
在Kubernetes中有两类Label Selector,分别是基于等式的Label
Selector,使用关键字为=, !=
;以及基于集合的Label
Selector,使用关键字为in, not in
。同时Label的选择条件也可以多次使用,使用逗号,
将多个Label
Selector进行分隔即可。
1 |
|
- 参考配置yaml文件:
1 |
|
经过Getting Started的全流程,我们可以得到下面一张概述图,描述Resource的组织方式。