TECH PLAY

NTTドコモビジネス

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

613

はじめに こんにちは、クラウド&ネットワークサービス部で SDPF のベアメタルサーバー・ハイパーバイザーの開発をしている山中です。 先日 GitHub Actions self-hosted runners のオートスケーリング構成の紹介(クラウドサービス開発を支える CI の裏側) の記事で、自作の runner controller と Docker を用いた、オンプレミスでの CI 環境構成についてご紹介しました。 今回の記事では、構築した CI 環境上で動かしている workflow の紹介をしながら、workflow 作成についての Tips をいくつかご紹介したいと思います。 engineers.ntt.com 記事を書いたモチベーション 実際の業務で GitHub Actions を使用するにあたって、ありがちな悩みを解決するための workflow の作成事例や工夫などの記事が検索であまり見つからなかったためです。 どのような workflow を作成するかは開発現場によって様々であり、今回紹介する Tips も汎用的ではないものもありますが、他のチームはこんな風にこの機能を使ってるんだ〜みたいに読んでもらえればなと思います。 なお、この記事は GitHub Actions を普段使っている方向けに書いていることや、後ろのトピックほど私たちのチーム独自の内容になっていることをご了承ください トピック run_name、Job Summary で workflow の実行パラメータを見やすく composite action、reusable workflow による処理の再利用の例 複数リポジトリ間での機密情報の扱い方 独自キャッシュによる効率的なファイル共有 run_name、Job Summary で workflow の実行パラメータを見やすく 私たちのチームではリポジトリごとに単体テストの workflow を作成しています。 単体テストは workflow_dispatch で実行することになっており、PR に紐づくブランチを指定して手動で workflow を実行することになっています。 私たちのリポジトリでは、単体テストは環境の初期化やテスト項目数の多さなどにより時間がかかるものが多く、1並列でしかテストを流せないという制約もあります。 Pull Request(以下、PR)の更新などによって自動でテストを流してしまうと、開発者が望むタイミングでテストを流しづらくなってしまうため、workflow_dispatch を使っています。 workflow_dispatch の歯がゆい点 そんな workflow_dispatch ですが、GUI 上での見え方にやや難があり、以下のように workflow 実行時に指定したブランチがひと目で分かりません。 右上の Filter からブランチを絞り込んで表示することも可能ですが、わざわざ絞り込むのが手間です。 on: push などで workflow を実行した場合はブランチを表示してくれるのですが、workflow_dispatch の場合は workflow の name や実行者ぐらいしか表示してくれません。 run-name の活用 こんなときに役立つのが、 run-name です。 workflow ファイル内で run-name を定義すると、実行された run の名前をカスタマイズでき、GUI 上での見え方に変化を与えることができます。 例えば以下のように run-name を定義することで「どの run がどのブランチから実行されたのか」がひと目で分かるようになります。 run-name: ${{ github.workflow }} (${{ github.ref_name }}) また、workflow 実行時に指定した inputs も run-name で参照でき、以下のように run-name を定義することで「どの run がどのパラメータで実行されたのか」もひと目で分かるようにもできます。 run-name: ${{ github.workflow }} / ${{ inputs.environment }} (${{ inputs.scenario }}) Job Summaries の活用 ここで表示した inputs ですが、数が多くなってくると run-name に全て表示しきれなくなってきます。 そんなときは Job Summaries を使うのがおすすめです。 GitHub Actions では workflow 実行時に渡した inputs を見やすい場所に表示してくれないため、 どのような inputs で workflow を実行したのか辿りづらい のですが、 以下のような step を定義しておくと、inputs を job summary として run の画面に表示できるようになります。 steps: - name: post test summary uses: actions/github-script@v6 with: script: | parametersTable = [ [{ data: 'key', header: true}, { data: 'value', header: true }], ['branch', '${{ github.ref_name }}'] ] retryCommand = `gh workflow run --ref ${{ github.ref_name }} '${{ github.workflow }}'` for ([key, value] of Object.entries(context.payload.inputs)) { parametersTable.push([key, value]) retryCommand += ` -f ${key}='${value}'` } await core.summary .addRaw("### Test Parameters\n") .addTable(parametersTable) .addRaw("\n### Retry Command\n") .addCodeBlock(retryCommand) .write() workflow の再実行コマンドの表示 上の図の下部に Retry Command というコードブロックが表示されていますが、これも工夫点の1つです。 workflow の再実行をしたい場合、同じパラメータで再実行するのであれば re-run の機能を使えばよいですが、少しだけパラメータを変更して再実行したい場合、inputs が多いとパラメータの再入力が面倒です。 そこで、同じパラメータで workflow を実行するためのコマンドを run の画面に表示し、 パラメータを少しだけ変更して workflow を再実行するという操作を簡単に行える ようにしています。 composite action、reusable workflow による処理の再利用の例 GitHub Actions における処理の再利用といえば composite action や reusable workflow ですよね。 自分で独自に custom action を作成する場合、 選択肢としては Docker container や JavaScript を用いて作成する方法がありますが、個人的に一番使いやすいと思っているのが composite action です。 Docker container や JavaScript だと workflow ファイルの記法から外れて所定の形式に従って Dockerfile を書いたり JavaScript を書いたりする必要がありますが、composite action の場合は workflow ファイルと ほぼ同じ記法 で一連の処理を記述でき、学習コストやメンテナンスコストを抑えられます。 composite action で定義した step は呼び出し元の job で定義された step と同じように実行されるため、 処理をまとめて複数の workflow で使い回せるようにしたり、workflow ファイルの巨大化を防ぐ のに向いています。 composite actions の活用例 私たちのチームでは先程述べた単体テストを行うリポジトリが数多くあり、それぞれに workflow ファイルを作成しています。 数が多いと同じ処理は共通化したい欲が強くなるため、共通処理リポジトリを作成し、そこに様々な composite action を作成しています。 特に共通化の恩恵を受けているのが、以下のように workflow の最初と最後に定義している pre-processing と post-processing という job です。 jobs: pre-processing: runs-on: xxx steps: - uses: <org>/<repo>/.github/actions/unit-test/pre-processing@master lint: ... unit-test: ... post-processing: if: always() needs: unit-test runs-on: xxx steps: - uses: <org>/<repo>/.github/actions/unit-test/pre-processing@master pre-processing と post-processing では以下のような処理を行っています。 post-processing job は if: always() により workflow が途中で失敗しても必ず実行されるようにしています。 pre-processing 先程でも述べた workflow 実行時に渡した inputs の表示 workflow 実行時に指定したブランチに紐づく PR のステータス更新( checks を pending に変更) workflow_dispatch だと PR の checks が自動更新されないため、手動で更新する必要がある post-processing workflow 実行時に指定したブランチに紐づく PR のステータス更新(checks を success や error などに変更) テスト結果の slack 通知 最近リリースされた Slack や Microsoft Teams で workflow についてのイベントを subscribe する機能 も非常に便利ですが、複数リポジトリのテスト結果を1つの Slack channel に投稿する場合、どのリポジトリからの通知なのかが若干分かりづらいため、私のチームでは workflow 側に定義した Slack 通知でもうしばらく運用しようかなと思っています。 reusable workflows の活用例 reusable workflow も私たちのチームにとって非常にありがたい機能です。 私たちのチームではベアメタルサーバー 1 とハイパーバイザー 2 の2つのプロダクトを開発しているのですが、 ハイパーバイザーはベアメタルサーバーの機能をがっつり利用しているため、ハイパーバイザーの結合テストではベアメタルサーバーの環境構築も必要となります。 このような事情のため、以下のように reusable workflow を呼び出すことで、ベアメタルサーバーとハイパーバイザーの結合テストの処理の共通化をうまく行っています。 マイクロサービスアーキテクチャにおける結合テストで、結合先のサービスをモック化できず自前で構築しないといけない場合には、このような reusable workflow の使い方が向いているのではないかと思います。 name: ベアメタルサーバーの結合テスト jobs: initialize-baremetal-environment: uses: <org>/<repo>/.github/workflows/initialize_baremetal_environment.yml@master with: ... create-baremetal-instance: ... name: ハイパーバイザーの結合テスト jobs: initialize-baremetal-environment: uses: <org>/<repo>/.github/workflows/initialize_baremetal_environment.yml@master with: ... initialize-hypervisor-environment: uses: <org>/<repo>/.github/workflows/initialize_hypervisor_environment.yml@master with: ... create-hypervisor-instance: ... 複数リポジトリ間での機密情報の扱い方 workflow 内で扱うパスワード等の機密情報の格納先の第一候補は secrets ですよね。 ですが、 扱っているリポジトリが多く、各リポジトリで同じ機密情報を使いたい場合、各リポジトリに同じ secrets を設定していくのは手間 です。 Organization Administrator の権限があれば、Organization に secrets を設定して各リポジトリから参照することもできますが、Organization Administrator ではない開発者からするとこの方法は取りづらいです。 secrets はコードとして管理できるものでもないので構成管理もしづらいです。 機密情報の参照用の自作 action そこで私たちのチームで作成しているのが「機密情報の参照用の自作 action」です。 共通処理リポジトリに set-credentials という composite action を作成しており、この action を以下のように呼び出すことで、機密情報を環境変数に設定して利用可能にしています。 steps: - uses: <org>/<repo>/.github/actions/common/set-credentials@master with: decryption_key: ${{ secrets.DECRYPTION_KEY }} credential_names: test_key test_middleware_pasword - runs: bundle exec rsepc ← 環境変数 test_key と test_middleware_password が export された状態で実行される set-credentials action の中身は以下です。 name: set credentials description: set credentials inputs: decryption_key: required: true credential_names: required: true description: ex) 'hoge_password fuga_password' runs: using: composite steps: - name: set credentials to GITHUB_ENV working-directory: ${{ github.action_path }} run: | for credential_name in ${{ inputs.credential_names }}; do credential_value=$(openssl xxxx -d -in secret.yml.enc -pass pass:${{ inputs.decryption_key }} xxxx | yq -r .${credential_name}) if [ $credential_value == null ]; then continue fi echo "::add-mask::${credential_value}" echo "${credential_name}=${credential_value}" >> $GITHUB_ENV done shell: bash action のディレクトリに配置してある secret.yml.enc というファイルを inputs で与えた decryption_key で復号しつつ credentials_names で指定した値だけを抽出し、GITHUB_ENV に格納して job 内で利用可能にしています。 このとき add-mask の workflow command によってログに機密情報の文字列が直接表示されないようにしているのがポイントです。 この方法だと 各リポジトリに設定する secrets は1つで済む ことになり、それ以外の機密情報はコードとして構成管理できるので取り回しもしやすくなります。 (機密情報を環境変数に格納すると、悪意のある外部 action から情報を読み取られるリスクがあるため、その点は注意しながら使っています) 独自キャッシュによる効率的なファイル共有 長い workflow を作っていくと、どの部分から処理を再実行できるようにするかを意識して job を分割していくことになります。 job を分割していくと job 内で利用するファイルを job 間で共有したくなってきますが、こんなときに便利なのが GitHub Actions の機能として提供されている cache です。 この機能を使うことで、ブランチやタグの制約はありますが job 間や run 間、workflow 間でキャッシュしたファイル(ディレクトリ)を使い回すことができます。 公式の cache の注意点 ですが、この cache 機能では 機密情報のキャッシングを行うのは推奨されません 。 私たちの workflow では、複数の job 間で Ansible のコードを使いまわしてテスト環境の構築などを行いたいと思い、Ansible のコードをディレクトリ丸ごとキャッシングしようとしていたのですが、この Ansible にはパスワードなどの機密情報が入っているため GitHub 公式の cache action でキャッシングするのは好ましくありません。 独自の cache action そこで作成したのが以下のような独自 cache action です。 name: cache description: save/restore local directory to/from remote storage or remove outdated directory on remote storage inputs: key: required: false default: ${{ github.run_id }} path: required: false default: . action: required: true type: choice options: - save - restore - clean runs: using: composite steps: - name: save local directory to remote storage by rsync if: ${{ inputs.action == 'save' }} run: | <rsync コマンドにより指定したパスの内容をストレージサーバに保存> shell: bash - name: restore local directory from remote storage by rsync if: ${{ inputs.action == 'restore' }} run: | <rsync コマンドによりストレージサーバから指定したパスの内容を取得> shell: bash - name: delete content last accessed at before 1 week if: ${{ inputs.action == 'clean' }} run: | <ストレージサーバ上の古いファイルを削除> shell: bash inputs として key path action を与えて action を呼び出すことで、runner が動作する環境上の path の内容を、検証環境内のストレージサーバ上に key という名前で save したり、逆にストレージサーバ上の内容を runner 環境上へ restore できるように作っています。 検証環境内の転送速度はかなり高いため、多少サイズが大きいディレクトリをキャッシングしても読み書きにさほど時間はかかりません。 デフォルトでは key は github.run_id としており、実行した run 内でのみキャッシュが共有可能となるため、job 間でのデータ共有に特化した作りとなっています。 もちろん公式の cache でも紹介されているように hashFiles と組み合わせることでライブラリ部分だけをキャッシングするといった使い方もしています。 独自 cache のスコープ この仕組みは、検証環境内の self-hosted runner でのみ動作させる前提であるため、意図的にキャッシュのスコープを制限しないようにしています。 つまり、異なるブランチ同士でも、はたまた リポジトリの垣根を超えても、 key を適切に指定すればキャッシュを利用できるようにしています 。 こうすることで、公式の cache を超えたリポジトリ横断での効率的な CI を実現することが可能となっています。 キャッシュの削除も定期実行 workflow で行っているため、ストレージサーバの容量がいつの間にか膨れ上がっていた、ということも防げるようになっています。 余談 先述した reusable workflow を使いつつ、job 間の依存関係を解きほぐしながら並列実行できる部分を積極的に並列実行するようにした結果、以下のような長い workflow ができあがりました。(workflow 自体の時間を短くできて re-run できる箇所も多くて良いのですが見づらい、、) さいごに いかがでしたでしょうか? GitHub Actions には様々な機能があり、それらを組み合わせてオリジナルの workflow を作っていく楽しさがあります。 みなさんも時間があるときに GitHub Actions の公式ドキュメントを全部読み漁って、自分の workflow に適した機能をぜひ見つけてください。 次回の記事では CD にフォーカスした内容についてご紹介をしたいと思います。 CD は CI よりも課題が多く現在も検討を続けている最中のため、具体的な方針が固まったら記事を執筆する予定です。 最後になりますが、SDPF クラウドは国内最大級のクラウドサービスです。 開発メンバーは、数千台以上の物理サーバーの操作の自動化をはじめとした、技術的難易度の高い課題に取り組みつつ、日々より良いサービスにしようと邁進しております。 今回紹介した workflow を活用した 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/ ↩
はじめに こんにちは、イノベーションセンターの竹中です。 本記事では、SR-TE を一元的に管理するためのソフトウェア実装である Pola PCE の概要や利用方法について紹介します。 Pola PCE は私と 三島 で実装し、現在 NTT Com より OSS 公開中です。 [Multi-AS Segment Routing 検証連載 #11] PCE 実装の検証 記事でも一部機能を紹介しましたが、本記事では Example を用いた試用環境の準備、ベンダールーターを用いての環境準備、また各機能について紹介します。 また、Go で開発したツールを OSS として公開する中で得た知見を NTT Communications Advent Calendar 2022 8日目 で紹介しているので、こちらも是非ご覧ください。 目次 Pola PCE 概要 Pola PCE とはどのような特徴を持つ PCE であるか、またその活用方法について記載 Docker 環境を用いた Explicit Path 機能の検証 手元の Docker 環境のみで Pola PCE を検証する手順を紹介 Multi-vendor で構成されるネットワークを用いた Dynamic Path 機能の検証 Multi-vendor 機器を用いて構築された SR-MPLS 環境において Pola PCE を利用する手順を紹介 SR-TE や PCE に興味を持ち、簡潔な構成で Pola PCE の機能を試してみたい方は、まずは Docker 環境を用いた検証をお勧めします。 なお PCE とは何か、については下記記事にて紹介しているためこちらも併せて参考にしてください。 engineers.ntt.com 開発へのモチベーション Pola PCE の紹介の前段として、開発に至ったモチベーションについて話します。 開発のモチベーションは大きく分けて 3 つあります。 1 つ目は、最新 RFC や Internet-Draft で議論中の PCE に関する機能を先行して実装し、検証したいという要求です。 自作の PCE を準備することで、RFC や Internet-Draft の更新に合わせてリアルタイムに実装状況を更新し、自作 PCE - 各社ベンダー製 PCC の相互接続検証を行いたいです。 2 つ目は、Multi-vendor 対応な PCE を利用したいという要求です。 Multi-AS Segment Routing 検証連載 で公開していますが、私たちは Multi-vendor 機器環境で Segment Routing の検証を行なっています。そのため、PCE についても Multi-vendor 対応な製品を利用したいです。 PCE の技術仕様に関しては RFC や Internet-Draft で未提案な部分もあり、PCC 機能にはベンダーごとに独自実装の部分も存在します。そのため、現状 Multi-vendor 環境において Color や Preference の情報などを含む PCEP の相互接続はまだできません。自作 PCE によってそのような問題の解決したいです。 3 つ目は、外部連携用の API を提供する軽量なツールが欲しいという要求です。 必要に応じて機能の分割や拡張などが行えるようにマイクロサービスアーキテクチャを目指し、機能ごとに分かれたコンポーネントを API で連携する仕組みを利用したいです。例えば SR Policy 投入機能は独立して動作させたり、トポロジーを描写するツールと連携させて利用したいです。 以上のモチベーションから Pola PCE を実装しました。 Pola PCE 概要 できること Pola PCE では現在(v1.1.2) SR-MPLS で構成されるネットワークにおいて 以下の機能が利用可能です。 PCC へ SR Policy の追加・更新 PCC で利用中の SR Policy の確認 GoBGP との連携による TED の取得 TED を保持することで経路計算が可能になり、Dynamic Path が利用可能 Multi-vendor 環境での利用 IOS XR、Junos、FRRouting を PCC として利用可能 マイクロサービスアーキテクチャに基づいた実装 Pola PCE はマイクロサービスアーキテクチャに基づいた実装となっており、それぞれのプロセス間の連携を gRPC が担っています。 そのため、各利用者の用途に応じて必要な機能のみを組み合わせて利用できます。 最小構成であれば Pola PCE 単体で SR Policy の管理や Explicit Path の追加や更新ができる簡素な SR Policy 管理ツールとして利用できます。 また Pola PCE のデーモンが gRPC インターフェースを提供しているため、デーモンを中核として、例えば以下のような高機能なネットワークコントローラを構築することも可能です。 構成 Pola PCE の構成は以下の通りです。 ツール単体は polad / pola CLI tool からなります。 polad は PCE デーモンであり、PCEP Session の管理や必要に応じたトポロジー情報の取得・管理、また、保持している情報を出力するための gRPC インターフェースを提供します。 pola CLI tool は polad の gRPC クライアントとなっており、入力したコマンドに応じて適切な gRPC リクエストを送信し polad の保持する情報を取得・更新します。 トポロジー情報の取得の必要があれば GoBGP と連携させたり、gRPC インターフェースを提供しているためユーザーが polad の gRPC クライアント機能をもつツールを用意することで Pola PCE を制御することも可能です。 実装予定の新機能 私たちはチームの取り組みとして SR-MPLS だけでなく SRv6 環境での検証も行っております。 SRv6 環境での利用も想定しているため、今後の直近のアップデート予定として PCEP の SRv6 対応 を開発中です。プロトコル拡張自体がまだ Internet-Draft で議論中のため、各ベンダーの SRv6 PCC 機能の実装状況や独自実装の内容を把握しつつ、Multi-vendor で動作する SRv6 PCE へと拡張する予定です。 Docker 環境を用いた Explicit Path 機能の検証 公開中の example を用いて動作検証をします。検証環境の構築には OSS の Docker ベースなネットワークエミュレータである tinet を利用しているため、Docker が利用できる x86_64 CPU 機器が1台あれば手元で試せます。 検証環境 公開中の spec.yaml を用いて、tinet により以下のトポロジーを作成します。 SR-MPLS ドメインは FRRouting version:latest (2022年12月5日現在では v8.4.1) を用いて構築されます。 手順 Docker、tinet のインストールについては手順を省略します。 pola リポジトリの取得 GitHub から pola リポジトリを clone します。 user@server:~$ git clone https://github.com/nttcom/pola トポロジーの構築・機器への config 投入 ディレクトリを sr-mpls_l3vpn へ変更したのちに tinet コマンドを 1 つ実行すると全環境構築が完了します。 user@server:~$ cd pola/examples/sr-mpls_l3vpn/ user@server:~/pola/examples/sr-mpls_l3vpn$ tinet upconf | sudo sh -x docker ps でトポロジー図に記載してある、各機器名称に従ったコンテナが作成・起動していることが確認できます。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fe439ecd0a78 host_ubuntu:latest "bash" 25 minutes ago Up 25 minutes host02 0b17cd9386aa host_ubuntu:latest "bash" 25 minutes ago Up 25 minutes host01 f92da0d39aea frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes p02 d5093e6fa5c1 frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes p01 8341a6836618 frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes pe02 a24a70528e1a frr:latest "/sbin/tini -- /usr/…" 26 minutes ago Up 25 minutes pe01 25c3666b6f17 pola:latest "bash" 26 minutes ago Up 26 minutes pola なお、Pola PCE や FRRouting の初期設定は example の spec.yaml に記載しているため、気になる方はご確認ください。 PCEP Session の確認 pola session コマンドによって PCEP Session を確認します。 docker exec で pola コンテナに入り、コマンドを実行します。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pola /bin/bash root@pola:/# pola session sessionAddr(0): 10.0.255.1 SR Policy の発行 SR Policy のパラメータ指定する yaml ファイルを作成し、 pola sr-policy add コマンドで SR Policy を PCC に発行します。 本検証では、pe01 から pe02 への VPN 経路(color 1)に対して、pe01 -> p01 -> p02 -> pe02 を通るようにするための SR Policy を発行します。 作成する yaml ファイル # policy1.yaml srPolicy : name : "policy1" pcepSessionAddr : "10.0.255.1" srcAddr : "10.255.0.1" dstAddr : "10.255.0.3" color : 1 segmentList : - sid : 16002 nai : "10.255.0.2" - sid : 16004 nai : "10.255.0.4" - sid : 16003 nai : "10.255.0.3" コマンド実行 root@pola:/# pola sr-policy add -f policy1.yaml --no-link-state success! 作成した SR Policy は pola sr-policy list コマンドで確認ができます。 ※ 現在 FRRouting で利用されている SR Policy の Color、Preference 項目は pola sr-policy list から確認できない状態です。FRRouting の PCEP RFC 対応が進むと確認できるようになります。 root@pola:/# pola sr-policy list LSP(0): PcepSessionAddr: 10.0.255.1 PolicyName: policy1 SrcAddr: 10.0.255.1 DstAddr: 10.255.0.3 Color: 0 Preference: 0 DstAddr: 10.255.0.3 SegmentList: 16002 -> 16004 -> 16003 経路と SR Policy の紐付け FRRouting の route-map を用いて、SR Policy と BGP で広告される経路とを紐付けます。 pe01 コンテナに入り、route-map を適用する config を投入します。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pe01 /bin/bash bash-5.1# vtysh -c 'conf t' -c 'router bgp 65000' -c 'address-family ipv4 vpn' -c 'neighbor 10.255.0.3 route-map color1 in' 疎通確認 疎通確認を行います。 まずは host01 から host02 へ ping が通ることを確認します。 user@server:~$ docker exec -it host01 ping -c 5 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=62 time=0.198 ms 64 bytes from 192.168.1.2: icmp_seq=2 ttl=62 time=0.126 ms 64 bytes from 192.168.1.2: icmp_seq=3 ttl=62 time=0.122 ms 64 bytes from 192.168.1.2: icmp_seq=4 ttl=62 time=0.110 ms 64 bytes from 192.168.1.2: icmp_seq=5 ttl=62 time=0.112 ms --- 192.168.1.2 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4098ms rtt min/avg/max/mdev = 0.110/0.133/0.198/0.032 ms また、host01 から host02 へ ping を打っている状態で pe01 の net0 interface を通るパケットをキャプチャします。 net0 interface から ping のパケットが出ていることと、SR Policy で指定した MPLS ラベルが積まれていることを確認できます。 host01 user@server:~$ docker exec -it host01 ping 192.168.1.2 pe01 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pe01 tcpdump -i net0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on net0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 11:24:48.358826 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 12, length 64 11:24:49.382800 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 13, length 64 11:24:49.957688 IP 10.0.0.2 > 224.0.0.5: OSPFv2, Hello, length 48 11:24:50.406778 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 14, length 64 11:24:51.430769 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 15, length 64 11:24:52.454779 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 16, length 64 Multi-vendor で構成されるネットワークを用いた Dynamic Path 機能の検証 GitHub から Pola PCE のバイナリファイルをダウンロードし、SR-MPLS を用いた Multi-vendor L3VPN 環境で検証します。 検証環境 以下のトポロジーを用いて検証します。 各ルーターの製品とバージョンは以下の通りです。 rt01: ASR9901(IOS XR 7.6.1) rt02: MX204(Junos 22.1R1.10) rt03: ASR9901(IOS XR 7.5.1) rt04: MX204(Junos 21.4R1.12) rt05: Cisco 8201(IOS XR 7.5.1) rt06: PTX10001-36MR(Junos 21.2R1.15-EVO) rt07: ASR9902(IOS XR 7.6.1) rt08: MX204(Junos 21.4R1.12) 手順 本検証では Dynamic Path 機能を検証します。 Dynamic Path 検証にあたって、Pola PCE が経路計算に利用するトポロジー情報を取得する手段として GoBGP を利用します。SR-MPLS ドメインの任意のルーターと GoBGP で BGP-LS Session を確立し、かつ Pola PCE と連携することで Pola PCE が Traffic Engineering Database (TED) としてトポロジー情報を保持します。 そのため、本章では GoBGP を Pola PCE で扱うための方法についても説明します。 Pola PCE / GoBGP のバイナリダウンロード Pola PCE と GoBGP のバイナリファイルをダウンロードします。 Pola PCE ダウンロード user@pce:~$ wget https://github.com/nttcom/pola/releases/download/v1.1.2/pola_1.1.2_linux_amd64.tar.gz user@pce:~$ tar -zxvf pola_1.1.2_linux_amd64.tar.gz user@pce:~$ sudo install -t <path が通っているディレクトリ> polad user@pce:~$ sudo install -t <path が通っているディレクトリ> pola GoBGP ダウンロード GoBGP は daemon (gobgpd) と CLI ツール (gobgp) の 2 つの実行ファイルから構成されますが、今回利用する実行ファイルは gobgpd のみになります。 user@pce:~$ wget https://github.com/osrg/gobgp/releases/download/v3.9.0/gobgp_3.9.0_linux_amd64.tar.gz user@pce:~$ tar -zxvf gobgp_3.9.0_linux_amd64.tar.gz user@pce:~$ sudo install -t <path が通っているディレクトリ> gobgpd gobgpd の起動 gobgpd は toml (または yaml、json) 形式のファイルに config を記載し、実行します。 トポロジー情報を取得するため、gobgpd を起動して RR である rt03 / rt04 と BGP-LS の Session を確立します。 作成する toml ファイル # gobgpd_cfg.toml [global.config] as = 65001 router-id = "10.99.0.254" [[neighbors]] [neighbors.config] neighbor-address = "10.255.1.3" peer-as = 65001 [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ls" [[neighbors]] [neighbors.config] neighbor-address = "10.255.1.4" peer-as = 65001 [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ls" gobgpd 起動 user@pce:~$ sudo gobgpd -f gobgpd_cfg.toml -l debug {"level":"info","msg":"gobgpd started","time":"2022-12-08T10:53:05Z"} {"Topic":"Config","level":"info","msg":"Finished reading the config file","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"config","level":"info","msg":"Add Peer","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"Peer","level":"info","msg":"Add a peer configuration","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"config","level":"info","msg":"Add Peer","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"Peer","level":"info","msg":"Add a peer configuration","time":"2022-12-08T10:53:05Z"} {"Duration":0,"Key":"10.255.1.3","Topic":"Peer","level":"debug","msg":"IdleHoldTimer expired","time":"2022-12-08T10:53:05Z"} {"Duration":0,"Key":"10.255.1.4","Topic":"Peer","level":"debug","msg":"IdleHoldTimer expired","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"Peer","level":"debug","msg":"state changed","new":"BGP_FSM_ACTIVE","old":"BGP_FSM_IDLE","reason":{"Type":7,"BGPNotification":null,"Data":null},"time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"Peer","level":"debug","msg":"state changed","new":"BGP_FSM_ACTIVE","old":"BGP_FSM_IDLE","reason":{"Type":7,"BGPNotification":null,"Data":null},"time":"2022-12-08T10:53:05Z"} 現時点では対向の rt03 / rt04 に BGP-LS Session を確立するための config が投入されていないため、BGP Session はまだ確立しません。 BGP-LS Session の確立 rt03 / rt04 に BGP-LS Session を確立するための config を投入します。 IOS XR / Junos 共に、BGP-LS Session を確立するための config を記載します。 IOS XR router isis 1 distribute link-state instance-id 32 ! ! router bgp 65001 address-family link-state link-state ! neighbor-group pola remote-as 65001 timers 10 30 update-source Loopback0 address-family link-state link-state ! ! neighbor 10.99.0.254 use neighbor-group pola BGP-LS Session の UP を確認します。 RP/0/RSP0/CPU0:rt03#show bgp link-state link-state summary Thu Dec 8 20:25:17.394 JST BGP router identifier 10.255.1.3, local AS number 65001 BGP generic scan interval 60 secs Non-stop routing is enabled BGP table state: Active Table ID: 0x0 RD version: 221 BGP main routing table version 221 BGP NSR Initial initsync version 65 (Reached) BGP NSR/ISSU Sync-Group versions 0/0 BGP scan interval 60 secs BGP is operating in STANDALONE mode. Process RcvTblVer bRIB/RIB LabelVer ImportVer SendTblVer StandbyVer Speaker 221 221 221 221 221 0 Neighbor Spk AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down St/PfxRcd 10.99.0.254 0 65001 7 51 221 0 0 00:00:59 0 Junos set policy-options policy-statement TE term 1 from family traffic-engineering set policy-options policy-statement TE term 1 then accept set protocols bgp group pola type internal set protocols bgp group pola local-address 10.255.1.4 set protocols bgp group pola family traffic-engineering unicast set protocols bgp group pola export TE set protocols bgp group pola neighbor 10.99.0.254 set protocols mpls traffic-engineering database import policy TE BGP-LS Session の UP を確認します。 user@rt04> show bgp group pola Group Type: Internal AS: 65001 Local AS: 65001 Name: pola Index: 2 Flags: <Export Eval> Export: [ TE ] Options: <GracefulShutdownRcv> Holdtime: 90 Preference: 0 Graceful Shutdown Receiver local-preference: 0 Total peers: 1 Established: 1 10.99.0.254+42121 lsdist.0: 0/0/0/0 polad の起動 polad は yaml 形式のファイルに config を記載し、実行します。 トポロジー情報の利用を有効化して GoBGP からトポロジー情報を取得するための config を記載します。 PCEP に関しては、PCC と疎通性のあるアドレス、PCEP のデフォルトポートである 4189 を設定しています。 また、GoBGP は gRPC のデフォルトポートである 50051 で gRPC リクエストを待ち受けています。そのため以下の config では GoBGP の gRPC クライアントの接続先ポートを 50051 とし、Pola PCE が提供する gRPC サーバは 50052 で待ち受けるように設定しています。 作成する yaml ファイル # polad_cfg.yaml global : pcep : address : "10.99.0.254" port : 4189 grpc-server : address : "127.0.0.1" port : 50052 log : path : "./pola_log/" name : "polad.log" ted : enable : true source : "gobgp" gobgp : grpc-client : address : "127.0.0.1" port : 50051 polad 起動 user@pce:~$ mkdir pola_log user@pce:~$ polad -f polad_cfg.yaml 2022-12-11T08:06:23.233Z info gRPC listen {"listenInfo": "127.0.0.1:50052", "server": "grpc"} 2022-12-11T08:06:23.233Z info PCEP listen {"listenInfo": "10.99.0.254:4189"} 2022-12-11T08:06:23.249Z info Request TED update {"source": "GoBGP", "session": "127.0.0.1:50051"} 2022-12-11T08:06:23.249Z info Update TED Update TED のログが出力されていれば、gobgpd との接続が完了し TED の更新が完了しています。 PCEP Session の確立 PCC とする rt01 / rt02 へ PCEP Session を確立するための config を投入します。 IOS XR / Junos 共に、PCEP Session を確立するための config を記載します。 IOS XR segment-routing traffic-eng pcc source-address ipv4 10.255.1.1 pce address ipv4 10.99.0.254 ! ! ! ! PCEP Session が確立していることを rt01 で確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng pcc ipv4 peer Sun Dec 11 17:41:06.157 JST PCC's peer database: -------------------- Peer address: 10.99.0.254, Precedence: 255, (best PCE) State up Capabilities: Stateful, Update, Segment-Routing, Instantiation Junos set protocols mpls lsp-external-controller pccd set protocols source-packet-routing lsp-external-controller pccd set protocols pcep pce pola local-address 10.255.1.2 set protocols pcep pce pola destination-ipv4-address 10.99.0.254 set protocols pcep pce pola pce-type active set protocols pcep pce pola pce-type stateful set protocols pcep pce pola lsp-provisioning set protocols pcep pce pola spring-capability PCEP Session が 確立していることを rt02 で確認します。 user@rt02> show path-computation-client status Session Type Provisioning Status Uptime pola Stateful Active On Up 102 LSP Summary Total number of LSPs : 0 Static LSPs : 0 Externally controlled LSPs : 0 Externally provisioned LSPs : 0/16000 (current/limit) Orphaned LSPs : 0 pola (main) Delegated : 0 Externally provisioned : 0 Pola PCE 各種情報の確認 PCC と PCEP Sessoin が確立できたため、Pola PCE の持つ各種情報を確認します。 Pola PCE の操作は pola CLI コマンドを用いて行います。 PCEP Session の確認 PCEP Session を確認します。 user@pce:~$ pola session -p 50052 sessionAddr(0): 10.255.1.1 sessionAddr(1): 10.255.1.2 TED の確認 gobgpd から取得したトポロジー情報を確認します。 user@pce:~$ pola ted -p 50052 Node: 1 0000.0aff.0107 Hostname: rt07 ISIS Area ID: 49.0000 SRGB: 16000 - 24000 Prefixes: 10.255.1.7/32 index: 7 Links: Local: 10.1.4.2 Remote: 10.1.4.1 RemoteNode: 0000.0aff.0106 Metrics: IGP: 10 TE: 10 Adj-SID: 24008 Local: 10.1.12.2 Remote: 10.1.12.1 RemoteNode: 0000.0aff.0105 Metrics: IGP: 10 TE: 10 Adj-SID: 24002 <snip> SR Policy の発行 SR Policy を表す yaml ファイルを作成し、pola sr-policy add コマンドで SR Policy を PCC に発行します。 本検証では、color 100 が付与されている rt01 から rt02 への VPN 経路・ rt02 から rt01 への VPN 経路それぞれに対して、TE メトリックに従った経路を通るための SR Policy を発行します。TE メトリックはトポロジー図に記載した設定をしているため、パケットはトポロジー図中青線の通りの経路を通ります。 作成する yaml ファイル # policy_dynamic_rt01.yaml asn : 65001 srPolicy : pcepSessionAddr : "10.255.1.1" name : "dynamic_rt01" srcRouterId : "0000.0aff.0101" dstRouterId : "0000.0aff.0102" color : 100 type : "dynamic" metric : "te" # policy_dynamic_rt02.yaml asn : 65001 srPolicy : pcepSessionAddr : "10.255.1.2" name : "dynamic_rt02" srcRouterId : "0000.0aff.0102" dstRouterId : "0000.0aff.0101" color : 100 type : "dynamic" metric : "te" コマンド実行 user@pce:~$ pola sr-policy add -f policy_dynamic_rt01.yaml -p 50052 success! user@pce:~$ pola sr-policy add -f policy_dynamic_rt02.yaml -p 50052 success! 作成した SR Policy は pola sr-policy list コマンドで確認ができます。 user@pce:~$ pola sr-policy list -p 50052 LSP(0): PcepSessionAddr: 10.255.1.1 PolicyName: dynamic_rt01 SrcAddr: 10.255.1.1 DstAddr: 10.255.1.2 Color: 100 Preference: 100 DstAddr: 10.255.1.2 SegmentList: 16005 -> 16008 -> 16006 -> 16002 LSP(1): PcepSessionAddr: 10.255.1.2 PolicyName: dynamic_rt02 SrcAddr: 10.255.1.2 DstAddr: 10.255.1.1 Color: 100 Preference: 100 DstAddr: 10.255.1.1 SegmentList: 16006 -> 16008 -> 16005 -> 16001 PCC で受けとった SR Policy の確認 各 PCC で受け取った経路が有効化されていることを確認します。 IOS XR RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Mon Dec 12 07:41:10.566 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:03:14 (since Dec 12 07:37:56.127) Candidate-paths: Preference: 100 (PCEP) (active) Name: dynamic_rt01 Requested BSID: dynamic PCC info: Symbolic name: dynamic_rt01 PLSP-ID: 3 Protection Type: unprotected-preferred Maximum SID Depth: 10 Dynamic (pce 10.99.0.254) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16008 [Prefix-SID, 10.255.1.8] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] 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 Max Install Standby Candidate Paths: 0 Junos user@rt02> show spring-traffic-engineering lsp detail Name: dynamic_rt02 Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.1.1-100<c> State: Up Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 4 Hop 1 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.6 SID type: 20-bit label, Value: 16006 Hop 2 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.8 SID type: 20-bit label, Value: 16008 Hop 3 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.5 SID type: 20-bit label, Value: 16005 Hop 4 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.1 SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) 本検証で発行した SR Policy は、BGP Color Extended Community の 100 が付加されている VPN 経路に対して適用されます。Color の付加方法は [Multi-AS Segment Routing 検証連載 #4] Color-Based Steering in Single-AS で紹介しているため、こちらの記事を参考にしてください。 疎通確認 疎通確認を行います。 rt01 の Customer ネットワークと rt02 の Customer ネットワークで相互に traceroute を行い、経路を確認します。 事情により rt06 を通る経路の traceroute は当該ホップの結果で * * * と表示されています。 rt01 -> rt02 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf Customer Mon Dec 12 08:30:46.261 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16008/16006/16002/130 Exp 0] 26 msec 4 msec 4 msec 2 10.1.5.2 [MPLS: Labels 16006/16002/130 Exp 0] 1 msec 1 msec 2 msec 3 * * * 4 192.168.1.254 1 msec 1 msec 1 msec rt02 -> rt01 user@rt02> traceroute 192.168.0.254 routing-instance Customer no-resolve traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 * * * 2 10.1.11.2 0.906 ms 0.837 ms 1.177 ms MPLS Label=16005 CoS=0 TTL=1 S=0 MPLS Label=16001 CoS=0 TTL=2 S=0 MPLS Label=24013 CoS=0 TTL=2 S=1 3 10.1.5.1 2.108 ms 2.132 ms 3.897 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24013 CoS=0 TTL=3 S=1 4 10.1.1.1 3.844 ms * 3.963 ms トポロジー図に記載の通りの経路となっていることが確認できます。 まとめ 本記事では Pola PCE の概要と手元の Docker 環境・ Multi-vendor ネットワーク環境での利用方法について紹介しました。 気軽に試験環境を作成できるため是非お手元で試してみてください! 質問や気になった点、機能追加も大歓迎です。 ブログコメントや GitHub Issues/PRs を是非お願いします! GitHub - nttcom/pola: PCEP Library and Stateful PCE Implementation with Go
この記事は、 NTTコミュニケーションズ Advent Calendar 2022 20日目の記事です。 こんにちは。コミュニケーション&アプリケーションサービス部の石井です。 普段の業務では文章要約技術を用いたAPIサービス 1 の開発・運用に取り組んでおります。 この記事ではグラフニューラルネットワーク(GNN)、特に Heterogeneous Graph(異種グラフ) を扱ったGNNについて紹介していこうと思います。 本記事で扱う内容 この記事で取り扱う内容は以下です。 グラフニューラルネットワーク(GNN)とは Heterogeneous Graph(異種グラフ) 機械学習におけるグラフベースの問題設定 Pytorch-geometricによるモデル構築 GNNの概要と Heterogeneous Graph について簡単に説明をした後に、実際にモデルを作成していく流れで展開していきます。 本記事ではアルゴリズムの詳細な解説などは省略しますので、より深く興味がある方はリンクをつけておきますので論文や解説記事を参照してみてください。 グラフニューラルネットワークとは グラフニューラルネットワーク(GNN)とはグラフで表現されたデータを深層学習で扱うためのニューラルネットワーク手法の総称です。グラフデータから表現抽出をして目的のタスクを解くというEnd2Endアプローチによる機械学習アルゴリズムとなります。メジャーな手法としては GCN 2 や GraphSAGE 3 などがあります。 GNN の仕組みについて GCN の手法を元に簡単に記載すると、グラフの頂点(ノード)の特徴量に対して隣接するノードの特徴量に重みを掛けたものを加えていく演算をすることで、対象ノードにグラフ構造の情報を加味した表現を獲得させるといった動きをします。より詳しい説明については distill 4 というサイトに「 Understanding Convolutions on Graphs 」というGNNの解説記事があるのでそちらを参考にしてみてください。 Heterogeneous Graph Heterogeneous Graphを理解する前提としてまずはグラフの定義から話していきます。 グラフ理論において、グラフとは頂点を示すノードとその間の関係であるエッジから表現されるデータ構造になります。つまり、ノード間をエッジで繋ぐことによってノードによるネットワーク構造を表現したものが、一般的によく目にするグラフと呼ばれるものになります。これを数式的に定義すると以下のようになります。 : ノードの集合、 : エッジの集合 そして、このグラフにおいて1種類のノードと、同じ意味合いのエッジによって関係を示したものを Homogeneous Graph と呼びます。一方で、複数の多様なノードとエッジを含んでいる関係のグラフを Heterogeneous Graph と言います。例えば、ソーシャルネットワークのような人と人の間を交友関係でリンクしたグラフは Homogeneous Graph であり、店舗利用関係のような人と店舗の間を購買実績でリンクしたグラフは Heterogeneous Graph となります。想起しやすいように図で示すと以下のようになります。 ちなみに GNN ベースのアルゴリズムの多くは入力が単一のノードとエッジを持つ、Homogeneous Graph を対象とした手法となっています(最近では HAT 5 のような Heterogeneous Graph を対象としたアルゴリズムも増えてきています)。その上で、なぜ Heterogeneous Graph を扱う必要があるのかという問いについてですが、現実世界において観測対象をグラフ表現で構造化しようとした場合に、複数のノードまたはエッジによる関係を定義する頻度が高いからです。ある人と人の関係を構造化する場合よりも、ある人と物やサービスの関係を構造化する場合の方がパターンが多いのは容易に想像できます。 と、ここまで Heterogeneous Graph の話をしてきましたが、そもそも GNN 自体が深層学習分野の中でも近年注目されている技術であり、データ分析競技協会である KDD CUP 2021 では「 OGB-LS 」というカテゴリで3つのグラフTaskが取り扱われるなどその注目度の高さが伺えます。また、今年開催された国際会議である KDD 2022 のResearch Trackの中では全254の論文の中で約80以上もの論文がグラフに関連した内容を取り扱うなどトレンド領域の1つとなっていることが分かりますね。 グラフにおける問題設定 次にグラフにおける問題設定について少し触れます。 通常の機械学習の問題設定では分類や回帰といったタスクを一般的に考えますが、グラフを扱う場合にはグラフに適用した問題設定を検討する必要があります。この問題設定には大きく3つの種類があります。 1つはノードを対象としたタスク(Node Centric)で、グラフ中におけるノード単位の分類や回帰といったタスクを扱います。2つめはグラフを対象としたタスク(Graph Centric)で、グラフ単位として分類や回帰といったタスクを扱います。グラフ単位のタスクは活用イメージがしづらいかと思いますが、化合物の分類などグラフが複数存在するパターンを想像してもらえると分かりやすいかと思います。最後はエッジを対象としたタスク(Edge Centric)で、各々ノード間のエッジに対して予測をして、「エッジが形成されるのか」や「エッジのクラスは何か」といったタスクを扱います。このようにグラフを扱う場合には自身の目的に応じたタスク設計が必要になるため、問題設定をしっかり検討した上で必要なデータ収集や実装を行っていきます。 また、もう少しタスクの補足をしておくと、上記の問題設定に加えて「trunsductive」と「inductive」と言う学習と推論時の状況について考慮しておくことも重要になります。 「trunsductive」とは学習と推論で同じグラフを扱う場合のことを指し、「inductive」は学習データにない新しいグラフを扱う場合のことを示します( semi-inductive 6 といった考え方も存在します)。なぜこのような問題設定の違いを意識する必要があるかというと、これは GNN のモデル構築にて選択するアルゴリズムが異なってくるためになります 7 。そのため、「trunsductive」と「inductive」どちらかによって選択可能なアルゴリズムに制約が出てくることに注意してください。 ただ、一般的には新しい未知のノードやエッジに対して予測を行いたいといった場合の活用シーンの方が多いと考えられるため、「inductive」な問題設定をベースとして考えておけばまずは良いかと思います。 実際に試してみた ここからは実際に Heterogeneous Graph を扱った GNN のモデルを構築してみようと思います。 今回は Kaggle で公開されている「 Recipes and Reviews 」のオープンデータを利用します。このデータは Food.com 8 と言う海外のレシピ共有サイトより料理レシピとそのレシピに対してのユーザレビューの情報をデータ収集したものになります。料理レシピのデータにはレシピにおけるメタ情報と定量的な栄養素といった特徴量を含んでおり、ユーザレビューのデータはあるユーザが該当する料理レシピを5段階で評価した内容が含まれています。データサイズについても500,000以上のレシピ数と1,400,000のレビュー数があるため比較的ボリュームの大きいデータとなっています。 利用するフレームワークですが、 Pytroch-geometric を用いて実装を行っていきます 9 。Pytorch-geometricでは、バージョン2.0.0より Heterogeneous Graph をサポートしています。そのため、Heterogeneous Graph を対象とするモデルを作成する場合にはバージョンに注意してご利用ください。 では、問題設定としては以下を考えてみようと思います。 ユーザとレシピの関係による二部グラブ構造の Heterogeneous Graph を定義して、ユーザがレシピに興味・関心を示すかを予測するタスク(リンク予測)を解こうと思います。データ内容はユーザによるレシピの評価を意味するため、評価したという事実を興味・関心があるという区分として扱うのは厳密には正しくはないとは思いますが、今回は便宜上このような問題設定とします。 データ準備 グラフデータ整形 データの読み込みからテーブルデータをグラフデータに変換するための処理を記述します。元となるデータは data/ のフォルダに配置しているためご自身の環境に合わせて適切に設定してください。 各ノードについてのID割り当てと各ノードIDによる COO形式 10 での集合リストによってエッジを表現してグラフデータを定義します。 import os import numpy as np import pandas as pd from tqdm import tqdm from sklearn.preprocessing import StandardScaler from sklearn.metrics import roc_curve, roc_auc_score import matplotlib.pyplot as plt import torch import torch.nn.functional as F from torch import Tensor from torch.nn import Module import torch_geometric import torch_geometric.transforms as T from torch_geometric.nn import SAGEConv, to_hetero from torch_geometric.data import HeteroData from torch_geometric.loader import LinkNeighborLoader # データの読み込み(pandas) df_recipes = pd.read_csv( '../data/food/recipes.csv' ) df_reviews = pd.read_csv( '../data/food/reviews.csv' ) # データ準備 df_reviews = df_reviews[df_reviews.RecipeId.isin(df_recipes[ "RecipeId" ].unique())] # 不要データ除外 df_recipes[ 'RecipeServings' ] = df_recipes[ 'RecipeServings' ].fillna(df_recipes[ 'RecipeServings' ].median()) # 欠損値補完 # ユーザノードとレシピノードのIDマップ作成 unique_user_id = df_reviews[ "AuthorId" ].unique() unique_user_id = pd.DataFrame( data={ "user_id" : unique_user_id, "mappedID" : pd.RangeIndex( len (unique_user_id)), } ) unique_recipe_id = df_reviews[ "RecipeId" ].unique() unique_recipe_id = pd.DataFrame( data={ "recipe_id" : unique_recipe_id, "mappedID" : pd.RangeIndex( len (unique_recipe_id)), } ) review_user_id = pd.merge( df_reviews[ "AuthorId" ], unique_user_id, left_on= "AuthorId" , right_on= "user_id" , how= "left" , ) review_recipe_id = pd.merge( df_reviews[ "RecipeId" ], unique_recipe_id, left_on= "RecipeId" , right_on= "recipe_id" , how= "left" , ) # ユーザIDとレシピIDのエッジ情報をTensorへ変換 tensor_review_user_id = torch.from_numpy(review_user_id[ "mappedID" ].values) tensor_review_recipe_id = torch.from_numpy(review_recipe_id[ "mappedID" ].values) tensor_edge_index_user_to_recipe = torch.stack( [tensor_review_user_id, tensor_review_recipe_id], dim= 0 , ) 前処理 レシピノードが持つ特徴量の前処理として標準化を行います。 今回利用する特徴量は既存のデータセット中に含まれる該当レシピの糖分や油分といった料理における構成要素のみをパラメータとして利用します。本来であればこのフェーズで特徴量エンジニアリングなどを行いますが今回は本題から外れてしまうのでスキップします。 # レシピノードの特徴量定義 recipe_feature_cols = [ "Calories" , "FatContent" , "SaturatedFatContent" , "CholesterolContent" , "SodiumContent" , "CarbohydrateContent" , "FiberContent" , "SugarContent" , "ProteinContent" , "RecipeServings" , ] df_recipes_feature = pd.merge(df_recipes, unique_recipe_id, left_on= 'RecipeId' , right_on= 'recipe_id' , how= 'left' ) df_recipes_feature = df_recipes_feature.sort_values( 'mappedID' ).set_index( 'mappedID' ) df_recipes_feature = df_recipes_feature[df_recipes_feature.index.notnull()] df_recipes_feature = df_recipes_feature[recipe_feature_cols] # 標準化 scaler = StandardScaler() scaler.fit(df_recipes_feature) scaler.transform(df_recipes_feature) df_recipes_feature = pd.DataFrame(scaler.transform(df_recipes_feature), columns=df_recipes_feature.columns) # レシピノードの特徴量をTensorへ変換 tensor_recipes_feature = torch.from_numpy(df_recipes_feature.values).to(torch.float) データローダー ここまで定義してきたデータを用いて Pytorch で扱えるデータセットとしてデータローダーを作成します。 データは RandomLinkSplit を用いてエッジに対してのデータ分割を行い、その後に LinkNeighborLoader でエッジベースのミニバッチを作成するデータローダーを定義します。この LinkNeighborLoader では全てのエッジの中からランダムサンプリングを適用し、そのエッジの隣接ノードから更にサンプリングを行うことで、全てのノードを使ったサブグラフによるミニバッチデータ作成を実施しています。 # HeteroDataオブジェクトの作成 data = HeteroData() data[ 'user' ].node_id = torch.arange( len (unique_user_id)) data[ 'recipe' ].node_id = torch.arange( len (unique_recipe_id)) data[ 'recipe' ].x = tensor_recipes_feature data[ 'user' , 'review' , 'recipe' ].edge_index = tensor_edge_index_user_to_recipe data = T.ToUndirected()(data) # 学習・評価用のデータ分割 transform = T.RandomLinkSplit( num_val= 0.1 , num_test= 0.1 , disjoint_train_ratio= 0.3 , neg_sampling_ratio= 2 , add_negative_train_samples= False , edge_types=( "user" , "review" , "recipe" ), rev_edge_types=( "recipe" , "rev_review" , "user" ), ) train_data, val_data, test_data=transform(data) # 学習用データローダー定義 edge_label_index = train_data[ "user" , "review" , "recipe" ].edge_label_index edge_label = train_data[ "user" , "review" , "recipe" ].edge_label train_loader = LinkNeighborLoader( data=train_data, num_neighbors=[ 20 , 10 ], neg_sampling_ratio= 2 , edge_label_index=(( "user" , "review" , "recipe" ), edge_label_index), edge_label=edge_label, batch_size= 256 , shuffle= True , ) # 検証用データローダー定義 edge_label_index = val_data[ "user" , "review" , "recipe" ].edge_label_index edge_label = val_data[ "user" , "review" , "recipe" ].edge_label val_loader = LinkNeighborLoader( data=val_data, num_neighbors=[ 20 , 10 ], edge_label_index=(( "user" , "review" , "recipe" ), edge_label_index), edge_label=edge_label, batch_size= 3 * 256 , shuffle= False , ) モデル学習 モデル定義 モデル全体像の簡単なアーキテクチャを説明すると、初めにユーザノードとレシピノードを分散表現に変換した後で、2層の GNN レイヤーにて分散表現から重要特徴量を抽出していき、最後に異なるノード間のエッジ存在確率を出力するようなモデルとなっています。また、GNN レイヤーにおけるアルゴリズムには GraphSAGE を用いており、これにより inductive な問題設定に対応したモデルとなるように配慮しています。 class GNN (Module): def __init__ (self, hidden_channels: int ): super ().__init__() self.conv1 = SAGEConv(hidden_channels, hidden_channels) self.conv2 = SAGEConv(hidden_channels, hidden_channels) def forward (self, x: Tensor, edge_index: Tensor) -> Tensor: x = self.conv1(x, edge_index).relu() x = self.conv2(x, edge_index) return x class Classifier (Module): def forward ( self, x_user: Tensor, x_recipe: Tensor, edge_label_index: Tensor ) -> Tensor: edge_feat_user = x_user[edge_label_index[ 0 ]] edge_feat_recipe = x_recipe[edge_label_index[ 1 ]] return (edge_feat_user * edge_feat_recipe).sum(dim=- 1 ) class Model (Module): def __init__ (self, hidden_channels: int ): super ().__init__() self.recipe_lin = torch.nn.Linear( 10 , hidden_channels) self.user_emb = torch.nn.Embedding(data[ "user" ].num_nodes, hidden_channels) self.recipe_emb = torch.nn.Embedding(data[ "recipe" ].num_nodes, hidden_channels) self.gnn = GNN(hidden_channels) self.gnn = to_hetero(self.gnn, metadata=data.metadata()) self.classifier = Classifier() def forward (self, data: HeteroData) -> Tensor: x_dict = { "user" : self.user_emb(data[ "user" ].node_id), "recipe" : self.recipe_lin(data[ "recipe" ].x) + self.recipe_emb(data[ "recipe" ].node_id), } x_dict = self.gnn(x_dict, data.edge_index_dict) pred = self.classifier( x_dict[ "user" ], x_dict[ "recipe" ], data[ "user" , "review" , "recipe" ].edge_label_index, ) return pred 学習と評価 学習用と検証用のデータローダーを用いてモデル学習とそのモデルの評価を行なっていきます。 評価はリンク予測によるエッジがあるかないかの2値分類となるため ROC-AUC 11 で精度を確認してみようと思います。 def train (model, loader, device, optimizer, epoch): model.train() for epoch in range ( 1 , epoch): total_loss = total_samples = 0 for batch_data in tqdm(loader): optimizer.zero_grad() batch_data = batch_data.to(device) pred = model(batch_data) loss = F.binary_cross_entropy_with_logits( pred, batch_data[ "user" , "review" , "recipe" ].edge_label ) loss.backward() optimizer.step() total_loss += float (loss) * pred.numel() total_samples += pred.numel() print (f "Epoch: {epoch:04d}, Loss: {total_loss / total_samples:.4f}" ) def validation (model, loader, device, optimizer): y_preds = [] y_trues = [] model.eval() for batch_data in tqdm(loader): with torch.no_grad(): batch_data = batch_data.to(device) pred = model(batch_data) y_preds.append(pred) y_trues.append(batch_data[ "user" , "review" , "recipe" ].edge_label) y_pred = torch.cat(y_preds, dim= 0 ).cpu().numpy() y_true = torch.cat(y_trues, dim= 0 ).cpu().numpy() auc = roc_auc_score(y_true, y_pred) return auc, y_pred, y_true # パラメータセット model = Model(hidden_channels= 64 ) device = torch.device( "cuda" if torch.cuda.is_available() else "cpu" ) optimizer = torch.optim.Adam(model.parameters(), lr= 0.001 ) model = model.to(device) # 学習・評価 train(model, train_loader, device, optimizer, 6 ) auc, y_pred, y_true = validation(model, val_loader, device, optimizer) # 精度確認(ROC-AUC曲線) fpr, tpr, thresholds = roc_curve(y_true, y_pred) plt.plot(fpr, tpr, label=f "AUC: {auc:.3f}" ) plt.xlabel( 'FPR: False positive rate' ) plt.ylabel( 'TPR: True positive rate' ) plt.legend(loc= 'lower right' ) plt.grid() 作成したモデルを用いて、検証用データより ROC-AUC を確認しました。 結果としてROC-AUCが 0.974 という数値でしたので、比較的高精度の予測が行えるモデルとなりました。 また、定量的な精度指標に加えて、予測モデルを使ってあるユーザノード(特定ユーザ)に対してランダム抽出したレシピに興味を持つかといったリンク予測がどれくらい当てられているのかを可視化してみました。 図の見方は真ん中のオレンジ色の丸が対象のユーザノードを示しており、その周りにある緑色のノードが興味を示す正例レシピノードで、グレー色が興味を示さない負例レシピノードとなります。上段の左図が正解データによるユーザとレシピの関係で、右図が予測結果によるユーザとレシピの関係です。下段の図はこれらの正解データの図と予測結果の図より正解と予測が一致するノードを緑色、不一致のノードを赤色で表現した図です。これを見ると興味を示すべきレシピに対して、興味がないと判別している予測がいくつかありますが、概ね予測がうまくできていることが見て取れますね。 終わりに 今回は Heterogeneous Graph の紹介から GNN でのモデリング方法について紹介しました。グラフデータはちょっと癖があるため扱いづらい部分もありますが、Pytorch-geometric などのフレームワークを用いることである程度簡単に実装できるようになっています。GNN が扱えるようになると問題解決の幅も広がっていくかと思いますので、興味がある方は是非試してみてください。 アドベントカレンダーも終盤ですが最後まで楽しんでいってください! https://www.ntt.com/about-us/press-releases/news/article/2020/0423.html ↩ M.Schlichtkrull, T.N.Kipf, P.Bloem, R.V.D.Berg, I.Titov, and M.Welling, " Modeling relational data with graph convolutional networks ", in European Semantic Web Conference, 2018. ↩ W.Hamilton, Z.Ying, and J.Leskovec, " Inductive representation learning on large graphs ", in NeurIPS, 2017. ↩ https://distill.pub/ ↩ Wang, Xiao, et al. " Heterogeneous graph attention network. " The world wide web conference. 2019. ↩ Ali, Mehdi, et al. " Improving Inductive Link Prediction Using Hyper-relational Facts. " International Semantic Web Conference. Springer, Cham, 2021. ↩ SONG, J. AND YU, K., 2021. " Framework for Indoor Elements Classification via Inductive Learning on Floor Plan Graphs. " ISPRS International Journal of Geo-Information, Volume 10. ↩ https://www.food.com/ ↩ Pytorch-geometric以外にも DGL や Pytorch-BigGraph などのライブラリがあります。 ↩ 疎行列を表現する格納方式の1つで、列・行・データの3つの1次元配列により疎行列を表現するデータ形式です。 ↩ ROC-AUC は二値分類のタスクに対する評価指標の1つ。範囲として 0.0 〜 1.0 の値をとり、1.0 に近づくほど予測精度が高いことを示す。 ↩
みなさんこんにちは デジタル改革推進部の浅野です。 NTT Comでは社内の分析課題をコンペにしてみんなで解くイベントがあるのですが、今回は正解データが十分用意できなくてもコンペを開催することに成功したので、その妙技をご紹介したいと思います。 (過去の開催は こちら にも投稿しております) 分析コンペとは 分析コンペとは予め用意された課題に対して参加者が予測を行いその精度を競う催しです。コンペサイトでは Kaggle や Atmacup などが有名です。 コンペでは参加者に学習用のデータが配られます。学習用データの中には正解データが含まれており、それを頼りに参加者は予測を作ります。さらに参加者に秘密の評価用データも裏にあり、最終的な順位はそちらで決めます。 つまりコンペを開くには、正解データを十分に用意しておく必要があります。 今回のコンペのテーマ 8月に新しいコンペのテーマ案として「社内にかかってくる迷惑電話の検知」が社内で挙がりました。迷惑電話の検知なので、予測対象となるのは迷惑電話番号となります。 背景としては、社内には月に数十万件の電話がかかってきており、ほとんどは業務の電話ですが、中には不動産勧誘などの業務外の電話が含まれています。迷惑電話は一度に大量にかかってくるといった特徴はわかっているものの、具体的な受信間隔や通話時間、そもそもどれくらい迷惑電話が含まれているのかなどは不明でした。また、社員からの通報も受け付けていましたが、正解データとしては使うには量が不足していました。 正解データが十分にない時点でコンペ化は困難に思えたのですが、色々とアイデアを出し合い、「Slack連携型ソシャゲ風 コンペシステム」を編み出しました。 Slack連携型ソシャゲ風 コンペシステム 今回 採用した仕組みは以下です。 参加者に素材を配布する 「通話履歴データ」はただのログです。正解ラベルはついていません。 「前日までの採点結果」はその名の通り、前日までに参加者から提出された怪しい電話番号の確認結果になります。 参加者が素材から迷惑電話番号を掘り出す 怪しい電話番号を見つけたらSlack Botへ提出する 提出には1個 魔法石を消費します。また、採点・公開済みの電話番号は提出できません。 毎朝メンテ時間を設け、運営が迷惑電話番号か確認する 採点結果を公開する 全ての採点結果を全員に公開します。 運営が知らない迷惑電話番号だった場合はボーナスポイントをつけます。(事前にコンペ運営側でもちょっと掘っていた) このサイクルを期間中、回し続けます。あらかじめ正解データを用意するのではなく、提出に応じて採点してフィードバックするのが肝です。(Active learningに似ている?) 正解データの不足を運営のマンパワーで解決する脳筋的解決法とも言えます。 このシステムのユニークな点として、 初日、正解データは全く与えられない 最初はドメイン知識を頼りに手探りで提出することになります。 状況が日々変化する 一度いいモデルを作ったら終わりではなく、正解データが毎日増えるので仮説検証を繰り返すことになります。実際のデータサインエンスの業務もそんな感じなのでよい練習?になります。 調整弁としての魔法石 運営による確認作業がキャパを超えないように、配布する魔法石によって提出数を制限します。 謎の戦略性がある いいロジックを見つけても、採点結果が翌日に全体に公開されてしまうので真似されてしまうかもれません。一方で温存しておくと先に掘られるかもしれません。 わかりやすい迷惑電話番号は早期に掘り尽くされてしまいますが、後発組はリスクなく先発組の残した採点結果を利用できます。 元々は正解データの不足を補うための仕組みでしたが、状況が刻々と変わるゲーム性が高い新感覚コンペに仕上がりました。 なお、参加者に配るデータ内の電話番号は全てハッシュ化しています。 これはプライバシー上の配慮のほか、参加者が検索を行なって迷惑電話番号か確認する行為を防ぐ意味もあります。 開催の様子 コンペには様々な部署から社員61名が参加しました。 参加登録や現在のランキング表示はSlackのbot上で行いました。 提出の推移は以下の通りです。 毎日100件前後の投稿があり、最終日には300件の投稿がありました。3週間の期間で総投稿数は1300件を超えました。 そして、全体では 252件の新たな迷惑電話番号 を発見できました。 閉会式では入賞者にヒーローインタビューを行ってロジックを共有してもらいました。 仮説を立ててロジックを組む方、機械学習を駆使して解く方、ドメイン知識をフル活用して驚異の正答率を出す方など、非常に内容の濃い面白い内容でした。コンペの詳しい内容についてはまたどこかでご紹介できればと思います。 最後はちゃんとサービス終了のお知らせを出してコンペは終了しました。 終わりに 社内で分析コンペを開くと社内の集合知が使えるのはもちろん、テーマの背景業務に対する社内理解が広がる、育成に繋がるなど色々とよいことがあります。コンペを開きたいけど正解データが手元になくて困ってる方、本記事が参考となれば幸いです。
はじめに こんにちは。イノベーションセンターテクノロジー部門の齋藤と申します。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。今回は、モバイル向けの推論フレームワークのncnnに触れてみたので、その結果について書いて行きます。 ncnnとは ncnn 1 とは、モバイル向けの推論フレームワークでAndroidとiOSにどちらも対応しています。Pytorchの場合モデルは、pthの形式で1つのファイルで構成されています。ncnnの場合モデルは、param(モデル構造)とbin(重み)ファイルに分割されています。 自身のncnnを使用するモチベーションは、ncnnのデータフォーマットにあります。モバイルで使用するフレームワークにTensorFlow Lite 2 がありますが、他のフレームワークからモデルを変換するためにNCHW形式からNHWC形式に変換する必要があります。ONNX 3 からTensorFlow 4 に変換する際、 Conv layerが余計に追加される問題 があるなど、うまく変換されないケースもあります。 https://github.com/onnx/onnx-tensorflow/issues/754#issuecomment-801775203 https://github.com/onnx/onnx-tensorflow/issues/782#issuecomment-1317208239 現在では、先程あげた例は上記リンクのように問題が解消されていると思います。しかし、モデルの変換をストレートにできた方が、変換されないケースに時間を使うことがないのではないかと考えています。 ncnnを使用することが推論速度やメモリの使用量の性能面で良さそうか考えた時に、MXNet 5 、ncnn、ONNX、および OpenVINO 6 の各フレームワークの性能を比較している ブログ がありました。このブログでは、Intel CPUを使用した場合ncnnよりもonnxを使用した方が推論時間やメモリ性能上、上回っていることを述べています。特にメモリの使用量に関して、ncnnがONNXと比較して5倍ほど消費してしまうことを 問題視 していましたが、以下で解消されるようです。 i dont know is it to late to answer this issue,i met zhe same situation as you. it is beacuse that ncnn have some acceleration algorithm witch consum more momery,if you want to reduce the usage you can set opt.use_winograd_convolution as false. In my project,r101 just uses about 450mb 推論速度やメモリの使用量をみるとONNXの方が良さそうに思えます。ただ、このベンチマークが各フレームワークの条件を揃えるためにfp32を使用していると仮定をすれば、fp16にすることにより推論速度の高速化は可能かと思われます。これらの理由により、今回はncnnを使いたいと思います。次は、ONNX -> ncnnによるモデルの変換をしていきます。 補足 N – バッチ サイズ C – 特徴マップの数 (チャネル数) H – 高さ W – 幅 onnx2ncnn 今回は ncnnのリポジトリ 内にあるONNXからncnnに変換するコードを実行しました。 onnx-simplifierの web版 もありそこでも変換できるみたいです。 cd tools/onnx onnx2ncnn input.onnx output.param output.bin ONNXのモデルの最適化をしてみたのですが、中をNetron 7 で確認をしたところ構造があまり変わっていないように見えたので今回は省きます。 Androidの環境構築 今回使用するモバイル開発に必要なツール群は主に以下となります。 Android NDK 8 ncnn Android NDK(Native Development Kit) Android NDKとは、AndroidでCやC++のコードを使用可能にするツールです。今回はC++をアプリで使っているため、利用しています。この URLからNDKをダウンロード 可能です。 unzip android-ndk-r25b-linux.zip cd android-ndk-r25b export NDK_PATH=${PWD} ncnn 実行方法には自分の手元でbuildする方法とbuild済みのものをダウンロードする方法がする方法があります。 https://github.com/Tencent/ncnn/releases/tag/20220729 。今回は自身の手元でbuildする方法を試したため以下のような手順を取りました。 git clone -b 20221128 https://github.com/Tencent/ncnn.git cd ncnn git submodule update --init export NCNN_DIR=${PWD} export ANDROID_ABI=arm64-v8a # ANDROID_PLATFORMは、Android13の場合33、Android12の場合31 export ANDROID_PLATFORM=33 # Vulkan: https://developer.android.com/ndk/guides/graphics export VULKAN_SDK=$(pwd)/1.2.189.0/x86_64 export LD_LIBRARY_PATH=${NCNN_DIR}/build/install/lib/:$LD_LIBRARY_PATH mkdir -p build_${ANDROID_ABI} cd build_${ANDROID_ABI} # ANDROID_PLATFORM # cmakeのversionは3.23で実行 cmake -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM=android-${ANDROID_PLATFORM} -DNCNN_VULKAN=ON -DNCNN_DISABLE_EXCEPTION=OFF -DNCNN_DISABLE_RTTI=OFF .. make -j$(nproc) install ここでビルドしたものもしくは、ダウンロードしたものをCMakeLists.txtにパスを記述しビルドをさせます。 今回はncnnの サンプルアプリ が出ていたので、それをベースに作成しています。 動作結果 実行環境は以下となります。 Pixel6 Pro プロセッサは、Google Tensor(CPU: ARM_Coretex-X1 , Architecture: armv8.2 a) 今回はcoco test 2017 9 画像の20枚使用し、推論速度の比較をしました。結果は以下の表のようになりました。モデルは、yolov5s 10 とfp16化したものを利用しております。 モデル  1枚あたりの平均推論時間 (ms)   yolov5s 82.085 yolov5s fp16 81.488 20枚の画像を使用した結果ではfp16化によってほぼ速度は変わらないような結果となっております。単純に速度が2倍になると考えておりましたが、今回動かした環境ではその恩恵が得られていないように思えるので、調査をしたいです。 終わりに 今回はncnnの実行環境の構築とモデルの動作をさせました。実行環境によって推論時間が異なりますが、 mmdeploynのベンチマーク のSnapDragon888の推論時間をみると近い値になっていることから、今回動かしたncnnの推論速度は違和感がないかなと思います。ONNXとncnnのモデルを比較していないため、ONNXと比較したncnnの良い部分を実感しにくいものとなってしまったため、今後調査してみたいです。 それでは、明日の投稿もお楽しみに。 https://github.com/Tencent/ncnn ↩ https://www.tensorflow.org/lite ↩ https://onnx.ai/ ↩ https://www.tensorflow.org ↩ https://mxnet.apache.org/ ↩ https://www.intel.co.jp/content/www/jp/ja/internet-of-things/openvino-toolkit.html ↩   https://netron.app/ ↩ https://developer.android.com/ndk?hl=ja ↩ https://cocodataset.org/#home ↩ https://github.com/ultralytics/yolov5 ↩
この記事は、 NTT Communications Advent Calendar 2022 16日目の記事です。 はじめに こんにちは、イノベーションセンター テクノロジー部門の池田です。 普段は SkyWay というプラットフォームを開発しています。 この記事では、GitHub ActionsからGoogle Cloud Platform(以下GCP)のCloud FunctionsにPipenvを利用したPythonアプリケーションをデプロイした際の話をGitHubのEnvironmentsなどに触れつつ紹介したいと思います。 モチベーション SkyWayで使うPythonのアプリケーションをクラウド上にデプロイしたかったのですが、毎度手動でデプロイするのはもちろん面倒です。 また、自動化した場合でもproduction,stagingなどの環境ごとに条件分岐を書いたり、意図しない自動デプロイが発生したりする可能性もあります。 これらの問題をEnvironmentsを使うことで解消できそうだったため導入しました。 行ったこと Environmentsの利用 Environments はGithub Actionsの機能で、それぞれの環境に対して別々の設定をできます。 例えば、production環境向けでは GCS_BUCKET_PRODUCTION という環境変数の値のバケットに、staging環境向けでは GCS_BUCKET_STAGING という環境変数の値のバケットにファイルをアップロードするということを行っていた場合、 それら接尾辞をつけた2つの環境変数を登録して条件分岐して利用するなどが必要でした。 しかし、Environmentsを利用することで環境毎に GCS_BUCKET へ別の値を入れて利用することで、不要な情報を減らして管理や開発ができます。 また、デプロイ前に承認を必須とすることが可能で、上記の不意のデプロイを防いだりデプロイ権限を持つ人の承認を得るというプロセスを構築したりできます。 デプロイ元のブランチも制限できますが正直なところyaml内でブランチを列挙するのと比較してどういった嬉しさがあるかは理解できていません。 一点、フリープランではプライベートリポジトリでのEnvironmentsが利用不能なのでご注意ください。 Environmentsの作成 Environmentsの設定にはリポジトリ毎のSettingsの左のメニューからアクセスできます。 Required reviewers を設定することで、実行する前にレビュアーの誰かに承認されることを必須とできます。誰が承認したかは各ワークフローの詳細ページから確認可能です。 環境毎のSecretは左のメニューのSecretsからではなくこちらのページの下のところで設定します。 環境間のSecretは名前が重複しても大丈夫ですし、むしろ同名の方が使う時の利便性が高いです。 GCPのサービスアカウントの生成&IAMの設定 GitHub ActionsからCloud Functionsに関数をデプロイするには、デプロイ用と関数実行用の2種類のサービスアカウントを用意する必要があります。 デプロイ及び実行に必要なすべての権限を持たせたサービスアカウントを共用することも仕組み上は可能ですが、デプロイ時・関数実行時共に余分な権限を持った状態で動作させることになるのでおすすめしません。 Cloud Functionsへデプロイする用のサービスアカウントでは Googleの提供するActionsのドキュメント にあるような以下の権限が必要になります。 Cloud Functions 管理者 サービス アカウント ユーザー Cloud Functionsを実行する用のサービスアカウントについては実行される関数の機能に基づいて設定する必要があります。 例えば以下で指定する様にSecret Manager内の値を使うのであればそれらへのアクセス権限を付与する必要があります。 同様にBigQueryやCloud Storageなどのサービスにアクセスするならそれらへの権限が必要です。 Secret Managerへの値の追加 特に機密性の高い情報を扱う場合は、GCPのSecret Managerを利用することがあるかと思います。 これを用いるとGitHub上に情報を置かず、Cloud Functionsの環境変数からも見えない値を利用できる様になります。 Secret Managerでのシークレットの作成については説明を省略しますが、作成後に概要タブにあるリソースID 1 は後で利用するのでメモしておくと良いです。 ワークフローを書く ここまででデプロイに必要な設定の準備をおこなったので、GitHub Actionsで実行するワークフローを設定していきます。 タイムアウトの設定 この用途に限りませんがタイムアウトの設定は重要です。 デフォルトでは 6時間 に設定されているため、知らないうちにタスクが実行されたままだと時間の枠を消費してしまいますし、Self-hosted runnerの場合は他のジョブがブロックされてしまいます。 今回は通常時だと6分ほどでデプロイが終わるので余裕を持たせて10分で設定しました。 これは timeout-minutes: 10 のように設定するだけです。 Environmentの設定 導入部分で環境による条件分岐が不要になると書きましたが、1回だけ条件文を書く必要があります。それがどの環境を使うのかの宣言部分です。しかし、environmentではifを使うことができません。 そのため、三項演算子で条件分岐する方法を参考 2 , 3 にしました。具体的には以下の様な形です。 このようにすると、mainブランチではproduction環境として、それ以外ではstaging環境として動きます。 environment : name : ${{ (github.ref == 'refs/heads/main' && 'production' ) || 'staging' }} requirements.txtの生成 Cloud Functionsへのデプロイ時にはCloud Buildが利用されており、その中で buildpacks を利用しているようです。 必要な依存情報の収集には requirements.txt を利用しているようで、このファイルが無い状態でデプロイしようとすると、デプロイには成功しますが実行時にモジュールが無いためエラーが発生します。 Pipenvで requirements.txt を生成するには pipenv requirements > requirements.txt を実行すれば生成できます。 requirements.txt は他のファイルと同様にリポジトリ内に置いてバージョン管理しても良いですが、 Pipfile や Pipfile.lock との二重管理になったり更新漏れの可能性があったりするためCIの中で一時的に作成するのが良いと思います。 requirements.txt とは関連しませんが、2022/12/12時点においてCloud FunctionsではPython 3.11には対応していないため、 Pipfile で python_version = "3.11" の様な設定をしているとデプロイ後の環境と差分が生まれるので注意が必要です。 GitHub Actionsの認証 google-github-actions/auth を用いてgcloudコマンドをGitHub Actionsから実行できるようにします。 色々と認証情報を渡す方法はありますが、JSON形式のサービスアカウントの鍵を用いる場合、 credentials_json にJSONを渡す必要があります。 この値はyaml内に直接埋め込むわけにはいかないので環境毎のSecretに登録しておきます。 デプロイ GitHub ActionsからCloud Functionsにデプロイするには大きく2つの方法があります。 1つは google-github-actions/setup-gcloud を用いてgcloudコマンドを利用できる様にした後、gcloudを次のステップで叩くという方法です。 もう1つは google-github-actions/deploy-cloud-functions を用いてgcloudコマンドを使わずにデプロイする方法です。 前者はコマンドを構築する必要があり、後者は対応していない引数 4 があるなどどちらも一長一短です。 以下は設定可能な項目で重要だと思った部分です。 キーは google-github-actions/deploy-cloud-functions のものを利用しています。 service_account_email 上で設定したCloud Functionsを実行する用のサービスアカウントの情報を渡すためのオプションです。 gcloudコマンドでは --service-account となっており紛らわしいですが、ここで渡すのはメールアドレスの文字列でありJSONの鍵ではないことに注意が必要です。 env_vars Cloud Functionsに環境変数として渡される値を KEY1=VALUE1,KEY2=VALUE2 のように渡します。 google-github-actions/deploy-cloud-functions では --set-env-vars 相当の動作しかできず、このオプションの有無に関わらず既存の環境変数は消えます。 gcloudコマンドでは --update-env-vars を使うことで既存の環境変数を残したまま一部に対して更新や追加が可能です。 secret_environment_variables 'API_KEY=projects/xxxx/secrets/yyyy/5' のようにSecret Managerのリソースへの参照を渡します。 gcloudコマンドでいう --set-secrets 相当です。gcloudコマンドとは異なりバージョンを指定しなかった場合にエラーにはならずにlatestとして扱ってくれます。 gcloudコマンドでは --update-secrets が環境変数と同様に存在します。 おわりに 本記事ではGitHub ActionsからCloud FunctionsにPipenvを利用したPythonアプリケーションをデプロイする方法について紹介しました。 Python以外のアプリケーションだったりCloud Functions以外のデプロイ先だったりに対しても活かせる部分があったのではないかと思います。 この記事の内容がより良い自動化につながると嬉しいです。 今回デプロイしたリポジトリは社内のGitHub Enterprise以下のOrganizationを利用しました。 社内のGitHub Enterpriseの詳細については こちら をご覧ください。 projects/xxxx/secrets/yyyy の様な形式の文字列です。 ↩ https://qiita.com/technote-space/items/cbeed6ddd0488499afaa ↩ https://dev.classmethod.jp/articles/github-actions-get-only-necessary-secrets-according-to-branch-name-in-ternary-operator-like-description/ ↩ たとえば第二世代の関数をデプロイできません。 ↩
この記事は、 NTT Communications Advent Calendar 2022 15日目の記事です。 2022/12/16 追記 想像以上に反響がありましたので、追記します。 「エンジニアのわがまま」発言について そのような発言が出たのは、エンジニア側とシステム担当が互いに本音をぶつけ合ったからこそでした。 限られた時間枠の中でエンジニア側から畳みかけるように数多くの問題意識や要望をシステム担当側に突きつけるような形となり、双方ヒートアップした結果としてそのような発言につながっていました。 また、システム担当からすると下記の事実もヒートアップにつながる一因だったと思います。 新しい事務用 PC のリリースをやり遂げた直後で、利用する社員から「以前より便利になった」との声も出ていたタイミングだった 事務用 PC と開発・検証用 PC の 2 台持ちが必要なのはエンジニアが多く、その対象が全社員ではないこと しかし、このイベントと今回紹介した取り組みを契機にシステム担当とエンジニアの間の風通しは格段と良くなり、エンジニア側としてもシステム担当が抱える思いや背景事情、価値観への理解が進みました。 システム担当側もエンジニアのペインに対する解像度が上がるとともに、エンジニアを頼ってくれるようになり、今では新しい技術の導入に際してエンジニアに相談が来ることも少なくありません。 本音をぶつけ合い、双方の価値観をすり合わせながら知恵を出し合った結果が今回の新しい開発・検証用 PC につながっています。 はじめに みなさんこんにちは、イノベーションセンターの @Mahito です。 普段は社内のエンジニアが働きやすくなることを目標に、コーポレートエンジニアやエンジニア向けイベントの企画・運営をしています。 今回はこの夏社内でリリースされたエンジニア向けの開発・検証用 PC について、私や社内のエンジニアたちがどう関わったかのかを少しご紹介したいと思います。 NTT Com では元々、会社支給の事務用 PC でメールを始めとする社内システムへのアクセスなどの業務を行い、開発・検証する場合は各部や開発チームなどが個別に管理する PC を利用していました。そのため、社内のエンジニアは事務用 PC と 開発・検証用 PC を 1 日の中で何度も往復する手間がありました。また、セキュリティ的にも各部やチームごとに対応がバラバラで、問題があった際に会社として統一的な対応が難しい状況にありました。 今回リリースした開発・検証用 PC では、社内のエンジニア有志がシステム担当やセキュリティ担当と話し合いをしながら、会社で求められる様々な要件を技術的に解決し、会社として統一的なセキュリティ対策を施しました。これにより、開発・検証用 PC からメールなど社内の主要なリソースへアクセスできるようになり、エンジニアが何度も PC を往復することなく一日の業務をほぼ済ますことができるようになりました。 きっかけ エンジニアが何度も PC を往復する状況を改善するきっかけは情報システムを担当する部署とエンジニアの話し合いでした。私が主催する NTT グループのエンジニア向けイベントにおいて、NTT グループ各社のシステム担当と、エンジニアがディスカッションする企画を実施しました。 NTT Com のエンジニア側からは、先に述べたように事務用 PC と開発・検証用 PC との往復が手間なため、会社として開発・検証用 PC からメールなどを見られるようにしてもらいたいという要望が上がりました。その場では開発・検証用 PC として MacBook を利用しているユーザが多く、会社として Mac を業務で使えるように認めて欲しいと言う声もありました。しかし、当時のシステム担当からは「それはエンジニアのわがまま」と言われ、その日に結論が出ることはありませんでした。 (議論するエンジニアとシステム担当) 議論には当時の経営層が数名参加していたこともあり、改めて私を含む社内のエンジニア、システム担当、セキュリティ担当とで話す場が設けられました。そこでは改めて開発・検証用 PC からメールなどにアクセスできるようにすることはわがままではなく、業務効率につながる改善だという話をしました。セキュリティ担当からも現状の開発・検証用 PC のセキュリティ的な課題を解決するきっかけにつながるとの援護もあり、「事務用 PC と同等のセキュリティが実現できるのであれば社内のリソースへのアクセスを認める」という条件が出されました。 わがままを技術で実現 セキュリティの要件の中で一番大きかったものは個人情報の漏洩対策です。 従来の開発・検証用 PC では社内のリソースにはアクセスできないため、個人情報などへのアクセスはありませんでした。しかし、メールなどの社内リソースにアクセスができるようになった場合、個人情報にアクセスする可能性があるため、個人情報の漏洩対策が必要となりました。 具体的には、 個人情報保護法施行規則 7条(個人の権利利益を害するおそれが大きいもの)の第1号においてカッコ書きの中に「高度な暗号化その他の個人の権利利益を保護するために必要な措置を講じたものを除く」という記述があり、この対応が必要となりました。 対応には 「個人情報の保護に関する法律についてのガイドライン」及び 「個人データの漏えい等の事案が発生した場合等の対応について」 に関する Q&A(抜粋) で、以下の1と2または3のいずれかの要件を満たすことが必要とされています。 暗号化した情報と復号鍵を分離するとともに復号鍵自体の漏えいを防止する適切な措置を講じていること 遠隔操作により暗号化された情報若しくは復号鍵を削除する機能を備えていること 第三者が復号鍵を行使できないように設計されていること 事務用 PC は Windows であったため、Windows で要件を満たす方法はすでにわかっていましたが、Mac ではまだ社内のノウハウがない状態でした。そこで我々は社内のエンジニア有志で情報を集め、検証環境を用意し、モブプログラミングならぬモブ設定会などを通じてノウハウを貯めていきました。 システム担当との話し合いから2ヶ月ほど経つ頃には、上記の要件を Apple T2 セキュリティチップ , FileVault 2 , Microsoft Intune などを利用することで技術的に満たせると確信を得ました。それに加え、情報システム担当・セキュリティ担当から出された追加の要件にも対応し、その翌月には新しい開発・検証用 PC のトライアルを社内の一部で開始しました。 セキュリティと柔軟な開発・検証業務の両立 新しい開発・検証用 PC では上記のように会社として求められるセキュリティの要件を満たしていきました。一方で、事務用 PC と完全に同等のセキュリティ要件で PC の設定やソフトウェアをガチガチに固めてしまうと、利用者の使い勝手のみならず開発・検証用途に求められる機能を損なう恐れがありました。 そのため、利用者が自由にソフトウェアや OS の設定をできるようにしつつ、必要なセキュリティを確保するために、下図のように管理範囲を明確にしました。 (新開発・検証用 PC と従来 PC との管理範囲比較) 上図で示すように、ハードウェアセキュリティ、ウイルス対策と検知、そして一部の OS の設定ポリシーをシステム担当側で管理し、それ以外を利用者に管理してもらいます。システム担当側では以下のような制限や設定を施していますが、利用者がこれらを意識することはほぼありません。 社内リソースへのアクセスは登録済みの PC に限定(端末制限) PC のポリシーチェック(パスワードポリシー、OS バージョン、その他設定など) EDR/AV の自動配布と全アクティビティの監視・異常検知 etc... これにより、PC へのソフトウェアのインストールなどの自由は残しつつ、何かあった際に会社側で検知・追跡・確認ができるようになっています。 社内の反応 トライアル当初から従来だと事務用 PC からしかアクセスできなかった、メールや社内ポータル、Office などの会社リソースを開発・検証用 PC から使える様になった上、従来の開発・検証用 PC とほぼ変わらない使い勝手ということもあり利用者からは好評を得ています。特に、私を含む Mac で開発・検証作業をしていた人からは「ほぼ一日の作業を Mac で完結できて便利」との声が上がっています。 この夏の全社リリースでは従来より初期の利用申請や設定の手間が少し増えたこともあり多少の混乱はありましたが、現在は特に大きな問題もなく新しい開発・検証用 PC の利用者は社内で着実に増えていっています。 まとめ 元々は「エンジニアのわがまま」と言われたところから始まった話ですが、この夏無事に新しい開発・検証用 PC を社内でリリースするに至りました(トライアルの開始から色々紆余曲折を経てだいぶ時間がかかりましたけど)。 まだすべての社内システムにアクセスできないことや、Linux デスクトップユーザのサポートが出来ていないなどの課題もあります。しかし、現在は最初の頃と異なり、情報システム担当やセキュリティ担当と話し合いをしながら課題解決に取り組みやすい状態にあります。この状態を活かして、今後も引き続き情報システム担当やセキュリティ担当、社内のエンジニアたちと協力をしながらエンジニアが働きやすくなる環境を目指して、残る課題の解決に当たっていきます。 以上で、 NTT Communications Advent Calendar 2022 15日目の記事は終わりです。 それでは、明日もお楽しみに!
この記事は、 NTT Communications Advent Calendar 2022 14日目の記事です。 はじめに 皆様こんにちは。イノベーションセンター所属の @sublimer です。 普段はWebRTCプラットフォーム 「SkyWay」 の開発・運用の業務に取り組んでおり、現在は新しいSkyWayの正式リリースに向けて、インフラ・バックエンド・フロントエンドのコードをガリガリ書く楽しい日々を送っています。 一方のプライベートでは、自宅Kubernetesクラスターを盆栽のごとく愛情を持って育てています。 今回は、新しいSkyWay正式リリースに向けて、TURNサーバー(P2P通信においてNAT越えのために使用されるサーバー)の負荷試験を行った際に得られた知見をご紹介します。 ⚠️ 注意 この記事では、負荷試験の実施方法について書いています。 負荷試験は、管理下にないシステムやサービスに対して行った場合は、DoS攻撃とみなされ罪に問われる可能性があります。 また、利用しているクラウドサービスが負荷試験を認めていない場合もあります。 もし負荷試験を実施する際は、十分に注意して実施するようにしてください。 今回の負荷試験についても、問題が生じないよう十分検討してから実施しています。 TURNサーバーの負荷試験の必要性 冒頭でも述べた通り、TURNサーバーはP2P通信においてNAT越えのために使用されます。 クライアントと通信先の間に位置してデータを中継することで、NAT越えを実現するのです。 例えばWebRTCを使ってP2Pで通信する際に、間にNATが入っていて直接通信できない場合でも、TURNサーバーがデータを中継することでNATを越えて通信できます。 このように、TURNサーバーは通信を中継する役割を担っているため、大量のトラフィックをさばく高い性能が求められます。 そのため、サービスリリース前に負荷試験を行い、どのくらいの負荷までならサービスに影響が出ないのか、想定通りにスケールするのかを確認しておくことが重要です。 試験方法の検討 プロトコルとしてHTTPを用いるケースであれば、 k6 、 Locust といったツールや、 k6 Cloud 、 Azure Load Testing といったサービスを用いることで、比較的容易に負荷試験を始めることができます。 しかし、TURNのプロトコルはHTTPほど一般的なものではないため、負荷試験の方法を検討するところからはじめました。 今回検討した方法は、以下の2つです。 turnutils_uclientを用いる ヘッドレスブラウザを用いる 2つを比較検討した結果、今回はturnutils_uclientを用いる方法を採用しました。以下で詳細を説明します。 まずturnutils_uclientについてです。 turnutils_uclient は、OSSのTURNサーバー、 「coturn」 に付属している、TURNのテスト用のCLIクライアントです。 turnutils_uclientを使う利点としては、様々なコマンドラインオプションがあるため、負荷試験の際のトラフィックを柔軟に制御できるという点が挙げられます。 またCLIツールであるため、動作のオーバーヘッドが比較的小さく、出力された試験結果の集計も容易にできます。 一方で、目的のトラフィックを発生させるためのオプションの指定がやや複雑であるという課題があります。 次にヘッドレスブラウザについてです。ヘッドレスブラウザは、GUIが無いブラウザのことで、 Puppeteer や Playwright が有名です。 予めWebRTCで通信するWebアプリケーションを作り、それをヘッドレスブラウザ上で動かすことで、テスト用のクライアントとして使うことができます。 ヘッドレスブラウザを使う利点としては、ブラウザ経由でWebRTCの通信ができるため、エンドユーザーの使い方に、より近い状況を再現して負荷試験ができるという点が挙げられます。 一方で、クライアント毎にブラウザを起動することになるため、負荷試験のように大量のクライアントを生成する際は、比較的大きなオーバーヘッドが生じるという課題があります。 両者を比較した結果、今回はturnutils_uclientを用いた方法を採用しました。 ヘッドレスブラウザを用いた方法の場合、ヘッドレスブラウザを制御するアプリケーションと試験用のWebアプリケーションの両方を作る必要があり、実装のためのコストがやや大きくなると想定されたためです。 turnutils_uclientの「オプションの指定がやや複雑である」という課題は、後述する負荷試験用のスクリプトを作成することで解決しました。 ここで、「WebRTCの通信を実際に行うヘッドレスブラウザの方が、より現実に即した試験が行えるのでは?」と思われた方もいるかもしれません。 もちろん、ヘッドレスブラウザを使った方が、よりエンドユーザーの使い方に近い状況で試験が行なえます。 ただ、今回はトラフィックが増加した際の負荷についての試験が目的ですので、指定しただけのトラフィックを発生させられれば問題ありません。 また、TURNサーバーは、中継しているデータがWebRTCの通信によるもなのかどうかに関わらず、単なるバイナリのデータとして中継しています。 以上を踏まえ、turnutils_uclientによって生成したトラフィックで試験を実施しても、問題は無いと判断しました。 試験観点の検討 今回は、以下の5つの観点について負荷試験を実施することとしました。 負荷を線形に増加させた際の挙動の確認 TURNのプロトコルごとの比較 新規接続のスパイクの影響の調査 VMスペックごとの比較 スケールアウト時の挙動の確認 それぞれの試験観点について、なぜ試験が必要なのかを簡単に説明します。 1. 負荷を線形に増加させた際の挙動の確認 TURNサーバーがさばけるトラフィックの限界値を調べるために実施します。 限界値が分かれば、その値に合わせて監視システムのアラート条件を決めたり、スケールアウトさせる基準を決めることができます。 2. TURNのプロトコルごとの比較 クライアント・サーバー間の通信で用いられるプロトコルごとの、特性を調べるために実施します。 TURNは、以下の3通りのプロトコルでクライアント・サーバー間の通信ができます。 TURN-UDP TURN-TCP TURN-TLS どのプロトコルが使われるかは動的に決まるケースがほとんどであるため、予め特性を把握しておく必要があります。 ちなみに、TURNと中継先のクライアントの間の通信は、WebRTCにおいてはUDPのみが用いられます。 3. 新規接続のスパイクの影響の調査 サービスを提供する際に、事前にエンドユーザーの使い方を予測するのは困難であるため、どの程度の新規接続のスパイクであればさばけるのかを調べるために実施します。 スパイクはある程度のトラフィックをさばいている状態で突然発生するため、定常状態として一定量のトラフィックを流した状態で試験を実施します。 4. VMスペックごとの比較 VMのスペックによってさばけるトラフィックは異なるため、どのスペックのVMを使えば最も費用対効果が高くなるのかを調べるために実施します。 5. スケールアウト時の挙動の確認 負荷が高くなった際にスケールアウトを実施して、きちんとスケールするかどうかを検証するために実施します。 TURNサーバーは1台1台が独立して動くため、基本的には台数を増やしてあげるだけでスケールします。 また、上記の5つの試験観点に加えてWebRTCのループバックの通信をTURN経由で行い、目視、及びwebrtc-internalsの値を確認してWebRTCの通信品質が大きく低下していないかを確認することとしました。 ループバックの通信では、ストップウォッチの画面を画面共有することで、簡易的にRTTの計測を実施しました。 正確なRTTの値を確認する際は、webrtc-internalsに表示される remote-inbound-rtp の roundTripTime 1 の値を確認しました。 負荷試験用のスクリプトの実装 turnutils_uclientはTURNのテスト用クライアントとして汎用的に使えるようになっているため、負荷試験用に用いる際にはオプションの指定について工夫が必要になります。 ここで、turnutils_uclientのオプションについて、いくつか抜粋して簡単に説明します。 -l : TURNのメッセージのサイズをbyte単位で指定します。 -n : TURNのメッセージの送信回数を指定します。 -z : TURNのメッセージの送信間隔をミリ秒単位で指定します。 -t : TURNサーバーとの間の通信をTCPで行います。 -S : TURNサーバーとの間の通信を暗号化します。 -t と組み合わせることで、TURN-TLSの通信になります。 このように、turnutils_uclientにはトラフィックの帯域幅や通信する時間を指定するオプションはありません。 ですので、「 bpsの通信を 秒間行う」という条件を指定したい場合は、メッセージの送信回数や送信間隔を計算してからオプションとして渡してあげる必要があります。 送信回数を 、送信間隔を msとすると、以下の計算式で と から と を求めることができます。 今回は、TURNのメッセージのサイズは1024 byteで固定としています。 したがって、例えば1 Mbps(1,000,000 bps)の通信を60秒間流す場合は、 、 msとなります。 今回負荷試験を実施するにあたって、上記の方法で値を計算しturnutils_uclientのオプションとして指定するためのスクリプトを、TypeScriptで実装しました。 試験用のスクリプトは、指定されたパラメーターを元にturnutils_uclientのオプションを計算し、 Node.jsの child_process 2 モジュールを使って、turnutils_uclientを子プロセスとして大量に起動する仕組みになっています。 今回負荷試験に用いた試験用のスクリプトには、オプションの値の計算に加えて、以下のような機能も実装しました。 試験の各パラメーターをコマンドライン引数として指定できる機能 TURNの認証情報を、APIから取得する機能 試験結果をJSONファイルとして出力する機能 複数の試験結果のJSONファイルを集計し、CSVファイルとして出力する機能 今回は、条件を変えて繰り返し試験をする必要があったため、試験の条件をコマンドライン引数として設定できるようにしました。 また、複数の試験結果を集計してCSVファイルとして出力することで、容易にグラフを作成できるようにしました。 負荷試験の実施 負荷試験を実施する前に、以下の2点の確認を実施しました。 負荷試験用のスクリプトが想定通りのトラフィックを生成できているか 負荷をかける側のVMが飽和状態になっていないか スクリプト内で行ってるパラメーターの計算処理が本当に正しいのかを、実際に試験用のトラフィックを生成して、そのトラフィックを計測して確認しました。 計測にはdstatコマンドとvnstatコマンドを使用し、負荷をかける側とかけられる側の両方のVMでトラフィックを計測しました。 その結果、想定通りのトラフィックを生成できていることが確認できました。 また、負荷をかける側がボトルネックとなり試験結果に影響することが無いよう、負荷をかける側のVMのメトリクスをチェックしました。 その結果を踏まえ、負荷をかける側のVMのスペックを設定しました。 実際の負荷試験では、予め検討した試験用パラメーターを指定してスクリプトを実行し、その結果をJSONファイルとして出力しました。 試験後は、出力されたJSONファイルを元に集計処理を行い、複数の試験結果をCSVファイルに出力した後、Google SpreadsheetにCSVファイルをインポートしてグラフ化を行いました。 最後に、試験結果やグラフをまとめ、負荷試験のレポートを作成しました。 この流れで、前述した5つの観点について試験を実施しました。 負荷試験の結果 今回は、「負荷を線形に増加させた際の挙動の確認」の観点についての試験結果を、抜粋してご紹介します。 トラフィック量を増やして繰り返し試験を実施した際のCPU Idleと送信トラフィックの値は、それぞれ以下のグラフのようになりました。 (上段:CPU Idle 下段:送信トラフィック) トラフィックが増えるに従ってCPU使用率も上昇していることが見て取れます。 また、CPU使用率がほぼ100%で張り付くと、ネットワークトラフィックも頭打ちとなり、性能が飽和状態になっていることが分かります。 このときの通信のRTTやパケロス率、jitterといった品質を示す指標は、いずれも非常に悪化していました。 加えて、TURN経由で行っているWebRTCのループバックの通信の映像にもカクつきが生じており、性能が上限に達していたことが分かりました。 この結果より、TURNサーバーはトラフィックの増加とともに負荷も高くなるものの、CPU使用率が張り付くまではトラフィックをさばける事が分かりました。 他の4つの観点についても試験し、TURNサーバーに性能面で大きな問題はないことが分かりました。 ところで、今回試験を実施する中で、turnutils_uclientには「標準出力にログを二重に出力してしまう」という意図しない挙動があることに気づきました。 折角の機会だと思い、この挙動を修正するプルリクエストを出してみました。 github.com OSSにプルリクエストを出した経験はあまりないのですが、すぐにマージしていただけて、貴重な経験ができたかなと思います。 おわりに TURNの負荷試験はインターネット上に公開されているノウハウがHTTPほどは無いため、手探りで進めた部分もありましたが、無事に試験を終えることができました。 今回の試験を通して、以下のような知見を得ることができました。 turnutils_uclientの各オプションの役割 負荷試験で確認すべき観点 TypeScriptでのツール開発の流れ 昨年、自分でTURNサーバーを実装してみた ことに加え、今回turnutils_uclientを用いて負荷試験を行い、TURNについての理解がより深まったかなと思います。 負荷試験を実施した後、今回の負荷試験で得られたデータを元に、TURNサーバーの台数やVMのスペックなどの検討を実施しました。 お客様に安定して利用していただけるよう、十分なリソースを確保しましたので、正式にリリースされた際はぜひ使っていただければと思います。 「新しいSkyWayを今すぐに使ってみたい!!」という方向けに、 Beta版としてもリリース していますので、気になる方はこちらもどうぞ!! また、SkyWayの開発チームでは、現在インターンシップの参加者を募集しています。 以下のリンク先の、「ビデオ通話プラットフォーム「SkyWay」、低遅延ライブ配信プラットフォーム「Smart vLive」、テレプレゼンスロボット(遠隔操作ロボット)のいずれかの技術開発」に詳細な内容が記載されています。 information.nttdocomo-fresh.jp 締切は、なんと今日!! 12月14日 13:00ですので、少しでも興味がある方はお早めに応募してみてください!! それでは、明日もお楽しみに!! 参考サイト turnutils_uclient · coturn/coturn Wiki · GitHub https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteinboundrtpstreamstats-roundtriptime ↩ https://nodejs.org/api/child_process.html ↩
この記事は、 NTT Communications Advent Calendar 2022 13日目の記事です。 はじめに こんにちは。 SDPF クラウド/サーバー 仮想サーバーチームの宮岸( @daiking1756 )です。 普段はOpenStackを使ってIaaSを裏側からお世話する仕事をしています。 この記事では 先日開催された第6回 NTT Agile MeetupのLightning Talkで話した「チーム定例の議事録を工夫した話」を元に、 当日は時間の関係で話せなかったチーム定例を改善するために行った3つのことを紹介していきます。 3行まとめ 最初に3行まとめです。 チームの定例のBefore Afterを書いておきます。 Before After 全ての議題を話し切るまで時間を定例を後ろに延長していた 後ろに予定を入れることで時間厳守 定例開始後に議題を決めることもあった 開始前に「議題の整理」を行うことで、短時間で濃い議論になった 各議題の完成の定義が曖昧であり、次回議事録を見た際に継続議論が必要なのかの判断が必要だった 各議題のステータスを管理することで話すべき議題の判断が容易になった なぜ定例にフォーカスしたのか パレートの法則(別名: 80:20の法則) という有名な法則があります。 文字通り、全体の数値の大部分は、全体を構成するうちの一部の要素が生み出しているという法則です。 プログラミングの文脈では「プログラムの処理にかかる時間の8割は、コード全体の2割の部分が占める」という話でよく用いられます。 つまり、コード全体の2割の部分は何度も繰り返し実行されることで(ループ処理や頻繁に呼ばれる関数など)、全体の実行時間の大部分を占めているということになります。 1 ここから、全体のパフォーマンスを改善させる1つの方針として、 繰り返し 実行される部分に着目することが大事ということが分かりますね。 そして「定例 = 繰り返し 行われる会議」なのです。 つまり、定例を改善することが、チームのパフォーマンス改善に大きく影響するのではないか?と思い、フォーカスすることにしました。 そもそもどんな定例をやっているのか 現在私のチームで行っている定例は大きく下記の3つです。 スクラムイベント(デイリースクラム、スプリントプランニング/レビュー/レトロスペクティブ) 2 企画チーム / 運用チーム / ベンダー との定例 サブチーム(チーム内で担当領域ごとに結成されているチーム)定例 このうち、影響範囲が少なく小さく始められる & 私の中で問題点が見えていた「サブチーム定例」を手始めに改善の対象としました。 定例がズルズル延長してしまう問題 サブチーム定例では、困っているタスクについて状況を確認したり、同期コミュニケーションの力でみんなの悩みを解決することを目的で行っています。 まず問題だと感じていたのは、 定例がズルズル後ろに延長していた ことです。 サブチームでの話題は参加者も少なく、議題のコンテキストも深く共有していることが多いため、深く長い議論になりがちです。 それ自体は全く問題ないのですが、時にはスコープ外の議論へ発展することもあり、設定した時間内で全ての議題を話し切ることができていませんでした。 他の定例の前に開催時間を変更 そこで、サブチーム定例をデイリースクラムの直前に行うようにしました。 これにより、デイリースクラムの時間になったら強制的にサブチーム定例を終了することになり、結果としてサブチーム定例がズルズル延長することは無くなりました。 ※ スクラムイベントは比較的時間通りに開始・終了できていたため、全体が遅延することもありません。 続けていると徐々にですが、参加者全員が時間制限を意識した定例を行うことが少しずつできるようになってきて、意識するだけでかなり変わるもんだなぁ(小並感)と思いました。 とはいえ、意識だけでは解決できない問題もありまして・・・。 話すべき内容が時間内に話しきれない 当たり前ですが、時間が短くなったことで、話すべき内容を時間内に話し切れないことが増えました。 また、その中には、優先度を上げて対応するべき内容が含まれていることもあり、このままではチームのアジリティが落ちてしまうと感じていました。 そこで、短時間の定例で 効果的に優先度の高い議題をカバー するために、 「議題の整理」 をしっかりすることにしました。 どんな風に「議題を整理」したのか 今までも事前に議題を議事録ページ 3 に書くことはありましたが、優先度やその議題のステータスが不明確なために、どの議題から話すべきかを決めるのに時間がかかっていました。 (それ故に、とりあえず上から話していき、時間が足りなくなっていました) そこで、各議題について下記の項目を整理した状態で、定例開始までに議事録ページに記載しておくようにしました。 項目は、よくあるMTGのベストプラクティスや、チームの状況を踏まえて、無理のない範囲で始められそうなものを えいや で選びました。 詳細はNTT Comが公開している リモートワークハンドブック をご参考下さい。 これらを議事録ページに反映させると下記の画像のようなイメージになります。 特に優先度とステータスの項目は、視覚的に分かりやすく色や絵文字をで表現しています。 チームの共通認識を作る 上記の「議題の整理」の効果を高めるために、 定例を運用する中で下記のルールをチームの共通認識として持つことにしました。 議題を記載する際は優先度順に記載する 優先度:高 以上の議題のステータスが定例中に✅へ遷移せず残ってしまった場合は、別途チーム内で話す時間を作り最速で✅へ遷移させる 読んでみると1つ1つは当たり前のことが書かれているのですが、一度書き起こして、ぶれない共通認識としておくことで、スムーズで効果的な定例が行われるになりました。 細かい話 繰り返し発生する定例の議事録は大きく2つの取り方があります。 新規と追記です。 自明ですが、一度書いておきます。 新規: 毎回新しい議事録ページを作成する 追記: 1つのページにどんどん追記していく サブチーム定例で多いのは新規と追記のハイブリッド型です。 基本的には追記していき、四半期に1回ほどのペースで新規ページにリニューアルしています。 また、サブチームでも四半期に一度は、立ち止まって仕事っぷりを振り返る時間を取っており、 振り返りの場で次の四半期にやることや目標を立てます。 この目標を定例ページの議事録の先頭に書いておくことで、チームや個人が目標を意識できるような効果も狙っています。 このあたりはまだまだ実験中の取り組みですが、いろいろ試しながら改善を続けています。 やってみての感想 手始めに自分が参加しているサブチーム定例について、いろいろ試行錯誤しながら改善活動を行っていきました。 すると隣のサブチームから「いい定例やってんね」「その議事録良さそうね」と声が掛かり、気づかないうちに他の定例にも布教されていることがありました。 また、たまに隣の定例を覗いてみると、そのチーム独自の文化やアレンジがされていることもあり、発見や学びが多くあります。 ドキュメント化されていないTipsや文化などの中にも重要なものは多いですが、その当事者たちの内側では当たり前になっていてその重要さや特殊さに気づかないことがありますよね。 内側で気づければよいのですが、これらを見出すのはその外側にいる人たちの役割なのかもしれませんね。 「越境はいいぞ」的な話は聞いたことがありましたが、今回の経験を通して越境の重要性を肌で感じることができました。 今後も積極的に越境していきます。 そして、ここまで読んでいて感じた方もいると思いますが、この記事中には定量的な話がほぼ出てきません。 定性的な話がメインでした。 それでもチームメンバーからのフィードバックにより、この取り組みに一定の効果があったことは確信しています。 しかし、定量的なデータも合わせて用意できていれば、更にこの取り組みの効果を多角的に分析できていたことでしょう。 また、そこから新たな改善ネタが見つかることもあるでしょう。 「問題の定量化と改善の計測」 大事。 特にこのようなブログ記事などで取り組みを紹介する際には、問題の定量化と改善の計測ができたら更に良かったと思っています。 計算機ではなく人間が絡む領域だと全てを定量化するのは難しくなりますが、今後はこの点も意識した改善活動を行っていきます。 おわりに いかがでしたでしょうか。 今回紹介した取り組みは私のチームではたまたまうまく機能していますが、決してベストプラクティスのようなものではありません。 もう少し厳格なルールや仕組みを作ったほうがいいチームもあれば、その逆もあるでしょう。 それを見極めるためには、チームをよく観察し、小さく試して小さな成功を積み上げていくことが大事だと思います。(私もまだまだ全然できていないですが) この記事の内容が少しでも皆さんのより良い定例ライフに繋がれば幸いです。 さて、NTT Comでは現在、現場受け入れ型インターンシップを募集しております。 なんと申し込み期限は12月14日(水)13:00迄まで! 残り時間僅かです。 様々なポジションで募集しておりますが、ぜひ仮想サーバーチームのインターンシップもチェックしてみて下さい。 engineers.ntt.com information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 単にI/O待ちなどで処理に時間がかかる場合も勿論ありますが、ここでは割愛しています。 ↩ 定例という感じでは無いですが、繰り返し行うという意味で挙げています。 ↩ SaaS型のドキュメント作成ツールを使って作成することを想定しています。 ↩
この記事は、 NTT Communications Advent Calendar 2022 12日目の記事です。 はじめに はじめまして、クラウド&ネットワークサービス部でデータマネジメントに関するサービス企画・販売企画を担当している古志将樹です。 今回はNTT Comで提供しているデータマネジメント基盤である「インフォマティカソリューション」について簡単に触ってみたので、その操作方法などを紹介したいと思います。 データ活用が必要とされる背景、課題 インフォマティカソリューションの紹介をする前に、データ利活用を進めるにあたってよくぶつかる壁を説明します。 近年、DX/データ分析の動きが盛り上がっていますが、そのために使用するデータは1つに集まっていることはまれで、クラウド、オンプレなど各所に散在してることがよくあります。そのため、以下のような課題をDX/データ分析を実施する前に解決する必要があります。 アプリケーション変革期においてデータ移行・連携・同期コストが増大すること 散在するデータ資産の品質・信頼性・安全性の担保ができないこと 業務・顧客サービス・デジタル戦略へのデータ資産活用が困難であること これらを解決するのが今回ご紹介するデータマネジメント基盤であるインフォマティカソリューションになります。 NTT Comではこちらのサービスをお客様のDXを推進するプラットフォームである Smart Data Platform(SDPF) 上でご提供しております。 マッピングチュートリアル では、ここからはインフォマティカソリューションの1つのデータ統合の機能であるIntelligent Data Management Cloud(IDMC)について、簡単なチュートリアルをやってみたいと思います。 Intelligent Data Management Cloud(IDMC)について こちらは、データ流通の基本となる部分になります。 AシステムからBシステムにデータを連携する といった機能を持っており、オンプレ、クラウドの各所に点在しているシステムのデータを統合する時に必須となります。 主に ポッド と セキュアエージェント というシステムの2つから構成されており、コントローラーである ポッド は、データ処理に関わる命令を セキュアエージェント に送り セキュアエージェント でデータ処理を行っています。 そのため、実データはセキュアエージェントに送られるわけですが、お客様によっては、インターネット上に実データを通したくないといったご要望をお持ちのこともあります。その場合においては、こちらNTT Comの FIC や UNO を使い、閉域網を使用して安全に他システムや様々なクラウドと連携できます。 特徴としては以下になります。 開発時は 変換部品 をドラッグアンドドロップで繋げていき、実装していく流れで以下のようなものがあります。これらを使用してデータ連携できます。 データの結合する ジョイナ データのグループ化、例えば売り上げの集計などを行う際に利用する アグリゲータ データのフィルタリングを行う フィルタ JSONファイルなど半構造化データを構造化形式に変換できる 構造パーサー ソース、ターゲットシステムへの接続は コネクタ を利用することでGUI上が可能。SAP、ERP、oracle、各種クラウドサービスなどの基本的なシステムや、REST、SOAP、ODataに対応しているシステムであれば接続可能となっております。 マッピングの流れ こちらが今回実施するマッピングの流れとなります。 以下に添付している画像に記載の通りですが、FTPサーバーとセキュアエージェント上にある 売り上げ一覧 と 商品マスタ を商品コードをキーに結合し、いくつかのデータ加工して最終的にあらかじめ用意しているOracleのテーブル上に格納するというマッピングとなります。 このマッピングでは、 コネクタを使用して簡単にデータソース、ターゲットと接続可能 、 繋げたシステムのデータ加工方法 、 半構造データでもインテリジェント構造モデルを使用してデータ加工可能 といった点を知っていただければと思います。 今回、各種データをFTPサーバー、セキュアエージェントに置いていますが、これがSAPやERP上の場合も利用するコネクタが異なるだけでデータの加工における設定は基本的に同じなので、ぜひ見てみてください。また2022年10月24日現在、インフォマティカ社から 1か月の無償トライアル も出されているのでお時間ある方は触ってみてください。 具体的な流れとしては以下になります。 インテリジェント構造モデルを使ったJsonファイル(商品マスタ)のパターン抽出 今回使用する商品マスタのデータはJson形式のため読み込める形にします。 コネクタの設定 データソースとターゲットの設定をします。 「売り上げ一覧」と「商品マスタの結合」 売り上げ一覧には product_code 、 性別 、 年齢 、 ストアコード などがあり、商品マスタには product_code 、 商品名 、 食品名 、 食品のカテゴリ 、 値段 などがあります。ですので product_code をキーに商品マスタと売り上げ一覧のデータを結合します。 商品名変更 新しく product_Name というカラムを作り一部の商品名を“江戸前幕の内弁当”に変更します。 収益計算 新しく Revenue というカラムを作り、税込み価格と販売数のカラムから収益を計算してrevenueに格納します。 オラクルへの格納 最後にあらかじめ用意していたOracleのテーブルに作成したデータを格納します。 以下は今回使用するデータになります。 sales.csv:売り上げデータです。 store_code sales_date sales_time product_code amount payment_code gender age 0 2006/7/18 0:00:02 A00001 1 1 M 20 27001 2006/7/19 0:00:05 A00002 2 1 F 30 27002 2006/7/20 0:00:08 A00003 1 1 F 20 27003 2006/7/21 0:00:11 A00004 1 2 F 50 27004 2006/7/22 0:00:14 A00005 1 3 M 60 27005 2006/7/23 0:00:17 A00006 3 4 F 20 27006 2006/7/24 0:00:20 A00007 1 1 M 20 27007 2006/7/25 0:00:23 A00008 1 2 M 30 19001 2006/7/26 0:00:26 A00009 2 1 M 20 19002 2006/7/27 0:00:29 A00010 5 1 F 60 19003 2006/7/28 0:00:32 A00011 3 2 F 20 19004 2006/7/29 0:00:35 A00012 1 1 F 20 19005 2006/7/30 0:00:38 A00013 1 1 M 40 19006 2006/7/31 0:00:41 A00014 4 3 F 20 19007 2006/8/1 0:00:44 A00015 1 1 M 20 0 2006/8/2 0:00:47 A00016 1 1 M 30 27001 2006/8/3 0:00:50 A00017 2 5 M 30 27002 2006/8/4 0:00:53 A00018 1 1 F 20 27003 2006/8/5 0:00:56 A00019 1 1 F 20 27004 2006/8/6 0:00:59 A00020 2 1 F 60 27005 2006/8/7 0:01:02 A00021 1 1 M 20 27006 2006/8/8 0:01:05 B00001 1 2 F 30 27007 2006/8/9 0:01:08 B00002 1 3 M 20 19001 2006/8/10 0:01:11 B00003 3 4 M 50 19002 2006/8/11 0:01:14 B00004 1 1 M 60 19003 2006/8/12 0:01:17 B00005 1 2 F 20 19004 2006/8/13 0:01:20 B00006 2 1 F 20 19005 2006/8/14 0:01:23 B00007 5 1 F 30 19006 2006/8/15 0:01:26 C00001 3 2 M 20 product.json:商品マスタデータ(一部)です。 [ { "M_PRODUCT_CODE": "A00001", "M_PRODUCT_NAME": "幕の内弁当", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "382", "M_UNIT_PRICE": "550", "M_UNI_PRICE_TAX": "578" }, { "M_PRODUCT_CODE": "A00002", "M_PRODUCT_NAME": "天丼", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "411", "M_UNIT_PRICE": "500", "M_UNI_PRICE_TAX": "525" }, { "M_PRODUCT_CODE": "A00003", "M_PRODUCT_NAME": "カツ丼", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "365", "M_UNIT_PRICE": "500", "M_UNI_PRICE_TAX": "525" } JSON_File.txt:product.jsonファイルのパスを記載したファイル、インテリジェント構造モデルによるproduct.jsonファイルの読み取りに使用します。 PATH /home/infa/product.json インテリジェント構造モデルによるJsonファイル(商品マスタ)のパターン抽出 今回使用する商品マスタのデータはJson形式のためデータ加工する前にパターン抽出して構造化します。インテリジェント構造モデルについての詳細は こちら をご覧ください。 では、まず適当なブラウザから Informatica Cloud に接続して、ユーザー名とパスワードを入力してログインします。 データ統合 をクリック > 新規 コンポーネント > インテリジェント構造モデル 以下の項目を入力 名前:インテリジェント構造モデルの名前です。適当な名前を入力ください プロジェクト > 参照 からこれから作成するモデルの保存場所を指定します スキーマ/サンプルファイル:今回使用する商品マスタのデータをローカルフォルダから選択 *ここでいうローカルフォルダはセキュアエージェントではなく、このブラウザにアクセスしているローカルのPCになります。 構造の検出 をクリック後、 保存 をクリックする 以上でモデルを作成できました。 同じ画面で検出したファイルの構造がツリー上で可視化されるようになります。 コネクタ設定 次にコネクタの設定をします。 IDMCではさまざまなコネクタを用意しておりまして、Salesforce、SAP、Oracle、Amazon S3など様々なサービスから指定のコネクタを使いデータを利用できます。 今回は、SA上とFTPサーバー上にあるデータを引っ張ってきて、oracleの既存のテーブルに格納しますので、 FTPコネクタ とセキュアエージェントにあるデータを利用できる フラットファイルコネクタ と オラクルコネクタ を利用します。 コネクタによって設定するプロパティは違いますが、以下ではOracleコネクタ設定時に入力が必須な項目について記載します。詳細は こちら をご覧ください。 管理者 > 接続  > 新しい接続 をクリック タイプ:コネクタのタイプを選択します。今回は Oracle を選択 ランタイム環境:このコネクタを使用する環境を選択します。 ユーザー名:Oracleで設定しているユーザー名です。 パスワード:Oracleで設定しているパスワードです。 ホスト:OracleのIPアドレスです ポート:Oracleのポート番号です。基本的には1521番でよいかと思います。 サービス名:Oraleのサービス名です コードページ:文字コードを選択します。 「売り上げ一覧」と「商品マスタ」の結合 コネクタの設定できましたら次はデータベースの設定です。 ここからの操作では変換部品を選択、繋げていき、プロパティで詳細設定するというのを繰り返していく形になります。 データ統合 > 新規 > マッピング > マッピング > 作成 でマッピングを作成 まずは売り上げデータの設定をします。 変換部品から ソース を選択 プロパティで ソース > 接続 からあらかじめ作成しておいたFTPコネクタを選択、 オブジェクト にて売り上げデータ(sales.csv)を選択 補足にはなりますが、プロパティの ソース > フィールド では読み込んだファイルのカラム名が表示され、プロパティの横にあるプレビューではデータの中身を確認できます。 商品マスタの設定をします。こちらは、先ほど作成したインテリジェント構造モデルを利用して、半構造化データであるJsonファイルを読み取れる形で出力するために、 構造パーサー という変換部品とJsonファイルへのパスが記載されたテキストファイル(JSON_File.txt)を使います。 変換部品から ソース を選択して、プロパティで ソース > 接続 からあらかじめ作成しておいたフラットファイルコネクタを選択。 オブジェクト にて商品マスタへのパスが記載されたファイル(JSON_File.txt)を選択。 変換部品から 構造パーサー を選択。プロパティにて 構造パーサー > インテリジェント構造モデル から先ほど作成したインテリジェント構造モデルを選択。 ソースと構造パーサーを繋げて、 構造パーサー のプロパティの フィールドマッピング にてPATH(JSON_File.txtに記載されているJSONファイルのパス)をFilePath(product.jsonのカラム)にドラッグアンドドロップ。 これでデータの読み込みは完了です。次にこちらを ジョイナー というパーツを使って売り上げデータと商品マスタを結合します。 ジョイナー という変換部品を選択 売り上げデータと商品マスタを ジョイナー に繋げる。商品マスタをmasterに、売り上げデータをdetailに入れます。 ジョイナー のプロパティにて設定をします 受信フィールドにてそれぞれmasterとdetailに設定したファイルのどのカラムを抽出するかを設定します。今回は商品マスタのファイルからは Product_Code Product_Name Price_TAX を、売り上げデータに関しては Age amaount product_code store_code を持ってきます。 結合条件でそれぞれのファイルの Product_Code をキーに結合するように設定します。この際、結合タイプというのがあるのですがこれはmaster側のみ、もしくはdetailのみにあるprooduct_codeを含めるかどうかを設定します。今回ノーマルというものにしていてmaster側のみ、またはdetailのみにあるprooduct_codeは除外するという感じです 商品名変更 この後はデータ加工部分になります。 1つ目で一部商品名を変更して新しく作成したProduct_Nameに格納、2つ目で価格と販売量から収益を計算します。 では、まず1つ目ですがこちらは式という変換部品を利用します。 式 という変換部品を選択する。 ジョイナー と 式 を繋げる。 プロパティの受信フィールドにて利用するカラムを指定します。 フィールド選択条件 を 名前付きフィールド に変更、 設定 にて age amount M_PRODUCT_NAME M_UNI_PRICE_TAX product_code store_code を選択する。 プロパティの式にて以下の式を作成します。これは新しく product_name を作成して、そのカラムに product_code が A00001 で store _code の先頭が 19 のものに指定の名称を入れます。そして、それ以外のものは既存のカラムである Product_Name を格納するというものです。 IIF(product_code='A00001' AND SUBSTR(store_code, 1, 2)='19', '江戸前幕の内弁当',M_PRODUCT_NAME ) 収益計算 次に収益の計算をします。 アグリゲータ という合計や平均などを行う際に利用する変換部品を使用します。こちらはグループごとの集計も可能なので今回は収益を計算してかつproduct_Nameとage、商品名と年齢が同じデータをグループ化してみます。 アグリゲータ という変換部品を選択 プロパティの受信フィールドにて フィールド選択条件 を 名前付きフィールド に変更、 設定 にて age 、 amount 、 M_UNI_PRICE_TAX 、 product_Name を選択 プロパティの集計にて +ボタン 選択して新しいフィールドにて,以下のように設定して OK をクリック フィールドマップ:出力フィールド 名前:Revenue(適当な名前を入れてください。) タイプ:decimal 精度:10 スケール:0 作成したRevenueのフィールドにて、「式」の 設定 をクリックして以下の記載後、「検証」ボタンで式が有効か確認。こちらは商品の税込み価格と売り上げ数から1つの商品の売上合計金額を算出する式になります。 SUM(To_Decimal(M_UNI_PRICE_TAX) * To_Decimal(amount) ) product_Name と age が同じレコードをグループ化します。プロパティ グループ化 の「+」を選択、 フィールド名 から以下の2つをそれぞれ選択 Product_Name age Oracleへの格納 ここまでがデータ加工の部分で最後にこれらをあらかじめ作成しておいたオラクルのテーブルに格納していきます。 ソース を選択 プロパティの 受信フィールド にてどのカラムを使うかを設定 プロパティの ターゲット > 接続 からあらかじめ作成しておいたoracleコネクタを選択。 オブジェクト > 選択 にてあらかじめOracle側で作成しておいたテーブル名を選択 フィールドマッピング にてどのカラムに今回作成したカラムを格納するかを設定。Oracleのテーブル側に今回作成したカラム( Product_Name 、 AGE 、 REVENUE )と同じ名称のカラムを作成しているので 同じ名称ごとに対応付けします。 以上でマッピングは完了です。以下がマッピングの完成形となります。 では、最後に実行してみましょう。画面右上の 保存 と 実行 をクリックすると今回作成したマッピングが実行されます。 どうでしたか?問題なく動きましたでしょうか?エラーが出た場合はエラーログも見られるので確認してみてください! 最後に 今回はインフォマティカの簡単なマッピングを作成しました。ご紹介したのはあくまで一例で、 Cloud Integration Hub というハブの機能や Cloud Application Integration というAPI開発の機能などIDMCにはいくつもの機能があります。 今後、DXやデータ活用が進む中そのためにデータ統合は必須の機能になっていくかと思いますので、この記事が参考になっていれば幸いです。 ご覧いただきありがとうございました! それでは、明日の記事もお楽しみに! 参考リンク Smart Data Platform(SDPF)とは NTT Com Knowledge Center(データ統合インフォマティカ ソリューション) インフォマティカ 1か月の無償トライアル
この記事は、 NTT Communications Advent Calendar 2022 10日目の記事です。 こんにちは! SDPF クラウド/サーバー ESI チーム入社1年目の飯國 ( @guni1192 ) です。 普段は SDPF クラウド/サーバーにおけるネットワークコントローラ ESI (Elastic Service Infrastructure) を開発しています。 今回は ESI チームにおける CI 改善の取り組みについて紹介します。 CI/CD をセルフホストしている方向けに、CI の Workflow や実行基盤の改善の一例として参考になればと思います。 今までの ESI チームの CI 基盤の課題 ESI チームでは Jenkins を運用していました。 ESIの開発当初(6、7年前)から大きく構成は変わっておらず、チーム内から以下のような問題点があげられました。 課題1: CIのJob内容が Jenkinsfile でコード管理されておらずブランチ単位でのJobの設定変更が不可能 変更する際は管理者となっている社員に依頼しなければならない 課題2: Jenkins Agent に Jobの実行に必要な依存関係(言語, ライブラリ, ミドルウェア)が溜まる サーバのストレージ容量が逼迫する (ストレージ容量を空けるためのオペレーションコスト) ソフトウェアの依存関係を正しくテストできない。 CI の実行環境は毎回作って壊してクリーンな状態にしたい 課題3: Flaky Test が多く、CI の Job が2つ以上溜まっているとほぼFailする 課題4: CI がパスするまでの時間が長い (3-4時間) この様な状態だと、1営業日で PR をマージすることはほぼ不可能な状態です。 ESIチームのCI基盤の要件 これまでの Jenkins の課題を踏まえ、CI基盤及びパイプラインを再設計することにしました。 以下の要件達成を目指します。 要件1: 開発者自身 が必要なワークフローを定義し、バージョン管理できる → 課題1 要件2: コンテナやVMなどの使い捨ての環境でCIを実行したい → 課題2 要件3: 環境要因で Fail しないキャパシティ → 課題3 要件4: CI が長くとも1時間程度で終了する → 課題4 GitHub Actions への移行 GitHub Actions で前述した要件を達成可能か検証しました。 GitHub Actions は GitHub が提供する CI/CD プラットフォームです。 開発者自身がWorkflowをYAML形式で定義し、リポジトリにPRを送ることでCI/CDパイプラインを作成できます(要件1達成)。 GitHub Actions に移行するにあたって、CI の Job を実行するサーバ(Runner)は Self-Hosted Runnerを用います。 Self-Hosted Runner は名前の通り、自分でベアメタルサーバ、VM、コンテナを用意し、その上で CI の Job を実行する Runner です。 Self-Hosted Runner についての社内事例を2つほど紹介します。 engineers.ntt.com engineers.ntt.com actions-runner-controller + Google Kubernetes Engine Self-Hosted Runner のオーケストレーションには actions-runner-controller を用います。 actions-runner-controller は Runner を Kubernetes の Custom Resource として扱うことができる Custom Controller です。 actions-runner-controller は Runner を Pod として提供し、使用されたら破棄する Ephemeral Runnerを標準で採用しています(要件2達成)。 また、Runnerの オートスケーリングや Prometheus Metrics の出力などが可能です。 今回は Kubernetesクラスタの管理に Google Kubernetes Engine(GKE) を使用しました。 とりあえず GitHub Actions へ移行してみた GKEに手っ取り早く環境構築して、Jenkinsのときと同じ様なフローをGitHub ActionsのWorkflowに作って検証してみました。 この時点で要件3、要件4は未達成で、WorkflowとGKEの構成の両方に手を入れる必要がありました。 Flaky Test の解消 Jenkinsを使っていたときから「何もしてないのにCIがFailする」という事象が頻発し、人がCIを利用していない時間帯を見計らってジョブを流すということをしていました。 CI 基盤が開発する上で大きなブロッカーになってしまっていました。 まずは原因を調査するために Grafana, Prometheus, Node Exporter, kube-state-metrics を導入し、Runner のパフォーマンスを計測しました。 原因は、Node の Disk I/O が過負荷になることで、Integration Test内でDBへの書き込みがタイムアウトになることでした。 対策としては大きく2つあげられます。 クラスタ全体のDisk I/Oのスループットを上げる タイムアウト値の緩和 今回は、できるだけテストコードを修正して仕様を変えたくなかったため、タイムアウト値の緩和は見送りました。 クラスタ全体のDisk I/O性能をあげるためにクラスタの構成や Kubernetes の設定を修正して、Job を処理できるようにしました。 Disk I/O の負荷分散 まず、Nodeの台数(=local storageの台数)を増やし、負荷を分散させます。 Node数を増やしただけでは、Kubernetes が Pod (Runner) を Disk I/O の多い Node にスケジュールして Disk I/O が偏る可能性があります。 そのため、 Pod Topology Spread Constraints などの仕組みを使って Pod を Node に分散配置します。 しかし、従来の Workflow では1つの Job を実行する Pod の Disk I/O が非常に高いため、1つの Job を複数の Pod に分散させるように Workflow を改修する必要があります(後述)。 ストレージ単体の性能を上げる (SSD化) Node のローカルストレージを HDD から SSD に変更しました。 クラスタ全体のDisk I/Oのスループットを上げた結果 数ヶ月運用し、今まで Disk I/O 要因で起こっていたと思われる Flaky Test が報告されなくなくなりました(要件3達成)。 それはそれとして、定量的に Flaky Test を検知する仕組みが必要だと考えたので今後の課題にしたいと思います。 CIの実行時間の短縮 次にCIの実行時間の短縮を図ります。 これまで ESI の Integration Test は Docker Compose で API, DB, 外部サービスの mock を含む複数のコンテナを構築し、APIのテストを行っていました。 Jenkins のときは Jenkins Agent はベアメタルサーバで容易にスケールアウトできる環境ではなかったため、 1つの Jenkins Agent 内で 複数の Docker Compose 環境を作ってテストしていました。 この構成だと、並列数(==テスト環境数)が大きく制限されてしまいます。 なぜなら、1つ Runner (Pod) 内で複数の環境を作ってしまうと、1つのNodeのlocal storageに負荷が集中するためです。 Disk I/O の負荷分散について前述しましたが、従来の構成ではほぼ機能しない仕組みとなっているのはこのためです。 Disk I/O の負荷分散及び、並列数をスケーラブルにするため、複数台の Runner によってテスト項目ごとにテストを分散実行するように Workflow を再設計しました。 ESIチームでは大きく分けて14種類のテスト項目がありました。 つまり、14並列でテストを実行すれば、CIの待機時間は 14種類のうち、一番長い実行時間のテスト項目となります。力技ですね。 今回はそれぞれテスト項目のチューニングを行った結果、実行時間が最長のテスト項目は1時間だと言うことがわかりました。 これにより、並列数次第ですが最短1時間程度でCIをパスできます。 Workflowの改修結果 Runnerを10並列で動かした結果、実行時間の大幅な短縮に成功しました(要件4達成)。 まとめ 今回は SDPF クラウド/サーバー ESI チームにおける CI の改善に関する取り組みについて紹介させていただきました。 CIはソフトウェアが常に動くことを保証するために欠かせないものです。 Workflow や基盤の両方から改善することは開発者体験の向上にも繋がります。 GitHub Actions on GKEに移行することで、開発者主体でワークフローを設計し、CIの実行時間の短縮、Flaky Testの解消ができました。 今後は、Self-Hosted Runner の信頼性の計測 (Prometheus Exporter の実装) や、Disk I/O のレイテンシを考慮した Kubernetes Scheduler の実装なども考えていきたいと思っています。 また、GitHub のロードマップに Self-Hosted Runner の Kubernetes への対応が入っているので注目です。 github.com ここからは宣伝です。 SDPF クラウド/サーバーは国内最大級の IaaS です。ESI チームはネットワーク機器のオーケストレーションを行うためのソフトウェアを開発しています。 ESI チームはインターンシップの募集も行っているため、クラウドサービスのソフトウェア開発に興味がある学生さんはぜひご応募ください。 ESI チームのポストは、 エンタープライズ向け大規模クラウド/ネットワークサービスを支えるコントローラ開発 内の クラウドネットワーク/仮想アプライアンス です。 締切(12/14)が迫っているため、ご応募はお早めに! information.nttdocomo-fresh.jp
目次 目次 はじめに ECCV2022概要 Workshop Instance-Level Recognition Workshop Keynote talk: Image Search and Matching Kaggle Google Universal Image Embedding Challenge Keynote talk: Few-Shot Learning for Object Aware Visual Recognition Language Assisted Product Search Granularity aware Adaptation for Image Retrieval over Multiple Tasks Where in the World is this Image? Transformer-based Geo-localization in the Wild" What to Hide from Your Students: Attention-Guided Masked Image Modeling Keynote talk: Instance level recog for SSL, and vise-versa 3rd Advanced Autonomous Driving Workshop 最後に はじめに こんにちは、イノベーションセンターの鈴ヶ嶺・加藤・齋藤です。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。10月23日から27日にかけて、コンピュータービジョン分野におけるトップカンファレンスのひとつである ECCV2022 がオフラインとオンラインのハイブリッドで開催され、NTT Comからは複数名オンラインで参加しました。その参加レポートを前後編に分けて紹介します。 前編では会議の概要とWorkshop、Kaggle、Keynote talkについて紹介します。後編では、論文の紹介をしたいと思います。 ECCV2022概要 ECCVは2年に1度開催されるコンピュータービジョン分野におけるトップカンファレンスのひとつです。今年は現地(Tel Aviv)とオンラインのハイブリッド開催でした。採択率は25.3%(1645/6773)となっております。ECCV2022でも、中国やアメリカの採択が割合を大きく占めているようです 1 。 Workshop Instance-Level Recognition Workshop https://ilr-workshop.github.io/ECCVW2022/ このワークショップではinstance-level recognitionという、固有名詞レベルで物体を分類する技術を取り扱っています。日本語では特定物体認識と呼ばれることが多いようです。この技術は以下のような応用が考えられます。 ARを用いた美術館や遺跡の説明 コマースにおける商品認識 画像検索 このタスクは以下のような特徴があり、特に多様性のあるデータセットの構築が困難であるようです。 Large-scale: 一般物体認識に比べカテゴリ数が遥かに多く、例えば観光地の画像を収集したGoogle Landmark Dataset (GLD) v2では200,000カテゴリ存在する。 Long-tailed: 有名どころならカテゴリあたり1000画像以上あるが、5枚以下しかないカテゴリもかなり存在する。 Limited appearance: 一部しか見えていないことがあるが、大抵の認識対象は剛体なので局所特徴量を比較する画像マッチングが役立つ。 本ワークショップでは、instance-level recognitionのさまざまな手法の紹介に加え、より広い分野にわたって収集された画像データセットや、それを用いたコンペティションの紹介も行われました。この章では各発表の概要を紹介していきます。 workshopの歴史 本ワークショップは2018年のGLDv1を用いた1st landmark detectionから始まりました。当時は観光地画像のみを対象としたlandmark detectionでしたが、近年は物体一般のinstance level recognitionを目標としており、今回は以下の2つのコンペティションが紹介されました。 Kaggle Google Universal Image Embedding Competition 2022 Amazon Alexa Language Assisted Product Search Challenge それぞれのコンペティションの概要はのちの節で述べます。 Keynote talk: Image Search and Matching 画像検索と画像マッチングの方法についての発表です。 一番基本的な画像マッチングは、画像から特徴点を抽出し、特徴量の近い点同士をマッチングしたのち、一番尤もらしい変換行列Hを算出します。 画像検索では画像間の視点がしばしば著しく異なるものの、画像マッチングで使う局所特徴量を活用して画像の類似度を測ったり、マッチングにより算出した変換行列を用いて特徴点の位置関係を検証し偽陽性(局所的に似た箇所はあるものの異なる物体が写っている検索候補)を排除したりできます。しかし大規模なデータベースからの画像検索では、局所特徴量によるマッチングを総当たりでと計算量が膨大になるため、別の手法が必要になります。この発表では画像検索について2つの論文が紹介されました。 DELG 2 では、ニューラルネットワークによって画像から抽出された大域特徴量をデータベースに保存し、検索時にはクエリに類似した大域特徴量を持つ画像に対して、同じくニューラルネットワークから抽出された局所特徴量でマッチングを適用し検索結果を洗練させました。この後処理はRerankingと呼ばれています。 (元論文のFig. 1から) Instance-level Image Retrieval using Reranking Transformers 3 では、従来は候補画像の局所特徴量にRANSACをかけ幾何的に正しく対応している特徴点をカウントすることで(これをGeometric verificationと呼びます)行われていたRerankingをTransformerで行いました。大域特徴量と局所特徴量にそれぞれposional encodingを行い、Transformerに入力して類似度を予測しています。 (元論文のFig. 2から) ここで紹介されているように、大域特徴量のみ、局所特徴量の集合のみを使った比較よりも、大域特徴量とRerankingの融合はより精度の高い画像検索を可能にすることがわかっています。 また、より発展した画像検索の手法としてDrill-down 4 が紹介されました。この手法では自然言語によるプロンプトを検索の補助として活用しており、公園でとった記念写真のような、特定のランドマークが存在しない非常に困難なケースでも「右にピンクのコートを着た女性がいる」などの条件を追加していきながらインタラクティブに画像検索ができるようになっています。 ふつう画像には様々なものが写っており、検索するユーザーがどれに言及するかは明らかではありません。そこでこのモデルではユーザーの文章群をいくつかの話題にカテゴリ分けし、それぞれの話題についての言語特徴をまとめたものと、画像に写っているさまざまな物体の画像特徴をまとめたものとを比較し類似度を算出しています。 (元論文のFig. 2から) そしてこのモデルを学習する時は、画像のさまざまな領域に説明のアノテーションがついたVisual Genome dataset 5 を用いて検索クエリを再現しています。 最後の話題は画像と言語の統合でした。 近年では画像とキャプションのペアを集めた大規模なデータセットで学習したモデルを用いて、「A picture of a [category name]」というキャプションと入力画像との一致度を比較するというゼロショットの画像分類手法が好成績を収めました 6 。さらに、画像単位で与えられているキャプションをもとに、その記述が画像のどこを指しているのかをバウンディングボックスやヒートマップで可視化する説明手法なども提案されています 7 。 Kaggle Google Universal Image Embedding Challenge https://www.kaggle.com/competitions/google-universal-image-embedding クエリとして与えられた画像に同じものが写っている画像を検索できるような特徴量抽出機を作るコンペティションです。 一般的な手法では以下のようにドメインごとに学習されていました。 Google landmark dataset: 建物や観光地の画像 iNaturalist dataset: 動植物の画像 さまざまなドメインにわたって表現可能な特徴量の計算がこのコンペティションのゴールでした。 kaggleで行われたコンペティションのフォーマット 学習データは配布せず、条件なしで任意の公開データセットを使用可能とした 画像から64次元の特徴量を出力するPytorch/Tensorflowモデルを提出する Kaggle kernel上で9時間以内に評価用のデータセットから特徴量計算 & 5 nearest neighborsの計算が可能なモデルとする 評価データセットは以下のようにlarge-scale, long-tailedな特徴を持っています。 衣服、家具、建物など10以上の分野にわたって収集 5000枚のクエリ 20万枚の検索用データベース クエリの8割は正例が25枚未満, 57%は10枚未満のカテゴリだった。 Winning solutions 上位陣は以下のような手法を用いていました。 large-scale pretrained model CLIP-ViT-H/L pretrained on LAION 5B 今回自然言語はほとんど関係ありませんが、画像とキャプションのペアをもとに事前学習されたCLIPが広く使われました。 Mixing training dataset General Purpose: GPR1200 Landmark: GLD Products: Products10k, Deepfashion, Alibaba Goods Art: MET 公開されているデータセットを収集しファインチューニングに活用していました。 Data Augmentation Class Balancing Multi-resolution, Overlapping Patches 少量のサンプルしかないカテゴリの対策として行われたようです。 Model Ensembling Model soup: https://arxiv.org/abs/2203.05482 最終的な出力は特徴量なので、出力をアンサンブルするのではなくモデルの重みをアンサンブルするModel soupがよく使われていました。 Arcface 元は顔認識用の手法ですが、今回のように似たような画像の特徴量を近づける距離学習というタスクに広く使われています。 以下の節では上位陣の解法の概要を説明します。 1st place solution https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359316 今回アンサンブルがあまり効かず、その理由として、モデルごとに異なる特徴空間の値をただ平均するのが良くないのではという仮説を立てています。そこで特徴空間の形を決めるのは最後のprojection layerのみであるという仮定のもと、head部をフリーズしてbackboneのみをファインチューニングするという方法をとったところ性能が向上しました。 4th place solution Zeroshotでも強力なモデルが作成可能であることを示しています。 https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359998 GPT-3を活用し、さまざまな物体のテキストプロンプトを生成("Give me a list of 100 diverse dishes as a Python list"とGPT-3に入れると、stringのリストが手に入る) CLIPのtext encoderに通して特徴量を計算し、64次元にPCA 得られたprojection layerをvision encoderにくっつける 一方で最終的な解法は次のようになっています。 https://www.kaggle.com/competitions/google-universal-image-embedding/discussion/359487 特徴としては以下の3点が挙げられます。 model soupの活用 Arcfaceの利用 H-14とL-14-336の異なるモデルサイズのアンサンブル Keynote talk: Few-Shot Learning for Object-Aware Visual Recognition 少量の教師データをもとに推論するタスクをfew-shot learningと呼びます。例として、動画のあるフレームで映っている対象物体にアノテーションを施すと、残りのフレームに映る対象を自動でセグメンテーションしてくれるというものが挙げられます。このようなタスクは直接instance-level recognitionとは関係ありませんが、一方のテクニックを他方に応用できる可能性があると発表者は述べていました。 Few-shot learningは画像分類とセグメンテーションの分野で広く研究されていますが、セグメンテーションよりも細かい対応づけ、つまり異なる物体間の意味的に同じ部分(生物なら頭や足など)を対応づけるということはまだ上手くできていません。本発表ではこの「意味的な対応づけの学習」に注目したベンチマークデータセットSPair-71k 8 やマッチング手法としてハフ変換を活用するもの 9 、attention機構を導入したもの 10 、特徴抽出器のさまざまな中間層から特徴マップを取り出して相関をとるもの 11 などが紹介されました。 そしてより発展的な問題として、従来はクエリ画像に対して1種類の分類やセグメンテーションしかできなかったfew-shot learningをマルチラベルに拡張したFS-CS 12 というタスクが紹介されました。このタスクはより現実に即したものと言えます。 Language Assisted Product Search Amazonでは以下のようなインタラクティブな買い物ボットを実現するための研究をおこなっています。 ユーザーが「鞄が欲しい」と言うと、ボットが鞄の商品画像を表示 さらに「赤いのが欲しい」と言うと、赤い鞄の画像を表示 さらに「もっと小さいのが欲しい」と言うと、より小さな赤い鞄の画像を表示 本発表ではこれを目標としたコンペティションを設計しオープンしたことが語られました。 Single-Shot Language-Assisted Product Retrieval https://eval.ai/web/challenges/challenge-page/1845/evaluation 商品画像とそれに対するフィードバックの文章をもとに、その要求に応えた商品画像を検索可能な特徴ベクトルを生成するコンペティションです。 このタスクを行うための評価データセットの構築には次のような障害がありました。 large-scale: 扱う商品があまりに多すぎる上に、今あるデータセットはその一部しかカバーしていない。 similar products: 何千もの似た様な商品が存在し、クエリごとにそれら全てを正例としてアノテーションするのは現実的でない。既存のデータセットは正例を1つに限定してしまっている。 diverse language: フィードバックのプロンプトは多様性に満ちており、単語ではなく文章を喋っているので自然言語処理が必要。 これらを意識し次のようなデータセットが作られました。 学習データ 100万枚の画像データベース 15000件のproduct triplets(クエリ画像1枚とフィードバック3件の組) 衣服のみ 評価データ 100万枚の画像データベース (学習データと重複なし) 15000件のproduct triplets 衣服と家具 これを構築するために3万件のproduct tripletsがアノテーションされましたが、これらは以下のように行われました。 元商品、欲しい商品、欲しくない商品の組を見せ、欲しくない商品を避けつつ元商品から欲しい商品に替えてくれるようなフィードバックをアノテーターに書かせる。 欲しい商品に似た画像を50枚収集し、元画像+フィードバックにマッチするものを正例に加える。 最終的に8500クエリを追加アノテーションし、そのうち79%が単一の正解サンプルをもっていて、5%が5件以上の正解サンプルを持っているという結果になった。 Baseline model VAL 13 というモデルが公式のベースラインとして利用されました。このモデルの特徴は次のようになっています。 フィードバック文章はLSTMに通して特徴を生成する。 クエリ画像は段階的に畳み込みながら、各レベルで文章特徴とcross attentionを行い中間特徴を生成する。 ターゲット画像はフィードバックとのcross attentionを行わずに中間特徴と比較しクエリの中間特徴に近づける。 このコンペティションは2023/1/1に終了予定です。 今後の方針として、複数回のフィードバックへの対応や、ユーザーがアップロードした画像に対応することなどが挙げられていました。 Granularity-aware Adaptation for Image Retrieval over Multiple Tasks 14 画像検索タスクに使われるモデルは基本的に様々な分野にわたる画像に対応していますが、対象を狭くしてより強力なモデルを作りたいというケースがあります。しかし新しく対象の分野でのデータセットを構築するのはコストが高く、また画像検索タスクに使われるモデルはドメイン変化に弱いことが知られています。そこでラベルなしのデータセットを使ってドメイン適応したいというのが本発表のモチベーションであり、巨大な事前学習済みモデルの効率的な転移学習を可能にするTransformerベースのAdapterFusion 15 をラベルなしデータセットに応用しました。 本手法ではラベルなしデータセット全体で特徴量を計算し、クラスタリングした結果を擬似ラベルとしてAdapterを学習します。工夫点として、対象のデータセットの粒度を段階的に細かくしていきながら学習を進めるということを行なっています。まず少ないクラスタ数で擬似ラベルを付けて転移学習し、段階的にクラスタ数を増やしながら転移学習を繰り返すことで精度の向上を図っています。 MRT dataset 評価用にMRTデータセットを利用しました。このデータセットは以下の6つのデータセットの合成となっています。 Aircraft Cars CUB Flowers Food-101 Products まずデータセット全体でモデルを学習し、評価時は各分野のテストデータを使って別々に精度を測り、それぞれのタスクに特化した検索ができているか評価します。 Where in the World is this Image? Transformer-based Geo-localization in the Wild Geo-localizationとは、画像から緯度経度を予測するタスクです。 データセットが大規模であること、時刻・天気・季節といった変数によって画像が大きく変化することなどがマッチングを困難にしていることが知られています。また似たような建物を他の地域が建てることもあるため注意が必要です。 Approach Vision Transformerを利用し、以下のような工夫を施しています。 セマンティックセグメンテーションで情報をまとめることで時刻や天気に対するロバスト性を確保 シーンタイプを同時に予測する(自然、都会、屋内など)ことでシーンごとに必要な特徴量を意識させる Dataset 利用したデータセットは以下の通りです。 Training: MediaEval Placing Task 2016 (Flickrから収集した4.72M geo-tagged images) Validation: YFC26k (25.6k geo-tagged) Test: Im2GPS, Im2GPS3k, YFCC4k What to Hide from Your Students: Attention-Guided Masked Image Modeling Masked image modeling(画像の一部を隠して復元させる)を通したVisual Transformerのself-supervised learning手法についての発表でした。 既存手法ではランダムにPatchを隠していました(random erasing)が、以下のような提案手法によって分類性能を向上させています。 Visual Transformerのself attentionを活用した効果的なerasing attentionの高いPatchを優先的に消すことで、random erasingよりも難しいサンプルを生成できる ヒントとなる部分を少し残すことでさらに性能が向上する Keynote talk: Instance level recog for SSL, and vise-versa ラベルのない大量の画像でself-supervised learningを行い、画像検索やコピー検出などの後段のタスクに活用するという手法を紹介しています。 かつてのself-supervised learningは以下のように行われていました。 instance discrimination 同じ画像にさまざまなData Augmentationをかけ、頑健な特徴量を計算する 画像数と同じだけカテゴリを用意するためスケールしないという欠点がある Constrastive learning negative pairよりpositive pairの方が近い特徴量になるように学習 大規模なnegative pairsを収集する工夫がなされた SimCLR 16 : バッチサイズを大きく取り、バッチ内の画像間をnegative pairとした。 MoCo 17 : これまでの入力に対する特徴を記憶し、negative pairの相手として採用する。記憶している特徴量が学習中の特徴抽出器に対して古くならないように、記憶の仕組みはキュー型を採用した。 negative pairsを使わないSSL:DINO そもそもnegative pairを使わない手法としてDINO 18 などが提案されています。DINOの特徴として以下の点が挙げられます。 Teacher networkとStudent networkを用意し自己蒸留 studentはteacherの出力と一致するように重みをSGDで更新 teacherはstudentの重みに指数移動平均(EMA)で緩やかについていく ネットワークの出力が単一ラベルにつぶれたり一様に平たくなってしまう問題は、teacher側の出力にCenteringとSharpeningをかけ、意味のある出力をstudentに真似させることで克服 一般的な蒸留と異なり、teacherには正解ラベルが与えられないこと、またstudentはteacherの出力を真似てteacherがstudentのパラメータを真似るというサイクルができていること特徴的です。 Oxford / Parisデータセット 19 を用いた実験では以下のことが示されています。 ImageNet (w/labels) を使った教師あり学習より、ラベルなしでImageNetをDINOで学習したものの方が高性能だった。 学習データをGoogle Landmark Datasetに替えるとより高性能になった 画像/映像コピー検出への応用でもDINOが良い精度を出していることが示されています。 3rd Advanced Autonomous Driving Workshop https://avvision.xyz/eccv22/ 自動運転に関する本ワークショップでは、3次元物体認識やセグメンテーションに加え、運転シミュレーターを用いた運転経路の予測など自動運転に関する様々な技術が取り扱われています。今回が3回目で、過去にはWACV'21やICCV'21でも開催されているとのことです。ここでは、ワークショップの中でも印象的だった、Andreas Geiger教授の招待講演を紹介したいと思います。 Learning Robust Policies for Self-Driving この招待講演では Andreas Geiger 教授の研究室から今年2022年に発表された3つの最新論文が紹介されていました。各論文の概要を以下で説明します。 Transfuser 20 Transfuserは、複数センサーから得られるマルチモーダルデータを入力として適切な運転経路予測をするモデルです。カメラから取得したRGB画像と、LiDARセンサから取得した点群の鳥瞰図(Bird's Eye View, BEV)とを各々の特徴抽出器に入力し、中間層で各モーダルの特徴マップにTransformerベースのCross Attentionを適用することで、画像と点群それぞれの特徴抽出器の出力が他方のモーダルの情報で補間されると述べられています。CARLAシミュレーターで生成したデータセットで評価したところ、同一入力のベースライン手法に比べ規則違反なくルートを完走する性能が大きく向上することが示されていました。 PlanT 21 Transfuserがセンサー群から得られるデータを入力とするモデルであったのに対し、PlanTでは周囲の運転エージェント(近くを走行する車両)の情報が即時的に得られることを仮定し、その情報から算出される特徴量(object-level representation)を入力として運転経路の予測を行います。Object-level representationは、運転エージェントの位置、向き、大きさ、エージェントが搭載するセンサーのデータを前述のTransfuserに入力して得られる属性とから算出され、各エージェントのrepresentationが1つのトークンとしてBERT 22 ベースのモデルに入力されます。この論文でもCARLAシミュレーターから生成されるデータセットで評価を行っており、Transfuserを上回る運転性能が達成できることが示されていました。 KING 運転経路を適切に予測するエージェントを獲得するには車両が衝突するようなシナリオを含め学習することが好ましい一方で、実世界でそのようなデータを取得することは危険かつ困難です。代替手段として運転シミュレーターでそのようなシナリオを再現する方法が考えられますが、実際はシミュレーター上でも現実的な衝突シーンの再現にはコストがかかるという問題があります。そこでこのKINGというアプローチでは、車両の衝突を助長するよう設計した目的関数を最適化することで環境内のエージェントの行動を変化させ、得られた行動から衝突が発生するシーンを生成することが提案されています。車両モデルには微分可能なbicycle modelを採用することで、目的関数はバックプロパゲーションでエンドツーエンドに最適化することが可能です。CARLAシミュレーター 23 を用いて実験をしたところ、提案手法で生成された車両の衝突を含むシナリオは、勾配情報を用いないブラックボックス最適化で生成されたシナリオに比べ、衝突をより回避する運転エージェントの獲得に寄与することが示されています。 最後に 本ブログでは、ECCV2022の概要と私たちが興味を持ったWorkshopをご紹介しました。後編では、私たちが気になった論文を紹介するのでぜひご覧になってください。 NTT Comでは、今回ご紹介した論文調査、画像や映像、更には音声言語も含めた様々なメディアAI技術の研究開発に今後も積極的に取り組んでいきます。また一緒に技術開発を進めてくれる仲間も絶賛募集中です。 アカデミックな研究に注力したくさん論文を書きたい 最新の技術をいち早く取り入れ実用化に結び付けたい AIアルゴリズムに加え、AI/MLシステム全体の最適な設計を模索したい 2022年12月06日現在、NTT Comでは 現場受け入れ型インターンシップ のエントリーを受付中です。私達のチームからも、AIエンジニアカテゴリに メディアAI技術開発エンジニア/リサーチャー というポストを出しています。インターンを通じて、会社やチームの雰囲気、そして私たちの取り組みを知っていただく機会にできればと考えています。皆様のご応募、心からお待ちしています! https://eccv2022.ecva.net/files/2022/10/ECCV22-Welcome-Slides-for-web.pdf ↩ Bingyi Cao, A. Araújo, and Jack Sim. "Unifying Deep local and global features for image Search." ECCV 2020. ↩ Fuwen Tan, Jiangbo Yuan, and Vicente Ordonez. "Instance-level Image Retrieval using Reranking Transformers." ICCV 2021. ↩ Fuwen Tan, Paola Cascante-Bonilla, Xiaoxiao Guo, Hui Wu, Song Feng and Vicente Ordonez. "Drill-down: Interactive Retrieval of Complex Scenes using Natural Language Queries." NeurIPS 2019 ↩ Ranjay Krishna, Yuke Zhu, Oliver Groth, Justin Johnson, Kenji Hata, Joshua Kravitz, Stephanie Chen, Yannis Kalantidis, Li Jia-Li, David Ayman Shamma, Michael Bernstein and Li Fei-Fei. "Visual Genome: Connecting Language and Vision Using Crowdsourced Dense Image Annotations." 2016. ↩ Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger and Ilya Sutskever. "Learning Transferable Visual Models From Natural Language Supervision." 2021. ↩ Ziyan Yang, Kushal Kafle, Franck Dernoncourt and Vicente Ordonez. "Improving Visual Grounding by Encouraging Consistent Gradient-based Explanations." 2022. ↩ Juhong Min, Jongmin Lee, Jean Ponce and Minsu Cho. "SPair-71k: A Large-scale Benchmark for Semantic Correspondence." 2019. ↩ Juhong Min and Minsu Cho. "Convolutional Hough Matching Network." CVPR 2021. ↩ Seungwook Kim, Juhong Min and Minsu Cho. "TransforMatcher: Match-to-Match Attention for Semantic Correspondence." CVPR 2022. ↩ Juhong Min, Dahyun Kang and Minsu Cho. "Hypercorrelation Squeeze for Few-Shot Segmentation." ICCV 2021. ↩ Dahyun Kang and Minsu Cho. "Integrative Few-Shot Learning for Classification and Segmentation." CVPR 2022. ↩ Yanbei Chen, Shaogang Gong and Loris Bazzani. "Image Search With Text Feedback by Visiolinguistic Attention Learning." CVPR 2020. ↩ Jon Almazán, Byungsoo Ko, Geonmo Gu, Diane Larlus and Yannis Kalantidis. "Granularity-aware Adaptation for Image Retrieval over Multiple Tasks." ECCV 2022. ↩ Jonas Pfeiffer, Aishwarya Kamath, Andreas Rücklé, Kyunghyun Cho and Iryna Gurevych. "AdapterFusion: Non-Destructive Task Composition for Transfer Learning." EACL 2021. ↩ Ting Chen, Simon Kornblith, Mohammad Norouzi and Geoffrey Hinton. "A Simple Framework for Contrastive Learning of Visual Representations." ICML 2020. ↩ Kaiming He, Haoqi Fan, Yuxin Wu, Saining Xie and Ross Girshick. "Momentum Contrast for Unsupervised Visual Representation Learning." CVPR 2020. ↩ Mathilde Caron, Hugo Touvron, Ishan Misra, Hervé Jégou, Julien Mairal, Piotr Bojanowski and Armand Joulin. "Emerging Properties in Self-Supervised Vision Transformers." ICCV 2021. ↩ Filip Radenović, Ahmet Iscen, Giorgos Tolias, Yannis Avrithis and Ondřej Chum. "Revisiting Oxford and Paris: Large-Scale Image Retrieval Benchmarking." CVPR 2018. ↩ Prakash, K. Chitta, and A. Geiger. Multi-modal fusion transformer for end-to-end autonomous driving. In Proc. IEEE Conf. on Computer Vision and Pattern Recognition (CVPR), 2021. ↩ Katrin Renz, Kashyap Chitta, Otniel-Bogdan Mercea, A. Sophia Koepke, Zeynep Akata and Andreas Geiger. PlanT: Explainable Planning Transformers via Object-Level Representations. CoRL 2022. ↩ I.Turc, M.-W. Chang, K. Lee, and K. Toutanova. Well-read students learn better: On the importance of pre-training compact models. arXiv.org, 1908.08962, 2019. ↩ Dosovitskiy, A., Ros, G., Codevilla, F., Lopez, A., Koltun, V.: CARLA: An open urban driving simulator. In: Proc. Conf. on Robot Learning (CoRL) (2017) ↩
この記事は、 NTT Communications Advent Calendar 2022 9日目の記事です。 対象読者 / わかること 対象読者 IoT デバイス接続の難しさに頭を抱えている 「クラウドにデータを送信する」までの要所をざっくり理解したい とにかく IoT を道具として使ってみたい、始めてみたい わかること 「クラウドにデータを送信する」までの一連の流れ Things Cloud を活用した「お手軽IoT」の始め方 はじめに こんにちは、Things Cloud のソリューションアーキテクトチーム 竹村です。私たちのチームは、5G&IoTサービス部で IoT プラットフォーム「 Things Cloud 」を活用したソリューションアーキテクトを担当しています。 早速ですが、皆さんは「IoT」と聞いて何を連想しますか。モノ同士が通信すること、クラウド上でデータを可視化すること、それとも現地の機器を自律制御することでしょうか。もちろんこれらは IoT で実現できることですが、共通していることがあります。それは 「何か実現したいことに対する手段であり目的ではない」 いう点です。 皆さんが IoT を活用して目指すゴールは、品質向上・コスト削減・事業拡大・業務DXなど様々だと思います。IoT によって、「現状把握→仮説立案→効果検証」の PDCA サイクルを高速に回すことも、自動化してビジネスをスケールさせることも可能になります。 つまり、IoTは 「数ある手段の中でも強力な手段の1つ」 とも言えます。 しかし、どうやったら IoT を導入できるのか、頭を抱えている方も多いのではないでしょうか。 IoT の技術領域は「デバイス」「ネットワーク」「クラウド」「データ利活用」「セキュリティ」など多岐にわたるため、いざ検討を始めるとサービス選定やシステム構築などの場面で多くの課題に直面します。 本記事では、「IoT を道具として使いこなしたい」「仮説検証や商用導入に多くの時間を使いたい」皆さんのご要望にお応えするため、Things Cloud とパートナーデバイスを活用した「お手軽IoT」の始め方についてご紹介します。 クラウドにデータを送信するには? それでは、IoT を道具として使いこなす第一歩「データをクラウドに送信する」までの STEP をみていく前に一度、私たちがハサミを使う場面を想像してみてください。 私たちはてこの原理を意識しなくても「なんか刃の奥の方が切りやすいぞ」と経験上学習して知っています。それと同じように、「なんとなく分かった」状態で IoT 使い始めることを目標に、肩の力を抜いてこれからお話しする内容もご覧いただければと思います。 そうです、IoT はただの道具です。(しかし、強力な道具です!) まず、IoTを構成する要素として、センサーと IoT-GW (ゲートウェイ)といったデバイスがあります。 センサーは、物理情報を科学原理に基いて信号に変換する機器のことで、温湿度センサーなどが存在します。IoT-GW は、センサー等の機器とクラウド間を中継する機器のことです。 「データをクラウドに送信する」には主に以下の3つの STEP があり、多くの場合では、IoT-GW のソフトウェアが3つの STEP を実行します。 センサープロトコル変換 センサーから送信される多種多様な形式のデータを解釈 データ形式変換 デバイスのデータ情報をクラウドが理解するデータモデルに変換 安全な通信 安全な通信(HTTPS / MQTTS など)によるクラウドへのデータ送信 それでは、それぞれの STEP について詳細を見ていきましょう。 STEP1:「センサープロトコル変換」 まずは、「プロトコル」という言葉について確認します。 プロトコルって? プロトコルとは、「約束された手順・ルール」のことです。 通信の世界におけるデータの送受信は、プロトコルと呼ばれる共通のルールのもとで実現されています。人間のコミュニケーションで例えると、日本語や英語などの共通の言語を用いることで情報の伝達を実現しているのと同じイメージです。 しかし、勝手にプロトコルを定めていくとその数は爆発的に増加し、ルールを知っている機器のみが通信できる状態になってしまいます。これは、地域・国ごとに異なる方言・母国語が存在している状況下で、コミュニティー間で言語による情報伝達に支障が生じる状況に似ています。 そのため、プロトコルは規格化(共通ルール化)されていき、様々なセンサーやデバイス間で共通のプロトコルを用いた通信が可能になります。グローバルなビジネスの場では、多くの人がコミュニケーションできるように英語を利用するようなイメージです。 センサープロトコル変換 さて、センサーの通信プロトコルも多岐にわたるため、やり取りされるデータ形式も多種多様です。そのため、センサーが利用するプロトコルやデータ形式に応じて、データを解釈する必要があります。 例えば、温湿度センサーからデータを受信する以下のようなケースを考えてみます。この場合、センサーのデータ形式に基づいて「今は温度27℃で湿度72%だな」と解釈できます。 # 例:温湿度センサー # 表現形式: 電文の4Byte の先頭 2Byte が温度(℃)、残り 2Byte を湿度(%)を表す 1b48 → 0001101101001000 → 00011011 , 01001000 → 27 , 72 → 27 ℃, 72 % また、プロトコルには手順・ルールだけでなく、このデータ形式まで規定しているものもあります。機器/メーカーによらずデータ形式を規定しているプロトコル(EnOceanなど)や、機器/メーカーごとに独自のデータ形式を用いるプロトコル(Modbus、BLE、LoRaWANなど)などがその例です。前者は機器/メーカーによらずソフトウェアを共通化でき、後者は機器/メーカーごとに開発が必要になります。 つまり、 各機器ごとにデータを解釈するためのプログラムが必要 となる可能性があるということです。 # 【参考】プロトコルの分類例 # 用途・通信距離等の分類 ・産業系プロトコル(Modbus など) ・近距離無線通信系プロトコル(Wi-Fi、BLE、Wi-SUN、ZigBee、EnOcean など) ・LPWA系プロトコル(LoRaWAN、Sigfox、LTE-M など) # データ形式での分類 ・データ形式を規定しているプロトコル(EnOcean など) ・独自のデータ形式を用いるプロトコル(Modbus、BLE、LoRaWANなど) STEP2:「データ形式変換」 センサーから得られたデータをクラウド側が要求するデータ形式に変換します。具体的には、対象とするクラウドが対応している以下のようなデータ形式へ変換します。 構造化データ(Excel、CSV、RDBデータなど) # 例:CSV key1,key2,key2 value1,value2,value3 半構造化データ(JSON、XMLなど) // 例:JSON { " key1 ": " value1 ", " key2 ": " value2 " } 非構造化データ(規則性のないテキスト、PDF、画像、音声、動画など) # 例:上記画像ファイルのビット列をHEX(16進数)表記したもの 89504e470d0a1a0a0000000d49484452 ... f87fbd0a6e6b89b8ca800000000049454e44ae426082 STEP3:「安全な通信」 クラウド側が対応している通信方式によってデータを送信します。多くのクラウドでは、HTTPS や MQTTS といった通信経路上を流れるデータを暗号化する通信プロトコルに対応しており、都度適切なデータ形式及び方法を用いて、変換後のデータを送信します。 データを送信するだけでも一苦労 いかがでしょうか、「データをクラウドに送信する」までにこのような STEP を経ることで、データの可視化やアラーム判定などのデータ利活用が開始できる状態になります。 ここまでで、「センサー/クラウドごとに開発がいるのか、大変そう」「データ利活用するまで先が長い」など少し難しそうだなと思われた方もいらっしゃるのではないでしょうか。 この記事を最後まで見ていただければ、「お手軽IoT」を始められますのでご安心ください。 「お手軽IoT」を実現するための道具として、「 Things Cloud 」というクラウドと、「 OpenBlocks 」というデバイスをご紹介します。 Things Cloud について Things Cloud は、NTT Communications の Smart Data Platform (略称SDPF) の中で、データ収集機能を提供するプラットフォームです。 センサーにつながる IoT デバイスからデータを収集し、蓄積できるクラウドサービスで、簡単な操作でデバイス情報を閲覧したりデータを可視化したり、ユーザー管理/通知/デバイス管理等、IoT に必要な機能一式が揃っています。 1 また、LoRaWAN 2 や Sigfox 3 ネットワークとの接続機能を有しているため、これらのプロトコルに対応したセンサーの選択が可能です。 それでは、 Things Cloud における「データ形式変換」と「安全な通信」について確認していきましょう。 Things Cloud における「データ形式変換」 Things Cloud では JSON 4 または CSV 5 形式のデータを要求するため、センサープロトコル変換等で得たデータを上記の形式に変換するプログラムが必要となります。 // 例:温度データ(JSON) { " c8y_TemperatureMeasurement ": { " T ": { " value ": 27 , " unit ": " ℃ " } } , " time ":" 2022-12-09T10:00:00.000+09:00 ", " source ": { " id ":" .... " } , " type ": " c8y_TemperatureMeasurement " } # 例:温度データ(CSV) 200 ,c8y_TemperatureMeasurement,T, 27 Things Cloud における「安全な通信」 Things Cloud では HTTPS(JSON 形式)または MQTTS(基本的には CSV 形式)に対応しており、都度適切なデータ形式及び方法を用いて、変換後のデータを送信します。 OpenBlocks について 本記事で紹介する OpenBlocks は、IoT-GW 機器として使うことができます。IoT-GW は、「データをクラウドに送信する」ための3つの STEP の処理を行う主体でもあります。 それでは、OpenBlocks が持つ「センサープロトコル変換」に関する機能に注目して、ご紹介していきます。 OpenBlocks における「センサープロトコル変換」 OpenBlocks は様々な通信規格やプロトコル(BLE・EnOcean・Wi-SUN・スマートメーター・Modbus等)を用いた主要メーカーの多種多様なセンサーに対応しており、Webブラウザ上での設定だけで接続できるという特長を持っています。 そのため、OpenBlocks が利用したいセンサーに対応していれば、「センサープロトコル変換」機能を開発することなく該当センサーを利用可能です。 6 「お手軽IoT」の始め方 ここまでで3つの STEP についてまとめると下記の通りです。 センサープロトコル OpenBlocks の持つ機能を利用することで多種多様なセンサーに対応可能 データ形式変換 Things Cloud が対応する JSON または CSV に変換するソフトウェアが必要 クラウドへの安全な通信 変換後のデータを HTTPS または MQTTS によって送信するソフトウェアが必要 上記の通り「データ形式変換」「安全な通信」の機能を持つソフトウェアを開発する必要があります。これらの機能を補完する「お手軽IoT」を始めるためのソフトウェア(以降、「データ送信用ソフトウェア」と呼びます) を開発しましたのでご紹介します。 データ送信用ソフトウェアは Node-RED 7 で実装されています 現在は EnOcean のみの対応 8 ですが、開発を継続して順次対応デバイス数を増やしていきます EnOcean の詳細については こちら をご参照ください 「お手軽IoT」の始め方について、EnOcean センサー利用を例にご紹介します。 データ送信用ソフトウェアのご利用については 問い合わせ先 からご連絡ください。 OpenBlocks の設定 OpenBlocks に関する操作・設定については、 OpenBlocks のマニュアル をご参照ください。 センサー接続 まずは、OpenBlocks にセンサーを接続します。 EnOcean の場合、無線プロトコルであるため物理的な接続はありませんが、機器によっては電源を ON にします。 センサー登録 利用するセンサーの情報を OpenBlocks に登録します。 センサー情報を登録するために、以下の項目を入力・保存します。 9 デバイスID:EnOcean におけるセンサーを識別する番号 ユーザーメモ:ユーザーが指定する任意の情報 EEP:EnOcean で定義されているデータ形式を示す番号 特に「デバイスID」「EEP」に入力する値は、筐体や取扱説明書などに記載されていることが多いため、そちらを参照します。 センサー情報を登録後、該当のセンサーに対して以下のように設定します。 10 初回は IoTデータ設定 及び ユニックスドメインソケット を参照して、以下の設定をしてください。 データ送信用ソフトウェアのインポート OpenBlocks 上で動作する Node-RED にデータ送信用ソフトウェアをインポートします。 Web ブラウザで Node-RED の実行環境にアクセスし、「読み込み」>「読み込むファイルを選択」にてデータ送信用ソフトウェアを選択します。 11 初回は OpenBlocks への Node-RED のインストール 及び アクセス方法 を参照して、必要な設定をしてください。 Things Cloud の設定 以下は Things Cloud 上での操作になります。 IoT-GW の ID 登録 Things Cloud に対して OpenBlocks の ID を登録します。 OpenBlocks 筐体の裏面に記載されている「SERIAL No.」の値を参照し、「デバイスID」に入力します。 接続の承認 Things Cloud で承認処理をします。 一定時間経過すると「承認」ボタンが出現するので、これをクリックします。 結果 上記の設定だけで Things Cloud にデータが送信・可視化されるようになります。「データをクラウドに送信する」までの3つの STEP がうまく隠蔽されていることを感じられたでしょうか。 まとめ IoT はビジネスにおける手段の1つ、だけど強力な手段 データ送信するまでには3つの STEP がある センサープロトコル変換 データ形式変換 安全な通信 Things Cloud と OpenBlocks を活用して今すぐ「お手軽IoT」をはじめよう 今後も NTT Communications は皆さんの IoT 活用をサポートしてまいります! 問い合わせ先 お手軽IoTを試してみたい方は、以下にお問い合わせください。 Things Cloud サービスについて:iot-info@ntt.com ※お手数ですが@を半角文字に置き換えてください それでは、明日の投稿もお楽しみに! https://engineers.ntt.com/entry/2022/03/01/130720 ↩ https://www.nttbizsol.jp/service/lorawan/about/ ↩ https://www.kccs.co.jp/sigfox/service/ ↩ https://developer.ntt.com/iot/docs/reference/measurements/ ↩ https://developer.ntt.com/iot/docs/device-sdk/mqtt/#mqtt-static-templates ↩ https://www.plathome.co.jp/partner-program/sensor-device/ ↩ https://nodered.jp/about/ ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/data_form/enocean ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/service/basic ↩ https://docs.plathome.co.jp/docs/openblocks/fw4/data_handling/enocean ↩ https://nodered.jp/docs/user-guide/editor/workspace/import-export ↩
この記事は、 NTT Communications Advent Calendar 2022 8日目の記事です。 サマリ OSS 公開中の Go による SDN コントローラー Pola PCE の開発ノウハウを紹介 開発・公開・運用に際してやったことと得られた Tips を紹介 (CI・ドキュメント・コンテナ・その他 Go 関連) はじめに イノベーションセンターの三島です。 普段の業務では Multi-AS Segment Routing(SRv6/SR-MPLS)や Telemetry などの技術検証、BGP 技術の検証と AS 運用などを行っています。 この記事では、SDN コントローラーを OSS として公開して得た知見を、Go による開発支援や GitHub を通じた公開・運用の Tips を交えつつご紹介します。 公開した OSS: Pola PCE 経路制御技術の Segment Routing(SR)において、発行した経路を管理するための SDN コントローラーである Pola PCE を開発・公開しています。 Pola PCE を SR 網に導入することで、大規模なネットワーク運用やアプリケーション単位での通信品質向上などを実現できます。 (プロダクトの詳細は後日また本ブログで解説予定ですので、ご期待ください。) Pola PCE は Go で開発し、2022年6月に OSS としてリリースしました。 2022年12月8日時点では v1.1.2 が公開中で、Go のパッケージ、クロスコンパイルしたバイナリ、Docker イメージの3種を配布中です。 開発の経緯と OSS 化に踏み切った理由 NTT Com では Multi-AS SR 技術を用いた社内ネットワークを運用しています。 その中で複雑な Traffic Engineering のユースケースとスケーラビリティを実現するため、継続的な機能追加やニーズに応じた拡張性を持ち、Multi-vendor 環境でも動作可能なコントローラーが必要となりました。 これらの要求を満たすため、Pola PCE を開発し自社ネットワークへ導入しました。 開発の当たってのより詳しいモチベーションは下記でご紹介しています。 大規模 SR 網の運用を効率化するネットワークコントローラの開発(NTT Tech Conference 2022) Segment Routing 用 Stateful PCE をフルスクラッチで開発した話 ネットワークで活用するソフトウェアは、開発するだけでなく、その後の機能拡張や他製品との連携が重要となります。 継続的な開発を進める上で、下記の3点を期待し OSS としての公開に踏み切りました。 ユーザや対応製品の増加・PCE 界隈の盛り上げ ネットワーク運用者に広く利用されることによる、プロトコル対応製品の増加や外部ツールの充実 多彩な環境での動作を通じた新規ニーズの開拓や知見の蓄積 新規ユースケースの獲得や機能要望の期待 コミュニティ運用による機能追加や知見の獲得 外部の Contributor の視点を取り入れることによる新規知見の獲得や機能向上 以降の章では、これらを達成することを目指した OSS 開発・公開・運用の Tips をご紹介します。 OSS 開発・公開・運用の Tips 前章の要件を満たすため、下記のポリシーで OSS 公開手法を検討しました。 広く利用者を増やすための工夫 Example の充実やドキュメント整備、SEO 対策 多彩な環境への適用 クロスコンパイル・Docker 等での配布 運用・メンテナンスの効率化 CI の 活用や標準的なプロジェクト構成への準拠、関連ライセンス表示等の自動化 それぞれのポリシーを満たすために実施した Tips を、(1) GitHub での公開・運用知見、(2) Go の開発支援、(3) その他 OSS 開発・運用の工夫の3つに分けて解説します。 GitHub での公開・運用知見 リポジトリ運営 Community Standards の整備 GitHub の公式機能として、リポジトリを潤滑に運用するためのチェックリストが公開されている 特に Description は検索時に表示されるページ名や後述の Docker イメージのラベルにもなるため重要 GitHub Actions あらかじめ作成したワークフローを自動実行し、CI を実現する機能 フォーマットに関するレビューやリリースなどの定型作業の負荷を低減 Pola PCE のワークフロー では、Git で Tag を付与した際にクロスコンパイルされたバイナリと Docker image をリリースする仕組みを作成 ドキュメント公開& SEO 対策 GitHub Pages プロジェクト全体に関する情報公開と SEO 対策を目的とした Web ページ gh-pages ブランチ を作成し、Setting → Pages より公開設定 SEO 対策の視点でも、GitHub のリポジトリページは Google 等のクローラーに認識されるのが遅い傾向にあるため、リリース直後の検索用にも作っておくと良い Pola PCE の場合、GitHub Pages は公開後半日程、GitHub のリポジトリページは公開後約2週間後に Google 検索結果へ表示された 環境構築方法を gh-pages/README.md に記載済。誰でも PR ベースで編集可能 下記の Hugo と Docsy を利用し、ページ作成を効率化 Hugo Markdown で記載したファイルから動的に Web ページを生成するツール カスタマイズの仕方などは Hugo のページ にまとまっている テンプレート一覧 も存在 Template を活用しつつ、Markdown ベースで手軽にブログが作成できるのは便利! Docsy 技術文書を書くためのテンプレート。gRPC・Selenium・etcd など様々なソフトウェアが使用中 各団体が 公式ページ で Example を提供している OSS プロダクトらしい公式ページを高速に開発可能。1 時間ほどで必要な機能・見た目のページを作ることができて助かった GitHub Container Registry(ghcr)による Docker イメージのリリース GitHub 上で Docker イメージを提供する機能 Docker Hub と異なり、公式機能で GitHub Actions との API 連携が可能 公式ワークフローを利用するだけで、リポジトリ情報・バージョンやライセンス情報などがラベルとして埋め込まれた Docker イメージを生成可能( 例 ) Docker イメージのも含め、全てを Github で完結して公開できるのは良い点 一方、Docker Hub と違いレジストリ名(ghcr.io)が必要になるため、イメージ名が長くなるデメリットも存在 その他 README.md の整備 トップページの README.md には、CI 状態や関連ページなどを Badge として付与 各階層にも README.md を配置し、ツールの使い方等を解説 コマンドの使い方や仕様などの記載は特に重要 main ブランチの保護 latest リリース破壊の防止 Setting → Branches → Branch protection rules Go の開発支援 プロジェクトの構造を Standard Go Project Layout に準拠 Standard Go Project Layout 可読性・保守性の高い Go プロジェクトのベストプラクティス 各ファイルの役割が明確になると共に、Go の開発者が理解しやすいプロジェクト構造になった linging/formatting golangci-lint CI から呼び出し、Go 関連の linting/formatting を行ってくれる機能 Push の度に GitHub Actions で実行 次の Go Report Card と合わせ、gocyclo のパラメータを調整することを推奨 Go Report Card lintng/formatting や 英語の綴りなどを確認し、Web 上で表示するサービス 設定不要でコードの確認ができるので便利 Go の文法的な lint/format の他、英語のスペル確認なども実行してくれる 結果は Badge として README.md に表示可能 editorconfig コードの統一性を持たせるための linter/formatter 開発者の環境を問わず、生成されるコードを統一する意図。 CI 実施前のチェックに利用 Vim/Emacs/VSCode など様々なエディターに導入可能 Go の場合その他の linter が充実しているため恩恵は薄いが、Markdown/YAML 他様々な形式に対応 いずれは開発環境の整え方のような手順を作る、あるいはネットワーク環境を含めた開発用コンテナを公開すると良さそう その他 gocredits Go で使用したライブラリのライセンスをまとめて表示してくれるツール OSS として公開する上で重要なライセンス管理をサポート 今のところリリースごとにローカルでコマンドを実行して利用中 Tag を付与した際、あるいは go ファイルが更新された際に自動実行するなど、CI に組み込むのが良さそう pkg.dev.go Go 公式のドキュメント機能 go install するだけでコードを自動解析してドキュメントを作ってくれるのは便利 その他 OSS 開発・運用の工夫 Getting Started や Example の整備 ツールの利用方法と試しやすい手順を公開することでユーザを増やす狙い Example は誰もが手軽に試すことができ、なるべく同じ環境で再現できると Good 手軽さと再現性の両立にはコンテナ環境がおすすめ Pola PCE は Tinet の設定ファイルを公開 Docker さえ準備すれば、誰でも気軽に設定済みの SR ネットワーク(D-Plane/C-Plane)を利用可能に! 実際に手元で動かした結果をもとにした問い合わせや感想もいただくことができた 公式アイコンの作成 トポロジー図・システム図等でソフトウェアを示すことができ、発表資料の顔にもなるので重要 遠目に Pola や PCE の P となるようにデザイン 公式ページや OSS の各所で利用するためのテーマカラーを決めておくと便利 Pola PCE では #81C0FC と #03498D の2色をテーマカラーとして設定、アイコンや GitHub Pages のテーマに利用 ステッカーを作成、JANOG 50 等のイベントで配布 興味を持っていただけた方が検索可能なように、名前入りのロゴを作成 自身で図やスライドを作成する際も便利であり、またこれをきっかけにお声がけいただいたりもしたため、やってよかった取り組みの1つ まとめ 本記事では、Pola PCE の開発経験を通じて得た知見を、Go 製 OSS の開発支援と・GitHub における公開の Tips としてご紹介しました。 各 Tips に記載した通り、各 CI やベストプラクティスに従うことでリポジトリ公開・運用やドキュメントの整備等を効率的に進めることができたと感じています。 より Pola PCE に寄せた感想としては、OSS としての公開を通じて、 研究での利用をしていただくなど、事前に期待した通りの一部 PCE 界隈の盛り上げやユースケースの創出に貢献できたかなと感じています。 また、今回はネットワークに関するソフトウェアであるため、 公開した Example ネットワーク環境を手元で動かした結果をもとに感想や問い合わせをいただくなど、裾野を広げることができたのもよかったポイントです。 OSS 公開を検討中の方や、開発に興味のある方はぜひそれぞれの Tips を参考にしてみてください。 また、その他こんな方法もあるよという知見をお持ちの方は、ぜひはてなブックマークやTwitterでコメントをお願いします! 宣伝 Pola PCE への Contribution 募集 Pola PCE の Contributor を募集中です! SR 網を運用中で、SDN 制御による運用効率化や新たなサービスを提供してみたい方 RFC/Internet-Draft 準拠のソフトウェアを開発してみたい方 お気軽に PR/Issue の作成をお願いします! 12/19 (月) に SR 視点で Pola PCE の内部構造と使い方を紹介する記事を公開予定ですので、ぜひそちらもご覧ください! また、PCE の活用事例も含め、SR の検証例を 連載記事 としてご紹介しています。こちらも合わせてご覧ください。 冬季インターンシップ開催のお知らせ 2022年度インターンシップの参加者を募集中です! 冬インターンシップ2022 公式ページ: 現場受け入れ型インターンシップ インターンシップ開催に関するブログ記事: 【コムで踏み切れ。】 冬期インターンシップを開催します! 期間は 2023年02月06日〜17日 のうち、土日祝を除く実働10日間です。 締め切りは 12月14日(水)13:00 までとなっていますので、興味のある方はお早めにご応募ください! 私たちのチーム( SR を用いたキャリアネットワークの開発 )では、SR-MPLS/SRv6 の技術検証や Pola PCE を含めた SR 周辺技術の OSS 実装等のテーマを用意しています。 また、参加者の希望するテーマをご提案いただくこともできます。 昨年は2名の方に参加いただき、FRRouting への BGP-LS の実装と、キャリアルーターを用いた SRv6 VPN の技術検証を実施していただきました。 体験記を寄稿してもらっていますので、こちらもぜひ参考にしてください。 インターンシップ体験記 〜BGP-LSの機能をFRRに実装してみた〜 インターンシップ体験記 〜SRv6 L3VPN機能検証〜
この記事は、 NTT Communications Advent Calendar 2022 7日目の記事です。 はじめに こんにちは、イノベーションセンター所属の志村と申します。 「Metemcyber」プロジェクトで脅威インテリジェンスに関する内製開発や、「NA4Sec」プロジェクトで攻撃インフラの解明・撲滅に関する技術開発を担当しています。 今回は「開発に使える脆弱性スキャンツール」をテーマに、GitHub Dependabot, Trivy, Grypeといったツールの紹介をさせていただきます。 脆弱性の原因とSCAによるスキャン 現在のソフトウェア開発は、多くのOSSを含む外部のソフトウェアに依存しています。Python、Go、npm など多くの言語は、様々なソフトウェアをパッケージとして利用できるエコシステムを提供しており、この仕組みを利用してOSSなどのコンポーネントをソフトウェアに組み込んで活用するのが一般的です。 外部パッケージを利用することで効率的な開発が可能になる一方で、それらに含まれる脆弱性の影響を受けてしまうリスクが増加しています。Snyk社のレポート 1 によれば、オープンソースのソフトウェアの脆弱性の約80%は間接的に依存しているパッケージによりもたらされるとされています。 そのため直接的に利用しているパッケージだけでなく、それらの依存先のパッケージまで含めて把握し、脆弱性がないか管理する必要があります。 ソフトウェアの依存関係を解析する手段として、SCA (Software Composition Analysis) と呼ばれるツールの活用が挙げられます。 SCAを活用してソフトウェアの依存関係から脆弱性を発見し、それらに対して適切に処置をする、ということが複雑化するソフトウェア開発における脆弱性対応では重要です。 SCA型のスキャンツールの紹介 脆弱性対策に使えるSCAは有償のものも含めて多くありますが、今回は無償で使える以下の3つを紹介します。 Dependabot Trivy Grype GitHub Dependabot 概要 GitHub Dependabot はRepositoryの構成要素を分析し、脆弱な依存関係を発見・通知するGitHubの機能です。 GitHub Dependabotの機能はPrivate Repoであっても無償で利用できるため、GitHubを利用しているならば万人にお勧めできます。 2 データソース GitHub Dependabotは、既知の脆弱性やマルウェアの情報が含まれるデータベースである  GItHub Advisory Database に含まれる脆弱性が通知されます。 NVD や各種言語のセキュリティアドバイザリの情報がデータソースとなっています。 GitHub Advisory Databaseの情報はGitHub独自の脆弱性IDで管理され、脆弱性の詳細や影響を受けるバージョン、修正済みのバージョンなどの情報が含まれています。 GitHub Advisory Databseの中でも、GitHubがサポートするエコシステムにマッピングされた脆弱性/マルウェアの情報を GitHub-reviewed Advisory と呼び、この情報がDependabot で通知されます。 GitHub-reviewed AdvisoryはGitHub社によるレビューやパッケージシステムとの対応づけが完了しており、影響を受けるバージョンや修復済みのバージョンの情報も含まれているため、対応策定に役立てることができます。 本ブログ執筆時点で対応しているエコシステムは以下の通りです。 Composer (registry: https://packagist.org/) Erlang (registry: https://hex.pm/) Go (registry: https://pkg.go.dev/) GitHub Actions ( https://github.com/marketplace?type=actions/ ) Maven (registry: https://repo.maven.apache.org/maven2) npm (registry: https://www.npmjs.com/) NuGet (registry: https://www.nuget.org/) pip (registry: https://pypi.org/) pub (registry: https://pub.dev/packages/registry) RubyGems (registry: https://rubygems.org/) Rust (registry: https://crates.io/) 使い方 Dependabot を利用するには、Repositoryページの Settings -> Code security and analysis にアクセスし、 Dependency graph および Dependabot 関連の機能をEnableにする必要があります。 GitHub Dependency Graph はRepositoryの中の言語依存ライブラリの管理ファイルなどを解析し、依存しているライブラリの一覧を抽出する機能です。 Dependency Graph で解析できる対象は About the dependency graph のドキュメントを参照してください。 Dependency Graphを有効にするとソースコードが解析され、依存パッケージが確認できる様になります。 解析結果はRepositoryページの Insight → Dependency Graph にアクセスすることで確認できます。 Dependabot alertsを有効化すると、GitHub Advisory Databaseに関連する情報が登録されると通知される様になります。 Dependabot alertsの情報は、Repositoryの Security -> Dependabot から確認できます。 Dependabot security updates を有効化していると、パッケージのバージョンを脆弱性修正バージョンに上げるPull Request が自動的に発行されます。 Trivy 概要 Trivy はGo製のツールで、Linux, Windows, Mac いずれの環境でも動作します。 Trivyはセキュリティのアーミーナイフをうたっており、以下のような多様な機能を有しています。 脆弱性スキャン OSパッケージ、言語のパッケージをスキャンし、脆弱性を発見する Secret スキャン 鍵ファイル (AWSキー、slackキーなど) が存在してないかを確認する Configスキャン Terraform 設定ファイルやDockerfileなどをスキャンし、セキュリティ上問題になりうる設定が含まれてないかチェックする いずれの機能もソフトウェアの安全性を高めるために有効ですが、今回は脆弱性スキャンについて取り上げます。 Trivyの脆弱性スキャンはファイルシステムなどを解析し、依存しているOSや言語のパッケージを抽出し、既存の脆弱性情報とマッチングすることで脆弱性の有無を判断します。 Trivyが脆弱性の有無を解析できる対象は以下の通りです。 OSパッケージ apt, yum などでインストールしたOSパッケージ情報を抽出し、脆弱性を表示する 言語パッケージ 言語の依存ライブラリの管理ファイル (pacakge-lock.json, pipenv.lock など) をスキャンして、パッケージのバージョンを取得し、脆弱性を表示する データソース Trivyの脆弱性スキャンは、Trivyが収集したOSや言語パッケージのバージョン情報を、Trivyの脆弱性DBとマッチングすることで行われます。 Trivyが利用する脆弱性DBは trivy-db というツールで作成されており、DB自体の更新もこのtrivy-db Repositoryの GitHub Actions で実行されています。 このDBは6時間ごとに更新されており、最新の脆弱性情報を参照できます。 Trivyがどのようなデータソースから情報を収集しているかは、Trivyドキュメントの Data Sources やtrivy-dbの ソースコード から読み解けます。前述したGitHub Advisory Database もデータソースに含まれています。 主なデータソースは以下の通りです。 GitHub Advisory Database Open Source Vulnerabilities GitLab Advisories Community NVD 各種OS/言語の脆弱性DB 使い方 Trivyの脆弱性スキャンの対象は以下の通りです。 コンテナイメージ ファイルシステム GIt Repo コンテナイメージのスキャン コンテナイメージをスキャンする場合は、 trivy image コマンド を利用します。 trivy image python:3.4-alpine 出力は以下の様になります。 イメージスキャンは コンテナイメージを指定して、その中に含まれるOSパッケージや言語ライブラリの脆弱性をスキャンできます。 ファイルシステムのスキャン ファイルシステムスキャンをする場合は、 trivy fs コマンド もしくは trivy rootfs コマンド を利用します。 trivy fs /path/to/project trivy rootfs / ファイルシステムをスキャンして、OSパッケージのファイルや言語のパッケージの設定ファイル (npm の package-lock.json など) を解析してバージョン情報などを抽出し、既知の脆弱性が発見された場合に通知します。 fsコマンドは主にローカルにあるプロジェクトのスキャン、rootfsコマンドはRootfsのスキャン (コンテナ内部でスキャンや、コンテナイメージのファイルのスキャンなど) での利用を想定されています。 参考 例えばPythonでは、fsコマンドだと Pipfile.lock などのパッケージの管理ファイルからパッケージとバージョンを抽出するのに対し、rootfsコマンドでは site-packages/ ディレクトリなどをスキャンして実際にインストールされているパッケージとバージョンを抽出する、という挙動の違いがあります。開発中のプロジェクトのソースコードをスキャンしたい場合はfsコマンド、コンテナやホストマシン内部など今動いている環境のスキャンはrootfs、の様に使い分けるのが良いでしょう。 fsコマンドとrootfsコマンドのスキャン対象の違いについては Trivyのドキュメント なども参照してください。 スキャンの挙動設定 Configファイル (trivy.yaml)を利用することで、スキャンの挙動を設定できます。設定可能な内容は  ドキュメント を参照してください。 また .trivyignore に脆弱性ID (CVE-ID) を記載することで、その脆弱性を無視できます。 スキャンフォーマットの指定 Trivyはスキャン結果の出力フォーマットが指定可能です。デフォルトのtableフォーマットはスキャン対象・脆弱性・Severityなどが表示され、視認性が高いフォーマットです。 それ以外にもjsonフォーマットなどを指定できます。 jsonフォーマットで出力すると、tableフォーマットでは出力されないスキャンターゲットのファイルパス ( PkgPath )などの情報が表示されます。 rootfs スキャンでマシン全体をスキャンした際など、どこで脆弱性が検知されたかわからない、という場合に有効です。 json オプションを使用する場合は --output オプションと組み合わせてファイルに出力するのが良いでしょう。 CIへの組み込み trivy-action を利用することで、GitHub Actions上でのTrivy実行が可能です。 GitHub Actionsでの設定方法は以下のようになります。以下の例では severity などの情報をworkflow上で記載していますが、 Trivy Configファイルに必要な設定を記載してRepoに配置しておき、そのファイルを  trivy-config で参照することもできます。 (ただし trivy-config を指定すると、 scan-type や scan-ref 以外のオプションは無視されるため注意してください。) name : build on : push : branches : - master pull_request : jobs : build : name : Build runs-on : ubuntu-20.04 steps : - name : Checkout code uses : actions/checkout@v2 - name : Build an image from Dockerfile run : | docker build -t docker.io/my-organization/my-app:${{ github.sha }} . - name : Run Trivy vulnerability scanner uses : aquasecurity/trivy-action@master with : image-ref : 'docker.io/my-organization/my-app:${{ github.sha }}' format : 'table' exit-code : '1' ignore-unfixed : true vuln-type : 'os,library' severity : 'CRITICAL,HIGH' SBOMの出力、SBOMファイルのスキャン Trivyはスキャン結果からSBOMを出力したり、SBOMファイルから脆弱性スキャンを行うこともできます。 SBOM活用の詳細については 12/1のアドベントカレンダー もぜひ参照してください。 Trivyで SBOMファイルを作成 する場合は、 --format で出力したいSBOMフォーマット ( spdx-json or cyclonedx ) を指定します。 trivy image --format spdx-json --output result.json alpine:3.15 TrivyでSBOMファイルをスキャンする場合は、 trivy sbom コマンド でSBOMファイルを指定します。 trivy sbom /path/to/spdx.json Grype 概要 Grype はTrivyより後発のセキュリティスキャンツールです。SBOMツールである Syft と連携して動作し、スキャン結果のSBOMファイルへの出力や、SBOMファイルを利用した脆弱性スキャンが可能です。 データソース Grypeのデータソースは Github で参照できます。 基本的にはTrivyと類似したデータソースになっています。 GitHub Advisory Database Open Source Vulnerabilities GitLab Advisories Community NVD 各種OSの脆弱性データベース Grypeのスキャン Gypeは以下のスキャンに対応しています。 コンテナイメージ OSパッケージ 言語のパッケージ SBOMファイルのスキャン CycloneDX, SBOM, syft形式のスキャンが可能 Grypeのスキャンは以下の様に行います。 grype python:3.4-alpine 出力は以下の様になります。 $ grype python:3.4-alpine ✔ Vulnerability DB [updated] ✔ Parsed image ✔ Cataloged packages [31 packages] ✔ Scanned image [106 vulnerabilities] NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY busybox 1.29.3-r10 apk CVE-2021-42379 High busybox 1.29.3-r10 apk CVE-2021-42376 Medium busybox 1.29.3-r10 apk CVE-2021-42385 High busybox 1.29.3-r10 apk CVE-2021-42378 High busybox 1.29.3-r10 apk CVE-2021-42381 High busybox 1.29.3-r10 apk CVE-2021-42380 High SBOMファイルを元にスキャンする場合は以下の様になります。Syftによって生成されたSBOMファイルなら正確な検知が可能になっています。 grype sbom:./spdx.json 脆弱性スキャンツールの使い分け ここまでGitHub Dependabot、Trivy、Grypeについて紹介しました。 これらのツールを活用することで、脆弱性の発見と対処が容易になります。 どの様に脆弱性スキャンツールを使い分けていくかはプロジェクトの状況などによりますが、個人としては以下をお勧めします。 GitHubを利用しているなら、Dependabot を利用してソースコードの脆弱性を検知・対応する コンテナイメージなどのソースコードのみではスキャンできない対象をCI/CDのプロセス内で検知したい場合、 trivy-action などを活用することでTrivyをCI/CD に組み込む デプロイ先の環境 (VM、コンテナなど) のスキャンを実施したい場合は、Trivyでrootfs や imageスキャンを実施する SBOMを用いた構成管理および脆弱性スキャンを実施したいなら、Syft + Grype の組み合わせで運用する 開発にGitHub を利用しているなら、まずはDependabotを有効化してしまって良いと思います。 GitHub Advisory Databseは優秀な脆弱性DBであり、その内容を通知してくれるDependabot を有効化することで迅速な対応が可能になります。 GitHub Dependabotではカバーできないコンテナイメージのスキャンなどを実施したいなら、TrivyをCI/CDに組み込むのがお勧めです。 Trivyはスキャンが高速なため、開発への影響を最小限に抑えつつセキュリティレベルを向上させることが期待できます。 GrypeはSyft との連携が念頭に置かれています。 Syftは強力なSBOMジェネレータのため、SBOMの生成にSyftを使っているのならGrypeと合わせて運用するのが良いと思われます。 CI/CDで実現する継続的な脆弱性スキャン Metemcyber PJではDependabotとTrivyを開発に組み込んでいます。 今回はTrivyを使ったCICDを例として取り上げ、どのようにCI/CD で継続した脆弱性スキャンを実現するかを紹介したいと思います。 trivy-action の活用 私たちは trivy-action を利用して、GitHub Actionを利用した脆弱性のスキャンを実施しています。 以下は trivy-action を利用して、Pull Request 時と mainへのpush時にfsスキャンを実施し、Trivyスキャンで脆弱性が発見されたらGitHub Actionsをfailさせる例 (一部抜粋) です。 name : Pipenv CI on : pull_request : branches : - main push : branches : - main workflow_dispatch : jobs : build : steps : - name : Check out code from GitHub uses : actions/checkout@v3 - name : Run Trivy vulnerability scanner in fs mode uses : aquasecurity/trivy-action@master with : scan-type : 'fs' scan-ref : './api' trivy-config : trivy.yaml Actionsで参照している trivy.yaml は以下の様になります。 debug : true exit-code : 1 severity : - HIGH - CRITICAL 上記のようなGitHub Actionsと trivy.yaml を用意しておくことで、Pull Requestを行うと自動的にTrivyスキャンが実施されます。 trivy-action はデフォルトで脆弱性スキャンとSecretスキャンを行うため、ソースコード内にトークンなどの機密情報が含まれていないかのチェックも可能になっています。 trivy.yaml の severity を用いると、スキャンの対象とする脆弱性の脅威レベルを設定できます。 上記の例ではTrivy基準でHigh以上の脆弱性を検知すると、 exit-code: 1 の設定によりActionがfailし、脆弱性を発見できる仕組みになっています。 Pipenvの採用 上記の仕組みを実現するために、Pythonのパッケージマネージャーとして pipenv を採用しています。 Pythonで環境を構築するには、 requirements.txt にインストールしたいパッケージを記述しておき、pipでインストール方法もあります。 pip install -r requirements.txt しかし私たちは以下の理由でpipenvを採用しています。 依存するパッケージを Pipfile.lock で明確化してTrivyでスキャンできる 開発環境でしか利用しないパッケージを分離し、Trivyのスキャンの対象外にできる Trivyのfsスキャンでは、 requirements.txt に記述されていない、間接的に依存するパッケージはfsスキャンで検知できません。 そのため requirements.txt に直接参照するパッケージのみを記述している場合、実際にインストールされているパッケージの脆弱性を見逃すことがあります 3 。 pipenvでは実際にインストールされるパッケージが Pipfile.lock に記述され、Trivyのfsスキャンで検知可能となります。 またpipenvでは、 --dev オプションを利用することで、開発環境のみで使うパッケージを別枠でインストールできます。 pipenv install --dev autopep8 Trivyではdevオプションでインストールしたパッケージは スキャン対象外 となります。これにより、プロダクション環境に存在する脆弱性のみを検知することが可能になります。 12/1のアドベントカレンダー でも言及がありましたが、脆弱性対応を適切に行うにはパッケージマネージャーの選定も重要になります。 pipenv を利用することで、実際にインストールされるパッケージ全てのスキャンや、devDependencies のスキャン対象からの除外を実現できるので、Trivy を活用する場合は採用を検討することをお勧めします。 まとめ 本記事では、DependabotやTrivy、Grypeといったスキャン系のツールを紹介しました。 ツールごとに強みがあるので、開発体制などに応じて使い分けていくことでセキュリティの向上が期待できます。 ただし、これらのツールを使えば全ての問題が解決するかというとその様なことはなく、スキャン結果をどのように運用に組み込むかは別途考えなくてはなりません。 脆弱性が発見された際に即時アップデートをするか、それとも通常のリリースサイクルで対応するのか。 修正バージョン自体存在しない脆弱性が発見された場合どうするかなど、実運用ではさまざまな課題に直面します。 これらは開発しているソフトウェアの性質や、ソフトウェアが取り扱う情報資産の重要度などに合わせて適切に決定していく必要があります。 今回紹介した脆弱性スキャンツールは、CICDを活用した開発プロセスへ導入し、早期に脆弱性を発見することで効果を発揮します。 そのためセキュリティの視点だけでなく、利用するパッケージマネージャーや開発・デプロイのプロセスといった視点も含めて改善していくことで、効果的な脆弱性対応のプロセスを実現できるでしょう。 宣伝 私たちはMetemcyber という、セキュリティインテリジェンスやTrivyなどのツールの結果を管理し、セキュリティアクションを実施していくツールを現在開発中です。 今後 MetemcyberのTwitterアカウント にて情報発信していく予定です。このアカウントでは  セキュリティインテリジェンスの発信 などの活動も行っているので、ぜひフォローしてください。 それでは、明日もお楽しみに。 https://go.snyk.io/rs/677-THP-415/images/State%20Of%20Open%20Source%20Security%20Report%202020.pdf ↩ https://github.co.jp/pricing.html ↩ pip freeze を利用してインストールされているパッケージを全て出力することでfsスキャンによる検知が可能になりますが、直接依存するパッケージと間接的に依存するパッケージが混在してしまいます。 ↩
この記事は、 NTT Communications Advent Calendar 2022 6日目の記事です。 はじめに こんにちは、SDPF クラウド・仮想サーバチームの松下です。 普段は OpenStack の開発・運用をしているエンジニアで、今年から新入社員としてJOINしました。 今回は、細々と取り組んでいるOSを自作する個人的な活動についてお話ししつつ、ちょっと普通とは違う開発にチャレンジする同志を増やしたいなと思い執筆しております。 人の子であれば、一度は何か古くからある難しそうなソフトウェアの自作に取り組みたくなるものです(主語デカ発言) 私も例に漏れずその一人で、現在RustでOSを自作しようとしているところです。 自作するOSは、「ゼロからのOS自作入門」 1 (通称「MikanOS本」)のお題であるMikanOSです。 このMikanOS本は、言語としてC++を利用しOSを自作していく本で、サポートページやGitHubが今もなお更新されるほど 注目されている名著・プロジェクトとなっています。 私の活動の特徴 MikanOSをRustで書くこと自体はやはり時代の潮流もあり、すでに行っている方々がいらっしゃいます。 その方々の実装を見ますと、基本的には"uefi-rs" 2 と呼ばれるCrateを用いて実装しているようです。 uefi-rsは、RustでUEFIアプリケーションを作成するためのCrateであり、MikanOSはUEFIを前提としているためこちらを用いて開発することが王道の戦略だと思います。 一方で私のRikan 3 は、このuefi-rsを使わずに開発を進めています。 uefi-rsを使わずに実装していくことによって、MikanOS本で開発するもの全てを理解できるのでは?と考えた末の戦略です。 この戦略の開発はMikanOS本ではあまり触れられないところを理解する必要があったり、 OS起動以前に大量の実装をする必要があるなど純粋な「自作OS」の範疇を少し超えるようなことをする必要があります。 しかし、それによってあまり知られていなさそうなことを知れたり「UEFI Specificationが愛読書」みたいなことが言えたりしちゃうのでとても楽しいです。 この記事では、皆さんにそれを始める最初のステップとして、UEFIで"Hello World"をするコードを解説しようと思います。 RustでUEFIアプリケーションを作る 今回利用するコードは私のRikanプロジェクトの最初のコミット 4 のものになります。 このプログラムは2つのファイルがメインとなりますので、その2つについて解説しようと思います。 まずは、全体の動きを説明するため、main.rsの中身を以下に載せます。 #![no_std] #![no_main] #![feature(abi_efiapi)] use core :: panic :: PanicInfo; use utf16_literal :: utf16; mod uefi ; #[no_mangle] pub extern "C" fn efi_main (ImageHandle: uefi :: EFI_HANDLE, SystemTable: & uefi :: SystemTable) -> uefi :: EFI_STATUS { let _conout = SystemTable. ConOut (); _conout. Reset ( false ); _conout. OutputString ( utf16! ( "Hello World \r\n " ). as_ptr ()); loop {} uefi :: EFI_STATUS :: Success } #[panic_handler] fn panic (_panic: & PanicInfo < '_ > ) -> ! { loop {} } Hello Worldするだけですので複雑なコードではありませんが、普通のRustプログラムではあまり見かけないものが書かれていると思います。 UEFIの環境ではOS起動以前の環境ですので標準ライブラリにあたるものは利用できません。 従って、1行目にあるように #![no_std] とすることでRustの標準ライブラリを用いない core と呼ばれる最低限のライブラリを使う環境で動かすことになります。 これによって、普段あまり気にすることのないpanic時にどうするかも自分で実装する必要があります。 これが最後の方の #[panic_handler] 以降の部分にあたり、今回はループし続けるだけの実装にしています。 メインの処理ですが、これは efi_main 関数の中身となります。 UEFIの仕様書ではUEFIが規定するデータ型とC言語の呼び出し規約 5 を利用するものとして定義されていますので、RustのABIではなくUEFI仕様書に沿ったものを利用するよう指定していく必要があります。 これは後述する構造体や列挙体にも当てはまります。 UEFI アプリケーションのエントリーポイントは、 extern "C" とすることでUEFI仕様に則った形とし、 マングリングされないように #[no_mangle] も追加します。 UEFIアプリケーションは、エントリーポイントで2つの値を受け取ることになります。 このうち、SystemTableの方が重要で、UEFIの各種機能を呼び出すためのアドレス群を格納する構造体へのポインタとなっています。 画面にテキストを出力する「Simple Text Output Protocol」と呼ばれる機能や、OS起動以前に利用できる機能が詰まっているBoot Serviceを利用する場合には、 この構造体を経由してそれらを呼び出します。 Hello Worldをする際には、前者の機能を利用するため efi_main 関数の最初でこの機能の呼び出しの準備をしています。 SystemTableの説明をするために、次にuefi.rsの抜粋を以下に示します。 (構造体のメンバも削っているので、実際のコードとはかなり違います。) #[repr(C)] pub enum EFI_STATUS { Success = 0 } type CHAR16 = u16 ; pub struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { Reset: extern "efiapi" fn (This: & EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, ExtendedVerification: bool ) -> EFI_STATUS, OutputString: extern "efiapi" fn (This: & EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, String : *const CHAR16) -> EFI_STATUS, } impl EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { pub fn Reset ( & self , ExtendedVerification: bool ) -> EFI_STATUS { unsafe {( self .Reset)( self , ExtendedVerification)} } pub fn OutputString ( & self , String : *const CHAR16) -> EFI_STATUS { unsafe {( self .OutputString)( self , String )} } } #[repr(C)] pub struct SystemTable { ConOut: *mut EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, BootServices: *mut EFI_BOOT_SERVICES, } impl SystemTable { pub fn ConOut ( & self ) -> &mut EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { unsafe { &mut * self .ConOut} } } 先ほども述べたようにSystemTableという構造体は、各種機能を呼び出すためのアドレスがメンバとなっています。 下方にあるSystemTableの構造体でそれらを定義しています。 エントリーポイントの時にも述べましたが、UEFIを利用するためにはC言語のABIに沿う必要があるため、 構造体の上に #[repr(C)] をつけることによってRustコンパイラにそれを示しています。 6 C言語では、アドレスさえわかればそれをもとに関数ポインタを作って呼び出せば良いのですが、 Rustぽく実装するためにアドレスを格納している構造体に対して impl してラッパーを作り呼び出しています。 UEFIの機能を呼び出すと返り値に EFI_STATUS が返却されますが、RustとしてはやはりResult型で表現したいためこのようにしています。 (この例ではそこまでやっていませんが...) 文字列を表示するためには、EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL構造体のメンバ変数であるOutputStringに格納されている関数にUTF16文字列のポインタを渡してあげれば良いため、 main.rsでは以下のようにすることでHello Worldを画面に表示できます。 _conout. OutputString ( utf16! ( "Hello World \r\n " ). as_ptr ()); uefi-rsを使わないMikanOSの実装の進め方 基本的には、Hello Worldで示したようなコードをひたすら書いていくだけになります。 つまり、以下のサイクルを回していく作業になります。 MikanOSの実装を読む 実装に必要な構造体や列挙体、関数に必要な引数などをUEFI Specificationで探す Rustでそれらを書く Rustぽく動かせるようにする デバッグする もし実装につまれば、uefi-rsを読んだりコードを入れ替えたりしたりすることで何かヒントは得られるのかなと思います。 Rikanを開発する上で感じたこと Rikanを開発する上で思ったことは以下の5点です。 構造体をひたすら定義し続ける苦行が辛い Rustぽく動くようにimplし続ける苦行が辛い グローバルアロケータを実装するまでまともなprintfデバッグすらできなくて辛い PanicInfoを利用するとpanicが起きたときにどこの行が原因で起きているのかが出るのでありがたい uefi-rsは偉大 まだday3までしか進んでいませんが、一番の山場はグローバルアロケータを実装するところかなと思います。 これを実装するまでは、変数の中身を表示することが難しいのでデバッグの難易度がかなり高い状態での開発になります。 また、UEFIをちょっと使うためにも多くのUEFIで定義される構造体を実装する必要があるため、uefi-rsは縁の下の力持ちだととても感じています。 まとめ この記事は、MikanOSをRustで実装することで、UEFIという普段意識しないものに対する理解を深める最初の1歩になればと思い執筆しました。 ソフトウェア開発において、開発対象とする領域を下支えする裏側を意識する機会は少ないかも知れません。 ブラックボックスにすることによって集中すべきところにしっかり集中するというのが大事だからです。 しかし、一度トラブルなどが起きたときは、その裏側を知っておくとその解決が速くなったりすることがあります。 そういった意味で、エンジニアとしては裏側を知っておくということは重要になると私は考えています。 NTT Comでは現在、現場受け入れ型インターンシップを募集しており、以下のリンクから応募できます。 いろいろなサービスで利用されているクラウド技術の裏側を知ることのできるインターンシップになっていますので、 この記事を読んで「クラウドサービスの裏側を知りたい!」となったあなたからの応募をお待ちしております。 information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 http://zero.osdev.jp ↩ https://github.com/rust-osdev/uefi-rs ↩ https://github.com/bean1310/Rikan ↩ https://github.com/bean1310/Rikan/tree/2e5f7a4456531e53a41508a1d498f3beea53ee1a ↩ 仕様書中に記述は見つかりませんでしたが、Microsoftのx64 calling conventionを採用しているように見えます。 https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention ↩ ここのサイトがこの違いを解説しています。 https://ryochack.hatenablog.com/entry/2018/03/23/184943 ↩
この記事は、 NTT Communications Advent Calendar 2022 4 日目の記事です。 こんにちは。 SDPF クラウド・仮想サーバーチームの杉浦です。 普段は OpenStack の開発・運用をしています。 みなさんはシェル芸と聞いてどのようなコマンドを想像しますか? 私は以下のような怖いコマンド 1 を想像していました # 無限に process を fork するコマンドです # 実行するときは自己責任でお願いします :(){ :|:& };: ですがシェル芸はもっと親しみやすくて 2 実用的なものです。 私はシェル芸のシェの字もできないくらいシェル芸初心者だったのですが、 1日1問、半年以内に習得 シェル・ワンライナー160本ノック という本を完走してシェル芸チョットワカルようになったので、本の宣伝をしつつ完走した感想を紹介しようと思います。 1日1問、半年以内に習得 シェル・ワンライナー160本ノック https://gihyo.jp/book/2021/978-4-297-12267-6 本を読む前は 本を読む前はシェル芸こそできなかったものの、 CLI 環境で十分生活できるくらいには bash とお友達でした。 cat や less 、 grep などの基本的なコマンドも使えましたし、 vim も難なく使いこなせていました。 ただ、例えば awk はほとんど使ったことがありませんでした。 xargs に関しては Stack Overflow ではたまにみるけどどういう動作をしているのかきちんと理解していませんでした。 テキストファイルの中身を解析するときは VSCode やスプレッドシートに貼り付けてなんとかしていました。 シェル芸をやってみようと思ったきっかけ ログファイルの解析やサーバーの管理の際にちょっとしたテキスト操作ができなくてもどかしい思いをしたからです。 仮想サーバーチームでは数千台規模の仮想サーバーを運用しています。 多数のサーバーを効率的に運用するために、開発用端末から SSH 経由でコマンドを叩いてサーバーの管理をするわけです。 あるとき、サーバーに搭載されているメモリ量を調べたいことがありました。 Linux では free コマンドを使うとメモリの使用状況を調べることができます。 $ free total used free shared buff/cache available Mem: 12235124 290432 4185192 5088 7759500 11720024 Swap: 0 0 0 調査対象が 1 台であれば Mem 行の total 列の数字をターミナルからコピーすればよいですが、複数のサーバーを横断的に調べたいときは Mem 行の total 列の数字を取り出す操作を自動化したくなります。 シェル芸がチョットワカルようになった今だったらいくつもの方法が考えられますが、当時は awk コマンドに馴染みがなかったのでコマンドがすっと出てこずに苦労しました。 どうやって解いたかはあまり覚えていませんが、 grep を使ったりスプレッドシートを使ったりしてなんとかしのいだ覚えがあります。 シェル芸本の読み方 シェル芸本では、シェル芸とは「Unix 系 OS のシェル上でワンライナーのコマンドを駆使すること」と定義されています。 冒頭で示したようなワンライナーだけではなく、日常の業務をスッと終わらせるために使うコマンドもシェル芸とみなせます。 この本では実践形式で手を動かしながらシェル芸を学びます。 コマンドの使い方を説明したあとに問題が出され、解説があります。 最初は問題があまり解けずつらい気持ちになるかもしれませんが、シェル芸の型が身につくとみるみる解けるように解けるようになって楽しいですよ。 最初は echo や ls コマンド、 Control + C の使い方から始まり、シグナルやファイルシステム、システムコールといった発展的なトピックも出てきます。 また、問題は初級・中級・上級に分かれていて自分のレベルに合わせて取り組むことができます。 Unix 初心者の方から上級者の方まで楽しむことができますね。 シェル芸本は 3 部構成になっています。 第 1 部: シェルとコマンドに親しむ 第 2 部: 発想力を鍛える 第 3 部: 応用する シェル芸初心者の方は最初から解いていくのがよいでしょう。 ただし、すべて解こうとすると時間がかかるので、必要なところをかいつまんで読んでもいいかもしれません。 例えば 文字コードとバイナリ で学ぶ内容は他の章ではあまり出てこないので飛ばしてしまってもいいでしょう。 第 3 部は第 1 部、第 2 部で身につけた知識を実際のサーバーの解析で応用できるかどうかを問う問題が出ます。 シェル芸に自身がある方は第 3 部から挑戦して、知識に不足があれば必要に応じで第 1 部、 2 部を参照する、といったやり方でもよさそうです。 シェル芸の構成要素 シェル芸は以下の要素が絡み合って構成されていると感じました。 基本コマンドの使い方 Bash の便利機能 オプション 便利なコマンド 正規表現の使い方 コマンドの組み合わせ方のパターン アート要素 基本コマンドの使い方 以下のコマンドはシェル芸でよく使います。 マニュアルやチートシートを見なくても使えるようにしておきましょう。 awk sed grep xargs wc sort uniq find cat date 基本的なコマンドの使い方は 第 1 章で解説があります。 Bash の便利機能 man bash すると bash の機能を確認できます。 例えば、以下の機能は知っておくと便利です。 Process Substitution <(command) >(command) Brace Expansion {n..m} {a,b,c} History Expansion fc !n ^CommandA^CommandB^ Parameter Expansion ${parameter:-word} ${parameter:offset:length} ${parameeter#word} ${parameter/pattern/string} bash の機能は第 2 章で詳しく学びます。 便利なコマンド awk を使えば大抵のことはできますが、便利なコマンドを知っているとワンライナーをシンプルにできます。 複雑な処理をしたいときは perl や python を使ったほうがいいかもしれません。 シェル芸の本を読んで一度は使ってみるとよいでしょう。 paste join dateutils zgrep xzgrep jq gron bc printf perl ruby teip 3 オプション オプションを使うとコマンドの動作や出力を制御できます。適切なオプションを知っているとコマンドの出力を加工する手間を省くことができます。 例えばシェル芸本では grep のオプションとして以下のものを使います。知らないオプションはありませんか? A a B C E f H m n o q v x z 正規表現 正規表現にはそれだけで本が書けてしまうくらい 4 機能が豊富にあります。 シェル芸本でも正規表現を多用します。 grep では -P オプション を使うと Perl の強力な正規表現 Perl-compatible regular expressions (PCREs) を使えます。また ruby にも強力な正規表現エンジン oniguruma 5 が内蔵されています。 grep -P や ruby では以下のような強力な機能が使えます。 メタ文字 \d : 数字にマッチする \p{Han} : 漢字にマッチする 後方参照 \1 、 \2 .... 先読み・後読み・否定先読み・否定後読み (?=pattern) (?<=pattern) (?!pattern) (?<!pattern) 部分式呼び出し \g<...> 正規表現は第 3 章で詳しく取り扱います。 コマンドの組み合わせ方のパターン Unix では単純なコマンドを組み合わせることで複雑な仕事を片付けることができます。 シェル芸では個々のコマンドやオプションの動作を覚えるのも大事ですが、コマンドの組み合わせ方も学ぶ必要があります。 例えば sort してから uniq するのは頻出パターンです。 wordlist.txt からユニークな単語を調べたいときは $ cat wordlist.txt | sort | uniq となりますね。 grep -o してから uniq するのもよく使います。 例えば story.txt からある単語の出現回数を知りたいときは $ cat wordlist.txt | grep -o the | uniq -c となります。 プロセス置換も使えると便利です。たとえば head と tail の出力を組み合わせたいときは以下のようになります。 $ cat <(head story.txt) <(tail story.txt) コマンドの組み合わせ方はシェル芸本全体を通して学びます。 アート要素 シェル芸本の中にはシェル芸っぽい解答もあります。 問題 29 には sort | uniq の代替として awk '!a[$1]' が紹介されています。 a[$1] で $1 の出現回数を数えるのですが初回は a[$1] が 0 なので、 ! を使うと初回のみ条件が成立して print されるというわけです。賢いですね。 他にも vim をコマンドとして使う 珍 解答もあったりします。例えば問題 31 を参照してください。 取り組んでみてどうなったか bash での生活がかなり豊かになりました。 具体的には、ワンライナーがすぐに作れるようになりました。 今まではシェルで込み入った処理をする場合、インターネットでよさそうなワンライナーを探したり、コマンドやオプションの意味を調べたりする必要があって、本質的でないことに時間がかかっていたように思います。 今では必要があれば man を参照するくらいで、かなりストレスフリーにワンライナーを書くことができます。 複数の解法を思いつくこともしばしばあります。 例えば、 free コマンドの出力のうち Mem 行の total 列の数字を取り出すワンライナーは次が考えられます。 $ free | grep Mem: | awk '{print $2}' $ free | xargs | awk '$0=$8' $ a=($(free | sed -n 2p)); echo ${a[1]} $ cat /proc/meminfo | grep -oP 'MemTotal:\s+\K\d+' また、複雑な操作をワンライナーとして表現できるようになったのも嬉しいポイントです。 ssh コマンドでは ssh hostname cat /etc/passwd とすることで SSH 越しにコマンドを叩くことができます。 ワンライナーを書くことができれば多数のホストに対してコマンドを実行できるようになるので嬉しいわけです。 シェル芸が役立った実例 お仕事でシェル芸が役立った実例を紹介します。 仮想サーバーチームの業務として、仮想化ソフトウェア qemu/KVM の管理業務があります。 以前 VM の動作が不調だという問い合わせを受けたときに VM の動作をホスト側から解析したことがありました。 qemu には trace-events 6 という VM がホスト側に発行するイベントをトレースする仕組みがあります。 trace-events を有効にすると、次のようなログを得られます。 20480@1663636838.696500:virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8a39c820 sector 12437488 nsectors 16 20480@1663636838.696531:blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 6367993856 bytes 8192 flags 0x0 20480@1663636838.699222:virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8a39c820 ret 0 20480@1663636838.699231:virtio_blk_req_complete vdev この中から特定の時間帯のログを取り出すにはどのようにすればよいでしょうか? 時刻の情報を取り出す まずは各行から時刻の情報を取り出したいです。 ログをよく見ると 20480@1663636838.696500 という出力が見つかります。 問題 68 で学ぶように、 @数値 という形式は Unix 時刻を表すときに使います。 次のようにすると Unix 時刻を読み込んで所定のフォーマットで出力できます。 $ date -d '@2147483647' Tue Jan 19 03:14:07 UTC 2038 よって 20480@1663636838.696500 の @ より後ろの部分は時刻を表していると推測できます。 @ より前はおそらく process id です。 awk でログをフィルタリングする まずは awk で処理しやすいように @ と : をスペースに変換します。 $ cat log.txt | tr '@:' ' ' 20480 1663636838.696500 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8a39c820 sector 12437488 nsectors 16 20480 1663636838.696531 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 6367993856 bytes 8192 flags 0x0 20480 1663636838.699222 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8a39c820 ret 0 次に 2 列目をよく見て特定の時間内のログを取り出せばよいでしょう。 date コマンドは日時の出力フォーマットを変更できます。 Unix 時刻の形式で出力したいときは +%s を指定します。 これを使って、開始時刻を $ date -d '2022/09/20 01:20:40' +%s 1663636840 終了時刻を $ date -d '2022/09/20 01:21:00' +%s 1663636860 としてみましょう。 awk コマンドは -v オプションを使うと awk プログラムの中で使える変数を定義できます。 時刻は $2 で参照できるので、次のようにすれば start から end までのログを取り出せますね。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end' 20480 1663636852.706415 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 1663636852.706452 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 1663636852.707988 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 1663636852.708002 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 後で Unix 時刻の前に @ がついていると都合がいいのでつけておきましょう。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end{$2="@"$2; print}' 20480 @1663636852.706415 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 @1663636852.706452 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 @1663636852.707988 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 @1663636852.708002 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 時刻のフォーマットを変更する 今までのワンライナーでログのフィルタリング自体はできましたが、時刻の表記が見づらいです。 $2 に対して date を適用して見やすくしたいですよね。 このようなときは teip コマンドが便利です。 $ teip -f 2 -- command とすると $2 に対してコマンド command が適用されます。 date は -f オプションを使うとファイルから時刻表現を読み取って解釈してくれます。標準入力から読み込むには - というファイル名を指定します。 ここでは $2 に対して date -f- '+%F %T.%N' を適用してみましょう。 $ cat log.txt | tr '@:' ' ' | awk -v start=$(date -d '2022/09/20 01:20:40' +%s) -v end=$(date -d '2022/09/20 01:21:00' +%s) 'start < $2 && $2 < end{$2="@"$2; print}' | teip -f 2 -- date -f- '+%F %T.%N' 20480 2022-09-20 01:20:52.706415000 virtio_blk_handle_write vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 sector 8951968 nsectors 16 20480 2022-09-20 01:20:52.706452000 blk_co_pwritev blk 0x55dc897db7f0 bs 0x55dc897dba50 offset 4583407616 bytes 8192 flags 0x0 20480 2022-09-20 01:20:52.707988000 virtio_blk_rw_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 ret 0 20480 2022-09-20 01:20:52.708002000 virtio_blk_req_complete vdev 0x55dc8a720ff0 req 0x55dc8b19b6f0 status 0 先頭に process id がついているのが気になりますが、これで OK とします。 最後に 達人プログラマー 7 の第 3 章では道具に習熟する大切さを説いています。 道具はあなたの能力を増幅します。道具のできが優れており、簡単に使いこなせるようになっていれば、より生産的になれるのです シェル芸本はかなりボリュームがあってそれなりに時間がかかりますが、 bash で生活している人は読んでおいて損はないと思います。みなさんもぜひシェル芸を身に着けて bash ともっと仲良くなりましょう! 仮想サーバーチームでは冬期インターンシップのポストを募集しています。 大規模なクラウドの裏側を知りたい、業務でシェル芸を使ってみたいと思ったらぜひご検討ください。 たくさんのご応募お待ちしています! engineers.ntt.com information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 https://explainshell.com/explain?cmd=%3A%28%29%7B%20%3A%7C%3A%26%20%7D%3B%3A ↩ まだシェル芸に親しみをもてていなかったら親しみを持てるようにしましょう。 ↩ https://github.com/greymd/teip ↩ https://www.oreilly.co.jp/books/9784873113593/ ↩ https://github.com/kkos/oniguruma ↩ https://qemu-project.gitlab.io/qemu/devel/tracing.html ↩ https://www.ohmsha.co.jp/book/9784274226298/ ↩
この記事は、 NTT Communications Advent Calendar 2022 1日目の記事です。 はじめに こんにちは。イノベーションセンターテクノロジー部門の西野と申します。 「Metemcyber」プロジェクトで、脅威インテリジェンスの運用や活用に関する研究開発をしています。 今回の記事では、SBOMを利用した脆弱性管理の取り組みについてご紹介します。 実は NTT Communications Advent Calendar に6年連続で寄稿しているので、そろそろ名前を覚えてあげてください。 SBOMとは? SBOMは「ソフトウェア部品表(Software Bill Of Materials)」と呼ばれるもので、一般的には特定のソフトウェアに含まれるコンポーネントの依存関係を記述するために利用されます。記述フォーマットとしては SPDX や CycloneDX が有名です。 SBOMに関するNTIAのレポート 1 では、SBOMのデータフィールドに含める具体的な情報として以下のような項目(ベースライン情報)を挙げています。 サプライヤー名 コンポーネント名 コンポーネントバージョン など NTIAのレポートは2021年にリリースされましたが、それぞれの仕様を載せているレポジトリ 2   3 を調べると、SBOMに使われている記述フォーマットの歴史はさらに古いことが確認できます。 # SPDX の firtst commit $ git log --reverse commit 01597d36837bdcdf70f0501f50284eb7a94a6341 ( tag: v1. 0 ) Author: Thomas Steenbergen < opensource@steenbe.nl > Date: Wed Aug 17 09:00:00 2011 + 0100 # CycloneDX の firtst commit $ git log --reverse commit 62da520711b3bfb6bb51b4736066e5127922c8e2 Author: Steve Springett < steve@springett.us > Date: Sun May 28 21:22:06 2017 -0500 SPDXは2010年に発案された規格で、元々はソフトウェア部品のライセンス管理を目的 4 として設計されました。 CycloneDXは2017年に発案された規格で、 OWASP Dependency-Track と呼ばれるコンポーネント分析プラットフォームのために設計 5 されました。主なユースケースは、脆弱性の識別、ライセンス管理、古いコンポーネントの分析です。 なぜ今SBOMなのか? SolarWinds製品への攻撃 6 、Codecovのセキュリティインシデント 7 を皮切りに、2021年頃からサプライチェーンを狙ったサイバー攻撃の危険性が本格的に認識されるようになりました。 Typosquattingはもちろん、 Dependency Confusion と呼ばれる新たなサプライチェーン侵害のテクニックも報告されています。 ソフトウェアサプライチェーンのセキュリティに注目が集まる中、SBOMの重要性は上がり続けており、2022年9月には米政府機関のソフトウェア調達 8 にSBOMの内容が盛り込まれる事態となりました。 さぁ皆さん、SBOMを使ってソフトウェアサプライチェーンのセキュリティを万全にしましょう。 …とは、ならない話が今回のメインテーマになります。 なぜ今までSBOMが話題にならなかったのか QualcommのセキュリティエンジニアリングVPであるAlex Gantman氏が、SBOMの課題 9 を端的に表現しています。 「最良の(アイディアであるが、誰も実行していない)プラクティス」 Gantman氏はSBOMに3つの問題があると指摘しています。 コンポーネントはアトミックではない 脆弱性は状況に依存する 間接的なレイヤは根本的な依存を排除しない コンポーネントはアトミックではない Gantman氏は、「ソフトウェア部品」のアナロジーがそもそも不適切であると指摘しています。ソフトウェアを分解しても、車の部品のように規格化されたモジュールとして取り出すことはできません。サードパーティのパッケージがどのように使用されているかも不明な状況で、コード断片に一意の識別子やバージョンを付与しても意味のある管理にはならないと述べています。 脆弱性は状況に依存する Gantman氏は、コンポーネントの利用方法を知らなければ実際のリスクを評価できないと指摘しています。タイヤがパンクする欠陥を見つけたとしても、走行中の車なのか、木のブランコなのかで対処の優先度は大きく変わります。これはSBOMに限った話ではありませんが、実際のサービス影響を考慮した脆弱性評価を現場は求めています。 間接的なレイヤは根本的な依存を排除しない Gantman氏は、サードパーティライブラリのリスクはソフトウェア提供者でなければ判断が難しいことを指摘しています。先に述べたように、アトミック性やコンテキストの問題がある以上、利用者によるサードパーティライブラリのリスク評価は低精度かつ高ノイズなものになります。その結果、利用者は提供されたSBOMの情報をもとにソフトウェア提供者へリスクを問い合わせる状況が生まれます。 利用者がソフトウェア提供者にサードパーティライブラリのリスク評価を委任できるのであれば、そもそもSBOMの情報を提供する意味があまりないように感じます。 運用上の課題は他にも SBOMは優れたアイディアですが運用例があまり報告されておらず、実際のメリットは未知数な部分が大きいです。 また、SBOMのユーザ提供はソフトウェアの模倣リスクを高めることにも繋がるため、各社ベンダはメリットとデメリットを十分理解した上で取り組む必要があります。 ソフトウェアセキュリティの業界フォーラムであるSAFECodeも、以下のような声明を出しています。 Much of the text in the NTIA request for comments describes benefits from an SBOM that are highly speculative. It is important that no guidance or requirements about SBOM be issued under the Executive Order until there are clear and complete demonstrations at scale that the putative benefits are in fact realizable.” - SAFECode 2022年9月の米国大統領令 10 の中には、「募集要項でSBOMを要求する場合がある」と書かれているだけですが、アメリカ以外の政府機関もこの方向に動いていく可能性は十分に考えられます。 実運用からみたSBOMの利用 NTTコミュニケーションズでは、既にSBOMを利用したアタックサーフェスマネジメントを実験的に始めています。 取り組みの際、私たちが考慮したことは「SBOMの生成や収集が目的になるような運用をしない」ことでした。先にも説明しましたが、SBOMのユースケースは、脆弱性の識別、ライセンス管理、古いパッケージの検出と多岐に渡ります。 「なんとなく良い/悪いけど、いまいち効果が分からない」事態を避けるため、実際の使い道を決めてからT字型に運用の幅を広げていくアプローチを取りました。 何のためのSBOMなのか 私たちは、脆弱性管理の観点から最初の取り組みを始めました。その理由は大きく分けて2つあります。 社内に脆弱性管理をするシステムが既に存在する GitHubのDependabotと機能や性能を比較検証できる これだけ聞くと、既に脆弱性管理ができているので取り組む意味がないと感じるかもしれません。しかし、「既存の脆弱性管理システムをSBOMでどう改善できるのか?」「業界標準のサービスと比較して、新たにSBOMを利用する意味があるのか?」という2点は、SBOMのメリットやデメリットを確認する素晴らしい試金石になりました。 誰のためのSBOMなのか また、SBOMの利用者についても考える必要があります。私たちの議論では対象となる人物像を3つに絞りました。 脅威アナリスト SBOMを利用して、収集したサイバー脅威情報をフィルタリングしたい SBOMを起点に、サイバー脅威情報のハンティングをしたい 開発者 脆弱なパッケージを利用しているか検知したい 脆弱なパッケージが見つかれば修正したい 修正できなければサイバー攻撃を緩和する実装を知りたい 余分なパッケージが混入しているか検知したい 余分なパッケージを利用していれば削除したい 運用者 脆弱なパッケージを利用しているか検知したい 脆弱なパッケージが見つかれば修正したい 修正できなければサイバー攻撃を緩和する手法を知りたい 正直にいうと、全てのロールで実際の検証ができたわけではないのですが、誰がどう使うのかを意識しながら検証を進められた点は非常によかったと思います。 Gantman氏の記事にも注釈がある部分ですが、SBOMはまず社内利用またはグループ会社間の利用を想定すべきだと個人的には思います。 「透明性の確保」を目的に外部提供を推進する動きもありますが、実際のユースケースを集めずにSBOMの生成や利用の基準を統一することは困難でしょう。 SBOM対応の脆弱性スキャナ OSSで有名な脆弱性スキャナはいくつかありますが、SBOMの観点から以下の2つを候補に選びました。 Trivy Grype (+Syft) どちらも非常に優れた脆弱性スキャナなので、どちらを使ってみるかは好みで良いと思います。開発の都合上、私たちは何度もスキャナを動かすことが想定されたので、スキャン速度が早い Trivy を採用しました。脆弱性スキャナの詳細に関しては、7日目の志村さんの記事をご覧ください! Gantman氏の問題を実運用から考える 結論から言うとSBOMに関するGantman氏の指摘は正しく、SBOMの実運用にはある程度の割り切りが必要です。 パッケージマネージャがアトミック性を決める コンテキスト問題はSBOMで解決できない(が、使われ方に偏りあり) 想定外のパッケージ混入を開発者に確認できる パッケージマネージャがアトミック性を決める 「コンポーネントはアトミックではない」は正しいですが、アトミック性を意識したソフトウェア開発は可能です。そして実運用の観点では、npmやPyPIなどの言語パッケージや、Ubuntuのaptで管理されるOSパッケージがアトミックな単位として機能します。 言い換えれば、「パッケージマネージャ」がなければ大規模なSBOM運用は難しいと思います。 本来のSBOMであれば、パッケージマネージャに拘らない絶対的な基準があるべきかもしれませんが、対応できたとしても実運用上は管理が困難です。 脆弱性管理やライセンス管理に関しては、「まずはパッケージ単位で管理できる状態をつくる」ことがSBOM導入の最初のステップになります。 コンテキスト問題はSBOMで解決できない 「脆弱性は状況に依存する」は正しいですが、ライブラリ次第では使われ方のコンテキストがほぼ一定のものがあります。npmにおいては、 google-auth-library のような認証系のライブラリがその例です。 一方、全く使われ方が想定できないものも存在します。ユーティリティ系のライブラリです。有名どころでは、 lodash や anymatch がありますが、社内のリポジトリを調べても本当に多種多様な使われ方をしていました。 SBOMはコンテキスト問題を解決できませんが、私たちはSBOMを根拠に社内で使われているライブラリの利用状況を調べることができました。 全てのパッケージを調べることは困難だと思いますが、パッケージの統計的な利用傾向やよく利用される上位のパッケージを調べることで、脅威アナリストはサービス影響のコンテキストを理解しやすくなると思います。 想定外のパッケージ混入を開発者に確認できる 「間接的なレイヤは根本的な依存を排除しない」は正しいですが、第三者がパッケージの使用について指摘できる点はメリットがあると感じました。 具体例を挙げると、 react-scripts の使用によって nth-check の脆弱性アラートが検出された事例です。 これは界隈では 有名なissue ですが、npmの脆弱性管理をシンプル化するには、本番環境で必要のないパッケージを dependencies ではなく devDependencies に移動する必要があります。 "dependencies": { "react": "^18.2.0", ... }, "devDependencies": { "react-scripts": "^5.0.1" ... }, SBOMの収集によって、本番環境に本来必要がないライブラリを検出できたことは、開発者のミスや勘違いを防ぐ仕組みとして意義があると思います。 逆に言えば、第三者にSBOMを提供するメリットは、実用上これくらいしかないのでは?という気がします。 Threatconnectome SBOMの検証を通じて、私たちはアタックサーフェスマネジメントツール Threatconnectome(スレットコネクトーム) を開発しました。 SBOMと脅威情報のマッチングによるアラート機能 脅威を軽減または削除するアクションの実行管理 アクションの実績からチームや個人のスキルレベルを評価 などの機能があります。 Software Identification(SWID)やCommon Platform Enumeration(CPE)は今回の目的では利用が難しく、UUIDと独自の命名規則でソフトウェア部品を管理しています。現在はTrivy対応だけですが、SyftやGrypeの連携も今後行う予定です。 来年にはOSS公開できると思いますので、皆さん期待して待っていてください。 SBOMを脆弱性管理に使ってみた感想 まず、社内の脆弱性管理を効率化するために、あらゆるメタデータを集めておくことが重要です。これはCODE BLUE 2022でAirbnb社のセキュリティエンジニアPlattner氏、Mashal氏が講演していた内容 11 とも被りますが、あくまで「SBOM」は収集するメタデータの1つに過ぎません。 ファイルフォーマットの選択もあまり意味がなく、 osquery でサーバのパッケージ情報を集めるような運用から始めても全く問題ないと思います。 むしろ、 重要なことは「どの情報を」「何のために使うか」 です。これを決めずにSBOM運用を始めるのはオススメしません。 社内の脆弱性管理システムの改善 誤解ないように説明すると、全てのプロダクトにいきなりSBOM管理は適用できません。理由は先にも述べた通り、「アトミック性を意識したソフトウェア開発」と「パッケージマネージャ」の存在が不可欠だからです。これらを考慮せずに作られたプロダクトに対して、大規模なSBOM管理は上手くいきません。 SBOM管理が可能なプロダクトに対し、アラート通知の効率化やオートクローズ対応を行うことで、 開発者や運用者に余裕をつくる ことが最初のポイントになると思います。 また、脆弱性対応の人為的ミスがないことを検証するフェーズも重要です。もし検証するフェーズがない場合は、SBOMを利用した検証から始めるのも良いでしょう。 GitHubのDependabotと比較 この部分の内容は、あくまでリポジトリを対象とした脆弱性管理であり、コンテナや物理サーバが対象ではないことに注意してください。 脆弱性管理の観点では、 trivy-db からエクスポートしたデータで脆弱性管理をしていたので、Dependabotで検知されたものは全て検出ができました。(trivy-dbは様々な脆弱性情報を収集しており、その中にはDependabotが使用する GitHub Security Advisories も含まれる) 私たちの環境では、GitHubアクションでmainブランチのSBOMを抽出し、その情報をThreatconnctomeに自動登録するワークフローを実行していました。 GitHub上で開発をしているのであれば、Dependabotによる脆弱性検知をまず最優先で有効化してください。 脆弱性 検知 の観点では、SBOM管理のソリューションを新たに導入するメリットはほぼないと思います。 一方、脆弱性 管理 の点ではDependabotアラートだけでは難しい状況がありました。実運用では「必ずしもライブラリのバージョンを上げたくない」ケースがあり、その場合は分散したレポジトリをアナリストが継続的に監視する必要があります。また、複数のレポジトリを横断する分析もGraphQLだけでは限界があり、アタックサーフェスマネジメントの点でも課題が残りました。 私たちはThreatconnctomeでこれらの問題を解決しました。組織によっては GitHub Advanced Security で十分なケースもあると思いますので、そちらの利用も是非検討してみてください。 SBOMで始める脆弱性管理の総論 SBOM管理ができないシステムの存在を受け入れる GitHubを利用しているのであれば、まずはDependabotアラートを有効化する パッケージマネージャで管理できる範囲のSBOM情報を収集する 脆弱性やサイバー攻撃の情報をSBOMと紐付けてアラート通知する SBOMを使って対応済みのアラートを検証する サービス運用やライブラリ利用のコンテキストに応じて、アラート通知を最適化する 利用するコンポーネントのアトミック性を確保し、SBOM管理ができるシステムを増やす パッケージマネージャで管理できない範囲のSBOM適用について検証する 脆弱性管理にSBOMを活用したいのであれば、開発チームと一体になってアトミック性の確保やコンテキストの問題に対処してください。つまり、 内製開発のプロセスから脆弱性対応を最適化する取り組み が必要です。それができなければ、SBOMで始める脆弱性管理は不十分で実用性のないものになるでしょう。 終わりに Metemcyberチームでは「SBOMによる脅威インテリジェンス利用の効率化」に取り組んでいましたが、やっていたことが今年偶然ヒットして驚いています。 NTTグループとしても、サプライチェーンセキュリティリスクに取り組むオープンコンソーシアム「セキュリティ・トランスペアレンシー・コンソーシアム」の設立に向けて準備 12 しており、現在私もその一員として活動しています。 最近は MetemcyberのGitHubレポジトリ を更新できていませんが、次年度は更新を再開する予定です。MISP配信をサブスク化する謎サービスが立ち上がるかもしれないので、引き続き動向をチェックしていただければと思います。 来年はいろいろ面白い情報を発信できると思いますので、是非 Twitterアカウント のフォローよろしくお願いします。 それでは、明日の記事もお楽しみに! https://www.ntia.doc.gov/report/2021/minimum-elements-software-bill-materials-sbom ↩ https://github.com/spdx/spdx-spec ↩ https://sbpgithub.com/CycloneDX/specification ↩ https://www.jolts.world/index.php/jolts/article/view/45 ↩ https://cyclonedx.org/about/history/ ↩ https://piyolog.hatenadiary.jp/entry/2020/12/20/045153 ↩ https://about.codecov.io/security-update/ ↩ https://www.jetro.go.jp/biznews/2022/09/e43da89f71ddf4b3.html ↩ https://www.linkedin.com/pulse/sbom-good-intentions-bad-analogies-uglyoutcomes-alex-gantman ↩ https://www.whitehouse.gov/wp-content/uploads/2022/09/M-22-18.pdf ↩ https://codeblue.jp/2022/talks/?content=talks_8 ↩ https://group.ntt/jp/newsrelease/2022/11/09/221109b.html ↩
クラウド&ネットワークサービス部の花川です。寒くなってきましたが、皆さんいかがお過ごしでしょうか。 タイトルの通り、NTTコミュニケーションズ(NTT Com)では、この冬に2つのインターンシップを開催します! *1 information.nttdocomo-fresh.jp この記事では、このうちの「現場受け入れ型インターンシップ」の紹介をしたいと思います! 現場受け入れ型インターンシップとは? 実際にプロダクト開発の現場で、NTT Comのエンジニア・デザイナーと一緒に働きながら、業務を体験していただくインターンシップです。 インターンシップを通して、NTT Comでのエンジニア・デザイナーの仕事を知ることができ、そして参加してくれる皆さんが成長できる内容となっています。 期間は、2023年02月06日〜17日のうち、土日祝を除く実働10日間です。 募集ポスト 募集ポストについては、下記のページの「受け入れポスト情報」をご覧ください。 information.nttdocomo-fresh.jp 記載されているもののうち、受け入れ会社に NTTコミュニケーションズ株式会社 と記載されたポストが、NTT Comでの業務になります! これまでのインターンシップの様子 これまで開催したインターンシップに参加してくれた学生の方々が、この Engineers' Blogに体験記を寄稿してくれています。 「インターンシップでどんなことに取り組むんだろう?」や「インターンシップを通して何が学べるのだろう?」といった疑問を解消する助けになれば幸いです。 AI分野 engineers.ntt.com セキュリティ分野 engineers.ntt.com engineers.ntt.com ネットワーク分野 engineers.ntt.com engineers.ntt.com ソフトウェア分野 engineers.ntt.com engineers.ntt.com engineers.ntt.com まとめ みなさんも、この冬、インターンシップに参加して圧倒的成長をしてみませんか? 大事なことなのでもう一度書いておきますが、現場受け入れ型インターンシップのページはこちらです。 information.nttdocomo-fresh.jp エントリーもこのページからできます。12月14日(水) 13:00締切となっています。 この記事を読んだそこのあなたからの応募を待っています! *1 : 今年は、NTT Comがドコモグループの一員になったこともあり、ドコモグループとしての開催となります。
サマリ 様々な Path Computation Element(PCE)実装を用いた Explicit Path の SR Policy 設定検証 この記事は Multi-AS Segment Routing 検証連載の第 11 回です。目次は こちら 概要 イノベーションセンターの竹中です。 本記事では私たちの検証環境において様々な PCE 実装を用いた SR Policy の発行方法を検証します。 4 つの PCE の検証手順を各章で記載しますが、それぞれの PCE の環境準備手順と初期設定、PCC との間で PCEP Session を確立するまでの設定は省略します。 つまり PCE と PCC 間で PCEP Session が確立した状態から、特定の経路を指定する SR Policy の発行と確認、さらに VPN 通信で当該経路が使用されていることを確認します。 なお、詳細は各章で後述しますが、 PCEP で BGP Color を扱う仕様は Internet-Draft として現在 ( 2022 年 11 月 ) も議論中です。 そのため一部 PCE においては Multi-vendor での SR Policy の発行に相互運用できない部分があったため、PCE と PCC の組み合わせは限定的になっています。 共通の検証環境を示した後、それぞれの PCE について検証します。 検証 検証構成 以下のトポロジーで検証します。 各ルーターの製品とバージョンは以下の通りです。 rt01: ASR9901(IOS XR 7.6.1) rt02: MX204(Junos 22.1R1.10) rt03: ASR9901(IOS XR 7.5.1) rt04: MX204(Junos 21.4R1.12) rt05: Cisco 8201(IOS XR 7.5.1) rt06: PTX10001-36MR(Junos 21.2R1.15-EVO) rt07: ASR9902(IOS XR 7.6.1) rt08: MX204(Junos 21.4R1.12) 各 PCE は PCC である rt01、rt02 それぞれと PCEP Session を確立し、 PCC に SR Policy を追加します。rt01 と rt02 間の最短パスは直結の経路ですが、今回は rt01(IOS XR)に対しては rt01 -> rt05 -> rt06 -> rt02 となる経路を、rt02(Junos)に対しては rt02 -> rt06 -> rt05 -> rt01 となる経路を各 PCE で設定します。 なお rt04、rt08 は本検証では利用していませんが、検証環境上の都合でトポロジー図に記載しています。 また、事情により rt06 を通る経路の traceroute は当該ホップの結果で * * * と表示されています。 IOS XR SR-PCE IOS XR は SR Policy を管理する PCE(SR-PCE)として利用可能であり、対象とする PCC や SR Policy を設定できます。 本節の検証では以下の図のように、rt03 を IOS XR SR-PCE として利用します。rt01(IOS XR)と PCEP Session を張り、SR Policy を発行します。 概要でも軽く言及しましたが、本検証に PCE として用いた IOS XR 7.5.1 は SR Policy の Color の指定を Vendor Information object 内で Cisco 社独自実装の PCEP TLV を用いて行っています。 Junos PCC では当該 TLV を解析できないため、rt02 を PCC とした検証は省略しています。 PCEP Session の確認 PCEP Session の確認は show コマンドで行います。 RP/0/RSP0/CPU0:rt03#show pce ipv4 peer Wed Nov 2 19:28:15.630 JST PCE's peer database: -------------------- Peer address: 10.255.1.1 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation SR Policy の発行 pce コマンド配下に SR Policy の定義を追加することで、PCC へ SR Policy を発行できます。 RP/0/RSP0/CPU0:rt03(config-pce-sr-te-pp-info)#show commit changes diff Wed Nov 2 19:34:01.914 JST Building configuration... !! IOS XR Configuration 7.5.1 pce + segment-routing + traffic-eng + segment-list name XR-PCE-PATH + index 1 mpls label 16005 + index 2 mpls label 16006 + index 3 mpls label 16002 ! + peer ipv4 10.255.1.1 + policy IOSXR-SR-PCE + color 100 end-point ipv4 10.255.1.2 + candidate-paths + preference 100 + explicit segment-list XR-PCE-PATH ! ! ! ! ! ! ! ! end PCE で発行済 SR Policy の確認 PCEP Session と同様に、show コマンドで 発行済 SR Policy の簡易情報が確認できます。 SR policy が up していることを確認します。 RP/0/RSP0/CPU0:rt03#show pce segment-routing traffic-eng policy Wed Nov 2 20:49:50.364 JST PCE's policy database: ---------------------- PCC Address: 10.255.1.1 Color: 100, Endpoint: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Candidate-paths: Symbolic-name: IOSXR-SR-PCE (Active) PLSP-ID: 1 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Fri Nov 11 18:37:54.029 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:00:44 (since Nov 11 18:37:10.075) Candidate-paths: Preference: 100 (PCEP) (active) Name: IOSXR-SR-PCE Requested BSID: dynamic PCC info: Symbolic name: IOSXR-SR-PCE PLSP-ID: 6 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce 10.255.1.3) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24018 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 traceroute で経路確認 rt01 の VRF 100 が持つネットワークから rt02 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 2 20:43:30.269 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 3 msec 2 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec Cisco Crosswork Network Controller (CNC) CNC は Cisco 社が提供する PCE を含むソフトウェアスイートであり、GUI でトポロジーの確認や SR Policy などのパス情報の管理ができます。 本節の検証では、以下の図のように CNC を設置します。rt01(IOS XR)と PCEP Session を張り、SR Policy を発行します。 IOS XR SR-PCE と同様に、現在(2022 年 11 月)CNC は SR Policy の Color の指定を Vendor Information object 内で Cisco 社独自実装の PCEP TLV を用いて行っています。 Junos PCC では当該 TLV を解析できないため、rt02 を PCC とした検証は省略しています。 PCEP Session の確認 CNC の構成の一部として、IGP / BGP / PCEP などのプロトコルを用いてネットワーク機器と通信するための Data Gateway が存在します。PCEP Session の確認は Data Gateway へログイン後、CLI コマンドにて行います。 RP/0/RP0/CPU0:sr-pce#show pce ipv4 peer Wed Nov 9 16:34:23.555 JST PCE's peer database: -------------------- Peer address: 10.255.1.1 State: Up Capabilities: Stateful, Segment-Routing, Update, Instantiation SR Policy の発行 GUI にて、以下の方法で SR Policy を追加できます。 Services & Traffic Engineering > Traffic Engineering を選択します。 Create > PCE Init を選択し、SR Policy 作成画面を開きます。 SR Policy の Headend、Endpoint、Color をそれぞれ選択します。 経由するルーターと利用する SID の組み合わせを選択し、Segment List を作成します。 Provision を選択することで、SR Policy が発行されます。 PCE で発行済 SR Policy の確認 GUI から確認できます。SID をもとにした経路表示や、IGP Shortest Path を考慮した経路の表示が可能です。 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Mon Nov 14 19:27:00.420 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:07:11 (since Nov 14 19:19:49.551) Candidate-paths: Preference: 100 (PCEP) (active) Name: CNC-Cisco Requested BSID: dynamic PCC info: Symbolic name: CNC-Cisco PLSP-ID: 8 Protection Type: protected-preferred Maximum SID Depth: 10 Dynamic (pce 10.61.0.248) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24013 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 traceroute で経路確認 rt01 の VRF 100 が持つネットワークから rt02 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:ar-rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 9 17:46:10.915 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 4 msec 4 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec Paragon Pathfinder Paragon Pathfinder(以下、 Pathfinder)は Juniper 社が提供する PCE であり、GUI でトポロジーの確認や SR Policy などのパス情報の管理ができます。 本節の検証では、以下の図のように Pathfinder を設置します。rt02(Junos)と PCEP Session を張り、SR Policy を発行します。 なお検証環境の都合上、本節のみ IP アドレスが変わっていることにご留意ください。 現在(2022 年 11 月)Pathfinder は SR Policy の Color の指定を Association object 内で Juniper 社独自実装の PCEP TLV を用いて行っています。 IOS XR PCC では当該 TLV を解析できないため、rt01 を PCC とした検証は省略しています。 PCEP Session の確認 画面下部の Node タブから、各ルーターの情報を確認できます。PCC である rt02 との間で PCEP Session が Up していることを確認します。 SR Policy の発行 GUI にて、以下の方法で SR Policy を追加できます。 画面下部の Tunnel タブへ切り替えた後 Add ボタンを選択し、SR Policy を発行します。 Properties タブで SR Policy の Provisioning Method(PCEP)、SR Policy 名、headend、Tailend をそれぞれ選択します。 また、Provisioning Type は SR を選択します。 Path タブを選択し、経路を選択します。 Loose Source Routing で rt06、rt05、rt01をそれぞれ指定します。 Submit を選択することで、Provision LSP 画面が閉じ、SR Policy が発行されます。 PCE で発行済 SR Policy の確認 GUI から SR Policy の状態を確認できます。 Tunnel タブに SR Policy が追加され、Op Status が Active となることを確認します。 PCC で受けとった SR Policy の確認 user@rt02> show spring-traffic-engineering lsp name Paragon-Juniper detail Name: Paragon-Juniper Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.2.7 State: Up Path Status: Up Outgoing interface: et-0/0/0.0 Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: IPv4 Adjacency ID, 10.2.11.2 -> 10.2.11.1 SID type: 20-bit label, Value: 99 Hop 2 (Strict): NAI: IPv4 Adjacency ID, 10.2.7.2 -> 10.2.7.1 SID type: 20-bit label, Value: 65 Hop 3 (Strict): NAI: IPv4 Adjacency ID, 10.2.5.1 -> 10.2.5.2 SID type: 20-bit label, Value: 24005 Total displayed LSPs: 1 (Up: 1, Down: 0) traceroute で経路確認 rt02 の VRF 100 が持つネットワークから rt01 の VRF 100 が持つネットワークへ traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 user@rt02> traceroute 192.168.0.254 routing-instance 100 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 et-0-1-4.rt06 (10.2.11.1) 1.284 ms 0.929 ms 0.844 ms MPLS Label=65 CoS=0 TTL=1 S=0 MPLS Label=24005 CoS=0 TTL=1 S=0 MPLS Label=24014 CoS=0 TTL=1 S=1 2 HundredGigE0-0-1-2.rt05 (10.2.7.1) 1.117 ms 1.127 ms 0.811 ms MPLS Label=24005 CoS=0 TTL=1 S=0 MPLS Label=24014 CoS=0 TTL=2 S=1 3 HundredGigE0-0-0-20.rt01 (10.2.5.2) 6.980 ms * 3.839 ms Pola PCE Pola PCE は、 私(竹中)と三島が開発し OSS として公開中の CLI ベースの SR PCE です。Pola PCE は IOS XR および Junos どちらに対しても yaml ファイルで記述した SR Policy の適用ができます。 本節の検証では、以下の図のように Pola PCE 用サーバーを設置します。rt01(IOS XR)・ rt02(Junos) と PCEP Session を張り、各 PCC へ SR Policy を発行します。 PCEP Session の確認 pola コマンドを用いて確認できます。 user@pce:~$ pola session sessionAddr(0): 10.255.1.2 sessionAddr(1): 10.255.1.1 SR Policy の発行 作成する SR Policy の情報を yaml ファイルに記載し、pola コマンドを用いて発行します。 rt01 SR Policy の定義 asn: 65001 srPolicy: pcepSessionAddr: 10.255.1.1 name: Pola-PCE-Cisco srcRouterId: 0000.0aff.0101 dstRouterId: 0000.0aff.0102 color: 100 type: explicit segmentList: - sid: 16005 - sid: 16006 - sid: 16002 pola コマンド user@pce:~$ pola sr-policy add -f blog11_cisco.yaml success! rt02 SR Policy の定義 asn: 65001 srPolicy: pcepSessionAddr: 10.255.1.2 name: Pola-PCE-Juniper srcRouterId: 0000.0aff.0102 dstRouterId: 0000.0aff.0101 color: 100 type: explicit segmentList: - sid: 16006 - sid: 16005 - sid: 16001 pola コマンド user@pce:~$ pola sr-policy add -f blog11_juniper.yaml success! PCE で発行済 SR Policy の確認 pola コマンドで発行済み SR Policy の情報が確認できます。 user@pce:~$ pola sr-policy list LSP(0): PcepSessionAddr: 10.255.1.1 PolicyName: Pola-PCE-Cisco SrcAddr: 10.255.1.1 DstAddr: 10.255.1.2 Color: 100 Preference: 100 DstAddr: 10.255.1.2 SegmentList: 16005 -> 16006 -> 16002 LSP(1): PcepSessionAddr: 10.255.1.2 PolicyName: Pola-PCE-Juniper SrcAddr: 10.255.1.2 DstAddr: 10.255.1.1 Color: 100 Preference: 100 DstAddr: 10.255.1.1 SegmentList: 16006 -> 16005 -> 16001 PCC で受けとった SR Policy の確認 PCC 側でも SR Policy を受け取っていること、また up していることを確認します。 rt01 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Fri Nov 11 20:33:37.079 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:00:20 (since Nov 11 20:33:17.017) Candidate-paths: Preference: 100 (PCEP) (active) Name: Pola-PCE-Cisco Requested BSID: dynamic PCC info: Symbolic name: Pola-PCE-Cisco PLSP-ID: 7 Protection Type: unprotected-preferred Maximum SID Depth: 10 Dynamic (pce 10.99.0.254) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24013 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 rt02 user@rt02> show spring-traffic-engineering lsp detail Name: Pola-PCE-Juniper Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.1.1-100<c> State: Up Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.6 SID type: 20-bit label, Value: 16006 Hop 2 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.5 SID type: 20-bit label, Value: 16005 Hop 3 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.1 SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) traceroute で経路確認 rt01 の VRF 100 が持つネットワークと rt02 の VRF 100 が持つネットワークで双方に traceroute を行います。 発行した SR Policy に従った経路となっていることが確認できます。 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf 100 Wed Nov 2 20:43:30.269 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16006/16002/18 Exp 0] 3 msec 2 msec 4 msec 2 * * * 3 192.168.1.254 2 msec 1 msec 1 msec user@rt02> traceroute 192.168.0.254 routing-instance 100 no-resolve traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 * * * 2 10.1.10.1 2.602 ms 4.062 ms 1.779 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24009 CoS=0 TTL=2 S=1 3 * 10.1.1.1 3.826 ms * まとめ 本記事では様々な PCE を用いて PCE から PCC へ SR Policy を発行する動作を検証しました。 特に Color 指定において相互運用性の制限がみえました。