Azure Functions を使って、Nature Remo から Mackerel にメトリックを投げる

もう何番煎じのネタかわかりませんが、Nature Remo のデータを Mackerel のサービスメトリックに投げてみたのでそのメモです。

Nature Remo を使っていた以前、iRemocon という同じようなセンシングデバイスを使っていました。

その時は Zabbix から iRemocon に telnet でデータを pull して、それをグラフ化&アラートに投げるというレガシーな方法でアクセスしていたのですが、Nature Remo にしてからその辺できていなかったのでGW の宿題としてやってみることにしました。せっかくなので、クラウドネイティブ的に、サーバーレスでやってみました。

今回使った技術としては、

  • TypeScript
  • Azure Functions
  • Azure DevOps(Pipelines)
  • Mackerel API
  • Nature Remo API

です。

Nature Remo API からのデータ取得と Mackrel API への投稿

ソースコードはこちら。使ってみたかったという理由で TypeScript にしています。
github.com


Nature Remo APIへのアクセスの仕方等はググるといくらでも出てくるのでここでは書きませんが、API はSwagger にあるので、こちらを参考にしました。

Swagger: http://swagger.nature.global/

Cloud API と Local API がありますが、Cloud API を使ってます。必要かどうかはさて置き、iRemocon と違って、湿度が 10 % 単位に丸めないで欲しいなぁと思いました。

Mackrel API への投稿はこちらのドキュメントを参考にしました。
mackerel.io

サービスメトリックの投稿についてはそんなにはまることなくすんなりいきましたが、メトリック名に日本語を使うと 400 が返ってくるので諦めて英語にしました。
あと、Slack からの Slash Commands を使って、例えば /mkr graph とかやったらグラフを取得する、みたいなことをしたかったのですが、グラフ画像を取ってくる API を見つけられていないのでどうしようかなぁと悩み中です。

Azure Functions

今回は、定期的にデータを取得して投稿する、ということをしたかったので、Azure Functions の Timer Trigger を使いました。
docs.microsoft.com

function.json に cron 式を書くことができます。
こんな感じ。*NIX 出身者的には分りやすくていい記法ですね。

"schedule": "0 */1 * * * *"

CI/CD

ソースコードGitHub にありますが、CI/CD の方法として Azure DevOps の Pipelines を使いました。
Azure Functions のデプロイセンターに Local Git を設定する方法も考えたのですが、TypeScript からトランスパイルをしないといけなかったので、Pipelines を使うことにしました。
Push があった場合、自動ビルドして、リリースはマニュアルとしました。

といってもやることはほとんどなく、ビルドは Azure Functions for Node.js というテンプレートがあったのでそのまま使えました。
テンプレート
f:id:mitsuki0820:20190429024835p:plain
設定後
f:id:mitsuki0820:20190429024918p:plain

リリースも、Azure Functions へのリリースがテンプレートとしてあったのでこれもそのまま使います。
テンプレート
f:id:mitsuki0820:20190429025038p:plain
設定後
f:id:mitsuki0820:20190429025203p:plain

まとめ

これまで Azure Functions を使う時は適当な JavaScript をポータルからそのまま入力する方法で何となく使ってましたが、今回初めて VS Code + TypeScript という開発環境でやってみました。

Functions + TypeScript + VS Code + Pipelines の組み合わせ、めちゃめちゃ楽ですね。
特に VS Code を使っていて、F5 → Functions のデバッグがコーディングを中断させず、ブレークポイントも手軽に張れるのはすごく便利。

Storage Account の静的 Web サイト ホスティング + Azure Functions with TypeScript で(開発生産性の観点でも)十分動的サイト作れそうですね。


次は Durable Functions やってみようと思います。

Microsoft の OpenHack に行ってきた

最初は特にブログに書くつもりもなかったのですが、あまりに素晴らしいイベントで、さすがにインプットをそのまま肥やしにするのはもったいないと思いましたので書くことにしました。

OpenHack とは?

Microsoft が主催する 3 日間の開発イベントです。全世界で開催されており、東京は(MSの)今期3回目、過去機械学習 や サーバーレス をテーマにした回があったようです。
今回のテーマは"Intelligent Bots"でした。

イベントカレンダーがこちらにあります。
openhack.microsoft.com


イベントの雰囲気はこんな感じです。
www.youtube.com

動画の様な感じで、100人くらいの参加者を 20 くらいのテーブルに分けて、4~5人 + コーチ1人が集まってテーブルごとに課題を進めていくような形です。

このコーチは世界各国から集められており、私のテーブルには韓国から来たエンジニアでした。
初日、受付が終わった後やたらと英語のスピーカーがいて、最初はやってしまった感がありましたが、参加者はほとんど日本人でホッとしました。。
ただ、コーチは英語しか話せないので基本的に英語でやり取りする必要があります。一応通訳者もいましたがいちいち通す時間ももったいないので、私含め皆さん自力で頑張っていました。

