diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..1dfbd04 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,115 @@ +name: Production Deployment + +on: + push: + branches: + - main + - refactored + +jobs: + build: + name: Build and Push Docker Image + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: git.nciphered.com + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push Image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + git.nciphered.com/${{ github.repository }}:latest + git.nciphered.com/${{ github.repository }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy-droplet: + name: Deploy to DigitalOcean Droplet + needs: build + runs-on: ubuntu-latest + if: ${{ secrets.DEPLOY_TARGET == 'droplet' }} + steps: + - name: Executing remote SSH commands to deploy + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.DROPLET_HOST }} + username: ${{ secrets.DROPLET_USER }} + key: ${{ secrets.DROPLET_SSH_KEY }} + script: | + # Authenticate with Gitea registry on Droplet + docker login -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}" git.nciphered.com + + # Ensure isolated network exists + docker network create dockernet 2>/dev/null || true + + # Pull latest image + docker pull git.nciphered.com/${{ github.repository }}:latest + + # Stop existing container + docker rm -f stick-app-container || true + + # Run container on the 'dockernet' network + docker run -d \ + --name stick-app-container \ + --network dockernet \ + -p 80:3007 \ + -e DATABASE_URL="${{ secrets.DATABASE_URL_DROPLET }}" \ + -e DATABASE_NAME="${{ secrets.DATABASE_NAME }}" \ + -e JWT_SECRET="${{ secrets.JWT_SECRET }}" \ + -e HOST="0.0.0.0" \ + -e PORT="3007" \ + --restart unless-stopped \ + git.nciphered.com/${{ github.repository }}:latest + + deploy-cloudrun: + name: Deploy to Google Cloud Run + needs: build + runs-on: ubuntu-latest + if: ${{ secrets.DEPLOY_TARGET == 'cloudrun' }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Authenticate with Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_KEY }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v2 + + - name: Configure Docker Authentication + run: | + gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet + + - name: Tag and Push Image to Artifact Registry + run: | + # Build/Tag for Google Artifact Registry + docker tag git.nciphered.com/${{ github.repository }}:latest asia-southeast1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/stick/app:latest + docker push asia-southeast1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/stick/app:latest + + - name: Deploy to Google Cloud Run + uses: google-github-actions/deploy-cloudrun@v2 + with: + service: stick-app + image: asia-southeast1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/stick/app:latest + region: asia-southeast1 + env_vars: | + DATABASE_URL=${{ secrets.DATABASE_URL_CLOUDRUN }} + DATABASE_NAME=${{ secrets.DATABASE_NAME }} + JWT_SECRET=${{ secrets.JWT_SECRET }} + HOST=0.0.0.0 + PORT=3007 + diff --git a/README.md b/README.md index 214d8cd..3b689fd 100644 --- a/README.md +++ b/README.md @@ -200,3 +200,51 @@ pub async fn delete_task_handler( Ok(Redirect::to("/tasks")) } ``` + +--- + +## Production Deployment to a Cloud Host (DigitalOcean Droplet) + +For production deployments (such as to a DigitalOcean Droplet), we avoid using `--network="host"`. Instead, we deploy both the database and the application container to a shared, user-defined Docker bridge network named **`dockernet`**. This provides secure internal DNS resolution and container isolation. + +### 1. Create the Isolated Docker Network +On your Droplet, create the bridge network: +```bash +docker network create dockernet +``` + +### 2. Build and Run the Database Infrastructure +Build the custom MongoDB infrastructure image using the dedicated `Infra.DockerFile`: +```bash +# 1. Build the database image +docker build -t stick-db -f Infra.DockerFile . + +# 2. Run the database container on 'dockernet' with host persistence +docker run --name stick-mongodb \ + --network dockernet \ + -v /var/lib/mongodb/data:/data/db \ + -d \ + stick-db +``` +*Note: The database container is named `stick-mongodb`. Other containers on `dockernet` can now resolve this container using `mongodb://stick-mongodb:27017`.* + +### 3. Build and Deploy the Application Container +Build the main application image and launch it on the same network: +```bash +# 1. Build the application image +docker build -t stick-app . + +# 2. Run the application container, linking to the database using its container name +docker run --name stick-app-container \ + --network dockernet \ + -p 80:3007 \ + -e DATABASE_URL="mongodb://stick-mongodb:27017" \ + -e DATABASE_NAME="stick_db" \ + -e JWT_SECRET="your_secure_production_jwt_signing_key_at_least_32_chars_long" \ + -e HOST="0.0.0.0" \ + -e PORT="3007" \ + -d \ + stick-app +``` +*Note: `-p 80:3007` maps the Droplet's external HTTP port 80 to the application's internal container port 3007.* +