Many Small Apps

See what we're up to: Runway is here! 🚀

Many Small Apps #

You have a lot of applications and they don't fit all on one node.

Setup #

Let’s say we have three nodes:

  • node001
  • node002
  • node003

And a lot of app stacks, each containing two stateless services: http and api.

And the api services in each app depend on a stateful shared service (shared-db), which lives in its own stack.

Stateful Services #

Stateful services are services where persistence is required. In our example we are using a common stateful service: a (MySQL) database.

Services that need persistence use volumes (only shared-db in this example) and need to either

  • be pinned to a node (which is what we’ll be doing here)
  • or be written in such a way that they form a cluster (for redundancy) if they start on more than one node (which is possible for example with minio).

If each one of your app stacks also uses volumes, then you’d need to pin them all to one of your nodes. That works but then you’re basically doing manually what Docker Swarm would do automatically.

Try to be stateless: * centralize data storage into a database, or an S3-compatible service like minio * add build artifacts directly into the images (for example, copy your frontend files into the webserver image, and don't serve that from a volume)

Pinning a service to a node is accomplished as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
version: "3.8"
services:
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: example
    networks:
      - db
    volumes:
      - data:/data
    deploy:
      placement:
        constraints: 
          - node.hostname == node001
networks:
  db:
    external: true
    name: db
volumes:
  data:

Stateless Services #

For stateless services (each webserver and api service), we can let Docker Swarm distribute them as it sees fit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: "3.8"
services:
  http:
    image: registry.example.com/my/http
    networks:
      - internal
    deploy:
      replicas: 1
      placement:
        max_replicas_per_node: 1
      resources:
        limits:
          cpus: '0.50'
  api:
    image: registry.example.com/my/api
    networks:
      - internal
      - db
    deploy:
      replicas: 1
      placement:
        max_replicas_per_node: 1
      resources:
        reservations:
          memory: 512M
networks:
  internal:
  db:
    external: true
    name: db

In detail:

  • replicas: 1 - we only want to start one of each service (that’s also the default)

  • max_replicas_per_node: 1 - and only one per node. This only comes into play if we increase replicas at some point, but it doesn’t hurt to keep in there from the start.

  • resource limit cpu: 0.5 - we limit each http service to half a CPU core so that traffic spikes in one app don’t affect all the other apps too much.

  • resource reservation memory: 512M - for the api services, we reserve some memory. This has the effect that Docker Swarm won’t start any more services if there isn’t at least another 512MB available on a particular node.

    If none of the nodes have enough RAM available, the deployment of new apps will fail - which lets us know that resources are getting scarce.