このブログについて

ソース: https://github.com/hMatoba/tetsujin

 ブログのバックエンドを作るのが趣味になっているような気がする。
 はじめはPython on GoogleAppEngineで作って、それのデータベースをNoSQLにしてみたらどうだとか始めた。そのあとでTornadoやFlask、BottleといったPythonのフレームワークをいろいろやってみて、Node.jsはasync,awaitが入る前にやってちょっときついと思ってストップした。
 その後WPFで好きだったC#で作ってみたくなって、.NET Coreのリリース直後にAzureに乗るものを作った。そして今回、.NET Core 2.0のリリースがあったのを契機にDocker運用するものに作り直した。

 ASP.NET Core MVCを使ってみた感想。モデル、ビュー(Razor)、コントローラのみを使うような、SinatraやFlaskのような、うっすい使い方もできる。スキャフォルディングとかあるけど無理に使う必要はない。フレームワークに縛られてるような感覚は強くない。その点、うまくやれるかは書き手次第。


 このブログはタグに並べた要素が全部盛りで実装されている。
 C#が好きなので、それがオープンソースになったのがうれしかった。だからほかのオープンソース技術をメインに組み合わせて、C#はMSの用意したセット(WindowsOSでIISとかSQLServerとかって構成)じゃないと使えないなんてことはないというのを実践してみたかった。DebianでNginx、MongoDBなどって構成。
 使用サービスの構成としては、Github、TravisCI、Docker Hub、Hyper.shが主なところ。
・Github→バージョン管理
・TravisCI→CIサービス
・Docker Hub→ビルドしたコンテナイメージの置き場所
・Hyper.sh→Docker開発者が「これほぼDockerやんけ」と言うぐらいにDockerライクなコンテナホスティングサービス。もちろんDockerで作ったコンテナをホスティングできる
 使用サービスの構成はシンプルなほうがいいという一般論も存在するだろうけど、これはこれで楽をするための構成を敷いている。Dockerを使ったサーバの構成管理、自動テスト、CIサービス上での自動テストからの自動デプロイなどなど。アプリの開発継続に注力できる。

 Githubへのプッシュで、TravisCIでのビルド、テストが行われる。テストにパスしてなおかつブランチがmasterだったらならDocker Hubへビルドされたコンテナイメージがアップされる。そののち、Hyper.shでそのコンテナイメージをプル、デプロイまでのコマンドが自動で流れる。Docker Hubを経由しているので、コンテナイメージがビルドの産物として残るようになっている。


・運用お値段
 VPSは安い。でもOSの管理が必要だからヤダ。AWSとかAzureのイカしたPaasを使いたいけど高い。
 DockerコンテナホスティングをしてくれるHyper.shというサービスがあり、これで月2000円以下で運用できている。リバースプロキシコンテナ+アプリコンテナの2コンテナをランニングさせるコスト1000円に加え、個人的にいろんなアプリで使うMongoDBを仕方なくVPSで運用しているコスト600円程度となっている。VPSとメジャーどころのPaasの中間あたりのお値段。
 問題としてHyper.shが日本にもアジアにもリージョンがないことによるレイテンシがががというのがあるけども妥協。


・構成
 ファイルはAzureStorage
 ↑にも書いたけどコンテナホスティングで運用していて、走っているコンテナはリバースプロキシ用一台とアプリ用一台
 証明書はLet's Encrypt
 コンテナのOSは、リバースプロキシのほうがAlpine、アプリはDebian
 テストはxUnitによる単体テストが実行できるようにしてある
 それに加えてSeleniumでのE2Eテストも用意してある。Seleniumは、一度C#で動かしたらきつかったのでPythonで動かしている
 バージョン管理はGithub
 Githubへのデプロイで、TravisCIでのデプロイが走る
 TravisCIではとりあえずSeleniumでE2Eテストをやっている
 TravisCIでテストに成功したら、そこからHyper.shへ自動デプロイが始まる
 ただし自動デプロイが始まるのはmasterブランチへのコミット時のみ


 いろいろ構成していて半分は趣味で試したかったものであるが、同時にデプロイの自動化という一年前から突き詰めたかった課題への挑戦もしている。これのおかげで、今後このブログエンジンを改良していくのにせっせと手作業でデプロイをする必要がなくなった。


