落とし穴に立て札を立てるが如く

ハマりどころの解決が少しでも早くなることを願って書いていきます

【小ネタ】PowershellでHashTableにHashTableを足す

f:id:nam_yu_sql:20210328183448j:plain

はじめに

最近Azure Pipelineとその縁でPowershellと仲良しこよしになっているなむゆです。
HashTableを扱うにあたって、HashTableに一つ一つのkeyvalueを追加する方法はどこでも解説されているのですが、HashTableにHashTableを足し合わせる方法についてはそこそこ調べても出てこなかったのでメモ書きです。

HashTableにHashTableを足し合わせる

$aと$bはどちらもHashTable型ですが、HashTableは以下のように + 演算子を使うことでHashTable同士を足し合わせることができます。

$a = @{  
    a="name a"  
    b="name b"  
}  
$b =@{  
    c="name c"  
    d="desperately"  
}  
  
$c= $a+$b  
  
# 出力  
# Name                           Value  
# ----                           -----  
# d                              desperately  
# a                              name a  
# c                              name c  
# b                              name b  

ただし、キーの値が被っているとエラーとなります。
以下の例だと、c のキーが重複するため、エラーとなります。

$a = @{  
    a="name a"  
    b="name b"  
    c="name c"  
}  
$b =@{  
    c="name c"  
    d="desperately"  
}  
  
$c= $a+$b  
  
# 出力  
# 項目は既に追加されています。辞書のキー: 'c'  追加されるキー:'c'  
# 発生場所 行:12 文字:1  
# + $c= $a+$b  
# + ~~~~~~~~~  
#     + CategoryInfo          : OperationStopped: (:) [], ArgumentException  
#     + FullyQualifiedErrorId : System.ArgumentException  

おわりに

HashTableにHashTableを足し合わせる方法についてはそこそこ調べても出てこなかった・・・と思って今調べたら「ハッシュテーブル同士 加算」あたりで色々出てきました。
ドキュメントにも記述にありますね。
「足し合わせる」よりも「加算」の方が言葉として正確でヒットしやすかったのかもしれません。
Powershellの「~について知りたいことのすべて」シリーズにはいつもお世話になっています。

参考

Azureパイプライン実行の流れをまとめてみる

f:id:nam_yu_sql:20210322063550j:plain この記事はcloud.config tech blogにもマルチポストしています。

tech-blog.cloud-config.jp

はじめに

パイプライン、回してますか?
今回もAzure Pipelineのネタで一席打ちます。
Azure Pipelineを回すとき、普段はなんとなしに「あー回ってんなーうわ止まったなんで」と眺めているのですが、Expressionの話を調べているとExpressionはコンパイルされるタイミングやタスクの開始時に評価されていることが分かり、「あーパイプラインも後ろでは順を追って回ってるんだなー」等考えるようになりました。
なので今回は、Azure Pipelineの実行の流れを調べたので、それを独断と偏見で重要そうなところをある程度かいつまんでまとめてみたいと思います。

Azure Pipelineのパイプライン実行の流れ

パイプライン実行の流れは大きく分けて「実行の最初に行われること」「stage単位で行われること」「job単位で行われること」「step単位で行われること」に分けられます。
stage,job,stepはYAML schema referenceで解説されているとおりの処理の単位で、stage、job、stepの順に細かくなっていきます。
しかし、これらの単位ごとの役割は単純に処理を括るだけのものではないのです。
順に見ていきましょう。

まず最初に

Azure DevOpsのパイプラインの実行APIを叩いたり、あるいはAzure DevOps上からパイプラインを実行したりした際、stepやjob等の手順に従って処理を始める前に、いくつかの前処理が走ります。
まず最初に、templateとtemplate expressionを処理します。
Templateとは大雑把に言うとパイプラインの一部を別のyamlファイルとして切り出すことで複数のパイプラインで使いまわせるようにする便利な仕組みなのですが、詳しくはまた今度解説したいと思います。
template expressionについては以前の記事で解説したとおりで、別名Compile time expressionと呼ばれ、つまるところパイプラインが回り始めるこのタイミングでyamlの文字列から実際の値に置き換わります。
それらが終わってから、一つ目のstageの処理に入ります。

stage単位での処理

stageは一番最初のものから順番に処理されていきます。
それぞれのstageの処理が始まるタイミングで、そのstageで使用するリソースが使える状態か、また、それらを利用する権限があるかをチェックします。
例えば、特定のazure リソースに接続するためのservice connectionで本当に対象のリソースにアクセスできるかをチェックします。
stageが始まるタイミングでこの処理が走るので、リソース名や権限関連の情報をvariableとして指定したい場合は、stageよりも上の層で指定する必要があります。そしてそうなると、runtime parameterで指定するか、あるいはpipeline層のvariable出しかこれらは設定できないことになります。
何か処理を行ってその結果で特定の場面で使用する認証情報を変えたい場合もあるかもしれませんが、その場合はstageを区切ってstage dapendenciesの仕組みを使って前のstageから認証情報を渡してやる必要があります。

一つのstageが終わると、前のstageの処理が成功していれば次のstageに移り、またはじめに戻って処理を始めます。
デフォルトでは前のstageが失敗していると残りのstageは実行されず、パイプライン全体として失敗とみなされます。

job単位での処理

