T-CREATOR

Ansible 変数設計:defaults → vars → extra_vars を使い分ける設計術

Ansible 変数設計:defaults → vars → extra_vars を使い分ける設計術

Ansible でインフラをコード化する際、変数の設計は成功の鍵を握ります。適切な変数設計ができていないと、環境ごとの設定変更が困難になったり、想定外の値で実行されてしまったりと、運用上の問題が発生してしまうでしょう。

本記事では、Ansible の代表的な変数定義方法である defaultsvarsextra_vars の使い分けを中心に、実践的な変数設計術をご紹介します。変数の優先順位を理解し、それぞれの特性を活かした設計を行うことで、柔軟で保守性の高い Ansible プロジェクトを実現できますよ。

背景

Ansible における変数の役割

Ansible は YAML 形式で記述された Playbook を実行し、サーバーの構成管理や自動化を行うツールです。変数を活用することで、同じ Playbook を異なる環境や条件で再利用できるようになります。

例えば、開発環境と本番環境でポート番号やドメイン名が異なる場合でも、変数を使えば Playbook 本体を変更せずに対応できるでしょう。これにより、コードの重複を避け、メンテナンス性を大幅に向上させられます。

変数定義の多様な方法

Ansible では変数を定義する方法が非常に多く用意されています。代表的なものだけでも以下のようなものがあります。

#定義場所用途
1role​/​defaults​/​main.ymlRole のデフォルト値
2role​/​vars​/​main.ymlRole 固有の変数
3group_vars​/​グループ単位の変数
4host_vars​/​ホスト単位の変数
5--extra-varsコマンドライン引数
6set_factタスク実行時の動的変数

これらの変数定義方法には明確な優先順位が存在し、理解せずに使うと予期しない動作を引き起こす原因となってしまいます。

以下の図は、Ansible における変数定義の主要な場所と、それらがどのように Playbook 実行に影響するかを示しています。

mermaidflowchart TB
    playbook["Playbook 実行"]
    playbook --> role["Role 読み込み"]
    role --> defaults["defaults/main.yml<br/>(デフォルト値)"]
    role --> vars["vars/main.yml<br/>(Role 固有値)"]
    playbook --> inventory["Inventory 読み込み"]
    inventory --> group_vars["group_vars/<br/>(グループ変数)"]
    inventory --> host_vars["host_vars/<br/>(ホスト変数)"]
    playbook --> cli["コマンドライン"]
    cli --> extra_vars["--extra-vars<br/>(実行時指定)"]

    defaults --> merge["変数マージ<br/>優先順位適用"]
    vars --> merge
    group_vars --> merge
    host_vars --> merge
    extra_vars --> merge
    merge --> execute["タスク実行"]

この図から分かるように、変数は複数の場所から読み込まれ、最終的にマージされて実行されます。

課題

変数の優先順位による混乱

Ansible の変数には 22 段階もの優先順位が存在します。この優先順位を理解していないと、「なぜこの値が使われているのか」が分からず、デバッグに多大な時間を費やすことになるでしょう。

特に、varsdefaults の違いを理解せずに使っている場合、外部から値を上書きしようとしても反映されないという問題が頻繁に発生します。

yaml# role/defaults/main.yml
web_port: 80

# role/vars/main.yml
web_port: 8080  # こちらが優先される

上記のように、vars に定義された値は defaults よりも優先されるため、defaults の値は無視されてしまいます。

環境ごとの設定管理の難しさ

開発、ステージング、本番といった複数環境を管理する際、環境ごとに異なる設定値を適切に管理する必要があります。しかし、変数の定義場所を適切に選ばないと、以下のような問題が発生するでしょう。

#問題影響
1環境ごとの値が分散して管理しづらい変更漏れ、設定ミス
2デフォルト値が上書きできない柔軟性の欠如
3実行時に値を変更できない運用の硬直化
4どの変数がどこで定義されているか不明保守性の低下

以下の図は、変数の優先順位とそれによる上書き関係を示しています。

