自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

知乎十萬級容器規(guī)模的分布式鏡像倉庫實踐

開發(fā) 架構(gòu) 開發(fā)工具 分布式
知乎在 2016 年已經(jīng)完成了全量業(yè)務(wù)的容器化,并在自研容器平臺上以原生鏡像的方式部署和運行。

 知乎在 2016 年已經(jīng)完成了全量業(yè)務(wù)的容器化,并在自研容器平臺上以原生鏡像的方式部署和運行。

后續(xù)我們陸續(xù)實施了 CI、Cron、Kafka、HAProxy、HBase、Twemproxy 等系列核心服務(wù)和基礎(chǔ)組件的容器化。

知乎既是容器技術(shù)的重度依賴者,也是容器技術(shù)的深度實踐者,本篇文章分享知乎在鏡像倉庫這個容器技術(shù)核心組件的生產(chǎn)實踐。

基礎(chǔ)背景

容器的核心理念在于通過鏡像將運行環(huán)境打包,實現(xiàn)“一次構(gòu)建,處處運行”,從而避免了運行環(huán)境不一致導(dǎo)致的各種異常。

在容器鏡像的發(fā)布流程中,鏡像倉庫扮演了鏡像的存儲和分發(fā)角色,并且通過 tag 支持鏡像的版本管理,類似于 Git 倉庫在代碼開發(fā)過程中所扮演的角色,是整個容器環(huán)境中不可缺少的組成部分。

鏡像倉庫實現(xiàn)方式按使用范圍可以分為兩類:

  • Docker Hub,在公網(wǎng)環(huán)境下面向所有容器使用者開放的鏡像服務(wù)
  • Docker Registry,供開發(fā)者或公司在內(nèi)部環(huán)境下搭建鏡像倉庫服務(wù)。

基于公網(wǎng)下載鏡像的網(wǎng)絡(luò)帶寬、延遲限制以及可控性的角度考慮,在私有云環(huán)境下通常需要采用 Docker Registry 來搭建自己的鏡像倉庫服務(wù)。

Docker Registry 本身開源,當前接口版本為 V2 (以下描述均針對該版本),支持多種存儲后端,如:

  • InMemory:A temporary storage driver using a local in memory map. This exists solely for reference and testing。
  • FileSystem:A local storage driver configured to use a directory tree in the local file system。
  • S3:A driver storing objects in an Amazon Simple Storage Service (S3) bucket。
  • Azure:A driver storing objects in Microsoft Azure Blob Storage。
  • Swift:A driver storing objects in Openstack Swift。
  • OSS:A driver storing objects in Aliyun OSS。
  • GCS:A driver storing objects in a Google Cloud Storage bucket。

默認使用本地磁盤作為 Docker Registry 的存儲,用下面的配置即可本地啟動一個鏡像倉庫服務(wù):

  1. $ docker run -d \ 
  2.   -p 5000:5000 \ 
  3.   --restart=always \ 
  4.   --name registry \ 
  5.   -v /mnt/registry:/var/lib/registry \ 
  6.   registry:2 

生產(chǎn)環(huán)境挑戰(zhàn)

很顯然,以上面的方式啟動的鏡像倉庫是無法在生產(chǎn)環(huán)境中使用的,問題如下:

  • 性能問題:基于磁盤文件系統(tǒng)的 Docker Registry 進程讀取延遲大,無法滿足高并發(fā)高吞吐鏡像請求需要。

且受限于單機磁盤,CPU,網(wǎng)絡(luò)資源限制,無法滿足上百臺機器同時拉取鏡像的負載壓力。

  • 容量問題:單機磁盤容量有限,存儲容量存在瓶頸。知乎生產(chǎn)環(huán)境中現(xiàn)有的不同版本鏡像大概有上萬個,單備份的容量在 15T 左右,加上備份這個容量還要增加不少。
  • 權(quán)限控制:在生產(chǎn)環(huán)境中,需要對鏡像倉庫配置相應(yīng)的權(quán)限認證。缺少權(quán)限認證的鏡像倉庫就如同沒有認證的 Git 倉庫一樣,很容易造成信息泄露或者代碼污染。

