Skip to main content

Kubernetes 101

·1750 字·4 分钟
云 & 云原生 kubernetes
Table of Contents

估计是继 Linux 后,目前影响最大的开源项目,概念比较多,比较复杂,文章稍长

  前提准备:K8s 学习前必须要先了解容器

Docker/容器入门(2020.9更新)
·8947 字·18 分钟
云 & 云原生 docker

Docker 🐳 是继 Java 十多年之后又一个“颠覆性”的技术,接触 Docker 三年有余,过去一年多给队伍做过多次介绍

功能概述 #

Kubernetes 定义为容器编排引擎(orchestration),Kubernetes 对于容器,就相当于 Linux 内核对于线程,考虑到容器作为云基础设施上的基本运行单位,Kubernetes 就类似云上的操作系统内核,其作用包含:
features

集群架构 #

architecture

Kubernetes 的组件分成两组 control panel & data panel 通常相应运行在两种机器上 master node & worker node

control panel(master node) #

master 控制节点负责管理集群,包含以下组件:

-   kube-apiserver
-   kube-controller-manager
-   kube-scheduler
-   etcd
master-node
master
kube-apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制,只有 API Server 会与 etcd 进行通信,其它模块都必须通过 API Server 访问集群状态。API Server 封装了核心资源对象的增删改查操作,以 RESTFul 接口方式提供给外部客户端和内部组件调用,API Server 再对相关的资源数据(全量查询 + 变化监听)进行操作,以达到实时完成相关的业务功能。
kube-controller-manager 负责维护各种资源对象,从现在状态(status)到用户想要的状态(spec),比如故障检测、自动扩展、滚动更新等,具体由各种 controller 负责执行:k8s-controller-manager 从逻辑上讲,每个控制器都是一个单独的进程,但为了降低复杂性,它们都被编译成单个二进制文件并在单个进程中运行。cloud-controller-manager 用于对接各种云基础设施(AWS,Azure,GCP 等)云厂商提供接口以便让 k8s 可以管理使用其各种资源。
kube-scheduler 负责资源的调度,主要用于收集和分析当前 Kubernetes 集群中所有负载节点的资源 (包括内存、CPU 等) 负载情况,并按照预定的调度策略将 Pod 调度到相应的机器上。调度策略是个比较复杂的问题,调度决策考虑的因素包括:个人和集体资源需求,硬件/软件/政策限制,亲和力和反亲和力规范,数据局部性,工作负载间的干扰,最后期限等。
etcd 保存了整个集群/资源的状态,是一个分布式 key-value 存储。

data panel(worker node) #

worker/slave 工作/负载节点运行具体的容器应用,执行并运行从 Master 传来的各种负载任务,包含以下组件:

-   Container Runtime
-   kubelet
-   kube-proxy
-   pod
worker-node
worker
kubelet 相当于 Master 的 agent,负责和 Master 的通信,维持 Pod/容器的生命周期,同时确定其符合 YAML 所定义的 PodSpec,其中包括关于 Volume(CVI)和网络(CNI)的管理。
kube-proxy 负责为 Service 提供内部的服务发现和负载均衡,把 request 转发到相关容器上。
Container runtime 负责镜像管理以及 Pod 和容器的真正运行时(CRI),默认的容器运行时为 Docker。
Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 k8s 调度的基本单位。Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。我们知道容器本质上就是进程,那么 Pod 实际上就是进程组了,只是这一组进程是作为一个整体来进行调度的。

此外还有非常重要的集群管理命令行客户端工具 Kubectl 通过 Kubectl 命令对 API Server 进行操作,API Server 响应并返回对应的命令结果,从而达到对 Kubernetes 集群的管理。命令主要模式:

kubectl [get|delete|edit|apply] [pods|deployment|services] [podName|serviceName|deploymentName]

Kubernetes 还有一些安装扩展才可以真正使用,包括网络 Calico/Flannel,CoreDNS,Dashboard 等,参考 安装扩展。这些组件构成了 Kubernetes 系统的物理基础,通过这些组件管理各种资源。

组件之间的通讯示意:

核心资源对象 #

和 OS 类似,Kubernetes 对底层静态和运行时的资源都进行了抽象,Kubernetes 的编排和管理功能就是通过对这些对象进行编排和管理

