こういった悩みはサーバーが10台を超えたあたりで多くのエンジニアが経験する。歴史あるシェルスクリプトを一夜でAnsibleに置き換えるのは現実的ではないが、かといってそのまま放置すれば負債は膨らみ続ける。
この記事では、シェルスクリプトからAnsibleへ移行する際の設計の考え方と変換パターンを解説する。RHEL 9系~Ubuntu 24.04 LTSを想定環境として、既存の自動化資産をどの順番でPlaybook化するかの判断基準から、冪等性を壊さない変換手順まで体系的にまとめた。
この記事のポイント
・移行は3フェーズに分けて段階的に進めれば本番を止めずに移行できる
・コマンド実行・条件分岐・ループ・ファイル操作の4パターンが変換の基本
・shell/commandモジュールは一時的な橋渡しと位置づけ専用モジュールへ移行する
・移行優先度は「複数台に繰り返し実行するスクリプト」から決めるのが鉄則
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜシェルスクリプトの自動化はAnsibleへの移行を必要とするのか
シェルスクリプトの問題は「動いている間は問題が見えない」ことにある。コマンドを順に並べたスクリプトは確かに動作するが、台数が増えるにつれて次の3つの問題が顕在化する。・冪等性がない:同じスクリプトを2回実行すると副作用が発生する(ユーザーが二重作成される、設定行が重複で追記されるなど)
・エラー処理が属人的:exit codeのチェックや途中失敗時の巻き戻し処理を書く人と書かない人で品質が大きく変わる
・環境差異の吸収が困難:RHEL系とDebian系でコマンドが違う、パスが違うといった条件分岐が肥大化していく
Ansibleはこれら3点を設計レベルで解決している。冪等性はモジュールの責務とし、エラー処理はblocks/rescueで構造化でき、環境差異はansible_os_familyによる条件分岐で吸収できる。シェルスクリプトからAnsibleへの移行は「書き直し」ではなく「設計レベルのアップグレード」と理解するとよい。
なお、Ansibleの基本的なPlaybook構造や実行モデルについてはAnsibleの入門ガイドで体系的にまとめている。移行前に全体像を把握しておくと変換作業がスムーズになる。
移行前に整理すべきシェルスクリプトの分類
すべてのシェルスクリプトを一律にAnsibleへ移行する必要はない。以下の2軸で整理し、移行優先度を決める。1. 実行頻度と台数で優先度を決める
移行投資対効果が高いのは「複数台に繰り返し実行するスクリプト」である。1回しか実行しない初期構築スクリプトや、単一サーバー専用のメンテナンス用スクリプトは優先度を下げてよい。・優先度【高】:10台以上に定期実行するスクリプト(パッチ適用、ユーザー配布、設定配布)
・優先度【中】:3台以上に不定期実行するスクリプト(ソフトウェアインストール、設定変更)
・優先度【低】:1台専用・1回実行の操作スクリプト(緊急対応、一時的なデータ移動)
2. 冪等性の有無でモジュール代替可否を判断する
スクリプトの各コマンドを読んで、Ansibleの既存モジュールで代替できるかを確認する。モジュールがあれば変換工数は低く、かつ冪等性が自動的に保証される。・モジュール代替可:ファイルコピー(copy/template)、パッケージインストール(dnf/apt)、サービス制御(systemd)、ユーザー作成(user)
・モジュール代替不可:独自バイナリの実行、古いAPIへのcurlコール、複雑なビジネスロジック
「モジュール代替不可」の処理はshell/commandモジュールで引き継ぐ形になるが、その場合はchanged_when/failed_whenによる結果制御を必ず設定する。冪等性が保証されないことをPlaybook上で明示するためだ。
シェルスクリプトをPlaybookに変換する4つの基本パターン
実際の変換作業は、シェルスクリプトのコードを「パターン別に読み替える」作業である。よく出てくる4パターンを解説する。1. コマンド実行をtaskに変換する
シェルスクリプトで `apt-get install -y nginx` と書いていた処理は、Ansibleではaptモジュールに置き換える。ポイントは「コマンドをそのまま貼り付けない」ことだ。変換前(シェルスクリプト):
# 変換前: シェルスクリプトの書き方 apt-get install -y nginx systemctl enable nginx systemctl start nginx
# 変換後: Ansibleのtask定義 - name: nginxをインストールする ansible.builtin.apt: name: nginx state: present - name: nginxを起動・自動起動に設定する ansible.builtin.systemd: name: nginx state: started enabled: true
2. 条件分岐(if文)をwhenとモジュールに置き換える
シェルスクリプトのifは「処理を実行してよいかのガード」として使われることが多い。Ansibleではwhen節とモジュール固有のstateで表現する。変換前(シェルスクリプト):
# 変換前: ifでOSを分岐 if [ -f /etc/redhat-release ]; then yum install -y httpd elif [ -f /etc/debian_version ]; then apt-get install -y apache2 fi
# 変換後: whenとansible_os_familyで分岐 - name: Apacheをインストールする(RHEL系) ansible.builtin.yum: name: httpd state: present when: ansible_os_family == "RedHat" - name: Apacheをインストールする(Debian系) ansible.builtin.apt: name: apache2 state: present when: ansible_os_family == "Debian"
3. ループ処理(for文)をloopに変換する
シェルスクリプトのforループは、Ansibleのloop句に変換できる。変換前(シェルスクリプト):
# 変換前: forでパッケージを繰り返しインストール for pkg in vim git curl wget; do dnf install -y $pkg done
# 変換後: loopでリスト処理 - name: 基本パッケージをインストールする ansible.builtin.dnf: name: "{{ item }}" state: present loop: - vim - git - curl - wget
4. ファイル操作をfileとcopyモジュールに移行する
`cp`・`mkdir`・`chmod`・`chown` といったファイル操作コマンドは、Ansibleのfileモジュールとcopyモジュールに対応している。変換前(シェルスクリプト):
# 変換前: ファイル操作コマンドの連続 mkdir -p /etc/myapp/conf.d cp templates/myapp.conf /etc/myapp/myapp.conf chmod 640 /etc/myapp/myapp.conf chown root:myapp /etc/myapp/myapp.conf
# 変換後: fileとcopyモジュールに集約 - name: 設定ディレクトリを作成する ansible.builtin.file: path: /etc/myapp/conf.d state: directory mode: '0755' - name: 設定ファイルを配置する ansible.builtin.copy: src: templates/myapp.conf dest: /etc/myapp/myapp.conf mode: '0640' owner: root group: myapp
移行を段階的に進める3フェーズのロードマップ
「全スクリプトを一斉にAnsibleへ」は失敗のもとである。段階的なロードマップを組むことで、本番サービスを止めずに資産を移行できる。フェーズ1:scriptモジュールで既存スクリプトを包む
最初のステップは変換ではなく「Ansibleで実行できる状態にする」ことだ。既存のシェルスクリプトをscriptモジュールから呼び出す形にするだけでも、複数台への一括実行とチェックモードの恩恵を受けられる。# フェーズ1: 既存スクリプトをそのままAnsibleで呼び出す - name: 既存の初期設定スクリプトを実行する ansible.builtin.script: cmd: /opt/scripts/init-server.sh changed_when: true
フェーズ2:モジュール代替可能な処理をPlaybookのtaskに変換する
フェーズ1で安定したら、スクリプトの中身を読んでモジュール代替可能な処理を1つずつPlaybookのtaskに変換していく。全部まとめて変換しようとせず、1スクリプトにつき1taskずつ置き換える「漸進的変換」を原則にする。変換したtaskはチェックモード(--check --diff)で本番サーバーへの影響を確認してから適用するとよい。変更差分がdiff形式で表示されるため、「実際に何が変わるか」を事前に把握できる。
フェーズ3:roleに分割してチームで再利用できる構造へ
スクリプト群の変換が進んだら、関連するtaskをroleとして整理する。例えば「Webサーバー構築」スクリプトがAnsibleのtask集になったなら、それを `roles/webserver/tasks/main.yml` に移動する。roleへの分割がチームでの再利用性と独立テスト(Molecule)の基盤になる。フェーズ1からフェーズ3への移行に要する期間は規模によるが、優先度【高】のスクリプト10本を変換するだけでも運用の安定感は大きく変わる。「完全移行」を目標にせず「徐々に置き換える」という姿勢で進めると継続しやすい。
よくある移行失敗パターンと対処法
移行プロジェクトでよく見かける失敗を3つ挙げる。失敗1:shell/commandモジュールをそのまま使い続ける
フェーズ1で止まってしまい、shell/commandモジュールで既存スクリプトを呼び出すだけのPlaybookになってしまうケースがある。これだと冪等性が保証されず、本番で二度実行したときに意図しない副作用が発生するリスクが残る。対処法はchanged_when/failed_whenを設定して、少なくとも「冪等でないことを明示する」ところから始めることだ。最終的には専用モジュールへの移行を目指す。
失敗2:コントロールノードのパスを前提にPlaybookを書く
シェルスクリプトの書き方に引きずられて `copy: src=/absolute/path/on/controller` のように絶対パスを書いてしまうケースがある。このパスはコントロールノード上のパスであり、AWXやCI環境で実行したときに失敗する。対処法はsrcに相対パス(roleのfilesディレクトリ相対)を使い、絶対パスはdestのみに限定するルールをチームで設けることだ。
失敗3:1つのPlaybookにすべての処理を詰め込む
シェルスクリプトの「全手順を1ファイルに」という書き方をPlaybookでもやってしまうケースがある。数百行のsite.ymlが生まれ、タグも整理されないまま肥大化すると、部分実行も再利用もできなくなる。対処法はフェーズ3のrole分割を早めに実施することと、タグ設計で実行範囲を制御できる構造を最初から意識することだ。
本記事のまとめ
シェルスクリプトからAnsibleへの移行は、段階を踏めば本番を止めずに進められる。| 移行フェーズ | 内容 | 得られるメリット |
|---|---|---|
| フェーズ1 | scriptモジュールで既存スクリプトを包む | 並列実行・実行履歴・チェックモード |
| フェーズ2 | モジュール代替可能な処理をtaskに変換 | 冪等性保証・変更差分の可視化 |
| フェーズ3 | 関連taskをroleに整理 | チーム再利用・独立テスト・CI連携 |
・コマンド実行・条件分岐・ループ・ファイル操作の4変換パターンを基本とする
・shell/commandモジュールは一時的な橋渡しと位置づけ、専用モジュールへ移行を目指す
・フェーズ3のrole分割まで進めると、チームでの再利用と自動テストが実現できる
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:AnsibleでSSL証明書の更新と配布を自動化する設計入門|community.cryptoモジュールで期限監視から鍵管理まで
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます