3

I have only one small web project to be run through the Docker and only one machine where I can't use virtualization and I don't really need that either. I would like to know how can I deploy my application to VPS with Docker without any downtime.

For now, I am just using a repository and creating docker container with docker-compose (including some configuration for production through specific .yaml file).

I guess the best would be to use Swarm, but I think it's not possible since I could only use one machine.

Jelean Thomas
  • 422
  • 1
  • 7
  • 19

2 Answers2

6

Single machine deployments are a great use case for Swarm. You can do "rolling updates" if your services that make it possible for zero downtime service updates (assuming your running 2 containers of a service).

Obviously, you won't have hardware or OS level fault-tolerance, but Swarm is a better solution for production then the docker-compose cli.

See all my reasons for using Swarm in this case in my GitHub AMA on the subject: Only one host for production environment. What to use: docker-compose or single node swarm?

See my YouTube video on an example of rolling updates.

Bret Fisher
  • 8,164
  • 2
  • 31
  • 36
  • For a beginner on Docker as myself, what is missing for me is tooling on docker swarm compared to docker-compose. For instance, it is not easy AFAIK to `exec` a command in a service instance as with a `docker exec -it container_name bash`. I do not know what is the equivalent command on swarm. What I have seen so far are very unfriendly hacks to accomplish it https://stackoverflow.com/questions/39362363/execute-a-command-within-docker-swarm-service – Eduardo Nov 24 '18 at 00:02
  • 1
    If you need to exec into a container directly, either use a GUI like Docker Enterprise or Portainer, or ssh/remote into the specific server where the container exists before exec. Swarm does not have a "exec feature" where it will pass your exec connection through a manager and to the node where the service task is, so you have to use the other methods above. – Bret Fisher Nov 24 '18 at 05:38
  • Thanks for the complementary information. I will take a look into Portainer and how to use it. It seems to be pretty comfortable to use. – Eduardo Nov 24 '18 at 12:53
5

Here's a simple approach we’ve used in production with just nginx and docker-compose: https://engineering.tines.com/blog/simple-zero-downtime-deploys

Basically, it’s this bash script:

reload_nginx() {  
  docker exec nginx /usr/sbin/nginx -s reload  
}

zero_downtime_deploy() {  
  service_name=tines-app  
  old_container_id=$(docker ps -f name=$service_name -q | tail -n1)

  # bring a new container online, running new code  
  # (nginx continues routing to the old container only)  
  docker-compose up -d --no-deps --scale $service_name=2 --no-recreate $service_name

  # wait for new container to be available  
  new_container_id=$(docker ps -f name=$service_name -q | head -n1)
  new_container_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $new_container_id)
  curl --silent --include --retry-connrefused --retry 30 --retry-delay 1 --fail http://$new_container_ip:3000/ || exit 1

  # start routing requests to the new container (as well as the old)  
  reload_nginx

  # take the old container offline  
  docker stop $old_container_id
  docker rm $old_container_id

  docker-compose up -d --no-deps --scale $service_name=1 --no-recreate $service_name

  # stop routing requests to the old container  
  reload_nginx  
}
  • If your service is on mutiple networks, new_container_ip will be a concatenated list of IPs (and the next step will fail). Replace the `{{range ... end}}` parameter with `{{(index .NetworkSettings.Networks "my-network-name").IPAddress}}` instead to pick just one. – Malcolm Crum Jul 24 '21 at 08:53
  • 1
    Without actually knowing the nginx configuration file can't really use this. Can you share it ? Couldnt find it in your blog post as well – Nilan Saha Aug 22 '22 at 03:30