ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Azure Container Service の Kubernetes に ASP.NET Core アプリケーションをデプロイする

前回の記事では、Azure Container Service に Kubernetes を展開し、nginx をデプロイしました。
gooner.hateblo.jp
今回は、ASP.NET Core アプリケーションをデプロイして、スケールアウトやアプリケーションの更新を試してみます。

ASP.NET Core アプリケーションを作る

先月アップデートされた dotnet コマンドを使って、ASP.NET Core MVC のテンプレートでアプリケーションを作ります。

$ dotnet new mvc -o mvcapp
$ cd mvcapp
$ dotnet restore
$ dotnet build
$ code .

Docker イメージを作りたいので、テンプレートを少し修正します。
まずは、Visual Studio Code を開いて、Dockerfile を追加します。

FROM microsoft/dotnet:1.1.1-sdk
COPY . /app
WORKDIR /app

RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]

EXPOSE 5000/tcp
ENV ASPNETCORE_URLS http://*:5000

ENTRYPOINT ["dotnet","run"]

次に、Dockerfile で環境変数に指定したURL(ポート番号)をアプリケーションの起動時に設定します。
Program.cs を開いて、環境変数から取得した値を UseUrls() メソッドで設定します。

public class Program
{
    public static void Main(string[] args)
    {
        // 環境変数からURLを取得する
        var config = new ConfigurationBuilder()
            .AddEnvironmentVariables("")
            .Build();
        var url = config["ASPNETCORE_URLS"] ?? "http://*:5000";
        
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseUrls(url)           // URLを設定する
            .Build();

        host.Run();
    }
}

Docker イメージをビルドします。

$ docker build -t thara0402/mvcapp:v1 ./

ビルドした Docker イメージを Docker Hub にプッシュします。

$ docker login
Username: thara0402
Password: 
Login Succeeded
$ docker push thara0402/mvcapp:v1

f:id:TonyTonyKun:20170320110613p:plain

ASP.NET Core アプリケーションをデプロイする

Azure Container Service に SSH 接続します。

$ ssh thara@gooner0318mgmt.eastus.cloudapp.azure.com -A

先ほどプッシュした Docker Hub のイメージを使って、Kubernetes に ASP.NET Core アプリケーションをデプロイします。Pod が作成されるので、外部から接続できるように Service で 5000 ポートを公開します。

$ kubectl run mvcapp --image thara0402/mvcapp:v1
deployment "mvcapp" created

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3237912479-6b159   1/1       Running   0          1m

$ kubectl expose deployments mvcapp --port=5000 --type=LoadBalancer
service "mvcapp" exposed

$ kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP    PORT(S)          AGE
kubernetes   10.0.0.1     <none>         443/TCP          1h
mvcapp       10.0.47.43   52.168.28.12   5000:32648/TCP   2m

ブラウザで 52.168.28.12:5000 に接続すると、ASP.NET Core MVC のテンプレートが表示されます。

f:id:TonyTonyKun:20170320110631p:plain

ASP.NET Core アプリケーションをスケールアウトする

デプロイした ASP.NET Core アプリケーションをスケールアウトしてみます。

$ kubectl scale deployment mvcapp --replicas=3
deployment "mvcapp" scaled

$ kubectl get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mvcapp    3         3         3            3           12m

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3237912479-1mrkl   1/1       Running   0          13s
mvcapp-3237912479-5k3bq   1/1       Running   0          13s
mvcapp-3237912479-6b159   1/1       Running   0          12m

Pod が2つ追加されて、3つにスケールアウトされました。Service がロードバランサーとなり、リクエストを割り振ってくれます。数秒でイメージを複製できるのはコンテナの便利なところです。

ASP.NET Core アプリケーションを更新する

ローカルで ASP.NET Core アプリケーションを更新して、Docker イメージをビルドします。タグは、V2 に変更しています。ビルドした Docker イメージを Docker Hub にプッシュします。

$ docker build -t thara0402/mvcapp:v2 ./
$ docker push thara0402/mvcapp:v2

f:id:TonyTonyKun:20170320110654p:plain

Azure Container Service に SSH 接続して、Pod にデプロイされている Docker イメージを更新します。

$ kubectl set image deployments/mvcapp mvcapp=thara0402/mvcapp:v2

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3312820128-400cp   1/1       Running   0          30s
mvcapp-3312820128-8wz2f   1/1       Running   0          30s
mvcapp-3312820128-tgvll   1/1       Running   0          30s

Pod の名前が変わっていることから、自動的に古い Pod が破棄され、新しい Docker イメージを使った Pod が作成されたことが分かります。
ブラウザで 52.168.28.12:5000 に接続すると、「mvcapp v2」に変更された ASP.NET Core MVC のアプリケーションが表示されます。

f:id:TonyTonyKun:20170320110712p:plain

ASP.NET Core アプリケーションを削除する

最後に、デプロイした ASP.NET Core アプリケーションを削除します。

$ kubectl delete service,deployment mvcapp
service "mvcapp" deleted
deployment "mvcapp" deleted

まとめ

今回は、Kubernetes に ASP.NET Core アプリケーションをデプロイして、スケールアウトやアプリケーションの更新を試してみました。

f:id:TonyTonyKun:20170321003533p:plain

ASP.NET Core アプリケーションの Docker イメージを Docker Hub 経由で、Pod にコンテナをデプロイしました。宣言的なコマンドを実行するだけで、簡単に Pod のレプリケーションを作成し、スケールアウトを構成できます。
ASP.NET Core アプリケーションの更新は、新しい Docker イメージを設定することで、自動的に古い Pod が破棄され、新しい Pod が作成されます。Pod を1つずつ更新したり、スワップする必要もありません。
次回以降は、もう少し実践的な内容を試してみたいと思います。
gooner.hateblo.jp

Visual Studio for MAC を試してみた

先日の Microsoft Connect(); で発表された MAC 向けの Visual Studio(Preview)を試してみました。
ASP.NET Core で作った Web API を Xamarin Forms のアプリから呼び出してみます。

インストール

こちらから無料でダウンロードすることができます。
www.visualstudio.com
Xcode が必須なので最初にインストールしましたが、それ以外はウィザード通りに進めれば、問題なくインストールできました。

ASP.NET Core で Web API を作る

Visual Studio for MAC を起動して、新しいプロジェクトを作成します。テンプレートは、ASP.NET Core Empty Web Application を選択します。

f:id:TonyTonyKun:20161127052227p:plain

Web API を実装するために必要な NuGet ライブラリをインストールします。

  • Microsoft.AspNetCore.Mvc
  • Json.NET

f:id:TonyTonyKun:20161127052323p:plain

ASP.NET Core プロジェクトで MVC を有効にするため、Startup クラスに AdddMvc と UseMvc の2つのメソッドを追加します。

public class Startup
{
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddMvc();
	}

	public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
	{
		loggerFactory.AddConsole();

		app.UseMvc();

		if (env.IsDevelopment())
		{
			app.UseDeveloperExceptionPage();
		}
		app.Run(async (context) =>
		{
			await context.Response.WriteAsync("Hello World!");
		});
	}
}

モデルとして、Person クラスを追加します。

public class Person
{
	public int Id { get; set; }
	public string Name { get; set; }
}

コントローラーとして、PersonController クラスを追加します。

[Route("api/person")]
public class PersonController : Controller
{
	public List<Person> Get()
	{
		return new List<Person> {
			new Person{ Id = 1, Name = "立花 瀧"},
			new Person{ Id = 2, Name = "宮水 三葉"},
		};
	}
}

これで Web API の実装は完了です。デバッグ実行して「localhost:5000/api/person」にアクセスすると、JSON が返却されます。

f:id:TonyTonyKun:20161127052626p:plain

Xamarin Forms から Web API を呼び出す

Xamarin Forms 用にもうひとつ Visual Studio for MAC を起動するには、ちょっとしたコツが必要です。Dock からでは複数の Visual Studio for MAC を起動できないので、ターミナルからコマンドで起動します。

open -n "/applications/Visual Studio.app"

もう1つの Visual Studio for MAC が起動したら、新しいプロジェクトを作成します。テンプレートは、Xamarin Forms App を選択します。

