tech.guitarrapc.cóm

Technical updates

Azure Web App のカスタムデプロイを使って特定のディレクトリをGithubと同期する

Azure で一番好きなサービスはダントツで Web App です。以前は Azure WebSite と呼ばれていました。やりすぎず、でも必要なことはできる。このバランス感が今でも崩れていないのはすごいです。

さて、Azure は各種SCM からのデプロイをサポートしており、そのデプロイ動作も制御できます。

今回は、カスタムデプロイを使って特定の GitHub ディレクトリと Web App のディレクトリを同期する方法を見てみましょう。

目次

何をするの

先日紹介した、OneGetのプライベートソースがありました。これを使ってデプロイの制御を行います。

tech.guitarrapc.com

github.com

次の流れでやってみましょう。

  1. Web App を起動しよう
  2. GitHub から継続的デプロイ連携*1を組む
  3. Kudu による状態確認
  4. push した変更の継続的デプロイ確認
  5. 一度連携を外して、別ブランチと連携し特定のディレクトリのみ同期する

継続的デプロイと合わせて、Kudu を使ってどう変化するか見てみましょう。

Web App を起動しよう

新ポータルで行います。旧ポータルでも余り操作は変わらないのでさくっと行きましょう。

新ポータルへ

https://portal.azure.com にアクセスして、

portal.azure.com

マイクロソフトアカウント、あるいは組織アカウントでログインします。ユーザー名のドメインに応じてそれぞれの画面にリダイレクトされます。

以前は旧ポータルがデフォルトでしたが、今ならログインすると新ポータルが見えると思います。

もし旧ポータルの場合は、右上のアカウントから Switch to Azure Preview Portal を選べばいいでしょう。

ログインでいつもはまるアレ

https://portal.azure.com は、ユーザー名のドメインに応じたリダイレクトをするのです。

が、Chromeだとちょくちょくリダイレクトに失敗してこけてそのまま身動きとれなくなるのはんぱなくストレスなのでいい加減直してほしいですね。*2

あと、Edge だとちょくちょく # のみになったり、突然 Office365 にリダイレクトしようとして組織アカウントじゃないので起こられたりするのも。

Chromeの場合は、プライベートセッションにするとだいたいいけます。Edge は、もう一度アドレスに https://portal.azure.com/?l=en&r=1 を入れると今度は入れるのでほげー。

Web App を新規作成

今回はリソースマネージャーも使わず簡単にいきます。

New > Web + Mobile > Web App

おしまい。Virtual Machine や AWS EC2 よりここは圧倒的に楽です。

今回は、AppName (つまりアクセス時のアドレスのためのサブドメイン)にguitarrapc-OneGet をつけました。これにより Web App の公開アドレスは https://guitarrapc-OneGet.azurewebsites.net となります。

作成直後から、

30秒程度で利用可能になりました。いい素早さですね。

GitHub から継続的デプロイ連携を組む

旧ポータルでの Web App作成だと、SCM*3デプロイの設定も作成時にでましたが完全ポータルでは今回出ませんでした。そこで、起動してある Web Appsに途中からデプロイを組んでみましょう。とはいえ、Jenkins や Circle CI も組む必要はなく、Visual Studio からの発行を行う必要もありません。

さっそく、Web App の下にスクロールします。

Set up continuous deployment というのがあり、ここが SCM からの継続的デプロイ設定箇所です。開いてみましょう。

Microsoft Azure > guitarrapc-oneget > Continuous Deployment > Choose source といくと、連携可能なSCM一覧がでてきました。

Azure Web Appは、Visual Studio Online 以外にも、git, GitHub, BitBucket, DropBpx, その他外部リポジトリ など各種SCMからの継続的デプロイが構成可能です。

今回は GitHub との継続的デプロイを選択します。するとリポジトリのWebHook を受け取るため GitHub アカウントとの連携を求められるので、Authorize から連携するユーザーで認証してください。*4

認証が終わったら、Choose Project からデプロイするリポジトリを選択します。

今回は MyOneGetServer ですね。

最後に、ブランチを master にして完了です。

連携直後に最新コミットが自動デプロイされる

