Kubernetesクラスタへのtt-rssのデプロイ方法

11 min read | Posted: 2025-03-23 | tags: Kubernetes , K3s , TTRSS , RSS , Self-hosting

Tiny Tiny RSS(tt-rss)は自宅サーバーで運用できるオープンソースのRSSリーダーである。複数デバイスでの同期、カスタマイズ可能なインターフェース、プラグインによる拡張性など、多くの優れた機能を持っている。

私がRSSリーダーを利用する理由は主に二つある:

  1. 情報の選択的取得: 現代の情報過多環境において、自分が選んだ情報源からのみ情報を受け取ることで、頭が疲れない。
  2. 誘惑の排除: 例えば、YouTubeのチャンネルをRSSで購読することで、おすすめの動画を観ずに済む。誘惑と戦う必要がなくなる。

tt-rssを選んだ理由は、サービス終了のリスクがなく、自宅サーバーで運用できるため。自己ホスティング可能なRSSリーダーとしてFreshRSSなども選択肢に挙がるが、tt-rssは拡張性において優れている。

Helmチャート(Kubernetesパッケージ管理ツール)を使ったtt-rssのデプロイ方法は見つかるものの、Kubernetesマニフェストを一から記述する方法についての情報はまだ少ない。そのため、以下の流れでtt-rssのK8sクラスタへのデプロイ方法を解説する:

  1. システム構成と環境の説明
  2. MySQLデータベースのデプロイ
  3. tt-rssアプリケーションのデプロイ
  4. Nginxリバースプロキシのデプロイ

本記事では、Raspberry PiのARM64アーキテクチャに対応したコンテナイメージを使用しているが、イメージを変更するだけで他のアーキテクチャでも適用可能である。

構成ファイルは別々に作成することも、一つにまとめることもできる。一括設定したい場合は、記事末尾のおまけの節を参照。この記事では、必要最低限の構成を紹介するため、自身の環境に合わせて自由にカスタマイズしてください。

事前知識:必要なKubernetesリソース

デプロイに使用する主要なKubernetesリソースは以下の通りである:

  • Namespace: リソースを論理的にグループ化し、tt-rssとNginxを分離管理
  • Deployment: アプリケーションのPod管理とスケーリング
  • Service: Podへのアクセスポイントを提供し、安定した通信を確保
  • PersistentVolumeClaim: データベースデータの永続化
  • Secret: パスワードなどの機密情報を安全に管理

システム構成

今回構築するシステムは以下のコンポーネントで構成される:

tt-rss構成図
図1: tt-rssのKubernetesデプロイ構成図

  1. Nginxリバースプロキシ: 外部からのアクセスを受け付け、tt-rssへ転送
  2. tt-rssアプリケーション: RSSリーダー本体
  3. MySQLデータベース: tt-rssのデータを保存

外部からのアクセスがNginxを経由してtt-rssに到達し、tt-rssはMySQLにデータを保存するという流れ。また、各コンポーネントは別々のPodとして実行され、Service経由で接続される。これにより個別の更新やスケーリングが可能になる。

MySQLのデプロイ

名前空間の作成

最初に、tt-rss関連のリソースを論理的に分離するための名前空間を作成する。これにより、リソースの管理がしやすくなり、他のアプリケーションとの衝突を避けられる。

# ttrss-namespace.yaml
# tt-rss関連のリソースを論理的にグループ化するための名前空間
apiVersion: v1
kind: Namespace
metadata:
  name: ttrss  # この名前空間内にtt-rssとMySQLのリソースを配置

データベース接続用シークレット

データベースのパスワードはSecretとして管理する。これにより、マニフェストファイルに平文でパスワードを記載せずに済み、セキュリティが向上する。

# MySQLのルートパスワードとtt-rssユーザー用パスワードをSecretとして作成
kubectl create secret generic mysql-secret \
--from-literal=mysql_root_pass=rootpassword \
--from-literal=mysql_ttrss_pass=ttrsspassword -n ttrss