f:id:TonyTonyKun:20161127052747p:plain

Web API を呼び出すために必要な NuGet ライブラリをインストールします。

  • Microsoft.HTTP.Client.Libraries
  • Json.NET

f:id:TonyTonyKun:20161127052828p:plain

Xamarin Forms プロジェクトにボタンとリストビューを追加して、コードビハインドから Web API を呼び出すコードを追加します。

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamarinApp01" x:Class="XamarinApp01.XamarinApp01Page">
  <StackLayout>
    <Button Text="your name" Clicked="getPerson" />
    <ListView x:Name="personList" >
      <ListView.ItemTemplate>
        <DataTemplate>
          <TextCell Text="{Binding Name}" />
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>
</ContentPage>
public partial class XamarinApp01Page : ContentPage
{
	public XamarinApp01Page()
	{
		InitializeComponent();
	}

	public async void getPerson(object sender, System.EventArgs e)
	{
		using (var client = new HttpClient())
		{
			var response = await client.GetAsync("http://localhost:5000/api/person");
			if (response.IsSuccessStatusCode)
			{
				var json = await response.Content.ReadAsStringAsync();
				this.personList.ItemsSource = JsonConvert.DeserializeObject<List<Person>>(json);
			}				
		}
	}
}
public class Person
{
	public int Id { get; set; }
	public string Name { get; set; }
}

これで Xamarin Forms の実装は完了です。

結果確認(iOS)

まず、ASP.NET Core プロジェクトをデバッグ実行しておきます。
次に、Xamarin Forms プロジェクトで iOS のプロジェクトをスタートアップに設定してデバッグ実行すると、エミュレータが起動して Web API を呼び出して結果を表示することができます。

f:id:TonyTonyKun:20161127053054p:plain

なお、iOS のプロジェクトにも Json.NET の NuGet ライブラリを追加しておく必要がありました。

結果確認(Android)

同様に Android のプロジェクトをスタートアップに設定してデバッグ実行すると、エミュレータが起動しますが、Web API を呼び出すタイミングでエラーが発生しました。

f:id:TonyTonyKun:20161127053207p:plain

Android のエミュレータには詳しくないのですが、どうやら localhost には接続できないようです。
そのため、Web API を Azure Web Apps にデプロイすることで、結果を表示することができました。

f:id:TonyTonyKun:20161128064749p:plain

まとめ

Visual Studio for MAC は、開発者が好みのデバイスで得意な言語を使ってモバイルとクラウドのアプリケーションを作れる時代を目指していることを実感できます。
まだプレビュー版ということもあり、Windows 版の Visual Studio にある Azure にダイレクトにデプロイする機能などはありませんが、正式リリースに向けて機能が充実していくことを期待しています。

ASP.NET Core を Docker Cloud で Azure に自動デプロイする

最近、ASP.NET Core を触り始めたので、de:code 2016 のセッションで行われていたデモを試してみました。

f:id:TonyTonyKun:20161010132905p:plain

セッションでは Azure と AWS にデプロイしていましたが、今回は Azure だけにデプロイします。

  1. Mac 上で ASP.NET Core のプロジェクトを作成する
  2. GitHub にプッシュする
  3. Docker Hub でビルドが実行され、Docker イメージが作成される
  4. Docker Cloud が Docker イメージを Azure にデプロイする

前準備(ASP.NET Core)

ASP.NET Core の環境構築については、前回の記事を参照してください。
gooner.hateblo.jp

前準備(Docker Hub)

Docker Hub は、Docker イメージを公開・共有できるサービスです。公開されている公式リポジトリを使ったり、自分で作った Docker イメージをアップロードできます。今回は、Automated Build という機能を利用して、GitHub のリポジトリと連携させます。

事前に、GitHub でリポジトリを作成しておきます。DockerCloudTest という名前で作成しました。
Docker Hub にログインして、右上の Create Automated Build を選択して GitHub のリポジトリとリンクさせます。

f:id:TonyTonyKun:20161010133543p:plain

前準備(Docker Cloud)

Docker Cloud は、コンテナ化されたアプリケーションを Docker Hub から取り出し、任意のクラウドやデータセンターへデプロイし、運用管理する機能を提供するサービスです。Docker 社が Tutum を買収し、Docker Cloud に名前を変えて提供しています。

