Kuchitama Tech Note

はてな記法がいつまでたっても覚えられないので、はてなダイアリーからマークダウンが使えるこっちに引っ越してきました。

MakerelでLamdaのColdStartを監視する、をLamdaでやる

この記事はMackerelアドベントカレンダー7日目の記事です。 こんにちは、MackerelアンバサダーのKuchitamaです。 昨日の記事は、 ore-publicさんの運用中に止めたり上げたりするプロセスをMackerelで監視するでした。

本来、「Makerelでエンジニアの退職後も定期バッチをヘルシーに保つTips(仮)」という記事を書く予定でしたが、12/23(月)の MackerelDay#2 のアンバサダーLTで話すことになったので、予定を変更してお送りしております。 元々の内容にご興味あったかたは是非会場にいらしてください。

mackerelio.connpass.com

AWS Lamdaのモニタリング

さて本題。 AWS Lamdaの監視については、 Mackarelでは AWSインテグレーションで実現できます。

mackerel.io

ただ、実運用において重要なメトリクスがありません。 そう、 cold start に関するログです。

cold startの発生率や発生した際の処理時間は、様々な条件で変化します。 そのため、AWS Lambdaを利用する人は様々な方法で、cold startを回避する、もしくは発生時の処理時間を短く抑えています。 この点では非常に多くの施策があるのですが、いざ自分で試すとなると、そういった施策の必要性、緊急性、効果の有無やその大小を計測する必要があります。

そこで、Makerel上でcold startのメトリクスを見れるようにしました。

今回の成果物はGitHubに公開しています。

https://github.com/Kuchitama/mackerel-cloudwatch-logs-metric

cold startの情報はどこにあるか

そもそも、cold startの情報はどこから確認できるのか。 AWS上で確認する方法は2つあります。

  • AWS X-Rray
  • AWS CloudWatch Logs

X-Ray

X-RayAWS上のリクエストをトレースするサービスです。

X-Rayで cold start が発生するリクエストを確認すると、下図のように Initialization という項目が表示されます。 この Initialization が、先の図にある Partial cold start に要した時間を指しています。逆にcold startが起こらない場合、Initializationの項目は表示されません。

https://image.slidesharecdn.com/2018-0205-srv-without-notes-c3782fb1-bc5e-4dd3-9f64-71c276338692-1466883780-180222233128/95/become-a-serverless-black-belt-optimizing-your-serverless-applications-aws-online-tech-talks-57-638.jpg?cb=1519342337

ただし、 X-Ray でこの情報を確認するには、sampling設定を調整して、 cold startが発生したリクエストがサンプリングされている必要があります。 監視という点では、X-Rayは不向きだと言えるでしょう。

CloudWatch Logs

もう一つ、cold startの情報が出力される場所があります。そう、Lambdaの実行ログです。

f:id:kuchitama:20191207135539p:plain
Terminal出力

この実行ログがどこに保存されるかというと、 AWS CloudWatch Logsに保存されています。

f:id:kuchitama:20191207135539p:plain
CloudWatchLogs

そして、 CloudWatch Logsは Insights という機能で、 ログに対してクエリを発行し、パース、フィルター、集計が可能です。

クエリの発行と結果の取得はAPIが提供されているので、プログラムに落とし込むのも容易です。

https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_Operations.html

  • StartQuery
  • GetQueryResult

今回は、この機能を使って cold start発生時に Partial cold start にかかる平均時間を取得し、 Mackerelに送信します。

どこでメトリクスを取得するか

severlessアーキテクチャを採用しているのに、監視のためにサーバを用意するのも憚られます。 監視プログラムの実行はLambdaに載せてしまうことにしましょう。 幸い、Lambdaからメトリック送信をされている方はすでにいらっしゃいました。

kmiya-bbm.hatenablog.com

こちらを参考にしつつ、 mkr のバイナリはDockerから取得することにしました。 リポジトリ内の prepare.sh を実行すると、Linux用の mkrバイナリがリポジトリ内に作成されます。

処理の流れ

Lamda function で実行する一連の処理は全て handler.js にまとめています。

Lambda FunctionqueueLambda FunctionqueueCloudWatchLogsCloudWatchLogsDynamoDBDynamoDBLambda FunctiondequeueLambda FunctiondequeueMackerelMackerelstartQueryreturn queryIdput queryIdscanreturn recordsdeletegetQueryResultresultcall "mkr throw"Post metrics

CloudWatchLogs Insight に投げたクエリは非同期に実行されるので、startQuery と getQueryResult の関数を分けています。 queue内で、startQueryで取得した queryId を DynamoDB に保存し、dequeue で読み込み getQueryResultで結果を取得、その結果をMakerelに送信しています。 あらかじめ、Makerelの AWSインテグレーションで、Lamdaの基本的な監視はできているので、今回は同じサービスに対してサービスメトリックとして投稿しています。

