OpenStack は Ceph ストレージを使用します。 Ceph は何をしますか?

OpenStack は Ceph ストレージを使用します。 Ceph は何をしますか?

[[212344]]

1 背景

1.1 Ceph の紹介

Ceph は、高いスケーラビリティ、高いパフォーマンス、高い信頼性の利点を備えた、非常に人気のあるオープンソースの分散ストレージ システムです。また、ブロック ストレージ サービス (rbd)、オブジェクト ストレージ サービス (rgw)、ファイル システム ストレージ サービス (cephfs) も提供します。これは現在、OpenStack の主流のバックエンド ストレージであり、OpenStack に兄弟のように近い存在であり、OpenStack に統合された共有ストレージ サービスを提供します。 Ceph を OpenStack バックエンド ストレージとして使用すると、次の利点があります。

  • すべてのコンピューティング ノードはストレージを共有するため、移行中にルート ディスクをコピーする必要はありません。コンピューティング ノードがクラッシュした場合でも、仮想マシンは別のコンピューティング ノード上ですぐに起動できます (退避)。
  • COW (Copy On Write) 機能を使用すると、仮想マシンを作成するときに、イメージ全体をダウンロードせずに、イメージに基づいてクローンを作成するだけで済みます。クローン作成操作には基本的にオーバーヘッドがないため、数秒で仮想マシンを作成できます。
  • Ceph RBD はシンプロビジョニングをサポートしています。これは、オンデマンドでスペースを割り当てることを意味し、Linux ファイルシステムのスパースファイルに似ています。 20 GB の仮想ハードディスクを作成すると、最初は物理的なストレージ領域を占有しません。ストレージ スペースは、データが書き込まれるときにのみオンデマンドで割り当てられます。

Ceph の詳細については、公式ドキュメントを参照してください。ここでは RBD のみに焦点を当てます。 RBD によって管理されるコア オブジェクトはブロック デバイスであり、通常はボリュームと呼ばれますが、Ceph ではイメージと呼ばれます (OpenStack イメージとの違いに注意してください)。 Ceph には、名前空間に似たプールの概念もあります。異なるプールでは、異なるレプリカ数、PG 数、配置戦略などを定義できます。各イメージではプールを指定する必要があります。イメージの命名規則は pool_name/image_name@snapshot です。たとえば、openstack/test-volume@test-snap は、openstackpool 内の test-volumeimage のスナップショット test-snap を意味します。したがって、次の 2 つのコマンドは同等です。

  1. rbd snap create --pool openstack --image test-image --snap test-snap
  2. rbd スナップは openstack/test-image@test-snap を作成します。

OpenStack プールに 1G イメージを作成するコマンドは次のとおりです。

  1. rbd -p オープンスタック作成  --size 1024 int32bit-テスト-1  

イメージはスナップショット機能をサポートしています。スナップショットを作成すると、現在のイメージのステータスが保存されます。これは、git commit 操作と同等です。ユーザーはいつでもイメージを任意のスナップショット ポイントにロールバックできます (git reset)。スナップショットを作成するコマンドは次のとおりです。

  1. rbd -p openstack snapを作成しますint32bit-test-1@snap-1

rbd リストを表示します:

  1. rbd コマンドは、次の例のように openstack ノードに対して実行されます。 grep int32bitテスト
  2. int32bit-テスト-1 1024M 2
  3. int32bit-テスト-1@スナップ-1 1024M 2

スナップショットに基づいて、クローンと呼ばれる新しいイメージを作成できます。クローンでは元のイメージがすぐにコピーされるのではなく、COW 戦略、つまりコピーオンライトが使用されます。オブジェクトを書き込む必要がある場合にのみ、オブジェクトが親からローカルにコピーされます。したがって、クローン操作は基本的に数秒で完了します。同じスナップショットに基づいて作成されたすべてのイメージは、スナップショット前のイメージ データを共有することに注意してください。したがって、クローンを作成する前にスナップショットを保護する必要があります。保護されたスナップショットは削除できません。クローン操作は、git ブランチ操作に似ています。イメージを複製するコマンドは次のとおりです。

  1. rbd -p openstack snap は int32bit-test-1@snap-1 を保護します
  2. rbd -p openstack クローン int32bit-test-1@snap-1 int32bit-test-2

イメージにどの子があるかを確認できるほか、イメージがどのイメージ クローンに基づいているか (親) も確認できます。

  1. $ rbd -p openstack children int32bit-test-1@snap-1
  2. openstack/int32bit-テスト-2
  3. rbd -p openstack 情報 int32bit-test-2 | grep 親
  4. 親: openstack/int32bit-test-1@snap-1

上記から、int32bit-test-2 は int32bit-test-1 の子であり、int32bit-test-1 は int32bit-test-2 の親であることがわかります。

スナップショットを継続的に作成し、イメージを複製すると、非常に長いイメージ チェーンが形成されます。チェーンが非常に長い場合、読み取りおよび書き込みのパフォーマンスに影響するだけでなく、管理も非常に面倒になります。幸いなことに、Ceph はチェーン上のすべてのイメージを 1 つのイメージにマージすることをサポートしています。この操作は flatten と呼ばれ、git merge 操作に似ています。フラット化では、最上位レベルの存在しないデータをすべてレイヤーごとにコピーする必要があるため、通常は非常に時間がかかります。

  1. $ rbd -p openstack フラット化 int32bit-test-2
  2. 画像の平坦化: 31% 完了...

この時点で、親子関係をもう一度見てみましょう。

  1. rbd -p openstack children int32bit-test-1@snap-1

この時点では、int32bit-test-1 には子がなく、int32bit-test-2 は完全に独立しています。

もちろん、Ceph は copy と呼ばれる完全なコピーもサポートしています。

  1. rbd -p openstack cp int32bit-test-1 int32bit-test-3

コピーはイメージを完全にコピーするため、非常に時間がかかりますが、コピーでは元のスナップショット情報はコピーされないことに注意してください。

Ceph は RBD イメージのエクスポートをサポートしています。

  1. rbd -p openstack エクスポート int32bit-test-1 int32bit-1.raw

「エクスポート」では画像全体がエクスポートされます。 Ceph は、特定のスナップショット ポイントからエクスポートを開始することを指定する差分エクスポート (export-diff) もサポートしています。

  1. rbd -p openstack export-diff int32bit-test-1 --from-snap snap-1 --snap snap-2 int32bit-test-1-diff.raw  

上記は、スナップショット ポイント snap-1 からスナップショット ポイント snap-2 にデータをエクスポートします。

もちろん、反対の操作は import と import-diff です。エクスポート/インポートは完全なイメージ バックアップをサポートしますが、エクスポート diff/インポート diff は差分イメージ バックアップを実装します。

Rbd イメージはストレージ スペースを動的に割り当てます。 du コマンドを使用すると、イメージが占有する実際の物理ストレージ領域を表示できます。

  1. $ rbd デュ int32bit-test-1
  2. 名前提供 使用
  3. int32bit-テスト-1 1024M 12288k

上記のイメージ割り当てのサイズは 1024M で、実際に占有されるスペースは 12288KB です。

画像を削除します。最初にすべてのスナップショットを削除し、依存する子がないことを確認する必要があることに注意してください。

  1. rbd -p openstack snap unprotect int32bit-test-1@snap-1
  2. rbd -p openstack スナップ rm int32bit-test-1@snap-1
  3. rbd -p openstack rm int32bit-test-1

1.2 OpenStackの概要

OpenStack は、IaaS レイヤーにおけるクラウド コンピューティング プラットフォームのオープン ソース実装です。 OpenStack の詳細については、私の個人ブログをご覧ください。ここでは、OpenStack が Ceph ストレージ システムに接続されたときに、ソース コード分析に基づいて Ceph が段階的に実行する内容にのみ焦点を当てます。この記事では、OpenStack のワークフロー全体を詳細に紹介するのではなく、Ceph に関連する実装にのみ焦点を当てます。 OpenStack ソース コードのアーキテクチャがよくわからない場合は、OpenStack ソース コードの読み方に関する以前の記事を参照してください。

この記事を読むと、次の質問が理解できるようになります。

  1. アップロードされた画像をなぜ RAW 形式に変換する必要があるのですか?
  2. 大きな画像ファイルを効率的にアップロードするにはどうすればいいですか?
  3. 仮想マシンを数秒で作成できるのはなぜですか?
  4. VM スナップショットの作成には数分かかるのに、ボリューム スナップショットの作成には数秒しかかからないのはなぜですか?
  5. 仮想マシンが存在する場合、イメージを削除できないのはなぜですか?
  6. 既存のボリュームを上書きするのではなく、空のボリュームにバックアップを復元する必要があるのはなぜですか?
  7. 画像からボリュームを作成しました。画像を削除してもいいですか?

この記事は Ceph ストレージを使用することを前提としていることに注意してください。つまり、Glance、Nova、Cinder はすべて Ceph を使用します。この結論は他の場合には当てはまらない可能性があります。

なお、この記事では最初にソースコードを掲載しますが、これは非常に長くて退屈です。概要セクションにすぐにジャンプして、各 OpenStack 操作に対応する Ceph の作業を表示できます。

2 一目見る

2.1 Glance の紹介

Glance によって管理されるコアエンティティは、OpenStack のコアコンポーネントの 1 つであるイメージです。 OpenStack のイメージ サービス (Image as Service) を提供し、主に OpenStack イメージとイメージ メタデータのライフサイクル管理、取得、ダウンロードを担当します。 Glance は、複数のストレージ システムへのイメージの保存をサポートしています。バックエンドのストレージシステムはストアと呼ばれ、イメージにアクセスするためのアドレスはロケーションと呼ばれます。場所は、http アドレスまたは rbd プロトコル アドレスにすることができます。ストア ドライバーが実装されていれば、Glance のストレージ バックエンドとして使用できます。ドライバーの主なインターフェースは次のとおりです。

  • get: 画像の場所を取得します。
  • get_size: 画像のサイズを取得します。
  • get_schemes: rbd、swift+https、http など、イメージにアクセスするための URL プレフィックス (プロトコル部分) を取得します。
  • add: 画像をバックエンドストレージにアップロードします。
  • delete: 画像を削除します。
  • set_acls: バックエンド ストレージの読み取りおよび書き込みアクセス権限を設定します。

メンテナンスの容易さのため、glance store は独立したライブラリとして Glance コードから分離され、glance_store プロジェクトによってメンテナンスされています。現在コミュニティによってサポートされているストアのリストは次のとおりです。

  • ファイルシステム: ローカルファイルシステムに保存します。デフォルトでは、/var/lib/glance/images ディレクトリに保存されます。
  • cinder: Cinder に保存します。
  • rbd: Ceph に保存します。
  • sheepdog: sheepdog に保存します。
  • swift: Swift オブジェクト ストレージに保存します。
  • vmware データストア: Vmware データストアに保存します。
  • http: 上記のすべてのストアは画像データを保存しますが、http ストアは特別です。画像データを保存しないため、add メソッドは実装されません。画像のURLアドレスのみが保存されます。仮想マシンが起動すると、コンピューティング ノードは指定された http アドレスからイメージをダウンロードします。

2.2 画像のアップロード

