Simple example deployment of a busybox container image to a k8s cluster set up in this previous tutorial using a couple of k8s constructs such as Deployments and Services, as well as using Helm. This tutorial will lay the groundwork for future exploration in GitOps flows using tools such as flux.
Background
GitOps is an interesting concept that captures essential topics around cloud-native deployments and management. Exploring tools such as flux to enable GitOps flows requires a workload that can be used to demonstrate the functionality (as deploying the fluxcd operator is not really valuable/interesting in itself). This tutorial will summarize the deployment of one of the smallest container image types, the busybox image, and have it respond to web GET requests with a simple “Hello” response.
Prerequisites
It’s assumed you follwed the previous tutorial and have a local k8s cluster running.
In addition, it’s also assumed that you have a functioning docker
environment that you can run locally (if this part is not true, you
can skip the next section “Implementation Using Docker”).
Implementation Using Docker
Let’s first test things locally using Docker on your workstation. Implementing a busybox web service is fairly straightforward. Using docker,
launch an instance of the busybox
container image as a daemon, mapping local port 8280 to the container port 8280. In addition, have the
container launch with a shell command that will write a simple Hello
string to an HTML page and use the httpd
binary start a simple web
server listening on port 8280 and serving contents from the /var/www/
directory:
$ docker run --rm -d -p 8280:8280 busybox \
sh -c "echo 'Hello' > /var/www/index.html && httpd -f -p 8280 -h /var/www/"
Once the above launches, you can perform a simple curl
request locally on your workstation and you should receive a response of Hello
:
$ curl http://localhost:8280/index.html
If this succeeds, you’re on your way and can deploy the service to k8s!
Deployment to k8s Cluster
There are many different ways to deploy a service to a k8s cluster. We’ll go over a couple of them, where Helm charts are the groundwork
we’ll lay for a future post dealing with fluxcd
.
Manual via kubectl
To deploy the same functionality as done prior using docker
, we’ll start with a few kubectl
commands/by hand. First, a namespace will
be created, and then the busybox
image will be deployed into the namespace using the same command format as before, and we’ll also expose
the deployment as a Service to enable accessing the web endpoint:
# create a test namespace
$ kubectl create ns test-busybox
# deploy the busybox functionality
$ kubectl run busybox --namespace=test-busybox \
--port=8280 \
--image=busybox \
-- sh -c "echo 'Hello' > /var/www/index.html && httpd -f -p 8280 -h /var/www/"
# watch for the pod to show "Running""
$ kubectl get pods -n test-busybox
# expose the deployment
$ kubectl expose pod busybox --type=NodePort \
--namespace=test-busybox
# get the node that the particular pod is deployed to
$ kubectl get pods --output=wide -n test-busybox
# based on the "NODE" column, determine the IP address of the node to use
# if you are using the previously configured cluster, this is likely one of the following:
# - master: 10.11.12.13 (this is only possible if you removed the taint to enable master scheduling of pods)
# - node1: 10.11.12.14
# - node2: 10.11.12.15
# get the NodePort for the running pod
$ kubectl get service busybox -n test-busybox
# capture the second port number after the ':' under PORT(S)
# make a GET request to the IP and Port captured in the previous steps
$ curl http://<NODE_IP>:<NODE_PORT>
# the response should print 'Hello'
If all goes well and you get the response, feel free to delete the namespace (and all corresponding resources) as we’ll be creating a new fresh namespace in the next section:
$ kubectl delete ns test-busybox
Semi-Automatic via Helm Charts
NOTE: The descriptions in this section assume a scratch Helm setup and build up contents from there. If you want to get
a Helm directory structure that already has the changes applied in this section and just install the deployment, refer to
and operate all helm
commands from
this sample chart director.
There weren’t that many kubectl
commands to run to get the busybox
web server deployed, but it’s still too manual/error
prone for our liking, not very repeatable, and iterations are slower than a configuration-driven approach. This is where Helm
shines. Helm is a package manager for k8s which affords a more declarative and complete approach to packaging and deploying
services.
First, we’ll create a chart using the helm
binary (can be installed using brew install helm
if you’re on a Mac), which
will create a directory structure for the package:
$ helm create busybox-http
There are a few components that are created, but we’ll focus on only the things we need and leave the rest of the discovery up to the reader via the official Helm docs. We’ll need to update a few files to get our deployment aligned.
First, edit the templates/deployment.yaml
file to insert a couple extra arguments into the template that will allow us to
configure the busybox
container to run the web server and configure liveness
and readiness
probes:
...
spec:
...
template:
...
spec:
...
containers:
- name: {{ .Chart.Name }}
...
command: ["/bin/sh"]
args: ["-c", "{{ .Values.bootCommand }}"]
...
ports:
- name: http
containerPort: 8280
...
livenessProbe:
httpGet:
...
port: {{ .Values.healthCheckPort }}
...
readinessProbe:
httpGet:
...
port: {{ .Values.healthCheckPort }}
...
...
Next, edit the values.yaml
file and update the following parameters (the remainder can be left alone for now) as well as
include values for the existing parameters we defined in the deployment template:
image:
repository: busybox
...
nameOverride: "busybox"
fullNameOverride: "busybox"
bootCommand: "echo 'Hello' > /var/www/index.html && httpd -f -p 8280 -h /var/www/"
healthCheckPort: 8280
...
serviceAccount:
create: false
...
service:
type: NodePort
port: 8280
...
Next, edit the Chart.yaml
to ensure the version of busybox
being deployed is relevant/latest (you can get the latest
version when you run the tutorial from here), or simply use latest
as the version to get the latest build version available without needing to look it up:
...
appVersion: latest
...
Next, perform a dry-run of the Helm install to ensure the resources generated using your values look correct.
$ helm install --dry-run \
--debug \
--namespace=test-busybox \
busybox-http .
Finally, if all looks well, create a namespace for the deployment and go ahead and install your service:
# create new namespace
$ kubectl create ns test-busybox
# install your service
$ helm install --namespace=test-busybox busybox-http .
We’ll now repeat the process for figuring out how to communicate with the service from the local workstation:
# get the node that the particular pod is deployed to
$ kubectl get pods --output=wide -n test-busybox
# based on the "NODE" column, determine the IP address of the node to use
# if you are using the previously configured cluster, this is likely one of the following:
# - master: 10.11.12.13 (this is only possible if you removed the taint to enable master scheduling of pods)
# - node1: 10.11.12.14
# - node2: 10.11.12.14
# get the NodePort for the running pod
$ kubectl get service busybox -n test-busybox
# capture the second port number after the ':' under PORT(S)
# make a GET request to the IP and Port captured in the previous steps
$ curl http://<NODE_IP>:<NODE_PORT>
# the response should print 'Hello'
If all goes well and you get the response, congratulations - you’ve successfully deployed your application via Helm!
You’re now in a great position to iterate quickly. For example - to demonstrate the fast iteration patterns, let’s
make a change to the Helm chart and update our deployment in place. Edit the values.yaml
file and increase the
value for replicaCount
to 2
and save the file.
Next, apply the change to the existing deployment (performing an upgrade
):
$ helm upgrade --namespace=test-busybox busybox-http .
Once the changes are applied, inspect your pods (kubectl get pods -n test-busybox
) to ensure that you have 2 pods now
showing as Running
, indicating that the upgrade was successful!
Finally, feel free to delete the namespace (and all corresponding resources) to clean up your k8s cluster, or continue experimenting with the Helm capabilities!
$ kubectl delete ns test-busybox
Credit
The above tutorial was pieced together with some information from the following sites/resources: