零基础快速入门Kubernetes

Kubernetes 简介

Kubernetes 是什么?

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

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

Kubernetes能做什么?

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

  • 基于容器的应用部署、维护和滚动升级。

  • 更加充分地利用硬件,最大程度获取运行企业应用所需的资源。

  • 有效管控应用部署和更新,并实现自动化操作。

  • 挂载和增加存储,用于运行有状态的应用。

  • 快速、按需扩展容器化应用及其资源。

  • 对服务进行声明式管理,保证所部署的应用始终按照部署的方式运行。

  • 利用自动布局、自动重启、自动复制以及自动扩展功能,对应用实施状况检查和自我修复。

  • 插件机制保证扩展性。

Kubernetes特性

  • 服务发现和负载均衡

    Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。

  • 存储编排

    Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。

  • 自动部署和回滚

    你可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态 更改为期望状态。例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。

  • 自动完成装箱计算

    Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。 当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。

  • 自我修复

    Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的 运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。

  • 密钥与配置管理

    Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

  • 水平扩缩

    使用一个简单的命令、一个 UI 或基于 CPU 使用情况自动对应用程序进行扩缩。

  • 为扩展性设计

    无需更改上游源码即可扩展你的 Kubernetes 集群。

Kubernetes架构

kubernetes架构

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

  • etcd 保存了整个集群的状态;

  • API Server 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;

  • Controller Manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;

  • Scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;

  • Kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理;

  • Container Runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI);

  • Kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;

  • kubectl 用于通过命令行与 kube-apiserver 进行交互,而对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

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

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

这些控制器包括:

  • 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应

  • 任务控制器(Job controller): 监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成

  • 端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)

  • 服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌

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 的特征

  • 包含多个共享 IPC 和 Network namespace 的容器,可直接通过 localhost 通信

  • 所有 Pod 内容器都可以访问共享的 Volume,可以访问共享数据

  • 无容错性:直接创建的 Pod 一旦被调度后就跟 Node 绑定,即使 Node 挂掉也不会被重新调度(而是被自动删除),因此推荐使用 Deployment、Daemonset 等控制器来容错

  • 优雅终止:Pod 删除的时候先给其内的进程发送 SIGTERM,等待一段时间(grace period)后才强制停止依然还在运行的进程

  • 特权容器(通过 SecurityContext 配置)具有改变系统配置的权限(在网络插件中大量应用)

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 都包括以下状态信息:

  • 地址:包括 hostname、外网 IP 和内网 IP

  • 条件(Condition):包括 OutOfDisk、Ready、MemoryPressure 和 DiskPressure

  • 容量(Capacity):Node 上的可用资源,包括 CPU、内存和 Pod 总数

  • 基本信息(Info):包括内核版本、容器引擎版本、OS 类型等

操作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

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

  • Deployment (推荐)

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 的 典型应用场景 包括:

  • 定义 Deployment 来创建 Pod 和 ReplicaSet

  • 滚动升级和回滚应用

  • 扩容和缩容

  • 暂停和继续 Deployment

DaemonSet

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

DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程

  • 在每个节点上运行日志收集守护进程

  • 在每个节点上运行监控守护进程

指定 Node 节点

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

  • nodeSelector:只调度到匹配指定 label 的 Node 上

  • nodeAffinity:功能更丰富的 Node 选择器,比如支持集合操作

  • podAffinity:调度到满足条件的 Pod 所在的 Node 上

StatefulSet

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

  • 稳定的持久化存储,即 Pod 重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现

  • 稳定的网络标志,即 Pod 重新调度后其 PodName 和 HostName 不变,基于 Headless Service(即没有 Cluster IP 的 Service)来实现

  • 有序部署,有序扩展,即 Pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依序进行(即从 0 到 N-1,在下一个 Pod 运行之前所有之前的 Pod 必须都是 Running 和 Ready 状态),基于 init containers 来实现

  • 有序收缩,有序删除(即从 N-1 到 0)

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 配置参数:

  • .spec.schedule 指定任务运行周期,格式同 Cron

  • .spec.jobTemplate 指定需要运行的任务,格式同 Job

  • .spec.startingDeadlineSeconds 指定任务开始的截止期限

  • .spec.concurrencyPolicy 指定任务的并发策略,支持 Allow、Forbid 和 Replace 三个选项

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会有如下几个问题:

  • Pod会随时被Deployment这样的控制器删除重建,那访问Pod的结果就会变得不可预知。

  • Pod的IP地址是在Pod启动后才被分配,在启动前并不知道Pod的IP地址。

  • 应用往往都是由多个运行相同镜像的一组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的类型

  • ClusterIP:默认类型,仅仅使用一个集群内部的IP地址,用于在集群内部互相访问的场景,通过ClusterIP访问Service。

  • NodePort:在集群内部IP的基础上,在集群的每一个节点的端口上开放这个服务,。你可以在任意<NodeIP>:NodePort地址上访问到这个服务。用于从集群外部访问的场景,通过节点上的端口访问Service,详细介绍请参见NodePort类型的Service

  • LoadBalancer:用于从集群外部访问的场景,其实是NodePort的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort,而外部只需要访问LoadBalancer,详细介绍请参见LoadBalancer类型的Service

  • None:用于Pod间的互相发现,这种类型的Service又叫Headless Service,详细介绍请参见Headless 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 规则都包含以下信息:

  • 可选的 host。在此示例中,未指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host。

  • 路径列表 paths(例如,/testpath),每个路径都有一个由 serviceName 和 servicePort 定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。

  • backend(后端)是 Service 文档中所述的服务和端口名称的组合。 与规则的 host 和 path 匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的 backend。

