第12章: 自動化とスクリプト作成
12.1 シェルスクリプトの高度な書き方(bash)
本節では、AlmaLinux 9 環境で堅牢かつ可読性の高い Bash スクリプトを作成するための手法を解説します。シェバンと厳格モードの導入、関数化によるモジュール設計、パラメータ展開や配列操作の活用、エラー処理とトラップの設定まで、公式ドキュメントと業界ベストプラクティスに基づき詳細に解説します。
12.1.1 シェバンと厳格モードの導入
実行ユーザー:任意のユーザー(環境に依存せず動作)
前提条件:Bash 5.x がインストール済み
期待結果:致命的エラーで即停止し、未定義変数参照やパイプ中の失敗を検出
#!/usr/bin/env bash # シェバン: 環境PATHから bash を呼び出す
set -eEuo pipefail # -e: エラーで即終了, -E: ERRトラップをサブシェルに継承, -u: 未定義変数エラー, -o pipefail: パイプ失敗検出
IFS=$'\n\t' # フィールド区切りを改行とタブのみに限定
: # Null コマンド: 条件式やサブシェル内での No-op
上記設定により、スクリプト実行中の思わぬ挙動や未定義変数の参照を防ぎ、予期しないバグを早期に検出できます。
12.1.2 関数化とスコープ管理
処理を関数として切り出し、local
変数でスコープを限定することで、再利用性と保守性を向上させます。関数は同一シェルコンテキスト内で実行され、サブシェルによるオーバーヘッドが発生しません。
実行ユーザー:root または sudo ユーザー
前提条件:rsync がインストール済み
期待結果:/backup 配下に web と home のバックアップが作成
function backup_dir() { # バックアップ処理を行う関数を定義
local src="$1" # 第1引数: バックアップ元ディレクトリ
local dest="$2" # 第2引数: バックアップ先ディレクトリ
rsync -av --delete "$src" "$dest" # rsync で同期: 古いファイルは削除
}
function main() { # メイン処理を関数化
backup_dir "/var/www" "/backup/web" # Webコンテンツをバックアップ
backup_dir "/home" "/backup/home" # ホームディレクトリをバックアップ
}
main # エントリポイントとして main 関数を実行
12.1.3 パラメータ展開と配列操作
Bash のパラメータ展開は、デフォルト値設定・未設定エラー通知・部分文字列取得・パターンマッチ削除などをスクリプト内で完結できます。Indexed 配列や連想配列を組み合わせ、複雑なデータ構造も簡潔に扱いましょう。
実行ユーザー:任意のユーザー
前提条件:CONFIG_PATH 環境変数が設定済み(テスト用)
期待結果:引数未指定時は default_user を、CONFIG_PATH 未定義時はエラー
# デフォルト値設定: 引数が空なら default_user
name="${1:-default_user}" # ${parameter:-word}
# 未設定エラー: CONFIG_PATH が未定義なら停止
config_path="${CONFIG_PATH:?CONFIG_PATH が未設定です}" # ${parameter:?word}
# 部分文字列取得: 6文字目から10文字を抽出
long_text="This is a long string example" # サンプル文字列
snippet="${long_text:5:10}" # ${parameter:offset:length}
# パターンによる削除: ファイル名から拡張子を除去
filename="archive.tar.gz" # サンプルファイル名
base="${filename%%.*}" # ${parameter%%pattern}
# Indexed 配列の宣言とループ
declare -a servers=("web01" "web02" "db01") # Indexed 配列初期化
for srv in "${servers[@]}"; do # 各要素を展開
echo "処理対象: $srv" # 要素を出力
done
# 連想配列の宣言と利用
declare -A users=(["alice"]="1001" ["bob"]="1002") # 連想配列初期化
for user in "${!users[@]}"; do # キーをループ
echo "$user の UID は ${users[$user]}" # 値を出力
done
12.1.4 エラー処理とトラップ
trap
を活用してエラー検出時に後始末処理やログ出力を行うことで、スクリプトの信頼性を高めます。ERR
トラップはデフォルトでサブシェルに継承されないため、set -E
を併用して関数やプロセス置換にも適用しましょう。
実行ユーザー:任意のユーザー
前提条件:なし
期待結果:エラー発生箇所の行番号を STDERR に出力しスクリプト停止
#!/usr/bin/env bash
set -eEuo pipefail # 厳格モードに加え、-E で ERR トラップを継承
on_error() { # エラーハンドラ関数を定義
local lineno="$1" # トラップから渡される行番号
echo "エラー発生: 行番号 ${lineno}" >&2 # STDERR に出力
}
trap 'on_error $LINENO' ERR # ERR 発生時に on_error を実行
false # テスト: 故意にエラーを発生
echo "この行は表示されません" # ERR によりここは実行されない
12.2 定期実行とイベント駆動(cron & systemd timers)
定期実行によって手作業の漏れや遅延を防ぎ、サービスの可用性と運用効率を向上できます。この節では、従来の cron
から最新の systemd timers
まで、AlmaLinux 9 環境での設定方法とベストプラクティスを詳細に解説します。
12.2.1 cron の基本とベストプラクティス
実行ユーザー:root または cron 権限ユーザー
前提条件:cron デーモンが稼働中
期待結果:スクリプトが指定どおりの日時に確実に実行される
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # cron 環境で必要なコマンドを検索可能に設定
HOME=/root # cron 実行時のホームディレクトリを指定
MAILTO="root@example.com" # 標準出力・標準エラーを root にメール通知
30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # 毎日 02:30 にバックアップ実行しログに追記
cron の環境変数は最小限なので、PATH
や HOME
、MAILTO
を明示的に設定します。
多重実行の防止
同じジョブが重複実行されないよう、flock
でロックをかけます。
0 * * * * /usr/bin/flock -n /var/lock/backup.lock \
/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # 毎時実行。ロック取得で重複を防止
flock
や PID ファイル)を併用してください。
12.2.2 systemd timers の高度な活用
実行ユーザー:systemd(root 権限)
前提条件:systemd バージョン 239 以上(RHEL 9 標準)
期待結果:タイマーとサービスユニットが連携して正確かつ可観測にジョブを実行
1).service ユニットの定義
[Unit] # Unit セクション開始
Description=Database Backup Service # サービスの説明
[Service] # Service セクション開始
Type=oneshot # 実行後すぐに終了するワンショットタイプ
ExecStart=/usr/local/bin/db_backup.sh # 実行するバックアップスクリプトを指定
2).timer ユニットの定義
OnCalendar
から RandomizedDelaySec
まで、主なオプションを解説します。
[Unit] # Unit セクション開始
Description=Daily DB Backup Timer # タイマーの説明
[Timer] # Timer セクション開始
OnCalendar=*-*-* 03:00:00 # 毎日 03:00 に実行
Persistent=true # システム停止中の遅延分を補完実行
AccuracySec=1min # 精度を 1 分に設定
RandomizedDelaySec=10min # 最大 10 分のランダム遅延を追加
Unit=db-backup.service # 対応するサービスユニットを指定
[Install] # インストールセクション開始
WantedBy=timers.target # timers.target に登録
3).timer の有効化・操作
# タイマーを有効化してブート時に自動起動設定
sudo systemctl enable db-backup.timer # ブート時に db-backup.timer を有効化
# タイマーを起動してスケジュール開始
sudo systemctl start db-backup.timer # 今すぐタイマーを起動
# タイマー一覧と次回実行時刻を確認
systemctl list-timers --all # 全タイマーのステータス一覧を表示
# ジョブの実行ログを確認
journalctl -u db-backup.service --since "24 hours ago" # 過去24時間分のログを出力
systemd timers
は cron
と異なり、依存関係の解決やログの一元管理、遅延補完など高度機能を標準で提供します。障害発生時も journalctl
で詳細に追跡可能です。
12.2.3 間隔実行とブート時実行:OnBootSec / OnUnitActiveSec
実行ユーザー:systemd(root 権限)
前提条件:periodic.sh スクリプトが /usr/local/bin に配置済み
期待結果:システム起動後や前回実行後の指定間隔でスクリプトが実行される
サービスユニット定義
[Unit] # Unit セクション開始
Description=Periodic Job Service # サービスの説明
[Service] # Service セクション開始
Type=oneshot # ワンショットタイプを指定
ExecStart=/usr/local/bin/periodic.sh # 実行するスクリプトを指定
タイマーユニット定義
[Unit] # Unit セクション開始
Description=Run periodic.sh at boot and every hour # タイマーの説明
[Timer] # Timer セクション開始
OnBootSec=10min # ブート後 10 分で実行
OnUnitActiveSec=1h # 前回実行から 1 時間後に実行
Unit=periodic-job.service # 対応するサービスユニットを指定
[Install] # インストールセクション開始
WantedBy=timers.target # timers.target に登録
有効化と確認
sudo systemctl enable periodic-job.timer # ブート時に有効化
sudo systemctl start periodic-job.timer # 今すぐタイマーを起動
systemctl list-timers # 全タイマーの状態を表示
12.2.4 一時ジョブと systemd-run の活用
systemd-run
を使うと、一時的なユニットファイルを作成せず即時ジョブを実行できます。
実行ユーザー:root
前提条件:systemd-run コマンドが利用可能
期待結果:指定した遅延後に一時的なユニットでコマンドが実行される
systemd-run --unit=tmp-sleep.timer --on-active=30s /bin/sleep 30 # 30 秒後に /bin/sleep 30 を一時ユニットで実行
12.3 構成管理ツール入門(Ansible vs Puppet vs Chef)
本節では、サーバー構成管理の代表的ツールである Ansible(エージェントレス)、Puppet(エージェント型/プル型)、Chef(エージェント型/プル型)を比較解説します。それぞれのアーキテクチャ、ワークフロー、コード例、運用上の注意点や拡張機能を、公式ドキュメントに基づいて解説します。
12.3.1 構成管理ツールの基本概念とアーキテクチャ
構成管理ツールは「宣言的(Declarative)」または「命令的(Imperative)」のモデルで動作し、対象ホストに desired state(望ましい状態)を適用します。
- エージェントレス(Push 型)
- Ansible: コントロールノードから SSH 接続で一方向に指示を送信
- メリット: エージェント不要、即時反映、設定がシンプル
- デメリット: 大規模並列実行時に制約あり
- エージェント型(Pull 型)
- Puppet / Chef: 各ノードに常駐エージェントが定期的にマスターへ接続して構成を取得
- メリット: 大規模スケールに強い、中央監視・報告機能が充実
- デメリット: 初期セットアップと証明書管理の手間、学習コスト
12.3.2 Ansible 実践ガイド
Ansible は Python ベースのエージェントレス構成管理ツールです。Playbook(YAML)、Inventory、Module、Role を組み合わせて管理します。公式サイト:Ansible Documentation
Inventory 定義
実行ユーザー:管理ノード上の任意ユーザー
前提条件:SSH キー認証が設定済み、Python3 インストール済み
期待結果:webservers, dbservers グループが登録される
[webservers] # webservers グループ定義
web01.example.com # ホスト1
web02.example.com # ホスト2
[dbservers] # dbservers グループ定義
db01.example.com # ホスト3
Playbook 基本構成
実行ユーザー:管理ノード上の任意ユーザー
前提条件:inventory ファイルが ./hosts に配置済み
期待結果:各 web ノードに Apache がインストール・起動される
---
- name: Install and configure Apache # Playbook の説明
hosts: webservers # 対象ホストグループ
become: true # sudo 権限で実行
vars: # 変数定義セクション
http_port: 80 # Apache の待ち受けポート
tasks: # タスク一覧
- name: Install httpd # Apache パッケージをインストール
dnf: # dnf モジュールを使用
name: httpd # パッケージ名
state: present # インストール済みを保証
- name: Deploy custom httpd.conf # 設定ファイルをテンプレートから配置
template: # template モジュールを使用
src: templates/httpd.conf.j2 # テンプレートファイルパス
dest: /etc/httpd/conf/httpd.conf # 配置先パス
owner: root # ファイル所有者
group: root # ファイルグループ
mode: '0644' # ファイル権限
- name: Ensure httpd is running # サービスが起動中か確認
systemd: # systemd モジュールを使用
name: httpd # サービス名
state: started # 起動済みを保証
enabled: true # ブート時起動を保証
Role 分割とディレクトリ構造
Ansible Role によって再利用性・可読性が向上します。以下は roles/web 配下の典型構造です。
roles/web/ # web ロールのルートディレクトリ
├── defaults/ # デフォルト変数
│ └── main.yml # default http_port: 80
├── files/ # 配布静的ファイル
│ └── index.html # サイトのトップページ
├── handlers/ # notify 用ハンドラ
│ └── main.yml # service: restart httpd
├── tasks/ # タスク定義
│ └── main.yml # Playbook tasks を分割
├── templates/ # Jinja2 テンプレート
│ └── httpd.conf.j2 # httpd.conf の雛形
├── vars/ # 変数
│ └── main.yml # role 固有変数
└── meta/ # 依存関係などメタ情報
└── main.yml
Vault と拡張機能
Ansible Vault で機密情報を暗号化・復号します。Ansible Galaxy からコミュニティ Role を取得可能。
実行ユーザー:管理ノードの任意ユーザー
前提条件:ansible-vault コマンドが利用可能
期待結果:暗号化された secrets.yml ファイルが生成される
ansible-vault create group_vars/all/secrets.yml # 新規に暗号化ファイルを作成
# ファイル内でパスワードや API キーを記述し保存
ansible-playbook site.yml --ask-vault-pass # 実行時にパスワード入力を促す
12.3.3 Puppet 入門
Puppet は Ruby ベースのエージェント/マスター型構成管理ツールです。Manifest (.pp)、Facter、Hiera、PuppetDB を組み合わせます。公式サイト:Puppet Documentation
基本的な Manifest
実行ユーザー:Puppet マスターまたは agent インストール済みノード
前提条件:puppetserver と puppet-agent がインストール済み
期待結果:クライアントノードに Apache がインストール・起動される
class apache::install { # apache::install クラス定義開始
package { 'httpd': # httpd パッケージリソース宣言
ensure => installed, # インストール済みを保証
}
service { 'httpd': # httpd サービスを管理
ensure => running, # 実行中を保証
enable => true, # ブート時起動を保証
require => Package['httpd'], # 依存関係: パッケージ後に起動
}
}
node default { # デフォルトノード定義
include apache::install # apache::install クラスを適用
}
Hiera による外部データ管理
Hiera で設定を階層化し、コードとデータを分離します。
# hiera.yaml: データソース設定
version: 5 # Hiera バージョン
defaults: # デフォルト設定
datadir: data # データディレクトリ
data_hash: yaml_data
hierarchy: # 階層構造定義
- name: "Node specific data"
path: "nodes/%{trusted.certname}.yaml"
- name: "Common data"
path: "common.yaml"
12.3.4 Chef 入門
Chef は Ruby DSL を用いるエージェント/サーバー型構成管理ツールです。Workstation、Chef Server、Chef Client、Cookbook、Knife の組み合わせが特徴。公式サイト:Chef Documentation
Cookbook の生成とレシピ定義
実行ユーザー:Chef ワークステーションのユーザー
前提条件:chef-client, knife がインストール済み
期待結果:cookbook/web_apache が作成され、default レシピが定義される
knife cookbook create web_apache # web_apache Cookbook を作成
# metadata.rb, recipes/default.rb などの雛形ファイルが生成される
default レシピの例
package, service, template リソースを使った基本構成。
package 'httpd' do # Apache パッケージをインストール
action :install # インストールアクション
end
template '/etc/httpd/conf/httpd.conf' do # 設定ファイル配置リソース
source 'httpd.conf.erb' # テンプレートファイル名
owner 'root' # 所有者を指定
group 'root' # グループを指定
mode '0644' # ファイル権限を指定
notifies :restart, 'service[httpd]', :immediately # 変更時に即時再起動
end
service 'httpd' do # Apache サービス管理リソース
action [:enable, :start] # ブート時有効化と起動を指定
end
12.3.5 ツール選定ガイドライン
以下を基準に自社環境に最適な構成管理ツールを選定してください。
- ノード台数: 小規模(10台未満)→Ansible、
大規模(数百台以上)→Puppet/Chef - 運用モデル: 即時変更反映→Ansible、
定期的チェック・レポート→Puppet/Chef - 学習コスト: 短期導入→Ansible、
豊富な機能→Puppet/Chef - 拡張性: コミュニティモジュール重視→Ansible Galaxy、
組織的ポリシー管理→Puppet Enterprise/Chef Automate
さらなる学習リソース
12.4 SSHキー管理とセキュリティ
SSHキー認証はパスワード認証に比べて強固なセキュリティを提供しますが、鍵の生成・配布・制御・運用を誤ると逆にリスクとなります。本節では、AlmaLinux 9 環境における SSH キー管理を以下のフェーズで徹底解説します。
- 12.4.1 パスフレーズ付き鍵の生成(Ed25519)
- 12.4.2 公開鍵の安全な配布と権限設定
- 12.4.3 authorized_keys による細かなアクセス制御
- 12.4.4 SSH 証明書を使った大規模認証基盤
- 12.4.5 大規模環境での鍵管理とローテーション
12.4.1 パスフレーズ付き鍵の生成(Ed25519)
実行ユーザー:任意のユーザー
前提条件:OpenSSH 8.2p1 以上がインストール済み
期待結果:安全な Ed25519 鍵ペアが生成され、秘密鍵にパスフレーズが設定される
ssh-keygen -t ed25519 -a 200 -f ~/.ssh/id_ed25519 -C "you@example.com" # -t: 鍵タイプを ed25519 に指定, -a: KDF ラウンド数を 200 に設定して耐ブルートフォース性を強化
# Enter passphrase: で安全なパスフレーズを入力
# Enter same passphrase again: で再入力し確認
12.4.2 公開鍵の安全な配布と権限設定
実行ユーザー:クライアント側ユーザー
前提条件:サーバー上で sshd が稼働中, ~/.ssh ディレクトリが存在しない場合は作成権限あり
期待結果:サーバーの ~/.ssh/authorized_keys
に公開鍵が追加され、適切なファイル権限が設定される
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com # 公開鍵を自動配布し ~/.ssh/authorized_keys に追加
# 手動配布例:
scp ~/.ssh/id_ed25519.pub user@server.example.com:~/temp_key.pub # 一時ファイルとして公開鍵を転送
ssh user@server.example.com 'mkdir -p ~/.ssh && chmod 700 ~/.ssh' # ~/.ssh を作成し、権限を 700 に設定
ssh user@server.example.com 'cat ~/temp_key.pub >> ~/.ssh/authorized_keys && rm ~/temp_key.pub' # 公開鍵を追記し一時ファイルを削除
ssh user@server.example.com 'chmod 600 ~/.ssh/authorized_keys' # authorized_keys の権限を 600 に設定
12.4.3 authorized_keys による細かなアクセス制御
実行ユーザー:サーバー上の対象ユーザー
前提条件:~/.ssh/authorized_keys が存在
期待結果:コマンド実行やポート/X11/エージェント転送を制限したキーエントリが追加される
echo 'command="internal-sftp -d /upload",no-port-forwarding,no-X11-forwarding,no-agent-forwarding \
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF… user@example.com' >> ~/.ssh/authorized_keys # 認証時に internal-sftp のみ許可し他の機能を無効化
12.4.4 SSH 証明書を使った大規模認証基盤
実行ユーザー:CA 管理者
前提条件:ssh-keygen がインストール済み, サーバーの /etc/ssh/sshd_config に書き込み権限あり
期待結果:CA 鍵ペアとユーザー証明書が生成され、sshd が CA を信頼するよう設定される
# CA 鍵ペアを生成(初回のみ)
ssh-keygen -f ~/.ssh/ca_key -t ed25519 -C "SSH CA key" # -f: CA 鍵ファイル名指定, -t: ed25519 鍵タイプ
# CA 公開鍵をサーバーに配置
scp ~/.ssh/ca_key.pub root@server.example.com:/etc/ssh/ca_key.pub # /etc/ssh/ca_key.pub にアップロード
ssh root@server.example.com 'echo "TrustedUserCAKeys /etc/ssh/ca_key.pub" >> /etc/ssh/sshd_config' # sshd_config に CA 公開鍵を追加
ssh root@server.example.com 'systemctl restart sshd' # sshd を再起動して設定を反映
# ユーザー公開鍵を CA で署名(証明書生成)
ssh-keygen -s ~/.ssh/ca_key -I user1 -n user1 -V +52w ~/.ssh/id_ed25519.pub # -s: CA 秘密鍵, -I: 証明書 ID, -n: ログイン許可プリンシパル, -V: 有効期間
# 証明書として ~/.ssh/id_ed25519-cert.pub が生成される
12.4.5 大規模環境での鍵管理とローテーション
多数ノードを統制する環境では、自動化と一元管理が必須です。以下の方法を組み合わせて運用してください。
- LDAP/AD と
AuthorizedKeysCommand
で動的に公開鍵を配布 - HashiCorp Vault の SSH シークレットエンジンでワンタイムキーを発行
- Ansible や Puppet/Chef で半年ごとに鍵をローテーション
- YubiKey/FIDO2 などハードウェアトークンで秘密鍵を保護
さらなる学習リソース
- ssh-keygen(1) — OpenSSH マニュアル
- sshd_config(5) — OpenSSH デーモン設定
- OpenSSH 公式ドキュメント
- HashiCorp Vault ドキュメント
12.5 学習のまとめ
本章では、AlmaLinux 9 環境における自動化技術を体系的に解説しました。以下のポイントを確実に理解・実践することで、運用の効率化と信頼性向上を同時に実現できます。
- 厳格モード(Strict Mode)の適用
#!/usr/bin/env bash
とset -eEuo pipefail
で致命的エラーを即検出し、IFS=$'\n\t'
で安全なフィールド分割を実現。 - 関数化とスコープ管理
処理ごとにfunction foo()
を定義し、local
変数でスコープを限定。モジュール設計により可読性・再利用性を向上。 - パラメータ展開と配列操作
${var:-default}
/${var:?error}
でデフォルト値や未設定検出を行い、Indexed 配列・連想配列で複雑データをネイティブに操作。 - エラー処理とトラップ
trap 'handler $LINENO' ERR
とset -E
でサブシェルまで ERR を捕捉。障害時に行番号やコマンド名をログ出力して後始末を自動化。 - 定期実行の選択肢
cron
はシンプル運用に、flock
で重複防止。systemd timers
はOnCalendar
・OnBootSec
・Persistent
など高度機能を標準提供。 - 構成管理ツールの特徴比較
エージェントレスの Ansible は SSH ベースで迅速導入、エージェント型の Puppet/Chef は証明書管理と常時チェックで大規模運用に強み。 - SSH キー管理とセキュリティ
Ed25519+高ラウンドのパスフレーズ付き鍵生成、authorized_keys
の強制コマンド・転送制限、証明書ベース認証や Vault 連携による動的キー発行で堅牢化。
💡 補足:本章で紹介した技術を組み合わせると、例えば「Ansible でデプロイしたスクリプトはすべて厳格モード適用」「systemd timers で定期実行」「証明書ベースの SSH でアクセスを制御」といった、運用の自動化・可観測性・セキュリティが一体となったエコシステムを構築できます。