知乎的生產(chǎn)環(huán)境中,有幾百個業(yè)務(wù)以及幾萬個容器運行在容器平臺上,繁忙時每日創(chuàng)建容器數(shù)近十萬,每個鏡像的平均大小在 1G 左右。

部署高峰期對鏡像倉庫的壓力是非常大的,上述性能和容量問題也表現(xiàn)的尤為明顯。

知乎解決方案

為了解決上述的性能和容量等問題,需要將 Docker Registry 構(gòu)造為一個分布式服務(wù),實現(xiàn)服務(wù)能力和存儲容量的水平擴展。

這其中最重要的一點是為 Docker Registry 選擇一個共享的分布式存儲后端,例如 S3,Azure,OSS,GCS 等云存儲。

這樣 Docker Registry 本身就可以成為無狀態(tài)服務(wù)從而水平擴展。

實現(xiàn)架構(gòu)如下:

該方案主要有以下幾個特點:

客戶端流量負載均衡

為了實現(xiàn)對多個 Docker Registry 的流量負載均衡,需要引入 Load Balance 模塊。

常見的 Load Balance 組件,如 LVS,HAProxy,Nginx 等代理方案都存在單機性能瓶頸,無法滿足上百臺機器同時拉取鏡像的帶寬壓力。

因此我們采用客戶端負載均衡方案,DNS 負載均衡:在 Docker daemon 解析 Registry 域名時,通過 DNS 解析到某個 Docker Registry 實例 IP 上,這樣不同機器從不同的 Docker Registry 拉取鏡像,實現(xiàn)負載均衡。

而且由于 Docker daemon 每次拉取鏡像時只需解析一次 Registry 域名,對于 DNS 負載壓力本身也很小。

從上圖可以看出,我們每一個 Docker Registry 實例對應(yīng)一個 Nginx,部署在同一臺主機上。

對 Registry 的訪問必須通過 Nginx,Nginx 這里并沒有起到負載均衡的作用,其具體的作用將在下文描述。

這種基于 DNS 的客戶端負載均衡存在的主要問題是無法自動摘掉掛掉的后端。

當某臺 Nginx 掛掉時,鏡像倉庫的可用性就會受到比較嚴重的影響。因此需要有一個第三方的健康檢查服務(wù)來對 Docker Registry 的節(jié)點進行檢查,健康檢查失敗時,將對應(yīng)的 A 記錄摘掉,健康檢查恢復(fù),再將 A 記錄加回來。

Nginx 權(quán)限控制

由于是完全的私有云,加上維護成本的考慮,我們的 Docker Registry 之前并沒有做任何權(quán)限相關(guān)的配置。

后來隨著公司的發(fā)展,安全問題也變的越來越重要,Docker Registry 的權(quán)限控制也提上了日程。

對于 Docker Registry 的權(quán)限管理,官方主要提供了兩種方式,一種是簡單的 basic auth,一種是比較復(fù)雜的 token auth。

我們對 Docker Registry 權(quán)限控制的主要需求是提供基本的認證和鑒權(quán),并且對現(xiàn)有系統(tǒng)的改動盡量最小。

basic auth 的方式只提供了基本的認證功能,不包含鑒權(quán)。而 token auth 的方式又過于復(fù)雜,需要維護單獨的 token 服務(wù)。

除非你需要相當全面精細的 ACL 控制并且想跟現(xiàn)有的認證鑒權(quán)系統(tǒng)相整合,否則官方并不推薦使用 token auth 的方式。這兩種方式對我們而言都不是很適合。

我們***采用了 basic auth + Nginx 的權(quán)限控制方式。basic auth 用來提供基本的認證,OpenRestry + lua 只需要少量的代碼,就可以靈活配置不同 URL 的路由鑒權(quán)策略。

我們目前實現(xiàn)的鑒權(quán)策略主要有以下幾種:

基于倉庫目錄的權(quán)限管理:針對不同的倉庫目錄,提供不同的權(quán)限控制。

例如 /v2/path1 作為公有倉庫目錄,可以直接進行訪問,而 /v2/path2 作為私有倉庫目錄,必須經(jīng)過認證才能訪問。

