=encoding utf-8 =pod =head1 Deploying Perl Apps using Docker, Gitlab & Kubernetes =head2 META =head3 Author Thomas Klausner =head3 Email domm AT plix.at =head3 URL http://domm.plix.at =head3 URL_slides http://domm.plix.at/talks/2019_riga_perl_docker_gitlab_kubernetes =head3 Date 2019-08-07 =head3 Location Rīga =head3 Event PerlCon 2019 =head2 What Cloud-ready Perl App Docker Kubernetes Helm Gitlab CI =head2 Why? Automate Deployment Process Non-devs should be able to deploy Deploy topic branches so customers can review Rollback deployments Play with new shiny =head3 What do we want? On each push to master, rebuild all affected libs and apps and automatically deploy them to C. Provide way to manually deploy to C. Deploy topic-branches to a per-branch environment that is similar to C but does not use live data. Do it in a reasonable time frame (a few minutes max) Allow roll-back to previous releases (at least for C) =head3 Alternative title: "How an old-school backend dev learned to work with Kubernetes" "How an old-school backend dev was forced to learn to work with Kubernetes because the colleague who initially did all that stuff quit his job to work for Google" Klaus Ita ("koki") Farhad Shahbazi - sphericalelephant.com =head3 Disclaimer: Simplified! a lot! =head2 Theory =head3 Cloud-ready Perl App I assume you know what a Perl app is :-) Cloud-ready? =for newslide Twelve-factor app https://12factor.net/ "A methodology for building software-as-a-service apps" I. Codebase One codebase tracked in revision control, many deploys =for newslide II. Dependencies Explicitly declare and isolate dependencies III. Config Store config in the environment =for newslide IV. Backing services Treat backing services as attached resources "backing service" = database etc "attached resource" = like a third-party service =for newslide VI. Processes Execute the app as one or more stateless processes "Twelve-factor processes are stateless and share-nothing. Any data that needs to persist must be stored in a stateful backing service." The cloud abhors state, but state pays the bills! =for newslide B: Easy to install, external services not hardcoded, no local state =head3 Docker "Enterprise Container Platform" https://www.docker.com/ "A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another." Your app and all it's dependencies in one B =for newslide You build docker containers using a C You can publish containers to a B You pull containers from a B and run them =for newslide Docker is a client-server application with a REST API The C CLI tool is a REST client After you install docker on your laptop you usually have a docker daemon running: ~/perl/talks/kubernetes$ ps xa | grep dockerd 1062 ? Ssl 6:57 /usr/bin/dockerd -H fd:// All the docker images you build and pull are stored on your disk =for newslide ~$ docker images =for newslide ~$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE gitlab-issues-overview latest 27ea2c3f5966 36 minutes ago 923MB my-app v0.001 e4c72efae2ca 2 days ago 929MB winxle-merchant latest e5e3aa31b2e0 5 weeks ago 218MB eu.gcr.io/wxl-vorteilswelt/perl-buildbase latest b41ac7615cc7 6 weeks ago 331MB debian buster-slim c6834bf0531b 8 weeks ago 68.4MB winxle-scripts latest feca77b35366 2 months ago 1.19GB winxle-api latest 50ec705c050e 3 months ago 1.21GB winxle-winner latest 6b201d6a72fb 5 months ago 1.2GB eu.gcr.io/wxl-vorteilswelt/validad-perl-base latest cf06d5bc680d 5 months ago 1.3GB hello-world latest 4ab4c602aa5e 5 months ago 1.84kB perl 5 c58a7ea6dfc4 5 months ago 885MB perl 5.28 c58a7ea6dfc4 5 months ago 885MB =for newslide pull ~# docker pull docker.elastic.co/elasticsearch/elasticsearch:6.6.0 run ~# docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" \ docker.elastic.co/elasticsearch/elasticsearch:6.6.0 use ~# curl http://localhost:9200 { "cluster_name" : "docker-cluster", "version" : { "number" : "6.6.0", }, "tagline" : "You Know, for Search" } =for newslide B: pack up your app in way that makes it very easy to run elsewhere =head3 Kubernetes κυβερνήτης a captain, a steersman, a pilot, a navigator Root of the prefix "cyber" =for newslide "Production-Grade Container Orchestration" https://kubernetes.io/ "Kubernetes is a portable, extensible open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation." B B You declare the state you want your app to be in Kubernetes makes sure that this is true =for newslide A very big API The C CLI to talk with the API "Nodes" = machines (physical or virtual) that run the services "Objects" =head3 Objects "A Kubernetes object is a 'record of intent'" Defined in an "object spec" or "manifest" YAML =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 **kind**: %%Deployment%% metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment **metadata:** name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment **spec**: **selector**: matchLabels: %%app%%: @@nginx@@ replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx **replicas**: @@2@@ template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 **template:** # **define pod** metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: # **define pod** metadata: **labels**: %%app%%: @@nginx@@ spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: %%app%%: @@nginx@@ replicas: 2 template: # **define pod** metadata: **labels**: %%app%%: @@nginx@@ spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 =for newslide apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 template: # **define pod**` metadata: labels: app: nginx **spec**: **containers**: - name: nginx **image**: %%nginx:1.7.9%% ports: - containerPort: 80 Uff We'll take a closer look later =head3 Object types C group of one or more containers that build a unit (eg. application + postfix for mailing) ephemeral C multiple instances of the same Pod, and how to restart them C how to access a group of Pods; stable =for newslide C Map external internet traffic to service And a lot more... =for newslide .------------------. | docker-image | '------------------' =for newslide .----------------------. | Pod | | .------------------. | | | docker-image | | | '------------------' | '----------------------' =for newslide .---------------------------------------------------. | Deployment | | .----------------------. .----------------------. | | | Pod | | Pod | | | | .------------------. | | .------------------. | | | | | docker-image | | | | docker-image | | | | | '------------------' | | '------------------' | | | '----------------------' '----------------------' | '---------------------------------------------------' =for newslide .---------------------------------------------------. | Service | '---------------------------------------------------' .---------------------------------------------------. | Deployment | | .----------------------. .----------------------. | | | Pod | | Pod | | | | .------------------. | | .------------------. | | | | | docker-image | | | | docker-image | | | | | '------------------' | | '------------------' | | | '----------------------' '----------------------' | '---------------------------------------------------' =for newslide .-------------------------------------------------------. | | | Your Kubernetes Cluster | | .---------------------------------------------------. | | | Service | | | '---------------------------------------------------' | | .---------------------------------------------------. | | | Deployment | | | | .----------------------. .----------------------. | | | | | Pod | | Pod | | | | | | .------------------. | | .------------------. | | | | | | | docker-image | | | | docker-image | | | | | | | '------------------' | | '------------------' | | | | | '----------------------' '----------------------' | | | '---------------------------------------------------' | '-------------------------------------------------------' =for newslide .---------------------------------------------------. | "The Internet" | '---------------------------------------------------' .-------------------------------------------------------. | | | Your Kubernetes Cluster | | .---------------------------------------------------. | | | Service | | | '---------------------------------------------------' | | .---------------------------------------------------. | | | Deployment | | | | .----------------------. .----------------------. | | | | | Pod | | Pod | | | | | | .------------------. | | .------------------. | | | | | | | docker-image | | | | docker-image | | | | | | | '------------------' | | '------------------' | | | | | '----------------------' '----------------------' | | | '---------------------------------------------------' | '-------------------------------------------------------' =for newslide .---------------------------------------------------. | "The Internet" | '---------------------------------------------------' .---------------------------------------------------. .-| Ingress |-. | '---------------------------------------------------' | | Your Kubernetes Cluster | | .---------------------------------------------------. | | | Service | | | '---------------------------------------------------' | | .---------------------------------------------------. | | | Deployment | | | | .----------------------. .----------------------. | | | | | Pod | | Pod | | | | | | .------------------. | | .------------------. | | | | | | | docker-image | | | | docker-image | | | | | | | '------------------' | | '------------------' | | | | | '----------------------' '----------------------' | | | '---------------------------------------------------' | '-------------------------------------------------------' =for newslide a lot of layers! And we need to multiply this for different environments: Production, Staging, Per-Branch-Review, ... =for newslide B: make sure all parts of your app are in their proper states and know where to find the other parts "Orchestration" =head3 Helm "Helm helps you manage Kubernetes applications" https://helm.sh/ Templating for manifest files =for newslide B: client (CLI) B: server running inside the k8s cluster B: a collection of files that describe a related set of Kubernetes resources nautical theme madness =for newslide B of ready-made Helm charts for various applications helm install stable/postgresql Provides charts to run PostgreSQL in Kubernetes =for newslide But we can write Helm charts for our own services =for newslide spec: replicas: **10** # %%production%% =for newslide spec: replicas: **10** # %%production%% spec: replicas: **2** # %%staging%% =for newslide spec: replicas: {{ .Values.global.myapp.replicas }} =for newslide spec: replicas: {{ @@.Values.global.myapp.replicas@@ }} file: **production-values.yaml** global: myapp: replicas: @@10@@ =for newslide spec: replicas: {{ @@.Values.global.myapp.replicas@@ }} file: **staging-values.yaml** global: myapp: replicas: @@2@@ =for newslide C package C C templates to generate kubernetes manifest files (in C) from data stored in C files IMO not a good tech match... Anybody wants to write something like this in Perl 5 or 6? =for newslide B: fine-tune your app to different environments =head3 Gitlab "A full DevOps tool" https://about.gitlab.com/ started as a git repo web viewer But now has issue handling, wiki and CI/CD features =for newslide B Continuous Integration B Continuous Delivery each commit should build and release a new version automatically instead of manual releases once a month :-) =for newslide B: run all the steps needed to build, test, release and deploy the app B: one step in a Pipeline B: something produced by a Job (eg, a tarball) to be used later in the Pipeline Gitlab also provides a B =for newslide =for img gitlab_pipeline_01.png =for newslide =for img gitlab_pipeline_02.png =for newslide B: automate build and deployment =head2 Summary - Theory B: Easy to install, external services not hardcoded, no local state B: pack up your app in way that makes it very easy to run elsewhere B: make sure all parts of your app are in their proper states and know where to find the other parts B: fine-tune your app to different environments via templating B: automate build and deployment =head2 Praxis =head3 What do we want? On each push to master, rebuild all affected libs and apps and automatically deploy them to C. Provide way to manually deploy to C. Deploy topic-branches to a per-branch environment that is similar to C but does not use live data. Do it in a reasonable time frame (a few minutes max) Allow roll-back to previous releases (at least for C) =head3 What do we have? Perl REST-APIS: Winxle-API, Winxle-Merchant, Profile Perl Daemons: Mailer, Profile-Notifier Go REST-APIs: wallapi, filestorage Plain old Backends: Winxle-Winner, Winxle-Admin Cron Jobs Javascript Frontends: winxle-SPA (react), winxle-merchant (vue) 3rd party tools: Elasticsearch, Redis, InfluxDB, Telegraf, imaginary/image-proxy, postfix, .. Postgres =for newslide Except Postgres, some other stateful object stores and a few legacy apps, everything is running inside kubernetes All of the services for this project live in a Monorepo =for newslide ~/validad/Winxle-Mono$ l drwxr-xr-x 8 domm domm 4096 Jan 21 18:36 Profile drwxr-xr-x 10 domm domm 4096 Jan 21 18:36 Winxle-API drwxr-xr-x 8 domm domm 4096 Aug 14 2018 Winxle-Admin drwxr-xr-x 8 domm domm 4096 Nov 29 11:51 Winxle-BonusCard drwxr-xr-x 6 domm domm 4096 Jan 21 18:37 Winxle-Merchant drwxr-xr-x 7 domm domm 4096 Jan 21 18:36 Winxle-Scripts drwxr-xr-x 9 domm domm 4096 Jan 21 18:36 Winxle-Winner drwxr-xr-x 8 domm domm 4096 Jan 2 13:06 deploy drwxr-xr-x 7 domm domm 4096 Oct 3 19:59 filestorage drwxr-xr-x 9 domm domm 4096 Nov 29 11:51 lib drwxr-xr-x 15 domm domm 4096 Sep 3 19:20 wallapi drwxr-xr-x 7 domm domm 4096 Jan 2 13:17 winxle-lib-js drwxr-xr-x 5 domm domm 4096 Jan 21 18:36 winxle-merchant-vue =head3 1: Make our app cloud-ready CPAN-style distribution List dependencies in C No hardcoded passwords, service locations etc No config files, but configuration via ENV vars "A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials." - https://12factor.net/config No state stored on disk, use a service =head4 Winxle-Merchant ~/validad/Winxle-Mono/Winxle-Merchant$ l -rw-r--r-- 1 domm domm 1093 Jan 21 18:36 Dockerfile -rw-r--r-- 1 domm domm 750 Feb 28 17:21 Makefile drwxr-xr-x 3 domm domm 4096 Jan 21 18:36 bin -rw-r--r-- 1 domm domm 1150 Feb 28 17:21 cpanfile drwxr-xr-x 3 domm domm 4096 Sep 30 12:42 lib =for newslide ~/validad/Winxle-Mono/Winxle-Merchant$ l -rw-r--r-- 1 domm domm 1093 Jan 21 18:36 Dockerfile -rw-r--r-- 1 domm domm 750 Feb 28 17:21 Makefile drwxr-xr-x 3 domm domm 4096 Jan 21 18:36 **bin** -rw-r--r-- 1 domm domm 1150 Feb 28 17:21 **cpanfile** drwxr-xr-x 3 domm domm 4096 Sep 30 12:42 **lib** B<✓> CPAN-style distribution B<✓> List dependencies in C =for newslide package Winxle::Merchant::Model::Image; sub **upload_image** { my ( $self, $payload ) = @_; my $ua = HTTP::Tiny->new; my $response = $ua->request( 'POST', $self->filestorage_api_url . '/images', { content => encode_json($payload) } ); } =for newslide package Winxle::Merchant::Model::Image; sub upload_image { my ( $self, $payload ) = @_; my $ua = @@HTTP::Tiny@@->new; my $response = $ua->%%request%%( '**POST**', $self->filestorage_api_url . '/images', { content => encode_json($payload) } ); } B<✓> No state stored on disk, use a service =for newslide package Winxle::Merchant::Model::Image; sub upload_image { my ( $self, $payload ) = @_; my $ua = HTTP::Tiny->new; my $response = $ua->request( 'POST', $self->@@filestorage_api_url@@ . '/images', { content => encode_json($payload) } ); } =for newslide package Winxle::Merchant::Model::Image; @@has@@ '**filestorage_api_url**' => ( is => 'ro', isa => 'Str', required => 1, ); B<✓> No hardcoded passwords, service locations etc =for newslide package @@Winxle::Merchant@@; use @@Bread::Board@@; ... service '%%Image%%' => ( class => '**Winxle::Merchant::Model::Image**', lifecycle => 'Singleton', dependencies => { dbic => '/DB/dbic', auth_model => '/Model/Auth' filestorage_api_url => $ENV{FILESTORAGE_API_URL}, }, ); =for newslide package Winxle::Merchant; use Bread::Board; ... service 'Image' => ( class => 'Winxle::Merchant::Model::Image', lifecycle => 'Singleton', @@dependencies@@ => { dbic => '/DB/dbic', auth_model => '/Model/Auth' **filestorage_api_url** => %%$ENV%%{@@FILESTORAGE_API_URL@@}, }, ); B<✓> No config files, but configuration via ENV vars =for newslide It's not very hard to write cloud-ready Perl apps Especially if you use sane coding guidelines anyway... Turning a cloud-ready app into a docker container is easy-ish: =head3 2: Generate reusable containers: Docker A C is basically a description on how to setup and run your app =for newslide FROM perl:5.28 COPY bin . COPY cpanfile . COPY lib . RUN cpanm -n --installdeps . COPY . /home/my-app WORKDIR /home/my-app ENTRYPOINT [ "plackup", "-s", "Starman", "/home/my-app/bin/my-app.psgi" ] =for newslide **FROM** @@perl:5.28@@ COPY bin . COPY cpanfile . COPY lib . RUN cpanm -n --installdeps . COPY . /home/my-app WORKDIR /home/my-app ENTRYPOINT [ "plackup", "-s", "Starman", "/home/my-app/bin/my-app.psgi" ] =for newslide FROM perl:5.28 COPY bin . COPY cpanfile . COPY lib . **RUN** @@cpanm -n --installdeps .@@ COPY . /home/my-app WORKDIR /home/my-app ENTRYPOINT [ "plackup", "-s", "Starman", "/home/my-app/bin/my-app.psgi" ] =for newslide FROM perl:5.28 COPY bin . COPY cpanfile . COPY lib . RUN cpanm -n --installdeps . COPY . /home/my-app WORKDIR /home/my-app **ENTRYPOINT** [ @@"plackup", "-s", "Starman", "/home/my-app/bin/my-app.psgi"@@ ] =for newslide Once we have a C, we can use it to build a Docker container docker build . This will get a base-image (perl:5.28), install the deps (using C) and define the entrypoint (i.e. the default command to run) =for newslide ~/my-app$ docker build . Sending build context to Docker daemon 6.951MB Step 1/9 : FROM perl:5.28 ---> c58a7ea6dfc4 Step 3/9 : COPY cpanfile . ---> 08f6b3921404 Step 4/9 : COPY bin . ---> 9a15f057dfe5 Step 5/9 : COPY lib . ---> fe72cbd18d6c =for newslide Step 6/9 : RUN cpanm -n --installdeps . ---> Running in 9228475b9796 --> Working on . Configuring /root ... OK ==> Found dependencies: Starman, Net::SSLeay, Plack, IO::Socket::SSL, Class::Accessor::Fast, GitLab::API::v4 --> Working on Starman Fetching http://www.cpan.org/authors/id/M/MI/MIYAGAWA/Starman-0.4014.tar.gz ... OK ==> Found dependencies: Module::Build::Tiny ... <== Installed dependencies for .. Finishing. 73 distributions installed Removing intermediate container 9228475b9796 ---> 436719e71bc4 =for newslide Step 7/9 : COPY . /home/my-app ---> c351a090e154 Step 8/9 : WORKDIR /home/my-app ---> Running in 49957f85c20c Removing intermediate container 49957f85c20c ---> b94275bd2b3f Step 9/9 : ENTRYPOINT [ "plackup", "-s", "Starman", "--workers", "10", "/home/my-app/bin/my-app.psgi" ] ---> Running in 33eda3752fee Removing intermediate container 33eda3752fee ---> e4c72efae2ca Successfully built e4c72efae2ca =for newslide The first time this will take some time Later builds are much faster (unless things have changed) =for newslide Successfully built e4c72efae2ca e4c72efae2ca is a sucky name =for newslide ~/my-app$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE e4c72efae2ca 3 minutes ago 929MB =for newslide ~/my-app$ docker **tag** e4c72efae2ca **my-app** =for newslide ~/my-app$ docker tag e4c72efae2ca my-app ~/my-app$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-app latest e4c72efae2ca 4 minutes ago 929MB Now the image has a B (C) and also a default B: C You should always provide a name and a tag. Version numbers or release names are good tags: =for newslide ~/my-app$ docker **tag** e4c72efae2ca **my-app:v0.001** ~/my-app$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-app v0.001 e4c72efae2ca 4 minutes ago 929MB We can now run the app: =for newslide **docker run** -e FILESTORAGE_API_URL=http://localhost:3003 -p 7007:5000 \ **my-app:v0.001** =for newslide **docker run** @@-e@@ @@FILESTORAGE_API_URL@@=%%http://localhost:3003%% -p 7007:5000 \ **my-app:v0.001** And push it into our Docker Registry docker push registry.validad.net/winxle/winxle-mono/my-app:v.001 =head3 3: Orchestrate docker images: Kubernetes .-------------------------------------------------------. | Your Kubernetes Cluster | | | | .---------------------------------------------------. | | | Service | | | '---------------------------------------------------' | | .---------------------------------------------------. | | | Deployment | | | | .----------------------. .----------------------. | | | | | Pod | | Pod | | | | | | .------------------. | | .------------------. | | | | | | | docker-image | | | | docker-image | | | | | | | '------------------' | | '------------------' | | | | | '----------------------' '----------------------' | | | '---------------------------------------------------' | '-------------------------------------------------------' =for newslide .-------------------------------------------------------. | Your Kubernetes Cluster | | | | .---------------------------------------------------. | | | Service | | | '---------------------------------------------------' | | .---------------------------------------------------. | | | **Deployment** | | | | .----------------------. .----------------------. | | | | | Pod | | Pod | | | | | | .------------------. | | .------------------. | | | | | | | docker-image | | | | docker-image | | | | | | | '------------------' | | '------------------' | | | | | '----------------------' '----------------------' | | | '---------------------------------------------------' | '-------------------------------------------------------' =for newslide ~$ kubectl get deployments =for newslide ~$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE winxle-api 3 3 3 3 115d winxle-merchant-api 2 2 2 2 150d winxle-scripts-mailer 1 1 1 1 164d winxle-winner-web 2 2 2 2 157d =for newslide ~$ kubectl get deployment winxle-merchant-api -o yaml =for newslide =for include_code k_deployment.yml =for newslide kind: Deployment metadata: annotations: ... and ... much ... more ... yaml ... (123 lines in total) =for newslide metadata: labels: app: winxle-merchant-api chart: winxle-merchant-0.1.0 heritage: Tiller lang: Perl release: vorteilswelt-production-vtw version: @@20190301-160216-master@@ name: winxle-merchant-api namespace: vorteilswelt-production-vtw =for newslide spec: **template**: spec: containers: image: eu.gcr.io/wxl-vorteilswelt/winxle-merchant:20190301-160216-master name: winxle-merchant-api =for newslide spec: template: spec: containers: **image**: eu.gcr.io/wxl-vorteilswelt/@@winxle-merchant:20190301-160216-master@@ name: winxle-merchant-api =for newslide spec: template: spec: containers: **readinessProbe**: failureThreshold: 3 %%httpGet%%: path: @@/merchant-api/_status@@ port: http scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5 Only if the endpoint C returns success will a pod be considered usable =for newslide spec: **replicas**: @@2@@ strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate =for newslide spec: replicas: 2 **strategy**: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: **RollingUpdate** =for newslide .---------------------------------------------------. | Deployment | | .----------------------. .----------------------. | | | Pod | | Pod | | | | .------------------. | | .------------------. | | | | | docker-image | | | | docker-image | | | | | '------------------' | | '------------------' | | | '----------------------' '----------------------' | '---------------------------------------------------' =for newslide .---------------------------------------------------------------------------. | Deployment | | .----------------------. .----------------------. .----------------------.| | | **New Pod** | | Pod | | Pod || | | .------------------. | | .------------------. | | .------------------. || | | | @@next version@@ | | | | docker-image | | | | docker-image | || | | '------------------' | | '------------------' | | '------------------' || | '----------------------' '----------------------' '----------------------'| '---------------------------------------------------------------------------' =for newslide .---------------------------------------------------------------------------. | Deployment | | .----------------------. .----------------------.| | | New Pod | | Pod || | | .------------------. | | .------------------. || | | | next version | | | | docker-image | || | | '------------------' | | '------------------' || | '----------------------' '----------------------'| '---------------------------------------------------------------------------' =for newslide .---------------------------------------------------------------------------. | Deployment | | .----------------------. .----------------------. .----------------------.| | | New Pod | | **New Pod** | | Pod || | | .------------------. | | .------------------. | | .------------------. || | | | next version | | | | @@next version@@ | | | | docker-image | || | | '------------------' | | '------------------' | | '------------------' || | '----------------------' '----------------------' '----------------------'| '---------------------------------------------------------------------------' =for newslide .---------------------------------------------------------------------------. | Deployment | | .----------------------. .----------------------. | | | New Pod | | New Pod | | | | .------------------. | | .------------------. | | | | | next version | | | | next version | | | | | '------------------' | | '------------------' | | | '----------------------' '----------------------' | '---------------------------------------------------------------------------' =for newslide 100 lines of YAML to say: Please make sure we always have 2 instances of the Docker image C running. =head4 Referencing other services docker run -e @@FILESTORAGE_API_URL@@=**http://localhost:3003** -p 7007:5000 \ my-app:v0.001 "Manual Orchestration" =for newslide spec: template: spec: containers: - **env**: - name: @@FILESTORAGE_API_URL@@ value: http://filestorage:8080 =for newslide spec: template: spec: containers: - env: - name: FILESTORAGE_API_URL value: **http://filestorage:8080** =for newslide C You can use DNS for service discovery. C will resolve to the IPs of the Pods providing the Filestorage service =head4 Services .-------------------------------------------------------. | Your Kubernetes Cluster | | | | .---------. .---------. .---------. | | | Service | | Service | | Service | | | '---------' '---------' '---------' | '-------------------------------------------------------' =for newslide ~$ kubectl get service filestorage -o yaml kind: Service metadata: name: filestorage namespace: vorteilswelt-production-vtw spec: ports: - name: http port: 8080 protocol: TCP targetPort: http selector: app: filestorage =for newslide ~$ kubectl get service filestorage -o yaml kind: Service metadata: name: filestorage namespace: vorteilswelt-production-vtw spec: ports: - name: http port: 8080 protocol: TCP targetPort: http **selector:** @@app@@: %%filestorage%% =for newslide ~# kubectl get pods **--selector** @@app@@=%%filestorage%% NAME READY STATUS RESTARTS AGE vorteilswelt-production-vtw-filestorage-67dcd5944-tnpsc 1/1 Running 0 1d vorteilswelt-production-vtw-filestorage-67dcd5944-wmwvn 1/1 Running 0 1d =for newslide ~# kubeclt get pod vorteilswelt-production-vtw-filestorage-67dcd5944-tnpsc -o yaml kind: Pod metadata: generateName: vorteilswelt-production-vtw-filestorage-5456bd6ff8- **labels:** @@app:@@ %%filestorage%% release: vorteilswelt-production-vtw =head4 Handling secrets containers: - **env**: - name: @@PGRW_DSN@@ %%value%%: **dbi:Pg:dbname=winxle_prod;host=gcloud-sqlproxy;port=5432** - name: PGRW_PASSWORD valueFrom: secretKeyRef: key: password name: psql-winxle-merchant =for newslide containers: - **env**: - name: PGRW_DSN value: dbi:Pg:dbname=winxle_prod;host=gcloud-sqlproxy;port=5432 - name: @@PGRW_PASSWORD@@ %%valueFrom%%: **secretKeyRef**: key: password name: **psql-winxle-merchant** =for newslide B is another Object provided by Kubernetes to store secrets :-) =for newslide ~# kubectl @@get secrets@@ **psql-winxle-merchant** -o yaml data: password: %%Tm90VGhlUGFzc3dvcmQK%% kind: Secret metadata: name: psql-winxle-merchant namespace: vorteilswelt-production-vtw (base64 encoded...) =for newslide containers: - env: - name: @@PGRW_PASSWORD@@ %%valueFrom%%: **secretKeyRef**: key: password name: **psql-winxle-merchant** =head4 Ingress ~$ kubectl get ingress winxle-merchant-api -o yaml kind: Ingress spec: rules: - host: eltern-vorteilswelt.winxle.com http: paths: - backend: **serviceName**: @@winxle-merchant-api@@ servicePort: http path: /merchant-api/ =for newslide ~$ kubectl get ingress winxle-merchant-api -o yaml kind: Ingress spec: rules: - @@host@@: **eltern-vorteilswelt.winxle.com** http: paths: - backend: serviceName: winxle-merchant-api servicePort: http @@path@@: **/merchant-api/** =head4 Recap C links to C which links to C (which are managed by a C) linking happens via B