针对docker容器无法使用gpu的问题

首先,确保宿主机安装好了GPU显卡驱动(这里GPU显卡驱动一般是指Nvidia显卡),通过命令:

nvidia-smi -l

可以检测是否正常。但启动docker时,发现找不到gpu驱动,命令如下:

docker run --name archlinux_for_cnangel -dit --gpus all --privileged=true --net=host -v home_cnangel:/home/cnangel -v /home/cnangel:/data -v /u:/u harbor.huhoo.net:4330/os/archlinux:latest /bin/bash

此时需要安装nvidia-docker2,它会自动带起2个重要的依赖(nvidia-container-toolkit和libnvidia-container-tools)安装:

dnf install nvidia-docker2 -y

然后重新运行上述docker run即可。

如何在fedora39上使用钉钉

在Linux系统中,一般有2大阵营,Debian和Redhat,其对应的桌面版本主要是Ubuntu和Fedora。

钉钉作为优秀的企业办公软件,当前也有Linux版本,由于Linux用户本身很少,故钉钉只发布了针对Ubuntu20.04的版本(com.alibabainc.dingtalk_7.1.0.31017_amd64.deb),对于Fedora系列无支持,那么应该如何支持Fedora系列呢?

fedora39发布了

2003 年 11 月 6 日,Fedora 项目发布了 Fedora Core 1,而在二十年零一天后的今天,Fedora Linux 39 终于正式发布,这是为台式机、笔记本电脑、服务器、云、边缘计算设备以及您能想到的任何其他终端打造的完整社区操作系统。

关于nginx证书的问题

一般证书上,对CRT和PEM证书没有特别做区分,故在nginx上部署了xxx.com_public.crt证书,发现curl 接口,出现如下错误:

curl 'https://xxx.com/api/campaign-names'
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

需要加上 -k 才能正确访问;若直接使用pem证书,则无需加上 -k 参数;

putty私钥连接不上fedora的问题

putty版本:0.70

首先排除了fedora38上rsa私钥以及配置的问题,就剩下2个可能:

1,历史私钥不兼容;

2,Putty的问题;

关于RHEL升级安装的一些问题

用 docker 在 Dockerfile 上每次升级都很顺利,但突然之间,升级RPM包,报以下错误:

... signature hdr data: bad, no. ... of bytes(64372) out of range

网上查了一下,主要是由于RPM的一些兼容性问题导致(RPM签名的头超过了64KB),详情见"signature hdr data BAD" on trying to parse RPM with more than 64KB signature header

如何解决的呢?

关于静态编译(二)

静态编译有其优势,但有些情况下,静态编译反而会造成一些不便。

工厂类注册问题

工厂类一般使用模板或宏的方式,如下列代码申明:

class ExpressionFactoryRegisterer
{
public:
   ExpressionFactoryRegisterer(const std::string &name, const ExpressionFactoryPtr &factory)
  {
       GetExpressionTable()->RegisterExpressionFactory(name, factory);
  }
};

#define REGISTER_EXPRESSION_CONCAT_IMPL(a, b) a##b
#define REGISTER_EXPRESSION_CONCAT(a, b) REGISTER_EXPRESSION_CONCAT_IMPL(a, b)

#define REGISTER_EXPRESSION_FACTORY(name, factory)                                                       \
   namespace                                                                                           \
   {                                                                                                   \
   BS_NAMESPACE(expression)::ExpressionFactoryRegisterer                                               \
   REGISTER_EXPRESSION_CONCAT(r, __COUNTER__)(name, factory);                                           \
   }

#define REGISTER_EXPRESSION_TYPE(name, type)                                                             \
   namespace                                                                                           \
   {                                                                                                   \
   BS_NAMESPACE(expression)::ExpressionFactoryRegisterer REGISTER_EXPRESSION_CONCAT(r, __COUNTER__)(   \
           name, std::make_shared<BS_NAMESPACE(expression)::TypedExpressionFactory<type>>());           \
   }

