前言
常見的nginx做反向代理架構,nginx轉發到後端的多台服務
┌─────────┐
│backend-1│
└─────────┘
┌─────────┐
┌─────┐ │backend-2│
│nginx│ ────► └─────────┘
└─────┘
...
┌─────────┐
│backend-n│
└─────────┘
在傳統的部署,如直接部署在nginx上,或者部署在docker中,nginx轉發到後面的的backend是固定在nginx的配置文件中的,這也很方便排查問題節點在哪兒:直接看access_log中的upstream_addr即可
該部署的優點非常明顯,簡單高效易維護,每一條鏈路,每一個狀態都非常清晰。但是缺點也很明顯,擴縮容艱難,一旦需要擴容backend,就非常麻煩了,需要首先部署backend,修改nginx轉發配置,最後重啓nginx
為了提高擴縮容效率,將該架構搬到k8s中
┌─────────┐
│backend-1│
└─────────┘
┌─────────┐
┌─────┐ ┌───────────────┐ │backend-2│
│nginx│ ────► │backend-service│ ────► └─────────┘
└─────┘ └───────────────┘ ...
┌─────────┐
│backend-n│
└─────────┘
通過k8s-service的能力,自動做服務發現,每當上/下線一個backend,就會動態發現backend的個數,從此之後,擴縮容就會變得非常簡單
但是新的問題來了,問題出現時,比如某個backend出現問題,導致從nginx的日誌出現了502,在access_log中顯示的upstream_addr並不是後端backend的地址,而是backend-service的地址,無法立刻知道到底是哪個backend 出問題
問題出現了,如何跟蹤一條request,能夠明確知道它進入了哪一個pod,就是本文需要探索的內容
nginx-ingress
如果架構遷移還在方案驗證階段,那麼恭喜,這個方法可以一勞永逸的解決nginx的問題,那就是利用nginx-ingress-controller來作為入口
安裝
這個就不班門弄斧了,直接祭出官網
- github倉庫地址
- 安裝腳本地址
這一步需要注意的點不多,就是鏡像的問題,鏡像有可能拉不下來,至於怎麼解決,大家可以看我之前的文章,關於如何拉鏡像
安裝完成之後
▶ kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-78f7f8bd46-tqjm4 1/1 Running 0 1m
nginx-ingress-controller本質和其他的nginx沒有什麼區別,只不過有一些額外的功能來輔助nginx提供更好的服務,這個一會再討論
backend後端服務
接着創建一個backend服務,使用最簡單的python tornado作為web容器
test.py
from tornado.ioloop import IOLoop
import tornado.httpserver as httpserver
import tornado.web
import os
class TestFlow(tornado.web.RequestHandler):
def get(self):
ret = 'i am backend in {}'.format(os.environ['HOSTNAME'])
self.write(ret)
def post(self, *args, **kwargs):
print(self.request.body.decode('utf-8'))
def applications():
urls = []
urls.append([r'/', TestFlow])
urls.append([r'/test', TestFlow])
return tornado.web.Application(urls)
def main():
app = applications()
server = httpserver.HTTPServer(app)
server.bind(10000, '0.0.0.0')
server.start(1)
IOLoop.current().start()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt as e:
IOLoop.current().stop()
finally:
IOLoop.current().close()
將其打包成鏡像
Dockerfile
FROM python:3.11-alpine
WORKDIR /opt
RUN pip3 install tornado -i https://mirrors.aliyun.com/pypi/simple/
ADD test.py /opt/test.py
CMD ["python3", "test.py"]
docker build . -t backend-service:v1
製作完成之後將其放入k8s,並且使用一個service作為訪問入口
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- image: backend-service:v1
imagePullPolicy: Never
name: backend
ports:
- containerPort: 10000
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: default
spec:
ports:
- port: 10000
protocol: TCP
targetPort: 10000
selector:
app: backend
type: ClusterIP
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
backend-6d4cdd4c68-mqzgj 1/1 Running 0 2m 10.244.0.40 wilson <none> <none>
backend-6d4cdd4c68-qjp9m 1/1 Running 0 2m 10.244.0.60 wilson <none> <none>
大功告成
創建ingress
繼續創建ingress,ingress其實就是nginx轉發到後端服務的配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-test-ingress
namespace: default
spec:
rules:
- host: wilsonchai.com
http:
paths:
- backend:
service:
name: backend-service
port:
number: 10000
path: /
pathType: Prefix
測試結果
折騰了半天,先捋一下整個的路徑,我們創建了nginx-ingress-controller,它是整個系統的入口,其次創建了後端服務backend,請求的終點是該服務,還創建了一系列的service以及ingress,他們的關係如圖所示

