When I started learning Docker around 3 years ago, I found it difficult to find a consolidated article that covers the docker basics and all the frequently used commands. I noted down those things here and there and today I have decided to put everything in this article which would be very handy for me as well as for you guys.
First of all use the below command to install docker and add yourself to docker group so that you would be able to access docker without being root.
sudo apt-get install docker
sudo usermod -aG docker $USER
Little bit about docker.
Docker is not a virtual machine. Docker is about isolation. It's like a user space isolation inside kernel. It can also be thought as process isolation as well as you will be running a full blown OS in that space which is completely separated from the other processes.
The image is like a reproducible output. From a developer's perspective, an image can be thought of as a Class. From the image we can run a container. So, a container can be visualized as an instantiation or object of the Class.
The host is the machine that is running docker.
Here is sample docker command.
docker run -it --rm ubuntu /bin/bash
Here docker is the CLI. run is the command. -it --rm are options and ubuntu /bin/bash are arguments. Of the two arguments ubuntu is the image and /bin/bash is the executable that needs to be executed in the container.
The above command first checks if the image ubuntu is available in the host machine. If Yes, then it starts a container based on the image. If No, it first downloads the image from the docker hub. Since we have not mentioned any version it will fetch the ubuntu:latest.
It is always advisable to mention a specific version with which you want to work. Once you have the container started, /bin/bash will be executed and you will be inside the bash shall where you will be able to run commands like ls, mkdir etc. ( The usual unix commands)
Now to check the container running, you can open one more terminal and run the below command.
It will show you the details of the running containers. Now we can exit the terminal where the container was running and it will stop the container. Look at the options that we used while running the container. -it means interactive and --rm means delete the container once it stops running.
You can remove the option --rm and run the command again. Now if the container is stopped, the container will not get deleted. To see all the containers regardless of the status, you can run the below command.
docker ps -a
This command will also display a container id. So, to start the container again, you can run the below command.
docker start container_id
Then you can again attach to the container using the below command.
docker attach container_id
To stop a container you can use the below command as well.
docker stop container_id
To remove a container you can use the below command
docker rm container_id
Now let's do some more complicated stuff. Look at the command below.
docker run -it --rm --name my-java-app -d -v %CD%:/src -w /src openjdk:8 java -jar my-app.jar
--name option is used to assign a name to the container. -d option means that you are running the container in the detach mode. So, you are not attaching to the container when it starts up. -v option maps a local mount point to the a directory inside the container.
Here we are mapping the current directory to the /src directory in the container. -w option tells the container to change the working directory to /src.
In our current directory in the host machine we have the jar file my-app.jar and now the current directory is mapped to /src in the container and hence from the /src in the container we would be able to run the jar.
But to run the jar, we need a container with jdk. Hence we are fetching openjdk version 8 from docker hub. So, basically openjdk is the image based on which the container will be created.
Now suppose our my-app.jar is a web app and we are running it on the port 3000. But if we go to http://localhost:3000, we will get an error.
We can find what's going wrong by inspecting the container using the below command.
docker inspect my-java-app
Here my-java-app is the name of our container. So, if we run this command we will not see anything in the ports section.
Because we have not mapped any host port to this container port 3000. We can modify the command as below.
docker run -it --rm --name my-java-app -d -v %CD%:/src -w /src -p 8080:3000 openjdk:8 java -jar my-app.jar
Here 8080 is the port in the host machine which is mapped to the port 3000 in the container. Now we should be able to access the app at http://localhost:8080
But now the above command becomes messy with lot of parameters in it and it's difficult to maintain. So, here comes the docker file to rescue.
We can create a file named Dockerfile and put the below info inside it and that would basically do the exactly same thing but it becomes more readable that way.
RUN mkdir /src
ADD my-app.jar /src
Now we have to build the image first using this docker file with the below command.
docker build -t my-app-img .
The above command will look for a file named Dockerfile in the current directory and it will build an image from it and will tag the image as my-app-img.
If your file name is not Dockerfile then you will have to explicitly specify the file name. Dockerfile is the default file name.
Once the image is built, you can check the image using the below command.
Now you can run the container based on the built image as below
docker run --rm -p 8080:3000 -d my-app-img
This looks much better and concise. But you can still argue that we need to provide the port binding and image name etc. as part of the command to be run. So, there should be a better way.
docker-compose to the rescue. docker-compose is a yml file where we can declare our services and port bindings etc. and it is much more concise when you have many services and maintenance becomes very easy.
To use docker-compose, first install docker compose using
sudo apt-get install docker-compose
Here is a sample docker-compose.yml file
The first line of the file defines which version of docker-compose we are using. In this case we are using version 2 of docker-compose. In the services section we declare our services.
We have two services config-service and eureka-service. The config-service binds the host port 8888 to the container port 8888. So, our config-service is running on port 8888 in our container.
image defines the name of the image that we are going to build. container_name defines the name of the container that will be run based on the built image.
The build section defines how to build the image. Suppose we already have the Dockerfiles created for our services as I have mentioned before.
The context parameter defines the relative path to the Dockerfile for the service from the directory of the docker-compose.yml file.
In the eureka-service, we mentioned that eureka-service depends on the config-service. So, docker will first try to start the config-service before it starts the eureka-service.
But here is the catch. Docker will not check whether config-service has properly started before it starts the eureka service. It will simply execute the command say "java -jar config.jar" before "java -jar eureka.jar". There is no guarantee that config-service will properly start before eureka-service.
There is also a environment section where we can give environment variable values. So, when eureka-service will start it will use the value http://config-service:8888 for the CLOUD_CONFIG_SERVER_URI environment variable.
Now we can start up the containers for our services by simply running the command below
To stop the containers simply run the command
If you want to guarantee that config-service starts up properly before eureka service, you can first start up the config service as below.
docker-compose config-service up
And once it starts up properly, then run
It is intelligent enough to see that config-service is already up and it will not try to start it again.
We can also have something called network in the docker-compose file but we will not go into that as you can always add more to it once you get the basics right.
So, that's it about docker basics. Hope this will make your docker journey little smooth. Leave your queries in the comment section.
Sharing is Caring!
RECOMMENDED POSTS FOR YOU