Successfully reported this slideshow.
Your SlideShare is downloading. ×

Ruby microservices with Docker - Sergii Koba

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 31 Ad
Advertisement

More Related Content

Slideshows for you (20)

Similar to Ruby microservices with Docker - Sergii Koba (20)

Advertisement

More from Ruby Meditation (20)

Recently uploaded (20)

Advertisement

Ruby microservices with Docker - Sergii Koba

  1. 1. Ruby microservices With Docker
  2. 2. About me ● Serhii Koba ● Position ○ Web Team lead at MobiDev ○ Lecturer at KhAI ● Fullstack ○ BE: Ruby, Go, Php, Python ○ FE: Vue JS ○ DO: Docker, Ansible ○ IoT, Blockchain, Android ● Blogger a bit ● Personal moto: Until we learn - we live Social media: ● http://1devblog.org ● github: @sergey-koba-mobidev ● twitter: @KobaSerhii ● facebook: @kobaserhii
  3. 3. What we will talk about ● How Docker helps building Ruby Microservices ● Automating development processes ● Repeatable environment ● Optimization of Docker images ● Refactoring Docker Compose files ● Central logging with Docker ● Routing requests to microservices ● Deploy?
  4. 4. What we will not talk about ● When to use microservices ● What is the best framework/language for microservices ● How to migrate to microservices ● How to install Docker, Docker Compose ● Docker for Dummies :) ● Is Rails good for Microservices
  5. 5. Docker
  6. 6. Two words about Docker ● Application build and deploy tool ● Containers based virtualization (shares host OS kernel) ● Each container is based on image ● Each piece of software (service) should be a container ● Lightweight ● Secure https://www.docker.com/what-container
  7. 7. Ruby microservice: Sinatra + Trailblazer # service.rb # https://github.com/bark-iot/ require 'sinatra' set :bind, '0.0.0.0' set :port, 80 get '/houses' do result = House::List.(user_id: USER['id']) if result.success? body House::Representer.for_collection.new(result['models'] ).to_json else status 422 body result['contract.default'].errors.messages.uniq.to_json end end # concepts/house/operations/list.rb class House < Sequel::Model(DB) class List < Trailblazer::Operation step Contract::Build() step Contract::Validate() step :list_by_user_id #... def list_by_user_id(options, params:, **) options['models'] = House.where(user_id: params[:user_id]).all options['models'] end end end Hi @apotonick
  8. 8. Dockerfile FROM ruby:2.5.0 # Pg RUN apt-get update -qq && apt-get install -y build-essential libpq-dev lsb-release ENV APP_ROOT /app RUN mkdir $APP_ROOT WORKDIR $APP_ROOT EXPOSE 80 # Bundle COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN gem update bundler && bundle install --jobs 4 # Copy the rest of source COPY . /app Build image docker build -f Dockerfile.test -t house-service/test . List images docker images REPOSITORY | TAG | IMAGE ID | CREATED | SIZE house-service/test | latest | c9b0fe5b01d1 | 2 minutes ago | 985MB Run docker run -i -p 80:80 house-service/test bundle exec ruby service.rb 985 MB
  9. 9. Dockerfile: Use Alpine linux FROM ruby:2.5.0-alpine # Pg RUN apk --update --upgrade add postgresql-dev git build-base ENV APP_ROOT /app RUN mkdir $APP_ROOT WORKDIR $APP_ROOT EXPOSE 80 # Bundle COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN gem update bundler && bundle install --jobs 4 # Copy the rest of source COPY . /app Build image docker build -f Dockerfile.test -t house-service/test . List images docker images REPOSITORY | TAG | IMAGE ID | CREATED | SIZE house-service/test | latest | ab58dd76e0d3 | 2 minutes ago | 320MB 320 MB
  10. 10. Dockerfile: Multistage builds FROM ruby:2.5.0-alpine as bundler # CODE FROM PREVIOUS SLIDE # Stage 2 FROM ruby:2.5.0-alpine RUN apk --update --upgrade add postgresql-dev EXPOSE 80 ENV APP_ROOT /app RUN mkdir $APP_ROOT WORKDIR $APP_ROOT # Copy the rest of source COPY . /app COPY --from=bundler /usr/local/bundle /usr/local/bundle Build image docker build -f Dockerfile.test -t house-service/test . List images docker images REPOSITORY | TAG | IMAGE ID | CREATED | SIZE house-service/test | latest | 8f5a46611b7e | 2 minutes ago | 127MB 127 MB
  11. 11. Dockerfile: Results 8x
  12. 12. Docker Compose
  13. 13. Docker Compose: Yml file Let's assume each service is placed in a separate folder ./house-service ./user-service ./device-service ... house-service: build: ../house-service command: bundle exec ruby service.rb networks: - my-network ports: - 80 volumes: - ../house-service/:/app environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db RACK_ENV: development stdin_open: true tty: true
  14. 14. Docker Compose: Multiple services services: db: image: postgres:latest ports: - "5432:5432" environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db redis: image: redis:4.0.5 house-service: … user-service: … user-service: build: ../user-service command: bundle exec ruby service.rb networks: - my-network ports: - 80 volumes: - ../user-service/:/app environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db RACK_ENV: development stdin_open: true tty: true Run services: docker-compose up -d 16 lines
  15. 15. Docker Compose: extension fields ● Yaml anchors and extend ● Docker Compose file version 3.4 ● “X-” sections (extension fields) x-base-service: &base-service command: bundle exec ruby service.rb networks: - my-network ports: - 80 environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db stdin_open: true tty: true users-service: << : *base-service build: ../users-service volumes: - ../users-service/:/app 5 lines
  16. 16. Docker Compose: Production In production we want to do things differently: ● Use images instead of build ● Remove stdin and tty ● Remove volumes with app code ● Remove test containers x-base-service: &base-service command: bundle exec ruby service.rb networks: - my-network ports: - 80 environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db RACK_ENV: production users-service: << : *base-service image: myapp/users-service:latest
  17. 17. Two options: ● Two separate docker-compose.yml files ● Overriding files Docker Compose: Multiple Files Run Development docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d # docker-compose.yml PRODUCTION house-service: image: myapp/house-service:latest command: bundle exec ruby service.rb networks: - my-network ports: - 80 environment: POSTGRES_USER: my_user POSTGRES_PASSWORD: my_pass POSTGRES_DB: my_db # docker-compose.dev.yml DEV house-service: build: ../user-service command: bundle exec ruby service.rb environment: RACK_ENV: development
  18. 18. Central Logging
  19. 19. Logging ● View logs from multiple Docker containers ● Tracing data across multiple containers ● Tracing user action across multiple containers ● Aggregate logs ● Search logs View logs on a single container docker-compose -f docker-compose.yml -f docker-compose.dev.yml logs house-service
  20. 20. Logging: ELK stack
  21. 21. Logging: ELK with Docker logstash: build: docker/logstash/ command: logstash -f /etc/logstash/conf.d/logstash.conf ports: - "12201:12201/udp" kibana: build: docker/kibana/ ports: - "5601:5601" elasticsearch: image: elasticsearch:latest command: elasticsearch -Enetwork.host=0.0.0.0 ports: - "9200:9200" - "9300:9300" environment: ES_JAVA_OPTS: "-Xms750m -Xmx750m" volumes: - /usr/share/elasticsearch/data
  22. 22. Logging: ELK with Docker # kibana.yml port: 5601 host: "0.0.0.0" elasticsearch_url: "http://elasticsearch:9200" elasticsearch_preserve_host: true kibana_index: ".kibana" default_app_id: "discover" request_timeout: 300000 shard_timeout: 0 verify_ssl: true bundled_plugin_ids: - plugins/dashboard/index - ... // logstash.conf input { gelf {} } output { elasticsearch { hosts => "elasticsearch:9200" } }
  23. 23. Logging: Gelf driver for Docker containers x-logging: &logging driver: gelf options: gelf-address: 'udp://localhost:12201' tag: '{{.Name}}' house-service: << : *logging user-service: << : *logging Template variable
  24. 24. Logging: Add log messages to Ruby service class House < Sequel::Model(DB) class Create < Trailblazer::Operation step Model(House, :new) step :generate_key_and_secret step Contract::Persist() step :log_success failure :log_failure #... def log_success(options, params:, model:, **) LOGGER.info "[#{self.class}] Created house with params #{params.to_json}. House: #{House::Representer.new(model).to_json}" end def log_failure(options, params:, **) LOGGER.info "[#{self.class}] Failed to create house with params #{params.to_json}" end end
  25. 25. Logging: Kibana
  26. 26. Routing
  27. 27. Routing: first try house-service: ... ports: - 3000 user-service: ... ports: - 3001 device-service: ... ports: - 3002 # house-service/service.rb require 'sinatra' set :bind, '0.0.0.0' set :port, 3000 # user-service/service.rb require 'sinatra' set :bind, '0.0.0.0' set :port, 3001 http://localhost:3000 http://localhost:3001
  28. 28. Routing: Docker haproxy lb: image: dockercloud/haproxy links: - house-service volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - 80:80 house-service: ... environment: VIRTUAL_HOST: "*/houses*" VIRTUAL_HOST_WEIGHT: 102 http://samos-it.com/posts/docker-multi-website-single-ip-host-haproxy.html Docker Swarm Compatible
  29. 29. Possible routers - Docker cloud proxy (Deprecated) - Traefik https://traefik.io/ - Caddy https://caddyserver.com/ - Zookeeper
  30. 30. Conclusion
  31. 31. Conclusion ● All in one environment and tools for Ruby microservices ● Minimized Docker images ● Minimized Docker Compose file ● Central logging and tracing using ELK ● Routing (balancing) requests What’s next? ● Authorization layers? ● gRPC for inter communication? ● Message broker? ● Migrating monolith Rails app to Microservices? :)

×