Raspberry Pi 4 に Raspberry Pi OS Lite をインストール

# 概要

個人的な「Raspberry Pi OS Lite」 （最小構成版のラズパイOS）設定をまとめました。

- サーバー用途とするので必要最低限のアプリのみ
- SDカードは摩耗が早い問題があるので、HDDを使う

# 環境

インストール対象の環境

- Raspberry Pi 4　Model B (8GB)
- 500GB 2.5インチHDD
  - HDDへのUSB変換アダプタで接続

インストールしたRaspberry Pi OSバージョン

```bash
uname -a
```

```txt
Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux
```

# 手順

## Raspberry Pi OS をインストールしたHDDを作成

一昔前はSDカードが必須と思いましたが、今は不要なようです。

### Raspberry Pi Imager をインストール

[Raspberry Pi OS – Raspberry Pi](https://www.raspberrypi.com/software/) からインストールし、Raspberry Pi Imagerを起動

### Raspberry Pi Imager 設定 & OSインストール済みHDD作成

Raspberry Pi Imagerを起動したら、下記の通り設定

- 「OSを選ぶ」 > Raspberry Pi OS (other) > 「Raspberry Pi OS Lite (64-bit)」 を選択
- 「ストレージを選ぶ」 > USB接続したHDDを選択
- 設定（右下の歯車アイコン）
  - 「SSHを有効化する」にチェック
  - 「公開鍵認証のみを許可する」に接続予定の端末のSSH公開鍵を指定
  - 「ユーザ名とパスワードを設定する」で、適当にユーザとパスワードを設定
    - 後々ログインや`su`コマンドで必要
  - 「ロケール設定をする」
    - タイムゾーンに「Asia/Tokyo」を選択
    - キーボードレイアウトに「jp」を選択 （SSH接続オンリーの予定なので、キーボードつなぐことはないと思うけど）

## Raspberry Piへの設定

### Raspberry Piの起動

先ほど作成したOSインストール済みHDDをラズパイへ接続し起動する。

### サーバにssh接続する

まずは、DHCPで繋がっているはずであろうラズパイのIPを知る必要があります。
自分はルーターの管理画面に接続機器一覧があるためすぐわかりました。
MACアドレスの範囲が決まっているのでそれで判断します。

### apt設定

OSの自動更新を無効にします。

```bash
sudo systemctl stop apt-daily.timer
sudo systemctl disable apt-daily.timer
sudo systemctl stop apt-daily-upgrade.timer
sudo systemctl disable apt-daily-upgrade.timer
sudo chmod -x /etc/cron.daily/apt-compat
# sudo chmod -x /etc/cron.daily/aptitude
```

### 電源LEDの設定変更

ラズパイ上にあるLEDの挙動を、起動中は点滅、シャットダウン後は点灯するよう変更します。
デフォルトでは、電力が足りていれば常に赤く点灯します。電力不足で点滅になります。
とくに電源周りに問題がない場合は起動中やシャットダウン後でも変わらず赤く点灯し続けるため、ラズパイが起動中かどうか判断しづらいです。

```bash
cat << EOS | sudo tee -a /boot/config.txt
dtparam=pwr_led_trigger=heartbeat
EOS
```

すべてのLEDを常に消灯させたい場合は下記コマンドを入力します （有線LAN（イーサネット）のLEDも消灯します）。

```bash
cat << EOS | sudo tee -a /boot/config.txt
# Disable the PWR LED
dtparam=pwr_led_activelow=off
# Disable the Activity LED
dtparam=act_led_trigger=none
dtparam=act_led_activelow=off
# Disable Ethernet LEDs
dtparam=eth_led0=14
dtparam=eth_led1=14
EOS
```

参考

- [kernel - Raspberry Pi 3b+ - Turning off power LED from /boot/config.txt no longer works with kernal 5.15.56-v7+ - Raspberry Pi Stack Exchange](https://raspberrypi.stackexchange.com/questions/138766/raspberry-pi-3b-turning-off-power-led-from-boot-config-txt-no-longer-works-w)

### NTPの設定

時刻合わせをNTPで行うよう設定します。

```bash
cat << EOS | sudo tee -a /etc/systemd/timesyncd.conf
NTP=ntp.jst.mfeed.ad.jp ntp.nict.jp
FallbackNTP=time.google.com
EOS
sudo systemctl daemon-reload
sudo systemctl restart systemd-timesyncd.service
```

### 固定IP設定

デフォルトではDHCPなので、固定IPへ変更します。
下記は例です。

```bash
sudo nmcli connection modify 'Wired connection 1' \
ipv4.addresses "192.168.1.100/24" \
ipv4.gateway "192.168.1.1" \
ipv4.dns "8.8.8.8 8.8.4.4" \
ipv4.method "manual"
```

<!-- TODO Wi-Fi接続は？ -->

### ホスト名を変更

管理のためホスト名を変更しておきます。

```bash
sudo hostnamectl set-hostname rpi
```

## 仮想メモリを無効化

8Gメモリのラズパイなので要らないかなと思い設定しました。
必要に応じてオンにします。

```bash
free -h
sudo swapoff --all
sudo apt-get purge -y --auto-remove dphys-swapfile
sudo rm -fr /var/swap

# 確認
free -h
```

仮想メモリをオンにする設定も記載しておきます。

```bash
swapfile_size=2GB
swapfile_path=/swapfile

sudo fallocate -l "$swapfile_size" "$swapfile_path"
sudo chmod 600 "$swapfile_path"
sudo mkswap "$swapfile_path"
sudo swapon "$swapfile_path"
ls -lh "$swapfile_path"
swapon --show
sudo cp /etc/fstab /root/fstab.default
echo "$swapfile_path none swap sw 0 0" | sudo tee -a /etc/fstab
```

### ssh設定

sshポートやルートログイン禁止などを設定します。

```bash
sudo cp /etc/ssh/sshd_config /root/sshd_config-default
cat << EOS | sudo patch -p1 -f /etc/ssh/sshd_config
--- /root/sshd_config-default
+++ /etc/ssh/sshd_config
@@ -14 +14 @@
-#Port 22
+Port 2222
@@ -33 +33 @@
-#PermitRootLogin prohibit-password
+PermitRootLogin no
EOS

# 確認
sudo /usr/sbin/sshd -t
# （設定ファイルに間違いがなければ何も表示されない）
```

指定されたGithub.comユーザの公開鍵リストを取得します。
こうすることで、普段`git clone`に使用している秘密鍵で、このサーバにssh接続出来るようになります。（最近のUbuntuインストーラーにこの機能ありますよね）

```bash
mkdir -p "$HOME/.ssh/"
chmod 700 "$HOME/.ssh/"
curl -f "https://github.com/${github_username}.keys" >"$HOME/.ssh/authorized_keys"

sudo service sshd restart
# 確認
# 別クライアントを起動してssh接続が正常にできるか確かめる
```

このサーバからgithub.comやgitlab.comからgit cloneするときの設定

```bash
cat << EOS
Host github github.com
    HostName github.com
    User git
Host gitlab.com
    User git
    HostName gitlab.com
    TCPKeepAlive yes
    identitiesonly yes
EOS
```

秘密鍵の生成

```bash
ssh-keygen -t rsa -b 4096 -C "$(hostname) auth key" -N '' -f "$HOME/.ssh/id_rsa"
chmod 600 "$HOME/.ssh/id_rsa"
```

### 基本的なアプリをインストール

server minimalなのでアプリが最低限しか入ってない想定。

```bash
sudo apt-get -y update
sudo apt-get -y install jq bat zip git vim-nox tmux
```

### Start upスクリプト設定

サーバが起動したとき、Discordに情報を通知します。

```bash
mkdir -p "$HOME/app/webhook"
cat << "EOF" >"$HOME/app/webhook/notify-startup.sh"
#!/usr/bin/env bash
set -e
set -u

hostname=$(/bin/hostname)
chat_url="https://discord.com/api/webhooks/xxxxxxxx/xxxxxxxx"

# ネットワークの準備ができておらず空振りする場合があったので、
# 十分待ったあと実行するようにします。
# ちゃんとするのだったら、このスクリプトをサービス化して順序を設定するのかな
sleep 20


# ついでにサーバのIP情報も通知する
local_ip_address=$(ip -br -f inet -4 address | grep -v lo | grep -v docker0 | head -n 1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')

# Global IPは、外部サイトから取得
global_ip_address=$(curl -s inet-ip.info)

curl -s -X POST -H "Content-Type: application/json" $chat_url \
    -d @- <<EOS
{
    "username": "$hostname",
    "content": "startup\t$hostname\t$local_ip_address\t$global_ip_address"
}
EOS
EOF
chmod +x "$HOME/app/webhook/notify-startup.sh"

# cronへの設定
cat << EOS | crontab # $HOMEは展開される
@reboot "$HOME/app/webhook/notify-startup.sh"
EOS
crontab -l
```

### ソフトウェア設定

個人的なVimの設定ファイルを設置します。

```bash
cd "$HOME"
mkdir vimrc
curl -sLJ https://github.com/rmatttu/vimrc/releases/latest/download/release.tar.gz |
  tar zxf - -C vimrc
./vimrc/install.sh
rm -r vimrc
```

個人的なShellの設定ファイルを設置します。

```bash
mkdir -p ~/.shell
curl -sLJ https://github.com/rmatttu/shell_config/releases/latest/download/release.tar.gz |
  tar zxf - -C ~/.shell
(
  cd ~/.shell
  ./install.sh
)
```

fishのインストールを行います。

```bash
sudo apt-get -y update
(
  os_version="Debian_11"
  echo "deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/$os_version/ /" | sudo tee -a /etc/apt/sources.list
  curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:3/$os_version/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/shells_fish_release_3.gpg > /dev/null
)
sudo apt-get -y update
sudo apt-get -y install fish peco
curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher
```

個人的なfishの設定ファイルを設置します。

```bash
mkdir -p ~/.config/fish
curl -sLJ https://github.com/rmatttu/fish-alpha/releases/latest/download/release.tar.gz |
  tar zxf - -C ~/.config/fish
touch ~/.config/fish/config_local.fish
```

ghqのインストール。

```bash
mkdir -p "$HOME/go/bin/"
tmpd="$(mktemp -d)"
(
  cd "$tmpd"
  wget -O "ghq.zip" https://github.com/x-motemen/ghq/releases/latest/download/ghq_linux_arm64.zip
  unzip ghq.zip
  find . -type f -name ghq -exec mv {} "$HOME/go/bin/" \;
  rm -r "$tmpd"
)
# うまく行かない？
fish -c "set -U fish_user_paths ~/go/bin $fish_user_paths"
```

Dockerのインストール。

```bash
curl -fsSL https://get.docker.com | sudo sh get-docker.sh
```

pipが入ってないのでインストールしておきます。

```bash
sudo apt-get -y install pip
```

### GPIOを操作できるようにする

```bash
sudo apt-get -y install pigpio pigpiod
sudo systemctl start pigpiod
sudo systemctl enable pigpiod

# 確認
sudo systemctl status pigpiod
```

### aptクリーンアップ

```bash
sudo apt -y autoremove
```

以上、完了です。

## その他

### バージョンの出力

```bash
cat /etc/os-release
```

出力例

```ini
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

### システムモニター

各種データを出力

```bash
# e.g. 35537
printf "%s\ttemp\t%s\n" "$now" "$(cat /sys/class/thermal/thermal_zone0/temp)"

# e.g. temp=34.0'C
value="$(vcgencmd measure_temp | grep -o '[0-9]*\.[0-9]*')"
printf "%s\tgpu_temp\t%s\n" "$now" "$value"

# e.g. volt=0.8800V
value="$(vcgencmd measure_volts | grep -o '[0-9]*\.[0-9]*')"
printf "%s\tgpu_volt\t%s\n" "$now" "$value"

# e.g. throttled=0x0
value="$(vcgencmd get_throttled | grep -o '0x[0-9]*$')"
printf "%s\tgpu_throttled\t%s\n" "$now" "$value"

# e.g. frequency(48)=1700419968
value="$(vcgencmd measure_clock arm | grep -o '[0-9]*$')"
printf "%s\tcpu_clock\t%s\n" "$now" "$value"
```