17 实际搭建多节点的Kubernetes集群

什么是 kubeadm

前面的几节课里我们使用的都是 minikube,它非常简单易用,不需要什么配置工作,就能够在单机环境里创建出一个功能完善的 Kubernetes 集群,给学习、开发、测试都带来了极大的便利。

不过 minikube 还是太“迷你”了,方便的同时也隐藏了很多细节,离真正生产环境里的计算集群有一些差距,毕竟许多需求、任务只有在多节点的大集群里才能够遇到,相比起来,minikube 真的只能算是一个“玩具”。

不过 Kubernetes 里的这些组件的配置和相互关系实在是太复杂了,用 Shell、Ansible 来部署的难度很高,需要具有相当专业的运维管理知识才能配置、搭建好集群,而且即使这样,搭建的过程也非常麻烦。

为了简化 Kubernetes 的部署工作,让它能够更“接地气”,社区里就出现了一个专门用来在集群中安装 Kubernetes 的工具,名字就叫“kubeadm”,意思就是“Kubernetes 管理员”。

而如果现在 kubelet 本身就运行在一个容器里,那么直接操作宿主机就会变得很麻烦。对于网络配置来说还好,kubelet 容器可以通过不开启 Network Namespace(即 Docker 的 host network 模式)的方式,直接共享宿主机的网络栈。可是,要让 kubelet 隔着容器的 Mount Namespace 和文件系统,操作宿主机的文件系统,就有点儿困难了


正因为如此,kubeadm 选择了一种妥协方案:把 kubelet 直接运行在宿主机上,然后使用容器部署其他的 Kubernetes 组件。




实验环境的架构是什么样的

里我画了一张系统架构图,图里一共有 3 台主机,当然它们都是使用虚拟机软件 VirtualBox/VMWare 虚拟出来的,下面我来详细说明一下

Kubernetes 入门实战03 中级篇_多节点

所谓的多节点集群,要求服务器应该有两台或者更多,为了简化我们只取最小值,所以这个 Kubernetes 集群就只有两台主机,一台是 Master 节点,另一台是 Worker 节点。当然,在完全掌握了 kubeadm 的用法之后,你可以在这个集群里添加更多的节点。

Kubernetes 入门实战03 中级篇_多节点_02

Kubernetes 入门实战03 中级篇_多节点_03

在通过了 Preflight Checks 之后,kubeadm 要为你做的,是生成 Kubernetes 对外提供服务所需的各种证书和对应的目录。



安装前的准备工作

不过有了架构图里的这些主机之后,我们还不能立即开始使用 kubeadm 安装 Kubernetes,因为 Kubernetes 对系统有一些特殊要求,我们必须还要在 Master 和 Worker 节点上做一些准备

Kubernetes 入门实战03 中级篇_多节点_04

Kubernetes 入门实战03 中级篇_多节点_05

Kubernetes 入门实战03 中级篇_多节点_06

Kubernetes 入门实战03 中级篇_多节点_07

安装 kubeadm

Kubernetes 入门实战03 中级篇_多节点_08

Kubernetes 入门实战03 中级篇_多节点_09

下载 Kubernetes 组件镜像

Kubernetes 入门实战03 中级篇_多节点_10

Kubernetes 入门实战03 中级篇_多节点_11


Kubernetes 入门实战03 中级篇_多节点_12

可以把这两种方法结合起来,先用脚本从国内镜像仓库下载,然后再用 minikube 里的镜像做对比,只要 IMAGE ID 是一样就说明镜像是正确的

安装 Master 节点

Kubernetes 入门实战03 中级篇_多节点_13


Kubernetes 入门实战03 中级篇_多节点_14

Kubernetes 入门实战03 中级篇_多节点_15

安装 Flannel 网络插件

Kubernetes 入门实战03 中级篇_多节点_16

Kubernetes 入门实战03 中级篇_多节点_17

安装 Worker 节点

Kubernetes 入门实战03 中级篇_多节点_18

