mirror of
https://github.com/wahyd4/you-music.git
synced 2026-08-08 20:59:47 +10:00
Add docker file and k8s manifest
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha,prefix={{branch}}-
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Summary
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
echo "### Docker Image Built and Pushed :rocket:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Registry:** ${{ env.REGISTRY }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Image:** ${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tags:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
# YouMusic Deployment Guide
|
||||
|
||||
Complete guide for deploying YouMusic to production using Docker and Kubernetes.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Docker Deployment](#docker-deployment)
|
||||
2. [Kubernetes Deployment](#kubernetes-deployment)
|
||||
3. [Data Persistence](#data-persistence)
|
||||
4. [Environment Configuration](#environment-configuration)
|
||||
5. [Backup and Restore](#backup-and-restore)
|
||||
|
||||
---
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Using Docker Compose (Recommended for Single Server)
|
||||
|
||||
The simplest way to deploy YouMusic is using Docker Compose:
|
||||
|
||||
```bash
|
||||
# Start the service
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop the service
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
Your music player will be available at `http://localhost:8000`
|
||||
|
||||
### Using Docker Run
|
||||
|
||||
```bash
|
||||
# Create data directory
|
||||
mkdir -p ./data
|
||||
|
||||
# Run container
|
||||
docker run -d \
|
||||
--name youmusic \
|
||||
-p 8000:8000 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
-e DATABASE_URL=sqlite+aiosqlite:///./data/youmusic.db \
|
||||
ghcr.io/wahyd4/youmusic:latest
|
||||
|
||||
# View logs
|
||||
docker logs -f youmusic
|
||||
```
|
||||
|
||||
### Building Custom Image
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/wahyd4/you-music.git
|
||||
cd you-music
|
||||
|
||||
# Build image
|
||||
docker build -t youmusic:custom .
|
||||
|
||||
# Run with custom image
|
||||
docker run -d \
|
||||
--name youmusic \
|
||||
-p 8000:8000 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
youmusic:custom
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- k3s/k8s cluster
|
||||
- kubectl configured
|
||||
- cert-manager (for TLS)
|
||||
- nginx-ingress controller
|
||||
|
||||
### Step-by-Step Deployment
|
||||
|
||||
#### 1. Build and Push Image
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t youmusic:latest .
|
||||
|
||||
# Tag for registry
|
||||
docker tag youmusic:latest ghcr.io/wahyd4/youmusic:latest
|
||||
|
||||
# Login to GitHub Container Registry
|
||||
echo $GITHUB_TOKEN | docker login ghcr.io -u wahyd4 --password-stdin
|
||||
|
||||
# Push to registry
|
||||
docker push ghcr.io/wahyd4/youmusic:latest
|
||||
```
|
||||
|
||||
Or use GitHub Actions (automatic on push to main):
|
||||
```bash
|
||||
git push origin main
|
||||
# Image will be built and pushed automatically
|
||||
```
|
||||
|
||||
#### 2. Configure Manifest
|
||||
|
||||
Edit `k8s/manifest.yaml`:
|
||||
|
||||
```yaml
|
||||
# Update image name (line 47)
|
||||
image: "ghcr.io/wahyd4/youmusic:latest"
|
||||
|
||||
# Update domain (line 125-126)
|
||||
- host: music.yourdomain.com
|
||||
secretName: youmusic-tls
|
||||
```
|
||||
|
||||
#### 3. Deploy to Cluster
|
||||
|
||||
```bash
|
||||
# Apply manifest
|
||||
kubectl apply -f k8s/manifest.yaml
|
||||
|
||||
# Check status
|
||||
kubectl get pods -l app=youmusic
|
||||
kubectl get svc youmusic
|
||||
kubectl get ingress youmusic-ingress
|
||||
```
|
||||
|
||||
#### 4. Verify Deployment
|
||||
|
||||
```bash
|
||||
# Watch pod startup
|
||||
kubectl get pods -l app=youmusic -w
|
||||
|
||||
# Check logs
|
||||
kubectl logs -f deployment/youmusic
|
||||
|
||||
# Check ingress
|
||||
kubectl describe ingress youmusic-ingress
|
||||
```
|
||||
|
||||
#### 5. Access Application
|
||||
|
||||
```bash
|
||||
# Get ingress IP/hostname
|
||||
kubectl get ingress youmusic-ingress
|
||||
|
||||
# Wait for TLS certificate
|
||||
kubectl get certificate youmusic-tls
|
||||
|
||||
# Access via domain
|
||||
https://music.yourdomain.com
|
||||
```
|
||||
|
||||
### Common Deployment Scenarios
|
||||
|
||||
#### Scenario 1: Private Registry
|
||||
|
||||
```bash
|
||||
# Create secret
|
||||
kubectl create secret docker-registry github-image-pull-secret \
|
||||
--docker-server=ghcr.io \
|
||||
--docker-username=wahyd4 \
|
||||
--docker-password=$GITHUB_TOKEN
|
||||
|
||||
# Uncomment in manifest
|
||||
# imagePullSecrets:
|
||||
# - name: github-image-pull-secret
|
||||
```
|
||||
|
||||
#### Scenario 2: Custom Storage
|
||||
|
||||
Update PVC in manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: youmusic-data-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: your-storage-class # Update this
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi # Adjust size
|
||||
```
|
||||
|
||||
#### Scenario 3: Enable Authentication
|
||||
|
||||
Add to ingress annotations:
|
||||
|
||||
```yaml
|
||||
nginx.ingress.kubernetes.io/auth-url: "https://auth.yourdomain.com/verify"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "https://auth.yourdomain.com/login"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Persistence
|
||||
|
||||
### Directory Structure
|
||||
|
||||
All persistent data is stored in `/app/data`:
|
||||
|
||||
```
|
||||
/app/data/
|
||||
├── youmusic.db # SQLite database
|
||||
├── youmusic.db-shm # SQLite shared memory
|
||||
├── youmusic.db-wal # SQLite write-ahead log
|
||||
├── music/ # Music files
|
||||
│ ├── song1.mp3
|
||||
│ ├── song2.mp3
|
||||
│ └── thumbnails/ # Song thumbnails
|
||||
│ ├── song1.jpg
|
||||
│ └── song2.jpg
|
||||
├── uploads/ # User uploads
|
||||
├── temp/ # Temporary downloads
|
||||
└── cache/ # Cache directory
|
||||
├── artists/ # Artist metadata
|
||||
│ ├── Westlife.json
|
||||
│ └── Taylor_Swift.json
|
||||
└── artist_images/ # Artist avatars
|
||||
├── Westlife.jpg
|
||||
└── Taylor_Swift.jpg
|
||||
```
|
||||
|
||||
### Volume Mounting
|
||||
|
||||
**Docker:**
|
||||
```bash
|
||||
-v /path/on/host:/app/data
|
||||
```
|
||||
|
||||
**Kubernetes:**
|
||||
```yaml
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /app/data
|
||||
```
|
||||
|
||||
### Storage Requirements
|
||||
|
||||
| Component | Size per Item | Example (100 items) |
|
||||
|-----------|---------------|---------------------|
|
||||
| Database | ~1KB per song | 100KB |
|
||||
| Music Files | 3-10MB per song | 500MB |
|
||||
| Thumbnails | 50-200KB per song | 10MB |
|
||||
| Artist Info | 2KB per artist | 200KB |
|
||||
| Artist Images | 100-200KB per artist | 15MB |
|
||||
| **Total** | - | **~525MB** |
|
||||
|
||||
**Recommended PVC Size:**
|
||||
- Small (100 songs): 10Gi
|
||||
- Medium (500 songs): 25Gi
|
||||
- Large (1000+ songs): 50Gi+
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `DATABASE_URL` | SQLite connection string | `sqlite+aiosqlite:///./data/youmusic.db` |
|
||||
| `MUSIC_DIR` | Music files directory | `/app/data/music` |
|
||||
| `UPLOAD_DIR` | Upload directory | `/app/data/uploads` |
|
||||
| `TEMP_DIR` | Temporary files | `/app/data/temp` |
|
||||
| `BASE_DIR` | Application base path | `/app` |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `FFMPEG_LOCATION` | FFmpeg binary path | `ffmpeg` |
|
||||
| `PROXY` | HTTP proxy for downloads | - |
|
||||
|
||||
### Setting Variables
|
||||
|
||||
**Docker:**
|
||||
```bash
|
||||
docker run -e DATABASE_URL="..." -e MUSIC_DIR="..." youmusic
|
||||
```
|
||||
|
||||
**Docker Compose:**
|
||||
```yaml
|
||||
environment:
|
||||
- DATABASE_URL=sqlite+aiosqlite:///./data/youmusic.db
|
||||
- MUSIC_DIR=/app/data/music
|
||||
```
|
||||
|
||||
**Kubernetes:**
|
||||
```yaml
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
value: "sqlite+aiosqlite:///./data/youmusic.db"
|
||||
- name: MUSIC_DIR
|
||||
value: "/app/data/music"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
### Docker Backup
|
||||
|
||||
```bash
|
||||
# Stop container
|
||||
docker stop youmusic
|
||||
|
||||
# Backup data directory
|
||||
tar czf youmusic-backup-$(date +%Y%m%d).tar.gz ./data
|
||||
|
||||
# Restart container
|
||||
docker start youmusic
|
||||
```
|
||||
|
||||
### Docker Restore
|
||||
|
||||
```bash
|
||||
# Stop container
|
||||
docker stop youmusic
|
||||
|
||||
# Restore data
|
||||
tar xzf youmusic-backup-20241030.tar.gz
|
||||
|
||||
# Start container
|
||||
docker start youmusic
|
||||
```
|
||||
|
||||
### Kubernetes Backup
|
||||
|
||||
```bash
|
||||
# Get pod name
|
||||
POD=$(kubectl get pod -l app=youmusic -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
# Backup entire data directory
|
||||
kubectl exec $POD -- tar czf - /app/data > youmusic-backup-$(date +%Y%m%d).tar.gz
|
||||
|
||||
# Or copy files directly
|
||||
kubectl cp $POD:/app/data ./youmusic-backup
|
||||
```
|
||||
|
||||
### Kubernetes Restore
|
||||
|
||||
```bash
|
||||
# Get pod name
|
||||
POD=$(kubectl get pod -l app=youmusic -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
# Restore from tar
|
||||
kubectl exec -i $POD -- tar xzf - -C /app < youmusic-backup-20241030.tar.gz
|
||||
|
||||
# Or copy files
|
||||
kubectl cp ./youmusic-backup $POD:/app/data-restore
|
||||
|
||||
# Move files (exec into pod)
|
||||
kubectl exec -it $POD -- bash
|
||||
mv /app/data-restore/* /app/data/
|
||||
```
|
||||
|
||||
### Database-Only Backup
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker exec youmusic sqlite3 /app/data/youmusic.db ".backup /app/data/backup.db"
|
||||
docker cp youmusic:/app/data/backup.db ./youmusic-db-backup.db
|
||||
|
||||
# Kubernetes
|
||||
kubectl exec deployment/youmusic -- sqlite3 /app/data/youmusic.db ".backup /app/data/backup.db"
|
||||
kubectl cp deployment/youmusic:/app/data/backup.db ./youmusic-db-backup.db
|
||||
```
|
||||
|
||||
### Automated Backups
|
||||
|
||||
Create a CronJob for automated backups:
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: youmusic-backup
|
||||
spec:
|
||||
schedule: "0 2 * * *" # Daily at 2 AM
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine:latest
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache sqlite
|
||||
cd /data
|
||||
sqlite3 youmusic.db ".backup backup-$(date +%Y%m%d).db"
|
||||
find /data -name "backup-*.db" -mtime +7 -delete
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: youmusic-data-pvc
|
||||
restartPolicy: OnFailure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring and Maintenance
|
||||
|
||||
### Health Checks
|
||||
|
||||
**Docker:**
|
||||
```bash
|
||||
# Check container health
|
||||
docker inspect youmusic | grep Health -A 10
|
||||
|
||||
# Manual health check
|
||||
curl http://localhost:8000/
|
||||
```
|
||||
|
||||
**Kubernetes:**
|
||||
```bash
|
||||
# Check pod health
|
||||
kubectl describe pod -l app=youmusic | grep -A 5 Conditions
|
||||
|
||||
# Check endpoints
|
||||
kubectl get endpoints youmusic
|
||||
```
|
||||
|
||||
### Log Management
|
||||
|
||||
**Docker:**
|
||||
```bash
|
||||
# View logs
|
||||
docker logs youmusic
|
||||
|
||||
# Follow logs
|
||||
docker logs -f youmusic
|
||||
|
||||
# Last 100 lines
|
||||
docker logs --tail=100 youmusic
|
||||
```
|
||||
|
||||
**Kubernetes:**
|
||||
```bash
|
||||
# View logs
|
||||
kubectl logs deployment/youmusic
|
||||
|
||||
# Follow logs
|
||||
kubectl logs -f deployment/youmusic
|
||||
|
||||
# Last 100 lines
|
||||
kubectl logs --tail=100 deployment/youmusic
|
||||
```
|
||||
|
||||
### Resource Monitoring
|
||||
|
||||
**Kubernetes:**
|
||||
```bash
|
||||
# Resource usage
|
||||
kubectl top pod -l app=youmusic
|
||||
|
||||
# Detailed metrics
|
||||
kubectl describe pod -l app=youmusic
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
**Docker:**
|
||||
```bash
|
||||
# Pull latest image
|
||||
docker pull ghcr.io/wahyd4/youmusic:latest
|
||||
|
||||
# Stop and remove old container
|
||||
docker stop youmusic
|
||||
docker rm youmusic
|
||||
|
||||
# Start new container
|
||||
docker run -d --name youmusic ...
|
||||
```
|
||||
|
||||
**Kubernetes:**
|
||||
```bash
|
||||
# Update image
|
||||
kubectl set image deployment/youmusic \
|
||||
youmusic=ghcr.io/wahyd4/youmusic:v2
|
||||
|
||||
# Or reapply manifest
|
||||
kubectl apply -f k8s/manifest.yaml
|
||||
|
||||
# Check rollout
|
||||
kubectl rollout status deployment/youmusic
|
||||
|
||||
# Rollback if needed
|
||||
kubectl rollout undo deployment/youmusic
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Database Locked
|
||||
|
||||
**Cause:** Multiple instances trying to write to SQLite
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Kubernetes: Ensure single replica
|
||||
kubectl scale deployment/youmusic --replicas=1
|
||||
|
||||
# Check for hung processes
|
||||
kubectl exec deployment/youmusic -- ps aux | grep python
|
||||
```
|
||||
|
||||
#### 2. Permission Denied
|
||||
|
||||
**Cause:** Volume mount permission issues
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check permissions
|
||||
kubectl exec deployment/youmusic -- ls -la /app/data
|
||||
|
||||
# Fix permissions
|
||||
kubectl exec deployment/youmusic -- chmod -R 755 /app/data
|
||||
```
|
||||
|
||||
#### 3. FFmpeg Not Found
|
||||
|
||||
**Cause:** FFmpeg not installed in container
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
kubectl exec deployment/youmusic -- which ffmpeg
|
||||
```
|
||||
|
||||
**Solution:** Image already includes ffmpeg, ensure using correct image.
|
||||
|
||||
#### 4. Storage Full
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
kubectl exec deployment/youmusic -- df -h /app/data
|
||||
```
|
||||
|
||||
**Solution:** Increase PVC size or clean up old files.
|
||||
|
||||
---
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Use Authentication**
|
||||
- Enable nginx auth
|
||||
- Use OAuth2 proxy
|
||||
- Restrict network access
|
||||
|
||||
2. **Regular Updates**
|
||||
- Keep image updated
|
||||
- Monitor security advisories
|
||||
- Update dependencies
|
||||
|
||||
3. **Backup Regularly**
|
||||
- Automated daily backups
|
||||
- Test restore procedures
|
||||
- Store backups securely
|
||||
|
||||
4. **Resource Limits**
|
||||
- Set CPU/memory limits
|
||||
- Monitor resource usage
|
||||
- Scale appropriately
|
||||
|
||||
5. **Network Policies**
|
||||
- Restrict ingress/egress
|
||||
- Use TLS everywhere
|
||||
- Limit exposed services
|
||||
|
||||
---
|
||||
|
||||
## Production Checklist
|
||||
|
||||
- [ ] Domain configured
|
||||
- [ ] TLS certificate issued
|
||||
- [ ] Storage provisioned
|
||||
- [ ] Backups configured
|
||||
- [ ] Authentication enabled
|
||||
- [ ] Resource limits set
|
||||
- [ ] Monitoring enabled
|
||||
- [ ] Logs aggregated
|
||||
- [ ] Health checks working
|
||||
- [ ] Update strategy defined
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2024-10-30
|
||||
**Version:** 1.0.0
|
||||
+14
-3
@@ -13,7 +13,7 @@ RUN npm run build
|
||||
|
||||
|
||||
# Final image
|
||||
FROM python:3.11-slim
|
||||
FROM python:3.13-slim
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -33,17 +33,28 @@ COPY backend/ ./backend/
|
||||
# Copy frontend build from builder stage
|
||||
COPY --from=frontend-builder /app/frontend/dist ./backend/static
|
||||
|
||||
# Create data directories
|
||||
RUN mkdir -p /app/data/music /app/data/uploads /app/data/temp
|
||||
# Create data directories with proper permissions
|
||||
RUN mkdir -p /app/data/music \
|
||||
/app/data/uploads \
|
||||
/app/data/temp \
|
||||
/app/data/cache/artists \
|
||||
/app/data/cache/artist_images \
|
||||
&& chmod -R 755 /app/data
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Environment variables
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV DATABASE_URL=sqlite+aiosqlite:///./data/youmusic.db
|
||||
ENV MUSIC_DIR=/app/data/music
|
||||
ENV UPLOAD_DIR=/app/data/uploads
|
||||
ENV TEMP_DIR=/app/data/temp
|
||||
ENV BASE_DIR=/app
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8000/ || exit 1
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
|
||||
+475
@@ -0,0 +1,475 @@
|
||||
# Kubernetes Deployment Guide for YouMusic
|
||||
|
||||
This guide explains how to deploy YouMusic to your k3s cluster.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- k3s cluster running
|
||||
- kubectl configured to access your cluster
|
||||
- cert-manager installed (for TLS certificates)
|
||||
- nginx ingress controller installed
|
||||
- Optional: Private container registry credentials
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Build and Push Docker Image
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t youmusic:latest .
|
||||
|
||||
# Tag for your registry (example with GitHub Container Registry)
|
||||
docker tag youmusic:latest ghcr.io/wahyd4/youmusic:latest
|
||||
|
||||
# Push to registry
|
||||
docker push ghcr.io/wahyd4/youmusic:latest
|
||||
```
|
||||
|
||||
### 2. Update Manifest
|
||||
|
||||
Edit `k8s/manifest.yaml` and update:
|
||||
|
||||
```yaml
|
||||
# Line 47: Update image name
|
||||
image: "ghcr.io/wahyd4/youmusic:latest"
|
||||
|
||||
# Line 125-126: Update domain name
|
||||
- host: music.junv.cc # Your domain
|
||||
secretName: youmusic-tls
|
||||
```
|
||||
|
||||
### 3. Create Secret for Private Registry (Optional)
|
||||
|
||||
If using private registry:
|
||||
|
||||
```bash
|
||||
kubectl create secret docker-registry github-image-pull-secret \
|
||||
--docker-server=ghcr.io \
|
||||
--docker-username=your-github-username \
|
||||
--docker-password=your-github-token \
|
||||
--docker-email=your-email@example.com
|
||||
```
|
||||
|
||||
Then uncomment in manifest:
|
||||
```yaml
|
||||
imagePullSecrets:
|
||||
- name: github-image-pull-secret
|
||||
```
|
||||
|
||||
### 4. Deploy to Kubernetes
|
||||
|
||||
```bash
|
||||
# Apply the manifest
|
||||
kubectl apply -f k8s/manifest.yaml
|
||||
|
||||
# Check deployment status
|
||||
kubectl get pods -l app=youmusic
|
||||
kubectl get svc youmusic
|
||||
kubectl get ingress youmusic-ingress
|
||||
```
|
||||
|
||||
### 5. Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check pod logs
|
||||
kubectl logs -f deployment/youmusic
|
||||
|
||||
# Check if pod is running
|
||||
kubectl get pods
|
||||
|
||||
# Describe pod for details
|
||||
kubectl describe pod -l app=youmusic
|
||||
```
|
||||
|
||||
## Manifest Structure
|
||||
|
||||
### PersistentVolumeClaim (PVC)
|
||||
|
||||
```yaml
|
||||
- Name: youmusic-data-pvc
|
||||
- Size: 50Gi
|
||||
- Access: ReadWriteOnce (single node)
|
||||
- Storage Class: local-path (default in k3s)
|
||||
```
|
||||
|
||||
**Stores:**
|
||||
- SQLite database (`/app/data/youmusic.db`)
|
||||
- Music files (`/app/data/music/`)
|
||||
- Uploaded files (`/app/data/uploads/`)
|
||||
- Thumbnails (`/app/data/music/thumbnails/`)
|
||||
- Artist cache (`/app/data/cache/artists/`)
|
||||
- Artist images (`/app/data/cache/artist_images/`)
|
||||
|
||||
### Deployment
|
||||
|
||||
```yaml
|
||||
- Replicas: 1 (SQLite limitation)
|
||||
- Strategy: Recreate (prevent multiple pods)
|
||||
- Image: Your registry image
|
||||
- Resources:
|
||||
- Requests: 200m CPU, 512Mi RAM
|
||||
- Limits: 2000m CPU, 2048Mi RAM
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `DATABASE_URL`: SQLite connection string
|
||||
- `MUSIC_DIR`: Music files directory
|
||||
- `UPLOAD_DIR`: Upload directory
|
||||
- `TEMP_DIR`: Temporary files
|
||||
- `BASE_DIR`: Application base directory
|
||||
|
||||
**Health Checks:**
|
||||
- Liveness: HTTP GET / every 10s
|
||||
- Readiness: HTTP GET / every 5s
|
||||
|
||||
### Service
|
||||
|
||||
```yaml
|
||||
- Type: ClusterIP
|
||||
- Port: 80 → 8000 (container)
|
||||
```
|
||||
|
||||
### Ingress
|
||||
|
||||
```yaml
|
||||
- Class: nginx
|
||||
- TLS: Enabled (Let's Encrypt)
|
||||
- Max Upload: 500MB
|
||||
- Timeouts: 600s
|
||||
```
|
||||
|
||||
**Annotations:**
|
||||
- `proxy-body-size: 500m` - Large file uploads
|
||||
- `proxy-read-timeout: 600` - Long downloads
|
||||
- `proxy-send-timeout: 600` - Long uploads
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Storage Class
|
||||
|
||||
Update if using different storage:
|
||||
|
||||
```yaml
|
||||
storageClassName: nfs-client # Or your storage class
|
||||
```
|
||||
|
||||
### Domain Configuration
|
||||
|
||||
Update your domain in:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- your-domain.com
|
||||
secretName: youmusic-tls
|
||||
rules:
|
||||
- host: your-domain.com
|
||||
```
|
||||
|
||||
### Enable Authentication (Optional)
|
||||
|
||||
Uncomment in ingress annotations:
|
||||
|
||||
```yaml
|
||||
nginx.ingress.kubernetes.io/auth-url: "https://pass.junv.cc/auth"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "https://pass.junv.cc/?redirect=https%3A%2F%2F$host$request_uri"
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Adjust based on your needs:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m # Increase for better performance
|
||||
memory: 512Mi # Increase if many downloads
|
||||
limits:
|
||||
cpu: 2000m # Max CPU
|
||||
memory: 2048Mi # Max memory
|
||||
```
|
||||
|
||||
## Persistent Data
|
||||
|
||||
All data is stored in the PVC:
|
||||
|
||||
```
|
||||
/app/data/
|
||||
├── youmusic.db # SQLite database
|
||||
├── music/ # Downloaded music files
|
||||
│ ├── song1.mp3
|
||||
│ ├── song2.mp3
|
||||
│ └── thumbnails/ # Song thumbnails
|
||||
│ ├── song1.jpg
|
||||
│ └── song2.jpg
|
||||
├── uploads/ # User uploaded files
|
||||
├── temp/ # Temporary download files
|
||||
└── cache/ # Cache directory
|
||||
├── artists/ # Artist metadata JSON
|
||||
│ └── Westlife.json
|
||||
└── artist_images/ # Artist avatars
|
||||
└── Westlife.jpg
|
||||
```
|
||||
|
||||
### Backup Data
|
||||
|
||||
```bash
|
||||
# Get pod name
|
||||
POD=$(kubectl get pod -l app=youmusic -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
# Copy data from pod
|
||||
kubectl cp $POD:/app/data ./youmusic-backup
|
||||
|
||||
# Or use exec to tar
|
||||
kubectl exec $POD -- tar czf - /app/data > youmusic-backup.tar.gz
|
||||
```
|
||||
|
||||
### Restore Data
|
||||
|
||||
```bash
|
||||
# Copy backup to pod
|
||||
kubectl cp ./youmusic-backup $POD:/app/data-restore
|
||||
|
||||
# Or restore from tar
|
||||
kubectl exec -i $POD -- tar xzf - -C /app < youmusic-backup.tar.gz
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Follow logs
|
||||
kubectl logs -f deployment/youmusic
|
||||
|
||||
# Last 100 lines
|
||||
kubectl logs --tail=100 deployment/youmusic
|
||||
|
||||
# All containers (if multiple)
|
||||
kubectl logs -f -l app=youmusic --all-containers
|
||||
```
|
||||
|
||||
### Pod Status
|
||||
|
||||
```bash
|
||||
# Get pod details
|
||||
kubectl describe pod -l app=youmusic
|
||||
|
||||
# Get events
|
||||
kubectl get events --sort-by=.metadata.creationTimestamp
|
||||
```
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
# CPU and memory usage
|
||||
kubectl top pod -l app=youmusic
|
||||
|
||||
# Node resources
|
||||
kubectl top nodes
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pod Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod status
|
||||
kubectl get pods -l app=youmusic
|
||||
|
||||
# Check events
|
||||
kubectl describe pod -l app=youmusic
|
||||
|
||||
# Check logs
|
||||
kubectl logs deployment/youmusic
|
||||
```
|
||||
|
||||
**Common Issues:**
|
||||
- Image pull errors → Check imagePullSecrets
|
||||
- Permission denied → Check volume permissions
|
||||
- Database locked → Ensure single replica only
|
||||
|
||||
### Database Issues
|
||||
|
||||
```bash
|
||||
# Exec into pod
|
||||
kubectl exec -it deployment/youmusic -- bash
|
||||
|
||||
# Check database
|
||||
cd /app/data
|
||||
ls -la youmusic.db
|
||||
|
||||
# Check SQLite
|
||||
sqlite3 youmusic.db "SELECT COUNT(*) FROM music;"
|
||||
```
|
||||
|
||||
### Storage Full
|
||||
|
||||
```bash
|
||||
# Check disk usage
|
||||
kubectl exec deployment/youmusic -- df -h /app/data
|
||||
|
||||
# Check directory sizes
|
||||
kubectl exec deployment/youmusic -- du -sh /app/data/*
|
||||
```
|
||||
|
||||
**Solution:** Increase PVC size:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi # Increase from 50Gi
|
||||
```
|
||||
|
||||
Then:
|
||||
```bash
|
||||
kubectl patch pvc youmusic-data-pvc -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
|
||||
```
|
||||
|
||||
### Ingress Not Working
|
||||
|
||||
```bash
|
||||
# Check ingress
|
||||
kubectl get ingress youmusic-ingress
|
||||
kubectl describe ingress youmusic-ingress
|
||||
|
||||
# Check cert-manager
|
||||
kubectl get certificate
|
||||
kubectl describe certificate youmusic-tls
|
||||
```
|
||||
|
||||
## Scaling Limitations
|
||||
|
||||
⚠️ **Important:** YouMusic uses SQLite, which does NOT support multiple concurrent writers.
|
||||
|
||||
**Limitations:**
|
||||
- ✅ Can handle multiple readers
|
||||
- ❌ Cannot scale replicas > 1
|
||||
- ❌ ReadWriteOnce PVC (single node only)
|
||||
|
||||
**For Production at Scale:**
|
||||
- Consider migrating to PostgreSQL/MySQL
|
||||
- Update DATABASE_URL environment variable
|
||||
- Enable multi-replica deployment
|
||||
|
||||
## Updates
|
||||
|
||||
### Update Image
|
||||
|
||||
```bash
|
||||
# Build new version
|
||||
docker build -t ghcr.io/wahyd4/youmusic:v2 .
|
||||
docker push ghcr.io/wahyd4/youmusic:v2
|
||||
|
||||
# Update deployment
|
||||
kubectl set image deployment/youmusic youmusic=ghcr.io/wahyd4/youmusic:v2
|
||||
|
||||
# Or edit manifest and reapply
|
||||
kubectl apply -f k8s/manifest.yaml
|
||||
|
||||
# Check rollout status
|
||||
kubectl rollout status deployment/youmusic
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# View rollout history
|
||||
kubectl rollout history deployment/youmusic
|
||||
|
||||
# Rollback to previous version
|
||||
kubectl rollout undo deployment/youmusic
|
||||
|
||||
# Rollback to specific revision
|
||||
kubectl rollout undo deployment/youmusic --to-revision=2
|
||||
```
|
||||
|
||||
## Clean Up
|
||||
|
||||
```bash
|
||||
# Delete all resources
|
||||
kubectl delete -f k8s/manifest.yaml
|
||||
|
||||
# Delete PVC (will delete all data!)
|
||||
kubectl delete pvc youmusic-data-pvc
|
||||
```
|
||||
|
||||
⚠️ **Warning:** Deleting the PVC will permanently delete all music, database, and cache!
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Using NFS Storage
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: youmusic-nfs-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
nfs:
|
||||
server: 192.168.1.5
|
||||
path: "/nfs/youmusic"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: youmusic-data-pvc
|
||||
spec:
|
||||
storageClassName: ""
|
||||
volumeName: youmusic-nfs-pv
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
```
|
||||
|
||||
### Multiple Environments
|
||||
|
||||
```bash
|
||||
# Create namespace
|
||||
kubectl create namespace youmusic-prod
|
||||
kubectl create namespace youmusic-dev
|
||||
|
||||
# Deploy to specific namespace
|
||||
kubectl apply -f k8s/manifest.yaml -n youmusic-prod
|
||||
kubectl apply -f k8s/manifest-dev.yaml -n youmusic-dev
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Enable Authentication** (Recommended)
|
||||
- Use nginx ingress auth
|
||||
- Integrate with OAuth2 proxy
|
||||
- Restrict access to trusted users
|
||||
|
||||
2. **Network Policies**
|
||||
- Restrict pod-to-pod communication
|
||||
- Limit egress for downloads
|
||||
|
||||
3. **Resource Limits**
|
||||
- Set CPU/memory limits
|
||||
- Prevent resource exhaustion
|
||||
|
||||
4. **Secrets Management**
|
||||
- Use Kubernetes secrets for sensitive data
|
||||
- Consider using sealed-secrets or external-secrets
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Check logs: `kubectl logs deployment/youmusic`
|
||||
- Review events: `kubectl get events`
|
||||
- Check documentation: See main README.md
|
||||
- GitHub Issues: [Your repo URL]
|
||||
|
||||
---
|
||||
|
||||
**Deployment Version:** 1.0.0
|
||||
**Last Updated:** 2024-10-30
|
||||
@@ -0,0 +1,159 @@
|
||||
# PersistentVolumeClaim for YouMusic data (SQLite DB, music, cache, etc.)
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: youmusic-data-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
nfs:
|
||||
server: 192.168.1.5
|
||||
path: "/fs/1000/nfs/k8s/you-music"
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: "youmusic-data-pvc"
|
||||
spec:
|
||||
storageClassName: ""
|
||||
volumeName: youmusic-data-pv
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
---
|
||||
# Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: youmusic
|
||||
labels:
|
||||
app: youmusic
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: youmusic
|
||||
replicas: 1 # Single replica due to SQLite (ReadWriteOnce)
|
||||
strategy:
|
||||
type: Recreate # Prevent multiple pods accessing SQLite simultaneously
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: youmusic
|
||||
spec:
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: youmusic-data-pvc
|
||||
containers:
|
||||
- name: youmusic
|
||||
image: "ghcr.io/wahyd4/youmusic:latest" # Update with your image
|
||||
imagePullPolicy: Always
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /app/data
|
||||
env:
|
||||
- name: PYTHONUNBUFFERED
|
||||
value: "1"
|
||||
- name: DATABASE_URL
|
||||
value: "sqlite+aiosqlite:///./data/youmusic.db"
|
||||
- name: MUSIC_DIR
|
||||
value: "/app/data/music"
|
||||
- name: UPLOAD_DIR
|
||||
value: "/app/data/uploads"
|
||||
- name: TEMP_DIR
|
||||
value: "/app/data/temp"
|
||||
- name: BASE_DIR
|
||||
value: "/app"
|
||||
- name: FFMPEG_LOCATION
|
||||
value: "ffmpeg"
|
||||
# Optional: Set proxy if needed
|
||||
# - name: PROXY
|
||||
# value: "http://proxy:port"
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2048Mi
|
||||
# If using private registry, add imagePullSecrets
|
||||
# imagePullSecrets:
|
||||
# - name: github-image-pull-secret
|
||||
|
||||
---
|
||||
# Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: youmusic
|
||||
labels:
|
||||
app: youmusic
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: web
|
||||
selector:
|
||||
app: youmusic
|
||||
|
||||
---
|
||||
# Ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: youmusic-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
kubernetes.io/tls-acme: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
# Increase upload size for large music files
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: 500m
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
# Optional: Enable authentication
|
||||
nginx.ingress.kubernetes.io/auth-url: "https://pass.junv.cc/auth"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "https://pass.junv.cc/?redirect=https%3A%2F%2F$host$request_uri"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- music.junv.cc # Update with your domain
|
||||
secretName: youmusic-tls
|
||||
rules:
|
||||
- host: music.junv.cc # Update with your domain
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: youmusic
|
||||
port:
|
||||
number: 80
|
||||
Reference in New Issue
Block a user