Quick-Start Guide

This is the quick-start guide that will assist you in spinning up a k2d instance and start consuming it.

Pre-requisites

To install and configure k2d, the following pre-requisites must be met:

  • docker server and client version 23.0+

  • kubectl version 1.26+

  • (optional) helm version 3.10+

  • Hardware Requirements

    • ARM v7 CPU of 700MHhz or higher

    • 512 MB RAM or higher

    • 16GB SD-Card or greater

Installation

The k2d image can run on the following platforms/archs: linux/amd64, linux/arm64, linux/386, linux/armv6 and linux/armv7.

The deployment of k2d is based on a simple Docker run:

docker run -d \
  --name k2d \
  --network host \
  --restart always \
  --env K2D_ADVERTISE_ADDR=YOUR_HOST_IP \
  --env K2D_SECRET=YOUR_OWN_SECRET \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  --volume /var/lib/k2d:/var/lib/k2d \
  portainer/k2d:1.0.0-alpha
  • Use the K2D_ADVERTISE_ADDR only if you have multiple net interfaces at the OS level

  • The K2D_SECRET is used as an authentication token to call k2d APIs:

    • If the K2D_SECRET environment variable is not set, it will be automatically generated, and you can retrieve it in the k2d container logs

  • Use of the host network is not mandatory. Using the bridge network is supported:

    • K2D_ADVERTISE_ADDR must be specified when using the bridge network

    • Docker Desktop for Mac and Docker Desktop for Windows do not support HOST network, so you must use bridge network if testing on these platforms.

  • Use of the volume mounts is for:

    • The /var/run/docker.sock must be mounted to interact with the underlying Docker Engine server

    • The /var/lib/k2d path must be persisted for:

      • SSL certificates

      • Secrets

      • Configmaps

      • Kubernetes Token

Usage

The k2d instance exposes an API endpoint to return a kubeconfig format to start interacting with it:

Note: You must use the K2D_SECRET to retrieve the config file successfully.

curl --insecure -H "x-k2d-secret: YOUR_OWN_SECRET" https://YOUR_HOST_IP:6443/k2d/kubeconfig

The output can be saved directly to your local ~/.kube/config, or set KUBECONFIG environment variable. You can treat it just like Kubernetes!

Now, use kubectl to interact with the k2d translator:

(⎈|k2d:N/A)➜  k2d  kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
k2d                         1/1     Running   0          47m

helm will also be supported as long as the chart contains the resources supported by k2d.

Common IIoT application manifest examples

Node-Red

Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-red
spec:
  replicas: 1
  selector:
    matchLabels:
      app: node-red
  template:
    metadata:
      labels:
        app: node-red
    spec:
      containers:
        - image: nodered/node-red
          imagePullPolicy: Always
          name: node-red
          ports:
            - containerPort: 1880
              protocol: TCP
          env:
            - name: TZ
              value: Europe/London
            - name: PGID
              value: "1000"
            - name: PUID
              value: "1000"
          volumeMounts:
          - mountPath: /data
            name: nodered-data
      volumes:
      - name: nodered-data
        hostPath:
          path: /var/lib/k2d/data/nodered
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: node-red
  name: node-red
spec:
  ports:
    - name: "1880"
      port: 1880
      protocol: TCP
      targetPort: 1880
  selector:
    app: node-red
  type: NodePort

Eclipse Mosquitto

Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mosquitto
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mosquitto
  template:
    metadata:
      labels:
        app: mosquitto
    spec:
      containers:
      - name: mosquitto
        image: eclipse-mosquitto
        ports:
        - containerPort: 1883
        volumeMounts:
        - mountPath: /mosquitto/config/mosquitto.conf
          name: config
      volumes:
      - name: config
        configMap:
          name: mosquitto-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mosquitto-config
data:
  mosquitto.conf: |
    listener 1883
    allow_anonymous true
---
apiVersion: v1
kind: Service
metadata:
  name: mosquitto-mqtt
