RaspberryPiにDockerを構築する

RaspberryPi上にDockerを走らせて、実際にミニサーバー代わりに使っている様子をネットに公開している人が少ないと思ったので記録に。あと今までどうやって設定を行ったかを記録代わりに。ラズパイの初期設定とかは何番煎じか分からないけど。

**注意**：以下の記述は2020年3月執筆時点の情報です.一部古い場合があります.

## 対象機種

- Raspberry pi 3 B+
- Raspberry pi 4 B (4GB RAM)

## 初期設定

注意:[Raspberry PiにRaspbianをインストールする for Mac OSX](https://qiita.com/ttyokoyama/items/7afe6404fd8d3e910d09)の記述は古くなっています。2021年3月時点で簡単なSD書き込み方法は「Raspberry Pi Imager」を使用することです。[詳しくはこの記事を参照してください](https://qiita.com/k_ken/items/9a71655400798f84d636#ラズパイにosをインストールする)。

### [Raspberry PiにRaspbianをインストールする for Mac OSX](https://qiita.com/ttyokoyama/items/7afe6404fd8d3e910d09)
OSをSDカードに書き込む

- SDカードを挿して、カードの場所を調べる。`/dev/disk2`だった。

```bash
$ diskutil list
```

- SDカードをアンマウントする。

```bash
$ sudo diskutil unmountDisk /dev/disk2
```

- `/dev/disk2`にraspbianを書き込む。書き込む場所を間違えると最悪パソコンのデータを完全消去することになる。

```bash
$ sudo dd bs=1m if=/your/directory/2019-09-26-raspbian-buster.img of=/dev/disk2
```

### 無線LAN設定
ここからは、`raspberry pi`を直接ディスプレイに表示して操作。

- `iwlist`で、Wi-FiのSSIDを調べる

```bash
$ sudo iwlist wlan0 scan | grep ESSID
```

- `/etc/wpa_supplicant/wpa_supplicant.conf`にWi-Fi設定を記述する。
    - 国設定は必須(`country=JP`)。
    - Wi-Fiパスワードが平文なのが気持ち悪ければ、`sudo wpa_passphrase "SSID" "pass" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf`と暗号化されたパスワードを書き加えて、平文は消す。

```bash
$ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
 
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=JP
network={
   ssid="Free001"
   psk="password"
   key_mgmt=WPA-PSK
}
```

- ラズパイを再起動。`ip a`でWi-Fiに接続しているかを確認。`wlan0`に`inet 192.168.nn.nn`とipアドレスが書かれていれば接続に成功している。

```bash
$ ip a
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.nn.nn/24 brd 192.168.0.255 scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
```

#### 参考サイト
- [[Raspberry Pi] Wi-FiやIPアドレスの設定を行う方法](https://dev.classmethod.jp/hardware/raspberrypi-remote-connect/)
- [Raspbian のWi-Fi設定（Raspberry Pi 初期設定）](https://raspida.com/wifi4raspbian#toc9)

### ssh有効化

`sudo raspi-config`で`raspi-config`を起動。`5 Interface Options` > `P2 SSH` を選択してSSHを有効化する。

`ssh pi@192.168.nn.nn`等で接続できるか確認。これ以降はリモートでラズパイを操作可能。

- [Raspberry Pi(Raspbian)でSSH接続する方法＆SSHのセキュリティ強化設定](https://nobuntu.jp/raspi-raspbian-ssh-setup/)
- [Raspberry Pi 3 model BでSSH接続の設定をする](https://blog.ko31.com/201803/raspi-ssh-setup/)

### 固定IP設定

ラズパイのIPアドレスをいちいち調べるのは面倒なので、固定IPを割り振る。`dhcpcd.conf`に以下を追記する。

```bash
$ sudo nano /etc/dhcpcd.conf
```

```text:/etc/dhcpcd.conf
interface wlan0
static ip_address=192.168.xx.xxx/24
static routers=192.168.xx.x
static domain_name_servers=192.168.xx.x 8.8.8.8 8.8.4.4
```
#### 参考リンク
- [[Raspberry Pi] Wi-FiやIPアドレスの設定を行う方法](https://dev.classmethod.jp/hardware/raspberrypi-remote-connect/)

### パッケージの更新

パッケージの更新。とりあえず以下のコマンドを実行する。

```bash
$ sudo rpi-update #ファームウェアとOSのカーネルをバージョンアップ
$ sudo reboot #再起動
$ sudo apt update #OSのパッケージを管理しているリストを更新
$ sudo apt full-upgrade #パッケージの更新
$ sudo apt-get autoremove -y 
$ sudo apt-get autoclean #SDカードの空き容量を確保する為、不要ファイルを削除
$ sudo reboot #再起動
```
#### 参考リンク
- [Updating and upgrading Raspbian](https://www.raspberrypi.org/documentation/raspbian/updating.md)
- [raspberrypiのOSを更新する方法](https://qiita.com/akiraichi5430/items/6b9855f59fb3a3f9de35)

### [ラズパイにSSH接続（鍵認証）](https://qiita.com/kuro___inu/items/93da8aa9b56847c3a2bf)

sshのログイン方法を公開鍵認証にする。

- ssh接続元で鍵生成をする

```bash
ssh-keygen -t rsa
```

- 一旦、ラズパイ(ssh接続先)に`ssh`接続した後。ホームディレクトリ(`~/`)で隠しディレクトリ`.ssh`等々の作成を行う。

```bash
# ログイン
$ ssh hoge_user@hogehoge.hoge.com
# フォルダとファイルを作る(既に存在している場合は実行しなくても可)
$ mkdir .ssh
$ touch .ssh/authorized_keys
```

- **公開鍵**(デフォルトだと`id_rsa.pub`)を転送。

```bash
$ scp ~/.ssh/id_rsa.pub pi@192.168.nnn.nnn:~/.ssh
```

- 権限設定。公開鍵を`authorized_keys`に登録。

```bash
# 権限の設定。個々を間違えると、公開鍵認証でログイン出来ないので注意
$ sudo chmod 700 .ssh
$ cat id_rsa.pub >> .ssh/authorized_keys # 公開鍵の内容を追加。
$ sudo chmod 600 .ssh/authorized_keys 
$ rm id_rsa.pub # SCPした公開鍵を削除削除
```

- `/etc/ssh/sshd_config`を変更してsshの各種設定を行う。以下の各種設定は万が一攻撃者がきても簡単に侵入させないようにするため。

```text:/etc/ssh/sshd_config
# port番号設定
Port nnnnn

# rootログインの禁止
PermitRootLogin no

# 鍵認証を有効化
PubkeyAuthentication yes

# パスワード認証を無効化
PasswordAuthentication no
```

- sshサーバーを再起動

```bash
$ sudo /etc/init.d/ssh restart
```

- 接続の確認

```bash
$ ssh -i .ssh/[非公開鍵] -p [port番号] pi@nnn.nnn.nnn.nnn
```

#### 参考サイト

- [【手順つき】SSHの公開鍵認証のやり方](https://eng-entrance.com/linux-ssh-key)
- [ssh公開鍵認証設定まとめ](https://qiita.com/ir-yk/items/af8550fea92b5c5f7fca)


### [Pi user-change or create new](https://www.raspberrypi.org/forums/viewtopic.php?t=227620)

新しいユーザーを作成してデフォルトのpiユーザーを無効化する。これもセキュリティ対策で攻撃者にユーザー名を推測しにくくするため。

- 新しいユーザーを作成

```bash
$ sudo adduser newusername #新しいユーザーを作成
```

- 新しいユーザーにpiと同じグループに所属させる

```bash
$ sudo usermod -a -G adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,spi,i2c,gpio newusername #新しいユーザーにpiと同じグループに所属させる
```

- 新規作成したユーザーにログイン
- piユーザーを無効化する

```bash
$ sudo usermod -L -s /bin/false -e 1 pi #piユーザーを無効化する
```

上のコマンドはなんだかやりすぎな気がするので、以下でもアカウントをロック可能

```bash
$ sudo passwd --lock pi
```

### [ファイアウォール設定](https://qiita.com/morinokami/items/aee081085dd679b1ea22)

ファイアウォールを設定するのが楽な`ufw`でファイアウォールを設定。

```bash
$ sudo apt-get install ufw  # ufwのインストール
$ sudo ufw allow 99  # ポートの開放
$ sudo ufw default deny  # 許可されたポート以外を閉じる
$ sudo ufw enable  # ufwの有効化
$ sudo systemctl status ufw #systemctlでも有効になっているか確認
```

もう少し細かく設定したい場合は[iptablesが難しいためufwでWEBサーバーのファイアウォール設定](https://qiita.com/shimakaze_soft/items/c3cce2bfb7d584e1fbce)を参照。

## SDカードの寿命を延ばす

連続稼働が前提にするので、なるべくSDカードが壊れにくい設定にする。
[Raspberry PiのSDカードが壊れた！寿命を延ばす方法 5+1選!](https://iot-plus.net/make/raspi/extend-sdcard-lifetime-5plus1/)を参考に。

### スワップを無効にする

スワップ領域を確認。

```
$ free -h
              total        used        free      shared  buff/cache   available
Mem:          3.8Gi       170Mi       3.1Gi        46Mi       521Mi       3.5Gi
Swap:          99Mi          0B        99M
```

スワップ設定を切る。

```bash
$ sudo swapoff --all
```

ラズパイのスワップファイルは`dphys-swapfile`を使っていて自動起動されているので、それも切る。

```bash
$ sudo systemctl stop dphys-swapfile #停止
$ sudo systemctl disable dphys-swapfile #自動起動停止
$ systemctl status dphys-swapfile #停止されたか確認

 dphys-swapfile.service - dphys-swapfile - set up, mount/unmount, and delete a 
   Loaded: loaded (/lib/systemd/system/dphys-swapfile.service; disabled; vendor 
   Active: inactive (dead)
```

### 不要パッケージの削除
SDカードの寿命とは関係ないけれど、不要なパッケージを消す

```bash
$ sudo apt-get autoremove -y python-pygame #ゲーム作らないので
$ sudo apt-get autoremove -y galculator #GUI計算機も消す
$ sudo apt-get remove --purge libreoffice* #Libre Officeを消す
$ sudo apt-get clean
$ sudo apt-get autoremove
```


## Raspberry Piにdockerとdocker-composeを導入
以下のコマンドで簡単にdockerをインストール。

- [Raspberry Piにdockerをインストールする](https://qiita.com/HyunwookPark/items/6e4854248c6d63958060)


```bash
$ curl -sSL https://get.docker.com | sh
```

`pip`を使えば簡単にdocker-composeをインストールできる。

- [How To Build a FastApi Server to Control a Raspberry Pi](https://medium.com/better-programming/how-to-build-a-docker-compose-app-for-a-raspberry-pi-7d7003b4cbc)

```bash
$ sudo pip3 install docker-compose
```

パスが通っていない場合があるので、必要ならパスを通す。`.bashrc`に以下を追記。`source .bashrc`で設定を反映。再起動してもパスが通っているか確認する。

```bash:.bashrc
export PATH="$PATH:~/.local/bin"
```

## Docker buildx
ラズパイは`ARM`なので、世の中を支配しているインテルの`x_86`とは違うCPUアーキテクチャー。よって、ほとんどのパソコンのDockerイメージとラズパイのDockerイメージに互換性がない。

Docker公式イメージ以外のほとんどのイメージは`x_86`ベースなので、一からラズパイで`ARM`アーキテクチャーのイメージを構築する必要性が出てくる。ラズパイでいちから構築するよりは`x_86`のパソコン/サーバーでARM版のイメージを作ってから、イメージをラズパイに転送する方が効率が良い。~~`buildx`で構築する方法を記す予定でしたが、ココで力尽きたのでリンク先をみてください（他力本願）。~~`buildx`を試した結果を[記事にしました](https://qiita.com/k_ken/items/1e5ec65554b5adfe22bf)ので参照してください。

- [How I built ARM based Docker Images for Raspberry Pi using buildx CLI Plugin on Docker Desktop?](https://collabnix.com/building-arm-based-docker-images-on-docker-desktop-made-possible-using-buildx/)
- [Raspberry PiでDockerを動かす](https://qiita.com/koduki/items/0ed303dac5d32646194f)
- [RaspberryPiでdocker構築に時間がかかった時のtips](https://qiita.com/k_ken/items/1e5ec65554b5adfe22bf)

軽いdockerイメージならraspberry pi上で`docker-compose build`とビルドするのも一つの手。公式Dockerイメージならば`ARM`アーキテクチャーのイメージがあるので、簡単な環境なら公式イメージを基に構築すればよい。~~ほぼゼロから構築するのでやっぱりDockerの恩恵にあずかれない。~~やり方は`おまけ：状態監視`を参照。

- [docker-compose コマンドまとめ](https://qiita.com/wasanx25/items/d47caf37b79e855af95f)
- [docker-compose build](https://docs.docker.com/compose/reference/build/)

## 結論

[データ収集環境](https://qiita.com/d0d0npa/items/c1550370fbe016288b7e)をラズパイに移行。`Raspberry Pi`買って拡張していくよりもミニデスクトップPC買った方が安いんじゃないのかと思わなくもないけれど、足りない周辺機器とか諸々含めると3,4万円

- `t2.micro`で動かして10時間かかっていたスクレイピング作業が、6時間に短縮
- 最初は1000円/月で動いていた[データ収集環境](https://qiita.com/d0d0npa/items/c1550370fbe016288b7e)も収集データが増えることで収集時間が長くなり2000円/月に。この金額はEC2無料枠を使い切ったうえでの値段なので、無料枠がなくなったときの値段が怖い。

## おまけ：状態監視
### ラズパイの温度を調べる
ラズパイの温度とか知りたい場合は以下のコマンド([RaspberryPi(Raspbian)のCPUの温度をcatを使わないで取得してみた](https://qiita.com/kouhe1/items/5f3f0e6fdb55e7164deb))

```bash
$ vcgencmd measure_temp
```

### Netdata

Raspberrypi 4BはCPU温度が気になってしまいますが、いちいちコマンド打つのも面倒なのでNetdataでラズパイをモニターする。~~`Tecnativa/docker-socket-proxy`は`AMD`でビルドされていないので、ラズパイでbuildする必要がある。~~2021年3月に[Docker hub](https://hub.docker.com/r/tecnativa/docker-socket-proxy)で`linux/arm/v7`のイメージが作成されているのが確認できたので、そちらを利用してください.

もし最初から構築したい場合の手順としては以下の通り

1. [githubのレポジトリ](https://github.com/Tecnativa/docker-socket-proxy)をクローン。
2. クローンしたディレクトリから`Dockerfile`と`haproxy.cfg`を取り出して、`docker-compose.yml`と同一ディレクトリに配置。
3. `docker-compose.yml`を置いてあるディレクトリに移動して`docker-compose build`を実行。

- [Install Netdata with Docker](https://docs.netdata.cloud/packaging/docker/)
- [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)

```bash:docker-compose.yml
version: '3'
services:
  netdata:
    image: netdata/netdata
    ports:
      - 19999:19999
    cap_add:
      - SYS_PTRACE
    security_opt:
      - apparmor:unconfined
    volumes:
      - /etc/passwd:/host/etc/passwd:ro
      - /etc/group:/host/etc/group:ro
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    environment:
      - DOCKER_HOST=proxy:2375
  proxy:
    build:
      context: ./
      dockerfile: Dockerfile
    restart: always
    volumes:
     - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CONTAINERS=1
```

温度を取得するのであれば有志が作ってくれたDockerイメージを使う。

- [Raspberry Piの負荷や温度をモニタするにはNetdataが便利です](https://signal-flag-z.blogspot.com/2019/12/raspberry-pi-netdata.html)
- [Dockerfiles/Netdata-Rpi/](https://github.com/SignalFlagZ/Dockerfiles/tree/master/Netdata-Rpi)

あるいは`/usr/lib/netdata/conf.d/charts.d.conf`の設定ファイルで`# sensors=force`とコメントアウトされているのを`sensors=force`とコメントを外せばCPU温度が有効化される。

- [Netdata for IoT](https://docs.netdata.cloud/docs/netdata-for-iot/)

## おまけ:PiJuice

24時間稼働前提のミニサーバーとして使うので、UPSとしてPiJuiceを購入。

PiJuiceをRaspberryPiにさす前に、`PiJuice-gui`をインストール（詳しいドキュメントは[PiSupply/PiJuice](https://github.com/PiSupply/PiJuice)や[README](https://github.com/PiSupply/PiJuice/blob/master/Software/README.md)）。

> Note: 'pijuice_cli' must be run as user Pi

[README](https://github.com/PiSupply/PiJuice/tree/master/Software#pijuice-cli)を読むと`pi`ユーザーが存在することが前提なので、`pi`ユーザーを無効にした場合は`apt-get install pijuice-gui`でうまくいかない場合がある。その時はマニュアルインストールをする（以下の手順ではマニュアルインストールはcliまでしかインストールしていない）。

### インストール

```bash
$ sudo apt-get install pijuice-gui
```

### マニュアルインストール

```bash
$ sudo apt-get install python3-urwid
```

[gitレポジトリ](https://github.com/PiSupply/PiJuice/tree/master/Software/Install)から最新の `deb`ファイルをダウンロードして、インストール。

```bash
$ wget https://github.com/PiSupply/PiJuice/raw/master/Software/Install/pijuice-base_1.5_all.deb
$ sudo dpkg -i ./pijuice-base_1.5_all.deb
```

`PiJuice`に`Raspberry Pi 4`を使う場合は`pijuice_cli`のステータスが起動しない([関連イシュー](https://github.com/PiSupply/PiJuice/issues/350))。`/boot/config.txt`に以下の一文を追加する。

```bash:/boot/config.txt
dtoverlay=i2c-bcm2708
```