Day 17 - Docker Project for DevOps Engineers
90DaysOfDevOps - Learn how to create a Docker image and DockerHub
Link to project file : github
Today I am going to do a small project of creating a Dockerfile for a REST API I wrote in Golang.
Essentially the task is to create a Docker image of the REST API and push it to DockerHub.
Setting up environment for this REST API in Golang
Installing Go
Install
Go version 1.21.6 on linux/amd64
The installation also includes :
Downloading the
tar.gz
file official website.Moving the extracted folder to
/usr/local/
folder.Adding the go executable
/usr/local/go/bin
file to the path variable
Check the go version by typing
go version
in the shell
Installing dependencies
Initializing the REST API project using
go mod init
. If you are from React background - this is similar tonpm init
.Install a package - gin with the command
go get github.com/gin-gonic/gin
.
Huh....too much work eyyy!!!๐ฃ
Feel the need to create some sort of automation to escape setting up the "environment" to run any such program which requires so much to setup?
Steps to make a docker image :
Create a new branch to work on this "feature" - optional but good practice.
Create a Dockerfile.
Push the image to DockerHub - optional.
What is a Dockerfile?
There is a command in docker to automate the creation of an image. This is the docker build
command which reads instructions written in the Dockerfile.
The most important instructions are as follows :
INSTRUCTION | FUNCTIONALITY |
FROM | Fetch the mentioned base image |
WORKDIR | Specify which directory in the file system of image to execute other instructions |
COPY | Copy files and directories |
ADD | Add local or remote files and directories to the image |
RUN | Execute OS commands in the image |
ENV | Setting environment variables |
EXPOSE | To tell docker to start the container on a given port |
USER | Set user and group ID - the application is gonna be run by this user |
CMD | The command to be executed after starting the container |
ENTRYPOINT | Specify default executable |
SHELL | Set the default image of a shell |
Create a Dockerfile
Navigate to the directory containing the project.
Let's take it practically and start with the task.
First things first, create a branch on git. It often goes neglected and we start working directly on the main branch, but we must follow good practices ๐ค.
I have created a branch named "docker_image" from the main branch using the following command :
git checkout -b docker_image # create and switch to docker_image branch
Create a separate git branch to work on this "task" of creating a Dockerfile and docker image from the main branch.
git checkout -b docker_image
- NOTE : The docker image won't be created in the same directory, thankfully! We can look into the specifics later though - one thing at a time :)
Create a Dockerfile from cli using
touch Dockerfile
. The Dockerfile contents are as follows :# Use Alpine Linux as the base image FROM alpine:3.18 # Set shell to sh SHELL ["/bin/sh", "-c"] # Create a non-root user RUN adduser -D -u 1000 definitely_not_root # Install necessary dependencies RUN apk update && \ apk upgrade && \ apk add --no-cache \ curl # Download and install Go 1.21.6 RUN curl -sSL https://dl.google.com/go/go1.21.6.linux-amd64.tar.gz | tar -C /usr/local -xz # Set Go environment variables ENV GOPATH=/go ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin ENV API_URL=http://127.0.0.1/ # Create a working directory WORKDIR /api # Copy the necessary files COPY . . # Download Go dependencies mentiond in the go.mod file RUN go mod download # Switch to the non-root user USER definitely_not_root # Expose port 8080 of container EXPOSE 80 # Command to start the session with the shell CMD ["sh"]
The Dockerfile here is pretty self-explanatory with the comments mentioned above each instruction. However, I would like to elaborate on a few points here.
The docker file instructs the usage of the base image of Alpine3.18 Linux with a default shell "sh" and this can also accept command line args due to the "-c" flag.
Create a user as it is not recommended basically anywhere to carry out tasks as the ROOT user.๐ตโ๐ซ
Update the local file containing the respository list and upgrade the system (alpine linux). Install curl to download stuff from the web.
Download the go package (.tar) file from offical repository and extract it to the desired folder.
Set environment variables.
Set a working directory for the user -
definitely_not_root
we created above in the Dockerfile.The REST API Go program uses the port 80 of the container i.e. we made the container to expose it's port 80 to other applications/host. We then map it to any port of the localhost that we want. ๐
Start the image with the application "sh" - the default minimal shell of UNIX.
Building image from Dockerfile
After the Dockerfile is completed, add a .dockerignore
file if you do not want to include certain files in the docker image generated from the Dockerfile. If you have used git, this is analogous to .gitignore
file.
For reference, the contents of the directory are shown below. The contents of the .dockerignore
file are also displayed.
Running the docker image
docker images #list all the images available on your device
You'll be able to see rest_api_golang_todos
in the images list.
Now, we have to map the docker container port to some port of the localhost to actually see the API working on our browser. At least to send the "GET" request!
docker run -it -p 9090:80 rest_api_golang_todos
The port flag i.e. "-p" is used to map the ports of container to that of host.
The syntax is as follows : -p host_port:container_port
. Thus we can see the API at localhost:9000/ as shown in the images below.
After the above command, the container will start.
You can now check your username with
whoami
.Start the REST API with the command :
go run main.go
Final result of Docker image
You can navigate around with the urls :
localhost:9000/todos
localhost:9000/todos/1
localhost:9000/todos/2
localhost:9000/todos/3
You might also wanna play around with Postman API for POST, DELETE and other methods, although I haven't tested the docker image with it. It did work locally with Postman though.
Pushing image to DockerHub
Before pushing to DockerHub, it is recommended to add a tag "1" like the following command :
docker image tag <image_ID> <image_name>:1
Now our latest image points to the image with version tag 1.
Now, before pushing an image to the DockerHub, I prefer having a separate pointer locally. Thus I created a new tag
akshaykhoje/rest_api_golang_todos:1
as follows :Ensure that you have an account on DockerHub.
Login from command line with the credentials using the command :
docker login
Final command to push the image to DockerHub
docker push akshaykhoje/rest_api_golang_todos:1
Pulling from the DockerHub
Link to the docker image : https://hub.docker.com/r/akshaykhoje/rest_api_golang_todos/
To directly use the docker image from the dockerhub, enter the following command :
docker pull akshaykhoje/rest_api_golang_todos:1
- I am to change the tags, but yeah at the time of writing, I happen to be playing around with tags and other Docker jargon/flags.
Use the docker image as follows :
docker run -it -p 9090:80 akshaykhoje/rest_api_golang_todos:1
The port flag "-p" means mapping of port 80 of the container to the port 9090 of the host machine. Read more.
Once, the container starts, start the API with the following command :
go run main.go