HackToTech

Hack To Technology

GC本を読んで学ぶ2

  • 全てのガベージコレクションは以下のいずれかに基づいている(ガベージコレクタ毎にさまざまな方法で領域ごとにこれらのアプローチを組み合わせているらしい)
    • マークスイープGC
    • コピーGC
    • マークコンパクトGC
    • 参照カウントGC

大体wikiに載ってる

  • ストップザ・ワールド的なアプローチ例
    • コレクタスレッド1
      • ガベージコレクションを実行するスレッド
    • ミューテータスレッドN
      • アプリケーションコードを実行するスレッド(コレクタスレッド実行時は停止)

これの利点はヒープのスナップショットがとれる + ヒープ中のオブジェクトのトポロジが変化しないこと

  • 自動メモリ管理を行うのに必要な機能
    • 新しいオブジェクトの領域の割当
    • 生きているオブジェクトの特定
    • 死んだオブジェクトの使用している領域の回収

完全に判別するのは厳しいので参照を追って参照が存在しないオブジェクトは死んでいる判定にすることによって安全に回収する

  • マークスイープGC
    • まずルート集合(レジスタ、スレッドのスタック、グローバル変数)からオブジェクトのグラフを走査して到達可能なオブジェクトにマークをつける(所謂tracing)
    • スイープフェーズでヒープ中の全てのオブジェクトにマークがついているか調べてなければゴミとして回収する(わかりやすい)
    • 間接的GCアルゴリズム
    • ゴミを探すというよりは生きているものを探して他は全てゴミとして扱う

Mark-and-Sweep: Garbage Collection Algorithm)

わかりやすい
コレクタがシングルスレッドなら探索リストをDFSで実装できる(これによってハードウェアキャッシュがあたることを期待できる)

三色抽象化を利用してオブジェクトの状態を表現できるらしい
黒(処理済み)、白(未処理(永遠に未処理の可能性もある))、灰(処理途中もしくは再処理が必要)
並行GCのアルゴリズムで特に有用らしい

  • 時間的局所性

    python total = 0 for i in range(1, 11): total += i

    とした場合にtotalに対して複数回のアクセスが発生するので、その値をキャッシュするのが有効(アドレスをイメージしたほうがわかりやすそう)

  • 空間的局所性

    python l = [1,2,3,4,5] for i in l: print(i)

    この場合lに対して複数回のアクセスが発生するので、その値付近をキャッシュするのが有効(これもメモリが連続して確保されている際にアクセスすることを考えたほうがわかりやすいか、CPUのプリフェッチとかもこれ系)

マークビットの領域は通常オブジェクトのヘッダワードの中に用意する
いまいちオブジェクトのヘッダワードのイメージがよくわからんとなったので適当に調べた感じ
Javaだとこんな感じらしい

セットアソシアティブ方式とダイレクトマッピング方式はこれがめちゃくちゃわかりやすかった
(アドレス空間/キャッシュ容量/ブロックサイズ/タグ/オフセット/インデックスとか用語から説明されてて助かる(基本と応用とったくせに完全に記憶から消失した))

オブジェクトヘッダにマークビットを用意することによってrace conditionを解消できる(外部に持つと更新状態が競合する可能性が存在する) ビットマップではなくバイトマップを使うとマーク処理のrace conditionが解消できるらしい(よくわかってない)

  • 安全性に関しての結論
    • コレクタはどこに格納された値であってもミューテータが保持する値を書き換えてはいけない(オブジェクトを移動させるどんなアルゴリズムも使えない, 移動するとポインタの修正が発生する)
      • オブジェクトのようなもの(のようなものって具体的になんなんだろう)のマークビットを変更することによってユーザのデータを破壊する恐れがある
    • ミューテータがコレクタのデータと干渉する機会を最小限にすることが有益
      • ヘッダワードに持つよりはオブジェクトと分離したデータ構造(これがバイトマップかな)に保存したほうがリスクが小さい
      • コレクタによるページング回数を減らすのも有益

マークスイープGCのメモリアロケータはフラグメンテーションを避ける為に一定サイズのブロックを連続して配置するらしい
(メモリアクセスのパターンが均一になってハードウェアのプリフェッチの機構と相性が良い)
マークフェーズのオブジェクトヘッダの読み取りによって発生するキャッシュミスの回数を減らす方法の一つにBiBoPというものがあるらしい
(同じページには単一の型のみを割り当てることによってオブジェクト毎の型情報をページ単位に圧縮が出来る)

GC本を読んで学ぶ1

ガベージコレクション: 自動的メモリ管理を構成する理論と実装を積ん読していたので消化するために前に読んでたメモをとりあえず残す

