使用ingress-nginx


文章轉載自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247496824&idx=1&sn=cd845d778f5188b1b7158d6c90836ccb&chksm=fdbafb65cacd72730ad6ebd4f08e81c953ffb1ab204780426fc37b06d2e3f4b89e99e73a300c&scene=178&cur_album_id=2180684251619196931#rd

Ingress 資源對象只是一個路由請求描述配置文件,要讓其真正生效還需要對應的 Ingress 控制器才行,Ingress 控制器有很多,這里我們先介紹使用最多的 ingress-nginx,它是基於 Nginx 的 Ingress 控制器。

運行原理

ingress-nginx 控制器主要是用來組裝一個 nginx.conf 的配置文件,當配置文件發生任何變動的時候就需要重新加載 Nginx 來生效,但是並不會只在影響 upstream 配置的變更后就重新加載 Nginx,控制器內部會使用一個 lua-nginx-module 來實現該功能。

我們知道 Kubernetes 控制器使用控制循環模式來檢查控制器中所需的狀態是否已更新或是否需要變更,所以 ingress-nginx 需要使用集群中的不同對象來構建模型,比如 Ingress、Service、Endpoints、Secret、ConfigMap 等可以生成反映集群狀態的配置文件的對象,控制器需要一直 Watch 這些資源對象的變化,但是並沒有辦法知道特定的更改是否會影響到最終生成的 nginx.conf 配置文件,所以一旦 Watch 到了任何變化控制器都必須根據集群的狀態重建一個新的模型,並將其與當前的模型進行比較,如果模型相同則就可以避免生成新的 Nginx 配置並觸發重新加載,否則還需要檢查模型的差異是否只和端點有關,如果是這樣,則然后需要使用 HTTP POST 請求將新的端點列表發送到在 Nginx 內運行的 Lua 處理程序,並再次避免生成新的 Nginx 配置並觸發重新加載,如果運行和新模型之間的差異不僅僅是端點,那么就會基於新模型創建一個新的 Nginx 配置了,這樣構建模型最大的一個好處就是在狀態沒有變化時避免不必要的重新加載,可以節省大量 Nginx 重新加載。

下面簡單描述了需要重新加載的一些場景:

  • 創建了新的 Ingress 資源
  • TLS 添加到現有 Ingress
  • 從 Ingress 中添加或刪除 path 路徑
  • Ingress、Service、Secret 被刪除了
  • Ingress 的一些缺失引用對象變可用了,例如 Service 或 Secret
  • 更新了一個 Secret

對於集群規模較大的場景下頻繁的對 Nginx 進行重新加載顯然會造成大量的性能消耗,所以要盡可能減少出現重新加載的場景。

由於 ingress-nginx 所在的節點需要能夠訪問外網(不是強制的),這樣域名可以解析到這些節點上直接使用,所以需要讓 ingress-nginx 綁定節點的 80 和 443 端口,所以可以使用 hostPort 來進行訪問,當然對於線上環境來說為了保證高可用,一般是需要運行多個 ·ingress-nginx 實例的,然后可以用一個 nginx/haproxy 作為入口,通過 keepalived 來訪問邊緣節點的 vip 地址。

!!! info "邊緣節點" 所謂的邊緣節點即集群內部用來向集群外暴露服務能力的節點,集群外部的服務通過該節點來調用集群內部的服務,邊緣節點是集群內外交流的一個 Endpoint。

第一個示例

安裝成功后,現在我們來為一個 nginx 應用創建一個 Ingress 資源,如下所示:

# my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    app: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: my-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-nginx
  namespace: default
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(關聯的 ingress-nginx 控制器)
  rules:
  - host: ngdemo.qikqiak.com  # 將域名映射到 my-nginx 服務
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 將所有請求發送到 my-nginx 服務的 80 端口
            name: my-nginx
            port:
              number: 80
# 不過需要注意大部分Ingress控制器都不是直接轉發到Service, 而是只是通過Service來獲取后端的Endpoints列表,直接轉發到Pod,這樣可以減少網絡跳轉,提高性能

直接創建上面的資源對象:

➜ kubectl apply -f my-nginx.yaml
deployment.apps/my-nginx created
service/my-nginx created
ingress.networking.k8s.io/my-nginx created
➜ kubectl get ingress
NAME           CLASS    HOSTS                ADDRESS         PORTS   AGE
my-nginx       nginx    ngdemo.qikqiak.com   192.168.31.31   80      30m

在上面的 Ingress 資源對象中我們使用配置 ingressClassName: nginx 指定讓我們安裝的 ingress-nginx 這個控制器來處理我們的 Ingress 資源,配置的匹配路徑類型為前綴的方式去匹配 /,將來自域名 ngdemo.qikqiak.com 的所有請求轉發到 my-nginx 服務的后端 Endpoints 中去。

