helen's blog

ずっとおもしろいことしてたいな。

ハムスターのために監視導入したら人間のための監視もしたほうが良さそうなことがわかりました

Mackerel Advent Calendar 2017 - Qiita の12/20の記事です

以前、【増枠】紅白 Milkcocoa 合戦!【祭り】 Milkcocoa Meetup vol12 - connpassでLTしたこれを

(要約:3時間で勝手にエアコンが切れるアパートで、
ハムスターのために温度が一定以上になったらラズパイでエアコンつけて夏を乗り切りました)

いろいろと事情が変わったので、今回はMackerelを使って改造してみました!

前回より

変わったこと

実は当時のアパートから引っ越ししたので事情がとても変わりました

  • エアコンが勝手に切れない!!
  • ハムスターたちとエアコンが遠いので、ハムスターのそばで温度を取ると赤外線が届かない
  • もしかして実はもう必要ないのでは...
当時の課題
  • 湿度を取っているのに表示していない、SPだと崩れてる
    • 得意でないフロントゴリゴリ実装でもう1個グラフを増やす気力が沸かなかった😂
  • 後追いでグラフを見て確認できるけど、状況をPushしていない
    • 当時をよく覚えていないけどHubot周りがうまくいかず諦めちゃった😂

ここにMackerelを導入してみました

Mackerelにカスタムメトリック投稿

物理的な実装はセンサーつないだだけなので割愛します

温度と湿度を取得して出力

ホストのカスタムメトリックを投稿する - Mackerel ヘルプ を参考にタブ区切りで出力

import time
import get_environment as env # 温度取得はmilkcocoa送信用に実装済みなので流用

# vars
epoch_seconds = str(time.time())

try:
    # get host_id
    with open('/var/lib/mackerel-agent/id') as mackerel_agent_id:
      host_id = mackerel_agent_id.read()

    # output datas
    print('\t'.join(['env.temp', env.temperature, epoch_seconds]))
    print('\t'.join(['env.hum', env.humidity, epoch_seconds]))

except Exception as e:
    print('failed in sending to mackerel')
    print(e)
実行結果
$ sudo /usr/bin/python /PATH_TO_SOURCE/mackerel_monitor_temperature.py
env.temp	22.40	1512901475.44
env.hum	36.40	1512901475.44
mackerel-agent.confに追記
# 追記内容
[plugin.metrics.raspi]
command = "sudo /usr/bin/python /PATH_TO_SOURCE/mackerel_monitory_temperature/mackerel_monitor_temperature.py"
agent起動
$ nohup sudo /usr/local/bin/mackerel-agent >> /var/log/mackerel-agent.log &
こうなる

青いグラフが湿度、オレンジが温度です

f:id:heleeen:20171211015744p:plain
※12/9の21時ごろに湿度がいきなりあがったのは冷しゃぶしたからです

監視を入れます

ハムスターにとって快適な温度は20〜26度で、
冬場は風邪を引かないための湿度は40〜60%が良いらしいのでこんな監視設定をします

$ mkr monitors
[
    {
        "id": "3bwDVM8jm1h",
        "name": "custom.env.hum over 60%",
        "memo": "ジメジメだよ(。>﹏<。)",
        "type": "host",
        "metric": "custom.env.hum",
        "operator": ">",
        "warning": 60,
        "critical": 65,
        "duration": 3
    },
    {
        "id": "3bwDQLE4bTS",
        "name": "custom.env.hum under 40%",
        "memo": "干からびちゃうよ(。ŏ﹏ŏ)",
        "type": "host",
        "metric": "custom.env.hum",
        "operator": "<",
        "warning": 45,
        "critical": 40,
        "duration": 3
    },
    {
        "id": "3bw5wNYbhN5",
        "name": "custom.env.temp over 26℃",
        "memo": "暑いよ(;´Д`)",
        "type": "host",
        "metric": "custom.env.temp",
        "operator": ">",
        "warning": 25,
        "critical": 26,
        "duration": 3
    },
    {
        "id": "3bw5memzmP3",
        "name": "custom.env.temp under 20℃",
        "memo": "寒いよ:;(∩´﹏`∩);:",
        "type": "host",
        "metric": "custom.env.temp",
        "operator": "<",
        "warning": 21,
        "critical": 20,
        "duration": 3
    }
]

監視設定した途端いきなりCRITICAL来た

f:id:heleeen:20171214005457p:plain

温度とか言ってる場合じゃない!!
(温度は無事20〜26度をさまよっていました)

