2022年9月24日土曜日

PostgreSQLをストリーミングレプリケーションで構成する (PostgreSQL 10.x版)

PostgreSQLは「ストリーミングレプリケーション」と呼ばれる構成を取ることで、DBの冗長構成を取ることができる。

ストリーミングレプリケーションの仕組みはこうだ。まず、構成は「マスター」と呼ばれる1台のDB更新・参照が可能なDBと、1台以上の参照のみ可能な「スタンバイ」で構成される。マスターにてDBの更新がされる際に出力される更新ログ「WAL (Write Ahead Log)」をスタンバイに転送し、DBに反映する。これによって、マスターとスタンバイの持つDBの内容が同一となるよう動作する。

今回、実際にPostgreSQLをインストールしてストリーミングレプリケーションを構成してみたので、その手順を記載する。

本記事ではPostgreSQL 10.xの手順を記載している。PostgreSQL 15.x版の手順は以下URLを参照すること。

環境

OSはRHEL 8を利用し、ディストリビューションでパッケージ提供されているPostgreSQLのバージョンをそのまま利用する。

  • OS : Red Hat Enterprise Linux 8.2
  • PostgreSQL : 10.6

マスターとスタンバイの2台にPostgreSQLをインストールし、ストリーミングレプリケーションを構成する。ホスト名やIPアドレスは以下図を参照いただきたい。

今回はマスターとスタンバイ両方で実施する作業と、片方のみで実施する作業がある。そのため、本記事で記載するプロンプトを以下の通り記載し、作業対象が判別できるようにした。

プロンプト 説明
[Master/Standby] マスターとスタンバイ両方で実施
[Master] マスターのみ実施
[Standby] スタンバイのみ実施

事前作業として、検証目的なので、firewalldとSELinuxは無効化しておこう。

[Master/Standby]# systemctl stop firewalld
[Master/Standby]# systemctl disable firewalld
[Master/Standby]# sed -ie 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
[Master/Standby]# reboot

ストリーミングレプリケーション構成手順概要

PostgreSQLでストリーミングレプリケーションを構成する手順の概要を以下に図示する。

①PostgreSQLインストール・初期セットアップ

1. dnfでPostgreSQLをインストール

まずは、PostgreSQLをインストールする。postgresql-serverがPostgreSQLのDBエンジン本体となり、依存関係でクライアントツール (posgresqlパッケージ) もインストールされる。それ以外に、各種管理用のスクリプトなどが含まれるpostgresql-contribパッケージをインストールしておく。

[Master/Standby]# dnf install postgresql-server postgresql-contrib -y

~(中略)~

依存関係が解決しました。
================================================================================
 パッケージ         Arch   バージョン                       リポジトリー  サイズ
================================================================================
インストール中:
 postgresql-contrib x86_64 10.6-1.module+el8+2469+5ecd5aae  dvd-AppStream 805 k
 postgresql-server  x86_64 10.6-1.module+el8+2469+5ecd5aae  dvd-AppStream 5.1 M
依存関係のインストール中:
 libpq              x86_64 12.1-3.el8                       dvd-AppStream 195 k
 perl-Carp          noarch 1.42-396.el8                     dvd-BaseOS     30 k
 perl-Exporter      noarch 5.72-396.el8                     dvd-BaseOS     34 k
 perl-libs          x86_64 4:5.26.3-416.el8                 dvd-BaseOS    1.6 M
 postgresql         x86_64 10.6-1.module+el8+2469+5ecd5aae  dvd-AppStream 1.5 M
 uuid               x86_64 1.6.2-42.el8                     dvd-AppStream  63 k
モジュールストリームの有効化中:
 postgresql                10

トランザクションの概要
================================================================================
インストール  8 パッケージ

~(以下略)~

2. postgresユーザーのパスワードを変更

PostgreSQLは原則postgresユーザーにて操作する。パッケージインストール後にpostgresユーザーのパスワードを設定しておこう。

[Master/Standby]# passwd postgres

3. 初期セットアップ

DBの初期データを作成するため、DB初期セットアップコマンドを実行する。/var/lib/pgsql/dataに初期状態のDBが作成される。

[Master/Standby]# postgresql-setup initdb
WARNING: using obsoleted argument syntax, try --help
WARNING: arguments transformed to: postgresql-setup --initdb --unit postgresql
 * Initializing database in '/var/lib/pgsql/data'
 * Initialized, logs are in /var/lib/pgsql/initdb_postgresql.log

4. DBを起動

一度このタイミングでDBが問題なく起動することを確認しておこう。

[Master/Standby]# systemctl start postgresql
[Master/Standby]# systemctl enable postgresql
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql.service  → /usr/lib/systemd/system/postgresql.service.

②マスターのDB初期設定

1. マスターにてWALアーカイブログディレクトリ作成

ストリーミングレプリケーションを構成するため、事前にWALアーカイブログを保存するディレクトリを作成する。今回は、/var/lib/pgsql/data/archivedirを保存場所とした。

[Master]$ su - postgres
[Master]$ mkdir /var/lib/pgsql/data/archivedir
[Master]$ chmod 700 /var/lib/pgsql/data/archivedir
[Master]$ ls -ld /var/lib/pgsql/data/archivedir
drwx------ 2 postgres postgres 6  6月  5 17:06 /var/lib/pgsql/data/archivedir

2. マスターにてレプリケーション用のDBユーザーを作成

レプリケーション用のDBユーザーを作成する。今回はdbreplというユーザーを作成した。

[Master]$ psql
postgres=# \du
                                               ロール一覧
 ロール名 |                                     属性                                     | 所属グループ
----------+------------------------------------------------------------------------------+--------------
 postgres | スーパーユーザー, ロール作成可, DB作成可, レプリケーション可, RLS のバイパス | {}

postgres=# create user dbrepl replication login encrypted password 'P@ssw0rd';
CREATE ROLE
postgres=# \du
                                               ロール一覧
 ロール名 |                                     属性                                     | 所属グループ
----------+------------------------------------------------------------------------------+--------------
 dbrepl   | レプリケーション可                                                           | {}
 postgres | スーパーユーザー, ロール作成可, DB作成可, レプリケーション可, RLS のバイパス | {}

postgres=# \q

3. マスターにて各種設定を実施

次に、マスターにてDBのレプリケーションに必要な各種設定を実施する。設定前に、念のため変更前の設定ファイルをバックアップしておこう。

[Master]$ cp /var/lib/pgsql/data/postgresql.conf ~/postgresql.conf.org
[Master]$ cp /var/lib/pgsql/data/pg_hba.conf ~/pg_hba.conf.org

postgresql.conf

PostgreSQLの各種設定を変更するため、postgresql.confを更新する。以下に設定内容の概要を記載する。

設定項目 設定内容
listen_addresses DBが接続を受け付けるIPアドレスを指定する。今回はすべてのインタフェースにて許可するため* (all)を設定する。
wal_level replicaで指定する。
synchronous_commit DBに更新があった際に、どこまで更新が反映されたら応答を返すかを設定する。例えば、localの場合はマスターでWALがディスクに記録されたら応答を返す。onの場合はスタンバイに転送されたWALがディスクに記録されたのちに応答を返す。今回は、onで設定する。
archive_mode スタンバイのレプリケーションが長期間停止した際に、マスター側のWALが削除された場合は、WALアーカイブログを使う可能性があるため、WALアーカイブログを有効にする。ストリーミングレプリケーションの場合はalwaysに設定すればよさそうだ。
archive_command WALアーカイブログを出力時に、WALをコピーするためのコマンドを指定する。
max_wal_senders スタンバイから接続を許可する接続数の最大値を設定する。スタンバイ1台の構成であるため、10に設定すれば十分となる。
wal_keep_segments ストリーミングレプリケーション用に保持するWALのファイル数。今回は決め打ちで30とした。
synchronous_standby_names スタンバイのリストを記載する。スタンバイを識別する名称は、後述するスタンバイのrecovery.confファイルで設定したapplication_nameとなる。今回はフェイルオーバー時の設定簡素化のため、* (all) で設定する。
hot_standby ストリーミングレプリケーションを有効にするためonに設定する。
lc_messages PostgreSQLのエラーメッセージを日本語から英語に変更する場合は、Cで設定する。この変更は任意である。
[Master]$ cd $PGDATA
[Master]$ vi postgresql.conf
listen_addresses = '*'
 ↑★すべてのインタフェースにて接続を受け付ける
