Kubernetes

零基础快速入门Kubernetes

2022-03-23  本文已影响0人  王勇1024

Kubernetes 简介

Kubernetes 是什么?

Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。

Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。Kubernetes 建立在 Google 在大规模运行生产工作负载方面拥有十几年的经验 的基础上,结合了社区中最好的想法和实践。

Kubernetes能做什么?

利用 Kubernetes,您能够达到以下目标:

Kubernetes特性

Kubernetes架构

kubernetes架构

Kubernetes 主要由以下几个核心组件组成:

控制平面组件(Control Plane Components)

控制平面的组件对集群做出全局决策(比如调度),以及检测和响应集群事件(例如,当不满足部署的 replicas 字段时,启动新的 pod)。

控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器。

kube-apiserver

API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。

Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

kube-apiserver

etcd

etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。

您的 Kubernetes 集群的 etcd 数据库通常需要有个备份计划。

要了解 etcd 更深层次的信息,请参考 etcd 文档

kube-scheduler

控制平面组件,负责监视新创建的、未指定运行节点(node)Pods,选择节点让 Pod 在上面运行。

调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

如果Pod中指定了NodeName属性,则无需Scheduler参与,Pod会直接被调度到NodeName指定的Node节点

kube-controller-manager

运行控制器进程的控制平面组件。

从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。

这些控制器包括:

kube-controller-manager

kube-controller-manager 中的各种控制器:

Node 组件

节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。

kubelet

每个Node节点上都运行一个 Kubelet 服务进程,默认监听 10250 端口,接收并执行 Master 发来的指令,管理 Pod 及 Pod 中的容器。每个 Kubelet 进程会在 API Server 上注册所在Node节点的信息,定期向 Master 节点汇报该节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。

kubelet

kube-proxy

kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。

kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。

如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。

容器运行时(Container Runtime)

容器运行环境是负责运行容器的软件。

Kubernetes 支持多个容器运行环境: DockercontainerdCRI-O 以及任何实现 Kubernetes CRI (容器运行环境接口)

工作负载

Kubernetes 提供若干种内置的工作负载资源,可满足不同业务场景的需要。

Pod

Kubernetes 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。

Pod 是一组紧密关联的容器集合,它们共享 IPC 和 Network namespace,是 Kubernetes 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。

kubelet

Pod 定义

在 Kubernetes 中,所有对象都使用 manifest(yaml 或 json)来定义,比如一个简单的 nginx 服务可以定义为 nginx.yaml,它包含一个镜像为 nginx 的容器:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80

Pod 的特征

Node

Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、kubelet 和 kube-proxy 服务。

不像其他的资源(如 Pod 和 Namespace),Node 本质上不是 Kubernetes 来创建的,Kubernetes 只是管理 Node 上的资源。虽然可以通过 Manifest 创建一个 Node 对象(如下 yaml 所示),但 Kubernetes 也只是去检查是否真的是有这么一个 Node,如果检查失败,也不会往上调度 Pod。

kind: Node
apiVersion: v1
metadata:
  name: 10-240-79-157
  labels:
    name: my-first-k8s-node

Node 的状态

每个 Node 都包括以下状态信息:

操作Node

# 查看 Node 列表
kubectl get nodes
# 查看指定 Node 概要信息
kubectl get node $NODENAME -owide
# 查看指定 Node 详细信息
kubectl describe node $NODENAME
# 给 Node 打标签
kubectl label nodes node-01 disktype=ssd
# 删除标签
kubectl label nodes node-01 disktype-
# 禁止调度
kubectl cordon $NODENAME
# 启用调度
kubectl uncordon $NODENAME

Namespace

Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 pods, services, replication controllers 和 deployments 等都是属于某一个 namespace 的(默认是 default),而 node, persistentVolumes 等则不属于任何 namespace。

Namespace 常用来隔离不同的用户,比如 Kubernetes 自带的服务一般运行在 kube-system namespace 中。

