K8s Kluster Bagaimana Mempertahankan IP Sumber Setelah Load Balancing

Pengantar

应用部署 tidak selalu sederhana 安装 dan 运行, terkadang perlu mempertimbangkan 网络 masalah. Artikel ini akan menjelaskan bagaimana layanan dapat memperoleh IP sumber permintaan di kluster K8s.

Layanan aplikasi umumnya bergantung pada informasi input, jika informasi input tidak bergantung lima elemen (source IP, source port, destination port, destination port, protocol), maka layanan tersebut memiliki ketergantungan jaringan rendah dan tidak perlu memikirkan detail jaringan.

Oleh karena itu, bagi mayoritas orang, tidak ada kebutuhan membaca artikel ini. Jika Anda tertarik dengan jaringan, atau ingin memperluas wawasan, Anda bisa melanjutkan membaca artikel ini untuk memahami lebih banyak skenario layanan.

Artikel ini berdasarkan K8s v1.29.4, sebagian narasi di dalamnya mengacu pada pod dan endpoint, di artikel ini dapat dianggap setara.

Jika ada kesalahan, silakan sampaikan, saya akan segera memperbaiknya.

Mengapa Informasi IP Sumber Hilang?

Kita pertama kali memahami apa itu IP sumber. Ketika A mengirim permintaan ke B, B akan memforward permintaan ke C, meskipun C melihat alamat IP sumber protokol adalah IP B, namun artikel ini menganggap A的IP sebagai IP sumber.

Ada dua jenis perilaku yang utama menyebabkan informasi sumber hilang:

  1. 网络地址转换(NAT), tujuannya untuk menghemat IPv4 publik, 负载均衡, dll. Ini akan menyebabkan IP sumber yang dilihat oleh layanan adalah NAT perangkat, bukan IP sumber asli.
  2. 代理(Proxy), 反向代理(RP, Reverse Proxy) dan 负载均衡(LB, Load Balancer) termasuk dalam kategori ini, disebut sebagai 代理服务器 dalam artikel ini. Layanan proxy ini akan memforward permintaan ke layanan backend, tetapi akan mengganti IP sumber dengan IP sendiri.
  • NAT sederhana adalah mengganti ruang port dengan ruang IP, alamat IPv4 terbatas, satu alamat IP dapat memetakan 65535 port, kebanyakan waktu port tersebut tidak habis, sehingga banyak subnet IP dapat berbagi satu IP publik,区分 pada port. Forma penggunaannya adalah: public IP:public port -> private IP_1:private port, lebih banyak konten silakan cari sendiri Network Address Translation
  • Layanan proxy dilakukan untuk menyembunyikan atau mengekspos, layanan proxy akan memforward permintaan ke layanan backend, sambil mengganti IP sumber dengan IP sendiri, untuk menyembunyikan IP sumber asli layanan backend, melindungi keamanan layanan backend. Forma penggunaan proxy adalah: client IP -> proxy IP -> server IP, lebih banyak konten silakan cari sendiri Proxy

NAT dan 代理服务器 sangat umum, kebanyakan layanan tidak dapat memperoleh IP permintaan asli.

ini adalah dua cara umum mengubah IP sumber, jika ada yang lain silakan tambahkan.

Cara Mempertahankan IP Sumber?

Berikut adalah contoh HTTP permintaan:

字段长度(字节)位偏移描述
IP 首部
源 IP40-31发送方的 IP 地址
目的 IP432-63接收方的 IP 地址
TCP 首部
源端口20-15发送端口号
目的端口216-31接收端口号
序列号432-63用于标识发送方发送的数据的字节流
确认号464-95如果设置了 ACK 标志,则为下一个期望收到的序列号
数据偏移496-103数据起始位置相对于 TCP 首部的字节数
保留4104-111保留字段,未使用,设置为 0
标志位2112-127各种控制标志,如 SYN、ACK、FIN 等
窗口大小2128-143接收方可以接收的数据量
检验和2144-159用于检测数据是否在传输过程中发生了错误
紧急指针2160-175发送方希望接收方尽快处理的紧急数据的位置
选项可变176-…可能包括时间戳、最大报文段长度等
HTTP 首部
请求行可变…-…包括请求方法、URI dan HTTP 版本
头部字段可变…-…包含 berbagai bidang header, seperti Host、User-Agent 等
空行2…-…Digunakan untuk memisahkan header dan bagian badan
主体可变…-…Badan permintaan atau respons opsional

