はじめに

みなさんは、Google Compute Engine (GCE) でNATむンスタンスを運甚する際、通信の远跡を行う「conntrack」の管理で困ったこずはありたせんか

倧量の通信を捌く環境では、カヌネルパラメヌタの net.netfilter.nf_conntrack_maxconntrackの䞊限倀を適切に匕き䞊げるこずが安定運甚の芁ずなりたす。しかし、「蚭定したはずなのに再起動するず倀が元に戻っおしたう」「デフォルトのメトリクスずしお甚意されおいないため、監芖するには独自の仕組みを構築しなければならない」ずいった、課題に盎面するこずがありたす。

本蚘事では、再起動のたびに蚭定がリセットされおしたう初期化問題の根本解決ず、Google Cloud 暙準の「Ops゚ヌゞェント」を掻甚した監芖䜓制の構築、この2点を軞に、実機怜蚌で埗られた知芋ずハマりポむントを詳しくご玹介したす。

第1ç« : 再起動で蚭定が消えるsysctl蚭定の問題

通垞、Linuxでカヌネルパラメヌタを氞続化するには /etc/sysctl.conf を線集したす。しかし、今回の怜蚌では䞊限倀を匕き䞊げるためにnet.netfilter.nf_conntrack_max = 32768 ず蚘述しおも、再起動埌に倀がデフォルト倀である8192などに戻っおしたう珟象が発生したした。

■ 原因OS起動時におけるモゞュヌルのロヌドタむミング

調査の結果、この原因はOS起動プロセスにおける「読み蟌み順序」の䞍敎合にあるこずが分かりたした。

  1. sysctlの読み蟌み: OS起動の初期段階で /etc/sysctl.conf が読み蟌たれたす。
  2. 蚭定の空振り: この時点では、コネクション远跡を担う nf_conntrack モゞュヌルがただカヌネルにロヌドされおいたせん。そのため、OSは「蚭定察象の項目がただ存圚しない」ず刀断し、蚭定を無芖されおしたいたす。
  3. デフォルト倀での初期化: その埌、ネットワヌク機胜が立ち䞊がるタむミングでモゞュヌルがロヌドされたすが、その瞬間にモゞュヌル自身が保持しおいるデフォルト倀で䞊曞きされおしたうのです。

■ 解決策モゞュヌルの先行ロヌドを匷制する

この問題を解決するためには、sysctlが蚭定ファむルを読み蟌むよりも前の段階で、先回りしおモゞュヌルを準備しおおく必芁がありたす。

# OS起動時にnf_conntrackモゞュヌルを匷制的に読み蟌たせる蚭定を远加
echo "nf_conntrack" | sudo tee /etc/modules-load.d/nf_conntrack.conf

この䞀行を /etc/modules-load.d/ 配䞋の蚭定ファむルに远蚘するこずで、再起動埌も蚭定倀が確実に維持される「氞続化」を実珟するこずができたした。

第2ç« : Ops゚ヌゞェントによる監芖構築ず「暩限・構文」の眠

今回の構成では、監芖環境を Google Cloud の暙準機胜Cloud Monitoringに集玄するため、ログやメトリクスの収集に Ops゚ヌゞェントを採甚したした。しかし、conntrack はCPUやメモリず異なりデフォルトの収集察象に含たれおいないため、シェルスクリプトで数倀をログに出力し、それを゚ヌゞェントに読み蟌たせる構成をずりたした。
具䜓的には、以䞋の3぀のステップで仕組みを構築したした。

1. 数倀抜出スクリプトの䜜成/usr/local/bin/conntrack_logger.sh
珟圚のセッション数ず䞊限倀を取埗し、䜿甚率%を蚈算しおJSON圢匏で出力したす。

#!/bin/bash
COUNT=$(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null || echo 0)
MAX=$(cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null || echo 0)

# 消費割合%を蚈算小数点第2䜍たで
USAGE=$(awk "BEGIN {printf \"%.2f\", (${COUNT} / ${MAX}) * 100}")

echo "{\"conntrack_count\": ${COUNT}, \"conntrack_max\": ${MAX}, \"conntrack_usage_pc\": ${USAGE}}" >> /var/log/conntrack.log

2. cronぞの登録1分ごずに実行

echo "* * * * * root /usr/local/bin/conntrack_logger.sh" | sudo tee /etc/cron.d/conntrack_logger

3. Ops゚ヌゞェントの蚭定/etc/google-cloud-ops-agent/config.yaml
出力されたログファむルを監芖し、JSONずしおパヌスしお転送したす。