Kubernetes 入门实战03 中级篇_多节点_19

17 Controller

Pod和Controller

Kubernetes 入门实战03 中级篇_多节点_20

Kubernetes 入门实战03 中级篇_多节点_21

Kubernetes 入门实战03 中级篇_多节点_22



18 Deployment:让应用永不宕机

“Deployment”,顾名思义,它是专门用来部署应用程序的,能够让应用永不宕机,多用来发布无状态的应用,是 Kubernetes 里最常用也是最有用的一个对象。

为什么要有 Deployment

Kubernetes 入门实战03 中级篇_多节点_23

Kubernetes 入门实战03 中级篇_多节点_24

Kubernetes 入门实战03 中级篇_多节点_25

Kubernetes 入门实战03 中级篇_多节点_26

Kubernetes 入门实战03 中级篇_多节点_27

总结:

maxUnavailable:和期望的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑;

maxSurge:和期望的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快。


还有我们也都知道,在线业务远不是单纯启动一个 Pod 这么简单,还有多实例、高可用、版本更新等许多复杂的操作。比如最简单的多实例需求,为了提高系统的服务能力,应对突发的流量和压力,我们需要创建多个应用的副本,还要即时监控它们的状态。如果还是只使用 Pod,那就会又走回手工管理的老路,没有利用好 Kubernetes 自动化运维的优势。

其实,解决的办法也很简单,因为 Kubernetes 已经给我们提供了处理这种问题的思路,就是“单一职责”和“对象组合”。既然 Pod 管理不了自己,那么我们就再创建一个新的对象,由它来管理 Pod,采用和 Job/CronJob 一样的形式——“对象套对象”。

如何使用 YAML 描述 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: xxx-dep

我们还是可以使用命令 kubectl create 来创建 Deployment 的 YAML 样板,免去反复手工输入的麻烦

Kubernetes 入门实战03 中级篇_多节点_28

得到的 Deployment 样板大概是下面的这个样子:

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ngx-dep
name: ngx-dep

spec:
replicas: 2
selector:
matchLabels:
app: ngx-dep

template:
metadata:
labels:
app: ngx-dep
spec:
containers:
- image: nginx:alpine
name: nginx

不同的地方在于它的“spec”部分多了 replicas、selector 这两个新字段,聪明的你应该会猜到,这或许就会是 Deployment 特殊能力的根本。没错,这两个新字段就是 Deployment 实现多实例、高可用等功能的关键所在

 replicas 字段。它的含义比较简单明了,就是“副本数量”的意思,也就是说,指定要在 Kubernetes 集群里运行多少个 Pod 实例

有了这个字段,就相当于为 Kubernetes 明确了应用部署的“期望状态”,Deployment 对象就可以扮演运维监控人员的角色,自动地在集群里调整 Pod 的数量

Kubernetes 入门实战03 中级篇_多节点_29

另一个关键字段 selector,它的作用是“筛选”出要被 Deployment 管理的 Pod 对象,下属字段“matchLabels”定义了 Pod 对象应该携带的 label,它必须和“template”里 Pod 定义的“labels”完全相同,否则 Deployment 就会找不到要控制的 Pod 对象,apiserver 也会告诉你 YAML 格式校验错误无法创建

而在线业务就要复杂得多了,因为 Pod 永远在线,除了要在 Deployment 里部署运行,还可能会被其他的 API 对象引用来管理,比如负责负载均衡的 Service 对象。

所以 Deployment 和 Pod 实际上是一种松散的组合关系,Deployment 实际上并不“持有”Pod 对象,它只是帮助 Pod 对象能够有足够的副本数量运行,仅此而已。如果像 Job 那样,把 Pod 在模板里“写死”,那么其他的对象再想要去管理这些 Pod 就无能为力了


那我们该用什么方式来描述 Deployment 和 Pod 的组合关系呢?

