In this post I'm going to analyze Docker role in each stage of application lifecycle, and try to highlight cases when you should consider moving to Swarm. It's not a getting started tutorial, but I'll create one in future.
Development with Docker
Docker really made my life much easier. Let's say, I need MySQL. Or Ghost (my blog is happily running on Ghost in docker container). Or Redis, postgreSQL, Ruby - it's all already containerized, one
docker run name_of_program_you_need line away. No more mess in my local workstation - download, use, throw away. I can easily extend existing containers, and I already know Docker good enough to quickly tell if image found in internet is rubbish. Etc, etc... since you're reading this post, I probably don't need to convince you that Docker is a really great piece of software. For development, you may add.
If you are working with Docker, you are probably using Docker-compose for bringing up your whole development stack together. It can look like this:
version: '2' services: web: build: . command: npm run dev ports: - 8080:80 redis: image: redis database: image: postgres
and run with
docker-compose up # --build if you want to rebuild
Database is then reachable as
postgresql://database, redis as
http://redis. No more hardcoded addresses.
It's a very convenient way of storing your development stack as a code, inside version control system. But what about production? It can't be so easy, right?
Production is a very different beast. Let's write down requirements for a typical medium traffic server.
- Availability - it just have to be working all the time, with as little downtimes as possible
- Performance - our server needs to handle traffic, so performance is important
- Easy deployment & rollback
- Gathering logs & metrics
- Load balancing - if something fails, we want our site to function properly
Docker, by many, is currently underestimated as a production-ready solution. I've decided to give it a shot, and almost year ago I containerized everything under the hood of PvP Center, product that I'm creating. There were some fails, related to docker filesystem (now I'm using
overlay2 and everything works OK), but in overall I think it was a very good decision.
Production deployment using raw Docker or Docker-compose
I've been there. I had Ansible configuration for downloading a new version of application and deploying every container (here you can find post about my base ansible configuration). It was working as desired. Let's take a look at our list:
- Performance - Docker process is a normal kernel process, no noticeable overhead
- Easy deployment - one button and it's happening. It took some time, because Ansible was checking multiple conditions, not only what version of container was deployed (probably it could be fixed)
- Rollbacks - yes! I was storing every image of my application inside Docker registry, with separate tag. Rolling back was really easy, as I was doing backward-compatible database migrations
But it had some problems:
- Can't be scaled to multiple hosts without effort of managing dynamic load balancer, which is non-trivial (Due to our need of 0-downtime deployment)
- Tricky to figure out how to integrate it with CI / CD system
- I was storing application-specific deployment requirements in separate architecture repository. When configuration changed, rollback was much harder.
I've sticked to it for some time. It was OK, but I had a feeling that I'm missing something. Too many hacks for 0d deployment, not-so-DRY configuration, Ansible deployment started too irritate me (slow).
But real reason why I decided to move to Docker Swarm, was scaling beyond one host. Potentially I COULD deploy application in the same way to multiple hosts and use external load balancer, but dynamically adding and removing them would quickly become a pain. I wanted to remove application specific configuration from Ansible, and keep it in my application repository.
It's a quite new addition to Docker (from version 1.12). It's designed to easily manage container scheduling over multiple hosts, using Docker CLI.
- Main point: It allows to connect multiple hosts with Docker together.
- It's relatively simple. Compared with Kubernetes, starting with Docker Swarm is really easy.
- High availability - there are two node types in cluster: master and worker. One of masters is the leader. If current leader fails, other master will become leader. If worker host fails, all containers will be rescheduled to other nodes.
- Declarative configuration. You tell what you want, how many replicas, and they'll be automatically scheduled with respect to given constraints.
- Rolling updates - Swarm stores configuration for containers. If you update configuration, containers are updated in batches, so service by default will be available all the time.
- Build-in Service discovery and Load balancing - similar to load balancing done by Docker-Compose. You can reference other services using their names, it doesn't matter where containers are stored, they will receive requests in a round-robin fashion
- Overlay network - if you expose port from service, it'll be available on any node in cluster. It really helps with external load balancing.
When to consider Docker Swarm
Here are 5 questions that you need to answer, before considering move to Docker Swarm:
- Do you need scaling beyond one host? It's always more complicated than single server, maybe you should just buy better one?
- Do you need high availability?
- Are your containers truly stateless? Swarm containers shouldn't use any volumes, theoretically it's possible, but was not reliable when I tested it. Consider moving to S3 with media files and keep your database out of Swarm.
- Do you have log aggregation system, such as ELK stack (this applies to all distributed system)?
- Do you need advanced features, available in more mature solutions (like Kubernetes)? Remember that getting familiar with Kubernetes is many times harder than with Swarm.
Experience in production
My application is using Swarm for 6 months now, migration from docker-compose took one week (including learning how to do this). I had to adjust my configuration to become truly stateless, and use external logs and metrics aggregators. At top-peak period I was using 35 nodes. Management of cluster is just easy, examples:
docker service scale name_of_service=30 or
docker service update --env-add SECRET_ENV=youdontneedtoknowit name_of_service. Screen of a status of my services:
Deployment pipeline looks like this:
Using newest Docker-compose v3 syntax with
deploy section and
Docker stack deploy command it's easier than ever to store your application configuration in VCS and easily deploy it to whole cluster without manual action. Example configuration:
version: '3' services: web: image: registry.gitlab.com/example/example # you need to use external image command: npm run prod ports: - 80:80 deploy: replicas: 6 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure
Whole deploy command is a one-liner:
docker stack deploy application. Also, I'm using Gitlab.com pipelines, and with them it's possible to achive result like this:
I can rollback to previous versions using web UI, even on mobile. And I love it!
This is my point of view to Docker Swarm. I've considered other options, but in my opinion it's currently the best choice if you just want to scale dockerized application to multiple hosts. Hope this post helped you! Next time I'll write how to start with Docker Swarm, stay tuned!