1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        真一文搞定 ingress-nginx 的使用

        共 12940字,需瀏覽 26分鐘

         ·

        2020-12-18 15:52

        前面我們學(xué)習(xí)了在 Kubernetes 集群內(nèi)部使用 kube-dns 實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的功能,那么我們部署在 Kubernetes 集群中的應(yīng)用如何暴露給外部的用戶使用呢?我們知道可以使用 NodePortLoadBlancer 類型的 Service 可以把應(yīng)用暴露給外部用戶使用,除此之外,Kubernetes 還為我們提供了一個(gè)非常重要的資源對象可以用來暴露服務(wù)給外部用戶,那就是 Ingress。對于小規(guī)模的應(yīng)用我們使用 NodePort 或許能夠滿足我們的需求,但是當(dāng)你的應(yīng)用越來越多的時(shí)候,你就會(huì)發(fā)現(xiàn)對于 NodePort 的管理就非常麻煩了,這個(gè)時(shí)候使用 Ingress 就非常方便了,可以避免管理大量的端口。

        Ingress 其實(shí)就是從 Kuberenets 集群外部訪問集群的一個(gè)入口,將外部的請求轉(zhuǎn)發(fā)到集群內(nèi)不同的 Service 上,其實(shí)就相當(dāng)于 nginx、haproxy 等負(fù)載均衡代理服務(wù)器,可能你會(huì)覺得我們直接使用 nginx 就實(shí)現(xiàn)了,但是只使用 nginx 這種方式有很大缺陷,每次有新服務(wù)加入的時(shí)候怎么改 Nginx 配置?不可能讓我們?nèi)ナ謩?dòng)更改或者滾動(dòng)更新前端的 Nginx Pod 吧?那我們再加上一個(gè)服務(wù)發(fā)現(xiàn)的工具比如 consul 如何?貌似是可以,對吧?Ingress 實(shí)際上就是這樣實(shí)現(xiàn)的,只是服務(wù)發(fā)現(xiàn)的功能自己實(shí)現(xiàn)了,不需要使用第三方的服務(wù)了,然后再加上一個(gè)域名規(guī)則定義,路由信息的刷新依靠 Ingress Controller 來提供。

        Ingress Controller 可以理解為一個(gè)監(jiān)聽器,通過不斷地監(jiān)聽 kube-apiserver,實(shí)時(shí)的感知后端 Service、Pod 的變化,當(dāng)?shù)玫竭@些信息變化后,Ingress Controller 再結(jié)合 Ingress 的配置,更新反向代理負(fù)載均衡器,達(dá)到服務(wù)發(fā)現(xiàn)的作用。其實(shí)這點(diǎn)和服務(wù)發(fā)現(xiàn)工具 consul、 consul-template 非常類似。

        ingress flow

        現(xiàn)在可以供大家使用的 Ingress Controller 有很多,比如 traefik、nginx-controller、Kubernetes Ingress Controller for Kong、HAProxy Ingress controller,當(dāng)然你也可以自己實(shí)現(xiàn)一個(gè) Ingress Controller,現(xiàn)在普遍用得較多的是 traefik 和 nginx-controller,traefik 的性能較 nginx-controller 差,但是配置使用要簡單許多,我們這里會(huì)重點(diǎn)給大家介紹 nginx-controller 的使用。

        安裝

        NGINX Ingress Controller 是使用 Kubernetes Ingress 資源對象構(gòu)建的,用 ConfigMap 來存儲(chǔ) Nginx 配置的一種 Ingress Controller 實(shí)現(xiàn)。

        要使用 Ingress 對外暴露服務(wù),就需要提前安裝一個(gè) Ingress Controller,我們這里就先來安裝 NGINX Ingress Controller,由于 nginx-ingress 所在的節(jié)點(diǎn)需要能夠訪問外網(wǎng),這樣域名可以解析到這些節(jié)點(diǎn)上直接使用,所以需要讓 nginx-ingress 綁定節(jié)點(diǎn)的 80 和 443 端口,所以可以使用 hostPort 來進(jìn)行訪問,當(dāng)然對于線上環(huán)境來說為了保證高可用,一般是需要運(yùn)行多個(gè) nginx-ingress 實(shí)例的,然后可以用一個(gè) nginx/haproxy 作為入口,通過 keepalived 來訪問邊緣節(jié)點(diǎn)的 vip 地址。

        所謂的邊緣節(jié)點(diǎn)即集群內(nèi)部用來向集群外暴露服務(wù)能力的節(jié)點(diǎn),集群外部的服務(wù)通過該節(jié)點(diǎn)來調(diào)用集群內(nèi)部的服務(wù),邊緣節(jié)點(diǎn)是集群內(nèi)外交流的一個(gè) Endpoint。

        所以我們這里需要更改下資源清單文件:

        ??helm?repo?add?ingress-nginx?https://kubernetes.github.io/ingress-nginx
        ??helm?repo?update
        ??helm?fetch?ingress-nginx/ingress-nginx
        ??tar?-xvf?ingress-nginx-3.15.2.tgz

        我們這里測試環(huán)境只有 master1 節(jié)點(diǎn)可以訪問外網(wǎng),這里我們就直接講 ingress-nginx 固定到 master1 節(jié)點(diǎn)上,采用 hostNetwork 模式(生產(chǎn)環(huán)境可以使用 LB + DaemonSet hostNetwork 模式)。然后新建一個(gè)名為 values-prod.yaml 的 Values 文件,用來覆蓋 ingress-nginx 默認(rèn)的 Values 值,對應(yīng)的數(shù)據(jù)如下所示:

        #?values-prod.yaml
        controller:
        ??name:?controller
        ??image:
        ????repository:?cnych/ingress-nginx
        ????tag:?"v0.41.2"
        ????digest:?

        ??dnsPolicy:?ClusterFirstWithHostNet
        ???
        ??hostNetwork:?true

        ??publishService:??#?hostNetwork?模式下設(shè)置為false,通過節(jié)點(diǎn)IP地址上報(bào)ingress?status數(shù)據(jù)
        ????enabled:?false

        ??kind:?DaemonSet

        ??nodeSelector:?
        ????role:?lb

        ??service:??#?HostNetwork?模式不需要?jiǎng)?chuàng)建service
        ????enabled:?false

        defaultBackend:
        ??enabled:?true
        ??name:?defaultbackend
        ??image:
        ????repository:?cnych/ingress-nginx-defaultbackend
        ????tag:?"1.5"

        然后使用如下命令安裝 ingress-nginx 應(yīng)用到 ingress-nginx 的命名空間中:

        ??kubectl?create?ns?ingress-nginx
        ??helm?install?--namespace?ingress-nginx?ingress-nginx?./ingress-nginx?-f?./ingress-nginx/values-prod.yaml?
        NAME:?ingress-nginx
        LAST?DEPLOYED:?Fri?Dec?11?14:19:05?2020
        NAMESPACE:?ingress-nginx
        STATUS:?deployed
        REVISION:?1
        TEST?SUITE:?None
        NOTES:
        The?ingress-nginx?controller?has?been?installed.
        Get?the?application?URL?by?running?these?commands:
        ??export?POD_NAME=$(kubectl?--namespace?ingress-nginx?get?pods?-o?jsonpath="{.items[0].metadata.name}"?-l?"app=ingress-nginx,component=controller,release=ingress-nginx")
        ??kubectl?--namespace?ingress-nginx?port-forward?$POD_NAME?8080:80
        ??echo?"Visit?http://127.0.0.1:8080?to?access?your?application."

        An?example?Ingress?that?makes?use?of?the?controller:

        ??apiVersion:?networking.k8s.io/v1beta1
        ??kind:?Ingress
        ??metadata:
        ????annotations:
        ??????kubernetes.io/ingress.class:?nginx
        ????name:?example
        ????namespace:?foo
        ??spec:
        ????rules:
        ??????-?host:?www.example.com
        ????????http:
        ??????????paths:
        ????????????-?backend:
        ????????????????serviceName:?exampleService
        ????????????????servicePort:?80
        ??????????????path:?/
        ????#?This?section?is?only?required?if?TLS?is?to?be?enabled?for?the?Ingress
        ????tls:
        ????????-?hosts:
        ????????????-?www.example.com
        ??????????secretName:?example-tls

        If?TLS?is?enabled?for?the?Ingress,?a?Secret?containing?the?certificate?and?key?must?also?be?provided:

        ??apiVersion:?v1
        ??kind:?Secret
        ??metadata:
        ????name:?example-tls
        ????namespace:?foo
        ??data:
        ????tls.crt:?
        ????tls.key:?
        ??type:?kubernetes.io/tls

        部署完成后查看 Pod 的運(yùn)行狀態(tài):

        ??kubectl?get?svc?-n?ingress-nginx
        NAME?????????????????????????????????TYPE????????CLUSTER-IP???????EXTERNAL-IP???PORT(S)???AGE
        ingress-nginx-controller-admission???ClusterIP???10.110.143.167???????????443/TCP???2m21s
        ingress-nginx-defaultbackend?????????ClusterIP???10.104.156.141???????????80/TCP????2m21s
        ??kubectl?get?pods?-n?ingress-nginx
        NAME????????????????????????????????????????????READY???STATUS????RESTARTS???AGE
        ingress-nginx-controller-596955b554-vfmhq???????1/1?????Running???0??????????31s
        ingress-nginx-defaultbackend-7bf9445d94-lkgw5???1/1?????Running???0??????????3m52s
        ??POD_NAME=$(kubectl?get?pods?-l?app.kubernetes.io/name=ingress-nginx?-n?ingress-nginx?-o?jsonpath='{.items[0].metadata.name}')
        ??kubectl?exec?-it?$POD_NAME?-n?ingress-nginx?--?/nginx-ingress-controller?--version
        -------------------------------------------------------------------------------
        NGINX?Ingress?controller
        ??Release:???????v0.41.2
        ??Build:?????????d8a93551e6e5798fc4af3eb910cef62ecddc8938
        ??Repository:????https://github.com/kubernetes/ingress-nginx
        ??nginx?version:?nginx/1.19.4

        -------------------------------------------------------------------------------

        當(dāng)看到上面的信息證明 ingress-nginx 部署成功了。

        Ingress

        安裝成功后,現(xiàn)在我們來為一個(gè) nginx 應(yīng)用創(chuàng)建一個(gè) Ingress 資源,如下所示:

        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:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?my-nginx
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        spec:
        ??rules:
        ??-?host:?ngdemo.qikqiak.com??#?將域名映射到?my-nginx?服務(wù)
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?my-nginx??#?將所有請求發(fā)送到?my-nginx?服務(wù)的?80?端口
        ??????????servicePort:?80?????#?不過需要注意大部分Ingress?controller都不是直接轉(zhuǎn)發(fā)到Service
        ????????????????????????????#?而是只是通過Service來獲取后端的Endpoints列表,直接轉(zhuǎn)發(fā)到Pod,這樣可以減少網(wǎng)絡(luò)跳轉(zhuǎn),提高性能

        直接創(chuàng)建上面的資源對象:

        ??kubectl?apply?-f?ngdemo.yaml
        deployment.apps?"my-nginx"?created
        service?"my-nginx"?created
        ingress.extensions?"my-nginx"?created

        注意我們在 Ingress 資源對象中添加了一個(gè) annotations:kubernetes.io/ingress.class: "nginx",這就是指定讓這個(gè) Ingress 通過 nginx-ingress 來處理。

        上面資源創(chuàng)建成功后,然后我們可以將域名 ngdemo.qikqiak.com 解析到 ingress-nginx 所在的邊緣節(jié)點(diǎn)中的任意一個(gè),當(dāng)然也可以在本地/etc/hosts中添加對應(yīng)的映射也可以,然后就可以通過域名進(jìn)行訪問了。

        ngdemo

        下圖顯示了客戶端是如果通過 Ingress Controller 連接到其中一個(gè) Pod 的流程,客戶端首先對 ngdemo.qikqiak.com 執(zhí)行 DNS 解析,得到 Ingress Controller 所在節(jié)點(diǎn)的 IP,然后客戶端向 Ingress Controller 發(fā)送 HTTP 請求,然后根據(jù) Ingress 對象里面的描述匹配域名,找到對應(yīng)的 Service 對象,并獲取關(guān)聯(lián)的 Endpoints 列表,將客戶端的請求轉(zhuǎn)發(fā)給其中一個(gè) Pod。

        ingress controller workflow

        URL Rewrite

        NGINX Ingress Controller 很多高級(jí)的用法可以通過 Ingress 對象的 annotation 進(jìn)行配置,比如常用的 URL Rewrite 功能,比如我們有一個(gè) todo 的前端應(yīng)用,代碼位于 https://github.com/cnych/todo-app,直接部署這個(gè)應(yīng)用進(jìn)行測試:

        ??kubectl?apply?-f?https://github.com/cnych/todo-app/raw/master/k8s/mongo.yaml
        ??kubectl?apply?-f?https://github.com/cnych/todo-app/raw/master/k8s/web.yaml
        ??kubectl?get?pods
        NAME????????????????????????READY???STATUS????RESTARTS???AGE
        mongo-5c9fd978bb-txn9j??????1/1?????Running???0??????????149m
        todo-566957d785-tdgs6???????1/1?????Running???0??????????3m31s
        ......
        ??kubectl?get?svc
        NAME???????????????????????TYPE????????CLUSTER-IP???????EXTERNAL-IP???PORT(S)??????????????????????AGE
        kubernetes?????????????????ClusterIP???10.96.0.1????????????????443/TCP??????????????????????54d
        mongo??????????????????????ClusterIP???10.96.95.11??????????????27017/TCP????????????????????150m
        todo???????????????????????ClusterIP???10.111.105.47????????????3000/TCP?????????????????????145m
        ......

        對應(yīng)的 Ingress 資源對象如下所示:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        spec:
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000

        就是一個(gè)很常規(guī)的 Ingress 對象,部署該對象后,將域名解析后就可以正常訪問到應(yīng)用:

        ingress nginx demo

        現(xiàn)在我們需要對訪問的 URL 路徑做一個(gè) Rewrite,比如在 PATH 中添加一個(gè) app 的前綴,關(guān)于 Rewrite 的操作在 ingress-nginx 官方文檔中也給出對應(yīng)的說明:

        ingress nginx rewrite

        按照要求我們需要在 path 中匹配前綴 app,然后通過 rewrite-target 指定目標(biāo),修改后的 Ingress 對象如下所示:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??namespace:?default
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????nginx.ingress.kubernetes.io/rewrite-target:?/$2
        spec:
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        ????????path:?/app(/|$)(.*)

        更新后,我們可以遇見到直接訪問域名肯定是不行了,因?yàn)槲覀儧]有匹配 / 的 path 路徑:

        ingress nginx rewrite 404

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

        ingress nginx rewrite 1

        我們可以看到已經(jīng)可以訪問到頁面內(nèi)容了,這是因?yàn)槲覀冊?path 中通過正則表達(dá)式 /app(/|$)(.*) 將匹配的路徑設(shè)置成了 rewrite-target 的目標(biāo)路徑了,所以我們訪問 todo.qikqiak.com/app 的時(shí)候?qū)嶋H上相當(dāng)于訪問的就是后端服務(wù)的 / 路徑,但是我們也可以發(fā)現(xiàn)現(xiàn)在頁面的樣式?jīng)]有了:

        ingress nginx rewrite 2

        這是因?yàn)閼?yīng)用的靜態(tài)資源路徑是在 /stylesheets 路徑下面的,現(xiàn)在我們做了 url rewrite 過后,要正常訪問也需要帶上前綴才可以:http://todo.qikqiak.com/stylesheets/screen.css,對于圖片或者其他靜態(tài)資源也是如此,當(dāng)然我們?nèi)ジ捻撁嬉腱o態(tài)資源的方式為相對路徑也是可以的,但是畢竟要修改代碼,這個(gè)時(shí)候我們可以借助 ingress-nginx 中的 configuration-snippet 來對靜態(tài)資源做一次跳轉(zhuǎn),如下所示:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??namespace:?default
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????nginx.ingress.kubernetes.io/rewrite-target:?/$2
        ????nginx.ingress.kubernetes.io/configuration-snippet:?|
        ??????rewrite?^/stylesheets/(.*)$?/app/stylesheets/$1?redirect;??#?添加?/app?前綴
        ??????rewrite?^/images/(.*)$?/app/images/$1?redirect;??#?添加?/app?前綴
        spec:
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        ????????path:?/app(/|$)(.*)

        更新 Ingress 對象后,這個(gè)時(shí)候我們刷新頁面可以看到已經(jīng)正常了:

        ingress nginx rewrite 3

        要解決我們訪問主域名出現(xiàn) 404 的問題,我們可以給應(yīng)用設(shè)置一個(gè) app-root 的注解,這樣當(dāng)我們訪問主域名的時(shí)候會(huì)自動(dòng)跳轉(zhuǎn)到我們指定的 app-root 目錄下面,如下所示:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??namespace:?default
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????nginx.ingress.kubernetes.io/app-root:?/app/
        ????nginx.ingress.kubernetes.io/rewrite-target:?/$2
        ????nginx.ingress.kubernetes.io/configuration-snippet:?|
        ??????rewrite?^/stylesheets/(.*)$?/app/stylesheets/$1?redirect;??#?添加?/app?前綴
        ??????rewrite?^/images/(.*)$?/app/images/$1?redirect;??#?添加?/app?前綴
        spec:
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        ????????path:?/app(/|$)(.*)

        這個(gè)時(shí)候我們更新應(yīng)用后訪問主域名 http://todo.qikqiak.com 就會(huì)自動(dòng)跳轉(zhuǎn)到 http://todo.qikqiak.com/app/ 路徑下面去了。但是還有一個(gè)問題是我們的 path 路徑其實(shí)也匹配了 /app 這樣的路徑,可能我們更加希望我們的應(yīng)用在最后添加一個(gè) / 這樣的 slash,同樣我們可以通過 configuration-snippet 配置來完成,如下 Ingress 對象:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??namespace:?default
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????nginx.ingress.kubernetes.io/app-root:?/app/
        ????nginx.ingress.kubernetes.io/rewrite-target:?/$2
        ????nginx.ingress.kubernetes.io/configuration-snippet:?|
        ??????rewrite?^(/app)$?$1/?redirect;
        ??????rewrite?^/stylesheets/(.*)$?/app/stylesheets/$1?redirect;
        ??????rewrite?^/images/(.*)$?/app/images/$1?redirect;
        spec:
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        ????????path:?/app(/|$)(.*)

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

        Basic Auth

        同樣我們還可以在 Ingress Controller 上面配置一些基本的 Auth 認(rèn)證,比如 Basic Auth,可以用 htpasswd 生成一個(gè)密碼文件來驗(yàn)證身份驗(yàn)證。

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

        然后根據(jù)上面的 auth 文件創(chuàng)建一個(gè) 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:?Zm9vOiRhcHIxJFNjcVhZcFN6JDc4Nm5ISFNaeDdwN2VscDM2WUo0YS8K
        kind:?Secret
        metadata:
        ??creationTimestamp:?"2019-12-08T06:40:39Z"
        ??name:?basic-auth
        ??namespace:?default
        ??resourceVersion:?"9197951"
        ??selfLink:?/api/v1/namespaces/default/secrets/basic-auth
        ??uid:?6b2aa299-b511-412e-85ea-d0e91e578af0
        type:?Opaque

        然后對上面的 my-nginx 應(yīng)用創(chuàng)建一個(gè)具有 Basic Auth 的 Ingress 對象:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?ingress-with-auth
        ??annotations:
        ????#?認(rèn)證類型
        ????nginx.ingress.kubernetes.io/auth-type:?basic
        ????#?包含?user/password?定義的?secret?對象名
        ????nginx.ingress.kubernetes.io/auth-secret:?basic-auth
        ????#?要顯示的帶有適當(dāng)上下文的消息,說明需要身份驗(yàn)證的原因
        ????nginx.ingress.kubernetes.io/auth-realm:?'Authentication?Required?-?foo'
        spec:
        ??rules:
        ??-?host:?foo.bar.com
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?my-nginx
        ??????????servicePort:?80

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

        ??curl?-v?http://k8s.qikqiak.com?-H?'Host:?foo.bar.com'
        *?Rebuilt?URL?to:?http://k8s.qikqiak.com/
        *???Trying?123.59.188.12...
        *?TCP_NODELAY?set
        *?Connected?to?k8s.qikqiak.com?(123.59.188.12)?port?80?(#0)
        >?GET?/?HTTP/1.1
        >?Host:?foo.bar.com
        >?User-Agent:?curl/7.54.0
        >?Accept:?*/*
        >
        <

        401?Authorization?Required

        401?Authorization?Required



        openresty/1.15.8.2



        我們可以看到出現(xiàn)了 401 認(rèn)證失敗錯(cuò)誤,然后帶上我們配置的用戶名和密碼進(jìn)行認(rèn)證:

        ??curl?-v?http://k8s.qikqiak.com?-H?'Host:?foo.bar.com'?-u?'foo:foo'
        *?Rebuilt?URL?to:?http://k8s.qikqiak.com/
        *???Trying?123.59.188.12...
        *?TCP_NODELAY?set
        *?Connected?to?k8s.qikqiak.com?(123.59.188.12)?port?80?(#0)
        *?Server?auth?using?Basic?with?user?'foo'
        >?GET?/?HTTP/1.1
        >?Host:?foo.bar.com
        >?Authorization:?Basic?Zm9vOmZvbw==
        >?User-Agent:?curl/7.54.0
        >?Accept:?*/*
        >
        <



        Welcome?to?nginx!



        Welcome?to?nginx!


        If?you?see?this?page,?the?nginx?web?server?is?successfully?installed?and
        working.?Further?configuration?is?required.



        For?online?documentation?and?support?please?refer?to
        nginx.org.

        Commercial?support?is?available?at
        nginx.com.



        Thank?you?for?using?nginx.




        可以看到已經(jīng)認(rèn)證成功了。當(dāng)然出來 Basic Auth 這一種簡單的認(rèn)證方式之外,NGINX Ingress Controller 還支持一些其他高級(jí)的認(rèn)證,比如 OAUTH 認(rèn)證之類的。

        灰度發(fā)布

        在日常工作中我們經(jīng)常需要對服務(wù)進(jìn)行版本更新升級(jí),所以我們經(jīng)常會(huì)使用到滾動(dòng)升級(jí)、藍(lán)綠發(fā)布、灰度發(fā)布等不同的發(fā)布操作。而 ingress-nginx 支持通過 Annotations 配置來實(shí)現(xiàn)不同場景下的灰度發(fā)布和測試,可以滿足金絲雀發(fā)布、藍(lán)綠部署與 A/B 測試等業(yè)務(wù)場景。ingress-nginx 的 Annotations 支持以下 4 種 Canary 規(guī)則:

        • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,適用于灰度發(fā)布以及 A/B 測試。當(dāng) Request Header 設(shè)置為 always 時(shí),請求將會(huì)被一直發(fā)送到 Canary 版本;當(dāng) Request Header 設(shè)置為 never時(shí),請求不會(huì)被發(fā)送到 Canary 入口;對于任何其他 Header 值,將忽略 Header,并通過優(yōu)先級(jí)將請求與其他金絲雀規(guī)則進(jìn)行優(yōu)先級(jí)的比較。
        • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 將請求路由到 Canary Ingress 中指定的服務(wù)。當(dāng) Request Header 設(shè)置為此值時(shí),它將被路由到 Canary 入口。該規(guī)則允許用戶自定義 Request Header 的值,必須與上一個(gè) annotation (即:canary-by-header) 一起使用。
        • nginx.ingress.kubernetes.io/canary-weight:基于服務(wù)權(quán)重的流量切分,適用于藍(lán)綠部署,權(quán)重范圍 0 - 100 按百分比將請求路由到 Canary Ingress 中指定的服務(wù)。權(quán)重為 0 意味著該金絲雀規(guī)則不會(huì)向 Canary 入口的服務(wù)發(fā)送任何請求,權(quán)重為 100 意味著所有請求都將被發(fā)送到 Canary 入口。
        • nginx.ingress.kubernetes.io/canary-by-cookie:基于 cookie 的流量切分,適用于灰度發(fā)布與 A/B 測試。用于通知 Ingress 將請求路由到 Canary Ingress 中指定的服務(wù)的cookie。當(dāng) cookie 值設(shè)置為 always 時(shí),它將被路由到 Canary 入口;當(dāng) cookie 值設(shè)置為 never 時(shí),請求不會(huì)被發(fā)送到 Canary 入口;對于任何其他值,將忽略 cookie 并將請求與其他金絲雀規(guī)則進(jìn)行優(yōu)先級(jí)的比較。

        需要注意的是金絲雀規(guī)則按優(yōu)先順序進(jìn)行排序:canary-by-header - > canary-by-cookie - > canary-weight

        總的來說可以把以上的四個(gè) annotation 規(guī)則劃分為以下兩類:

        • 基于權(quán)重的 Canary 規(guī)則

        • 基于用戶請求的 Canary 規(guī)則

        下面我們通過一個(gè)示例應(yīng)用來對灰度發(fā)布功能進(jìn)行說明。

        第一步. 部署 Production 應(yīng)用

        首先創(chuàng)建一個(gè) production 環(huán)境的應(yīng)用資源清單:

        #?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

        然后創(chuàng)建一個(gè)用于 production 環(huán)境訪問的 Ingress 資源對象:

        #?production-ingress.yaml
        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?production
        ??annotations:
        ????kubernetes.io/ingress.class:?nginx
        spec:
        ??rules:
        ??-?host:?echo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?production
        ??????????servicePort:?80

        直接創(chuàng)建上面的幾個(gè)資源對象:

        ??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??????echo.qikqiak.com?????10.151.30.11???80??????90s

        應(yīng)用部署成功后,將域名 echo.qikqiak.com 映射到 master1 節(jié)點(diǎn)(ingress-nginx 所在的節(jié)點(diǎn))的外網(wǎng) IP,然后即可正常訪問應(yīng)用了:

        ??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-

        第二步. 創(chuàng)建 Canary 版本

        參考將上述 Production 版本的 production.yaml 文件,再創(chuàng)建一個(gè) Canary 版本的應(yīng)用。

        #?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 規(guī)則進(jìn)行流量切分了。

        第三步. Annotation 規(guī)則配置

        1. 基于權(quán)重:基于權(quán)重的流量切分的典型應(yīng)用場景就是藍(lán)綠部署,可通過將權(quán)重設(shè)置為 0 或 100 來實(shí)現(xiàn)。例如,可將 Green 版本設(shè)置為主要部分,并將 Blue 版本的入口配置為 Canary。最初,將權(quán)重設(shè)置為 0,因此不會(huì)將流量代理到 Blue 版本。一旦新版本測試和驗(yàn)證都成功后,即可將 Blue 版本的權(quán)重設(shè)置為 100,即所有流量從 Green 版本轉(zhuǎn)向 Blue。

        創(chuàng)建一個(gè)基于權(quán)重的 Canary 版本的應(yīng)用路由 Ingress 對象。

        #?canary-ingress.yaml
        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?canary
        ??annotations:
        ????kubernetes.io/ingress.class:?nginx?
        ????nginx.ingress.kubernetes.io/canary:?"true"???#?要開啟灰度發(fā)布機(jī)制,首先需要啟用?Canary
        ????nginx.ingress.kubernetes.io/canary-weight:?"30"??#?分配30%流量到當(dāng)前Canary版本
        spec:
        ??rules:
        ??-?host:?echo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?backend:
        ??????????serviceName:?canary
        ??????????servicePort:?80

        直接創(chuàng)建上面的資源對象即可:

        ??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????????????80/TCP???????????????????????8m23s
        production?????????????????ClusterIP???10.105.182.15????????????80/TCP???????????????????????22m
        ......
        ??kubectl?get?ingress
        NAME?????????CLASS????HOSTS????????????????ADDRESS????????PORTS???AGE
        canary??????????echo.qikqiak.com?????10.151.30.11???80??????108s
        production??????echo.qikqiak.com?????10.151.30.11???80??????22m

        Canary 版本應(yīng)用創(chuàng)建成功后,接下來我們在命令行終端中來不斷訪問這個(gè)應(yīng)用,觀察 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 版本應(yīng)用分配了 30% 左右權(quán)重的流量,所以上面我們訪問10次有3次訪問到了 Canary 版本的應(yīng)用,符合我們的預(yù)期。

        2. 基于 Request Header: 基于 Request Header 進(jìn)行流量切分的典型應(yīng)用場景即灰度發(fā)布或 A/B 測試場景。

        在上面的 Canary 版本的 Ingress 對象中新增一條 annotation 配置 nginx.ingress.kubernetes.io/canary-by-header: canary(這里的 value 可以是任意值),使當(dāng)前的 Ingress 實(shí)現(xiàn)基于 Request Header 進(jìn)行流量切分,由于 canary-by-header 的優(yōu)先級(jí)大于 canary-weight,所以會(huì)忽略原有的 canary-weight 的規(guī)則。

        annotations:
        ??kubernetes.io/ingress.class:?nginx?
        ??nginx.ingress.kubernetes.io/canary:?"true"???#?要開啟灰度發(fā)布機(jī)制,首先需要啟用?Canary
        ??nginx.ingress.kubernetes.io/canary-by-header:?canary??#?基于header的流量切分
        ??nginx.ingress.kubernetes.io/canary-weight:?"30"??#?會(huì)被忽略,因?yàn)榕渲昧?canary-by-headerCanary版本

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

        注意:當(dāng) Request Header 設(shè)置為 never 或 always 時(shí),請求將不會(huì)或一直被發(fā)送到 Canary 版本,對于任何其他 Header 值,將忽略 Header,并通過優(yōu)先級(jí)將請求與其他 Canary 規(guī)則進(jìn)行優(yōu)先級(jí)的比較。

        ??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

        這里我們在請求的時(shí)候設(shè)置了 canary: never 這個(gè) Header 值,所以請求沒有發(fā)送到 Canary 應(yīng)用中去。如果設(shè)置為其他值呢:

        ??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

        由于我們請求設(shè)置的 Header 值為 canary: other-value,所以 ingress-nginx 會(huì)通過優(yōu)先級(jí)將請求與其他 Canary 規(guī)則進(jìn)行優(yōu)先級(jí)的比較,我們這里也就會(huì)進(jìn)入 canary-weight: "30" 這個(gè)規(guī)則去。

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

        annotations:
        ??kubernetes.io/ingress.class:?nginx?
        ??nginx.ingress.kubernetes.io/canary:?"true"???#?要開啟灰度發(fā)布機(jī)制,首先需要啟用?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%流量到當(dāng)前Canary版本

        同樣更新 Ingress 對象后,重新訪問應(yīng)用,當(dāng) Request Header 滿足 canary: user-value時(shí),所有請求就會(huì)被路由到 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

        3. 基于 Cookie:與基于 Request Header 的 annotation 用法規(guī)則類似。例如在 A/B 測試場景下,需要讓地域?yàn)楸本┑挠脩粼L問 Canary 版本。那么當(dāng) cookie 的 annotation 設(shè)置為 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing",此時(shí)后臺(tái)可對登錄的用戶請求進(jìn)行檢查,如果該用戶訪問源來自北京則設(shè)置 cookie users_from_Beijing 的值為 always,這樣就可以確保北京的用戶僅訪問 Canary 版本。

        同樣我們更新 Canary 版本的 Ingress 資源對象,采用基于 Cookie 來進(jìn)行流量切分,

        annotations:
        ??kubernetes.io/ingress.class:?nginx?
        ??nginx.ingress.kubernetes.io/canary:?"true"???#?要開啟灰度發(fā)布機(jī)制,首先需要啟用?Canary
        ??nginx.ingress.kubernetes.io/canary-by-cookie:?"users_from_Beijing"??#?基于?cookie
        ??nginx.ingress.kubernetes.io/canary-weight:?"30"??#?會(huì)被忽略,因?yàn)榕渲昧?canary-by-cookie

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

        ??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

        我們可以看到應(yīng)用都被路由到了 Canary 版本的應(yīng)用中去了,如果我們將這個(gè) Cookie 值設(shè)置為 never,則不會(huì)路由到 Canary 應(yīng)用中。

        HTTPS

        如果我們需要用 HTTPS 來訪問我們這個(gè)應(yīng)用的話,就需要監(jiān)聽 443 端口了,同樣用 HTTPS 訪問應(yīng)用必然就需要證書,這里我們用 openssl 來創(chuàng)建一個(gè)自簽名的證書:

        ??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

        這個(gè)時(shí)候我們就可以創(chuàng)建一個(gè) HTTPS 訪問應(yīng)用的:

        apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?ingress-with-auth
        ??annotations:
        ????#?認(rèn)證類型
        ????nginx.ingress.kubernetes.io/auth-type:?basic
        ????#?包含?user/password?定義的?secret?對象名
        ????nginx.ingress.kubernetes.io/auth-secret:?basic-auth
        ????#?要顯示的帶有適當(dāng)上下文的消息,說明需要身份驗(yàn)證的原因
        ????nginx.ingress.kubernetes.io/auth-realm:?'Authentication?Required?-?foo'
        spec:
        ??rules:
        ??-?host:?foo.bar.com
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?my-nginx
        ??????????servicePort:?80
        ??tls:
        ??-?hosts:
        ????-?foo.bar.com
        ????secretName:?foo-tls

        除了自簽名證書或者購買正規(guī)機(jī)構(gòu)的 CA 證書之外,我們還可以通過 letsencrypt 來自動(dòng)生成合法的證書。

        CertManager 自動(dòng) HTTPS

        安裝配置

        cert-manager 是一個(gè)云原生證書管理開源項(xiàng)目,用于在 Kubernetes 集群中提供 HTTPS 證書并自動(dòng)續(xù)期,支持 Let's Encrypt/HashiCorp/Vault 這些免費(fèi)證書的簽發(fā)。在 Kubernetes 中,可以通過 Kubernetes Ingress 和 Let's Encrypt 實(shí)現(xiàn)外部服務(wù)的自動(dòng)化 HTTPS。

        cert-manager 架構(gòu)

        上面是官方給出的架構(gòu)圖,可以看到 cert-manager 在 Kubernetes 中定義了兩個(gè)自定義類型資源:Issuer(ClusterIssuer)Certificate。

        • 其中 Issuer 代表的是證書頒發(fā)者,可以定義各種提供者的證書頒發(fā)者,當(dāng)前支持基于 Let's Encrypt/HashiCorp/Vault 和 CA 的證書頒發(fā)者,還可以定義不同環(huán)境下的證書頒發(fā)者。
        • Certificate 代表的是生成證書的請求,一般其中存入生成證書的元信息,如域名等等。

        一旦在 Kubernetes 中定義了上述兩類資源,部署的 cert-manager 則會(huì)根據(jù) IssuerCertificate 生成 TLS 證書,并將證書保存進(jìn) Kubernetes 的 Secret 資源中,然后在 Ingress 資源中就可以引用到這些生成的 Secret 資源作為 TLS 證書使用,對于已經(jīng)生成的證書,還會(huì)定期檢查證書的有效期,如即將超過有效期,還會(huì)自動(dòng)續(xù)期。

        要在 Kubernetes 集群上安裝 cert-manager 也非常簡單,官方提供了一個(gè)單一的資源清單文件,包含了所有的資源對象,所以直接安裝即可:

        #?Kubernetes?1.16+
        ??kubectl?apply?-f?https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml
        #?Kubernetes?<1.16
        ??kubectl?apply?--validate=false?-f?https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager-legacy.yaml

        上面的命令會(huì)創(chuàng)建一個(gè)名為 cert-manager 的命名空間,安裝大量的 CRD 以及 AdmissionWebhook 對象,可以通過如下命令來查看是否安裝成功:

        ??kubectl?get?pods?-n?cert-manager
        NAME??????????????????????????????????????READY???STATUS????RESTARTS???AGE
        cert-manager-5597cff495-q6rzh?????????????1/1?????Running???0??????????5m31s
        cert-manager-cainjector-bd5f9c764-5sc7d???1/1?????Running???0??????????5m31s
        cert-manager-webhook-5f57f59fbc-mvcq4?????1/1?????Running???0??????????5m30s

        正常情況下可以看到 cert-manager、cert-manager-cainjector 以及 cert-manager-webhook 這幾個(gè) Pod 處于 Running 狀態(tài)。我們可以通過下面的測試來驗(yàn)證下是否可以簽發(fā)基本的證書類型,創(chuàng)建一個(gè) Issuer 資源對象來測試 webhook 工作是否正常(在開始簽發(fā)證書之前,必須在群集中至少配置一個(gè) Issuer 或 ClusterIssuer 資源):

        ??cat?<?>?test-selfsigned.yaml
        apiVersion:?v1
        kind:?Namespace
        metadata:
        ??name:?cert-manager-test
        ---
        apiVersion:?cert-manager.io/v1
        kind:?Issuer
        metadata:
        ??name:?test-selfsigned
        ??namespace:?cert-manager-test
        spec:
        ??selfSigned:?{}??#?配置自簽名的證書機(jī)構(gòu)類型
        ---
        apiVersion:?cert-manager.io/v1
        kind:?Certificate
        metadata:
        ??name:?selfsigned-cert
        ??namespace:?cert-manager-test
        spec:
        ??dnsNames:
        ??-?example.com
        ??secretName:?selfsigned-cert-tls
        ??issuerRef:
        ????name:?test-selfsigned
        EOF

        這里我們創(chuàng)建了一個(gè)名為 cert-manager-test 的命名空間,創(chuàng)建了一個(gè)自簽名的 Issuer 證書頒發(fā)機(jī)構(gòu),然后使用這個(gè) Issuer 來創(chuàng)建一個(gè)證書請求的 Certificate 對象,直接創(chuàng)建上面的資源清單即可:

        ??kubectl?apply?-f?test-selfsigned.yaml
        namespace/cert-manager-test?created
        issuer.cert-manager.io/test-selfsigned?created
        certificate.cert-manager.io/selfsigned-cert?created

        創(chuàng)建完成后可以檢查新創(chuàng)建的證書狀態(tài),在 cert-manager 處理證書請求之前,可能需要稍微等幾秒:

        ??kubectl?describe?certificate?-n?cert-manager-test
        Name:?????????selfsigned-cert
        Namespace:????cert-manager-test
        ......
        Spec:
        ??Dns?Names:
        ????example.com
        ??Issuer?Ref:
        ????Name:???????test-selfsigned
        ??Secret?Name:??selfsigned-cert-tls
        Status:
        ??Conditions:
        ????Last?Transition?Time:??2020-12-12T03:29:07Z
        ????Message:???????????????Certificate?is?up?to?date?and?has?not?expired
        ????Reason:????????????????Ready
        ????Status:????????????????True
        ????Type:??????????????????Ready
        ??Not?After:???????????????2021-03-12T03:29:06Z
        ??Not?Before:??????????????2020-12-12T03:29:06Z
        ??Renewal?Time:????????????2021-02-10T03:29:06Z
        ??Revision:????????????????1
        Events:
        ??Type????Reason?????Age???From??????????Message
        ??----????------?????----??----??????????-------
        ??Normal??Issuing????6s????cert-manager??Issuing?certificate?as?Secret?does?not?exist
        ??Normal??Generated??6s????cert-manager??Stored?new?private?key?in?temporary?Secret?resource?"selfsigned-cert-sppz7"
        ??Normal??Requested??6s????cert-manager??Created?new?CertificateRequest?resource?"selfsigned-cert-z4nvl"
        ??Normal??Issuing????5s????cert-manager??The?certificate?has?been?successfully?issued

        從上面的 Events 事件中我們可以證書已經(jīng)成功簽發(fā)了,生成的證書存放在一個(gè)名為 selfsigned-cert-tls 的 Secret 對象下面:

        ??kubectl?get?secret?-n?cert-manager-test????????????????????????????
        NAME??????????????????TYPE??????????????????????????????????DATA???AGE
        default-token-t928x???kubernetes.io/service-account-token???3??????64s
        selfsigned-cert-tls???kubernetes.io/tls?????????????????????3??????63s
        ??kubectl?get?secret?-n?cert-manager-test?selfsigned-cert-tls?-o?yaml
        apiVersion:?v1
        data:
        ??ca.crt:?......
        ??tls.crt:?......
        ??tls.key:?......
        kind:?Secret
        ......
        ??name:?selfsigned-cert-tls
        ??namespace:?cert-manager-test
        ??resourceVersion:?"13461084"
        ??selfLink:?/api/v1/namespaces/cert-manager-test/secrets/selfsigned-cert-tls
        ??uid:?42e456dc-6d34-4269-b207-f1f3bd50db8b
        type:?kubernetes.io/tls

        到這里證明我們的 cert-manager 已經(jīng)安裝成功了。我們需要注意的是 cert-manager 的功能非常強(qiáng)大,不只是可以支持 ACME 類型的證書簽發(fā),還支持其他眾多的類型,比如 SelfSigned(自簽名)、CA、Vault、Venafi、External、ACME,只是我們一般主要是使用 ACME 來幫我們生成自動(dòng)化的證書。

        下面我們就來使用 cert-manager 結(jié)合 ingress-nginx 為 Kubernetes 應(yīng)用自動(dòng)簽發(fā) Let's Encrypt 類型的 HTTPS 證書。

        自動(dòng)化 HTTPS

        Let's Encrypt 使用 ACME 協(xié)議來校驗(yàn)域名是否真的屬于你,校驗(yàn)成功后就可以自動(dòng)頒發(fā)免費(fèi)證書,證書有效期只有 90 天,在到期前需要再校驗(yàn)一次來實(shí)現(xiàn)續(xù)期,而 cert-manager 是可以自動(dòng)續(xù)期的,所以事實(shí)上并不用擔(dān)心證書過期的問題。目前主要有 HTTP 和 DNS 兩種校驗(yàn)方式。

        HTTP-01 校驗(yàn)

        HTTP-01 的校驗(yàn)是通過給你域名指向的 HTTP 服務(wù)增加一個(gè)臨時(shí) location,在校驗(yàn)的時(shí)候 Let's Encrypt 會(huì)發(fā)送 http 請求到 http:///.well-known/acme-challenge/,其中 YOUR_DOMAIN 就是被校驗(yàn)的域名,TOKEN 是 cert-manager 生成的一個(gè)路徑,它通過修改 Ingress 規(guī)則來增加這個(gè)臨時(shí)校驗(yàn)路徑并指向提供 TOKEN 的服務(wù)。Let's Encrypt 會(huì)對比 TOKEN 是否符合預(yù)期,校驗(yàn)成功后就會(huì)頒發(fā)證書了,不過這種方法不支持泛域名證書。

        使用 HTTP 校驗(yàn)這種方式,首先需要將域名解析配置好,也就是需要保證 ACME 服務(wù)端可以正常訪問到你的 HTTP 服務(wù)。這里我們以上面的 TODO 應(yīng)用為例,我們已經(jīng)將 todo.qikqiak.com 域名做好了正確的解析。

        由于 Let's Encrypt 的生產(chǎn)環(huán)境有著嚴(yán)格的接口調(diào)用限制,所以一般我們需要先在 staging 環(huán)境測試通過后,再切換到生產(chǎn)環(huán)境。首先我們創(chuàng)建一個(gè)全局范圍 staging 環(huán)境使用的 HTTP-01 校驗(yàn)方式的證書頒發(fā)機(jī)構(gòu):

        ??cat?<apiVersion:?cert-manager.io/v1
        kind:?ClusterIssuer
        metadata:
        ??name:?letsencrypt-staging-http01
        spec:
        ??acme:
        ????#?ACME?服務(wù)端地址
        ????server:?https://acme-staging-v02.api.letsencrypt.org/directory
        ????#?用于?ACME?注冊的郵箱
        ????email:[email protected]
        ????#?用于存放?ACME?帳號(hào)?private?key?的?secret
        ????privateKeySecretRef:
        ??????name:?letsencrypt-staging-http01
        ????solvers:
        ????-?http01:?#?ACME?HTTP-01?類型
        ????????ingress:
        ??????????class:?nginx??#?指定ingress的名稱
        EOF

        同樣再創(chuàng)建一個(gè)用于生產(chǎn)環(huán)境使用的 ClusterIssuer 對象:

        ??cat?<apiVersion:?cert-manager.io/v1
        kind:?ClusterIssuer
        metadata:
        ??name:?letsencrypt-http01
        spec:
        ??acme:
        ????server:?https://acme-v02.api.letsencrypt.org/directory
        ????email:[email protected]
        ????privateKeySecretRef:
        ??????name:?letsencrypt-http01
        ????solvers:
        ????-?http01:?
        ????????ingress:
        ??????????class:?nginx
        EOF

        創(chuàng)建完成后可以看到兩個(gè) ClusterIssuer 對象:

        ??kubectl?get?clusterissuer
        NAME?????????????????????????READY???AGE
        letsencrypt-http01???????????True????17s
        letsencrypt-staging-http01???True????3m12s

        有了 Issuer/ClusterIssuer 證書頒發(fā)機(jī)構(gòu),接下來我們就可以生成免費(fèi)證書了,cert-manager 給我們提供了 Certificate 這個(gè)用于生成證書的自定義資源對象,不過這個(gè)對象需要在一個(gè)具體的命名空間下使用,證書最終會(huì)在這個(gè)命名空間下以 Secret 的資源對象存儲(chǔ)。我們這里是要結(jié)合 ingress-nginx 一起使用,實(shí)際上我們只需要修改 Ingress 對象,添加上 cert-manager 的相關(guān)注解即可,不需要手動(dòng)創(chuàng)建 Certificate 對象了,修改上面的 todo 應(yīng)用的 Ingress 資源對象,如下所示:

        ??cat?<apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????cert-manager.io/cluster-issuer:?"letsencrypt-staging-http01"??#?使用哪個(gè)issuer
        spec:
        ??tls:
        ??-?hosts:
        ????-?todo.qikqiak.com?????#?TLS?域名
        ????secretName:?todo-tls???#?用于存儲(chǔ)證書的?Secret?對象名字?
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        EOF

        更新了上面的 Ingress 對象后,正常就會(huì)自動(dòng)為我們創(chuàng)建一個(gè) Certificate 對象:

        ??kubectl?get?certificate??????????????????????????????
        NAME???????READY???SECRET?????AGE
        todo-tls???False???todo-tls???34s

        在校驗(yàn)過程中會(huì)自動(dòng)創(chuàng)建一個(gè) Ingress 對象用于 ACME 服務(wù)端訪問:

        ??kubectl?get?ingress?????
        NAME????????????????????????CLASS????HOSTS????????????????ADDRESS????????PORTS?????AGE
        cm-acme-http-solver-tgwlb??????todo.qikqiak.com?????10.151.30.11???80????????25s
        my-nginx???????????????????????ngdemo.qikqiak.com???10.151.30.11???80????????23h
        todo???????????????????????????todo.qikqiak.com?????10.151.30.11???80,?443???33s

        校驗(yàn)成功后會(huì)將證書保存到 todo-tls 的 Secret 對象中:

        ??kubectl?get?certificate?????????????????????????
        NAME???????READY???SECRET?????AGE
        todo-tls???True????todo-tls???21m
        ??kubectl?get?secret?????????????????????????????????????????????????
        NAME??????????????????????????TYPE??????????????????????????????????DATA???AGE
        default-token-hpd7s???????????kubernetes.io/service-account-token???3??????55d
        todo-tls??????????????????????kubernetes.io/tls?????????????????????2??????20m
        ???kubectl?describe?certificate?todo-tls?
        Name:?????????todo-tls
        Namespace:????default
        ......
        Events:
        ??Type????Reason?????Age???From??????????Message
        ??----????------?????----??----??????????-------
        ??Normal??Issuing????22m???cert-manager??Issuing?certificate?as?Secret?does?not?exist
        ??Normal??Generated??22m???cert-manager??Stored?new?private?key?in?temporary?Secret?resource?"todo-tls-tr4pq"
        ??Normal??Requested??22m???cert-manager??Created?new?CertificateRequest?resource?"todo-tls-2gchg"
        ??Normal??Issuing????21m???cert-manager??The?certificate?has?been?successfully?issued

        證書自動(dòng)獲取成功后,現(xiàn)在就可以講 ClusterIssuer 替換成生產(chǎn)環(huán)境的了:

        ??cat?<apiVersion:?extensions/v1beta1
        kind:?Ingress
        metadata:
        ??name:?todo
        ??annotations:
        ????kubernetes.io/ingress.class:?"nginx"
        ????cert-manager.io/cluster-issuer:?"letsencrypt-http01"??#?使用生產(chǎn)環(huán)境的issuer
        spec:
        ??tls:
        ??-?hosts:
        ????-?todo.qikqiak.com?????#?TLS?域名
        ????secretName:?todo-tls???#?用于存儲(chǔ)證書的?Secret?對象名字?
        ??rules:
        ??-?host:?todo.qikqiak.com
        ????http:
        ??????paths:
        ??????-?path:?/
        ????????backend:
        ??????????serviceName:?todo
        ??????????servicePort:?3000
        EOF
        ??kubectl?get?certificate?????????????????????????
        NAME???????READY???SECRET?????AGE
        todo-tls???True????todo-tls???25m

        校驗(yàn)成功后就可以自動(dòng)獲取真正的 HTTPS 證書了,現(xiàn)在在瀏覽器中訪問 https://todo.qikqiak.com 就可以看到證書是有效的了。

        DNS-01 校驗(yàn)

        DNS-01 的校驗(yàn)是通過 DNS 提供商的 API 拿到你的 DNS 控制權(quán)限, 在 Let's Encrypt 為 cert-manager 提供 TOKEN 后,cert-manager 將創(chuàng)建從該 TOKEN 和你的帳戶密鑰派生的 TXT 記錄,并將該記錄放在 _acme-challenge.。然后 Let's Encrypt 將向 DNS 系統(tǒng)查詢該記錄,如果找到匹配項(xiàng),就可以頒發(fā)證書,這種方法是支持泛域名證書的。(不知道是不是公眾帳號(hào) bug 了,后面內(nèi)容添加不上了,提示內(nèi)容太多)

        參考鏈接

        • https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
        • https://v2-1.docs.kubesphere.io/docs/zh-CN/quick-start/ingress-canary/
        • https://cert-manager.io/docs/tutorials/acme/ingress/


        另外我們平臺(tái)的雙12課程優(yōu)惠活動(dòng)正在進(jìn)行中,今天最后一天優(yōu)惠,年底最后一次超車計(jì)劃,還不快上車嗎?點(diǎn)擊即刻參與~



        ?點(diǎn)擊屏末?|??|?即刻學(xué)習(xí)

        瀏覽 60
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            五月天婷婷久久 | 亚洲成人视频在线 | 国产精品久久久久久久久搜平片 | 久久婷婷免费视频 | 黄色成人视频网站 | 大桥未久av被撕裂的自尊观看 | 宝贝夹住乖含着吸出来男男 | 一级成人片 | 看一级操屄大黄片 | 91艹逼|