おうちkubernetesを録画サーバにしてみた

## はじめに

[この記事](https://developers.cyberagent.co.jp/blog/archives/14721/)をみて自分でもおうちkubernetesを作ってみたくなり、実際に作ったのはいいものの使いみちがなく、
１年ほど放置していました。

![IMG_3140.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68003/1e77bf3d-f008-2a33-eabd-d190ee3ada66.jpeg)

私は、自宅で録画サーバを構築しているのですが、録画サーバの一部をラズパイに構築して、せっかくおうち化したkubernetesを有効活用しようと思います。

## 録画サーバの構成

- PT3 × 2
  - チューナーボードです。現在は製造が終了しているため価格が高騰しています。
- mirakurun
  - TVチューナサーバです。RESTAPIが実装されていてTCP経由でTVストリームを取得できます。
  - https://github.com/Chinachu/Mirakurun
- chinachu γ
  - 録画ソフトです。TVチューナから取得したストリームデータを録画するソフトです。

今回は、mirakurunは現行のままで運用し、`chinachu γ`をラズパイkubernetesに構築しTCP経由で録画ができる環境を構築します。
最終的にやりたいことは、録画後のデータをmp4にエンコードして、NASに保存することです。

## 構成

|   |  |
|:--|:--|
| Raspberry Pi 3 Model B  | 3個 | 
| microSDカード  | 3枚 |
| 4段積層式 Raspberry Pi3 ケース  | 1個 |
| Anker PowerPort 6 (60W 6ポート USB急速充電器) | 1個 |
| microUSB ケーブル  | 4本 |
| TP-Link WIFI Nano(TL-WR802N) | 1個 |
| microUSB 給電 スイッチングハブ (LAN-SW05PSBE) | 1個 |
| 外付けHDD 4TB(HD-TPA4U3-B) | 1個 |

※TP-Link WIFI Nanoで無線でのアクセスをしていましたが、2.4GHzのためワイヤレスヘッドホンの電波と干渉してしまったため、
 現在は有線接続しています。

## kubernetesインストール

kubernetesのインストールは他の方が投稿している記事が山ほどありますので、詳細は省きます。

クラスター構成は以下です。

```shell-session
$ kubectl get nodes
NAME      STATUS   ROLES    AGE   VERSION
k8spi01   Ready    master   98d   v1.13.3
k8spi02   Ready    <none>   98d   v1.13.3
k8spi03   Ready    <none>   98d   v1.13.3
```

## chinachuのビルド
[こちらで](https://github.com/madpepper/docker-rpi-tv-sys)ラズパイで動かすためのカスタマイズをされている方がいましたので、使用して構築していきます。

```
git clone https://github.com/madpepper/docker-rpi-tv-sys.git
```

TVチューナサーバのmirakurunは今回ラズパイで運用しないのでchinachu部分のみイメージビルドして、
private registryにpushします。

ちなみにprivate registryはkubernetesに構築済みです。

```shell-session
$ cd docker-rpi-tv-sys/chinachu
$ docker build --rm -t chinachu .

# private registryにpush
$ docker tag chinachu:latest 192.168.11.101:30500/chinachu:latest
$ docker push 192.168.11.101:30500/chinachu:latest
```

## HDDをマウント

録画データを外付けHDDに保存するためのマウント設定をします。
今回はk8spi03にHDDをマウントします。


## persistentVolume
録画データを保存する永続がボリュームを設定します。
比較的構築が容易なNFSを利用します。

```shell
# NFSサーバのインストール
$ sudo apt install nfs-kernel-server

# NFSクライアントのインストール(k8spi03以外のホストにインストール)
$ sudo apt install nfs-common
```

以下k8spi03ホストにて作業

```shell
# nfs用ディレクトリの作成
$ mkdir -p /mnt/share/nfs
$ echo "/mnt/share/nfs	192.168.11.0/24(rw,async,crossmnt,no_root_squash,no_subtree_check)" | sudo tee -a /etc/exports

$ sudo systemctl start nfs-server.service
$ sudo systemctl enable nfs-server.service

## statusで確認
$ sudo systemctl status nfs-server.service
---
nfs-server.service - NFS server and services
   Loaded: loaded (/lib/systemd/system/nfs-server.service; enabled; vendor preset: enabled)
   Active: active (exited) since Mon 2019-12-16 00:32:32 JST; 6 days ago
  Process: 25077 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
  Process: 25075 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
 Main PID: 25077 (code=exited, status=0/SUCCESS)
   Memory: 0B
      CPU: 0
   CGroup: /system.slice/nfs-server.service

Dec 16 00:32:32 k8spi03 systemd[1]: Starting NFS server and services...
Dec 16 00:32:32 k8spi03 systemd[1]: Started NFS server and services. 
```

### persistent volumeの作成

録画データのディレクトリと設定ファイルのディレクトリをボリューム共有するためpvを作成します。

```chinachu_pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: chinachu-rec
  annotations:
    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  capacity:
    storage: 3500Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: k8spi03.local
    path: /mnt/share/nfs/chinachu_rec
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: chinachu-conf
  annotations:
    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  capacity:
    storage: 100Mi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: k8spi03.local
    path: /mnt/share/nfs/chinachu_conf
```

```
$ kubectl apply -f chinachu_pv.yaml
```

pv, pvcが作成されているか確認します。

```shell
$ kubectl get pv,pvc -o wide
NAME                             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
persistentvolume/chinachu-conf   100Mi      RWO            Recycle          Bound    default/chinachu-conf   slow                    91d
persistentvolume/chinachu-rec    3500Gi     RWO            Recycle          Bound    default/chinachu-rec    slow                    91d

NAME                                  STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/chinachu-conf   Bound    chinachu-conf   100Mi      RWO            slow           91d
persistentvolumeclaim/chinachu-rec    Bound    chinachu-rec    3500Gi     RWO            slow           91d
```


## chinachuのマニフェスト作成
作成したマニフェストは以下になります。
web, apiのポート外部からアクセスされるのでNodePortを固定で設定しました。

```manifest.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chinachu
  labels:
    app: chinachu
spec:
  replicas: 1
  selector:
    matchLabels:
      app: chinachu
  template:
    metadata:
      labels:
        app: chinachu
    spec:
      containers:
      - name: chinachu
        image: 192.168.11.101:30500/chinachu:latest
        env:
        - name: TZ
          value: "Asia/Tokyo"
        volumeMounts:
        - mountPath: "/usr/local/chinachu/conf"
          name: conf
        - mountPath: "/usr/local/chinachu/recorded"
          name: recorded
        ports:
        - containerPort: 10772
          name: web-port
        - containerPort: 20772
          name: api-port
      volumes:
      - name: conf
        persistentVolumeClaim:
          claimName: chinachu-conf
      - name: recorded
        persistentVolumeClaim:
          claimName: chinachu-rec
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: chinachu
  name: chinachu-web
spec:
  type: NodePort
  ports:
  - port: 10772
    targetPort: web-port
    nodePort: 31772
  selector:
    app: chinachu
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: chinachu
  name: chinachu-api
spec:
  type: NodePort
  ports:
  - port: 20772
    targetPort: api-port
    nodePort: 31272
  selector:
    app: chinachu
```

```
$ kubectl apply -f manifest.yaml
```

起動できたか確認します。

```
$ kubectl get pods,svc -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP            NODE      NOMINATED NODE   READINESS GATES
pod/chinachu-79554fd54b-4dqqg   1/1     Running   0          18h     10.244.1.18   k8spi02   <none>           <none>
pod/registry                    1/1     Running   0          6d23h   10.244.2.8    k8spi03   <none>           <none>

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE     SELECTOR
service/chinachu-api   NodePort    10.104.102.113   <none>        20772:31272/TCP   18h     app=chinachu
service/chinachu-web   NodePort    10.102.118.9     <none>        10772:31772/TCP   18h     app=chinachu
service/kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP           99d     <none>
service/srv-registry   NodePort    10.111.164.192   <none>        5000:30500/TCP    6d23h   app=registry
```

## ブラウザで確認

webにアクセスできるか確認してみます。
http\://<hostIP>:30772

![191223-0001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68003/85c4b2c6-d404-9147-312f-a495fd033d03.png)

mirakurunから番組表であるEPGデータを取得できていました。
試しに録画してみましたが、問題なく再生できました。

## 最後に
放置していたおうちkubernetesを有効活用できそうです。
ただ、ラズパイはmicroSDがストレージとなっているため、耐久性が不安がありますが試しに使い続けてみようと思います。

今後はラズペリーパイのハードウェアエンコードを使用したmp4への自動エンコード処理を組み込んで行けたらと思います。