在 Minikube 中直接上手学习 K8S Service

K8S 为什么需要 Service?

  1. Pod 的 IP 不是固定的;
  2. 一组 Pod 实例之间有负载均衡的需求;

上机操作

以下使用 minikube 作实例:

# 安装
brew install minikube
# 启动
minikube start
# 创建 ~/.kube/config 请注意先备份
minikube update-context
# 进入宿主机
minikube ssh

集群启动后,创建 Deployment 和 Service

apiVersion: v1
kind: Service
metadata:
  namespace: default
  name: nginx-svc
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 80
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          protocol: TCP

这个 Service 使用了 selector 字段来声明这个 Service 只代理携带了 app=nginx 标签的 Pod。并且,这个 Service 的 80 端口,代理的是 Pod 的 8080 端口。

> kubectl get endpoints nginx-svc

NAME        ENDPOINTS                                      AGE
nginx-svc   10.244.0.13:80,10.244.0.14:80,10.244.0.15:80   4m29s

只有处于 Running 状态,且 readinessProbe 检查通过的 Pod,才会出现在 Service 的 Endpoints 列表里。并且,当某一个 Pod 出现问题时,Kubernetes 会自动把它从 Service 里摘除掉。

> kubectl get svc nginx-svc

NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-svc   NodePort   10.110.83.228   <none>        80:32597/TCP   4m37s

这个ip是 k8s 自动为 Service 分配的。Service 提供的是 Round Robin 方式的负载均衡。可以使用以下方式进行测试

> minikube ip
192.168.49.2

> minikube ssh
curl http://192.168.49.2:32597

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Service 工作原理

Service 是由 kube-proxy 组件,加上 iptables 来共同实现的。

  1. 凡是目的地址是 10.110.83.228、目的端口是 80 的 IP 包,都应该跳转到另外一条名叫 KUBE-SVC-KBEH26U7YV6VBLSX 的 iptables 链进行处理。使用 iptables-save 查看
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx-svc:default" -m tcp --dport 32597 -j KUBE-EXT-KBEH26U7YV6VBLSX

-A KUBE-SERVICES -d 10.110.83.228/32 -p tcp -m comment --comment "default/nginx-svc:default cluster IP" -m tcp --dport 80 -j KUBE-SVC-KBEH26U7YV6VBLSX
  1. KUBE-SVC-KBEH26U7YV6VBLSX 是一组规则,这三个目的地就是Service 代理的 3 个pod。
-A KUBE-SVC-KBEH26U7YV6VBLSX -m comment --comment "default/nginx-svc:default -> 10.244.0.13:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-E36YYYCTO5EK2IJ4
-A KUBE-SVC-KBEH26U7YV6VBLSX -m comment --comment "default/nginx-svc:default -> 10.244.0.14:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-INUGID7JOCIXUMGA
-A KUBE-SVC-KBEH26U7YV6VBLSX -m comment --comment "default/nginx-svc:default -> 10.244.0.15:80" -j KUBE-SEP-H22OXDXNEK3GBZK4

iptables 规则的匹配是从上到下逐条进行的,所以为了保证上述三条规则每条被选中的概率都相同,我们应该将它们的 probability 字段的值分别设置为 1/3(0.333…)、1/2 和 1。这种设计思路,在我们日常开发中,也是值的借鉴的。Endpoints 对应的 iptables 规则,正是 kube-proxy 通过监听 Pod 的变化事件,在宿主机上生成并维护的。缺点:宿主机有大量Pod时,大量 iptables 规则不断被刷新,占用该宿主机 CPU 资源。

IPVS 模式

IPVS 模式的工作原理,其实跟 iptables 模式类似。当我们创建了前面的 Service 之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址按如文档方法开启 ipvs。Minikube 操作开启 IPVS

# 安装 ipvsadm 工具
sudo apt-get update -y
sudo apt-get install -y ipvsadm

准备就绪后,重新创建 nginx-svc。kube-proxy 就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略。

kubectl get svc nginx-svc
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-svc   NodePort   10.97.127.252   <none>        80:31379/TCP   3m3s
ipvsadm -ln

TCP  10.97.127.252:80 rr
  -> 10.244.0.13:80               Masq    1      0          0
  -> 10.244.0.14:80               Masq    1      0          0
  -> 10.244.0.15:80               Masq    1      0          0

相比于 iptables,IPVS 在内核中的实现其实也是基于 Netfilter 的 NAT 模式,所以在转发这一层上,理论上 IPVS 并没有显著的性能提升。但是,IPVS 并不需要在宿主机上为每个 Pod 设置 iptables 规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价。“将重要操作放入内核态”是提高性能的重要手段。不过需要注意的是,IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,还是要靠 iptables 来实现。只不过,这些辅助性的 iptables 规则数量有限,也不会随着 Pod 数量的增加而增加。所以,在大规模集群里,我非常建议你为 kube-proxy 设置–proxy-mode=ipvs 来开启这个功能。它为 Kubernetes 集群规模带来的提升,还是非常巨大的。

Service 与 DNS

在 Kubernetes 中,Service 和 Pod 都会被分配对应的 DNS A 记录(从域名解析 IP 的记录)。再次创建了 ClusterIP 模式 service(type: ClusterIP) 和 Headless Service(.spec.clusterIP: None)

