本文是《Docker必知必会系列》第八篇,原文发布于个人博客:悟尘记。
一、前言
1、Kubernetes 是什么
Kubernetes (K8s) 是 Google 基于 Borg 开源的容器编排调度引擎,是云原生应用的基石,已成为事实标准。基于规范描述集群架构,定义服务的最终状态,并使系统自动地达到和维持该状态。
上图揭示了从传统部署时代 到 虚拟化部署时代 再到 容器部署时代 的演进过程。
2、为什么需要 Kubernetes
在生产环境中,您需要管理运行应用程序的容器,并确保不会停机。例如,如果一个容器发生故障,则需要启动另一个容器。如果有个系统工具来处理这些需求,会不会更容易?这就是 Kubernetes!
Kubernetes 提供了一个可弹性运行分布式系统的框架。满足扩展、故障转移、部署模式等要求。提供:
服务发现和负载均衡
Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,并提供负载均衡来分配网络流量。存储编排
Kubernetes 允许您自动挂载您选择的存储系统,例如本地存储、公共云提供商等。自动部署和回滚
您可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为所需状态。例如,Kubernetes 可以自动化为您的部署创建新容器,删除现有容器并将它们的所有资源用于新容器。自动二进制打包
Kubernetes 允许您指定每个容器所需 CPU 和内存(RAM)。当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。自我修复
Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。密钥与配置管理
Kubernetes 允许您存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。您可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。Kubernetes 提供了构建开发人员平台的基础,但是在重要的地方保留了用户的选择和灵活性。很多云服务商基于K8s来构建自己的PaaS平台。
3、Kubernetes 概述
要使用 Kubernetes,你需要用 Kubernetes API 对象 来描述集群的 预期状态(desired state) :包括你需要运行的应用或者负载,它们使用的镜像、副本数,以及所需网络和磁盘资源等等。你可以使用命令行工具 kubectl
来调用 Kubernetes API 创建对象,通过所创建的这些对象来配置预期状态。你也可以直接调用 Kubernetes API 和集群进行交互,设置或者修改预期状态。
一旦你设置了你所需的目标状态,Kubernetes Control Plane
会通过 Pod 生命周期事件生成器(PLEG)使集群的当前状态与预期匹配。为此,Kubernetes 会自动执行各类任务,比如运行或者重启容器、调整给定应用的副本数等等。
Kubernetes Control Plane
Kubernetes Control Plane
管理 Kubernetes 如何与你的集群进行通信。维护系统中所有的 Kubernetes 对象的状态记录,并且通过连续的控制循环来管理这些对象的状态。在任意的给定时间点,Kubernetes Control Plane
的控制环都能响应集群中的变化,并且让系统中所有对象的实际状态与你提供的预期状态相匹配。
Kubernetes Master 节点
Kubernetes master 节点负责维护集群的目标状态,包括多个进程。通常这些进程都运行在集群中一个单独的节点上,这个节点被称为 master 节点。当你要与 Kubernetes 通信时,使用如 kubectl
的命令行工具,就可以直接与 Kubernetes master 节点进行通信。Master节点包含的进程如下:
- kube-apiserver:所有其他组件通过与
apiserver
接口交互,实现集群状态管理。 - kube-controller-manager:一个用于调节系统状态的守护进程,通过
apiserver
监视集群状态,并保持当前状态与所需状态一致。 - kube-scheduler:负责Pod调度。
master 节点也可以扩展副本数,来获取更好的可用性及冗余。
Kubernetes Node 节点
集群中的 Node 节点(虚拟机、物理机等等)都是用来运行你的应用和云工作流的机器。Master 节点控制所有 Node 节点;你很少需要和 Node 节点进行直接通信。
集群中的每个 Node 节点运行两个进程:
- kubelet:和 master 节点进行通信。
- kube-proxy:将 Kubernetes 的网络服务代理到每个节点上。
4、Kubernetes 对象
Kubernetes 包含若干用来表示系统状态的抽象层,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。这些抽象使用 Kubernetes API 对象来表示。
Kubernetes 对象 是持久化的实体,表示整个集群的状态。具体来说,它们描述了如下信息:
- 哪些容器化应用在运行(以及在哪个 Node 上)
- 可以被应用使用的资源
- 关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略
对象一旦创建,Kubernetes 系统将持续工作以确保对象存在。通过创建对象,本质上是在告知 Kubernetes 系统,所需要的集群工作负载看起来是什么样子的,这就是 Kubernetes 集群的 期望状态(Desired State)。
操作 Kubernetes 对象需要使用 Kubernetes API。比如,当使用 kubectl
命令行接口时,CLI 会执行必要的 Kubernetes API 调用,也可以在程序中使用 客户端库 直接调用 Kubernetes API。
基本的 Kubernetes 对象包括:
- Pod:Kubernetes 的原子对象,表示您的集群上一组正在运行的容器。
- Service:将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。
- Volume:包含可被 Pod 中容器访问的数据的目录。
- Namespace:命名空间是 Kubernetes 为了在同一物理集群上支持多个虚拟集群而使用的一种抽象。
Kubernetes 也包含大量的被称作 Controller
的高级抽象。控制器基于基本对象构建并提供额外的功能和方便使用的特性。具体包括:
- Deployment:管理应用副本的 API 对象。
- DaemonSet:确保 Pod 的副本在集群中的一组节点上运行。
- StatefulSet:用来管理 Deployment 和扩展一组 Pod,并且能为这些 Pod 提供序号和唯一性保证。
- ReplicaSet:下一代副本控制器。
- Job:需要运行完成的确定性的或批量的任务。
有关更多详细信息,请参阅:了解 Kubernetes 对象。
二、Kubernetes 架构
当部署完 Kubernetes,即拥有了一个完整的集群。 集群由一组被称作节点的机器组成。这些节点上运行 Kubernetes 所管理的容器化应用。每个集群至少有一个工作节点。
这张图表展示了 Kubernetes 集群中所包含的所有相互关联的组件:
1、控制平面组件(Control Plane Components)
控制平面组件对集群做出全局决策(比如调度),以及检测和响应集群事件(例如,当不满足部署的 replicas
字段时,启动新的 Pod)。
控制平面组件可以在集群中的任何节点上运行。然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件,并且不会在此计算机上运行用户容器。
kube-apiserver
主节点上负责提供 Kubernetes API 服务的组件。它是 Kubernetes 控制平面的前端,在设计上考虑了水平扩缩的需要。 换言之,通过部署多个实例可以实现扩缩。
etcd
etcd 是兼具一致性和高可用性的键值数据库,作为保存 Kubernetes 所有集群数据的后台数据库。
kube-scheduler
主节点上的组件,该组件监视那些尚未指定运行节点的新创建 Pod,并选择节点让 Pod 在上面运行。
调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。
kube-controller-manager
在主节点上运行控制器的组件。从逻辑上讲,每个控制器都是一个单独的进程,但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。这些控制器包括:
- 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应。
- 副本控制器(Replication Controller): 负责为系统中的每个副本控制器对象维护正确数量的 Pod。
- 端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)。
- 服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌.
云控制器管理器-(cloud-controller-manager)
运行与基础云提供商交互的控制器。下面的控制器可以依赖云提供商:
- 节点控制器(Node Controller): 用于检查云提供商以确定节点是否在云中停止响应后被删除。
- 路由控制器(Route Controller): 用于在底层云基础架构中设置路由。
- 服务控制器(Service Controller): 用于创建、更新和删除云提供商负载均衡器。
- 数据卷控制器(Volume Controller): 用于创建、附加和装载卷、并与云提供商进行交互以编排卷。
2、Node 组件
节点(Node)是执行工作的机器,根据您的集群环境,节点可以是一个虚拟机或者物理机器。每个节点都包含用于运行 Pods 的必要服务,包括容器运行环境、kubelet 和 kube-proxy。
kubelet
一个在集群中每个节点上运行的代理。它保证容器都运行在 Pod 中。
kubelet
接收一组通过各类机制提供给它的 PodSpecs
,确保这些 PodSpecs
中描述的容器处于运行状态且健康。kubelet
不会管理不是由 Kubernetes 创建的容器。
kube-proxy
kube-proxy
是集群中每个节点上运行的网络代理,实现 Kubernetes Service 概念的一部分。kube-proxy
维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。
容器运行环境(Container Runtime)
容器运行环境是负责运行容器的软件。Kubernetes 支持 Docker、 containerd、cri-o、 rktlet 等多种运行环境。
3、插件(Addons)
插件使用 Kubernetes 资源 (DaemonSet, Deployment等) 扩展了集群功能。因为其提供了集群级别的特性,所以插件的命名空间属于 kube-system
。有关可用插件的扩展列表,请参见:插件 (Addons)
三、部署 Kubernetes
您可以在本地机器、云、本地数据中心上部署 Kubernetes 集群,或选择一个托管的 Kubernetes 集群。还可以跨各种云提供商或裸机环境创建自定义解决方案。
为了学习 Kubernetes,建议使用基于 Docker 的解决方案:Docker 是 Kubernetes 社区支持或生态系统中用来在本地计算机上设置 Kubernetes 集群的一种工具。如果是为了生产环境,您应该认真评估适当的解决方案,具体请参照官方文档。
1、Docker Desktop 启用 Kubernetes
在 Docker Desktop 的 Preferences
页面,点击 Kubernetes
,选择 Enable Kubernetes
,待左下角的 Kubernetes
状态变为 running
时,Kubernetes 即已启动成功。
在控制台执行 kubectl version
,能够正常输出信息,则说明Kubernetes 已经成功启动。
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:26:26Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:18:29Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/amd64"}
如果您使用的是 Oh-My-Zsh,可通过编辑 ~/.zshrc
文件,添加 kubectl 插件以启用 kubectl 自动补全功能。
plugins=(kubectl)
2、安装 Dashboard
Dashboard 是官方提供的基于网页的 Kubernetes 用户管理界面。您可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。您可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源(如 Deployment,Job,DaemonSet 等等)。例如,您可以对 Deployment 实现弹性伸缩、发起滚动升级、重启 Pod 或者使用向导创建新的应用。
默认情况下不会部署 Dashboard。可以通过以下命令部署:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
在要访问 Dashboard 的机器上执行 kubectl proxy
命令,然后就可以通过 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
地址访问 Dashboard了。
当前,Dashboard 仅支持使用 Bearer 令牌登录。 接下来,我们将创建一个名为 admin-user
的服务账号,并为其创建 ClusterRoleBinding。
将以下内容复制到 dashboard-adminuser.yaml
文件中。
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
然后执行:
kubectl apply -f dashboard-adminuser.yaml
找到可以用来登录的令牌:
$ kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')
Name: admin-user-token-8mgwp
Namespace: kubernetes-dashboard
Labels: <none>
Annotations: kubernetes.io/service-account.name: admin-user
kubernetes.io/service-account.uid: c32b99ea-a002-4e00-afdf-58ce40fbe5eb
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 20 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkJPNFJmTFJ1ajBKYm4xMGpvV01vYnRRSDdzRnFmd1ZsbUpPQ0lvdFNJWFkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3......povxntPXVTJomVTN7mJ-6vBm8m-kCUF6nxA14c8s5Kis22XPGyPgiUmXNAagXJPJH3cFVO0exs5JVzdj3gtvRXQ
复制 token
并将其粘贴到登录屏幕上的 Token
字段中。点击登录按钮,就可以以管理员身份登录了。
四、Kubernetes 动手实践
一旦运行了 Kubernetes 集群,就可以在其上部署容器化应用程序。 为此,您需要创建 Kubernetes Deployment 配置。Deployment 指挥 Kubernetes 如何创建和更新应用程序的实例。创建 Deployment 后,Kubernetes master 将应用程序实例调度到集群中的各个节点上。
创建应用程序实例后,Kubernetes Deployment 控制器会持续监视这些实例。 如果托管实例的节点关闭或被删除,则 Deployment 控制器会将该实例替换为群集中另一个节点上的实例。 这提供了一种自我修复机制来解决机器故障维护问题。
1、环境检查
通过运行 kubectl version
命令,检查 kubectl 是否已配置为与您的集群通信。运行 kubectl get nodes
命令,查看集群中的节点及pods。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 112m v1.16.6-beta.0
$ kubectl get pods
No resources found in default namespace.
在这里,我们看到可用的节点(本例中为1)。 Kubernetes 将根据 Node 可用资源选择将我们的应用程序部署到何处。
2、基于 kubectl 部署第一个应用
让我们使用 kubectl create deploy
命令在 Kubernetes 上部署我们的第一个应用程序。
创建一个名为 k8s-nginx 的 deployment(需要提供部署名称和应用程序镜像位置):
$ kubectl create deployment --image nginx k8s-nginx
deployment.apps/k8s-nginx created
查看正在运行的 pods,会看到只有一个 pods 正在运行:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
k8s-nginx-5c86f54778-92824 0/1 ContainerCreating 0 11s
查看创建的 deployment:
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
k8s-nginx 0/1 1 0 24s
扩展部署,让 2 个 nginx pods 同时运行:
$ kubectl scale deployment --replicas 2 k8s-nginx
deployment.apps/k8s-nginx scaled
列出 pods ,会看到已经变成 2 个 pods 正在运行:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
k8s-nginx-5c86f54778-92824 0/1 Running 0 67s
k8s-nginx-5c86f54778-j7dch 0/1 Running 0 6s
将 pods 暴露到互联网上:
kubectl expose deployment k8s-nginx --port=8086 --type=LoadBalancer
service/k8s-nginx exposed
查看已经创建的服务,获取服务地址:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
k8s-nginx LoadBalancer 10.100.197.113 <none> 8086/TCP 74s
要清理这两个自动复制的容器,需要删除 deployment:
$ kubectl delete deployment k8s-nginx
deployment.apps "k8s-nginx" deleted
# 再次列出 pods 会发现容器已经被清理。
$ kubectl get pods
No resources found in default namespace.
3、基于 Deployments 管理应用
可以通过创建一个Kubernetes Deployment 对象来运行一个应用,在YAML文件中描述Deployment。
首先,创建 deployment 描述文件 k8s-nginx-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-nginx-deployment # 创建名为 k8s-nginx-deployment 的 Deployment
labels:
app: k8s-nginx
spec:
replicas: 3 # 创建 3 个 Pods
selector: # 定义 Deployment 如何查找要管理的 Pods
matchLabels:
app: k8s-nginx
template:
metadata:
labels: # 将 Pod 标记为 k8s-nginx
app: k8s-nginx
spec:
containers:
- name: k8s-nginx # 指定创建的容器名字为 k8s-nginx
image: nginx # 指定运行 Docker Hub 中 nginx 最新版本镜像
ports: # 暴露端口
- containerPort: 8189
通过 kubectl apply
命令创建 Deployment:
$ kubectl apply -f k8s-nginx-deployment.yaml
deployment.apps/k8s-nginx-deployment created
运行 kubectl get deployments
以检查 Deployment 是否已创建:
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
k8s-nginx-deployment 3/3 3 3 47s
几秒钟后,Deployment 已创建所有三个副本,并且所有副本都是最新的(它们包含最新的 Pod 模板)并且可用。
更新 Deployment
让我们更新 nginx Pods,以使用 nginx:1.9.1
镜像,而不是最新版本。
$ kubectl --record deployment.apps/k8s-nginx-deployment set image deployment.v1.apps/k8s-nginx-deployment k8s-nginx=nginx:1.9.1
deployment.apps/k8s-nginx-deployment image updated
deployment.apps/k8s-nginx-deployment image updated
查看状态更新:
$ kubectl rollout status deployment.v1.apps/k8s-nginx-deployment
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "k8s-nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "k8s-nginx-deployment" successfully rolled out
更新成功后,可以通过运行 kubectl get deployments
来查看 Deployment。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
k8s-nginx-deployment 3/3 3 3 14m
运行 get pods
现在应仅显示新的 Pods:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
k8s-nginx-deployment-785b758787-jsbtl 1/1 Running 0 2m31s
k8s-nginx-deployment-785b758787-xdsl4 1/1 Running 0 4m12s
k8s-nginx-deployment-785b758787-zxd7b 1/1 Running 0 2m28s
下次要更新这些 Pods 时,只需再次更新 Deployment Pod 模板。
Deployment 可确保在更新时仅关闭一定数量的 Pods。默认情况下,它确保至少 75%所需 Pods 运行(25%最大不可用)。Deployment 还确保仅创建一定数量的 Pods 高于期望的 Pods 数。默认情况下,它可确保最多增加 25% 期望 Pods 数(25%最大增量)。
如果仔细查看上述 Deployment ,将看到它首先创建了一个新的 Pod,然后删除了一些旧的 Pods,并创建了新的 Pods。它不会杀死老 Pods,直到有足够的数量新的 Pods 已经出现,并没有创造新的 Pods,直到足够数量的旧 Pods 被杀死。它确保至少 2 个 Pods 可用,并且总共最多 4 个 Pods 可用。
获取 Deployment 的更多信息
$ kubectl describe deployments
kubectl describe deployments
Name: k8s-nginx-deployment
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 16m deployment-controller Scaled up replica set k8s-nginx-deployment-99f6477fd to 3
Normal ScalingReplicaSet 6m39s deployment-controller Scaled up replica set k8s-nginx-deployment-785b758787 to 1
Normal ScalingReplicaSet 4m57s deployment-controller Scaled down replica set k8s-nginx-deployment-99f6477fd to 2
Normal ScalingReplicaSet 4m57s deployment-controller Scaled up replica set k8s-nginx-deployment-785b758787 to 2
Normal ScalingReplicaSet 4m54s deployment-controller Scaled down replica set k8s-nginx-deployment-99f6477fd to 1
Normal ScalingReplicaSet 4m54s deployment-controller Scaled up replica set k8s-nginx-deployment-785b758787 to 3
Normal ScalingReplicaSet 4m49s deployment-controller Scaled down replica set k8s-nginx-deployment-99f6477fd to 0
可以看到,当第一次创建 Deployment 时,它创建了一个 ReplicaSet (nginx-deployment-99f6477fd)并将其直接扩展至 3 个副本。更新 Deployment 时,它创建了一个新的 ReplicaSet (nginx-deployment-785b758787),并将其扩展为 1,然后将旧 ReplicaSet 缩小到 2,以便至少有 2 个 Pod 可用,并且最多创建 4 个 Pod。然后,它继续向上和向下扩展新的和旧的 ReplicaSet ,具有相同的滚动更新策略。最后,将有 3 个可用的副本在新的 ReplicaSet 中,旧 ReplicaSet 将缩小到 0。
有时,可能需要回滚 Deployment ;例如,当 Deployment 不稳定时,例如循环崩溃。默认情况下,所有 Deployment 历史记录都保留在系统中,以便可以随时回滚(可以通过修改修改历史记录限制来更改该限制)。
也可以通过设置自动缩放器,基于现有 Pods 的 CPU 利用率设置要运行 Pods的最小和最大值。
还可以在触发一个或多个更新之前暂停 Deployment ,然后继续它。这允许在暂停和恢复之间应用多个修补程序,而不会触发不必要的 Deployment 。
关于 Deployment 的更多信息,请查阅官方文档:Deployments
参考
官方文档:Kubernetes 文档
官方文档:Creating sample user