ソリューションファイルとプロジェクトファイルが同じディレクトリにある状態で、Dockerサポート(Dockerfile)を作成すると、docker build時にビルドが失敗する。

Visual StudioのソリューションエクスプローラーからDockerサポートの追加をして、docker build をしたところ、こんなエラーが出ました。
ログの全体はこんな感じ。

> docker build . -t sample
Sending build context to Docker daemon  339.5MB
Step 1/16 : FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
 ---> df0d46ff6229
Step 2/16 : WORKDIR /app
 ---> Using cache
 ---> e6407778a2d1
Step 3/16 : EXPOSE 80
 ---> Using cache
 ---> a17ec6fc7197
Step 4/16 : FROM microsoft/dotnet:2.2-sdk AS build
 ---> 343e2dc38168
Step 5/16 : WORKDIR /src
 ---> Using cache
 ---> 064e8cb7ff8d
Step 6/16 : COPY signalsample.csproj ./
 ---> 3361ca72b93c
Step 7/16 : RUN dotnet restore /signalsample.csproj
 ---> Running in d5b618d70751
MSBUILD : error MSB1001: Unknown switch.
Switch: /signalsample.csproj

For switch syntax, type "MSBuild /help"
The command '/bin/sh -c dotnet restore /signalsample.csproj' returned a non-zero code: 1

msbuild 時によくわからないスイッチがあると言われる。

For switch syntax, type “MSBuild /help”
The command ‘/bin/sh -c dotnet restore /signalsample.csproj’ returned a non-zero code: 1

dockerfileを見ると、

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY signalsample.csproj ./
RUN dotnet restore /signalsample.csproj
COPY . .
WORKDIR /src/
RUN dotnet build signalsample.csproj -c Release -o /app

FROM build AS publish
RUN dotnet publish signalsample.csproj -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "signalsample.dll"]

8行目の restore でプロジェクトファイルの指定/から始まっていてこいつがスイッチとして認識されているっぽい。

RUN dotnet restore /signalsample.csproj

たぶん、dotnet コマンドからプロジェクトを作ったあとに、同じフォルダーにソリューションフォルダーを作った事が原因だと思われる。(dotnet build や dotnet publish はソリューションフォルダーが無いことを考慮しているのに、dotnet restore は考慮されていのか…)
とりあえず、/を取るか、./とするとdocker buildもdocker-compose buildもうまく通った。

RUN dotnet restore signalsample.csproj
広告
カテゴリー:Visual Studio タグ:

Trelloを使って振り返りしたときに、単にリストごとにカードの一覧を出力するツールを作った

https://github.com/karuakun/TrelloFurikaerier

Trelloで振り返りをしたんだけれど、やっぱりプレーンテキストにエクスポートしたい。 だけれど、無料のTrelloで出力できるのはJSONファイルだけ。 ボードごとのタイトルと、その発言を作成した人の単純なリストをマークダウン形式で出力するためのツールです。

以前作った https://github.com/karuakun/typetalk-cli-dotnet-cli-sample は McMaster.Extensions.CommandLineUtils の使い方と、.Net Core のコンソールアプリの標準形を作るにはどうしたら良いを考えながら作ったので、必要以上に複雑になってしまった。
今回はコマンドライン解析を標準の ConfigurationBuilder にまかせてしまい、機能は何も無いので超シンプル。

カテゴリー:作ってみた

ReSharper で一度にクラスを別々のファイルに移動する

見失って検索したので、自分向けのメモ
https://pleiades.io/help/resharper/Move_classes_into_separate_files_in_one_go.html

こんな感じのサイトでJSONからC#のコードを生成したんだけれど、もちろんファイルが分かれないので一つのファイルにまとまってしまう。
http://json2csharp.com/#

ReShaperの機能でそんなのがあったなーと検索。
⇒感動する

カテゴリー:プログラミング タグ:

C#とAWSのStepFunctionsを試してみたログ

一旦動くところまで作ったけれど、他の言語になりそうなのでとりあえずログとして残しておく。
AWS Toolkit でパッケージすると、LambdaとStepFunctionsの紐づけがわかりやすくて、AWSにデプロイしたあとにも管理しやすいので良いと思ったんだけれどなー。

