TECH PLAY

NTTドコモビジネス

NTTドコモビジネス の技術ブログ

602

目次 目次 はじめに ECCV2022概要 Workshop Instance-Level Recognition Workshop Keynote talk: Image Search and Matching Kaggle Google Universal Image Embedding Challenge Keynote talk: Few-Shot Learning for Object Aware Visual Recognition Language Assisted Product Search Granularity aware Adaptation for Image Retrieval over Multiple Tasks Where in the World is this Image? Transformer-based Geo-localization in the Wild" What to Hide from Your Students: Attention-Guided Masked Image Modeling Keynote talk: Instance level recog for SSL, and vise-versa 3rd Advanced Autonomous Driving Workshop 最後に はじめに こんにちは、イノベーションセンターの鈴ヶ嶺・加藤・齋藤です。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。10月23日から27日にかけて、コンピュータービジョン分野におけるトップカンファレンスのひとつである ECCV2022 がオフラインとオンラインのハイブリッドで開催され、NTT Comからは複数名オンラインで参加しました。その参加レポートを前後編に分けて紹介します。 前編では会議の概要とWorkshop、Kaggle、Keynote talkについて紹介します。後編では、論文の紹介をしたいと思います。 ECCV2022概要 ECCVは2年に1度開催されるコンピュータービジョン分野におけるトップカンファレンスのひとつです。今年は現地(Tel Aviv)とオンラインのハイブリッド開催でした。採択率は25.3%(1645/6773)となっております。ECCV2022でも、中国やアメリカの採択が割合を大きく占めているようです 1 。 Workshop Instance-Level Recognition Workshop https://ilr-workshop.github.io/ECCVW2022/ このワークショップではinstance-level recognitionという、固有名詞レベルで物体を分類する技術を取り扱っています。日本語では特定物体認識と呼ばれることが多いようです。この技術は以下のような応用が考えられます。 ARを用いた美術館や遺跡の説明 コマースにおける商品認識 画像検索 このタスクは以下のような特徴があり、特に多様性のあるデータセットの構築が困難であるようです。 Large-scale: 一般物体認識に比べカテゴリ数が遥かに多く、例えば観光地の画像を収集したGoogle Landmark Dataset (GLD) v2では200,000カテゴリ存在する。 Long-tailed: 有名どころならカテゴリあたり1000画像以上あるが、5枚以下しかないカテゴリもかなり存在する。 Limited appearance: 一部しか見えていないことがあるが、大抵の認識対象は剛体なので局所特徴量を比較する画像マッチングが役立つ。 本ワークショップでは、instance-level recognitionのさまざまな手法の紹介に加え、より広い分野にわたって収集された画像データセットや、それを用いたコンペティションの紹介も行われました。この章では各発表の概要を紹介していきます。 workshopの歴史 本ワークショップは2018年のGLDv1を用いた1st landmark detectionから始まりました。当時は観光地画像のみを対象としたlandmark detectionでしたが、近年は物体一般のinstance level recognitionを目標としており、今回は以下の2つのコンペティションが紹介されました。 Kaggle Google Universal Image Embedding Competition 2022 Amazon Alexa Language Assisted Product Search Challenge それぞれのコンペティションの概要はのちの節で述べます。 Keynote talk: Image Search and Matching 画像検索と画像マッチングの方法についての発表です。 一番基本的な画像マッチングは、画像から特徴点を抽出し、特徴量の近い点同士をマッチングしたのち、一番尤もらしい変換行列Hを算出します。 画像検索では画像間の視点がしばしば著しく異なるものの、画像マッチングで使う局所特徴量を活用して画像の類似度を測ったり、マッチングにより算出した変換行列を用いて特徴点の位置関係を検証し偽陽性(局所的に似た箇所はあるものの異なる物体が写っている検索候補)を排除したりできます。しかし大規模なデータベースからの画像検索では、局所特徴量によるマッチングを総当たりでと計算量が膨大になるため、別の手法が必要になります。この発表では画像検索について2つの論文が紹介されました。 DELG 2 では、ニューラルネットワークによって画像から抽出された大域特徴量をデータベースに保存し、検索時にはクエリに類似した大域特徴量を持つ画像に対して、同じくニューラルネットワークから抽出された局所特徴量でマッチングを適用し検索結果を洗練させました。この後処理はRerankingと呼ばれています。 (元論文のFig. 1から) Instance-level Image Retrieval using Reranking Transformers 3 では、従来は候補画像の局所特徴量にRANSACをかけ幾何的に正しく対応している特徴点をカウントすることで(これをGeometric verificationと呼びます)行われていたRerankingをTransformerで行いました。大域特徴量と局所特徴量にそれぞれposional encodingを行い、Transformerに入力して類似度を予測しています。 (元論文のFig. 2から) ここで紹介されているように、大域特徴量のみ、局所特徴量の集合のみを使った比較よりも、大域特徴量とRerankingの融合はより精度の高い画像検索を可能にすることがわかっています。 また、より発展した画像検索の手法としてDrill-down 4 が紹介されました。この手法では自然言語によるプロンプトを検索の補助として活用しており、公園でとった記念写真のような、特定のランドマークが存在しない非常に困難なケースでも「右にピンクのコートを着た女性がいる」などの条件を追加していきながらインタラクティブに画像検索ができるようになっています。 ふつう画像には様々なものが写っており、検索するユーザーがどれに言及するかは明らかではありません。そこでこのモデルではユーザーの文章群をいくつかの話題にカテゴリ分けし、それぞれの話題についての言語特徴をまとめたものと、画像に写っているさまざまな物体の画像特徴をまとめたものとを比較し類似度を算出しています。 (元論文のFig. 2から) そしてこのモデルを学習する時は、画像のさまざまな領域に説明のアノテーションがついたVisual Genome dataset 5 を用いて検索クエリを再現しています。 最後の話題は画像と言語の統合でした。 近年では画像とキャプションのペアを集めた大規模なデータセットで学習したモデルを用いて、「A picture of a [category name]」というキャプションと入力画像との一致度を比較するというゼロショットの画像分類手法が好成績を収めました 6 。さらに、画像単位で与えられているキャプションをもとに、その記述が画像のどこを指しているのかをバウンディングボックスやヒートマップで可視化する説明手法なども提案されています 7 。 Kaggle Google Universal Image Embedding Challenge https://www.kaggle.com/competitions/google-universal-image-embedding クエリとして与えられた画像に同じものが写っている画像を検索できるような特徴量抽出機を作るコンペティションです。 一般的な手法では以下のようにドメインごとに学習されていました。 Google landmark dataset: 建物や観光地の画像 iNaturalist dataset: 動植物の画像 さまざまなドメインにわたって表現可能な特徴量の計算がこのコンペティションのゴールでした。 kaggleで行われたコンペティションのフォーマット 学習データは配布せず、条件なしで任意の公開データセットを使用可能とした 画像から64次元の特徴量を出力するPytorch/Tensorflowモデルを提出する Kaggle kernel上で9時間以内に評価用のデータセットから特徴量計算 & 5 nearest neighborsの計算が可能なモデルとする 評価データセットは以下のようにlarge-scale, long-tailedな特徴を持っています。 衣服、家具、建物など10以上の分野にわたって収集 5000枚のクエリ 20万枚の検索用データベース クエリの8割は正例が25枚未満, 57%は10枚未満のカテゴリだった。 Winning solutions 上位陣は以下のような手法を用いていました。 large-scale pretrained model CLIP-ViT-H/L pretrained on LAION 5B 今回自然言語はほとんど関係ありませんが、画像とキャプションのペアをもとに事前学習されたCLIPが広く使われました。 Mixing training dataset General Purpose: GPR1200 Landmark: GLD Products: Products10k, Deepfashion, Alibaba Goods Art: MET 公開されているデータセットを収集しファインチューニングに活用していました。 Data Augmentation Class Balancing Multi-resolution, Overlapping Patches 少量のサンプルしかないカテゴリの対策として行われたようです。 Model Ensembling Model soup: https://arxiv.org/abs/2203.05482 最終的な出力は特徴量なので、出力をアンサンブルするのではなくモデルの重みをアンサンブルするModel soupがよく使われていました。 Arcface 元は顔認識用の手法ですが、今回のように似たような画像の特徴量を近づける距離学習というタスクに広く使われています。 以下の節では上位陣の解法の概要を説明します。 1st place solution https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359316 今回アンサンブルがあまり効かず、その理由として、モデルごとに異なる特徴空間の値をただ平均するのが良くないのではという仮説を立てています。そこで特徴空間の形を決めるのは最後のprojection layerのみであるという仮定のもと、head部をフリーズしてbackboneのみをファインチューニングするという方法をとったところ性能が向上しました。 4th place solution Zeroshotでも強力なモデルが作成可能であることを示しています。 https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359998 GPT-3を活用し、さまざまな物体のテキストプロンプトを生成("Give me a list of 100 diverse dishes as a Python list"とGPT-3に入れると、stringのリストが手に入る) CLIPのtext encoderに通して特徴量を計算し、64次元にPCA 得られたprojection layerをvision encoderにくっつける 一方で最終的な解法は次のようになっています。 https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359487 特徴としては以下の3点が挙げられます。 model soupの活用 Arcfaceの利用 H-14とL-14-336の異なるモデルサイズのアンサンブル Keynote talk: Few-Shot Learning for Object-Aware Visual Recognition 少量の教師データをもとに推論するタスクをfew-shot learningと呼びます。例として、動画のあるフレームで映っている対象物体にアノテーションを施すと、残りのフレームに映る対象を自動でセグメンテーションしてくれるというものが挙げられます。このようなタスクは直接instance-level recognitionとは関係ありませんが、一方のテクニックを他方に応用できる可能性があると発表者は述べていました。 Few-shot learningは画像分類とセグメンテーションの分野で広く研究されていますが、セグメンテーションよりも細かい対応づけ、つまり異なる物体間の意味的に同じ部分(生物なら頭や足など)を対応づけるということはまだ上手くできていません。本発表ではこの「意味的な対応づけの学習」に注目したベンチマークデータセットSPair-71k 8 やマッチング手法としてハフ変換を活用するもの 9 、attention機構を導入したもの 10 、特徴抽出器のさまざまな中間層から特徴マップを取り出して相関をとるもの 11 などが紹介されました。 そしてより発展的な問題として、従来はクエリ画像に対して1種類の分類やセグメンテーションしかできなかったfew-shot learningをマルチラベルに拡張したFS-CS 12 というタスクが紹介されました。このタスクはより現実に即したものと言えます。 Language Assisted Product Search Amazonでは以下のようなインタラクティブな買い物ボットを実現するための研究をおこなっています。 ユーザーが「鞄が欲しい」と言うと、ボットが鞄の商品画像を表示 さらに「赤いのが欲しい」と言うと、赤い鞄の画像を表示 さらに「もっと小さいのが欲しい」と言うと、より小さな赤い鞄の画像を表示 本発表ではこれを目標としたコンペティションを設計しオープンしたことが語られました。 Single-Shot Language-Assisted Product Retrieval https://eval.ai/web/challenges/challenge-page/1845/evaluation 商品画像とそれに対するフィードバックの文章をもとに、その要求に応えた商品画像を検索可能な特徴ベクトルを生成するコンペティションです。 このタスクを行うための評価データセットの構築には次のような障害がありました。 large-scale: 扱う商品があまりに多すぎる上に、今あるデータセットはその一部しかカバーしていない。 similar products: 何千もの似た様な商品が存在し、クエリごとにそれら全てを正例としてアノテーションするのは現実的でない。既存のデータセットは正例を1つに限定してしまっている。 diverse language: フィードバックのプロンプトは多様性に満ちており、単語ではなく文章を喋っているので自然言語処理が必要。 これらを意識し次のようなデータセットが作られました。 学習データ 100万枚の画像データベース 15000件のproduct triplets(クエリ画像1枚とフィードバック3件の組) 衣服のみ 評価データ 100万枚の画像データベース (学習データと重複なし) 15000件のproduct triplets 衣服と家具 これを構築するために3万件のproduct tripletsがアノテーションされましたが、これらは以下のように行われました。 元商品、欲しい商品、欲しくない商品の組を見せ、欲しくない商品を避けつつ元商品から欲しい商品に替えてくれるようなフィードバックをアノテーターに書かせる。 欲しい商品に似た画像を50枚収集し、元画像+フィードバックにマッチするものを正例に加える。 最終的に8500クエリを追加アノテーションし、そのうち79%が単一の正解サンプルをもっていて、5%が5件以上の正解サンプルを持っているという結果になった。 Baseline model VAL 13 というモデルが公式のベースラインとして利用されました。このモデルの特徴は次のようになっています。 フィードバック文章はLSTMに通して特徴を生成する。 クエリ画像は段階的に畳み込みながら、各レベルで文章特徴とcross attentionを行い中間特徴を生成する。 ターゲット画像はフィードバックとのcross attentionを行わずに中間特徴と比較しクエリの中間特徴に近づける。 このコンペティションは2023/1/1に終了予定です。 今後の方針として、複数回のフィードバックへの対応や、ユーザーがアップロードした画像に対応することなどが挙げられていました。 Granularity-aware Adaptation for Image Retrieval over Multiple Tasks 14 画像検索タスクに使われるモデルは基本的に様々な分野にわたる画像に対応していますが、対象を狭くしてより強力なモデルを作りたいというケースがあります。しかし新しく対象の分野でのデータセットを構築するのはコストが高く、また画像検索タスクに使われるモデルはドメイン変化に弱いことが知られています。そこでラベルなしのデータセットを使ってドメイン適応したいというのが本発表のモチベーションであり、巨大な事前学習済みモデルの効率的な転移学習を可能にするTransformerベースのAdapterFusion 15 をラベルなしデータセットに応用しました。 本手法ではラベルなしデータセット全体で特徴量を計算し、クラスタリングした結果を擬似ラベルとしてAdapterを学習します。工夫点として、対象のデータセットの粒度を段階的に細かくしていきながら学習を進めるということを行なっています。まず少ないクラスタ数で擬似ラベルを付けて転移学習し、段階的にクラスタ数を増やしながら転移学習を繰り返すことで精度の向上を図っています。 MRT dataset 評価用にMRTデータセットを利用しました。このデータセットは以下の6つのデータセットの合成となっています。 Aircraft Cars CUB Flowers Food-101 Products まずデータセット全体でモデルを学習し、評価時は各分野のテストデータを使って別々に精度を測り、それぞれのタスクに特化した検索ができているか評価します。 Where in the World is this Image? Transformer-based Geo-localization in the Wild Geo-localizationとは、画像から緯度経度を予測するタスクです。 データセットが大規模であること、時刻・天気・季節といった変数によって画像が大きく変化することなどがマッチングを困難にしていることが知られています。また似たような建物を他の地域が建てることもあるため注意が必要です。 Approach Vision Transformerを利用し、以下のような工夫を施しています。 セマンティックセグメンテーションで情報をまとめることで時刻や天気に対するロバスト性を確保 シーンタイプを同時に予測する(自然、都会、屋内など)ことでシーンごとに必要な特徴量を意識させる Dataset 利用したデータセットは以下の通りです。 Training: MediaEval Placing Task 2016 (Flickrから収集した4.72M geo-tagged images) Validation: YFC26k (25.6k geo-tagged) Test: Im2GPS, Im2GPS3k, YFCC4k What to Hide from Your Students: Attention-Guided Masked Image Modeling Masked image modeling(画像の一部を隠して復元させる)を通したVisual Transformerのself-supervised learning手法についての発表でした。 既存手法ではランダムにPatchを隠していました(random erasing)が、以下のような提案手法によって分類性能を向上させています。 Visual Transformerのself attentionを活用した効果的なerasing attentionの高いPatchを優先的に消すことで、random erasingよりも難しいサンプルを生成できる ヒントとなる部分を少し残すことでさらに性能が向上する Keynote talk: Instance level recog for SSL, and vise-versa ラベルのない大量の画像でself-supervised learningを行い、画像検索やコピー検出などの後段のタスクに活用するという手法を紹介しています。 かつてのself-supervised learningは以下のように行われていました。 instance discrimination 同じ画像にさまざまなData Augmentationをかけ、頑健な特徴量を計算する 画像数と同じだけカテゴリを用意するためスケールしないという欠点がある Constrastive learning negative pairよりpositive pairの方が近い特徴量になるように学習 大規模なnegative pairsを収集する工夫がなされた SimCLR 16 : バッチサイズを大きく取り、バッチ内の画像間をnegative pairとした。 MoCo 17 : これまでの入力に対する特徴を記憶し、negative pairの相手として採用する。記憶している特徴量が学習中の特徴抽出器に対して古くならないように、記憶の仕組みはキュー型を採用した。 negative pairsを使わないSSL:DINO そもそもnegative pairを使わない手法としてDINO 18 などが提案されています。DINOの特徴として以下の点が挙げられます。 Teacher networkとStudent networkを用意し自己蒸留 studentはteacherの出力と一致するように重みをSGDで更新 teacherはstudentの重みに指数移動平均(EMA)で緩やかについていく ネットワークの出力が単一ラベルにつぶれたり一様に平たくなってしまう問題は、teacher側の出力にCenteringとSharpeningをかけ、意味のある出力をstudentに真似させることで克服 一般的な蒸留と異なり、teacherには正解ラベルが与えられないこと、またstudentはteacherの出力を真似てteacherがstudentのパラメータを真似るというサイクルができていること特徴的です。 Oxford / Parisデータセット 19 を用いた実験では以下のことが示されています。 ImageNet (w/labels) を使った教師あり学習より、ラベルなしでImageNetをDINOで学習したものの方が高性能だった。 学習データをGoogle Landmark Datasetに替えるとより高性能になった 画像/映像コピー検出への応用でもDINOが良い精度を出していることが示されています。 3rd Advanced Autonomous Driving Workshop https://avvision.xyz/eccv22/ 自動運転に関する本ワークショップでは、3次元物体認識やセグメンテーションに加え、運転シミュレーターを用いた運転経路の予測など自動運転に関する様々な技術が取り扱われています。今回が3回目で、過去にはWACV'21やICCV'21でも開催されているとのことです。ここでは、ワークショップの中でも印象的だった、Andreas Geiger教授の招待講演を紹介したいと思います。 Learning Robust Policies for Self-Driving この招待講演では Andreas Geiger 教授の研究室から今年2022年に発表された3つの最新論文が紹介されていました。各論文の概要を以下で説明します。 Transfuser 20 Transfuserは、複数センサーから得られるマルチモーダルデータを入力として適切な運転経路予測をするモデルです。カメラから取得したRGB画像と、LiDARセンサから取得した点群の鳥瞰図(Bird's Eye View, BEV)とを各々の特徴抽出器に入力し、中間層で各モーダルの特徴マップにTransformerベースのCross Attentionを適用することで、画像と点群それぞれの特徴抽出器の出力が他方のモーダルの情報で補間されると述べられています。CARLAシミュレーターで生成したデータセットで評価したところ、同一入力のベースライン手法に比べ規則違反なくルートを完走する性能が大きく向上することが示されていました。 PlanT 21 Transfuserがセンサー群から得られるデータを入力とするモデルであったのに対し、PlanTでは周囲の運転エージェント(近くを走行する車両)の情報が即時的に得られることを仮定し、その情報から算出される特徴量(object-level representation)を入力として運転経路の予測を行います。Object-level representationは、運転エージェントの位置、向き、大きさ、エージェントが搭載するセンサーのデータを前述のTransfuserに入力して得られる属性とから算出され、各エージェントのrepresentationが1つのトークンとしてBERT 22 ベースのモデルに入力されます。この論文でもCARLAシミュレーターから生成されるデータセットで評価を行っており、Transfuserを上回る運転性能が達成できることが示されていました。 KING 運転経路を適切に予測するエージェントを獲得するには車両が衝突するようなシナリオを含め学習することが好ましい一方で、実世界でそのようなデータを取得することは危険かつ困難です。代替手段として運転シミュレーターでそのようなシナリオを再現する方法が考えられますが、実際はシミュレーター上でも現実的な衝突シーンの再現にはコストがかかるという問題があります。そこでこのKINGというアプローチでは、車両の衝突を助長するよう設計した目的関数を最適化することで環境内のエージェントの行動を変化させ、得られた行動から衝突が発生するシーンを生成することが提案されています。車両モデルには微分可能なbicycle modelを採用することで、目的関数はバックプロパゲーションでエンドツーエンドに最適化することが可能です。CARLAシミュレーター 23 を用いて実験をしたところ、提案手法で生成された車両の衝突を含むシナリオは、勾配情報を用いないブラックボックス最適化で生成されたシナリオに比べ、衝突をより回避する運転エージェントの獲得に寄与することが示されています。 最後に 本ブログでは、ECCV2022の概要と私たちが興味を持ったWorkshopをご紹介しました。後編では、私たちが気になった論文を紹介するのでぜひご覧になってください。 NTT Comでは、今回ご紹介した論文調査、画像や映像、更には音声言語も含めた様々なメディアAI技術の研究開発に今後も積極的に取り組んでいきます。また一緒に技術開発を進めてくれる仲間も絶賛募集中です。 アカデミックな研究に注力したくさん論文を書きたい 最新の技術をいち早く取り入れ実用化に結び付けたい AIアルゴリズムに加え、AI/MLシステム全体の最適な設計を模索したい 2022年12月06日現在、NTT Comでは 現場受け入れ型インターンシップ のエントリーを受付中です。私達のチームからも、AIエンジニアカテゴリに メディアAI技術開発エンジニア/リサーチャー というポストを出しています。インターンを通じて、会社やチームの雰囲気、そして私たちの取り組みを知っていただく機会にできればと考えています。皆様のご応募、心からお待ちしています! https://eccv2022.ecva.net/files/2022/10/ECCV22-Welcome-Slides-for-web.pdf ↩ Bingyi Cao, A. Araújo, and Jack Sim. "Unifying Deep local and global features for image Search." ECCV 2020. ↩ Fuwen Tan, Jiangbo Yuan, and Vicente Ordonez. "Instance-level Image Retrieval using Reranking Transformers." ICCV 2021. ↩ Fuwen Tan, Paola Cascante-Bonilla, Xiaoxiao Guo, Hui Wu, Song Feng and Vicente Ordonez. "Drill-down: Interactive Retrieval of Complex Scenes using Natural Language Queries." NeurIPS 2019 ↩ Ranjay Krishna, Yuke Zhu, Oliver Groth, Justin Johnson, Kenji Hata, Joshua Kravitz, Stephanie Chen, Yannis Kalantidis, Li Jia-Li, David Ayman Shamma, Michael Bernstein and Li Fei-Fei. "Visual Genome: Connecting Language and Vision Using Crowdsourced Dense Image Annotations." 2016. ↩ Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger and Ilya Sutskever. "Learning Transferable Visual Models From Natural Language Supervision." 2021. ↩ Ziyan Yang, Kushal Kafle, Franck Dernoncourt and Vicente Ordonez. "Improving Visual Grounding by Encouraging Consistent Gradient-based Explanations." 2022. ↩ Juhong Min, Jongmin Lee, Jean Ponce and Minsu Cho. "SPair-71k: A Large-scale Benchmark for Semantic Correspondence." 2019. ↩ Juhong Min and Minsu Cho. "Convolutional Hough Matching Network." CVPR 2021. ↩ Seungwook Kim, Juhong Min and Minsu Cho. "TransforMatcher: Match-to-Match Attention for Semantic Correspondence." CVPR 2022. ↩ Juhong Min, Dahyun Kang and Minsu Cho. "Hypercorrelation Squeeze for Few-Shot Segmentation." ICCV 2021. ↩ Dahyun Kang and Minsu Cho. "Integrative Few-Shot Learning for Classification and Segmentation." CVPR 2022. ↩ Yanbei Chen, Shaogang Gong and Loris Bazzani. "Image Search With Text Feedback by Visiolinguistic Attention Learning." CVPR 2020. ↩ Jon Almazán, Byungsoo Ko, Geonmo Gu, Diane Larlus and Yannis Kalantidis. "Granularity-aware Adaptation for Image Retrieval over Multiple Tasks." ECCV 2022. ↩ Jonas Pfeiffer, Aishwarya Kamath, Andreas Rücklé, Kyunghyun Cho and Iryna Gurevych. "AdapterFusion: Non-Destructive Task Composition for Transfer Learning." EACL 2021. ↩ Ting Chen, Simon Kornblith, Mohammad Norouzi and Geoffrey Hinton. "A Simple Framework for Contrastive Learning of Visual Representations." ICML 2020. ↩ Kaiming He, Haoqi Fan, Yuxin Wu, Saining Xie and Ross Girshick. "Momentum Contrast for Unsupervised Visual Representation Learning." CVPR 2020. ↩ Mathilde Caron, Hugo Touvron, Ishan Misra, Hervé Jégou, Julien Mairal, Piotr Bojanowski and Armand Joulin. "Emerging Properties in Self-Supervised Vision Transformers." ICCV 2021. ↩ Filip Radenović, Ahmet Iscen, Giorgos Tolias, Yannis Avrithis and Ondřej Chum. "Revisiting Oxford and Paris: Large-Scale Image Retrieval Benchmarking." CVPR 2018. ↩ Prakash, K. Chitta, and A. Geiger. Multi-modal fusion transformer for end-to-end autonomous driving. In Proc. IEEE Conf. on Computer Vision and Pattern Recognition (CVPR), 2021. ↩ Katrin Renz, Kashyap Chitta, Otniel-Bogdan Mercea, A. Sophia Koepke, Zeynep Akata and Andreas Geiger. PlanT: Explainable Planning Transformers via Object-Level Representations. CoRL 2022. ↩ I.Turc, M.-W. Chang, K. Lee, and K. Toutanova. Well-read students learn better: On the importance of pre-training compact models. arXiv.org, 1908.08962, 2019. ↩ Dosovitskiy, A., Ros, G., Codevilla, F., Lopez, A., Koltun, V.: CARLA: An open urban driving simulator. In: Proc. Conf. on Robot Learning (CoRL) (2017) ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 9日目の記事です。 対象読者 / わかること 対象読者 IoT デバイス接続の難しさに頭を抱えている 「クラウドにデータを送信する」までの要所をざっくり理解したい とにかく IoT を道具として使ってみたい、始めてみたい わかること 「クラウドにデータを送信する」までの一連の流れ Things Cloud を活用した「お手軽IoT」の始め方 はじめに こんにちは、Things Cloud のソリューションアーキテクトチーム 竹村です。私たちのチームは、5G&IoTサービス部で IoT プラットフォーム「 Things Cloud 」を活用したソリューションアーキテクトを担当しています。 早速ですが、皆さんは「IoT」と聞いて何を連想しますか。モノ同士が通信すること、クラウド上でデータを可視化すること、それとも現地の機器を自律制御することでしょうか。もちろんこれらは IoT で実現できることですが、共通していることがあります。それは 「何か実現したいことに対する手段であり目的ではない」 いう点です。 皆さんが IoT を活用して目指すゴールは、品質向上・コスト削減・事業拡大・業務DXなど様々だと思います。IoT によって、「現状把握→仮説立案→効果検証」の PDCA サイクルを高速に回すことも、自動化してビジネスをスケールさせることも可能になります。 つまり、IoTは 「数ある手段の中でも強力な手段の1つ」 とも言えます。 しかし、どうやったら IoT を導入できるのか、頭を抱えている方も多いのではないでしょうか。 IoT の技術領域は「デバイス」「ネットワーク」「クラウド」「データ利活用」「セキュリティ」など多岐にわたるため、いざ検討を始めるとサービス選定やシステム構築などの場面で多くの課題に直面します。 本記事では、「IoT を道具として使いこなしたい」「仮説検証や商用導入に多くの時間を使いたい」皆さんのご要望にお応えするため、Things Cloud とパートナーデバイスを活用した「お手軽IoT」の始め方についてご紹介します。 クラウドにデータを送信するには? それでは、IoT を道具として使いこなす第一歩「データをクラウドに送信する」までの STEP をみていく前に一度、私たちがハサミを使う場面を想像してみてください。 私たちはてこの原理を意識しなくても「なんか刃の奥の方が切りやすいぞ」と経験上学習して知っています。それと同じように、「なんとなく分かった」状態で IoT 使い始めることを目標に、肩の力を抜いてこれからお話しする内容もご覧いただければと思います。 そうです、IoT はただの道具です。(しかし、強力な道具です!) まず、IoTを構成する要素として、センサーと IoT-GW (ゲートウェイ)といったデバイスがあります。 センサーは、物理情報を科学原理に基いて信号に変換する機器のことで、温湿度センサーなどが存在します。IoT-GW は、センサー等の機器とクラウド間を中継する機器のことです。 「データをクラウドに送信する」には主に以下の3つの STEP があり、多くの場合では、IoT-GW のソフトウェアが3つの STEP を実行します。 センサープロトコル変換 センサーから送信される多種多様な形式のデータを解釈 データ形式変換 デバイスのデータ情報をクラウドが理解するデータモデルに変換 安全な通信 安全な通信(HTTPS / MQTTS など)によるクラウドへのデータ送信 それでは、それぞれの STEP について詳細を見ていきましょう。 STEP1:「センサープロトコル変換」 まずは、「プロトコル」という言葉について確認します。 プロトコルって? プロトコルとは、「約束された手順・ルール」のことです。 通信の世界におけるデータの送受信は、プロトコルと呼ばれる共通のルールのもとで実現されています。人間のコミュニケーションで例えると、日本語や英語などの共通の言語を用いることで情報の伝達を実現しているのと同じイメージです。 しかし、勝手にプロトコルを定めていくとその数は爆発的に増加し、ルールを知っている機器のみが通信できる状態になってしまいます。これは、地域・国ごとに異なる方言・母国語が存在している状況下で、コミュニティー間で言語による情報伝達に支障が生じる状況に似ています。 そのため、プロトコルは規格化(共通ルール化)されていき、様々なセンサーやデバイス間で共通のプロトコルを用いた通信が可能になります。グローバルなビジネスの場では、多くの人がコミュニケーションできるように英語を利用するようなイメージです。 センサープロトコル変換 さて、センサーの通信プロトコルも多岐にわたるため、やり取りされるデータ形式も多種多様です。そのため、センサーが利用するプロトコルやデータ形式に応じて、データを解釈する必要があります。 例えば、温湿度センサーからデータを受信する以下のようなケースを考えてみます。この場合、センサーのデータ形式に基づいて「今は温度27℃で湿度72%だな」と解釈できます。 # 例:温湿度センサー # 表現形式: 電文の4Byte の先頭 2Byte が温度(℃)、残り 2Byte を湿度(%)を表す 1b48 → 0001101101001000 → 00011011 , 01001000 → 27 , 72 → 27 ℃, 72 % また、プロトコルには手順・ルールだけでなく、このデータ形式まで規定しているものもあります。機器/メーカーによらずデータ形式を規定しているプロトコル(EnOceanなど)や、機器/メーカーごとに独自のデータ形式を用いるプロトコル(Modbus、BLE、LoRaWANなど)などがその例です。前者は機器/メーカーによらずソフトウェアを共通化でき、後者は機器/メーカーごとに開発が必要になります。 つまり、 各機器ごとにデータを解釈するためのプログラムが必要 となる可能性があるということです。 # 【参考】プロトコルの分類例 # 用途・通信距離等の分類 ・産業系プロトコル(Modbus など) ・近距離無線通信系プロトコル(Wi-Fi、BLE、Wi-SUN、ZigBee、EnOcean など) ・LPWA系プロトコル(LoRaWAN、Sigfox、LTE-M など) # データ形式での分類 ・データ形式を規定しているプロトコル(EnOcean など) ・独自のデータ形式を用いるプロトコル(Modbus、BLE、LoRaWANなど) STEP2:「データ形式変換」 センサーから得られたデータをクラウド側が要求するデータ形式に変換します。具体的には、対象とするクラウドが対応している以下のようなデータ形式へ変換します。 構造化データ(Excel、CSV、RDBデータなど) # 例:CSV key1,key2,key2 value1,value2,value3 半構造化データ(JSON、XMLなど) // 例:JSON { " key1 ": " value1 ", " key2 ": " value2 " } 非構造化データ(規則性のないテキスト、PDF、画像、音声、動画など) # 例:上記画像ファイルのビット列をHEX(16進数)表記したもの 89504e470d0a1a0a0000000d49484452 ... f87fbd0a6e6b89b8ca800000000049454e44ae426082 STEP3:「安全な通信」 クラウド側が対応している通信方式によってデータを送信します。多くのクラウドでは、HTTPS や MQTTS といった通信経路上を流れるデータを暗号化する通信プロトコルに対応しており、都度適切なデータ形式及び方法を用いて、変換後のデータを送信します。 データを送信するだけでも一苦労 いかがでしょうか、「データをクラウドに送信する」までにこのような STEP を経ることで、データの可視化やアラーム判定などのデータ利活用が開始できる状態になります。 ここまでで、「センサー/クラウドごとに開発がいるのか、大変そう」「データ利活用するまで先が長い」など少し難しそうだなと思われた方もいらっしゃるのではないでしょうか。 この記事を最後まで見ていただければ、「お手軽IoT」を始められますのでご安心ください。 「お手軽IoT」を実現するための道具として、「 Things Cloud 」というクラウドと、「 OpenBlocks 」というデバイスをご紹介します。 Things Cloud について Things Cloud は、NTT Communications の Smart Data Platform (略称SDPF) の中で、データ収集機能を提供するプラットフォームです。 センサーにつながる IoT デバイスからデータを収集し、蓄積できるクラウドサービスで、簡単な操作でデバイス情報を閲覧したりデータを可視化したり、ユーザー管理/通知/デバイス管理等、IoT に必要な機能一式が揃っています。 1 また、LoRaWAN 2 や Sigfox 3 ネットワークとの接続機能を有しているため、これらのプロトコルに対応したセンサーの選択が可能です。 それでは、 Things Cloud における「データ形式変換」と「安全な通信」について確認していきましょう。 Things Cloud における「データ形式変換」 Things Cloud では JSON 4 または CSV 5 形式のデータを要求するため、センサープロトコル変換等で得たデータを上記の形式に変換するプログラムが必要となります。 // 例:温度データ(JSON) { " c8y_TemperatureMeasurement ": { " T ": { " value ": 27 , " unit ": " ℃ " } } , " time ":" 2022-12-09T10:00:00.000+09:00 ", " source ": { " id ":" .... " } , " type ": " c8y_TemperatureMeasurement " } # 例:温度データ(CSV) 200 ,c8y_TemperatureMeasurement,T, 27 Things Cloud における「安全な通信」 Things Cloud では HTTPS(JSON 形式)または MQTTS(基本的には CSV 形式)に対応しており、都度適切なデータ形式及び方法を用いて、変換後のデータを送信します。 OpenBlocks について 本記事で紹介する OpenBlocks は、IoT-GW 機器として使うことができます。IoT-GW は、「データをクラウドに送信する」ための3つの STEP の処理を行う主体でもあります。 それでは、OpenBlocks が持つ「センサープロトコル変換」に関する機能に注目して、ご紹介していきます。 OpenBlocks における「センサープロトコル変換」 OpenBlocks は様々な通信規格やプロトコル(BLE・EnOcean・Wi-SUN・スマートメーター・Modbus等)を用いた主要メーカーの多種多様なセンサーに対応しており、Webブラウザ上での設定だけで接続できるという特長を持っています。 そのため、OpenBlocks が利用したいセンサーに対応していれば、「センサープロトコル変換」機能を開発することなく該当センサーを利用可能です。 6 「お手軽IoT」の始め方 ここまでで3つの STEP についてまとめると下記の通りです。 センサープロトコル OpenBlocks の持つ機能を利用することで多種多様なセンサーに対応可能 データ形式変換 Things Cloud が対応する JSON または CSV に変換するソフトウェアが必要 クラウドへの安全な通信 変換後のデータを HTTPS または MQTTS によって送信するソフトウェアが必要 上記の通り「データ形式変換」「安全な通信」の機能を持つソフトウェアを開発する必要があります。これらの機能を補完する「お手軽IoT」を始めるためのソフトウェア(以降、「データ送信用ソフトウェア」と呼びます) を開発しましたのでご紹介します。 データ送信用ソフトウェアは Node-RED 7 で実装されています 現在は EnOcean のみの対応 8 ですが、開発を継続して順次対応デバイス数を増やしていきます EnOcean の詳細については こちら をご参照ください 「お手軽IoT」の始め方について、EnOcean センサー利用を例にご紹介します。 データ送信用ソフトウェアのご利用については 問い合わせ先 からご連絡ください。 OpenBlocks の設定 OpenBlocks に関する操作・設定については、 OpenBlocks のマニュアル をご参照ください。 センサー接続 まずは、OpenBlocks にセンサーを接続します。 EnOcean の場合、無線プロトコルであるため物理的な接続はありませんが、機器によっては電源を ON にします。 センサー登録 利用するセンサーの情報を OpenBlocks に登録します。 センサー情報を登録するために、以下の項目を入力・保存します。 9 デバイスID:EnOcean におけるセンサーを識別する番号 ユーザーメモ:ユーザーが指定する任意の情報 EEP:EnOcean で定義されているデータ形式を示す番号 特に「デバイスID」「EEP」に入力する値は、筐体や取扱説明書などに記載されていることが多いため、そちらを参照します。 センサー情報を登録後、該当のセンサーに対して以下のように設定します。 10 初回は IoTデータ設定 及び ユニックスドメインソケット を参照して、以下の設定をしてください。 データ送信用ソフトウェアのインポート OpenBlocks 上で動作する Node-RED にデータ送信用ソフトウェアをインポートします。 Web ブラウザで Node-RED の実行環境にアクセスし、「読み込み」>「読み込むファイルを選択」にてデータ送信用ソフトウェアを選択します。 11 初回は OpenBlocks への Node-RED のインストール 及び アクセス方法 を参照して、必要な設定をしてください。 Things Cloud の設定 以下は Things Cloud 上での操作になります。 IoT-GW の ID 登録 Things Cloud に対して OpenBlocks の ID を登録します。 OpenBlocks 筐体の裏面に記載されている「SERIAL No.」の値を参照し、「デバイスID」に入力します。 接続の承認 Things Cloud で承認処理をします。 一定時間経過すると「承認」ボタンが出現するので、これをクリックします。 結果 上記の設定だけで Things Cloud にデータが送信・可視化されるようになります。「データをクラウドに送信する」までの3つの STEP がうまく隠蔽されていることを感じられたでしょうか。 まとめ IoT はビジネスにおける手段の1つ、だけど強力な手段 データ送信するまでには3つの STEP がある センサープロトコル変換 データ形式変換 安全な通信 Things Cloud と OpenBlocks を活用して今すぐ「お手軽IoT」をはじめよう 今後も NTT Communications は皆さんの IoT 活用をサポートしてまいります! 問い合わせ先 お手軽IoTを試してみたい方は、以下にお問い合わせください。 Things Cloud サービスについて:iot-info@ntt.com ※お手数ですが@を半角文字に置き換えてください それでは、明日の投稿もお楽しみに! https://engineers.ntt.com/entry/2022/03/01/130720 ↩ https://www.nttbizsol.jp/service/lorawan/about/ ↩ https://www.kccs.co.jp/sigfox/service/ ↩ https://developer.ntt.com/iot/docs/reference/measurements/ ↩ https://developer.ntt.com/iot/docs/device-sdk/mqtt/#mqtt-static-templates ↩ https://www.plathome.co.jp/partner-program/sensor-device/ ↩ https://nodered.jp/about/ ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/data_form/enocean ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/service/basic ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/data_handling/enocean ↩ https://nodered.jp/docs/user-guide/editor/workspace/import-export ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 8日目の記事です。 サマリ OSS 公開中の Go による SDN コントローラー Pola PCE の開発ノウハウを紹介 開発・公開・運用に際してやったことと得られた Tips を紹介 (CI・ドキュメント・コンテナ・その他 Go 関連) はじめに イノベーションセンターの三島です。 普段の業務では Multi-AS Segment Routing(SRv6/SR-MPLS)や Telemetry などの技術検証、BGP 技術の検証と AS 運用などを行っています。 この記事では、SDN コントローラーを OSS として公開して得た知見を、Go による開発支援や GitHub を通じた公開・運用の Tips を交えつつご紹介します。 公開した OSS: Pola PCE 経路制御技術の Segment Routing(SR)において、発行した経路を管理するための SDN コントローラーである Pola PCE を開発・公開しています。 Pola PCE を SR 網に導入することで、大規模なネットワーク運用やアプリケーション単位での通信品質向上などを実現できます。 (プロダクトの詳細は後日また本ブログで解説予定ですので、ご期待ください。) Pola PCE は Go で開発し、2022年6月に OSS としてリリースしました。 2022年12月8日時点では v1.1.2 が公開中で、Go のパッケージ、クロスコンパイルしたバイナリ、Docker イメージの3種を配布中です。 開発の経緯と OSS 化に踏み切った理由 NTT Com では Multi-AS SR 技術を用いた社内ネットワークを運用しています。 その中で複雑な Traffic Engineering のユースケースとスケーラビリティを実現するため、継続的な機能追加やニーズに応じた拡張性を持ち、Multi-vendor 環境でも動作可能なコントローラーが必要となりました。 これらの要求を満たすため、Pola PCE を開発し自社ネットワークへ導入しました。 開発の当たってのより詳しいモチベーションは下記でご紹介しています。 大規模 SR 網の運用を効率化するネットワークコントローラの開発(NTT Tech Conference 2022) Segment Routing 用 Stateful PCE をフルスクラッチで開発した話 ネットワークで活用するソフトウェアは、開発するだけでなく、その後の機能拡張や他製品との連携が重要となります。 継続的な開発を進める上で、下記の3点を期待し OSS としての公開に踏み切りました。 ユーザや対応製品の増加・PCE 界隈の盛り上げ ネットワーク運用者に広く利用されることによる、プロトコル対応製品の増加や外部ツールの充実 多彩な環境での動作を通じた新規ニーズの開拓や知見の蓄積 新規ユースケースの獲得や機能要望の期待 コミュニティ運用による機能追加や知見の獲得 外部の Contributor の視点を取り入れることによる新規知見の獲得や機能向上 以降の章では、これらを達成することを目指した OSS 開発・公開・運用の Tips をご紹介します。 OSS 開発・公開・運用の Tips 前章の要件を満たすため、下記のポリシーで OSS 公開手法を検討しました。 広く利用者を増やすための工夫 Example の充実やドキュメント整備、SEO 対策 多彩な環境への適用 クロスコンパイル・Docker 等での配布 運用・メンテナンスの効率化 CI の 活用や標準的なプロジェクト構成への準拠、関連ライセンス表示等の自動化 それぞれのポリシーを満たすために実施した Tips を、(1) GitHub での公開・運用知見、(2) Go の開発支援、(3) その他 OSS 開発・運用の工夫の3つに分けて解説します。 GitHub での公開・運用知見 リポジトリ運営 Community Standards の整備 GitHub の公式機能として、リポジトリを潤滑に運用するためのチェックリストが公開されている 特に Description は検索時に表示されるページ名や後述の Docker イメージのラベルにもなるため重要 GitHub Actions あらかじめ作成したワークフローを自動実行し、CI を実現する機能 フォーマットに関するレビューやリリースなどの定型作業の負荷を低減 Pola PCE のワークフロー では、Git で Tag を付与した際にクロスコンパイルされたバイナリと Docker image をリリースする仕組みを作成 ドキュメント公開& SEO 対策 GitHub Pages プロジェクト全体に関する情報公開と SEO 対策を目的とした Web ページ gh-pages ブランチ を作成し、Setting → Pages より公開設定 SEO 対策の視点でも、GitHub のリポジトリページは Google 等のクローラーに認識されるのが遅い傾向にあるため、リリース直後の検索用にも作っておくと良い Pola PCE の場合、GitHub Pages は公開後半日程、GitHub のリポジトリページは公開後約2週間後に Google 検索結果へ表示された 環境構築方法を gh-pages/README.md に記載済。誰でも PR ベースで編集可能 下記の Hugo と Docsy を利用し、ページ作成を効率化 Hugo Markdown で記載したファイルから動的に Web ページを生成するツール カスタマイズの仕方などは Hugo のページ にまとまっている テンプレート一覧 も存在 Template を活用しつつ、Markdown ベースで手軽にブログが作成できるのは便利! Docsy 技術文書を書くためのテンプレート。gRPC・Selenium・etcd など様々なソフトウェアが使用中 各団体が 公式ページ で Example を提供している OSS プロダクトらしい公式ページを高速に開発可能。1 時間ほどで必要な機能・見た目のページを作ることができて助かった GitHub Container Registry(ghcr)による Docker イメージのリリース GitHub 上で Docker イメージを提供する機能 Docker Hub と異なり、公式機能で GitHub Actions との API 連携が可能 公式ワークフローを利用するだけで、リポジトリ情報・バージョンやライセンス情報などがラベルとして埋め込まれた Docker イメージを生成可能( 例 ) Docker イメージのも含め、全てを Github で完結して公開できるのは良い点 一方、Docker Hub と違いレジストリ名(ghcr.io)が必要になるため、イメージ名が長くなるデメリットも存在 その他 README.md の整備 トップページの README.md には、CI 状態や関連ページなどを Badge として付与 各階層にも README.md を配置し、ツールの使い方等を解説 コマンドの使い方や仕様などの記載は特に重要 main ブランチの保護 latest リリース破壊の防止 Setting → Branches → Branch protection rules Go の開発支援 プロジェクトの構造を Standard Go Project Layout に準拠 Standard Go Project Layout 可読性・保守性の高い Go プロジェクトのベストプラクティス 各ファイルの役割が明確になると共に、Go の開発者が理解しやすいプロジェクト構造になった linging/formatting golangci-lint CI から呼び出し、Go 関連の linting/formatting を行ってくれる機能 Push の度に GitHub Actions で実行 次の Go Report Card と合わせ、gocyclo のパラメータを調整することを推奨 Go Report Card lintng/formatting や 英語の綴りなどを確認し、Web 上で表示するサービス 設定不要でコードの確認ができるので便利 Go の文法的な lint/format の他、英語のスペル確認なども実行してくれる 結果は Badge として README.md に表示可能 editorconfig コードの統一性を持たせるための linter/formatter 開発者の環境を問わず、生成されるコードを統一する意図。 CI 実施前のチェックに利用 Vim/Emacs/VSCode など様々なエディターに導入可能 Go の場合その他の linter が充実しているため恩恵は薄いが、Markdown/YAML 他様々な形式に対応 いずれは開発環境の整え方のような手順を作る、あるいはネットワーク環境を含めた開発用コンテナを公開すると良さそう その他 gocredits Go で使用したライブラリのライセンスをまとめて表示してくれるツール OSS として公開する上で重要なライセンス管理をサポート 今のところリリースごとにローカルでコマンドを実行して利用中 Tag を付与した際、あるいは go ファイルが更新された際に自動実行するなど、CI に組み込むのが良さそう pkg.dev.go Go 公式のドキュメント機能 go install するだけでコードを自動解析してドキュメントを作ってくれるのは便利 その他 OSS 開発・運用の工夫 Getting Started や Example の整備 ツールの利用方法と試しやすい手順を公開することでユーザを増やす狙い Example は誰もが手軽に試すことができ、なるべく同じ環境で再現できると Good 手軽さと再現性の両立にはコンテナ環境がおすすめ Pola PCE は Tinet の設定ファイルを公開 Docker さえ準備すれば、誰でも気軽に設定済みの SR ネットワーク(D-Plane/C-Plane)を利用可能に! 実際に手元で動かした結果をもとにした問い合わせや感想もいただくことができた 公式アイコンの作成 トポロジー図・システム図等でソフトウェアを示すことができ、発表資料の顔にもなるので重要 遠目に Pola や PCE の P となるようにデザイン 公式ページや OSS の各所で利用するためのテーマカラーを決めておくと便利 Pola PCE では #81C0FC と #03498D の2色をテーマカラーとして設定、アイコンや GitHub Pages のテーマに利用 ステッカーを作成、JANOG 50 等のイベントで配布 興味を持っていただけた方が検索可能なように、名前入りのロゴを作成 自身で図やスライドを作成する際も便利であり、またこれをきっかけにお声がけいただいたりもしたため、やってよかった取り組みの1つ まとめ 本記事では、Pola PCE の開発経験を通じて得た知見を、Go 製 OSS の開発支援と・GitHub における公開の Tips としてご紹介しました。 各 Tips に記載した通り、各 CI やベストプラクティスに従うことでリポジトリ公開・運用やドキュメントの整備等を効率的に進めることができたと感じています。 より Pola PCE に寄せた感想としては、OSS としての公開を通じて、 研究での利用をしていただくなど、事前に期待した通りの一部 PCE 界隈の盛り上げやユースケースの創出に貢献できたかなと感じています。 また、今回はネットワークに関するソフトウェアであるため、 公開した Example ネットワーク環境を手元で動かした結果をもとに感想や問い合わせをいただくなど、裾野を広げることができたのもよかったポイントです。 OSS 公開を検討中の方や、開発に興味のある方はぜひそれぞれの Tips を参考にしてみてください。 また、その他こんな方法もあるよという知見をお持ちの方は、ぜひはてなブックマークやTwitterでコメントをお願いします! 宣伝 Pola PCE への Contribution 募集 Pola PCE の Contributor を募集中です! SR 網を運用中で、SDN 制御による運用効率化や新たなサービスを提供してみたい方 RFC/Internet-Draft 準拠のソフトウェアを開発してみたい方 お気軽に PR/Issue の作成をお願いします! 12/19 (月) に SR 視点で Pola PCE の内部構造と使い方を紹介する記事を公開予定ですので、ぜひそちらもご覧ください! また、PCE の活用事例も含め、SR の検証例を 連載記事 としてご紹介しています。こちらも合わせてご覧ください。 冬季インターンシップ開催のお知らせ 2022年度インターンシップの参加者を募集中です! 冬インターンシップ2022 公式ページ: 現場受け入れ型インターンシップ インターンシップ開催に関するブログ記事: 【コムで踏み切れ。】 冬期インターンシップを開催します! 期間は 2023年02月06日〜17日 のうち、土日祝を除く実働10日間です。 締め切りは 12月14日(水)13:00 までとなっていますので、興味のある方はお早めにご応募ください! 私たちのチーム( SR を用いたキャリアネットワークの開発 )では、SR-MPLS/SRv6 の技術検証や Pola PCE を含めた SR 周辺技術の OSS 実装等のテーマを用意しています。 また、参加者の希望するテーマをご提案いただくこともできます。 昨年は2名の方に参加いただき、FRRouting への BGP-LS の実装と、キャリアルーターを用いた SRv6 VPN の技術検証を実施していただきました。 体験記を寄稿してもらっていますので、こちらもぜひ参考にしてください。 インターンシップ体験記 〜BGP-LSの機能をFRRに実装してみた〜 インターンシップ体験記 〜SRv6 L3VPN機能検証〜
アバター
この記事は、 NTT Communications Advent Calendar 2022 7日目の記事です。 はじめに こんにちは、イノベーションセンター所属の志村と申します。 「Metemcyber」プロジェクトで脅威インテリジェンスに関する内製開発や、「NA4Sec」プロジェクトで攻撃インフラの解明・撲滅に関する技術開発を担当しています。 今回は「開発に使える脆弱性スキャンツール」をテーマに、GitHub Dependabot, Trivy, Grypeといったツールの紹介をさせていただきます。 脆弱性の原因とSCAによるスキャン 現在のソフトウェア開発は、多くのOSSを含む外部のソフトウェアに依存しています。Python、Go、npm など多くの言語は、様々なソフトウェアをパッケージとして利用できるエコシステムを提供しており、この仕組みを利用してOSSなどのコンポーネントをソフトウェアに組み込んで活用するのが一般的です。 外部パッケージを利用することで効率的な開発が可能になる一方で、それらに含まれる脆弱性の影響を受けてしまうリスクが増加しています。Snyk社のレポート 1 によれば、オープンソースのソフトウェアの脆弱性の約80%は間接的に依存しているパッケージによりもたらされるとされています。 そのため直接的に利用しているパッケージだけでなく、それらの依存先のパッケージまで含めて把握し、脆弱性がないか管理する必要があります。 ソフトウェアの依存関係を解析する手段として、SCA (Software Composition Analysis) と呼ばれるツールの活用が挙げられます。 SCAを活用してソフトウェアの依存関係から脆弱性を発見し、それらに対して適切に処置をする、ということが複雑化するソフトウェア開発における脆弱性対応では重要です。 SCA型のスキャンツールの紹介 脆弱性対策に使えるSCAは有償のものも含めて多くありますが、今回は無償で使える以下の3つを紹介します。 Dependabot Trivy Grype GitHub Dependabot 概要 GitHub Dependabot はRepositoryの構成要素を分析し、脆弱な依存関係を発見・通知するGitHubの機能です。 GitHub Dependabotの機能はPrivate Repoであっても無償で利用できるため、GitHubを利用しているならば万人にお勧めできます。 2 データソース GitHub Dependabotは、既知の脆弱性やマルウェアの情報が含まれるデータベースである  GItHub Advisory Database に含まれる脆弱性が通知されます。 NVD や各種言語のセキュリティアドバイザリの情報がデータソースとなっています。 GitHub Advisory Databaseの情報はGitHub独自の脆弱性IDで管理され、脆弱性の詳細や影響を受けるバージョン、修正済みのバージョンなどの情報が含まれています。 GitHub Advisory Databseの中でも、GitHubがサポートするエコシステムにマッピングされた脆弱性/マルウェアの情報を GitHub-reviewed Advisory と呼び、この情報がDependabot で通知されます。 GitHub-reviewed AdvisoryはGitHub社によるレビューやパッケージシステムとの対応づけが完了しており、影響を受けるバージョンや修復済みのバージョンの情報も含まれているため、対応策定に役立てることができます。 本ブログ執筆時点で対応しているエコシステムは以下の通りです。 Composer (registry: https://packagist.org/) Erlang (registry: https://hex.pm/) Go (registry: https://pkg.go.dev/) GitHub Actions ( https://github.com/marketplace?type=actions/ ) Maven (registry: https://repo.maven.apache.org/maven2) npm (registry: https://www.npmjs.com/) NuGet (registry: https://www.nuget.org/) pip (registry: https://pypi.org/) pub (registry: https://pub.dev/packages/registry) RubyGems (registry: https://rubygems.org/) Rust (registry: https://crates.io/) 使い方 Dependabot を利用するには、Repositoryページの Settings -> Code security and analysis にアクセスし、 Dependency graph および Dependabot 関連の機能をEnableにする必要があります。 GitHub Dependency Graph はRepositoryの中の言語依存ライブラリの管理ファイルなどを解析し、依存しているライブラリの一覧を抽出する機能です。 Dependency Graph で解析できる対象は About the dependency graph のドキュメントを参照してください。 Dependency Graphを有効にするとソースコードが解析され、依存パッケージが確認できる様になります。 解析結果はRepositoryページの Insight → Dependency Graph にアクセスすることで確認できます。 Dependabot alertsを有効化すると、GitHub Advisory Databaseに関連する情報が登録されると通知される様になります。 Dependabot alertsの情報は、Repositoryの Security -> Dependabot から確認できます。 Dependabot security updates を有効化していると、パッケージのバージョンを脆弱性修正バージョンに上げるPull Request が自動的に発行されます。 Trivy 概要 Trivy はGo製のツールで、Linux, Windows, Mac いずれの環境でも動作します。 Trivyはセキュリティのアーミーナイフをうたっており、以下のような多様な機能を有しています。 脆弱性スキャン OSパッケージ、言語のパッケージをスキャンし、脆弱性を発見する Secret スキャン 鍵ファイル (AWSキー、slackキーなど) が存在してないかを確認する Configスキャン Terraform 設定ファイルやDockerfileなどをスキャンし、セキュリティ上問題になりうる設定が含まれてないかチェックする いずれの機能もソフトウェアの安全性を高めるために有効ですが、今回は脆弱性スキャンについて取り上げます。 Trivyの脆弱性スキャンはファイルシステムなどを解析し、依存しているOSや言語のパッケージを抽出し、既存の脆弱性情報とマッチングすることで脆弱性の有無を判断します。 Trivyが脆弱性の有無を解析できる対象は以下の通りです。 OSパッケージ apt, yum などでインストールしたOSパッケージ情報を抽出し、脆弱性を表示する 言語パッケージ 言語の依存ライブラリの管理ファイル (pacakge-lock.json, pipenv.lock など) をスキャンして、パッケージのバージョンを取得し、脆弱性を表示する データソース Trivyの脆弱性スキャンは、Trivyが収集したOSや言語パッケージのバージョン情報を、Trivyの脆弱性DBとマッチングすることで行われます。 Trivyが利用する脆弱性DBは trivy-db というツールで作成されており、DB自体の更新もこのtrivy-db Repositoryの GitHub Actions で実行されています。 このDBは6時間ごとに更新されており、最新の脆弱性情報を参照できます。 Trivyがどのようなデータソースから情報を収集しているかは、Trivyドキュメントの Data Sources やtrivy-dbの ソースコード から読み解けます。前述したGitHub Advisory Database もデータソースに含まれています。 主なデータソースは以下の通りです。 GitHub Advisory Database Open Source Vulnerabilities GitLab Advisories Community NVD 各種OS/言語の脆弱性DB 使い方 Trivyの脆弱性スキャンの対象は以下の通りです。 コンテナイメージ ファイルシステム GIt Repo コンテナイメージのスキャン コンテナイメージをスキャンする場合は、 trivy image コマンド を利用します。 trivy image python:3.4-alpine 出力は以下の様になります。 イメージスキャンは コンテナイメージを指定して、その中に含まれるOSパッケージや言語ライブラリの脆弱性をスキャンできます。 ファイルシステムのスキャン ファイルシステムスキャンをする場合は、 trivy fs コマンド もしくは trivy rootfs コマンド を利用します。 trivy fs /path/to/project trivy rootfs / ファイルシステムをスキャンして、OSパッケージのファイルや言語のパッケージの設定ファイル (npm の package-lock.json など) を解析してバージョン情報などを抽出し、既知の脆弱性が発見された場合に通知します。 fsコマンドは主にローカルにあるプロジェクトのスキャン、rootfsコマンドはRootfsのスキャン (コンテナ内部でスキャンや、コンテナイメージのファイルのスキャンなど) での利用を想定されています。 参考 例えばPythonでは、fsコマンドだと Pipfile.lock などのパッケージの管理ファイルからパッケージとバージョンを抽出するのに対し、rootfsコマンドでは site-packages/ ディレクトリなどをスキャンして実際にインストールされているパッケージとバージョンを抽出する、という挙動の違いがあります。開発中のプロジェクトのソースコードをスキャンしたい場合はfsコマンド、コンテナやホストマシン内部など今動いている環境のスキャンはrootfs、の様に使い分けるのが良いでしょう。 fsコマンドとrootfsコマンドのスキャン対象の違いについては Trivyのドキュメント なども参照してください。 スキャンの挙動設定 Configファイル (trivy.yaml)を利用することで、スキャンの挙動を設定できます。設定可能な内容は  ドキュメント を参照してください。 また .trivyignore に脆弱性ID (CVE-ID) を記載することで、その脆弱性を無視できます。 スキャンフォーマットの指定 Trivyはスキャン結果の出力フォーマットが指定可能です。デフォルトのtableフォーマットはスキャン対象・脆弱性・Severityなどが表示され、視認性が高いフォーマットです。 それ以外にもjsonフォーマットなどを指定できます。 jsonフォーマットで出力すると、tableフォーマットでは出力されないスキャンターゲットのファイルパス ( PkgPath )などの情報が表示されます。 rootfs スキャンでマシン全体をスキャンした際など、どこで脆弱性が検知されたかわからない、という場合に有効です。 json オプションを使用する場合は --output オプションと組み合わせてファイルに出力するのが良いでしょう。 CIへの組み込み trivy-action を利用することで、GitHub Actions上でのTrivy実行が可能です。 GitHub Actionsでの設定方法は以下のようになります。以下の例では severity などの情報をworkflow上で記載していますが、 Trivy Configファイルに必要な設定を記載してRepoに配置しておき、そのファイルを  trivy-config で参照することもできます。 (ただし trivy-config を指定すると、 scan-type や scan-ref 以外のオプションは無視されるため注意してください。) name : build on : push : branches : - master pull_request : jobs : build : name : Build runs-on : ubuntu-20.04 steps : - name : Checkout code uses : actions/checkout@v2 - name : Build an image from Dockerfile run : | docker build -t docker.io/my-organization/my-app:${{ github.sha }} . - name : Run Trivy vulnerability scanner uses : aquasecurity/trivy-action@master with : image-ref : 'docker.io/my-organization/my-app:${{ github.sha }}' format : 'table' exit-code : '1' ignore-unfixed : true vuln-type : 'os,library' severity : 'CRITICAL,HIGH' SBOMの出力、SBOMファイルのスキャン Trivyはスキャン結果からSBOMを出力したり、SBOMファイルから脆弱性スキャンを行うこともできます。 SBOM活用の詳細については 12/1のアドベントカレンダー もぜひ参照してください。 Trivyで SBOMファイルを作成 する場合は、 --format で出力したいSBOMフォーマット ( spdx-json or cyclonedx ) を指定します。 trivy image --format spdx-json --output result.json alpine:3.15 TrivyでSBOMファイルをスキャンする場合は、 trivy sbom コマンド でSBOMファイルを指定します。 trivy sbom /path/to/spdx.json Grype 概要 Grype はTrivyより後発のセキュリティスキャンツールです。SBOMツールである Syft と連携して動作し、スキャン結果のSBOMファイルへの出力や、SBOMファイルを利用した脆弱性スキャンが可能です。 データソース Grypeのデータソースは Github で参照できます。 基本的にはTrivyと類似したデータソースになっています。 GitHub Advisory Database Open Source Vulnerabilities GitLab Advisories Community NVD 各種OSの脆弱性データベース Grypeのスキャン Gypeは以下のスキャンに対応しています。 コンテナイメージ OSパッケージ 言語のパッケージ SBOMファイルのスキャン CycloneDX, SBOM, syft形式のスキャンが可能 Grypeのスキャンは以下の様に行います。 grype python:3.4-alpine 出力は以下の様になります。 $ grype python:3.4-alpine ✔ Vulnerability DB [updated] ✔ Parsed image ✔ Cataloged packages [31 packages] ✔ Scanned image [106 vulnerabilities] NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY busybox 1.29.3-r10 apk CVE-2021-42379 High busybox 1.29.3-r10 apk CVE-2021-42376 Medium busybox 1.29.3-r10 apk CVE-2021-42385 High busybox 1.29.3-r10 apk CVE-2021-42378 High busybox 1.29.3-r10 apk CVE-2021-42381 High busybox 1.29.3-r10 apk CVE-2021-42380 High SBOMファイルを元にスキャンする場合は以下の様になります。Syftによって生成されたSBOMファイルなら正確な検知が可能になっています。 grype sbom:./spdx.json 脆弱性スキャンツールの使い分け ここまでGitHub Dependabot、Trivy、Grypeについて紹介しました。 これらのツールを活用することで、脆弱性の発見と対処が容易になります。 どの様に脆弱性スキャンツールを使い分けていくかはプロジェクトの状況などによりますが、個人としては以下をお勧めします。 GitHubを利用しているなら、Dependabot を利用してソースコードの脆弱性を検知・対応する コンテナイメージなどのソースコードのみではスキャンできない対象をCI/CDのプロセス内で検知したい場合、 trivy-action などを活用することでTrivyをCI/CD に組み込む デプロイ先の環境 (VM、コンテナなど) のスキャンを実施したい場合は、Trivyでrootfs や imageスキャンを実施する SBOMを用いた構成管理および脆弱性スキャンを実施したいなら、Syft + Grype の組み合わせで運用する 開発にGitHub を利用しているなら、まずはDependabotを有効化してしまって良いと思います。 GitHub Advisory Databseは優秀な脆弱性DBであり、その内容を通知してくれるDependabot を有効化することで迅速な対応が可能になります。 GitHub Dependabotではカバーできないコンテナイメージのスキャンなどを実施したいなら、TrivyをCI/CDに組み込むのがお勧めです。 Trivyはスキャンが高速なため、開発への影響を最小限に抑えつつセキュリティレベルを向上させることが期待できます。 GrypeはSyft との連携が念頭に置かれています。 Syftは強力なSBOMジェネレータのため、SBOMの生成にSyftを使っているのならGrypeと合わせて運用するのが良いと思われます。 CI/CDで実現する継続的な脆弱性スキャン Metemcyber PJではDependabotとTrivyを開発に組み込んでいます。 今回はTrivyを使ったCICDを例として取り上げ、どのようにCI/CD で継続した脆弱性スキャンを実現するかを紹介したいと思います。 trivy-action の活用 私たちは trivy-action を利用して、GitHub Actionを利用した脆弱性のスキャンを実施しています。 以下は trivy-action を利用して、Pull Request 時と mainへのpush時にfsスキャンを実施し、Trivyスキャンで脆弱性が発見されたらGitHub Actionsをfailさせる例 (一部抜粋) です。 name : Pipenv CI on : pull_request : branches : - main push : branches : - main workflow_dispatch : jobs : build : steps : - name : Check out code from GitHub uses : actions/checkout@v3 - name : Run Trivy vulnerability scanner in fs mode uses : aquasecurity/trivy-action@master with : scan-type : 'fs' scan-ref : './api' trivy-config : trivy.yaml Actionsで参照している trivy.yaml は以下の様になります。 debug : true exit-code : 1 severity : - HIGH - CRITICAL 上記のようなGitHub Actionsと trivy.yaml を用意しておくことで、Pull Requestを行うと自動的にTrivyスキャンが実施されます。 trivy-action はデフォルトで脆弱性スキャンとSecretスキャンを行うため、ソースコード内にトークンなどの機密情報が含まれていないかのチェックも可能になっています。 trivy.yaml の severity を用いると、スキャンの対象とする脆弱性の脅威レベルを設定できます。 上記の例ではTrivy基準でHigh以上の脆弱性を検知すると、 exit-code: 1 の設定によりActionがfailし、脆弱性を発見できる仕組みになっています。 Pipenvの採用 上記の仕組みを実現するために、Pythonのパッケージマネージャーとして pipenv を採用しています。 Pythonで環境を構築するには、 requirements.txt にインストールしたいパッケージを記述しておき、pipでインストール方法もあります。 pip install -r requirements.txt しかし私たちは以下の理由でpipenvを採用しています。 依存するパッケージを Pipfile.lock で明確化してTrivyでスキャンできる 開発環境でしか利用しないパッケージを分離し、Trivyのスキャンの対象外にできる Trivyのfsスキャンでは、 requirements.txt に記述されていない、間接的に依存するパッケージはfsスキャンで検知できません。 そのため requirements.txt に直接参照するパッケージのみを記述している場合、実際にインストールされているパッケージの脆弱性を見逃すことがあります 3 。 pipenvでは実際にインストールされるパッケージが Pipfile.lock に記述され、Trivyのfsスキャンで検知可能となります。 またpipenvでは、 --dev オプションを利用することで、開発環境のみで使うパッケージを別枠でインストールできます。 pipenv install --dev autopep8 Trivyではdevオプションでインストールしたパッケージは スキャン対象外 となります。これにより、プロダクション環境に存在する脆弱性のみを検知することが可能になります。 12/1のアドベントカレンダー でも言及がありましたが、脆弱性対応を適切に行うにはパッケージマネージャーの選定も重要になります。 pipenv を利用することで、実際にインストールされるパッケージ全てのスキャンや、devDependencies のスキャン対象からの除外を実現できるので、Trivy を活用する場合は採用を検討することをお勧めします。 まとめ 本記事では、DependabotやTrivy、Grypeといったスキャン系のツールを紹介しました。 ツールごとに強みがあるので、開発体制などに応じて使い分けていくことでセキュリティの向上が期待できます。 ただし、これらのツールを使えば全ての問題が解決するかというとその様なことはなく、スキャン結果をどのように運用に組み込むかは別途考えなくてはなりません。 脆弱性が発見された際に即時アップデートをするか、それとも通常のリリースサイクルで対応するのか。 修正バージョン自体存在しない脆弱性が発見された場合どうするかなど、実運用ではさまざまな課題に直面します。 これらは開発しているソフトウェアの性質や、ソフトウェアが取り扱う情報資産の重要度などに合わせて適切に決定していく必要があります。 今回紹介した脆弱性スキャンツールは、CICDを活用した開発プロセスへ導入し、早期に脆弱性を発見することで効果を発揮します。 そのためセキュリティの視点だけでなく、利用するパッケージマネージャーや開発・デプロイのプロセスといった視点も含めて改善していくことで、効果的な脆弱性対応のプロセスを実現できるでしょう。 宣伝 私たちはMetemcyber という、セキュリティインテリジェンスやTrivyなどのツールの結果を管理し、セキュリティアクションを実施していくツールを現在開発中です。 今後 MetemcyberのTwitterアカウント にて情報発信していく予定です。このアカウントでは  セキュリティインテリジェンスの発信 などの活動も行っているので、ぜひフォローしてください。 それでは、明日もお楽しみに。 https://go.snyk.io/rs/677-THP-415/images/State%20Of%20Open%20Source%20Security%20Report%202020.pdf ↩ https://github.co.jp/pricing.html ↩ pip freeze を利用してインストールされているパッケージを全て出力することでfsスキャンによる検知が可能になりますが、直接依存するパッケージと間接的に依存するパッケージが混在してしまいます。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 6日目の記事です。 はじめに こんにちは、SDPF クラウド・仮想サーバチームの松下です。 普段は OpenStack の開発・運用をしているエンジニアで、今年から新入社員としてJOINしました。 今回は、細々と取り組んでいるOSを自作する個人的な活動についてお話ししつつ、ちょっと普通とは違う開発にチャレンジする同志を増やしたいなと思い執筆しております。 人の子であれば、一度は何か古くからある難しそうなソフトウェアの自作に取り組みたくなるものです(主語デカ発言) 私も例に漏れずその一人で、現在RustでOSを自作しようとしているところです。 自作するOSは、「ゼロからのOS自作入門」 1 (通称「MikanOS本」)のお題であるMikanOSです。 このMikanOS本は、言語としてC++を利用しOSを自作していく本で、サポートページやGitHubが今もなお更新されるほど 注目されている名著・プロジェクトとなっています。 私の活動の特徴 MikanOSをRustで書くこと自体はやはり時代の潮流もあり、すでに行っている方々がいらっしゃいます。 その方々の実装を見ますと、基本的には"uefi-rs" 2 と呼ばれるCrateを用いて実装しているようです。 uefi-rsは、RustでUEFIアプリケーションを作成するためのCrateであり、MikanOSはUEFIを前提としているためこちらを用いて開発することが王道の戦略だと思います。 一方で私のRikan 3 は、このuefi-rsを使わずに開発を進めています。 uefi-rsを使わずに実装していくことによって、MikanOS本で開発するもの全てを理解できるのでは?と考えた末の戦略です。 この戦略の開発はMikanOS本ではあまり触れられないところを理解する必要があったり、 OS起動以前に大量の実装をする必要があるなど純粋な「自作OS」の範疇を少し超えるようなことをする必要があります。 しかし、それによってあまり知られていなさそうなことを知れたり「UEFI Specificationが愛読書」みたいなことが言えたりしちゃうのでとても楽しいです。 この記事では、皆さんにそれを始める最初のステップとして、UEFIで"Hello World"をするコードを解説しようと思います。 RustでUEFIアプリケーションを作る 今回利用するコードは私のRikanプロジェクトの最初のコミット 4 のものになります。 このプログラムは2つのファイルがメインとなりますので、その2つについて解説しようと思います。 まずは、全体の動きを説明するため、main.rsの中身を以下に載せます。 #![no_std] #![no_main] #![feature(abi_efiapi)] use core :: panic :: PanicInfo; use utf16_literal :: utf16; mod uefi ; #[no_mangle] pub extern "C" fn efi_main (ImageHandle: uefi :: EFI_HANDLE, SystemTable: & uefi :: SystemTable) -> uefi :: EFI_STATUS { let _conout = SystemTable. ConOut (); _conout. Reset ( false ); _conout. OutputString ( utf16! ( "Hello World \r\n " ). as_ptr ()); loop {} uefi :: EFI_STATUS :: Success } #[panic_handler] fn panic (_panic: & PanicInfo < '_ > ) -> ! { loop {} } Hello Worldするだけですので複雑なコードではありませんが、普通のRustプログラムではあまり見かけないものが書かれていると思います。 UEFIの環境ではOS起動以前の環境ですので標準ライブラリにあたるものは利用できません。 従って、1行目にあるように #![no_std] とすることでRustの標準ライブラリを用いない core と呼ばれる最低限のライブラリを使う環境で動かすことになります。 これによって、普段あまり気にすることのないpanic時にどうするかも自分で実装する必要があります。 これが最後の方の #[panic_handler] 以降の部分にあたり、今回はループし続けるだけの実装にしています。 メインの処理ですが、これは efi_main 関数の中身となります。 UEFIの仕様書ではUEFIが規定するデータ型とC言語の呼び出し規約 5 を利用するものとして定義されていますので、RustのABIではなくUEFI仕様書に沿ったものを利用するよう指定していく必要があります。 これは後述する構造体や列挙体にも当てはまります。 UEFI アプリケーションのエントリーポイントは、 extern "C" とすることでUEFI仕様に則った形とし、 マングリングされないように #[no_mangle] も追加します。 UEFIアプリケーションは、エントリーポイントで2つの値を受け取ることになります。 このうち、SystemTableの方が重要で、UEFIの各種機能を呼び出すためのアドレス群を格納する構造体へのポインタとなっています。 画面にテキストを出力する「Simple Text Output Protocol」と呼ばれる機能や、OS起動以前に利用できる機能が詰まっているBoot Serviceを利用する場合には、 この構造体を経由してそれらを呼び出します。 Hello Worldをする際には、前者の機能を利用するため efi_main 関数の最初でこの機能の呼び出しの準備をしています。 SystemTableの説明をするために、次にuefi.rsの抜粋を以下に示します。 (構造体のメンバも削っているので、実際のコードとはかなり違います。) #[repr(C)] pub enum EFI_STATUS { Success = 0 } type CHAR16 = u16 ; pub struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { Reset: extern "efiapi" fn (This: & EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, ExtendedVerification: bool ) -> EFI_STATUS, OutputString: extern "efiapi" fn (This: & EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, String : *const CHAR16) -> EFI_STATUS, } impl EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { pub fn Reset ( & self , ExtendedVerification: bool ) -> EFI_STATUS { unsafe {( self .Reset)( self , ExtendedVerification)} } pub fn OutputString ( & self , String : *const CHAR16) -> EFI_STATUS { unsafe {( self .OutputString)( self , String )} } } #[repr(C)] pub struct SystemTable { ConOut: *mut EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, BootServices: *mut EFI_BOOT_SERVICES, } impl SystemTable { pub fn ConOut ( & self ) -> &mut EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { unsafe { &mut * self .ConOut} } } 先ほども述べたようにSystemTableという構造体は、各種機能を呼び出すためのアドレスがメンバとなっています。 下方にあるSystemTableの構造体でそれらを定義しています。 エントリーポイントの時にも述べましたが、UEFIを利用するためにはC言語のABIに沿う必要があるため、 構造体の上に #[repr(C)] をつけることによってRustコンパイラにそれを示しています。 6 C言語では、アドレスさえわかればそれをもとに関数ポインタを作って呼び出せば良いのですが、 Rustぽく実装するためにアドレスを格納している構造体に対して impl してラッパーを作り呼び出しています。 UEFIの機能を呼び出すと返り値に EFI_STATUS が返却されますが、RustとしてはやはりResult型で表現したいためこのようにしています。 (この例ではそこまでやっていませんが...) 文字列を表示するためには、EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL構造体のメンバ変数であるOutputStringに格納されている関数にUTF16文字列のポインタを渡してあげれば良いため、 main.rsでは以下のようにすることでHello Worldを画面に表示できます。 _conout. OutputString ( utf16! ( "Hello World \r\n " ). as_ptr ()); uefi-rsを使わないMikanOSの実装の進め方 基本的には、Hello Worldで示したようなコードをひたすら書いていくだけになります。 つまり、以下のサイクルを回していく作業になります。 MikanOSの実装を読む 実装に必要な構造体や列挙体、関数に必要な引数などをUEFI Specificationで探す Rustでそれらを書く Rustぽく動かせるようにする デバッグする もし実装につまれば、uefi-rsを読んだりコードを入れ替えたりしたりすることで何かヒントは得られるのかなと思います。 Rikanを開発する上で感じたこと Rikanを開発する上で思ったことは以下の5点です。 構造体をひたすら定義し続ける苦行が辛い Rustぽく動くようにimplし続ける苦行が辛い グローバルアロケータを実装するまでまともなprintfデバッグすらできなくて辛い PanicInfoを利用するとpanicが起きたときにどこの行が原因で起きているのかが出るのでありがたい uefi-rsは偉大 まだday3までしか進んでいませんが、一番の山場はグローバルアロケータを実装するところかなと思います。 これを実装するまでは、変数の中身を表示することが難しいのでデバッグの難易度がかなり高い状態での開発になります。 また、UEFIをちょっと使うためにも多くのUEFIで定義される構造体を実装する必要があるため、uefi-rsは縁の下の力持ちだととても感じています。 まとめ この記事は、MikanOSをRustで実装することで、UEFIという普段意識しないものに対する理解を深める最初の1歩になればと思い執筆しました。 ソフトウェア開発において、開発対象とする領域を下支えする裏側を意識する機会は少ないかも知れません。 ブラックボックスにすることによって集中すべきところにしっかり集中するというのが大事だからです。 しかし、一度トラブルなどが起きたときは、その裏側を知っておくとその解決が速くなったりすることがあります。 そういった意味で、エンジニアとしては裏側を知っておくということは重要になると私は考えています。 NTT Comでは現在、現場受け入れ型インターンシップを募集しており、以下のリンクから応募できます。 いろいろなサービスで利用されているクラウド技術の裏側を知ることのできるインターンシップになっていますので、 この記事を読んで「クラウドサービスの裏側を知りたい!」となったあなたからの応募をお待ちしております。 information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 http://zero.osdev.jp ↩ https://github.com/rust-osdev/uefi-rs ↩ https://github.com/bean1310/Rikan ↩ https://github.com/bean1310/Rikan/tree/2e5f7a4456531e53a41508a1d498f3beea53ee1a ↩ 仕様書中に記述は見つかりませんでしたが、Microsoftのx64 calling conventionを採用しているように見えます。 https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention ↩ ここのサイトがこの違いを解説しています。 https://ryochack.hatenablog.com/entry/2018/03/23/184943 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 4 日目の記事です。 こんにちは。 SDPF クラウド・仮想サーバーチームの杉浦です。 普段は OpenStack の開発・運用をしています。 みなさんはシェル芸と聞いてどのようなコマンドを想像しますか? 私は以下のような怖いコマンド 1 を想像していました # 無限に process を fork するコマンドです # 実行するときは自己責任でお願いします :(){ :|:& };: ですがシェル芸はもっと親しみやすくて 2 実用的なものです。 私はシェル芸のシェの字もできないくらいシェル芸初心者だったのですが、 1日1問、半年以内に習得 シェル・ワンライナー160本ノック という本を完走してシェル芸チョットワカルようになったので、本の宣伝をしつつ完走した感想を紹介しようと思います。 1日1問、半年以内に習得 シェル・ワンライナー160本ノック https://gihyo.jp/book/2021/978-4-297-12267-6 本を読む前は 本を読む前はシェル芸こそできなかったものの、 CLI 環境で十分生活できるくらいには bash とお友達でした。 cat や less 、 grep などの基本的なコマンドも使えましたし、 vim も難なく使いこなせていました。 ただ、例えば awk はほとんど使ったことがありませんでした。 xargs に関しては Stack Overflow ではたまにみるけどどういう動作をしているのかきちんと理解していませんでした。 テキストファイルの中身を解析するときは VSCode やスプレッドシートに貼り付けてなんとかしていました。 シェル芸をやってみようと思ったきっかけ ログファイルの解析やサーバーの管理の際にちょっとしたテキスト操作ができなくてもどかしい思いをしたからです。 仮想サーバーチームでは数千台規模の仮想サーバーを運用しています。 多数のサーバーを効率的に運用するために、開発用端末から SSH 経由でコマンドを叩いてサーバーの管理をするわけです。 あるとき、サーバーに搭載されているメモリ量を調べたいことがありました。 Linux では free コマンドを使うとメモリの使用状況を調べることができます。 $ free total used free shared buff/cache available Mem: 12235124 290432 4185192 5088 7759500 11720024 Swap: 0 0 0 調査対象が 1 台であれば Mem 行の total 列の数字をターミナルからコピーすればよいですが、複数のサーバーを横断的に調べたいときは Mem 行の total 列の数字を取り出す操作を自動化したくなります。 シェル芸がチョットワカルようになった今だったらいくつもの方法が考えられますが、当時は awk コマンドに馴染みがなかったのでコマンドがすっと出てこずに苦労しました。 どうやって解いたかはあまり覚えていませんが、 grep を使ったりスプレッドシートを使ったりしてなんとかしのいだ覚えがあります。 シェル芸本の読み方 シェル芸本では、シェル芸とは「Unix 系 OS のシェル上でワンライナーのコマンドを駆使すること」と定義されています。 冒頭で示したようなワンライナーだけではなく、日常の業務をスッと終わらせるために使うコマンドもシェル芸とみなせます。 この本では実践形式で手を動かしながらシェル芸を学びます。 コマンドの使い方を説明したあとに問題が出され、解説があります。 最初は問題があまり解けずつらい気持ちになるかもしれませんが、シェル芸の型が身につくとみるみる解けるように解けるようになって楽しいですよ。 最初は echo や ls コマンド、 Control + C の使い方から始まり、シグナルやファイルシステム、システムコールといった発展的なトピックも出てきます。 また、問題は初級・中級・上級に分かれていて自分のレベルに合わせて取り組むことができます。 Unix 初心者の方から上級者の方まで楽しむことができますね。 シェル芸本は 3 部構成になっています。 第 1 部: シェルとコマンドに親しむ 第 2 部: 発想力を鍛える 第 3 部: 応用する シェル芸初心者の方は最初から解いていくのがよいでしょう。 ただし、すべて解こうとすると時間がかかるので、必要なところをかいつまんで読んでもいいかもしれません。 例えば 文字コードとバイナリ で学ぶ内容は他の章ではあまり出てこないので飛ばしてしまってもいいでしょう。 第 3 部は第 1 部、第 2 部で身につけた知識を実際のサーバーの解析で応用できるかどうかを問う問題が出ます。 シェル芸に自身がある方は第 3 部から挑戦して、知識に不足があれば必要に応じで第 1 部、 2 部を参照する、といったやり方でもよさそうです。 シェル芸の構成要素 シェル芸は以下の要素が絡み合って構成されていると感じました。 基本コマンドの使い方 Bash の便利機能 オプション 便利なコマンド 正規表現の使い方 コマンドの組み合わせ方のパターン アート要素 基本コマンドの使い方 以下のコマンドはシェル芸でよく使います。 マニュアルやチートシートを見なくても使えるようにしておきましょう。 awk sed grep xargs wc sort uniq find cat date 基本的なコマンドの使い方は 第 1 章で解説があります。 Bash の便利機能 man bash すると bash の機能を確認できます。 例えば、以下の機能は知っておくと便利です。 Process Substitution <(command) >(command) Brace Expansion {n..m} {a,b,c} History Expansion fc !n ^CommandA^CommandB^ Parameter Expansion ${parameter:-word} ${parameter:offset:length} ${parameeter#word} ${parameter/pattern/string} bash の機能は第 2 章で詳しく学びます。 便利なコマンド awk を使えば大抵のことはできますが、便利なコマンドを知っているとワンライナーをシンプルにできます。 複雑な処理をしたいときは perl や python を使ったほうがいいかもしれません。 シェル芸の本を読んで一度は使ってみるとよいでしょう。 paste join dateutils zgrep xzgrep jq gron bc printf perl ruby teip 3 オプション オプションを使うとコマンドの動作や出力を制御できます。適切なオプションを知っているとコマンドの出力を加工する手間を省くことができます。 例えばシェル芸本では grep のオプションとして以下のものを使います。知らないオプションはありませんか? A a B C E f H m n o q v x z 正規表現 正規表現にはそれだけで本が書けてしまうくらい 4 機能が豊富にあります。 シェル芸本でも正規表現を多用します。 grep では -P オプション を使うと Perl の強力な正規表現 Perl-compatible regular expressions (PCREs) を使えます。また ruby にも強力な正規表現エンジン oniguruma 5 が内蔵されています。 grep -P や ruby では以下のような強力な機能が使えます。 メタ文字 \d : 数字にマッチする \p{Han} : 漢字にマッチする 後方参照 \1 、 \2 .... 先読み・後読み・否定先読み・否定後読み (?=pattern) (?<=pattern) (?!pattern) (?<!pattern) 部分式呼び出し \g<...> 正規表現は第 3 章で詳しく取り扱います。 コマンドの組み合わせ方のパターン Unix では単純なコマンドを組み合わせることで複雑な仕事を片付けることができます。 シェル芸では個々のコマンドやオプションの動作を覚えるのも大事ですが、コマンドの組み合わせ方も学ぶ必要があります。 例えば sort してから uniq するのは頻出パターンです。 wordlist.txt からユニークな単語を調べたいときは $ cat wordlist.txt | sort | uniq となりますね。 grep -o してから uniq するのもよく使います。 例えば story.txt からある単語の出現回数を知りたいときは $ cat wordlist.txt | grep -o the | uniq -c となります。 プロセス置換も使えると便利です。たとえば head と tail の出力を組み合わせたいときは以下のようになります。 $ cat <(head story.txt) <(tail story.txt) コマンドの組み合わせ方はシェル芸本全体を通して学びます。 アート要素 シェル芸本の中にはシェル芸っぽい解答もあります。 問題 29 には sort | uniq の代替として awk '!a[$1]' が紹介されています。 a[$1] で $1 の出現回数を数えるのですが初回は a[$1] が 0 なので、 ! を使うと初回のみ条件が成立して print されるというわけです。賢いですね。 他にも vim をコマンドとして使う 珍 解答もあったりします。例えば問題 31 を参照してください。 取り組んでみてどうなったか bash での生活がかなり豊かになりました。 具体的には、ワンライナーがすぐに作れるようになりました。 今まではシェルで込み入った処理をする場合、インターネットでよさそうなワンライナーを探したり、コマンドやオプションの意味を調べたりする必要があって、本質的でないことに時間がかかっていたように思います。 今では必要があれば man を参照するくらいで、かなりストレスフリーにワンライナーを書くことができます。 複数の解法を思いつくこともしばしばあります。 例えば、 free コマンドの出力のうち Mem 行の total 列の数字を取り出すワンライナーは次が考えられます。 $ free | grep Mem: | awk '{print $2}' $ free | xargs | awk '$0=$8' $ a=($(free | sed -n 2p)); echo ${a[1]} $ cat /proc/meminfo | grep -oP 'MemTotal:\s+\K\d+' また、複雑な操作をワンライナーとして表現できるようになったのも嬉しいポイントです。 ssh コマンドでは ssh hostname cat /etc/passwd とすることで SSH 越しにコマンドを叩くことができます。 ワンライナーを書くことができれば多数のホストに対してコマンドを実行できるようになるので嬉しいわけです。 シェル芸が役立った実例 お仕事でシェル芸が役立った実例を紹介します。 仮想サーバーチームの業務として、仮想化ソフトウェア qemu/KVM の管理業務があります。 以前 VM の動作が不調だという問い合わせを受けたときに VM の動作をホスト側から解析したことがありました。 qemu には trace-events 6 という VM がホスト側に発行するイベントをトレースする仕組みがあります。 trace-events を有効にすると、次のようなログを得られます。 20480@1663636838.696500:virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8a39c820 sector 12437488 nsectors 16 20480@1663636838.696531:blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 6367993856 bytes 8192 flags 0x0 20480@1663636838.699222:virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8a39c820 ret 0 20480@1663636838.699231:virtio_blk_req_complete vdev この中から特定の時間帯のログを取り出すにはどのようにすればよいでしょうか? 時刻の情報を取り出す まずは各行から時刻の情報を取り出したいです。 ログをよく見ると 20480@1663636838.696500 という出力が見つかります。 問題 68 で学ぶように、 @数値 という形式は Unix 時刻を表すときに使います。 次のようにすると Unix 時刻を読み込んで所定のフォーマットで出力できます。 $ date -d '@2147483647' Tue Jan 19 03:14:07 UTC 2038 よって 20480@1663636838.696500 の @ より後ろの部分は時刻を表していると推測できます。 @ より前はおそらく process id です。 awk でログをフィルタリングする まずは awk で処理しやすいように @ と : をスペースに変換します。 $ cat log.txt | tr '@:' ' ' 20480 1663636838.696500 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8a39c820 sector 12437488 nsectors 16 20480 1663636838.696531 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 6367993856 bytes 8192 flags 0x0 20480 1663636838.699222 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8a39c820 ret 0 次に 2 列目をよく見て特定の時間内のログを取り出せばよいでしょう。 date コマンドは日時の出力フォーマットを変更できます。 Unix 時刻の形式で出力したいときは +%s を指定します。 これを使って、開始時刻を $ date -d '2022/09/20 01:20:40' +%s 1663636840 終了時刻を $ date -d '2022/09/20 01:21:00' +%s 1663636860 としてみましょう。 awk コマンドは -v オプションを使うと awk プログラムの中で使える変数を定義できます。 時刻は $2 で参照できるので、次のようにすれば start から end までのログを取り出せますね。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end' 20480 1663636852.706415 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 1663636852.706452 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 1663636852.707988 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 1663636852.708002 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 後で Unix 時刻の前に @ がついていると都合がいいのでつけておきましょう。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end{$2="@"$2; print}' 20480 @1663636852.706415 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 @1663636852.706452 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 @1663636852.707988 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 @1663636852.708002 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 時刻のフォーマットを変更する 今までのワンライナーでログのフィルタリング自体はできましたが、時刻の表記が見づらいです。 $2 に対して date を適用して見やすくしたいですよね。 このようなときは teip コマンドが便利です。 $ teip -f 2 -- command とすると $2 に対してコマンド command が適用されます。 date は -f オプションを使うとファイルから時刻表現を読み取って解釈してくれます。標準入力から読み込むには - というファイル名を指定します。 ここでは $2 に対して date -f- '+%F %T.%N' を適用してみましょう。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end{$2="@"$2; print}' | teip -f 2 -- date -f- '+%F %T.%N' 20480 2022-09-20 01:20:52.706415000 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 2022-09-20 01:20:52.706452000 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 2022-09-20 01:20:52.707988000 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 2022-09-20 01:20:52.708002000 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 先頭に process id がついているのが気になりますが、これで OK とします。 最後に 達人プログラマー 7 の第 3 章では道具に習熟する大切さを説いています。 道具はあなたの能力を増幅します。道具のできが優れており、簡単に使いこなせるようになっていれば、より生産的になれるのです シェル芸本はかなりボリュームがあってそれなりに時間がかかりますが、 bash で生活している人は読んでおいて損はないと思います。みなさんもぜひシェル芸を身に着けて bash ともっと仲良くなりましょう! 仮想サーバーチームでは冬期インターンシップのポストを募集しています。 大規模なクラウドの裏側を知りたい、業務でシェル芸を使ってみたいと思ったらぜひご検討ください。 たくさんのご応募お待ちしています! engineers.ntt.com information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 https://explainshell.com/explain?cmd=%3A%28%29%7B%20%3A%7C%3A%26%20%7D%3B%3A ↩ まだシェル芸に親しみをもてていなかったら親しみを持てるようにしましょう。 ↩ https://github.com/greymd/teip ↩ https://www.oreilly.co.jp/books/9784873113593/ ↩ https://github.com/kkos/oniguruma ↩ https://qemu-project.gitlab.io/qemu/devel/tracing.html ↩ https://www.ohmsha.co.jp/book/9784274226298/ ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 1日目の記事です。 はじめに こんにちは。イノベーションセンターテクノロジー部門の西野と申します。 「Metemcyber」プロジェクトで、脅威インテリジェンスの運用や活用に関する研究開発をしています。 今回の記事では、SBOMを利用した脆弱性管理の取り組みについてご紹介します。 実は NTT Communications Advent Calendar に6年連続で寄稿しているので、そろそろ名前を覚えてあげてください。 SBOMとは? SBOMは「ソフトウェア部品表(Software Bill Of Materials)」と呼ばれるもので、一般的には特定のソフトウェアに含まれるコンポーネントの依存関係を記述するために利用されます。記述フォーマットとしては SPDX や CycloneDX が有名です。 SBOMに関するNTIAのレポート 1 では、SBOMのデータフィールドに含める具体的な情報として以下のような項目(ベースライン情報)を挙げています。 サプライヤー名 コンポーネント名 コンポーネントバージョン など NTIAのレポートは2021年にリリースされましたが、それぞれの仕様を載せているレポジトリ 2   3 を調べると、SBOMに使われている記述フォーマットの歴史はさらに古いことが確認できます。 # SPDX の firtst commit $ git log --reverse commit 01597d36837bdcdf70f0501f50284eb7a94a6341 ( tag: v1. 0 ) Author: Thomas Steenbergen < opensource@steenbe.nl > Date: Wed Aug 17 09:00:00 2011 + 0100 # CycloneDX の firtst commit $ git log --reverse commit 62da520711b3bfb6bb51b4736066e5127922c8e2 Author: Steve Springett < steve@springett.us > Date: Sun May 28 21:22:06 2017 -0500 SPDXは2010年に発案された規格で、元々はソフトウェア部品のライセンス管理を目的 4 として設計されました。 CycloneDXは2017年に発案された規格で、 OWASP Dependency-Track と呼ばれるコンポーネント分析プラットフォームのために設計 5 されました。主なユースケースは、脆弱性の識別、ライセンス管理、古いコンポーネントの分析です。 なぜ今SBOMなのか? SolarWinds製品への攻撃 6 、Codecovのセキュリティインシデント 7 を皮切りに、2021年頃からサプライチェーンを狙ったサイバー攻撃の危険性が本格的に認識されるようになりました。 Typosquattingはもちろん、 Dependency Confusion と呼ばれる新たなサプライチェーン侵害のテクニックも報告されています。 ソフトウェアサプライチェーンのセキュリティに注目が集まる中、SBOMの重要性は上がり続けており、2022年9月には米政府機関のソフトウェア調達 8 にSBOMの内容が盛り込まれる事態となりました。 さぁ皆さん、SBOMを使ってソフトウェアサプライチェーンのセキュリティを万全にしましょう。 …とは、ならない話が今回のメインテーマになります。 なぜ今までSBOMが話題にならなかったのか QualcommのセキュリティエンジニアリングVPであるAlex Gantman氏が、SBOMの課題 9 を端的に表現しています。 「最良の(アイディアであるが、誰も実行していない)プラクティス」 Gantman氏はSBOMに3つの問題があると指摘しています。 コンポーネントはアトミックではない 脆弱性は状況に依存する 間接的なレイヤは根本的な依存を排除しない コンポーネントはアトミックではない Gantman氏は、「ソフトウェア部品」のアナロジーがそもそも不適切であると指摘しています。ソフトウェアを分解しても、車の部品のように規格化されたモジュールとして取り出すことはできません。サードパーティのパッケージがどのように使用されているかも不明な状況で、コード断片に一意の識別子やバージョンを付与しても意味のある管理にはならないと述べています。 脆弱性は状況に依存する Gantman氏は、コンポーネントの利用方法を知らなければ実際のリスクを評価できないと指摘しています。タイヤがパンクする欠陥を見つけたとしても、走行中の車なのか、木のブランコなのかで対処の優先度は大きく変わります。これはSBOMに限った話ではありませんが、実際のサービス影響を考慮した脆弱性評価を現場は求めています。 間接的なレイヤは根本的な依存を排除しない Gantman氏は、サードパーティライブラリのリスクはソフトウェア提供者でなければ判断が難しいことを指摘しています。先に述べたように、アトミック性やコンテキストの問題がある以上、利用者によるサードパーティライブラリのリスク評価は低精度かつ高ノイズなものになります。その結果、利用者は提供されたSBOMの情報をもとにソフトウェア提供者へリスクを問い合わせる状況が生まれます。 利用者がソフトウェア提供者にサードパーティライブラリのリスク評価を委任できるのであれば、そもそもSBOMの情報を提供する意味があまりないように感じます。 運用上の課題は他にも SBOMは優れたアイディアですが運用例があまり報告されておらず、実際のメリットは未知数な部分が大きいです。 また、SBOMのユーザ提供はソフトウェアの模倣リスクを高めることにも繋がるため、各社ベンダはメリットとデメリットを十分理解した上で取り組む必要があります。 ソフトウェアセキュリティの業界フォーラムであるSAFECodeも、以下のような声明を出しています。 Much of the text in the NTIA request for comments describes benefits from an SBOM that are highly speculative. It is important that no guidance or requirements about SBOM be issued under the Executive Order until there are clear and complete demonstrations at scale that the putative benefits are in fact realizable.” - SAFECode 2022年9月の米国大統領令 10 の中には、「募集要項でSBOMを要求する場合がある」と書かれているだけですが、アメリカ以外の政府機関もこの方向に動いていく可能性は十分に考えられます。 実運用からみたSBOMの利用 NTTコミュニケーションズでは、既にSBOMを利用したアタックサーフェスマネジメントを実験的に始めています。 取り組みの際、私たちが考慮したことは「SBOMの生成や収集が目的になるような運用をしない」ことでした。先にも説明しましたが、SBOMのユースケースは、脆弱性の識別、ライセンス管理、古いパッケージの検出と多岐に渡ります。 「なんとなく良い/悪いけど、いまいち効果が分からない」事態を避けるため、実際の使い道を決めてからT字型に運用の幅を広げていくアプローチを取りました。 何のためのSBOMなのか 私たちは、脆弱性管理の観点から最初の取り組みを始めました。その理由は大きく分けて2つあります。 社内に脆弱性管理をするシステムが既に存在する GitHubのDependabotと機能や性能を比較検証できる これだけ聞くと、既に脆弱性管理ができているので取り組む意味がないと感じるかもしれません。しかし、「既存の脆弱性管理システムをSBOMでどう改善できるのか?」「業界標準のサービスと比較して、新たにSBOMを利用する意味があるのか?」という2点は、SBOMのメリットやデメリットを確認する素晴らしい試金石になりました。 誰のためのSBOMなのか また、SBOMの利用者についても考える必要があります。私たちの議論では対象となる人物像を3つに絞りました。 脅威アナリスト SBOMを利用して、収集したサイバー脅威情報をフィルタリングしたい SBOMを起点に、サイバー脅威情報のハンティングをしたい 開発者 脆弱なパッケージを利用しているか検知したい 脆弱なパッケージが見つかれば修正したい 修正できなければサイバー攻撃を緩和する実装を知りたい 余分なパッケージが混入しているか検知したい 余分なパッケージを利用していれば削除したい 運用者 脆弱なパッケージを利用しているか検知したい 脆弱なパッケージが見つかれば修正したい 修正できなければサイバー攻撃を緩和する手法を知りたい 正直にいうと、全てのロールで実際の検証ができたわけではないのですが、誰がどう使うのかを意識しながら検証を進められた点は非常によかったと思います。 Gantman氏の記事にも注釈がある部分ですが、SBOMはまず社内利用またはグループ会社間の利用を想定すべきだと個人的には思います。 「透明性の確保」を目的に外部提供を推進する動きもありますが、実際のユースケースを集めずにSBOMの生成や利用の基準を統一することは困難でしょう。 SBOM対応の脆弱性スキャナ OSSで有名な脆弱性スキャナはいくつかありますが、SBOMの観点から以下の2つを候補に選びました。 Trivy Grype (+Syft) どちらも非常に優れた脆弱性スキャナなので、どちらを使ってみるかは好みで良いと思います。開発の都合上、私たちは何度もスキャナを動かすことが想定されたので、スキャン速度が早い Trivy を採用しました。脆弱性スキャナの詳細に関しては、7日目の志村さんの記事をご覧ください! Gantman氏の問題を実運用から考える 結論から言うとSBOMに関するGantman氏の指摘は正しく、SBOMの実運用にはある程度の割り切りが必要です。 パッケージマネージャがアトミック性を決める コンテキスト問題はSBOMで解決できない(が、使われ方に偏りあり) 想定外のパッケージ混入を開発者に確認できる パッケージマネージャがアトミック性を決める 「コンポーネントはアトミックではない」は正しいですが、アトミック性を意識したソフトウェア開発は可能です。そして実運用の観点では、npmやPyPIなどの言語パッケージや、Ubuntuのaptで管理されるOSパッケージがアトミックな単位として機能します。 言い換えれば、「パッケージマネージャ」がなければ大規模なSBOM運用は難しいと思います。 本来のSBOMであれば、パッケージマネージャに拘らない絶対的な基準があるべきかもしれませんが、対応できたとしても実運用上は管理が困難です。 脆弱性管理やライセンス管理に関しては、「まずはパッケージ単位で管理できる状態をつくる」ことがSBOM導入の最初のステップになります。 コンテキスト問題はSBOMで解決できない 「脆弱性は状況に依存する」は正しいですが、ライブラリ次第では使われ方のコンテキストがほぼ一定のものがあります。npmにおいては、 google-auth-library のような認証系のライブラリがその例です。 一方、全く使われ方が想定できないものも存在します。ユーティリティ系のライブラリです。有名どころでは、 lodash や anymatch がありますが、社内のリポジトリを調べても本当に多種多様な使われ方をしていました。 SBOMはコンテキスト問題を解決できませんが、私たちはSBOMを根拠に社内で使われているライブラリの利用状況を調べることができました。 全てのパッケージを調べることは困難だと思いますが、パッケージの統計的な利用傾向やよく利用される上位のパッケージを調べることで、脅威アナリストはサービス影響のコンテキストを理解しやすくなると思います。 想定外のパッケージ混入を開発者に確認できる 「間接的なレイヤは根本的な依存を排除しない」は正しいですが、第三者がパッケージの使用について指摘できる点はメリットがあると感じました。 具体例を挙げると、 react-scripts の使用によって nth-check の脆弱性アラートが検出された事例です。 これは界隈では 有名なissue ですが、npmの脆弱性管理をシンプル化するには、本番環境で必要のないパッケージを dependencies ではなく devDependencies に移動する必要があります。 "dependencies": { "react": "^18.2.0", ... }, "devDependencies": { "react-scripts": "^5.0.1" ... }, SBOMの収集によって、本番環境に本来必要がないライブラリを検出できたことは、開発者のミスや勘違いを防ぐ仕組みとして意義があると思います。 逆に言えば、第三者にSBOMを提供するメリットは、実用上これくらいしかないのでは?という気がします。 Threatconnectome SBOMの検証を通じて、私たちはアタックサーフェスマネジメントツール Threatconnectome(スレットコネクトーム) を開発しました。 SBOMと脅威情報のマッチングによるアラート機能 脅威を軽減または削除するアクションの実行管理 アクションの実績からチームや個人のスキルレベルを評価 などの機能があります。 Software Identification(SWID)やCommon Platform Enumeration(CPE)は今回の目的では利用が難しく、UUIDと独自の命名規則でソフトウェア部品を管理しています。現在はTrivy対応だけですが、SyftやGrypeの連携も今後行う予定です。 来年にはOSS公開できると思いますので、皆さん期待して待っていてください。 SBOMを脆弱性管理に使ってみた感想 まず、社内の脆弱性管理を効率化するために、あらゆるメタデータを集めておくことが重要です。これはCODE BLUE 2022でAirbnb社のセキュリティエンジニアPlattner氏、Mashal氏が講演していた内容 11 とも被りますが、あくまで「SBOM」は収集するメタデータの1つに過ぎません。 ファイルフォーマットの選択もあまり意味がなく、 osquery でサーバのパッケージ情報を集めるような運用から始めても全く問題ないと思います。 むしろ、 重要なことは「どの情報を」「何のために使うか」 です。これを決めずにSBOM運用を始めるのはオススメしません。 社内の脆弱性管理システムの改善 誤解ないように説明すると、全てのプロダクトにいきなりSBOM管理は適用できません。理由は先にも述べた通り、「アトミック性を意識したソフトウェア開発」と「パッケージマネージャ」の存在が不可欠だからです。これらを考慮せずに作られたプロダクトに対して、大規模なSBOM管理は上手くいきません。 SBOM管理が可能なプロダクトに対し、アラート通知の効率化やオートクローズ対応を行うことで、 開発者や運用者に余裕をつくる ことが最初のポイントになると思います。 また、脆弱性対応の人為的ミスがないことを検証するフェーズも重要です。もし検証するフェーズがない場合は、SBOMを利用した検証から始めるのも良いでしょう。 GitHubのDependabotと比較 この部分の内容は、あくまでリポジトリを対象とした脆弱性管理であり、コンテナや物理サーバが対象ではないことに注意してください。 脆弱性管理の観点では、 trivy-db からエクスポートしたデータで脆弱性管理をしていたので、Dependabotで検知されたものは全て検出ができました。(trivy-dbは様々な脆弱性情報を収集しており、その中にはDependabotが使用する GitHub Security Advisories も含まれる) 私たちの環境では、GitHubアクションでmainブランチのSBOMを抽出し、その情報をThreatconnctomeに自動登録するワークフローを実行していました。 GitHub上で開発をしているのであれば、Dependabotによる脆弱性検知をまず最優先で有効化してください。 脆弱性 検知 の観点では、SBOM管理のソリューションを新たに導入するメリットはほぼないと思います。 一方、脆弱性 管理 の点ではDependabotアラートだけでは難しい状況がありました。実運用では「必ずしもライブラリのバージョンを上げたくない」ケースがあり、その場合は分散したレポジトリをアナリストが継続的に監視する必要があります。また、複数のレポジトリを横断する分析もGraphQLだけでは限界があり、アタックサーフェスマネジメントの点でも課題が残りました。 私たちはThreatconnctomeでこれらの問題を解決しました。組織によっては GitHub Advanced Security で十分なケースもあると思いますので、そちらの利用も是非検討してみてください。 SBOMで始める脆弱性管理の総論 SBOM管理ができないシステムの存在を受け入れる GitHubを利用しているのであれば、まずはDependabotアラートを有効化する パッケージマネージャで管理できる範囲のSBOM情報を収集する 脆弱性やサイバー攻撃の情報をSBOMと紐付けてアラート通知する SBOMを使って対応済みのアラートを検証する サービス運用やライブラリ利用のコンテキストに応じて、アラート通知を最適化する 利用するコンポーネントのアトミック性を確保し、SBOM管理ができるシステムを増やす パッケージマネージャで管理できない範囲のSBOM適用について検証する 脆弱性管理にSBOMを活用したいのであれば、開発チームと一体になってアトミック性の確保やコンテキストの問題に対処してください。つまり、 内製開発のプロセスから脆弱性対応を最適化する取り組み が必要です。それができなければ、SBOMで始める脆弱性管理は不十分で実用性のないものになるでしょう。 終わりに Metemcyberチームでは「SBOMによる脅威インテリジェンス利用の効率化」に取り組んでいましたが、やっていたことが今年偶然ヒットして驚いています。 NTTグループとしても、サプライチェーンセキュリティリスクに取り組むオープンコンソーシアム「セキュリティ・トランスペアレンシー・コンソーシアム」の設立に向けて準備 12 しており、現在私もその一員として活動しています。 最近は MetemcyberのGitHubレポジトリ を更新できていませんが、次年度は更新を再開する予定です。MISP配信をサブスク化する謎サービスが立ち上がるかもしれないので、引き続き動向をチェックしていただければと思います。 来年はいろいろ面白い情報を発信できると思いますので、是非 Twitterアカウント のフォローよろしくお願いします。 それでは、明日の記事もお楽しみに! https://www.ntia.doc.gov/report/2021/minimum-elements-software-bill-materials-sbom ↩ https://github.com/spdx/spdx-spec ↩ https://sbpgithub.com/CycloneDX/specification ↩ https://www.jolts.world/index.php/jolts/article/view/45 ↩ https://cyclonedx.org/about/history/ ↩ https://piyolog.hatenadiary.jp/entry/2020/12/20/045153 ↩ https://about.codecov.io/security-update/ ↩ https://www.jetro.go.jp/biznews/2022/09/e43da89f71ddf4b3.html ↩ https://www.linkedin.com/pulse/sbom-good-intentions-bad-analogies-uglyoutcomes-alex-gantman ↩ https://www.whitehouse.gov/wp-content/uploads/2022/09/M-22-18.pdf ↩ https://codeblue.jp/2022/talks/?content=talks_8 ↩ https://group.ntt/jp/newsrelease/2022/11/09/221109b.html ↩
アバター
クラウド&ネットワークサービス部の花川です。寒くなってきましたが、皆さんいかがお過ごしでしょうか。 タイトルの通り、NTTコミュニケーションズ(NTT Com)では、この冬に2つのインターンシップを開催します! *1 information.nttdocomo-fresh.jp この記事では、このうちの「現場受け入れ型インターンシップ」の紹介をしたいと思います! 現場受け入れ型インターンシップとは? 実際にプロダクト開発の現場で、NTT Comのエンジニア・デザイナーと一緒に働きながら、業務を体験していただくインターンシップです。 インターンシップを通して、NTT Comでのエンジニア・デザイナーの仕事を知ることができ、そして参加してくれる皆さんが成長できる内容となっています。 期間は、2023年02月06日〜17日のうち、土日祝を除く実働10日間です。 募集ポスト 募集ポストについては、下記のページの「受け入れポスト情報」をご覧ください。 information.nttdocomo-fresh.jp 記載されているもののうち、受け入れ会社に NTTコミュニケーションズ株式会社 と記載されたポストが、NTT Comでの業務になります! これまでのインターンシップの様子 これまで開催したインターンシップに参加してくれた学生の方々が、この Engineers' Blogに体験記を寄稿してくれています。 「インターンシップでどんなことに取り組むんだろう?」や「インターンシップを通して何が学べるのだろう?」といった疑問を解消する助けになれば幸いです。 AI分野 engineers.ntt.com セキュリティ分野 engineers.ntt.com engineers.ntt.com ネットワーク分野 engineers.ntt.com engineers.ntt.com ソフトウェア分野 engineers.ntt.com engineers.ntt.com engineers.ntt.com まとめ みなさんも、この冬、インターンシップに参加して圧倒的成長をしてみませんか? 大事なことなのでもう一度書いておきますが、現場受け入れ型インターンシップのページはこちらです。 information.nttdocomo-fresh.jp エントリーもこのページからできます。12月14日(水) 13:00締切となっています。 この記事を読んだそこのあなたからの応募を待っています! *1 : 今年は、NTT Comがドコモグループの一員になったこともあり、ドコモグループとしての開催となります。
アバター
サマリ 様々な Path Computation Element(PCE)実装を用いた Explicit Path の SR Policy 設定検証 この記事は Multi-AS Segment Routing 検証連載の第 11 回です。目次は こちら 概要 イノベーションセンターの竹中です。 本記事では私たちの検証環境において様々な PCE 実装を用いた SR Policy の発行方法を検証します。 4 つの PCE の検証手順を各章で記載しますが、それぞれの PCE の環境準備手順と初期設定、PCC との間で PCEP Session を確立するまでの設定は省略します。 つまり PCE と PCC 間で PCEP Session が確立した状態から、特定の経路を指定する SR Policy の発行と確認、さらに VPN 通信で当該経路が使用されていることを確認します。 なお、詳細は各章で後述しますが、 PCEP で BGP Color を扱う仕様は Internet-Draft として現在 ( 2022 年 11 月 ) も議論中です。 そのため一部 PCE においては Multi-vendor での SR Policy の発行に相互運用できない部分があったため、PCE と PCC の組み合わせは限定的になっています。 共通の検証環境を示した後、それぞれの PCE について検証します。 検証 検証構成 以下のトポロジーで検証します。 各ルーターの製品とバージョンは以下の通りです。 rt01: ASR9901(IOS XR 7.6.1) rt02: MX204(Junos 22.1R1.10) rt03: ASR9901(IOS XR 7.5.1) rt04: MX204(Junos 21.4R1.12) rt05: Cisco 8201(IOS XR 7.5.1) rt06: PTX10001-36MR(Junos 21.2R1.15-EVO) rt07: ASR9902(IOS XR 7.6.1) rt08: MX204(Junos 21.4R1.12) 各 PCE は PCC である rt01、rt02 それぞれと PCEP Session を確立し、 PCC に SR Policy を追加します。rt01 と rt02 間の最短パスは直結の経路ですが、今回は rt01(IOS XR)に対しては rt01 -> rt05 -> rt06 -> rt02 となる経路を、rt02(Junos)に対しては rt02 -> rt06 -> rt05 -> rt01 となる経路を各 PCE で設定します。 なお rt04、rt08 は本検証では利用していませんが、検証環境上の都合でトポロジー図に記載しています。 また、事情により rt06 を通る経路の traceroute は当該ホップの結果で * * * と表示されています。 IOS XR SR-PCE IOS XR は SR Policy を管理する PCE(SR-PCE)として利用可能であり、対象とする PCC や SR Policy を設定できます。 本節の検証では以下の図のように、rt03 を IOS XR SR-PCE として利用します。rt01(IOS XR)と PCEP Session を張り、SR Policy を発行します。 概要でも軽く言及しましたが、本検証に PCE として用いた IOS XR 7.5.1 は SR Policy の Color の指定を Vendor Information object 内で Cisco 社独自実装の PCEP TLV を用いて行っています。 Junos PCC では当該 TLV を解析できないため、rt02 を PCC とした検証は省略しています。 PCEP Session の確認 PCEP Session の確認は show コマンドで行います。 RP/0/RSP0/CPU0:rt03#show pce ipv4 peer Wed Nov 2 19:28:15.630 JST PCE's peer database: -------------------- Peer address: 10.255.1.1 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation SR Policy の発行 pce コマンド配下に SR Policy の定義を追加することで、PCC へ SR Policy を発行できます。 RP/0/RSP0/CPU0:rt03(config-pce-sr-te-pp-info)#show commit changes diff Wed Nov 2 19:34:01.914 JST Building configuration... !! IOS XR Configuration 7.5.1 pce + segment-routing + traffic-eng + segment-list name XR-PCE-PATH + index 1 mpls label 16005 + index 2 mpls label 16006 + index 3 mpls label 16002 ! + peer ipv4 10.255.1.1 + policy IOSXR-SR-PCE + color 100 end-point ipv4 10.255.1.2 + candidate-paths + preference 100 + explicit segment-list XR-PCE-PATH ! ! ! ! ! ! ! ! end PCE で発行済 SR Policy の確認 PCEP Session と同様に、show コマンドで 発行済 SR Policy の簡易情報が確認できます。 SR policy が up していることを確認します。 RP/0/RSP0/CPU0:rt03#show pce segment-routing traffic-eng policy Wed Nov 2 20:49:50.364 JST PCE's policy database: ---------------------- PCC Address: 10.255.1.1 Color: 100, Endpoint: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Candidate-paths: Symbolic-name: IOSXR-SR-PCE (Active) PLSP-ID: 1 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Fri Nov 11 18:37:54.029 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:00:44 (since Nov 11 18:37:10.075) Candidate-paths: Preference: 100 (PCEP) (active) Name: IOSXR-SR-PCE Requested BSID: dynamic PCC info: Symbolic name: IOSXR-SR-PCE PLSP-ID: 6 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce 10.255.1.3) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24018 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 traceroute で経路確認 rt01 の VRF 100 が持つネットワークから rt02 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 2 20:43:30.269 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 3 msec 2 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec Cisco Crosswork Network Controller (CNC) CNC は Cisco 社が提供する PCE を含むソフトウェアスイートであり、GUI でトポロジーの確認や SR Policy などのパス情報の管理ができます。 本節の検証では、以下の図のように CNC を設置します。rt01(IOS XR)と PCEP Session を張り、SR Policy を発行します。 IOS XR SR-PCE と同様に、現在(2022 年 11 月)CNC は SR Policy の Color の指定を Vendor Information object 内で Cisco 社独自実装の PCEP TLV を用いて行っています。 Junos PCC では当該 TLV を解析できないため、rt02 を PCC とした検証は省略しています。 PCEP Session の確認 CNC の構成の一部として、IGP / BGP / PCEP などのプロトコルを用いてネットワーク機器と通信するための Data Gateway が存在します。PCEP Session の確認は Data Gateway へログイン後、CLI コマンドにて行います。 RP/0/RP0/CPU0:sr-pce#show pce ipv4 peer Wed Nov 9 16:34:23.555 JST PCE's peer database: -------------------- Peer address: 10.255.1.1 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation SR Policy の発行 GUI にて、以下の方法で SR Policy を追加できます。 Services & Traffic Engineering > Traffic Engineering を選択します。 Create > PCE Init を選択し、SR Policy 作成画面を開きます。 SR Policy の Headend、Endpoint、Color をそれぞれ選択します。 経由するルーターと利用する SID の組み合わせを選択し、Segment List を作成します。 Provision を選択することで、SR Policy が発行されます。 PCE で発行済 SR Policy の確認 GUI から確認できます。SID をもとにした経路表示や、IGP Shortest Path を考慮した経路の表示が可能です。 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Mon Nov 14 19:27:00.420 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:07:11 (since Nov 14 19:19:49.551) Candidate-paths: Preference: 100 (PCEP) (active) Name: CNC-Cisco Requested BSID: dynamic PCC info: Symbolic name: CNC-Cisco PLSP-ID: 8 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce 10.61.0.248) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24013 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 traceroute で経路確認 rt01 の VRF 100 が持つネットワークから rt02 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:ar-rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 9 17:46:10.915 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 4 msec 4 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec Paragon Pathfinder Paragon Pathfinder(以下、 Pathfinder)は Juniper 社が提供する PCE であり、GUI でトポロジーの確認や SR Policy などのパス情報の管理ができます。 本節の検証では、以下の図のように Pathfinder を設置します。rt02(Junos)と PCEP Session を張り、SR Policy を発行します。 なお検証環境の都合上、本節のみ IP アドレスが変わっていることにご留意ください。 現在(2022 年 11 月)Pathfinder は SR Policy の Color の指定を Association object 内で Juniper 社独自実装の PCEP TLV を用いて行っています。 IOS XR PCC では当該 TLV を解析できないため、rt01 を PCC とした検証は省略しています。 PCEP Session の確認 画面下部の Node タブから、各ルーターの情報を確認できます。PCC である rt02 との間で PCEP Session が Up していることを確認します。 SR Policy の発行 GUI にて、以下の方法で SR Policy を追加できます。 画面下部の Tunnel タブへ切り替えた後 Add ボタンを選択し、SR Policy を発行します。 Properties タブで SR Policy の Provisioning Method(PCEP)、SR Policy 名、headend、Tailend をそれぞれ選択します。 また、Provisioning Type は SR を選択します。 Path タブを選択し、経路を選択します。 Loose Source Routing で rt06、rt05、rt01をそれぞれ指定します。 Submit を選択することで、Provision LSP 画面が閉じ、SR Policy が発行されます。 PCE で発行済 SR Policy の確認 GUI から SR Policy の状態を確認できます。 Tunnel タブに SR Policy が追加され、Op Status が Active となることを確認します。 PCC で受けとった SR Policy の確認 user@rt02> show spring-traffic-engineering lsp name Paragon-Juniper detail Name: Paragon-Juniper Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.2.7 State: Up Path Status: Up Outgoing interface: et-0/0/0.0 Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: IPv4 Adjacency ID, 10.2.11.2 -> 10.2.11.1 SID type: 20-bit label, Value: 99 Hop 2 (Strict): NAI: IPv4 Adjacency ID, 10.2.7.2 -> 10.2.7.1 SID type: 20-bit label, Value: 65 Hop 3 (Strict): NAI: IPv4 Adjacency ID, 10.2.5.1 -> 10.2.5.2 SID type: 20-bit label, Value: 24005 Total displayed LSPs: 1 (Up: 1, Down: 0) traceroute で経路確認 rt02 の VRF 100 が持つネットワークから rt01 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 user@rt02> traceroute 192.168.0.254 routing-instance 100 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 et-0-1-4.rt06 (10.2.11.1) 1.284 ms 0.929 ms 0.844 ms MPLS Label=65 CoS=0 TTL=1 S=0 MPLS Label=24005 CoS=0 TTL=1 S=0 MPLS Label=24014 CoS=0 TTL=1 S=1 2 HundredGigE0-0-1-2.rt05 (10.2.7.1) 1.117 ms 1.127 ms 0.811 ms MPLS Label=24005 CoS=0 TTL=1 S=0 MPLS Label=24014 CoS=0 TTL=2 S=1 3 HundredGigE0-0-0-20.rt01 (10.2.5.2) 6.980 ms * 3.839 ms Pola PCE Pola PCE は、 私(竹中)と三島が開発し OSS として公開中の CLI ベースの SR PCE です。Pola PCE は IOS XR および Junos どちらに対しても yaml ファイルで記述した SR Policy の適用ができます。 本節の検証では、以下の図のように Pola PCE 用サーバーを設置します。rt01(IOS XR)・ rt02(Junos) と PCEP Session を張り、各 PCC へ SR Policy を発行します。 PCEP Session の確認 pola コマンドを用いて確認できます。 user@pce:~$ pola session sessionAddr(0): 10.255.1.2 sessionAddr(1): 10.255.1.1 SR Policy の発行 作成する SR Policy の情報を yaml ファイルに記載し、pola コマンドを用いて発行します。 rt01 SR Policy の定義 asn: 65001 srPolicy: pcepSessionAddr: 10.255.1.1 name: Pola-PCE-Cisco srcRouterId: 0000.0aff.0101 dstRouterId: 0000.0aff.0102 color: 100 type: explicit segmentList: - sid: 16005 - sid: 16006 - sid: 16002 pola コマンド user@pce:~$ pola sr-policy add -f blog11_cisco.yaml success! rt02 SR Policy の定義 asn: 65001 srPolicy: pcepSessionAddr: 10.255.1.2 name: Pola-PCE-Juniper srcRouterId: 0000.0aff.0102 dstRouterId: 0000.0aff.0101 color: 100 type: explicit segmentList: - sid: 16006 - sid: 16005 - sid: 16001 pola コマンド user@pce:~$ pola sr-policy add -f blog11_juniper.yaml success! PCE で発行済 SR Policy の確認 pola コマンドで発行済み SR Policy の情報が確認できます。 user@pce:~$ pola sr-policy list LSP(0): PcepSessionAddr: 10.255.1.1 PolicyName: Pola-PCE-Cisco SrcAddr: 10.255.1.1 DstAddr: 10.255.1.2 Color: 100 Preference: 100 DstAddr: 10.255.1.2 SegmentList: 16005 -> 16006 -> 16002 LSP(1): PcepSessionAddr: 10.255.1.2 PolicyName: Pola-PCE-Juniper SrcAddr: 10.255.1.2 DstAddr: 10.255.1.1 Color: 100 Preference: 100 DstAddr: 10.255.1.1 SegmentList: 16006 -> 16005 -> 16001 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 rt01 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Fri Nov 11 20:33:37.079 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:00:20 (since Nov 11 20:33:17.017) Candidate-paths: Preference: 100 (PCEP) (active) Name: Pola-PCE-Cisco Requested BSID: dynamic PCC info: Symbolic name: Pola-PCE-Cisco PLSP-ID: 7 Protection Type: unprotected-preferred Maximum SID Depth: 10 Dynamic (pce 10.99.0.254) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24013 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 rt02 user@rt02> show spring-traffic-engineering lsp detail Name: Pola-PCE-Juniper Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.1.1-100<c> State: Up Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.6 SID type: 20-bit label, Value: 16006 Hop 2 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.5 SID type: 20-bit label, Value: 16005 Hop 3 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.1 SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) traceroute で経路確認 rt01 の VRF 100 が持つネットワークと rt02 の VRF 100 が持つネットワークで双方に traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 2 20:43:30.269 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 3 msec 2 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec user@rt02> traceroute 192.168.0.254 routing-instance 100 no-resolve traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 * * * 2 10.1.10.1 2.602 ms 4.062 ms 1.779 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24009 CoS=0 TTL=2 S=1 3 * 10.1.1.1 3.826 ms * まとめ 本記事では様々な PCE を用いて PCE から PCC へ SR Policy を発行する動作を検証しました。 特に Color 指定において相互運用性の制限がみえました。
アバター
こんにちは、5G&IoTプラットフォーム部で、IoT Connect Gatewayの機能開発を担当している角田です。 前回のコンフィグマネージャーの概要に引き続き、コンフィグマネージャーでIoTデバイスへ配信する設定ファイルの生成方法についてご紹介します。 【IoT Connect Gateway】コンフィグマネージャーのご紹介 1. 設定ファイルの生成の流れ IoTデバイスへ配信する設定ファイルを生成するには、以下の作業を実施します。 テンプレート を作成する 共通パラメーター を作成する デバイスグループ を作成する プロファイル を作成する それぞれの作業の関係をざっくりと纏めると下図のようになります。詳細は以降の内容で説明します。 詳細情報については、下記の公式ドキュメントをご参照ください。 「コンフィグマネージャー」 を利用する 1.1. テンプレートについて テンプレートでは、設定ファイルの雛形をテキスト形式かつ任意のフォーマットで記述します。テンプレートエンジンの1つである Jinja2 の記法や、IoT Connect Gatewayのクラウドアダプタ機能と同等の プレースホルダ機能 が使用できます。 Jinja2の記法に則ったテンプレートを記述することで、設定ファイルの生成時に、下記の値がテンプレートへ動的に埋め込まれ、IoTデバイス毎の設定ファイルを生成できます。 共通パラメーター デバイスグループにおけるデバイス固有の値(以降、デバイスパラメーターと呼びます) また、利用ケースは限られると思いますが、テンプレート単体で設定ファイルを生成できます。その場合は、テンプレートの内容そのものが設定ファイルとなります。 以下は、環境変数を想定したテンプレートの例です。 MQTT_TOPIC = " {{ device.topic }} " MQTT_HOST = " {{ global.host }} " HOSTNAME = " $imsi " {{ }} のように2つの中括弧で囲われた部分が、共通パラメーターやデバイスパラメーターの値で置き換えられます。ただし、コンフィグマネージャーでは、 {{ device.xxxx }} のように device から始まる表記は、デバイスパラメーターを置き換えるために予約されているので注意が必要です。 *1 また、 $imsi のように $xxx と記述することで、SIM情報が持っているIMSIやIMEIの情報をテンプレートに埋め込むことができます。このプレースホルダ機能で使用可能な値は、下記の公式ドキュメントをご参照ください。 各種共通仕様#プレースホルダ 1.2. 共通パラメーターについて 共通パラメーターでは、IoTデバイス共通で使用する値を、JSON形式で記述します。 以下は、共通パラメーターの例です。 { " global ": { " host ": " an1.icgw.ntt.com " } } 上記のJSON文字列の host キーの値である an1.icgw.ntt.com をテンプレートに埋め込みたい場合は、テンプレートに {{ global.host }} と記述します。上記の例では、2階層のJSON文字列ですが、2階層以上のJSON文字列も使用できます。下記のJSON文字列の host キーの値である an1.icgw.ntt.com をテンプレートに埋め込みたい場合は、 {{ global.mqtt.host }} とテンプレートに記述します。 { " global ": { " mqtt ": { " host ": " an1.icgw.ntt.com " } } } 1.3. デバイスグループについて デバイスグループは、設定ファイルの生成・配信対象のSIMを登録し、デバイスパラメーターをJSON形式で記述します。デバイスパラメーターを記述することで、IoTデバイス固有の値をテンプレートに埋め込むことができます。 以下は、デバイスパラメーターの例です。 { " default ": { " topic ": " default_topic " } , " imsi_999990000000001 ": { " topic ": " topic_aaaaa " } , " imsi_999990000000002 ": { " topic ": " topic_aaaaa " } } デバイスパラメーターの最上層のキーは、 default および imsi_<IMSI番号> にする必要があります。 上記のJSON文字列の imsi_999990000000001 キーの中の topic をテンプレートに埋め込みたい場合は、テンプレートに {{ device.topic }} と記述します。テンプレート側では、IMSI番号を特に意識する必要はありません。設定ファイルの生成時に、コンフィグマネージャーが imsi_<IMSI番号> キーの値を元に、適切にテンプレートの内容を置き換えます。また、テンプレートに {{ device.topic }} と記述しているものの、 imsi_<IMSI番号> キーの中に topic が存在しない場合は、 default キーの topic が使用されます。 1.4. プロファイルについて プロファイルは、テンプレート・共通パラメーター・デバイスグループの組み合わせを指定し、その組み合わせを元に、IoTデバイス毎の設定ファイルを生成します。プロファイルに指定する各種パラメータを切り替えることで、生成・配信する設定ファイルを即座に切り替えることができます。また、設定ファイルの誤配信を防止する事前チェック機能や、各種パラメータのお気に入りの組み合わせを登録できるプロファイルセットの機能があります。 2. 設定ファイルを生成する それでは、実際にIoT Connect Gatewayのポータル上から、設定ファイルを作成していきます。 2.1. テンプレートを作成する IoT Connect Gatewayのポータル左メニューのコンフィグマネージャーから【パラメーター】を選択します。【テンプレート】タブを選択し、【新規作成】ボタンを押します。 【テンプレート名】【説明】【ラベル】、および【内容】に下記のJSON文字列を入力し、【作成】ボタンを押します。 MQTT_TOPIC = " {{ device.topic }} " MQTT_HOST = " {{ global.host }} " HOSTNAME = " $imsi " 2.2. 共通パラメーターを作成する IoT Connect Gatewayのポータル左メニューのコンフィグマネージャーから【パラメーター】を選択します。【共通パラメーター】タブを選択し、【新規作成】ボタンを押します。 【パラメーター名】【説明】【ラベル】、および【内容】に下記のJSON文字列を入力し、【作成】ボタンを押します。 { " global ": { " host ": " an1.icgw.ntt.com " } } 2.3. デバイスグループを作成する IoT Connect Gatewayのポータル左メニューのコンフィグマネージャーから【パラメーター】を選択します。【デバイスグループ】タブを選択し、【新規作成】ボタンを押します。 【デバイスグループ名】【説明】【ラベル】を入力し、IoTデバイスの設定ファイルの生成・配信対象のSIMを追加するために【SIM管理】ボタンを押します。 デバイスグループに含めたいSIMを選択し、【保存】ボタンを押します。 【内容】に下記のJSON文字列を入力し、【作成】ボタンを押します。 imsi_<IMSI番号> キーのIMSI番号は適宜ご自身の環境で使用するIMSI番号に合わせてください。また、【IMSIの値を反映】ボタンを押すことで、 default キーおよび imsi_<IMSI番号> キーを持つ、デバイスパラメーターの雛形を【内容】に自動で反映できます。 { "default": { "topic": "default_topic" }, "imsi_440130000191127": { "topic": "topic_aaaa" }, "imsi_440130000200529": { "topic": "topic_bbbb" } } 2.4. プロファイルの作成 IoT Connect Gatewayのポータル左メニューのコンフィグマネージャーから【プロファイル】を選択し、【新規作成】ボタンを押します。 作成済みの【テンプレート】【共通パラメーター】【デバイスグループ】を設定します。 【チェック】ボタンを押し、デバイスグループへ設定したSIM毎に生成される設定ファイルを事前に確認します。 各種パラメーターの設定と事前チェックが完了したら、【作成】ボタンを押し、プロファイルを作成します。 【作成】ボタンを押すことで、プロファイル作成の結果画面に遷移するので、【生成】ボタンを押します。 IoTデバイス毎の設定ファイルを生成するには、【生成】ボタンを押す必要があります。 プロファイル作成の結果画面では、IoTデバイス毎の設定ファイル(今回だと、IMSI番号が 440130000191127 と 440130000200529 のための設定ファイル)は、まだ生成されていません。 設定ファイルが生成されたかどうかは、【生成状態】のチェックマークで確認できます。 確認画面が表示されるので、【OK】を押します。 以下の結果画面に遷移し、【生成状態】が緑色のチェックマークであれば、設定ファイルが生成されています。 以上が、コンフィグマネージャーの設定ファイルの生成の流れになります。 3. その他(注意点など) 設定ファイルの生成後に、各種パラメーターの内容を変更した場合は、【再生成】を実施する必要があります。 本記事ではあまり触れていませんが、プロファイルセットが保存する内容は、テンプレートや共通パラメーターのIDの組合わせです。つまり、特定のプロファイルセットが参照しているテンプレートや共通パラメーターの内容が変更されてしまった場合は、ロールバックを実施しても、テンプレートや共通パラメーターの変更後の内容が反映されてしまうのでご注意ください。 4. おわりに コンフィグマネージャーの設定ファイルの生成編はいかがだったでしょうか。IoT Connect Gatewayのポータル上で、簡単にIoTデバイス毎の設定ファイルを生成できることがお分かり頂けたかと思います。IoTデバイス毎の設定ファイルをそれぞれ用意する必要は無く、設定ファイルの雛形と設定の差分のみ記述すれば良い仕組みになっています。なので、大量のIoTデバイスを扱う場合でも、比較的少ない記述量で、IoTデバイス毎の設定ファイルを生成できるような便利機能になっています。ご興味がありましたら、ぜひ活用してみてください! *1 : 共通パラメーターの最上層のキーに device は指定できません。2階層目以降では device キーを指定できます。
アバター
こんにちは、イノベーションセンターの福田優真です。 NTT Comでは AWS Outposts を日本で初めて導入し、様々な検証を進めています。 プレスリリース 国内初、「AWS Outposts」に自社データ分析ツールを組み込んだソリューションを開発 Engineers' Blogでの紹介 【日本初導入】 AWS Outposts ラックを徹底解説 第1回 〜導入・利用方法の概要〜 第2回 〜AWS CDKによるInfrastructure as Code〜 第3回 〜TerraformによるPrivate EKS構築〜 AWS Outpostsで実現するオンプレデータレイク Local cluster for EKS on AWS Outpostsについて紹介 今回は Amazon ElastiCache を Infrastructure as Code (IaC) によって Outposts 上へとプロビジョニングする際に得られたノウハウを共有したいと思います。 AWS Outposts AWS Outposts は AWS が提供する、ハイブリッドクラウド 1 を実現するための製品です。 AWS が用意するラックやサーバーをオンプレ内に設置することで Amazon EC2 や Amazon S3 といったサービスをオンプレで利用できるようにしてくれます。 これによって AWS のサービスの柔軟性をオンプレ上に展開しつつ、データはオンプレ内のみで処理し、必要なところは AWS の各種サービスとシームレスに連携するといったことが可能になります。 また、オンプレ側に AWS サービスを展開できるため、 AWS のサービスを非常に近いロケーションで利用可能になるため、低レイテンシーが求められる関係で AWS の導入が難しかった場面でも AWS のサービスが利用できるようになります。 このようにオンプレ内へ AWS サービスの柔軟性を持ち込みつつ、オンプレとの連携をシームレスにやってくれるソリューションが Outposts です。 より詳しく Outposts について知りたい方は先述した本エンジニアブログの記事を更に参考にしてみてください。 現在、 Outposts は 42U ラックタイプと 1U/2U のサーバータイプの2種類が販売されていますが、今回利用する ElastiCache on Outposts は 42U ラックタイプでのみ提供されています 2 。 Amazon ElastiCache Amazon ElastiCache とは AWS が提供するフルマネージドで Memcached/Redis 互換なインメモリキャッシングサービスです。 パッチ適用やモニタリング、設定作業等は AWS 側にまかせつつ高速なキャッシュを利用できるため、本サービスを利用すれば自前で Memcached や Redis を運用するよりも楽にプロビジョニング・運用を行えます。 また、スケーリング等もノードの追加や削除のみを行えば自動で行ってくれるため、スケーラブルなキャッシュシステムを簡単に構築・運用できます。 AWS Cloud Development Kit AWS Cloud Development Kit (CDK) は、 AWS 上のリソースを TypeScript や Go といったプログラミング言語を通して管理できるようにしてくれるフレームワークです。 CDK はリソースの状態を記述したテンプレートを元に各種リソースのプロビジョニングを行う AWS CloudFormation というサービスをバックエンドで用いています。 CloudFormation 自体は YAML や JSON でテンプレートを記述します。 そのため、全ての設定値を宣言的に書かなければならず、同じような設定値を何度も記述したり、一部プロパティが同じ設定値となる似たようなリソースをそれぞれ別個に記述したりしなければなりません。 CloudFormation は指定された配列の内容を Join する関数や、その配列の中から指定された位置の要素を取り出したりといった最低限の機能は提供されていますが、インフラの状態が大きくなればなるほど記述するテンプレートの行数は膨大になっていきます。 CDK はこれを TypeScript や Go を利用することで解決します。 具体的には TypeScript 等で記載されたコードからテンプレートを生成してくれます。 CDK を使うことで、ユーザーはプログラミング言語の関数化やクラス化といった強力な抽象力を用いてインフラのあるべき状態を記述できます。 プログラミング言語の抽象化力によって、ユーザーは YAML テンプレートと比較してより少ない記述で AWS リソースの状態を記述できます。 今回はこの CDK を通して ElastiCache on Outposts を利用する方法について解説します。 なお、ここでは CDK v2 のものを利用するので、 CDK v1 のものを利用したい方は別途読み替えてください。 ElastiCache on Outposts のプロビジョニング VPC を準備する(必須) ElastiCache は VPC 上にあるサブネットの上で動作します。 さらに、 ElastiCache を Outposts で利用するにはそのサブネットを Outpost 上へデプロイしておかなければなりません。 イメージとしては次のような図の通りの構成が要求されます。 CDK による Outposts 上への VPC とサブネットのプロビジョニングは、以前私が書いた 記事 で紹介しているので、そちらを参考にしてください。 この Outposts 上へプロビジョニングしたサブネットはそのままだと ElastiCache のサブネットとしては使えません。 ElastiCache にプロビジョニングしたサブネットを利用させるにはそのサブネットを含むサブネットグループというものを作る必要があります。 サブネットグループとは複数個のサブネットを1つの固まりとしたものです。 // TypeScript による例 import * as cdk from "aws-cdk-lib" ; import { Construct } from "constructs" ; export class Stack extends cdk.Stack { constructor( scope: Construct , id: string , props?: cdk.StackProps ) { super( scope , id , props ); const subnetGroup = new cdk.aws_elasticache.CfnSubnetGroup ( this , "SubnetGroup" , { subnetIds: [ "<Outposts 上にデプロイしたサブネットの ID>" ] , description: "<デプロイするサブネットグループを設置する説明等>" , cacheSubnetGroupName: "<サブネットグループの名前>" } ); } } subnetIds にはサブネットグループへと登録したい Outposts 上にデプロイしたサブネットが持つ ID ( subnet-xxx という形式をしています)を指定します。 また、 description にはデプロイするサブネットグループがどのような目的で設置されているのかといった説明を自由に記述できます。 ここに指定した文字列はデプロイしたサブネットグループの サブネットグループの詳細 画面の 説明 という欄に表示されます。 この description にはよりわかりやすくそのサブネットグループがデプロイされた経緯等を記載しておくとあとでデプロイされた目的等を把握しやすくなります。 cacheSubnetGroupName にはデプロイするサブネットグループに付ける名前を指定します。 ただし、この cacgeSubnetGroupName はデプロイ先アカウントで既にデプロイされている他のサブネットグループと被らないようにする必要があります。 名前が被った状態でデプロイしようとすると Cache subnet group <サブネットグループの名前> already exists. というエラーでデプロイできません。 ここまでで ElastiCache を利用するための準備は完了です。 クラスターモードが無効な ElastiCache をデプロイする Outposts 上にプロビジョニングする方法はパブリッククラウド上にプロビジョニングする方法と変わりません。 Outposts へデプロイするにあたって本質的な部分となるのは Outposts 上へサブネットをプロビジョニングする部分であり、それさえ済んでしまえばあとはパブリッククラウドへプロビジョニングする通常の ElastiCache と変わりはありません。 // TypeScript による例 const replicationGroup = new cdk.aws_elasticache.CfnReplicationGroup ( this , "ReplicationGroup" , { cacheNodeType: "cache.m5.large" , replicationGroupDescription: "<デプロイする ElastiCache クラスターの設置目的の説明等>" , replicationGroupId: "<ElastiCache クラスターの ID>" , cacheSubnetGroupName: subnetGroup.cacheSubnetGroupName , engine: "redis" , engineVersion: "6.2" , cacheParameterGroupName: "default.redis6.x" , snapshotRetentionLimit: 1 , numCacheClusters: 1 , automaticFailoverEnabled: false , multiAzEnabled: false , } ); 設定しているプロパティについて解説します。 cacheNodeType プロパティにはデプロイしたい ElastiCache のノードタイプを設定します。 ここに指定できるものは購入している Outposts 次第で変化します。 パブリック AWS 上の ElastiCache は様々なノードタイプを選択できます 3 が、 NTT Com 側で購入している Outposts は m5 のみ利用できるので、 cache.m5.large を指定しています。 おそらく、 r5 等を利用できる Outposts 上では cache.r5.large 等の指定が可能となるはずです。 replicationGroupDescription ですが、ここはサブネットグループの description プロパティと同じです。 このプロパティに設定した文字列が クラスターの詳細 画面の 説明 欄に表示されます。 replicationGroupId は ElastiCache クラスターに付ける名前を指定します。 ただし、名前は既にプロビジョニング済みである他の ElastiCache クラスターと被らないようにする必要があります。 cacheSubnetGroupName に先程 Outposts 上のサブネットを含めたサブネットグループの名前を指定します。 サブネットグループには cacheSubnetGroupName というプロパティがあるので、先に作成したサブネットグループの cacheSubhetGroupName を指定しています。 engine には使用する ElastiCache のエンジンを指定します。 今回は Redis を利用するので "redis" を指定します。 engineVersion には使用するエンジンのバージョンを指定します。 今回は "6.2" を指定して Redis v6.2 を利用するようにしています。 cacheParameterGroupName には ElastiCache のパラメータをまとめたパラメータグループというものの名前を指定します。 パラメータグループの画面には Memcached/Redis のバージョン毎のデフォルトパラメータを設定したパラメータグループがあり、今回は Redis の6系のパラメータグループを指定します。 numCacheClusters はクラスター数を指定するプロパティです。今回は検証用途で設定するため、 Outposts の容量を使いすぎないよう 1 に設定しています。 automaticFailoverEnabled は名前の通り、フェイルオーバー機能を有効にするかを設定します。 複数のクラスターをデプロイする( numCacheClusters を 1 よりも大きい値に設定する)場合 true を設定できます。 しかし、今回は numCacheClusters を 1 と設定しており、このとき automaticFailoverEnabled は false に設定しなければなりません。 そのため、このプロパティには false を指定しています。 multiAzEnabled は複数の AZ で機能させるようにするかを指定するプロパティですが、今回は1つの Outposts でしか利用しないため、このプロパティには false を指定しています。 設定自体は以上で完了です。 しかし、実際にデプロイしようとするとサブネットグループと同時にデプロイしようとしている場合、サブネットグループの作成完了を待たずに並行して Redis クラスターのデプロイを試そうとします。 その時点ではサブネットグループの作成が完了していないため、デプロイ時に Cace subnet group "<プロビジョニングするサブネット名>" does not exist というエラーが出てデプロイに失敗します。 これを解決するには、作成したいサブネットグループと Redis クラスターの間にデプロイの順序関係を持たせます。 replicationGroup.addDependsOn ( subnetGroup ); このコードによってサブネットグループがデプロイされたのちに Redis クラスターがデプロイされるという順序関係を宣言できます。 この関係性は削除のときにも発揮され、まずサブネットグループに依存している Redis クラスターが削除されてからサブネットグループが削除されるという順番になります。 以上の設定を施してデプロイすると ElastiCache クラスターが Available になるまで待ったのちにデプロイ完了となります。 Memcached / クラスターモード有効な Redis をデプロイする Memcached やクラスターモードが有効な Redis もデプロイ可能なのですが、こちらはクラスターモードが無効な Redis クラスターと違い、いくつか注意点が存在します。 まず、 CloudFormation によるデプロイですが、元から CloudFormation で用意されている ElastiCache をデプロイするためのコンポーネントである AWS::ElastiCache::CacheCluster との相性が良くありません。 私たちも このリソースに対応する CDK リソース でデプロイしようと試みてみましたが、 Can specify AZ arguments for outpost subnet group というエラーが出てデプロイできませんでした。 こういった CloudFormation が提供しているコンポーネントが利用できない場合、カスタムリソースというものを利用してデプロイを行うことになります。 カスタムリソースとは AWS API 等を実行する Lambda 関数を用意し、 CloudFormation によるプロビジョニング時にその Lambda 関数を実行して CloudFormation の枠組みでリソースのライフサイクルを管理する仕組みです。 これによって CloudFormation が提供しているコンポーネントでは扱えないリソースや新しい機能などを、 CloudFormation の枠組みの中で管理できるようになります。 今回のように Outposts 上へ CloudFormation からリソースをプロビジョニングする場合は、しばしば CloudFormation の提供するコンポーネントとの相性が悪いため、カスタムリソースを利用して管理することが多いです。 デプロイ方法ですが、次のようにします。 // TypeScript による例 const cacheCluster = new cdk.custom_resources.AwsCustomResource ( this , "CacheCluster" , { policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls ( { resources: cdk.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE , } ), onCreate: { service: "ElastiCache" , action: "createCacheCluster" , physicalResourceId: cdk .custom_resources .PhysicalResourceId.fromResponse ( "CacheCluster.CacheClusterId" ), parameters: { CacheClusterId: "<ElastiCache クラスター名>" , NumCacheNodes: 1 , CacheNodeType: "cache.m5.large" , // Redis を利用する場合は `Engine` に "redis" を指定します Engine: "memcached" , // Engine が `redis` である場合は Redis のバージョン(たとえば `6.2`)を指定します EngineVersion: "1.6.12" , // Engine が `redis` である場合はパラメータグループも Redis のもの // (たとえば6系の Redis を利用するのであれば `default.redis6.x`)を指定します CacheParameterGroupName: "default.memcached1.6" , CacheSubnetGroupName: subnetGroup.cacheSubnetGroupName , } , } , // ここはちゃんと動いていませんが、参考として // onDelete: { // service: "ElastiCache", // action: "deleteCacheCluster", // parameters: { // CacheClusterId: "<ElastiCache クラスター名>" // } // }, } ); CloudFormation ではカスタムリソースのプロビジョニング時に呼ばれる Lambda 関数のコードを自前で書いたり、その Lambda 関数を CloudFormation が呼び出せるようにグルーコードを書いたりする必要があります。 一方、 CDK を利用している場合、 AWS API を呼び出すのみであればこれら Lambda 関数のコードや Lambda 関数を CloudFormation から駆動するためのグルーコードをラップしてくれている AwsCustomResource というコンポーネントを利用できます。 この AwsCustomResource は作成時や削除時に呼ばれる AWS API の名前や設定値を記載するだけで指定した AWS API を呼び出すカスタムリソースを用意できます。 今回は ElastiCache の CreateCacheCluster という API を使ってクラスターモードが有効な ElastiCache on Outposts クラスターを構築したいので、 service に ElastiCache を指定し、 action に createCacheCluster を指定しています。 AwsCustomResource 内で用意される Lambda 関数は JavaScript で記述されている 4 ようで、 AWS SDK for JavaScript の API リファレンス から serivce に利用したいサービスと対応するサービス名を、 action へ利用したい API と対応したメソッド名を指定します。 たとえば、 EC2 のインスタンスを AwsCustomResource で作成するのであれば、 service に EC2 を、 action に runInstances を指定します。 physicalResourceId には AWS API のレスポンスにある CacheCluster.CacheClusterId というプロパティの値を設定するようにしています。 基本的に physicalResourceId には AWS API のレスポンス内に含まれるリソースの ID を指定すれば問題ありません。 リソースの ID がどのプロパティに含まれているのかは AWS SDK for JavaScript の API リファレンスを参照してください。 AWS SDK for JavaScript のレスポンスも JavaScript のオブジェクトなので、プロパティにアクセスする際は JavaScript オブジェクトのプロパティアクセスと同じ表記でアクセスします(例示したコードであれば CacheCluster オブジェクト内に CacheClusterId というプロパティがあるので、 CacheCluster.CacheClusterId を指定しています)。 // TypeScript による例 // レスポンスから Physical Resource ID を取得してくれる便利メソッド cdk.custom_resources.PhysicalResourceId.fromResponse ( // レスポンス内の CacheCluster.CacheClusterId プロパティにデプロイした // ElastiCache クラスターの ID が入っているので、これを Physical Resource ID として利用する "CacheCluster.CacheClusterId" ) parameters には AWS SDK for JavaScript の各種メソッドに指定するパラメータ値をそのまま記載します。 今回のコード例であれば createCacheCluster メソッドに指定する値として次のものを指定しています。 CacheClusterId NumCacheNodes Engine EngineVersion CacheParameterGroupName CacheSubnetGroupName ほとんどはクラスターモードが無効な ElastiCache クラスターと同じプロパティなので、差分である CacheClusterId について説明します。 こちらはクラスターモードが無効な ElastiCache クラスターの replicationGroupId に相当するもので、プロビジョニングする ElastiCache クラスターにつける名前を指定します。 ただし、指定する名前は既にデプロイされている他の ElastiCache クラスターと被らないものを設定する必要があります。 クラスターモードが有効な ElastiCache クラスターもクラスターモードが無効な Redis クラスターと同じで先にサブネットグループが作成されている必要があるため、次のようにしてデプロイ順序を設定しておく必要があります。 // TypeScript による例 cacheCluster.node.addDependency ( subnetGroup ); 今回は ElastiCache クラスターの削除部分をコメントアウトによって無効化していますが、削除する場合は onDelete へ使用する AWS API に対応した AWS SDK for JavaScript のサービス名とメソッド名を各 service , action プロパティに指定します。 ElastiCache であれば ElastiCache サービスに deleteCacheCluster というものがあるので、そちらを serivce と action に設定します。 deleteCacheCluster は削除するクラスター名だけ指定すればいいので、 parameters には削除するキャッシュクラスター ID を指定しています。 デプロイ方法を紹介するために簡便な AwsCustomResource を利用していますが、これをそのまま使うには注意が必要です。 AwsCustomResource は対象とするリソースの各ライフサイクル(削除/更新/作成)にあたって AWS API を1つしか指定できません。 今回であれば CreateCacheCluster API, DeleteCacheCluster API を削除/作成のライフサイクルに利用しているため、これらライフサイクルにて他の AWS API を利用できません。 なので、 ElastiCache のように AWS API からのレスポンスはすぐに返ってくるが、リソースの初期化処理や削除処理がその後に裏で動くような場合に問題が発生します。 CreateCacheCluster API はリソースの作成までは待機してくれるものの、リソースの初期化処理までは待機してくれません。 初期化が終了しなければデプロイした ElastiCache クラスターは削除できないため、 CDK や CloudFormation を通して初期化処理が終了するまでリソースの削除を行えません。 さらに、作成した ElastiCache クラスターの削除についても DeleteCacheCluster API を利用しますが、こちらも削除が完了するのを待たずに削除が開始した時点でレスポンスが返ってきます。 これによって、リソースの削除時に削除が完了していないがカスタムリソースとしてはレスポンスが返ってきた時点でリソースの削除は完了したとみなされます。 すると、まだ ElastiCache クラスターが削除されていないのに ElastiCache クラスターを削除したと見做されサブネットグループの削除が開始されます。 しかし、サブネットグループは ElastiCache クラスターが完全に削除されなければ削除できないため、サブネットグループの削除に失敗するという状況が発生します。 これは今回紹介する AwsCustomResource では回避できませんが、 AwsCustomResource の利用を止め、自前でカスタムリソースをハンドルする Lambda 関数を用意すれば回避できます。 具体的にはハンドラーである Lambda 関数へリソースの完全削除等を監視する仕組みを入れれば問題は回避できます 5 。 まとめ ElastiCache on Outposts を利用する手段を CDK を用いた場合で解説しました。 ElastiCache on Outposts 自体は AWS のマネージメントコンソールからオンプレミス上にデプロイするように指定すればデプロイできるのですが、 IaC をしたい場合はここに載せた方法を参考にしてもらえれば幸いです。 ただし、記事中でも触れていますがまだ上手くデプロイできなかったりデプロイは可能なものの CDK/CloudFormation での管理をしづらかったりと IaC をやるには難しい部分もあります。 サブネットグループとクラスターのみあれば最低限十分であるため、 ElastiCache だけ IaC の枠組みからはずして管理するということも選択肢には入ると思います。 一方、クラスターモードを無効にした Redis であれば CloudFormation の範囲でデプロイでき、きちんと Available になるまで待機してリソースの作成完了としてくれるため、 Outposts 上で動かすのも Public AWS と変わらないレベルで簡単にデプロイし管理できるのは驚きでした。 すなわち、簡単にオンプレミス上にフルマネージドな Redis クラスターを作成・運用できるため、運用の大部分を簡素化できますし、 IaC による管理も可能となりインフラの再現性も高めることができます。 最後になりますが、 Outposts 上の ElastiCache を利用する場合の参考にしていただければと思います。 今日、 ハイブリッドクラウド の語は、クラウドベンダーごとに異なる解釈が与えられています。この記事では AWS Outposts を紹介しますので、 AWS の提唱する クラウドからオンプレミス、そしてエッジまで、必要な場所で一貫した AWS エクスペリエンスを提供 することをハイブリッドクラウドであるとします( 参考 )。 ↩ AWS Outposts の 概要 にある 特徴の比較 に AWS Outposts ラックは ElastiCache の記載があり、一方 AWS Outposts サーバーにはないことが確認できます。 ↩ https://docs.aws.amazon.com/ja_jp/AmazonElastiCache/latest/red-ug/CacheNodes.SupportedTypes.html ↩ https://github.com/aws/aws-cdk/blob/8b20446aeccc787de1511f01dd27cc590d57ce89/packages/%40aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts#L397 ↩ ただし、 Lambda 関数の最大実行時間は 15分 です。したがってデプロイ時間を超える場合は回避するのがより難しいものとなります。 ↩
アバター
サマリ Segment Routing Traffic Engineering(SR-TE)を一元管理可能な経路計算サーバである Path Computation Element(PCE)を紹介 SR-PCE を用いて PE へ SR Policy を送信し、SR-TE の一元管理を検証 この記事は Multi-AS Segment Routing 検証連載の第 10 回です。目次は こちら 概要 イノベーションセンターの三島です。 本記事では SR-TE を効率的に管理・運用するための実現手法である PCE を紹介し、PCE を用いた SR Policy の管理を検証します。 SR-TE の問題点と Path Computation Element(PCE) SR-TE は様々な要件を実現するために使われます。 例えば、複数の利用者・複数のアプリケーションごとの目的に応じた Quality of Service(QoS)要件、 Service Function Chaining(SFC)要件、リンク利用率の要件などです。 これらの要件の中には帯域保証やリンク利用率の最適化など、ネットワーク全体の状態を把握したときにのみ真に最適なパスを計算できるものが存在します。 そのような複雑な要件を満たす SR Policy を各 PE ルーターで分散して計算するには限界があります。 一方で、SR-TE の実装には各 PE ルーターへの設定が必要です。 台数が増えるに従って設定作業のみならず、パスに用いる SID をはじめとしたパラメータの管理も複雑になります。 そこでコントローラーを用いて SR-TE を一元管理することにより、ネットワーク全体の状態を把握したうえでパスを計算し、かつ、設定項目を集約できることが期待されます。 このような SR-TE の一元管理を実現する技術として Path Computation Element(PCE)があります。 PCE は、歴史的には MPLS 網の TE を一元管理するために考案された技術であり、 RFC5440/8231/8281/8664 などで標準化されています。 一般的な PCE の構成を下記に示します。 PCE から SR-TE の経路を受け取るノードを Path Computation Client(PCC)と呼びます。 PCC は、PCE から Segment List や Policy 名、Color など、ある宛先に対する SR Policy を構成するための情報を受信し、SR-TE を実現します。 PCE と PCC の間は、SR ネットワークの LinkState の共有(図中緑部分の通信)と、作成した経路の共有(図中水色部分の通信)が行われます。 PCEP や BGP SR Policy は経路を state として、NETCONF は経路を config としてそれぞれ扱うことができます。 LinkState の共有 LinkState の共有には、BGP-LS という技術が用いられます。 BGP-LS とは、Multi-Protocol BGP(MP-BGP)を利用した LinkState の共有技術であり、RFC7752 で標準化されています。 MP-BGP の Address Family Identifier(AFI)16388、Sub-AFI(SAFI)71 を利用し、IS-IS や OSPF の LinkState を送信します。 PCE は BGP-LS により SR ネットワークの LinkState を収集することで、SR domain の トポロジーと SID 情報を入手し経路計算を実現します。 経路情報の共有 経路情報の共有には、BGP SR Policy、NETCONF の他に、Computation Element Communication Protocol(PCEP)という技術が用いられます。 PCEP とは、PCE-PCC 間や複数の PCE 間の通信を実現するプロトコルです。 TCP port 4189 を利用し、Segment List や SR Policy 名、Affinity など様々な情報を送受信できます。 なお、本記事では PCEP を利用して検証を実施します。 Stateless PCE と Stateful PCE PCE には、発行済み の 経路(LSP / SR Policy)や帯域などの予約済みリソース、パス計算要求などの情報を管理しない Stateless PCE と、これらの情報を管理する Stateful PCE が存在します。 Stateful PCE は既存の予約済みリソースを管理するため、それらを条件として考慮した複雑な経路を計算することが可能となります。 Stateful PCE には、PCC からのリクエストに基づいた経路計算のみを行う Passive Stateful PCE と、自ら経路を発行し PCC へ送信する Active Stateful PCE が存在します。 Active Stateful PCE は新規 SR Policy の発行や既存 SR Policy の更新などの操作を実現できるため、SR Policy の集中的な管理に適しています。 次章では、Active Stateful PCE による SR Policy の集中的な管理を目的とし動作検証を行います。 検証 本章では、IOS XR に実装された SR-PCE 機能を利用し、Cisco/Juniper それぞれの PCC に SR Policy を送信します。 検証に使用するトポロジーは下記の通りです。 このトポロジーにおいて PCE から PE1/PE2 のそれぞれに SR Policy を送信し、下記の経路を実現します。 以降では、下記の手順で PCE による SR-TE の一元管理を検証します。 BGP-LS ・ PCEP の設定: PCE ・ PCC のそれぞれに BGP-LS ・ PCEP を設定し、Active Stateful PCE による SR-TE の一元管理環境を構成 SR Policy の設定: PCE に対し、送信する SR Policy を設定 動作確認: BGP-LS ・ PCEP セッション・ SR Policy の状態確認と Traceroute による動作確認 1. BGP-LS ・ PCEP の設定 PCE BGP-LS PE1 ・ PE2 向けの BGP-LS 設定を投入します。 ※ 最低限1つのピアを設定すれば BGP-LS ですべての情報を受信し動作可能です。本検証では冗長化と Multi-vendor の相互接続検証を兼ねるため、双方に向けて設定します。 router bgp 65000 bgp router-id 10.255.0.254 address-family link-state link-state ! neighbor 10.0.255.1 remote-as 65000 address-family link-state link-state ! ! neighbor 10.0.255.2 remote-as 65000 address-family link-state link-state ! ! ! PCEP PCEP の待ち受けアドレスを指定します。 ※ IOS XR の SR-PCE 機能の仕様上、PCC 側が PCEP セッションに利用するアドレスは、BGP-LS の LinkState から参照可能な必要があるようです。そのため本検証環境では Loopback アドレスを PCEP セッションに利用します。 pce address ipv4 10.0.255.254 PCC/PE1(IOS XR) BGP-LS IS-IS を TED に export し、BGP-LS で PCE へ広告します。 router isis 1 distribute link-state instance-id 32 ! router bgp 65000 address-family link-state link-state ! neighbor 10.255.0.254 remote-as 65000 update-source Loopback0 address-family link-state link-state ! ! ! PCEP PCC 機能を設定し、PCEP を利用可能にします。 segment-routing traffic-eng pcc source-address ipv4 10.255.0.1 pce address ipv4 10.255.0.254 PCC/ PE2(Junos) BGP-LS IS-IS を TED に export し、BGP-LS で PCE へ広告します。 set policy-options policy-statement TE term 1 from family traffic-engineering set policy-options policy-statement TE term 1 then accept set protocols mpls traffic-engineering database import policy TE set routing-options autonomous-system 65000 set protocols bgp group SR-PCE type internal set protocols bgp group SR-PCE local-address 10.255.0.2 set protocols bgp group SR-PCE family traffic-engineering unicast set protocols bgp group SR-PCE export TE set protocols bgp group SR-PCE neighbor 10.255.0.254 PCEP PCC 機能を設定し、PCEP を利用可能にします。 set protocols mpls lsp-external-controller pccd set protocols source-packet-routing lsp-external-controller pccd set protocols pcep pce SR-PCE local-address 10.255.0.2 set protocols pcep pce SR-PCE destination-ipv4-address 10.255.0.254 set protocols pcep pce SR-PCE pce-type active set protocols pcep pce SR-PCE pce-type stateful set protocols pcep pce SR-PCE lsp-provisioning set protocols pcep pce SR-PCE spring-capability 2. SR Policy の設定(PCE) PE1 から PE2、 PE2 から PE1 への各 SR-TE 経路を記述した SR Policy を設定します。 pce segment-routing traffic-eng segment-list name PE1-to-PE2 index 1 mpls label 16003 index 2 mpls label 16004 index 3 mpls label 16002 ! segment-list name PE2-to-PE1 index 1 mpls label 16004 index 2 mpls label 16003 index 3 mpls label 16001 ! peer ipv4 10.255.0.1 policy PE1 candidate-paths preference 1 explicit segment-list PE1-to-PE2 ! ! ! ! ! peer ipv4 10.255.0.2 policy PE2 candidate-paths preference 1 explicit segment-list PE2-to-PE1 ! ! ! ! ! ! ! ! 3. 動作確認 PCE BGP-LS BGP-LS による LinkState 受信を確認します。(出力が長いため一部省略) RP/0/RP0/CPU0:pce#show bgp link-state link-state Thu Oct 13 17:10:53.338 JST BGP router identifier 10.255.0.254, local AS number 65000 BGP generic scan interval 60 secs Non-stop routing is enabled BGP table state: Active Table ID: 0x0 RD version: 339 BGP main routing table version 339 BGP NSR Initial initsync version 13 (Reached) BGP NSR/ISSU Sync-Group versions 0/0 BGP scan interval 60 secs Status codes: s suppressed, d damped, h history, * valid, > best i - internal, r RIB-failure, S stale, N Nexthop-discard Origin codes: i - IGP, e - EGP, ? - incomplete Prefix codes: E link, V node, T IP reacheable route, S SRv6 SID, u/U unknown I Identifier, N local node, R remote node, L link, P prefix, S SID L1/L2 ISIS level-1/level-2, O OSPF, D direct, S static/peer-node a area-ID, l link-ID, t topology-ID, s ISO-ID, c confed-ID/ASN, b bgp-identifier, r router-ID, s SID i if-address, n nbr-address, o OSPF Route-type, p IP-prefix d designated router address Network Next Hop Metric LocPrf Weight Path *>i[V][L2][I0x0][N[c65000][s0000.0aff.0001.00]]/264 10.255.0.2 100 0 i *>i[V][L2][I0x0][N[c65000][s0000.0aff.0002.00]]/264 10.255.0.2 100 0 i *>i[V][L2][I0x0][N[c65000][s0000.0aff.0003.00]]/264 10.255.0.2 100 0 i *>i[V][L2][I0x0][N[c65000][s0000.0aff.0004.00]]/264 <略> PCEP PCC とのセッションを確認します。 detail オプションを 利用することで PCEP で送受信されたメッセージの詳細を確認できます。 RP/0/RP0/CPU0:pce#show pce ipv4 peer detail Thu Oct 13 16:55:22.855 JST PCE's peer database: -------------------- Peer address: 10.255.0.1 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation PCEP has been up for: 00:02:04 PCEP session ID: local 0, remote 0 Sending KA every 30 seconds Minimum acceptable KA interval: 20 seconds Peer timeout after 120 seconds Maximum SID Depth: 10 Statistics: Keepalive messages: rx 4 tx 5 Request messages: rx 0 tx 0 Reply messages: rx 0 tx 0 Error messages: rx 0 tx 0 Open messages: rx 1 tx 1 Report messages: rx 4 tx 0 Update messages: rx 0 tx 0 Initiate messages: rx 0 tx 0 Last PCError: Received: None Sent: None Peer address: 10.255.0.2 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation PCEP has been up for: 00:00:49 PCEP session ID: local 0, remote 0 Sending KA every 30 seconds Minimum acceptable KA interval: 20 seconds Peer timeout after 120 seconds Maximum SID Depth: 5 Statistics: Keepalive messages: rx 2 tx 2 Request messages: rx 0 tx 0 Reply messages: rx 0 tx 0 Error messages: rx 0 tx 0 Open messages: rx 1 tx 1 Report messages: rx 4 tx 0 Update messages: rx 0 tx 2 Initiate messages: rx 0 tx 1 Last PCError: Received: None Sent: None Stateful PCE として、それぞれの PE に送信済みの SR Policy を管理できていることを確認します。 RP/0/RP0/CPU0:pce#show pce lsp detail Thu Oct 13 16:55:48.352 JST PCE's tunnel database: ---------------------- PCC 10.255.0.1: Tunnel Name: PE1 Color: 1 Interface Name: srte_c_1_ep_10.255.0.2 LSPs: LSP[0]: source 10.255.0.1, destination 10.255.0.2, tunnel ID 4, LSP ID 2 State: Admin up, Operation up Setup type: Segment Routing Binding SID: 24014 Maximum SID Depth: 10 Preference: 128 Bandwidth: requested 0 kbps, applied 0 kbps Protection type: protected-preferred PCEP information: PLSP-ID 0x4, flags: D:0 S:0 R:0 A:1 O:1 C:1 LSP Role: Single LSP State-sync PCE: None PCC: 10.255.0.1 LSP is subdelegated to: None Reported path: Metric type: TE, Accumulated Metric 0 SID[0]: Node, Label 16003, Address 10.255.0.3 SID[1]: Node, Label 16004, Address 10.255.0.4 SID[2]: Node, Label 16002, Address 10.255.0.2 Computed path: (Local PCE) Computed Time: Wed Oct 12 20:25:43 JST 2022 (20:30:05 ago) Metric type: TE, Accumulated Metric 0 SID[0]: Node, Label 16003, Address 10.255.0.3 SID[1]: Node, Label 16004, Address 10.255.0.4 SID[2]: Node, Label 16002, Address 10.255.0.2 Recorded path: None Disjoint Group Information: None PCC 10.255.0.2: Tunnel Name: PE2 LSPs: LSP[0]: source 10.255.0.2, destination 10.255.0.1, tunnel ID 4, LSP ID 0 State: Admin up, Operation active Setup type: Segment Routing Binding SID: None Maximum SID Depth: 5 Preference: 100 Bandwidth: requested 0 kbps, applied 0 kbps Protection type: protected-preferred PCEP information: PLSP-ID 0x3, flags: D:1 S:0 R:0 A:1 O:2 C:1 LSP Role: Single LSP State-sync PCE: None PCC: 10.255.0.2 LSP is subdelegated to: None Reported path: Metric type: TE, Accumulated Metric 0 SID[0]: Node, Label 16004, Address 10.255.0.4 SID[1]: Node, Label 16003, Address 10.255.0.3 SID[2]: Node, Label 16001, Address 10.255.0.1 Computed path: (Local PCE) Computed Time: Thu Oct 13 16:55:09 JST 2022 (00:00:39 ago) Metric type: TE, Accumulated Metric 0 SID[0]: Node, Label 16004, Address 10.255.0.4 SID[1]: Node, Label 16003, Address 10.255.0.3 SID[2]: Node, Label 16001, Address 10.255.0.1 Recorded path: SR IPv4 Node 16004, Node-id: 10.255.0.4, flags 0x0 SR IPv4 Node 16003, Node-id: 10.255.0.3, flags 0x0 SR IPv4 Node 16001, Node-id: 10.255.0.1, flags 0x0 Disjoint Group Information: None PCC: IOS XR(PE1) PCEP PCEP セッションを確認します。 RP/0/RP0/CPU0:pe1#show segment-routing traffic-eng pcc ipv4 peer Thu Oct 13 16:57:04.343 JST PCC's peer database: -------------------- Peer address: 10.255.0.254, Precedence: 255, (best PCE) State up Capabilities: Stateful, Update, Segment-Routing, Instantiation PCE から受信した SR Policy を確認します。 RP/0/RP0/CPU0:pe1#show segment-routing traffic-eng policy Thu Oct 13 16:58:13.189 JST SR-TE policy database --------------------- Color: 1, End-point: 10.255.0.2 Name: srte_c_1_ep_10.255.0.2 Status: Admin: up Operational: up for 20:32:28 (since Oct 12 20:25:44.366) Candidate-paths: Preference: 128 (PCEP) (active) Name: PE1 Requested BSID: dynamic PCC info: Symbolic name: PE1 PLSP-ID: 4 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce) (valid) Metric Type: TE, Path Accumulated Metric: 0 16003 [Prefix-SID, 10.255.0.3] 16004 [Prefix-SID, 10.255.0.4] 16002 [Prefix-SID, 10.255.0.2] Attributes: Binding SID: 24014 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Traceroute Traceroute により SR Policy による TE を確認します。 RP/0/RP0/CPU0:pe1# traceroute 192.168.1.254 vrf 100 Thu Oct 13 16:58:35.907 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.0.2 [MPLS: Labels 16004/16002/16 Exp 0] 12 msec 3 msec 10 msec 2 10.0.2.2 [MPLS: Labels 16002/16 Exp 0] 2 msec 7 msec 3 msec 3 192.168.1.254 6 msec 11 msec 2 msec PCC: Junos(PE2) PCEP PCE とのセッションを確認します。 user@pe2> show path-computation-client active-pce brief PCE SR-PCE -------------------------------------------- General PCE IP address : 10.255.0.254 Local IP address : 10.255.0.2 Priority : 0 PCE status : PCE_STATE_UP Session type : PCE_TYPE_STATEFULACTIVE LSP provisioning allowed : On P2MP LSP report allowed : Off P2MP LSP update allowed : Off P2MP LSP init allowed : Off PCE-mastership : main PCE Traffic Steering : Off Counters PCReqs Total: 0 last 5min: 0 last hour: 0 PCReps Total: 0 last 5min: 0 last hour: 0 PCRpts Total: 4 last 5min: 4 last hour: 4 PCUpdates Total: 2 last 5min: 2 last hour: 2 PCCreates Total: 1 last 5min: 1 last hour: 1 Timers Local Keepalive timer: 30 [s] Dead timer: 120 [s] LSP cleanup timer: 60 [s] Remote Keepalive timer: 30 [s] Dead timer: 120 [s] LSP cleanup timer: 0 [s] Errors PCErr-recv PCErr-sent PCE-PCC-NTFS PCC-PCE-NTFS Pcupdate empty ero action counters Send-err : 0 Tear down path : 0 Routing decision : 0 Routing decision failed: 0 PCE から受信した SR Policy を確認します。 user@pe2> show spring-traffic-engineering lsp detail Name: PE2 Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.0.1 State: Up Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: IPv4 Node ID, Node address: 10.255.0.4 SID type: 20-bit label, Value: 16004 Hop 2 (Strict): NAI: IPv4 Node ID, Node address: 10.255.0.3 SID type: 20-bit label, Value: 16003 Hop 3 (Strict): NAI: IPv4 Node ID, Node address: 10.255.0.1 SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) Traceroute Traceroute により SR Policy による TE を確認します。 user@pe2> traceroute 192.168.0.254 routing-instance 100 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 10.0.3.2 (10.0.3.2) 2.653 ms 2.063 ms 1.805 ms MPLS Label=16003 CoS=0 TTL=1 S=0 MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=1 S=1 2 10.0.2.1 (10.0.2.1) 2.305 ms 2.208 ms 2.083 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=2 S=1 3 10.0.0.1 (10.0.0.1) 3.548 ms * 4.584 ms 上記より、PCE による SR Policy の一元管理(送信&状態管理)と、PCC での TE が確認できました。 まとめ 本記事では、PCE を構築し、PCEP を用いた SR Policy の送信と SR-TE の実現を検証しました。 PCE を活用することで、大規模な SR 網の状態を一元管理し、効率的に運用することが可能となります。 次回は各社 PCE 実装を用いた SR Policy の発行方法を検証します。 (2022/11/28 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #11] PCE 実装の検証
アバター
DevOpsプラットフォームの取り組みを紹介する8回目の記事です。 Qmonus Value Stream 開発チームの浅井です。 連載第8回では、Qmonus Value Streamのアーキテクチャとその技術スタックについて紹介します。 これまでの連載ではQmonus Value Streamで利用している技術要素として、 第4回 ではデータ記述言語であるCUE言語を、 第5回 ではKubernetes NativeなオープンソースフレームワークであるTektonを紹介してきました。 Qmonus Value Streamではこれらの技術要素に加え、複数のGCPプロダクトやSaaSを利用することでシステムを構築しています。 本記事では、Qmonus Value Stream全体のアーキテクチャと、利用している主要なフレームワークやサービスについて紹介します。1つ1つの要素についての説明は少ないですが、システムを構成するアーキテクチャの一例として参考になればと思います。 全体像 Qmonus Value Streamのシステムアーキテクチャの概要図です。 クラウド基盤としてGoogle Cloud Platform (GCP) を利用しており、中心となる機能は全てGoolge Kubernetes Engine (GKE) 上に構築しています。 これはこれまでの記事でいくつか触れていますが、CI/CD機能を実現するためにKubernetesの拡張機能を活用していることに由来します。 GKE上では、主要な機能を具備しAPIを提供するAPIサーバ (FastAPI) 、CI/CD機能を提供するためのTektonおよびKubernetesカスタムオペレータ、ログ基盤としてLokiなどが実行されています。 ユーザがQmonus Value Streamを利用する場合、まずCloudflareからWebページを取得し、ブラウザ上での操作に対応したリクエストをAPIサーバに送信します。 APIサーバは受けたリクエストに応じてTekton PipelineRunリソースなどを作成することでCI/CDパイプラインを駆動し、それらのリソースのステータスをAPIで応答しブラウザ上に表示することで、パイプラインのステータス・進捗を可視化しています。 Qmonus Value Streamのシステムアーキテクチャは大きく分けて以下の機能郡に分かれており、それぞれで用いているコンポーネントの役割と特徴について簡単に説明していきます。 WAF (Web Application Firewall)・LB (Load Balancer) メインサービス CI/CD ログ基盤 CI/CD 実行基盤 Metrics Monitoring GCP Service Log コンポーネントと技術スタックの紹介 WAF・LB WAF・LBは、アプリケーションへのアクセスの前段として、アプリケーションへのアクセスの処理と外部公開しているエンドポイントの保護を担当しています。 Cloudflare Cloudflare はContent Delivery Network (CDN) やセキュリティサービスなどを提供するサービスです。 Qmonus Value StreamではフロントエンドアプリケーションのホスティングにClouflare Pagesを利用しています。Cloudflare Pagesを利用することで、githubのpushイベントをトリガーに手軽にビルドと環境へのデプロイができ、安定して手軽にそして早く開発を進めることができます。 また、Cloudflareをリバースプロキシとしても利用しており、外部へ公開しているドメインへのアクセスは全てCloudflareへ集約させています。 その過程でWAFサービスも利用することで、各エンドポイントの保護も行っています。 マネージドWAFを利用することで高いレベルのセキュリティを手軽に保つことができています。 Cloud Load Balancing Cloud Load Balancing (GCLB) はGCPが提供する、パフォーマンスとスケーラビリティに優れた負荷分散を実現するマネージドロードバランササービスです。 Qmonus Value Streamでは外部公開しているサービスは全てGKE上に構築されており、KubernetesにおけるIngressではGCLBを利用しています。Ingressには他にも選択肢がありますが、GKEと手軽に連携ができる他、Cloud LoggingやCloud ArmorといったGCPのサービスとの連携もやりやすいという利点があるため、GCLBを利用しています。 Cloud Armor Cloud Armor は、インターネットからアプリケーションへの攻撃を保護するIPフィルタリングやWAFなどの機能を持つGCPのマネージドセキュリティサービスです。外部APIを保護するために利用しています。 メインサービス メインサービスは、Qmonus Value Streamが扱うリソースやCI/CD基盤を管理し、REST APIを提供しています。 NuxtJSを用いて実装されたユーザインタフェースと組み合わせて使用し、認証認可によるAPI保護のためにAuth0と連携しています。 NuxtJS NuxtJS は、Vue.jsアプリケーションを作成するための多用な機能を備えたフルスタックフレームワークです。 NuxtJSといえばServer Side Rendering (SSR) やStatic Site Generation (SSG) の機能で有名ですが、ルーティングの自動生成やVuexのサポートなど、開発の助けとなる機能を豊富に備えています。 Qmonus Value StreamのフロントエンドはSingle Page Applicationとして構成されておりSSRを使用していないため、NuxtJSを使う必然性はないのですが、コア機能の実装に集中し開発速度を向上するためにあえて使用しています。 FastAPI FastAPI は、Pythonのウェブアプリケーションフレームワークとして一般的なFlaskと同様のマイクロフレームワークです。 ブラウザに展開されたアプリケーションやKubernetesオペレータなどからのアクセスを受け付けるAPIサーバを構成するためのフレームワークとして利用しています。 FastAPIは、型システムを利用した堅牢性やStarletteを利用した高速な動作などが特徴です。 型システムとしてpydanticを採用しており、型定義をするだけでリクエストデータのバリデーション・シリアライズを実行してくれます。 API開発において、受け入れるデータのバリデーションやシリアライズは多くの労力と実装が必要なため、データ型を定義するだけで良いこの仕組みは開発において非常に嬉しい機能です。 また、APIコールバック関数の定義からOpen API Specification (OAS) を自動生成する機能もあります。OASを利用してAPIクライアントを自動生成し、フロントエンドアプリケーションで利用することで、開発を効率化しています。 Auth0 Auth0 はWebアプリケーションやモバイルアプリケーション、APIなどに対して認証認可基盤をクラウドで提供するIDaaSサービスです。パブリッククラウドのIdPとの連携、OpenID ConnectやSAMLへの対応、さらに多要素認証にも対応しています。 Qmonus Value Streamでは、Auth0を利用することでユーザの認証と認可を実現しています。Auth0ではOAuth2.0やOpenID ConnectをインテグレーションするためのSDKを提供しており、手軽にAuth0とアプリケーションを連携させることができます。加えて、ソーシャルアカウント連携もAuth0の画面上で操作するだけ実現できます。これらの機能を用いることで、認可フローの実装やトークンの管理などに時間をかけずに、コア機能の開発に専念できています。 BigQuery BigQuery はGCPが提供する、大規模なデータ処理が可能なマネージドデータウェアハウスです。主に、GCLBで取得したログやCI/CDタスクの実行履歴などの情報を保存しています。 BigQueryに格納されたCI/CDタスクのイベントログをBigQuery SQLで加工することで、ユーザ自身でデプロイ頻度などのDevOps Metricsを確認できる機能を提供しています。Qmonus Value Streamで提供しているDevOps Metricsの仕組みについては別の記事で紹介予定です。 Firestore・Cloud Storage Firestore はGCPが提供する、スケーラブルなマネージドNoSQLデータベースサービスです。Qmonus Value StreamのUIから設定する各種データや、CI/CDタスクを実行する際に生成されるTektonマニフェストなどを保存しています。 また、Firestoreに保存できないサイズや形式のBlobなどデータはCloud Storage (GCS) に保存しています。 CI/CD ログ基盤 CI/CD ログ基盤は、Tektonが生成したPodのログを収集・保存し、ユーザに提供する機能を担当しています。 Grafana Loki Loki はGrafana Labsが公開している、複数のコンポーネントから構成されるログ収集基盤です。 主要なコンポーネントとして、収集したログを最初に処理しIngesterへ送信するDistributor、Distributorから送信されたログを任意のストレージへ保存するIngester、LogQLの形式でクエリを受け取り、検索処理を実行するQuerierが存在します。これらを組み合わせることによって、ログの収集と保存、検索を実現しています。また、Lokiは他のログ基盤とは異なり、ログのメタデータのみをインデックス化するため、ログを保存するコストが少ないという特徴があります。 Qmonus Value Streamでは、CI/CDタスクの実行ログの保存・閲覧機能を提供するためにLokiを使用し、ログを保存するためのバックエンドとしてGCSを用いています。 Tekton Pipelineで実行される処理は最終的にPod内のコンテナで実行されます。Lokiはこれらコンテナ内の標準出力を保存し、後から検索して参照できるようにしています。ユーザは、CI/CDタスクの実行中はWebsocketによりLokiを通してpod内のログを直接見ることができ、過去に実行したログはLokiを通してストレージに保存したログを参照することで確認できるようになっています。 Envoy Envoy はサービスメッシュの通信バスやエッジプロキシとして利用可能な、Webサービスのために設計されたL4/L7プロキシソフトウェアです。認証機能を持たないLokiに対して、EnvoyプロキシをAuth0と連携させることで、アクセス元のユーザの認証・認可を実現しています。 CI/CD 実行基盤 CI/CD 実行基盤は、CI/CD機能を担うコンポーネントを実行するための基盤を提供する役割を担当しています。 AssemblyLine AssemblyLineはQmonus Value Streamの独自コンポーネントです。Kubernetesカスタムオペレーターとして実装されており、複数のTekton Pipelineを順に実行するワークフローエンジンとしての機能を持ち、柔軟性の高いCI/CDパイプラインを構成・実行するために必要な様々な機能を有しています。 Qmonus Value StreamでのAssemblyLineの役割と実現方式の詳細な説明については 第7回 で解説しています。 Tekton Tekton はCI/CDシステムを作成するためのKubernetes Nativeなオープンソースフレームワークです。 TektonではStep、Task、Pipelineと呼ばれる概念があり、これらを組み合わせて順次実行することで柔軟なパイプラインを構成できるという特徴を持っています。 Qmonus Value StreamでのTektonの利用とTekton自体の詳細な説明については 第5回 で解説しています。 Qmonus Value Streamでは、コンテナビルド・デプロイ・リリースといったCI・CDの標準機能を基本タスクとして提供しており、Buildkit、CUE、Pulumiなどの技術を用いて実現しています。詳しくは 第2回 (後編) を参照してみてください。 Metrics Metricsは、Qmonus Value Stream上で実行されたCI/CDタスクの時系列データを収集し、後から利用できるようBigQueryへと保存しておく処理を担当しています。 Cronjob CronjobはKubernetes上のリソースで、一般的に知られるCronと同様、スケジュールされた時間に任意の処理を実行させることができます。 CronjobはJobと呼ばれるリソースを介してPodを作成することで、そのPodが管理するコンテナ上で任意のプロセスを実行します。 Qmonus Value StreamではCI/CDタスクの実行履歴がFirestoreに保存されているため、データ解析のためにCronjobを用いてBigQueryへ転送しています。任意のスケジュールタスクの実行にはBigQueryスケジューラなどの利用が考えられますが、実行工数の低さや、実行履歴の管理などの観点からCronjobを採用しています。 Monitoring Monitoringは、各コンポーネントの稼働状況とパフォーマンスを計測、可視化し、一定のしきい値を超えた場合はユーザや私たち開発者に通知する役割を担当しています。 Datadog Datadog はSaaSベースの運用監視と分析サービスです。 GCPやAmazon Web Service (AWS)、Microsoft Azureなどのクラウドサービスをサポートしており、プラットフォームを横断して監視対象のアプリケーションを管理できます。 データ収集と可視化に加え、インフラ監視やApplication Performance Monitoring (APM)、Logの管理といったことも行うことができます。 Qmonus Value Streamでは監視対象のGKEにDatadog Agentを導入することで、ディスクI/OやCPU利用率、ネットワークトラフィックなどのシステムレベルのデータを収集し、Datadog上で可視化しています。 また、CPU利用率とメモリ利用率は実行するCI/CDタスクのパフォーマンスにも影響することから、設定したしきい値を超えた場合にSlackでアラートを通知することで、インフラの状況を把握しやすい環境を構築しています。 GCP Service Log GCP Service Logは、GCPの各サービスで取得可能なログを集約し、後から参照できるよう用途に応じたストレージへ保存する役割を担当しています。 Cloud Logging Cloud Logging はGCPが提供するマネージドサービスで、GCP上のリソースで生成されるログのストレージ設定や検索、分析、アラートなどの機能が利用できます。 Cloud Loggingで収集されたログは、ログバケットやGCS、BigQueryに保存できます。 Qmonus Value Streamではログを確認する一般的な用途に加え、参照する頻度は低いが長期間保存したいログはGCSのバケットへ、分析対象となるログはBigQueryのデータセットへと分けて保存するといったストレージ設定も利用しています。 おわりに DevOpsプラットフォームの取り組み連載の8回目の記事として、Qmonus Value Streamを支えるアーキテクチャと主要な技術スタックを紹介しました。紹介したアーキテクチャは現在のスナップショットであり、今後も更新を続けていく予定です。 全ての要素についてはご紹介できませんでしたが、サービスを構成するアーキテクチャの一例として参考になれば嬉しいです。 Qmonus Value Streamチームは、メンバ一人一人が、利用者であるアプリケーション開発者のために信念を持って活動を続けています。 プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで 、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 CI/CDプラットフォーム開発、ソフトウェアエンジニア NTTグループのクラウド基盤構築およびCI/CD導入、ソリューションアーキテクト 次はQmonus Value Streamにおける開発スタイルと内部アーキテクチャについて紹介します。 お楽しみに!
アバター
はじめに こんにちは、イノベーションセンターの鈴ヶ嶺です。 本記事では、自社データセンター等でAWSサービスを利用可能なAWS Outpostsにおける Elastic Kubernetes Service(EKS) で新たに追加された Local cluster の概要や通信切断検証の結果について紹介します。 以前記事にした2022年3月17日時点では、ワーカーノードはAWS Outposts上で実行し、KubernetesコントロールプレーンはAWS Cloud上で実行する構成でした。この構成がLocal clusterによってKubernetesコントロールプレーンもAWS Outposts上で実行可能になりました。これによりAWS CloudとAWS Outposts間の通信切断などによるアプリケーションのダウンタイムのリスクを軽減することが可能となりました。 engineers.ntt.com 今までのEKS on Outpostsの問題点 上図に、今までのAWS Outposts上でEKSを利用する構成を示しています。現在ではExtended clusterと命名されています。 この構成では、KubernetesコントロールプレーンがAWS Cloud上に存在しています。 このことからAWS CloudとAWS Outpostsのファイバー切断などの通信切断やAWS Cloud上で障害が起きた場合にKubernetesの操作が不可能となる問題がありました。 データセンターに設置するAWS Outpostsの性質を考慮するとEKS on Outpostsには、自律的に動作をして外部の障害に対して頑健であることが求められます。 EKS Local cluster on Outposts 上図にAWS Outposts上でKubernetesコントロールプレーンを実行可能となったLocal clusterの構成を示しています。 この構成ではAWS CloudとAWS Outposts間で通信を必要としていません。 これにより先に述べた外部の障害に対してKubernetesの操作のダウンタイムを抑えた頑健なシステムが構築可能となります。 Extended clusterとLocal clusterの違い ここでは従来のExtended clusterとLocal clusterの主な違いを次の表にまとめました。詳細はこちらの デプロイオプションの比較 を参照してください。 Extended cluster Local cluster Kubernetes version 1.23, 1.22, 1.21, 1.20から選択可能(2022/10/31時点) 1.21のみ Amazon EKS 最適化 AMI Amazon Linux, Windows, Bottlerocket Amazon Linux Kubernetes API サーバー認証 IAM, OIDC IAM, X.509証明書 シークレットエンベロープ暗号化 対応 非対応 サービスアカウントの IAM ロール 対応 非対応 Local clusterはKubernetesのversionやAMIが固定化されています。また、通信障害時にも動作可能とするためのX.509証明書によるAPIサーバの認証があります。こちらの認証方法については後述する通信切断の検証で説明します。 構築方法 AWS Consoleでの構築は次のように設定します。Kubernetesコントロールプレーンの場所をAWS Outpostsに選択することでLocal clusterを構築できます。 現時点では、レプリカ数は3に固定されています。 Terraformでは、次のように outpost_config を設定することでLocal clusterを構築できます。 resource "aws_eks_cluster" "outposts_local_cluster" { name = "outposts-local-cluster" role_arn = aws_iam_role.local_cluster_role.arn enabled_cluster_log_types = [ "api" , "audit" , "authenticator" , "controllerManager" , "scheduler" ] vpc_config { security_group_ids = [ aws_security_group.cluster.id ] # Amazon EKS セキュリティグループの要件および考慮事項を参照 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/sec-group-reqs.html subnet_ids = [ aws_subnet.outposts_subnet01.id ] # AWS Outposts上のsubnetを指定 endpoint_public_access = false endpoint_private_access = true } outpost_config { control_plane_instance_type = "m5.large" # Clusterのインスタンスタイプ outpost_arns = [ local.outpost_arn ] # AWS OutpostsのARN } depends_on = [ aws_iam_role_policy_attachment.eks_local_cluster_policy, ] } resource "aws_iam_role" "local_cluster_role" { name = "outposts-local-cluster" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } resource "aws_iam_role_policy_attachment" "eks_local_cluster_policy" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKSLocalOutpostClusterPolicy" role = aws_iam_role.local_cluster_role.name } 実際に構築したLocal clusterのEKSは、次のようになります。クラスタのダッシュボードには、KubernetesコントロールプレーンにAWS OutpostsのARNが表示されています。 次のEC2一覧には、Kubernetesコントロールプレーン用のEC2([EKSの名前]-controle-plane-XXXXXXX)が立てられていることが分かります。このEC2はAWS Outposts上のリソースになります。 また、kubectlでnode情報を見ると以下のようにcontrol-planeとワーカーノードが確認できます。 おそらくcontrol-planeがNot Readyなのは以下のようにTaintsで汚染してワークロードがスケジュールされることを防ぐためだと思われます。 各インスタンスは node-role.eks-local.amazonaws.com/control-plane で汚染されているため、コントロールプレーンインスタンスでワークロードがスケジュールされることはありません。 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/eks-outposts-local-cluster-create.html ❯ kubectl get node NAME STATUS ROLES AGE VERSION ip-10-2-1-174.ap-northeast-1.compute.internal NotReady control-plane,master 46h v1.21.13 ip-10-2-1-185.ap-northeast-1.compute.internal NotReady control-plane,master 46h v1.21.13 ip-10-2-1-61.ap-northeast-1.compute.internal NotReady control-plane,master 46h v1.21.13 ip-10-2-2-15.ap-northeast-1.compute.internal Ready <none> 46h v1.21.14-eks-ba74326 ip-10-2-2-198.ap-northeast-1.compute.internal Ready <none> 46h v1.21.14-eks-ba74326 ip-10-2-2-23.ap-northeast-1.compute.internal Ready <none> 46h v1.21.14-eks-ba74326 通信切断検証 ここでは通信切断時にもダウンタイムなくKubernetesを動作可能かを検証します。まずAWS Outpostsに接続されたルータのBGPを切断して以下のようにサービスリンクの接続ステータスが0となっていることを確認します。 ここから次の3つのケースに分けて検証します。 Extended clusterの場合 Local cluster(AWS IAM認証)の場合 Local cluster(X.509証明書)の場合 1. Extended clusterの場合 従来のKubernetesコントロールプレーンがAWS Cloud上にある構成の場合は次のようにサーバに接続できないというエラーが表示されます。 ❯ aws eks --region [Region] update-kubeconfig --name [EKSクラスタの名前] ❯ kubectl get svc Unable to connect to the server: net/http: TLS handshake timeout 通信切断時にExtended clusterの場合、Kubernetesの操作が不可能となります。 2. Local cluster(AWS IAM認証)の場合 KubernetesコントロールプレーンがAWS Outposts上に存在する場合でも、通信切断時にIAM認証で操作した場合は次のようにUnauthorizedというエラーが表示されます。 この事象はネットワーク切断中にAWS Cloud上のIAMを使用できないことが原因でおきます。 ❯ aws eks --region [Region] update-kubeconfig --name [EKSクラスタの名前] ❯ kubectl get svc error: You must be logged in to the server (Unauthorized) 1. Extended clusterの場合と同様に、通信切断時にLocal cluster(AWS IAM認証)の場合、Kubernetesの操作が不可能となります。 このためKubernetesコントロールプレーンをAWS Outposts上で実行するだけでは、AWS CloudとAWS Outposts間の通信切断によるKubernetesの操作のダウンタイムのリスクを軽減できません。 対策として次に記述したX.509証明書を事前に設定する必要があります。 3. Local cluster(X.509証明書)の場合 AWS CloudとAWS Outposts間の通信障害時に、アクセスするためには次のようにX.509証明書を通信障害以前に事前準備する必要があります。 詳細はこちらの ネットワーク切断中のローカルクラスターへの認証 を参照してください。 以下にコマンドを記述します。 openssl req -new -newkey rsa:4096 -nodes -days 365 -keyout admin.key -out admin.csr -subj "/CN=admin" BASE64_CSR=$(cat admin.csr | base64 -w 0) # macの場合 # BASE64_CSR=$(cat admin.csr | base64) cat << EOF > admin-csr.yaml apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest metadata: name: admin-csr spec: signerName: kubernetes.io/kube-apiserver-client request: ${BASE64_CSR} usages: - client auth EOF kubectl create -f admin-csr.yaml kubectl certificate approve admin-csr kubectl get csr admin-csr -o jsonpath='{.status.certificate}' | base64 --decode > admin.crt kubectl create clusterrolebinding admin --clusterrole=cluster-admin --user=admin --group=system:masters aws eks describe-cluster --name [EKSクラスタの名前] --query "cluster.certificateAuthority" --output text | base64 --decode > ca.crt kubectl config --kubeconfig admin.kubeconfig set-cluster [EKSクラスタの名前] --certificate-authority=ca.crt --server [EKSクラスタのAPIエンドポイント] --embed-certs kubectl config --kubeconfig admin.kubeconfig set-credentials admin --client-certificate=admin.crt --client-key=admin.key --embed-certs kubectl config --kubeconfig admin.kubeconfig set-context admin@[EKSクラスタの名前] --cluster [EKSクラスタの名前] --user admin kubectl config --kubeconfig admin.kubeconfig use-context admin@[EKSクラスタの名前] kubectl get svc --kubeconfig admin.kubeconfig # 確認 上記により --kubeconfig admin.kubeconfig でX.509証明書によるアクセスが可能となりました。 これにより次のように通信障害時にアクセスした場合も問題なくアクセス可能となります。 ❯ kubectl get svc --kubeconfig admin.kubeconfig NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 5h13m まとめ 本記事では、新たに追加されたAWS OutpostsのEKS Local clusterの概要や従来のExtended clusterとの違い、構築方法について紹介しました。 また、AWS CloudとAWS Outposts間の通信切断検証からダウンタイムなくKubernetesをローカル環境で実行可能であることを確認しました。 外部の障害に対して頑健なシステムを構築する場合には非常に効果的な機能であると思います。
アバター
はじめに こんにちは、クラウド&ネットワークサービス部で SDPF のベアメタルサーバー・ハイパーバイザーの開発をしている山中です。 先日 NTT Engineers' Festa という技術イベントが開催され、多くのエンジニアで賑わいました。 NTT Engineers' Festa は NTT グループのエンジニアが技術交流するイベントであり、ハンズオンやディスカッション、登壇発表など様々なセッションが数日に渡って行われます。 私もこのイベントに参加し、自分のチームで行っている GitHub Actions の self-hosted runners を活用した Continuous Integration(以下、CI)事例について発表をしました。 概要としては、オンプレミスの VMware vSphere(以下、vSphere)環境上で自作の Ruby アプリケーションと Docker によって self-hosted runners のオートスケーリングを行い、数十以上のリポジトリを対象とした月に数万分ほど時間がかかる大規模な CI を実行しているという話になります。 この記事では、発表内容をブログ向けにアレンジし、当日話せなかった内容も加えてご紹介したいと思います。 発表で用いたスライドは こちら になります。 GitHub Actions について GitHub Actions は GitHub が提供する自動化プラットフォームです。 プルリクエストやイベントに対する様々なイベントをトリガーとして、事前に定義した workflow を GitHub 上から実行できます。 workflow は GitHub 上で構成管理されるため、GitHub から提供される様々なエコシステムと自然に連携でき、効率的に開発を行える点が非常に大きな魅力となっています。 self-hosted runners について self-hosted runners は workflow 内の処理(job)を自分が用意した環境で実行できる仕組みです。 通常よく使われる GitHub-hosted runners とは異なり、runner が動作するマシンやコンテナなどの環境を自分で用意する手間はありますが、その手間の代わりに runner の利用時間に伴う従量課金がありません 。 自分で環境を用意するということは、用途に応じたハードウェア構成・指定したパッケージ等が入った環境を使って job を実行できるということであり、 自分が用意した環境上に OSS の actions/runner を起動して GitHub に登録するだけで準備は整うため、 細かな要件が求められる CI でも利用可能です。 なぜ self-hosted runners のオートスケーリングが必要なのか? 無料利用可能で便利な self-hosted runners ですが、実際のサービス開発における CI で使用するにあたって、解決しておきたい大きな課題が2つ存在します。 1つ目の課題は、 job の実行数が self-hosted runners の登録数に縛られる という点です。 1つの self-hosted runners が同じ時間に処理できる job は1つであるため、 例えば複数人が同時に workflow を実行し、10個の job が GitHub 側の queue に積まれた場合、self-hosted runners を1つしか登録していなければ、10個のジョブはシーケンシャルにしか実行できません。 もう1つの課題は、 self-hosted runners の動作する環境は複数の job 間で使い回されてしまう という点です。 self-hosted runners は基本的には一度登録したら登録されっぱなしのため、ある job の実行により self-hosted runners の動作する環境が汚されてしまった場合(ファイルを配置したりシステムの設定を変更するなど)、その self-hosted runners を利用して次に実行される job は汚れた環境で実行されることになってしまいます。 workflow の設計を工夫すればこの問題を回避できますが、CI の結果が不安定(flaky)になる潜在的な要因となってしまうため好ましくありません。 これらの課題を解決するのが self-hosted runners のオートスケーリング です。 job を実行するごとに self-hosted runners が起動するマシンやコンテナを必要な分だけ動的に用意することで、job の同時実行数の制限や環境が使い回されることによる問題を解消できます。 self-hosted runners のオートスケーリング構成例 私のチームで開発している SDPF ベアメタルサーバー 1 / SDPF ハイパーバイザー 2 の CI における、self-hosted runners のオートスケーリング構成についてご紹介します。 全体構成 実際に運用している構成は以下の図になります。 私たちのチームは物理サーバーを提供するサービスを開発するチームでもあり、開発で使用する検証環境も物理サーバーを使って構築しています。 検証環境内の物理サーバーには VMware ESXi をインストールしており、VMware vCenter を使って vSphere 環境を構築しています。 この vSphere 環境上に本番環境を模した検証環境を複数作成しており、検証環境内の VM を使って各種テストを実行しています。 この vSphere 環境上で GitHub Actions と連携して CI の中心的な役割を担うのが、自作の runner controller と Docker が動作する VM です。 この VM は GitHub からの webhook と連携し、以下のように動作します。 workflow を実行し job が GitHub Actions の queue に積まれる。 job が queue に積まれた旨の webhook が runner controller へ送信される。 runner controller は webhook の内容に応じて runner が埋め込まれたコンテナを起動する。 コンテナ起動とともにコンテナ内で runner が起動し、webhook の送信元のリポジトリに runner が登録される。 登録された runner を job が確保する。 確保された runner が動作する環境上で job の処理が実行される。 job の終了後、GitHub から runner が自動で登録解除され、コンテナも自動で削除される。 job を複数実行すると 1台の CI 用 VM 上で runner が起動したコンテナが複数デプロイされることになりますが、 実際に負荷がかかるテスト処理などは検証環境内の別の VM で実行しているため、このようなシンプルな構成でも安定した動作しています。 runner のアプリケーションもコンテナイメージに封入しているため、複数コンテナを起動してもディスク容量は大きく消費しないようにもなっています。 コンテナの動作が軽量なこともあり、job が queue へ積まれてから10秒ほどで runner が GitHub へ登録されるようになっています。 コンテナを使用することで「 job の実行環境の分離性 」も簡単に手に入れることもできるため、self-hosted runners のオートスケーリングとコンテナはとても相性が良いと言えます。 もともと私たちのチームでは、GitHub Actions を導入する前は Jenkins を使って CI を回しており、Jenkins を使っていた頃からなるべくデグレしないようにと考えた結果、このような構成となりました。 self-hosted runners のオートスケーリングを行うための OSS も公式で紹介されていますが、 既に存在するオンプレミス資産の活用やチームメンバーのスキルセットなどを鑑みた結果、OSS には頼らず自分たちで仕組みを作ったという経緯になります。 runner controller runner controller は Ruby の Sinatra(Web アプリケーションフレームワーク)を用いて書いた100行にも満たない小さなアプリケーションです。 runner controller のソースコード(一部抜粋) class RunnerController < Sinatra :: Base RUNNER_LABEL = ' custom-runner ' post ' / ' do data = JSON .parse(request.body.read) # in-progress や completed の場合は何もしない halt if data.dig( ' workflow_job ' , ' status ' ) != ' queued ' job_id = data.dig( ' workflow_job ' , ' id ' ) run_id = data.dig( ' workflow_job ' , ' run_id ' ) repo = data.dig( ' repository ' , ' name ' ) labels = data.dig( ' workflow_job ' , ' labels ' ) logger.info( " job of #{ repo } is queued. job_id: #{ job_id } , run_id: #{ run_id }" ) # runs-on に所定のラベル名が指定されていない場合は何もしない unless labels.include?( RUNNER_LABEL ) logger.warn( " skipped because labels do not have #{ RUNNER_LABEL } . job_id: #{ job_id } , labels: #{ labels }" ) halt end # コンテナ起動 command = [ ' sudo docker run ' , ' --detach ' , ' --volume ~/.ssh:/home/user/.ssh ' , " --env GITHUB_USERNAME= #{ ENV .fetch( ' GITHUB_USERNAME ' ) }" , " --env GITHUB_TOKEN= #{ ENV .fetch( ' GITHUB_TOKEN ' ) }" , " --env REPOSITORY= #{ repo }" , " --env RUNNER_LABEL= #{ RUNNER_LABEL }" , ' --rm ' , ' action-runner:latest ' ].join( ' ' ) stdout, stderr, status = Open3 .capture3(command) raise stderr unless status.success? container_id = stdout.chop logger.info( " container #{ container_id } for #{ repo } runs. job_id: #{ job_id } , run_id: #{ run_id }" ) end end runner controller は、受け取った webhook の内容を読み取り「workflow_job の status が queued」かつ「workflow_job の labels に指定した値が含まれている」場合のみコンテナを起動します。 コンテナの起動方法には以下のようないくつかの工夫を行っています。 ホスト側の ~/.ssh ディレクトリをマウントすることで、コンテナイメージに鍵や接続設定を埋め込まずにコンテナ内から検証環境内の他の VM への ssh でのコマンド実行や Ansible 適用を可能としている。 self-hosted runners の起動・登録時に必要な情報をコントローラから渡すことで、イメージの再利用性を高めたり機密情報を持たせないようにしている。 self-hosted runners の登録に必要なクレデンシャル、登録先のリポジトリ、登録時の割り当てるラベルなどを渡している。 --rm オプションを付けることで self-hosted runners の処理終了時にコンテナも自動で削除されるようにしている。 runner controller に GitHub から webhook を送信するためには、GitHub 側で webhook の登録もしなければなりません。 私たちのチームでは扱っているリポジトリが数十以上あり、全てのリポジトリに手動で webhook を登録するのは大変なため、 以下のように gh コマンドを使ってターミナルから簡単に webhook を設定できるようにしていたりもします。 gh api /repos/<owner>/<repo>/hooks --input - << EOL { "name": "web", "active": true, "events": ["workflow_job"], "config": { "url": "<runner_controller_endpoint>", "content_type": "json", "insecure_ssl": "0", "secret": "<webhook_secret>" } } EOL この仕組みでは runner はリポジトリごとに登録していますが、リポジトリごとではなく organization にも登録 できます。 organization の admin 権限を持っている場合は、organization に runner を登録して各リポジトリの workflow から使用させたほうが runner の管理の複雑度は減ります。 self-hosted runners コンテナイメージ Dockerfile は以下になります。 Dockerfile(一部抜粋) # syntax = docker/dockerfile:1.3-labs FROM ubuntu:18.04 # job の実行時に必要になる ruby や Ansible などインストール ... # 作業用の一般ユーザを作成 ARG USER=user RUN <<EOL useradd -m ${USER} --group sudo echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers EOL USER ${USER} # docker build 時に指定したバージョンの action-runner を配置 ARG RUNNER_VERSION=dummy RUN <<EOL mkdir ~/action-runner && cd ~/action-runner wget https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz tar xzf actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz rm actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz sudo ./bin/installdependencies.sh EOL # self-hosted runners の起動・登録スクリプトを配置、実行権限を付与 COPY ./wake_runner.sh /home/${USER}/wake_runner.sh RUN <<EOL sudo chown ${USER}:${USER} ~/wake_runner.sh sudo chmod 755 ~/wake_runner.sh EOL ENTRYPOINT ~/wake_runner.sh 自分で Dockerfile を書くことの一番のメリットは、Ruby や Ansible といった自分たちの CI で必要になるソフトウェアを予めインストールしておくことができるという点です。 GitHub-hosted runners 上で動かすことを想定した job では、 ruby/setup-ruby の action を呼び出し job の中で Ruby を使用可能とするのが定番ですが、この場合 Ruby を使いたい job 全てに ruby/setup-ruby の action 呼び出しを定義しなければいけません。 一方、自前で用意した環境に予め Ruby がインストールされている状態にしておけば、ruby/setup-ruby の action をわざわざ呼び出さずとも workflow 内のどこでもいつでも Ruby が使用可能となるわけです。 このメリットはかなり大きく、冗長な記述を減らすことができますし、workflow の記述を 本質的に行いたい処理(実際にテストを流す部分など)に注力 できるようにもなります。 イメージの build 時には、封入する runner のバージョンを指定できるようにしています。 以下のコマンドを workflow のスケジュール実行機能 を使って定期実行することで、GitHub-hosted runners と同様に、新しいバージョンの runner がリリースされると自動的にそのバージョンの runner を使って CI を回すことができるようにも作り込んでいます。 latest_runner_version=$( git ls-remote --tags https://github.com/actions/runner.git | jq -rR 'capture("/v(?<version>.+)$") | .version' | tail -1 ) docker buildx build -f Dockerfile \ -t action-runner:latest \ -t action-runner:${latest_runner_version} \ --build-arg RUNNER_VERSION=${latest_runner_version} \ ./context ENTORYPOINT で指定しているスクリプトは以下です。 #!/bin/bash -ex cd ~/action-runner REGISTRATION_TOKEN=$(curl -X POST -u ${GITHUB_USERNAME}:${GITHUB_TOKEN} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/<owner>/${REPOSITORY}/actions/runners/registration-token | jq -r .token) ./config.sh --url https://github.com/<owner>/${REPOSITORY} --token ${REGISTRATION_TOKEN} --unattended --disableupdate --ephemeral --labels ${RUNNER_LABEL} ./run.sh デフォルトの挙動では、runner は job の実行前に自身を最新バージョンへ自動でアップデートするようになっていますが、 私たちの仕組みでは runner のアップデートは先ほど説明した定期実行 workflow によるイメージの作り直しによって実現しているため --disableupdate オプションを指定して自動アップデートを抑制しています。 この仕組みにおいて重要な役割を担っているのが --ephemeral オプションです。 このオプションを指定して self-hosted runners を起動することで、job を1つ実行した runner は job の終了後に自動で GitHub から登録解除され、プロセスも終了するようになります。 これにより複数の job で1つの self-hosted runners が使い回されなくなり、コンテナ起動時に指定した --rm オプションと組み合わせて、runner の終了時にコンテナの削除まで行うことで、不要になったファイルやプロセスが何も残らないようにしています。 導入してみた感想 Jenkins の運用・追加開発が辛い状況を改善したいということで導入した GitHub Actions ですが、狙い通り開発の効率が格段に向上しました。 workflow ファイルも書きやすく、GitHub とも密結合しているため CI の開発のやりやすさが格段に向上しました。 こういった使いやすい自動化ツールを導入することで、様々な日々の作業を自動化しようというモチベーションが湧きやすくなると感じました。 self-hosted runners のオートスケーリングも非常に便利であり、並列数や課金のことを一切気にせず大量の job を実行できるという体験は非常に気持ち良いです。 これにより開発自体もスケールしやすくなっていると感じています。 と言いつつも、最初はオートスケーリングなしで始めるのも全然ありです。 前回実行した job のゴミを削除する処理を workflow の最初に仕込んだり workflow の書き方を工夫することで、job 間で self-hosted runners が使い回されることの影響は抑えることができます。 私たちも最初は1リポジトリに1 self-hosted runners を専属で起動させっぱなしの状態から始め、ある程度 CI がこなれてきてからさらなる改善としてオートスケーリングに取り組みました。 GitHub Actions 自体の機能の進化も早く、今回紹介した self-hosted runners のオートスケーリングのための機能のほとんどは、2021年リリースとなっています。 ロードマップ を眺めると将来的に実装される予定の機能も分かるため、今後どういった機能が実装されるかもある程度見越して CI を作り込むのが吉だと思います。 メジャーな機能は GitHub の Changelog をウォッチしておくと素早くキャッチアップでき、マイナーですが便利な機能の追加が runner のリリースノート からたまに見つかることもあるため、こちらも時々チェックするのがオススメです。 さいごに いかがでしたでしょうか? GitHub Actions は CI ツールの中では後発ですが、ユーザー数や活用事例もどんどん増加し、既に確固たる地位を築いているように思われます。 みなさんも GitHub Actions の self-hosted runners をオートスケーリングさせて快適な CI ライフをぜひ送ってみてください。 次回の記事では、この仕組み上で動かしている workflow の例についてご紹介したいと思います。 様々な機能を盛り込みつつ workflow を作り込んでいるため、実際に開発をするにあたっての参考例として、みなさんのお役に立てるのではないかと考えています。 最後になりますが、SDPF クラウドは国内最大級のクラウドサービスです。 開発メンバーは、数千台以上の物理サーバーの操作の自動化をはじめとした、技術的難易度の高い課題に取り組みつつ、日々より良いサービスにしようと邁進しております。 今回紹介した CI 環境の設計・構築など、大規模サービスだからこそのやりがいのある課題もたくさん転がっています。 もし私たちのチームに興味を持たれた方は こちら からの応募をお願いいたします。 専有型の物理サーバーをオンデマンドに利用可能とするサービス。 https://sdpf.ntt.com/services/baremetal-server/ ↩ ベアメタルサーバー上に vSphere ESXi や Hyper-V など代表的なハイパーバイザーを予めセットアップした状態で利用可能とするサービス。 https://sdpf.ntt.com/services/vsphere/ https://sdpf.ntt.com/services/hyper-v/ ↩
アバター
イントロダクション こんにちは、NTTコミュニケーションズ デジタル改革推進部 データドリブンマネジメント部門の有賀唯貴です! 現在、社内の同じ部署の浅野秀平さんと「でじかいロボット部」を立ち上げて同好会として非公式に活動しています! 今回は活動第一弾として、ROS(Robot Operating System)を使ったラジコン製作に取り組んだのでその内容を報告します。 ※ でじかいロボット部は非公式な活動です。ただいま絶賛部員募集中です! ROSとは ROS とは、ロボットシステム構築を効率化するための共通機能を提供するオープンソースの基盤ソフトウエアです。 ロボットを開発するために便利で必要な機能や汎用な実装として モジュール・コンポーネント化のフレームワーク 再利用を重視する開発フレームワークで車輪を再開発しない モジュール間通信による分散処理 センシング、モータ制御、画像処理、経路計算、音声認識、パラメータ管理等々、同時に影響しあいながら駆動し続ける必要があるプロセス間を矛盾なく同時に動作させる 物理的に分散したPC間でも分散処理・通信が簡単にできるので、IoTやクラウド技術との親和性も高い 複数言語対応 C++, Python, java, Erlangなど 可視化シュミレータ 機体、周囲環境、センサ情報をGUIで可視化、GUI操作で指令 各種パラメータ管理GUI、CLI デバッグ、データの再生、プロット、ログ記録、起動 が標準で揃っており、オープンソースコミュニティでは 音声認識 画像処理(顔認識、感情認識、行動認識、物体認識など) 強化学習 などのAI,人工知能分野のロボティクス用モジュールや 自動車の自動運転システム、 Autoware 移動ロボットの自律移動システム Navigation package ドローンの自動航行システム 制御・経路の自動計算ライブラリ群 二足歩行用自律移動システム 四足歩行用自律移動システム などのカスタマイズ、インテグレーションされた汎用システムが公開されており、最新ロボット開発のデファクトスタンダードとなっています。 世は大ロボット時代!ROSやってないのお前だけです。 ※現在(2022/10/20)はROS1からROS2への移行時期となっており、ここに載せている情報はROS1についての情報です。ROS2関連の最新動向はこれから追っていく予定です。 ROSによるラジコン制御 今回の全体図です。 PCからラジコン上のラズパイへ機体の速度と回転角を送信します。ラズパイはホイールの角速度を計算し、PMW信号を出してサーボモータを制御します。全体の接続をROSの機能を中心に実装します。 最初ラズパイとサーボモータの間にArduinoを挟むつもりでしたが、ラズパイが直接PWM信号を出せるのに途中で気づいて取り除きました。 車体の製作 車体はどこの家庭にもある レーザー加工機 を使い、3mm厚のアクリル板から切り出します。 図面と作成物は以下です タイヤを固定する為の複雑な形状の部品は、秘密裏に持ち込んだ 小型3Dプリンタ を使って作ります。 強度が心配ですが、壊れてもすぐに作り直せるのが3Dプリンタのいいところです。 そのほか、使ったパーツは大体以下です。 タイヤは メカナムホイール サーボモータ バッテリー Raspberry pi に元々ついてるPWM信号ピンをモーターに接続することで、モーターとラズパイを繋いでいます。 参照 赤枠のピンをモータに繋ぎます。 メカナムホイールを使用した4輪駆動を採用したことで全方向への移動(前進後進、左右スライド、その場で回転、これら三つの成分の合成された「飛ぶ」以外のあらゆる方向・姿勢の移動)ができます。 制御ソフトウェアの開発 遠隔PCからの指令はx方向速度、y方向速度、z軸周りの回転角です。受け取った指令と下記のメカナムホイール4輪駆動モデルの逆運動方程式に従って、ホイールの角速度を計算します。( 参照 ) 外部のPCと通信し、指令を受け取って上記の方程式からホイールの角速度を計算し、規格に合うようにモーターへPWM信号を送ることで、所望の移動をロボットに行わせています。このようなプログラムをROSのフレームワークで作成しました。 以下は構築したソフトウェア概要です。 完成品がこちら フィードバック制御なしでこれはいい感じと思っていましたが、、、 何ということでしょうか!!! 机の上から落ちてバラバラになってしまいました! おわりに でじかいロボット部はAI・IoTに続く事業領域としてロボティクスをぶち上げるべく、これからも技術と既成事実を積み重ねていきます。 また、日々の業務の中でロボットのプロトタイプが必要になった際は、お気軽にロボット部までご相談ください。 ※ でじかいロボット部は非公式な活動であり、デジタル改革推進部とはなんの関係もありません。
アバター
はじめまして、5G&IoTプラットフォーム部で、IoT Connect Gatewayの機能開発を担当している角田です。本日は、9月初旬に追加されたIoT Connect Gatewayの機能についてご紹介します。 IoTデバイス毎に異なる設定更新を遠隔で実現、運用コストを75%削減 追加された機能は、AWS S3互換のオブジェクトストレージへのデータ転送機能と、IoTデバイスのコンフィグ生成・更新を支援するコンフィグマネージャーです。本記事では、これらの2つの新機能のうち、コンフィグマネージャーの概要をご紹介します。 コンフィグマネージャーの背景 日頃IoTデバイスを扱っていて、IoTデバイスの設定ファイルの生成と更新が面倒だと感じたことがありませんか? 例えば、学習や業務のパフォーマンスを維持・向上するために、居室内の温湿度・気圧・二酸化炭素の濃度を把握し、定期的に空気環境を調整したいなどのケースです。 居室内でも様々な場所にIoTデバイスを設置する必要があったり、階数や拠点が分かれていれば、(どこまでやるか次第ですが)その分だけIoTデバイスを設置する必要があるかと思います。そして、IoTデバイスの数が数百、数千個と増えていけばいくほど、IoTデバイスごとの設定ファイルの生成も大変になります。設定ファイルの生成だけではなく、更新も同じように大変になりそうです。 IoTデバイスが数個であれば、IoTデバイスの設定ファイルの生成と更新の大変さを感じることは少ないかもしれませんが、数百、数千個のIoTデバイスの設定ファイルを1つずつ生成・更新する作業は相当大変ですよね。 これは居室環境の把握に限らず、農業や製造業などの規模が大きいシチュエーションであれば、いずれ直面する課題になると考えています。 コンフィグマネージャーは、そういったシチュエーションで直面するかもしれない、IoTデバイスの設定ファイルの生成と更新の大変さを削減できる便利機能になっています! コンフィグマネージャーとは IoTデバイスが利用する設定ファイルの生成と遠隔での更新を一気通貫でできる機能です。 IoTデバイスが利用する設定ファイルのテンプレートやそれに埋め込むパラメータを事前に用意しておくことで、IoTデバイス毎の設定を一気に生成できます。 IoTデバイス毎に個々の設定を用意する必要がないため、大量のIoTデバイスを扱う場合でも、比較的少ない記述量で、IoTデバイス毎の設定ファイルを生成できるというメリットがあります。 設定ファイルの生成後に、コンフィグマネージャーが提供する配信用エントリポイントに接続することでIoTデバイスは設定ファイルを取得できます。同じエントリポイントであっても、IoTデバイスに応じて異なる設定ファイルが提供される仕組みになっています。 遠隔からIoTデバイスに対して設定ファイルを配信できるので、現場に担当者が赴いて、IoTデバイス毎に設定ファイルを地道に配信するような手間を削減できるようになります。 *1 生成された設定の更新時刻を取得するためのエントリポイントも用意されているので、IoTデバイス側の実装次第では、設定ファイルが更新された場合のみ、新たな設定ファイルを取得するという仕組みも実現できます。 詳細情報については、下記の機能概要をご参照ください。 IoT Connect Gateway 概要 コンフィグマネージャー 機能 コンフィグマネージャーの始め方 コンフィグマネージャーは、以下のステップで使い始めることができます。 IoT Connect Mobile Type S のeSIMの調達・開通 開通時にIoT Connect Gatewayを有効化 コンフィグ生成の設定 テンプレート・共通パラメータ・デバイスグループの作成 プロファイルの作成とコンフィグ生成 コンフィグ配信の設定 配信ポリシの作成 IoT Conenct Mobile Type SのeSIMの調達・開通を除き、コンフィグマネージャー自体の利用ステップは、 大きく分けて コンフィグ生成の設定 と コンフィグ配信の設定 の2ステップです。 これらの2ステップを実施するだけで、IoTデバイスから設定ファイルを取得できるようになります。 ポータル上からテンプレートや共通パラメータなどを編集できる機能があったり、テンプレートや共通パラメータのお気に入りの組み合わせを登録できるプロファイルセット、もしくは設定ファイルのチェック機能などの便利機能も備えているのでぜひ活用してみてください! 詳細手順については、下記のチュートリアルをご参照ください。 コンフィグマネージャーを利用する 終わりに コンフィグマネージャーは、大量のIoTデバイスを扱う上で課題になる「IoTデバイスの設定ファイルの生成と更新の大変さ」を削減できる機能です。IoTデバイスのキッティング時の初期設定ファイルをコンフィグマネージャーから配布したり、IoTデバイスの稼働中に設定ファイルを更新したいケースなど、様々なケースに対応させることができます。 IoTデバイス側の実装次第では、プロトコル変換機能やクラウドアダプター機能などの他機能と連携させることで、マルチクラウド対応かつゼロタッチプロビジョニング可能な環境を構築することもできます。コンフィグマネージャーをぜひ活用してみてください。 過去連載の紹介  IoT Connect Gatewayは、今日までにプロトコル変換機能に加えて、スタンダード、イベント、またはファンクションなどの様々なクラウドアダプター機能を提供しています。 IoT Connect Gateway が提供する一部の機能や取り組みは、以下の過去連載で紹介されているのでご参照ください。 IoT Connect Gateway を使ってみた 第1回 〜ICGWのご紹介〜 IoT Connect Gateway を使ってみた 第2回 〜AWS IoTCoreに接続してみよう〜 IoT Connect Gateway を使ってみた 第3回 〜観葉植物育成状況の可視化〜 IoT Connect Gatewayを使ってみた 番外編 第2回 ~インターンシップでStorage転送機能を使って開発してみた~ SDPFを使ってみた 第4回 ~SDPFサービス群の組合せ事例のご紹介<IoTプラットフォームの自動構築>~ IoT Connect GatewayのSDP開発に迫る *1 : 配信用エントリポイントから設定ファイルを取得するHTTPクライアントはIoTデバイス側で別途実装する必要があります
アバター
みなさんこんにちは、社内のエンジニアが働きやすくすることを目標にする Engineer Empowerment プロジェクトの @Mahito です。 社内勉強会を始めたけれど長く続かないという話は時々、知人から聞いたり Twitter で見かけたりすることがあります。 今回は NTT Com で 2014 年から 8 年間続いている社内勉強会 TechLunch の運営を続ける際に行っていることについて書きたいと思います。 本記事は少々長めになっているため、先に内容をまとめると以下のようになります。 社内勉強会 TechLunch の紹介 社内勉強会を長く続けるためにどんなことを考えたか 続けていくために「ゆるく」したこと 発表の敷居を下げる 運営が頑張りすぎない 参加者にもゆるく楽しんでもらう TechLunch とは NTTコミュニケーションズでは、TechLunch と称して社内ランチ技術勉強会を 8年以上に渡り、153回開催しています。(2022/10/20 時点) お昼休みの時間を利用して、お昼ごはんを食べながらゆる〜く、気軽に参加できる勉強会となっています。 参加者が TechLunch に参加することで他部/チームの取り組みやスキルについて学んだり、わからないことを聞ける相手が増えることを目指しています。 もともとは @iwashi86 が異動直後に話せる人が欲しいと言う理由で始めたそうですが、途中で iwashi86 の都合の悪い期間があったため、私がヘルプとして運営に参加しました。 私自身も NTT グループの他社から異動してきた身なので、TechLunch に参加したり発表をしたりすることで他チームの人を知ったり知ってもらったりとお世話になっていました。そのため、この勉強会を長く続けていく手伝いができればと思い色々試行錯誤をして今に至っています。 長く続けるために考えたこと 先に述べたように、 社内勉強会が続かない といった危惧は TechLunch にも当てはまります。 TechLunch では開催を不定期にしており、発表者を希望する人が現れると日程の調整・周知・開催の流れになるのですが、お分かりのとおり発表者がいないと開催されません。 この8年間の間で、平均すると約 19 日に 1 回のペースで開催されていますが、一番開催間隔が長いときは 84 日開催されませんでした。 つまり、TechLunch を継続していくためには発表者を増やすための働きかけが大事になってきます。 発表の担当を持ち回りにすることで定期的に開催をする方法もありますが、TechLunch は社員が自由に開催し、気軽に参加できるイベントでありたいという私個人の思いがあります。 やらされ感での発表ではなく、発表したい人が気軽に発表できる方向でなんとか出来ないかと考えました。 続けるためにゆるく TechLunch を長く続けていくためにどうするかと思案しているときに、昔あるイベントのボランティアスタッフをしていた時の話を思い出しました。 私はプログラミング言語 Ruby のイベントである RubyKaigi のボランティアスタッフを 2011, 2013 と経験させていただきました。 その際に 2011 と 2013 で同じイベントのスタッフをしているにも関わらず、2013 のイベントは過去と比べて運営サイドに余裕というかゆるさがありました。 不思議に思い、打ち上げの際に主催者の方にそのことを尋ねると 「長く続けていくために燃え尽きないようにゆるくやっていこうと思った」 という話をされました。 当時の私は 2011 年のイベントの時に味わった、スタッフみんなで一生懸命頑張り、参加者に喜んでもらう感じが楽しかったので今ひとつ理解できずに終わりました。しかし、数年が経ち、その話をふと思い出した時に TechLunch をもっとゆるくすることで続けていけるのではと考えました。 そこで、TechLunch をゆるくすべく以下のようなことを考え実施しています。 発表内容の敷居をさげる TechLunch は技術セミナーという形で開催していたため、技術的な内容、特に最新の技術や話題の技術の発表が多く、聞く側にとっては刺激的で魅力的なものが多くありました。一方でこうした発表が続くと、次の発表に対しても最新の技術の話や技術的に濃い話が期待され、それ以外が発表しにくいという空気がありました。 こうした空気を変えるために、 TechLunch ではあえて IT とは離れた技術の発表も取り入れることにしました。 以下は IT ではない技術の話のタイトル(一部)です。 3ヶ月のコミットから始める一生役に立つ英語学習法のススメ 英語圏以外での仕事を進める方法(タイ編)とアジア展開のお話 身近に感じよう!簡単!ラグビー説明会 【年末特番】いま、日本酒がおもしろい! 「写真撮影のキホン」x「それを支える技術の話」 長くよく飛ぶ紙ヒコーキ作りませんか? NTT Com は海外トレーニー制度として1年間海外で OJT をするしくみや、ラグビーチームを持っていたこともありその経験者やラグビー選手がいます。 また、個人の趣味として日本酒やカメラを嗜む人、紙飛行機の地区大会で優勝した人など様々なスキルを持った人に、その経験や知識を発表してもらうことで、「こんな発表もありなのか」と発表の敷居を下げるのに一役を買ってもらいました。 よく飛ぶ紙飛行機の折り方をレクチャーされて真剣に紙飛行機を折る大人たち 実際に、こうした発表も取り入れることで「こんな発表をしても大丈夫ですか?」という相談も増え、発表の敷居が少し下がったのかなと感じています。 来月にはパイプオルガンについてリモートで現物を使いながら話をしてくれるという人も現れたので今から楽しみです。 発表者都合での開催 当初の TechLunch は金曜日にオフィスで開催と、曜日と場所が決まっていました。 しかし、発表者が発表しやすいタイミングでできたほうが良いのではということで、曜日を任意に変更し、リモートでの発表もありというふうに形を変えていきました。 最近はリモートワークが前提となったため、発表、参加ともにすべてオンラインとなりましたが、リモートでの発表も事前に経験をしていたので、特に混乱なく運営を続けることが出来ました。 時間に関しては TechLunch と名をつけていることもあり、ここだけは変更をせずに開催を続けていますが、NTT Com は TechNight というイベントも有り、夜にワイワイ発表をする・聞くイベントもあります。 発表者を社内に限らない TechLunch は社内の勉強会として運営していますが、時々社外の方にお願いをして発表をしてもらったりもしています。 以下は社外の人に発表してもらったタイトル(一部)です。 Heapstats: Troubleshooting with Serviceability and the New Runtime Monitoring Tool Gitの便利ワザ トランザクション入門 シリコンバレーで3ヶ月働いて体感した自律型組織のススメ ビジネスセクターからソーシャルセクターへ 〜NTTグループ元技術者が語るNPOでの働きがい〜 NPO の話は、前職で読書会を一緒にやっていた先輩が NPO に転職をしており、私から発表をお願いして発表を引き受けていただきました。 これ以外にも、面白い技術を持っている人や詳しい人にお願いをして発表に来てもらうことで、参加者には時々いつもの TechLunch とは違う発表を聞いてもらっています。 ゆるく運営 TechLunch の運営は私を含め社内の有志 4 人で現在運営しています。 運営のやることは 発表者との日程調整、周知、司会 ぐらいに抑えているため、タスクとしてはかなり軽いものとなっています。 昔は発表者との調整や周知をメールでやり取りをし、Slack で追加の周知、リモートで参加する人のための配信を準備と少し準備が大変でした。 現在は調整と周知を Slack に絞ったことや、発表・参加ともにフルリモートへ変わったため準備がかなり楽になりました。 運営の作業もゆるくやることにより、運営する我々が燃え尽きず長く続けるようにと考え、やることを極力減らしリモート化も相まって今の形に落ち着いています。 参加者にもゆるく TechLunch の参加には申込不要、出欠も取らず参加したい人が参加したいタイミングで参加できるようにしています。 他にも、参加者が楽しく参加できる仕掛けとして、オフラインで開催していた頃は希望者でお金を出し合ってピザのデリバリーを頼んだり、出張の板前寿司を頼んだりしていました。 写真:寿司に群がる参加者 参加者は以前からも Slack を使った実況などをしてくれていましたが、リモートでの開催になってからはさらに実況や質問を Slack へ書き込むようになりました。Slack での実況を通じて、参加者が発表を聞きながら意見を交わしたり交流が行われていたりします。最近、発表者の方が最初にチェックインとして参加者に質問を投げかけ、回答を書いたり Slack のリアクションをしてもらうった際は、実況がいつもより盛り上がっていました。こうした手法も取り入れながら参加者がより気軽に楽しんで参加できる仕組みなどを今後取り入れていければと考えています。 まとめ TechLunch では、発表者の敷居を下げ、運営の負担を減らし、参加者が楽しめる仕掛けを用意することで 8 年間続く社内勉強会となっています。 今後もゆるく長く続けていくことで、社内のノウハウの共有や参加者のスキルアップなどにつなげる場にしていきたいと思っています。 また、今回の記事が、みなさんの開催される勉強会などの継続するヒントになれば幸いです。 そして、最近では皆さんにも参加していただける NTT Com Open TechLunch というイベントもはじめました。なんとタイムリーに次回は 10/27(木) に開催されますので、興味のある方はご参加ください!
アバター
はじめに こんにちは、クラウド&ネットワークサービス部の福岡( @tkygtr6 )です。 普段は SDPF クラウドの IaaS である、ベアメタルサーバー・ハイパーバイザーサービス開発のソフトウェアエンジニアとして働いています。 先日 Tech Night で「業務で ISUCON することになった話 〜課金 API の高速化〜」と題して発表しましたので、その内容について簡単にかいつまんで紹介します。 Tech Night とは社内で数ヶ月に1回開催されている、お酒を飲みながら技術でワイワイするイベントです。 各人が 5分〜20分ほどのネタを持ち寄って発表することになっていて、全体として 2〜3 時間ほどになるのが通例です。 コロナ禍になる前までは オンサイトで開催 されていましたが、最近はリモートでの開催が続いています。 発表内容の概略 ISUCON 1 とは LINE 株式会社が運営を行なっている Web アプリケーションの高速化についてのコンテストであり 、参加者は与えられた遅い Web アプリケーションを所定の時間内にどれくらい高速化できるかを競います。(本記事ではこの ISUCON を競技 ISUCON と呼びます。) 今回 Tech Night で発表した内容は、業務中に競技としての ISUCON に参加や開催した話ではなく、業務中に発見した遅い API の原因を特定し ISUCON の知識を用いて高速化した(業務 ISUCON)話となります。 特に発表では、以下の二点に焦点を当てて話しました。 どのように競技 ISUCON で身につけた知識を業務に生かすことができたのか (DB のインデックス周りの話) 業務 ISUCON は競技 ISUCON とどのようなところが違ったのか (主に検証の話) この記事では発表の内容の一部を紹介します。 本発表のフルスライドは こちら で公開しておりますので、興味のある方は是非ご覧ください。 どんな API を高速化したのか 高速化の対象になった課金 API はハイパーバイザーサービスが(内部向けに)提供している API ですので、まずはサービスの概要について説明します。 ハイパーバイザーサービスとは、vSphere や Hyper-V のようなハイパーバイザーをインストール済みの物理サーバーをオンデマンドに提供するサービスです。 また、ハイパーバイザー上で稼働する VM のライセンスについてもワンストップで提供するという機能も同時に提供しており、お客さまが自前でライセンス管理をする手間を取り除いています。 お客さまが使用した VM の有料 OS ライセンスについては、他のクラウドの使用料金と合算して請求されます。 この機能を実現するために我々クラウド事業者は、お客さまがある期間にどの種類の VM をどれくらい利用していたのかを収集する必要があります。 これを行っているのが今回高速化の対象になった課金 API です。 具体的には「2022年の8月にお客さま(テナント) A が使用した VM は?」という リクエストに対して「期間中に使用された VM X は 2つ、VM Y は1つです」と返すようなものをイメージしていただければと思います。 どのように高速化したのか 調査を進める中で、当該の API の中では、数百万レコード以上からなる巨大なテーブルに対してのクエリが発行されていて、それがボトルネックになっているようだということがわかりました。 さらに、この巨大テーブルには適切なインデックスが貼られていなさそうであるということが判明しました。 詳しい技術的な部分については フルスライド の P23〜P26あたりを参照していただければと思うのですが、改善と測定をループを回す中で、ただシンプルなインデックスを貼るだけでは高速化できなさそうであるということがわかりました。 したがって、クエリに含まれる JOIN 句を取り除き、さらに複合インデックスを貼ることによって、クエリを改善するに至りました。 最終的には、高速化前と比較して、30倍の高速化を達成できました。 業務 ISUCON ならではのハマりポイント ここまでに至る道のりは一見シンプルなようですが、業務 ISUCON ならではのハマりポイントがいくつかありました。 検証のための環境構築が難しい 競技 ISUCON では商用の環境を直接操作することによって高速化を行うことになりますが、実サービスを運用する立場ではそのような乱暴なことはできません。 特に改善を加えた後に実際に速度が改善されたかどうかについての検証するためには、検証環境で DB の設定を商用とできる限り近づける必要があります。 その過程で、どうしても商用環境を用いた場合と同じような性能を出すことができないという問題が生じたため、切り分けを行う必要がありました。 切戻し方法の検証が必要 競技 ISUCON では高速化してしまえばそれで終了ですが、業務でリリースをする時には、何らかの原因で失敗した場合に備えて、あらかじめ切り戻し検証を実施する必要があります。 今回取り入れた改善の内容には複合インデックスの付与が含まれていたのですが、切り戻し時に複合インデックスの削除がうまくいかないという問題が発生したため、手順を追加する必要がありました。 余談 最終的にはクエリの最適化 + 複合インデックスを貼ることで対処したのですが、チームではインデックスの追加はしない方が良いのでは、という意見もありました。 API 自体を高速化するには過去の不要なレコードを削除するというやり方の方が簡単なのではないか、という話です。 ただ、どれくらい古いレコードを削除すれば良いか検討が必要なこと、大量のレコードを削除することにも危険があるということで、クエリの最適化 + 複合インデックスを貼るという方針で進めることになりました。 今回は没になったとはいえ、エンジニア的に「かっこいい」やり方以外の方法もあらかじめ比較検討することは大切であるということを学びました。 おわりに 「業務で ISUCON することになった話 〜課金 API の高速化〜」という題で、Tech Night で発表しました。 ISUCON によって獲得したデータベース周りのスキルが業務に活かせることを実感した一方、検証を通して信頼性を担保しながら開発する勘所を掴むことができて良い勉強になりました。 個人的な話になりますが Tech Night は入社前にも参加したことがあり、その時からいつか自分も発表する側になりたいと思っていたので、その目標を果たすことができてよかったです。 また、発表中に slack を通して普段関わらない部署のエンジニアとワイワイ交流できて楽しかったです、皆さんありがとうございました! 最後になりますが、SDPF クラウドは国内最大級のクラウドサービスです。 開発メンバーは、数千台以上の物理サーバの操作の自動化をはじめとした、技術的難易度の高い課題に取り組みつつ、日々より良いサービスにしようと邁進しております。 今回の発表で取り上げた DB のチューニングのような、長く運用されることで初めて気づくような面白い課題もたくさん転がっています。 もし我々のチームに興味を持たれた方は こちら からの応募をお願いいたします。 「ISUCON」は、LINE株式会社の商標または登録商標です。 https://isucon.net ↩
アバター
サマリ SR-MPLS L3VPN における TI-LFA を用いた障害時の高速迂回の実現 IOS XR + Junos の Multi-vendor 環境での動作検証 この記事は Multi-AS Segment Routing 検証連載の第 9 回です。目次は こちら 概要 イノベーションセンターの岩田です。 本記事では前回までに紹介してきた SR-MPLS L2/L3 VPN をサービスとして提供する際に欠かせない障害発生時の高速迂回手法についてご紹介します。 障害時の迂回動作について これまでの記事で紹介してきた SR-MPLS 上での L2/L3 VPN を実際にサービスとして提供する際には、機器の故障などが発生した場合でも通信が断絶する時間を少なくするための設計をすることがサービス品質の観点において欠かせません。 通常、ネットワーク上のノードやリンクに障害があった場合、障害を検知したルーターは障害により変更された新しい IGP の LinkState 情報を他ルーターと交換します。 その後、各ルーターが迂回路を計算しルーティング情報を更新することで通信が復旧します。 下図は迂回など事前準備の無い NW において障害発生から復旧までの過程を表現した図です。 図中の ② 〜 ④ の過程にあたる、各ルーター上で IGP が経路を再計算し、トポロジーを収束させる過程には数秒を要し、その間通信が断絶することになります。 図中の ② 〜 ④ の過程で通信が断絶する時間をいかに少なくするかという点はサービスの品質向上に欠かせない考慮点となります。 このような障害時の課題を解決するための技術として次節で紹介する Fast Reroute があります。 Fast Reroute Fast Reroute(FRR)は各ノードにおいて、IGP 情報をもとに保護したいノードやリンクに対するバックアップパスをあらかじめ計算しておき、それらに障害が発生した際、即座にバックアップパスへ切り替える事で通信が断絶する時間を短縮するための技術です。 下図は、FRR を適用したネットワークにおいて障害発生から復旧までの過程を表現したものです。 図中の ③ のように、障害を検知した後、IGP により再計算された経路に切り替わるまでの間、各ノードが自律的に判断し事前に計算した経路へ切り替えるため通信が断絶する時間を 50 ミリ秒以下に短縮できます。 1 FRR の実現方式としては、Loop-Free Alternate(LFA)、Remote-LFA、Topology Independent-LFA(TI-LFA)などがあります。 本記事では Segment Routing(SR)を用いたネットワークで利用できる TI-LFA を用いて検証します。 TI-LFA TI-LFA は SR domain で用いることができる FRR の手法の 1 つで、現在 Internet-Draft 2 として議論されています。 前節でも触れましたが、LFA はその名の通りループしないバックアップパスをあらかじめ計算し用意しておき、障害が発生した際、即座にその経路へ切り替えることで通信が断絶する時間を短くするための技術です。 LFA、Remote-LFA では適用するトポロジーによってはバックアップパスを計算できない課題がありますが、TI-LFA はトポロジーに依存することなくバックアップパスを計算できます。 検証 本章では TI-LFA を用いて保護したネットワークにおいて障害が発生した場合、FRR で 50ms 以内に迂回が実現されることを検証します。 また、比較のため保護していない場合についても検証し、FRR によって復旧時間が短縮されることも確認します。 検証は以下のようなトポロジーを用いて行います。 ベンダー間の TI-LFA 機能の互換性の検証のため IOS XR と Junos のルーターを交互に配置しています。 また本検証で用いる各ルーターの機種名と OS のバージョンは以下の通りです。 rt01(ASR 9901、 IOS XR 7.6.1) rt02(MX204、 Junos 21.3R1.9) rt05(Cisco 8201、 IOS XR 7.5.1) rt06(PTX10001-36MR、 Junos 21.4R1.15-EVO) rt07(ASR 9902、 IOS XR 7.6.1) 復旧時間の計測は、 ping コマンドを用いて行います。 VM 間で ICMP パケットを一定間隔(10ms 毎)で送信し続けておき、 rt01 と rt02 間のリンクを切断した際にどの程度パケットがロスするかを計測する事で切り替えにかかった時間を測定します。 また各ベンダーの TI-LFA の動作確認を行いたいため、IOS XR(rt01)、Junos(rt02) のルーターそれぞれに対し検証します。 IOS XR ルーターについては以下の手順で確認します。 vm01 から vm02 に対し、ping コマンドを用いて ICMP パケットを短い一定間隔で送信し続けておく vm02 では tcpdump コマンドを用いて ICMP の request パケットをキャプチャしておく rt01 と rt02 間リンクの rt02 側インタフェース(xe-0/1/0)をシャットダウンする事で擬似的な障害を発生させる インタフェースをシャットダウンした際に、vm01 から vm02 への ICMP パケットがどれ位ロスされたかを確かめる Junos ルーターについては、以下の手順で確認します。 vm02 から vm01 に対し、ping コマンドを用いて ICMP パケットを短い一定間隔で送信し続けておく vm01 では tcpdump コマンドを用いて ICMP の request パケットをキャプチャしておく rt01 と rt02 間リンクの rt01 側インタフェース(Te0/0/0/8)をシャットダウンする事で擬似的な障害を発生させる インタフェースをシャットダウンした際に、vm02 から vm01 への ICMP パケットがどれ位ロスされたかを確かめる 事前準備 まず、rt01 と rt02 間で VRF 100 による L3VPN を実装します。 L3VPN の設定は 第 4 回の記事 を参考にして以下を実施します。 VRF 100 による L3VPN 作成 BGP color の付与と広告 リンク切断時の復旧動作(FRR による保護なし) IOS XR ルーターでの経路切り替え動作 リンク切断 以下の設定を追加し、rt01 と rt02 間リンクの rt02 側インターフェースである xe-0/1/0.0 をシャットダウンすることで擬似的に障害を発生させます。 [edit] user@rt02# show | compare [edit interfaces xe-0/1/0] + disable; vm01 から vm02 へ ICMP request の送信 以下のコマンドを実行します。 user@vm01:~$ sudo ping -i 0.01 192.168.1.1 復旧に要した時間 vm02 において tcpdump コマンドを用いて、vm01 から受信した ICMP request パケットをキャプチャすると以下のような結果となりました。 rt06 を経由する経路へ切り替わると、ホップ数が 3 増加するため ttl は 3 減少します。 ttl に着目し障害が発生した時点でのパケットを探すと ICMP のシーケンス番号 が 355 から 368 の間でパケットロスが確認でき、経路が切り替わっている事が分かります。 よって、IOS XR では通信復旧に(369 - 354)* 10ms = 150ms 程度要したことが確認できました。 user@vm02:~$ sudo tcpdump icmp[icmptype] == 8 -i ens192 -v 03:17:55.457552 IP (tos 0x0, ttl 62, id 32297, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 352, length 64 03:17:55.473551 IP (tos 0x0, ttl 62, id 32301, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 353, length 64 03:17:55.489561 IP (tos 0x0, ttl 62, id 32305, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 354, length 64 03:17:55.729656 IP (tos 0x0, ttl 59, id 32344, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 369, length 64 03:17:55.745566 IP (tos 0x0, ttl 59, id 32348, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 370, length 64 03:17:55.761589 IP (tos 0x0, ttl 59, id 32352, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 31, seq 371, length 64 Junos ルーターでの経路切り替え動作 リンク切断 RP/0/RSP0/CPU0:rt01(config)#show commit changes diff precise Tue Sep 20 17:55:55.645 JST Building configuration... !! IOS XR Configuration 7.6.1 + interface TenGigE0/0/0/8 + shutdown ! end vm02 から vm01 への ICMP request の送信 以下のコマンドを実行します。 user@vm02:~$ sudo ping -i 0.01 192.168.0.1 復旧に要した時間 vm01 において、vm02 から受信した ICMP の request パケット情報を確認します。 ICMP のシーケンス番号が 262 から 274 までのパケットが欠けていることから、Junos では通信復旧に (275 - 261) * 10ms = 140ms 程度要したことが確認できました。 user@vm01:~$ sudo tcpdump icmp[icmptype] == 8 -i ens192 -v 03:23:40.910480 IP (tos 0x0, ttl 62, id 43550, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 259, length 64 03:23:40.926459 IP (tos 0x0, ttl 62, id 43553, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 260, length 64 03:23:40.942443 IP (tos 0x0, ttl 62, id 43555, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 261, length 64 03:23:41.166526 IP (tos 0x0, ttl 59, id 43589, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 275, length 64 03:23:41.182452 IP (tos 0x0, ttl 59, id 43592, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 276, length 64 03:23:41.198547 IP (tos 0x0, ttl 59, id 43593, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 58, seq 277, length 64 リンク切断時の復旧動作(FRR による保護あり) 続いて FRR(TI-LFA)を用いて rt01 と rt02 リンクを保護した場合を検証します。 TI-LFA の設定(IOS XR) 保護したいノードに対し以下の設定を追加します。 rt01(IOS XR) RP/0/RSP0/CPU0:rt01#show running-config router isis 1 interface tenGigE 0/0/0/8 Tue Sep 20 16:52:44.640 JST router isis 1 interface TenGigE0/0/0/8 point-to-point address-family ipv4 unicast fast-reroute per-prefix fast-reroute per-prefix ti-lfa ! ! ! 以下のように TeGigE0/0/0/8 が利用される 10.255.1.2/32 への経路に対し、バックアップパスが計算されている事が確認できます。 RP/0/RSP0/CPU0:rt01#show isis fast-reroute sr-only Tue Sep 20 16:57:00.430 JST IS-IS 1 IPv4 Unicast FRR backups Codes: L1 - level 1, L2 - level 2, ia - interarea (leaked into level 1) df - level 1 default (closest attached router), su - summary null C - connected, S - static, R - RIP, B - BGP, O - OSPF E - EIGRP, A - access/subscriber, M - mobile, a - application i - IS-IS (redistributed from another instance) D - Downstream, LC - Line card disjoint, NP - Node protecting P - Primary path, SRLG - SRLG disjoint, TM - Total metric via backup Maximum parallel path count: 8 L2 10.255.1.2/32 [10/115] via 10.1.2.2, TenGigE0/0/0/8, rt02, SRGB Base: 16000, Weight: 0 Backup path: TI-LFA (link), via 10.1.7.2, TenGigE0/0/0/9 rt06, SRGB Base: 16000, Weight: 0, Metric: 40 P node: rt05.00 [10.255.1.5], Label: 16005 Prefix label: 16002 Backup-src: rt02.00 L2 10.255.1.3/32 [40/115] via 10.1.2.2, TenGigE0/0/0/8, rt02, SRGB Base: 16000, Weight: 0 Backup path: LFA, via 10.1.7.2, TenGigE0/0/0/9, rt06, SRGB Base: 16000, Weight: 0, Metric: 50 L2 10.255.1.4/32 [20/115] via 10.1.7.2, TenGigE0/0/0/9, rt06, SRGB Base: 16000, Weight: 0 No FRR backup L2 10.255.1.5/32 [30/115] via 10.1.2.2, TenGigE0/0/0/8, rt02, SRGB Base: 16000, Weight: 0 Backup path: LFA, via 10.1.7.2, TenGigE0/0/0/9, rt06, SRGB Base: 16000, Weight: 0, Metric: 40 L2 10.255.1.6/32 [10/115] via 10.1.7.2, TenGigE0/0/0/9, rt06, SRGB Base: 16000, Weight: 0 No FRR backup L2 10.255.1.7/32 [30/115] via 10.1.7.2, TenGigE0/0/0/9, rt06, SRGB Base: 16000, Weight: 0 No FRR backup RP/0/RSP0/CPU0:rt01# TI-LFA の設定(Junos) FRR を利用するための設定を追加します。 rt02 (Junos) set policy-options policy-statement lbpf then load-balance per-packet set routing-options forwarding-table export lbpf set protocols isis backup-spf-options use-post-convergence-lfa 保護したいノードに対し以下の設定を追加します。 rt02 (Junos) set protocols isis interface xe-0/1/0.0 level 2 post-convergence-lfa 以下のように xe-0/1/0.0 が利用される 10.255.1.1/32 への経路に対し、バックアップパスが計算されている事が確認できます。 user@rt02> show route table inet.3 inet.3: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden) @ = Routing Use Only, # = Forwarding Use Only + = Active Route, - = Last Active, * = Both 10.255.1.1/32 *[L-ISIS/14] 00:03:24, metric 20 > to 10.1.2.1 via xe-0/1/0.0 to 10.1.3.2 via et-0/0/1.0, Push 16001, Push 16006(top) 10.255.1.3/32 *[L-ISIS/14] 00:03:24, metric 30 > to 10.1.3.2 via et-0/0/1.0, Push 16003 10.255.1.4/32 *[L-ISIS/14] 00:03:24, metric 30 > to 10.1.2.1 via xe-0/1/0.0, Push 16004 to 10.1.3.2 via et-0/0/1.0, Push 16004 10.255.1.5/32 *[L-ISIS/14] 00:03:24, metric 20 > to 10.1.3.2 via et-0/0/1.0 10.255.1.6/32 *[L-ISIS/14] 00:03:24, metric 20 > to 10.1.2.1 via xe-0/1/0.0, Push 16006 to 10.1.3.2 via et-0/0/1.0, Push 16006 10.255.1.7/32 *[L-ISIS/14] 00:03:24, metric 30 > to 10.1.3.2 via et-0/0/1.0, Push 16007 IOS XR ルーターでの経路切り替え動作 リンク切断 [edit] user@rt02# show | compare [edit interfaces xe-0/1/0] + disable; vm01 から vm02 への ICMP request の送信 以下のコマンドを実行します。 user@vm01:~$ sudo ping -i 0.01 192.168.1.1 復旧に要した時間 vm02 において、vm01 から受信した ICMP の request パケット情報を確認します。 ICMP のシーケンス番号が 382 の パケットが欠けていることから、IOS XR では通信復旧に(383 - 381)* 10ms = 20ms 程度要したことが確認できました。 user@vm02:~$ sudo tcpdump icmp[icmptype] == 8 -i ens192 -v 03:04:30.433895 IP (tos 0x0, ttl 62, id 57521, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 378, length 64 03:04:30.449904 IP (tos 0x0, ttl 62, id 57525, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 379, length 64 03:04:30.465836 IP (tos 0x0, ttl 62, id 57528, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 380, length 64 03:04:30.481802 IP (tos 0x0, ttl 62, id 57531, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 381, length 64 03:04:30.513843 IP (tos 0x0, ttl 59, id 57536, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 383, length 64 03:04:30.529835 IP (tos 0x0, ttl 59, id 57540, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 384, length 64 03:04:30.545934 IP (tos 0x0, ttl 59, id 57541, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > vm02: ICMP echo request, id 30, seq 385, length 64 Junos ルーター(rt02)での経路切り替え動作 リンク切断 RP/0/RSP0/CPU0:rt01(config)#show commit changes diff precise Tue Sep 20 15:26:25.506 JST Building configuration... !! IOS XR Configuration 7.6.1 + interface TenGigE0/0/0/8 + shutdown ! end vm02 から vm01 への ICMP request の送信 以下のコマンドを実行します。 user@vm02:~$ sudo ping -i 0.01 192.168.0.1 復旧に要した時間 vm01 において、vm02 から受信した ICMP の request パケット情報を確認します。 パケットが欠けていないことから、Junos では通信復旧に (295 - 294) * 10ms = 10ms 程度要したことが確認できました。 user@vm01:~$ sudo tcpdump icmp[icmptype] == 8 -i ens192 -v 03:10:58.573764 IP (tos 0x0, ttl 62, id 14664, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 292, length 64 03:10:58.589749 IP (tos 0x0, ttl 62, id 14668, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 293, length 64 03:10:58.605753 IP (tos 0x0, ttl 62, id 14670, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 294, length 64 03:10:58.621777 IP (tos 0x0, ttl 59, id 14672, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 295, length 64 03:10:58.637835 IP (tos 0x0, ttl 59, id 14674, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 296, length 64 03:10:58.653771 IP (tos 0x0, ttl 59, id 14676, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.1.1 > vm01: ICMP echo request, id 57, seq 297, length 64 検証まとめ 障害が発生した際に、各ルーターで復旧に要した時間は以下のようになりました。 IOS XR、Junos 共に、FRR を用いて保護することで 50ms 以下で復旧できることが確認できました。 OS TI-LFA(FRR) 設定なし TI-LFA(FRR) 設定有り IOS XR 150ms 以内 20ms 以内 Junos 140ms 以内 10ms 以内 まとめ 本記事では、SR-MPLS L3VPN における TI-LFA を用いた障害時の高速迂回手法について紹介しました。 また、Multi-vendor で TI-LFA を利用しない場合と利用した場合の動作検証をそれぞれ行い、TI-LFA を利用する事で障害時に復旧までの時間を 50ms 以内にできる事を確認しました。 次回の記事では PCEP を用いた SR Policy の適用方法について紹介予定です。 (2022/11/14 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #10] PCE を用いた SR-TE の一元管理 https://datatracker.ietf.org/doc/html/rfc4105#section-4.2.3 ↩ https://datatracker.ietf.org/doc/html/draft-ietf-rtgwg-segment-routing-ti-lfa ↩
アバター