himanago

C#とかAzureとかMS系の技術メモ中心に書きたいです。最近はLINE関連技術やVUIも多め。

Ignite The Tour Osaka: OPS20「インシデントに対応する」フォローアップ~内容・デモ解説編その2~

間が空いてしまいましたがその2です。。

f:id:himanago:20200131235952p:plain

1/23(木)・24(金) 、インテックス大阪にて開催された Microsoft の大型カンファレンス「Ignite The Tour Osaka」で登壇した「インシデントに対応する」セッションのフォロー記事の続きです。

★前回:Ignite The Tour Osaka: OPS20「インシデントに対応する」フォローアップ~内容・デモ解説編その1~ - himanago

前回の内容を受けてのデモ

f:id:himanago:20200207154452p:plain

Tailwind Traders の課題がいくつかありましたが、

f:id:himanago:20200131201244p:plain

これに対して、まず「インシデントトラッキング」を行うための「ユニークなチャネル」を作ることで解決しようというデモです。

内容と登場技術

コミュニケーションに関する課題に対処するために、エンジニアがインシデントに関する議論を行うユニークなチャネルを作成する必要があります。

これはそのインシデントにのみ「固有の」チャネルであることが必要で、インシデント対応に集中したり、あとから分析したりするためにも普段使用しているチャネルとは分けるべきです。

そして今回は

  • Azure Boards(Azure DevOps)への issue 作成&オンコールなエンジニアのアサイ
  • Microsoft Teams へそのインシデントに関するチャネルの作成
  • オンコールなエンジニアへの連絡

を Azure Logic Apps で自動化します。

f:id:himanago:20200207155437p:plain

デモ

ざざっと画面キャプチャで流れを見ていきます。

Logic Apps フロー

ライブで作った部分も含め、見ていきます。

全体像

このような流れです。

f:id:himanago:20200207155853p:plain

Azure Monitor から連携することを想定していて、インシデント情報は JSON を HTTP で受け取るようにつくります(デモでは Postman で JSON を投げました)。

Azure Boards への issue 作成

f:id:himanago:20200207160257p:plain

まずは Azure DevOps へ issue を作ります。タイトルに JSON で飛んできたインシデント内容を入れておきます。Logic Apps では JSON の内容を項目(動的なコンテンツ)としてマウスクリックのみで扱えるのでとても楽ですね。

説明(内容)はひとまず空欄にしておきます。

Teams へのチャネル作成

インシデント対応チーム用の、このインシデント専用のチャネルを作ります。

f:id:himanago:20200207160555p:plain

ひとつ前のステップで作った issue の番号(ID)とランダムな数値をチャネル名に入れているのがポイントです。

Logic Apps ではステップ間の連動もこのように簡単にできます。

issue に Teams チャネルへのリンクを張る

f:id:himanago:20200207160817p:plain

チャネルのリンク(ひとつ前のステップで得られるチャネル ID を使って作る)を issue の説明に載せます。

オンコールなエンジニアを見つける

f:id:himanago:20200207160959p:plain

オンコールなエンジニアの情報は、今回は Azure Table Storage に入っているのでそこから取得します。

ちなみに Storage Explorer で見ると、このようになっています。

f:id:himanago:20200207161337p:plain

Table Storage から得た情報は JSON 形式なので、パースします。

f:id:himanago:20200207161436p:plain

パースした情報を使い、メールアドレスを issue の担当者として設定します。

f:id:himanago:20200207161516p:plain

インシデントチャネルに情報を投稿

最後のステップとして、インシデントの内容および issue へのリンクを含んだメッセージを作成したチャネルに投稿します。

今回は Adaptive Cards という形式でメッセージを送ってみます(オリジナルのセッションでは HTML 形式。どちらも「フローボット」という連携方法で送ります)。

{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "Image",
            "url": "https://globaleventcdn.blob.core.windows.net/assets/ops/ops20/screenshots/alert.gif",
            "size": "Small",
            "spacing": "None"
        },
        {
            "type": "TextBlock",
            "text": "[AlertType]: [SeverityDescription]"
        },
        {
            "type": "TextBlock",
            "text": "Issue を見る:"
        }
    ],
    "actions": [
        {
            "type": "Action.OpenUrl",
            "title": "Incident [ID]",
            "url": "https://xxxxxxxxx.visualstudio.com/xxxxxxx/_workitems/edit/[ID]/"
        }
    ]
}

