How to Create a Docker Image From a Container – DataSet

In this article, I’ll provide step-by-step instructions on how to create a Docker container, modify its internal state, and then save the container as an image.

This is really useful when you’re working on how an image should be built because you can keep tweaking a running container until it works the way you want it to. When you’re done, just save it as an image. You can use the following guide to customize and deploy the DataSet agent for common tasks, such as adding parsers for log files, adding red/sampling rules, running custom plug-ins, or mounting volumes for application log files.

Before jumping right into it, we’d like to invite you to a relevant webinar on how to get the most value from your Kubernetes audit logs. If you run Docker containers in K8s environments, we’ll cover best practices for implementing comprehensive, secure, and efficient audit logs on production Kubernetes clusters. We look forward to seeing you at the webinar.

Step 1: Create

a base container

Let’s start by creating a running container. So that we don’t get bogged down in the details of any particular container, we can use nginx.

Docker’s create command will create a new container for us from the command line

: ~ docker create -name nginx_base -p 80:80 nginx:alpine

Here we have requested a new container named nginx_base with port 80 exposed to localhost. We are using nginx:alpine as the base image for the container.

If you don’t have the nginx:alpine image in your local Docker image repository, it will download automatically. When this happens, you’ll see something like this

: Cannot find image ‘nginx:alpine’ locally Alpine: Library Pull / nginx df9b9388f04a: Full Pull 5867cba5fcbd: Full Pull 4b639e65cb3b: Full Pull 061ed9e2b976: Full Pull bc19f3e8eeb1: Full Pull 4071be97c256: Full Pull Summary: sha256:5a0df7fb7c8c03e4158ae9974bfbd6a15da2bdfdeded4fb694367ec812325d31 Status: Download Latest Image for nginx:alpine 85b13f4d8a9bcdab4fbae540cf7bf3704eab13b57c5f44a2d3529d86f1c72ba5

Step 2: Inspect

images

If you look at the list of images on your system, you will now see the nginx: alpine image:

➜ ~ docker images -a REPOSITORY TAG IMAGE SIZE SIZE CREATED amitsharma/nginx-reverse-proxy v1 1037dc5f8db4 3 weeks ago 142MB nginx-reverse-proxy last 1037dc5f8db4 3 weeks ago 142MB amitsharma/web-server-app v1 09a0abf08e08 3 weeks ago 58.3MB web-server-app more Recent 09a0abf08e08 3 weeks ago 58.3MB nginx alpine 51696c87e77e 4 weeks ago 23.4MB Step

3: Inspect

containers

Note here that the container is not running, so you won’t see it in the list of containers unless you use the -a flag (-a is for everyone).

➜ ~ docker ps -a CREATED CONTAINER ID IMAGE COMMAND STATE PORT NAMES c365af6303e4 nginx: alpine “/docker-entrypoint….” 6 minutes ago Created nginx_base

Step 4: Start

the container

Let’s start the container and see what happens

. ➜ ~ docker start nginx_base nginx_base

Now visit http://localhost with your browser. You will see the default page “Welcome to nginx!”. We are now running an nginx container.

Step 5: Modify

the running container

So if you want to modify this running container to behave in a specific way, there are a variety of ways to do it

.

To keep things as simple as possible, we’re just going to copy a new file .html index to the server. You could do pretty much anything you wanted here.

Let’s create a new index file.html and copy it to the running container. Using an editor on your machine, create a file .html index in the same directory from which you have been running Docker commands.

Then paste the following HTML into it:

<html> <head> <title>Hello World</title> </head> <body> <h1>Hello World!</h1> </body>

Then save the file and return to the command line. We’ll use the docker cp command to copy this file to the running container.

➜ ~ docker cp index.html nginx_base:/usr/share/nginx/html/index.html Now

reload your browser or revisit http://localhost. You will see the message “Hello World!” instead of the default nginx welcome page.

Step 6: Create an image from

a container So, at this point,

we’ve updated the contents of a running container and as long as we keep that container, we don’t need to do anything

.

However, we want to know how to save this container as an image so that we can make other containers based on it. The Docker commands to do this are pretty simple.

To save a Docker

container, we just need to use

the docker commit command like this: ➜ ~ docker commit nginx_base sha256:0c17f0798823c7febc5a67d5432b48f525320d671beb2e6f04303f3da2f10432

Now look at the list of Docker images:

➜ ~ docker images -a REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 0c17f0798823 About a minute ago 23.4MB amitsharma/nginx-reverse-proxy v1 1037dc5f8db4 3 weeks ago 142MB nginx-reverse-proxy last 1037dc5f8db4 3 weeks ago 142MB amitsharma/web-server-app v1 09a0abf08e08 3 weeks ago 58.3MB latest web-server-app 09a0abf08e08 3 weeks ago 58.3MB nginx alpine 51696c87e77e 4 weeks ago 23.4MB

