Refine Docker, docker-compose, and EC2 deploy
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
284fb12576
commit
dfebb51bd2
50
.github/workflows/deploy-ec2.yml
vendored
50
.github/workflows/deploy-ec2.yml
vendored
@ -37,6 +37,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Deploy to EC2 with rollback
|
- name: Deploy to EC2 with rollback
|
||||||
uses: appleboy/ssh-action@v1.2.0
|
uses: appleboy/ssh-action@v1.2.0
|
||||||
|
env:
|
||||||
|
EC2_ENV: ${{ secrets.EC2_ENV }}
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.EC2_HOST }}
|
host: ${{ secrets.EC2_HOST }}
|
||||||
username: ${{ secrets.EC2_USER }}
|
username: ${{ secrets.EC2_USER }}
|
||||||
@ -51,11 +53,7 @@ jobs:
|
|||||||
mkdir -p "$APP_DIR"
|
mkdir -p "$APP_DIR"
|
||||||
cd "$APP_DIR"
|
cd "$APP_DIR"
|
||||||
|
|
||||||
echo "Writing .env file from EC2_ENV secret..."
|
# Initialize git repo if needed
|
||||||
# Overwrite .env on the server with the contents of the EC2_ENV variable
|
|
||||||
# EC2_ENV is provided by GitHub as a multi-line secret matching your local .env
|
|
||||||
printf '%s\n' "$EC2_ENV" > .env
|
|
||||||
|
|
||||||
if [ ! -d ".git" ]; then
|
if [ ! -d ".git" ]; then
|
||||||
echo "Cloning repository for the first time..."
|
echo "Cloning repository for the first time..."
|
||||||
git clone ${{ github.repositoryUrl }} .
|
git clone ${{ github.repositoryUrl }} .
|
||||||
@ -71,15 +69,33 @@ jobs:
|
|||||||
git checkout ${{ github.ref_name }}
|
git checkout ${{ github.ref_name }}
|
||||||
git reset --hard ${{ github.sha }}
|
git reset --hard ${{ github.sha }}
|
||||||
|
|
||||||
echo "Pulling docker images and rebuilding containers..."
|
echo "Writing .env file from EC2_ENV secret..."
|
||||||
if command -v docker-compose >/dev/null 2>&1; then
|
# Write .env file AFTER pulling latest code so it's in the right location
|
||||||
DC="docker-compose"
|
# EC2_ENV is provided by GitHub as a multi-line secret matching your local .env
|
||||||
else
|
printf '%s\n' "$EC2_ENV" > .env
|
||||||
DC="docker compose"
|
|
||||||
|
echo "Checking Docker and Docker Compose..."
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
echo "Error: Docker is not installed"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$DC pull || true
|
if command -v docker-compose >/dev/null 2>&1; then
|
||||||
|
DC="docker-compose"
|
||||||
|
elif docker compose version >/dev/null 2>&1; then
|
||||||
|
DC="docker compose"
|
||||||
|
else
|
||||||
|
echo "Error: Docker Compose is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Using: $DC"
|
||||||
|
$DC version
|
||||||
|
|
||||||
|
echo "Stopping existing containers..."
|
||||||
|
$DC down || true
|
||||||
|
|
||||||
|
echo "Building and starting containers with docker-compose..."
|
||||||
set +e
|
set +e
|
||||||
$DC up -d --build
|
$DC up -d --build
|
||||||
DEPLOY_EXIT=$?
|
DEPLOY_EXIT=$?
|
||||||
@ -89,26 +105,32 @@ jobs:
|
|||||||
echo "Deployment failed, attempting rollback..."
|
echo "Deployment failed, attempting rollback..."
|
||||||
if [ -n "$PREV_COMMIT" ]; then
|
if [ -n "$PREV_COMMIT" ]; then
|
||||||
git reset --hard "$PREV_COMMIT"
|
git reset --hard "$PREV_COMMIT"
|
||||||
|
printf '%s\n' "$EC2_ENV" > .env
|
||||||
$DC up -d --build || true
|
$DC up -d --build || true
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Waiting for containers to be healthy..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
echo "Running health check..."
|
echo "Running health check..."
|
||||||
HEALTH_URL="${HEALTH_URL:-http://localhost:3000/api/health}"
|
HEALTH_URL="${HEALTH_URL:-http://localhost:3000/api/health}"
|
||||||
|
|
||||||
for i in {1..10}; do
|
for i in {1..15}; do
|
||||||
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
|
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
|
||||||
echo "Health check passed."
|
echo "Health check passed on attempt $i."
|
||||||
|
echo "Deployment successful!"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Health check attempt $i failed, retrying..."
|
echo "Health check attempt $i failed, retrying in 5 seconds..."
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Health check failed after multiple attempts, rolling back..."
|
echo "Health check failed after multiple attempts, rolling back..."
|
||||||
if [ -n "$PREV_COMMIT" ]; then
|
if [ -n "$PREV_COMMIT" ]; then
|
||||||
git reset --hard "$PREV_COMMIT"
|
git reset --hard "$PREV_COMMIT"
|
||||||
|
printf '%s\n' "$EC2_ENV" > .env
|
||||||
$DC up -d --build || true
|
$DC up -d --build || true
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
11
Dockerfile
11
Dockerfile
@ -2,13 +2,20 @@ FROM node:20-alpine
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
COPY package*.json prisma.config.ts ./
|
COPY package*.json prisma.config.ts ./
|
||||||
COPY prisma ./prisma
|
COPY prisma ./prisma
|
||||||
|
|
||||||
RUN npm install && npx prisma generate
|
# Install dependencies and generate Prisma client
|
||||||
|
RUN npm install --only=production && npx prisma generate
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build the Next.js application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["npm", "run", "dev"]
|
# Use production start command
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
|||||||
@ -11,21 +11,34 @@ services:
|
|||||||
- "5444:5432"
|
- "5444:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
# Override DATABASE_URL to use internal docker network hostname
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@db:5432/${POSTGRES_DB:-dropshipping}?schema=public
|
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@db:5432/${POSTGRES_DB:-dropshipping}?schema=public
|
||||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
NODE_ENV: production
|
||||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
|
|
||||||
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
|
||||||
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|||||||
26
src/app/api/health/route.ts
Normal file
26
src/app/api/health/route.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { NextResponse } from "next/server"
|
||||||
|
import { prisma } from "@/lib/prisma"
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// Simple database connectivity check
|
||||||
|
await prisma.$queryRaw`SELECT 1`
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
status: "ok",
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Health check failed:", error)
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
status: "error",
|
||||||
|
message: "Database connection failed"
|
||||||
|
},
|
||||||
|
{ status: 503 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user