Skip to content
DevOps Madnessa blog by Ioannis Moustakis

Continuous Development for Kubernetes with Skaffold

· 5 min read

Developing modern applications can be a process that involves many steps. Some of these include writing your code, testing, builds, local deployments, persisting your code in a Git repository, running CI/CD jobs, deploying to an environment.

Skaffold attempts to simplify this workflow for developers by gathering all these processes in one place, allowing them to focus on writing code.

Why Skaffold

Skaffold logo

Skaffold is an open-source project from Google and has some characteristics that make it attractive for developers.

It’s extremely lightweight, runs everywhere with easy-to-remember skaffold commands, has a lot of features for Kubernetes-native development, and is built with optimized development in mind.

Even more, it enables you to focus on your application development locally while Skaffold continuously deploys to your local or remote Kubernetes cluster.

Detects changes in your source code and handles anything related to build, push, test, and deploy automatically. Can be used effectively as part of your CI/CD process as it provides the building blocks of these pipelines.

Simplicity is considered a core aspect of the project as everything is defined in a single pluggable, declarative configuration for your project, the skaffold.yaml file.

Sounds promising right? Let’s go give it a spin!

Simplify your local development experience with Skaffold

First things first; follow this link to install it according to your environment if you would like to follow along.

To simulate a Kubernetes cluster in order to deploy our demo app I am going to use the Kubernetes mode from Docker Desktop, which provides us a single node cluster.

For the needs of this article, we’ll use a simple go web server as our demo application. You can find all the files used for the demo here.

Our main.go file

package main

import (
	"fmt"
	"log"
	"net/http"
)

func handler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "Testing Skaffold")
}

func main() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Dockerfile

FROM golang:1.15.5

COPY main.go .
RUN go build -o /webserver main.go
CMD ["/webserver"]

Kubernetes simple pod manifest

apiVersion: v1
kind: Pod
metadata:
  name: webserver
  labels:
      name: webserver
spec:
  containers:
  - name: webserver
    image: moustakis/skaffold-demo

and service to expose our web app

apiVersion: v1
kind: Service
metadata:
  name: webserver
spec:
  selector:
    name: webserver
  ports:
  - port: 8080
    targetPort: 8080
  type: LoadBalancer

At this stage, since we have our source code, Dockerfile and k8s manifests we can ask skaffold to generate a simple config for us to get started. Simple run skaffold init at the root of the project directory.

Terminal output of skaffold init generating skaffold.yaml with a docker build artifact and kubectl deploy manifests, then confirming the file was written

Amazing! skaffold.yaml has been created and our k8s manifests and image name were automatically filled for us.

We are ready to try the continuous development flow of skaffold by running skaffold dev next. During dev mode, Skaffold will watch for source file changes, and when it detects them, will rebuild & push your images, test built images, and redeploy the application to your cluster.

skaffold dev is considered Skaffold’s main mode of operation for development, as it allows us to work continuously while iterating our application.

Without any further modifications to the config file, we expect skaffold to build locally our docker image and deploy it to our local k8s cluster.

Terminal output of skaffold dev: it tags the image, builds the Dockerfile with the local docker daemon, deploys the pod and service, waits for deployments to stabilize and watches for changes

Super! We already see from the command output that our image was built successfully and the k8s manifests seem to have been deployed. Let’s verify.

Terminal output of kubectl get pods, svc and ep showing the webserver pod running and its LoadBalancer service on port 8080

Time to check our web server.

Browser at localhost:8080 showing the demo response Testing Skaffold

Great success, that was extremely easy. What happens if we modify our source code and hit save?

In our main.go file I will change the message as shown below to test the continuous development process.

Editor with main.go where the handler response line is changed to: This has been changed. Do your magic skaffold!

The moment we hit save, we notice skaffold picked up the changes and reruns the whole flow. After refreshing the browser and we have the updated message, sweet!

Browser at localhost:8080 showing the updated response: This has been changed. Do your magic skaffold!

Something to note here is that the behavior of skaffold in these cases is always predictable as the actions have certain precedence over one another.

  1. File sync

  2. Build

  3. Test

  4. Deploy

You might prefer to trigger the flow yourself. In this case, skaffold run is your friend. Skaffold will perform the workflow described in skaffold.yaml exactly once.

For debugging purposes try skaffold debug. This mode configures specific containers for debugging in pods per the container’s runtime technology. Check this page for more information about how the debug mode works.

To get an overview of all possible commands simply type skaffold on your terminal.

Terminal output of the skaffold command listing its end-to-end pipelines (run, dev, debug), building blocks (build, test, deploy, delete, render, apply), init and other commands

For detailed information around all the possible configuration options of skaffold.yaml check this page.

Architecture Design and Pipeline Stages

The chain of actions that Skaffold performs can be shown in this picture:

Skaffold pipeline stages: detect source changes, build, test, tag, render manifests, deploy, then tail logs and forward ports, with file sync and cleanup on exit

A great feature of Skaffold is that it’s designed with pluggability and customizability in mind. You can modify the workflow according to your needs, skip stages, pick your favorite tools for each stage.

Check this image for different combinations of tools that you can use for each stage.

Skaffold architecture diagram: source changes feed artifact builds (Dockerfile, Bazel or Jib — locally, in-cluster with kaniko, or on Google Cloud Build), then testing, tagging by git commit, sha256, template or date, pushing, and deploying with kubectl, Helm or Kustomize; files needing no compilation sync directly to the pod

That’s all folks, hope you enjoyed this. We explored skaffold, a tool that facilitates continuous development and promises to make our development workflows easier.

Ioannis

Cloud infrastructure architect and technical writer — IaC × AI agents. About