In order to deploy applications and solutions in production environment, the traditional way is to install the products in VMs (Virtual Machines), but there are certain disadvantages to this approach.
Docker evolved as a solution to all the above problems. Docker is an engine running on top of any operating system and it helps to run code in the form of containers, which are self-contained entities, where all dependencies are integrated, and thus able to run by itself with no dependency on the host operating system. It brings along the below advantages:
1. Images are the templates based on which, the individual containers (self-contained processes) are spawned | 1. Containers are the running instances that gets swooned, based on images |
2. Images are static entities | 2. Containers are the running processes, hence dynamic in nature |
Docker Installation is straight forward. Follow the documentation to install docker in your preferred platform.
docker build -t <imagename:tag> . Example : docker build -t node server:v1 .
Note : The Dockerfile needs to be in the same directory where you execute the docker command.
FROM node COPY . /apps RUN ["npm", "install"] CMD ["node", "app.js"]
docker images
Note : “docker images” is equivalent to “docker image ls”
docker image prune
docker image rm <list of images separated by comma>
docker run -itd <docker_image>
docker exec -it <container_id> /bin/bash
docker inspect <image_name>
Note: Docker images have tags (-t) and IDs, whereas docker containers have Names and IDs.
You can build a docker image and push it to the public registry or any private registry. Below are the precise steps for the same.
1. Create a Dockerfile
Example Dockerfile ------------------------- FROM tomcat ENTRYPOINT ["/usr/local/tomcat/bin/startup.sh"]
2. Build an image with the Dockerfile with the right name. You need to use the name as <dockerhub account>/<your repository in dockerhub>:<image tag>
docker build . -t anbanerj11/tomcat_animesh:v1
Note that if you have build the image already with the different name, you can retag the image using below command.
docker tag tomcat_animesh:v1 anbanerj11/tomcat_animesh:v1
In addition, the target registry is dockerhub, by default. In case you want to connect to a private registry, use the below command.
docker build . -t <registry host>/<registry name>/<account>/tomcat_animesh:v1 For docker hub, the registry host is hub.docker.com and the registry name is library.
3. Push the image to dockerhub
docker push anbanerj11/tomcat_animesh:v1
4. Now spawn the container
docker run --name mytomcat anbanerj11/tomcat_animesh:v1
Note that you specify the name of the container using –name flag. If no name is provided, a default name matching with the image is allocated.
In many situations, containers exit before going into the running state. Let me jot down few important points that helps to both create the right Dockerfile to avoid container failures built from that image spawned from that Dockerfile.
1. There must be either one CMD or ENTRYPOINT instruction in the Dockerfile. Without that, you can’t run a container from that image. Note that the CMD or ENTRYPOINT can be in any layer though. For example, in case you have a FROM instruction in the Dockerfile, and the reference image has an ENTRYPOINT or CMD, that should be fine as well.
docker pull <image_name>
Example : docker pull node
docker images
docker ps
docker ps -a
docker start <container_name>|<ID> docker stop <container_name>|<ID>
docker run -d --name <container_name> <image_name>
docker rm <container_id>
docker container prune
docker attach <container_name>|<id>
docker run -itd --rm tomcat
FROM <image> | Creates a layer with the specified image. A Dockerfile starts with this command, which, in most cases, creates the OS layer. However, it can include other images, which includes the OS layer already. |
WORKDIR <Path> | Change directory inside the container |
CMD [“command”,”parameter”] | Command to execute while creating a container from an image. There can be only one effective CMD in a Dockerfile. You can specify multiple, but only the last one will take effect. In addition, the CMD instructions can be overridden while running the container by passing the commands in argument. |
RUN <command> | This is a command to be executed at build time |
ENTRYPOINT [“command”] | This is executed at runtime, when the ENTRYPOINT instructions come first, followed by the effect CMD instruction, to build the final instructions at container runtime. For example, consider the below instructions in Dockerfile. CMD [“status”] CMD [ “run” ] ENTRYPOINT [ “./catalina.sh” ] Here is the final command that is executed at runtime: ./catalina.sh run |
VAR | This is used to define a variable that is known to the container at build time |
ENV | This is used to define a variable that is known to the container at build time and at run time. |
COPY | This is to copy files and directory contents from local to the container image |
Look at the below Dockerfile. See, how the environment variable PORT is used in another environment variable JAVA_OPTS.
FROM tomcat AS stage FROM openjdk:11 RUN mkdir /usr/local/tomcat COPY --chown=root:root --from=stage /usr/local/tomcat /usr/local/tomcat ENV PORT=8555 ENV JAVA_OPTS="-Dtomcatport=${PORT}" WORKDIR /usr/local/tomcat/conf COPY --chown=root:root ./server.xml . WORKDIR /usr/local/tomcat/bin CMD [ "run" ] ENTRYPOINT [ "./catalina.sh" ]
Finally, look at how the tomcat uses the port in server.xml
<?xml version="1.0" encoding="UTF-8"?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="${tomcatport}" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server>
Volumes are a way to persist your data inside the container, so that it persists across container restarts or crashes or deletions. Volumes are certain paths in the docker host which are mapped to a certain path inside the container. There are two kinds of Docker Volumes.
1. Bind Mount
docker run --mount type=bind,source=/tmp/temporary,target=/var/ tomcat
In this case, the docker host path /tmp/temporary is bound to the container path /var
2. Volume Mount
docker run --mount type=volume,source=<volume_name>,target=<target_path> <container_name> Example - docker run --mount type=volume,source=newvol,target=/tmp/animesh tomcat