Azureの小ネタ (改)

~Azureネタを中心に、色々とその他の技術的なことなどを~

AzureでNgnixを動かす

Nginx(エンジンエックス)がIISを抜いて第2位のシェアをもつWebサーバーになったらしく、注目を浴びてます。そんなものをAzureで動かそうと思っていたら、すでに数ヶ月前に、たんたかさんのBlogに掲載されていました(nginxをWindows Azureで使ってみる | たんたか)。

とはいえ、ちょと違うアプローチでやってみようと思ったので、そのときのメモ。

Azureらしく

Azureらしくログなどは、ローカルストレージにはき出したいし、診断ログとしてBlobに転送したくなるのはAzurerだと思われますので、とりあえずは、その線で攻めてみましょう。

用意するもの

用意するものは以下。

sedなんですけど、ファイルを書き換えたいためだけに用意します。そういった類のツールならなんでも構わないわけですが、Powershellとかでやるの面倒そうだったので。Gnu Windows版のバイナリは、他のGnu DLLを必要とするので、GNU sed with Oniguruma (Onigsed)を使ってます。試したら、sed.exe単独で動いたので(と思ったけどAzureには配置してないので、本当はなにか必要かも)

ローカルストレージとエンドポイントの設定

ログや一時ファイルを保存するローカルストレージを定義します。以下のような感じ。

次にエンドポイントとして、80番を定義。

サービス構成ファイル

自動化するために、サービス構成ファイルを修正します。ポイントは以下。

  • ProgramEntryPoint でnginxを起動 (なんとなく)
  • スタートアップタスクで、ngnixの設定ファイルを修正するようにする。必要な情報は、RoleInstanceValue経由環境変数としてでコマンドに渡されます。
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="nginx" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRole1" vmsize="Small">
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <Startup>
      <Task commandLine="setup_test.cmd" executionContext="elevated" taskType="simple">
        <Environment>
          <Variable name="xport">
            <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='HttpIn']/@port" />
          </Variable>
          <Variable name="xls">
            <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='LocalStorage1']/@path" />
          </Variable>
        </Environment>
      </Task>
    </Startup>
    <Runtime>
      <EntryPoint>
        <ProgramEntryPoint commandLine="nginx/nginx.exe" setReadyOnProcessStart="true" />
      </EntryPoint>
    </Runtime>
    <Contents>
      <Content destination="nginx">
        <SourceDirectory path="..\nginx-1.1.15" />
      </Content>
    </Contents>
    <Endpoints>
      <InputEndpoint name="HttpIn" protocol="tcp" port="80" localPort="80" />
    </Endpoints>
    <LocalResources>
      <LocalStorage name="LocalStorage1" cleanOnRoleRecycle="false" sizeInMB="1024" />
    </LocalResources>
  </WorkerRole>
</ServiceDefinition>

スタートアップタスク

エミュレータでのデバッグをかねて、スタートアップタスクはデバッグ用に間接的に呼び出します。以下のようにしておくと、コマンドプロンプトが開いた状態で中断するのでデバッグする場合に便利です。


以下、start_test.cmd

start /w setup.cmd

confファイルには、あらかじめ ###logs###などのようにプレースホルダを置いておいて、そこを置換してます。なにか別のうまい方法があるかもしれませんが、とりあえず。パスの区切り文字もスラッシュに変換してますけど本当に必要かは不明。


以下、start.cmd

del  nginx\conf\nginx.conf
set  xlogs=%xls%logs
set  xtemp=%xls%temp

mkdir %xlogs%
mkdir %xtemp%

sed.exe -e "s/##logs##/%xlogs:\=\/%\//g;s/##temp##/%xtemp:\=\/%\//g;s/##port##/%xport%/g" nginx\conf\nginx.conf.org > nginx\conf\nginx.conf


以下、ngnix.confファイル。ログ以外にも以外と、一時フォルダを指定する部分が多くて難儀しました。ただ、このファイルを読む前にログフォルダを作ろうとして警告がでますが、華麗にスルーです。worker_processなんかも、インスタンスサイズから自動的に設定したりするといいんでしょうかね。

worker_processes  1;

working_directory ##temp##;
error_log         ##logs##error.log;
pid               ##logs##nginx.pid;

events {
    worker_connections  1024;
}

http {

    include       mime.types;
    default_type  application/octet-stream;

    client_body_temp_path   ##temp##client_body_temp;
    proxy_temp_path         ##temp##proxy_temp;
    fastcgi_temp_path       ##temp##fastcgi_temp;
    scgi_temp_path          ##temp##/scgi_temp;
    uwsgi_temp_path         ##temp##uwsgi_temp;
    access_log              ##logs##access.log;


    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       ##port##;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

ログの出力

ログの出力は、diagnostics.wadcfg で行っています。エントリポイントを実行ファイルにしてしまったので、OnStartで実行できなくなってしまったので。

<?xml version="1.0" encoding="utf-8" ?>
<DiagnosticMonitorConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2010/10/DiagnosticsConfiguration"
      configurationChangePollInterval="PT1M"
      overallQuotaInMB="4096">
  <Directories bufferQuotaInMB="1000"
     scheduledTransferPeriod="PT1M">
    <CrashDumps container="wad-crash-dumps" directoryQuotaInMB="256" />
    <DataSources>
      <DirectoryConfiguration container="wad-custom" directoryQuotaInMB="256">
        <LocalResource name="LocalStorage1" relativePath="logs"/>
      </DirectoryConfiguration>
    </DataSources>
  </Directories>
</DiagnosticMonitorConfiguration>

エミュレーター上での実行

実行すると一端コマンドプロンプトで停止しますんで、環境変数とかみたり、デバッグっぽいことが可能です。exitで抜けると続行されます。

続行すると、ファイアーウォールの確認画面がでる場合があります。したがって、Azure上で実行するときはFirewallの設定をしないとだめです。今回はエミュレーター上での実行までなので、次回。

で実行されました。エミュレーターでの実行なので、ポート番号がちゃんと81番に書き換わってます。


ログもBLOBに転送されているようです。

問題と考察

デバッグを停止、アプリケーションが削除されてもngnix.exeが死にません。Compute Emulatorを停止しないと、プロセスが死んでくれません。OnStartでngnix.exeを起動して、OnStopでngnix.exe -s stopとかやるアプローチのほうが確実にサーバーを停止できるような気もしますが、せっかくProgramEntryPointがあるのにもったいない気もします。逆にAzure上では、問題にならないかも。
あとは、Webロールアクセラレータみたいに、confファイルの変更に対応したり、最初のコンテンツ展開をBLOBを経由して行ったり、まあ色々用途に合わせて実装してみるとおもしそうですが、用途があるんでしょうか。


まあ、色んなやり方が考えられるので、それを試すのは楽しいですね。実用に耐えるかともかくw。