上面資源創建成功后,然后我們可以將域名 ngdemo.qikqiak.com 解析到 ingress-nginx 所在的邊緣節點中的任意一個,當然也可以在本地 /etc/hosts 中添加對應的映射也可以,然后就可以通過域名進行訪問了。

下圖顯示了客戶端是如何通過 Ingress 控制器連接到其中一個 Pod 的流程,客戶端首先對 ngdemo.qikqiak.com 執行 DNS 解析,得到 Ingress 控制器所在節點的 IP,然后客戶端向 Ingress 控制器發送 HTTP 請求,然后根據 Ingress 對象里面的描述匹配域名,找到對應的 Service 對象,並獲取關聯的 Endpoints 列表,將客戶端的請求轉發給其中一個 Pod。

前面我們也提到了 ingress-nginx 控制器的核心原理就是將我們的 Ingress 這些資源對象映射翻譯成 Nginx 配置文件 nginx.conf,我們可以通過查看控制器中的配置文件來驗證這點:

➜ kubectl exec -it $POD_NAME -n ingress-nginx -- cat /etc/nginx/nginx.conf

......
upstream upstream_balancer {
        server 0.0.0.1; # placeholder
        balancer_by_lua_block {
                balancer.balance()
        }
        keepalive 320;
        keepalive_timeout  60s;
        keepalive_requests 10000;
}

......
## start server ngdemo.qikqiak.com
server {
        server_name ngdemo.qikqiak.com ;

        listen 80  ;
        listen [::]:80  ;
        listen 443  ssl http2 ;
        listen [::]:443  ssl http2 ;

        set $proxy_upstream_name "-";

        ssl_certificate_by_lua_block {
                certificate.call()
        }

        location / {

                set $namespace      "default";
                set $ingress_name   "my-nginx";
                set $service_name   "my-nginx";
                set $service_port   "80";
                set $location_path  "/";
                set $global_rate_limit_exceeding n;
                ......
                proxy_next_upstream_timeout             0;
                proxy_next_upstream_tries               3;

                proxy_pass http://upstream_balancer;

                proxy_redirect                          off;

        }

}
## end server ngdemo.qikqiak.com
......

我們可以在 nginx.conf 配置文件中看到上面我們新增的 Ingress 資源對象的相關配置信息,不過需要注意的是現在並不會為每個 backend 后端都創建一個 upstream 配置塊,現在是使用 Lua 程序進行動態處理的,所以我們沒有直接看到后端的 Endpoints 相關配置數據。

Nginx 配置

如果我們還想進行一些自定義配置,則有幾種方式可以實現:使用 Configmap 在 Nginx 中設置全局配置、通過 Ingress 的 Annotations 設置特定 Ingress 的規則、自定義模板。接下來我們重點給大家介紹使用注解來對 Ingress 對象進行自定義。

Basic Auth

我們可以在 Ingress 對象上配置一些基本的 Auth 認證,比如 Basic Auth,可以用 htpasswd 生成一個密碼文件來驗證身份驗證。

➜ htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo

然后根據上面的 auth 文件創建一個 secret 對象:

➜ kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
➜ kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJFUxYlFZTFVoJHdIZUZQQ1dyZTlGRFZONTQ0dXVQdC4K
kind: Secret
metadata:
  name: basic-auth
  namespace: default
type: Opaque

然后對上面的 my-nginx 應用創建一個具有 Basic Auth 的 Ingress 對象:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic  # 認證類型
    nginx.ingress.kubernetes.io/auth-secret: basic-auth  # 包含 user/password 定義的 secret 對象名
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'  # 要顯示的帶有適當上下文的消息,說明需要身份驗證的原因
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(關聯的 ingress-nginx 控制器)
  rules:
  - host: bauth.qikqiak.com  # 將域名映射到 my-nginx 服務
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 將所有請求發送到 my-nginx 服務的 80 端口
            name: my-nginx
            port:
              number: 80

直接創建上面的資源對象,然后通過下面的命令或者在瀏覽器中直接打開配置的域名:

➜ kubectl get ingress
NAME                CLASS    HOSTS                        ADDRESS         PORTS   AGE
ingress-with-auth   nginx    bauth.qikqiak.com            192.168.31.31   80      6m55s
➜ curl -v http://192.168.31.31 -H 'Host: bauth.qikqiak.com'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
> GET / HTTP/1.1
> Host: bauth.qikqiak.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:49:03 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required - foo"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0

我們可以看到出現了 401 認證失敗錯誤,然后帶上我們配置的用戶名和密碼進行認證:

