ようへいの日々精進XP

よかろうもん

【とりあえず走り書き】Auth0 で Custome Claim を ID Token に含める方法

tl;dr

Auth0 でユーザー登録した際に一緒にポストしたメタデータとか ID Token に含める方法を調べたので, 取り急ぎメモ. 以下の記事 (というかドキュメント) が参考になりました.

auth0.com

とりあえず走り書き

メタデータ

Auth0 において, ユーザーに紐付けることが出来るメタデータには二種類あって, user_metadata と app_metadata が利用可能です. どちらをどのように使うかについては未調査です.

Custom claim を ID Token に含める

ID Token の標準的な情報 (Standard claims) 以外が Custom claim となり, これを ID Token に含める為には, Rule で以下のコードを書きます.

function (user, context, callback) {
  const namespace = 'https://myapp.example.com/';
  context.idToken[namespace + 'nickname'] = user.user_metadata.nickname;
  callback(null, user, context);
}

上記は user_matadata に登録されている nickname を ID Token に含める場合のコード例です. 尚, user_metadata には以下のように登録されています.

{
  "nickname": "testtest"
}

Auth0 の Rule 自体の詳細については割愛しますが, ユーザーがアプリケーションを認証する際に実行する JavaScript の関数で認証のプロセスが完了すると実行され, Auth0 の機能をカスタマイズして拡張することが出来るものとなります.

以上

走り書きでした. 引き続き, 時間を見つけてメモります.

2019 年 03 月 23 日 (土)

ジョギング

  • 右足くるぶしの下に痛みが気になったので一回休み

奥さんとデート

  • 奥さんが新宮のパン屋さんで明太子塩パンを爆買いしてた
  • 確かに, 爆買いしたくなるほど美味しい
  • その後は東京インテアリアでカーテンなどを購入

Apple Magic Trackpad 2 を購入した

周りの方たちの評判が良いので、奥さんに頼み込んで買ってもらいました。入力デバイスにはお金を惜しむなという婆ちゃんの教えの通り、ちょっとお高めのスペースグレイデルモです。

いい感じです.

2019 年 03 月 21 日 (木)

ジョギング

  • 自宅からグルっと博多, 天神の街を走ってきた
  • 45 分くらい
  • 懸垂 x 5
  • 右足の踵のくるぶし下のあたりに痛み

体調を崩す (6)

  • やっと戻ってきた感じ

奥さんとデート

イチロー選手引退

【細かすぎて伝わらないかもしれない tips】時代はイミュータブルインフラストラクチャだけど, 敢えて monit について書いてみる

tl;dr

お仕事にて, 指定したプロセスが停止したら (それだけではないですが), 自動的にそのプロセスを起動してくれる monit というツールを使いました.

mmonit.com

monit の詳細については, インターネット上の記事がたくさんありますので, そちらをご一読ください.

monit は 2000 年前半にリリースされたいにしへのツールで, 現在でもメンテナンスが続けれています. monit が出来ることは以下の通りで, その気になれば, monit だけでサーバーの運用が出来てしまうと思います.

  • プロセス, ファイル, サービスの監視
  • 監視対象に異常を検知したら, 対象のプロセスの再起動, 指定したメールアドレスに通知, 指定したコマンド実行

サーバーリソースは使い捨てが望ましいと言われている時代, サーバーはコンテナ化され, そのコンテナのオーケストレーションも自動化されていく中で, 1 台のサーバーの中で個々のサービスプロセス自体の死活を監視し, 再起動を促すようなツールの存在意義が薄れてしまいそうな状況にありながら, monit は現在もバージョンアップを重ねていることは素晴らしいことだと思います.

ということで, 今回, 格闘した環境は以下の通りです.

$ cat /etc/system-release
Amazon Linux AMI release 2018.03

$ monit -V
This is Monit version 5.2.5
Copyright (C) 2000-2011 Tildeslash Ltd. All Rights Reserved.

