Slide 1

Slide 1 text

How Honestbee Does CI/CD On Kubernetes @vincentdesmet

Slide 2

Slide 2 text

$ whoami DevOps at Honestbee DevOps at Swatmobile @vincentdesmet / github.com/so0k Singapore-Kubernetes-User-Group Cloud-Native-Singapore

Slide 3

Slide 3 text

What is Honestbee?

Slide 4

Slide 4 text

What is Swat? https://swatmobile.io

Slide 5

Slide 5 text

Overview - Honestbee (~2 years): - Containers - Drone - Kubernetes - Helm - Vault - Swat (~3 weeks): - docker cloud + stacks → Kubernetes + Helm - Pluggable CD using Keel

Slide 6

Slide 6 text

What are Containers? ● Packages up software binaries & dependencies ● Immutable & testable ● Isolate software from each other ● Portable across environments a self-contained process ref: kubernetes-comic

Slide 7

Slide 7 text

Why Containers? image: ruby:2.1 services: - postgres stages: - Build - Test - Staging - Production ... source: GitLab CI Lightweight Reproducible builds

Slide 8

Slide 8 text

Why Containers? $ make get-tags-prod getting apse1a tag: master-ebf3205b getting apse1b tag: master-ebf3205b $ make get-tags-staging getting apse1a tag: staging-98a2aa4f getting apse1b tag: staging-98a2aa4f Note: "latest" is not a version Versioned packages

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

More than just packaging and Isolation - Scheduling - Resource Optimisation - Monitoring - Lifecycle and health - Auth{n,z} - Scaling - Discovery - … Source

Slide 11

Slide 11 text

ref: kubernetes-comic

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Version 1: sed kube-install: update-manifests kubectl create -f manifests/${SHORT_NAME}-deployment.tmp.yaml kube-update: update-manifests kubectl patch deployment ${SHORT_NAME} -p '{"spec":{"template":{"spec":{"containers":[{"name":"'"${SHORT_NAME}"'","image":"' "${IMAGE}"'"}]}}}}' update-manifests : @sed 's#\(image:\) .*#\1 $(IMAGE)#' manifests/${SHORT_NAME}-deployment.yaml > manifests/${SHORT_NAME}-deployment.tmp.yaml

Slide 14

Slide 14 text

Version 2: jinja

Slide 15

Slide 15 text

$ kubectl get deploy -l release=backend NAME DESIRED CURRENT UP-TO-DATE AVAILABLE backend-admin 16 16 16 16 backend-core 32 32 32 32 backend-core-bee 10 10 10 10 backend-karafka 1 1 1 1 backend-scheduler 1 1 1 1 backend-worker-all 8 8 8 8 backend-worker-critical 2 2 2 2 backend-worker-food-import 1 1 1 1 backend-worker-high 2 2 2 2 backend-worker-low 12 12 12 12 $ kubectl get hpa -l release=backend NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS backend-admin Deployment/backend-admin 9% / 70% 16 24 16 backend-core Deployment/backend-core 28% / 70% 32 48 32 backend-core-bee Deployment/backend-core-bee 20% / 80% 10 16 10 $ kubectl get scheduledscaler -l release=backend NAME AGE backend-admin 9d backend-core 9d backend-core-bee 9d

Slide 16

Slide 16 text

Different requirements

Slide 17

Slide 17 text

apiVersion: apps/v1beta1 kind: Deployment metadata: labels: app: backend component: service-core name: backend-core spec: replicas: 32 ... template: ... spec: containers: - env: - name: NEW_RELIC_APP value: backend-core-apse1a;backend-core ... image: myregistry/ backend:master-ebf3205b volumeMounts: - mountPath: /app/config/application.yml name: backend-config subPath: application.yml volumes: - name: backend-config secret: items: - key: config apiVersion: apps/v1beta1 kind: Deployment metadata: labels: app: backend component: scheduler name: backend-scheduler spec: replicas: 1 ... template: ... spec: containers: - command: - bin/rake - resque:scheduler env: - name: NEW_RELIC_APP ... image: myregistry/ backend:master-ebf3205b volumeMounts: - mountPath: /app/config/application.yml name: backend-config subPath: application.yml volumes: - name: backend-config secret: items:

Slide 18

Slide 18 text

Kubernetes Deployment challenges - Each application has multiple components - Every component has its own k8s resources How to: - Deploy, Manage, Edit and Update multiple k8s configurations - Deploy multiple k8s configurations as a single application - Parameterise and support multiple environments - Manage application releases: rollout, rollback, history, ... - ...

Slide 19

Slide 19 text

Helm Package Manager for Kubernetes - Aims to provide Apt/Yum/Homebrew User Experience - Ensure collaboration - Shareable Packages Deployment Manager - Repeatable deployments - Manage multiple configurations - Update, Rollback and test application deployments

Slide 20

Slide 20 text

Chart, Repositories, Releases - Chart: “Package”, “Bundle” - Repository: Package Repository - Release: Installed Chart (same chart can be installed multiple times)