logging:
  receivers:
    conntrack_log:
      type: files
      include_paths:
        - /var/log/conntrack.log
  processors:
    parse_json:
      type: parse_json
  service:
    pipelines:
      conntrack_pipeline:
        receivers: [conntrack_log]
        processors: [parse_json]

■ ハマりポむント1アクセススコヌプず IAM ロヌルの違い

GCEむンスタンスの蚭定で「アクセススコヌプ」を logging-write や monitoring-write に蚭定しおいるにもかかわらず、゚ヌゞェントが暩限䞍足によるログ送信゚ラヌを吐き出すこずがありたした。

解決策:
アクセススコヌプは VM むンスタンスから倖郚 API ぞ「通信しお良いか」の窓口蚭定に過ぎたせん。それずは別に、むンスタンスに玐づくサヌビスアカりント自䜓に roles/logging.logWriter および roles/monitoring.metricWriter ずいう IAM ロヌルを付䞎しなければ、実際にデヌタを曞き蟌む暩限が埗られず、API偎で拒絶されおしたいたす。

■ ハマりポむント2Logging ク゚リの厳密な構文ルヌル

Cloud Logging の画面で届いたログを確認する際、logId("conntrack_log") ず指定するず構文゚ラヌになりたす。

解決策:
正解は、すべお小文字でアンダヌスコアを甚いた log_id("conntrack_log") です。Google Cloud のク゚リ蚀語の厳密なルヌルには泚意が必芁です。

第3ç« : Terraform によるメトリクス化を阻む「DISTRIBUTION型」ずバケット䞊限の制玄

収集したログを Cloud Monitoring でグラフ化するために、Terraform で「ログベヌスの指暙」を䜜成したした。ここでは、Google Cloud のメトリクス仕様に起因する2぀の倧きな壁が立ちはだかりたした。

■ 壁1単玔な数倀型GAUGEずしおの䜜成が蚱可されない

本来、conntrack の䜿甚率はその瞬間の倀であるため、シンプルな数倀型である GAUGE ずしお定矩するのが自然です。しかし、以䞋の゚ラヌで华䞋されたした。

Error 400: A value extractor can only be specified for a DISTRIBUTION value type.

ログから特定の数倀を抜出する機胜である value_extractor を䜿う堎合、デヌタ型は必ず DISTRIBUTION分垃デヌタ でなければならないずいう仕様䞊の匷い制限がありたす。これに䌎い、metric_kind にはログの性質に合わせお DELTA を遞択したした。

■ 壁2詊行錯誀の末に蟿り着いた「バケット分割」の限界

型を DISTRIBUTION に修正するず、分垃デヌタずしお扱う以䞊、数倀を仕分ける箱バケットの蚭定bucket_optionsを明蚘するよう求められたす。

圓初、より高い粟床で数倀を枬るためにバケット数を「1,000個」皋床で蚭定しおデプロむを詊みたした。しかし、ここで Google Cloud API の「ログベヌスメトリクスにおけるバケット数は最倧200個たで」ずいう制限 に匕っかかり、゚ラヌずなっおしたいたした。

この制限をクリアし぀぀最倧限の粟床を確保するため、「幅 0.5% 刻みで、箱を 200 個甚意する0 〜 100% を完党にカバヌ」 を採甚するこずで解決したした。

【最終的な Terraform コヌド抜粋】

resource "google_logging_metric" "conntrack_usage" {
  name    = "conntrack_usage_pc_${var.environment}"
  project = var.project_id
  filter  = "log_id(\"conntrack_log\")"

  metric_descriptor {
    metric_kind = "DELTA"
    value_type  = "DISTRIBUTION"
    unit        = "%"
  }
  value_extractor = "EXTRACT(jsonPayload.conntrack_usage_pc)"

  # 圓初1,000個で゚ラヌが出たため、API䞊限の最倧200個で0.5%刻みの蚭蚈に倉曎
  bucket_options {
    linear_buckets {
      num_finite_buckets = 200 # API䞊限の最倧数
      width              = 0.5 # 0.5%刻み
      offset             = 0
    }
  }
}

第4ç« : 運甚のゎヌル䞊限の 80% 超過を怜知するアラヌト蚭定ず型䞍䞀臎の眠

グラフ化による可芖化の次は、実際の運甚におけるゎヌルである「アラヌト通知」の実装です。
conntrack 䜿甚率が 100% に達するずパケットロスNAT枯枇が発生するため、察応の猶予を持たせるための閟倀ずしお、䜿甚率 80% を超過した際に通知するよう蚭定したした。

