Files
nofx/.github/workflows/pr-docker-compose-healthcheck.yml
T
0xYYBB | ZYY | Bobo 36fcad03c5 fix(docker): revert healthcheck to wget for Alpine compatibility (#986)
## Problem
PR #906 changed healthcheck commands from `wget` to `curl`, but Alpine Linux
(our base image) does not include `curl` by default, causing all containers
to fail healthchecks with:
```
exec: "curl": executable file not found in $PATH
```
This creates a configuration mismatch:
- docker/Dockerfile.backend uses `wget` ( works)
- docker-compose.yml uses `curl` ( fails)
## Root Cause Analysis
Alpine Linux philosophy is minimalism:
-  `wget` is pre-installed (part of busybox)
-  `curl` requires `apk add curl` (~3MB extra)
The original PR #906 made two commits:
1. First commit (3af8760): `curl` → `wget` ( correct fix)
2. Second commit (333b2ef): `wget` → `curl` ( introduced bug)
The second commit was based on incorrect assumption that "curl is more
widely available than wget in Docker images". This is false for Alpine.
## Solution
Revert healthcheck commands back to `wget` to match:
1. Alpine's pre-installed tools
2. Dockerfile.backend healthcheck (line 68)
3. Docker and Kubernetes best practices
## Testing
 Verified `wget` exists in Alpine containers:
```bash
docker run --rm alpine:latest which wget
# Output: /usr/bin/wget
```
 Added new CI workflow to prevent regression:
- `.github/workflows/pr-docker-compose-healthcheck.yml`
- Validates healthcheck compatibility with Alpine
- Ensures containers reach healthy status
## Impact
- **Before**: Containers show (unhealthy), potential auto-restart loops
- **After**: Containers show (healthy), monitoring systems happy
- **Scope**: Only affects docker-compose deployments
- **Breaking**: None - this is a bug fix
## References
- PR #906: Original (incorrect) fix
- Alpine Linux: https://alpinelinux.org/about/
- Dockerfile.backend L68: Existing `wget` healthcheck
Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com>
Co-authored-by: tinkle-community <tinklefund@gmail.com>
Co-authored-by: Shui <88711385+hzb1115@users.noreply.github.com>
2025-11-15 22:21:11 -05:00

153 lines
4.9 KiB
YAML

name: PR Docker Compose Healthcheck
# 驗證 docker-compose.yml 的 healthcheck 配置在 Alpine 容器中正常工作
on:
pull_request:
branches:
- main
- dev
paths:
- 'docker-compose.yml'
- 'docker/Dockerfile.backend'
- 'docker/Dockerfile.frontend'
- '.github/workflows/pr-docker-compose-healthcheck.yml'
jobs:
healthcheck-test:
name: Test Docker Compose Healthcheck
runs-on: ubuntu-22.04
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create minimal .env for testing
run: |
cat > .env <<EOF
# Minimal config for healthcheck testing
NOFX_BACKEND_PORT=8080
NOFX_FRONTEND_PORT=3000
NOFX_TIMEZONE=UTC
DATA_ENCRYPTION_KEY=test-key-32-chars-minimum-length
JWT_SECRET=test-jwt-secret-minimum-32-chars
EOF
- name: Create minimal config.json
run: |
cat > config.json <<EOF
{
"ai_models": [],
"exchanges": [],
"traders": []
}
EOF
- name: Start services with docker compose
run: |
docker compose up -d
echo "✅ Services started, waiting for healthcheck..."
- name: Wait for healthcheck start_period
run: |
echo "⏳ Waiting 70 seconds for healthcheck start_period to complete..."
sleep 70
- name: Verify backend healthcheck
run: |
echo "🔍 Checking backend container health..."
BACKEND_HEALTH=$(docker inspect nofx-trading --format='{{.State.Health.Status}}')
echo "Backend health status: $BACKEND_HEALTH"
if [ "$BACKEND_HEALTH" != "healthy" ]; then
echo "❌ Backend container is not healthy!"
echo "Health status: $BACKEND_HEALTH"
echo ""
echo "Health logs:"
docker inspect nofx-trading --format='{{json .State.Health}}' | jq
echo ""
echo "Container logs:"
docker logs nofx-trading
exit 1
fi
echo "✅ Backend container is healthy"
- name: Verify frontend healthcheck
run: |
echo "🔍 Checking frontend container health..."
FRONTEND_HEALTH=$(docker inspect nofx-frontend --format='{{.State.Health.Status}}')
echo "Frontend health status: $FRONTEND_HEALTH"
if [ "$FRONTEND_HEALTH" != "healthy" ]; then
echo "❌ Frontend container is not healthy!"
echo "Health status: $FRONTEND_HEALTH"
echo ""
echo "Health logs:"
docker inspect nofx-frontend --format='{{json .State.Health}}' | jq
echo ""
echo "Container logs:"
docker logs nofx-frontend
exit 1
fi
echo "✅ Frontend container is healthy"
- name: Verify healthcheck commands are Alpine-compatible
run: |
echo "🔍 Verifying healthcheck commands use Alpine-compatible tools..."
# Check that docker-compose.yml uses wget (not curl)
if grep -q 'test:.*curl' docker-compose.yml; then
echo "❌ ERROR: docker-compose.yml uses 'curl' which doesn't exist in Alpine!"
echo ""
echo "Alpine Linux (used by our containers) includes 'wget' but not 'curl'."
echo "Please use 'wget --no-verbose --tries=1 --spider' instead."
exit 1
fi
if ! grep -q 'test:.*wget' docker-compose.yml; then
echo "⚠️ WARNING: No wget healthcheck found in docker-compose.yml"
else
echo "✅ Healthcheck uses Alpine-compatible 'wget' command"
fi
- name: Test healthcheck commands inside containers
run: |
echo "🧪 Testing healthcheck commands directly..."
# Test backend healthcheck command
echo "Testing backend healthcheck..."
docker exec nofx-trading wget --no-verbose --tries=1 --spider http://localhost:8080/api/health
echo "✅ Backend healthcheck command works"
# Test frontend healthcheck command
echo "Testing frontend healthcheck..."
docker exec nofx-frontend wget --no-verbose --tries=1 --spider http://127.0.0.1/health
echo "✅ Frontend healthcheck command works"
- name: Show container status
if: always()
run: |
echo "📊 Final container status:"
docker ps --format "table {{.Names}}\t{{.Status}}"
- name: Show logs on failure
if: failure()
run: |
echo "📋 Backend logs:"
docker logs nofx-trading
echo ""
echo "📋 Frontend logs:"
docker logs nofx-frontend
- name: Cleanup
if: always()
run: |
docker compose down -v
rm -f .env config.json