ちなみに Adaptive Cards は VS Code 用の拡張機能もあり、プレビューを見ることもできます(コマンドパレットから「Adaptive Card: Preview」)。

f:id:himanago:20200207162707p:plain

f:id:himanago:20200207162635p:plain

Logic App は以下のようになります。動的なコンテンツをいくつか埋め込みます。 f:id:himanago:20200207162044p:plain

実際の動き

Postman から JSON を飛ばすと、インシデントの「28」issue が作成され、次々に自動で行われていきます。

f:id:himanago:20200207173458g:plain

このあと、Teams でコミュニケーションをとりながらインシデント対応をしていきます。

補足

なお、当日はできませんでしたが、オリジナルではさらにオンコールなエンジニアへの DM も作っています。

さらに以下のようなステップを追加すれば、

f:id:himanago:20200207164618p:plain

このようなメッセージが飛んでくるので、安心ですね。

f:id:himanago:20200207164750p:plain

Ignite The Tour Osaka: OPS20「インシデントに対応する」フォローアップ~内容・デモ解説編その1~

はじめに

f:id:himanago:20200131235952p:plain

1/23(木)・24(金) 、インテックス大阪にて開催された Microsoft の大型カンファレンス「Ignite The Tour Osaka」にて「インシデントに対応する」というセッションで登壇しました。

Microsoft Ignite The Tour Osaka - 2020年1月

Azure 上で動くシステムでインシデントが発生した場合の対応方法に関して、45分でインシデント対応に関する一般的なお話に加え、Azure や Teams を使ったデモを3つ行うという盛りだくさんのセッションです。

時間が短めの中でかなり速く進めたので、終了後に質問いただいた方や、アンケートでも「デモが短い」「時間が足りない」という声がありましたので、少しフォローアップとして記事を書きます。

しゃべった身としても、せっかくのいろんな要素が詰まった完成度の高いデモの面白さを十分に伝えられていないのはもったいない&オリジナルの作者様に申し訳がたたないので、なんとか、この記事が届いてくれることを祈ります。

なお、この記事とは別に環境の作り方やハマったところなども続けて記事化する予定です。

セッションの概要と参考リソース

このセッションは「最新の運用プラクティスによる信頼性の向上」というラーニングパスの2番目に位置づけられるものでした。 OPS10 ~ OPS50 までの計5つのセッションを続けて聞くと体系的に理解できるという設計になっています。

ラーニングパスセッションは、アメリカで行われたセッションを「Tour」各地でローカライズして行うもので、オリジナルのスライド・コンテンツの内容は変えずに行うという制約がありました(翻訳はOK)。

各地のスピーカーや、そのセッションを聴いた方が自分でも試してみたい場合に備え、デモ環境作成方法やプレゼンテーション内容・アメリカのオリジナル版のセッション動画などすべて公開されています。

OPS20「インシデントに対応する」に関するリソースは以下です。

セッション動画&リンク

techcommunity.microsoft.com

GitHub リポジトリ

github.com

なお、環境作成やデモに関してはリポジトリに書いてあるとおりに勧めれば基本的にはいけるのですが、一部ハマりポイント?がありちょっと苦戦したので、そのあたりは別途記事を上げる予定です(+本家にフィードバック予定)。

日本語スライド

大阪での登壇で使用したセッション資料です。

www.slideshare.net

こちら、今回の大阪のために翻訳しました。
気を付けた点としては、

  • スペースが許す限りなるべくオリジナルの英語記述を残した(誤訳怖い)
  • 用語の訳し方を一部『入門 監視』に合わせた(特に前半)。

あたりです。

書籍『入門 監視』はセッション内でも紹介しましたが、役割の部分など訳に困った部分で本の前半に非常に似た章があり、助かりました。

www.oreilly.co.jp

セッション構成と内容

