Apache APISIX 是 Apache 软件基金会下的云原生 API 网关,它兼具动态、实时、高性能等特点,提供了负载均衡、动态上游、灰度发布(金丝雀发布)、服务熔断、身份认证、可观测性等丰富的流量管理功能。很多场景下,我们可以选择在APISIX中使用CORS(跨源资源共享)来解决跨域问题。

APISIX提供了CRD和Annotations模式来启用CORS。本文主要介绍如何在APISIX Ingress Annotations模式下启用CORS。

环境:

应用 信息
Kubernetes v1.25.7
apisix apache/apisix:3.4.0-debian
apisix-ingress-controller apache/apisix-ingress-controller:1.6.0

IngressAnnotations模式

IngressAnnotations模式,可以通过在Ingress资源上添加注释来配置APISIX的行为。比如添加一个CORS注释来指示APISIX启用CORS:

annotations
    k8s.apisix.apache.org/enable-cors: "true"
    kubernetes.io/ingress.class: apisix

接着,我们以给一个Http服务my-service, 创建了一个Ingress资源的例子来演示, Ingress详细如下:

创建Ingress

创建my-service的ingress资源:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: match-ingress
  namespace: my-ns
  annotations:
    k8s.apisix.apache.org/enable-cors: "true"
    kubernetes.io/ingress.class: apisix
spec:
  rules:
  - http:
      paths:
      - backend:
          service:
            name: my-service
            port:
              number: 8000
        path: /v2
        pathType: Prefix

在这个示例中,我们添加了【k8s.apisix.apache.org/enable-cors: “true”】的注释, 即启用cors功能,默认允许来自任何源的请求访问my-service的API。

Dashboard启用CORS

打开APISIX Dashboard界面:在【插件】中查找“CORS” 。单击该插件,然后单击“启用”。

然后,可以在路由点击查看my-service的对应路由,如下:

{
  "uris": [
    "/v2/",
    "/v2/*"
  ],
  "name": "ing_my-service-ingress_cfc81dbe",
  "desc": "Created by apisix-ingress-controller, DO NOT modify it manually",
  "plugins": {
    "cors": {
      "allow_credential": false,
      "allow_headers": "*",
      "allow_methods": "*",
      "allow_origins": "*",
      "expose_headers": "*",
      "max_age": 5
    }
  },
  "upstream_id": "7c630009",
  "labels": {
    "managed-by": "apisix-ingress-controller"
  },
  "status": 1
}

plugins.cors为跨域的对应规则配置,说明插件启用成功。

测试

然后我们向my-service的发起一个测试请求:

curl -i -k 'http://[my-cluster-ip:nodeport]/v2/my-service/api/geeker'   -X OPTIONS \
   -H "Access-Control-Allow-Origin: *" \
   -H "Access-Control-Request-Method: POST" \
   -H "Access-Control-Request-Headers: content-type" \
   -H "Origin: http://localhost/" 
   

输出:

HTTP/1.1 200 OK
Date: Sat, 15 Jul 2023 13:53:57 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.4.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Max-Age: 5
Access-Control-Expose-Headers: *
Access-Control-Allow-Headers: *

如果返回结果中出现 CORS 相关的 header( ccess-Control-Allow-Origin: * < Access-Control-Allow-Methods: * < Access-Control-Allow-Headers: * < Access-Control-Expose-Headers: * < Access-Control-Max-Age: 5 ),则代表插件生效、跨域成功。

遇到的问题

在增加好ingress后测试API,发现没有CORS 相关的 header

,跨域失败

HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Origin: *
Date: Sat, 15 Jul 2023 13:46:16 GMT
Server: APISIX/3.4.0

经检查,是ApiSix的config配置有误,在配置的插件里,开启了其他插件,没有包括cors,导致插件不生效。去掉后,重启插件配置生效、跨域成功。

plugins:
  # the plugins you enabled
  - log-rotate
  - proxy-rewrite

参考

背景:

sealos安装的Kubernetes集群,删除后增加节点失败。

Error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.Master --domain sealos.hub on 10.0.0.Node:22, output: , error: Process exited with status 139 from signal SEGV,

环境:

应用 信息
os Ubuntu
sealos v4.2.2
Kubernetes v1.25.7

问题1:文件时md5校验失败

操作步骤:

sealos delete --nodes 10.0.0.X 
sealos add --nodes 10.0.0.X 

执行报错:

sha256 sum not match

Sealos 的 –debug 参数是一个全局参数,用于开启调试模式,以便在出现问题时能更详细地了解系统的运行情况。

sealos add --nodes 10.0.0.X  --debug

