kubernetes 入门


原文链接: kubernetes 入门

第一章 kubernetes 入门

  1. kubectl run nginx --image=nginx:alpine --replicas=2 --port=80
    deployment "nginx" created

To expose your service to the public internet, run:

  1. kubectl expose deployment nginx --target-port=80 --type=LoadBalancer
    service "nginx" exposed

You can see that they are running by:

  1. kubectl get po
    NAME READY STATUS RESTARTS AGE
    nginx-3800858182-h9v8d 1/1 Running 0 1m
    nginx-3800858182-wqafx 1/1 Running 0 1m

Kubernetes will ensure that your application keeps running, by automatically restarting containers that fail, spreading containers across nodes, and recreating containers on new nodes when nodes fail.

To find the public IP address assigned to your application, execute:

kubectl get service nginx
NAME CLUSTER_IP EXTERNAL_IP PORT(S) AGE
nginx 10.179.240.1 25.1.2.3 80/TCP 8s

You may need to wait for a minute or two for the external ip address to be provisioned.

In order to access your nginx landing page, you also have to make sure that traffic from external IPs is allowed. Do this by opening a firewall to allow traffic on port 80.

If you’re running on AWS, Kubernetes creates an ELB for you. ELBs use host names, not IPs, so you will have to do kubectl describe service/nginx and look for the LoadBalancer Ingress host name. Traffic from external IPs is allowed automatically.
Killing the application

To kill the application and delete its containers and public IP address, do:

kubectl delete deployment,service nginx
deployment "nginx" deleted
service "nginx" deleted

1.1 Kubernetes 是什么

  • 首先,它是一个全新的基于容器技术的分布式架构领先方案;

  • 其次,Kubernetes 是一个开放的开发平台;

  • 最后,Kubernetes 是一个完备的分布式系统支撑平台。

1.2 为什么要用 Kubernetes

使用 Kubernetes 的理由很多,最根本的一个理由就是:IT 从来都是一个由新技术驱动的行业。
使用 Kubernetes 所带来的好处:

  • 首先,最直接的感受就是我们可以 “轻装上阵” 地开发复杂系统了;

  • 其次,使用 Kubernetes 就是在全面拥抱微服务架构;

  • 然后,我们的系统可以随时随地整体 “搬迁” 到公有云上;

  • 最后,Kubernetes 系统架构具备了超强的横向扩容能力。

1.3 Kubernetes 基本概念和术语

在 Kubernetes 中,Node、Pod、Replication Controller、Service 等概念都可以看作一种资源对象,通过 Kubernetes 提供的 Kubectl 工具或者 API 调用进行操作,并保存在 etcd 中。

1.3.1 Node(节点)

Node(节点)是 Kubernetes 集群中相对于 Master 而言的工作主机,在较早的版本中也被称为 Minion。Node 可以是一台物理主机,也可以是一台虚拟机(VM)。在每个 Node 上运行用于启动和管理 Pod 的服务 Kubelet,并能够被 Master 管理。在 Node 上运行的服务进行包括 Kubelet、kube-proxy 和 docker daemon。

Node 信息如下:

Node 地址:主机的 IP 地址,或者 Node ID。

Node 运行状态:包括 Pending、Running、Terminated 三种状态。

Node Condition(条件):描述 Running 状态 Node 的运行条件,目前只有一种条件 ----Ready。Ready 表示 Node 处于健康状态,可以接收从 Master 发来的创建 Pod 的指令。

Node 系统容量:描述 Node 可用的系统资源,包括 CPU、内存数量、最大可调度 Pod 数量等。

其他:Node 的其他信息,包括实例的内核版本号、Kubernetes 版本号、Docker 版本号、操作系统名称等。

1. Node 的管理

Node 通常是物理机、虚拟机或者云服务商提供的资源,并不是由 Kubernetes 创建的。我们说 Kubernetes 创建一个 Node,仅仅表示 Kubernetes 在系统内部创建了一个 Node 对象,创建后即会对其进行一系列健康检查,包括是否可以连通、服务是否正确启动、是否可以创建 Pod 等。如果检查未能通过,则该 Node 将会在集群中被标记为不可用(Not Ready)。

2. 使用 Node Controller 对 Node 进行管理

