k8s 基本概念

Kubernetes 中的大部分概念如 Node、 Pod、 ReplicationController、 Service 等都可以看作一种“资源对象”, 几乎所有的资源对象都可以通过 Kubernetes 提供的 kubectl 工具(或者 API 编程调用) 执行 增、 删、 改、 查 等操作并将其保存 在 etcd 中持久化存储。

Master

几乎所有的控制命令都是发给 Master 的,一般来说 Master 会单独部署在一个虚拟机或 X86服务器上

Master 的进程包括:

  • Kubernetes API Server( kube- apiserver), 提供了 HTTP Rest 接口的关键服务进程, 是 Kubernetes 里所有资源的增、删、改、查等操作的唯一 入口,也是集群控制 的入口进程。
  • Kubernetes Controller Manager( kube- controller- manager), Kubernetes 里所有资源对象的自动化控制中心, 可以理解为资源对象的“大总管”。
  • Kubernetes Scheduler( kube- scheduler), 负责资源调度(Pod 调度)的程, 相当于公交公司的“ 调度室”。

其实 Master 节点上往往还启动了一个 etcd Server 进程, 因为 Kubernetes 里 的 所有 资源 对象 的 数据 全部 是 保存 在 etcd 中的。

Node

除了 Master,Kubernetes 集群中的其他机器被称为 Node 节点.