MySQLデータベースのPVC

データベースのデータを永続化するために、PersistentVolumeClaimを作成する。これにより、Podが再起動してもデータが失われない。

# mysql-pvc.yaml
# MySQLのデータを永続化するためのPersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  namespace: ttrss  # ttrss名前空間に配置
  name: mysql-pvc  # このPVCはMySQLデプロイメントから参照される
spec:
  accessModes:
    - ReadWriteOnce  # 単一ノードからの読み書きアクセスを許可
  resources:
    requests:
      storage: 1Gi  # 1GBのストレージを要求(必要に応じて調整可能)

MySQLデプロイメント

MySQLデータベースをKubernetesクラスタにデプロイする。このデプロイメントでは、ARM64対応のMySQLイメージを使用し、必要な環境変数とボリュームマウントを設定する。

# mysql-deployment.yaml
# MySQLデータベースをデプロイするためのマニフェスト
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: ttrss  # ttrss名前空間に配置
  name: mysql  # デプロイメント名
spec:
  replicas: 1  # MySQLは単一インスタンスで実行(レプリケーション構成ではない)
  selector:
    matchLabels:
      app: mysql  # このラベルを持つPodを管理対象とする
  template:
    metadata:
      labels:
        app: mysql  # Podにラベルを付与
    spec:
      containers:
        - name: mysql
          image: arm64v8/mysql:8.0  # ARM64アーキテクチャ用のMySQLイメージ
          env:
            # MySQLの設定を環境変数で指定
            - name: MYSQL_ROOT_PASSWORD  # ルートパスワード(Secretから取得)
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_root_pass
            - name: MYSQL_DATABASE
              value: ttrss  # tt-rss用のデータベース名
            - name: MYSQL_USER
              value: ttrss  # tt-rss用のデータベースユーザー
            - name: MYSQL_PASSWORD  # ユーザーパスワード(Secretから取得)
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_ttrss_pass
          ports:
            - containerPort: 3306  # MySQLの標準ポート
          volumeMounts:
            - name: mysql-storage  # データの永続化のためのボリューム
              mountPath: /var/lib/mysql  # MySQLのデータディレクトリ
      volumes:
        - name: mysql-storage
          persistentVolumeClaim:
            claimName: mysql-pvc  # 先に作成したPVCを使用

MySQLサービス

MySQLにアクセスするためのサービスを作成する。このサービスによりtt-rssアプリケーションからデータベースへ安定してアクセスできるようになる。

# mysql-service.yaml
# MySQLにアクセスするためのサービス定義
apiVersion: v1
kind: Service
metadata:
  namespace: ttrss  # ttrss名前空間に配置
  name: mysql  # サービス名(このDNS名でクラスタ内からアクセス可能)
spec:
  selector:
    app: mysql  # mysql Podをターゲットとする
  ports:
    - port: 3306  # 標準のMySQLポート
  type: ClusterIP  # クラスタ内部でのみアクセス可能

tt-rssのデプロイ

tt-rssデプロイメント

tt-rssアプリケーション本体をデプロイする。このデプロイメントでは、tt-rssコンテナと、データベース接続に必要な環境変数を設定する。

# ttrss-deployment.yaml
# tt-rssアプリケーションをデプロイするためのマニフェスト
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: ttrss  # ttrss名前空間に配置
  name: ttrss  # デプロイメント名