なぜかひどい英語を話す私が通訳するようなこともあり、こういう時やっぱり普段英語にある程度触れているのと全く触れていないのは違うなぁというのは感じつつ、(もう何度思ったかわかりませんが)改めて英語ちゃんとやろうと思いましたね。。

どんな感じで進めるの?

進め方としては、コーチから課題を出されて、それを1つずつ解いていくような形です。たぶんコーチによる部分もあると思いますが私のテーブルでは、こういう課題があります、では皆さん頑張って、という感じでかなりスパルタでした。

ただ、そこからみんなでワイワイガヤガヤやりながら、分からないところはコーチにアドバイスを求めて、という感じで、すごく楽しい雰囲気ですすめられました。

同じテーブルになった方は開発経験があまりない or JavaScript/Git のような Web 開発をやったことがない or Azure をあまりご存知でない、というスキルセットだったので、MS からの参加者という負い目(?)もあり、私は結構フォローに回っていました。
そういう意味で、チーム開発のようなものも久しぶりに味わえたので楽しかったですね。

課題の内容は?

内容については公開されていないので具体的には伏せますが、イベントをやるのでお客様はFAQを自動化したいと考えてます、という背景説明があり、まずは開発環境を整えてエコーボットを作ってください、というざっくりした内容で、あとは参考リンクが載っているくらいです。そのあとの課題も、ボットに会話させましょう、フィードバックを得られるようにしましょう、という感じで、課題というか追加の要件が書かれているような内容でした。

ざっくりどんな感じかはこちらの登録ページを見ると分かると思います。
www.microsoftevents.com

マニュアルが用意されていて上から順番にやっていけば一通りの環境ができます、という感じのセミナー形式ではないので、個人の技量がかなり試されるのは間違いない内容でしたね。

まぁ実際の現場では背景や要件も違いますし、当然ながら順番に手順が用意されているわけでは無いので、今回の内容はかなり実践向きと思います。
他のテーブルでは、そういうセミナータイプのものを期待されて来た方が初日の午前でお帰りになったというのを聞いて、もったいないなぁと思いつつ、結構人を選ぶスタイルだよなぁと思いました。

特にガチガチの日本企業に新卒入社して社外の勉強会やカンファレンスに行ったことが無いタイプの人には厳しいんじゃないかなと思います。

豪華なケータリング&ランチ

本筋とはずれますが、、、とにかく食べ物&飲み物が十分に用意されていて、とても無料とは思えないメニューでした。
こんな感じ。

■ 初日
今半(写真無し)

■ 2日目
崎陽軒
f:id:mitsuki0820:20190425224745p:plain

■ 3日目
なだ万
f:id:mitsuki0820:20190425224742p:plain

ランチのお弁当以外にも、コーヒー・お茶・ジュース各種、ドーナツ、お菓子各種等海外のカンファレンスでよくある様なケータリングの豊富さで、2日目の最後は懇親会でお酒も出てくるという抜かりないフォーメーション。

さすがに一緒のテーブルにいた人も、これヤバくないですか。。後で請求されませんよね。。と心配していました。

どういう機能を使ったの?

技術的な観点でざっくりと今回触ってみたものと所感です。

JavaScript
他の人をフォローしつつ思ったのですが、やっぱり型が無いのはしんどい。。大文字小文字間違えてるのに実行するまでわからないとか、ブロックスコープがないみたいなJS特有の仕様とか、慣れてない人 or 大規模開発には結構きついんじゃないかと思います。手軽な反面、他の言語を知っている人からすると逆にはまる言語だなと思いました。

■ Azure Bot Service
ボットをフレームワーク化した SDK + ツール群を指します。ボットのフレームワーク、というのが慣れないので、ダイアログとかターンコンテキストとか、概念を理解するのに初日は苦労しました。
まぁ書いていればそのうち慣れるかなとは思います。今は v4 が最新らしいです。

この3日間はとにかくドキュメントを読み込んでました。
docs.microsoft.com

■ Cosmos DB
Azure の NoSQL データベースです。
これは仕事でも触ったことがあるのでサクサクできました。

Cosmos DB ではまることは無かったですが、Bot Service からうまく保存ができない(なぜか会話ごとに保存されるのではなく、コンテキスト単位で上書きされていく)ので、もうちょっと悩んでみようと思います。

■ Azure Search
これも Azure の、フルテキスト検索サービスです。
データをインポートして、ポータルから検索してみるくらいはやったことはありましたが、JavaScript から、検索してボットに返すというのは初めてだったので、axios で REST 叩いて、ボットがレスポンスした時はちょっと感動しましたね。

公式の Node.js の SDK が無いのはちょっと厳しかったです。
※たどり着けなかっただけかなぁ。。これっぽい気もしていつつ理解するより axios 叩いた方が早いと判断しました。

非公式のライブラリも見つけましたが、何となくaxiosで叩いたら動いたのでもういいやと思って使いませんでした。