Mackrelで見てみると

AWSインテグレーションで取得した 平均処理時間と 今回作成した initialDurationのグラフを並べてみます。 initialDurationの集計が5分おきに実行されるので、スパイクの発生時間に少し誤差がありますが、2つのグラフを並べて同じ画面で見れるので問題発生時の調査が格段にしやすくなりました。

f:id:kuchitama:20191207141230p:plain
カスタムダッシュボード上でグラフを並べて表示

また、Monitorsで閾値を設定することで、サービスの改修を続けるうちにパフォーマンスが悪化してしまうということも検知できるようになりました。

f:id:kuchitama:20191207140032p:plain
アラートの閾値設定画面

まとめ

ということで、 Lamdaを使ってLamdaの監視を実現、特に重要となる cold startのメトリクスをMakerel上で監視できるようになりました。

注意点として、 Lambda, DynamoDB, CloudWatchLogs Insightの料金は従量課金となっています。コストに関してはAWSのドキュメントを確認していただきたいですが、監視の間隔や計測するログデータの量のバランスをとって調整してください。

以上、Makerelアドベントカレンダー7日目の記事でした。 明日は、YujiSoftwareさんが何かツールを作って紹介してくださるようです。

qiita.com

また最後になりましたが、今回の内容について、元となるLamda上でのメトリクス投稿について記事を紹介してくださった a-know さん、そもそも記事を残してくださった kmiya_bbm さん、本当にありがとうございました。 あと、記事書いてる間ほったらかしてる息子よ、ごめんね。

f:id:kuchitama:20191207140547j:plain
ぼっちでYouTubu漬け

Mackerelアンバサダーになりました

昨日、株式会社はてな様からオファーを頂き、Mackerelアンバサダーに就任しました。

いい機会なので、私とMackerelの関わりを振り返ってみようと思います。

使い始め

どうやら、2014年10月1日から使い始めたみたいです。フリュー在籍中で、ソーシャルゲーム開発をしてた時期ですね。 このときは完全に遊び半分で個人で試し始めました。

ずいぶん古い…

そして、その日のうちに独自プラグイン作り始めてる???

したら、当時はてなCTOだった id:stanaka さんにサポートしていただきました。

これで、僕は完全にMackerel推しになりました。だって、こんなサポートされたら好きになってまうやろ。 このとき作ったプラグインScala製でJVMのメトリクスを取得するものでした。 Scalaの練習のつもりで、作ったのですがJVMの起動のオーバーヘッドが大きくて使い勝手はよくありませんでした。 というか、同様の機能を持ったGo製プラグインが公式にありますしね。

github.com

業務導入

シェアウィズ転職直後に入ったプロジェクトでMackerel導入を決めました。 当時、大阪の長堀橋にあったオフィスまで、わざわざ東京からはてなの担当者に説明に来ていただきました。 このときは、サーバ台数もそれほどなかったので、無料枠です。(ここまでしてもらっておきながら…)

このとき、MackerelのAnsible RoleにPR送ってますね。

blog.share-wis.com

github.com github.com

微力ながら、Mackerelに貢献できました。

その後も、 id:a-know さんが新機能の説明兼ユーザインタビューで、オフィスまで来ていただくなど、相変わらず手厚いサポートを続けていただきました。

しかも、この時もらったグラスは、はじめての子育てに大きく貢献してくれました。

が、割られてしまいました。。。

その後、別の受託開発をしていたプロジェクトで、監視にMackerelを導入しました。 こっちは、ちゃんと(お客さんが)お金を払っての導入で、やっぱりサーバ台数少ないんですが、Mackerelに微力ながら貢献できました。 多分、今もサーバ台数増やしつつ利用は続いていると思います。

そして今

キャッシュビーに移ってからも、引き続きMackerelを(無料プランで)利用しています。

あと、家の Raspberry Pi & Nature Remo と連携させて、室内の気温監視もしています。そういえば、このメトリクス設定する時トラブって、 id:daiksy さんが助けてくれました。多謝

振り返ってみれば、なんだかんだと、Mackerelとは長い付き合いですね。 本当に微々たることしかできませんが、アンバサダー業がんばります。

Terraform と AWS のちょっとした知見 - Terraformでインフラ構築#2

Terraformを使って、AWSを管理しているプロジェクトでちょっとした作業を行った際の個人的な知見の備忘録。 割としょうもない内容だけど、運用続けていくうえで抑えておきたいのでメモを残す。

Terraformのバージョンは最新に追従できていなくて、 v0.9.11 を使用。

EC2のインスタンスタイプ変更時はEBSはそのまま残る

EC2をスケールアップした際の挙動。 インスタンスがstopされ、インスタンスタイプが指定のものに変更されて、再起動される。 この時、EBSは変更されないので、EBSに保存されていたデータはそのまま引き継がれる。

ここにある作業内容をそのままTerraformがやっている。 https://recipe.kc-cloud.jp/archives/1348

tfファイルの変更内容

resource "aws_instance" "example" {
  #...
-  instance_type = "t2.micro"
+  app_ec2_instance_type = "t2.small"
  #...
}

Elastic IPの設定

EIPを設定する際に、 下記のように aws_eip だけ設定していると、EC2インスタンスインスタンスタイプ変更時に再起動された際に EIPがアタッチされなかった。 aws_eip_association の設定が必要

resource "aws_eip" "example" {
  instance_id = "i-XXXXXXXX"
  vpc = true
}

tfファイルの変更内容

resource "aws_eip" "example" {
-  instance_id = "i-XXXXXXXX"
  vpc = true
}
+ 
+ resource "aws_eip_association" "example" {
+   instance_id = "i-XXXXXXXX"
+   allocation_id = "${aws_eip.example.id}"
+ }

RDSインスタンスタイプ変更時には apply_immediately を trueにする

これを設定しておかないと、インスタンスタイプが変更されずにやきもきすることになる。というか、やきもきした。 Terraformというか、RDSの知見。

tfファイルの変更内容

resource "aws_db_instance" "example" {
  # ...
- apply_immediately = "false"  
+ apply_immediately = "true"  
  # ...
}

RDSインスタンスタイプ変更時にはストレージは変更されない

これはEC2と同様。 当たり前といえば当たり前だが、ちゃんと把握しておかないと作業するときドキドキするので、メモしておく

tfファイルの変更内容

resource "aws_db_instance" "example" {
  # ...
- instance_class = "db.t2.micro"
+ instance_class = "db.m4.small"
  # ...
}

RDSインスタンスタイプ変更時にエンドポイントは変更されない

これも当たり前だけど、一応メモ。 RDSのエンドポイントは、EC2と違ってIPに依存しないので、再起動が発生しても影響を受けない。


kuchitama.hateblo.jp

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

大阪でReactもくもく会に行ってきた

ちょっと時間が経ってしまったけど、 5/19に大阪で非公開のReactもくもく会というイベントが開かれて、参加してきたのでログを残す。

https://facebook.github.io/react/img/logo.svg

参加した経緯

最近、業務でそろそろReactをプロダクトに採用する機運が高まってきた。 そんなタイミングで、ちょうど知り合いから、Reactもくもく会やるけど参加しないかと声が掛かった。 Reactやってないけど、ReactNativeは触ってるので、普段からReact周りのキャッチアップはしててTwitterやFBに流してたので、それ見てこえかけてもらえたらしい。

得られた学び

とにかく、めちゃくちゃ学びが多かった。 実務でReactやってる人たちが集まる会だったので、公開可能ラインが判断つかないので、大丈夫そうなところだけ挙げると↓な感じ。

  • reflux良さそう

github.com

  • Airbnbのスタイルガイドはちゃんと読もう github.com
  • eslintにも追加しよう github.com
  • package.jsonの書き方とか、ちょっとした、でも役に立つテクニックを教えてもらえた

やったこと

業務での採用に向けて、Rails5.1でReactする。 でもnodeのバージョンが古くてはまってて、ほとんど進まなかった…

思ったこと

  • WebViewアプリでReactしてる人がいたけど、Reactのコードのところ完全にReactNativeでも通用する感じだった
    • ReactNativeが安定するまでは、React+OnsenUI+WebViewとかReact+OnsenUI+(Cordva|Monaca) で良さそう
    • Reactファミリーの Learn once, Write Anywhere は本物
  • ひょっとして、僕は会の趣旨に会わないくらい初心者かも知れない

共有したこと

  • ReactNativeのプロダクト運用のつらみ
    • ReactNativeのバージョンアップペースが早い
    • マイグレーションつらい
    • BreakingChangeが普通にある

今後について

来月も開催予定です。 このもくもく会の趣旨は、 1. 参加者同士のノウハウの交換にフォーカスしたい 2. そのため完全な初心者をフィルタリングするためにしばらくクローズドなイベントとして開催 3. でも、参加者が増えるのは歓迎 といったところです。

プロダクトでReact使うぐらいのノウハウを持ってて、他の人の知見知りたければお声がけください。

ぼくはterraformだけ触ろうと思った - terraform でインフラ構築#1

f:id:kuchitama:20170414230227p:plain

