Tự xây dựng cho mình ứng dụng trên docker
Giới thiệu Docker là một công cụ tuyệt vời được thiết kể để tạo mới, chạy các tứng dụng bằng cách sử dụng các container. Các container cho phép đóng gói các ứng dụng với các thư viện, service, database... tất cả sẽ được đóng gói lại trong một container duy nhất. Nhờ vậỵ các developer sẽ chỉ cần ...
Giới thiệu
Docker là một công cụ tuyệt vời được thiết kể để tạo mới, chạy các tứng dụng bằng cách sử dụng các container. Các container cho phép đóng gói các ứng dụng với các thư viện, service, database... tất cả sẽ được đóng gói lại trong một container duy nhất. Nhờ vậỵ các developer sẽ chỉ cần tập trung vào việc viết code mà ko phải quan tâm đến việc cài cắm các service hay các ứng dụng thứ 3. Ở phần trước mình đã giới thiệu cho các bạn về các thao tác cơ bản để làm việc với docker. Trong phần này mình sẽ hướng dẫn các bạn tự xây dựng image cho riêng mình và kết các hợp các container để xây dựng ứng dụng rails .
Dockerfile
Định nghĩa: Dockerfile chứa tập hợp các lệnh để docker có thể đọc hiểu và thực hiện để đóng gói thành một image theo yêu cầu của người phát triển. Có thể hiểu Dockerfile là một file text chứa tất cả các câu lệnh để chỉ dẫn cho docker xây dựng images. Bằng cách sử dụng câu lệnh docker build docker sẽ tự động xây dựng theo các bước mà developer viết trên dockerfile. Format của Dockerfile :
# Comment INSTRUCTION arguments
Phần tiếp theo là ý nghĩa của các chỉ dẫn trong Dockerfile. Hoặc bạn có thể tham khảo ý nghĩa của các câu lệnh và cách sử dụng tại đây
- FROM : Là base image để chúng ta tiến hành build một image mới trên image. Vd: FROM ruby || ubuntu || mysql
- MAINTAINER : thông tin của người phát triển Dockerfile
- RUN : Sử dụng khi muốn thực thi các command trong quá trình xây dựng image
- COPY : Copy một file từ host machine tới docker container
- WORKDIR : image sẽ trỏ tới directory khai báo, nếu không có thì docker sẽ tự tạo mới
- ENV : Định nghĩa các biến môi trường
- CMD : Sử dụng khi muốn thực thi các command trong quá trình build container mới từ image
Sau đây là Dockerfile mình xây dựng với base image: ubuntu kết hợp với nginx
~/workspace/Docker/myUbuntu$ touch Dockerfile ~/workspace/Docker/myUbuntu$ ls Dockerfile
Sau đó mình sẽ thêm vào Dockerfile nội dung như sau:
FROM ubuntu:16.04 CMD echo "Hello ubuntu" # Update the repository RUN apt-get update # Install necessary tools RUN apt-get install -y nano wget dialog net-tools vim git # Download and Install Nginx RUN apt-get install -y nginx # Remove file index docker RUN rm -v /var/www/html/index.nginx-debian.html ADD index.nginx-debian.html /var/www/html EXPOSE 80 CMD [ "nginx", "-g", "daemon off;" ]
Trước khi build mình sẽ thay đổi 1 chút trang index.html của thằng nginx bằng cách như sau:
~/workspace/Docker/myUbuntu$ ls Dockerfile index.nginx-debian.html
Bạn có thể tạo tùy ý tạo trang index theo cách mà bạn mong muốn. Tiếp theo mình sẽ sử dụng các câu lệnh docker build -t "myubuntu:1.0" . (với tham số -t: khái báo tên và nhãn quả images, '.' để chỉ Dockerfile ở thư mục hiện tại đang trỏ tới) để build image với tên là myUbuntu.
~/workspace/Docker/myUbuntu$ docker build -t "myubuntu:1.0" . Sending build context to Docker daemon 4.608 kB Step 1/9 : FROM ubuntu:16.04 ... Successfully built 34eb6725fd74 ~/workspace/Docker/myUbuntu$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE myubuntu 1.0 34eb6725fd74 58 minutes ago 346 MB
Sau khi build xong image ta sẽ thực hiện tạo container từ image myubuntu vừa tạo được bằng câu lệnh sau
~/workspace/Docker/myUbuntu$ docker run -p 8080:80 -it myubuntu:1.0 /bin/bash
Sau khi vào được bash shell của container mình sẽ thực hiện start dịch vụ nginx bằng câu lệnh sau
root@56eefd1ad30c:/# service nginx start * Starting nginx nginx
Kết quả sau khi chạy container:
Dockercompose
Docker compose là một công cụ dùng để quản lý và liên kết nhiều containers , mỗi container chạy 1 service riêng biệt nhưng phục vụ chung một ứng dụng. Docker-compose có định dạng giống file yaml (key - value), chứa các chỉ dẫn để khởi động, liên kết các container với nhau, sử dụng docker-compose cũng đơn giản chỉ với một vài câu lệnh đơn giản là có thể hiểu được. Ba bước cơ bản để sử dụng compose file:
- Xây dựng Dockerfile cho từng ứng dụng riêng biệt 2.Định nghĩa các service trong file docker-compose.yml để liên kết và chạy các ứng dụng cùng một thời điểm nhưng tách biệt môi trường 3.Sự dụng 2 câu lệnh sau để build và start docker-compose: docker-compose build và docker-compose up
Phần tiếp theo mình sẽ sử dụng docker-compose để liên kết 3 containers, mỗi container chạy 1 service như hình dưới gồm có nginx, rails và mysql server để tạo một ứng dụng hoàn chỉnh như sau: Tiếp theo là cấu trúc Dockkercompose trong thư mục. Ở bước đầu tiền mình sẽ chỉ liện kết 2 service là rails và db do đó mình sẽ commann phần nginx lại. Docker-compose
version: "2" services: # web: # build: ./nginx # depends_on: # - app # links: # - app # ports: # - 8080:80 app: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/noteapp ports: - "3000:3000" depends_on: - db links: - db db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 123456 ports: - "3307:3306"
Mình xin được giải thích sơ qua về file docker compose sau:
- version: để khai báo phiên bản định dạng mà mình thiết kế file docker-compose, ví thế tùy từng phiên bản mà các câu lệnh chỉ dẫn khác nhau(tham khảo tại đây)
- services: chứa các container sẽ được khởi tạo và chạy
- build: địa chỉ của dockerfile
- command: khai báo lệnh sẽ được chạy khi start container
- ports: port của hostmachine : port của container(bi) Tạo docker file cho rails app như sau. Dockerfile:
# Base image: FROM ruby:2.3.3 # cài đặt các thư viện RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs # tạo thư mục chứa ứng dụng RUN mkdir /noteapp WORKDIR /noteapp COPY Gemfile /noteapp/Gemfile COPY Gemfile.lock /noteapp/Gemfile.lock RUN bundle install COPY . /noteapp
Tạo Gemfile và chỉnh sửa nội dùng như sau:
source "https://rubygems.org" gem "rails"
Cuối cùng là Gemfile.lock, sau khi bạn đã khởi tạo hoàn tất các file trên, tiếp theo ta sẽ thực hiện build dockerfile và tạo mới rails app bằng các câu lệnh sau
~/workspace/Docker/seminar-docker$ docker-compose build # waiting to install ~/workspace/Docker/seminar-docker$ docker-compose run app rails new . --force --database=mysql --skip-bundle
Sau khi tạo mới project thì lúc này trong thư mục docker sẽ chứa các folder hoặc các file được sinh tự động của rails. Tuy nhiên ta vẫn phải cấp quyền chỉnh sửa trên thư mục như sau:
~/workspace/Docker/seminar-docker$ cd .. ~/workspace/Docker$ sudo chmod 777 -R seminar-docker/
Sau đó ta sẽ chỉnh sửa các file mặc định của thằng rails để ứng dụng có thể chạy được :
#config/database.yml default: &default adapter: mysql2 encoding: utf8 pool: 5 username: root password: 123456 host: db #socket: /var/run/mysqld/mysqld.sock development: <<: *default database: noteapp_development test: <<: *default database: noteapp_test production: <<: *default database: noteapp_production username: noteapp password: <%= ENV['NOTEAPP_DATABASE_PASSWORD'] %>
Trong Gemfile mình sẽ sửa lại một chút như sau:
gem "rails", "~> 5.1.4" gem "mysql2"
Sau khi sửa mình sẽ build lại và start toàn bộ container:
~/workspace/Docker/seminar-docker$ docker-compose build ~/workspace/Docker/seminar-docker$ docker-compose up
Ta có thể xem welcome page của Rails tại địa chỉ: 'localhost:3000'
Add Nginx’s container
Trước khi build mình muốn giải thích lí do tại sao lại chọn nginx để kết hợp với ứng dụng. Nginx là proxy server dùng để nhận các request từ phía client sau đó sẽ đưa tới rails server, tại đây thì hệ thống sẽ thực hiện các business logic và trả kết quả về cho client. Nginx đã giải quyết được nhiều vấn đề còn tồn đọng như C10k ngoài ra nginx có hỗ trỡ Load Balancing do đó nó trở thành một trong những webserver phổ biến hiện nay. Dưới đây là một cấu trúc phổ biến của thằng rails trên môi trường production. Quay lại phần trước, trong folder nginx được khởi tạo ở trước, ta sẽ tạo dockerfile cho nginx container :
# Base image: FROM nginx # Install dependencies RUN apt-get update -qq && apt-get -y install apache2-utils # establish where Nginx should look for files ENV RAILS_ROOT /var/www/noteapp # Set our working directory inside the image WORKDIR $RAILS_ROOT # create log directory RUN mkdir log # Copy Nginx config template COPY nginx-app.conf /tmp/docker_example.nginx # substitute variable references in the Nginx config template for real values from the environment # put the final config in its place RUN envsubst "$RAILS_ROOT" < /tmp/docker_example.nginx > /etc/nginx/conf.d/default.conf # Use the "exec" form of CMD so Nginx shuts down gracefully on SIGTERM (i.e. `docker stop`) CMD [ "nginx", "-g", "daemon off;" ]
Tiếp theo ta sẽ tạo file nginx-app.conf (nginx/nginx-app.conf) để thay đổi cho phù hợp với ứng dụng :
#It's used for proxying requests to other servers. upstream puma_rails_app { #That app is a named resource that was specified in the docker-compose.yml file. server app:3000; } server { listen 80; proxy_buffers 64 16k; proxy_max_temp_file_size 1024m; proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; location / { try_files $uri $uri/ @rails_app; } location @rails_app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://puma_rails_app; # limit_req zone=one; access_log /var/www/noteapp/log/nginx.access.log; error_log /var/www/noteapp/log/nginx.error.log; } }
Tiếp theo mình sẽ bỏ command của service web trong docker-compose và commad port của app :
version: "2" services: web: build: ./nginx depends_on: - app links: - app ports: - 8080:80 app: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/noteapp # ports: # - "3000:3000" depends_on: - db links: - db db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 123456 ports: - "3307:3306"
Cuối cùng mình sẽ thực hiện build lại ứng dụng như sau:
~/workspace/Docker/seminar-docker$ docker-compose build ~/workspace/Docker/seminar-docker$ docker-compose up
Kiểm tra lại trang welcome của rails đã lên chưa ở địa chỉ localhost:8080 nhé