CentOS 7.9 / Debian 11 源码编译redis 7.0.5
Table of Contents
如果你手上没有合适的干净的Linux系统,可以用docker容器来编译
# centos7
docker pull centos:centos7.9.2009
# debian 11(bullseye是系统代号)
docker pull debian:bullseye
下载解压
由于Redis是一个开源的软件,它的源码在github上就有,所以我们下载的时候去它的github的Releases中下载你需要的版本就行。
在Releases中复制源码包链接,用wget下载
wget https://github.com/redis/redis/archive/refs/tags/7.0.5.tar.gz
解压→进入解压后的文件夹→查看文件夹中的文件
tar -zxf 7.0.5.tar.gz
cd redis-7.0.5
关于编译
C/C++语言编写的程序,源码编译都是用make
命令编译,然后用make install
命令安装,但关键是在编译之前需要生成一个“Makefile”文件,然后make
命令才能根据这个Makefile来编译,目前生成Makefile的方法有两种:
- 1、AutoTools方式:如果源码中有
configure
文件,那就代表用的是AutoTools(包含autoconf
,autotools
,libtool
)方式,configure
文件就是编写程序的人用autoconf
和automake
这两个工具生成的,我们执行./configure
即可生成Makefile,当然configure也是有很多参数的,你可以执行./configure --help
来查找有哪些参数,结合程序官方文档或网上资料,给定你需要的参数再来执行configure命令,一般来说会有一个--prefix=/path/to/xxx/
用于指定把编译好的程序安装到哪个文件夹中; - 2、cmake方式:cmake会根据源码包中的CMakeLists.txt文件生成Makefile。
以上两种方式,无论是哪种,都需要gcc
(C语言编译器,是GNU Compiler Collection的缩写,中文翻译GNU编译器集合),如果你的程序是C++写的,还需要g++
(C++编译器),并且还需要pkg-config
(用于自动给编译器添加合适的编译选项)。
编译
根据关于编译中的说法,编译一个软件,首先我们需要生成一个Makefile文件,但是我们用ls -l
查看一下前面解压好的源码文件,可以发现它里面已经有一个Makefile文件了
total 264
-rw-rw-r-- 1 root root 37069 Sep 22 03:42 00-RELEASENOTES
-rw-rw-r-- 1 root root 51 Sep 22 03:42 BUGS
-rw-rw-r-- 1 root root 5027 Sep 22 03:42 CODE_OF_CONDUCT.md
-rw-rw-r-- 1 root root 2634 Sep 22 03:42 CONTRIBUTING.md
-rw-rw-r-- 1 root root 1487 Sep 22 03:42 COPYING
drwxrwxr-x 7 root root 4096 Sep 22 03:42 deps
-rw-rw-r-- 1 root root 11 Sep 22 03:42 INSTALL
-rw-rw-r-- 1 root root 151 Sep 22 03:42 Makefile
-rw-rw-r-- 1 root root 6888 Sep 22 03:42 MANIFESTO
-rw-rw-r-- 1 root root 22441 Sep 22 03:42 README.md
-rw-rw-r-- 1 root root 106545 Sep 22 03:42 redis.conf
-rwxrwxr-x 1 root root 279 Sep 22 03:42 runtest
-rwxrwxr-x 1 root root 283 Sep 22 03:42 runtest-cluster
-rwxrwxr-x 1 root root 1578 Sep 22 03:42 runtest-moduleapi
-rwxrwxr-x 1 root root 285 Sep 22 03:42 runtest-sentinel
-rw-rw-r-- 1 root root 1695 Sep 22 03:42 SECURITY.md
-rw-rw-r-- 1 root root 14005 Sep 22 03:42 sentinel.conf
drwxrwxr-x 4 root root 4096 Sep 22 03:42 src
drwxrwxr-x 11 root root 4096 Sep 22 03:42 tests
-rw-rw-r-- 1 root root 3055 Sep 22 03:42 TLS.md
drwxrwxr-x 8 root root 4096 Sep 22 03:42 utils
所以,这就不需要用./configure
或cmake
的方式来生成Makefile文件了,而是直接make
就可以,但由于没有./configure
来配置参数,所以参数需要直接传给make
,具体有哪些参数呢?你只能看源码文件夹中的Readme.md文件了。
我总结了一下,大概有以下选项
# 开启tls,主要是让客户端和服务器端连接时可以使用tls协议(体现为连接时使用`rediss://`协议)
BUILD_TLS=yes
# 生成systemd的service文件,主要是能让你使用systemctl来管理redis的启动停止重启
USE_SYSTEMD=yes
# 指定安装目录
PREFIX=/usr/local/redis/
假设我三个选项都使用(你可以只挑你想要的选项),那么编译语句如下(如果在docker里面,就不要用USE_SYSTEMD)
# 编译
make BUILD_TLS=yes USE_SYSTEMD=yes
# 安装,PREFIX选项用于指定安装位置,必须放在make install中,放在make中是不会起作用的
make install PREFIX=/usr/local/redis/
其实,make和make install是可以结合起来的,不是用&&
,而是直接make install
make BUILD_TLS=yes PREFIX=/usr/local/redis/ USE_SYSTEMD=yes install
安装依赖
但是上面语句不能直接运行,需要先安装依赖,make和gcc是编译C语言程序必须的,而openssl11是因为启用了tls,以下是centos7系统安装依赖
yum update -y
yum install -y epel-release
yum update -y
yum install -y make
yum install -y gcc
# 如果你使用了BUILD_TLS=yes,需要安装openssl
yum install -y openssl11 openssl11-devel
# 如果你使用了USE_SYSTEMD=yes选项,在centos7中要安装systemd-devel
yum install -y systemd-devel
如果你是debian系统(我这里用的debian 11),则要安装这些依赖
apt update -y
apt install -y make
apt install -y gcc
# 如果没有会提示not found,但不影响最终编译
apt install -y pkg-config
# 如果你使用了BUILD_TLS=yes,需要安装openssl
apt install -y openssl libssl-dev
# 如果你使用了USE_SYSTEMD=yes选项,在debian中要安装libsystemd-dev
apt install -y libsystemd-dev
解决错误
安装完依赖后,再次运行编译安装语句(如果在docker中则去掉最后一个选项USE_SYSTEMD=yes
)
make BUILD_TLS=yes PREFIX=/usr/local/redis/ USE_SYSTEMD=yes install
如果是在debian中,上边的编译安装应该已经完成了,但如果在centos7中,很不幸,编译会失败,它会报两个错误
ssl.c:46:25: fatal error: openssl/ssl.h: No such file or directory
#include <openssl/ssl.h>
zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory
#include <jemalloc/jemalloc.h>
找不到ssl.h是因为程序默认是去/usr/include/
下找要包含的标准头文件的,而openssl/ssl.h
是在redis代码里include
指令指定的,但是我们去/usr/include/
下查找openssl,你会发现只有openssl11(而不是“openssl”),这是因为我们安装的是openssl11,所以它肯定找不到,我们只需要做一个软链接到相同目录就行,相当于给它换个名字
ln -s /usr/include/openssl11/openssl/ /usr/include/openssl
如果以下命令能输出ssl.h,则说明上述软链接正确了
ls -l /usr/include/openssl/ | grep ssl.h
然后清理一下再重新make,但是要用make distclean
而不是用make clean
来清理,主要是因为它里面带一些依赖包,deps文件夹中,它们的缓存也要被同时清理。
清理之后,我们再来重新编译(如果在docker中则去掉最后一个选项USE_SYSTEMD=yes
)
make BUILD_TLS=yes PREFIX=/usr/local/redis/ USE_SYSTEMD=yes install
这次你会发现它又报另一个错
/usr/bin/ld: cannot find -lssl
/usr/bin/ld: cannot find -lcrypto
collect2: error: ld returned 1 exit status
make[1]: *** [redis-server] Error 1
make[1]: Leaving directory `/root/redis-compile/redis-7.0.5/src'
make: *** [all] Error 2
我们看上边的报错,主要是前两行,找不到-lssl
和-lcrypto
,这两个是什么呢?-l
表示它是动态链接库,动态链接库都是以“lib”开头,以“.so”结尾的文件,而中间部分就是除去“-l”的部分,即-lssl
和-lcrypto
对应的动态链接库名称分别为“libssl.so”和“libcrypto.so”,也就是说编译的时候无法找到“libssl.so”和“libcrypto.so”这两个文件。
此时我们可以用find / -name libssl.so
全局搜索一下,发现它是个软链接,位于/usr/lib64/openssl11/
中,如下所示
[root@52fa920152b6 redis-7.0.5]# ls -l /usr/lib64/openssl11/
total 0
lrwxrwxrwx 1 root root 22 Oct 13 19:24 libcrypto.so -> ../libcrypto.so.1.1.1k
lrwxrwxrwx 1 root root 19 Oct 13 19:24 libssl.so -> ../libssl.so.1.1.1k
但由于它们在openssl11文件夹中,是无法被编译器搜索到的,我也尝试过把/usr/lib64/openssl11/
这个地址写入到/etc/ld.so.conf
文件中并执行ldconfig
,但是没有用,一样报找不到-lssl
和-lcrypto
。
所以我们换种方法,直接把它们做个软链接到/usr/lib64/
,名字必须是libcrypto.so
和libssl.so
,而之前的/usr/lib64/openssl11/
就可以删掉了,我觉得它没啥用了
ln -s /usr/lib64/libcrypto.so.1.1.1k /usr/lib64/libcrypto.so
ln -s /usr/lib64/libssl.so.1.1.1k /usr/lib64/libssl.so
rm -rf /usr lib64/openssl11
现在我们用make distclean
清理一下,然后再次重新编译(如果在docker中则去掉最后一个选项USE_SYSTEMD=yes
)
make BUILD_TLS=yes PREFIX=/usr/local/redis/ USE_SYSTEMD=yes install
可以发现已经不报错了,最后几句如下
Hint: It's a good idea to run 'make test' ;)
INSTALL redis-server
INSTALL redis-benchmark
INSTALL redis-cli
由于我们指定安装到/usr/local/redis/
中,redis编译好的可执行文件会被安装到redis目录下的bin目录中
root@459873c023b6:~/redis3-7.0.5# ls -l /usr/local/redis/
total 4
drwxr-xr-x 2 root root 4096 Oct 15 14:10 bin
root@459873c023b6:~/redis3-7.0.5# ls -l /usr/local/redis/bin/
total 29336
-rwxr-xr-x 1 root root 7569488 Oct 15 14:10 redis-benchmark
lrwxrwxrwx 1 root root 12 Oct 15 14:10 redis-check-aof -> redis-server
lrwxrwxrwx 1 root root 12 Oct 15 14:10 redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 7513992 Oct 15 14:10 redis-cli
lrwxrwxrwx 1 root root 12 Oct 15 14:10 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 14948984 Oct 15 14:10 redis-server
如果没有指定安装目录,则会直接安装到/usr/local/bin/
中
[root@52fa920152b6 redis-7.0.5]# ls -l /usr/local/bin/ | grep redis
-rwxr-xr-x 1 root root 5228768 Oct 13 20:15 redis-benchmark
lrwxrwxrwx 1 root root 12 Oct 13 20:15 redis-check-aof -> redis-server
lrwxrwxrwx 1 root root 12 Oct 13 20:15 redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 5445536 Oct 13 20:15 redis-cli
lrwxrwxrwx 1 root root 12 Oct 13 20:15 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 11482712 Oct 13 20:15 redis-server
疑问
- 疑问一:使用指定安装目录的安装方式,为什么redis目录下只有一个bin,没有etc?因为etc一般是放配置文件用的。没错,这也正是redis这个
make install
的缺陷,其实你解压源码后,源码文件夹下就已经有一个redis.conf配置文件了,只不过安装的时候是不会把它安装过去的,你需要自己在redis目录下建一个etc文件夹并把配置文件复制进去; - 疑问二:使用了
USE_SYSTEMD=yes
选项,但是好像也没什么作用呀,也没有redis.service文件生成?是的,github提issue问过,有个redis源码贡献者回答如下,反正就是说不会生成redis.servcie文件
> This flag only controls compilation: it enables systemd support logic and linking against libsystemd.
> 意思是这个选项只是用于启用了systemd相关的支持逻辑以及针对libsystemd的链接。
配置systemd启动方式
前面疑问里说过,使用USE_SYSTEMD=yes
并不会自动生成redis.service,所以我们必须自己创建这个文件,这个文件从哪儿来呢?很简单,你用包管理器安装一个redis,它会自动有一个redis.service文件在/etc/systemd/system/
下。
以下是我在debian 11中用apt install redis
安装后,在/etc/systemd/system/
下找到的redis.service文件中的内容,如果你源码安装指定了位置,那改改ExecStart中的路径就行了
[Unit]
Description=Advanced key-value store
After=network.target
Documentation=http://redis.io/documentation, man:redis-server(1)
[Service]
Type=notify
ExecStart=/usr/bin/redis-server /etc/redis/redis.conf --supervised systemd --daemonize no
PIDFile=/run/redis/redis-server.pid
TimeoutStopSec=0
Restart=always
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=2755
UMask=007
PrivateTmp=yes
LimitNOFILE=65535
PrivateDevices=yes
ProtectHome=yes
ReadOnlyDirectories=/
ReadWritePaths=-/var/lib/redis
ReadWritePaths=-/var/log/redis
ReadWritePaths=-/var/run/redis
NoNewPrivileges=true
CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE
MemoryDenyWriteExecute=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictRealtime=true
RestrictNamespaces=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# redis-server can write to its own config file when in cluster mode so we
# permit writing there by default. If you are not using this feature, it is
# recommended that you replace the following lines with "ProtectSystem=full".
ProtectSystem=true
ReadWriteDirectories=-/etc/redis
[Install]
WantedBy=multi-user.target
Alias=redis.service
这样的话就可以用systemctl start redis
的方式来启动了。
配置非sysemd启动方式
如果没有用USE_SYSTEMD=yes
,可以运行redis源码目录下的utils/install_server.sh
来安装,它会自动安装一个SysV启动脚本到/etc/init.d/
中,它做的事情如下
[root@52fa920152b6 utils]# ./install_server.sh
Welcome to the redis service installer
This script will help you easily set up a running redis server
Please select the redis port for this instance: [6379]
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf]
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log]
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379]
Selected default - /var/lib/redis/6379
Please select the redis executable path [/usr/local/bin/redis-server]
Selected config:
Port : 6379
Config file : /etc/redis/6379.conf
Log file : /var/log/redis_6379.log
Data dir : /var/lib/redis/6379
Executable : /usr/local/bin/redis-server
Cli Executable : /usr/local/bin/redis-cli
Is this ok? Then press ENTER to go on or Ctrl-C to abort.
Copied /tmp/6379.conf => /etc/init.d/redis_6379
Installing service...
Successfully added to chkconfig!
Successfully added to runlevels 345!
Starting Redis server...
Installation successful!