オリジナルのリポジトリにも時間配分は書いてありますが、以下のような感じです。

  • Section1
    • 導入&インシデント対応の基礎(16分)
    • Demo1(7分)
  • Section2
    • 修復の改善(6分)
    • Demo2(5分)
  • Section3
    • TTR 短縮のためのツール(1分)
    • Demo3(7分)
  • Section4
    • まとめ・クロージング(2分)

いやー、正直時間足りないです…(笑)
説明もパワポのノート欄にびっしり台本が書いてあって、ほんとにこれ時間通りに終わるの?っていう盛りだくさんな内容。
前日までに何度も通しでしゃべって、なんとか切り詰めた…ってかんじです。

では、以下ざっくりと内容です。

Section1:導入&インシデント対応の基礎

  • しゃべったこと:ラーニングパスのひとつ前・OPS10 からのつながりとインシデント対応に関する前提知識。
  • デモ:インシデント発生をトリガーとしたかんばんチケット・コミュニケーションチャネル作成の自動化
  • 技術要素:Azure Logic Apps, Azure Boards, Azure Table Storage, Microsoft Teams, Adaptive Cards

f:id:himanago:20200131195833p:plain

毎年、「DevOps Research and Assessment」という組織が「State of DevOps」というレポートを出していて、最新の2019年のレポートで紹介された数字について。

1時間以内にサービスの中断を検出、対応、および修正できるチームを「エリート/ハイパフォーマー」として分類。
真ん中のレベルに分類されるチームは、24時間以内にインシデントから回復し、「ローパフォーマー」は、サービスの中断から回復するまでに1週間から1か月かかる。

f:id:himanago:20200131195541p:plain

エリート/ハイパフォーマーは、ローパフォーマーよりも2,600倍以上速くインシデントから回復していて、本番環境へのデプロイも200倍以上多く行っているというデータが出ています。

インシデントとは

今回のセッションはインシデント対応がメインのテーマですが、まずインシデントについて考えてみます。

f:id:himanago:20200131200235p:plain

インシデントが「サービスの中断」であることは問題ないですが、ユーザーが依存しているサービスが止まるとユーザーの仕事や生活に影響してしまいます。

「恐れられ回避される」とは

  • 停止の重要性を軽視
  • 意図的にラベルを違うものに
  • 責任逃れのためサービス中断を報告しない

などがあり、きちんとした体制がない場合こういったことが起こりえますが、最近は DevOps や SRE といった考え方から、こういったことが起こらないよう、インシデントについて改めて考えるようになってきています。

またインシデントは「主観的」なとらえ方をされがちで、さまざまな組織や業界によって、何がインシデントなのかという認識はばらばらです。顧客が影響を受けたときだけなのか、そうではないシステム障害も含むのか。主観的に扱われることは、インシデントの重大度レベルを識別する際にも問題となります。

最後に、エンジニアが行うことのほとんどは計画された仕事ですが、インシデント対応は違います。インシデントに関する作業は予定外です。多くの場合、悪いことと見なされがちですが、ちょっと立ち止まって考えてみると、これはエンドユーザーに価値を提供するための「投資」でもあると捉えられるのかもしれません。

インシデントはシステムの "鼓動"

f:id:himanago:20200131200946p:plain

インシデントは「システムの鼓動・心拍」と考えるとよい、といわれ、心拍たるインシデントは、システムについて多くのことを教えてくれます。

強力な監視基盤がある場合(やシステムで何が起こっているか詳細を知っている場合)には、多くのアラート、インシデント、および対応の機会を生成しますが、そうすると、常にシステムで何が起こっているのかがわかるようになります。

そのすべては、インシデントの最初のフェーズである検知・検出の一部です。

f:id:himanago:20200131201129p:plain

これはひとつ前の OPS10 で詳しく説明があった箇所ですが、このセッションは、アラートを受信したあとの、対応と修復が主題です。

Tailwind Traiders の課題

架空のオンライン小売業者 Tailwind Traders は、Azure 上に構築されたかっこいい EC サイトを持っていますが、インシデントに関して次のような課題に直面しています。

f:id:himanago:20200131201244p:plain