前回の紹介からわかるように、画像のアップロードは主にストアの add メソッドによって実装されます。

  1. @機能チェック 
  2. def add (self, image_id, image_file, image_size, context=None,
  3. 検証者=なし):
  4. チェックサム = hashlib.md5()
  5. 画像名 = str(画像ID)
  6. self.get_connection(conffile=self.conf_file,
  7. rados_id= self.user )を connとして:
  8. fsid = なし
  9. hasattr(conn, 'get_fsid' )の場合:
  10. fsid = conn.get_fsid()
  11. conn.open_ioctx(self.pool)ioctxとして使用します
  12. 順序= int (math.log(self.WRITE_CHUNKSIZE, 2))
  13. 試す:
  14. loc = self._create_image(fsid, conn, ioctx, イメージ名,
  15. 画像サイズ、順序)
  16. rbd.ImageExistsを除く:
  17. msg = _( 'RBD イメージ %s は既に存在します' ) % image_id
  18. 例外を発生させます。重複(メッセージ=msg)
  19. ...

image_file はファイルではなく、イメージのすべてのデータを保存し、read(bytes) メソッドを通じてイメージの内容を読み取る LimitingReader インスタンスであることに注意してください。

上記のソースコードから、glance はまず ceph 接続セッションを取得し、次に _create_image メソッドを呼び出して、イメージと同じサイズの rbd イメージを作成します。

  1. def _create_image(self, fsid, conn, ioctx, イメージ名,
  2. サイズ順序、コンテキスト=なし):
  3. librbd = rbd.RBD()
  4. 機能 = conn.conf_get( 'rbd_default_features' )
  5. librbd。作成(ioctx、イメージ名、サイズ順序、古いフォーマット= False
  6. 機能 = int (機能))
  7. 店舗所在地を返す({
  8. 'fsid' : fsid,
  9. 'プール' : self.pool,
  10. '画像' : 画像名,
  11. 'スナップショット' : DEFAULT_SNAPNAME、
  12. }, self.conf)

したがって、上記の手順は、rbd コマンドを通じて大まかに表現されます。

  1. rbd -p ${rbd_store_pool}を作成します  --size ${画像サイズ} ${画像ID}  

ceph で rbd イメージを作成したら、次のようにします。

  1. rbd.Image(ioctx, image_name)をイメージとして使用します:
  2. 書き込まれたバイト数 = 0
  3. オフセット = 0
  4. チャンク = utils.chunkreadable(image_file,
  5. 自己.WRITE_CHUNKSIZE)
  6. チャンク内のチャンクの場合:
  7. オフセット += image.write(チャンク, オフセット)
  8. チェックサム更新(チャンク)

Glance は image_file からブロックごとにデータを読み取り、新しく作成された rbd イメージに書き込み、チェックサムを計算していることがわかります。ブロック サイズは rbd_store_chunk_size によって構成され、デフォルトは 8MB です。

*** ステップを見てみましょう:

  1. loc.snapshotの場合:
  2. image.create_snap(loc.snapshot)
  3. image.protect_snap(loc.snapshot)

コードからわかるように、最初のステップはイメージ スナップショット (スナップショットの名前は snap) を作成し、それを保護することです。

アップロードしたイメージが cirros、イメージ サイズが 39 MB、イメージ UUID が d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6、設定が ceph の openstack プールに保存されていると仮定すると、対応する ceph 操作プロセスはおおよそ次のようになります。

  1. rbd -p オープンスタック作成  --サイズ 39 d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6  
  2. rbd -p openstack スナップ作成d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap
  3. rbd -p openstack スナップ保護 d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap

これは rbd コマンドで確認できます。

  1. int32bit rbd ls -l | grep d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6
  2. d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6 40162k 2
  3. d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap 40162k 2 はい

意味合い

これまで、Ceph にイメージをアップロードするプロセスを紹介してきましたが、Glance にイメージをアップロードするプロセスは省略しました。ただし、画像は Glance API を介して Glance にアップロードする必要があることは間違いありません。画像が非常に大きい場合、Glance API を介して HTTP プロトコルが使用されますが、これは非常に時間がかかり、API 管理ネットワークの帯域幅を占有します。 rbd import を通じて画像を直接インポートすることで、画像のアップロードの効率を大幅に向上できます。

まず、Glance を使用して空のイメージを作成し、その uuid をメモします。

  1. 一目でわかるイメージ-作成 

uuid が d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6 であると仮定して、rbd コマンドを使用してイメージを直接インポートし、スナップショットを作成します。

  1. rbd -p openstack import cirros.raw --image=d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6  
  2. rbd -p openstack スナップ作成d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap
  3. rbd -p openstack スナップ保護 d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap

Glance ミラーの場所の URL を設定します。

  1. FS_ID=`ceph -s | grep クラスター | awk '{print $2}' `
  2. 一目で場所を確認- 追加  --url rbd://${FS_ID}/openstack/d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6/snap d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6  

Glance イメージのその他のプロパティを設定します。

  1. 一目でわかる画像 -更新  --name="シロ" \  
  2. --ディスクフォーマット=raw --コンテナフォーマット=bare d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6  

2.3 画像の削除

イメージを削除する場合は、逆のプロセスになります。つまり、次のように、最初に unprotext -> snap rm -> rm を実行します。

  1. 試す:
  2. self._unprotect_snapshot(イメージ、スナップショット名)
  3. image.remove_snap(スナップショット名)
  4. rbd.ImageBusy除く:
  5. 例外を発生させる。InUseByStore()
  6. rbd.RBD().remove(ioctx, イメージ名)

イメージを削除するときは、現在の rbd イメージにサブイメージがないことを確認する必要があります。サブイメージがないと、削除は失敗します。

3 ノヴァ

3.1 Novaの紹介

Nova によって管理されるコア エンティティはサーバーであり、OpenStack にコンピューティング サービスを提供して OpenStack のコア コンポーネントとなります。 Nova のサーバーは仮想マシンだけを指すのではなく、あらゆるコンピューティング リソースの抽象化である可能性があることに注意してください。仮想マシン以外にも、ベアメタルマシンやコンテナなどでも構いません。

ただし、ここでは次のことを前提としています。

  • サーバーは仮想マシンです。
  • イメージタイプはrbdです。
  • コンピューティング ドライバーは libvirt です。

仮想マシンを起動する前に、まずルート ディスク (Nova ではイメージと呼ばれます) を準備する必要があります。 Glance と同様に、Nova のイメージはローカル ディスク、Ceph、Cinder (ボリュームからのブート) のストレージもサポートします。画像が保存される場所は画像の種類によって決まることに注意してください。 raw、qcow2、ploop などでローカルディスクに保存できます。イメージタイプが rbd の場合、イメージは Ceph に保存されます。異なる画像タイプは、異なる画像バックエンドによって管理されます。 rbd バックエンドは、nova/virt/libvirt/imageackend の Rbd クラス モジュールによって実装されます。

3.2 仮想マシンを作成する

仮想マシンを作成するプロセスについては詳細に分析しません。よく分からない場合は、以前のブログを確認してください。 Nova の libvirt ドライバーが仮想マシンのルート ディスク イメージをどのように準備するかを直接学習してみましょう。コードは nova/virt/libvirt/driver.py の spawn メソッドにあり、イメージを作成するために _create_image メソッドが呼び出されます。

  1. def spawn(self, コンテキスト, インスタンス, image_meta, 注入されたファイル,
  2. admin_password、network_info=なし、block_device_info=なし):
  3. ...
  4. self._create_image(コンテキスト、インスタンス、disk_info[ 'マッピング' ]、
  5. injection_info=注入情報、
  6. ブロックデバイス情報=ブロックデバイス情報)
  7. ...

