hello云胜

技术与生活

0%

Ingress,ingressClass,ingressController

刚开始接触到k8s的ingress很容易被这几个名词搞糊涂,因为很像啊。

首先,ingress做的事情是作为流量的总入口。

为什么要有ingress?

因为service的功能不够用了。使用service对外暴露服务,使用nodeport方式对端口的消耗太大。

而且service是四层的负载均衡。在tcp/ip协议栈上转发流量,可操作的空间太小。

ingress是七层负载均衡,可以根据http协议,设置更灵活路由。

ingress

ingress就是一些路由规则配置。只是描述文件,不是一个要实际干活、处理流量的应用程序。

比如,取一个平台上的ingress配置

1
kubectl get ingress rstar-cloud-v3 -n butterfly-test -oyaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubesphere.io/creator: "9364565475"
creationTimestamp: "2022-09-02T06:03:46Z"
generation: 1
name: rstar-cloud-v3
namespace: butterfly-test
resourceVersion: "8512706"
uid: 8c19873b-c286-47bd-9cda-44d5256e251c
spec:
ingressClassName: nginx
rules:
- host: rstar.test.rrswl.com
http:
paths:
- backend:
service:
name: rstar-web-admin
port:
number: 8081
path: /api
pathType: ImplementationSpecific

看spec里有一个ingressClassName,这个是下面要说到的ingressClass

剩下的就是路由规则rules了

ingressController

上面说到ingress只是一个路由描述文件,不是一个要实际干活、处理流量的应用程序。

实际干活的是这个ingressController

按理来说Kubernetes 应该把 Ingress Controller 内置实现,但是因为Ingress Controller 要做的事情太多,与上层业务联系太密切,所以 Kubernetes 把 Ingress Controller 的实现交给了社区,任何人都可以开发 Ingress Controller,只要遵守 Ingress 规则就好。

这就造成了 Ingress Controller“百花齐放”的盛况。

从ingress的功能上我们可以看出ingress的功能和nginx是高度重合的。所以基于nginx的实现是目前k8s里使用最广泛的ingresscontroller。

而因为nginx是开源的,所以又有很多的变种。

