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服务,那么需要经过如下的步骤:

  1. 首先启动Kubernetes环境,Master和Node将自身的信息注册到etcd数据库中
  2. nginx服务到安装请求通过kubectl发送到Master节点上的ApiServer组件
  3. ApiServer组件调用Scheduler组件来决定把这个服务安装到哪个Node节点上。这个过程需要从etcd中读取各个Node节点的信息,然后按照调度算法进行选择,并将选择结果告知ApiServer
  4. ApiServer调用ControllerManager来调度Node节点安装nginx服务
  5. Kubelet接收到指令之后,通知Docker,由Docker来启动一个nginx的Pod。这里的Pod是Kubernetes中的最小操作单元,Container容器必须运行在某个Pod当中
  6. 至此,一个nginx服务就运行成功了,如果外界需要访问该服务,则需要通过KubeProxy来令Pod产生可访问的代理,之后外界用户就可以访问集群中的nginx服务

下面统一介绍Kubernetes中的相关概念:

  • Master:集群控制节点,每个集群至少需要一个Master节点来负责集群的管理和控制
  • Node:工作负载节点,由Master分配容器到这些Node工作节点上,然后Node节点中的Docker负责容器的运行
  • Pod:Kubernetes中的最小控制单元,容器都是运行在Pod中的。一个Pod中可以有一个或者多个容器
  • Controller:控制器,可以通过控制器来实现对Pod的管理,例如启动Pod、停止Pod、伸缩Pod的数量等
  • Service:Pod对外服务的统一入口,其中可以维护同一类的多个Pod
  • Label:标签,用于对资源进行分类,同一类资源会拥有相同的标签
  • 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
kubectl [command] [type] [name] [params]
  • command:指定需要对资源执行的操作,例如create、get、delete
  • type:指定资源的类型,例如deployment、pod、service
  • name:指定资源的名称,注意是大小写敏感的
  • params:指定额外的可选参数

下面是一些命令的举例:

1
2
3
4
5
6
7
8
9
# 查看所有pod
kubectl get pod

# 查看某个pod
kubectl get pod pod_name

# 查看某个pod,以yaml格式展示结果
kubectl get pod pod_name -o yaml
kubectl get pod pod_name -o wide

Tips:在Node节点上运行kubectl

kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行kubectl,需要将master上的.kube文件复制到node节点上,可以通过在master节点上执行scp命令来完成文件的复制。

资源类型

在Kubernetes中所有的内容都被抽象为resource,可以通过下面的命令查看:

1
kubectl api-resources

其中常用的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
kubectl --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 显示版本信息,包括Server以及Client

管理方式

Kubernetes共有三种资源管理方式,分别是命令式对象管理、命令式对象配置以及声明式对象配置。

  • 命令式对象管理:指的是直接使用命令去操作Kubernetes资源
  • 命令式对象配置:通过命令和配置文件去操作Kubernetes资源,命令包括create/patch
  • 声明式对象配置:通过apply命令和配置文件操作Kubernetes资源,其中apply用于创建和更新资源,无法删除资源
类型 操作对象 适用环境 优点 缺点
命令式对象管理 对象 测试 简单 只能操作活动对象,无法审计、跟踪
命令式对象配置 文件 开发 可以审计、跟踪 项目大时,配置文件多,操作麻烦
声明式对象配置 目录 开发 支持目录操作 意外情况下难以调试

命令式对象管理

命令式对象管理指的是直接使用命令去操作Kubernetes资源,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建一个namesapce
kubectl create namespace dev

# 查看namespace
kubectl get ns

# 在dev namepsace下创建并运行一个nginx的pod
kubectl run pod --image=nginx:latest -n dev

# 查看pod
kubectl get pod -n dev
# 结果如下
NAME READY STATUS RESTARTS AGE
pod 1/1 Running 0 46s

# 删除指定pod
kubectl delete pod pod -n dev

命令式对象配置

命令式对象配置就是使用命令配合配置文件一起来操作Kubernetes Resource。 这种方式可以简单的认为是命令 + yaml配置文件(里面是命令需要的各种参数)

例如我们可以创建一个配置文件nginx-pod.yaml,这个配置文件基本描述了Pod运行的环境以及相关的属性,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Namespace
metadata:
name: dev

---

apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:latest

该配置文件完成了两个资源的定义。首先定义了一个namespace,名称为dev。然后定义了一个pod,该pod的名称为nginxpod,运行在dev namespace中,同时在详细信息spec中指定了容器的名称以及对应的镜像。

之后执行create命令进行资源创建,提示创建了namespace dev 以及pod nginxpod,可以通过相关get命令查看到创建出的资源。

1
2
3
kubectl create -f nginx-pod.yaml
# namespace/dev created
# pod/nginxpod created