先述の通り, monit については, インターネット上に多くの記事がありますので, そちらの記事の方が monit の出来ることや良さ, 欠点等も網羅されているかと思います. あくまでも, 本記事は自分が monit でやりたかったことをどのように実現したか, 簡単なデモを交えて書いていきます.

俺はこうした

プロセスが停止したら, 再起動して欲しい

シンプルな例です.

Apache のプロセスが停止したら, Apache のプロセスを再起動して欲しい場合, monit の設定は以下のように書きます. 尚, 設定は /etc/monit.conf に書いても良いですし, /etc/monit.d/ 以下に任意のファイル名で作成しても構いません.

check process httpd with pidfile "/var/run/httpd/httpd.pid"
  start program = "/sbin/service httpd start"
  stop  program = "/sbin/service httpd stop"

/var/log/monit には以下のようなログが記録され, httpd プロセスは自動的に起動されています.

[UTC Mar 20 23:43:20] error    : 'httpd' process is not running
[UTC Mar 20 23:43:20] info     : 'httpd' trying to restart
[UTC Mar 20 23:43:20] info     : 'httpd' start: /sbin/service
[UTC Mar 20 23:44:21] info     : 'httpd' process is running with pid 2686

尚, monit はデフォルトは 60 秒間隔で監視対象をチェックしています.

プロセスが停止したら, 通知してから再起動して欲しい

人間というのは, 色々と欲が出てくるもので, 再起動してくれるんなら, 一報入れてから再起動してよって思ってしまいます. そんな時には以下のように設定を書きました.

check process httpd with pidfile "/var/run/httpd/httpd.pid"
  start program = "/sbin/service httpd start"
  stop  program = "/sbin/service httpd stop"
  if does not exist
    then exec "/bin/bash -c '/path/to/bin/slak -env /path/to/bin/.env && /sbin/service httpd restart'"
    else if succeeded then exec "/path/to/bin/slak -env /path/to/bin/.env"

上記の例は, exec コマンドを使って, Slack に通知する為の別のスクリプトを叩いている例となります. キモというか, 苦肉の策となるのが, 以下の部分です.

  if does not exist
    then exec "/bin/bash -c '/path/to/bin/slak -env /path/to/bin/.env && /sbin/service httpd restart'"

if does not exist は, 監視対象のプロセスが存在しない場合という条件で, その条件が真となる場合に then exec 以降が実行されることになりますが, 複数のコマンドを実行する場合には /bin/bash -c... で書きはじめて && でコマンドを連結するみたいな書き方をする必要がありました.

httpd プロセスが停止すると, Slack の指定したチャンネルに以下のように通知されます.

f:id:inokara:20190321092926p:plain

/var/log/monit には以下のようなログが記録されます.

[UTC Mar 21 00:02:27] error    : 'httpd' process is not running
[UTC Mar 21 00:02:27] info     : 'httpd' exec: /bin/bash
[UTC Mar 21 00:03:27] info     : 'httpd' process is running with pid 3153
[UTC Mar 21 00:03:27] info     : 'httpd' exec: /path/to/bin/slak

プロセスの再起動が完了したら通知して欲しい

せっかくなので, monit によって再起動が完了したら, 通知してもらいましょう. 既に, 設定は掲出していますが, 改めて, 以下に記載します.

check process httpd with pidfile "/var/run/httpd/httpd.pid"
  start program = "/sbin/service httpd start"
  stop  program = "/sbin/service httpd stop"
  if does not exist
    then exec "/bin/bash -c '/path/to/bin/slak -env /path/to/bin/.env && /sbin/service httpd restart'"
    else if succeeded then exec "/path/to/bin/slak -env /path/to/bin/.env"

ポイントは, else if succeeded then exec "/path/to/bin/slak -env /path/to/bin/.env" となります. これは, monit によるチェックが成功したら exec 以降の処理を実行することを意味しています.

全体的な処理の流れとしては, httpd プロセスが停止したら, Slack に通知してから httpd プロセスの再起動を行い, 60 秒後 (厳密には 60 秒よりも短い) の次のチェックでプロセスチェックに成功 (プロセスが存在) していたら Slack に通知という感じです.

