第9章:システム運用の自動化

コンピュータがコンピュータを管理する。この単純な概念が、現代システム運用の根幹を支えている。しかし、人間による手動運用から自動化への移行は、単なる効率化以上の意味を持つ。それは、システムの複雑性が人間の認知限界を超えた時点で生じる必然的な進化である。

なぜinitシステムからsystemdへの移行が業界に大きな議論を巻き起こしたのか。ログ管理において、テキストファイルからバイナリ形式への変更がなぜ重要なのか。これらの変化の背景には、運用規模の拡大と、それに伴う運用品質への要求の高度化がある。

9.1 サービス管理システムの進化

initシステムからsystemdへの設計思想の変遷

Unixにおけるプロセス管理の歴史は、システムの複雑化とともに進化してきた。System V init から systemd への移行は、単なる技術的改良ではなく、システム管理に対する根本的な設計思想の転換を表している。

System V init の限界

従来の init システムは、シンプルさを重視した設計であった:

System V init の特徴:
1. シーケンシャルな起動プロセス
   - /etc/rc.d/rc0.d から rc6.d までの段階的実行
   - 各スクリプトの順次実行(並列処理なし)
   - 起動完了まで数分を要する場合

2. シェルスクリプトベースの制御
   - 起動・停止・再起動はすべてシェルスクリプト
   - プロセス管理の複雑性をスクリプト作成者に委ねる
   - デバッグとメンテナンスの困難性

3. プロセス監視機能の欠如
   - プロセスの異常終了検出機能なし
   - 自動再起動機能なし
   - 依存関係管理の限定性

実際の問題事例

# System V init での典型的な問題
service httpd start
# 成功したように見えるが、実際は設定エラーで起動していない

ps aux | grep httpd
# プロセスが存在しない

# ログを確認しないと原因が分からない
tail /var/log/httpd/error_log
# [error] (98)Address already in use: make_sock: could not bind to address 0.0.0.0:80

systemd の設計革新

systemd は、現代システムの要求に応える包括的なソリューションを提供する:

systemd の特徴:

1. 並列起動による高速化
   - 依存関係グラフによる最適化
   - ソケットベースアクティベーション
   - 起動時間の大幅短縮(30秒 → 5秒程度)

2. 宣言的サービス定義
   - .service ファイルによる標準化
   - 設定の一貫性と可読性向上
   - 人的エラーの削減

3. プロセスライフサイクル管理
   - 自動再起動機能
   - リソース制限
   - セキュリティ機能の統合

systemd unit ファイルの例

[Unit]
Description=Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
Wants=httpd-init.service

[Service]
Type=notify
Environment=LANG=C
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
# 自動再起動の設定
Restart=on-failure
RestartSec=5
# セキュリティ強化
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/log/httpd /var/run/httpd

[Install]
WantedBy=multi-user.target

依存関係管理の高度化

systemd の依存関係管理は、複雑なシステムでの確実な起動順序を保証する:

依存関係の種類:

Requires=:必須依存
- 指定サービスが起動失敗時、このサービスも停止
- データベースサーバー → Webアプリケーション

Wants=:推奨依存  
- 指定サービス起動失敗でも、このサービスは継続
- ロギングサービス → アプリケーション

After=:順序依存
- 指定サービス起動後に開始
- ネットワーク → Webサーバー

Before=:逆順序依存
- このサービス起動後に指定サービス開始
- データベース → アプリケーション

実際の起動分析

# 起動時間の分析
systemd-analyze
# Startup finished in 2.547s (kernel) + 3.021s (initrd) + 8.123s (userspace) = 13.691s

# 詳細な起動チェーン分析  
systemd-analyze critical-chain
# The time when unit became active or started is printed after the "@" character.
# multi-user.target @8.087s
# └─apache2.service @7.292s +794ms
#   └─network.target @7.286s
#     └─NetworkManager.service @6.234s +1.051s
#       └─dbus.service @6.195s

# グラフィカルな依存関係表示
systemd-analyze plot > startup.svg

cgroup を活用したリソース制御

Control Groups(cgroup)は、Linuxカーネルが提供するリソース制御機能である。systemd は cgroup を活用して、プロセス単位から階層的なリソース管理を実現している。

cgroup の階層構造

cgroup v2 の階層例:
/sys/fs/cgroup/
├── system.slice/          # システムサービス
│   ├── ssh.service
│   ├── httpd.service
│   └── mysql.service
├── user.slice/            # ユーザープロセス
│   ├── user-1000.slice
│   └── user-1001.slice  
└── machine.slice/         # 仮想マシン・コンテナ
    ├── docker-container1.service
    └── libvirt-vm1.service

