一成一代

ここ最近、醤油とか塩が続いていたので、豚骨臭いラーメンが食べたいなと思い、東比恵駅出てすぐにある『一成一代』に行ってきた。先週東比恵来たとき、改札出たやたら豚骨の良い匂いさせていて、その匂いで久しぶりに豚骨が食べたくなってしまったもので。

f:id:griefworker:20201129224025j:plain

ラーメンはもちろん食べるけど、餃子もチャーハンも食べてみたかったので、全部食べられる『一成一代セット』を注文。ラーメンは泡系でクリーミー。ベースのタレがちょっと甘めかな、濃厚で結構好みな感じのラーメンだった。同じ泡系でも、一双や一幸舎よりも甘めに感じた。豚骨くさいだけよりは、甘さがあるほうが助かるので、泡系の中でも比較的自分好みのスープ。

f:id:griefworker:20201129224035j:plain

餃子は底がカリッと焼かれていて、それに対して皮や具はフワッとしていて、なかなかの美味さ。お酒が欲しくなるくらいに。レモンサワー飲みながら食べたいなと思った。

f:id:griefworker:20201129224045j:plain

チャーハンは、調理風景を見ていると、あらかめ仕込みの段階で味付けしておいたものを、中華鍋で焼く感じっぽかった。味自体は良かったんだけど、ちょっと炒め足りない気が。チャーハンを食べている感じではなかったな。もうちょっと炒めたら、もっと美味しく仕上がったと思うんで、そこだけが残念ポイント。

f:id:griefworker:20201129224059j:plain

ラーメン自体630円、セットで1080円と、昨今のラーメンの高価格化からすれば比較的良心的。なかなか満足感あった。白めしは100円で、おかわり自由のようだし、近くの東福岡高校の男子生徒あたりがたくさん常連になっていそうだな。

博多 一成一代
〒812-0007 福岡県福岡市博多区東比恵2-17-23 ローズマンション第一博多
1,000円(平均)1,000円(ランチ平均)
r.gnavi.co.jp

Azure SQL Database に接続する WCF サービスを Windows コンテナのプロセス分離モードで動かす実験

はじめに

最近、自分の中で Docker 熱、というか Windows コンテナ熱が再燃。Windows コンテナのプロセス分離モードで WCF サービスをホストするのと、Windows コンテナの中から Azure SQL Database に繋ぐところまでは、以前実験した。

tnakamura.hatenablog.com

tnakamura.hatenablog.com

今度はこれらを合体。Azure SQL Database に接続する WCF サービスを、Windows コンテナのプロセス分離モードで動かしてみる。

WCF サービス

Azure SQL Database に接続して、バージョンを取得する SQL を実行するだけの簡単な WCF サービスを作成。接続文字列は環境変数で渡せるようにしておく。

using System;
using System.Data.SqlClient;
using System.ServiceModel;
using System.Threading;

namespace WcfWCOW
{
    class Program
    {
        static void Main(string[] args)
        {
            if (string.IsNullOrEmpty(TestService.ConnectionString))
            {
                Console.WriteLine("環境変数 CONNECTION_STRING を設定してください。");
                return;
            }

            var host = new ServiceHost(typeof(TestService));
            try
            {
                var binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;

                host.AddServiceEndpoint(
                    typeof(ITestService),
                    binding,
                    "net.tcp://localhost:8080/TestService");
                host.Open();

                foreach (var endpoint in host.Description.Endpoints)
                {
                    Console.WriteLine(endpoint.ListenUri);
                }

                Thread.Sleep(-1);

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
                host.Close();
            }
        }
    }

    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        string Test();
    }

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class TestService : ITestService
    {
        public static string ConnectionString { get; }

        static TestService()
        {
            ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING");
        }

        public string Test()
        {
            try
            {
                using (var connection = new SqlConnection(ConnectionString))
                {
                    connection.Open();
                    using (var command = connection.CreateCommand())
                    {
                        command.CommandText = @"SELECT @@VERSION";
                        return (string)command.ExecuteScalar();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
                throw;
            }
        }
    }
}

WCF クライアント

WCF サービスが動くコンテナの IP アドレスは起動するたび変わるので、コマンドライン引数で渡せるようにしておく。

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using WcfWCOW;

namespace ConsoleClient
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("ホスト名または IP アドレスを指定してください。");
                return;
            }
            var host = args[0];
            var factory = new ChannelFactory<ITestService>(
                new NetTcpBinding(SecurityMode.None),
                $"net.tcp://{host}:8080/TestService");
            var channel = factory.CreateChannel();

            var result = channel.Test();
            Console.WriteLine(result);

            Console.WriteLine("Enter で終了");
            Console.ReadLine();

            ((IChannel)channel).Close();
        }
    }
}

