Here I use a Play application , but any application can be deployed in a similar way. Your application should be designed in a way that ensures that same code can run on any number of machines without any change.
You should have an application running on local inside a Docker container. You may refer to this link on how to get this done for a Play application.
What we wish to do?
- Deploy this application into production
- Setup Log rotation
- Setup Load Balancers
- NGINX Setup
Here I would be using a simple NGINX load balancer, and using capistrano for deployment.
Networking Setup
- Hosts Running the Application:
- server1.com
- server2.com
- server3.com
- Load Balancer Host: loadbalancer-server.com
The requests flow is like:
User –> Load balancer(NGINX) –> Application Host (NGINX) –> Application Host (Application)
Only the NGINX is exposed to the outside world on the application host.
Setup
- capistrano:
sudo gem install capistrano
(on local) - docker-compose:
sudo pip install docker-compose
(on host machine) - git
The code would be cloned from Git and not from local filesystem. Deployment using Docker Registry is also possible but would consume a lot of bandwidth if you consider that Java containers might end up in range of > 2 GB.
Setting up the Application Host
The diagram here is like
Request -> [NGINX DOCKER CONTAINER] -> [APPLICATION DOCKER CONTAINER]
With capistrano the deployment process is usually a two step process -
- Fetch latest code from Git
- Run deployment scripts on pulled in code
Using docker-compose allows us to simplify run-time configurations a lot. We can avoid running complex docker run
commands, and put the required information inside a file. At the time of writing docker-compose isn’t production-ready yet. But it is a great help to have and just a matter of time.
To use docker-compose, we have to define a docker-compose.yml file in the root folder of our application.
Here, we define two containers - web (the application) and nginx. Notice that we link the nginx to the web container. This is useful for communication between two containers. It essentially creates an entry in the /etc/hosts file with web pointing to an internal IP Address.
I am assuming we already have a Dockerfile for our application lying the root dir of our project. We create another Dockerfile for the NGINX container
And add a nginx.conf file
Notice, that we would be replacing the app_location with web. Remember that web actually creates a hosts entry. So we can directly use it here. The sed command does a find/replace on the nginx.conf file.
The setup has an advantage that the Play application though running in the same host, is completely invisible to the outside world. This is a great security advantage provided by docker. All communication must only happen through the Nginx running.
docker-compose makes our lives easier and we dont have to remember the command line docker commands.
docker-compose build
: Build the containersdocker-compose up -d
: Start the process defined in RUN commands and run as daemondocker-compose stop
: Bring down the containers
We would be executing the stop, build and up commands in this order while deploying.
Deployment Scripts (Capistrano)
I would be using capistrano for executing these commands, but you could be using just any other script for the purpose. I firstly get capistrano into the project - cap install
. This would create a Capfile along with a config directory. We have stages specified in the config/deploy directory. We should edit the config/deploy/production.rb since we are dealing with production environment here. If deploying to staging or uat you can create a similar environment. In the production.rb define your application servers -
Note that generally you would be having a DB server also. But you should keep it out of the the main application servers. Their deployments is rather straightforward.
In the config/deploy.rb create tasks -
One important point to note is that we run the docker_start command on each machine sequentially. This is done to ensure that your application suffers no downtime during deployment and the load balancer send the requests to other machines which are up in the period. Of course if have huge changes in which the older and newer versions are not compatible at all with each other, then you might need a different mechanism.
Now to deploy -
cap production deploy
: Fetches from gitcap production deploy:docker_start
: Starts the dockerized containers
Setting up Load Balancer
This is done the usual way. I am using an Nginx docker container for this.
You may refer to this project on Github.
Conclusion
Thus we have a load balancer setup. This routes to the application hosts. The hosts have only nginx containers exposed to the outside world. The nginx container can talk to play containers.
The code is hosted on Github
(TODO - upload code to github)
Comments Section
Feel free to comment on the post but keep it clean and on topic.
blog comments powered by Disqus