Node Controller 是 Kubernetes Master 中的一个组件,用于管理 Node 对象。它的两个主要功能包括:集群范围内的 Node 信息同步,以及单个 Node 的生命周期管理。
Node 信息同步可以通过 kube-controller-manager 的启动参数 --node-sync-period 设置同步的时间周期。

3. Node 的自注册

当 Kubelet 的 --register-node 参数被设置为 true(默认值即为 true)时,Kubelet 会向 apiserver 注册自己。这也是 Kubernetes 推荐的 Node 管理方式。

Kubelet 进行自注册的启动参数如下:

--apiservers=: apiserver 地址;

--kubeconfig=: 登录 apiserver 所需凭据 / 证书的目录;

--cloud_provider=: 云服务商地址,用于获取自身的 metadata;

--register-node=: 设置为 true 表示自动注册到 apiserver。

4. 手动管理 Node

Kubernetes 集群管理员也可以手工创建和修改 Node 对象。当需要这样操作时,先要将 Kubelet 启动参数中的 --register-node 参数的值设置为 false。这样,在 Node 上的 Kubelet 就不会把自己注册到 apiserver 中去了。

另外,Kubernetes 提供了一种运行时加入或者隔离某些 Node 的方法。具体操作请参考第四章。

1.3.2 Pod

Pod 是 Kubernetes 的最基本操作单元,包含一个活多个紧密相关的容器,类似于豌豆荚的概念。一个 Pod 可以被一个容器化的环境看作应用层的 “逻辑宿主机”(Logical Host)。一个 Pod 中的多个容器应用通常是紧耦合的。Pod 在 Node 上被创建、启动或者销毁。

为什么 Kubernetes 使用 Pod 在容器之上再封装一层呢?一个很重要的原因是,Docker 容器之间的通信受到 Docker 网络机制的限制。在 Docker 的世界中,一个容器需要 link 方式才能访问另一个容器提供的服务(端口)。大量容器之间的 link 将是一个非常繁重的工作。通过 Pod 的概念将多个容器组合在一个虚拟的 “主机” 内,可以实现容器之间仅需要通过 Localhost 就能相互通信了。

一个 Pod 中的应用容器共享同一组资源,如下所述:

PID 命名空间:Pod 中的不同应用程序可以看到其他应用程序的进程 ID;

网络命名空间:Pod 中的多个容器能够访问同一个 IP 和端口范围;

IPC 命名空间:Pod 中的多个容器能够使用 SystemV IPC 或者 POSIX 消息队列进行通信;

UTS 命名空间:Pod 中的多个容器共享一个主机名;

Volumes(共享存储卷):Pod 中的各个容器可以访问在 Pod 级别定义的 Volumes。

1. 对 Pod 的定义

对 Pod 的定义通过 Yaml 或 Json 格式的配置文件来完成。下面的配置文件将定义一个名为 redis-slave 的 Pod,其中 kind 为 Pod。在 spec 中主要包含了 Containers(容器)的定义,可以定义多个容器。

apiVersion: v1
kind: Pod
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
containers:
- name: slave
image: kubeguide/guestbook-redis-slave
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 6379

Pod 的生命周期是通过 Replication Controller 来管理的。Pod 的生命周期过程包括:通过模板进行定义,然后分配到一个 Node 上运行,在 Pod 所含容器运行结束后 Pod 也结束。在整个过程中,Pod 处于一下 4 种状态之一:

Pending:Pod 定义正确,提交到 Master,但其所包含的容器镜像还未完成创建。通常 Master 对 Pod 进行调度需要一些时间,之后 Node 对镜像进行下载也需要一些时间;

Running:Pod 已被分配到某个 Node 上,且其包含的所有容器镜像都已经创建完成,并成功运行起来;

Succeeded:Pod 中所有容器都成功结束,并且不会被重启,这是 Pod 的一种最终状态;

Failed:Pod 中所有容器都结束了,但至少一个容器是以失败状态结束的,这也是 Pod 的一种最终状态。

Kubernetes 为 Pod 设计了一套独特的网络配置,包括:为每个 Pod 分配一个 IP 地址,使用 Pod 名作为容器间通信的主机名等。关于 Kubernetes 网络的设计原理将在第 2 章进行详细说明。

另外,不建议在 Kubernetes 的一个 Pod 内运行相同应用的多个实例。

