Rails – Docker – Circle CI – Github

Tram Ho

As the title, in this article I will guide you to create a rails project, use docker, circle CI and check the statut pass before merging PR on github.

Create rails app with docker

Create rails app

First of all, you need to create a new rails app locally (so locally you need to install rails first)

I use db as mysql, so I have -d mysql

Once the rails app is created, don’t do anything in a hurry, we will move the app to docker as well.

First, you can roughly understand that docker will be a separate computer, helping you run the software and programs that you have installed. 1 computer will have hardware and software. In terms of hardware, usually docker will use the maximum resources that the host machine (your computer is installing docker), in this article I will not go into depth on this issue, if you want to learn, can go to here to read more. As for the software, there will usually be the operating system, which program is needed.

Create Docker

If you know how to install win, or ghost win, this docker part will be relatively easy to understand.

Create Dockerfile

Dockerfile roughly understands the commands for you to create an image , just like when you install win, you need a win .iso , to boot into the usb, and then install it on your computer. In fact, this process is more ghost than that, because in addition to the operating system, there will be necessary software available.

Create a file called Dockerfile right in the project directory


The capital letters are syntax of Dockerfile, you can refer to here

  • FROM: as English name, từ . The image that you are about to create will be based on another image, just like in order to create ghost win files, you must have 1 máy chạy win before. Here I will be based on 1 ubuntu machine, with pre-installed ruby ​​2.7.1. ruby:2.7.1-slim-buster is an image created by someone else, uploaded on the Internet, and you just need to download it for use. You can go to https://hub.docker.com/ to find more images. (I got the ruby ​​image at https://hub.docker.com/_/ruby )
  • RUN: will use you to run commands, the commands will be the same as those you type with your real machine, also need apt-get update, then install. Here I will run some commands to install the necessary software and lib for my rails app: git, nodejs, yarn, libssl-dev, default-libmysqlclient-dev, then bundler to be able to bundle install
  • followed by the 3-line cluster

I create a folder called myapp in my máy tính mới . Next the myapp directory will be placed as the working directory, the following commands WORKDIR will run in the myapp directory, just like myapp will be the ổ C so that from now on installed, or copy what will be default install to ổ C After that, I will copy the entire contents of the circle-ci folder (which is the rails project on the real machine) into myapp.

  • Next, I will copy the file entrypoint.sh into /usr/bin/ and authorize it to execute (that is, to run a bash file, instead of ./usr/bin/entrypoint.sh, you can call boy entrypoint.sh ).
  • ENTRYPOINT: will help run the command entrypoint.sh when your computer is booted
  • EXPOSE: will set the network port that your máy tính will listen to

Create entrypoint.sh

Create a file entrypoint.sh in the directory of the project

Typically, in other tutorials you may find the line CMD ["rails", "server", "-b", ""] at the end of the Dockerfile file. In essence, CMD is like ENTRYPOINT is also to run commands when máy tính is booted. Because here, I need to run many commands, so I included them in this bash file. You can read more about distinguishing CMD vs ENTRYPOINT

Create image

Next we will create an image for later use for cài hoặc ghost win .

In the project directory, run the command

  • docker build : is the command to create the image
  • -t hatd/circle-ci:3.0 : is the option of the build command, helps to name the image, where my image is named hatd/circle-ci , and the image has a tag of 3.0 . In the next section I will explain why I named it like this.
  • . : sign . This will imply that the build command is run under the Dockerfile file in the same directory

There are many other options of the build command, you can see here .

Wait a moment for the build command to finish running, and run the docker image ls command to display all of the images on your computer. And you will see the image you just created

Create docker-compose.yml

Docker compose is like a script, to help you boot your máy tính up

Create the file docker-compose.yml in the directory of the project


  • file docker-compose.yml will be written in the syntax of version 3, depending on different versions, different syntax, can refer to list version
  • services: list the máy tính that I will boot, here I will boot 2 computers, db and web . db is for running mysql as the database, and the web is for running the rails server
  • volumes: list the sub-disks, like in the computer with drive C, drive D, when installing win, it will be installed in drive C, and drive D remains intact, so there will be no data loss.
  • networks : create networks, so that computers can connect with each other

