10万個のコンテナを備えたZhihuの分散画像ウェアハウスの実践

10万個のコンテナを備えたZhihuの分散画像ウェアハウスの実践

Zhihuは2016年に全事業のコンテナ化を完了し、自社開発のコンテナプラットフォーム上にネイティブイメージの形で展開・実行しました。

その後、CI、Cron、Kafka、HAProxy、HBase、Twemproxy などの一連のコア サービスと基本コンポーネントのコンテナ化を実装しました。

Zhihu は、コンテナ テクノロジーのヘビー ユーザーであると同時に、コンテナ テクノロジーの深い知識を持つ実践者でもあります。この記事では、コンテナ テクノロジーの中核コンポーネントであるイメージ リポジトリにおける Zhihu の制作実践を紹介します。

基本的な背景

コンテナの中心的な概念は、イメージを通じてオペレーティング環境をパッケージ化して「一度ビルドすればどこでも実行」を実現し、一貫性のないオペレーティング環境によって発生するさまざまな例外を回避することです。

コンテナイメージのリリースプロセスにおいて、イメージリポジトリはイメージの保存と配布の役割を果たしており、タグによるイメージのバージョン管理をサポートしています。これは、コード開発プロセスにおける Git リポジトリの役割に似ており、コンテナ環境全体に欠かせない部分です。

ミラー リポジトリの実装は、使用範囲に基づいて 2 つのカテゴリに分けられます。

  • パブリックネットワーク環境のすべてのコンテナユーザーに公開されているイメージサービス、Docker Hub
  • Docker Registry は、開発者や企業が社内環境にイメージ ウェアハウスを構築するためのサービスです。

ネットワーク帯域幅、レイテンシの制限、パブリック ネットワークからイメージをダウンロードする際の制御可能性を考慮すると、通常は Docker Registry を使用してプライベート クラウド環境で独自のイメージ ウェアハウス サービスを構築する必要があります。

Docker Registry はオープンソースであり、現在のインターフェース バージョンは V2 (以下の説明はこのバージョンに関するものです) であり、次のような複数のストレージ バックエンドをサポートしています。

  • InMemory: ローカルのメモリマップを使用する一時ストレージ ドライバー。これは参照とテストのためだけに存在します。
  • FileSystem: ローカル ファイル システム内のディレクトリ ツリーを使用するように構成されたローカル ストレージ ドライバー。
  • S3: Amazon Simple Storage Service (S3) バケットにオブジェクトを保存するドライバー。
  • Azure: Microsoft Azure Blob Storage にオブジェクトを保存するドライバー。
  • Swift: Openstack Swift にオブジェクトを保存するドライバー。
  • OSS: Aliyun OSS にオブジェクトを保存するドライバー。
  • GCS: Google Cloud Storage バケットにオブジェクトを保存するドライバー。

デフォルトでは、ローカル ディスクが Docker レジストリのストレージとして使用されます。次の構成を使用して、イメージ リポジトリ サービスをローカルで開始できます。

  1. $ docker run -d \
  2. -p 5000:5000 \
  3. --restart=常に\  
  4. --name レジストリ \  
  5. -v /mnt/registry:/var/lib/registry \
  6. レジストリ:2

生産環境の課題

当然ですが、上記の方法で起動したイメージリポジトリは実稼働環境では使用できません。問題は次のとおりです。

  • パフォーマンスの問題: ディスク ファイル システムに基づく Docker レジストリ プロセスでは読み取り遅延が大きく、高同時実行性と高スループットのイメージ要求のニーズを満たすことができません。

また、単一マシンのディスク、CPU、ネットワーク リソースの制限により、数百台のマシンから同時にイメージを取得する負荷圧力に対応することは不可能です。

  • 容量の問題: 単一のマシンのディスクの容量は限られており、ストレージ容量にボトルネックがあります。 Zhihu の制作環境には、約数万種類の異なるバージョンの画像が存在します。バックアップ1つの容量は約15Tで、バックアップを追加すると容量が大幅に増加します。
  • 権限制御: 実稼働環境では、イメージ リポジトリに対して対応する権限認証を構成する必要があります。権限認証のないミラー リポジトリは、認証されていない Git リポジトリと同じで、情報漏洩やコード汚染が簡単に発生する可能性があります。

Zhihu の運用環境では、コンテナ プラットフォーム上で数百のビジネスと数万のコンテナが実行されています。忙しい時期には、毎日約 10 万個のコンテナが作成され、各イメージの平均サイズは約 1G になります。

展開のピーク時には、イメージ リポジトリへの負荷が非常に高くなり、上記のパフォーマンスと容量の問題が特に顕著になります。

知乎ソリューション

前述のパフォーマンスと容量の問題を解決するためには、Docker Registryを分散サービスとして構築し、サービス機能とストレージ容量の水平拡張を実現する必要があります。