2つ目の原文に「reactionary」とあり、これは訳に迷ったのですが(スライドには直訳を記載)、ニュアンス的には「反射的」とか「ただ反応するだけ」とかの意味になります。

これらは、Tailwind Traders に限らず、私たちもよく直面する課題ではないでしょうか?

インシデント対応の体制・役割・ローテーション

体制と役割

インシデント対応のチームは、必ず複数のエンジニアで構成します。誰かひとりに責任が集中したりしてはいけません。

チームは、以下のような役割のエンジニアで構成します。

f:id:himanago:20200131233505p:plain

プライマリ・レスポンダー

いつでも連絡がとれる、「オンコール」なエンジニア。

セカンダリ・レスポンダー

プライマリ・レスポンダーのバックアップです。
プライマリ・レスポンダーに連絡がつかない場合や、そのサポートを行います。

専門家(SME

専門家は常に連絡が取れるべきというわけではありませんが、問題の分野ごとに特定しておく必要はあります。
たとえばデータベースの専門家、フロントエンドの専門家など。
プライマリレスポンダーとセカンダリレスポンダーが自分で問題を診断・解決できない場合に連絡できる人がいると安心できます。

現場指揮官(インシデント・コマンダー

大規模な停止が発生した場合に活躍する統括役。多くの異なるコンポーネント、異なるシステム、チーム間での調整が必要です。 エンジニアが問題解決に集中し、人々がぶつかったり、作業を中断したりしないようにします。 そのためインシデント・コマンダーは何が起こっているのか、誰が何をしているのかを把握しなければなりません。

書記(スクライブ)

会話をできる限り詳細に文書化する役割です。
インシデント対応では、電話やビデオチャットを使用して問題解決のためのコミュニケーションを行うことがありますが、文字に起こさない限り何を言って何をしていたかを詳細に追うことができません(録音でも追いづらいので文書化必須でしょう)。
対応中に振り返ったり、対応後に分析したりすることはインシデント対応において重要です。

コミュニケーション・コーディネーター

インシデント・コマンダーと連携してより多くの情報を共有する人です。インシデント対応を行うエンジニアが持つ以上の情報を、顧客や、カスタマーサポート、販売チーム、マーケティングチームなど外部の人々がもつことがあります。
インシデントやシステムを取り巻く状況や状況を認識するため、コミュニケーションの管理を担当し、他の利害関係者が何が起こっているのか、何が行われているのかを確実に把握できるようにします。

ローテーション

f:id:himanago:20200131234423p:plain

ローテーションは、シフトで管理されます。
エンジニアは、特定の定期的なローテーションに対して「オンコール」の責任を負います。

作成できるシフトにはさまざまな種類があり、「24時間 x 7日間(全曜日)」をローテーションするものや、大きな会社なら "太陽のシフト" に従うローテーションシフトを組めます。これは異なるタイムゾーンでエンジニアが引き継ぐことで、自分の勤務時間だけオンコールであればよくなる、というものです。そうはいっても週末に関しては柔軟性を求められるので、うまくシフトのカスタマイズをすることが必要です。

このあたりは書籍『入門 監視』の前半に詳しいので、あわせて読んでみていただくとよいかと思います。


長くなったので、いったんここで切ります。

次の記事では、Section 1 のデモ(アラートをトリガーとして動く Logic Apps と Teams の連携)について書いていきます。

Logic App から Azure AD のユーザー追加を行う

Azure ユーザーの追加処理を Logic Apps で作ったのでメモ。

Azure AD の ユーザー作成&グループ追加

Azure AD のアクションは、Logic App のデザイナーで「Azure AD」と検索すれば出てきます。 以下のように細かくアクションを用意してくれています。

f:id:himanago:20200106143536p:plain

ユーザー追加のほかにもいろいろありますが、今回はユーザーを作ったあとに指定したグループに入れてみます。
フローはこんなかんじ。Logic Apps なら(たとえば HTTP リクエストで受け取った)パラメータを使ったり、作成した直後のユーザーを参照したりすることも簡単です。

f:id:himanago:20200106150809p:plain

アクションの実行に使用するユーザーとロール

Logic App で Azure AD の操作をする際、そのアクションを動かす Azure アカウントを指定する必要があり、作成時にサインインする必要があります。

f:id:himanago:20200106145040p:plain

ここで使用するアカウントは普段使っているアカウントではなく、Logic App から使う専用アカウントを作っておくほうがセキュリティ・運用的によいと思います。

Azure AD を操作するための権限が必要なので、適切なロールを付与しておかなければなりません。ユーザー作成とグループへの追加は、「ユーザー管理者」をつけておけば動きました。

f:id:himanago:20200106145705p:plain

ちなみに成功すると、その接続情報が Logic App と同じリソースグループに増えてます。

f:id:himanago:20200106145813p:plain

おわりに

Logic Apps を使うとAzure AD の操作もデザイナーをポチポチするだけで実現できるので便利ですね。

【Uno Platform】WebAssembly 実行時の JavaScript 相互運用

Uno Platform は、UWP を iOS/Android や WebAssembly で動くようにするものですが、WebAssembly で Uno を動かすときには、C#/XAML で足りない部分を補ったり、既存の JavaScript ライブラリ/コードを併用したりするために、C#JavaScript を相互に呼び合いたい場面が出てきます。

同じく C# で WebAssembly を実現する Blazor でも JavaScript との相互運用機能 が用意されていますが、Uno Platform でもできるようなので試してみました。

JavaScript ファイルの使用

Wasm のプロジェクトにある WasmScripts フォルダに js ファイルを「埋め込みリソース」として配置すれば、読み込んでくれます。既存のライブラリなどを読み込むこともできますね。

f:id:himanago:20191229120655p:plain

C# コードから JavaScript の呼び出し

JavaScript を配置できるとしても、それを C# 側から呼べないと意味がありません。
C# から呼ぶには、Uno.Foundation 名前空間にある WebAssemblyRuntime.InvokeJS メソッドを使います。

さきほどの JavaScript ファイルの中身に、こんなかんじの適当な関数を定義しておきます。

function sayHello() {
    alert('Hello from JS!');
}

XAML にボタンを一つだけ置いて、コードビハインドを以下のように書いてみます。
if ディレクティブで WASM のときだけ JavaScript 呼び出しで、その他は普通に MessageDialog です。

using System;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#if __WASM__
using Uno.Foundation;
#endif

namespace UnoJavaScriptInteropSample
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void callJsButton_Click(object sender, RoutedEventArgs e)
        {
#if __WASM__
            WebAssemblyRuntime.InvokeJS("sayHello();");
#else
            await new MessageDialog("Hello from MessageDialog").ShowAsync();
#endif
        }
    }
}