・なおしたい点
 dotnetコマンドが現状docker composeを使ったプロジェクトをビルドしてくれない。だから一部プロジェクトでCI環境でのビルドができず、開発環境でビルドしたものをDocker Hub経由で使っている。CI環境でビルドができないのはSDKの不足が原因。そのSDKはVisualStudio Communityに用意されているのだが、SDKがオープンソースになっていないという理由でdotnet CLIに入れるかどうかもたついている。
'17/9/28追記
docker composeが入っているプロジェクトのビルド方法を変えて対応完了

comment: 0

遠隔でMongoDBサーバのバックアップを取る2

 前回に構築したMongoDBサーバの自動遠隔バックアップシステムを削除し、新しくバックアップシステムを組んだ。今回はMongoDBサーバでバックアップコマンドを実行し、それをAzure Blob StorageにPythonでアップするという流れ。

backup.sh

#!/bin/sh
cd /home/h/backup
mongodump --host foo -u bar -p boo
tar zcvf dump.tar.gz dump
python3.5 /home/h/up.py
rm -rf *


up.py

from azure.storage.blob import BlockBlobService
import datetime

account = "xxx"
key = "yyy"
block_blob_service = BlockBlobService(account_name=account, account_key=key)
filename = datetime.datetime.now().strftime("%Y%m%d")
block_blob_service.create_blob_from_path("mongo", filename, "dump.tar.gz")



comment: 0

Azure Storageエミュレータのエミュレータを作ってみた

 開発環境で使うAzureのストレージエミュレータが、ローカルでしか使えなかったので、NginXでリバプロ対応する方法を書いた。
AzureStorageのエミュレータに外部(Dockerコンテナ)からコンニチワ
なんでローカルではなく外部から使いたかったかというと、アプリをDockerコンテナにするから、ストレージ部分はアプリをステートレスにするためにコンテナの外に締め出したかった。それが開発環境でも。

 NginXで、Windowsで動いてるエミュレータへリバプロを立てるより、そもそもエミュレータがこれまた一つのDockerコンテナアプリで動いてるほうが便利じゃね?と思った。思ったのち、「エミュレータはSQLServerで作ってあるよん」て情報を思い出したので、じゃあぼくはとASP.NET Core MVC + MongoDBで作ってみた。まだ簡単なPUTとGETの機能のみだが、公式のエミュレータ接続ライブラリでAPIを使って接続できるようになっている。
Sokyu: Azure Storage(Blob) Emulator
 データを格納するためのMongoDBをこのエミュレータと接続できるように置いた状態で、このエミュレータの接続設定をそいつへ向けて起動すれば動作する。そういうわけで、今のところ外部ストレージとしてMongoDBが必要。


 ためしにエミュレータを起動した状態で、C#の公式のライブラリでJPEGのPUTとGETを一つやってみる。
 まずPUTから。muscles.jpgというファイルを用意してPUTで投げる。
using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using System;
using System.IO;
using System.Threading.Tasks;

namespace PlayAzureStorage
{
class Program
{
private static async Task FuncAsync()
{
var accountName = "devstoreaccount1";
var key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var connectionString = $"AccountName={accountName};"
+ $"AccountKey={key};"
+ $"BlobEndpoint=http://192.168.11.17:32782/devstoreaccount1;";

var account = CloudStorageAccount.Parse(connectionString);
Console.WriteLine("connect: " + account.BlobStorageUri);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("test");

var name = "muscles.jpg";
using (var stream = File.OpenRead(name))
{
var blockBlob = container.GetBlockBlobReference(name);
await blockBlob.UploadFromStreamAsync(stream);
Console.WriteLine("Uploaded.");
}
}

static void Main(string[] args)
{
FuncAsync().Wait();
}
}
}

問題なく動作していれば上記はエラーなく終了する。
 続いてGETで、今保存したmuscles.jpgを読みだしてみる。アクセス権限の機能はまだ実装していなので、ブラウザでお目当てのファイルのパスへアクセスすれば読み出せる。

comment: 0

AzureFileStorageのエミュレータにlocalhostじゃないIPを使ってコンニチワ

