使用 zerotier 打通云上与本地网络并搭建 k8s 集群

最近不少云厂商都推出了云主机包年优惠活动,虽然配置不高但好在有公网 IP,恰好家里有一台 Mac mini 在吃灰,花了三杯咖啡钱买了一年某云的云主机打算将其和家里的 Mac mini 组成 k8s 集群,方便平时学习和测试同时也能通过云主机的公网 IP 访问到 Mac mini 上运行的服务。为了方便讲解下面以 ecsmacmini 表示两台主机,先来看看两台主机的网络信息:

  • ecs

    • eth0 172.19.174.87
    • internet 47.102.84.170
  • macmini

    • eth0 172.16.14.253

两台主机处于不同的网络中,其中 ecs 主机具有公网 IP,很显然这两台主机并不满足 k8s 的网络要求,因此需要先使用 ZeroTier 组建一个虚拟局域网将两台主机连接起来。ZeroTier 是一个基于 P2P 的虚拟局域网软件,理论上只要主机连接了互联网就能与其它主机组成虚拟局域网,并且组网的主机不需要有公网 IP。使用 ZeroTier 组网非常简单,再加上网上已经有非常多关于 ZeroTier 的相关教程所以组网流程简单带过。

组建虚拟局域网

打开 ZeroTier 官网 注册一个账户,可以用 Github 或者 Google 账户注册,注册完毕后进入到个人页面,点击 Create A Network 按钮创建一个网络,创建成功后下方会出现网络的信息:

20240110100635

Network ID 是网络的唯一标识,主机可以通过这个标识加入网络。主机加入网络后 ZeroTier 会在 Subnet 指定的网段内给主机分配 IP 地址,如果网段与主机网络的网段有冲突则需要点击网络进入网络管理页面,在 IPv4 Auto-Assign 栏中修改网段。

网络创建好之后就可以将主机加入到网络中,加入网络流程也非常简单,在 下载页面 下载对应操作系统的客户端安装即可,Linux 系统可以使用以下命令一键安装:

curl -s https://install.zerotier.com | sudo bash

ZeroTier 客户端包含核心程序 zerotier-one 以及命令行管理工具 zerotier-cli,安装完毕后就可以使用管理工具将主机加入到创建好的虚拟网络中:

$ zerotier-cli join <network-id>
200 join OK

<network-id> 替换为刚创建的网络 Network ID,返回 200 join OK 表示正常,接下来在需要组网的主机上重复以上步骤即可。

所有主机都操作完后回到 ZeroTier 控制台,点击刚创建的网络进入网络管理页面,下拉找到 Members 栏就会看到刚加入的主机,选中主机前面的 Auth? 复选框授主机加入网络,稍等片刻后刷新页面,在 Managed IPs 列可以看到 ZeroTier 为主机分配的 IP 地址,网络中的主机可以通过这个 IP 地址与其它主机进行通信。

20240110102813

在主机上执行 zerotier-cli listnetworks 命令可以查看主机所加入的网络信息:

$ zerotier-cli listnetworks
200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
200 listnetworks 233ccaac273421e8 berserk_taylor ea:55:d4:39:11:4a OK PRIVATE ztr4n75dop 192.168.195.183/24

为了更好的区分主机可以给主机设置 short-name,这个名字并不只是起到装饰作用,配合 ZeroTier 提供的 ZeroNSD 可以通过这个名字访问指定的主机,如同 k8s 集群中使用 .cluster.local 域名访问 Service,具体操作流程这里就不展开了感兴趣的同学可以看官方文档。

从图中可以看到 ecs 主机分配的 IP 地址为 192.168.195.79macmini 主机分配的 IP 地址为 192.168.195.183,下面在 ecs 使用虚拟局域网 IP 来 ping 一下 macmini 测试网络连通性:

$ ping 192.168.195.79 -c 3
PING 192.168.195.79 (192.168.195.79) 56(84) bytes of data.
64 bytes from 192.168.195.79: icmp_seq=1 ttl=64 time=875 ms
64 bytes from 192.168.195.79: icmp_seq=2 ttl=64 time=5.67 ms
64 bytes from 192.168.195.79: icmp_seq=3 ttl=64 time=5.69 ms

--- 192.168.195.79 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 5.671/295.576/875.365/409.972 ms

