Test a Server with Docker Compose on Github Actions

Every year, Atomic Object hosts the Atomic Games — a hackathon where college students develop game agents for two-player games, ranging from real-time strategy (RTS) to classic board games. The competitive environment challenges participants to create bots that interact with both the game server and their opponents’ bots. The runtime of each game involves three key processes: Player 1’s bot, Player 2’s bot, and the game server.

To enhance the development experience and facilitate rapid testing and iteration of these game agents, implementing a robust CI/CD pipeline is essential. Here, I’ll walk you through setting up a GitHub Actions workflow that automates building your bot, running the server and both bots using Docker Compose, and publishing the game results as pipeline outcomes.

Why Automate with GitHub Actions and Docker Compose?

Manual testing can be time-consuming and error-prone, especially when dealing with multiple components like game servers and bots. Automating this process ensures consistency, saves time, and provides immediate feedback on the performance and compatibility of your bots within the game environment. An advantage of using Docker Compose is that it will allow you to use the same process to test locally as used in Github. If you opt not to use Docker Compose, you may want to look at Act for testing Github Action workflows locally.

Step-by-Step Guide to Setting Up the Workflow

1. Repository Setup

Ensure your project repository is structured to include separate directories for the server and each bot. For example:

/project-root
│
├── server/
└── sdks/
    │
    ├── ruby/
    └── python/

2. Create a Docker Compose File

Docker Compose simplifies the orchestration of multiple containers. Create a docker-compose.yml file at the root of your repository:

version: '3.8'

version: '3.8'
services:
  p1:
    build:
      context: sdks/ruby
      dockerfile: Dockerfile
    networks:
      - game-network

  p2:
    build:
      context: sdks/ruby
      dockerfile: Dockerfile
    depends_on:
      - p1
    networks:
      - game-network

  server:
    build:
      context: server
      dockerfile: Dockerfile
    ports:
      - "9090:9090"
    depends_on:
      - p2
    networks:
      - game-network

networks:
  game-network:
    driver: bridge

This configuration builds and runs the server and both bots as separate services.

3. Define the GitHub Actions Workflow

Create a workflow file at .github/workflows/ci.yml:

name: CI Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Build Docker Compose Services
        run: docker compose build

      - name: Run Docker Compose
        run: docker compose up server

      - name: Collect Test Results
        uses: actions/upload-artifact@v3
        with:
          name: game-results
          path: ./server/results/

      - name: Tear Down Docker Compose
        if: always()
        run: docker compose down

4. Building the Bots and Server

Ensure each service (server, bot1, bot2) has a corresponding Dockerfile that defines how to build the image. For example, in sdks/python/Dockerfile:

FROM python:3

WORKDIR /usr/src/app

COPY . .

EXPOSE 9090

CMD [ "python", "./client.py", "9090" ]

Repeat similar setups for the server and bot2. Important note, while using the Docker Compose network, the server will refer to the players base IP:Port based on the service name, i.e.:

CMD [ "python", "./server.py", "9090" "-p1ip", "p1", "-p1port", "9090", "-p2ip", "p2", "-p2port", "9090" ]

This example is particularly simple, but if you start producing more complicated Docker images spending time optimizing Docker Layers may benefit your application.

5. Running the Server and Bots

The docker compose up server command starts all services because the server depends on p2, and p2 depends on p1. The server is coded to terminate with the outcome of the game as a result.

6. Publishing Results

The actions/upload-artifact step uploads the game results, making them accessible via the GitHub Actions interface.

7. Cleaning Up

The docker compose down command stops and removes all containers, networks, and volumes created by docker compose up. Using if: always() ensures that cleanup occurs regardless of the previous steps’ success or failure.

Integrating GitHub Actions with Docker Compose

Integrating GitHub Actions with Docker Compose provides a streamlined and automated approach to developing, testing, and iterating on game agents for Atomic Games. This setup not only enhances efficiency but also ensures that your bots are consistently evaluated in a controlled environment, leading to more robust and competitive entries in the hackathon. Implementing such workflows empowers participants to focus on refining their strategies and improving their bots’ performance, ultimately contributing to the success of Atomic Games.

Conversation

Join the conversation

Your email address will not be published. Required fields are marked *