'17/9/21追記
IPを使っても、結局エミュレータにアクセスするマシンを仮想化で外に置いたら、エミュレータが使えなくなった。だから外の環境からエミュレータを使うのは無理かも。
********


Azure Storage Emulatorにコンテナ内からつなごうとして、結局リバースプロキシを立てた話

 少し前に、開発に使うAzureのストレージエミュレータがローカルからしか使えん、けどアプリはDockerコンテナにしたいから、Fiddlerでリバースプロキシするってのを書いた。書いておいて、やっぱFiddlerを開発の度に立ち上げるのもアレだなーと思ったので、一般的なサーバソフトで、まずはNginXで同じことをやってみることにした。今回は.NET CoreでC#によるエミュレータへの書き込み、ブラウザで書き込んだファイルの読み出しまでやる。

 .NET CoreのC#で、Azureのストレージエミュレータにアクセスして、適当なテキストファイルを書き込むコードを用意する。
using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using System;
using System.IO;
using System.Threading.Tasks;

namespace PlayAzureStorage
{
class Program
{
private static async Task Func2Async()
{
var accountName = "devstoreaccount1";
var key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var connectionString = $"AccountName={accountName};"
+ $"AccountKey={key};"
+ $"BlobEndpoint=http://192.168.11.17:20000/devstoreaccount1;";
//+ "BlobEndpoint=http://127.0.0.1:1000/devstoreaccount1;";

var account = CloudStorageAccount.Parse(connectionString);
Console.WriteLine("connect: " + account.BlobStorageUri);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("test");
var isCreated = await container.CreateIfNotExistsAsync();
if (isCreated)
{
var permissions = await container.GetPermissionsAsync();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
await container.SetPermissionsAsync(permissions);
}

var name = "foo.txt";
using (var stream = File.OpenRead(name))
{
var blockBlob = container.GetBlockBlobReference(name);
blockBlob.Properties.CacheControl = "public, max-age=2678400";
blockBlob.Properties.ContentType = "text/plain";
await blockBlob.UploadFromStreamAsync(stream);
Console.WriteLine("Uploaded.");
}
}


static void Main(string[] args)
{
Func2Async().Wait();
}
}
}

 適当なテキストファイルfoo.txtをプロジェクトに用意しておく。あと接続文字列にあるIPとポート番号は自分の環境に合ったもので。今回はリバプロで、ポート20000に来たものをポート10000に飛ばす。

 NginXを落としてきて、解凍したディレクトリ内にあるnginx.confをリバプロとして動作するように書き換える。その辺に転がってるリバプロサンプルを書き換えて、外部からのC#コードによる書き込みを実行したところ二度エラーが出た。一つはヘッダをもとのままになるように設定することで解決した。もう一つはNginXがデフォではHTTPのバージョンを1.0に書き換えてしまうためにエラーになったので、バージョン1.1で動くように設定。
 最終的に設定ファイルは下記のとおり。
nginx.conf

worker_processes 1;

events {
worker_connections 1024;
}

http {
sendfile on;

upstream app_servers {
server 127.0.0.1:10000;
}

server {
listen 20000;

location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
}

 nginx.confを用意したら、"start nginx"で起動しておく。

 まずストレージエミュレータを起動する。次に、用意しておいたC#コードを実行して、ストレージエミュレータへの書き込みを実行してみる。エラーなく処理が終了するはず。エラーが出てくる場合、Azureのライブラリがエラーを出すので、本来エミュレータから送られてくるエラーメッセージはそのままでは見られない。ここらはFiddlerを使ってキャプチャしておけば、デバッグに役立つ。


 ストレージエミュレータへの書き込みが成功したら、ブラウザからそのファイルを参照してみる。
http://192.168.11.17:20000/devstoreaccount1/test/foo.txt

成功。

 Azureを使ったWebアプリの開発をしたいが、その開発環境で使うストレージエミュレータがローカルからのアクセスしか受け付けないので、NginXでリバプロを立てて外部(Dockerのコンテナ内を想定)からのアクセスを受け付けるようにしてみた。うまくいった。
 しかしこれ、けっこうメンドウだったのでエミュレータがコンテナで用意できたらなーと思っている。
comment: 0