­

Arm64架構下靜態編譯Nginx

這段時間,我一直忙於將 Rainbond 源碼構建模塊移植到 Arm64/aarch64 架構中。這一源碼構建模塊可以將指定代碼倉庫中包含的源碼,拉取構建成為容器鏡像,在各種容器平台中運行。目前支持的源碼類型包括:Java(Maven、Gradle、jar、war)、Nodejs(前端Vue、後端項目)、Golang、Python、PHP、.NetCore、靜態Html。

Rainbond源碼構建簡介

Rainbond 源碼構建模塊由 builderrunner 兩個子模塊組成。

builder 負責將源碼進行編譯,並打包成為 Heroku 風格的 slug.tgz 包。slug.tgz 包中會包含編譯完成的產物(比如jar包、二進制等),以及編譯產物運行所需要的基礎環境(比如 jdk、tomcat、nginx、apache)。

runner 負責提供 slug.tgz 包運行的基礎環境。這是一個通用的基礎環境,不必再區分語言,無論何種語言生成的 slug.tgz 包都適用。

為何要編譯Nginx

Nginx 是靜態Html 、 Nodejs前端項目運行所使用的默認 Web-Server。builder 會在這兩種語言編譯完成後,前往 Rainbond 專用的雲端對象存儲中拉取 Nginx 安裝包。這種預編譯的安裝包,Nginx 官方只會提供 x86_64 版本。

在源碼構建模塊移植到 Arm64/aarch64 架構的過程中,我不可避免的要自己重新編譯 Arm 架構可用的 Nginx 預編譯安裝包。

為何要進行靜態編譯

最開始,我是希望走一些捷徑,Nginx 的核心是要有一個可執行二進制文件,那麼我是否可以從別處得到這種可執行文件。

Nginx 官方雖不提供我直接需要的 Arm64 預編譯安裝包,但是卻為各大操作系統發行版提供 Arm64 環境下的安裝源。對於 builder 所使用的 Ubuntu:14.04 操作系統而言,可以通過 apt install nginx 的方式安裝。然後我就可以得到我想要的可執行文件了。但是下面兩個問題的出現,阻斷了這條思路。

  • Ubuntu:14.04 源提供的 Nginx 版本過低。
  • 使用更高版本的 Ubuntu:18.04 安裝的 Nginx 版本可以滿足我的要求,但是提取到的可執行文件在 Ubuntu:14.04 無法運行,缺少必要的庫文件。

此時我意識到,由源安裝而來的 Nginx 是動態編譯版本,apt 等包管理工具會自動處理所需的依賴,然而我並不想要一點點嘗試我所缺少的庫都由哪些包安裝,這很耗神。

我希望這個可執行文件可以像 golang 語言編譯出的二進制文件一樣,將所有需要的庫都編譯到二進制中去,從而免除對操作系統的要求。理論上,這種方式得到的二進制在運行效率上也會更高。

簡單的查詢後,我了解到,我所需要的,是進行靜態編譯。

準備工作

閱讀 Nginx 官方提供的 源碼編譯文檔 了解到,我至少需要以下依賴需要處理:

  • PCRE(Perl Compatible Regular Expressions):基於 Perl 的正則表達式函數庫,Nginx 的 Core 、Rewrite 模塊需要它。pcre-8.44.tar.gz

  • zlib:小而美的壓縮庫,Nginx 的 Gzip 模塊需要它。zlib-1.2.11.tar.gz

  • OpenSSL:用於安全通信的工具包,非常著名,Nginx 所有和安全通信相關的模塊都需要它,比如Https。openssl-1.1.1l.tar.gz

我已經把它們的安裝包上傳到 Rainbond 官方對象存儲上,讀者若有需求,可以點擊下載。

進行編譯的硬件環境,是位於擁有 M1 芯片的 MacBookPro 筆記本 ,利用 Docker Desktop 啟動的 ubuntu:1404 容器。容器中預裝了 gccmake 軟件包。

編譯過程

解壓所有的依賴軟件包,以及 Nginx 的源碼包,所有源碼包都位於同級目錄下:

# 解壓已經下載好的依賴軟件包
$ tar xzf pcre-8.44.tar.gz
$ tar xzf zlib-1.2.11.tar.gz
$ tar xzf openssl-1.1.1l.tar.gz
# 下載並解壓 nginx stable 源碼包
$ wget //nginx.org/download/nginx-1.18.0.tar.gz
$ tar zxf nginx-1.18.0.tar.gz
$ cd nginx-1.18.0

執行 configure ,並指定靜態編譯參數:

$ ./configure \
--with-cc-opt='-static -static-libgcc' \
--with-ld-opt=-static \
--prefix=/app/nginx \
--with-http_ssl_module \
--with-openssl=../openssl-1.1.1l \
--with-pcre=../pcre-8.44 \
--with-zlib=../zlib-1.2.11

開始執行編譯:

$ make && make install

打包編譯出來的 Nginx 目錄即可:

$ tar czf nginx-1.18.0-arm64.tar.gz /app/nginx

驗證

查看編譯後產生的可執行文件,會發現該二進制文件的編譯類型為靜態類型,這樣的文件,可以在 arm64 架構下的任意 Linux 環境下運行。

$ file /usr/local/nginx/sbin/nginx
/usr/local/nginx/sbin/nginx: ELF 64-bit LSB  executable, ARM aarch64, version 1 (SYSV), statically linked, for GNU/Linux 3.7.0, BuildID[sha1]=66e5740a16bdfe6bc2f04c5371fd706ae7ca5395, not stripped