Ansibleのcopy・fileモジュールでファイル管理を自動化する方法|バックアップ・権限設定・シンボリックリンクの実践パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > Ansibleのcopy・fileモジュールでファイル管理を自動化する方法|バックアップ・権限設定・シンボリックリンクの実践パターン
「サーバーが増えるたびに、設定ファイルをsftpで手動アップロードしている。バックアップを取り忘れて上書きしてしまう。パーミッションを間違えてApacheが起動しない。」
こういった手作業のミスは、サーバーが2台でも10台でも確実に起きる。とくに同じ設定ファイルを複数のサーバーへ展開する作業は、コピーミス・権限ミス・バックアップ漏れの温床だ。

この記事では、Ansibleのcopyモジュールとfileモジュールを使って、設定ファイルの配布・権限設定・バックアップを冪等に自動化する方法を解説する。RHEL 9.4 / Rocky Linux 9環境で動作確認済み。基本的なPlaybookの構造は理解している前提で、実務で即使える設計パターンを中心に説明する。

この記事のポイント

・copyモジュールはコントロールノードのファイルをリモートサーバーへ冪等に配布する
・backup: yesを指定すると上書き前に元ファイルが自動でバックアップされる
・fileモジュールはstate: directory/link/absentでディレクトリ・リンク・削除を冪等管理できる
・fetchモジュールでリモートの設定ファイルをコントロールノードへ一括回収できる


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

Ansibleのファイル操作で発生しがちな問題

手作業でサーバーに設定ファイルを配布する場合、以下の問題が繰り返し発生する。

・古いファイルをバックアップせず上書きしてしまい、ロールバックできなくなる
chmodchownを忘れてサービスが起動しない
・10台のうち1台だけ古い設定が残り、動作が異なる
・どのサーバーに何のバージョンが入っているか追跡できない

Ansibleのcopyモジュールとfileモジュールを正しく使えば、これらを一括で解消できる。いずれも冪等性(何度実行しても結果が同じになる性質)を持っているため、意図しない二重変更が発生しない。

copyモジュールで設定ファイルを複数サーバーへ配布する

1. copyモジュールの基本構文

copyモジュールは、コントロールノード(Ansibleを実行するサーバー)側のファイルを、リモートホスト側の指定パスへコピーする。

# files/postfix/main.cf を複数サーバーの /etc/postfix/main.cf へ配布 - name: postfix main.cf を配布する copy: src: files/postfix/main.cf dest: /etc/postfix/main.cf owner: root group: root mode: '0644'

主なオプションは次のとおり。

src:コントロールノード側のファイルパス(Playbookからの相対パスまたは絶対パス)
dest:リモートホスト側の配置先パス
owner / group:コピー後のファイル所有者とグループ
mode:コピー後のパーミッション('0644'のようにクォートした文字列で指定する)
content:srcの代わりに直接ファイル内容を文字列で指定できる(短い設定ファイルに便利)

実行すると以下のように出力される。ファイルに差分がある場合はchanged、すでに同じ内容ならok(変更なし)になる。

$ ansible-playbook -i hosts.yaml site.yml TASK [postfix main.cf を配布する] **** changed: [192.168.10.11] changed: [192.168.10.12] ok: [192.168.10.13] # すでに同じ内容のため変更なし PLAY RECAP ***** 192.168.10.11 : ok=2 changed=1 unreachable=0 failed=0 192.168.10.12 : ok=2 changed=1 unreachable=0 failed=0 192.168.10.13 : ok=1 changed=0 unreachable=0 failed=0

192.168.10.13はすでに最新の設定が配布済みだったため変更されていない。これが冪等性の動作だ。

2. backup: yesで元ファイルを上書き前に自動退避する

本番サーバーへ設定ファイルを配布する場合、上書き前に元ファイルをバックアップしておきたい。backup: yesを加えると、変更が発生した場合に自動でバックアップファイルが生成される。

- name: httpd.conf を配布する(上書き前に自動バックアップ) copy: src: files/apache/httpd.conf dest: /etc/httpd/conf/httpd.conf owner: root group: root mode: '0644' backup: yes

backup: yesを指定すると、変更が発生した場合に元のファイルが以下のような名前で同じディレクトリに退避される。

# バックアップ後の確認 $ ls -la /etc/httpd/conf/ -rw-r--r-- 1 root root 11764 8月 10 09:15 httpd.conf -rw-r--r-- 1 root root 11502 8月 8 14:30 httpd.conf.68494.2026-08-10@09:15:22~

ファイル名には「元のファイル名 + プロセスID + タイムスタンプ」が付与される。障害発生時はcpコマンドでこのバックアップを元に戻せる。

3. ディレクトリをまるごと配布する(srcのスラッシュに注意)

srcにディレクトリを指定する場合、末尾のスラッシュ有無で動作が変わる。