jobは、agent単位で実行される処理の単位です。
一つのstageの中に複数のjobがあった場合、それらはそれぞれ別のagentで並列に実行されます。
stage内では、それぞれのjobのdependson等の値から、最初に実行できるjobを選び、それから実行していきます。
行うjobが決まると、まずはそのjobで使用するagentを決めます。yamlの中では pool: の部分を参照し、どの種類のagentを使用するか決めます。
agentはそれぞれの処理を物理的に行うもので、実体はself-hostedにしろMicrosoft-hostedにしろVMです。
ユーザーが構成したVMを使う場合をself-hostedと呼んだり、microsoftが作成したテンプレ構成のものを使う場合はMicrosoft-hostedと呼んだりする形です。
agentについては以前の記事で触れているのでそちらも見てみてください。
使用できるagentがあればそれを使用して処理を始めますが、無ければ使えるagentが出てくるまでこの状態で待ちになります。
使用するagentが確保できれば、そのagentにjobで行う処理の内容を送信し、それぞれのtaskで指定された処理を始めます。

そのjobが終わったら、他のjobのdependsOnやconditionから次に行うjobを決定し、実行します。
なお、job毎にagentを確保するという点ですが、これは別のjobに移るごとにそのagentのVMの中身がリセットされるということでもあります。
なので、生成したファイルに依存する処理は、できる限り同じjobに含めるようにしましょう。
あるいは、blob storageなどの共通で使える外部リソースを利用してjob間でファイルを受け渡す必要があります。

step単位での処理

stepはjobとは違い、上から順に処理されていきます。
ここまでくるとyamlに記述された個別のtaskを実行するのみで、事前準備として特筆することはあまりありません。
ちなみに、Azure Pipelineのタスクはこちらで纏められているとおり多種多様なのですが、個々のagentで行われる実体としてはpowershellコマンドかNode.jsの形になるようです。これは豆ですね。
また、jobの間ではマシン内に保存したファイルは引き継がれないという話をしましたが、task間では同じマシンを連続で使うためファイルが保持されます。

一つのstepが終わると、その結果を記録してから次のstepを開始します。
しかし、jobの中で複数のstepを順番に実行しているときに一つでも失敗した場合、そのjobの中のそれ以降のstepはデフォルトでは実行されなくなります。
conditionでalways()を指定しているstepは例外的に実行されるので、処理が途中で一度転んでも実行したいような処理(例えば変更のクリーンアップとか)は最後のstepとしてalways()の設定をして実行するといいです。

おわりに

今回はAzure Pipelineのパイプライン実行の流れについて纏めて共有しました。
yamlを書いて回せば回る」くらいの認識でもAzure Pipelineを運用することはできますが、その後ろで起きていることを知ることでその仕組みについてもう少し理解を深めることができるかと思います。
Azure Pipelineを極めて最強のDevOpsエンジニアを目指しましょう。

参考

Azure Identity SDKのDefaultAzureCredentialでローカルデバッグ時に詰まった話

f:id:nam_yu_sql:20210318072751j:plain この記事はcloud.config tech blogにもマルチポストしています。

tech-blog.cloud-config.jp

はじめに

Azure DevOpsとバックエンドを反復横跳びしているなむゆです。
最近はAzureのサービスとの絡みも増えて、インプットが増えている気がします。嬉しいですね。
今回はそんなAzureのサービスと絡む.NETアプリ開発の中で一つ問題が起きて七転八倒したお話を共有します。

Azure Identity SDKとは

Azure Identity SDKとは、Azure Active Directoryを用いたトークン認証をサポートするSDKです。
他のAzure SDKを用いてAzureリソースにアクセスしようとする時の認証に使います。
このSDKに含まれるものの中でも特に優秀なのが「DefaultAzureCredentials」というクラスで、これはこのクラスのインスタンスを渡しておくだけでAzure環境上であればそのアプリがデプロイされたリソースのマネージドIDを用いて認証を行ってくれます。
さらに、ローカル環境でデバッグする際はその環境のVisual StudioVisual Studio Code、あるいはコマンドコンソールでaz loginした際の認証情報を使って認証を行ってくれるので、Azure環境でしか動かないということもありません。
オンプレ環境であっても環境変数を見てくれるので死角なしです。
どうしてそうなっているかというと、このクラスの動作としてはその環境の認証情報がある場所を順に探索し、見つかればそれを使うというロジックになっているためです。
具体的な順番はDefaultAzureCredentialに書いてありますが、例えば一番最初は環境変数を確認しに行くのですがそこに必要な認証情報がなければ次はマネージドIDを探しに行きます。
それでも見つからなければ次は使用しているVisual Studioの認証情報を・・・といったように順に探しに行き、どこかで認証情報が見つかればそれを使う形になっています。
さらに、認証情報をappsettings等の環境情報として管理しなくてよくなるというメリットもあります。
ローカルではvisual studioやazure cliでログインしていれば、Azure上ではデプロイ先のリソースのマネージドIDを設定しておけばいいので、環境ごとに環境情報ファイルを書き換えて保存する手間が省けます。
今回も、それを用いて実装しようとしていました。

どんなエラーが起きたか

まずはローカルでデバッグだというわけで認証情報取得順の中ではAzure CLIの認証情報を使いたいため、別コンソールでaz loginしました。
.NETアプリケーションからAzure Keyvaultの接続する際、認証情報の取得をAzure Identity SDKのDefaultAzureCredentialクラスを使用して行おうとしました。
サンプルのコードはこちらで、大体そのサンプルコードの通りに実装してデバッグしてみました。
するとkeyvaultアクセスを行っている行で例外が発生しました。
以下がその例外のメッセージです。