朝の喉のイガイガ感の原因がはっきりしました

ここまでで

  • ちょっとPython書いたらメトリック投稿できた
  • フロント頑張らなくてもグラフ表示できる!!!
  • 簡単に湿度まで可視化できた!!
  • しかも実はラズパイの死活監視までできている

はまったところ

invalid JSONされる
2017/12/03 11:54:14 WARNING <command> metadata plugin "raspi": outputs invalid JSON: env.temp	22.50	1512302054.61

mackerel-agent.confの書き方がだめでした

# これでエラー
[plugin.metadata.raspi]
command = "sudo /usr/bin/python /home/pi/work/mackerel_monitory_temperature/mackerel_monitor_temperature.py"

# "plugin.metrics."ではじめないといけない
[plugin.metrics.raspi]
command = "sudo /usr/bin/python /home/pi/work/mackerel_monitory_temperature/mackerel_monitor_temperature.py"

ドキュメントろくに呼んでない自分が完全に悪いけど
エラーメッセージがもうちょっと親切だとありがたかったかも...
エージェント経由ってタブ文字区切りだとだめなの...?で数日悩みました( ;∀;)

カスタムメトリックってJSON書かないと監視できないのかと思っちゃった(●´ϖ`●)

f:id:heleeen:20171214000914p:plain

下まで見ても見つからず...

f:id:heleeen:20171214000928p:plain

入力すればよかったのか😂

おわりに

f:id:heleeen:20171219234519p:plain

アドベントカレンダー書くぞと帰ってきて加湿器つけてこんな感じで
湿度がCRITICALとWARNINGを行き来しています
お湯を800mlくらい蒸発させたときは湿度20%くらいあがって、すごい即効性がありました
やったことが数字になってわかるのもおもしろいね(●´ϖ`●)

DeployerでmacからはデプロイできるのにLinuxで実行したら死んだ

業務で使っているDeployerで普段ローカルからデプロイしていたものを
AWSのEC2に乗っけて動かそうとしたら思わぬエラーが出たのでメモ

デプロイ手順(こんな手順を踏まないといけない経緯は悲しいので書かない)

  1. デプロイサーバー(EC2)で不要なファイル以外を圧縮 ←ここで死んだ
  2. デプロイ先(EC2)へ転送
  3. デプロイ先(EC2)で解凍
  4. 以降はDeployerの動作に任せる
エラー内容
[RuntimeException]
  tar: ./proj.tar.gz: 読み込んだファイルが変更されています
 
Exception trace:
 () at phar:///home/deploy-user/deploy/deployer.phar/src/functions.php:335
 runLocally() at /home/deploy-user/deploy/include/tasks.php:27
 {closure}() at n/a:n/a
 call_user_func() at phar:///home/deploy-user/deploy/deployer.phar/src/Task/Task.php:79
 Deployer\Task\Task->run() at phar:///home/deploy-user/deploy/deployer.phar/src/Executor/SeriesExecutor.php:43
 Deployer\Executor\SeriesExecutor->run() at phar:///home/deploy-user/deploy/deployer.phar/src/Console/TaskCommand.php:82
 Deployer\Console\TaskCommand->execute() at phar:///home/deploy-user/deploy/deployer.phar/vendor/symfony/console/Command/Command.php:256
 Symfony\Component\Console\Command\Command->run() at phar:///home/deploy-user/deploy/deployer.phar/vendor/symfony/console/Application.php:791
 Symfony\Component\Console\Application->doRunCommand() at phar:///home/deploy-user/deploy/deployer.phar/vendor/symfony/console/Application.php:186
 Symfony\Component\Console\Application->doRun() at phar:///home/deploy-user/deploy/deployer.phar/vendor/symfony/console/Application.php:117
 Symfony\Component\Console\Application->run() at phar:///home/deploy-user/deploy/deployer.phar/src/Deployer.php:101
 Deployer\Deployer->run() at phar:///home/deploy-user/deploy/deployer.phar/bin/dep:63
 require() at /home/deploy-user/deploy/deployer.phar:4
 
deploy [-p|--parallel]
原因

圧縮しようとしている中にtarファイルを作っている
prj/配下を圧縮している最中にprj/[圧縮ファイル].tar.gzを置けるか置けないかの違いでした

なぜ今まで(mac)は行けたの?

macはtarの提供元が違った

# mac
bsdtar 2.8.3 - libarchive 2.8.3