最も重要な点は、S3、Azure、OSS、GCS、その他のクラウド ストレージなど、Docker Registry 用の共有分散ストレージ バックエンドを選択することです。

このようにして、Docker Registry 自体がステートレス サービスになり、水平方向にスケーリングできるようになります。

実装アーキテクチャは次のとおりです。

このプログラムの主な機能は次のとおりです。

クライアントトラフィックの負荷分散

複数の Docker レジストリのトラフィック負荷分散を実現するには、Load Balance モジュールを導入する必要があります。

LVS、HAProxy、Nginx、その他のプロキシ ソリューションなどの一般的な負荷分散コンポーネントはすべて、単一マシンのパフォーマンスのボトルネックがあり、数百台のマシンから同時にイメージを取得する帯域幅の圧力に対応できません。

そのため、クライアント側の負荷分散ソリューションである DNS 負荷分散を使用します。Docker デーモンがレジストリ ドメイン名を解決すると、DNS を介して Docker レジストリ インスタンス IP に解決され、異なるマシンが異なる Docker レジストリからイメージをプルして負荷分散を実現します。

また、Docker デーモンはイメージをプルするたびにレジストリ ドメイン名を 1 回解決するだけでよいため、DNS 負荷自体も非常に小さくなります。

上の図からわかるように、各 Docker Registry インスタンスは、同じホストにデプロイされた Nginx に対応しています。

レジストリへのアクセスは Nginx を経由する必要があります。 Nginx はここでは負荷分散の役割を果たしません。具体的な役割については後述します。

この DNS ベースのクライアント負荷分散の主な問題は、障害が発生したバックエンドを自動的に削除できないことです。

Nginx サーバーがクラッシュすると、イメージ リポジトリの可用性に重大な影響が生じます。したがって、Docker レジストリ ノードをチェックするには、サードパーティのヘルス チェック サービスが必要です。ヘルスチェックが失敗すると、対応する A レコードが削除されます。ヘルスチェックが復元されると、A レコードが再度追加されます。

Nginx の権限制御

完全にプライベートなクラウドであり、メンテナンスコストを考慮して、当社の Docker レジストリではこれまで権限関連の構成を一切行っていません。

その後、会社が成長するにつれて、セキュリティの問題がますます重要になり、Docker Registry の権限制御も議題に上がりました。

Docker Registry の権限管理については、公式では主に 2 つの方法を提供しています。1 つはシンプルな基本認証、もう 1 つはより複雑なトークン認証です。

Docker レジストリの権限制御に対する主な要件は、基本的な認証と承認を提供し、既存のシステムへの変更を最小限に抑えることです。

基本認証方式では、基本認証機能のみが提供され、承認は含まれません。ただし、トークン認証方法は複雑すぎるため、別のトークン サービスのメンテナンスが必要になります。

非常に包括的で洗練された ACL 制御が必要で、既存の認証システムと統合する場合を除き、トークン認証の公式方法は推奨されません。どちらのアプローチも私たちには適していません。

基本認証+Nginx権限制御方式を採用しました。基本認証は基本認証を提供するために使用されます。 OpenRestry + lua では、さまざまな URL のルーティング認証戦略を柔軟に構成するために、少量のコードのみが必要です。

現在実装している認証戦略は主に次のとおりです。

ウェアハウス ディレクトリに基づく権限管理: 異なるウェアハウス ディレクトリに対して異なる権限制御を提供します。

たとえば、/v2/path1 はパブリック ウェアハウス ディレクトリであり、直接アクセスできますが、/v2/path2 はプライベート ウェアハウス ディレクトリであり、アクセスする前に認証を受ける必要があります。

マシンベースの権限管理: 特定のマシンのみにイメージをプル/プッシュする権限が与えられます。

Nginx イメージキャッシュ

Docker レジストリ自体はファイル システムに基づいており、応答の遅延が大きく、同時実行能力が低いという問題があります。レイテンシを削減し、同時実行性を向上させ、バックエンド ストレージの負荷を軽減するには、Docker レジストリにキャッシュを追加する必要があります。

Docker Registry は現在、イメージレベルのメタ情報をメモリまたは Redis にキャッシュすることのみをサポートしていますが、イメージ データ自体をキャッシュすることはできません。

また、URL インターフェース データ キャッシュを実装するために Nginx も使用します。キャッシュが大きくなりすぎないようにするには、キャッシュの有効期限を設定して、最後に読み取った画像データのみをキャッシュするようにします。

主な構成は次のとおりです。

  1. proxy_cache_path /dev/shm/registry-cache レベル=1:2 キー_ゾーン=registry-cache:10m 最大サイズ=124G;

キャッシュを追加した後、Docker Registry のパフォーマンスは以前に比べて大幅に向上しました。