設定が完了すると、10秒ほど読み込みして

現在の状態が表示されます。リポジトリにまだ push していないので、継続的デプロイは実行されていません。と思いきや??

継続的デプロイを設定すると直後に最新コミットがデプロイされます。

結果、先ほどの Web App のアドレスにアクセスすると、見事に表示されます。簡単素敵。

デプロイのログ

デプロイの結果を選択すると、デプロイ履歴が表示されます。履歴を選択すると、そのログが表示されます。さらにビルドログを選択することで詳細が表示されます。

これをみることで、nuget restore の状態、MSBuild がいつ、どう動いたのかなどはログで簡単に追うことができます。

Kudu による状態確認

Azure Web App が、PaaS として強力なのはもちろんですが、なにより Kudu の存在が大きいでしょう。

github.com

Kudu は Azure Web App の git デプロイの裏のエンジンです。が、それだけでなく、Kudu を使うことで、Azure Web App の中身や裏で何が起こっているのか。今どんな状態なのかまで、あたかも IaaS を扱うごとく容易に把握できます。

Kudu Services にアクセス

早速Kudu のポータルにアクセスします。Wiki にかかれている通り、公開されている Web App アドレスのドメインに .scmを差し込むだけです。

github.com

例えば、https://guitarrapc-oneget.azurewebsites.net/ なら、 https://guitarrapc-oneget.scm.azurewebsites.net/となります。

見えましたね?

PowerShell でのデバッグコンソール

Kudu を使うことで、cmd か PowerShell をデバッグコンソールで扱えます。もちろんファイル操作もある程度可能です。

早速 PowerShell のデバッグコンソールを開きましょう。Debug Console > PowerShell と進みます。

PowerShell コンソールが ブラウザ上に表示されました。

表示されているパスが、基点となります。

Azure Web App の構成

ざっくり環境変数を見れば概要はつかめます。

gist.github.com

通常良く見る C:\ がシステムドライブな構成 とは異なり、D:\ が基本です。あとは、もろもろ見えますが、それほど大きな差異はなくふつうに扱えますね。