通常在 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 使用注意点

  • ConfigMap 文件大小限制:1MB(*ETCD要求)

  • Pod 只能引用相同 Namespace 中的ConfigMap

  • Pod 引用的 ConfigMap 不存在时,Pod 无法创建成功。即 Pod 创建前需要先创建好 ConfigMap

  • 使用 envFrom 从 ConfigMap 来配置环境变量时,如果 ConfigMap 中的某些 key 被认为无效(比如 key 名称中带有数字),该环境变量将不会注入容器,但是 Pod 可以正常创建

  • 只有通过 K8s API 创建的 Pod 才能使用 ConfigMap,其他方式创建的 Pod(如 Manifest 创建的 static pod)不能使用 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详解

  • PV:PV描述的是持久化存储卷,主要定义的是一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录。

  • PVC:PVC描述的是Pod所希望使用的持久化存储的属性,比如,Volume存储的大小、可读写权限等等。

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

  • StorageClass:PV和PVC方法虽然能实现屏蔽底层存储,但是PV创建比较复杂(可以看到PV中csi字段的配置很麻烦),通常都是由集群管理员管理,这非常不方便。Kubernetes解决这个问题的方法是提供动态配置PV的方法,可以自动创PV。管理员可以部署PV配置器(provisioner),然后定义对应的StorageClass,这样开发者在创建PVC的时候就可以选择需要创建存储的类型,PVC会把StorageClass传递给PV provisioner,由provisioner自动创建PV。

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容器特点

  • 镜像非常小,目前在700KB左右

  • 永远处于Pause(暂停)状态

Pause容器功能

  • 维护 Pod 网络协议栈,实现 Pod 中各容器共享网络协议栈。

  • 扮演 PID 1 的角色,并在子进程成为孤儿进程的时候通过调用 wait() 收割这些僵尸子进程。

Init Container

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

Init Container 的应用场景:

  • 等待其他模块Ready

  • 做初始化配置

  • 其它场景:如将 pod 注册到一个中央数据库、配置中心等

下面的例子定义了一个具有 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 的类型包括两种:

  • exec:执行一段命令

  • HTTP:发送 HTTP 请求。

参考下面的配置:

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(存活探针)

  • 表明容器是否正在运行。

  • 如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略的影响。

  • 如果容器不提供存活探针,则默认状态为 Success。

2. readinessProbe(就绪探针)

  • 表明容器是否可以正常接受请求。

  • 如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。

  • 初始延迟之前的就绪状态默认为 Failure。

  • 如果容器不提供就绪探针,则默认状态为 Success。

3. startup probes(启动探针)

  • 启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被前面的探针kill掉

三种健康检查方法

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

  • exec 提供命令或shell的检测,在容器中执行命令检查,返回码为0健康,非0异常(风险提醒:由于exec探针与低版本systemd存在兼容性问题,需要慎重使用,如需使用需咨询Hulk同学)

  • httpGet http协议探测,在容器中发送http请求,根据http返回码判断业务健康情况

  • tcpSocket tcp协议探测,向容器发送tcp建立连接,能建立则说明正常

配置探针

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

  • initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。

  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。

  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。

  • successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。

  • failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

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

  • Always:当容器失效时,由Kubelet自动重启该容器。RestartPolicy的默认值。

  • OnFailure:当容器终止运行且退出码不为0时由Kubelet重启。

  • Never:无论何种情况下,Kubelet都不会重启该容器。

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

镜像拉取策略

支持三种 ImagePullPolicy

  • Always:不管本地镜像是否存在都会去仓库进行一次镜像拉取。校验如果镜像有变化则会覆盖本地镜像,否则不会覆盖。

  • Never:只是用本地镜像,不会去仓库拉取镜像,如果本地镜像不存在则Pod运行失败。

  • IfNotPresent:只有本地镜像不存在时,才会去仓库拉取镜像。ImagePullPolicy的默认值。