➜ curl -v http://192.168.31.31 -H 'Host: bauth.qikqiak.com' -u 'foo:foo'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Host: bauth.qikqiak.com
> Authorization: Basic Zm9vOmZvbw==
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 16 Dec 2021 10:49:38 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
< ETag: "61814ff2-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0

可以看到已經認證成功了。除了可以使用我們自己在本地集群創建的 Auth 信息之外,還可以使用外部的 Basic Auth 認證信息,比如我們使用 https://httpbin.org 的外部 Basic Auth 認證,創建如下所示的 Ingress 資源對象:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # 配置外部認證服務地址
    nginx.ingress.kubernetes.io/auth-url: https://httpbin.org/basic-auth/user/passwd
  name: external-auth
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: external-bauth.qikqiak.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

上面的資源對象創建完成后,再進行簡單的測試:

➜ kubectl get ingress
NAME                CLASS    HOSTS                        ADDRESS         PORTS   AGE
external-auth       <none>   external-bauth.qikqiak.com                   80      72s
➜ curl -k http://192.168.31.31 -v -H 'Host: external-bauth.qikqiak.com'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
> GET / HTTP/1.1
> Host: external-bauth.qikqiak.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:57:25 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0

然后使用正確的用戶名和密碼測試:

➜ curl -k http://192.168.31.31 -v -H 'Host: external-bauth.qikqiak.com' -u 'user:passwd'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Host: external-bauth.qikqiak.com
> Authorization: Basic dXNlcjpwYXNzd2Q=
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 16 Dec 2021 10:58:31 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
< ETag: "61814ff2-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0

如果用戶名或者密碼錯誤則同樣會出現401的狀態碼:

➜ curl -k http://192.168.31.31 -v -H 'Host: external-bauth.qikqiak.com' -u 'user:passwd123'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Host: external-bauth.qikqiak.com
> Authorization: Basic dXNlcjpwYXNzd2QxMjM=
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:59:18 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0

當然除了 Basic Auth 這一種簡單的認證方式之外,ingress-nginx 還支持一些其他高級的認證,比如我們可以使用 GitHub OAuth 來認證 Kubernetes 的 Dashboard。

URL Rewrite

ingress-nginx 很多高級的用法可以通過 Ingress 對象的 annotation 進行配置,比如常用的 URL Rewrite 功能。很多時候我們會將 ingress-nginx 當成網關使用,比如對訪問的服務加上 /app 這樣的前綴,在 nginx 的配置里面我們知道有一個 proxy_pass 指令可以實現:

location /app/ {
  proxy_pass http://127.0.0.1/remote/;
}

proxy_pass 后面加了 /remote 這個路徑,此時會將匹配到該規則路徑中的 /app 用 /remote 替換掉,相當於截掉路徑中的 /app。同樣的在 Kubernetes 中使用 ingress-nginx 又該如何來實現呢?我們可以使用 rewrite-target 的注解來實現這個需求,比如現在我們想要通過 rewrite.qikqiak.com/gateway/ 來訪問到 Nginx 服務,則我們需要對訪問的 URL 路徑做一個 Rewrite,在 PATH 中添加一個 gateway 的前綴,關於 Rewrite 的操作在 ingress-nginx 官方文檔中也給出對應的說明:

按照要求我們需要在 path 中匹配前綴 gateway,然后通過 rewrite-target 指定目標,Ingress 對象如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

更新后,我們可以預見到直接訪問域名肯定是不行了,因為我們沒有匹配 / 的 path 路徑:

➜ curl rewrite.qikqiak.com
default backend - 404

但是我們帶上 gateway 的前綴再去訪問:

我們可以看到已經可以訪問到了,這是因為我們在 path 中通過正則表達式 /gateway(/|$)(.*) 將匹配的路徑設置成了 rewrite-target 的目標路徑了,所以我們訪問 rewite.qikqiak.com/gateway/ 的時候實際上相當於訪問的就是后端服務的 / 路徑。

要解決我們訪問主域名出現 404 的問題,我們可以給應用設置一個 app-root 的注解,這樣當我們訪問主域名的時候會自動跳轉到我們指定的 app-root 目錄下面,如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

這個時候我們更新應用后訪問主域名 rewrite.qikqiak.com 就會自動跳轉到 rewrite.qikqiak.com/gateway/ 路徑下面去了。但是還有一個問題是我們的 path 路徑其實也匹配了 /app 這樣的路徑,可能我們更加希望我們的應用在最后添加一個 / 這樣的 slash,同樣我們可以通過 configuration-snippet 配置來完成,如下 Ingress 對象:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/gateway)$ $1/ redirect;
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

更新后我們的應用就都會以 / 這樣的 slash 結尾了。這樣就完成了我們的需求,如果你原本對 nginx 的配置就非常熟悉的話應該可以很快就能理解這種配置方式了。

