【Ansible】PostgreSQL構築の自動化

PostgreSQLの構築をAnsibleで自動化してみました。
PostgreSQLは、SI系のシステムでOracle DBの代替として広く使われているDBMSです。
ソフトウェアをインストールしてサービスを起動するところまでを、シンプルな構成でPlaybookにまとめました。

前提条件

以下の前提条件で検証しています。

  • ハードウェア: VirtualBox上の仮想マシン
  • OS: CentOS 8.1
  • ミドルウェア:
    • PostgreSQL: 10.6 (OS同梱版)
  • ツール:
    • Ansible: 2.9.11
    • psycopg2: 2.7.5
  • 構築対象サーバはインターネットと疎通が取れること
  • 構築対象サーバのローカル環境でAnsibleを実行

Ansbileのインストール手順は以下の記事を参照してください。
Ansibleのインストール (RHEL 8 / CentOS 8)
Ansible管理対象サーバの設定

ディレクトリ構造

今回作成するファイル一式は、以下のディレクトリ構造で任意の場所に配置します。

.
├── ansible.cfg
├── group_vars
│   └── db.yml
├── hosts
├── roles
│   └── postgresql
│       ├── defaults
│       │   └── main.yml
│       ├── handlers
│       │   └── main.yml
│       ├── tasks
│       │   ├── configurations.yml
│       │   ├── db_cluster.yml
│       │   ├── main.yml
│       │   ├── packages.yml
│       │   └── services.yml
│       └── templates
│           └── postgresql.conf.j2
└── site.yml

Ansible設定ファイル (ansible.cfg)

Ansibleの動作を制御する設定ファイルを定義します。
通常は、初めてSSH接続するホストは公開鍵の登録を行う必要があります。今回は利便性を考慮して、鍵チェックを無効化するためのオプションを指定します。

[ansible.cfg]

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null

インベントリ (hosts)

PostgreSQLの構築対象サーバ(ターゲットノード)を定義したインベントリファイルを作成します。
ホスト名を記載し、ansible_hostに接続先IPアドレスを指定します。ターゲットノード自身でPlaybookを実行する場合は、IPアドレスを127.0.0.1に指定します。
今回はdb1のみ有効なホストとして定義していますが、コメントアウトしているdb02のように複数のホストをまとめて管理することも可能です。
[all:vars]の箇所では、全ホスト共通のログインユーザをansible_userで指定しています。
コメントアウトされているansible_passwordansible_become_passにそれぞれログインパスワードと特権ユーザパスワードを入力すれば、Playbook実行時のパスワード手動入力を省くことも可能です。

[hosts]

########################
# ホスト一覧
########################
db01 ansible_host=127.0.0.1
# db02 ansible_host=192.168.0.1

########################
# ホストグループ所属定義
########################
[db]
db01
# db02

########################
# 接続情報変数
########################
[all:vars]
ansible_user=ansible
# ansible_password=P@ssword
# ansible_become_pass=P@ssword

インベントに定義したホスト特有のパラメータを、グループ変数として定義します。ファイル名を<グループ名>.ymlとすることで、該当するグループに所属するホストに対して、Playbookの処理中に変数を使用できます。
今回は、ソフトウェアのインストールバージョンを定義しています。
後述のデフォルト変数でグループ変数と同じ名前が存在する場合、グループ変数の値が優先されます。

[group_vars/db.yml]

---
########################
# システム固有パラメータ
########################

# ソフトウェアバージョン
postgresql_version: "10.6"

Playbook (site.yml)

Playbookの本体を作成します。このファイルでは、次の事項を定義します。

  • Playbook実行対象のターゲットノード
  • 特権ユーザの切り替え方式
  • 適用するロール

以下のファイルでは、dbグループに所属するホストに対して、postgresqlのロールの処理内容を実行しています。
becomeは特権ユーザへの切り替え有無、become_methodは切り替えコマンドを指定しています。今回はbecome_methodsuを指定してrootユーザへ変更していますが、SSHログインユーザがsudoの権限を持っている場合は、同文字列を指定してください。

[site.yml]

---
- hosts: db
  become: yes
  become_method: su
  roles:
    - postgresql

ロール (roles/postgresql)

ロールは、管理対象の単位(多くはミドルウェア)ごとに、処理や変数のファイルをまとめたものです。今回は、PostgreSQL用にpostgresqlという名前のロールを作成します。

デフォルト変数

ロールがデフォルトで使用するパラメータを定義します。タスクやテンプレートの内部で、各変数の値が代入されます。
postgresql_version(パッケージのインストールバージョン)の値は、指定がない場合に最新バージョンのパッケージがインストールされるように*としています。今回は、前述のグループ変数で同じ名前の変数を定義しているので、実際は10.6で上書かれます。

