Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Package: Alwatr Accelerated Nginx Container #221

Merged
merged 21 commits into from
Sep 4, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/container/nginx/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!conf/
30 changes: 30 additions & 0 deletions packages/container/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ARG NGINX_VERSION=1.23
ARG NGINX_IMAGE=docker.io/nginx:${NGINX_VERSION}-alpine

FROM $NGINX_IMAGE

# Default enviroment for nginx template
alimd marked this conversation as resolved.
Show resolved Hide resolved
ENV NGINX_ERROR_LOG_LEVEL=notice \
NGINX_ACCESS_LOG="/var/log/nginx/access.log json" \
NGINX_WORKER_CONNECTIONS=2048 \
NGINX_CLIENT_MAX_BODY_SIZE=10m \
NGINX_SENDFILE=on \
NGINX_TCP_NOPUSH=off \
NGINX_TCP_NODELAY=off \
NGINX_OPEN_FILE_CACHE_VALID=5m \
NGINX_EXPIRES_HTML=epoch \
NGINX_EXPIRES_STATIC=max \
NGINX_EXPIRES_DEFAULT=5m \
NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=1

RUN mkdir -p /var/www/html

COPY html/ /var/www/

# All nginx configuration include entrypoints
COPY conf/ /etc/nginx/

EXPOSE 80
ENTRYPOINT ["/etc/nginx/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -fso /dev/null http://localhost/ || exit 1
7 changes: 7 additions & 0 deletions packages/container/nginx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @alwatr/nginx - Alwatr Accelerated Web Server

High performance, optimized NGINX for server web applications and api proxy with fast cache.

## Cloud Native Application Best Practices

The right way of using `@alwatr/nginx` is behind kubernetes ingress or simple edge reverse-proxy, then don't config edge stuff like gzip compression, ssl, etc or even config domain or multi websites!
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/sh
# vim:sw=2:ts=2:sts=2:et
alimd marked this conversation as resolved.
Show resolved Hide resolved

set -e

ME=$(basename $0)

auto_envsubst() {
local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}"
local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}"
local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}"

local template defined_envs relative_path output_path subdir
defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
[ -d "$template_dir" ] || return 0
if [ ! -w "$output_dir" ]; then
echo >&3 "$ME: ERROR: $template_dir exists, but $output_dir is not writable"
return 0
fi
find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do
relative_path="${template#$template_dir/}"
output_path="$output_dir/${relative_path%$suffix}"
subdir=$(dirname "$relative_path")
# create a subdirectory where the template file exists
mkdir -p "$output_dir/$subdir"
echo >&3 "$ME: Running envsubst on $template to $output_path"
envsubst "$defined_envs" < "$template" > "$output_path"
done
}

auto_envsubst

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/bin/sh
# vim:sw=2:ts=2:sts=2:et

set -eu

LC_ALL=C
ME=$( basename "$0" )
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0

touch /etc/nginx/nginx.conf 2>/dev/null || { echo >&2 "$ME: error: can not modify /etc/nginx/nginx.conf (read-only file system?)"; exit 0; }

ceildiv() {
num=$1
div=$2
echo $(( (num + div - 1) / div ))
}

get_cpuset() {
cpusetroot=$1
cpusetfile=$2
ncpu=0
[ -f "$cpusetroot/$cpusetfile" ] || return 1
for token in $( tr ',' ' ' < "$cpusetroot/$cpusetfile" ); do
case "$token" in
*-*)
count=$( seq $(echo "$token" | tr '-' ' ') | wc -l )
ncpu=$(( ncpu+count ))
;;
*)
ncpu=$(( ncpu+1 ))
;;
esac
done
echo "$ncpu"
}

get_quota() {
cpuroot=$1
ncpu=0
[ -f "$cpuroot/cpu.cfs_quota_us" ] || return 1
[ -f "$cpuroot/cpu.cfs_period_us" ] || return 1
cfs_quota=$( cat "$cpuroot/cpu.cfs_quota_us" )
cfs_period=$( cat "$cpuroot/cpu.cfs_period_us" )
[ "$cfs_quota" = "-1" ] && return 1
[ "$cfs_period" = "0" ] && return 1
ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
[ "$ncpu" -gt 0 ] || return 1
echo "$ncpu"
}