UWP だとこうなりますが、

f:id:himanago:20191229145334p:plain

WebAssembly で実行するとこのようになります。

f:id:himanago:20191229144325p:plain

ちゃんと JavaScript コードが呼ばれていますね。

特定要素に対する処理を JS にやらせる

JavaScript のコードを書く際、C#/XAML で生成した特定のコントロールJavaScript に操作させたくなります。

そのために使えるのがコントロールHtmlId プロパティで、生成された HTML 要素の id 属性として使われる値がとれます。これを JavaScript に渡せば特定のコントロールに対する DOM 操作などを簡単に書けます。

たとえば、TextBoxIsReadOnly プロパティは WebAssembly では対応しておらず、True にしても UWP では当然読み取り専用になるので、これと WebAssembly の動きをそろえたい…という場合に以下のようにテキストボックスを readonly にする関数に対し id を渡すよう、以下のように実装すれば対応可能です。

#if __WASM__
        protected override void OnLoaded()
        {
            WebAssemblyRuntime.InvokeJS($"toReadonly({textBox.HtmlId});");
        }
#endif

テキストボックスを readonly にする関数のほうの実装ですが、C# 側の TextBox の HtmlId でとれる値が、実は input 要素のものではなく、それを囲む外側の div である点を考慮する必要があります。

Uno Platform では、UWP のコントロールと対応する見た目の HTML 要素を WebAssembly で生成していますが、結果的に生成されるのが単一のタグで構成されたものであるとは限りません。そのため、一度どのようなタグの要素が生成されるのかを確認してから実装をする必要があります(そのため JavaScript では以下のように2段階で input 要素を探す)。

