背景与目标
本文基于 3 节点裸金属 K8s 集群(ubuntu-111/112/113),介绍如何部署 Traefik 作为 Ingress Controller,并通过一个 Python HTTP 服务验证整个链路的可用性。
在 3 节点 K8s 集群中部署 Traefik Ingress Controller 的方案。通过节点标签与亲和性配置实现 Traefik 在特定节点的调度,启用 hostNetwork 模式直接占用端口。配合 HAProxy 进行多节点代理与健康检查,保障高可用。最后部署 Python HTTP 服务并通过 Traefik Ingress 暴露,验证了从负载均衡到应用服务的完整链路,对比了 hostNetwork 与 NodePort 模式的差异。

本文基于 3 节点裸金属 K8s 集群(ubuntu-111/112/113),介绍如何部署 Traefik 作为 Ingress Controller,并通过一个 Python HTTP 服务验证整个链路的可用性。
给 ubuntu-111、ubuntu-112 都打上 traefik-work=true 标签,作为 Traefik 调度的目标节点:
# 给 ubuntu-111 打标签
kubectl label nodes ubuntu-111 traefik-work=true
# 给 ubuntu-112 打标签
kubectl label nodes ubuntu-112 traefik-work=true
# 验证标签是否添加成功
kubectl get nodes ubuntu-111 ubuntu-112 --show-labels | grep traefik-work
在 Traefik 的 values.yaml 中,配置节点亲和性匹配两个打标签的节点,同时设置 Pod 反亲和性避免同一节点运行多个 Traefik 实例:
affinity:
# 节点亲和性:匹配 traefik-work=true 的节点(ubuntu-111/112)
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: traefik-work
operator: In
values:
- "true"
# Pod 反亲和性:避免同一节点运行多个 Traefik Pod
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: traefik
topologyKey: kubernetes.io/hostname
hostNetwork: true
deployment:
replicas: 2
pod:
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
runAsUser: 0
ports:
web:
port: 80
hostPort: 80
websecure:
port: 443
hostPort: 443
将上述配置写入 traefik-values.yaml,执行以下命令完成部署:
# 添加 Traefik Helm 仓库
helm repo add traefik https://traefik.github.io/charts
helm repo update
# 部署 Traefik(指定 Chart 版本,使用自定义配置)
helm install traefik traefik/traefik \
--namespace kube-system \
--create-namespace \
--version 39.1.0-ea.2 \
-f traefik-values.yaml
# 验证 Traefik Pod 调度结果(应分别运行在 ubuntu-111/112)
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik -o wide
修改 HAProxy 配置,代理两个 Traefik 节点的 80/443 端口,并增加 TCP 层健康检查(验证 Traefik 服务可用性):
#!/bin/bash
set -euo pipefail
# 基础配置
HAPROXY_NODE_IP="192.168.56.102"
MASTER_NODES=("192.168.56.111" "192.168.56.112" "192.168.56.113")
K8S_API_PORT=6443
HAPROXY_PORT=6443
# Traefik 配置(代理 ubuntu-111/112 的 80/443 端口)
TRAEFIK_NODES=("192.168.56.111" "192.168.56.112")
TRAEFIK_HTTP_PORT=80
TRAEFIK_HTTPS_PORT=443
# 安装 haproxy
apt update -y && apt install -y haproxy net-tools || true
mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak$(date +%Y%m%d%H%M%S) || true
# 生成 haproxy.cfg
cat > /etc/haproxy/haproxy.cfg <<EOF
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy group haproxy daemon
maxconn 2000
defaults
log global
mode tcp
option tcplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
# K8s apiserver 代理(原有配置)
frontend k8s-api-frontend
bind ${HAPROXY_NODE_IP}:${HAPROXY_PORT}
default_backend k8s-api-backend
backend k8s-api-backend
mode tcp
balance roundrobin
option tcp-check
EOF
# 添加 master 节点到 apiserver backend
for master in "${MASTER_NODES[@]}"; do
cat >> /etc/haproxy/haproxy.cfg <<EOF
server master-${master//./-}${master}:${K8S_API_PORT} check inter 2000 fall 3 rise 2
EOF
done
# Traefik HTTP 代理(多节点 + 健康检查)
cat >> /etc/haproxy/haproxy.cfg <<EOF
frontend traefik-http-frontend
bind ${HAPROXY_NODE_IP}:${TRAEFIK_HTTP_PORT}
default_backend traefik-http-backend
backend traefik-http-backend
mode tcp
balance roundrobin
option tcp-check
tcp-check connect port ${TRAEFIK_HTTP_PORT}
tcp-check send GET /ping HTTP/1.1\r\nHost: localhost\r\n\r\n
tcp-check expect string 200 OK
EOF
# 添加两个 Traefik 节点到 HTTP backend(带健康检查)
for node in "${TRAEFIK_NODES[@]}"; do
cat >> /etc/haproxy/haproxy.cfg <<EOF
server traefik-node-${node//./-}-http ${node}:${TRAEFIK_HTTP_PORT} check inter 2000 fall 3 rise 2 timeout connect 3s
EOF
done
# Traefik HTTPS 代理(多节点 + 健康检查)
cat >> /etc/haproxy/haproxy.cfg <<EOF
frontend traefik-https-frontend
bind ${HAPROXY_NODE_IP}:${TRAEFIK_HTTPS_PORT}
default_backend traefik-https-backend
backend traefik-https-backend
mode tcp
balance roundrobin
option tcp-check
tcp-check connect port ${TRAEFIK_HTTPS_PORT}
EOF
# 添加两个 Traefik 节点到 HTTPS backend(带健康检查)
for node in "${TRAEFIK_NODES[@]}"; do
cat >> /etc/haproxy/haproxy.cfg <<EOF
server traefik-node-${node//./-}-https ${node}:${TRAEFIK_HTTPS_PORT} check inter 2000 fall 3 rise 2 timeout connect 3s
EOF
done
# 重启 haproxy 并验证
systemctl daemon-reload
systemctl enable --now haproxy
systemctl restart haproxy
# 验证 HAProxy 状态
if systemctl is-active --quiet haproxy; then
echo -e "\033[32mHAProxy 部署成功:\"
echo -e " - K8s API: ${HAPROXY_NODE_IP}:${HAPROXY_PORT}"
echo -e " - Traefik HTTP: ${HAPROXY_NODE_IP}:${TRAEFIK_HTTP_PORT} (代理到${TRAEFIK_NODES[*]})"
echo -e " - Traefik HTTPS: ${HAPROXY_NODE_IP}:${TRAEFIK_HTTPS_PORT} (代理到${TRAEFIK_NODES[*]})\033[0m"
else
echo -e "\033[31mHAProxy 启动失败\033[0m"
systemctl status haproxy --no-pager
exit 1
fi
option tcp-check:启用 TCP 层健康检查,验证后端端口是否存活;tcp-check send GET /ping HTTP/1.1\r\nHost: localhost\r\n\r\n:向 Traefik 的 /ping 接口发送请求(Traefik 内置健康检查接口);tcp-check expect string 200 OK:期望返回 200 状态码,确认 Traefik 服务正常;check inter 2000 fall 3 rise 2:每 2 秒检查一次,连续 3 次失败标记为不可用,连续 2 次成功恢复可用;timeout connect 3s:连接超时时间 3 秒,避免健康检查阻塞。# 查看 HAProxy 后端状态
echo "show stat" | socat /run/haproxy/admin.sock stdio | grep traefik
# 查看 HAProxy 日志(验证健康检查结果)
tail -f /var/log/haproxy.log | grep traefik
| 特性 | hostNetwork 模式 | NodePort 模式 |
|---|---|---|
| 网络模型 | Pod 直接使用节点的网络命名空间,监听节点 IP 和端口。 | Service 在节点上打开一个端口(NodePort),流量通过 kube-proxy 转发到 Pod。 |
| 端口占用 | 直接占用节点的 80/443 等标准端口,同一节点上的多个 Pod 不能监听同一端口。 | 使用 30000-32767 范围内的端口,避免与主机服务冲突。 |
| 性能 | 性能最优,无额外的 NAT 和转发开销。 | 存在 kube-proxy 的转发开销,性能略低。 |
| 隔离性 | 差,Pod 可以看到节点上的所有网络接口和连接。 | 好,Pod 的网络栈与节点隔离。 |
| 高可用 | 需要通过 Pod 反亲和性确保每个节点只运行一个 Pod,配合 HAProxy 健康检查实现高可用。 | 自动负载均衡到所有节点的 NodePort,高可用更简单,但依赖 kube-proxy。 |
| 适用场景 | 对性能要求极高的 Ingress Controller、网络插件等。 | 通用的服务暴露方式,适合大多数应用。 |
from http.server import BaseHTTPRequestHandler, HTTPServer
import os, socket, json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.end_headers()
# 获取容器与 Pod 元数据
pod_name = os.getenv("POD_NAME", socket.gethostname())
node_name = os.getenv("NODE_NAME", "unknown-node")
app_info = {
"app": "python-http-server",
"container": os.getenv("CONTAINER_NAME", "python-http-server")
}
response = f"""
<h1>✅ Python HTTP Server</h1>
<p>Pod: {pod_name}</p>
<p>Node: {node_name}</p>
<p>App Info: {json.dumps(app_info, indent=2)}</p>
"""
self.wfile.write(response.encode('utf-8'))
def run(server_class=HTTPServer, handler_class=Handler, port=8080):
server_address = ('0.0.0.0', port)
httpd = server_class(server_address, handler_class)
print(f"🚀 Python HTTP 服务器启动:0.0.0.0:{port}")
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.server_close()
print("🛑 服务器已停止")
if __name__ == '__main__':
run()
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-http-server
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: python-http-server
template:
metadata:
labels:
app: python-http-server
spec:
containers:
- name: python-http-server
image: 192.168.56.102/library/python-http-server:v1
ports:
- containerPort: 8080
name: http
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: CONTAINER_NAME
value: "python-http-server"
---
apiVersion: v1
kind: Service
metadata:
name: python-http-server
namespace: default
spec:
selector:
app: python-http-server
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: python-http-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "traefik"
spec:
rules:
- host: python-http.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: python-http-server
port:
number: 80
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik -o wide
# 预期结果:两个 Traefik Pod 分别运行在 ubuntu-111、ubuntu-112
# 查看 Traefik 后端状态(STATUS 列为 UP 表示正常)
echo "show stat" | socat /run/haproxy/admin.sock stdio | grep traefik | awk '{print $1,$2,$18}'
# 配置本地 hosts
echo "192.168.56.102 python-http.example.com" >> /etc/hosts
# 多次访问验证负载均衡(会轮询到两个 Traefik 节点)
curl http://python-http.example.com
curl http://python-http.example.com
# 停止 ubuntu-111 上的 Traefik Pod
kubectl delete pod -n kube-system $(kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik -o name | grep ubuntu-111 | cut -d/ -f2)
# 查看 HAProxy 后端状态(ubuntu-111 节点会被标记为 DOWN,流量自动切换到 ubuntu-112)
echo "show stat" | socat /run/haproxy/admin.sock stdio | grep traefik
本次实践完成了 Traefik 在多节点(ubuntu-111/112)的部署,通过节点标签和亲和性实现精准调度,配合 HAProxy 的多节点代理和健康检查,保障了入口流量的高可用。对比 hostNetwork 和 NodePort 模式,清晰体现了不同网络模式的优劣,也验证了 Python HTTP 服务通过 Traefik Ingress 暴露的完整链路,为 K8s 流量管理的学习打下了坚实基础。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online