get_quota_v2() {
cpuroot=$1
ncpu=0
[ -f "$cpuroot/cpu.max" ] || return 1
cfs_quota=$( cut -d' ' -f 1 < "$cpuroot/cpu.max" )
cfs_period=$( cut -d' ' -f 2 < "$cpuroot/cpu.max" )
[ "$cfs_quota" = "max" ] && return 1
[ "$cfs_period" = "0" ] && return 1
ncpu=$( ceildiv "$cfs_quota" "$cfs_period" )
[ "$ncpu" -gt 0 ] || return 1
echo "$ncpu"
}

get_cgroup_v1_path() {
needle=$1
found=
foundroot=
mountpoint=

[ -r "/proc/self/mountinfo" ] || return 1
[ -r "/proc/self/cgroup" ] || return 1

while IFS= read -r line; do
case "$needle" in
"cpuset")
case "$line" in
*cpuset*)
found=$( echo "$line" | cut -d ' ' -f 4,5 )
break
;;
esac
;;
"cpu")
case "$line" in
*cpuset*)
;;
*cpu,cpuacct*|*cpuacct,cpu|*cpuacct*|*cpu*)
found=$( echo "$line" | cut -d ' ' -f 4,5 )
break
;;
esac
esac
done << __EOF__
$( grep -F -- '- cgroup ' /proc/self/mountinfo )
__EOF__

while IFS= read -r line; do
controller=$( echo "$line" | cut -d: -f 2 )
case "$needle" in
"cpuset")
case "$controller" in
cpuset)
mountpoint=$( echo "$line" | cut -d: -f 3 )
break
;;
esac
;;
"cpu")
case "$controller" in
cpu,cpuacct|cpuacct,cpu|cpuacct|cpu)
mountpoint=$( echo "$line" | cut -d: -f 3 )
break
;;
esac
;;
esac
done << __EOF__
$( grep -F -- 'cpu' /proc/self/cgroup )
__EOF__

case "${found%% *}" in
"/")
foundroot="${found##* }$mountpoint"
;;
"$mountpoint")
foundroot="${found##* }"
;;
esac
echo "$foundroot"
}

get_cgroup_v2_path() {
found=
foundroot=
mountpoint=

[ -r "/proc/self/mountinfo" ] || return 1
[ -r "/proc/self/cgroup" ] || return 1

while IFS= read -r line; do
found=$( echo "$line" | cut -d ' ' -f 4,5 )
done << __EOF__
$( grep -F -- '- cgroup2 ' /proc/self/mountinfo )
__EOF__

while IFS= read -r line; do
mountpoint=$( echo "$line" | cut -d: -f 3 )
done << __EOF__
$( grep -F -- '0::' /proc/self/cgroup )
__EOF__

case "${found%% *}" in
"")
return 1
;;
"/")
foundroot="${found##* }$mountpoint"
;;
"$mountpoint")
foundroot="${found##* }"
;;
esac
echo "$foundroot"
}

ncpu_online=$( getconf _NPROCESSORS_ONLN )
ncpu_cpuset=
ncpu_quota=
ncpu_cpuset_v2=
ncpu_quota_v2=

cpuset=$( get_cgroup_v1_path "cpuset" ) && ncpu_cpuset=$( get_cpuset "$cpuset" "cpuset.effective_cpus" ) || ncpu_cpuset=$ncpu_online
cpu=$( get_cgroup_v1_path "cpu" ) && ncpu_quota=$( get_quota "$cpu" ) || ncpu_quota=$ncpu_online
cgroup_v2=$( get_cgroup_v2_path ) && ncpu_cpuset_v2=$( get_cpuset "$cgroup_v2" "cpuset.cpus.effective" ) || ncpu_cpuset_v2=$ncpu_online
cgroup_v2=$( get_cgroup_v2_path ) && ncpu_quota_v2=$( get_quota_v2 "$cgroup_v2" ) || ncpu_quota_v2=$ncpu_online

ncpu=$( printf "%s\n%s\n%s\n%s\n%s\n" \
"$ncpu_online" \
"$ncpu_cpuset" \
"$ncpu_quota" \
"$ncpu_cpuset_v2" \
"$ncpu_quota_v2" \
| sort -n \
| head -n 1 )

sed -i.bak -r 's/^(worker_processes)(.*)$/# Commented out by '"$ME"' on '"$(date)"'\n#\1\2\n\1 '"$ncpu"';/' /etc/nginx/nginx.conf
38 changes: 38 additions & 0 deletions packages/container/nginx/conf/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
# vim:sw=4:ts=4:et

