Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kubernetes-native Ruby development

Kubernetes-native Ruby development

Imaging you develop your application onto Kubernetes cluster with all the infrastructure got up.

Here are my thoughts about the way to get there and the tooling to use for it.

Salahutdinov Dmitry

February 29, 2020
Tweet

More Decks by Salahutdinov Dmitry

Other Decks in Programming

Transcript

  1. 2008: ruby application $ gem install -v 2.1.0 rails Fetching

    actionmailer-2.1.0.gem Fetching rails-2.1.0.gem Fetching activesupport-2.1.0.gem Fetching activeresource-2.1.0.gem Fetching actionpack-2.1.0.gem Fetching activerecord-2.1.0.gem Successfully installed activesupport-2.1.0 Successfully installed activeresource-2.1.0 Successfully installed actionpack-2.1.0 Successfully installed actionmailer-2.1.0 Successfully installed activerecord-2.1.0 Successfully installed rails-2.1.0 6 gems installed $ rails new brand_new_2008 No isolation, hard to manage several gemsets 7
  2. DC: dummy dependencies $ cd amplifr && docker-compose up server

    $ cd ../amplifr-front && docker-compose up server $ cd ../amplifr-logux && docker-compose up server $ … $ cd ../amplifr-screenshot ERROR: Can't find a suitable configuration file in this directory or any parent. Are you in the right directory? Supported filenames: docker-compose.yml, docker-compose.yaml 25
  3. Docker compose: duplication FROM gcr.io/amplifr.com/amplifr/amplifr-baseimage:latest AS builder LABEL maintaner="[email protected]" RUN

    apt-get update RUN apt-get -y install build-essential git libidn11-dev libpq-dev libxml2-dev libxslt1-dev ADD . /app WORKDIR /app RUN gem update --system && bundle config build.nokogiri --use-system- libraries RUN bundle install --without development:test -j4 --deployment -- path /usr/local/bundle FROM gcr.io/amplifr.com/amplifr/amplifr-baseimage:latest LABEL maintaner="[email protected]" COPY --from=builder /usr/local/bundle/ /usr/local/bundle/ COPY --chown=nobody:nogroup . /app WORKDIR /app RUN bundle exec rake assets:precompile && bundle exec rake assets:clean CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] FROM ruby:2.7 RUN apt-get update -qq \ && apt-get install -yq --no-install-recommends \ … && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ENV LANG=C.UTF-8 \ GEM_HOME=/bundle \ BUNDLE_JOBS=4 \ BUNDLE_RETRY=3 ENV BUNDLE_PATH $GEM_HOME ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH \ BUNDLE_BIN=$BUNDLE_PATH/bin ENV PATH $BUNDLE_BIN:$PATH RUN mkdir -p /app WORKDIR /app Dockerfile Dockerfile.dev 26
  4. Docker compose: duplication Kubernetes manifests vs. docker-compose.yml version: '3.4' services:

    app: &app build: context: . dockerfile: Dockerfile.dev args: PG_VERSION: '9.6' NODE_VERSION: '12.0.0' image: amplifr-dev:0.1.0 volumes: - .:/app:cached - bundle:/bundle … mail: image: drujensen/mailcatcher:latest ports: - '1025:1025' - '1080:1080' imgproxy: image: darthsim/imgproxy ports: - '8080:8080' 27
  5. Todo • Deploy the whole infrastructure locally/remote • Redeploy/sync cycle

    • Debug • Interactive: REPL/bash (+ generators) 29 To run development on top of Kubernetes
  6. Parametrize K8s manifest • RAILS_ENV=production/development • MAIN_DOMAIN=amplifr.com/amplifr.local • REPLICAS (web,sidekiq)=12/1

    • Memory/CPU limits • NODE_ENV=production/development • DEBUG_LEVEL=info/debug Raw manifest, helm charts, kustomize 32
  7. Fail #1: helm charts {{- if and .Values.ingress.enable .Values.production.enable }}

    apiVersion: v1 kind: List items: - apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.allow-http: "false" kubernetes.io/tls-acme: "false" nginx.ingress.kubernetes.io/proxy-body-size: 32m labels: app: {{ template "amplifr.name" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" release: {{ .Release.Name | quote }} heritage: {{ .Release.Service | quote }} name: app-amplifr-com spec: rules: - host: app.amplifr.com http: paths: - backend: serviceName: {{ .Release.Name | quote }} servicePort: 80 path: / - backend: serviceName: {{ .Release.Name | quote }} servicePort: 80 Dry helm charts 35
  8. Fail #2: certificates Manage certificates $ cfssl gencert -initca \

    builder/ca-csr.json | cfssljson -bare ca - $ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \ -config=builder/ca-config.json -profile=server \ builder/ca-server.json | cfssljson -bare server $ mv *.{csr,pem} ./builder { "CN": “amplifr.local”, "key": { "algo": "rsa", "size": 2048 }, 36
  9. minikube config set memory 8192 minikube config set cpus 2

    minikube start minikube ip # 192.168.64.7 echo "$(minikube ip) \ amplifr.local” | sudo tee -a /etc/hosts minikube addons enable ingress Fail #3: resources 37
  10. Fail #4: ENV variables $ env | wc -l 160

    Prioritize, reduce, generalize, dry env variables $ env | grep amplifr | grep com RAILS_ASSET_HOST=https://app.amplifr.com AMPLIFR_MAIN_DOMAIN=app.amplifr.com DIRECT_UPLOAD_URL=https://direct.amplifr.com IMGPROXY_URL=https://imgproxy.amplifr.com LANDING_URL=https://amplifr.com LOGUX_WSS_URL=wss://logux.amplifr.com 38
  11. Sane defaults DEFAULTS_SET = "defaults/#{ENV['DEFAULTS_SET'] || 'kubernetes'}" require_relative DEFAULTS_SET #

    Defaults module is a container for general purpose constants and # configuration options used as sane defaults for the Settings object # (config/settings.yml). Environment variables defined by .env file # will always have precedence over a default. # # A value belongs here if: # # 1. It is not a secret (like an API key). # 2. It is not specific to a personal development environment. # 3. It does not depend on dev environment (RAILS_ENV). # NOTE: Use this module for common constants for all presets module Defaults # Inherit preset-specific constants include DEFAULTS_SET.camelize.constantize AMPLIFR_MAIN_PROTOCOL = 'https' IMGPROXY_MAX_SRC_RESOLUTION = 67.2 end module Defaults module Kubernetes # TODO SCREENSHOT_URL = 'http://screenshot.screenshot' end end module Defaults module Local AMPLIFR_MAIN_DOMAIN = 'localhost.amplifr.com:3001' LANDING_URL = 'https://localhost.amplifr.com:3001' # TODO end end module Defaults module DockerCompose AMPLIFR_MAIN_DOMAIN = 'localhost.amplifr.com:3001' LANDING_URL = 'https://localhost.amplifr.com:3001' SCREENSHOT_URL = 'http://screenshot' # TODO end end https://dev.to/amplifr/sane-defaults-25pe 39
  12. Deployed -infrastructure review (all the services) -dried helm-charts/k8s manifests -dried

    ENV variables (12 factors?) -K8s experience -deploy anywhere(g.e. for any branch with ci) 40
  13. Attach to container As it runs in development mode with

    debugger support # Switch to raw terminal mode, sends stdin to #’bash' in ruby-container from pod 123456-7890 # and sends stdout/stderr from 'bash' back to # the client kubectl attach 123456-7890 \ -c ruby-container -i -t 44
  14. Continuous deployment FROM ruby:2.7 WORKDIR /app ADD Gemfile* ./ RUN

    bundle install ADD . ./ require 'rack/lobster' run Rack::Lobster.new Dockerfile Source code Sync changes Rebuild/redeploy Speeds up development feedback cycle response time 47
  15. Skaffold config apiVersion: skaffold/v2alpha1 kind: Config build: artifacts: - image:

    ruby-example context: backend sync: infer: - '*.rb' - 'config.ru' https://dev.to/amplifr/continuous-deployment-to-minikube-with-skaffold-18ib 49
  16. 52

  17. 53

  18. Tilt manages dependencies In fact you manage dependencies with python

    code Titl microservices demo source code 54
  19. Tilt features • Tiltfile is a code • Dependencies •

    WebUI, manage all from one window • Helm/Kustomize/Docker-compose 56
  20. Sync files back: ksync $ ksync create --selector app.kubernetes.io/ksync="true" \

    —reload=false -n default $(pwd)/backend /app/ # start ksync $ ksync watch --log-level=debug 60
  21. Conclusion -Deep dive into Kubernetes (developer skill) -Review on your

    project infrastructure -Dry you application config -Cleanup helm-charts/k8s-manifests -Remote-development - idea of the future -Fun 66