FreeBSDっていうところが提供しているbsdtarっていうのが入ってる
https://www.freebsd.org/cgi/man.cgi?query=bsdtar&sektion=1

# Amazon Linux
$ tar --version
tar (GNU tar) 1.26
Copyright (C) 2011 Free Software Foundation, Inc.
使用許諾 GPLv3+: GNU GPL version 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

[参考訳]
これはフリーソフトウェアです. 変更と再配布は自由です.
法律で認められる範囲で「無保証」です.

作者: John Gilmore, Jay Fenlason.
対処

prj/配下に圧縮ファイルを置かない

入ってるものが違うからそりゃエラー変わるよね!

仮想サーバか物理サーバか調べたら変なの出てきた

ちょっと物理か仮想か調べることになったので調べてたら想定外のものが出てきたので
調査方法と他をめも

調べ方

dmidecode

HWの情報を出してくれるコマンド
dmidecode

出力サンプル↓
http://www.nongnu.org/dmidecode/sample/dmidecode.txt

やってみた
$ sudo dmidecode -t system
# Product Name: KVMが出たら仮想

$ sudo dmidecode -s system-serial-number
# Not Specified が出たら仮想
# 上のコマンドのSerial Numberだけ表示

変なの出た

$ sudo dmidecode -t system
# Product Nameを抜粋
Product Name: VMware Virtual Platform

$ sudo dmidecode -s system-serial-number
Serial Number: VMware XX XX XX # 謎のシリアル番号が出る

KVMとかNot Specifiedじゃないけどとても仮想くさい名前が出て困る

VMware Virtual Platform

  • 1999/2/8にVMwareが出した
  • x86ベースのOSを動くようにしたもの
  • 今はVMware Workstation
  • という仮想化製品

名前からして疑いの余地なかったけど仮想でした
仮想だと思ってたサーバーが実は物理だったり逆だったりという調査でした

CakePHPでALLHTTPSに

CakePHPで全ページをhttpsにするのに苦労したときのメモが出てきたのでまとめてたら
当時の自分何してたんだってことに気づきました( ゚∀゚)

一般的な?やり方

SecurityコンポーネントとblackHoleCallbackを使って
こんな感じにAppControllerに以下追加

public $components = array('Security');

public  function beforeFilter(){
    if( $_SERVER['SERVER_PORT'] == 80 ) { // 何らかの手段でhttpであることを判定させる
        $this->Security->blackHoleCallback = 'forceHttps';
        // ↓でアクション単位の設定ができ, 指定なしなので全アクションhttpsになる
        $this->Security->requireSecure();
    }
}

public function forceHttps() {
    // $this->here でURIを取れる
    $this->redirect('https://' . env('SERVER_NAME') . $this->here);
}

でもapacheでhttp→httpsにしたところ、
既に特殊Routingを組まれているせいで上記がこけたのと
Controllerでやるのはなぁと思っていたので下記対応を入れてみた

routesに書く

Router::fullbaseUrl('https://' . $_SERVER["HTTP_HOST"]);

lib/Cake/Routing/Router.phpを見ると

public static function fullBaseUrl($base = null) {
  if ($base !== null) { // fullbaseUrlを設定すると
    self::$_fullBaseUrl = $base;
    Configure::write('App.fullBaseUrl', $base); // 上書きされる!
  }
  if (empty(self::$_fullBaseUrl)) { // 設定されなかったら
    self::$_fullBaseUrl = Configure::read('App.fullBaseUrl'); // 設定を見る
  }
  return self::$_fullBaseUrl;
}

こんな感じでConfigのApp.fullBaseUrlが上書きされている

なので

普通にConfigでやればよかった!
Configure::write('App.fullBaseUrl', 'https://'.$_SERVER["HTTP_HOST"]);

当時の自分、相当必死だったんだろうなぁ...と思った

個人的には

$this->Security->blackHoleCallback = 'forceHttps';

の見た目が気持ち悪くて苦手

cakePHPでapacheでリダイレクトさせようとしたけどだめだったよ

諦めたというオチです

やったこと
RewriteRule ^/helen https://www.google.co.jp/ [R=302,L]

こんな感じでapacheで /helen -> google にリダイレクトさせようとしてたのですがだめでした。

原因
AllowOverride All

これのせいで.htaccessのほうが強くなり、親に設定したリダイレクトが効いてませんでした。

オチ

apacheでリダイレクトできないけど

header('Location: https://www.google.co.jp/');

はしたくなかったのでapp/Config/routes.phpに下記設定。
ref) ルーティング