[roles/postgresql/defaults/main.yml]

---
########################
# デフォルトパラメータ
########################

# インストールパッケージ
postgresql_version: "*"
postgresql_packages:
  - "postgresql-{{ postgresql_version }}"
  - "postgresql-server-{{ postgresql_version }}"

# ディレクトリ構成
postgresql_homedir: "/var/lib/pgsql"
postgresql_datadir: "{{ postgresql_homedir }}/data"
postgresql_logdir: "/var/log/pgsql"

# postgresql.confパラメータ
postgresql_conf:
  # 接続設定
  listen_addresses: "*"
  port: "5432"
  max_connections: "100"

  # パフォーマンス設定
  shared_buffers: "256MB"
  work_mem: "16MB"
  maintenance_work_mem: "64MB"
  dynamic_shared_memory_type: "posix"

  # ログ設定
  logging_collector: "on"
  log_destination: "stderr"
  log_directory: "{{ postgresql_logdir }}"
  log_filename: "postgresql-%Y%m%d.log"
  log_truncate_on_rotation: "on"
  log_rotation_age: "1d"
  log_rotation_size: "0"
  log_line_prefix: "[%m]%u %d %p[%l] "
  log_timezone: "Japan"

  # クライアント接続設定
  datestyle: "iso, ymd"
  timezone: "Japan"
  lc_messages: "ja_JP.UTF-8"
  lc_monetary: "ja_JP.UTF-8"
  lc_numeric: "ja_JP.UTF-8"
  lc_time: "ja_JP.UTF-8"
  default_text_search_config: "pg_catalog.simple"

タスク

構築対象サーバのあるべき姿をタスクファイルに定義します。タスクは、処理カテゴリごとにファイルを分割することで、再利用性を高めています。

main.yml

ロール呼び出し時にデフォルトで読み込まれるmain.ymlには、各ファイルのインポート処理を定義します。

[roles/postgresql/tasks/main.yml]

---
- name: "パッケージ管理"
  import_tasks: packages.yml

- name: "DBクラスタ作成"
  import_tasks: db_cluster.yml

- name: "設定ファイル管理"
  import_tasks: configurations.yml

- name: "サービス起動状態管理"
  import_tasks: services.yml

packages.yml

パッケージ管理タスクは以下の通りです。
postgresql_packagesで定義したパッケージ名の配列をyumモジュールに渡します。該当パッケージがインストールされていない場合、yumコマンドでパッケージがインストールされます。

[roles/postgresql/tasks/packages.yml]

---
- name: "インストールパッケージ"
  yum:
    name: "{{ postgresql_packages }}"
    state: present

db_cluster.yml

DBクラスタ作成タスクは以下の通りです。
DBクラスタの作成はpostgresユーザに変更した後initdbコマンドを使用するのが一般的ですが、root権限を持つユーザが直接実行できるようpostgresql-setupコマンドを使用しています。
initdbオプションの引数はPGSETUP_INITDB_OPTIONS環境変数に格納しておく必要があるので、shellモジュールのenvironmentアーギュメントを使用しています。
DBクラスタの作成は、初回設定時に一度だけ実行すればよいので、createsでDBクラスタないのファイルを指定して、ファイルが存在しない場合はタスクの実行をスキップします。これにより冪等性が担保されます。

[roles/postgresql/tasks/db_cluster.yml]

---
- name: "DBクラスタの初期化"
  shell: "/bin/postgresql-setup initdb"
  environment:
    PGSETUP_INITDB_OPTIONS: "-D {{ postgresql_datadir }} --encoding=UTF-8 --no-locale"
  args:
    creates: "{{ postgresql_datadir }}/PG_VERSION"

configurations.yml

ソフトウェア設定タスクは以下の通りです。
ログディレクトリは、fileモジュールで定義したディレクトリが存在しない場合に新規作成します。
postgresql.confは、後述するテンプレートを使用し、予め定義した変数を埋め込んだ設定ファイルを所定のディレクトリに配置します。

[roles/postgresql/tasks/configurations.yml]

---
- name: "ログディレクトリ"
  file:
    path: "{{ postgresql_logdir }}"
    state: "directory"
    owner: "postgres"
    group: "postgres"
    mode: "0755"

- name: "設定ファイル(postgresql.conf)"
  template:
    src: "postgresql.conf.j2"
    dest: "{{ postgresql_datadir }}/postgresql.conf"
    owner: "postgres"
    group: "postgres"
    mode: "0600"
  notify: "restart_postgresql"

services.yml

サービス起動タスクは以下の通りです。
systemdモジュールを使用して、サービス起動状態と自動起動設定を管理します。

[roles/postgresql/tasks/services.yml]

---
- name: "サービス起動"
  systemd:
    name: "postgresql.service"
    state: "started"
    enabled: yes

