How To Develop Applications on Kubernetes with Okteto
How To Develop Applications on Kubernetes with Okteto

Tutorial

How To Develop Applications on Kubernetes with Okteto

DevelopmentOpen SourceKubernetesDigitalOcean Managed Kubernetes

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:

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:

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 package name. Executable commands must always use package main.
  • The import section indicates which packages the code depends on. In this case it uses fmt for string manipulation, and net/http for the HTTP server.
  • The main function is the entry point to your binary. The http.HandleFunc method is used to configure the server to call the helloServer function when a request to the / path is received. http.ListenAndServe starts an HTTP server that listens on all network interfaces on port 8080.
  • The helloServer function contains the logic of your request handler. In this case, it will write Hello 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:

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 builder stage contains the Go build tools. It’s responsible for copying the files and building the Go binary.
  • The prod stage 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:

~/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:

  • metadata defines the name for your deployment.
  • replicas defines how many copies of it you want running.
  • template tells Kubernetes what to deploy, and what labels to add. In this case, a single container, with the okteto/hello-world:latest image, listening on port 8080, and with the app: hello-world label. Note that this label is the same used in the selector section.

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 DigitalOcean Load Balancer:

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:

  • metadata tells Kubernetes how to name your service.
  • type tells Kubernetes how you want to expose your service. In this case, it will expose it externally through a Digital Ocean Load Balancer.
  • The ports label tells Kubernetes which ports you want to expose, and how to map them to your deployment. In this case, you will expose port 80 externally and direct it to port 8080 in your deployment.
  • selector tells Kubernetes how to direct traffic. In this case, any pod with the app: hello-world label 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:

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:

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.

Hello World Okteto

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 curl command is used to transfer data to and from a server.
  • The -s flag suppresses any output.
  • The -S flag shows errors.
  • The -f flag causes the request to fail on HTTP errors.
  • The -L flag makes the request follow redirects.
  • The | operator pipes this output to the sh command, which will download and install the latest okteto binary 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:

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
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
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 DigitalOcean!:

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 DigitalOcean!")
}

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
Output
Starting hello-world server...

Go back to the browser and reload the page for your “Hello World” application.

Hello world DigitalOcean"

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 DigitalOcean Kubernetes Marketplace.

Creative Commons License