function toReadonly(id) {
    const box = document.getElementById(id);
    box.getElementsByTagName('input')[0].setAttribute('readonly', 'readonly');
}

イベントの連携

あとおもしろいのはイベントです。以下のドキュメントにも記載がありますが、この機能は JavaScript 側から dispatchEvent を実行することで C# 側に用意したイベントハンドラを呼び出すことができます。

(Wasm) Handling custom HTML events

C# で using に Uno.Extensions を追加して、RegisterHtmlEventHandler/RegisterHtmlCustomEventHandler すると、そこで登録したイベントハンドラJavaScriptdispatchEvent を受け取れます。

気を付けたいのは、JavaScript 側から dispatchEvent する要素と、C# 側で RegisterHtmlEventHandler/RegisterHtmlCustomEventHandler するコントロールを合わせなければならないということです。これも HTML 側でどんな要素が対応しているか確認してしっかり一致させておかないとうまく動きません。

下記のコードは、テキストボックスのダブルクリックで C# 側のイベントを呼ぶ処理です。

外側の div 要素と C#/XAML 側の TextBox コントロールが一致するので、テキストボックスをダブルクリックしてイベントを走らせたければ、JavaScript で input 要素のダブルクリックイベントの処理内で、外側の div から dispatchEvent するのがポイントです。

#if __WASM__
        protected override void OnLoaded()
        {
            WebAssemblyRuntime.InvokeJS($"addDblclickEventListener({textBox.HtmlId});");
            textBox.RegisterHtmlCustomEventHandler("dblclickEvent", OnDblclickEvent, isDetailJson: false);
        }

        private async void OnDblclickEvent(object sender, HtmlCustomEventArgs e)
        {
            await new MessageDialog(e.Detail).ShowAsync();
        }
#endif
function addDblclickEventListener(id) {
    const box = document.getElementById(id);
    const tb = box.getElementsByTagName('input')[0];

    tb.addEventListener('dblclick', (e) => {
        box.dispatchEvent(new CustomEvent("dblclickEvent", { detail: e.target.id }));
    });
}

JavaScript から C# 側に文字列や JSON を渡すことができるので、わりと幅広いことができそうです。

まとめ

Uno における C#JavaScript の相互運用をみてみました。イベントの連携などでいろいろできそうですが、あまり複雑なことをしすぎるとコードがごちゃごちゃしてしまいますし、せっかく C#クロスプラットフォーム対応できるという利点が薄れてしまうので多用しすぎないように注意したいところです。

上記検証に使ったサンプルは下記に置きました。

GitHub - himanago/UnoJavaScriptInteropSample

#技術書典 8 にて「牛めし警察で学ぶ Uno Platform」を頒布します!

ひらりん, ちょまど, かずき」というサークル名で、 技術書典8 (IT や機械工作などの技術書を専門とする同人誌即売会) で本を出します!

techbookfest.org

サークル名の由来

f:id:himanago:20191219232136j:plain

ご覧のとおりです!私(ひらりん)と、Microsoftちょまどさん、かずきさん。
※順番は、サークル加入順

いつも本当にお世話になっているお二人と、前回に続き一緒にサークルできるのがとても楽しみです。
競争率の高い技術書典、無事に当選しとてもうれしいです。

サークルカット

仮ですが、すでにちょまどさんに C# ちゃんのサークルカットを用意してもらっています!
背景の C# コードが、実はかずきさんがささっと書いたもの(8.0 の新機能 をちゃんと入れている!)だったりします。

f:id:himanago:20191219234234j:plain

技術書典8 について

今回は2日間開催ですが、その2日目である 3/1(日) に参加します!

techbookfest.org

経緯

前回の技術書典7 で、個人で参加した際にお二人に手伝っていただきました。

3人でサークルを回したのがとても楽しかったので、今度は書くところから3人で参加したい!と、今回のサークル参加が実現しました。

どんな本?

牛めし警察で学ぶ Uno Platform』と題し、最近話題の「Uno Platform」を使って、ちょまどさんの伝説のアプリ「松屋警察」をリメイクします!

