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

  1. FROM alpine:edge
  2.  
  3. # For nghttp2-dev, we need this respository.
  4. RUN echo https://dl-cdn.alpinelinux.org/alpine/edge/testing >>/etc/apk/repositories 
  5.  
  6. ENV CURL_VERSION 7.50.1
  7.  
  8. RUN apk add --update --no-cache openssl openssl-dev nghttp2-dev ca-certificates
  9. RUN apk add --update --no-cache --virtual curldeps g++ make perl && \
  10. wget https://curl.haxx.se/download/curl-$CURL_VERSION.tar.bz2 && \
  11. tar xjvf curl-$CURL_VERSION.tar.bz2 && \
  12. rm curl-$CURL_VERSION.tar.bz2 && \
  13. cd curl-$CURL_VERSION && \
  14. ./configure \
  15.     --with-nghttp2=/usr \
  16.     --prefix=/usr \
  17.     --with-ssl \
  18.     --enable-ipv6 \
  19.     --enable-unix-sockets \
  20.     --without-libidn \
  21.     --disable-static \
  22.     --disable-ldap \
  23.     --with-pic && \
  24. make && \
  25. make install && \
  26. cd / && \
  27. rm -r curl-$CURL_VERSION && \
  28. rm -/var/cache/apk && \
  29. rm -/usr/share/man && \
  30. apk del curldeps
  31. 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

想象一下,在网络上一次又一次地下载拉取这些镜像

你是否正在考虑这能否对硬盘和带宽产生同样25倍价值的提升?在某些情况下,是相等的,但是Alpine每天都在不断地改进和提高,并且提供了很多杀手锏级别的特性,比如说通过文件名来查找(例子:需要定位哪一个apk包包含了二进制文件mke2fs完全没有问题。)。在使用其它工具时,我必须花费大量的时间来学习它们奇怪的使用方式,对此我感到十分愤怒,不同于这些工具,我对使用Alpine十分高兴,并且它不断地给我惊喜。尤其是在使用一些小工具类型的容器时,比如说curl,镜像体积的缩小对我来说非常棒。

详细的构建步骤

让我们更深入地了解Dockerfile。

  1. FROM alpine:edge
  2. # For nghttp2-dev, we need this respository.
  3. 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仓库有效。

  1. ENV CURL_VERSION 7.50.1

当cURL发布了新版本,我们想要更新镜像,我们仅仅需要修改这个文件的一处位置——环境变量,7.50.1表示在写作时cURL最新的稳定版。

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

这些是我们想要最终保留在镜像的依赖,默认证书和库是为了让curl支持SSL(HTTPS连接)。注意—no-cache,这个确保了apk不会使用多余的硬盘空间来缓存包位置查找的结果,最终就会节省镜像的空间。

下一条RUN命令只会产生一个文件层(因此我们可以安装一些依赖,使用它们,然后清除它们,不将它们保留在最终镜像中)。这条命令内容比较多,让我们一步一步来看它们到底做了什么操作。

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

以上全都是成功编译和安装curl所需要的工具。--virtual是一个非常有用的apk特性——虚拟包。你可以给予包的集合一个标签,然后通过使用一条命令 apk del virtual-pkg-name来将它们全部清除。

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

获得cURL的源码压缩包,解压它,删除压缩包(我们在解压后就不需要它了),然后使用cd命令进入到源文件目录。

  1. ./configure \
  2.     --with-nghttp2=/usr \
  3.     --prefix=/usr \
  4.     --with-ssl \
  5.     --enable-ipv6 \
  6.     --enable-unix-sockets \
  7.     --without-libidn \
  8.     --disable-static \
  9.     --disable-ldap \
  10.     --with-pic && \
  11. make && \
  12. 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的支持,因此我保留了这些已存在的配置。

  1. cd / && \
  2. rm -r curl-$CURL_VERSION && \
  3. rm -/var/cache/apk && \
  4. rm -/usr/share/man && \
  5. apk del curldeps

以上全都是清除工作。

保留构建目录(也就是二进制文件被安装的地方),去除源代码目录,运行apk del curldepsenter code here命令来清除我们之前创建的虚拟包,接下来再去除/var/cache/apk(这是包缓存,老实说,我也不清楚为什么使用了—no-cache选项,缓存依旧存在)和/usr/share/man目录(帮助手册,在man命令没有被安装的情况下,这是无用的)。其中一些清除操作,尤其是缓存和帮助页面的清除,某种程度上可以说是对缩小镜像体积的一种怪癖,毕竟它们实际上不会超过1MB。这些都是我通过运行du | sort -n后,认为在最终镜像中可能不必要的内容,我只能说,我狂热地追求尽可能地缩小镜像体积。

由于以上的这些操作都属于同一个RUN命令,因此这最终会产生一个相对小的镜像层,尽管在命令最开始的时候,我们为了构建最终的产品,安装了将近212MB的依赖。如果这些操作分布在不同的层,清除操作实际上不会真正地在最终镜像上删除这些文件,相反,只是将这些文件隐藏了起来。

最后一条:

  1. CMD ["curl"]

docker run image命令将会默认调用curl命令。当然这也能够替换为ENTRYPOINT,但是我并不介意CMD能够简单地通过docker run被重新赋值。

构建并且运行镜像

首先是构建,将Dockerfile丢进一个空目录下,然后在这个目录下运行:

  1. $ docker build -t yourname/curl .

