Docker & Kubernetes 101
Published at Dec 20, 2018 better viewed as a slideshow
Docker
Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and ship it all out as one package. — OpenSource.com, What is Docker?
Infrastructure
Don’t worry about the system your application will ultimately run on.
Install Docker
Install at Docker’s getting-started
Setup Docker
First, ensure you are using an up-to-date docker engine. In the terminal,
- Run
docker info
and check for any errors - Run
docker --version
and ensure you have version >=17.06
Setup Docker Resources
Ensure you have set enough resources for Docker to run smoothly: In the taskbar, click the whale icon → Preferences → Advanced
(I set 6 CPUs and 8GB RAM for Docker)
Settings

World Smallest Image
Every single Docker container begins as a pure vanilla Linux machine that knows nothing.

Where It All Begins
A Docker image consists of read-only layers each of which represents a Dockerfile instruction. The layers are stacked and each one is a delta of the changes from the previous layer.
Images start as a fresh, isolated machine and the very same dependencies get added to it. Every. Single. Time.
FROM scratch
ADD hello /
CMD ["/hello"]
Build your First Image
docker build -t tiny-hello-world .
docker images
Run your First Container
docker run tiny-hello-world
docker ps -a
Clean After Yourself
Delete a container:
docker rm condescending_ptolemy
Next time, run container & delete after exit:
docker run --rm tiny-hello-world
Advanced
Delete all stopped containers:
docker container prune
Delete all dangling images:
docker image prune

