Docker 101: Fundamentals and Practice

YAHW (Yet Another "Hello World!")Note: I used ls before the command to show you that I was in the folder that I created the hello.

py file.

As we did earlier, let's take a step back and understand how that worked.

Breaking it downWe are running pretty much the same command we ran last section, apart from two things.

The -v $(pwd):/src option tells the Docker Daemon to start up a volume in our container.

Volumes are the best way to persist data in Docker.

In this example, we are telling docker that we want the current directory — retrieved from $(pwd) — to be added to our container in the folder /src.

Note: You can use any other name or folder that you want, not only /srcIf you want to check that /src/hello.

py actually exists inside our container, you can change the end of our command from python hello.

py to bash.

This will open an interactive shell inside our container, and you can use it just like you would expect.

Isn't that crazy?Note: we can only use bash here because it comes pre-installed in the python:3 image.

Some images are so simple that they don't even have bash.

That doesn't mean you can't use it, but you'll have to install it yourself if you want it.

The last bit of our command is the python /src/hello.

py instruction.

By running it, we are telling our container to look inside its /src folder and execute the hello.

py file using python.

Maybe you can already see the wonders you can do with this power, but I'll highlight it for you anyway.

Using what we just learned, we can pretty much run any code from any language inside any computer without having to install any dependencies at the host machine — except for Docker, of course.

That's a lot of bold text for one sentence, so make sure you read that twice!Part 3.

Easiest "Hello, World!" possible from a Python image using DockerfileAre you tired of saying hello to our beautiful planet, yet?.That's a shame, cause we are gonna do it again!The last command we learned was a little bit verbose, and I can already see myself getting tired of typing all of that code every time I wanna say "Hello, World!".

Let's automate things a little bit further now.

Create a file named Dockerfile and add the following content to it:# DockerfileFROM python:3WORKDIR /src/appCOPY .

.

CMD [ “python”, “.

/hello.

py” ]Now run this command in the same folder you created the Dockerfile:docker build -t hello .

All that's left to do now is to go crazy using this code:docker run helloNote that you don’t even need to be in the same folder anymoreYou already know how it is.

Let's take a moment to understand how a Dockerfile works now.

Breaking it downStarting with our Dockerfile, the first line FROM python:3 is telling Docker to start everything with the base image we are already familiar with, python:3.

The second line, WORKDIR /src/app, sets the working directory — inside our container — for some instructions that we'll execute later, like CMD or COPY.

You can see the rest of the supported instructions for WORKDIR right here.

The third line, COPY .

 .

is basically telling Docker to copy everything from our current folder (first .

), and paste it on /src/app (second .

).

The paste location was set with the WORKDIR command right above it.

Note: We could achieve the same results by removing the WORKDIR instruction and replacing the COPY .

 .

instruction with COPY .

/src/app.

In that case, we would also need to change the last instruction, CMD ["python", ".

/hello.

py"] to CMD ["python", "/src/app/hello.

py"].

Finally, the last line CMD ["python", ".

/hello.

py"] is providing the default command for our container.

It's essentially saying that every time we run a container from this configuration, it should run python .

/hello.

py.

Keep in mind that we are implicitly running /src/app/hello.

py instead of only hello.

py, since that's what where we pointed our WORKDIR to.

Note: The CMD command can be overwritten at runtime.

For instance, if you want to run bash instead, you would do docker run hello bash after building the container.

With our Dockerfile finished, we go ahead and start our build process.

The docker build -t hello .

command reads all the configuration we added to our Dockerfile and creates a docker image from it.

That's right, just like the python:3 image we've been using for this entire article.

The .

at the end tells Docker that we want to run a Dockerfile at our current location, and the -t hello option gives this image the name hello, so we can easily reference it at runtime.

After all of that, all we need to do is run the usual docker run instruction, but this time with the hello image name at the end of the line.

That will start a container from the image we recently built and finally print the good ol' "Hello, World!" in our terminal.

Extending our base imageWhat do we do if we need some dependency to run our code that does not come pre-installed with our base image?.To solve that problem, docker has the RUN instruction.

Following our python example, if we needed the numpy library to run our code, we could add the RUN instruction right after our FROM command.

# DockerfileFROM python:3# NEW LINERUN pip3 install numpyWORKDIR /src/appCOPY .

.

CMD [ “python”, “.

/hello.

py” ]The RUN instruction basically gives a command to be executed by the container's terminal.

That way, since our base image already comes with pip3 installed, we can use pip3 install numpy.

Note: On a real python app, you would probably add all the dependencies you need to a requirements.

txt file, copy it over to the container, and then update the RUN instruction to RUN pip3 install -r requirements.

txt.

Part 4.

"Hello, World!" from a Nginx image using a long-lived detached containerI know you are probably tired of hearing me say it, but I have one more "Hello" to say before I go.

Let's go ahead and use our newly acquired docker power to create a simple long-lived container, instead of these short-lived ones we've been using so far.

Create a index.

html file in a new folder with the following content.

# index.

html<h1>Hello, World!</h1>Now, let's create a new Dockerfile in the same folder.

# DockerfileFROM nginx:alpineWORKDIR /usr/share/nginx/htmlCOPY .

.

Build the image and give it the name simple_nginx, like we previously did.

docker build -t simple_nginx .

Lastly, let's run our newly created image with the following command:docker run –rm -d -p 8080:80 simple_nginxYou might be thinking that nothing happened because you are back to your terminal, but let's take a closer look with the docker ps command.

I had to crop the output, but you’ll see a few other columns thereThe docker ps command shows all the running containers in your machine.

As you can see in the image above, I have a container named simple_nginx running in my machine right now.

Let's open up a web browser and see if nginx is doing its job by accessing localhost:8080.

Hurray!.(this is the last time, I promise)Everything seems to be working as expected, and we are serving a static page through the nginx running inside our container.

Let's take a moment to understand how we accomplished that.

Breaking it downI'm going to skip the Dockerfile explanation because we already learned those commands in the last section.

The only "new" thing in that configuration is the nginx:alpine image, which you can read more about it here.

Apart from what is new, this configuration works because nginx uses theusr/share/nginx/html folder to search for a index.

html file and start serving it, so since we named our file index.

html and configured the WORKDIR to be usr/share/nginx/html, this setup will work right out of the box.

The build command is exactly like the one we used in the last section as well, we are only using the Dockerfile configuration to build an image with a certain name.

Now for the fun part, the docker run –rm -d -p 8080:80 simple_nginx instruction.

Here we have two new flags.

The first one is the detached (-d) flag, which means that we want to run this container in the background, and that's why we are back at our terminal right after using the docker run command, even though our container is still running.

The second new flag is the -p 8080:80 option.

As you might have guessed, this is the port flag, and it's basically mapping the port 8080 from our local machine to the port 80 inside our container.

You could have used any other port instead of 8080, but you cannot change the port 80 without adding an additional setting to the nginx image, since 80 is the standard port the nginx image exposes.

Note: If you want to stop a detached container like this one, you can use the docker ps command to get the container's name, and then use the docker stop instruction with the desired container's name at the end of the line.

Part 5.

The endThat's it!.If you are still reading this, you have all the basics to start using Docker today on your personal projects or daily work.

Let me know what you thought about this article in the comments, and I'll make sure to write a follow-up article covering more advanced topics like docker-compose somewhere in the near future.

If you have any questions, please let me know.

Cheers!.. More details

Leave a Reply