第一章

  • 動的なメモリ割り当てを利用することによって、コンパイル時に総サイズがわからないオブジェクトでも割り付けたり、開放したりすることができる
  • 動的に割り付けたオブジェクトはヒープに保存される
  • アクティベーションレコードとはなんぞ?(スタック内の割付らしい)
  • スタックフレームという領域があるらしい(よくわからん)
  • 静的割付(オブジェクトの名前をコンパイル時あるいはリンク時に決定するメモリ上の場所に束縛する方法)
  • なぜヒープへの割付が重要か?
    • オブジェクトのサイズを動的に決められる(配列の上限値をハードコードするとプログラムが壊れる恐れがある)
    • 再帰的なデータ構造を定義できる(リスト、ツリー、マップなど)
    • 新しく生成したオブジェクトを親の手続きに返せる(これによってファクトリーメソッドなどの技法を使える)
    • 関数の値として別の関数を返せる(所謂クロージャやサスペンション←初めて聞いた)
  • ヒープに割り付けたオブジェクトには参照を用いてアクセスする(ポインタ、ハンドル)
  • ハンドルを利用することで、オブジェクトの再配置が容易になる
  • 明示的解放(CでいうfreeやC++のdeleteなど)
  • 手動で解放する場合はプログラミングエラーを犯すリスクが有る(エラーは2種類)
    • 一つはメモリを解放するのが早すぎるエラーで、メモリへの参照が残っているのに誤って解放してしまうケース(このような参照を宙ぶらりんポインタと呼ぶ)
      • 例: A ref B ref C
      • Bを手動解放した場合にAはBへの参照が残り、Cは到達不能となり解放することもできなくなる
      • 宙ぶらりんのポインタを検出する方法の一つにファットポインタがある
        • ファットポインタには、ポインタが指しているオブジェクトとポインタ自身のバージョン番号を格納しておく
      • 参照をたどるような操作の際には、必ずポインタに格納してあるバージョン番号が、オブジェクトのバージョンと一致していることをチェックする
        • (この手法は、オーバーヘッドのため、デバッキングツールでしか使えないことが多い, また完全に信頼できる方法でもない)
    • 二つめはメモリリーク
      • 先程の例のように一箇所のメモリ解放の誤りが宙ぶらりんポインタ(A)とメモリリーク(C)の両方を引き起こしてしまうこともありうる
  • スレッドセーフなデータ構造にアクセスするアルゴリズムでは、さまざまな問題に対処する必要がある(デッドロック、ライブロック、ABAエラー)
  • ガベージコレクションは宙ぶらりんポインタが作られるのを防止する(オブジェクトが回収されるのは、到達可能なオブジェクトからのポインタが1つもなくなった時だけ)
  • GCは原則的にすべてのごみを解放することを保証する(すなわち到達不能なオブジェクトは全て最終的にはGCによって回収される)
  • 注意点は以下
    • 追跡型GC(Tracing collection)が用いる「ごみ」の定義は、決定可能なものであり、二度とアクセスされないオブジェクトすべてがごみとみなされるわけではない
    • GCの実装によっては、効率上の理由からオブジェクトの一部を回収しないことがあるということ
  • ガベージコレクタのみがオブジェクトを解放するので、二重解放問題は生じない(解放に関するすべての決定はコレクタが行う)
  • コレクタはヒープ内のオブジェクトの構造やオブジェクトにアクセスする可能性のあるスレッドに関するグローバルな情報を持っている
  • ガベージコレクションが望ましい理由は、コードを書くのが簡単になるということではない

    • 簡単にはなるが、それよりも重要なのはガベージコレクションによってメモリ管理の問題がインターフェイスから切り離せるということ
    • ガベージコレクションなしではメモリ管理の問題がプログラム中に散乱することになる
    • ガベージコレクションは再利用性を改善する、これが様々な分野でほぼすべての近代的な言語で必須とされている理由
    • ガベージコレクションはスペースリークが絶対にないという保証はできない
    • なぜなら、データ構造が到達可能なまま際限なく大きくなる場合(キャッシュ等)ガベージコレクションでは解決できない
    • さらに到達可能だが二度とアクセスされないような場合についても同様
  • GCに求められる要素

    • 安全性
      • 生きているオブジェクトの回収などを行わないこと
      • スループット
      • mark/cons比
    • 完全性
      • ヒープ中のごみがすべて回収されること, かなり難しい(常にそれが最適とは限らない)
      • 並行GC中に新たにでたごみを浮遊ごみと呼ぶ(そのため、並行GCの文脈では完全性をすべてのごみがいずれは回収されることとしたほうが良いことになる)
    • 即時性
      • GCアルゴリズムによって異なる、ここでもまた時間と空間のトレードオフが導かれる
    • 停止時間

AWS Batchのexporterを書いてみた

久しぶりのブログ更新
ここ最近読書によるインプット過多で全然アウトプットしていないので久しぶりにまともなものを書く

golangの勉強がてら書いてみたので見る人が見たら酷いかもしれない
シンプルにそれぞれのStateのJobをメトリクスとして取得してみた
Queue毎にカウントしただけのメトリクスを吐き出すことも考えたけど、PromQLで計算すれば出来るから一旦はこのままにしてみた
github.com

コード書いたついでに久しぶりにGrafanaも周りも触ったらかなり改善されていい感じだったので、仕事でもGrafana使いたい欲がすごい(今はDatadogを主に使ってる)

特定のIAMロールで実行できるSystemsManagerのドキュメントを制限する方法

特定のIAMロールで実行できるSystemsManagerのドキュメントを制限する方法