wal_level = replica    ←★コメントアウトを外す
synchronous_commit = on  ←★コメントアウトを外す
archive_mode =  always  ←★コメントアウトを外しalwaysに設定
archive_command = 'test ! -f /var/lib/pgsql/data/archivedir/%f && cp %p /var/lib/pgsql/data/archivedir/%f'
 ↑★コメントアウトを外し上記に設定
max_wal_senders = 10    ←★コメントアウトを外す
wal_keep_segments = 30   ←★コメントアウト外し30に設定
synchronous_standby_names = '*' ←★コメントアウトを外し* (all) に設定
hot_standby = on       ←★コメントアウトを外す
lc_messages = 'C'      ←★エラーメッセージを日本語から英語にするため変更

pg_hba.conf

PostgreSQLにスタンバイからの接続許可を行うため、pg_hba.confに追記する。

[Master]$ cd $PGDATA
[Master]$ vi pg_hba.conf
~(中略)~
host    replication     dbrepl          192.168.11.116/32       md5
host    replication     dbrepl          192.168.11.117/32       md5

4. マスターのDB再起動

設定反映するため、DBを再起動する。一部設定はリロードでは反映されないため、必ず再起動を実施しよう。

[Master]$ systemctl restart postgresql
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
'postgresql.service'を再起動するには認証が必要です。
Authenticating as: root
Password:
==== AUTHENTICATION COMPLETE ====

③マスターのDBをスタンバイにコピー(ベースバックアップ取得)

1. マスターのDBをスタンバイにコピー(ベースバックアップ取得)

スタンバイはマスターからベースバックアップを取得することで、初期設定と初期データのコピーを行う。コピーはPostgreSQLのバックアップ機能であるベースバックアップを使用する。

ベースバックアップを取得するため、スタンバイのDBを一度停止する。

[Standby]# systemctl stop postgresql

DB初期化時に作成されたデータをすべて削除してから、pg_basebackupコマンドを用いてマスターのベースバックアップを直接コピーする。

