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>

        用 StatefulSet 實(shí)現(xiàn)同步服務(wù)和異步服務(wù)的管理

        共 4276字,需瀏覽 9分鐘

         ·

        2021-12-29 10:00


        一個(gè)服務(wù)里既有同步邏輯又有異步邏輯是非常常見的事,比如可以通過 Http 接口調(diào)用服務(wù)或者通過消息隊(duì)列傳遞消息來實(shí)現(xiàn)同樣的服務(wù)邏輯,同一套代碼邏輯區(qū)別只是入口不一樣。在 Golang 服務(wù)里我們用 goroutine 非常容易的起兩個(gè)入口;在 Python 服務(wù)里我們可以用多進(jìn)程也容易實(shí)現(xiàn)。

        現(xiàn)在我們看另外一種場(chǎng)景,服務(wù)同樣需要對(duì)外提供同步和異步入口,但是服務(wù)本身是計(jì)算密集型的且非常占用資源,典型的例子比如使用 GPU 的推理服務(wù)。首先推理服務(wù)啟動(dòng)時(shí)需要加載模型,而這一般需要很大的顯存可能達(dá)到數(shù)個(gè) G 甚至超過十個(gè) G。但是我們的單張顯卡顯存當(dāng)然是有上限的,就拿 T4 來說顯存只有 15 個(gè) G 可用,所以一個(gè)推理服務(wù)同時(shí)啟動(dòng)同步和異步可能顯存就不夠;影響更嚴(yán)重的一個(gè)問題是即使顯存足夠同時(shí)啟動(dòng)同步和異步服務(wù),但是當(dāng)同步和異步同時(shí)進(jìn)行計(jì)算是,對(duì)顯卡的計(jì)算資源會(huì)存在爭(zhēng)奪的情況,那么這樣計(jì)算速度可能會(huì)大大降低,嚴(yán)重降低了推理服務(wù)的性能。為了解決上述問題一個(gè)很自然的辦法就是同步和異步分別部署一套服務(wù)。對(duì)于一個(gè)這樣的服務(wù)說是沒問題的我們多加一個(gè)服務(wù)節(jié)點(diǎn)、一套 CI/CD 配置、一套服務(wù)配置、一套應(yīng)用配置等就完成了。但是有這樣需求的服務(wù)有多個(gè),并且數(shù)目會(huì)持續(xù)增加,那么分別部署同步和異步的成本和服務(wù)維護(hù)的成本就急劇增加。

        在這篇文章里我就分享下我們的解決辦法,如何在一個(gè)服務(wù)里(一個(gè)節(jié)點(diǎn),一套配置)獨(dú)立運(yùn)行同步和異步實(shí)例。(這里的實(shí)例指的是在 K8s 中一個(gè) deployment 里的一個(gè) Pod)。

        精確實(shí)例數(shù)量

        一個(gè)服務(wù)的總實(shí)例數(shù)目是通過配置定義的。假如我們配置的服務(wù)實(shí)例數(shù)是 10,然后在這 10 個(gè)實(shí)例當(dāng)中一部分是同步實(shí)例,一部分是異步實(shí)例。如何保證這些實(shí)例數(shù)量是按照我們要求分布的呢?

        按比例隨機(jī)

        假如同步和異步的實(shí)例數(shù)比例是 4:6, 在服務(wù)啟動(dòng)腳本里我們可以生成一個(gè) 1-10 的隨機(jī)整數(shù)然后跟同步實(shí)例數(shù)比例比較,如果小于等于 4 則以同步的方式啟動(dòng)實(shí)例,否則以異步的方式啟動(dòng)。示例腳本如下

        ??#!/bin/bash

        ??#?同步實(shí)例數(shù)比例(通過環(huán)境變量注入)
        ??SYNCS=$SYNC_RATE

        ??#?默認(rèn)啟動(dòng)同步實(shí)例
        ??if?[?-z?"$SYNCS"?];?then
        ????SYNCS=10
        ??fi
        ??Rand=$((?(?RANDOM?%?10?)??+?1?))

        ??if?[?"$Rand"?-le?"$SYNCS"?];?then
        ????#?啟動(dòng)grpc?server(同步)
        ????python?/data/app/grpc_server/server.py

        ??else
        ????#?啟動(dòng)任務(wù)進(jìn)程(異步)
        ????python?/data/app/start/__init__.py
        ??fi

        很明顯隨機(jī)啟動(dòng)無法準(zhǔn)確的控制實(shí)例數(shù)量,尤其是總實(shí)例數(shù)目較少的時(shí)候?qū)嵗植计钶^大。

        StatefulSet

        我們知道在 K8s 中除了常用的服務(wù)形式 Deployment 還有一種比較常用的服務(wù)形式是 StatefulSet, 后者相比前者來說最顯著的一個(gè)區(qū)別是 StatefulSet 下的 Pod 名字是也有序號(hào)的。例如一個(gè) StatefulSet 形式服務(wù)名是 web-service 且有 3 個(gè)實(shí)例,那么 3 個(gè)實(shí)例的名字分別是 web-service-0、web-service-1、web-service-2。我們可以正好利用 StatefulSet 這種 Pod 按編號(hào)順序啟動(dòng)的方式來控制同步和異步實(shí)例啟動(dòng)的數(shù)量。思路就是比如我們?cè)O(shè)置好了同步實(shí)例的數(shù)量,那么在每次實(shí)例啟動(dòng)的時(shí)候我們可以根據(jù)實(shí)例的編號(hào)和同步實(shí)例數(shù)做比較,如果編號(hào)比同步數(shù)小那么就以同步的方式啟動(dòng)實(shí)例,反之則以異步的方式啟動(dòng)。示例腳本如下

        ??#!/bin/bash

        ??#?pod?編號(hào)
        ??POD_NUM=`echo?${POD_NAME}?|?awk?-F'-'?'{print?$NF}'`
        ??#?同步實(shí)例數(shù)(通過環(huán)境變量注入)
        ??SYNCS=$SYNC_INSTANCE

        ??#?默認(rèn)同步方式(INSTANCE是期望的總實(shí)例數(shù))
        ??if?[?-z?"$SYNCS"?];?then
        ????SYNCS=$INSTANCE
        ??fi

        ??if?[?"$POD_NUM"?-lt?"$SYNCS"?];?then
        ????#?啟動(dòng)grpc?server(同步)
        ????python?/data/app/grpc_server/server.py

        ??else
        ????#?啟動(dòng)任務(wù)進(jìn)程(異步)
        ????python?/data/app/start/__init__.py
        ??fi

        所以通過上面的描述我們看到可以利用 StatefulSet 來精確控制同步和異步實(shí)例數(shù)量。

        容器多進(jìn)程管理

        在我們上面的例子中因?yàn)榉?wù)同時(shí)包含了同步實(shí)例和異步實(shí)例,同步實(shí)例有暴露端口的需求而異步實(shí)例是沒有對(duì)外暴露端口的,這樣帶來的矛盾就是如果服務(wù)配置了端口,則我們的基礎(chǔ)平臺(tái)會(huì)對(duì)異步服務(wù)配置的端口健康檢查和服務(wù)注冊(cè),結(jié)果就是異步服務(wù)必然健康檢查失敗而啟動(dòng)失敗。為了繞過基礎(chǔ)平臺(tái)的功能我們決定對(duì)服務(wù)不配置暴露端口,同步服務(wù)自己實(shí)現(xiàn)服務(wù)注冊(cè)的功能。即我們?cè)谕椒?wù)中除了啟動(dòng)服務(wù)進(jìn)程之外,再啟動(dòng)一個(gè)服務(wù)注冊(cè)管理進(jìn)程,實(shí)現(xiàn)服務(wù)注冊(cè)、服務(wù)心跳和服務(wù)注銷功能。上面這個(gè)情況比較特殊,可能其他同學(xué)并不會(huì)遇到,我們拿這個(gè)例子是為了說明在 K8s 容器中有多個(gè)進(jìn)程該怎么管理。上面說到除了服務(wù)進(jìn)程之外,容器中還存在一個(gè)服務(wù)注冊(cè)進(jìn)程,這個(gè)進(jìn)程必須實(shí)現(xiàn)在容器銷毀的時(shí)候必須向注冊(cè)中心注銷服務(wù)實(shí)例。在 K8s 中,Pod 停止時(shí) kubelet 會(huì)先給容器中的主進(jìn)程發(fā) SIGTERM 信號(hào)來通知進(jìn)程進(jìn)行 shutdown 以實(shí)現(xiàn)優(yōu)雅停止,如果超時(shí)進(jìn)程還未完全停止則會(huì)使用 SIGKILL 來強(qiáng)行終止。但是在我們的場(chǎng)景中我們的業(yè)務(wù)進(jìn)程是在腳本中啟動(dòng)的,容器的啟動(dòng)入口使用了腳本,所以容器中的主進(jìn)程并不是我們所希望的業(yè)務(wù)進(jìn)程而是 shell 進(jìn)程,導(dǎo)致業(yè)務(wù)進(jìn)程收不到 SIGTERM 信號(hào),自然就無法實(shí)現(xiàn)主動(dòng)注銷服務(wù)。我們利用 trap 來實(shí)現(xiàn)

        trap 捕捉信號(hào)

        通常 trap 都在腳本中使用,主要有 2 種功能:

        1. 忽略信號(hào)。當(dāng)運(yùn)行中的腳本進(jìn)程接收到某信號(hào)時(shí)(例如誤按了 CTRL+C),可以將其忽略,免得腳本執(zhí)行到一半就被終止
        2. 捕捉到信號(hào)后做相應(yīng)處理,比如清理創(chuàng)建的臨時(shí)文件

        常用信號(hào)

        Signal?????Value???Comment

        ─────────────────────────────

        SIGHUP??????1??????終止進(jìn)程,特別是終端退出時(shí),此終端內(nèi)的進(jìn)程都將被終止
        SIGINT??????2??????中斷進(jìn)程,幾乎等同于sigterm,會(huì)盡可能的釋放執(zhí)行clean-up,
        ???????????????????釋放資源,保存狀態(tài)等(CTRL+C)
        SIGQUIT?????3??????從鍵盤發(fā)出殺死(終止)進(jìn)程的信號(hào)
        SIGKILL?????9??????強(qiáng)制殺死進(jìn)程,該信號(hào)不可被捕捉和忽略,進(jìn)程收到該信號(hào)后不會(huì)
        ???????????????????執(zhí)行任何clean-up行為,所以資源不會(huì)釋放,狀態(tài)不會(huì)保存
        SIGTERM????15??????殺死(終止)進(jìn)程,幾乎等同于sigint信號(hào),會(huì)盡可能的釋放執(zhí)行
        ???????????????????clean-up,釋放資源,保存狀態(tài)等
        SIGSTOP????19??????該信號(hào)是不可被捕捉和忽略的進(jìn)程停止信息,收到信號(hào)后會(huì)進(jìn)入stopped狀態(tài)
        SIGTSTP????20??????該信號(hào)是可被忽略的進(jìn)程停止信號(hào)(CTRL+Z)

        trap 使用

        trap?[-lp]?[[arg]?signal_spec?...]
        -l ???打印信號(hào)名稱以及信號(hào)名稱對(duì)應(yīng)的數(shù)字。
        -p????顯示與每個(gè)信號(hào)關(guān)聯(lián)的trap命令。

        參數(shù)
        arg:接收到信號(hào)時(shí)執(zhí)行的命令或函數(shù)
        signal_spec:信號(hào)名稱或信號(hào)名稱對(duì)應(yīng)的數(shù)字

        通過上述介紹之后我們知道給容器多進(jìn)程傳遞信號(hào)方式為:可以在 shell 中使用 trap 來捕獲信號(hào),當(dāng)收到信號(hào)后觸發(fā)回調(diào)函數(shù)來將信號(hào)通過 kill 傳遞給業(yè)務(wù)進(jìn)程。示例腳本如下

        #!/bin/bash

        #?pod?編號(hào)
        POD_NUM=`echo?${POD_NAME}?|?awk?-F'-'?'{print?$NF}'`
        #?同步實(shí)例數(shù)
        SYNCS=$SYNC_INSTANCE

        if?[?-z?"$SYNCS"?];?then
        ??SYNCS=$INSTANCE
        fi

        if?[?"$POD_NUM"?-lt?"$SYNCS"?];?then
        ??#?啟動(dòng)grpc?server
        ??python?/data/app/grpc_server/server.py?&?pid1="$!"
        ??python?/data/app/grpc_server/register.py?&?pid2="$!"
        ??handle_sigterm()?{
        ????echo?"[INFO]?Received?SIGTERM"
        ????kill?-SIGTERM?$pid1?$pid2?#?傳遞?SIGTERM?給業(yè)務(wù)進(jìn)程
        ????wait?$pid1?$pid2?#?等待所有業(yè)務(wù)進(jìn)程完全終止
        ??}
        ??trap?handle_sigterm?TERM?#?捕獲?SIGTERM?信號(hào)并回調(diào)?handle_sigterm?函數(shù)
        ??wait?#?等待回調(diào)執(zhí)行完,主進(jìn)程再退出

        else
        ??#?啟動(dòng)任務(wù)進(jìn)程
        ??python?/data/app/start/__init__.py
        fi

        下圖是實(shí)現(xiàn)的效果

        原文鏈接:https://lxkaka.wang/service-manage/


        你可能還喜歡

        點(diǎn)擊下方圖片即可閱讀

        云原生優(yōu)質(zhì) Podcast 推薦大全,太強(qiáng)了!

        云原生是一種信仰???

        關(guān)注公眾號(hào)

        后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!



        點(diǎn)擊?"閱讀原文"?獲取更好的閱讀體驗(yàn)!


        發(fā)現(xiàn)朋友圈變“安靜”了嗎?

        瀏覽 42
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(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>
            小骚逼网站 | 9人成午夜电影 | 午夜视频三区 | 成人电影高清无码在线观看 | 我挺进她的下面疯狂运动 | 99热久 | 操嫩穴视频| 一区二区三区伦理片 | 五月天婷婷网址 | 无码国产传媒爱豆传媒人妻 |