1.3.3 Label(标签)

Label 是 Kubernetes 系统中的一个核心概念。Label 以 key/value 键值对的形式附加到各种对象上,如 Pod、Service、RC、Node 等。Label 定义了这些对象的可识别属性,用来对它们进行管理和选择。Label 可以在创建时附加到对象上,也可以在对象创建后通过 API 进行管理。

在为对象定义好 Label 后,其他对象就可以使用 Label Selector(选择器)来定义其作用的对象了。

Label Selector 的定义由多个逗号分隔的条件组成。

"labels": {
"key1": "value1",
"key2": "value2"
}

当前有两种 Label Selector:基于等式的(Equality-based)和基于集合的(Set-based),在使用时可以将多个 Label 进行组合来选择。

基于等式的 Label Selector 使用等式类的表达式来进行选择:

name = redis-slave: 选择所有包含 Label 中 key="name" 且 value="redis-slave" 的对象;
env != production: 选择所有包括 Label 中的 key="env" 且 value 不等于 "production" 的对象。
基于集合的 Label Selector 使用集合操作的表达式来进行选择:

name in (redis-master, redis-slave): 选择所有包含 Label 中的 key="name" 且 value="redis-master" 或 "redis-slave" 的对象;
name not in (php-frontend): 选择所有包含 Label 中的 key="name" 且 value 不等于 "php-frontend" 的对象。
在某些对象需要对另一些对象进行选择时,可以将多个 Label Selector 进行组合,使用逗号 "," 进行分隔即可。基于等式的 LabelSelector 和基于集合的 Label Selector 可以任意组合。例如:

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

1.3.4 Replication Controller(RC)

Replication Controller 是 Kubernetes 系统中的核心概念,用于定义 Pod 副本的数量。在 Master 内,Controller Manager 进程通过 RC 的定义来完成 Pod 的创建、监控、启停等操作。

根据 Replication Controller 的定义,Kubernetes 能够确保在任意时刻都能运行用于指定的 Pod“副本”(Replica)数量。如果有过多的 Pod 副本在运行,系统就会停掉一些 Pod;如果运行的 Pod 副本数量太少,系统就会再启动一些 Pod,总之,通过 RC 的定义,Kubernetes 总是保证集群中运行着用户期望的副本数量。

同时,Kubernetes 会对全部运行的 Pod 进行监控和管理,如果有需要(例如某个 Pod 停止运行),就会将 Pod 重启命令提交给 Node 上的某个程序来完成(如 Kubelet 或 Docker)。

可以说,通过对 Replication Controller 的使用,Kubernetes 实现了应用集群的高可用性,并大大减少了系统管理员在传统 IT 环境中需要完成的许多手工运维工作(如主机监控脚本、应用监控脚本、故障恢复脚本等)。

对 Replication Controller 的定义使用 Yaml 或 Json 格式的配置文件来完成。以 redis-slave 为例,在配置文件中通过 spec.template 定义 Pod 的属性(这部分定义与 Pod 的定义是一致的),设置 spec.replicas=2 来定义 Pod 副本的数量。

apiVersion: v1
kind: ReplicationController
metadata:
name: redis-slave
labels: redis-slave
name: redis-slave
spec:
replicas: 2
selector:
name: redis-slave
template:
metadata:
labels:
name: redis-slave
spec:
container:
- name: slave
image: kubeguide/guestbook-redis-slave
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 6379

通常,Kubernetes 集群中不止一个 Node,假设一个集群有 3 个 Node,根据 RC 的定义,系统将可能在其中的两个 Node 上创建 Pod。

1.3.5 Service(服务)

在 Kubernetes 的世界里,虽然每个 Pod 都会被分配一个单独的 IP 地址,这个 IP 地址会随时 Pod 的销毁而消失。这就引出一个问题:如果有一组 Pod 组成一个集群来提供服务,那么如何来访问它们呢?

Kubernetes 的 Service(服务)就是用来解决这个问题的核心概念。

一个 Service 可以看作一组提供相同服务的 Pod 的对外访问接口。Service 作用于哪些 Pod 是通过 Label Selector 来定义的。

1. 对 Service 的定义

对 Service 的定义同样使用 Yaml 或 Json 格式的配置文件来完成。以 redis-slave 服务的定义为例:

apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
ports:
- port: 6379
selector:
name: redis-slave

通过该定义,Kubernetes 将会创建一个名为 “redis-slave” 的服务,并在 6379 端口上监听。spec.selector 的定义表示该 Service 将包含所有具有 "name=redis-slave" 的 Label 的 Pod。

在 Pod 正常启动后,系统将会根据 Service 的定义创建出与 Pod 对应的 Endpoint(端点)对象,以建立起 Service 与后端 Pod 的对应关系。随着 Pod 的创建、销毁,Endpoint 对象也将被更新。Endpoint 对象主要有 Pod 的 IP 地址和容器所需监听的端口号组成。

2. Pod 的 IP 地址和 Service 的 Cluster IP 地址

Pod 的 IP 地址是 Docker Daemon 根据 docker0 网桥的 IP 地址段进行分配的,但 Service 的 Cluster IP 地址是 Kubernetes 系统中的虚拟 IP 地址,由系统动态分配。Service 的 Cluster IP 地址相对于 Pod 的 IP 地址来说相对稳定,Service 被创建时即被分配一个 IP 地址,在销毁该 Service 之前,这个 IP 地址都不会再变化了。而 Pod 在 Kubernetes 集群中生命周期较短,可能被 ReplicationContrller 销毁、再次创建,新创建的 Pod 将会分配一个新的 IP 地址。

3. 外部访问 Service

由于 Service 对象在 Cluster IP Range 池中分配到的 IP 只能在内部访问,所以其他 Pod 都可以无障碍地访问到它。但如果这个 Service 作为前端服务,准备为集群外的客户端提供服务,我们就需要给这个服务提供公共 IP 了。

Kubernetes 支持两种对外提供服务的 Service 的 type 定义:NodePort 和 LoadBalancer。具体分析参见 如何从外部访问 Kubernetes 集群中的应用

  • NodePort

在定义 Service 时指定 spec.type=NodePort,并指定 spec.ports.nodePort 的值,系统就会在 Kubernetes 集群中的每个 Node 上打开一个主机上的真实端口号。这样,能够访问 Node 的客户端都就能通过这个端口号访问到内部的 Service 了。

以 php-frontend service 的定义为例,nodePort=80,这样,在每一个启动了该 php-frontend Pod 的 Node 节点上,都会打开 80 端口。

apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
name: frontend
spec:
type: NodePort
ports:
- port: 80
nodePort: 30001
selector:
name: frontend
  • LoadBalancer

如果云服务商支持外接负载均衡器,则可以通过 spec.type=LoadBalaner 定义 Service,同时需要制定负载均衡器的 IP 地址。使用这种类型需要指定 Service 的 nodePort 和 clusterIP。例如:

apiVersion: v1
kind: Service
metadata: {
"kind" "Service",
"apiVersion": "v1",
"metadata": {
"name": "my-service"
},
"spec": {
"type": "LoadBalaner",
"clusterIP": "10.0.171.239",
"selector": {
"app": "MyApp"
},
"ports": [
{
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 30061
}
],
},
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "146.148.47.155"
}
]
}
}
}

在这个例子中,status.loadBalancer.ingress.ip 设置的 146.146.47.155 为云服务商提供的负载均衡器的 IP 地址。

之后,对该 Service 的访问请求将会通过 LoadBalancer 转发到后端 Pod 上去,负载分发的实现方式则依赖于云服务上提供的 LoadBalancer 的实现机制。

1.3.6 Volume(存储卷)

Volume 是 Pod 中能够被多个容器访问的共享目录。Kubernetes 的 Volume 概念与 Docker 的 Volume 比较类似,但不完全相同。Kubernetes 中的 Volume 与 Pod 生命周期相同,但与容器的生命周期不相关。当容器终止或者重启时,Volume 中的数据也不会丢失。另外,Kubernetes 支持多种类型的 Volume,并且一个 Pod 可以同时使用任意多个 Volume。
Kubernetes 提供了非常丰富的 Volume 类型,下面逐一进行说明。

EmptyDir:一个 EmptyDir Volume 是在 Pod 分配到 Node 时创建的。从它的名称就可以看出,它的初始内容为空。在同一个 Pod 中所有容器可以读和写 EmptyDir 中的相同文件。当 Pod 从 Node 上移除时,EmptyDir 中的数据也会永久删除。