mermaidflowchart LR
    defaults["defaults<br/>優先度: 低"]
    group_vars["group_vars<br/>優先度: 中"]
    host_vars["host_vars<br/>優先度: 中高"]
    role_vars["vars<br/>優先度: 高"]
    extra["extra_vars<br/>優先度: 最高"]

    defaults -->|上書き| group_vars
    group_vars -->|上書き| host_vars
    host_vars -->|上書き| role_vars
    role_vars -->|上書き| extra
    extra -->|最終値| result["実行時の値"]

チーム開発での一貫性の欠如

複数人で Ansible のコードを書く場合、変数定義のルールが統一されていないと、可読性や保守性が大きく低下します。ある人は defaults を使い、別の人は vars を使うといった状況では、コードレビューも困難になるでしょう。

明確な設計方針がないと、プロジェクトが大きくなるにつれて変数の管理が複雑化し、最終的には誰も全体像を把握できない状態に陥ってしまいます。

解決策

変数の優先順位を理解する

まず、Ansible の変数優先順位の基本を押さえましょう。完全な優先順位は 22 段階ありますが、実務で重要なのは以下の順序です(下に行くほど優先度が高い)。

#優先度定義場所説明
1最低role​/​defaults​/​main.ymlRole のデフォルト値
2inventory file または scriptInventory の変数
3中低group_vars​/​all全グループ共通変数
4group_vars​/​*グループ別変数
5中高host_vars​/​*ホスト別変数
6role​/​vars​/​main.ymlRole 固有の変数
7最高--extra-varsコマンドライン引数

この優先順位を踏まえた設計を行うことで、柔軟性と予測可能性を両立できます。

defaults の使い方:上書き可能なデフォルト値

defaults は最も優先度が低い変数定義方法です。この特性を活かして、上書きされることを前提としたデフォルト値を定義しましょう。

defaults の配置場所

yaml# roles/webserver/defaults/main.yml
---
# Web サーバーのデフォルト設定
# 環境や要件に応じて上書き可能

defaults に定義すべき変数

yaml# roles/webserver/defaults/main.yml
---
# ポート番号(開発環境では 8080、本番では 80 など)
nginx_port: 80

# ワーカープロセス数(サーバースペックに応じて変更)
nginx_worker_processes: auto

# ログレベル(開発では debug、本番では warn など)
nginx_log_level: warn

# タイムアウト設定(要件に応じて調整)
nginx_timeout: 60

上記のように、環境やサーバーの性質によって変わる可能性がある値を defaults に定義します。こうすることで、標準的な設定はそのまま使いつつ、必要に応じて group_varshost_vars で上書きできるようになりますね。

defaults のメリット

yaml# roles/webserver/defaults/main.yml
# デフォルトは一般的な設定値
nginx_max_body_size: 1m
nginx_keepalive_timeout: 65
# group_vars/production.yml で本番環境用に上書き
# nginx_max_body_size: 100m
# nginx_keepalive_timeout: 120

このパターンでは、本番環境だけ大きなファイルアップロードに対応する必要がある場合、group_vars で該当する変数だけを上書きすれば良いでしょう。

vars の使い方:絶対に変更されない固定値

vars は優先度が高いため、外部から上書きされたくない固定値を定義する場所として使います。

vars の配置場所

yaml# roles/webserver/vars/main.yml
---
# Role の内部ロジックで使用する固定値
# 外部から変更されるべきではない

vars に定義すべき変数

yaml# roles/webserver/vars/main.yml
---
# パッケージ名(OS によって異なる)
nginx_package_name: nginx

# 設定ファイルのパス(OS の標準位置)
nginx_config_path: /etc/nginx/nginx.conf
nginx_site_available: /etc/nginx/sites-available
nginx_site_enabled: /etc/nginx/sites-enabled

# サービス名
nginx_service_name: nginx

これらの値は Role の実装に依存する固定値なので、外部から変更されると正しく動作しなくなる可能性があります。そのため、優先度の高い vars に定義するのが適切でしょう。

OS ごとの条件分岐

yaml# roles/webserver/vars/RedHat.yml
---
# RedHat 系 OS の固定値
nginx_package_name: nginx
nginx_user: nginx
nginx_group: nginx
yaml# roles/webserver/vars/Debian.yml
---
# Debian 系 OS の固定値
nginx_package_name: nginx
nginx_user: www-data
nginx_group: www-data
yaml# roles/webserver/tasks/main.yml
---
# OS に応じた変数ファイルを読み込む
- name: OS 固有の変数を読み込む
  include_vars: '{{ ansible_os_family }}.yml'