到这里两台处于不同网络的主机已经可以通过 ZeroTier 网络相互通信了,需要注意的是网络连接质量与主机所处的网络有关。理想情况下主机之间是通过 P2P 直接通信的,但在建立通信前仍需向 ZeroTier 服务器请求对方主机的信息所以第一个 PING 的延迟会比较高。即使两台主机可以直接通信,所处的网络有防火墙或者 NAT 网络也会导致网络连接质量下降。云主机机房是在我所在的城市所以与家里的 Mac mini 连接质量还不错,但在离家不到五公里的公司访问虚拟局域网内的主机会严重丢包甚至无法连接,所以连接质量还是与主机所在的网络有关。

最坏的情况下主机之间无法直接通信,此时 ZeroTier 会通过中继服务器转发数据包,这种情况下网络连接质量会非常差,但可以通过搭建 moon 节点来优化,具体流程可以参考 ZeroTier 官方文档 这里就不展开讨论。

主机加入网络后 ZeroTier 会在主机上新增一个以 zt 开头的虚拟网卡,也就是 zerotier-cli listnetworks 命令 <dev> 列的值,在我的主机上名字是 ztr4n75dop,这块网卡的名字非常重要,后面我们会用到它。

到这里组网就完成了,最后来汇总一下主机的网络信息:

  • ecs

    • eth0 172.19.174.87
    • internet 47.102.84.170
    • ztr4n75dop 192.168.195.79
  • macmini

    • eth0 172.16.14.253
    • ztr4n75dop 192.168.195.183

搭建 k8s 集群

解决了节点间的连通问题后就可以按照正常流程部署 k8s 集群了,唯一需要注意的是 cni 插件要使用 ZeroTier 创建的虚拟网卡来完成节点间的通信。下面使用轻量化 k8s 发行版 k3s 来搭建集群,由于云主机的配置比较低所以我 macmini 主机作为 master 节点。

k3s 为我们提供了几种配置集群的方法,这里使用 命令行参数 方式配置 master 节点:

以下命令用到的 IP 地址请按照实际情况替换

$ curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_EXEC=server sh -s - \
  --flannel-iface=ztr4n75dop \
  --flannel-backend=host-gw \
  --tls-san=192.168.195.183 \
  --tls-san=47.102.84.170 \
  --node-name=macmini

解释一下关键参数:

  • --flannel-iface=ztr4n75dop 指定 flannel 使用 ZeroTier 虚拟网卡
  • --flannel-backend=host-gw 使用 flannel 使用性能更好的 host-gw 模式
  • --tls-san=192.168.195.183 将 master 节点的 ZeroTier IP 添加到 TLS SAN 中,以便后续通过这个 IP 访问 k8s 集群
  • --tls-san=47.102.84.170 将云主机公网 IP 添加到 TLS SAN 中,以便后续通过公网 IP 访问 k8s 集群

安装命令执行完毕使用 k3s kubectl get no 命令查看 master 节点状态:

$ k3s kubectl get no
NAME      STATUS   ROLES                  AGE   VERSION
macmini   Ready    control-plane,master   1m   v1.29.0+k3s1

master 节点 Ready 后就可以配置 worker 节点了,在此之前需要在 master 节点上执行 k3s token create 命令生成一个 bootstrap token。接着在 worker 节点上执行以下命令:

$ curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_EXEC=agent sh -s - \
  --server=https://192.168.195.183:6443 \
  --token=<bootstrap token> \
  --flannel-iface=ztr4n75dop \
  --node-label=svccontroller.k3s.cattle.io/enablelb=true \
  --node-external-ip=172.19.174.87 \
  --node-name=ecs

worker 节点的安装参数与 master 节点稍微有点区别:

  • --server=https://192.168.195.183:6443 指定 master 节点的地址
  • --node-label=svccontroller.k3s.cattle.io/enablelb=true 为 worker 节点添加 svccontroller.k3s.cattle.io/enablelb 标签,拥有这个标签的节点会被 k3s 用作集群负载均衡的节点,因为 worker 节点有公网 IP 所以需要将这个标签添加到 worker 节点上
  • --node-external-ip=172.19.174.87 将 eth0 网卡的 IP 地址设置为节点的 external ip,k3s 会用这个 IP 地址作为 Service 的 LoadBalancer IP