PID ファイルではなく, プロセス名で監視したい

with pidfile ではなく, matching を利用します.

check process httpd matching "httpd"
  start program = "/sbin/service httpd start"
  stop  program = "/sbin/service httpd stop"

matching の後のプロセス名がちゃんとチェックされるかどうかは, 以下のコマンドを利用してチェックすることが出来ます.

$ sudo monit procmatch httpd

以下のように出力されます.

List of processes matching pattern "httpd":
------------------------------------------
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
        /usr/sbin/httpd
------------------------------------------
Total matches: 9
WARNING: multiple processes matched the pattern. The check is FIRST-MATCH based, please refine the pattern

どんな風にモニタリングされているのか知りたい

monit -vI を実行します.

$ sudo monit -vI

以下のように出力されます.

Process Name          = httpd
 Pid file             = /var/run/httpd/httpd.pid
 Monitoring mode      = active
 Start program        = '/sbin/service httpd start' timeout 30 second(s)
 Stop program         = '/sbin/service httpd stop' timeout 30 second(s)
 Existence            = if does not exist 1 times within 1 cycle(s) then exec '/bin/bash -c /path/to/bin/slak -env /path/to/bin/.env && /sbin/service httpd restart' timeout 0 cycle(s) else if succeeded 1 times within 1 cycle(s) then exec '/path/to/bin/slak -env /path/to/bin/.env' timeout 0 cycle(s)
 Pid                  = if changed 1 times within 1 cycle(s) then alert
 Ppid                 = if changed 1 times within 1 cycle(s) then alert

System Name           = system_ip-xx-x-x-xxx.ap-northeast-1.compute.internal
 Monitoring mode      = active

以上

今回, monit のほんの一部の機能を使ってみました. 通知の部分については, ちょっと格闘しちゃいましたが, プロセスが停止したら再起動させるというシンプルな使い方であれば, とても簡単に使うことが出来ました.

ということで, 素敵な monit ライフをお過ごしください.

おまけ

monit から Slack に通知するコマンドは初心者レベルの Go 言語で作ってみました. クソコードでお恥ずかしい限りですが, おまけとして掲載させて頂きます.

package main

import (
    "bytes"
    "flag"
    "fmt"
    "github.com/joho/godotenv"
    "net/http"
    "os"
    "strings"
)

const (
    AppVersion = "0.0.1"
)

var (
    argEnv     = flag.String("env", "", ".env ファイルのパスを指定.")
    argVersion = flag.Bool("version", false, "バージョンを出力.")
)

func loadEnv(envPath string) {
    var err error
    if envPath != "" {
        err = godotenv.Load(envPath)
    } else {
        err = godotenv.Load()
    }

    if err != nil {
        fmt.Println("Error loading .env file")
        os.Exit(1)
    }
}

func main() {
    flag.Parse()

    if *argVersion {
        fmt.Println(AppVersion)
        os.Exit(0)
    }

    loadEnv(*argEnv)
    username := os.Getenv("SLACK_USER_NAME")
    channel := os.Getenv("SLACK_CHANNEL")
    icon_emoji := os.Getenv("SLACK_ICON")

    descStr := `{"title":"description","value":"` + os.Getenv("MONIT_DESCRIPTION") + `"}`
    var text string
    var attachmentStr string
    if strings.Contains(os.Getenv("MONIT_DESCRIPTION"), "not running") {
        text = os.Getenv("MONIT_HOST") + " にて " + os.Getenv("MONIT_SERVICE") + " プロセスが停止しました."
        attachmentStr = `{"color":"#ff0000","fields":[` + descStr + `]}`
    } else {
        text = os.Getenv("MONIT_HOST") + " にて " + os.Getenv("MONIT_SERVICE") + " プロセスが再起動しました."
        attachmentStr = `{"color":"#008000","fields":[` + descStr + `]}`
    }
    jsonStr := `{"channel":"` + channel + `","username":"` + username + `","text":"` + text + `","icon_emoji":"` + icon_emoji + `","attachments":[` + attachmentStr + `]}`

    req, err := http.NewRequest(
        "POST",
        os.Getenv("SLACK_URL"),
        bytes.NewBuffer([]byte(jsonStr)),
    )
    if err != nil {
        fmt.Print(err)
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Print(err)
    }

    // fmt.Print(resp)
    defer resp.Body.Close()
}