Kubernetes 采用的是这种“贴标签”的方式,通过在 API 对象的“metadata”元信息里加各种标签(labels),我们就可以使用类似关系数据库里查询语句的方式,筛选出具有特定标识的那些对象。通过标签这种设计,Kubernetes 就解除了 Deployment 和模板里 Pod 的强绑定,把组合关系变成了“弱引用“

Kubernetes 入门实战03 中级篇_多节点_30

Kubernetes 入门实战03 中级篇_多节点_31

Kubernetes 入门实战03 中级篇_多节点_32

Kubernetes 入门实战03 中级篇_多节点_33

模拟测试

Kubernetes 入门实战03 中级篇_多节点_34

调整pod数量

Kubernetes 入门实战03 中级篇_多节点_35

但要注意, kubectl scale 是命令式操作,扩容和缩容只是临时的措施,如果应用需要长时间保持一个确定的 Pod 数量,最好还是编辑 Deployment 的 YAML 文件,改动“replicas”,再以声明式的 kubectl apply 修改对象的状态。

使用selector 筛选pod


Kubernetes 入门实战03 中级篇_多节点_36

19 Daemonset:忠实可靠的看门狗

DaemonSet,它会在 Kubernetes 集群的每个节点上都运行一个 Pod,就好像是 Linux 系统里的“守护进程”(Daemon)

Deployment 并不关心这些 Pod 会在集群的哪些节点上运行,在它看来,Pod 的运行环境与功能是无关的,只要 Pod 的数量足够,应用程序应该会正常工作

Kubernetes 入门实战03 中级篇_多节点_37

Kubernetes 入门实战03 中级篇_多节点_38

对应的YAML如下

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: redis-ds
labels:
app: redis-ds

spec:
selector:
matchLabels:
name: redis-ds

template:
metadata:
labels:
name: redis-ds
spec:
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379

看清楚与 Deployment 的差异

Kubernetes 入门实战03 中级篇_多节点_39

了解到这些区别,现在,我们就可以用变通的方法来创建 DaemonSet 的 YAML 样板了,你只需要用 kubectl create 先创建出一个 Deployment 对象,然后把 kind 改成 DaemonSet,再删除 spec.replicas 就行了

Kubernetes 入门实战03 中级篇_多节点_40

Kubernetes 入门实战03 中级篇_多节点_41

kubectl taint node master node-role.kubernetes.io/master:NoSchedule-

Kubernetes 入门实战03 中级篇_多节点_42

第二种方法,为 Pod 添加字段 tolerations,让它能够“容忍”某些“污点”,就可以在任意的节点上运行了

Kubernetes 入门实战03 中级篇_多节点_43

Kubernetes 入门实战03 中级篇_多节点_44

什么是静态 Pod

Kubernetes 入门实战03 中级篇_多节点_45

Kubernetes 入门实战03 中级篇_多节点_46

20 Service:微服务架构的应对之道

为了更好地支持微服务以及服务网格这样的应用架构,Kubernetes 又专门定义了一个新的对象:Service,它是集群内部的负载均衡机制,用来解决服务发现的关键问题

为什么要有 Service

在 Kubernetes 集群里 Pod 的生命周期是比较“短暂”的,虽然 Deployment 和 DaemonSet 可以维持 Pod 总体数量的稳定,但在运行过程中,难免会有 Pod 销毁又重建,这就会导致 Pod 集合处于动态的变化之中。

这种“动态稳定”对于现在流行的微服务架构来说是非常致命的,试想一下,后台 Pod 的 IP 地址老是变来变去,客户端该怎么访问呢?如果不处理好这个问题,Deployment 和 DaemonSet 把 Pod 管理得再完善也是没有价值的。

但 LVS、Nginx 毕竟不是云原生技术,所以 Kubernetes 就按照这个思路,定义了新的 API 对象:Service

所以估计你也能想到,Service 的工作原理和 LVS、Nginx 差不多,Kubernetes 会给它分配一个静态 IP 地址,然后它再去自动管理、维护后面动态变化的 Pod 集合,当客户端访问 Service,它就根据某种策略,把流量转发给后面的某个 Pod

