ばーろぐわにる

SIerからWEB系?インフラエンジニアにジョブチェンジした見習いの備忘録。投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。

bashで変数の中で変数を使う

今回も既出の情報シリーズをお送りいたします

やりたいこと

例えば ${hoge_${fuga}} とか ${hoge_$(echo ${fuga})} みたいなことがやりたいと思ってもそのままやろうとすると怒られる。

$ echo ${hoge_${fuga}}
bash: ${hoge_${fuga}}: bad substitution
$ echo ${hoge_$(echo ${fuga})}
bash: ${hoge_$(echo ${fuga})}: bad substitution

なぜなら、変数展開時の記述方式を満たしていないので

Shell Parameter Expansion (Bash Reference Manual)

evalを使う

evalは式の展開を2段階に分けれます

「eval」コマンドは、指定した文字列を評価後に連結して、現在のシェルに実行させます。主にシェルスクリプトや環境設定用のファイルで使用します

www.atmarkit.co.jp

$ hoge=hoge
$ fuga=fuga
$ hoge_fuga=hogefuga
$ eval "echo \${hoge_${fuga}}"
hogefuga

最初の式の展開では評価してほしくない ${hoge_${fuga}}\エスケープしています。なので ${fuga} だけが最初に評価され、その後に echo ${hoge_fuga} が評価されるので構文エラーになりません。もちろん $() も使えます。

便利ですね!

メールヘッダーのRFCを読んだメモ

なにこれ

RFC5322を読んだメモ

この辺を使って読みました。フィールド定義のところしか読めてないですが

Mouse Dictionary - Chrome Web Store

Google Translate - Chrome Web Store

RFC日本語訳一覧

イントロダクション

  • RFC5322はRFC2822の更新版
  • メールヘッダー(封筒って言われている)の仕様のみを記載している

フィールド定義

  • フィールド名:フィールド値 で書かれる
  • フィールドの順序は保証されていない
  • 必須のフィールドはorigination date(orig-date), originator address(from)のみ。他はオプション扱い

The Origination Date Field

orig-date

  • orig-date = "Date:" date-time CRLF
    • 作成者がメッセージを作成完了し、メール転送準備が整ったとき。
      • メールの送信ボタンを押したときとか。
      • 転送の開始時間などは考慮されてない

Originator Fields

  • from = "From:" mailbox-list CRLF
    • 送信元
  • sender = "Sender:" mailbox CRLF
    • Fromに複数のメールボックス情報が含まれている場合、Senderに単一のメールボックス情報が含まれる
    • 作成者と送信者が異なる場合、Senderに送信者が入る
      • RFCには秘書がメールを送信した場合のケースが書かれているが、具体的にどんな場面で使われるのか想像がつかない
      • Fromが複数=連名で送る場合とからし
  • reply-to = "Reply-To:" address-list CRLF
    • 返信時に送信すべき宛先。なければFromに対して送信される

Destination Address Field

  • to = "To:" address-list CRLF
  • cc = "Cc:" address-list CRLF
    • cc -> carbon copyの略
  • bcc = "Bcc:" [address-list / CFWS] CRLF
    • bcc -> blind carbon copy
    • to, ccのメールアドレスに送るときにはbccは削除される
    • bccのアドレスに送信するときはそのまま残る
      • 実装によってはbccのリストに載っているメールアドレスに対して、個々のメールアドレスのみを記載したbccで送信してくれる
      • bccに1つもアドレスを含めずにTo, Ccへ送信することで、誰かにbccが送信されたことを示す実装がある
      • bccの使い方は実装によって異なる