monit は監視対象に異常を検知した場合, 以下のような環境変数に値をセットします.

  • MONIT_HOST
  • MONIT_SERVICE
  • MONIT_DESCRIPTION

これらを利用して, プロセスが停止した場合, 再起動した場合の処理分岐を行っています. また, Slack の Incomming Webhook の URL 等の情報は .env ファイルに書いて, 別で管理することを想定しています. コマンドは以下のように実行します.

/path/to/bin/command -env /path/to/env

2019 年 03 月 20 日 (水)

ジョギング

  • 久しぶりに山王公園を
  • 懸垂 5 回

体調を崩す (5)

  • 朝起きた時のムカつきは相変わらず
  • 倦怠感もあまり変わらない気がするけど
  • なんとか一日過ごせた

仕事

  • EC2 を立てて, その中に諸々のミドルウェアを仕込むという仕事の目処が立った
  • サーバーは使い捨てのご時世だけど, ちゃんと動かそうとすると色々と考慮する点があり (というか考慮すべき点を忘れていて) 思ったよりも時間がかかってしまった
  • 時間はかかったけど, まとめるものは纏めた (ドキュメントとか) し, 一応, 一端のインフラストラクチャアズコードはやれたはずのでリハビリとしては良かった

【細かすぎて伝わらないかもしれない tips】Terraform の Ramdom プロバイダ が地味に便利です

tl;dr

Availability Zone のどっちか!とか, Subnet ID のどっちか!をランダムに欲しい時にどうしようかなと思っていたら, Terraform には Random Provider というプロバイダが用意されていたので, 色々と触ってみました.

www.terraform.io

ここのリソースの細かいオプションはドキュメントを読みましょう.

尚, 検証に利用した環境は以下の通りです. 最新でなくてごめんなさい.

$ terraform version
Terraform v0.11.11
+ provider.random v2.1.0

Your version of Terraform is out of date! The latest version
is 0.11.13. You can update by downloading from www.terraform.io/downloads.html

俺はこうした

random_id リソース

何が出来るか

何に使えるか

  • リソースにユニークな ID を付与したい時など

tf サンプル

resource "random_id" "sample" {
  byte_length = 8
}
output "ramdom_id-b64_url" {
  value = "${random_id.sample.b64_url}"
}
output "ramdom_id-b64_std" {
  value = "${random_id.sample.b64_std}"
}
output "ramdom_id-hex" {
  value = "${random_id.sample.hex}"
}
output "ramdom_id-dec" {
  value = "${random_id.sample.dec}"
}

output

Outputs:

ramdom_id-b64_std = +5xe3FnkIKw=
ramdom_id-b64_url = -5xe3FnkIKw
ramdom_id-dec = 18130470500380713132
ramdom_id-hex = fb9c5edc59e420ac

random_integer リソース

何が出来るか

何に使えるか

  • ランダムな数字が欲しい時
  • プライオリティの指定とか

tf サンプル

resource "random_integer" "sample" {
  min     = 1
  max     = 99999
}
output "ramdom_integer" {
  value = "${random_integer.sample.result}"
}

output

Outputs:

ramdom_integer = 16343

random_pet リソース

何が出来るか

何に使えるか

  • リソースにちょっと癒やしを求めたユニークな ID を付与したい時など

tf サンプル

resource "random_pet" "sample" {}
output "ramdom_pet" {
  value = "${random_pet.sample.id}"
}

output

Outputs:

ramdom_pet = lenient-viper

random_shuffle リソース

何が出来るか

何に使えるか

  • どちらかの Availability Zone (ap-northeast-1a または ap-northeast-1c) が欲しい時

tf サンプル

resource "random_shuffle" "sample" {
  input = ["foo", "bar", "buz", "qux", "quux"]
  result_count = 2
}
output "ramdom_shuffle" {
  value = "${random_shuffle.sample.result}"
}