このように、OS ごとに異なる固定値を vars で管理し、実行時に適切なファイルを読み込むパターンも有効です。

extra_vars の使い方:実行時の動的な上書き

extra_vars は最も優先度が高く、実行時に動的に値を変更したい場合に使用します。

コマンドラインでの指定

bash# 開発環境で実行(ポート 8080 を使用)
ansible-playbook site.yml \
  --extra-vars "nginx_port=8080"
bash# 本番環境で実行(ポート 80、ワーカー数を 4 に設定)
ansible-playbook site.yml \
  --extra-vars "nginx_port=80 nginx_worker_processes=4"

上記のように、同じ Playbook を異なるパラメータで実行できるのが extra_vars の強みです。

JSON ファイルでの指定

json# config/production.json
{
  "nginx_port": 80,
  "nginx_worker_processes": 4,
  "nginx_max_body_size": "100m",
  "nginx_keepalive_timeout": 120
}
bash# JSON ファイルを使って実行
ansible-playbook site.yml \
  --extra-vars "@config/production.json"

設定値が多い場合は、JSON ファイルにまとめておくと管理しやすくなりますね。

CI/CD パイプラインでの活用

bash# GitLab CI/CD での利用例
# .gitlab-ci.yml から環境変数を渡す
ansible-playbook site.yml \
  --extra-vars "environment=${CI_ENVIRONMENT_NAME}" \
  --extra-vars "deploy_version=${CI_COMMIT_TAG}"
yaml# roles/deploy/tasks/main.yml
---
# extra_vars で渡された値を使用
- name: アプリケーションをデプロイ
  docker_container:
    name: app
    image: 'myapp:{{ deploy_version }}'
    env:
      ENVIRONMENT: '{{ environment }}'

CI/CD パイプラインから動的にバージョン番号や環境名を渡す際、extra_vars が非常に便利でしょう。

変数設計の基本方針

以下の表に、どの変数定義方法を使うべきかの判断基準をまとめました。

#変数の性質推奨する定義場所理由
1環境ごとに変わる可能性があるdefaults上書き可能性を確保
2Role の実装に依存する固定値vars誤った上書きを防ぐ
3実行時に動的に変更したいextra_vars最高優先度で確実に反映
4グループ共通の設定group_vars環境別の管理がしやすい
5特定ホストだけの設定host_varsホスト固有の値を分離

具体例

実践例 1:Web サーバー Role の変数設計

実際の Web サーバー構築を例に、変数設計を見ていきましょう。

プロジェクト構成

pythonwebserver-project/
├── roles/
│   └── webserver/
│       ├── defaults/
│       │   └── main.yml      # デフォルト値
│       ├── vars/
│       │   ├── main.yml      # 共通固定値
│       │   ├── RedHat.yml    # RedHat 系固定値
│       │   └── Debian.yml    # Debian 系固定値
│       └── tasks/
│           └── main.yml
├── group_vars/
│   ├── development.yml       # 開発環境設定
│   ├── staging.yml           # ステージング環境設定
│   └── production.yml        # 本番環境設定
└── site.yml

defaults の定義:柔軟性を持たせる

yaml# roles/webserver/defaults/main.yml
---
# ================================================
# Web サーバーのデフォルト設定
# 環境や要件に応じて上書き可能
# ================================================

# 基本設定
nginx_port: 80
nginx_ssl_port: 443

# パフォーマンス設定(サーバースペックに応じて調整)
nginx_worker_processes: auto
nginx_worker_connections: 1024

# タイムアウト設定
nginx_keepalive_timeout: 65
nginx_client_body_timeout: 60
nginx_send_timeout: 60

これらの値は環境によって変わる可能性があるため、defaults に定義しています。

vars の定義:固定値を守る

yaml# roles/webserver/vars/main.yml
---
# ================================================
# Web サーバーの固定値
# Role の実装に依存するため変更不可
# ================================================

# 設定ファイルのパス
nginx_config_dir: /etc/nginx
nginx_config_file: /etc/nginx/nginx.conf
nginx_vhost_dir: /etc/nginx/sites-available
nginx_vhost_enabled_dir: /etc/nginx/sites-enabled

