在鲲鹏云CentOS 7.5上编译安装puppet-agent和puppetserver
Puppet Agent 官方可以支持aarch64,可以通过官方资源库安装,安装后可以正常运行,Puppet Server官方并未宣称可以支持aarch64,在官方repo可以下载到跨平台(noarch)的安装包,但是在CentOS 7.5 aarch64里安装后,puppetserver无法启动。
一、问题诊断
通过/var/log/puppetlabs/puppetserver/puppetserver.log,可看到异常:
Caused by: org.jruby.embed.EvalFailedException: (LoadError) libfacter was not built with JRuby support.
at org.jruby.embed.internal.EmbedEvalUnitImpl.run(EmbedEvalUnitImpl.java:132)
分析程序可见/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.rb会尝试加载facter.jar,如果这个文件不存在,就会出现上述异常。从x86_64机器上把facter.jar复制到/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.jar,再次尝试启动,发现还是启动失败,但异常变为“无法判断系统特性”:
Caused by: org.jruby.exceptions.RaiseException: (Error) Cannot determine basic system flavour
at RUBY.(root)(/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/feature/base.rb:40)
at org.jruby.RubyKernel.require(org/jruby/RubyKernel.java:1040)
反编译facter.jar发现这个Java程序可以直接运行,会使用JNI调用libfacter.so。尝试运行java -jar facter.jar os,发现出现异常:
Exception in thread "main" java.lang.UnsatisfiedLinkError: /opt/puppetlabs/puppet/lib/libfacter.so.3.14.0: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /opt/puppetlabs/puppet/lib/libfacter.so.3.14.0)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
是无法加载动态链接库,设置export LD_LIBRARY_PATH=/opt/puppetlabs/puppet/lib后,再次尝试运行,发现异常变为找不到库里的函数:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.puppetlabs.Facter.lookup(Ljava/lang/String;)Ljava/lang/Object;
at com.puppetlabs.Facter.lookup(Native Method)
at com.puppetlabs.Facter.main(Facter.java:38)
使用objdump -T libfacter.so | grep lookup检查libfacter.so里的函数,可以发现缺少找不到JNI需要的Java_com_puppetlabs_Facter_lookup函数。
基本可以断定puppet-agent有问题,缺少facter.jar,libfacter.so里也缺少JNI调用的函数,需要重新编译puppet-agent。
二、编译puppet-agent
Puppet agent是开源的,源代码托管在github:https://github.com/puppetlabs/puppet-agent,编译过程比一般开源软件要复杂得多,我们要编译的是arm 64位(aarch64)版,可以在aarch64服务器上直接编译,也可以使用x86_64服务器交叉编译,官方源码中的构建脚本是为交叉编译准备的,以及默认在puppet公司内网环境构建,所以需要对构建配置做一定的修改才可能编译成功,需要在尝试构建过程中不断修复遇到的坑,如果从0开始尝试,会有一定的困难。
(一)交叉编译过程
由于官方源码编译配置对Arm64位的支持就是交叉编译,所以交叉编译会相对简单一些,构建过程如下:
1、准备构建环境
需要准备2台CentOS 7.5 x86_64服务器,一台配置不需要太高(4核8G足够),一台配置高一些会节约构建时间(16核32G以上更好)。
调度服务器上,需要安装一下工具:
(1)git:yum install -y git;
(2)ruby 2.1以上:可以下载ruby源代码编译安装,也可以使用rvm(较简单,过程省略);
(3)安装bundler:gem install bundler
调度机需要能ssh到执行机,在调度用使用ssh-keygen生成秘钥对,把/root/.ssh/id_rsa.pub内容放到执行机的/root/.ssh/authorized_keys里即可,在调度机使用ssh root@执行机IP,可以测试是否可以ssh登录过去。
2、在调度机下载构建所需源代码,建议放到一个构建目录下,如:/opt/puppet-build
(1)pl-build-tools-vanagon:https://github.com/puppetlabs/pl-build-tools-vanagon
(2)puppet-runtime: https://github.com/puppetlabs/puppet-runtime
(3)puppet-agent: https://github.com/puppetlabs/puppet-agent:构建时最新版本是6.5.0,检出该版本git checkout 6.5.0
3、准备一台nexus服务器,或者一台http服务器也可以,过程中会起到减少重复下载的作用,假定url是http://nexus.domain1.com/;
4、构建交叉编译环境:
(1)在pl-build-tools-vanagon目录下执行bundle install安装依赖的gem;
(2)准备交叉编译需要的aarch64的sysroot:
① 找一台CentOS 7.5 aarch64服务器,在上面安装java-1.7.0-openjdk-devel.aarch64;
② 打包必须的文件(必须叫el-7-aarch64-sysroot):tar --transform 's/^/el-7-aarch64-sysroot/' -cPzf el-7-aarch64-sysroot.tar.gz /usr /lib /lib64;
③ 计算el-7-aarch64-sysroot.tar.gz的md5值备用:md5sum el-7-aarch64-sysroot.tar.gz
④ 上传el-7-aarch64-sysroot.tar.gz文件到nexus服务器上记住URL,假定为http://nexus.domain1.com/repository/mirrors/el-7-aarch64-sysroot.tar.gz;
(3)修改构建脚本:
① configs/platforms/el-7-aarch64.rb b/configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/platforms/el-7-x86_64.rb b/configs/platforms/el-7-x86_64.rb:注释掉plat.add_build_repository那行;
③ configs/projects/pl-build-tools.rb:修改proj.setting(:buildsources_url, "#{proj.artifactory_url}/generic/buildsources"),为proj.setting(:buildsources_url, "http://nexus.domain1.com/repository/mirrors/"),构建过程中会优先从这个地址下载依赖的源代码,以及el-7-aarch64-sysroot.tar.gz,如果构建失败了,可以从执行机的/var/tmp/的临时目录里找到下载的源代码包,上传到http服务器,重复构建时,可大大节省下载时间,还有就是下载过程中有个别无法下载的,需要***后才能下载,可在能***的机器下载后,上传到http服务器解决;
④ configs/components/sysroot.rb:修改when "el-7-aarch64"下面的pkg.md5sum的值为上面计算的el-7-aarch64-sysroot.tar.gz的md5值;
⑤ configs/projects/pl-gcc.rb:修改第一部分proj.version "6.1.0",版本号改为4.8.2,proj.release "6",6改为9;
⑥ configs/components/gcc.rb:修改第一部分pkg.version "6.1.0",版本号改为4.8.2,pkg.md5sum,修改后后面4.8.2段的md5sum值;
修改gcc版本的原因是避免libstdc++.so版本过高与操作系统自带的不一致,避免不必要的麻烦。
(4)构建交叉编译工具,slaveip是指编译执行机的ip,依次:
① 构建x86_64版gcc,需要的原因是构建脚本会调用/opt/pl-build-tools/bin下的gcc编译器,构建日志输出到文件,构建后会安装到执行机的/opt/pl-build-tools/下:bundle exec build pl-gcc el-7-x86_64 ${slaveip} > ../pl-gcc.out 2>&1
② 构建x86_64版gettext:bundle exec build pl-gettext el-7-x86_64 ${slaveip} > ../pl-gettext.out 2>&1
③ 构建x86_64版cmake:bundle exec build pl-cmake el-7-x86_64 ${slaveip} > ../pl-cmake.out 2>&1
④ 构建x86_64版ruby: bundle exec build pl-ruby el-7-x86_64 ${slaveip} > ../pl-ruby.out 2>&1
⑤ 构建aarch64版binutils:bundle exec build pl-binutils el-7-aarch64 ${slaveip} > ../pl-binutils-aarch64.out 2>&1
⑥ 构建用于交叉编译的gcc编译器:bundle exec build pl-gcc el-7-aarch64 ${slaveip} > ../pl-gcc-aarch64.out 2>&1
gcc和cmake的构建时间相当长(CPU核数越多越快),耐心等待,建议放到screen里执行,通过out文件检查构建日志,发现问题可排除后重新执行,注意:如果失败,建议删除执行机的/opt/pl-build-tools/后从头开始构建。
构建成功后,会在output目录下生成rpm文件,一共有6个rpm文件。
(5)安装交叉编译环境:
① 把output下的子目录里的6个rpm文件传到执行机上:用scp即可;
② 登录到执行机:删除/opt/pl-build-tools:rm -rf /opt/pl-build-tools;
③ 安装6个rpm,放到同一个目录下:rpm -ivh *.rpm --replacefiles。
5、构建puppet-runtime:
(1)进入puppet-runtime,执行bundle install安装依赖的gem;
(2)修改构建脚本:
① configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/projects/_shared-agent-settings.rb:修改proj.setting(:buildsources_url, "#{proj.artifactory_url}/generic/buildsources"),为proj.setting(:buildsources_url, "http://nexus.domain1.com/repository/mirrors/"),构建过程中会优先从这个地址下载依赖的源代码;
(3)执行构建:
① 在启动前确保执行机的/opt/puppetlabs目录不存在:rm -rf /opt/puppetlabs
② 设置locale,否则构建到一半时会失败:export LC_ALL=en_US.UTF-8
③ 在puppet-runtime启动构建:bundle exec build agent-runtime-master el-7-aarch64 ${slaveip} > ../runtime.out 2>&1
构建时间在配置较好的机器上大约需要15~20分钟,建议放到screen里执行。
构建成功后,目标文件会在output目录里。
6、构建puppet-agent:
(1)进入puppet-agent,执行bundle install安装依赖的gem;
(2)修改构建脚本:
① configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/components/puppet-runtime.json:修改地址为puppet-runtime/output目录,版本号是文件名里的那串数字,如{"location":"file:///opt/puppet-build/puppet-runtime/output","version":"201906190"}
③ configs/components/facter.rb,如不改JNI还是编译不进去:在定义special_flags变量那行下面,加上special_flags += " -DJAVA_AWT_LIBRARY=NotNeeded -DJAVA_JVM_LIBRARY=NotNeeded "
④ configs/projects/puppet-agent.rb:在proj.version_from_git下面增加一行proj.release '2',修改版本发布号
(3)执行构建:
① 在启动前确保执行机的/opt/puppetlabs目录不存在:rm -rf /opt/puppetlabs
②在puppet-agent目录启动构建:bundle exec build puppet-agent el-7-aarch64 ${slaveip} > ../agent.out 2>&1
构建时间在配置较好的机器上大约需要15~20分钟,建议放到screen里执行。
构建成功后,rpm文件会在output目录里。
(二)在aarch64服务器上原生编译(虽然也可以,但是建议使用交叉编译)
由于官方源码编译配置对Arm64位的支持就是交叉编译,所以需要做一定修改后才可能构建成功。
1、准备构建环境
需要准备2台CentOS 7.5 aarch64服务器,一台配置不需要太高(4核8G足够),一台配置高一些会节约构建时间(16核32G以上更好)。
调度服务器上,需要安装一下工具:
(1)git:yum install -y git;
(2)ruby 2.1以上:可以下载ruby源代码编译安装,也可以使用rvm(较简单,过程省略);
(3)安装bundler:gem install bundler
调度机需要能ssh到执行机,在调度用使用ssh-keygen生成秘钥对,把/root/.ssh/id_rsa.pub内容放到执行机的/root/.ssh/authorized_keys里即可,在调度机使用ssh root@执行机IP,可以测试是否可以ssh登录过去。
2、在调度机下载构建所需源代码,建议放到一个构建目录下,如:/opt/puppet-build
(1)pl-build-tools-vanagon:https://github.com/puppetlabs/pl-build-tools-vanagon
(2)puppet-runtime: https://github.com/puppetlabs/puppet-runtime
(3)puppet-agent: https://github.com/puppetlabs/puppet-agent:构建时最新版本是6.5.0,检出该版本git checkout 6.5.0
3、准备一台nexus服务器,或者一台http服务器也可以,过程中会起到减少重复下载的作用,假定url是http://nexus.domain1.com/;
4、构建编译环境:
(1)在pl-build-tools-vanagon目录下执行bundle install安装依赖的gem;
(2)修改构建脚本:
① configs/platforms/el-7-aarch64.rb b/configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/platforms/el-7-aarch64.rb b/configs/platforms/el-7-aarch64.rb:plat.cross_compiled true,true修改为false,因为我们是原生编译;
③ configs/projects/pl-build-tools.rb:修改proj.setting(:buildsources_url, "#{proj.artifactory_url}/generic/buildsources"),为proj.setting(:buildsources_url, "http://nexus.domain1.com/repository/mirrors/"),构建过程中会优先从这个地址下载依赖的源代码,如果构建失败了,可以从执行机的/var/tmp/的临时目录里找到下载的源代码包,上传到http服务器,重复构建时,可大大节省下载时间,还有就是下载过程中有个别无法下载的,需要***后才能下载,可在能***的机器下载后,上传到http服务器解决;
④ configs/projects/pl-build-tools.rb:在platform_triple = "aarch64-redhat-linux"行上面,elsif语句改为elsif platform.architecture == "aarch64" && platform.is_rpm? && platform.is_cross_compiled?,否则构建时误判为交叉编译;
⑤ configs/projects/pl-gcc.rb:修改第一部分proj.version "6.1.0",版本号改为4.8.2,proj.release "6",6改为9;
⑥ configs/components/gcc.rb:修改第一部分pkg.version "6.1.0",版本号改为4.8.2,pkg.md5sum,修改后后面4.8.2段的md5sum值;
如果不降低gcc版本,会在编译到cmake时编译失败,虽然可以解决,但是还是会有libstdc++.so版本过高与操作系统自带的不一致,降低版本可避免不必要的麻烦。
(4)构建编译工具,slaveip是指编译执行机的ip,依次:
① 构建gcc,需要的原因是构建脚本会调用/opt/pl-build-tools/bin下的gcc编译器,构建日志输出到文件,构建后会安装到执行机的/opt/pl-build-tools/下:bundle exec build pl-gcc el-7-aarch64 ${slaveip} > ../pl-gcc.out 2>&1
② 构建gettext:bundle exec build pl-gettext el-7-aarch64 ${slaveip} > ../pl-gettext.out 2>&1
③ 构建cmake:bundle exec build pl-cmake el-7-aarch64 ${slaveip} > ../pl-cmake.out 2>&1
④ 构建ruby: bundle exec build pl-ruby el-7-aarch64 ${slaveip} > ../pl-ruby.out 2>&1
gcc和cmake的构建时间相当长(CPU核数越多越快),耐心等待,建议放到screen里执行,通过out文件检查构建日志,发现问题可排除后重新执行,注意:如果失败,建议删除执行机的/opt/pl-build-tools/后从头开始构建。
5、构建puppet-runtime:
(1)进入puppet-runtime,执行bundle install安装依赖的gem;
(2)修改构建脚本:
① configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/platforms/el-7-aarch64.rb b/configs/platforms/el-7-aarch64.rb:plat.cross_compiled true,true修改为false,因为我们是原生编译;
③ configs/projects/_shared-agent-settings.rb:修改proj.setting(:buildsources_url, "#{proj.artifactory_url}/generic/buildsources"),为proj.setting(:buildsources_url, "http://nexus.domain1.com/repository/mirrors/"),构建过程中会优先从这个地址下载依赖的源代码;
④ configs/components/openssl-1.1.1.rb:在elsif platform.architecture =~ /64$/下面那段的target = 'linux-x86_64'改成:
if platform.architecture =~ /aarch64/
cflags = "#{settings[:cflags]} -fPIC"
cflags += " -O2"
target = 'linux-aarch64'
else
target = 'linux-x86_64'
end
否则会被误判为x86_64环境,构建失败。
(3)执行构建:
① 在启动前确保执行机的/opt/puppetlabs目录不存在:rm -rf /opt/puppetlabs
② 设置locale,否则构建到一半时会失败:export LC_ALL=en_US.UTF-8
③ 在puppet-runtime启动构建:bundle exec build agent-runtime-master el-7-aarch64 ${slaveip} > ../runtime.out 2>&1
构建时间在配置较好的机器上大约需要15~20分钟,建议放到screen里执行。
构建成功后,目标文件会在output目录里。
(4)上传puppet-runtime到http服务器,供puppet-agent构建时下载,原生编译时,构建工具有bug无法从output目录中取文件:
把puppet-runtime/output下所有文件上传至http://nexus.domain1.com/repository/mirrors/下,建议使用curl上传:
for f in *; do curl -u username:password --upload-file $f http://nexus.domain1.com/repository/mirrors/$f ; done
6、构建puppet-agent:
(1)进入puppet-agent,执行bundle install安装依赖的gem;
(2)修改构建脚本:
① configs/platforms/el-7-aarch64.rb:注释掉plat.add_build_repository那两行;
② configs/platforms/el-7-aarch64.rb b/configs/platforms/el-7-aarch64.rb:plat.cross_compiled true,true修改为false,因为我们是原生编译;
③ configs/components/puppet-runtime.json:修改地址及版本号,版本号是文件名里的那串数字,如{"location": "http://nexus.domain1.com/repository/mirrors/","version":"201906190"}
④ configs/components/facter.rb,如不改JNI还是编译不进去:在定义special_flags变量那行下面,加上special_flags += " -DJAVA_AWT_LIBRARY=NotNeeded -DJAVA_JVM_LIBRARY=NotNeeded "
⑤ configs/projects/puppet-agent.rb:在proj.version_from_git下面增加一行proj.release '2',修改版本发布号
(3)执行构建:
① 在启动前确保执行机的/opt/puppetlabs目录不存在:rm -rf /opt/puppetlabs
②在puppet-agent目录启动构建:bundle exec build puppet-agent el-7-aarch64 ${slaveip} > ../agent.out 2>&1
构建时间在配置较好的机器上大约需要15~20分钟,建议放到screen里执行。
构建成功后,rpm文件会在output目录里。
(三)验证puppet-agent构建结果
1、将构建好的puppet-agent在CentOS 7.5 aarch64服务器安装,使用yum install -y puppet-agent-6.5.0-2.el7.aarch64.rpm
2、执行java -jar /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.jar os,如无问题会输出一段json信息
三、解决puppetserver启动失败的问题:
(一)安装puppetserver
1、下载puppetserver安装包,理论上应该从http://yum.puppetlabs.com/puppet6/el/7/aarch64/下载,但是该目录下只有puppet-agent,并不提供puppetserver,只能从http://yum.puppetlabs.com/puppet6/el/7/x86_64/下载,写文章时最新版本是puppetserver-6.4.0-1.el7.noarch.rpm,puppetserver是基于jruby的,可以跨平台;
2、安装puppetserver,需要先安装前面编译的puppet-agent,才能安装puppetserver;
3、尝试启动puppetserver:systemctl start puppetserver,这是会发现还是启动失败,异常还是:
Caused by: org.jruby.exceptions.RaiseException: (Error) Cannot determine basic system flavour
at RUBY.(root)(/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/feature/base.rb:40)
at org.jruby.RubyKernel.require(org/jruby/RubyKernel.java:1040)
(二)诊断puppetserver启动时无法判断系统特性的问题
1、通过异常信息,打开/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/feature/base.rb;
2、可以发现该ruby程序会使用Puppet.features检测系统中是否有syslog、etc等模块;
3、进一步跟踪可以发现jruby会从/opt/puppetlabs/server/apps/puppetserver/puppet-server-release.jar里的META-INF/jruby.home/lib/ruby/stdlib/ffi/platform/aarch64-linux/加载syslog.rb、etc.rb等,这是ruby的FFI扩展框架;
4、在puppet-server-release.jar的META-INF/jruby.home/lib/ruby/stdlib/ffi/platform/x86_64-linux/下可以找到需要的rb文件;
5、创建补丁目录:mkdir -p puppet-server-patch/META-INF/jruby.home/lib/ruby/stdlib/ffi/platform/aarch64-linux/;
6、将META-INF/jruby.home/lib/ruby/stdlib/ffi/platform/aarch64-linux/*.rb文件放到补丁目录下;
7、在puppet-server-patch目录下修补puppet-server-release.jar:jar -uMvf /opt/puppetlabs/server/apps/puppetserver/puppet-server-release.jar META-INF;
8、再次尝试启动puppetserver:systemctl start puppetserver;
9、可以发现puppetserver可以正常启动了,测试功能也会发现功能正常。
(三)创建修补版puppetserver安装包
1、为了确保安装包尽可能“干净”,新找一台CentOS 7.5 aarch64,全新安装puppetserver,安装后直接打puppet-server-release.jar补丁;
2、安装rpmrebuild:yum install -y rpmrebuild;
3、重新构建rpm包:rpmrebuild puppetserver,构建是可指定版本号,建议使用2.el7,构建出puppetserver-6.4.0-2.el7.noarch.rpm,该rpm安装后即是补丁后的版本。
- 点赞
- 收藏
- 关注作者
评论(0)