Service request failed.  
Status: 401 (Unauthorized)  
  
Content:  
{"error":{"code":"Unauthorized","message":"AKV10032: Invalid issuer. Expected one of (keyvaultの属するテナントIDがいくつか入る), found (Visual StudioでログインしているユーザーのテナントID))"}}  
  
Headers:  
Cache-Control: no-cache  
Pragma: no-cache  
WWW-Authenticate: REDACTED  
x-ms-keyvault-region: (どこかのリージョン)  
x-ms-client-request-id: (clientIdが入っている)  
x-ms-request-id: REDACTED  
x-ms-keyvault-service-version: 1.2.205.0  
x-ms-keyvault-network-info: conn_type=Ipv4;addr=(IPアドレス);act_addr_fam=InterNetwork;  
X-Powered-By: REDACTED  
Strict-Transport-Security: REDACTED  
X-Content-Type-Options: REDACTED  
Date: (リクエストした時間)  
Content-Length: 345  
Content-Type: application/json; charset=utf-8  
Expires: -1  
  

エラーを見る限りkeyvaultにアクセスできる認証情報ではない認証情報が使われていました。
Azure CLIでログインした際の認証情報でもありません。
Visual Studio Enterprise関連の認証情報です。<- 大ヒント

原因

本当は話を引っ張ろうとも思ったのですが無理です。
前述のDefaultAzureCredential()のロジックとしてAzure CLIで取得した認証情報より先にVisual Studioでログインした情報を使用して認証しようとしていました。
この認証情報では目標のkeyvaultにはアクセスできないため、認証できないとしてエラーになっていました。

解決方法

「じゃあ途中の使いたくない認証情報を探しに行くのをスキップすればいいじゃん」という発想で「DefaultAzureCredential Exclude VisualStudio」等で調べたところ・・・ありました
DefaultAzureCredentialクラスをインスタンス化する際にOptionsとして ExcludeVisualStudioCredential = true を渡しておくことで、認証情報を探すフローからvisual studioのものを外してくれます。
他に、TokenCacheというらしいものもフローに含まれているらしい(ドキュメントに記載なし)ので、これも除外するようにした以下のコードにしたところAzure CLIの認証情報を使ってリソースにアクセスできました。

var client = new SecretClient(new Uri("KeyVaultのUrl"), new DefaultAzureCredential(new DefaultAzureCredentialOptions  
            {  
                ExcludeVisualStudioCredential = true,  
                ExcludeVisualStudioCodeCredential = true,  
                ExcludeSharedTokenCacheCredential = true  
            }  
        )  
    );  

おわりに

今回はAzureリソースにアクセスする際の認証情報の取得周りで少し詰まったことを共有しました。
同じ部分で詰まっている方の助けになれば幸いです~

参考

ややこssssssっし~なAzure PipelineのExpressionsのお話

f:id:nam_yu_sql:20210313234023j:plain この記事はcloud.config tech blogにもマルチポストしています。

tech-blog.cloud-config.jp

はじめに

世間ではいろんな人がトレーナーになって馬を走らせているようですがなむゆは相変わらずパイプラインを走らせています。あるいは回しています。
パイプラインを実行することの言い回しって「走る」と「回す」の二つを同じくらいよく聞くのですがこれを読んでいる人はパイプラインは走らせているのでしょうか、回しているのでしょうか気になる今日この頃です。

さて、今回はAzure Pipelineでパイプラインのyamlを書いていてしばらく混乱していたことがあるので整理がてら一席打ちたいと思います。
内容としてはAzure Pipelineのパイプライン定義yamlでよく書く$[]や${{}}、$()といったもの、いわゆるExpressionというものについてです。

Expressions

Expressionは直訳すると「式」になります。
C#ラムダ式(lambda-expression)とか言いますよね。
特殊な記法の式を書くことで、関数を簡単に書いたりなんやかんやするアレです。
で、Azure Pipelineにおいても特殊な記法をする「式」を用いて様々な操作を行うことがあります。
使い方のよくやる例としては以前の記事で共有したvariableやparameterの値の取得があります。
他にもラムダ式のような関数を定義する式も作れたりするのですが、個別の記法はExpressionsのドキュメントを参照してください。
さて、問題はというと、これらの記法は主に3つあり、どれも共通の機能の一つとしてパイプラインで設定した変数を扱うことができるのですが、それぞれ取得できる値や使いどころが微妙に違っていることです。
似たようなものなのでどれか一つの書き方で書こうとしていたら特定の場面では使えなかったりします。
なので、それぞれのExpressionの特徴を把握して使いどころを間違えないようになりましょう。

まず初めにAzure Pipelineで使用できる変数について復習

以前の記事でも取り上げたお話ですが、今回の話で重要なトピックなので最初に軽く解説していきます。
Expressionの使い分けの一つに「どのExpressionではどの変数が呼べるか」という点があるためです。

Azure Pipelineの機能として設定して使用できる変数はよく使うもので3つほどあります。
一つ目はパラメータです。これはランタイムパラメータとも呼ばれ、パイプラインの実行ごとに手動で指定してやるパラメータです。
パイプラインyamlでパラメータを設定しているとAzure Pipelineでパイプラインを実行する際にブランチ名のドロップダウンの下に入力欄が出てくるのでそこで指定します。