# ログファイルのパス
nginx_access_log: /var/log/nginx/access.log
nginx_error_log: /var/log/nginx/error.log

# サービス名
nginx_service_name: nginx

パスやサービス名は Role の動作に直結するため、vars で保護します。

OS 別の固定値

yaml# roles/webserver/vars/Debian.yml
---
# Debian/Ubuntu 系の固定値
nginx_package_name: nginx
nginx_user: www-data
nginx_group: www-data
nginx_pid_file: /run/nginx.pid
yaml# roles/webserver/vars/RedHat.yml
---
# RedHat/CentOS 系の固定値
nginx_package_name: nginx
nginx_user: nginx
nginx_group: nginx
nginx_pid_file: /var/run/nginx.pid

OS ごとに異なる値も vars で管理し、安全性を確保します。

環境別の設定:group_vars で管理

yaml# group_vars/development.yml
---
# ================================================
# 開発環境の設定
# ================================================

# 開発用ポート(本番と競合しないように)
nginx_port: 8080

# デバッグ用の設定
nginx_worker_processes: 1
nginx_log_level: debug

# 開発環境ではアップロードサイズ制限を緩和
nginx_client_max_body_size: 50m
yaml# group_vars/production.yml
---
# ================================================
# 本番環境の設定
# ================================================

# 本番用ポート
nginx_port: 80

# パフォーマンス重視の設定
nginx_worker_processes: 4
nginx_worker_connections: 2048
nginx_log_level: warn

# セキュリティを考慮したサイズ制限
nginx_client_max_body_size: 10m

# 本番環境では長めのタイムアウト
nginx_keepalive_timeout: 120

環境ごとに異なる値は group_vars で定義し、defaults の値を上書きします。この設計により、環境間の差分が明確になりますね。

実践例 2:データベース Role の変数設計

次に、データベース構築の例を見てみましょう。

defaults の定義

yaml# roles/database/defaults/main.yml
---
# データベースのデフォルト設定

# MySQL のバージョン(環境によって変更可能)
mysql_version: '8.0'

# ポート番号
mysql_port: 3306

# 文字コード設定
mysql_character_set: utf8mb4
mysql_collation: utf8mb4_general_ci

# 接続設定
mysql_max_connections: 100
mysql_wait_timeout: 28800

vars の定義

yaml# roles/database/vars/main.yml
---
# データベースの固定値

# パッケージ名
mysql_package_name: mysql-server

# 設定ファイルパス
mysql_config_file: /etc/mysql/my.cnf
mysql_config_dir: /etc/mysql/conf.d

# データディレクトリ
mysql_datadir: /var/lib/mysql

# サービス名
mysql_service_name: mysql

実行時の動的な設定変更

bash# テスト環境で最大接続数を増やして実行
ansible-playbook database.yml \
  --extra-vars "mysql_max_connections=500"
bash# 特定のバージョンをインストール
ansible-playbook database.yml \
  --extra-vars "mysql_version=8.0.35"

実行時に特定のパラメータだけを変更したい場合、extra_vars を使えば柔軟に対応できるでしょう。

実践例 3:変数の優先順位を活用した設計パターン

以下の図は、実際の変数値がどのように決定されるかのフローを示しています。

mermaidflowchart TD
    start["Playbook 実行開始"]
    start --> load_defaults["defaults 読み込み<br/>nginx_port: 80"]
    load_defaults --> check_group["group_vars 確認"]
    check_group -->|development| apply_dev["開発環境設定適用<br/>nginx_port: 8080"]
    check_group -->|production| apply_prod["本番環境設定適用<br/>nginx_port: 80(変更なし)"]
    apply_dev --> check_extra
    apply_prod --> check_extra
    check_extra["extra_vars 確認"]
    check_extra -->|指定あり| apply_extra["extra_vars 適用<br/>nginx_port: 9000"]
    check_extra -->|指定なし| no_extra["変更なし"]
    apply_extra --> final
    no_extra --> final["最終値の確定"]
    final --> execute["タスク実行"]

段階的な値の決定プロセス

