Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Building a Custom Alpine-Based LNP Stack Docker Image

Notes May 8 4

To create a lightweight Docker image based on Alpine Linux that includes Nginx and PHP-FPM (an LNP stack), start by defining a base PHP layer.

For compatibility with legacy applications, use PHP 5.6 on Alpine 3.3:

# php5.dockerfile
FROM alpine:3.3

ENV TIMEZONE=Asia/Shanghai \
    PHP_MEMORY_LIMIT=512M \
    MAX_UPLOAD=50M \
    PHP_MAX_FILE_UPLOAD=200 \
    PHP_MAX_POST=100M

RUN echo "http://dl-4.alpinelinux.org/alpine/v3.3/main" > /etc/apk/repositories && \
    apk update && \
    apk add tzdata && \
    cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && \
    echo "${TIMEZONE}" > /etc/timezone && \
    apk add \
        php-intl php-mcrypt php-openssl php-gmp php-json php-dom \
        php-pdo php-zip php-zlib php-mysqli php-bcmath php-gd \
        php-xcache php-pdo_mysql php-gettext php-xmlreader php-xmlrpc \
        php-bz2 php-memcache php-iconv php-curl php-ctype php-fpm \
        php-phar php && \
    curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer && \
    sed -i 's/;daemonize\s*=\s*yes/daemonize = no/g' /etc/php/php-fpm.conf && \
    sed -i 's/listen\s*=\s*127.0.0.1:9000/listen = 9000/g' /etc/php/php-fpm.conf && \
    sed -i "s|;date.timezone =.*|date.timezone = ${TIMEZONE}|" /etc/php/php.ini && \
    sed -i "s|memory_limit =.*|memory_limit = ${PHP_MEMORY_LIMIT}|" /etc/php/php.ini && \
    sed -i "s|upload_max_filesize =.*|upload_max_filesize = ${MAX_UPLOAD}|" /etc/php/php.ini && \
    sed -i "s|max_file_uploads =.*|max_file_uploads = ${PHP_MAX_FILE_UPLOAD}|" /etc/php/php.ini && \
    sed -i "s|post_max_size =.*|post_max_size = ${PHP_MAX_POST}|" /etc/php/php.ini && \
    sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/' /etc/php/php.ini && \
    mkdir -p /www && \
    apk del tzdata && \
    rm -rf /var/cache/apk/*

Build this as the foundational image:

docker build -t alpine-lnp-base:v1 -f php5.dockerfile .

Next, extend this base to include Nginx and process supervision using Supervisor:

# nginx.dockerfile
FROM alpine-lnp-base:v1

ENV TIME_ZONE=Asia/Shanghai

RUN mkdir -p /opt/www /var/log/www && \
    sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
    apk update && \
    apk add nginx supervisor

COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf
COPY supervisord.conf /etc/supervisord.conf

CMD ["supervisord", "-c", "/etc/supervisord.conf"]

The main Nginx configuration (nginx.conf) enables performance tuning and logging:

worker_processes auto;
error_log /var/log/nginx/error.log;
events {
    worker_connections 65535;
    use epoll;
}
http {
    include mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off;
    keepalive_timeout 120;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_types text/plain text/css application/json application/javascript text/xml application/rss+xml image/svg+xml;
    gzip_vary on;
    client_max_body_size 50m;
    client_body_buffer_size 256k;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    include conf.d/*.conf;
}

The site-specific configuration (default.conf) sets up PHP handling via FastCGI:

server {
    listen 80;
    server_name localhost;
    root /opt/www;
    index index.html index.php;
    charset utf-8;
    error_log /var/log/www/error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Supervisor manages both Nginx and PHP-FPM processes in the foreground:

[supervisord]
nodaemon=true

[program:php-fpm]
command=php-fpm
autostart=true
autorestart=true
stdout_events_enabled=true
stderr_events_enabled=true

[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
startsecs=5

[program:crond]
command=crond -f
autostart=true
autorestart=true

Build the final image:

docker build -t alpine-lnp:v1 -f nginx.dockerfile .

Run the container with port 80 exposed:

docker run -d -p 80:80 --name lnp-container alpine-lnp:v1

Access the runing container for debugging if needed:

docker exec -it lnp-container sh

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.