需要注意的是 --node-label=svccontroller.k3s.cattle.io/enablelb=true--node-external-ip=172.19.174.87 这两个参数需要加到拥有公网 IP 的节点上,无论这个节点是 worker 还是 master。

稍等片刻后回到 master 节点上确认一下 worker 节点的状态:

$ k3s kubectl get no
NAME     STATUS   ROLES                 AGE   VERSION
macmini  Ready    control-plane,master  10m    v1.29.0+k3s1
ecs      Ready    <none>                30s    v1.29.0+k3s1

到这里集群就搭建完毕了,通过 ZeroTier 虚拟网络两台「相隔千里」的主机成功组成了一个 k8s 集群!

访问集群

为了方便运维可以将 APIServer 通过公网 IP 暴露出来,这样就可以通过公网 IP 访问集群了,在 master 节点创建 /root/apiserver-expose.yaml 文件:

# /root/apiserver-expose.yaml
apiVersion: v1
kind: Service
metadata:
  name: apiserver-expose
  namespace: default
spec:
  ports:
    - protocol: TCP
      port: 8443 # 注意,不能使用 6443 端口,因为这个端口已经被 APIServer 占用了
      targetPort: 6443
      name: apiserver
  type: LoadBalancer

---
kind: Endpoints
apiVersion: v1
metadata:
  name: apiserver-expose
  namespace: default
subsets:
  - addresses:
      - ip: 192.168.195.183 # master 节点的 ZeroTier IP 地址
    ports:
      - port: 6443
        name: apiserver

执行 k3s kubectl apply -f /root/apiserver-expose.yaml 命令创建 Service,然后将 master 节点上 /etc/rancher/k3s/k3s.yaml 文件复制到本地 /root/.kube/config,将里面 server 字段的 IP 替换为云主机的公网 IP,注意端口要修改为 8443,最后在云主机管理后台放行 8443 端口,然后就可以在本地使用 kubectl 访问集群了。

ingress

k3s 自带 traefik ingress controller,我们可以创建一个 whoami 应用测试一下 ingress 是否正常工作:

# /root/whoami.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  namespace: default
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      name: whoami
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: traefik/whoami
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      nodeSelector:
        kubernetes.io/hostname: macmini

---
apiVersion: v1
kind: Service
metadata:
  name: whoami
  namespace: default
spec:
  selector:
    app: whoami
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP

---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: whoami
  namespace: default
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: whoami
                port:
                  number: 80
            pathType: Prefix
            path: /whoami

我特意将 Pod 部署在 macmini 节点,以便验证是否可以通过云主机的公网 IP 访问 macmini 上运行的服务。将配置文件提交到 k8s 后就可以进行测试了:

$ curl http://47.102.84.170/whoami
Hostname: whoami-564b668c57-rb689
IP: 127.0.0.1
IP: ::1
IP: 10.42.0.9
IP: fe80::f47f:bfff:fedf:88f0
RemoteAddr: 10.42.0.8:39084
GET /whoami HTTP/1.1
Host: 47.102.84.170
User-Agent: curl/8.4.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.195.79
X-Forwarded-Host: whoami.tests.com
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: traefik-f4564c4f4-7swl5
X-Real-Ip: 192.168.195.79

可以看到 ingress 正常工作,但是从 whoami 输出的 X-Real-Ip 头可以看出 whoami 并没有获取到正确的客户端 IP,通过 X-Forwarded-For 头可以断定根因是 traefik 没有获取到客户端 IP 地址。有经验的同学立马就知道问题出在哪里:

$ k3s kubectl get svc/traefik -n kube-system -o=jsonpath='{.spec.externalTrafficPolicy}'
Cluster

问题就出在 traefik Service 的 externalTrafficPolicy 字段,关于这个字段的作用以及会如何影响客户端 IP 大家可以查看 k8s 官方文档 这里就不展开讨论了,解决方案是将 externalTrafficPolicy 字段修改为 Local。k3s 使用 Helm Controller 来管理 traefik 所以我们要在 /var/lib/rancher/k3s/server/manifests/traefik.yaml 文件进行修改:

# /var/lib/rancher/k3s/server/manifests/traefik.yaml
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: traefik-crd
  namespace: kube-system