You can see that there is a new image there. It has no repository or tag, but it exists. This is an image created from the running container. Let’s tag it to make it easier to find later.

Step 7: Tag

the image

Using docker tag, we can name the image we just created. We need the image ID for the command, so since the image ID listed above is 0c17f0798823, our command will be: ➜~docker tag 0c17f0798823

hello_world_nginx

And if we look at the image index again, we can see that the <None> were replaced: Actually, we can use complicated tags here with version numbers and all other fixes from a tag command, But for our example, we will only create an image with a meaningful name.

Step

8: Create tagged images

You can also tag the image as it is created by adding another argument to the end of the command like this

: ➜ ~ docker commit nginx_base hello_world_nginx

This command effectively confirms and tags at the same time, which is useful but not mandatory

.

Step 9: Delete the original

container

We previously started a Docker container. We can see that it is still running using the docker ps command.

➜ ~ docker ps CREATED CONTAINER ID IMAGE COMMAND STATE PORT NAMES C365AF6303E4 nginx:Alpine “/docker-entrypoint….” 33 minutes ago Up to 25 minutes 0.0.0.0:80->80/tcp nginx_base

Let’s stop and remove the currently running Docker container and delete it.

➜ ~ docker stop nginx_base nginx_base ➜ ~ docker rm nginx_base nginx_base

If we list all Docker containers, We shouldn’t have any

: ➜ ~ docker ps -a CREATED CONTAINER ID IMAGE COMMAND STATE PORT NAMES

Now, let’s create a new container based on the image we just created and get started

. ➜ ~ docker run -name hello_world -d -p 80:80 hello_world_nginx 7ca08e03862dcdaba754718be2fef18b8f9c57291fe25da239bd615a7802a80f

Note that docker run is the equivalent of running docker create followed by docker start; We’re only saving one step here.

The -d option tells Docker to run the separate container for us to retrieve our command prompt.

Step 10: Look

at running containers

If you look at the running containers now, you’ll see that we have one called

hello_world: ➜ ~ docker ps CREATED CONTAINER ID IMAGE COMMAND STATE PORT NAMES 7ca08e03862d hello_world_nginx “/docker-entrypoint….” 2 minutes ago Up to 2 minutes 0.0.0.0:80->80/tcp hello_world

Now go see http://localhost.

As you can see, the page .html index now displays the message “Hello World!” just as we wanted.

Stop the hello_world container before moving on to the next section.

➜ ~ Docker Stop hello_world hello_world

Step 11: Consider Your Options

There are some optional things we can do using the commit command that will change the information about our images

.

For example, we may want to record who is the author of our image or capture a confirmation message that tells us the status of the image

.

All of these are controlled via optional parameters for the commit command

. Let’s go back to our original container. We’re going to use a slightly different command here for easier cleaning

: ➜ ~ docker run -name nginx_base -rm -d -p 80:80 nginx:alpine 92158632375ff41c0232cf69fcdff7beeec981ffd552eaaef11f45db3f061da3

This command will run the nginx:alpine image with the name nginx_base; the creation of the image will be included in the execution of the command.

The -rm will cause the container to be deleted when it is turned off. -d instructs the command-line client to run in separate mode. This will allow us to execute other commands from the same terminal.

So if you visit http://localhost now, you should see nginx’s default welcome page.

We changed things about the previous running container, so I won’t repeat that work here; instead, we want to look at the various options around the commit subcommand.

Option A: Set

authorship

Let’s start by setting the authorship of the image. If you inspect the Docker image hello_world_nginx above, you will find that its author field is blank.

We’ll use the docker inspect command to get the image details and remove the author line.

➜ ~ Docker inspect hello_world_nginx | grep Author “Author”: “”, So if we use the author option in the docker commit

command, we can set the value of the author field. ➜ ~ docker commit -author

amit.sharma@sentinelone.com nginx_base authored sha256:d0229f7f014bc510c16ec03d3c9ebcf25594827fde18c274cea2f44d116c948e

And we can check the authorship of that image:

➜ ~ docker inspect authored | grep Author “Author”: “amit.sharma@sentinelone.com”,

Let’s delete that image and try some other options

: ➜ ~ docker rmi authored Untagged: authored:latest Removed: sha256:d0229f7f014bc510c16ec03d3c9ebcf25594827fde18c274cea2f44d116c948e Deleted: sha256:6d2a62cfa2e2801b9a9e5ed0a5ccf5e173e621e7fe05b15f32379537464e38ec

