12/08/2018, 16:18

Docker with rails app

Bạn cần phải cài đặt Docker, Docker có thể chạy trên hầu hết Linux distribution và có một số tool để chạy trên OSX cũng như trên windows. Bạn có thể cài docker thông qua các link sau : [Linux] https://docs.docker.com/linux/started/ [Mac] http://docs.docker.com/mac/started/ [Windows] ...

Bạn cần phải cài đặt Docker, Docker có thể chạy trên hầu hết Linux distribution và có một số tool để chạy trên OSX cũng như trên windows. Bạn có thể cài docker thông qua các link sau :

  • [Linux] https://docs.docker.com/linux/started/
  • [Mac] http://docs.docker.com/mac/started/
  • [Windows] http://docs.docker.com/windows/started/

Ta sẽ tạo ra một Rails project mà không cần đến việc cài đặt Ruby bởi Rails Docker image đã hỗ trợ

docker run -it --rm --user "$(id -u):$(id -g)" 
  -v "$PWD":/usr/src/app -w /usr/src/app rails:4 rails new --skip-bundle drkiq

Đầu tiên docker sẽ tìm images rails:4 trên local nếu không có nó sẽ download về. Sẽ mất một khoảng thời gian để nó download xong. -v "$PWD":/usr/src/app -w /usr/src/app Đoạn này sẽ connect local woking directory với /usr/src/app trong docker image. Điều này cho phép container run Rails scaffold --user đảm bảo việc bạn có full quyền trên các file đã tạo, không phải là root.

Sửa Gemfile

gem 'unicorn', '~> 4.9'
gem 'pg', '~> 0.18.3'
gem 'sidekiq', '~> 4.0.1'
gem 'redis-rails', '~> 4.0.0'

Sửa file config/database.yml

development:
  url: <%= ENV['DATABASE_URL'].gsub('?', '_development?') %>
test:
  url: <%= ENV['DATABASE_URL'].gsub('?', '_test?') %>
staging:
  url: <%= ENV['DATABASE_URL'].gsub('?', '_staging?') %>
production:
  url: <%= ENV['DATABASE_URL'].gsub('?', '_production?') %>

Sửa config/screts.yml file

---
development: &default
  secret_key_base: <%= ENV['SECRET_TOKEN'] %>
test:
  <<: *default
staging:
  <<: *default
production:
  <<: *default

sửa file config/application.rb

config.log_level = :debug
    config.log_tags  = [:subdomain, :uuid]
    config.logger    = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))

   # vì chúng ta sử dụng redis cho sidekiq, nên ta cũng sử redis để lưu cacche. 
    config.cache_store = :redis_store, ENV['CACHE_URL'],
                         { namespace: 'drkiq::cache' }
# config để active job biết ta dùng sidekiq
    config.active_job.queue_adapter = :sidekiq

Tạo file unicon

worker_processes ENV['WORKER_PROCESSES'].to_i
listen ENV['LISTEN_ON']
# set timeout khi request page
timeout 30
preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true
# fast LAN.
check_client_connection false

before_fork do |server, worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.connection.disconnect!
  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end

Tạo file config cho sidekiq

#config/initializers/sidekiq.rb

sidekiq_config = { url: ENV['JOB_WORKER_URL'] }

Sidekiq.configure_server do |config|
  config.redis = sidekiq_config
end

Sidekiq.configure_client do |config|
  config.redis = sidekiq_config
end

Tạo file env

#.drkiq.env

#  để lấy token chạy câu lệnh sau`rake secret`
SECRET_TOKEN=900bb6bbff924de7a046532769196ec79512b749ed2994dda391528d72ed6a97a0d9ce1d649e9e8e2b2574561340873e9926bd3044d8863d3537276568b0a7ff

WORKER_PROCESSES=1
LISTEN_ON=0.0.0.0:8000
CACHE_URL=redis://redis:6379/0
JOB_WORKER_URL=redis://redis:6379/0

