<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      LoadBalancer在kubernetes架構下的實踐

      Backgound

      借助于kubernetes優秀的彈性擴縮功能,運行其中的應用程序能夠在流量突增的時候坦然應對,在流量低谷的時候無需擔心成本。但于此同時,也帶來了極大的挑戰: 彈性擴縮導致容器IP動態變化,客戶端無法直接依賴于容器IP進行訪問,我們必須通過某種方式固定流量入口,將流量通過該固定入口均衡地分發到后端,在容器擴縮的過程能夠隨著容器啟停動態更新后端地址。

      在這種場景下,我們自然而然地會想到廣泛使用的LoadBalancer。kubernetes中service資源其實就是一種LoadBalancer。 service可以會產生一個serviceIP,通過label selecter選定一組pod,流量會通過該serviceIp負載均衡到后端的pod。

      service有很多類型: ClusterIP,NodePort,LoadBalancer。在應用于實際復雜的業務場景,以上類型各有利弊:

      • ClusterIP 是通過分配一個虛擬IP給每個service,通過kube-proxy實現轉發,這個虛擬的IP在集群外無法被直接訪問到,只適合集群內部的互相調用。
      • NodePort 是通過將流量轉發到宿主機上,然后通過kube-proxy轉發到對應的pod, 每創建一個該類型的service就會占用一個宿主機的端口用做轉發。此種類型的service雖然可以實現集群外部訪問, 但是無法大規模應用,因為service比較多的時候,端口容易沖突,管理起來比較麻煩。
      • LodaBalacner會創建一個真實的LoadBalancer, 然后將流量轉發到NodePort service之上,因為此時NodePort端口對用戶透明,由kubernetes自動分配并管理,所以不存在上述提到的端口沖突的問題。 但是缺點就是性能,功能和擴展性
        • 性能不高: 需要經過nodePort的轉發,LB首先將流量轉發到其中某一臺node上面,然后再經過kube-proxy的轉發,如果pod沒有在這一臺機器上面,還需要再轉發一次到其他的node上面,如此一來就多了一跳。
        • 擴展性: 同時由于LoadBalancer會直接將所有的node掛載到LB之上,如果集群規模變大,到了幾百幾千臺就會達到LB的限制,無法繼續添加機器。社區雖然提供了externalTraficPolicy這種機制,只掛載pod所在的node到LB, 但是這樣會導致流量轉發不均衡,例如如果nodeA上面有兩個pod,nodeB上面有一個pod, LB是將流量平均的轉達到兩個node上面, 而不是根據pod數目設置不同的權重, 參見社區Caveats and Limitations when preserving source IPs
        • 功能: 會有源IP丟失的問題,在轉發過程中需要做SNAT和NAT, 在某些業務場景下無法滿足用戶需求。

      除了service之外,還有ingress用來實現負載均衡。 ingress本質上是一個代理,廣泛用于七層協議,對于一些四層或者gRPC類型的支持不太好。同時ingress controller容器本身也會發生容器漂移等現象,也需要一個四層的負載均衡動態地轉發流量到后端。

      Requirement

      明確了上述各種類型的service的特點之后,我們需要明確我們所需要的service到底是什么樣子,主要體現為: 功能,可用性,性能。

      功能

      能夠在集群外部被訪問到,將流量從外部均勻地傳遞到集群內的多個容器。這其實就是kubernetes中LoadBalacner類型的service,對于每一個service我們使用一個真實的負載均衡器,借助于公司內部的或者公有云廠商提供的負載均衡設備即可,這些產品一般都比較成熟。

      性能

      流量能夠高效地轉發到容器中,LoadBalancer作為底層基礎架構,需要滿足各種各樣業務對網絡性能的要求。流量能夠高效的轉發到容器內, 這點需要我們LB后端直接掛載容器,不用再經過NodePort或者iptable轉發, 對于這點我們需要對底層網絡有一定的要求,需要LB能夠連接到podIP上,需要VPC直連的容器網絡方案,而overlay方式的容器網絡在容器集群外是無法直接訪問的,此處就無法使用。不過一般情況下,真正在生產環境中被廣泛使用的也就是VPC直連的容器網絡方案,各個云廠商也有提供相應的解決方案。

      VPC直連的網絡方案現在被廣泛采用,不光是為了解決LB連接的問題, 還具有其他優勢:

      • 首先業務需要podIP可以被直接訪問到,便于架構上云時進行遷移,有些時候, 部分業務在容器里, 部分還在物理機上,他們需要能夠互通。
      • 性能需求,VPC直連的沒有overlay封包解包的性能損耗
      • 方便診斷,維護起來更加簡單,可以直接看做是物理機使用
      • 目前各個云廠商都有相關的CNI插件, 有利于多云架構的實現

      對于LB直接連接容器, 其實在之前的架構下也是這么做的,已經證明了可行性。 只是老的架構是通過在富容器中啟動一個agent,由agent注冊自身容器IP到LB。老架構由于設計的較早,當時容器還是被當作虛擬機使用,當時還沒有kubernetes,沒有controller模式, 隨著慢慢發展暴露出很多問題:

      • 權限管理難以實現, 分散在各個容器之中
      • 異常處理不集中, 在容器被暴力清理掉之后,來不及從LB上解綁就退出, 進而導致流量繼續轉發到該容器之中, 或者需要另一個異步清理的進程來實現清理
      • 系統調用耦合嚴重,接口難以升級, 升級接口需要重啟所有的容器
      • 耗費資源,每個富容器中都會有相關的agent

      由于老的架構設計較早,問題比較多,再重新思考這個問題的時候, 希望用云原生的方式,運用operater模式實現整個流程。

      可用性

      在容器動態擴縮過程中,需要保證流量平滑遷移,不能導致業務流量丟失。這是最基本的可用性保證。也是需要考慮最多的地方。kubernetes為了架構的簡單,將功能分成多個模塊異步執行,例如pod啟動和健康檢查是由kubelet負責,但是流量轉發是由kube-proxy負責,他們之間沒有直接的交互,這就會碰到分布式系統中執行時序的問題。如果容器還沒啟動流量就已經轉發過來了就導致流量的丟失,或者容器已經退出但流量繼續轉發過來也會導致流量的丟失,這些情況對于滾動更新的pod尤其明顯。 因為所有的操作都需要遠程調用來操作LoadBalaner, 我們不得不考慮執行速度帶來的影響。

      一般情況下對于容器啟動的時候我們無需過多擔心, 只有啟動之后才能接收流量, 需要擔心的容器退出的過程中,需要確保流量還沒有摘掉前容器不能退出,否則就會導致流量丟失。主要體現為兩點:

      • 滾動更新的過程中需要保證新版本容器正常接收到流量之后才能繼續滾動更新的過程,才能去刪除老版本容器。如果隨便kill掉老版本實例,此時新版本注冊還沒有生效, 就會導致流量的丟失。
      • 在退出的過程中需要等待流量完全摘除掉之后才能去刪除容器。
      滾動更新過程

      對于滾動更新, 該過程一般是由對應的workload controller負責的, 例如deployment,statfulSet。 以deployment滾動更新為例,如果不加干預整個流程為: 新版本pod啟動,readiness探針通過, controller將podIP掛載到LB上面, LB生效一般都需要時間,此時流量還不能轉發到新版本pod里面。于此同時deployment認為新容器已經就緒,就進行下一步,刪除掉老版本的pod。 此時新老版本都不能接收流量了,就導致了整個服務的不可用。這里根本原因是deployment認為pod就緒并不會考慮LB是否就緒,LB是k8s系統外部的資源,deployment并不認識。退一步來講,我們平時使用的InCluster類型的service也是有這個問題的,kubelet中容器退出和kube-proxy流量摘除似乎是同時進行的,并沒有時序保證,如果kube-proxy執行的稍微慢一點,kubelet中容器退出的稍微快一點,就會碰到流量丟失地情況。幸運的是目前kub-proxy是基于iptables實現的轉發,刷新iptables規則在一般情況下執行速度足夠快,我們很難碰到這種情況。 但是如果我們基于LoadBalancer直接掛載容器IP,就沒有這么幸運了,我們需要遠程調用操作LB,而且需要云廠商的LB生效都比較慢,鑒于此,我們需要想辦法等到LB就緒之后才能認為整個pod就緒, 即pod就緒等于容器就緒(健康檢查探針通過) + LB掛載就緒, pod就緒后才能進行滾動更新。

      社區也碰到過過這個問題,開發了Pod Readiness Gates(ready++)的特性,用戶可以通過 ReadinessGates 自定義 Pod 就緒的條件,當用戶自定義的條件以及容器狀態都就緒時,kubelet 才會標記 Pod 準備就緒。 如下所示,用戶需要設置readinessGate:

      apiVersion: extensions/v1beta1
      kind: Deployment
      metadata:
        labels:
          run: nginx
        name: nginx
      spec:
        replicas: 1
        selector:
          matchLabels:
            run: nginx
        template:
          metadata:
            labels:
              run: nginx
          spec:
            readinessGates:
            - conditionType: cloudnativestation.net/load-balancer-ready    # <- 這里設置readinessGatea
            containers:
            - image: nginx
              name: nginx
      
      

      當我們給deployment設置了readinessGate這個字段之后, 當pod啟動成功通過reainess的檢查之后,并不會認為整個pod已經就緒,因為此時LB還沒有就緒, 如果我們此時觀察pod的status會發現如下信息

        status:
          conditions:
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:34:18Z"
            status: "True"
            type: Initialized
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:34:18Z"
            message: corresponding condition of pod readiness gate "cloudnativestation.net/load-balancer-ready"
              does not exist.
            reason: ReadinessGatesNotReady
            status: "False"
            type: Ready                   # <--- Ready為False
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:34:20Z"
            status: "True"
            type: ContainersReady          # <--- container Ready為Ture
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:34:18Z"
            status: "True"
            type: PodScheduled
          containerStatuses:
          - containerID: docker://42e761fd53ccb2b2886c500295ceeff8f1d2ffc2376eb66dd95a436c395b95c0
            image: nginx:latest
            imageID: docker-pullable://nginx@sha256:2e6775f4300fc79b9d7fe6bb60c83b5fefe584258d9318ed408746789af48885
            lastState: {}
            name: nginx
            ready: true
            restartCount: 0
            state:
              running:
                startedAt: "2020-03-14T11:34:19Z"
      

      conditions信息中 ContainerReady為True, 但是Ready卻為False, message中提示"對應的readiness gate condition還不存在", 那我們只需要patch上對應的condition即可, 如下所示:

        status:
          conditions:
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:38:03Z"
            message: LB synced successfully
            reason: LBHealthy
            status: "True"
            type: cloudnativestation.net/load-balancer-ready       # <--- 增加readiness gate condtion
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:38:03Z"
            status: "True"
            type: Initialized
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:38:05Z"
            status: "True"
            type: Ready                                     # <--- pod狀態變為ready
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:38:05Z"
            status: "True"
            type: ContainersReady
          - lastProbeTime: null
            lastTransitionTime: "2020-03-14T11:38:03Z"
            status: "True"
            type: PodScheduled
          containerStatuses:
          - containerID: docker://65e894a7ef4e53c982bd02da9aee2ddae7c30e652c5bba0f36141876f4c30a01
            image: nginx:latest
            imageID: docker-pullable://nginx@sha256:2e6775f4300fc79b9d7fe6bb60c83b5fefe584258d9318ed4087467
      

      手動設置完readiness gate的condtion之后整個pod才能變為ready。

      容器退出過程

      對于容器退出的過程中, 我們需要及時將流量從LB上面摘除。 一個pod典型的退出流程為: 我們從控制臺下達刪除pod的命令時,apiserver會記錄pod deletionTimestamp 標記在pod的manifest中, 隨后開始執行刪除邏輯,首先發送SIGTERM 信號, 然后最大等待terminationGracePeriodSeconds發送SIGKILL信號強制清理, terminationGracePeriodSeconds該值用戶可以自行在pod的manifest中指定。
      結合整個退出過程,我們需要在監聽到容器退出開始時(也就是deletionTimestamp被標記時) 在LB上將該pod流量權重置為0, 這樣新建連接就不到達該容器,同時已有連接不受影響,可以繼續提供服務。等到容器真正退出時才將該pod從LB上面摘除。用戶如果想要更加安全的流量退出邏輯,可以設置一個稍長一點的terminationGracePeriodSeconds, 甚至設置prestop邏輯或者處理SIGTERM信號, 讓pod在退出前等待足夠長的時間將流量徹底斷掉,

      Action

      明確了整個架構中的關鍵點后,就是具體的實現環節了。 這部分我們可以借鑒社區提供的service controller及各個云廠商LB在kubernetes中的應用。 社區為了屏蔽掉不同云廠商產品的差異,開發了cloud-controller-manager, 其內部定義了很多接口, 各個云廠商只需要實現其中的接口就可以在合適的時候被調用。 對于LoadBalancer定義接口如下:

      // LoadBalancer is an abstract, pluggable interface for load balancers.
      type LoadBalancer interface {
      	// TODO: Break this up into different interfaces (LB, etc) when we have more than one type of service
      	// GetLoadBalancer returns whether the specified load balancer exists, and
      	// if so, what its status is.
      	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
      	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
      	GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error)
      	// GetLoadBalancerName returns the name of the load balancer. Implementations must treat the
      	// *v1.Service parameter as read-only and not modify it.
      	GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string
      	// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer
      	// Implementations must treat the *v1.Service and *v1.Node
      	// parameters as read-only and not modify them.
      	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
      	EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error)
      	// UpdateLoadBalancer updates hosts under the specified load balancer.
      	// Implementations must treat the *v1.Service and *v1.Node
      	// parameters as read-only and not modify them.
      	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
      	UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error
      	// EnsureLoadBalancerDeleted deletes the specified load balancer if it
      	// exists, returning nil if the load balancer specified either didn't exist or
      	// was successfully deleted.
      	// This construction is useful because many cloud providers' load balancers
      	// have multiple underlying components, meaning a Get could say that the LB
      	// doesn't exist even if some part of it is still laying around.
      	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
      	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
      	EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error
      }
      

      當用戶創建LoabBalancer類型的service時,cloud-controller-manager中的service controller就會利用informer監聽service的創建、更新、刪除事件,然后調用各個云廠商注冊的接口,云廠商只需要提供以上的接口就行了。

      對于Loadbalancer,具體各個廠商實現不同, 但是目前的實現基本都是直接掛載nodePort, 可以看到上述EnsureLoadBalancer中傳遞的參數也是nodes列表。 上述的接口我們無法直接使用,需要對其改造, 實現一個自定義的service controller。在EnsureLoadBalancer的時候傳遞的參數也應該是pod的IP列表, 我們掛載的是pod而不是node。所以此處需要不斷監聽pod的變化,然后選擇判斷該pod是否被service label selector選中,如果選中則該pod是service的后端,需要設置將流量轉發到該pod上面, 這里很多熟悉kubernetes的小伙伴就會好奇,這里不是和endpoints的功能一模一樣嗎? 為什么不直接監聽endpoint, 然后將endpoint中的ip列表拿出來直接使用?

      要弄明白這個問題,我們需要回顧我們在保證流量不丟的時候設置了readinessGate, 此時pod就緒狀態會變為: 容器就緒+LB就緒。但是在endpoint的工作原理中, endpoint controller會判斷pod是否就緒,pod就緒之后才會將podIP放在endpoint的結構體中。而我們期望容器就緒之后就在endpoint顯示出來,這樣我們就可以拿著這個enpoint的ip列表去注冊到LB上, LB注冊成功之后,pod才能變為就緒。 社區endpoint中iplist的順序和我們期望的略有差異, 只能自己實現一個類似的結構體了,和社區的使用方式大部分相同, 只是判斷就緒的邏輯略有不同。

      自定義endpoint的另外一個原因是: endpoint controller會將service選中的所有pod分為ready和unready兩組, 當pod剛啟動時, 還未通過readiness探針檢查時會將pod放置在unReadAddress列表中,通過readiness檢查后會移動到address列表中,隨后在退出時會直接將pod移出address列表中。 在我們的場景下,更加合理的邏輯應該是在退出過程中應該從endpoint中address列表移動到unReadyAddress列表,這樣我們就可以根據unReadyAddress來決定在退出的時候將哪些podIP在LB上面將權重置為0。

      自定義endpoint controller并沒有更改kubernetes原來的endpoint controller的代碼, 這里我們只是作為一個內部的數據結構體使用, 直接結合在service controller中即可,也無需監聽endpoint變化,直接監聽pod變化生成對應的service 即可。

      收獲

      在落地kubernetes的過程中, 相信kube-proxy被不少人詬病,甚至有不少公司完全拋棄了kube-proxy。 不好的東西我們就要積極探索一種更好,更適合公司內部情況的解決方案。目前該滿足了不同業務上云時的網絡需求,承載了不同的流量類型。 同時很好地應用在多云環境下,私有云和公有云下都可以適配, 盡管私有云或者公有云的底層網絡方案或者LB實現不同,但是整個架構相同,可以無縫地在私有云,aws, 阿里,金山云直接遷移。

      kubernetes的快速發展為我們帶來了很多驚喜,但是于此同時很多細節的地方需要打磨,需要時間的沉淀才能更加完美, 相信在落地kubernetes的過程中不少人被kubernetes的網絡模型所困擾,此時我們需要根據企業內部的情況, 結合已有的基礎設施,根據社區已經提供的和尚未提供的功能進行一些大膽的微創新,然后探索更多的可能性。

      posted @ 2020-05-24 15:04  gaorong404  閱讀(5654)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 欧美成人午夜在线观看视频| 另类 专区 欧美 制服| 99久久亚洲综合精品成人| 高潮喷水抽搐无码免费| 国产免费一区二区三区在线观看| 中文字幕无线码中文字幕免费| 国产仑乱无码内谢| 免费无码一区二区三区蜜桃| 色欲国产精品一区成人精品| 婷婷四房综合激情五月在线| 妓女妓女一区二区三区在线观看| 欧洲熟妇色xxxx欧美老妇多毛网站| 亚洲中文字幕有综合久久| 久久久亚洲精品无码| 91午夜福利一区二区三区 | 少妇xxxxx性开放| 亚洲精品久荜中文字幕| 欧美成人h精品网站| 麻豆一区二区中文字幕| 乌鲁木齐市| 国产老妇伦国产熟女老妇高清| 芦溪县| 国产91精品调教在线播放| 久久影院九九被窝爽爽| 99久久精品免费看国产电影| 99久久亚洲精品无码毛片| 天堂а√在线最新版中文在线| 国产午夜精品理论大片| 国产日韩精品一区二区三区在线| 大色综合色综合网站| 中文字幕在线亚洲日韩6页| 欧美性猛交xxxx乱大交丰满| 日本东京热不卡一区二区| 成人午夜大片免费看爽爽爽 | 四虎国产精品永久在线国在线| 99精品国产一区二区三| 久久天天躁狠狠躁夜夜婷 | 亚洲成女人图区一区二区| 国产区成人精品视频| 国产精品一区二区久久毛片| 在线 国产 欧美 专区|