{"id":37008,"date":"2020-05-28T08:30:24","date_gmt":"2020-05-27T23:30:24","guid":{"rendered":"https:\/\/jirak.net\/wp\/deploying-nginx-and-nginx-plus-with-docker\/"},"modified":"2020-05-28T10:34:10","modified_gmt":"2020-05-28T01:34:10","slug":"deploying-nginx-and-nginx-plus-with-docker","status":"publish","type":"post","link":"https:\/\/jirak.net\/wp\/deploying-nginx-and-nginx-plus-with-docker\/","title":{"rendered":"Deploying NGINX and NGINX Plus with Docker"},"content":{"rendered":"<p>Deploying NGINX and NGINX Plus with Docker<\/p>\n<p>\n<\/p>\n<p><em><strong>Note<\/strong>: This post was updated in May&nbsp;2020 to make the Docker commands comply with current standards and to provide an updated NGINX&nbsp;Plus Dockerfile for Debian and Alpine Linux distributions.<\/em><\/p>\n<p><a target=\"_blank\" title=\"Docker\" href=\"https:\/\/www.docker.com\" rel=\"noopener noreferrer\">Docker<\/a>\u00a0is an\u00a0open platform for building, shipping, and running distributed applications as containers (lightweight, standalone, executable packages of software that include everything needed to run an application). Containers can in turn be deployed and orchestrated by container orchestration platforms such as Kubernetes. (In addition to the Docker container technology for NGINX Open Source and NGINX&nbsp;Plus discussed in this blog, NGINX provides the <a href=\"https:\/\/www.nginx.com\/products\/nginx\/kubernetes-ingress-controller\">NGINX Open Source and NGINX&nbsp;Plus Ingress Controllers for Kubernetes<\/a>; for NGINX&nbsp;Plus subscribers, support is included at no extra cost.)<\/p>\n<p>As software applications, NGINX Open Source and NGINX&nbsp;Plus are great use cases for Docker, and we publish an\u00a0<a target=\"_blank\" title=\"NGINX Image\" href=\"https:\/\/registry.hub.docker.com\/_\/nginx\/\" rel=\"noopener noreferrer\">NGINX Open Source image<\/a> on\u00a0<a title=\"Docker Hub Registry\" href=\"https:\/\/registry.hub.docker.com\" target=\"_blank\" rel=\"noopener noreferrer\">Docker&nbsp;Hub<\/a>, the repository of Docker images. This article describes how you can deploy NGINX Open Source using this image from Docker&nbsp;Hub, or create and deploy your own Docker image of NGINX&nbsp;Plus.<\/p>\n<h2>Introduction<\/h2>\n<p>The Docker open platform includes the Docker&nbsp;Engine&nbsp;&ndash;\u00a0the open source runtime that builds, runs, and orchestrates containers&nbsp;&ndash;\u00a0and Docker&nbsp;Hub, a hosted service where Dockerized applications are distributed, shared, and collaborated on by the entire development community or within the confines of a specific organization.<\/p>\n<p>Docker containers enable developers to focus their efforts on application \u201ccontent\u201d by separating applications from the constraints of infrastructure. Dockerized applications are instantly portable to any infrastructure&nbsp;&ndash;\u00a0laptop, bare&#8209;metal server, VM, or cloud&nbsp;&ndash;\u00a0making them modular components that can be readily assembled and reassembled into fully featured distributed applications and continuously innovated on in real time.<\/p>\n<p>For more information about Docker, see\u00a0<a target=\"_blank\" title=\"Why Docker?\" href=\"https:\/\/www.docker.com\/why-docker\" rel=\"noopener noreferrer\">Why Docker?<\/a> or the full <a target=\"_blank\" title=\"Docker documentation\" href=\"https:\/\/docs.docker.com\" rel=\"noopener noreferrer\">Docker\u00a0documentation<\/a>.<\/p>\n<h2>Using the NGINX Docker Image<\/h2>\n<p>You can create an NGINX instance in a Docker container using the <a target=\"_blank\" title=\"NGINX Docker Image\" href=\"https:\/\/registry.hub.docker.com\/_\/nginx\/\" rel=\"noopener noreferrer\">NGINX&nbsp;image<\/a> from Docker&nbsp;Hub.<\/p>\n<p>Let&#8217;s start with a very simple example. To launch\u00a0an instance of NGINX running in a container and using the default NGINX configuration, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker run --name mynginx1 -p 80:80 -d nginx<\/strong>\r\nfcd1fb01b14557c7c9d991238f2558ae2704d129cf9fb97bb4fadf673a58580d<\/code><\/pre>\n<p>This command creates a container named <strong>mynginx1<\/strong> based on the NGINX\u00a0image. The command returns the long form of the container ID, which is used in the name of log files; see <a href=\"#docker-logging\">Managing Logging<\/a>.<\/p>\n<p>The <span><code>-d<\/code> option<\/span> specifies that the container runs in detached mode, which means that it continues to run until stopped but does not respond to commands run on the command line. We discuss <a href=\"#working-with-oss\">later<\/a> how to interact with the container. <\/p>\n<p>The <span><code>-p<\/code> option<\/span> tells Docker to map the ports exposed in the container by the NGINX image&nbsp;&ndash; port&nbsp;80&nbsp;&ndash; to the specified port on the Docker host. The first parameter specifies the port in the Docker host, while the second parameter is mapped to the port exposed in the container.<\/p>\n<p>To verify that the container was created and is running, and to see the port mappings, we run <span><code>docker<\/code> <code>ps<\/code><\/span>. (We&#8217;ve split the output across multiple lines here to make it easier to read.)<\/p>\n<pre><code class=\"terminal\"># <strong>docker ps<\/strong>\r\nCONTAINER\u00a0ID\u00a0 IMAGE\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0  COMMAND\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 CREATED\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  STATUS\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ...  \r\nfcd1fb01b145  nginx:latest  \"nginx -g 'daemon of  16 seconds ago  Up 15 seconds ... \r\n\r\n    ... PORTS              NAMES\r\n    ... 0.0.0.0:80-&gt;80\/tcp mynginx1<\/code><\/pre>\n<p>The <code>PORTS<\/code> field in the output reports that port&nbsp;80 on the Docker host is mapped to port&nbsp;80 in 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:<\/p>\n<pre><code class=\"terminal\"># <strong>curl http:\/\/localhost:49167<\/strong>\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;title&gt;Welcome to nginx!&lt;\/title&gt;\r\n&lt;style&gt;\r\n body {\r\n width: 35em;\r\n margin: 0 auto;\r\n font-family: Tahoma, Verdana, Arial, sans-serif;\r\n }\r\n&lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt;Welcome to nginx!&lt;\/h1&gt;\r\n&lt;p&gt;If you see this page, the nginx web server is successfully installed and\r\nworking. Further configuration is required.&lt;\/p&gt;\r\n\r\n&lt;p&gt;For online documentation and support please refer to\r\n&lt;a href=\"http:\/\/nginx.org\/\"&gt;nginx.org&lt;\/a&gt;.&lt;br\/&gt;\r\nCommercial support is available at\r\n&lt;a href=\"https:\/\/www.nginx.com\/\"&gt;nginx.com&lt;\/a&gt;.&lt;\/p&gt;\r\n\r\n&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;\/em&gt;&lt;\/p&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n<h2 id=\"working-with-oss\">Working with the NGINX Docker Container<\/h2>\n<p>So now we have a working NGINX Docker container, but how do we manage the content and the NGINX configuration? And what about logging?<\/p>\n<h3 id=\"ssh\">A Note About SSH<\/h3>\n<p>It is common to enable SSH access to NGINX instances, but the NGINX image does not have OpenSSH installed, because Docker containers are generally intended to be for a single purpose (in this case running NGINX), and for normal operations there is no need to have shell access directly to the NGINX container. Instead we&#8217;ll use other methods supported by Docker. For a detailed discussion of alternatives to SSH access, see <a target=\"_blank\" title=\"Why you don't need SSHd in your Docker Containers\" href=\"https:\/\/blog.docker.com\/2014\/06\/why-you-dont-need-to-run-sshd-in-docker\/\" rel=\"noopener noreferrer\">Why You Don&#8217;t Need to Run SSHd in Your Docker Containers<\/a>.<\/p>\n<h3>Managing Content and Configuration Files<\/h3>\n<p>There are several ways you can manage both the content served by NGINX and the NGINX configuration files. Here we cover a few of the options.<\/p>\n<h4>Option 1&nbsp;&ndash; Maintain the Content and Configuration on the Docker Host<\/h4>\n<p>When the container is created we\u00a0can tell Docker to mount a local directory on the Docker host to a directory in the container. The NGINX image uses the default NGINX configuration, which uses <strong>\/usr\/share\/nginx\/html<\/strong> as the container&#8217;s root directory and puts configuration files in <strong>\/etc\/nginx<\/strong>. For a Docker host with content in the local directory <strong>\/var\/www<\/strong> and configuration files in <strong>\/var\/nginx\/conf<\/strong>, run this command (which appears on multiple lines here only for legibility):<\/p>\n<pre><code class=\"terminal\"># <strong>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<\/strong><\/code><\/pre>\n<p>Now any change made to the files in the local directories <strong>\/var\/www<\/strong> and <strong>\/var\/nginx\/conf<\/strong> on the Docker host are reflected in the directories <strong>\/usr\/share\/nginx\/html<\/strong> and <strong>\/etc\/nginx<\/strong> in the container. The <code>readonly<\/code>&nbsp;option means these directories can be changed only on the Docker host, not from within the container.<\/p>\n<h4>Option 2&nbsp;&ndash; Copy Files from the Docker Host<\/h4>\n<p>Another option is to have Docker copy the content and configuration files from a local directory on the Docker host during container creation. Once a\u00a0container is created, the files are maintained by creating a new container when files change or by modifying the files in the container. A simple way to copy the files is to create a <strong>Dockerfile<\/strong>\u00a0with commands that are run during generation of a new Docker image based on the NGINX image from Docker&nbsp;Hub. For the file&#8209;copy (<code>COPY<\/code>) commands in the <strong>Dockerfile<\/strong>, the local directory path is relative to the build context where the <strong>Dockerfile<\/strong>\u00a0is located.<\/p>\n<p>In our example, the content is in the <strong>content<\/strong> directory and the configuration files are in the <strong>conf<\/strong> directory, both subdirectories of the directory where the <strong>Dockerfile<\/strong> is located. The NGINX image includes default NGINX configuration files as <strong>\/etc\/nginx\/nginx.conf<\/strong> and <strong>\/etc\/nginx\/conf.d\/default.conf<\/strong>. Because we instead want to use the configuration files from the host, we include <code>RUN<\/code> commands that delete the default files:<\/p>\n<pre><code class=\"config\">FROM nginx\r\nRUN rm \/etc\/nginx\/nginx.conf \/etc\/nginx\/conf.d\/default.conf\r\nCOPY\u00a0content \/usr\/share\/nginx\/html\r\nCOPY\u00a0conf \/etc\/nginx<\/code><\/pre>\n<p>We create our own NGINX image by running the following command from the directory where the <strong>Dockerfile<\/strong> is located. Note the period (&#8220;.&#8221;) at the end of the command. It defines the current directory as the build context, which contains the <strong>Dockerfile<\/strong> and the directories to be copied.<\/p>\n<pre><code class=\"terminal\"># <strong>docker build -t mynginx_image1 .<\/strong><\/code><\/pre>\n<p>Now we run this command to create a container called <strong>mynginx3<\/strong> based on the <strong>mynginx_image1<\/strong> image:<\/p>\n<pre><code class=\"terminal\"># <strong>docker run --name mynginx3 -p 80:80 -d mynginx_image1<\/strong><\/code><\/pre>\n<p>If we want to make changes to the files in the container, we use a helper container as described in Option&nbsp;3.<\/p>\n<h4 id=\"option-3\">Option 3&nbsp;&ndash; Maintain Files in the Container<\/h4>\n<p>As mentioned in <a href=\"#ssh\">A Note About SSH<\/a>, we can&#8217;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\u00a0has shell access. For the helper container to have access to the files, we must create a new image that has the proper <a target=\"_blank\" title=\"Docker Volumes\" href=\"https:\/\/docs.docker.com\/storage\/volumes\/\" rel=\"noopener noreferrer\"><!-- Tony changed on 9-Apr-2020 after noticing that the former value, https:\/\/docs.docker.com\/engine\/tutorials\/dockervolumes\/, was redirecting to this -->Docker data volumes<\/a> defined for the image. Assuming we want to copy files as in Option&nbsp;2 while also defining volumes, we use the following <strong>Dockerfile<\/strong>:<\/p>\n<pre><code class=\"config\">FROM nginx\r\nCOPY content \/usr\/share\/nginx\/html\r\nCOPY conf \/etc\/nginx\r\nVOLUME \/usr\/share\/nginx\/html\r\nVOLUME \/etc\/nginx<\/code><\/pre>\n<p>We then create the new\u00a0NGINX image by running the following command (again note the final period):<\/p>\n<pre><code class=\"terminal\"># <strong>docker build -t mynginx_image2 .<\/strong><\/code><\/pre>\n<p>Now we run this command to create an NGINX container (<strong>mynginx4<\/strong>) based on the <strong>mynginx_image2<\/strong> image:<\/p>\n<pre><code class=\"terminal\"># <strong>docker run --name mynginx4 -p 80:80 -d mynginx_image2<\/strong><\/code><\/pre>\n<p>We then run the following command to start a helper container <strong>mynginx4_files<\/strong> that has a shell, enabling us to access the content and configuration directories of the <strong>mynginx4<\/strong> container we just created:<\/p>\n<pre><code class=\"terminal\"># <strong>docker run -i -t --volumes-from mynginx4 --name mynginx4_files debian \/bin\/bash<\/strong>\r\nroot@b1cbbad63dd1:\/#<\/code><\/pre>\n<p>The new\u00a0<strong>mynginx4_files<\/strong> helper container runs in the foreground with a\u00a0persistent standard input (the <span><code>-i<\/code> option<\/span>) and a tty (the <span><code>-t<\/code> option).<\/span> All volumes defined in <strong>mynginx4<\/strong>\u00a0are mounted as local directories in the helper container.<\/p>\n<p>The <code>debian<\/code>&nbsp;argument means that the helper container uses the Debian image\u00a0from Docker&nbsp;Hub. Because the NGINX image also uses Debian (and all of our examples so far use the NGINX image), it is most efficient to use Debian for the helper container, rather than having Docker load another operating system. The <code>\/bin\/bash<\/code>&nbsp;argument means that the <code>bash<\/code> shell runs in the helper container, presenting a shell prompt that you can use to modify files as needed.<\/p>\n<p>To start and stop the container, run the following commands:<\/p>\n<pre><code class=\"terminal\"># <strong>docker start mynginx4_files<\/strong>\r\n# <strong>docker stop mynginx4_files<\/strong><\/code><\/pre>\n<p>To exit the shell but leave the container running, press <code>Ctrl+p<\/code> followed by <code>Ctrl+q<\/code>. To regain shell access to a\u00a0running container, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker attach mynginx4_files<\/strong><\/code><\/pre>\n<p>To exit the shell and terminate the container, run the <code>exit<\/code> command.<\/p>\n<p><strong>Note:<\/strong> As an alternative to the preceding commands, you can run the following command to open an interactive shell to a running NGINX container. However, we recommend this only for advanced users.<\/p>\n<ul>\n<li>\n<p>On Debian systems:<\/p>\n<pre><code class=\"terminal\"># <strong>docker exec -it <em>NGINX_container_ID<\/em> bash<\/strong><\/code><\/pre>\n<\/li>\n<li>\n<p>On Alpine Linux systems:<\/p>\n<pre><code class=\"terminal\"># <strong>docker exec -it <em>NGINX_container_ID<\/em> sh<\/strong><\/code><\/pre>\n<\/li>\n<\/ul>\n<h3 id=\"docker-logging\">Managing Logging<\/h3>\n<p>You can configure either default or customized logging.<\/p>\n<h4>Using Default Logging<\/h4>\n<p>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 <code>stdout<\/code> and <code>stderr<\/code>; all messages from both logs\u00a0are then written to the file <span style=\"font-weight: bold\">\/var\/lib\/docker\/containers\/<em>container-ID<\/em>\/<em>container-ID<\/em>-json.log<\/span> on the Docker\u00a0host. The <strong><em>container&#8209;ID<\/em><\/strong> is the long&#8209;form ID returned when you create a container. To display it, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker inspect --format '{{ .Id }}' <em>container-name<\/em><\/strong><\/code><\/pre>\n<p>You can use both the Docker command line and the <a target=\"_blank\" title=\"Docker Engine API\" href=\"https:\/\/docs.docker.com\/engine\/api\/latest\/#operation\/ContainerLogs\" rel=\"noopener noreferrer\">Docker Engine API<\/a> to extract the log messages.<!-- Tony notes 4-Jan-2018: At the time of publication the URL for the actual API call was docs.docker.com\/reference\/api\/docker_remote_api_v1.14\/#get-container-logs. The current equivalent is docs.docker.com\/engine\/api\/v1.35\/#operation\/ContainerLogs. Substituting 'latest' for 'v1.35' redirects you to the most recent version of the API. Unfortunately, the #operation\/ContainerLogs hash is lost during the redirect, but I provide instructions on finding it in the paragraph following the 'curl' command. --> <\/p>\n<p>On the command line, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker logs <em>container-name<\/em><\/strong><\/code><\/pre>\n<p>To use the Docker Remote API, issue a <code>GET<\/code> request using the Docker Unix sock:<\/p>\n<pre><code class=\"config\">curl --unix-sock \/var\/run\/docker-sock http:\/\/localhost\/containers\/<em>container-name<\/em>\/logs?stdout=1&amp;stderr=1<\/code><\/pre>\n<p>To include only access log messages in the output, include only <code>stdout=1<\/code>; to limit the output to error log messages, include only <code>stderr=1<\/code>. To learn about other available options, see the <a target=\"_blank\" title=\"Docker documentation\" href=\"https:\/\/docs.docker.com\/engine\/api\/latest\/#operation\/ContainerLogs\" rel=\"noopener noreferrer\">Docker Engine API\u00a0documentation<\/a> (search for &#8220;Get container logs&#8221; on that page).<\/p>\n<h4>Using Customized Logging<\/h4>\n<p>If you want to implement another method of log collection, or if you want to configure logging differently in certain configuration blocks (such as <code>server{}<\/code> and <code>location{}<\/code>), define a Docker volume for the directory or directories in which to store the log files in the container, create a helper container to access the log files, and use whatever logging tools you like. To implement this, create a new image that contains the volume or volumes for the logging files.<\/p>\n<p>For example, to configure NGINX to store log files in <strong>\/var\/log\/nginx\/log<\/strong>,\u00a0we can start with the <strong>Dockerfile<\/strong> from <a href=\"#option-3\">Option&nbsp;3<\/a> and simply add a <code>VOLUME<\/code> definition for this directory:<\/p>\n<pre><code class=\"config\">FROM nginx\r\nCOPY content \/usr\/share\/nginx\/html\r\nCOPY conf \/etc\/nginx\r\nVOLUME \/var\/log\/nginx\/log<\/code><\/pre>\n<p>We\u00a0can then create an image as described above and use it to create an NGINX container and a helper container that have access to the logging directory. The helper container can have any desired logging tools installed.<\/p>\n<h3>Controlling NGINX<\/h3>\n<p>Since we do not have direct access to the command line of the NGINX container, we cannot use the <code>nginx<\/code> command to control NGINX. Fortunately we can use <a target=\"_blank\" title=\"Controlling NGINX\" href=\"https:\/\/nginx.org\/en\/docs\/control.html\" rel=\"noopener noreferrer\">signals<\/a> to control NGINX, and Docker provides the <code>kill<\/code> command for sending signals to a container.<\/p>\n<p>To reload the NGINX configuration, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker kill -s HUP <em>container-name<\/em><\/strong><\/code><\/pre>\n<p>To restart NGINX, run this command to restart the container:<\/p>\n<pre><code class=\"terminal\"># <strong>docker restart <em>container-name<\/em><\/strong><\/code><\/pre>\n<h2>Deploying NGINX&nbsp;Plus with Docker<\/h2>\n<p>So far we have discussed Docker for NGINX Open Source, but you can also use it with the commercial product, NGINX&nbsp;Plus. The difference is you first need to create an NGINX&nbsp;Plus image, because as a commercial offering NGINX&nbsp;Plus is not available at Docker&nbsp;Hub. Fortunately, this is quite easy to do.<\/p>\n<p><strong>Note<\/strong>: Never upload your NGINX&nbsp;Plus images to a public repository such as Docker&nbsp;Hub. Doing so violates your license agreement.<\/p>\n<h3>Creating a Docker Image of NGINX&nbsp;Plus<\/h3>\n<p>To generate\u00a0an NGINX&nbsp;Plus image, first create a <strong>Dockerfile<\/strong>. The examples we provide here use Debian&nbsp;10 (Buster) Alpine Linux&nbsp;3.11 as the base Docker image. Before you can create the NGINX&nbsp;Plus Docker image, you have to download your version of the <span style=\"font-weight:bold\">nginx-repo.crt<\/span> and <span style=\"font-weight:bold\">nginx-repo.key<\/span> files. NGINX&nbsp;Plus customers can find them at the customer portal, <strong><a target=\"_blank\" href=\"https:\/\/cs.nginx.com\" rel=\"noopener noreferrer\">https:\/\/cs.nginx.com<\/a><\/strong>; if you are doing a free trial of NGINX&nbsp;Plus, they were provided with your trial package. Copy the files to the directory where the <strong>Dockerfile<\/strong> is located (the Docker build context).<\/p>\n<p>As with NGINX Open Source, by default the NGINX&nbsp;Plus access and error logs are linked to the Docker log collector. No volumes are specified, but you can add them if desired, or each <strong>Dockerfile<\/strong> can be used to create base images from which you can create new images with volumes specified, as described previously.<\/p>\n<p>We purposely do not specify an NGINX Plus version in the sample Dockerfiles, so that you don&#8217;t have to edit the file when you update to a new release of NGINX&nbsp;Plus. We have, however, included commented versions of the relevant instructions for you to uncomment if you want to make the file version&#8209;specific.<\/p>\n<p>Similarly, we&#8217;ve included commented version of instructions that install <a href=\"https:\/\/www.nginx.com\/products\/nginx\/modules\/\">official dynamic modules for NGINX&nbsp;Plus<\/a>.<\/p>\n<p>By default, no files are copied from the Docker host as a container is created. You can add <code>COPY<\/code> definitions to each <strong>Dockerfile<\/strong>, or the image you create can be used as the basis for another image as described above.<\/p>\n<h4>NGINX Plus Dockerfile (Debian&nbsp;10)<\/h4>\n<p><code data-gist-id=\"36e97fc87efb5cf0039978c8e41a34b5\" data-gist-file=\"Dockerfile\" data-gist-line=\"1-76\"><\/code>&lt;!&#8211; [config scrolling=&quot;true&quot;]FROM debian:buster-slim<\/p>\n<p>LABEL maintainer=&quot;NGINX Docker Maintainers &#8221;<\/p>\n<p># Define NGINX versions for NGINX Plus and NGINX Plus modules<br \/>\n# Uncomment this block and the versioned nginxPackages block in the main RUN<br \/>\n# instruction to install a specific release<br \/>\n# ENV NGINX_VERSION   21<br \/>\n# ENV NJS_VERSION     0.3.9<br \/>\n# ENV PKG_RELEASE     1~buster<\/p>\n<p># Download certificate and key from the customer portal (https:\/\/cs.nginx.com)<br \/>\n# and copy to the build context<br \/>\nCOPY nginx-repo.crt \/etc\/ssl\/nginx\/<br \/>\nCOPY nginx-repo.key \/etc\/ssl\/nginx\/<\/p>\n<p>RUN set -x<br \/>\n# Create nginx user\/group first, to be consistent throughout Docker variants<br \/>\n    &amp;&amp; addgroup &#8211;system &#8211;gid 101 nginx<br \/>\n    &amp;&amp; adduser &#8211;system &#8211;disabled-login &#8211;ingroup nginx &#8211;no-create-home &#8211;home \/nonexistent &#8211;gecos &#8220;nginx user&#8221; &#8211;shell \/bin\/false &#8211;uid 101 nginx<br \/>\n    &amp;&amp; apt-get update<br \/>\n    &amp;&amp; apt-get install &#8211;no-install-recommends &#8211;no-install-suggests -y ca-certificates gnupg1<br \/>\n    &amp;&amp;<br \/>\n    NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62;<br \/>\n    found=&#8221;;<br \/>\n    for server in<br \/>\n        ha.pool.sks-keyservers.net<br \/>\n        hkp:\/\/keyserver.ubuntu.com:80<br \/>\n        hkp:\/\/p80.pool.sks-keyservers.net:80<br \/>\n        pgp.mit.edu<br \/>\n    ; do<br \/>\n        echo &#8220;Fetching GPG key $NGINX_GPGKEY from $server&#8221;;<br \/>\n        apt-key adv &#8211;keyserver &#8220;$server&#8221; &#8211;keyserver-options timeout=10 &#8211;recv-keys &#8220;$NGINX_GPGKEY&#8221; &amp;&amp; found=yes &amp;&amp; break;<br \/>\n    done;<br \/>\n    test -z &#8220;$found&#8221; &amp;&amp; echo &gt;&amp;2 &#8220;error: failed to fetch GPG key $NGINX_GPGKEY&#8221; &amp;&amp; exit 1;<br \/>\n    apt-get remove &#8211;purge &#8211;auto-remove -y gnupg1 &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*<br \/>\n# Install the latest release of NGINX Plus and\/or NGINX Plus modules<br \/>\n# Uncomment individual modules if necessary<br \/>\n# Use versioned packages over defaults to specify a release<br \/>\n    &amp;&amp; nginxPackages=&#8221;<br \/>\n        nginx-plus<br \/>\n        # nginx-plus=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-xslt<br \/>\n        # nginx-plus-module-xslt=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-geoip<br \/>\n        # nginx-plus-module-geoip=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-image-filter<br \/>\n        # nginx-plus-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-perl<br \/>\n        # nginx-plus-module-perl=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-njs<br \/>\n        # nginx-plus-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE}<br \/>\n    &#8221; <\/p>\n<p>    &amp;&amp; echo &#8220;Acquire::https::plus-pkgs.nginx.com::Verify-Peer &#8220;true&#8221;;&#8221; &gt;&gt; \/etc\/apt\/apt.conf.d\/90nginx<br \/>\n    &amp;&amp; echo &#8220;Acquire::https::plus-pkgs.nginx.com::Verify-Host &#8220;true&#8221;;&#8221; &gt;&gt; \/etc\/apt\/apt.conf.d\/90nginx<br \/>\n    &amp;&amp; echo &#8220;Acquire::https::plus-pkgs.nginx.com::SslCert     &#8220;\/etc\/ssl\/nginx\/nginx-repo.crt&#8221;;&#8221; &gt;&gt; \/etc\/apt\/apt.conf.d\/90nginx<br \/>\n    &amp;&amp; echo &#8220;Acquire::https::plus-pkgs.nginx.com::SslKey      &#8220;\/etc\/ssl\/nginx\/nginx-repo.key&#8221;;&#8221; &gt;&gt; \/etc\/apt\/apt.conf.d\/90nginx<br \/>\n    &amp;&amp; printf &#8220;deb https:\/\/plus-pkgs.nginx.com\/debian buster nginx-plusn&#8221; &gt; \/etc\/apt\/sources.list.d\/nginx-plus.list<br \/>\n    &amp;&amp; apt-get update<br \/>\n    &amp;&amp; apt-get install &#8211;no-install-recommends &#8211;no-install-suggests -y<br \/>\n                        $nginxPackages<br \/>\n                        gettext-base<br \/>\n                        curl<br \/>\n    &amp;&amp; apt-get remove &#8211;purge &#8211;auto-remove -y &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/* \/etc\/apt\/sources.list.d\/nginx-plus.list<br \/>\n    &amp;&amp; rm -rf \/etc\/apt\/apt.conf.d\/90nginx \/etc\/ssl\/nginx<\/p>\n<p># Forward request logs to Docker log collector<br \/>\nRUN ln -sf \/dev\/stdout \/var\/log\/nginx\/access.log<br \/>\n    &amp;&amp; ln -sf \/dev\/stderr \/var\/log\/nginx\/error.log<\/p>\n<p>EXPOSE 80<\/p>\n<p>STOPSIGNAL SIGTERM<\/p>\n<p>CMD [&#8220;nginx&#8221;, &#8220;-g&#8221;, &#8220;daemon off;&#8221;]<br \/>\n[\/config] &#8211;&gt;<\/p>\n<h4>NGINX Plus Dockerfile (Alpine Linux&nbsp;3.11)<\/h4>\n<p><code data-gist-id=\"36e97fc87efb5cf0039978c8e41a34b5\" data-gist-file=\"Dockerfile.alpine\" data-gist-line=\"1-83\"><\/code>&lt;!&#8211; [config scrolling=&quot;true&quot;]FROM alpine:3.11<\/p>\n<p>LABEL maintainer=&quot;NGINX Docker Maintainers &#8221;<\/p>\n<p># Define NGINX versions for NGINX Plus and NGINX Plus modules<br \/>\n# Uncomment this block and the versioned nginxPackages in the main RUN<br \/>\n# instruction to install a specific release<br \/>\n# ENV NGINX_VERSION 21<br \/>\n# ENV NJS_VERSION   0.3.9<br \/>\n# ENV PKG_RELEASE   1<\/p>\n<p># Download certificate and key from the customer portal (https:\/\/cs.nginx.com)<br \/>\n# and copy to the build context<br \/>\nCOPY nginx-repo.crt \/etc\/apk\/cert.pem<br \/>\nCOPY nginx-repo.key \/etc\/apk\/cert.key<\/p>\n<p>RUN set -x<br \/>\n# Create nginx user\/group first, to be consistent throughout Docker variants<br \/>\n    &amp;&amp; addgroup -g 101 -S nginx<br \/>\n    &amp;&amp; adduser -S -D -H -u 101 -h \/var\/cache\/nginx -s \/sbin\/nologin -G nginx -g nginx nginx<br \/>\n# Install the latest release of NGINX Plus and\/or NGINX Plus modules<br \/>\n# Uncomment individual modules if necessary<br \/>\n# Use versioned packages over defaults to specify a release<br \/>\n    &amp;&amp; nginxPackages=&#8221;<br \/>\n        nginx-plus<br \/>\n        # nginx-plus=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-xslt<br \/>\n        # nginx-plus-module-xslt=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-geoip<br \/>\n        # nginx-plus-module-geoip=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-image-filter<br \/>\n        # nginx-plus-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-perl<br \/>\n        # nginx-plus-module-perl=${NGINX_VERSION}-${PKG_RELEASE}<br \/>\n        # nginx-plus-module-njs<br \/>\n        # nginx-plus-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${PKG_RELEASE}<br \/>\n    &#8221;<br \/>\n    KEY_SHA512=&#8221;e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin&#8221;<br \/>\n    &amp;&amp; apk add &#8211;no-cache &#8211;virtual .cert-deps<br \/>\n        openssl<br \/>\n    &amp;&amp; wget -O \/tmp\/nginx_signing.rsa.pub https:\/\/nginx.org\/keys\/nginx_signing.rsa.pub<br \/>\n    &amp;&amp; if [ &#8220;$(openssl rsa -pubin -in \/tmp\/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)&#8221; = &#8220;$KEY_SHA512&#8221; ]; then<br \/>\n        echo &#8220;key verification succeeded!&#8221;;<br \/>\n        mv \/tmp\/nginx_signing.rsa.pub \/etc\/apk\/keys\/;<br \/>\n    else<br \/>\n        echo &#8220;key verification failed!&#8221;;<br \/>\n        exit 1;<br \/>\n    fi<br \/>\n    &amp;&amp; apk del .cert-deps<br \/>\n    &amp;&amp; apk add -X &#8220;https:\/\/plus-pkgs.nginx.com\/alpine\/v$(egrep -o &#8216;^[0-9]+.[0-9]+&#8217; \/etc\/alpine-release)\/main&#8221; &#8211;no-cache $nginxPackages<br \/>\n    &amp;&amp; if [ -n &#8220;\/etc\/apk\/keys\/nginx_signing.rsa.pub&#8221; ]; then rm -f \/etc\/apk\/keys\/nginx_signing.rsa.pub; fi<br \/>\n    &amp;&amp; if [ -n &#8220;\/etc\/apk\/cert.key&#8221; &amp;&amp; -n &#8220;\/etc\/apk\/cert.pem&#8221;]; then rm -f \/etc\/apk\/cert.key \/etc\/apk\/cert.pem; fi<br \/>\n# Bring in gettext so we can get `envsubst`, then throw<br \/>\n# the rest away. To do this, we need to install `gettext`<br \/>\n# then move `envsubst` out of the way so `gettext` can<br \/>\n# be deleted completely, then move `envsubst` back.<br \/>\n    &amp;&amp; apk add &#8211;no-cache &#8211;virtual .gettext gettext<br \/>\n    &amp;&amp; mv \/usr\/bin\/envsubst \/tmp\/ <\/p>\n<p>    &amp;&amp; runDeps=&#8221;$(<br \/>\n        scanelf &#8211;needed &#8211;nobanner \/tmp\/envsubst<br \/>\n            | awk &#8216;{ gsub(\/,\/, &#8220;nso:&#8221;, $2); print &#8220;so:&#8221; $2 }&#8217;<br \/>\n            | sort -u<br \/>\n            | xargs -r apk info &#8211;installed<br \/>\n            | sort -u<br \/>\n    )&#8221;<br \/>\n    &amp;&amp; apk add &#8211;no-cache $runDeps<br \/>\n    &amp;&amp; apk del .gettext<br \/>\n    &amp;&amp; mv \/tmp\/envsubst \/usr\/local\/bin\/<br \/>\n# Bring in tzdata so users could set the timezones through the environment<br \/>\n# variables<br \/>\n    &amp;&amp; apk add &#8211;no-cache tzdata<br \/>\n# Bring in curl and ca-certificates to make registering on DNS SD easier<br \/>\n    &amp;&amp; apk add &#8211;no-cache curl ca-certificates<br \/>\n# Forward request and error logs to Docker log collector<br \/>\n    &amp;&amp; ln -sf \/dev\/stdout \/var\/log\/nginx\/access.log<br \/>\n    &amp;&amp; ln -sf \/dev\/stderr \/var\/log\/nginx\/error.log<\/p>\n<p>EXPOSE 80<\/p>\n<p>STOPSIGNAL SIGTERM<\/p>\n<p>CMD [&#8220;nginx&#8221;, &#8220;-g&#8221;, &#8220;daemon off;&#8221;]&#8211;&gt;<\/p>\n<h4>Creating the NGINX&nbsp;Plus Image<\/h4>\n<p>With the\u00a0<strong>Dockerfile<\/strong>, <span style=\"font-weight:bold\">nginx-repo.crt<\/span>, and <span style=\"font-weight:bold\">nginx-repo.key<\/span> files in the same directory, run the following command there to create a Docker image called <strong>nginxplus<\/strong> (as before, note the final period):<\/p>\n<pre><code class=\"terminal\"># <strong>docker build --no-cache -t nginxplus .<\/strong><\/code><\/pre>\n<p>Note the <span><code>--no-cache<\/code> option<\/span>, which tells Docker to build the image from scratch and ensures the installation of the latest version of NGINX&nbsp;Plus. If the <strong>Dockerfile<\/strong> was previously used to build an image and you do not include the <span><code>--no-cache<\/code> option<\/span>, the new image uses the version of NGINX&nbsp;Plus from the Docker cache. (We purposely do not specify a version in the <strong>Dockerfile<\/strong> so that the file does not need to change at every new release of NGINX&nbsp;Plus.) Omit the <span><code>--no-cache<\/code> option<\/span> if it&#8217;s acceptable to use the NGINX&nbsp;Plus version from the previously built image.<\/p>\n<p>Output like the following from the  <span><code>docker<\/code> <code>images<\/code> <code>nginxplus<\/code><\/span> command indicates that the image was created successfully:<\/p>\n<pre><code class=\"terminal\"># <strong>docker images nginxplus<\/strong>\r\nREPOSITORY  TAG     IMAGE ID      CREATED        VIRTUAL SIZE\r\nnginxplus   latest  ef2bf65931cf  6 seconds ago  91.2 MB<\/code><\/pre>\n<p>To create a container named <strong>mynginxplus<\/strong> based on this image, run this command:<\/p>\n<pre><code class=\"terminal\"># <strong>docker run --name mynginxplus -p 80:80 -d nginxplus<\/strong><\/code><\/pre>\n<p>You can control and manage NGINX&nbsp;Plus containers in the same way as NGINX containers.<\/p>\n<h2>Summary<\/h2>\n<p>NGINX, NGINX&nbsp;Plus, and Docker work extremely well together. Whether you use the NGINX Open Source image from Docker&nbsp;Hub or create your own NGINX&nbsp;Plus image, you can easily spin up new instances of NGINX and NGINX&nbsp;Plus in Docker containers and deploy them in your Kubernetes environment. You can also easily create new Docker images from the base images, making your containers even easier to control and manage. Make sure that all NGINX&nbsp;Plus instances running in your Docker containers are covered by your subscription. For details, please <a href=\"https:\/\/www.nginx.com\/contact-sales\">contact the NGINX sales team<\/a>.<\/p>\n<p>There is much more to Docker than we have been able to cover in this article. For more information, download our free O&#8217;Reilly eBook&nbsp;&ndash; <a href=\"https:\/\/www.nginx.com\/resources\/library\/container-networking-docker-kubernetes\/\">Container Networking: From Docker to Kubernetes<\/a>&nbsp;&ndash; or check out\u00a0<a target=\"_blank\" title=\"Docker\" href=\"https:\/\/www.docker.com\" rel=\"noopener noreferrer\">www.docker.com<\/a>.<\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\/blog\/deploying-nginx-nginx-plus-docker\/\">Deploying NGINX and NGINX Plus with Docker<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\">NGINX<\/a>.<\/p>\n<p>Source: <a href=\"https:\/\/www.nginx.com\/blog\/deploying-nginx-nginx-plus-docker\/\" target=\"_blank\" rel=\"noopener noreferrer\">Deploying NGINX and NGINX Plus with Docker<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>Deploying NGINX and NGINX Plus with Docker Note: This post was updated in May&nbsp;2020 to make the Docker commands comply with current standards and to provide an updated NGINX&nbsp;Plus Dockerfile for Debian and Alpine Linux distributions. Docker\u00a0is an\u00a0open platform for building, shipping, and running distributed applications as containers (lightweight, standalone, executable packages of software that include everything needed to run an application). Containers can in turn be deployed and orchestrated by container orchestration platforms such as Kubernetes. (In addition to the Docker container technology for NGINX Open Source and NGINX&nbsp;Plus discussed in this blog, NGINX provides the NGINX Open Source and NGINX&nbsp;Plus Ingress Controllers for Kubernetes; for NGINX&nbsp;Plus subscribers, support is included at no extra cost.) As software applications, NGINX Open Source and NGINX&nbsp;Plus are great use cases for Docker, and we publish an\u00a0NGINX Open Source image on\u00a0Docker&nbsp;Hub, the repository <a class=\"mh-excerpt-more\" href=\"https:\/\/jirak.net\/wp\/deploying-nginx-and-nginx-plus-with-docker\/\" title=\"Deploying NGINX and NGINX Plus with Docker\">[ more&#8230; ]<\/a><\/p>\n<\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[169],"tags":[652],"class_list":["post-37008","post","type-post","status-publish","format-standard","hentry","category-news","tag-nginx"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37008","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/comments?post=37008"}],"version-history":[{"count":1,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37008\/revisions"}],"predecessor-version":[{"id":37009,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37008\/revisions\/37009"}],"wp:attachment":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/media?parent=37008"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/categories?post=37008"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/tags?post=37008"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}