My First Containerized Node Application
I recommend Dockerfile documentation. It’s very well written.
Dockerfile
Node service:
## creates a layer from the node:carbon Docker image
FROM node:8.14-alpine
## create the app directory for inside the Docker image
WORKDIR /usr/src/app
## copy and install app dependencies from the package.json (and the package-lock.json) into the root of the directory created above
COPY package*.json ./
RUN yarn install
## bundle app source inside Docker image
COPY . .
## expose port 8080 to have it mapped by Docker daemon
EXPOSE 3000
## define the command to run the app
CMD [ "yarn", "run", "start" ]
app.js
var express = require('express');
var app = express();
process.on('SIGINT', function () {
process.exit();
});
app.get('/', function (req, res) {
res.send("Hello World!
");
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
package.json
{
"name": "docker-101-scene-02",
"version": "1.0.0",
"main": "index.js",
"author": "Rafael Bodill",
"license": "MIT",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.16.4"
}
}
Let’s Build & Run
docker build -t test-node .
docker run -it --rm test-node
On a new terminal:
curl localhost:3000
Where’s my service? :(
Expose Ports to Host
docker run -it --rm -p 3000:3000 test-node
On a new terminal:
curl localhost:3000
Hooray!
Let’s Add a Route
Append to app.js
:
app.get('/healthz', function (req, res) { res.send('OK') })
Rebuild image:
docker build -t test-node .
docker run -it --rm test-node
Docker Layer Caching

Docker Layers
$ docker history test-node
IMAGE CREATED SIZE CREATED BY
d17562ebd02e 27 minutes ago 0B CMD ["yarn" "start"]
c24c2a8c7a2a 27 minutes ago 0B EXPOSE 3000
001439668f54 27 minutes ago 1.78MB COPY dir:0dc6500de44402cb2…
24e151c2462b 27 minutes ago 7.13MB yarn install
9dd639668f68 27 minutes ago 226B COPY file:b365e33dfa656564…
a93bcafb8a76 27 minutes ago 0B WORKDIR /usr/src/app
.. Wait .. Our app.js
is 1.78MB?
Ignoring Files
Create a .dockerignore
file with:
.git
.dockerignore
*.md
*.png
Dockerfile
node_modules
Rebuild image:
docker build -t test-node .
docker history test-node
Now it’s better.

Linux Containers
Images vs. Containers
Docker images are executable packages that include everything needed to run an application — the code, a runtime, libraries, environment variables, etc.
Docker containers are a runtime instance of an image — what the image becomes in memory when executed.
Examples of Docker containers. Each one comes from a specific Docker image.

Entering Container’s Shell
docker run -d --name bob -p 3000:3000 test-node
docker exec -it bob sh
You are now INSIDE the matrix container!
> netstat -ntulp
> wget -q -O- localhost:3000
> top
> free -m
> exit
Running Different Commands
docker run --rm -it test-node sh
docker run --rm -it test-node ls -alp
View Container Information
docker inspect bob | less
docker logs -f bob
docker stats bob
Manage State
docker stop bob
docker start bob
docker restart bob
docker rm -f bob

Private Services
docker run --rm -p 3000:3000 artifactory.local/myapp
Oh oh. What’s missing here?
Mount Volumes
Let’s mount our configuration from a different Git repository:
git clone git@git.local:mycompany/myconfig.git
docker run --rm -v $PWD/myconfig:/etc/myapp
-p 3000:3000 artifactory.local/myapp
Dog-gone it. What else are we missing?
Environment Variables
Oh. Our environment variable! It tells the configuration where to read from:
docker run --name bob -v $PWD/myconfig:/etc/myapp
-e MYAPP_CFG_ROOT=/etc/myapp -p 3000:3000
artifactory.local/myapp
Viewing Logs
In a different terminal,
docker logs -f bob
docker exec -it bob tail -f /var/log/myapp/api.log

Simplifying Workspaces
Docker Compose offers a number of benefits over the Docker CLI in all stages of development.
Essentially, what Docker Compose is, is a recipe card — it’s a recipe of services that make up an application and the docker-compose.yml dictates how the services are mixed together.
Simple Example
Let’s inspect our docker-compose.yml
file:
version: "2"
services:
myapp:
image: artifactory.local/myapp
environment:
MYAPP_CFG_ROOT: /etc/myapp
ports:
- "3000:3000"
volumes:
- ./myconfig:/etc/myapp
Running Your Composition
git clone git@git.local:mycompany/myconfig.git
docker-compose up
On a different terminal:
curl localhost:3000
Let’s Add (3rd)Parties!
version: "2"
services:
myapp:
image: artifactory.local/myapp
environment:
MYAPP_CFG_ROOT: /etc/myapp
ports:
- "3000:3000"
volumes:
- ./myconfig:/etc/myapp
redis:
image: "redis:alpine"
Let’s run it:
docker-compose up
Whoa!
Give me morrrre!
version: "2"
services:
myapp:
image: artifactory.local/myapp
environment:
MYAPP_CFG_ROOT: /etc/myapp
ports:
- "3000:3000"
volumes:
- ./myconfig:/etc/myapp
redis:
image: "redis:alpine"
mysql:
image: "mysql:5.7"
environment:
MYSQL_ROOT_PASSWORD: root
Let’s run it:
docker-compose up
Access Services
## Access MySQL:
docker exec -it scene-05_mysql_1 mysql -u root -p
## Access Redis:
docker exec -it scene-05_redis_1 redis-cli GET /
More on Docker-Compose
docker-compose ps
- Lists all services in network.docker-compose up
- Brings up the network & services.docker-compose stop
- Stops the network, saves state of services.docker-compose start
- Restarts the services.docker-compose down
- Burn it all down! Destroy network & services.

Kubernetes (K8S) 101
https://docs.google.com/presentation/d/1BhUfwkiRTQZsvN9kCpxpbT09kpWFTiuAprOfGemrf6Q
Workstation Setup
brew uninstall --force bash-completion
brew install kubernetes-cli kubectx fzf stern bash-completion@2
Append to your ~/.bash_profile
for awesome shell completion:
if [ -f /usr/local/share/bash-completion/bash_completion ]; then
. /usr/local/share/bash-completion/bash_completion
fi
Setup kubectl Context
Cluster + User = Context
## Integration
kubectl config set-cluster foobar-int-developer --server=https://1.2.3.4:6443 --certificate-authority=$HOME/.kube/int/ca.pem
kubectl config set-credentials int-developer --client-certificate=$HOME/.kube/int/developer.crt --client-key=$HOME/.kube/int/developer.key
kubectl config set-context foobar-int-developer --cluster=foobar-int-developer --user=int-developer
## Staging
kubectl config set-cluster foobar-stg-developer --server=https://5.6.7.8:6443 --certificate-authority=$HOME/.kube/stg/ca.pem
kubectl config set-credentials stg-developer --client-certificate=$HOME/.kube/stg/developer.crt --client-key=$HOME/.kube/stg/developer.key
kubectl config set-context foobar-stg-developer --cluster=foobar-stg-developer --user=stg-developer
kctx
kubectl config use-context foobar-int-developer
kctx
kctx foobar-int-developer
kctx foobar-stg-developer
kubectl Cheat-sheet
kubectl get namespaces
kubectl get deploy
kubectl get -n ingress-nginx deploy
kubectl get -n kube-system deploy
kubectl get --all-namespaces deploy
kubectl get pods -w
kubectl get pods -o wide
kubectl get events
kubectl get pods,svc,deploy
kubectl describe deploy push-service
kubectl top node
kubectl top pods
kubectl logs -f deploy/trips
kubectl logs -f deploy/trips -c trips
kubectl logs --previous trips-7d7f649996-nz876
kubectl exec -it trips-6c6887547d-lhbsb -- tail -f /var/log/myapp/api.log
Examining Deployed Objects
kubectl get deploy
kubectl get pod
kubectl get svc
kubectl get deploy,pod,svc | grep myapp
kubectl get all | grep myapp
Describe Deployment
kubectl describe deploy myapp
kubectl describe pod myapp-649887b547-fh7vw
kubectl top pod | grep myapp
Analyze Logs
kubectl logs -f myapp-649887b547-fh7vw
kubectl exec -it myapp-649887b547-fh7vw -- tail -f /var/log/myapp/api.log
Jump into Container
$ kubectl exec -it myapp-649887b547-fh7vw sh
> top
> free -h
> exit