- 請求通過nodePort進來進入nginx-ingress。
▶ kubectl -n ingress-nginx get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.98.224.124 <pending> 80:30296/TCP,443:31592/TCP 30m - nginx-ingress檢查ingress規則,將請求轉發至backend-service
▶ kubectl get svc backend-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE backend-service ClusterIP 10.105.148.194 <none> 10000/TCP 30m - backend-service隨機轉發該流量進入一個pod
▶ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES backend-6d4cdd4c68-mqzgj 1/1 Running 0 30m 10.244.0.40 wilson <none> <none> backend-6d4cdd4c68-qjp9m 1/1 Running 0 30m 10.244.0.60 wilson <none> <none>
訪問集羣: curl -H "host: wilsonchai.com" 127.0.0.1:30296
▶ curl -H "host: wilsonchai.com" 127.0.0.1:30296
i am backend in backend-6d4cdd4c68-qjp9m
請求順利到達了backend,再檢查一下nginx-ingress的日誌
10.244.0.1 - - [28/Nov/2025:09:28:45 +0000] "GET / HTTP/1.1" 200 40 "-" "curl/7.81.0" 78 0.001 [default-backend-service-10000] [] 10.244.0.60:10000 40 0.001 200 1896b48d60ef31861478e713d65c9660
在upstream_addr上面,顯示的就是後端pod的ip: 10.244.0.60
至此,終於解決了開篇提出的問題,如果有request報錯502、503等,可以精準的定位到時哪個pod出現了問題
小結
Nginx Ingress Controller 能夠直接記錄後端 Pod IP,是因為它在轉發流量時,繞過了 Service 的 Cluster IP,直接與後端 Pod 建立連接,Nginx Ingress Controller 並不是一個普通的客户端,而是一個特殊的 K8s Controller
- Service 與 Endpoints: 當創建一個 Service 時,K8s 會自動創建一個相應的 Endpoints 對象(或 EndpointSlice)。這個 Endpoints 對象會實時記錄所有與 Service 標籤匹配的 後端 Pod 的實際 IP 地址和端口
- Controller 實時同步: Nginx Ingress Controller 會持續監聽 K8s API Server 中所有 Service 對應的 Endpoints 對象的變化(即 Pod 的創建、刪除、就緒狀態改變)
檢驗一下,再次訪問: curl -H "host: wilsonchai.com" 127.0.0.1:30296
登錄到nginx-ingress-controller容器,使用netstat檢查連接狀態
ingress-nginx-controller-78f7f8bd46-tqjm4:/etc/nginx$ netstat -anpt | grep ESTABLISH
tcp 0 0 10.244.0.16:36074 10.244.0.40:10000 ESTABLISHED -
果然直接與後端的pod建立了連接
後記
盲目的搬進k8s真的是完全錯誤的,解決了新的問題,又帶來更多不可控的問題,所以架構遷移真的需要做好調研:新架構能夠滿足需求,並且新架構的缺點也能夠克服或者忍受,方可行動
有位兄弟説了,如果沒有使用nginx-ingress-controller,而是直接把nginx直接搬遷到k8s,那怎麼辦?並且該文只是解決了nginx訪問後端的問題,如果是服務之間的訪問,又該怎麼辦呢?並且這是http協議,如果是其他協議, 比如gRPC,又該怎麼辦呢?
大家稍安勿躁,本文只是一個引子,後面會把這些問題統統説清楚,敬請期待
聯繫我
- 聯繫我,做深入的交流

至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...