在K8s上部署Mastodon

准备工作

因为原来用于跑毛象的服务器(参见旧文《Docker下的Mastodon安装笔记》 )到期停机了,正好手里有一个高配的arm免费机可以用,于是就打算把服务迁移过来——顺便试试部署到K8s上。

需要:

  • 一台或多台服务器集群,内存至少4G,推荐8G,最好16G或更多,有外网IP,有一个域名
  • 安装Kubernetes集群(可以使用k3s、minikube或云服务商的K8s服务,本文使用一个kubeadm安装的单节点k8s,见《在单机上用kubeadm安装K8s》)
  • 一个SMTP服务——第三方或本地,这里用了第三方服务
  • 创建必要的存储目录(根据实际节点名称调整路径,这里使用/var/data)
  • 拉取Redis/Postgresql/ES/Mastodon镜像
crictl pull redis:7-alpine
crictl pull postgres:17-alpinie
crictl pull elasticsearch:7.10.1
crictl pull ghcr.io/mastodon/mastodon:v4.5.2
crictl pull ghcr.io/mastodon/mastodon-streaming:v4.5.2

配置存储

1. 创建命名空间

首先创建mastodon命名空间:

kubectl apply -f ns_mastodon.yml

ns_mastodon.yml

apiVersion: v1
kind: Namespace
metadata:
  name: mastodon

2. 配置Local PV/PVC(用于有状态服务)

我们使用local PV/PVC为PostgreSQL、Redis和Elasticsearch提供持久化存储:

Redis存储配置

# 创建Redis数据目录(在目标节点上)
sudo mkdir -p /var/data/redis/data
sudo chown 999:999 /var/data/redis/data

# 应用PV/PVC配置
kubectl apply -f local_redis.yml

local_redis.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-redis-data
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi  # 总容量,根据实际需要调整
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-redis-data
  local:
    path: /var/data/redis/data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - myserver  # 替换为实际节点名称
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-data-pvc
  namespace: mastodon
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi  # 主数据存储大小
  storageClassName: local-redis-data
  selector:
    matchLabels:
      type: local

PostgreSQL存储配置

# 创建PostgreSQL数据目录(在目标节点上)
sudo mkdir -p /var/data/pgdata
sudo mkdir -p /var/data/pgbak
sudo chown 70:70 /var/data/pgdata /var/data/pgbak
sudo chmod 700 /var/data/pgdata

# 应用PV/PVC配置
kubectl apply -f local_posgresql.yml  # 内容参考redis,不过需要配置data和bak两个PV/PVC

Elasticsearch存储配置

# 创建Elasticsearch数据目录(在目标节点上)
sudo mkdir -p /var/data/elasticsearch
sudo chown 1000:1000 /var/data/elasticsearch

# 应用PV/PVC配置
kubectl apply -f local_elasticsearch.yml  # 内容参考redis

3. 配置NFS PV/PVC(用于Mastodon Public文件夹)

Mastodon需要共享存储来存储用户上传的媒体文件,因为需要同时给两个服务(web和streaming)使用,所以使用NFS PV/PVC:

# 创建NFS共享目录
sudo mkdir -p /var/data/mastodon
sudo chown 991:991 /var/data/mastodon

# 配置NFS服务器(如果尚未配置)
# 安装NFS服务器
sudo apt-get install nfs-kernel-server

# 配置NFS导出
echo "/var/data/mastodon *(rw,sync,no_subtree_check,no_root_squash,all_squash,anonuid=991,anongid=991)" | sudo tee -a /etc/exports
sudo exportfs -a  # 应用配置
sudo systemctl restart nfs-kernel-server
sudo exportfs -v  # 查看应用结果

# 应用NFS配置
kubectl apply -f nfs_mastodon.yml

nsf_mastodon.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-mastodon-data-web
  labels:
    type: nfs
spec:
  capacity:
    storage: 150Gi  # 总容量,根据实际需要调整
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs-mastodon-data-web
  nfs:
    server: 10.244.0.1  # flannel的IP
    path: /var/data/mastodon
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mastodon-data-web-pvc
  namespace: mastodon
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 150Gi  # 主数据存储大小
  storageClassName: nfs-mastodon-data-web
  selector:
    matchLabels:
      type: nfs
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-mastodon-data-sidekiq
  labels:
    type: nfs
