# Docker Guide This document covers Docker deployment, configuration, and troubleshooting for The Biergarten App. ## Overview The project uses Docker Compose to orchestrate multiple services: - SQL Server 2022 database - Database migrations runner (DbUp) - Database seeder - .NET API - Test runners See the [deployment diagram](diagrams/pdf/deployment.pdf) for visual representation. ## Docker Compose Environments ### 1. Development (`docker-compose.dev.yaml`) **Purpose**: Local development with persistent data **Features**: - Persistent SQL Server volume - Hot reload support - Swagger UI enabled - Seed data included - `CLEAR_DATABASE=true` (drops and recreates schema) **Services**: ```yaml sqlserver # SQL Server 2022 (port 1433) database.migrations # DbUp migrations database.seed # Seed initial data api.core # Web API (ports 8080, 8081) ``` **Start Development Environment**: ```bash docker compose -f docker-compose.dev.yaml up -d ``` **Access**: - API Swagger: http://localhost:8080/swagger - Health Check: http://localhost:8080/health - SQL Server: localhost:1433 (sa credentials from .env.dev) **Stop Environment**: ```bash # Stop services (keep volumes) docker compose -f docker-compose.dev.yaml down # Stop and remove volumes (fresh start) docker compose -f docker-compose.dev.yaml down -v ``` ### 2. Testing (`docker-compose.test.yaml`) **Purpose**: Automated CI/CD testing in isolated environment **Features**: - Fresh database each run - All test suites execute in parallel - Test results exported to `./test-results/` - Containers auto-exit after completion - Fully isolated testnet network **Services**: ```yaml sqlserver # Test database database.migrations # Fresh schema database.seed # Test data api.specs # Reqnroll BDD tests repository.tests # Repository unit tests service.auth.tests # Service unit tests ``` **Run Tests**: ```bash # Run all tests docker compose -f docker-compose.test.yaml up --abort-on-container-exit # View results ls -la test-results/ cat test-results/api-specs/results.trx cat test-results/repository-tests/results.trx cat test-results/service-auth-tests/results.trx # Clean up docker compose -f docker-compose.test.yaml down -v ``` ### 3. Production (`docker-compose.prod.yaml`) **Purpose**: Production-ready deployment **Features**: - Production logging levels - No database clearing - Optimized build configurations - Health checks enabled - Restart policies (unless-stopped) - Security hardening **Services**: ```yaml sqlserver # Production SQL Server database.migrations # Schema updates only api.core # Production API ``` **Deploy Production**: ```bash docker compose -f docker-compose.prod.yaml up -d ``` **Note**: In real production, use orchestration platforms (Kubernetes, ECS, etc.). ## Service Dependencies Docker Compose manages startup order using health checks: ```mermaid sqlserver (health check) ↓ database.migrations (completes successfully) ↓ database.seed (completes successfully) ↓ api.core / tests (start when ready) ``` **Health Check Example** (SQL Server): ```yaml healthcheck: test: ["CMD-SHELL", "sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'"] interval: 10s timeout: 5s retries: 12 start_period: 30s ``` **Dependency Configuration**: ```yaml api.core: depends_on: database.seed: condition: service_completed_successfully ``` ## Volumes ### Persistent Volumes **Development**: - `sqlserverdata-dev` - Database files persist between restarts - `nuget-cache-dev` - NuGet package cache (speeds up builds) **Testing**: - `sqlserverdata-test` - Temporary, typically removed after tests **Production**: - `sqlserverdata-prod` - Production database files - `nuget-cache-prod` - Production NuGet cache ### Mounted Volumes **Test Results**: ```yaml volumes: - ./test-results:/app/test-results ``` Test results are written to host filesystem for CI/CD integration. **Code Volumes** (development only): ```yaml volumes: - ./src:/app/src # Hot reload for development ``` ## Networks Each environment uses isolated bridge networks: - `devnet` - Development network - `testnet` - Testing network (fully isolated) - `prodnet` - Production network This prevents cross-environment communication and improves security. ## Environment Variables All containers are configured via environment variables from `.env` files: ```yaml env_file: ".env.dev" # or .env.test, .env.prod environment: ASPNETCORE_ENVIRONMENT: "Development" DOTNET_RUNNING_IN_CONTAINER: "true" DB_SERVER: "${DB_SERVER}" DB_NAME: "${DB_NAME}" DB_USER: "${DB_USER}" DB_PASSWORD: "${DB_PASSWORD}" JWT_SECRET: "${JWT_SECRET}" ``` For complete list, see [Environment Variables](environment-variables.md). ## Common Commands ### View Services ```bash # Running services docker compose -f docker-compose.dev.yaml ps # All containers (including stopped) docker ps -a ``` ### View Logs ```bash # All services docker compose -f docker-compose.dev.yaml logs -f # Specific service docker compose -f docker-compose.dev.yaml logs -f api.core # Last 100 lines docker compose -f docker-compose.dev.yaml logs --tail=100 api.core ``` ### Execute Commands in Container ```bash # Interactive shell docker exec -it dev-env-api-core bash # Run command docker exec dev-env-sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'password' -C ``` ### Restart Services ```bash # Restart all services docker compose -f docker-compose.dev.yaml restart # Restart specific service docker compose -f docker-compose.dev.yaml restart api.core # Rebuild and restart docker compose -f docker-compose.dev.yaml up -d --build api.core ``` ### Build Images ```bash # Build all images docker compose -f docker-compose.dev.yaml build # Build specific service docker compose -f docker-compose.dev.yaml build api.core # Build without cache docker compose -f docker-compose.dev.yaml build --no-cache ``` ### Clean Up ```bash # Stop and remove containers docker compose -f docker-compose.dev.yaml down # Remove containers and volumes docker compose -f docker-compose.dev.yaml down -v # Remove containers, volumes, and images docker compose -f docker-compose.dev.yaml down -v --rmi all # System-wide cleanup docker system prune -af --volumes ``` ## Dockerfile Structure ### Multi-Stage Build All service Dockerfiles use multi-stage builds: ```dockerfile # Stage 1: Build FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY ["Project/Project.csproj", "Project/"] RUN dotnet restore COPY . . RUN dotnet build -c Release # Stage 2: Runtime FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final WORKDIR /app COPY --from=build /app/build . ENTRYPOINT ["dotnet", "Project.dll"] ``` **Benefits**: - Smaller final images (no SDK) - Cached layers speed up builds - Separation of build and runtime dependencies ## Troubleshooting ### Port Already in Use **Problem**: Port 8080 or 1433 already bound **Solution**: ```bash # Find process using port lsof -ti:8080 lsof -ti:1433 # Kill process kill -9 $(lsof -ti:8080) # Or change port in docker-compose.yaml ports: - "8081:8080" # Map to different host port ``` ### Container Won't Start **Problem**: Container exits immediately **Solution**: ```bash # View logs docker compose -f docker-compose.dev.yaml logs # Check container status docker compose -f docker-compose.dev.yaml ps # Inspect container docker inspect ``` ### Database Connection Failed **Problem**: API can't connect to SQL Server **Check**: 1. SQL Server container is running: ```bash docker compose -f docker-compose.dev.yaml ps sqlserver ``` 2. Health check is passing: ```bash docker inspect dev-env-sqlserver | grep -A 10 Health ``` 3. Connection string is correct in `.env` file 4. SQL Server is accepting connections: ```bash docker exec dev-env-sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'password' -C -Q "SELECT 1" ``` ### Out of Disk Space **Problem**: No space left on device **Solution**: ```bash # Check Docker disk usage docker system df # Remove unused data docker system prune -af --volumes # Remove specific volumes docker volume ls docker volume rm ``` ### SQL Server Container Unhealthy **Problem**: Health check failing **Reasons**: - Incorrect password - Container still starting up - Insufficient memory **Solution**: ```bash # Check logs docker compose -f docker-compose.dev.yaml logs sqlserver # Verify password matches .env file grep DB_PASSWORD .env.dev # Increase memory in Docker Desktop settings (min 4GB recommended) ``` ### Build Failures **Problem**: Docker build fails **Common Causes**: - Missing project references - Incorrect COPY paths - Network issues during restore **Solution**: ```bash # Build without cache docker compose -f docker-compose.dev.yaml build --no-cache # Check Dockerfile COPY paths match project structure # Ensure all .csproj files are in correct locations ``` ### Test Results Not Appearing **Problem**: `test-results/` folder is empty **Solution**: ```bash # Ensure folder has write permissions chmod -R 755 test-results/ # Check test container logs docker compose -f docker-compose.test.yaml logs api.specs # Verify volume mount docker compose -f docker-compose.test.yaml config | grep -A 5 volumes ``` ## Performance Optimization ### Build Cache Leverage Docker layer caching: 1. Copy `.csproj` files first 2. Run `dotnet restore` 3. Copy source code 4. Build application This allows dependency layer caching when only source changes. ### Volume Mounts for Development Use NuGet cache volume for faster rebuilds: ```yaml volumes: - nuget-cache-dev:/root/.nuget/packages ``` ### Resource Limits Set memory/CPU limits for containers: ```yaml deploy: resources: limits: memory: 2GB cpus: '1.0' ``` ## CI/CD Integration ### GitHub Actions Example ```yaml name: Run Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run tests run: | docker compose -f docker-compose.test.yaml build docker compose -f docker-compose.test.yaml up --abort-on-container-exit - name: Upload test results uses: actions/upload-artifact@v3 with: name: test-results path: test-results/ ``` ## Security Best Practices 1. **Don't commit `.env` files** - Use `.env.example` as template 2. **Use secrets management** - For production credentials 3. **Run as non-root user** - Configure USER in Dockerfile 4. **Scan images** - Use `docker scan` or Trivy 5. **Keep base images updated** - Regularly update FROM images 6. **Minimize installed packages** - Only install what's needed 7. **Use specific tags** - Avoid `latest` tag ## Additional Resources - [Docker Compose Documentation](https://docs.docker.com/compose/) - [.NET Docker Images](https://hub.docker.com/_/microsoft-dotnet) - [SQL Server Docker Images](https://hub.docker.com/_/microsoft-mssql-server)