set -e

if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
exec 3>&1
else
exec 3>/dev/null
fi

if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
if /usr/bin/find "/entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
echo >&3 "$0: /entrypoint.d/ is not empty, will attempt to perform configuration"

echo >&3 "$0: Looking for shell scripts in /entrypoint.d/"
find "/entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do
case "$f" in
*.sh)
if [ -x "$f" ]; then
echo >&3 "$0: Launching $f";
"$f"
else
# warn on shell scripts without exec bit
echo >&3 "$0: Ignoring $f, not executable";
fi
;;
*) echo >&3 "$0: Ignoring $f";;
esac
done

echo >&3 "$0: Configuration complete; ready for start up"
else
echo >&3 "$0: No files found in /entrypoint.d/, skipping configuration"
fi
fi

exec "$@"
9 changes: 9 additions & 0 deletions packages/container/nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Configuration File - Nginx Server Configs
# http://nginx.org/en/docs/dirindex.html

# Sets the worker threads to the number of CPU cores available in the system for best performance.
# Set NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=1 to enable auto-tuning.
# Maximum number of connections = worker_processes * worker_connections
worker_processes auto;

include /etc/nginx/conf.d/*.conf;
25 changes: 25 additions & 0 deletions packages/container/nginx/conf/templates/00-main.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Run as a unique, less privileged user for security reasons.
# user www-data www-data; # user group
user nginx;

# The file storing the process ID of the main process
pid /var/run/nginx.pid;

# Reduces timer resolution in worker processes, thus reducing the number of gettimeofday() system calls made. By default, gettimeofday() is called each time a kernel event is received. With reduced resolution, gettimeofday() is only called once per specified interval.
timer_resolution 100ms;

# Defines named thread pools used for multi-threaded reading and sending of files without blocking worker processes.
thread_pool default threads=32 max_queue=65536;

events {
# Specifies the connection processing method to use (https://nginx.org/en/docs/events.html).
# There is normally no need to specify it explicitly, because nginx will by default use the most efficient method.
# use epoll;

# Sets the maximum number of simultaneous connections that can be opened by a worker process.
# Should be < worker_rlimit_nofile.
worker_connections $NGINX_WORKER_CONNECTIONS;

# accept as many connections as possible, may flood worker connections if set too low -- for testing environment
# multi_accept on;
}
3 changes: 3 additions & 0 deletions packages/container/nginx/conf/templates/10-http.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
http {
include /etc/nginx/conf.d/http/*.conf;
}
17 changes: 17 additions & 0 deletions packages/container/nginx/conf/templates/http/00-main.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
server_tokens off;

index index.html index.htm;

tcp_nopush $NGINX_TCP_NOPUSH;
tcp_nodelay $NGINX_TCP_NODELAY;
sendfile $NGINX_SENDFILE;
sendfile_max_chunk 512k;

# fix dns for docker and ssl
resolver 127.0.0.11 ipv6=off valid=10s;
resolver_timeout 5s;
#resolver 1.1.1.1 8.8.8.8 8.8.4.4 valid=60s;
#resolver_timeout 15s;

# Sets the maximum allowed size of the client request body
client_max_body_size NGINX_CLIENT_MAX_BODY_SIZE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#real_ip_header X-Real-IP;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
set_real_ip_from 172.0.0.0/10;
set_real_ip_from 10.0.0.0/10;
29 changes: 29 additions & 0 deletions packages/container/nginx/conf/templates/http/20-log.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$upstream_response_time "$upstream_cache_status"';

log_format json escape=json '{'
'"time_local": "$time_local", '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"request": "$request", '
'"status": "$status", '
'"body_bytes_sent": "$body_bytes_sent", '
'"http_referer": "$http_referer", '
'"http_user_agent": "$http_user_agent" ,'
'"http_x_forwarded_for": "$http_x_forwarded_for", '
'"request_method": "$request_method", '
'"request_time": "$request_time", '
'"host": "$host", '
'"server_name": "$server_name"'
'"upstream_addr": "$upstream_addr", '
'"upstream_status": "$upstream_status", '
'"upstream_response_time": "$upstream_response_time", '
'"upstream_response_length": "$upstream_response_length", '
'"upstream_cache_status": "$upstream_cache_status"'
'}';

error_log /var/log/nginx/error.log $NGINX_ERROR_LOG_LEVEL;

access_log $NGINX_ACCESS_LOG;
Loading