tech.guitarrapc.cóm

Technical updates

Pulumi の state を Export したり Importする

この記事は、Pulumi dotnet Advent Calendar 2019 の16日目です。

qiita.com

Pulumi を WebUI でProject/Stack 配下で管理するということは、ほかの Project/Stack に移動したいときに何か操作がいりそうですね。 それをどうするのか見てみましょう。

この記事の内容までで概ね、完全に理解した()といいたくなるのではないでしょうか。

目次

TL;DR

  • Pulumi は state をExport することで他の Pulumi Project に取り込むための json を取得できる
  • Exportされた json を取り込むときは Import を使う

Summary

State をほかの Pulumi Project で利用することが考えられる。 普段はまず意識する機会が少ないだろうが動作を確認しておく。

terraform でいうところの tfstate をほかの backend で管理するときの操作に相当する。

export

pulumi の Stack にある state を出力する。Pulumi のプロジェクトがあるパスでないと動作しないので注意。

pulumi stack export > stack.json

例えば空のスタックは次の state になっている。

{
    "version": 3,
    "deployment": {
        "manifest": {
            "time": "2019-11-20T12:45:40.5026846+09:00",
            "magic": "1d5f3b62e78892f89b30a2cc29a7eee340fe39a0f777515c72079024e9b448a6",
            "version": "v1.5.2"
        },
        "secrets_providers": {
            "type": "service",
            "state": {
                "url": "https://api.pulumi.com",
                "owner": "guitarrapc",
                "project": "pulumi",
                "stack": "dev"
            }
        }
    }
}

Import

標準出力からpulumiの Stack に State をインポートする。

pulumi stack import < stack.json

どこかの Stack を丸っと別の Pulumi アカウントにお引越しするときなどに便利。

Pulumi でstateから特定のリソースを消す

この記事は、Pulumi dotnet Advent Calendar 2019 の15日目です。

qiita.com

Pulumi の State から特定のリソースだけ管理対象外にしたい、そんなことはきっと起こります。 どのようにやればいいのか見てみましょう。

目次

TL;DR

  • pulumi state rm '<URN>' で消せる
  • URN には、$xxxx が混じるので、シングルクォートで囲っておくと安全。リソース名に$% のような特殊文字は使わないほうがよさそう
  • URN は pulumi stack --show-urns で表示できるので、pulumi stack --show-urns | grep xxxx にすると楽

想定ケース

pulumi で、Route53 を ComponentResource を使わずに組んでいた。

private Zone CreateRoute53Resource(string name, string zoneName, Dictionary<string, object> tags)
{
    var zone = new Zone($"{name}-zone-{zoneName}", new Pulumi.Aws.Route53.ZoneArgs
    {
        Name = zoneName,
        Tags = tags,
    }, new CustomResourceOptions { Parent = this });
    return zone;
}

これをグルーピングしたいので、ComponentResource で組むようにしたい。 早速書いてみて、CreateRoute53Resource は不要なので削除して pulumi up すると次のような実行プランになった。

     Type                       Name                                                    Plan
     pulumi:pulumi:Stack        pulumi-dev
     └─ pkg:EksClusterResource  sandbox
 +      ├─ pkg:Route53Resource  sandbox-route53                                         create
 +      │  └─ aws:route53:Zone  sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com  create
 -      └─ aws:route53:Zone     sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com  delete

delete になっているのが、もともとあったリソース定義である CreateRoute53Resource 部分。

ここを消さないようにしたい。 (既存のリソースを取り込むのは Pulumi で既存のリソースを取り込む | kinoco Kibela 参照)

対応方法

pulumi の state は pulumi state で操作できる。特定のリソースのstate を消すなら次のコマンドになる。

pulumi state rm <URN>

この時、pulumi のリソースごとに振られた一意のID、URN が必要になる。 URN は、pulumi stack --show-urns で一覧表示できる。

$ pulumi stack --show-urns

Current stack is dev:
    Owner: guitarrapc
    Last updated: 15 minutes ago (2019-11-19 11:21:31.7024525 +0900 JST)
    Pulumi version: v1.5.2
