feat: add local production Docker testing workflow
Add tooling to test production Docker images locally before CI/CD: - docker-compose.prod-test.yml: Local production test environment - TESTING_PRODUCTION_LOCALLY.md: Comprehensive testing guide - Makefile: Added prod-test-* commands for easy testing New Make commands: - make prod-test - Build and run production image - make prod-test-build - Build with no cache - make prod-test-up - Start in background - make prod-test-logs - View logs - make prod-test-shell - Debug in container - make prod-test-status - Check supervisor status - make prod-test-clean - Fresh start Benefits: - 5-10min faster feedback vs CI/CD builds - Easier debugging with direct container access - Catch Docker/config issues before pushing - Test supervisor, nginx, php-fpm, queue workers locally Usage: make prod-test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
53
Makefile
53
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: help dev dev-down dev-build dev-shell dev-logs dev-vite prod-build prod-up prod-down prod-logs prod-shell prod-vite migrate test clean install
|
||||
.PHONY: help dev dev-down dev-build dev-shell dev-logs dev-vite prod-build prod-up prod-down prod-logs prod-shell prod-vite prod-test prod-test-build prod-test-up prod-test-down prod-test-logs prod-test-shell prod-test-status prod-test-clean migrate test clean install
|
||||
|
||||
# Default target
|
||||
.DEFAULT_GOAL := help
|
||||
@@ -56,6 +56,43 @@ prod-artisan: ## Run artisan in production (usage: make prod-artisan CMD="migrat
|
||||
prod-vite: ## Build production assets (for CI/CD)
|
||||
./vendor/bin/sail npm run build
|
||||
|
||||
# ==================== Production Testing (Local) ====================
|
||||
prod-test: ## Test production image locally (foreground)
|
||||
@echo "🔨 Building and testing production image locally..."
|
||||
docker-compose -f docker-compose.prod-test.yml up --build
|
||||
|
||||
prod-test-build: ## Build production test image (no cache)
|
||||
@echo "🔨 Building production test image..."
|
||||
docker-compose -f docker-compose.prod-test.yml build --no-cache --pull
|
||||
|
||||
prod-test-up: ## Start production test environment (background)
|
||||
@echo "🚀 Starting production test environment..."
|
||||
docker-compose -f docker-compose.prod-test.yml up -d
|
||||
@echo ""
|
||||
@echo "✅ Production test running!"
|
||||
@echo " App: http://localhost:8080"
|
||||
@echo " Database: localhost:5433"
|
||||
@echo ""
|
||||
@echo "View logs: make prod-test-logs"
|
||||
|
||||
prod-test-down: ## Stop production test environment
|
||||
docker-compose -f docker-compose.prod-test.yml down
|
||||
|
||||
prod-test-logs: ## View production test logs
|
||||
docker-compose -f docker-compose.prod-test.yml logs -f app
|
||||
|
||||
prod-test-shell: ## Open shell in production test container
|
||||
docker-compose -f docker-compose.prod-test.yml exec app /bin/sh
|
||||
|
||||
prod-test-status: ## Check supervisor status in production test
|
||||
@echo "📊 Supervisor status:"
|
||||
docker-compose -f docker-compose.prod-test.yml exec app supervisorctl status
|
||||
|
||||
prod-test-clean: ## Clean production test environment (removes volumes)
|
||||
@echo "🧹 Cleaning production test environment..."
|
||||
docker-compose -f docker-compose.prod-test.yml down -v
|
||||
@echo "✅ Cleaned! Run 'make prod-test' for fresh start"
|
||||
|
||||
# ==================== Database ====================
|
||||
migrate: ## Run database migrations (Sail)
|
||||
./vendor/bin/sail artisan migrate
|
||||
@@ -98,13 +135,15 @@ mailpit: ## Open Mailpit web UI
|
||||
help: ## Show this help message
|
||||
@echo "\n📦 CannaBrands Docker Commands\n"
|
||||
@echo "Local Development (Sail):"
|
||||
@grep -E '^dev.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nProduction:"
|
||||
@grep -E '^prod.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -E '^dev.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nProduction Testing (Local):"
|
||||
@grep -E '^prod-test.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[33m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nProduction (K8s/Deployment):"
|
||||
@grep -E '^prod-[^t].*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nDatabase:"
|
||||
@grep -E '^(migrate|seed).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -E '^(migrate|seed).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nTesting:"
|
||||
@grep -E '^test.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -E '^test.*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo "\nUtilities:"
|
||||
@grep -E '^(clean|install|mailpit).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -E '^(clean|install|mailpit).*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
||||
@echo ""
|
||||
|
||||
174
TESTING_PRODUCTION_LOCALLY.md
Normal file
174
TESTING_PRODUCTION_LOCALLY.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Testing Production Docker Image Locally
|
||||
|
||||
This guide helps you test the production Docker image locally before pushing to CI/CD.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Build and start the production image locally
|
||||
docker-compose -f docker-compose.prod-test.yml up --build
|
||||
|
||||
# Access the app at: http://localhost:8080
|
||||
# PostgreSQL accessible at: localhost:5433
|
||||
```
|
||||
|
||||
## Why Test Locally?
|
||||
|
||||
- ✅ **Faster feedback** - 2-5 min vs 10-15 min through CI/CD
|
||||
- ✅ **Save CI resources** - Don't waste build minutes
|
||||
- ✅ **Easier debugging** - Direct container access
|
||||
- ✅ **Catch issues early** - Before they hit version control
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Build and Run
|
||||
```bash
|
||||
# Start everything (builds if needed)
|
||||
docker-compose -f docker-compose.prod-test.yml up
|
||||
|
||||
# Build with no cache (clean build)
|
||||
docker-compose -f docker-compose.prod-test.yml build --no-cache
|
||||
|
||||
# Run in background
|
||||
docker-compose -f docker-compose.prod-test.yml up -d
|
||||
|
||||
# View logs
|
||||
docker-compose -f docker-compose.prod-test.yml logs -f app
|
||||
```
|
||||
|
||||
### Debug Inside Container
|
||||
```bash
|
||||
# Execute shell in running container
|
||||
docker-compose -f docker-compose.prod-test.yml exec app /bin/sh
|
||||
|
||||
# Check supervisor status
|
||||
docker-compose -f docker-compose.prod-test.yml exec app supervisorctl status
|
||||
|
||||
# View nginx logs
|
||||
docker-compose -f docker-compose.prod-test.yml exec app cat /var/log/nginx/error.log
|
||||
|
||||
# View Laravel logs
|
||||
docker-compose -f docker-compose.prod-test.yml exec app tail -f /var/www/html/storage/logs/laravel.log
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
```bash
|
||||
# Stop everything
|
||||
docker-compose -f docker-compose.prod-test.yml down
|
||||
|
||||
# Remove volumes too (fresh database)
|
||||
docker-compose -f docker-compose.prod-test.yml down -v
|
||||
```
|
||||
|
||||
## Testing Workflow
|
||||
|
||||
### Before Pushing to CI/CD:
|
||||
|
||||
1. **Make your changes** to Dockerfile, configs, etc.
|
||||
|
||||
2. **Test locally:**
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod-test.yml up --build
|
||||
```
|
||||
|
||||
3. **Verify it works:**
|
||||
- App accessible at http://localhost:8080
|
||||
- No errors in logs: `docker-compose -f docker-compose.prod-test.yml logs app`
|
||||
- All services running: `docker-compose -f docker-compose.prod-test.yml exec app supervisorctl status`
|
||||
|
||||
4. **If it works, push to develop:**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "fix: your change"
|
||||
git push origin develop
|
||||
```
|
||||
|
||||
5. **If it doesn't work, debug:**
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose -f docker-compose.prod-test.yml logs app
|
||||
|
||||
# Exec into container
|
||||
docker-compose -f docker-compose.prod-test.yml exec app /bin/sh
|
||||
|
||||
# Make fixes and rebuild
|
||||
docker-compose -f docker-compose.prod-test.yml up --build
|
||||
```
|
||||
|
||||
## Differences from Production
|
||||
|
||||
This local test environment differs from production K8s in:
|
||||
|
||||
- Uses local PostgreSQL (not persistent volume)
|
||||
- Runs on localhost:8080 (not ingress with TLS)
|
||||
- Uses test APP_KEY (not secret from K8s)
|
||||
- Single replica (not multiple pods)
|
||||
- No load balancer or ingress
|
||||
|
||||
But it **does test**:
|
||||
- ✅ Dockerfile builds correctly
|
||||
- ✅ All directories exist and have correct permissions
|
||||
- ✅ Supervisor starts all services (nginx, php-fpm, workers, scheduler)
|
||||
- ✅ Laravel boots and connects to database
|
||||
- ✅ Migrations run successfully
|
||||
- ✅ Application responds to HTTP requests
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port conflicts
|
||||
If 8080 or 5433 are already in use, edit `docker-compose.prod-test.yml` and change the ports.
|
||||
|
||||
### Build errors
|
||||
```bash
|
||||
# Clean build from scratch
|
||||
docker-compose -f docker-compose.prod-test.yml build --no-cache --pull
|
||||
```
|
||||
|
||||
### Container crashes immediately
|
||||
```bash
|
||||
# Check logs for error
|
||||
docker-compose -f docker-compose.prod-test.yml logs app
|
||||
|
||||
# Try running without detach to see output
|
||||
docker-compose -f docker-compose.prod-test.yml up
|
||||
```
|
||||
|
||||
### Need fresh database
|
||||
```bash
|
||||
# Remove volumes and recreate
|
||||
docker-compose -f docker-compose.prod-test.yml down -v
|
||||
docker-compose -f docker-compose.prod-test.yml up
|
||||
```
|
||||
|
||||
## Integration with Development Workflow
|
||||
|
||||
### Using alongside Laravel Sail
|
||||
|
||||
Sail and prod-test can run simultaneously:
|
||||
- Sail dev environment: http://localhost (port 80)
|
||||
- Production test: http://localhost:8080
|
||||
|
||||
Just make sure to use different database ports (Sail: 5432, prod-test: 5433).
|
||||
|
||||
### When to use each:
|
||||
|
||||
**Laravel Sail (docker-compose.yml):**
|
||||
- Daily development
|
||||
- Running artisan commands
|
||||
- Testing code changes
|
||||
- Hot reload with Vite
|
||||
|
||||
**Production Test (docker-compose.prod-test.yml):**
|
||||
- Before pushing to develop
|
||||
- Testing Dockerfile changes
|
||||
- Testing supervisor/nginx configs
|
||||
- Verifying production build process
|
||||
|
||||
## Next Steps After Local Success
|
||||
|
||||
Once your image works locally:
|
||||
|
||||
1. ✅ Commit and push to develop
|
||||
2. ✅ CI/CD builds the same image
|
||||
3. ✅ Deploy to Kubernetes with confidence
|
||||
4. ✅ Much faster iteration cycle!
|
||||
75
docker-compose.prod-test.yml
Normal file
75
docker-compose.prod-test.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
# Docker Compose for testing production image locally
|
||||
# Usage: docker-compose -f docker-compose.prod-test.yml up --build
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# PostgreSQL database for local testing
|
||||
postgres:
|
||||
image: postgres:17
|
||||
environment:
|
||||
POSTGRES_DB: cannabrands_test
|
||||
POSTGRES_USER: cannabrands_test
|
||||
POSTGRES_PASSWORD: test_password
|
||||
ports:
|
||||
- "5433:5432" # Different port to avoid conflicts with Sail
|
||||
volumes:
|
||||
- postgres_test_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U cannabrands_test"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Your production Laravel app
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
GIT_COMMIT_SHA: local-test
|
||||
APP_VERSION: local
|
||||
ports:
|
||||
- "8080:80" # Map to 8080 to avoid conflicts with Sail
|
||||
environment:
|
||||
# Application
|
||||
APP_NAME: "Cannabrands CRM (Test)"
|
||||
APP_ENV: local
|
||||
APP_DEBUG: "true"
|
||||
APP_KEY: "base64:zYloN4cn3qDHbL6h2VrP32M7+/D582YVrtMT25DR+ik=" # Test key
|
||||
APP_URL: http://localhost:8080
|
||||
|
||||
# Database
|
||||
DB_CONNECTION: pgsql
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_DATABASE: cannabrands_test
|
||||
DB_USERNAME: cannabrands_test
|
||||
DB_PASSWORD: test_password
|
||||
|
||||
# Cache & Sessions
|
||||
CACHE_DRIVER: file
|
||||
SESSION_DRIVER: database
|
||||
QUEUE_CONNECTION: database
|
||||
VIEW_COMPILED_PATH: /var/www/html/storage/framework/views
|
||||
|
||||
# Mail (use log driver for testing)
|
||||
MAIL_MAILER: log
|
||||
MAIL_FROM_ADDRESS: test@cannabrands.local
|
||||
MAIL_FROM_NAME: "Cannabrands CRM Test"
|
||||
|
||||
# Logging
|
||||
LOG_CHANNEL: stack
|
||||
LOG_LEVEL: debug
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
volumes:
|
||||
postgres_test_data:
|
||||
driver: local
|
||||
Reference in New Issue
Block a user