Sharing your containers on Docker Hub
To share our containers, we'll use Docker Hub and publish the two containers. The rest of the team can pull the pre-built containers from Docker Hub and use them without having to deal with the source code repository at all. They are just microservices to them, just as we don't need the source to Mosca, MongoDB, or Redis with those containers.
Of course, the development team is going to have to run them.
We have created an organization on Docker Hub, dockerfordevelopers, which we will use to publish the containers for this book. You won't be able to push to it, but we can. In order to publish to Docker Hub, you will need to use the docker login command, and you must have already created an account on https://hub.docker.com/.
You can also create your own organization on Docker Hub where you can share your own containers. If you want to use the examples in the GitHub repository for this chapter, you will have to edit the scripts to replace dockerfordevelopers with your own organization name.
Since we are creating our own custom containers, we will need some .sh scripts for each container, as explained in the previous chapter. There are a set of .sh scripts for the publisher and the subscriber.
The Dockerfile used to build the container for the publisher is almost identical to the one used in the previous chapter:
# we will inherit from the NodeJS v12 image on Docker Hub
FROM node:12
# set time zone so files' timestamps are correct
ENV TZ=America/Los_Angeles
# we include procps and telnet so you can use these with shell.sh prompt
RUN apt-get update -qq >/dev/null && apt-get install -y -qq curl procps telnet >/dev/null
# add a user - this user will own the files in /home/app
RUN useradd --user-group --create-home --shell /bin/false app
# set up and copy files to /home/app
ENV HOME=/usr/app
WORKDIR /home/app
COPY . /home/app
# install our NodeJS packages (from package.json)
RUN yarn install
# we run a script to stat the server; the array syntax makes it so ^C will work as we want
CMD ["yarn", "start"]
The major difference in this Dockerfile and the one in the previous chapter is that we are not installing Apache and PHP, but we are inheriting from node:12 and installing our Node.js program's required packages.
We are inheriting from node:12 in this Dockerfile for the publisher. The Dockerfile for the subscriber is identical, except that it inherits from node:13. This illustrates how you can have containers with different base software versions on the same host; this would be unpleasant to deal with on a host without containers.
Note
The node:12 and node:13 containers are pulled from Docker Hub and updated each time we build the containers.
The following is the build.sh script that is used to build the publisher:
#!/bin/sh
# build.sh
# we use the "docker build" command to build a container named "dockerfordevelopers/publisher" from . (current directory)
# Dockerfile is found in the current directory, and determines how the container is built.
docker build -t dockerfordevelopers/publisher .
The build.sh script is very short and only really consists of the line, a single command. It is easier to type ./build.sh instead of the whole docker build -t dockerfordevelopers/publisher . command. This also makes the process less error-prone and you don't have to memorize the command-line switches and format.
There is a nearly identical build.sh script for the subscriber, too. Only the name of the container built is different: dockerfordevelopers/subscriber.
The output of the build.sh script for the publisher is as follows:
# ./build.sh
Sending build context to Docker daemon 4.902MB
Step 1/9 : FROM node:12
Step 2/9 : ENV TZ=America/Los_Angeles
Step 3/9 : RUN apt-get update -qq >/dev/null && apt-get install -y -qq curl procps telnet >/dev/null
Step 4/9 : RUN useradd --user-group --create-home --shell /bin/false app
Step 5/9 : ENV HOME=/usr/app
Step 6/9 : WORKDIR /home/app
Step 7/9 : COPY . /home/app
Step 8/9 : RUN yarn install
yarn install v1.16.0
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 1.55s.
Step 9/9 : CMD ["yarn", "start"]
---> Running in f882d870bc6a
Removing intermediate container f882d870bc6a
---> b8f9439e36fa
Successfully built b8f9439e36fa
Successfully tagged dockerfordevelopers/publisher:latest
You can see that the 1/9, 2/9, 3/9, and so on steps map one to one to the lines in our Dockerfile. The first line in our Dockerfile reads From Node:12 and the Step 1/1 line reads From Node:12. Similarly, Step 2/2 is the second line in the Dockerfile. The build process follows the Dockerfile as a series of steps to build the final container image.
The last line in the output tells us that the name of the container is dockerfordevelopers/publisher:latest. We use this name to push our build container to Docker Hub.
We use the push.sh script to perform the commands to push the publisher container to the organization on Docker Hub:
#!/bin/sh
# push.sh
docker push dockerfordevelopers/publisher
This is another one-line .sh script for our convenience.
The following is the output of the push.sh script for the publisher:
# ./push.sh
The push refers to repository [docker.io/dockerfordevelopers/publisher]
9502c45a0d0e: Pushed
79b7f0047832: Pushed
bca5484440a2: Pushed
…
6a335755bda7: Pushed
latest: digest: sha256:e408ae01416511ad8451c31e532e3c2c6eb3324 ad43834a966ff161f9062e9ad size: 3056
#
We have a sort of template or pattern for working with custom containers in our microservices architecture project:
- We edit and debug the code for our container.
- We run the build.sh script to build a container image.
- We run the push.sh script to push the container to Docker Hub.
Your fellow developers can now run the publisher image. This is run on a second machine, such as a developer's workstation:
# docker run --rm dockerfordevelopers/publisher
Unable to find image 'dockerfordevelopers/publisher:latest' locally
latest: Pulling from dockerfordevelopers/publisher
c5e155d5a1d1: Pull complete
221d80d00ae9: Pull complete
4250b3117dca: Pull complete
69df12c70287: Pull complete
…
Digest: sha256:e408ae01416511ad8451c31e532e3c2c6eb3324ad 43834a966ff161f9062e9ad
Status: Downloaded newer image for dockerfordevelopers/publisher:latest
yarn run v1.16.0
$ node ./index.js
Of course, on this second machine, the developer has installed and run the required microservices: Mosca, MongoDB, and Redis. The application will not run without all the microservices running within Docker.
Pushing to Docker Hub on your development host and pulling from Docker Hub on a production host is a simple way to deploy containers for production. It is not very robust, however. We will cover better schemes for deployment in later chapters.