hostPath:在 Pod 上挂载宿主机上的文件或目录。

gcePersistentDisk:使用这种类型的 Volume 表示使用谷歌计算引擎(Google Compute Engine,GCE)上永久磁盘(Persistent Disk,PD)上的文件。与 EmptyDir 不同,PD 上的内容会永久保存,当 Pod 被删除时,PD 只是被卸载(Unmount),但不会被删除。需要注意的是,你需要先创建一个永久磁盘(PD)才能使用 gcePersistentDisk。

awsElasticBlockStore:与 GCE 类似,该类型的 Volume 使用 Amazon 提供的 Amazon Web Service(AWS)的 EBS Volume,并可以挂在到 Pod 中去。需要注意到是,需要首先创建一个 EBS Volume 才能使用 awsElasticBlockStore。

nfs:使用 NFS(网络文件系统)提供的共享目录挂载到 Pod 中。在系统中需要一个运行中的 NFS 系统。

iscsi:使用 iSCSI 存储设备上的目录挂载到 Pod 中。

glusterfs:使用开源 GlusterFS 网络文件系统的目录挂载到 Pod 中。

rbd:使用 Linux 块设备共享存储(Rados Block Device)挂载到 Pod 中。

gitRepo:通过挂载一个空目录,并从 GIT 库 clone 一个 git respository 以供 Pod 使用。

secret:一个 secret volume 用于为 Pod 提供加密的信息,你可以将定义在 Kubernetes 中的 secret 直接挂载为文件让 Pod 访问。secret volume 是通过 tmfs(内存文件系统)实现的,所以这种类型的 volume 总是不会持久化的。

persistentVolumeClaim:从 PV(PersistentVolume)中申请所需的空间,PV 通常是一种网络存储,例如 GCEPersistentDisk、AWSElasticBlockStore、NFS、iSCSI 等。

1.3.7 Namespace(命名空间)

Namespace(命名空间)是 Kubernetes 系统中的另一个非常重要的概念,通过将系统内部的对象 “分配” 到不同的 Namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。

Kubernetes 集群在启动后,会创建一个名为 “default” 的 Namespace,通过 Kubectl 可以查看到。
使用 Namespace 来组织 Kubernetes 的各种对象,可以实现对用户的分组,即 “多租户” 管理。对不同的租户还可以进行单独的资源配额设置和管理,使得整个集群的资源配置非常灵活、方便。

1.3.8 Annotation(注解)

Annotation 与 Label 类似,也使用 key/value 键值对的形式进行定义。Label 具有严格的命名规则,它定义的是 Kubernetes 对象的元数据(Metadata),并且用于 Label Selector。Annotation 则是用户任意定义的 “附加” 信息,以便于外部工具进行查找。
用 Annotation 来记录的信息包括:

build 信息、release 信息、Docker 镜像信息等,例如时间戳、release id 号、PR 号、镜像 hash 值、docker registry 地址等;

日志库、监控库、分析库等资源库的地址信息;

程序调试工具信息,例如工具名称、版本号等;

团队的联系信息,例如电话号码、负责人名称、网址等。

1.3.9 小结

上述这些组件是 Kubernetes 系统的核心组件,它们共同构成了 Kubernetes 系统的框架和计算模型。通过对它们进行灵活组合,用户就可以快速、方便地对容器集群进行配置、创建和管理。

除了以上核心组件,在 Kubernetes 系统中还有许多可供配置的资源对象,例如 LimitRange、ResourceQuota。另外,一些系统内部使用的对象 Binding、Event 等请参考 Kubernetes 的 API 文档。

1.4 Kubernetes 总体架构

Kubernetes 集群由两类节点组成:Master 和 Node。在 Master 上运行 etcd、API Server、Controller Manager 和 Scheduler 四个组件,其中后三个组件构成了 Kubernetes 的总控中心,负责对集群中所有资源进行管理和调度。在每个 Node 上运行 Kubelet、Proxy 和 Docker Daemon 三个组件,负责对本节点上的 Pod 的生命周期进行管理,以及实现服务代理的功能。另外在所有节点上都可以运行 Kubectl 命令行工具,它提供了 Kubernetes 的集群管理工具集。

`