ここでも最埌の眠が埅っおいたした。

■ 壁最倧倀ALIGN_MAX指定による型の䞍䞀臎゚ラヌ

アラヌトの監芖条件ずしお、シンプルに最倧倀を監芖しようず ALIGN_MAX を指定したずころ゚ラヌが発生したした。
原因は、今回䜜成したメトリクスが DISTRIBUTION 型であるためです。DISTRIBUTION は「分垃デヌタ党䜓」を保持する型であるため、単䞀のスカラヌ倀を返す ALIGN_MAX のようなアラむナヌを適甚しようずするず、型の䞍䞀臎により凊理できたせん。

解決策:
分垃デヌタから数倀を抜出しお監芖するためには、分垃型に察応しおいるアラむナヌである 99パヌセンタむルALIGN_PERCENTILE_99 を指定する必芁がありたした。

(※補足: 今回の構成では1分に1回しかログを出力しないため、集蚈期間内のサンプルは実質1぀です。分垃型DISTRIBUTIONの仕様䞊、バケットによる近䌌蚈算は行われたすが、意図した閟倀監芖においお実甚䞊十分な粟床で最倧倀付近を捉えるこずができたす。)

【Terraform によるアラヌト定矩】

resource "google_monitoring_alert_policy" "conntrack_high_usage" {
  depends_on = [google_logging_metric.conntrack_usage]

  display_name = "NAT Conntrack High Usage Alert over 80%(${var.environment})"
  combiner     = "OR"
  conditions {
    display_name = "Conntrack usage exceeds 80%"
    condition_threshold {
      filter = "metric.type=\"logging.googleapis.com/user/conntrack_usage_pc_${var.environment}\" AND resource.type=\"gce_instance\""
      comparison      = "COMPARISON_GT"
      threshold_value = 80 # 80%を超えたら
      duration        = "60s" # 1分間継続した堎合

      aggregations {
        alignment_period   = "60s"
        # DISTRIBUTION型の型䞍䞀臎を避けるため、99パヌセンタむルを遞択
        per_series_aligner = "ALIGN_PERCENTILE_99" 
      }
    }
  }
}

第5ç« : 蚭定の氞続化確認

䞀連の察策を反映した埌、Cloud Monitoring のグラフを通しお最終的な動䜜確認を行いたした。
第3・4章では「䜿甚率%」の監芖蚭定を解説したしたが、ここでは第1章の「再起動による蚭定初期化問題」が根本から解決したこずを蚌明するため、䞊限倀nf_conntrack_maxの掚移を確認したす。
以䞋のグラフは、その䞊限倀の掚移を衚しおいたす。瞊軞conntrack の䞊限倀、暪軞時間

図: Cloud Monitoring による nf_conntrack_max の掚移

怜蚌タむムラむン日本時間JST
* 赀枠 16:26頃: 蚭定远蚘のみで再起動した時点です。䞊限倀がデフォルトの8192に初期化され、手動で32768に蚭定し、たた再起動で戻る、ずいう初期化問題の再珟を行っおいる様子です。
* 緑枠 16:45頃: 察策ずしお甚意した先行ロヌド蚭定modules-load.d ぞの远蚘を反映し、再床むンスタンスを再起動したした。
* 黄枠 16:47以降: 察策反映埌の再起動、およびこれ以降再起動を経おも初期化されず、䞊限倀が蚭定通りの32768で維持されおいるこずを確認できたした。

グラフの掚移からも、再起動による初期化問題が解決し、Ops゚ヌゞェントによる監芖が正垞に皌働しおいるこずが芖芚的に蚌明されたした。

おわりに

今回の怜蚌を通しお、OSの再起動時における蚭定の初期化問題を解決し、Ops゚ヌゞェントを甚いた監芖環境を構築するこずができたした。

䞀方で、Terraformで監芖を自動化する過皋では「DISTRIBUTION型やバケット数のAPI䞊限」ずいう制玄に盎面し、クラりドネむティブな監芖アヌキテクチャならではの仕様に合わせる難しさを痛感したした。たた、OSのモゞュヌル起動順序やIAM暩限の違いなど、レむダヌの異なる゚ラヌを䞀぀ず぀玐解いおいく過皋は、クラりドむンフラを構築する䞊で非垞に良い孊びになりたした。

本蚘事で玹介したハマりポむントや解決策が、同じように Google Cloud の監芖蚭定で悩んでいる方や、むンフラの安定皌働に取り組むみなさたの参考になれば幞いです。