Skaffold: Local development with Kubernetes

Skaffold is an amazing tool for doing local development with a kubernetes cluster. I’m able to locally test my changes, without having to re-deploy all the time. The productivity boost for our dev teams, is speeding up feature development and enabling a faster release cycle.
I started using Skaffold for all new projects in my current position, well for any new microservice in our ETL data pipeline and it works pretty well on a minikube cluster.
Getting Started with tooling
Let’s get our minikube cluster setup. Install minikube locally by using homebrew:
$ brew install minikube
Make sure it was installed successfully:
$ minikube version
Output:
minikube version: v1.27.1commit: fe869b5d4da11ba318eb84a3ac00f336411de7ba
Start minikube:
$ minikube start
Use the minikube addon to enable the ingress nginx controller:
$ minikube addons enable ingress
Install skaffold on your machine:
$ brew install skaffold
Validate that the install was successful:
$ skaffold version
The skaffold docs talk about their init command, which is pretty easy to run:
$ skaffold init
So if your project has no kubernetes manifests yet, the init command tries to bootstrap your project for you. Now you can interact with all common cli tools such as kubectl & kustomize
Getting started with our app
For this skaffold demo, we’re going to create a basic NodeJS app. Add your package.json to the root directory of this project:
{
"name": "skaffold-nodejs-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon src"
},
"repository": {
"type": "git",
"url": ""
},
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1"
},
"devDependencies": {
"chai": "^4.3.4",
"mocha": "^9.0.2",
"nodemon": "^2.0.12",
"supertest": "^6.1.3"
}
}
Now create a src
directory and create a file called index.js
and add:
const express = require('express');
const app = express();
app.get('/', (request, response) => {
response.status(200).send({ message: 'Local Development: Hello World!' });
});
app.listen(3000, () => {
console.log('Server is running!');
});
In the example above we have the following:
request: we define a route handler to make a simple GET request. The app
will send back a 200 response and display:
"Local Development: Hello World!"
on the browser.
our app will listen in on port 3000, here is a list of other places where you will need to make changes to accommodate for this:
Dockerfile: we're going to EXPOSE port 3000 right before we run npm start
service.yaml: the Service manifest port will be set to 3000
deployment.yaml: the Deployment manifest will set the containerPort to 3000
Create a Dockerfile, skaffold will use this Dockerfile to build your images:
FROM node:16.18.0 AS build
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "npm", "run", "start" ]
Creating the Deployment Manifest File
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: express-app
resources:
limits:
memory: 512Mi
cpu: "1"
ports:
- containerPort: 3000
Creating the Service Manifest File
Now we need to create a service manifest for your deployment & that looks like this:
apiVersion: v1
kind: Service
metadata:
name: app-svc
spec:
type: NodePort
selector:
app: app
ports:
- port: 3000
targetPort: 3000
Add your skaffold.yaml file
apiVersion: skaffold/v2beta21
kind: Config
profiles:
- name: dev
activation:
- command: dev
build:
artifacts:
- image: express-app
context: .
sync:
manual:
- src: 'src/**/*.js'
dest: .
If you look at the example above you’ll see that the manual sync rule must point out the src
of where the files live and the dest
field, which is the relative path of your image.
Bringing up your local cluster:
skaffold dev --port-forward
Now in this output, we can see:
Listing files to watch...
- express-app
Generating tags...
- express-app -> express-app:ca56ce4
Checking cache...
- express-app: Found Locally
Tags used in deployment:
- express-app -> express-app:c1365267f7badf38c83efb341be2521a041ac566d5796fbf38ca6ac7348774d6
Starting deploy...
- deployment.apps/app created
- service/app-svc created
that the deployment & service was created. Now, when any js file changes, Skaffold will continuously apply the deployment.yaml file & build your changes,