Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

12.2. 容器會遇到的問題

12.2.1. 程序啟動的區別

在Linux是一般是採用守護進程方式啟動。啟動後進入後台,啟動採用 systemd 。

容器中啟動通常是直接運行,這樣的運行方式,相當於你在linux的Shell 終端直接運行一樣,是在前台運行,隨時 CTRL + C 或者關閉終端窗口,程序就會退出。容器採用這種方式啟動,就是為了讓 docker 管理容器,docker 能夠感知到容器的當前狀態,如果程序退出,docker 將會重新啟動這個容器。

守護進程方式需要記錄 pid 即父進程ID,用於後面管理該進程,例如可以實現 HUP 信號處理。也就是 reload 操作,不用退出當前程序實現配置檔案刷新。處理 HUP 信號,無需關閉 Socker 連接埠,也不會關閉綫程或進程,用戶體驗更好。

容器是直接運行(前台運行),所以沒有 PID 也不能實現 reload 操作。 配置檔案更新需要重新啟動容器,容器啟動瞬間TCP Socker 連接埠關閉,此時用戶會 timeout。甚至該服務可能會引起集群系統的雪崩效應。

很多鏡像製作者更趨向使用環境變數傳遞啟動參數。

當然你也可以在容器中使用 systemd ,這樣做容器不能直接感知到容器的運行狀態,systemctl stop example 後,容器仍然正常。需要做存活和健康檢查。通過健康狀態判斷容器的工作情況。如果處于非健康狀態,將該節點從負載均衡節點池中將它踢出去。

Linux 啟動一個應用遠遠比docker 啟動一個容器速度要快。因為物理機或者虛擬機的Linux操作系統已經啟動,虛擬機也分配了資源,運行執行檔基本上是瞬間啟動。而 docker 啟動容器,要分配資源(分配內存和CPU資源,新建檔案系統),相當於創建一個虛擬機的過程,最後載入約200MB左右的鏡像,並將鏡像運行起來,所以啟動所需時間較長,有時不可控,尤其是Java應用更為突出。

12.2.2. 存儲面臨的問題

傳統 Linux 直接操作本地硬碟,IO性能最大化。

私有雲還好辦公有雲處處受限。

自建的 Docker 或 Kubrnetes 可以使用宿主主機資源,公有雲只能使用網絡檔案系統和分散式系統。

這也是我的架構中 KVM,Docker,Kubernetes,物理機混合使用的原因,根據業務場景的需要來選擇哪種方案。

物理機上部署 docker 可以分配宿主主機的所有資源,適合做有狀態的服務的存儲持久化的需求。

私有雲 Kubernetes 適合做 CPU密集型運算服務,雖然通過local 卷和 hostPath 可以綁定,但是管理起來不如 Docker 更方便。

NFS 基本是做實驗用的,不能用在生產環境。我20年的職業生涯遇到過很多奇葩,例如 NFS 卡頓,NFS 用一段時間後訪問不了,或者可以訪問,檔案內容是舊的等等。

無論是NFS是更先進的分散式檔案系統,如果不是 10G乙太網,基本都不能用在生產環境。10年前我用4電口1G網卡做連接埠聚合勉強可以用於生產環境,不過10年前的互聯網生態跟當今不同,那時還是以圖文為主,確切的說是文字為主,配圖還很少。

所以涉及到存儲使用分散式檔案系統的前提是必須是 10G以上乙太網或者8G以上的FC 存儲。這樣才不會有IO瓶頸。任何分散式檔案系統都不可能比本地檔案系統穩定,除了速度還有延遲等等。

10GB 電口,光口乙太網已經出來十幾年了,相對比較便宜,可以使用 4光口 10G網卡,然後做連接埠聚合,變成 40G 網口。

現在 40G光口交換機都在10-20萬之間。一個40G的交換口可以分出四個10GB口。

如果使用40GB以上的乙太網,那麼總成本可能會超過物理機+虛擬機的解決方案。

12.2.3. 內部域名DNS

由於在集群環境中容器名稱是隨機,IP地址是不固定的,甚至連接埠也是動態的。為了定位到容器的節點,通常集群中帶有DNS功能,為每個節點分配一個域名,在其他容器中使用域名即可訪問到需要的容器。

看似沒有問題,我的職業生涯中就遇到過DNS的問題,bind,dnsmseq 我都用過,都出現過事故。解析卡頓,ping www.domain.com 後遲遲解析不出IP。最長一次用了幾分鐘才解析到IP地址。

所以後面就非常謹慎,配置檔案中我們仍然使用域名,因為修改配置檔案可能需要 reload 應用,或者重新部署等等。域名寫入配置,方便IP地址變更。例如 db.host=db.netkiller.cn 同時我們會在 /etc/hosts 中增加 xxx.xxx.xxx.xxx db.netkiller.cn 。這樣主要使用 /etc/hosts 做解析,一旦漏掉 /etc/hosts 配置 DNS 還能工作。

故障分析,DNS 使用 UDP 協議 53 連接埠,UDP 在網絡中傳輸不會返回狀態,有無數種可能導致 DNS 解析失敗。例如內部的交換機繁忙,背板頻寬不夠(用戶存儲轉發數據包,你可以理解就是交換機的內存),路由的問題等等……

12.2.4. 容器與網絡

相比傳統網絡,容器中的網絡環境是十分複雜的。傳統網絡中一個數據包僅僅經過路由器,交換機,達到伺服器,最多在服務前在增加一些防火牆,負載均衡等設備。

