Envoy 基礎(chǔ)教程:使用 Unix Domain Socket(UDS) 與上游集群通信

Envoy Proxy 在大多數(shù)情況下都是作為 Sidecar 與應(yīng)用部署在同一網(wǎng)絡(luò)環(huán)境中敌买,每個應(yīng)用只需要與 Envoy(localhost)交互,不需要知道其他服務(wù)的地址宽堆。然而這并不是 Envoy 僅有的使用場景澎粟,它本身就是一個七層代理,通過模塊化結(jié)構(gòu)實(shí)現(xiàn)了流量治理护侮、信息監(jiān)控等核心功能,比如流量治理功能就包括自動重連敬矩、熔斷概行、全局限速蠢挡、流量鏡像和異常檢測等多種高級功能弧岳,因此 Envoy 也常常被用于邊緣代理,比如 Istio 的 Ingress Gateway业踏、基于 Envoy 實(shí)現(xiàn)的 Ingress Controller(Contour禽炬、AmbassadorGloo 等)勤家。

我的博客也是部署在輕量級 Kubernetes 集群上的(其實(shí)是 k3s 啦)腹尖,一開始使用 Contour 作為 Ingress Controller,暴露集群內(nèi)的博客伐脖、評論等服務(wù)热幔。但好景不長,由于我在集群內(nèi)部署了各種奇奇怪怪的東西讼庇,有些個性化配置 Contour 無法滿足我的需求绎巨,畢竟大家都知道,每抽象一層就會丟失很多細(xì)節(jié)蠕啄。換一個 Controller 保不齊以后還會遇到這種問題场勤,索性就直接裸用 Envoy 作為邊緣代理,大不了手?jǐn)] YAML 唄歼跟。

當(dāng)然也不全是手?jǐn)]和媳,雖然沒有所謂的控制平面,但儀式感還是要有的哈街,我可以基于文件來動態(tài)更新配置啊留瞳,具體的方法參考 Envoy 基礎(chǔ)教程:基于文件系統(tǒng)動態(tài)更新配置

1. UDS 介紹

說了那么多廢話骚秦,下面進(jìn)入正題撼港。為了提高博客的性能坪它,我選擇將博客與 Envoy 部署在同一個節(jié)點(diǎn)上,并且全部使用 HostNetwork 模式帝牡,Envoy 通過 localhost 與博客所在的 Pod(Nginx) 通信往毡。為了進(jìn)一步提高性能,我盯上了 Unix Domain Socket(UDS靶溜,Unix域套接字)开瞭,它還有另一個名字叫 IPC(inter-process communication,進(jìn)程間通信)罩息。為了理解 UDS嗤详,我們先來建立一個簡單的模型。

現(xiàn)實(shí)世界中兩個人進(jìn)行信息交流的整個過程被稱作一次通信(Communication)瓷炮,通信的雙方被稱為端點(diǎn)(Endpoint)葱色。工具通訊環(huán)境的不同,端點(diǎn)之間可以選擇不同的工具進(jìn)行通信娘香,距離近可以直接對話苍狰,距離遠(yuǎn)可以選擇打電話、微信聊天烘绽。這些工具就被稱為 Socket淋昭。

同理,在計(jì)算機(jī)中也有類似的概念:

  • Unix 中翔忽,一次通信由兩個端點(diǎn)組成,例如 HTTP 服務(wù)端和 HTTP 客戶端。
  • 端點(diǎn)之間想要通信,必須借助某些工具,Unix 中端點(diǎn)之間使用 Socket 來進(jìn)行通信。

Socket 原本是為網(wǎng)絡(luò)通信而設(shè)計(jì)的奥喻,但后來在 Socket 的框架上發(fā)展出一種 IPC 機(jī)制憎兽,就是 UDS。使用 UDS 的好處顯而易見:不需要經(jīng)過網(wǎng)絡(luò)協(xié)議棧,不需要打包拆包碍粥、計(jì)算校驗(yàn)和矿瘦、維護(hù)序號和應(yīng)答等,只是將應(yīng)用層數(shù)據(jù)從一個進(jìn)程拷貝到另一個進(jìn)程搞动。這是因?yàn)椋?strong>IPC 機(jī)制本質(zhì)上是可靠的通訊箩溃,而網(wǎng)絡(luò)協(xié)議是為不可靠的通訊設(shè)計(jì)的瞭吃。

