Building Images & Running Containers

End to End

  1. Find a base image: docker.io, quay.io, gcr.io, registry.redhat.io

  2. Craft your Dockerfile

  3. Configure docker: eval $(minikube --profile myprofile decoder-env)

  4. Build your image: docker build -t liulx/myimage:v1 .

    a. Test via:

    - `docker run -it -p 8080:8080 --name myboot liulx/myimage:v1`
    - `docker run -d -p 8080:8080 --name my boot liulx/myboot:v1`
    - `curl $(minikube --profile myprofile ip):8080`

    b. If remote repo, do docker tag and docker push

    c. docker stop containername to stop testing

  5. kubectl apply -f myDeployment.yaml

  6. kubectl apply -f myService.yaml

  7. Expose a URL via your kubernetes distribution’s load-balancer

docker build

1
docker build -t something/animagename:tag

The .indicates where to find the Dockerfile and assets to be included in the build process.

You can alse explicitly identify the Dockerfile:

  • docker build -t somestring/animagename:tag -f somedirectory/Dockerfile_Production .
  • docker build -t somestring/animagename:tag -f somedirectory/Dockerfile_Testing .
  • docker build -f src/main/docker/Dockerfile.native -t mystuff/myimage:v1 .

Builiding Images

Options Include:

  1. docker build then kubectl run or kubectl create -f deploy.yml
  2. Jib - Maven/Gradle plugin by google
  3. Shift maven plugin by Red hat
  4. s2i - source to image
  5. Tekton - pipeline-based image building
  6. No docker: red hat’s podman, Google’s kaniko, Uber’s makisu
  7. Buildpacks - similar to Heroku & Cloud Foundry

Namespaces

1
2
3
4
5
6
- `kubectl create namespace demo`
- `kubectl get namespace demo -o yaml`
- `kubectl get namesapce demo --export -o yaml`
- `kubectl delete namespace/demo`
- `kubectl config set-context --current --namespace=myspace`
- ` kubectl config view --minify --template '{{ index .contexts 0 "context" "namespace" }}'` confirm your context

Pods

1
2
3
4
5
6
7
8
9
10
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: quarkus-demo
spec:
containers:
- name: quarkus-demo
image: quay.io/burrsutter/quarkus-demo:1.0.0
EOF

ReplicaSets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-quarkus-demo
spec:
replicas: 3
selector:
matchLabels:
app: quarkus-demo
template:
metadata:
labels:
app: quarkus-demo
env: dev
spec:
containers:
- name: quarkus-demo
image: quay.io/burrsutter/quarkus-demo:1.0.0
EOF

See what it produced:

1
2
3
4
5
$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
rs-quarkus-demo-64dnl 1/1 Running 0 49s app=quarkus-demo,env=dev
rs-quarkus-demo-8pn8b 1/1 Running 0 29s app=quarkus-demo,env=dev
rs-quarkus-demo-ksm5f 1/1 Running 0 16s app=quarkus-demo,env=dev

Deployments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: quarkus-demo-deployment
spec:
replicas: 3
selector:
matchLabels:
app: quarkus-demo
template:
metadata:
labels:
app: quarkus-demo
env: dev
spec:
containers:
- name: quarkus-demo
image: quay.io/burrsutter/quarkus-demo:1.0.0
imagePullPolicy: Always
ports:
- containerPort: 8080
EOF

Run curl inside Pod: kubectl exec -it pod name -- curl localhost:8080

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: the-service
spec:
selector:
app: quarkus-demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: NodePort
EOF

curl those Endpoints behind that service:

1
2
3
$ IP=$(minikube --profile 9steps ip)
$ PORT=$(kubectl get service the-service -o jsonpath="{.spec.ports[*].nodePort}")
$ while true; do curl $IP:$PORT; sleep .5; done

Scale

Let’s scale up the application to 3 replicas, there are several possible ways to achieve this result.

You can edit the myboot-deployment.yml, updating replicas to 3

1
$ kubectl replace -f kubefiles/myboot-deployment.yml

Or use the kubectl scale command

1
$ kubectl scale --replicas=3 deployment/myboot

Or you might patch it

1
$ kubectl patch deployment/myboot -p '{"spec":{"replicas":3}}'

Or use the kubectl edit command which essentially gives you “vi” for editing the deployment yaml

