記念撮影

BUMP OF CHICKEN の新曲が iTunes Store で配信されていたので購入。 YouTube の公式チャンネルで PV が公開された通知が Gmail に届いて知ることになるとは。

テレビを全く見なくなったので知らなかったが、 どうやらカップヌードルCM 「HUNGRY DAYS 魔女の宅急便 篇」で起用されているらしい。 ちょっとセンチな気分にさせるメロディと歌詞だった。 夏祭りの終わりのような、 夏休みの終わりのような、 いや、青春時代の終わりのような。 この曲も聞けば聞くほど良くなっていく。 1曲リピートがしばらく止まりそうにない。

記念撮影

記念撮影

『WEB+DB PRESS Vol.99』を読んだ

特集1『良いコードって何だろう?』

良いコードかどうか判断し説明できる能力に「絶対良感」という名前をつけたのは面白い。 著名なライブラリやフレームワークをお手本するのも良いアイデアだと思う。 周りがお手本にできるコードを書いているとは限らないし。 OSSソースコードを読む習慣がつけば理想。 DRY にする目安の3アウトチェンジのルールも、基準が明快で導入しやすいと思う。

特集2『UIテスト自動化』

iOS には XCTest、 Android には Espresso という公式の UI 自動テストツールが提供されていて、 しかもここまで使えるシロモノになっているとは。 ちょっと見ない間にテスト環境も進化していた。 Appium は知っていて、いつか導入しようと思っていたけど、重い腰がなかなか上がらなかった。 標準で提供されているなら始めやすい。 もはや UI 自動テストを書かない理由が無いな。

特集3『実践Kubernetes』

Docker でWebサービスの本番運用を検討していて、Kubernetes を使うかもしれないので調べないといけないな、 と思っていたからタイムリーだった。 Kubernetes は Google Container Engine が抽象化してくれるとはいえ、 アプリケーションエンジニアが扱うのは難しそうな印象。 Azure App Service や Elastic Beanstalk でも Docker コンテナをデプロイできるんで、自分の場合はこれらの PaaS を使う方がよさそうだ。

WEB+DB PRESS Vol.99

WEB+DB PRESS Vol.99

  • 作者: ?橋健一,谷口禎英,井本大登,山崎勝平,大和田純,内村元樹,坂東昌哉,平田敏之,牧大輔,板敷康洋,大?浩崇,穴井宏幸,原口宗悟,久田真寛,ふしはらかん,のざきひろふみ,うらがみ,ひげぽん,池田拓司,はまちや2,竹原,片田雄樹,渋江一晃,WEB+DB PRESS編集部編
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/06/24
  • メディア: 大型本
  • この商品を含むブログを見る

ASP.NET Core で Basic 認証

ASP.NET Core で Basic 認証を行うサンプルを書いてみた。ユーザーは固定なので、本番で使うときは ASP.NET Core Identity みたいに、ユーザー情報をストアから取得するようにしたほうがいいだろうな。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace BasicAuthExample
{
    // Basic 認証ミドルウェア
    public class BasicAuthenticationMiddleware
    {
        const string USER_NAME = "tnakamura";

        const string PASSWORD = "test12345";

        readonly RequestDelegate _next;

        public BasicAuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // Basic 認証のヘッダー
            // Authorization: Basic <userName:password を Base64 エンコードした文字列>
            // からユーザー名とパスワードを取り出してチェックする
            string header = context.Request.Headers["Authorization"];
            if (header != null && header.StartsWith("Basic"))
            {
                var encodedCredentials = header.Substring("Basic".Length).Trim();
                var credentials = Encoding.UTF8.GetString(Convert.FromBase64String(encodedCredentials));
                var separatorIndex = credentials.IndexOf(':');
                var userName = credentials.Substring(0, separatorIndex);
                var password = credentials.Substring(separatorIndex + 1);

                if (userName == USER_NAME && password == PASSWORD)
                {
                    // コンテキストにユーザー情報をセットする
                    var claims = new[]
                    {
                        new Claim(ClaimTypes.Name, userName),
                        new Claim(ClaimTypes.Role, "User")
                    };
                    var identity = new ClaimsIdentity(claims, "Basic");
                    context.User = new ClaimsPrincipal(identity);

                    await _next(context);
                    return;
                }
            }

            // ブラウザの認証ダイアログを出すには、レスポンスヘッダーに
            // WWW-Authenticate: Baic が必要
            context.Response.Headers["WWW-Authenticate"] = "Basic";
            context.Response.StatusCode = 401;
        }
    }

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

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

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMiddleware<BasicAuthenticationMiddleware>();

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"Hello {context.User.Identity.Name}!");
            });
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