リソース制限の実装

  1. CPUリソース制御
    # /etc/systemd/system/webserver.service
    [Service]
    CPUQuota=50%          # CPU使用率を50%に制限
    CPUAccounting=yes     # CPU使用量の記録を有効
    
    # 実行時のCPU制限変更
    systemctl set-property httpd.service CPUQuota=30%
       
    # CPU使用量の確認
    systemctl show httpd.service | grep CPU
    # CPUUsageNSec=45234567890
    # CPUQuotaPerSecUSec=300000  # 30% = 300ms/1s
    
  2. メモリ制御
    [Service]
    MemoryMax=1G          # 最大メモリ使用量
    MemorySwapMax=0       # スワップ使用禁止
    MemoryAccounting=yes  # メモリ使用量記録
    
    # メモリ制限の動的変更
    systemctl set-property mysql.service MemoryMax=2G
       
    # メモリ使用状況の確認
    systemctl show mysql.service | grep Memory
    # MemoryMax=2147483648
    # MemoryCurrent=1524670464
    
  3. I/O制御
    [Service]
    IOReadBandwidthMax=/dev/sda 50M    # 読み込み50MB/s制限
    IOWriteBandwidthMax=/dev/sda 20M   # 書き込み20MB/s制限
    IOAccounting=yes
    

プロセス分離の実装

systemd は、セキュリティ機能と組み合わせて、プロセス分離を実現する:

[Service]
# ファイルシステム分離
PrivateTmp=true           # 専用/tmpディレクトリ
ProtectSystem=strict      # /usr, /boot等を読み取り専用
ProtectHome=true          # /homeへのアクセス禁止
ReadWritePaths=/var/log/myapp  # 書き込み許可パス

# ネットワーク分離
PrivateNetwork=true       # 専用ネットワーク名前空間
IPAddressAllow=192.168.1.0/24  # 許可IPアドレス

# ユーザー分離
User=webapp               # 実行ユーザー
Group=webapp              # 実行グループ
DynamicUser=true          # 動的ユーザー作成

実際の適用例

# Webアプリケーションの分離設定
cat > /etc/systemd/system/webapp.service << EOF
[Unit]
Description=Web Application
After=network.target

[Service]
Type=simple
User=webapp
Group=webapp
ExecStart=/opt/webapp/bin/server
Restart=always

# リソース制限
CPUQuota=25%
MemoryMax=512M

# セキュリティ強化
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/webapp /var/lib/webapp
NoNewPrivileges=true
PrivateDevices=true

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl start webapp.service

# 制限の確認
systemctl show webapp.service | grep -E "(CPU|Memory)"

9.2 ログ管理アーキテクチャの最適化

journald:バイナリログの利点と課題

systemd-journald は、従来のテキストベースログファイルから、構造化されたバイナリログへの転換を実現した。この変更は、ログ管理の効率性と機能性を大幅に向上させる一方で、従来の運用手法との互換性という課題も提起している。

従来のログ管理の限界

syslog の課題:
1. 非構造化データ
   - テキスト解析による情報抽出の困難性
   - ログフォーマットの非統一
   - 検索性能の低下

2. ログローテーション管理
   - logrotate による複雑な設定
   - ローテーション中のログ欠損リスク
   - 圧縮ファイルの管理負荷

3. リアルタイム性の欠如
   - ログファイルの tail -f による監視
   - イベント駆動処理の困難性
   - 大量ログでの性能問題

journald の技術的革新

journald は、これらの課題を根本的に解決する:

journald の特徴:

1. 構造化ログエントリ
   - キー・バリュー形式での情報格納
   - メタデータの自動付与
   - 効率的なインデックス化

2. バイナリ形式による最適化
   - 圧縮による容量削減(60-80%削減)
   - 高速検索(O(log n))
   - インデックスによる効率的アクセス

3. 統合されたログ管理
   - カーネル、システム、アプリケーションログの統一
   - 自動的なメタデータ追加
   - 設定不要のログローテーション

journald のログ構造

# ログエントリの詳細表示
journalctl -o verbose -n 1
# Fri 2024-06-18 10:15:30 JST [s=7f4c8d2e1a9c...]
# MESSAGE=Started HTTP Server
# _PID=1234
# _UID=33
# _GID=33
# _COMM=httpd
# _EXE=/usr/sbin/httpd
# _CMDLINE=/usr/sbin/httpd -DFOREGROUND
# _SYSTEMD_CGROUP=/system.slice/httpd.service
# _SYSTEMD_UNIT=httpd.service
# PRIORITY=6
# _TRANSPORT=journal
# _HOSTNAME=server01
# _MACHINE_ID=a1b2c3d4...

高度な検索機能

# ユニット別ログ表示
journalctl -u httpd.service

# 時間範囲指定
journalctl --since "2024-06-18 09:00:00" --until "2024-06-18 10:00:00"

# 優先度フィルタ
journalctl -p err               # エラー以上のみ
journalctl -p warning..crit     # 警告からクリティカルまで

# プロセスID指定
journalctl _PID=1234

# 複合条件
journalctl _SYSTEMD_UNIT=httpd.service PRIORITY=3

# リアルタイム監視
journalctl -f -u httpd.service

ログ永続化の設定

# journald 設定ファイル
cat > /etc/systemd/journald.conf << EOF
[Journal]
Storage=persistent      # 永続化有効
Compress=yes           # 圧縮有効
SystemMaxUse=1G        # 最大使用容量
SystemMaxFileSize=100M  # 単一ファイル最大サイズ
MaxRetentionSec=7day   # 保持期間
MaxFileSec=1day        # ファイル分割間隔
EOF

systemctl restart systemd-journald

