Skip to content

Commit 85ac2dd

Browse files
committed
Introduce Alpine slim image variant
And source entrypoint files with .envsh extension
1 parent 0ae08b9 commit 85ac2dd

14 files changed

+1137
-9
lines changed

.github/workflows/alpine-mainline.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,39 @@ jobs:
7676
context: "{{ defaultContext }}:mainline/alpine-perl"
7777
tags: nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}-alpine-perl, nginxinc/nginx-unprivileged:mainline-alpine-perl, nginxinc/nginx-unprivileged:1-alpine-perl, nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}-alpine-perl, nginxinc/nginx-unprivileged:alpine-perl
7878
push: true
79+
80+
slim:
81+
name: Build Alpine NGINX mainline slim Docker image
82+
runs-on: ubuntu-20.04
83+
strategy:
84+
fail-fast: false
85+
steps:
86+
- name: Check out the codebase
87+
uses: actions/checkout@v3
88+
89+
- name: Set up QEMU
90+
uses: docker/setup-qemu-action@v2
91+
92+
- name: Set up Docker Buildx
93+
uses: docker/setup-buildx-action@v2
94+
95+
- name: Login to Docker Hub
96+
uses: docker/login-action@v2
97+
with:
98+
username: ${{ secrets.DOCKERHUB_USERNAME }}
99+
password: ${{ secrets.DOCKERHUB_TOKEN }}
100+
101+
- name: Parse NGINX mainline version
102+
id: version
103+
run: |
104+
echo "::set-output name=major::$(cat mainline/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $1 }')"
105+
echo "::set-output name=minor::$(cat mainline/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $2 }')"
106+
echo "::set-output name=patch::$(cat mainline/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $3 }')"
107+
108+
- name: Build and push NGINX mainline slim Alpine image
109+
uses: docker/build-push-action@v3
110+
with:
111+
platforms: linux/amd64, linux/arm64
112+
context: "{{ defaultContext }}:mainline/alpine-slim"
113+
tags: nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}-alpine-slim, nginxinc/nginx-unprivileged:mainline-alpine-slim, nginxinc/nginx-unprivileged:1-alpine-slim, nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}-alpine-slim, nginxinc/nginx-unprivileged:alpine-slim
114+
push: true

.github/workflows/alpine-stable.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,39 @@ jobs:
7676
context: "{{ defaultContext }}:stable/alpine-perl"
7777
tags: nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}-alpine-perl, nginxinc/nginx-unprivileged:stable-alpine-perl, nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}-alpine-perl
7878
push: true
79+
80+
slim:
81+
name: Build Alpine NGINX stable slim Docker image
82+
runs-on: ubuntu-20.04
83+
strategy:
84+
fail-fast: false
85+
steps:
86+
- name: Check out the codebase
87+
uses: actions/checkout@v3
88+
89+
- name: Set up QEMU
90+
uses: docker/setup-qemu-action@v2
91+
92+
- name: Set up Docker Buildx
93+
uses: docker/setup-buildx-action@v2
94+
95+
- name: Login to Docker Hub
96+
uses: docker/login-action@v2
97+
with:
98+
username: ${{ secrets.DOCKERHUB_USERNAME }}
99+
password: ${{ secrets.DOCKERHUB_TOKEN }}
100+
101+
- name: Parse NGINX stable version
102+
id: version
103+
run: |
104+
echo "::set-output name=major::$(cat stable/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $1 }')"
105+
echo "::set-output name=minor::$(cat stable/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $2 }')"
106+
echo "::set-output name=patch::$(cat stable/alpine-slim/Dockerfile | awk '$1 == "ENV" && $2 == "NGINX_VERSION" { print $3 }' | awk -F. '{ print $3 }')"
107+
108+
- name: Build and push NGINX stable slim Alpine image
109+
uses: docker/build-push-action@v3
110+
with:
111+
platforms: linux/amd64, linux/arm64
112+
context: "{{ defaultContext }}:stable/alpine-slim"
113+
tags: nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }}-alpine-slim, nginxinc/nginx-unprivileged:stable-alpine-slim, nginxinc/nginx-unprivileged:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}-alpine-slim
114+
push: true

