ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis开机自启动全攻略:覆盖Linux、Windows、macOS与Docker

Redis开机自启动全攻略:覆盖Linux、Windows、macOS与Docker 1. Redis 的进程模型为什么装好了不等于开机能用1.1 前台进程、后台进程与守护进程的关系我在不少项目里都遇到过同一个场景开发环境跑着 Redis 好好的重启一次服务器所有人傻眼Redis 没起来。客户那边 ET 的监控面板上一片飘红一查原因无非是当初安装 Redis 的人只执行了redis-server启动而 Redis 本身并没有被系统服务管理器接管。要理解开机自启动这件事先得搞清楚 Redis 的运行方式。Redis 的redis.conf里有一个daemonize参数默认值是yes。这参数的意思是Redis 进程启动后会 fork 一个子进程然后把父进程退出让自己脱离当前终端成为一个后台守护进程。听起来是不是很像开机自启动其实差远了。daemonize yes只解决进程不会随着终端关闭而退出的问题它管不着服务器重启后谁会重新拉起这个进程。操作系统重启之后所有进程全部归零Redis 不会平白无故自己长出来。另一个容易混淆的概念是前台运行。如果你执行redis-server的时候没有指定配置文件或者配置里写了daemonize noRedis 就会一直占据当前终端窗口。这个状态下的 Redis 一旦你关掉 SSH 会话就可能跟着挂掉。所以很多老的运维同学喜欢用nohup redis-server 这种方式把进程从 HUP 信号里保护起来。这招应急可以用但作为长期方案完全不合格因为服务器一重启什么 nohup 都不管用。1.2 操作系统开机自启动的本质谁在替你拉起进程开机自启动的真正含义是在系统启动流程中由某个系统级组件去主动执行启动指令。在 Windows 上这个组件叫服务控制管理器SCM在 Linux 上早期是 SysV init现在基本是 systemd在 macOS 上则是 launchd。它们的工作方式大同小异系统开机 - 启动网络/文件系统等基础服务 - 读取服务定义 - 按依赖关系拉起对应的程序 - 进程退出后还能自动重新拉起。所以你会发现一个关键点想让 Redis 开机自启动本质上是让服务管理器认识 Redis、知道 Redis 在哪儿、该用什么参数启动。Redis 本身不自带注册为系统服务的功能除了 Windows 官方社区编译版里提供的redis-server --service-install之外其他平台都需要你手工去写一个服务定义文件。这个认知特别重要。我见过有人折腾了半天的 crontab 写reboot还有人干脆写了个 shell 脚本放到/etc/rc.local结果现代 Linux 发行版上/etc/rc.local默认根本没有执行权限。这些都是绕了远路。归根结底不同平台有各自的正规管道顺着服务管理器走才能保证依赖顺序、崩溃重启、日志采集这些能力。下面就把 Linux、Windows、macOS、Docker 四种场景一个个拆开讲。2. Linux 下 systemd最主流也最容易踩坑的方案2.1 手写一个 redis.service 服务单元在 Linux 上用 systemd 管理 Redis核心任务就一个在/etc/systemd/system/目录下写一个名为redis.service的单元文件然后告诉 systemd 启用它。我用过很多种写法这里给一个我验证过、稳定跑了好几年的版本[Unit] DescriptionRedis In-Memory Data Store Afternetwork.target Wantsnetwork.target [Service] Userredis Groupredis Typesimple ExecStart/usr/local/bin/redis-server /etc/redis/redis.conf ExecStop/usr/local/bin/redis-cli -h 127.0.0.1 -p 6379 shutdown Restarton-failure RestartSec5 LimitNOFILE10240 [Install] WantedBymulti-user.target先看[Unit]部分。Afternetwork.target的意思是在网络服务准备好之后再启动 Redis因为 Redis 要监听 TCP 端口网络没起来容易出问题Wants则是弱依赖声明让 systemd 尽量把 network 也带上。这两个字段看着不起眼但真实环境里跳过它们偶尔会因为启动顺序问题导致 Redis 绑定端口失败。再看[Service]部分重点来了。Userredis和Groupredis指定了 Redis 进程的运行身份。安全角度上讲绝不能让 Redis 以 root 身份跑万一哪天漏洞被人利用等于直接给攻击者送了一个 root shell。所以标准做法是先创建专门的 redis 用户useradd -r -s /sbin/nologin redis mkdir -p /var/lib/redis # 持久化数据目录 chown redis:redis /var/lib/redis chmod 750 /var/lib/redisExecStart里写的是启动命令。很多人问为什么用/usr/local/bin/redis-server这么长的绝对路径而不是直接用redis-server。因为 systemd 在执行服务时使用的 PATH 环境变量非常精简未必包含你自定义安装的目录。用绝对路径能省掉一堆命令找不到的奇怪问题。同样地后面接的/etc/redis/redis.conf是配置文件你要是装 Redis 时装在了别的路径这里务必改成实际路径。ExecStop用的是redis-cli shutdown这很重要。Redis 在收到shutdown命令时会先把内存里的数据按照持久化配置落盘到 RDB 或 AOF然后才退出。如果你图省事用kill -9进程直接被干掉缓冲区里的数据可能没来得及写全。systemd 在停止服务时本来会先发 SIGTERM但通过redis-cli shutdown能触发更优雅的收尾流程。Restarton-failure表示进程异常退出时自动拉起RestartSec5是两次重启之间的等待时间。LimitNOFILE10240调整文件描述符上限Redis 在连接数高的时候很吃文件描述符这个限制要提前放宽。最后[Install]里的WantedBymulti-user.target声明了这个服务属于哪个启动级别写到这才能用systemctl enable把它挂到开机启动序列里。写完后执行两行命令让服务生效并启动systemctl daemon-reload systemctl enable --now redisdaemon-reload是必须的因为你新增或修改了服务单元文件systemd 需要重新加载enable是设置开机自启--now是立即启动。2.2 daemonize 参数这个看似无关的配置决定生死这一节我单独拿出来讲因为它是 Linux 上配 systemd 最容易翻车的点没有之一。很多人的 Redis 配置文件里daemonize yes保持默认值没动然后照着网上的教程写了个Typesimple的单元文件。启动的时候systemctl start redis好像成功了但紧接着systemctl status redis就会报错说是服务启动失败进程找不到。问题出在进程模型上。daemonize yes会让 Redis 自己 fork 出子进程并让父进程退出。systemd 的Typesimple模式认为ExecStart 进程还活着服务就是运行中。可实际上 ExecStart 那个父进程很快就退出了systemd 一看主进程都死了这服务肯定是崩了啊。然后开始各种报错甚至触发Restarton-failure反复拉起拉起又失败陷入死循环。标准解法是在 Redis 配置里把daemonize改成nodaemonize no这样 Redis 进程就以前台方式运行systemd 能直接监控这个进程的生命周期。进程挂没挂systemd 一清二楚重启策略也能精准触发。还有一种更精细的写法用supervised systemd。在 Redis 配置里加上supervised systemd这会让 Redis 启动并初始化完成后主动通过 systemd 的 notify 机制告诉 systemd我准备好了。配合服务单元文件里的Typenotifysystemd 就不会在 Redis 还在加载数据的时候误判为启动超时。不过Typenotify对新手而言复杂度偏高我自己在很多存量服务器上迁数据量大的实例时才用它。一般场景下daemonize noTypesimple已经够稳定选哪套方案看你对控制精度的需求。2.3 权限、路径和配置文件的三个隐藏雷区第一个雷区是数据持久化目录的权限。Redis 启动时要写 RDB 文件如果你用的是 AOF 持久化模式还要写 AOF 日志。这些文件默认写在启动命令所在的目录也就是配置里dir参数指定的路径。很多教程说修改 redis.conf 的 dir 参数为 /var/lib/redis但忘了改目录 owner。结果 redis 用户根本没有那个目录的写权限Redis 启动时报错也无法写数据严重时直接拒绝启动。排查这类问题最快的办法是看日志文件。日志位置在配置里的logfile参数可能是文件路径也可能是输出到标准输出。用 systemd 跑的话标准输出会被 journald 捕获直接执行journalctl -u redis -n 50就能看到真实的报错信息比盯着systemctl status猜要高效得多。第二个雷区是配置文件里的bind地址。默认配置通常只监听 127.0.0.1这本来是安全的但如果你在外面加了内网 IP要确认这个地址在服务器上真的存在。我遇到过一次改配置时手滑写了个不存在的 IPRedis 启动后默认端口 6379 根本没人监听应用连不上一脸懵。第三个雷区是升级 Redis 版本之后配置文件格式的兼容性。比如你原来用的是 Redis 4 的配置直接换到 Redis 7 的二进制有些旧参数可能已经废弃或改名Redis 会按下划线警告方式处理甚至直接启动失败。每次升级前先备份配置文件再逐项对照新版本的默认配置别图省事一把梭。3. Windows 下注册成服务一条命令解决但细节不能省3.1 服务注册命令的参数与执行步骤Windows 平台的情况特殊。Redis 官方并没有正式发布 Windows 版本社区里常用的大多是编译好的 Windows 分支。好在这些发行包基本都保留了服务注册功能用法也统一通过redis-server自带的--service-install参数。首先以管理员身份打开 CMD 或 PowerShell然后切换到 Redis 解压目录执行redis-server.exe --service-install redis.windows.conf --service-name Redis这里redis.windows.conf是配置文件如果路径带空格一定要用双引号把整个路径包起来。--service-name Redis是指定注册到 Windows 里的服务名后面带不带这个参数默认服务名就是 Redis。执行成功后Windows 服务列表里会出现一个名为 Redis 的服务。但注意这一步只是注册服务还没有启动。你可以通过下面这条命令启动它redis-server.exe --service-start或者直接在 Windows 服务管理工具里把服务状态切到启动。无论哪种方式服务启动后会在后台运行跟你在终端里手动启动 Redis 的效果一致。验证是否监听成功用redis-cli ping看到返回 PONG 就说明通了。需要修改 Redis 配置的时候改完配置文件记得重启服务否则新配置不会生效。重启命令同样是二选一redis-server.exe --service-restart或者用 Windows 自带指令Restart-Service Redis。3.2 服务启动失败时如何定位问题Windows 上 Redis 服务注册成功不代表万事大吉。我见过最典型的失败现场是配置文件里写了logfile redis.log而没有写绝对路径服务启动时的工作目录又不是解压目录导致日志文件写到奇怪的地方去了甚至因为权限不足直接启动失败。遇到服务启动不起来第一步去 Windows 事件查看器里找。在事件查看器 - Windows 日志 - 系统里筛选来源为 Redis 或者 Service Control Manager 的事件基本能看到一条明确的失败原因。第二步要确认配置文件的路径是否对得上。很多人喜欢把 Redis 目录放在 C 盘但配置文件里面引用的一些相对路径比如dir ./在系统服务环境下解析的是系统目录不是你的解压目录。所以最稳妥的做法是把dir配置成绝对路径比如dir C:\redis-data并确保这个目录存在且当前系统账号有写入权限。还有一个容易忽略的点服务运行账号。默认情况下Windows 服务会用 LocalSystem 账号运行。LocalSystem 权限很高但网络访问模型跟普通用户不完全一样。如果你的 Redis 需要被局域网内其他机器访问bind配置要监听 0.0.0.0 或者具体网卡 IP不能只留 127.0.0.1。否则从别的机器连过来直接 Connection Refused。3.3 更新配置后的重启规范Windows 服务模式下很多人在改完 redis.conf 之后习惯性地去服务管理器里点击停止再点启动。这当然可以但我更推荐用命令行里的--service-restart一步到位。为什么因为服务重启涉及优雅退出直接杀进程再启动可能丢失未持久化的数据--service-restart会走 Redis 的 shutdown 流程让数据落盘后再拉起新进程更安全。卸载服务的命令也别记混了redis-server.exe --service-uninstall这在调试完想清理环境的时候很实用。注意卸载前先把服务停止不然系统会提示服务还在运行无法完成卸载。Windows 服务模式下如果你想让 Redis 在系统重启后自动启动很多社区版在注册服务时默认就是自动类型。不放心的可以去services.msc里检查 Redis 服务的启动类型是否为自动。如果是手动右键改成自动就行。这个操作虽然跟纯命令行相比有点非技术但确实是最直观的确认方式。4. macOS 下两条路brew services 与手工 launchd 配置4.1 brew services 的一键托管macOS 上用 Homebrew 安装 Redis 是最常见的路径。如果你是这么装的那么恭喜自启动只需要一条命令brew services start redis这条命令做了什么它背后其实是 Homebrew 帮你生成了一份 launchd 的 plist 配置文件并注册到了用户的 LaunchAgents 目录里。launchd 会在你开机登录的时候自动加载这个服务实现自启动。同时brew services还会负责进程崩溃后的自动拉起。好处显而易见不用手动写 XML 配置文件不用记launchctl那一堆子命令所有状态可以通过brew services info redis查看。如果你只是想在本地开发环境用 Redis这条路是最省心的。但注意brew services start有一个特性它是跟当前用户登录绑定的。如果 Mac 开机之后停在登录界面没有登录任意用户这个服务不会启动。对个人开发机来说这无所谓因为本来就要登录才能用但如果想把 Redis 当成系统级后台服务任何用户未登录就启动那就需要换用 launchd 的系统级配置方式。4.2 手工编写 LaunchDaemon plist 的完整过程自己写 launchd 配置不算难核心就是写一个 plist 文件放在/Library/LaunchDaemons/目录下然后通过launchctl加载。注意放/Library/LaunchDaemons/需要 root 权限加载后以系统守护进程方式运行放~/Library/LaunchAgents/则不需要 root但只在用户登录后生效。Redis 如果作为服务器上的常驻服务应该用前者。我通常这么写文件名com.redis.server.plist?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keyLabel/key stringcom.redis.server/string keyProgramArguments/key array string/usr/local/bin/redis-server/string string/usr/local/etc/redis.conf/string /array keyRunAtLoad/key true/ keyKeepAlive/key true/ keyStandardOutPath/key string/usr/local/var/log/redis/redis.stdout.log/string keyStandardErrorPath/key string/usr/local/var/log/redis/redis.stderr.log/string /dict /plistLabel是这个服务的唯一标识符最好用反域名格式避免和系统自带服务冲突。ProgramArguments数组里写的是要执行的命令和参数注意写成全路径。如果你用的是 Apple Silicon 的 MacHomebrew 安装路径是/opt/homebrew要相应改成/opt/homebrew/bin/redis-server。RunAtLoad设为 true表示 launchd 加载这个配置时立刻启动KeepAlive设为 true表示进程退出后 launchd 会自动把它再拉起来。最后两个路径是标准输出和标准错误的重定向目标。配置文件写好之后先用plutil -lint检查语法plutil -lint /Library/LaunchDaemons/com.redis.server.plist然后执行sudo launchctl load -w /Library/LaunchDaemons/com.redis.server.plistload是加载-w表示覆盖禁用标记如果之前标记过 Disabled 这次也会强制生效。同样的道理卸载用sudo launchctl unload -w /Library/LaunchDaemons/com.redis.server.plist。需要提醒的是macOS 的launchctl在新版本里引入了launchctl bootstrap和launchctl bootout这类新命令老的load/unload虽然有所保留但已算 deprecated。不过考虑到大量存量脚本还在用老写法以及无所谓对错你只要选一种风格保持一致就行。尝鲜的话官方更推荐sudo launchctl bootstrap system /Library/LaunchDaemons/com.redis.server.plist我没打算在这里挑起新旧命令之争实践上两种都能跑通新手选老命令的教程多排错也容易。最后还是要提 daemonize 那个问题。macOS 的 launchd 同样要求进程在前台运行所以redis.conf里的daemonize必须保持no。否则你加载完 launchd 服务后会发现Redis 进程起来了但 launchd 认为服务已经退出KeepAlive 不断帮你重启形成一套诡异的僵尸循环。5. Docker 部署场景restart 策略就是你的自启动开关5.1 三种常用 restart 策略的取舍容器化部署和上面几种方式最大的不同在于你不直接管理 Redis 进程而是让 Docker 守护进程dockerd管理容器。因此自启动这个需求在 Docker 世界里被翻译成restart策略。最简单的方式是在docker run里加参数docker run -d --name redis-server \ -p 6379:6379 \ --restart unless-stopped \ -v redis-data:/data \ redis:7如果你用 docker-compose则在 compose 文件里写services: redis: image: redis:7 container_name: redis-server restart: unless-stopped ports: - 6379:6379 volumes: - redis-data:/datarestart策略有四个值值得比较。no是默认值容器退出后不自动重启on-failure只在容器因错误退出退出码非 0时重启适合那些你希望报错就拉起的场景always是无条件重启容器只要不是被人主动docker stop的退出就拉起甚至 Docker 守护进程自己重启后也会把这容器重新拉起来unless-stopped和always的区别在于如果你手动docker stop了容器docker 重启时不会把它拉起来这点对开发环境特别友好。我自己的生产环境推荐用unless-stopped。为什么不用always因为运维维护时有手动停容器做整治的诉求。比如要把 Redis 版本从 6 升级到 7需要docker stop旧容器然后重新建新容器。用always策略的话只要系统或 dockerd 重启过停止状态的旧容器会被再次拉起升级过程中容易出现两个容器抢 6379 端口的情况。用unless-stopped就不会有这个烦恼手动停掉就是停掉等新容器替上来。容器启动后如果发现策略设错了不需要重建直接改docker update --restart unless-stopped redis-server命令短小精悍运维救场很管用。5.2 容器自启后的常见连锁问题容器本身开机自启只是第一步。我遇到过好多回容器设了restart: alwaysDocker 也开了自启但服务器重启后 Redis 还是连不上。一查问题出在容器网络或数据卷挂载上。常见现象之一Docker 守护进程启动时会按策略拉起所有容器但容器内部 Redis 绑定的是容器自己的 6379 端口转发到宿主机的 6379。如果宿主机上还有别的东西也占用了 6379比如你之前直接redis-server裸跑了一个那容器端口映射失败Redis 对外不可用。所以迁移到容器方案时先把裸进程清理干净。常见现象之二数据卷权限问题。Redis 官方镜像默认以 redis 用户运行数据写到/data目录。如果你用-v /data/redis:/data挂载了宿主机目录而这个目录权限是 root:root 且权限为 755容器内的 redis 用户没有写权限AOF 落地失败Redis 启动后没多久就崩。处理方式很简单提前执行chown -R 999:999 /data/redis999 是 redis 官方镜像里的 UID直接对应容器内 redis 用户。还有一个容易被忽略的点是时区和时区语言环境。容器默认时区是 UTCRedis 日志里的时间戳跟你的业务时间差八个小时排查问题时看着日志时间对不上会误导人。加环境变量TZAsia/Shanghai可以顺手解决。不是核心问题但真碰上了挺浪费时间的。6. 自启动是否生效按这套验证清单别等故障了才检查6.1 各平台快速验证命令配置好自启动之后最糟糕的做法是以为配好了然后不管。服务器重启一次通常要花几十秒到几分钟如果 Redis 没起来业务早就开始跌单了。所以在配置完的当下一定要做一次完整的验证。Linux 下验证分为两步。先看 enable 状态systemctl is-enabled redis输出应该是enabled。再看运行状态systemctl status redis确认Active: active (running)。如果你压根没重启服务器is-enabled已经能说明开机自启是否注册成功。Windows 下用命令行查询服务状态sc query Redis看STATE是否显示为RUNNING再检查启动类型sc qc Redis输出里的START_TYPE如果是AUTO_START开机自启就稳了。macOS 下如果你用的是 launchd执行sudo launchctl list | grep redis能看到进程 PID 说明正在运行。用 brew services 的话brew services list直接看最后一列started表示已经在跑。Docker 场景下验证容器和策略docker inspect --format{{.HostConfig.RestartPolicy.Name}} redis-server输出unless-stopped或always都是对的。这个命令在故障排查时非常常用你要快速知道一个容器是否带自动恢复策略一条命令就能看到结果。6.2 排查顺序与日志阅读方法如果验证发现 Redis 没起来按照下面的顺序排查基本能覆盖绝大多数问题。先确认二进制和配置文件路径是否存在。很多人换了 Redis 版本比如从源码编译升级到二进制包结果服务配置里还写着旧路径文件不存在服务当然起不来。这个最简单ls一下就行。再确认配置里daemonize是否设置正确。前面反复强调过systemd 和 launchd 场景都必须为noWindows 服务模式实际上也要求以前台方式受服务管理器监控。这个错只影响自启场景手动跑redis-server /path/conf反而一切正常所以特别容易被漏判。然后是端口占用问题。如果netstat -tlnp | grep 6379显示端口被另一个进程占用新启动的 Redis 会绑定失败报错信息里很可能写着Address already in use。解决方法是找到占用进程是遗留的旧 Redis 就停掉是别的应用就调整端口。再往下是持久化目录权限。这个问题在 Linux 和容器场景里都有确认dir参数指向的目录对运行用户可写。怎么看切换到运行用户执行touch 目录/test能写入就说明没权限问题。最后是内存问题。内存极小的 VPS 上跑 Redis如果配置文件里maxmemory没设置Redis 在内存耗尽时可能被 OOM Killer 直接 killsystemd 里的Restarton-failure会帮你拉起但拉起来又被杀形成死循环。这时看日志会发现进程反复退出journalctl里甚至能看到内核的 OOM 记录。解决方式是给 Redis 设置合理的maxmemory和淘汰策略。日志阅读其实比想象中简单。Linux 用journalctl -u redis -n 100Windows 用事件查看器macOS 用sudo log show --predicate process redis-serverDocker 用docker logs redis-server。基本看一眼最新几十行报错问题定位就八九不离十了。最怕的是盯着服务状态猜那永远在浪费时间。我自己实际配置过很多台机器的 Redis 自启动从裸机到容器都有。说句实在话真正把服务跑崩的从来不是不会配置而是配置完就不管了。开服务、设开机自启、改配置这三步做完之后花三十秒做一轮验证后面能省掉无数个被报警电话叫醒的深夜。
返回列表