Editor – The NGINX Plus Dockerfiles for Alpine Linux and Debian were updated in November 2021 to reflect the latest software releases. They also (along with the revised instructions) use Docker secrets to pass license information when creating an NGINX Plus image.
Docker is an open platform for creating, shipping, and running distributed applications as containers (lightweight, standalone, executable software packages that include everything needed to run an application). In turn, containers can be deployed and orchestrated using container orchestration platforms such as Kubernetes. (In addition to the Docker container technology discussed in this blog, NGINX provides the NGINX F5 ingress controller in versions based on NGINX Open Source and NGINX Plus; for NGINX Plus subscribers, support is included at no additional cost.)
As software applications, NGINX Open Source and F5 NGINX
Plus are great use cases for Docker, and we publish an Open Source NGINX image on Docker Hub, the Docker image repository. This post explains how to:
Deploy NGINX Open Source using the Docker Hub image Manage NGINX
- in the container (connect, configure, register, reload, and restart)
- Deploy NGINX Plus using a
Docker image you create Introduction Docker’s open platform includes Docker Engine, the open-source runtime that builds, runs, and orchestrates containers,
and Docker Hub, a
hosted service where Dockerized applications are distributed, shared, and collaborated upon by the entire development community or within the boundaries of a specific organization
. Docker containers allow developers to focus their efforts on the “content” of the application by separating applications from infrastructure limitations.
Dockerized applications are instantly portable to any infrastructure (laptop, bare-metal server, VM or cloud), making them modular components that can be easily assembled and reassembled into full-featured distributed applications and continuously innovate in real time.
For more information about Docker, see Why Docker? or the full Docker documentation.
Using the NGINX open source Docker image You can create an NGINX instance in a Docker container
using the NGINX open source image from Docker Hub
.
Let’s start with a very simple example. To launch an NGINX instance running in a container and using the default NGINX settings, run this command
: # docker run -name mynginx1 -p 80:80 -d nginx fcd1fb01b14557c7c9d991238f2558ae2704d129cf9fb97bb4fadf673a58580d
This command creates a container named mynginx1 based on the NGINX image. The command returns the long form of the container identifier, which is used in the name of the log files; see Managing the registry.
The -p option instructs Docker to map the port exposed in the container by the NGINX image (port 80) to the port specified on the Docker host. The first parameter specifies the port on the Docker host, while the second parameter maps to the exposed port on the container.
The -d option specifies that the container runs in separate mode, which means that it continues to run until it stops, but does not respond to commands that are executed on the command line. In the next section, we explain how to interact with the container.
To verify that the container was created and is running, and to view port assignments, we run docker ps. (We’ve split the output into several lines here to make it easier to read.)
# docker ps CONTAINER ID IMAGE COMMAND STATE CREATED… fcd1fb01b145 nginx:latest “nginx -g ‘daemon of 16 seconds ago Up 15 seconds … … PORT NAMES … 0.0.0.0:80->80/tcp mynginx1
The PORTS field in the output indicates that port 80 on the Docker host is mapped to port 80 on the container. Another way to verify that NGINX is running is to make an HTTP request to that port. The code for the default NGINX welcome page appears:
#curl http://localhost <! DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, The Nginx web server is installed and working properly. Additional configuration required.</p> <p>For documentation and online support, see <a href=”http://nginx.org/”>nginx.org</a>.<br/> Commercial support is available at <a href=”https://www.nginx.com/”>nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
Working with NGINX Docker Container
So now we have a NGINX Docker
container up and running, but how do we manage NGINX content and configuration? And what about logging? A
note on
SSH
It is common to enable SSH access to NGINX instances, but the NGINX image does not have OpenSSH installed, because Docker containers are usually meant to be for a single purpose (in this case running NGINX). Instead, we’ll use other methods supported by Docker.
As an alternative to the following
commands, you can run the following command to open an interactive shell in a running NGINX container (instead of starting an SSH session). However, we recommend this only for advanced users.
On Alpine Linux systems: # docker exec -it NGINX_container_ID
- sh On Debian
systems
: # docker exec -it
-
NGINX_container_ID bash
Content Management and Configuration Files There are several ways to manage both NGINX served content and NGINX
configuration files
. Here we cover some of the options.
Option 1 – Keep content and settings
on the Docker host When the container
is created, we can tell Docker to mount a local directory on the Docker host to a container directory. The NGINX image uses the default NGINX configuration, which uses /usr/share/nginx/html as the root directory of the container and places the configuration files in /etc/nginx. For a Docker host with content in the /var/www local directory and configuration files in /var/nginx/conf, run this command (which appears in several lines here just for readability): # docker run -name mynginx2 -mount type=bind source=/var/www,target=/usr/share/nginx/html,readonly -mount type=bind,source=/var/nginx/conf, target=/etc/nginx/conf
, readonly -p 80:80 -d nginx Now any changes made to the files in the local directories /var/www and /var/nginx/conf on the Docker host are reflected in the /usr/share/nginx/
html and /etc /nginx directories in the container. The read-only option means that these directories can only be changed on the Docker host, not from the container.
Option 2 – Copy
files from the Docker host
Another option is to have Docker copy the contents and configuration files from a local directory to the Docker host during container creation. Once a container is created, the files are kept by creating a new container when the files change or by modifying the files in the container. An easy way to copy the files is to create a Dockerfile with commands that are executed during the generation of a new Docker image based on the NGINX image from Docker Hub. For file-copy (COPY) commands in the Dockerfile, the local directory path is relative to the build context where the Dockerfile is located.
In our example, the content is in the
content directory and the configuration files are in the conf directory, both subdirectories of the directory where the Dockerfile is located. The NGINX image includes default NGINX configuration files such as /etc/nginx/nginx.conf and /etc/nginx/conf.d/default.conf. Because we want to use the host configuration files instead, we include a RUN command that removes the default files:
FROM nginx RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf COPY content /usr/share/nginx/html COPY conf /etc/nginx
We create our own NGINX image by running the following command from the directory where the Dockerfile is located Is. Note the period (“.”) at the end of the command. Defines the current directory as the build context, which contains the Dockerfile and directories to copy.
# docker build -t mynginx_image1. Now we run this command to create a container named mynginx3
based on the image mynginx_image1: # docker run -name mynginx3
-p 80:80 -d mynginx_image1
If we want to make changes to the archive files, we use a helper container as described in option 3.
Option 3 – Keep files in the container
As mentioned in A Note About SSH, we can’t use SSH to access the NGINX container, so if we want to edit the content or configuration files directly, we have to create a helper container that has shell access. In order for the helper container to access the files, we must create a new image that has the appropriate Docker data volumes defined for the image. Assuming we want to copy files as in option 2 while also defining volumes, we use the following Dockerfile:
FROM nginx RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf COPY content /usr/share/nginx/html COPY conf /etc/nginx VOLUME /usr/share/nginx/html VOLUME /etc/nginx
Then we create the new NGINX image by running the following command (again note the endpoint):
# docker build -t mynginx_image2 .
Now we run this command to create an NGINX container (mynginx4) based on the image mynginx_image2: # docker run -name mynginx4 -p 80:80 -d mynginx_image2 Then we run the following command to start a helper container mynginx4_files that has a shell, allowing us to access the content and configuration directories of mynginx4
container we just created
: # docker run -i -t –volumes-from mynginx4 -name mynginx4_files debian /bin/bash root@b1cbbad63dd1:/#
The new mynginx4_files helper container runs in the foreground with a persistent standard input (the -i option) and a tty (the -t option). All volumes defined in mynginx4 are mounted as local directories in the auxiliary container.
The debian argument means that the helper container uses the Debian image from Docker Hub. Because the NGINX image also uses Debian (and all our examples so far use the NGINX image), it is more efficient to use Debian for the auxiliary container, rather than having Docker load another operating system. The /bin/bash argument means that the bash shell runs in the helper container, presenting a shell prompt that you can use to modify files as needed.
To start and stop the
container, run the following commands:
# docker start mynginx4_files # docker stop mynginx4_files
To exit the shell but leave it running, press Ctrl+p followed by Ctrl+q. To retrieve shell access to a running container, run this command:
# docker attach mynginx4_files To exit the
shell and terminate the container, run the exit command
. Registry Management You can configure default
or custom
logging. Using the
default log The NGINX image is configured to send the main NGINX
access and error logs to the Docker log collector by default. This is done by linking them to stdout and stderr respectively; all messages from both registries are written to the /var/lib/docker/containers/container-ID/container-ID-json file.log on the Docker host, where container-ID is the long-form ID that is returned when creating a container. For the initial container we created in Using the NGINX Open Source Docker Image, for example, it’s fcd1fb01b14557c7c9d991238f2558ae2704d129cf9fb97bb4fadf673a58580d.
To retrieve the container ID of an existing container, run this command, where container-name is the
value set by the -name parameter when the container is created (for the previous container ID, for example, it is mynginx1):
# docker inspect -format ‘{{ . Id }}’ container-name Although you can view the logs
by opening the container-ID-json file.log directly, it is usually easier to run this command:
# docker logs container-name
You can also use the Docker Engine API to extract the log messages, issuing a GET request against the Docker Unix socket. This command returns both the access log (represented by stdout=1) and the error log (stderr=1), but you can also request them individually:
curl -unix-socket /var/run/docker-sock http://localhost/containers/container-name/logs?stdout=1&stderr=1
For information about other query parameters, see the Docker Engine API documentation (search for “Get container logs” on that page).
Using the Registry
Custom If you want to implement another log collection method, or if you want to configure logging differently in certain configuration blocks (such as server{} and location{}), define a Docker volume for the directory or directories in which to store log files in the container, create a supporting container to access log files, and use the logging tools you want. To implement this, create a new image that contains the volume or volumes for the log files.
For example, to configure NGINX to store log files in /var/log/nginx/log, we can start with the Dockerfile from option 3 and simply add a VOLUME definition for this directory:
FROM nginx RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf COPY content /usr/share/nginx/html COPY conf /etc/nginx VOLUME /var/log/nginx/log
We can then create an image as described above and use it to create an NGINX container and a helper container that have access to the log directory. The auxiliary container may have the desired logging tools installed.
NGINX
Control
Since we do not have direct access to the NGINX container command line, we cannot use the nginx command to control NGINX. Fortunately, we can use signals to control NGINX, and Docker provides the kill command to send signals to a container.
To reload
the NGINX configuration, run this command: # docker kill -s HUP container-name To restart NGINX,
run this command to restart the container:
# docker restart container-name Deploying NGINX Plus with Docker So far we have discussed Docker for NGINX Open Source, but you can also use it with the commercial product,
NGINX Plus
. The difference is that you must first create an NGINX Plus image, because as a commercial offering, NGINX Plus is not available on Docker Hub. Fortunately, this is quite easy to do.
Note: Never upload your NGINX Plus images to a public repository such as Docker Hub. Doing so violates your license agreement.
Create an NGINX Plus
Docker
image
To generate an NGINX Plus image, first create a Dockerfile. The examples we provide here use Alpine Linux 3.14 and Debian 11 (Bullseye) as Docker base images. Before you can create the NGINX Plus Docker image, you must download its version from the nginx-repo.crt and nginx-repo.key files. NGINX Plus customers can find them in the customer portal; if you are doing a free trial of NGINX Plus, they were provided with your trial package. Copy the files to the directory where the Dockerfile (the Docker build context) is located.
As with NGINX
Open Source, by default, NGINX Plus access and error logs are linked to the Docker log collector. No volumes are specified, but you can add them if you want, or each Dockerfile can be used to create base images from which you can create new images with specified volumes, as described above.
By the way, we don’t specify a version of NGINX Plus
in the sample Dockerfile, so you don’t have to edit the file when upgrading to a new version of NGINX Plus. However, we have included annotated versions of the relevant instructions so that you can stop commenting if you want the file version to be specific.
Similarly, we have included (annotated) instructions that install official dynamic modules for NGINX Plus.
By default, no files are copied from the Docker host when a container is created. You can add COPY definitions to each Dockerfile, or the image you create can be used as the basis for another image as described above.
NGINX Plus Dockerfile (
Debian 11
) NGINX Plus Dockerfile (
Alpine Linux 3.14)
Creating the NGINX Plus Image
With the Dockerfile, nginx-repo.crt
and nginx-repo.key files in the same directory, run the following command there to create a Docker image named nginxplus (as before, note the end period):
# DOCKER_BUILDKIT=1 docker build -no-cache -t nginxplus -secret id=nginx-crt,src=your_cert_file -secret id=nginx-key,src=your_key_file .
The DOCKER_BUILDKIT=1 flag indicates that we are using Docker BuildKit to build the image, as required when the -secret option discussed below is included.
The -no-cache option tells Docker to create the image from scratch and guarantees the installation of the latest version of NGINX Plus. If the Dockerfile was previously used to create an image and does not include the -no-cache option, the new image uses the NGINX Plus version of the Docker cache. (As noted, we deliberately don’t specify a version of NGINX Plus in the Dockerfile so that the file doesn’t have to change in each new version of NGINX Plus.) Omit the -no-cache option if it is acceptable to use the NGINX Plus version of the previously created image.
The -secret option passes your NGINX Plus license certificate and key to the Docker build context without risking exposing the data or having the data persist between Docker build layers. The values of the id arguments cannot be changed without altering the base Dockerfile, but you must set the src arguments in the path to your NGINX Plus certificate and key files (the same directory where you are building the Docker image if you followed the instructions above).
Output like the following from Docker images The nginxplus command
indicates that the image was created successfully: # Docker images
nginxplus REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE nginxplus latest EF2BF65931CF 6 seconds ago 91.2 MB
To create a container named mynginxplus based on this image, run this command: # docker run -name mynginxplus
-p 80:80 -d nginxplus
You can control and manage NGINX Plus containers in the same way as Open Source NGINX containers.
Summary
NGINX
, NGINX Plus and Docker work very well together. Whether you use the open source NGINX image from Docker Hub or create your own NGINX Plus image, you can easily activate new NGINX and NGINX Plus instances in Docker containers and deploy them to your Kubernetes environment. You can also easily create new Docker images from the base images, making your containers even easier to control and manage. Ensure that all NGINX Plus instances running in your Docker containers are covered by your subscription. For more information, please contact the NGINX sales team.
There’s a lot more to Docker than we’ve been able to cover in this article. For more information, download our free O’Reilly eBook, Container Networking: From Docker to Kubernetes, or check out www.docker.com.
Related documentation
Deploying NGINX
and NGINX Plus on Docker