output

Outputs:

ramdom_shuffle = [
    bar,
    buz
]

random_string リソース

何が出来るか

何に使えるか

  • ランダムな文字列が欲しい時
  • パスワードとか

tf サンプル

resource "random_string" "sample" {
  length = 16
  special = true
}
output "ramdom_string" {
  value = "${random_string.sample.result}"
}

output

Outputs:

ramdom_string = 0ucqy18F+VR9lhC5

random_uuid リソース

何が出来るか

何に使えるか

  • リソース名に UUID を含む文字列で名前をつける必要がある時 (ってどんな時?)

tf サンプル

resource "random_uuid" "sample" {}
output "ramdom_uuid" {
  value = "${random_uuid.sample.result}"
}

output

Outputs:

ramdom_uuid = d8a65bb0-46c4-17a0-ab47-ba1d7b15d562

以上

ざっくりと Ramdom プロバイダを触ってみました. まじで Terraform ってなんでも出来ますよね.

【細かすぎて伝わらないかもしれない tips】Terraform でアロケーション済みの IP を EC2 にアタッチする方法

tl;dr

EC2 を作るときに一緒に EIP を払い出すようなサンプルはよく見かけるんだけど, 既に払い出している EIP に EC2 をアタッチする場合, Terraform ではどう書くのか調べたのでメモしておきます.

尚, 検証に利用した環境は以下の通りです. 最新でなくてごめんなさい.

$ terraform version
Terraform v0.11.11
+ provider.random v2.1.0

Your version of Terraform is out of date! The latest version
is 0.11.13. You can update by downloading from www.terraform.io/downloads.html

俺はこうした

その前に

EC2 と EIP を一緒のタイミングで作って, EC2 にアタッチする場合の tf ファイルは以下のような感じで書きました.

resource "aws_eip" "my-eip" {
  instance                = "${aws_instance.your-instance.id}"
  vpc                     = true
}

resource "aws_instance" "your-instance" {
...
  subnet_id                   = "${random_shuffle.launch_subnet_id.result[0]}"
  vpc_security_group_ids      = ["${aws_security_group.your-security-group.id}"]
...
}

アロケーション済みの EIP を EC2 にアタッチする場合

aws_eip_association リソースを使うと良かったです.

variable "eip-allocation-id" {
  default = "eipalloc-1234567890abcdefg"
}

resource "aws_eip_association" "eip-association" {
  instance_id   = "${aws_instance.your-instance.id}"
  allocation_id = "${var.eip-allocation-id}"
}

resource "aws_instance" "your-instance" {
...
  subnet_id                   = "${random_shuffle.launch_subnet_id.result[0]}"
  vpc_security_group_ids      = ["${aws_security_group.your-security-group.id}"]
...
}

EIP と関係ないですが, 複数の Availability Zone に別れたサブネットのどちらかに EC2 を所属させたい時に random_shuffle リソースを使うと良かったです. このネタは別の記事にて.

以上

  • Terraform は奥深い

2019 年 03 月 19 日 (火)

ジョギング

  • 引き続き, おやすみ
  • そろそろ走らないといけない

体調を崩す (4)

  • 朝から体調がいまいちでムカつきと体のだるさがあった
  • 熱いシャワーをガッと浴びて気合を入れて仕事へ...有給が無いので仕方 nothing

monit (2)

  • 通知周りは妥協案で解決
  • init.d のスクリプトを書いたり, 検証したり, itamae レシピに落とし込んだりしてた

2019 年 03 月 18 日 (月)

ジョギング

  • 引き続き, おやすみ

体調を崩す (3)

  • 仕事をお休みしたかったけど, 既に有給がないので気持ちは這いつくばる感じで出社
  • 午前中は, 寒気とムカムカがひどかったけど, 午後から持ち直した感じ

monit

  • 久しぶりに monit でプロセスが死んだら, 自動的に立ち上げる仕組みを作ったけど...
  • 通知周りが思ったように動かなくて一回休み