C# で AWS Lambdaって記事はよく見るけれど、C# で StepFunctions ってのはあんまり見ない。
検索してみるとこの記事がわかりやすかった。
https://www.red-gate.com/simple-talk/cloud/cloud-development/aws-step-function-serverless-applications/

とりあえずやって見よう

環境を作る

Visual StudioにAWS Toolkitが入っていない場合は、Visual Studioの更新機能と更新プログラムから「AWS Toolkit for Visual Studio 2017」をインストールする。

2018-12-12.0

プロジェクトを作る

新しいプロジェクトに、AWS Lambdaというカテゴリができるので、「AWS Serverless Application (.NET Core)」を選択してプロジェクトを作成する。ここでつけたプロジェクト名がLambdaのハンドラー名になるのでちょっと気にしておきたい(まぁ、あとでserverless.templateをいじれば変えられるけれど)。

下の方に .NET Frameworkのバージョンを選択するところがあるけれど、AWS Lambdaでは .net core しか使えないので、ここのドロップダウンはどこにも使われないので無視してOK。

2018-12-12.1.png

続いて、どのBlueprintを元にプロジェクトを作るかを聞かれるので、「Step Functions Hello World」を選択してFinishボタンを押す。

2018-12-12.2.png

プロジェクトの構成を確認する

こんな感じのプロジェクトができるので、軽く各ファイルの役割を説明すると

2018-12-12.3

aws-lambda-tools-defaults.json

デプロイに使う設定、AWSのリージョンやデプロイ資源を置くS3のバケットなど設定などを記述する。ここで指定しないものは後でデプロイのときに指定することもできるので、s3-buketやstack-nameを空にしておいて、デプロイ時に指定するみたいな使い方をするみたい。特に変更はいらないと思うのでまずは気にしなくて良い。

serverless.template

Cloud Formation Template っぽい記法で、Lambda や Role、StepFunction などのAWSの各リソースの作成方法を定義する。CloudFormationに不慣れだと読むのが辛いけれど、とりあえずLambdaを増やしたら要素をコピーして追加するぐらいの認識で良いかも。

State.cs

StepFunctionで持ち回るStateの定義、基本的に各ステートで実行されるタスクは、このStateを受け取り、Stateを返すようなメソッドのインターフェイスになる。StateMacnineの内部でもここのステートを参照できる。

state-machine.json

ステートマシーンの定義、各ステートでどんなメソッドを実行するとか、Stateの状態を見て分岐させるとか、数秒ウエイトするみたいな状態とその実行方法、次のステートに移る方法をJSONで定義する。

StepFunctionTasks.cs

各ステートでどんな処理をするのかをC#のメソッドで記述する。メソッドがそれぞれ独立したLambdaに展開される。各メソッドは、State.csで定義されたステートと、実行しているLambdaのContext情報を受け、Stateを更新して次のLambdaにわたす処理を定義する。

State.cs

StepFunctionで持ち回るStateの定義、基本的に各ステートで実行されるタスクは、このStateを受け取り、Stateを返すようなメソッドのインターフェイスになる。StateMacnineの内部でもここのステートを参照できる。

ここで定義したステート定義は内部的に Newtonsoft.Json でシリアライズ/デシリアライズされ、各ステップで利用できる。例えば C# で enum で定義したプロパティーなどを、StateMacnine 側で比較の材料にしたい場合などは数値として渡っていってしまうので、定義する型には気をつけておきたい。

namespace AWSServerless1
{
    /// <summary>
    /// The state passed between the step function executions.
    /// </summary>
    public class State
    {
        /// <summary>
        /// Input value when starting the execution
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The message built through the step function execution.
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// The number of seconds to wait between calling the Salutations task and Greeting task.
        /// </summary>
        public int WaitInSeconds { get; set; }
    }
}

StepFunctionTasks.cs

各ステートでどんな処理をするのかをC#のメソッドで記述する、メソッドがそれぞれ独立したLambdaに展開される。各メソッドは、State.csで定義されたステートと、実行しているLambdaのContext情報を受け、Stateを更新して次のLambdaにわたす処理を定義する。

