kubectl config current-context # what cluster
kubectl version # client/server versions
kubectl version --short # readable client server version
kubectl get nodes # list cluster nodes
kubectl describe nodes # lots of node info
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl get pods -w # watch list change
kubectl get pods -o wide # more information
kubectl get pod mypod # single pod
kubectl describe pods # more detail pod info/events
kubectl get pod mypod -o yaml # single pod full yaml
kubectl get pod mypod -o json # single pod full json
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
args:
- /bin/echo
- "Hello World!"
kubectl create -f pod.yaml # create from declarative configuration (or apply...)
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (notice args shown)
kubectl logs mypod # show pod stdout/err
Sadly the naming is inconsistent/confusing:
Description: | Docker: | Kubernetes:
--------------------|---------------|-------------
command to run | entrypoint | command
command arguments | cmd | args
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
- '/bin/echo "Hello World!"'
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (shows command)
kubectl logs mypod # show pod stdout/err
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
args:
- '/bin/echo "Hello World!"'
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (command + args now)
kubectl logs mypod # show pod stdout/err
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
args:
- '/bin/echo "${MESSAGE}"'
env:
- name: MESSAGE
value: "Hello DFWUUG!"
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (command + args now)
kubectl logs mypod # show pod stdout/err
Look around a running pod.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
args:
- '/bin/echo "${MESSAGE}" && sleep 1000'
env:
- name: MESSAGE
value: "Hello DFWUUG!"
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (updated command + args now)
kubectl logs mypod # show pod stdout/err
Like docker exec - enter a running container.
kubectl exec -i -t mypod /bin/sh
ls # works as expected
df -h # mix of container file systems and external
ps -ef # only pod processes are visible
free -m # shows node memory info
uptime # for node...
Labels are selectors that can be used by Kubernetes (more later). Annotations are like labels, but for other tools and libraries.
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
args:
- '/bin/echo "${MESSAGE}" && sleep 1000'
env:
- name: MESSAGE
value: "Hello DFWUUG!"
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (see labels and annotations...)
Convenient, but labels are really more for use by Kubernetes to do useful things.
kubectl get pods -l app=hello # returns mypod
kubectl get pods -l app=lol # no resources found
Resource requests specifiy a minimum (used by Kubernetes for bin-packing). Resource limits specifiy a maximum allowed.
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: busybox
name: hello
command:
- '/bin/sh'
- '-c'
args:
- '/bin/echo "${MESSAGE}" && sleep 1000'
env:
- name: MESSAGE
value: "Hello DFWUUG!"
resources:
requests:
cpu: "256m"
memory: "64Mi"
limits:
cpu: "1024m"
memory: "256Mi"
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (notice cpu/memory resource spec)
kubectl describe nodes # show node details (notice resource utilization)
Expose container TCP/UDP ports
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods
kubectl describe pods # more detail pod info/events (notice port details)
Connect to pod ports over a secure tunnel with kubectl port-forward
kubectl port-forward mypod 8080:80 # now browsable on http://localhost:8080
Kubectl cp copies files and directories to a running container. Replace the default Nginx index.html with something else.
<html>
<head>
<title>Hello DFWUUG!</title>
</head>
<body>
<h1>Hello DFWUUG!</h1>
</body>
</html>
kubectl cp index.html mypod:/usr/share/nginx/html/index.html
View the updated content in a browser:
kubectl port-forward mypod 8080:80 # now browsable on http://localhost:8080
The content is not persistent and disappears when the pod restarts.
kubectl exec -i -t mypod /bin/bash
kill 1
Check the content in a browser:
kubectl port-forward mypod 8080:80 # now browsable on http://localhost:8080 - back to default nginx page
Persists as long as a pod is on the same node.
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
volumeMounts:
- name: myhtml
mountPath: /usr/share/nginx/html
volumes:
- name: myhtml
emptyDir: {}
kubectl create -f pod.yaml # create from declarative configuration
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080 ... no content.. emptyDir
kubectl cp index.html mypod:/usr/share/nginx/html/index.html
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080 - custom content as expected
kubectl exec -i -t mypod /bin/bash
kill 1
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080 - custom content still exists
Deleting the pod destroys the emptyDir volume.
Volumes can be shared between containers in a pod.
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
volumeMounts:
- name: myhtml
mountPath: /usr/share/nginx/html
- image: busybox
name: datewriter
command:
- '/bin/sh'
- '-c'
args:
- 'while true ; do date > /content/index.html ; sleep 1 ; done'
volumeMounts:
- name: myhtml
mountPath: /content
volumes:
- name: myhtml
emptyDir: {}
kubectl create -f pod.yaml # create from declarative configuration
kubectl get pods # list running pods (notice 2/2)
kubectl describe pods # more detail pod info/events (notice details for each container)
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080 - updating content
Containers in the same pod share localhost.
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
volumeMounts:
- name: myhtml
mountPath: /usr/share/nginx/html
- image: busybox
name: datewriter
command:
- '/bin/sh'
- '-c'
args:
- 'while true ; do date > /content/index.html ; sleep 1 ; done'
volumeMounts:
- name: myhtml
mountPath: /content
- image: jcdemo/flaskapp
name: flaskapp
ports:
- name: flask
containerPort: 5000
volumes:
- name: myhtml
emptyDir: {}
kubectl create -f pod.yaml # create from declarative configuration
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080 - updating content
kubectl port-forward mypod 5000 # browsable on http://localhost:5000 - flask app content
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api/ {
proxy_pass http://localhost:5000/;
}
}
kubectl cp default.conf mypod:/etc/nginx/conf.d/default.conf -c hello
kubectl exec -i -t mypod -c hello /bin/bash
kill -HUP 1
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080/ - same date content
# browsable on http://localhost:8080/api/ - flask content
Store configuration as a Kubernetes object.
kubectl create configmap --from-file=default.conf my-nginx-config
kubectl get configmaps # list configmaps
kubectl get configmap my-nginx-config -o yaml # display configmap content
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
volumeMounts:
- name: myhtml
mountPath: /usr/share/nginx/html
- name: nginx-config
mountPath: /etc/nginx/conf.d
- image: busybox
name: datewriter
command:
- '/bin/sh'
- '-c'
args:
- 'while true ; do date > /content/index.html ; sleep 1 ; done'
volumeMounts:
- name: myhtml
mountPath: /content
- image: jcdemo/flaskapp
name: flaskapp
ports:
- name: flask
containerPort: 5000
volumes:
- name: myhtml
emptyDir: {}
- name: nginx-config
configMap:
name: my-nginx-config
kubectl create -f pod.yaml # create from declarative configuration
Make sure the configMap is being used
kubectl port-forward mypod 8080:80 # browsable on http://localhost:8080/ - same date content
# browsable on http://localhost:8080/api/ - flask content
Services have their own IPs and use selector (labels) to forward traffic to pods.
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 80
type: LoadBalancer
kubectl create -f service.yaml
kubectl get services # shows services names, ip(s), ports
Browse to app on service external IP from service.
Deployments allow for replica sets of pods, autoscaling, and rolling updates.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydeployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
name: mypod
labels:
app: hello
annotations:
appversion: "0.1"
spec:
containers:
- image: nginx
name: hello
ports:
- name: http
containerPort: 80
volumeMounts:
- name: myhtml
mountPath: /usr/share/nginx/html
- name: nginx-config
mountPath: /etc/nginx/conf.d
- image: busybox
name: datewriter
command:
- '/bin/sh'
- '-c'
args:
- 'while true ; do date > /content/index.html ; sleep 1 ; done'
volumeMounts:
- name: myhtml
mountPath: /content
- image: jcdemo/flaskapp
name: flaskapp
ports:
- name: flask
containerPort: 5000
volumes:
- name: myhtml
emptyDir: {}
- name: nginx-config
configMap:
name: my-nginx-config
kubectl create -f deployment.yaml
kubectl get deployments # list deployments
kubectl describe deployment mydeployment # additional deployment details
Browse To Public IP http://service-public-ip/
args:
- 'while true ; do date > /content/index.html ; sleep 1 ; done'
args:
- 'while true ; do date +"%s" > /content/index.html ; sleep 1 ; done'
kubectl get deployments -w # show events as the deployment is updated
kubectl apply -f deployment.yaml # update the existing deployment
Browse To Public IP http://service-public-ip/ - now showing date as epoch.