mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2025-12-06 13:54:41 +08:00
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 🤖 Generated with [Claude Code](https://claude.com/claude-code) 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>
This commit is contained in:
committed by
tangmengqiu
parent
11e4022867
commit
b66fd5fb0a
152
.github/workflows/pr-docker-compose-healthcheck.yml
vendored
Normal file
152
.github/workflows/pr-docker-compose-healthcheck.yml
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
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
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
networks:
|
||||
- nofx-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
depends_on:
|
||||
- nofx
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1/health"]
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
Reference in New Issue
Block a user