通过 Gateway API 和 mTLS 在 Kubernetes 中暴露 OTel Collector
本篇博文旨在演示如何使用 Kubernetes Gateway API 和 双向 TLS (mTLS) 进行身份验证和加密,将运行在 Kubernetes 中的 OpenTelemetry (OTel) Collector 安全地暴露给外部世界。
随着可观测性在现代分布式系统中变得越来越重要,通过在一个或多个 Kubernetes 集群中部署的 OTel Collector 来集中遥测数据已成为一种常见做法。通常,运行在 Kubernetes 集群外部的服务或代理需要将数据发送到这些 Collector。公开内部服务需要仔细考虑安全性和标准化。这就是 Kubernetes Gateway API 和 mTLS 发挥作用的地方。
通常,这种设置在您拥有集群外部的应用程序或工作负载,并且需要收集它们的遥测数据时很有用。一些例子:
- 混合云/本地环境:运行在传统数据中心、不同云环境或 Kubernetes 集群外部的应用程序或服务器需要将其指标、跟踪或日志转发到您的集中式可观测性解决方案。
- 多集群遥测聚合:在可能运行在多个 Kubernetes 集群的设置中,您可能指定一个集群来托管主要的 OTel Collector 部署。其他“分支”集群中的 Collector 将充当客户端,通过其外部端点将数据导出到此中央 Collector。例如,在多集群服务网格设置中,工作负载可能需要进行尾部采样。在这种情况下,配置了 尾部采样处理器并通过 Gateway 公开的中央 Collector 会聚合所有集群的 Span 以做出采样决策。
- 边缘计算/物联网:部署在边缘的设备通常需要将其操作数据发送回中央平台。
- 无服务器函数/PaaS:运行在无服务器平台(如 AWS Lambda、Google Cloud Functions)或集群外部的平台即服务产品中的应用程序可能需要导出 OTLP 数据。
- 外部监控代理:需要连接到集群内共享 Collector 的第三方代理或本地运行的开发实例。
- 客户端监控:来自浏览器和移动应用程序等外部客户端的遥测数据。虽然 mTLS 可能不用于从浏览器导出遥测数据,但最终必须提供 Collector。
先决条件
在开始之前,请确保您拥有以下内容:
- Kubernetes 集群:Minikube、Kind、Docker Desktop、Gardener 或云提供商的托管 Kubernetes 服务均可。
kubectl:已配置以与您的集群进行交互。helm:已配置以安装 Helm Chart。- Gateway API 实现:在本例中,我们将使用 Istio。其他实现,如 Contour、NGINX Gateway Fabric 等,也可以使用,可能只需进行少量配置调整。
openssl:用于生成证书的 OpenSSL CLI。
由于 Gateway API 的某些部分仍处于 Alpha/Beta 阶段,因此对特定方面的支持可能有所不同或默认未启用。请参考您正在使用的 Gateway 实现的文档。例如,在撰写本文时,如果您使用的是 Istio,请确保在安装期间启用了 PILOT_ENABLE_ALPHA_GATEWAY_API。
什么是 Kubernetes Gateway API?
Kubernetes Gateway API 是旧版 Ingress API 的演进。它提供了一种更具表现力、面向角色且灵活的方式来管理集群的入站流量。 GAMMA 项目定义了 Gateway API 的实现。引入它是出于以下原因:
- Ingress 的局限性:Ingress API 虽然有用,但已变得有限。它缺乏跨实现的标准化,并且路由功能也因不同实现而异。
- 角色分离:Gateway API 分离了关注点
GatewayClass:定义了一种负载均衡器类型(例如,Istio、GKE LB)。由基础设施管理员管理。Gateway:表示一个请求特定GatewayClass的负载均衡器实例。定义侦听器(端口、协议、TLS)。由集群操作员管理。它们也可以跨命名空间共享。HTTPRoute、GRPCRoute、TCPRoute、TLSRoute等:定义应用级路由规则,并连接到Gateway。由应用程序开发人员/所有者管理。
- 可移植性:标准化的 API 定义旨在提高跨不同底层网关/服务网格实现的便携性。
- 表现力:原生支持高级功能,如请求头操作、流量拆分、mTLS 配置、gRPC 路由等。
总而言之,与传统的 Ingress API 相比,Gateway API 为管理南北向流量提供了更健壮和标准化的模型。
mTLS - 简要介绍
双向 TLS (mTLS) 扩展了标准 TLS,要求客户端和服务器都必须提供和验证证书以进行相互身份验证。
标准 TLS(如网站上的 HTTPS)会向客户端验证服务器的身份。双向 TLS (mTLS) 更进一步:
- 客户端验证服务器的身份(使用服务器的证书)。
- 服务器也验证客户端的身份(使用客户端的证书)。
双向 TLS 很重要,因为它提供了强大的身份验证、确保端到端加密,并符合零信任安全原则:
确保只有受信任的客户端(拥有由受信任证书颁发机构 (CA) 签名的有效证书)才能连接到您公开的服务。
经过身份验证的客户端和服务器(如 Gateway)之间的所有通信都经过加密。
它通过要求双方进行验证来支持零信任模型,从不默认假定信任。
场景
以下是我们将在 Kubernetes 中公开 OTel Collector 部署所遵循的步骤。
- 在 Kubernetes 中部署一个 OTel Collector,并配置一个简单的 OTLP/gRPC 接收器。
- 生成一个自签名根 CA、一个服务器证书(用于 Gateway)和一个客户端证书(用于外部客户端)。
- 配置一个 Kubernetes
Gateway资源,使其监听特定端口、终止 TLS 并要求提供客户端证书(mTLS)。 - 配置一个
GRPCRoute,将来自Gateway的传入 gRPC 流量路由到内部 OTel Collector 服务。 - 配置一个外部客户端(另一个 OTel Collector),使用客户端证书并通过 OTLP/gRPC 导出数据,并信任根 CA。