二つ目はvariableとして定義するstaticな変数です。これはパイプライン固有の固定値を設定するために用います。
root、stage、jobのレベルのそれぞれのスコープで指定することができ、変数名が被った場合はよりスコープが狭い方が優先されます。

三つ目は、variable groupとして設定する変数です。これはAzure DevopsのLibraryから設定する値で、Libraryからいつでも設定し直せます。じゃあparameterでいいようにも思えますが、parameter程実行ごとに変更するものではない値の設定や、また、variable groupにはsecret属性を持たせることもできるため、parameterとはまた違った目的で利用します。

それぞれの書き方は以下の通りです。
上から順にランタイムパラメータ、 group:の形で書かれているのはvariable groupの名前、 -namevalue に分かれているのがstaticのvariableです。

parameters:  
  - name: "sampleParam"  
    type: string  
    displayName: "SampleParameter"  
variables:  
  - group: SAMPLE_VARIABLE_GROUP  
  - name: SAMPLE_VARIABLE_VALUE  
    value: sample variable value  

${{}}について

${{}} の形で記述されるこのExpressionはTemplate expression、またはCompile time expressionといいます。
msdnExpressionsのページではCompile time expressionと呼ばれていますが、Define variablesのページではTemplate expressionと呼ばれています。実際はどちらも同じものです。
Compile timeと呼ばれるとおりAzure pipelineが回り始める下準備としてパイプラインyamlから実際の実行プランが作られる段階で、そのExpressionで計算された値(変数の呼び出しをするExpressionだったらその変数の値とか)と置き換えられます。
yamlから実行プランにコンパイルされる際に実際の値置き変えられるので、指定したスコープより内側であればyaml上のどこにでも書くことができます。
取得できる変数は、先程説明した3つの変数のうち固定値のvariableとruntime parameterです。
variable groupの値は取得できません。残念。
書き方としては以下のようになります。

${{ parameters.変数名 }} # runtime parameterから値を取りたい場合  
${{ varibales.変数名 }} # 固定値のvariableから値を取りたい場合  

$[]について

$[] の形で記述されるこのExpressionはRuntime expressionといいます。
こちらはCompile time expressionとは対照的に変数の値の評価のタイミング、つまりExpressionから実際の値に置き換えられるタイミングは処理の実行時になっています。
つまり、variableの値が以前のtask等で書き替えられた場合、そのExpressionが書かれている処理が行われるタイミングのvariableの値が使用されます。
別のタスクでvariableの値を書き換える例はこちらにあります。
このExpressionは使い方に癖があります。
Runtime expressionを使う際は、キーバリューペアの右側全体をカバーしなければならないのです!!!
キーバリューペアの右側全体をカバー・・・?意味が分からないですね。
これはvariableの解説ページのRuntime expression syntaxに書かれていることで、原文では

The runtime expression must take up the entire right side of a key-value pair.

とあります。
これが意味することとしては、例えばvariableの値としてruntime expressionを指定するときに以下のように書きますが・・・

  - name: SAMPLE_VARIABLE_VALUE  
    value: $[variables.sampleval]  

この時、 value: $[variables.sampleval]: を挟んだ右側全体をカバーしなければならないという意味です。なので、例えば以下のような書き方はできません。

  - name: SAMPLE_VARIABLE_VALUE  
    value: sampleval is $[variables.sampleval]  

そのexpression以外のものを : の右側に書くとエラーになってしまいます。
他の、例えばCompile time expressionは以下のような書き方ができます。

  - name: SAMPLE_VARIABLE_VALUE  
    value: sampleval is ${{ variables.sampleval }}  

後述のMacro expressionでも・・・

  - name: SAMPLE_VARIABLE_VALUE  
    value: sampleval is $(sampleval)  

というように書けます。
使えるタイミングとしてはvariableとconditionのキーバリューペアになるそうなのですがvariableの値の指定に使うのはちょっと不便ですね。
ちなみにconditionのキーバリューとはstageやjobに対して設定できる値で、この値がtrueの場合のみそのstageやjobを実行するといった動作を指定できます。
これを使用して、「前の処理が成功した場合のみこの処理を実行」というようなロジックを組み込むことができます。
実際にcondition内でどのような判定ロジックを組み込むかはSpecify conditionsのドキュメントを参照です。
今回は変数の取得周りに話を限って解説しています。この辺りは一旦話を広げ始めると無限に広がってしまうのですよ・・・
なお、取得できる変数は、先程説明した3つの変数のうち固定値のvariableとvariabe groupで指定されているvariableです。
runtime parameterの値は取得できません。Runtime Expressionなのにruntime parameterの値を取得できないとはこれいかに。
書き方としては以下のようになります。

$[varibales.変数名] # 固定値のvariable、variable groupのvariable 共通  

$()について

$() の形で記述されるこのExpressionはMacro expressionといいます。
Macro expressionはパイプラインの中で、それぞれのタスクが実行される前に評価されて実際の値に置き換えられます。
こちらもCompile time expressionと同じく、適切なスコープ内でなら基本的にどこでも使えるのですが、一部Runtime Expressionと同じような制限を受けるところがあります。
それは、キーバリューペアの中では : の右側にしか書けないということです。yamlコンパイルされ、プランとしては出来上がった後で評価されるため、プランの構造自体は操作できない・・・というような意味合いかと個人的には考えています。
ただ、前述のとおりRuntime Expressionとは違って : の右側であれば別の文字列の中に混ぜて記述することも可能です。
なお、取得できる変数は、先程説明した3つの変数のうちRuntime expressionと同じく固定値のvariableとvariabe groupで指定されているvariableです。
runtime parameterの値は取得できません。その場合はCompile time expressionを使いましょう。
書き方としては以下のようになります。

$(変数名) # 固定値のvariable、variable groupのvariable 共通  

変数名を直接書ける分シンプルですね。

まとめ

表にしてまとめると以下のようになります。

Expression名 書き方 variablegroupの値が取れるか variableの固定値が取れるか parameterの値が取れるか 評価タイミング 使用可能箇所 注意点
template expression ${{ parameters.パラメータ名 }}
または${{ variables.変数名 }}
x o o コンパイル どこでも(スコープが適切であれば) 別名: Compile time expression
runtime expression $[variables.変数名] o o x 実行時 variableとconditionの定義箇所 定義の右側全体をカバーする必要あり
macro expression $(変数名) o o x タスク実行前 タスクの定義内 定義の右側にのみ書ける

また、これらのExpressionの評価順はcompile time expression -> macro expression -> runtime expression となっています。
評価タイミングがやってくる前にvariableの値に変更があった場合、評価のタイミングが早い別のexpressionとは違った値が取得される可能性がありますので、ユースケースによっては色々利用できます。

おわりに

今回はAzure pipelineのExpressionについて、変数の取得方法の話と絡めて共有しました。
Expressionについてはよく使うユースケースとしてはパイプラインの変数の取得なのですが、実は本来それだけのためのものでないという話や、評価タイミングについてはそもそもパイプラインyamlからどのようにしてパイプライン実行されるかという話と絡んでいたりするという話もあったりして、切り口によって無限に話が膨らんでいってしまうので説明はすごく難しいです。
今回のこの記事も何度も「ややこssssssっし!!」と呟きながら纏めていました。
少なくともよく気になる観点については纏められたかなと思うので、少しでもお役に立てれば幸いです。

参考

  • Devine Variables
    Variableとそれを使うExpressionの解説はこちら。
    ここでは${{}}のことはTemplate expressionと呼ばれています。
  • Expressions
    Expressionそのものについての解説はこちら。
    ただし、こちらではmacro expressionについての解説はありません。なんで。
  • Specify conditions
    Runtime expressionの使いどころであるconditionの解説はこちら。
  • Add & use variable groups
    Variable Groupに関する解説はこちら。
    Azure Devops上でvariable groupを編集、追加する方法はCreate a variable groupの章のClassicタブにあります。
  • Runtime parameters
    Runtime parameterについての解説はこちら。
  • YAML schema reference
    Azure Pipelineの処理の内容を定義するパイプラインyamlの全体的な書き方はこちら。
    特に、stage、job、stepといった階層の話はvariableの指定、使用できるスコープの話と関連するので、variableについて調べるなら併せて読むと理解が深まるかと思います。

【もっそい小ネタ】Azure PipelineのAPIでリポジトリのtagを指定してパイプラインを実行

f:id:nam_yu_sql:20210310215613j:plain
この記事はcloud.config tech blogにもマルチポストしました。

tech-blog.cloud-config.jp

はじめに

以前の記事で、Azure PipelineのAPIからパイプラインの実行ができることを共有しました。
今回は、その際にgit上のタグを指定することで指定したタグのコミットの時点のコードを使ってパイプラインを実行できることを共有します。
CI/CDパイプラインを実行する際に開発中のアプリケーションの特定のバージョンのものをデプロイしたいこともあるかと思いますが、gitのタグを利用することが一つの方法になるかと思います。

リポジトリのtagを指定してパイプラインを実行する

さっくりパイプラインyamlを作成、更新するコミットをmasterブランチに打ち、それぞれのコミットに以下のようなタグを振りました。
f:id:nam_yu_sql:20210310215625p:plain
ちなみにパイプラインyamlの中身は以下のようになっています。

trigger:  
- none  
  
pool:  
  vmImage: ubuntu-latest  
  
steps:  
- script: echo This is pipeline v2  
  displayName: 'Run a one-line script'  

v1のコミットにおいては、 - script: echo This is pipeline v2 の行が - script: echo This is pipeline v2 になっています。

それでは、まずは以下のリクエストをbodyに入れてパイプラインを回すAPIを叩いてみます。
Azure Pipelineを回すAPIの叩き方はこちらで紹介しています。

{  
    "resources":   
    {  
        "repositories":   
        {  
            "self":   
            {  
            "refs/heads/master"  
            }  
        }  
    }  
}  

結果はこのようになりました。
f:id:nam_yu_sql:20210310215639p:plain
masterブランチの先端のコミットの状態のpipeline.yamlが読まれ、 This is pipeline v2 のメッセージが表示されていますね。

それでは次は、リクエストボディを以下のようにしてリクエストを送信します。

{  
    "resources":   
    {  
        "repositories":   
        {  
            "self":   
            {  
            "refName": "refs/tags/v1"  
            }  
        }  
    }  
}  