Slide 21

Slide 21 text

Helm Charts Render(Templates + Values) = Release Templates: - The Go Template language: {{ .foo | quote }} - Variables, simple control structures (looping, conditionals, ... ) - 50+ functions from Go/Sprig template libraries ...

Slide 22

Slide 22 text

Easy to manage deployments $ helm install backend --values production.yaml $ helm history backend REVISION UPDATED STATUS CHART DESCRIPTION 110 Tue Apr 24 17:53 SUPERSEDED backend-2.0.7 Upgrade complete 111 Wed Apr 25 10:53 SUPERSEDED backend-2.0.7 Upgrade complete 112 Wed Apr 25 14:58 SUPERSEDED backend-2.0.7 Upgrade complete 113 Wed Apr 25 15:02 SUPERSEDED backend-2.0.7 Upgrade complete ... $ helm rollback backend 112

Slide 23

Slide 23 text

Tip: Labels Crucial for: - Service Discovery - Graphing / Alerting - Troubleshooting Define standard set of labels include them for consistency ## _helpers.tpl {{- /* labels.standard prints the standard Helm labels. The standard labels are frequently used in metadata. */ -}} {{- define "labels.standard" }} app: {{ template "fullname" . }} chart: {{ template "chartref" . }} heritage: {{ .Release.Service | quote }} release: {{ .Release.Name | quote }} {{- end }} ## deploy.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: {{ template "backend.fullname" . }} labels: {{- include "labels.standard" . | indent 4 }} ...

Slide 24

Slide 24 text

Public Charts hub.kubeapps.com

Slide 25

Slide 25 text

Tip: Powerful templating For example: iterate yaml maps using the range function {{- range $key, $value := .Values.map }} Key: $key Value: $value {{- end }} map: foo: "bar" baz: "qux" Key: foo Value: bar Key: baz Value: qux

Slide 26

Slide 26 text

{{- range $api_name, $api := .Values.api.types }} {{- if gt $api.replicas 0.0 -}} apiVersion: apps/v1beta1 kind: Deployment metadata: name: {{ template "fullname" $ }}-{{ $api_name }} labels: {{ include "labels.standard" $ | indent 4 }} component: service-{{ $api_name }} spec: replicas: {{ $api.replicas }} ... --- apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: {{ template "fullname" $ }}-{{ $api_name }} labels: {{ include "labels.standard" $ | indent 4 }} component: {{ $api_name }}-service spec: scaleTargetRef: apiVersion: apps/v1beta1 kind: Deployment name: {{ template "fullname" $ }}-{{ $api_name }} minReplicas: {{ $api.autoscaling.min }} maxReplicas: {{ $api.autoscaling.max }} targetCPUUtilizationPercentage: {{ $api.autoscaling.cpuTarget }} --- {{- with $api.autoscaling.steps }} apiVersion: "scaling.k8s.restdev.com/v1alpha1" kind: ScheduledScaler ... steps: {{ toYaml . | indent 2 }} {{- end }} --- {{ end -}} {{- end -}} api: types: admin: autoscaling: min: 4 max: 12 cpuTarget: 70 steps: - runat: '0 30 11 * * *' mode: range minReplicas: 8 # 11:30am: pre-lunch schedule maxReplicas: 12 ... resources: limits: cpu: 2 memory: 6Gi requests: cpu: 2 memory: 6Gi config: admin-panel-enabled: true url: foo.honestbee.com core-bee: autoscaling: min: 2 max: 16 cpuTarget: 80 steps: - runat: '0 00 08 * * *' mode: range minReplicas: 4 # 08:00am: day schedule maxReplicas: 16

Slide 27

Slide 27 text

## Tip: Use Helm Values to automate monitoring / alerts ## i.e. Terraform + https://coveo.github.io/gotemplate/ {{- $Values := yaml "production.yaml" }} {{- define "container_graphs" }} {{ $container := . }} graph { title = "Memory - Honestbee-{{ $container }}" viz = "timeseries" request { q = "avg:kubernetes.memory.requests { $helm_release, kube_container_name:{{ $container }} }" type = "line" ... } } {{- end }} resource "datadog_timeboard" "backend_production" { title = "Backend Timeboard (Terraform)" {{- range $api, $definition := $Values.api.types }} {{- template "container_graphs" $api }} {{- end }} template_variable { name = "helm_release" prefix = "helm_release" default = "backend-production" } } api: types: admin: autoscaling: min: 4 max: 12 cpuTarget: 70 steps: - runat: '0 30 11 * * *' mode: range minReplicas: 8 # 11:30am: pre-lunch schedule maxReplicas: 12 ... resources: limits: cpu: 2 memory: 6Gi requests: cpu: 2 memory: 6Gi config: admin-panel-enabled: true url: foo.honestbee.com core-bee: autoscaling: min: 2 max: 16 cpuTarget: 80 steps: - runat: '0 00 08 * * *' mode: range minReplicas: 4 # 08:00am: day schedule maxReplicas: 16