Tes di atas struktur permintaan HTTP, dapat ditemukan, ada TCP选项, request line, header fields, body yang variabel, di mana TCP options ruang terbatas, umumnya tidak digunakan untuk mentransfer IP sumber, request line membawa informasi tetap tidak dapat diperluas, HTTP body setelah dienkripsi tidak dapat dimodifikasi, hanya HTTP header fields cocok untuk memperluas mentransfer IP sumber.

HTTP header dapat menambah X-REAL-IP field, digunakan untuk mentransfer IP sumber, operasi ini biasanya ditempatkan di proxy server, lalu proxy server akan mengirim permintaan ke layanan backend, layanan backend kemudian dapat melalui field ini memperoleh informasi IP sumber.

Perlu dicatat, perlu memastikan proxy server berada di NAT device sebelumnya, sehingga dapat memperoleh IP sumber asli. Kita bisa di produk 阿里云 melihat 负载均衡器 ini kategori produk terpisah, posisinya di jaringan berbeda dari layanan aplikasi umum.

Panduan Operasi K8S

Menggunakan proyek whoami sebagai contoh untuk melakukan deployment.

Membuat Deployment

Pertama buat layanan:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: docker.io/traefik/whoami:latest
          ports:
            - containerPort: 8080

Langkah ini akan membuat Deployment, yang berisi 3 Pod, setiap Pod berisi satu kontainer, kontainer tersebut akan menjalankan layanan whoami.

Membuat Service

Bisa buat NodePort atau LoadBalancer jenis layanan, mendukung akses eksternal, atau buat ClusterIP jenis layanan, hanya mendukung akses dalam kluster, lalu menambah Ingress layanan, melalui Ingress mengekspos akses eksternal.

NodePort bisa diakses melalui NodeIP:NodePort atau melalui Ingress layanan, nyaman untuk pengujian, bagian ini menggunakan NodePort layanan.

apiVersion: v1
kind: Service
metadata:
  name: whoami-service
spec:
  type: NodePort
  selector:
    app: whoami
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
      nodePort: 30002

Setelah membuat layanan, dengan curl whoami.example.com:30002 mengakses, dapat dilihat IP yang dikembalikan adalah NodeIP, bukan IP permintaan asli whoami.

Catatan, ini bukan IP klien yang benar, ini adalah IP internal kluster. Ini yang terjadi:

  • Klien mengirim paket ke node:nodePort
  • node2 menggunakan IP-nya sendiri untuk mengganti alamat IP sumber paket (SNAT)
  • node2 mengganti tujuan IP paket menjadi IP Pod
  • Paket diarahkan ke node1, lalu ke endpoint
  • Balasan Pod diarahkan kembali ke node2
  • Balasan Pod dikirim kembali ke klien

Ditampilkan sebagai gambar:

Konfigurasi externalTrafficPolicy: Local

Untuk menghindari kasus ini, Kubernetes memiliki fitur dapat mempertahankan IP sumber klien. Jika menyetel service.spec.externalTrafficPolicy menjadi Local, kube-proxy hanya akan memproxy permintaan ke endpoint lokal, bukan memforward aliran ke node lain.

apiVersion: v1
kind: Service
metadata:
  name: whoami-service
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    app: whoami
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
      nodePort: 30002

Menggunakan curl whoami.example.com:30002 untuk pengujian, ketika whoami.example.com dipetakan ke IP node kluster banyak, ada proporsi tertentu tidak dapat diakses. Perlu dikonfirmasi catatan domain hanya berisi endpoint (pod) yang ditempatkan di node (node) IP.

Konfigurasi ini mempunyai biaya, yaitu kehilangan kemampuan load balancing dalam kluster, pelanggan hanya dapat mengakses node yang telah ditempatkan endpoint untuk mendapatkan respons.

Batas akses node

Ketika klien mengakses Node 2, tidak ada respons.

Membuat Ingress