Computer db : installed under the file ghost : mysql: 5.7.33. Environment variables are taken from the .env file. Since there is no .env variable in the .env file, it should be listed separately in the environment . With the command mysql_cache:/var/lib/mysql , allows you to synchronize data from /var/lib/mysql (config, mysql data) out of mysql_cache and vice versa. And this computer will be connected to the internal network

Web computer : installed according to the ghost file that we just created earlier hatd/circle-ci:3.0 . This machine also fetches the environment variables from the .env file. This computer will synchronize the myapp folder in máy tính web , in addition to the circle-ci folder on our real machine (this will help us when editing the code in the circle-ci folder, the code in máy tính web also corrected). ports: "3000:3000" will map port 3000 on the máy tính web to port 3000 of the real computer (because the rails server runs by default at port 3000). depends_on: db máy tính web depends_on: db will have to wait for the máy tính db be opened before opening, of course, if the server is turned on while the database is not turned on, it will be OK. Connect to the internal network together to be able to communicate with each other. 2 stdin_open: true tty: true , stdin_open: true understand that it will help you when you enter máy tính web will display what you type, the same log lines as on the terminal on the real machine.

The docker-compose.yml version 3 syntax + options are listed here

Start rails server

First you need to add the .env file and edit the config / database.yml file


p / s: db is the name of the service db in docker-compose.yml

config / database.yml

Run the following command to boot our 2 máy tính

Wait a moment for the rails server to start successfully

Next comes the create db job. Since the app is already running on máy tính web , you need to be on máy tính web to be able to run the command.

To get into máy tính web (web container), run the command

  • docker exec : is a command that helps get into the máy tính web
  • -it: is an option of the exec command, making input and output similar to the actual terminal on the machine
  • circle-ci_web_1: is the container name (the name of the máy tính web ), you can use the container id here, but I often use the container name because it’s easy to remember, the structure of the container name is {tên project}_{tên service}_1 (usually the service seldom has the same name, so it ends with 1). The active list container can be checked with the docker container ls command
  • bash: this is the command that will run when we get into the container, it will open a terminal

Once inside the container, run the create db commands as usual: rails db:create , rails db:migrate . And then you can access localhost: 3000 and you will see the root page of the rails app

From now on, but commands related to rails like bundle install, rspec, rubocop will run in the web container.

Config Circle Ci

In this lesson, I will use circle Ci to check whether running rspec or rubocop has a pass or not

Add a gem

Add some necessary gems: rspec-rails, rubocop, rubocop-rails, simplecov, rspec_junit_formatter. Run bundle install

Add 1 Post model: rails generate scaffold Post title:string body:text published:boolean , db:migrate