Slide 28

Slide 28 text

$ gotemplate -P templates/timeboard.tf.template > timeboard.tf $ terraform apply datadog_timeboard.backend_production: Refreshing state... (ID: 102245) An execution plan has been generated and is shown Below. Resource actions are indicated with the following symbols: ~ update in-place Terraform will perform the following actions: ~ datadog_timeboard.backend_production graph.0.request.0.q: "max:kubernetes.cpu..." => "avg:kubernetes.cpu..." Plan: 0 to add, 1 to change, 0 to destroy. Tip: Automate monitoring / alerts

Slide 29

Slide 29 text

Tip: Helm Values Defines your application configuration per environment! - Should be source controlled or tracked - roboll/helmfile - skuid/helm-value-store - Should not contain secrets SECRETS

Slide 30

Slide 30 text

Private Chart Repository Honestbee: Version 1 - Self-host: - Using s3 Bucket + VPC endpoint - CI/CD: - Package - Generate Index - Push to s3 articles/devops/2017-07/drone-helm- repository resource "aws_s3_bucket" "b" { bucket = "${var.bucket_name_prefix}.${var.domain_name}" policy = "${data.aws_iam_policy_document.s3-read.json}" website { index_document = "index.html" } cors_rule { allowed_headers = ["*"] allowed_methods = ["PUT", "POST"] allowed_origins = [ "https://${var.bucket_name_prefix}.${var.domain_name}", ] } } data "aws_iam_policy_document" "s3-read" { statement { sid = "Access-from-specific-VPC-only" effect = "Allow" actions = [ "s3:GetObject" ] resources = [ "arn:aws:s3:::${var.bucket_name_prefix}.${var.domain_name}/*", ] condition { test = "StringEquals" variable = "aws:sourceVpce" values = [ "${values(data.terraform_remote_state.kops-state.vpc_endpoint)}", ] } } }

Slide 31

Slide 31 text

Integration with CI/CD

Slide 32

Slide 32 text

Private Chart Repository Honestbee Version 2: - Use ChartMuseum - CI/CD: - Package and push honestbee/drone-chartmuseum ChartMuseum is an open-source Helm Chart Repository written in Go (Golang), with support for cloud storage backends, including Google Cloud Storage and Amazon S3. Works as a valid Helm Chart Repository, and also provides an API for uploading new chart packages to storage etc.

Slide 33

Slide 33 text

Secrets? Version 1 (k8s 1.7): Vault + VaultController - Hashicorp Vault - Supports k8s auth - roboll/kube-vault-controller - Add k8s custom resource: secretClaim kind: SecretClaim apiVersion: vaultproject.io/v1 metadata: name: {{ template "fullname" . }} labels: {{- include "labels.standard" . | indent 4 }} spec: type: Opaque path: "secret/{{ .Values.env }}/{{ .Release.Name }}" renew: 3900

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Swat May 7 → May 25 docker cloud + stack → kubernetes + helm routing-worker: autoredeploy: true deployment_strategy: every_node environment: - MASTER_URL=master - MASTER_SECRET=foo expose: - "3352" image: swat/routing-worker:staging links: - routing-master:master tags: - staging routing-master: autoredeploy: true environment: - VIRTUAL_HOST=http://bar.com,https://bar.com - ... services: routing-master: replicaCount: 1 image: repository: swat/routing-master tag: master pullPolicy: Always port: 3352 healthChecks: livenessPath: / readinessPath: / resources: limits: cpu: 500m memory: 500Mi ingress: enabled: true hosts: - "foo.com" environment: MASTER_URL: "master" services: routing-master: image: tag: staging resources: limits: cpu: 250m memory: 300Mi ingress: hosts: - "bar.com" default staging staging 1:1

Slide 36

Slide 36 text

Swat May 7 → May 25 docker cloud + stack → kubernetes + helm - Automated haproxy config through VIRTUAL_HOST envvars - Automated Let's Encrypt Certificates - Manual DNS configuration - Challenges in Secret Management - zalando/kube-ingress-aws-controller - Automated AWS ALB configuration - Automated AWS Certificate Management - kubernetes/external-dns - Automated DNS configuration using AWS Route53 - mozilla/sops - Automated secret encrypt / decrypt using AWS KMS 1:1

Slide 37

Slide 37 text

Swat: Secrets vNext (k8s 1.8+): Bank-vaults + vault-sidekick - banzaicloud/bank-vaults: - Automated unsealing - Automated Vault initialisation - UKHomeOffice/vault-sidekick - Vault audit by Service

Slide 38

Slide 38 text

Swat - Pluggable CD May 25 → May 31 docker cloud + stack → helm + keel.sh - Automated deployment on image push - Git branch → Image tag (no sha) - Poll registry for image changes - Trigger Kubernetes rolling update using newer image digest through helm - Provides slack notifications (optional approval workflow): Re-use existing CI

Slide 39

Slide 39 text

Join us

Slide 40

Slide 40 text

No content