基于機器的權(quán)限管理:只允許某些特定的機器有 pull/push 鏡像的權(quán)限。

Nginx 鏡像緩存

Docker Registry 本身基于文件系統(tǒng),響應(yīng)延遲大,并發(fā)能力差。為了減少延遲提升并發(fā),同時減輕對后端存儲的負載壓力,需要給 Docker Registry 增加緩存。

Docker Registry 目前只支持將鏡像層級 meta 信息緩存到內(nèi)存或者 Redis 中,但是對于鏡像數(shù)據(jù)本身無法緩存。

我們同樣利用 Nginx 來實現(xiàn) URL 接口數(shù)據(jù)的 cache。為了避免 cache 過大,可以配置緩存失效時間,只緩存最近讀取的鏡像數(shù)據(jù)。

主要的配置如下所示:

  1. proxy_cache_path /dev/shm/registry-cache levels=1:2 keys_zone=registry-cache:10m max_size=124G; 

加了緩存之后,Docker Registry 性能跟之前相比有了明顯的提升。

經(jīng)過測試,100 臺機器并行拉取一個 1.2G 的 image layer,不加緩存平均需要 1m50s,花費最長時間為 2m30s。

添加緩存配置之后,平均的下載時間為 40s 左右,花費最長時間為 58s,可見對鏡像并發(fā)下載性能的提升還是相當明顯的。

HDFS 存儲后端

Docker Registry 的后端分布式存儲,我們選擇使用 HDFS,因為在私有云場景下訪問諸如 S3 等公有云存儲網(wǎng)絡(luò)帶寬和時延都無法接受。

HDFS 本身也是一個穩(wěn)定的分布式存儲系統(tǒng),廣泛應(yīng)用在大數(shù)據(jù)存儲領(lǐng)域,其可靠性滿足生產(chǎn)環(huán)境的要求。

但 Registry 的官方版本里并沒有提供 HDFS 的 Storage Driver,所以我們根據(jù)官方的接口要求及示例,實現(xiàn)了 Docker Registry 的 HDFS Storage Driver。

出于性能考慮,我們選用了一個 Golang 實現(xiàn)的原生 HDFS Client (colinmarc/hdfs)。

Storage Driver 的實現(xiàn)比較簡單,只需要實現(xiàn) Storage Driver 以及 FileWriter 這兩個 interface 就可以了。