Identification Fields

  • message-id = "Message-ID:" msg-id CRLF
    • 3.6の図だとオプショナル扱いだが、全てのメッセージに必要(SHOULD)
    • 特定のバージョンの特定のメッセージを特定できる一意な識別子。一意であることは生成するホストで保証する
    • メッセージが変更されてもmessage-idは変更されない
  • in-reply-to = "In-Reply-To:" 1*msg-id CRLF
    • 全てのReplyのメッセージに必要(SHOULD)
    • 親メッセージ(返信元)のmessage-idが含まれる。親メッセージが複数の場合は複数
    • 新しいメッセージの場合、in-reply-toフィールド自体が存在しない
  • references = "References:" 1*msg-id CRLF
    • 親メッセージにrefelencesフィールドが存在する場合
      • 親のreferencesフィールドに親のmessage-idを加える
    • 親メッセージにrefelencesフィールドが存在しないが、in-reply-toフィールドが存在する場合
      • 親in-reply-toフィールドの値をreferencesに格納、その後に親のmessage-idを加える
    • 親メッセージにin-reply-to, references, message-id(これは必須では?)が含まれない場合
      • referencesフィールドは付与しない
        • これだけ見ると最初の返信メッセージにはreferencesフィールドは付与しないように見えるが、Gmailでは最初に返信したメッセージにもreferencesフィールドが付与されている
      • スレッド機能はreferencesフィールドをたどることで実装されている。故に複数の親メッセージを持つreplyに対してreferencesフィールドを生成することは非推奨
  • msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
    • グローバルにユニークなidになっていること
    • 生成方法はいくつかあるが以下を推奨
  • id-left = dot-atom-text / obs-id-left
  • id-right = dot-atom-text / no-fold-literal / obs-id-right
  • no-fold-literal = "[" *dtext "]"

Informational Fields

  • 読み飛ばした

Resent Fields

  • 読み飛ばした

Trace Fields

  • 読み飛ばした

単語メモ

  • envelope
    • 封筒, 包むもの
  • semantics
    • セマンティクス。意味論?
  • facilitate
    • 楽にする、容易にする
  • dictate
    • 口述する、意図する
  • consist
    • 構成する、成り立つ
  • for instance
    • 例えば

参考

メール関連のRFC | SendGridブログ

「入門 監視」を読んだ

なにこれ

こちらを読んだのでそのメモです。

www.oreilly.co.jp

メモ

1章 監視のアンチパターン

  • ツールで監視を決めるな。やりたいことに合わせてツールを選び、組み合わせ、必要であれば作る
  • 監視はスキル。全員が一定水準を満たすべき
    • 誰かに責任を押し付けてはいけない
  • メトリクスは高頻度で投げる。最低でも1分に1回
  • 障害→監視の追加を繰り返すのはNG。何回か繰り返すようならアプリケーションを安定して動くように作り直すべき

2章 監視のデザインパターン

  • できる限り構造化ログを使う
    • ログ量が少なかったり、複雑な解析を行わなければ非構造化でもよし
  • ダッシュボードはそのシステムを一番知ってる人に作らせる
  • メトリクスはアラートを出すために集めているわけではない。質問を投げかけるためにある
    • エモい
  • 使えるならSaaS使おう

3章 アラート、オンコール、インシデント管理

  • 通知方法
    • 即時対応が必要→SMSなどのページャ
    • 注意が必要→チャット
  • アラートレポートで改善点を見つけ出す
  • オンコール
    • バックアップ担当は作らないほうがいい。2倍のシフト数になる
    • オンコール担当は責任を負う。何回も対応できないなら問題あり
  • インシデント管理プロセスは作っておこう

4章 統計入門

  • 中央値はサンプリングしたデータの真ん中の値
    • (n+1)/2 番目の値
  • パーセンタイル
    • データを昇順にならべて小さい方から数えて任意の位置に存在する値
    • 90パーセンタイル → 小さい順から並べて90番目のデータ
  • 標準偏差
    • 正規分布なデータにしか適用できない。監視では使えないことが多い

5章 ビジネスを監視する

  • ビジネスKPIはアプリケーション、インフラの調子をも示してくれる
  • KPIを全員がすぐに目が届くところに表示しておく
  • 成功よりも失敗のメトリクスを追う。Goal(サービスが満たすべき目標)に直結するので
  • 何をKPIにすべきかは人と話して決める
    • プロダクトオーナー、エンジニアリングマネージャ、シニアエンジニア

