Mais conteúdo relacionado Semelhante a Azure Api Management 俺的マニュアル 2020年3月版 (20) Azure Api Management 俺的マニュアル 2020年3月版9. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Subscription
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 9
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
10. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 10
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Subscription
API:
API Managementから到達できるAPIであればなんでも登録
できる。まずはここへ API を登録するところからスタート。
PolicyではRequest/Response/Errorに対して処理をC#で書
くことができる。
11. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 11
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Subscription
Products(製品/成果物):
docsでは「成果物」もしくは「製品」と記載されている。
Portalでは「製品」となっているので注意。
APIに対してアクセス制御を行うのが主な役割。
APIと成果物はN:Nの関係性。APIは複数のProductsに登録が
できる。
12. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 12
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Group:
Developerは複数の(Custom)グループに所属することがで
きる。ユーザー登録したDeveloperは必ずDevelopersグループ
に所属する。(ユーザー登録はいろんなシナリオで可能)
Productsのアクセス制御にGroupを登録することで、その
Groupに所属する開発者はその成果物を使用することができる
ようになる。(※Subscription使用による例外あり)
GroupとProductsはN:Nで紐づけることができる。
Subscription
13. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 13
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Policyでできること(やること)の代表格
セキュリティ&アクセス制限
• API キー、JWT トークン、証明書、その他の資格情報を検証
• 使用量クォータとレート制限を適用
• IPアドレスやユーザーIDで接続を制限・スロットリング
キャッシュ
• Backend APIのResponseをキャッシュ
その他
• Request/Reponseログを転送
• 外部サービスをHttpで呼び出し
Subscription
Policy:
PolicyはBackend APIへRequestを投げる前後に処理を入れることができる。処理
はC#で実装する。
PolicyはBackend APIが公開する複数の操作(Operation)ごとに設定もできるし、
Backend API全てに共通の設定もできるなど、スコープが4段階ある。
(操作の例: /id)
実に多彩な処理の実装が可能。テンプレートもあるが、外部にReqeustを投げるこ
とができるので独自実装を頑張ればかなりのことができる。
14. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 14
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Policyによるアクセス制限:
ProductsにもPolicyが設定できる。API単位より
上のスコープでの設定と思えばOK。
例えば使用量上限があるプランのAPI群を公開し
たい場合、そのプラン用の成果物を作成して使用
量クォータによる制限のPolicy実装をここに設定
することになる。
Subscription
アクセス制御:
アクセス制御はProductsとグループを紐づけること
で可能となる。
ここでいうアクセス制御とは、Developer(User)に
対してのものであることに注意。(APIを外部から使
用するClientに対する制御ではない)
15. Developers Guests
Custom
Admins
API Management Overview
API Management
Group
API
Policy
Frontend Backend Api
/path https://~
https://<APIM Name>.azure-api.net
アクセス制御
Developer(User)
N:N
Operations
In
Out
Err
Policy
N:N
Developer Portal
Nextscape Inc. 15
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Products(製品)
N:N
Subscription:
俗にいうAPIキー。このキーをHeaderに入れないと
Requestが失敗する。 ProductsとDeveloperを紐
づけた設定の場合、Developer(User)からの利用許
諾の申請を必須とすることができる。
【重要Update】
従来、SubscriptionはProductsとDeveloper
を紐づけなければならなかったが、大きな仕
様追加がある。詳しくは「サブスクリプショ
ン」を参照
Subscription
17. APIを追加する
Nextscape Inc. 17
• Blank APIを選ぶと、自分でOperationを追加する必要がある
• 他のやつは定義を読んで自動的にメソッドを追加してくれる
• OpenAPI(Swagger)の仕様を公開しておくのが一般的(AzureならApp Servcie、
Azure以外ならOpenAPIを選択することになる)
20. • APIを登録しただけだと動かない!
• missing subscription key というエラーが出たときの原因は大抵コレ
Products(製品)へAPIを登録する(オプション)
Nextscape Inc. 20
GroupA
グループ
アクセス制御
開発者(ユーザー)成果物(製品)API
既存API
新規API
Productsに紐づけないと、
開発者からは見えない。
自分がAdminsグループに
所属する管理者の場合、
Portalからは見えるが実行
時はエラーになる。
成果物に紐づいていないので実
行するとエラーになる
従来の仕様によるエラーの説明
最新の仕様は『サブスクリプション』の章を参照
23. Nextscape Inc. 23
• OpenAPI(Swagger)を公開していなくてもメソッド追加してくれそうな予
感がする。
APIの追加時にApp Serviceを選ぶと?
• 実際にやってみるとこんな感じで必ず8つメ
ソッドが追加される
• メソッド名は適当な名前。AppServiceで公開し
ているAPIを読んで作成していないことがわか
る
• 表示名(DisplayName)は変えられるが、メソッド
のシステム名(name)は変えられない。
PowerShellやAzure Cliなどで扱う時に困る
• 素直にApp ServiceはOpenAPIを公開しておけっ
てことだ
25. • ポリシーはXML形式。4ブロックに分かれている
ポリシーの基本形
<policies>
<inbound>
<!–- Backend APIを叩く前に呼びたい処理 -->
<base />
</inbound>
<backend>
<!–- Policyは1つだけしか実装できない。Backend APIを叩くための
Poilicyがデフォルトで実装されている -->
<base />
</backend>
<outbound>
<base />
<!–- Backend APIからのreponseを返す前に呼びたい処理 -->
</outbound>
<on-error>
<base />
</on-error>
</policies>
Nextscape Inc. 25
詳細は後述
詳細は後述
26. • C#の実装方法は2つの形式がある。
• どちらも括弧の前に@付ける
XMLの属性値にC#で実装していく
<set-variable name=“date1" value="@(context.Timestamp.ToString("R"))" />
<set-variable name=“date2" value="@{
var results = DateTime.Now.ToString(“yyyy/MM/dd HH:mm:ss.fffff”);
return results;
}" />
Nextscape Inc. 26
単文の場合は@()
複文の場合は@{}
• ポリシー式で使用できる .NET framework の型は決まっているため、usingは不要
• 保存時に名前空間を解決してくれているっぽい(解決できないとエラーになる)
• 言い換えると外部dll を読み込ませることができない(Nugetもできない)
っていうかこれラムダ式だわ インテリセンスが効かないんだよね・・・
27. • Reqeust/Response含めたあらゆる情報はこいつが持っている
暗黙的なオブジェクト context
Nextscape Inc. 27
Context
Timestamp: DateTime
Variables: IReadOnlyDictionary<string, object>
Response
Request
LastError
Headers: IReadOnlyDictionary<string, string[]>
IpAddress: string
Body
※一部抜粋
アクセス日時
<set-variables>でセットされた値を保持
Requestのヘッダー値
Request元のIPAddress
RequestのBody
29. • ちょっと見方にコツがいる。
• まず、英語に切り替える!
• url の ja-jp を en-us にすればいい
• 例:Requestの情報がほしい
• まず、contextから探す。
• context.Requestというプロパティが
あるのでリンクをクリック
contextの中身を知らないと実装できない
Nextscape Inc. 29
35. • 同期リクエストの場合は<send-request>
• Responseを取得できる
• 非同期リクエストの場合は< send-request-one-way >
• Responseは取得できない
外部へリクエストを投げるポリシー
• <send-request>のResponseは、指定した変数名でcontext.Variablesディク
ショナリに格納される
<send-request mode="new" response-variable-name="testResponse" timeout="5" ignore-
error="true">
・・・
</send-request>
Nextscape Inc. 35
36. • Blob SDKが使えないのでREST Apiで実装
• ログを書く処理は非同期にしたいので、 send-request-one-way使用したい
• でもエラーでまくって辛かった。どうやってデバッグするか悩んだ
• 編み出したやり方は以下
• まずはsend-requestで実装。
• send-reqeustにはresponseを変数に入れる機能がある
• responseは、IReseponseクラス。
• responseの中身を見るために、set-variableの実行結果をTraceで見ることに(詳しくはこの後)
追加Blobにログを書くPolicy作ってみた
<set-variable name="test" value="@{
var hoge = ((IResponse)context.Variables["testResponse"]).Body.As<string>();
return hoge;
}" />
<send-request mode="new" response-variable-name="testResponse" timeout="5" ignore-
error="true">
・・・
</send-request>
Nextscape Inc. 36
responseを格納する変数
変数値をTraceで表示させるために意味なく
他の変数にセットする
37. • これを埋めていく
追加Blobにログを書くPolicy作ってみた
<inbound>
<base />
<set-variable name="version" value="@("2015-04-05")" />
<set-variable name="putDate" value="@(context.Timestamp.ToString("R"))" />
<set-variable name="logData" value="@{
// ログに出力する内容を作る
return something;
}" />
<set-variable name="authorizationHeader" value="@{
// BlobのBearer Authorizationヘッダーを作る
return authorizationHeader;
}" />
<send-one-way-request mode="new">
// 非同期のリクエストを投げる
</send-one-way-request>
</inbound>
Nextscape Inc. 37
40. Globalレベル(API の All APIsに設定する)
成果物レベル(製品)
API レベル(API の All Operationsに設定する)
各Operationsレベル
ポリシーには階層がある
• 上から順に評価される。
Nextscape Inc. 40
43. • <backend>には1つだけしかPolicyを実装できない制約がある!
<backend>について
Nextscape Inc. 43
<backend>
<retry condition="@(context.Response.StatusCode == 500)" count="3" interval="1">
<forward-request />
</retry>
</backend>
<backend>
<base />
<retry condition="@(context.Response.StatusCode == 500)" count="3" interval="1">
</retry>
</backend>
エラーとなってしまう例
入れ子にして回避
上位スコープに
<forward-request />
が設定されているため必要
<base />を削除
44. • Visual Studio Code用のスニペットならある・・・けど使えないかな
• https://github.com/Azure/api-management-policy-snippets
ポリシーの素敵なエディタはないのか
Nextscape Inc. 44
46. ユーザー(Developer) と Group
• Docsにおけるユーザーとは、Azure API Managementで公開された API を使用する開発者
(Developer)のこと
• ユーザーとGroupはN:Nで紐づけることができる
• Group は Products とN:Nで紐づけることで開発者へのAPI公開をまとめて制御する
Group
アクセス制御
Products(製品)
アクセス制御
GroupA
GroupB
API
API
API
API
API
ユーザー(Developer)
全部のAPI使用可
下2つのAPIのみ
使用可
Nextscape Inc. 46
47. ユーザー(Developer) と Group
• デフォルトで用意されているグループは3つ
• Administrators, Develpers, Guests
• ユーザーを新規追加すると、デフォルトでDevelopersグループ所属になる
Nextscape Inc. 47
ユーザー(Developer)
Developers
Guests
Admins
デフォルトグループ
GroupA
GroupB
カスタムグループ
ユーザーをPortalから追加しても、開発者
ポータルから開発者自身がサインアップ
しても、必ずDevelpersグループの所属に
なる
50. API Manegement Role別機能差
APIM作成者 or APIM 共同作成者 APIM Operator Role APIM Reader Role
Portal フル権限 参照のみ。変更設定できるのは
スケールアップ・ダウン、SSL、
カスタムドメインの設定ぐらい
メトリックス参照で
きるぐらい。その他
は参照すらできない
開発者ポータル フル権限 × ×
所属Group Administrator Guest Guest
• APIMの作成者、またはAPIM 共同作成者ロール
の権限があるAzureユーザーは、暗黙的に
Administratorグループに所属する(共同作成者
は画面にユーザーとしては表示されない)
• APIM Service Reader Role, Operator Roleの権限を
もつAzureユーザー、もしくはユーザー登録を
していない(認証していない)場合は暗黙的
にGuestグループに所属する
Nextscape Inc. 50
52. 試してみた
Nextscape Inc. 52
• 承認が必要なProducts(製品)を作る
• Products(製品)に対してカスタムグループを紐づけておく
• 開発者ポータルからこの成果物に対して
サブスクリプション申請
• Portalからサブスクリプションを承認
• 紐づいたカスタムグループにユーザが登録されるか
53. 試してみた
Nextscape Inc. 53
• 承認が必要なProducts(製品)を作る
• Products(製品)に対してカスタムグループを紐づけておく
• 開発者ポータルからこの成果物に対して
サブスクリプション申請
• Portalからサブスクリプションを承認
• 紐づいたカスタムグループにユーザが登録されるか
Developerグループも紐づけておかないと、開発
者ポータルにProducts(製品)が表示されない
54. 試してみた
Nextscape Inc. 54
• 承認が必要なProducts(製品)を作る
• Products(製品)に対してカスタムグループを紐づけてお
く
• 開発者ポータルからこの成果物に対して
サブスクリプション申請
• Portalからサブスクリプションを承認
• 紐づいたカスタムグループにユーザが登録されるか
56. 試してみた
Nextscape Inc. 56
• 承認が必要なProducts(製品)を作る
• Products(製品)に対してカスタムグループを紐づけておく
• 開発者ポータルからこの成果物に対して
サブスクリプション申請
• Portalからサブスクリプションを承認
• 紐づいたカスタムグループにユーザが登録されるか
58. 試してみた
Nextscape Inc. 58
• 承認が必要なProducts(製品)を作る
• Products(製品)に対してカスタムグループを紐づけておく
• 開発者ポータルからこの成果物に対して
サブスクリプション申請
• Portalからサブスクリプションを承認
• 紐づいたカスタムグループにユーザが登録されるか
追加されなかった・・・残念
60. • サブスクリプションとは、API または Products に対してAPIキーによるアクセス制御のこと
• 登録したユーザーだけが特定API、特定Productsを使用できるようにしたい場合の設定である
• (Front APIへのRequest時に、headerに key のセットが必須となる)
サブスクリプション
Nextscape Inc. 60
GroupA
グループ
アクセス制御
ユーザー(Developer)Products(製品)
API
既存API
新規API
従来の仕様では、サブスクリプションは必ず Productsとユーザーの両方に対して紐づけを持っていた。
この仕様のせいで、新規に作成したAPIをProductsへ紐づけをし忘れてしまってアクセスエラーとなる
ミスがあった
Subscription
Productsに紐づ
いていないので
アクセスすると
エラーになる
62. • 新仕様その2
• 従来のサブスクリプションは、Productsとユーザーの両方に対して必ず紐づけなければならなかった。
• 新しい仕様ではユーザーとの紐づけはオプションとなった(スタンドアロンサブスクリプション)
(これはサブスクリプションキーを共有したいシナリオに対応するためである)
• 「すべてのAPI」とのみ紐づけをもつ
• 「指定したAPI」とのみ紐づけをもつ
• 「Products」とのみ紐づけをもつ
サブスクリプション
Nextscape Inc. 62
GroupA
グループ
アクセス制御
Products(製品)
API
既存API新規API
Subscription
すべてのAPI ユーザー(Developer)
Subscriptionを作
成して紐づけな
いと、エラーに
なるのは変わら
ない
64. Nextscape Inc. 64
• 組み込みの「すべてのAPI」に対するサブスクリプションは
「Build-in all-access subscription」という名称
(ちなみにPowershellでこいつの定義を覗いてみると、システム名はmasterという)
サブスクリプション
非アクティブになってい
て削除できない
65. Nextscape Inc. 65
• Built-in all-access subscriptionのサブスクリプションキーを使用したくない
場合は?
• APIを選択して、Settings画面の「Subscription required」のチェックを外す
サブスクリプション
このチェックを外すとア
クセス無制限ということ
になるので、本番では別
のアクセス制御をちゃん
と設定すること
67. • Portalに
• 「サブスクリプションの追加」
• 「サブスクライバーの追加」
• の2つの表記があり、用途の違いがわからなくて混乱しちゃうので整理しておく。
サブスクリプション
「サブスクリプションの追加」とは、「すべてのAPI」「単一API」「Products(製品)」のいずれかを必須指定し、
オプションとしてユーザー(Developer) を指定して新しいサブスクリプションを作成すること
Nextscape Inc. 67
77. • 開発者が
• 自らユーザーとして登録する機能
• 開発者が使いたい成果物(APIを含んでいる)に対して利用許諾を申請する機能
• APIをお手軽に試すテスト機能
• 不具合を報告する機能(管理者にはmailが飛んでくる)
• などを持つ、開発者のための画面。
• APIMagementインスタンスとは全く別のリソースと思ったほうが良い
• 開発者ポータルはAPIMagementとは無関係のWebクライアントとして認識したほう
がdocsが読みやすい
• AzurePortalでは、(基本的に)開発者ポータルへのログインユーザーの権
限管理しかしない
• 権限管理以外だと、開発者ポータルがAPIMgmtに対してRequestする前に外部
IDProviderからid_token(JWT)を得る、もしくは認証サーバーからaccess_tokenを得
るための機能のための設定をするぐらい(ややこしいので後述する)
そもそも開発者ポータルとは何か
Nextscape Inc. 77
94. Nextscape Inc. 94
用途が混在していてわかりにくいので要注意
Portalのセキュリティセクション
• クライアント認証の検証時に使う証明書をUploadする
• APIを呼び出す時に使う証明書をUploadする
(どちらもUploadだけじゃダメで、Policyにて実装が必要)
• 開発者ポータルの認証方式の設定
• 開発者ポータルからAPIをテストする時にTokenを取得する
ために必要な認可サーバー(or IDProvider)の設定
1 2
3
4
APIClient
3
2
4は証明書の検証もできるが、PolicyでJWTの検証をするのがメイン1
1
説明済み
※従来「セキュリティ」にあった「ユーザー」と「委任」は、めでたく「Developer Portal」へ移動しました
101. • API Managementは認証・認可(承認)の機能は持っていない。あくまでJWTの検証をするPolicy
がデフォルトで用意されているだけ
• JWTを検証する、ということはIDProviderはAzureAD、B2Cだけしか使えないわけじゃない。
OpenIdConnectをサポートするIDProviderであればなんでも大丈夫
validate-jwt 1 2
3
APIClient 4
は証明書の検証もできるが、PolicyでJWTの検証をするのがメイン1
Client
API Mgmt
B2C
IDProvider
JWT
JWT
• OAuth2.0ではなくOpenId Connectであることに注意。
OAuth2.0の返すaccess_tokenは仕様が策定されてないし、
通常JWTは使用されない。(ランダム文字列)
• OpenId Connectであれば、id_tokenがJWTで返却される。
• id_tokenの中にClameが格納されている。
Nextscape Inc. 101
111. • ASP.NET Core 2.1で作る
• VS, VSCodeのどっちでもいいけど個人認証ありでプロ
ジェクトを作る
• dotnet new mvc --auth Individual (VSCodeの場合)
Webクライアントを作る
JWTJWT
ValidateOpenId Connect JWT
Nextscape Inc. 111
112. • GoogleのOpenId Connectによる認証を実施する設定
• StartUpクラスのConfigureServicesメソッドに次の実装をする
Webクライアントを作る
services.AddAuthentication()
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = Configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
googleOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
googleOptions.SaveTokens = true;
});
• SaveTokens = trueにセットしておくと、id_tokenがHttpContextにセットされて
ほしいんだけどされない。(access token, refresh token専用らしい)
• Contollerで
• とすればid_tokenが取得できて、これをHeaderに入れてWebAPIに投げたかった
• でもどうやってもid_tokenをControllerに渡せなかった・・・。
HttpContext.GetTokenAsync("id_token")
ClientId、ClientSecretは
Google Dev Consoleで認証
作成した結果をセットする
「ユーザーシートク
レット]ってやつ
dotnet user-secrets –h
でヘルプが出る
Nextscape Inc. 112
122. • services.AddMvcメソッドの上に実装する
JWTのValidation実装をStartupクラスにする
Nextscape Inc. 124
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.Audience = Configuration["Authentication:Google:ClientId"];
options.Authority = "https://accounts.google.com";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
NameClaimType = "name",
AuthenticationType = "Google." + JwtBearerDefaults.AuthenticationScheme,
ValidIssuers = new[] { options.Authority, "accounts.google.com" },
};
});
ClientIdはGoogle Dev
Consoleで認証作成した結
果をセットする
132. • ClientIdを入れるだけで実装終了
validate-jwtを実装する(APIレベルに対して)
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-
validation-error-message="Unauthorized" require-expiration-time="true" require-
scheme="Bearer" require-signed-tokens="true" clock-skew="0">
<openid-config url="https://accounts.google.com/.well-known/openid-configuration" />
<audiences>
<audience><!-- ClientId --></audience>
</audiences>
<issuers>
<issuer>accounts.google.com</issuer>
<issuer>https://accounts.google.com</issuer>
</issuers>
</validate-jwt>
</inbound>
Nextscape Inc. 134
137. • Clientが渡してきたクライアント証明書を検証する実装
証明書の使い方
• クライアント認証の検証時に使う証明書をUploadする
• APIを呼び出す時に使う証明書をUploadする
(どちらもUploadだけじゃダメで、Policyにて実装が必要)2
1
<choose>
<when condition="@(context.Request.Certificate == null
|| !context.Deployment.Certificates.Any(c => c.Value.Thumbprint ==
context.Request.Certificate.Thumbprint))" >
<return-response>
<set-status code="403" reason="Invalid client certificate" />
</return-response>
</when>
</choose>
※未検証
Nextscape Inc. 139
1 2
3
APIClient 4
147. リビジョン
Nextscape Inc. 149
• Revisionを作成すると、Revsion番号が新しく発番されて、;rev=<num>という文字列が
入った新しいURLが作成される
WebAppsのスロット同じ目的の機能と理解すればいい
URLの例
https://<resourceName>.azure-
api.net/echo;rev=2/resource
• 特定のRevisionをメインに切り替えると、;rev=<num>の付与も切り替わる
メインURL(Revision=1)
https://<resourceName>.azure-api.net/echo/resource
Revision=2のURL
https://<resourceName>.azure-
api.net/echo;rev=2/resource
Revision=1のURL
https://<resourceName>.azure-
api.net/echo;rev=1/resource
メインのURL
https://<resourceName>.azure-api.net/echo/resource
Revision=2をメインに切り替え
148. リビジョン
Nextscape Inc. 150
• Revsionが入ったURLは、知っていれば誰でもアクセスできてしまうので、サブスクリプション
キー必須にするなどしてAPIを守るようにすること
リビジョン使用時の注意
• 本番で稼働しているAPI定義やPoliciyに影響を与えることなく、安全に変更することができる
• APIM定義をPublishする前に稼働確認ができる
• 切り替え時に変更点を記載すると開発者ポータルに表示できるため、開発者に対して何をし
たのかアナウンスできる
• リビジョンの切り替えがすぐ反映されるので、問題があったらすぐにロールバックできる
(即時反映なので、DNS切り替えではないみたい)
リビジョンを使用する理由
155. • 例 /v1, /ver1
パス
• 例 ?ver=1, ?api-ver=2, ?api-version=2018-11-01
クエリ文字列
• 例 ver:v1, api-ver:2, api-ver:2018-11-01
Header
バージョン
Nextscape Inc. 157
同時に複数のバージョンのAPIを公開する機能
バージョン文字列は、好き
な文字列が使用可能(数字、
日付、名前など)
バージョンの差異はバージョン文字列を次の3種類のいずれかに埋め込む
163. Nextscape Inc. 165
docs では透過的バージョンアップという手法が提案されている
• https://docs.microsoft.com/ja-jp/azure/api-management/api-management-sample-cache-
by-key#transparent-versioning
• APIM側のバージョンアップにクライアントが合わせるのではなく、クライアント
に合わせてAPIM側がバージョンを切り替えるやり方のことらしい
クライアントに影響を与えないバージョンアップ戦略
API
APIM
通常はクライアントが
バージョンを選択する
API
APIM
透過的バージョンアップは
APIMがバージョンを選択する
BackendAPI
v1
v2
v3
v1
v2
v3
BackendAPI
v1
v2
v3
164. Nextscape Inc. 166
docsに記載されているサンプルの図解
• どのクライアントがアクセスしてきたのかをSubscriptionKeyで判定する方法
透過的バージョンアップの構成
API
APIM
BackendAPI
v1
v2
v3
1.HeaderにSubscriptionKey
を入れてRequest
バージョン管理
2. SubscriptionKeyをキーにキャッシュに
データがあるかを確認
3. データがキャッシュがな
かったら、SubscriptionKeyを
キーに外部からバージョンを
取得し、キャッシュへ格納
4.BackendAPIのURLを取得し
たバージョンに書き換え
キャッシュにはSubscriptionKeyを
キーにバージョンが格納されている
KeyValueのキャッシュ機能が
APIMにはある(フラグメント
キャッシュ)
165. Nextscape Inc. 167
docs の日本語が難しいが、恐らくこういうことが言いたいんだと思われる
• https://docs.microsoft.com/ja-jp/azure/api-management/api-management-sample-cache-
by-key#tenant-isolation
バックエンドの障害対応と段階的ロールアウト
API
APIM
BackendAPI
v1
BackendAPI
v1
BackendAPI
v1
v1
同じバージョンのBackendAPIを複数用意
しておき、クライアントがどの
BackendAPIにアクセスするのかをAPIMが
判定するように設計する
• 設定方法は透過的バージョンアップとほ
ぼ同じ
• BackendAPIのハードウェア障害が起こった時
に、影響があるクライアントが限定される
• BackendAPIのハードウェアごとにバージョン
アップをしていくことで段階的にロールアウ
トが可能となる(似非カナリアリリース)
167. API Management のエラー処理
Nextscape Inc. 169
• API Mamagementでいうエラーは、Policyで発生したエラーと、Backend API
で発生したエラーと二種類あることを意識しておくこと
Policy
In
Out
Err
API
(Backend)
(Publisher)
Client
(FrontEnd)
(Consumer)
Backend
BackendAPIのResponseが
4xx, 5xxの場合
<outbound>でエラーハン
ドリング
PolicyがエラーをThrowの場合
<on-error>でハンドリング
API Management
169. • エラー処理の例
Policyで発生したエラーへの対処
Nextscape Inc. 171
<policies>
<inbound>
<rate-limit calls="5" renewal-period="60" />
<quota calls="100" renewal-period="604800" />
<base />
</inbound>
・・・
<on-error>
<base />
</on-error>
</policies>
エラー処理はまだ入れていない
1分間に5回までのリクエスト制限 わざと1分以内に6回リクエストした
Policyが返却した
独自のStatusCode
170. • エラー処理を実装した
Policyで発生したエラーへの対処
Nextscape Inc. 172
<on-error>
<base />
<choose>
<when condition="@(context.Response.StatusCode == 429)">
<return-response>
<set-status code="499" reason="catch error in onerror" />
<set-header name="ErrorSource" exists-action="override">
<value>@(context.LastError.Source)</value>
</set-header>
<set-header name="ErrorReason" exists-action="override">
<value>@(context.LastError.Reason)</value>
</set-header>
・・・
<set-body>Response is customized in on-error.</set-body>
</return-response>
</when>
<otherwise />
</choose>
</on-error>
ResponseのStatusCodeが429の場合は499を返却する
わざと1分以内に6回リクエストした
※API Managementでは、Custom の Http StatusCodeは101~599の範囲のみ設定可能
エラー詳細をHeaderへセット(全実装は次ページ)
なぜかReason
を表示してく
れない・・・
171. • エラーの詳細情報をheaderへセットする実装(docsまんまだけど)
Policyで発生したエラーへの対処
<on-error>
<set-header name="ErrorSource" exists-action="override">
<value>@(context.LastError.Source)</value>
</set-header>
<set-header name="ErrorReason" exists-action="override">
<value>@(context.LastError.Reason)</value>
</set-header>
<set-header name="ErrorMessage" exists-action="override">
<value>@(context.LastError.Message)</value>
</set-header>
<set-header name="ErrorSection" exists-action="override">
<value>@(context.LastError.Section)</value>
</set-header>
<set-header name="ErrorStatusCode" exists-action="override">
<value>@(context.Response.StatusCode.ToString())</value>
</set-header>
<base />
</on-error>
Nextscape Inc. 173
• デバッグ用途の実装ならこ
れでいい
• 本番用の実装では逆に詳細
を出力しすぎないようにし
ないといけない
• Sectionの情報は出力し
ない、とか
• 本番用の実装では、エラー
発生時にだけロギングする
仕掛けが必要。<on-error>
でEventHubへの出力を検討
すること
• Storageへの出力だと確
実に出力される保証が
ない
172. • context.Response.StatusCodeを使って、対処方法を分岐させる
• 基本的なエラー処理の方針は、Responseの加工となる(必要なら加工する)
• エラーの発生時のログ出力は通常はAPI側で実装していなければいけないが、APIMでログ出力
をする必要があるならここでEventHubへ出力する実装をする
エラーハンドリングは <outbound>セクション に実装する
Backend APIで発生したエラーへの対処
Nextscape Inc. 174
<outbound>
<base />
<choose>
<when condition="@(context.Response.StatusCode>=500)">
<return-response>
<set-status code="499" reason="ChangeStatusCode" />
<set-body>Error Raised in backend API.</set-body>
</return-response>
</when>
<otherwise />
</choose>
</outbound>
ResponseのStatusCodeが5xx系の場
合は499を返却する
179. • GatewayLogにて出力され
るエラーログはこんな感
じ
ロギング
Nextscape Inc. 181
{
"Level": 4,
"isRequestSuccess": 0,
"time": "2018-11-05T13:57:09.0583391Z",
"operationName": "Microsoft.ApiManagement/GatewayLogs",
"category": "GatewayLogs",
"durationMs": 0,
"callerIpAddress": "13.91.254.72",
"correlationId": “********-****-****-****-************",
"location": "Japan West",
"properties": {
"lastError": {
"source": "validate-jwt",
"reason": "TokenNotPresent",
"message": "JWT not present.",
"scope": "api",
"section": "inbound"
},
"method": "GET",
"url": "https://nsuesaka.azure-api.net/echo/resource?param1=sample",
"responseCode": 401,
"responseSize": 118,
"cache": "none",
"apiId": "echo-api",
"operationId": "retrieve-resource",
"productId": "starter",
"clientProtocol": "HTTP/1.1",
"apiRevision": "1"
},
“resourceId”: “/SUBSCRIPTIONS/*******-****-****-…"
}
エラーを出力したポリシー
ポリシーのスコープ
ポリシーを実装したセクション
• この情報以上は出力できな
いので、例えばこのリクエ
ストは誰?という特定はで
きない
180. • フルカスタマイズしたログを出力したいならこのポリシーを使う
• docsにはREST APIを使用する例しかないが、PowerShellのほうが全然簡単
EventHubへ出力するポリシー
Nextscape Inc. 182
$context = New-AzureRmApiManagementContext `
-ResourceGroupName <resourceGroupName> `
-ServiceName <serviceName>
New-AzureRmApiManagementLogger `
-Context $apimContext `
-LoggerId "LoggerId123" `
-Name “eventHubName" `
-ConnectionString `
"Endpoint=sb://<eventHubNameSpace>.servicebus.windows.net/;SharedAccessKeyName=SendKey;SharedAc
cessKey=<key>" `
-Description “Test logger"
設定先のAPI Management のContextオブジェクトを取得する
任意の文字列
任意の文字列
EventHub名
API ManagementにEventHub対応のロガーを作成
182. Nextscape Inc. 184
• <log-to-eventhub>ポリシーの書き方
• <log-to-eventhub>は<inbound>, <backend>, <outbound>, <on-error>で記載可
EventHubへ出力するポリシー
<inbound>
<base />
<log-to-eventhub logger-id=“<LoggerId>”>@(“文字列ならOK!”)</log-to-eventhub>
</inbound>
<inbound>
<base />
<log-to-eventhub logger-id="LoggerCreatedFromPowerShell">@( string.Join(",",
DateTime.UtcNow, context.Deployment.ServiceName, context.RequestId,
context.Request.IpAddress, context.Operation.Name) )</log-to-eventhub>
</inbound>
• 実装例
• contextから色々引っ張ってくるべし
183. Nextscape Inc. 185
• うまく実装できると、Traceにこんな感じで出力される
EventHubへ出力するポリシー
log-to-eventhub (0.163 ms)
{
"message": "Expression was successfully evaluated.",
"expression": " string.Join(¥",¥", DateTime.UtcNow, context.Deployment.ServiceName, context.RequestId,
context.Request.IpAddress, context.Operation.Name) ",
"value": "11/20/2018 12:13:58 PM,nsuesaka.azure-api.net,545118f2-18db-4044-8ac2-
4d0102fdfcd2,13.91.254.72,Retrieve resource“
}
log-to-eventhub (0.055 ms)
{
"message": "Log to EventHub Policy was successfully processed“
}
185. Nextscape Inc. 187
• ゴリゴリに加工したログ(docsより)
EventHubへ出力するポリシー
<log-to-eventhub logger-id="conferencelogger" partition-id="0">
@{
var requestLine = string.Format("{0} {1} HTTP/1.1¥r¥n",
context.Request.Method,
context.Request.Url.Path + context.Request.Url.QueryString);
var body = context.Request.Body?.As<string>(true);
if (body != null && body.Length > 1024)
{
body = body.Substring(0, 1024);
}
var headers = context.Request.Headers
.Where(h => h.Key != "Authorization" && h.Key != "Ocp-Apim-Subscription-Key")
.Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
.ToArray<string>();
var headerString = (headers.Any()) ? string.Join("¥r¥n", headers) + "¥r¥n" : string.Empty;
return "request:" + "¥n"
+ requestLine + headerString + "¥r¥n" + body;
}
</log-to-eventhub>
187. Nextscape Inc. 189
• Log Analyticsへの結果出力は1時間に1回
ぐらい?すごく遅いので、名前の通り
あくまで分析用
Log Analyticsへ接続する
通信量
189. ユニット、という単位でスケールする
スケーリング
Nextscape Inc. 191
レベル Developer Basic Standard Premium
最大ユニット数 1 2 4 10/リージョン
料金 5483.28/月 16,799.52/月 78,387.84/月 319,064.4/月
7.37/時間 22.58/時間 105.36/時間 428.85/時間
SLA 99.9% 99.9% 99.9% 99.95%
予測最大スループット/ユニットごと 500 Req/Sec 1000 Req/Sec 2500 Req/Sec 4000 Req/Sec
(2018/11 東日本の単価。課金は時間単位)
• 1ユニットの作成には15分~40分かかる
• 従量課金の場合は自動スケールするため対象外
※ユニット=APIMインスタンス、の理解で間違いない
194. Nextscape Inc. 196
パフォーマンスへの影響
• BackendAPIは元のリージョンに1つだけだと、プライマリリージョン以外にデプロイされたAPIMはアクセ
スがある度に異なるリージョンのBackendAPIを叩きに行くため、パフォーマンスが悪い
解決方法は2つ
• BackendAPIも各リージョンへデプロイし、APIMがデプロイされている場所に応じてBackendAPIを切り替え
るようにポリシーを作成する
• キャッシュを使う(キャッシュのセクションを参照)
複数リージョンへのデプロイの懸念点
195. Nextscape Inc. 197
• APIM自身がどのリージョンにデプロイされているかを判定して、
BackendAPIを切り替えるポリシーの例
複数リージョンへのデプロイ
<inbound>
<base />
<choose>
<when condition="@("West US".Equals(context.Deployment.Region, StringComparison.OrdinalIgnoreCase))">
<set-backend-service base-url="http://contoso-us.com/" />
</when>
<when condition="@("East Asia".Equals(context.Deployment.Region, StringComparison.OrdinalIgnoreCase))">
<set-backend-service base-url="http://contoso-asia.com/" />
</when>
<otherwise>
<set-backend-service base-url="http://contoso-other.com/" />
</otherwise>
</choose>
</inbound>
docに記載されているもの
201. キャッシュは2つのポリシーを対で設定する
• キャッシュからデータを取得するポリシー cache-lookup を inboundに設定
• データをキャッシュに格納するポリシー cache-store を outboundに設定
キャッシュ
Nextscape Inc. 203
<inbound>
<base />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false>
<vary-by-header>Accept</vary-by-header>
<vary-by-query-parameter>version</vary-by-query-parameter>
</cache-lookup>
</inbound>
<outbound>
<cache-store duration="seconds" />
<base />
</outbound>
開発者キーごとに応答
をキャッシュするか
開発者グループごとに応
答をキャッシュするか
キャッシュするポリシーを設定
キャッシュを検索す
るポリシー
キャッシュ保持時間を設定
キャッシュする単位。
複数指定可能
202. • キャッシュする場所と数
CDNとの違い
Nextscape Inc. 204
• 事前キャッシュ
CDN
Edgeサーバーが全世界各地に配置してある。Edgeまでの物理的な距離が近いほどEdgeまで
の到達時間は早いためキャッシュしてあればレスポンスは速い。が、キャッシュする場所
が多いということはOriginアクセスも意外と多い
API Management
テナント単位の共有データ キャッシュ。(テナント=作成済みAPIMリソース)が使用され
るため、1リージョン1キャッシュ。複数のユニットにスケールアップしても、同じ
キャッシュ データにアクセス可能。ただし、複数リージョンにデプロイした場合はそれぞ
れのリージョン別のキャッシュとなる
CDN できる。Poralから、もしくはAPIを使用したキャッシュも可能
API Management できない。<outbound>ポリシーでキャッシュするので、事前にはできない
203. • パージ
CDNとの違い
Nextscape Inc. 205
• キャッシュコントロール
CDN
任意のタイミングでキャッシュを削除できる。Poralから、もしくはAPIを使用した削除も
可能
API Management 任意に削除できない。キャッシュ保持経過時間が過ぎるのを待つしかない
CDN
• クエリ文字列ごとに違うキャッシュにできる
• Http-Headerの各種メタごとに違うキャッシュにできる
API Management CDNと同じ
205. • docs記載のシナリオを図解
Nextscape Inc. 207
フラグメントキャッシュとは
1. Request API
2. キー使ってキャッシュに
顧客データがあるかを確認
顧客データ
航空予約システム
4.予約データを取得
3. キャッシュに顧客データが
なかったら、外部からデータ
を取得し、キャッシュへ格納
6. Response
5.取得した予約デー
タの一部のプレース
ホルダに対して、顧
客データをセット顧客データを埋めるプ
レースホルダが予約デー
タにある、という前提が
現実的じゃない
206. フラグメントキャッシュとは
Nextscape Inc. 208
• フラグメントキャッシュポリシーは3つのポリシーを使う
キャッシュから値を取得するポリシーcache-lookup-value
• このポリシーで取得した値は、context.variables[keyName]に格納される
値をキャッシュに格納するポリシーcache-store-value
• key, value, durationを設定する
必要に応じてフラグメントキャッシュを削除する
• cache-remove-value ポリシー
詳しくはこちら
https://docs.microsoft.com/ja-jp/azure/api-management/api-management-caching-policies
207. • 実装例(docsより)
Nextscape Inc. 209
フラグメントキャッシュとは
<inbound>
<cache-lookup-value
key="@("userprofile-" + context.Variables["enduserid"])“ variable-name="userprofile" />
<choose>
<when condition="@(!context.Variables.ContainsKey("userprofile"))">
<send-request mode="new“
response-variable-name="userprofileresponse“ timeout=“10“ ignore-error="true">
・・・
</send-request>
<set-variable name="userprofile“
value="@(((IResponse)context.Variables["userprofileresponse"]).Body.As<string>())" />
<cache-store-value key="@("userprofile-" + context.Variables["enduserid"])“
value="@((string)context.Variables["userprofile"])“ duration="100000" />
</inbound>
“userprofile-xxxx”というキーで
キャッシュから値を取得
値がなかったら
データをキャッシュへ格納
外部からデータを取得して 変数に格納
210. Backup / Restore コマンド
誰もがイメージする Backup/Restoreはこの機能のこと
REST APIもしくはPowerShellコマンドしか用意されていない(Portal に該当機能なし)
バックアップした結果はBlob Storageへ格納される(Blobのリージョンとレベルに注意。
APIMと同じリージョンにLRSで保存するとDR的には意味がないってこと)
Backup時と同じレベル(Dev, Basic, Std, Prem)のインスタンスしかRestoreできない
30日以内のバックアップしか復元できない!(エラーになってしまう)
バックアップを元に新しいAPI Managementインスタンスを作成可能
Nextscape Inc. 212
REST APIを使うためにはAzrueADに
設定が必要。かなり面倒なのでお
勧めしない(PowerShellがいい)
211. Backup / Restore コマンド
Nextscape Inc. 213
PS C:¥> # バックアップ先のStorageのContextを取得する
PS C:¥> $storageContext = New-AzureStorageContext -StorageAccountName "ContosoStorage" -StorageAccountKey $storageKey
PS C:¥>
PS C:¥> # バックアップ実行
PS C:¥> Backup-AzureRmApiManagement `
>> -ResourceGroupName "ContosoGroup02" `
>> -Name "ContosoApi" `
>> -StorageContext $storageContext `
>> -TargetContainerName "ContosoBackups" `
>> -TargetBlobName "ContosoBackup.apimbackup"
PowerShell によるバックアップ(REST APIに比べて圧倒的に簡単!)
• バックアップしてみる
たった2つのコマンドだけ!
212. Backup / Restore コマンド
Nextscape Inc. 214
PS C:¥> # バックアップ先のStorageのContextを取得する
PS C:> $storageContext = New-AzureStorageContext -StorageAccountName "ContosoStorage" -StorageAccountKey $storageKey
PS C:¥>
PS C:¥> # 復元実行
PS C:> Restore-AzureRmApiManagement `
>> -ResourceGroupName "ContosoGroup" `
>> -Name "RestoredContosoApi" `
>> -StorageContext $storageContext `
>> -SourceContainerName "ContosoBackups" `
>> -SourceBlobName "ContosoBackup.apimbackup"
PowerShell による復元方法(こっちも2つのコマンドだけ。書き方ほぼ同じ)
• Restore してみる
• 復元先の API Managementインスタンスはあらかじめ作成しておくこと
• Backup時と同じインスタンスレベル(Dev, Basic, Std, Premium)にすること
• Restoreはめっちゃ時間かかる。15分~45分ぐらい
213. APIの設定に限定した Backup / Restore機能
ImportでAPI のコピーが可能(違うAPIMインスタンスへのImportもOK)
Azure Portal, REST API, Powershellコマンドのいずれかで実行可能
バックアップデータはファイルダウンロードしてローカルに格納
(Azure上に格納ではない)
Importかなり早い(数秒)
API の Export / Import
Nextscape Inc. 215
216. Nextscape Inc. 218
• Open API 3.0形式を選んだ場合、こんな内容ファイルがダウンロードされる
API の Export / Import
{
"openapi": "3.0.1",
"info": {
"title": "Echo API",
"version": ""
},
"servers": [
{
“url”: "https:// .azure-api.net/echo"
}
],
"paths": {
"/resource": {
"get": {
"summary": "Retrieve resource",
"description": "A demonstration of a GET call on a sa
mple resource. It is handled by an "echo" backend which returns a r
esponse equal to the request (the supplied headers and body are being
returned as received).",
"operationId": "retrieve-resource",
"parameters": [
{
"name": "param1",
217. Nextscape Inc. 219
• Import でAPIをコピーしてみる
• titleをImport先の他のAPIと重複しないように修正する
• basePathは他のAPIと重複しても気にしないでいい(Import 時に反映されない)
API の Export / Import
{
"swagger": "2.0",
"info": {
"title": "Echo API2",
"version": "1.0"
},
"host": "nsuesaka2.azure-api.net",
"basePath": "/echo",
"schemes": [
"https"
],
…
「Echo API」→ 「Echo API2」
218. Nextscape Inc. 220
• Blank APIテンプレートで APIを新規に作成する
API の Export / Import
• Display Nameに適当な値を入れる。どうせImportすると上書
きされるので、なんでもOK
• API URL suffix とは前頁の basePathのこと。他APIと重複しない
値を設定する必要がある。(後から変更可能)
222. Nextscape Inc. 224
• Import した APIの定義を見てみる
API の Export / Import
{
"swagger": "2.0",
"info": {
"title": "Echo API2",
"version": "1.0"
},
"host": "nsuesaka2.azure-api.net",
"basePath": "/fuga",
"schemes": [
"https"
],
…
"paths": {
"/resource": {
"get": {
"description": "A demonstration of a GET call on a sample resource. …",
"operationId": "retrieve-resource",
"summary": "Retrieve resource",
"parameters": [
{
"name": "param1",
"in": "query",
"description": "A sample parameter that is required and has …",
API作成時につけた API URL suffix
223. API, Policy, 成果物、グループの設定情報がファイル形式(json)で
APIM内部の Git Repositoryに存在する
Git へ Pushすると、 APIM のRepositoryからAPIMへDeployするらしい
(WebAppsとそっくりな動作だ)
同じインスタンスに対するBackup/ Restoreのみ?
ユーザー、サブスクリプション、仮想ネットワークの接続設定はフ
jsonファイルに書き込まれていない(Restore されない)
Git の Repository を Clone する
Nextscape Inc. 225
229. Nextscape Inc. 231
• ローカルの Clone から、API Managementへ反映
• ローカルから Push し、その後API Management へデプロイする
(Push だけじゃ反映されないので注意!)
Git の Repository を Clone する
Pushしてから
デプロイ
230. Nextscape Inc. 232
異なるAPI Management インスタンスへ Push&Deployできるのか実験
Git の Repository を Clone する
Loggerを全部削除すれば、できるのかも・・・。試していない
• remote 先を 異なる APIM にして git push --force で無理やりPush&Deploy
• Push 先の構成も git clone して、ローカルで置き換えてから Push&Deploy
次の2つを試し
たがNGだった
Notas do Editor OAuth2.0の時、FacebookやAzureADは認可サーバーと呼ばれ、OpenIdConnectの場合はIDProviderと呼ばれる。
意味合い的には同じもの。 カナリアリリースは、新バージョンと旧バージョンの二つの環境をLBなどで少しずつ重みを変えていく手法。
段階的ロールアウトは複数回の新バージョンデプロイがあるのでちょっと違う。似てるけど。 Login-AzureRmAccount
Select-AzureRmSubscription -Subscription Azure機能検証環境 予測最大スループット2(ユニットごと)500 要求/秒1,000 要求/秒2,500 要求/秒4,000 要求/秒