Name                           Value                                           
----                           -----                                           
ALLUSERSPROFILE                D:\local\ProgramData                            
APP_POOL_CONFIG                C:\DWASFiles\Sites\#1guitarrapc-OneGet\Config...
APP_POOL_ID                    ~1guitarrapc-OneGet                             
APPDATA                        D:\local\AppData                                
APPSETTING_ScmType             GitHub                                          
APPSETTING_WEBSITE_AUTH_ENA... False                                           
APPSETTING_WEBSITE_NODE_DEF... 0.10.32                                         
APPSETTING_WEBSITE_SITE_NAME   guitarrapc-OneGet                               
aspnet:DisableFcnDaclRead      true                                            
aspnet:PortableCompilationO... true                                            
aspnet:PortableCompilationO... Microsoft.Web.Compilation.Snapshots.SnapshotH...
AZURE_JETTY9_CMDLINE           -Djava.net.preferIPv4Stack=true -Djetty.port=...
AZURE_JETTY9_HOME              D:\Program Files (x86)\jetty-distribution-9.1...
AZURE_TOMCAT7_CMDLINE          -Dport.http=%HTTP_PLATFORM_PORT% -Djava.util....
AZURE_TOMCAT7_HOME             D:\Program Files (x86)\apache-tomcat-7.0.50     
AZURE_TOMCAT8_CMDLINE          -Dport.http=%HTTP_PLATFORM_PORT% -Djava.util....
AZURE_TOMCAT8_HOME             D:\Program Files (x86)\apache-tomcat-8.0.23     
branch                         master                                          
CommonProgramFiles             D:\Program Files (x86)\Common Files             
CommonProgramFiles(x86)        D:\Program Files (x86)\Common Files             
CommonProgramW6432             D:\Program Files\Common Files                   
COMPUTERNAME                   RD00155E002C72                                  
ComSpec                        D:\Windows\system32\cmd.exe                     
deployment_branch              master                                          
DEPLOYMENT_SOURCE              D:\home                                         
DEPLOYMENT_TARGET              D:\home\site\wwwroot                            
DIRCMD                         /OG /ON                                         
EnableNuGetPackageRestore      true                                            
FP_NO_HOST_CHECK               NO                                              
GO_WEB_CONFIG_TEMPLATE         C:\Program Files (x86)\SiteExtensions\Kudu\47...
HOME                           D:\home                                         
HOME_EXPANDED                  C:\DWASFiles\Sites\#1guitarrapc-OneGet\Virtua...
HOMEDRIVE                      D:                                              
HOMEPATH                       \home                                           
JAVA_HOME                      D:\Program Files\Java\jdk1.7.0_51               
KUDU_SELECT_NODE_VERSION_CMD   node "C:\Program Files (x86)\SiteExtensions\K...
KUDU_SELECT_PYTHON_VERSION_CMD python "C:\Program Files (x86)\SiteExtensions...
KUDU_SYNC_CMD                  kudusync                                        
LOCAL_EXPANDED                 C:\DWASFiles\Sites\#1guitarrapc-OneGet          
LOCALAPPDATA                   D:\local\LocalAppData                           
MSBUILD_PATH                   D:\Windows\Microsoft.NET\Framework\v4.0.30319...
NPM_JS_PATH                    D:\Program Files (x86)\npm\1.4.28\node_module...
NUGET_EXE                      C:\Program Files (x86)\SiteExtensions\Kudu\47...
NUMBER_OF_PROCESSORS           8                                               
OS                             Windows_NT                                      
Path                           C:\Program Files (x86)\SiteExtensions\Kudu\47...
PATHEXT                        .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;....
POST_DEPLOYMENT_ACTION         postdeployment                                  
POST_DEPLOYMENT_ACTIONS_DIR    D:\home\site\deployments\tools\PostDeployment...
PROCESSOR_ARCHITECTURE         x86                                             
PROCESSOR_ARCHITEW6432         AMD64                                           
PROCESSOR_IDENTIFIER           AMD64 Family 16 Model 8 Stepping 1, AuthenticAMD
PROCESSOR_LEVEL                16                                              
PROCESSOR_REVISION             0801                                            
ProgramData                    D:\local\ProgramData                            
ProgramFiles                   D:\Program Files (x86)                          
ProgramFiles(x86)              D:\Program Files (x86)                          
ProgramW6432                   D:\Program Files                                
PROMPT                         $P$G                                            
PSModulePath                   WindowsPowerShell\Modules;D:\Program Files (x...
PUBLIC                         D:\Users\Public                                 
REGION_NAME                    Japan West                                      
REMOTEDEBUGGINGBITVERSION      vx86                                            
REMOTEDEBUGGINGPORT                                                            
REWRITETABLE                                                                   
SCM_BUILD_ARGS                                                                 
SCM_COMMAND_IDLE_TIMEOUT       60                                              
SCM_DNVM_PS_PATH               C:\Program Files (x86)\SiteExtensions\Kudu\47...
SCM_GIT_EMAIL                  windowsazure                                    
SCM_GIT_USERNAME               windowsazure                                    
SCM_LOGSTREAM_TIMEOUT          1800                                            
SCM_TRACE_LEVEL                1                                               
ScmType                        GitHub                                          
SITE_BITNESS                   x86                                             
SystemDrive                    D:                                              
SystemRoot                     D:\Windows                                      
TEMP                           D:\local\Temp                                   
TMP                            D:\local\Temp                                   
USERDOMAIN                     WORKGROUP                                       
USERNAME                       RD00155E002C72$                                 
USERPROFILE                    D:\local\UserProfile                            
WEBJOBS_DEPLOY_CMD             deploy_webjobs.cmd                              
webpages:Enabled               true                                            
webpages:Version               3.0.0.0                                         
WEBROOT_PATH                   D:\home\site\wwwroot                            
WEBSITE_AUTH_ENABLED           False                                           
WEBSITE_COMPUTE_MODE           Shared                                          
WEBSITE_HOSTNAME               guitarrapc-oneget.azurewebsites.net             
WEBSITE_HTTPLOGGING_ENABLED    0                                               
WEBSITE_IIS_SITE_NAME          ~1guitarrapc-OneGet                             
WEBSITE_INSTANCE_ID            24db6f195ef296845321623d224468be044afda818348...
WEBSITE_NODE_DEFAULT_VERSION   0.10.32                                         
WEBSITE_OWNER_NAME             f6f639a1-58f5-4277-ac58-ee8bd698878d+japanwes...
WEBSITE_SCM_ALWAYS_ON_ENABLED  0                                               
WEBSITE_SCM_SEPARATE_STATUS    1                                               
WEBSITE_SITE_MODE              Limited                                         
WEBSITE_SITE_NAME              guitarrapc-OneGet                               
WEBSITE_SKU                    Free                                            
WEBSOCKET_CONCURRENT_REQUES... 5                                               
windir                         D:\Windows                                      
windows_tracing_flags                                                          
windows_tracing_logfile                    
継続的デプロイによるクローン先パス

リポジトリがクローンされるパスを知っておくのは、デプロイを弄るうえで大事です。

起点になるのは、$env:Home\Site\repository となります。

PS D:\home> ls site


    Directory: D:\home\site


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            deployments                       
d----          9/8/2015   4:57 PM            locks                             
d----          9/8/2015   4:24 PM            repository                        
d----          9/8/2015   4:24 PM            wwwroot   

この辺の環境変数と合致しないのが腑に落ちないので、詳しい人の解説が欲しいですね。Kudu で監視する限り、Repository パスにClone しているようにみえるのですが...。

deployment_branch              master                                          
DEPLOYMENT_SOURCE              D:\home                                         
DEPLOYMENT_TARGET              D:\home\site\wwwroot   
アプリケーションが稼動するサイトパス

こちらもデプロイをいじるうえで必須です。

$env:\WEBROOT_PATH

となります。

デプロイによるリポジトリの変化をみる

パスがわかればこっちのものです。デプロイによって、クローンされたパスにファイルがあるか確認できますね。

先ほどのデプロイの時間と

クローンされたパスの時間、ビルド生成物をみれば一致していることがわかります。

gist.github.com

PS D:\home> ls site/repository/MyOneGetServer


    Directory: D:\home\site\repository\MyOneGetServer


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            App_Readme                        
d----          9/8/2015   4:24 PM            bin                               
d----          9/8/2015   4:24 PM            DataServices                      
d----          9/8/2015   4:24 PM            obj                               
d----          9/8/2015   4:24 PM            Properties                        
-a---          9/8/2015   4:24 PM       1952 Default.aspx                      
-a---          9/8/2015   4:24 PM      35561 favicon.ico                       
-a---          9/8/2015   4:24 PM       9588 MyOneGetServer.csproj             
-a---          9/8/2015   4:24 PM        973 packages.config                   
-a---          9/8/2015   4:24 PM       5582 Web.config                        
-a---          9/8/2015   4:24 PM       1299 Web.Debug.config                  
-a---          9/8/2015   4:24 PM       1360 Web.Release.config                


PS D:\home> ls site/repository/MyOneGetServer\bin


    Directory: D:\home\site\repository\MyOneGetServer\bin


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            roslyn                            
-a---          9/8/2015   4:24 PM     176128 Elmah.dll                         
-a---          9/8/2015   4:24 PM      29344 Microsoft.CodeDom.Providers.DotNet
                                             CompilerPlatform.dll              
-a---          9/8/2015   4:24 PM       1805 Microsoft.CodeDom.Providers.DotNet
                                             CompilerPlatform.xml              
-a---          9/8/2015   4:24 PM      45416 Microsoft.Web.Infrastructure.dll  
-a---          9/8/2015   4:24 PM      81600 Microsoft.Web.XmlTransform.dll    
-a---          9/8/2015   4:24 PM       5120 MyOneGetServer.dll                
-a---          9/8/2015   4:24 PM       5582 MyOneGetServer.dll.config         
-a---          9/8/2015   4:24 PM      11776 MyOneGetServer.pdb                
-a---          9/8/2015   4:24 PM     126976 Ninject.dll                       
-a---          9/8/2015   4:24 PM     384512 Ninject.pdb                       
-a---          9/8/2015   4:24 PM     325908 Ninject.xml                       
-a---          9/8/2015   4:24 PM     532656 NuGet.Core.dll                    
-a---          9/8/2015   4:24 PM      49840 NuGet.Server.dll                  
-a---          9/8/2015   4:24 PM      26624 RouteMagic.dll                    
-a---          9/8/2015   4:24 PM      10752 WebActivatorEx.dll       
デプロイによるサイトの変化をみる

同様にサイトもわかりますね。リポジトリの生成物と一致しているはずです。

gist.github.com

PS D:\home> ls site/wwwroot


    Directory: D:\home\site\wwwroot


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            App_Readme                        
d----          9/8/2015   4:24 PM            bin                               
d----          9/8/2015   4:24 PM            DataServices                      
-a---          9/8/2015   4:24 PM       1952 Default.aspx                      
-a---          9/8/2015   4:24 PM      35561 favicon.ico                       
-a---          9/8/2015   4:24 PM        973 packages.config                   
-a---          9/8/2015   4:24 PM       5569 Web.config                        


PS D:\home> ls site/wwwroot/bin


    Directory: D:\home\site\wwwroot\bin


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            roslyn                            
-a---          9/8/2015   4:24 PM     176128 Elmah.dll                         
-a---          9/8/2015   4:24 PM      29344 Microsoft.CodeDom.Providers.DotNet
                                             CompilerPlatform.dll              
-a---          9/8/2015   4:24 PM      45416 Microsoft.Web.Infrastructure.dll  
-a---          9/8/2015   4:24 PM      81600 Microsoft.Web.XmlTransform.dll    
-a---          9/8/2015   4:24 PM       5120 MyOneGetServer.dll                
-a---          9/8/2015   4:24 PM     126976 Ninject.dll                       
-a---          9/8/2015   4:24 PM     532656 NuGet.Core.dll                    
-a---          9/8/2015   4:24 PM      49840 NuGet.Server.dll                  
-a---          9/8/2015   4:24 PM      26624 RouteMagic.dll                    
-a---          9/8/2015   4:24 PM      10752 WebActivatorEx.dll                

意図通りにデプロイされていますね。

push した変更の継続的デプロイ確認

さて、継続デプロイということは、SCMと連携して変更が自動的に Web App にデプロイされるはずです。

GitHub の場合は、Webhook を使っており、push のイベントを受けると自動デプロイされます。早速、master にテストコミットをします。*5

これを push すると?

即座に Github から Webhook が飛び、Web App でデプロイが開始されました。

デプロイが完了して、Active なバージョンが更新されていますね。

Kudu での確認

kudu で変更が反映されたか見てみましょう。

意図した通り、変更した Readme.md のみが更新されています。

PS D:\home> ls site/repository


    Directory: D:\home\site\repository


Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
d----          9/8/2015   4:24 PM            MyOneGetServer                    
d----          9/8/2015   4:24 PM            MyOneGetServer.Script             
d----          9/8/2015   4:24 PM            packages                          
-a---          9/8/2015   4:24 PM       2581 .gitattributes                    
-a---          9/8/2015   4:24 PM       3412 .gitignore                        
-a---          9/8/2015   4:24 PM        224 .gitmodules                       
-a---          9/8/2015   4:24 PM       1526 MyOneGetServer.sln                
-a---          9/8/2015   5:16 PM        393 README.md    

一度連携を外して、別ブランチと連携し特定のディレクトリのみ同期する

さてようやく本題です。

このGitHub での継続デプロイは、このままだと Web Siteのコンテンツが Github のビルド結果と同一になってしまいます。

しかし現実にはこれでは困るシーンがいくつかあります。

  • WordPress をホスティングしており、一部のWordPress管理外の静的ファイルのみ Github と連動させたい場合は、丸ごとGitHubと同期されては困ります
  • デプロイのフローに特殊な処理をはさみ込みたい場合も、デプロイを調整したくなるでしょう。
  • プライベートNuGet サーバーからライブラリを取得してきたい

そこで出てくるのが、カスタムデプロイです。

参考

参考にするのは、Kudu の Wiki と Channel 9 です。

github.com

Azure ビデオ | Microsoft Azure

Custom Deploy の概要

どこのサイトをみてもざっくりと誰も書いていないのですが、単純です。*6

  • リポジトリ直下に .deployment ファイルがあると、そこの[config]属性に書かれたcommand= のコマンドを実行します
  • .deploymentから、別のcmdや.ps1を呼び出すことも可能でより複雑な処理を書くこともできます

たったこれだけです。早速 MyOneGetServer のリポジトリで、試してみましょう。

カスタムデプロイ構成

やることは単純です。

  • MyOneGetServer.Scriptフォルダ を、wwwroot 直下に同期する

これを行うために以下の構成にします。

  • .deployment ファイルを作成する
  • BuildScript\deploy.ps1 で同期処理を記述する
  • 一度継続的デプロイ連携を切る
  • 変更を push する
  • 再度継続デプロイを連携してカスタムデプロイの確認

この .deployment に記述された内容だけ実施されるのはとても大事です。何しろ 継続的デプロイ連携をした瞬間にデプロイされるので、事前に .deployment の内容だけ実施されるとわかっていれば、運用中のアプリを壊す心配がなくなります。

.deploy ファイルの作成

とりあえず、さくっと作ります。

gist.github.com

できましたね。

続いて、BuildScript\deploy.ps1 を実行するようにします。

gist.github.com

今回のように、PowerShell スクリプトを実行する場合は、-ExecutionPolicy RemoteSigned を指定しましょう。でないと、デフォルトがRestricted になっており、.ps1 が実行できません。

BuildScript\deploy.ps1 の記述

続いて、BuildScript\deploy.ps1 を作成します。

gist.github.com

同期内容を記述します。

同期処理には注意です。Azure Web Apps のデプロイ過程では、Web Deployment を呼び出してもエラーが出ます。

kudu でも実行できないことが確認できます。

そのため、PowerShell で頑張らずとも、ここは RoboCopy を使って同期処理を書きましょう。

gist.github.com

ポイントは、try{}catch{} での exit処理です。PowerShell は、cmd などからの外部呼び出しでふつーにエラーをだしても終了コードは1のままです。

そこで、明示的にエラーを外部に通知するためには、終了コードとともに終了させる必要があります。*7今回は、エラーを起こす可能性のある、RoboCopy の処理でのエラーを捉えるようにしています。

これで準備は完了しました。

一度継続的デプロイ連携を切る

それでは、すでに存在するアプリケーションでの SCM連携を模擬するため、一度連携を切ります。

連携は、Disconnectを選択することで切れます。

実行すると無事に切れましたね。

連携を切っても、連携してたアプリが消えるわけではないので安心してください。

変更を push する

それでは、先ほどの .deploymentBuildScript\deploy.ps1 を push します。

github.com

再度継続デプロイを連携してカスタムデプロイの確認

連携したときのデプロイが、「カスタムデプロイがない時のビルド結果のデプロイ」ではなく「deploy.ps1 で記述したカスタムデプロイ処理のみ実施」されればokです。

さくっと連携します。

デプロイがキックされます。

ログを見ると、うまくいってそうですね。

kudu でみると、連携前のフォルダ構成に、

意図した通りMyOneGetServer.Script が増えていますね。

まとめ

いかがでしたでしょうか? Azure Web App 最高ですね。継続的デプロイが CIいらずでここまで簡単にできるのは、.NET ではなかなか難しいものです。AppVeyor とかもふつーにCIでつらいですしね。

カスタムデプロイやkudu での確認も簡単です。

ぜひぜひ使ってみてください。

*1:継続的デリバリーでもどっちでも表現はお好きに

*2:Azure が利用できるようになってから数年たってもなおらないしクッキー、セッション削除でもダメなの辛い

*3:Source Code Management : ソースコードの管理基盤を指します。git やGitHub、Subversion が有名ですね

*4:新ポータルでは新しいウィンドウを開くことなく連携できますが、旧ポータルでは別ウィンドウが裏で開くので気づき辛いのが悲しいですね

*5:本当は別ブランチでやりたかったけどいいです

*6:こういう基礎中の基礎を誰も書いてないの謎

*7:throw ではだめということです