> kubectl get svc
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-svc              NodePort    10.97.127.252   <none>        80:31379/TCP   11h
nginx-svc-cluster-ip   ClusterIP   10.106.66.77    <none>        80/TCP         23m
nginx-svc-headless     ClusterIP   None            <none>        80/TCP         19m

对于 CLUSTER-IP 有值的,不管是 NodePort 还是 ClusterIP 类型,Service 的 A记录格式都是: ..svc.cluster.local而对于指定了 clusterIP=None 的 Headless Service 来说,它的 A 记录的格式也是:..svc.cluster.local。但是,当你访问这条 A 记录的时候,它返回的是所有被代理的 Pod 的 IP 地址的集合。我们登上一个 pod 看一看,首先需要安装 nslookup

kubectl exec nginx-7c5ddbdf54-gg5ql -it /bin/bash
# 安装 nslookup
apt update && apt install -y dnsutils

> nslookup nginx-svc
Server:                10.96.0.10
Address:        10.96.0.10#53

Name:        nginx-svc.default.svc.cluster.local
Address: 10.97.127.252

> nslookup nginx-svc-cluster-ip
Server:                10.96.0.10
Address:        10.96.0.10#53

Name:        nginx-svc-cluster-ip.default.svc.cluster.local
Address: 10.106.66.77

> nslookup nginx-svc-headless
Server:                10.96.0.10
Address:        10.96.0.10#53

Name:        nginx-svc-headless.default.svc.cluster.local
Address: 10.244.0.13
Name:        nginx-svc-headless.default.svc.cluster.local
Address: 10.244.0.14
Name:        nginx-svc-headless.default.svc.cluster.local
Address: 10.244.0.15

附录 Service 类型

在 Kubernetes 中,Service 类型用于将一组 Pod 暴露为网络服务。Service 类型有四种:

  • ClusterIP: 这是默认的 Service 类型。ClusterIP 服务具有内部 IP 地址,只能由集群内部的其他 Pod 访问。
  • NodePort: NodePort 将服务暴露在每个节点的外部 IP 地址上,并使用指定的端口。任何人都可以使用该端口从集群外部访问服务。
  • LoadBalancer: LoadBalancer 服务使用云提供商的负载均衡器将服务暴露在互联网上。
  • ExternalName: ExternalName 服务指向外部 DNS 名称。

ClusterIPNodePort 的主要区别在于 可访问性:

  • ClusterIP: 仅限于集群内部访问,外部无法访问。适合内部服务或测试环境。
  • NodePort: 可以从集群外部通过 Node 的 IP 地址和端口访问。适合需要从外部访问的服务,例如 Web 服务或 API。

默认值:默认情况下,Kubernetes 服务的类型为 ClusterIP。这意味着如果您不指定 service.spec.type 字段,则将创建 ClusterIP 服务。

Read more

golang proto api 校验国际化 protovalidate

golang proto api 校验国际化 protovalidate

众所周知,protobuf 原型文件扩展很多功能,比如生成 http 接口层代码,顺势就有了生成接口参数校验代码的需求。 早期可以使用https://github.com/bufbuild/protoc-gen-validate 来实现,通过生成特定的 go 代码的方式来实现校验。 github 中也提到目前趋于稳定,不会有更多新特性的支持,推荐大家使用新的版本 protovalidate,https://github.com/bufbuild/protovalidate 。该版本是protoc-gen-validate 的“精神继承者”。它不需要任何代码生成并支持自定义约束。 现在我们尝试新版本,并且增加国际化支持。 go get github.com/bufbuild/protovalidate-go import "github.com/bufbuild/protovalidate-go" syntax = "proto3"; package

By brian
git clone 复制一个整个仓库并推送到新地址

git clone 复制一个整个仓库并推送到新地址

要使用 git clone --bare 复制一个新的仓库并推送到远程仓库,可以按照以下步骤操作: 1. 克隆一个裸仓库 首先,使用 git clone --bare 命令克隆源仓库。假设源仓库的 URL 是 https://github.com/user/source-repo.git,你可以执行以下命令: bash复制 git clone --bare https://github.com/user/source-repo.git 这将创建一个新的裸仓库(没有工作区),通常会创建一个名为 source-repo.git 的目录。 2. 进入裸仓库目录 进入刚刚克隆的裸仓库目录: bash复制 cd source-repo.git 3. 添加新的远程仓库 接下来,

By brian
搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

在搜索时使用英文关键词,提高结果质量。尽量使用 google.com 以下搜索引擎技巧在 google.com 进行测试,效果都很好,前三个非常常用且强烈推荐。 使用精确搜索 * 建议: 使用双引号 "" 搜索完全匹配的短语,避免无关结果。 * 示例: * "Java NullPointerException" fix * 场景: 找到错误信息的精确解决方案。 利用站内搜索 * 建议: 使用 site: 限制搜索范围到特定网站。 * 示例: * site:stackoverflow.com "TypeError: undefined is not a function" * 场景: 搜索 Stack Overflow、官方文档或技术博客的特定内容。 一些关键词 * 建议:

By brian
沪ICP备2022013452号-1