Application Deployment¶
Understanding the KUBECONFIG¶
Locating kubeconfig¶
Kubectl
has configuration file, which is used to tell to Kubectl
where you kubernetes cluster is.
Default location of the kubeconfig
file is in ~/.kube/config
Everytime we spin up a kubernetes cluster, kind
will go ahead an update kubeconfig
file locally.
You will see similiar behaviour when you going to be using Minikube
clusters.
Note
For cloud provides you may need to run additional commands to fetch the kubeconfig
file locally.
Let's examine our ~/.kube/config
file, after we've created 2 kind
clusters.
kind: Config
apiVersion: v1
clusters:
- list of clusters (addresses \ endpoints)
current-context: kind-stg
contexts:
- list of contexts ( which user and cluster to use when running commands)
users:
- list of users (thing that identifies us when accessing a cluster [certificate])
Result
We can see that kubeconfig file is very simple and it has:
- list of clusters
- list of contexts
- list of users (simply a way to authenticate with a cluster)
- Current context
Note
kubernetes by default uses certificate authentication. Other auth plugins exist (gke, aks, eks)
Using kubectl config commands¶
Altertnatively, you can use command to interact with kubeconfig
which is kubectl config
.
Here is ther commands that are telling kubectl
which context to use:
Using different config files¶
You can also tell your kubectl
to use different config files.
This is useful to keep your production config separate from your development ones
Set the $KUBECONFIG
environment variable to a path:
Export seperate configs using kind
. This is possible with cloud based clusters as well:
kind --name dev export kubeconfig --kubeconfig ~/.kube/dev-config
kind --name stg export kubeconfig --kubeconfig ~/.kube/stg-config
Switch to stg
only cofig:
Reset back to shared KUBECONFIG file:
Working with Kubernetes resources¶
Now that we have cluster access, next we can read resources from the cluster
with the kubectl get
command.
Namespaces¶
Kubernetes has the notion of namespaces
to organize the resources and manage access control (RBACs).
When a cluster is first created, some namespaces
are created by default:
The default
namespace is, well, the default. kube-system
namespace is a really specific namesapce hosting all the mandatory cluster resources.
Let's create namespace kubecon
with the kubectl create
command:
We can create a namespace with the kubectl create
command:
Create k8s resources with command line¶
Let's create
Create a pod
declaratively with YAML¶
kubectl
can be used to generate YAML manifests and help you build your own:
kubectl create <resource>
to create a resource (kubectl run
for pods)-–output yaml
(-o yaml
) to dump the yaml-–dry-run=client
to simulate the resource and not create it in the clusterkubectl explain <resource>
to get more informations on a resource type
Then run the deployment command:
kubectl run \
--image=alpine:latest \
-o yaml \
--dry-run=client \
simple-pod \
--command -- tail -f /dev/null > simple-pod.yaml
kubectl apply -f simple-pod.yaml
Use kubectl get po
to list the resulting pod:
Using kubectl describe
can give even more infos:
Name: simple-pod
Namespace: default
Priority: 0
Service Account: default
Node: dev-worker/10.89.0.3
Start Time: Thu, 20 Oct 2022 17:16:19 -0400
Labels: run=simple-pod
Annotations: <none>
Status: Running
IP: 10.244.1.5
IPs:
IP: 10.244.1.5
Containers:
simple-pod:
Container ID: containerd://ef45ec5ea986965ef51da85b8d157c590f68b252114248f4339d0be35feeccfd
Image: alpine:latest
Image ID: docker.io/library/alpine@sha256:bc41182d7ef5ffc53a40b044e725193bc10142a1243f395ee852a8d9730fc2ad
Port: <none>
Host Port: <none>
Command:
tail
-f
/dev/null
State: Running
Started: Thu, 20 Oct 2022 17:16:21 -0400
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-p2mpj (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-p2mpj:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 6m10s default-scheduler Successfully assigned default/simple-pod to dev-worker
Normal Pulling 6m9s kubelet Pulling image "alpine:latest"
Normal Pulled 6m8s kubelet Successfully pulled image "alpine:latest" in 422.571559ms
Normal Created 6m8s kubelet Created container simple-pod
Normal Started 6m8s kubelet Started container simple-pod
Create a deployment
declaratively with YAML¶
kubectl
can be used to create sample resources, like we just did with pods
. Usually you can't deploy the generated resource directly to a cluster. It is needed to update the resource to configure some aspects that can't be configures in the CLI.
For example, generate a basic deployment:
kubectl create deployment \
--image=alpine:latest \
--replicas=2 \
-o yaml \
--dry-run=client \
simple-deployment
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: simple-deployment
name: simple-deployment
spec:
replicas: 2
selector:
matchLabels:
app: simple-deployment
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: simple-deployment
spec:
containers:
- image: alpine:latest
name: alpine
resources: {}
status: {}
Here the container is missing a real command to execute, like the tail -f /dev/null
that we added in the pod
.
Now deploy this manifest:
kubectl create deployment \
--image=alpine:latest \
--replicas=2 \
-o yaml \
--dry-run=client \
simple-deployment > simple-deployment.yaml
kubectl apply -f simple-deployment.yaml
Use kubectl get deployment
to see the result:
In Kubernetes, Deployments creates ReplicaSets, which in turn creates Pods:
NAME READY STATUS RESTARTS AGE
NAME READY STATUS RESTARTS AGE
simple-deployment-5cff76596f-clgl7 0/1 CrashLoopBackOff 5 (86s ago) 1m35s
simple-deployment-5cff76596f-vhnvh 0/1 CrashLoopBackOff 5 (83s ago) 1m35s
simple-pod 1/1 Running 0 3m30s
Oops, something is not quite right with our deployment.
Please, don't try to debug right now. Let's move on.
Create a service
declaratively with YAML¶
Services are the entry-point to your pods. they "load-balance" the requests between all the associated pods.
Here is how to create one:
kubectl create service clusterip simple-service \
--tcp=5678:8080 \
-o yaml \
--dry-run=client > simple-service.yaml
kubectl apply -f simple-service.yaml
Check that the service is OK:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
simple-service ClusterIP 10.96.94.15 <none> 5678/TCP 4s
The kubernetes
service is a default service pointing to the Kubernetes API of the cluster.
Ok now we have some stuff in the kind
cluster.
Deploy a real application¶
The most common and recommended way to create resources in Kubernetes is with the kubectl apply
command.
This command takes in declarative YAML
files.
Now deploy a real application, composed of a Mysql database, a web application and associated services:
cat > mysql-secret.yaml << EOF
kind: Secret
apiVersion: v1
metadata:
name: mysql
type: Opaque
data:
password: cm9vdHBhc3N3ZA==
EOF
kubectl apply -f mysql-secret.yaml
cat > mysql-deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: gowebapp-mysql
labels:
run: gowebapp-mysql
tier: backend
spec:
replicas: 1
selector:
matchLabels:
run: gowebapp-mysql
strategy:
type: Recreate
template:
metadata:
labels:
run: gowebapp-mysql
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
value: rootpasswd
image: ghcr.io/cloud-native-canada/k8s_setup_tools:mysql
name: gowebapp-mysql
ports:
- containerPort: 3306
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 30
timeoutSeconds: 2
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 25
timeoutSeconds: 2
resources:
requests:
cpu: 20m
memory: 252Mi
limits:
cpu: 500m
memory: 1G
EOF
kubectl apply -f mysql-deployment.yaml
cat > mysql-service.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: gowebapp-mysql
labels:
run: gowebapp-mysql
spec:
clusterIP: None
ports:
- port: 3306
targetPort: 3306
selector:
run: gowebapp-mysql
EOF
kubectl apply -f mysql-service.yaml
cat > app-deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: gowebapp
labels:
run: gowebapp
spec:
replicas: 1
selector:
matchLabels:
run: gowebapp
template:
metadata:
labels:
run: gowebapp
spec:
containers:
- env:
- name: DB_PASSWORD
value: rootpasswd
image: ghcr.io/cloud-native-canada/k8s_setup_tools:app
name: gowebapp
ports:
- containerPort: 9000
livenessProbe:
httpGet:
path: /register
port: 9000
initialDelaySeconds: 15
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /register
port: 9000
initialDelaySeconds: 25
timeoutSeconds: 5
resources:
requests:
cpu: "20m"
memory: "10Mi"
limits:
cpu: "50m"
memory: "100Mi"
EOF
kubectl apply -f app-deployment.yaml
Some spotted a problem already: the password is hardcoded. We'll get to it later.
Check the application is running:
NAME READY STATUS RESTARTS AGE
gowebapp-5994456fcb-ctt4x 1/1 Running 0 31m
gowebapp-mysql-684db8fdcd-nwfn4 1/1 Running 0 34m
Next¶
Now we have something to play with, let's start playing !