前面我们了解到 Deployment 只是保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题。一个Pod只是一个运行服务的实例,随时可能在一个节点上停止,在另一个节点以一个新的IP启动一个新的Pod,因此不能以确定的IP和端口号提供服务。

要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的后端服务实例。在K8S集群中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务

在K8S集群中,微服务的负载均衡是由kube-proxy实现的。kube-proxy是k8s集群内部的负载均衡器。它是一个分布式代理服务器,在K8S的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的kube-proxy就越多,高可用节点也随之增多。与之相比,我们平时在服务器端使用反向代理作负载均衡,还要进一步解决反向代理的高可用问题。

Kubernetes 入门实战03 中级篇_多节点_47

Kubernetes 入门实战03 中级篇_多节点_48

Kubernetes 入门实战03 中级篇_多节点_49

Kubernetes 入门实战03 中级篇_多节点_50

Kubernetes 入门实战03 中级篇_多节点_51

Service 概述

service 是一个固定接入层,客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端 pod,这个 service 工作依赖于在 kubernetes 集群之上部署的一个附件,就是 kubernetes 的 dns 服务 (不同 kubernetes 版本的 dns 默认使用的也是不一样的,1.11 之前的版本使用的是 kubeDNS,较新的版本使用的是 coredns),service 的名称解析是依赖于 dns 附件的,因此在部署完 k8s 之后需要再部署 dns 附件,kubernetes 要想给客户端提供网络功能(比如分配ip),需要依赖第三方的网络插件(flannel,calico 等)。每个 K8s 节点上都有一个组件叫做 kube-proxy,kube-proxy 这个组件将始终监视着 apiserver 中有关 service 资源的变动信息,需要跟 master 之上的 apiserver 交互,随时连接到 apiserver 上获取任何一个与 service 资源相关的资源变动状态,这种是通过 kubernetes 中固有的一种请求方法 watch(监视) 来实现的,一旦有 service 资源的内容发生变动(如创建,删除),然后操作会被存在etcd里,然后会把我们的请求调度到后端特定的 pod 资源之上的规则,这个规则可能是 iptables,也可能是 ipvs,取决于 service 的实现方式(可以自己配置规则)。比如创建了一个新的svc,svc会有一个ip,这个ip的网段是创建集群的时候配置的(默认是10),不过这个ipping不通,是只存在于防火墙规则里的虚拟ip。

Service 工作原理

k8s 在创建 Service 时,会根据标签选择器 selector(lable selector)来查找 Pod,据此创建与 Service 同名的 endpoint 对象,当 Pod 地址发生变化时,endpoint 也会随之发生变化,service 接收前端 client 请求的时候,就会通过 endpoint,找到转发到哪个 Pod 进行访问的地址。(至于转发到哪个节点的 Pod,由负载均衡 kube-proxy 决定)


Kubernetes 入门实战03 中级篇_多节点_52


你可以看到,这里 Service 使用了 iptables 技术,每个节点上的 kube-proxy 组件自动维护 iptables 规则,客户不再关心 Pod 的具体地址,只要访问 Service 的固定 IP 地址,Service 就会根据 iptables 规则转发请求给它管理的多个 Pod,是典型的负载均衡架构。

不过 Service 并不是只能使用 iptables 来实现负载均衡,它还有另外两种实现技术:性能更差的 userspace 和性能更好的 ipvs


如何使用 YAML 描述 Service

这里 Kubernetes 又表现出了行为上的不一致。虽然它可以自动创建 YAML 样板,但不是用命令kubectl create,而是另外一个命令kubectl expose,也许 Kubernetes 认为“expose”能够更好地表达 Service“暴露”服务地址的意思吧。

export out="--dry-run=client -o yaml"
kubectl expose deploy ngx-dep --port=80 --target-port=80 $out

生成的yaml文件如下

apiVersion: v1
kind: Service
metadata:
name: ngx-svc

