ラズパイ初心者がRaspberrypiZeroWHを使ってLINEで動くスマートロックを作ってみた(サーバー構築編1)

# 概要

**賃貸のスマートホーム化をしたい！！！**

と思い、「まずは家に入るところから」とスマートロックの購入を検討したところ、安くても10000円以上･･･
僕はこう感じた

**あんな鍵回すだけの機械になんで10000円も払わなあかんねん！！！！**
**こんなんなら自分で作ったるわ！！！**

元からラズパイを触りたかったということもあって、勢いでRaspberrypiZeroWHを購入。
せっかくラズパイを使うなら、なにかサーバーを立てて操作したいと思った。
過去にLINEbotを作ったという経験もあり、LINEbotで操作できるスマートロックを作ることとした。

今回の記事はLINEで動くスマートロック制作のためのサーバー構築編1。
ラズパイのセットアップからラズパイ本体にWebサーバーを構築、https通信でHelloWorldするところまで書いていく。

ただのメモなので再現性はないかも。参考程度に。

## 筆者のスキル
- 研究でnumpyやpandasを使ったデータ解析をしていた
- サーバー関係はEC2を使ってApacheでWebサーバーを構築できるレベル(ほぼ初心者)
- Herokuを使ってLINEbotを作ったことがある
- ラズパイ経験はなし
- 電気回路系は何もわからない

## 今回使うスキル
- Linuxの基本操作
- Flask
- uwsgi
- nginx
- Docker、Docker-compose
- line-bot-sdk

## 必要なもの
- RaspberrypiZeroWH
- ラズパイの電源ケーブル
- サーボモーター(SG92R)
- ジャンパワイヤー3本

## 開発環境
- Windows10

# OSのインストール・SSH通信
宅配ボックスにRaspberrypiが、届き、ワクワクしながら箱を開ける。
最初からSDカードにOSが入ってるとのことなので、早速起動して、いざセットアップ･･･!!


･･･ん･･･？

**あ！！！！！！有線のキーボードがない！！！！！！！！！！！！！！なにも操作できないじゃんか！！！！！！！！！**

どうしよう、さっそく調べる。
どうやらSDカードであらかじめWifiのセッティングファイルをぶち込んでラズパイを起動することで、何も操作せずともラズパイにSSH通信を許可することが出来るらしい。

というわけで、OSのインストール方法とSSH通信の方法を書いていく。

## OSのインストール
RaspberryPiImagerを使うと便利。
こちらを参照

https://qiita.com/karaage0703/items/79e09db0ef3ebcf94882

SSH通信でしか操作しないならGUIもいらないので、
RaspberryPiOS(other) → RaspberryPiOSLiteでOK
GUIが入ってない分かなり軽量。

## SSH
こちらを参照

https://qiita.com/murs313/items/81757bf8bc74b6b76cdf

要約すると、OSの入っているSDカード直下に以下のファイルを作ればOK

```:ssh
(何も書かない)
```

```:wpa_supplicant.conf
country=JP
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
    ssid="WifiのSSID"
    psk="WifiのPW"
}
```

sshに「(何も書かない)」って書いちゃだめだよ！！

ファイルを生成し終えたら、RaspberrypiにSDカードをいれて早速起動！

5分くらい待ってから、自分のPCのコマンドプロンプトから以下のコマンドを実行
初期パスワードはraspberrypi

```:コマンドプロンプト
$ ssh pi@raspberrypi
pi@raspberrypi's password:(入力しても何も表示されないが、ちゃんと入力されてる)
```

色々表示されて、以下のような待ち受けになったら成功

```:コマンドプロンプト
pi@raspberrypi:~ $
```
# Docker、Docker-composeをインストール
今まで使ったことなかったが、勉強のためにDockerを用いてサーバー構築することにした。
実際、使う必要もなかった気がするが･･･
ただ、また同じ環境を作りたくなった際、環境構築が楽になるかもしれない。


## Dockerインストール
```:コマンドプロンプト
pi@raspberrypi:~ $ curl -sSL https://get.docker.com | sh

// グループの追加
pi@raspberrypi:~ $ sudo usermod -aG docker pi

// OS再起動（グループ追加を反映）
pi@raspberrypi:~ $ sudo shutdown -r now

// dockerサービスの有効化
pi@raspberrypi:~ $ sudo systemctl enable docker

// バージョン確認
pi@raspberrypi:~ $ docker --version
Docker version 20.10.5, build 55c4c88
```

## Docker-composeインストール
githubにソースコードが落ちてるらしいので、そっちでインストールしたほうがいいと思うが、
pipでもインストールが可能らしく、個人的に使い慣れてるのでこちらを用いた。
ラズパイにvenv環境が最初から入っていたか覚えていないので、入っていなかったら各自インストールするように！

```:コマンドプロンプト
//venv環境の作成
pi@raspberrypi:~ $ python -m venv venv

//venv環境の中に入る
pi@raspberrypi:~ $ source venv/bin/activate

//pipのアップデート
(venv)pi@raspberrypi:~ $ pip install --upgrade pip

// docker-composeのインストール
(venv)pi@raspberrypi:~ $ pip install docker-compose
```

# nginx+flaskでHTTP通信でHelloWorld!
## docker-composeを用いてnginxサーバーとflaskサーバーをuwsgiを用いて繋げる
こちらを参照。

https://dafukui.hatenablog.com/entry/2018/12/19/172901

基本この記事のディレクトリ構成をパクればうまくいくはず。
あ、ただ、docker-composeは環境変数が設定されてないのでコマンド実行時は~/venv/bin/docker-composeと絶対パスor相対パスを指定しなければならないので注意。めんどくさい人は環境変数を設定してくれ！

## ルーターのポートを開放してhttpリクエストを送信！
この記事の通りのディレクトリ構成にした後、自身のルーターのポート番号80番を開けて早速アクセス！
ルーターのポートの開け方は多分自身のルーターの説明書に書いてあるので、各自確認。

グローバルIPを確認したらブラウザに入力
http://グローバルIP

因みにグローバルIPアドレスはこのサイトで見れる。
https://www.cman.jp/network/support/go_access.cgi

![2021-04-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533399/291d4ac6-c87a-1e8e-0eec-b67223e70dad.png)

**成功！**

# ドメイン、DDNS取得
こちらの記事を参照

https://qiita.com/mizuki_takahashi/items/b0c5adebea48b9f2f7a6

お名前.comでドメインを取得し、グローバルIPと関連付ける。
.workドメインであれば年間1円で取得できるから実質タダ。

グローバルIPは時々刻々で変化してていくので、DDNSサービスを用いる必要がある。
DDNSサービスを用いれば変化するグローバルIPとドメインを関連付けることが出来る。
DDNSサービスにはmydnsを用いた。

お名前.comでドメイン取得し、mydnsで動的IPとドメインを関連付けたら早速アクセス
http://www.自分の登録したドメイン

先ほどと同様HelloWorld!が表示されたら成功！

# https通信
主な流れは以下の通り

1. SSL証明書発行前の準備(main.pyを少し改造)
1. Let's encryptで証明書発行
1. nginxの設定ファイルを編集してポート443番解放、.pemを関連付ける
1. ymlファイルの.pemの位置をマウントしdocker-compose実行
1. ルーターのポート443を開放し、ブラウザでhttpsアクセス

## SSL証明書発行前の準備
main.pyに以下の項目を追加。
これを追加しなきゃ正しく証明書を発行できない。

```python3:main.py  
from flask import Flask, render_template, request, abort

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "hello world!"

############ ここを追加!! ###############################################
@app.route("/.well-known/acme-challenge/<filename>")
def well_known(filename):
    return render_template('.well-known/acme-challenge/'+ filename)
########################################################################

if __name__ == "__main__":
    app.run()
```

main.pyのファイルが入っているところにtemplate/.well-known/acme-challengeを作る

```:コマンドプロンプト
(venv)pi@raspberrypi:mkdir -p ~/app/template/.well-known/acme-challenge
(venv)pi@raspberrypi:touch ~/app/template/.well-known/acme-challenge/test.html
```

## Let's encryptで証明書発行

```:コマンドプロンプト
(venv)pi@raspberrypi:sudo yum install certbot-apache
(venv)pi@raspberrypi:sudo certbot certonly
```

対話形式で指示に従って入力していく
How would you like to authenticate with the ACME CA?の質問に対してはwebrootの方を選択
あとはメアドとかドメインとか適当に。

/etc/letsencrypt/live/ドメイン名/ にpemファイル等が作成されてたら成功！

これをnginxディレクトリにコピー

```:コマンドプロンプト
(venv)pi@raspberrypi:mkdir ~/nginx/ssl
(venv)pi@raspberrypi:sudo cp /etc/letsencrypt/live/ドメイン名/fullchain.pem ~/nginx/ssl
(venv)pi@raspberrypi:sudo cp /etc/letsencrypt/live/ドメイン名/privkey.pem ~/nginx/ssl
```

## nginxの設定ファイルを編集してポート443番解放、.pemを関連付ける
nginxの設定ファイルに以下の項目を追加

```:~/nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    upstream uwsgi {
        server uwsgi:3031;
    }

    server {
        listen 80;
        charset utf-8;

        location / {
            include uwsgi_params;
            uwsgi_pass uwsgi;
        }
    }

    ##############ここを追加######################################
    server {
	listen 443 ssl;
	charset utf-8;
        location / {
            include uwsgi_params;
            uwsgi_pass uwsgi;
        }

	ssl_certificate       /etc/nginx/ssl/fullchain.pem;
	ssl_certificate_key   /etc/nginx/ssl/privkey.pem;
    }
    #############################################################
}
```

## ymlファイルの.pemの位置をマウントしdocker-compose実行
ymlファイルに以下の項目を追加

```:~/docker-compose.yml
version: "3"
services:

  uwsgi:
    # ビルドするDockerfileのでディレクトリ相対パス
    build: ./app
    # 指定したパスをコンテナにマウントする。"ホストのパス:コンテナのパス"となる
    volumes:
      - ./app:/var/www/
    # 解放するポートを指定。"ホスト:コンテナ"のマッピング となる
    ports:
      - "3031:3031"
    # コンテナ内の環境変数を指定する
    environment:
      TZ: "Asia/Tokyo"

  nginx:
    build: ./nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      # nginxのログをホストOSの /tmp/nginx_log に出力する
      - /tmp/nginx_log:/var/log/nginx
      ############ここを追加####################
      - ./nginx/ssl:/etc/nginx/ssl/
      #########################################
    links:
      - uwsgi
    ports:
      - "80:80"
      ################ここを追加################
      - "443:443"
      #########################################
    environment:
      TZ: "Asia/Tokyo"
```

ymlファイルの設定が完了したら、docker-composeを実行

```:コマンドプロンプト
//docker-composeを実行
(venv)pi@raspberrypi:~/venv/bin/docker-compose build && ~/venv/bin/docker-compose up -d
// 正しく実行されているか確認
(venv)pi@raspberrypi:~/venv/bin/docker-compose ps
```

## ルーターのポート443を開放し、ブラウザでhttpsアクセス
自身のルーターの443ポートを開放。ルーターの説明書を参考に。
ブラウザでhttpsアクセスを試す
https://www.ドメイン名

HelloWorldが出力されたら成功！！！


疲れたので、今回はここまで！
次回はLINEとWebhookしてLINEbot作るよ！