Deploy a multi-container test and production backend using Docker Compose health checks and automated test runs.
Domain
Container Security & Orchestration
Difficulty
⭐⭐⭐⭐☆ (Advanced)
Course Module
Deployment & DevOps for IoT
Deliverables
Multi-container Docker Compose Config
1. Docker Compose Dependency & Health Check Architecture
If integration tests start before the target backend API is ready to accept connections, the test runner will fail. The diagram below illustrates how Docker Compose coordinates container startup. The `backend` container starts first. Its health check loops every 5 seconds, querying the `/health` endpoint. Once the server returns a 200 OK status, the `test-runner` container starts, runs the test suite, and outputs the results.
2. Part 1: Step-by-Step Container Deployment Commands
Follow these detailed steps to build the test configuration files, define compose tasks, configure container health checks, and run the test suite.
STEP 1
Launch VM Terminal and Navigate to Project Workspace
Boot up your VirtualBox Ubuntu machine. Open the terminal (Ctrl+Alt+T) and change directories to the project folder.
ubuntu@iot-vm:~$ cd ~/workspace/backend_scaffold
We change the working directory of our shell to the backend workspace, where we will configure the Docker deployment files.
STEP 2
Create the Test Environment Dockerfile
Open a new file in gedit to write the Docker configuration for the test environment.
We open `docker-compose.yml` to configure the database, backend, and test-runner containers, setting up port routing and startup dependencies.
STEP 4
Start the Containerized Environment
Build the images and start the services using Docker Compose. The terminal will log the compilation and startup sequence.
ubuntu@iot-vm:~/workspace/backend_scaffold$ sudo docker-compose up --build --exit-code-from test-runner
We run `docker-compose up` with the `--exit-code-from` flag as root to start the services and exit Compose once the test-runner finishes executing the test suite.
STEP 5
Monitor Active Container Logs
Query the container logs in a new terminal window to verify that the Express server started successfully and the health check is active.
We run `docker logs` as root to view the console output of the backend container, verifying that the server started and is handling health-check requests.
3. Multi-Container Networking Model
The diagram below maps the network paths on the virtual Docker bridge network, illustrating how the test-runner container resolves and queries the backend service.
4. Part 2: Complete Docker Configurations
Below is the complete C++ firmware code showing how abstract inheritance interfaces work, followed by the complete Python script to listen to the serial port and print the data.
Asset 1: Test Image Blueprint Configuration (`Dockerfile.test`)
Line-by-Line Code Breakdown
FROM node:20-alpine: Loads the alpine Node base image.
COPY package*.json tsconfig.json jest.config.js ./: Copies package, compiler, and runner configurations into the test environment.
CMD ["npm", "run", "test"]: Defines the default test command to execute when the container starts.
# Dockerfile for Integration Test Runner ServiceFROMnode:20-alpineWORKDIR/appCOPYpackage*.json ./COPYtsconfig.json ./COPYjest.config.js ./RUNnpm installCOPY. .CMD ["npm", "run", "test"]
healthcheck: ...: Configures the backend container health check parameters.
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]: Defines the command to verify server status. The curl command exits with status code 0 on success and 1 on failure.
depends_on: ...: Configures startup dependencies for the test runner.
condition: service_healthy: Instructs Compose to wait until the backend health check passes before launching the test-runner container.
Build output logs showing both containers compiling and starting.
Compose console logs showing the test-runner container starting only after the backend passes the health check, executing the test suite, and exiting with status code 0.
6. Closing Explanation: Why We Did This & What It Accomplishes
Architectural Intent & Operational Impact
Why We Did This
Running integration tests in a separate container guarantees that tests run in an isolated environment, avoiding conflicts with local dev configurations.
Configuring service startup dependencies ensures that tests only run when the target API is healthy, preventing false test failures.
Exiting the Compose stack with the test-runner container's exit code makes it easy to integrate the test suite into automated CI/CD pipelines.
What This Accomplishes
Deploys a containerized testing pipeline in Docker Compose, validating your API endpoints automatically on startup.