Go and Redis running on Kubernetes with Minukube
Go is a simple, fast and powerful programing language. Go is growing a lot into the DevOps Engineering scene like Hashicorp Stack or even Kubernetes. One of the main advantages of GO is the fact that you can generate a single binary with all you needed, bundled in a single file. This makes distribution so much easier.
Go is also very compact for some use cases and you can write so less code and still very very efficient and get best of performance. Today we will see how to create a very, very, very simple service in go. This service will access redis to increment how many times it was called. We will use Minikube in order to run kubernetes locally and we will store our data in Redis.
Let's get down to the code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"net/http" | |
"os" | |
"github.com/go-redis/redis" | |
) | |
func handle(w http.ResponseWriter, r *http.Request) { | |
if "/favicon.ico" == r.URL.Path { | |
return | |
} | |
fmt.Print("request: " + r.URL.Path + " ") | |
client := redis.NewClient(&redis.Options{Addr: os.Getenv("REDIS_URL")}) | |
client.Incr("kcount") | |
val, err := client.Get("kcount").Result() | |
if err != nil { | |
panic(err) | |
} | |
result := string("key count: " + string(val)) | |
fmt.Print(result + "\n\r") | |
fmt.Fprintf(w, result) | |
} | |
func main() { | |
fmt.Print("Serving at 0.0.0.0:9090... ") | |
http.HandleFunc("/", handle) | |
http.ListenAndServe("0.0.0.0:9090", nil) | |
} |
As you can see we are exposing a service on the port :9090 and he have a handler function which takes care of the HTTP request to this very server. We want to avoid double counting for this service and some user might call it from the browser so we need to ignore the /favicon.ico request that's why we use the if on the very first lines of the code.
We are connecting on Redis but we are reading Redis URL from an OS env var. I'm doing this so we can run this code in many ways like locally, docker, docker-compose, and kubernetes. IF you have Redis locally now you can do $ REDIS_URL=localhost:6379 go run main.go .For this simple service, we are using Redis commands INCR and GET in order to increment keys(+1) and get the current value of the key.
Go Dockerfile
Right now we need to create a Dockerfile to run our go simple service. In order to do that we will create a Dockerfile file and add the following content:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FROM golang:1.8 | |
RUN mkdir /app | |
WORKDIR /app | |
COPY . /app/ | |
RUN go get github.com/go-redis/redis | |
CMD ["go","run","main.go"] |
Kubernetes deployment
Now we need create 2 yaml files in order to describe the kuerbenetes deployment and Service for the go web app. However before doing that we will create 2 other yaml files in order to deploy Redis so our redis docker container runs on kubernetes as well. We also link connect this 2 containers using kubernetes internal DNS service.
Redis Deployment on Kubernetes
Let's create a file called: redis-deployment.yaml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: extensions/v1beta1 | |
kind: Deployment | |
metadata: | |
name: redis | |
spec: | |
replicas: 1 | |
template: | |
metadata: | |
labels: | |
app: redis | |
role: master | |
tier: backend | |
spec: | |
containers: | |
- name: redis | |
image: redis | |
resources: | |
requests: | |
cpu: 100m | |
memory: 100Mi | |
ports: | |
- containerPort: 6379 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: redis | |
labels: | |
app: redis | |
role: master | |
tier: backend | |
spec: | |
type: LoadBalancer | |
ports: | |
- port: 6379 | |
targetPort: 6379 | |
selector: | |
app: redis | |
role: master | |
tier: backend |
Right now we can create the deployment and service yaml files for the web go simple service application. So first create a file called: webappgo-deployment.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: extensions/v1beta1 | |
kind: Deployment | |
metadata: | |
name: webappgo | |
spec: | |
replicas: 1 | |
template: | |
metadata: | |
labels: | |
app: webappgo | |
tier: frontend | |
spec: | |
containers: | |
- name: webappgo | |
image: webappgo:v1 | |
resources: | |
requests: | |
cpu: 100m | |
memory: 100Mi | |
env: | |
- name: REDIS_URL | |
value: redis.default.svc.cluster.local:6379 | |
- name: GET_HOSTS_FROM | |
value: dns | |
ports: | |
- containerPort: 9090 |
Then, create another file called webappgo-service.yaml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: webappgo | |
labels: | |
app: webappgo | |
tier: frontend | |
spec: | |
type: LoadBalancer | |
ports: | |
- port: 9090 | |
selector: | |
app: webappgo | |
tier: frontend |
When we write the go code we were expecting to receive the redis URL via OS ENV var. We can set this vars via Kubernetes and kubernetes will forward this vars to Docker we did this on the property called env. You might notice some specific name for the redis url which is: redis.default.svc.cluster.local:6379 .This is the kubernetes internal DNS. Where redis is the name of the service, the default is the default namespace and cluster.local is the domain.
Another important thing to notice is that the spec type on the service in LoadBalancer so we are exposing the port 9090.
kubectl and Minikube
okay, now we can go on and deploy theses files in kubernetes on our minikube local cluster.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Deploy | |
# | |
# Build docker image for go service and start minikube | |
# | |
docker build -t webappgo:v1 . | |
minikube start | |
eval $(minikube docker-env) | |
docker build -t webappgo:v1 . | |
# | |
# Clean up previous deploys | |
# | |
kubectl delete deployment redis | |
kubectl delete service redis | |
kubectl delete deployment webappgo | |
kubectl delete service webappgo | |
# | |
# Do the deploy | |
# | |
kubectl create -f kubernetes/redis/ | |
kubectl create -f kubernetes/go/ | |
# | |
# Connect on redis | |
# | |
kubectl describe service redis | |
redis-cli -h 192.168.99.100 -p 30458 |
You might notify we are using kubectl create -f and passing a directory. This is created because you don't need to specify a file by files and long as you have redis and go deployment and services files properly in each respective directory.
Now we can run the GO simple service in our browser. You might need to run $ kubectl describe services webappgo to get the proper port mapping.
IF you go to the minikube dashboard(http://192.168.99.100:30000/#!/pod?namespace=default) you can the see the GO service LOGs, like this:
You can get all the files in my github.
Cheers,
Diego Pacheco