Namespace 操作

kubectl 可以通过 --namespace 或者 -n 选项指定 namespace。如果不指定,默认为 default。查看操作下, 也可以通过设置 --all-namespace=true 来查看所有 namespace 下的资源。

查询
$ kubectl get namespaces
NAME          STATUS    AGE
default       Active    11d
kube-system   Active    11d
创建
#(1) 命令行直接创建
$ kubectl create namespace new-namespace

#(2) 通过文件创建
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: new-namespace

$ kubectl create -f ./my-namespace.yaml

注意:命名空间名称满足正则表达式 a-z0-9?, 最大长度为 63 位

删除
$ kubectl delete namespaces new-namespace

注意:

  1. 删除一个 namespace 会自动删除所有属于该 namespace 的资源。

  2. default 和 kube-system 命名空间不可删除。

Replication Controller

ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态。 换句话说,ReplicationController 确保一个 Pod 或一组同类的 Pod 总是可用的。

当 Pod 数量过多时,ReplicationController 会终止多余的 Pod。当 Pod 数量太少时,ReplicationController 将会启动新的 Pod。 与手动创建的 Pod 不同,由 ReplicationController 创建的 Pod 在失败、被删除或被终止时会被自动替换。 例如,在中断性维护(如内核升级)之后,你的 Pod 会在节点上重新创建。 因此,即使你的应用程序只需要一个 Pod,你也应该使用 ReplicationController 创建 Pod。 ReplicationController 类似于进程管理器,但是 ReplicationController 不是监控单个节点上的单个进程,而是监控跨多个节点的多个 Pod。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

确保Pod数量

弹性伸缩

ReplicationController 的替代方案

ReplicaSet 是下一代 ReplicationController, 支持新的基于集合的标签选择算符。 它主要被 Deployment 用来作为一种编排 Pod 创建、删除及更新的机制。 请注意,我们推荐使用 Deployment 而不是直接使用 ReplicaSet,除非 你需要自定义更新编排或根本不需要更新。

Deployment 是一种更高级别的 API 对象, 它以类似于 kubectl rolling-update 的方式更新其底层 ReplicaSet 及其 Pod。 如果你想要这种滚动更新功能,那么推荐使用 Deployment,因为与 kubectl rolling-update 不同, 它们是声明式的、服务端的,并且具有其它特性。

Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationController 来方便的管理应用。

apiVersion: apps/v1      
kind: Deployment         
metadata:
  name: nginx            
spec:
  replicas: 3                    
  selector:              
    matchLabels:
      app: nginx
  template:              
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        name: container-0
        resources:  # 资源限制
          limits:
            cpu: 100m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
      imagePullSecrets:
      - name: default-secret

操作 Deployment

# 创建 Deployment
$ kubectl create -f docs/user-guide/nginx-deployment.yaml --record
deployment "nginx-deployment" created

# 查看 Deployment
$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         0         0            0           1s

# 扩容
$ kubectl scale deployment nginx-deployment --replicas 10

# 如果集群支持 horizontal pod autoscaling 的话,还可以为 Deployment 设置自动扩展:
$ kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80

# 更新镜像也比较简单
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

# 回滚
$ kubectl rollout undo deployment/nginx-deployment

Deployment 的 典型应用场景 包括:

DaemonSet

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

DaemonSet 的一些典型用法:

指定 Node 节点

DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上:

StatefulSet

StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设计),其应用场景包括

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

MySQL示例

Job

Job 负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

CronJob

CronJob 即定时任务,就类似于 Linux 系统的 crontab,在指定的时间周期运行指定的任务。

CronJob 配置参数:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

负载均衡

Pod创建完成后,如何访问Pod呢?直接访问Pod会有如下几个问题:

Service

Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。

每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。

apiVersion: v1
kind: Service
metadata:
  name: nginx        # Service的名称
