Azure BlobでのWebサイト公開がつらい


Azure StorageのBlobはだいたいAWS S3と同じだと思っていた。検索をかけてもBlobでサイト公開の記事は結構出てくる。
しかし実際にやってみると知らなかったルールがこれでもかと出てきて結構つらい目にあってしまった。

## 状況

自分は今、あるサイトを運営している。CMSを用いており、これが静的コンテンツとして全てを出力してくれるので現行はnginxだが将来はblobで済ませても良いのではと研究を開始した。

Azure VMでLinuxを用いている。Azureの使用は指定で変更はできない。

とりあえず.NET CoreとLinux版のazcopyをインストールしてストレージアカウントを新たに取得した。blobを利用するにはTableやFile、Queueも利用できるgeneralなアカウントとblob専用のアカウントがあるが、課金額がblob専用ストレージのほうが安いのでとりあえずblob専用ストレージにした。

## 問題1：$rootコンテナにはディレクトリを作れない

Azureのblobストレージへのアクセスは以下のURL形式を用いる。
```
https://アカウント名.blob.core.windows.net/コンテナ名/パス
```

ここでサイトをblobに移行するにはパスがトップの'/'から開始できないといけない。つまりコンテナ名を無くせなければならない。

これには解が用意してあり、`$root`という特殊な名前でコンテナを作成すればコンテナ名無しで`$root`内のコンテンツにアクセスが可能になる。

しかし、ここで問題が出る。URLにはコンテナ名領域が既に含まれているので`$root`コンテナにはディレクトリを作成できないのだ。(正確にはディレクトリで無く、'/'を含むprefixのようだがここでは面倒なので細かい話は置いておく。)

この問題の解としてはDOCUMENT_ROOTの直下にあるディレクトリは全て個別にcontainerを作らなければいけないということらしい。

https://social.msdn.microsoft.com/Forums/azure/en-US/765a4d50-983c-4d88-bad4-807cb1a9f7e7/the-requested-uri-does-not-represent-any-resource-on-the-server?forum=windowsazuredata

理屈で考えればもっともな話でこれはそんなに大した問題ではないだろう。

## 問題2：azcopyは問題1を自動で解決してくれない

しかし、DOCUMENT_ROOT以下を全部blobに上げればそれだけで簡単にWebサイトを構築できると信じていた自分には次なる問題が発生した。Linuxでblobにファイルツリーをコピーするにはazcopyというオフィシャルなツールが最も簡単に思われるのだが、このツールは問題1のルールを考慮してはくれない。つまりazcopyで以下の要領でDOCUMENT_ROOT以下全てをblobの$rootコンテナにコピーしようとすると、

```
azcopy --source /var/www/html   --destination 'https://アカウント名.blob.core.windows.net/$root' --dest-key 'アクセスキー' --set-content-type --exclude-older --recursive
```

次のようなエラーが発生する。


>The requested URI does not represent any resource on the server.


実際にはこのエラーにより問題1の存在を知った訳であるが前後した。

問題1の解にて説明した通り、この問題を解決するには個々のディレクトリの名前でコンテナを作成する必要がある。しかし、azcopyはそれを自動ではやってくれない。

幸い、Azure storageには全てREST APIが用意されており、コンテナを作ることも可能だ。だからプログラマならチャッチャとコードを書いてコンテナを作ってはそのコンテナに対しazcopyを実行するプログラムを作るべきだろう。後でやるんじゃないかな。

## 問題3：コンテナ名の制約がきつい。

個別にコンテナを作る必要があるのなら仕方がない。ポータルにてそうしようとした瞬間に次の問題に突き当たった。

コンテナ名の制約が厳しいのだ。

Azure Blobのコンテナ名の制約は次の通りだ。


>This name may only contain lowercase letters, numbers, and hyphens, and must begin with a letter or a number. Each hyphen must be preceded and followed by a non-hyphen character. The name must also be between 3 and 63 characters long.


つまり、コンテナ名は小文字、数字、ハイフンのみで小文字か数字で始まり、ハイフンは必ずそれ以外の文字で挟まなければならず、3文字から63文字以内でなければならない。