Docker Cloud にログインして、Link Provider から Azure とのリンクを設定します。Azure のサブスクリプションID を入力して、証明書をダウンロードします。ダウンロードした証明書は、旧 Azure ポータルの設定の管理証明書からアップロードしておきます。

f:id:TonyTonyKun:20161010134017p:plain

次に、Create a Node を選択して、Node Cluster を作成します。

f:id:TonyTonyKun:20161010134112p:plain

数分待つと、DEPLOYING が DEPLOYED に変わり、Azure 上にホスト用の VM が作成されます。

f:id:TonyTonyKun:20161010134134p:plain

ちなみに、Azure ポータルで見ると、Linux の仮想マシンが作成されていることが分かります。

f:id:TonyTonyKun:20161011032403p:plain

Azure 以外では、AWS や SoftLayer などのクラウドにもデプロイすることができます。

ASP.NET Core アプリケーションを GitHub にプッシュする

Mac 上で Yeoman を使って、ASP.NET Core MVC アプリケーションを作成します。ローカルの git にコミットした後、GitHub にプッシュします。

git init
git add -A
git commit -m ‘initial commit’
git remote add origin https://github.com/xxx/DockerCloudTest.git
git push -u origin master

Docker Hub に Docker イメージを作成する

Yeoman で作成したプロジェクトには、Dockerfile が含まれています。

FROM microsoft/dotnet:latest
RUN apt-get update && apt-get install -y sqlite3 libsqlite3-dev && rm -rf /var/lib/apt/lists/*
COPY . /app
WORKDIR /app
RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]
RUN ["dotnet", "ef", "database", "update"]
EXPOSE 5000/tcp
CMD ["dotnet", "run", "--server.urls", "http://*:5000"]

GitHub にプッシュして数分待つと Building が Success に変わり、Docker イメージが作成されます。

f:id:TonyTonyKun:20161010134251p:plain

Docker Cloud から Azure にデプロイする

Docker Cloud では、デプロイする Docker イメージを Service として管理します。Create a Service の My repositories から Docker Hub のリポジトリを選択します。

f:id:TonyTonyKun:20161010134331p:plain

ポート番号は 80 で接続したいので、Published にチェックを入れて設定します。

f:id:TonyTonyKun:20161010134352p:plain

Create & Deploy ボタンを押して数分待つと、Starting が Running に変わり、デプロイが完了します。Service Endpoints の URL をクリックして、tcp を http に変えてブラウザからアクセスすると、ASP.NET Core MVC のページが表示されます。

f:id:TonyTonyKun:20161010134427p:plain

CI / CD を設定してデプロイを自動化する

Docker Cloud と Docker Hub に CI / CD を設定して繋げることで、先ほどは手動でデプロイした部分を自動化します。
まずは、Docker Cloud で Redeploy のトリガーを作成します。

f:id:TonyTonyKun:20161010134511p:plain

次に、Docker Hub で Web Hooks を設定します。Docker Cloud で作成した Redeploy トリガーの URL を貼り付けて、先頭に https://cloud.docker.com/ を付加します。

f:id:TonyTonyKun:20161010134550p:plain

結果確認

Visual Studio Code を使って、ASP.NET Core MVC アプリケーションの About.cshtml を変更して、GitHub にプッシュします。

@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<h4>Hello Docker Cloud !!!</h4>

GitHub → Docker Hub → Docker Cloud の順番で処理が自動化され、最終的に Azure にデプロイされます。

f:id:TonyTonyKun:20161010134715p:plain

まとめ

Docker を使うと、クラウド上のいろいろなサービスを繋げることで、開発ライフサイクルを自動化できます。
今回は Docker Cloud の無償版を利用したので Azure 上 に1つのノードしか展開できませんでしたが、有償版であれば Azure と AWS にノードを分けて展開したり、一方のベンダーに障害が発生したら別のベンダーにフェールオーバーしたりできます。
自分の作ったアプリケーションが、クラウドベンダーを超えて展開した Docker 上で動かせるのは面白いと思いました。