pleasanter+PostgreSQL+SSL+dockerで自動バックアップを作ってみた

# 概要

- [前回記事](https://qiita.com/yamada28go/items/3ebd4ed43e97cf3ca0e5)の続きです。
- 本格的にシステムを使う事を考えた場合、バックアップはちゃんと考えておく必要が有ります。
- 今回は自動でバックアップできるようにシステム化しました。
- 対応できるバックアップ種別は「Fullバックアップ」と「PITR(ポイント・イン・タイム・リカバリ)」です。
- AWS S3の設定を入れておくことで、バックアップデータをS3に同期する事も出来ます。

# コンテナ構成

バックアップ作業用にコンテナが追加になっています。
追加されたコンテナを下表に示します。
postgres-dbとcron-backupはDBの領域を共有しており、
PITRが使える状態になっています。

| コンテナ名 | 概要 |
|:-----------|:------------|
| cron-backup     | バックアップ用クーロンコンテナ      | 
| postgres-db       | PostgreSQL DB        | 
| pleasanter-web| PleasanterのWebシステム        | 

# バックアップスクリプト

自動バックアップ機構に使用されるスクリプトは以下となります。
これらのスクリプトはpcron-backupコンテナに配備されます。

| スクリプト名 | 概要 |
|:-----------|:------------|
|   /var/backup_sh/pg_rman.sh   |   PITRを実行   | 
|   /var/backup_sh/pg_dumpall.sh   | Full Backupを実行       | 
|   /var/backup_sh/pg_dumpall.sh   | S3同期を実行      | 


# バックアップ設定
## ディレクトリ構成
pcron-backupコンテナから見たディレクトリ構成し下表となります。
共有が〇となっているパスに関してはpostgres-dbコンテナと共有しています。

バックアップ結果のデータは「/var/db_backup」に集まってくるように出来ています。
このため、このディレクトリをコンテナ外の接続してバックアップを取得しておけば必要なコンテナの外側からバックアップは取れる状態になります。

| パス名 | 概要 |コンテナ間共有 |
|:-----------|:------------|:------------|
| /var/db_backup/PITR/       |    ポイント・イン・タイム・リカバリ用のバックアップディレクトリ    | |
| /var/db_backup/dumpall     | 全データバックアップ用のディレクトリ      | |
| /var/backup_sh| バックアップ用Shell格納ディレクトリ        | |
| /var/| バックアップ用Shell格納ディレクトリ        | |
| /var/lib/postgresql/data/|   postgresqlデータ領域    |〇  | 
| /var/lib/postgresql/arclog| postgresqlWAL領域       | 〇| 

## 取得頻度

バックの動作管理はcronにより制御されています。
該当cromの設定は「cron-backup\config\crontab」にあります。
初期状態では以下の指定になっています。
必要に応じて、対象時間を切り替えてください。

| 種別 | 頻度 |
|:-----------|:------------|
|全バックアップ|毎日午前3時に1回|
|PITR(ポイント・イン・タイム・リカバリ)|30分毎|

## バックアップの開始

これらバックアップスクリプトはDocker Composeを起動すると自動的に起動を開始します。
開始にあたり、何か特別な操作は不要です。

逆にバックアップを取りたくない場合、該当のコンテナを起動しないように

# バックアップ設定　オプション機能　S3同期
## 概要

ここまでのバックアップ機能だけですと、何かあってコンテナが飛んだ時にデータを損失する危険性があります。
バックアップはどこか別の場所に確保しておく必要があります。
現代は、S3という便利なものかあるのでそこに自動同期するようにつくってあります。

## 有効化

AWS CLIの設定が存在する時に有効化されます。
有効化判定の対象となるパスし以下です。

「/root/.aws/config」

この設定ファイルはconfigとしてコンテナ内部にコピーさせるよりは、コンテナの外に置いておいてマウントさせるほうが楽です。
(以下の★の部分です。)

```yaml:docker-compose.yml
  cron-backup:
    build:
      context: cron-backup/.
    volumes:
      - *APP_DB_data
      - *APP_DB_arclog
      # 自動バックアップされた結果は「/var/db_backup」に格納される。
      # コンテナの外からアクセスする場合は、このパスを外から見える所に配置しておく
      - db-backup:/var/db_backup
      # S3で自動バックアップする場合
      # aws cliの設定を以下パスに行う。
      # 存在しない場合はバックアップは行われない
      - ./cron-backup/config/aws-cli:/root/.aws/ ←　★
```

## 設定

ホストがWindows環境の場合、GITの設定によっては、**設定ファイルがCRLF**で取得される事があります。
設定ファイルをその状態でコンテナ内に持っていくと**改行コードが合わなくて正しく動かない**ので、設定ファイルの改行コードの取り扱いには注意してください。

まず、AWS CLIの設定が必要です。
このあたりは一般的な設定なので、ご自分のアカウントから必要な情報を取得してください。

```config:cron-backup\config\aws-cli\credentials
[default]
aws_access_key_id = アクセスキー
aws_secret_access_key = シークレットキー 
```

次に、バックアップ対象の設定を行います。
これはshell形式の設定となります。


```bash:cron-backup\config\aws-cli\S3Config.sh
#!/bin/bash

# --- S3 同期設定

# 同期対象となる S3 バケット名
export S3_TARGET_BUCKET_NAME={バケット名}

# 同期対象となる S3 ディレクトリ名
export S3_TARGET_DIRECTORY_NAME={ディレクトリ名}
```

## 実行タイミング

S3への同期はバックアップ処理が実行されたタイミングとなります。
設定が正しくできていれば、開始のために特別な設定は不要です。

## セーフモード

S3への同期はaws cliの「rsync」コマンドのdeleteオプション付きで行われます。
このコマンドを用いた場合、**同期先のディレクトリは同期元のディレクトリと同一**になるように動作します。
ここで、同一とはつまり**「同期元になくて同先先にあるファイルは消去される」**という意味です。
新規に環境を作った直後に設定を間違えてこのバックアップが動いてしまうと、必要なデータが消えてしまい大変な事になります。

そこで、S3のバックアップ処理にはセーフモードが付いています。
セーフモードでは、S3の対象バケットの対象ディレクトリに既存のコンテナがもっているバックアップ系統とは異なるデータが確認された場合、上書きはせずに「ディレクトリ名-safe」というディレクトリを作ってそちら側にバックアップを行うという処理となります。
バックアップ系統の同一性確認には、バックアップ時に生成される「BackupFiles.sha256.7z」というファイルを参照して実施します。

基本的には、ユーザーはあまり気にする必要はありませんが、**環境移行の時などは**既存のバックアップを壊してしまわないように十分注意して実行してください。
また、初回バックアップ実施後は「ディレクトリ名-safe」のようなディレクトリが出来ていないか確認してください。

----


# リストア

リストアに関しては、
バックアップデータの取得方法により戻し方が変わってきます。

## 全バックアップで取得した場合

以下、cron-backupコンテナで作業した事とします。
バックアップファイルは7z形式、かつ、暗号化されています。
適宜復元してください。
(暗号キーに関してはshellファイル「cron-backup\shell\pg_dumpall.sh」を参照してください)
以下コマンドは、復元されたファイルが「backup」という名称となっている事を前提としています。
全バックアップからの復元は以下コマンド一発で完了です。

``` bash
 psql -h postgres-db -p 5432  -U postgres -f backup
```

## PITR

PITRの場合、少しややこしいです。
PITRの場合、postgresが動いているとうまく復元できないため、
cron-backupコンテナだけ起動させます。

```
docker-compose up pleasanter-cron-backup
```

### 手順0 . データコピー

pleasanter-cron-backupコンテナで実施します。
復元に使用するデータを用意します。
復元に用いるデータは「/var/db_backup/PITR/」にコピーしてください。

### 手順1 . クーロン停止

pleasanter-cron-backupコンテナで実施します。
クーロンを普通に起動しておくとバックアップクーロンがデータのバックアップを開始してしまいます。
そこで、バックアップクーロン停止しておきます。

```
crontab -l > my_cron_backup.txt
crontab -r
```
### 手順2 . バックアップ済みデータ確認

pleasanter-cron-backupコンテナで実施します。
以下コマンドで取得済みのバックアップ一覧を表示します。

```
/usr/lib/postgresql/12/bin/pg_rman show  -B /var/db_backup/PITR/
```

コマンドを実行すると以下のようにバックアップとして戻すことが出来るタイミングの一覧が表示されます。

```
root@885777f50d56:~/out/cron-backup/shell# /usr/lib/postgresql/12/bin/pg_rman show  -B /var/db_backup/PITR/
=====================================================================
 StartTime           EndTime              Mode    Size   TLI  Status
=====================================================================
2020-09-22 21:44:35  2020-09-22 21:44:38  INCR    33kB     3  OK
2020-09-22 21:43:32  2020-09-22 21:43:35  INCR    33kB     3  OK
2020-09-22 21:40:54  2020-09-22 21:40:58  INCR    33kB     3  OK
2020-09-22 21:39:32  2020-09-22 21:39:35  INCR    33kB     3  OK
2020-09-22 21:38:47  2020-09-22 21:38:51  INCR    33kB     3  OK
2020-09-22 21:32:47  2020-09-22 21:36:18  FULL    61MB     3  OK
```

### 手順3 . リストア

pleasanter-cron-backupコンテナで実施します。
PITRの場合、戻し方には二つのパターンがあります。

#### 最終バックのタイミングに戻す

以下コマンドを用います。


```
/usr/lib/postgresql/12/bin/pg_rman restore  -B /var/db_backup/PITR/ -D /var/lib/postgresql/data/
```

#### 任意タイミングに戻す

任意タイミングに戻す場合、戻す対象の時間を指定する事が可能です。
コマンドは以下となり、「recovery-target-time」として対象時間を指定します。

```
/usr/lib/postgresql/12/bin/pg_rman restore  --recovery-target-time '2020-09-06 14:29:00'  -B /var/db_backup/PITR/ -D /var/lib/postgresql/data/
```

### 手順6 . クーロン戻し

pleasanter-cron-backupコンテナで実施します。
クーロンの設定を元に戻します。

```
crontab my_cron_backup.txt
crontab -l
```

## DBの整合性チェック


ここの処理は「全バックアップ」、「PITR」のいずれから戻した場合においても共通で必要となります。
バックアップ処理ではシステム動作中にデータを抜いてきています。
このため、DB上に存在するセッション情報などが中途半端な状態で残っています。

以下コマンドを使って、DB上の不要なデータを一度クリーンします。
(docker ホストで実行します。)

```
docker ps | grep leasanter-web | cut -d' ' -f 1  | xargs -I {} docker exec {} cmdnetcore/codedefiner.sh
```

## 後処理

DBを戻したら後処理が必要です。

### 速やかなFUllバックアップ

動くことを確認したら速やかにFullバックアップを取りましょう。
以下コマンドを直接叩く事でFullバックアップの取得が可能です。
ただし、S3設定がされている場合、S3への同期も走るので、十分注意して実施してください。

「/var/backup_sh/pg_dumpall.sh」
 
### PITRにおけるベースバックアップの再取得

バックアップから戻した後の場合、PITRではベースバックアップがそれまでに取得されているものと変わっている可能性があ要ります。
(PITRでは、ベースバックアップに差分を重ねるという取り方をするが、リストアによりベースバックアップと最新のDB状態がすれた場合、後続のバックアップ処理が正しく実施できなくなる。)
そこで、ベースバックアップの再取得が必要となります。

1. 「/var/db_backup/PITR/」パスにPITRのバックアップデータがあります。これを移動させます。
2. 「/var/backup_sh/pg_rman.sh」コマンドを使って、バックアップデータが空の状態で再度バックアップを実施します。(S3同期するので注意が必要)


# 参考

[OSC北海道2014_JPUG資料 ](https://www.slideshare.net/satock/osc-hokkaido2014-backuprecovery)
[MastodonのPostgreSQLをpg_rmanを使ってバックアップする](https://blog.misosi.ru/2017/04/24/backup-postgresql-by-pg_rman-with-docker/)
[pg_rman](http://ossc-db.github.io/pg_rman/index-ja.html)
[pg_rmanを使ってみる](https://think-t.hatenablog.com/entry/2014/06/08/163035)
[pg_rmanによるPostgreSQLの簡単バックアップ＆リカバリ](https://qiita.com/bwtakacy/items/84a446c642ffae76859b)