More test: I can take some tests to be able to run rspec ( https://github.com/hatd/circle-ci/tree/master/spec )

Then run rspec spec/ , rubocop to confirm the pass locally first

Upload docker image

Since the Circle CI also uses docker to build the environment, a docker image will also be required. So we will need to upload the hatd/circle-ci:3.0 we just created to the internet, so that Circle CI can download:

  1. Please register an account on https://hub.docker.com/
  2. Create a repository named: circle-ci (identical to the name of our image)
  3. Login docker locally with the command: docker login --username=yourhubusername , yourhubusername is the username that when you register for an account, mine is hatd
  4. Push image with the command: docker push yourhubusername:imagename , in my case it will be docker push hatd/circle-ci . That is why, from the beginning, I named the image like that. If you don’t have a given name in the beginning, you can use the command docker tag image_id yourhubusername/repo_name:version_tag to change the name with the tag of an image. Refer

That way, your image is uploaded, and circle ci can be pulled back and built the docker

Add config circle ci

Add the .circleci/config.yml file in the project


  • Currently the version where circle ci support is 2, and the highest version is 2.1
  • Version 2.1 support orbs, I do not know very well, so please ignore it
  • references: list the environment variables to use, since these variables are simple here, they can be listed directly in the file, but later on with sensitive information, we will have to set it in circle ci ( DATABASE_HOSTNAME is , not db like in docker-compose.yml anymore)
  • jobs: list of jobs that will be run, here I have only 1 job named test_rspec
  • workflows: list the jobs that will be run through, in order, Here I have a workflow named test , run a job test_rspec

job test_rspec : will be built using docker.

  • There will be 2 dockers built here, 1 using the hatd/circle-ci:3.0 that I uploaded to the docker hub earlier, 2 is the mysql image, similar to the mysql I used in docker-compose.yml, both there will be the necessary env variables.
  • steps: lists the steps that will need to be run in the job, in order.
  • checkout: means git checkout, help pull the code back, and put it in ~ / project
  • run: help run the commands, here will need to run a series of commands, to finally be able to run the command bundle exec rubocop and bundle exec rspec spec/ to successfully check

You can read more about configs here

Setup Circle CI on the web

Remember to push the code to github first

  • Log in to https://app.circleci.com/dashboard , through your github account
  • Go to the Projects tab, you will see the github repository, select Set Up Project , this will always display the Start Build button, because we already have a .circleci / config.yml file and a valid file. If there is no config.yml file, or the file is corrupted, there will be a download button for config file, or commit a config file to the project.

project has file config.yml

project does not have config.yml file

  • After clicking the Start Build button, you will be transferred to the pipelines screen, where you will see the first pipeline running, wait until it finishes, if success is successful, you have already completed the circle setup.

Next, so that when there is a new commit to github, circle ci will run, you need to go to Project Settings , in Advanced , enable GitHub Status Updates


Try to push a new commit to github, and wait for the new pipeline to finish, then click job test_rspec , and watch out for step Bundle install

seen it for a long time, right? Since each new pipeline, we will build 1 out a new docker, and will run the new bundle install again, although there is no fix to the gemfile, that will be very time consuming. So we will need to cache this section again

Modify the step bundle install as follows

  • restore_cache: will restore the cache with the key v1-gem-cache-{{ checksum "Gemfile.lock" }} , if this cache is not available, circle ci will find the cache with the key v1-gem-cache- , if If there is no more, there is no cache
  • save_cache: will save the cache, with the key is v1-gem-cache-{{ checksum "Gemfile.lock" }} , the data is taken from the path vendor/bundle . Why is vendor/bundle , you notice, in the env list, I have to BUNDLE_PATH: vendor/bundle , with this env variable, bundle install will install the gem to this path , so we will cache the gems already install

After adding config, let’s see the results

So in the Bundle Install step, it only takes time to bundle check

You can read more about caching here

Wait db:

After adding the cache bundle, there is a problem, that is, the steps are executed too quickly, so when running the step Database setup , docker mysql has not been built yet.

So we need to have one more step to wait for the database to be ready to connect before running step Database setup

After a while, I saw this way https://stackoverflow.com/a/54249757 , something like using a script to check if mysql host is ready or not. I will use wait-for-it.sh .

After adding wait-for-it.sh file to the project, add step before the Database setup step

Get test results and coverage

Usually, when running tests on ci, in addition to knowing the success / error test, you also need to know where the error is, how much% the coverage is, then where those show up.

Circle CI supports collect test data and coverage

Let’s revise step Rspec test a little bit, and add 2 new steps

this time we will output the rpsec output to html, and put it in the ~/rspec folder, and copy the coverage folder (the directory generated by the gem coverage). And then save it

When you click on the job test_rspec , there will be 2 more tabs, TESTS and ARTIFACTS



Clicking to ~/rspec/coverage/index.html you can go to the coverage page for that commit


Status check Github

Now, let’s set up so that PR must pass circle ci to merge

  1. Go to the settings of the repo
  2. On the Branches tab, add a new rule
  3. Enter the Branch name pattern as the branch that will compare in PR, usually master
  4. Select Require status checks to pass before merging and choose ci/circleci: test_rspec

Thus in PR there will be more check status

must pass to merge PR

And you can see where the fail is

The article is still sketchy, please comment in the comments for me

Share the news now

Source : Viblo