Published

- 5 min read

From Docker Compose to Score: A Platform Engineering Guide

img of From Docker Compose to Score: A Platform Engineering Guide

Welcome! You’ve probably seen Platform Engineering everywhere, along with terms like Internal Developer Platform (IDP), Internal Developer Portal, and the shift towards graph-based backends instead of traditional pipelines. It feels like discovering design patterns for the first time or hearing about a new frontend framework—you’re eager to dive in. But where should you begin?

Here’s the context I’m coming from:

  • All applications are containerized and deployed to a Kubernetes cluster.
  • Developers use Docker Compose or Helm (with intercept tools) for local development. Some require a container registry, while others build images locally.
  • Applications are deployed via pipelines or a GitOps approach.
  • Infrastructure is managed using Terraform.

With a focus on self-service and an outstanding developer experience (DevEx), I decided to explore the Score specification, aiming to standardize a platform-agnostic specification for developers to use across environments. In future posts, I plan to walk through each layer of the stack, providing a complete platform overview.

This post will guide you on transitioning from Docker Compose to the Score Specification. Once transitioned, Score can generate deployment modes in Docker Compose or other supported options.

What is Score?

Score is a developer-centric, platform-agnostic specification (YAML file) for managing workloads. It ensures consistent configuration across local and remote environments. In Score, you define workloads (which can be mapped to Kubernetes Pods) and other key definitions like containers, services, and resources.

Score supports multiple implementations (CLI tools) that generate platform-specific configuration files, such as Kubernetes or Docker Compose. You can define a Score file and then choose the platform you want to deploy it on. Since we are focusing on Docker Compose, the relevant implementation is score-compose.

For a deeper understanding, the score documentation site provides comprehensive details.

Our Example application

The application we’ll use follows a basic architecture: frontend, backend, and database — known as box-box-cylinder (pictured when you see the architecture diagram). The technologies used are Next.js for the frontend, .NET for the backend, and Microsoft SQL Server for the database. See the source code. Note that this is not production-ready; it’s just a sample for demonstrating Score.

Docker Compose File

The Docker Compose file is straightforward for the three components, but here are a couple of key aspects to note:

  • Build: Since this is for local development, the images can be rebuilt after changes, eliminating the need for reliance on a container registry.
  • Ports: Ports are exposed to the host, allowing developers to connect directly to the database for debugging purposes, for instance.
   name: manually-created
services:
  backend:
    build:
      context: ./backend
    environment:
      ASPNETCORE_ENVIRONMENT: UAT
    depends_on:
      - db
    ports:
      - target: 8080
        published: '8082'

  frontend:
    build:
      context: ./frontend
    environment:
      ENVIRONMENT: UAT
    depends_on:
      - backend
    ports:
      - target: 3000
        published: '3233'

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    env_file:
      - ./backend/.env
    ports:
      - target: 1433
        published: '1433'

Defining the Score Specification

For our application, we decided to define the following files for managing our workloads:

  • score-frontend.yaml
  • score-backend.yaml
  • score-database.yaml

Score Frontend File

   apiVersion: score.dev/v1b1

metadata:
  name: frontend

service:
  ports:
    frontend:
      port: 3233
      targetPort: 3000

containers:
  frontend:
    image: ./frontend
    variables:
      ENVIRONMENT: UAT

Score Backend File

   apiVersion: score.dev/v1b1

metadata:
  name: backend

service:
  ports:
    backend:
      port: 8082
      targetPort: 8080

containers:
  backend:
    image: ./backend
    variables:
      ASPNETCORE_ENVIRONMENT: UAT

Score Database File

   apiVersion: score.dev/v1b1

metadata:
  name: database

service:
  ports:
    database:
      port: 1433
      targetPort: 1433

containers:
  database:
    image: mcr.microsoft.com/mssql/server:2022-latest
    variables:
      ENVIRONMENT: UAT

Generate Docker Compose file with score-compose

You would need to follow the instructions to install score-compose first and assuming Docker is already installed.

Initialize

Once installed, initialize score-compose:

   score-compose init --no-sample

The flag ‘—no-sample’ ensures init does not create a default score.yaml file as we have our files defined already.

Generate

Run generate on for each workload:

   score-compose generate score-frontend.yaml --build=frontend=./aspnetcore -o score-compose.yaml
score-compose generate score-backend.yaml --build=backend=./backend -o score-compose.yaml
score-compose generate score-database.yaml -o score-compose.yaml

It’s important to note that the generate command is accumulative, meaning each run will add to the existing generated state. Multiple runs are currently necessary because the —build parameter does not yet support workload context (though this feature is coming soon).

Here’s a breakdown of the parameters:

  • —build: Specifies an optional build context for the container. The format is either —build=container=./dir or —build=container={“context”:“./dir”}. This is especially useful for development as it allows the image to be built when running docker compose up, unlike the database, where we use an existing image.
  • -o: Specifies the output file for the generated Docker Compose file. By default, this is compose.yaml.

We run one last generate to expose ports to the host:

   score-compose generate score-database.yaml --publish 8002:backend:8002 --publish 5348:frontend:5348 --publish 1433:database:1433 -o score-compose.yaml

Here’s a deeper look into the parameter:

• —publish: This flag makes the defined ports accessible on the Docker host. It follows the format HOST_PORT:/:CONTAINER_PORT, where the container port is the port inside the container that you want to map to the host. This is useful for making services in your workloads accessible from outside the container environment.

Generated Docker-Compose file

The resultant file looks mostly like the one that was manually created:

   name: src
services:
  backend-backend:
    annotations:
      compose.score.dev/workload-name: backend
    build:
      context: ./backend
    environment:
      ENVIRONMENT: UAT
    hostname: backend
    ports:
      - target: 8002
        published: '8002'
  database-database:
    annotations:
      compose.score.dev/workload-name: database
    environment:
      ENVIRONMENT: UAT
    hostname: database
    image: mcr.microsoft.com/mssql/server:2022-latest
    ports:
      - target: 1433
        published: '1433'
  frontend-frontend:
    annotations:
      compose.score.dev/workload-name: frontend
    environment:
      ENVIRONMENT: UAT
    hostname: frontend
    image: ./frontend
    ports:
      - target: 5348
        published: '5348'

The naming convention with the services is ‘workloadName-containerName’ so you could adjust it if you dont like this convention.

Docker-compose up

To get up and running, we execute docker-compose up specifying our generated file:

   docker-compose -f score-compose.yaml up

Your application should now be running as expected. The only aspect I haven’t covered yet is the depends_on property, but I will update this as soon as I explore it further.

Conclusion

Just like learning the Docker Compose specification, the Score specification is straightforward and easy to adopt. We’ve walked through a simple example application and created Score files to define our workloads. You could even install a different implementation, such as score-k8s, and generate manifest files to deploy to a Kubernetes cluster. I hope this introduction provided valuable insights into the possibilities. Would you consider transitioning to Score?

Related Posts

There are no related posts yet. 😢