class: nord-dark, center, middle .logo[] # Grokking containers --- class: nord-dark, left, middle ## Why Docker? * A single *portable* artifact for app + dependencies regardless of language * Portable across dev/test/prod * Portable across AWS/Azure/GCP/Everyone else * Portable within AWS/Azure/GCP * Better division of labour: Devs build images, Ops run them. --- class: nord-dark, middle, left #### Directive on Service and Digital ##### Mandatory Procedures for Enterprise Architecture Assessment ### A.2.3.10.4 Run applications in containers --- class: nord-dark, left, middle ## Docker built a nice DX around Linux Kernel features * Namespaces * Cgroups * OverlayFS ??? https://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/ http://man7.org/linux/man-pages/man7/mount_namespaces.7.html http://man7.org/linux/man-pages/man7/sysvipc.7.html https://www.researchgate.net/profile/Roberto_Pietro/publication/309965523_To_Docker_or_Not_to_Docker_A_Security_Perspective/links/5bd2f7c1a6fdcc3a8da6c537/To-Docker-or-Not-to-Docker-A-Security-Perspective.pdf https://lwn.net/Articles/531114/ http://karelzak.blogspot.com/2009/12/unshare1.html --- class: nord-dark, left, middle #### $> man 7 namespaces >A namespace wraps a global system resource in an abstraction that
makes it appear to the processes within the namespace that they have their own isolated instance of the global resource
. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. --- class: nord-dark, left, middle ### A "global system resource": Hostname There is just one for the whole system! --- class: nord-dark, left, middle ### A "global system resource": Hostname Let's give a process it's "own isolated instance of the global resource". --- class: nord-dark, center, middle, terminal Changing hostname within a namespace  --- class: nord-dark, left, middle ### 8 global resources * uts: hostname * user: user and group numbers * net: network devices * pid: process numbers (process id) * mnt: mount point numbers (mount id) * ipc: message queue numbers (msqid), semaphore set ids (semid), shared memory segment ids (shmid) * cgroups: visibility into group membership * time: ability to set CLOCK_MONOTONIC and CLOCK_BOOTTIME --- class: nord-dark, left, middle ### Another "global system resource": Process IDs * ID must be unique! * A single pool starting at 0 ending at 4194304 (cat /proc/sys/kernel/pid_max) * Each new process increments the number * Wraps when it runs out --- class: nord-dark, left, middle ### Process IDs: A process only sees pids in the same namespace --- class: nord-dark, center, middle, terminal The htop process monitor showing what's visible in it's namespace  --- class: nord-dark, left, middle ### Process IDs: Adding new processes to existing namespaces --- class: nord-dark, center, middle, terminal Launching a process into an existing namespace  --- class: nord-dark, left, middle ## Namespaces: The important parts * The Linux Kernel makes it "appear to the processes within the namespace that they have their own isolated instance of the global resource" * Processes can be given their own "isolated instance" of each "global resource", separately or together. * Giving a process an "isolated instance" of every "global resource" is indistinguishable (to the process) from having a machine to itself. * This is called "OS-level" virtualization --- class: nord-dark, left, middle ## OverlayFS * Built into the kernel back in 2014 * Can “present a filesystem which is the result over overlaying one filesystem on top of the other.” --- class: nord-dark, left, middle ### OverlayFS: overlaying one filesystem on top of another ```bash $ tree . ├── a │ └── a.txt ├── b │ └── b.txt ├── c │ └── c.txt ├── everything └── workdir 5 directories, 3 files ``` --- class: nord-dark, left, middle ### OverlayFS: overlaying one filesystem on top of another ```bash $ sudo mount -t overlay -o lowerdir=a:b:c none everything [sudo] password for mike: $ tree . ├── a │ └── a.txt ├── b │ └── b.txt ├── c │ └── c.txt ├── everything │ ├── a.txt │ ├── b.txt │ └── c.txt └── workdir 5 directories, 6 files ``` --- class: nord-dark, left, middle ## Docker: Namespacing and OverlayFS in action --- class: nord-dark, left, middle ```bash [mike@ouroboros ~]$ docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu 5bed26d33875: Pull complete f11b29a9c730: Pull complete 930bda195c84: Pull complete 78bf9a5ad49e: Pull complete Digest: sha256:bec5a2727be7fff3d308193cfde3491f8fba1a2ba392b7546b43a051853a341d Status: Downloaded newer image for ubuntu:latest docker.io/library/ubuntu:latest [mike@ouroboros ~]$ docker run -it ubuntu root@f0e2650b7332:/# ps PID TTY TIME CMD 1 pts/0 00:00:00 bash 10 pts/0 00:00:00 ps root@f0e2650b7332:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var ``` --- class: nord-dark, left, middle ## Digging deeper * Building a container from scratch in Go - Liz Rice https://youtu.be/Utf-A4rODH8 * Cgroups, namespaces, and beyond: what are containers made from? - Jérôme Petazzoni https://youtu.be/sK5i-N34im8 --- class: nord-dark, center, middle ## What about deployment? --- class: nord-dark, center, middle .logo[] ## Kubernetes --- class: nord-dark, left, middle ### Why Kubernetes? * A single skillset across VM's (Kubevirt), serverless (Kubeless, Knative) & containers * A single skillset across all major cloud providers and on-prem * A single skillset across simple (monolithic) and complex (microservices) deployments * Minimize cloud vendor lock-in --- class: nord-dark, center, middle ### The Pod: the "unit of deployment" .logo[] --- class: nord-dark, center, middle ### The Pod: the "unit of deployment" .logo[] --- class: nord-dark, center, middle ### The worker .logo[] --- class: nord-dark, center, middle ### The cluster: n workers + master .xxl[] --- class: nord-dark, center, middle ### The cluster: n workers + multi-master .xxl[] --- class: nord-dark, middle, left #### Directive on Service and Digital ##### Mandatory Procedures for Enterprise Architecture Assessment ### A.2.3.12.4 Use distributed architectures --- class: nord-dark, left, middle ### OK, but how? --- class: nord-dark, left, middle ### Minimum Viable Kubernetes ```bash # create a cluster $ gcloud beta container clusters create \ --region=northamerica-northeast1 \ --num-nodes=1 \ --release-channel=stable \ helloworld # Create a deployment for an image: kubectl create deployment helloworld --image mikewilliamson/helloworld # Expose it's port to the internet as port 80 $ kubectl expose deployment/helloworld --type LoadBalancer --port 80 --target-port 3000 ``` --- class: nord-dark, left, middle ### Minimum Viable Kubernetes: the result ```bash $ kubectl get svc helloworld NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE helloworld LoadBalancer 10.15.252.125 34.95.1.130 80:30021/TCP 10m $ curl 34.95.1.130
React App
You need to enable JavaScript to run this app.
``` --- class: nord-dark, center, middle ### Minimum Viable Kubernetes: the result .k8slb[] --- class: nord-dark, left, middle ### Kubernetes for reals * Declarative: Tell Kubernetes what we want, our "desired state" * Captured in code: Describe your system in code, version that code in git. ```bash # Imperative: ☹ kubectl create deployment helloworld --image mikewilliamson/helloworld kubectl expose deployment/helloworld --type LoadBalancer --port 80 --target-port 3000 # Declarative: 😀 kubectl create --dry-run deployment --image mikewilliamson/helloworld helloworld -o yaml > deployment.yaml kubectl create service loadbalancer helloworld --tcp=80:3000 --dry-run -o yaml > service.yaml # Version them in git, make sure Kubernetes has the latest version. kubectl apply -f . ``` --- class: nord-dark, middle, left ```yaml apiversion: apps/v1 kind: deployment metadata: labels: app: helloworld name: helloworld spec: replicas: 1 selector: matchlabels: app: helloworld template: metadata: labels: app: helloworld spec: containers: - image: mikewilliamson/helloworld name: helloworld ``` --- class: nord-dark, middle, left ```yaml apiVersion: v1 kind: Service metadata: labels: app: helloworld name: helloworld spec: ports: - name: 80-3000 port: 80 protocol: TCP targetPort: 3000 selector: app: helloworld type: LoadBalancer status: loadBalancer: {} ``` --- class: nord-dark, left, middle ## Why this stuff matters * dev/test/prod is just `kubectl apply` * AWS/Azure/GCP is just `kubectl apply` * Basic Scaling: `replicas: 1` > `replicas: 2` * Deploy: `myimage:v3` > `myimage:v4` * Rollback: `myimage:v4` > `myimage:v3` * Patch: `myimage:v4` > `myimage:v5` --- class: nord-dark, left, middle ## Security implications * Slimmer servers: Less code, fewer vulns * Image building (additive) > hardening (subtractive) ```bash $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest a187dde48cd2 2 hours ago 5.6MB ubuntu latest 4e5021d210f6 3 days ago 64.2MB debian latest 971452c94376 3 weeks ago 114MB fedora latest 536f3995adeb 4 weeks ago 193MB centos latest 470671670cac 2 months ago 237MB ``` --- class: nord-dark, center, middle, terminal ## Security implications  --- class: nord-dark, middle, center ## Fin