spec:
  selector:          # Label Selector,选择包含app=nginx标签的Pod
    app: nginx
  ports:
  - name: service0
    targetPort: 80   # Pod的端口
    port: 8080       # Service对外暴露的端口
    protocol: TCP    # 转发协议类型,支持TCP和UDP
  type: ClusterIP    # Service的类型

Pod、RC和Service的关系

Service的类型

Ingress

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。

下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:

YAML 示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

Ingress 规则

每个 HTTP 规则都包含以下信息:

通常在 Ingress 控制器中会配置 defaultBackend(默认后端),以服务于任何不符合规约中 path 的请求。

DefaultBackend

没有 rules 的 Ingress 将所有流量发送到同一个默认后端。 defaultBackend 通常是 Ingress 控制器 的配置选项,而非在 Ingress 资源中指定。

如果 hosts 或 paths 都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端。

配置和存储

ConfigMap

应用程序的运行可能会依赖一些配置,而这些配置又是可能会随着需求产生变化的,如果我们的应用程序架构不是应用和配置分离的,那么就会存在当我们需要去修改某些配置项的属性时需要重新构建镜像文件的窘境。现在,ConfigMap 可以很好的帮助我们实现应用和配置分离,避免因为修改配置项而重新构建镜像。

ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

注意:

ConfigMap 并不提供保密或者加密功能。 如果你想存储的数据是机密的,请使用 Secret, 或者使用其他第三方工具来保证你的数据的私密性,而不是用 ConfigMap。

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 类属性键;每一个键都映射到一个简单的值
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # 类文件键
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true  

你可以使用四种方式来使用 ConfigMap 配置 Pod 中的容器:

  1. 在容器命令和参数内

  2. 容器的环境变量

  3. 在只读卷里面添加一个文件,让应用来读取

  4. 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # 定义环境变量
        - name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
          valueFrom:
            configMapKeyRef:
              name: game-demo           # 这个值来自 ConfigMap
              key: player_initial_lives # 需要取值的键
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    # 你可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
    - name: config
      configMap:
        # 提供你想要挂载的 ConfigMap 的名字
        name: game-demo
        # 来自 ConfigMap 的一组键,将被创建为文件
        items:
        - key: "game.properties"
          path: "game.properties"
        - key: "user-interface.properties"
          path: "user-interface.properties"

ConfigMap 使用注意点

Secret

Secret 是在集群中用于存储密码、token等敏感信息用的资源对象。其中的敏感数据采用 base-64 编码保存,相比存储在明文的 ConfigMap 中更规范、更安全。

Kubernetes Secret 默认情况下存储为 base64-编码的、非加密的字符串。 默认情况下,能够访问 API 的任何人,或者能够访问 Kubernetes 下层数据存储(etcd) 的任何人都可以以明文形式读取这些数据。 为了能够安全地使用 Secret,我们建议你(至少):

  1. 为 Secret 启用静态加密

  2. 启用 或配置 RBAC 规则来限制对 Secret 的读写操作。 要注意,任何被允许创建 Pod 的人都默认地具有读取 Secret 的权限。

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  # 你可以像 Opaque Secret 一样在这里添加额外的键/值偶对
  extra: YmFyCg==

LocalVolume

本地数据卷(Local Volume)代表一个本地存储设备,比如磁盘、分区或者目录等。主要的应用场景包括分布式存储和数据库等需要高性能和高可靠性的环境里。本地数据卷同时支持块设备和文件系统,通过 spec.local.path 指定;但对于文件系统来说,kubernetes 并不会限制该目录可以使用的存储空间大小。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: nginx
    name: mynginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: html
  volumes:
  - name: html # 名称
    hostPath: # 存储类型
      path: /data # 物理节点上的真实路径
      type: Directory # 如果该路径不存在讲如何处理,Directory是要求目录必须存在

PV、PVC和 StorageClass

参考:k8s之PV、PVC、StorageClass详解

