Kubernetes is a popular container orchestration system that lets you automate application deployment, scaling and management tasks via simple command line calls. This guide describes how to build and deploy a simple dockerized web app to a Kubernetes cluster on CentOS 8.
We’ve launched the UpCloud Managed Kubernetes, a fully managed container orchestration service with all the benefits of a self-maintained system but without any of the headaches! See how quick and easy it is to get started by following our dedicated tutorial.
This guide is divided into three main parts:
1. Testing out containers
2. Building a simple dockerized app using Dockerfile
3. Deploying the app to Kubernetes
If you do not yet have a running Kubernetes cluster, have a look at our earlier tutorial on how to install Kubernetes on CentOS 8.
Testing out containers
Let’s start by running a simple docker app to test the container platform.
1. Run hello-world app with the following command:
docker run hello-world
You should see the app print “Hello from Docker!” to your terminal towards the end of the output.
Running a known container pulls the required image from a public registry and saves it on your docker server.
2. Check the list of docker images
docker images
After running the hello-world app once, it should show up on the images list.
REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest fce289e99eb9 15 months ago 1.84kB
Now let’s try a more advanced docker app
1. Run the Ubuntu docker app in interactive mode.
docker run -it ubuntu bash
On success, you’ll be brought directly to Ubuntu prompt.
2. Test the internet connection from inside docker app by running a command that requires network access.
On Ubuntu’s terminal prompt, run the following to check it has an internet connection.
apt update
On success, you should see something like the below.
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB] Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB] ... Get:17 http://archive.ubuntu.com/ubuntu bionic-backports/main amd64 Packages [2496 B] Get:18 http://archive.ubuntu.com/ubuntu bionic-backports/universe amd64 Packages [4247 B] Fetched 17.7 MB in 5s (3439 kB/s)
Then stop the Ubuntu container with the following command.
exit
In case you cannot access the internet, you might see something similar to the error underneath.
Err:1 http://security.ubuntu.com/ubuntu bionic-security InRelease Temporary failure resolving 'security.ubuntu.com' Err:2 http://archive.ubuntu.com/ubuntu bionic InRelease Temporary failure resolving 'archive.ubuntu.com' ...
To fix this, make sure IP masquerade is enabled at the firewall and then try again.
firewall-cmd --add-masquerade --permanent firewall-cmd --reload
OK, we’ve completed the first part, now let us move on to the next step.
Building a simple dockerized app using Dockerfile
Dockerizing apps is a great way of creating consistent and reliable environments for many tasks regardless of the underlying operating system.
For this example, we are going to make a simple Golang web server that takes input via the server URL and prints out a hello message.
1. Start by creating a new directory named “goserver”
mkdir goserver
2. Create a file named “main.go” in the goserver directory
vi goserver/main.go
Enter the following code, then save the file.
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", HelloServer) http.ListenAndServe(":8080", nil) } func HelloServer(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %s!n", r.URL.Path[1:]) }
3. Create another new file named “Dockerfile” with the following command.
vi Dockerfile
Then add the following content to that file, save and exit the text editor.
FROM golang:alpine as builder WORKDIR /build COPY /goserver . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main . FROM scratch COPY --from=builder /build/main /app/ WORKDIR /app ENV PORT=8080 CMD ["./main"]
4. Next, build a docker image based on our goserver.
docker build -f Dockerfile -t goserver .
5. Additionally, you may wish to clean unused images from the build process.
docker system prune -f
6. Then check that your web app was added to the images list.
docker images
You should see a line such as in the example below.
REPOSITORY TAG IMAGE ID CREATED SIZE goserver latest 6c0fb70f56fe 55 seconds ago 7.41MB
7. Afterwards, test run the web app.
docker run -d -p 8090:8080 --name goserver goserver
8. You can verify that the deployment was successful by checking the running images.
docker ps -f name=goserver
You should see the “goserver” container running with the status “Up”.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a16ebdf117b9 goserver "./main" 54 seconds ago Up 53 seconds 0.0.0.0:8090->8080/tcp goserver
Your web app is now reachable both locally and over the internet.
9. Call it on the command line, for example by using curl with the command below.
curl http://localhost:8090/Joe
You should have curl available but if not, install curl using sudo dnf install curl
You can also open the page in your web browser. Replace the public-ip-address with the IP of your Master node.
http://public-ip-address:8090/Joe
You should see the following response.
Hello, Joe!
12. Once done, stop the web server and remove the image.
docker stop goserver && docker rm goserver
Congratulations, you should now have an idea of what’s included in making simple dockerized apps.
Deploying a dockerized app to Kubernetes
We’ve now tested out the container platform and built our own dockerized web app, so the last thing to do is to deploy it on our Kubernetes cluster.
This part is referring to the Kubernetes configuration installed in our previous tutorial.
1. Create the following configuration file on the master node. Replace the <master_private_IP> with the private IP address of your master node.
cat > /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
],
"insecure-registries": [ "<master_private_IP>:5000" ]
}
EOF
2. Add the exception for the registry also on all worker nodes. Again, replace the <master_private_IP> with the private IP address of your master node.
cat > /etc/docker/daemon.json <<EOF
{
"insecure-registries": [ "<master_private_IP>:5000" ]
}
EOF
Then restart Docker on all nodes.
systemctl restart docker
Continue with the rest of the steps on the master node only.
We are going to be using a private registry container for storing and distributing our dockerized app in our cluster.
3. Get the latest version of the docker registry.
docker pull registry:2
4. Run the docker registry on port 5000 using the master node’s private IP address. Replace the <master_private_IP> with the correct address.
docker run -dit --restart=always -p <master_private_IP>:5000:5000 --name registry -e REGISTRY_STORAGE_DELETE_ENABLED=true registry:2
5. Next, create a deployment artefact for our “goserver” app.
Please be aware that indentation matters. This artefact is using two spaces for indentation.
vi deployment.yaml
Enter the following content and again replace the <master_private_IP> with the correct address.
apiVersion: apps/v1
kind: Deployment
metadata:
name: goserver
spec:
selector:
matchLabels:
app: goserver
replicas: 2
revisionHistoryLimit: 0
progressDeadlineSeconds: 30
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 2
template:
metadata:
labels:
app: goserver
spec:
containers:
- name: goserver
image: <master_private_IP>:5000/goserver:v1
ports:
- hostPort: 8090
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
labels:
app: goserver
name: goserver
spec:
selector:
app: goserver
ports:
- protocol: TCP
port: 8090
targetPort: 8080
type: ClusterIP
6. Tag the docker app to add the private registry address and port. As before, replace the <master_private_IP> with the private IP address of your master node.
docker tag goserver:latest <master_private_IP>:5000/goserver:v1
7. Then check to make sure the tagged app was created.
docker images
It should show up on the list.
8. Push the tagged app to the private registry so it can be pulled by Kubernetes deployment. Again, replace the <master_private_IP> with the private IP address of your master node.
docker push <master_private_IP>:5000/goserver:v1
9. Check that the tagged app was successfully pushed to the registry by querying the registry. Replace the <master_private_IP> with the private IP address of your master node.
curl -s -X GET http://<master_private_IP>:5000/v2/goserver/tags/list
{"name":"goserver","tags":["v1"]}
Now that we have set up our private repository and pushed the docker image to it, it can be used to deploy the app onto our Kubernetes cluster.
10. Deploy our goserver using the command below.
kubectl apply -f deployment.yaml
11. Check that the deployment rollout succeeded.
kubectl rollout status deployment/goserver
12. You can also check that the pods are up and running.
kubectl get pods
13. You should now be able to access the web server on both master and worker nodes.
curl http://localhost:8090/Joe
Or open browser to http://<public-IP-address>:8090/Joe
Replace the <public-IP-address> with either the public IP address of your master or worker node.
You should see the familiar greeting.
Hello, Joe!
14. When you wish to delete the test app, revert the deployment with the following command.
kubectl delete -f deployment.yaml
Done!
All done!
When you need to update “goserver” codes, you need to dockerized again. Then you also need to push it to the registry with different tags (e.g. v2).
That’s all folks, happy dockerizing!
Akash Kundu
I am new to kubernetes.
I wanted to know that if we are just doing an shell/bash echo “Hello World ” in a dockerfile , can we get the result on the browser at a certain port in kubernetes.
Do we need to have an existing application like nodejs/service like httpd always and then build the dockerfile. Then the app can be taken to k8s and be hosted on a web server port by k8s.
Janne Ruostemaa
Hi Akash, thanks for the question. The example of deploying a dockerized app can help adapt already existing containers for Kubernetes but it’s not necessary for creating new. If you are looking for a simple Hello world example, check out this guide from Kubernetes.