yaml# 1. defaults で基本値を設定
# roles/webserver/defaults/main.yml
nginx_port: 80
nginx_worker_processes: auto
yaml# 2. 開発環境では group_vars で上書き
# group_vars/development.yml
nginx_port: 8080 # defaults の 80 を上書き
# nginx_worker_processes は defaults のまま
bash# 3. 実行時に extra_vars でさらに上書き
ansible-playbook site.yml \
  -i inventory/development \
  --extra-vars "nginx_port=9000"

# 最終的な値:
# nginx_port: 9000 (extra_vars)
# nginx_worker_processes: auto (defaults)

このように、段階的に値が上書きされていく仕組みを理解することで、意図した通りの設定を確実に適用できます。

実践例 4:チーム開発での変数設計ルール

チームで開発する際の、変数設計のベストプラクティスをまとめます。

変数命名規則

yaml# roles/webserver/defaults/main.yml
---
# ================================================
# 変数命名規則:
# {role名}_{カテゴリ}_{項目名}
# ================================================

# OK: Role 名がプレフィックスになっている
webserver_nginx_port: 80
webserver_nginx_worker_processes: auto
webserver_ssl_certificate_path: /etc/ssl/certs/server.crt

# NG: 汎用的すぎて衝突の可能性
port: 80
worker_processes: auto

Role 名をプレフィックスにすることで、他の Role との変数名の衝突を防げますね。

ドキュメント化

yaml# roles/webserver/defaults/main.yml
---
# ================================================
# Web サーバー設定変数
# ================================================

# ----------------------------------------------
# 基本設定
# ----------------------------------------------

# HTTP ポート番号
# デフォルト: 80
# 環境別の推奨値:
#   - 開発: 8080
#   - 本番: 80
webserver_nginx_port: 80

# SSL ポート番号
# デフォルト: 443
webserver_nginx_ssl_port: 443

# ----------------------------------------------
# パフォーマンス設定
# ----------------------------------------------

# ワーカープロセス数
# 推奨値: CPU コア数と同じ値
# auto: 自動検出(推奨)
webserver_nginx_worker_processes: auto

# ワーカーあたりの最大接続数
# 推奨値: 1024〜4096
webserver_nginx_worker_connections: 1024

各変数に説明とデフォルト値、推奨値を記載することで、チームメンバーが理解しやすくなります。

バリデーション

yaml# roles/webserver/tasks/validate.yml
---
# 変数の妥当性をチェック
- name: ポート番号が有効な範囲かチェック
  assert:
    that:
      - webserver_nginx_port | int >= 1
      - webserver_nginx_port | int <= 65535
    fail_msg: 'ポート番号は 1〜65535 の範囲で指定してください'

- name: ワーカープロセス数が正の整数かチェック
  assert:
    that:
      - webserver_nginx_worker_processes == 'auto' or
        (webserver_nginx_worker_processes | int > 0)
    fail_msg: "ワーカープロセス数は 'auto' または正の整数を指定してください"
yaml# roles/webserver/tasks/main.yml
---
# 最初にバリデーションを実行
- name: 変数の検証
  import_tasks: validate.yml

# その後、本来のタスクを実行
- name: Nginx のインストール
  package:
    name: '{{ nginx_package_name }}'
    state: present

変数のバリデーションを行うことで、設定ミスによるエラーを早期に発見できるでしょう。

実践例 5:複雑な環境での変数管理

複数のデータセンター、複数の環境を持つ大規模プロジェクトでの変数管理例です。

ディレクトリ構造

pythoncomplex-project/
├── group_vars/
│   ├── all/
│   │   ├── common.yml           # 全環境共通
│   │   └── vault.yml            # 暗号化された機密情報
│   ├── datacenter_tokyo/
│   │   └── location.yml         # 東京 DC 固有設定
│   ├── datacenter_osaka/
│   │   └── location.yml         # 大阪 DC 固有設定
│   ├── environment_dev/
│   │   └── env.yml              # 開発環境設定
│   └── environment_prod/
│       └── env.yml              # 本番環境設定
└── host_vars/
    ├── web01.yml                # web01 固有設定
    └── web02.yml                # web02 固有設定

全環境共通の設定

yaml# group_vars/all/common.yml
---
# 全環境で共通の設定

# タイムゾーン
common_timezone: Asia/Tokyo