namespace WcfWCOW
{
    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        string Test();
    }
}

Dockerfile

あらかじめビルドしておいた WCF サービスのファイル一式をコピーして起動するように、Dockerfile を記述。

FROM mcr.microsoft.com/dotnet/framework/wcf:4.8
WORKDIR /app
EXPOSE 8080
COPY ./WcfWCOW/bin/Release/ .
ENTRYPOINT ["WcfWCOW.exe"]

起動

Dockerfile からコンテナイメージをビルドし、起動。Azure SQL Database の接続文字列を環境変数で渡している。

> docker build -t tnakamura/wcfwcow:1.0 .
> docker run --rm --isolation process -e CONNECTION_STRING="接続文字列" --name wcfwcow tnakamura/wcfwcow:1.0

エンドポイントのアドレスが表示されたら、WCF サービスの起動に成功したことになる。

f:id:griefworker:20201124103250p:plain

接続

docker inspect で起動したコンテナの IP アドレスを調べ、その IP アドレスを引数にクライアントを起動。

> docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" wcfwcow
> .\ConsoleClient.exe コンテナのIPアドレス

Azure SQL Database のバージョンが表示されたら実験成功。

f:id:griefworker:20201124103306p:plain

おわりに

Azure SQL Database に接続する WCF サービスを Windows コンテナのプロセス分離モードで動かすことに成功した。次のステップは、Azure Kubernetes Service かな。実験もいよいよ最終段階。

実をいうと、Windows コンテナのホスト側で動かしている SQL Server Express にも繋ぎたかったけど、host.docker.internal で繋げなかった。Windows コンテナだったから?プロセス分離だったから?今回も解決できず時間切れ。いずれリベンジしたい。

かぐや様は告らせたい(20)

会長とかぐやが無事付き合ったから主人公交代したのか?ってくらいに石上が話の中心。かぐやの助力もあって、目標としていた学年 50 位以内を達成したし、ホント男を上げている。さすがは裏主人公といったところか。何気にモテ期到来しているし。まぁ、そんな石上でもつばめ先輩を落とすのは難しいだろうなぁ、というのは薄々感じている。*1

石上が自らかぶった汚名をどうやってそそぐのかが、本作中で最重要な伏線の1つだったわけだけど、つばめ先輩がこれまで築き上げてきた繋がりを駆使して、石上の悪い噂を良い噂で上書きするとはね。これほどの誠意を見せられたら、告白の返事がどんなであろうと受け入れるしかないでしょ。作中の言葉を借りるなら、そんなつばめ先輩を好きになった石上は正しい。

*1:まぁ本誌ではすでに結果出てるけど。

波のおと

福岡市博多区東比恵にある『波のおと』に行ってきた。最寄駅である東比恵駅から800mくらい離れていて、歩いて行くにはちょっと遠かったな。ここは福岡市でも最近増えてきた豚骨ではないラーメンを出すお店。塩ラーメンと醤油ラーメンを売りにしているようだった。

f:id:griefworker:20201123182132j:plain

平日なら餃子と肉味噌ご飯のセットがお安くなっていたけど、行ったのが休日だったので仕方ない。塩ラーメンの麺増量で注文した。

f:id:griefworker:20201123182140j:plain