Dockerfile-alpine-slim.template

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
ARG IMAGE=alpine:%%ALPINE_VERSION%%
2+
FROM alpine:%%ALPINE_VERSION%%
3+
4+
LABEL maintainer="NGINX Docker Maintainers <[email protected]>"
5+
6+
ENV NGINX_VERSION %%NGINX_VERSION%%
7+
ENV PKG_RELEASE %%PKG_RELEASE%%
8+
9+
ARG UID=101
10+
ARG GID=101
11+
12+
RUN set -x \
13+
# create nginx user/group first, to be consistent throughout docker variants
14+
&& addgroup -g $GID -S nginx || true \
15+
&& adduser -S -D -H -u $UID -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx || true \
16+
&& apkArch="$(cat /etc/apk/arch)" \
17+
&& nginxPackages="%%PACKAGES%%
18+
" \
19+
# install prerequisites for public key and pkg-oss checks
20+
&& apk add --no-cache --virtual .checksum-deps \
21+
openssl \
22+
&& case "$apkArch" in \
23+
x86_64|aarch64) \
24+
# arches officially built by upstream
25+
set -x \
26+
&& KEY_SHA512="e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin" \
27+
&& wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \
28+
&& if [ "$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)" = "$KEY_SHA512" ]; then \
29+
echo "key verification succeeded!"; \
30+
mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \
31+
else \
32+
echo "key verification failed!"; \
33+
exit 1; \
34+
fi \
35+
&& apk add -X "%%PACKAGEREPO%%v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \
36+
;; \
37+
*) \
38+
# we're on an architecture upstream doesn't officially build for
39+
# let's build binaries from the published packaging sources
40+
set -x \
41+
&& tempDir="$(mktemp -d)" \
42+
&& chown nobody:nobody $tempDir \
43+
&& apk add --no-cache --virtual .build-deps \
44+
gcc \
45+
libc-dev \
46+
make \
47+
openssl-dev \
48+
pcre2-dev \
49+
zlib-dev \
50+
linux-headers \
51+
bash \
52+
alpine-sdk \
53+
findutils \
54+
&& su nobody -s /bin/sh -c " \
55+
export HOME=${tempDir} \
56+
&& cd ${tempDir} \
57+
&& curl -f -O https://hg.nginx.org/pkg-oss/archive/%%REVISION%%.tar.gz \
58+
&& PKGOSSCHECKSUM=\"%%PKGOSSCHECKSUM%% *%%REVISION%%.tar.gz\" \
59+
&& if [ \"\$(openssl sha512 -r %%REVISION%%.tar.gz)\" = \"\$PKGOSSCHECKSUM\" ]; then \
60+
echo \"pkg-oss tarball checksum verification succeeded!\"; \
61+
else \
62+
echo \"pkg-oss tarball checksum verification failed!\"; \
63+
exit 1; \
64+
fi \
65+
&& tar xzvf %%REVISION%%.tar.gz \
66+
&& cd pkg-oss-%%REVISION%% \
67+
&& cd alpine \
68+
&& make base \
69+
&& apk index -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \
70+
&& abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \
71+
" \
72+
&& cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \
73+
&& apk del .build-deps \
74+
&& apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \
75+
;; \
76+
esac \
77+
# remove checksum deps
78+
&& apk del .checksum-deps \
79+
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
80+
&& if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \
81+
&& if [ -n "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \
82+
&& if [ -n "/etc/apk/keys/nginx_signing.rsa.pub" ]; then rm -f /etc/apk/keys/nginx_signing.rsa.pub; fi \
83+
# Bring in gettext so we can get `envsubst`, then throw
84+
# the rest away. To do this, we need to install `gettext`
85+
# then move `envsubst` out of the way so `gettext` can
86+
# be deleted completely, then move `envsubst` back.
87+
&& apk add --no-cache --virtual .gettext gettext \
88+
&& mv /usr/bin/envsubst /tmp/ \
89+
\
90+
&& runDeps="$( \
91+
scanelf --needed --nobanner /tmp/envsubst \
92+
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
93+
| sort -u \
94+
| xargs -r apk info --installed \
95+
| sort -u \
96+
)" \
97+
&& apk add --no-cache $runDeps \
98+
&& apk del .gettext \
99+
&& mv /tmp/envsubst /usr/local/bin/ \
100+
# Bring in tzdata so users could set the timezones through the environment
101+
# variables
102+
&& apk add --no-cache tzdata \
103+
# forward request and error logs to docker log collector
104+
&& ln -sf /dev/stdout /var/log/nginx/access.log \
105+
&& ln -sf /dev/stderr /var/log/nginx/error.log \
106+
# create a docker-entrypoint.d directory
107+
&& mkdir /docker-entrypoint.d
108+
109+
# implement changes required to run NGINX as an unprivileged user
110+
RUN sed -i 's,listen 80;,listen 8080;,' /etc/nginx/conf.d/default.conf \
111+
&& sed -i '/user nginx;/d' /etc/nginx/nginx.conf \
112+
&& sed -i 's,/var/run/nginx.pid,/tmp/nginx.pid,' /etc/nginx/nginx.conf \
113+
&& sed -i "/^http {/a \ proxy_temp_path /tmp/proxy_temp;\n client_body_temp_path /tmp/client_temp;\n fastcgi_temp_path /tmp/fastcgi_temp;\n uwsgi_temp_path /tmp/uwsgi_temp;\n scgi_temp_path /tmp/scgi_temp;\n" /etc/nginx/nginx.conf \
114+
# nginx user must own the cache and etc directory to write cache and tweak the nginx config
115+
&& chown -R $UID:0 /var/cache/nginx \
116+
&& chmod -R g+w /var/cache/nginx \
117+
&& chown -R $UID:0 /etc/nginx \
118+
&& chmod -R g+w /etc/nginx
119+
120+
COPY docker-entrypoint.sh /
121+
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
122+
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
123+
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
124+
ENTRYPOINT ["/docker-entrypoint.sh"]
125+
126+
EXPOSE 8080
127+
128+
STOPSIGNAL SIGQUIT
129+
130+
USER $UID
131+
132+
CMD ["nginx", "-g", "daemon off;"]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/sh
2+
# vim:sw=4:ts=4:et
3+
4+
set -e
5+
6+
ME=$(basename $0)
7+
DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf"
8+
9+
# check if we have ipv6 available
10+
if [ ! -f "/proc/net/if_inet6" ]; then
11+
echo >&3 "$ME: info: ipv6 not available"
12+
exit 0
13+
fi
14+
15+
if [ ! -f "/$DEFAULT_CONF_FILE" ]; then
16+
echo >&3 "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist"
17+
exit 0
18+
fi
19+
20+
# check if the file can be modified, e.g. not on a r/o filesystem
21+
touch /$DEFAULT_CONF_FILE 2>/dev/null || { echo >&3 "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; }
22+
23+
# check if the file is already modified, e.g. on a container restart
24+
grep -q "listen \[::]\:8080;" /$DEFAULT_CONF_FILE && { echo >&3 "$ME: info: IPv6 listen already enabled"; exit 0; }
25+
26+
if [ -f "/etc/os-release" ]; then
27+
. /etc/os-release
28+
else
29+
echo >&3 "$ME: info: can not guess the operating system"
30+
exit 0
31+
fi
32+
33+
echo >&3 "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE"
34+
35+
case "$ID" in
36+
"debian")
37+
CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3)
38+
echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || {
39+
echo >&3 "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version"
40+
exit 0
41+
}
42+
;;
43+
"alpine")
44+
CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2)
45+
echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || {
46+
echo >&3 "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version"
47+
exit 0
48+
}
49+
;;
50+
*)
51+
echo >&3 "$ME: info: Unsupported distribution"
52+
exit 0
53+
;;
54+
esac
55+
56+
# enable ipv6 on default.conf listen sockets
57+
sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE
58+
59+
echo >&3 "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE"
60+
61+
exit 0
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
ME=$(basename $0)
6+
7+
auto_envsubst() {
8+
local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}"
9+
local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}"
10+
local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}"
11+
12+
local template defined_envs relative_path output_path subdir
13+
defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
14+
[ -d "$template_dir" ] || return 0
15+
if [ ! -w "$output_dir" ]; then
16+
echo >&3 "$ME: ERROR: $template_dir exists, but $output_dir is not writable"
17+
return 0
18+
fi
19+
find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do
20+
relative_path="${template#$template_dir/}"
21+
output_path="$output_dir/${relative_path%$suffix}"
22+
subdir=$(dirname "$relative_path")
23+
# create a subdirectory where the template file exists
24+
mkdir -p "$output_dir/$subdir"
25+
echo >&3 "$ME: Running envsubst on $template to $output_path"
26+
envsubst "$defined_envs" < "$template" > "$output_path"
27+
done
28+
}
29+
30+
auto_envsubst
31+
32+
exit 0

0 commit comments

Comments
 (0)