spec:
  chart: https://%{KUBERNETES_API}%/static/charts/traefik-crd-25.0.2+up25.0.0.tgz
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: traefik
  namespace: kube-system
spec:
  chart: https://%{KUBERNETES_API}%/static/charts/traefik-25.0.2+up25.0.0.tgz
  set:
    global.systemDefaultRegistry: ""
  valuesContent: |-
    ...
    service:
      ipFamilyPolicy: "PreferDualStack"
      # 修改 externalTrafficPolicy 字段
      spec:
        externalTrafficPolicy: Local
    # 新增 nodeSelector 字段,将 traefik 部署在拥有公网 IP 的节点上
    nodeSelector:
      svccontroller.k3s.cattle.io/enablelb: 'true'

这里对 traefik Chart 的 Values 进行了两处修改:

  • service.spec 将 Service 的 externalTrafficPolicy 修改为 Local
  • nodeSelector 将 traefik 部署在集群负载均衡节点,因为 externalTrafficPolicy 字段设置为 Local 后外部流量进入集群后不会进行转发,云主机是外部流量的入口,如果云主机上没有 traefik Pod 那么就无法处理请求了,而 macmini 节点没有公网 IP 因此不会有外部流量流入,没有必要部署 traefik。

修改完毕后 Helm Controller 会自动应用更改,稍等片刻后再次访问 whoami 应用:

$ curl http://47.102.84.170/whoami
Hostname: whoami-564b668c57-rb689
IP: 127.0.0.1
IP: ::1
IP: 10.42.0.9
IP: fe80::f47f:bfff:fedf:88f0
RemoteAddr: 10.42.1.5:53872
GET /whoami HTTP/1.1
Host: 47.102.84.170
User-Agent: curl/8.4.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 119.131.118.130
X-Forwarded-Host: 47.102.84.170
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: traefik-7c99f95995-dj6q7
X-Real-Ip: 119.131.118.130

可以看到修改后 traefik 获取到了真实的客户端 IP 地址并传递给了 whoami,至此就可以通过公网 IP 访问集群以及 macmini 上运行的应用了!