[Standby]# su - postgres
[Standby]$ rm -rf /var/lib/pgsql/data/*
[Standby]$ pg_basebackup -D /var/lib/pgsql/data -h 192.168.11.116 -X stream -c fast -U dbrepl -W
パスワード:   ←★DBレプリケーション用のユーザーのパスワードを指定
[Standby]$ echo $?
0        ←★0であることを確認

④スタンバイのDB設定(レプリケーション設定)

1. スタンバイにてレプリケーション設定を実施

ベースバックアップを行うとpostgresql.confpg_hba.confも一緒にスタンバイへコピーされるため、追加で設定は不要となる。スタンバイ独自で設定が必要なファイルは、recovery.confとなる。

recovery.conf

recovery.confにはPostgreSQLをスタンバイとして動作させるための設定を記載する。以下に設定内容の概要を記載する。

設定項目 設定内容
standby_mode スタンバイで動作させるため、onを設定する。
primary_conninfo application_nameは任意の名前でよいが、今回はスタンバイのホスト名を設定する。userpasswordは前の手順で作成したレプリケーション用DBユーザーを指定する。hostportはマスターのIPアドレスとポート番号 (デフォルト5432) を指定する。
recovery_target_timeline ストリーミングレプリケーションの場合はlatestを設定する。
restore_command ストリーミングレプリケーションでWALアーカイブログを用いる場合のコピーコマンドを指定する。今回は、scpコマンドを利用する。scp実行時にパスワードを聞かれないようにするため、後程SSHキーの交換を行っておく。
[Standby]$ cd $PGDATA
[Standby]$ vi recovery.conf
standby_mode = 'on'
primary_conninfo = 'application_name=t1117psgl user=dbrepl password=''P@ssw0rd'' host=192.168.11.116 port=5432'
recovery_target_timeline = 'latest'
restore_command = 'scp -o StrictHostKeyChecking=no 192.168.11.116:$PGDATA/archivedir/%f "%p"'

2. マスターとスタンバイのSSHキーを互いに交換

スタンバイからマスターのアーカイブログを取得する際にscpコマンドを使うため、その際にパスワードを聞かれないよう、SSHキーの交換を行っておく。

[Master]$ ssh-keygen
[Master]$ ssh-copy-id 192.168.11.117

[Standby]$ ssh-keygen
[Standby]$ ssh-copy-id 192.168.11.116

3. スタンバイのDBを起動

以上で準備が整ったので、停止していたスタンバイのDBを起動する。

[Standby]$ systemctl start postgresql
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
'postgresql.service'を開始するには認証が必要です。
Authenticating as: root
Password:
==== AUTHENTICATION COMPLETE ====

4. マスターにてレプリケーション状態確認

起動後、マスターにてレプリケーションの状態確認を行う。レプリケーションが開始されると、pg_stat_replicationテーブルにレコードが記載される。もし、なにも表示されない場合は、何らかの理由でレプリケーションが正常にできていないことを意味する。

[Master]$ psql -x -c "SELECT * FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
pid              | 3486
usesysid         | 16384
usename          | dbrepl
application_name | t1117psgl
client_addr      | 192.168.11.117
client_hostname  |
client_port      | 51458
backend_start    | 2022-06-05 17:37:57.698886+09
backend_xmin     |
state            | streaming
sent_lsn         | 0/7000060
write_lsn        | 0/7000060
flush_lsn        | 0/7000060
replay_lsn       | 0/7000060
write_lag        |
flush_lag        |
replay_lag       |
sync_priority    | 1
sync_state       | sync   ←★syncであることを確認

⑤レプリケーション動作確認

1. マスターにてDB及びテーブルを作成

それでは、実際にマスターにて更新した内容がストリーミングレプリケーションによってスタンバイ側に反映されることを確認しよう。

まず、テスト用DBとしてtestdbを作成する。

[Master]$ psql -l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |     アクセス権限
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
(3 行)

[Master]$ createdb testdb
[Master]$ psql -l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |     アクセス権限
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
 testdb    | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
(4 行)

次に、testdbに対してテーブルmytableを作成する。mytableidnameの二つの列を持つシンプルなテーブルとしている。

[Master]$ psql -d testdb
testdb=# \dt
リレーションが見つかりませんでした。
testdb=# create table mytable (id integer primary key, name varchar(20));
CREATE TABLE
testdb=# \dt
             リレーション一覧
 スキーマ |  名前   |    型    |  所有者
----------+---------+----------+----------
 public   | mytable | テーブル | postgres
(1 行)

testdb=# \q

2. スタンバイにレプリケーションされることを確認

作成したテーブルmytableに対して、レコードを3行登録する。

[Master]$ psql -d testdb -c "insert into mytable (id, name) values (1, 'Ichiro');"
[Master]$ psql -d testdb -c "insert into mytable (id, name) values (2, 'Jiro');"
[Master]$ psql -d testdb -c "insert into mytable (id, name) values (3, 'Saburo');"
[Master]$ psql -d testdb -c "select * from mytable;"
 id |  name
----+--------
  1 | Ichiro
  2 | Jiro
  3 | Saburo
(3 行)

スタンバイで確認すると、3行のレコードが存在することを確認できる。

[Standby]$ psql -d testdb -c "select * from mytable;"
 id |  name
----+--------
  1 | Ichiro
  2 | Jiro
  3 | Saburo
(3 行)

なお、スタンバイは参照のみ可能となるため、以下の通りDBの更新を行うとエラーとなり失敗する点に注意しよう。

[Standby]$ psql -d testdb -c "insert into mytable (id, name) values (4, 'Shiro');"
ERROR:  cannot execute INSERT in a read-only transaction

以上で、PostgreSQLをインストールしてストリーミングレプリケーションを構成できた。次回は、マスターとスタンバイのDBの役割を切り替えるフェイルオーバーの手順を記載する。

次回記事↓。

PostgreSQLのDBを手動フェイルオーバーする手順 (ストリーミングレプリケーション構成)

参照

2022年9月10日土曜日

GitLabをインストールする手順

現在、自宅環境ではAnsibleやSeleniumによる環境の自動化対応を進めている。その中で、やはりコードを管理するバージョン管理システム (VCS) が欲しくなり、以前から自宅環境として欲しかったGitLabを構築することにした。

ということで、本記事ではGitLabをAlmaLinuxにインストールする手順を記載する。

環境

GitLabをインストールするOSは、AlmaLinuxを用いる。ただし、Red Hat系のディストリビューションであるCentOSやRocky Linuxなどでも同様の手順で構築できるだろう。

  • OS : AlmaLinux 8.5
  • GitLab : 15.1.2

GitLabをインストールするサーバのスペックはCPU4コア・メモリ8GBとした。公式サイトに記載の最小要件はCPU4コア・メモリ4GBとなるが、メモリを多く消費するようなので余裕をもって8GBで構築している。

ちなみに、当初CPU2コア、メモリ2GBの環境で構築を進めていたが、メモリ不足となりインストールがいつまでも終了しない事態となったことから、少なくとも最小要件は必ず満たすようサーバを用意した方がよさそうだ。

また、今回は手順簡略化のため、SELinuxとfirewalldは停止しておく。

# systemctl stop firewalld
# systemctl disable firewalld
# sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
# reboot

GitLabインストール手順

1. Postfixをインストール

GitLabの機能においてメール送信を利用する場合は、Postfixを使うようだ。本記事ではメール送信までの設定や確認は対象外としているが、将来利用できるようインストールしておく。

# dnf install postfix -y
# systemctl enable postfix
# systemctl start postfix

2. GitLabのリポジトリを登録

GitLabはdnfを用いてインストールするため、GitLabのリポジトリを登録する。リポジトリ登録用のシェルが公開されているので、以下の通りコマンドを実行するだけで、リポジトリ登録が完了する。

# curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  6928  100  6928    0     0  39588      0 --:--:-- --:--:-- --:--:-- 39588
Detected operating system as almalinux/8.
Checking for curl...
Detected curl...
Downloading repository file: https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/config_file.repo?os=almalinux&dist=8&source=script
done.

~(中略)~

完了しました!
Generating yum cache for gitlab_gitlab-ee...
GPG 鍵 0x51312F3F をインポート中:
 Userid     : "GitLab B.V. (package repository signing key) <packages@gitlab.com>"
 Fingerprint: F640 3F65 44A3 8863 DAA0 B6E0 3F01 618A 5131 2F3F
 From       : https://packages.gitlab.com/gitlab/gitlab-ee/gpgkey
GPG 鍵 0xF27EAB47 をインポート中:
 Userid     : "GitLab, Inc. <support@gitlab.com>"
 Fingerprint: DBEF 8977 4DDB 9EB3 7D9F C3A0 3CFC F9BA F27E AB47
 From       : https://packages.gitlab.com/gitlab/gitlab-ee/gpgkey/gitlab-gitlab-ee-3D645A26AB9FBD22.pub.gpg
Generating yum cache for gitlab_gitlab-ee-source...

The repository is setup! You can now install packages.

3. dnfにてGitLabをインストール

リポジトリが登録されているので、dnfにてGitLabをインストールする。GitLab Enterprise Edition (gitlab-ee) という名前になっているが、有償ライセンス登録をしなければ、機能上もライセンス上もGitLab Community Editionと同様の機能のみ利用できるようになっており、特に問題はない。

# dnf install gitlab-ee -y

4. GitLabを初期設定

単純にdnfでインストールしただけでは、初期設定がされておらずGitLabを利用することができないため、初期設定を実施する。

まず、GitLabを公開するURLを定義する。設定ファイルは/etc/gitlab/gitlab.rbとなるので、vi等を用いてexternal_urlの項目を修正しよう。

# vi /etc/gitlab/gitlab.rb
external_url 'http://gitlab.example.com'

設定が完了したら、以下コマンドで設定を反映させる。この処理は数分~数十分程度の時間を要するので、完了まで気長に待とう。もしいつまでも処理が完了しない場合は、CPUやメモリのリソース不足を疑おう。

# gitlab-ctl reconfigure

~(中略)~

Notes:
Default admin account has been configured with following details:
Username: root
Password: You didn't opt-in to print initial root password to STDOUT.
Password stored to /etc/gitlab/initial_root_password. This file will be cleaned up in first reconfigure run after 24 hours.

NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.

gitlab Reconfigured!

初期設定が完了すると、gitlab-runsvdirというサービス名でGitLabが起動しているはずだ。

# systemctl status gitlab-runsvdir
● gitlab-runsvdir.service - GitLab Runit supervision process
   Loaded: loaded (/usr/lib/systemd/system/gitlab-runsvdir.service; enabled; ve>
   Active: active (running) since Thu 2022-07-07 13:51:09 JST; 1 day 20h ago
 Main PID: 3492 (runsvdir)
    Tasks: 431 (limit: 4915)
   Memory: 5.0G
   CGroup: /system.slice/gitlab-runsvdir.service
           tq  3492 runsvdir -P /opt/gitlab/service log: ......................>
           tq  3494 runsv logrotate
           tq  3504 svlogd -tt /var/log/gitlab/logrotate
           tq  3529 runsv redis
           tq  3531 /opt/gitlab/embedded/bin/redis-server unixsocket:/var/opt/g>
           tq  3548 svlogd -tt /var/log/gitlab/redis

~(以下略)~

5. 管理者ユーザ (root) の初期パスワードを確認

管理者ユーザ (root) の初期パスワードは自動生成され、その内容が/etc/gitlab/initial_root_passwordに記載されている。

# cat /etc/gitlab/initial_root_password
# WARNING: This value is valid only in the following conditions
#          1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run).
#          2. Password hasn't been changed manually, either via UI or via command line.
#
#          If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.

Password: LEDXXXXqJIZWfXXXXeoZxXXXXNe6XXXXjXFQeJ7ZXXXX

# NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours.

6. GitLabにログイン確認

それではGitLabにログインしてみよう。設定したURLまたはIPアドレスにブラウザでアクセスしてみよう。問題なければ、ログイン画面が表示されるはずだ。

ユーザ名をroot、パスワードは先ほど確認した自動生成パスワードを入力してログインできれば、インストールは成功となる。

最後にパスワードを変更しておこう。右上のユーザのアイコン→「Edit profile」→「Password」を選択し、「New password」に新しいパスワードを設定し「Save password」ボタンを押せば変更できる。

以上で、GitLabをAlmaLinuxにインストールする手順は完了となる。

2022年9月3日土曜日

AnsibleからLinuxサーバに接続するための事前準備をするPlaybookを作ってみた

AnsibleでLinuxサーバに接続する際には、事前作業が必要となる。この事前作業については、以下の記事に記載した。

必要な作業内容は以下フローの通りとなる。

この事前の作業自体も手間なので、Ansibleで自動化できないかと考えてみた。

RHEL系ディストリビューション(RHEL 8以前)では、rootによるSSHログインがデフォルトで有効であり、Ansible用のユーザがなくともrootユーザを用いることでAnsibleから接続することが可能となる。そこで、Ansibleの事前作業はrootユーザにて接続して実行し、事前作業完了後は、Ansible用のユーザにて接続できるようにする。

本記事では、AnsibleからLinuxサーバに接続するための事前準備をするPlaybookを作ってみたので紹介する。

環境

今回の動作検証はAlmaLinux 8に対して実施した。ただし、Red Hat系のディストリビューションであるCentOSやRocky Linuxなどでも同様の手順で構築できるだろう。

  • OS : AlmaLinux 8.5
  • Ansible : ansible [core 2.13.1]

Playbookの説明

Playbookのファイルの記載内容は大きく以下のように構成される。

設定項目 説明
hosts 実行対象のサーバを記載する。サーバのリストは別ファイルで作成し、そのファイルの中で実行する対象のサーバやグループを指定する。
vars 変数を記載する。ここで記載した変数は{{ 変数名 }}という形で参照できる。また、変数は配列して複数の要素を持たせることもできる。
tasks Ansibleで実行するメインの処理を記載する。
handlers Ansibleでメイン処理実行状況に応じて実行する事後処理を記載する。例えば、設定ファイル更新がされた場合 (実行結果がchangedの場合) は設定反映のためにサービス再起動を行うといった使い方をする。今回のPlaybookではSSHD設定後のサービス再起動の処理を記載する。

vars

変数は以下表の通り設定する。

変数 説明
ansible_user rootを指定。
ansible_password rootのパスワードを平文またはAnsible Vaultで暗号化して指定する。
os_user.name 作成するAnsible接続用ユーザ名を指定する。今回はansibleuserという名前でユーザを作成する。
os_user.uid ansibleuserを作成する際のUIDを指定する。今回は2001を指定する。
os_user.password Ansible接続用ユーザに指定するパスワードを指定する。パスワードは平文ではなく暗号化されたものを記載する。作成方法は複数あるようだが、ansibleuserユーザを作成済みのサーバがある場合は、そのサーバの/etc/shadowから確認すると手っ取り早い。
control_node_ssh_key 事前にAnsibleコントロールノードにてssh-keygenコマンドで作成したSSH公開鍵である~/.ssh/id_rsa.pubの内容をそのまま指定する。

実際のPlaybookの抜粋を以下に記載する。SSH公開鍵はかなり長い文字列となるが、特に気にならなければ1行で記載しても問題ない。気になる場合は、ダブルクォートで囲んで\を末尾に記載することで、改行して記載することができる。

  vars:
    ansible_user: root
    ansible_password: [rootのパスワード]

    os_user:
      name: ansibleuser
      uid: 2001
      password: '$6$7yLTtwuuOp/T$P06CVjt/yz13CCnS2RDcXXMn/aFDng1n1VWXXnZ7asHR13AOXXqtbEKLprEHphmLjJuOtXXorUYWRprxNhAE2A0'

    control_node_ssh_key: "\
          ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCo/wii2sMw08gntc6OREkVvuuZSpiSUBULmksMdKXAnTqyndaUWDuuzm7hT78GAAAAFCL0/1TlAAAA44QMD5rOtkBQmR\
                    ~(中略)~
          AAAAA1+VbxBBBBy2FATUts9dqFaNpjg9cCt5my3nZe9x8UEXZZiP8ye/7Wev6lHSl2wVU7NPL5N08pCP4qV+MFEIhCcw7T72nAL01W8e4Woz0AwN7QRgKolQzS+z5PuYGFU\
          4LTbEEEEO1eUsOxGFFFFSVHYQM= root@t1025alma"

tasks

タスクは以下表の通りとなる。

タスク名 モジュール 設定
Create ansibleuser user Ansible接続用ユーザを作成。
Add /etc/sudoers lineinfile Ansible接続用ユーザのsudo設定を追加。validatevisudoによる構文チェックをするように構成している。
Copy ssh key lineinfile AnsibleコントロールノードのSSH公開鍵を実行対象サーバのAnsible接続用ユーザのauthorized_keysに追加。
Set sshd setting (PermitRootLogin yes -> no) lineinfile SSHDのPermitRootLoginの設定値を更新し、rootユーザのSSHログインを無効化する。
Check ping by ansible ping module ping Ansible接続用ユーザを用いて、Ansibleより接続ができることを確認する。このタスクはAnsible接続用ユーザで実行する必要があるため、varsを指定しansible_userの値を上書きしている。実行結果は「Show ping result」のタスクで表示する。

実際のPlaybookの記載内容は後述する。

handlers

ハンドラーは以下表の通りとなる。

タスク名 モジュール 設定
Restart sshd service SSHDの設定ファイル修正後にサービスを再起動する。

実際のPlaybookの記載内容は後述する。

Playbook全文

実際のPlaybook全文を以下に記載する。

---
- name: Setup linux ansible settings
  gather_facts: false
  hosts: linux_servers

  vars:
    ansible_user: root
    ansible_password: [rootのパスワード]

    os_user:
      name: ansibleuser
      uid: 2001
      password: '$6$7yLTtwuuOp/T$P06CVjt/yz13CCnS2RDcXXMn/aFDng1n1VWXXnZ7asHR13AOXXqtbEKLprEHphmLjJuOtXXorUYWRprxNhAE2A0'

    control_node_ssh_key: "\
          ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCo/wii2sMw08gntc6OREkVvuuZSpiSUBULmksMdKXAnTqyndaUWDuuzm7hT78GAAAAFCL0/1TlAAAA44QMD5rOtkBQmR\
                    ~(中略)~
          AAAAA1+VbxBBBBy2FATUts9dqFaNpjg9cCt5my3nZe9x8UEXZZiP8ye/7Wev6lHSl2wVU7NPL5N08pCP4qV+MFEIhCcw7T72nAL01W8e4Woz0AwN7QRgKolQzS+z5PuYGFU\
          4LTbEEEEO1eUsOxGFFFFSVHYQM= root@t1025alma"

  tasks:
    # Ansible実行用ユーザ作成
    - name: Create ansibleuser
      ansible.builtin.user:
        name: "{{ os_user.name }}"
        uid: "{{ os_user.uid }}"
        password: "{{ os_user.password }}"
        state: present

    # sudo設定
    - name: Add /etc/sudoers
      ansible.builtin.lineinfile:
        path: /etc/sudoers
        line: '{{ os_user.name }} ALL=(root) NOPASSWD:ALL'
        state: present
        validate: visudo -cf %s

    # SSH公開鍵をコピー
    - name: Copy ssh key
      ansible.builtin.lineinfile:
        path: /home/{{ os_user.name }}/.ssh/authorized_keys
        line: "{{ control_node_ssh_key }}"
        owner: "{{ os_user.name }}"
        group: "{{ os_user.name }}"
        mode: '0600'
        create: true
        state: present

    # rootによるSSHログイン拒否
    - name: Set sshd setting (PermitRootLogin yes -> no)
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
      become: true
      notify: Restart sshd

    # 接続確認
    - name: Check ping by ansible ping module
      ansible.builtin.ping:
      changed_when: false
      register: result
      vars:
        ansible_user: "{{ os_user.name }}"

    - name: Show ping result
      ansible.builtin.debug:
        msg: "{{ result }}"

  handlers:
    # SSHDサービス再起動
    - name: Restart sshd
      ansible.builtin.service: name=sshd state=restarted
      become: true
      changed_when: false

実行結果

実際にPlaybookを実行した結果は以下となる。Show ping resultにてpongが返ってきていれば問題ない。

# ansible-playbook -i hosts -l t1193alma linux/setup_linux_ansible_settings.yml 

PLAY [Setup linux ansible settings] ********************************************************************************

TASK [Create ansibleuser] ******************************************************************************************
changed: [t1193alma]

TASK [Add /etc/sudoers] ********************************************************************************************
changed: [t1193alma]

TASK [Copy ssh key] ************************************************************************************************
changed: [t1193alma]

TASK [Set sshd setting (PermitRootLogin yes -> no)] ****************************************************************
changed: [t1193alma]

TASK [Check ping by ansible ping module] ***************************************************************************
ok: [t1193alma]

TASK [Show ping result] ********************************************************************************************
ok: [t1193alma] => {
    "msg": {
        "changed": false,
        "failed": false,
        "ping": "pong"
    }
}

RUNNING HANDLER [Restart sshd] *************************************************************************************
ok: [t1193alma]

PLAY RECAP *********************************************************************************************************
t1193alma                  : ok=7    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

なお、本Playbook実行後、rootによるSSHログインは拒否されるようになるため、次回以降は本Playbookは処理を実行することができなくなる。
※厳密には、Playbook実行時にSSHの接続情報がキャッシュされるため、実行後60秒以内であればrootユーザによる本Playbookの再実行は可能となる。

# ansible-playbook -i hosts -l t1193alma linux/setup_linux_ansible_settings.yml 

PLAY [Setup linux ansible settings] **********************************************************************************************************************

TASK [Create ansibleuser] ********************************************************************************************************************************
fatal: [t1193alma]: UNREACHABLE! => {"changed": false, "msg": "Invalid/incorrect password: Permission denied, please try again.", "unreachable": true}

PLAY RECAP ***********************************************************************************************************************************************
t1193alma                  : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

以上で、AnsibleからLinuxサーバに接続するための事前準備をするPlaybookの説明は完了となる。

人気の投稿