// 内部遷移
Router::redirect(
    '/helen/index/', 
    array('controller' => 'hoge', 'action' => 'fuga'), 
    array('status' => 301)
);

// もちろん/helen/だけでも外部サイトでも行ける
Router::redirect(
    '/helen/*', 
    'https://www.google.co.jp/', 
    array('status' => 301)
);
おまけ

cakePHPに入っているラスボスはこんな.htaccessです
app/.htaccess at master · cakephp/app · GitHub

ログを見るとこんな感じ。多いのでちょっと整形しました。
DocumentRootは/home/vagrant/webRoot/です

[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] strip per-dir prefix: /home/vagrant/webRoot/helen -> helen
[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] applying pattern '^' to uri 'helen'
[rid#7f8af0cdc7f8/initial] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/helen' pattern='!-d' => matched
[rid#7f8af0cdc7f8/initial] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/helen' pattern='!-f' => matched
[rid#7f8af0cdc7f8/initial] (2) [perdir /home/vagrant/webRoot/] rewrite 'helen' -> 'index.php'
[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] add per-dir prefix: index.php -> /home/vagrant/webRoot/index.php
[rid#7f8af0cdc7f8/initial] (2) [perdir /home/vagrant/webRoot/] strip document_root prefix: /home/vagrant/webRoot/index.php -> /index.php
[rid#7f8af0cdc7f8/initial] (1) [perdir /home/vagrant/webRoot/] internal redirect with /index.php [INTERNAL REDIRECT]
[rid#7f8af0cee110/initial/redir#1] (3) [perdir /home/vagrant/webRoot/] strip per-dir prefix: /home/vagrant/webRoot/index.php -> index.php
[rid#7f8af0cee110/initial/redir#1] (3) [perdir /home/vagrant/webRoot/] applying pattern '^' to uri 'index.php'
[rid#7f8af0cee110/initial/redir#1] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/index.php' pattern='!-d' => matched
[rid#7f8af0cee110/initial/redir#1] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/index.php' pattern='!-f' => not-matched
[rid#7f8af0cee110/initial/redir#1] (1) [perdir /home/vagrant/webRoot/] pass through /home/vagrant/webRoot/index.php

分解すると

# /helenのアクセスのDocumentRootをはずす
[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] strip per-dir prefix: /home/vagrant/webRoot/helen -> helen

# RewriteRuleの ^ をhelenに適応できるか?
[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] applying pattern '^' to uri 'helen'

# RewriteCondそれぞれ確認
[rid#7f8af0cdc7f8/initial] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/helen' pattern='!-d' => matched
[rid#7f8af0cdc7f8/initial] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/helen' pattern='!-f' => matched

# matchedなのでhelen -> index.phpへ書き換え
[rid#7f8af0cdc7f8/initial] (2) [perdir /home/vagrant/webRoot/] rewrite 'helen' -> 'index.php'

# DocumentRootをつける
[rid#7f8af0cdc7f8/initial] (3) [perdir /home/vagrant/webRoot/] add per-dir prefix: index.php -> /home/vagrant/webRoot/index.php

# はずす
[rid#7f8af0cdc7f8/initial] (2) [perdir /home/vagrant/webRoot/] strip document_root prefix: /home/vagrant/webRoot/index.php -> /index.php

# /index.phpへリダイレクト
[rid#7f8af0cdc7f8/initial] (1) [perdir /home/vagrant/webRoot/] internal redirect with /index.php [INTERNAL REDIRECT]

ここでridが変わる

# またDocumentRootを外す
[rid#7f8af0cee110/initial/redir#1] (3) [perdir /home/vagrant/webRoot/] strip per-dir prefix: /home/vagrant/webRoot/index.php -> index.php

# 条件にマッチするか確認するよ
[rid#7f8af0cee110/initial/redir#1] (3) [perdir /home/vagrant/webRoot/] applying pattern '^' to uri 'index.php'

# 条件だよ
[rid#7f8af0cee110/initial/redir#1] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/index.php' pattern='!-d' => matched
[rid#7f8af0cee110/initial/redir#1] (4) [perdir /home/vagrant/webRoot/] RewriteCond: input='/home/vagrant/webRoot/index.php' pattern='!-f' => not-matched

# これを表示するよ
[rid#7f8af0cee110/initial/redir#1] (1) [perdir /home/vagrant/webRoot/] pass through /home/vagrant/webRoot/index.php

.htaccessしか効いてないのに親をいじってapache再起動しまくってたのが虚しい