【Ansible】PostgreSQL構築の自動化
PostgreSQLの構築をAnsibleで自動化してみました。
PostgreSQLは、SI系のシステムでOracle DBの代替として広く使われているDBMSです。
ソフトウェアをインストールしてサービスを起動するところまでを、シンプルな構成でPlaybookにまとめました。
Contents
前提条件
以下の前提条件で検証しています。
- ハードウェア: 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_password
とansible_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_method
にsu
を指定して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_password
とansible_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