In this article we will explore 3 issues:
- What is docker? Why is it so attractive?
- Steps to build a Rails application using docker.
- Add entrypoint and wait-for-it.sh
I. What is Docker? Why is it so attractive?
1.1. What is docker?
- Docker is a tool designed to make creating, deploying and running applications easy to use containers.
- Containers allow developers to package an application with all the necessary parts, such as libraries and other depedencies, and repackage them in a package.
1.2. Why is it so attractive?
Reason 1: Build once, Run anywhere
- Making app building easier. => Save time. Instead of struggling to set up the environment to fit the project environment, you can take half a day to install, then when you go home, you want to do more, but your computer does not meet the environment. project, it took you all night to set up. What a torture.
Reason 2: Increase performance, reduce application size.
- In a way, docker is a bit like a virtual machine, but not like a virtual machine (brain damage =))). Instead of creating an entire virtual operating system, Docker allows applications to use the same Linux kernel as the system they are running and only requires applications to be ported with things that are not yet running on the server.
Reason 3: Docker is open source.
- This means that you or anyone can contribute to Docker and expand it to meet specific needs if you need additional features that are not available.
Scattered like that, let’s get to the point now.
2. Install Docker, Docker Compose into ROR app
To folow the steps below, you need to install Docker , Docker Compose
In this article I use Ubuntu 16.04
Step 1: Create a Rails application:
1 2 | rails new rails-docker-app |
Step 2: Create a Dockerfile in the project’s root directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | FROM ruby:2.6.5 # Install apt based dependencies RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl && curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y --no-install-recommends nodejs && apt-get clean && rm -rf /var/lib/apt/lists/* # Configure the main working directory. ENV APP_DIR /app RUN mkdir -p $APP_DIR WORKDIR $APP_DIR # Create a bundle folder in container ENV BUNDLE_PATH=/bundle BUNDLE_BIN=/bundle/bin GEM_HOME=/bundle ENV PATH="${BUNDLE_BIN}:${PATH}" COPY . $APP_DIR |
I will explain each command line in the above file
FROM ruby:2.6.5
: Using Ruby version 2.6.5RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl && curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y --no-install-recommends nodejs && apt-get clean && rm -rf /var/lib/apt/lists/*
: Run apt-get update to update apt repository , then install the basic package for the codeENV APP_DIR /app
: Set environment variables to work with / appRUN mkdir -p $APP_DIR
: Create a folder $ APP_DIRWORKDIR $APP_DIR
: Define directory as directory $ APP_DIRENV BUNDLE_PATH=/bundle BUNDLE_BIN=/bundle/bin GEM_HOME=/bundle
: Set environment variablesBUNDLE_PATH
,BUNDLE_BIN
,GEM_HOME
into the bundle folder to declare the gem installed into this forder bundle. Otherwise it will default to / usr / local / bundle. this works when you add a new gem it will check if the gem has been installed or not it will only install that gem only.ENV PATH="${BUNDLE_BIN}:${PATH}"
: Mount the bundle folder defined above with your computer’s gem path.COPY . $APP_DIR
: Copy the local folder into the $ APP_DIR folder of the container
Step 3: Create a “docker-compose.yml” in the project’s root directory:
Docker’s definition of Compose is a great place to start.
Compose is a tool to identify and run multi-container Docker applications. I will create a file docker-compose.yml and explain each line of code in the comment as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | version: "3.7" # Sử dụng version 3.7 services: # Tạo một service db để chạy mysql db: image: mysql:5.7.28 # Tạo một image mysql version 5.7.28 ports: - 3306:3306 # Map cổng 3306 ở ngoài máy local với cổng 3306 của mysql trong docker volumes: - db-data:/var/lib/mysql # Tạo một thư mục db-data để lưu dữ liệu ngoài máy thật, sau đó map vào thư mục của container là /var/lib/mysql env_file: .env # Sử dụng file env để lưu biến môi trường restart: always # Để tránh downtime # Tạo một service app app: build: context: . dockerfile: ./Dockerfile command: scripts/wait-for-it.sh -t 120 db:3306 -- scripts/entrypoint # Note: Mình sẽ giải thích rõ hơn tại sao có dòng này ở bên dưới nha. volumes: - .:/app - bundle:/bundle # Map bundle folder in computer with bundle folder in container ports: - "3000:3000" # Map cổng 3000 trong container ra ngoài cổng 3000 của máy local. links: - db env_file: .env stdin_open: true # Chạy debug khi up docker-compose tty: true volumes: bundle: db-data: |
Explain:
Volumes : All data in the container will be lost when the container is down, meaning that the data in the app container and the data in the db container (including the records created when I run seed, rails c, … ) will be lost.
Docker generates volumes that allow us to store data outside the actual computer, then map it into the container’s data store.
In the above code, you see the definition of db services using mysql, to be able to use you need to create more:
- .env file to save environment variables
1 2 3 4 5 | # .env.example DATABASE_HOSTNAME=db DATABASE_USERNAME=your_username DATABASE_PASSWORD=your_password |
- file database.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # database.yml default: &default adapter: mysql2 encoding: utf8 host: <%= ENV["DATABASE_HOST"] %> username: <%= ENV["DATABASE_USERNAME"] %> password: <%= ENV["DATABASE_PASSWORD"] %> development: <<: *default database: rails-docker-demo_development test: <<: *default database: rails-docker-demo_test |
3. Entrypoint
vs wait-for-it.sh
The code for docker-compose.yml file has this line
command: scripts / wait-for-it.sh -t 120 db: 3306 – scripts / entrypoint
What effect does it have? Let’s find out why using this command.
The first is scripts/wait-for-it.sh -t 120 db:3306
The drawback of dockers is that we don’t know when a container is ready to run. Docker support uses depends_on
to control the boot order. But it verifies the container is running before starting the service.
To ensure no booting of your container before the dependencies are ready to be accepted.
We add wait-for-it.sh to the scripts folder to control everything that is initialized before the container starts.
You can read and read the wait-for-it.sh script file here
The second is scripts/entrypoint
This file is used to run commands when you run docker compose up. You follow the commands in the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #entrypoint #!/bin/bash set -e # Các gem đã cài đặt đã được mount từ volume vào folder /bundle trong container sẽ không phải install nữa, # chỉ khi có thay đổi mới thực hiện install. bundle check || bundle install --binstubs="$BUNDLE_BIN" # Check xem đã có database chưa, nếu chưa sẽ tạo database và migrate database bundle exec rails db:prepare rm -f tmp/pids/server.pid # Chạy rails server bundle exec rails s -b 0.0.0.0 -p 3000 exec " <a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="9abeda">[email protected]</a> " |
Once you’ve completed the steps above, run the following command:
1 2 3 | docker-compose build docker-compose up |
Off container:
1 2 | docker-compose down |
** Note: **
- You only need to build the image using the
docker-compose build
command when there are changes in Dockerfile - Other cases, it just up to the command
docker-compose up
and down to turn off the commanddocker-compose down
whiff.
Some common commands:
docker ps -a
: Displays all containersdocker start/stop container_id
: Start / stop containerdocker attach container_id
: Debug in the dockerdocker-compose run app /bin/bash
: Run bash in docker
Above is how to build a Rails app using docker. Hope to help you! Thankyou