Image Creation, Management, and Registry(Part 1)
Tags: docker, linux, devops, microservices
Published: 2021-09-12 12:35:35

### Working with Docker Images

Every Docker container is based on an image.

Till now we have been using images that were created by others and available in Docker Hub.

Docker can build images automatically by reading the instructions from a Dockerfile

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

![Dockerfile build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7yjvsd1903ntc4oar2r.png)

### Overview of Dockerfile

Basic format of Dockerfile:

```dockerfile
FROM busybox
COPY cat.txt
ADD add.txt
ADD compress.tar.gz /tmp
CMD ["sh"]
```
A Dockerfile must start with a FROM instruction.
The FROM instruction specifies the Base Image from which you are building.

There are multiple INSTRUCTIONS that are available in Dockerfile, some of these include:

*   FROM
*   RUN
*   CMD
* LABEL
* EXPOSE
* ENV
* ADD
* COPY
* ENTRYPOINT
* VOLUME
* USER

Many more....

### COPY vs ADD Instruction

_COPY_ and _ADD_ are both Dockerfile instructions that serve similar purposes.

They let you copy files from a specific location into a Docker image.

#### Difference between COPY and ADD

**_COPY_** takes in an src and destination. It only lets you copy in a local file or directory from your host

**_ADD_** lets you do that too, but it also supports 2 other sources.

First, you can use a URL instead of a local file/directory. Secondly, you can extract a tar file from the source directly into the destination.

#### Use WGET/CURL wherever possible

Using ADD to fetch packages from remote URLs is strongly discouraged; you should use curl or wget instead.

This will execute all the lines repeatedly, will execute everytime same file. So, its not a good practice

```dockerfile
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
```

This will not create unnecessary files and will make a sequence, which will deal with only file file and then process.

```dockerfile
RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all
```

Lets have a demo to show you how **_ADD_** work.
1. Create a Compressed file
2. Create a Dockerfile and define the file to copy from to destination
3. Run that Dockerfile
4. Verify the Output on the Destination directory.

##### Step 1. Create Compressed file

```shell
root@dporwal-docker:~# touch compress.txt
root@dporwal-docker:~# tar -czvf compress.tar.gz compress.txt 
root@dporwal-docker:~# docker build -t demobusybox .
```

![createCompressedfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d98v9ueu25jxcgef7mwh.png)

#### Step 2. Create a Dockerfile and define the file to copy from to destination

```shell
vi Dockerfile
```

_dockerfile1_

![Dockerfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ab6hu6futbb956xb4i9l.png)

#### Step 3. Run that Dockerfile

```shell
$ docker build .
```

#### Step 4. Verify the Output on the Destination directory.

Start the container that is build by DOcerfile

![verifyOutput](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2lurryux6f8l8bsmh28.png)

### ***EXPOSE*** Instruction

The _EXPOSE_ instruction informs Docker that the container listens on the specified network ports at runtime and it does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.

In above dockerfile1 you can see the scenario where person will not be able to get the port it should intends to publish because of not using _EXPOSE_ command

![inaccesible](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gksefea5yg8sf8ageiby.png)

So, now we will use this _EXPOSE_ command Dockerfile.

Here I'm creating a dockerfile, which will launch nginx server on port 9080.

```dockerfile
FROM ubuntu:latest
LABEL version="0.0.1"
LABEL maintainer="porwal.deepak2000@gmail.com"

RUN apt-get update && apt-get upgrade -y

RUN apt-get install nginx -y 

EXPOSE 9080

CMD ["nginx", "-g", "daemon off;"]
```
This is my initial environment of my docker server.

![inital stage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mx2m576mf65fl7otafjv.png)
 
Now we will run below commands to build the custom image _exposedemo_ with tag 0.0.1

```shell
docker image build --tag exposedemo:0.0.1 .
```

![build exposedemo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yojc8oiv2020c6qz60ee.png)
 
And here we will create a container from the _exposedemo_ image we build.

```shell
docker container run -d exposedemo:0.0.1
```

And in this image you can see in yellow box the container is running on port **9080**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdwhpk4q7sjymy1mzado.png)
 
to Verify you can also use this command.

```shell
docker inspect exposedemo:0.0.1 
```

![verifyexpose](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6toe473r96zikg6g0nka.png)

### ***HEALTHCHECK*** Instruction

HEALTHCHECK instruction Docker allows us to tell the platform on how to test that our application is healthy.

When Docker starts a container, it monitors the process that the container runs. If the process ends, the container exits. 

That's just a basic check and does not necessarily tell the detail about the application.

We can specify certain options before the CMD operation, these includes:

