Previously, we have created a simple job pod using
kubectl run job1 --image=nginx
In this post, we will see how we can create a Pod with our own executable.
All the code for this chapter (and other chapters) is available at https://github.com/param108/kubernetes101
Simple Web Service
If you go to the github link above, you will see a directory 011
which has the code and yaml snippets for this chapter. The directory looks like this
011
└── firstpod
├── Dockerfile
├── Makefile
├── webpod.yml
├── Readme.md
└── web
├── go.mod
├── go.sum
├── Makefile
├── Readme.md
├── web
└── web.go
Prerequisites
- docker
- golang (version >= 1.12)
I will leave it to you to install the pre-requisites using google. (suffering builds character :-))
Setup
To setup, checkout the repository to your local machine and build the docker image.
git clone https://github.com/param108/kubernetes101.git
cd kubernetes101/011/firstpod
make docker
This will tell you if the pre-requisites are setup correctly.
Next we need to setup minikube so that we can push the docker image into Minikube’s private docker registry.
Minikube
Minikube does *not* share the same docker registry as your laptop. If you push the image to your laptop docker registry, it will NOT be available for use with Minikube.
To point to Minikube’s docker server to push your images you need to do the following.
eval $(minikube -p minikube docker-env)
This sets up the following environment variables.
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/home/param/.minikube/certs"
export MINIKUBE_ACTIVE_DOCKERD="minikube"
These tell the docker client to use Minikube’s docker service instead of that on the local machine. This needs to be setup in each shell.
Build the image in Minikube’s docker
In the same shell where you ran the eval
command build the docker image again.
make docker # from the kubernetes101/011/firstpod/ directory
Dockerfile description
The web application listens on a port 8080 and responds to /ping
and returns <html>ok</html>
FROM ubuntu:18.04
ADD web/web /web
RUN apt-get update && apt-get install -y wget
RUN wget https://dl.google.com/go/go1.13.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.13.linux-amd64.tar.gz
EXPOSE 8080
CMD /web
This dockerfile uses a base ubuntu 18.04 image and adds our compiled executable web
as the /web
path in the image. It then installs wget
in order to install golang
. Finally the Expose
command is only informative, you can treat it as a comment. It means that the port 8080 should be opened on the host machine. Again its only informative and doesnt really do anything.
The CMD line tells docker which application to run once everything is setup. In this case our web executable.
Pod spec
In order to run this application in Kubernetes we need to create a PodSpec.
A podspec tells Kubernetes what to run and how.
apiVersion: "v1"
kind: Pod
metadata:
name: web
labels:
app: web
version: v1
role: backend
spec:
containers:
- name: web
image: web:latest
imagePullPolicy: Never
command: ["/web"]
args: []
ports:
- containerPort: 8080
protocol: TCP
hostPort: 8080
imagePullPolicy: This decides how often kubernetes will try and pull the image from the public dockerhub. As our image (which we built above) isnt on dockerhub, we Never want to pull it.
ports:
containerPort: The port on the container that needs to be expose. Should use the same value as the EXPOSE command in the dockerfile.
hostPort: which port on the host we would like to listen on. This allows forms a tunnel from the hostPort to the containerPort. Allows us to connect to the Pod from outside the internal network of kubernetes.
containers: As you can see, this is a list of maps. Each entry corresponds to a containter, so we can have multiple containers in this pod and all will be started together. In this post we only have 1, but in the next post will have more.
Creating a pod
Lets create the pod by applying the file webpod.yml. (Its there in the repository).
kubectl apply -f webpod.yml
Lets check if its created.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 10s
Lets see if its working.
$ curl localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
This should work no? Actually No! You assume that our laptop is the node on which kubernetes is running. Minikube is running the kubernetes cluster on the VM it created! Lets look at that pod’s details.
$ kubectl get pods/web -o yaml
...
...
hostIP: 192.168.99.100
phase: Running
podIP: 172.17.0.2
podIPs:
- ip: 172.17.0.2
...
...
The hostIP is 192.168.99.100. Lets try to hit that.
$ curl 192.168.99.100:8080/ping
<html>ok</html>$
It works!
Checking for life
As the pod is listening on a port inside kubernetes, it must have an ip address no? If we get the ip address we should be able to hit it no ?
Get the details of the pod using the below command. The output will be quite large, but we are only interested in the section I have shown below.
$ kubectl get pods/web -o yaml
...
...
hostIP: 192.168.99.100
phase: Running
podIP: 172.17.0.2
podIPs:
- ip: 172.17.0.2
...
...
(to understand more about any kubernetes api you can find it here.)
hostIP: Is the IP of the host of the node on which the pod is running. In our case, as this is minikube its running in the VM it created.
podIP and podIPs: tell us the IPs allocated to the pod. These IPs unfortunately local to the cluster and not accessible from outside, even from your laptop.
In the normal case, you would use a service to expose pods to the outside world.
In GCP you can create VPC Native clusters which make podIPs available as local ip addresses available to other internal ips in the VPC network.
Getting IN from INSIDE
If you have the correct permissions, you can get into the pod using kubectl!
kubectl exec -it pods/web /bin/bash
So whats going on here? Well we are asking kubernetes to run the command /bin/bash
on our container and asking it to run on a tty -it
making it interactive.
$ kubectl exec -it pods/web /bin/bash
root@web:/# ls /web
/web
root@web:/#
What you see here is that kubernetes has provided us a shell inside the web container! This is similar to the docker exec command.
root@web:/# apt install -y curl
...
...
...
root@web:/# curl localhost:8080/ping
<html>ok</html>root@web:/#
root@web:/#
voila! Type exit
to leave the shell. Don’t worry it wont kill the pod!.
The sequel
This is actually not much of a test. We are just trying to hit the containers localhost from the container itself. This obviously works. What we really want to do is to hit the url from outside the pod. Say from another pod.
Lets create a new pod with a different name.
apiVersion: "v1"
kind: Pod
metadata:
name: web2
labels:
app: web
version: v1
role: backend
spec:
containers:
- name: web
image: web:latest
imagePullPolicy: Never
command: ["/web"]
args: []
ports:
- containerPort: 8080
protocol: TCP
hostPort: 8081
Edit the webpod.yml as above and apply it again. The 2 changes are
- metadata:name – name has to be unique
- spec:ports:hostPort – has to be unique otherwise pod will remaining in pending state until the Port is “free“.
Now lets get the ip address of pods/web
and hit it from pods/web2
using the exec command. The ip address of pods/web is 172.17.0.2 from above.
root@web2:/# apt install -y curl
...
...
...
root@web2:/# curl 172.17.0.2:8080/ping
<html>ok</html>root@web2:/#
root@web2:/#
Learnings
minikube has its own docker registry which you can talk to!
pods get an internal ip which is not *usually* accessible from outside the kubernetes cluster
kubectl exec
can be used to get into running pods!any kubernetes spec definition can be found here.
Conclusion
With this, we have created our first pod. It doesn’t do anything but its a first step anyway. In the next post we will create a complete web service with postgres db running in one pod and look at the limitations of the approach.