ci: Add docker images build
This commit is contained in:
12 files changed
+1539
No files matched your search
@@ -0,0 +1,394 @@
|
||||
# Container Deployment Guide
|
||||
|
||||
This guide covers the containerized deployment setup for the www.ermnvldmr.com website, including both the main Astro site and the Storybook component library.
|
||||
|
||||
## Overview
|
||||
|
||||
The project uses Docker containers to package and deploy:
|
||||
- **Main Site**: Astro static website served via nginx
|
||||
- **Storybook**: Component library served via nginx
|
||||
- **Registry**: GitHub Container Registry (ghcr.io)
|
||||
- **CI/CD**: GitHub Actions with semantic versioning
|
||||
|
||||
## Container Architecture
|
||||
|
||||
### Multi-stage Build Strategy
|
||||
|
||||
Both containers follow a multi-stage build pattern:
|
||||
|
||||
1. **Builder Stage**: Node.js 20 Alpine with pnpm for building assets
|
||||
2. **Production Stage**: nginx:1.25-alpine for serving static content
|
||||
|
||||
### Security Features
|
||||
|
||||
- **Non-root user**: Containers run as `nginx-user` (UID 1001)
|
||||
- **Non-privileged ports**: Expose port 8080 instead of 80
|
||||
- **Security headers**: CSP, HSTS, and other security headers configured
|
||||
- **Signal handling**: dumb-init for proper signal handling
|
||||
|
||||
### Images
|
||||
|
||||
- **Main Site**: `ghcr.io/deytenit/www.ermnvldmr.com`
|
||||
- **Storybook**: `ghcr.io/deytenit/www.ermnvldmr.com/storybook`
|
||||
|
||||
## Tagging Strategy
|
||||
|
||||
### Release Tags (from `release/semantic-dispatch`)
|
||||
|
||||
For both images:
|
||||
- `vX.X.X` - Full version (e.g., `v1.2.3`)
|
||||
- `vX.X` - Minor version (e.g., `v1.2`)
|
||||
- `vX` - Major version (e.g., `v1`)
|
||||
- `latest` - Latest stable release
|
||||
- `{sha}` - Commit SHA for traceability
|
||||
|
||||
### Pre-release Tags (from `next/semantic-dispatch`)
|
||||
|
||||
For both images:
|
||||
- `vX.X.X-next.X` - Full prerelease version (e.g., `v1.2.3-next.1`)
|
||||
- `vX.X.X-next` - Latest prerelease for that version
|
||||
- `vX.X-next` - Latest prerelease for minor version
|
||||
- `vX-next` - Latest prerelease for major version
|
||||
- `latest-next` - Latest prerelease overall
|
||||
- `{sha}` - Commit SHA for traceability
|
||||
|
||||
## Local Development
|
||||
|
||||
### Using Docker Compose
|
||||
|
||||
Start both services locally:
|
||||
|
||||
```bash
|
||||
# Start both containers
|
||||
docker-compose up
|
||||
|
||||
# Start in background
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop services
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
Access points:
|
||||
- **Main Site**: http://localhost:3000
|
||||
- **Storybook**: http://localhost:6006
|
||||
|
||||
### Building Individual Images
|
||||
|
||||
```bash
|
||||
# Build Astro site
|
||||
docker build -f docker/astro/Dockerfile -t www-astro .
|
||||
|
||||
# Build Storybook
|
||||
docker build -f docker/storybook/Dockerfile -t www-storybook .
|
||||
|
||||
# Run containers
|
||||
docker run -p 3000:8080 www-astro
|
||||
docker run -p 6006:8080 www-storybook
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Manual Container Deployment
|
||||
|
||||
Pull and run the latest images:
|
||||
|
||||
```bash
|
||||
# Pull latest stable images
|
||||
docker pull ghcr.io/deytenit/www.ermnvldmr.com:latest
|
||||
docker pull ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest
|
||||
|
||||
# Run main site
|
||||
docker run -d \
|
||||
--name www-ermnvldmr-astro \
|
||||
-p 3000:8080 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/deytenit/www.ermnvldmr.com:latest
|
||||
|
||||
# Run storybook
|
||||
docker run -d \
|
||||
--name www-ermnvldmr-storybook \
|
||||
-p 6006:8080 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The containers are designed to be stateless and don't require runtime environment variables. Configuration is baked into the build.
|
||||
|
||||
### Health Checks
|
||||
|
||||
Both containers include health check endpoints:
|
||||
|
||||
```bash
|
||||
# Check container health
|
||||
curl http://localhost:3000/health # Astro site
|
||||
curl http://localhost:6006/health # Storybook
|
||||
```
|
||||
|
||||
### Resource Requirements
|
||||
|
||||
**Recommended minimums per container:**
|
||||
- **CPU**: 0.1 cores
|
||||
- **Memory**: 64MB
|
||||
- **Storage**: 100MB
|
||||
|
||||
**Production recommendations:**
|
||||
- **CPU**: 0.25 cores
|
||||
- **Memory**: 128MB
|
||||
- **Storage**: 500MB (for logs and cache)
|
||||
|
||||
## Reverse Proxy Configuration
|
||||
|
||||
### nginx Configuration Example
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name www.ermnvldmr.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://astro-container:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name storybook.ermnvldmr.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://storybook-container:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Traefik Configuration Example
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml with Traefik
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
astro:
|
||||
image: ghcr.io/deytenit/www.ermnvldmr.com:latest
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.astro.rule=Host(`www.ermnvldmr.com`)"
|
||||
- "traefik.http.services.astro.loadbalancer.server.port=8080"
|
||||
|
||||
storybook:
|
||||
image: ghcr.io/deytenit/www.ermnvldmr.com/storybook:latest
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.storybook.rule=Host(`storybook.ermnvldmr.com`)"
|
||||
- "traefik.http.services.storybook.loadbalancer.server.port=8080"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker logs container-name
|
||||
|
||||
# Common causes:
|
||||
# - Missing dependencies in build
|
||||
# - Permission issues
|
||||
# - Port conflicts
|
||||
```
|
||||
|
||||
#### Build Failures
|
||||
|
||||
```bash
|
||||
# Check build logs
|
||||
docker build -f docker/astro/Dockerfile -t test .
|
||||
|
||||
# Common causes:
|
||||
# - Node version mismatch
|
||||
# - pnpm cache issues
|
||||
# - Missing build dependencies
|
||||
```
|
||||
|
||||
#### Permission Denied
|
||||
|
||||
```bash
|
||||
# Check if running as correct user
|
||||
docker exec container-name whoami
|
||||
|
||||
# Should output: nginx-user
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
Enable debug mode in nginx:
|
||||
|
||||
```bash
|
||||
# Create debug container
|
||||
docker run -it --entrypoint=/bin/sh ghcr.io/deytenit/www.ermnvldmr.com:latest
|
||||
|
||||
# Check nginx config
|
||||
nginx -t
|
||||
|
||||
# Check file permissions
|
||||
ls -la /usr/share/nginx/html/
|
||||
```
|
||||
|
||||
### Performance Tuning
|
||||
|
||||
#### nginx Optimization
|
||||
|
||||
For high-traffic deployments, consider:
|
||||
|
||||
- Adjusting worker processes
|
||||
- Enabling gzip compression levels
|
||||
- Tuning keepalive settings
|
||||
- Adding rate limiting
|
||||
|
||||
#### Container Resources
|
||||
|
||||
Monitor and adjust:
|
||||
|
||||
```bash
|
||||
# Check resource usage
|
||||
docker stats
|
||||
|
||||
# Update container limits
|
||||
docker run --memory=256m --cpus=0.5 ...
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Container Security
|
||||
|
||||
- Containers run as non-root user
|
||||
- Minimal attack surface with Alpine base
|
||||
- Regular security updates via base image updates
|
||||
- No sensitive data in containers
|
||||
|
||||
### Network Security
|
||||
|
||||
- Expose only necessary ports
|
||||
- Use internal networks for service communication
|
||||
- Implement proper firewall rules
|
||||
- Enable HTTPS with reverse proxy
|
||||
|
||||
### Registry Security
|
||||
|
||||
- Images are signed and scanned
|
||||
- Access controlled via GitHub permissions
|
||||
- Automatic vulnerability scanning
|
||||
- Regular base image updates
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Monitoring
|
||||
|
||||
```bash
|
||||
# Simple health check script
|
||||
#!/bin/bash
|
||||
if curl -f http://localhost:3000/health >/dev/null 2>&1; then
|
||||
echo "Astro site: healthy"
|
||||
else
|
||||
echo "Astro site: unhealthy"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Log Management
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
docker-compose logs -f astro
|
||||
docker-compose logs -f storybook
|
||||
|
||||
# Log rotation (configure docker daemon)
|
||||
# /etc/docker/daemon.json
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Metrics Collection
|
||||
|
||||
Consider integrating with:
|
||||
- Prometheus + Grafana
|
||||
- nginx prometheus exporter
|
||||
- Container metrics exporters
|
||||
- Application performance monitoring
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
The containers are automatically built and pushed when:
|
||||
|
||||
1. **Pre-release**: Merge to `next` branch triggers prerelease
|
||||
2. **Release**: Manual workflow dispatch creates stable release
|
||||
|
||||
### Workflow Outputs
|
||||
|
||||
Each workflow provides:
|
||||
- `new_release_published`: Boolean indicating if release was created
|
||||
- `new_release_version`: The version number of the release
|
||||
|
||||
### Manual Triggers
|
||||
|
||||
```bash
|
||||
# Trigger release workflow via GitHub CLI
|
||||
gh workflow run "release/semantic-dispatch" --ref main
|
||||
|
||||
# Trigger prerelease via merge to next branch
|
||||
git checkout next
|
||||
git merge feature-branch
|
||||
git push origin next
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Regular Updates
|
||||
|
||||
1. **Base Image Updates**: Rebuild monthly for security patches
|
||||
2. **Dependency Updates**: Follow project dependency update schedule
|
||||
3. **Configuration Review**: Quarterly review of nginx and security settings
|
||||
|
||||
### Backup Strategy
|
||||
|
||||
Since containers are stateless:
|
||||
- Source code is backed up in Git repository
|
||||
- Container images are versioned in registry
|
||||
- No persistent data to backup
|
||||
|
||||
### Rollback Procedure
|
||||
|
||||
```bash
|
||||
# Rollback to previous version
|
||||
docker pull ghcr.io/deytenit/www.ermnvldmr.com:v1.2.2
|
||||
docker stop current-container
|
||||
docker run -d --name new-container ghcr.io/deytenit/www.ermnvldmr.com:v1.2.2
|
||||
|
||||
# Or use docker-compose with specific version
|
||||
docker-compose down
|
||||
# Edit docker-compose.yml to specify version
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
For additional support or questions, refer to the project repository issues or documentation.
|
||||
@@ -0,0 +1,303 @@
|
||||
# Container Development Guide
|
||||
|
||||
This guide covers development workflow with the containerized setup.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker and Docker Compose installed
|
||||
- pnpm v9.12.3+
|
||||
- Node.js v20+
|
||||
|
||||
### Local Development
|
||||
|
||||
1. **Clone and setup**:
|
||||
```bash
|
||||
git clone https://github.com/deytenit/www.ermnvldmr.com.git
|
||||
cd www.ermnvldmr.com
|
||||
pnpm install
|
||||
```
|
||||
|
||||
2. **Start development servers** (non-containerized):
|
||||
```bash
|
||||
pnpm dev # Astro dev server on :4321
|
||||
pnpm storybook # Storybook dev server on :6006
|
||||
```
|
||||
|
||||
3. **Test containerized build**:
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
### Container Development Workflow
|
||||
|
||||
#### Building Images Locally
|
||||
|
||||
```bash
|
||||
# Build both images
|
||||
docker-compose build
|
||||
|
||||
# Build specific image
|
||||
docker-compose build astro
|
||||
docker-compose build storybook
|
||||
|
||||
# Build with no cache
|
||||
docker-compose build --no-cache
|
||||
```
|
||||
|
||||
#### Testing Container Changes
|
||||
|
||||
```bash
|
||||
# Start containers
|
||||
docker-compose up -d
|
||||
|
||||
# Check logs
|
||||
docker-compose logs -f astro
|
||||
docker-compose logs -f storybook
|
||||
|
||||
# Test health endpoints
|
||||
curl http://localhost:3000/health
|
||||
curl http://localhost:6006/health
|
||||
|
||||
# Stop containers
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Debugging Containers
|
||||
|
||||
#### Interactive Shell Access
|
||||
|
||||
```bash
|
||||
# Access running container
|
||||
docker-compose exec astro sh
|
||||
docker-compose exec storybook sh
|
||||
|
||||
# Or start debug container
|
||||
docker run -it --entrypoint=/bin/sh $(docker-compose config | grep image: | head -1 | cut -d: -f2-)
|
||||
```
|
||||
|
||||
#### Common Debug Commands
|
||||
|
||||
```bash
|
||||
# Inside container:
|
||||
whoami # Check user
|
||||
nginx -t # Test nginx config
|
||||
ls -la /usr/share/nginx/html/ # Check files
|
||||
ps aux # Check processes
|
||||
netstat -tlnp # Check ports
|
||||
```
|
||||
|
||||
## Docker Configuration
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
docker/
|
||||
├── astro/
|
||||
│ └── Dockerfile # Astro site container
|
||||
├── storybook/
|
||||
│ └── Dockerfile # Storybook container
|
||||
└── nginx/
|
||||
├── nginx.conf # Main nginx config
|
||||
├── astro.conf # Astro site config
|
||||
└── storybook.conf # Storybook config
|
||||
```
|
||||
|
||||
### Build Context
|
||||
|
||||
The `.dockerignore` file optimizes build context:
|
||||
- Excludes `node_modules/`, `dist/`, build artifacts
|
||||
- Includes source files, package files, configs
|
||||
- Reduces build time and image size
|
||||
|
||||
### Multi-stage Build Benefits
|
||||
|
||||
1. **Smaller Images**: Only runtime dependencies in final image
|
||||
2. **Security**: No build tools in production image
|
||||
3. **Caching**: Efficient layer caching for faster builds
|
||||
4. **Consistency**: Same build environment across all environments
|
||||
|
||||
## nginx Configuration
|
||||
|
||||
### Security Features
|
||||
|
||||
- Non-root user execution
|
||||
- Security headers (HSTS, CSP, etc.)
|
||||
- Rate limiting capabilities
|
||||
- Access controls for sensitive paths
|
||||
|
||||
### Performance Optimizations
|
||||
|
||||
- Gzip compression for assets
|
||||
- Caching headers for static files
|
||||
- Connection keep-alive
|
||||
- Efficient static file serving
|
||||
|
||||
### Configuration Customization
|
||||
|
||||
Edit files in `docker/nginx/`:
|
||||
- `nginx.conf`: Global nginx settings
|
||||
- `astro.conf`: Astro-specific server block
|
||||
- `storybook.conf`: Storybook-specific server block
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions Workflow
|
||||
|
||||
The workflows are triggered by semantic versioning:
|
||||
|
||||
1. **Development**: Push to feature branches
|
||||
2. **Pre-release**: Merge to `next` branch
|
||||
3. **Release**: Manual dispatch or release branch
|
||||
|
||||
### Build Process
|
||||
|
||||
1. Install dependencies with pnpm
|
||||
2. Build Astro static site
|
||||
3. Build Storybook static site
|
||||
4. Create semantic release
|
||||
5. Build and push Docker images (if release created)
|
||||
|
||||
### Image Tagging
|
||||
|
||||
Images follow semantic versioning:
|
||||
- Release: `latest`, `v1.2.3`, `v1.2`, `v1`
|
||||
- Pre-release: `latest-next`, `v1.2.3-next.1`, etc.
|
||||
|
||||
## Testing
|
||||
|
||||
### Container Testing
|
||||
|
||||
```bash
|
||||
# Test image builds
|
||||
docker build -f docker/astro/Dockerfile -t test-astro .
|
||||
docker build -f docker/storybook/Dockerfile -t test-storybook .
|
||||
|
||||
# Test containers run
|
||||
docker run -p 3000:8080 test-astro &
|
||||
docker run -p 6006:8080 test-storybook &
|
||||
|
||||
# Test endpoints
|
||||
curl -f http://localhost:3000/
|
||||
curl -f http://localhost:6006/
|
||||
curl -f http://localhost:3000/health
|
||||
curl -f http://localhost:6006/health
|
||||
|
||||
# Cleanup
|
||||
docker stop $(docker ps -q)
|
||||
```
|
||||
|
||||
### Security Testing
|
||||
|
||||
```bash
|
||||
# Scan images for vulnerabilities
|
||||
docker scout cves test-astro
|
||||
docker scout cves test-storybook
|
||||
|
||||
# Check for non-root execution
|
||||
docker run --rm test-astro whoami # Should output: nginx-user
|
||||
|
||||
# Test file permissions
|
||||
docker run --rm test-astro ls -la /usr/share/nginx/html/
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Image Size Optimization
|
||||
|
||||
Current optimizations:
|
||||
- Multi-stage builds
|
||||
- Alpine Linux base images
|
||||
- Minimal runtime dependencies
|
||||
- Optimized layer ordering
|
||||
|
||||
### Build Performance
|
||||
|
||||
- Layer caching in GitHub Actions
|
||||
- Efficient `.dockerignore`
|
||||
- Parallel builds for multiple platforms
|
||||
- Registry cache utilization
|
||||
|
||||
### Runtime Performance
|
||||
|
||||
- nginx optimizations for static content
|
||||
- Gzip compression
|
||||
- Proper caching headers
|
||||
- Health check optimizations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Build Issues
|
||||
|
||||
1. **pnpm version mismatch**:
|
||||
```bash
|
||||
# Update Dockerfile pnpm version
|
||||
RUN corepack prepare pnpm@9.12.3 --activate
|
||||
```
|
||||
|
||||
2. **Permission issues**:
|
||||
```bash
|
||||
# Check user creation in Dockerfile
|
||||
RUN adduser -S -D -H -u 1001 nginx-user
|
||||
```
|
||||
|
||||
3. **Missing dependencies**:
|
||||
```bash
|
||||
# Ensure all deps in package.json
|
||||
pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
### Common Runtime Issues
|
||||
|
||||
1. **Container won't start**:
|
||||
```bash
|
||||
docker logs container-name
|
||||
# Check nginx config: nginx -t
|
||||
```
|
||||
|
||||
2. **404 errors**:
|
||||
```bash
|
||||
# Check file permissions and paths
|
||||
docker exec container ls -la /usr/share/nginx/html/
|
||||
```
|
||||
|
||||
3. **Health check failures**:
|
||||
```bash
|
||||
# Test health endpoint manually
|
||||
curl -v http://localhost:8080/health
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
|
||||
```bash
|
||||
# Monitor resource usage
|
||||
docker stats
|
||||
|
||||
# Check nginx access logs
|
||||
docker exec container tail -f /var/log/nginx/access.log
|
||||
|
||||
# Profile application
|
||||
docker exec container top
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
### Making Changes to Containers
|
||||
|
||||
1. Modify Docker configurations
|
||||
2. Test locally with `docker-compose up --build`
|
||||
3. Validate with security and performance tests
|
||||
4. Update documentation
|
||||
5. Create pull request
|
||||
|
||||
### Release Process
|
||||
|
||||
1. Changes merged to `next` create pre-releases
|
||||
2. Manual workflow dispatch creates stable releases
|
||||
3. Both trigger automated container builds
|
||||
4. Images are automatically tagged and pushed
|
||||
|
||||
---
|
||||
|
||||
For more detailed deployment information, see [container-deployment.md](./container-deployment.md).
|
||||
Reference in new issue
Block a user