# スラッシュなし: conf/ ディレクトリごと dest 配下にコピーされる # 結果: /etc/nginx/conf/nginx.conf のような形になる src: files/nginx/conf dest: /etc/nginx/ # スラッシュあり: conf/ の中身だけ dest に展開される # 結果: /etc/nginx/nginx.conf のような形になる src: files/nginx/conf/ dest: /etc/nginx/

この挙動はrsyncコマンドと同じルールだ。「ディレクトリの中身だけを配置したい」場合はスラッシュあり、「ディレクトリ自体を配置したい」場合はスラッシュなし、と覚えておく。
copyモジュールを使ったファイル配布の設計を実務で身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

fileモジュールでパーミッション・ディレクトリ・シンボリックリンクを一括管理する

fileモジュールは、ファイルやディレクトリの「属性」(パーミッション・所有者・グループ・シンボリックリンク・存在有無)を冪等に管理するモジュールだ。copyと組み合わせることで、「ファイルを配布して、権限を正しく設定する」という一連の作業をPlaybookで完結させられる。

1. fileモジュールの基本構文

# 既存ファイルのパーミッションと所有者を設定する - name: /var/log/app のパーミッションを設定する file: path: /var/log/app/access.log owner: nginx group: nginx mode: '0640'

stateパラメータを指定しない場合、対象パスの現在の状態(ファイル・ディレクトリ・リンク)を変更せず、属性だけを変更する。

2. state: directoryでディレクトリを冪等に作成する

state: directoryを指定すると、ディレクトリが存在しない場合は作成し、すでに存在する場合は属性のみ変更する。mkdir -p相当の動作だ。

# アプリケーションのログディレクトリを作成する - name: ログディレクトリを作成する file: path: /var/log/myapp state: directory owner: appuser group: appuser mode: '0755' # 配布前にディレクトリを先に作成するパターン(依存関係が明確になる) - name: nginx設定ディレクトリを用意する file: path: /etc/nginx/conf.d state: directory owner: root group: root mode: '0755' - name: nginx仮想ホスト設定を配布する copy: src: files/nginx/vhost.conf dest: /etc/nginx/conf.d/vhost.conf owner: root group: root mode: '0644'

fileタスクを先に置くことで、「配布先ディレクトリがない」エラーを防げる。

3. state: linkでシンボリックリンクを管理する

state: linkを指定すると、srcからpath(またはdest)へのシンボリックリンクを作成する。

# /etc/nginx/sites-enabled/default -> /etc/nginx/sites-available/default - name: Nginx仮想ホストを有効化する(シンボリックリンク) file: src: /etc/nginx/sites-available/default dest: /etc/nginx/sites-enabled/default state: link owner: root group: root

リンクがすでに正しく存在すればok、存在しなければchanged(リンク作成)になる。

4. state: absentでファイルを冪等に削除する

state: absentを指定すると、対象パスが存在する場合に削除する。存在しない場合は何もしない(冪等)。

# 不要な設定ファイルを削除する - name: デフォルトのNginx設定を無効化する file: path: /etc/nginx/sites-enabled/default state: absent # 古いバックアップファイルを削除する - name: 旧バージョンの設定ファイルを除去する file: path: /etc/httpd/conf/httpd.conf.old state: absent

fetchモジュールでリモートサーバーの設定を手元に回収する

fetchモジュールはcopyの逆方向だ。リモートサーバー側のファイルをコントロールノード側へ回収する。本番サーバーの現在の設定をまとめてバックアップしたい場合に使う。

1. fetchモジュールの基本構文とflat: trueの使い分け

# リモートの /etc/nginx/nginx.conf をコントロールノードへ回収する(デフォルト) # 保存先: ./backup/192.168.10.11/etc/nginx/nginx.conf - name: Nginx設定を回収する fetch: src: /etc/nginx/nginx.conf dest: ./backup/ flat: no # デフォルト: ホスト名ディレクトリ配下に保存される # flat: yes の場合 # 保存先: ./backup/nginx.conf(ホスト名ディレクトリは作成されない) # 複数ホストから回収すると上書きされるため注意 - name: Nginx設定を回収する(flat形式) fetch: src: /etc/nginx/nginx.conf dest: ./backup/nginx.conf flat: yes

複数ホストから回収する場合はflat: no(デフォルト)にしておくことを推奨する。flat: yesは単一ホストからの回収か、ループでホストごとにファイル名を変えるケースで使う。

2. 複数ホストから設定ファイルを一括回収する実践パターン

# 全Webサーバーから現在の設定を一括回収してバックアップする - name: 本番Webサーバーの設定を回収する fetch: src: /etc/httpd/conf/httpd.conf dest: ./backup/{{ inventory_hostname }}/httpd.conf flat: yes

