K3s Docker Compose Infrastructure Integration

The deployment of Kubernetes in local development environments has historically been hampered by the resource-heavy nature of standard distributions. K3s, a lightweight, CNCF-certified Kubernetes distribution and Sandbox project, addresses this by providing a single binary that operates using under 512MB of RAM. While k3d offers a high-level utility for managing these clusters, there is a profound technical advantage to utilizing Docker Compose for K3s orchestration. By leveraging a docker-compose.yml file, engineers can achieve a granular, declarative state of their local cluster without relying on external wrapper tools. This approach transforms a complex orchestration platform into a manageable containerized service, allowing for precise control over networking, volume persistence, and resource constraints.

K3s Architectural Foundations in Containerized Environments

K3s is engineered specifically for low-resource environments, making it the ideal candidate for container-in-container execution. When deployed via Docker Compose, K3s functions as a single-node cluster by default, though it can be expanded. The architecture is designed to be lean, stripping away unnecessary legacy cloud providers and utilizing a simplified structure to ensure it fits within the tight memory constraints of a developer's workstation.

The primary objective of running K3s through Docker Compose is to eliminate the overhead associated with virtual machines while maintaining a production-like environment. This allows for fast iteration times, enabling developers to test manifests and Helm charts in a real Kubernetes environment rather than relying on simple docker-compose service stacks.

Deep Dive into K3s Docker Compose Implementation

Implementing K3s via Docker Compose requires a specific configuration to ensure the Kubernetes API server and the underlying container runtime function correctly within the Docker isolation layer.

The Declarative Configuration

The core of this deployment is the docker-compose.yml file. This file defines the server role, the image version, and the necessary system permissions.

yaml version: '3.7' services: server: image: rancher/k3s:v1.24.0-rc1-k3s1-amd64 networks: - default command: server tmpfs: - /run - /var/run ulimits: nproc: 65535 nofile: soft: 65535 hard: 65535 privileged: true restart: always environment: # - K3S_TOKEN=${K3S_PASSWORD_1} # Only required if we are running more than 1 node - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml - K3S_KUBECONFIG_MODE=666 volumes: - k3s-server:/var/lib/rancher/k3s # This is just so that we get the kubeconfig file out - ./k3s_data/kubeconfig:/output - ./k3s_data/docker_images:/var/lib/rancher/k3s/agent/images expose: - "6443" # Kubernetes API Server - "80" # Ingress controller port 80 - "443" # Ingress controller port 443 ports: - 6443:6443 volumes: k3s-server: {} networks: default: ipam: driver: default config: - subnet: "172.98.0.0/16" # Self-defined subnet on local machine

Technical Breakdown of Configuration Parameters

The configuration above is not merely a list of settings but a carefully tuned environment designed to overcome the limitations of Docker containers.

Privileged Mode and System Constraints

The privileged: true flag is a mandatory requirement for K3s in Docker. Because Kubernetes must manage network interfaces and mount filesystems, it requires elevated privileges that exceed standard container boundaries. Without this, the K3s binary cannot initialize the necessary kernel modules or manage the container runtime.

The ulimits section is critical for stability. By setting nproc and nofile (both soft and hard) to 65535, the system prevents the "too many open files" error, which is common in Kubernetes clusters where many pods are communicating simultaneously.

Resource Mapping and Persistence

To ensure the cluster is not ephemeral, volume mapping is utilized. The k3s-server named volume persists the cluster state in /var/lib/rancher/k3s. Additionally, host-path mappings are used for administrative access.

  • The mapping ./k3s_data/kubeconfig:/output allows the Kubeconfig file to be exported from the container to the host machine, enabling the use of kubectl from the host.
  • The mapping ./k3s_data/docker_images:/var/lib/rancher/k3s/agent/images ensures that container images are cached and managed efficiently.

Network Topology

The network configuration employs a custom subnet 172.98.0.0/16. Defining a specific subnet prevents IP collisions with other Docker networks on the host machine, ensuring that the Kubernetes internal networking does not conflict with existing infrastructure.

Operational Execution and Cluster Interaction

Once the configuration is defined, the deployment follows a strict sequence of operational steps to ensure the cluster is accessible.

Deployment Sequence

Before initiating the cluster, the directory structure must be prepared to receive the exported configuration files.

  1. Create the local data directory:
    mkdir -p k3s_data/kubeconfig

  2. Launch the cluster in detached mode:
    docker compose up -d

Accessing the Cluster

The Kubernetes API is exposed on port 6443. To interact with the cluster, the host's kubectl tool must be pointed to the exported Kubeconfig file. This is best achieved through a shell alias to avoid repetitive typing.

alias k='kubectl --kubeconfig '"${PWD}"'/k3s_data/kubeconfig/kubeconfig.yaml'

Once the alias is established, the cluster state can be verified by listing all pods across all namespaces.

k get pods -A

Expected System State

