问题背景

目前我负责的一个项目是Debian8 + Ruby2.1 + Puppet3.7这样的环境. 但系统对Duppet的依赖太严重了, 没法将该软件剔除. 由于Debian8已经不再维护, 准备升级Debian9, 但是发现Ruby2.1以及Puppet3.7在新的系统中无法被安装.

所以最终的打算就是编译一份固定版本的Ruby+Puppet, 最好是在Debian9甚至Debian10上都可以运行, 最主要的就是对系统的依赖最小.

为什么选择Docker

有朋友可能会讲, 为什么要用Docker呢, 直接在一台Debian8的机器上编译打包好像也没什么不可以. 这个想法是可行的, 但是这样有以下两个小问题:

  1. 除非新安装的Debian8, 否则系统一定有其他依赖
  2. 如果想要纯净的系统, 最好的办法就是每次启动虚拟机, 但是启动虚拟机的开销挺大的.

基于以上两点的考虑, 我最终考虑使用Docker, 它既拥有了纯净的系统, 有可以快速的启动关闭, 另外一点是Docker构建时, 还可以复用先前的层.

Docker构建脚本

以下是我在构建时使用的代码, 重要部分已经给出了注释.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
FROM debian:jessie

# upgrade & install required packages
RUN echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib\n \
deb-src http://mirrors.163.com/debian/ jessie main non-free contrib\n \
deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib\n \
deb-src http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib" > /etc/apt/sources.list

# 安装编译环境
RUN apt-get update && apt-get upgrade && apt-get install -y wget ruby-build

WORKDIR /home

# 下载源码包
RUN wget http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.5.tar.gz
RUN wget https://downloads.puppetlabs.com/puppet/puppet-3.7.2.tar.gz
RUN tar -zxf ruby-2.1.5.tar.gz
RUN tar -zxf puppet-3.7.2.tar.gz

# 编译, 将文档以及一些模块屏蔽掉
RUN cd ruby-2.1.5 \
&& ./configure --prefix=/opt/puppet37 --disable-install-doc --disable-install-rdoc --disable-install-capi \
&& make -j8 \
&& make install

# In debian9, there is no library, wo need copy it.
RUN cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 /opt/puppet37/lib/
RUN cp /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 /opt/puppet37/lib/

# Must specify the version, newer facter will block the puppet.
RUN /opt/puppet37/bin/gem install hiera -v 1.3.4
RUN /opt/puppet37/bin/gem install facter -v 2.2.0

# 安装Puppet
RUN /opt/puppet37/bin/ruby /home/puppet-3.7.2/install.rb --no-rdoc

# 打包
RUN tar -cf puppet37_2_1_5.tar /opt/puppet37
RUN xz -9 puppet37_2_1_5.tar

# 构建
# docker build -t pp37 .
# docker run --rm --entrypoint cat pp37 /home/puppet37_2_1_5.tar.xz > ./puppet37_2_1_5.tar.xz

# 安装
# cp puppet37_2_1_5.tar.xz /tmp
# xz -d /tmp/puppet37_2_1_5.tar.xz
# tar -xf /tmp/puppet37_2_1_5.tar -C /

通过上面的编译过程, 我们已经获得了一份几乎完全不依赖系统环境的Ruby环境, 构建的结果就是puppet37_2_1_5.tar.xz, 你可以考虑将打包好的软件拷贝出来安装到机器上, 或是将上面的Dockerfile作为多阶段构建的一部分.

有一点也请大家注意, 编译结果最好与系统的环境分离, 就类似上面, 我将程序安装在了/opt/puppet37目录, 这点在构建时请一定要注意, 并多做一下测试.

总结

由于Docker环境本身的依赖就很少, 并且具有快速运行的机制, 使用Docker作为旧版本软件的编译工具也是不错的选择. 截止目前, 我在项目开发中, 已经在编译Nginx+Luass5时使用了Docker.