_create_image メソッドのコードは次のとおりです。

  1. def _create_image(self, コンテキスト, インスタンス,
  2. disk_mapping、injection_info=なし、サフィックス= ''
  3. disk_images=なし、block_device_info=なし、
  4. fallback_from_host=なし、
  5. ignore_bdi_for_swap = False ):
  6. booted_from_volume = self._is_booted_from_volume(block_device_info)
  7. ...
  8. # ディレクトリが存在し書き込み可能であることを確認する
  9. fileutils.ensure_tree(libvirt_utils.get_instance_path(インスタンス))
  10. ...
  11. self._create_and_inject_local_root(コンテキスト、インスタンス、
  12. booted_from_volume、サフィックス、
  13. ディスクイメージ、インジェクション情報、
  14. フォールバック元ホスト
  15. ...

このメソッドは、まず仮想マシンのデータ ディレクトリ /var/lib/nova/instances/${uuid}/ をローカルに作成し、次に _create_and_inject_local_root メソッドを呼び出してルート ディスクを作成します。

  1. def _create_and_inject_local_root(self, コンテキスト, インスタンス,
  2. booted_from_volume、サフィックス、ディスクイメージ、
  3. injection_info、fallback_from_host):
  4. ...
  5. booted_from_volumeでない場合:
  6. root_fname = imagecache.get_cache_fname(disk_images[ 'image_id' ])
  7. サイズ= instance.flavor.root_gb * 単位.Gi
  8. backend = self.image_backend.by_name(インスタンス、 'ディスク' + サフィックス、
  9. CONF.libvirt.images_type)
  10. backend.SUPPORTS_CLONEの場合:
  11. def clone_fallback_to_fetch(*args, **kwargs):
  12. 試す:
  13. backend.clone(コンテキスト、disk_images[ 'image_id' ])
  14. 例外を除く。ImageUnacceptable:
  15. libvirt_utils.fetch_image(*引数、**キーワード)
  16. fetch_func = clone_fallback_to_fetch
  17. それ以外
  18. fetch_func = libvirt_utils.fetch_image
  19. self._try_fetch_image_cache(バックエンド、fetch_func、コンテキスト、
  20. root_fname、ディスクイメージ[ 'image_id' ]、
  21. インスタンス、サイズ、fallback_from_host)
  22. ...

image_backend.by_name() メソッドは、イメージ タイプ名 (ここでは Rbd) でイメージ バックエンド インスタンスを返します。コードから、バックエンドがクローン操作 (SUPPORTS_CLONE) をサポートしている場合はバックエンドの clone() メソッドが呼び出され、サポートしていない場合は fetch_image() メソッドを通じてイメージがダウンロードされることがわかります。明らかに、Ceph rbd はクローンをサポートしています。 Rbd の clone() メソッドを見てみましょう。コードは nova/virt/libvirt/imagebackend.py モジュールにあります:

  1. def clone(self, context, image_id_or_uri):
  2. ...
  3. 場所内の場所:
  4. self.driver.is_cloneable(location, image_meta): の場合
  5. LOG.debug( '選択された場所: %(loc)s' , { 'loc' : location})
  6. self.driver.clone(location, self.rbd_name)を返します
  7. ...

このメソッドは、Glance イメージのすべての場所を走査し、driver.is_cloneable() メソッドを使用してクローン作成がサポートされているかどうかを判断します。クローン作成がサポートされている場合は、driver.clone() メソッドが呼び出されます。ドライバーは Nova のストレージ ドライバーで、コードは nova/virt/libvirt/storage にあり、rbd ドライバーは rbd_utils.py モジュールにあります。まず、is_cloneable() メソッドを確認しましょう。

  1. def is_cloneable(self、image_location、image_meta):
  2. url = 画像の場所[ 'url' ]
  3. 試す:
  4. fsid、プール、イメージ、スナップショット = self.parse_url(url)
  5. exception.ImageUnacceptableを除き e:
  6. 戻る 間違い 
  7. self.get_fsid() != fsid の場合:
  8. 戻る 間違い 
  9. image_meta.get( 'disk_format' ) != 'raw'の場合:
  10. 戻る 間違い 
  11. #画像が読み取れる確認する
  12. 試す:
  13. self.exists(イメージ、プール=プール、スナップショット=スナップショット)を返します
  14. rbd.Errorを除き e:
  15. LOG.debug( '画像 %(loc)s を開けません: %(err)s' ,
  16. 辞書(loc=url, err=e))
  17. 戻る 間違い 

次の状況ではクローンがサポートされていないことがわかります。

  • Glance 内の rbd イメージの場所が不正です。 rbd の場所には、fsid、プール、イメージ ID、スナップショットの 4 つのフィールドが含まれている必要があります。フィールドは / で区切られます。
  • Glance と Nova は異なる Ceph クラスターに接続されています。
  • Glance 画像は RAW 形式ではありません。
  • Glance の rbd イメージには snap という名前のスナップショットがありません。

3番目の項目に特に注意してください。イメージが raw 形式でない場合、Nova は仮想マシンの作成時にクローン操作をサポートしないため、イメージは Glance からダウンロードする必要があります。このため、Glance が Ceph ストレージを使用する場合は、イメージを raw 形式に変換する必要があります。

*** clone メソッドを見てみましょう:

  1. def clone(self, image_location, dest_name, dest_pool=None):
  2. _fsid、プール、イメージ、スナップショット = self.parse_url(
  3. 画像の場所[ 'url' ])
  4. RADOSClient(self, str(pool))src_clientとして使用します
  5. RADOSClient(self, dest_pool)dest_clientとして使用します:
  6. 試す:
  7. RbdProxy().clone(src_client.ioctx,
  8. 画像、
  9. スナップショット、
  10. dest_client.ioctx、
  11. str(宛先名)、
  12. 機能=src_client.features)
  13. rbd.PermissionErrorを除く:
  14. 例外が発生します。Forbidden(_( '書き込み権限がありません'  
  15. 'ストレージ プール %s' ) % dest_pool)

このメソッドは、Ceph の clone メソッドのみを呼び出します。同じ Ceph クラスターを使用する場合、なぜ 2 つの ioctx が必要なのか疑問に思う人もいるかもしれません。これは、Glance と Nova が同じ Ceph プールを使用しない可能性があり、1 つのプールが 1 つの ioctx に対応するためです。

上記の操作は、次の rbd コマンドとほぼ同等です。

  1. rbd クローン ${glance_pool}/${イメージ uuid}@snap ${nova_pool}/${仮想マシン uuid}.disk

Nova と Glance が使用するプールが両方とも openstack であり、Glance イメージの uuid が d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6、Nova 仮想マシンの uuid が cbf44290-f142-41f8-86e1-d63c902b38ed であると仮定すると、対応する rbd コマンドはおおよそ次のようになります。

  1. rbdクローン\
  2. オープンスタック/d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@スナップ\
  3. オープンスタック/cbf44290-f142-41f8-86e1-d63c902b38ed_ディスク

さらに次のことを確認しました:

  1. int32bit $ rbd -p openstack ls | grep cbf44290-f142-41f8-86e1-d63c902b38ed
  2. cbf44290-f142-41f8-86e1-d63c902b38ed_ディスク
  3. int32ビット $ rbd -p openstack情報 cbf44290-f142-41f8-86e1-d63c902b38ed_disk
  4. rbdイメージ'cbf44290-f142-41f8-86e1-d63c902b38ed_disk' :
  5. サイズ2048 MB オブジェクト数 256
  6. オーダー23 (8192 kB オブジェクト)
  7. ブロック名プレフィックス: rbd_data.9f756763845e
  8. フォーマット: 2
  9. 機能: レイヤー化、排他ロック、オブジェクト マップ、高速差分、ディープ フラット化
  10. フラグ:
  11. create_timestamp: 2017年11月22日水曜日 05:11:17
  12. 親: openstack/d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap
  13. 重複: 40162 kB

出力からわかるように、Nova は実際に cbf44290-f142-41f8-86e1-d63c902b38ed_disk という名前の rbd イメージを作成しており、その親は openstack/d1a06da9-8ccd-4d3e-9b63-6dcd3ead29e6@snap です。

意味合い

  1. 仮想マシンを作成するときに、イメージをコピーしたりダウンロードしたりする必要はありません。代わりに、単純なクローン操作なので、仮想マシンの作成は数秒で完了します。
  2. イメージに依存する仮想マシンがある場合、イメージを削除することはできません。つまり、イメージを削除する前に、そのイメージに基づいて作成されたすべての仮想マシンを削除する必要があります。

3.3 仮想マシンのスナップショットの作成

まず最初に、本題から外れたことを言わせてください。 Nova がイメージの作成とスナップショットの作成を混同しているように感じます。私はこれら 2 つの違いを理解しています。

  • イメージの作成: 仮想マシンのルート ディスクを Glance にアップロードします。
  • スナップショットの作成: イメージ形式に従って仮想マシンのスナップショットを作成します。 qcow2 と rbd の両方の形式は明らかにスナップショットをサポートしています。スナップショットは Glance に保存せず、Nova または Cinder (Cinder から起動) で管理する必要があります。

しかし実際には、Nova がスナップショットを作成するためのサブコマンドは image-create であり、API メソッドも _action_create_image() と呼ばれ、その後に呼び出されるメソッドは snapshot() と呼ばれます。実際、ほとんどのイメージ タイプでは、クラウド ハード ディスクから起動されていない場合 (ボリュームから起動)、実際のスナップショットではなく、イメージが作成され、つまり Glance にイメージがアップロードされます。

もちろん、これは単なる名前の違いです。イメージの作成とスナップショットの作成に違いはありません。

仮想マシンのスナップショットは、libvirtdriver の snapshot() メソッドによって実装されます。コードは nova/virt/libvirt/driver.py にあります。コアコードは次のとおりです。

  1. スナップショットを定義します(自己、コンテキスト、インスタンス、イメージ ID、タスクの状態の更新):
  2. ...
  3. ルートディスク = self.image_backend.by_libvirt_path(
  4. インスタンス、ディスクパス、イメージタイプ=ソースタイプ)
  5. 試す:
  6. update_task_state(task_state=task_states.IMAGE_UPLOADING、
  7. 予想される状態 = task_states.IMAGE_PENDING_UPLOAD)
  8. メタデータ[ 'location' ] = root_disk.direct_snapshot(
  9. コンテキスト、スナップショット名、イメージフォーマット、イメージID、
  10. インスタンス.image_ref)
  11. self._snapshot_domain(コンテキスト、live_snapshot、virt_dom、状態、
  12. 実例)
  13. self._image_api です。更新(コンテキスト、image_id、メタデータ、
  14. purge_props = False )
  15. (NotImplementedError、exception.ImageUnacceptable)を除き e:
  16. ...

Nova はまず、disk_path を通じて対応するイメージ バックエンドを取得し、imagebackend.Rbd を返します。次に、バックエンドの direct_snapshot() メソッドを呼び出します。これは次のようになります。

  1. def direct_snapshot(self, コンテキスト, スナップショット名, イメージフォーマット,
  2. イメージID、ベースイメージID):
  3. fsid = self.driver.get_fsid()
  4. parent_pool = self._get_parent_pool(コンテキスト、ベースイメージID、fsid)
  5.  
  6. self.driver.create_snap(self.rbd_name, スナップショット名, protect= True )
  7. 場所 = { 'url' : 'rbd://%(fsid)s/%(pool)s/%(image)s/%(snap)s' %
  8. dict(fsid=fsid,
  9. プール=self.pool、
  10. イメージ=self.rbd_name、
  11. snap=スナップショット名)}
  12. 試す:
  13. self.driver.clone(場所、イメージID、dest_pool=親プール)
  14. self.driver.flatten(イメージID、プール=親プール)
  15. ついに:
  16. self.cleanup_direct_snapshot(場所)
  17. self.driver.create_snap(image_id, 'snap' , pool=parent_pool,
  18. 保護 = True )
  19. 戻り値( 'rbd://%(fsid)s/%(pool)s/%(image)s/snap' %
  20. dict(fsid=fsid, pool=parent_pool, image=image_id))

コードを分析すると、おおまかに次のステップに分けられます。

  • Ceph クラスターの fsid を取得します。
  • 仮想マシンのルート ディスクに対応する rbd イメージの一時スナップショットを作成します。スナップショット名はランダムな UUID です。
  • 作成したスナップショットを保護します(保護)。
  • スナップショットに基づいて、スナップショット uuid という名前の新しい rbd イメージを複製します。
  • クローンされたイメージに対してフラット化操作を実行します。
  • 作成された一時スナップショットを削除します。
  • クローンされた rbd イメージのスナップショットを作成し、snap という名前を付けて、protect を実行します。

rbd コマンドに対応して、仮想マシンの uuid が cbf44290-f142-41f8-86e1-d63c902b38ed で、スナップショットの uuid が db2b6552-394a-42d2-9de8-2295fe2b3180 であると仮定すると、対応する rbd コマンドは次のようになります。

  1. # ディスクのスナップショットを作成し Glance のストレージ プールクローンします
  2. rbd -p openstackスナップ作成\
  3. cbf44290-f142-41f8-86e1-d63c902b38ed_disk@3437a9bbba5842629cc76e78aa613c70
  4. rbd -p openstack スナップ保護 \
  5. cbf44290-f142-41f8-86e1-d63c902b38ed_disk@3437a9bbba5842629cc76e78aa613c70
  6. rbd -p openstackクローン\
  7. cbf44290-f142-41f8-86e1-d63c902b38ed_disk@3437a9bbba5842629cc76e78aa613c70 \
  8. db2b6552-394a-42d2-9de8-2295fe2b3180
  9. # イメージをフラット化して、ソーススナップショットから切り離します
  10. rbd -p openstack フラット化 db2b6552-394a-42d2-9de8-2295fe2b3180
  11. #ソーススナップショットすべて完了しました。クリーンアップします
  12. rbd -p openstack snap の保護を解除 \
  13. cbf44290-f142-41f8-86e1-d63c902b38ed_disk@3437a9bbba5842629cc76e78aa613c70
  14. rbd -p openstack スナップ rm \
  15. cbf44290-f142-41f8-86e1-d63c902b38ed_disk@3437a9bbba5842629cc76e78aa613c70
  16. # 'snap'という保護されたスナップショットを作成します アップロードされた画像それを配布する 
  17. rbd -p openstack スナップ作成db2b6552-394a-42d2-9de8-2295fe2b3180@snap
  18. rbd -p openstack スナップ保護 db2b6552-394a-42d2-9de8-2295fe2b3180@snap

3437a9bbba5842629cc76e78aa613c70 は一時スナップショットの名前であり、ランダムに生成された UUID です。

意味合い

他のストレージバックエンドでは主にイメージのアップロードに時間がかかりますが、Ceph ストレージを使用する場合は主に rbd フラット化プロセスに時間がかかるため、仮想マシンのスナップショットの作成には通常数分かかります。なぜフラット化操作を実行する必要があるのか​​疑問に思う人もいるかもしれません。直接クローンすることはできないのでしょうか?コミュニティがこれを実行するのには理由があります:

  • フラット化操作が実行されない場合、仮想マシンのスナップショットは仮想マシンに依存します。つまり、スナップショットが存在する限り仮想マシンを削除することはできず、これは明らかに不合理です。
  • 前の質問を拡張して、スナップショットに基づいて仮想マシンが作成され、仮想マシンがスナップショットを作成し、このサイクルが繰り返されるとします。 rbd イメージ全体の依存関係が非常に複雑になり、管理できなくなります。
  • rbd イメージ チェーンが長くなるにつれて、対応する IO 読み取りおよび書き込みパフォーマンスも低下します。

3.4 仮想マシンの削除

libvirt ドライバーが仮想マシンを削除するコードは、nova/virt/libvirt/driver.py の destroy メソッドにあります。

  1. def destroy(self, context, instance, network_info, block_device_info=None,
  2. destroy_disks = True ):
  3. self._destroy(インスタンス)
  4. self.cleanup(コンテキスト、インスタンス、ネットワーク情報、ブロックデバイス情報、
  5. ディスクを破棄する

前の _destroy メソッドは実際には仮想マシンのシャットダウン操作であることに注意してください。つまり、Nova は最初に仮想マシンをシャットダウンしてから削除操作を実行します。次に、cleanup() メソッドが呼び出され、リソースのクリーンアップが実行されます。ここでは、ディスクをクリーニングするプロセスにのみ焦点を当てます。

  1. ...
  2. destroy_disksの場合:
  3. # NOTE(haomai): 必要に応じてボリュームを破棄する
  4. CONF.libvirt.images_type == 'lvm'の場合:
  5. self._cleanup_lvm(インスタンス、ブロックデバイス情報)
  6. CONF.libvirt.images_type == 'rbd'の場合:
  7. self._cleanup_rbd(インスタンス)
  8. ...

イメージ タイプは rbd なので、_cleanup_rbd() メソッドが呼び出されます。

  1. def _cleanup_rbd(self, インスタンス):
  2. instance.task_state == task_states.RESIZE_REVERTING の場合:
  3. filter_fn = lambda disk: (disk.startswith(instance.uuid)および 
  4. ディスク.endswith( 'ディスク.local' ))
  5. それ以外
  6. filter_fn = lambda ディスク: disk.startswith(instance.uuid)
  7. LibvirtDriver._get_rbd_driver().cleanup_volumes(filter_fn)

通常の削除操作のみを考慮し、サイズ変更の元に戻す操作は無視します。そのため、filter_fn は lambda disk: disk.startswith(instance.uuid)、つまり仮想マシンの uuid で始まるすべてのディスク (rbd イメージ) になります。ここでは imagebackend の Rbd ドライバーは呼び出されず、ストレージ ドライバーが直接呼び出されることに注意してください。コードは nova/virt/libvirt/storage/rbd_utils.py にあります:

  1. cleanup_volumesを定義します(self、filter_fn):
  2. RADOSClient(self, self.pool)クライアントとして使用します:
  3. ボリューム = RbdProxy().list(client.ioctx)
  4. フィルター内のボリューム(filter_fn, volumes):
  5. self._destroy_volume(クライアント、ボリューム)

このメソッドは、まずすべての rbd イメージのリストを取得し、次に filter_fn メソッドを使用して仮想マシンの uuid から始まるイメージをフィルタリングし、_destroy_volume メソッドを呼び出します。

  1. def _destroy_volume(self, client, volume, pool=None):
  2. "" "RBD ボリュームを破棄し、必要に応じて再試行します。" ""  
  3. def _cleanup_vol(ioctx、ボリューム、retryctx):
  4. 試す:
  5. RbdProxy().remove(ioctx, ボリューム)
  6. loopingcall.LoopingCallDone(retvalue= False )を発生させます。
  7. rbd.ImageHasSnapshotsを除く:
  8. self.remove_snap(ボリューム、libvirt_utils.RESIZE_SNAPSHOT_NAME、
  9. ignore_errors = True )
  10. (rbd.ImageBusy、rbd.ImageHasSnapshotsを除く):
  11. LOG.warning( 'rbd によるプール %(pool)s 内の %(volume)s の削除に失敗しました' ,
  12. { 'volume' : ボリューム、 'pool' : self.pool})
  13. retryctx[ '再試行' ] -= 1
  14. retryctx[ 'retries' ] <= 0の場合:
  15. loopingcall.LoopingCallDone() を発生させる
  16.  
  17. # NOTE(danms): 10秒間放置します
  18. retryctx = { '再試行' : 10}
  19. タイマー = loopingcall.FixedIntervalLoopingCall(
  20. _cleanup_vol、client.ioctx、ボリューム、retryctx)
  21. timed_out = timer.start(interval=1).wait()
  22. タイムアウトの場合:
  23. # NOTE(danms):エラーを伝播させるためにこれをもう一度実行しますが、
  24. # 成功した場合は、loopingcall例外を発生させない
  25. 試す:
  26. _cleanup_vol(クライアント.ioctx、ボリューム、再試行ctx)
  27. loopingcall.LoopingCallDoneを除く:
  28. 合格

このメソッドは、_cleanup_vol() メソッドを使用して rbd イメージを最大 10+1 回削除しようとします。スナップショットがある場合は、まずそれが削除されます。

仮想マシンの uuid が cbf44290-f142-41f8-86e1-d63c902b38ed であると仮定すると、対応する rbd コマンドはおおよそ次のようになります。

  1. イメージ場合は$(rbd -p openstack ls | grep '^cbf44290-f142-41f8-86e1-d63c902b38ed' );
  2. rbd -p openstack rm "$image"を実行します
  3. 終わり

4 シンダー

4.1 Cinderの紹介

Cinder は AWS の EBS に似た OpenStack のブロック ストレージ サービスであり、管理するエンティティはボリュームです。 Cinder はボリュームプロバイダー機能を実装していませんが、Ceph、Fujitsu、NetApp などのさまざまなストレージシステムのボリュームの管理を担当します。ボリュームの作成、スナップショット、バックアップなどの機能をサポートしています。接続されたストレージ システムはバックエンドと呼ばれます。 cinder/volume/driver.py の VolumeDriver クラスで定義されたインターフェースが実装されている限り、Cinder はストレージ システムに接続できます。

Cinder はローカル ボリュームの管理をサポートするだけでなく、別の Ceph クラスターや Swift オブジェクト ストレージ システムなどのリモート ストレージ システムにローカル ボリュームをバックアップすることもできます。この記事では、ソース Ceph クラスターからリモート Ceph クラスターにバックアップするケースのみを検討します。

4.2 ボリュームの作成

ボリュームは cinder-volume サービスによって作成され、エントリ ポイントは cinder/volume/manager.py の create_volume() メソッドです。

  1. def create_volume(self, context, volume, request_spec=None,
  2. filter_properties=なし、allow_reschedule= True ):
  3. ...
  4. 試す:
  5. # NOTE(flaper87): ドライバの初期化 
  6. #タスク自体によって検証されます。
  7. フローエンジン = create_volume.get_flow(
  8. コンテキスト_昇格、
  9. 自己、
  10. 自己.db、
  11. 自己ドライバー、
  12. 自己.scheduler_rpcapi、
  13. 自己ホスト、
  14. 音量、
  15. 再スケジュールを許可、
  16. コンテクスト、
  17. リクエスト仕様、
  18. フィルタープロパティ、
  19. image_volume_cache=self.image_volume_cache、
  20. 例外を除く:
  21. msg = _( "マネージャーボリュームフローの作成に失敗しました。" )
  22. LOG.exception(msg、リソース={ 'type' : 'volume' 'id' :volume.id})
  23. 例外を発生させます。CinderException(msg)
  24. ...

Cinder はタスクフロー フレームワークを使用してボリュームを作成します。タスクフローの具体的な実装は、cinder/volume/flows/manager/create_volume.py にあります。ここでは、execute() メソッドに注目します。

  1. def execute (self、コンテキスト、ボリューム、ボリュームスペック):
  2. ...
  3. create_type == 'raw'の場合:
  4. model_update = self._create_raw_volume(ボリューム、**volume_spec)
  5. elif create_type == 'snap' の場合:
  6. model_update = self._create_from_snapshot(コンテキスト、ボリューム、
  7. **ボリュームスペック)
  8. elif create_type == 'source_vol' :
  9. model_update = self._create_from_source_volume(
  10. コンテキスト、ボリューム、**volume_spec)
  11. elif create_type == 'image' :
  12. model_update = self._create_from_image(コンテキスト、
  13. 音量、
  14. **ボリュームスペック)
  15. それ以外
  16. 例外が発生します。VolumeTypeNotFound(volume_type_id=create_type)
  17. ...

コードから、ボリューム作成には 4 つのタイプがあることがわかります。

  • raw: 空のボリュームを作成します。
  • スナップショットから作成: スナップショットに基づいてボリュームを作成します。
  • ボリュームから作成: これは既存のボリュームをコピーするのと同じです。
  • イメージから作成: Glance イメージに基づいてボリュームを作成します。

最も簡単な方法は、空のボリュームを作成することです。コードは次のとおりです。

  1. def _create_raw_volume(self, ボリューム, **kwargs):
  2. ret = self.driver.create_volume(ボリューム)
  3. ...

ドライバーの create_volume() メソッドを直接呼び出します。ドライバーは RBDDriver で、コードは cinder/volume/drivers/rbd.py にあります。

  1. def create_volume(self, ボリューム):
  2. RADOSClient(self)クライアントとして:
  3. self.RBDProxy() です。作成(client.ioctx、
  4. ボリューム名、
  5. サイズ
  6. 注文
  7. 古いフォーマット = False
  8. 機能=クライアント.機能)
  9.  
  10. 試す:
  11. volume_update = self._enable_replication_if_needed(ボリューム)
  12. 例外を除く:
  13. self.RBDProxy().remove(client.ioctx、vol_name) を実行します。
  14. err_msg = (_( 'イメージのレプリケーションを有効にできませんでした' ))
  15. 例外を発生させます。ReplicationError(reason=err_msg,
  16. ボリュームID=ボリュームID)

サイズの単位は MB で、vol_name は volume-${volume_uuid} です。

ボリュームの uuid が bf2d1c54-6c98-4a78-9c20-3e8ea033c3db、Ceph プールが openstack、作成されたボリュームのサイズが 1GB であると仮定すると、対応する rbd コマンドは次のようになります。

  1. rbd -p openstack作成\
  2. --新しいフォーマット --サイズ 1024 \  
  3. ボリューム-bf2d1c54-6c98-4a78-9c20-3e8ea033c3db

これは rbd コマンドで確認できます。

  1. int32bit $ rbd -p openstack ls | grep bf2d1c54-6c98-4a78-9c20-3e8ea033c3db
  2. ボリューム-bf2d1c54-6c98-4a78-9c20-3e8ea033c3db

スナップショットから作成

スナップショットからボリュームを作成する場合も、次のようにドライバー メソッドを直接呼び出します。

  1. def _create_from_snapshot(self, コンテキスト, ボリューム, スナップショットID,
  2. ** クワルグ):
  3. スナップショット = objects.Snapshot.get_by_id(コンテキスト、スナップショットID)
  4. model_update = self.driver.create_volume_from_snapshot(ボリューム、
  5. スナップショット

RBDDriver の create_volume_from_snapshot() メソッドを見てみましょう。

  1. def create_volume_from_snapshot(自己、ボリューム、スナップショット):
  2. "" "スナップショットからボリュームを作成します。" 「」  
  3. volume_update = self._clone(ボリューム、self.configuration.rbd_pool、
  4. snapshot.volume_name、スナップショット。名前
  5. self.configuration.rbd_flatten_volume_from_snapshotの場合:
  6. self._flatten(self.configuration.rbd_pool、ボリューム)
  7. int (volume.size )の場合:
  8. self._resize(ボリューム)
  9. ボリューム更新を返す

コードからわかるように、スナップショットからスナップショットを作成する手順は 3 つのステップに分かれています。

  • rbd スナップショットからクローン操作を実行します。
  • rbd_flatten_volume_from_snapshot が True に設定されている場合、フラット化操作が実行されます。
  • 作成時にサイズが指定されている場合は、サイズ変更操作が実行されます。

新しく作成されたボリュームの uuid が e6bc8618-879b-4655-aac0-05e5a1ce0e06、スナップショットの uuid が snapshot-e4e534fc-420b-45c6-8e9f-b23dcfcb7f86、スナップショットのソース ボリューム uuid が bf2d1c54-6c98-4a78-9c20-3e8ea033c3db、指定されたサイズが 2、rbd_flatten_volume_from_snapshot が False (デフォルト値) であると仮定すると、対応する rbd コマンドは次のようになります。

  1. rbd クローン openstack/volume-bf2d1c54-6c98-4a78-9c20-3e8ea033c3db@snapshot-e4e534fc-420b-45c6-8e9f-b23dcfcb7f86 openstack/volume-e6bc8618-879b-4655-aac0-05e5a1ce0e06  
  2. rbd resize --size 2048 openstack/volume-e6bc8618-879b-4655-aac0-05e5a1ce0e06  

ソース コードの分析から、Cinder がスナップショットからボリュームを作成するときに、ユーザーはフラット化操作を実行するかどうかを構成できます。

  • フラット化操作を実行する場合、スナップショットからボリュームを作成するのに数分かかる場合があります。スナップショットは作成後いつでも削除できます。
  • フラット化操作を実行しない場合は、スナップショットに基づいて作成されたすべてのボリュームを削除する前に、スナップショットまたはスナップショットのソース ボリュームを削除できないことに注意してください。

2 番目の点はより複雑かもしれません。たとえば、スナップショットに基づいてボリュームが作成され、そのボリュームに基づいてスナップショットが作成され、そのスナップショットに基づいてボリュームが作成された場合、ユーザーはソース ボリュームまたはスナップショットを削除できません。

ボリュームから作成

ボリュームからボリュームを作成するには、ソース ボリューム ID (source_volid) を指定する必要があります。

  1. def _create_from_source_volume(self, コンテキスト, ボリューム, ソースボリュームid,
  2. ** クワルグ):
  3. # NOTE(harlowja): ソースボリュームが消えてしまった場合、これが
  4. #このデータベース呼び出しは失敗するはずなので、それ検出します
  5. #
  6. # NOTE(harlowja): これおそらく これが起こる最適な場所ではない
  7. #そして、アクションの実行中はソースボリューム適切なロックをかける必要があります
  8. ソースボリュームを使用する # が進行中です。
  9. srcvol_ref = objects.Volume.get_by_id(コンテキスト、ソースボリュームID)
  10. model_update = self.driver.create_cloned_volume(ボリューム、srcvol_ref)

ドライバーの create_cloned_volume() メソッドを直接確認します。このメソッドには、rbd イメージ クローンに対して許可される最大深度である、非常に重要な構成項目 rbd_max_clone_depth があります。 rbd_max_clone_depth <= 0 の場合、クローン作成は許可されません。

  1. #要求があれば完全なコピーを行う
  2. self.configuration.rbd_max_clone_depth <= 0 の場合:
  3. RBDVolumeProxy(self, src_name, read_only= True )を volとして使用します
  4. vol.copy(vol.ioctx, 宛先名)
  5. self._extend_if_required(ボリューム、src_vref)
  6. 戻る 

これは rbd copy コマンドと同等です。

rbd_max_clone_depth > 0 の場合:

  1. # それ以外の場合は COW クローンを実行します。
  2. RADOSClient(self)クライアントとして:
  3. src_volume = self.rbd.Image(client.ioctx、src_name)
  4. LOG.debug( "スナップショットを作成しています='%s'" 、 clone_snap)
  5. 試す:
  6. #ソースボリューム新しいスナップショットを作成する
  7. src_volume.create_snap(クローン_snap)
  8. src_volume.protect_snap(クローン_snap)
  9. # ソースボリュームのスナップショットをクローンします
  10. LOG.debug( "'%(src_vol)s@%(src_snap)s' を " にクローンしています 
  11. 「'%(dest)s'」
  12. { 'src_vol' : src_name, 'src_snap' : clone_snap,
  13. 'dest' : 宛先名})
  14. self.RBDProxy().clone(client.ioctx, src_name, clone_snap,
  15. client.ioctx、dest_name、
  16. 機能=クライアント.機能)

このプロセスは、仮想マシンのスナップショットを作成するプロセスと非常に似ています。どちらも、まずソース イメージに基づいてスナップショットを作成し、次にスナップショットに基づいてクローン操作を実行します。違いは、フラット化操作が実行されるかどうかにあります。仮想マシンのスナップショットを作成するときは、フラット化操作を実行する必要があり、操作はクローンの深さによって異なります。

  1. 深さ = self._get_clone_depth(クライアント、src_name)
  2. 深さ >= self.configuration.rbd_max_clone_depth の場合:
  3. dest_volume = self.rbd.Image(client.ioctx、dest_name)
  4. 試す:
  5. dest_volume.flatten()
  6. except例外をeとして:
  7. ...
  8.  
  9. 試す:
  10. src_volume.unprotect_snap(クローン_snap)
  11. src_volume.remove_snap(クローン_snap)
  12. except例外をeとして:
  13. ...

現在の深度が最大許容深度 rbd_max_clone_depth を超える場合、フラット化操作が実行され、作成されたスナップショットが削除されます。

作成されたボリュームの UUID が 3b8b15a4-3020-41a0-80be-afaa35ed5eef で、ソース ボリュームの UUID が bf2d1c54-6c98-4a78-9c20-3e8ea033c3db であると仮定すると、対応する rbd コマンドは次のようになります。

  1. ボリュームID=3b8b15a4-3020-41a0-80be-afaa35ed5eef
  2. ソースボリュームID=bf2d1c54-6c98-4a78-9c20-3e8ea033c3db
  3. CINDER_POOL = オープンスタック
  4. # rbd_max_clone_depth <= 0 の場合は完全コピーを実行します。
  5. [[ "$rbd_max_clone_depth" -le 0 ]]の場合;次に、 rbd copy ${CINDER_POOL}/volume-${SOURCE_VOLID} openstack/volume-${VOLID} を実行します。
  6. 終了 0
  7. フィ
  8. # それ以外の場合は COW クローンを実行します。
  9. #ソースボリューム新しいスナップショットを作成する
  10. rbd スナップは${CINDER_POOL}/volume-${SOURCE_VOLID}@volume-${VOLID}.clone_snapを作成します。
  11. rbd スナップ保護 ${CINDER_POOL}/volume-${SOURCE_VOLID}@volume-${VOLID}.clone_snap
  12. # ソースボリュームのスナップショットをクローンします
  13. rbdクローン\
  14. ${CINDER_POOL}/volume-${SOURCE_VOLID}@volume-${VOLID}.clone_snap \
  15. ${CINDER_POOL}/ボリューム-${VOLID}
  16. # 宛先ボリュームがクローンであり rbd_max_clone_depthに達した場合、
  17. # クローン作成後にdestをフラット化します
  18. 深さ=$(get_clone_depth ${CINDER_POOL}/ボリューム-${VOLID})
  19. [[ "$depth" -ge "$rbd_max_clone_depth" ]] の場合;それから 
  20. # 宛先ボリュームをフラット化する
  21. rbd フラット化 ${CINDER_POOL}/ボリューム-${VOLID}
  22. #一時スナップを削除
  23. rbd スナップ保護解除 \
  24. ${CINDER_POOL}/volume-${SOURCE_VOLID}@volume-${VOLID}.clone_snap
  25. rbd スナップ rm ${CINDER_POOL}/volume-${SOURCE_VOLID}@volume-${VOLID}.clone_snap
  26. フィ

rbd_max_clone_depth > 0 かつ depth < rbd_max_clone_depth の場合、rbd コマンドで確認します。

  1. int32bit $ rbd情報ボリューム-3b8b15a4-3020-41a0-80be-afaa35ed5eef
  2. rbd イメージ'volume-3b8b15a4-3020-41a0-80be-afaa35ed5eef' :
  3. サイズ1024 MB オブジェクト数 256
  4. オーダー22 (4096 kB オブジェクト)
  5. ブロック名プレフィックス: rbd_data.ae2e437c177a
  6. フォーマット: 2
  7. 機能: レイヤー化、排他ロック、オブジェクト マップ、高速差分、ディープ フラット化
  8. フラグ:
  9. create_timestamp: 2017年11月22日水曜日12:32:09
  10. 親: openstack/volume-bf2d1c54-6c98-4a78-9c20-3e8ea033c3db@volume-3b8b15a4-3020-41a0-80be-afaa35ed5eef.clone_snap
  11. 重複: 1024 MB

volume-3b8b15a4-3020-41a0-80be-afaa35ed5eef の親は次のようになります。

  1. volume-bf2d1c54-6c98-4a78-9c20-3e8ea033c3db@volume-3b8b15a4-3020-41a0-80be-afaa35ed5eef.clone_snap`

画像から作成

イメージからボリュームを作成します。 Glance と Cinder が同じ Ceph クラスターを使用していると仮定すると、Cinder はイメージをダウンロードせずに Glance から直接クローンを作成できます。

  1. def _create_from_image(self, コンテキスト, ボリューム,
  2. 画像の場所、画像ID、画像メタ、
  3. image_service、**kwargs):
  4. ...
  5. model_update、クローン = self.driver.clone_image(
  6. コンテクスト、
  7. 音量、
  8. 画像の場所、
  9. イメージメタ、
  10. イメージサービス
  11. ...

ドライバーの clone_image() メソッドを見てみましょう。

  1. def clone_image(self, コンテキスト, ボリューム,
  2. 画像の場所、画像のメタ情報、
  3. イメージサービス):
  4. #すべての場所を反復処理してクローン可能な場所探します
  5. url_locationsurl_locationの場合:
  6. url_locationself._is_cloneable(の場合
  7. url_location、image_meta):
  8. _prefix、プール、イメージ、スナップショット = \
  9. self._parse_location(url_location)
  10. volume_update = self._clone(ボリューム、プール、イメージ、スナップショット)
  11. volume_update[ 'provider_location' ] = なし
  12. self._resize(ボリューム)
  13. volume_update をTrue で返す 
  14. return ({}、 false

RBDは直接クローンであり、このプロセスは基本的に仮想マシンの作成と同じです。ボリュームを作成するときに新しいサイズが指定されている場合は、RBDのサイズを呼び出して容量拡張操作を実行します。

新しく作成されたボリュームUUIDが87EE1EC6-3FE4-413B-A4C0-8EC7756BF1B4であると仮定して、GLANCE画像UUIDはDB2B6552-394A-42D2-9DE8-2295FE2B3180、RBDコマンドは次のとおりです。

  1. RBDクローンOpenStack/DB2B6552-394A-42D2-9DE8-2295FE2B3180@snap \
  2. OpenStack/Volume-87EE1EC6-3FE4-413B-A4C0-8EC7756BF1B4
  3.  
  4. if [[-n "$ size" ]];その後、 RBDはsezize -size $ size \  
  5. OpenStack/Volume-87EE1EC6-3FE4-413B-A4C0-8EC7756BF1B4
  6. フィ

次のようにRBDコマンドを介して確認します。

  1. int32bit $ rbd info openstack/volume-87ee1ec6-3fe4-413b-a4c0-8ec7756bf1b4
  2. RBD画像'Volume-87EE1EC6-3FE4-413B-A4C0-8EC7756BF1B4'
  3. 768オブジェクトサイズ3072 MB
  4. 注文22(4096 KBオブジェクト)
  5. block_name_prefix:rbd_data.affc488ac1a
  6. 形式:2
  7. 機能:レイヤー化、排他的ロック、オブジェクトマップ、高速ディフ、ディープフラッテン
  8. フラグ:
  9. create_timestamp:12月22日水曜日13:07:50 2017年
  10. 親:OpenStack/DB2B6552-394A-42D2-9DE8-2295FE2B3180@SNAP
  11. オーバーラップ:2048 MB

新しく作成されたRBD画像の親は、OpenStack/DB2B6552-394A-42D2-9DE8-2295FE2B3180@SNAPであることがわかります。

注:実際、私はこの方法がフラットな操作を実行する必要があると個人的に考えています。それ以外の場合、ボリュームが存在する場合、Glanceは画像を削除できません。これは、GINDERサービスのステータスに依存するGLANCEサービスに相当します。これは少し不合理です。

4.3スナップショットの作成

スナップショットエントリは、create_snapshot()cinder/volume/manager.pyの方法です。この方法では、TaskFlow Frameworkを使用するのではなく、次のようにドライバーCreate_SnapShot()メソッドを直接呼び出します。

  1. ...
  2. 試す:
  3. utils.require_driver_initialized(self.driver)
  4. snapshot.context = context
  5. model_update = self.driver.create_snapshot(snapshot)
  6. ...
  7. 例外を除く:
  8. ...

rbddriverのcreate_snapshot()メソッドは非常に簡単です:

  1. def create_snapshot(self、snapshot):
  2. "" "RBDスナップショットを作成します。" 「」  
  3. rbdvolumeproxy(self、snapshot.volume_name)ボリュームとして
  4. snap = utils.convert_str( snapshot.name
  5. volume.create_snap(snap)
  6. volume.protect_snap(snap)

したがって、ボリュームのスナップショットは、実際にはCeph RBD画像スナップショットに対応しています。 Snapshot UUIDがE4E534FC-420B-45C6-8E9F-B23DCFCB7F86であり、ボリュームUUIDがBF2D1C54-6C98-4A78-9C20-3E8EA033C3C3DBであると仮定します。

  1. rbd -p openstack snap create \
  2. Volume-BF2D1C54-6C98-4A78-9C20-3E8EA033C3C3DB@SNAPSHOT-E4E534FC-420-45C6-8E9F-B23DCFCB7F86
  3. RBD -P OpenStack Snap Protect \
  4. Volume-BF2D1C54-6C98-4A78-9C20-3E8EA033C3C3DB@SNAPSHOT-E4E534FC-420-45C6-8E9F-B23DCFCB7F86

ここから、仮想マシンのスナップショットとボリュームスナップショットの違いを見ることができます。仮想マシンのスナップショットは、ルートディスクRBDイメージスナップショットからクローン化され、平らにする必要がありますが、ボリュームスナップショットはRBDイメージスナップショットを作成するだけです。したがって、仮想マシンのスナップショットには通常数分かかりますが、ボリュームスナップショットは数秒で完了できます。

4.4ボリュームバックアップを作成します

ボリュームバックアップを理解する前に、まずスナップショットとバックアップの違いを明確にする必要があります。 Git Analogyを使用でき、スナップショットはGITコミット操作に似ています。これは、主にバックトラッキングとロールバックに使用されるデータが送信されていることのみを示しています。クラスターがクラッシュすると、通常、データはスナップショットから完全に回復しません。バックアップはGit Pushに似ており、データセキュリティをリモートストレージシステムにプッシュし、主にデータセキュリティを確保するために使用され、ローカルデータが失われた場合でもバックアップから復元できます。 Cinderのディスクバックアップは、複数のストレージバックエンドもサポートしています。ここでは、ボリュームとバックアップドライバーが両方ともCEPHである状況を検討します。その他の詳細については、Cinderデータボリュームバックアップの原則と実践を参照してください。生産では、ボリュームとバックアップが異なるCephクラスターを使用して、ボリュームCEPHクラスターがハングしたときに、データを別のクラスターから迅速に回復できるようにするために使用する必要があります。この記事は機能をテストするためだけのものであるため、同じCephクラスターを使用しています。プールの区別により、VolumeはOpenStackPoolを使用し、バックアップはCinder_Backuppoolを使用します。

さらに、Cinderはインクリメンタルバックアップをサポートし、ユーザーは-incrementalパラメーターを指定して、フルバックアップまたは増分バックアップを使用するかどうかを決定できます。ただし、Ceph Backendの場合、Cinderは常に最初にインクリメンタルバックアップを実行しようとします。インクリメンタルバックアップが失敗した場合にのみ、ユーザーが - インクリメンタルパラメーターを指定したかどうかに関係なく、完全なバックアップにフォールバックします。それにもかかわらず、バックアップを完全なバックアップとインクリメンタルバックアップの2つの状況に分割します。最初のバックアップのみが完全なバックアップになり、残りのバックアップはインクリメンタルバックアップであることに注意してください。

フルバックアップ(***バックアップ)

cephbackupdriverのバックアップ()メソッドを直接見て、コードはcinder/backup/drivers/ceph.pyにあります。

  1. self._file_is_rbd(volume_file)の場合:
  2. #RBDのボリュームの場合、インクリメンタルバックアップを試みます。
  3. log.debug( "ボリュームファイルはRBD:インクリメンタルバックアップを試みます。"
  4. 試す:
  5. 更新= self._backup_rbd(backup、volume_file、
  6. 音量。名前、長さ)
  7. 例外を除く.backuprbdoperationfailed:
  8. log.debug( "Volume%s。"の完全なバックアップの強制
  9. do_full_backup = true  

ここでは、主にソースボリュームがRBDであるか、つまりCephバックエンドが使用されているかどうかを決定します。増分バックアップは、ボリュームがCeph Storageバックエンドも使用する場合にのみ実行できます。

_backup_rbd()メソッドを確認しましょう。

  1. from_snap = self._get_most_recent_snap(source_rbd_image)
  2. base_name = self._get_backup_base_name(volume_id、diff_format = true
  3. image_created = false  
  4. rbd_driver.radosclient(self、backup.container)クライアントとして
  5. base_nameではない場合  in self.rbd.rbd()。リスト(ioctx = client.ioctx):
  6. ...
  7. 新しいベースイメージを作成します
  8. self._create_base_image(base_name、length、client)
  9. image_created = true  
  10. それ以外
  11. ...

from_snapは、最後のバックアップのスナップショットポイントです。私たちが最初のバックアップであったため、from_snapはなしであり、base_name形式はボリューム - %s.backup.baseです。このベースは何をしますか? _create_base_image()メソッドを確認しましょう。

  1. def _create_base_image(self、 name size 、rados_client):
  2. old_format、feature = self._get_rbd_support()
  3. self.rbd.rbd()。 create (ioctx = rados_client.ioctx、
  4. name = name
  5. サイズ=サイズ
  6. old_format = old_format、
  7. 機能=機能、
  8. stripe_unit = self.rbd_stripe_unit、
  9. stripe_count = self.rbd_stripe_count)

ベースは実際には、前のボリュームと同じサイズの空のボリュームであることがわかります。

つまり、最初のバックアップである場合、バックアップのCephクラスターは最初にボリュームと同じサイズの空のボリュームを作成します。

ソースコードを引き続き見てみましょう。

  1. def _backup_rbd(self、backup、volume_file、volume_name、length):
  2. ...
  3. new_snap = self._get_new_snap_name(backup.id)
  4. log.debug( "バックアップSnapshot = '%s'" 、new_snapの作成)
  5. source_rbd_image.create_snap(new_snap)
  6.  
  7. 試す:
  8. self._rbd_diff_transfer(volume_name、rbd_pool、base_name、
  9. backup.container、
  10. src_user = rbd_user、
  11. src_conf = rbd_conf、
  12. dest_user = self._ceph_backup_user、
  13. dest_conf = self._ceph_backup_conf、
  14. src_snap = new_snap、
  15. from_snap = from_snap)
  16.                              
  17. def _get_new_snap_name(self、backup_id):
  18. return utils.convert_str( "backup。%s.snap。%s"  
  19. backup_id time。time ()))

まず、ソースボリュームに新しいスナップショットが作成され、スナップショットはバックアップと呼ばれます。$ {backup_id} .snap。$ {timestamp}、次にrbd_diff_transfer()メソッドが呼び出されます。

  1. def _rbd_diff_transfer(self、src_name、src_pool、dest_name、dest_pool、
  2. src_user、src_conf、dest_user、dest_conf、
  3. src_snap = none、from_snap = none):
  4. src_ceph_args = self._ceph_args(src_user、src_conf、pool = src_pool)
  5. dest_ceph_args = self._ceph_args(dest_user、dest_conf、pool = dest_pool)
  6.  
  7. cmd1 = [ 'rbd' 'export-diff' ] + src_ceph_args
  8. from_snap場合 なし
  9. cmd1.extend([ ' - from-snap' 、from_snap]))
  10. src_snapの場合:
  11. path = utils.convert_str( "%s/%s@%s"  
  12. %(src_pool、src_name、src_snap))
  13. それ以外
  14. path = utils.convert_str( "%s/%s" %(src_pool、src_name)))
  15. cmd1.extend([path、 ' - ' ])
  16.  
  17. cmd2 = [ 'rbd' 'import-diff' ] + dest_ceph_args
  18. rbd_path = utils.convert_str( "%s/%s" %(dest_pool、dest_name)))
  19. cmd2.extend([ ' - ' 、rbd_path])
  20.  
  21. ret、stderr = self._piped_execute(cmd1、cmd2)
  22. retの場合:
  23. msg =(_( "rbd diff op failed-(ret =%(ret)s stderr =%(stderr)s)" )%
  24. { 'ret' :ret、 'stderr' :stderr})
  25. log.info(msg)
  26. 例外を上げる.backuprbdoperationfailed(msg)

このメソッドは、RBDコマンドを呼び出し、最初にエクスポートディフサブコマンドを介してソースRBDイメージの違いファイルをエクスポートし、Import-Diffを介してバックアップ画像にインポートします。

ソースボリュームのUUIDが075C06ED-37E2-407D-B998-E270C4EDC53Cであると仮定し、サイズは1GBで、バックアップUUIDはDB563496-0C15-4349-95F3-FC5194BBB11Aであると仮定します。

  1. Volume_Id = 075C06ED-37E2-407D-B998-E270C4EDC53C
  2. backup_id = db563496-0c15-4349-95f3-fc5194bfb11a
  3. rbd -p cinder_backup create   - サイズ1024 volume-$ {volume_id} .backup.base  
  4. new_snap = volume-$ {volume_id}@backup.$ {backup_id }.snap.1511344566.67
  5. rbd -p openstack snap create $ {new_snap}
  6. rbd export -diff - プールopenstack $ {new_snap} - \  
  7. | rbd import-diff -pool cinder_backup-volume-$ {volume_id} .backup.base  

次のように、RBDコマンドを介して確認できます。

  1. #ボリュームセフクラスター
  2. int32bit $ rbd -p openstack snap ls volume-075c06ed-37e2-407d-b998-e270c4edc53c
  3. スナピッド                                                              サイズ タイムスタンプ 
  4. 52 backup.db563496-0c15-4349-95f3-fc5194bfb11a.snap.151134566.67 1024 mb水曜日11月22日17:56:15 2017
  5. #バックアップCEPHクラスター
  6. int32bit $ rbd -p cinder_backup ls -l
  7. 名前                                                                                                                    サイズの親FMTプロトロック
  8. Volume-075C06ED-37E2-407D-B998-E270C4EDC53C.BACKUP.BASE 1024M 2
  9. Volume-075C06ED-37E2-407D-B998-E270C4EDC53C.backup.base@backup.db5563496-0c15-4349-95f3-fc5194bfb11a.snap.15113456.67 1024m 2

出力から、ソースボリュームはID 52のスナップショットを作成します。バックアップのCEPH、ボリューム-075C06ED-37E2-407D-B998-E270C4EDC53C.BACKUP.BASEのCEPHクラスターに空のボリュームが作成されます。

増分バックアップ

前のプロセスは完全なバックアップと同じです。_backup_rbd()メソッドに直接ジャンプします。

  1. from_snap = self._get_most_recent_snap(source_rbd_image)
  2. rbd_driver.radosclient(self、backup.container)クライアントとして
  3. base_nameではない場合  in self.rbd.rbd()。リスト(ioctx = client.ioctx):
  4. ...
  5. それ以外
  6. self._snap_exists(base_name、from_snap、client)でない場合:
  7. errmsg =(_( "snapshot = '%(snap)s'はベースに存在しません"  
  8. "image = '%(base)s' - 増分の中止"  
  9. 「バックアップ」 )%
  10. { 'snap' :from_snap、 'base' :base_name})
  11. log.info(errmsg)
  12. 例外を上げる.backuprbdoperationfailed(errmsg)

まず、ソースボリュームに対応するRBD画像の最も親のスナップショットを取得し、バックアップのCephクラスターのベースに同じスナップショットがあるかどうかを判断します(前のバックアップによると、ソースボリュームと同じスナップショットが必要です。

後の部分を見続けましょう:

  1. new_snap = self._get_new_snap_name(backup.id)
  2. source_rbd_image.create_snap(new_snap)
  3.  
  4. 試す:
  5. 前=時間時間()
  6. self._rbd_diff_transfer(volume_name、rbd_pool、base_name、
  7. backup.container、
  8. src_user = rbd_user、
  9. src_conf = rbd_conf、
  10. dest_user = self._ceph_backup_user、
  11. dest_conf = self._ceph_backup_conf、
  12. src_snap = new_snap、
  13. from_snap = from_snap)
  14. from_snapの場合:
  15. source_rbd_image.remove_snap(from_snap)

これは基本的にフルバックアップと同じです。唯一の違いは、from_snapが現時点では誰もいないことであり、from_snapが後で削除されることです。 _RBD_DIFF_TRANSFERメソッドを使用して、前のコードを変換できます。

ソースボリュームUUIDが075C06ED-37E2-407D-B998-E270C4EDC53Cであると仮定します11a、対応するRBDコマンドはほぼ次のとおりです。

  1. Volume_Id = 075C06ED-37E2-407D-B998-E270C4EDC53C
  2. backup_id = e3db9e85-d352-47e2-bced-5bad68da853b
  3. db563496-0c15-4349-95f3-fc5194bfb11a
  4. rbd -p openstack snap create \
  5. volume-$ {volume_id}@backup.$ {backup_id} .snap.1511348180.27
  6. rbd export-diff -pool openstack \  
  7. - from-snap backup。$ {parent_id} .snap.15113445667 \ \  
  8. openstack/volume-$ {volume_id}@backup.$ {backup_id }.snap.1511348180.27- \
  9. | rbd import -diff -pool cinder_backup- \  
  10. cinder_backup/volume-$ {volume_id} .backup.base
  11. rbd -p openstack snap rm \
  12. volume-$ {volume_id} .backup.base@backup.$ {parent_id} .snap.151134566.67

次のように、RBDコマンドを介してそれを確認します。

  1. int32bit $ rbd -p openstack snap ls volume-075c06ed-37e2-407d-b998-e270c4edc53c
  2. スナピッド                                                              サイズ タイムスタンプ 
  3. 53 backup.e3db9e85-d352-47e2-bced-5bad68da853b.snap.1511348180.27 1024 MB水曜日11月22日18:56:20 2017
  4. int32bit $ rbd -p cinder_backup ls -l
  5. 名前                                                                                                                    サイズの親FMTプロトロック
  6. Volume-075C06ED-37E2-407D-B998-E270C4EDC53C.BACKUP.BASE 1024M 2
  7. Volume-075C06ED-37E2-407D-B998-E270C4EDC53C.backup.base@backup.db5563496-0c15-4349-95f3-fc5194bfb11a.snap.15113456.67 1024m 2
  8. Volume-075C06ED-37E2-407D-B998-E270C4EDC53C.backup.base@backup.e3db9e85-d352-47e2-bced-5bad68da853b.snap.1511348180.27 1024m 2

分析の結果と一致して、ソースボリュームのスナップショットは古いボリュームを削除し、上部のボリュームのみを保持し、バックアップはすべてのスナップショットを保持します。

4.5バックアップと回復

バックアップ回復は、バックアップの逆プロセスです。つまり、リモートストレージからローカルにデータを復元します。バックアップと回復のソースコードは、cinder/backup/drivers/ceph.pyのrestore()方法にあります。このメソッドは_restore_volume()メソッドを直接呼び出すので、_restore_volume()メソッドを直接見てみましょう。

  1. def _restore_volume(self、backup、volume、volume_file):
  2. length = int (volume。size * unit.gi
  3.  
  4. base_name = self._get_backup_base_name(backup.volume_id、
  5. diff_format = true
  6. rbd_driver.radosclient(self、backup.container)クライアントとして
  7. diff_allowed、restore_point = \
  8. self._diff_restore_allowed(base_name、backup、volume、
  9. volume_file、クライアント)

その中で、_diff_restore_allowed()は非常に重要な方法です。この方法は、直接輸入による回復をサポートするかどうかを決定します。この方法の実装を確認しましょう。

  1. def _diff_restore_allowed(self、base_name、backup、volume、volume_file、
  2. rados_client):
  3. rbd_exists、base_name = self._rbd_image_exists(base_name、
  4. backup.volume_id、
  5. rados_client)
  6. rbd_existsではない場合:
  7. 戻る 、なし
  8. restore_point = self._get_restore_point(base_name、backup.id)
  9. restore_pointの場合:
  10. self._file_is_rbd(volume_file)の場合:
  11. volume.id == backup.volume_idの場合:
  12. 戻る  false 、restore_point
  13. self._rbd_has_extents(volume_file.rbd_image)の場合:
  14. 戻る  false 、restore_point
  15. 戻る 確かに、restore_point

この方法から、データを回復するために差分インポート方法をサポートすることで、次のすべての条件を満たす必要があることがわかります。

  • バックアップクラスターのボリュームに対応するRBDベース画像が存在する必要があります。
  • 回復ポイントが存在する必要があります。つまり、バックアップベース画像に対応するスナップショットが存在する必要があります。
  • 回復ターゲットのボリュームはRBDでなければなりません。つまり、ボリュームのストレージバックエンドもCEPHでなければなりません。
  • 回復ターゲットのボリュームは空のボリュームである必要があり、既存のコンテンツを含む上書き画像をサポートしていません。
  • 回復ターゲットのボリュームUUIDとバックアップのソースボリュームUUIDは同じではありません。つまり、元のボリュームを上書きすることはできません。

言い換えれば、Cinderはデータの再構築を既存のボリューム(ソースボリュームを含む)にサポートしていますが、Ceph Backendを使用すると、増分回復がサポートされず、効率が非常に低くなります。

したがって、CEPHストレージバックエンドを使用する場合は、バックアップを空のボリューム(ボリュームを指定しない)に復元することをお勧めします。既存のボリュームに復元することはお勧めしません。

Cinderは、バックアップが取得した新しいボリュームまたは元のボリュームへの復元をサポートすることに注意してください。後者の場合、これは最も安全なアクションとみなされたため、完全なコピーが実施されます。したがって、常に新しいボリューム(デフォルト)に復元することをお勧めします。

ここでは、空のボリュームに復元すると仮定します。コマンドは次のとおりです。

  1. Cinder Backup-Restore -Name int32bit-Restore-1 \  
  2. E3DB9E85-D352-47E2-BCED-5BAD68DA853B

-volumeパラメーターを指定しなかったことに注意してください。現時点では、増分回復が実行され、コードは次のように実装されます。

  1. def _diff_restore_rbd(self、backup、restore_file、restore_name、
  2. restore_point、restore_length):
  3. rbd_user = restore_file.rbd_user
  4. rbd_pool = restore_file.rbd_pool
  5. rbd_conf = restore_file.rbd_conf
  6. base_name = self._get_backup_base_name(backup.volume_id、
  7. diff_format = true
  8. 前=時間時間()
  9. 試す:
  10. self._rbd_diff_transfer(base_name、backup.container、
  11. restore_name、rbd_pool、
  12. src_user = self._ceph_backup_user、
  13. src_conf = self._ceph_backup_conf、
  14. dest_user = rbd_user、dest_conf = rbd_conf、
  15. src_snap = restore_point)
  16. 例外を除く.backuprbdoperationfailed:
  17. 上げる
  18. self._check_restore_vol_size(backup、restore_name、restore_length、
  19. rbd_pool)

増分回復が非常に簡単であることがわかるのは非常に簡単です。以前に導入された_rbd_diff_transfer()メソッドを呼び出して、バックアップCEPHクラスターに対応するベース画像のスナップショットをボリュームCEPHクラスターに対応してサイズ変更します。

バックアップUUIDがE3DB9E85-D352-47E2-BCED-5BAD68DA853Bであると、ソースボリュームUUIDは075C06ED-37E2-407D-B998-E270C4EDC53Cであり、ターゲットボリュームはF65CF53426-5252626-5252626262626262626262626262626262624262626262626262 、次に、対応するRBDコマンドは次のとおりです。

  1. backup_id = e3db9e85-d352-47e2-bced-5bad68da853b
  2. source_volume_id = 075c06ed-37e2-407d-b998-e270c4edc53c
  3. DEST_VOLUME_ID = F65CF534-5266-44BB-AD57-DDBA21D9E5F9
  4. rbd export-diff - プールcinder_backup \  
  5. cinder_backup/volume-$(source_volume_id} .backup.base@backup.$ {backup_id} .snap.1511348180.27- \
  6. | RBD Import-Diff -Pool OpenStack-OpenStack/Volume-$ {dest_volume_id}  
  7. rbd -p openstack sezize - -size $ {new_size} volume -$ {dest_volume_id}  

上記の5つの条件のいずれかが満たされていない場合、シンダーは完全なバックアップを実行し、完全なバックアップはデータを断片的に書くことです。

  1. def _transfer_data(self、src、src_name、dest、dest_name、length):
  2. chunks = int (length / self.chunk_size)
  3. 範囲チャンク(0、チャンク):
  4. 前=時間時間()
  5. data = src.read (self.chunk_size)
  6. Dest.Write(データ)
  7. dest.flush()
  8. delta =( time。time () -
  9. rate =(self.chunk_size / delta) / 1024
  10. 降伏 その他の保留中のバックアップ
  11. eventlet.sleep(0)
  12. rem = int (length%self.chunk_size)
  13. remの場合:
  14. Dest.Write(データ)
  15. dest.flush()
  16. 降伏 その他の保留中のバックアップ
  17. eventlet.sleep(0)

この場合、それは非常に非効率的で時間がかかるため、使用することはお勧めしません。

5 結論

5.1 概要

1. 画像をアップロードする

  1. rbd -p ${GLANCE_POOL}を作成  --size $ {size} $ {image_id}  
  2. rbd -p $ {glance_pool} snap create $ {image_id} @snap
  3. rbd -p ${GLANCE_POOL} スナップ保護 ${IMAGE_ID}@snap

2. 画像を削除する

  1. rbd -p ${GLANCE_POOL} スナップ ${IMAGE_ID}@snap の保護を解除
  2. rbd -p ${GLANCE_POOL} スナップ rm ${IMAGE_ID}@snap
  3. rbd -p ${GLANCE_POOL} rm ${IMAGE_ID}

5.2 ノヴァ

1 仮想マシンを作成する

  1. rbd clone $ {glance_pool}/$ {image_id} @snap $ {nova_pool}/$ {server_id} _disk

2 仮想マシンのスナップショットを作成する

  1. #ディスクをスナップショットして視線のストレージプールクローンします
  2. rbd -p $ {nova_pool} snap create $ {server_id} _disk@$ {random_uuid}
  3. rbd -p $ {nova_pool} snap protect $ {server_id} _disk@$ {random_uuid}
  4. rbd clone $ {nova_pool}/$ {server_id} _disk@$ {random_uuid} $ {glance_pool}/$ {image_id}
  5. ソーススナップショットからそれを分離する画像を平らにします
  6. rbd -p $ {glance_pool} flatten $ {image_id}
  7. ソーススナップショットすべて完了し、それをきれいにします
  8. rbd -p $ {nova_pool} snap unprotect $ {server_id} _disk@$ {random_uuid}
  9. rbd -p $ {nova_pool} snap rm $ {server_id} _disk@$ {random_uuid}
  10. 「スナップ」と呼ばれる保護されたスナップショットを作成します アップロードされた画像渡さます 
  11. rbd -p $ {glance_pool} snap create $ {image_id} @snap
  12. rbd -p ${GLANCE_POOL} スナップ保護 ${IMAGE_ID}@snap

3 仮想マシンの削除

  1. $ rbd -p $ {nova_pool} ls | grep "^$ {server_id} " ;
  2. rbd -p $ {nova_pool} rm "$ image" ;
  3. 終わり

5.3 シンダー

1 ボリュームを作成する

(1)空のボリュームを作成する

  1. rbd -p $ {cinder_pool} create   -new-format - size $ {size} volume-$ {volume_id}  

(2)スナップショットから作成

  1. RBDクローン\
  2. $ {cinder_pool}/volume-$ {source_volume_id} @snapshot- $ {snapshot_id} \
  3. ${CINDER_POOL}/ボリューム-${VOLUME_ID}
  4. rbd resize - サイズ$ {size} openstack/volume-$ {volume_id}  

(3)ボリュームから作成

  1. #rbd_max_clone_depth <= 0の場合は完全なコピーを行います
  2. if [ "$ rbd_max_clone_depth" -le 0]];その後、 rbdコピー\
  3. $ {cinder_pool}/volume-$ {source_volume_id} $ {cinder_pool}/volume-$ {volume_id}
  4. 終了 0
  5. フィ
  6. #それ以外の場合は、牛のクローンを行います。
  7. ソースボリューム新しいスナップショットを作成します
  8. RBDスナップcreate \
  9. ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap
  10. RBDスナップ保護\
  11. ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap
  12. #ここで、ソースボリュームスナップショットをクローンします
  13. RBDクローン\
  14. $ {cinder_pool }/volume-$ {source_volume_id }@volume-$ {volume_id} .clone_snap \
  15. ${CINDER_POOL}/ボリューム-${VOLUME_ID}
  16. #DESTボリュームクローン RBD_MAX_CLONE_DEPTHが到達した場合、
  17. #クローニング後に運命を平らにします
  18. 深さ= $(get_clone_depth $ {cinder_pool}/volume-$ {volume_id})
  19. if [ "$ dept" -ge "$ rbd_max_clone_depth" ]];それから 
  20. # 宛先ボリュームをフラット化する
  21. rbd フラット化 ${CINDER_POOL}/ボリューム-${VOLUME_ID}
  22. #一時スナップを削除
  23. rbd スナップ保護解除 \
  24. ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap
  25. rbd スナップ rm \
  26. ${CINDER_POOL}/volume-${SOURCE_VOLUME_ID}@volume-${VOLUME_ID}.clone_snap
  27. フィ

(4)イメージから作成する

  1. rbd clone $ {glance_pool}/$ {image_id} @snap $ {cinder_pool}/volume-$ {volume_id}
  2. if [[-n "$ {size}" ]];その後、 rbd resize --size $ {size} $ {cinder_pool}/volume-$ {volume_id}  
  3. フィ

2 スナップショットを作成する

  1. rbd -p $ {cinder_pool} snap create volume-$ {volume_id} @snapshot- $ {snapshot_id}
  2. rbd -p $ {cinder_pool} snap protect volume-$ {volume_id} @snapshot- $ {snapshot_id}

3 バックアップを作成する

(1)最初のバックアップ

  1. rbd -p $ {backup_pool} create   - サイズ \  
  2. $ {volume_size} volume-$ {volume_id} .backup.base
  3. NEW_SNAP=ボリューム-${VOLUME_ID}@backup.${BACKUP_ID}.snap.${タイムスタンプ}
  4. rbd -p ${CINDER_POOL} スナップ${NEW_SNAP}を作成します
  5. rbd エクスポート-diff ${CINDER_POOL}/volume-${VOLUME_ID}${NEW_SNAP} - \
  6. | rbd import-diff -pool $ {backup_pool} - volume-$ {volume_id} .backup.base  

(2)増分バックアップ

  1. rbd -p $ {cinder_pool} snap create \
  2. volume-$ {volume_id}@backup.$ {backup_id }.snap.$ { Timestamp}
  3. rbd export-diff -pool $ {cinder_pool} \  
  4. - from-snap backup。$ {parent_id} .snap。$ {last_timestamp} \  
  5. $ {cinder_pool }/volume-$ {volume_id}@backup.$ {backup_id }.snap.$ {Timestramp} - \
  6. | rbd import-diff --pool ${BACKUP_POOL} - \  
  7. $ {backup_pool}/volume-$ {volume_id} .backup.base
  8. rbd -p $ {cinder_pool} snap rm \
  9. volume-$ {volume_id} .backup.base@backup.$ {parent_id }.snap.$ {last_timestamp}

4 バックアップとリカバリ

  1. rbd export-diff -pool $ {backup_pool} \  
  2. volume -$ {source_volume_id} .backup.base@backup.$ {backup_id }.snap.$ {Timestramp} - \
  3. | rbd import-diff -pool $ {cinder_pool} - volume-$ {dest_volume_id}  
  4. rbd -p $ {cinder_pool} resize - size $ {new_size} volume -$ {dest_volume_id}  

【この記事は51CTOコラムニスト「Fu Guangping」によるオリジナル記事です。転載が必要な場合は51CTOまでご連絡ください]

この著者の他の記事を読むにはここをクリックしてください

<<:  エッジコンピューティングは AI の開発を促進します。将来、クラウドコンピューティングを排除できるでしょうか?

>>:  ブロックチェーンとは何ですか? また、データセンターやクラウド コンピューティングにどのような影響を与えますか?

推薦する

SEOを他人の視点から見ることは実は不思議ではない

ご存知のとおり、検索エンジン最適化とは、実際には高品質のウェブサイト情報を構築し、外部の高権威プラッ...

地域コミュニティのネットワークと地域の都市開発の現実をどのように組み合わせてホットスポットを創出できるでしょうか?

少し前に、「今後、二級都市、三級都市(県城)が地域コミュニティの爆発点になる」という記事を見ました。...

6つの主要な情報フロー広告プラットフォーム、どのチャネルが最も強力か

iResearchは、今後2年間、情報フロー広告は50%以上の成長を続け、全体の市場規模は2017年...

SEO サービスの倫理規定

今日、インターネットマーケティングは商品を販売するための重要な手段となっています。インターネットを通...

詳細が成功か失敗かを決める:ウェブサイトのディレクトリパスの最適化設計方法

ウェブサイトの最適化において、多くのウェブマスターは全体的な最適化戦略を追求していますが、いくつかの...

SEO の観点からプレーンテキスト リンクの効果を探る

伝統的なSEOの概念から見ると、ハイパーテキストリンク、アンカーテキストリンク、プレーンテキストリン...

動画サイトは自作コンテンツの収益化能力が弱く、主なトラフィック源となることは難しい。

国内の主流動画サイトはこぞって自主制作ドラマの分野に参入している(写真提供:テンセントテクノロジー)...

2014年に個人ウェブマスターにとって最も価値のあるビジネスモデル:セルフメディアスター

お金も技術もリソースもないのに、私たち草の根ウェブマスターに他に何ができるでしょうか? 急速に発展し...

ドメイン9ヶ月2.99ドル登録com割引コード

domain.comがEIGに買収された後、初めて新しいプラットフォーム管理システムに変更されました...

調査によると、マルチアクセスエッジクラウド市場は2025年までに着実に成長する見込み

[[430000]]調査会社IDCは、2021年はマルチアクセスエッジコンピューティング(MEC)市...

さまざまな電子商取引プラットフォームでのダブルイレブン活動

ダブル11がどんどん早く来るようになってきているようです。 19日と20日、JD.comとTmall...

中国におけるPinterestのような製品の現状:ソーシャルeコマースがトレンド

(写真提供:Webmaster Network) ))))))つい最近、アメリカの有名な金融ウェブサ...

第9回CNNIC年次総会:CNドメイン名が個人登録に開放される可能性

2012年1月12日、ドメイン名の個人登録の許可は、ドメイン名業界で常に最も議論されているトピックで...

weloveservers 春の特別企画 - 512/768/2000M メモリ VPS プロモーション

サーバーは Intel Xeon Quad-Core E3-1240 u、ハードディスク RAID1...