代码注册如下:

REGISTER_EXPRESSION_FACTORY("log", std::make_shared<UnaryMathExpressionFactory<Func__log>>());

这样注册之后,一个简单的"反射"机制建立起来。但一般情况下,为了避免多重定义,会降代码注册的部分写到 .cc 文件中,但静态编译的时候, .cc 生成的 .o 文件,会通过 ar 工具打包成 .a 文件,此时 .a 文件直接去链接,静态区过程会滞后,导致 RegisterExpressionFactory 动作变成运行态执行。当程序真正执行时,初始化从静态区获取不到注册的类,导致空结果或指针。

正确的方式有2种:

  • 动态编译:通过 -shared 生成 so 文件,然后链接;

  • .o 直接链接:将生成的 .o 文件直接链接,或通过 ar 工具解压 .a 文件,然后再链接 .o 文件;

这样程序会再链接的时候,将注册内容真正的放入静态区,程序启动时,直接加载到对应变量中,就不会为空啦。

关于静态编译(一)

一般C/C++语言的编译,我还是喜欢使用automake,虽然它缺少通配符的支持,但其支持Makefile的原生语法以及一旦生成了Makefile,在任意有Makefile的目录修改Makefile.am之后都可以直接make;且结合libtool之后,动态库和静态库可以随心编译;这个是cmake不能比的,但cmake也有其优点,比如通配等。

这里不是比较automake和cmake,但在工作当中,当一个三方库(sse_pb)既有动态库,又有静态库时,我们使用-lsse_pb连接时,系统会默认使用动态库,但我们需要优先使用静态库链接时, automake方式比较简单,但对于cmake,这里提供3种办法:

1.使用GCC编译参数

我们可以使用GCC的编译参数-Wl,-Bstatic-Wl,-Bdynamic来实现指定优先静态编译:

find_package(PkgConfig)
pkg_check_modules(brpc REQUIRED IMPORTED_TARGET brpc)
pkg_search_module(sse_pb REQUIRED sse_pb)
add_library(basic_search SHARED)
target_link_libraries(basic_search
  PRIVATE
  basic_search_common
  basic_search_util

...

  PUBLIC
  PkgConfig::brpc
  -Wl,-Bstatic
   ${sse_pb_LIBRARIES}
  -Wl,-Bdynamic
   )

值得注意的是 pkg_check_modules 会将 pc 文件内的链接 -lsse_pb 预先生成 /usr/lib64/libsse_pb.so 形式,导致即使添加GCC参数也不会生效。所以正确的方式是使用 pkg_search_module 来生成cmake的宏变量,一般存在两组值:

  • 用于普通情况, pkg-config 在使用--libs 选项调用时提供的信息,如<XXX> = <prefix>

  • 用于 pkg-config 在额外添加--static 选项调用时提供的信息 (<XXX> = <prefix>_STATIC)

对应的宏变量有下列这些:

<XXX>_FOUND:如果模块存在,则设置为 1
<XXX>_LIBRARIES:只有库 (没有 "-l")
<XXX>_LINK_LIBRARIES:库及其绝对路径
<XXX>_LIBRARY_DIRS:库的路径 (没有 "-L")
<XXX>_LDFLAGS:所有必需的链接器标志
<XXX>_LDFLAGS_OTHER:所有其他链接器标志
<XXX>_INCLUDE_DIRS:预处理器标记'-I'(没有'-I')
<XXX>_CFLAGS:所有必需的 cflags
<XXX>_CFLAGS_OTHER:其他编译器标志
<YYY>_VERSION:模块版本
<YYY>_PREFIX:模块的前缀目录
<YYY>_INCLUDEDIR:包含模块的目录
<YYY>_LIBDIR:模块的 Lib 目录

如上述 ${sse_pb_LIBRARIES} 的设置,就是将 sse_pb 库以静态优先的方式链接起来。

2.使用CMAKE_FIND_LIBRARY_SUFFIXES