Kubernetes管理员设置好网络存储的类型,提供对应的PV描述符配置到Kubernetes,使用者需要存储的时候只需要创建PVC,然后在Pod中使用Volume关联PVC,即可让Pod使用到存储资源,它们之间的关系如下图所示。

Pod 进阶

Pause Container

Pause容器,又叫Infra容器,它与用户容器”捆绑“运行在同一个 Pod 中,最大的作用是维护 Pod 网络协议栈。

Pause容器是一个非常小的镜像,大概 700KB 左右,是一个C语言写的、永远处于“暂停”状态的容器。由于有了这样一个 Pause Container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Pause Container 的 Network Namespace 中。

所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP地址、Mac地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。

它执行另一个重要的功能——即它扮演 PID 1 的角色,并在子进程成为孤儿进程的时候通过调用 wait() 收割这些僵尸子进程。这样我们就不用担心我们的 Pod 的 PID namespace 里会堆满僵尸进程了。在 UNIX 系统中,PID 为 1 的进程是 init 进程,即所有进程的父进程。init 进程比较特殊,它维护一张进程表并且不断地检查其他进程的状态。init 进程的其中一个作用是当某个子进程由于父进程的错误退出而变成了“孤儿进程”,便会被 init 进程收养并在该进程退出时回收资源。

Pause容器特点

Pause容器功能

Init Container

Init Container是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的Init Container执行完后,主容器才会被启动。我们知道一个Pod里面的所有容器是共享数据卷和网络命名空间的,所以Init Container里面产生的数据可以被主容器使用到的。

Init Container 的应用场景:

下面的例子定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice 启动, 第二个等待 mydb 启动。 一旦这两个 Init容器 都启动完成,Pod 将启动 spec 节中的应用容器。

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

Pod Hook

Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook。

Hook 的类型包括两种:

参考下面的配置:

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler> /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

在容器创建之后,容器的 Entrypoint 执行之前,这时候 Pod 已经被调度到某台 node 上,被某个 kubelet 管理了,这时候 kubelet 会调用 postStart 操作,该操作跟容器的启动命令是在同步执行的,也就是说在 postStart 操作执行完成之前,kubelet 会锁住容器,不让应用程序的进程启动,只有在 postStart 操作完成之后容器的状态才会被设置成为 RUNNING。

如果 postStart 或者 preStop hook 失败,将会终止容器。

健康检查

Pod的健康状态由三类探针来检查:LivenessProbe、ReadinessProbe和 StartupProbe。

探针类型

1. livenessProbe(存活探针)

2. readinessProbe(就绪探针)

3. startup probes(启动探针)

三种健康检查方法

每种探测机制支持三种健康检查方法,分别是命令行exec,httpGet和tcpSocket,其中exec通用性最强,适用与大部分场景,tcpSocket适用于TCP业务,httpGet适用于web业务。

配置探针

Probe 有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:

apiVersion: v1
kind: Pod
metadata:
  name: exec-liveness-probe
  annotations:
    kubernetes.io/description: "exec-liveness-probe"
spec:
  containers:
  - name: exec-liveness-probe
    image: centos:latest
    imagePullPolicy: IfNotPresent
    args:    #容器启动命令,生命周期为30s
    - /bin/sh
    - -c
    - touch /tmp/liveness-probe.log && sleep 10 && rm -f /tmp/liveness-probe.log && sleep 20
    livenessProbe:
      exec:  #健康检查机制,通过ls -l /tmp/liveness-probe.log返回码判断容器的健康状态
        command:
        - ls 
        - l 
        - /tmp/liveness-probe.log
      initialDelaySeconds: 1
      periodSeconds: 5

生命周期

Pod生命周期

Pod的状态

