Running DHCP in Kubernetes
My continuing venture in to Kubernetes led me to wanting to run DHCP inside of it.
Installing DHCP inside of a Docker container is simple. I chose to use Alpine as my base image because it is small and lightweight.
Here is my simple Dockerfile
:
FROM alpine:3.14.2
RUN apk add --no-cache dhcp
VOLUME /etc/bind
CMD ["dhcpd", "-4", "-d", "-cf", "/etc/dhcp/dhcpd.conf"]
The breakdown of the arguments is this
-4
means run as an IPv4 DHCP server-d
is for Debug, or what we use it for, run in the foreground-cf
is for the config file.
Build that image and push it up to your registry.
For K8s, there will be 3 manifests needed to deploy it. The deployment for the pod, a persistent volume claim for the DHCP leases and the secret containing the dhcpd.conf
file. The reason we do a secret for the dhcpd.conf
file is because you store the ddns
update key in there. If you don't need it then you could modify it to be a config map.
The key part in the deployment
is to run on the hostNetwork
. That way the pod can directly access the network card and respond to DHCP requests. You also need to expose UDP port 67 and have a volume for the leases so it can persist those across pod restarts.
Here is my deployment file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: dhcp
spec:
selector:
matchLabels:
app.kubernetes.io/component: dhcp
app.kubernetes.io/part-of: dhcp
replicas: 1
revisionHistoryLimit: 1
template:
metadata:
labels:
app.kubernetes.io/name: dhcp
app.kubernetes.io/instance: dhcp
app.kubernetes.io/component: dhcp
app.kubernetes.io/part-of: dhcp
spec:
hostNetwork: true
imagePullSecrets:
- name: exampleregistry
containers:
- name: dhcp4
image: example.azurecr.io/dhcp4:1.0.6
resources:
requests:
memory: 10Mi
cpu: 10m
limits:
memory: 200Mi
cpu: 500m
volumeMounts:
- mountPath: "/var/lib/dhcp"
name: dhcp-leases
readOnly: false
- mountPath: "/etc/dhcp"
name: dhcp-secrets
readOnly: true
ports:
- containerPort: 67
hostPort: 67
protocol: UDP
name: dhcp
volumes:
- name: dhcp-leases
persistentVolumeClaim:
claimName: dhcpleases
- name: dhcp-secrets
secret:
secretName: dhcp
The PVC is simple. You can probably get away doing a ReadWriteOnce
. But I always do ReadWriteMany
on all of my PVC's. It allows for future flexibility if needed.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dhcpleases
labels:
app.kubernetes.io/name: dhcpleases
app.kubernetes.io/instance: dhcpleases
app.kubernetes.io/component: dhcp
app.kubernetes.io/part-of: dhcp
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
```
The secret will contain a single entry, dhcpd.conf
. This will contain the config file in its entirety.
apiVersion: v1
kind: Secret
metadata:
name: dhcp
type: Opaque
stringData:
dhcpd.conf: |
option domain-name "example.lan";
option domain-name-servers 192.168.0.190;
update-conflict-detection true;
default-lease-time 600;
max-lease-time 7200;
subnet 192.168.0.0 netmask 255.255.255.0 {
range 192.168.0.20 192.168.0.99;
option routers 192.168.0.1;
key "rndc-key" {
algorithm hmac-md5;
secret "xxx";
};
zone example.lan. {
primary 192.168.0.190;
key rndc-key;
}
zone 0.168.192.in-addr.arpa. {
primary 192.168.0.190;
key rndc-key;
}
ddns-update-style standard;
ddns-dual-stack-mixed-mode true;
authoritative;
Conclusion
Getting DHCP running in Kubernetes was the final piece of getting my entire network (aside from DNS) running Kubernetes. The reason I did not put my network DNS in Kubernetes is that it is required to start the Kubernetes cluster and pull the image. A chicken and the egg problem arises if I put DNS in it. I do however, have DNS running in a Docker container on a Raspberry Pi. More on that later.
This was a fun project that I kind of enjoyed doing.