spec:
selector:
app: ngx-dep

ports:
- port: 80
targetPort: 80
protocol: TCP

Kubernetes 入门实战03 中级篇_多节点_53

如何在 Kubernetes 里使用 Service

首先,我们创建一个 ConfigMap,定义一个 Nginx 的配置片段,它会输出服务器的地址、主机名、请求的 URI 等基本信息:


apiVersion: v1
kind: ConfigMap
metadata:
name: ngx-conf

data:
default.conf: |
server {
listen 80;
location / {
default_type text/plain;
return 200
'srv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\ndate: $time_iso8601\n';
}
}

然后我们在 Deployment 的“template.volumes”里定义存储卷,再用“volumeMounts”把配置文件加载进 Nginx 容器里

apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-dep

spec:
replicas: 2
selector:
matchLabels:
app: ngx-dep

template:
metadata:
labels:
app: ngx-dep
spec:
volumes:
- name: ngx-conf-vol
configMap:
name: ngx-conf

containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80

volumeMounts:
- mountPath: /etc/nginx/conf.d
name: ngx-conf-vol

Kubernetes 入门实战03 中级篇_多节点_54

Kubernetes 入门实战03 中级篇_多节点_55


Kubernetes 入门实战03 中级篇_多节点_56


Kubernetes 入门实战03 中级篇_多节点_57


在 Pod 里,用 curl 访问 Service 的 IP 地址,就会看到它把数据转发给后端的 Pod,输出信息会显示具体是哪个 Pod 响应了请求,就表明 Service 确实完成了对 Pod 的负载均衡任务。

Kubernetes 入门实战03 中级篇_多节点_58

如何以域名的方式使用 Service

Kubernetes 入门实战03 中级篇_多节点_59


Kubernetes 入门实战03 中级篇_多节点_60


Kubernetes 入门实战03 中级篇_多节点_61


可以看到,现在我们就不再关心 Service 对象的 IP 地址,只需要知道它的名字,就可以用 DNS 的方式去访问后端服务。


如何让 Service 对外暴露服务

由于 Service 是一种负载均衡技术,所以它不仅能够管理 Kubernetes 集群内部的服务,还能够担当向集群外部暴露服务的重任。

Service 对象有一个关键字段“type”,表示 Service 是哪种类型的负载均衡。前面我们看到的用法都是对集群内部 Pod 的负载均衡,所以这个字段的值就是默认的“ClusterIP”,Service 的静态 IP 地址只能在集群内访问。

除了“ClusterIP”,Service 还支持其他三种类型,分别是“ExternalName”“LoadBalancer”“NodePort”。不过前两种类型一般由云服务商提供,我们的实验环境用不到,所以接下来就重点看“NodePort”这个类型。

如果我们在使用命令 kubectl expose 的时候加上参数 --type=NodePort,或者在 YAML 里添加字段 type:NodePort,那么 Service 除了会对后端的 Pod 做负载均衡之外,还会在集群里的每个节点上创建一个独立的端口,用这个端口对外提供服务,这也正是“NodePort”这个名字的由来。

Kubernetes 入门实战03 中级篇_多节点_62

因为这个端口号属于节点,外部能够直接访问,所以现在我们就可以不用登录集群节点或者进入 Pod 内部,直接在集群外使用任意一个节点的 IP 地址,就能够访问 Service 和它代理的后端服务了。

Kubernetes 入门实战03 中级篇_多节点_63


Kubernetes 入门实战03 中级篇_多节点_64

kube-proxy 组件介绍

Kubernetes service 只是把应用对外提供服务的方式做了抽象,真正的应用跑在 Pod 中的 container 里,我们的请求转到 kubernetes nodes 对应的 nodePort 上,那么 nodePort 上的请求是如何进一步转到提供后台服务的 Pod 的呢? 就是通过 kube-proxy 实现的


kube-proxy 部署在 k8s 的每一个 Node 节点上,是 Kubernetes 的核心组件,我们创建一个 service 的时候,kube-proxy 会在 iptables 中追加一些规则,为我们实现路由与负载均衡的功能。在 k8s1.8 之前,kube-proxy 默认使用的是 iptables 模式,通过各个 node 节点上的 iptables 规则来实现 service 的负载均衡,但是随着 service 数量的增大,iptables 模式由于线性查找匹配、全量更新等特点,其性能 会显著下降。从 k8s 的 1.8 版本开始,kube-proxy 引入了 IPVS 模式,IPVS 模式与 iptables 同样基于 Netfilter,但是采用的 hash 表,因此当 service 数量达到一定规模时,hash 查表的速度优势就会显现出来,从而提高 service 的服务性能。


service 是一组 pod 的服务抽象,相当于一组 pod 的 LB,负责将请求分发给对应的 pod。service 会为这个 LB 提供一个 IP,一般称为 cluster IP。kube-proxy 的作用主要是负责 service 的实现,具体来说,就是实现了内部从 pod 到 service 和外部的从 node port 向 service 的访问。

1、kube-proxy 其实就是管理 service 的访问入口,包括集群内 Pod 到 Service 的访问和集群外访问 service。

2、kube-proxy 管理 sevice 的 Endpoints,该 service 对外暴露一个 Virtual IP,也可以称为是 Cluster IP, 集群内通过访问这个 Cluster IP:Port 就能访问到集群内对应的 serivce 下的 Pod。

Kubernetes 入门实战03 中级篇_多节点_65


Kubernetes 自 1.9-alpha 版本引入了 ipvs 代理模式,自 1.11 版本开始成为默认设置。客户端请求时到达内核空间时,根据 ipvs 的规则直接分发到各 pod 上。kube-proxy 会监视 Kubernetes Service 对象和 Endpoints,调用 netlink 接口以相应地创建 ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod。与 iptables 类似,ipvs 基于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能

此外,ipvs 为负载均衡算法提供了更多选项

Kubernetes 入门实战03 中级篇_多节点_66

如果某个服务后端 pod 发生变化,标签选择器适应的 pod 又多一个,适应的信息会立即反映到 apiserver 上,而 kube-proxy 一定可以 watch 到 etc 中的信息变化,而将它立即转为 ipvs 或者 iptables 中的规则,这一切都是动态和实时的,删除一个 pod 也是同样的原理

Kubernetes 入门实战03 中级篇_多节点_67


21 Ingress:集群进出流量的总管

Service 很有用,但也只能说是“基础设施”,它对网络流量的管理方案还是太简单,离复杂的现代应用架构需求还有很大的差距,所以 Kubernetes 就在 Service 之上又提出了一个新的概念:Ingress。

为什么要有 Ingress

Service 还有一个缺点,它比较适合代理集群内部的服务。如果想要把服务暴露到集群外部,就只能使用 NodePort 或者 LoadBalancer 这两种方式,而它们都缺乏足够的灵活性,难以管控,这就导致了一种很无奈的局面:我们的服务空有一身本领,却没有合适的机会走出去大展拳脚


Kubernetes 入门实战03 中级篇_多节点_68

为什么要有 Ingress Controller

Ingress 也只是一些 HTTP 路由规则的集合,相当于一份静态的描述文件,真正要把这些规则在集群里实施运行,还需要有另外一个东西,这就是 Ingress Controller,它的作用就相当于 Service 的 kube-proxy,能够读取、应用 Ingress 规则,处理、调度流量。

不过 Ingress Controller 要做的事情太多,与上层业务联系太密切,所以 Kubernetes 把 Ingress Controller 的实现交给了社区,任何人都可以开发 Ingress Controller,只要遵守 Ingress 规则就好。

根据 Docker Hub 上的统计,Nginx 公司的开发实现是下载量最多的 Ingress Controller,所以我将以它为例,讲解 Ingress 和 Ingress Controller 的用法

Kubernetes 入门实战03 中级篇_多节点_69

