VPS + Flask で、おうちの状況をどこでも確認

* 今年は Advent Calendar に 2 つ参加することができました。
	* もうひとつは[こちら](https://qiita.com/andromedroid/items/d29072d807e24d2dbfbd)です。

## Prologue

* IoT 家電が普及してきたことで、家の各種状態を
外出先から見るということの需要が増えてきました。

* でも、家庭内 LAN の機器に WAN から接続するためには
[NAT 越え](https://ja.wikipedia.org/wiki/NAT_traversal)などのややこしい話をクリアする必要があり、
一歩間違えば家庭内 LAN を`脆弱`にしてしまうリスクもあるため、
安易にオススメできません。

* そこで…

## VPS を使って家庭内 LAN と公開サーバーを切り離そう

* 今は VPS のプロバイダーや契約プランが増え、ワンコインから
契約できるため、気軽に利用できるようになりました。

* VPS を利用して、家庭内 LAN の機器を WAN に公開することなく、
家庭内の状況を監視するようにします。

## 仕組み概念図

![diagram_home_report.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275706/b9ba8a57-2686-9614-76ee-2e2eaf5d1a27.png)

* 家庭内 LAN 側からのアクションは `upload` だけなので、  
家庭内の機器がセキュリティリスクに曝されることはありません。

## VPS を準備する

* まだ VPS を契約していない方々
	* 契約してください！
	* ググったら各社の特徴比較の記事がいっぱいあります。

* 準備の内容は、ここでは省略します。
	* ご契約の会社が公開している手順をご覧ください。
	* どの手順でも、終わった段階で作業 PC から VPS に  
	ssh で接続できるようになっているはずです。

## 家庭の状況を収集し、upload する仕組みを作る。

* ここでは簡単な事例として、RasPi で定期的に vmstat を実行し、  
その直近 10 回を表示することにします。
	* ここでは、[こちら](https://qiita.com/andromedroid/items/d29072d807e24d2dbfbd)で準備した Pi4 を使います。

### 秘密鍵と公開鍵を作る

* まず RasPi から password 認証無しに VPS に  
ssh で login できるように秘密鍵と公開鍵を準備します。

~~~
pi $ ssh-keygen -t ed25519
~~~

* デフォルトのままポチポチしたらこうなっていると思います。

~~~
pi $ ls ~/.ssh/
id_ed25519	id_ed25519.pub
~~~

* VPS をセットアップした作業 PC から rsync で  
`id_ed25519.pub` を引っ張ってきて、VPS に上げます。

~~~
PC $ rsync -av pi@192.168.xxx.yyy:~/.ssh/id_ed25519.pub ./
PC $ rsync -av id_ed25519.pub [VPS USER]@[VPS IP]:~/
PC $ ssh [VPS USER]@[VPS IP]
VPS $ cat ~/id_ed25519.pub >> ~/.ssh/authorized_keys
~~~

* ~/.ssh/config を書きます。
	* ホスト名は、ここでは仮に `flask-vps` とします。

~~~
Host    flask-vps
    HostName    aaa.bbb.ccc.ddd
    Port        xxxxx
    User        username
    IdentityFile    ~/.ssh/id_ed25519
~~~

* RasPi から VPS に login できることを確認します。

~~~
pi $ ssh flask-vps
VPS $ 
~~~

### make_report.sh

* 次に vmstat の結果と、実行した日時を txt に出力する script を作ります。

~~~bash
#!/bin/bash

date
echo
vmstat -S M 2 5
echo
~~~

* ここの内容をもっと工夫することで、様々な情報を収集することができます。

### submit.sh

* 更に make_report.sh で `log_report.txt` を作成し、定期的に
最後の方を切り出して VPS に upload する shellscript を作ります。
	* ファイル名のネーミングが今イチですがご容赦ください。

* 無限 loop で service として動かすため、ファイルを別にしました。
	* make_report を function にして 1 ファイルにすることもできます。

* upload する VPS のディレクトリは、ここでは
`~/workspace/home_report/` とします。

~~~bash
#!/bin/bash

LOG='log_report.txt'
PERIODIC_MINUTES='10'
VPS_SVR="flask-vps:~/workspace/home_report/static"

while :
do
	./make_report.sh >> $LOG
	cat $LOG | tail -n 100 > recent_${LOG}
	rsync -av recent_${LOG} $VPS_SVR/
#	exit 0
	sleep $(($PERIODIC_MINUTES * 60))
done
~~~

* 実行した後、VPS 側できちんと upload できていることを確認します。

* できていたら、RasPi で submit.sh を service 実行します。

~~~
$ nohup ./submit.sh &
$ exit
~~~

* これで、RasPi が稼働中で、ネットワークに接続されている間は  
定期的に `recent_log_report.txt` が VPS に upload されます。
	* シャットダウンや再起動を行うと停止するので、  
	その際はまた `nohup ./submit.sh &` してください。

## upload された家庭の状況を Flask で公開する

* ここまでの仕組みで `~/workspace/home_report/static/`  
の中に定期的にデータが上がってくるので、あとはこのデータを  
Web ページのコンテンツとして表示するだけです。

### home_report.py

* あまり書くことないです。

~~~python
"""
advent calendar 2019 flask.
"""

from __future__ import absolute_import, division, print_function, unicode_literals
import sys
from os import path
from flask import Flask, render_template

app	= Flask( __name__)

@app.route( '/')
def entrance() :

	return	"{}".format( __name__)

@app.route( '/home-report')
def default_report() :

	with open( 'static/recent_log_report.txt', 'r') as file :
		report	= file.readlines()

	return	render_template( 'report.html', report = report)

def main( argv) :

	host_ip		= argv[1]
	host_port	= int( argv[2])

	app.run(	host	= host_ip,
				port	= host_port )

if __name__ == "__main__" :

	if len( sys.argv) < 3 :
		print( "USAGE: {:s} [ip] [port]".format( sys.argv[0]))
		sys.exit( 1)

	main( sys.argv)
~~~

### templates/report.html

* 最低限です。

~~~html
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title>Home Report</title>
		<style>
			div.report {
				background-color	: #EEE;
				font-family			: monospace;
				max-width			: 800px;
				padding				: 20px;
				margin				: 0 auto 0 40px;
			}
		</style>
	</head>
	<body>
		<h1>Home Report</h1>

		<div class="report">
			<!-- {{report}} -->
			{% for line in report %}
				{{line}}<br>
			{% endfor %}
		</div>
	</body>
</html>
~~~

### VPS 側で Flask を実行する

* ここまでで、VPS の作業ディレクトリの中はこんな感じです。

~~~
~/workspace/home_report/
|-- home_report.py
|-- static/
|  `-- recent_log_report.txt
`-- templates/
   `-- report.html
~~~

* service として実行します。

~~~
VPS $ export IP=`networkctl status | grep -i address | awk '{print $2}'`
VPS $ export PORT='xxxxx'
VPS $ nohup python3 home_report.py $IP $PORT &
VPS $ exit
~~~

### 完成

* ブラウザーで VPS の `http://IP:PORT/home-report` にアクセスすると、
`static/recent_log_report.txt` の内容が表示されました。

	![ss_home_report.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275706/5b9f307a-a457-0056-3cb7-f114fd1bd8d4.png)

	* 見た目はショボいですが、  
	直近の家庭の状況が見られるサイトができました。

## Epilogue

* 今回は RasPi の情報だけでしたが、
RasPi から収集可能な家庭内のテキストデータや画像なら
何でも upload することができるため、汎用性は高いです。  

* VPS の入門ネタとしても、ぜひご活用ください :smiley:

---

## EOF