```shell
HEALTHCHECK --interval=5s CMD ping -c 1 172.17.0.2

--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--start-period=DURATION (default: 0s)
--retries=N (default: 3)
```

Lets create a container _demohealthcheck_ which we will be using to check the connection, and will create another container _monitor_ where we will implement **HEALTCHECK** command by checking connection with _demohealthcheck_ container.

Commands:

```shell
docker container run -dt --name demohealthcheck busybox sh
```

We will get the IP Address of this container to connect.

```shell
docker inspect demohealthcheck 
```
![demohealthcheck](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38w2tq1ce9p7wqzk0fcw.png)
 
Now lets create dockerfile and implement _HEALTHCHECK_ command

```dockerfile
FROM demohealthcheck
HEALTHCHECK --interval=5s CMD ping -c 1 172.17.0.3
```

![healthcheckdockerfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvdi1xnc4altxdu0qidy.png)

Building image and container from the above docker file and check the connection.

![monitoringimage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebaey6ht7kixse1o4w1c.png)

We can also check the HEALTHCHECK Logs by

```sell
docker inspect monitor
```

![inspectmonitor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xjk9as718m2g4jvfnkl.png)

if *ExitCode: 1* then, there is something wrong with container.

Exit Code | Description
--- | ---
0: Success | the container is healthy and ready to use
1: Failure | the container is not working correctly
2: Reserved | do not use the exit code

We can also use HealthCheck commands while building the container.
like below we are using _--health-cmd_

command:

```shell
docker run -dt --name tmp --health-cmd "curl -f http://localhost" busybox sh
```

and we will see that container is unhealthy.On doing _docker inspect tmp_ we found that curl command is not found.

![unhealthycontainer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7jajt8oig92y34njgfcl.png)

![curlcommandnotfound](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m750agsusljqeqsjb3v3.png)

Now we will remove this container and will try to check the container after every 5 sec and it will retry 1 time on failure.

_you can try below command and let me know in discussion if you are good to do so._

```shell
docker run -dt --name tmp --health-cmd "curl -f http://localhost" --health-interval=5s --health-retries=1 busybox sh
```

for more commands you can check:
https://docs.docker.com/engine/reference/run/#healthcheck

### Dockerfile ***ENTRYPOINT***

The best use for ENTRYPOINT is to set the image’s main command
ENTRYPOINT doesn’t allow you to override the command.
It is important to understand the distinction between CMD and ENTRYPOINT.

As, CMD command in Dockerfile can be overridden by runtime command. Whereas ENTRYPOINT command will append to the given command given at runtime.


Sample Code Snippet:

```dockerfile
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
```
![CMD Command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w72pz1vjqwl9zkvubvq5.png)

![entrypoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rksyf36qhynl292sxxze.png)
 
### ***WORKDIR*** Instruction

The _WORKDIR_ instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile

```dockerfile
FROM busybox
RUN mkdir /root/demo
WORKDIR /root/demo
RUN touch dporwal.txt
CMD ['/bin/sh']
```

![workdircmd](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytw4hdyzpm06j196jswo.png)
 

The WORKDIR instruction can be used multiple times in a Dockerfile


Sample Snippet:

```
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
```

Output = /a/b/c

![Exampleabove snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r14v8lg88t71auoob9aw.png)
 
### ***ENV*** Instruction

_Passing as argument during run time._
The ENV instruction sets the environment variable <key> to the value <value>.

You can use -e, --env, and --env-file flags to set simple environment variables in the container you’re running or overwrite variables that are defined in the Dockerfile of the image you’re running.

Example Snippet:

```shell
docker run --env VAR1=value1 --env VAR2=value2 ubuntu env | grep VAR

Eg.
docker run -dt --name env01 --env USER=USERADMIN busybox sh
```


![envcommand](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4f4nrbo4m11dq8ncv03.png)
 
_implementation of ENV in dockerfile_

Example Snippet:

```dockerfile
FROM busybox
ENV NGINX 1.2
RUN touch web-$NGINX.txt
CMD ["/bin/sh"]
```
![envdockerfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4xn7jyl2p6agf6wfwc0q.png)

References:
[Official Docker](https://docs.docker.com/get-docker/)
[Udemy Course](https://www.udemy.com/course/docker-certified-associate/)

Credit:
[Zeal Vora](https://in.linkedin.com/in/zealvora)

[Prev: Getting Started with Docker](https://dev.to/dporwal/docker-20kl)

[Next: Image Creation, Management, and Registry(Part 2)](https://dev.to/dporwal/image-creation-management-and-registry-part-2-514n)