# 容量使用状況確認
journalctl --disk-usage
# Archived and active journals take up 456.7M in the file system.

従来ツールとの互換性

journald は、既存の syslog ツールとの互換性を維持する:

# rsyslog との連携設定
cat > /etc/rsyslog.d/00-journal.conf << EOF
# journald から rsyslog への転送
$ModLoad imjournal
$IMJournalStateFile /var/lib/rsyslog/imjournal.state
$OmitLocalLogging on
*.* @@log-server.example.com:514
EOF

# fluentd との連携
# /etc/fluent/fluent.conf
<source>
  @type systemd
  tag systemd
  path /var/log/journal
  pos_file /var/log/fluentd-journald.log.pos
  filters [{ "_SYSTEMD_UNIT": "httpd.service" }]
  read_from_head true
</source>

大容量ログの効率的処理戦略

現代のシステムが生成するログ量は膨大である。効率的な処理戦略なしには、ログがシステムの負荷要因となる。

ログ量の実態

典型的なログ生成量(中規模Webサイト):
- Webサーバー:100-500MB/日
- アプリケーション:50-200MB/日  
- データベース:20-100MB/日
- システム:10-50MB/日
合計:200-850MB/日(月間 6-25GB)

大規模環境:
- 1日あたり 10-100GB
- 月間 0.3-3TB
- 年間 3.6-36TB