Uno Platform は、デスクトップアプリ(UWP)、モバイルアプリ(Android/iOS)、Web アプリ(WebAssembly)として動くものが C#XAML で作れるいま注目のフレームワークです。

platform.uno

オリジナルは Xamarin.Forms で Custom Vision の画像判定(松屋牛めし吉野家の牛丼を見分ける)を行うモバイルアプリでしたが、今回は Uno Platform でそれを Webアプリとしても動くようにします。

この本では、Uno Platform の概要から、それを使ってアプリを作るところまで一通り解説していく予定です。
ちょまどさんには表紙や挿絵でかわいらしいイラストを描いていただきつつ、アプリデザインも担当いただきます!
そして、かずきさんには圧倒的な技術力と安定感でコードや本文の記述の正確性・質を高めてくださることでしょう。

もうこれだけで、楽しくて役に立つ本になること間違いなしだと思っています。
(ちなみに私は実際のアプリを組み立てていく部分の執筆を担当する予定です!)

メンバーよりひとこと

ひらりん (@himarin269)

大尊敬するお二人とまたご一緒できて、そしてその成果をみなさまにお届けできることとなりとてもうれしいです。
扱うテーマも Uno Platform というとても Hot な技術なので、その楽しさ・すばらしさをたくさん学んで、多くの方に伝えられたらなと思います。
牛めし警察で学ぶ Uno Platform』、どうぞよろしくお願いします!

ちょまどさん (@chomado)

貴重な機会をたまわりまして、誠にありがとうございます。
同人誌を書くのは数年ぶりとなり、大変嬉しく思います。
3人で楽しく本を書いて (描いて) まいりますので、皆さまと 3/1 (日) 技術書典 day 2 でお会いできるのを楽しみにしております!
(゚▽゚ っ)З

かずきさん (@okazuki)

Uno Platform は C# + XAMLVisual Studio による強力なツールサポートをフル活用して UWP, Android, iOS, WebAssembly のアプリを開発出来る凄いプラットフォームです。 少し Uno Platform のソースコードを見たのですが非常に頑張っててソースコードの内容でも好感を持ちました。面白そうなテクノロジーなので興味がありましたら頑張って書くので是非お手に取ってみてください。


みなさんに楽しい本をお届けできるよう、これから 3人で頑張って書いていきます!
3/1(日) 、技術書典8(2日目)でお会いしましょう!よろしくお願いします!!

Azure Functions の DI でエラー

この記事は Microsoft Azure Advent Calendar 2019 の 17日目の記事…ではありません(笑)

qiita.com

アドカレ記事は別途書いてます。今日中には上げます!

[追記] 書きました↓
Durable Functions を使ってコードのみでアンケート回答に便利な LINE Bot を簡単につくる - Qiita

で、この記事は、アドカレ記事を書いていたときに遭遇したエラーについてのメモです。

エラーの内容

前提としては、

  • Azure Functions で LINE Bot を作っていた。
  • Startup で Messaging API SDK 関連のクラスを2つ DI しようとした

という状況。

以下のように Startup を書いたら…

using System;
using LineDC.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))]
namespace Sample.LineBot
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton<IDurableWebhookApplication, EnqBotApp>(_ =>
                new EnqBotApp(LineMessagingClient.Create(
                    Environment.GetEnvironmentVariable("ChannelAccessToken")),
                    Environment.GetEnvironmentVariable("ChannelSecret")));
        }
    }
}

なぜかこんなエラーが。