この後にも自然言語処理やら認証やらありますが、残念ながらそこまでたどり着けませんでした。

まとめ

MS のエンジニアがコーチングしてくれて、3日間みっちりコードを書けて、ご飯も食べられて、しかも無料、とここまでやってくれる学習イベントって義務教育以来参加したことが無いので(義務教育も給食費掛かりますしw)、間違いなくお勧めのイベントです。

次回あったらぜひまた参加したいと思います。

Azure 関連のブログ移動先のまとめ

TechNet ブログが無くなってしまうということで、MSDN フォーラムに移動され始めています。
(フォーラムって。。というのはさて置き。。)

自分の FeedlyRSS を更新しつつ ブログの移動先をまとめてみました。随時更新していく予定です(たぶん)。

  • とりあえず日本語のブログを対象にしています。
  • Azure 以外も含まれます。

(2019/3/27更新)

Azure Policy で監査した結果を Azure Monitor で通知する

上限付きのサブスクリプションで使っていると地味に SSD のディスクってコミットから削られていきます。
ということで、Azure Policy を使って、SSD ディスクを見つけるポリシーを書いてみました。

また、検知するだけだとダッシュボードを見に行く必要があるので、Log Analytics を使って、アラートも作ってみます。

Azure Policyの定義

下の JSON を見てもらえれば分ると思いますが、type が Microsoft.Compute/disks のもので、SKU の名前が SSD の場合、アクティビティログに出すようにしました。

{
  "if": {
    "allof": [
      {
        "field": "type",
        "equals": "Microsoft.Compute/disks"
      },
      {
        "field": "Microsoft.Compute/disks/sku.name",
        "in": [
          "Premium_LRS",
          "StandardSSD_LRS",
          "UltraSSD_LRS"
        ]
      }
    ]
  },
  "then": {
    "effect": "audit"
  }
}

ポリシーを作ったら、ログをひっかけるときに使う、定義 ID をコピーしておきます。
f:id:mitsuki0820:20190113054221p:plain

Log Analytics のクエリと Azure Monitor の設定

続いて、Log Analytics のクエリです。
アクティビティログを見ると、OperationNameは、"Microsoft.Authorization/policies/audit/action" の様です。
また、ログの Properties から、policies 要素を取ってます。この部分もうちょっと改善したいところですが。。

また、policies を展開した 変数 a には、ポリシーのプロパティが入っているので文字列として扱って、ポリシーの 定義 ID(policyDefinitionId) をひっかけてます。

AzureActivity
| where OperationName == "Microsoft.Authorization/policies/audit/action"
| extend a = extractjson("$", Properties)
| where a contains "8630d37b-2d65-44ff-be11-a0763c3c31ea"

Azure Policy のチェック間隔は、24 時間に1回なので、アラートは設定できる最大の 1 時間に 1 回チェックするようにしました。

f:id:mitsuki0820:20190113054552p:plain

ポリシーと組み合わせるといろいろできそうなので積極的に使ってみようと思います。

Azure のカスタムポータルを作ってみた

この記事は、Microsoft Azure Tech Advent Calendar 2018 - Qiita の 24 日目の記事です。

Azure を実際に検証や運用で使ってみると、どうしても Azure ポータルだけでは事足りないことが出てきますが、そんな時、Azure CLI や Azure PowerShell でツール化することがあると思います。
今回は、Azure の API の勉強がてら、Web アプリケーションとして、ツールを作ってみました。

Azure Util Portal

いろいろ説明するよりも見た方が早いので作ったサイトです。


その前にお決まりです。
このツールは、あくまで個人的に作成したもので、所属する企業・組織に一切関係はありません。
またこのツールは、アカウントの認証を行い、取得したトークンを使って Azure の API へアクセスします。もちろん悪意を持って作ったツールではありませんが、不具合等により、Azure の環境に対して意図しない変更が加わることが考えられます。自己責任のもとご利用ください。


ということで、こちらです↓

Azure Util Portal
https://azutil.nomupro.com/

※これは MS 社員向けの補足なのですが、残念ながら、社内テナントでは Azure のリソースへアクセスする権限を持つアプリケーションが作れない(正確にはITにリクエストする必要がある)ので、MSA 等他のテナントのサブスクリプションでログインしてください。

画面

f:id:mitsuki0820:20181223200701g:plain

  • ログイン:MSA に対応するところまでいけませんでした。。組織アカウントでログインしてください。
  • 空のリソース グループを取得 : リソースが一つも含まれていないリソースグループを取得します。
  • 仮想マシンにアタッチされていない管理ディスクの取得 : 何個も仮想マシンを作っていると、放置された管理ディスクが出てきます。そんな管理ディスクを見つけます。
  • SSD の管理ディスクを取得 : 特に検証環境として使っている場合、検証のタイミング以外、SSD の管理ディスクは不要なことがあります。SSD の管理ディスク(Premium/Standard SSD)を見つけます。
  • SSD の管理ディスクを HDD に変換 : SSD の管理ディスクを一気に HDD に変換します。※変更コマンドのため実行には注意してください。
  • Easy Virtual Machine searcher : 仮想マシンを名前で検索します。