Current stack resources (51):
    TYPE                                                                     NAME
    pulumi:pulumi:Stack
       └─ aws:route53/zone:Zone                                              sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com
             URN: urn:pulumi:dev::pulumi::pkg:MyResource$aws:route53/zone:Zone::sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com

この例のように、リソース名とURN はセットになっているので、リソース名がわかるならそれで grep すれば結果は絞り込める。

$ pulumi stack --show-urns | grep eks-sandbox-pulumi.my.exmple.com

URN が特定できたら、pulumi state rm で削除すればok。 注意点として、URN に $ が混じっているため、シェルによっては変数扱いされたりする。そういった場合に備えて シングルクォート' で括るほうがいいだろう。

pulumi state delete 'urn:pulumi:dev::pulumi::pkg:MyResource$aws:route53/zone:Zone::sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com'

この状態で pulumi up してみると、先ほどあった delete はなくなっている。

     Type                       Name                                                    Plan
     pulumi:pulumi:Stack        pulumi-dev
     └─ pkg:EksClusterResource  sandbox
 +      └─ pkg:Route53Resource  sandbox-route53                                         create
 +         └─ aws:route53:Zone  sandbox-route53-zone-eks-sandbox-pulumi.my.exmple.com  create

REF

pulumi stack rm

Pulumi でリソースのパラメーターが変化しても無視 Ignore Changes する

この記事は、Pulumi dotnet Advent Calendar 2019 の14日目です。

qiita.com

すでに Pulumi で扱うこと自体はなるほど、という感じになってきましたでしょうか。 完全に理解した() を目指して、さらに進めてみます。

Terraform 作ったリソースだが、実際には Kubernetes や ECS、Code Deploy などいろいろな AWS リソースによって値が変わるということはよくあります。 こういった場合、そのリソースの影響を受けるプロパティに発生した変更を無視するようにすることが一般的にとられています。

Pulumi も当然同じことは起こりますが、どのようにすればいいのでしょうか? 見てみましょう。

目次

TL;DR

dotnet では、CustomResourceOptions.IgnoreChanges でプロパティを指定すればいいとあるが、実際は dotnet のプロパティではなくリソースのプロパティなので大文字/小文字など、実リソースとdotnet プロパティの言葉の差異に気を付けること。

つまり、 nameof(Property) は使えない。

基本

ResourceOption あるいは CustomResourceOption で IgnoreChanges プロパティに無視したいリソースのプロパティを指定する。

// Changes to the value of `prop` will not lead to updates/replacements
var res = new MyResource("res", new MyResourceArgs { prop = "new-value" }, new ResourceOptions { IgnoreChanges = { "prop" } });

リソースのプロパティ名を確認する

pulumi up をしたときに差分が表示されるが、[diff: ~propertyName] にある ~ 以降の文字列がリソースのプロパティとなる。 このプロパティ文字列を、CustomResourceOptions.IgnoreChanges プロパティで指定することで、IgnoreChanges が機能するようになる。

例えば、aws:ec2:LaunchConfigurationsandbox-asg-autoscale-lc リソースで ami id が変わっても無視したい場合を考える。

ami id が変わると、場合によっては ami のバグを踏んでしまう可能性があるため安定した ami id を都度指定したいなどの要求

pulumi up をした状態では次のように表示される。

$ pulumi up
Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           'dotnet build -nologo .' completed successfully
������
     pulumi:pulumi:Stack                   pulumi-dev                             2 messages
���������                        Name                       Plan        Info
     └─ pkg:component:ekscluster           sandbox
        └─ pkg:component:autoscaling       sandbox-asg
 +-        ├─ aws:ec2:LaunchConfiguration  sandbox-asg-autoscale-lc   replace     [diff: ~imageId]
 ~         └─ aws:autoscaling:Group        sandbox-asg-autoscale-asg  update      [diff: ~launchConfiguration]

注目するのはこのライン

 +-        ├─ aws:ec2:LaunchConfiguration  sandbox-asg-autoscale-lc   replace     [diff: ~imageId]

[diff: ~propertyName]に注目すると、imageId がリソースのプロパティとわかる。

C# コード上で、Pulumi リソースで CustomResourceOptions を渡すときに、IgnoreChanges にこの文字列を含める。

new LaunchConfiguration("sandbox-asg-autoscale-lc", new LaunchConfigurationArgs{
  // いろいろ定義
}, new CustomResourceOptions { Parent = this, IgnoreChanges = new List<string> { "imageId" } });

再度 pulumi up をすると、IgnoreChanges で指定した通り、ami id の変化が無視されるようになる。

$ pulumi up

Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           'dotnet build -nologo .' completed successfully
     pulumi:pulumi:Stack  pulumi-dev           2 messages
     Type                 Name        Plan     Info

Diagnostics:
  pulumi:pulumi:Stack (pulumi-dev):

Resources:
    57 unchanged

トラブルシュート

Q. IgnoreChanges に、nameof(Prop) と指定したが IgnoreChanges されない。

  1. リソースのプロパティ != C# Pulumi 上のプロパティ であるため、nameof(Prop) (C# なので Pascalになっている) だと IgnoreChanges されない。pulumi up や state、Web UI を見てリソースのプロパティを確認する必要がある。

REF

Pulumi - Programming Model - ignoreChanges

dotnet test にタイムアウトを仕掛ける

dotnet test をCI で実行していて、永遠に終わらないのを仕込んでしまったのですがどう対処するかです。

目次

TL;DR

  • dotnet test 自体に --timeout 引数はない
  • dotnet test としては Runsettings の RunConfiguration.TestSessionTimeout プロパティでtimeout制約を提供している
  • テスト全体の所要時間で決め打ちしてもいいなら timeoutコマンドを組み合わせることもできる
  • テストメソッドごとに [Fact(Timeout = int)] で提供も可能

特に、CircleCI を Performance Plan で実行していると、クレジットが目も当てられないことになります。

異常なテストを捕捉したい

捉えたい異常は、あるテストケースに誤って無限に実行されるバグを仕込んでしまった状況です。 今回の状況では、CircleCI の no_output_timeout ではタイムアウトがなされずテストが何時間も実行されていました。

どんなテストプロジェクトなのか見通してみます。

  • 1つのテストプロジェクトには多くのテストケースを書いている
  • CIで普段からテストを実行しているのでdotnet test ごとの所要時間は判明している
  • VSでも実行していることから、ほとんどのテストケースは1ms で終わっていることが個別に判明している
  • ごくまれにPRにおいて永遠にテストケースが実行されるバグが混じり、dotnet test が終了しない
  • CI 上は実行中のため failed にならず気づけない

この状況下では、やりたいことはテストケース1つ一つを気にするのではなく、テスト自体が異常かどうかを判別したくなります。

打てる手は大きく2つありそうです。

  • テスト実行 dotnet test をタイムアウトさせる
  • テストケースごとにタイムアウトを設定する

順にみてみましょう。

テスト実行 dotnet test をタイムアウトさせる

実行環境は、 CircleCI です。

dotnet test をタイムアウトさせるということは、個別のテストケースは気にせず、テストがこれぐらいの時間で終わってほしいことを明示することになります。 テストケースが数多くあり、どこで無限に終わらないバグを作るか予測はムズカシイので、個別のテストケースに Timeout をつけるよりも妥当そうです。

dotnet test をRunsettings.TestSessionTimeout で実行タイムアウトさせる

dotnet test --timeout のようなパラメーターがアレば簡単に設定できそうですが、残念ながら--timeout パラメーターは用意されていません。

docs.microsoft.com

dotnet test 的には、RunSettings の RunConfiguration.TestSessionTimeout でタイムアウトを設定できます。

gist.github.com

RunSettings は、2つの方法で設定できます。

RunSettings を dotnet test の引数で実行時上書きする

dotnet test の引数には、RunSettings arguments を使った RunSettings の実行時上書きが提供されています。 RunSettings の指定は、[name]=[value] ペアを -- 引数の後ろで指定することで利用できます。

TestSessionTimeout はRunConfiguration の子要素で、ミリ秒で指定します。 例えば、テスト実行を1秒でタイムアウトするように指定するにはこう書きます。

dotnet test  -- RunConfiguration.TestSessionTimeout=1000

このパラメーターは、テスト実行時間自体を見ているのでテスト開始前の時間は無視されます、純粋にテスト実行時間で指定すればいいので最高です。1