テストの結果、100 台のマシンで 1.2G のイメージ レイヤーをキャッシュなしで並列にプルするのに平均 1 分 50 秒かかり、最長時間は 2 分 30 秒でした。

キャッシュ設定を追加した後、平均ダウンロード時間は約 40 秒、最長時間は 58 秒になります。同時画像ダウンロードのパフォーマンスの向上が明らかであることがわかります。

HDFS ストレージ バックエンド

プライベート クラウド シナリオで S3 などのパブリック クラウド ストレージにアクセスする際のネットワーク帯域幅とレイテンシが許容できないため、Docker Registry のバックエンド分散ストレージとして HDFS を使用することを選択しました。

HDFS 自体も安定した分散ストレージ システムであり、ビッグ データ ストレージの分野で広く使用されており、その信頼性は実稼働環境の要件を満たしています。

ただし、レジストリの公式バージョンでは HDFS ストレージ ドライバーが提供されていないため、公式のインターフェース要件と例に基づいて、Docker レジストリ用の HDFS ストレージ ドライバーを実装しました。

パフォーマンス上の理由から、Golang で実装されたネイティブ HDFS クライアント (colinmarc/hdfs) を選択しました。

ストレージ ドライバーの実装は比較的簡単です。実装する必要があるのは、Storage Driver と FileWriter の 2 つのインターフェイスだけです。

具体的なインターフェースは次のとおりです。

  1. タイプStorageDriverインターフェース{
  2. //名前 人間が読める「名前」を返す 運転手
  3. 名前() 文字列
  4.  
  5. // GetContentは   "パス"   []バイトとして
  6. GetContent(ctx context.Context, パス文字列) ([]byte, error)
  7.  
  8. // PutContentは[]バイトのコンテンツ指定された場所格納します  "パス"
  9. PutContent(ctx context.Context、パス文字列、コンテンツ[]byte) エラー
  10.  
  11. // Readerはio.ReadCloserを取得   "パス"  
  12. //指定されたバイトオフセット
  13. Reader(ctx context.Context、パス文字列、オフセット int64) (io.ReadCloser、エラー)
  14.  
  15. // Writer は書き込まれたコンテンツを保存する FileWriter を返します
  16. // 指定れた場所  "パス"  への電話の後 専念
  17. Writer(ctx context.Context、パス文字列、追加ブール値) (FileWriter、エラー)
  18.  
  19. // Statは指定されたパスFileInfoを取得します。これには現在の 
  20. //サイズ バイト作成時刻
  21. Stat(ctx context.Context, パス文字列) (FileInfo, エラー)
  22.  
  23. // リストは、オブジェクト直接の子孫であるオブジェクトリストを返します
  24. //指定されたパス。
  25. リスト(ctx context.Context, パス文字列) ([]文字列, エラー)
  26.  
  27. // MoveはsourcePath格納されているオブジェクトをdestPath移動し、
  28. // 元のオブジェクト。
  29. 移動(ctx context.Context、sourcePath 文字列、destPath 文字列) エラー
  30.  
  31. //削除は、保存されているすべてのオブジェクトを再帰的に削除します  "パス"  およびそのサブパス。
  32. 削除(ctx context.Context、パス文字列) エラー
  33. URLFor(ctx context.Context、パス文字列、オプションマップ[文字列]インターフェース{}) (文字列、エラー)
  34. }
  35.  
  36. FileWriterインターフェース型{
  37. io.WriteCloser
  38.  
  39. //サイズ この FileWriter書き込まれたバイト返します
  40. サイズ() int64
  41.  
  42. // キャンセルすると、この FileWriterから書き込まれたコンテンツすべて削除されます。
  43. キャンセル() エラー
  44.  
  45. //コミットはこのFileWriter書き込まれたすべてのコンテンツフラッシュ
  46. //今後のStorageDriver.GetContent呼び出し利用可能 
  47. // ストレージドライバー.リーダー。
  48. コミット()エラー
  49. }

注意する必要があるのは、ストレージ ドライバーの Writer メソッドの append パラメーターです。これにより、ストレージ バックエンドとそのクライアントが対応する append メソッドを提供する必要があります。

HDFS クライアント colinmarc/hdfs は append メソッドを実装していないため、このメソッドを実装しました。

画像のクリーンアップ

継続的インテグレーションシステムでは、各本番環境のコードリリースはコンテナイメージの構築とリリースに対応しており、イメージリポジトリのストレージスペースが継続的に増加します。ストレージスペースを解放するには、未使用の画像を適時にクリーンアップする必要があります。

ただし、Docker Registry 自体にはイメージの TTL を設定するための仕組みがないため、スケジュールされたクリーンアップ スクリプトを自分で開発する必要があります。