しばらく待って・・・・パイプラインが回りました。
結果を見てみましょう。
パイプラインの実行結果一覧では今回の実行ではv1のタグが付いたコミットを使用している表示がされています。
f:id:nam_yu_sql:20210310215707p:plain
また、実行結果の詳細画面では・・・
f:id:nam_yu_sql:20210310215716p:plain
This is pipeline v1 と表示されていますね。

指定したタグのコミットのコードがパイプラインで使用されていることが確認できました。
この状態ならpipeline.yaml以外のコードもこのコミットの状態のものなので、タグを指定することでアプリケーションのコードも固定することができます。
これを用いることはデプロイするアプリケーションのバージョンを管理する方法の一つとして使えるかと思います。

おわりに

今回はAzure Pipelineを実行するAPIの話を土台に、gitのタグをリクエストで指定してやることでそのタグのコミットのコードが使用できることを共有しました。
と言っても、肝なのはリクエストに含まれる「refName」のパラメータ一つなので、今回は小ネタとして共有しました。
幸せなCI/CDライフを!

【Ignite2021】Ignite併設イベントで技術学習するついでに無料でマイクロソフト試験が受けられるらしいんよ~

f:id:nam_yu_sql:20210304090930j:plain
この記事は後ほどcloud config tech blogにもマルチポストしました。

tech-blog.cloud-config.jp

はじめに

おつかれさまです。調子どうですか~。
最近仕事中に雑談するときはいつもそういって話しかけてるなむゆです。
人に話しかけるのになんて言って話しかけたらいいかずっと分からずに人垣の後ろから覗き込むだけだったキョロ充はこれで卒業できした。
と言っても実際にはキョロ充は精神的には一生キョロ充なので本当は卒業の概念などなく、生涯その枷をはめて生きていくしかないのでそれができたところでってところはあるのですが。
精々自分はまるでそうではないかのように振舞うことしかできないのですがそれをやると今度は自分のやることなすことがどこか嘘っぽく感じて無限にみじめになるのです。

さて、今年もIgniteが始まりました。
昨日は家に帰ってセッション見るぞと思ったのですが会期中は生放送を見るか、それを逃したらオンデマンドで見れるようになるまで生放送から24時間ほど待たなければならないようです。
そんなわけで昨日の夜に見たのはMicrosoft現CEOサティアナデラのkeynoteくらいだったりします。
あとは気になるセッションをお気に入りに入れて後でオンデマンド公開されるのに備えたりしてました。
ただ、それ見るだけと味気ないのと夕飯を食べ終わる前にセッションが終わってしまったのでイベントサイト内をうろうろしていたのですが面白そうな一角があったのでそこについて紹介です。

Microsoft Ignite Cloud Skills Challengeで一人一枚無料で試験オファーを入手できるそうよ

Microsoft Ignite Cloud Skills Challengeは、Microsoft Igniteに併設されたMicrosoft関連技術の学習キャンペーンです。
Microsoft関連技術の学習コンテンツであるMicrosoft Learnの教材を用いて、Microsoft Igniteで触れられた技術の分野ごとに作られたChallengeをこなしていくことによって、その分野の技術を習得しませんかということです。

Challengeは7つあります。興味のある分野を触ってみましょう。↓

また、このキャンペーンの参加には特典があって、それがMicrosoft認定試験の試験オファーになります。
このオファーを使うことで、一定の範囲内の試験を一回無料で受けることができます。
Microsoft認定試験は普通に受けようとすると普通2万円ほどかかるので、こういったキャンペーンを利用して受けておくのは結構お得感があります。

細かいルールはこちらのページにデ〇ノート裏表紙のHow to use itよろしくまとめられています。
以下重要そうな項目をピックアップ。

  • The Microsoft Ignite Cloud Skills Challengeは3月2日の世界標準時午後4時から3月30日世界標準時午後4時まで開催されています。
  • Challengeをいくつこなしても受け取れるオファーは一回分だけです。
  • オファーの引き換えができるのは021年4月7日から2021年6月30日までです。
  • 引き換えられる対象の試験は以下の通り。
    • AZ-104: Microsoft Azure Administrator
    • DP-100: Designing and Implementing a Data Science Solution on Azure
    • MS-700: Managing Microsoft Teams
    • MS-100: Microsoft 365 Identity and Services
    • MS-101: Microsoft 365 Mobility and Security
    • DA-100: Analyzing Data with Microsoft Power BI
    • SC-200: Microsoft Security Operations Analyst
    • SC-300: Microsoft Identity and Access Administrator
    • SC-400: Microsoft Information Protection Administrator

また、ページの下部によくあるQ&Aもあるので目を通しておくといいかと思います。
以下重要そうな内容をピックアップ。
ただ、確実を期すために一度は直接見た方がいいと思います。
- 会期内に一つでもchallengeを完了していると、登録しているMicrosoft Learnのプロファイルに2021年に4月7日に無料試験が関連付けられます。
- チャレンジ登録前にLearnで該当する教材を完了していれば、Challengeの登録後しばらくすると進行状況に反映されます。

Microsoft Ignite Cloud Skills Challengeの始め方

