The idea
Tools for collaborative work or pair programming are more important than ever. In addition to these tools, video chat systems are often used for communication. These offer many advantages, but also have the big disadvantage of being very bandwidth intensive. Often, a voice chat connection is sufficient for the task. A well known solution (at least for gamers) is Mumble. It a free, open source voice communication system. It is well known for its high quality and low latency. My goal was to set up a mumble server in our cloud infrastructure as a supplement to existing communication channels In this article I am giving a detailed manual based on my experience.
The Cloud
To manage our applications in our cloud infrastructure we decided to use a stack based on HashiCorp Tools and several well know DevOps tools like Terraform, Consul, Vault, Nomad and Ansible. Last but not least we use Traefik as our application proxy. We are very happy with that setup as it makes the management of our container based applications a breeze.
Step 1: Mumble server in a docker container
I used the github project PHLAK/docker-mumble which sets up the mumble server, creates the necessary directory and users and builds a ready-to-use docker image for our mumble server.
Step 2: Build automation with Gitlab CI/CD
In the buildApp stage the image is built in a gitlab ci/cd pipeline and pushed to the gitlab container registry.
buildApp:
stage: build
before_script:
# login to the gitlab docker registry
- /usr/local/bin/dockerd &
- docker -H unix:///var/run/docker.sock login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
image: docker:dind
only:
- master
script:
# Create a docker image
- docker -H unix:///var/run/docker.sock build -t git.yourHost.com:yourPort/mumble:$CI_COMMIT_SHA .
# Push the image to the gitlab registry
- docker -H unix:///var/run/docker.sock push git.yourHost.com:yourPort/mumble:$CI_COMMIT_SHA
tags:
- "docker"
In the deployApp stage I use our bastion host to create the mumble.nomad file. This file is then used to start a nomad job and deploy a mumble docker container into our nomad cluster.
deployApp:
stage: deploy
script:
# Create the nomad group name
- export NOMAD_GROUP="mumbleGroup"
# Create the project name used in the nomad template
- export NOMAD_PROJECT_NAME="mumble"
# Use the nomad template to create a nomad file
- ./nomad-template.sh > mumble.nomad
# Run the nomad job
- nomad job run mumble.nomad
only:
- master
tags:
- 'bastion'
Step 3: Creating a nomad job specification
The nomad-template.sh script from the deployApp stage is helping me to create the job specification.
First I define the data center where it should be deployed and the type of the job.
datacenters = ["dc1"]
type = "service"
Next I provide a definition on how to perform updates.
update {
max_parallel = 1
canary = 1
min_healthy_time = "30s"
healthy_deadline = "9m"
auto_revert = true
auto_promote = true
}
Followed by the configuration for the group and the task. The task basically represents the docker run command. I define which image should be used and which port should be exposed.
group "$NOMAD_GROUP" {
count = 1
# the task contains all steps needed to start a new service
task "$NOMAD_PROJECT_NAME" {
# create a docker container with the given image
driver = "docker"
config {
image = "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
force_pull = true
auth {
username = "$CI_REGISTRY_USER"
password = "$CI_REGISTRY_PASSWORD"
}
# exposed ports of this container (using the map we can define a name for the port to reuse later)
port_map {
app = 64738
}
}
Next I am going to register our container in consul and add it to traffic to be accessible from outside our network.
service {
# port to use for consul
port = "app"
# config for traefik
tags = [
"traefik.enable=true",
"traefik.tags=service",
"traefik.tcp.routers.$NOMAD_PROJECT_NAME-tcp.rule=HostSNI(\`$NOMAD_PROJECT_NAME.yourDomain.com\`)",
"traefik.tcp.routers.$NOMAD_PROJECT_NAME-tcp.entrypoints=voicechat-tcp",
"traefik.tcp.routers.$NOMAD_PROJECT_NAME-tcp.tls.passthrough=true",
"traefik.udp.routers.$NOMAD_PROJECT_NAME-udp.entrypoints=voicechat-udp",
]
}
Finally, I allocate resources for the container and set up the restart params of the task in case of failure.
resources {
cpu = 500 # MHz
memory = 1024 # MB
network {
mbits = 100
port "app" {}
}
}
restart {
interval = "30m"
attempts = 2
delay = "15s"
mode = "fail"
}
Step 4: Add the tcp and udp entry point to traefik
Mumble uses tcp and udp to transfer data between the server and the client. I added the new entry points (voicechat-tcp and voicechat-udp) and the ports to the docker-compose configuration of traefik.
command:
- "--entrypoints.voicechat-tcp.address=:64738"
- "--entrypoints.voicechat-udp.address=:64738/udp"
ports:
- "64738:64738"
- "64738:64738/udp"
Conclusion
After these four steps I am running an auto deployd mumble-server in our cloud. This method works for all kind of apps and shows the beauty and elegance of cloud based infrastructure.