资源类别 #

核心资源对象类别
Workload:Pod、ReplicaSet、Deployment、StatefulSet、DaemonSet、Job 等
Networking:Service、Ingress 等
Storage: Volume、CSI、ConfigMap、Secret 等
RBAC:Namespace、None、Role、ClusterRole、RoleBinding、ClusterRoleBinding 等
元数据:HPA、PodTemplate、LimitRange 等


资源对象类型 workload
k8s-workloads


资源对象类型 networking
k8s-network


资源对象类型 storage
k8s-storage


资源对象类型 RBAC
k8s-rbac

通过这些资源体现了 Kubernetes 的能力(能干啥事):

k8s 管理资源示例:
k8s-resources

  Kubernetes 的入门就是理解和使用资源对象,详细的参考文档: 使用 Kubernetes 对象 | Kubernetes

  手动编辑和管理这些资源 definition file 是比较繁琐和易错的,有相应的 DevOps 工具来支撑,从学习的角度还是需要先了解一定的底层细节

  (具体如何管理各种资源的原理需要大量篇幅,这里不展开)

namespaces #

k8s 把容器 namespace 的概念放大到集群规模,通过 namespace 来组织资源,一个物理 Kubernetes 集群通过 namespace 划分成几个虚拟的集群:
kube_namespace

大部分资源对象都是具有 namespace 特性的,如果没有指定,默认 default - 同一台机器上可部署不同的 namespace 资源,它们是不相关的/相互隔离的,namespace 和 RBAC 资源管理紧密相关。有一些和 namespace 无关或者是跨 namespace 的资源 - 通常是和 cpu 算力、存储、网络相关,如 Nodes,PersisentVolumes,它们可以通过 namespace 的 Quotas 来限定。k8s 集群有三个默认的 namespace:default kube-system kube-public

没有完美的方法来划分 namespace,可以根据需要和用途,如:

  • 按项目或团队划分
  • 按部署环境划分,如 dev,staging,prod
  • 根据特殊要求划分,如 blue/green 部署

核心资源对象简介 #

抽象层现实层是否使用
namespace
资源描述
PodcontainerPod 是 k8s 最小的负载调度单元,等同一个或多个 container(但同属于一个 container namespace)
Replicasetload balancing跟踪并保证指定 pod 的实例个数
Deployment跟踪并保证指定 pod 和 replicaset 的配置
StatefulSet加强版的 deployment,StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设计),其应用场景包括: 1. 稳定的持久化存储,即 pod 重新调度后还是能访问到相同的持久化数据; 2. 稳定的网络标志,即 pod 重新调度后其 PodName 和 HostName 不变;3. 有序部署,有序扩展,即 pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依序进行(即从 0 到 N-1,在下一个 pod 运行之前所有之前的 pod 必须都是 Running 和 Ready 状态); 4. 有序收缩,有序删除(即从 N-1 到 0);
Nodehost运行 pod 的虚机或物理机器
Servicenetwork一组相关 pods 的入口点
Ingressreverse proxyingress 负责把 service 暴露到 k8s 外部
Clusterdatacenter一组相关 k8s 节点组成的集群,包括 master 控制节点和 worker 负载节点
Namespace在一个 k8s 集群内划分的虚拟集群
StorageClassdisk配置存储源用以创建 PersistentVolumens
PersistentVolumedisk partition配置文件系统,可以被挂载在 Pod 中一个或者多个容器的指定路径下面
PersistentVolumeClaim把 PersistentVolume 绑定到 pod 上
ConfigMapenv var定义各种 properties
Secretsecured env var加密或带有使用权限的 properties

资源配置文件 definition file #

在 Kubernetes 中,所有资源对象都在配置文件(yaml 或 json)中定义/声明,基本格式:

apiVersion:
kind:
    # 资源类别,标记创建什么类型的资源: Pod, Deployment, Job 等
