證Python HTTP服務(wù):TaoToken統(tǒng)一Key接入Ingress路由配置實(shí)戰(zhàn))
1. 為什么要在 K8s 里折騰 Traefik 和 Python HTTP 服務(wù)如果你正在學(xué) Kubernetes大概率會(huì)遇到這樣一個(gè)尷尬場(chǎng)景Pod 跑起來了Service 也建好了但集群外的人根本訪問不到。這時(shí)候你需要一個(gè) Ingress Controller 來當(dāng)門衛(wèi)而 Traefik 就是那個(gè)配置直觀、自帶 Dashboard、對(duì)新手相對(duì)友好的選擇。它能把外部請(qǐng)求按域名和路徑轉(zhuǎn)發(fā)到集群內(nèi)的 Service還能自動(dòng)感知 Pod 變化不用你手動(dòng)改配置。這篇要做的是一條完整鏈路在 K8s 集群里部署 Traefik 作為 Ingress Controller再跑一個(gè) Python HTTP 服務(wù)最后通過 Ingress 路由把請(qǐng)求打進(jìn)去用 curl 驗(yàn)證整條路真的通了。適合已經(jīng)會(huì)kubectl get pods、知道 Deployment 和 Service 是什么、但還沒親手配過 Ingress 的同學(xué)。熱詞里提到的 HAProxy 傳統(tǒng)方案我也會(huì)拿來對(duì)比——它做四層負(fù)載均衡很穩(wěn)但七層路由和動(dòng)態(tài)服務(wù)發(fā)現(xiàn)這塊Traefik 在 K8s 場(chǎng)景下省心不少。過程中我會(huì)用 TaoToken 的統(tǒng)一 Key 通道來輔助生成和校對(duì) YAML 配置這樣你不用在多個(gè) AI 工具之間來回切一個(gè) Key 就能把配置骨架、排錯(cuò)思路一起搞定。下面從環(huán)境準(zhǔn)備開始一步步來。2. TaoToken 前置準(zhǔn)備統(tǒng)一 Key 接入 AI 輔助配置在動(dòng)手寫 YAML 之前先把 AI 輔助這條線搭好。TaoToken 的作用是提供一個(gè)統(tǒng)一的 API 通道你拿一個(gè) Key 就能調(diào)用多種模型來幫你生成 Traefik 的 IngressRoute、檢查 Python 服務(wù)代碼、甚至解釋報(bào)錯(cuò)。對(duì)于這種配置密集型任務(wù)有個(gè)能隨時(shí)問的助手會(huì)快很多。第一步是拿到 API Key。打開官網(wǎng) https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注冊(cè)后在控制臺(tái)里創(chuàng)建 Key??刂婆_(tái)地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 進(jìn)去之后左側(cè)菜單能找到 API Keys 入口https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。創(chuàng)建完記得復(fù)制保存頁(yè)面刷新后就看不到了。拿到 Key 之后API 的基礎(chǔ)地址是 https://taotoken.net/api 注意這個(gè)地址不帶 UTM 參數(shù)直接填就行。如果你用的是 OpenAI 兼容的客戶端把 base_url 指向它Key 填進(jìn)去就能用。想先試試模型通不通可以去模型對(duì)話頁(yè)面 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 發(fā)一句話驗(yàn)證。注意Key 屬于敏感憑證不要寫進(jìn) YAML 提交到 Git也不要在文章或截圖里暴露完整字符串。建議用環(huán)境變量或者 K8s Secret 管理。如果你打算長(zhǎng)期用 AI 輔助寫 K8s 配置和代碼可以看看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更適合高頻的編碼和 Agent 場(chǎng)景。接入文檔在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到參數(shù)問題可以查。3. 可復(fù)制配置Traefik 部署與 Python 服務(wù)骨架3.1 集群與節(jié)點(diǎn)準(zhǔn)備假設(shè)你有一個(gè) 3 節(jié)點(diǎn)的集群節(jié)點(diǎn)名分別是 ubuntu-111、ubuntu-112、ubuntu-113。我們讓 Traefik 只跑在前兩個(gè)節(jié)點(diǎn)上用節(jié)點(diǎn)標(biāo)簽來控制調(diào)度。先給節(jié)點(diǎn)打標(biāo)簽kubectl label nodes ubuntu-111 traefik-worktrue kubectl label nodes ubuntu-112 traefik-worktrue kubectl get nodes ubuntu-111 ubuntu-112 --show-labels | grep traefik-work預(yù)期輸出里能看到兩個(gè)節(jié)點(diǎn)都帶上了traefik-worktrue。這一步的意義是后面用 nodeAffinity 把 Traefik 釘在這兩個(gè)節(jié)點(diǎn)避免它跑到 ubuntu-113 上。3.2 Traefik 的 values.yaml用 Helm 裝 Traefik 最省事。先加倉(cāng)庫(kù)helm repo add traefik https://traefik.github.io/charts helm repo update然后寫一個(gè)traefik-values.yaml核心是節(jié)點(diǎn)親和性、Pod 反親和性、hostNetwork 和端口綁定affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: traefik-work operator: In values: - true 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這里幾個(gè)點(diǎn)值得說清楚。nodeAffinity保證 Traefik 只落在打了標(biāo)簽的節(jié)點(diǎn)podAntiAffinity用topologyKey: kubernetes.io/hostname保證同一個(gè)節(jié)點(diǎn)上不會(huì)跑兩個(gè) Traefik 副本hostNetwork: true讓 Pod 直接用節(jié)點(diǎn)網(wǎng)絡(luò)配合hostPort把 80/443 綁到節(jié)點(diǎn)上。NET_BIND_SERVICE能力是為了允許綁定 1024 以下的端口。安裝命令helm install traefik traefik/traefik \ --namespace kube-system \ --create-namespace \ --version 39.1.0-ea.2 \ -f traefik-values.yaml裝完檢查調(diào)度結(jié)果kubectl get pods -n kube-system -l app.kubernetes.io/nametraefik -o wide你應(yīng)該看到兩個(gè) Pod分別落在 ubuntu-111 和 ubuntu-112 上。3.3 Python HTTP 服務(wù)代碼寫一個(gè)最簡(jiǎn)單的 HTTP 服務(wù)返回 Pod 名和節(jié)點(diǎn)名方便驗(yàn)證路由到底打到了哪個(gè)副本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; charsetutf-8) self.end_headers() 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 h1Python HTTP Server/h1 pPod: {pod_name}/p pNode: {node_name}/p pApp Info: {json.dumps(app_info, indent2)}/p self.wfile.write(response.encode(utf-8)) def run(port8080): server_address (0.0.0.0, port) httpd HTTPServer(server_address, Handler) print(fPython HTTP 服務(wù)啟動(dòng): 0.0.0.0:{port}) try: httpd.serve_forever() except KeyboardInterrupt: httpd.server_close() if __name__ __main__: run()打成鏡像推到你的私有倉(cāng)庫(kù)比如192.168.56.102/library/python-http-server:v1。3.4 Deployment、Service 與 IngressRouteTraefik 3.x 推薦用 IngressRoute 這種 CRD比原生 Ingress 表達(dá)力更強(qiáng)。先建 Deployment 和 ServiceapiVersion: 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然后是 IngressRoute這是 Traefik 自己的 CRDapiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: python-http-ingressroute namespace: default spec: entryPoints: - web routes: - match: Host(python-http.example.com) kind: Rule services: - name: python-http-server port: 80entryPoints: web對(duì)應(yīng) Traefik 的 80 端口入口match用 Host 規(guī)則匹配域名services指向剛才的 Service。應(yīng)用這些資源kubectl apply -f python-http-deploy.yaml kubectl apply -f python-http-ingressroute.yaml kubectl get pods -l apppython-http-server kubectl get ingressroute4. 驗(yàn)證請(qǐng)求curl 打通整條路由配置都上了現(xiàn)在驗(yàn)證。先確認(rèn) Traefik 的 Pod 狀態(tài)kubectl get pods -n kube-system -l app.kubernetes.io/nametraefik -o wide兩個(gè) Pod 都 Running且分布在 ubuntu-111 和 ubuntu-112。接著在本地配 hosts把域名指到 Traefik 所在節(jié)點(diǎn)假設(shè)你通過其中一個(gè)節(jié)點(diǎn)訪問echo 192.168.56.111 python-http.example.com | sudo tee -a /etc/hosts然后 curlcurl http://python-http.example.com預(yù)期輸出是一段 HTML里面能看到Pod:和Node:字段。多請(qǐng)求幾次你會(huì)發(fā)現(xiàn) Pod 名在變說明 Service 在做負(fù)載均衡for i in $(seq 1 6); do curl -s http://python-http.example.com | grep -o Pod: [^]*; done如果 Traefik 前面還掛了 HAProxy 做四層代理HAProxy 的配置里可以加 TCP 健康檢查探測(cè) Traefik 的/ping接口backend traefik-http-backend mode tcp balance roundrobin option tcp-check tcp-check connect port 80 tcp-check send GET /ping HTTP/1.1\r\nHost: localhost\r\n\r\n tcp-check expect string 200 OK server traefik-node-111 192.168.56.111:80 check inter 2000 fall 3 rise 2 server traefik-node-112 192.168.56.112:80 check inter 2000 fall 3 rise 2這樣 HAProxy 負(fù)責(zé)四層轉(zhuǎn)發(fā)和節(jié)點(diǎn)級(jí)健康檢查Traefik 負(fù)責(zé)七層路由分工明確。想看 HAProxy 后端狀態(tài)echo show stat | socat /run/haproxy/admin.sock stdio | grep traefikSTATUS 列顯示 UP 就說明健康檢查通過。5. 本篇常見錯(cuò)排查5.1 IngressRoute 不生效curl 返回 404最常見的原因是 entryPoints 名字對(duì)不上。Traefik 默認(rèn)的入口點(diǎn)叫web和websecure如果你在 values.yaml 里改過名字IngressRoute 里也要跟著改。用kubectl get ingressroute -o yaml檢查 match 規(guī)則再確認(rèn) Traefik 的 Dashboard 或日志里有沒有加載到這條路由kubectl logs -n kube-system -l app.kubernetes.io/nametraefik --tail505.2 Pod 一直 Pending調(diào)度不上去多半是 nodeAffinity 的標(biāo)簽沒打?qū)蛘?values 里values: - true寫成了布爾值true。K8s 的標(biāo)簽值必須是字符串寫成true不帶引號(hào)在某些解析器里會(huì)報(bào)錯(cuò)。用kubectl describe pod看 Events會(huì)明確告訴你哪個(gè)節(jié)點(diǎn)不滿足親和性。5.3 hostNetwork 下端口沖突如果節(jié)點(diǎn)上已經(jīng)有服務(wù)占了 80 或 443Traefik 的 Pod 會(huì)起不來報(bào)address already in use。檢查節(jié)點(diǎn)端口占用sudo ss -tlnp | grep -E :80|:443要么停掉沖突服務(wù)要么改用 NodePort 模式把 hostPort 去掉讓 Service 用 30000 以上的端口暴露。5.4 Python 服務(wù)返回 502Traefik 能路由到 Service但連不上后端 Pod。先確認(rèn) Service 的 targetPort 和容器 containerPort 一致都是 8080。再進(jìn) Pod 里自測(cè)kubectl exec -it pod-name -- curl localhost:8080如果 Pod 內(nèi)能通、外面不通問題在 Service selector 或 IngressRoute 的 service port 上。Service 的 port 是 80IngressRoute 里寫的也應(yīng)該是 80不是 8080。5.5 用 AI 輔助排錯(cuò)遇到報(bào)錯(cuò)信息看不懂可以把日志貼到模型對(duì)話里讓它解釋https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。比如 Traefik 的no available server或者 HAProxy 的503 Service Unavailable讓模型幫你定位是健康檢查失敗還是后端沒注冊(cè)。長(zhǎng)期做 K8s 運(yùn)維和配置生成的話Coding Plan 會(huì)更順手https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。6. 繼續(xù)接入把 AI 通道用起來整條鏈路跑通之后你會(huì)發(fā)現(xiàn) Traefik 的配置其實(shí)不復(fù)雜難的是排錯(cuò)和版本差異。Traefik 2.x 和 3.x 的 CRD apiVersion 就不一樣2.x 是traefik.containo.us/v1alpha13.x 變成了traefik.io/v1alpha1寫錯(cuò)就是 404。這種細(xì)節(jié)用 AI 輔助核對(duì)能省不少時(shí)間。如果你還沒拿 Key從 API Keys 頁(yè)面創(chuàng)建https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入方式參考文檔https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。API 地址統(tǒng)一用 https://taotoken.net/api 一個(gè) Key 走通模型對(duì)話、配置生成和排錯(cuò)。最后留一個(gè)實(shí)操建議把 Traefik 的 Dashboard 通過 IngressRoute 暴露出來用Host加PathPrefix限制訪問這樣你能直觀看到路由規(guī)則有沒有加載、后端服務(wù)健康不健康。Dashboard 默認(rèn)在 Traefik Pod 的 8080 端口加一條 IngressRoute 指向它就行。配好之后每次改完 YAML 刷新一下 Dashboard比翻日志快得多。