每个 Node 节点上都运行着以下一组关键进程。

  • kubelet: 负责Pod 的创建 删除 启动 停止, 同时与 Master 节点密切协作, 实现集群管理的基本功能。
  • kube- proxy: 实现 Kubernetes Service 的通信与负载均衡机制的重要组件。
  • Docker Engine( dockerDocker 引擎, 负责本机的容器创建和管理工作。

查看节点

[root@centos ~]# kubectl get node
NAME        STATUS    AGE
127.0.0.1   Ready     2d

查看节点详细信息,包括磁盘状态

[root@centos ~]# kubectl describe node 127.0.0.1
Name:            127.0.0.1 # 节点名称
Role:
Labels:            beta.kubernetes.io/arch=amd64 # 节点标签
            beta.kubernetes.io/os=linux
            kubernetes.io/hostname=127.0.0.1
Taints:            <none>
CreationTimestamp:    Fri, 02 Mar 2018 21:59:52 +0800 # 创建时间
Phase:
Conditions:
  Type            Status    LastHeartbeatTime            LastTransitionTime            Reason                Message
  ----            ------    -----------------            ------------------            ------                -------
  OutOfDisk         False     Mon, 05 Mar 2018 10:24:15 +0800     Fri, 02 Mar 2018 21:59:52 +0800     KubeletHasSufficientDisk     kubelet has sufficient disk space available # 磁盘信息 True 表示磁盘满了.
  MemoryPressure     False     Mon, 05 Mar 2018 10:24:15 +0800     Fri, 02 Mar 2018 21:59:52 +0800      KubeletHasSufficientMemory     kubelet has sufficient memory available
  DiskPressure         False     Mon, 05 Mar 2018 10:24:15 +0800     Fri, 02 Mar 2018 21:59:52 +0800     KubeletHasNoDiskPressure     kubelet has no disk pressure
  Ready         True     Mon, 05 Mar 2018 10:24:15 +0800     Sat, 03 Mar 2018 10:50:27 +0800     KubeletReady             kubelet is posting ready status
Addresses:        127.0.0.1,127.0.0.1,127.0.0.1
Capacity:
 alpha.kubernetes.io/nvidia-gpu:    0
 cpu:                    4
 memory:                8002252Ki
 pods:                    110
Allocatable:
 alpha.kubernetes.io/nvidia-gpu:    0
 cpu:                    4
 memory:                8002252Ki
 pods:                    110
System Info:
 Machine ID:            3ce150b846d1364ca00934cb4b7425e7
 System UUID:            6648964A-8A37-49F8-AE2E-CD51D601E0A4
 Boot ID:            6c79106a-2825-43e5-a49a-ce1837fed475
 Kernel Version:        3.10.0-693.el7.x86_64
 OS Image:            CentOS Linux 7 (Core)
 Operating System:        linux
 Architecture:            amd64
 Container Runtime Version:    docker://1.12.6
 Kubelet Version:        v1.5.2
 Kube-Proxy Version:        v1.5.2
ExternalID:            127.0.0.1
Non-terminated Pods:        (6 in total)
  Namespace            Name            CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ---------            ----            ------------    ----------    ---------------    -------------
  default            mysql-j6vpx        0 (0%)        0 (0%)        0 (0%)        0 (0%)
  default            myweb-cwhhl        0 (0%)        0 (0%)        0 (0%)        0 (0%)
  default            myweb-ljwtl        0 (0%)        0 (0%)        0 (0%)        0 (0%)
  default            myweb-n0vrj        0 (0%)        0 (0%)        0 (0%)        0 (0%)
  default            myweb-rnjq9        0 (0%)        0 (0%)        0 (0%)        0 (0%)
  default            myweb-sgfhs        0 (0%)        0 (0%)        0 (0%)        0 (0%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  0 (0%)    0 (0%)        0 (0%)        0 (0%)
No events.

Pod

Pod 是 kubernetes 中 最基本的单位,每个 Pod 都有一个根容器叫 Pause

为什么需要根容器?

  • 一组容器作为一个 Pod,无法对整体进行简单的判断并作出行动,比如一个容器死了是整体都死了吗?因此需要一个无关的根容器来代表整个容器的状态.
  • Pod 的多个业务容器共享 Pause 的 IP, 并共享容器挂载的卷,既解决了通信问题又解决了共享问题

kubernetes 中,每个Pod都分配了一个唯一的 IP, 称之为 Pod-IP, 不同 Pod 和其他 pod 是可以通信的

在 Kubernetes 里, 一个计算资源进行配额限定需要设定以下两个参数。

  • Requests: 该资源的最小申请量,系统必须满足要求。
  • Limits: 该资源最大允许使用的量, 不能被突破, 当容器试图使用超过这个量的资源时, 可能会被 Kubernetes Kill 并重启。

通常来说我们会把 Requests 的值设置为一个比较小的数值,符合容器平时的工作负载情况下的资源需求,而 Limit 设置为峰值负载情况下资源占用的最大量,比如下面这段,表明 MYSQL 容器申请至少0.25个 CPU 一级64MiB 内存,在运行过程中容器所使用的资源配额为0.5个 CPU 以及128MiB 内存:

spec:
  containers:
  - name: db
    image: mysql
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"  

Label

一个 Label 是一个 key=value 的键值对,由用户自主设定, Label 可以附加到各种资源对象上,例如 Node、Pod、Service、RC 等,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源对象上去,Label 通常在资源对象定义时确定,也可以在对象创建后动态添加或删除。

Label 定义后,可以通过 Label Selector 查询和筛选拥有某些 Label 的资源对象。查询类似 SQL的 where 查询条件:

  • name = redis-slave 匹配name等于 redis-slave 的资源对象。
  • env != production 匹配不具有标签 env=production 的资源对象, 比如 env=test 就满足此条件。
  • name in (redis-slave,redis-master) 匹配所有包含 redis-slave 和 redis-master 的资源对象。
  • name notin (php-frontend),匹配所有不具有标签 name=php-frontend 的资源对象

多个表达式可以组合实现,需要用”,” 分割,几个条件之间是 “AND” 的关系

name= redis- slave, env!= production
name notin (php- frontend), env!= production

Label 选择器的使用场景:

  • kube-controller 进程通过资源对象 RC 定义标签来筛选要监控的副本数量,实现自动控制流程。
  • kube-proxy 进程通过 service 的标签选择器来选择对应的 Pod,自动建立每个 service 到对应 Pod的请求转发路由表,从而实现 service 的智能负载均衡机制。
  • 通过对某些 Node 定义特定标签,并且在 Pod 定义文件中使用 NodeSelector 这种标签调度策略,kube-scheduler 进程可实现 Pod“定向调度”的特性。

多 label

RC

RC 定义了一个期望的场景,即声明某种 Pod 的副本数在任何时候都符合预期,其包括:

  • 副本数
  • 用于筛选的 Pod 和 标签选择器
  • 当 Pod 副本数小于预期数量时,用于创建新 Pod 的模板

RC 提交到集群–Master Controller Manager 组件收到通知,定期巡检当前存活的目标 Pod,并确保目标 Pod 实力数量刚好等于此 RC 的预期值,如果多于 Pod 副本,就会停掉一些,否则会创建一些 Pod。

动态缩放rc

[root@centos ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     1         1         1         2d
myweb     5         5         5         1d
[root@centos ~]# kubectl scale rc mysql --replicas=3
replicationcontroller "mysql" scaled
[root@centos ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     3         3         1         2d
myweb     5         5         5         1d
[root@centos ~]#

删除 RC 并不会影响通过该 RC 已经创建好的 Pod,为了删除所有 Pod,可以设置 replicas=0 然后更新该 RC,另外可以使用 stop 或 delete 删除 RC 和 RC 控制的全部 Pod。

Deployment

从 k8s 1.2引入的新概念, 与RC 相似度90%,目的是为了更好的解决 Pod 的编排问题。Deployment 在内部使用了 Replica Set 来实现目的。

Deployment 相对于 RC 的一个最大的升级就是随时知道当前 Pod “部署”的进度

Deployment 的场景:

  • 创建一个 DP 对象来生成对应的 Replica Set 并完成 Pod 副本的创建过程。
  • 检查 DP 的状态来看部署动作是否完成。
  • 更新 DP 以创建新的 Pod(比如镜像升级)。
  • 如果当前 DP 不稳定,回滚到一个早先的 DP 版本。
  • 挂起或恢复一个 DP

例子,tomcat-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier,operator: In,values: [frontend]}
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080  

然后在集群中创建 deployment

[root@centos ~]# kubectl create -f tomcat-dp.yaml
deployment "frontend" created

然后查看

[root@centos ~]# kubectl get deployments
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   1         1         1            1           2m

其中

  • DESIRED 为 Pod 副本数量的期望值,即定义的 Replica。
  • CURRENT 为当前的 Replica 值,它会不断变化直到达到 DESIRED 值,表明整个部署过程完成。
  • UP-TO-DATA 最新版本的 Pod 的副本数量,在滚动升级过程中,有多少个 Pod 副本已经升级成功了。
  • AVAILABLE 当前集群中可用的 Pod 副本数量。

查看对应的 Replica Set

[root@centos ~]# kubectl get rs
NAME                 DESIRED   CURRENT   READY     AGE
frontend-141477217   1         1         1         11m

查看 pods

[root@centos ~]# kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
frontend-141477217-jcckp   1/1       Running   0          12m

HPA

HPA = Horizontal Pod Autoscaler 意思是横向自动扩容。

通过手工执行 kubectl scale 效率低,不符合自动化、智能化的定位。HPA 通过追踪分析 RC 控制的所有目标 Pod 的负载变化情况,来确定是否需要针对性的调整目标 Pod 的副本数,这是 HPA 的实现原理。

HPA 的Pod 负载度量指标:

  • CPUutilizationPercentage,目标 Pod 所有副本自身 CPU 利用率的平均值。
  • 引用程序自定义的度量指标,比如服务在每秒内的请求数(TPS 或 QPS)

例子

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name:php-apache
  namespace: default
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    kind: Deployment
    name: php-apache
  targetCPUUtilizationPercentage: 90  

命令方式实现

kubectl autoscale deployment php- apache --cpu- percent= 90 --min= 1 --max= 10

Service

之前的 Pod RC 都是为服务做“嫁衣”的

k8s 定义了一个服务的访问入口地址,前端通过这个入口访问其背后的一组由 Pod 副本组成的集群,service 与后端 Pod 通过 label selector 来实现对接。 而 RC 的作用是保证 service 的服务能能力和服务质量能够始终处于预期的标准。

每个 Pod 都会被分配一个单独的 IP 地址,而且每个 Pod 都提供一个独立的 endpoint 以被客户端访问,现在多个 Pod 组成的集群提供服务,一般是需要部署一个负载均衡器实现转发。在 k8s 中,node 上的 kube-proxy 就是一个软件负载均衡器,它负责把对 service 的请求转发到后端的某个 Pod 实例上,部署实现服务的负载均衡与会话保持机制,每个 service 分配一个全局唯一的虚拟 IP 地址,这个虚拟 IP 被称为 cluster IP,其在 service 的整个生命周期内是不变的。

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
  selector:
    tier: frontend  

这里的标签与之前创建的 Tomcat 对应,查看暴露的地址和端口

[root@centos ~]# kubectl get endpoints
NAME             ENDPOINTS             AGE
kubernetes       192.168.10.115:6443   2d
mysql            172.17.0.3:3306       3m
tomcat-service   172.17.0.2:8080       37s

查看 service 的 cluster IP:

[root@centos ~]# kubectl get svc tomcat-service -o yaml

得到如下内容

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-03-05T06:46:41Z
  name: tomcat-service
  namespace: default
  resourceVersion: "256242"
  selfLink: /api/v1/namespaces/default/services/tomcat-service
  uid: f6544c3e-2040-11e8-b481-fa163eee21c2
spec:
  clusterIP: 10.254.1.165
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    tier: frontend
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

其中 targetPort 属性用来确定提供该服务的容器暴露(EXPOSE)的端口,而 port 则定义 service 的虚拟端口,因为前面 Tomcat 没有指定 targetport,则默认与 port 相同。

多端口问题

其样例:

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
    name: service-port
  - port: 8005
    name: shutdown-port
  selector:
    tier: frontend  

为什么要给多端口命名,这就需要涉及 k8s 的服务发现了。

服务发现

首先,每个 k8s 中的 service 都有一个唯一的 cluster IP 以及唯一的名字,而名字是由开发者自定义的,部署的时候也没必要改变,所以完全可以固定在配置中。

早期 k8s 采用 linux 环境变量的方式解决问题。

这种方式不太方便,后来 k8s 通过 ADD-On 增值包的方式引入了 DNS 系统,把服务名作为 DNS 域名。

外部系统访问 service 的问题

采用nodeport 访问最为直接,但是 nodeport 无法解决例如负载均衡的问题,需要有一个 load balancer

Volume (存储卷)

k8s 的 volume 是 Pod 中能够给多容器访问的共享目录,与 docker 的 volume 类似,但不等价。首先,k8s 的 volume 定义在 Pod 上,然后被一个 Pod 里的多个容器挂载到具体的文件目录下,其次,k8s 的 volume 与 Pod 的生命周期相同,但是与容器的生命周期不相关。最后,k8s 支持多种类型的 volume,例如 ceph 等分布式文件系统。

k8s 的 volume 类型有:emptyDir(初始为空的,用来存放一些临时文件)、hostPath(主机文件或目录,存放日子等需要永久保存的文件)、gcePersistentDisk(谷歌公有云的永久磁盘,数据不会被删除,但是有限制条件)、awsElasticBlockStore(与前者类似)、NFS、其他类型的(iscsi等)。

Persistent volume

简称 PV,可以理解为 k8s 集群中的某个网络存储中对应的一块存储:

  • PV 只能是网络存储,不属于任何 Node,但是可以在 Node 上访问。
  • PV 并不是定义在 Pod 上,而是独立在 Pod 之外。
  • PV 目前的类型有 GCE NFS RBD iSCSCI aswElasticBlockStore GlusterFS

Namespace

中文名命令空间,可以实现多租户的资源隔离,通过将集群内部的资源对象分配到不同的 namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同分组在共享使用整个集群的资源的同时还能被分别管理。

默认会有一个 default 的 namespace。

[root@centos ~]# kubectl get namespaces
NAME          STATUS    AGE
default       Active    2d
kube-system   Active    2d

如果不特别指明,则创建的 Pod RC service 都会被分配到 default 中。

定义 namespace,创建一个 namespace-demo.yaml 的文件

apiVersion: v1
kind: Namespace
metadata:
  name: development

然后创建查看 namespace

[root@centos ~]# kubectl create -f namespace-demo.yaml
namespace "development" created
[root@centos ~]# kubectl get namespaces
NAME          STATUS    AGE
default       Active    2d
development   Active    4s
kube-system   Active    2d

添加一个pod 的配置,直到其 namespace 为 development

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: development
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "36000"
    name: busybox  

创建

[root@centos ~]# kubectl create -f busybox.yaml
pod "busybox" created

查看

[root@centos ~]# kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
frontend-141477217-jcckp   1/1       Running   0          1h
mysql-7p27m                1/1       Running   0          42m
myweb-0n7b9                1/1       Running   0          42m
myweb-5dcff                1/1       Running   0          42m
myweb-mbxk4                1/1       Running   0          42m
myweb-md64b                1/1       Running   0          42m
myweb-rkql9                1/1       Running   0          42m

直接查看不显示,需要加参数

[root@centos ~]# kubectl get pods --namespace=development
NAME      READY     STATUS    RESTARTS   AGE
busybox   1/1       Running   0          57s