Option B: Create messages

Suppose you want a confirmation message to remind you what the image is about or what the state of the container was at the time the image was created.

There is a -message option you can use to include that information.

Run this command

: ➜ ~ docker commit -message ‘this is a basic nginx image’ nginx_base mmm sha256:d717f5e1285ec7a539f1e59908375ef3111f59f176ec0e40ec5835ddc96d9816 Using the image name

, we can view the history of the Docker image to see our message. Here we are using the docker history command to display the change history of the image we created: ➜ ~ docker history

mmm IMAGE CREATED BY SIZE COMMENT d717f5e1285e About a minute ago nginx -g daemon off; 1.09kB this is a basic image nginx 51696c87e77e 4 weeks ago /bin/sh -c #(nop) CMD [“nginx” “-g” “daemon… 0B 4 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B 4 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B 4 weeks ago /bin/sh -c #(nop) ENTRYPOINT [“/docker-entr… 0B 4 weeks ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB 4 weeks ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB 4 weeks ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB 4 weeks ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB 4 weeks ago /bin/sh -c set -x && addgroup -g 101 -S … 17.8MB 4 weeks ago /bin/sh -c #(nop) ADD file:5d673d25da3a14ce1… 5.57MB

Notice that we see all the history here, and the first entry is from our confirmation of the running container. The first line that appears shows our confirmation message in the rightmost column.

Let’s delete this image and look at the other options

: ➜ ~ docker rmi mmm Untagged: mmm:latest Removed: sha256:d717f5e1285ec7a539f1e59908375ef3111f59f176ec0e40ec5835ddc96d9816 Deleted: sha256:6d2a62cfa2e2801b9a9e5ed0a5ccf5e173e621e7fe05b15f32379537464e38ec

Option C: Confirm without pause

When you use the commit command, the container will stop.

For our little gaming container this isn’t important, but you might be doing something like capturing an image of a production system where pausing isn’t an option.

You can add the -pause=false flag to the commit command and the image will be created from the container without the pause.

➜ ~ docker commit -pause=false nginx_base wo_pause sha256:d78b9fb9c8a0115dd22ad6d142507d44c6300e90bbc32feb62891092100a0c9a

If you don’t pause the container, you risk damaging your

data.

For example, if the container is in the middle of a write operation, the data being written might be corrupted or come out incomplete. That’s why, by default, the container stops before the image is created.

Let’s remove this image and look at the other options

: ➜~docker rmi wo_pause Untagged: wo_pause:latest Removed: sha256:d78b9fb9c8a0115dd22ad6d142507d44c6300e90bbc32feb62891092100a0c9a Removed: sha256:6d2a62cfa2e2801b9a9e5ed0a5ccf5e173e621e7fe05b15f32379537464e38ec

Option D: Change Settings

The last option I want to discuss is the -c or -change indicator. This option allows you to configure image settings.

You can change any of the following image settings during the confirmation process

:

  • CMD
  • ENTRYPOINT
  • ENV EXPOSE

  • LABEL
  • ONBUILD
  • USER VOLUME
  • WORKDIR

The original Nginx docker file contains the following settings:

  • CMD [“nginx”, “-g”, “daemon off;”]
  • ENV NGINX_VERSION 1.15.3
  • EXPOSE 80

So we’ll only play with one of those for a moment. The NGINX_VERSION and EXPOSE could cause problems with the container startup, so we’ll mess with the command line (CMD) executed by the container.

Nginx allows us to pass the -T command line argument that will dump your configuration to the standard. Let’s make

an image with an alternative CMD value as follows: ➜ ~ docker commit -change=’CMD [“nginx”, “-T”]’ nginx_base conf_dump sha256:0a6cf9c4443e9d0a7722aeaf528ffc6b40622bf991928dfb97bd3db0a3ea6dee Now stop the

nginx_base container with this command

: ➜ ~ docker stop nginx_base nginx_base

And start a new container from the image we just created

: ➜ ~ docker run -name dumper -p 80:80 conf_dump

The settings for the nginx process will be rolled back to standard when you run this command. You can scroll back several pages to find the command we executed.

Docker Imaging

: Conclusion

The docker commit subcommand is very useful for diagnostic activities and bootstrapping new images from existing containers

.

As I showed earlier, there are also many useful options available. The Docker CLI has many other power commands. If you wish, you can explore some of them here.

DataSet is the best log analytics solution for dynamic container environments. It’s the only solution that provides unmatched performance and scale while optimizing total cost of ownership.

For more information about working with Docker and DataSet, see these resources:Installing the DataSet agent in DockerConfiguring the DataSet agent for Docker