先日のホッテントリにこんな記事が…

dekotech.dekokun.info

丁度この記事見る直前ぐらいにterraformを仕事で使って、全く同じ気持ちを抱きました。 terraformでもCloudFormationでも、CLIクラウドインフラ構築とかやってた人は触ったら抜け出せなくなると思います。 ただ、terraformはAWSに限定されないです。強い。

www.terraform.io

なぜterraform

www.terraform.io

terraformは安心のHashiCorp製のオーケストレーションツールです。 terraformを使うことで、インフラの Infrastructure as Code が実現できます。

というかインフラに限らず、かなり幅広く XXX as Codeできますね。 社内IT担当者とか結構嬉しいんじゃないでしょうか GitHubリポジトリやチームメンバーの管理もできるみたいです(なんでもありやな)

改めて言うまでもないかも知れませんが、メリットを挙げるとこんな感じだと思います。

  • 設定がバージョン管理できるので、属人性を無くせる
  • 設定反映の自動化により手作業によるオペレーションミスが減らせる
  • DryRunで実行前に挙動をチェックできるのでオペレーションミスが減らせる

terraformのインストール

Go製なので、↓から、自分のマシン用の実行ファイル落としてきてパス通ったフォルダに置くだけ、ぽん

Download Terraform - Terraform by HashiCorp

簡単ですね

動かしてみる

DryRunして、実際に立ち上げて、そして落とすという一連の操作をやってみます。

1. 設定ファイルを作成

とりあえず、デモとして AWSAmazon Linuxインスタンスを立ち上げる設定を書きます。 とりあえずのステップワンなので1ファイルに全部書いちゃいます。 適当なディレクトリを作ってその中に、下記のgistの ec2.tf ファイルを置きます。

$ mkdir terraform
$ cd terraform 
$ vim ec2.tf

gist.github.com

ちなみに、インスタンスタイプが t2.micro でなく t1.micro になっているのは、とりあえず最小構成としてEC2インスタンスだけを立ち上げたかったからです。 t2.microVPC必須になっていますが、この設定ファイルでは VPCについての設定を書いていないため、EC2インスタンスを立ち上げることができません。

2. DryRunでチェック

設定が書けたら、 plan で確認します。

$ terraform plan

f:id:kuchitama:20170414225002p:plain

3. 実際に立ち上げる

問題なさそうなので、インスタンスを立ち上げましょう。 apply を実行します。

$ terraform apply

f:id:kuchitama:20170414225121p:plain

マネジメントコンソールを確認すると、インスタンスができてました。

f:id:kuchitama:20170414225149p:plain

4. 立ち上げたインスタンスを停止する

さて、とりあえず動作確認は充分ですし、お金がもったいないので、いらないインスタンスには退場してもらいましょう。

destroy を実行します。

$ terraform destroy

f:id:kuchitama:20170414225226p:plain

ターミネートできました。

f:id:kuchitama:20170414225328p:plain

こんな感じで、 plan apply destory で、AWSが簡単に操作できるのが terraform です。 もうシェル芸でAWS構築する必要がなくなりました。

Docker上のRedashのバックアップとリストア手順

昨日の書いたエントリがホッテントリ入りしてて、喜ばしい限りです。

kuchitama.hateblo.jp

悲しい思いをしましたが、いく分か取り戻せた気がします。

今日は、昨日の反省を活かして、トラブルがあっても大丈夫なようにRedashのPostgresデータのバックアップとリストアの手順をまとめます。

ちゃんとリストアの手順までまとめておかないとね!!

でないと、実況Youtubeをするはめになる… (実際昨日は @kiy0taka さんと、 @irof さんに実況しないのって煽られてました)

例によってDocker上で運用している前提です。 AWSとかGCP上で運用しているなら、こちらのドキュメントに従えば多分大丈夫だと思います。

バックアップ手順

ホストOSのシェルで、下記のコマンドを実行すれば、バックアップファイルが手に入ります。

$ cid=`docker ps | grep redash_postgres | awk '{print $1}'`

$ docker exec ${cid} /bin/bash -c 'pg_dump -U postgres postgres | gzip > /usr/local/redash_backup.gz'
$ docker cp ${cid}:/usr/local/redash_backup.gz redash_backup.gz

たまたま、ホストマシンとして使ってる Mac mini にJenkinsがインストール済みだったので、Jenkinsで定期的に実行できるように、↓の Pipeline Scriptを用意しました。(実際には、更に外部ストレージにコピーしています)

redash_backup

リストア手順

何かあった時に Youtubeで中継することの無いよう、リストアの手順まできっちり用意します。 基本的に、昨日のエントリにあるとおり、Volume名をしていている前提で進めます。 Volume名を指定せずに運用している場合は、 docker-compose down して、 up して、データの流しこみを行えば問題ないと思います。