6章 フロントエンド監視

  • フロントエンドのパフォーマンス監視のゴールは動き続けることではなく、素早くロードされること
  • フロントエンドのパフォーマンス改善はビジネスに大きなインパクトを与える
    • 特にロード時間。業績に影響を与えた実績あり
    • ロード時間は4秒以下を目指せ
  • JavaScriptの仕組みは理解しておこう(=監視対象の仕組みは理解しておこう)
    • <script> はDOMのパースを中断してスクリプトのダウンロードをおこなう。故にスクリプト数を増やせば増やすほどロードに時間がかかる。
    • HTML5でasync属性が追加、バックグラウンドでスクリプトをダウンロードできるようになった。
  • Navigation Timing API
    • ブラウザが提供するAPI。ここを通じてページパフォーマンスのメトリクスを公開
    • 各処理の開始、終了時間をメトリクスとして提供
  • ログ収集はSaaSを使おう
    • Sentryとか
  • シンセティック監視
    • 外部監視的なやつ。外からcurl叩くイメージ
    • CIでロード時間を定期的に計測

7章 アプリケーション監視

  • APMはビジネスコンテキストまでは理解してくれてない。当然だけど。
  • ビルド、デプロイの監視
    • デプロイ後、エラー率が高まってないかの相関を調べれる
  • healthエンドポイントパターン
    • ヘルスチェック用のエンドポイントを作る
    • 依存性があるリソース(DB, Cache, 外部エンドポイント)があれば、その接続まで含めてヘルスチェックしたほうがよい
      • ただし、リソースにかかる負荷が大きくなるので要検討
  • アプリケーションロギング
    • 構造化ログを使おう
    • ログにするかメトリクスにするかはユースケース次第。チームで相談しよう
  • ログレベルの問題点
    • ログレベルはアプリケーションを作るときに決まる。また、全て正常に動いている前提で重要度が振られる
      • ある問題が発生した場合、DEBUGレベルの情報が必要な場合でもデフォルトがINFOで取得してたら情報を失う
      • インフラの構成によってはそれはERRORなのか?というものも出てくる
      • 例えばリトライ処理もできている場合、接続失敗はERRORとして出すか?
  • ログはディスクに書き込み、その後ネットワーク越しに送るが良い
    • ログ送信が非同期になるから
  • 分散トレーシング
    • マイクロサービスなど1リクエストが複数のサービスにまたがって処理される場合に有用
    • リクエストに一意なIDを付与、そのIDをつけて回ることでリクエストを追跡して監視、各サービスでどれぐらい時間がかかったかを計測できる

8章 サーバ監視

  • OS標準(CPU, MEM, DISK)のメトリクスは取得するが、アラートは設定しないのがおすすめ
  • メモリ
    • /proc/meminfo
      • Buffers
      • Cached
        • 最近アクセスされたファイルのコンテンツのキャッシュ
      • MemAvairable
        • 利用可能なメモリ領域
    • OOM Killer
      • システムログを killed processgrepすれば発見できる
      • OOM Killerが発生したらアラートを投げる仕組みを作っておいたほうがよい
  • ディスク
    • /proc/diskstats から情報を取得してることが多い
    • iostat -x
      • iowaitが高くなってないか
      • utilが100%になってないか
  • ロードアベレージ
    • CPUに処理してもらうのを待っているプロセスがいくつあるか
    • システムによっては高くても動くものはあるので一概に高いと良くないとは言えない
  • SNMP
    • サーバ監視で使うのはやめたほうがよい
  • WEBサーバ
    • 秒間リクエスト数
    • 5XX、4XXの数
  • データベースサーバ
    • コネクション数
      • MySQLでは=スレッド数
    • 秒間クエリ数
  • ロードバランサ
    • WEBサーバと同じ
  • キュー
    • キューの長さ
    • キューの消費率
  • キャッシュ
    • キャッシュから追い出されたアイテム数
    • ヒット、ミス比率
  • NTP
    • ntpstatコマンドの終了コードで監視するのがおすすめ
  • スケジュールジョブの監視

9章 ネットワーク監視

  • 現状すぐに利用することはなさそうなので読み飛ばした

10章 セキュリティ監視

11章 監視アセスメントの実行

  • 監視アセスメントのデモだったので省略

感想

書かれていることが普段の監視でやっていること大きなズレが無くて安心しました。サーバ監視の章は具体的なコマンド、実装例が書かれていて参考になりました。また監視を構築する上でコミュニケーションはとても大切で、それが今足りてないかもなという気づきもあり読んでよかったです。

Linuxコンテナのタイムゾーンを変更する

最初にしていた勘違い

コンテナのタイムゾーンってホストOSのタイムゾーンが自動的に同期されるよね