阶段 描述
Pending Pod 已被 Kubernetes 接受,但尚未创建一个或多个容器镜像。这包括被调度之前的时间以及通过网络下载镜像所花费的时间,执行需要一段时间。
Running Pod 已经被绑定到了一个节点,所有容器已被创建。至少一个容器正在运行,或者正在启动或重新启动。
Succeeded 所有容器成功终止,也不会重启。
Failed 所有容器终止,至少有一个容器以失败方式终止。也就是说,这个容器要么已非 0 状态退出,要么被系统终止。
Unknown 由于一些原因,Pod 的状态无法获取,通常是与 Pod 通信时出错导致的。

Pod的生命周期

Pod的状态

配置 Pod

环境变量

环境变量为容器提供了一些重要的资源,包括容器和 Pod 的基本信息以及集群中服务的信息等:

(1) hostname

HOSTNAME 环境变量保存了该 Pod 的 hostname。

(2)容器和 Pod 的基本信息

Pod 的名字、命名空间、IP 以及容器的计算资源限制等可以以 Downward API 的方式获取并存储到环境变量中。

使用 Volume

Volume 可以为容器提供持久化存储,比如

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}

更多挂载存储卷的方法参考 Volume

RestartPolicy

支持三种 RestartPolicy

注意,这里的重启是指在 Pod 所在 Node 上面本地重启,并不会调度到其他 Node 上去。

镜像拉取策略

支持三种 ImagePullPolicy

注意:

资源限制

Kubernetes 通过 cgroups 限制容器的 CPU 和内存等计算资源,包括 requests(请求,调度器保证调度到资源充足的 Node 上,如果无法满足会调度失败)和 limits(上限)等:

比如 nginx 容器请求 30% 的 CPU 和 56MB 的内存,但限制最多只用 50% 的 CPU 和 128MB 的内存:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
    - image: nginx
      name: nginx
      resources:
        requests:
          cpu: "300m"
          memory: "56Mi"
        limits:
          cpu: "500m"
          memory: "128Mi"

注意

QoS

QoS是 Quality of Service 的缩写,即服务质量。Kubernetes根据Pod中Containers Resource的request和limit的值来定义Pod的QoS Class。其中,指定容器request,代表系统确保能够提供的资源下限值。指定容器limit,代表系统允许提供的资源上限值。

QoS 主要分为Guaranteed、Burstable 和 Best-Effort三类,优先级从高到低。

调度控制

Pod调度策略除了系统默认的kube-scheduler调度器外还有以下几种实现方式:

  1. nodeName(直接指定Node主机名)

  2. nodeSelector (节点选择器,为Node打上标签,然后Pod中通过nodeSelector选择打上标签的Node)

  3. 污点taint与容忍度tolerations

  4. NodeAffinity 节点亲和性

  5. PodAffinity Pod亲和性

  6. PodAntAffinity Pod反亲和性

更多信息参考:https://cloud.tencent.com/developer/article/1644857

NodeName

NodeNamed这种调度方式比较简单,我们可以指定Pod在哪台Node上进行运行,通过spec.nodeName参数来指定Node主机名称即可。

apiVersion: v1
kind: Pod
metadata:
  name: nodename-pod
spec:
#指定该Pod运行在k8s-node02节点上
  nodeName: k8s-node02
  containers:
  - image: busybox:latest
    name: nodename-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

NodeSelector

NodeSelector用于将Pod调度到匹配Label的Node上,所以要先给node打上标签,然后在Pod配置清单中选择指定Node的标签。先给规划node用途,然后打标签,例如将两台node划分给不同团队使用:

首先给 Node 打上标签

kubectl label nodes node-01 team=development

然后在 pod 中指定 nodeSelector 为 team=development:

apiVersion: v1
kind: Pod
metadata:
  name: nodeselector-pod
spec:
  nodeSelector:                      #指定标签选择器
    team: development                #label指定开发团队的label
  containers:
  - image: busybox:latest
    name: nodeselector-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

NodeAffinity

NodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。比如下面的例子代表调度到包含标签 kubernetes.io/e2e-az-name 并且值为 e2e-az1 或 e2e-az2 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的 Node。

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

PodAffinity

PodAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件 Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.io/google_containers/pause:2.0

自定义资源

我们平时在部署一个简单的 Webserver 到 Kubernetes 集群中的时候,都需要先编写一个 Deployment 的控制器,然后创建一个 Service 对象,通过 Pod 的 label 标签进行关联,最后通过 Ingress 或者 type=NodePort 类型的 Service 来暴露服务,每次都需要这样操作,是不是略显麻烦,我们就可以创建一个自定义的资源对象,通过我们的 CRD 来描述我们要部署的应用信息,比如镜像、服务端口、环境变量等等,然后创建我们的自定义类型的资源对象的时候,通过控制器去创建对应的 Deployment 和 Service,是不是就方便很多了,相当于我们用一个资源清单去描述了 Deployment 和 Service 要做的两件事情。

CRD

CRD(CustomResourceDefinition,自定义资源)是对 Kubernetes API 的扩展,kubernetes 中的每个资源都是一个 API 对象的集合,例如我们在 YAML 文件里定义的那些 spec 都是对 kubernetes 中的资源对象的定义,所有的自定义资源可以跟 kubernetes 中内建的资源一样使用 kubectl 操作。

创建 CRD

创建新的 CustomResourceDefinition(CRD)时,Kubernetes API Server 会为您指定的每个版本创建新的 RESTful 资源路径。CRD 可以是命名空间的,也可以是集群范围的,可以在 CRD scope 字段中所指定。与现有的内置对象一样,删除命名空间会删除该命名空间中的所有自定义对象。CustomResourceDefinition 本身是非命名空间的,可供所有命名空间使用。

参考下面的 CRD,将其配置保存在 resourcedefinition.yaml 文件中:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名称必须符合下面的格式:<plural>.<group>
  name: crontabs.stable.example.com
spec:
  # REST API使用的组名称:/apis/<group>/<version>
  group: stable.example.com
  # REST API使用的版本号:/apis/<group>/<version>
  versions:
    - name: v1
      # 可以通过 served 来开关每个 version
      served: true
      # 有且仅有一个 version 开启存储
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # Namespaced或Cluster
  scope: Namespaced
  names:
    # URL中使用的复数名称: /apis/<group>/<version>/<plural>
    plural: crontabs
    # CLI中使用的单数名称
    singular: crontab
    # CamelCased格式的单数类型。在清单文件中使用
    kind: CronTab
    # CLI中使用的资源简称
    shortNames:
    - ct

创建该 CRD:

kubectl create -f resourcedefinition.yaml

创建自定义对象

创建 CustomResourceDefinition 对象后,您可以创建自定义对象。自定义对象可包含自定义字段。这些字段可以包含任意 JSON。在以下示例中, cronSpec 和 image 自定义字段在自定义对象中设置 CronTab。CronTab 类型来自您在上面创建的 CustomResourceDefinition 对象的规范。

如果您将以下 YAML 保存到 my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

并创建它:

kubectl create -f my-crontab.yaml

然后,您可以使用 kubectl 管理 CronTab 对象。例如:

$ kubectl get crontab
NAME                 AGE
my-new-cron-object   6s

使用 kubectl 时,资源名称不区分大小写,您可以使用 CRD 中定义的单数或复数形式,以及任何短名称。

您还可以查看原始 YAML 数据:

kubectl get ct -o yaml

删除CRD

删除 CustomResourceDefinition 时,服务器将删除 RESTful API 端点并删除存储在其中的所有自定义对象

kubectl delete -f resourcedefinition.yaml
kubectl get crontabs
Error from server (NotFound): Unable to list "crontabs": the server could not find the requested resource (get crontabs.stable.example.com)

Operator

