Share
Embed
Developer Tooling for Kubernetes Configuration
Talk from Configuration Management Camp, a rerun and rebranding of the talk I gave at KubeCon. All about testing and linting tools for Kubernetes configs, taking inspiration from the various configuration management tools.
Gareth Rushgrove
February 05, 2018
Transcript
74 slides
-
- Infrastructure as code - Validation, linting and unit testing
- Demos and examples -
Infrastructure as code is the process of managing and provisioning
computers through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools https://en.wikipedia.org/wiki/Infrastructure_as_Code -
Increasingly configuration: - Contains logic - Takes arguments - Interfaces
with other configuration - Spans multiple versions of software -
As the community adopts higher-level tools like Helm, ksonnet, Compose,
Kedge, Kapitan, etc. this becomes more relevant to Kubernetes users -
Take bitnami/kube-manifests as an example. It contains 7000+ lines of
JSON in 159 files generated from 2000 lines of Jsonnet. -
Sidenote Brian Grant from Google found 43 tools for managing
K8S configurations -
I posit that the lessons learnt applying testing practices to
infrastructure as code apply to Kubernetes configs -
Widely adopted tools break down into - Validation - Linting
- Unit testing - Acceptance testing -
apiVersion: v1 kind: Service metadata: name: redis-master labels: app: redis
role: master tier: backend spec: ports: - port: 6379 targetPort: "6379" selector: app: redis role: master1 tier: backend Is this a valid Kubernetes configuration file? -
Is this a valid Kubernetes configuration file? apiVersion: v1 kind:
ReplicationController spec: replicas: none selector: app: nginx loadbalancer: lb-1 templates: name: nginx labels: backend app: nginx spec: containers: - name: nginx image: nginx -
Is this Helm template valid for Kubernetes? apiVersion: v1 kind:
Service metadata: name: {{ template "fullname" . }} labels: app: {{ template "fullname" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" spec: ports: - name: memcache port: 11211 targetPort: memcache selector: app: {{ template "fullname" . }} -
Is this Puppet code valid for Kubernetes? kubernetes_pod { 'sample-pod':
ensure => present, metadata => { namespace => 'default', }, spec => { containers => [{ name => 'container-name', image => 'nginx', }] }, } -
Kubernetes has a well-defined set of API primitives; Pods, Deployments,
Services, ReplicationControllers, etc. -
That’s a lot of JSON PS> Get-Content -Path swagger.json |
Measure-Object -line).Lines 85340 PS> (Get-ChildItem -Path v*/*.json -Recurse | Measure-Object).Count 26181 PS> (Get-ChildItem -Path v*/*.json -Recurse | Get-Content | Measure-Object -line).Lines 7296392 -
Validate directly using the schemas $ jsonschema -F "{error.message}" -i
hello-nginx.json 1.6.6-standalone/deployment.json u'template' is a required property -
Validation is useful for - Catching typos - Fast feedback
- Missing properties - Checking against multiple K8 versions -
A nice CLI for validating K8S configurations $ kubeval --help
Validate a Kubernetes YAML file against the relevant schema Usage: kubeval <file> [file...] [flags] Flags: -h, --help help for kubeval -v, --kubernetes-version string Version of Kubernetes to validate against --openshift Use OpenShift schemas instead of upstream Kubernetes --schema-location string Base URL used to download schemas. Can also be specified with the environment variable KUBEVAL_SCHEMA_LOCATION --version Display the kubeval version information and exit -
Validate YAML files on the command line $ kubeval my-invalid-rc.yaml
The document my-invalid-rc.yaml contains an invalid ReplicationController --> spec.replicas: Invalid type. Expected: integer, given: string $ echo $? 1 $ cat my-invalid-rc.yaml | kubeval The document my-invalid-rc.yaml contains an invalid ReplicationController --> spec.replicas: Invalid type. Expected: integer, given: string $ echo $? 1 -
Validate multiple resources in the same file $ kubeval multi.yaml
The document fixtures/multi.yaml contains a valid Service The document fixtures/multi.yaml contains an invalid Deployment --> spec.template.spec.containers.0.env.0.value: Invalid type. Expected: string, given: integer The document fixtures/multi.yaml contains an invalid ReplicationController --> spec.replicas: Invalid type. Expected: integer, given: string The document fixtures/multi.json contains a valid Deployment The document fixtures/multi.yaml contains a valid ReplicationController -
Validate against multiple versions of Kubernetes $ kubeval --v 1.7.9
my-invalid-rc.yaml The document my-invalid-rc.yaml contains an invalid ReplicationController --> spec.replicas: Invalid type. Expected: integer, given: string $ kubeval --v 1.8.1 my-invalid-rc.yaml The document my-invalid-rc.yaml contains an invalid ReplicationController --> spec.replicas: Invalid type. Expected: integer, given: string -
Use as a library in other Go tools import (
"github.com/garethr/kubeval/kubeval" ) results, err := kubeval.Validate(fileContents, fileName) -
Lots of teams have a script to check certain properties
of their K8S configurations against internal policies -
Our internal tooling includes a linter that renders helm charts
and validates the resources produced pass certain internal rules. -
- Podspec needs resource requests - Ensure certain labels are
set - Prevent usage of latest images - Prohibit privileged containers - Enforce naming conventions -
Run tests against your configurations $ kubetest rc.yaml --verbose INFO
rc.yaml should not use latest images WARN rc.yaml ReplicationController should have at least 4 replicas -
Skylark is a dialect of Python. It is an untyped
dynamic language with high-level data types, first-class functions with lexical scope, and garbage collection -
A Skylark interpreter is typically embedded within an application which
may define additional domain-specific functions and data types beyond those provided by the core language -
Example tests for kubetest #// vim: set ft=python: def test_for_latest_image():
if spec["kind"] == "ReplicationController": for container in spec["spec"]["template"]["spec"]["containers"]: tag = container["image"].split(":")[-1] assert_not_equal(tag, "latest", "should not use latest images") def test_minimum_replicas(): if spec["kind"] == "ReplicationController": test = spec["spec"]["replicas"] >= 4 assert_true(test, "ReplicationController should have at least 4 replicas") test_for_latest_image() test_minimum_replicas() -
Tests enforcing a team label #// vim: set ft=python: def
test_for_team_label(): if spec["kind"] == "Deployment": labels = spec["spec"]["template"]["metadata"]["labels"] assert_contains(labels, "team", "should indicate which team owns the deployment") test_for_team_label() -
Many assertions are common, they are the result of emerging
community best-practice -
Using kubeval with Helm $ git clone https://github.com/kubernetes/helm.git $ cd
helm/docs/examples $ ls alpine nginx README.md $ helm template nginx | kubeval The document stdin contains a valid Secret The document stdin contains a valid ConfigMap The document stdin contains a valid Service The document stdin contains a valid Pod The document stdin contains a valid Deployment The document stdin contains a valid Job -
Tests for our Helm Chart #// vim: set ft=python: def
test_for_latest_image(): if spec["kind"] in ["Job", "Deployment"]: for container in spec["spec"]["template"]["spec"]["containers"]: tag = container["image"].split(":")[-1] assert_not_equal(tag, "latest", spec["kind"] + " should not use latest images") test_for_latest_image() -
Using kubetest with Helm $ helm template nginx | kubetest
--verbose INFO stdin Deployment should not use latest images INFO stdin Job should not use latest images -
Using kubeval with ksonnet $ cat deployment.jsonnet local k =
import "ksonnet.beta.2/k.libsonnet"; local deployment = k.apps.v1beta1.deployment; local container = deployment.mixin.spec.template.spec.containersType; local containerPort = container.portsType; // Create nginx container with container port 80 open. local nginxContainer = container.new("nginx", "nginx:1.13.0") + container.ports(containerPort.newNamed("http", 80)); // Create default Deployment object from nginx container. deployment.new("nginx", 5, nginxContainer, {app: "nginx"}) -
Using kubeval with ksonnet $ cat deployment.jsonnet local k =
import "ksonnet.beta.2/k.libsonnet"; local deployment = k.apps.v1beta1.deployment; local container = deployment.mixin.spec.template.spec.containersType; local containerPort = container.portsType; // Create nginx container with container port 80 open. local nginxContainer = container.new("nginx", "nginx:1.13.0") + container.ports(containerPort.newNamed("http", 80)); // Create default Deployment object from nginx container. deployment.new("nginx", 5, nginxContainer, {app: "nginx"}) $ jsonnet deployment.jsonnet | kubeval The document stdin contains a valid Deployment -
Using kubetest with ksonnet $ jsonnet deployment.jsonnet | kubetest --verbose
INFO stdin should not use latest images WARN stdin ReplicationController should have at least 7 replicas -
Using kubeval with Puppet $ cat guestbook.pp kubernetes_service { 'frontend':
ensure => 'present', metadata => { 'labels' => {'app' => 'guestbook', 'tier' => 'frontend'}, 'namespace' => 'default', }, spec => { 'type' => 'LoadBalancer', 'ports' => [ {'port' => 80, 'protocol' => 'TCP'} ], 'selector' => { 'app' => 'guestbook', 'tier' => 'frontend' }, }, -
Using kubeval with Puppet $ puppet kubernetes convert --manifest guestbook.pp
| kubeval The document stdin contains a valid ReplicationController The document stdin contains a valid Service The document stdin contains a valid ReplicationController The document stdin contains a valid Service The document stdin contains a valid ReplicationController The document stdin contains a valid Service -
Using kubetest with Puppet $ puppet kubernetes convert --manifest guestbook.pp
| kubetest --verbose INFO stdin should not use latest images INFO stdin should not use latest images INFO stdin should not use latest images -
kubeval is a great tool to validate your Kubernetes configuration
files as part of your CI pipeline -
Some of the current tools lend themselves to native testing,
others are just data and templates -
Having testing tools that work with different configuration approaches can
make moving between tools easier