Docker レジストリ内のイメージを削除する方法は 2 つあります。 1つは画像を削除することです。

  1. /v2/<名前>/manifests/<参照>を削除します。

もう 1 つは、イメージ レイヤーの BLOB データを直接削除することです。

  1. /v2/<名前>/blobs/<ダイジェスト>を削除します。

コンテナイメージレイヤー間には依存関係があるため、期限切れのイメージへの参照をクリーンアップするには最初の方法を使用し、Docker Registry 自体がイメージレイヤーデータが参照されていないと判断した後、物理的な削除を実行することをお勧めします。

今後の展望

適切な開発と変革により、水平拡張により単一マシンのパフォーマンスボトルネックとストレージ容量の問題を解決できる分散イメージ ウェアハウス サービスを実装し、既存の生産環境のニーズに非常によく適合しました。

しかし、実稼働環境で大規模にイメージを配布する場合、サーバー(ストレージ、帯域幅など)には依然として大きな負荷がかかります。

したがって、大規模な画像配信のシナリオでは、Alibaba のオープンソース Dragonfly や Tencent の FID プロジェクトなどの P2P モデルを使用して画像を配信および送信する方が適切です。

Zhihu の現在の事業のほぼすべてはコンテナ上で実行されています。ビジネスの急速な成長に伴い、分散イメージ リポジトリ ソリューションはパフォーマンスのボトルネックにますます近づいていきます。

そのため、Zhihu の急速に拡大するビジネス ニーズを満たすために、今後は P2P ミラー配信ソリューションを導入する予定です。

<<:  Red Hat の Steven Willmott: API とハイブリッド クラウドについて知っておくべきこと

>>:  Kubernetes、Docker Swarm、Amazon ECS 間のコンテナ戦争

推薦する

最適化されたトレーニングウェブサイトを記録する

前回と同様に、この記事には他の URL リンク、特に以前に最適化された URL リンクをあまり多く含...

顧客に感動を与えるソフトな記事の作り方

顧客に感動を与えるソフトな記事の作り方について語るとき、星文天下はまず『鋼鉄はいかに鍛えられたか』に...

企業ウェブサイトの IP を 2 か月以内に 0 から 300 に増やすにはどうすればよいでしょうか?

2011年10月中旬からこのプロジェクトにコンタクトを取り、計画を立て始めました。当時、企業情報の検...

ASO 最適化、ASO を行う際に知っておくべきこと!

1. 世界は論争の渦中にあり、CP はワインのランキングについて議論しています。ASOの世界は 2 ...

2022年雲奇会議開幕、アリババの張勇氏:より先進的な技術でより大きな責任を担う

11月3日に開幕した2022年雲奇カンファレンスで、アリババの張勇会長兼CEOは、テクノロジーは過去...

2021 年のクラウド コンピューティングのトレンド トップ 10

近年、クラウドコンピューティング技術は急速に発展しました。一部の業界専門家は、クラウド コンピューテ...

ウェブサイトが悪意を持って利用されていないか確認する

マルウェアやスパムに感染したくない人は誰もいません。だからこそ、安全な Web サイトを運営するため...

リンクの購入は科学であり、慎重に慎重に行う必要があります

ウェブサイトの最適化におけるフレンドリー リンクの役割は明らかです。フレンドリー リンクの品質は、ウ...

分類情報ウェブサイトにはさまざまなルートがあり、共同購入の運営は明らかに差別化されている

北京から唐勲芳が報告機密情報ウェブサイトは疑惑の渦に巻き込まれている。 3月25日、58.comは資...

bluevm-1g メモリ/50g ハードディスク/2T トラフィック/年間 39 ドル

bluevm] は、目立たない会社という印象を与えます。その中核となるコンセプトは、低価格で高品質の...

曲頭条にはまだ突破のチャンスはあるのだろうか?

Qutoutiao は、新しい形式の情報閲覧を創造することに特化したソフトウェアです。モバイル アプ...

YYミュージックは急速に成長しており、オンライン教育が今後の焦点となる

YY(別名「YY」、ウェブサイト:http://www.yy.com/)は、2005年にNetEas...

hiformance: VPS、バーゲンハンティングと市場価格への挑戦、複数のコンピュータルーム、Windows、Alipay 対応

hiformance の電子メール通知についてご説明します。今から 9 月 18 日までの 1 週間...

edgenat: 全製品 30% オフ、韓国専用サーバー、10 か月分を 24 か月分、VPS オプション - US AS4837\Korea CN2\US CN2 GIA ネットワーク

edgenatは現在、すべてのVPSと韓国のハイエンド独立サーバーに30%割引と生涯継続割引を提供し...

モバイルクライアント向けのいくつかの主要なプロモーション方法の詳細な説明

昨日、私は Sutu.com の公開クラスにゲストとして参加し、主にモバイル インターネット製品のプ...