Using Docker, CircleCI, GitHub, Heroku Pipelines for Local Development and CI/CD for Rails 5 Application
That was a very long title to describe this process but I hope to simplify that in the coming paragraphs.
Last week I was meeting with a friend and fellow developer and the conversation lead to choosing technology infrastructure platforms when starting new project. There are endless choices to pick from with Amazon Web Services, Microsoft Azure and Google Cloud Platform and a plethora of others. Each one comes with a learning curve and pros and cons but as our conversation continued on key point that was made stuck with me.
Focus on the first 10,000 users and then focus 100,000, then 1 million.This was a statement that floored me and stopped me in my tracks. Often times when building and architecting ideas our grand visions of future success can sometimes force us to make decisions about scaling to 1 million users when we haven’t focused on the first 10,000 users. The point that was so apparent in this statement is that if you overlook the importance of making your app infrastructure strong to handle the product needs for a smaller set of scaling will be difficult. The other point in this conversation that struck me was;
Scale when you need to scale, don’t focus on that right away.This led me this weekend to try to get a MVP built not focused on scaling but focused on building a development environment and process that had the following things;
- Local Development Environment
- Tested and Testable Code
- Easy to manage deployments and infrastructure
- Docker Configuration - https://docs.docker.com/compose/rails/
- Heroku Pipelines - https://www.youtube.com/watch?v=_tiecDrW6yY
- CircleCi Config for Rails - https://robots.thoughtbot.com/circleci-2-rails
What did I learn from this project
Rails server processes hanging around on docker-compose upRails applications running in Docker can still have running instances of server processes even when docker-compose down
has been run. One way to fix this is to change the start command for the Rails server in the docker-compose.yml
from using bundle exec rake to something that will remove existing server processes. Below is what I used and now each time I run docker-compose up
the Rails server starts with no issues.
bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
dotenv-rails is a helpful gem
As I was starting on the CircleCI integration I wanted to utilize the test environment built in from Rails for running tests both locally and on CircleCI. The problem I ran into was the default host for the database described in the Docker setup has the has the host listed as db and CircleCI uses localhost when connecting to one the prebuilt images.
Enter the dotenv-rails gem.
This gem allowed me to create a .env file with environment variable that I am able to set locally and store a different environment variable in my CircleCI configuration. This allowed me to run the code against the test environment both locally and in the cloud.
Heroku Pipelines are great and Heroku as infrastructure is Easy to Work with
From the early day of my Rails development back in the day Heroku was one of the standards for any Rails project for deployment. I think all the Rails way books made it the default. As I shifted away from Rails development to .NET and Node work primarily my projects took me away from Heroku. This project allowed me to get back into and explore the new offerings that I hadn’t fully seen since I’ve been primarily working with AWS and Azure recently.
Heroku Pipelines offer developers an easy to configure solution for setting up a continuous development pipeline for managing and releasing code to multiple different environments. Within minutes of setting the project I was able to have a process that included Review Apps => Staging => Production environment. Pull Requests would trigger the Review Apps to build a sample application to test current working code on the server itself which was a nice treat. Next, once the CircleCI tests would pass and I would merge that PR down to Master an automatic build was triggered and code was deployed to my Staging environment. Lastly, my production environment would never be touched until I felt ready to promote those changes to prod. The process of this was all automated and one final click of a button to promote code to a production environment. It was simple.
The process of setting up this process of local development with CI and CD took roughly a half day to work through the various configuration quirks I ran into. What I do have now with this application is a local development environment using Docker that can be tested locally and test against a CI server that will deploy to a staging environment upon successful completion of test that within one click could be deployed to production servers.
The next steps in this process for this app will be to focus on building out the features and addressing the scaling issues that may arise from increased traffic but fortunately Heroku makes it very simple to add more web and worker dynos to handle the load.
All in all I am very happy with the results of this setup and will try to setup up other languages and frameworks with a similar approach in the future.
The main lesson learned from this is - find the right tool for what you are looking to achieve now because when you build for scale you may introduce unnecessary complexity. Focus on the first 10,000 users and then focus 100,000, then 1 million.