使用 alpine 静态编译 angie (nginx fork)

选型

  • 为什么要用 Alpine ?
    因为 Alpine 是基于 musl 的发行版。 glibc 编译的静态二进制依然会依赖 glibc 动态库,用于 NSS 等功能,而 musl 不会。此外,alpine 软件源里自带了很多流行库的 static 版本。
  • 为什么是 angie ?
    angie 是 nginx 的一个 fork,开发者是原 nginx 项目的部分核心开发者,提供了一些 nginx 一直以来缺失的功能,并且把一部分 Nginx PLUS 的功能包含了进去。

安装依赖

apk add libtree build-base brotli-dev gd-dev geoip-dev hiredis-dev jansson-dev libmaxminddb-dev libxml2-dev libxslt-dev linux-headers openssl-dev pcre2-dev perl-dev pkgconf zeromq-dev zlib-dev zstd-dev !zstd-static luajit-dev gd perl perl-fcgi perl-io-socket-ssl perl-net-ssleay perl-protocol-websocket tzdata uwsgi-python3 openssl-dev pcre2-dev zlib-dev openssl-libs-static zlib-static libxslt-dev libxml2-dev libxslt-static libxml2-static gd-dev geoip-dev perl-dev libedit-dev pcre2-dev pcre2-static quickjs quickjs-static xz-static xz-dev zlib-dev zlib-static

可能依赖有漏的有多的,需要有空再整理一下。

由于我们要编译静态程序,所以依赖都是要同时安装 -dev-static 的。

configure

./configure --prefix=. --sbin-path=angie --modules-path=modules --conf-path=etc/nginx.conf --error-log-path=log/error.log --http-log-path=log/access.log --pid-path=run/nginx.pid --lock-path=run/nginx.lock --http-client-body-temp-path=cache/client_temp --http-proxy-temp-path=cache/proxy_temp --http-fastcgi-temp-path=cache/fastcgi_temp --http-uwsgi-temp-path=cache/uwsgi_temp --http-scgi-temp-path=cache/scgi_temp --user=root --group=root --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -static -lxml2 -llzma -lz' --with-http_xslt_module --add-module=../njs-0.9.4/nginx

大部分参数都是复制的 nginx docker 里的,去掉了我不需要的 mail 模块(这真的有人用吗)。注意添加静态编译额外的参数:

  • --with-ld-opt='-static' 指示编译器使用静态链接
  • -lxml2 -llzma -lz libxml2 依赖 lzma 和 libz 。 nginx 检查 libxml2 支持的时候,链接没有带上 lzma 和 lz。
    由于链接器需要先指定依赖树的根节点再指定叶子节点,光增加 -llzma -lz 没有用,需要先指定 -lxml2 才行
  • --add-module 增加一起编译的模块;这里加了 njs 引擎方便使用 js 相关功能

编译

make -j

编译结果

# libtree objs/angie
objs/angie
# file objs/angie
objs/angie: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, BuildID...

没有额外依赖,说明静态编译正确。

如果用 alpine 自己的 ldd 的话会输出一行 ld-musl 类似这样:

# ldd objs/angie
        /lib/ld-musl-x86_64.so.1 (0x...)

这是完全正常的,不代表文件是动态链接的。这是 ldd 的原理导致,因为我们编译的 PIE 文件,还是会有 .dynamic 区,这会导致 musl ldd 加载 ld 来试图解析动态依赖,从而产生输出结果。使用 glibc 的 ldd 检查就不会这样。参考 polarathene 在 runc 仓库的解释

此外,用 ldd 检查文件的依赖本身很危险,参考 ldd(1) and untrusted binaries (Julio Merino)。可以使用 libtree 作为替代。

本页面最后修改于 2025-11-5距今约 31 天

Created By 三三好记性不如烂 Wiki - 人工大脑CC BY-SA or CC BY-NC-SA 4.0