Kubernetes Operator 是一种封装、部署和管理 Kubernetes 应用的方法。我们使用 Kubernetes API(应用编程接口)和 kubectl 工具在 Kubernetes 上部署并管理 Kubernetes 应用。

Kubernetes Operator 是一种特定于应用的控制器,可扩展 Kubernetes API 的功能,来代表 Kubernetes 用户创建、配置和管理复杂应用的实例。

Operator SDK

该项目是Operator Framework的一个组件,它是一个开源工具包,以有效,自动化和可扩展的方式管理称为Operators的Kubernetes原生应用程序。

Operator SDK是一个框架,旨在简化Operator的编写,它提供如下功能:

安装 Operator SDK

$ mkdir -p $GOPATH/src/github.com/operator-framework
$ cd $GOPATH/src/github.com/operator-framework
$ git clone https://github.com/operator-framework/operator-sdk
$ cd operator-sdk
$ git checkout master
$ make dep
$ make install

该过程需要几分钟,请耐心等待。确认 $GOPATH/bin/operator-sdk 文件位于您的 $PATH 目录下。

创建项目

$ cd $GOPATH/src/github.com/<your-github-repo>/
$ operator-sdk new <operator-project-name> --api-version=<your-api-group>/<version> --kind=<custom-resource-kind>
$ cd <operator-project-name>

更多信息参考:Kubernetes Operator 快速入门教程

Helm

<u>Helm</u>是一个 Kubernetes 应用的包管理工具,用来管理 chart——预先配置好的安装包资源,有点类似于 Ubuntu 的 APT 和 CentOS 中的 YUM。2019 年 11 月 13 日,Helm 3 发布,2020 年 4 月 30 日,从 CNCF 中毕业。

Helm chart 是用来封装 Kubernetes 原生应用程序的 YAML 文件,可以在你部署应用的时候自定义应用程序的一些 metadata,便与应用程序的分发。

Helm 和 chart 的主要作用是:

下面是 Helm 的架构图。

Helm架构图

更多信息参考:https://jimmysong.io/kubernetes-handbook/practice/helm.html

调试和运维

kubectl

你可以使用 Kubectl 命令行工具管理 Kubernetes 集群。 kubectl 在 $HOME/.kube 目录中查找一个名为 config 的配置文件。

语法

使用以下语法 kubectl 从终端窗口运行命令:

kubectl [command] [TYPE] [NAME] [flags]

其中 command、TYPE、NAME 和 flags 分别是:

kubectl命令

Kubectl常用命令

Kubernetes kubectl 命令表

调试运行中的 Pod

# 查看 pod 概要信息
kubectl get pod $POD_NAME -n $NAMESPACE -owide
# 查看 pod 详细信息
kubectl describe pod $POD_NAME -n $NAMESPACE
# 查看 node 概要信息
kubectl get node $NODE_NAME -owide
# 查看 node 详细信息
kubectl describe node $NODE_NAME
# dump 输出 pod 的日志(stdout)
kubectl logs -f $POD_NAME -n $NAMESPACE
# dump 输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
kubectl logs -f $POD_NAME -c $CONTAINER_NAME
# 流式输出 pod 的日志(stdout)
kubectl logs -f $POD_NAME -n $NAMESPACE
# 流式输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
kubectl logs -f $POD_NAME -c $CONTAINER_NAME
# 强制删除 pod
kubectl delete pod $POD_NAME -n $NAMESPACE --force --grace-period=0
# 在已存在的容器中执行命令(只有一个容器的情况下)
kubectl exec $POD_NAME -- ls /
# 在已存在的容器中执行命令(pod 中有多个容器的情况下)
kubectl exec $POD_NAME -c $CONTAINER_NAME -- ls /
# 连接到运行中的容器
kubectl attach $POD_NAME -i 
# 显示kubernetes 系统中最近的一些事件
kubectl get events

Docker

操作容器

