Deploy Microservice with Docker
Docker Containers
"Docker provides an integrated technology suite that enables development and IT operations teams to build, ship, and run distributed applications anywhere."Docker is an open source project that enables you to package any application in a lightweight, portable container. Docker has the ability to package applications in such a way that they can run anywhere.
Docker is a powerful technology and it's supported with the majority of large public cloud. It is important to know that Docker is not a virtualization platform, it bases its operation on Linux Container.
Docker provides many benefits when used properly:
- It facilitates the development and packaging of applications in a way that leverages the skills developers already have
- It allows developers to easily create test environments
- It simplifies the maintenance operations
Check Docker installation
This article does not explain how to install Docker, so refer to the documentation on the site www.docker.com to properly install the software.In the example that follows it has been used an installation of Docker on Ubuntu (see
https://docs.docker.com/engine/installation/ubuntulinux/ )
To verify that the installation of Docker is up and running type
$ sudo docker info
To test a simple container type
$ sudo docker run hello-world
The animated GIF service
I will use the example published in the post "Provide a service for creating animated gif" to create and deploy a Microservice API on Docker.
The example code of the API can be downloaded from GitHub
https://github.com/maxzerbini/packagemain/tree/master/html/gif
The example code of the API can be downloaded from GitHub
https://github.com/maxzerbini/packagemain/tree/master/html/gif
Get the source code and copy it in the GOPATH and get also its dependencies (gin-gonic ,
freetype ).
The environment is configured in this way:
The environment is configured in this way:
$ env
HOME=/home/ubuntu
GOPATH=$HOME/gocode
GOROOT=/usr/local/goHOME=/home/ubuntu
GOPATH=$HOME/gocode
The paths that will be seen in the following examples will refer to these folders.
Get the dependences, the API code and build the project
$ go get github.com/gin-gonic/gin
$ go get github.com/golang/freetype
$ go get github.com/maxzerbini/packagemain
$ cd $GOPATH/src/github.com/maxzerbini/packagemain/html/gif$ go build -i -o gif
$ go get github.com/golang/freetype
$ go get github.com/maxzerbini/packagemain
$ cd $GOPATH/src/github.com/maxzerbini/packagemain/html/gif$ go build -i -o gif
Adding the Dockerfile
Docker allows all of the dependency issues to be discovered during the development and test cycles. That’s a lot simpler and saves a lot of time.
Add the Dockerfile to the project directory:
Dockerfile
FROM golang:latest
# Copy the local package files to the container’s workspace.
ADD . /go/src/github.com/maxzerbini/packagemain/html/gif
# Install our dependencies
RUN go get github.com/gin-gonic/gin
RUn go get github.com/golang/freetype
# Install api binary globally within container
RUN go install github.com/maxzerbini/packagemain/html/gif
# Add data volume
VOLUME /ttf
# Set binary as entrypoint
ENTRYPOINT /go/bin/gif
# Expose default port (8000)
EXPOSE 8000
# Copy the local package files to the container’s workspace.
ADD . /go/src/github.com/maxzerbini/packagemain/html/gif
# Install our dependencies
RUN go get github.com/gin-gonic/gin
RUn go get github.com/golang/freetype
# Install api binary globally within container
RUN go install github.com/maxzerbini/packagemain/html/gif
# Add data volume
VOLUME /ttf
# Set binary as entrypoint
ENTRYPOINT /go/bin/gif
# Expose default port (8000)
EXPOSE 8000
The base image is latest version of the official golang, it is used as a base for the container. Then the configuration file asks Docker to copy (ADD) the source code of the application and after RUN some go get to install the dependencies.
This API requires a data volume for reading the fonts file. The Dockerfile mounts a data volume on path /ttf .
Finally the ENTRYPOINT is declared specifying the executable that Docker will start, and it's exported to the port on which the application listens.
Update the Animated GIF code
The service developed in the example Animated GIF is fine to be distributed and launched with Docker, but it requires a small change to gain access to the data volume and read the TrueType font.This is the updated code:
// Generate an animated gif
func GenerateAnimation(text string, fontfile string, out io.Writer) {
const (
nframes = 64 // number of animation frames
delay =8 // delay between frames in 10ms units
)
var xsize int = 40 + 30 * len(text)
var ysize int = 200
// Read the font data.
ttfpath := os.Getenv("TTF_PATH")
fontBytes, err := ioutil.ReadFile(ttfpath+"/"+fontfile)
if err != nil {
log.Println(err)
return
}
f, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Println(err)
return
}
var palette = make([]color.Color,0)
// generate palette
for i := 0; i < nframes; i++ {
palette = append(palette, color.RGBA{R:0,G:0,B:uint8(4*i), A:255})
}
anim := gif.GIF{LoopCount: nframes}
for i := 0; i < nframes; i++ {
rect := image.Rect(0, 0, xsize, ysize)
img := image.NewPaletted(rect, palette)
for x := 0; x < xsize; x++ {
for y := 0; y < ysize; y++ {
img.SetColorIndex(x, y, uint8(i))
}
}
WriteText(text, f, img)
anim.Delay = append(anim.Delay, delay)
anim.Image = append(anim.Image, img)
}
gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}
The Docker-Compose tool
Docker-Compose is a tool for defining and running multi-container Docker applications. This tool uses a Compose file to configure our application’s services. Using a single command, we can create and start all the services from our configuration.We need only to define the services that make up the application in docker-compose.yml file so they can be run together in an isolated environment.
We can use the usual commands to install or upgrade Docker-Compose
$ sudo -i
$ curl -L https://github.com/docker/compose/releases/download/1.5.1/docker
$ chmod +x /usr/local/bin/docker-compose
Defining the docker-compose.yml file
This file can be added in the home directory or in some other directory, but better not to include it in the project files.
docker-compose.yml
api:
build: ./gocode/src/github.com/maxzerbini/packagemain/html/gif
ports:
- 8000:8000
volumes:
- /home/ubuntu/gocode/src/github.com/maxzerbini/packagemain/html/gif:/ttf
environment:
- TTF_PATH=/ttf
build: ./gocode/src/github.com/maxzerbini/packagemain/html/gif
ports:
- 8000:8000
volumes:
- /home/ubuntu/gocode/src/github.com/maxzerbini/packagemain/html/gif:/ttf
environment:
- TTF_PATH=/ttf
The file defines the application api and it specifies how to compile the application, which ports are necessary and which data volume is mounted. It can also define environment variables.
Running and testing
Run this command to create the image$ sudo docker-compose build
The execution of the container can be started with
$ sudo docker-compose up
We can test the microservice calling the animated.gif endpoint on a browser
http://localhost:8000/animated.gif?name=Massimo
Minimizing the Docker image size
Running the command
$ sudo docker images
we can see that the image list includes our service ubuntu_api
$ sudo docker images
we can see that the image list includes our service ubuntu_api
ubuntu_api latest f9cad0c4bb85 1 minutes ago 742.8 MB
golang latest f2675afabc6a 7 days ago 709.3 MB
ubuntu latest 91e54dfb1179 12 weeks ago 188.4 MB
hello-world latest af340544ed62 3 months ago 960 B
How can we reduce the size?
Compile the application
To remove the need to use golang as the basis for our image we need to compile the source code. The compilation must take place including all libraries, even those dynamics will be added to the executable statically.
We must return to the project folder and run this command
$ CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gif
We will produce the executable gif for the Linux platform linking all static libraries and after this we can change the Dockerfile this way:
Dockerfile
FROM alpine:latest
WORKDIR $HOME/gocode/src/github.com/maxzerbini/packagemain/html/gif
ADD gif /
# Add data volume
VOLUME /ttf
# Set binary as entrypoint
CMD ["/gif"]
# Expose default port (8000)
EXPOSE 8000
WORKDIR $HOME/gocode/src/github.com/maxzerbini/packagemain/html/gif
ADD gif /
# Add data volume
VOLUME /ttf
# Set binary as entrypoint
CMD ["/gif"]
# Expose default port (8000)
EXPOSE 8000
This image is produced from alpine, a Linux distribution in just 5MB, we could also use scratch (the void container image) to create our image.
Run this command to build the image and start it
$ sudo docker-compose build | sudo docker-compose up
Test the service calling the API endpoint
http://localhost:8000/animated.gif?name=Thin%20API
Now watching the images on Docker we see that the new image has a size considerably smaller:
$ sudo docker images
ubuntu_api latest ecd3e5b36615 1 minutes ago 15.5 MB
<none> <none> f9cad0c4bb85 15 minutes ago 742.8 MB
golang latest f2675afabc6a 7 days ago 709.3 MB
ubuntu latest 91e54dfb1179 12 weeks ago 188.4 MB
hello-world latest af340544ed62 3 months ago 960 B
Conclusions
APIs built into the Go language can easily be installed in Docker containers. Docker Hub already hosts all basis images. Docker-Compose helped us to simplify the creation of the image and to launch the application.
Finally, in this example we exploited the ability of Go to create a statically linked binary that fully contains all the application and we published a Microservice API in a lightweight Docker container.
Finally, in this example we exploited the ability of Go to create a statically linked binary that fully contains all the application and we published a Microservice API in a lightweight Docker container.
Commenti
Posta un commento