metadata:
    # 元数据内部是嵌套的字段
    # 定义了资源对象的名称、命名空间(k8s级别的不是系统的)等、标签、注解、UID等
    annotations:
    name: myproj-mysql
    labels: # labels recommended by k8s
        app.kubernetes.io/name: mysql
        app.kubernetes.io/instance: mysql-abcxzy
        app.kubernetes.io/version: '5.7.21'
        app.kubernetes.io/component: database
        app.kubernetes.io/part-of: wordpress
        app.kubernetes.io/managed-by: helm
        app.kubernetes.io/created-by: controller-manager
data:
    # 针对数据资源,如 Secret, ConfigMap
spec:
    # 规范定义资源应该拥有什么样的特性,依靠控制器确保特性能够被满足,各种资源有自己的spec
    # 用户定义所期望了资源状态
    containers:
        - name:
          image:
          ports:
              - containerPort:
          env:
              - name:
                value:
status:
    # 显示资源的当前状态,k8s 就是确保当前状态向目标状态无限靠近从而满足用户期望
    # 它是只读的,代表了资源当前状态
  • Labels

labels 标签 以 key/value 的方式附加到对象上(key 最长不能超过 63 字节,value 可以为空,也可以是不超过 253 字节的字符串)。 Label 定义好后其他对象可以使用 Selector 来选择一组相同 label 的对象(比如 ReplicaSet 和 Service 用 label 来选择一组 Pod)。Label Selector 支持以下几种方式:

-   等式,如 app=nginx 和 env!=production
-   集合,如 env in (production, qa)
-   多个 label(它们之间是 AND 关系),如 app=nginx,env=test

标签对于灵活访问 K8s 资源对象非常重要,在 kubectl 可以选择或过滤 Pod:

kubectl get pods -l "app=firstapp" --show-labels
kubectl get pods -l "app=firstapp,tier=frontend" --show-labels
kubectl get pods -l "app=firstapp,tier!=frontend" --show-labels
kubectl get pods -l "app,tier=frontend" --show-labels #equality-based selector
kubectl get pods -l "app in (firstapp)" --show-labels  #set-based selector
kubectl get pods -l "app not in (firstapp)" --show-labels  #set-based selector
kubectl get pods -l "app=firstapp,app=secondapp" --show-labels # comma means and => firstapp and secondapp
kubectl get pods -l "app in (firstapp,secondapp)" --show-labels # it means or => firstapp or secondapp
  • nodeSelector

有了 nodeSelector 我们可以指定 Pod 在哪个节点上运行:

kubectl apply -f podnode.yaml
kubectl get pods -w #always watch
kubectl label nodes minikube hddtype=ssd #after labelling node, pod11 configuration can run, because node is labelled with hddtype:ssd
  • Annotations

Annotations 是 key/value 形式附加于对象的注解。不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。比如 deployment 使用 annotations 来记录 rolling update 的状态。

资源实例 #

下面介绍几个核心资源具体配置实例:

  • ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
    name: simple-web-config
    namespace: default
data:
    configuration_key: 'Configuration value'

ConfigMap 可以通过三种方式在 Pod 中调用,三种分别方式为:设置环境变量、设置容器命令行参数以及在 PersistentVolume 中直接挂载文件或目录。

  • Secret

data 默认下知识 base64 编码,不过 k8s 支持 加密或者和 role-based access control

apiVersion: v1
kind: Secret
metadata:
    name: simple-web-secrets
# Opaque can hold generic secrets, so no validation will be done.
type: Opaque
data:
    # Secrets should be encoded in base64
    secret_configuration_key: 'c2VjcmV0IHZhbHVl'
  • Pod
apiVersion: v1
kind: Pod
metadata:
    name: my-web-server
spec:
    # <containers> is a list of container definition to embed in the pod
    containers:
        - name: web
          image: nginx
          ports:
              - name: web
                containerPort: 80
                protocol: TCP
          env:
              - name: SOME_CONFIG
                # Create a line "value: <config_entry>" from the ConfigMap data
                valueFrom:
                    configMapKeyRef:
                        name: simple-web-config
                        key: configuration_key
              - name: SOME_SECRET
                # Create a line "value: <config_entry>" from the Secret data
                valueFrom:
                    secretKeyRef:
                        name: simple-web-secrets
                        key: secret_configuration_key
  • Replica Set