Banyak layanan diberikan ke pengguna menggunakan format http/https, bentuk https://ip:port mungkin membuat pengguna merasa asing. Umumnya akan menggunakan Ingress mengubah layanan NodePort sebelumnya ke port 80/443 sebuah domain.

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

Setelah diterapkan, menggunakan curl whoami.example.com untuk pengujian, dapat dilihat ClientIP selalu IP Pod di node Ingress Controller.

root@client:~# curl whoami.example.com
...
RemoteAddr: 10.42.1.10:56482
...

root@worker:~# kubectl get -n ingress-nginx pod -o wide
NAME                                       READY   STATUS    RESTARTS   AGE    IP           NODE          NOMINATED NODE   READINESS GATES
ingress-nginx-controller-c8f499cfc-xdrg7   1/1     Running   0          3d2h   10.42.1.10   k3s-agent-1   <none>           <none>

Menggunakan Ingress reverse proxy NodePort layanan, artinya di endpoint sebelum dua lapis layanan, perbedaan ditunjukkan gambar di bawah.

graph LR
    A[Client] -->|whoami.example.com:80| B(Ingress)
    B -->|10.43.38.129:32123| C[Service]
    C -->|10.42.1.1:8080| D[Endpoint]
graph LR
    A[Client] -->|whoami.example.com:30001| B(Service)
    B -->|10.42.1.1:8080| C[Endpoint]

Pada jalur 1, saat akses eksternal Ingress, aliran pertama mencapai endpoint adalah Ingress Controller, lalu mencapai endpoint whoami.
Ingress Controller secara essence adalah layanan LoadBalancer,

kubectl -n ingress-nginx get svc

NAMESPACE   NAME             CLASS   HOSTS                       ADDRESS                                              PORTS   AGE
default     echoip-ingress   nginx   ip.example.com       172.16.0.57,2408:4005:3de:8500:4da1:169e:dc47:1707   80      18h
default     whoami-ingress   nginx   whoami.example.com   172.16.0.57,2408:4005:3de:8500:4da1:169e:dc47:1707   80      16h

Oleh karena itu, dapat melalui penyesuaian externalTrafficPolicy yang disebutkan sebelumnya ke dalam Ingress Controller untuk mempertahankan IP sumber.

Sama sekali perlu menetapkan configmap ingress-nginx-controller di configmap di dalam use-forwarded-headers menjadi true, agar Ingress Controller dapat mengenali field X-Forwarded-For atau X-REAL-IP.

apiVersion: v1
data:
  allow-snippet-annotations: "false"
  compute-full-forwarded-for: "true"
  use-forwarded-headers: "true"
  enable-real-ip: "true"
  forwarded-for-header: "X-Real-IP" # X-Real-IP or X-Forwarded-For
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.10.1
  name: ingress-nginx-controller
  namespace: ingress-nginx

Perbedaan utama antara NodePort layanan dan ingress-nginx-controller layanan terletak pada, NodePort backend umumnya tidak ditempatkan di setiap node, sedangkan ingress-nginx-controller backend umumnya ditempatkan di setiap node yang diekspos eksternal.

Dengan NodePort layanan menyetel externalTrafficPolicy akan menyebabkan permintaan lintas node tidak respons, sedangkan Ingress dapat men-set HEADER terlebih dahulu kemudian melakukan forward proxy, mengimplementasikan mempertahankan IP sumber dan kemampuan load balancing sekaligus.

Kesimpulan

  • Alamat Jaringan (NAT), Proxy, Reverse Proxy, Load Balancer menyebabkan IP sumber hilang.
  • Untuk mencegah IP sumber hilang, layanan proxy saat forward dapat menempatkan IP asli di field header HTTP X-REAL-IP, melalui layanan proxy ditransmisikan. Jika menggunakan banyak lapis proxy, dapat menggunakan field X-Forwarded-For, field ini dengan tumpukan merekam IP sumber serta daftar IP jalur proxy.
  • Layanan NodePort kluster menyetel externalTrafficPolicy: Local dapat mempertahankan IP sumber, namun kehilangan kemampuan load balancing.
  • ingress-nginx-controller ditempatkan dengan bentuk daemonset di semua node role loadbalancer, menyetel externalTrafficPolicy: Local dapat mempertahankan IP sumber dan mempertahankan kemampuan load balancing.

Referensi