spec:
  replicas: 1  # 単一インスタンスでデプロイ
  selector:
    matchLabels:
      app: ttrss  # このラベルを持つPodを管理対象とする
  template:
    metadata:
      labels:
        app: ttrss  # Podにラベルを付与
    spec:
      containers:
        - name: ttrss
          image: nventiveux/ttrss  # tt-rssのコンテナイメージ
          env:
            # データベース接続情報を環境変数で設定
            - name: TTRSS_DB_HOST
              value: mysql  # MySQLサービス名
            - name: TTRSS_DB_PORT
              value: "3306"  # MySQLポート
            - name: TTRSS_DB_NAME
              value: ttrss  # データベース名
            - name: TTRSS_DB_USER
              value: ttrss  # データベースユーザー
            - name: TTRSS_DB_PASS  # データベースパスワード(Secretから取得)
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_ttrss_pass
            - name: TTRSS_DB_TYPE
              value: mysql  # データベースタイプ
            - name: TTRSS_SELF_URL_PATH
              value: "http://IP_Address:30080/"  # tt-rssの外部アクセスURL(自環境に合わせて変更)
          ports:
            - containerPort: 80  # tt-rssのWebサーバーポート

tt-rssサービス

tt-rssにアクセスするためのサービスを作成する。このサービスによりNginxからtt-rssへの通信が可能になる。

# ttrss-service.yaml
# tt-rssアプリケーションにアクセスするためのサービス定義
apiVersion: v1
kind: Service
metadata:
  namespace: ttrss  # ttrss名前空間に配置
  name: ttrss  # サービス名(このDNS名でクラスタ内からアクセス可能)
spec:
  selector:
    app: ttrss  # ttrss Podをターゲットとする
  ports:
    - name: http
      port: 80  # サービスポート
      targetPort: 80  # コンテナポート
      protocol: TCP
  type: ClusterIP  # クラスタ内部でのみアクセス可能

Nginxのデプロイ

名前空間の作成

Nginxのリソースを分離管理するための専用の名前空間を作成する。これにより、tt-rssとは別の管理ができるようになる。

# nginx-namespace.yaml
# Nginxリソースを分離管理するための名前空間
apiVersion: v1
kind: Namespace
metadata:
  name: nginx  # Nginx関連のリソース用の名前空間
  labels:
    app: nginx
    role: reverse-proxy  # リバースプロキシとしての役割を明示

Nginx設定用ConfigMap

Nginxの設定をConfigMapとして管理する。これにより、Nginx設定を柔軟に更新でき、デプロイメントと設定を分離できる。

# nginx-configmap.yaml
# Nginxの設定をConfigMapとして管理
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: nginx  # nginx名前空間に配置
  name: nginx-config  # ConfigMap名