1
2
3
$ kubectl edit deployment/myboot
/replicas
esc-w-q

Note: You can use VS Code as your editor with export KUBE_EDITOR=”code -w”

You can then use “kubectl get pods” to see the pods that have been created

1
$ kubectl get pods

Note: 3 pods might push you out of your memory limits for your VM. Check your memory usage with:

1
2
3
4
5
$ minishift -p 9steps ssh
# or
$ minikube ssh
$ free -m
$ top -o %MEM

Update

Update MyRESTController.java

1
greeting = environment.getProperty("GREETING","Bonjour");

Compile & Build the fat jar

1
mvn clean package

You can test with “java -jar target/boot-demo-0.0.1.jar” and “curl localhost:8080”. Ideally, you would have unit tests executed with “mvn test” as well.

Build the new docker image with a v2 tag

1
2
$ docker build -t 9stepsawesome/myboot:v2 .
$ docker images | grep myboot

Rollout the update

1
2
3
$ kubectl set image deployment/myboot myboot=9stepsawesome/myboot:v2
OR
$ kubectl set image deployment/myboot myboot=quay.io/burrsutter/myboot:v2

And if you were running your polling curl command, you might see

1
2
3
4
5
6
7
8
9
10
11
12
Aloha from Spring Boot! 346 on myboot-79bfc988c8-ttz5r
Aloha from Spring Boot! 270 on myboot-79bfc988c8-ntb8d
Aloha from Spring Boot! 348 on myboot-79bfc988c8-ttz5r
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
curl: (7) Failed to connect to 192.168.99.105 port 31218: Connection refused
Bonjour from Spring Boot! 0 on myboot-757658fc4c-qnvqx
Bonjour from Spring Boot! 1 on myboot-5955897c9b-zthj9

We will be working on those errors when we address Liveness and Readiness probes in Step 7

For now, undo the update, going back to v1

1
$ kubectl rollout undo deployment/myboot

Scale to 1

1
$ kubectl scale --replicas=1 deployment/myboot

Hit the sysresources endpoint

1
2
$ curl $(minikube -p 9steps ip):$(kubectl get service/myboot -o jsonpath="{.spec.ports[*].nodePort}")/sysresources
Memory: 1324 Cores: 2

Note: you can use echo to see what this URL really looks like on your machine

1
2
$ echo $(minikube -p 9steps ip):$(kubectl get service/myboot -o jsonpath="{.spec.ports[*].nodePort}")/sysresources
192.168.99.105:30479/sysresources

The results of the “sysresources” call should be about 1/4 memory and all the cores that were configured for the VM. You can double check this with the following command:

1
2
3
4
$ minikube config view
- cpus: 2
- memory: 6144
- vm-driver: virtualbox

Now, let’s apply resource contraints via Kubernetes & deployment yaml. Look at myboot-deployment-resources.yml

1
$ kubectl replace -f kubefiles/myboot-deployment-resources.yml

Curl sysresources again

1
2
$ curl $(minikube -p 9steps ip):$(kubectl get service/myboot -o jsonpath="{.spec.ports[*].nodePort}")/sysresources
Memory: 1324 Cores: 2

In another terminal window, watch the pods

1
$ kubectl get pods -w

and now curl the consume endpoint

1
$ curl $(minikube -p 9steps ip):$(kubectl get service/myboot -o jsonpath="{.spec.ports[*].nodePort}")/consume

and you should see a OOMKilled

1
2
3
4
5
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
myboot-68d666dd8d-m9m5r 1/1 Running 0 1m
myboot-68d666dd8d-m9m5r 0/1 OOMKilled 0 2m
myboot-68d666dd8d-m9m5r 1/1 Running 1 2m

And you can also see the OOMKilled with the kubectl describe command

1
2
3
4
5
6
7
8
9
10
$ kubectl describe pod -l app=myboot
...
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Tue, 31 Jul 2018 13:42:18 -0400
Finished: Tue, 31 Jul 2018 13:44:24 -0400
Ready: True
Restart Count: 1
...

JIB

1
2
3
4
5
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.6.1</version>
</plugin>

Build Docker Image and run:

1
2
3
4
eval $(minishift docker-env)
mvn compile jib:dockerBuild -Dimage=9stepsawesome/myboot:v1
docker run -it -p 8080:8080 9stepsawesome/myboot:v1
curl $(minishift ip):8080