比如社区的 Kubernetes Ingress Controller(https://github.com/kubernetes/ingress-nginx)、Nginx 公司自己的 Nginx Ingress Controller(https://github.com/nginxinc/kubernetes-ingress)、还有基于 OpenResty 的 Kong Ingress Controller(https://github.com/Kong/kubernetes-ingress-controller)等等。

根据 Docker Hub 上的统计,Nginx 公司的开发实现是下载量最多的 Ingress Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# kubectl get deploy nginx-ingress-nginx-ingress -oyaml
apiVersion: apps/v1
kind: Deployment
...
spec:
...
template:
metadata:
annotations:
prometheus.io/port: "9113"
prometheus.io/scheme: http
prometheus.io/scrape: "true"
creationTimestamp: null
labels:
app: nginx-ingress-nginx-ingress
spec:
containers:
- args:
- -ingress-class=nginx

看ingresscontroller的yaml文件里有个参数ingress-class,就是指向我们创建的某个ingress-class

ingressClass

综上所述,看起来用ingressController处理路由,ingress做路由配置,整条链路已经通了。但是随着Ingress 在实践中的大量应用,发现了一个新的问题。

  • 集群里有不同的租户,他们对 Ingress 的需求差异很大甚至有冲突,无法部署在同一个 Ingress Controller 上。
  • Ingress 规则太多,都交给一个 Ingress Controller 处理会让它不堪重负;
  • 多个 Ingress 对象没有很好的逻辑分组方式,管理和维护成本很高;

所以,k8s又提出了一个 Ingress Class 的概念。让它插在 Ingress 和 Ingress Controller 中间,作为流量规则和控制器的协调人,解除了 Ingress 和 Ingress Controller 的强绑定关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# kubectl get ingressClass nginx -oyaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
meta.helm.sh/release-name: nginx-ingress
meta.helm.sh/release-namespace: default
creationTimestamp: "2022-08-26T03:40:44Z"
generation: 1
labels:
app.kubernetes.io/managed-by: Helm
name: nginx
resourceVersion: "5250353"
uid: 60afbf7d-84a9-425e-bd37-7233e0cbee7a
spec:
controller: nginx.org/ingress-controller

Ingress Class 本身并没有什么实际的功能,只是起到联系 Ingress 和 Ingress Controller 的作用,所以它的定义非常简单,在“spec”里只有一个必需的字段“controller”,表示要使用哪个 Ingress Controller,具体的名字就要看实现文档了。

比如,如果我要用 Nginx 开发的 Ingress Controller,那么就要用名字“nginx.org/ingress-controller”:

Helm

是什么?

helm是k8s的包管理工具。可以方便的将之前打包好的yaml文件部署到k8s上。

之前部署一个应用的过程:编写yaml文件 –> 创建service对外暴露 –> 通过ingress域名访问

这种方式的缺点:只适合简单的应用。实际上我们部署微服务项目,可能有几十个服务,用这种方式就不合适了。需要维护大量的yaml文件。

使用helm的优势:

  1. 使用helm可以将大量yaml文件作为一个整体进行管理
  2. 实现yaml文件的高效复用。
  3. 实现应用级别的版本管理

总之,helm的作用就是让我们在k8s中部署应用更高效。

helm的核心概念:

  1. helm

    是一个命令行客户端工具。用于chart的创建、打包、发布。

  2. chart

    应用描述,实际上就是把yaml文件的打包。

  3. release

    基于chart部署的实体。就是应用的一个版本。

image-20210517142131666

helm安装

  1. 从helm官网:https://helm.sh/,下载v3版本的安装包

    wget https://get.helm.sh/helm-v3.6.0-linux-amd64.tar.gz

  2. 放到服务器上,解压。将helm目录移动到/usr/bin目录下。

    1
    2
    tar -zxvf helm-v3.6.0-linux-amd64.tar.gz
    mv linux-amd64/helm /usr/bin/
  3. 配置helm仓库

    添加仓库:helm repo add 仓库名称 仓库地址

    1
    helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

    添加阿里云的仓库

    1
    2
    helm repo list  #查看仓库
    helm repo remove aliyun #删除仓库

还有一个常用的仓库:

helm repo add bitnami https://charts.bitnami.com/bitnami

  1. 更新仓库

    1
    helm repo update

常用Helm命令

  1. 快速搜索应用

    helm search repo 名字

  2. 列出所有版本

    1
    helm search repo redis -l
  3. 下载指定版本

    1
    helm pull bitnami/redis-cluster --version 8.2.5

    要下载最新版本就不用加–version

  4. 根据搜索结果进行安装

    helm install 安装之后的名称 搜索到的应用名称

    1
    helm install xx名字 .

    自定义变量

    1
    helm install xx名字 . -f values.yaml
  5. 查看安装状态

    1
    helm list -n redis

    helm status 安装之后的名称

  6. 删除

    1
    helm uninstall my-redis-cluster -n redis
使用helm部署redis
1
2
# kubectl create ns redis
# helm install my-redis-cluster bitnami/redis-cluster -f values.yaml -n redis

自己创建chart,完成部署

  1. 使用命令创建chart

    1
    helm create 自定义chart的名字

    目录作用:

    • charts:
    • Chart.yaml: 配置chart的基本信息,版本等。
    • templates:自己写的yaml文件放在这
    • valus.yaml: 定义全局变量
  2. 在template中创建自己的yaml文件

  3. 安装自己的chart

    1
    helm install 应用名字  自己的chart名字
  4. 更新自己的chart

    1
    helm upgrade 应用名字  自己的chart名字

使用helm进行yaml文件复用

通过传递参数,动态渲染yaml模板。做到yaml的高效复用。

  1. 在values.yaml中定义变量和值

  2. 在具体的yaml文件中,获取定义的变量值

    表达式:{{.Values.变量名称 }}

  3. 执行

    1
    helm install 应用名字  自己的chart名字

查看envoy的日志

  1. 确认envoy的日志是打开的

​ istio的一个configmap里

image-20240612112024597

image-20240612111550758

image-20240611135624310

image-20240611150227225

可用于跨k8s集群的服务网格

image-20240611151638806

image-20240611153332737

image-20240611154737714

相当于把k8s的ingress配置拆成了2个

image-20240611160907257

第三个不推荐,相当于不适用istio的控制平面了

image-20240611161056342

istio里的ingress是个loadbalancer,而egress本质上就是个普通的service,多开放了几个端口而已,它并不是必须的,就是为了收敛请求。你可以理解为它就是把网格内的流量都收敛到这个服务,然后由它转发出去,它扮演了一个逻辑上的网关角色,并无特殊之处。

使用egress的同时还必须使用service entry,中间调了二次;如果直接使用service entry反而更省事,那egress的优势是什么呢?把出流量收敛到一处,统一管理,或者安全等方面的需求。

可配置超时策略

可配置重试策略

image-20240614092309800

同时配置的情况,取二者的小

可配置熔断策略

image-20240614092349941

image-20240611165740181

流量镜像功能

image-20240612095735229

小结

image-20240612095750122

Istio实战

Istio的两次架构变更

image-20210919170331754

Mixer组件的问题:

  1. 性能。mixer组件是在进程外
  2. 耦合。修改mixer组件后要重启

image-20210919170624207

架构优美,职责清晰,边界分明

插件拆出—>适配器—>解决耦合问题—>但是性能更差

image-20210919171128290

MVP理论,《精益创业》。可用产品,持续迭代。

istio架构太复杂,对用户体验不好。

image-20210919171431663

image-20210919171535661

Istio核心功能

流量控制

主要功能

  1. 路由,流量转移
  2. 流量进出
  3. 网络弹性能力
  4. 测试相关。故障注入,流量镜像。

核心资源(CRD)

  1. 虚拟服务。一组路由规则
  2. 目标规则。对应虚拟服务路由目标的真实地址。负载均衡策略。
  3. 网关。存在于整个网格的边界,管理进出网格的流量。
  4. 服务入口。把外部的服务注册到网格内。不同的服务部署在不同的集群中。可以用服务入口统一管理。
  5. Sidecar。

可观察性

  • 可观察性 ≠ 监控。
  • 研发角度 vs 运维角度。主动 vs 被动。
  • 指标、日志、追踪

安全

安全架构

image-20210919174550532

认证

认证策略通过创建crd实现

授权

授权策略也是通过创建crd实现

安装和部署

准备:

一个kubernetes环境

下载安装gotrman

mattn/goreman: foreman clone written in go language (github.com)

1
go install github.com/mattn/goreman@latest

下载etcd的二进制文件,我用的是3.5.0版本

Release v3.5.0 · etcd-io/etcd (github.com)

把etcd的二进制文件,加入到全局path /usr/local/bin

1
2
ln -s /root/etcd/etcd-v3.5.0-linux-amd64/etcdctl  /usr/local/bin/etcdctl
ln -s /root/etcd/etcd-v3.5.0-linux-amd64/etcd /usr/local/bin/etcd

(也可以mv过去)

创建一个文件Procfile

来源 etcd/Procfile at v3.5.0 · etcd-io/etcd (github.com)

修改了bin/etcd 为 etcd

1
2
3
4
5
# Use goreman to run `go get github.com/mattn/goreman`
etcd1: etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd2: etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
etcd3: etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger=zap --log-outputs=stderr
#proxy: bin/etcd grpc-proxy start --endpoints=127.0.0.1:2379,127.0.0.1:22379,127.0.0.1:32379 --listen-addr=127.0.0.1:23790 --advertise-client-url=127.0.0.1:23790 --enable-pprof

使用goreman快速启动三个etcd节点

1
goreman -f Procfile start

image-20240819155916601

测试

image-20240819162053976

部署Skywalking

部署elasticsearch

Skywalking官方建议存储使用es,所以先部署es。

注意,在目前官方说明中,skywalking分别有es6和es7两个版本

我们这里安装es6版本。

1
docker pull docker.elastic.co/elasticsearch/elasticsearch:6.8.13

测试环境下,先启动单节点实例

1
docker run --name elasticsearch -p 9200:9200 -p 9300:9300  -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d docker.elastic.co/elasticsearch/elasticsearch:6.8.13

-e “discovery.type=single-node” 设置为单节点
特别注意:
-e ES_JAVA_OPTS=”-Xms512m -Xmx512m” 测试环境下,设置ES的初始内存和最大内存,否则导致过大启动不了ES

部署skywalking

拉取镜像

1
2
3
4
5
docker pull apache/skywalking-base:8.3.0-es6

docker pull apache/skywalking-oap-server:8.3.0-es6

docker pull apache/skywalking-ui:8.3.0

启动 SkyWalking Server :

1
docker run --name skywalking-oap --restart always -p 1234:1234 -p 11800:11800 -p 12800:12800 -d --link elasticsearch:elasticsearch -e SW_STORAGE=elasticsearch -e SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200 apache/skywalking-oap-server:8.3.0-es6

默认端口就是12800

启动 UI :

1
docker run --name skywalking-ui --restart always -p 9898:8080 --link skywalking-oap:skywalking-oap -d -e SW_OAP_ADDRESS=skywalking-oap:12800 apache/skywalking-ui:8.3.0

访问9898端口

image-20210115094730287

安装成功

使用

以java使用为例。

1,拿到agent包

去官网下载skywalking的完整tar包

解压,把里面的agent文件夹整个传到被监控应用所在的服务器上

2,配置被监控应用的启动参数

1
-javaagent:/path/to/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=my-App-name -Dskywalking.collector.backend_service=localhost:11800

etcdserver: mvcc: database space exceeded

它是指当前 etcd db 文件大小超过了配额,当出现此错误后,你的整个集群将不可写入,只读,对业务的影响非常大。

原因:

当 etcd server 收到 put/txn 等写请求的时候,会首先检查下当前 etcd db 大小加上你请求的 key-value 大小之和是否超过了配额(quota-backend-bytes)。

一方面默认 db 配额仅为 2G,当你的业务数据、写入 QPS、Kubernetes 集群规模增大后,你的 etcd db 大小就可能会超过 2G。

另一方面我们知道 etcd v3 是个 MVCC 数据库,保存了 key 的历史版本,当你未配置压缩策略的时候,随着数据不断写入,db 大小会不断增大,导致超限。

最后你要特别注意的是,如果你使用的是 etcd 3.2.10 之前的旧版本,请注意备份可能会触发 boltdb 的一个 Bug,它会导致 db 大小不断上涨,最终达到配额限制。

解决:

首先当然是调大配额。具体多大合适呢?etcd 社区建议不超过 8G

但是,当你把配额(quota-backend-bytes)调大后,集群依然拒绝写入

原因是

当发现超过了配额时,etcd会产生一个告警(Alarm)请求,告警类型是 NO SPACE,并通过 Raft 日志同步给其它节点,告知 db 无空间了,并将告警持久化存储到 db 中

etcd的Apply 模块在执行每个命令的时候,都会去检查当前是否存在 NO SPACE 告警,如果有则拒绝写入。所以还需要你额外发送一个取消告警(etcdctl alarm disarm)的命令,以消除所有告警。

我们还需要检查 etcd 的压缩(compact)配置是否开启、配置是否合理。etcd 保存了一个 key 所有变更历史版本,如果没有一个机制去回收旧的版本,那么内存和 db 大小就会一直膨胀,在 etcd 里面,压缩模块负责回收旧版本的工作。

压缩模块支持按多种方式回收旧版本,比如保留最近一段时间内的历史版本。不过你要注意,它仅仅是将旧版本占用的空间打个空闲(Free)标记,后续新的数据写入的时候可复用这块空间,而无需申请新的空间。如果你需要回收空间,减少 db 大小,得使用碎片整理(defrag), 它会遍历旧的 db 文件数据,写入到一个新的 db 文件。但是它对服务性能有较大影响,不建议你在生产集群频繁使用。

最后你需要注意配额(quota-backend-bytes)的行为,默认’0’就是使用 etcd 默认的 2GB 大小,你需要根据你的业务场景适当调优。如果你填的是个小于 0 的数,就会禁用配额功能,这可能会让你的 db 大小处于失控,导致性能下降,不建议你禁用配额。