Table of Contents
*The author selected Girls Who Code to receive a donation as part of the Write for DOnations program.*
Introduction
The Okteto CLI is an open-source project that provides a local development experience for applications running on Kubernetes. With it you can write your code on your local IDE and as soon as you save a file, the changes can be pushed to your Kubernetes cluster and your app will immediately update. This whole process happens without the need to build Docker images or apply Kubernetes manifests, which can take considerable time.
In this tutorial, you’ll use Okteto to improve your productivity when developing a Kubernetes-native application. First, you'll create a Kubernetes cluster and use it to run a standard "Hello World" application. Then you’ll use Okteto to develop and automatically update your application without having to install anything locally.
Prerequisites
Before you begin this tutorial, you'll need the following:
- A Kubernetes 1.12+ cluster. In this tutorial, the setup will use a Kubernetes cluster with three nodes, but you are free to create a cluster using another method.
kubectlanddoctlinstalled and configured to communicate with your cluster.
- Docker running on your local machine.
Step 1 — Creating the Hello World Application
The "Hello World" program is a time-honored tradition in web development. In this case, it is a simple web service that responds "Hello World" to every request. Now that you've created your Kubernetes cluster, let's create a "Hello World" app in Golang and the manifests that you'll use to deploy it on Kubernetes.
First change to your home directory:
cd ~
Now make a new directory called hello_world and move inside it:
mkdir hello_world
cd hello_world
Create and open a new file under the name main.go with your favorite IDE or text editor:
nano main.go
main.go will be a Golang web server that returns the message Hello world!. So, let's use the following code:
[label main.go]
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Println("Starting hello-world server...")
http.HandleFunc("/", helloServer)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
func helloServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello world!")
}
The code in main.go does the following:
- The first statement in a Go source file must be the
packagename. Executable commands must always usepackage <^>main<^>.
- The
importsection indicates which packages the code depends on. In this case it usesfmtfor string manipulation, andnet/httpfor the HTTP server.
- The
mainfunction is the entry point to your binary. Thehttp.HandleFuncmethod is used to configure the server to call thehelloServerfunction when a request to the/path is received.http.ListenAndServestarts an HTTP server that listens on all network interfaces on port8080.
- The
helloServerfunction contains the logic of your request handler. In this case, it will writeHello world!as the response to the request.
You need to create a Docker image and push it to your Docker registry so that Kubernetes can pull it and then run the application.
Open a new file under the name Dockerfile with your favorite IDE or text editor:
nano Dockerfile
The Dockerfile will contain the commands required to build your application's Docker container. Let's use the following code:
[label Dockerfile]
FROM golang:alpine as builder
RUN apk --update --no-cache add bash
WORKDIR /app
ADD . .
RUN go build -o app
FROM alpine as prod
WORKDIR /app
COPY --from=builder /app/app /app/app
EXPOSE 8080
CMD ["./app"]
The Dockerfile contains two stages, builder and prod:
- The
builderstage contains the Go build tools. It's responsible for copying the files and building the Go binary.
- The
prodstage is the final image. It will contain only a stripped down OS and the application binary.
This is a good practice to follow. It makes your production containers smaller and safer since they only contain your application and exactly what is needed to run it.
Build the container image (replace <^>your_DockerHub_username<^> with your Docker Hub username):
docker build -t <^>your_DockerHub_username<^>/hello-world:latest
Now push it to Docker Hub:
docker push <^>your_DockerHub_username<^>/hello-world:latest
Next, create a new folder for the Kubernetes manifests:
mkdir k8s
When you use a Kubernetes manifest, you tell Kubernetes how you want your application to run. This time, you'll create a deployment object. So, create a new file deployment.yaml with your favorite IDE or text editor:
nano k8s/deployment.yaml
The following content describes a Kubernetes deployment object that runs the okteto/hello-world:latest Docker image. Add this content to your new file, but in your case replace okteto listed after the image label with <^>your_DockerHub_username<^>:
[label ~/hello_world/k8s/deployment.yaml]
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
selector:
matchLabels:
app: hello-world
replicas: 1
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: <^>your_DockerHub_username<^>/hello-world:latest
ports:
- containerPort: 8080
The deployment manifest has three main sections:
metadatadefines the name for your deployment.
replicasdefines how many copies of it you want running.
templatetells Kubernetes what to deploy, and what labels to add. In this case, a single container, with theokteto/hello-world:latestimage, listening on port8080, and with theapp: hello-worldlabel. Note that this label is the same used in theselectorsection.
You'll now need a way to access your application. You can expose an application on Kubernetes by creating a service object. Let's continue using manifests to do that. Create a new file called service.yaml with your favorite IDE or text editor:
nano k8s/service.yaml
The following content describes a service that exposes the hello-world deployment object, which under the hood will use a load balancers:
[label k8s/service.yaml]
apiVersion: v1
kind: Service
metadata:
name: hello-world
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 8080
name: http
selector:
app: hello-world
The service manifest has four main sections:
metadatatells Kubernetes how to name your service.
typetells Kubernetes how you want to expose your service. In this case, it will expose it externally through a the cloud provider Load Balancer.
- The
portslabel tells Kubernetes which ports you want to expose, and how to map them to your deployment. In this case, you will expose port80externally and direct it to port8080in your deployment.
selectortells Kubernetes how to direct traffic. In this case, any pod with theapp: hello-worldlabel will receive traffic.
You now have everything ready to deploy your "Hello World" application on Kubernetes. We will do this next.
Step 2 — Deploying Your Hello World Application
In this step you'll deploy your "Hello World" application on Kubernetes, and then you'll validate that it is working correctly.
Start by deploying your application on Kubernetes:
kubectl apply -f k8s
You'll see the following output:
[secondary_label Output]
deployment.apps "hello-world" created
service "hello-world" created
After about one minute or so, you will be able to retrieve your application's IP. Use this kubectl command to check your service:
kubectl get service hello-world
You'll see an output like this listing your Kubernetes service objects. Note your application's IP in the the EXTERNAL-IP column:
[secondary_label Output]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world ClusterIP <^>your_cluster_ip<^> <^>your_external_ip<^> 8080/TCP 37s
Open your browser and go to <^>your_external_ip<^> listed for your "Hello World" application. Confirm that your application is up and running before continuing with the next step.
Until this moment, you've followed a fairly traditional pathway for developing applications with Kubernetes. Moving forward, whenever you want to change the code in your application, you'll have to build and push a new Docker image, and then pull that image from Kubernetes. This process can take quite some time. Okteto was designed to streamline this development inner-loop. Let's look at the Okteto CLI and see just how it can help.
Step 3 — Installing the Okteto CLI
You will now improve your Kubernetes development productivity by installing the Okteto CLI. The Okteto command line interface is an open-source project that lets you synchronize application code changes to an application running on Kubernetes. You can continue using your favorite IDE, debuggers, or compilers without having to commit, build, push, or redeploy containers to test your application–as you did in the previous steps.
To install the Okteto CLI on a macOS or Linux machine, run the following command:
curl https://get.okteto.com -sSfL | sh
Let's take a closer look at this command:
- The
curlcommand is used to transfer data to and from a server.
- The
-sflag suppresses any output.
- The
-Sflag shows errors.
- The
-fflag causes the request to fail on HTTP errors.
- The
-Lflag makes the request follow redirects.
- The
|operator pipes this output to theshcommand, which will download and install the latestoktetobinary in your local machine.
If you are running Windows, you can alternately download the file through your web browser and manually add it to your $PATH.
Once the Okteto CLI is installed, you are ready to put your "Hello World" application in development mode.
Step 4 — Putting Your Hello World Application in Development Mode
The Okteto CLI is designed to swap the application running on a Kubernetes cluster with the code you have in your machine. To do so, Okteto uses the information provided from an Okteto manifest file. This file declares the Kubernetes deployment object that will swap with your local code.
Create a new file called okteto.yaml with your favorite IDE or text editor:
nano okteto.yaml
Let's write a basic manifest where you define the deployment object name, the Docker base image to use, and a shell. We will return to this information later. Use the following sample content file:
[label okteto.yaml]
name: hello-world
image: okteto/golang:1
workdir: /app
command: ["bash"]
Prepare to put your application in development mode by running the following command:
okteto up
[secondary_label Output]
✓ Development environment activated
✓ Files synchronized
Namespace: default
Name: hello-world
Welcome to your development environment. Happy coding!
default:hello-world /app>
The okteto up command swaps the "Hello World" application into a development environment, which means:
- The Hello World application container is updated with the docker image
okteto/golang:1. This image contains the required dev tools to build, test, debug, and run the "Hello World" application.
- A file synchronization service is created to keep your changes up-to-date between your local filesystem and your application pods.
- A remote shell starts in your development environment. Now you can build, test, and run your application as if you were in your local machine.
- Whatever process you run in the remote shell will get the same incoming traffic, the same environment variables, volumes, or secrets as the original "Hello World" application pods. This, in turn, gives you a highly realistic, production-like development environment.
In the same console, now run the application as you would typically do (without building and pushing a Docker image), like this:
go run main.go
[secondary_label Output]
Starting hello-world server...
The first time you run the application, Go will download your dependencies and compile your application. Wait for this process to finish and test your application by opening your browser and refreshing the page of your application, just as you did previously.
Now you are ready to begin developing directly on Kubernetes.
Step 5 — Developing Directly on Kubernetes
Let's start making changes to the "Hello World" application and then see how these changes get reflected in Kubernetes.
Open the main.go file with your favorite IDE or text editor. For example, open a separate console and run the following command:
nano main.go
Then, change your response message to <^>Hello world from the cloud provider!<^>:
[label main.go]
package main
import (
"fmt"
"net/http"
)
func main() {
fmt.Println("Starting hello-world server...")
http.HandleFunc("/", helloServer)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
func helloServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "<^>Hello world from the cloud provider!<^>")
}
It is here that your workflow changes. Instead of building images and redeploying containers to update the "Hello World" application, Okteto will synchronize your changes to your development environment on Kubernetes.
From the console where you executed the okteto up command, cancel the execution of go run main.go by pressing CTRL + C. Now rerun the application:
default:hello-world /app> go run main.go
[secondary_label Output]
Starting hello-world server...
Go back to the browser and reload the page for your "Hello World" application.
Your code changes were applied instantly to Kubernetes, and all without requiring any commits, builds, or pushes.
Conclusion
Okteto transforms your Kubernetes cluster into a fully-featured development platform with the click of a button. In this tutorial you installed and configured the Okteto CLI to iterate your code changes directly on Kubernetes as fast as you can type code. Now you can head over to the Okteto samples repository to see how to use Okteto with different programming languages and debuggers.
Also, if you share a Kubernetes cluster with your team, consider giving each member access to a secure Kubernetes namespace, configured to be isolated from other developers working on the same cluster. This great functionality is also provided by the Okteto App in the Kubernetes Marketplace.