data:
  default.conf: |
    server {
      listen 80;
      server_name localhost;
      
      # tt-rssへのリバースプロキシ設定
      location / {
        # 内部DNSを使用してtt-rssサービスにリクエストを転送
        proxy_pass http://ttrss.ttrss.svc.cluster.local:80/;
        
        # クライアント情報を適切にtt-rssに伝えるためのヘッダー設定
        proxy_set_header Host $host:30080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    }        

Nginxデプロイメント

Nginxをデプロイする。このデプロイメントでは、Nginxコンテナを実行し、先ほど作成したConfigMapを設定ファイルとしてマウントする。

# nginx-deployment.yaml
# Nginxリバースプロキシをデプロイするためのマニフェスト
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: nginx  # nginx名前空間に配置
  name: nginx  # デプロイメント名
  labels:
    app: nginx
spec:
  replicas: 1  # 単一インスタンスでデプロイ(高可用性が必要なら増やすことも可能)
  selector:
    matchLabels:
      app: nginx  # このラベルを持つPodを管理対象とする
  template:
    metadata:
      labels:
        app: nginx  # Podにラベルを付与
    spec:
      volumes:
        - name: nginx-config  # 設定ファイル用のボリューム
          configMap:
            name: nginx-config  # 先に作成したConfigMapを参照
      containers:
        - name: nginx
          image: nginx:latest  # Nginxの公式イメージを使用
          ports:
            - containerPort: 80  # Nginxの標準HTTPポート
          volumeMounts:
            - name: nginx-config  # ConfigMapをマウント
              mountPath: /etc/nginx/conf.d/  # Nginxの設定ディレクトリ

Nginxサービス

Nginxサービスを作成してノードポートで公開する。これにより、クラスタ外部からtt-rssにアクセスできるようになる。

# nginx-service.yaml
# Nginxサービスを外部公開するためのマニフェスト
apiVersion: v1
kind: Service
metadata:
  namespace: nginx  # nginx名前空間に配置
  name: nginx-service  # サービス名
spec:
  selector:
    app: nginx  # nginx Podをターゲットとする
  ports:
  - port: 80  # サービスポート
    targetPort: 80  # コンテナポート
    nodePort: 30080  # ノードの30080ポートで外部公開
  type: NodePort  # ノードポートタイプのサービスとして公開

tt-rssへのアクセス

デプロイが完了したら、以下のURLでtt-rssにアクセスできる:

http://<ノードIP>:30080/

初回アクセス時に、デフォルトのユーザー名「admin」とパスワード「password」でログインできる。

tt-rssのログイン画面
図2: tt-rssのログイン画面

正常に動作しない場合は、kubectl describe podkubectl logs コマンドで詳細を確認してください。

おまけ:一括設定

Nginx manifest

# nginx-all.yaml
# Nginxに関する全てのリソースを一括で定義するマニフェスト

# Nginx用の名前空間
apiVersion: v1
kind: Namespace
metadata:
  name: nginx
  labels:
    app: nginx
    role: reverse-proxy
---
# Nginx設定用ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: nginx 
  name: nginx-config
data:
  default.conf: |
    server {
      listen 80;
      server_name localhost;
      
      # tt-rssへのリバースプロキシ設定
      location / {
        # 内部DNSを使用してtt-rssサービスにリクエストを転送
        proxy_pass http://ttrss.ttrss.svc.cluster.local:80/;
        
        # クライアント情報を適切にtt-rssに伝えるためのヘッダー設定
        proxy_set_header Host $host:30080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    }        
---
# Nginxデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: nginx 
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1  # 高可用性が必要なら増やすことも可能
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-config
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/
---
# Nginxサービス(NodePortで外部公開)
apiVersion: v1
kind: Service
metadata:
  namespace: nginx 
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  type: NodePort

tt-rss manifest

# ttrss-all.yaml
# tt-rssとMySQLに関する全てのリソースを一括で定義するマニフェスト

# tt-rss用の名前空間
apiVersion: v1
kind: Namespace
metadata:
  name: ttrss
---
# MySQLデータ用のPVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  namespace: ttrss
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
# MySQLデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: ttrss
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: arm64v8/mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_root_pass
            - name: MYSQL_DATABASE
              value: ttrss
            - name: MYSQL_USER
              value: ttrss
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_ttrss_pass
          ports:
            - containerPort: 3306
          volumeMounts:
            - name: mysql-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-storage
          persistentVolumeClaim:
            claimName: mysql-pvc
---
# MySQLサービス
apiVersion: v1
kind: Service
metadata:
  namespace: ttrss
  name: mysql
spec:
  selector:
    app: mysql
  ports:
    - port: 3306
  type: ClusterIP
---
# tt-rssデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: ttrss
  name: ttrss
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ttrss
  template:
    metadata:
      labels:
        app: ttrss
    spec:
      containers:
        - name: ttrss
          image: nventiveux/ttrss
          env:
            - name: TTRSS_DB_HOST
              value: mysql
            - name: TTRSS_DB_PORT
              value: "3306"
            - name: TTRSS_DB_NAME
              value: ttrss
            - name: TTRSS_DB_USER
              value: ttrss
            - name: TTRSS_DB_PASS
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_ttrss_pass
            - name: TTRSS_DB_TYPE
              value: mysql
            - name: TTRSS_SELF_URL_PATH
              value: "http://IP_Address:30080/"
          ports:
            - containerPort: 80 
---
# tt-rssサービス
apiVersion: v1
kind: Service
metadata:
  namespace: ttrss
  name: ttrss
spec:
  selector:
    app: ttrss
  ports:
    - name: http
      port: 80
      targetPort: 80 
      protocol: TCP
  type: ClusterIP