One of the most interesting thing in starting a new project is the possibility to learn new things. For the last year, I had the occasion to work on the ops side of running an app and not just limit myself to the backend programming. It was the occasion to learn more about docker and also aws services and kubernetes. Now I want to share a bit of what I would have wanted to know as a series of articles. The first one will be about running a rails app in docker.
I. the starting point
I know you are probably like "Show me your dockerfile already" so here it is.
Now I'll explain briefly the steps described above:
1.1 The first step is the base image you will use. While you can build a docker image from scratch by yourself it's easier to start from a prebuilt image containing most of the needed tools. In our case, we start from the official ruby image that comes with ruby and bundler preinstalled.
1.2 Install the system libraries required for the gems you will use. We will clean the downloaded packages after that to have a lighter image that's easier to upload.
1.3 Prepare a place for your code
1.4 Copy your application's code. The first param to the copy command is the location of the code you want to have in your docker image, the second one is where do you want it on the image's file system.
1.5 Install any gems required to run your application.
1.6 Define the command that will be executed when you run the docker image. In the case of a rails app it will be rails s. Note unlike the previous steps this command will be executed when running the image not when building it.
Now we can test it by using the command : docker build .
I cut a bit the logs in step 6 to save space.
Note how every command in the dockerfile result in a step. The result of each step is cached so the next builds will be faster. If let say you change your code but no new general library is defined in the dockerfile the new build will reuse the cached versions for steps 1 to 4.
You can check if the image was built successfully by using the command: docker images
Since we built the image without passing any extra params the name and tag of the resulting image is none. The most important info to us is image id that will be useful to run the image.
Now that you have the image id you can also run it: docker run -p 8000:80 e33b3d9250ff
The -p param will map the ports. the first one is the port from which you want to be able to access your container, the second one should be the one set in the dockerfile (Check the -p argument in the CMD command at the end). You can now access the rails application on localhost:8000.
II. the next step
The simple flow described in the previous part is enough to run your application but if you need to build often we can optimize it a bit.
1. bundle install
One of the most time-consuming steps is gem installation and it's executed with even the smallest change made to the codebase of the application. But we can use the step caching I mentioned before to help us avoid it when it's not necessary.
To achieve this we will first copy just the gemfile of the app and install it and then copy the rest of the files. This way if no changes to the gemfile were made we won't need to reinstall the gems.
2. Assets
When hosting the website in production you may need to also generate assets. we will add this step also. This will recompile assets with each build which is okay for now.
So this is the final dockerfile:
Top comments (3)
Iโm not very familiar with Puma which is the default gem. not sure if there is any downside to this. you might want to try nginx passenger?
14+ years Rails dev here putting my 2 cents.
I would say more projects use Puma over Passenger.
Passenger requires less configuration though Puma can be used without Nginx.
We use the pod in a kubernetes cluster. For now I didn't have any issues by using puma.
I must admit I'm unfamiliar with passenger so I dont know if it may have any benefits.