灰度發布

在日常工作中我們經常需要對服務進行版本更新升級,所以我們經常會使用到滾動升級、藍綠發布、灰度發布等不同的發布操作。而 ingress-nginx 支持通過 Annotations 配置來實現不同場景下的灰度發布和測試,可以滿足金絲雀發布、藍綠部署與 A/B 測試等業務場景。

ingress-nginx 的 Annotations 支持以下 4 種 Canary 規則:

  • nginx.ingress.kubernetes.io/canary-by-header:基於 Request Header 的流量切分,適用於灰度發布以及 A/B 測試。當 Request Header 設置為 always 時,請求將會被一直發送到 Canary 版本;當 Request Header 設置為 never 時,請求不會被發送到 Canary 入口;對於任何其他 Header 值,將忽略 Header,並通過優先級將請求與其他金絲雀規則進行優先級的比較。

  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用於通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當 Request Header 設置為此值時,它將被路由到 Canary 入口。該規則允許用戶自定義 Request Header 的值,必須與上一個 annotation (canary-by-header) 一起使用。

  • nginx.ingress.kubernetes.io/canary-weight:基於服務權重的流量切分,適用於藍綠部署,權重范圍 0 - 100 按百分比將請求路由到 Canary Ingress 中指定的服務。權重為 0 意味着該金絲雀規則不會向 Canary 入口的服務發送任何請求,權重為 100 意味着所有請求都將被發送到 Canary 入口。

  • nginx.ingress.kubernetes.io/canary-by-cookie:基於 cookie 的流量切分,適用於灰度發布與 A/B 測試。用於通知 Ingress 將請求路由到 Canary Ingress 中指定的服務的cookie。當 cookie 值設置為 always 時,它將被路由到 Canary 入口;當 cookie 值設置為 never 時,請求不會被發送到 Canary 入口;對於任何其他值,將忽略 cookie 並將請求與其他金絲雀規則進行優先級的比較。

需要注意的是金絲雀規則按優先順序進行排序:canary-by-header - > canary-by-cookie - > canary-weight

總的來說可以把以上的四個 annotation 規則划分為以下兩類:

  • 基於權重的 Canary 規則

  • 基於用戶請求的 Canary 規則

下面我們通過一個示例應用來對灰度發布功能進行說明。

第一步. 部署 Production 應用

首先創建一個 production 環境的應用資源清單:

# production.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production
  labels:
    app: production
spec:
  selector:
    matchLabels:
      app: production
  template:
    metadata:
      labels:
        app: production
    spec:
      containers:
      - name: production
        image: cnych/echoserver
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: production
  labels:
    app: production
spec:
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: production

然后創建一個用於 production 環境訪問的 Ingress 資源對象:

# production-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production
spec:
  ingressClassName: nginx
  rules:
  - host: echo.qikqiak.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: production
            port:
              number: 80

直接創建上面的幾個資源對象:

➜ kubectl apply -f production.yaml
➜ kubectl apply -f production-ingress.yaml
➜ kubectl get pods -l app=production
NAME                         READY   STATUS    RESTARTS   AGE
production-856d5fb99-d6bds   1/1     Running   0          2m50s
➜ kubectl get ingress
NAME         CLASS    HOSTS                ADDRESS        PORTS   AGE
production   <none>   echo.qikqiak.com     10.151.30.11   80      90s

應用部署成功后,將域名 echo.qikqiak.com 映射到 master1 節點(ingress-nginx 所在的節點)的 IP即可正常訪問應用:

➜ curl http://echo.qikqiak.com

Hostname: production-856d5fb99-d6bds

Pod Information:
 node name: node1
 pod name: production-856d5fb99-d6bds
 pod namespace: default
 pod IP: 10.244.1.111

Server values:
 server_version=nginx: 1.13.3 - lua: 10008

Request Information:
 client_address=10.244.0.0
 method=GET
 real path=/
 query=
 request_version=1.1
 request_scheme=http
 request_uri=http://echo.qikqiak.com:8080/

