サポートの作業を自動化していく(第一回 Ansible編)


はじめに

第一回目はAnsibleとDockerを使って、DBセットアップを自動化して楽をします。
次回以降はStackStormを使ったAPI化(って言って良いのかな?)やSlack連携でのSSH接続無しOperation等を扱っていく予定です。

背景と目的

背景

弊社ではクラウド基盤システムの展開、及びそのサポートサービスを実施しています。自分はCloud Management System(以下、CMS)のサポートを行っておりますが、お客様からの問い合わせの際に様々なログファイルの分析をしたり、DBの確認をしたりしています。
このときにDBのDumpファイルを扱いますが、こちらの処理に時間を取られることがよくあります。

DB取り込みの流れ

お客様から問い合わせを頂いた際にはコールセンター担当者が、技術センターのファイルサーバへファイルを保存します。
その後、コールセンターからエンジニアへ、ファイルの保管場所が通知されます。
エンジニアは通知された保管場所からファイルをコピーし、分析やDBへの取り込みを行います。

ちなみに、弊社ではDCに設置されたVDIを利用しています。
そのため、技術センターからVDI環境へのファイルコピーは、技術センターからDCへファイルコピーを行っていることになります。更にDBサーバは技術センターに設置されているので、DBの取り込みの場合にはまた技術センターからDCへファイルを戻すといったムダが出ています。

DB確認環境

複数のDBを並行して確認する必要性や、すぐに最新のDBの取り込みを行うため、DBサーバはDockerにて立ち上げています。
最新のDB Dumpを受領したら、古いものを破棄して新規のMySQLを立ち上げています。
MySQLのアクセスはご多分に漏れずPort番号で割り振っています。

この立ち上げの作業は、アーカイブファイルの解凍、コンテナの新規作成、DB Dumpの取り込みと、意外と時間が掛かるものが並んでいます。
それぞれのパラメータ指定も意外とめんどくさいです…

目的

このDBの取り込み作業を"まずは"自動化します。

自動化すること

  • コールセンターから連絡を受けた保存場所からファイルを取得
  • 圧縮されたファイルを展開
  • コンテナを作成
  • DB Dumpの取り込み

時間のかかる処理を自動で実施させることで、DBの取り込み処理を指定いる間は別なことをできるようにします。

今回はやらないこと

  • SSHを使わないためのトリガーの作成

ココらへんはStackStormを使ってWebhookで叩いたり、Slack Botから叩いたりする仕組みを作りたいので、後回しにします。

手順

1.DBサーバにファイルサーバの共有フォルダをマウント

autofsを使ってWindowsServerの共有フォルダをマウントします。
ここは我流の設定なので、ちゃんとした設定を調べてもらったほうがいいです。

2.WindowsでのパスからLinuxでのパスへの置き換え

今回の処理で一番めんどくさかった処理がWindowsからLinuxへのパスの置き換えです。
Windowsではフォルダ構造を表すのに"\"を使います。一方、Linuxでは"/"を用います。
Ansibleでは正規表現を用いて置き換えができますが、ちょっと癖がありました。

以下の例では、変数を多用してパスの置き換えを実現しています。
base_path: コールセンターから受け取った共有フォルダのパス(この下に受領したファイルが入っています)
replace_path: 正規表現を用いてWindows用のパスに含まれる"\"を"/"へ置き換えます。"\"は特殊文字となっている関係上、いつものノリで使うとクォーテーションがおかしいなどのエラーが出まくります。Jinja2のクォーテーションを""ではなく、''でくくってやるとうまく動くみたいです。
samba_path: 共有フォルダのパスをマウントされた(=Local)パスへ置き換えます。"\"で一回ハマっているので、こっちはすんなりできました。

var設定
  vars:
    replace_path: '{{ base_path | regex_replace("\\","/") }}'
    samba_path: '{{ replace_path | replace("//IPアドレス/share/お客様取得ログフォルダ","/mnt/logs") }}'
    tmp_path: "/tmp"
    dumpfile: "/tmp/sqldump.sql"

3.コンテナの起動と、DBの取り込み