今後もまた実装したくなることがありそうなので、残しとく
結論から言うと大体下のようなIAM Policyで制限をかけることが可能

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ec2:*:*:instance/*"
            ],
            "Condition": {
                "BoolIfExists": {
                    "ssm:SessionDocumentAccessCheck": "true"
                },
                "StringLike": {
                    "ssm:resourceTag/Name": "FugaFuga"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": "arn:aws:ssm:*:*:document/HogeHogeDocument"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
        }
    ]
}

やっている内容について書いていく

まずはこのstatement

{
    "Effect": "Allow",
    "Action": "ssm:StartSession",
    "Resource": [
        "arn:aws:ec2:*:*:instance/*"
    ],
    "Condition": {
        "BoolIfExists": {
            "ssm:SessionDocumentAccessCheck": "true"
        },
        "StringLike": {
            "ssm:resourceTag/Name": "FugaFuga"
        }
    }
}

単純にActionとResourceはSSMのStartSessionをどのEC2インスタンスに対してでも実行できるようにしている
その上でConditionを使用して対象の絞り込みをかける
BoolIfExistsのssm:SessionDocumentAccessCheckは下の公式を読むとよくわかるが、
AWS CLI でセッションドキュメントのアクセス許可チェックを適用する
通常ssm:StartSessionを許可した場合にSSM-SessionManagerRunShellがデフォルトで許可される
これが許可されてしまうとインスタンスに接続してごにょごにょ何でも出来てしまうのでまず明示的に許可していないドキュメントへのアクセスを遮断する為に必要
次にStringLikeで ssm:resourceTag/Name を絞っているが、これは特定の名前のインスタンスにのみ実行を許可するために使っている(今回であればNameがFugaFugaのインスタンスだけとなる)
これもよくある、この種類のインスタンスに対しては良いけど他へは実行できて欲しくないといったときに重宝する

次にこのstatement

{
    "Effect": "Allow",
    "Action": "ssm:StartSession",
    "Resource": "arn:aws:ssm:*:*:document/HogeHogeDocument"
}

前のstatementで明示的に許可をしていないドキュメントについてはアクセス出来なくさせたので、
ドキュメントを指定することによって実行を許可している
なんで前のstatementと分けて書いているかというと、
前のstatementはConditionのStringLikeでTagの一致を条件として使用しているので、ドキュメントにも同様のTagがないと弾かれてしまうようになる(ドキュメントのName tagにFugaFugaを設定するつもりはないので、今回は分けている)

最後にこのstatement

{
    "Effect": "Allow",
    "Action": [
        "ssm:TerminateSession",
        "ssm:ResumeSession"
    ],
    "Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
}

自分自身がStartSessionで作成したsessionに対してResumeとTerminateを出来るように許可している

書いてみると意外と単純だが、Conditionを扱うのが久しぶりだと若干頭を悩ませる

IAMで特定のロールからのみAsuume Roleをさせる方法

IAMで特定のロールからのみAsuume Roleをさせる方法

Assume元を再作成した場合にも出来るようにしたくて試行錯誤したついでにメモを残す

Principalを使用する方法

公式にも書いてある方法
基本的に一番よく使うのはこれだと思う

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    // 対象のアカウント及びロール
    "Principal": { "AWS": "arn:aws:iam::123456789012:root" },
    "Action": "sts:AssumeRole",
    "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }
  }
}

ただこれを使った場合

"Principal": { "AWS": "arn:aws:iam::123456789012:root" },

ここでUserやRoleを指定することになるが、これは保存時に自動的にPrincipalIDに変更される(ドキュメントにも書いてあるが)

Principal 要素に特定の IAM ロールまたはユーザーの ARN が含まれている場合、この ARN はポリシーの保存時に一意のプリンシパル ID に変換されます。 これにより、第三者がロールやユーザーを削除して再作成することで、そのユーザーのアクセス許可をエスカレートするリスクを緩和できます。 したがって、信頼ポリシーの Principal 要素で指し示しているユーザーまたはロールを削除し、再作成する場合、ロールを編集して ARN を置き換える必要があります。

ので、再作成したりしない or 再作成してもPrincipalを更新するので問題がない場合はこれでOK
ただ再作成してもAssumeできるようにしたいなら次のように実装しないとできなかった

Principal + Conditionを使用する方法

さっきと同様にPrincipalでまずアカウントを絞り込む
その上で、元となるロールをConditionのArnEqualsで更に絞り込む
こうすることによってそのアカウントのロールが再作成されてもAssumeできる

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        // 対象のアカウント
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "ArnEquals": {
          // 対象のアカウントのロール
          "aws:PrincipalArn": "arn:aws:iam::123456789012:role/assume-from"
        }
      }
    }
  ]
}

BPF本を読みながら学ぶ10

BPF本を読みながら学ぶ10

10章、ネットワークについて
割と知らない単語があって調べるのに苦労した
後半はbpftraceを用いたツールの説明が多い

目次

用語

  • Queueing Discipline(キューイングの規則)
    • 入力側 (ingress: 入口)、出力側 (egress: 出口) いずれかの デバイスのキューを管理するアルゴリズムのこと。

  • AQM(Active Queue Management)
    • CoDel(Controlled Delay)
    • RED(Random Early Detection)
  • XDP(eXpress Data Path)
    • NICドライバー内でのeBPFによるフックを介したパケット処理
    • 柔軟で処理速度が優秀(ACMの動画を見るにDDoS攻撃とかに対しても有用っぽい(BPF本側でも言及されてた))
    • 無駄なコンテキストスイッチが走らない(カーネル空間側で処理する)
  • TCP輻輳制御
    • Cubic(default)
    • Reno
    • Tahoe
    • DCTCP
    • BBR

参考

ネットワークのパフォーマンス分析

  1. ネットワークの統計情報を確認する
    パケットレート/スループット、およびTCP接続がある場合はTCPの接続/再送信のレート
    ss / nstat / netstat / sar
  2. TCP接続の特徴を観測し、非効率部分を探す
    tcplife
  3. ネットワークインタフェースのスループット制限に達しているかを確認する
    sar / nicstat
  4. TCPの再送信やドロップなどの異常なTCPイベントをトレースする
    tcpretrans / tcpdrop / skb:kfree_skb tracepoint
  5. DNSの名前解決の遅延を測定する
    gethostlatency
  6. コネクションの遅延、TTFBの遅延、スタックの遅延など様々な箇所からネットワークの遅延状況を測定する
  7. bufferbloatなど負荷によって遅延時間を大きく変化するので注意すること
  8. 負荷生成ツールでホスト間のネットワークスループットを調べて既知のワークロードに対するネットワークイベントを調べる
    iperf / netperf
  9. bpftoolを使用する
  10. カーネルのスタックトレースのCPUプロファイリングを使用して、プロトコルおよびドライバーの処理に費やされるCPU時間を定量化する
  11. トレースポイントとkprobes

ss

ソケットの統計をダンプする
netstatと同様の情報を表示できる

フィールド1

Netid 接続の種類
u_str: TCP
u_dgr: UDP
v_str: vSocket
State ESTAB: Established
UNCONN: Close
Recv-Q
Send-Q
Local Address:Port ローカルのホスト or IPアドレス + Port
Peer Address:Port リモートのホスト or IPアドレス + Port
❯ ss | head -n10
Netid   State   Recv-Q   Send-Q                 Local Address:Port   Peer Address:Port
u_str   ESTAB   0         0                                 * 47233              * 47234
u_str   ESTAB   0         0                                 * 45092              * 44080
u_str   ESTAB   0         0                                 * 44587              * 44588
u_str   ESTAB   0         0       /run/systemd/journal/stdout 43268              * 42713
u_str   ESTAB   0         0                                 * 43077              * 44112
u_str   ESTAB   0         0   /var/run/dbus/system_bus_socket 42983              * 44430
u_str   ESTAB   0         0                                 * 53695              * 48964
u_str   ESTAB   0         0                /run/user/1000/bus 45967              * 46626
u_str   ESTAB   0         0                                 * 44351              * 44352
u_str   ESTAB   0         0                                 * 230142             * 230141
u_str   ESTAB   0         0                                 * 44138              * 44139
u_str   ESTAB   0         0                                 * 42390              * 45199
u_str   ESTAB   0         0   /run/containerd/containerd.sock 33779              * 34376
u_str   ESTAB   0         0                                 * 30251              * 30252
icmp6   UNCONN  0         0                                 *:ipv6-icmp          *:*
tcp     ESTAB   0         0                                 192.168.221.129:49716 192.30.253.124:https
tcp     ESTAB   0         0                                 192.168.221.129:40098 35.161.236.240:https
v_str   ESTAB   0         0                                 1081845576:1023 0:976
v_str   ESTAB   0         0                                 1081845576:3128913646 0:976

フィールド2

users プロセス名
プロセスID
ファイルディスクリプタ
timer タイマー名
タイマーの有効期限
再送の発生回数
uid ソケットの属するユーザーID
ino VFSのソケットのinodeの番号
sk ソケットのUUID
skmem r?: パケットを受信するために割り当てられたメモリ
rb?: パケットの受信に割り当てることができる合計のメモリ
t?: パケットを送信するのに使用しているメモリ(レイヤー3へ送信済み)
tb?: パケットの送信に割り当てることができる合計のメモリ
f?: ソケットによってキャッシュとして割り当てられたが、パケットの送受信にはまだ使われていないメモリ
(パケットの送受信にメモリが必要な場合、追加のメモリを割り当てる前にこのキャッシュ内のメモリが使用される)
w?: パケットの送信に割り当てられたメモリ(レイヤー3へ未送信)
o?: ソケットオプションの保存に使用されるメモリ(例えばTCP MD5署名のkey)
bl?: skバックログキューに使用されるメモリ
(プロセスのコンテキストでは、プロセスがパケットを受信しているところで、新たにパケットを受信した際にskバックログキューに入れられ、プロセスは即座に受信することができる)
d?: 生のUDPのドロップカウンターっぽい?
おそらく、この辺この辺
cubic 輻輳のアルゴリズム名が表示される(デフォルトなのでcubic)
rto TCPの再送信タイムアウト値(ミリ秒)
rtt 平均ラウンドトリップタイム(ミリ秒)/ラウンドトリップタイムの平均偏差(ミリ秒)
ato ACKのタイムアウト(ミリ秒)、遅延ACKモードで使用する
mss 最大セグメントサイズ
pmtu パスのMTU値
rcvmss ピアに知らせる最大セグメントサイズ(MTU - 40(TCPヘッダとIPヘッダ分) bytes)
advmss Advertised MSS(適切な和訳がわからなかった)
cwnd 輻輳ウィンドウサイズ
bytes_acked 正常に送信されたバイト数
bytes_received 受信したバイト数
segs_out 送信されたセグメント数
segs_in 受信したセグメント数
data_segs_out RFC4898 tcpEStatsDataSegsOut(tcpi_data_segs_out)
send egressのbps
lastsnd 最後のパケットが送信されてからの時間(ミリ秒)
lastrcv 最後にパケットを受信してからの時間(ミリ秒)
lastack 最後にACKを受信してからの時間(ミリ秒)
pacing_rate ペーシングのレート(bps)/最大ペーシングのレート(bps)
delivery_rate tcp_rate_gen()で測定された最新のgoodput(詳細)値
app_limited ソケットのスループットが送信アプリケーションによって制限されたときにgoodputが測定されたかどうかを示すbool値
busy データを送信中の時間(ミリ秒)
rcv_space 受信バッファのソケットオートチューニングをするヘルパー変数
rcv_ssthresh 受信のスロースタートサイズの閾値(現在のウィンドウクランプ)
minrtt 最小ラウンドトリップタイム
❯ ss -tiepm
State Recv-Q   Send-Q   Local Address:Port    Peer Address:Port
ESTAB 0        0      192.168.221.129:49716 192.30.253.124:https
users:(("firefox",pid=4300,fd=44)) timer:(keepalive,71582min,0) uid:1000 ino:233806 sk:2d4 <->
     skmem:(r0,rb131072,t0,tb87040,f0,w0,o0,bl0,d0) cubic rto:300 rtt:28.715/50.18 ato:40 mss:1460 pmtu:1500 rcvmss:1460 advmss:1460 cwnd:10 bytes_acked:1631 bytes_received:4091 segs_out:20 segs_in:31 data_segs_out:14 data_segs_in:16 send 4.1Mbps lastsnd:17864 lastrcv:17864 lastack:17864 pacing_rate 8.1Mbps delivery_rate 132.7Mbps app_limited rcv_space:14600 rcv_ssthresh:64076 minrtt:0.235
ESTAB  0        0     192.168.221.129:40098 35.161.236.240:https
users:(("firefox",pid=4300,fd=70)) timer:(keepalive,5min42sec,0) uid:1000 ino:88797 sk:2d1 <->
     skmem:(r0,rb131072,t0,tb87040,f0,w0,o0,bl0,d0) cubic rto:204 rtt:0.227/0.044 ato:40 mss:1460 pmtu:1500 rcvmss:1460 advmss:1460 cwnd:10 bytes_acked:10911 bytes_received:12344 segs_out:359 segs_in:552 data_segs_out:274 data_segs_in:277 send 514.5Mbps lastsnd:257984 lastrcv:257984 lastack:257984 pacing_rate 1027.4Mbps delivery_rate 108.1Mbps app_limited busy:80ms rcv_space:14600 rcv_ssthresh:64076 minrtt:0.223

参考

ip

ルーティング、ネットワークデバイス、インターフェイス、トンネルの表示/操作

フィールド

interface name インターフェイス名(lo, ens33)
interface flag UP: インターフェイスが有効である状態
LOWER_UP: イーサネットケーブルが差し込まれ、デバイスがネットワークに接続されている
LOOPBACK: インターフェイスは他のホストと通信を行わない(バウンスされたパケットのみを受信可能)
BROADCAST: 同じリンクを共有するすべてのホストにパケットを送信する機能がある(例: イーサネットリンク)
MULTICAST: マルチキャスト機能が有効
mtu Maximum Transmission Unit
qdisc インターフェイスキューイングアルゴリズム
noqueue: キューに入れない
fq_codel: 詳細
state 状態
mode おそらくLINKモード
DEFAULT or DORMANT
group デバイスグループ(/etc/iproute2/groupに載っているものが指定できる)
qlen デバイスのデフォルトの送信キューの長さ
link リンク(loopback, ethernet)
brd ブロードキャストアドレス
RX bytes: 受信したバイト数の合計
packets: 受信したパケット数の合計
errors: 受信したエラーパケット数の合計
dropped: 受信した際にバッファに空きがなかったりしてドロップしたパケット数の合計
overrun: 受信リングバッファのオーバーフローの合計
mcast: 受信したマルチキャストの合計
TX bytes: 送信したバイト数の合計
packets: 送信したパケット数の合計
errors: 送信した問題のあるパケット数の合計
dropped: 送信したパケットのドロップした回数(レアらしい)
carrier: シグナルの変調に問題があった数の合計(デュプレックスミスマッチとか物理ケーブル/コネクタの問題とか)
collsns: 衝突(コリジョン)した回数
❯ ip -s link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    RX: bytes  packets  errors  dropped overrun mcast   
    696562     8158     0       0       0       0       
    TX: bytes  packets  errors  dropped carrier collsns 
    696562     8158     0       0       0       0       
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:7b:a7:48 brd ff:ff:ff:ff:ff:ff
    RX: bytes  packets  errors  dropped overrun mcast   
    65473200   82427    0       0       0       0       
    TX: bytes  packets  errors  dropped carrier collsns 
    7752597    46923    0       0       0       0       
❯ ip route
default via 192.168.221.2 dev ens33 proto dhcp metric 100 
169.254.0.0/16 dev ens33 scope link metric 1000 
192.168.221.0/24 dev ens33 proto kernel scope link src 192.168.221.129 metric 100 

参考

nstat

カーネルのSNMPカウンターとネットワークインターフェースの統計を確認するシンプルなツール

統計情報の確認

❯ nstat -s
#kernel
IpInReceives                    37402              0.0
IpInAddrErrors                  3                  0.0
# 省略

参考

netstat

ネットワーク接続、経路テーブル、インターフェースの状態、マスカレード接続、 netlink メッセージ、マルチキャストのメンバーシップを表示する

フィールド

Proto ソケットのプロトコル
RefCnt 参照カウント(このソケットに接続されているプロセスの数)
Flags ACC: SO_ACCEPTON(ソケットが接続されておらず、かつそれを用いるプログラムが接続要求を待っている場合に用いられる)
W: SO_WAITDATA
N: SO_NOSPACE
Type ソケットのアクセスタイプ
SOCK_DGRAM: データグラム(connectionsless)モードのソケット
SOCK_STREAM: ストリーム(connection)ソケット
SOCK_RAW: RAWソケット
SOCK_RDM: 信頼性の高いメッセージを届けるソケット
SOCK_SEQPACKET: シーケンシャルパケットのソケット
SOCK_PACKET: RAWインターフェースアクセスのソケット
UNKNOWN: 不明
State FREE: ソケットは割り当てられていない
LISTENING: ソケットは接続要求の待ち状態
CONNECTING: ソケットは接続中の状態
CONNECTED: ソケットは接続状態
DISCONNECTING: ソケットは切断中の状態
空欄: ソケットは他のソケットに接続されていない
UNKNOWN: 基本的に現れない
I-Node inodeの番号
PID/Program name このソケットをオープンしているプロセスID/プロセス名
Path ソケットに結び付けられているプロセスのパス
❯ netstat -anp
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス            外部アドレス            状態       PID/Program name    
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp        0      0 192.168.221.129:42142   13.35.67.43:443         ESTABLISHED 8601/firefox        
tcp        0      0 192.168.221.129:42340   99.84.55.56:443         TIME_WAIT   -                   
tcp        0      0 192.168.221.129:53566   172.217.25.206:443      ESTABLISHED 8601/firefox        
tcp        0      0 192.168.221.129:45744   107.178.248.96:443      ESTABLISHED 8601/firefox        
tcp        0      0 192.168.221.129:47086   104.107.111.107:443     ESTABLISHED 8601/firefox
# 省略
稼働中のUNIXドメインソケット (サーバと確立)
Proto RefCnt Flags       Type       State         I-Node   PID/Program name     パス
unix  2      [ ]         DGRAM                    31499    -                    /var/spool/postfix/dev/log
unix  2      [ ACC ]     STREAM     LISTENING     41813    2880/gnome-session-  @/tmp/.ICE-unix/2880
unix  2      [ ]         DGRAM                    43395    2859/systemd         /run/user/1000/systemd/notify
unix  2      [ ACC ]     SEQPACKET  LISTENING     25863    -                    /run/udev/control
unix  2      [ ACC ]     STREAM     LISTENING     43398    2859/systemd         /run/user/1000/systemd/private
unix  2      [ ACC ]     STREAM     LISTENING     43402    2859/systemd         /run/user/1000/gnupg/S.gpg-agent.ssh
unix  2      [ ACC ]     STREAM     LISTENING     43403    2859/systemd         /run/user/1000/gnupg/S.dirmngr
unix  2      [ ACC ]     STREAM     LISTENING     43404    2859/systemd         /run/user/1000/gnupg/S.gpg-agent.browser
unix  2      [ ACC ]     STREAM     LISTENING     43405    2859/systemd         /run/user/1000/snapd-session-agent.socket
❯ netstat -s
Ip:
    Forwarding: 1
    15127 total packets received
    3 with invalid addresses
    0 forwarded
    0 incoming packets discarded
    15122 incoming packets delivered
    14728 requests sent out
Icmp:
    1 ICMP messages received
    0 input ICMP message failed
    ICMP入力ヒストグラム:
        echo requests: 1
    4 ICMP messages sent
    0 ICMP messages failed
    ICMP出力ヒストグラム:
        destination unreachable: 3
        echo replies: 1
IcmpMsg:
        InType8: 1
        OutType0: 1
        OutType3: 3
Tcp:
    53 active connection openings
    0 passive connection openings
    2 failed connection attempts
    3 connection resets received
    0 connections established
    14109 segments received
    13845 segments sent out
    3 segments retransmitted
    0 bad segments received
    15 resets sent
Udp:
    954 packets received
    3 packets to unknown port received
    0 packet receive errors
    872 packets sent
    0 receive buffer errors
    0 send buffer errors
    IgnoredMulti: 57

参考

nicstat

パケット、キロバイト/秒、平均パケットサイズなど、すべてのNICのネットワーク統計を出力する

フィールド

Time 時間(HH:MM:SS)
Int インターフェイス名
rKB/s キロバイト/秒の読み取り(受信)
wKB/s キロバイト/秒の書き込み(送信)
rPk/s パケット(TCP Segments, UDP Datagrams)/秒の読み取り(受信)
wPk/s パケット(TCP Segments, UDP Datagrams)/秒の書き込み(送信)
rAvs 読み取りのパケットの平均サイズ(受信)
wAvs 書き込みのパケットの平均サイズ(送信)
%Util インターフェイスの使用率
Sat 飽和(Saturation)/インターフェイスのエラー/秒の数
❯ nicstat
    Time      Int   rKB/s   wKB/s   rPk/s   wPk/s    rAvs    wAvs %Util    Sat
01:02:50    ens33   27.27    0.31   19.31    4.04  1446.1   78.08  0.02   0.00
01:02:50       lo    0.02    0.02    0.19    0.19   87.71   87.71  0.00   0.00

ethtool

ネットワークドライバーとハードウェア設定のクエリもしくはコントロール

-Sでデバイスの統計情報の確認

❯ ethtool -S ens33
NIC statistics:
     rx_packets: 82901
     tx_packets: 17923
     rx_bytes: 119175472
     tx_bytes: 1435399
     rx_broadcast: 0
     tx_broadcast: 0
     rx_multicast: 0
     tx_multicast: 0
     rx_errors: 0
     tx_errors: 0
     tx_dropped: 0
     multicast: 0
     collisions: 0
     rx_length_errors: 0
     rx_over_errors: 0
     rx_crc_errors: 0
     rx_frame_errors: 0
     rx_no_buffer_count: 0
     rx_missed_errors: 0
     tx_aborted_errors: 0
     tx_carrier_errors: 0
     tx_fifo_errors: 0
     tx_heartbeat_errors: 0
     tx_window_errors: 0
     tx_abort_late_coll: 0
     tx_deferred_ok: 0
     tx_single_coll_ok: 0
     tx_multi_coll_ok: 0
     tx_timeout_count: 0
     tx_restart_queue: 0
     rx_long_length_errors: 0
     rx_short_length_errors: 0
     rx_align_errors: 0
     tx_tcp_seg_good: 15
     tx_tcp_seg_failed: 0
     rx_flow_control_xon: 0
     rx_flow_control_xoff: 0
     tx_flow_control_xon: 0
     tx_flow_control_xoff: 0
     rx_long_byte_count: 119175472
     rx_csum_offload_good: 82844
     rx_csum_offload_errors: 0
     alloc_rx_buff_failed: 0
     tx_smbus: 0
     rx_smbus: 0
     dropped_smbus: 0

-iでドライバーの詳細の表示

❯ ethtool -i ens33
driver: e1000
version: 7.3.21-k8-NAPI
firmware-version: 
expansion-rom-version: 
bus-info: 0000:02:01.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: no

BPF本を読みながら学ぶ9

9章、 ディスクI/Oについて

目次

blkparse

Trace Actions 説明
C Complete 以前に発行したリクエストが完了した
D Issued ブロックレイヤーのキューまたはI/Oスケジューラに存在していたリクエストがドライバへ発行された
I Inserted 内部キューへの追加とドライバによる後のサービスのためにI/Oスケジューラにリクエストを送信した
Q Queued I/Oをキューに入れようとしている
実際のリクエストはまだ存在しない
B Bounced データページがハードウェアから到達できないため、より低いメモリロケーションへバウンスする必要がある
これにより、データをカーネルバッファとの間でコピーする必要がある為、I/Oパフォーマンスが大幅に低下する
M Back merge I/Oの開始から終了までの間に以前にインサートされたリクエストが存在する為、I/Oスケジューラはそれらをマージできる
要するに順番通りにくっつけて処理する感じ
F Front merge バックマージと同じ
以前にインサートされたリクエストが開始されるところで、このI/Oが終了する点を除く
要するに順番を入れ替えてくっつけて処理する感じ
M Front or Back merge そのまま
G Get request 任意のタイプのリクエストをブロックデバイスに送信するには、初めに構造体のリクエストコンテナを割り当てる必要がある
S Sleep 利用可能なリクエストの構造体がなかった為、発行者はリクエストの構造体が解放されるのを待つ必要がある
P Plug 空のブロックデバイスキューにI/Oがキューイングされる前に、Linuxはデータが必要になって将来的にキューにI/Oが追加されることを予想してキューにプラグを差し込む
U Unplug デバイスにいくつかのリクエストデータがキューイングされていて、ドライバへリクエストの送信を開始する
タイムアウトの期間を経過した場合、もしくは多くのリクエストがキューに追加された際に自動的に発生する場合がある
T Unplug due to timer キューにプラグを差し込んだあとに、I/Oを要求するリクエストがない場合、Linuxは定義された期間が経過した後、自動的にプラグを取り外す
X Split RAIDもしくはデバイスマッパーの仕組みでは、インカミングI/Oがデバイスもしくは内部ゾーンにまたがるケースがあり、サービスのために細かく分割するひつようがある
RAID/デバイスマッパーの仕組みが不適切であるために、パフォーマンスの問題が発生していることを示す場合もあるが、通常の境界条件の一部にすぎない場合もある
A Remap スタックされたデバイスの場合、インカミングI/Oは、I/Oスタックでリマップされる
リマップアクションは、何が何にリマップされているかを詳細に示す

参考

rwbs

R Read
W Write
M Meta-data
S Synchronous
A Read-Ahead
F Flush or Force Unit Access
D Discard
E Erase
N No data

参考

I/Oスケジューラ

  • クラシックスケジューラ
    • Noop
      • スケジューリングを行わない(要するにリクエスト順に処理する)
    • Deadline
      • レイテンシのデッドライン(ミリ秒単位で指定)を強制しようとする(リアルタイムシステムで望ましい)
      • レイテンシの外れ値が防げる(スタベーション)
    • CFQ
      • CPUのスケジューリングと同様にI/Oタイムスライスを割り当てる
  • マルチキュースケジューラ
    • BFQ
      • 遅いI/Oデバイスでも良好なレスポンスを返せる
      • 遅いCPUもしくは、高スループットのデバイスには不向き
      • でかいアプリケーションをロードするときに有用
    • kyber
      • 高速なマルチキューデバイスように設計されている
      • 2つのリクエストキューが存在する
        • 同期リクエストキュー
        • 非同期リクエストキュー
    • none
      • スケジューリングを行わない
      • NVMEとかの高速ランダムI/Oデバイスに向いている
    • mq-deadline
      • マルチキューデバイス向けのデッドラインI/Oスケジューラ
      • CPUオーバーヘッドがかなり低いため、オールラウンダー

ディスクI/Oのパフォーマンス分析

  1. ディスクのパフォーマンス分析から始める
  2. 基本的なディスクメトリクスを確認する(リクエストタイム, IOS, Utilization)
  3. 正常な状態がわからない場合はfioとかで計測する
  4. I/Oのレイテンシの分布を確認し、マルチモーダル分析と外れ値を確認する
    biolatency
  5. 個々のI/Oをトレースし、パターンを探る
    biosnoop
  6. bpftraceを使用する

blktrace

ブロックデバイスのライブトレースを実行する

フィールド

デバイスのメジャー,マイナーバージョン
CPU ID
シーケンス番号
タイムスタンプ(秒)
プロセスID
トレースアクション(ID) blkparse
RWBS rwbs
アドレス/サイズ[デバイス]
❯ sudo btrace /dev/sda1
  8,0    1        1     0.000000000 14725  A  WS 15475728 + 8 <- (8,1) 15473680
  8,1    1        2     0.000000479 14725  Q  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    1        3     0.000006639 14725  G  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    1        4     0.000007478 14725  P   N [ThreadPoolForeg]
  8,1    1        5     0.000008846 14725  U   N [ThreadPoolForeg] 1
  8,1    1        6     0.000009364 14725  I  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    1        7     0.000026680 14725  D  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    1        8     0.000198604  4914  C  WS 15475728 + 8 [0]
  8,0    1        9     0.000237768   422  A  WS 17113736 + 8 <- (8,1) 17111688
  8,1    1       10     0.000238263   422  Q  WS 17113736 + 8 [jbd2/sda1-8]
  8,1    1       11     0.000240262   422  G  WS 17113736 + 8 [jbd2/sda1-8]
  8,1    1       12     0.000240623   422  P   N [jbd2/sda1-8]
  8,0    1       13     0.000240940   422  A  WS 17113744 + 8 <- (8,1) 17111696
  8,1    1       14     0.000241052   422  Q  WS 17113744 + 8 [jbd2/sda1-8]
  # 省略
  8,1    1       79     0.000250469   422  U   N [jbd2/sda1-8] 1
  8,1    1       80     0.000250716   422  I  WS 17113736 + 184 [jbd2/sda1-8]
  8,1    1       81     0.000252183   422  D  WS 17113736 + 184 [jbd2/sda1-8]
  8,1    1       82     0.000475940  4914  C  WS 17113736 + 184 [0]
  8,0    1       83     0.000501828   422  A FWFS 17113920 + 8 <- (8,1) 17111872
  8,1    1       84     0.000502301   422  Q  WS 17113920 + 8 [jbd2/sda1-8]
  8,1    1       85     0.000503486   422  G  WS 17113920 + 8 [jbd2/sda1-8]
  8,1    1       86     0.000504014   422  I  WS 17113920 + 8 [jbd2/sda1-8]
  8,1    1       87     0.000510367   370  D  WS 17113920 + 8 [kworker/1:1H]
  8,1    1       88     0.000614921  4914  C  WS 17113920 + 8 [0]
  8,0    1       89     0.196179477 14725  A  WS 411398904 + 8 <- (8,1) 411396856
  8,1    1       90     0.196180172 14725  Q  WS 411398904 + 8 [ThreadPoolForeg]
  8,1    1       91     0.196184976 14725  G  WS 411398904 + 8 [ThreadPoolForeg]
  8,0    3        1     0.615787407 15059  A  WS 15475728 + 8 <- (8,1) 15473680
  8,1    3        2     0.615788204 15059  Q  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    3        3     0.615792400 15059  G  WS 15475728 + 8 [ThreadPoolForeg]
  8,1    3        4     0.615793068 15059  P   N [ThreadPoolForeg]
  8,0    3        5     0.615809324 15059  A  WS 15475736 + 8 <- (8,1) 15473688
  8,1    3        6     0.615809496 15059  Q  WS 15475736 + 8 [ThreadPoolForeg]
  8,1    3        7     0.615810115 15059  M  WS 15475736 + 8 [ThreadPoolForeg]
  8,1    3        8     0.615811006 15059  U   N [ThreadPoolForeg] 1
  8,1    3        9     0.615811499 15059  I  WS 15475728 + 16 [ThreadPoolForeg]
  8,1    3       10     0.615815352 15059  D  WS 15475728 + 16 [ThreadPoolForeg]
  8,1    3       11     0.616105591     0  C  WS 15475728 + 16 [0]
CPU1 (8,1):
 Reads Queued:           0,        0KiB     Writes Queued:          61,      252KiB
 Read Dispatches:        0,        0KiB     Write Dispatches:       14,      252KiB
 Reads Requeued:         0      Writes Requeued:         0
 Reads Completed:        0,        0KiB     Writes Completed:       14,      252KiB
 Read Merges:            0,        0KiB     Write Merges:           47,      188KiB
 Read depth:             0          Write depth:             2
 IO unplugs:             9          Timer unplugs:           0
CPU3 (8,1):
 Reads Queued:           0,        0KiB     Writes Queued:           2,        8KiB
 Read Dispatches:        0,        0KiB     Write Dispatches:        1,        8KiB
 Reads Requeued:         0      Writes Requeued:         0
 Reads Completed:        0,        0KiB     Writes Completed:        1,        8KiB
 Read Merges:            0,        0KiB     Write Merges:            1,        4KiB
 Read depth:             0          Write depth:             2
 IO unplugs:             1          Timer unplugs:           0

Total (8,1):
 Reads Queued:           0,        0KiB     Writes Queued:          63,      260KiB
 Read Dispatches:        0,        0KiB     Write Dispatches:       15,      260KiB
 Reads Requeued:         0      Writes Requeued:         0
 Reads Completed:        0,        0KiB     Writes Completed:       15,      260KiB
 Read Merges:            0,        0KiB     Write Merges:           48,      192KiB
 IO unplugs:            10          Timer unplugs:           0

Throughput (R/W): 0KiB/s / 337KiB/s
Events (8,1): 254 entries
Skips: 0 forward (0 -   0.0%)

biotop

プロセスtopによるブロックデバイス(ディスク)I/O
topのディスクI/O版

フィールド

loadavg /proc/loadavg
PID キャッシュされたプロセスID(存在する場合は)
基本的にはI/Oの原因となるプロセスを特定する(保証はされない)
COMM キャッシュされたプロセス名(存在する場合は)
基本的にはI/Oの原因となるプロセスを特定する(保証はされない)
D 方向(Direction)
R(Read)
W(Write)
MAJ メジャーデバイス番号
MIN マイナーデバイス番号
DISK ディスクデバイス名
I/O インターバル間のI/Oの数
Kbytes インターバル間のI/OのKbytesの合計
AVGms デバイスに発行されてから完了するまでのI/Oレイテンシの平均時間(ミリ秒)
16:50:00 loadavg: 0.14 0.04 0.01 1/756 30610

PID    COMM             D MAJ MIN DISK       I/O  Kbytes  AVGms
422    jbd2/sda1-8      W 8   0   sda          2     132   0.26
28892  kworker/u256:2   W 8   0   sda         11      44   0.32
30344  cp               W 8   0   sda          1       4   0.23
30345  cp               W 8   0   sda          1       4   0.17
30286  cat              W 8   0   sda          1       4   0.31
30513  npm              W 8   0   sda          1       4   0.30
30343  cp               W 8   0   sda          1       4   0.28
30567  cat              W 8   0   sda          1       4   0.30
0                       R 0   0   ?            2       0   0.18