From d2d5b5f9b6ac56921abc4e616c2c3163fa712b12 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 18 Jul 2023 09:22:49 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=B9=E3=83=88=E3=82=A2?= =?UTF-8?q?=E7=94=A8=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=97=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-restore-backup/.dockerignore | 12 +++ ecs/jskult-restore-backup/.env.example | 9 +++ ecs/jskult-restore-backup/.gitignore | 11 +++ ecs/jskult-restore-backup/.vscode/launch.json | 17 +++++ .../.vscode/recommended_settings.json | 31 ++++++++ ecs/jskult-restore-backup/Dockerfile | 40 ++++++++++ ecs/jskult-restore-backup/Pipfile | 16 ++++ ecs/jskult-restore-backup/Pipfile.lock | 69 +++++++++++++++++ ecs/jskult-restore-backup/README.md | 65 ++++++++++++++++ ecs/jskult-restore-backup/entrypoint.py | 10 +++ .../mysql_dpkg_selection.txt | 3 + ecs/jskult-restore-backup/src/__init__.py | 0 .../src/logging/get_logger.py | 37 ++++++++++ .../src/restore_backup.py | 74 +++++++++++++++++++ .../src/system_var/__init__.py | 0 .../src/system_var/constants.py | 2 + .../src/system_var/environment.py | 14 ++++ 17 files changed, 410 insertions(+) create mode 100644 ecs/jskult-restore-backup/.dockerignore create mode 100644 ecs/jskult-restore-backup/.env.example create mode 100644 ecs/jskult-restore-backup/.gitignore create mode 100644 ecs/jskult-restore-backup/.vscode/launch.json create mode 100644 ecs/jskult-restore-backup/.vscode/recommended_settings.json create mode 100644 ecs/jskult-restore-backup/Dockerfile create mode 100644 ecs/jskult-restore-backup/Pipfile create mode 100644 ecs/jskult-restore-backup/Pipfile.lock create mode 100644 ecs/jskult-restore-backup/README.md create mode 100644 ecs/jskult-restore-backup/entrypoint.py create mode 100644 ecs/jskult-restore-backup/mysql_dpkg_selection.txt create mode 100644 ecs/jskult-restore-backup/src/__init__.py create mode 100644 ecs/jskult-restore-backup/src/logging/get_logger.py create mode 100644 ecs/jskult-restore-backup/src/restore_backup.py create mode 100644 ecs/jskult-restore-backup/src/system_var/__init__.py create mode 100644 ecs/jskult-restore-backup/src/system_var/constants.py create mode 100644 ecs/jskult-restore-backup/src/system_var/environment.py diff --git a/ecs/jskult-restore-backup/.dockerignore b/ecs/jskult-restore-backup/.dockerignore new file mode 100644 index 00000000..8b9da402 --- /dev/null +++ b/ecs/jskult-restore-backup/.dockerignore @@ -0,0 +1,12 @@ +tests/* +.coverage +.env +.env.example +.report/* +.vscode/* +.pytest_cache/* +*/__pychache__/* +Dockerfile +pytest.ini +README.md +*.sql diff --git a/ecs/jskult-restore-backup/.env.example b/ecs/jskult-restore-backup/.env.example new file mode 100644 index 00000000..2bda940a --- /dev/null +++ b/ecs/jskult-restore-backup/.env.example @@ -0,0 +1,9 @@ +DB_HOST=************ +DB_PORT=3306 +DB_USERNAME=************ +DB_PASSWORD=************ +DB_SCHEMA=src05 + +JSKULT_BACKUP_DUMP_S3_FILE_PATH=******************* + +LOG_LEVEL=INFO diff --git a/ecs/jskult-restore-backup/.gitignore b/ecs/jskult-restore-backup/.gitignore new file mode 100644 index 00000000..cf44449e --- /dev/null +++ b/ecs/jskult-restore-backup/.gitignore @@ -0,0 +1,11 @@ +.vscode/settings.json +.env +my.cnf + +# python +__pycache__ + +# python test +.pytest_cache +.coverage +.report/ \ No newline at end of file diff --git a/ecs/jskult-restore-backup/.vscode/launch.json b/ecs/jskult-restore-backup/.vscode/launch.json new file mode 100644 index 00000000..a0178c26 --- /dev/null +++ b/ecs/jskult-restore-backup/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // IntelliSense を使用して利用可能な属性を学べます。 + // 既存の属性の説明をホバーして表示します。 + // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "(DEBUG)jskult batch dbdump", + "type": "python", + "request": "launch", + "program": "entrypoint.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/ecs/jskult-restore-backup/.vscode/recommended_settings.json b/ecs/jskult-restore-backup/.vscode/recommended_settings.json new file mode 100644 index 00000000..b5e79d73 --- /dev/null +++ b/ecs/jskult-restore-backup/.vscode/recommended_settings.json @@ -0,0 +1,31 @@ +{ + "[python]": { + "editor.defaultFormatter": null, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true + } + }, + // 自身の環境に合わせて変えてください + "python.defaultInterpreterPath": "", + "python.linting.lintOnSave": true, + "python.linting.enabled": true, + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, + "python.linting.flake8Args": [ + "--max-line-length=200", + "--ignore=F541" + ], + "python.formatting.provider": "autopep8", + "python.formatting.autopep8Path": "autopep8", + "python.formatting.autopep8Args": [ + "--max-line-length", "200", + "--ignore=F541" + ], + "python.testing.pytestArgs": [ + "tests/batch/ultmarc" + ], + + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/ecs/jskult-restore-backup/Dockerfile b/ecs/jskult-restore-backup/Dockerfile new file mode 100644 index 00000000..3c410b72 --- /dev/null +++ b/ecs/jskult-restore-backup/Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.9 + +ENV TZ="Asia/Tokyo" + +WORKDIR /usr/src/app +COPY Pipfile Pipfile.lock ./ +# mysql-apt-config をdpkgでインストールする際に標準出力に渡す文字列ファイルをコピー +COPY mysql_dpkg_selection.txt ./ +# 必要なパッケージインストール +RUN apt update && apt install -y less vim curl wget gzip unzip sudo lsb-release + +# mysqlをインストール +RUN \ + wget https://dev.mysql.com/get/mysql-apt-config_0.8.25-1_all.deb && \ + dpkg -i mysql-apt-config_0.8.25-1_all.deb < mysql_dpkg_selection.txt && \ + apt update && \ + apt install -y mysql-client + +# aws cli v2 のインストール +RUN \ + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ + unzip awscliv2.zip && \ + sudo ./aws/install + +# python関連のライブラリインストール +RUN \ + pip install --upgrade pip wheel setuptools && \ + pip install pipenv --no-cache-dir && \ + pipenv install --system --deploy && \ + pip uninstall -y pipenv virtualenv-clone virtualenv + +# パッケージのセキュリティアップデートのみを適用するコマンドを実行 +RUN \ + apt install -y unattended-upgrades && \ + unattended-upgrades + +COPY src ./src +COPY entrypoint.py entrypoint.py + +CMD ["python", "entrypoint.py"] diff --git a/ecs/jskult-restore-backup/Pipfile b/ecs/jskult-restore-backup/Pipfile new file mode 100644 index 00000000..f032c7ea --- /dev/null +++ b/ecs/jskult-restore-backup/Pipfile @@ -0,0 +1,16 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] +autopep8 = "*" +flake8 = "*" + +[requires] +python_version = "3.9" + +[pipenv] +allow_prereleases = true diff --git a/ecs/jskult-restore-backup/Pipfile.lock b/ecs/jskult-restore-backup/Pipfile.lock new file mode 100644 index 00000000..f948e3df --- /dev/null +++ b/ecs/jskult-restore-backup/Pipfile.lock @@ -0,0 +1,69 @@ +{ + "_meta": { + "hash": { + "sha256": "cc5f54bfb2073051a26f113ceac64e12fdd0bf8faa36f1a42210cc9c921c134b" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "autopep8": { + "hashes": [ + "sha256:86e9303b5e5c8160872b2f5ef611161b2893e9bfe8ccc7e2f76385947d57a2f1", + "sha256:f9849cdd62108cb739dbcdbfb7fdcc9a30d1b63c4cc3e1c1f893b5360941b61c" + ], + "index": "pypi", + "version": "==2.0.2" + }, + "flake8": { + "hashes": [ + "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7", + "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181" + ], + "index": "pypi", + "version": "==6.0.0" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053", + "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610" + ], + "markers": "python_version >= '3.6'", + "version": "==2.10.0" + }, + "pyflakes": { + "hashes": [ + "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf", + "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.1" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + } + } +} diff --git a/ecs/jskult-restore-backup/README.md b/ecs/jskult-restore-backup/README.md new file mode 100644 index 00000000..f955ff97 --- /dev/null +++ b/ecs/jskult-restore-backup/README.md @@ -0,0 +1,65 @@ +# 実消化&アルトマークダンプ復元スクリプト + +## 概要 + +実消化&アルトマークダンプ復元スクリプト + +## 環境情報 + +- Python 3.9 +- MySQL 8.23 +- VSCode + +## 環境構築 + +- Python の構築 + + - Merck_NewDWH 開発 2021 の Wiki、[Python 環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照 + - 「Pipenv の導入」までを行っておくこと + - 構築完了後、プロジェクト配下で以下のコマンドを実行し、Python の仮想環境を作成する + - `pipenv install --dev --python ` + - この手順で出力される仮想環境のパスは、後述する VSCode の設定手順で使用するため、控えておく + +- MySQL の環境構築 + - Windows の場合、以下のリンクからダウンロードする + - + - Docker を利用する場合、「newsdwh-tools」リポジトリの MySQL 設定を使用すると便利 + - 「crm-table-to-ddl」フォルダ内で以下のコマンドを実行すると + - `docker-compose up -d` + - Docker の構築手順は、[Docker のセットアップ手順](https://nds-tyo.backlog.com/alias/wiki/1754332)を参照のこと + - データを投入する + - 立ち上げたデータベースに「src05」スキーマを作成する + - [ローカル開発用データ](https://ndstokyo.sharepoint.com/:f:/r/sites/merck-new-dwh-team/Shared%20Documents/03.NewDWH%E6%A7%8B%E7%AF%89%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA3/02.%E9%96%8B%E7%99%BA/90.%E9%96%8B%E7%99%BA%E5%85%B1%E6%9C%89/%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF?csf=1&web=1&e=VVcRUs)をダウンロードし、mysql コマンドを使用して復元する + - `mysql -h <ホスト名> -P <ポート> -u <ユーザー名> -p src05 < src05_dump.sql` +- 環境変数の設定 + - 「.env.example」ファイルをコピーし、「.env」ファイルを作成する + - 環境変数を設定する。設定内容は PRJ メンバーより共有を受けてください +- VSCode の設定 + - 「.vscode/recommended_settings.json」ファイルをコピーし、「settings.json」ファイルを作成する + - 「python.defaultInterpreterPath」を、Python の構築手順で作成した仮想環境のパスに変更する + +## 実行 + +- VSCode 上で「F5」キーを押下すると、バッチ処理が起動する。 +- 「entrypoint.py」が、バッチ処理のエントリーポイント。 +- 実際の処理は、「src/restore_backup.py」で行っている。 + +## フォルダ構成 + +```txt +. +├── .env.example -- 環境変数のサンプル値 +├── Dockerfile -- Dockerイメージ作成用 +├── Pipfile -- pythonの依存関係管理 +├── Pipfile.lock -- 依存関係バージョン固定 +├── README.md -- 当ファイル +├── entrypoint.py -- エントリーポイントとなるファイル +├── mysql_dpkg_selection.txt -- Dockerイメージでdpkgを使うときに外部から注入する選択値 +└── src -- ソースコードフォルダ + ├── logging + │ └── get_logger.py -- ロガー + ├── restore_backup.py -- dump復元処理本体 + └── system_var + ├── constants.py -- 定数ファイル + └── environment.py -- 環境変数ファイル +``` diff --git a/ecs/jskult-restore-backup/entrypoint.py b/ecs/jskult-restore-backup/entrypoint.py new file mode 100644 index 00000000..2bacdf0c --- /dev/null +++ b/ecs/jskult-restore-backup/entrypoint.py @@ -0,0 +1,10 @@ +"""実消化&アルトマーク 日次バッチ処理前DBダンプ取得のエントリーポイント""" +from src import restore_backup + +if __name__ == '__main__': + try: + exit(restore_backup.exec()) + except Exception: + # エラーが起きても、正常系のコードで返す。 + # エラーが起きた事実はbatch_process内でログを出す。 + exit(0) diff --git a/ecs/jskult-restore-backup/mysql_dpkg_selection.txt b/ecs/jskult-restore-backup/mysql_dpkg_selection.txt new file mode 100644 index 00000000..d3cb46f9 --- /dev/null +++ b/ecs/jskult-restore-backup/mysql_dpkg_selection.txt @@ -0,0 +1,3 @@ +1 +1 +4 \ No newline at end of file diff --git a/ecs/jskult-restore-backup/src/__init__.py b/ecs/jskult-restore-backup/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/jskult-restore-backup/src/logging/get_logger.py b/ecs/jskult-restore-backup/src/logging/get_logger.py new file mode 100644 index 00000000..f36f1199 --- /dev/null +++ b/ecs/jskult-restore-backup/src/logging/get_logger.py @@ -0,0 +1,37 @@ +import logging + +from src.system_var.environment import LOG_LEVEL + +# boto3関連モジュールのログレベルを事前に個別指定し、モジュール内のDEBUGログの表示を抑止する +for name in ["boto3", "botocore", "s3transfer", "urllib3"]: + logging.getLogger(name).setLevel(logging.WARNING) + + +def get_logger(log_name: str) -> logging.Logger: + """一意のログ出力モジュールを取得します。 + + Args: + log_name (str): ロガー名 + + Returns: + _type_: _description_ + """ + logger = logging.getLogger(log_name) + level = logging.getLevelName(LOG_LEVEL) + if not isinstance(level, int): + level = logging.INFO + logger.setLevel(level) + + if not logger.hasHandlers(): + handler = logging.StreamHandler() + logger.addHandler(handler) + + formatter = logging.Formatter( + '%(name)s\t[%(levelname)s]\t%(asctime)s\t%(message)s', + '%Y-%m-%d %H:%M:%S' + ) + + for handler in logger.handlers: + handler.setFormatter(formatter) + + return logger diff --git a/ecs/jskult-restore-backup/src/restore_backup.py b/ecs/jskult-restore-backup/src/restore_backup.py new file mode 100644 index 00000000..da481eaf --- /dev/null +++ b/ecs/jskult-restore-backup/src/restore_backup.py @@ -0,0 +1,74 @@ +"""実消化&アルトマークダンプ復元スクリプト""" + +import os +import re +import subprocess +import textwrap + +from src.logging.get_logger import get_logger +from src.system_var import constants, environment + +logger = get_logger('実消化&アルトマークダンプ復元スクリプト') + + +def exec(): + try: + logger.info('実消化&アルトマークダンプ復元スクリプト:開始') + + # MySQL接続情報を作成する + my_cnf_file_content = f""" + [client] + user={environment.DB_USERNAME} + password={environment.DB_PASSWORD} + host={environment.DB_HOST} + """ + # my.cnfファイルのパス + my_cnf_path = os.path.join('my.cnf') + + # my.cnfファイルを生成する + with open(my_cnf_path, 'w') as f: + f.write(textwrap.dedent(my_cnf_file_content)[1:-1]) + + # 復元対象のダンプファイルを特定 + s3_file_path = environment.JSKULT_BACKUP_DUMP_S3_FILE_PATH + + # S3 URIになっているか確認 + s3_file_path_pattern = r"s3://([\w-]+)/dump/(\d{4})/(\d{2})/(\d{2})/backup_rds_(\d+).gz" + if re.match(s3_file_path_pattern, s3_file_path) is None: + logger.warning('環境変数[JSKULT_BACKUP_DUMP_S3_FILE_PATH]に指定された、S3 URIの形式が不正です。') + return constants.BATCH_EXIT_CODE_SUCCESS + + # aws s3 cpコマンドを実行してdumpファイルを標準出力に取得する + s3_cp_process = subprocess.Popen(['aws', 's3', 'cp', s3_file_path, '-'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # gunzipコマンドを実行してdumpファイルを解凍する + gunzip_process = subprocess.Popen(['gzip', '-c'], stdin=s3_cp_process.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # mysqldumpコマンドを実行し、dumpを取得する + mysql_process = subprocess.Popen( + ['mysql', f'--defaults-file={my_cnf_path}', '-P', f"{environment.DB_PORT}", environment.DB_SCHEMA], + stdin=gunzip_process.stdout, stderr=subprocess.PIPE + ) + # aws s3 cpの標準出力をgunzipに接続したため、標準出力をクローズする + s3_cp_process.stdout.close() + # gunzipの標準出力をaws mysqlに接続したため、標準出力をクローズする + gunzip_process.stdout.close() + + # パイプラインを実行し、エラーハンドリング + _, error = s3_cp_process.communicate() + if s3_cp_process.returncode != 0: + logger.error(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + _, error = gunzip_process.communicate() + if gunzip_process.returncode != 0: + logger.error(f'`gunzip`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + _, error = mysql_process.communicate() + if mysql_process.returncode != 0: + logger.error(f'`mysql`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + + logger.info('[NOTICE]実消化&アルトマークダンプ復元スクリプト:終了(正常終了)') + return constants.BATCH_EXIT_CODE_SUCCESS + + except Exception as e: + logger.exception(f'実消化&アルトマークダンプ復元スクリプト中に想定外のエラーが発生しました :{e}') + return constants.BATCH_EXIT_CODE_SUCCESS diff --git a/ecs/jskult-restore-backup/src/system_var/__init__.py b/ecs/jskult-restore-backup/src/system_var/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/jskult-restore-backup/src/system_var/constants.py b/ecs/jskult-restore-backup/src/system_var/constants.py new file mode 100644 index 00000000..8a555af3 --- /dev/null +++ b/ecs/jskult-restore-backup/src/system_var/constants.py @@ -0,0 +1,2 @@ +# バッチ正常終了コード +BATCH_EXIT_CODE_SUCCESS = 0 diff --git a/ecs/jskult-restore-backup/src/system_var/environment.py b/ecs/jskult-restore-backup/src/system_var/environment.py new file mode 100644 index 00000000..4ab083e0 --- /dev/null +++ b/ecs/jskult-restore-backup/src/system_var/environment.py @@ -0,0 +1,14 @@ +import os + +# Database +DB_HOST = os.environ['DB_HOST'] +DB_PORT = int(os.environ['DB_PORT']) +DB_USERNAME = os.environ['DB_USERNAME'] +DB_PASSWORD = os.environ['DB_PASSWORD'] +DB_SCHEMA = os.environ['DB_SCHEMA'] + +# AWS +JSKULT_BACKUP_DUMP_S3_FILE_PATH = os.environ['JSKULT_BACKUP_DUMP_FILE_PATH'] + +# 初期値がある環境変数 +LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')