为什么要有 IngressClass

Kubernetes 入门实战03 中级篇_多节点_70

Kubernetes 入门实战03 中级篇_多节点_71

使用Ingress Class 进行分类

如何使用 YAML 描述 Ingress/Ingress Class

Kubernetes 入门实战03 中级篇_多节点_72


看下Ingress的yaml文件

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ngx-ing

spec:

ingressClassName: ngx-ink

rules:
- host: ngx.test
http:
paths:
- path: /
pathType: Exact
backend:
service:
name: ngx-svc
port:
number: 80

“rules”的格式比较复杂,嵌套层次很深。不过仔细点看就会发现它是把路由规则拆散了,有 host 和 http path,在 path 里又指定了路径的匹配方式,可以是精确匹配(Exact)或者是前缀匹配(Prefix),再用 backend 来指定转发的目标 Service 对象

Kubernetes 入门实战03 中级篇_多节点_73

如何在 Kubernetes 里使用 Ingress/Ingress Class

Kubernetes 入门实战03 中级篇_多节点_74

Kubernetes 入门实战03 中级篇_多节点_75

可以看到,Ingress 对象的路由规则 Host/Path 就是在 YAML 里设置的域名“ngx.test/”,而且已经关联了第 20 讲里创建的 Service 对象,还有 Service 后面的两个 Pod。


如何在 Kubernetes 里使用 Ingress Controller

Kubernetes 入门实战03 中级篇_多节点_76

Ingress Controller的yaml文件

apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-kic-dep
namespace: nginx-ingress

spec:
replicas: 1
selector:
matchLabels:
app: ngx-kic-dep

template:
metadata:
labels:
app: ngx-kic-dep
...
spec:
containers:
- image: nginx/nginx-ingress:2.2-alpine
...
args:
- -ingress-class=ngx-ink

Kubernetes 入门实战03 中级篇_多节点_77

Kubernetes 入门实战03 中级篇_多节点_78

还有最后一道工序,因为 Ingress Controller 本身也是一个 Pod,想要向外提供服务还是要依赖于 Service 对象。所以你至少还要再为它定义一个 Service,使用 NodePort 或者 LoadBalancer 暴露端口,才能真正把集群的内外流量打通

Kubernetes 入门实战03 中级篇_多节点_79

22 实战演练:玩转Kubernetes

kubeadm 使用容器技术封装了 Kubernetes 组件,所以只要节点上安装了容器运行时(Docker、containerd 等),它就可以自动从网上拉取镜像,然后以容器的方式运行组件,非常简单方便。

Kubernetes 入门实战03 中级篇_多节点_80

WordPress 网站基本架构

Kubernetes 入门实战03 中级篇_多节点_81

重点是我们已经完全舍弃了 Docker,把所有的应用都放在 Kubernetes 集群里运行,部署方式也不再是裸 Pod,而是使用 Deployment,稳定性大幅度提升。

原来的 Nginx 的作用是反向代理,那么在 Kubernetes 里它就升级成了具有相同功能的 Ingress Controller。WordPress 原来只有一个实例,现在变成了两个实例(你也可以任意横向扩容),可用性也就因此提高了不少。而 MariaDB 数据库因为要保证数据的一致性,暂时还是一个实例。

还有,因为 Kubernetes 内置了服务发现机制 Service,我们再也不需要去手动查看 Pod 的 IP 地址了,只要为它们定义 Service 对象,然后使用域名就可以访问 MariaDB、WordPress 这些服务。

1. WordPress 网站部署 MariaDB

apiVersion: v1
kind: ConfigMap
metadata:
name: maria-cm

data:
DATABASE: 'db'
USER: 'wp'
PASSWORD: '123'
ROOT_PASSWORD: '123'

把 MariaDB 由 Pod 改成 Deployment 的方式,replicas 设置成 1 个,template 里面的 Pod 部分没有任何变化,还是要用 envFrom把配置信息以环境变量的形式注入 Pod,相当于把 Pod 套了一个 Deployment 的“外壳”

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: maria-dep
name: maria-dep

