Cài đặt môi trường phát triển Rails với Docker
Bài đăng này đã không được cập nhật trong 3 năm
Mô tả
Trong series này mình sẽ từng bước xây dựng 1 ứng dụng Single Page Application với Rails API
, ReactJS
.
Ở bài trước mình đã trình bày Ý tưởng
, thiết kế Wireframe
, Database
và khởi tạo folder cho project
Hôm nay mình sẽ hướng dẫn các bạn cách cài đặt môi trường phát triển với Docker
Công việc của chúng ta bao gồm các các phần chính:
- Xây dựng các Docker Image
- Kết hợp các
containers
với Docker Compose - Cấu hình NGINX
- Chạy DEMO
Các bạn có thể xem lại bài trước để khởi tạo project Rails
và ReactJS
Đây là những gì mình thêm vào để Docker
nó:
docker
├── entrypoint
├── images
│ ├── db
│ │ └── Dockerfile
│ └── nginx
│ ├── Dockerfile
│ └── nginx.conf
└── wait-for-it.sh
Dockerfile
docker-compose.yml
Các bạn có thể xem chi tiết code tại https://github.com/oNguyenVanThinh/spa_tasks_api/pull/1/files
Bonus: Chia sẻ chút về kinh nghiệm
tìm hiểu công nghệ của mình. Khi đọc một bài viết gì mình cũng có 3 bước:
- Đọc lướt qua một lượt xem có những phần gì, cố gắng hiểu một cách tổng quan
- Đọc kỹ hơn để hiểu từng chi tiết nhỏ
- Thực hành: Làm theo các bước hướng dẫn
Các bạn có thể áp dụng ngay cho việc đọc bài viết này !
Xây dựng các Docker image
Đầu tiên phải vào thư mục để code api với Rails mình đã tạo từ bài trước (Github)
$ cd spa_tasks_api/
Image: Rails App
Bắt đầu từ image Ruby 2.4
trên Docker Hub (https://hub.docker.com/_/ruby/ - đó chính là Ubuntu và đã cài đặt ruby 2.4)
Mình cài thêm Nodejs
(để có yarn
-> tính năng asset precompile
của Rails) và Vim
(editor để giúp chỉnh sửa file)
Tạo 1 folder trong image là usr/src/app
đây chính là folder mà chúng ta code Rails và copy các các file có trong project hiện tại vào image (trong đó có 2 file Gemfile
, Gemfile
) và sau đó dùng bundle để để cài các thư viện cần thiết.
Khi tạo container từ image Rails, nó sẽ chạy server lắng nghe ở cổng 3001
Tại sao mình chọn 3001
thay vì mình muốn để 3000
cho React App
# ./Dockerfile
FROM ruby:2.4
RUN apt-get update
RUN apt-get install -y build-essential nodejs --no-install-recommends
RUN apt-get install -y vim
RUN mkdir -p usr/src/app
WORKDIR usr/src/app
COPY Gemfile .
COPY Gemfile.lock .
RUN gem install bundler && bundle install
COPY . .
ENTRYPOINT ["bundle", "exec"]
CMD ["rails", "server", "-p", "3001", "-b", "0.0.0.0"]
EXPOSE 3001
Image: Postgresql
Mình dùng database là Postgresql
vì vậy mình cũng cần phải build một image bắt đầu từ postgres được pull từ Docker Hub.
# ./docker/images/db/Dockerfile
FROM postgres
Image: Nginx
Đây là đoạn giới thiệu về NGINX
Tiếng Việt
Nginx là một máy chủ proxy ngược mã nguồn mở (open source reverse proxy server) sử dụng phổ biến giao thức HTTP, HTTPS, SMTP, POP3 và IMAP , cũng như dùng làm cân bằng tải (load balancer), HTTP cache và máy chủ web (web server). Dự án Nginx tập trung vào việc phục vụ số lượng kết nối đồng thời lớn (high concurrency), hiệu suất cao và sử dụng bộ nhớ thấp. Nginx được biết đến bởi sự ổn định cao, nhiều tính năng, cấu hình đơn giản và tiết kiệm tài nguyên.
Tiếng Anh
NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities, NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers.
Nói thật mình cũng chẳng hiểu rõ NGINX
lắm. Mình chỉ biết dùng nó lắng nghe ở cổng 80
(là cổng của giao thức HTTP, cổng mặc định khi duyệt web) và mình dùng nó để cấu hình các service xem cái nào hoạt động ở cổng nào. Trong bài này thì mình dùng nó để cấu hình proxy
cho đường dẫn /api/
thì nó trỏ vào Rails server
ở cổng 3001
và đường dẫn /
thì trỏ vào Node server
ở cổng 3000
.
# ./docker/images/nginx/Dockerfile
FROM nginx
RUN mkdir -p /etc/nginx/conf.d/
COPY nginx.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]
Đây là file cấu hình NGINX
:
# ./docker/images/nginx/nginx.conf
upstream rails_app {
server app:3001;
}
server {
listen 80;
keepalive_timeout 10;
location / {
proxy_pass http://rails_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
Kết hợp các containers với Docker Compose
Sau khi đã có các Image
cần thiết. Bước tiếp theo là chúng ta sẽ cần dùng docker-compose.yml
để giúp các Image
này có thể kết hợp với nhau và thành 1 sản phẩm. Có 3 service chính là db
, app
, nginx
ở các cổng: 5432
, 3001
, 80
Mình dùng Docker Volume
để giúp tạo ra các driver
. Trong service db
thì có db-data
là external: false để giúp giữ lại được dữ liệu trong trường hợp container
được tạo lại và service app
thì có thông giữa code hiện tại của project với code trong image .:/usr/src/app
để mỗi lần code có cập nhật thì không cần copy lại.
Khi chạy các service với nhau bằng docker-compose
thì một internal network
sẽ được tạo lập. Chúng ta có thể biết được địa chỉ IP
của các service dễ dàng với link
và depends_on
. Ở đây mình dùng depends_on
. Và như thế container app
connect db
bằng IP
là db
. nginx
biết Rails app có địa chỉ IP
là app
.
# ./docker-compose.yml
version: '2'
volumes:
db-data:
external: false
services:
db:
build: ./docker/images/db
ports:
- 5432:5432
volumes:
- db-data:/var/lib/psql/db-data
app:
build: .
volumes:
- .:/usr/src/app
ports:
- 3001:3001
command: docker/wait-for-it.sh db:5432 -- docker/entrypoint
depends_on:
- db
nginx:
build: ./docker/images/nginx
ports:
- 80:80
depends_on:
- app
Vì khi chạy ứng dụng Rails ngoài việc chạy rails server
, thì cần chạy nhiều lệnh khác như bundle
để tải về các thư viện, rake:db:create
trong trường hợp chưa có database, rake:db:migrate
trong trường hợp thay đổi các bảng, vì thế mình tạo file entrypoint
để lưu tất cả các lên đó, và dùng đoạn batch wait-for-it.sh
để chạy các lệnh này.
Bạn có thể tải file wait-for-it.sh từ github của mình: https://github.com/oNguyenVanThinh/spa_tasks_api/blob/7009148d3e2a0e9c734a3f31105ffd20e5387414/docker/wait-for-it.sh
# ./docker/entrypoint
set -e
bundle check || bundle install
bundle exec rake db:create
bundle exec rake db:migrate
bundle exec puma -C config/puma.rb
exec "$@"
Sau đó thêm vào file cấu hình database.
(mình có thể dùng db
thay cho địa chỉ ip của postgresql server là do tính năng link
của docker-compose
)
# ./config/database.yml
default: &default
adapter: postgresql
encoding: unicode
+ username: postgres
+ host: db
+ port: 5432
Chỉnh sửa file config của puma từ cổng 3000 -> 3001
# ./config/puma.rb
+ port ENV.fetch("PORT") { 3001 }
Và đặc biệt, cần cấp quyền có thể chạy cho 2 file là wait-for-it.sh
và entrypoint
. Điều này rất quan trọng!. Vì nếu không cấp quyền nó sẽ không thể chạy.
sudo chmod +x docker/wait-for-it.sh
sudo chmod +x docker/entrypoint
Chạy demo
docker-compose build
docker-compose up
Giờ bạn có thể mở http://localhost:3001/ Hoặc sửa file /etc/hosts để vào bằng tên miền của bạn thích:
# ~/etc/hosts
127.0.0.1 localhost
127.0.0.1 spatasksapi
Vậy là mình có thể vào web bằng đường dẫn http://spatasksapi/ rồi
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6603bc5367ea spatasksapi_nginx "nginx -g 'daemon of…" 19 minutes ago Up 14 minutes 0.0.0.0:80->80/tcp spatasksapi_nginx_1
718b71fa4334 spatasksapi_app "bundle exec docker/…" 19 minutes ago Up 14 minutes 3000/tcp, 0.0.0.0:3001->3001/tcp spatasksapi_app_1
edbbdb98e019 spatasksapi_db "docker-entrypoint.s…" 28 minutes ago Up 14 minutes 0.0.0.0:5432->5432/tcp spatasksapi_db_1
Tóm lược
Bạn cũng có thể xem chi tiết code
của bài viết này tại:
https://github.com/oNguyenVanThinh/spa_tasks_api/pull/1/files
Nếu thấy hữu ích thì nhớ upvote
vào bên trái bài viết nhé Và follow
đến nhận thông báo khi có bài viết mới nhé !
Chúc các bạn tràn đầy năng lượng và làm việc hiệu quả
All rights reserved