声明 Pod 的横向水平扩展,selector 标签选择器,可以使用 matchLabels、matchExpressions 两种类型的选择器来选中目标 Pod:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
    name: myrs
    namespace: default
spec:
    replicas: 2
    selector:
        matchLabels:
            app: myapp
            release: canary
    template:
        metadata:
            name: myapp-pod
            labels:
                app: myapp # 标签一定要符合 replicaset 标签选择器的规则
                release: canary
        spec:
            containers:
                - name: myapp-containers
                  image: ikubernetes/myapp:v1
                  ports:
                      - name: http
                        containerPort: 80
  • Deployment

一般不直接管理 Pod,而是通过 Deployment 来管理应用程序。Deployment 通过控制 ReplicaSet 来实现功能,除了支持 ReplicaSet 的扩缩容意外,还支持滚动更新和回滚等,还提供了声明式的配置,这个是我们日常使用最多的控制器。它是用来管理无状态的应用。Deployment 在滚动更新时候,通过控制多个 ReplicaSet 来实现,ReplicaSet 又控制多个 POD,多个 ReplicaSet 相当于多个应用的版本。

k8s 基于 template 创建 Pod
k8s 基于 selector 创建 Replicaset,实例数目有 replicas 指定

apiVersion: apps/v1
kind: Deployment
metadata:
    name: my-web-server-deployment
    namespace: default
    labels:
        app: webserver
spec:
    # <selector> should retrieve the Pod defined below, and possibly more
    selector:
        matchLabels:
            app: webserver
            instance: nginx-ws-deployment
    # <replicas> asks for 3 pods running in parallel at all time
    replicas: 3
    # The content of <template> is a Pod definition file, without <apiVersion> nor <kind>
    template:
        metadata:
            name: my-web-server
            namespace: default
            labels:
                app: webserver
                instance: nginx-ws-deployment
        spec:
            containers:
                - name: web
                  image: nginx
                  ports:
                      - name: web
                        containerPort: 80
                        protocol: TCP
                  env:
                      - name: SOME_CONFIG
                        # Create a line "value: <config_entry>" from the ConfigMap data
                        valueFrom:
                            configMapKeyRef:
                                name: simple-web-config
                                key: configuration_key
                      - name: SOME_SECRET
                        # Create a line "value: <config_entry>" from the Secret data
                        valueFrom:
                            secretKeyRef:
                                name: simple-web-secrets
                                key: secret_configuration_key
  • Service

由于 Pod 可以被动态创建,所以其提供的服务不能通过其 ip 地址直接访问。Service 为 Pod 控制器控制的 Pod 实例提供一个固定的访问端点 - 一个 k8s 集群内部的虚拟 ip,集群内部通过虚拟 ip 访问一个服务,负载均衡由 Kube-proxy 实现。Service 的工作还依赖于一个安装扩展,就是 CoreDNS ,它将 Service 地址提供一个域名解析。组成 Service 的 Pod 由 selector 里的 lables 来选定。

有三种 service:

  • ClusterIP:在集群内部 IP 上公开服务,选择此值使得服务只能从集群内部访问,默认方式

  • NodePort:把 Service 绑定在 Pod 所在节点的一个静态端口 Node Port(30000-32767),NodePort 服务到 ClusterIP 服务的路由会自动创建。这样能够从集群外部联系 NodePort 来请求服务(由于要公开 Node Port,这种 Servide 会带来安全隐患)

  • LoadBalancer:使用云提供商的负载均衡器向外部公开服务,负载均衡器到 Node Port 或 ClusterIP 服务的路由是自动创建的

  • ExternalName:通过返回 CNAME 记录及其值,将服务映射到 externalName 字段的内容(例如 foo.bar.example.com)

apiVersion: v1
kind: Service
metadata:
    name: simple-web-service-clusterip
spec:
    # ClusterIP is the default service <type>
    type: ClusterIP
    # Select all pods declaring a <label> entry "app: webserver"
    selector:
        app: webserver
    ports:
        - name: http
          protocol: TCP
          # <port> is the port to bind on the service side
          port: 80
          # <targetPort> is the port to bind on the Pod side
          targetPort: 80

了解 Service 需要先了解 K8s 网络如何工作。

K8s 网络 #