麺は平麺でもっちりむっちりしていて、非常に食感がよかった。腹持ちが良さそうな麺。透明感あるスープは出汁の旨味が効いていて、滋味あふれる感じで、完飲不可避だった。麺とスープのクオリティ高い。あと、チャーシューだけじゃなくつみれも入っていたのが、得した気分になった。フワッと柔らかくて、もっと入っていても良かったんじゃないかなぁ、ってぐらいに美味。

ただ、チャーシューが普通というか、欲を言えば、低温調理されたチャーシューだったらもっと良かったのに。肉厚でボリュームはあったけど、脂身が苦手な自分にはちょっと合わなかった。その点を差し引いても、東比恵まで足を運んだかいはあった。

r.gnavi.co.jp

IdentityServer4 メモ

IdentityServer4 でいろいろ試したときのメモ。当時のバージョンは 4.1.1。

www.nuget.org

  • IdentityServer4 の「Interactive Applications with ASP.NET Core」で ProfileService が呼ばれるタイミング
    • 以下の順番で呼ばれていた
      1. Account/Login
      2. ProfileService.IsActivateAsync
      3. ProfileService.IsActivateAsync
      4. ProfileService.GetProfileDataAsync
    • IResourceOwnerPasswordValidator は呼ばれなかった。
      • GrantTypes が Code だったから。
      • ResourceOwnerPassword だったら呼ばれるはず。
  • ProfileService の IsActivateAsync と GetProfileDataAsync に任意のデータを渡せるか
    • IdentityServerUser.AdditionalClaims に追加したら渡せた。
    • ProfileService.GetProfileDataAsync で context.IssueClaims に、JWT に含めたいクレームを追加する。
      • Client.AlwaysIncludeUserClaimsInIdToken = true にしないと、追加したクレームが JWT に含まれない?
        • 含まれないのは ID トークンだけ。
        • アクセストークンには含まれていた。
    • IsActivateAsync は常に true を返せばいい。
      • AspNetCore.Identity の ProfileService はそうしている。
    • GetProfileDataAsync の context から、IdentityServerUser.AdditionalClaims に追加したクレームが取り出せる。

無邪気

七隈にある『無邪気』に行ってきた。ここは、福岡では数少ない家系のラーメンを出すお店。以前同じく家系の入船食堂に行ったとき美味しかったんで、こっちにもつい期待してしまうのは仕方ない。

f:id:griefworker:20201114225541j:plain

かなり久しぶりに食べた家系ラーメンは、近くに福大があるからかどうか因果関係は不明だけど、予想より大味だった。スープの濃さはいいとして、表面の油の量が多くて、だいぶ胃にもたれてきた。後味がスープではなく油の後味に。カスタマイズできるから、油控えめにすればよかった。初来店ってことで全部普通にしたのがいけなかったか…。

f:id:griefworker:20201114225548j:plain

家系のスープが濃いのは折込済み。ご飯と一緒に食べると美味しい感じに調節してあるのがよく分かる。純粋にスープに関しては予想通り濃厚で良かったんだけど、なにせ油がくどい。家系に油はいらないんじゃないか。そういうものなのかな。

自分以外の客はほとんど福大生っぽかった。太麺でボリュームあるし、100円でご飯食べ放題なので、福大生のお財布に優しいと思う。37歳の胃袋にはだいぶ大味で胃にきた。油少なめにして、大盛りではなくご飯を注文するのが正解だったかもな。リベンジする機会があるかどうか。

r.gnavi.co.jp

Packer で Azure VM のイメージを作成しようとしたら WinRM の有効化でタイムアウトした

Microsoft Docs の資料を参考に、Packer を使って Azure VM のイメージを作成しようと試みた。

docs.microsoft.com

ところが、WinRM の有効化を待機しているところでタイムアウトwinrm_timeout を延ばしてもダメ、vm_size を上げてもダメ、image_sku を変えてもダメときた。

原因はしょーもなくて、WinRM が使う 5896 番ポートが社内のゲートウェイでブロックされていたから。Packer のテンプレートや Azure 側には何の問題も無かった。こんなことで 1日潰れて萎える。自由なインターネット環境が欲しい。今後は真っ先に社内のネットワークを疑うようにしよう。