Igniteのページからアクセスする場合は、ページ上部「Learning Zone」から「Cloud Skills Challenge」を選択することでCloud Skills Challengeのトップページに移動できます。
あるいは、こちらのリンクから移動できます。
f:id:nam_yu_sql:20210304091043p:plain
トップページを下にスクロールしていくと、前述の7つのChallengeがあるのでそのうちのどれかしら興味のあるChallengeの「Go to challenge」をクリックします。
f:id:nam_yu_sql:20210304091057p:plain
いずれかのchallengeのページに移動したら、「チャレンジに参加する」ボタンをクリックします。
f:id:nam_yu_sql:20210304091115p:plain
このタイミングでMicrosoftアカウントでログインしていなければ、ログインしておきます。
f:id:nam_yu_sql:20210304091123p:plain
ログインすると、Cloud Skills Challengeの通知用メールアドレスと今いる国を設定します。ここにCloud Skills Challengeの各種お知らせが届きます。
f:id:nam_yu_sql:20210304091137p:plain
これで登録完了です。
ほら、褒められてますよ。よかったですね。
「学習の開始」をクリックするとそのchallengeのページに遷移し、「学習を続行する」のボタンから対応するLearnが確認できるので一つ一つ完了させていきましょう。
f:id:nam_yu_sql:20210304091148p:plain
個人的にはこの「すごいですね」とかLearn完了時の「やりま
したね」がツボです。なんで「やりま」と「したね」の間に行替えがあるかは実際にLearnをやってみると分かると思います。

おわりに

今回はIgnite 2021に併設されたイベントのCloud Skills Challengeについて共有しました。
ちなみに私はMicrosoft Ignite Azure Data Scientist ChallengeをやってDP-100を受けようかと考えています。
最新のMicrosoft技術を学んでさらにその知識で受けられる認定試験の無料オファーが入手できるらしいので、お時間と興味がある方はぜひチャレンジしてみてください。

参考

Azure Machine Learningで作ったモデルをAKSにデプロイしてみる

f:id:nam_yu_sql:20210301083433j:plain
この記事はcloud.config tech blogにもマルチポストしていまます。

tech-blog.cloud-config.jp

はじめに

以前、Azure Machine Learningで回帰モデルを作成するやり方について記事を書きました。
機械学習でモデルをトレーニングしたら、それを何らかのアプリで使用できるようにする必要があると思います。
Azure Machine Learningでは、そのモデルをアプリとしてデプロイして利用する方法が整備されていて、それを利用することで作成したモデルをWebAPIで簡単に利用できるようになります。
今回はその方法の共有です。

回帰モデルを作成してAKSにデプロイする

それでは実際に手を動かしていきます。
以前の記事で作成したAzure Machine Learningのリソースやモデルがあればそれを利用してもいいですが、今回は今回でリソースの作り方から解説していきます。

Azure Machine Learning リソースを作成する

まずは前回同様Azure Machine Learningのリソースを作成します。
ポータル画面上部の検索窓から「Azure Machine Learning」を検索し、青いフラスコみたいなアイコンの「Machine learning」を選択します。
画面左上の「+追加」をクリックしてリソースの作成画面に入ります。

リソースの作成画面では適当なワークスペース名を設定します。
リージョンは基本的に今作業している地域から近い場所が望ましいですが、後述の理由で別のリージョンを設定した方がいい場合もあります。
それ以外は基本そのままで「確認及び作成」ボタンをクリックし、検証されたら「作成」をクリックしてリソースを作成します。
f:id:nam_yu_sql:20210301083617p:plain
しばらく待ってリソースが作成されたらそのリソースに移動し、「スタジオの起動」ボタンを押してスタジオ画面に移動します。
f:id:nam_yu_sql:20210301083626p:plain

コンピューティングクラスターを作成する

スタジオ画面に移動したらまずはモデルのトレーニングに使用するコンピューティングクラスターを作成します。
スタジオ画面の左端のアイコンの一覧から下から4つ目、コンピュータのモニターのようなアイコンをクリックし、開いた画面の中でタブを「コンピューティングクラスター」に移動します。
そこで「+新規」のボタンをクリックし、コンピューティングクラスターを新規作成します。
f:id:nam_yu_sql:20210301083644p:plain
コンピューティングクラスターの設定画面ではコンピューティングクラスターの性能や名称を設定しておきます。
性能はStandard_D11程度が妥当かと思います。
f:id:nam_yu_sql:20210301083654p:plain
後はコンピューティング名を設定し、「作成」ボタンを押してコンピューティングクラスターを作成します。
f:id:nam_yu_sql:20210301083705p:plain

モデルのテンプレートを利用してトレーニングする