当初想定していた処理手順は、人がやっていた処理の手順通りにアーカイブファイルの解凍→コンテナ起動→DB Dumpの取り込みの流れでした。しかし、この流れではコンテナの起動ラグでMySQLサービスが上がりきらずに、DB Dump取り込みが失敗するケースがありました。コンテナ起動はバックグラウンド実行できるため、うまいこと使って起動処理を隠蔽します。

改善した処理の流れは、コンテナ起動→アーカイブファイルの解凍→DB Dumpの取り込みにしています。
コンテナ起動処理中のMySQLのサービス起動時間を、アーカイブファイルの解凍処理(とPause処理)で隠蔽しています。Pauseは様子を見ながら外してもいいかな…
アーカイブファイルの解凍処理では、DB Dumpがtgzでアーカイブされていることと、基本的には1フォルダに1 Dumpファイルになることを利用して、tgzファイルを自動取得しています。(※力技含む)
findモジュールの返り値はList形式になるので、with_itemでループを回して値を取り出します(でも1ファイルなので1回だけ)

task設定
  tasks:
   - name: Start SQL Container
     docker_container:
      name: "{{ container_name }}"
      image: mysql
      ports: "{{ port }}:3306"
      env:
          MYSQL_ROOT_PASSWORD: mysql

   - name: Find SQL Dump archive
     find:
      paths: "{{ samba_path }}"
      patterns: "^.*sqldump.*\\.tzg$"
      use_regex: yes
     register: sqlfile

   - name: Extract TGZ file
     shell: "tar -xzf {{ item.path }} -C {{ tmp_path }}"
     with_items:
      - "{{ sqlfile.files}}"

   - name: Wait to Start Container
     pause:
      seconds: 10

   - name: Import Dump file
     shell: "mysql -h 127.0.0.1 -P {{ port }} -u root -pmysql mysql < {{ dumpfile }}"
     failed_when: False

ファイルをまとめるとこんな感じのPlaybookが出来上がります。

create_sql.yml
---
- hosts: 127.0.0.1
  vars:
    replace_path: '{{ base_path | regex_replace("\\","/") }}'
    samba_path: '{{ replace_path | replace("//IPアドレス/share/お客様取得ログフォルダ","/mnt/logs") }}'
    tmp_path: "/tmp"
    dumpfile: "/tmp/sqldump.sql"

  tasks:
   - name: Start SQL Container
     docker_container:
      name: "{{ container_name }}"
      image: mysql
      ports: "{{ port }}:3306"
      env:
          MYSQL_ROOT_PASSWORD: mysql

   - name: Find SQL Dump archive
     find:
      paths: "{{ samba_path }}"
      patterns: "^.*sqldump.*\\.tzg$"
      use_regex: yes
     register: sqlfile

   - name: Extract TGZ file
     shell: "tar -xzf {{ item.path }} -C {{ tmp_path }}"
     with_items:
      - "{{ sqlfile.files}}"

   - name: Wait to Start Container
     pause:
      seconds: 10

   - name: Import Dump file
     shell: "mysql -h 127.0.0.1 -P {{ port }} -u root -pmysql mysql < {{ dumpfile }}"
     failed_when: False

4.いざ実行

今回作成したものの実行はLinux上のコマンドラインで実行します。(今の所、)共有フォルダのパスや、ポート番号、コンテナ名は外部ファイルから読み込みます。コマンド一発でファイルの取得からコンテナの立ち上げまで自動でやってくれるようになりました。

Playbookの実行
# ansible-playbook create_sql_container.yml --extra-vars="@db_vars.yml"
変数用ファイルの例
---
container_name: sql1
port: 3307
base_path: \\Pアドレス\share\お客様取得ログフォルダ\20180622

5.おまけ

削除も自動化したかったので、こっちも作ってあります。Playbookにするほどの処理してないねー。
引数は作成用に作ったのを流用できます。

delete_sql_container.yml
---
- hosts: 127.0.0.1
  tasks:
   - name: Remove SQL Container
     docker_container:
      name: "{{ container_name }}"
      state: absent
      keep_volumes: false
Playbookの実行(削除編)
# ansible-playbook delete_sql_container.yml --extra-vars="@db_vars.yml"

まとめ/次回予告

人々の手を煩わせていたDB構築は潰えた。だがそれは自動化との戦いの幕開けでしか無かった。連綿と続く無駄な作業を前にKanno-sanが立ち上がる。次回「Webhook」。API化の幕が開く。