设置
步骤 1:安装 Gateway API CRD。
默认情况下,Kubernetes 集群中不会安装 Gateway API。在撰写此博文时,最新版本是 v1.2。如果 Gateway API CRD 不存在,请安装它:
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
#To support GRPCRoute as of now. Would not be required once GRPCRoute CRD becomes GA.
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.2.0" | kubectl apply -f -
步骤 2:生成自签名证书
为了在客户端和服务器之间设置 mTLS,我们需要一套证书。在此演示场景中,我们将使用自签名证书。在此演示中,我们将使用相同的 CA 来签名客户端和服务器。我们将使用 openssl 来创建证书。有关配置详情,请参阅 openssl 文档。
# Variables (adjust domain/names as needed)
export ROOT_CA_SUBJ="/CN=MyDemoRootCA"
# Use a relevant CN/SAN for the server/gateway. If clients connect via IP, include it.
# For DNS, use the hostname clients will use (e.g., otel.example.com)
export SERVER_HOSTNAME="otel-gateway.example.com"
export SERVER_SUBJ="/CN=${SERVER_HOSTNAME}"
export CLIENT_SUBJ="/CN=external-otel-client"
# 1. Create Root CA certificate and key
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj "${ROOT_CA_SUBJ}" -keyout rootCA.key -out rootCA.crt
# 2. Create Server CSR & sign with Root CA
openssl req -newkey rsa:4096 -nodes -keyout server.key -out server.csr -subj "${SERVER_SUBJ}" \
-addext "subjectAltName = DNS:${SERVER_HOSTNAME}" # Add SAN for hostname validation
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 \
-extfile <(printf "subjectAltName=DNS:${SERVER_HOSTNAME}") # Ensure SAN is in the final cert
# 3. Create Client CSR and sign with Root CA
openssl req -newkey rsa:4096 -nodes -keyout client.key -out client.csr -subj "${CLIENT_SUBJ}"
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 365 -sha256
对于生产环境,切勿对外面向公众互联网公开的端点使用自签名证书。请使用受信任的公共 CA(例如,通过 cert-manager 的 Let's Encrypt)或托管的内部 PKI 系统颁发的证书。获取证书的过程会有所不同,但在 Kubernetes 中使用它们的概念是相似的。确保服务器证书的通用名 (CN) 或主题备用名 (SAN) 与客户端连接时使用的 主机名匹配。
步骤 3:创建 otel-collector 命名空间
我们将在给定的命名空间中部署 OTel Collector 设置。之后,根据您使用的 Gateway/服务网格实现,您可能需要相应地配置命名空间。例如,使用 Istio 时,我们可以创建带有 istio-injection:enabled 的命名空间,以便 Istio 自动处理命名空间中部署的工作负载。
namespace.yaml:
# OpenTelemetry Collector Namespace
---
apiVersion: v1
kind: Namespace
metadata:
name: otel-collector
labels:
istio-injection: enabled #Relavent only if you are using Istio.
应用此配置:
kubectl apply -f namespace.yaml
步骤 4:部署 OTel Collector(服务器)
让我们创建一个简单的 OTel Collector 部署和服务。在给定的配置中,OTel Collector 将打印传入的遥测数据。此配置将根据您的用例而改变。
otel-collector-server.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-conf
namespace: otel-collector # Deploy collector in otel-collector namespace
data:
config.yaml: |
receivers:
otlp:
protocols:
grpc:
# Note: No TLS config here. TLS terminates at the Gateway.
endpoint: 0.0.0.0:4317
processors:
batch:
exporters:
# For demo purposes, log to stdout
debug:
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector-server
namespace: otel-collector # Deploy collector in otel-collector namespace
spec:
replicas: 1
selector:
matchLabels:
app: otel-collector-server
template:
metadata:
labels:
app: otel-collector-server
spec:
containers:
- name: otel-collector
# Use a specific, recent version tag in production
image: otel/opentelemetry-collector:latest
ports:
- containerPort: 4317 # OTLP gRPC
name: otlp-grpc
volumeMounts:
- name: otel-collector-config-vol
mountPath: /etc/otelcol
volumes:
- name: otel-collector-config-vol
configMap:
name: otel-collector-conf
---
apiVersion: v1
kind: Service
metadata:
name: otel-collector-server-svc
namespace: otel-collector
spec:
selector:
app: otel-collector-server
ports:
- name: grpc
protocol: TCP
port: 4317
targetPort: 4317
应用此配置:
kubectl apply -f otel-collector-server.yaml
步骤 5:将证书存储为 Kubernetes Secrets
Gateway 需要访问服务器证书/密钥以及 CA 证书来验证客户端。
使用服务器证书和密钥创建一个 Secret。我们还将存储用于签名客户端的 CA 证书。在此演示中,为了方便起见,我们将其放在 otel-collector 命名空间中。
kubectl create -n otel-collector secret generic otel-gateway-server-cert --from-file=tls.crt=server.crt --from-file=tls.key=server.key --from-file=ca.crt=rootCA.crt
步骤 6:配置 Kubernetes Gateway API 资源
我们需要两个资源:Gateway 和 GRPCRoute。为简单起见,在此演示中我们将资源保留在同一个 otel-collector 命名空间中。这会根据您的部署设置而改变。
otel-gateway-resources.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: otel-gateway
namespace: otel-collector
spec:
gatewayClassName: istio
listeners:
- name: otlp-grpc-mtls
port: 4317
protocol: HTTPS
hostname: 'otel-gateway.example.com'
tls:
mode: Terminate
certificateRefs:
- group: '' # Core API group for Secrets
kind: Secret
name: otel-gateway-server-cert # The certificates that were uploaded as secrets in the provious step.
options:
# This structure might vary depending on the implementation of your Gateway/Service Mesh
# Please refer documentation of the implementation installed.
# For Istio, we set tls termination mode here
gateway.istio.io/tls-terminate-mode: MUTUAL
---
# GRPCRoute is generally preferred for OTLP/gRPC
# Ensure GRPCRoute CRD (v1alpha2 or v1) is installed
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: otel-collector-grpcroute
# Namespace where the backend service resides
namespace: otel-collector
spec:
# Link this route to our Gateway in the istio-system namespace
parentRefs:
- name: otel-gateway
namespace: otel-collector # Namespace of the Gateway resource
sectionName: otlp-grpc-mtls # Attach to the specific listener by name
# Define routing rules for gRPC traffic
rules:
- backendRefs:
- name: otel-collector-server-svc # Name of your internal OTel service
namespace: otel-collector # Namespace of the backend service
port: 4317 # Target port on the service
在
GatewayCRD 中,我们配置gatewayclass和listeners。在这种情况下,我们配置一个listener,包含必要的port和hostname。我们还配置了在此处终止的tls。我们使用上传为 Secret 的证书。options块用于配置任何特定于实现的参数。在
GRPCRoute中,我们选择一个 Gateway 和特定的listener。我们还配置了路由将请求转发到的后端。在这种情况下,是otel-collector-server-svc。
我们在 Gateway 中使用 options 来进行 mTLS 的特定于实现的配置。目前,Gateway API 没有明确的 双向 TLS 模式。请参阅最新的 Gateway API 文档以获取更新。
应用 Gateway 配置:
kubectl apply -f otel-gateway.yaml
此时,Istio(或其他)Gateway 应配置为监听端口 4317(通常通过 LoadBalancer Service 暴露),使用指定的服务器证书和客户端 CA 要求 mTLS,并将有效的 gRPC 流量路由到 otel-collector-server-svc。
要获取 Gateway 的详细信息,您可以运行以下命令:
# To get the Gateway hostname/IP
kubectl -n otel-collector get gateway otel-gateway -o jsonpath='{.status.addresses[0].value}'
# To get the port
kubectl -n otel-collector get gtw otel-gateway -o jsonpath='{.spec.listeners[?(@.name=="otlp-grpc-mtls")].port}'
您还可以查看为您的 Gateway 创建的 Kubernetes 服务。
kubectl -n otel-collector get svc
步骤 7:配置外部 OTel Collector(客户端)
为了测试该设置,请配置一个位于集群外部的 OTel Collector,使其使用 mTLS 向 Gateway 的外部端点发送数据。
在此演示中,客户端(OTel Collector)通过 Docker 在本地运行。
以下示例 otel-client-config.yaml 是一个简单的配置,用于抓取 CPU 和内存指标并将它们发送到服务器:
receivers:
# Example: Receiver generating some data, e.g., host metrics
hostmetrics:
collection_interval: 10s
scrapers:
cpu:
memory:
# Add other scrapers as needed
processors:
batch:
exporters:
otlp/grpc:
# IMPORTANT: Point to the Gateway's external IP/hostname and port
# Replace <GATEWAY_EXTERNAL_IP_OR_HOSTNAME> with the actual address
# It should match the hostname/SAN in the server certificate if using hostname
# Use the hostname 'otel-gateway.example.com' if you have DNS configured at the gateway.
endpoint: <GATEWAY_EXTERNAL_IP_OR_HOSTNAME>:4317
tls:
# We MUST enable TLS configuration for the client for mTLS
insecure: false # Ensure server certificate is validated against the CA
# Path to the CA certificate file to verify the server
ca_file: /etc/cert/rootCA.crt
# Path to the client's certificate file
cert_file: /etc/cert/client.crt
# Path to the client's private key file
key_file: /etc/cert/client.key
# Optional but recommended: Specify the server name for validation
# Must match the CN or SAN in the server certificate (server.crt)
# This is required if DNS is not configured at the Gateway and endpoint does not match the Gateway Hostname
server_name_override: otel-gateway.example.com
service:
pipelines:
# Example sending host metrics
metrics:
receivers: [hostmetrics]
processors: [batch]
exporters: [otlp/grpc]
# Add additional traces/logs pipelines if the client generates them
要运行客户端:
将
<GATEWAY_EXTERNAL_IP_OR_HOSTNAME>替换为您 Istio Gateway LoadBalancer 服务的实际外部 IP 地址或 DNS 名称。如果使用主机名(otel-gateway.example.com),请确保您的客户端机器能够将此主机名解析到正确的 IP(例如,通过/etc/hosts进行测试,或实际的 DNS)。如果
endpoint与服务器证书中的 SAN/CN 值不同,请使用server_name_override。将生成的
rootCA.crt、client.crt和client.key文件放在客户端 Collector 可访问的目录中。在此演示中,我们将其放在certs文件夹中。运行客户端 Collector(根据需要调整路径和镜像标签):
# Running command assuming certificates and config are in the current directory. docker run --rm -v $(pwd)/certs:/etc/cert/ \ -v $(pwd)/otel-client-config.yaml:/etc/otelcol-contrib/config.yaml \ otel/opentelemetry-collector-contrib:0.119.0
我们通过挂载 otel-client-config.yaml 和包含证书的 certs 文件夹来运行 opentelemetry-collector-contrib 的容器。
步骤 8:测试连接
检查服务器日志:查看 Kubernetes 中
otel-collector-serverpod 的日志。如果配置了debug导出器,您应该会看到指示它正在接收数据批次的条目。kubectl logs -n otel-collector -l app=otel-collector-server -f检查客户端日志:查看外部客户端 Collector 的日志(例如,Docker 容器输出)。您应该会看到诸如
Everything is ready. Begin running and processing data.之类的消息。任何连接错误消息(例如,"certificate signed by unknown authority"、"bad certificate")或connection refused错误都表示存在问题。请检查:- Gateway IP/主机名可达性。
- 防火墙规则。
- 客户端使用的正确证书(
ca_file、cert_file、key_file)。 - 匹配服务器证书 SAN/CN 的正确
server_name_override。 - Gateway 上的正确 mTLS 配置(包括客户端 CA 验证)。
- Gateway 控制器日志(例如,
istio-system中的istio-ingressgatewaypod 日志),以排查 TLS 错误。
测试失败案例
- 尝试不在客户端的
otlp/grpc导出器配置中包含tls:部分的情况下运行客户端。连接应被 Gateway 拒绝(可能是 TLS 握手失败或连接重置)。 - 尝试在客户端配置中注释掉
ca_file、cert_file或key_file。连接应失败。 - 如果您有另一个由不同 CA 签名的证书,请尝试将其用作客户端证书。Gateway 应在 mTLS 握手期间拒绝它,因为它不是由受信任的 CA 签名的。
- 尝试不在客户端的
注意事项
在此演练中,某些步骤以特定方式进行,以便于运行和理解配置和场景。在生产环境中配置此设置时,必须注意这些问题:
自签名证书在生产环境中不应使用。此外,用于客户端的CA证书通常也与用于签名服务器证书的证书不同。- Kubernetes Gateway API 正在不断发展,越来越多的功能被添加到规范中。其中许多功能现已处于 Alpha/Beta 阶段,很快将正式可用,例如
GRPCRoute。请参阅最新的 Kubernetes Gateway API 文档。 - Kubernetes Gateway API 旨在使配置尽可能具有可移植性和与实现无关。理想情况下,一旦规范成熟和发展,就会是这种情况。在此之前,配置的某些方面在不同实现之间会存在细微差别。例如,mTLS 在 Gateway 中的配置方式现在是这样的。
- 在生产环境中运行时,您可能希望使用
spec的infrastructure块来配置特定于基础设施提供商的参数,例如DNS。 - 生产环境中的设置将具有端到端加密通信。例如,在使用 Istio 时,集群中由 Istio 管理的命名空间中运行的所有组件都可以强制彼此通信。这是通过 PeerAuthentication 实现的。其他服务网格实现也会有类似的概念。
- 在处理多个命名空间中的路由和网关时,您可能需要引用后端
services等资源,以及其他命名空间中的配置。有关详细信息,请参阅 Gateway ReferenceGrant。
其他 Gateway 实现
虽然我们使用了 Istio(gatewayClassName: istio),但 Gateway API 的核心优势在于其标准化潜力。如果您使用的是 Contour、NGINX Gateway Fabric、HAPROXY 等,Gateway 和 GRPCRoute 资源定义在理想情况下会非常相似。主要区别可能在于:
gatewayClassName的特定值。- 在如何配置特定于实现的特性或选项方面存在细微差别(例如,在
options结构中指定客户端配置的确切语法)。 - 底层 Gateway 控制器/代理的部署、管理和公开方式(例如,LoadBalancer 服务的名称和命名空间)。
请始终查阅您选择的具体 Gateway API 实现的文档,特别是关于 mTLS 配置的详细信息。
结论
Kubernetes Gateway API 比旧版 Ingress API 提供了重大改进,它提供了一种更强大、可移植、标准化的方法。它是一种更灵活、面向角色的入站流量管理方式。
通过将 Gateway API 与双向 TLS (mTLS) 相结合,您可以安全地公开 OpenTelemetry Collector 等内部服务,确保强大的客户端身份验证和加密通信。