UDS 與網(wǎng)絡(luò) Socket 最明顯的區(qū)別在于碌嘀,網(wǎng)絡(luò) Socket 地址是 IP 地址加端口號,而 UDS 的地址是一個 Socket 類型的文件在文件系統(tǒng)中的路徑歪架,一般名字以 .sock 結(jié)尾股冗。這個 Socket 文件可以被系統(tǒng)進(jìn)程引用,兩個進(jìn)程可以同時打開一個 UDS 進(jìn)行通信和蚪,而且這種通信方式只會發(fā)生在系統(tǒng)內(nèi)核里魁瞪,不會在網(wǎng)絡(luò)上進(jìn)行傳播。下面就來看看如何讓 Envoy 通過 UDS 與上游集群 Nginx 進(jìn)行通信吧惠呼,它們之間的通信模型大概就是這個樣子:

2. Nginx 監(jiān)聽 UDS

首先需要修改 Nginx 的配置导俘,讓其監(jiān)聽在 UDS 上,至于 Socket 描述符文件的存儲位置剔蹋,就隨你的意了旅薄。具體需要修改 listen 參數(shù)為下面的形式:

listen      unix:/sock/hugo.sock;

當(dāng)然,如果想獲得更快的通信速度泣崩,可以放在 /dev/shm 目錄下少梁,這個目錄是所謂的 tmpfs,它是 RAM 可以直接使用的區(qū)域矫付,所以讀寫速度都會很快凯沪,下文會單獨(dú)說明。

3. Envoy-->UDS-->Nginx

Envoy 默認(rèn)情況下是使用 IP 地址和端口號和上游集群通信的买优,如果想使用 UDS 與上游集群通信妨马,首先需要修改服務(wù)發(fā)現(xiàn)的類型,將 type 修改為 static

type: static

同時還需將端點(diǎn)定義為 UDS:

- endpoint:
    address:
      pipe:
        path: "/sock/hugo.sock"

最終的 Cluster 配置如下:

- "@type": type.googleapis.com/envoy.api.v2.Cluster
  name: hugo
  connect_timeout: 15s
  type: static
  load_assignment:
    cluster_name: hugo
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            pipe:
              path: "/sock/hugo.sock"

最后要讓 Envoy 能夠訪問 NginxSocket 文件杀赢,Kubernetes 中可以將同一個 emptyDir 掛載到兩個 Container 中來達(dá)到共享的目的烘跺,當(dāng)然最大的前提是 Pod 中的 Container 是共享 IPC 的。配置如下:

spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: envoy
        ...
        volumeMounts:
        - mountPath: /sock
          name: hugo-socket
          ...
      - name: hugo
        ...
        volumeMounts:
        - mountPath: /sock
          name: hugo-socket
          ...
      volumes:
      ...
      - name: hugo-socket
        emptyDir: {}

現(xiàn)在你又可以愉快地訪問我的博客了脂崔,查看 Envoy 的日志滤淳,成功將請求通過 Socket 轉(zhuǎn)發(fā)給了上游集群:

[2020-04-27T02:49:47.943Z] "GET /posts/prometheus-histograms/ HTTP/1.1" 200 - 0 169949 1 0 "66.249.64.209,45.145.38.4" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" "9d490b2d-7c18-4dc7-b815-97f11bfc04d5" "fuckcloudnative.io" "/dev/shm/hugo.sock"

嘿嘿,Google 的爬蟲也來湊熱鬧砌左。

你可能會問我:你這里的 Socket 為什么在 /dev/shm/ 目錄下安备馈?別急汇歹,還沒結(jié)束呢屁擅,先來補(bǔ)充一個背景知識。

4. Linux 共享內(nèi)存機(jī)制

共享內(nèi)存(shared memory)秤朗,是 Linux 上一種用于進(jìn)程間通信(IPC)的機(jī)制煤蹭。

進(jìn)程間通信可以使用管道,Socket,信號硝皂,信號量常挚,消息隊(duì)列等方式,但這些方式通常需要在用戶態(tài)稽物、內(nèi)核態(tài)之間拷貝奄毡,一般認(rèn)為會有 4 次拷貝;相比之下贝或,共享內(nèi)存將內(nèi)存直接映射到用戶態(tài)空間吼过,即多個進(jìn)程訪問同一塊內(nèi)存,理論上性能更高咪奖。嘿嘿盗忱,又可以改進(jìn)上面的方案了。

共享內(nèi)存有兩種機(jī)制:

  • POSIX 共享內(nèi)存(shm_open()羊赵、shm_unlink()
  • System V 共享內(nèi)存(shmget()趟佃、shmat()、shmdt()

其中昧捷,System V 共享內(nèi)存歷史悠久闲昭,一般的 UNIX 系統(tǒng)上都有這套機(jī)制;而 POSIX 共享內(nèi)存機(jī)制接口更加方便易用靡挥,一般是結(jié)合內(nèi)存映射 mmap 使用序矩。

mmapSystem V 共享內(nèi)存的主要區(qū)別在于:

  • System V shm 是持久化的,除非被一個進(jìn)程明確的刪除跋破,否則它始終存在于內(nèi)存里簸淀,直到系統(tǒng)關(guān)機(jī)。
  • mmap 映射的內(nèi)存不是持久化的幔烛,如果進(jìn)程關(guān)閉啃擦,映射隨即失效囊蓝,除非事先已經(jīng)映射到了一個文件上饿悬。
  • /dev/shm 是 Linux 下 sysv 共享內(nèi)存的默認(rèn)掛載點(diǎn)。

POSIX 共享內(nèi)存是基于 tmpfs 來實(shí)現(xiàn)的聚霜。實(shí)際上狡恬,更進(jìn)一步,不僅 PSM(POSIX shared memory)蝎宇,而且 SSM(System V shared memory) 在內(nèi)核也是基于 tmpfs 實(shí)現(xiàn)的弟劲。

從這里可以看到 tmpfs 主要有兩個作用:

  • 用于 System V 共享內(nèi)存,還有匿名內(nèi)存映射姥芥;這部分由內(nèi)核管理兔乞,用戶不可見。
  • 用于 POSIX 共享內(nèi)存,由用戶負(fù)責(zé) mount庸追,而且一般 mount 到 /dev/shm霍骄,依賴于 CONFIG_TMPFS

雖然 System V 與 POSIX 共享內(nèi)存都是通過 tmpfs 實(shí)現(xiàn)淡溯,但是受的限制卻不相同读整。也就是說 /proc/sys/kernel/shmmax 只會影響 System V 共享內(nèi)存,/dev/shm 只會影響 POSIX 共享內(nèi)存咱娶。實(shí)際上米间,System VPOSIX 共享內(nèi)存本來就是使用的兩個不同的 tmpfs 實(shí)例。

System V 共享內(nèi)存能夠使用的內(nèi)存空間只受 /proc/sys/kernel/shmmax 限制膘侮;而用戶通過掛載的 /dev/shm屈糊,默認(rèn)為物理內(nèi)存的 1/2

概括一下:

  • POSIX 共享內(nèi)存與 System V 共享內(nèi)存在內(nèi)核都是通過 tmpfs 實(shí)現(xiàn)琼了,但對應(yīng)兩個不同的 tmpfs 實(shí)例另玖,相互獨(dú)立。
  • 通過 /proc/sys/kernel/shmmax 可以限制 System V 共享內(nèi)存的最大值表伦,通過 /dev/shm 可以限制 POSIX 共享內(nèi)存的最大值谦去。

5. Kubernetes 共享內(nèi)存

Kubernetes 創(chuàng)建的 Pod,其共享內(nèi)存默認(rèn) 64MB蹦哼,且不可更改鳄哭。

為什么是這個值呢?其實(shí)纲熏,Kubernetes 本身是沒有設(shè)置共享內(nèi)存的大小的妆丘,64MB 其實(shí)是 Docker 默認(rèn)的共享內(nèi)存的大小。

Docker run 的時候局劲,可以通過 --shm-size 來設(shè)置共享內(nèi)存的大猩准稹:

?? → docker run  --rm centos:7 df -h |grep shm
shm              64M     0   64M   0% /dev/shm

?? → docker run  --rm --shm-size 128M centos:7 df -h |grep shm
shm             128M     0  128M   0% /dev/shm

然而,Kubernetes 并沒有提供設(shè)置 shm 大小的途徑鱼填。在這個 issue 里社區(qū)討論了很久是否要給 shm 增加一個參數(shù)药有,但是最終并沒有形成結(jié)論,只是有一個 workgroud 的辦法:將 Memory 類型的 emptyDir 掛載到 /dev/shm 來解決苹丸。

Kubernetes 提供了一種特殊的 emptyDir:可以將 emptyDir.medium 字段設(shè)置為 "Memory"愤惰,以告訴 Kubernetes 使用 tmpfs(基于 RAM 的文件系統(tǒng))作為介質(zhì)。用戶可以將 Memory 介質(zhì)的 emptyDir 掛到任何目錄赘理,然后將這個目錄當(dāng)作一個高性能的文件系統(tǒng)來使用宦言,當(dāng)然也可以掛載到 /dev/shm,這樣就可以解決共享內(nèi)存不夠用的問題了商模。

使用 emptyDir 雖然可以解決問題奠旺,但也是有缺點(diǎn)的:

  • 不能及時禁止用戶使用內(nèi)存蜘澜。雖然過 1~2 分鐘 Kubelet 會將 Pod 擠出,但是這個時間內(nèi)响疚,其實(shí)對 Node 還是有風(fēng)險(xiǎn)的兼都。
  • 影響 Kubernetes 調(diào)度,因?yàn)?emptyDir 并不涉及 Node 的 Resources稽寒,這樣會造成 Pod “偷偷”使用了 Node 的內(nèi)存扮碧,但是調(diào)度器并不知曉。
  • 用戶不能及時感知到內(nèi)存不可用杏糙。

由于共享內(nèi)存也會受 Cgroup 限制慎王,我們只需要給 Pod 設(shè)置 Memory limits 就可以了。如果將 Pod 的 Memory limits 設(shè)置為共享內(nèi)存的大小宏侍,就會遇到一個問題:當(dāng)共享內(nèi)存被耗盡時赖淤,任何命令都無法執(zhí)行,只能等超時后被 Kubelet 驅(qū)逐谅河。

這個問題也很好解決咱旱,將共享內(nèi)存的大小設(shè)置為 Memory limits50% 就好。綜合以上分析绷耍,最終設(shè)計(jì)如下:

  1. 將 Memory 介質(zhì)的 emptyDir 掛載到 /dev/shm/吐限。
  2. 配置 Pod 的 Memory limits
  3. 配置 emptyDirsizeLimitMemory limits 的 50%褂始。

6. 最終配置

根據(jù)上面的設(shè)計(jì)诸典,最終的配置如下。

Nginx 的配置改為:

listen      unix:/dev/shm/hugo.sock;

Envoy 的配置改為:

- "@type": type.googleapis.com/envoy.api.v2.Cluster
  name: hugo
  connect_timeout: 15s
  type: static
  load_assignment:
    cluster_name: hugo
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            pipe:
              path: "/dev/shm/hugo.sock"

Kubernetes 的 manifest 改為:

spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: envoy
        resources:
          limits:
            memory: 256Mi
        ...
        volumeMounts:
        - mountPath: /dev/shm
          name: hugo-socket
          ...
      - name: hugo
        resources:
          limits:
            memory: 256Mi
        ...
        volumeMounts:
        - mountPath: /dev/shm
          name: hugo-socket
          ...
      volumes:
      ...
      - name: hugo-socket
        emptyDir:
          medium: Memory
          sizeLimit: 128Mi

7. 參考資料

微信公眾號

掃一掃下面的二維碼關(guān)注微信公眾號崎苗,在公眾號中回復(fù)?加群?即可加入我們的云原生交流群狐粱,和孫宏亮、張館長胆数、陽明等大佬一起探討云原生技術(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肌蜻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子必尼,更是在濱河造成了極大的恐慌蒋搜,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胰伍,死亡現(xiàn)場離奇詭異齿诞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)骂租,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斑司,“玉大人渗饮,你說我怎么就攤上這事但汞。” “怎么了互站?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵私蕾,是天一觀的道長。 經(jīng)常有香客問我胡桃,道長踩叭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任翠胰,我火速辦了婚禮容贝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘之景。我一直安慰自己斤富,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布锻狗。 她就那樣靜靜地躺著满力,像睡著了一般。 火紅的嫁衣襯著肌膚如雪轻纪。 梳的紋絲不亂的頭發(fā)上油额,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音刻帚,去河邊找鬼悔耘。 笑死,一個胖子當(dāng)著我的面吹牛我擂,可吹牛的內(nèi)容都是我干的衬以。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼校摩,長吁一口氣:“原來是場噩夢啊……” “哼看峻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衙吩,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤互妓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坤塞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冯勉,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年摹芙,在試婚紗的時候發(fā)現(xiàn)自己被綠了灼狰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡浮禾,死狀恐怖交胚,靈堂內(nèi)的尸體忽然破棺而出份汗,到底是詐尸還是另有隱情,我是刑警寧澤蝴簇,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布杯活,位于F島的核電站,受9級特大地震影響熬词,放射性物質(zhì)發(fā)生泄漏旁钧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一互拾、第九天 我趴在偏房一處隱蔽的房頂上張望歪今。 院中可真熱鬧,春花似錦摩幔、人聲如沸彤委。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焦影。三九已至,卻和暖如春封断,著一層夾襖步出監(jiān)牢的瞬間斯辰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工坡疼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彬呻,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓柄瑰,卻偏偏與公主長得像闸氮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子教沾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355