この子に処理を全部書くと当たり前のことながら、メンテ不可能になるので適切な設計が必要になる。
DIしたい場合は、コンストラクターでコンテナを作って各メソッドでコンテナから処理を取り出してRunする感じになる。

この例の場合は、GreetingメソッドとSalutationsメソッドがそれぞれ独立したLambdaになり、StepFunctionsのStatemacnineから呼び出される。

using Amazon.Lambda.Core;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace AWSServerless3
{
    public class StepFunctionTasks
    {
        public StepFunctionTasks() { }

        public State Greeting(State state, ILambdaContext context)
        {
            state.Message = "Hello";

            if(!string.IsNullOrEmpty(state.Name))
            {
                state.Message += " " + state.Name;
            }

            // Tell Step Function to wait 5 seconds before calling 
            state.WaitInSeconds = 5;

            return state;
        }

        public State Salutations(State state, ILambdaContext context)
        {
            state.Message += ", Goodbye";

            if (!string.IsNullOrEmpty(state.Name))
            {
                state.Message += " " + state.Name;
            }

            return state;
        }
    }
}

Greetingメソッドでは、ステートを更新して次のステップでWaitする時間を動的に変更するために、StateのWaitInSecondsに5を設定している。

serverless.template

Cloud Formation Template っぽい記法で、Lambda や Role、StepFunction などのAWSの各リソースの作成方法を定義する。CloudFormationに不慣れだと読むのが辛いけれど、とりあえずLambdaを増やしたら要素をコピーして追加するぐらいの認識で良いかも。

構成としてはこんな感じになっている。
2018-12-12.4

GreetingTask や SalutationsTask の部分を展開するとこんな感じになっているので、新しいステップで実行したい関数が増えた場合は、このブロックを追加していけば良い。作成時点でのアセンブリ名や名前空間でHandler名が決定されるので、もし途中でアセンブリ名や名前空間、クラス名などを変えたらこっちも一緒に変えてあげたほうが後々わかりやすそう。

    "GreetingTask" : {
        "Type" : "AWS::Lambda::Function",
        "Properties" : {
            "Handler" : "AWSServerless1::AWSServerless1.StepFunctionTasks::Greeting",
            "Role"    : {"Fn::GetAtt" : [ "LambdaRole", "Arn"]},
            "Runtime" : "dotnetcore2.1",
            "MemorySize" : 256,
            "Timeout" : 30,
            "Code" : {
                "S3Bucket" : "",
                "S3Key" : ""
            }
        }
    },

    "SalutationsTask" : {
        "Type" : "AWS::Lambda::Function",
        "Properties" : {
            "Handler" : "AWSServerless1::AWSServerless1.StepFunctionTasks::Salutations",            
            "Role"    : {"Fn::GetAtt" : [ "LambdaRole", "Arn"]},
            "Runtime" : "dotnetcore2.1",
            "MemorySize" : 256,
            "Timeout" : 30,
            "Code" : {
                "S3Bucket" : "",
                "S3Key" : ""
            }
        }
    },

state-machine.json

ステートマシーンの定義、各ステートでどんなメソッドを実行するとか、Stateの状態を見て分岐させるとか、数秒ウエイトするみたいな状態とその実行方法、次のステートに移る方法をJSONで定義する。

{
  "Comment": "State Machine",
  "StartAt": "Greeting",
  "States": {
    "Greeting": {
      "Type": "Task",
      "Resource": "${GreetingTask.Arn}",
      "Next": "WaitToActivate"
    },
    "WaitToActivate": {
      "Type": "Wait",
      "SecondsPath": "$.WaitInSeconds",
      "Next": "Salutations"
    },
    "Salutations": {
      "Type": "Task",
      "Resource": "${SalutationsTask.Arn}",
      "End": true
    }
  }
}

ここで定義したものは、AWS上のコンソールからみると、こんな感じのステートマシンとして表示される。
2018-12-12.5

Greeting状態やSalutations状態では、TypeをTaskとしてserverless.templateで定義したLambdaを呼び出している。定義時点では各LambdaのArnは決定できていないので、${GreetingTask.Arn}や${SalutationsTask.Arn}のような変数になっている。

