The following command will clone a repository containing a webapp with a microservice architecture built and deployed with Bazel and Skaffold.
The backend API is written in Go and the frontend is written using NextJS.
git clone git@github.com:ddlees/microservices.git
The Magic!
If everything is installed correctly, the following command will build and deploy the api and ui container images continuously. Make a change to api/api.go or ui/pages/index.tsx and watch how Bazel and Skaffold work together to propogate your change out to your cluster.
skaffold dev
Note: Changes to ui/pages/index.tsx will appear automatically. To see the ui consume any changes made to api/api.go be sure to refresh your browser.
How does it all work?
Working Backwards
The skaffold.yaml file
Here we tell skaffold how each artifact in the project is built. The api image is built using Skffold's integrated Bazel support so we only need to give it the Bazel target. The ui image is a little special; here we're relying on Bazel to pack our existing tooling into the container and letting skaffold sync the files. This allows teams to gradually migrate building their artifacts with Bazel if they wish to do so or not.
artifacts:-image:apibazel:target://api:image.tarargs:-'--platforms'-'@io_bazel_rules_go//go/toolchain:linux_amd64'-image:uicustom:buildCommand:'bazel run ui:latest --platforms @build_bazel_rules_nodejs//toolchains/node:linux_amd64'dependencies:paths:-ui/**/*
Working Backwards
The skaffold.yaml file
Skaffold supports syncing changed files to a deployed container to avoid the need to rebuild, redeploy, and restart the corresponding pod. Since we want to use our existing tooling to rebuild the ui, here we're telling skaffold which files it should sync to leverage the hot-reloading feature in our existing tooling.
Now that Skaffold knows how to build our container images we need to tell it how to deploy our images and which ports to forward from the cluster to the local machine.
In this case we're telling Skaffold to deploy our native Kubernetes manifests located in the k8s directory using kubectl.
The rules_go and rules_docker Bazel rule sets give us declarative APIs for telling Bazel what we want to build. Here we're telling Bazel to build a go binary for our host machine and a container image to run on our cluster.
The rules_nodejs Bazel rule set gives us API to leverage existing tooling for frontend development. In this case, we're having Bazel leverage existing tooling to build artifacts for production and development suitable for the host machine. Additionally, we're having Bazel wrap the tooling in a NodeJS container image for us to use on our cluster.
next(
name = "ui",
args = ["dev", "ui"],
data = _RUNTIME_DEPS + ["//:node_modules"],
)
next(
name = "dist",
args = ["build", "ui", "$(@D)"a],
data = _RUNTIME_DEPS + ["@npm//:node_modules"],
output_dir = True,
)
nodejs_image(
name = "image",
args = ["dev", "ui"],
data = _RUNTIME_DEPS + _DEPENDENCIES,
entry_point = "@npm//:node_modules/next/dist/bin/next",
)
Working Backwards
The WORKSPACE file
The WORKSPACE file is Bazel's way of declaring what rules and/or external dependencies required to build the artifacts
in your project. In lieu of explaining each WORKSPACE function, the gist of what this is doing in the example
repository is that it's pulling the required ruleset for building NodeJS/Javascript/Typescript projects, the ruleset
for building Golang projects and the ruleset for building container images.