spec:
  # Not specifying the type will make it ClusterIP by default
  selector:
    app: mosquitto  
  ports:
    - protocol: TCP
      port: 1883
      targetPort: 1883
  type: NodePort

Influx-DB

Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: influxdb-deployment
spec:
  selector:
    matchLabels:
      app: influxdb
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: influxdb
    spec:
      containers:
        - image: influxdb:1.7.4
          name: influxdb
          ports:
            - containerPort: 8086
            # This examples uses hostPort that will expose InfluxDB
            # at the host level without the user of a Kubernetes Service
            - hostPort: 8086
          volumeMounts:
            - mountPath: /var/lib/influxdb
              name: influxdb-data
            - mountPath: /etc/influxdb/influxdb.conf
              name: influxdb-config
              subPath: influxdb.conf
              readOnly: true
          envFrom:
            - secretRef:
                name: influxdb-secrets
      volumes:
        - name: influxdb-data
          hostPath:
            path: /var/lib/k2d/data/influxdb-data
        - name: influxdb-config
          configMap:
            name: influxdb-config

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: influxdb-config
data:
  influxdb.conf: |+
    reporting-disabled = false
    bind-address = "127.0.0.1:8088"

    [meta]
      dir = "/var/lib/influxdb/meta"
      retention-autocreate = true
      logging-enabled = true

    [data]
      dir = "/var/lib/influxdb/data"
      index-version = "inmem"
      wal-dir = "/var/lib/influxdb/wal"
      wal-fsync-delay = "0s"
      query-log-enabled = true
      cache-max-memory-size = 1073741824
      cache-snapshot-memory-size = 26214400
      cache-snapshot-write-cold-duration = "10m0s"
      compact-full-write-cold-duration = "4h0m0s"
      max-series-per-database = 1000000
      max-values-per-tag = 100000
      max-concurrent-compactions = 0
      max-index-log-file-size = 1048576
      trace-logging-enabled = false
      tsm-use-madv-willneed = false

    [coordinator]
      write-timeout = "10s"
      max-concurrent-queries = 0
      query-timeout = "0s"
      log-queries-after = "0s"
      max-select-point = 0
      max-select-series = 0
      max-select-buckets = 0

    [retention]
      enabled = true
      check-interval = "30m0s"

    [shard-precreation]
      enabled = true
      check-interval = "10m0s"
      advance-period = "30m0s"

    [monitor]
      store-enabled = true
      store-database = "_internal"
      store-interval = "10s"

    [subscriber]
      enabled = true
      http-timeout = "30s"
      insecure-skip-verify = false
      ca-certs = ""
      write-concurrency = 40
      write-buffer-size = 1000

    [http]
      enabled = true
      bind-address = ":8086"
      auth-enabled = true
      log-enabled = true
      suppress-write-log = false
      write-tracing = false
      pprof-enabled = true
      debug-pprof-enabled = false
      https-enabled = false
      https-certificate = "/etc/ssl/influxdb.pem"
      https-private-key = ""
      max-row-limit = 0
      max-connection-limit = 0
      shared-secret = ""
      realm = "InfluxDB"
      unix-socket-enabled = false
      unix-socket-permissions = "0777"
      bind-socket = "/var/run/influxdb.sock"
      max-body-size = 25000000
      access-log-path = ""
      max-concurrent-write-limit = 0
      max-enqueued-write-limit = 0
      enqueued-write-timeout = 30000000000

    [logging]
      format = "auto"
      level = "info"
      suppress-logo = false

    [ifql]
      enabled = false
      log-enabled = true
      bind-address = ":8082"

    [[graphite]]
      enabled = false
      bind-address = ":2003"
      database = "gatling"
      retention-policy = ""
      protocol = "tcp"
      batch-size = 5000
      batch-pending = 10
      batch-timeout = "1s"
      consistency-level = "one"
      separator = "."
      udp-read-buffer = 0
      templates = [
        "gatling.*.*.*.*.*.* measurement.test.injector.simulation.request.status.field",
        "gatling.*.*.*.users.*.* measurement.test.injector.simulation.measurement.request.field"
      ]

    [[collectd]]
      enabled = false
      bind-address = ":25826"
      database = "collectd"
      retention-policy = ""
      batch-size = 5000
      batch-pending = 10
      batch-timeout = "10s"
      read-buffer = 0
      typesdb = "/usr/share/collectd/types.db"
      security-level = "none"
      auth-file = "/etc/collectd/auth_file"
      parse-multivalue-plugin = "split"

    [[opentsdb]]
      enabled = false
      bind-address = ":4242"
      database = "opentsdb"
      retention-policy = ""
      consistency-level = "one"
      tls-enabled = false
      certificate = "/etc/ssl/influxdb.pem"
      batch-size = 1000
      batch-pending = 5
      batch-timeout = "1s"
      log-point-errors = true

    [[udp]]
      enabled = false
      bind-address = ":8089"
      database = "udp"
      retention-policy = ""
      batch-size = 5000
      batch-pending = 10
      read-buffer = 0
      batch-timeout = "1s"
      precision = ""

    [continuous_queries]
      log-enabled = true
      enabled = true
      query-stats-enabled = false
      run-interval = "1s"

    [tls]
      min-version = ""
      max-version = ""

