Slide 1

Slide 1 text

Kubernetes-native Ruby development (Salahutdinov Dmitry) Saint P Ruby Meetup, “RubyDay", 29/02/2020 1

Slide 2

Slide 2 text

Hey, my name is Dmitry 2

Slide 3

Slide 3 text

About myself :) 3

Slide 4

Slide 4 text

4 Learn Ruby with kids

Slide 5

Slide 5 text

AMPLIFR 5 SPRING30 30% discount coupon

Slide 6

Slide 6 text

6 Introduction

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

https://www.cloudcity.io/blog/2015/07/10/how-bundler-works-a-history-of-ruby-dependency-management/ 2009: Bundler 8

Slide 9

Slide 9 text

2009: isolated gem sets 9

Slide 10

Slide 10 text

2010: RVM (+gemsets) 10

Slide 11

Slide 11 text

2014: Docker 11

Slide 12

Slide 12 text

2014: isolated environment 12

Slide 13

Slide 13 text

2016: Amplifr With one of two micro services 13

Slide 14

Slide 14 text

2015: Kubernetes 14

Slide 15

Slide 15 text

2016: Amplifr 15

Slide 16

Slide 16 text

2016: Amplifr on Kubernetes 16

Slide 17

Slide 17 text

2020: micro-service paradise 17

Slide 18

Slide 18 text

2020: IaC 18

Slide 19

Slide 19 text

2020: independent deploy 19

Slide 20

Slide 20 text

2020: development 20

Slide 21

Slide 21 text

2020: development $ cd amplifr && docker-compose up server 21

Slide 22

Slide 22 text

2020: development $ cd amplifr-front && docker-compose up server 22

Slide 23

Slide 23 text

2020: development $ cd amplifr-logux && docker-compose up server 23

Slide 24

Slide 24 text

DC: incomplete environment We run critical services only 24

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Kubernetes for development? RAILS_ENV=development NODE_ENV=development LOG_LEVEL=debug DOMAIN=amplifr.local 28

Slide 29

Slide 29 text

Todo • Deploy the whole infrastructure locally/remote • Redeploy/sync cycle • Debug • Interactive: REPL/bash (+ generators) 29 To run development on top of Kubernetes

Slide 30

Slide 30 text

30 Deploy

Slide 31

Slide 31 text

https://amplifr.local 31

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Kustomize 33

Slide 34

Slide 34 text

Helm 34

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Debug 41

Slide 42

Slide 42 text

Telepresence 42

Slide 43

Slide 43 text

Telepresence 43

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Remote debugger Expose port of deployments, just as raw docker port exposing 45

Slide 46

Slide 46 text

Re-deploy 46

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Google Skaffold 48

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Skaffold dev-mode 50

Slide 51

Slide 51 text

Skaffold features •Helm/Kustomize/Docker-compose •Profiles support •Dev mode (rebuild/sync files) •Build strategies 51

Slide 52

Slide 52 text

52

Slide 53

Slide 53 text

53

Slide 54

Slide 54 text

Tilt manages dependencies In fact you manage dependencies with python code Titl microservices demo source code 54

Slide 55

Slide 55 text

Titl team is user-friendly Great 2 minutes Titl introduction video! And human-friendly 55

Slide 56

Slide 56 text

Tilt features • Tiltfile is a code • Dependencies • WebUI, manage all from one window • Helm/Kustomize/Docker-compose 56

Slide 57

Slide 57 text

57 Interactive

Slide 58

Slide 58 text

Clone manifest 58

Slide 59

Slide 59 text

Run “utility pod” kubectl run -it ruby-utility \ --image=ruby-example:7c021a795 \ --env RACK_ENV=development \ -- bash 59

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

Kubedude One ruby script to manage them all 61

Slide 62

Slide 62 text

Final solution 62

Slide 63

Slide 63 text

63 npm install Tools on top of Kubernetes

Slide 64

Slide 64 text

Good news 64

Slide 65

Slide 65 text

Manual copy files 65

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

Thank you ❤ @dsalahutdinov1 @dsalahutdinov https://dev.to/amplifr https://amplifr.com 67