RunSettings をファイルで指定する

dotnet test-s--settings 引数を利用すると、RunSettings を定義した .runsettings ファイルを指定できます。

例えば先ほどの Sample.runsettings を指定するなら次のように書きます。

dotnet test --settings Sample.runsettings

ただ、.runsettingsファイルを指定するにはテストプロジェクトごとに runsettings ファイルのパスを仕込む必要があります。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <RunSettingsFilePath>$(SolutionDir)\example.runsettings</RunSettingsFilePath>
  </PropertyGroup>
  ...
</Project>

Configure unit tests with a .runsettings file - Visual Studio | Microsoft Docs

実行時の引数指定のほうがやりやすいので、私はこの方法は避けています。

timeout コマンドで dotnet test をタイムアウトさせる

.NETCore を想定しているので、CI 環境は Linux であることが多いかと思います。 ということは、安定のtimeout コマンドをつかってdotnet test 全体の時間に制約をかけることもできます。

timeout -sKill SECOND dotnet test

dotnet test 自体の実行時間が定まっているのであればなかなか便利なのでこれもおすすめです。

テストケースごとにタイムアウトを設定する

xUnit であれば、[Fact(Timeout = int)] を使ってテストケースごとにタイムアウトを仕掛けることができます。

gist.github.com

あらかじめ、このテストは時間がかかるなど個別にタイムアウトをさせたいケースでとても有用です。 特に、nightly バッチなどで実行させる時間のかかるテストでは個別に設定するのはうれしいでしょう。

実行側はタイムアウトを知らなくていいので、並列実行でも制御しやすいですしね。

まとめ

dotnet test で無限テスト実行、やらないとは言い切れないのでそんな時はtimeout を設定してあげましょう。

REF


  1. VsTest での接続タイムアウトは VSTEST_CONNECTION_TIMEOUT 環境変数で調整できます。(デフォ90sec)

Redis向け GUI ツール RedisInsight を使う

Redis を扱うにあたって、GUIでRedisの状態が把握できるツールがあると開発していて便利です。

最近は RDBTools を使っていたのですが、2か月ぶりにサイトをみると 2019/12/31 でEOLとのアナウンスと RedisInsight のGAリリースが出ていました。

このRedisInsight が、無料で使えるRedis GUIの中で完成度が高く、Redis Labs 本家が開発しているのでオススメできそうです。

目次

TL;DR

  • Redis のGUIツールとして、以前RDBTools を紹介したのですが31st December 2019 で EOL を迎えます
  • 後継ツールとして RedisInsight が RedisLabs からGAリリースされています
  • 安定しており、RDB Toolsにあった無料ユーザー制約も解除されて使いやすいのでオススメ

redislabs.com

その他のGUIツール

RedisLab で紹介されています。

So, You're Looking for the Redis GUI? | Redis Labs

  • RedisInsight
  • Redsmin
  • Redis Commander
  • Redis Desktop Manager
  • Induction (開発終了)

いろいろありますが好きなのを使うといいでしょう。 触った感触は RedisInsight がRDBToolsと同様の使いやすさで好みです。 Redis Desktop Manager は、どんどん好みとずれているので一年前から使うのをやめました。

もともとRDBToolsは HashedInが開発しており、Redis Labs が買収しました。 Redis Labs 案外こういう動きがあるので面白いです。

Redis Labs has acquired RDBTools from HashedIn

ダウンロード/インストール

ドキュメントを見つつ進めるといいでしょう。

docs.redislabs.com

インストール時の注意

もし、すでにRDBTools をインストールしていた場合注意が必要です。

RedisInsightはRDBTools とポートがバッティングするので、、先に RDBTools をアンインストールしてからインストールが必要です。 手元でアンインストールを忘れて入れたところ盛大にバッティングしたので、次の手順が安定だと思います。

  • RDBTools の停止
  • RDBTools アンインストール
  • RedisInsight インストール

Docker

docker で立ち上げておけばインストール不要で便利です。 docker-compose でローカルのRedisを起動する構成にしているなら、まとめて立ち上げてもいいでしょう。