容器網絡部分實現方式SDN(軟件定義網絡)相比物理機(路由器、交換機、無服務)實現相對複雜。容器裡面使用了IP轉發,連接埠轉發,軟路由,lvs,7層負載均衡等等技術…… 調試起來非常複雜。docker 的 iptables 規則很頭痛。

例如一個TCP/IP 請求,需要經過多層虛擬網絡設備(docker0,bridge0,tun0……)層層轉發,再經過4層和7層的各種應用拆包,封包,最終到達容器內部。

有興趣你可以測試一下對比硬件設備,容器的網絡延遲和吞吐量。

容器的任何操作都是占用宿主主機的資源的,所以存在更多不可控的因素。例如CPU負載瞬態變化可以影響虛擬網絡,如UDP丟包。而物理機+交換機+路由器的方案不會有任何影響。

在我學習Cisco的時候一直想不通,三層交換機都能啟用路由了,為什麼不在增加NAT功能?路由器都提供了ACL為什麼不增加防火牆功能?為什麼要分成三個設備?

為什麼在高端的箱式插卡設備裡,交換,路由,防火牆都能實現。僅僅是因為價格?

電信級別的設備 24口交換機只會配滿足24口交換的CPU和背板,賣出24口的價格。如果增加NAT功能,不僅會影響交換能力,網絡設備本身價格透明且敏感。

箱式網絡設備定位園區,城域網,骨幹網,不是用於接入伺服器。價格空間很大,廠家採用堆料方式開發,功能要滿足各種用戶需求和各種業務的需要。用途比IDC接入更複雜。

12.2.5. 容器的管理

傳統服務可以通過鍵盤和顯示器本地管理,OpenSSH 遠程管理,通過配置還能使用串口。

容器的管理讓你抓狂 docker exec 和 kubectl exec 進入後與傳統Linux差異非常大,這是鏡像製作者造成了。

控制台環境

  • 有些鏡像沒有初始化 shell 只有一個 $ 符號
  • 沒有彩色顯示
  • 可能不支持 UTF-8,中文亂碼
  • 可能不是標準 ANSI/XTerm 終端
  • 鍵盤定義五花八門,可能不是美式104鍵盤
  • 國家和時區並不是東八區,上海
  • HOME 目錄也是不是 /root

OS 不完整

  • 想查看連接埠情況,發現 netstat 和 ss 命令沒有。
  • 想查看IP地址,發現 ifconfig, ip 命令沒有。
  • 想測試IP地址是否暢通,發現 ping, traceroute 沒有。
  • 想測試URL,發現 curl , wget 沒有。

有些鏡像 dnf,yum,apk,apt 可以使用,有些鏡像把包管理也給閹割了,你想安裝上述工具都安裝不了。

臥槽!!! 一萬匹草泥馬

然後就自己用 Dockerfile 編譯,整出200MB的鏡像,臥槽這麼大。

你還會發現你編譯的程序沒有yum/dnf官方製作的好,它們做了編譯器的優化。你如果不相信你可以編譯一個 nginx 然後看看bin/nginx這個檔案的大小,你再使用 dnf install nginx 安裝,比較以下兩個二進制檔案。你會發現你編譯出來的二進制檔案足有8M大小,而dnf安裝的可能只有4M左右。

12.2.6. 容器與安全

12.2.6.1. 網絡安全

很多容器的鏡像中是不包含 iptables 的,所以無法做顆粒度很細的容器內部網絡安全設置。即使你製作的鏡像帶有iptables ,多數容器的側咯,IP地址和連接埠是隨機變化的。綁定IP地址又帶了容器的複雜性。

一旦攻入一個容器,進入容器後,容器與容器間基本是暢通無阻。

12.2.6.2. 掛馬風險

在容器中藏一個後門比物理機更容易,如上文所說很多容器中沒有調試相關命令,限制了你排查後門的難度。所以Dockerfile 製作鏡像,最好使用官方鏡像衍生出你的鏡像。

12.2.6.3. 隔離安全

容器間是隔離安全的,kubernetes 還有明明空間和RBAC等等。但是......

有時我們為了產生持久化會使用本地捲,或將容器目錄掛載到節點宿主主機中。這也帶來一個安全問題,一是可能大家使用了相同的檔案或目錄名,相互覆蓋對方的檔案。二是不小心將敏感信息寫入了到宿主主機,存在信息泄露風險。

除此之外 local volume, hostPath 以及沒有用戶認證的網絡檔案系統,都存在這種風險。例如NFS,因為Pod是隨機IP地址,NFS伺服器無法通過IP地址設置訪問規則。

12.2.7. 容器與監控

談到監控,跳不開 prometheus(普羅米修斯),它並不能覆蓋到所有監控。

我曾經寫過一篇文章《監控的藝術》網上可以搜到。

12.2.8. 容器與CI/CD

在DevOps場景中,使用 docker 或 kubernetes 做 CI/CD 是很扯淡的。

當 git 產生提交後,gitlab/jenkins 啟動容器,下載代碼,編譯,打包,測試,產生構建物,編譯 Dockerfile ,上傳 docker 鏡像到 registry,最後部署到容器執行。

臥槽!!!速度能急死你。

於是乎,我們做了 Cache。 不用每次都 pull 鏡像,緩存 Maven 的 .m2 庫,不再清理代碼(mvn clean)提速不少,測試環境湊合用吧。 注意不mvn clean 有時會編譯出錯

至于生產環境,我就不說了,有多少人真用CD部署生產環境。

12.2.9.