Using Skaffold to accelerate Microservice Development

Using Skaffold to accelerate Microservice Development
Photo by Growtika / Unsplash

When transitioning from monolithic application development to microservice development, it becomes more tedious to work on applications. Microservices can often use different technologies, but can also utilize Kubernetes native components which you cannot easily simulate with other tools like Docker Compose.

Skaffold and other development tools built for Kubernetes help you accelerate your development, allowing you to deploy your microservices application directly onto Kubernetes, with the help of Helm or Kustomize. Using Helm will allow us to easily pull in other dependencies in our own chart and application as well.

Repository configuration

When starting to work on microservice development, we have a few repository choices to think about, such as:

  • Monorepo: all your microservices use the same repository as base, allowing you to easily build re-useable components and sharing of dependencies (e.g. you generate the RPC server and client library). You can easily submit a single PR with your new features.
  • Multirepo: all your microservices are scattered over multiple repositories. This allows for a more decoupled structure, shorter build steps, etc. However, when working on multiple services you need to submit multiple PRs which either need to be work independently or need to be strictly linked to prevent only part of your functionality from being deployed.

For this article, we will focus on the Monorepo approach, but you could also create a separate repository for your Skaffold files and update your paths to link to the Dockerfiles in multiple repositories.

Getting started

This section can be skipped if you already have a Kubernetes environment and Skaffold installed.

Skaffold installation

First you will need to install Skaffold. MacOS users can install it using brew. For other platforms, please follow the instructions on the Skaffold website.

brew install skaffold

Kubernetes environment

The other requirement is that we need a Kubernetes cluster to deploy this on. This can be any cluster, be it in the cloud, on bare-metal or running on your development machine. I recommend using kind (Kubernetes in Docker).

Once again, for MacOS users you can install it using brew. For other platforms you can install it using Go.

# MacOS
brew install kind

# Others (please check the latest version)
go install sigs.k8s.io/[email protected]

Now we need to setup our cluster by running the following command:

kind cluster create

Configuration

Skaffold can be configured using a skaffold.yaml file which allows you to configure your containers and Kubernetes manifests.

Project structure

Before we start creating the file, let's setup our repository structure:

monorepo.git
- skaffold.yaml
- svcs
  - svc-1
    - Dockerfile
    - src/**
  - svc-2
    - Dockerfile
    - src/**
.kubernetes
  - Chart.yaml
  - values.yaml
  - templates
    - svc-1
      - deployment.yaml
      - service.yaml
    - svc-2
      - deployment.yaml
      - service.yaml

An minimal repository file structure

Our file structure contains the following elements:

  • The skaffold.yaml-configuration file allowing us to configure our repository.
  • A services (svcs) folder containing our different microservices.
    • Each service (svc) has it's own Dockerfile to build a container and it's source code.
  • A Helm folder (.kubernetes) which contains the Kubernetes manifests and helm templates.
    • We can add sub-charts for a Redis, DB or other 3rd party tool as dependency in our Helm's Chart.yaml.

Using this structure, we have 2 microservices we wish to run using Skaffold. Let's start configuring. A Skaffold configuration is split into 2 sections.

  • How to build the containers
  • How to deploy the containers

Build configuration

#nonk8s <- tell our editor to not treat this as a Kubernetes resource
apiVersion: skaffold/v4beta6
kind: Config
build:
  local: 
    push: false
  repository
    tagPolicy:
      sha256: {}
  artifacts:
    - context: svcs/svc-a
      docker:
        target: dev
        buildArgs: []
      image: yourrepository/svc-a
      sync:
        manual:
          - src: "**/*.go"
          - dest: .
    - context: svcs/svc-b
      docker:
        target: dev
        buildArgs: []
      image: yourrepository/svc-b
      sync:
        manual:
          - src: "**/*.go"
          - dest: .

Skaffold configuration for build

Artifacts are where you define each build artifact for every service. Point them to the folder where the Dockerfile is and add any additional parameters you would normally use when running docker build for this Dockerfile.

💡
When you are using the multirepo approach, point the context of each artifact to the correct Dockerfile location (e.g. by moving up a folder with ../svc-a)

Sync allows us to configure "hot-reload" functionality, by default Skaffold (when running in dev-mode) will rebuild the container after a file change, but by setting up a sync it will directly sync changed files. Your application should have hot-reload functionality for this to work. If it does not, leave out the sync section.

Now that Skaffold knows how to prepare the containers, it is time to configure the deployment.

Deploy configuration

Below our build key in our configuration file, we can continue with deployment configuration:

deploy:
  helm:
    releases:
      - name: application-name
        namespace: application-namespace
        createNamespace: true
        chartPath: .kubernetes
        setValues:
          "svcA.image.repository": yourrepository/svc-a
          "svcA.image.tag": ""
          "svcB.image.repository": yourrepository/svc-b
          "svcB.image.tag": ""
          # other key value pairs you would override from your values.yaml

Skaffold configuration for deployment

Please note that we are setting the repository value exactly as defined in the build step. This allows Skaffold to automatically inject the correct sha256-hash as tag after it has completed the build step of the container.

It is important that your Helm chart allows replacing the image repository and tag in the deployment.yaml of your services and these are defined in your values.yaml. For example:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: svc-a
          image: "{{ .Values.svcA.image.repository }}:{{ .Values.svcA.image.tag }}"

Helm example to load container image from values

Final steps

You can now run either skaffold dev or skaffold debug and your application will be launched on your Kubernetes environment.

You can either access the application through your Ingress resource if you created this in your Helm repository, or use the --port-forward flag to expose the port on the container.

Other perks are that this very closely resembles your Kubernetes production deployment, preventing bugs between your local development environment and Kubernetes.

If you have any questions, or would like to have a further explanation on any of these topics, feel free to let me know in the comments section below.

If you would like to be the first to be notified on new topics, make sure to subscribe below.

Happy coding!