支持HTTP2的cURL——基於Alpine的最小化Docker鏡像


cURL是我喜歡的開源軟件之一。雖然cURL的強大常常被認為是理所當然的,但我真心地認為它值得感謝和尊重。如果我們的工具箱失去了curl,那些需要和網絡重度交互的人(我們大多數人都是這樣的)將會陷入到困境中。curl速度快、體積小,並且和大多數好工具一樣,簡潔干凈,盡量不影響用戶,只做它們需要做的事情。

如果有人想使用curl中的一種功能(比如UNIX套接字支持),而恰恰系統提供的包的配置不支持這種功能,或者包版本太老而不包含這種功能,由於curl享有“數據傳輸的瑞士軍刀”的美譽,可以想象這種情況是有可能發生的。因此你會發現,你想要編譯一個包含你所需要的功能的curl。編譯自己的軟件是令人害怕的,尤其是當你不擅長解決這類問題時,讓我們先暫時拋開這個事實,自定義你所使用的軟件將會是一次難以置信的、解放自身的體驗。

如果需要,你可以編譯自己的軟件,並且不受限於其它人交給你的軟件包,明確這一點將會給你帶來自信。突然間,你腦中可能會充斥着興奮的快感——你能夠按照你喜歡的方式安裝和配置任何軟件,並不需要接受其它人配置中的限制。人類常常喜歡給一樣東西加上自己的標記,這就是使用開源軟件的令人上癮的原因之一。

由於Docker文件系統的分離特性,它成為了完成這類事情最佳的選擇。你並不需要擔心安裝依賴庫或者運行一個糟糕的make install類似的命令,會將你的本地文件系統弄亂。它能夠讓你在真實的環境中進行操作,並且允許你犯錯。這對於學習知識是一個極佳的工具,因為將事情完全弄糟是學習的必經之路,並且在容器中將事情弄糟,你能夠輕易地將這些容器丟棄,這種方式比弄糟自己的本地系統要安全多了。除此之外,如果你將這些步驟以腳本的形式寫入到Dockerfile中,那么在后續重新構建時,構建步驟將保持一致。盡管Dockerfile不能100%保證構建的結果,但這總比README中的隨意描述好多了。

接下來讓我們一起構建一個Dockerfile來創建一個支持HTTP2的、體積最小的、基於Alpine Linux的鏡像。重點將會放在減小鏡像體積和能夠100%自定義curl上。

方法步驟

我們將會:

  1. 討論為什么我們要關注HTTP2?
  2. 簡要地看一遍Dockerfile,讓你對構建過程有初步了解。
  3. 討論為什么將Alpine作為基礎鏡像?
  4. 詳細解釋Dockerfile,一步一步地理解它。
  5. 構建並且運行鏡像。

 

為什么選擇HTTP2?

引用自 https://http2.github.io/

由於HTTP的危急現狀,HTTP/2成為了HTTP的代替品。HTTP/2並不是對HTTP協議的完全重寫,HTTP方法,狀態碼,語義都保留了下來,並且應該能夠使用和HTTP/1.x(可能有細微調整)一樣的API來表示HTTP/2協議。


HTTP/2協議重點關注性能,尤其是端用戶的接收延遲、網絡和服務器資源的使用。其中一個主要的目標就是從瀏覽器訪問一個Web網站可以只通過一條連接來實現。

簡要地說,HTTP2想要解決HTTP/1.1的一些缺點,包括性能問題。通過對前面鏈接中的樣本用例進行測試,CloudFlare聲明HTTP/2對我的電腦會有4~8倍的速度提升。能夠提升網頁4~8倍的速度?是的,你沒有聽錯。

Dockerfile

這就是支持HTTP2的curl的Dockerfile

FROM alpine:edge

# For nghttp2-dev, we need this respository.
RUN echo https://dl-cdn.alpinelinux.org/alpine/edge/testing >>/etc/apk/repositories 

ENV CURL_VERSION 7.50.1

RUN apk add --update --no-cache openssl openssl-dev nghttp2-dev ca-certificates
RUN apk add --update --no-cache --virtual curldeps g++ make perl && \
wget https://curl.haxx.se/download/curl-$CURL_VERSION.tar.bz2 && \
tar xjvf curl-$CURL_VERSION.tar.bz2 && \
rm curl-$CURL_VERSION.tar.bz2 && \
cd curl-$CURL_VERSION && \
./configure \
    --with-nghttp2=/usr \
    --prefix=/usr \
    --with-ssl \
    --enable-ipv6 \
    --enable-unix-sockets \
    --without-libidn \
    --disable-static \
    --disable-ldap \
    --with-pic && \
