Building a container from scratch and deploying it to Google Kubernetes Engine
There are many articles that describe how to deploy a container to Kubernetes. There are a bunch that describe how to deploy to Google Kubernetes Engine specifically. But, there are fewer that describe how to create a container from scratch AND deploy it to GKE, for the complete newbie looking to understand basic concepts of building containerized apps and running them in the cloud.
Building a simple golang Hello World container application
Let’s build a simple Go app that renders Hello, World! within a container.
Note: I use Docker Desktop to manage/run my containers on my local development laptop. Some steps below assume you do as well.
Create a file, main.go, in an application directory of your choosing. Or even better, fork my repo to download the code for this exercise. You can see main.go below:
This main.go file spins up a service listening on port 5000. When called, the service calls the helloworld method to print “Hello, World!” onto the screen.
The next step is to create a Dockerfile file (no extension) in the same directory. This file provides instructions to Docker for how to build your container image.
A container image is an immutable encapsulation of your application. The below docker file uses the golang base image as a starting point (you’ll always start “FROM” a base image, and extend). It then creates a working directory and builds our golang app and copies the binaries into a folder that will be placed within the container called hello_go_http.
With our Dockerfile in place, we can now run “docker build” to build our image:
docker build --platform linux/amd64 -t [name]/hello-world-container:1.0.0 .
Since I’m running on Mac OS, M1 ARM architecture I need to specifically call out the “lunux/amd64” architecture as the platform, since that is the platform the golang image is built on. Depending on your laptop, you may need to do likewise. If you are on an Intel platform, omit “-platform linux/amd64” since you’re already on amd architecture. Also, note you’ll want to change the [name] to your company name or personal name. Mine is set to my account name on Docker Hub, where my image is hosted.
After you build your image, you should see your image in Docker Desktop > Images. Click Run to launch your image:
After you click Run, you’ll see your container in Docker Desktop > Containers. Click on the CLI to launch into the container, and then curl localhost with the port number we specified in main.go:
Hello, World! Our Go app is running in the container!
Now that we have confirmed our container works, we need to deploy the container to a container repository where it can be accessible to Kubernetes to pull into the cluster we’ll host our app on when we go to deploy our app. I’m using Docker Hub as my registry (it’s free and publicly accessible) but most organizations will have a private/secured registry you’ll be asked to use for proprietary apps.
Docker Desktop makes publishing your image to Docker Hub a synch. Simply find your image, and click Push to Hub to push to your own personal repository. You can also do this via the command line.
You can see my example on Docker Hub of the above container that got pushed.
The next step is to deploy our container to a Google Cloud kubernetes cluster so our users can access it.
Connect to GCP
First off, we need to get connected to the gcloud CLI so that we can deploy to GCP from our laptop.
- go to cloud.google.com and create a free trial if you don’t already have an account
- install gcloud SDK so that you can interact with Google Cloud from your terminal
- run the following to login:
gcloud auth login
4. Run the following command to set your current project to the default project setup for you when you created your free trial.
gcloud config set project [project id]
You can see your project ID by clicking “My First Project” in the header:
GCP croups apps/services together within a “project”. By setting your project, your terminal now has context to which to make changes in GCP.
Create a new GKE Kubernetes cluster
If you don’t have one, create a new cluster in GKE that we can deploy to. If you just spun up a free trial of GCP, you may need to “Enable” the Kubernetes/GKE api first (click Kubernetes on the left nav):
Run the following command in the same terminal window to create a new three-node cluster in GKE named “hello-cluster”:
gcloud container clusters create hello-cluster --numnodes 3
This may take a minute or two. You can see your new cluster in GCP:
To continue the exercise, you must connect to this cluster from your terminal by running the following:
gcloud container clusters get-credentials hello-clusterkubectl get nodes
“kubectl get nodes” is a kubernetes API command we can run to test to make sure connectivity to our cluster is operational:
Deploy your container to GKE
Let’s deploy our app to GKE!
We will use the Kubernetes API to do this, specifically the “apply” feature. The first thing we need to do is create our “deployment” manifest, that tells kubernetes what our app is named, as well as what container image we want to use which encapsulates our app.
In the root of your golang project, create a new file with a .yaml extension, similar to “hello-world-deployment.yaml”:
This file specifies an app named “hello-world-deployment”, as well as our container image philwicklund/hello-world-container:1.0.0. It also specifies the port the app runs on (5000), and that we want two “replicas” of our container (pod) running across our three nodes/servers in our cluster, for redundancy and high-availability purposes.
Deploy your container/app by running the following command from your source code folder location:
kubectl apply -f hello-world-deployment.yaml
“Kubectl get pods” will help you confirm your pods/containers were deployed to the cluster successfully:
You can also confirm your app/pods/containers were deployed by clicking “Workloads” within GKE:
Let’s make sure our image/app is working correctly by getting to the command line within the running container itself. Choose one of the pods (from “get pods”) and run the following command to ssh into that pod/container:
kubectl exec --stdin --tty [pod_ID] -- /bin/bash
Once you get into the command line, you can access the linux OS as you’d expect (see ls examples below). Also, from here, like before, we can curl the app to confirm our web server is running:
Notice the “hello_go_http” folder we packaged up in our image earlier, and the same “Hello, World!” response.
Deploy a service load balancer so that we can access our app externally
Optional step… but helpful. By default, only other pods on kubernetes cluster can communicate with other pods on the same cluster. External access isn’t allowed. Let’s deploy a service that exposes our container so that we can access it externally.
In your project, create a new yaml file like before, named “hello-world-service.yaml” with the following code:
This will tell kubernetes that we want to expose our pods/containers to the outside world via a load balancer. GKE knows to provision a GCP load balancer when we apply this comment. The GCP load balancer will provision an external IP and distribute the traffic across the two “replicas” we provisioned earlier. In this configuration, note how we’ll map requests to port 8989 into port 5000 when it reaches our container.
Run the following to provision your load balancer:
kubectl apply -f hello-world-service.yaml
After you run the command you can run “kubectl get service” to check the status. It may take a few minutes before the external IP is provisioned.
That’s it! You should be able see our app, similar to below, from the internet:
Cheers,
Phil
Disclaimer: I am an employee of Google. Opinions stated herein are my own and not that of my company.