$.ステートに定義されたプロパティー名でステートの情報にアクセスできる。WaitInSecondsWaitToActivate状態では、ステートの状態を元にメソッドを数秒待ちたいので$.WaitInSecondsを参照してWaitしている。

この例では、単に上から下に流れるだけのステートしか定義していので、TaskタイプやWaitタイプしか使っていないけれど、StasteMacnine自体はAmazon States Language(https://states-language.net/spec.html)というDSLで定義されているので、Choiceタイプで条件分岐させたり、Parallelタイプで並列実行したりといったタスクを作ることもできる。

Choiceタイプでは、Lambdaから渡ってきたステート情報を元に分岐をかくことができるけれど、データ型に従った比較メソッドが用意されているのでステートの型と合うように分岐を書く必要がある。
https://dev.classmethod.jp/cloud/aws/aws-step-functions-states-choice/

ステートに日本語を使ったら、AWSにデプロイするときにステートが文字化けしてしまったので、英数字で定義するのが無難みたい。

とりあえずサンプルはココまで

カテゴリー:AWS タグ: ,

RiderのCodeVisionはVisual StudioのCodeLensっぽいもの

Rider 2018.3のEAPでCodeVisionというものが有効になるらしい。
Code Vision in Rider: Enriching the Editor with Contextual Information and Navigation(https://blog.jetbrains.com/dotnet/2018/12/10/code-vision-rider-enriching-editor-contextual-information-navigation/)

少し前の JetBrains “.NET” Night Tokyo 2018 @ Lifebear(https://lifebear.connpass.com/event/107559/)でJetBrainsのMaarten BalliauwさんのRiderでCodeLensっぽいものが表示されていて気になったんだけれど、EAPとして公開されたみたい。

Visual StudioのCodeLensに比べ、表示方法をカスタマイズできたり、取得できるメトリクスが多いみたいな違いがあるのかな。
コレって、Rider以外の他のIDEにも展開されるといいなー。

カテゴリー:メモ タグ:

Docker for Windows のバージョンをVersion 2.0.0.0-win78 (28905)にしたら、なぜかWindows Containerモードになってた

そのまま、常駐しているクジラを選択して、switch to linux containers… から linux containerに変更しました。

カテゴリー:未分類

asp.net core の Secret Manager

前回 .net coreにおける設定値の解決 で説明したとおり、asp.net coreの設定情報はWebHostBuilderで定義した順序でJSONファイルやら環境変数やらを合成していく方法が取られているわけなんだけれど、asp.net coreには開発環境の機密情報を管理する仕組みとしてSecret Managerがあります。
https://docs.microsoft.com/ja-jp/aspnet/core/security/app-secrets?view=aspnetcore-2.1&tabs=windows

これまでもgit-hubなどの公開リポジトリーにソースコードを公開する場合は、開発環境とは言えデータベースの接続文字列が入るのは気持ち悪いので、機密情報は通常の設定ファイルとは別にして.gitignoreで除外設定したりしていました(接続文字列が入ってたりすると、git-hubから警告メールも来るし)。

Secret Managerがこれまでと違うのは、これまでプロジェクトにJOINした人がREADME.mdなどに記載された情報を元に設定ファイルを作ったり、必要に応じて人にもらって設定ファイルをセットアップしていたのが、Secret Managerに値を設定する場合はREADME.mdなどにコマンドを書いておけば簡単に環境のセットアップができるようになったことです。

まぁ、企業のプライベートリポジトリーでソースコードを管理していて、かつ開発環境に対する設定なら、環境設定もめんどくさいので、xxx.{env}.json(connectionString.Development.jsonとか)なファイルに接続情報を書いて、リポジトリーに入れちゃってもいいかなぁと思わなくもないけれど。

今の所、保存する設定情報の種類で下記のように保存先を変更しています。

開発環境 検証環境、本番環境
環境によらないアプリケーション共通の設定 xxxxx.json xxxxx.json
環境ごとの設定 xxxxx.{env}.json xxxxx.{env}.json
データベースの接続情報などの機密情報 connectionStrings.{env}.configなど or Secret Manager コンテナの環境変数,Azure KeyValut, AWS Secrets Managerなど
カテゴリー:ASP.NET, 未分類