Share
Embed
Kubernetes Pod internals with the fundamentals of Containers
* Short link: https://hyojun.me/~k8s-pod-internal-en
* This slide is also available in Korean: https://hyojun.me/~k8s-pod-internal-ko
Hyojun Jeon
April 01, 2021
Transcript
79 slides
-
Kubernetes Pod internals with the fundamentals of Containers Hyojun Jeon
(https://hyojun.me) -
• The smallest deployable unit in Kubernetes • A group
of containers What is a Kubernetes Pod? -
• The smallest deployable unit in Kubernetes • A group
of containers To understand Kubernetes Pods, we need to understand containers What is a Kubernetes Pod? -
• Root directory isolation (chroot) • Linux namespaces ◦ Mount
(mnt) ◦ Process ID (pid) ◦ Network (net) ◦ Interprocess Communication (ipc) ◦ Unix Time-Sharing (uts) ◦ User ID (user) • Control groups (cgroup) • OverlayFS • … etc. → Let's find out what these are. How are containers isolated? -
/ ├── bin ├── boot ├── etc ├── home │
└── container │ ├── bin │ ├── boot │ ├── etc │ ├── home │ ├── opt │ ├── tmp │ └── ... ├── opt ├── tmp └── ... / ├── bin ├── boot ├── etc ├── home ├── opt ├── tmp └── ... Container root How are containers isolated? (1) Isolating root directory (chroot) -
bash / ls dependencies bash / ls binaries $ chroot
<NEWROOT> <COMMAND> chroot Sets an isolated root directory (a new root path) for a process and its children How are containers isolated? (1) Isolating root directory (chroot) -
$ lsns -p <pid> → Lists the namespaces of the
specified process. This process is running on “cgroup”, “user” namespaces of the init process. Isolated namespaces for this process Linux Namespace → A kernel feature to isolate system resources between processes How are containers isolated? (2) Linux Namespaces -
unshare → Runs a process with isolated namespaces. # Run
a process(/bin/bash) with an isolated mount namespace(-m option). $ unshare -m /bin/bash # Run a process (/bin/bash) with isolated mount (-m) and IPC (-i) namespaces $ unshare -m -i /bin/bash How are containers isolated? (2) Linux Namespaces -
Mount A command to attach a file system to the
big file tree, to make it accessible in Unix systems. # mount -t <type> <device> <dir> # e.g. Mount “tmpfs”(temporary file storage) into “/root/test” $ mount -t tmpfs tmpfs /root/test How are containers isolated? (3) Mount (mnt) namespace -
/ bin dir1 tmp1 tmp2 tmp3 This mount point is
only visible on the isolated mount namespace root test How are containers isolated? (3) Mount (mnt) namespace -
$ echo $$ 1111 $ unshare -m /bin/bash $ echo
$$ 2222 $ mkdir -p test && mount -t tmpfs tmpfs /root/test $ df | grep test tmpfs 2.0G 0 2.0G 0% /root/test $ exit $ df | grep test Show the current process ID Run `/bin/bash` with an isolated mount namespace(`-m` option) Exit `/bin/bash`(isolated mount namespace) and then check the file systems. The file system mounted into `/root/test` shouldn’t be visible. Mount tmpfs(temporary file storage) into `/root/test` Mount namespace Allows processes to have different mount points How are containers isolated? (3) Mount (mnt) namespace -
OverlayFS storage driver How are containers isolated? (3) Mount (mnt)
namespace + OverlayFS -
OverlayFS storage driver Container Image (read-only) How are containers isolated?
(3) Mount (mnt) namespace + OverlayFS -
OverlayFS storage driver Files created/changed/deleted in the container (writable) How
are containers isolated? (3) Mount (mnt) namespace + OverlayFS -
OverlayFS storage driver Image Layer + Container Layer = merged
= The final mounted file system How are containers isolated? (3) Mount (mnt) namespace + OverlayFS -
OverlayFS storage driver $ docker inspect ubuntu | jq ".[].GraphDriver"
{ "Data": { "LowerDir": "/var/lib/docker/overlay2/.../diff", "MergedDir": "/var/lib/docker/overlay2/.../merged", "UpperDir": "/var/lib/docker/overlay2/.../diff", "WorkDir": "/var/lib/docker/overlay2/.../work" }, "Name": "overlay2" } *workdir How are containers isolated? (3) Mount (mnt) namespace + OverlayFS -
Container is a “Process” running on an “isolated environment” From
inside the container, it looks like a virtual machine. But from the outside (host), it's just a process. How are containers isolated? (4) Process ID (pid) namespace -
For the same process, The PIDs are different between outside
and inside the container. . Outside(host) PID=115679 Inside(container) PID=1 How are containers isolated? (4) Process ID (pid) namespace -
1 2 3 5(1) 6(2) 7(3) 8(4) 4 Global PID
namespace (*) PID recognized in the isolated PID namespace How are containers isolated? (4) Process ID (pid) namespace -
# Run a process (/bin/bash) with an isolated PID namespace
(-p option). $ unshare -f -p /bin/bash $ echo $$ # Show the current PID 1 Inside a container running on an isolated PID namespace, the first executed process (the entrypoint) always has the PID of 1. How are containers isolated? (4) Process ID (pid) namespace -
Isolates Inter-Process Communication (based on System V) • System V
IPC ◦ Shared memory(shm) ◦ Semaphores ◦ POSIX message queues(/proc/sys/fs/mqueue) • IPC objects are visible only to the processes on the same namespace How are containers isolated? (5) Inter-Process Communication (ipc) namespace -
Isolates network interfaces, routing tables, and firewall rules. # Create
a network namespace named `test-ns` $ ip netns add test-ns $ ip netns list test-ns # Create a virtual ethernet interface pair (veth1, veth2) # Add `veth1` to `test-ns` namespace, and `veth2` to the network namespace of PID 1 $ ip link add veth1 netns test-ns type veth peer name veth2 netns 1 How are containers isolated? (6) Network (net) namespace -
# On the new namespace `test-ns`, run the command “ip
link list” to list network interfaces. # In `test-ns`, there are only `veth1` and loopback interfaces. $ ip netns exec test-ns ip link list 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 8: veth1@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 2a:aa:60:ee:27:d4 brd ff:ff:ff:ff:ff:ff link-netnsid 0 # On the host default network namespace, run the command “ip link list”. $ ip link list 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 (… omit …) 7: veth2@if8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether d2:a1:90:78:3c:4b brd ff:ff:ff:ff:ff:ff link-netnsid 0 How are containers isolated? (6) Network (net) namespace Isolates network interfaces, routing tables, and firewall rules. -
Docker's networking veth veth docker bridge eth0 veth veth container1
container2 External Network Containers are running on an isolated network namespace, connected to the host via veth peers. The veths are connected to the “docker bridge” on the host. To communicate with the outside world, the containers have to go through the bridge. How are containers isolated? (6) Network (net) namespace -
Unix Time-Sharing? This comes from the concept of sharing computing
resources among multiple users. Multiple users are sharing the same machine, but we want to make them feel like they're using separate machines. How are containers isolated? (7) Unix Time-Sharing (uts) namespace -
Make a namespace for each user to isolate the hostnames!
Unix Time-Sharing? How are containers isolated? (7) Unix Time-Sharing (uts) namespace Multiple users are sharing the same machine, but we want to make them feel like they're using separate machines. This comes from the concept of sharing computing resources among multiple users. -
$ hostname ubuntu $ unshare -u /bin/bash $ hostname hyojun
$ hostname hyojun $ exit $ hostname ubuntu Run /bin/bash on an isolated UTS namespace Change hostname to “hyojun” and then show the current hostname Exit bash, and then show the current hostname. = it keeps the original hostname(ubuntu) (The hostname was changed only in the process where the UTS namespace was isolated.) Show the current hostname hostname, domainname isolation How are containers isolated? (7) Unix Time-Sharing (uts) namespace -
Map a different uid for a host user Host Container
(Isolated user namespace) root(uid:0) hyojun(uid:1000) This user is root inside the container, but non-root on the host. How are containers isolated? (8) User ID (user) namespace -
However, in Docker, the user namespace is not isolated. Docker
container use the namespace of host PID=1 by default How are containers isolated? (8) User ID (user) namespace -
If the user namespace of the host is shared to
its containers, the users inside the containers can exercise the authority of the same uid on the host. Host Container (Non-isolated user namespace) root(uid:0) root(uid:0) How are containers isolated? (8) User ID (user) namespace -
$ sudo usermod -aG docker <your-user> > After installing Docker,
add a user to docker group so that non-root users can run docker This is the command you've run at least once after installing Docker. How are containers isolated? (8) User ID (user) namespace -
$ sudo usermod -aG docker <your-user> > After installing Docker,
add a user to docker group so that non-root users can run docker This is the command you've run at least once after installing Docker. WARNING! How are containers isolated? (8) User ID (user) namespace -
Users without root privileges can exercise root authority to the
bound host root directory through Docker. This is because the container root uid 0 has the same uid on the host. non-root$ docker run -ti -v /:/host ubuntu:18.04 /bin/bash If you bind the host's “/” root directory to the container… How are containers isolated? (8) User ID (user) namespace -
• Compatibility issues with sharing PID and Network namespaces. •
Compatibility issues with external volumes or drivers that do not support user mapping. • The complexity of ensuring access rights for the files bound from the host, if the host uid and the container uid differ. • However, while the root on a unisolated user namespace has almost the same permissions as the host's root, it does not include all permissions. Why doesn't Docker isolate the user namespace? How are containers isolated? (8) User ID (user) namespace -
Kubernetes does not support user namespace isolation yet, either. A
good article to read on this topic: https://kinvolk.io/blog/2020/12/improving-kubernetes-and-container-security-with-user-namespaces/ How are containers isolated? (8) User ID (user) namespace -
When not isolating the user namespace... • Restrict only trusted
users to run the container runtime (e.g. Docker). • Make sure that the container's processes do not run as the root user. ◦ Assign a specific user and group to run processes • Do not bind any of the host’s important directories, to prevent containers from accessing them. Kubernetes provides security settings based on the same principles. https://kubernetes.io/docs/concepts/policy/pod-security-policy/#users-and-groups How are containers isolated? (8) User ID (user) namespace -
A Linux kernel feature to limit and isolate resource allocations
among process groups • CPU • Memory • Network • Disk Limit CPU and memory usage... Prioritize network traffic, ... Provide statistics on usage, or etc. How are containers isolated? (9) Control group (cgroup) -
• A container is a process running on an isolated
environment. • Isolated environments are implemented through Linux namespaces. ◦ Mount (mnt) ◦ Process ID (pid) ◦ Network (net) ◦ Interprocess Communication (ipc) ◦ Unix Time-Sharing (uts) ◦ User ID (user) • Processes’ resource usage are limited through cgroups. How are containers isolated? Recap -
• The smallest deployable unit in Kubernetes • A group
of containers What is a Kubernetes Pod? -
deploy replicas: 1 Kubernetes Cluster Node 1 Node 2 ...
K8s applications are deployed as Pods. -
replicas: 1 Kubernetes Cluster Node 1 Node 2 Pod ...
deploy K8s applications are deployed as Pods. -
replicas: 2 Kubernetes Cluster Node 1 Node 2 Pod Pod
... deploy K8s applications are deployed as Pods. -
“The smallest deployable unit”? • In most cases, Pods are
usually managed by using the below types of workload resources. ◦ Job - Manages Pods that are executed once and terminated when the task is completed. ◦ ReplicaSet - Ensures that the specified number of Pods(replica) are running. ◦ DaemonSet - Ensures that only one pod running for each node ◦ StatefulSet - Manages Pods running stateful applications ◦ Deployment - Manages deployment of Pod, ReplicaSet updates A pod is the most basic and smallest unit created and managed by Kubernetes. -
• The smallest deployable unit in Kubernetes • The group
of containers What is a Kubernetes Pod? -
There can be more than one container in a pod.
Pod container 1 container 2 container 3 ... Pod container 1 A pod with a single container A pod with multiple containers -
A container that runs a web server https://kubernetes.io/docs/concepts/cluster-administration/logging/#sidecar-container-with-a-logging-agent A case
of running multiple containers in a pod -
A container that runs an agent to forward logs generated
by the web server to an external log system https://kubernetes.io/docs/concepts/cluster-administration/logging/#sidecar-container-with-a-logging-agent A case of running multiple containers in a pod -
https://pixabay.com/photos/bmw-500-old-motorcycle-sidecar-4344066/ • One Primary Container that plays the main role
• One or more Sidecar Containers ◦ It serves supporting features for the primary container e.g. Monitoring, Logging, etc... When a pod consists of multiple containers... https://pixabay.com/photos/bmw-500-old-motorcycle-sidecar-4344066/ “Sidecar” attached to a motorcycle -
Can't we run all processes in one container? That seems
complicated... -
Can't we run all processes in one container? That seems
complicated... You can, but that’s not recommended. -
In a container, it’s recommended to run a single process
• We learned that a container is a process that runs in an isolated environment. • The first process executed in the isolated PID namespace is pid 1. The state of the first process run in the container = Container’s life -
Even if the container is running, we cannot guarantee that
all desired processes are running fine. The state of all processes running in the container ≠ Container’s life If there are multiple processes running inside a container... -
spec: template: (...) spec: containers: - name: hello image: busybox
command: ['sh', '-c', 'sleep 3600'] restartPolicy: Always (...) Don't worry, Kubernetes restarts the container according to the declared restartPolicy. • Always • OnFailure • Never When a specific container of a Kubernetes Pod is terminated -
Pod Node Kubelet Pod Container 1 Container 2 Container 4
(terminated) Container 3 Retries with exponential back-off delay (10s, 20s, 40s,… up to 5 minutes) When a specific container of a Kubernetes Pod is terminated -
Considerations for configuring Pods • Should the containers run on
the same node? (Containers in the same Pod always run in the same node) • Should the containers scale horizontally by the same number? (Pod are the units of scalability) • Should the containers be deployed together as a group? -
• A pod is a group of containers. • If
so... what’s shared and what’s isolated between containers in the same Pod? Isolation between containers in a Kubernetes Pod -
We learned the fundamentals of containers... Let's take a look!
• A pod is a group of containers. • If so... what’s shared and what’s isolated between containers in the same Pod? Isolation between containers in a Kubernetes Pod -
apiVersion: batch/v1 kind: Job metadata: name: two-containers-pod spec: template: #
This is the pod template spec: containers: - name: hello image: busybox command: ['sh', '-c', 'echo "first container" && sleep 3600'] - name: hello2 image: busybox command: ['sh', '-c', 'echo "second container" && sleep 3600'] restartPolicy: OnFailure # The pod template ends here This pod has 2 containers two-containers-pod.yml Isolation between containers in a Kubernetes Pod -
you can check the containers of the Pod on this
node (Kubernetes v1.20.2) Isolation between containers in a Kubernetes Pod -
The cgroup, user namespace are not isolated. *cgroup namespace Isolation
between containers in a Kubernetes Pod -
The mnt, uts, pid namespace are isolated (These are not
shared even for containers running on the same pod) Isolation between containers in a Kubernetes Pod -
Pause? What is this? The ipc and net namespaces are
shared between the containers in the pod → It’s possible to do IPC like using shared memory between containers. → The IP addresses and ports are shared between containers. It means that you have to be careful of port conflicts on containers in the same pod. Isolation between containers in a Kubernetes Pod -
Pause Container? The Pause container creates and holds isolated IPC
and Network namespaces. → The rest of the containers share these namespaces. This is to prevents issues in shared namespaces in the pod, even when a container running a user application terminates unexpectedly. -
Pause Container? 1. Terminates when SIGINT or SIGTERM is given,
without doing anything. -
Pause Container? 2. Plays the role of reaping zombie processes.
(if using PID namespace sharing option) 1. Terminates when SIGINT or SIGTERM is given, without doing anything. -
PID namespace sharing on Kubernetes If there is a risk
of Zombie process occurring in some containers, You can delegate the Zombie process reaping role to the Pause container by activating the Kubernetes “PID namespace sharing” option. • PID namespace sharing is enabled by default in Kubernetes v1.7. • However, from v1.8, it is disabled again due to compatibility issues with containers that depend on the init system. ◦ https://github.com/kubernetes/kubernetes/issues/48937 Reference link: https://www.ianlewis.org/en/almighty-pause-container -
apiVersion: batch/v1 kind: Job metadata: name: two-containers-pod spec: template: #
This is the pod template spec: shareProcessNamespace: true containers: - name: hello image: busybox command: ['sh', '-c', 'echo "first container" && sleep 3600'] - name: hello2 image: busybox command: ['sh', '-c', 'echo "second container" && sleep 3600'] restartPolicy: OnFailure # The pod template ends here two-containers-pod.yml Just add this configuration. PID namespace sharing on Kubernetes -
pause container’s PID namespace In the container running “sleep” process,
you can see the other container processes running on the same pod. PID 1 is always “pause” process when enabling PID namespace sharing. PID namespace sharing on Kubernetes -
Creating a pod without Kubernetes (Exercise) # Docker version 19.03.15
# Run a `pause` container $ docker run -d --ipc="shareable" --name pause k8s.gcr.io/pause:3.2 # Run a container executing `sleep` command on the ipc, net, pid namespaces of pause container $ docker run -ti --rm -d --name sleep-busybox \ --net=container:pause \ --ipc=container:pause \ --pid=container:pause \ busybox sleep 3600 # Run a container executing `ps` command on the ipc, net, pid namespaces of pause container. # We can see the other container’s processes(pause, sleep) because the pid namespace is shared. $ docker run --rm --name ps-busybox \ --net=container:pause \ --ipc=container:pause \ --pid=container:pause \ busybox ps # Let’s take a look the namespaces of “sleep-busybox” container $ ps -ef | grep sleep $ sudo lsns -p <PID> -
Comparing with Kubernetes Pod (Exercise) apiVersion: batch/v1 kind: Job metadata:
name: practice spec: template: # This is the pod template spec: shareProcessNamespace: true containers: - name: sleep image: busybox command: ['sh', '-c', 'sleep 3600'] restartPolicy: OnFailure # The pod template ends here practice.yml $ kubectl apply -f practice.yml # Determine which node the pod is running on $ kubectl get pods -o wide # In the node, # let’s take a look at the `sleep` container’s namespaces node# $ ps -ef | grep sleep node# $ sudo lsns -p <PID> -
Recap: Kubernetes Pod Concept • What is a pod? ◦
The smallest unit that can be deployed in Kubernetes ▪ Pods are managed by various types of resources (Job, ReplicaSet, etc.) • A group of one or more containers ◦ running a single container ◦ running multiple containers ▪ Primary Container ▪ Sidecar Containers -
• It’s not recommended to run multiple processes in one
container ◦ Even if the container is running, we cannot guarantee that all processes are running well. • When a container in a Kubernetes Pod is shut down, Kubelet restarts the container according to the restartPolicy. • Considerations to configure pod ◦ Should the containers run on the same node? ◦ Should the containers scale horizontally by the same number? ◦ Should the containers be deployed together as a group? Recap: Kubernetes Pod Concept -
• Isolation between containers in Kubernetes Pods ◦ shared namespaces
with Host → cgroup, user ◦ shared namespaces with the containers in the same pod → ipc, net ◦ Isolated namespaces for each container → mount, uts, pid ▪ pid namespace sharing is optional • The pause container? ◦ Creates and holds isolated IPC and Network namespace. ◦ Plays the role of reaping zombie processes when enabling PID namespace sharing. Recap: Kubernetes Pod Concept