Ansible + K8s for the laziest person -Part 1
The easiest way to automate app deployment with ansible and K8s.
This is contemporary business requirements for applications include one or more of the following features as self-healing infrastructure, auto-scaling, high-availability with multi-server failover, flexible storage backends,multi-cloud compatibility, etc.
Pre-requisites
- Create a simple web application (Go)
- Create a Local Minikube or Remote Kubernetes Cluster
- Ansible command
Let's build a single Hello World with Go!!
We’re going to build a tiny Go app and run it locally. We’ll then use this app to demonstrate the basics of containers and Kubernetes.
# export VERSION=1.14 && curl -O https://dl.google.com/go/go$VERSION.linux-amd64.tar.gz && tar -C /usr/local -xzf go$VERSION.linux-amd64.tar.gz && export PATH=$PATH:/usr/local/go/bin
If the installation worked, you should be able to enter go version and get the version you just installed:
# go version
go version go1.13.4 darwin/amd64
Creating a ‘Hello World’ app in Go
We’re going to write the most basic HTTP request response app and code used in this example is also available in this book’s code repository, in the hello-go/23 directory.
Now, inside the hello directory, create the file hello.go with the following Go code below:
# cat >> hello.go <<'EOF' package mainimport(
"fmt"
"log"
"net/http"
)//Hello Server responds to requests with the given URL path.
func HelloServer(w http.ResponseWriter,r *http.Request){
fmt.Fprintf(w, "Hello, you requested: %s", r.URL.Path)
log.Printf("Received request for path: %s", r.URL.Path)
}func main(){
var addr string = ":8180"
handler := http.HandlerFunc(HelloServer)
if err := http.ListenAndServe(addr, handler); err != nil {
log.Fatalf("Could not listen on port %s %v", addr, err)
}
}
EOF
Run the following command:
# go build hello.go
After a couple seconds, you should see a new hello binary. Run it by typing:
# ./hello
# ./hello
2020/05/25 13:41:41 Received request for path: /
2020/05/25 13:41:41 Received request for path: /favicon.ico
2020/05/25 13:41:45 Received request for path: /test
It’s always nice to have applications log to standard output (stdout) and standard error (stderr), because in the cloud-native world, these logs are easy to route and store centrally.
Running Hello Go in Docker
Building Go apps in Docker containers is easy. Go maintains a number of images on Docker Hub containing all the necessary tooling to build your app, and all you need to do is copy in the source and run go build.
It’s time to create a Dockerfile to instruct Docker how to build our Hello Go app container image
# cat >> Dockerfile <<'EOF'FROM golang:1-alpine as build
WORKDIR /app
COPY hello.go /app
RUN go build hello.goFROM alpine:latest
WORKDIR /app
COPY --from=build /app /appEXPOSE 8180
ENTRYPOINT ["./hello"]EOF
Using a multi-stage build, we can build Hello Go in one container (named build using that as build statement), then copy Hello Go into a very small container for deployment.
# docker build -t hello-go .Sending build context to Docker daemon 7.345MB
Step 1/9 : FROM golang:1-alpine as build
---> 459ae5e869df
Step 2/9 : WORKDIR /app
---> Using cache
---> 522a081c45d0
Step 3/9 : COPY hello.go /app
---> 4ba6e4b6abf2
Step 4/9 : RUN go build hello.go
---> Running in a8a883ae99b5
Removing intermediate container a8a883ae99b5
---> aa683bb3e3ac
Step 5/9 : FROM alpine:latest
latest: Pulling from library/alpine
cbdbe7a5bc2a: Already exists
Digest: sha256:9a839e63dad58492d93f90c59c978c1ed79109ea4fb9a54
Status: Downloaded newer image for alpine:latest
---> f70734b6a266
Step 6/9 : WORKDIR /app
---> Running in 1aed3c3faf4c
Removing intermediate container 1aed3c3faf4c
---> fa740f902660
Step 7/9 : COPY --from=build /app/hello /app/hello
---> c0d6a74ba13d
Step 8/9 : EXPOSE 8180
---> Running in f86014215c95
Removing intermediate container f86014215c95
---> a6f0392e62c1
Step 9/9 : ENTRYPOINT ["./hello"]
---> Running in 662b9dee2e9b
Removing intermediate container 662b9dee2e9b
---> eb6fd22d5c4e
Successfully built eb6fd22d5c4e
Successfully tagged hello-go:latest
After a couple minutes (or less if you already had the base images downloaded!), you should be able to see the hello-go container image when you run docker images:
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-go latest 6258e740c56f 3 minutes ago 13.1MB
golang 1-alpine 459ae5e869df 9 days ago 370MB
alpine latest f70734b6a266 4 weeks ago 5.61MB
Running the container
To run the container and expose the internal port 8180 to your host, run the command:
# docker run --name hello-go --rm -p 8180:8180 hello-go
After a second or two, the webserver should be operational. Also, to stop and terminate the container, press Ctrl-C in the terminal where you ran docker run.
Summary
In this part, We learned the Go programming language at a very basic level and how to build a simple application container and run it using Docker.
Second peace using Kubernetes (Minikuke)
Let’s go to the second part of our deployment using Minikube. There are a variety of installation guides depending on what OS and distribution you’re running.
Install Minikube following the link here.
Building the Hello Go container in Minikube
Minikube runs a separate VM on your workstation, and that VM doesn’t have access to your local Docker registry. If you try to deploy the hello-go:latest image inside Minikube’s Kubernetes cluster, Kubernetes will complain it can’t pull the image.
# eval $(minikube docker-env)
If you run docker images now, you’ll see a number of images that are not present in your local workstation’s Docker installation. And you can verify the hello-go:latest image is not present.
Now that you’re operating in Minikube’s Docker environment, build and tag the hello-go image again:
# docker build -t hello-go .
For now, though, let’s deploy our Hello Go app into the Minikube Kubernetes environment using the kubectl command line utility:
# kubectl create deployment hello-go --image=hello-go
This command creates a new Deployment resource named hello-go. The Deployment will run one Pod by default, and that Pod will start a container with the hello-go image we built inside Minikube’s Docker environment a few moments ago.
# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/hello-go-799d5b7566-hsx89 0/1 ErrImagePull 0 6sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15mNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-go 1 1 1 0 6sNAME DESIRED CURRENT READY AGE
replicaset.apps/hello-go-799d5b7566 1 1 0 6s
It looks like Kubernetes is having trouble pulling the hello-go image (thus the ErrImagePull message). You can get more details about the pod’s woes using kubectl.
So instead, we can modify the hello-go deployment to only attempt pulling the container image if it’s not already present. In your terminal, run:
# kubectl edit deployment hello-go
At this point, Hello Go is running in Kubernetes. But we won’t be able to access the app from anywhere, because there is nothing exposing port 8180 to the outside world. In Docker, we used -p 8180:8180 to expose a port from the Docker container to a port on the host. In Kubernetes, we can ‘expose’ a deployment to the outside world using a Kubernetes Service.
# kubectl expose deployment hello-go --type=LoadBalancer --port=8180
Ideally, this will pop open a browser window in your default browser, and you’ll see the response from Hello Go:
# minikube service hello-go
Opening kubernetes service default/hello-go in default browser...
LSOpenURLsWithRole() for the URL http://192.168.99.100:30895.
Let’s scale Hello Go to four instances, instead of one. Use kubectl scale to increase the deployments replicas:
# kubectl scale deployments/hello-go --replicas=4
deployment.extensions/hello-go scaled
# kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-go-5c55c74956-9wp5h 1/1 Running 0 15m
hello-go-5c55c74956-c9dq7 1/1 Running 0 3m
hello-go-5c55c74956-lc8f2 1/1 Running 0 3m
hello-go-5c55c74956-nk8r4 1/1 Running 0 3m
Summary
In this part, We learned how to deploy the same application into a Kubernetes cluster using Minikube. However, If you are a lazy person Go to part 2.