Tạo Dockerfile file

FROM ruby:2.2.3-slim

# Install dependencies:
# - build-essential: To ensure certain gems can be compiled
# - nodejs: Compile assets
# - libpq-dev: Communicate with postgres through the postgres gem
# - postgresql-client-9.4: In case you want to talk directly to postgres
RUN apt-get update && apt-get install -qq -y build-essential nodejs libpq-dev postgresql-client-9.4 --fix-missing --no-install-recommends

ENV INSTALL_PATH /drkiq
RUN mkdir -p $INSTALL_PATH

# Gán context của câu lệnh sẽ được chạy trên Docker
WORKDIR $INSTALL_PATH

# đảm bảo các gem được cached và chỉ update khi chúng thay đổi, giảm thời gian build khi gem không thay đổi 
COPY Gemfile Gemfile
RUN bundle install

# Copy in the application code from your work station at the current directory
# over to the working directory.
COPY . .

# Cung cấp  dummy data cho  Rails 
RUN bundle exec rake RAILS_ENV=production DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname SECRET_TOKEN=pickasecuretoken assets:precompile

# Expose a volume so that nginx will be able to read in assets in production.
VOLUME ["$INSTALL_PATH/public"]

#  start  Unicorn server.
CMD bundle exec unicorn -c config/unicorn.rb

Tạo dockerignore file

dockerignore

.git
.dockerignore
Gemfile.lock

Tạo Docker Compose Configuration File

#docker-compose.yml

postgres:
  image: postgres:9.4.5
  environment:
    POSTGRES_USER: user_name
    POSTGRES_PASSWORD: yourpassword
  ports:
    - '5432:5432'
  volumes:
    - drkiq-postgres:/var/lib/postgresql/data

redis:
  image: redis:3.0.5
  ports:
    - '6379:6379'
  volumes:
    - drkiq-redis:/var/lib/redis/data

drkiq:
  build: .
  links:
    - postgres
    - redis
  volumes:
    - .:/e-elearning
  ports:
    - '8000:8000'
  env_file:
    - .drkiq.env

sidekiq:
  build: .
  command: bundle exec sidekiq -C config/sidekiq.yml
  links:
    - postgres
    - redis
  volumes:
    - .:/drkiq
  env_file:
    - .drkiq.env

Docker Compose cho phép bạn chạy một hoặc nhiều Docker container một cách dễ dàng. Bạn có thể định nghĩa mọi thứ trong YAML file.

Tạo Volumes

Trong docker-compose.yml ta đang tham chiếu tới volumn, nhưng chúng chưa được tạo ra. Chạy lệnh sau để create volumn

docker volume create --name drkiq-postgres
docker volume create --name drkiq-redis

Khi data được save vào db PostgreSQL hay Redis, nó cũng được lưu lại vào trong những volumn này ở workstation, nên bạn sẽ không lo việc mất data khi restart service bởi vì Docker container là staless

Chạy tất cả mọi thứ

docker-compose up

Lần đầu khi chạy câu lệnh này sẽ tốn thơi gian bởi nó cẩn phải pull tất cả các Docker images mà ứng dụng cần về

Khởi tạoi DB

# OSX/Windows users will want to remove --­­user "$(id -­u):$(id -­g)"
docker­-compose run --­­user "$(id ­-u):$(id -­g)" drkiq rake db:reset
docker­-compose run --­­user "$(id ­-u):$(id -­g)" drkiq rake db:migrate

Trong lúc chạy lệnh trên có thể sẽ thông báo command khởi động Redis và PostgreSQL một cách tự động, Điều này là vì khi bạn định docker-compose nó sẽ khởi động các dependencies

Chạy lại docker-compose lần 2

docker-compose up

sau đó access vào localhost:8000 nó sẽ mở rails introduction page

Nguồn tham khảo

https://semaphoreci.com/community/tutorials/dockerizing-a-ruby-on-rails-application

0