手順は下記の通り

  1. RedashのDockerコンテナを停止する
  2. PostgresのVolumeにつながったコンテナを起動
  3. テーブル削除&テーブル再作成
  4. バックアップデータの流し込み
  5. Redashを起動して動作確認

1. RedashのDockerコンテナを停止する

コンテナが稼働中であれば、一旦止めてしまいます

$ docker-compose -f docker-compose.production.yml donw --remove-orphans

2. PostgresのVolumeにつながったコンテナを起動

$ docker run -d -v redash_postgres_data:/var/lib/postgresql/data -p 5432:5432 postgres:9.3

# Docker のcontainer id を確認
$ cid=`docker ps | grep postgres | awk '{print $1}'`

$ echo ${cid}
ecd9564a3315

3. テーブル削除&テーブル再作成

# コンテナの IPアドレスを取得
$ dbip=`docker inspect {コンテナID}  | jq .[].NetworkSettings.IPAddress`

$ echo ${dbip}
172.17.0.2

$ psql -h ${dbip} -U postgres template1
psql (9.5.6)
Type "help" for help.

template1=# DROP DATABASE IF EXISTS postgres;
DROP DATABASE
template1=# CREATE DATABASE postgres;
CREATE DATABASE
template1=# \q

4. バックアップデータの流し込み

docker cp でホストマシン上にあるバックアップファイルを、コンテナにコピーして、流し込みます。

$ docker cp redash_backup.gz {コンテナID}:/usr/local/redash_backup.gz

$ docker exec -t {コンテナID} /bin/bash -c 'zcat /usr/local/redash_backup.gz | psql -U postgres -d postgres'

5. Redashを起動して動作確認

$ docker-compose -f docker-compose.production.yml up -d

f:id:kuchitama:20170405142923j:plain

補足

バックアップ用のスクリプトは、基本的に、JenkinsのJobとして、定期実行する前提で書いたので、 Docker Compose は使っていません。 Redashのディレクトリから手動で実行するのであれば、 docker ps より docker-compose ps を使ったほうがいいかも知れません。

PostgresのVolumeに名前をつけていない場合で、マウント中のVolumeを知りたいときは、 docker inspect が使えます。 情報量が多いので、jq を使って、絞り込みました。

docker inspect | jq -r .[].Mounts[].Name

読む予定の本

Docker入門

Docker入門

Docker

Docker

Docker上のRedashのデータをなくしてから復旧するまでの全記録

会社で、Redashを運用してるんですが、そのRedashがぶっ飛んで、もうだめだーと思ったものの、なんとか復旧出来たので、忘れないうちにやったことをまとめておきます。

結論から言うと、 ちゃんとDocker, Docker Composeの運用ノウハウ勉強してから、Redash立てようなって話です。

f:id:kuchitama:20170405144512p:plain

長いので先にまとめ

  • RedashをDockerで運用してた
  • docker-compose down したら、データが全て消えた
  • Volumeを指定してなかったのが原因
  • Volumeディレクトリから発掘して事なきを得た
  • RedashをDockerで運用するなら volumes は指定しような

経緯

会社で、Mac mini に Docker for Mac を入れて、 Redash運用してます。 コイツでプロダクションデータの可視化なんかもやってて、非エンジニアの人に色々見せるのに重宝してました。

で、何が起こったのかというと↓な感じ

何やらかしたの?

元々、 Redash のバージョンが v1.0.0-rc.2 でした。 つい先日、v1がリリースされたので、喜び勇んで、運用しているRedashのバージョンをあげようとしました。

github.com

Redashのドキュメントを見ながら実行しようとしたんですが、どうもドキュメントは、AWSとかGCPとかで運用してるの前提に記載されてるらしい。

なら、立ち上がってるDocker Container を一度落として、Redashのリポジトリをpullすれば、アップグレードできるんじゃないかって思ったんです。

そして、 docker-compose down --remove-orphans を実行しました。してしまいました。

で、改めて docker-compose -d -f docker-compose.production.yml up を実行して、ブラウザで Redashを開いたところ、 Internal Server Error の文字が…

PostgreSQL のデータを確かめるために、↓を実行すると…

$ docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                           NAMES
960b42350d18        redash/nginx:latest   "nginx -g 'daemon ..."   56 minutes ago      Up 56 minutes       443/tcp, 0.0.0.0:8090->80/tcp   redash_nginx_1
e853151fdff4        redash_server         "/app/bin/docker-e..."   56 minutes ago      Up 56 minutes       0.0.0.0:5000->5000/tcp          redash_server_1
fabcbe1076ac        postgres:9.3          "docker-entrypoint..."   56 minutes ago      Up 56 minutes       5432/tcp                        redash_postgres_1
50874f74a6fe        redis:2.8             "docker-entrypoint..."   56 minutes ago      Up 56 minutes       6379/tcp                        redash_redis_1
0d6d983060b4        redash_worker         "/app/bin/docker-e..."   56 minutes ago      Up 56 minutes       5000/tcp                        redash_worker_1

$ docker exec -it fabcbe1076ac /bin/bash
root@fabcbe1076ac:/# psql -U postgres
psql (9.3.16)
Type "help" for help.
postgres=# \dt
No relations found.

な、何もない!!!!!

@kiy0taka さんの天啓

というわけで、失意にくれて、某slackチャンネルで嘆いていると、 @kiy0taka さんが色々とアドバイスをくれました

kiy0taka [9:37 AM] 
Data Volumeごと消えちゃったの?

kuchitama [9:44 AM] 
docker立ち上げ直した時に、上書きされたっぽいすね…

kiy0taka [9:45 AM] 
なんと…

kuchitama [9:46 AM] 
docker-compose down がまずかったのかな…

kiy0taka [9:48 AM] 
あーもしかしてvolumeに名前つけてなかった?

kuchitama [9:51 AM] 
これをそのまま動かしてましたね〜
マジメに compose 勉強するか
https://github.com/getredash/redash/blob/master/docker-compose.production.yml

kiy0taka [9:52 AM] 
お、まだデータあるかも

[9:56]  
postgresのvolumeが指定されてないから `docker volume ls` したどれかに残ってるかもしれない

復旧するためにやったこと

  1. Volumeの確認
  2. PostgreSQLで使ってたVolumeの特定
  3. Volumeからpg_dumpを取得
  4. Volume名を指定してVolume作成
  5. 旧Volumeからデータコピー

Volumeの確認

まずは、今残っているVolumeの確認

$ docker volume list
DRIVER              VOLUME NAME
local               03caf1bf6ea2a81c3c11e699fb994f7f6eecf17ce34a424442d6d52bc461d006
local               066d7758d45f3e3bf53e7a8e6b000a43a0ecbdfd515402118b069b1f587d3f42
local               1cd40529441b334487a45cb46766f020229d78c3ac7f49618bb727898b34e561
local               241c844e7745b0478fe084c8c289e656c7843b9538e2e1789d37e971c5700a2a
local               2626cc49fae0425de3814f341b6b0f2d4c039bb8163c8a600100752eeda61d65
local               2d9eca5a4c13a533f7afb47ecffd20726c31dbffd4ab8604b538ceecc32c6401
local               4a80807c699663692c4e20039874851efc3ea1ea1836a40bbab8b7eca062fa7d
local               6f1efb9cf644d645d34e4905bf762248b4d3405fdcda8f645214d07d4d12cb27
local               70850d1355368612e3908ec7033ed805ae8f292673597c7b2bdc0bcc7616e14a
local               72669aa7e81aef5880e9d9323baadf479d8feeb12a811f6cc8f723533b8d184d
local               748cdf747fc8ff16cf5030de1d8dc96ca4af46896988717a310cad31bee98163
local               7aad2bd160a5780b02768da17e30541e68a5ebcf23fbf74319e4e8d157ceb575
local               7c55dc6918b3620f859b8dae54c5dc4cbaa774da77e0bf1505274aa068a53202
local               9817cf6340ec933949f5d021417a885d2ef33544f849053566b6d9f13ad4c07d
local               98336ea63f91ec5cf7c19e267daed350167f8040c148b701b0f79027e76e8418
local               c1612d3c38d3dd45ae7c17675d751316f418cdbde734650fd2340a90e2e8f739
local               c6130e1ce308011c2419bdfb525ea943cc26ff10b00bf958f65e97056be93837
local               c8525c0edd65e51b87cee226a213940d9430867d8222f373eaccfcb2f5310041
local               dd70b3531d7f6a722a140c1b875cf730d81e5486a93e6e3c674ad436d7457357
local               df15a22599482c15b845d3c6fb3e3272b3d0626dee546f55f82ee5a5990f0845
local               e0b8c01f409cc73b7c1d040dde080d062b35c46e13b09063078a05e5958f5a5f
local               f2533120680c4d5377579e25054a3b235a9a8f140474470b41aab5e9220c6056

この中のどれかに、Redashのpostgresデータが残っているはず! ということで、Volumeのディレクトリを見てみようとホストの /var/lib/docker/volumes を見ようとしますが、

/var/lib/docker ディレクトリがない!!!