A successfully initialized K3s cluster will display several system-level pods in the kube-system namespace. These components are essential for the cluster's functionality.

Pod Name Role Status
local-path-provisioner Storage Provisioning Running
coredns DNS Resolution Running
metrics-server Resource Monitoring Running
traefik Ingress Controller Running
svclb-traefik Service Load Balancer Running

Comparative Analysis: K3s vs. RKE2 in Docker Compose

While K3s is the lightweight champion, RKE2 (Rancher Kubernetes Engine 2) is an alternative for those requiring higher security and FIPS 140-2 compliance.

RKE2 Implementation Details

RKE2 is more complex because it lacks a readily available official container image in the same manner as K3s. Users must often rely on community-built images or build their own.

The docker-compose.yml for RKE2 mirrors the K3s structure but utilizes different environment variables and paths:

  • Image used: sachua/rke2-test:v1.27.1-rke2r1
  • Volume path: /var/lib/rancher/rke2
  • Environment variable: RKE2_KUBECONFIG_OUTPUT

Performance and Boot-up Differences

RKE2 generally takes longer to load than K3s. This is due to a larger number of initial setup tasks and a more rigorous security posture. While K3s is optimized for speed and low memory, RKE2 is optimized for security and compliance.

Alternative Orchestration: k3d

For users who find Docker Compose too manual, k3d provides a high-level wrapper that simplifies the lifecycle of K3s clusters.

The k3d Framework

k3d is a small program designed specifically to run K3s in Docker. It uses a Docker image built from the K3s repository to spin up multiple nodes. This allows a single Docker host to run multiple simultaneous K3s clusters, each with its own set of server and agent nodes.

Capabilities of k3d

k3d extends the basic functionality of K3s in Docker through several automated features:

  • Cluster Lifecycle: Ability to create, stop, start, delete, grow, and shrink clusters and individual nodes.
  • Configuration: Management via command line flags or configuration files.
  • Registry Management: Integration with container registries for image pulling.
  • Image Import: Capability to import images directly from the local Docker daemon into the cluster's container runtime.
  • Kubeconfig Management: Automated merging of cluster Kubeconfigs into the user's default $HOME/.kube/config.

Advanced k3d Deployment Example

k3d allows for the creation of complex multi-node topologies with a single command. For example, a cluster with three servers and two agents can be spawned as follows:

k3d cluster create mycluster --api-port 127.0.0.1:6445 --servers 3 --agents 2 --volume '/home/me/mycode:/code@agent[*]' --port '8080:80@loadbalancer'

In this scenario, k3d creates six containers:
- 1 load balancer.
- 3 servers (control-plane nodes).
- 2 agents (worker nodes).

The --api-port flag maps the internal Kubernetes API port (6443) to a specific host port (6445).

Advanced K3s Configuration and Troubleshooting

Running K3s in a containerized environment introduces specific challenges regarding logging and service management.

Log Analysis

When K3s is running within a systemd environment, logs are typically found in /var/log/syslog and viewed using journalctl. However, in a Docker Compose setup, logs are accessed via the Docker daemon.

For those utilizing the install script for K3s, certain flags can be used to disable auto-starting:

curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_START=true INSTALL_K3S_SKIP_ENABLE=true sh -

Troubleshooting Connection Issues

If kubectl cannot connect to the K3s cluster, the following checks should be performed:

  • Verify the container status: docker ps should show the server container as Up.
  • Check the Kubeconfig: Ensure the file at ./k3s_data/kubeconfig/kubeconfig.yaml exists and has the correct permissions (mode 666).
  • Network Validation: Ensure port 6443 is not being blocked by a host firewall.

Analysis of Local Kubernetes Strategies

The choice between raw Docker Compose, k3d, and RKE2 depends entirely on the required balance between control, speed, and security.

Docker Compose provides the most transparent implementation. It allows an engineer to see exactly how the container is configured, how volumes are mounted, and how the network is structured. This is invaluable for those who want to learn the underlying mechanics of K3s or for those who need to integrate the cluster into a larger, multi-container application stack.

k3d, conversely, is a productivity tool. It abstracts the complexity of Docker Compose into a set of intuitive commands. Its ability to quickly scale clusters (growing or shrinking nodes) makes it superior for testing scalability or high-availability configurations locally.

RKE2 occupies the high-security niche. For organizations where FIPS 140-2 compliance is a requirement, RKE2 is the only viable option among the three. Although it is slower to boot and more complex to configure in Docker, it provides the security guarantees necessary for regulated industries.

In summary, K3s in Docker Compose represents a shift toward "Infrastructure as Code" for local development. By defining the cluster in a YAML file, the environment becomes reproducible, version-controllable, and easily shareable among a development team. This eliminates the "it works on my machine" problem by ensuring that every developer is running an identical Kubernetes control plane.

Sources

  1. sachua.github.io
  2. suse.com
  3. docs.k3s.io

Related Posts