シェルスクリプトからAnsibleへ段階的に移行する設計入門|既存の自動化資産をPlaybookに変換する手順と判断基準

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Ansible > シェルスクリプトからAnsibleへ段階的に移行する設計入門|既存の自動化資産をPlaybookに変換する手順と判断基準
「サーバーが増えてシェルスクリプトが手に負えなくなった。でもAnsibleへの移行は大変そうで、どこから手をつければいいのかわからない」

こういった悩みはサーバーが10台を超えたあたりで多くのエンジニアが経験する。歴史あるシェルスクリプトを一夜でAnsibleに置き換えるのは現実的ではないが、かといってそのまま放置すれば負債は膨らみ続ける。

この記事では、シェルスクリプトからAnsibleへ移行する際の設計の考え方と変換パターンを解説する。RHEL 9系~Ubuntu 24.04 LTSを想定環境として、既存の自動化資産をどの順番でPlaybook化するかの判断基準から、冪等性を壊さない変換手順まで体系的にまとめた。

この記事のポイント

・移行は3フェーズに分けて段階的に進めれば本番を止めずに移行できる
・コマンド実行・条件分岐・ループ・ファイル操作の4パターンが変換の基本
・shell/commandモジュールは一時的な橋渡しと位置づけ専用モジュールへ移行する
・移行優先度は「複数台に繰り返し実行するスクリプト」から決めるのが鉄則


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜシェルスクリプトの自動化は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 Playbook):

# 変換後: Ansibleのtask定義 - name: nginxをインストールする ansible.builtin.apt: name: nginx state: present - name: nginxを起動・自動起動に設定する ansible.builtin.systemd: name: nginx state: started enabled: true

aptモジュールは `state: present` によって「すでにインストール済みなら何もしない」という冪等性を自動的に持つ。シェルスクリプトでは `dpkg -l nginx | grep -q ii || apt-get install -y nginx` のように書かなければならなかった処理が、モジュール1行で同等の安全性になる。

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

変換後(Ansible Playbook):

# 変換後: 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"

`ansible_os_family` はAnsibleのfact(gather_factsで収集するホスト情報)であり、実行時に自動で設定される。「RHELかDebianかを自分で判定するロジック」を書く必要がなくなり、クロスプラットフォーム対応のPlaybookが格段に書きやすくなる。

3. ループ処理(for文)をloopに変換する

シェルスクリプトのforループは、Ansibleのloop句に変換できる。

変換前(シェルスクリプト):

# 変換前: forでパッケージを繰り返しインストール for pkg in vim git curl wget; do dnf install -y $pkg done

変換後(Ansible Playbook):

# 変換後: loopでリスト処理 - name: 基本パッケージをインストールする ansible.builtin.dnf: name: "{{ item }}" state: present loop: - vim - git - curl - wget

なお、dnfモジュールはnameにリストを直接渡すことができる。forからloopへ機械的に変換して動作させた後、Ansibleらしい書き方に最適化するとよい。loop変換の詳細なパターンはAnsibleの公式ドキュメントのloopセクションが参考になる。

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

変換後(Ansible Playbook):

# 変換後: 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

copyモジュールはファイルの内容・パーミッション・所有者をまとめて管理できる。シェルスクリプトで4行必要だったコードが2つのtaskになり、チェックモードでも変更内容を事前確認できるようになる。

移行を段階的に進める3フェーズのロードマップ

「全スクリプトを一斉にAnsibleへ」は失敗のもとである。段階的なロードマップを組むことで、本番サービスを止めずに資産を移行できる。

フェーズ1:scriptモジュールで既存スクリプトを包む

最初のステップは変換ではなく「Ansibleで実行できる状態にする」ことだ。既存のシェルスクリプトをscriptモジュールから呼び出す形にするだけでも、複数台への一括実行とチェックモードの恩恵を受けられる。

# フェーズ1: 既存スクリプトをそのままAnsibleで呼び出す - name: 既存の初期設定スクリプトを実行する ansible.builtin.script: cmd: /opt/scripts/init-server.sh changed_when: true

この段階ではまだシェルスクリプトの冪等性問題は解決していないが、「実行履歴の記録」「並列実行の制御」「実行結果の一元管理」といったAnsibleの管理メリットはすぐに得られる。現場で運用しながら次フェーズに移行する時間を稼ぐためのステップだ。

フェーズ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分割まで進めると、チームでの再利用と自動テストが実現できる
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Ansibleを使ったサーバー自動化の設計から実践まで学べる講座を用意しています。シェルスクリプト管理の限界を感じ始めたエンジニアに特に役立つ内容です。>> 詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。