TECH PLAY

NTTドコモビジネス

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

613

こんにちは、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 ↩
DevOpsプラットフォームの取り組みを紹介する7回目の記事です。 Qmonus Value Stream 開発チームの奥井( @HirokiOkui )です。 連載第7回では、Qmonus Value Streamの中核を担うコンポーネントであるAssemblyLineについて深堀りします。 第2回 および 第6回 で解説したとおり、Qmonus Value Streamでは、AssemblyLineという独自のリソースを定義してCI/CDパイプラインを構成します。 AssemblyLineは、 Tekton と同様にKubernetesのカスタムオペレーターとして実装されています。 AssemblyLineは、Tekton Pipelineを実行するワークフローエンジンとしての責務に加えて、柔軟性の高いCI/CDパイプラインを構成・実行するために必要な様々な機能を有しています。 本記事では、AssemblyLineが有する機能の概要について紹介したのち、それらの機能をどのように実現しているかを解説します。 AssemblyLineが果たす3つの役割 Qmonus Value Streamでは、アプリケーションのビルド・デプロイ・リリースを一貫して行うCI/CDパイプラインをAssemblyLine(組立ライン)と呼称しています。 CI/CDパイプラインを構成する一連のリソースのうち一番上の層に位置するのがAssemblyLineであり、AssemblyLineを実行することでPipelineが駆動され、実際のタスク処理が実行されます。 AssemblyLineの構成例を以下に示します。 AssemblyLineは複数のステージから構成され、1つのステージは1つのPipelineで構成されます。 また、ステージ間の依存関係を定義することでDAG(有向非巡回グラフ)を形成し、各ステージを順番に実行できます。 このように、AssemblyLineに対して複数のPipelineを定義することで、多くの処理をまとめて実行する高度なCI/CDパイプラインを構成できます。 AssemblyLineは、TektonのPipelineやTaskと同様に、Kubernetesのカスタムリソースとして構成されています。 このため、AssemblyLineのマニフェストはKuberenetes YAMLの形式で記述します。 具体的な記述例は、 第2回 の記事を参照してください。 このように、Qmonus Value Streamでは、Pipeline/Taskとすでに2層構造(Stepも含めると3層構造)になっているTektonの構成に対して、さらにAssemblyLineという上位の層を追加しています。 複数のTaskを統合する層として既にPipelineが提供されている以上、この構成は冗長に思えることでしょう。 AssemblyLine層を追加した理由は、大きく分けて3つあります。 1. Pipeline・Taskを再利用可能な汎用モジュールにする AssemblyLine層が必要な1つ目の理由は、Pipeline・Taskの汎用性を高めて再利用可能にし、ユーザが自身で開発するCI/CDパイプラインマニフェストをできるだけ少なくするためです。 再利用可能な汎用モジュールとして実装しCloud Native Adapterに組み込むことで、ユーザはCloud Native Adapterを選択するだけで商用にデプロイするCI/CDパイプラインを構成することを可能にしています。 (Cloud Native Adapterについては、 第3回 をご覧ください) TektonのTaskとPipelineについて、簡単におさらいします。 Taskは、KubernetesのPodを生成し、TaskにStepとして定義された複数のコンテナを順番に起動する責務を持ちます。 KubernetesにおいてPodはワークロードリソースの最小の単位であり、これを起動する責務を持つことから、TaskはCI/CDパイプライン定義における最小の単位となります。 必要なコンテナを組み合わせることで、任意の処理を行うTaskが構成できます。 一方Pipelineは、自身に定義されたDAGに従ってTaskを順番に実行する責務を持ちます。 複数のTaskを直列・並列に組み合わせることで、任意の複雑なパイプラインを構成できます。 ここで、再利用性を高めるために汎化することを考えます。 Taskは複数コンテナを組み合わせて任意の処理を組み上げられますが、あまり多くのコンテナを包含して責務を持ちすぎると、汎用モジュールとして扱うことができません。 再利用性の観点から、1つもしくは数個のコンテナを使って、例えばGitからのチェックアウト、コンテナのビルド、スキャンなど、限られた責務だけを具備させることが望ましいです。 Taskを再利用可能な汎用モジュールとして構成すると、AssemblyLine層がない場合には、Taskを組み合わせてCI/CDパイプラインを構成することをPipeline一層だけで行わなければなりません。 単一のアプリケーションのビルド・デプロイ・テストだけを行うようなシンプルなCIであれば、1つのPipelineだけで十分でしょう。 一方で、総合検証環境でテスト用のカスタムビルドを作成しつつ、複数のマイクロサービスをデプロイするケースや、検証用に複数の試験環境を構成したい場合はどうでしょうか。 1つのPipelineで組み上げると複雑になりすぎるため、複数の異なるPipelineに分割し、それぞれを実行することになるでしょう。 複数のPipelineに分割された一方で実施したい目的は1つのため、複数のPipeline実行を自動化するためにシェルスクリプトなどでラップすることになりそうです。 Pipeline間に順序依存があったり、Pipelineの実行結果を後段のPipelineで利用する必要がある場合は、シェルスクリプトでワークフローもどきを書き始めることになってしまいます。 Qmonus Value Streamでは、Pipelineを分割し統合管理する必要が生じる複雑なユースケースを想定して、AssemblyLineという複数のPipelineを統合するコンポーネントを設けました。 そして、Pipelineの責務を「1つのアプリケーションを1つの環境に対して、ビルド・デプロイ・リリースなどのまとまった処理のみ実行する」ように限定することを選択しました。 この粒度で分割されたPipelineを組み合わせてAssemblyLineが構成され、AssemblyLineを実行することでCI/CDパイプラインを駆動します。 これにより、以下の効果を狙っています。 Pipelineが、ビルド・デプロイ・リリースというCI/CDパイプラインにおいて異なるアプリケーションや異なる環境向けに再利用しやすい粒度で構成されるため、これをビルディングブロックとして扱うことで、複数のアプリケーションや複数の環境を扱う複雑なCI/CDパイプラインでも簡単に組み上げることができます。 汎用Taskを実装する際に、単一責任の原則を守りやすくなります。 汎化の層がTaskだけの場合は、あまりに細かな粒度だとPipeline側で扱いにくくなることからTaskの粒度を小さくすることを妨げる圧力がかかります。 Pipelineも汎化の層となることで、Taskの再利用性を高めることがPipelineの構成の容易性を高めることに繋がり、Taskの汎化の価値が高まります。 AssemblyLineにより、複数Pipelineからなるワークフローを形成できるため、前述のように複数Pipelineの実行を自動化するためのスクリプティングを行う必要がなくなります。 Pipelineと同様の宣言的記述でAssemblyLineを表現し、宣言的なCI/CDパイプラインというエクスペリエンスを一貫します。 Cloud Native Adapterをコンパイルして生成されるPipelineは、上述のとおり「1つのアプリケーションを1つの環境に対して、ビルド・デプロイ・リリースなどのまとまった処理のみ実行する」責務のみを有しています。 下の図で示すように、生成されたPipelineをビルディングブロックとして扱い、リリースされるまでのライフサイクルを構成する一連のAssemblyLineにおいて複数回使用できます。 AssemblyLine、Pipeline、Taskのそれぞれの機能と責務をまとめると、以下のとおりです。 なお補足ですが、TektonにはPipeline-in-Pipelineという拡張機能があり、これを用いることで、Tekton公式が提供する機能だけでPipelineよりも上位の層を追加できます。 このため、本節に記載した1つ目の理由だけであれば、Pipeline-in-Pipelineを使用するという選択肢もあります。 2. パラメータマッピングと注入を集約する AssemblyLine層が必要な2つ目の理由は、Pipelineに対してパラメータを渡すためにパラメータをマッピングおよび注入する責務をAssemblyLineが担っているためです。 Qmonus Value Streamでは、複数の層でパラメータマッピングをするのではなく、パラメータが供給される層とパラメータを注入する層をすべてAssemblyLineに限定することで、パラメータ設定の透明性と保守性を高めています ( 第6回 で詳しく説明していますので、詳細は割愛します)。 具体例を下図に示します。AssemblyLineのステージ定義において、アプリケーションと環境のペアから構成されるスコープ(例:AppA x Staging)と、ビルド用の汎用Pipelineを指定することで、「AppA x Staging」用に配置されたパラメータセットをビルド用のPipelineのパラメータインターフェースに対して注入できます。 このパラメータマッピング・注入の機構により、大量のパラメータを小さなスコープに分割して効率的に管理できます。 3. リトライ機能の提供 Tektonを汎用のワークフローエンジンとして実用していくことを考えると、致命的に足りない機能があります。 まず、Pipeline/Taskを失敗したところからリトライする機能がありません。 一時的な事由により失敗したケースを考慮して、指定回数に限り自動でリトライする機能はありますが、手動で再開させることができません。 また、Taskが失敗したときに、DAGを逆方向にたどりながら補償トランザクション(実行前の状態に切り戻す処理)を実行していくこともできません。 これらの機能がないことはすなわち、ワークフローエンジンが一般的に備えている分散トランザクション管理機能が備わっていないことに相当します。 Tektonは、主たる責務がCIの実行であるというドメイン要件から、大胆に割り切った設計となっており、上記の機能不足は問題になりません。 CIではミッションクリティカルなシステムを扱わないため、仮に失敗しても再度実行して成功すれば良いと割り切ることができ、トランザクションの考慮を放棄できます。 重要なのは「再実行できる」ことだけであり、失敗時の未開放リソースの開放や再実行を妨げる中間ファイルの破棄さえできれば十分です。 このケースに対応するために、TektonはFinally機能を具備しており、Taskが失敗した場合に「再実行できる」状態まで戻すための術を利用者に提供しています。 「失敗してもPipelineの先頭から再実行できる」ことさえ担保すれば良い、と割り切って考えると、Tektonは必要な機能を効率的に具備していると言えます。 「失敗したらPipelineの先頭から再実行」と割り切るTektonの思想は理に適っているように見えますが、実シーンを考えると困るケースが多々あります。 例えば、デプロイの際にデータベースのマイグレーションを伴うケースはどうでしょうか。 クリティカルなエラーが出た場合には当然切り戻す必要がありますが、たまたまネットワークが瞬断していたとか、軽微な構成誤りであり即座に修正してデプロイを再開できるケースにおいて、データベースを切り戻して再度先頭からやり直すことは避けたいでしょう。 また、ビルドなど時間がかかる処理を伴う場合も、全体を再度やり直すことは避けたいでしょう(ビルドについては、アーティファクトの有無で分岐したりキャッシュを活用する方法もあります)。 このように、Task単位でのリトライが選択できずPipeline単位でのリトライしかできないのは、ユーザのオペレーションの選択肢を減らし、利便性を損なうことに繋がります。 利用者目線で見ると、失敗したTaskの先頭から前回実行時の状態を引き継いで再開できる方が、Pipelineの先頭からやり直しになるよりも自然です。 また、リトライ時に前回実行時のパイプライン定義・パラメータ・内部状態を引き継くことができません。 Qmonus Value StreamのAssemblyLineはCD機能も提供しており、1つのAssemblyLineで複数リージョンに対してデプロイをするようなユースケースを想定しています。 複数リージョンに対してデプロイしている最中に、途中で失敗したケースを考えてみます。 失敗した原因を解析し解消できた場合は、リトライにより失敗した環境のデプロイから再開しますが、初回実行とリトライ実行のパイプラインの構成やパラメータが変わってしまうと、複数のリージョンに対して一貫したデプロイを行うことができません。 リトライ実行であっても、前回実行時のパイプライン定義・パラメータ・内部状態を引き継ぎ、同じ状態で処理を再開することが必要となります。 このリトライ機能とリトライ時に状態を引き継ぐ機能は、CDツールには欠かせない機能となっています。 複数環境への決定的かつ一貫したデプロイを実現するために必須の機能であり、 Google Cloud Deploy などパブリッククラウドから提供されるCDツールにも類似の機能を具備されています。 Tektonでは具備されていないTask単位でのリトライと、リトライ時に状態を引き継ぐ機能を、AssemblyLineによって補います。 これが、AssemblyLineが必要となる3つ目の理由です。 なお、本節の冒頭で言及したTektonに補償トランザクションの機能がないという設計については、CI/CDパイプラインの取りうる状態と複雑性を減らす観点から、AssemblyLineでも同じ方針を採用しています。 AssemblyLineの実現方式 Kubernetes カスタムオペレーターとして実装 AssemblyLineは、Kubernetesのカスタムリソースとして定義されています。 Kubernetesは、ユーザが独自でカスタムリソースを定義することで様々な機能拡張ができます。 TektonのPipelineやTaskもKubernetesのカスタムリソースです。 Kubernetesでは、Podなどの標準のリソースもしくはカスタムリソースを宣言的に定義することで、制御対象のシステムをデプロイし、宣言された状態になるまで自動的にデプロイ処理が繰り返されます。 現在の状態(Current State)が宣言されたあるべき状態(Desired State)に至るまで繰り返されるループ処理のことをReconciliation Loopと呼び、Kubernetes上で定義されるあらゆるリソースは、Reconciliation Loopによって制御されます。 ( Managing Kubernetes より) Tekton Pipelineは、このReconciliation Loopの機構を使ってDAGで定義されたTaskを先頭から順番に実行するつくりになっており、AssemblyLineでも同様の仕組みを採用しています。 Tekton PipelineがTaskを実行する処理とAssemblyLineがPipelineを実行する処理は、ロジックがかなり似ています。 AssemblyLineの設計・実装においてTekton Pipelineの設計やアプローチを参考にすることで、Reconciliation Loopを用いてワークフローエンジンを実装するTektonのノウハウを活かしています。 カスタムリソースを用いてReconciliation Loopによる制御を行うには、Kubernetesの標準リソースとは異なり、Reconciliation Loopで実行される処理を実装する必要があります。 この処理を行うプログラムはカスタムコントローラと呼ばれ、カスタムリソースの定義マニフェスト(Custom Resource Definition: CRD)とカスタムコントローラの2つをあわせてオペレーターと呼びます。 オペレーターを実装するにはいくつかの方法がありますが、現在では kubebuilder を利用するのが一般的であり、AssemblyLineオペレーターもkubebuilderを用いて実装しました。 kubebuilderではカスタムリソースを定義・拡張する専用のCLIがあり、CRDやマニフェストの自動生成機能も充実しています。 Reconciliation Loopのロジック開発に集中し、簡単にオペレーターを開発できますので、興味があれば一度オペレーターの開発にチャレンジしてみてください。 AssemblyLineを構成する3つのカスタムリソース 以下の図に、AssemblyLineオペレーターを構成する3つのカスタムリソースを示します。 比較のために、Tekton Pipelineオペレーターを構成するカスタムリソースも併記しています。 第5回 でも紹介したとおり、Tektonは、Pipelineリソースで実行対象のTaskやパラメータマッピングを宣言的に定義し、PipelineRunリソースを定義することでワークフローを走らせます。 定義と実行で異なるリソースを用いるため、処理の内容をPipelineリソースに事前に定義しておき、実行時にはパラメータだけを指定してPipelineRunを作成する、という分担が可能です。 PipelineRunに都度実行対象のワークフロー全体を記述する手間が省けます。 PipelineRunはワークフローエンジンとして振る舞うためにカスタムコントローラが必要ですが、Pipelineは実行対象のTaskと実行順序が定義されているだけであり、カスタムコントローラがありません。 Pipelineで定義されているTaskは、PipelineRunを作成することで、カスタムコントローラによって実行されます。 AssemblyLineにおいても同様に、定義と実行でリソースを分離した構成を採用しています。 AssemblyLineに実行対象のPipeline・実行順序・パラメータマッピングを定義しますが、AssemblyLineは実際の処理を駆動しないため、カスタムコントローラがありません。 AssemblyLineで定義されているPipelineは、AssemblyLineFactoryを作成することで、カスタムコントローラによって実行されます。 AssemblyLineオペレーターでは、AssemblyLineFactoryとAssemblyLineRunの2つのカスタムリソースを用いてPipelineを実行します。 Tekton PipelineオペレーターではPipelineRunだけがこの責務を担っているため、大きく構成が異なっています。 これには、記事の前半で説明したAssemblyLineが必要となる理由のうち、3つめの「リトライ機能」が関係しています。 AssemblyLineオペレーターは、AssemblyLineFactoryでAssemblyLineRunの実行状態を管理しつつ前回実行時の状態を保持することで、「リトライ機能」を実現しています。 PipelineRunの責務が「1回のPipeline実行」である一方、AssemblyLineにはリトライ機能があることから、AssemblyLineRunの責務は「1回のAssemblyLine実行における1回のトライ」になります。 ただし、前述のとおり、リトライをしても同じデプロイ結果に収束するためには、前回のトライのパイプライン定義・パラメータ・内部状態を引き継ぐ必要があり、それらを永続化する機能が必要です。 その機能を担うのが、AssemblyLineFactoryです。 AssemblyLineFactoryは、「1回のAssemblyLine実行における複数回のトライ」を責務として持ち、過去のトライの状態を永続化し、リトライ実行時にはリトライ用に加工されたAssemblyLineRunを1つ生成します。 このため、N回リトライを行った場合は、AssemblyLineFactoryは1つですが、AssemblyLineRunはN+1このリソースが生成されることになります。 AssemblyLineオペレーターとTekton Pipelineオペレーターの違いをまとめると、以下の3つの点において設計に差異があります。 リトライを可能にするためにAssemblyLineFactoryが追加されている リトライ用のAssemblyLineRunを生成するために、初回実行時に使用したマニフェスト・パラメータ・進捗状況を管理する責務を担っている AssemblyLineRunがリトライの都度別のリソースとして生成される このようにAssemblyLineオペレーターは、Tekton Pipelineオペレーターの実装を参考にしつつも、Qmonus Value Streamにおいて要求されるAssemblyLineの3つの機能、特にリトライ機能の実現のために様々な追加拡張を導入したことで生み出されました。 KubernetesのReconciliation Loopをワークフローエンジンに応用するというTektonの発想はとても面白く、手続きアプローチで実装したワークフローエンジンに比べてエラーに対する頑強性が高いというメリットもあります。 しかしながら、いざAssemblyLineオペレーターを実装してみると、その時々のリソース状態に応じて次に実行する内容が変わるために条件分岐が増えてしまう問題、状態のパターン網羅が出来ておらず考慮外の状態に陥ってしまう問題、テストが難しい問題などにより、難易度の高い開発となりました。 これらの詳細については、また別の記事で改めて紹介できればと考えています。 おわりに DevOpsプラットフォームの取り組み連載の7回目の記事として、Qmonus Value Stream中核を担うコンポーネントであるAssemblyLineについて深堀りしました。 AssemblyLineには大きく3つの責務があること、Pipelineを参考にKubernetesのカスタムリソースとして実装されていることを紹介しました。 Qmonus Value Streamチームは、メンバ一人一人が、利用者であるアプリケーション開発者のために信念を持って活動を続けています。 プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで 、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 CI/CDプラットフォーム開発、ソフトウェアエンジニア NTTグループのクラウド基盤構築およびCI/CD導入、ソリューションアーキテクト 次回は、Cloud Native AdapterによるPipeline/Task生成機能について踏み込んでご紹介します。 お楽しみに!
サマリ SR-MPLS で構成されたネットワークにおいて、Per-Flow Steering を実現 IOS XR + Junos の Multi-vendor 環境で、5-tuple と IP Precedence のそれぞれを条件とした Traffic Engineering (TE) の検証に成功 この記事は Multi-AS Segment Routing 検証連載の第 8 回です。目次は こちら 概要 イノベーションセンターの三島です。本記事では Per-Flow での SR Policy 適用手法を紹介します。 SR Policy の適用については、これまでの連載記事にて宛先 prefix ごとの TE を実現する例を紹介しました。 より多様なユースケースを実現するためには prefix 単位の経路制御に加えて、アプリケーション単位や送信元単位など、様々な経路制御の実現が必要となります。 本記事ではそのような prefix 単位以外での SR Policy の適用方法を紹介します。 SR Policy の定義と適用方法 第 4 回記事 でも説明しました通り SR を用いた TE (SR-TE) の実現には SR Policy を利用します。 SR Policy は、2022 年 7 月に RFC9256 として発行されています。 この RFC では、SR Policy はどのノードへ送るかを指定する Endpoint、経路を指定する Segment List、属性である Color の 3 つの要素から構成されると定義されています。 今回のテーマである SR Policy の適用方法については、RFC9256 の 8 章にて下記が紹介されています。(一部本記事用に表現を編集) Local BSID Matching パケットへ付与された BSID に基づいて対応する SR Policy を適用する手法 Per-Destination Steering 宛先の経路に基づいて SR Policy を適用する手法 宛先の単位としては BGP で広告された prefix 単位、VRF 単位、広告元の Endpoint 単位が使用可能 SR Policy の自動インスタンス化が可能で、 第 7 回 で紹介した On-Demand Next-Hop(ODN) を使用可能 Per-Flow Steering パケットが持つ宛先以上の情報に基づいて SR Policy を適用する手法 ingress PE の ingress interface にて match 条件を評価、パケット識別のための Forwarding Class へ分類し、Class ごとに対応する SR Policy を選択 各レイヤーの条件としては次のような値が使用可能 L2 (Ethernet): destination/source/VLAN/CoS L3 (IP): destination/source/ToS (Differentiated Services Code Point (DSCP) / IP Precedence) L4 (transport): destination/source port Policy-Based Steering ルーティングポリシーに基づいて SR Policy を適用する手法 Policy-Based Routing の一種として実装 今回の記事では Per-Flow Steering の 実現例として、 5-tuple を基にした TE と IP Precedence を基にした TE をそれぞれ検証し紹介します。 検証 本章では、あるパケットを 5-tuple、IP Precedence のそれぞれで match させ、SR Policy を適用する例を検証します。 検証環境は下記の通りです。 事前準備 IOS XR では Per-Flow Steering を適用する BGP 経路に対して特定の Color をつける必要があります。 今回は、IBGP で対向の PE から受け取る全ての経路に color 1 を付与します。 extcommunity-set opaque COLOR-1 1 end-set route-policy COLOR-1-POLICY set extcommunity color COLOR-1 end-policy router bgp 65001 neighbor-group ibgp address-family vpnv4 unicast route-policy COLOR-1-POLICY in exit 5-tuple で match させる例 まずは、5-tuple で match したパケットのみ TE を適用する例を紹介します。 本章では以下の条件で match させ、SR Policy を適用していきます。 host1 -> host2: 送信先アドレスが 192.168.1.1 かつ、送信先ポートが 80 番 host2 -> host1: 送信先アドレスが 192.168.0.1 かつ、送信先ポートが 80 番 また、 match したパケットは下記の経路で転送されます。その他のパケットは IGP メトリックに従って転送されます。 パケットの分類 まず PE1、PE2 にて上記で指定した条件に一致するパケットを Forwarding Class に分類するための設定をします。 PE1(IOS XR) 5-tuple を定義した ACL を match 条件 とする class-map を定義します。 ipv4 access-list TCP-ANY-ANY-192.168.1.1-80 1 permit tcp any host 192.168.1.1 eq 80 ! class-map type traffic match-all 5TUPLE-CLASSMAP match access-group ipv4 TCP-ANY-ANY-192.168.1.1-80 end-class-map class-map と forward-class を対応させる policy-map を定義し、customer 向けの interface に attach します。 interface TenGigE0/0/0/33.100 service-policy type pbr input 5TUPLE-POLICYMAP ! policy-map type pbr 5TUPLE-POLICYMAP class type traffic 5TUPLE-CLASSMAP set forward-class 1 ! class type traffic class-default ! end-policy-map 作成した class-map と policy-map を確認します。 RP/0/RSP0/CPU0:PE1#show class-map type traffic 5TUPLE-CLASSMAP Thu Sep 8 13:33:34.767 JST 1) ClassMap: 5TUPLE-CLASSMAP Type: traffic Referenced by 1 Policymaps RP/0/RSP0/CPU0:PE1#show policy-map type pbr pmap-name 5TUPLE-POLICYMAP detail Thu Sep 8 14:24:51.531 JST ipv4 access-list TCP-ANY-ANY-192.168.1.1-80 1 permit tcp any host 192.168.1.1 eq www class-map type traffic match-all 5TUPLE-CLASSMAP match access-group ipv4 TCP-ANY-ANY-192.168.1.1-80 end-class-map ! policy-map type pbr 5TUPLE-POLICYMAP class type traffic 5TUPLE-CLASSMAP set forward-class 1 ! class type traffic class-default ! end-policy-map ! PE2(Junos) Multifield Classifier の firewall filter を設定します。 set firewall family inet filter 5TUPLE term TCP-ANY-ANY-192.168.0.1-80 from destination-address 192.168.0.1/32 set firewall family inet filter 5TUPLE term TCP-ANY-ANY-192.168.0.1-80 from protocol tcp set firewall family inet filter 5TUPLE term TCP-ANY-ANY-192.168.0.1-80 then count 5TUPLE-COUNT set firewall family inet filter 5TUPLE term TCP-ANY-ANY-192.168.0.1-80 then forwarding-class assured-forwarding set firewall family inet filter 5TUPLE term TCP-ANY-ANY-192.168.0.1-80 then accept set firewall family inet filter 5TUPLE term DEFAULT from protocol-except tcp set firewall family inet filter 5TUPLE term DEFAULT then count DEFAULT-COUNT set firewall family inet filter 5TUPLE term DEFAULT then forwarding-class best-effort set firewall family inet filter 5TUPLE term DEFAULT then accept set interfaces xe-0/1/7 unit 100 family inet filter input 5TUPLE 作成した firewall filter を確認します。 user@PE2> show firewall filter 5TUPLE Filter: 5TUPLE Counters: Name Bytes Packets 5TUPLE-COUNT 0 0 DEFAULT-COUNT 0 0 SR Policy の定義 作成した Forwarding Class に対応する SR Policy を定義します。 PE1(IOS XR) Explicit-Path と SR Policy を定義します。 5-tuple 条件と一致した場合の TE パスに加え、デフォルトの TE パスも定義する必要があります。 IOS XR においては、Forwarding Class ごとに分類されたパケットへの TE の適用は policy 5TUPLE-MATCH のように SR Policy を用いて行います。 segment-routing traffic-eng segment-list 5TUPLE-TE index 1 mpls label 16003 index 2 mpls label 16004 index 3 mpls label 16002 ! policy FC-1 color 101 end-point ipv4 10.255.1.2 candidate-paths preference 100 explicit segment-list 5TUPLE-TE ! ! ! ! policy DEFAULT color 100 end-point ipv4 10.255.1.2 candidate-paths preference 100 dynamic metric type igp ! ! ! ! ! policy 5TUPLE-MATCH color 1 end-point ipv4 10.255.1.2 candidate-paths preference 100 per-flow forward-class 0 color 100 forward-class 1 color 101 forward-class default 0 作成した SR Policy を確認します。 RP/0/RSP0/CPU0:PE1# show segment-routing traffic-eng policy Thu Sep 8 14:27:40.143 JST SR-TE policy database --------------------- Color: 101, End-point: 10.255.1.2 Name: srte_c_101_ep_10.255.1.2 Status: Admin: up Operational: up for 3d02h (since Sep 5 18:15:54.714) Candidate-paths: Preference: 100 (configuration) (active) Name: FC-1 Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Explicit: segment-list 5TUPLE-TE (valid) Weight: 1, Metric Type: TE 16003 [Prefix-SID, 10.255.1.5] 16004 16002 Attributes: Binding SID: 24023 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 Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 3d01h (since Sep 5 18:49:30.081) Candidate-paths: Preference: 100 (configuration) (active) Name: DEFAULT Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (valid) Metric Type: IGP, Path Accumulated Metric: 10 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24025 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 Color: 1, End-point: 10.255.1.2 Name: srte_c_1_ep_10.255.1.2 Status: Admin: up Operational: up for 00:10:21 (since Sep 8 20:17:19.421) Candidate-paths: Preference: 100 (configuration) (active) Name: 5TUPLE-MATCH Requested BSID: dynamic Per-flow Information: Forward PDP PDP Class Color Status ------- ------- ------- 0 100 Up 1 101 Up Default Forward Class: 0 Attributes: Binding SID: 24006 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 PE2(Junos) Explicit-Path と SR Policy を定義します。 5-tuple 条件と一致した場合の TE パスに加え、デフォルトの TE パスも定義する必要があります。 Junos においては、Forwarding Class ごとに分類されたパケットへの TE の適用は 5TUPLE-FORWARDING などのように routing-options を用いて行います。 set protocols source-packet-routing segment-list 5TUPLE-TE P2 label 16004 set protocols source-packet-routing segment-list 5TUPLE-TE P1 label 16003 set protocols source-packet-routing segment-list 5TUPLE-TE PE1 label 16001 set protocols source-packet-routing source-routing-path FC-1 to 10.255.1.1 set protocols source-packet-routing source-routing-path FC-1 primary 5TUPLE-TE set protocols source-packet-routing compute-profile DYNAMIC-IGP metric-type igp set protocols source-packet-routing source-routing-path DEFAULT to 10.255.1.1 set protocols source-packet-routing source-routing-path DEFAULT primary odn_path compute DYNAMIC-IGP set class-of-service forwarding-policy next-hop-map 5TUPLE-POLICYMAP forwarding-class best-effort lsp-next-hop DEFAULT set class-of-service forwarding-policy next-hop-map 5TUPLE-POLICYMAP forwarding-class assured-forwarding lsp-next-hop FC-1 set policy-options policy-statement 5TUPLE-FORWARDING then cos-next-hop-map 5TUPLE-POLICYMAP set routing-options forwarding-table export 5TUPLE-FORWARDING 作成した SR Policy を確認します。 user@PE2> show spring-traffic-engineering lsp detail Name: DEFAULT Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.1.1 State: Up Path: odn_path Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Enabled , Compute Result:success , Compute-Profile Name:DYNAMIC-IGP Total number of computed paths: 1 Computed-path-index: 1 BFD status: N/A BFD name: N/A TE metric: 10, IGP metric: 10 Delay metrics: Min: 16777215, Max: 16777215, Avg: 16777215 Metric optimized by type: IGP computed segments count: 1 computed segment : 1 (computed-node-segment): node segment label: 16001 router-id: 10.255.1.1 Name: FC-1 Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.1.1 State: Up Path: 5TUPLE-TE Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Disabled , Compute Result:N/A , Compute-Profile Name: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: None SID type: 20-bit label, Value: 16004 Hop 2 (Strict): NAI: None SID type: 20-bit label, Value: 16003 Hop 3 (Strict): NAI: None SID type: 20-bit label, Value: 16001 疎通確認 mtr を用いて経路を確認します。 host1 user@host1:~$ mtr -n -e 192.168.1.1 host1 (192.168.0.1) 2022-09-08T23:33:23+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.0.254 0.0% 5 1.5 1.4 1.3 1.5 0.1 2. 192.168.1.254 0.0% 4 0.3 0.4 0.3 0.5 0.1 3. 192.168.1.1 0.0% 4 0.6 0.5 0.4 0.6 0.1 user@host1:~$ mtr -n -e -T -P 80 192.168.1.1 host1 (192.168.0.1) 2022-09-08T23:30:58+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.0.254 0.0% 5 1.5 1.5 1.3 1.6 0.1 2. 10.1.1.2 0.0% 5 1.9 2.8 1.9 3.5 0.6 [MPLS: Lbl 16004 TC 0 S 0 TTL 1] [MPLS: Lbl 16002 TC 0 S 0 TTL 1] [MPLS: Lbl 123 TC 0 S 1 TTL 1] 3. (waiting for reply) 4. 192.168.1.254 0.0% 5 0.7 2.3 0.7 8.6 3.5 5. 192.168.1.1 0.0% 5 0.5 0.5 0.4 0.6 0.1 host2 user@host2:~$ mtr -n 192.168.0.1 host2 (192.168.1.1) 2022-09-08T23:36:14+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.1.254 0.0% 5 0.5 0.5 0.3 0.6 0.1 2. 10.1.2.1 0.0% 5 1.4 1.5 1.4 1.6 0.1 3. 192.168.0.1 0.0% 5 0.4 0.5 0.4 0.5 0.0 user@host2:~$ mtr -n -T -P 80 192.168.0.1 host2 (192.168.1.1) 2022-09-08T23:35:22+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.1.254 0.0% 7 0.6 1.9 0.3 10.0 3.6 2. (waiting for reply) 3. 10.1.4.1 0.0% 7 3.1 3.1 2.0 3.9 0.6 [MPLS: Lbl 16001 TC 0 S 0 TTL 1] [MPLS: Lbl 24002 TC 0 S 1 TTL 2] 4. 10.1.1.1 0.0% 7 1.5 1.5 1.3 1.6 0.1 5. 192.168.0.1 0.0% 7 0.4 0.5 0.4 0.5 0.1 IP Precedence で match させる例 IP Precedence で match したパケットのみ TE を適用する例を紹介します。 本章では host1 と host2 間にていずれの方向にも IP Precedence == 1 で match させ、SR Policy を適用していきます。 match したパケットは下記の経路で転送されます。その他のパケットは IGP メトリックに従って転送されます。 なお、一部設定は前章での 5-tupple による TE の設定と競合するため、検証をなぞる際は別環境で行うか前章での設定を削除してから行ってください。 パケットの分類 まず PE1、PE2 にて上記で指定した条件に一致するパケットを Forwarding Class に分類するための設定をします。 PE1(IOS XR) IP Precedence を match 条件 とする class-map を定義します。 class-map type traffic match-all IP-PRECEDENCE1 match precedence 1 end-class-map class-map と forward-class を対応させる policy-map を定義し、customer 向けの interface に attach します。 policy-map type pbr PRECEDENCE1-POLICYMAP class type traffic IP-PRECEDENCE1 set forward-class 1 ! class type traffic class-default ! end-policy-map interface TenGigE0/0/0/33.100 service-policy type pbr input PRECEDENCE1-POLICYMAP 作成した class-map と policy-map を確認します。 RP/0/RSP0/CPU0:PE1#show class-map type traffic IP-PRECEDENCE1 Thu Sep 8 14:31:23.599 JST 1) ClassMap: IP-PRECEDENCE1 Type: traffic Referenced by 0 Policymaps RP/0/RSP0/CPU0:PE1#show policy-map type pbr pmap-name PRECEDENCE1-POLICYMAP detail Thu Sep 8 14:41:12.876 JST class-map type traffic match-all IP-PRECEDENCE1 match precedence 1 end-class-map ! policy-map type pbr PRECEDENCE1-POLICYMAP class type traffic IP-PRECEDENCE1 set forward-class 1 ! class type traffic class-default ! end-policy-map ! PE2(Junos) Multifield Classifier の firewall filter を設定します。 set firewall family inet filter IP-PRECEDENCE1 term 1 from precedence 1 set firewall family inet filter IP-PRECEDENCE1 term 1 then count IP-PRECEDENCE1-COUNT set firewall family inet filter IP-PRECEDENCE1 term 1 then forwarding-class assured-forwarding set firewall family inet filter IP-PRECEDENCE1 term 1 then accept set firewall family inet filter IP-PRECEDENCE1 term 2 from precedence-except 1 set firewall family inet filter IP-PRECEDENCE1 term 2 then count NOT-IP-PRECEDENCE1 set firewall family inet filter IP-PRECEDENCE1 term 2 then forwarding-class best-effort set firewall family inet filter IP-PRECEDENCE1 term 2 then accept set interfaces xe-0/1/7 unit 100 family inet filter input IP-PRECEDENCE1 作成した firewall filter を確認します。 user@PE2> show firewall filter IP-PRECEDENCE1 Filter: IP-PRECEDENCE1 Counters: Name Bytes Packets IP-PRECEDENCE1-COUNT 0 0 NOT-IP-PRECEDENCE1 0 0 SR Policy の定義 作成した Forwarding Class に対応する SR Policy を定義します。 PE1(IOS XR) Explicit-Path と SR Policy を定義します。 segment-routing traffic-eng segment-list IP-PRECEDENCE-TE index 2 mpls label 16003 index 3 mpls label 16002 ! policy FC-1 color 101 end-point ipv4 10.255.1.2 candidate-paths preference 100 explicit segment-list IP-PRECEDENCE-TE ! ! ! ! policy DEFAULT color 100 end-point ipv4 10.255.1.2 candidate-paths preference 100 dynamic metric type igp ! ! ! ! ! policy IP-PRECEDENCE-MATCH color 1 end-point ipv4 10.255.1.2 candidate-paths preference 100 per-flow forward-class 0 color 100 forward-class 1 color 101 forward-class default 0 作成した SR Policy を確認します。 RP/0/RSP0/CPU0:PE1#show segment-routing traffic-eng policy detail Fri Sep 9 08:44:11.638 JST SR-TE policy database --------------------- Color: 101, End-point: 10.255.1.2 Name: srte_c_101_ep_10.255.1.2 Status: Admin: up Operational: up for 3d14h (since Sep 5 18:15:54.714) Candidate-paths: Preference: 100 (configuration) (active) (reoptimizing) Name: FC-1 Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Explicit: segment-list IP-PRECEDENCE-TE (valid) Weight: 1, Metric Type: TE 16003 [Prefix-SID, 10.255.1.5] 16002 LSPs: LSP[0]: LSP-ID: 2 policy ID: 2 (active) Local label: 24022 State: Programmed Binding SID: 24023 LSP[1]: LSP-ID: 3 policy ID: 2 (reoptimized) Local label: 24004 State: Install timer pending Attributes: Binding SID: 24023 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 Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 3d13h (since Sep 5 18:49:30.081) Candidate-paths: Preference: 100 (configuration) (active) Name: DEFAULT Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (valid) Metric Type: IGP, Path Accumulated Metric: 10 16002 [Prefix-SID, 10.255.1.2] LSPs: LSP[0]: LSP-ID: 2 policy ID: 4 (active) Local label: 24024 State: Programmed Binding SID: 24025 Attributes: Binding SID: 24025 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 Color: 1, End-point: 10.255.1.2 Name: srte_c_1_ep_10.255.1.2 Status: Admin: up Operational: up for 00:00:03 (since Sep 9 08:44:08.469) Candidate-paths: Preference: 100 (configuration) (active) Name: IP-PRECEDENCE-MATCH Requested BSID: dynamic Per-flow Information: Forward PDP PDP Class Color Status ------- ------- ------- 0 100 Up 1 101 Up Default Forward Class: 0 LSPs: LSP[0]: LSP-ID: 4 policy ID: 5 (active) State: Programmed Binding SID: 24014 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 Max Install Standby Candidate Paths: 0 PE2(Junos) Explicit-Path と SR Policy を定義します。 set protocols source-packet-routing segment-list IP-PRECEDENCE-TE P1 label 16003 set protocols source-packet-routing segment-list IP-PRECEDENCE-TE PE1 label 16001 set protocols source-packet-routing source-routing-path FC-1 to 10.255.1.1 set protocols source-packet-routing source-routing-path FC-1 primary IP-PRECEDENCE-TE set protocols source-packet-routing compute-profile DYNAMIC-IGP metric-type igp set protocols source-packet-routing source-routing-path DEFAULT to 10.255.1.1 set protocols source-packet-routing source-routing-path DEFAULT primary odn_path compute DYNAMIC-IGP set class-of-service forwarding-policy next-hop-map IP-PRECEDENCE-POLICYMAP forwarding-class best-effort lsp-next-hop DEFAULT set class-of-service forwarding-policy next-hop-map IP-PRECEDENCE-POLICYMAP forwarding-class assured-forwarding lsp-next-hop FC-1 set policy-options policy-statement IP-PRECEDENCE-FORWARDING then cos-next-hop-map IP-PRECEDENCE-POLICYMAP set routing-options forwarding-table export IP-PRECEDENCE-FORWARDING 作成した SR Policy を確認します。 user@PE2> show spring-traffic-engineering lsp detail Name: DEFAULT Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.1.1 State: Up Path: odn_path Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Enabled , Compute Result:success , Compute-Profile Name:DYNAMIC-IGP Total number of computed paths: 1 Computed-path-index: 1 BFD status: N/A BFD name: N/A TE metric: 10, IGP metric: 10 Delay metrics: Min: 16777215, Max: 16777215, Avg: 16777215 Metric optimized by type: IGP computed segments count: 1 computed segment : 1 (computed-node-segment): node segment label: 16001 router-id: 10.255.1.1 Name: FC-1 Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.1.1 State: Up Path: IP-PRECEDENCE-TE Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Disabled , Compute Result:N/A , Compute-Profile Name:N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 2 Hop 1 (Strict): NAI: None SID type: 20-bit label, Value: 16003 Hop 2 (Strict): NAI: None SID type: 20-bit label, Value: 16001 疎通確認 mtr を用いて経路を確認します。 host1 通常の ICMP パケットは最短経路を通ります。 user@host1:~$ mtr -n -e 192.168.1.1 host1 (192.168.0.1) 2022-09-08T23:56:49+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.0.254 0.0% 6 1.4 1.4 1.3 1.5 0.1 2. 192.168.1.254 0.0% 5 0.5 0.5 0.3 0.6 0.1 3. 192.168.1.1 0.0% 5 0.4 0.4 0.4 0.5 0.0 IP Precedence が 1(ToS が 32)のパケットは想定通りに TE が適用されています。 user@host1:~$ mtr -n -e -Q 32 192.168.1.1 host1 (192.168.0.1) 2022-09-09T00:03:10+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.0.254 0.0% 5 1.5 1.5 1.4 1.6 0.1 2. 10.1.1.2 0.0% 4 2.7 2.8 2.0 3.7 0.7 [MPLS: Lbl 16002 TC 1 S 0 TTL 1] [MPLS: Lbl 123 TC 1 S 1 TTL 1] 3. 192.168.1.254 0.0% 4 0.4 0.5 0.4 0.6 0.1 4. 192.168.1.1 0.0% 4 0.5 0.5 0.4 0.5 0.0 host2 通常の ICMP パケットは最短経路を通ります。 user@host2:~$ mtr -n 192.168.0.1 host2 (192.168.1.1) 2022-09-09T00:03:41+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.1.254 0.0% 6 10.8 8.0 0.4 13.7 6.0 2. 10.1.2.1 0.0% 5 1.6 1.6 1.4 1.7 0.1 3. 192.168.0.1 0.0% 5 0.4 0.5 0.4 0.5 0.0 IP Precedence が 1(ToS が 32)のパケットは想定通りに TE が適用されています。 user@host2:~$ mtr -n -T -P 80 192.168.0.1 host2 (192.168.1.1) 2022-09-09T00:04:23+0000 Keys: Help Display mode Restart statistics Order of fields quit Packets Pings Host Loss% Snt Last Avg Best Wrst StDev 1. 192.168.1.254 0.0% 9 0.5 0.4 0.4 0.5 0.1 2. 10.1.3.2 0.0% 9 2.8 2.9 1.9 3.5 0.5 [MPLS: Lbl 16001 TC 1 S 0 TTL 1] [MPLS: Lbl 24002 TC 1 S 1 TTL 1] 3. 10.1.1.1 0.0% 8 1.5 1.5 1.4 1.6 0.1 4. 192.168.0.1 0.0% 8 0.5 0.5 0.5 0.6 0.0 まとめ 本記事では、SR Policy の適用手法をまとめることで宛先 prefix よりも細かい単位での経路制御を紹介しました。 検証では例として 5-tuple と IP Precedence のそれぞれを match 条件として実装しましたが、実運用ではユースケースに応じて適切な match 条件を選択してください。 次回の記事では TI-LFA を利用した 高速迂回(FastReRoute)について紹介予定です。 (2022/10/03 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #9] TI-LFA を用いた障害時の高速迂回
はじめに イノベーションセンター Network Analytics for Security (以下、NA4Sec) プロジェクトリーダーの神田です。 NA4Secチームでは兄弟プロジェクトである Metemcyber ( @Metemcyber ) *1 と連携して、サイバー脅威インテリジェンス(以下、脅威インテリジェンス) *2 の配信をはじめました。 本記事ではこの取り組みについて、発案者であるチームメンバーの @strinsert1Na (以下、strinsert1Na) との対談も交えながら紹介します。 NA4Secプロジェクトとは? NA4Secプロジェクトは「NTTはインターネットを安心、安全にする社会的責務がある」を理念として、攻撃インフラの解明、撲滅を目指すプロジェクトです。 NTT Com イノベーションセンターのみならずNTTセキュリティ・ジャパンやエヌ・エフ・ラボラトリーズ(以下、NFLabs. )からもメンバーが参画し、日夜攻撃インフラを追跡しています。 また、NTT Comグループにおける脅威インテリジェンスチームとしての側面も持ち合わせており、有事において脅威インテリジェンスを提供し、意思決定を支援することもあります。 一例としてアクセスログ解析サービス Visionalist で使用していたドメインが第三者によりドロップキャッチされた際には、不審なスクリプトが配置されたインフラを分析し、リスクを評価するとともに対応を提言しました。 *3 脅威インテリジェンス配信はじめました 今年度に入ってNFLabs. からマルウェアの静的解析を得意とする strinsert1Na がチームに参画しました。 ここからは脅威インテリジェンス配信をはじめた経緯やその狙いについて発案者 strinsert1Na との対談形式で紹介します。 経緯 神田(以下、kanda): 今年度からNA4Secチームに参画してもらったわけですが、もともと脅威インテリジェンス発信には興味があったのですか? strinsert1Na: もともと個人の趣味で標的型攻撃の検体を収集したり解析したりしていて、情報発信もしていました。 しかし、マルウェア解析はすごくリソースが必要な行為だし多くの企業ができるものでもない。脅威インテリジェンスサービスもすぐにIoC(Indicator of Compromise) *4 を公開するとは限らない。 公開情報から得られたマルウェアのIoCを信頼できる場所を使って発信できないだろうか、と考えていました。 kanda: そんなときに隣のMetemcyberチームがTwitterアカウントを使って発信する情報を探していることを知って、これはちょうどいいぞ、と。 strinsert1Na: はい(笑)。脅威インテリジェンス関連を扱っているプロジェクトだし、「そのまま脅威インテリジェンスを発信させていただきますかー」程度のノリでやってみることにしました。 狙い kanda: 脅威インテリジェンスを発信するにあたっての思いや狙いについて聞かせてください。 strinsert1Na: 私は「日本に関連する標的型攻撃を継続的にウォッチするアカウントが欲しいなぁ」と前々から思っていたので、この理想を自分で作ってみようかなと思いました。 標的型攻撃は時々刻々と手口が変化するため、「一度レポートを読んで終わり」ではなく継続的な観測と対策が必要不可欠だと私は考えています。しかし、これらの関連情報やIoCは初報以降、継続的に公開されることは多くなく、海外の脅威インテリジェンスサービスが日本関連の脅威インテリジェンスをリアルタイムに生成することも少ないです。 なので、本Twitterアカウントを監視するだけでなるべく多くの人が標的型攻撃のIoCをリアルタイムで観測し、具体的な防御に活かせるようになれば...というのが狙いです。 kanda: ないなら自らやってやろうというわけですね。情報発信するにあたって何か意識したことはありますか? strinsert1Na: 一般人が流し読むような”ポエム”ではなく、現場のエンジニアがインシデントハンドリング/スレットハンティング *5 で活用できるような”戦術(Tactical)脅威インテリジェンス”の側面から価値を提供することを一番意識しています。 なので、マルウェア/インテリジェンスアナリストがTweetを見ただけで脅威インテリジェンスの真偽を確認できて、IoCを収集できるような投稿にすることを心がけました。 本Twitterアカウントの情報を自動収集することで、即座に防御に対する意思決定に貢献できれば嬉しいなと思っています。 実際に情報発信してみて kanda: 実際に発信してみてどうでした? strinsert1Na: 「脅威インテリジェンス発信と言ってもTwitterに投稿するだけでしょ?」と軽く考えていましたが、使いやすいフォーマットやぱっと見でのわかりやすさ、140字でのまとめ方など創意工夫がかなり必要でした。 SHA256ハッシュを載せたいけど載せるとハッシュ値とC2の情報だけで140文字消化してしまうし、IoCとして有効かをきちんと判断できる証拠画像も欲しいし。 結果として絵文字を使ってビジュアルに表現しながら文字をなるべく削り、IoCベースですぐに脅威インテリジェンスとして消費できる形にまとめました。 自分の人生で最も1Tweetに時間がかかりましたが、納得のできる形になったのではないかと思っています。 🚨⚡ #LODEINFO (🇯🇵) C2: 🌍 http[:]//172.104.72[.]4/ (ASN: AS63949, ISP: Linode, LLC 🇯🇵) and 2 more (172.105.223[.]216, 45.77.28[.]124). Family: 🦠 LODEINFO (v0.6.2) MD5: 🔒 da1c9006b493d7e95db4d354c5f0e99f Sample: 📄 https://t.co/MvDcBjsfwV H/T to NA4Sec Team pic.twitter.com/YUhX1WCrWH — Metemcyber (@Metemcyber) 2022年8月5日 kanda: 情報発信後の反響や手応えは? strinsert1Na: 発信した情報をプロに拾ってもらってより詳細なアーティファクト分析 *6 の提供につながった事例が早速ありました。まだ2Tweetしかしていませんが、再現性のある脅威インテリジェンスはきちんと世の中の役に立っているのだということが実感できました。 Nice share! 🦠 HUI Loader (d839ab2c2ea8f082f98478cb552d6709) ☠️Create a service name "WxUpdateServiceInfo" ->spawn thread for load system.ini-> decoding sc and inject decoded sc into svchost.exe (1/4) https://t.co/zs3buBPm3e pic.twitter.com/yrJx99ePpF — m4n0w4r (@kienbigmummy) 2022年8月25日 今後の意気込み kanda: 最後に今後の意気込みをぜひ。 strinsert1Na: これからも公開情報をもとに継続的に観測を続け、IoCを発信していくつもりです。 JPCERT/CCやベンダのレポートからハンティングに使うルールを日々アップデートしながら、今後も最新の情報を追えるように努力していきます。 また、Twittterに載せる脅威インテリジェンスはなるべく自動収集できる形にできればいいなと考えていて、今後も決まったフォーマットで発信するつもりです。 みなさんの意見を取り入れつつ、改善してより使いやすい形にしていきたいと思っているので、公式アカウント( @Metemcyber )や私のアカウント( @strinsert1Na )にReplyでフィードバックをいただけると助かります。 おわりに 本記事では、NA4SecチームとMetemcyberチームで連携した脅威インテリジェンス発信について紹介しました。 今後も様々な脅威インテリジェンスを発信していく予定です。ご期待ください。 また、発信内容やフォーマットについてのフィードバックも大歓迎です。 ぜひ忌憚のないご意見をお聞かせください。 (さらに公式アカウント @Metemcyber をフォローしていただけると関係メンバーみな泣いて喜びます) おまけ NA4Sec/Metemcyberチームではそれぞれ一緒に働く仲間を募集しています。 「脅威インテリジェンス」をキーワードに活躍の場を探している方、プロジェクトの理念に共感していただける方のご応募をお待ちしています。 Threat Intelligence Analyst / 脅威インテリジェンスアナリスト (NA4Sec) Threat Intelligence Engineer / 脅威インテリジェンスエンジニア (Metemcyber) NFLabs. ではオフェンシブセキュリティを中心としたエンジニア・マネージャーを募集中です。 また、Twitterやエンジニアブログでも情報を発信していますのでぜひ一度ご覧ください。 NFLabs. RECRUIT NFLabs. 公式Twitterアカウント( @NFLaboratories ) NFLabs. エンジニアブログ *7 *1 : Metemcyberについては こちらのブログ記事 でも紹介しています *2 : サイバー脅威インテリジェンス/Cyber Threat Intellgence(CTI):広義には「サイバー攻撃に関する情報を収集して整理・分析したもの」のこと。詳細は こちらの記事 でも解説しています *3 : Visionalistの件については 別のブログ記事 を公開していますので、詳細が気になる方はそちらをご覧ください *4 : IoC(Indicator of Compromise):IP・ドメイン・URL・ファイルハッシュなど、サイバー攻撃にみられる直接的な痕跡情報のこと *5 : スレットハンティング/Threat Hunting:自組織に未検知の脅威が侵入している可能性を前提に、能動的に脅威を探索すること *6 : アーティファクト分析/Artifact Analysis:コンピュータセキュリティインシデントに関わるアーティファクト(マルウエアや攻撃ツール、およびその関連技術)に関して、その実態や対策に関する技術的な調査、分析を行うこと(「 はじめてのJPCERT/CC 」より) *7 : strinsert1NaはNFLabs. エンジニアブログにも記事を寄稿しています → 「無名のセキュリティエンジニアがたった2本のブログ記事からSoftware Designで連載をすることになった( 非技術編 、 技術編 )」
はじめに イノベーションセンター Network Analytics for Security (以下、NA4Sec) プロジェクトリーダーの神田です。 NA4Secチームでは兄弟プロジェクトである Metemcyber ( @Metemcyber ) *1 と連携して、サイバー脅威インテリジェンス(以下、脅威インテリジェンス) *2 の配信をはじめました。 本記事ではこの取り組みについて、発案者であるチームメンバーの @strinsert1Na (以下、strinsert1Na) との対談も交えながら紹介します。 NA4Secプロジェクトとは? NA4Secプロジェクトは「NTTはインターネットを安心、安全にする社会的責務がある」を理念として、攻撃インフラの解明、撲滅を目指すプロジェクトです。 NTT Com イノベーションセンターのみならずNTTセキュリティ・ジャパンやエヌ・エフ・ラボラトリーズ(以下、NFLabs. )からもメンバーが参画し、日夜攻撃インフラを追跡しています。 また、NTT Comグループにおける脅威インテリジェンスチームとしての側面も持ち合わせており、有事において脅威インテリジェンスを提供し、意思決定を支援することもあります。 一例としてアクセスログ解析サービス Visionalist で使用していたドメインが第三者によりドロップキャッチされた際には、不審なスクリプトが配置されたインフラを分析し、リスクを評価するとともに対応を提言しました。 *3 脅威インテリジェンス配信はじめました 今年度に入ってNFLabs. からマルウェアの静的解析を得意とする strinsert1Na がチームに参画しました。 ここからは脅威インテリジェンス配信をはじめた経緯やその狙いについて発案者 strinsert1Na との対談形式で紹介します。 経緯 神田(以下、kanda): 今年度からNA4Secチームに参画してもらったわけですが、もともと脅威インテリジェンス発信には興味があったのですか? strinsert1Na: もともと個人の趣味で標的型攻撃の検体を収集したり解析したりしていて、情報発信もしていました。 しかし、マルウェア解析はすごくリソースが必要な行為だし多くの企業ができるものでもない。脅威インテリジェンスサービスもすぐにIoC(Indicator of Compromise) *4 を公開するとは限らない。 公開情報から得られたマルウェアのIoCを信頼できる場所を使って発信できないだろうか、と考えていました。 kanda: そんなときに隣のMetemcyberチームがTwitterアカウントを使って発信する情報を探していることを知って、これはちょうどいいぞ、と。 strinsert1Na: はい(笑)。脅威インテリジェンス関連を扱っているプロジェクトだし、「そのまま脅威インテリジェンスを発信させていただきますかー」程度のノリでやってみることにしました。 狙い kanda: 脅威インテリジェンスを発信するにあたっての思いや狙いについて聞かせてください。 strinsert1Na: 私は「日本に関連する標的型攻撃を継続的にウォッチするアカウントが欲しいなぁ」と前々から思っていたので、この理想を自分で作ってみようかなと思いました。 標的型攻撃は時々刻々と手口が変化するため、「一度レポートを読んで終わり」ではなく継続的な観測と対策が必要不可欠だと私は考えています。しかし、これらの関連情報やIoCは初報以降、継続的に公開されることは多くなく、海外の脅威インテリジェンスサービスが日本関連の脅威インテリジェンスをリアルタイムに生成することも少ないです。 なので、本Twitterアカウントを監視するだけでなるべく多くの人が標的型攻撃のIoCをリアルタイムで観測し、具体的な防御に活かせるようになれば...というのが狙いです。 kanda: ないなら自らやってやろうというわけですね。情報発信するにあたって何か意識したことはありますか? strinsert1Na: 一般人が流し読むような”ポエム”ではなく、現場のエンジニアがインシデントハンドリング/スレットハンティング *5 で活用できるような”戦術(Tactical)脅威インテリジェンス”の側面から価値を提供することを一番意識しています。 なので、マルウェア/インテリジェンスアナリストがTweetを見ただけで脅威インテリジェンスの真偽を確認できて、IoCを収集できるような投稿にすることを心がけました。 本Twitterアカウントの情報を自動収集することで、即座に防御に対する意思決定に貢献できれば嬉しいなと思っています。 実際に情報発信してみて kanda: 実際に発信してみてどうでした? strinsert1Na: 「脅威インテリジェンス発信と言ってもTwitterに投稿するだけでしょ?」と軽く考えていましたが、使いやすいフォーマットやぱっと見でのわかりやすさ、140字でのまとめ方など創意工夫がかなり必要でした。 SHA256ハッシュを載せたいけど載せるとハッシュ値とC2の情報だけで140文字消化してしまうし、IoCとして有効かをきちんと判断できる証拠画像も欲しいし。 結果として絵文字を使ってビジュアルに表現しながら文字をなるべく削り、IoCベースですぐに脅威インテリジェンスとして消費できる形にまとめました。 自分の人生で最も1Tweetに時間がかかりましたが、納得のできる形になったのではないかと思っています。 🚨⚡ #LODEINFO (🇯🇵) C2: 🌍 http[:]//172.104.72[.]4/ (ASN: AS63949, ISP: Linode, LLC 🇯🇵) and 2 more (172.105.223[.]216, 45.77.28[.]124). Family: 🦠 LODEINFO (v0.6.2) MD5: 🔒 da1c9006b493d7e95db4d354c5f0e99f Sample: 📄 https://t.co/MvDcBjsfwV H/T to NA4Sec Team pic.twitter.com/YUhX1WCrWH — Metemcyber (@Metemcyber) 2022年8月5日 kanda: 情報発信後の反響や手応えは? strinsert1Na: 発信した情報をプロに拾ってもらってより詳細なアーティファクト分析 *6 の提供につながった事例が早速ありました。まだ2Tweetしかしていませんが、再現性のある脅威インテリジェンスはきちんと世の中の役に立っているのだということが実感できました。 Nice share! 🦠 HUI Loader (d839ab2c2ea8f082f98478cb552d6709) ☠️Create a service name "WxUpdateServiceInfo" ->spawn thread for load system.ini-> decoding sc and inject decoded sc into svchost.exe (1/4) https://t.co/zs3buBPm3e pic.twitter.com/yrJx99ePpF — m4n0w4r (@kienbigmummy) 2022年8月25日 今後の意気込み kanda: 最後に今後の意気込みをぜひ。 strinsert1Na: これからも公開情報をもとに継続的に観測を続け、IoCを発信していくつもりです。 JPCERT/CCやベンダのレポートからハンティングに使うルールを日々アップデートしながら、今後も最新の情報を追えるように努力していきます。 また、Twittterに載せる脅威インテリジェンスはなるべく自動収集できる形にできればいいなと考えていて、今後も決まったフォーマットで発信するつもりです。 みなさんの意見を取り入れつつ、改善してより使いやすい形にしていきたいと思っているので、公式アカウント( @Metemcyber )や私のアカウント( @strinsert1Na )にReplyでフィードバックをいただけると助かります。 おわりに 本記事では、NA4SecチームとMetemcyberチームで連携した脅威インテリジェンス発信について紹介しました。 今後も様々な脅威インテリジェンスを発信していく予定です。ご期待ください。 また、発信内容やフォーマットについてのフィードバックも大歓迎です。 ぜひ忌憚のないご意見をお聞かせください。 (さらに公式アカウント @Metemcyber をフォローしていただけると関係メンバーみな泣いて喜びます) おまけ NA4Sec/Metemcyberチームではそれぞれ一緒に働く仲間を募集しています。 「脅威インテリジェンス」をキーワードに活躍の場を探している方、プロジェクトの理念に共感していただける方のご応募をお待ちしています。 Threat Intelligence Analyst / 脅威インテリジェンスアナリスト (NA4Sec) Threat Intelligence Engineer / 脅威インテリジェンスエンジニア (Metemcyber) NFLabs. ではオフェンシブセキュリティを中心としたエンジニア・マネージャーを募集中です。 また、Twitterやエンジニアブログでも情報を発信していますのでぜひ一度ご覧ください。 NFLabs. RECRUIT NFLabs. 公式Twitterアカウント( @NFLaboratories ) NFLabs. エンジニアブログ *7 *1 : Metemcyberについては こちらのブログ記事 でも紹介しています *2 : サイバー脅威インテリジェンス/Cyber Threat Intellgence(CTI):広義には「サイバー攻撃に関する情報を収集して整理・分析したもの」のこと。詳細は こちらの記事 でも解説しています *3 : Visionalistの件については 別のブログ記事 を公開していますので、詳細が気になる方はそちらをご覧ください *4 : IoC(Indicator of Compromise):IP・ドメイン・URL・ファイルハッシュなど、サイバー攻撃にみられる直接的な痕跡情報のこと *5 : スレットハンティング/Threat Hunting:自組織に未検知の脅威が侵入している可能性を前提に、能動的に脅威を探索すること *6 : アーティファクト分析/Artifact Analysis:コンピュータセキュリティインシデントに関わるアーティファクト(マルウエアや攻撃ツール、およびその関連技術)に関して、その実態や対策に関する技術的な調査、分析を行うこと(「 はじめてのJPCERT/CC 」より) *7 : strinsert1NaはNFLabs. エンジニアブログにも記事を寄稿しています → 「無名のセキュリティエンジニアがたった2本のブログ記事からSoftware Designで連載をすることになった( 非技術編 、 技術編 )」
DevOpsプラットフォームの取り組みを紹介する6回目の記事です。 Qmonus Value Stream 開発チームの奥井 ( @HirokiOkui ) です。 連載第6回では、パラメータを効率的に管理するためのQmonus Value Streamの取り組みについて紹介します。 第3回 で解説したとおり、Qmonus Value StreamではInfrastructure as Code(以後IaC)およびCI/CDパイプラインを記述するためにCUE言語を用いています。 CUE言語は洗練されたデータ記述言語であり、CUE言語が有する型システムやプログラマビリティにより、宣言的マニフェストを高品質かつ効率的に記述することが可能になります。 しかしながら、CUE言語を用いても改善できない領域が存在します。 それは、パラメータです。 本記事では、IaCおよびCI/CDパイプラインの構築におけるパラメータの課題について掘り下げたのち、この解決のためにQmonus Value Streamが導入している3つのプラクティスを紹介します。 IaCとCI/CDにおけるパラメータの肥大化 IaCやCI/CDの技術領域では、非常に多くのパラメータを扱うことが要求されます。 クラウドインフラストラクチャの構成をコードで扱うということは、即ち管理対象の全てのクラウドリソースが要求(もしくは許可)している全てのパラメータを決定し、設定することと同義です。 試験環境や本番環境など複数の環境をIaCで扱う場合、ドメイン名やマシンリソースの割当など多くのパラメータで環境ごとに異なる値が必要になります。 環境ごとにマニフェスト全体を複製すると保守性が下がるため、ソフトウェアエンジニアリングのプラクティスに従いテンプレート化やモジュール化による再利用を行うと、それらのテンプレート・モジュールを利用するためにパラメータが必要になります。 CI/CDパイプラインにおいても同様で、ビルド・スキャン・テスト・デプロイなどの各タスクを実行するには、それぞれのタスクを実行するためのパラメータが必要です。 IaC・CI/CDにおけるモジュール化では、一般的なソフトウェア開発に比べてパラメータが増えやすい傾向にあります。 一般的なソフトウェア開発では、ドメインモデリングでよりよい抽象を見つけて Deep Module (インターフェースが狭くて実装が深いモジュール)を目指すことで、パラメータの数を減らすことができます。 一方でIaC・CI/CDは、扱う対象リソースがすでにDeep Moduleとなっており、モジュール化してもテンプレートマッピング程度のShallow Module(インターフェースが広くて実装が浅いモジュール)にしかならず、パラメータの数を大きく減らすことができません。 そのため、扱うリソースの数が増えて複雑度が増すほど、比例してパラメータの数が増えていきます。 加えて、近代的なソフトウェアエンジニアリングのプラクティスがパラメータの増大を助長します。 マイクロサービスアーキテクチャの普及により、サービスはコンテキストの境界で分割され、多数の小さなサービスを扱うことが増えています。 また、クラウドコンピューティングの普及で環境構築のコストが下がったこと、アジリティの高い開発のためテストに重点を置くようになり試験環境が複数必要になったことから、CI/CDパイプラインの中で複数の環境にデプロイすることが一般的になっています。 下図に示すように、開発初期はシンプルなマニフェストと少数のパラメータで事足りたのに、プロダクトの成長に伴いアプリケーションと環境の数が増加し、IaCマニフェストとパラメータが肥大化して管理が大変になったという方も多いのではないでしょうか。 IaCやCI/CDを宣言的マニフェストで管理したときに構成ファイルが増えすぎる問題は a Wall of YAML(YAMLの壁)と揶揄され、SREやリリースエンジニアリングに携わるものの大きな悩みとなっています。 YAMLの壁問題は、データ表現形式(およびデータ記述言語)の表現力・保守性と、パラメータ管理の2つに大きく分解できると考えます。 前者は、CUE言語の登場により飛躍的な向上を遂げました。 一方で後者については、未だ有効な解が見つかっていないのが実情です。 Tektonにおけるパラメータ管理の複雑さ Qmonus Value StreamではCI/CD実行基盤としてTektonを採用していますが、Tektonもパラメータ問題をまだ解けておらず、 最新のリリース で パラメータ流通の効率化の機能 が開発されるなど、試行錯誤を繰り返しています。 Tektonにおけるパラメータの問題とはどのようなものか、見てみましょう。 Tektonを用いて、Gitの変更をトリガとしてCIを実行するには、以下に示す5つのコンポーネントが必要となります(PipelineとTaskについては、 連載第5回 で詳細に解説しています)。 Events:GitプロバイダーなどによるWebhook。 Interceptorを記述することでデータを加工・拡張することが可能。 TriggerBinding:Eventsのペイロードのうち、TriggerTemplateに渡すパラメータを定義する。 TriggerTemplate:TriggerBindingから渡されるパラメータと、その他パラメータをハードコードして、Pipelineを実行するためのパラメータ一式を定義する。 Pipeline:実行可能なパイプライン定義。 Taskの有向非巡回グラフを持ち、順番にTaskを実行する。 実行時にパラメータを受け取り、Taskに引き渡す責務を持つ。 Task:Pipelineから実行パラメータを受け取り、Kubernetes Podを生成してタスクとして定義された処理を実行する。 Tektonは、上記のとおり責務ごとに異なるコンポーネントに分割されており、疎結合に作られていて入れ替えが容易なことから、高い柔軟性を具備しています。 Tektonコミュニティから提供される汎用的なTaskを組み入れつつ、オープンソースとして幅広いユースケースに対応するため、パイプラインの柔軟な構成が可能なこの設計は理に適っています。 一方で、柔軟性の高さ故に複雑であることがTektonの課題です。 上記の例を見ても分かる通り、Gitの変更をトリガとして単純なCIタスクを実行したいというだけでも、多くのコンポーネントが必要になります。 パラメータに注目してみると、その複雑さは一目瞭然です。 以下の図では、前述した5つのコンポーネントのうちパラメータマッピングの責務を持っているコンポーネントと、値の注入が可能なコンポーネントを示しました。 TriggerBinding、TriggerTemplate、Pipelineは、目的は異なれど全てパラメータマッピングの責務を持っています。 また、最終的に実行する対象となるTaskに対してパラメータの値を注入できる層は、Defaulting(値未指定時のデフォルト値の挿入)が可能なTaskも含めて4つもあります。 パラメータマッピング・注入層が複数あることにはメリットもあります。 下位の層でパラメータを凝集したりハードコードすることが可能となり、上位で必要なパラメータの数を減らせます。 シンプルなユースケースであれば、このアプローチは有効に機能するでしょう。 その一方で、層が多くて構成が複雑なため、継続的かつインクリメンタルな改善を繰り返すことで以下の問題が顕在化します。 パラメータがTaskに到達するまでの途中経路で、誤った値を挿入するリスクが高まります。 行き過ぎた汎化をして依存が多すぎる場合と、汎化せず複製された類似コンポーネントが多数ある場合のどちらでも発生します。 最終的にTaskで実行される際に使われる値を追いかけるのが困難になります。 想定と異なる設定値がTask実行時に使われていた場合に、パラメータマッピング・注入層が沢山あるため、上位のマニフェストを順に辿っていかないと、問題箇所に行き着くことができません。 Taskのパラメータが増えた場合に、外部から値を注入するには、Pipeline、TriggerTemplate…と値を注入する階層に至るまで再帰的に新規パラメータを追加する必要が生じます。 これは、PipelineやTriggerTemplateのレベルで汎化され凝集されている場合は問題にはなりませんが、複製のストラテジーを取っている場合は、大量のマニフェストを漏れなく修正する必要が生じます。 このようにIaC・CI/CDでは、パラメータマッピング・注入層を増やすことで得られるメリットよりも、複雑さの増加というデメリットの方が大きくなる傾向にあります。 リリースエンジニアやSREの努力と、コードにガバナンスを効かせるソフトウェアエンジニアリングのプラクティスによりこの問題に立ち向かうことはできますが、限界があります。 私達もDevOps基盤チームとして、増え続ける一方のCI/CD YAMLと戦い続けていましたが、そのあまりの多さと複雑さと管理コストの増大によって徐々に立ち行かなくなっていました。 IaC・CI/CDのパラメータの問題は、一般的なソフトウェアエンジニアリングとは背景・要件が異なるため、異なる解き方が求められていると考えています。 Qmonus Value Streamのアプローチ Qmonus Value Streamは、前述の問題に対して独自のアプローチで問題解決に取り組んでいます。 パラメータの増大と複雑さに立ち向かい、構成が複雑化しても保守性を損なわないために、我々が導入した3つのプラクティスについて紹介します。 1. パラメータマッピング・注入層を1つに限定する Qmonus Value Streamでは、パラメータマッピング・注入の層を1層に限定することで、透明性を高め、Taskに渡されるパラメータを追いかけることを容易にしています。 Qmonus Value Streamは以下の図に示すとおり、PipelineおよびTaskに加えて、Pipelineを実行するAssemblyLine、AssemblyLineに対してパラメータの値を注入するConfiguration RegistryやGit Eventsによって構成されます。 AssemblyLineとConfiguration Registryは、Qmonus Value Streamで独自に開発したコンポーネントです。 このように、前述したTektonのコンポーネントの例と同様に5つで構成されており、コンポーネント間の依存関係に差異はあるものの、有する責務も大きくは変わりません。 ここで、パラメータマッピング・注入の責務をもつコンポーネントに対して、Tektonの例と同様にマークを記述します。 以下の図を見ると、パラメータマッピング・注入の責務を持つコンポーネントがAssemblyLineに限定されていることがわかります。 Qmonus Value Streamでは、Cloud Native AdapterによりPipeline/Taskが自動生成され、この際にPipelineからTaskにどのようにパラメータをマッピングするかが一意に決定されるため、ユーザは関与できません。 また、Configuration RegistryやGit Eventsはパラメータを提供することだけを責務としており、Taskで実行されるパラメータの決定に関与する機能を持ちません。 唯一、Defaultingにより値を注入できる余地がPipelineに残されていますが、この値はAssemblyLineでのパラメータ未指定時の初期値として扱われます。 これにより、利用者は「AssemblyLineが有するパラメータマッピング・注入の機能を用いて、Pipelineが提供するパラメータのインターフェースをどのように満たすか」という問題にのみ集中すれば良くなります。 パラメータマッピングを行う箇所が1つに凝集されるため、1つの変更に伴うコード修正箇所を1箇所に限定できます。 AssemblyLineの層を見ればTaskで使われる値が一意に特定できるため、パラメータの調査も容易です。 3つ以上のパラメータマッピング・注入層が存在したTektonの例と比べると、大幅に複雑さが低減されています。 本設計の実現には、Cloud Native AdapterからPipeline/Taskマニフェストを自動生成する機構が重要な役割を果たしています。 こちらの詳細については、別の記事で紹介予定です。 2. スコープを限定し、関心を分離する パラメータマッピング・注入層をAssemblyLineの1層に限定したことで、パラメータの透明性を高めマッピングの複雑さを低減できましたが、そのトレードオフとして、1層で扱うパラメータが増大するという問題が生じます。 複数のパラメータマッピング・注入層がある場合は、下位の層でパラメータが決定できる場合はそこで注入してしまうことで、上位の層で注入するパラメータを減らすという選択を取ることができました (ただし、前述の通りこの選択は別の問題を引き起こすため、一概に良いものとは言えません)。 一方、パラメータマッピング・注入層を1層に限定する場合は、パラメータを挿入する余地がなくなるため、すべてのパラメータを1箇所で指定しなければなりません。 Qmonus Value Streamでは、Pipelineがタスクを実行するスコープ(以下、実行スコープ)に制限を導入し、同時に扱うコンテキストを減らすことで、この問題に対応します。 連載第2回 で言及したとおり、AssemblyLineは複数のStageによって構成され、1つのStageで1つのPipelineを実行します。 各Stageでは、1つのアプリケーションと1つの環境のペアをタスク実行対象として指定します。 このため、Qmonus Value Streamでは、1つのアプリケーションと1つの環境に対してタスク実行する粒度でPipelineを構成することが強制されます。 AssemblyLine Stageで実行対象とパイプラインを指定する例を以下に示します。 図中に記されている「AppA x Staging」や「AppA x Prod」が、Pipelineの実行スコープとなります。 Pipelineの実行スコープを1つのアプリケーションと1つの環境に限定することで、スコープを限定して関心を分離し、Pipelineごとに取り扱うパラメータの数を絞ります。 その結果、1層で扱うパラメータを小さく分割して管理できます。 Tekton自体にはこのような制約はなく、複数のアプリケーションや複数の環境を対象とした任意の大きさのPipelineを実行できます。 Tektonだけを用いてCI/CDパイプラインを構成する場合、Pipelineが複数のTaskをまとめて実行できる唯一の単位となりますので、このような構成を取ることが有効な場合もあるでしょう。 一方で、複数のアプリケーション・複数の環境を同時に1つのPipelineで扱うということは、多数のコンテキストを同時に扱うことになり、パラメータの増大に繋がります。 Qmonus Value StreamではPipelineの実行スコープが限定されますが、1つのワークフローとして束ねられるスコープには制限がなく、任意のスコープのワークフローを構成できます。 1つのアプリケーションと1つの環境を選択したPipelineをビルディングブロックとしてAssemblyLineを定義することで、複数のアプリケーションと複数の環境を対象としたワークフローを構成できるためです。 この仕組みにより、1つのワークフローで実行したい処理のスコープが広がった場合でも、Pipelineの実行スコープとパラメータを小さく維持できます。 3. スコープごとのパラメータセットとパラメータの自動注入 Pipelineの実行スコープを限定することで、パラメータマッピング・注入層を1つに限定したことによるパラメータの増加を限定的にできました。 この機構を活用して、更にパラメータ設定の負担を下げることを検討します。 Pipelineの実行スコープが1つのアプリケーションと1つの環境に限定されたので、パラメータを提供するConfiguration Registry側もこれにあわせて、アプリケーションと環境のペアの単位でパラメータセットを配備します。 これにより、Pipelineへのパラメータマッピング・注入がやりやすくなります。 Configuration Registryの機構により、GUIからアプリケーションや環境の設定をするだけで、設定した内容が自動で取り込まれ、アプリケーションと環境のペアの単位でのパラメータセットが構成されます。 下図に示すとおり、AssemblyLine StageでPipelineと実行対象を指定することは、満たすべきパラメータインターフェースと、その注入に利用できるパラメータセットを指定することに相当します。 加えて、AssemblyLine Stageの定義では、前述の通りパラメータのマッピングを柔軟に記述できます。 パラメータのマッピングは以下の形式で記述します。 params : - name : <Pipelineのインターフェースのキー> value : <`$(params.<パラメータセットのキー>)`で表される置換対象、を含む任意の文字列> パラメータマッピングが記述されているキーについては、記述の内容に従って置換評価された結果が格納されます。 パラメータマッピングが記述されていないキーについては、インターフェースで指定されているキーと同じキーを用いてパラメータセットを参照します。 以下の例では、Pipelineは appName と baseUrl の2つのパラメータを要求しています。 それに対して、AssemblyLine Stageでは baseUrl のみパラメータマッピングが記述されており、 $(params.domainName) が値に指定されています。 この場合、 baseUrl にはパラメータセットの domainName の値が注入され、 appName にはパラメータセットにおける同名の appName の値が注入されます。 キーが一致する場合の暗黙のパラメータ自動注入(以下、Implicit Binding)があることで、パラメータマッピングの記述量を減らすことができます。 TektonのPipeline/Taskのように、キーが一致している場合であってもすべて明示的な記述が必要だと、パラメータ数が多い場合には面倒です。 記述を忘れると、実行時にパラメータ不足でエラーとなり、余計な工数増に繋がります。 Implicit Bindingは、Pipelineの実行スコープを限定していることでより効果を発揮します。 Configuration Registryのパラメータセットは、決まった仕様・命名規則でパラメータを出力するため、Pipeline側をその命名規則に合わせて構成することで、明示的なマッピングが必要なパラメータの数を減らせます。 Configuration Registryに任意のKey-Valueを追加し、Pipelineのインターフェースにあわせてパラメータセットを拡張することで、同様にマッピングを減らせます。 また、パラメータマッピング・注入層を1層に集めていることで、パラメータの値の透明性を損なうことなくImplicit Bindingを活用できます。 パラメータマッピング・注入層が多数あるなかでパラメータ記述を省略すると、タスクで実際に利用される値を辿るのが更に難しくなります。 前述した通り、Tektonでも Propagated Parameters というPipelineからTaskへのパラメータ流通の効率化の機能を提供していますが、透明性を損なわず利用できる一部記法に限られており、効果が限定的です。 Qmonus Value Streamでは、パラメータマッピング・注入層を1層に限定しPipelineの実行スコープを限定しているからこそ、任意のケースでImplicit Bindingを活用しつつ、値の透明性と保守性を損なわず両立することが可能となっています。 おわりに DevOpsプラットフォームの取り組み連載の6回目の記事として、IaC・CI/CDの複雑化に伴うパラメータの増大という問題について紹介し、その解決のためにQmonus Value Streamが導入しているプラクティスを3つ紹介しました。 パラメータの問題は業界としてまだ発展途上であり、私達もさらなる改善に向けて試行錯誤を繰り返しています。 長い記事となってしまいましたが、私達がパラメータの問題をどのように捉え、その解決に向けてどのような取り組みを実践しているか、少しでも伝われば嬉しいです。 Qmonus Value Streamチームは、メンバ一人一人が、利用者であるアプリケーション開発者のために信念を持って活動を続けています。 プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで 、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 CI/CDプラットフォーム開発、ソフトウェアエンジニア NTTグループのクラウド基盤構築およびCI/CD導入、ソリューションアーキテクト 次はQmonus Value Streamの重要な構成要素であるAssemblyLineについて踏み込んでご紹介します。 お楽しみに!
サマリ SR-MPLS で構成されたネットワークにおいて、リンクの遅延を測定し Delay-Based TE を実現 IOS XR + Junos の Multi-vendor 環境での動作検証 この記事は Multi-AS Segment Routing 検証連載の第 7 回です。目次は こちら 概要 イノベーションセンターの竹中です。本記事ではリンク遅延値を用いる Delay-Based TE について説明します。 Delay-Based TE は各リンクの遅延時間をメトリックとして、E2E 遅延が最短となる経路を選択する経路制御手法です。遅延最短経路はシビアなリアルタイム性が求められる、例えばライブ配信や遠隔操作などでの活用が期待されます。 以降では、Segment Routing を用いたネットワークにおける Delay-Based TE の実現手法と、遅延の実測方法や Multi-vendor 環境における制約を紹介します。 Delay Measurement IGP Metric や TE Metric を利用する TE では、それぞれの Metric をインターフェースに静的に設定し、経路計算を行なっていました。 それに対し、Delay-Based TE を行うためにはリンクの遅延に対応する Delay Metric を動的に計測し、経路計算をする必要があります。 本節では遅延の計測方法について紹介します。 一般的な遅延計測プロトコルのユースケースには大きく分けて 2 パターンあります。 リンク遅延測定 接続されている 2 台のルーター間の遅延測定 Delay-based TE で用いる Delay Metric の算出に利用可能 IOS XR / Junos で実装済み E2E 遅延測定 E2E パスの遅延測定 まだ標準化されていないが独自機能により SR Policy 単位で測定可能 IOS XR にて実装有り 一般的に、遅延計測のためには Two-Way Active Measurement Protocol (TWAMP) と呼ばれる技術が利用されます。 本記事では Multi-vendor における TWAMP を利用したリンク遅延測定の検証と、Delay-Based TE の検証を実施します。 TWAMP の仕組み TWAMP は、 RFC5357 で標準化されている、IP ネットワークにおいてリンクの単方向遅延や双方向遅延を測定できるプロトコルです。 計測用のテストセッションを確立するための Server / Control-Client と、実際に計測用のパケットを交換する Session-Sender / Session-Reflector の 4 つの要素で構成されます。実装上は Server と Session-Reflector の組み合わせで responder として、Control-Client と Session-Sender の組み合わせで controller として扱われることもあります。 TWAMP を用いた遅延計測のおおまかな流れは次の通りです。 Control-Client が TWAMP の well-known port (もしくは設定されたポート) で TCP コネクションを開始し、Server が greeting message を応答する Control-Client が通信モードと、必要に応じて完全性保護と暗号化のための情報を応答する。Server はモード受け入れの応答をする Control-Client がテストセッションを要求し、セッションを開始する Session-Sender と Session-Reflector の間でテストパケットを交換し計測する セッションを終了する なお、セッション管理を排除し簡略化された TWAMP Light と呼ばれるプロトコルも定義されています。こちらは Server / Control-Client / Session-Sender を持つ controller と、Session-Reflector のみを持つ responder から構成されます。TWAMP Light においては responder はセッション情報を持たず、テストパケットに応答するのみになります。 補足として、TWAMP 検証の中で IOS XR / Junos において確認できた制約を記載します。 なお各バージョン番号は我々が検証に使用したバージョンです。 IOS XR NCS55A2 (IOS XR 7.4.1) Single-vendor、Multi-vendor 問わず controller / responder として動作確認可能 ASR9901 (IOS XR 7.4.1) Single-vendor、Multi-vendor 問わず controller / responder として動作確認可能 Cisco 8201 (IOS XR 7.5.1) well-known port である 862 が利用不可。利用可能ポートは XRv9000 (IOS XR 7.4.1) responder としては 利用可能だが、controller としては利用不可 Junos MX204 (Junos 21.2R1.10) Single-vendor、Multi-vendor 問わず controller / responder として動作確認可能。well-known port 以外の利用可能ポートは MX10003 (Junos 21.2R1.10) Single-vendor、Multi-vendor 問わず controlle / responder として動作確認可能。well-known port 以外の利用可能ポートは PTX10001-36MR (Junos 21.2R1.15-EVO) Single-vendor、Multi-vendor 問わず controlle / responder として動作確認可能。well-known port 以外の利用可能ポートは vMX (Junos 21.3R1.9) Single-vendor において controller / responder として動作確認可能。Multi-vendor については未検証。 特にポート番号の制約に着目すると Cisco 8201 と Juniper 機器群の間で相互接続できないことが予想されます。そして実際に確認したところ測定できませんでした。 Delay Metric の広告 Delay Metric を TE で利用するためには、SR Policy を管理するコントローラーや ingress PE といった、経路を計算する機器が各リンクの Delay Metric を保持している必要があります。広告方法としては IS-IS や OSPF などの IGP において TLV によってお互いに広告する方法や、BGP-LS を用いて AS を越えて広告する方法が挙げられます。 IGP では TE 向け拡張が RFC で定義され 1 2 、その中に Delay Metric の広告用 TLV も含まれています。 本連載では一貫して IGP に IS-IS を用いた AS 内の経路学習を行なってるため、本検証での Delay Metric の広告も IS-IS で行います。 Delay Metric を用いた TE の実装パターン Delay Metric を用いた TE の実装方法は次の 2 種類があります。 SR Policy の dynamic path による TE Delay Metric をリンクコストの制約として計算した Flex-Algorithm による TE SR Policy の dynamic path とは 第 5 回記事 の検証でも利用した、SR Policy で指定したメトリックを用いて Segment List を計算する方法です。 Flex-Algorithm に関する説明と検証例は 第 6 回記事 にて行なったので、実装方法などはそちらを参照ください。 以降の検証章ではリンク遅延の計測から IGP での広告、Delay Metric を用いた SR Policy の作成と動作確認を行います。 TWAMP 動作検証 本記事での検証は、遅延の計測・広告と TE 実装で章を分けて説明します。 まず本章では TWAMP を用いたリンク遅延測定と IS-IS で測定値を広告する設定例を紹介します。 動作検証環境は下図の通りです。各ルーター間のリンクは直結です。 なお、IGP にて広告するリンク間遅延の測定手法として Cisco / Juniper ともに TWAMP Light 3 形式で実装されているため、TWAMP Light の設定方法について紹介します。 設定と確認 TWAMP のサーバー、クライアント、IS-IS で広告するための設定を追加します。 なお IS-IS の基本設定の記載は省略します。基本設定は 第 1 回記事 を参照ください。 rt01 (IOS XR) ipsla responder twamp-light test-session 1 local-ip 10.1.2.1 local-port 862 remote-ip 10.1.2.2 remote-port any vrf default ! ! ! performance-measurement interface TenGigE0/0/0/8 next-hop ipv4 10.1.2.2 delay-measurement ! ! ! rt02 (Junos) set services rpm twamp server light port 862 set protocols isis interface xe-0/1/0.0 delay-measurement 上記設定を全て投入後、各ルーターにて計測が行われていること、IS-IS で広告していることを確認します。 rt01 (IOS XR) RP/0/RSP0/CPU0:rt01#show performance-measurement interfaces tenGigE 0/0/0/8 brief Tue Aug 16 20:05:55.144 JST ---------------------------------------------------------------------------------------------------------------------------------------------------------------- 0/0/CPU0 ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Interface Name Tx/Rx Avg/Min/Max/Variance (uSec) -------------------------------------------------------------------------------------------------------------------------------------------------------------- TenGigE0/0/0/8 9/9 85/75/102/10 RP/0/RSP0/CPU0:rt01#show isis database rt01.00-00 verbose Wed Aug 24 17:27:37.673 JST IS-IS 1 (Level-2) Link State Database LSPID LSP Seq Num LSP Checksum LSP Holdtime/Rcvd ATT/P/OL rt01.00-00 * 0x000097b7 0x4c8f 991 /* 0/0/0 (snip) Metric: 10 IS-Extended rt02.00 Local Interface ID: 26, Remote Interface ID: 349 Interface IP Address: 10.1.2.1 Neighbor IP Address: 10.1.2.2 Affinity: 0x00000000 Physical BW: 10000000 kbits/sec Reservable Global pool BW: 0 kbits/sec Global Pool BW Unreserved: [0]: 0 kbits/sec [1]: 0 kbits/sec [2]: 0 kbits/sec [3]: 0 kbits/sec [4]: 0 kbits/sec [5]: 0 kbits/sec [6]: 0 kbits/sec [7]: 0 kbits/sec Admin. Weight: 10 Ext Admin Group: Length: 32 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 Physical BW: 10000000 kbits/sec Link Average Delay: 79 us Link Min/Max Delay: 52/114 us Link Delay Variation: 18 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24009 rt02 (Junos) user@rt02> show services rpm twamp client Connection Session Sender Sender Reflector Reflector Name Name address port address port __r__2 __r__3 10.1.2.2 59823 10.1.2.1 862 user@rt02> show services rpm twamp client probe-results control-connection __r__2 Owner: __r__2, Test: __r__3 TWAMP-Server-Status: Light, Number-Of-Retries-With-TWAMP-Server: 0 Reflector address: 10.1.2.1, Reflector port: 862, Sender address: 10.1.2.2, sender-port: 52980 Test size: 10 probes Probe results: Response received Probe sent time: Tue Aug 16 20:08:50 2022 Probe rcvd/timeout time: Tue Aug 16 20:08:50 2022 Rtt: 138 usec, Egress jitter: 28 usec, Ingress jitter: -44 usec, Round trip jitter: -137 usec Egress interarrival jitter: 99 usec, Ingress interarrival jitter: 105 usec, Round trip interarrival jitter: 137 usec (snip) user@rt02> show isis database rt02.00-00 extensive IS-IS level 1 link-state database: IS-IS level 2 link-state database: rt02.00-00 Sequence: 0x1d6ea, Checksum: 0xf3cb, Lifetime: 1146 secs (snip) TLVs: (snip) IS extended neighbor: rt01.00, Metric: default 10 SubTLV len: 51 IP address: 10.1.2.2 Neighbor's IP address: 10.1.2.1 Local interface index: 349, Remote interface index: 26 Unidirectional link delay: 76 Min unidirectional link delay: 53 Max unidirectional link delay: 91 Unidirectional delay variation: 104 P2P IPV4 Adj-SID - Flags:0x30(F:0,B:0,V:1,L:1,S:0,P:0), Weight:0, Label: 45 P2P IPv4 Adj-SID: 45, Weight: 0, Flags: --VL-- また、各ルーターにて対向から IS-IS でリンク遅延の広告が行われていることを確認します。 rt01 (IOS XR) RP/0/RSP0/CPU0:rt01#show isis database rt02.00-00 verbose Tue Aug 16 20:14:16.022 JST IS-IS 1 (Level-2) Link State Database LSPID LSP Seq Num LSP Checksum LSP Holdtime/Rcvd ATT/P/OL rt02.00-00 0x0001b8fc 0xf246 1049 /1198 0/0/0 (snip) Metric: 10 IS-Extended rt01.00 Interface IP Address: 10.1.2.2 Neighbor IP Address: 10.1.2.1 Local Interface ID: 349, Remote Interface ID: 26 Link Average Delay: 76 us Link Min/Max Delay: 60/107 us Link Delay Variation: 119 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:45 (snip) rt02 (Junos) user@rt02> show isis database rt01.00-00 extensive IS-IS level 1 link-state database: IS-IS level 2 link-state database: rt01.00-00 Sequence: 0x9455, Checksum: 0x1a27, Lifetime: 630 secs (snip) TLVs: (snip) Extended IS Reachability TLV, Type: 22, Length: 159 IS extended neighbor: rt02.00, Metric: default 10 SubTLV len: 148 Local interface index: 26, Remote interface index: 349 IP address: 10.1.2.1 Neighbor's IP address: 10.1.2.2 Administrative groups: 0 <none> Maximum bandwidth: 10Gbps Maximum reservable bandwidth: 0bps Current reservable bandwidth: Priority 0 : 0bps Priority 1 : 0bps Priority 2 : 0bps Priority 3 : 0bps Priority 4 : 0bps Priority 5 : 0bps Priority 6 : 0bps Priority 7 : 0bps Traffic engineering metric: 10 Unknown sub-TLV type 252, length 32 Maximum bandwidth: 10Gbps Unidirectional link delay: 79 Min unidirectional link delay: 52 Max unidirectional link delay: 114 Unidirectional delay variation: 18 P2P IPV4 Adj-SID - Flags:0x30(F:0,B:0,V:1,L:1,S:0,P:0), Weight:0, Label: 24009 P2P IPv4 Adj-SID: 24009, Weight: 0, Flags: --VL-- (snip) 考察 Cisco ルーター同士(rt01-rt03 間)、Juniper ルーター同士(rt02-rt04 間)で TWAMP を用いてリンク遅延計測をしたところ、下記の計測結果に表示されているとおり遅延値に大きく差がありました。これは推測ですが、TWAMP の Session-Reflector が動作する場所の違いによるものだと考えられます。 ※ このため、現状 Multi-vendor 環境において TWAMP によるリンク遅延実測値を用いた Delay-Based TE は必ずしも実態としての最短遅延パスを通るとは限らないと思われます。 Cisco ルーター間のリンク遅延計測結果 RP/0/RSP0/CPU0:rt01#show performance-measurement interfaces hundredGigE 0/0/0/20 brief Thu Aug 18 16:24:37.574 JST ---------------------------------------------------------------------------------------------------------------------------------------------------------------- 0/0/CPU0 ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Interface Name Tx/Rx Avg/Min/Max/Variance (uSec) -------------------------------------------------------------------------------------------------------------------------------------------------------------- HundredGigE0/0/0/20 6/6 3/3/4/0 Juniper ルーター間のリンク遅延計測結果 user@rt02> show services rpm twamp client Connection Session Sender Sender Reflector Reflector Name Name address port address port __r__0 __r__1 10.1.8.1 58574 10.1.8.2 862 __r__2 __r__3 10.1.2.2 50830 10.1.2.1 862 user@rt02> show services rpm twamp client probe-results control-connection __r__0 Owner: __r__0, Test: __r__1 TWAMP-Server-Status: Light, Number-Of-Retries-With-TWAMP-Server: 0 Reflector address: 10.1.8.2, Reflector port: 862, Sender address: 10.1.8.1, sender-port: 58042 Test size: 10 probes Probe results: Response received Probe sent time: Thu Aug 18 16:25:39 2022 Probe rcvd/timeout time: Thu Aug 18 16:25:39 2022 Rtt: 449 usec, Egress jitter: 97 usec, Ingress jitter: 29 usec, Round trip jitter: 169 usec Egress interarrival jitter: 102 usec, Ingress interarrival jitter: 91 usec, Round trip interarrival jitter: 92 usec (snip) 次章では広告されたリンク遅延の値を利用して Delay-Based TE を実装します。 Delay-Based TE 動作検証 本章では IS-IS によって広告されたリンク遅延を用いて TE を実装するための設定例を紹介します。 なお、本記事では SR Policy の作成に On-Demand Next-Hop(ODN)と呼ばれる手法を利用します。 ODN は SR Policy を動的に生成可能な仕組みです。SR Policy の生成条件となる color と、パラメータである Segment List(explicit or dynamic)を定義しておくことで、 IBGP で対象となる経路を受け取った際に BGP nexthop を endpoint とした SR Policy を自動で生成できます。 ODN を利用することで、複数の egress PE がある場合でも endpoint ごとの SR Policy の設定が不要となります。 トポロジー 下記のようなトポロジーで検証します。本章においては、リンク遅延(Delay Metric)は TE のわかりやすさのため図の通りに手動で設定します。 リンク遅延は実測値ベースのため往路と復路で非対称になり得ます。本検証では PE1 -> P1、P2 -> PE2 の片方向のみ値を 100 とし、他の Delay Metric は 10 とします。 非対称な Metric によって E2E の経路も非対称となることを確認します。 このように Delay Metric を設定したネットワークにおいて Delay-Based TE を実装した場合、次の図のような経路となることが想定されます。PE1 -> PE2 への転送時には Delay Metric の大きいリンクを避ける経路となり、PE2 -> PE1 への転送時にはリンクの Delay Metric が全て等しく最短経路となるためです。 物理構成や基本的な interface、IGP、BGP の設定は 第 1 回記事 をご参照ください。 検証において利用する機器は下記の通りです。 PE1、P1: Cisco IOS XR 7.4.1 PE2、P2: Junos 21.3R1.9 Delay Metric の手動設定と確認 リンク遅延の実測値を利用した場合 TE の様子が分かりにくくなるため、各ルーターにて手動で Delay Metric を付与します。 PE1 (IOS XR) performance-measurement interface GigabitEthernet0/0/0/0 delay-measurement advertise-delay 100 ! ! interface GigabitEthernet0/0/0/1 delay-measurement advertise-delay 10 ! ! ! P1 (IOS XR) performance-measurement interface GigabitEthernet0/0/0/0 delay-measurement advertise-delay 10 ! ! interface GigabitEthernet0/0/0/1 delay-measurement advertise-delay 10 ! ! interface GigabitEthernet0/0/0/2 delay-measurement advertise-delay 10 ! ! ! P2 (Junos) set protocols isis interface ge-0/0/0.0 delay-metric 10 set protocols isis interface ge-0/0/1.0 delay-metric 100 set protocols isis interface ge-0/0/2.0 delay-metric 10 PE2 (Junos) set protocols isis interface ge-0/0/0.0 delay-metric 10 set protocols isis interface ge-0/0/1.0 delay-metric 10 Delay Metric が IS-IS にて広告されていることを PE1 にて確認します。 RP/0/RP0/CPU0:PE1#show isis database verbose Wed Aug 17 19:34:32.686 JST IS-IS 1 (Level-2) Link State Database LSPID LSP Seq Num LSP Checksum LSP Holdtime/Rcvd ATT/P/OL PE1.00-00 * 0x0000068a 0xcd94 1016 /* 0/0/0 (snip) Metric: 10 IS-Extended P1.00 Local Interface ID: 7, Remote Interface ID: 7 Interface IP Address: 10.0.0.1 Neighbor IP Address: 10.0.0.2 Physical BW: 1000000 kbits/sec Link Average Delay: 100 us Link Min/Max Delay: 100/100 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24003 Metric: 10 IS-Extended P2.00 Local Interface ID: 8, Remote Interface ID: 333 Interface IP Address: 10.0.1.1 Neighbor IP Address: 10.0.1.2 Physical BW: 1000000 kbits/sec Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24001 Hostname: PE1 P1.00-00 0x00000680 0x67b0 1197 /1200 0/0/0 (snip) Metric: 10 IS-Extended PE1.00 Local Interface ID: 7, Remote Interface ID: 7 Interface IP Address: 10.0.0.2 Neighbor IP Address: 10.0.0.1 Physical BW: 1000000 kbits/sec Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24005 Metric: 10 IS-Extended PE2.00 Local Interface ID: 8, Remote Interface ID: 333 Interface IP Address: 10.0.2.1 Neighbor IP Address: 10.0.2.2 Physical BW: 1000000 kbits/sec Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24003 Metric: 10 IS-Extended P2.00 Local Interface ID: 9, Remote Interface ID: 335 Interface IP Address: 10.0.4.1 Neighbor IP Address: 10.0.4.2 Physical BW: 1000000 kbits/sec Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24001 PE2.00-00 0x000006a3 0x86aa 1050 /1200 0/0/0 (snip) Metric: 10 IS-Extended P1.00 Interface IP Address: 10.0.2.2 Neighbor IP Address: 10.0.2.1 Local Interface ID: 333, Remote Interface ID: 8 Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:17 Metric: 10 IS-Extended P2.00 Interface IP Address: 10.0.3.1 Neighbor IP Address: 10.0.3.2 Local Interface ID: 334, Remote Interface ID: 334 Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:16 (snip) P2.00-00 0x000006aa 0xca39 1066 /1198 0/0/0 (snip) Metric: 10 IS-Extended PE1.00 Interface IP Address: 10.0.1.2 Neighbor IP Address: 10.0.1.1 Local Interface ID: 333, Remote Interface ID: 8 Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:17 Metric: 10 IS-Extended PE2.00 Interface IP Address: 10.0.3.2 Neighbor IP Address: 10.0.3.1 Local Interface ID: 334, Remote Interface ID: 334 Link Average Delay: 100 us Link Min/Max Delay: 100/100 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:16 Metric: 10 IS-Extended P1.00 Interface IP Address: 10.0.4.2 Neighbor IP Address: 10.0.4.1 Local Interface ID: 335, Remote Interface ID: 9 Link Average Delay: 10 us Link Min/Max Delay: 10/10 us Link Delay Variation: 0 us ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:18 (snip) ODN による SR Policy の定義 各 PE にて ODN を設定します。 PE1 (IOS XR) segment-routing traffic-eng on-demand color 100 dynamic metric type latency ! ! ! ! ! PE2 (Junos) set routing-options dynamic-tunnels ODN-100 spring-te source-routing-path-template ODN-100-TMP color 100 set routing-options dynamic-tunnels ODN-100 spring-te destination-networks 0.0.0.0/0 set protocols source-packet-routing compute-profile DYNAMIC-DELAY metric-type delay set protocols source-packet-routing source-routing-path-template ODN-100-TMP primary odn_path compute DYNAMIC-DELAY BGP 経路に color 100 を付加することで動的に SR Policy が作成されます。 color の付加方法は 第4回記事 を参照ください。 color 付加後、Delay-Based TE のための SR Policy が作成されていることが確認できます。 PE1 (IOS XR) RP/0/RP0/CPU0:PE1#show segment-routing traffic-eng policy Wed Aug 17 21:02:43.435 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.0.3 Name: srte_c_100_ep_10.255.0.3 Status: Admin: up Operational: up for 00:00:49 (since Aug 17 21:01:53.919) Candidate-paths: Preference: 200 (BGP ODN) (active) Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (valid) Metric Type: LATENCY, Path Accumulated Metric: 30 16003 [Prefix-SID, 10.255.0.4] 16001 [Prefix-SID, 10.255.0.2] 16002 [Prefix-SID, 10.255.0.3] Preference: 100 (BGP ODN) Requested BSID: dynamic PCC info: Symbolic name: bgp_c_100_ep_10.255.0.3_discr_100 PLSP-ID: 1 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce) (invalid) Metric Type: NONE, Path Accumulated Metric: 0 Attributes: Binding SID: 24007 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no PE2 (Junos) user@PE2> show spring-traffic-engineering lsp detail Name: 10.255.0.1:64:dt-srte-ODN-100 Tunnel-source: Dynamic Tunnel Module(DTM) Tunnel Forward Type: SRMPLS Tunnel-template: ODN-100-TMP To: 10.255.0.1-100<c> State: Up Path: odn_path Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Enabled , Compute Result:success , Compute-Profile Name:DYNAMIC-DELAY Total number of computed paths: 1 Computed-path-index: 1 BFD status: N/A BFD name: N/A TE metric: 110, IGP metric: 20 Delay metrics: Min: 20, Max: 20, Avg: 20 Metric optimized by type: Minimum-Delay computed segments count: 1 computed segment : 1 (computed-node-segment): node segment label: 16001 router-id: 10.255.0.1 疎通確認 traceroute を用いて TE によるパケットの往復の転送経路を確認します。 リンク遅延は実測値ベースのため往路と復路で非対称になり得ます。本検証では PE1 -> P1、P2 -> PE2 の片方向のみ値を 100 としたので PE1 -> PE2 の経路のみ PE1 - P1 と P2 - PE2 のリンクを避ける経路となるはずです。 PE1 -> PE2 RP/0/RP0/CPU0:PE1#traceroute 192.168.1.254 vrf 100 Thu Aug 18 14:48:00.861 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.1.2 [MPLS: Labels 16002/16003/16 Exp 0] 18 msec 3 msec 7 msec 2 10.0.4.1 [MPLS: Labels 16003/16 Exp 0] 23 msec 20 msec 19 msec 3 192.168.1.254 12 msec 2 msec 8 msec PE2 -> PE1 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.729 ms 1.820 ms 1.731 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=1 S=1 2 10.0.1.1 (10.0.1.1) 14.716 ms * 11.153 ms 次の図のように、P1 -> PE1、PE2 -> P2 のリンク遅延を大きくすることで PE2 -> PE1 の経路も PE1 -> PE2 と同じ経路を通ることを確認します。 PE2 [edit] user@PE2# set protocols isis interface ge-0/0/1.0 delay-metric 100 [edit] user@PE2# show | compare [edit protocols isis interface ge-0/0/1.0] - delay-metric 10; + delay-metric 100; P1 performance-measurement interface GigabitEthernet0/0/0/0 delay-measurement <- advertise-delay 10 +> advertise-delay 100 ! ! ! end 再度 PE2 -> PE1 で traceroute を行うと経路が変わっていることが確認できます。 PE2 -> PE1 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.2.1 (10.0.2.1) 8.389 ms 8.118 ms 10.108 ms MPLS Label=16004 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.4.2 (10.0.4.2) 2.344 ms 2.011 ms 1.516 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=2 S=1 3 10.0.1.1 (10.0.1.1) 13.928 ms * 13.418 ms まとめ 本記事では、遅延計測技術である TWAMP と Delay Metric を用いて経路制御をする Delay-Based TE について紹介しました。 また Multi-vendor での TWAMP Light による遅延計測や計測結果の広告と、TE の実装について動作検証しました。 特に現状では遅延計測に対応できない機器の組み合わせがありました。 次回の記事では宛先 prefix 以外をトリガにした SR Policy の適用方法について紹介予定です。 (2022/09/16 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #8] SR Policy の適用方法と活用 IS-IS TE Metric Extensions: https://tex2e.github.io/rfc-translater/html/rfc8570.html ↩ OSPF TE Metric Extensions: https://tex2e.github.io/rfc-translater/html/rfc7471.html ↩ TWAMP Light: https://www.rfc-editor.org/rfc/rfc5357.html#appendix-I ↩
目次 目次 はじめに MMDetectionとは MMDeployとは 実験内容 利用したモデル 計測結果 まとめ はじめに イノベーションセンターの加藤です。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。 今年登場したJetsonの最新版モデル「Jetson AGX Orin」は、前世代である「Jetson AGX Xavier」シリーズの最大8倍のパフォーマンス 1 、ビジョンや自然言語処理など様々な学習済みモデルにおいては最大5倍の高速化 2 が謳われており、エッジデバイス上で動かせるAIアプリケーションの幅がかなり広がりそうです。普段メディアAIに取り組んでいる私としてはどのレベルまでの物体検出モデルがエッジ上で動かせるようになったのかが気になりました。そこで本記事ではMMDetectionの提供する物体検出モデルを NVIDIA TensorRT を用いて各デバイス上で最適化し、COCO Val 2017データセットを用いてモデルの推論速度を調査しました。 MMDetectionとは https://github.com/open-mmlab/mmdetection MMDetectionは OpenMMLab の提供する物体検出ツールボックスです。ソースコードだけでなく学習済みモデルも公開されているので、さまざまなモデルを使って物体検出の推論ができます。 このツールボックスは PyTorch ベースですが、各Jetsonデバイスの性能をめいっぱい活かすために、MMDeployというツールを用いて学習済みモデルをTensorRTモデルに変換してから速度を計測しました。 MMDeployとは https://github.com/open-mmlab/mmdeploy MMDeployはMMDetection同様にOpenMMLabが提供するツールで、OpenMMLab製のモデルを ONNX やTensorRTなどのバックエンド用に変換して利用できます。物体検出のMMDetectionだけでなく、分類( MMClassification )やセグメンテーション( MMSegmentation )、姿勢推定( MMPose )などのモデルにも対応しています。 実は以前までは内部で使われているビルドオプションの都合上Orinに対応したバイナリがビルドできずMMDeployをOrinで使うにはひと手間必要でしたが、私の プルリクエスト によりversion 0.6以降はデフォルトの設定で利用できるようになっています :) 実験内容 使用したデバイスは以下の2点です。 Jetson AGX Xavier Jetson AGX Orin 32GB いずれのデバイスにも NVIDIA JetPack SDK 5.0を導入しています。 評価データセットは5000枚の画像からなる COCO Val 2017 を使いました。 そしてMMDeployは変換したモデルの ベンチマークを計測することができる ので、本記事ではこの機能を用いてXavierとOrinそれぞれにおけるTensorRTモデルの推論速度(FPS)を計測しました。 実験コードは こちら にアップロードしていますので参考にしてください。 利用したモデル 実験に使用したモデルの一覧を示します。configリンクは全てMMDetectionのリポジトリ内のコンフィグファイルを指しており、そのコンフィグファイルが置いてあるディレクトリのREADMEでモデルの概要を見ることができます。 モデル名(バックボーン) 入力サイズ box AP link YOLOV3(DarkNet53) 320x320 27.9 config SSD300(VGG16) 300x300 25.5 config RetinaNet(ResNet50) 800x1333 36.5 config FCOS(ResNet50) 800x1333 36.6 config FSAF(ResNet50) 800x1333 37.4 config YOLOX-s 640x640 40.5 config Faster R-CNN (ResNet50) 800x1333 37.4 config ATSS (ResNet50) 800x1333 39.4 config Cascade R-CNN (ResNet50) 800x1333 40.4 config GFL(ResNet50) 800x1333 40.2 config Cascade Mask R-CNN (ResNet50) 800x1333 41.2 config Mask R-CNN (ResNet50) 800x1333 38.2 config Mask R-CNN (Swin Transformer) 800x1333 42.7 config 計測結果 Swin TransformerをバックエンドにしたMask R-CNNはまだまだOrin上でも厳しそうですが、その他のモデルでは半精度化することによって20-30FPSとほぼリアルタイムに動作させられることがわかりました。 AGX Xavierと比較すると、どのモデルにおいても大きな速度向上が見られ、最小でも2.5倍、最大で10倍近い高速化が見られます。 一方で単純計算なら処理速度が2倍になるはずの半精度化(fp16)の恩恵はAGX Xavierが大きく、AGX Orinではあまり速度が伸びませんでした。メモリアクセスが間に合っておらず性能を出しきれていないというのが仮説の1つとして挙げられますが、より詳細な調査が必要そうです。 まとめ 本記事では様々な物体検出モデルをAGX XavierとAGX Orinで動かし、Jetson最新モデルのAGX Orinがどれくらい性能向上したかを調査しました。すると今までエッジ上ではリアルタイム処理に使えなかった高解像度・高性能なモデルが、AGX Orinでは現実的な処理速度を実現していることがわかりました。これは今までのエッジ用AIアプリケーションの性能向上につながるだけでなく、遠くの物や小さいものをリアルタイムに検知する必要があるタスクにおいてもスタンドアローンで処理可能になることも考えられ、自動運転のようにカメラに写る物体の遠近のレンジが大きい環境や、群衆の混雑度監視のように広い範囲に細々とした物体が大量に写る環境などといった解像度がネックになるタスクでもエッジAIが戦えるようになりそうです。 https://www.nvidia.com/ja-jp/autonomous-machines/embedded-systems/jetson-orin/ ↩ https://youtu.be/hHDZV1V4zq8?t=379 ↩
目次 はじめに SDP開発とは SDP開発とは SSS/SDPフレームワーク ICGW-SDP基盤 全体構成 CI/CD ICGWの可視化 APIリクエスト数 リソース利用状況 おわりに はじめに こんにちは、5G & IoT部/IoTサービス部門のIoT Connect Gateway (ICGW)サービス開発チームの岩田です。 我々のチームでは2021年度下期に私の主導のもと、ICGWのSDP開発というものを行い、 Smart Data Platform (SDPF)ポータル対応 およびSDPF上で展開している他のサービスとの自動連携を実現しました。 本記事ではこのICGWのSDP開発に焦点を当てて、開発背景や全体構成ならびにCI/CDを紹介します。 また、ICGWの可視化関連の情報も合わせて掲載しますので、今後の開発のご参考になれば幸いです。 ICGWのサービス自体やユースケースに関して、過去の記事で紹介していますのでご参照ください。 IoT Connect Gateway を使ってみた 第1回 〜ICGWのご紹介〜 IoT Connect Gateway を使ってみた 第3回 〜観葉植物育成状況の可視化〜 SDP開発とは サービス開発における背景 図1 従来のNTT Comとオンラインサービス時代のサービス開発イメージ 従来のNTT Comのサービスでは、サービスのコアとなる技術要素のみが開発の対象となっており、そのサービスをユーザに届けて対価を得る部分はプロセス整備として扱われ開発の主な対象ではありませんでした。 しかし、近年サービスのオンライン化やセルフマネジメント化が強く推進される中で開発しなければならない範囲は拡大し続けています。 ユーザがサービスのセルフマネジメントを行うためのAPI/GUI、それらを提供するための認証/権限管理機構、ユーザのサービス利用状況の管理/システム化などが必要となってきます。 また、サービスへの申込みや料金計算をオンライン化/自動化したい場合はさらに開発対象の範囲は広がります。 SSS/SDPフレームワーク SDPFポータルで展開するサービスを開発する上で欠かせないSSS/SDPというフレームワークを紹介します。 SSS は「Shared Support Service」、 SDP は「Service Delivery Platform」の略語でNTT Comの用語です。 このフレームワークにおいて、各SDPは サービスのコアになる技術 と それをユーザが利用するためのIF(GUI/API) の開発に集中し、SSSがその土台となる契約の申し込み、契約管理、認証の基盤やユーザ権限管理、料金計算等を一手に担います。 SSS/SDPフレームワークに則り開発を進めることで、それぞれの役割を明確に分担して重複開発を避けられるというアイディアになっています。 SSSや他のSDPとやり取りするための統一的な規格にサービス仕様を合わせることを我々のチームではSDP開発と呼んでいます。 SDP開発によって我々のICGWにおいても、サービス運用やユーザ利便性の面において多くのメリットをもたらしました。 図2 ICGWにおけるSDP開発のBefore/After 図3はSDP開発によって新規に実装されたGUIのサンプルです。 ユーザが設定を投入するためのページだけでなく、利用状況を視覚的に表現したページを提供することによって利便性向上を図っています。 図3 SDP開発による新規提供GUI ICGW-SDP基盤 ICGW全体構成 図4 ICGW全体構成 ICGWは大きく分けて ICGW-core基盤 と ICGW-SDP基盤 の2つからなっています。 ICGW-core基盤はSDP開発前から存在しているもので、デバイスからのデータが流れるデータプレーンやSO APIを提供しています。 ICGW-SDP基盤はSDP開発時に新設されたもので、ユーザが設定を投入するためのGUIやAPI、SSSや他SDPと連携するためのAPIなどを提供しています。 もともとICGWはSDPに対応しない形でサービスリリースしたため、サービスのコアとなる部分はすでに出来ていました。 そのため、ICGW-SDP基盤では特にユーザIFに注力する形での開発となりました。 ICGW-SDP基盤のAPIは裏側でSO APIをコールしており、基本的にユーザのリクエストをそのまま裏へ流し、必要に応じてSDPの規格を満たすようにリクエスト内容を加工できるような設計になっています。 実際にこのような二段構成で運用してみたところ、いくつかのメリットがありましたので列挙しておきます。 ICGW-SDP基盤ではユーザビリティ向上に注力して開発できる ICGW-SDP基盤で対応しない限りユーザに見えることはないので、ICGW-core基盤での新規機能開発がやりやすい サービス仕様やユーザビリティの観点でユーザに見せたくない情報があった場合に、それらをICGW-SDP基盤側でコントロールしやすい CI/CD 我々のチームでは GUI開発 、 API開発 、 CI/CD ともにNTT Comの内製ツールであるQmonusシリーズというものを利用しています。 特にCI/CDを司る Qmonus Value Stream (Qmonus VS)に関しては、開発者ブログに紹介記事がありますので、詳細はそちらをご覧ください。 [DevOpsプラットフォームの取り組み #1] Qmonus Value Streamの紹介 図5は実際のQmonus VSの画面で、新しいバージョンのAPIをステージング環境にデプロイした時の様子です。 Qmonus VSは基本的にGitのブランチマージをトリガーに、いくつかのステップを経てデプロイが実行されます。 ICGWチームでは特にAPIをデプロイする際に、APIの自動試験が実行されるようにステップを構成しており、テストをパスしなければロールバックするという実装になっています。 図5 Qmonus Value StreamにおけるAPIデプロイ時の画面 GUIの自動試験に関してはGithub Actionsを採用しており、同じくブランチマージや任意のタイミングで試験を実施できるようになっています。 また、図6のようにダッシュボードを活用することで、試験中のスナップショットや動画を保存し共有できるような機構も現在計画中です。 図6 GUI自動試験の結果画面 ICGWの可視化 SDP開発に伴い、私の方で開発者/企画者向けにいくつかの可視化ポータルをGCPを活用して作成してみましたので、この場でご紹介します。 APIリクエスト数 図7 APIリクエスト数可視化ポータル こちらはAPIのリクエスト数をレスポンスコードごとに可視化したグラフです。 ICGW-SDP基盤で提供しているIFがどの時間帯にどれくらい利用されているか、500番エラーが多発していないかといった観点のために作成しました。 また、エラーの内容からユーザの間違えやすい傾向なども分析でき、ユーザビリティ向上にも活用できます。 アラート機能と組み合わせることで、リクエスト数の条件に応じてメールやSlack通知を発行することも可能です。 図8が可視化のための全体アーキテクチャです。以下のステップで構成しています。 ユーザのAPIリクエストログをLoggingに転送する レスポンスコードごとに該当のログを抽出できるようにフィルタを追加する ステップ2で抽出したログに対してMonitoring上でカウントしグラフ化する 図8 APIリクエスト数可視化用アーキテクチャ リソース利用状況 図9 リソース利用状況可視化ポータル こちらは全ユーザの設定情報やトラフィック情報をもとに、リソースの利用状況とサービスごとのトラフィック量を時系列的に可視化したポータルです。 どれくらいの数のユーザがどのサービスを使っているかという市場分析用途で作成しました。 これらのグラフは開発者だけでなく企画者にもサービス展開を考える上でご活用いただいています。 図10が可視化のための全体アーキテクチャです。以下のステップで構成しています。 必要なデータを抽出するためのソースコードをStorageに保存する Functionsをビルドする SchedulerからFunctionsが定期的に実行される Funtionsによって抽出された各種データをBigQueryに保存する BigQueryに保存されたデータをData Studioで可視化する 図10 リソース利用状況可視化用アーキテクチャ これらの構成は全てIaaSとしてTerraform管理されており、ソースコードに変更が加わった場合はステップ1/2が自動で実行されるようになっています。 FunctionsからICGW基盤へAPIコールを行う際に、VPCを経由してCloud NATから出ていくことで外部IPを固定でき、ICGW基盤側でのアクセスコントロールが容易になっています。 Data Studioは同じくGoogleが提供している可視化ポータルで、権限があればBigQueryに保存されたデータも容易に読み込むことができ、データの更新も自動で行ってくれます。 また、作成した可視化ポータルは共有リンクを取得することで簡単にチームメンバーへ展開できます。 おわりに ICGW SDP開発のリーダーとして、今回は開発の裏側(?)を紹介しました。 私自身は開発やCI/CDに関してまだまだ勉強途中であり、試行錯誤しながら挑戦しているところなので、より良い手法や開発スタイルがあれば取り込んでいきたいです。 また、今回の可視化ポータルのように、実際に構成を考えて自分の手を動かしてシステムを作っていくことには今後も挑戦できたらと思います。 本記事を通して、ICGWにも興味を持っていただけたら幸いです! IoT Connect Gatewayサービス詳細はこちら
サマリ Flexible Algorithm による VPN 経路の TE を実現 IOS XR + Junos の Multi-vendor 環境で動作検証に成功 この記事は Multi-AS SR 検証連載の第 6 回です。目次は こちら 概要 イノベーションセンターの田島です。 本記事では Flexible Algorithm を用いた Traffic Engineering について解説します。 Flexible Algorithm で計算された最適経路によって L3VPN の転送が行われる例や、トポロジーが変化しても制約を満たす経路が自動で再計算される例を検証で示します。 これまでの記事でも紹介したように TE を実施する場合において、対象が L3VPN と L2VPN とでは差異が無いため、本記事の内容は L2VPN でも適用可能です。 なお Flexible Algorithm の略称としては Flex-Algo、Flex Algo、Flex-Algorithm などが存在します。本記事中で略記する場合は以下 Flex-Algorithm と表記します。 本記事では Flex-Algorithm についての説明と、ルーターでの動作検証を紹介します。 Flexible Algorithm とは Flexible Algorithm とは IGP での経路計算時に複数の異なるメトリックや制約を付け加え、複数の基準による最適経路を並行して決定し使用するための技術です。 リンクのメトリックとは帯域幅を基にしたコスト、遅延、または TE 用のコストのことです。 制約とは一部のリンクを使用しないような状況のことです。 従来の IGP では、リンクコストを基にし全リンクを使用可能な場合の最適経路のみを計算可能でしたが、 Flex-Algorithm により複数の指標を使い分けることができ、また仮想的にアンダーレイの一部に限定して通信させることができます。 例えば、リンク帯域を基にした最適経路とリンク間遅延を基にした最適経路をそれぞれ Flex-Algorithm で定義し、各 Algorithm を利用するための SR Policy を定義しておきます。 第 4 回の記事 のように BGP color 等によってこれらの SR Policy を選択することで Flex-Algorithm の適用ができます。 このように最適化させたい意図ごとに仮想的にアンダーレイを分割し選択することで、各通信の性質や要求ごとに適切なアンダーレイおよび最適なパスでの通信が実現されます。 Flex-Algorithm により区別されたトポロジーは128から255まで番号をつけることができ、 メトリックの種類やトポロジー制約の記述などとともに定義されます。 各リンクに属性を割り当て、特定の属性のリンクを含むか、または含まないかで制約が記述されます。 この属性を color と表記し、本記事で後述する例でも RED や BLUE のようにリンクを区別します。 なお 第 4 回の記事 に BGP color の説明がありましたが、別物です。 Flex-Algorithm の color は、各属性を1ビットと見立てて割り当てられていれば1に対応するビットマップとして実装され Extended Administrative Group (EAG) や Administrative Group (AG) の値として保存されます。 これらの情報からなる定義は Flexible Algorithm Definition (FAD) と呼ばれ、 IGP により広告されます。 Flex-Algorithm は IETF Network Working Group での Internet-Draft 1 段階の技術であり、2022年8月時点では完全に標準化されている方式ではありません。 しかし、各社のルーター製品やオープンソース製品などにおいて実装が始まっています。 FAD の広告には EAG や AG などの Type Length Value (TLV) を使用しますが、使用する TLV や Sub-TLV を相互に認識可能で無ければ相互接続ができません。 実際に Extended Administrative Group や Application-Specific Link Attribute の解釈が違うことで意図した動作にならないことがありましたが、 IOS XR 7.4.1 と Junos 21.3 以降の組み合わせでは相互運用できることを確かめました。 Flex-Algorithm の動作例 Flex-Algorithm の動作をトポロジー例を用いて説明します。 図の中央上部にアンダーレイトポロジーを記載しています。 PE 2台、 P 2台の各リンクに対して RED と BLUE のラベル、 IGP メトリックと TE メトリックを設定しています。 このアンダーレイで 2 つの Flex-Algorithm を作成します。 Algorithm 128 : RED リンクは使用禁止。 IGP メトリックに従って最適経路を算出する。 Algorithm 129 : BLUE リンクのみを使用する。 TE メトリックに従って最適経路を算出する。 それぞれ図の左下と右下のような、制約が反映され指定のメトリックがついたトポロジーとして仮想的に認識されます。 この状態で転送経路としてどちらかの Algorithm を選択すると、それぞれの仮想的なトポロジー内での最適経路に従って転送されます。 Algorithm を明示的や自動的に選択するために SRGB をそれぞれ分けます。 例えばこの例だと Algorithm 128 側を SRGB 17000 から、 129 側を 18000 からというように設定します。 これにより Algorithm を指定した宛先の指定ができます。 リンク障害などで元のアンダーレイトポロジーから変化した場合でも、それぞれの Algorithm ごとに再計算されます。 これは自動計算されるバックアップパスも Algorithm の制約に従うことを意味します。 しかし気を付けるべき点もあります。制約を入れた後のトポロジーで単一障害点となるリンクが存在するような非冗長な場合において、そのリンクが障害状態になると当該 Algorithm はネットワークが分断され不通になってしまいます。 実際のアンダーレイが問題なく冗長化されていて通信継続させたい場合は、当該 Algorithm の使用を指定する SR Policy の定義などでフォールバック先を指定しておく必要があります。 Flex-Algorithm の適用検証 実際に Flex-Algorithm を用いて TE を実装します。 概要部分での説明に用いた次の図のトポロジーを使用し Algorithm 128 を設定します。 なお、次節で障害時の動作を確かめます。 このアンダーレイで RED リンクを使用せず IGP メトリックに従う Algorithm 128 を定義します。 あらかじめ顧客 A の BGP 経路には BGP color 100 をつけておき、定義した Algorithm 128 を顧客 A の VPN 経路に使用します。 転送経路は次のように表せ、これを実装します。 なお 129 を実装しない理由としては FAD の解釈にメーカー差があり想定の経路へは TE されないためです。 2023/09/19 追記:本記事公開後の OS のバージョンアップによって FAD の相互運用ができるようになりました。 第 15 回 の記事を参照ください。 検証に用いたバージョンは下記の通りです。 PE1、P1: Cisco IOS XR 7.4.1 PE2、P2: Junos OS 21.3R1.9 第 4 回の記事 の通りに L3VPN および BGP color の SR Policy を作成します。実施内容は下記の通りで、詳しくはリンク先記事を参照ください。 VRF 100 と 200 による L3VPN 作成 BGP color の付与と広告 BGP color による SR Policy の適用 ここからは次のステップで設定し確認します。 リンクに対する制約やメトリックの定義 Flex-Algorithm 128 の定義 ラベル学習の確認 SR Policy の設定と確認 traceroute による VPN の通信経路確認 1. リンクに対する制約やメトリックの定義 各ルーターにてインターフェースに対し Flex-Algorithm の color と IGP および TE のメトリックを指定します。 color はビットマップでフラグ管理されるため、各色で何ビット目を用いるかの定義がそれぞれのルーターに必要です。 今回は行きと帰りの経路を対象にするため 1 つのリンクに対して両端のルーターで同じ設定にします。 PE に関しては VPN の next-hop として Loopback インターフェースが使用されるため、特定の color のリンクのみを使用するといった制約を意味する include-any や include-all を用いる際は Loopback に当該 color を忘れずに付ける必要があります。 BLUE は最終的に本検証で使用しませんが、設定方法の参考として同時に設定します。 PE1 (IOS XR) P1 向け Gi0/0/0/0 を RED かつ BLUE にします PE であるため Loopback0 に BLUE をつけます router isis 1 affinity-map RED bit-position 1 affinity-map BLUE bit-position 2 ! interface Loopback0 affinity flex-algo BLUE ! interface GigabitEthernet0/0/0/0 affinity flex-algo RED BLUE address-family ipv4 unicast metric 10 ! ! interface GigabitEthernet0/0/0/1 address-family ipv4 unicast metric 10 ! ! ! segment-routing traffic-eng interface GigabitEthernet0/0/0/0 metric 10 ! interface GigabitEthernet0/0/0/1 metric 10 ! ! ! P1 (IOS XR) PE1 向け Gi0/0/0/0 を RED かつ BLUE にします PE2 向け Gi0/0/0/1 と P2 向け Gi0/0/0/2 を BLUE にします router isis 1 affinity-map RED bit-position 1 affinity-map BLUE bit-position 2 ! interface GigabitEthernet0/0/0/0 affinity flex-algo RED BLUE address-family ipv4 unicast metric 10 ! ! interface GigabitEthernet0/0/0/1 affinity flex-algo BLUE address-family ipv4 unicast metric 10 ! ! interface GigabitEthernet0/0/0/2 affinity flex-algo BLUE address-family ipv4 unicast metric 10 ! ! ! segment-routing traffic-eng interface GigabitEthernet0/0/0/0 metric 10 ! interface GigabitEthernet0/0/0/1 metric 100 ! interface GigabitEthernet0/0/0/2 metric 10 ! ! ! PE2 (Junos) P1 向け ge-0/0/0 と P2 向け ge-0/0/1 を BLUE にします PE であるため lo0 に BLUE をつけます set protocols isis interface ge-0/0/0.0 level 2 te-metric 100 set protocols isis interface ge-0/0/0.0 level 2 application-specific attribute-group Algo129 admin-group BLUE set protocols isis interface ge-0/0/0.0 level 2 application-specific attribute-group Algo129 application flex-algorithm set protocols isis interface ge-0/0/0.0 level 2 metric 10 set protocols isis interface ge-0/0/1.0 level 2 te-metric 10 set protocols isis interface ge-0/0/1.0 level 2 application-specific attribute-group Algo129 admin-group BLUE set protocols isis interface ge-0/0/1.0 level 2 application-specific attribute-group Algo129 application flex-algorithm set protocols isis interface ge-0/0/1.0 level 2 metric 10 set protocols isis interface lo0.0 level 2 application-specific attribute-group Algo129 admin-group BLUE set protocols isis interface lo0.0 level 2 application-specific attribute-group Algo129 application flex-algorithm set protocols mpls admin-groups RED 1 set protocols mpls admin-groups BLUE 2 set protocols mpls interface ge-0/0/0.0 admin-group BLUE set protocols mpls interface ge-0/0/1.0 admin-group BLUE set protocols mpls interface lo0.0 admin-group BLUE P2 (Junos) P1 向け ge-0/0/1 と PE2 向け ge-0/0/2 を BLUE にします set protocols isis interface ge-0/0/0.0 level 2 te-metric 10 set protocols isis interface ge-0/0/0.0 level 2 metric 10 set protocols isis interface ge-0/0/1.0 level 2 te-metric 10 set protocols isis interface ge-0/0/1.0 level 2 application-specific attribute-group Algo129 admin-group BLUE set protocols isis interface ge-0/0/1.0 level 2 application-specific attribute-group Algo129 application flex-algorithm set protocols isis interface ge-0/0/1.0 level 2 metric 100 set protocols isis interface ge-0/0/2.0 level 2 te-metric 10 set protocols isis interface ge-0/0/2.0 level 2 application-specific attribute-group Algo129 admin-group BLUE set protocols isis interface ge-0/0/2.0 level 2 application-specific attribute-group Algo129 application flex-algorithm set protocols isis interface ge-0/0/2.0 level 2 metric 10 set protocols mpls admin-groups RED 1 set protocols mpls admin-groups BLUE 2 set protocols mpls interface ge-0/0/1.0 admin-group BLUE set protocols mpls interface ge-0/0/2.0 admin-group BLUE 2. Flex-Algorithm 128 の定義 Algorithm 128 を定義し広告します。 下記では各ルーターごとに掲示していますが、同じものが設定されます。 Algorithm ごとの転送に用いるため、各 Node-SID も Algorithm ごとに付与します。 ここでは Algorithm 128 は 17000 番台へと空間を分けています。 下記設定では比較をわかりやすくするために、 Algorithm がついていない元の Node-SID の設定も残しています。 PE1 (IOS XR) router isis 1 flex-algo 128 advertise-definition affinity exclude-any RED ! interface Loopback0 address-family ipv4 unicast prefix-sid index 1 prefix-sid algorithm 128 index 1001 ! ! ! P1 (IOS XR) router isis 1 flex-algo 128 advertise-definition affinity exclude-any RED ! interface Loopback0 address-family ipv4 unicast prefix-sid index 2 prefix-sid algorithm 128 index 1002 ! ! ! PE2 (Junos) Junos において RSVP などのシグナリングが別に動作していないときはドキュメントに従い set protocols isis traffic-engineering advertisement always が必要です。 set policy-options policy-statement Flex-Algo-SID term 1 from route-filter 10.255.0.3/32 exact set policy-options policy-statement Flex-Algo-SID term 1 then prefix-segment algorithm 128 index 1003 set policy-options policy-statement Flex-Algo-SID term 1 then prefix-segment algorithm 128 node-segment set routing-options flex-algorithm 128 definition metric-type igp-metric set routing-options flex-algorithm 128 definition spf set routing-options flex-algorithm 128 definition admin-group exclude RED set protocols isis source-packet-routing node-segment ipv4-index 3 set protocols isis source-packet-routing flex-algorithm 128 set protocols isis source-packet-routing strict-asla-based-flex-algorithm set protocols isis traffic-engineering advertisement always set protocols isis export Flex-Algo-SID P2 (Junos) set policy-options policy-statement Flex-Algo-SID term 1 from route-filter 10.255.0.4/32 exact set policy-options policy-statement Flex-Algo-SID term 1 then prefix-segment algorithm 128 index 1004 set policy-options policy-statement Flex-Algo-SID term 1 then prefix-segment algorithm 128 node-segment set routing-options flex-algorithm 128 definition metric-type igp-metric set routing-options flex-algorithm 128 definition spf set routing-options flex-algorithm 128 definition admin-group exclude RED set protocols isis source-packet-routing node-segment ipv4-index 4 set protocols isis source-packet-routing flex-algorithm 128 set protocols isis source-packet-routing strict-asla-based-flex-algorithm set protocols isis traffic-engineering advertisement always set protocols isis export Flex-Algo-SID 3. ラベル学習の確認 上記設定が完了すると FAD の広告がされるため、各ルーターで各 Algorithm の情報を見ることができます。 FAD の内容と、ラベルが学習されているのかを確認します。 PE1 (IOS XR) PE1 自身の広告内容を確認します。 FAD の定義内容や隣接である P1 へのリンクの color が見えます。 なおビットマップのため RED は 1 << 1 = 0x2 となり、 BLUE は 1 << 2 = 0x4 となり、 RED かつ BLUE は 0x2 & 0x4 = 0x6 と表現されます。 RP/0/RP0/CPU0:PE1#show isis database verbose Mon Aug 15 10:50:02.431 JST IS-IS 1 (Level-2) Link State Database LSPID LSP Seq Num LSP Checksum LSP Holdtime/Rcvd ATT/P/OL PE1.00-00 * 0x000007ed 0xe7e5 1067 /* 0/0/0 Area Address: 49.0000 NLPID: 0xcc IP Address: 10.255.0.1 Metric: 10 IP-Extended 10.0.0.0/30 Prefix Attribute Flags: X:0 R:0 N:0 E:0 A:0 Metric: 10 IP-Extended 10.0.1.0/30 Prefix Attribute Flags: X:0 R:0 N:0 E:0 A:0 Metric: 10 IP-Extended 10.255.0.1/32 Prefix-SID Index: 1, Algorithm:0, R:0 N:1 P:0 E:0 V:0 L:0 Prefix-SID Index: 1001, Algorithm:128, R:0 N:1 P:0 E:0 V:0 L:0 Prefix-SID Index: 2001, Algorithm:129, R:0 N:1 P:0 E:0 V:0 L:0 Prefix Attribute Flags: X:0 R:0 N:1 E:0 A:0 Source Router ID: 10.255.0.1 Hostname: PE1 Router ID: 10.255.0.1 Router Cap: 10.255.0.1 D:0 S:0 Segment Routing: I:1 V:0, SRGB Base: 16000 Range: 8000 SR Local Block: Base: 15000 Range: 1000 Node Maximum SID Depth: Label Imposition: 10 SR Algorithm: Algorithm: 0 Algorithm: 1 Algorithm: 128 Algorithm: 129 Flex-Algo Definition: Algorithm: 128 Metric-Type: 0 Alg-type: 0 Priority: 128 Flex-Algo Exclude-Any Ext Admin Group: 0x00000002 Flex-Algo Definition: Algorithm: 129 Metric-Type: 0 Alg-type: 0 Priority: 128 Flex-Algo Include-Any Ext Admin Group: 0x00000004 Metric: 10 IS-Extended P1.00 (snip) Application Specific Link Attributes: L flag: 0, SA-Length: 1, UDA-Length: 1 Standard Applications: 0x10 FLEX-ALGO User Defined Applications: 0x10 Ext Admin Group: 0x00000006 Affinity: 0x00000006 ADJ-SID: F:0 B:0 V:1 L:1 S:0 P:0 weight:0 Adjacency-sid:24003 (snip) 元々の Node-SID だけではなく、各 Algorithm で設定した Node-SID も学習されています。 RP/0/RP0/CPU0:PE1#show mpls forwarding Mon Aug 15 11:07:47.194 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ (snip) 16003 16003 SR Pfx (idx 3) Gi0/0/0/0 10.0.0.2 5416 (snip) 17003 17003 SR Pfx (idx 1003) Gi0/0/0/1 10.0.1.2 0 (snip) PE2 (Junos) FAD を参照するために show isis database extensive 以外にも Flex-Algorithm のコマンドがあります。 user@PE2> show isis spring flex-algorithm flex-algorithm-id 128 Flex Algo: 128 Level: 2, Color: 100, Participating, FAD supported Winner: P1, Metric: 0, Calc: 0, Prio: 128, Excl: 0x2, FAD supported Exclude: 0x2 RED Spf Version: 632 Participation toggles: 1 Last Route add: 0 Last Route rem: 0 Last Route chg: 0 Last Route fail: 0 Last Route def: 0 Total Route add: 16 Total Route rem: 8 Total Route chg: 0 Total Route fail: 0 Total Route def: 0 Topo refresh count: 2 PE1, Metric: 0, Calc: 0, Prio: 128, Excl: 0x2, FAD supported P2, Metric: 0, Calc: 0, Prio: 0, Excl: 0x2, FAD supported PE2, Metric: 0, Calc: 0, Prio: 0, Excl: 0x2, FAD supported Full SPFs: 631, Partial SPFs: 0 IS-IS Flex Algo 128 level 2 SPF log: Start time Elapsed (secs) Count Reason Sun Aug 14 11:03:06 0.000056 1 Periodic SPF (snip) ラベルも学習されています。 user@PE2> show route table mpls.0 (snip) 16001 *[L-ISIS/14] 5d 16:04:39, metric 30 to 10.0.2.1 via ge-0/0/0.0, Swap 16001 > to 10.0.3.2 via ge-0/0/1.0, Swap 16001 (snip) 17001 *[L-ISIS/14] 5d 14:55:16, metric 30 > to 10.0.3.2 via ge-0/0/1.0, Swap 17001 (snip) 4. SR Policy の設定と確認 On Demand Nexthop (ODN) を使用して特定の BGP color の経路に使用する Algorithm を指定します。 PE1 (IOS XR) segment-routing traffic-eng on-demand color 100 dynamic metric type igp ! sid-algorithm 128 ! ! ! ! SR Policy が up 状態になり、宛先も PE2 の Algorithm 128 の SID (17003) が選択されています。 RP/0/RP0/CPU0:PE1#show segment-routing traffic-eng policy color 100 Mon Aug 15 13:27:34.701 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.0.3 Name: srte_c_100_ep_10.255.0.3 Status: Admin: up Operational: up for 4d12h (since Aug 10 14:31:37.908) Candidate-paths: Preference: 200 (BGP ODN) (active) Requested BSID: dynamic Constraints: Prefix-SID Algorithm: 128 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (valid) Metric Type: IGP, Path Accumulated Metric: 30 17003 [Prefix-SID: 10.255.0.3, Algorithm: 128] Preference: 100 (BGP ODN) Requested BSID: dynamic PCC info: Symbolic name: bgp_c_100_ep_10.255.0.3_discr_100 PLSP-ID: 3 Constraints: Prefix-SID Algorithm: 128 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce) (invalid) Last error: No path Metric Type: NONE, Path Accumulated Metric: 0 Attributes: Binding SID: 24012 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no 顧客 A の BGP 経路も上記で設定した SR Policy (24012) を使用するようになっています。 RP/0/RP0/CPU0:PE1#show route vrf 100 192.168.1.0/24 detail Mon Aug 15 13:34:37.770 JST Routing entry for 192.168.1.0/24 Known via "bgp 65000", distance 200, metric 0, type internal Installed Aug 10 14:31:37.916 for 4d13h Routing Descriptor Blocks 10.255.0.3, from 10.255.0.3 Nexthop in Vrf: "default", Table: "default", IPv4 Unicast, Table Id: 0xe0000000 Route metric is 0 Label: 0x10 (16) Tunnel ID: None Binding Label: 0x5dcc (24012) Extended communities count: 0 Source RD attributes: 0x0000:65000:100 NHID:0x0(Ref:0) Route version is 0x5 (5) No local label IP Precedence: Not Set QoS Group ID: Not Set Flow-tag: Not Set Fwd-class: Not Set Route Priority: RIB_PRIORITY_RECURSIVE (12) SVD Type RIB_SVD_TYPE_REMOTE Download Priority 3, Download Version 22 No advertising protos. PE2 (Junos) set routing-options flex-algorithm 128 color 100 こちらも PE1 の Algorithm 128 の SID (17001) が選択されています。 user@PE2> show route table inetcolor.0 inetcolor.0: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 10.255.0.1-100<c>/64 *[L-ISIS/14] 5d 17:18:36, metric 30 > to 10.0.3.2 via ge-0/0/1.0, Push 17001 (snip) 顧客 A の BGP 経路も 17001 SID として積むようになっています。 user@PE2> show route table 100.inet.0 192.168.0.0/24 extensive 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) 192.168.0.0/24 (1 entry, 1 announced) TSI: KRT in-kernel 192.168.0.0/24 -> {indirect(1048579)} *BGP Preference: 170/-101 Route Distinguisher: 65000:100 Next hop type: Indirect, Next hop index: 0 Address: 0x704fafc Next-hop reference count: 3 Source: 10.255.0.1 Next hop type: Router, Next hop index: 654 Next hop: 10.0.3.2 via ge-0/0/1.0, selected Label operation: Push 24004, Push 17001(top) Label TTL action: prop-ttl, prop-ttl(top) Load balance label: Label 24004: None; Label 17001: None; Label element ptr: 0x780fb50 Label parent element ptr: 0x782f1d0 Label element references: 1 Label element child references: 0 Label element lsp id: 0 Session Id: 0x142 Protocol next hop: 10.255.0.1-100<c> Label operation: Push 24004 Label TTL action: prop-ttl Load balance label: Label 24004: None; Indirect next hop: 0x7199204 1048579 INH Session ID: 0x0 State: <Secondary Active Int Ext ProtectionCand> Local AS: 65000 Peer AS: 65000 Age: 4d 10:32:58 Metric: 0 Metric2: 30 Validation State: unverified ORR Generation-ID: 0 Task: BGP_65000.10.255.0.1 Announcement bits (1): 0-KRT AS path: ? Communities: target:65000:100 color:0:100 Import Accepted VPN Label: 24004 Localpref: 100 Router ID: 10.255.0.1 Primary Routing Table: bgp.l3vpn.0 Thread: junos-main Indirect next hops: 1 Protocol next hop: 10.255.0.1-100<c> Metric: 30 Label operation: Push 24004 Label TTL action: prop-ttl Load balance label: Label 24004: None; Indirect next hop: 0x7199204 1048579 INH Session ID: 0x0 Indirect path forwarding next hops: 1 Next hop type: Router Next hop: 10.0.3.2 via ge-0/0/1.0 Session Id: 0x142 10.255.0.1-100<c>/64 Originating RIB: inetcolor.0 Metric: 30 Node path count: 1 Forwarding nexthops: 1 Next hop type: Router Next hop: 10.0.3.2 via ge-0/0/1.0 Session Id: 0x0 5. traceroute による VPN の通信経路確認 PE1 -> PE2 PE1 -> P2 -> P1 -> PE2 と通過していることが観測できます。 RP/0/RP0/CPU0:PE1#traceroute 192.168.1.254 vrf 100 Mon Aug 15 13:39:24.020 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.1.2 [MPLS: Labels 17003/16 Exp 0] 15 msec 13 msec 3 msec 2 10.0.4.1 [MPLS: Labels 17003/16 Exp 0] 27 msec 8 msec 28 msec 3 192.168.1.254 3 msec 9 msec 2 msec PE2 -> PE1 逆順の PE2 -> P1 -> P2 -> PE1 と通過していることが観測できます。 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.2.1 (10.0.2.1) 2.871 ms 13.558 ms 2.522 ms MPLS Label=17001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=1 S=1 2 10.0.4.2 (10.0.4.2) 2.522 ms 2.372 ms 2.534 ms MPLS Label=17001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=2 S=1 3 10.0.1.1 (10.0.1.1) 10.075 ms * 4.672 ms リンク障害時の Flex-Algorithm 検証 前節で設定したネットワークにて1つのリンクを故意に閉塞し、障害時においても Algorithm 内でパスが切り替わる動作を確かめます。 落とすリンクは P1 と P2 の間です。 当該のリンクを落とした後も Algorithm 128 は有効なため、 Algorithm 128 内での最短パスに切り替わります。 切り替え前の P2 および PE2 のテーブル どちらも P1 を向いています。 P2 user@P2> show route table mpls.0 label 17003 17003 *[L-ISIS/14] 5d 20:37:15, metric 20 > to 10.0.4.1 via ge-0/0/2.0, Swap 17003 PE2 user@PE2> show route table mpls.0 label 17001 17001 *[L-ISIS/14] 01:52:13, metric 40 > to 10.0.2.1 via ge-0/0/0.0, Swap 17001 切り替え後の P2 および PE2 のテーブル どちらもメトリックが増え、直接それぞれ転送するようになります。 P2 user@P2> show route table mpls.0 label 17003 17003 *[L-ISIS/14] 00:00:29, metric 100 > to 10.0.3.1 via ge-0/0/1.0, Pop PE2 user@PE2> show route table mpls.0 label 17001 17001 *[L-ISIS/14] 00:00:22, metric 120 > to 10.0.3.2 via ge-0/0/1.0, Swap 17001 traceroute による VPN の通信経路確認 PE1 -> PE2 リンク障害時の図の通り PE1 -> P2 -> PE2 と通過していることが観測できます。 RP/0/RP0/CPU0:PE1#traceroute 192.168.1.254 vrf 100 Mon Aug 15 15:45:51.232 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.1.2 [MPLS: Labels 17003/16 Exp 0] 8 msec 12 msec 2 msec 2 192.168.1.254 10 msec 2 msec 8 msec PE2 -> PE1 逆順の PE2 -> P2 -> PE1 と通過していることが観測できます。 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.428 ms 1.969 ms 1.463 ms MPLS Label=17001 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=1 S=1 2 10.0.1.1 (10.0.1.1) 12.766 ms * 9.734 ms まとめ 本記事ではアンダーレイの分割や複数指標での最適経路計算ができる Flexible Algorithm ついて解説しました。 Multi-vendor での実装とリンク障害時の動作検証を紹介しました。 Flexible Algorithm はまだ draft 段階のため、機能追加が盛んで相互運用性も限定的ですが、ネットワーク網の意図を表現する方法として VPN の TE にも使用可能と考えられます。 次回の記事では、Flex-Algorithm のメトリックとしても使える、遅延測定値を基にした Delay-based TE を紹介予定です。 (2022/09/05 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #7] Delay-Based TE https://datatracker.ietf.org/doc/html/draft-ietf-lsr-flex-algo ↩