ハンドラ

notifyが指定されているタスクの実行結果に変更が発生した場合に呼び出される処理を定義します。
main.ymlの中で、notifyで呼び出された名前と同じ名前のタスクを作成します。
以下では、設定ファイルが変更された時にPostgreSQLを再起動するハンドラタスクをrestart_postgresqlという名前で定義します。

[roles/postgresql/handlers/main.yml]

---
- name: "restart_postgresql"
  systemd:
    name: "postgresql.service"
    state: "restarted"

テンプレート

設定ファイルの雛形となるテンプレートを定義します。
デフォルト変数で定義したpostgresql_conf配下のパラメータ名を{{ }}で囲んで記載します。設定ファイルがデプロイされるときは、変数の値が該当箇所に代入されます。

[roles/postgresql/templates/postgresql.conf.j2]

# 接続設定
listen_addresses = '{{ postgresql_conf.listen_addresses }}'
port = {{ postgresql_conf.port }}
max_connections = {{ postgresql_conf.max_connections }}

# パフォーマンス設定
shared_buffers = {{ postgresql_conf.shared_buffers }}
work_mem = {{ postgresql_conf.work_mem }}
maintenance_work_mem = {{ postgresql_conf.maintenance_work_mem }}
dynamic_shared_memory_type = {{ postgresql_conf.dynamic_shared_memory_type }}

# ログ設定
logging_collector = {{ postgresql_conf.logging_collector }}
log_destination = '{{ postgresql_conf.log_destination }}'
log_directory = '{{ postgresql_conf.log_directory }}'
log_filename = '{{ postgresql_conf.log_filename }}'
log_truncate_on_rotation = {{ postgresql_conf.log_truncate_on_rotation }}
log_rotation_age = {{ postgresql_conf.log_rotation_age }}
log_rotation_size = {{ postgresql_conf.log_rotation_size }}
log_line_prefix = '{{ postgresql_conf.log_line_prefix }}'
log_timezone = '{{ postgresql_conf.log_timezone }}'

# クライアント接続設定
datestyle = '{{ postgresql_conf.datestyle }}'
timezone = '{{ postgresql_conf.timezone }}'
lc_messages = '{{ postgresql_conf.lc_messages }}'
lc_monetary = '{{ postgresql_conf.lc_monetary }}'
lc_numeric = '{{ postgresql_conf.lc_numeric }}'
lc_time = '{{ postgresql_conf.lc_time }}'
default_text_search_config = '{{ postgresql_conf.default_text_search_config }}'

Playbookの実行結果

ansible-playbookコマンドでPlaybookを実行します。
オプションに指定している-k-Kは、それぞれSSHログインユーザとrootユーザのパスワードをプロンプトから対話式に入力するためのものです。インベントリファイル内で、ansible_passwordansible_become_passでそれぞれパスワードを指定している場合は不要です。

$ ansible-playbook -i hosts site.yml -k -K

SSH password:                              ←SSHログインユーザパスワードを入力
BECOME password[defaults to SSH password]: ←rootユーザパスワードを入力

PLAY [db] **************************************************************************

TASK [Gathering Facts] *************************************************************
ok: [db01]

TASK [postgresql : インストールパッケージ] ****************************************************
changed: [db01]

TASK [postgresql : DBクラスタの初期化] *****************************************************
changed: [db01]

TASK [postgresql : ログディレクトリ] *******************************************************
changed: [db01]

TASK [設定ファイル(postgresql.conf)] *****************************************************
changed: [db01]

TASK [postgresql : サービス起動] *********************************************************
changed: [db01]

RUNNING HANDLER [restart_postgresql] ***********************************************
changed: [db01]

PLAY RECAP *************************************************************************
db01                       : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Playbookの実行が終わると、構築対象サーバごとに完了したタスク数がokに、構成変更が発生したタスク数がchangedに表示されます。
今回作成したPlaybookは冪等性を意識して作っているので、もう一度Playbookを実行しても変更は発生しません。

$ ansible-playbook -i hosts site.yml -k -K

SSH password:                              ←SSHログインユーザパスワードを入力
BECOME password[defaults to SSH password]: ←rootユーザパスワードを入力

PLAY [db] **************************************************************************

TASK [Gathering Facts] *************************************************************
ok: [db01]

TASK [postgresql : インストールパッケージ] ****************************************************
ok: [db01]

TASK [postgresql : DBクラスタの初期化] *****************************************************
ok: [db01]

TASK [postgresql : ログディレクトリ] *******************************************************
ok: [db01]

TASK [設定ファイル(postgresql.conf)] *****************************************************
ok: [db01]

TASK [postgresql : サービス起動] *********************************************************
ok: [db01]

PLAY RECAP *************************************************************************
db01                       : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です