spec:
  capacity:
    storage: 150Gi  # 总容量,根据实际需要调整
  volumeMode: Filesystem
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs-mastodon-data-sidekiq
  nfs:
    server: 10.244.0.1  # 连同一个NFS
    path: /var/data/mastodon
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mastodon-data-sidekiq-pvc
  namespace: mastodon
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 150Gi  # 主数据存储大小
  storageClassName: nfs-mastodon-data-sidekiq
  selector:
    matchLabels:
      type: nfs

部署有状态服务(Stateful Headless Services)

1. 部署Redis

创建Redis的StatefulSet配置:

kubectl apply -f svc_redis.yml

svc_redis.yml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: mastodon
spec:
  serviceName: redis-headless  # 需要一个 Headless Service 管理网络标识
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: redis-data
          mountPath: /data
        livenessProbe:
          exec:
            command: ["redis-cli", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          exec:
            command: ["redis-cli", "ping"]
          initialDelaySeconds: 5
          periodSeconds: 10
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: redis-data-pvc  # 直接使用现有 PVC
  volumeClaimTemplates: []
---
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
  namespace: mastodon
spec:
  clusterIP: None  # Headless Service,直接解析到 Pod IP
  selector:
    app: redis
  ports:
  - port: 6379
    name: redis

2. 部署PostgreSQL

PostgreSQL使用StatefulSet和Headless Service:

kubectl apply -f svc_postgresql.yml  # 内容参考svc_redis.yml,不过要mount两个卷

3. 部署Elasticsearch

创建Elasticsearch的StatefulSet配置:

kubectl apply -f svc_elasticsearch.yml  # 内容参考svc_redis.yml

4. 验证部署

kubectl get statefulsets -n mastodon
kubectl get pods -n mastodon -l app=postgresql
kubectl get services -n mastodon | grep postgresql

配置Mastodon

1. 准备环境配置文件

编辑 env.production 文件,配置以下关键参数:

# Redis配置
REDIS_HOST=redis-headless.mastodon.svc.cluster.local
REDIS_PORT=6379

# 数据库配置
DB_HOST=postgresql-headless.mastodon.svc.cluster.local
DB_PORT=5432
DB_NAME=mastodon
DB_USER=mastodon
DB_PASS=你的数据库密码

# Elasticsearch配置
ES_ENABLED=true
ES_HOST=es-headless.mastodon.svc.cluster.local
ES_PORT=9200

# 域名配置
LOCAL_DOMAIN=你的域名

2. 创建Secret配置

将环境配置创建为Secret:

kubectl create secret generic mastodon-env --from-env-file=env.production -n mastodon

3. 部署Mastodon服务

注意:除非是全新安装的实例,否则此操作需要在数据库恢复后进行。

创建Mastodon的Deployment配置:

kubectl apply -f svc_mastodon.yml  # 类似svc_redis.yml,不过里面有三个服务:web,streaming,sidekiq

其中mastodon的web和streaming是以NodePort方式输出服务。

恢复数据库

1. 全库备份恢复方式

使用pg_dumpall备份全库再恢复到k8s里。

# 从源docker环境备份整个库
docker exec postgresql pg_dumpall -U postgres | gzip > pgoldbak.sql.gz
# 把备份传输到k8s环境
# 恢复数据
gunzip -c pgoldbak.sql.gz | kubectl exec postgresql -i -n mastodon --command -- psql -U postgres

2. 基于WAL的增量备份恢复方式

备份恢复方式参考《PostgreSQL的连续备份配置

3. 启动实例

恢复数据库确认无误后即可启动毛象实例。

启动服务

所有服务启动后检查状态:

kubectl get all -n mastodon

如果所有Pod都处于Running状态,说明部署成功。如果有问题,则需要查看日志检查原因。

kubectl logs -f <podname> -nmastodon

配置Nginx反向代理

创建Nginx配置,将流量代理到Mastodon服务:

cat > nginx/mastodon.conf << EOF
upstream backend {
    server <NODE_IP>:3xxxx;  # 替换为节点IP和mastodon-web的NodePort
}

upstream streaming {
    server <NODE_IP>:3yyyy;  # 替换为节点IP和mastodon-streaming的NodePort
}

server {
    listen 80;
    server_name 你的域名;
    return 301 https://\$server_name\$request_uri;
}

server {
    listen 443 ssl http2;
    server_name 你的域名;

    ssl_certificate /etc/ssl/你的域名/fullchain.pem;
    ssl_certificate_key /etc/ssl/你的域名/privkey.pem;

    root /var/data/mastodon/public;
    
    location / {
        try_files \$uri @proxy;
    }

    location @proxy {
        proxy_set_header Host \$host;
        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;
        proxy_set_header Proxy "";
        proxy_pass_header Server;

        proxy_pass http://backend;
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection "upgrade";

        tcp_nodelay on;
    }

    location /api/v1/streaming/ {
        proxy_set_header Host \$host;
        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;
        proxy_set_header Proxy "";

        proxy_pass http://streaming;
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection "upgrade";

        tcp_nodelay on;
    }
}
EOF

日常运维

1. 监控服务状态

# 查看所有资源状态
kubectl get all -n mastodon

# 查看Pod日志
kubectl logs -f deployment/mastodon-web -n mastodon
kubectl logs -f deployment/mastodon-streaming -n mastodon
kubectl logs -f deployment/mastodon-sidekiq -n mastodon

# 查看有状态服务日志
kubectl logs -f statefulset/postgresql -n mastodon
kubectl logs -f statefulset/redis -n mastodon
kubectl logs -f statefulset/elasticsearch -n mastodon

2. 定期清理

由于联邦宇宙中的内容会不断增加,需要定期清理过期数据,具体实现与之前docker方式部署一样,只是把docker命令改成相应的kubectl命令。

可以将这些清理任务添加到crontab中定期执行。

3. 备份与恢复

也可以参考前文实现,或者参考pgsql的wal备份那篇用备份wal的方式实现连续备份。

4. 升级Mastodon版本

升级Mastodon需要按顺序进行:

  1. 备份数据库
  2. 更新镜像版本
  3. 重新部署服务
# 更新镜像版本
sed -i 's|ghcr.io/mastodon/mastodon:v4.5.2|ghcr.io/mastodon/mastodon:新版本|g' svc_mastodon.yml
sed -i 's|ghcr.io/mastodon/mastodon-streaming:v4.5.2|ghcr.io/mastodon/mastodon-streaming:新版本|g' svc_mastodon.yml

# 重新部署
kubectl apply -f svc_mastodon.yml

故障排除

1. Pod无法启动

检查Pod状态和日志:

kubectl describe pod <pod-name> -n mastodon
kubectl logs <pod-name> -n mastodon

常见问题:

  • 存储卷挂载失败:检查PV/PVC状态 kubectl get pv,pvc -n mastodon
  • 资源配置不足:检查资源限制 kubectl describe pod <pod-name> -n mastodon | grep -A 5 Resources
  • 镜像拉取失败:检查镜像名称和标签

2. 服务无法访问

检查服务状态:

kubectl get services -n mastodon
kubectl describe service <service-name> -n mastodon

确保NodePort在正确范围内(30000-32767)。

3. 数据库连接问题

检查数据库服务:

# 测试数据库连接
kubectl run test-db-connection --rm -i --restart=Never \
  --image=postgres:17-alpine --namespace=mastodon -- \
  psql -h postgresql-headless.mastodon.svc.cluster.local -U postgres -c "\l"

4. 存储问题

检查存储状态:

kubectl get pv,pvc -n mastodon
kubectl describe pvc <pvc-name> -n mastodon

确保本地目录存在且有正确权限。

总结

本指南介绍了如何使用Kubernetes部署Mastodon实例,包括:

  1. 存储配置:使用local PV/PVC为有状态服务(PostgreSQL、Redis、Elasticsearch)提供持久化存储
  2. 网络配置:使用NFS PV/PVC为Mastodon public文件夹提供共享存储
  3. 服务部署:使用StatefulSet和Headless Service部署有状态服务,使用Deployment部署无状态服务
  4. 运维管理:包括监控、清理、备份、升级等日常运维操作

相比docker-compose部署方式,Kubernetes部署提供了更好的可扩展性、高可用性和运维便利性。可以根据实际需求调整资源配置和副本数量。

注意事项

  1. 安全性:生产环境需要配置更严格的安全策略,如Network Policies、Pod Security Policies等
  2. 性能:根据实际负载调整资源限制和副本数量
  3. 监控:建议配置Prometheus和Grafana进行监控
  4. 备份:定期备份数据库和重要数据
  5. 更新:关注Mastodon和安全更新,及时升级

如有问题,请参考Mastodon官方文档或Kubernetes官方文档。

推送到[go4pro.org]