---
apiVersion: v1
kind: Secret
metadata:
  name: influxdb-secrets
type: Opaque
stringData:
  INFLUXDB_CONFIG_PATH: /etc/influxdb/influxdb.conf  
  INFLUXDB_ADMIN_USER: admin  
  INFLUXDB_ADMIN_PASSWORD: kraken  
  INFLUXDB_DB: gatling  
  INFLUXDB_USER: user  
  INFLUXDB_USER_PASSWORD: kraken

Grafana

Don't forget to run chown -R 472:472 grafana/ at the host level

Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: grafana
  name: grafana
spec:
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
      containers:
        - name: grafana
          image: grafana/grafana:9.1.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
              name: http-grafana
              protocol: TCP
          # readiness and liveness probes will be ignored by k2d
          # as they are not yet supported
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /robots.txt
              port: 3000
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 30
            successThreshold: 1
            timeoutSeconds: 2
          livenessProbe:
            failureThreshold: 3
            initialDelaySeconds: 30
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: 3000
            timeoutSeconds: 1
          # requests will be ignored by k2d as they are not yet supported
          resources:
            requests:
              cpu: 250m
              memory: 750Mi
          volumeMounts:
            - mountPath: /var/lib/grafana
              name: grafana-pv
          user:
      volumes:
        - name: grafana-pv
          hostPath:
            path: /var/lib/k2d/data/grafana
---
apiVersion: v1
kind: Service
metadata:
  name: grafana
spec:
  ports:
    - port: 3000
      protocol: TCP
      targetPort: 3000
  selector:
    app: grafana
  type: NodePort

NGINX

Manifest
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - protocol: TCP
    port: 80 
    targetport: 80
    # You can specify a custom nodePort
    nodePort: 30779
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

The List of Supported API Resources

To identify which Kubernetes API resources are supported by k2d, you can run kubectl api-resources to obtain the list:

(⎈|k2d:N/A)➜  k2d  kubectl api-resources
NAME                       SHORTNAMES   APIVERSION                NAMESPACED   KIND
configmaps                 cm           v1                        true         ConfigMap
events                                  v1                        false        Event
namespaces                 ns           v1                        false        Namespace
nodes                                   v1                        false        Node
pods                                    v1                        true         Pod
secrets                                 v1                        true         Secret
services                   svc          v1                        true         Service
deployments                             apps/v1                   true         Deployment
selfsubjectaccessreviews                authorization.k8s.io/v1   false        SelfSubjectAccessReview
events                                  events.k8s.io/v1          false        Event

Last updated