Docker Swarm Configuration for InkBlink
Requirements
| Tailscale Download (MAC/PC) | Download | Tailscale |
| Docker Swarm (PC) | Windows | Docker Docs |
Initializing Docker Swarm for Inkblink
All machines that run Inkblink must be in the same Tailscale network.
WINDOWS : If docker swarm cannot be initialized using the Tailscale IP address, run a separate Tailscale image using docker. See Run Tailscale Using Docker
# Print Tailscale IP
tailscale ip -4
# if running Tailscale on a separate image
# When Tailscale is running:
docker exec -it <tailscale-container-name> tailscale ip -4
Once you have found out what the Tailscale IP address for your machine is, initialize a docker swarm using that address
# Initialize Docker Swarm
docker swarm init --advertise-addr <YOUR_TAILSCALE_IP> --listen-addr 0.0.0.0:2377
To verify the swarm has been initialized successfully:
$ netstat -an | grep 2377
TCP [::1]:2377 [::]:0 LISTENING
A swarm has been initialized. For other nodes to join the swarm, each node must copy and paste the swarm token in their respective terminals. The token can be retrieved by the manager of the swarm using :
# The swarm manager can view nodes and assign nodes tasks
docker swarm join-token worker|manager
Expected Output:
$ docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-0xuent3dqa4feid2p8tfmtbncsbr7idxq1g682jdiiq9bi71ex3-boafyko6qug1khac6mi38uc17 <YOUR_TAILSCALE_IP>
To view joined nodes to your swarm, input:
# Run
docker node ls
# Expected output
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
y5eyvalhfj1umrewfk1eqiqni * docker-desktop Ready Active Leader 27.5.1
Using the listed IDs, the docker swarm manager can now assign roles to each node.
Assigning Roles to Nodes
To assign roles to a node, the service that you want to assign must have a label in compose.yaml. A typical compose.yaml file may look like this:
primary_server:
image: server
build:
context: ./backend
hostname: primary_server
ports:
- "8887:8887"
- "5001:5001"
- "5002:5002"
- "5003:5003"
environment:
TAILSCALE_IP: ${PRIMARY_SERVER_IP}
KAFKA_BOOTSTRAP_SERVERS: ${KAFKA_IP}:9092
BACKUP_SERVER_1_IP: ${BACKUP_SERVER_1_IP}
BACKUP_SERVER_2_IP: ${BACKUP_SERVER_2_IP}
BACKUP_SERVER_3_IP: ${BACKUP_SERVER_3_IP}
PRIMARY_SERVER_IP: ${PRIMARY_SERVER_IP}
COORDINATOR_IP: ${COORDINATOR_IP}
command: >
sh -c "/wait-for-backups.sh && mvn exec:java -Dexec.mainClass='com.server.WebServer' -Dexec.args='8887 5001 ${BACKUP_SERVER_1_IP}:6001,${BACKUP_SERVER_2_IP}:7001,${BACKUP_SERVER_3_IP}:4001 true'"
networks:
- my_network
deploy:
placement:
constraints:
- node.labels.role == main_services
# Under deploy/placement/constraints, you can add a label to the service to assign later to a node
Once you have added labels to each service, you can now update each node's role:
# Assigning a role to a node
# Only a swarm manager can assign roles to nodes
docker node update --label-add role=<LABEL_NAME> <NODE_ID>
Preliminary steps to initialize the service is now complete.
Building Docker Image for Inkblink
All nodes that will run the service must each have the same source files as the manager. Ensure that the source files are up to date with the manager and build the images using:
docker compose build
The worker node does not run the images independently. Once the swarm initiates the services, the workers will run the respective images automatically.
Deploying Containers using Docker Swarm
The compose.yaml utilizes several variables that are imported from .env located in the same path as the compose file. The .env file must be updated before building and deploying. The .env file may look something like this:
# Replace with corresponding tailscale IPs
KAFKA_IP=100.83.215.115
PRIMARY_SERVER_IP=100.83.215.115
BACKUP_SERVER_1_IP=100.83.215.115
BACKUP_SERVER_2_IP=100.83.215.115
BACKUP_SERVER_3_IP=100.83.215.115
COORDINATOR_IP=100.83.215.115
Update the values of these variables manually to each node's respective Tailscale IP address.
The compose.yaml file does not automatically import these vars when building! Run the following to create a new compose.yaml file
# Export vars in .env to local terminal
export $(grep -v '^#' .env | xargs)
# Create another compose file that manager will use to deploy
envsubst < compose.yaml > rendered_compose.yaml
The created rendered_compose.yaml file will be used to deploy the containers to the swarm.
The swarm manager can now build the containers using:
# Build using new compose file
docker compose -f rendered_compose.yaml build
To now deploy the service:
# Deploy containers to swarm
docker stack deploy -c rendered-compose.yaml inkblink
Confirm that services are running using either Docker Desktop or the terminal:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a2130cb26fb client:latest "docker-entrypoint.s…" About an hour ago Up About an hour inkblink_client_1.1.u9sic4vg0y0ecuynpykhvnvx9
4246003ed693 server:latest "mvn exec:java -Dexe…" About an hour ago Up About an hour 8887/tcp inkblink_backup_server_3.1.j7orcp0g7bgnzowew2d2pudm6
5a37d51f4152 server:latest "mvn exec:java -Dexe…" About an hour ago Up About an hour 8887/tcp inkblink_backup_server_2.1.v3oyyx2phblj1v1rnnkebvi3f
9de19e542fd2 server:latest "mvn exec:java -Dexe…" About an hour ago Up About an hour 8887/tcp inkblink_backup_server_1.1.qg62zpihy890nqlnl0mkmh73o
28eadabc00e6 server:latest "sh -c '/wait-for-ba…" About an hour ago Up About an hour (Paused) 8887/tcp inkblink_primary_server.1.wyla6fgxpjhovzbriygfefs0v
af844889dc73 connection_coordinator:latest "mvn exec:java -Dexe…" About an hour ago Up About an hour 8887/tcp inkblink_connection_coordinator.1.y8voelddpfqjgll2t7a0koyrt
97285295c564 apache/kafka:latest "/__cacert_entrypoin…" About an hour ago Up About an hour 9092/tcp inkblink_kafka.1.vv48duzjxu7cx6snpcxperut5
4029d93bd800 tailscale/tailscale "tailscaled" 23 hours ago Up 2 hours tailscale-proxy
WINDOWS : Running Tailscale as a Docker Instance
Create a new compose file, tailscale-compose.yaml with the following contents
services:
tailscale-proxy:
container_name: tailscale-proxy
image: tailscale/tailscale
network_mode: "host"
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- TS_STATE_DIR=/var/lib/tailscale
- TS_EXTRA_ARGS=--hostname=${HOSTNAME:-proxy-node}
volumes:
- tailscale-state:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
restart: unless-stopped
command: tailscaled
volumes:
tailscale-state:
Run the following
docker compose -f tailscale-compose.yaml build
# Run the tailscale image in the background
docker compose up -d
The image above will be running without being previously authenticated to join a Tailscale network instance. To join a network, you must be logged in. To do so, run :
# Activate tailscale
# This runs the command `tailscale up` in the container tailscale-proxy
docker exec -it tailscale-proxy tailscale up
You'll be prompted to a link which you can copy and paste in your local browser to log in.