make && \
make install && \
cd / && \
rm -r curl-$CURL_VERSION && \
rm -r /var/cache/apk && \
rm -r /usr/share/man && \
apk del curldeps
CMD ["curl"]


以上的構建過程大概做了以下事情:

  1. 我們安裝了一些包,里面包含了我們所需要的、支持SSL(HTTPS)和HTTP2的庫。
  2. 我們安裝了一些編譯cURL所必需的庫。
  3. 我們下載和解壓了cURLDE 源代碼(在寫作時的最新穩定版)。
  4. 我們配置,編譯,然后安裝了curl
  5. 我們清理了那些構建需要的、但是不想保留在最終鏡像中的依賴。
  6. 我們將默認的CMD設置為curl

 

為什么選擇Alpine?

Alpine Linux是體積最小的Linux發行版,它重點關注於安全和速度。使用apk能夠很快地安裝軟件包,默認情況下,鏡像只包含了完成基礎UNIX任務所需要的東西 ,因此相對於其它Docker基礎鏡像,體積會更小。

對比常用的、沒有壓縮過的基礎鏡像(在寫作時使用的是:latest標簽):

  • Alpine - 4.8MB
  • Ubuntu - 124.8 MB
  • Debian - 125.1MB
  • Centos - 196MB

 

image-size-chart.png


想象一下,在網絡上一次又一次地下載拉取這些鏡像

你是否正在考慮這能否對硬盤和帶寬產生同樣25倍價值的提升?在某些情況下,是相等的,但是Alpine每天都在不斷地改進和提高,並且提供了很多殺手鐧級別的特性,比如說通過文件名來查找(例子:需要定位哪一個apk包包含了二進制文件mke2fs完全沒有問題。)。在使用其它工具時,我必須花費大量的時間來學習它們奇怪的使用方式,對此我感到十分憤怒,不同於這些工具,我對使用Alpine十分高興,並且它不斷地給我驚喜。尤其是在使用一些小工具類型的容器時,比如說curl,鏡像體積的縮小對我來說非常棒。

詳細的構建步驟

讓我們更深入地了解Dockerfile。

FROM alpine:edge
# For nghttp2-dev, we need this respository.
RUN echo https://dl-cdn.alpinelinux.org/alpine/edge/testing >>/etc/apk/repositories 


在Alpine的edge分支中,nghttp2包(支持cURL中的HTTP2所必需的包)只有在testing倉庫有效,因此這幾行命令確保了當我們執行apk installnghttp2包能夠被正確安裝。閱讀“如何讓cURL支持HTTP2”的文檔就會發現, nghttp2庫是必需的(由於HTTP2所帶來的復雜性),並且在Alpine的歸檔中閑逛時,發現了edge分支中nghttp2只在testing倉庫有效。

ENV CURL_VERSION 7.50.1


當cURL發布了新版本,我們想要更新鏡像,我們僅僅需要修改這個文件的一處位置——環境變量,7.50.1表示在寫作時cURL最新的穩定版。

RUN apk add --update --no-cache openssl openssl-dev nghttp2-dev ca-certificates


這些是我們想要最終保留在鏡像的依賴,默認證書和庫是為了讓curl支持SSL(HTTPS連接)。注意—no-cache,這個確保了apk不會使用多余的硬盤空間來緩存包位置查找的結果,最終就會節省鏡像的空間。

下一條RUN命令只會產生一個文件層(因此我們可以安裝一些依賴,使用它們,然后清除它們,不將它們保留在最終鏡像中)。這條命令內容比較多,讓我們一步一步來看它們到底做了什么操作。

RUN apk add --update --no-cache --virtual curldeps g++ make perl && \


以上全都是成功編譯和安裝curl所需要的工具。--virtual是一個非常有用的apk特性——虛擬包。你可以給予包的集合一個標簽,然后通過使用一條命令 apk del virtual-pkg-name來將它們全部清除。

wget https://curl.haxx.se/download/curl-$CURL_VERSION.tar.bz2 && \
tar xjvf curl-$CURL_VERSION.tar.bz2 && \
rm curl-$CURL_VERSION.tar.bz2 && \
cd curl-$CURL_VERSION && \