Sealos传输文件时比较md5查看是否报错,issues给了一个解决办法:可以关闭校验。

#  export SEALOS_SCP_CHECKSUM=false

修改完,出现了另外一个问题:

问题2:sealctl 增加host失败

Error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.Master --domain sealos.hub on 10.0.0.Node:22, output: , error: Process exited with status 139 from signal SEGV,

 debug模式查看详细

2023-06-28T09:56:43 debug show registry info, IP: 10.0.0.X:22, Domain: sealos.hub, Data: /var/lib/registry2023-06-28T09:56:43 debug start to exec /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.X --domain sealos.hub on 10.0.0.M:222023-06-28T09:56:44 error Applied to cluster error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.X --domain sealos.hub on 10.0.0.M:22, output: , error: Process exited with status 139 from signal SEGV,2023-06-28T09:56:44 debug save objects into local: /root/.sealos/default/Clusterfile, objects: [apiVersion: apps.sealos.io/v1beta1kind: Cluster

可见,是seactl执行"sealctl hosts add –ip 10.0.0.X –domain sealos.hub" 失败。此文件是从master拷贝过来的:

/var/lib/containers/storage/overlay/5dd64dde7bc046cfd7554458e2950c41c0d86536e4e96532cfa2ee60685404d4/merged/opt to dst /var/lib/sealos/data/default/rootfs/opt2023-06-28T09:56:40 debug remote copy files src /var/lib/containers/storage/overlay/44ce75c1b3e483c61ecfbc07a72f87da8627ce40d9df9451f2d04dfad8dffc65/merged/opt to dst /var/lib/sealos/data/default/rootfs/opt2023-06-28T09:56:42 debug remote copy files src

我们试着手动拷贝过去,执行是正常的。初步判断是传输的文件出错 ,issure沟通,建议试着“scp进程可能有问题,比较md5查看是否有问题。export SEALOS_SCP_CHECKSUM = true”,还是出现 “sha256 sum not match”。无效。

解决

这个v4.2.2 的一个bug,希望下个版本能修复。最终一个临时处理办法:重置集群

# sealos reset 

我尝试重置集群,再加入node是可行的。

小结

本文主要记录排查、解决【sealos-v4.2.2删除节点后再增加节点失败】的过程,原因sealos的版本v4.2.2 问题,,其一,旧版本在拷贝seactl文件到node时会抛出失败:sha256比较文件时,经排查判断是传输过程文件出错了。其二,可以临时通过重置集群来恢复。 希望对你有所帮助。

参考

Metrics Server是一个可扩展的、高效的容器资源度量源,用于Kubernetes内置的自动伸缩管道。

kubectl top 可以查看node、pod 的实时资源使用情况:如CPU、内存。但需要预装 Metrics Server ,不然会出现如下错误提示:

kubectl top pods
error: Metrics API not available

Metrics Server 从 Kubelet 收集资源指标,并通过Metrics API在 Kubernetes apiserver 中公开, 以供Horizo​​ntal Pod Autoscaler和Vertical Pod Autoscaler使用。指标 API 也可以通过 访问kubectl top,从而更容易调试自动缩放管道。

今天主要介绍安装的过程,以及记录安装过程遇到一些问题。

开始安装

这里我们采用官网给的清单 components.yaml安装最新的 Metrics Server 版本:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

查看是否安装成功:

# kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   0/1     1            0           118s

发现安装失败。

问题1:镜像拉取失败

查看pod详细信息:

# kubectl get pod  -n kube-system
NAME                              READY   STATUS         RESTARTS      AGE
metrics-server-55c774cdbb-jmfz8   0/1     ErrImagePull   0             2m34s

查看pod日志:

# kubectl logs pods/metrics-server-55c774cdbb-jmfz8 -n kube-system
Error from server (BadRequest): container "metrics-server" in pod "metrics-server-55c774cdbb-jmfz8" is waiting to start: trying and failing to pull image

从日志可知是镜像拉取不下来,我们可以修改镜像地址。可以从dockerhub,查找最新版本,如作者安装的版本是:metrics-server:v0.6.1。打开components.yaml , 修改Deployment,spec.containers.coimage 为搜索到的,如“registry.aliyuncs.com/google_containers/metrics-server:v0.6.1",修改完详细如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        image: registry.aliyuncs.com/google_containers/metrics-server:v0.6.1

更新部署deployment:

# kubectl delete  deployment metrics-server -n kube-system
deployment.apps "metrics-server" deleted

# kubectl apply -f components.yaml

问题2:证书

更新后,发现另外一个问题:

# server.go:132] unable to fully scrape metrics: unable to fully scrape metrics from node docker-desktop: unable to fetch metrics from node docker-desktop: Get "https://192.168.1.88:10250/stats/summary?only_cpu_and_memory=true": x509: cannot validate certificate for 192.168.1.88 because it doesn't contain any IP SANs

从日志可知是权限验证(证书)出了问题,通过搜索github issues ,issue回复里提供了一个解决方法是在可以关闭证书验证,详细如下:

修改清单components.yam, 增加:

- --kubelet-insecure-tls

修改完 Deplyment详细如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --kubelet-insecure-tls
        - --metric-resolution=15s

其中:

–kubelet-preferred-address-types: 优先使用 InternalIP 来访问 kubelet,这样可以避免节点名称没有 DNS 解析记录时,通过节点名称调用节点 kubelet API 失败的情况(未配置时默认的情况);

–kubelet-insecure-tls: kubelet 的 10250 端口使用的是 https 协议,连接需要验证 tls 证书。–kubelet-insecure-tls 不验证客户端证书。

然后,检查是否启动成功:

#kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   1/1     1            1           14m

success。

参考

背景:

在导入外部集群到rancher时,cattle-cluster-agent报错

level=fatal msg="looking up cattle-system/cattle ca/token: no secret exists for service account cattle-system/cattle"

环境:

应用 信息
Kubernetes v1.25.7
Rancher v2.6.6

解决

经查,K8s在版本v1.24启用新测试功能特性-LegacyServiceAccountTokenNoAutoGeneration,默认是开启的。

服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的持有者令牌来验证请求。

服务账号通常由 API 服务器自动创建并通过 ServiceAccount 准入控制器关联到集群中运行的 Pod 上。 持有者令牌会挂载到 Pod 中可预知的位置,允许集群内进程与 API 服务器通信。 服务账号也可以使用 Pod 规约的 serviceAccountName 字段显式地关联到 Pod 上。

我们可以通过k8s 组件上指定的各种特性开关控制 - 【特性门控是描述 Kubernetes 特性的一组键值对。你可以在 Kubernetes 的各个组件中使用 –feature-gates 标志来启用或禁用这些特性。】 关闭该特性:

打开 /etc/kubernetes/manifest/kube-controller-manager.yml的 spec.container.command中增加

- --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false

修改完,例如:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-controller-manager
    tier: control-plane
  name: kube-controller-manager
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-controller-manager
    - --allocate-node-cidrs=true
    - --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
    - --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
    - --bind-address=0.0.0.0
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --cluster-cidr=1.2.0.0/10
    - --cluster-name=kubernetes
    - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
    - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
    - --controllers=*,bootstrapsigner,tokencleaner
    - --feature-gates=EphemeralContainers=true
    - --kubeconfig=/etc/kubernetes/controller-manager.conf
    - --leader-elect=true
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    - --root-ca-file=/etc/kubernetes/pki/ca.crt
    - --service-account-private-key-file=/etc/kubernetes/pki/sa.key
    - --service-cluster-ip-range=10.88.0.0/22
    - --use-service-account-credentials=true
    - --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false
    ...

重启 kube-controller-manager,ps:如果是多个master,需要每个都修改。

小结

本文记录导入外部集群到rancher时,遇到的用户认证密钥不正确的问题。回顾k8s在1.23~1.25的更新日志,以及查阅issues,原因是1.24启用了新测试特性LegacyServiceAccountTokenNoAutoGeneration,该特性使用经过签名的持有者令牌来验证请求。可以通过k8s组件上指定的各种特性开关控制来关闭此特性来解决报错,最终正常导入到rancher。

参考

背景:

sealos安装的Kubernetes集群,master节点出现大量端口占用。

环境:

应用 信息
os Ubuntu
sealos v4.1.7
Kubernetes v1.23.9

排查问题

按照本地端口对输出进行排序

netstat -anp | grep ESTABLISHED | awk '{print $4}' | sort | uniq -c | sort -n
    28 10.0.0.101:6443
    111 127.0.0.1:2379
  9009 10.0.0.101:5000

查找本地端口对应的应用程序

  lsof -i :5000
  image-cri 2811249 root 4120u  IPv4 291818320      0t0  TCP master1:41710->master1:5000 (ESTABLISHED)

可见,是image-cri-shim占用的端口数。

image-cri-shim 工作原理

image-cri-shim 是一个基于 CRI (Container Runtime Interface) 和 kubelet 的 gRPC (Google Remote Procedure Call) shim。CRI 是 Kubernetes 中用于与容器运行时进行交互的接口,而 kubelet 是负责维护容器运行状态和节点级别的资源管理的 Kubernetes 组件。

常用操作:

  • 启动服务: systemctl start image-cri-shim
  • 停止服务: systemctl stop image-cri-shim
  • 重启服务: systemctl restart image-cri-shim
  • 查看服务状态: systemctl status image-cri-shim
  • 参考日志: journalctl -u image-cri-shim -f

重现问题

  1. 在测试环境安装相同版本的sealos版本4.1.7,这里就不赘述,可以参考(https://sealos.io/zh-Hans/docs/getting-started/kuberentes-life-cycle)

  2. 安装好之后,发现image-cri-shim的版本(4.1.3)不对,

image-cri-shim --version
image-cri-shim version 4.1.3-ed0a75b9
  1. 更新image-cri-shim版本为4.1.7
wget https://github.com/labring/sealos/releases/download/v4.1.7/sealos_4.1.7_linux_amd64.tar.gz && tar xvf sealos_4.1.7_linux_amd64.tar.gz.1 image-cri-shim
sealos exec -r master,node "systemctl stop image-cri-shim"
sealos scp "./image-cri-shim" "/usr/bin/image-cri-shim"
sealos exec -r master,node "systemctl start image-cri-shim"
sealos exec -r master,node "image-cri-shim -v"

输出类似以下内容,表示成功:

image-cri-shim version 4.1.7-ed0a75b9
192.168.1.101:22: image-cri-shim version 4.1.7-ed0a75b9
192.168.1.102:22: image-cri-shim version 4.1.7-ed0a75b9
  1. 启动后,观察日志:
root@master1:~# journalctl -u image-cri-shim -f
-- Logs begin at Wed 2023-04-19 22:42:32 UTC. --
May 25 06:49:38 master1 image-cri-shim[1116090]: 2023-05-25T06:49:38 info actual imageName: pause:3.6
May 25 06:49:38 master1 image-cri-shim[1116090]: 2023-05-25T06:49:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 06:54:38 master1 image-cri-shim[1116090]: 2023-05-25T06:54:38 info actual imageName: pause:3.6
May 25 06:54:38 master1 image-cri-shim[1116090]: 2023-05-25T06:54:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 06:59:38 master1 image-cri-shim[1116090]: 2023-05-25T06:59:38 info actual imageName: pause:3.6
May 25 06:59:38 master1 image-cri-shim[1116090]: 2023-05-25T06:59:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
  1. 监控端口数的变化
netstat -na | grep 5000 | wc -l
96
116
120
...
  1. 可见,每个5分钟,占用的端口数就增加20左右。问题复现了,接着我们来看看怎么处理。

更新版本为4.2.0

从sealos的github的issues有提供一个修复方案:升级image-cri-shim的版本为4.2.0

wget https://github.com/labring/sealos/releases/download/v4.2.0/sealos_4.2.0_linux_amd64.tar.gz && tar xvf sealos_4.2.0_linux_amd64.tar.gz image-cri-shim
sealos exec -r master,node "systemctl stop image-cri-shim"
sealos scp "./image-cri-shim" "/usr/bin/image-cri-shim"
sealos exec -r master,node "systemctl start image-cri-shim"
sealos exec -r master,node "image-cri-shim -v"

输出如下,表示成功:

image-cri-shim version 4.2.0-f696a621
192.168.1.101:22: image-cri-shim version 4.2.0-f696a621
192.168.1.102:22: image-cri-shim version 4.2.0-f696a621

观察是否已修复

  1. 观察inamge-cri-shim日志:
root@master1:~# journalctl -u image-cri-shim -f
-- Logs begin at Wed 2023-04-19 22:42:32 UTC. --
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info Timeout: {15m0s}
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info criRegistryAuth: map[]
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info criOfflineAuth: map[sealos.hub:5000:{Username:admin Password:passw0rd Auth: Email: ServerAddress:http://sealos.hub:5000 IdentityToken: RegistryToken:}]
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info socket info shim: /var/run/image-cri-shim.sock ,image: /run/containerd/containerd.sock, registry: http://sealos.hub:5000
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info changed ownership of socket "/var/run/image-cri-shim.sock" to root/root
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info changed permissions of socket "/var/run/image-cri-shim.sock" to -rw-rw----
May 25 07:44:41 master1 image-cri-shim[1159432]: 2023-05-25T07:44:41 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 07:49:42 master1 image-cri-shim[1159432]: 2023-05-25T07:49:42 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 07:54:42 master1 image-cri-shim[1159432]: 2023-05-25T07:54:42 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
  1. 监控端口数的变化
root@master1:~# netstat -na | grep 5000 | wc -l
1 
6 
1
  1. 可见,请求是还有,但并没有增加端口数,看来问题修复了。

ps: 我们重现问题的Kubenetes版本是v1.23.9,在实际开发中发现其他版本的v1.25.7也会出现类似问题,验证此版本待后续更新。

小结

本文主要记录排查、复现、处理【sealos安装的Kubenetes集群master节点大量占用端口】的过程,原因系image-cri-shim的版本问题,旧版本会频繁请求master,导致创建大量的连接,占用端口数。通过升级image-cri-shim版本可以解决该问题。

参考

背景: 公司项目中,需要暴露对内网的Websocket服务。

Websocket 它是一种基于 TCP 的一种独立实现,APISIX 可以对 TCP/UDP 协议进行代理并实现动态负载均衡, 在 nginx 世界,称 TCP/UDP 代理为 stream 代理,在 APISIX 这里我们也遵循了这个声明,下文统称Stream。

SNI(Server Name Indication)是用来改善 SSL 和 TLS 的一项特性,它允许客户端在服务器端向其发送证书之前向服务器端发送请求的域名,服务器端根据客户端请求的域名选择合适的 SSL 证书发送给客户端。

本文主要介绍ApiSix如何基于SNI代理TCP/UDP。

启用Stream 代理

在 conf/config.yaml 配置文件设置 stream_proxy 选项, 指定一组需要进行动态代理的 IP 地址。默认情况不开启 stream 代理。

apisix:
  stream_proxy: # TCP/UDP proxy
    tcp: # TCP proxy address list
      - 9100
      - "127.0.0.1:9101"
    udp: # UDP proxy address list
      - 9200
      - "127.0.0.1:9211"

如果 apisix.enable_admin 为 true,上面的配置会同时启用 HTTP 和 stream 代理。如果你设置 enable_admin 为 false,且需要同时启用 HTTP 和 stream 代理,设置 only 为 false:

apisix:
  enable_admin: false
  stream_proxy: # TCP/UDP proxy
    only: false
    tcp: # TCP proxy address list
      - 9100

启用 TLS:

首先,我们需要给对应的 TCP 地址启用 TLS

  stream_proxy: # TCP/UDP proxy
    only: false
    tcp: # TCP proxy address list
      - 9100
      - addr: 9333
        tls: true

配置路由

当连接为 TLS over TCP 时,我们可以通过 SNI 来匹配路由,比如:

curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: Your-Api-Key' -X PUT -d '
  {
      "sni": "mysite.com",
      "server_port": 9333,
      "upstream": {
          "scheme": "tls",
          "nodes": {
              "192.168.1.33:8080": 1
          },
          "type": "roundrobin"
      }
  }'

查看是否创建成功:

curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: Your-Api-Key'

如创建错误,可以删除

curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: Your-Api-Key' -X DELETE 

查看更多stream路由:

curl http://127.0.0.1:9180//apisix/admin/stream_routes -H 'X-API-KEY: Your-Api-Key' 

更多Api参考: https://apisix.apache.org/zh/docs/apisix/2.12/admin-api/#stream-route

上传SNI证书

上传站点(mysite.com)的证书到ApiSix

测试

curl -v https://mysite.com:9333/
*   Trying x.x.y.y...
* TCP_NODELAY set
* Connected to mysite.com:19333 (x.x.y.y) port 9333 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=*.mysite.com
*  start date: Mar 29 00:00:00 2023 GMT
*  expire date: Jun 27 23:59:59 2023 GMT
*  subjectAltName: host "mysite.com" matched cert's "*.mysite.com"
*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL RSA Domain Secure Site CA
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: mysite.com:9333
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain; charset=utf-8
< Sec-Websocket-Version: 13
< X-Content-Type-Options: nosniff
< Date: Sun, 23 Apr 2023 05:50:07 GMT
< Content-Length: 12
<
Bad Request
* Connection #0 to host mysite.com left intact
* Closing connection 0

Success。

过程中遇见的问题

  1. 连接不上
 curl -v http://mysite.com
*   Trying x.x.y.y...
* TCP_NODELAY set
* Connected to mysite.com (x.x.y.y.) port 19333
> GET / HTTP/1.1
> Host: mysite.com:19333
> User-Agent: curl/7.64.1
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer

需要排查路由器端口是否正常开放,ApiSix的端口是否正常,经排查原因是:配置路由时"server_port"缺失

{
      "sni": "mysite.com",
      "upstream": {
          "scheme": "tls",
          "nodes": {
              "192.168.1.33:8080": 1
          },
          "type": "roundrobin"
      }
  }
  1. handshark失败,证书错误
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to mysite.com:19333
* Closing connection 0
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to mysite.com:19333

排位原因是:这里作者使用的ApiSix版本是2.12,缺失前缀 【addr】

  stream_proxy: # TCP/UDP proxy
    only: false
    tcp: # TCP proxy address list
      - 9100
      - 9333
        tls: true

参考

在 Kubernetes 中,Kubelet 是在每个节点上运行的重要组件之一,它负责管理容器的生命周期。而 CRI(Container Runtime Interface)则是 Kubelet 用于与容器运行时进行通信的接口(如下图)。

CRI 采用了 ProtoBuffer 和 gPRC,规定 kubelet 该如何调用容器运行时去管理容器和镜像,Kubernetes 通过CRI可支持多种类型的OCI容器运行时,例如 docker、contained、CRI-O、runC、fraki和Kata Containers 等)。

为了方便用户进行容器运行时的调试工作,社区提供了 crictl 工具,用于与 CRI 接口进行交互,本文简要介绍如何使用 crictl 对 Kubernetes节点进行调试 。

kubelet-layout:

安装

你可以从 cri-tools 发布页面 下载一个压缩的 crictl 归档文件,用于几种不同的架构。 下载与你的 kubernetes 版本相对应的版本。 提取它并将其移动到系统路径上的某个位置,例如 /usr/local/bin/。

  • 查看版本,验证安装
crictl --version

输出例如如下,说明安装成功:

crictl version v1.23.0 

查看或编辑配置

要查看或编辑当前配置,请查看或编辑 /etc/crictl.yaml 的内容。

cat /etc/crictl.yaml
image-endpoint: unix:///var/run/image-cri-shim.sock
runtime-endpoint: unix:///run/containerd/containerd.sock

调试节点

  • 列出运行中的容器:
crictl ps

例如我们列出k8s集群的所有容器,例如输出:

CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID
508e30da66ce7       7a71aca7b60fc       3 days ago          Running             calico-node               0                   e0ec650992997
9daa288a68426       f822f80398b9a       3 days ago          Running             calico-typha              0                   f5c4bd6471941
300d948e75019       f6bc1b780606f       3 days ago          Running             kube-controller-manager   1                   d5d681744a377
1cfdc1a6726ae       0198979b7707e       3 days ago          Running             kube-scheduler            1                   eb6ff07ees98c
3699c312c56f9       9e6a540eeeb62       3 days ago          Running             kube-proxy                0                   e8707140d12941
4159d7ec37b29       5bc0062e9555c       3 days ago          Running             kube-apiserver            0                   22d043569737f
8f56a047e8627      25f8c7f3da61c       3 days ago          Restart             etcd                      0                   458e540c798c8

本例中,etcd容器一直启动,可以使用以下命令获取容器的日志:

crictl logs container-id

如此,通过日志帮助定位问题。

更多命令

  • 列出所有的pods
crictl pods
  • 创建容器
crictl run --runtime=remote \
  docker.io/library/nginx:latest \
  nginx-container

ps:使用远程容器CRI来使用最新的 nginx 镜像启动nginx-container的容器。

  • 删除容器:
crictl rm nginx-container
  • 列出所有镜像:
crictl images
  • 帮助
 crictl -h 
NAME:
   crictl - client for CRI

USAGE:
   crictl [global options] command [command options] [arguments...]

VERSION:
   v1.23.0

COMMANDS:
   attach              Attach to a running container
   create              Create a new container
   exec                Run a command in a running container
   version             Display runtime version information
   images, image, img  List images
   inspect             Display the status of one or more containers
   inspecti            Return the status of one or more images
   imagefsinfo         Return image filesystem info
   inspectp            Display the status of one or more pods
   logs                Fetch the logs of a container
   port-forward        Forward local port to a pod
   ps                  List containers
   pull                Pull an image from a registry
   run                 Run a new container inside a sandbox
   runp                Run a new pod
   rm                  Remove one or more containers
   rmi                 Remove one or more images
   rmp                 Remove one or more pods
   pods                List pods
   start               Start one or more created containers
   info                Display information of the container runtime
   stop                Stop one or more running containers
   stopp               Stop one or more running pods
   update              Update one or more running containers
   config              Get and set crictl client configuration options
   stats               List container(s) resource usage statistics
   completion          Output shell completion code
   help, h             Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --config value, -c value            Location of the client config file. If not specified and the default does not exist, the program's directory is searched as well (default: "/etc/crictl.yaml") [$CRI_CONFIG_FILE]
   --debug, -D                         Enable debug mode (default: false)
   --image-endpoint value, -i value    Endpoint of CRI image manager service (default: uses 'runtime-endpoint' setting) [$IMAGE_SERVICE_ENDPOINT]
   --runtime-endpoint value, -r value  Endpoint of CRI container runtime service (default: uses in order the first successful one of [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]). Default is now deprecated and the endpoint should be set instead. [$CONTAINER_RUNTIME_ENDPOINT]
   --timeout value, -t value           Timeout of connecting to the server in seconds (e.g. 2s, 20s.). 0 or less is set to default (default: 2s)
   --help, -h                          show help (default: false)
   --version, -v                       print the version (default: false)

以上。

参考

本文主要学习记录Kubernetes集群暴露服务的方式: Ingress。

简介

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

IngressController 为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

上图清晰标识出了Ingress的流量走向,其中:

  • Ingress基于DNS名称(host)或URL路径把请求转发⾄指定的Service资源的规则。它仅是⼀组路由规则的集合。
  • Ingress控制器是真正实现“流量穿透”,可以由具有反向代理(HTTP/HTTPS)功能的服务程序实现 , 然后根据这些规则的匹配机制路由请求流量

Ingress 资源声明

Ingress是Kubernetes API的标准资源类型之⼀ ,一个最小Ingress例子:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

其中:

  • Ingress 需要指定 apiVersion、kind、 metadata和 spec 字段。
  • Ingress 对象的命名必须是合法的 DNS 子域名名称。
  • Ingress annotations 来配置一些选项, ⽤于识别其所属的Ingress控制器的类别。
  • Ingress rules 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP(S) 流量的规则。

更多参考: https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/

Ingress Controller

Ingress控制器可以由任何具有反向代理(HTTP/HTTPS)功能的服务程序实现,目前支持和维护 AWS、 GCE 和 Nginx Ingress 控制器。

  • Nginx Ingress 作为反向代理和负载均衡器。
  • Apache APISIX Ingress 控制器 是一个基于 Apache APISIX 网关 的 Ingress 控制器。

更多参考:https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress-controllers/

安装 Ingress Nginx

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/cloud/deploy.yaml  -O ingress-nginx-deploy.yaml

kubectl apply -f  ingress-nginx-deploy.yaml

观测是否成功:

kubectl get pods -n ingress-nginx --watch

如果成功的话,查看namespace下所有的资源信息

root@master:/home/master# kubectl get all -n ingress-nginx
NAME                                            READY   STATUS    RESTARTS   AGE
pod/ingress-nginx-controller-123456   1/1     Running   0          1h

NAME                                         TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.104.182.98    192.168.1.100   80:31666/TCP,443:31888/TCP   1d
service/ingress-nginx-controller-admission   ClusterIP      10.111.228.99   <none>         443/TCP                      1d

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           1d

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-123456   1         1         1      1d

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           1s         1d
job.batch/ingress-nginx-admission-patch    1/1           2s         1d

使用Ingress 发布 Tomcat

部署Tomcat Service

  1. 创建deployment

        kubectl create deployment web --image=tomcat:8.0.50-jre8-alpine
    
  2. 将 Deployment 暴露出来

    kubectl expose deployment web --type=NodePort --port=8080
    
    
  3. 将 Deployment 暴露出来

    root@master:/home/master# kubectl get service web
        NAME   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
        web    NodePort   10.100.183.22   <none>        8080:32562/TCP   30s
    
  4. 验证nodeport 是否正常访问 tomcat ,浏览器访问 http://matster_ip:32562

  5. 创建 ingress

 apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingressfoo
      annotations:
        kubernetes.io/ingress.class: "nginx"
      namespace: default
    spec:
      rules:
      - host: ingressfoo.io
        http:
          paths:
          - backend:
              service:
                name: web
                port:
                  number: 8080
            path: /bar
            pathType: Prefix

查看ingress是否创建成功

root@master:/home/master# kubectl get ingress
NAME         CLASS    HOSTS                     ADDRESS        PORTS   AGE
ingressfoo   <none>   ingressfoo.io   192.168.1.100   80      1h

说明Ingress创建成功,修改hosts :

ingressfoo.io 192.168.1.100

验证访问 ingressfoo.io/bar

success

参考

本文主要介绍Kubernetes集群资源监控机制和资源指标 前言 临近双11,对于码农,尤其是后端,尤其是某宝某东的后端,那是多么激动人心(心惊胆战)的一夜,为什么?怕宕机呀~。那么就让我们来构建护城河–监控与自动扩容,来抵挡千军万马–高并发场景。首先让我们学习一些基础:k8s集群资源监控机制和资源指标。 分析 从需求出发,我们自然需要收集一些数据(资源指标),再根据指标做一系列的操作(control),比如说预警警告、统计、自动扩容等。 首先,我们希望可以监控整个Kubernetes集群的健康状况,包括: 整个集群的资源利⽤率 集群中的所有⼯作节点是否运⾏正常、系统资源容量⼤⼩ 每个⼯作节点上运⾏的容器化应⽤的数量 k8s资源控制 我们看看k8s如何资源控制 限制节点 以购物平台为例,微服务广受推崇,比如双11当天,用户进首页是流畅的,但是进活动主页就卡顿,到了零点时,加入购物车的按钮都转圈了。设想你的设计可能是三个微服务(主页服务 、双十一活动服务、订单服务),可想而知,活动服务是压力最大的,我们希望,就算宕机,不要影响其他的服务。所以可以把活动服务限制运行在某节点。 创建一个会被调度到特定节点上的 Pod,你也可以通过设置 nodeName 将某个 Pod 调度到特定的节点 nodeName: foo-node # 调度 Pod 到特定的节点 限制内存 还是上面的例子,我们把活动服务调度到了 foo-node。那么剩下的服务没有去限制,但想想订单服务的压力也不小,这里我们希望限制它的资源上限。 要为容器指定内存请求,请在容器资源清单中包含 resources:requests 字段。 同理,要指定内存限制,请包含 resources:limits。 resources: requests: memory: "1000Gi" limits: memory: "1000Gi" 限制CPU 要为容器指定 CPU 请求,请在容器资源清单中包含 resources: requests 字段。 要指定 CPU 限制,请包含 resources:limits resources: limits: cpu: "100" requests: cpu: "100" Example 环境准备: k8s集群(master * 1,node * 2 ) kubectl 命令行工具( 笔者使用rancher) 创建一个namespace(stress)。 增加一个deployment, 修改内存上限为10M 1.

前两篇(1.volume2.pv&pvc)通过部署redis学习实战了k8s的来Volume、PV和PVC。但是,应⽤程序存在“有状态”和“⽆状态”两种类别,显然redis属于读写磁盘需求的有状态应⽤程序,如⽀持事务功能的RDBMS存储系统,所以,本文学习实战k8s有状态应用的部署。

Stateful基础

StatefulSet是Pod资源控制器的⼀种实现,⽤于部署和扩展有状态应⽤的Pod资源,确保它们的运⾏顺序及每个Pod资源的唯⼀性。适用以下需求的应用:

  • 稳定且唯⼀的⽹络标识符。
  • 稳定且持久的存储。
  • 有序、优雅地部署和扩展。
  • 有序、优雅地删除和终⽌。
  • 有序⽽⾃动地滚动更新。

部署

接着,这里把之前的redis存储修改为stateful的方式,修改后的步骤:

  1. 创建 ConfigMap (参考第一篇)
  2. 修改 Deployment 为StatefulSets

修改部署StatefulSets

mkdir my-redis-statefulsets.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: my-ns
spec:
  replicas: 1
  serviceName: redis
  selector:
    matchLabels:
      name: redis
  template:
    metadata:
      labels:
        name: redis
    spec:
      containers:
        - name: redis
          image: redis
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
#          command: ["redis-server","/etc/redis/redis.conf"]
          command:
            - redis-server
            - /etc/redis/redis.conf
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: my-redis-config
              mountPath: /etc/redis/redis.conf
              subPath: redis.conf
            - name: my-redis-storage
              mountPath: /data
      volumes:
        - name: my-redis-storage
          emptyDir: {}
        - name: my-redis-config
          configMap:
            name: my-redis-config
            items:
              - key: redis.conf
                path: redis.conf
 
---
kind: Service
apiVersion: v1
metadata:
  labels:
    name: redis
  name: redis
  namespace: my-ns
spec:
  type: NodePort
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
    nodePort: 30379
  selector:
    name: redis          

其中:

  1. Headless Service:⽤于为Pod资源标识符⽣成可解析的DNS资源记录
  2. StatefulSet ⽤于管控Pod资源
  3. volumeClaimTemplate则基于静态或动态的PV供给⽅式为Pod资源提供 专有且固定的存储(这里我们直接使用了第二篇创建的pv)

测试

redis-client 连接 NodeId:NodePort

# redis-cli -h YourNodeIp-p 30379 -a 123456
YourNodeIp:30379> info
# Serverredis_version:7.0.4

连接成功!

参考

jefffff

Stay hungry. Stay Foolish COOL

Go backend developer

China Amoy