# 启动容器并启动bash(交互方式):
docker run -i -t <image_name/continar_id> /bin/bash
# 启动容器以后台方式运行(更通用的方式):
$docker run -d -it  image_name
# ps:这里的 image_name 包含了tag:hello.demo.kdemo:v1.0

# 附着到容器
# 附着到正在运行的容器
docker attach <id、container_name>
# 进入正在运行的容器内部,同时运行bash(比attach更好用)
docker exec -t -i <id/container_name>  /bin/bash
# 查看容器日志
docker logs <id/container_name>
# 实时查看日志输出
docker logs -f <id/container_name> (类似 tail -f) (带上时间戳-t)
# 列出当前所有正在运行的container
docker ps
# 用一行列出所有正在运行的container(容器多的时候非常清晰)
docker ps | less -S
# 列出所有的container
docker ps -a  
# 列出最近一次启动的container
docker ps -l 
# 显示一个运行的容器里面的进程信息
docker top Name/ID  
# 查看容器内部详情细节:
docker inspect <id/container_name>
# 在容器中安装新的程序
docker run image_name apt-get install -y app_name  

ps:docker exec是如此的有用,以至于我们通常是将其封装为一个脚本,放到全局可调用的地方,比如,可以写成一个indocker.sh:

$cat indocker.sh 
docker exec -t -i $1 /bin/bash
# 查看需要附着的容器id
$docker ps | less -S
CONTAINER ID        IMAGE                                                 
9cf7b563f689        hello.demo.kdemo:v160525.202747

$./indocker.sh 9cf7b563f689 

操作镜像

# 列出镜像
docker images
# 下载image
docker pull image_name
# 删除一个或者多个镜像;
docker rmi image_name  
# 显示一个镜像的历史;
docker history image_name
# 发布docker镜像
docker push new_image_name
# ps:要发布到私有Registry中的镜像,在镜像命名中需要带上Registry的域名(如果非80端口,同时需要带上端口号)比如:
docker push dockerhub.yourdomain.com:443/hello.demo.kdemo:v1.0
# 拉取docker镜像
docker pull image_name

常见问题

下表中列举了 Pod 的状态信息:

状态 描述
Error Pod 启动过程中发生错误。
NodeLost Pod 所在节点失联。
Unkown Pod 所在节点失联或其他未知异常。
Waiting Pod 等待启动。
Pending Pod 等待被调度。
ContainerCreating Pod 容器正在被创建。
Terminating Pod 正在被销毁。
CrashLoopBackOff 容器退出,Kubelet 正在将它重启。
InvalidImageName 无法解析镜像名称。
ImageInspectError 无法校验镜像。
ErrImageNeverPull 策略禁止拉取镜像。
ImagePullBackOff 正在重试拉取。
RegistryUnavailable 连接不到镜像中心。
ErrImagePull 通用的拉取镜像出错。
CreateContainerConfigError 不能创建 Kubelet 使用的容器配置。
CreateContainerError 创建容器失败。
RunContainerError 启动容器失败。
PreStartHookError 执行 preStart hook 报错。
PostStartHookError 执行 postStart hook 报错。
ContainersNotInitialized 容器没有初始化完毕。
ContainersNotReady 容器没有准备完毕。
ContainerCreating 容器创建中。
PodInitializing Pod 初始化中。
DockerDaemonNotReady Docker 还没有完全启动。
NetworkPluginNotReady 网络插件还没有完全启动。

K8S的10个常见失败问题的原因

Kubernetes 排错之 Pod 异常

K8S常见Pod 异常状态的处理

Pod 一直处于 ContainerCreating 或 Waiting 状态

Pod 一直处于 ImagePullBackOff 状态

Pod 一直处于 Pending 状态

Pod 一直处于 Terminating 状态

Pod 健康检查失败

Pod 处于 CrashLoopBackOff 状态

容器进程主动退出

扩容阅读

阿里云 Kubernetes 入门教程

上一篇下一篇

猜你喜欢

热点阅读