K8s 自身的网络设计:

  • 每个 Pod 都有唯一的、自己的 IP 地址(Pod 内的容器共享网络命名空间)
  • Pod 的 IP 在整个集群中是唯一的
  • 所有 Pod 都可以与所有其他 Pod 通信,无需 NAT(网络地址转换)
  • 所有 Node 都可以与所有 Pod 通信,无需 NAT

不同供应商和设备的容器和节点的联网很难处理。因此,K8s 将这个责任交给了 CNI(Container Network Interface 容器网络接口) 来处理网络需求。CNI 是一个云原生计算基金会项目,由用于编写插件以在 Linux 容器中配置网络接口的规范和库组成,以及许多受支持的插件。用户选择的 CNI 插件包括:Flannel,Calico,Weave, 和 Canal,其中 Calico 是比较流行的开源 CNI 插件之一。

部署资源 #

资源对象配置文件定义好后,就可以通过 kubectl 来管理

# <kind> is the type of resource to create (e.g. deployment, secret, namespace, quota, ...)
$ kubectl create <kind> <name>
$ kubectl edit   <kind> <name>
$ kubectl delete <kind> <name>

# All those commands can be used through a description file.
$ kubectl create -f <resource>.yaml
$ kubectl edit   -f <resource>.yaml
# create & update
$ kubectl apply  -f <resource>.yaml
$ kubectl delete -f <resource>.yaml
更新和回滚
maxUnavailable:在更新期间,显示删除的容器的最大数量(总计:10 个容器;如果 maxUn:2,则该时间段内运行的最小:8 个容器)
maxSurge:在更新期间,显示集群上运行的最大容器数(总计:10 个容器;如果 maxSurge:2,则一次最多运行 12 个容器)
重新创建策略:先删除所有 Pod,然后从头创建 Pod。如果两个不同版本的软件相互产生负面影响,则可以使用此策略。
滚动策略(默认):逐步更新 Pod。Pod 是逐步更新的,所有 Pod 不会同时删除。
kubectl set image deployment rolldeployment nginx=httpd:alpine --record     # change image of deployment
kubectl rollout history deployment rolldeployment                           #shows record/history revisions
kubectl rollout history deployment rolldeployment --revision=2              #select the details of the one of the revisions
kubectl rollout undo deployment rolldeployment                              #returns back to previous deployment revision
kubectl rollout undo deployment rolldeployment --to-revision=1              #returns back to the selected revision=1
kubectl rollout status deployment rolldeployment -w                         #show live status of the rollout deployment
kubectl rollout pause deployment rolldeployment                             #pause the rollout while updating pods
kubectl rollout resume deployment rolldeployment                            #resume the rollout if rollout paused

内部架构 #

Kubernetes 虽然庞大复杂,但是其架构和设计机理却是很清晰。

k8s-layers
分层 & 插件架构
核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)
接口层:kubectl 命令行工具、客户端 SDK 以及集群联邦
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
 - Kubernetes 外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS 应用、ChatOps 等
 Kubernetes 内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等

核心组件:
k8s-core-components

生态系统架构:
k8s-ecosystem

Kubernetes 以及云原生 #

以 Kubernetes 为基础成长的云原生生态: Cloud Native Landscape

技术是生产力发展的核心,发生第二次工业革命是因为有了蒸汽动力等技术的出现,发生 Internet 是因为有了 HTML,CSS,Tcp/Ip 等技术,发生 digital transformation 是因为有了 Kubernetes 等云原生技术,其中的一些显著特点:

  • 轻量级的容器技术

  • 不依赖底层 infrastructure 或服务,包括 OS,VM,物理机器,以及各种云基础设施

  • 声明式资源管理

  • 按需自动弹性扩展

  • 拥有强壮性和自愈功能

  • 编程语言无关

  • API 驱动

  • Stateless/Stateful 应用分离

  • 微服务架构

  • 敏捷和自动化 DevOps

  • 自服务

云原生众说纷纭,我的看法就是容器和 Kubernetes,因为还未看到有第二种技术来实现支持上述特性。

Kubernetes 相当一个云操作系统内核,内外沿涉及到的东西几乎是个无底洞,不是 3-5 个小时或 3-5 天就能搞定,慢慢来。