同时也可以使用删除命令,同时删除这两个资源:

1
2
3
kubectl delete -f nginx-pod.yaml 
# namespace "dev" deleted
# pod "nginxpod" deleted

声明式对象配置

声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。

同样使用上面的实例配置文件,首先第一次执行apply命令,发现创建了资源。之后我们可以继续执行,发现资源没有变动。

1
2
3
4
5
6
7
kubectl apply -f nginx-pod.yaml     
# namespace/dev created
# pod/nginxpod created

kubectl apply -f nginx-pod.yaml
# namespace/dev unchanged
# pod/nginxpod unchanged

实际上,声明式对象配置使用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
2
3
4
apiVersion: v1
kind: Namespace
metadata:
name: dev

Pod

Pod是Kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。Kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的,例如可以通过kubectl get pod -n kube-system查看集群系统相关Pod。

我们可以通过kubectl run命令来指定在集群中运行Pod,例如:

1
2
3
4
5
kubectl run nginx --image=nginx:latest --port=80 --namespace dev 
# 命令格式: kubectl run (pod名称) [参数]
# --image 指定Pod的镜像
# --port 指定端口
# --namespace 指定namespace

同样也可以使用delete删除对应的pod。kubectl delete pod nginx -n dev

注意:在较老版本的kubectl中,run命令会生成deplyment来控制Pod,而在最新的kubectl中,run命令则是直接创建Pod。

  • 参考yaml配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP

Deployment

在Kubernetes中,Pod是最小的控制单元,但是Kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障时,会尝试进行重启或重建Pod。

Kubernetes中Pod控制器的种类有很多,在Getting Started中我们先介绍其中的Deployment。

可以通过下面的命令创建一个Deployment:

1
2
3
4
5
6
7
kubectl create deployment nginx --image=nginx:latest --port=80 --replicas=3 -n dev

# 命令格式: kubectl create deployment 名称 [参数]
# --image 指定pod的镜像
# --port 指定端口
# --replicas 指定创建pod数量
# --namespace 指定namespace

之后可以通过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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx:latest
name: nginx
ports:
- containerPort: 80
protocol: TCP

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
kubectl expose deploy nginx --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n dev

这里我们将名为nginx的deployment暴露为了一个ClusterIP类型的Service,可以通过kubectl get svc命令查看,可以发现其中有一个CLUSTER-IP字段,这就是Service的IP。在Service的生命周期中,这个地址是不会变动的,可以通过访问这个IP来访问当前Service对应的Pod。

同样我们也可以将nginx deployment暴露为一个NodePort类型的Service,只需要指定type为NodePort即可。

1
kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev

创建完成之后,同样可以查看svc详情,使用命令kubectl get svc svc-nginx2 -n dev -o wide。发现其中的端口映射如下:

1
2
NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
svc-nginx2 NodePort 10.97.137.15 <none> 80:31727/TCP 56m app=nginx

这表示将宿主机的31727端口和Deployment对应的80端口进行映射,此时可以使用浏览器访问http://localhost:31727,可以看见nginx的欢迎页面。这里的浏览器就是从集群之外进行访问的。

  • 参考配置yaml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.105.165.43 #固定svc的内网ip
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP # or NodePort

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
2
3
4
5
6
7
8
9
10
11
# 给Pod打Label
kubectl label pod nginx-cd55c47f5-jh9sf version=1.0 -n dev

# 更新Pod的Label [--overwrite]
kubectl label pod nginx-cd55c47f5-jh9sf version=2.0 -n dev -- overwrite

# 查看Label [--show-labels]
kubectl get pod nginx-cd55c47f5-jh9sf -n dev --show-labels

# 删除Label [-]
kubectl label pod nginx-cd55c47f5-jh9sf version- -n dev

基于标签,我们可以进行标签的选择。Kubernetes提供了Label Selector来完成这一操作。Label用于给某个资源对象定义标识,而Label Selector用于查询和筛选拥有某些标签的资源对象。

在Kubernetes中有两类Label Selector,分别是基于等式的Label Selector,使用关键字为=, !=;以及基于集合的Label Selector,使用关键字为in, not in。同时Label的选择条件也可以多次使用,使用逗号,将多个Label Selector进行分隔即可。

1
2
# 筛选Label [-l / --selector]
kubectl get pod -n dev -l version=2.0 --show-labels
  • 参考配置yaml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
version: "3.0"
env: "test"
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP

经过Getting Started的全流程,我们可以得到下面一张概述图,描述Resource的组织方式。


Kubernetes学习笔记(1)-简介与基本使用
http://example.com/2023/07/29/Kubernetes学习笔记-1-简介与基本使用/
作者
EverNorif
发布于
2023年7月29日
许可协议