技術的なこと

各サービスの関連図です。
f:id:mitsuki0820:20181223192216p:plain

API 周り

Azure の API が載っています。
docs.microsoft.com

開発中はこれとにらめっこしながら実装していきました。
生の API はちょっととっつきにくいかもしれませんが、一度実行できる環境を整えると手軽に扱えると思います。
新しい発見もあったりするので、よく使うサービスの API ドキュメントをじっくり読んでみるのはいかがでしょうか。

認証・認可

認証・認可には、Azure AD を使っていますが、ここが一番はまりました。。
何にはまったかというと冒頭に記載の通り、Microsoft のテナントでは、恐らく悪用されないようにと思いますが、リソース アクセスに制限がかけられている様で、認証コードを取得するタイミングで管理者に許可されていない旨のエラーが出ます。

早めに諦めればよかったのですが、何とかならないかと頑張った結果、結局何ともならず、一番時間が掛かってしまいました。

他のテナントでも設定によっては同じ状態になると思うので参考までにログイン画面を貼っておきます。

f:id:mitsuki0820:20181223173751p:plain

この状態では、管理者から同意してもらえない限りはトークンをもらうことはできません。


アプリケーションの登録は、Azure AD の[アプリの登録(プレビュー)]から行います。
以前は別のポータルサイトもありややこしかったのですが、Azure AD のメニューに統合され始めました。
f:id:mitsuki0820:20181223174525p:plain

ちなみに、Azure AD へのアクセス方法も現状少しややこしい状況になっています。
少し古いですが、このスライドが参考になります。

www.slideshare.net

Azure AD の認証・認可を理解するには、Open ID Connect や OAuth 2.0 の認証プロトコルの知識は当然ですが、Microsoft アカウントや組織アカウントのようなアカウントの種類と v1 エンドポイントやv2 エンドポイントのようなエンドポイントの種類があることを覚えておく必要があります。
また、それに応じて、MSAL や ADAL といったライブラリを使い分ける必要があります。
この辺の内容を書き始めるととても書ききれないので、詳しくは、中村さんの記事(Microsoft Graph を使ってみよう : Azure AD における認証概要 - Qiita)を参考にしつつ、Azure AD のドキュメントをじっくり読むといいでしょう。

アプリケーション

肝心のアプリケーション部分は、JavaScriptフレームワークの一つである、Vue.jsを使いました。
SPA をここまでちゃんと書いたのは初めてなのですが、Vue.js はかなりわかりやすいフレームワークでした。jQuery を使ったことはあったのですが、要素指定をしてfunctionのネストをして、、、というのを考えるとはるかに使いやすいものでした。
これまで JavaScript は騙し騙し使ってきていましたがちゃんと勉強しようと思わせてくれたフレームワークでした。

CSS は、Bootstrap 4 を使いました。ただあまりデザインにこだわっていると時間がなくなりそうだったので、結果の表示は必要最低限、見れればいいやくらいの感じにしました。

サーバー

作ったアプリケーションはどこかにホストする必要があります。
今回は、HTML と JS だけで動くアプリケーションということもあり、GA したての、Storage Account の静的 Web サイトを使ってみました。(※結構前から使えるようになってましたがGAしてなかったようです)

azure.microsoft.com

使い方は簡単です。v2 のストレージアカウントを作って、[静的な Web サイト]を有効にしてあげるだけで、あとは、$web というコンテナができるのでそこにファイルを置くと HTTP サーバーとしてアクセスできるようになります。

CI/CD

CI/CD 環境として、Azure DevOps を使っています。
Vue.js の単一コンポーネントを webpack な環境で作ってみたので、npm run build をして、JavaScript を生成してあげる必要があります。

毎回変更するたびに手作業でビルドしてデプロイするのは避けたかったので、Azure DevOps の Pipelines を使って、Git へのプッシュがあった場合に、ビルドとリリースをするようにしました。
リリースは、Storage Account へ azcopy するようにしました。

ちなみに、Builds はこんな感じです。

resources:
- repo: self
queue:
  name: Hosted Ubuntu 1604
  demands: npm

steps:
- task: Npm@1
  displayName: 'npm install'
  inputs:
    verbose: false


- script: |
   npm run build
   mkdir _src
   cp -ipR index.html dist _src
   
  displayName: 'Command Line Script'

- task: ArchiveFiles@2
  displayName: 'Archive files'
  inputs:
    rootFolderOrFile: '$(System.DefaultWorkingDirectory)/_src'

    includeRootFolder: false

    archiveType: tar

    archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).tar.gz'


- task: PublishBuildArtifacts@1
  displayName: 'Publish artifacts: drop'