されませんでした

Linuxってどこでタイムゾーンを設定してるの?

色々調べたところ、以下の優先順位で設定されます。

  1. 環境変数 TZ
  2. /etc/localtime (/usr/local/etc/localtime)

ただしこれはGNU Cライブラリを利用している場合、他のCライブラリを使っている場合は異なる挙動になると思います。これはPOSIXというUNIX OSの標準規格で定義されているようです。

www.gnu.org

docker runしてコンテナを起動するとき、何もオプションを付けなければ環境変数TZには何も入っていないので /etc/localtime が利用されます。このファイルはコンテナイメージ独自のものなので残念ながらホストOSのタイムゾーンが同期されることはありませんでした。

タイムゾーンを変更する

前述の通り、これはGNU Cライブラリを利用しているコンテナイメージにしか適用されない認識です。

  • docker run -e TZ=Asia/Tokyo
  • docker run -it --rm -v /etc/localtime:/etc/localtime
    • ホストの /etc/localtime をマウントします。これならホストOSとタイムゾーンの同期が取れます

最後に

誤りあればご指摘お願いします!

Dockerfileで引数を元にベースイメージを指定する

目的

  • docker buildの引数でDockerfileのベースイメージ(FROM部分)を指定する
    • ベースイメージが環境毎に違う場合(AWSアカウントが別れてて、ECRが別々とか)、1つのDockerfileで違うリポジトリを指定したいときとかに便利

やりかた

Dockerfile

ARG BASE_IMAGE
FROM ${BASE_IMAGE}:latest
RUN hogehoge

コマンド

docker build --build-arg BASE_IMAGE=XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/ruby .

--build-argを環境変数で指定してあげれば環境差異を吸収できる

備考

  • ARGはFROMの前における唯一の命令
  • FROMの前に宣言したARGはFROMでしか使えない。FROM以降で再度ARGを使いたい場合、もう一度定義してあげる
ARG BASE_IMAGE
FROM ${BASE_IMAGE}:latest
ARG BASE_IMAGE
RUN hogehoge

参考

docs.docker.com

aws-sdk-goでpaginate

なにこれ

  • aws-sdk-goでpaginateするサンプル
    • paginateとは一度のリクエストで全ての結果を出力できない際、NextTokenを引数にしてリクエストすることでページ割して全ての結果を取得することだと思ってる
    • boto3であればPaginatorsというのを使うと上記の処理をうまくやってくれる

aws-sdk-goの場合

  • paginateできるAPIHogehogePagesWithContext みたいな関数が用意されているのでこれを使う

サンプル

今回の場合、 ecs.DescribeContainerInstances の引数として渡すインスタンスのARNリストを ecs.ListContainerInstancesPagesWithContext で取得する。引数で渡すARNの上限となる100ずつでページ分割する。

package main

import (
    "context"
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/ecs"
)

const ClusterName = "your-cluster-name"

func main() {
    var instancesList [][]string

    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))
    svc := ecs.New(sess)
    ctx := context.Background()

    err := svc.ListContainerInstancesPagesWithContext(ctx, &ecs.ListContainerInstancesInput{
        Cluster: aws.String(ClusterName),
        MaxResults: aws.Int64(100), // DescribeContainerInstances limitation
    }, func(p *ecs.ListContainerInstancesOutput, b bool) bool {
        var instances []string
        for _, arn := range p.ContainerInstanceArns {
            instances = append(instances, aws.StringValue(arn))
        }
        instancesList = append(instancesList, instances)
        return true
    })

    if err != nil {
        panic(fmt.Sprintf("failed to list container instances for cluster %s: %v", ClusterName, err))
    }

    fmt.Println("Container instances list: ", instancesList)
}
  • ListContainerInstancesPagesWithContext の引数について
    • ctx
      • groutineのキャンセル、タイムアウトなどで利用。このへんまだわかってない
    • ecs.ListContainerInstancesInput
      • ListCOntainerInstancesのインプット。MaxResultsを100にすることで各ページ100ずつ出力してくれる
    • func
      • 各ページ毎で実行する処理を各。今回は上限100ずつのArnのSliceをappendする

参考

https://github.com/aws/aws-sdk-go#api-request-pagination-and-resource-waiters