というのも、 Docker for MacApple の Hypervisor 上で動作しています。つまり、仮想マシンにDockerコンテナがいるわけです。 そのため、 /var/lib/docker はホストマシン上には存在していません。

と、ここで更に @kiy0taka さんからアドバイスが!

screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty

で、Hipervisor上の仮想マシンにアクセスできました。

キヨタカ兄さんマジありがとう!!!

/ # cd /var/lib/docker/volumes/
/var/lib/docker/volumes # ls
03caf1bf6ea2a81c3c11e699fb994f7f6eecf17ce34a424442d6d52bc461d006
066d7758d45f3e3bf53e7a8e6b000a43a0ecbdfd515402118b069b1f587d3f42
1cd40529441b334487a45cb46766f020229d78c3ac7f49618bb727898b34e561
241c844e7745b0478fe084c8c289e656c7843b9538e2e1789d37e971c5700a2a
2626cc49fae0425de3814f341b6b0f2d4c039bb8163c8a600100752eeda61d65
2d9eca5a4c13a533f7afb47ecffd20726c31dbffd4ab8604b538ceecc32c6401
4a80807c699663692c4e20039874851efc3ea1ea1836a40bbab8b7eca062fa7d
6f1efb9cf644d645d34e4905bf762248b4d3405fdcda8f645214d07d4d12cb27
70850d1355368612e3908ec7033ed805ae8f292673597c7b2bdc0bcc7616e14a
72669aa7e81aef5880e9d9323baadf479d8feeb12a811f6cc8f723533b8d184d
748cdf747fc8ff16cf5030de1d8dc96ca4af46896988717a310cad31bee98163
7aad2bd160a5780b02768da17e30541e68a5ebcf23fbf74319e4e8d157ceb575
7c55dc6918b3620f859b8dae54c5dc4cbaa774da77e0bf1505274aa068a53202
9817cf6340ec933949f5d021417a885d2ef33544f849053566b6d9f13ad4c07d
98336ea63f91ec5cf7c19e267daed350167f8040c148b701b0f79027e76e8418
c1612d3c38d3dd45ae7c17675d751316f418cdbde734650fd2340a90e2e8f739
c6130e1ce308011c2419bdfb525ea943cc26ff10b00bf958f65e97056be93837
c8525c0edd65e51b87cee226a213940d9430867d8222f373eaccfcb2f5310041
dd70b3531d7f6a722a140c1b875cf730d81e5486a93e6e3c674ad436d7457357
df15a22599482c15b845d3c6fb3e3272b3d0626dee546f55f82ee5a5990f0845
e0b8c01f409cc73b7c1d040dde080d062b35c46e13b09063078a05e5958f5a5f
f2533120680c4d5377579e25054a3b235a9a8f140474470b41aab5e9220c6056
metadata.db

postgreで使ってたVolumeの特定

この中から、Postgresコンテナ で利用してたVolumeを特定します

/var/lib/docker/volumes # find . | grep pg_clog 
./241c844e7745b0478fe084c8c289e656c7843b9538e2e1789d37e971c5700a2a/_data/pg_clog
./241c844e7745b0478fe084c8c289e656c7843b9538e2e1789d37e971c5700a2a/_data/pg_clog/0000
./7aad2bd160a5780b02768da17e30541e68a5ebcf23fbf74319e4e8d157ceb575/_data/pg_clog
./7aad2bd160a5780b02768da17e30541e68a5ebcf23fbf74319e4e8d157ceb575/_data/pg_clog/0000
./2626cc49fae0425de3814f341b6b0f2d4c039bb8163c8a600100752eeda61d65/_data/pg_clog
./2626cc49fae0425de3814f341b6b0f2d4c039bb8163c8a600100752eeda61d65/_data/pg_clog/0000
./7c55dc6918b3620f859b8dae54c5dc4cbaa774da77e0bf1505274aa068a53202/_data/pg_clog
./7c55dc6918b3620f859b8dae54c5dc4cbaa774da77e0bf1505274aa068a53202/_data/pg_clog/0000
./e0b8c01f409cc73b7c1d040dde080d062b35c46e13b09063078a05e5958f5a5f/_data/pg_clog
./e0b8c01f409cc73b7c1d040dde080d062b35c46e13b09063078a05e5958f5a5f/_data/pg_clog/0000
./c8525c0edd65e51b87cee226a213940d9430867d8222f373eaccfcb2f5310041/_data/pg_clog
./c8525c0edd65e51b87cee226a213940d9430867d8222f373eaccfcb2f5310041/_data/pg_clog/0000
./98336ea63f91ec5cf7c19e267daed350167f8040c148b701b0f79027e76e8418/_data/pg_clog
./98336ea63f91ec5cf7c19e267daed350167f8040c148b701b0f79027e76e8418/_data/pg_clog/0000
./1cd40529441b334487a45cb46766f020229d78c3ac7f49618bb727898b34e561/_data/pg_clog
./1cd40529441b334487a45cb46766f020229d78c3ac7f49618bb727898b34e561/_data/pg_clog/0000