我们还可以使用 find_library ,通过改变查找 .a 文件的顺序来获得静态文件优先规则,如下所示:

if (WIN32 OR MSVC)
   set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
elseif (UNIX)
   # 仅查找静态库,强制后缀为 .a
   set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")

   # 如果只是优先查找静态库,保证 .a 后缀在前面即可,把默认的后缀加上
   # set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
endif()

find_library(sse_pb_lib sse_pb)
target_link_libraries(basic_search
   ${sse_pb_lib}
)

3.写死路径

在这种 set_property 方式下,主要依赖路径:

add_library(sse_pb STATIC IMPORTED)
set_property(TARGET sse_pb PROPERTY IMPORTED_LOCATION /path/to/libsse_pb.a)
target_link_libraries(basic_search sse_pb)

其中 /path/to/libsse_pb.a 就可以随意发挥了。

从上可以看出,最灵活也是最优雅的还是第一种方式,可以依赖pkg-config建立很好的环境变量,从而进行静态编译。

在rhel安装高版本nodejs

背景

RHEL8 尝试使用 slidev 的时候,安装提示node版本至少 >=14.0 ,而当前其nodejs版本是:

rpm -q nodejs nodejs-10.23.1-1.module_el8.4.0+645+9ce14ba2.x86_64

所以需要升级nodejs版本。

安装过程

在正常情况下,一般要么通过源代码编译解决,要么通过已打包好的安装包或对应软件仓库源来解决。这里介绍一种更加简单的方式。

首先,查看nodejs在当前仓库源下可用的版本:

```

sudo dnf module list nodejs Last metadata expiration check: 4:02:13 ago on Mon 27 Feb 2023 11:11:34 AM CST. CentOS Stream 8 - AppStream Name Stream Profiles Summary nodejs 10 [d] common [d], development, minimal, s2i Javascript runtime nodejs 12 common [d], development, minimal, s2i Javascript runtime nodejs 14 common [d], development, minimal, s2i Javascript runtime nodejs 16 common [d], development, minimal, s2i Javascript runtime nodejs 18 [e] common, development, minimal, s2i Javascript runtime

Extra Packages for Enterprise Linux Modular 8 - x86_64 Name Stream Profiles Summary nodejs 13 default, development, minimal Javascript runtime nodejs 16-epel default, development, minimal Javascript runtime

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled ```

其最高版本是18,那么需要先reset,然后设置18,命令行如下:

```

sudo dnf module reset nodejs -y sudo dnf module enable nodejs:18 -y ```

设置好对应高版本源后,开始安装:

```

sudo dnf install nodejs -y ```

即可完成nodejs版本的升级。

在容器下编译的一些问题解决

在Ubuntu20.04下编译zookeeper时,发现如下错误:

... Duplicate cpuset controllers detected. ... Picking /sys/fs/cgroup/cpuset ...

此错误是一个警告,,正常来说不应该引起编译失败,但因为zookeeper将javacc的输出作为了META-INF/MANIFEST.MF里面的version_info,导致这堆警告也被打入到MANIFEST.MF里面,这样jar包的检测机制认为这个jar包是有问题的。

解决方案有几种:

1,放宽jar包的检测机制:看上去后面运行这个jar包都会带着这个信息;

2,解决这个警告信息:看上去靠谱一些。

发现从代码层解决,需要升级新的openjdk,当前这个版本openjdk对容器化的"支持"不是很好,于是只能改日志等级,但我不想改变现在的代码和配置,采用如下命令行设置即可:

export JAVA_HOME=/usr/lib/jvm/java
export ANT_HOME=/usr/share/ant
export PATH=${ANT_HOME}/bin:${JAVA_HOME}/bin:${PATH}
export JAVA_TOOL_OPTIONS="-Xlog:disable -Xlog:all=warning:stderr:uptime,level,tags"

整个过程虽然有一些曲折,但最终解决了问题。

Monthly Archives

Pages

Powered by Movable Type 7.9.4