Basics

K8s
K8s fundamentals

These are rough notes and not meant for consumption by others! Any course material will be seperate and may or may not be related to these notes!

Do stuff: kubectl apply -f

Multiple resources in one yaml with ---

Deployments

kind: deployment

[[Pods]] can have more than 1 container but usually contain just one

[Deployments] control pods and will restart Pods if they fail. Deployments are a type of [[controller]]. You usually deploy pods via a deployment. kubectl create deployment. Deployments keep track of pods via labels and a label selector. If you change the pod’s labels the deployment might lose track of the pods.

A [[controller]] is a K8s resource that manages other resources.

-o yaml is great for seeing labels, you can swithc to - json and pipe to jq

Execute a command in a container by doing kubectl exec -it <pod name> -- sh

Services

kind: service

You can’t switch a Service from one type to another in every version of Kubernetes, so you’ll need to delete the original ClusterIP Service for the API before you can deploy the ExternalName Service.

You can

DNS

When you deploy a service, you can reference it from within the namespace by its name. For example, the following service:

apiVersion: v1
kind: Service
metadata:
  name: numbers-api
spec:
  ports:
    - port: 80
  selector:
    app: numbers-api
  type: ClusterIP # this isn't necessary, it's the default

Can be referenced with just http://numbers-api – that is how it’s referenced in the application.

If the pod trying to reach that service is in a different namespace, you have to use the fully qualified name <service-name>.<namespace>, which would be http://numbers-api.default in this case.

Testing DNS

The best way to test internal DNS problems is to use nslookup in a pod. You can deploy a pod specifically for network testing from the official k8s docs:

apiVersion: v1
kind: Pod
metadata:
  name: dnsutils
  namespace: default
spec:
  containers:
  - name: dnsutils
    image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
    command:
      - sleep
      - "infinity"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

You can also reference the yaml like this kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml

Let’s say I’ve deployed a service called numbers-api in the default namespace:

$ kl get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
numbers-api   ClusterIP   10.98.154.232    <none>        80/TCP           13m

You can now test it with nslookup:

# use nslookup <service-name> if the pod dnsutils ins int he same namespace as the service
kubectl exec -i -t dnsutils -- nslookup numbers-api
# or if your pod is in a different namespace than the service use <service-name>.<namespace>
kubectl exec -i -t dnsutils -- nslookup numbers-api.default

If you are experiencing further issues, follow these debugging instructions

Labels

If you have overlapping labels for a particular deployment, the service will route to all deployments that match that label. If you want to control for this, add additional unique labels. Just having one label like “myapp” can be dangerous for this reason.

Services deal with networking. These use labels, too via a selector.

Routing internal traffic ClusterIP

ClusterIP: default service that is internal DNS. type: ClusterIP

Forward port 8080 on your local computer to port 80 in container: kubectl port-forward deploy/numbers-web 8080:80

Routing external traffic: LoadBalancer

type: LoadBalancer Uses labels too

Routing Traffic Outside K8s ExternalNameService

type: ExternalName

You have to watch out when making HTTP requests through ENS, b/c the header wil still contain the original hostname, which will probably get rejected. It’s fine for things like TCP etc for databases.

Namespaces

This is relevant to networking b/c resources outside the default namespace will have a different network address

kubectl get svc -n default kubectl get svc -n kube-system

For example, the internal kube-dns service:

 kl get svc -n kube-system                                                                                                                                                                            (master)kiamol
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   9d

Can be accessed like this kl exec deploy/sleep-1 -- sh - c'nslookup kube-dns.kube-system.svc.cluster.local

Configuring Applications

You can environment variables to Pod specs

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
        - name: sleep
          image: kiamol/ch03-sleep
          env:
          - name: KIAMOL_CHAPTER
            value: "04"

You usually don’t set configs in pod specs. You ususally use [[ConfigMap]]

How to reference a configmap instead/in addition to of an env variable:

% cat sleep/sleep-with-configMap-env-file.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
        - name: sleep
          image: kiamol/ch03-sleep
          envFrom: # This section will bring in all env variables from the config map `sleep-config-env-file` which we create below.  This can be thought of as the "baseline" config.
          - configMapRef:
              name: sleep-config-env-file
          env: # This section can override any environment variables from the config, including any other configs that are elswhere.  So this will override other things
          - name: KIAMOL_CHAPTER
            value: "04"
          - name: KIAMOL_SECTION
            valueFrom:
              configMapKeyRef: # this came from another configMap
                name: sleep-config-literal
                key: kiamol.section

Creating a [[ConfigMap]]

Method 1: from env file

This way is not recommended b/c you have to use kl create rather than kl apply , and you want to use kl apply for everything

Start with an env file, like this:

% cat sleep/ch04.env                                                                                                                                                                                   
KIAMOL_CHAPTER=ch04
KIAMOL_SECTION=ch04-4.1
KIAMOL_EXERCISE=try it now

Create a config file from an env file

% kl create configmap sleep-config-env-file --from-env-file=sleep/ch04.env                                                                                                                             
configmap/sleep-config-env-file created

Update your deployment by making changes to add the reference to the config file (see previous section)

kl apply -f sleep/sleep-with-configMap-env-file.yaml

Method 2: from ConfigMap spec

This is more flexible and powerful, you can embed arbitrary files like json files that can be read by your app.

Create a spec:

% cat todo-list/configMaps/todo-web-config-dev.yaml                                                                                                                                                    
apiVersion: v1
kind: ConfigMap
metadata:
  name: todo-web-config-dev
data: # we are going to mount this json file into the container so the app can use it
  config.json: |-
    {
      "ConfigController": {
        "Enabled" : true
      }
    }

Apply this spec: kl apply -f todo-list/configMaps/todo-web-config-dev.yaml

P.S. You could have also seen the yaml file for the other configmap we created earlier with kl get cm/sleep-config-env-file -o yaml and used that yaml file

Use the config map in the deployment spec, and additionally mount a volume containing the config:

% cat todo-list/todo-web-dev.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-web
spec:
  selector:
    matchLabels:
      app: todo-web
  template:
    metadata:
      labels:
        app: todo-web
    spec:
      containers:
        - name: web
          image: kiamol/ch04-todo-list
          volumeMounts: # This will load the config json file into `/app/config` in your container
            - name: config
              mountPath: "/app/config" #directory path to mount the volume **BE CAREFUL** if you mounted this to `/app`, then it would have wiped out all the files!
              readOnly: true
      volumes: # volumes are defined at pod level
        - name: config  # Name matches the volume mount
          configMap: # volume source is the Config Map
            name: todo-web-config-dev  #ConfigMap name

Be careful when specifying the mount path, lots of people make mistakes here and overwrite existing data. K8s will not merge directories for you!

If you change the config map, it will refresh the files in the directory. You have to make sure your app is watching that directory though.

Instead of loading the whole config map, you can selectively mount files in the config map like this:

% cat todo-list/todo-web-dev-no-logging.yaml                                                                                                                                                                     
apiVersion: apps/v1
...
      volumes:
        - name: config
          configMap:
            name: todo-web-config-dev
            items:
            - key: config.json
              path: config.json