spec:
replicas: 1
selector:
matchLabels:
app: maria-dep

template:
metadata:
labels:
app: maria-dep
spec:
containers:
- image: mariadb:10
name: mariadb
ports:
- containerPort: 3306

envFrom:
- prefix: 'MARIADB_'
configMapRef:
name: maria-cm

再为 MariaDB 定义一个 Service 对象,映射端口 3306,让其他应用不再关心 IP 地址,直接用 Service 对象的名字来访问数据库服务

apiVersion: v1
kind: Service
metadata:
labels:
app: maria-dep
name: maria-svc

spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: maria-dep

Kubernetes 入门实战03 中级篇_多节点_82

2. WordPress 网站部署 WordPress

因为刚才创建了 MariaDB 的 Service,所以在写 ConfigMap 配置的时候“HOST”就不应该是 IP 地址了,而应该是 DNS 域名,也就是 Service 的名字maria-svc,这点需要特别注意:

apiVersion: v1
kind: ConfigMap
metadata:
name: wp-cm

data:
HOST: 'maria-svc'
USER: 'wp'
PASSWORD: '123'
NAME: 'db'

WordPress 的 Deployment 写法和 MariaDB 也是一样的,给 Pod 套一个 Deployment 的“外壳”,replicas 设置成 2 个,用字段“envFrom”配置环境变量

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: wp-dep
name: wp-dep

spec:
replicas: 2
selector:
matchLabels:
app: wp-dep

template:
metadata:
labels:
app: wp-dep
spec:
containers:
- image: wordpress:5
name: wordpress
ports:
- containerPort: 80

envFrom:
- prefix: 'WORDPRESS_DB_'
configMapRef:
name: wp-cm

然后我们仍然要为 WordPress 创建 Service 对象,这里我使用了“NodePort”类型,并且手工指定了端口号“30088”(必须在 30000~32767 之间):

apiVersion: v1
kind: Service
metadata:
labels:
app: wp-dep
name: wp-svc

spec:
ports:
- name: http80
port: 80
protocol: TCP
targetPort: 80
nodePort: 30088

selector:
app: wp-dep
type: NodePort

Kubernetes 入门实战03 中级篇_多节点_83

Kubernetes 入门实战03 中级篇_多节点_84


3.WordPress 网站部署 Nginx Ingress Controller

首先我们需要定义 Ingress Class,名字就叫“wp-ink”,非常简单

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: wp-ink

spec:
controller: nginx.org/ingress-controller

然后用 kubectl create 命令生成 Ingress 的样板文件,指定域名是“wp.test”,后端 Service 是“wp-svc:80”,Ingress Class 就是刚定义的“wp-ink”:

kubectl create ing wp-ing --rule="wp.test/=wp-svc:80" --class=wp-ink $out

得到的 Ingress YAML

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wp-ing

spec:
ingressClassName: wp-ink

rules:
- host: wp.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wp-svc
port:
number: 80

接下来就是最关键的 Ingress Controller 对象了,它仍然需要从 Nginx 项目的示例 YAML 修改而来,要改动名字、标签,还有参数里的 Ingress Class


这个 Ingress Controller 不使用 Service,而是给它的 Pod 加上一个特殊字段 hostNetwork,让 Pod 能够使用宿主机的网络,相当于另一种形式的 NodePort:

apiVersion: apps/v1
kind: Deployment
metadata:
name: wp-kic-dep
namespace: nginx-ingress

spec:
replicas: 1
selector:
matchLabels:
app: wp-kic-dep

template:
metadata:
labels:
app: wp-kic-dep

spec:
serviceAccountName: nginx-ingress

# use host network
hostNetwork: true

containers:
...

Kubernetes 入门实战03 中级篇_多节点_85

Kubernetes 入门实战03 中级篇_多节点_86

Kubernetes 入门实战03 中级篇_多节点_87