$ ansible-playbook -i hosts.yaml fetch_configs.yml TASK [本番Webサーバーの設定を回収する] ***** changed: [web01.example.internal] changed: [web02.example.internal] changed: [web03.example.internal] # 回収後のディレクトリ構造 $ tree backup/ backup/ ├── web01.example.internal │ └── httpd.conf ├── web02.example.internal │ └── httpd.conf └── web03.example.internal └── httpd.conf

この構造ならdiff backup/web01.example.internal/httpd.conf backup/web02.example.internal/httpd.confで差分を確認できる。

実務パターン:Postfix設定の一括配布と権限設定

実際の現場ではcopyとfileを組み合わせて1つのroleまたはPlaybookにまとめることが多い。Postfix設定の配布を例にすると以下のようになる。

Postfixの基本設定については「Postfix の基本設定の解説」を参照してほしい。

--- - name: Postfix設定の配布と権限設定 hosts: mail_servers become: yes tasks: # 1. 配布前のバックアップ - name: 現在のmain.cfを回収する fetch: src: /etc/postfix/main.cf dest: ./backup/{{ inventory_hostname }}/main.cf flat: yes # 2. 設定ファイルを配布(上書き前に自動バックアップ) - name: main.cf を配布する copy: src: files/postfix/main.cf dest: /etc/postfix/main.cf owner: root group: postfix mode: '0640' backup: yes notify: postfix を再起動する # 3. キューディレクトリのパーミッション確認 - name: Postfixキューディレクトリの権限を設定する file: path: /var/spool/postfix state: directory owner: root group: postfix mode: '0755' handlers: - name: postfix を再起動する service: name: postfix state: restarted

$ ansible-playbook -i hosts.yaml postfix_deploy.yml --diff TASK [main.cf を配布する] ***** --- before: /etc/postfix/main.cf +++ after: /home/ansible/.ansible/tmp/ansible-tmp-.../main.cf @@ -71,6 +71,7 @@ myhostname = mail.example.internal +mynetworks = 192.168.10.0/24 changed: [mail01.example.internal] RUNNING HANDLER [postfix を再起動する] ***** changed: [mail01.example.internal] PLAY RECAP ***** mail01.example.internal : ok=4 changed=2 unreachable=0 failed=0

--diffオプションを付けると変更内容がdiff形式で表示されるため、適用前に変更箇所を確認できる。

よくあるエラーと対処法

「Permission denied」が出た時の確認手順

fatal: [192.168.10.11]: FAILED! => { "msg": "Destination /etc/nginx/nginx.conf not writable" }

このエラーが出た場合、become: yesの設定を確認する。Playbook全体またはタスク単位でbecomeを指定していないと、接続ユーザー権限でのコピーとなるため権限エラーになる。

# Playbook全体にbecome: yesを付ける - name: Nginx設定配布 hosts: web become: yes # これを追加 tasks: - name: nginx.conf を配布する copy: src: files/nginx/nginx.conf dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644'

srcのスラッシュ有無でディレクトリ配布の挙動が変わる罠

# スラッシュなし: files/conf ディレクトリ自体が /etc/nginx/conf として作成される # 結果: /etc/nginx/conf/nginx.conf - copy: src: files/conf dest: /etc/nginx/ # スラッシュあり: files/conf/ の中身が /etc/nginx/ 配下に展開される # 結果: /etc/nginx/nginx.conf - copy: src: files/conf/ dest: /etc/nginx/

意図しないディレクトリ構造が作成されてサービスが設定ファイルを見つけられない、という事故はよく起きる。ディレクトリコピーをするときは--diff--checkを組み合わせて実行前に確認することを習慣にしてほしい。

# 本番適用前のdry-run確認 $ ansible-playbook -i hosts.yaml site.yml --check --diff

本記事のまとめ

やりたいこと モジュール・設定
設定ファイルを複数サーバーへ配布する copy: src: ... dest: ...
上書き前に元ファイルを自動退避する copy: backup: yes
パーミッション・所有者を設定する copy: owner: ... group: ... mode: ...
ディレクトリを冪等に作成する file: state: directory
シンボリックリンクを作成する file: state: link src: ... dest: ...
ファイルを冪等に削除する file: state: absent
リモートの設定ファイルを手元に回収する fetch: src: ... dest: ...
ディレクトリ全体を配布する copy: src: .../(スラッシュあり)dest: ...
copyでファイルを配布し、fileで属性を管理し、fetchでバックアップを回収する。この3つの組み合わせで、手作業によるミスをほぼゼロにできる。まずは自分のチームで最もよく使う設定ファイル(Nginx、Postfix、Samba等)の1種類から始めて、Playbookに落とし込む習慣をつけてほしい。

Samba設定ファイルの配布例については「Samba バージョン確認の方法」も合わせて参照してほしい。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

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

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

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

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

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

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

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

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

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。