<think>嗯,用户问的是外部普罗米修斯如何监控k8s集群。首先,我得确定用户对普罗米修斯和k8s的了解程度。可能他们已经有k8s环境,想要用外部的普罗米修斯实例来监控,而不是使用集群内部的。这时候需要考虑两者的网络连接、服务发现、权限等问题。 用户可能是运维或者开发人员,负责维护集群的监控系统。他们可能已经尝试过内部部署,但出于资源隔离、集中管理或者避免单点故障的原因,想要将普罗米修斯部署在集群外部。这时候需要确保外部普罗米修斯能够访问k8s的API,以及各个组件的metrics端点。 接下来,我得回忆外部普罗米修斯监控k8s的常见方法。通常有两种方式:通过k8s API自动发现,或者手动配置目标。自动发现更灵活,特别是动态环境,但需要配置RBAC和正确的服务账户。手动配置可能适合静态环境,但不够灵活。 然后,需要具体步骤。首先,确保k8s组件(如kubelet、apiserver、coredns等)已经暴露了metrics端口。接着,配置普罗米修斯的服务发现,比如使用kubernetes_sd_configs,配置相应的角色,如node、endpoints、pod等。然后,可能需要relabel_configs来筛选和重写标签,确保目标正确。 权限方面,普罗米修斯需要能够访问k8s API,所以需要创建ServiceAccount,ClusterRole和ClusterRoleBinding。这部分需要详细说明,比如创建具有get、list、watch权限的ClusterRole,绑定到ServiceAccount,然后外部普罗米修斯使用对应的token进行认证。 另外,要考虑网络访问,外部普罗米修斯需要能访问k8s节点的IP和metrics端口。如果是托管集群,可能需要配置安全组或防火墙规则,允许外部访问。如果是自建集群,可能需要配置NodePort或者LoadBalancer服务来暴露metrics端点。 存储和配置管理方面,外部普罗米修斯的配置文件中需要定义scrape_configs,使用kubernetes_sd_configs,正确配置TLS和认证信息。可能需要使用Bearer Token文件,比如挂载ServiceAccount的token。 还有可能需要监控自定义的应用,这时候需要确保应用的Pod暴露了metrics端点,且有正确的annotations或labels供普罗米修斯自动发现。例如,使用prometheus.io/scrape: "true"这样的注解。 最后,可能需要集成Alertmanager和Grafana,进行告警和可视化。这部分用户可能已经了解,但如果有需要的话,可以简要提及。 总结下来,回答需要涵盖服务发现配置、权限设置、网络访问、配置文件和实际监控对象的处理步骤。同时,要提醒用户注意安全,比如使用RBAC最小权限,加密通信等。外部</think> Prometheus 监控 Kubernetes 集群的核心是通过服务发现机制动态获取监控目标,配合权限配置实现跨集群的数据采集。以下是具体实现步骤和原理: --- ### **1. 前置条件** - **Metrics 端点暴露**:确保 Kubernetes 组件(kubelet、API Server 等)和应用已暴露 Prometheus 格式的 metrics 接口(如 `:10250/metrics`)。 - **网络连通性**:外部 Prometheus 需能访问 Kubernetes 节点的 IP 及 Service 网络(可能需要打通防火墙/NAT)。 --- ### **2. 服务发现配置** 在 Prometheus 的 `prometheus.yml` 中配置 Kubernetes 服务发现模块: ```yaml scrape_configs: - job_name: 'kubernetes-nodes' kubernetes_sd_configs: - role: node # 发现集群所有节点 scheme: https tls_config: insecure_skip_verify: true # 生产环境应配置证书 bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__address__] regex: '(.*):10250' replacement: '${1}:10255' target_label: __address__ ``` --- ### **3. 权限配置(RBAC)** 为 Prometheus 创建 ServiceAccount 绑定权限: ```yaml # prometheus-rbac.yaml apiVersion: v1 kind: ServiceAccount metadata: name: prometheus-external namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus-external rules: - apiGroups: [""] resources: ["nodes", "services", "pods", "endpoints"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus-external roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus-external subjects: - kind: ServiceAccount name: prometheus-external namespace: monitoring ``` 执行 `kubectl apply -f prometheus-rbac.yaml` 应用配置。 --- ### **4. 访问凭证配置** 外部 Prometheus 需使用 ServiceAccount 的 Token 访问 Kubernetes API: ```yaml # 获取 Token(在集群内部执行) kubectl -n monitoring get secret $(kubectl -n monitoring get sa prometheus-external -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 -d > token ``` 将生成的 `token` 文件挂载到外部 Prometheus 容器中,在配置中引用。 --- ### **5. 动态目标 Relabeling** 通过 `relabel_configs` 重写标签,筛选有效监控目标: ```yaml relabel_configs: # 仅抓取带注解 prometheus.io/scrape=true 的 Pod - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true # 从注解中获取自定义 metrics 路径和端口 - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 target_label: __address__ ``` --- ### **6. 高级监控对象** - **API Server**:直接监控 `kubernetes.default.svc:443`。 - **CoreDNS**:通过 Endpoints 发现 `kube-dns` 服务。 - **Node Exporter**:部署 DaemonSet 在每个节点暴露 `9100` 端口。 --- ### **7. 网络访问模式** | 模式 | 配置方式 | 适用场景 | |--|-----------------------------------|----------------------------| | Direct NodeIP | 直接访问节点 IP + 端口 | 简单环境,节点 IP 可直连 | | NodePort | 通过 NodePort Service 暴露 metrics | 托管集群(如 EKS、GKE) | | Ingress | 通过 Ingress 统一入口 | 需要 HTTPS/域名管理的场景 | --- ### **8. 安全增强** - **TLS 加密**:配置 `tls_config` 使用合法证书。 - **网络策略**:限制 Prometheus 源 IP 访问权限。 - **审计日志**:监控 Kubernetes API 的异常访问。 --- ### **9. 验证调试** - 访问 Prometheus 的 `Targets` 页面检查状态。 - 使用 `kubectl get --raw /api/v1/nodes/<node>/proxy/metrics` 手动验证端点。 - 查看 Prometheus 日志排查连接问题。 --- 通过以上步骤,外部 Prometheus 可动态监控 Kubernetes 集群的核心组件、节点资源及业务应用,实现跨集群的统一监控体系。对于混合或多集群场景,可结合 Thanos 或 VictoriaMetrics 实现数据联邦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值