次はモデルを作成してトレーニングしていきます。
前回の記事ではこの部分を手作業で行いましたが、学習用のサンプルモデルはテンプレートにあるので、今回はそれを利用します。
スタジオ画面左端のアイコン一覧で「デザイナー」のアイコンをクリックし、デザイナーのトップ画面に移動します。
そして、その画面の上部に「Regression - Automobile Price Prediction (Basic)」というサンプルがあるので、これをクリックします。
これは自動車の価格を予測する回帰モデルで、メーカーやドアの数などといったその車の要素から価格を予測します。
f:id:nam_yu_sql:20210301083720p:plain
サンプルをクリックすると前回の記事で作成したようなモデルトレーニングのパイプラインが自動生成されているので、さっそくモデルのトレーニングを行っていきます。
まずは、トレーニングを行うコンピューティング先を指定します。
パイプラインのデザイナー画面の右の方から、「コンピューティング先を選択」をクリックします。
f:id:nam_yu_sql:20210301083733p:plain
するとコンピューティング先のセットアップのモーダルが出てくるので、作成したコンピューティングインスタンスを選択し、「保存」をクリックします。
f:id:nam_yu_sql:20210301083754p:plain
これでモデルのトレーニングを行うクラスターがセットアップできたので、それを使用してトレーニングを行っていきます。
デザイナー画面の上部に「送信」のボタンがあるのでこれをクリックします。
f:id:nam_yu_sql:20210301083804p:plain
セットアップが面では実験は「新規作成」をクリックし、実験名を入力して「送信」ボタンをクリックして実験を開始します。
そこそこ時間がかかるのでしばらく待ちましょう。
f:id:nam_yu_sql:20210301083820p:plain
ただ、最近はAzure上のVMのリソースがひっ迫しているのか、VMの割り当てに時間がかかりまくった挙句「VMが足りなくて割り当てられなかったよー」というようなメッセージが表示されることがあります。
Azure Machine LearningでのVMリソースは基本的に普段は割り当てず、トレーニングを行う時にだけ払い出すようにできます。(例えば、クラスター作成時に「最小クラスター数」を0にした場合とか)
その際、「よしトレーニングやるぞVM確保しなきゃ」となったタイミングでそのリージョンにVMの数が足りないとそれによってVMの確保に失敗することがあるためです。
特に自分の場合東日本リージョンで何度か試したのですが何度やってもVMが確保できなかったため、別のリージョンにAzure Machine Learningのリソースを作り直してこの先の作業をやり直したりしました。
このエラーが出たら、Azure Machine Learningのリソースの再作成からやり直す羽目になります。

リアルタイム推論パイプラインを作成して実行する

回帰モデルのトレーニングが完了するとデザイナー画面の上部に「推論パイプラインの作成」というドロップダウンメニューが出てくるのでこれをクリックして、「リアルタイム推論パイプライン」をクリックし、リアルタイム推論パイプラインを作成します。
これによって既存のデザイナーのパイプラインにいくらか要素が追加されたパイプラインが新たに生成されます。
新たに追加されたパイプラインでは、それまでのパイプラインの書くモジュールに加えて、「Web Service Input」と「Web Service Output」という要素が追加されています。
f:id:nam_yu_sql:20210301083909p:plain
パイプラインが新たに生成されたら、こちらも同様に画面上部から「送信」をクリックしてモデルのトレーニングを行いましょう。
モデルのトレーニングを行っている間に次の工程である推論クラスターの作成を行います。

推論クラスターを作成する

推論クラスターは、回帰モデルをWebアプリとしてデプロイする先のAKSkubernetesクラスターになります。
これを作成します。
再び、コンピューティングの画面に移動し、「推論クラスター」のタブを選択し、「作成」のボタンをクリックします。
f:id:nam_yu_sql:20210301083929p:plain
まずは作成するクラスターのVMサイズを選択しますが、安めのサイズを選択しておきます。
f:id:nam_yu_sql:20210301084037p:plain
次の画面では分かりやすいコンピューティング名を設定し、クラスターを作成します。
f:id:nam_yu_sql:20210301084047p:plain

推論クラスターにアプリをデプロイする

推論クラスターが作成でき、リアルタイム推論パイプラインのモデルトレーニングが完了したら、リアルタイム推論パイプラインの編集画面に戻ります。
画面の上部の「送信」ボタンの横に「デプロイ」ボタンが有効になっているかと思います。
これをクリックします。

すると、「リアルタイムエンドポイントのセットアップ」というモーダルが出てくるので、回帰モデルをWebアプリとしてデプロイする準備をします。
分かりやすい名前を設定し、コンピューティングの種類としては「Azure Kubernetes Service」を選択します。
その下のコンピューティング名には先程作成した推論クラスターを選択し、「デプロイ」をクリックして回帰モデルをWebアプリとしてAKS上にデプロイします。
f:id:nam_yu_sql:20210301084108p:plain

アプリをテストする

これでモデルがAKS上で利用できるようになったのですが、実際にそのモデルを試してみましょう。
今度は、スタジオ画面左は地のアイコン一覧から、「エンドポイント」をクリックします。
するとリアルタイムエンドポイントの一覧が表示されるので、先程作成した名前のエンドポイントを選択します。
f:id:nam_yu_sql:20210301084116p:plain
エンドポイントの詳細画面が表示されますが、画面上部から「テスト」タブを選択します。
すると、作成した回帰モデルにリクエストする各種パラメータの入力フォームが出てくるので、ドアの数、メーカー名などを好みに設定して「テスト」ボタンを押します。
f:id:nam_yu_sql:20210301084144p:plain
すると、画面右にテスト結果の出力が表示されます。
これを下の方までたどっていくと「Scored Labels」という値があるかと思います。
この値が、今回の回帰モデルで予測された「price」の値になります。
f:id:nam_yu_sql:20210301084213p:plain
これにて後ろでテストが行われ、回帰モデルがWebアプリとしてAKS上にデプロイされていることが確認できました。

おわりに

今回はAzure Machine Learningで作成したモデルをWebアプリとしてAKS上にデプロイする方法を共有しました。
WebAPIとしてデプロイすることで他アプリからも利用しやすくなり、また、AKS上で独立したサービスとすることでモデルの更新などにも対応しやすくなります。
モデルをアプリ化して独立したアプリとするという考え方はAzure Machine Learning以外のプラットフォームでも使えそうな概念かと思うので、よっしゃこれから機械学習アプリを作るぞという際には心にとめておいていただけると幸いです。

参考