ログ分割戦略

  1. 時間ベース分割
    # systemd-journald の時間分割
    SystemMaxFileSize=100M
    MaxFileSec=1day
       
    # 結果:1日ごと、または100MB到達時に新ファイル作成
    ls -la /var/log/journal/*/
    # system@a1b2c3d4-20240618.journal
    # system@a1b2c3d4-20240619.journal
    
  2. サービス別分割
    # rsyslog によるサービス別分割
    cat > /etc/rsyslog.d/50-default.conf << EOF
    # HTTPサーバーログ
    :programname, isequal, "httpd" /var/log/httpd/access.log
       
    # データベースログ
    :programname, isequal, "mysql" /var/log/mysql/mysql.log
       
    # その他
    *.* /var/log/syslog
    EOF
    

ログ圧縮とアーカイブ

# 自動圧縮スクリプトの例
cat > /usr/local/bin/log-compress.sh << 'EOF'
#!/bin/bash

LOG_DIR="/var/log/archive"
COMPRESS_DAYS=1
DELETE_DAYS=90

# 1日経過したログを圧縮
find /var/log -name "*.log" -mtime +${COMPRESS_DAYS} \
  -not -path "/var/log/archive/*" \
  -exec gzip {} \;

# 圧縮済みログをアーカイブディレクトリに移動
find /var/log -name "*.log.gz" -mtime +1 \
  -not -path "/var/log/archive/*" \
  -exec mv {} ${LOG_DIR}/ \;

# 90日経過したアーカイブを削除
find ${LOG_DIR} -name "*.log.gz" -mtime +${DELETE_DAYS} \
  -delete
EOF

chmod +x /usr/local/bin/log-compress.sh

# systemd timer での定期実行
cat > /etc/systemd/system/log-compress.service << EOF
[Unit]
Description=Compress old log files

[Service]
Type=oneshot
ExecStart=/usr/local/bin/log-compress.sh
EOF

cat > /etc/systemd/system/log-compress.timer << EOF
[Unit]
Description=Run log compression daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl enable --now log-compress.timer

ログ転送の最適化

# rsyslog の非同期転送設定
cat > /etc/rsyslog.d/90-forwarding.conf << EOF
# 非同期処理キューの設定
$ActionQueueType LinkedList
$ActionQueueFileName remote
$ActionResumeRetryCount -1
$ActionQueueSaveOnShutdown on
$ActionQueueTimeoutEnqueue 10
$ActionQueueDiscardMark 9750
$ActionQueueHighWaterMark 9000

# バッファリング設定
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name

# 複数ログサーバーへの冗長転送
*.* @@log-server1.example.com:514
*.* @@log-server2.example.com:514
EOF

9.3 メトリクス収集システムの設計

Prometheus エコシステムの実装原理

Prometheus は、現代のメトリクス収集において de facto standard となっている。その設計思想と実装を理解することで、効果的な監視システムを構築できる。

Prometheus の設計原理

Prometheus の特徴:
1. Pull モデル
   - 中央サーバーがメトリクスを能動的に収集
   - ネットワーク構成の簡素化
   - スケーラビリティの向上

2. 時系列データベース
   - ラベルベースの多次元データ
   - 効率的な圧縮アルゴリズム
   - 高速クエリ処理

3. 分散アーキテクチャ対応
   - 階層化による大規模対応
   - 連邦機能による統合
   - 高可用性設計

メトリクス収集の実装

  1. Node Exporter によるシステムメトリクス
    # Node Exporter のインストール
    wget https://github.com/prometheus/node_exporter/releases/download/v1.8.0/node_exporter-1.8.0.linux-amd64.tar.gz
    tar xvfz node_exporter-*.tar.gz
    sudo cp node_exporter-1.8.0.linux-amd64/node_exporter /usr/local/bin/
       
    # systemd サービス設定
    cat > /etc/systemd/system/node_exporter.service << EOF
    [Unit]
    Description=Node Exporter
    After=network.target
       
    [Service]
    Type=simple
    User=prometheus
    ExecStart=/usr/local/bin/node_exporter
    Restart=always
       
    [Install]
    WantedBy=multi-user.target
    EOF
       
    systemctl enable --now node_exporter.service
       
    # メトリクス確認
    curl http://localhost:9100/metrics | grep -E "(cpu|memory|disk)"
    # node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67
    # node_memory_MemTotal_bytes 8.589934592e+09
    # node_disk_io_time_seconds_total{device="sda"} 123.45
    
  2. アプリケーションメトリクスの実装
    # Python アプリケーションでのメトリクス実装
    from prometheus_client import Counter, Histogram, Gauge, start_http_server
    import time
       
    # メトリクス定義
    REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'status'])
    REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')
    ACTIVE_CONNECTIONS = Gauge('active_connections', 'Active connections')
       
    @REQUEST_DURATION.time()
    def process_request(method, path):
        # ビジネスロジック
        time.sleep(0.1)
           
        # メトリクス更新
        REQUEST_COUNT.labels(method=method, status=200).inc()
        return "OK"
       
    # メトリクスサーバー起動
    start_http_server(8000)
    
  3. Prometheus サーバー設定
    # prometheus.yml
    global:
      scrape_interval: 15s
      evaluation_interval: 15s
       
    scrape_configs:
      - job_name: 'prometheus'
        static_configs:
          - targets: ['localhost:9090']
       
      - job_name: 'node-exporter'
        static_configs:
          - targets: 
            - 'server1:9100'
            - 'server2:9100'
            - 'server3:9100'
        scrape_interval: 5s
        metrics_path: /metrics
       
      - job_name: 'webapp'
        static_configs:
          - targets: ['webapp1:8000', 'webapp2:8000']
        scrape_interval: 10s
       
    rule_files:
      - "alert.rules.yml"
       
    alerting:
      alertmanagers:
        - static_configs:
            - targets: ['alertmanager:9093']
    

PromQL によるクエリ最適化

# CPU使用率の計算
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# メモリ使用率
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

# ディスクI/O使用率
rate(node_disk_io_time_seconds_total[5m]) * 100

# レスポンス時間の95パーセンタイル
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# エラー率
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100

監視データの可視化とアラート設計

効果的な監視システムは、適切な可視化とアラート機能を提供する必要がある。情報の過多も不足も、システム運用に悪影響を与える。

Grafana ダッシュボード設計

{
  "dashboard": {
    "title": "System Overview",
    "panels": [
      {
        "title": "CPU Usage",
        "type": "stat",
        "targets": [
          {
            "expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
            "legendFormat": "CPU Usage %"
          }
        ],
        "thresholds": [
          {"color": "green", "value": 0},
          {"color": "yellow", "value": 70},
          {"color": "red", "value": 90}
        ]
      },
      {
        "title": "Memory Usage",
        "type": "timeseries",
        "targets": [
          {
            "expr": "node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes",
            "legendFormat": "Used Memory"
          },
          {
            "expr": "node_memory_MemAvailable_bytes", 
            "legendFormat": "Available Memory"
          }
        ]
      }
    ]
  }
}

階層化されたアラート設計

  1. 緊急度別アラート分類 ```yaml

    alert.rules.yml

    groups:

    • name: critical rules:
      • alert: HighCPUUsage expr: 100 - (avg(rate(node_cpu_seconds_total{mode=”idle”}[5m])) * 100) > 90 for: 2m labels: severity: critical annotations: summary: “High CPU usage detected” description: “CPU usage is above 90% for more than 2 minutes”

      • alert: DiskSpaceLow expr: (node_filesystem_free_bytes / node_filesystem_size_bytes) * 100 < 10 for: 1m labels: severity: critical annotations: summary: “Disk space critically low”

    • name: warning rules:
      • alert: HighMemoryUsage expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 80 for: 5m labels: severity: warning annotations: summary: “Memory usage is high” ```
  2. Alertmanager による通知制御
    # alertmanager.yml
    global:
      smtp_smarthost: 'mail.example.com:587'
      smtp_from: 'alerts@example.com'
       
    route:
      group_by: ['alertname', 'instance']
      group_wait: 10s
      group_interval: 10s
      repeat_interval: 1h
      receiver: 'default'
      routes:
      - match:
          severity: critical
        receiver: 'critical-alerts'
        group_wait: 5s
        repeat_interval: 5m
      - match:
          severity: warning  
        receiver: 'warning-alerts'
        repeat_interval: 30m
       
    receivers:
    - name: 'default'
      email_configs:
      - to: 'admin@example.com'
        subject: 'Alert: '
        body: |
             
          Alert: 
          Description: 
             
       
    - name: 'critical-alerts'
      email_configs:
      - to: 'oncall@example.com'
        subject: '[CRITICAL] '
      slack_configs:
      - api_url: 'YOUR_SLACK_WEBHOOK_URL'
        channel: '#alerts'
        title: 'Critical Alert'
       
    - name: 'warning-alerts'
      email_configs:
      - to: 'team@example.com'
        subject: '[WARNING] '
    

アラート疲れの防止

# アラート抑制ルール
inhibit_rules:
- source_match:
    severity: 'critical'
  target_match:
    severity: 'warning'
  equal: ['instance']

# 時間帯別アラート制御
- source_match:
    alertname: 'MaintenanceMode'
  target_match_re:
    alertname: '.*'
  equal: ['instance']

9.4 設定管理の自動化戦略

Ansible:べき等性の実装とスケーラビリティ

設定管理の自動化において、べき等性(同じ操作を何度実行しても同じ結果になる性質)は最も重要な概念の一つである。Ansible は、この原則を基盤として設計されている。

べき等性の実装原理

# べき等性を持つPlaybook例
- name: Ensure web server is installed and configured
  hosts: webservers
  tasks:
  - name: Install Apache
    package:
      name: httpd
      state: present
    # 既にインストール済みの場合、何も行わない
  
  - name: Start Apache service
    service:
      name: httpd
      state: started
      enabled: yes
    # 既に起動・有効化済みの場合、変更なし
  
  - name: Configure Apache
    template:
      src: httpd.conf.j2
      dest: /etc/httpd/conf/httpd.conf
      backup: yes
    notify: restart apache
    # 設定内容が同じ場合、ファイル更新なし
  
  handlers:
  - name: restart apache
    service:
      name: httpd
      state: restarted
    # 設定変更時のみ実行される

大規模環境でのスケーラビリティ対策

  1. インベントリの階層化
    # inventory/production/hosts
    [webservers]
    web01.example.com
    web02.example.com
    web03.example.com
       
    [dbservers]
    db01.example.com
    db02.example.com
       
    [webservers:vars]
    http_port=80
    max_clients=200
       
    [dbservers:vars]
    mysql_port=3306
    max_connections=100
       
    [production:children]
    webservers
    dbservers
       
    [production:vars]
    environment=production
    backup_enabled=true
    
  2. ロール指向設計 ```yaml

    roles/webserver/tasks/main.yml

    • name: Include OS-specific variables include_vars: “.yml”

    • name: Install web server packages package: name: “” state: present

    • name: Configure web server template: src: “” dest: “” notify: restart web server

    • name: Ensure web server is running service: name: “” state: started enabled: yes ```

  3. パフォーマンス最適化
    # ansible.cfg
    [defaults]
    host_key_checking = False
    gathering = smart
    fact_caching = redis
    fact_caching_timeout = 3600
       
    [ssh_connection]
    ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r
    pipelining = True
       
    # 並列実行数の調整
    forks = 20
    

実行結果の例

ansible-playbook -i inventory/production webserver.yml

PLAY [Configure web servers] **********************************

TASK [Gathering Facts] ***************************************
ok: [web01.example.com]
ok: [web02.example.com]
ok: [web03.example.com]

TASK [webserver : Install Apache] ***************************
ok: [web01.example.com]  # 既にインストール済み
changed: [web02.example.com]  # 新規インストール
ok: [web03.example.com]

TASK [webserver : Configure Apache] *************************
changed: [web01.example.com]  # 設定変更
ok: [web02.example.com]  # 設定変更なし
ok: [web03.example.com]

RUNNING HANDLER [webserver : restart apache] ****************
changed: [web01.example.com]  # web01のみ再起動

PLAY RECAP ***************************************************
web01.example.com      : ok=4    changed=2    unreachable=0    failed=0
web02.example.com      : ok=3    changed=1    unreachable=0    failed=0
web03.example.com      : ok=3    changed=0    unreachable=0    failed=0

Infrastructure as Code の実践指針

Infrastructure as Code(IaC)は、インフラストラクチャの構成をコードとして管理する手法である。バージョン管理、レビュープロセス、自動テストを通じて、運用品質の向上を実現する。

IaC の実装階層

IaC 実装の階層構造:

1. インフラストラクチャ層(Terraform/CloudFormation)
   - ネットワーク設定
   - サーバーインスタンス
   - ストレージリソース

2. プロビジョニング層(Ansible/Chef/Puppet)
   - OS設定
   - ミドルウェアインストール
   - アプリケーションデプロイ

3. アプリケーション層(Kubernetes/Docker Compose)
   - アプリケーション設定
   - サービス定義
   - スケーリング設定

Terraform による宣言的インフラ管理

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# VPC作成
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
  }
}

# サブネット作成
resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"
  availability_zone = var.availability_zones[count.index]
  
  map_public_ip_on_launch = true
  
  tags = {
    Name = "${var.environment}-public-${count.index + 1}"
    Type = "public"
  }
}

# EC2インスタンス
resource "aws_instance" "web" {
  count                  = var.instance_count
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.public[count.index % length(aws_subnet.public)].id
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = base64encode(templatefile("user-data.sh", {
    environment = var.environment
  }))
  
  tags = {
    Name = "${var.environment}-web-${count.index + 1}"
  }
}

変数とモジュール化

# variables.tf
variable "environment" {
  description = "Environment name"
  type        = string
  default     = "production"
}

variable "instance_count" {
  description = "Number of instances"
  type        = number
  default     = 3
}

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
  default     = ["us-west-2a", "us-west-2b", "us-west-2c"]
}

# outputs.tf
output "instance_ids" {
  description = "EC2 instance IDs"
  value       = aws_instance.web[*].id
}

output "public_ips" {
  description = "Public IP addresses"
  value       = aws_instance.web[*].public_ip
}

状態管理とリモートバックエンド

# backend.tf
terraform {
  backend "s3" {
    bucket         = "terraform-state-bucket"
    key            = "production/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

実行フロー

# 初期設定
terraform init

# 計画の確認
terraform plan -var-file="production.tfvars"

# 適用
terraform apply -var-file="production.tfvars"

# 出力確認
terraform output

9.5 オーケストレーション設計の実際

Kubernetes クラスタ設計の考慮点

Kubernetes は、コンテナオーケストレーションの標準となっているが、その設計には多くの考慮点がある。適切な設計なしには、複雑性がメリットを上回る結果となる。

クラスタアーキテクチャの設計選択

  1. マルチマスター構成
    # kubeadm-config.yaml
    apiVersion: kubeadm.k8s.io/v1beta3
    kind: ClusterConfiguration
    kubernetesVersion: v1.28.0
    controlPlaneEndpoint: "k8s-api.example.com:6443"
    etcd:
      external:
        endpoints:
        - https://etcd1.example.com:2379
        - https://etcd2.example.com:2379
        - https://etcd3.example.com:2379
    networking:
      serviceSubnet: "10.96.0.0/12"
      podSubnet: "10.244.0.0/16"
    apiServer:
      advertiseAddress: "10.0.1.10"
    
  2. ノード分類とテイント設定
    # マスターノードの設定
    kubectl taint nodes master1 node-role.kubernetes.io/control-plane:NoSchedule
       
    # 専用ワーカーノードの設定
    kubectl label nodes worker1 node-type=compute
    kubectl label nodes worker2 node-type=storage
    kubectl label nodes worker3 node-type=monitoring
       
    # GPU専用ノード
    kubectl taint nodes gpu-node1 nvidia.com/gpu:NoSchedule
    kubectl label nodes gpu-node1 accelerator=nvidia-tesla-v100
    

リソース管理とQoS制御

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: nginx:1.21
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      nodeSelector:
        node-type: compute
      tolerations:
      - key: "node-type"
        operator: "Equal"
        value: "compute"
        effect: "NoSchedule"

ネットワークポリシーによるセキュリティ制御

# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-app-netpol
spec:
  podSelector:
    matchLabels:
      app: web-app
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
    - podSelector:
        matchLabels:
          role: load-balancer
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: database
    ports:
    - protocol: TCP
      port: 5432
  - to: []  # DNS resolution
    ports:
    - protocol: UDP
      port: 53

効率的なワークロード配置戦略

Kubernetes でのワークロード配置は、パフォーマンス、可用性、コスト効率性のバランスを取る必要がある。

アフィニティとアンチアフィニティ

# pod-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  template:
    spec:
      affinity:
        # Podアンチアフィニティ(分散配置)
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-app
            topologyKey: "kubernetes.io/hostname"
        
        # ノードアフィニティ(特定ノード選択)
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-type
                operator: In
                values:
                - compute
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: zone
                operator: In
                values:
                - zone-a

Horizontal Pod Autoscaler(HPA)

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60

Vertical Pod Autoscaler(VPA)

# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: web-app
      maxAllowed:
        cpu: 1000m
        memory: 1Gi
      minAllowed:
        cpu: 100m
        memory: 50Mi

実際の運用における配置戦略例

# ノードのリソース使用状況確認
kubectl top nodes
# NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
# worker-1   256m         12%    1024Mi          32%
# worker-2   180m         9%     768Mi           24%
# worker-3   420m         21%    1536Mi          48%

# Pod配置状況の確認
kubectl get pods -o wide
# NAME                       NODE       STATUS
# web-app-5f6d8b7c9d-abc12   worker-1   Running
# web-app-5f6d8b7c9d-def34   worker-2   Running
# web-app-5f6d8b7c9d-ghi56   worker-3   Running

# スケジューリング失敗の確認
kubectl get events --field-selector reason=FailedScheduling

9.4 障害対応とトラブルシューティングの自動化

インシデント検出と初期対応の自動化

システム障害は避けられないが、その影響を最小限に抑えることは可能である。自動化された障害対応システムは、人間の介入を待たずに初期対応を実行できる。

包括的な診断スクリプト

#!/bin/bash
# comprehensive_diagnostics.sh - システム状態の包括的診断

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_DIR="/var/log/diagnostics/${TIMESTAMP}"
mkdir -p "${REPORT_DIR}"

# 診断レポートの開始
exec > >(tee -a "${REPORT_DIR}/diagnostic_report.log")
exec 2>&1

echo "=== システム診断開始: ${TIMESTAMP} ==="

# 1. ネットワーク診断
echo -e "\n=== ネットワーク診断 ==="
{
    echo "## 接続性テスト"
    ping -c 3 8.8.8.8 || echo "外部接続: 失敗"
    ping -c 3 $(ip route | grep default | awk '{print $3}') || echo "ゲートウェイ接続: 失敗"
    
    echo -e "\n## リスニングポート"
    ss -tlnp | grep -E ':80|:443|:22|:3306|:5432|:6379'
    
    echo -e "\n## ネットワーク統計"
    netstat -s | grep -E 'retransmit|error|failed|timeout'
    
    echo -e "\n## DNS解決テスト"
    for domain in google.com $(hostname -f); do
        dig +short $domain || echo "DNS解決失敗: $domain"
    done
} > "${REPORT_DIR}/network_diagnostics.log"

# 2. システムリソース診断
echo -e "\n=== システムリソース診断 ==="
{
    echo "## CPU使用状況"
    mpstat -P ALL 1 3
    echo -e "\n## 高CPU使用プロセス"
    ps aux --sort=-%cpu | head -20
    
    echo -e "\n## メモリ使用状況"
    free -h
    echo -e "\n## メモリ詳細"
    cat /proc/meminfo | grep -E 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree'
    
    echo -e "\n## ディスク使用状況"
    df -h
    echo -e "\n## iノード使用状況"
    df -i
    
    echo -e "\n## I/O統計"
    iostat -x 1 3
} > "${REPORT_DIR}/resource_diagnostics.log"

# 3. サービス状態診断
echo -e "\n=== サービス状態診断 ==="
{
    echo "## systemdサービス状態"
    systemctl list-units --failed
    
    echo -e "\n## 重要サービスの詳細状態"
    for service in nginx httpd mysql postgresql redis docker kubelet; do
        if systemctl list-unit-files | grep -q "^${service}.service"; then
            echo -e "\n### ${service}"
            systemctl status ${service} --no-pager
            journalctl -u ${service} --since "1 hour ago" --no-pager | tail -50
        fi
    done
} > "${REPORT_DIR}/service_diagnostics.log"

# 4. ログ分析
echo -e "\n=== ログ分析 ==="
{
    echo "## システムログのエラー"
    journalctl -p err --since "1 hour ago" --no-pager
    
    echo -e "\n## カーネルメッセージ"
    dmesg -T | grep -E 'error|fail|warn' | tail -50
    
    echo -e "\n## 認証ログ"
    grep -E 'Failed|Invalid|error' /var/log/auth.log | tail -50
} > "${REPORT_DIR}/log_analysis.log"

# 5. セキュリティチェック
echo -e "\n=== セキュリティチェック ==="
{
    echo "## ログイン失敗"
    lastb | head -20
    
    echo -e "\n## 現在の接続"
    ss -tnp | grep ESTABLISHED
    
    echo -e "\n## ファイアウォール状態"
    iptables -L -n -v
} > "${REPORT_DIR}/security_check.log"

# 診断結果の要約
echo -e "\n=== 診断結果要約 ==="
CRITICAL_ISSUES=0

# クリティカルな問題の検出
if ! ping -c 1 8.8.8.8 &>/dev/null; then
    echo "⚠️  外部ネットワーク接続不可"
    ((CRITICAL_ISSUES++))
fi

if [ $(df -h / | awk 'NR==2 {print int($5)}') -gt 90 ]; then
    echo "⚠️  ルートファイルシステム使用率90%超過"
    ((CRITICAL_ISSUES++))
fi

if [ $(free | awk '/^Mem:/ {print int($3/$2 * 100)}') -gt 90 ]; then
    echo "⚠️  メモリ使用率90%超過"
    ((CRITICAL_ISSUES++))
fi

if systemctl list-units --failed | grep -q failed; then
    echo "⚠️  失敗したsystemdサービスあり"
    ((CRITICAL_ISSUES++))
fi

echo -e "\nクリティカルな問題: ${CRITICAL_ISSUES}件"
echo "詳細レポート: ${REPORT_DIR}"

# アラート送信(必要に応じて)
if [ ${CRITICAL_ISSUES} -gt 0 ]; then
    # Slackへの通知
    if [ -n "${SLACK_WEBHOOK_URL}" ]; then
        curl -X POST ${SLACK_WEBHOOK_URL} \
            -H 'Content-Type: application/json' \
            -d "{\"text\":\"⚠️ システム診断: ${CRITICAL_ISSUES}件のクリティカルな問題を検出\\nホスト: $(hostname)\\n詳細: ${REPORT_DIR}\"}"
    fi
fi

自動修復スクリプト

#!/bin/bash
# auto_remediation.sh - 一般的な問題の自動修復

LOG_FILE="/var/log/auto_remediation.log"
exec >> "${LOG_FILE}" 2>&1

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 1. ディスク容量の自動クリーンアップ
check_disk_space() {
    local threshold=85
    local usage=$(df -h / | awk 'NR==2 {print int($5)}')
    
    if [ ${usage} -gt ${threshold} ]; then
        log "ディスク使用率 ${usage}% - クリーンアップ開始"
        
        # 古いログファイルの削除
        find /var/log -type f -name "*.log" -mtime +30 -delete
        find /var/log -type f -name "*.gz" -mtime +7 -delete
        
        # パッケージキャッシュのクリア
        if command -v apt-get &>/dev/null; then
            apt-get clean
        elif command -v yum &>/dev/null; then
            yum clean all
        fi
        
        # Dockerの不要なイメージとコンテナを削除
        if command -v docker &>/dev/null; then
            docker system prune -af --volumes
        fi
        
        # journalログのローテーション
        journalctl --vacuum-time=7d
        
        log "クリーンアップ完了 - 新しい使用率: $(df -h / | awk 'NR==2 {print $5}')"
    fi
}

# 2. メモリ不足への対応
check_memory() {
    local available=$(free -m | awk '/^Mem:/ {print $7}')
    local total=$(free -m | awk '/^Mem:/ {print $2}')
    local usage=$((100 - (available * 100 / total)))
    
    if [ ${usage} -gt 90 ]; then
        log "メモリ使用率 ${usage}% - メモリ解放開始"
        
        # ページキャッシュのクリア
        sync && echo 1 > /proc/sys/vm/drop_caches
        
        # 高メモリ使用プロセスの特定
        ps aux --sort=-%mem | head -10 > /tmp/high_memory_processes.log
        
        log "メモリ解放完了"
    fi
}

# 3. サービスの自動再起動
check_services() {
    local critical_services=("nginx" "mysql" "postgresql" "redis" "docker")
    
    for service in "${critical_services[@]}"; do
        if systemctl list-unit-files | grep -q "^${service}.service"; then
            if ! systemctl is-active ${service} &>/dev/null; then
                log "サービス ${service} が停止 - 再起動試行"
                systemctl restart ${service}
                
                sleep 5
                if systemctl is-active ${service} &>/dev/null; then
                    log "サービス ${service} の再起動成功"
                else
                    log "エラー: サービス ${service} の再起動失敗"
                    # エスカレーション処理
                    send_alert "Critical: ${service} service failed to restart on $(hostname)"
                fi
            fi
        fi
    done
}

# 4. ネットワーク接続の修復
check_network() {
    if ! ping -c 1 8.8.8.8 &>/dev/null; then
        log "ネットワーク接続なし - 修復試行"
        
        # ネットワークサービスの再起動
        systemctl restart NetworkManager || systemctl restart networking
        
        # DNSキャッシュのクリア
        systemctl restart systemd-resolved || service nscd restart
        
        # デフォルトルートの確認と再設定
        if ! ip route | grep -q default; then
            # DHCPクライアントの再起動
            dhclient -r && dhclient
        fi
        
        sleep 5
        if ping -c 1 8.8.8.8 &>/dev/null; then
            log "ネットワーク接続の修復成功"
        else
            log "エラー: ネットワーク接続の修復失敗"
        fi
    fi
}

# アラート送信関数
send_alert() {
    local message="$1"
    
    # Slack通知
    if [ -n "${SLACK_WEBHOOK_URL}" ]; then
        curl -X POST ${SLACK_WEBHOOK_URL} \
            -H 'Content-Type: application/json' \
            -d "{\"text\":\"${message}\"}"
    fi
    
    # メール通知
    if command -v mail &>/dev/null; then
        echo "${message}" | mail -s "Auto Remediation Alert" admin@example.com
    fi
}

# メイン処理
main() {
    log "自動修復スクリプト開始"
    
    check_disk_space
    check_memory
    check_services
    check_network
    
    log "自動修復スクリプト完了"
}

# cronで定期実行される前提
main

予防的監視とアラート

# Prometheus アラートルール
groups:
  - name: infrastructure
    interval: 30s
    rules:
      # ディスク容量予測
      - alert: DiskWillFillIn4Hours
        expr: |
          predict_linear(
            node_filesystem_avail_bytes{mountpoint="/"}[1h], 
            4 * 3600
          ) < 0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "ディスクが4時間以内に満杯になる予測"
          description: "のディスク使用量が現在のペースで増加すると4時間以内に満杯になります"
      
      # メモリリーク検出
      - alert: PossibleMemoryLeak
        expr: |
          (
            rate(process_resident_memory_bytes[1h]) > 0
          ) and (
            process_resident_memory_bytes > 1e9
          )
        for: 30m
        labels:
          severity: warning
        annotations:
          summary: "メモリリークの可能性"
          description: "のメモリ使用量が継続的に増加しています"
      
      # サービス劣化検出
      - alert: ServiceDegradation
        expr: |
          (
            rate(http_requests_total{status=~"5.."}[5m]) 
            / 
            rate(http_requests_total[5m])
          ) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "サービス劣化検出"
          description: "のエラー率が5%を超えています"

まとめ

システム運用の自動化は、技術的進歩と運用規模の拡大に対応する必然的な進化である。init から systemd への移行、テキストログからバイナリログへの変換、そして Infrastructure as Code の普及は、いずれも複雑性の管理という共通課題への対処として理解できる。

重要なことは、自動化技術の選択において、組織の成熟度と運用体制を考慮することである。最先端の技術を導入することが目的ではなく、運用品質の向上と持続可能性の確保が目標である。段階的な導入と継続的な改善によって、真に効果的な自動化システムを構築することが可能となる。

次章では、これらの運用基盤の上で動作するセキュリティ実装と高可用性設計を探る。自動化されたシステムにおけるセキュリティ課題と、分散システムでの一貫性保証という、現代インフラストラクチャの核心的問題に取り組む。