一旦构建完镜像,运行镜像就显得非常直接了。让我们来检查看看一切是否按照nghttp2.org上描述的那样工作。-s表示启动安静模式,--http2表示使用HTTP2协议,-I能够返回请求头,以此验证我们使用了正确的协议。

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

很好,一切正常工作。并且最终的镜像体积保持在16MB左右。这对于私人订制的curl构建来说是不错的,毕竟编译curl需要上百MB的依赖的支持。

结论

    • Alpine Linux非常棒。
    • 从零构建你自己的工具是难以想象的,但确实令人兴奋。
    • Docker非常适合于从源代码构建工具。
    • 你能够拥有支持HTTP2的cURL工具。

支持HTTP2的cURL——基于Alpine的最小化Docker镜像的更多相关文章

  1. 最小化docker镜像

    kubernetes离线安装包,仅需三步 如何让镜像尽可能小 很容器想到from scratch, 就是没任何基础镜像 FROM scratch COPY p / ENTRYPOINT [" ...

  2. 最小化 Java 镜像的常用技巧

    背景 随着容器技术的普及,越来越多的应用被容器化.人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积.本文将介绍精简容器镜像的必要性并以基于 spring boot ...

  3. 制作基于Buildbot的自动化测试系统Docker镜像

    Buildbot in Docker 前言 最近使用Buildbot做了一个自动测试的框架,为了部署方便,可以把测试框架做成Docker镜像,方便部署.这里记录下过程,供大家参考. 项目介绍 项目是一 ...

  4. 使用alpine制作最小化的JDK基础镜像

    注意:这里使用的是oracle的JRE,版本是1.8. 1.解压jre包,删除根目录下文本文件,然后删除其他不必要文件. #解压 tar xvcf jre-8u161-linux-x64.tar.gz ...

  5. 构建最小JDK Docker镜像

    参考: https://my.oschina.net/shyloveliyi/blog/1627020 1.首先下载jre,下载地址是https://www.java.com/en/download/ ...

  6. 基于Alpine镜像定制自己的工具箱

    Alpine介绍 Alpine 操作系统是一个面向安全的轻型 Linux 发行版.目前 Docker 官方已开始推荐使用 Alpine 替代之前的 Ubuntu 做为基础镜像环境.这样会带来多个好处. ...

  7. curl 支持 http2

    让 curl 支持 HTTP2 我们需要安装 nghttp2(http2 的 C 语言库) 源码安装 安装 nghttp2 git clone https://github.com/tatsuhiro ...

  8. nginx支持HTTP2的配置过程

    一.获取安装包 http://zlib.net/zlib-1.2.11.tar.gz https://www.openssl.org/source/openssl-1.0.2e.tar.gz (ope ...

  9. 基于alpine用dockerfile创建的tomcat镜像

    1.下载alpine镜像 [root@docker43 ~]# docker pull alpine Using default tag: latest Trying to pull reposito ...

随机推荐

  1. xmpp聊天室(5)

    聊天室 //初始化聊天室 XMPPJID *roomJID = [XMPPJID jidWithString:ROOM_JID]; xmppRoom = [[XMPPRoom alloc] initW ...

  2. Tensorflow学习笔记(1):tf.slice()函数使用

    tensorflow 当中的一个常用函数:Slice() def slice(input_, begin, size, name=None) 函数的功能是根据begin和size指定获取input的部 ...

  3. 洛谷——P3173 [HAOI2009]巧克力

    P3173 [HAOI2009]巧克力 题目描述 有一块n*m的矩形巧克力,准备将它切成n*m块.巧克力上共有n-1条横线和m-1条竖线,你每次可以沿着其中的一条横线或竖线将巧克力切开,无论切割的长短 ...

  4. Linux---shell基本指令

    1. 显示当前目录 pwd wangzhengchao@ubuntu:~$ cd /home/wangzhengchao/Desktop/ wangzhengchao@ubuntu:~/Desktop ...

  5. Oracle 回滚(ROLLBACK)和撤销(UNDO)

    一.回滚(ROLLBACK)和撤销(UNDO) 回滚和前滚是保证Oracle数据库中的数据处于一致性状态的重要手段. 在9i版本以前 Oracle使用数据库中的回滚段来实现未提交数据或因系统故障导致实 ...

  6. PHP 锁机制

    应用环境 解决高并发,库存为负数的情况 阻塞模式 如果其他进程已经加锁文件,当前进程会一直等其他进程解锁文件后继续执行 flock($fp, LOCK_EX) // 文件锁 非阻塞模式 如果其他进程已 ...

  7. LINUX-系统信息

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  8. 【Codeforces 924C】Riverside Curio

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 设第i天总共的线数为t[i] 水平线上线数为m[i]是固定的 水平线下的线数设为d[i] 则d[i]+m[i]+1=t[i] 也就是说问题可以 ...

  9. spring boot jpa 事务管理

    spring boot 对jpa的支持极为方便,基本上不需要作太多配置,只需要加上注解就能支持事务: @Controller @Transactional(rollbackOn = Exception ...

  10. nyoj_278_排队_201403282135

    排队 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 周末了,软件ACM的队员准备玩玩游戏,娱乐一下,CY想了一个好主意,所有队员站成一个圈,从1开始报数,凡是报出指 ...