ArchiveFiles で特定の複数のファイルの指定が分からなかったので、とりあえず script の中でフォルダーにファイルをコピーしてそれをアーカイブしています。
なんかもっとやりようがあるんだろうなぁ。

それにしても Azure DevOps 、統合されている感があってかなり使いやすいです。UI として左にメニューがあるのが結構好きですね。

Azure CDN

Storage Account にカスタムドメインを設定するのでもいいのですが、Azure CDN を使ってみました。
プロバイダーは、Akamai、Verizon、Microsoft が選択できます。今回は Microsoft を使いました。

カスタムドメインSSL 対応のため証明書の設定に若干時間が掛かりましたが、特にはまったところもなく数回のクリックで CDN まで使えるというのはほんとに便利な時代です。

開発環境

開発は、Visual Studio Code を使いました。
拡張は前から入っていたものがありますが、Bracket Pair Colorizer というブラケットをカラーリングしてくれるものを新たに入れてみました。これがあるのとないのとでは使い勝手が全然違ってくるのでお勧めです。
marketplace.visualstudio.com

あとは、Emacs バインド拡張は外せませんね(

REST API のテストには Postman を使いました。同期機能、変数の定義ができるあたり便利でした。
www.getpostman.com

まとめ

API でのリソースのコントロールは Azure の生の部分を触っているようで面白いですね。
引き続き、いろんな機能を実装していってみようと思います。

Yamaha RTX 810 で Azure の VNet ゲートウェイと BGP 接続する

ExpressRoute はなかなか触れるものでは無いので、仮想ネットワーク ゲートウェイ で BGP を試してみました。
対向のデバイスは RTX810 です。

IPSec に関して、以前はルートベース(IKEv2)のコンフィグが公開されていませんでしたが、今は対応しているコンフィグが公開されているので基本コピペで行けます。
BGP の設定に関しては、neighbor の ignore-capability=on さえ忘れなければうまくつながる(はず)です。

Azure の設定

Azure の設定としては、次のリソースの設定をする必要があります。

後で変更できなくて一番時間がかかるのは 仮想ネットワーク ゲートウェイ なので SKU の選択だけは慎重にする必要があります。

仮想ネットワーク ゲートウェイ

仮想ネットワークゲートウェイ(以下VNetGW)は、BGP が利用できる SKU が決まっており、VpnGw1/VpnGw2/VpnGw3 を選択する必要があります。
VNetGW は特に作成に20~30分かかるので SKU 選択は注意しましょう。

f:id:mitsuki0820:20181029103409p:plain

最初の作成画面で BGP の ASN の設定とかもできますが、後で変更できます。
ちなみに BGP の設定は構成の画面から設定できます。この設定は、あとでルーターに設定するときに使います。
この ASN は、Azure 側のルーターの ASN です。
f:id:mitsuki0820:20181029103731p:plain

ローカル ネットワーク ゲートウェイ

Azure から見たローカルの設定です。こちらも構成から設定します。

  • IP アドレス : ルーターのグローバル IP アドレスです。
  • アドレス : ルーター配下のローカルのネットワークですが、BGP の場合は、ピアとなるルーターの IP アドレスのみでいいので、/32で指定できます。
  • BGP 設定の構成 : こちらはローカル側の ASN/ピアの設定です。

f:id:mitsuki0820:20181029104227p:plain

接続

ローカルとの接続設定です。ここで設定するのは IPSec のための 共有キー と BGP の有効化の設定です。
特に共有キーは間違えないようにしましょう。BGP は有効にするだけです。が、これまでの設定ができていないとエラーが出ます。
f:id:mitsuki0820:20181029104819p:plain

とりあえず BGP と接続するだけなら Azure の設定はこれくらいで複雑な設定はありません。

このあたりのドキュメントが参考になります。
Azure VPN Gateways: Resource Manager: PowerShell で BGP を構成する | Microsoft Docs

ルーター

ルーター側の設定としては、まずは IPSec で接続して、そのあとに BGP でピアを張る形になります。

IPSec の設定

基本↓の設定でそのまま行けます。VPN よく張ってる人なら悩むことはないと思います。
Microsoft AzureとVPN(IPsec IKEv2)接続するルーターの設定 : コマンド設定

ここでは ipsec sa policy 1 1 esp aes256-cbc sha256-hmac anti-replay-check=off とかのゲートウェイ IDをそのままコピペしないように(1であればもちろん問題ないですが)しましょう。
(恥ずかしながら私はこんなところではまってました。。)

BGP の設定

BGP に関しても基本こちらのドキュメントの設定で行けます。
BGP-4 設定ガイド

が、1点、ignore-capability=on という設定を入れてあげる必要があります。ここに一番はまりました。。
BGP のプロトコルとして、使用できる機能(capability)という概念があるのですが、そのネゴシエーションで一致しなかった場合でも接続してしまうかどうか(無視するかどうか)を設定するオプションです。
色々調べていた時に、↓のJANOG の資料を何となく眺めていてもしやと思い、これを on にすると無事接続できました。

https://www.attn.jp/maz/p/t/pdf/janog41-bgp-tutorial.pdf

ちなみに、こんなエラーが出てきます。

2018/10/29 12:35:25: [BGP] Neighbor 172.21.1.254 (External AS 65001) Event [Start]  State [Idle] -> [Active]
2018/10/29 12:35:25: [BGP] Neighbor 172.21.1.254 (External AS 65001) Event [RecvOpen]  State [OpenSent] -> [Idle] 
↑------------------------------------------------- ここ ------------------------------------------------- ↑
2018/10/29 12:35:25: [BGP] SEND 192.168.1.1 -> 172.21.1.254 type 3 (Notification) length 21
2018/10/29 12:35:25: [BGP] RECV 172.21.1.254 -> 192.168.1.1 type 1 (Open) length 63
2018/10/29 12:35:25: [BGP] SEND 192.168.1.1 -> 172.21.1.254 type 1 (Open) length 29
2018/10/29 12:35:25: [BGP] Neighbor 172.21.1.254 (External AS 65001) Event [Open]  State [Connect] -> [OpenSent]
2018/10/29 12:35:25: [BGP] Neighbor 172.21.1.254 (External AS 65001) Event [ConnectRetry]  State [Active] -> [Connect]

BGP の遷移として、OpenSent -> OpenConfirm となるべきところが、Idle に戻ってしまっています。
これが延々繰り返されます。

ちなみに最終的な設定です。

bgp use on
bgp autonomous-system 65050
# ロギングの設定。設定中は必須。
bgp log neighbor packet
# 東日本(bgptest-gw) へのピア
bgp neighbor 1 65001 172.21.1.254 hold-time=30 gateway=tunnel5 local-address=192.168.1.1 ignore-capability=on
# 西日本(bgptest-gw-west) へのピア
bgp neighbor 2 65002 10.10.1.254 hold-time=30 gateway=tunnel1 local-address=192.168.1.1 ignore-capability=on
bgp router id 192.168.1.1
# ローカルのルートをアドバタイズする設定
bgp import filter 1 include 192.168.0.0/16
# 全てのルートをアドバタイズする設定
bgp import filter 100 include 0.0.0.0/0
# 静的経路のうち、192.168.0.0/16(filter 1で定義) のルートを ASN:65001 にアドバタイズ
bgp import 65001 static filter 1
# BGP の経路のすべて(filter 100)のルートを ASN:65001 にアドバタイズ
bgp import 65001 bgp 65002 filter 100
# 静的経路のうち、192.168.0.0/16(filter 1で定義) のルートを ASN:65002 にアドバタイズ
bgp import 65002 static filter 1
# BGP の経路のすべて(filter 100)のルートを ASN:65002 にアドバタイズ
bgp import 65002 bgp 65001 filter 100

最後に、 bgp configure refresh を忘れずに。

やってみた構成

せっかく時間をかけて GW を作ってみたのでいろいろ検証してみました。
構成を以下の様にしてみました。

  • RTX からそれぞれ IPSec で接続
  • 東日本と西日本に GW を作成
  • 東日本、西日本にそれぞれ複数の仮想ネットワークを接続し、VNet ピアリングで接続
  • RTX の ASN は 65050、東日本は 65001、西日本は 65002
  • 東日本のルートを西日本へ、西日本のルートを東日本にアドバタイズ
  • ホールドタイムを 30 に(特に意味はなく本当に変わるのかの確認)

f:id:mitsuki0820:20181031201416p:plain

RTX の状態

それぞれの AS からルートが流れていることが確認できます。

(抜粋)
RTX810# show ip route
Destination         Gateway          Interface       Kind  Additional Info.
default             -                    PP[01]    static  filter:20,21,22,40,41
default             -                    PP[01]    static  filter:9,12
default             -                    PP[01]    static  filter:11
10.2.1.0/24         -                 TUNNEL[3]    static
10.10.0.0/16        -                 TUNNEL[1]       BGP  path=65002
10.10.1.254/32      -                 TUNNEL[1]    static
10.12.0.0/16        -                 TUNNEL[1]       BGP  path=65002
172.21.0.0/16       -                 TUNNEL[5]       BGP  path=65001
172.21.1.254/32     -                 TUNNEL[5]    static
172.24.0.0/24       -                 TUNNEL[5]       BGP  path=65001
172.24.1.0/24       -                 TUNNEL[5]       BGP  path=65001
172.24.2.0/24       -                 TUNNEL[5]       BGP  path=65001
192.168.1.0/24      192.168.1.1           VLAN1  implicit
192.168.1.1/32      -                 TUNNEL[5]       BGP  path=65001
192.168.2.0/24      192.168.2.2           VLAN2  implicit
192.168.10.0/24     -                 TUNNEL[2]    static

東日本から受け取ったルート、アドバタイズしたルートが確認できます。

RTX810# show status bgp neighbor 172.21.1.254 received-routes
Total routes: 5
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 172.21.0.0/16      0.0.0.0                       65001 IGP
* 172.24.0.0/24      0.0.0.0                       65001 IGP
* 172.24.1.0/24      0.0.0.0                       65001 IGP
* 192.168.1.1/32     0.0.0.0                       65001 IGP
* 172.24.2.0/24      0.0.0.0                       65001 IGP

RTX810# show status bgp neighbor 172.21.1.254 advertised-routes
Total routes: 4
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 192.168.1.0/24     192.168.1.1                   65050 IGP
* 192.168.2.0/24     192.168.1.1                   65050 IGP
* 10.10.0.0/16       192.168.1.1                   65050 65002 IGP
* 10.12.0.0/16       192.168.1.1                   65050 65002 IGP

西日本から受け取ったルート、アドバタイズしたルートが確認できます。

RTX810# show status bgp neighbor 10.10.1.254 received-routes
Total routes: 3
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 10.10.0.0/16       0.0.0.0                       65002 IGP
* 10.12.0.0/16       0.0.0.0                       65002 IGP
  192.168.1.1/32     0.0.0.0                       65002 IGP

RTX810# show status bgp neighbor 10.10.1.254 advertised-routes
Total routes: 7
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 172.21.0.0/16      192.168.1.1                   65050 65001 IGP
* 172.24.0.0/24      192.168.1.1                   65050 65001 IGP
* 172.24.1.0/24      192.168.1.1                   65050 65001 IGP
* 172.24.2.0/24      192.168.1.1                   65050 65001 IGP
* 192.168.1.0/24     192.168.1.1                   65050 IGP
* 192.168.1.1/32     192.168.1.1                   65050 65001 IGP
* 192.168.2.0/24     192.168.1.1                   65050 IGP

あとはキープアライブ、ホールドタイムがちゃんと変わっているか見てみました。
RTX の場合、キープアライブは、ホールドタイムの 1/3 なので今回は10秒です。
IPSec を切断してみたところ、Update も大体30秒後くらいに出て、ルーティングテーブルから消えていました。

2018/10/31 21:36:18: [BGP] RECV 10.10.1.254 -> 192.168.1.1 type 4 (KeepAlive) length 19
2018/10/31 21:36:12: [BGP] SEND 192.168.1.1 -> 172.21.1.254 type 4 (KeepAlive) length 19
2018/10/31 21:36:10: [BGP] SEND 192.168.1.1 -> 10.10.1.254 type 4 (KeepAlive) length 19
2018/10/31 21:36:09: [BGP] RECV 172.21.1.254 -> 192.168.1.1 type 4 (KeepAlive) length 19
2018/10/31 21:36:08: [BGP] RECV 10.10.1.254 -> 192.168.1.1 type 4 (KeepAlive) length 19
2018/10/31 21:36:02: [BGP] SEND 192.168.1.1 -> 172.21.1.254 type 4 (KeepAlive) length 19
2018/10/31 21:36:01: [BGP] RECV 172.21.1.254 -> 192.168.1.1 type 4 (KeepAlive) length 19
Azure の状態

Azure の PowerShell で見てみます。

まずは GW の状態。

> Get-AzureRmVirtualNetworkGatewayBGPPeerStatus -VirtualNetworkGatewayName bgptest-gw -ResourceGroupName bgptest

LocalAddress Neighbor    Asn   State     ConnectedDuration RoutesReceived MessagesSent MessagesReceived
------------ --------    ---   -----     ----------------- -------------- ------------ ----------------
172.21.1.254 192.168.1.1 65050 Connected 00:45:11.8993646  4              816          800

> Get-AzureRmVirtualNetworkGatewayBGPPeerStatus -VirtualNetworkGatewayName bgptest-gw-west -ResourceGroupName bgptest

LocalAddress Neighbor    Asn   State     ConnectedDuration RoutesReceived MessagesSent MessagesReceived
------------ --------    ---   -----     ----------------- -------------- ------------ ----------------
10.10.1.254  192.168.1.1 65050 Connected 00:45:39.2323757  6              401          380


東日本の GW からアドバタイズしたルート、RTXから受け取ったルート

> $VgwName = 'bgptest-gw'
> Get-AzureRmVirtualNetworkGatewayAdvertisedRoute -VirtualNetworkGatewayName $VgwName -ResourceGroupName $RGName -Peer 192.168.1.1

LocalAddress Network        NextHop      SourcePeer Origin AsPath Weight
------------ -------        -------      ---------- ------ ------ ------
172.21.1.254 172.21.0.0/16  172.21.1.254            Igp    65001  0
172.21.1.254 172.24.0.0/24  172.21.1.254            Igp    65001  0
172.21.1.254 172.24.1.0/24  172.21.1.254            Igp    65001  0
172.21.1.254 192.168.1.1/32 172.21.1.254            Igp    65001  0
172.21.1.254 172.24.2.0/24  172.21.1.254            Igp    65001  0

>  Get-AzureRmVirtualNetworkGatewayLearnedRoute  -VirtualNetworkGatewayName $VgwName -ResourceGroupName $RGName 

LocalAddress Network        NextHop     SourcePeer   Origin  AsPath      Weight
------------ -------        -------     ----------   ------  ------      ------
172.21.1.254 172.21.0.0/16              172.21.1.254 Network             32768
172.21.1.254 172.24.0.0/24              172.21.1.254 Network             32768
172.21.1.254 172.24.1.0/24              172.21.1.254 Network             32768
172.21.1.254 192.168.1.1/32             172.21.1.254 Network             32768
172.21.1.254 192.168.1.0/24 192.168.1.1 192.168.1.1  EBgp    65050       32768
172.21.1.254 192.168.2.0/24 192.168.1.1 192.168.1.1  EBgp    65050       32768
172.21.1.254 172.24.2.0/24              172.21.1.254 Network             32768
172.21.1.254 10.12.0.0/16   192.168.1.1 192.168.1.1  EBgp    65050-65002 32768
172.21.1.254 10.10.0.0/16   192.168.1.1 192.168.1.1  EBgp    65050-65002 32768

西日本の GW からアドバタイズしたルート、RTXから受け取ったルート

> $VgwName = 'bgptest-gw-west'
> Get-AzureRmVirtualNetworkGatewayAdvertisedRoute -VirtualNetworkGatewayName $VgwName -ResourceGroupName $RGName -Peer 192.168.1.1

LocalAddress Network        NextHop     SourcePeer Origin AsPath Weight
------------ -------        -------     ---------- ------ ------ ------
10.10.1.254  10.10.0.0/16   10.10.1.254            Igp    65002  0
10.10.1.254  10.12.0.0/16   10.10.1.254            Igp    65002  0
10.10.1.254  192.168.1.1/32 10.10.1.254            Igp    65002  0

>  Get-AzureRmVirtualNetworkGatewayLearnedRoute  -VirtualNetworkGatewayName $VgwName -ResourceGroupName $RGName

LocalAddress Network        NextHop     SourcePeer  Origin  AsPath      Weight
------------ -------        -------     ----------  ------  ------      ------
10.10.1.254  10.10.0.0/16               10.10.1.254 Network             32768
10.10.1.254  10.12.0.0/16               10.10.1.254 Network             32768
10.10.1.254  192.168.1.1/32             10.10.1.254 Network             32768
10.10.1.254  192.168.1.0/24 192.168.1.1 192.168.1.1 EBgp    65050       32768
10.10.1.254  192.168.2.0/24 192.168.1.1 192.168.1.1 EBgp    65050       32768
10.10.1.254  172.24.2.0/24  192.168.1.1 192.168.1.1 EBgp    65050-65001 32768
10.10.1.254  172.24.1.0/24  192.168.1.1 192.168.1.1 EBgp    65050-65001 32768
10.10.1.254  172.24.0.0/24  192.168.1.1 192.168.1.1 EBgp    65050-65001 32768
10.10.1.254  172.21.0.0/16  192.168.1.1 192.168.1.1 EBgp    65050-65001 32768
Azure の仮想マシンから確認

JPEAST-Stagin の仮想マシン
JPEAST-Staging に VM を立ててみて、有効なルートから見てみました。
f:id:mitsuki0820:20181031210305p:plain
ちゃんと RTX からアドバタイズされているものが登録されています。

また、JPWEST-Production に traceroute してみたところ、RTX を経由してたどり着きました。

[tsunomur@vm2 ~]$ traceroute 10.12.0.4
traceroute to 10.12.0.4 (10.12.0.4), 30 hops max, 60 byte packets
 1  192.168.1.1 (192.168.1.1)  10.572 ms  11.485 ms  12.123 ms
 2  10.12.0.4 (10.12.0.4)  28.158 ms  28.156 ms  28.145 ms
他やってみたこと
  • JPEAST-Staging - bgptest-vnet 間のピアリング削除した状態 : JPEAST-Stagin -> bgptest-vnet の通信はできない。当然 RTX への通信もできない。
  • [ゲートウェイ転送を許可する]、[リモートゲートウェイを使用する]オプションを無効 : JPEAST-Stagin -> bgptest-vnet の通信はできない。

また、JPEAST-Common の VM から、JPWEST-Production への VM は、グローバル VNet ピアリングが優先されるため、RTX を通ることはありません。

まとめ

BGP の機能としてはもっとありますが、ちょっと触ってみるくらいであればそんなに難しくはないなと思いました。
ただNW機器は、ベンダーやファームウェアのバージョンの違いで相性みたいなものがあるので検証は必須だなというのを身をもって思い知らされました。。
さらに大きな規模な環境・複雑な構成で運用するとなった場合、日々の監視やHWのメンテ、HW故障時(もしくは Azure 障害時)の対応とか考えるとちゃんと運用できる体制を整えておかないといけないですね。