候補は8件に絞られました。

そこから、どれが運用してたRedashのPostgresのボリュームなのかを特定するために↓で1つずつ調べて回ります。 1つずつVolumeをバインドした Postgres のコンテナを立ち上げて、DBの中身を確認していく地道な作業です

$ docker run -d -v {絞り込んだvolumeのID}:/var/lib/postgresql/data -p 5432:5432 postgres:9.3
$ docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                           NAMES
295afcefc16c        postgres:9.3          "docker-entrypoint..."   11 seconds ago      Up 6 seconds        0.0.0.0:5432->5432/tcp          objective_feynman

$ docker exec -it {↑のコンテナID} /bin/bash

# psql -U postgres

postgres=# \dt

postgres=# select * from users;

postgres=# \q

# exit

$ docker kill {↑のコンテナID}

この繰り返しで、Redashのデータが入っていたVolumeが特定できました。

Volumeからpg_dumpを取得

データが入っていたVolumeが特定できたので、今後なにが起こってもいいようにまずはデータをバックアップしておきます。 DBのデータを確認したのと同じ要領で、Postgresのコンテナを立ち上げて、pg_dumpを実行して、docker cp でファイルをホストマシンに保存しました。

$ docker run -d -v {絞り込んだvolumeのID}:/var/lib/postgresql/data -p 5432:5432 postgres:9.3
$ docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                           NAMES
295afcefc16c        postgres:9.3          "docker-entrypoint..."   11 seconds ago      Up 6 seconds        0.0.0.0:5432->5432/tcp          objective_feynman

$ docker exec -it {↑のコンテナID} /bin/bash

# pg_dump -U postgres postgres | gzip > /usr/local/redash_backup.gz

# exit

$ docker cp 295afcefc16c:/usr/local/redash_backup.gz ~/Desktop

Volume名を指定してVolume作成

まずは、2度とVolumeが変わってしまわないように、Volume名を指定します。 ここでも、 @kiy0taka さんのアドバイス

このgistにあるように、 postgresvolumes と一番下にあるトップレベルの volumes を指定しました。 この辺の設定は↓に記載が有ります。

docs.docker.com

そんでもって、一旦 docker-compose up して、 down します。 これで、名前のついたVolumeが作られます。

$docker-compose -f docker-compose.production.yml up -d
Creating network "redash_default" with the default driver
Creating volume "redash_postgres_data" with default driver
Creating redash_worker_1
Creating redash_redis_1
Creating redash_postgres_1
Creating redash_server_1
Creating redash_nginx_1

$ docker volume list
DRIVER              VOLUME NAME
# ...中略...
local               redash_postgres_data

これで、何度 docker-compose up しても、同じVolumeが RedashのPostgresコンテナにバインドされます。

旧Volumeからデータコピー

新しい redash_postgres_data Volumeを常に利用するようになったので、このVolumeを元のVolumeで上書きしました。 まず、コンテナを落として、Hipervisor上でファイルを上書きしてしまいます。

$ docker-compose down --remove-orphans                 
Stopping redash_server_1 ... done
Stopping redash_redis_1 ... done
Stopping redash_postgres_1 ... done
Stopping redash_worker_1 ... done
Removing orphan container "redash_nginx_1"
Removing redash_server_1 ... done
Removing redash_redis_1 ... done
Removing redash_postgres_1 ... done
Removing redash_worker_1 ... done
Removing network redash_default

$ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty

/ # cd /var/lib/docker/volumes/
/var/lib/docker/volumes cp -R 7aad2bd160a5780b02768da17e30541e68a5ebcf23fbf74319e4e8d157ceb575/_data/ redash_postgres_data/

(Ctrl+a):kill

これで、docker-compose up して、ブラウザでRedashを開いてみると、ちゃんと表示されました。

f:id:kuchitama:20170405142923j:plain

た、助かったぁぁぁ

composeをちゃんと身に着けたいので

この辺読んでみよう(目次にcomposeの項目がある)

Docker入門

Docker入門

Docker

Docker

最後に

ほんとに @kiy0taka さんありがとうございました

人って温かい。。。

そういえば、 @kiy0taka 兄さんには、社会人なりたてのころにもこんなこと教えてもらったりしてたなぁ

kuchitama.hateblo.jp

追加

バックアップとリストアの手順をまとめました

kuchitama.hateblo.jp