# NTP サーバー
common_ntp_servers:
  - ntp.nict.jp
  - ntp1.jst.mfeed.ad.jp

# 監視エージェント設定
common_monitoring_enabled: true

データセンター別の設定

yaml# group_vars/datacenter_tokyo/location.yml
---
# 東京データセンター固有の設定

dc_location: tokyo
dc_dns_servers:
  - 10.0.1.10
  - 10.0.1.11
dc_ntp_server: ntp.tokyo.internal
yaml# group_vars/datacenter_osaka/location.yml
---
# 大阪データセンター固有の設定

dc_location: osaka
dc_dns_servers:
  - 10.1.1.10
  - 10.1.1.11
dc_ntp_server: ntp.osaka.internal

環境別の設定

yaml# group_vars/environment_prod/env.yml
---
# 本番環境の設定

env_name: production
env_log_level: warn
env_debug_mode: false

# 本番環境のリソース設定
env_webserver_worker_processes: 4
env_webserver_max_connections: 2048
env_database_max_connections: 500
yaml# group_vars/environment_dev/env.yml
---
# 開発環境の設定

env_name: development
env_log_level: debug
env_debug_mode: true

# 開発環境のリソース設定(控えめ)
env_webserver_worker_processes: 1
env_webserver_max_connections: 512
env_database_max_connections: 100

Inventory ファイルでのグループ組み合わせ

ini# inventory/production
[datacenter_tokyo]
web01.tokyo.prod
web02.tokyo.prod

[datacenter_osaka]
web01.osaka.prod
web02.osaka.prod

[environment_prod:children]
datacenter_tokyo
datacenter_osaka

[webservers]
web01.tokyo.prod
web02.tokyo.prod
web01.osaka.prod
web02.osaka.prod

このように Inventory でグループを組み合わせることで、web01.tokyo.prod は以下の変数を継承します。

  1. group_vars​/​all​/​ の共通設定
  2. group_vars​/​datacenter_tokyo​/​ の東京 DC 設定
  3. group_vars​/​environment_prod​/​ の本番環境設定
  4. host_vars​/​web01.yml の個別設定(存在する場合)

まとめ

Ansible の変数設計は、プロジェクトの保守性と柔軟性を大きく左右する重要な要素です。本記事では、defaultsvarsextra_vars の使い分けを中心に、実践的な設計術をご紹介しました。

変数設計の重要ポイントを以下にまとめます。

変数定義場所の使い分け

定義場所優先度用途
defaults最低上書き可能なデフォルト値
varsRole の実装に依存する固定値
group_vars環境・グループ別の設定
host_vars中高ホスト固有の設定
extra_vars最高実行時の動的な上書き

設計の基本原則

変数設計では、以下の原則を守ることが重要です。

まず、上書き可能性を考慮することが大切でしょう。環境によって変わる可能性がある値は defaults に定義し、柔軟性を確保します。一方で、Role の動作に不可欠な固定値は vars で保護し、予期しない上書きを防ぎましょう。

次に、命名規則を統一することで、変数名の衝突を防ぎ、可読性を向上させられます。Role 名をプレフィックスにする規則を採用すると良いですね。

また、ドキュメント化を徹底することも忘れてはいけません。各変数の用途、デフォルト値、推奨値をコメントで記載することで、チーム全体の理解が深まります。

さらに、バリデーションを実装することで、設定ミスを早期に発見できるでしょう。assert モジュールを活用して、変数の妥当性をチェックする仕組みを導入しましょう。

実践での活用

実際のプロジェクトでは、以下のような場面で変数設計が活きてきます。

開発環境と本番環境で異なる設定を管理する際、defaults でデフォルト値を定義し、group_vars で環境ごとの差分を管理することで、変更箇所を最小限に抑えられます。

CI/CD パイプラインから実行する場合は、extra_vars でバージョン番号や環境名を動的に渡すことで、柔軟なデプロイが可能になるでしょう。

複数のデータセンターや環境を管理する大規模プロジェクトでは、グループの階層構造を活用し、共通設定と個別設定を適切に分離することが重要です。

適切な変数設計により、Ansible のコードは再利用性が高く、保守しやすいものになります。本記事で紹介した設計パターンを活用して、皆さんのインフラコードをより良いものにしていってくださいね。

関連リンク