2019-12-17T03:21:41.080 [Error] Executed 'HttpStart' (Failed, Id=5cf2c546-9406-4870-89cc-bae17e4e4e5d)
System.InvalidOperationException : Unable to resolve service for type 'Sample.LineBot.EnqBotApp' while attempting to activate 'Sample.LineBot.DurableEnqFunctions'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp,Type type,Type requiredBy,Boolean isDefaultParameterRequired)
   at lambda_method(Closure ,IServiceProvider ,Object[] )
   at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IServiceProvider serviceProvider) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs : 37
   at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs : 32
   at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.<>c__DisplayClass1_1.<.ctor>b__0(IFunctionInstanceEx i) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs : 20
   at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.Create(IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs : 26
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.CreateInstance(IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs : 44
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ParameterHelper.Initialize() at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs : 846
   at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.TryExecuteAsyncCore(IFunctionInstanceEx functionInstance,CancellationToken cancellationToken) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs : 116

メッセージで調べるといろいろ出てくるのですが、根本的な原因・解決策はすぐにはわからず。

解決策

とりあえずいろいろやってみましたが、以下のように記述を変えたらエラーが出なくなることがわかりました。

using System;
using LineDC.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))]
namespace Sample.LineBot
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var client = LineMessagingClient.Create(Environment.GetEnvironmentVariable("ChannelAccessToken"));

            builder.Services
                .AddSingleton<ILineMessagingClient>(_ => client)
                .AddSingleton<EnqBotApp>(_ => new EnqBotApp(client, Environment.GetEnvironmentVariable("ChannelSecret")));
        }
    }
}

このエラーが出たら DI のしかたを見直してみるといいのかも。

※ 追記 ※

WebhookApplication クラスのスコープがまずかったので修正。

using System;
using LineDC.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))]
namespace Sample.LineBot
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services
                .AddSingleton<ILineMessagingClient>(_ => LineMessagingClient.Create(Environment.GetEnvironmentVariable("ChannelAccessToken")))
                .AddTransient<EnqBotApp>(s => new EnqBotApp(s.GetService<ILineMessagingClient>(), Environment.GetEnvironmentVariable("ChannelSecret")));
        }
    }
}

今回またかずきさんに DI に関するご指摘をいただきました(技術書典7 に続き3か月ぶり2回目)。 s.GetService<T>() で DI コンテナに登録しているサービスをとれるとのことも教えていただいた。知らなかった。

DI まわりはちゃんと勉強して毎回ちゃんとスコープに気を付けないとあぶないですね。

Microsoft Open Tech Night #1 で LT をしました

少し間があいてしまいましたが、以下のイベントで LT しました。

msdevjp.connpass.com

タイトルは「Azure Application Gatewayでオンプレ DMZクラウドへ拡張する」。

勉強会のテーマにあわせ、いつもと違ってインフラ系での登壇。

ちょうど業務でやっている外部公開な Web アプリの Azure 移行についての話をしました。

内容

L7 ロードバランサの Application Gateway を使って、オンプレ DMZ にある複数のアプリを段階的に移行していく、という話です。

まず Application Gateway を立てて f:id:himanago:20191210233814p:plain

バックエンドプールに Web App 追加 f:id:himanago:20191210233852p:plain

アプリをひとつずつ移行 f:id:himanago:20191210233911p:plain

という流れで移行します。実際に業務でやっている内容なので、けっこう試行錯誤したところも多かったのですが、当日のツイートで

というのがあり、「おなじことやっている方がいたのかー!」とうれしかったのと同時に、「MVP になると答え合わせの対象として見られるのか…」とちょっとドキッとしました。

いままで以上に、下手なことは言えないな…と思ったので、これからもっと技術にきちんと向き合っていかなければ、とあらためて思いました。

資料

当日の資料はこちらです。

www.slideshare.net

雑記

今回のイベントは代官山に新しくできた「Azure Daikanyama Base」という施設で行われました。はじめての施設でしたが、相性が悪かったのか何なのか、なぜかどうやっても自分の PC が Wi-Fi につながらず。

Zoom での配信もあったため、登壇者のネット接続はマスト。しかたなく、おなじくメイン登壇のひとりだったちょまどさんの Surface Go を借りて登壇しました。

で、そこで問題が。発表者席は教壇的なものではなく、ふつうの椅子に座って使う机だったので、立ちでしゃべると Surface Go の画面が小さすぎて見えない!
そのため発表はだいぶ手間取り、ぎこちないかんじになってしまいました。。

立ちで発表するこだわりは捨てて、いっそ座っちゃえばよかったかも。ちょっと反省。

とりあえず LT はなんとかなったのでよかったんですが、この Azure Daikanyama Base、これから先ちょくちょく利用することになるはずのところ、今後ずっとネットにつながらないままなんだろうか。。