浅谈本站nginx的Dockerfile
因为项目的更换,经常需要接触web环境的搭建。搭建的步骤基本上大同小异的,经常会因为测试和生产环境而重复进行两次同样的操作,在还原和误操作等方面也经常浪费许多时间。接触了docker之后,有一种相见恨晚的感觉,即使在开发的时候,也可以尽量少的污染到系统,软件依赖的问题也同时得到了解决。对于喜欢升级系统或软件的人来说,在折腾的路上,减少了许多头疼的事情。
在搭建本站的前期,去看了nginx官方的Dockerfile,其实是看不太懂的。以往搭建都是通过编译源码并且支持brotli压缩算法的形式进行的。因此,基于过往经验,编写了比较符合个人习惯的Dockerfile。
搭建nginx环境主要分为用户创建、编译及运行依赖安装、日志和运行目录创建并分配权限、nginx和openssl以及brotli下载、nginx编译的步骤。根据环境需求也可以省略某些操作。
载体系统和通用参数
FROM alpine:3.11
LABEL maintainer="kotomi@kotomiko.com"
ARG BUILD_ROOT=/usr/local/nginx
ARG CACHE_ROOT=/var/cache/nginx
ARG NGINX_VERSION=1.16.1
ARG OPENSSL_VERSION=1.1.1d
ARG OPENSSL_SHA256=1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2
FROM alpine:3.11
alpine是特意为docker而制作的操作系统,差不多什么都没有。因此大小只有5.61M,根据环境要求,按需做加法。很符合一个容器只做一件事的思想。系统内安装的都是运行时依赖,对整体大小的物尽其用。
FROM
是每个Dockerfile必须要有的语句,且通常都在首行,运行软件总是需要一个操作系统作为载体的。
ARG通用参数和ENV环境变量
ARG
相当于全局参数,能够被Dockerfile的后续语句使用。比如上述的ARG NGINX_VERSION=1.16.1
在之后都被${NGINX_VERSION}
所使用,以后更新nginx版本就可以很方便的修改。
在官方的Dockerfile中更常见的是ENV
。它在编写Dockerfile时与ARG
的作用是相同的。使用ENV
的写法如下.
ENV BUILD_ROOT /usr/local/nginx
ENV CACHE_ROOT /var/cache/nginx
ENV NGINX_VERSION 1.16.1
ENV OPENSSL_VERSION 1.1.1d
ENV OPENSSL_SHA256 1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2
它们的不同之处在于。
build Dockerfile的时候,
ARG
是可以被build语句中的参数覆盖的。docker build -t kotomiko/nginx:latest --build-arg NGINX_VERSION=1.18.0 .
此时编译过程中会下载1.18.0版本的nginx并编译。
ARG
在容器build结束后,就不再能被访问了。而ENV
则始终可以访问,在容器运行的时候依然可以,ENV
是被写入环境变量的。程序可以以访问环境变量的方式,访问Dockerfile中的ENV
。ENV
可以在运行容器的时候被覆盖。如果不存在则会被添加。docker build -t kotomiko/nginx:latest --env NGINX_VERSION=1.18.0 .
RUN命令构建软件
RUN命令整体上就是终端交互的那些命令的组合。通过安装编译期和运行期组件,创建用户,授权最终编辑nginx的一个流程。
安装编译依赖组件。使用alpine的包管理软件apk。
apk add --no-cache --virtual .build-deps \ git \ gcc \ libc-dev \ make \ pcre-dev \ zlib-dev \ perl-dev \ linux-headers \
安装运行依赖组件。分开管理编译与运行组件,在nginx的编译结束后,可以方便的删除掉编译依赖。
apk add --no-cache --virtual .nginx-rundeps \ pcre \
创建运行用户及用户组。禁止登录,不创建home目录
addgroup -g 109 -S nginx adduser -s /sbin/nologin -G nginx -S -D -H -u 109 nginx
目录所有者变更
touch /var/run/nginx/nginx.pid chown nginx:nginx /var/log/nginx chown nginx:nginx ${CACHE_ROOT} chown nginx:nginx /var/run/nginx/nginx.pid
源码下载
wget -S -O nginx.tar.gz https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz wget -S -O openssl.tar.gz https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz git clone https://github.com/google/ngx_brotli.git cd ngx_brotli git submodule update --init
nginx编译
BUILD_CONFIG="\ --prefix=/usr/local/nginx \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx/nginx.pid \ --lock-path=/var/run/nginx.lock \ --user=nginx \ --group=nginx \ --with-threads \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_gzip_static_module \ --http-log-path=/var/log/nginx/access.log \ --http-client-body-temp-path=${CACHE_ROOT}/client \ --http-proxy-temp-path=${CACHE_ROOT}/proxy \ --http-fastcgi-temp-path=${CACHE_ROOT}/fastcgi \ --http-uwsgi-temp-path=${CACHE_ROOT}/uwsgi \ --http-scgi-temp-path=${CACHE_ROOT}/scgi \ --add-module=${BUILD_ROOT}/ngx_brotli \ --with-openssl=${BUILD_ROOT}/openssl \ "\ && ./configure $BUILD_CONFIG \ && make \ && make install \
CMD容器启动命令
CMD ["nginx", "-g", "daemon off;"]
每次运行容器时,都会运行这个命令来启动nginx。切记一点。nginx不能以后台模式启动。Docker容器并非完全的虚拟机软件。它总是需要一个处于活动状态的进程来判断当前容器的状态的。当容器启动时,内部运行命令nginx -g daemon off;
此时会有一个/bin/sh
的进程处于激活状态。如果nginx以后台模式启动的话,/bin/sh
会在nginx启动后退出。docker容器也会跟随着退出。这适用于所有以Docker启动的软件。
构建Dockerfile
docker build -t kotomiko/nginx:latest .
通过docker build
命令构建Dockerfile-t
用于指定镜像的名字:latest
则为标签。.
用于表示当前目录,docker会在当前目录寻找Dockerfile并进行构建。 在构建完成后通过docker image ls
可以看到类似的输出
REPOSITORY TAG IMAGE ID CREATED SIZE
kotomiko/nginx latest fdfd9046e4d0 12 seconds ago 11MB
alpine 3.11 f70734b6a266 7 weeks ago 5.61MB
运行容器
docker run -d -p 80:80 kotomiko/nginx:latest
运行刚刚构建的image。-d
容器会在后台运行,-p 80:80
将容器的80端口映射到主机的80端口。此时访问http://localhost验证下是否成功。或是查看docker ps -a
输出的STATUS是否处于Up状态。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2977d7a642d3 kotomiko/nginx:latest "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp youthful_wing
此时一个容器就被创建出来。可以使用docker stop 2977d7a642d3
、docker restart 2977d7a642d3
和docker start 2977d7a642d3
对容器进行重启,停止等操作。2977d7a642d3
是容器的ID。
映射容器目录
虽然上面成功启动了一个容器,但我们修改nginx.conf很麻烦。应当将conf映射到容器中。通过修改主机目录的conf文件,来更改nginx的配置。
先把nginx.conf复制到主机上。
docker run --rm -i -t kotomiko/nginx:latest cat /etc/nginx/nginx.conf > /docker/nginx/nginx.conf
之后映射conf。后续可以通过修改主机上的nginx.conf更改容器中的nginx配置.
ocker run -d -p 80:80 -v /docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro kotomiko/nginx:latest
Dockerfile完整内容
FROM alpine:3.11
LABEL maintainer="kotomi@kotomiko.com"
ARG BUILD_ROOT=/usr/local/nginx
ARG CACHE_ROOT=/var/cache/nginx
ARG NGINX_VERSION=1.16.1
ARG OPENSSL_VERSION=1.1.1d
ARG OPENSSL_SHA256=1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2
RUN apk add --no-cache --virtual .build-deps \
git \
gcc \
libc-dev \
make \
pcre-dev \
zlib-dev \
perl-dev \
linux-headers \
&& apk add --no-cache --virtual .nginx-rundeps \
pcre \
&& addgroup -g 109 -S nginx \
&& adduser -s /sbin/nologin -G nginx -S -D -H -u 109 nginx\
&& mkdir /var/run/nginx \
&& mkdir /var/log/nginx \
&& mkdir -p ${CACHE_ROOT} \
&& mkdir -p ${BUILD_ROOT} \
&& touch /var/run/nginx/nginx.pid \
&& chown nginx:nginx /var/log/nginx \
&& chown nginx:nginx ${CACHE_ROOT} \
&& chown nginx:nginx /var/run/nginx/nginx.pid \
&& cd ${BUILD_ROOT} \
&& wget -S -O nginx.tar.gz https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \
&& wget -S -O openssl.tar.gz https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
&& echo "$OPENSSL_SHA256 *openssl.tar.gz" | sha256sum -c - \
&& git clone https://github.com/google/ngx_brotli.git \
&& cd ngx_brotli \
&& git submodule update --init \
&& cd .. \
&& mkdir nginx \
&& mkdir openssl \
&& tar --extract --file nginx.tar.gz --directory ./nginx --strip-components 1 \
&& tar --extract --file openssl.tar.gz --directory ./openssl --strip-components 1 \
&& BUILD_CONFIG="\
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/run/nginx.lock \
--user=nginx \
--group=nginx \
--with-threads \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_gzip_static_module \
--http-log-path=/var/log/nginx/access.log \
--http-client-body-temp-path=${CACHE_ROOT}/client \
--http-proxy-temp-path=${CACHE_ROOT}/proxy \
--http-fastcgi-temp-path=${CACHE_ROOT}/fastcgi \
--http-uwsgi-temp-path=${CACHE_ROOT}/uwsgi \
--http-scgi-temp-path=${CACHE_ROOT}/scgi \
--add-module=${BUILD_ROOT}/ngx_brotli \
--with-openssl=${BUILD_ROOT}/openssl \
"\
&& cd nginx \
&& ./configure $BUILD_CONFIG \
&& make \
&& make install \
&& strip /usr/sbin/nginx* \
&& cd .. \
&& rm -rf ${BUILD_ROOT} \
&& apk del .build-deps
CMD ["nginx", "-g", "daemon off;"]