獲得cURL的源碼壓縮包,解壓它,刪除壓縮包(我們在解壓后就不需要它了),然后使用cd命令進入到源文件目錄。

./configure \
    --with-nghttp2=/usr \
    --prefix=/usr \
    --with-ssl \
    --enable-ipv6 \
    --enable-unix-sockets \
    --without-libidn \
    --disable-static \
    --disable-ldap \
    --with-pic && \
make && \
make install && \


在熟悉的./configure;make;make install命令的基礎上加上了一些cURL特有的偏好設置。--with-nghttp2=/usr就是用來配置HTTP2支持的,由於我們將nghttp2-dev安裝在Aline的/usr/lib目錄下,在構建cURL的時候,程序會自動在/usr下的lib目錄尋找一個包配置文件。因此,你可能在其他的例子中看到參數設置為/usr/local或者其它目錄。

大多數的其它參數(除了—with-ssl)都是都拷貝自上游對curl包的請輸入鏈接描述APKBUILD文件。由於Alpine的包維護者比較可靠,因此我決定復用這些已經存在的配置。如果我對這么做感到太魯莽,那么我將會深入進去,然后從底層的角度來決定哪些我需要,哪些不需要,但是我還是希望它們包含UNIX套接字和IPV6的支持,因此我保留了這些已存在的配置。

cd / && \
rm -r curl-$CURL_VERSION && \
rm -r /var/cache/apk && \
rm -r /usr/share/man && \
apk del curldeps


以上全都是清除工作。

保留構建目錄(也就是二進制文件被安裝的地方),去除源代碼目錄,運行apk del curldepsenter code here命令來清除我們之前創建的虛擬包,接下來再去除/var/cache/apk(這是包緩存,老實說,我也不清楚為什么使用了—no-cache選項,緩存依舊存在)和/usr/share/man目錄(幫助手冊,在man命令沒有被安裝的情況下,這是無用的)。其中一些清除操作,尤其是緩存和幫助頁面的清除,某種程度上可以說是對縮小鏡像體積的一種怪癖,畢竟它們實際上不會超過1MB。這些都是我通過運行du | sort -n后,認為在最終鏡像中可能不必要的內容,我只能說,我狂熱地追求盡可能地縮小鏡像體積。

由於以上的這些操作都屬於同一個RUN命令,因此這最終會產生一個相對小的鏡像層,盡管在命令最開始的時候,我們為了構建最終的產品,安裝了將近212MB的依賴。如果這些操作分布在不同的層,清除操作實際上不會真正地在最終鏡像上刪除這些文件,相反,只是將這些文件隱藏了起來。

最后一條:

CMD ["curl"]


docker run image命令將會默認調用curl命令。當然這也能夠替換為ENTRYPOINT,但是我並不介意CMD能夠簡單地通過docker run被重新賦值。

構建並且運行鏡像

首先是構建,將Dockerfile丟進一個空目錄下,然后在這個目錄下運行:

$ docker build -t yourname/curl .


一旦構建完鏡像,運行鏡像就顯得非常直接了。讓我們來檢查看看一切是否按照nghttp2.org上描述的那樣工作。-s表示啟動安靜模式,--http2表示使用HTTP2協議,-I能夠返回請求頭,以此驗證我們使用了正確的協議。

$ docker run yourname/curl curl -s --http2 -I https://nghttp2.org
HTTP/2 200 
date: Sat, 06 Aug 2016 21:47:31 GMT
content-type: text/html
last-modified: Thu, 21 Jul 2016 14:06:56 GMT
etag: "5790d700-19e1"
accept-ranges: bytes
content-length: 6625
x-backend-header-rtt: 0.00166
strict-transport-security: max-age=31536000
server: nghttpx nghttp2/1.14.0-DEV
via: 2 nghttpx
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff


很好,一切正常工作。並且最終的鏡像體積保持在16MB左右。這對於私人訂制的curl構建來說是不錯的,畢竟編譯curl需要上百MB的依賴的支持。

結論

    • Alpine Linux非常棒。
    • 從零構建你自己的工具是難以想象的,但確實令人興奮。
    • Docker非常適合於從源代碼構建工具。
    • 你能夠擁有支持HTTP2的cURL工具。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM