1. The presentation introduces Docker, Kubernetes, and Envoy as foundational tools for building microservices. Docker allows packaging applications into portable containers, Kubernetes provides a platform to manage containers across clusters of hosts, and Envoy handles traffic routing and resilience at the application layer.
2. The presenters demonstrate how to build a simple Python web application into a Docker container image. They then deploy the containerized application to a Kubernetes cluster using Kubernetes objects like deployments and services. This allows the application to scale across multiple pods and be accessed via a stable service endpoint.
3. Finally, the presenters note that as applications become distributed across microservices, failures at the application layer (L7) become more common and
2. datawire.io
Before we begin ...
โ Youโre running latest version of Docker on your laptop
โ Youโve created an account on hub.docker.com
โ Youโve downloaded some key Docker images
โ docker pull datawire/ambassador-envoy
โ docker pull prom/prometheus
โ docker pull python:3-alpine
โ Youโve set up a working environment (weโve pre-installed everything for you)
โ Ubuntu Docker image
โ git clone https://github.com/datawire/shopbox
โ cd shopbox
โ ./shopbox.sh
โ You have your favorite terminal & text editor ready to go!
Go to the presentation here: https://d6e.co/2xQyXzN
2
3. datawire.io
About us
โ Been working with microservices for past 2.5 years, both with our own cloud services
and with consulting
โ We build open source tools for developers building microservices, based on our own
experiences
โ Host microservices.com, which has talks from dozens of practitioners on
microservices
3
4. datawire.io
Introduction: Microservices,
Kubernetes, Docker, Envoy
20 minutes Presentation
Core Concepts of Docker &
Kubernetes
70 minutes Workshop
Break around 3pm 30 minutes
Building & deploying a
microservice in production
60 minutes Workshop
Wrap-up 20 minutes Presentation / Q&A
Feedback 5 minutes
Our schedule for today
4
5. datawire.io
Our focus today
1. Trying to communicate the key concepts
2. Minimize time spent on the mechanics, since theyโre impossible to remember until
you do them often enough
3. Try to give you a mental model to understand the different (overwhelming) choices
in the Kubernetes ecosystem, and how to start.
4. If you donโt understand the purpose of a specific exercise, please ask!
Also โฆ
Minikube is a popular choice for these trainings, since you can run things locally. But
minikube is a big performance hog and isnโt quite as realistic with some of the things
weโre doing, so weโre going to try to use the Internet. (We have done some work to
minimize bandwidth consumption.)
5
9. Take the standard development process:
Code
(Dev)
Test
(QA)
Prod
(Ops)
Release
(Release)
Define
(Product)
10. 1
0
โฆ and make it distributed.
> No central release, test, dev cycle. Each person/team operates
an independent development process.
> Team needs to have the skills / knowledge to operate all
aspects of the development process.
Service A Service B Service C
11. 1
1
Microservices is a distributed
development process for cloud-native
software.
> Not an architecture! Your architecture supports
the process, and not the other way around.
13. Start by creating an independent process!
(with a from-scratch API service)
New serviceMonolith
independent
process!!
(think spinning off a
developer or dev team)
14. 1
4
1. Self-sufficiency. Each team needs to be self-sufficient in all aspects
of the development cycle to avoid bottlenecks.
2. Safety. Since itโs hard to be an expert in every area of the
development, need to insure an oops isnโt catastrophic.
Give the developer / dev team the ability to
SUCCESSFULLY operate independently.
15. The definition of self-sufficient safety varies based on
the evolution of your microservice.
Stage 1:
Prototyping
Stage 2:
Production
Stage 3: Internal
service
consumption
Service doesnโt crash
Workflow for prod
deploys, monitoring,
& debugging
No cascade failures
Productive dev
environment
Transparently add
service resilience
No negative user
impact
Self
sufficiency
Safety
17. 17
1. Microservices is a distributed development workflow
that helps you go faster.
2. An efficient workflow for your team provides
self-sufficiency and safety.
3. The Kubernetes ecosystem, Docker, and Envoy provide
the foundational components you need to build that
workflow.
20. datawire.io
What is a container?
โ Lightweight Linux environment. It is a form of virtualizationโฆ but very different
from a full virtual machine.
โ Immutable, deployable artifact.
โ Runnable.
โ Popularized by Docker but there are many runtime implementations (e.g. LXC,
Rkt).
20
21. datawire.io
What is Docker?
โ A tool, ecosystem and platform for building, pushing and
running containers.
โ The most popular container runtime currently.
โ Default container runtime in Kubernetes.
21
22. datawire.io
Why Containers?
โ Easy and fast to produce.
โ Great way to isolate different components in a complex system.
โ Ensures a reproducible runtime for your app along the dev -> build -> test -> prod
pipeline.
โ Easy to share in a team or with external partners.
22
24. datawire.io
Letโs Get Started...
โ Weโve built an Ubuntu container image for you
โ Includes all the client-side tools weโll use for the training (e.g., kubectl)
โ Weโll use Kubernaut for Kubernetes clusters
โ On-demand, ephemeral clusters (designed for CI โฆ or training!)
โ The container mounts a local directory into /workspace in
the image your files are synchronized with your laptop.
24
25. datawire.io
Letโs Get Started...
Run the below commands in your terminal if you havenโt
already:
$ git clone https://github.com/datawire/shopbox
$ cd shopbox
$ ./shopbox.sh
Pre-configured dev environment with kubectl (with tab completion),
kubernaut, and various utilities we will use today.
25
27. datawire.io
Letโs build a service as a container
A simple web application: Quote of The Moment (โQOTMโ).
โ Requires Python
โ Uses Flask
GET STARTED
$ git clone https://github.com/datawire/qotm
$ cd qotm
27
28. datawire.io
The Dockerfile
FROM python:3-alpine
MAINTAINER Datawire <dev@datawire.io>
LABEL PROJECT_REPO_URL = "git@github.com:datawire/qotm.git"
PROJECT_REPO_BROWSER_URL = "https://github.com/datawire/qotm"
DESCRIPTION = "(Obscure) Quote of the Moment!"
VENDOR = "Datawire, Inc."
VENDOR_URL = "https://datawire.io/"
WORKDIR /service
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . ./
EXPOSE 5000
ENTRYPOINT ["python3", "qotm/qotm.py"]
28
29. datawire.io
Letโs build it!
Run the below command to build the image (the trailing period on the below
command is required!):
$ docker build -t <your-docker-user>/qotm:1 .
29
Each Docker image consists of layers. A layer is an ordered union of files
and directory changes to an image.
Because layers are cached, putting the parts of the Dockerfile least likely to
change first (e.g., the OS) can make a huge difference in build speed.
30. datawire.io
What Just Happened?
1. Docker executed the instructions in the Dockerfile. Each command created a new
layer.
2. Docker composes an image from all the layers.
3. The docker engine pointed a named reference
<your-docker-user>/qotm:1 at the final image.
30
31. datawire.io
Tagging is Important and Useful
โ Tags allow you to easily reference and reuse an image.
โ You can create multiple tags to point at the same image which can be useful in
sharing contexts.
โ Tags can be pushed to a Docker registry so other people can reuse your image!
31
32. datawire.io
Run the image!
Images are an inert, immutable file. When you want to run an image, a container is
produced.
RUN THE IMAGE
$ docker run --rm -dit -p 5000:5000 <your-docker-user>/qotm:1
# open another shell (that is *not* running shopbox)
$ curl localhost:5000
32
33. datawire.io
Share the image
Weโve got an image running on your laptop, but you really want to share it -- with
Kubernetes, with your colleagues, with your family & friends ...
PUSH THE IMAGE
$ docker login
$ docker push <your-docker-user>/qotm:1
33
You can see the image on your public Docker Hub account at
https://hub.docker.com.
34. datawire.io
One last thing ...
Docker does a great job of running the container. Why canโt I just use Docker
everywhere?
WHAT HAPPENS IF WE CRASH?
$ curl localhost:5000/crash
$ curl localhost:5000
Uh oh โฆ we need something that is a little bit smarter!
34
36. datawire.io
What is Kubernetes?
โ Runs massive numbers of containers based on
lessons learned by Google.
โ Schedules and runs all kinds of containers
โ long-lived (e.g. services)
โ short-lived (e.g. pre-launch hooks, cronjobs etc)
โ Kubernetes can be thought of as a Distributed OS or process manager
36
37. datawire.io
The Office Tower Analogy
Your product is the building
as a whole.
Your business logic is
the offices and workers
37
38. datawire.io
The Office Tower Analogy
Kubernetes provides the
infrastructure to build your
app around.
38
It is the foundational app
platform for your team to
build your businesses apps
around.
39. datawire.io
Kubernetes Architecture
Types of nodes: Masters and Workers
39
Docker Kubelet
Kubeproxy
Kubernetes Node
Docker Kubelet
Kubeproxy
Kubernetes Node
Docker Kubelet
Kubeproxy
Kubernetes Node
Etcd API Server
Controller Manager
Kubernetes Master
Scheduler
40. datawire.io
4 basic concepts
40
Container packages your code in
a portable way
Pod gives your code a temporary
home inside the cluster
Deployment keeps your code
running, even when it is updated
Service provides a stable address
that can reach many pods
42. datawire.io
Kubernaut
โ You can get your own Kubernetes cluster easily with Google, Microsoft etc.
โ Or you can install Kubernetes yourself in AWS
โ To simplify things, weโre going to let you borrow some of our Kubernetes clusters
:)
โ Weโre going to use Kubernaut which provides on-demand K8S clusters for our
internal CI/CD systems.
42
46. datawire.io
Letโs run our container
$ kubectl run qotm --image=<your-docker-user>/qotm:1
$ kubectl get pods
We see a pod! How do we talk to the pod?
46
Pod gives your code a temporary
home inside the cluster
47. datawire.io
We need to talk to the pod!
We can tell Kubernetes to forward requests from outside the cluster to the pod, and
vice versa.
$ kubectl port-forward <pod-name> 5000 &
$ curl localhost:5000
47
48. datawire.io
What happens when a pod crashes?
48
Letโs crash the pod again.
$ curl localhost:5000/crash
$ curl localhost:5000
Note: donโt run this loop too often. If you crash your server too frequently, Kubernetes
will assume it is having deeper problems, and will introduce a delay before attempting
to restart.
49. datawire.io
What just happened?
The Kubernetes pod automatically restarted the container!
โ By default, Kubernetes will detect failures and auto-restart (with exponential
backoff, capped at 5 minutes)
โ Kubernetes also lets you extend this with custom liveness and readiness probes
49
50. datawire.io
Managing pods
โ What if we want to update the software on our pod?
โ What if we want more than one pod running, for availability or scalability
reasons?
50
Deployment keeps your code
running, even when it is updated
52. datawire.io
Letโs try using a deployment
52
$ kubectl get pods
$ kubectl delete deployment qotm
$ kubectl apply -f kubernetes/qotm-deployment.yaml
$ kubectl get pods
We see weโre now running three pods!
To save bandwidth, this
deployment.yaml points to a
prebuilt QOTM image weโve
already uploaded. Feel free to
edit it to point to your Docker
repo.
53. datawire.io
How do we talk to these pods?
It would be silly to set up port-forwards to each pod โฆ and load balancing would be
nice.
53
Service provides a stable address
that can reach many pods
55. datawire.io
Services Illustrated
A Service becomes a DNS A record pointing the pod IP addresses
55
IP: 100.124.71.175
blog-0
kube-worker-0
IP: 100.124.71.176
blog-1
kube-worker-1
Kubernetes cluster
blog DNS (short) => blog
DNS (long) => blog.default.cluster.local
56. datawire.io
Service Flavors
โ Many different flavors of โServiceโ in Kubernetes
โ ClusterIP
โ NodePort
โ LoadBalancer
โ ExternalName - often forgotten, but very useful!
56
57. datawire.io
Letโs try using a service
57
$ kubectl apply -f kubernetes/qotm-service.yaml
$ kubectl get services # get qotm port highlighted below
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.96.0.1 <none> 443/TCP 2d
qotm 10.107.109.252 <nodes> 80:<port>/TCP 6s
$ kubectl cluster-info # get cluster hostname
$ curl http://<cluster-hostname>:<port>
# or use this script for convenience:
$ curl $(url.sh qotm)
58. datawire.io
Recap: The source -> Kubernetes workflow
58
A
Build a container image that contains your code,
dependencies, and configuration, based on the Dockerfile.
B Tag the image.
C Push image to a container registry.
D Update Kubernetes manifest with tag.
E Apply Kubernetes manifest to cluster.
F Repeat for all dependencies.
59. datawire.io 59
Container packages your code in
a portable way
Pod gives your code a temporary
home inside the cluster
Deployment keeps your code
running, even when it is updated
Service provides a stable address
that can reach many pods
62. The definition of self-sufficient safety varies based on
the evolution of your microservice.
Stage 1:
Prototyping
Stage 2:
Production
Stage 3: Internal
service
consumption
Service doesnโt crash
Workflow for prod
deploys, monitoring,
& debugging
No cascade failures
Productive dev
environment
Transparently add
service resilience
No negative user
impact
63. datawire.io
Measuring user impact is a L7 problem!
โ What is L7?
โ We really mean application-level protocols
โ HTTP, gRPC, Thrift, redis://, pgsql://, ...
โ In a microservices architecture, your services are an L7 network
โ For you to write a service that talks to your users and/or other services, you need
to understand & manage L7
63
64. datawire.io
L7 is now a development concern
โ Everything has always been wired together with L7
โ But in development, you could leave it (mostly) to operations
โ Now, with microservices, L7 is a development concern as well:
โ More services
โ More remote dependencies
โ Greater release frequency
โ So what does this mean for you?
64
65. datawire.io
You: stuck in the middle
โ Users consume your service over L7
โ You consume your dependencies over L7
โ Literally everything in this picture is a source of failure
65
Layer 7
66. datawire.io
All your distributed systems problems are amplified
โ Single points of failure
โ Catastrophic failure modes
โ Cascade failures
66
67. datawire.io
In plain terms
67
Layer 7
Users can
DDOS you
Your dependencies can
fail
Your hardware can failYour hardware can fail
You ship a buggy
update
68. datawire.io
In plain terms
68
Layer 7
Users can
DDOS you
Your dependencies can
fail
Your hardware can failYour hardware can fail
You ship a buggy
update
(rate limiting) (circuit breakers, timeouts, etc.)(software level redundancy)
69. datawire.io
How do we protect us from ourselves?
โ If all our redundant hardware runs the same code, our own bugs quickly become
the biggest source of catastrophic failure
69
70. datawire.io
Create software level redundancy
โ Redundant hardware protects us from mechanical failures
โ We need redundant software implementations to protect us from our own bugs
โ Canary testing is the most basic version of this
โ run multiple versions of your code to improve resiliency (like genetic diversity)
Envoy helps us do this as well, but we need to wire it into our developer workflow
โ This is what we will focus on
70
71. datawire.io
Envoy
โ Modern, L7 proxy, designed for distributed cloud architectures
โ L7 observability
โ Resilience
โ Global rate limiting
โ Advanced load balancing
โ Circuit breakers
โ HTTP/2 & gRPC support
โ APIs for managing fleets of Envoys
โ Adopted by the CNCF (which also hosts Kubernetes, Prometheus, Docker, among
other projects)
โ Originally written by the engineering team at Lyft, and now with committers from
Google, IBM, Datawire, and others
โ Alternatives: NGINX Plus, HAProxy
71
72. datawire.io
First, some setup
โ Weโre going to deploy multiple times from source to prod
โ This means
โ Your deployment file needs to be templated, so you can point to multiple different versions in
production
โ You donโt want to manually run the docker build, etc commands by hand
โ So weโre going to use a simple open source tool, Forge, to automate this process
โ Weโve already set up the QOTM service to support Forge, which means:
โ A service.yaml file that specifies the service name
โ A Jinja2-templated Kubernetes manifest
โ So run forge setup from your workspace directory to configure Forge for your
environment
72
73. datawire.io
Now, we can automatically deploy
# letโs delete the original services
$ kubectl delete svc qotm
$ kubectl delete deploy qotm
# deploy from source to cluster, using templated k8s manifest
$ forge deploy
73
74. datawire.io
Canary testing
โ Route X% of your traffic to new version
โ Monitor your metrics to make sure no difference between old version and new
version
โ Gradually ramp up traffic to new version
Benefits
โ Immediate rollback to old version
โ Minimize impact of any error
Costs
โ Need extra capacity for canary testing
โ Need a L7 router (you can only do coarse canaries with K8S)
74
75. datawire.io
Letโs deploy Envoy!
At the top-level workspace directory (adjacent to your existing QOTM service)
$ git clone https://github.com/datawire/envoy-canary.git
$ forge deploy
$ API=$(url.sh api)
$ curl $API/qotm/ # donโt forget trailing slash
75
76. datawire.io
What did we just do?
โ We deployed:
โ Envoy, pre-configured to collect stats, and work with Kubernetes
โ A (stub) auth service, which Envoy calls on a per request basis
โ We are now routing to your QOTM through Envoy
โ QOTM we made a ClusterIP service instead of a NodePort
โ If we are having bandwidth problems, we can manually deploy the following
images:
โ datawire/auth-training:1
โ datawire/envoy-training:1
76
77. datawire.io
Letโs set up monitoring with Prometheus
$ git clone https://github.com/datawire/prometheus-canary
$ forge deploy
$ url.sh prometheus
In your browser, visit http://<prometheus-url>.
77
78. datawire.io
Now, letโs deploy a canary
In qotm/qotm.py, add a time.sleep(0.5) to the statement() function.
$ CANARY=true forge deploy
$ while true; do curl $API/qotm/; done
78
79. datawire.io
Monitor the canary
In Prometheus, execute this query:
{__name__=~"envoy_cluster_qotm_upstream_rq_time_timer|envoy_cluste
r_qotm_canary_upstream_rq_time_timer"}
Hit execute periodically to see changes (you might also want to reduce the granularity
of the time window to 5 minutes).
79
81. datawire.io
The story so far ...
1. Adopting a fast, distributed workflow is critical to accelerating productivity.
2. Start building your workflow by thinking about the single developer/team, for a
single service.
3. We showed how Kubernetes, Docker, Envoy, and monitoring (e.g., Prometheus)
can be used to build your workflow.
4. Your workflow depends on the stage of your service.
5. Managing L7 is really important, and gives you new, critical capabilities such as
canary testing and transparent monitoring.
81
82. 5
Analyze metrics by collecting them from Envoy and
adding to Prometheus.
82
Stage 1
prototyping
workflow
Stage 2
production
workflow
Recapping the workflow
1 Bootstrap the service. Clone a GitHub repo.
2 Code.
3
Run your code (in a dev Kube cluster). Docker build,
Kubernetes manifest, etc.
4
Deploy to production Kubernetes cluster. Canary
routing via Envoy.
84. Service mesh
Stage 1:
Prototyping
Stage 2:
Production
Stage 3: Internal
service
consumption
Service doesnโt crash
Workflow for prod
deploys, monitoring,
& debugging
No cascade failures
Productive dev
environment
Transparently add
service resilience
No negative user
impact
85. datawire.io
Service meshes
โ When you have stage 3 services, you want to think about a service mesh
โ But you should start with 1 service, so donโt worry about the service mesh right away!
โ Provide two critical functions
โ Observability (e.g., tracing) across all of your services
โ Resilience across all of your services
โ Function by deploying a sidecar proxy (e.g., Envoy) next to each of your services
โ Use iptables or equivalent to insure all service egress traffic is routed through
sidecar
โ Sidecar adds in trace IDs, circuit breaking, etc.
85
87. datawire.io
Stateful services
โ Databases and such can be deployed with a Kubernetes manifest -- same
technique as Envoy or the existing services, but a different configuration
โ Standard canary testing doesnโt work as well for stateful services
โ Envoy supports shadowing of requests
โ (Weโre working on this so itโs more useable)
โ If you have non-K8S resources (e.g., AWS RDS, etc.) consider adding the
Terraform/Ansible/etc. Scripts for creating these resources in another folder as
part of your standard service
87
88. datawire.io
Organizational adoption
โ Build an API service, just like Stripe or Twilio
โ Staff with a single, spinoff team
โ Define the purpose of the service from the perspective of the user
โ Donโt allow the service team to make any changes to the existing code base, or
vice versa
88
89. datawire.io
Thank you!
โ (Anonymous) feedback survey -- would be VERY grateful if you could spend 5
minutes filling it out so we can get better
โ https://d6e.co/2yxVnmn
โ Feel free to email us:
โ richard@datawire.io
โ rhs@datawire.io
โ plombardi@datawire.io
โ If youโre interested in any of our open source tools, check them out:
โ https://forge.sh for deployment
โ https://www.telepresence.io for fast development cycles
โ https://www.getambassador.io easiest way to deploy/configure Envoy on Kubernetes
89
91. datawire.io
Testing microservices
โ Traditional approach to testing a microservices architecture is with a staging
environment
โ First push services to staging
โ Then run integration tests
โ If tests pass, then push to production
โ But this introduces a big tradeoff
โ In order to do a โtrueโ integration testing, you need to synchronize your different service versions
in staging โฆ and push those versions into production
โ But this introduces a bottleneck!
โ So you want to think about more distributed strategies for integration testing
91