注意:

  • 默认为 IfNotPresent,但 :latest 标签的镜像默认为 Always。

  • 拉取镜像时 docker 会进行校验,如果镜像中的 MD5 码没有变,则不会拉取镜像数据。

  • 生产环境中应该尽量避免使用 :latest 标签,而开发环境中可以借助 :latest 标签自动拉取最新的镜像。

资源限制

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

  • spec.containers[].resources.limits.cpu:CPU 上限,可以短暂超过,容器也不会被停止

  • spec.containers[].resources.limits.memory:内存上限,不可以超过;如果超过,容器可能会被终止或调度到其他资源充足的机器上

  • spec.containers[].resources.limits.ephemeral-storage:临时存储(容器可写层、日志以及 EmptyDir 等)的上限,超过后 Pod 会被驱逐

  • spec.containers[].resources.requests.cpu:CPU 请求,也是调度 CPU 资源的依据,可以超过

  • spec.containers[].resources.requests.memory:内存请求,也是调度内存资源的依据,可以超过;但如果超过,容器可能会在 Node 内存不足时清理

  • spec.containers[].resources.requests.ephemeral-storage:临时存储(容器可写层、日志以及 EmptyDir 等)的请求,调度容器存储的依据

比如 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"

注意

  • CPU 的单位是 CPU 个数,可以用 millicpu (m) 表示少于 1 个 CPU 的情况,如 500m = 500millicpu = 0.5cpu

  • 内存的单位则包括 E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki 等。

QoS

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

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

  • Guaranteed

    对于 QoS 类为 Guaranteed 的 Pod:

    • Pod 中的每个容器,包含初始化容器,必须指定内存请求和内存限制,并且两者要相等。

    • Pod 中的每个容器,包含初始化容器,必须指定 CPU 请求和 CPU 限制,并且两者要相等。

  • Burstable

    如果满足下面条件,将会指定 Pod 的 QoS 类为 Burstable:

    • Pod 不符合 Guaranteed QoS 类的标准。

    • Pod 中至少一个容器具有内存或 CPU 请求。

  • BestEffort

    对于 QoS 类为 BestEffort 的 Pod,Pod 中的容器必须没有设置内存和 CPU 限制或请求。

调度控制

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。这个功能比较绕,以下面的例子为例:

  • 如果一个 “Node 所在 Zone 中包含至少一个带有 security=S1 标签且运行中的 Pod”,那么可以调度到该 Node

  • 不调度到 “包含至少一个带有 security=S2 标签且运行中 Pod” 的 Node 上

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的编写,它提供如下功能:

  • 高级API和抽象,更直观地编写操作逻辑

  • 用于脚手架和代码生成的工具,可以快速引导新项目

  • 扩展以涵盖常见的操作员用例

安装 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>
  • operator-project-name:创建的项目的名称

  • your-api-group:Kubernetes 自定义 API 的组名,一般用域名如 jimmysong.io

  • version:Kubernetes 自定义资源的 API 版本

  • custom-resource-kind:CRD 的名称

更多信息参考: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 分别是:

  • command:指定要对一个或多个资源执行的操作,例如 create、apply、get、describe、delete。

  • TYPE:指定资源类型。资源类型不区分大小写, 可以指定单数、复数或缩写形式。例如,以下命令输出相同的结果:

    kubectl get pod pod1
    kubectl get pods pod1
    kubectl get po pod1
    
  • NAME:指定资源的名称。名称区分大小写。 如果省略名称,则显示所有资源的详细信息 kubectl get pods。

    在对多个资源执行操作时,你可以按类型和名称指定每个资源,或指定一个或多个文件:

  • flags: 指定可选的参数。例如,可以使用 -s 或 -server 参数指定 Kubernetes API 服务器的地址和端口。

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 入门教程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 一、Kubernetes是什么 Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”,简称 k8s,...
    宏势阅读 25,708评论 4 8
  • 写在前面 kubernetes中涉及很多概念,包含云生态社区中各类技术,学习成本比较高,k8s中通常以编写yaml...
    HappyLau谈云计算阅读 544评论 0 1
  • 简介 kubernetes简称k8s。是用于自动部署,扩展和管理容器化应用程序的开源系统。中文官网:https:/...
    Y了个J阅读 1,354评论 0 0
  • 前言 kubernetes 是什么?kubernetes 是一个容器管理平台。 kubernetes 提出了 po...
    lzp1234阅读 339评论 0 1
  • 什么是Kubernetes? Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节...
    青竹心_zxx阅读 123评论 0 0