Azure App Service で Spring Boot 製 Web アプリケーションの Blue-Green Deployment を行う

Azure App Service で Spring Boot 製 Web アプリケーションを動かすことに成功した。

tnakamura.hatenablog.com

ただし、このままだと新しいバージョンをデプロイするときにサービスを停止しないといけない。実行中に App Service Editor で jar ファイルを更新しようとしたが、ステータスコード 409 のエラーになった。停止すれば更新できた。

開発中でも毎回サービスを停止してデプロイするのは手間だし、万が一動かなかったときはツライ。ステージング環境にデプロイして動作確認してから、本番環境と入れ替えたいので、Blue-Green Deployment をやることにした。

デプロイメントスロットを作成

名前は staging にしておく。 一から設定したくないので、既存の環境をコピーする。

staging にデプロイ

作戦した staging デプロイメントスロットでも App Service Editor は使える。 jar ファイルと web.config を wwwroot 直下にアップロードし、staging を再起動する。

あとは、staging で動作確認したら、スワップを実行して本番環境と入れ替えればいい。

Spring Boot 製 Web アプリケーションを Microsoft Azure の App Service で動かす

Spring Boot を使って作成した Web アプリケーションを、 Microsoft Azure の App Service で動かすことができたので、 手順をメモしておく。

App Service で Web App を作成

Microsoft Azure ポータルの App Service ページで、 SQL とか付いてない Web App を新規作成する。

Java 8 を有効化

作成した Web App のアプリケーションの設定で、Java 8 を選択する。 Web コンテナーは使わないけど選択しておかなければいけないみたいなので、 最新の Tomcat にしておく。

web.config を作成

Spring Boot 組み込みの Tomcat を使って動かすために、 web.config を用意する必要がある。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
    </handlers>
    <httpPlatform processPath="%JAVA_HOME%\bin\java.exe"
        arguments="-Djava.net.preferIPv4Stack=true -Dserver.port=%HTTP_PLATFORM_PORT% -jar &quot;%HOME%\site\wwwroot\<jar ファイル名>&quot;">
    </httpPlatform>
  </system.webServer>
</configuration>

Spring Boot 製 Web アプリケーションをビルド

ビルドツールには Gradle を使っているので、gradle build を実行。 すると build\libs 下に jar ファイルが出力される。

Web App にアップロード

web.config と jar ファイルを、App Service に作成した Web App にアップロードする。 ファイルのアップロードは FTP ではなく、App Service Editor(プレビュー)を使って行う。

wwwroot 直下に、web.config と jar ファイルをアップロードすれば OK。

再起動

あとは Web App を再起動すれば、Spring Boot 製 Web アプリケーションが動く。

プログラマー定年まであと一年

34回目の誕生日を迎えてしまった。タイトル通り、プログラマー定年まであと一年。まぁ、今の職場にいる限りは、35歳どころか40歳になってもまだ余裕でコード書いてそうだ。そういったキャリアパスが用意されているわけじゃなくて、単に上が詰まっているからなんだけど。

このままでいいとは思っていないし、子供が幼稚園に行きだしたら家計が赤字転落しかねないので、なんとかしなければいけないのだが、仕事と子育てに忙殺されてなかなか思うようにいってない。一番望みのあったプライベートプロジェクトも結局ストップしてしまった。小遣い程度のお金ですら、稼ぐのは難しい。ビジネスを起こして家族を養っている人は凄い。 社員を養っている人にいたっては凄すぎる。

今の職場で急激に収入が上がることはまず無い。 かといって福岡で探してみても、今より良い条件の場所もそうそう無かったりする。「 住んでる場所が収入を決める」というのは誰のツイートだったか。 まったくその通りだと思う。

というかマネージャー経験が無い時点で転職は諦めている。 技術基盤チームみたいなことばかりやってたからな。 仕事内容自体はそこまで不満なくて、ただ今後のキャリアをどうしたものか悩んでいて、 とりあえずプライベートプロジェクトで何か当てる以外に、 今の状況を打開する方法が思いつかない。 詰んだ感があるな。

Azure App Services から Azure Database for PostgreSQL に接続できるようにする方法

Azure App Services のアウトバウンド IP を、Azure Database for PostgreSQLファイアウォール規則に追加する必要があるみたいだ。

Azure App Services のアウトバウンド ID は

  • [プロパティ] - [送信 IP アドレス]

で確認できた。4 つの IP アドレスが表示されているので、4 つすべてをファイアウォール規則に追加して、ようやく接続できるようになった。

まだプレビューだから仕方ない。正式リリース時には Azure SQL Database みたいに Azure 内部からの接続を許可するオプションが付くでしょ。きっと。