具體的接口如下:

  1. type StorageDriver interface { 
  2.     // Name returns the human-readable "name" of the driver。 
  3.     Name() string 
  4.  
  5.     // GetContent retrieves the content stored at "path" as a []byte. 
  6.     GetContent(ctx context.Context, path string) ([]byte, error) 
  7.  
  8.     // PutContent stores the []byte content at a location designated by "path"
  9.     PutContent(ctx context.Context, path string, content []byte) error 
  10.  
  11.     // Reader retrieves an io.ReadCloser for the content stored at "path" 
  12.     // with a given byte offset. 
  13.     Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) 
  14.  
  15.     // Writer returns a FileWriter which will store the content written to it 
  16.     // at the location designated by "path" after the call to Commit
  17.     Writer(ctx context.Context, path string, append bool) (FileWriter, error) 
  18.  
  19.     // Stat retrieves the FileInfo for the given path, including the current 
  20.     // size in bytes and the creation time
  21.     Stat(ctx context.Context, path string) (FileInfo, error) 
  22.  
  23.     // List returns a list of the objects that are direct descendants of the 
  24.     //given path. 
  25.     List(ctx context.Context, path string) ([]string, error) 
  26.  
  27.     // Move moves an object stored at sourcePath to destPath, removing the 
  28.     // original object. 
  29.     Move(ctx context.Context, sourcePath string, destPath string) error 
  30.  
  31.     // Delete recursively deletes all objects stored at "path" and its subpaths. 
  32.     Delete(ctx context.Context, path string) error 
  33.     URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) 
  34.  
  35. type FileWriter interface { 
  36.     io.WriteCloser 
  37.  
  38.     // Size returns the number of bytes written to this FileWriter. 
  39.     Size() int64 
  40.  
  41.     // Cancel removes any written content from this FileWriter. 
  42.     Cancel() error 
  43.  
  44.     // Commit flushes all content written to this FileWriter and makes it 
  45.     // available for future calls to StorageDriver.GetContent and 
  46.     // StorageDriver.Reader. 
  47.     Commit() error 

其中需要注意的是 Storage Driver 的 Writer 方法里的 append 參數(shù),這就要求存儲后端及其客戶端必須提供相應(yīng)的 append 方法。

colinmarc/hdfs 這個 HDFS 客戶端中沒有實現(xiàn) append 方法,我們補充實現(xiàn)了這個方法。

鏡像清理

持續(xù)集成系統(tǒng)中,每次生產(chǎn)環(huán)境代碼發(fā)布都對應(yīng)有容器鏡像的構(gòu)建和發(fā)布,會導(dǎo)致鏡像倉庫存儲空間的持續(xù)上漲,需要及時清理不用的鏡像釋放存儲空間。

但 Docker Registry 本身并沒有配置鏡像 TTL 的機制,需要自己開發(fā)定時清理腳本。

Docker Registry 刪除鏡像有兩種方式,一種是刪除鏡像:

  1. DELETE /v2/<name>/manifests/<reference> 

另一種是直接刪除鏡像層 blob 數(shù)據(jù):

  1. DELETE /v2/<name>/blobs/<digest> 

由于容器鏡像層級間存在依賴引用關(guān)系,所以推薦使用***種方式清理過期鏡像的引用,然后由 Docker Registry 自身判斷鏡像層數(shù)據(jù)沒有被引用后再執(zhí)行物理刪除。

未來展望

通過適當?shù)拈_發(fā)和改造,我們實現(xiàn)了一套分布式的鏡像倉庫服務(wù),可以通過水平擴容來解決單機性能瓶頸和存儲容量問題,很好的滿足了我們現(xiàn)有生產(chǎn)環(huán)境需求。

但是在生產(chǎn)環(huán)境大規(guī)模分發(fā)鏡像時,服務(wù)端(存儲、帶寬等)依然有較大的負載壓力。

因此在大規(guī)模鏡像分發(fā)場景下,采用 P2P 的模式分發(fā)傳輸鏡像更加合適,例如阿里開源的 Dragonfly 和騰訊開發(fā)的 FID 項目。

知乎當前幾乎所有的業(yè)務(wù)都運行在容器上,隨著業(yè)務(wù)的快速增長,該分布式鏡像倉庫方案也會越來越接近性能瓶頸。

因此我們在后續(xù)也會嘗試引入 P2P 的鏡像分發(fā)方案,以滿足知乎快速增長的業(yè)務(wù)需求。

責任編輯:武曉燕 來源: 知乎
相關(guān)推薦

2013-03-22 14:44:52

大規(guī)模分布式系統(tǒng)飛天開放平臺

2016-01-12 14:59:40

分布式存儲分布式存儲架構(gòu)

2017-10-27 08:40:44

分布式存儲剪枝系統(tǒng)

2017-10-17 08:33:31

存儲系統(tǒng)分布式

2022-11-24 10:01:10

架構(gòu)分布式

2023-07-18 18:14:51

云原生軟件架構(gòu)

2018-05-07 15:15:26

服務(wù)器爬蟲數(shù)據(jù)

2018-09-03 08:36:04

知乎容器大數(shù)據(jù)

2025-02-11 09:12:55

2021-10-30 19:30:23

分布式Celery隊列

2017-06-16 21:00:02

Python爬蟲

2023-02-28 07:01:11

分布式緩存平臺

2024-09-27 09:19:30

2022-03-21 19:44:30

CitusPostgreSQ執(zhí)行器

2024-04-08 11:04:03

2019-06-19 15:40:06

分布式鎖RedisJava

2021-08-26 00:23:14

分布式存儲高可用

2021-08-24 05:02:34

云原生容器分布式

2024-05-07 07:58:10

數(shù)據(jù)架構(gòu)大數(shù)據(jù)中間件架構(gòu)

2022-09-07 08:18:26

分布式灰度方案分支號
點贊
收藏

51CTO技術(shù)棧公眾號