Red Hat - Open Innovation Labsのブログ - TECH PLAY

TECH PLAY

Red Hat  - Open Innovation Labs

Red Hat - Open Innovation Labs の技術ブログ

全900件

クラウドインフラ全般を担当しているソリューションアーキテクトの伊藤です。この記事は OpenShift Advent Calendar 2019 - Qiita 11日目のエントリになります。 アドベントカレンダーを埋めるのが最後になってしまったようで空いている枠を取ったら2日連続になりました。しくしく(;O;)。 ■はじめに オンプレミス環境においてOpenShift4をデプロイしようとしたときに、どんなインフラにデプロイをしますか? 2019年12月初旬現在リリースされている最新のOpenShift4.2では、 ・OpenStack ・Baremetal ・vSphere この3つのインフラ環境を選択することができます。   本日はOpenStackをインフラとして選択して、IPIでOpenShiftを構築してみたいと思います。 既に多くの記事でOpenShift4のインストールについて触れられているので、この記事ではインフラ環境としてOpenStackを選択した際の違いや注意点について記載することとします。     ■OpenShift4が必要なインフラリソース OpenShift4をOpenStack上稼働させるためには、OpenStackの以下のリソースが必要です。 Ref:   https://docs.openshift.com/container-platform/4.2/installing/installing_openstack/installing-openstack-installer-custom.html#installation-osp-default-deployment_installing-openstack-installer-custom   上記表で必要になるリソースは以下のように利用されます。 Floating IPが必要数2で、実際の利用が3になっているのは、bootstrapノードが診断用に1つのFloating IPを要求するためです。 Floating IPが同時に必要となる最大数は2つです。   ■OpenStackインフラの注意点 ・オブジェクトストレージ(Swift) インストールに使うユーザアカウントはswiftoperatorロールと、temp-urlをサポートさせる必要があります。 ・ネットワーク(Neutron) クラスタ内部の冗長化のためにVRRPとマルチキャストDNSが用いられていますので、インスタンス間でマルチキャストが許可されている必要があります。   ■事前に準備する2つのファイル ・clouds.yaml (stein) [stack@director15sos ~]$ cat clouds.yaml clouds: shiftonosp15: auth: auth_url: http://10.208.80.189:5000/v3 project_name: shiftstack4 username: shiftstack_user password: **** user_domain_name: Default project_domain_name: Default project_id: 826baaabe6fe4be6bdaefb71c2053a67   ・install-config.yaml いつものinstall-config.yamlファイルです。 (stein) [stack@director15sos ~]$ cat install-config.yaml.bk apiVersion: v1 baseDomain: openshift.run compute: - hyperthreading: Enabled name: worker platform: {} replicas: 3 controlPlane: hyperthreading: Enabled name: master platform: {} replicas: 3 metadata: creationTimestamp: null name: espresso (14文字以内) networking: clusterNetwork: - cidr: 10.128.0.0/14 hostPrefix: 23 machineCIDR: 172.31.0.0/16 networkType: OpenShiftSDN serviceNetwork: - 172.30.0.0/16 platform: openstack: cloud: shiftonosp15 computeFlavor: 4v-16G externalNetwork: public (openstack neutron networkで重複するとランダムで選択される) lbFloatingIP: 10.208.80.163 octaviaSupport: "0" region: "RegionOne" trunkSupport: "0" externalDNS: 10.208.81.1 pullSecret: '{"auths":{"cloud.openshift.com":{"auth":"=========","email":"takito@redhat.com"}}}' sshKey: 'ssh-rsa =======' ■コマンドの投入 この後に投入するコマンドは既にIPIについての記事に記載されている通りと同様です。 ./openshift-install create install-config ./openshift-install create cluster   上記の構築者が行うべき事前準備を行ったら、問題が発生しなければあとはこの2つのコマンドで、クラスタコンソールへアクセスし利用を開始することが可能になります。 ■OpenShift4 on OpenStackに関するドキュメンテーション、URL   ・Shift on Stackのオフィシャルドキュメント https://docs.openshift.com/container-platform/4.2/installing/installing_openstack/installing-openstack-installer-custom.html   ・OpenStackのためのRHCOS QCOW2イメージ置き場 https://mirror.openshift.com/pub/openshift-v4/dependencies/rhcos/4.2/latest/   ・OpenShiftのインストーラとクライアントソフトウェア ocコマンド、openshift-installコマンド https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/   ・Shift on Stack UPIの説明 Github上にはExperimentalな状態ですが記載があります。 https://github.com/openshift/installer/blob/master/docs/user/openstack/install_upi.md   まとめ OpenShift4をインストールするにあたって、IPIを使ったインストールであれば若干のインフラの違いのみを意識すれば後のやり方は変わりません!  
Red Hatの須江です。 本記事は 赤帽エンジニア Advent Calendar 2019 の10日目です。 子供を皮膚科に連れて行ったりなんだりで、気づいたら12/11になってますが、細かいことは気にせず進めます。 セッション資料と動画 redhat.lookbookhq.com redhat.lookbookhq.com 「データ指向アプリケーションデザイン」をメインテーマに選んだわけ デジタルトランスフォーメーション(DX)がバズワード化して久しいですが、自分は常に「DXは目的ではなく手段なので、DXしたあとにどうありたいかのビジョンを持ち、そこから逆算していまやることを考える」ことが重要だと考えています。 ビジョンを持つためには、まずDX後の世界がどうなっているのかをイメージできるようになる必要があります。 そこで、2019/6/20に開催された「DX&Open Hybrid Cloud Day」では『DX後の世界はどうなるのか』というイメージを共有するために、書籍「アフターデジタル」で取り上げられている中国のデジタル化(デジタライゼーション)の実態を紹介しました。 要約すると、DX後の世界はだいたいこんな感じです。 デジタルがリアルを飲み込む(OMO)ので、デジタルがビジネスの生命線になる あらゆる存在(人/モノ)から生成される大量のイベントデータをリアルタイム処理してアクションを起こすことが競合優位性構築に必須となる 様々なステークホルダーとダイナミックに連携して優れたユーザーエクスペリエンスを提供することが求められる 大雑把に言うと、現在GAFAクラスの企業がやっていることに近いことを、ふつーのエンタープライズ企業でもやるようになる、ということです。 DX後の世界では、現在とは桁違いの量のデータを扱いつつ、ビジネスの規模の変動(拡大/縮小)に柔軟に追随でき、システムアップデートやシステム障害によるサービス停止を極力回避するようなアーキテクチャが求められます。 それを経済的に合理的で、かつフレキシブルな形で実現するための戦略としては、いまのところCloud Nativeという考え方が妥当であると考えています。 Cloud Nativeを阻むものはなにか? これまでOpenShift担当として、いろいろな方とコンテナ導入やCloud Nativeへの移行についてお話してましたが、モノリシックで巨大なRDBMSが主要な課題の一つといってよいかと思います。 アプリケーションをモダナイズしてマイクロサービスやサーバーレスに分解していくことは、簡単ではありませんが、そんなに難しいわけでもありません。 アプリケーションを分解するよりも、モノリシックで巨大なRDBMSを分解することの方がはるかに難易度が高いというのが、現時点での自分の見解です。 ありがちなRDBMS中心アーキテクチャ エンタープライズシステムでよくありがちな、RDBMSを中心として複数のシステムが連携するアーキテクチャを誇張して表現したのがこちらです。(これは日本に限らずグローバルでもみられる傾向のようです。) このアーキテクチャには以下のような課題があります: 巨大なRDBMSを「Data Hub」として利用 RDBMSの能力増強は原則スケールアップ 能力増強に伴ってコストパフォーマンスが悪化 スケールアップに実用上の上限がある RDBMSが苦手なワークロードの増加(特にSoE系) 追記(INSERT)をメインとするもの (例: IoT機器の計測データ) リレーショナルモデルでは非効率なもの (例: 時系列データ、グラフデータ) スキーマ(データ構造)を進化させることが難しい Schema on Write: 書き込み時にデータ構造を確定しなければならない 別のアプローチ) Schema on Read: スキーマの進化に適した方法、DataLakeでよく使われる モノリシックなデータストアにすべてのデータを格納 Write中心か、Read中心かによって、効率的な格納方式が異なる セカンダリインデックスを増やすと、Writeのパフォーマンスが落ちる トレードオフの調整が難しく、Try&Errorもやりにくい(DB全体に影響) 特にDXを進めるにあたって解決が必須となることが多く、近年になって本腰を入れて解決に取り組む動きが出てきたなという感触です。 去年くらいから折に触れてこういう話をしているのですが、最近になって「詳しく聞かせてください」と食いつかれることが明らかに増えてきました。(というか日本企業ほぼすべてこの問題で悩んでいるんじゃないかというくらいの食いつき。) どうして巨大なRDBMSに依存するアーキテクチャになってしまったのか? こうなった原因はいくつかあるとは思いますが、自分が考えるに「データの一貫性の担保が簡単かつ確実だから」、これに尽きるんじゃないかという結論に至りました。 で、セッションでは「データのTimeliness(即時性)」に関する制約を少し緩和してあげれば、RDBMSのトランザクション管理機構に頼らなくても「データのIntegrity(整合性)」を担保することは可能です、ということをお話しました。 実際、NoSQL系のDBなどではトランザクション管理機構を備えていないものもありますし、RDBMSであってもDBが分散しているとトランザクションは限定的にしか使えませんので、一般的に受け入れがたい条件ではないと思っています。 データ中心からイベント中心へ Timelinessに関する制約を緩和できるならば、Integrityを担保しつつ、複数のシステムにデータを分散することが可能になります。 ということは、Cloud Nativeな分散システムにデータストアやデータパイプラインをのせることが可能になるわけです。 Integrityと分散合意 Cloud Nativeな分散システムにおいてデータのIntegrityを担保するという問題は、信頼性の低いインフラストラクチャの上でシステム全体として矛盾が発生しないようにするにはどうすればよいか?という問題に読み替えることができます。 この分野は既に研究が進んでおり、「分散合意」というアルゴリズムを利用することで解決できることがわかっています。 Kubernetesがリソースの管理に利用しているetcdも、内部ではRaftという分散合意アルゴリズムを利用してIntegrityを担保しています。 自分はコンピュータサイエンスの出身ではない(大学は物性理論物理学専攻です)ので、このへんはまだまだ勉強中というのが正直なところです。 詳しい解説はこちらの書籍をご参照くださいー。 分散処理の対象としてイベントを利用することの利点 分散システムにおいてデータを扱う場合、データをロストしないためにレプリケーション(複製)しておくのが一般的な対策です。 しかし、データが可変(mutable)だと、レプリケーションを含めた整合性の保証が非常に難しくなります。 そこで、分散システムにおいては不変(immutable)なデータを扱うようにすると、設計がシンプルになることが多いです。 具体的には、状態(=データベースのレコードに相当)をfactとして記録するのではなく、状態の変化を引き起こすイベントをfactとして記録するようにします。 このあたりは、以下のブログエントリに詳しく解説されています。ぜひご一読を。 rheb.hatenablog.com ログベースメッセージブローカー(LBMB)を中心としたアーキテクチャ イベント中心なアーキテクチャを採用する場合、重要なのは (a) イベントが確実に伝達できること (b) イベントの伝達経路がボトルネックにならないこと です。 エンタープライズシステムでよく使われているメッセージブローカー(例えばIBM MQなど)は、(a)は満たしていますが、(b)が課題になることが多いです。 そこで、(a)(b)両方を満たすものとして登場したのがApache Kafkaに代表される「ログベースメッセージブローカー(LBMB)」です。 従来型メッセージブローカーとLBMBは、メッセージ(イベント)の取り扱いのセマンティクスが異なります。 これらは排他的なものではなく、ケースバイケースで使い分けるべきものです。(機能や適用シーンが異なるので、KafkaがあるからMQいらないよねとはならないです。) LBMBを中心に据えたアーキテクチャは次のようになります。 さらにこれを推し進めて、必要がなければRDBMSを使わずに直接LBMBにイベントを入れてもいいわけです。 こうすることで、Cloud Nativeスタイルに移行しつつ、全体を最適化することがやりやすくなると考えています。 「ゼロトラストネットワーク」について セッションでは時間がなく、あまり触れることができませんでしたが、Cloud Native時代のセキュリティは根本的に考え直す必要があると思ってます。 具体的には、伝統的なネットワークのゾーニングによるアクセス制御から、次の世代のアーキテクチャにステップアップしていくべきではないでしょうか? マイクロサービスやサーバーレスを駆使して、データも分散処理できるような仕組みを作って、Cloud Nativeなアーキテクチャに移行した場合、「ネットワークのセグメント」という粗い粒度でのアクセスコントロールではもはや不十分です。 ではどうするのかというと、「ゼロトラストネットワーク」という考え方がヒントになると思います。 具体的な事例としては、書籍中でも紹介されているGoogleのBeyondCorpが参考になるでしょう。 ゼロトラストネットワークは現在発展中の分野で、具体的な製品とかソリューションが揃っているわけではありません。 いろいろと試行錯誤してソリューションを作っていくフェーズです。 しかしながら、この考え方を取り入れたプロダクトは今後増えていくでしょう。 例えばIstionの内部コンポーネントであるCitadelが参考になると思います。 https://istio.io/docs/ops/deployment/architecture/#citadel OpenShift4の話が少なかった・・・ 取り上げるべきトピックの優先度を勘案した結果、今回はOpenShift自体の話はあまり入れられませんでした。ゴメンナサイ。 他のセッションではOpenShiftの話とかOperatorの話とかたくさんありますので、補完いただければと。。 redhat-forum.jp 真面目な話ですが、今後はシステムのモダナイゼーションやCloud Native化は「アプリケーション」と「データ」の2軸で考えていくべき、と考えておりまして、今回は「分散システムにおけるデータの扱い」が目下最重要でお伝えすべきことを判断しました。 そこで、OpenShift自体の話や、Service Mesh/Serverlessの話は必要最小限にさせていただきました。 アプリケーションアーキテクチャの話は、DBの解体と、データパイプラインのCloud Native化が進展すれば、自然とCloud Nativeに向かっていくものと考えています。 逆説的ですが、そうすればマイクロサービスやサーバーレス(Function)を使うことが当たり前になっていくと思います。 まとめ マイクロサービスとかサーバーレスを活用したモダンでCloud Nativeなアーキテクチャにステップアップするには、データをどうするかも含めて考えなきゃね、という至極当然の話でした。 Kubernetes Operatorが登場したことで、データベースやメッセージブローカーなどのステートフルワークロードをk8sに乗せる流れができつつあります。 そのへんの話を詳しく聞きたい方は、ぜひOpenShiftコミュニティの大忘年会にお越しください!!! www.openshift.run
クラウドインフラ全般を担当しているソリューションアーキテクトの伊藤です。この記事は OpenShift Advent Calendar 2019 - Qiita 10日目 のエントリになります。 アドベントカレンダーと言えば、毎年息子にレゴのアドベントカレンダーを買ってあげており、息子が「クリスマスまであと何日!?」と毎朝楽しみに開いていたのですが、 今年は遂に要らないと言われてしまいました。10歳を過ぎサンタクロースの正体に気が付いてしまったのかもしれません。しくしく(;O;)。 ■はじめに 通常OpenShiftをデプロイする際、「マスターノードを3ノードで構成してください。」とお願いをしています。 「高可用性のためです」 クラウドにはAZ(アベイラビリティゾーン)と呼ばれる障害範囲を局所化するための概念が存在します。 ネットワークスイッチやストレージ、電源など1ノードより大きく集約されているコンポーネントで、インフラ障害が発生してしまった際に同時に影響してしまう範囲を示しています。 クラウドでは利用者がどのノードがどこに収容されているか、ある1つのコンポーネントが障害を起こしてしまった時に影響を受けるノードは誰なのか、ということを意図的に分からないようにしているので、ある程度の大きな障害を見越してAZという形で障害範囲を示しています。 つまり3つのノードを1つのAZに集約してしまうと、障害が発生した際には同時に3つのノードが失われてしまう可能性があります。 3つのノードが独立した可用性を獲得するには、それぞれ別のAZを割り当てる必要があります。 ■クラウドは3ノード、3AZで高可用性を担保させるべし OpenShiftのマスターノードはクラウド上にデプロイされるクラスタシステムになります。 クラウド上にデプロイされるクラスタシステムにとって、理想的な高可用性を実現させるには3つのノードと3つのAZが必要です。 「高可用性のためであればネットワーク機器とか2ノードでHA構成をしているし、Active/Backupということで2ノードではダメなんでしょうか?」 「Active用のAZ、Backup用のAZという役割なので、2つのAZを使って構成してはダメなんですか?」 では、以下の観点で考えてみましょう。 ■観点と前提 ・ある機能(今回はOpenShift マスターノード)において高可用性(HA)を実現したい ・2ノードでHA もしくは 3台以上でHAを実現するという2つのプランの中で検討する ・HAを実現した上でやり取りされる実際のデータについては今回の観点から外す、あくまでもHAを構成、維持する仕組みという観点に限定する ■2台でHA 2台のノードがActive-Standby(Active-Backup/Master-Slave)環境を実現してHAを実現します。 時代遅れということではなく、現在でも環境次第では多く利用されています。 環境次第とは? HAを実現するにあたっては、必ずハートビートと言われるHA内の別のノードにステータスを送る通信を行っています この通信はActiveノード、Standbyノードぞれぞれの役割を選出する時にあたっては、 ある1つのノードは「私は内藤(というノード名と仮定)である、優先順位は100である」、また別のノードは「私は伊藤(というノード名と仮定)である、優先順位は90である」 →優先順位の高い内藤がActiveに選定される、といった形で通常は優先順位などで主従関係を決めます。 また、HAが構成された後はActive側が「内藤は元気だけど、伊藤は大丈夫?」→Standby側が「伊藤は元気です!」などと言う非常に簡単な挨拶(ハートビート)を行い正常性を確認します。 ★この通信に使うネットワークはそれぞれのノードが正常に動作している時に、通信が途絶えるような状況は起きてはいけません!!★ ハートビート用のネットワークの通信が途絶えてしまうと、スプリットブレインが発生します。 スプリットブレインとは、それぞれのノードが対向ノードと通信できず対向ノードが正常に動作できていないと判断し、両方がActiveになることを指します。 ・内藤「おれがデータを処理するんだ」、伊藤「おれがデータを処理するんだ」 このような状況が発生してしまうと、そのHAシステムは正常に動作しているとは言えず、データ整合性欠如、データ破壊、通信断といった障害が発生します。 2ノード構成では、1ノードで異常が発生すると、残りは1ノードになるので1ノードで処理を継続することが普通に発生しうるということです。 #マルチアクティブ(Active-Active)構成によるHAであっても、データの処理の仕方が違うため上記のような不正の状態が起きないだけで、HAを構成するという観点では同様の考え方になります。 この ★スプリットブレインという状況は絶対に防がないといけません★ ハートビート用のネットワーク通信が途絶えないように、通常は ・中間経路から壊れるものを排除するため、ノード間は直接通信ケーブルで接続する ・中間経路にスイッチ等壊れるものが混入する場合は、ハートビート用のネットワークを2つ以上用意する という堅牢な構成を実現します。 ★「通信が途絶えるときは対向がダウンもしくは異常動作した時だ!!」 ★ というわけで、環境次第の環境とは、「絶対に通信が途絶えないネットワークが提供されている」ということを指すということがお分かりいただけたかと思います。 さて、絶対通信が途絶えてはならないハートビート用のネットワークですが、 クラウドの利用者側はこの「途絶えてはいけない!!」ということを獲得できるのでしょうか? ・・・できませんね?、実際にプライベートクラウドの設計構築を行うと分かりますが実現できません。利用者に通信が途絶えないネットワークの提供を保証することはできません。 なぜならクラウドにおいて利用者視点では、中間経路がどのようになっているか関知するすべがないからです。 ここで3ノード以上でHAを実現するということに光が当たります。 ■3ノード以上でHA では、どこかで通信が途絶えるようなことが発生しうる環境において、HAを実現するにはどのようにしたらよいでしょうか? それは分散合意という仕組みです。 OpenShiftのマスターノードでは、etcdという分散KVSでRaftと呼ばれる分散合意のアルゴリズムが使われています。 マスターノードが使うデータをetcdで扱う事で、ノードがダウンしてしまってもクラスタ自体は動作を続けることができます。 それぞれのノードが通信を行い、通信できているノード数をそれぞれのノードが管理しています。普段は3ノード以上で集合体を構成しており、誰か1ノードがリーダーとして高い権限を保有しています。 3ノード以上で構成された集合体でノードダウン/ネットワーク通信障害などが発生した場合、依然通信できているノード数が過半数を上回って(マジョリティ)いれば処理を継続することができます。集合体のリーダーがマジョリティに残っていれば、リーダーは変わらず処理は継続されます。 マジョリティ側にリーダーが存在しない場合、マジョリティ側でリーダーの再選出が行われます。 そして少数側(マイノリティ)は動作を停止します。(2ノードHA時のようにスプリットブレインからのデータ破壊、不整合が起きないようにするため保全モードに入る) これにより、通信が途絶える可能性のあるネットワーク環境でHAを実現します。 では、3ノードでHAを構成した環境で、1対1対1と全台のネットワークがそれぞれスプリットしてしまったケースですが、 これは二重障害と呼びます。通常一般的なシステム設計において二重障害は起きてしまったらしょうがない、コストバランスが取れなくなるので考慮しないことが多いです。 2ノードHAにおいても、HAのハートビート用の通信は通常2つ用意されるので、これもまた二重障害は考慮しないということになるので同じ条件です。 HAを構成するにあたってはノードがダウンするか否かということより、ノード間の(原因は問わず)通信が途絶えるか否かというのが大事です。 HAの心髄はネットワークにあります。 そしてあるひとつのAZがダウンしてしまう事は十分考えられるので、1つのAZに1つのノードを配置するという考えに至ります。 以上が、3ノード、3AZが必要な理由になります。 3ノードを2AZに収容する設計では、2ノード収容されている側のAZで障害が発生した場合に、安全のためクラスタは動作を停止してしまうということもご理解頂けます。 ■クラウド時代のインフラ設計は障害の局所化がポイント ここからは余談になりますですが、オンプレのデータセンタで3つAZを作るにはどうしたらよいでしょうか? 計算するためのノードを生かすには、ネットワーク、ストレージとサーバへの電力の3つが必要です。 ネットワークとストレージはAZの数だけ別に準備をします。ネットワークは自律的に経路交換し多重障害に耐えられる層のネットワークコンポーネントまではAZの数だけ準備する必要があります。 一方データセンタに供給される電力は一般的なデータセンタでは変電所から2つの経路で引き込みが行われますので、そこで分けると2つのAZしか作れません(;O;)しくしく。。。 サーバに供給されるまでの電力経路は以下になります。 発電所 => 変電所 = (責任分界点) > 受電設備 => 非常用発電設備 => UPS => ラック内電源バー この順番で電気が巡ってきますので、以下のような構成にしてAZを4つ作ります。 ここで作られた4つのAZのうち普段は3つのAZを使ってクラスタを構築します。 そして、もしこの構成において一番重要な変電所の1つで障害が発生し停電してしまった場合はどのようにクラスタを維持しましょうか? しばらくは非常用発電機(72時間)やUPS(5分)が、それぞれのAZに電力を供給し続けますが、いずれはブラックアウトが発生します。 もし変電所Bに障害が発生したとき、AZ1,AZ2,AZ3のAZを用いてクラスタを構築していたら、ブラックアウトしてしまってもマジョリティが生存することになりますので無視して良いです。 しかし変電所Aに障害が発生したら、このクラスタはマイノリティしか生存しないことになり、クラスタを維持することができません。 この場合、ここで使っていないAZ4のAZを用いてクラスタを補強するとともに、AZ1かAZ2のノードをクラスタから待避させて、ブラックアウトしたときにマジョリティが生存する構成にします。これで重大障害が起きてもシステムを維持させることができます。 このように何かが起きたとき、その何かによって影響する範囲はどこなのか?何は助かるのか?ということが瞬時にわかるシンプルなインフラを構築することで、大規模なリソースを早く安価に簡単に供給することができるようになります。 そしてこのようなインフラのSLAを考慮した上で、アプリケーション側(元々の話だとetcd)で高い可用性を担保するというクラウドの正しい使い方ができるようになります。 #極力アルゴリズムや技術、ソフトウェア固有の情報を取り除いて、専門的な用語を最小限に抑えた情報を目指しました。 #2ノードHAにおいてQuorumなどの仕組みでマジョリティを確保するやり方もありますが、これはLinux-HA以外のファイヤウォールやロードバランサなどにおいては 一般的ではない概念なので2ノードHAは純粋に2ノードでHAを目指している構成で説明しました。
Red Hatの福岡オフィスでソリューションアーキテクトをしている田中司恩です。 この記事は OpenShift Advent Calendar 2019 - Qiita 9日目のエントリになります。 *1 今回と次回、2回に分けてOCP4/UPIインストールにおけるWorkerノードの追加について説明します。 OCP4/UPIインストールについては下記記事を参照ください。 rheb.hatenablog.com 本記事の章立てはこのようになります。 UPIにおけるWorkerノード追加の概要 RHCOSノードの追加 Workerノードの手動削除 UPIにおけるWorkerノード追加の概要 UPIインストールではインストールコンフィグ(install-config.yaml)にWorkerノードの台数を 0 と記載してインストールを行います。 インストール時に起動したWorkerノードの台数が、初期クラスター構築時のWorkerノードの台数となります。 また、クラスター構築時にWorkerノードに使用できるOSはRHCOSのみです。 それに対し、IPIで構築したクラスターにおいてノードは、Machineというリソースとしてノードが管理されます。 また、0個以上のMachineをグループ化したMachineSetにより、管理者はレプリカ数の増減によって容易にノードの追加/削除が行えます。 しかしながら、UPIで作成したクラスターにはMachine/MachineSetはありませんので同じ方法は使えません *2 。 ですので、UPIクラスターにおいては手動作業でのWorkerノードの追加作業が必要となります。 OCP4では追加のWorkerノードとして、RHCOSとRHELノードが選択できます。 下記はRHCOSとRHELノードを使った場合の簡単な比較表です。 RHCOSノード RHELノード OSのインストール作業 不要 必要 OSの管理 不要。OTAでアップデート。 必要。個別にパッチ適用必要。 Masterノードでの利用 可能 不可 OS用のサブスクリプション 不要 不要 続いてRHCOSノードの追加方法について説明します。 RHCOSノードの追加 [図1] RHCOSノードの追加 RHCOSのノードの追加方法について、実はドキュメントに該当する記載がありません *3 。その代わり、下記のナレッジに関連する記載があります。 How to add OpenShift 4 RHCOS Worker Nodes in UPI - Red Hat Customer Portal 要約すると下記の通りです。 Workerノードはマスターノードと同じ手順で追加できる(vSphere/ベアメタルのドキュメントに説明がある) 新規のインストールと同様に worker.ign を使用してインストールを行う。マシンの起動時に既存のOpenShiftクラスターのMachine Config Operator(MCO)と同期してクラスターに追加される (※注意※)vSphere/ベアメタルのインストールドキュメントに記載されているように、ノードのCSR(証明書署名要求)を手動で承認する必要がある(場合がある)。 ナレッジにも「ドキュメントに書いてる通り」という表現がありますが、探してみてもRHCOSノードの追加を指している箇所はありません。現時点ではいくつかの情報を組み合わせるとなんとか上記のことが理解できます。 しかしながらやってみると手順は非常に簡単で、まとめると下記のようになります。 新しいWorkerノードマシンを用意する 新規クラスタ構築時と同様に、RHCOSのインストールを行う(またはPXE boot)。 起動処理が終わりクラスターと同期するとCSRの承認待ちになる CLIでCSRの承認を行う クラスターにノードが追加される 実際にテストしてみます。構成はいつものUPI検証環境で、3ノードクラスターにRHCOSのWorkerノード: worker-2 を追加する、というシナリオで実行します。 *4 検証環境の構成図は下記の通りです。 [図2] 検証環境ネットワーク構成図 ここではWorkerノード: worker-2 の起動処理が終わった後からの手順を紹介します。 今回の手順そのものではないですが、CSRの承認に関連するドキュメントは下記にあります。 6.1.12. マシンの CSR の承認 追加するWorkerノードを起動して、しばらく待ってからCSRの確認を行います $ oc get csr NAME AGE REQUESTOR CONDITION csr-9ffrp 3m7s system:serviceaccount:openshift-machine-config-operator:node-bootstrapper Pending Pending のものが出てきました。 この時点ではクラスターからノードはまだ見えません $ oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 8d v1.14.6+31a56cf75 master-1 Ready master,worker 8d v1.14.6+31a56cf75 master-2 Ready master,worker 8d v1.14.6+31a56cf75 CSRの承認を行います $ oc get csr -o name | xargs oc adm certificate approve certificatesigningrequest.certificates.k8s.io/csr-9mfxc approved ノードの確認を行います $ oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 8d v1.14.6+31a56cf75 master-1 Ready master,worker 8d v1.14.6+31a56cf75 master-2 Ready master,worker 8d v1.14.6+31a56cf75 worker-2 NotReady worker 16s v1.14.6+31a56cf75 ノードは追加されているのですが、実は同時にCSRが追加されています *5 。 再度CSRの確認をします。 $ oc get csr NAME AGE REQUESTOR CONDITION csr-9mfxc 3m51s system:serviceaccount:openshift-machine-config-operator:node-bootstrapper Approved,Issued csr-pvfms 34s system:node:worker-2 Pending 追加で Pending のものが出てきましたので、もう一度CSRの承認をします $ oc get csr -o name | xargs oc adm certificate approve certificatesigningrequest.certificates.k8s.io/csr-9mfxc approved certificatesigningrequest.certificates.k8s.io/csr-pvfms approved $ oc get csr NAME AGE REQUESTOR CONDITION csr-9mfxc 7m45s system:serviceaccount:openshift-machine-config-operator:node-bootstrapper Approved,Issued csr-pvfms 4m28s system:node:worker-2 Approved,Issued 必要なCSRが承認され、無事にRHCOSのWorkerノード: worker-2 がクラスターに追加されました。 RHCOSノードの追加については以上となります。 Workerノードの手動削除 Workerノードの追加ができたので、今度は手動での削除を行います。 手動削除に関するドキュメントは下記です。 6.7.1. RHCOS コンピュートマシンのクラスターからの削除 (参考)ドキュメント上はRHCOSを対象としていますが、RHLEノードも同様の手順で削除が可能です 削除手順 oc adm cordon コマンドを実行して、ノードにスケジュール対象外 (unschedulable) のマークを付けます。 <node_name> にはRHCOS コンピュートマシンのノード名を指定します。 $ oc adm cordon <node_name> 次に、ノードからすべての Pod をドレイン (解放) します。 <node_name> には分離した RHCOS コンピュートマシンのノード名を指定します。 $ oc adm drain <node_name> --force --delete-local-data --ignore-daemonsets 最後にノードを削除します。 <node_name> にはドレイン (解放) した RHCOS コンピュートマシンのノード名を指定します。 $ oc delete nodes <node_name> 以上の手順でWorkerノードの手動削除が行えます。 実行例 Workerノード: worker-2 を削除する例です $ oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 8d v1.14.6+31a56cf75 master-1 Ready master,worker 8d v1.14.6+31a56cf75 master-2 Ready master,worker 8d v1.14.6+31a56cf75 worker-2 Ready worker 21m v1.14.6+31a56cf75 $ oc adm cordon worker-2 node/worker-2 cordoned $ oc adm drain worker-2 --force --delete-local-data --ignore-daemonsets node/worker-2 already cordoned WARNING: ignoring DaemonSet-managed Pods: openshift-cluster-node-tuning-operator/tuned-vtn7n, openshift-dns/dns-default-thxhj, openshift-image-registry/node-ca-v6qzq, openshift-machine-config-operator/machine-config-daemon-7m2k2, openshift-monitoring/node-exporter-wqqp9, openshift-multus/multus-wgwvx, openshift-sdn/ovs-c58jd, openshift-sdn/sdn-wcv8s node/worker-2 drained $ oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 8d v1.14.6+31a56cf75 master-1 Ready master,worker 8d v1.14.6+31a56cf75 master-2 Ready master,worker 8d v1.14.6+31a56cf75 worker-2 Ready,SchedulingDisabled worker 22m v1.14.6+31a56cf75 $ oc delete nodes worker-2 node "worker-2" deleted $ oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 8d v1.14.6+31a56cf75 master-1 Ready master,worker 8d v1.14.6+31a56cf75 master-2 Ready master,worker 8d v1.14.6+31a56cf75 Workerノードの削除については以上です。 まとめ OCP4/UPIクラスターにRHCOSノードを追加、削除する方法をご紹介しました。特に複雑な手順は無いので、UPI環境でも自由にWorkerノードの追加/削除が行えます。IPIと違い自動的なノードの追加/削除の機能はありませんが、UPIでは自分で工夫して環境に合わせたカスタマイズを行うことができる点がメリットです。次回は引き続き、RHELノードの追加方法について説明します。 Let's get Big Ideas ! *1 : 本来はOCP4でGPUを使う内容の予定でしたが、諸事情により変更になりました…。今後の記事にご期待ください。 *2 : UPIが管理者がノードを用意するという前提のため *3 : 2019年12月時点、v4.1/4.2において *4 : 3ノードクラスターについてはこちらの記事を参照。 https://rheb.hatenablog.com/entry/openshift42-3node-baremetal *5 : 1回目のCSR承認を行わないと追加のCSRは出てきません
Red Hatの須江です。 本記事は OpenShift Advent Calendar 2019 の8日目です。 諸事情により12/9になってしまいましたが、本来は12/8分です。諸事情についてはのちほど。 OpenShift4をIPI(Installer Provisioned Infrastructure)でGCPにインストールする方法 基本的にはドキュメントの「第3章 GCP へのインストール」に従って粛々と進めればよいです。 https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.2/html/installing/installing-on-gcp が、、、2019/12/9現在、GCPのIPIインストーラにバグがあり、この手順通りにすすめてもインストールが正常に完了しません。 Bug 1779866 - When installing openshift via IPI on GCP, compute nodes fail to join the cluster https://bugzilla.redhat.com/show_bug.cgi?id=1779866 Failed IPI install on GCP #2747 https://github.com/openshift/installer/issues/2747 そこで今回は、GCPへのOpenShift4インストール時に注意すべきポイントと、今回のようにインストールが正常に完了できない場合に何が起こっているのか確認する方法についてまとめます。 GCPへのIPIインストールの事前準備 GCPプロジェクト新規作成 IPIインストールではインストーラが多数のリソースを作成するため、リソース管理やアクセス管理を楽にするために専用のプロジェクトを作成しておいたほうがよいです。 APIサービスの有効化 インストーラがGoogle CloudのAPIを利用して環境を構築するので、ドキュメントに指定のあるAPIを有効化しておきます。 ドキュメンに"Google DNS API"とありますが、現在は"Google Cloud DNS API"に名前が変わっているようです。 一部APIは課金を有効にしていないと使えないため、クレジットカード等で決済手段を登録しておいてください。 Cloud DNSにManaged DNSゾーンを追加 インストーラが必要なDNSレコードを自動で登録するため、プロジェクトにOpenShift4クラスタ用のPublic Managed DNSゾーンを作成してください。 GCPでドメインを取得してもよいですし、外部DNSでサブドメインを作成して委譲してもよいです。 リソースリミットの緩和申請 Compute Engine API Persistent Disk SSD (GB) 上限がデフォルト500GBと少ないので、1000GBくらいに拡張するようサポートチケットを発行してください。ドキュメントによると、デフォルトの構成(Master3台+Worker3台)で最低896GB必要とのことです。 Compute Engine API: CPUs こちらも上限がデフォルト24で、Bootstrap Nodeの消滅後でぎりぎり足りる量です。ドキュメントによると28必要とあるので、事前に拡張しておくことをおすすめします。 申請してから実際に拡張されるまで2 business dayかかるよ、と言われますが、実際には数分から数十分程度で対応してもらえるようです。 サービスアカウント作成 インストーラが利用するサービスアカウントを作成し、プロジェクトオーナーのロールを付与しておきます。 ロールを細かく割り当てる場合には以下を参照してください。 - https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.2/html/installing/installing-on-gcp#installation-gcp-service-account_installing-gcp-account インストーラから参照するため、サービスアカウントのキーをJSON形式で生成し、~/.gcp/osServiceAccount.json にコピーしておいてください。 GCPへのIPIインストール try.openshift.com からGCPを選択してインストーラとCLI、Pull Secretなどを入手します。 インストール方法自体は他の環境とほぼ同じです。 GCP特有のパラメータは、インストーラがAPI経由で自動取得してデフォルト値を提示してくれますので、特に事前に調査しておかなくても大丈夫です。 問題発生! その時には 今回発生した現象 インストーラの進捗が99%から進まなくなり、"Still waiting for the cluster to initialize: Some cluster operators are still updating: authentication, console, image-registry, ingress, monitoring"が繰り返し表示されたあと、タイムアウトになってインストーラが異常終了します。 time="2019-12-09T01:03:42+09:00" level=debug msg="Still waiting for the cluster to initialize: Working towards 4.2.10: 99% complete, waiting on authentication, console, image-registry, ingress, monitoring" time="2019-12-09T01:04:42+09:00" level=debug msg="Still waiting for the cluster to initialize: Working towards 4.2.10: 99% complete" time="2019-12-09T01:07:27+09:00" level=debug msg="Still waiting for the cluster to initialize: Some cluster operators are still updating: authentication, console, image-registry, ingress, monitoring" time="2019-12-09T01:09:13+09:00" level=debug msg="Still waiting for the cluster to initialize: Working towards 4.2.10: 99% complete" 何が起こっていたのか Workerの仮想マシンは起動していましたが、クラスタのNodeとして追加されていない状態でした。 そのため、MasterにデプロイできないPodがpending状態になっており、それ以上インストーラの処理が進まない状態になっていました。 どうやって調べたか OpenShift4クラスタの管理コンソールやCLIでの接続情報はインストール完了時に表示されるため、インストールが途中で止まった場合には通常の手段でクラスタにログインすることができません。 しかし、今回のようにMasterの構築が完了している場合には、インストーラが生成したkubeconfigを利用してクラスタを操作することができます。 $ export KUBECONFIG=${INSTALL_DIR}/auth/kubeconfig $ oc get machine -n openshift-machine-api NAME STATE TYPE REGION ZONE AGE test-nldkb-m-0 RUNNING n1-standard-4 asia-northeast1 asia-northeast1-a 3h7m test-nldkb-m-1 RUNNING n1-standard-4 asia-northeast1 asia-northeast1-b 3h7m test-nldkb-m-2 RUNNING n1-standard-4 asia-northeast1 asia-northeast1-c 3h7m test-nldkb-w-a-sq659 RUNNING n1-standard-4 asia-northeast1 asia-northeast1-a 3h5m test-nldkb-w-b-55w5k RUNNING n1-standard-4 asia-northeast1 asia-northeast1-b 3h5m test-nldkb-w-c-snj7m RUNNING n1-standard-4 asia-northeast1 asia-northeast1-c 3h5m =>Machineは起動している(GCP管理コンソールからも確認できる) $ oc get nodes NAME STATUS ROLES AGE VERSION test-nldkb-m-0.asia-northeast1-a.c.openshift-261412.internal Ready master 3h v1.14.6+31a56cf75 test-nldkb-m-1.asia-northeast1-b.c.openshift-261412.internal Ready master 3h v1.14.6+31a56cf75 test-nldkb-m-2.asia-northeast1-c.c.openshift-261412.internal Ready master 3h v1.14.6+31a56cf75 =>Node一覧にWorkerが存在しない あとは、確認したいリソースをdescribeしたり、Podのログを見たりして、問題点を絞り込んでいきます。 網羅的に情報を取得したい場合 個別にリソースのダンプやログを取得するのが面倒な場合には、以下コマンドでクラスタ全体の情報をダンプすることもできます。 $ oc adm must-gather 実行すると must-gather.local.3528907227254367601 という感じでディレクトリが作成され、その下にクラスタ全体のリソースやログが階層化されたディレクトリに格納されます。 本来の用途はRed Hatのカスタマーサポートに情報を提供いただくためですが、コスト節約のためにクラスタを削除したいけど情報は残しておきたい、という場合などにも有用です。 おわりに 自分は普段AWSをメインに使っているのですが、「GCPでもIPIなら簡単にクラスタ建てられるぜイェーイ」と軽く考えていて撃沈しました。 いつか雪辱を果たしたいと思いますので、これからOpenShift4 on GCPの情報をこまめにウォッチします。(反省)
「 マイクロサービスとメッセージングのなぜ [概要編] 」は こちら です。 レッドハットでインテグレーションのためのミドルウェア製品のテクニカルサポートを担当している山下です。 概要編ではメッセージングの良い面ばかりに焦点を当ててきましたが、今回の疑問編ではメッセージングを検討し始めたときに疑問に思ったり困りがちなことを説明したいと思います。概要編とは異なり、細かな技術的内容も含まれますので、その時々で必要な部分や興味ある部分だけ読んでいただければと思います。 (ところで、当初は前回を前編、そして今回を後編にして終わらせようと思っていたのですが、今回もあまりに長くなってしまったので、構成を変えたのでした。 このため当初の前編は概要編と名前を変更しています。) ではまず主に疑問とされることを確認して、その後に対処法を見ていきましょう。 メッセージングを利用することによる主な疑問 対処方法 Q1: メッセージは重複配信されることがある アプリケーションでの冪等性の考慮 Idempotent Consumer パターン Q2: どのようにサービス内のデータベース更新とメッセージ発行をアトミックに実現するのか? データ更新とイベント発行をアトミックに実現する (Transactional outbox パターン) Q3: サービスが発行したコマンドに対する応答を受け取る方法 そもそもコマンドに対する応答とは? コマンドに対する応答イベントを受け取る方法 とはいえコマンドの応答を明示的に受けたい Q4: ユーザがコマンドの実行状況を把握する方法 ビューを通して状態を知る 個別ユーザ向けのメールボックスの提供 クライアント側でのバリデーション 楽観的ロックで最新情報による判断を強制する Q5: 消えないイベントとイベントのバージョニング 間違ったイベントを無効にする イベントのバージョンアップ まとめ Special Thanks 参考文献 [PR] メッセージングを利用することによる主な疑問 Q1: メッセージは重複配信されることがある マイクロサービスでは、システムを構成する各種ノードやネットワークで障害が発生することを前提として受け入れます。一方で、それぞれのサービスは個別にデータベースを持ち、機能上の制約、パフォーマンス特性、そして耐障害性などの理由からXAのような分散トランザクションを利用しません。このためメッセージが失われないようにするには、サービスやメッセージングブローカー間の様々なレイヤーにて送信が成功したことが確実に判断できるまでリトライが行われます。しかし一方でこれはメッセージの重複配信が行われる原因ともなります。 Q2: どのようにサービス内のデータベース更新とメッセージ発行をアトミックに実現するのか? サービスはコマンドを受けると、データベースを更新して同時にイベントを発行することが一般的です。しかし、データベースとメッセージングブローカーは、前述のように分散トランザクションは利用しません。このためデータベースのコミットには成功したものの、メッセージ送信には失敗したということがあり得ます。Kafkaはシステムおけるデータハブとしての役割を担っているため、受け入れられないデータ不整合の原因となります。 Q3: サービスが発行したコマンドに対する応答を受け取る方法 RESTなどの同期通信ではその応答結果を受け取ることが可能です。しかし、メッセージングでは、Fire and Forgetとも言われるように、メッセージは投げっぱなしとするために、サービスはその応答を受取ることはできません。しかし、ビジネス上のアクションや応答を期待してコマンドを送信するケースもあります。メッセージングではどのように応答を受け取ったらよいのでしょうか? Q4: ユーザがコマンドの実行状況を把握する方法 多くのモノリシックなシステムではコマンドは同期処理され、応答が返ってくれば処理は完了しており、完了画面に処理結果を表示させることができます。しかし、メッセージングでのコマンドは、キュー(トピック)に入り、いつコマンドが処理されるのか、あるいはされたのか、そして処理結果としてビューが最新になっているかどうかも、簡単にはわかりません。またユーザは最新でない情報に基づいた判断によって新規コマンドを発行してしまう可能性もあります。ユーザはどのようにコマンドの実行状況を知るのでしょうか? Q5: 消えないイベントとイベントのバージョニング リレーショナルデータベースであればスキーマはAlter文で変更することができます。一方、イベントを一次情報として永続化するイベントソーシングでは、たとえバグによる間違ったイベントでも原則として消せません。しかし一方ではGDPRにも代表されるように消去はユーザーの権利でさえあり得ます。どのようにイベントをキャンセルするのでしょうか?またイベントのバージョンアップはどのように実現したら良いのでしょうか? Q6: サービス間の連携やビジネスプロセスに焦点を宛てた設計やテストが不可欠 (後続編にて) モノリシックなシステムとは異なり、マイクロサービスではサービスが協調して全体としてビジネスプロセスが実現されるので、サービス間のプロトコルやそこで利用されるメッセージの設計が重要となります。またサービスごとにローカルデータベースが存在するために、サービスをまたいた完全な一貫性を保ったトランザクションは実現できません。そしてビジネスプロセスは通常複数のサービスにまたがって実現されるため、個別のサービスでのローカルトランザクションを組み合わせて、データの整合をとりながらビジネスプロセスを完了なければなりません。どのように実現したら良いのでしょうか?(これについては別途後続編で説明します) 対処方法 それでは、これらの疑問に対して対処法を見ていきましょう。そのまま適用できる完全な解決策ばかりではありませんが、可能な限りヒントとなることを記載するようにしました。実際の開発に適用するには参考文献も合わせて参照してください。 Q1: メッセージは重複配信されることがある アプリケーションでの冪等性の考慮 マイクロサービスではサービス個別にデータベースを持ち、データベースあるいはメッセージングブローカーの機能上の制約や、パフォーマンスそして信頼性の理由から、分散トランザクションは利用されません。 これらのサービスやメッセージングシステムの間でメッセージが失われないようにするためには、様々なレベルのレイヤーで送信が成功したことが確実に判断できるまでリトライが行われることになります。これがッセージの重複配信を引き起こす原因となり「少なくとも1回以上(at least once)」の配信となります。逆に、リトライを行わない場合にはロストが発生することがあるため「最大でも1回(at most once)」の配信となります。さらにリトライの回数や時間に上限を設けるならロストも重複も発生する可能性があります。 どれを選択するかはシステムの設計次第ですが、ロストが許されないケースでは基本的に「少なくとも1回以上(at least once)」の配信を前提とし、重複配信時のデータ不整合を防ぐための設計がアプリケーションレベルで必要となります(冪等性の設計)。 これはそれぞれのコマンドやイベントごとに考慮しなければならない設計ではあるものの、実際にはそれほど頻繁に重複やロストが起こるわけではありません。多少なりデータ不整合が許容できる場合には対策しなくても良いことがあります。また株価情報やIoTによる情報など、絶え間なく最新のデータが送られてきて、ロストしてもすぐ次のメッセージの最新状態で上書きすれば実用上は問題ないといったケースもあります。また"いいね"ボタンなど、ロストさせてもビジネス上の大きな影響はなく、ユーザが気がついたときに再実行すればよい程度のケースもあります。 あるいは、重複処理しても結果がうまいこと変わらないといったケースもあります。例えば、ユーザ名をユーザ名で重複して上書きしても結果が変わらないといった具合です。またこれを積極的に用いて、必要とされる最新状態をイベントに含めておき、重複処理されても結果が同じとなるイベントに設計しておく方法もあります。このように無視もしくは回避する方法をまずは検討します。 Idempotent Consumer パターン とはいえ、ビジネス上の取引として「正確に一回(exactly once)」の処理を行わなければならないこともあります。この場合には、Idempotent Consumer パターンを利用します。 基本的な考え方としては、メッセージにトランザクションID(もしくはリクエストID)を持たせるようにします。そして受信側(コンシューマ側)では同じトランザクションIDを持ったメッセージが配信された場合には重複として破棄するようにします。重複ではないメッセージは、その処理を行うとともに同一ローカルトランザクション内でトランザクションIDを保存します。 実装方法にはいくつか方法がありますが、リレーショナルデータベースを利用している場合の単純な方法は次のようなものです。まず新規に発生したコマンドやイベントにはトランザクションIDとしてUUIDを付与し、そしてそこから後続で派生して発行されるイベントやコマンドにも同じIDを使いまわすようにします。そして受信側(コンシューマ側)では、オブジェクトID(より正確には集約ルートのID)もしくはコマンド型やイベント型ごとに、処理したメッセージのIDを数日程度保存するようにします。NoSQLやイベントソーシングでは、いくらか異なる方法を用いますが基本的な考え方は同じです。 また、イベント特有の重複排除の方法として、オブジェクトが発行するイベントに(トランザクションIDではなく、)インクリメンタルなシーケンスナンバーをつける方法ががあります。この場合、受信側(コンシューマ側)では、オブジェクトごとに最大のシーケンスナンバーのみを保存すればよく、シーケンスナンバー以下の値を持つメッセージは重複として破棄することができます。これは個別のオブジェクト(より正確には集約)の単位で、適切な排他制御などにより、インクリメンタルなシーケンスナンバーをイベントに付与でき、そして受信側(コンシューマ側)にてイベントの処理順も担保できる場合に有効です。 このように重複処理の抑制は、(メッセージングブローカーやサービスメッシュなどの通信の問題として完全に分離することはできないため、)アプリケーションレベルでの冪等性の設計や支援を含めて担保される必要があります。 ノート:「正確に一回(exactly once)」の配信に利用できる Kafkaの機能 [興味ない方は読み飛ばしてください] Kafkaを利用する場合には、条件によっては簡単に重複配信に対応できるケースがあります。 単一Kafkaクラスタ内にて、(他のデータベースを更新せずに、)メッセージの送受信とその処理を行えるのであればKafkaのトランザクション機能が使えます。これは例えば メッセージ内容の変換や拡張をするようなケースで利用できます。 また、Kafkaのメッセージには、トピックのパーティション内での位置を表すオフセット値が含まれています。これをトランザクションIDの代わりに受信側(コンシューマ)で保存して利用する方法があります。あるいはオフセットの保存先としてKafkaが提供する__consumer_offsetsトピックを利用せずに、受信側(コンシューマ)のデータベースに保存するようにして、メッセージ処理と同じトランザクションの中で同時にオフセットをコミットする方法もあります。これらはトピック内の同一メッセージが受信側(コンシューマ)に重複して配信されるケースには有効です。しかし、トピックに対して同一メッセージがリトライなどにより送信されて、実際にメッセージがトピック内に重複して存在してしまった場合には検知できません。 一方でKafkaブローカーにはメッセージの送信側(プロデューサ)からのリトライによる重複メッセージを検出する仕組み(enable.idempotence オプション)もあります。この仕組みでは送信側(プロデューサ)のKafkaクライアントは、メッセージにシーケンスナンバー(とプロデューサID)をつけるようになります。そして送信側(プロデューサ)が何らかの理由によりリトライしたことによる重複は、Kafkaブローカー側で検出して破棄するようになります。 ところがこの方法も完璧ではなく、送信側(プロデューサ)プロセスに障害が生じ、再起動などによってKafkaへのメッセージ送信そのものが再実行されるような状況では、(新たなプロデューサIDが割り当てられ、)同一メッセージとはみなされずにトピックに重複が発生します。これに対応するために transactional.id を指定することで再起動の前後で同じプロデューサであることを明示的に指定することができますが、そのためにはアプリケーション側で transactional.id の維持管理が必要となります。 このようにKafkaには、「正確に一回(exactly once)」の配信のために利用できる機能がいくつかあるのですが、条件や制限もあるために、実際に利用できるかどうかは状況次第です。 Q2: どのようにサービス内のデータベース更新とメッセージ発行をアトミックに実現するのか? データ更新とイベント発行をアトミックに実現する (Transactional outbox パターン) サービスはコマンドを受けると、データベースを更新して同時にイベントを発行する必要がありますが、これらを同時にXAなどの分散トランザクションに参加させることはできません。こうしたデータ更新とイベント発行をアトミックに実現して、システムのデータ不整合を避けるためには Transactional outbox パターンが利用できます。 これは予めデータベースの中にメッセージを一時的に挿入することができる概念的なキューとしてのOutboxテーブルを用意しておきます。その上で、発行するイベントは、アプリケーションのデータ更新と同じトランザクション内でOutboxテーブルに挿入してコミットします。これによりアプリケーションのデータ更新とイベント発行の両方が確実に行われる(あるいはロールバックにより両方が確実に行われない)ことを保証します。その後に別途、Outbox テーブルに挿入されたイベントを取り出してKafkaのトピックへとメッセージを送信します。この際に Debezium などのチェンジデータキャプチャ(CDC)を利用することも可能です。 なお、outboxテーブルからKafkaへとメッセージを転記する際の障害やプロセスが落ちて再実行されるような場合に、メッセージが重複して発行される可能性があります。このため前述の冪等性の設計も合わせて必要になることに注意してください。 ノート: RESTのような同期通信でも冪等性の考慮が必要なことがあります ところで少し話は戻って、冪等性の考慮はメッセージング特有の問題とは言えなくなってきました。サーキットブレーカーやサービスメッシュ、AWS step functions などの利用により、RESTなどによる同期通信であってもリトライできる(場合によってはロストも防ぐ)環境が整ってきました。このため同期通信のケースでも冪等性の設計が必要になることがあります。 しかし同期通信を用いてサービス間をまたぐビジネスプロセスを実現することは少し慎重になったほうが良いかもしれません。SOAでの失敗パターンの1つとして、サービスを横断してビジネスプロセスを実現する中央集権的なプロセス管理がコントロールを手放さず、個別サービスに対して同期通信による主従関係を強いたため、サービスに本来あるべき知識とコントロールを奪って骨抜きにしたことが指摘されています。 Q3: サービスが発行したコマンドに対する応答を受け取る方法 そもそもコマンドに対する応答とは? RESTなどによる同期通信ではその場で応答結果を受け取ることが可能です。一方メッセージングでは、コマンドに対する明示的な応答というものは基本的に存在しません(しかし例外的に取り扱う方法は後述)。 サービスは何らかの状態変更があればイベントを発行します。ですから、コマンドに続いて発生する状態変更のイベントを購読することで、遠回しながら結果や状況を知ることができます。 RESTなどの同期通信では、コマンドに対する処理が長くとも1分程度で終わることが暗黙的な前提となっており、人を介するプロセスや長時間のビジネスプロセスは発生しませんし、返り値の型や値も通常は1つに定まります。しかし一方の非同期なコマンドではそうした制限はなく、明示的に返り値を受けるための専用の仕組みが存在するわけでもありません。これはサービスが組織と対応するものだと考えれば分かりやすいかもしれません。実際のサービスや組織では、同期通信に求められるような1分間でビジネス上の意味ある結果を提供できる機能ばかりではありません。 例えば注文コマンドを考えた場合、コマンドの宛先の組織(サービス)内部では受注生産や発注のために数日かかるビジネスプロセスが実行(更に内部で受注イベントなどの細粒度ともいえるイベントやコマンドが利用)されることもあります。また注文コマンド1つに対して、「発送済イベント」や「到着済イベント」といった複数のイベントが時間を分けて発生することもあります。そして注文内容の不備によるバリデーションエラーや、生産停止や在庫破損による「引当失敗イベント」といった、様々な型を持つ例外イベントが発生する可能性もありますし、当初の呼び出し先とは異なるサービスから発行されたイベントであることもあります。 このようにコマンドに対する応答イベントというのは、状況によって時間も型も数も、そして応答元サービスさえも異なるものです。しかしそれでも、コマンドとイベントの対応関係は実際のビジネスや組織のインタラクションに対応しており、サービス間において同意を得た明確な契約に従うものです。 こうしたサービスの間のコマンドと応答イベントの繰り返しの中でビジネスプロセスが進行していきます。実際にこうしたビジネス上の様々なイベントに対応することは困難ではありますが、むしろそれらへの対応関係を歪めずに直接相対できることが非同期メッセージングの価値でもあります。 コマンドに対する応答イベントを受け取る方法 イベントはサービスの状態変更をシステム全体に向けて知らせる仕組みなので、コマンドの送信元に向けて専用に発行されるものではありません。逆に言えばコマンドの送信側サービスは、自身の興味ある各種のイベントを監視して備える必要があります。これには様々な方法がありますが、ここでは関係するイベントを受け取ってハンドルするための具体的な方法例をもう少しだけ説明します。 通常イベントの中には、変更されたオブジェクトのID(より正確には集約ID)が含まれており、どのオブジェクトに変更があったのかは分かります。 一方イベントを購読するサービスでは、その内部の1つ(または複数)のオブジェクトにイベントをディスパッチすることになります。この際にイベント内容からディスパッチ先のオブジェクトが即座に特定できることもありますが、そうでない場合にはサービス内にある情報を用いてディスパッチ先のオブジェクトを特定しなければなりません。 オブジェクトを特定するマッピング方法として、まずは関連する外部キーであるオブジェクトID(あるいはビジネス上の候補キーとなる何らかのNo.など)を利用する方法があります。例えば、もしもイベントを購読するサービス内のオブジェクトにて、外部キーとしてオブジェクトのIDを保持している場合には、その関連を利用してマッピングすることができます。他には相関IDを利用する方法もあります。これはメッセージIDや前述のトランザクションID(あるいはそれに類したID)をコマンドに付与し、コマンドから関連して発生したイベントに、それを相関IDとして引き回させ続けることでマッピングします。 マッピングを完了させてオブジェクトを特定した後は、即座にイベントハンドラ内でオブジェクトにイベントを処理させることもできますが、受け取ったイベントをコマンドのキュー(トピック)側に入れ直して実行を待つ方法もあります。これは(概要編の最後の方で少し触れたように)排他制御のために複数のスレッドから同じオブジェクトを操作させたくない場合や、1つのイベントから多数のオブジェクトへとマッピングが必要な状況で役に立つことがあります。 またところで、これから何らかの作成を指示するようなコマンドでは、そもそも相手先オブジェクトもそのID自体も存在しません。このような場合には、コマンド発行側で相手先オブジェクトのID(UUIDなど)を代理作成して保持しておき、それを作成指示コマンドで指定することで、相手先オブジェクトのIDを事前に把握する方法もあります。 とはいえコマンドの応答を明示的に受けたい コマンドに対する応答をメッセージングで受けられないのはどうしても困るケースがあるかもしれません。同期通信のようにコマンドの実行結果を明示的に受ける方法として、(イベントのトピックを通さずに、)コマンド結果を専門に受けるキュー(トピック)を用意する方法もあります。この場合、サービスのコマンド結果は(イベントとは別に、)この専用のキューに相関IDをつけたメッセージとして応答します。 この方法は、特定のコマンドや呼び出し元に特化した結果をメッセージに付加することができ、またコマンドエラーなどで状態変更がない場合でも結果を受け取れます。そして別途後述するSAGAを実装するためにも役立つこともあります。しかしサービス間の依存関係を強固なものにしてしまう傾向もあるために利用には注意が必要です。 なお、こうした実行結果を単に伝えるようなメッセージは、コマンドやイベントとは役割が異なるため、ドキュメントと呼ばれて区別されることがあります。 Q4: ユーザがコマンドの実行状況を把握する方法 ビューを通して状態を知る 多くのモノリシックなシステムではコマンドは同期処理され、応答が返ってきた時には処理は完了し、完了画面に処理結果を表示させる可能です。しかし、メッセージングによる非同期なコマンドでは、コマンドはキュー(トピック)に入り、いつコマンドが処理されるのか、あるいはされたのかは簡単には分かりません。 コマンドの結果としてユーザに対する明確な応答はなくとも、ビジネスプロセスの進捗に応じて、サービス内のオブジェクト状態が通常は変わっていきます。ですからコマンドの返り値ではなく、ビューからこれらのオブジェクトの状態を参照することで進捗を確認することができます。実際のところメッセージングによるシステムでは、コマンドの実行完了に続いて様々なイベントが発生しながら後続処理進んでいくために、コマンド自体の完了にはあまり意味があるものではありません。(完了画面への遷移というよりは)一覧画面や詳細画面などから様々なオブジェクトのステータスをいつでも確認できるようにします。 個別ユーザ向けのメールボックスの提供 もしも個別ユーザへの通知が必要なら、各ユーザごとにメールボックスを提供し、そこへコマンド成否や進捗状況を通知する方法もあります。これは実際にメールで通知する方法もあれば、マイページのようなところから汎用的に通知を参照/管理できるメールボックスを用意する方法もあります。また個別の要件に特化した各ユーザ向けのビュー(個人の注文履歴や申請履歴など)を開発することもできます。 さらに、ユーザの利用している画面やデバイスへと通知が必要なら、WebSocketやgRPCのストリームを用いて、コマンドを発行したユーザの画面やデバイスに通知やデータ転送することもできます。データハブとしてのKafkaは状態変化のイベントを様々なサービスに配信することができますが、これは実のところデータセンター内にとどまるものではありません。クライアントの画面やデバイスへと同様にイベントのプッシュ範囲を広げ、デバイス内にビューを作成して画面表示することも可能です。多くのクライアントでは、こうしたビューモデルやイベントを取り扱うための十分なフレームワークやライブラリを既に持っています。 クライアント側でのバリデーション ところで現実的な問題として、ユーザの実行したコマンドが何らかの不備によってバリデーションによりエラーとなった場合には、やはり即座にユーザへと通知できなければ不便なこともあります。このためクライアント側での事前のバリデーションが特に有効です。 これにはコマンド実行の条件を満たさない場合に、クライアント画面側で選択できる数値を制限したりやボタンを押せないようにすることや、WEBサーバで簡易バリデーションを行うことも含まれます。なお最新データはサービス側にしかないので、こうした事前のバリデーションにも限界があり、サービス側でのバリデーションも合わせて必要です。 これによってバリデーションロジックがクライアント側とサービスに分散しますが、いくらかクライアント側へのロジック流出を回避する方法として、ビューにバリデーションを補助する情報を持たせる方法もあります。例えばビューに、注文の変更が可能かどうか、またキャンセルが可能かどうか、といったフラグを持たせ、クライアント画面ではこのフラグを基にボタンの有効無効を判断するようにします。モノリシックなシステムとは異なり、イベントソーシングの場合には様々な目的に特化したビューをいくらでもカジュアルに作成して利用するようになります。 また、イベントソーシングでは集約を超えた単位でのバリデーション(例えばユーザ名のユニークチェックなど)は難しい傾向もあるため、このような場合にはビューを用いたクライアント側でのバリデーションが重要になります。 楽観的ロックで最新情報による判断を強制する メッセージングにおいては、ビューが最新になっているかどうか簡単にはわかりません。ユーザは最新でない情報に基づいた判断によって新規コマンドを発行してしまう可能性や、タイミング悪く他のユーザが既に別コマンドで指示していることもあります。 これはメッセージングに特有の問題ではなく、一般的な静的画面のブラウザアプリなどでも起こることですが、こうした場合と同様に楽観的ロックによるバージョンチェックを行って解決することができます。これは予め画面に渡す各レコードや情報にバージョン番号をもたせておきます。そして、コマンド発行時にはそのバージョン番号をあわせて送信し、サービス側では対象レコードのバージョン番号が同じ場合にのみコマンドの処理を行います。もしもバージョン番号が異なっていれば、最新の情報に基づく判断でないか、もしくはタイミング悪く他のユーザが同じオブジェクトを既に更新したとして、コマンドをエラーにします。 Q5: 消えないイベントとイベントのバージョニング 間違ったイベントを無効にする リレーショナルデータベースであれば、スキーマはAlter文で変更することもできます。一方、イベントを一次情報として永続化するイベントソーシングでは原則としてイベントは消せません。これはイベントがビジネス上の監査ログの意味合いとなるだけでなく、実装上も一貫してイベントをイミュータブルにすることで、進化を続ける各種ビューを繰り返し導出するために必要なことでもあります。 もしも注文を削除するという、ビジネス上考えられることであれば、単に「注文削除イベント」を発行すれば、イベントログにはそれが追記され、そしてそのイベントを購読するビューは、注文に削除フラグを立てるか、あるいは実際にビューのデータベースからレコード削除することができます。 ではもしも、バグによって間違って作成されてしまったイベントはどうしたら良いのでしょうか。この場合キャンセルイベントを利用する方法があります。これはキャンセル対象のイベントへのリンクと、キャンセル理由のテキストを含めたキャンセルイベントを作成するというものです。 また、GDPRにも代表されるように個人情報の消去をユーザに求められる場合にも、やはりイベント自体は削除しません。このような場合には予めイベントの暗号化を行っておき、ユーザ情報の復号化に必要な暗号化キーを削除します。復号手段をなくすことで事実上の削除を行います。 イベントのバージョンアップ 各種のモデルやビューはシステムの成長に応じて進化し続ける一方で、イベントソーシングにおいては、イベントはシステムのライフサイクル全体に渡って基本的には削除も変更も行われません。このためイベントを拡張する場合には、従来のイベントと新しいイベントをシステム内に共存させるためにバージョン管理が必要となります。 バージョンアップ間での互換性を得る簡単な方法としては、イベントのシリアライズフォーマットとして、JSONやXML、Avro、protobufsなどの弱いスキーマ(Weak Schema)を利用する方法があります。この場合フィールドの追加などのいくつかの変更に関しては互換性を得ることができます。 しかしフィールド名の変更など、弱いスキーマでは対応できないバージョンアップもあります。この場合には、イベントにバージョン番号をつけて、メッセージの受信側(コンシューマ)にてアプリケーションが理解できるイベントに、ダウンキャストやアップキャストをする仕組みを実装することになります。ただし、こうしたイベントの変換コードが重なり続ければ負の遺産になり得ます。 基本的にはイベントは削除できないものですが、とはいえ不都合なこともあるかもしれません。あまり積極的にお勧めできないとされていますが、全イベントを置き換える方法 ( Copy and Replace )もあります。詳細はここでは割愛しますが「 Versioning in an Event Sourced System 」書籍の中で、上記を含む様々なバージョン更新のテクニックや手順が紹介されていますので興味ある方はご覧ください。 まとめ 今回の疑問編では、メッセージングに関して良く疑問とされることとその主な解決方法を説明してきました。まずメッセージの重複配信への対処方法として冪等性の設計やIdempotent Consumerパターンについて確認しました。そして、アプリケーション処理とメッセージ発行をアトミックに行うTransactional outbox パターンも説明しました。その後、コマンドにおいてそもそも応答とは何か、そしてその応答イベントを受ける方法についても説明しました。また、ユーザがコマンドの実行状況を知る方法や、関連してクライアント側でのバリデーションが有効であることも確認しました。そして最後にイベントを無効にするキャンセルイベントのテクニックや、簡単ながらイベントのバージョニングについても触れました。 もしかすると非同期メッセージングは面倒くさいと思ったかもしれません。実際面倒くさいのですが、これは非同期メッセージングが同期通信とは異なるレベルで、ビジネス上と技術上の問題に取り組んでいるからでもあります。 次回は、結果整合性を保ったトランザクションや、サービス間の連携やビジネスプロセスに焦点を宛てた設計として、SAGAやEventStormingについて説明したいと思います。関連して現在のシステム開発で主流となっているリレーショナルデータベースとデータ中心の設計に対する警告についてもいくらか触れたいと思っています。(とはいえ今回の内容も当初は全く書く気のなかったことばかり、、自分自身今回の記事のスコープがよく分からなくなっていて、次回の内容もでたとこ勝負なのであまり期待しないでください、、次はいつ頃になるかな、、。) Special Thanks 勉強会での議論がこの記事の基になっています。参加者の方々、そしてレビューしてくださった方々、どうもありがとうございました! JavaEE勉強会 : Microservices Patterns 読書会 JCUG : Camel In Action 読書会 参考文献 Microservices Patterns Introducing EventStorming Versioning in an Event Sourced System Enterprise Integration Patterns Designing Event-Driven Systems Domain Modeling Made Functional Exploring CQRS and Event Sourcing UMLによるビジネスモデリング [PR] オープンソースの Apache Kafka をベースとした製品として、Red Hatから AMQ Streams が提供されています。 AMQ Streams にはさらに、KubernetesやOpenShiftの上でApache Kafkaの運用の自動化を実現する、オープンソースの Strimzi をベースとした機能も含まれています。Red Hat製品に興味ない方もKubernetesを利用している方は Strimzi をぜひ使ってみてください。
レッドハットでコンサルタントをしている id:nanodayo です。 赤帽エンジニア Advent Calendar 2019の8日目のエントリーです。 ご存知、Ansible のplaybookはYAML形式で記述しますが、YAMLのAnchor と Aliasも適用できるのでご紹介します。 なお、多用すると複雑なplaybookになり保守しにくくなるので強くはおすすめはしません。 YAMLのAnchorとAlias 同じような記述をたくさん書くときに便利な記法です。 以下のような仮想マシンの変数定義(vars)があり、殆ど同じパラメータを設定するので、共通設定として一箇所で書きたいとします。 (説明用に作った物なのでデータ構造は適当です。) vars: vms: - hostname: "test0.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" - hostname: "test1.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" - hostname: "test2.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net3" このような定義の場合、例えばimage の定義を全台違うものにするには3箇所に変更が必要なってしまうので 雛形を決めて、1箇所だけで管理できるようにしたいケースでAnchorとAliasを使います。 --- - name: yaml anchor and alias test hosts: all gather_facts: no vars: vms: # Anchorに登録 - &test hostname: "test0.example.com" image: "cirros" flavor: "m1.tiny" az: "az1" nics: - name: "net0" - name: "net1" # 全く同じ内容を参照 - *test # 参照先で部分的に上書き - hostname: "test1.example.com" <<: *test # 参照先で部分的に上書き2 - hostname: "test2.example.com" # list はlistごと上書き nics: - name: "net3" <<: *test tasks: - name: display vms data debug: var: vms 実行すると以下のようなデータとして扱われていることがわかります。 TASK [display vms data] ********************************************************************************************************************** ok: [localhost] => { "vms": [ { "az": "az1", "flavor": "m1.tiny", "hostname": "test0.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test0.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test1.example.com", "image": "cirros", "nics": [ { "name": "net0" }, { "name": "net1" } ] }, { "az": "az1", "flavor": "m1.tiny", "hostname": "test2.example.com", "image": "cirros", "nics": [ { "name": "net3" } ] } ] } test0.example.comの内容をひな形にして参照し、test1.example.com, test2.example.comのように参照先で定義している値で上書きされてる事がわかります。 データ構造全部まるごとAnchorに登録ではなく、部分的に使うこともできます。 部分的なAnchorを追加したサンプルは以下。 vars: vms: # Anchorに登録。部分的なものも追加 - &test hostname: "test0.example.com" image: &image "cirros" flavor: "m1.tiny" az: &az "az1" nics: &nics - name: "net0" - name: "net1" # 全く同じ内容を持ってくる - *test # 参照先で部分的に上書き - hostname: "test1.example.com" <<: *test # 参照先で部分的に上書き2 - hostname: "test2.example.com" # list はlistごと上書き nics: - name: "net3" <<: *test # 部分的なAnchor を参照 - hostname: "test3.example.com" image: *image flavor: "m1.tiny" az: *az nics: *nics vars に限らず task の記述に対しても適用ができます。 --- - name: yaml anchor and alias test hosts: all gather_facts: no tasks: - name: sample task block block: &task_block - &task name: sample task in block 1 debug: &module msg: "sample message 1" ignore_errors: yes - name: sample task in block 2 debug: msg: "sample message 2" <<: *task - name: sample task out of block debug: <<: *module - name: sample task block(alias) block: *task_block とまぁ一見便利な記法なのですが、Ansible playbookで使う場合に関しては 同じ値を一箇所で定義するだけであれば、varsの値に変数を定義すれば可能 特定のホスト群でのデフォルト値を設定したい場合は group vars や role default 等が登録できる。 複数task をまとめたものを再利用したいならファイルを分けて include_playbook などで読み出せる と、Ansible側の機能で事足りる場合もあります。 GitLab CIでAnchor Aliasを使う Ansible以外ですと、GitLab CIのYAMLファイルを記載する際、パイプラインの処理で共通の内容が多く定義するのにお勧めできます。 以下、GitLab CIのパイプラインの定義例です。 細かな説明はしませんが、パイプラインに必要な前処理(before_script)等が、どのパイプラインでも同じというケースが多いです。 .Unit_test_template: &unit_test_template stage: unit_test only: - staging - master image: name: unittest_container before_script: - eval $(ssh-agent -s) - echo "${CICD_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null - echo -e "Host *\n\tPort 22\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config - ansible -c local tests/unit_test_presetup.yml after_script: - ansible -c local tests/unit_test_cleanup.yml tags: - container-server Unit_test1: <<: *unit_test_template script: - bash unit_test1.sh Unit_test2: <<: *unit_test_template script: - bash unit_test2.sh なお、GitLab側のドキュメントにもっと良いサンプルが載っています。 https://docs.gitlab.com/ce/ci/yaml/#special-yaml-features おまけ Anchor / Alias以外のYAMLの特徴で、Ansibleで使えるか試してみたもの紹介します。 変数の値の型を指定する 通常、シングルクォートやダブルクォート でくくっていれば文字列、そうでなければ数値として解釈されるので、あまり明示的に指定するケースはないと思われますが、以下のように値の前に !!str 等を書けば指定ができるようです。 --- - name: yaml test hosts: all gather_facts: no vars: string_test: !!str 1234 int_test: !!int 1234 tasks: - name: step1 string_test debug: msg: "{{ string_test }} is {{ string_test | type_debug }}" - name: step2 int_test debug: msg: "{{ int_test }} is {{int_test | type_debug}}" ... 実行結果 # ansible-playbook -i hosts yaml-test.yml PLAY [yaml test] ***************************************************************************************************************************** TASK [step1 string_test] ********************************************************************************************************************* ok: [localhost] => { "msg": "1234 is AnsibleUnicode" } TASK [step2 int_test] ************************************************************************************************************************ ok: [localhost] => { "msg": "1234 is int" } PLAY RECAP *********************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ... を使って好きな場所で終了(はできない) YAMLの文法上、 --- が開始 ... が終了となっています。 省略も可能なので、特にplaybookでは ... を書かないケースが多いです。 「playbookのデバッグ時に、止めたい箇所に ... を入れれば exit命令的に使えるのでは」と思って試しましたが、 ... より後にコメントではないものを書くとsyntax errorになります。 # これは動きません --- - name: yaml test hosts: all gather_facts: no vars: string_test: !!str 1234 int_test: !!int 1234 tasks: - name: step1 string_test debug: msg: "{{ string_test }} is {{ string_test | type_debug }}" ... - name: step2 int_test debug: msg: "{{ int_test }} is {{int_test | type_debug}}" yamllintでも同様にNGだったので、YAML的はそういう使い方を想定してないのでしょう。
はじめに Red Hat Tech Nightとは 今回のハイライト ハイライト1:過去最大の参加者・Twitterトレンド入り ハイライト2:過去最大のLT数、新メンバー多数発表 ハイライト3:社長をいじることができた さいごに Misc. はじめに こんにちは!レッドハットでマーケティングに所属する中村( @fideleruuth )です。この記事は、 赤帽エンジニア Advent Calendar 2019 の7日目の記事です。今回のブログ記事では、別のことを発信することを考えていたのですが、Red Hat Tech Nightのハイライトについてお伝えするのを忘れていたのを思い出しましたので、そちらをご紹介したいと思います。今日は土曜日ということもありますので、気楽に読んでいただければと思います。(LT資料へのリンクもあります) ちなみに、メインのRed Hat Forum Tokyo 2019についても サマリレポート が公開されていますので(資料やセッション動画も)、ぜひこちらも御覧ください。 rheb.hatenablog.com Red Hat Tech Nightとは Red Hat Forum Tokyo 2018と合わせて、レッドハット日本法人のエンジニアの発案から、オープンソース技術をより多くの人に広める活動としてはじまった夜の勉強会です。詳細は以下のブログ記事が詳しいのでご確認ください。 rheb.hatenablog.com レッドハットのエンジニアはその技術領域に応じて様々なコミュニティ活動に顔を出していますが、Red Hat Tech Nightでは普段は表に出てこないようなレッドハットのエンジニアも含め一同に揃って最新の技術や個々のエンジニアが気になっている技術情報について、一方的にお伝えするという場です。 そのため、Red Hat Tech Nightではほぼトピックを指定せず、レッドハットが関わる技術領域であればなんでも良いくらいの大雑把さで、エンジニア何名かがやりたくなったらやる、という再度強調しますが、なんとも一方的な(Self-drivenであってUser-drivenではない)会になっています。 一方で、レッドハットのユニークさは、最先端の技術開発を行うアップストリーム、そして製品版として提供するダウンストリームという両方に足を突っ込みながらビジネスを回していることにあると(個人的に)思います。Red Hat Tech Nightは特に最先端の技術開発を行うアップストリームに関する情報を、情報感度の高い(あるいは技術にキャッチアップしようとしている)エンジニア向けに多くの刺激を与えていると感じています。そういう意味で、今回の会の冒頭で僕から紹介した通り、「聴講者を置いてきぼりにする新感覚ミートアップ」「レッドハットの珍獣を眺める会」という表現をしています。 今回のハイライト さてさて、非常に前置きが長くなってしまいましたが、本題である「 第3会 Red Hat Tech Night 2019.11 」のハイライトと、そういえば資料が公開されていなかったようなので、この機会に公開しておこうと思います。 ハイライト1:過去最大の参加者・Twitterトレンド入り 3回しかやっていないので、過去最大とか言ってもアレなんですが、前回のRed Hat Forum併設でのキャパは150名が限界でしたが、今回は200名以上入れるくらいの部屋を活用しました。結果、ちゃんとカウントできていないのですが、観測できているだけで250名以上の方が現地参加されていたようです。 一方的な情報提供であるにも関わらず、このように多くの方々に参加いただけたのは非常に嬉しいことでした。 また、Red Hat Forumではあまりツイートが促されずにうずうずしていた参加者の皆さんがいた気がしたので、ガンガンツイートを促した結果、当日Twitterトレンド入りしていたようです。最新の技術についての率直な意見が聞けたり、ついていけずにワクワクする技術を知れた/帰ってから勉強しようというモチベーションに繋がっているという証なのではないかと考えております。発表する側も聞き手も楽しめる、不思議なイベントです笑 すでにトレンドに入っておる。 #RHTN pic.twitter.com/7e74a1GpIL — akasagix (@akasagix) 2019年11月15日 ハイライト2:過去最大のLT数、新メンバー多数発表 2つ目のハイライトはそのLTの数です。LT大会ってせいぜい10名くらいが 限度だと思うのですが、今回のLTは15人が実施と非常に多くのエンジニアが、様々なトピックで発表しました。今回は古参メンバーのみならず、入社1年以内(昨年のRed Hat Tech Nightがきっかけで入社した人も!)のエンジニアからの発表も多くありました。Red HatってLinuxの会社じゃないの?と思う方も多いと思いますが、技術が進化していくのと並行して、新しい血が入ってエンジニアの多様性もパワーアップしています。 Connpassへスライドを貼り付けるのを忘れていたので、遅ればせながらこのタイミングで公開いたします。5分のLTではキャッチアップできなかった内容など読み直していただければと思います。 ▼▼▼発表スライド▼▼▼ こちらから閲覧 ハイライト3:社長をいじることができた もう一つ今回の試み?として、社長をいじることができたのも、司会をやった僕としては非常によかったポイントです。普段はきっちりとスーツを着て、公式なイベントの基調講演をするイメージのある社長ですが、帰りがけにフラッと現れてくれました(実は事前にお願いしてましたが)。 個人的には毎回Ansibleのコミュニティでスポンサーする際に望月社長の顔写真を載せたりしてこのひとがお金くれてるよ〜とお知らせしているのですが、なかなか本物がイベントに現れることはありません。Forumと併設しているからこそ、このような形で望月社長に一言コメントいただけたことはレッドハットのエンジニア陣もすごく喜んでいました。 ※口癖で、「一応、社長」とか言ってごめんなさい。 エンタープライズ色が強いため、ビジビジ(ビジネスっぽく堅苦しい)してしまうことが多いのですが、社長含めカジュアルなイメージを改めて感じてもらえていれば、と思っています。 社長の望月 さいごに さて、3回目となると小慣れてきた感がありますが、色々な人のご支援があり、この会が成り立っています。超絶ノリで企画から実行までやっているのですが、このようなノリだけで運営しているような会を支援してくれている会社とLT登壇、パネルディスカッション登壇へご協力いただいたレッドハットエンジニアの皆さんに感謝するとともに、一緒にオープンなカルチャー/雰囲気を作っていただいている参加いただいたユーザーの方々にも感謝いたします。 次にやるのはいつになるか、皆さんのご期待に添えるかわかりませんが、技術だけでなく、文化をお伝えし、感じていただき、更にはその場で人(仲間)と繋がり、新たなきっかけとなる場づくりを継続していければと思います。 それでは、See You Next Time!! Connpassで Red Hat OSSアドボケイトチーム のメンバーになると次回のイベント通知を受けることが可能です。いつになるかわかりませんが、気長にお待ち下さい! Misc. 当日流していた音楽は僕の選曲でした。アングラ感、あとはLTをラップバトルっぽい感と解釈して元祖ヒップホップ系含めて選曲いたしました。 music.apple.com
Red Hatの福岡オフィスでソリューションアーキテクトをしている田中司恩です。 この記事は OpenShift Advent Calendar 2019 - Qiita 7日目のエントリになります。 前回 、 前々回 と、ネットワーク制限環境下の重たい内容が続いたので、今回は軽ーい内容でお届けします。 はじめに さて、今回も例のごとくOCP4/UPI *1 な内容です。 *2 今回のテーマはコレです。 [図1] 3ノードクラスター 本題に入る前に…今回の記事を書くにあたり、下記のような伏線がありました。 OCP4.2でマスターノードがスケジュール対象になった IPIでインストールしたOCP4.2クラスタで、Worker Nodeを Replica=0 にしてみたらクラスタが生きてた Baremetal IPIのデモ動画を観た https://youtu.be/B4JbGDpDJEs?t=1254 項目1については、以前書いた記事で解説済みです。下記を参照ください。 rheb.hatenablog.com 次に項目2。スケールダウンのテストをしている時に項目1のことを思い出し、Masterノードをスケジュール対象にして、Workerノードを0台にしたら見事にクラスタが動いてしまいました。 トドメが項目3。先日の Kubecon/NA と併催されていた、 OpenShift Commons Gatherings のビデオと資料が公開されました。 Recap: OpenShift Commons Gathering at Kubecon/NA San Diego [Videos + Slides] – Red Hat OpenShift Blog 上記リンク内の「OpenShift 4 Release and Road Map Update」の動画の中で、今後のOCPで実装予定のBaremetal IPIのデモがありました *3 。Baremetal IPIの詳細は割愛しますが *4 、その中でMasterノードが3台のみでクラスターが立ち上がっているのを確認しました。 その後にドキュメントを色々と漁っていると見つけました。英語版のリリースノートに記載があるのを。 Chapter 1. OpenShift Container Platform 4.2 release notes OpenShift Container Platform 4.2 | Red Hat Customer Portal 1.2.1.4. Three-node bare metal deployments (Technology Preview) In OpenShift Container Platform 4.2, the ability to deploy a cluster without distinct compute, or worker nodes, on bare metal clusters is a Technology Preview feature. If you do not create worker machines when you install your cluster on bare metal infrastructure that you provision, all Pods, including the router Pods, are deployed on the control plane, or master, machines instead. Note that this deployment method is not available in cloud-based clusters in OpenShift Container Platform 4.2. It is not recommended to use technology preview features in production clusters. 以前OCP4.2の記事を書いた時には無かったので履歴を調べてみると、どうも11月に入ってから追加されたようです *5 。 要約するとこんな感じです。 OCP4.2において、Workerノード無しでベアメタルに構成する方法はテクノロジープレビュー(TP) *6 ベアメタル上にWorkerノード無しでインストールした場合は、全てのPodはMaster上で展開される OCP4.2において、この構成方法はクラウドベースのクラスタでは使えない TP扱いなので本番環境で使用することは推奨しない ということで前置きが長くなってしまいましたが、3ノードベアメタルデプロイについて解説していきます。 *7 概要 OCP4.xのインストール要件では、Master Node × 3台、Worker Node × 2台が必要で、最小5ノードでのクラスタ構成となっています。3ノードベアメタルデプロイでは、Worker Node無しでMaster Nodeのみでクラスタを構成するのが特徴です。 3ノードベアメタルデプロイにおけるインストール要件や事前準備は通常のUPIインストールと同様です。 詳細は下記記事を参照ください。 rheb.hatenablog.com 今回の構成図はこのようになります。 [図2] 検証環境ネットワーク構成図 非常にシンプルです。構成上、Worker Nodeが無くなっただけで他に変化はありません。 インストール方法 めちゃくちゃ簡単です。通常のUPIインストール時には、Bootstrap、Master、Workerノードを起動しますが、今回は BootstrapとMasterノードを起動するだけ 、です。それ以外は通常通りUPIインストールを実行してください。 デフォルトでMasterノードはスケジュール対象になっていますので、インストールコンフィグファイルの修正も不要です。 下記が3ノードでクラスタが立ち上がった画面です。 [図3] 3ノードクラスタDashboard ノード一覧 # oc get nodes NAME STATUS ROLES AGE VERSION master-0 Ready master,worker 26m v1. 14 . 6 +6ac6aa4b0 master-1 Ready master,worker 26m v1. 14 . 6 +6ac6aa4b0 master-2 Ready master,worker 26m v1. 14 . 6 +6ac6aa4b0 以上で3ノードベアメタルデプロイのインストール完了です。 動作確認 あまりにも普通に動いているので特に確認する点も無いのですが、とりあえずOTAアップデートができるかどうか試してみました。 アップデートパス:v4.2.8→v4.2.9 [図4] OTAアップデート画面 Update now のアイコンが変わっているのに気付いた方はよくOCP4を触っている方です。もしくはOTAマニアです。 ポチッとUpdateを押して、しばらく待てば完了です。無事アップデートできました。 クラウド上のCluster Managerはこんな感じです。 [図5] OTA後、Cluster Manager画面 NodesはMaster 3台のみが認識されています。 まとめ ほとんど前置きで終わってしまった感がありますが、OCP4.2のクラスタをMasterノード3台で構成する方法の解説でした。 本来の目的は、今後出てくるであろうBaremetal IPIを見越した機能かと思われます *8 。 この機能の今後の動向は分かりませんが、うまく活用するとOCP4を試すのが少しは楽になるのではないでしょうか。 ドキュメント上は「ベアメタルデプロイ」となっていますが、パブリッククラウド上でもUPIの手順で構築可能かと思います(未確認)。開発/検証環境は3ノードにしてコストを下げる、といった使い方もできるかもしれません。 何れにせよ、試してみる場合はTPな点はお忘れなく… Let's get Big Ideas ! *1 : OpenShift Container Platform 4 / User Provisioned Infrastructure *2 : そろそろUPI芸人と呼ばれそう *3 : Baremetal IPIのデモは20:54辺りから、36:50辺りまで *4 : またいずれ検証して記事にしたいと思います *5 : https://github.com/openshift/openshift-docs/commit/783968b48d298ee4e8e5a6f1ea252be3acb1f372#diff-e21937773545bc689525f2a41cb1cab6 *6 : こちらを参照→「テクノロジープレビュー」機能とはどんな機能ですか? - Red Hat Customer Portal https://access.redhat.com/ja/solutions/387003 *7 : タイトルが「3ノードベアメタルデプロイ」となっているのはドキュメントに合わせたため。 *8 : デモ動画を見る限り、一旦Master Node 3台でクラスターが起動して、その後Worker Nodeが追加される
レッドハットでコンサルタントをしております織と申します。赤帽エンジニア Advent Calendar 2019の5日目のエントリーです (6日目の方が先に出ちゃいましたが)。 本記事では、「レッドハットが考えるNFV環境向けのチューニング」についてご紹介します。 前提 NFV環境向けのレッドハットの製品は、現時点ではOpenStackが相当します。レッドハットの考えるOpenStackを使ったNFV環境とはどういうものかというと、 KVMホスト(OpenStackコンピュートノード)上でSR-IOVもしくはOVS-DPDKを使い 仮想マシン上でDPDKのアプリをぶん回し ショートパケットの通信でなるべくパケットをドロップしない というユースケースを想定しています。 仮想マシン上で稼働するDPDKアプリがパケットを落とさないようにするためには、DPDKのPMD(Poll Mode Driver)にアサインしたVCPUが止まることなくパケットを転送し続ける必要があります。 「仮想マシンの中で止まることなくパケットを転送し続ける」のは意外と難しく、 仮想マシンのVCPUを処理するQEMUスレッドが、Linuxカーネルのスケジューリングによって別の物理CPUコアにマイグレートした 仮想マシンのVCPUがアサインされた物理CPUコアに対して、何か(カーネルスレッド等)が割り込んだ 等が原因となってパケ落ちが発生します。 RHOSPには「Network Functions Virtualization Planning and Configuration Guide」というドキュメント 1 があり、ここに記載された手順にしたがって環境を構築すると、a., b. のような事象が可能な限り発生しないようにカーネル等のパラメータをチューニングします。 ガイドに記載された内容は、TripleOに慣れていないと何をしているのかわかりにくいのですが、乱暴にまとめると カーネル起動パラメータに isolcpus=... を指定し 2 tunedのプロファイルで cpu-partitioning を使う という設定をコンピュートノードに対して施します。以下、この内容について少し掘り下げて見てみます。 isolcpus 通常Linuxカーネルは、CPUの負荷状況に応じて、プロセスを実行する物理コアを移動することがあります。 カーネル起動パラメータisolcpusでコア番号を指定すると、その物理コアにスケジュールされたプロセスは、CPUの負荷状況が変わっても別の物理コアに再スケジュールされなくなります。 もともとReal TIme処理用に実装されたisolcpusを指定することで、上記 a. の事象を回避します。 tunedのcpu-partitioningプロファイル 実はisolcpusだけだと、上記 b. の事象は回避できません。b. の事象を回避するために、様々な細かいチューニングをこっそりとやってくれるのがtunedのcpu-partitioningプロファイルです。 tuned は、設定したプロファイルに応じてRHELの各種パラメータを動的に変更してくれます。あらかじめ用意されているプロファイルは下記になります。 # tuned-adm list Available profiles: - balanced - General non-specialized tuned profile - cpu-partitioning - Optimize for CPU partitioning - desktop - Optimize for the desktop use-case - hpc-compute - Optimize for HPC compute workloads - latency-performance - Optimize for deterministic performance at the cost of increased power consumption - network-latency - Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance - network-throughput - Optimize for streaming network throughput, generally only necessary on older CPUs or 40G+ networks - powersave - Optimize for low power consumption - throughput-performance - Broadly applicable tuning that provides excellent performance across a variety of common server workloads - virtual-guest - Optimize for running inside a virtual guest - virtual-host - Optimize for running KVM guests Current active profile: virtual-guest cpu-partitioningプロファイルでどういう設定が適用されるかは、 /usr/lib/tuned/cpu-partitioning/tuned.conf を見ればわかります。 おおざっぱにまとめると、以下のようなチューニングを実施してくれます。 latency-performanceプロファイルの設定 を適用する (CPU Governerをperformanceにしたり、スケジューラや仮想メモリ周りのsysctlパラメータを設定したり) network-latencyプロファイルの設定 を適用する (Transparent Hugepageを禁止したり、NUMAバランシングを禁止したり、ネットワーク関連のsysctlパラメータをいくつか設定したり) sysctlで下記の設定をする (ハング判定のタイムアウトを延ばすとか(nosoftlockupしてますが)、NMIウォッチドッグの停止とか、仮想メモリの統計情報のアップデート間隔を延ばすとか、タイマー割り込みのスケジューリングとか) kernel.hung_task_timeout_secs = 600 kernel.nmi_watchdog = 0 vm.stat_interval = 10 kernel.timer_migration = 1 sysfsに対して下記の値を設定する (カーネルのworker threadのスケジューリング) /sys/devices/virtual/workqueue/cpumask に関しては、同じ設定をするようにdracutの pre-udev フックを仕込む (起動初期、カーネルモジュールの初期化等のworker threadのスケジューリング) /sys/bus/workqueue/devices/writeback/cpumask = <isolcpusで指定した以外のコア> /sys/devices/virtual/workqueue/cpumask = <isolcpusで指定した以外のコア> /sys/devices/system/machinecheck/machinecheck*/ignore_ce = 1 systemd( /etc/systemd/system.conf )に対して下記を設定する (systemdが起動するサービスのスケジューリング) CPUAffinity = <isolcpusで指定した以外のコア> /etc/sysconfig/irqbalance に下記を設定する (IRQ処理しないCPUコアの指定) IRQBALANCE_BANNED_CPUS=<isolcpusで指定したコア> /proc/sys/kernel/sched_domain/cpu*/*/flags のフラグをクリアする (スケジューラドメイン内でのロードバランスの禁止) カーネルモジュール kvm のパラメータを設定する ( kvmclock_periodic_sync=0 ) (kvmclockの同期をやめる) カーネルモジュール kvm_intel のパラメータを設定する ( ple_gap=0 ) (Pause Loop Exitingの制御) サービス ksm , ksmtuned を停止する (Kernel Samepage Mergingを使わない) /proc/irqに対してisolcpusで指定した以外のコアを設定する (IRQを処理するCPUコアの指定) /proc/irq/default_smp_affinity /proc/irq/*/smp_affinity qemu, dpdk/pmd以外の稼働中の全プロセスに対して、isolcpusで指定した以外のコアを設定するよう sched_setaffinity(2) を発行 カーネル起動パラメータに下記を追加してinitramfsを再作成 (Scheduling-clock割り込みやRCUコールバックのスケジューリングとかsoft lockupのログ禁止とか) nohz=on nohz_full=<isolcpusで指定したコア> rcu_nocbs=<isolcpusで指定したコア> tuned.non_isolcpus=<isolcpusで指定した以外のコア> intel_pstate=disable nosoftlockup 参考文献 関連情報が下記のKnowledge Baseに載っていますので、もし興味があればご参照ください。 3 How to configure CPU pinning without use of 'isolcpus' kernel cmdline parameter in Red Hat OpenStack Platform 現時点での最新バージョンのRHOSP15(upstreamのSteinに相当)だと こちら ↩ ガイドにしたがってTripleO Heat TemplateのHeat Environment Fileを記述すると、isolcpusの他にDPDKで必要なhugepageやSR-IOVで必要なIOMMUに関するカーネル起動パラメータも設定されますが、詳細は割愛します ↩ 実はこのKnowledge BaseからリンクされているBugzilla#1497182に、本エントリに記載したパラメータを設定するにいたった事情や議論が書かれていてとてもおもしろいのですが、このBugzillaエントリは残念ながら「中の人フラグ」がついたBugzillaアカウントじゃないと見ることができません…もしどうしても読みたい方はぜひレッドハットに入社してください! ↩
みなさんこんにちわ。ソリューションアーキテクト の荒木です。 赤帽エンジニア アドベントカレンダー6日目は、11月15日 に開催されました、Red Hat Forum Tokyo 2019 の特設企画"AKAsAKA Studio".『Red Hat の疑問に答えちゃいます』の続編になります。 コーナー概要 『Red Hat の疑問に答えちゃいます』のコーナーは、Red Hat 製品や会社、その他、なんでも質問を広く募って、その中から、番組進行サイドが独断と偏見で抽出し、絶対に正しいとは言えないレベルで(つまり、主観と直感で)答えるという企画でした。実際に、放送をご覧いただきました方は、楽しんでいただけたでしょうか。 今日は、番組で取り上げさせていただいたもの、お答えできなかったものを、改めてここに取り上げてみたいと思います。内容は、非テクニカルですので、お時間のある時に斜め読みいただければと思います。 尚、放送にて回答した内容とは異る事を書くかもしれませんが、ご容赦ください。また、質問は、ペンネームで頂き、こちらも掲載させていただきました。本記事に掲載するうえでは、敬称を略させていただいております。 RHEL愛 Q1 Q2 Red Hat Enterprise Linux は、私達にとって最も有名な製品です。 個人的には、Open Source Software として初めて使ったのが、Linux Magazine 等の雑誌付録のRed Hat Linux 6(つまり、無償版...)だったもので、その系譜で最新バージョンがリリースされたというのは、今年とても印象的な出来事でした。 また、Red Hat 製品の多くは、Red Hat Eneterprise Linux を前提に、QEが行われる為、OSが進化することで、他製品もその恩恵を得ることができることとなります。ですので、私個人としては、新しくなった事はたいへん喜ばしい事であると感じています。 尚、この質問に関しては、私が答えるよりも 森若の講演をご覧いただくのがより良いのではないかと思いますので、掲載いたします。 Red Hat 社名の由来 Q3 この質問、放送で取り上げさせていただいたのですが、私は恥ずかしながら知りませんでした、、、私がちゃんと覚えるという意味で、改めてここに、掲載させて頂こうと思います。 Red Hat got its name from founder Marc Ewing who wore a red Cornell University lacrosse hat, given to him by his grandfather, while attending Carnegie Mellon University. [1] ということで簡単に訳すと、 Red Hat という名前は、創業者のマーク ユーイングがカーネギメロン大学に在学中に、彼の祖父から贈られたコーネル大学の赤いラクロスハットをかぶっていた事に由来しているとのことです。なぜ、違う大学の帽子を被っていたのか。そして、Red Hat といえば、赤いフェドラハットですが、そのルーツは、ラクロスハットだった。ということで、謎の深みにハマってしまいそうな話題ではあります。が、ちゃんと説明できるエピソードがあってよかったです。 新製品には体当たり Q4 これも、放送で取り上げさせていただきましたが、Red Hat のエンジニア像をある面で、とても象徴していることじゃないかと思いますので、改めて掲載します。 尚、あくまで、『私の場合』として参考としていただけたら嬉しいです。 進める順番/優先順位 製品マニュアル コミュニティーのフォーラムや関連するBLOG 素振り トレーニングコンテンツ コードを眺める 私は主にクラウド製品を扱うプリセールスですが、Red Hat OpenStack Platform や、OpenShift Container Platform といった複数のソフトウェア、コンポーネントを組み合わせて構成されている製品については、まず、その大枠を掴むことから始めるようにしています。 とは言うものの、馴染みのないテクノロジーだと、ちょっとドキュメントを眺めてみたところでほとんど理解できません。結果、沢山の疑問やキーワードっぽい単語を得ることとなります。これらのよく分からない事をたよりに、コミュニティの情報をざっと収集します。 ちなみに、私の場合、ちょっと調べるとすぐに飽きてしまうので、嫌にならないうちに動かすことを試みます。クラウド製品やプラットフォーム製品は、まずは、構築しないと観察することもできませんので、「取り敢えずインストール&セットアップしてみよう」となるわけです。ほとんどの場合、この段階が最も大きな壁となるように思います。Red Hat Enterprise Linux を除いては、一発でインストール&セットアップできることは無いように思います。 尚、スムーズに出来無い理由は、様々ありますが、基本的には、マニュアルで想定している前提条件と、実験用に構築する環境の前提条件が異っている事が主な原因ではないかと思います。 時間の許される限り、動くまでひたすら何度も繰り返し、構築しては壊し、しては壊しする(この行為を一部社員の間で、素振りと呼んでいます。)そのうちに、知識の誤りの訂正と、不具合のワークアラウンド知識が貯っていきます。 急いでいたり、時間が確保できない場合は、社内のトレーニングコンテンツを使います。また、色々、試行錯誤した上で、やはり、思い通りに動かせない場合や、環境の制約で確認することが出来無い機能については、ソースコードを参照しています。 先にも述べましたとおり、ここに紹介したのは、あくまで私のやり方ですので、エンジニアによって実際は、様々、異るアプローチを取っているように思います。ただ、他のブログ記事からも想像できますが、共通してるのは、どのエンジニアも、なるべく早いタイミングで、実際に手を動かしてテクノロジーに触れているという点と、想定通りにいかない場合、積極的にソースコードを確認している点ではないかなと思います。また、もし、コードの不具合と断定できた場合は、コミュニティーにパッチの提案をしたりもしています。 こういった、全然特殊でもなんでもない取り組みを経て、時間の経過と共にドキュメントが充実してくるという点もあり、新しい製品やテクノロジーを使い倒せるようにしています。 おしまい...に... ということで、幾つかピックアップして、回答してみました。放送の時に宣言した、「好みのエディタアンケート」については、まだ着手できていないので、結果が出た頃に記事とさせていただこうと思います。 最後となりますが、事前の質問の投稿や、番組を現地やネット経由でご覧いただきました皆様、ありがとうございました。もし、こういった企画が楽しんでいただけるようでしたら、継続的に、情報発信の方法の一つとして、動画媒体も活用していきたいなと思いました。 それではまたの機会に〜 引用/注釈 [1] : 25 things you should know about Red Hat
皆さんこんにちは、ソリューションアーキテクトの岡野です。 私から今回は、Ansible Tower のオブジェクトの管理方法についてご紹介したいと思います。 そもそも、このような記事を書くきっかけとなったのは、お客様からしばしば頂く以下のような質問です。 ”複数の Ansible Tower 間でオブジェクトの移動って出来ますか?” 例えば検証環境と本番環境にそれぞれ Ansible Tower が存在し、検証環境でうまくいったら、そのオブジェクトをそのまま本番環境に移植したい。つまり、エクスポート、インポートのようなことを複数の Ansible Tower 間でやりたい、というリクエストです。 残念ながら現時点ではこのような機能は実装されていません。全てのオブジェクトの丸ごとコピーであればバックアップ・リストアで簡単に出来るのですが、特定のオブジェクトのみの移動は難しい、というのが現状でした。 ただ、面白い方法があります。ちょっと考え方を変えて、 "Ansible Tower のオブジェクト自体を Tower モジュールで管理する" というのが今回のブログの趣旨です。Ansible には3,000を超えるモジュールがあって、いろんな端末を操ることが出来ますが、Ansible Tower 自体をモジュールで操ることも可能です。 イメージとしてはこんな感じです。 Ansible Tower モジュール https://docs.ansible.com/ansible/latest/modules/list_of_web_infrastructure_modules.html#ansible-tower 別に難しいことはなく、”Ansible Tower のオブジェクト自体をモジュールで管理することが出来る” ということだけ理解していれば、あとはモジュールを使って書くだけです。♪ 例えば、ジョブテンプレート作成の場合は以下の様に書くことが出来ます。 --- - name: Manage Job Template hosts: localhost tasks: - name: Manage Job Template tower_job_template: name: Apache Install tower_username: admin tower_password: password job_type: run inventory: Workshop Inventory project: Ansible Workshop Examples playbook: apache_install.yml credential: Workshop Credentials state: present become_enabled: yes これで、Ansible Tower上にジョブテンプレートを作成することが出来ます。もちろん、stateを変数で定義し、作成・削除を管理することも可能です。ジョブテンプレート以外にも、認証情報、プロジェクト、インベントリなどなど、 Tower のオブジェクトに対応したモジュールもありますので、これらを使って Ansible Tower のオブジェクトを Ansible で、さらに言うと、Git 上で管理することが可能となります。Ansible Tower 3.6でサポートされた Webhook 機能を使えば、Git 上での Playbook の更新をトリガーに Tower 上のオブジェクトを自動で更新することも可能です。 ちなみに上記 Playbook 内で指定されている、Ansible Tower に関する認証情報ですが、 Ansible Tower の機能を使えば、登録された認証情報から "TOWER_USERNAME" 等の環境変数を使ってプレイブック内に自動で入力することが可能です(下記参照)。このあたりの認証情報の管理手法も Ansible Tower の有用な機能の一つです。 tower_username: "{{ lookup('env', 'TOWER_USERNAME') }}" tower_password: "{{ lookup('env', 'TOWER_PASSWORD') }}" tower_host: "{{ lookup('env', 'TOWER_HOST') }}" Gitを使って Ansible Tower のオブジェクト自体も管理をする。この場合、Ansible Tower は便利な機能をギュッと詰め込んだ、オペレーションを橋渡しするハブと考えることが出来ます。面白い仕組みだと思います。コードで全てを管理したい方、是非ご検討ください!
こんにちは、Red Hat の小杉です。 今回の投稿は各 Advent Calendar とは全く関係なく投稿いたします。 Change Data Capture Change Data Capture をご存知でしょうか。Wikipedia によると In databases, change data capture (CDC) is a set of software design patterns used to determine (and track) the data that has changed so that action can be taken using the changed data. とあります。データベースの変更データを使用して何かしらのアクションを実行するためのソフトウェアのデザインパターンのセットということです。 Red Hat Integration - Change Data Capture (Debezium) オープンソースの Change Data Capture のプロダクト Debezium があります。Red Hat Integration 2019-12 においてこの Debeizum を UpStream とする Change Data Capture が Tech Preview になりました。 access.redhat.com CDC を聞いたこともないという人のためにユースケースを交えてなるべくわかりやすく Tech Preview の内容を試してみました。 medium.com マイクロサービスを導入するに当たって巨大モノリス DB が壁になっているケースでは Data Virtualization が、マイクロサービス間のデータ連携には今回の CDC が欠かせない存在となると思います。組み合わせて使うとさらに効果が発揮できるソリューションとなっています。
この記事は Rookだらけの Advent Calendar 2019 5日目の記事です。 はいども。レッドハットでストレージを中心にクラウドインフラを生業にしているウツノミヤです。 CM空けてBパートです。ひとつのネタをAパートBパートに分けるあたりにウツノミヤの台所事情が垣間見えるRookアドカレ。引き続きみんな楽しんでね! qiita.com そして引き続きRookとOpenShift Japan CommunityのTwitterアカウント、 @japan_rook と @openshiftjp のフォローお願いしまーす! OBにPUTしてみる。 前回 OBCでBucketを作ってappにattachしたんで、実際にデータをPUTしてみましょう。 あ、appはナイスな記事を書かれている人を見つけたので、この記事の最後の方で使われてるappを使わせてもらってます。ていうかこの記事あれば俺の記事いらん…? examples-and-blogs/examples/rook-ceph-provisioner at master · yard-turkey/examples-and-blogs · GitHub まあいいや、so whatやね。ナナナナーナナーナー♪ このappを外部に公開してどんなappなのか見てみましょう。迷わず行けよ行けば分かるさ。 [utubo@tutsunom ceph]$ kubectl -n default expose pod photo1 --type=LoadBalancer --name=photo1 service/photo1 exposed [utubo@tutsunom ceph]$ kubectl -n default get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 100.64.0.1 <none> 443/TCP 40h photo1 LoadBalancer 100.71.221.229 xxxxd83c8e91d437b977cfc70d39006a-xxxx95985.us-east-1.elb.amazonaws.com 3000:32040/TCP 38m ほんでちょっと待って、ブラウザでexternal-IPの3000番にアクセスしてやると… すげーちゃんとしてる! じゃあせっかくなので記念に毎回表示してるアイキャッチの画像をうpしてみます。 のまえに、Cephのオブジェクトストアをチェックしておきます。 [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` radosgw-admin bucket list [ "hogehoge-11231ae6-cca4-4995-ab4c-41390b8b69f1" ] [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph df (省略) POOLS: POOL ID STORED OBJECTS USED %USED MAX AVAIL ssd-pool 1 0 B 0 0 B 0 15 GiB hdd-pool 2 0 B 0 0 B 0 34 GiB objstore.rgw.control 3 0 B 8 0 B 0 45 GiB objstore.rgw.meta 4 961 B 5 192 KiB 0 45 GiB objstore.rgw.log 5 128 B 180 96 KiB 0 45 GiB objstore.rgw.buckets.index 6 0 B 1 0 B 0 45 GiB objstore.rgw.buckets.non-ec 7 0 B 0 0 B 0 45 GiB .rgw.root 8 3.7 KiB 16 720 KiB 0 45 GiB objstore.rgw.buckets.data 9 0 B 0 0 B 0 45 GiB とりあえずBucketはあると。そしてPoolのObjectの数もチェックしておきます。 じゃあうpします。 d[〒] もう一回 ceph df の出力を見ると、 [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph df (省略) POOLS: POOL ID STORED OBJECTS USED %USED MAX AVAIL ssd-pool 1 0 B 0 0 B 0 15 GiB hdd-pool 2 0 B 0 0 B 0 34 GiB objstore.rgw.control 3 0 B 8 0 B 0 45 GiB objstore.rgw.meta 4 961 B 5 192 KiB 0 45 GiB objstore.rgw.log 5 128 B 180 96 KiB 0 45 GiB objstore.rgw.buckets.index 6 0 B 1 0 B 0 45 GiB objstore.rgw.buckets.non-ec 7 0 B 0 0 B 0 45 GiB .rgw.root 8 3.7 KiB 16 720 KiB 0 45 GiB objstore.rgw.buckets.data 9 572 KiB 1 1.7 MiB 0 45 GiB objstore.rgw.buckets.data にオブジェクトが一個増えてますね。無事PoolにPUTされたということがわかります。 ついでにもうひとつ、14MiBくらいのファイルもうpしてみると、 ceph df の出力はこうなりました。 [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph df (省略) POOLS: POOL ID STORED OBJECTS USED %USED MAX AVAIL ssd-pool 1 0 B 0 0 B 0 15 GiB hdd-pool 2 0 B 0 0 B 0 34 GiB objstore.rgw.control 3 0 B 8 0 B 0 45 GiB objstore.rgw.meta 4 961 B 5 192 KiB 0 45 GiB objstore.rgw.log 5 128 B 180 96 KiB 0 45 GiB objstore.rgw.buckets.index 6 0 B 1 0 B 0 45 GiB objstore.rgw.buckets.non-ec 7 0 B 0 0 B 0 45 GiB .rgw.root 8 3.7 KiB 16 720 KiB 0 45 GiB objstore.rgw.buckets.data 9 14 MiB 5 42 MiB 0.03 45 GiB OBJECTが1→5になって、4個増えました。RGWで置かれるデータはデフォルト4MiBでストライプされるので、14MiBのファイルは4つのオブジェクトにストライプされて置かれてるってことです。 OBCを削除する Bucketが使えることがわかったのでお片付けをしましょう。PVCと同じ様にOBCを削除します。 [utubo@tutsunom ceph]$ kubectl -n default delete obc objstore-obc-delete objectbucketclaim.objectbucket.io "objstore-obc-delete" deleted [utubo@tutsunom ceph]$ kubectl -n default get obc,ob No resources found in default namespace. 消えました。Cephクラスタの方でBucketを見てみると、 [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` radosgw-admin bucket list [] こちらも消えました。StorageClassの方で reclaimPolicy: Delete にしているので、OBCが消されるとBucketも連動して消されます。 蛇足かもしれんけど一応 reclaimPolicy: Retain のStorageClassを作ってOBC作って消して確認してみましょう。 [utubo@tutsunom ceph]$ cat my-sc-bkt-retain.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: ceph-bkt-retain provisioner: ceph.rook.io/bucket reclaimPolicy: Retain parameters: objectStoreName: objstore objectStoreNamespace: rook-ceph [utubo@tutsunom ceph]$ kubectl create -f my-sc-bkt-retain.yaml storageclass.storage.k8s.io/ceph-bkt-retain created [utubo@tutsunom ceph]$ cat my-obc-retain.yaml apiVersion: objectbucket.io/v1alpha1 kind: ObjectBucketClaim metadata: name: objstore-obc-retain namespace: default spec: generateBucketName: fugafuga storageClassName: ceph-bkt-retain [utubo@tutsunom ceph]$ kubectl create -f my-obc-retain.yaml objectbucketclaim.objectbucket.io/objstore-obc-retain created [utubo@tutsunom ceph]$ kubectl -n default get obc,ob NAME AGE objectbucketclaim.objectbucket.io/objstore-obc-retain 3m19s NAME AGE objectbucket.objectbucket.io/obc-default-objstore-obc-retain 3m18s [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` radosgw-admin bucket list [ "fugafuga-06f9f7d7-dcd4-4ce9-8d24-a412b39bb291" ] はいBucketできてますね。じゃあOBC消したらBuketは、 [utubo@tutsunom ceph]$ kubectl -n default delete obc objstore-obc-retain objectbucketclaim.objectbucket.io "objstore-obc-retain" deleted [utubo@tutsunom ceph]$ kubectl -n default get obc,ob No resources found in default namespace. [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` radosgw-admin bucket list [ "fugafuga-06f9f7d7-dcd4-4ce9-8d24-a412b39bb291" ] あるんだなあ。 まとめ というわけで2回にわたって今回はRook 1.1の新機能Object Bucket Claimについて超簡単にご紹介しました。実際やってみて、ほとんどPVCと同じ感じで違和感無く使えました。オブジェクトストレージってBucket作ったり消したりのライフサイクル管理がどうしてもストレージ側の運用になってくるので、Kubernetes側でアプリケーションと合わせる形で管理できるのは便利なんじゃないかなと思いました。 というわけで今日はここまで。
Red Hatのさいとうです。この記事は Ansible 2 Advent Calendar の12月4日のエントリです。 今回(12/4)と次回(12/25)の2回の予定で How to contribute - AWX と題して、Ansible TowerのアップストリームプロジェクトであるAWXにコードをコントリビュートする方法について、開発環境の構築、アップストリームプロジェクトのfork、開発用コンテナのビルド、コードの修正とテストの一連の流れを紹介します。 AWXの開発環境の準備する ここでは、CentOS 7(Minimal Install)の上に開発環境を準備していきます。まずは開発環境に必要なパッケージ群をインストールしましょう。 ※AWXの開発環境では、docker-composeを利用してdockerコンテナとしてAWXサービスをビルド・起動させます。このビルド・起動の部分が、RHEL8/CentOS 8のpodmanにはまだ対応していない状況です(みんなRHEL8/CentOS 8の上でどうやってAWXの開発してるんだろう...ちなみに僕はFedora31 & moby-engineを開発環境として利用しています)。 $ sudo yum install epel-release $ sudo yum install git make python36 $ sudo yum install docker $ sudo systemctl enable docker $ sudo systemctl start docker 今回は、あくまでも外部から隔絶されたローカルな開発環境を構築するのを目的としているため、利便性を優先して、以下の設定を実施しておきます。みなさんの環境の /var/run/docker.sock は Owner:Group を root:docker にして、dockerグループに開発に利用しているユーザIDを所属させるなどの適切な設定を行ってください。 $ sudo ls -l /var/run/docker.sock srw-rw----. 1 root root 0 Dec 4 19:11 /var/run/docker.sock $ sudo chmod 666 /var/run/docker.sock 続いて、AWXのダッシュボードをビルドするために必要なNodeJS(13.x)をインストールします。 $ curl -sL https://rpm.nodesource.com/setup_13.x | sudo bash - $ sudo yum install nodejs 最後に、AWXコンテナのビルドに必要なdocker-composeをインストールします。 $ pip3 install docker-compose --user UpstreamからAWXリポジトリをforkしてクローンする Upstreamの AWXプロジェクトのリポジトリ を、みなさんのGithubアカウントでforkします。そして、そのforkしたプロジェクトを git clone してください。 $ git clone git@github.com:<みなさんのアカウント>/awx.git つぎに、Upstreamのリポジトリをリモートリポジトリとして登録します(ここではupstreamという名前で登録しています)。ついでにforkいたリポジトリをUpstreamに追随(fetch & merge)させておきます。※このあたりのGitオペレーションは、みなさんの流儀にあわせて行ってください。 $ cd awx $ git remote add upstream https://github.com/ansible/awx.git $ git remote -v origin git@github.com:<みなさんのアカウント>/awx.git (fetch) origin git@github.com:<みなさんのアカウント>/awx.git (push) upstream https://github.com/ansible/awx.git (fetch) upstream https://github.com/ansible/awx.git (push) $ git fetch upstream $ git merge upstream/devel これで、ソースコードの準備もおわりました。 トピックブランチを切って開発する UpstreamへのPull Requestを行うために、トピックブランチを切って作業します。僕は、すでにオープンされているIssueに対する修正を投げるときには Issue/NNNNN というトピックブランチを切るようにしています。いきなりPull Requestを投げるときには PR/それっぽい名前 という感じでブランチ名をつけていますが、ひとそれぞれですから、みなさんの都合でわかりやすい名前を 英語 でつけてください。 $ git checkout -b PR/adc20191204 以降は、AWXの Contributing guide にしたがって進めます。以降の作業で注意しなければならないのは、開発環境上(ここでは[host])で実施する作業と、その環境上に起動させた開発用のコンテナ上(ここでは[container])で作業する必要がある点です。 ※以降の作業例では開発環境で行う作業には [host] 、開発用のコンテナ上で行う作業には [container] と明記しておきます。 1. サンプルの local_settings.py をコピーします サンプルから変更する箇所は特にありません。そのままコピーして利用しましょう。 [host]$ cp awx/settings/local_settings.py.docker_compose awx/settings/local_settings.py 2. AWXコンテナをビルドします Contributing guideでは指定されていませんが、トピックブランチで作業している関係で、ビルド時に COMPOSE_TAG=devel を設定してやる必要があります。ビルドには、それなりに時間がかかります。ビールでも飲みつつまちましょう。 [host]$ COMPOSE_TAG=devel make docker-compose-build ビルドが完了すると、以下のようなイメージ群が開発環境に存在しているはずです。 [host]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ansible/awx_devel latest ef0d34a9281d 10 seconds ago 2.45 GB gcr.io/ansible-tower-engineering/awx_devel devel ef0d34a9281d 10 seconds ago 2.45 GB docker.io/centos 8 0f3e07c0138f 2 months ago 220 MB 3. UIをビルドします 公式のドキュメントにしたがうと、多国語対応していない状態(英語のみ)でAWXのダッシュボードが起動してきます。日本語のUIの不具合は、われわれが修正するしかないですよね?そこで、多国語対応した状態でUIをビルドします。 [host]$ make clean [host]$ make languages [host]$ make ui-devel AWXのダッシュボードのフロントエンド(JavaScript)部分に手を加えた場合は、上記の作業を修正の都度実施する必要があります。 4. AWXコンテナを起動します 以下のコマンドで開発用のコンテナが起動してきます。ログイン画面のアイコンは開発者だけが見ることのできる Angry Potato です :) [host]$ COMPOSE_TAG=devel make docker-compose コンテナの初期化と起動には、それなりの時間がかかります。DBのマイグレーション処理など、一連の初期化処理が完了すると、HTTPSで開発環境のPort:8043にアクセスすることで、AWXのダッシュボードにログインできます。 AWX Login 以降、ターミナルはAWXコンテナからの出力用に保持して、別のターミナルから開発環境にSSHして以降の作業を進めます。 5. 仕上げとして初期データを投入する まずはAWXの管理者(awx)ユーザを作成します。作業後は、このawxアカウントでダッシュボードにログインすることができるようになります。 [host]$ docker exec -it tools_awx_1 bash [container]$ awx-manage createsuperuser Username (leave blank to use 'awx'): awx Email address: Password: <パスワード> Password (again): <パスワード再入力> Superuser created successfully. 続いて、Demo用のProject/Inventory/Credential/Job Templateを作成して終了です。 [container]$ awx-manage create_preload_data (changed: True) [container]$ exit 実際にブラウザからawxユーザでログインしてみてください。 AWX Dashboard 次回は、実際にコードに変更を加えてテストする手順について紹介する予定です。さて、今日はもう帰るとするかな。
この記事は Rookだらけの Advent Calendar 2019 4日目の記事です。 ども。レッドハットでストレージを中心にクラウドインフラを生業にしているウツノミヤです。 さあイッテンヨンまで後1ヶ月になりました。1.5のIWGPヘビーの方が格上なのにどうも1.4のIWGP ICの方が盛り上がってるんですよね…リマッチなのに。このあたりいつも饒舌な内藤選手が何も喋らないことでうまくメディアと世論をコントロールしてる感じがします。あとみんな二冠二冠と言い出したから、IWGPヘビー級のベルトが二冠のための手段に扱われてちょっと相場が下がってる感があるのも否めないかな。まあこれは個人的なアレですけど。 まあプロレスの話はこれくらいにしといて、Rookアドカレ、見ていただいていますか? 今年はじめてアドカレやってますが、もう4日目にしてヒイヒイ言っとるウツノミヤです。全部俺系の人は引き出しがヤバいですね。ほんとに敬服します。 qiita.com 引き続き、RookとOpenShift Japan CommunityのTwitterアカウント、 @japan_rook と @openshiftjp のフォローお願いしまーす! Ceph RADOS Gateway (RGW) はい、てなわけで今回はRook-Cephをオブジェクトストレージで使う方法を紹介します。 Cephは本質的にRADOSがオブジェクトストアなわけで、当然オブジェクトストレージとして使えます。libradosというライブラリを使うとRADOSに直接オブジェクトをPUT/GETできるんだけど、いやー無理っすな感じですよね。だってオブジェクトストレージはS3 (Compatible) APIがもうデファクトなんだから。 なのでCephにはRADOS Gateway(RGW)というクライアントからRADOSへS3でアクセスするためのインターフェースとなるコンポーネントがあります。 左のオレンジ色のやつがRGW それではこいつをRook-Cephでデプロイしてみましょう。KubernetesとRook-Cephの環境はざっくり次のとおりですが、詳細は こちらの12/1と12/3の記事 をご覧ください。 [utubo@tutsunom ceph]$ kubectl get node --sort-by=".metadata.creationTimestamp" NAME STATUS ROLES AGE VERSION ip-172-20-91-64.ec2.internal Ready master 25h v1.15.5 ip-172-20-53-29.ec2.internal Ready master 25h v1.15.5 ip-172-20-113-40.ec2.internal Ready master 25h v1.15.5 ip-172-20-93-28.ec2.internal Ready node 25h v1.15.5 ip-172-20-42-193.ec2.internal Ready node 25h v1.15.5 ip-172-20-102-19.ec2.internal Ready node 25h v1.15.5 [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod NAME READY STATUS RESTARTS AGE csi-cephfsplugin-6sn69 3/3 Running 0 4h56m csi-cephfsplugin-d78h4 3/3 Running 0 4h56m csi-cephfsplugin-kx28b 3/3 Running 0 4h56m csi-cephfsplugin-provisioner-974b566d9-6xkcj 4/4 Running 0 4h56m csi-cephfsplugin-provisioner-974b566d9-bms5q 4/4 Running 0 4h56m csi-rbdplugin-6mnjk 3/3 Running 0 4h56m csi-rbdplugin-kmn5b 3/3 Running 0 4h56m csi-rbdplugin-provisioner-579c546f5-69xh8 5/5 Running 0 4h56m csi-rbdplugin-provisioner-579c546f5-hsdbm 5/5 Running 0 4h56m csi-rbdplugin-tgbd9 3/3 Running 0 4h56m rook-ceph-mgr-a-d45bf555f-fwjbv 1/1 Running 0 4h55m rook-ceph-mon-a-5f94d65bd-fzk8s 1/1 Running 0 4h56m rook-ceph-mon-b-5d9dd8778b-chmhw 1/1 Running 0 4h56m rook-ceph-mon-c-d8bd74999-2tm6x 1/1 Running 0 4h55m rook-ceph-operator-fb8b96548-pg6zv 1/1 Running 0 4h57m rook-ceph-osd-0-5fd45cb745-qcp2d 1/1 Running 0 4h51m rook-ceph-osd-1-7d54784df7-nzd7t 1/1 Running 0 4h50m rook-ceph-osd-10-56f589bc-pl7md 1/1 Running 0 4h50m rook-ceph-osd-11-58cc586588-ffxhk 1/1 Running 0 4h50m rook-ceph-osd-2-868497f7fd-knxjh 1/1 Running 0 4h50m rook-ceph-osd-3-78dc45bcdb-45f84 1/1 Running 0 4h51m rook-ceph-osd-4-577cb784b8-5tzcp 1/1 Running 0 4h50m rook-ceph-osd-5-6b6587fb8c-m5x6f 1/1 Running 0 4h50m rook-ceph-osd-6-65c76d4696-m5rgw 1/1 Running 0 4h51m rook-ceph-osd-7-5f5db64776-68qfk 1/1 Running 0 4h50m rook-ceph-osd-8-5c7fc696c-9gdrg 1/1 Running 0 4h50m rook-ceph-osd-9-b4c5b567d-ws8c6 1/1 Running 0 4h51m rook-ceph-osd-prepare-ip-172-20-102-19.ec2.internal-pxqh2 0/1 Completed 0 4h55m rook-ceph-osd-prepare-ip-172-20-42-193.ec2.internal-n9jgc 0/1 Completed 0 4h55m rook-ceph-osd-prepare-ip-172-20-93-28.ec2.internal-4rl68 0/1 Completed 0 4h55m rook-ceph-tools-5bc668d889-2jd6c 1/1 Running 0 4h48m rook-discover-4955r 1/1 Running 0 4h57m rook-discover-bddtk 1/1 Running 0 4h57m rook-discover-w5ljg 1/1 Running 0 4h57m [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd df tree ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS TYPE NAME -1 0.17569 - 180 GiB 24 GiB 11 MiB 0 B 12 GiB 156 GiB 13.34 1.00 - root default -5 0.17569 - 180 GiB 24 GiB 11 MiB 0 B 12 GiB 156 GiB 13.34 1.00 - region us-east-1 -4 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - zone us-east-1a -3 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - host ip-172-20-42-193-ec2-internal 0 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.0 6 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.6 3 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.3 9 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.9 -14 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - zone us-east-1b -13 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - host ip-172-20-93-28-ec2-internal 2 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.2 8 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.8 5 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.5 11 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.11 -10 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - zone us-east-1c -9 0.05856 - 60 GiB 8.0 GiB 3.8 MiB 0 B 4 GiB 52 GiB 13.34 1.00 - host ip-172-20-102-19-ec2-internal 1 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.1 7 hdd 0.01949 1.00000 20 GiB 2.0 GiB 960 KiB 0 B 1 GiB 18 GiB 10.00 0.75 0 up osd.7 4 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.4 10 ssd 0.00980 1.00000 10 GiB 2.0 GiB 960 KiB 0 B 1 GiB 8.0 GiB 20.01 1.50 0 up osd.10 TOTAL 180 GiB 24 GiB 11 MiB 0 B 12 GiB 156 GiB 13.34 MIN/MAX VAR: 0.75/1.50 STDDEV: 5.27 下みたいなyamlでRGWとオブジェクトストレージ用のPoolが作られます。 apiVersion: ceph.rook.io/v1 kind: CephObjectStore metadata: name: objstore namespace: rook-ceph spec: metadataPool: failureDomain: host replicated: size: 3 dataPool: failureDomain: host replicated: size: 3 gateway: type: s3 sslCertificateRef: port: 80 securePort: instances: 2 placement: annotations: resources: [utubo@tutsunom ceph]$ kubectl create -f my-object.yaml cephobjectstore.ceph.rook.io/objstore created [utubo@tutsunom ceph]$ kubectl get pod -l app=rook-ceph-rgw NAME READY STATUS RESTARTS AGE rook-ceph-rgw-objstore-a-65cf59bbb7-6r8f8 1/1 Running 0 10s rook-ceph-rgw-objstore-b-754d5f7cbd-v6mdk 1/1 Running 0 10s rook-ceph-rgw-<objectstore name>〜 というのが生まれました。これがRGWです。 RGWは1つでももちろん動くんですが、SPOF回避のためとに複数置くのがCephでのベストプラクティスです。またRGWがパフォーマンスボトルネックになることを回避するため複数で負荷分散する意味もあるので、Rook-Cephでも複数あったほうがいいんじゃないでしょうか奥さん、てことで2つ置きました。 次にPoolを見てみましょう。 [utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph df RAW STORAGE: CLASS SIZE AVAIL USED RAW USED %RAW USED hdd 120 GiB 108 GiB 6.0 GiB 12 GiB 10.01 ssd 60 GiB 48 GiB 6.0 GiB 12 GiB 20.02 TOTAL 180 GiB 156 GiB 12 GiB 24 GiB 13.35 POOLS: POOL ID STORED OBJECTS USED %USED MAX AVAIL ssd-pool 1 0 B 0 0 B 0 15 GiB hdd-pool 2 0 B 0 0 B 0 34 GiB objstore.rgw.control 3 0 B 8 0 B 0 45 GiB objstore.rgw.meta 4 0 B 0 0 B 0 45 GiB objstore.rgw.log 5 50 B 178 48 KiB 0 45 GiB objstore.rgw.buckets.index 6 0 B 0 0 B 0 45 GiB objstore.rgw.buckets.non-ec 7 0 B 0 0 B 0 45 GiB .rgw.root 8 3.7 KiB 16 720 KiB 0 45 GiB objstore.rgw.buckets.data 9 0 B 0 0 B 0 45 GiB おお、なにやらObject Store用にたくさんPoolが切られています。名前を見たら何にどんなデータが入るか何となく想像付くきますが、割とバシバシPoolが切られますね。 とにもかくにも、RGWもできたしPoolもできたのでオブジェクトストレージとしての基盤は完成です。 Object Bucket / Object Bucket Claim Rook 1.1からObject Bucket(OB) / Object Bucket Claim(OBC)というリソースが使えるようになりました。これはPV/PVCと同じようにapp側のリクエストによって動的にオブジェクトストレージのBucketを提供するものです。 これを使ってPoolにBucketを作り、appから使ってみます。 PVと同じようにOBC用のStorageClassを作ります。 [utubo@tutsunom ceph]$ cat my-sc-bkt-delete.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: ceph-bkt-delete provisioner: ceph.rook.io/bucket # set the reclaim policy to delete the bucket and all objects # when its OBC is deleted. reclaimPolicy: Delete parameters: objectStoreName: objstore objectStoreNamespace: rook-ceph [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl create -f my-sc-bkt-delete.yaml storageclass.storage.k8s.io/ceph-bkt-delete created [utubo@tutsunom ceph]$ kubectl -n default get sc NAME PROVISIONER AGE ceph-bkt-delete ceph.rook.io/bucket 5s default kubernetes.io/aws-ebs 30h gp2 (default) kubernetes.io/aws-ebs 30h できました。 objectStoreName には先に作ったオブジェクトストア( CephObjectStore リソース)の名前を入れます。 次はOBCです。PVCと同じように、こんな感じです。 [utubo@tutsunom ceph]$ cat my-obc-delete.yaml apiVersion: objectbucket.io/v1alpha1 kind: ObjectBucketClaim metadata: name: objstore-obc-delete namespace: default spec: #bucketName: generateBucketName: hogehoge storageClassName: ceph-bkt-delete [utubo@tutsunom ceph]$ kubectl create -f my-obc-delete.yaml objectbucketclaim.objectbucket.io/my-store-obc-delete created [utubo@tutsunom ceph]$ kubectl -n default get obc,ob NAME AGE objectbucketclaim.objectbucket.io/objstore-obc-delete 54s NAME AGE objectbucket.objectbucket.io/obc-default-objstore-obc-delete 54s [utubo@tutsunom ceph]$ kubectl -n default describe obc/objstore-obc-delete Name: objstore-obc-delete Namespace: default Labels: <none> Annotations: <none> API Version: objectbucket.io/v1alpha1 Kind: ObjectBucketClaim Metadata: Creation Timestamp: 2019-12-03T08:05:02Z Generation: 2 Resource Version: 446423 Self Link: /apis/objectbucket.io/v1alpha1/namespaces/default/objectbucketclaims/objstore-obc-delete UID: 25f8a119-5358-41c4-81c5-13ad833f11d9 Spec: Object Bucket Name: obc-default-objstore-obc-delete Additional Config: <nil> Bucket Name: hogehoge-11231ae6-cca4-4995-ab4c-41390b8b69f1 Canned Bucket Acl: Generate Bucket Name: hogehoge Ssl: false Storage Class Name: ceph-bkt-delete Versioned: false Status: Phase: bound Events: <none> [utubo@tutsunom ceph]$ kubectl -n default describe ob/obc-default-objstore-obc-delete Name: obc-default-objstore-obc-delete Namespace: Labels: <none> Annotations: <none> API Version: objectbucket.io/v1alpha1 Kind: ObjectBucket Metadata: Creation Timestamp: 2019-12-03T08:05:03Z Finalizers: objectbucket.io/finalizer Generation: 1 Resource Version: 446421 Self Link: /apis/objectbucket.io/v1alpha1/objectbuckets/obc-default-objstore-obc-delete UID: 6bfaf276-596a-43f1-b89f-72f4e3f064d5 Spec: Additional State: Ceph User: ceph-user-1hIDnfeK Claim Ref: API Version: objectbucket.io/v1alpha1 Kind: ObjectBucketClaim Name: objstore-obc-delete Namespace: default UID: 25f8a119-5358-41c4-81c5-13ad833f11d9 Endpoint: Additional Config: <nil> Bucket Host: rook-ceph-rgw-objstore.rook-ceph Bucket Name: hogehoge-11231ae6-cca4-4995-ab4c-41390b8b69f1 Bucket Port: 80 Region: Ssl: false Sub Region: Reclaim Policy: Delete Storage Class Name: ceph-bkt-delete Status: Conditions: Phase: bound Events: <none> ほいできた。 Bucketが作られたのですが、ここでSecretを見てみます。 [utubo@tutsunom ceph]$ kubectl -n default get secret NAME TYPE DATA AGE default-token-zd6jt kubernetes.io/service-account-token 3 34h objstore-obc-delete Opaque 2 5m なんかありますね。そうです、OBと共にS3のAccessKeyとSecretKeyができているのです。これがappの方で出てきます。 [utubo@tutsunom ceph]$ kubectl -n default get secret objstore-obc-delete -o jsonpath="{['data']['AWS_ACCESS_KEY_ID']}" | base64 --decode && echo NKI5QC8N7FDC93PKU1KP [utubo@tutsunom ceph]$ kubectl -n default get secret objstore-obc-delete -o jsonpath="{['data']['AWS_SECRET_ACCESS_KEY']}" | base64 --decode && echo 67pnuGUJSMTTtMnF5BSbP9xXmQ8A8nfgaG5FHvOB OBCのyamlにある generateBucketName は、作られるBucketに振られる名前のプリフィクスとなります。 kubectl describe の出力にあるBucket Nameから確認できます。 Bucket Nameはyamlの中で bucketName として固定の名前を振ることができます。が、Bucket名がカブる可能性があるので固定の名前を振るのはあんまりお勧めじゃないっぽい。 generateBucketName を使うとプリフィクス以降はほぼカブらないように自動で振られるのでGood。 「そんな長ったらしいBucket Name覚えるのめんどくせーーーーーーーーーーよ」ですが、Bucket Nameなんて覚えなくていいです。実際にappにOBをattachしてみましょう。 なんか画像をアップロードしたり表示したりするようないい感じのappを見つけたので、そのままパクらせてもらいます。 [utubo@tutsunom ceph]$ cat my-obc-app.yaml apiVersion: v1 kind: Pod metadata: name: photo1 labels: name: photo1 spec: containers: - name: photo1 image: docker.io/screeley44/photo-gallery:latest imagePullPolicy: Always envFrom: - configMapRef: name: objstore-obc-delete - secretRef: name: objstore-obc-delete ports: - containerPort: 3000 protocol: TCP [utubo@tutsunom ceph]$ kubectl -n default create -f my-obc-app.yaml pod/photo1 created うむ。これで podに入ってenvを見てみましょう。 [utubo@tutsunom ceph]$ kubectl -n default exec -it photo1 bash root@photo1:/usr/src/app# env | grep -e AWS -e BUCKET BUCKET_SUBREGION= BUCKET_HOST=rook-ceph-rgw-objstore.rook-ceph BUCKET_NAME=hogehoge-11231ae6-cca4-4995-ab4c-41390b8b69f1 BUCKET_PORT=80 AWS_SECRET_ACCESS_KEY=67pnuGUJSMTTtMnF5BSbP9xXmQ8A8nfgaG5FHvOB BUCKET_REGION= BUCKET_SSL=false AWS_ACCESS_KEY_ID=NKI5QC8N7FDC93PKU1KP ほら、OBCのBucketとSecretがenvに書かれてるでしょう。 envFrom の configMapRef と secretRef で指定したOBCからそれぞれの情報が環境変数で取り入れられています。 だからappの中では環境変数で指定すりゃいいので、覚える必要はないっす。 まとめ 今回はRook 1.1の新機能Object Bucket ClaimでBucket作るところまで紹介しました。次回はこれをappから使った時の振る舞いを見てみましょう。 というわけで今日はここまで。
Red Hatの森若です。 この記事は 赤帽エンジニア Advent Calendar 2019 の3日目です。 今回は Red Hat Tech Night でボツにした方のネタ、flatpakについて書きます。 スライドはこちら。 Linuxデスクトップ元年……? 今年は令和元年ですが、Linux界隈で"元年"と言えば"Linuxデスクトップ元年"と言いたいところです。「今年こそLinuxデスクトップ元年」って何回聞いたかよくわかりません。 私がこの記事を書くのに使っているのもFedora 31です。Linuxデスクトップ環境そのものはごく順調に進化しているのですが残念ながら普及しているとは言いにくいです。 デスクトップ環境としてのRHEL Red Hat Enterprise Linuxは言わずと知れたRed Hatの看板製品で、従来デスクトップ用途もカバーしていたのですが、ついに今年RHELのデスクトップ向け型番はなくなってしまいました……(workstationは引き続き提供しています)。 RHEL 8に含まれるデスクトップ向けソフトウェアも少なく、RHELで提供される範囲だけでデスクトップ環境として利用するのは無理があります。たとえば H.264のcodecはfirefoxにはciscoから提供されたライブラリがついてくるものの、一般的なビデオ再生などで使えません。端的に言ってしまうと「Youtubeは再生できるけどローカルのビデオは再生できない」というようなつらい状況です。 RHEL 8にデスクトップアプリケーションを追加する Red Hat ForumのRHEL8ブースで動画を流したかったのですが、上述のようにcodecもないですし、関連ライブラリとVLCをインストールしたくなりました。 RHELに含まれないデスクトップアプリケーションを追加でインストールしようというときに、いくつか考えられる手法があります。 自前でビルドする: 昔ながらの方法です。RHELのパッケージ管理とうまく連携させるのが難しいのでできれば避けたいところです。 EPELやrpmfusionなどでRHEL8むけのパッケージを入れる: よく使われる手法です。将来の更新時に依存関係の競合が起きると面倒です。 snapd: コンテナのようにホストのシステムと独立して、依存関係込みでアプリケーションを導入します。 パッケージと競合しない点が便利です。EPELに含まれています。 flatpak: コンテナのようにホストのシステムと独立して、依存関係込みでアプリケーションを導入します。パッケージと競合しない点が便利です。 RHELに含まれています。 flatpakはRHEL8にも含まれています。今回はこれを使ってみましょう。 flatpakとは? flatpakは、アプリケーションを配布するための技術で、OSTreeを基盤としています。アプリケーションのバイナリとバイナリが依存するライブラリをまとめて配布し、ディストリビューションによりません(厳密にはkernelの違いなどで影響があります)。さらにデスクトップ環境との統合が行われています。アプリケーションが基盤とする環境(ランタイム)として、freedesktop.org, GNOME, KDE(Qt)の3種類のランタイムが標準的に提供されています。 OSTreeとは? OSTree は、一般的なディレクトリツリーをgitのようにバージョン管理します。gitにもバイナリファイルを配置できますが、OSTreeでは起動可能なシステムのディレクトリツリーを扱う事を考慮しています。具体的な特徴としては以下のようなものがあります。 複数バージョンを同時にチェックアウトできる 容量を節約するためハードリンクを行い同一内容のファイルは実体を1つしか持たない リポジトリのミラーを行う際に最近N世代だけのミラーを実施できる OSTreeは、CoreOS(旧 Atomic Host)でのOSアップデートの基盤技術として利用されています。同じ技術をアプリケーション配布用に使うのがflatpakです。 flathub.org flatpak形式でアプリケーションを配布しているサイトとして有名なのは flathub.org です。利用のための設定は非常に簡単で、RHELのデスクトップ環境が既に手元にあるならflatpakパッケージを入れたあと、flathub.orgで3クリックして設定パッケージを導入することで完了します。 念のため、flathubで配布されているそれぞれのソフトウェアにたいして、Red Hatからのサポートは特にありません。これは配布手法に何を使っても同じですので気にせず進みます……。 以下はGNOME SoftwareでAudio & Video カテゴリのアプリケーションを見たときのスクリーンショットです。RHEL 8そのままだと存在しなかったVLCのアイコンが画面下のほうに登場しています。 まとめ RHELをデスクトップ用途に使うのはしんどいのですが、もし使う必要がでてきたらflatpakとflathubを思いだすと少し楽ができます。背景の技術であるOSTreeも面白いので探ってみてください :) おまけ 個人的にはOSTree推しですが、実は最新のflatpakではOSTreeだけではなくコンテナイメージで標準的に使われるOCI形式も扱えたりします。
皆様こんにちは、今年も残すところあと1か月となってしまいましたが、いかがお過ごしでしょうか。 Red Hatでテクニカルサポートエンジニアをしている八木澤(ひよこ大佐)です。 Ansible Towerを使っていると、「新しくリリースされたAnsible Engineを検証したいけど、今の環境を置き換えたくない」「一部のPlaybookで互換性の問題があるので、古いバージョンのAnsible Engine」を使いたいというケースが出てくるかと思います。今回の赤帽エンジニアブログでは、Ansible Towerの「 カスタムvirtualenv(venv) 」機能について解説します。 virtualenvとは? virtualenv とは、システムと分離された仮想のPython環境を作成できるソフトウェアです。例えば、検証のためにPython環境を用意しなければならないようなケースで、システムのデフォルトの環境に影響を与えずに新しいPython環境を作成することができます。 Ansible Towerでは、デフォルトでvirtualenvの仕組みを利用しており、「/var/lib/awx/venv」というパスの下に、「ansible」「awx」という2つのvirtualenvがはじめから存在します。これらの中には、Ansible Towerがジョブを実行する際に利用するPythonパッケージなどがあらかじめインストールされています。 カスタムvirtualenvで何ができるのか Ansible Tower 3.3以降では、virtualenvを自分で作成し、Playbookの実行に必要なPythonパッケージやAnsible Engineをインストールすることができます。これを「カスタムvirtualenv」と呼んでいます。 カスタムvirtualenvを作成すると、前述したように古いバージョンのAnsible Engineを共存させたり、デフォルトの環境に影響を与えることなく、特定のジョブテンプレートのみ指定したvirtualenv内のパッケージを使用するよう指定できたりします。新しいAnsible Engineと互換性のないPlaybookなどをAnsible Towerで利用する必要があるケースでは、既存の環境に影響を与えないため非常に便利です。また、作成したvirtualenvは単一のディレクトリとして存在するため、手軽にバックアップを取得したり、削除したりすることができます。 カスタムvirtualenvを作成する ※本記事では、本稿執筆時点(2019年12月2日)の最新版であるAnsible Tower 3.6.1での手順を前提に解説します。その他のバージョンをお使いの場合は、該当するバージョンのAnsible Towerドキュメントを参照してください。また、本稿で紹介しているデフォルトのvirtualenvのパス(/var/lib/awx/venv/)以外にカスタムvirtualenvを作成する方法は、Ansible Tower 3.5.0以降でのみ利用可能です。 公式ドキュメントに詳細な手順が紹介されていますので、そちらも併せてご確認ください。 https://docs.ansible.com/ansible-tower/3.6.1/html/upgrade-migration-guide/virtualenv.html 1. カスタムvirtualenv用のディレクトリを作成する(オプション) デフォルトのvirtualenvのパス(/var/lib/awx/venv/)直下以外でカスタムvirutalenvを作成する場合、まずカスタムvirtualenv用のディレクトリを作成し、適切なパーミッション(0755)を指定します。 デフォルトのvirtualenvのパスに作成する場合は、この手順は不要です。 # mkdir /opt/my-envs $ chmod 0755 /opt/my-envs また、作成したディレクトリへのパスを通す必要がありますので、Tower UIの「設定」から「システム」を開き、「CUSTOM VIRTUAL ENVIRONMENT PATHS」にカスタムvirtualenvを作成したディレクトリのパスを追記します。 2. カスタムvirtualenvを作成する 以下のコマンドを実行しカスタムvirtualenvを作成します。 # virtualenv /opt/my-envs/custom-venv New python executable in /opt/my-envs/custom-env/bin/python Installing setuptools, pip, wheel...done. Python3用のカスタムvenvを作成する場合は、以下のコマンドを実行します。Python 2とPython 3で手順が異なりますので注意してください。 # python3 -m venv /opt/my-envs/custom-venv カスタムvirtualenvを作成すると、指定したパスに以下のようなディレクトリが生成されます。 # ls -la /opt/my-envs/custom-env/ total 0 drwxr-xr-x 5 root root 56 Dec 2 10:25 . drwxr-xr-x 3 root root 24 Dec 2 10:25 .. drwxr-xr-x 2 root root 248 Dec 2 10:25 bin drwxr-xr-x 2 root root 23 Dec 2 10:25 include drwxr-xr-x 3 root root 23 Dec 2 10:25 lib lrwxrwxrwx 1 root root 3 Dec 2 10:25 lib64 -> lib 3. カスタムvirtualenvを有効化する では、早速作成したカスタムvirtualenvを有効化してみましょう。「bin」ディレクトリ内の activate という実行ファイルを指定することでカスタムvirtualenvを有効化できます。 # source /opt/my-envs/custom-venv/bin/activate コマンドを実行すると、プロンプトの左側が「(custom-env)」に変化したことがわかります。これにより、現在virtualenvが有効化されていることがわかります。 4. パッケージをインストールする では、実際にパッケージをインストールしてみましょう。今回は、Ansible 2.7.15をあらたにインストールします。 psutil は、Ansible Tower 3.6が依存しているパッケージです。 (custom-env) # pip install -U pip (custom-env) # pip install psutil (custom-env) # pip install -U "ansible == 2.7.15" psutilのインストール時にエラーが発生する場合は以下のパッケージがインストールされていない可能性がありますので、インストールしてから再度試してみてください。 # yum -y install gcc gcc-c++ kernel-devel python-devel インストールが完了したら、カスタムvirtualenv内に存在しているパッケージを確認してみましょう。 pip freeze コマンドで、現在virtualenvにインストールされているパッケージの一覧が確認できます。 (custom-env) # pip freeze DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support ansible==2.7.15 bcrypt==3.1.7 cffi==1.13.2 cryptography==2.8 enum34==1.1.6 ipaddress==1.0.23 Jinja2==2.10.3 MarkupSafe==1.1.1 paramiko==2.6.0 psutil==5.6.7 pycparser==2.19 PyNaCl==1.3.0 PyYAML==5.1.2 six==1.13.0 正常にAnsible 2.7.15がインストールされていることがわかります。virutalenvから抜けるには、 deactivate コマンドを実行します。 (custom-env) # deactivate これで、カスタムvirtualenvの作成は完了です。 5. カスタムvirtualenvを指定してジョブテンプレートを実行する ジョブテンプレートに先程作成したvirtualenvを指定します。このTowerノードには、Ansible 2.9.1がシステムワイドにインストールされています。 # ansible --version ansible 2.9.1 Ansible Towerに同梱されている「Demo Job Template」を実行してみましょう。ジョブテンプレートの設定で「詳細度」を「3」にすることで、実行したAnsible Engineのバージョンが出力されます。 ご覧の通りAnsible 2.9.1で実行されています。そこで、ジョブテンプレートの設定から、「ANSIBLE 環境」の設定を「デフォルト環境の使用」から「/opt/my-envs/custom-env/」に変更します。 設定を変更したら、再度ジョブテンプレートを実行してみましょう。すると、カスタムvirtualenv内のAnsible 2.7.15で実行されていることがわかります。 このように、カスタムvirtualenvを活用することで、既存のAnsible Towerの環境に影響を与えることなく、特定のAnsible Engineのバージョンを共存させたりすることができるようになります。Ansible TowerでのAnsible実行環境の管理に悩んでいる方は、ぜひ一度お試しください。 Happy Automation!
Red HatでOpenShiftのサポートをしている id:nekop です。 OpenShift Advent Calendar 2019 の2日目のエントリです。 OpenShift Service Mesh は Istio の製品版ですが、通常のIstioをセットアップしたときと少し構成が異なり、Istio CNIというものが利用されています。 IstioはトラフィックをインターセプトしてSidecar proxyへ流すためにiptablesのルールを利用しています。このiptablesのルールをどのように適用するか、というところですが、通常のセットアップではInit containerがSidecar proxyと共にアプリケーションのPodへInjectされて利用されます。このInit containerはiptablesを利用するため、NET_ADMIN capabilityが許可されている特権コンテナ(privileged, 要はroot権限)として設定される必要があります。しかし、特権コンテナを許可してしまうと、アプリケーションのすぐ横にセキュリティレベルの弱いコンテナが配置されることになりますし、特権コンテナ起動が許可されているサービスアカウントを流用して他の悪意のある特権コンテナを起動していろいろできてしまったりするので回避したいところです。そこで利用されるのがIstio CNIというCNIプラグインです。Istio CNIでは特権Init contaienrではなくCNIの仕組み内からiptablesのルール適用を行っています。 github.com これがOpenShift 4.2上でどのように構成されているか見ていきたいと思います。この例では test-istio というプロジェクトを対象に見ていきます。 OpenShift Service MeshではServiceMeshMemberRollsというリソースにIstioを使うプロジェクトを記述するとセットアップが実行されます。 $ oc get -n istio-system servicemeshmemberrolls.maistra.io NAME MEMBERS default [knative-serving test-knative test-istio] OpenShift 4では multus-cni というCNIプラグインを柔軟に選択できるCNIメタプラグインが標準となっており、上記設定を行うとMultusのNetworkAttachmentDefinitionというリソースがプロジェクトに作成され、istio-cniが有効化されます。NetworkAttachmentDefinitionの中身は利用するCNIの名前だけ指定されているものです。 $ oc get network-attachment-definitions.k8s.cni.cncf.io --all-namespaces NAMESPACE NAME AGE knative-serving istio-cni 23m test-istio istio-cni 23m test-knative istio-cni 32d $ oc get -n test-istio network-attachment-definitions.k8s.cni.cncf.io istio-cni -o yaml apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: generation: 1 name: istio-cni namespace: test-istio この設定はどこに紐づいているかというと、OpenShift Service Meshに含まれるDaemonSetが各ノードのmultus設定ディレクトリにCNI設定ファイルを配置していて、その名前を指定しています。 $ oc set volumes -n openshift-operators ds/istio-node daemonsets/istio-node host path /opt/multus/bin as cni-bin-dir mounted at /host/opt/cni/bin host path /etc/cni as etc-cni-dir mounted at /host/etc/cni/ このDaemonSetによって各ホストに以下のファイル群が配置されています。 /etc/cni/multus/net.d/istio-cni.conf /etc/cni/multus/net.d/istio-cni.kubeconfig /opt/cni/bin/istio-cni /opt/cni/bin/istio-iptables.sh 実際のCNI設定内容は以下のようになっています。 $ oc debug node/node01 # chroot /host # cat /etc/cni/multus/net.d/istio-cni.conf { "cniVersion": "0.3.0", "name": "istio-cni", "type": "istio-cni", "log_level": "info", "kubernetes": { "kubeconfig": "/etc/cni/multus/net.d/istio-cni.kubeconfig", "cni_bin_dir": "/opt/multus/bin", "iptables_script": "istio-iptables.sh", "exclude_namespaces": [ "openshift-operators" ] } } OpenShift Service Meshでは、このようにDaemonSetでMultusにistio-cni設定を配置、MultusのNetworkAttachmentDefinitionで有効化するという仕組みを利用して特権コンテナを利用しない、より安全なIstioのセットアップを実現しています。
Red Hatの福岡オフィスでソリューションアーキテクトをしている田中司恩です。 早いもので今年も12月がやってきました。皆さんにとってはどんな一年だったでしょうか? 自分は3月にRed Hatに入り、その後も福岡に引っ越ししたりとあっという間の一年でした。 さてもう一つ、12月は何と言ってもアドベントカレンダーの時期ですね! ということで、今年のOpenShiftなアドベントカレンダーはコレ! OpenShift Advent Calendar 2019 - Qiita ということで早速開始です!こちらは1日目のエントリになります。 前回記事では、OpenShift 4.2におけるネットワーク制限環境下でのインストールについて説明しました。 rheb.hatenablog.com 前回の後半でSamples Operatorの設定に関するパートがありました。 そこで十分に説明できなかった内容について、「Samples Operator設定編」と題して説明します。 本記事中で使用する環境やファイル名は前回記事の内容をそのまま使用しています。 本記事の章立てはこのようになります。 Samples Operator 設定概要 ネットワーク制限環境下でのイメージストリームサンプルの使用 ミラーレジストリーを利用したアプリのデプロイ 参考:全イメージミラーシェルスクリプト Samples Operator 設定概要 ドキュメントでは詳細な記述がなく、唐突にSamples Operatorについての内容が出てくるので初めて設定を行う時は戸惑うことと思います。 本記事では「何をすればどうなるのか」という点に焦点を当てて説明します。 ネットワーク制限環境下でのインストール後、イメージストリームの各イメージの参照ができずエラーになりSamples Operatorが Degraded になります。 前回記事ではこの対処に「Samples Operatorで全てのイメージの管理を無効化する」という方法を説明しました。 [図1] Samples Operatorで全てのイメージの管理を無効化 もう一つの方法として、使いたいイメージをミラーレジストリーにミラーし、それ以外を管理対象から外す、という方法があります。 [図2] 使いたいイメージをミラーレジストリーにミラー <参考>全てのイメージをミラーすれば管理対象から外す必要が無いのでは?という点について記事末尾の「参考:全イメージミラーシェルスクリプト」を参照ください。 Samples Operatorの設定をまとめた内容が下記の表になります。 パターン managementState skippedImagestreams Status 説明 設定難度 1 Managed なし Degraded イメージにでも一つでもエラーがあると、2時間経過後にOperatorがDegradedになる - 2 Managed 設定有り True ミラーイメージ以外の、エラーが出ているイメージを全てskipped指定する 高 3 Removed なし True Samples Operator管理の全てのイメージを未使用にする 低 4 Removed 設定有り True 利用するミラーイメージのみをskipped指定する 中 まず、パターン1は初期状態のことで、全てのイメージが参照できずSamples Operatorが Degraded になります。 これを回避する方法として2,3,4のパターンがあります。 パターン1に対し、パターン3では全てのイメージを未使用にします。前回の記事で説明した方法になり、設定難度も低いです。 パターン2と4は似ていますが設定方法が異なります。また、パターン4では必要最低限の設定で済むのでパターン2に比べると簡単です。 次の項目ではイメージのミラーリングと、Samples Operatorの設定について説明します。 ネットワークが制限環境下でのイメージストリームサンプルの使用 イメージをミラーして利用する方法は下記のステップで実現できます イメージのミラー 認証局証明書の登録 samplesRegistry の値変更 個別イメージのスキップ設定 対象のドキュメントは下記です 第9章 ネットワークが制限されたインストール 4.2 | Red Hat Customer Portal 1. イメージのミラー イメージのミラーに使うコマンドは下記です。オプションなどの詳細は oc image mirror -h を参照。 oc image mirror SRC DST イメージのミラーの実行の前に、ミラーレジストリーのリポジトリーを確認してみます。 $ curl -u user:user -k https://registry.test.example.local:5000/v2/_catalog {"repositories":["ocp4/openshift4"]} クラスタのインストール(場合によってはアップグレードも)直後だと、インストール(アップグレード)に使用したものしか登録されていません。 registry.redhat.io のイメージを、ミラーレジストリーにミラーを実行します。対象は nginx-110-rhel7 を指定した例です。 $ oc image mirror registry.redhat.io/rhscl/nginx-110-rhel7:latest registry.test.example.local:5000/rhscl/nginx-110-rhel7:latest -a pull-secret-2.txt 実行結果 registry.test.example.local:5000/ rhscl/nginx-110-rhel7 blobs: 〜省略〜 registry.test.example.local:5000/rhscl/nginx-110-rhel7:latest info: Mirroring completed in 27.91s (3.887MB/s) 再度ミラーレジストリーのリポジトリーを確認してみます。 curl -u user:user -k https://registry.test.example.local:5000/v2/_catalog {"repositories":["ocp4/openshift4","rhscl/nginx-110-rhel7"]} ミラーしたイメージが追加されているのが確認できます。 この時点でGUIを確認すると、証明書が登録されていないためエラーが出ています 例)Builds > Image Streams > nginx [図3] 証明書のエラー 次の手順で証明書の登録を行います 2. 認証局証明書の登録 クラスターのイメージ設定オブジェクトに、レジストリーホストで作成した自己署名証明書を信頼されたCAとして登録します。 $ oc create configmap registry-config --from-file=/opt/registry/certs/domain.crt -n openshift-config 実行結果 configmap/registry-config created 3. samplesRegistry の値変更 <2019/12/4追記> samplesRegistry についてはこちらを参照 1.2. Samples Operator の設定パラメーター 4.2 | Red Hat Customer Portal イメージのインポート元のURLを、ミラーレジストリーのURLで上書きします。 *1 $ oc patch configs.samples.operator.openshift.io/cluster --type merge --patch '{"spec":{"samplesRegistry":"registry.test.example.local:5000"}}' 実行結果 config.samples.operator.openshift.io/cluster patched GUIでは下記のように変更されます 例)Builds > Image Streams > nginx samplesRegistry の値変更前 [図4] samplesRegistryの値変更前 TagsのFromのURLが registry.redhat.io となっています samplesRegistry の値変更後 [図5] samplesRegistryの値変更後 TagsのFromのURLが registry.test.example.local:5000 に変わりました イメージをミラーした nginx:1.10 はイメージの参照ができるのでエラーが消えています。 まだミラーしていない nginx:1.12 はエラーのままです。 4. 個別イメージのスキップ設定 ここで行うことはSamples Operatorの管理配下から、指定のイメージを除外する設定です。 先の項目では、まだミラーしていない nginx:1.12 にはエラーが出ていました。 このエラーをこのままにしておくと、2時間後にSamples Operatorは Degraded になります。 省略されないミラーリングされないイメージがあるか、または Samples Operator が Removed に変更されない場合、 Samples Operator はイメージストリームのインポートが失敗し始めてから 2 時間後に Degraded ステータスを報告します。 こうした場合に指定のイメージストリームを除外設定することで、 Degraded になることを回避できます。 下記のコマンドを実行します $ oc edit configs.samples.operator.openshift.io spec: に skippedImagestreams: を追加し、除外するイメージストリーム名(今回は nginx )を指定します 〜省略〜 spec: architectures: - x86_64 managementState: Managed samplesRegistry: registry.test.example.local:5000 skippedImagestreams: - nginx 〜省略〜 GUIでは下記のように変更されます 例)Builds > Image Streams > nginx [図6] skippedImagestreamsの追加前 nginxのラベルは managed=true です [図7] skippedImagestreamsの追加後 nginxのラベルが managed=false に変わりました さらに除外するイメージストリームを追加します。 今回はyamlファイルを用意して、 oc apply で実行してみます。 image.yaml $ cat image.yaml apiVersion: samples.operator.openshift.io/v1 kind: Config metadata: name: cluster spec: skippedImagestreams: - nginx - nodejs コマンド実行 $ oc apply -f image.yaml config.samples.operator.openshift.io/cluster configured skippedImagestreams にさらに追加後 [図8] skippedImagestreamsにさらに追加後 nginxとnodejsのラベルが managed=false になっています このように、エラーが出ているイメージを除外指定する方法が、設定概要で説明したパターン2の方法になります。 さて、この状態でSamples Operatorのイメージ管理を無効化するとどうなるでしょうか? やってみます。 $ oc patch configs.samples.operator.openshift.io/cluster --type merge --patch '{"spec":{"managementState":"Removed"}}' config.samples.operator.openshift.io/cluster patched 結果はこのようになります。 [図9] Samples Operatorのイメージ管理を無効化 元々Samples Operator管理対象外のイメージと、除外指定をしたイメージが残ります。 このように使用したいイメージを除外設定とし、全体のイメージ管理をオフにすることで、必要最低限の設定で済ませることができます。 この方法がパターン4の方法になります。 (参考)例で紹介したnginxとnodejsですが、どちらものGitのソースを利用したS2Iビルドを行います。 今回のようなネットワーク制限環境下ではそもそもクラスタからインターネットへのアクセスができず、Githubを利用するような用途には実際には利用できません。 ミラーレジストリーを利用したアプリのデプロイ ミラーしたイメージストリームを利用してアプリのデプロイを行ってみます。 今回はS2Iを利用しないredisを使用します。 oc new-app を使う手順は通常通りです。 前提として rhscl/redis-32-rhel7:latest がすでにミラー済みとします。 プロジェクト test にredisをデプロイ $ oc new-project test Now using project "test" on server "https://api.test.example.local:6443". You can add applications to this project with the 'new-app' command. For example, try: oc new-app django-psql-example to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application: kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node $ oc new-app redis --> Found image a00c397 (7 weeks old) in image stream "openshift/redis" under tag "3.2" for "redis" Redis 3.2 --------- Redis 3.2 available as container, is an advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; or getting the member with highest ranking in a sorted set. In order to achieve its outstanding performance, Redis works with an in-memory dataset. Depending on your use case, you can persist it either by dumping the dataset to disk every once in a while, or by appending each command to a log. Tags: database, redis, redis32, rh-redis32 * This image will be deployed in deployment config "redis" * Port 6379/tcp will be load balanced by service "redis" * Other containers can access this service through the hostname "redis" * This image declares volumes and will default to use non-persistent, host-local storage. You can add persistent volumes later by running 'oc set volume dc/redis --add ...' --> Creating resources ... imagestreamtag.image.openshift.io "redis:3.2" created deploymentconfig.apps.openshift.io "redis" created service "redis" created --> Success Application is not exposed. You can expose services to the outside world by executing one or more of the commands below: 'oc expose svc/redis' Run 'oc status' to view your app. $ oc status In project test on server https://api.test.example.local:6443 svc/redis - 172.30.207.125:6379 dc/redis deploys openshift/redis:3.2 deployment #1 deployed 50 seconds ago - 1 pod 3 infos identified, use 'oc status --suggest' to see details. $ oc get pods NAME READY STATUS RESTARTS AGE redis-1-deploy 0/1 Completed 0 63s redis-1-wp9mj 1/1 Running 0 55s $ oc get is/redis -n test NAME IMAGE REPOSITORY TAGS UPDATED redis image-registry.openshift-image-registry.svc:5000/test/redis 3.2 問題無くアプリのデプロイができました。 まとめ 前回の記事から引き続き、ネットワーク制限環境下での内容について解説しました。 クラスタのインストール後にエラーを回避し、適切な設定を行うためには、Samples Operatorやイメージに関する内容についても理解する必要があります。 ここまで行っても全ての機能が完全に使える訳ではありませんので、やはり何らかの手段でクラスタをインターネットへ接続する環境を整えることを推奨します。 Let's get Big Ideas ! 参考:全イメージミラーシェルスクリプト 本記事のイメージのミラー手順では、個別のイメージタグを指定してミラーする方法を紹介しました。 Samples Operatorに登録されているミラーが必要なイメージストリーム数は211あります。 *2 流石に手作業で全てをミラーするのは手間なので、全てをミラーするサンプルスクリプトを作成しました。 ※ 注意 下記のスクリプトは検証用に作成したものです。本番環境で実行することは推奨しません。 mirror.sh #!/bin/bash KUBECONFIG="kubeconfig" SRC_URL="registry.redhat.io" DST_URL="registry.test.example.local:5000" SECRET="pull-secret-2.txt" ### MAIN ### export KUBECONFIG IMAGE_LIST=`oc get imagestreams -n openshift -o json |jq -r '.items[]?.spec.tags[]?.from.name | select(startswith("registry"))'` for image in $IMAGE_LIST; do IMAGE_NAME=`echo $image | sed -e "s/registry\.test.example\.local\:5000//"` # Dry-run #oc image mirror $SRC_URL$IMAGE_NAME $DST_URL$IMAGE_NAME -a $SECRET --dry-run # Exec oc image mirror $SRC_URL$IMAGE_NAME $DST_URL$IMAGE_NAME -a $SECRET done 全てのミラーの完了に要する時間は、ネットワークの環境にもよりますが数時間はかかります。 また、 /opt/registry の容量が47GB増大します。 さらに、これで全てのイメージタグがミラーできる訳ではなく、どうしてもエラーが出てミラーできないものがいくつかあります(イメージストリーム内の一部のタグのみミラーできない、等も含む)。 検証環境でミラー後にダウンロードが完了しなかったイメージストリームは下記の通りです。 エラーが出ているイメージのみを取得しリスト化 *3 $ oc get configs.samples.operator.openshift.io -o json | jq -r .items[].status.conditions[].reason | grep -v 'null' | tr ' ' '\n' | sort apicast-gateway httpd jboss-amq-62 jboss-amq-63 jboss-datagrid65-client-openshift jboss-datagrid65-openshift jboss-datagrid71-openshift jboss-decisionserver64-openshift jboss-eap64-openshift jboss-eap70-openshift jboss-eap71-openshift jboss-processserver64-openshift jboss-webserver30-tomcat7-openshift jboss-webserver30-tomcat8-openshift jboss-webserver31-tomcat7-openshift jboss-webserver31-tomcat8-openshift jenkins jenkins-agent-maven jenkins-agent-nodejs modern-webapp nodejs redhat-openjdk18-openshift redhat-sso70-openshift redhat-sso71-openshift redhat-sso72-openshift rhpam73-businesscentral-indexing-openshift このように、全てのイメージをミラーをすることは難しく、Samples Operatorを Managed で実行するためには パターン2となり skippedImagestreams を組み合わせる必要があります。 *1 : ドキュメントの 9.1.6 手順3 では oc get のコマンドが載っていますが、間違いというよりは情報不足です。 *2 : cli、cli-artifacts、installer、installer-artifacts、must-gather、tests、jenkins、jenkins-agent-maven、jenkins-agent-nodejs を除く *3 : jenkinsはインストール時にquay.ioからミラーするので本来は対象外