ディレクトリ名にはそんな制約が無いから見事に既存のディレクトリがこれに引っ掛かった。アンダースコアが用いられているのだ。もうしょうがないからディレクトリ名を変えるしかないのだが、そうすると既存のHTML全てからリンク先も変更が必要になる。そんなものシェル芸人なら1行だろう。しかし、段々と嫌な気分になってきた。


##問題4：デフォルトファイルが指定できない

これがもう最悪な問題でここでかなり断念した。

Azure Blobではブラウザ側で明にURLにてindex.htmlを指定しないとindex.htmlが表示できない。

https://feedback.azure.com/forums/217298-storage/suggestions/6417741-static-website-hosting-in-azure-blob-storage

ユーザ達がコメント欄で激怒しているのが良くわかる。

1月からずっと放っておかれているのでやる気が無いのじゃなかろうか。

回避策としてはAzure CDNを使えとある。CDNを用いることはメリットも大きいし悪い話ではないのだが、なぜこんな簡単なことが実現できないかと言えば、やはりCDNを使わせたいのではと感じる。

##問題5：カスタムドメインにてSSLが利用できない

これも現状、最悪である。

https://feedback.azure.com/forums/217298-storage/suggestions/3007732-make-it-possible-to-use-ssl-on-blob-storage-using

サイトを公開するならカスタムドメインの使用とSSLの使用は当たり前になりつつある。もう直ぐにChrome 62が公開されてHTTPのフォームは危険だと表示されてしまう。おまけにService Worker等の新しい機能はSSLでの配信以外では動作しない。

回避策としてはこれもAzure CDNを使えということだがユーザ達は納得していない。無料で使えるなら誰も文句は言わないだろうが余計な出費がかかるのでは納得いかないものがあるだろう。

##結論

そんなこんなでAzure Blobだけで静的なWebサイトを公開するのはかなり面倒が多い。CDNの使用を前提にできないのであればあまりお勧めできない。逆にCDNを使うのは当たり前だろうというアクセスの多い人達には問題にならないだろう。

小さなVMかコンテナでSSLとリダイレクトを担当させるのもありかと思ったけど運用が減らないのが悲しい。リバースプロキシだと転送量による課金をBlobで無料化という恩恵が無くなってしまって意味がない。

念の為、確認してみたがAWS S3ではこれらの問題は存在しない。

AWS S3でのURL構成は以下の通りであり、pathにコンテナ名のようなものは存在しない。
`<bucket-name>.s3-website-<AWS-region>.amazonaws.com`

index.htmlは指定しなくても表示される。
http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/WebsiteHosting.html

カスタムドメインにてSSLの利用が可能である
https://blog.majimena.co.jp/tech/2016/03/31/aws-ssl.html

AzureにはAzureの優れている点もあるだろうし、これだけでAWSが優れているという気はない。しかし、Blobに関してはS3のほうが便利だと言わざるを得ない。MicrosoftにはMicrosoftのビジネス上の戦略があるのだろうけど、できれば早々にこれらの問題を改善して欲しい。

##警告：azcopyでは--set-content-typeを忘れないように

最後にちょっとだけazcopyの話。
最初に嵌ったのだけど、blobはファイルのMIMEをデフォルトでoctet/streamにするそうで、azcopyは--set-content-typeを指定しないとそちらにしてしまう。するとブラウザでhtmlが描画されずにダウンロードされるという情けない結果になる。--set-content-typeを指定して初めてファイルの拡張子に従って自動で設定してくれるのだけれども、デフォルトの挙動設計が間違っている気がしてならない。

この他にもazcopyはincludeで個別ファイル名を指定するとか、そのくせexcludeはタイムスタンプしか無いとか非常にオプション関係の設計が良くなく感じる。当たり前だがUnixのコマンドらしくない。.NET Coreが必須というのも頂けないので誰かが代わりとなるものを作ればあっという間に駆逐されるような感じだ。誰かLinux向けでcliの良いものを作ってください。

## 最後に

誤解や誤りがあるかと思いますがご指摘頂ければと思います。特に回避策は喜びます。