docker run -d -v redisinsight:/db -p 8001:8001 redislabs/redisinsight

docs.redislabs.com

私はdocker-compose でredis と一緒にあげています。

version: "3"

networks:
  redis:
    driver: bridge

services:
  redis:
    image: redis:5.0.7-alpine
    ports:
      - 6379:6379
    command: redis-server --requirepass MY_SUPER_AWESOME_PASSWORD
    networks:
      - redis

  redisinsight:
    image: redislabs/redisinsight:latest
    ports:
      - 8001:8001
    volumes:
      - ./redisinsight:/db
    networks:
      - redis

ローカルインストール

自分のPCにインストールするなら、トップからダウンロードができます。

https://redislabs.com/redisinsight/

RedisInsightのダウンロード

まだインストーラーが Chocolatey/Scoop や Homebrew で提供されておらずダウンロードしてインストールが必要です。 ダウンロードには、フォーム入力で Email とかいろいろ入力求められるのでいい感じで入れます。

なおダウロードリンクです。

Linux上でも起動できるのでいい感じで使えるでしょう。 もちろん WSL 上の Ubuntu でいれて Windows から見るとかでもいいのです。

gist.github.com

起動

RDBTools 同様に、ブラウザで操作をします。

DockerやLinuxなら起動後メッセージが出るので、ブラウザでアクセスすればokです。

Starting webserver...
Visit http://0.0.0.0:8001 in your web browser.
Press CTRL-C to exit.

WindowsならRedisInsight でスタートメニュー検索で起動すると、localhost:8001 でホスティングしてブラウザが自動的に開きます。

Windows のスタートメニューで RedisInsightで検索

RedisInsightの起動ダイアログ

接続

任意のRedisに接続できるます。また、EC2から実行することで ElastiCache を自動探索したり、使っているならRedis Enterprise の探索オプションがあります。

自分の接続したいRedis Databaseの追加

ローカルに起動したRedis on Dockerへの接続を見てみましょう。

  • ローカルの Redis を docker-compose などで立ち上げます。(リモート先のRedisが接続可能ならそれでも
  • Add Redis Database を選んで接続情報を入力します
  • Nameは適当にどうぞ
  • Host は 名前解決面倒なので、127.0.0.1 で
  • Port は 6379 (プレースホルダーに見えるけど入れないとダメです
  • Password を設定しているなら入れましょう

ローカル にDockerで立てたRedisへ接続

接続情報が正しければつながるのが確認されて登録できます。

一覧に戻るので自分で入れたNameのRedisを選択してお好きにどうぞ!

追加したDatabaseが表示される

できること

RDBTools と基本UI構成は同じですが機能的には圧倒的にいいです。 とりあえず欲しい機能はほぼすべて入っています。クラスタシャード管理がないぐらいかな

  • 無料でも時間制限がなくなった (RDB Tools で無料ユーザーだとこの制約がありました)
  • CLIがブラウザから実行できる(別途インストールも不要
  • Stream/Profiler のサポート
  • SlowLog も確認できます
  • メモリを Exportできるので、ほかの人の環境に突っ込むこともできます。(RDBがいいですね。人間が見たり、プログラムで分析するなら JSON
  • Config の確認
  • クライアント一覧の確認

CLIの実行

Redis CLIの実行

Stream への接続

Streamへの接続

Analysis

Analysisでキーの状況を分析

キーごとのMemory の使用量を確認

データのExport

JSON で出力できるのはうれしいですね。

{"hashkey_01" : {"address": "127.0.0.1", "port": "80", "name": "pod-12345"}}

Configを確認したり書き換えが可能です。

接続しているクライアントを確認できるのもいいでしょう。

できないこと

UI上は RDB Tools にあった Cluster Management が消えているように見えますが、それ以外は拡充されているように見えます。

RDBToolsにあった Cluster Management

まとめ

今のところ触った限りでは Windows, macOS 上で動作も安定しています。 ローカル開発環境は docker-compose で Redis/DB がまとめて起動するので、組み込みました。

RedisInsightに限らず、操作をブラウザから行うツールが最近増えていますが、非常に使いやすいので今後も増えるとうれしいですね。

RedisInsight おすすめです。