Request Headers:
 accept=*/*
 host=echo.qikqiak.com
 user-agent=curl/7.64.1
 x-forwarded-for=171.223.99.184
 x-forwarded-host=echo.qikqiak.com
 x-forwarded-port=80
 x-forwarded-proto=http
 x-real-ip=171.223.99.184
 x-request-id=e680453640169a7ea21afba8eba9e116
 x-scheme=http

Request Body:
 -no body in request-

第二步. 創建 Canary 版本參考將上述 Production 版本的 production.yaml 文件,再創建一個 Canary 版本的應用。

# canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
  labels:
    app: canary
spec:
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      labels:
        app: canary
    spec:
      containers:
      - name: canary
        image: cnych/echoserver
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: canary
  labels:
    app: canary
spec:
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: canary

接下來就可以通過配置 Annotation 規則進行流量切分了。

第三步. Annotation 規則配置

  1. 基於權重:基於權重的流量切分的典型應用場景就是藍綠部署,可通過將權重設置為 0 或 100 來實現。例如,可將 Green 版本設置為主要部分,並將 Blue 版本的入口配置為 Canary。最初,將權重設置為 0,因此不會將流量代理到 Blue 版本。一旦新版本測試和驗證都成功后,即可將 Blue 版本的權重設置為 100,即所有流量從 Green 版本轉向 Blue。

創建一個基於權重的 Canary 版本的應用路由 Ingress 對象。

# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"   # 要開啟灰度發布機制,首先需要啟用 Canary
    nginx.ingress.kubernetes.io/canary-weight: "30"  # 分配30%流量到當前Canary版本
spec:
  ingressClassName: nginx
  rules:
  - host: echo.qikqiak.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: canary
            port:
              number: 80

直接創建上面的資源對象即可:

➜ kubectl apply -f canary.yaml
➜ kubectl apply -f canary-ingress.yaml
➜ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
canary-66cb497b7f-48zx4      1/1     Running   0          7m48s
production-856d5fb99-d6bds   1/1     Running   0          21m
......
➜ kubectl get svc
NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
canary                     ClusterIP   10.106.91.106    <none>        80/TCP                       8m23s
production                 ClusterIP   10.105.182.15    <none>        80/TCP                       22m
......
➜ kubectl get ingress
NAME         CLASS    HOSTS                ADDRESS        PORTS   AGE
canary       <none>   echo.qikqiak.com     10.151.30.11   80      108s
production   <none>   echo.qikqiak.com     10.151.30.11   80      22m

Canary 版本應用創建成功后,接下來我們在命令行終端中來不斷訪問這個應用,觀察 Hostname 變化:

➜ for i in $(seq 1 10); do curl -s echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-d6bds
Hostname: canary-66cb497b7f-48zx4
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: production-856d5fb99-d6bds

由於我們給 Canary 版本應用分配了 30% 左右權重的流量,所以上面我們訪問10次有3次訪問到了 Canary 版本的應用,符合我們的預期。

  1. 基於 Request Header: 基於 Request Header 進行流量切分的典型應用場景即灰度發布或 A/B 測試場景。

在上面的 Canary 版本的 Ingress 對象中新增一條 annotation 配置 nginx.ingress.kubernetes.io/canary-by-header: canary(這里的 value 可以是任意值),使當前的 Ingress 實現基於 Request Header 進行流量切分,由於 canary-by-header 的優先級大於 canary-weight,所以會忽略原有的 canary-weight 的規則。

annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要開啟灰度發布機制,首先需要啟用 Canary
  nginx.ingress.kubernetes.io/canary-by-header: canary  # 基於header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 會被忽略,因為配置了 canary-by-headerCanary版本

更新上面的 Ingress 資源對象后,我們在請求中加入不同的 Header 值,再次訪問應用的域名。

注意:當 Request Header 設置為 never 或 always 時,請求將不會或一直被發送到 Canary 版本,對於任何其他 Header 值,將忽略 Header,並通過優先級將請求與其他 Canary 規則進行優先級的比較。

➜ for i in $(seq 1 10); do curl -s -H "canary: never" echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds

這里我們在請求的時候設置了 canary: never 這個 Header 值,所以請求沒有發送到 Canary 應用中去。如果設置為其他值呢:

➜ for i in $(seq 1 10); do curl -s -H "canary: other-value" echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: canary-66cb497b7f-48zx4
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: production-856d5fb99-d6bds
Hostname: canary-66cb497b7f-48zx4
Hostname: production-856d5fb99-d6bds
Hostname: canary-66cb497b7f-48zx4

由於我們請求設置的 Header 值為 canary: other-value,所以 ingress-nginx 會通過優先級將請求與其他 Canary 規則進行優先級的比較,我們這里也就會進入 canary-weight: "30" 這個規則去。

這個時候我們可以在上一個 annotation (即 canary-by-header)的基礎上添加一條 nginx.ingress.kubernetes.io/canary-by-header-value: user-value 這樣的規則,就可以將請求路由到 Canary Ingress 中指定的服務了。

annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要開啟灰度發布機制,首先需要啟用 Canary
  nginx.ingress.kubernetes.io/canary-by-header-value: user-value
  nginx.ingress.kubernetes.io/canary-by-header: canary  # 基於header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 分配30%流量到當前Canary版本

同樣更新 Ingress 對象后,重新訪問應用,當 Request Header 滿足 canary: user-value時,所有請求就會被路由到 Canary 版本:

➜ for i in $(seq 1 10); do curl -s -H "canary: user-value" echo.qikqiak.com | grep "Hostname"; done
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
  1. 基於 Cookie:與基於 Request Header 的 annotation 用法規則類似。例如在 A/B 測試場景下,需要讓地域為北京的用戶訪問 Canary 版本。那么當 cookie 的 annotation 設置為 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing",此時后台可對登錄的用戶請求進行檢查,如果該用戶訪問源來自北京則設置 cookie users_from_Beijing 的值為 always,這樣就可以確保北京的用戶僅訪問 Canary 版本。

同樣我們更新 Canary 版本的 Ingress 資源對象,采用基於 Cookie 來進行流量切分,

annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要開啟灰度發布機制,首先需要啟用 Canary
  nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing"  # 基於 cookie
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 會被忽略,因為配置了 canary-by-cookie

更新上面的 Ingress 資源對象后,我們在請求中設置一個 users_from_Beijing=always 的 Cookie 值,再次訪問應用的域名。

➜ for i in $(seq 1 10); do curl -s -b "users_from_Beijing=always" echo.qikqiak.com | grep "Hostname"; done
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4
Hostname: canary-66cb497b7f-48zx4

我們可以看到應用都被路由到了 Canary 版本的應用中去了,如果我們將這個 Cookie 值設置為 never,則不會路由到 Canary 應用中。

HTTPS

如果我們需要用 HTTPS 來訪問我們這個應用的話,就需要監聽 443 端口了,同樣用 HTTPS 訪問應用必然就需要證書,這里我們用 openssl 來創建一個自簽名的證書:

➜ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=foo.bar.com"

然后通過 Secret 對象來引用證書文件:

# 要注意證書文件名稱必須是 tls.crt 和 tls.key
➜ kubectl create secret tls foo-tls --cert=tls.crt --key=tls.key
secret/who-tls created

這個時候我們就可以創建一個 HTTPS 訪問應用的:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    # 認證類型
    nginx.ingress.kubernetes.io/auth-type: basic
    # 包含 user/password 定義的 secret 對象名
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # 要顯示的帶有適當上下文的消息,說明需要身份驗證的原因
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
  ingressClassName: nginx
  tls:  # 配置 tls 證書
  - hosts:
    - foo.bar.com
    secretName: foo-tls
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

除了自簽名證書或者購買正規機構的 CA 證書之外,我們還可以通過一些工具來自動生成合法的證書,cert-manager 是一個雲原生證書管理開源項目,可以用於在 Kubernetes 集群中提供 HTTPS 證書並自動續期,支持 Let's Encrypt/HashiCorp/Vault 這些免費證書的簽發。在 Kubernetes 中,可以通過 Kubernetes Ingress 和 Let's Encrypt 實現外部服務的自動化 HTTPS。

TCP與UDP

由於在 Ingress 資源對象中沒有直接對 TCP 或 UDP 服務的支持,要在 ingress-nginx 中提供支持,需要在控制器啟動參數中添加 --tcp-services-configmap 和 --udp-services-configmap 標志指向一個 ConfigMap,其中的 key 是要使用的外部端口,value 值是使用格式 <namespace/service name>: :[PROXY]:[PROXY] 暴露的服務,端口可以使用端口號或者端口名稱,最后兩個字段是可選的,用於配置 PROXY 代理。

比如現在我們要通過 ingress-nginx 來暴露一個 MongoDB 服務,首先創建如下的應用:

# mongo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo
  labels:
    app: mongo
spec:
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
    spec:
      volumes:
      - name: data
        emptyDir: {}
      containers:
      - name: mongo
        image: mongo:4.0
        ports:
        - containerPort: 27017
        volumeMounts:
        - name: data
          mountPath: /data/db
---
apiVersion: v1
kind: Service
metadata:
  name: mongo
spec:
  selector:
    app: mongo
  ports:
  - port: 27017

直接創建上面的資源對象:

➜ kubectl apply -f mongo.yaml
➜ kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
mongo           ClusterIP   10.98.117.228    <none>        27017/TCP   2m26s
➜ kubectl get pods -l app=mongo
NAME                     READY   STATUS    RESTARTS   AGE
mongo-84c587f547-gd7pv   1/1     Running   0          2m5s

現在我們要通過 ingress-nginx 來暴露上面的 MongoDB 服務,我們需要創建一個如下所示的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
  namespace: ingress-nginx
data:
  "27017": default/mongo:27017

然后在 ingress-nginx 的啟動參數中添加 --tcp-services-configmap=$(POD_NAMESPACE)/ingress-nginx-tcp 這樣的配置即可,由於我們這里使用的是 Helm Chart 進行安裝的,我們只需要去覆蓋 Values 值重新安裝即可,修改 ci/daemonset-prod.yaml 文件:

# ci/daemonset-prod.yaml
# ...... 其他部分省略,和之前的保持一致

tcp:  # 配置 tcp 服務
  27017: "default/mongo:27017"  # 使用 27017 端口去映射 mongo 服務
  # 9000: "default/test:8080"   # 如果還需要暴露其他 TCP 服務,繼續添加即可

配置完成后重新更新當前的 ingress-nginx:

➜ helm upgrade --install ingress-nginx . -f ./ci/daemonset-prod.yaml --namespace ingress-nginx

重新部署完成后會自動生成一個名為 ingress-nginx-tcp 的 ConfigMap 對象,如下所示:

➜ kubectl get configmap -n ingress-nginx ingress-nginx-tcp -o yaml
apiVersion: v1
data:
  "27017": default/mongo:27017
kind: ConfigMap
metadata:
  ......
  name: ingress-nginx-tcp
  namespace: ingress-nginx

在 ingress-nginx 的啟動參數中也添加上 --tcp-services-configmap=$(POD_NAMESPACE)/ingress-nginx-tcp 這樣的配置:

➜ kubectl get pods -n ingress-nginx
NAME                                            READY   STATUS    RESTARTS        AGE
ingress-nginx-controller-gc582                  1/1     Running   0               5m17s
➜ kubectl get pod ingress-nginx-controller-gc582 -n ingress-nginx -o yaml
apiVersion: v1
kind: Pod
......
  containers:
  - args:
    - /nginx-ingress-controller
    - --default-backend-service=$(POD_NAMESPACE)/ingress-nginx-defaultbackend
    - --election-id=ingress-controller-leader
    - --controller-class=k8s.io/ingress-nginx
    - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
    - --tcp-services-configmap=$(POD_NAMESPACE)/ingress-nginx-tcp  # tcp 配置參數
    - --validating-webhook=:8443
    - --validating-webhook-certificate=/usr/local/certificates/cert
    - --validating-webhook-key=/usr/local/certificates/key
......
    ports:
......
    - containerPort: 27017
      hostPort: 27017
      name: 27017-tcp
      protocol: TCP
......

現在我們就可以通過 ingress-nginx 暴露的 27017 端口去訪問 Mongo 服務了:

➜ mongo --host 192.168.31.31 --port 27017
MongoDB shell version v4.0.3
connecting to: mongodb://192.168.31.31:27017/
Implicit session: session { "id" : UUID("10f462eb-32b8-443b-ad85-99820db1aaa0") }
MongoDB server version: 4.0.27
......

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
>

同樣的我們也可以去查看最終生成的 nginx.conf 配置文件:

➜ kubectl exec -it ingress-nginx-controller-gc582 -n ingress-nginx -- cat /etc/nginx/nginx.conf
......
stream {
    ......
    # TCP services
    server {
            preread_by_lua_block {
                    ngx.var.proxy_upstream_name="tcp-default-mongo-27017";
            }
            listen                  27017;
            listen                  [::]:27017;
            proxy_timeout           600s;
            proxy_next_upstream     on;
            proxy_next_upstream_timeout 600s;
            proxy_next_upstream_tries   3;
            proxy_pass              upstream_balancer;
    }
    # UDP services
}

TCP 相關的配置位於 stream 配置塊下面。從 Nginx 1.9.13 版本開始提供 UDP 負載均衡,同樣我們也可以在 ingress-nginx 中來代理 UDP 服務,比如我們可以去暴露 kube-dns 的服務,同樣需要創建一個如下所示的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: udp-services
  namespace: ingress-nginx
data:
  53: "kube-system/kube-dns:53"

然后需要在 ingress-nginx 參數中添加一個 - --udp-services-configmap=$(POD_NAMESPACE)/udp-services 這樣的配置,當然我們這里只需要去修改 Values 文件值即可,修改 ci/daemonset-prod.yaml 文件:

# ci/daemonset-prod.yaml
# ...... 其他部分省略,和之前的保持一致

tcp:  # 配置 tcp 服務
  27017: "default/mongo:27017"  # 使用 27017 端口去映射 mongo 服務
  # 9000: "default/test:8080"   # 如果還需要暴露其他 TCP 服務,繼續添加即可

udp:  # 配置 udp 服務
  53: "kube-system/kube-dns:53"

然后重新更新即可。

全局配置

除了可以通過 annotations 對指定的 Ingress 進行定制之外,我們還可以配置 ingress-nginx 的全局配置,在控制器啟動參數中通過標志 --configmap 指定了一個全局的 ConfigMap 對象,我們可以將全局的一些配置直接定義在該對象中即可:

containers:
  - args:
    - /nginx-ingress-controller
    - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
    ......

比如這里我們用於全局配置的 ConfigMap 名為 ingress-nginx-controller:

➜ kubectl get configmap -n ingress-nginx
NAME                        DATA   AGE
ingress-nginx-controller    1      5d2h

比如我們可以添加如下所示的一些常用配置:

➜ kubectl edit configmap ingress-nginx-controller -n ingress-nginx
apiVersion: v1
data:
  allow-snippet-annotations: "true"
  client-header-buffer-size: 32k  # 注意不是下划線
  client-max-body-size: 5m
  use-gzip: "true"
  gzip-level: "7"
  large-client-header-buffers: 4 32k
  proxy-connect-timeout: 11s
  proxy-read-timeout: 12s
  keep-alive: "75"   # 啟用keep-alive,連接復用,提高QPS
  keep-alive-requests: "100"
  upstream-keepalive-connections: "10000"
  upstream-keepalive-requests: "100"
  upstream-keepalive-timeout: "60"
  disable-ipv6: "true"
  disable-ipv6-dns: "true"
  max-worker-connections: "65535"
  max-worker-open-files: "10240"
kind: ConfigMap
......

修改完成后 Nginx 配置會自動重載生效,我們可以查看 nginx.conf 配置文件進行驗證:

➜ kubectl exec -it ingress-nginx-controller-gc582 -n ingress-nginx -- cat /etc/nginx/nginx.conf |grep large_client_header_buffers
        large_client_header_buffers     4 32k;

由於我們這里是 Helm Chart 安裝的,為了保證重新部署后配置還在,我們同樣需要通過 Values 進行全局配置:

# ci/daemonset-prod.yaml
controller:
  config:
    allow-snippet-annotations: "true"
    client-header-buffer-size: 32k  # 注意不是下划線
    client-max-body-size: 5m
    use-gzip: "true"
    gzip-level: "7"
    large-client-header-buffers: 4 32k
    proxy-connect-timeout: 11s
    proxy-read-timeout: 12s
    keep-alive: "75"   # 啟用keep-alive,連接復用,提高QPS
    keep-alive-requests: "100"
    upstream-keepalive-connections: "10000"
    upstream-keepalive-requests: "100"
    upstream-keepalive-timeout: "60"
    disable-ipv6: "true"
    disable-ipv6-dns: "true"
    max-worker-connections: "65535"
    max-worker-open-files: "10240"

# 其他省略

此外往往我們還需要對 ingress-nginx 部署的節點進行性能優化,修改一些內核參數,使得適配 Nginx 的使用場景,一般我們是直接去修改節點上的內核參數,為了能夠統一管理,我們可以使用 initContainers 來進行配置:

initContainers:
- command:
  - /bin/sh
  - -c
  - |
    mount -o remount rw /proc/sys
    sysctl -w net.core.somaxconn=65535  # 具體的配置視具體情況而定
    sysctl -w net.ipv4.tcp_tw_reuse=1
    sysctl -w net.ipv4.ip_local_port_range="1024 65535"
    sysctl -w fs.file-max=1048576
    sysctl -w fs.inotify.max_user_instances=16384
    sysctl -w fs.inotify.max_user_watches=524288
    sysctl -w fs.inotify.max_queued_events=16384
image: busybox
imagePullPolicy: IfNotPresent
name: init-sysctl
securityContext:
  capabilities:
    add:
    - SYS_ADMIN
    drop:
    - ALL
......

由於我們這里使用的是 Helm Chart 安裝的 ingress-nginx,同樣只需要去配置 Values 值即可,模板中提供了對 initContainers 的支持,配置如下所示:

controller:
  # 其他省略,配置 initContainers
  extraInitContainers:
  - name: init-sysctl
    image: busybox
    securityContext:
      capabilities:
        add:
        - SYS_ADMIN
        drop:
        - ALL
    command:
    - /bin/sh
    - -c
    - |
      mount -o remount rw /proc/sys
      sysctl -w net.core.somaxconn=65535  # socket監聽的backlog上限
      sysctl -w net.ipv4.tcp_tw_reuse=1  # 開啟重用,允許將 TIME-WAIT sockets 重新用於新的TCP連接
      sysctl -w net.ipv4.ip_local_port_range="1024 65535"
      sysctl -w fs.file-max=1048576
      sysctl -w fs.inotify.max_user_instances=16384
      sysctl -w fs.inotify.max_user_watches=524288
      sysctl -w fs.inotify.max_queued_events=16384

同樣重新部署即可:

➜ helm upgrade --install ingress-nginx . -f ./ci/daemonset-prod.yaml --namespace ingress-nginx

部署完成后通過 initContainers 就可以修改節點內核參數了,生產環境建議對節點內核參數進行相應的優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM