ARTICLE DETAIL

资讯详情

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

Codex 通过 SSH 连接服务器集群:从密钥配置到批量任务调度

Codex 通过 SSH 连接服务器集群:从密钥配置到批量任务调度 1. 为什么要把 codex 放到服务器集群上跑很多人第一次接触 codex都是在本地终端里敲几行命令跑个小脚本感觉挺顺手。可一旦任务规模上来比如要批量处理几十个仓库、要跑长时间的重构任务、要在多个节点之间同步上下文本地那台笔记本就扛不住了。风扇狂转、内存告急、网络一断任务全废这种体验我相信不少人都经历过。把 codex 通过 SSH 接到服务器集群上本质上是把计算和交互这两件事拆开。你的本地机器只负责输入指令、看输出真正的推理调用、文件读写、批量任务调度全部交给集群里的节点去干。这样做有几个直接好处一是算力和网络更稳定集群通常有更好的带宽和更长的在线时间二是任务可以后台常驻你关掉笔记本它照样跑三是多节点并行时可以按机器分工避免单点瓶颈。这里说的 SSH就是 Secure Shell一种加密的远程登录协议。你可以把它理解成一根加密的网线把你本地终端和远端服务器连起来你在本地敲的命令实际是在远端执行。codex 本身是个命令行工具它并不关心自己跑在哪台机器上只要那台机器能访问到它需要的模型端点和文件系统就行。所以codex 通过 SSH 连接服务器集群这件事拆开看就是两件事先把 SSH 通道打通再让 codex 在远端正常跑起来。适合读这篇的人大概有三类一是刚上手 codex、想把它从本地搬到服务器的新手二是已经在用 SSH 但总在认证、环境变量、路径上翻车的同学三是需要管理多台机器、想做批量登录和自动化传输的运维向用户。下面我会按通道搭建—环境落地—批量管理—排错这条线把每一步的坑和理由都讲清楚。2. SSH 通道的搭建从密钥到免密登录2.1 密钥认证为什么比密码靠谱先说结论只要条件允许一律用密钥认证别用密码。原因很实在——密码可以被暴力猜、可以在脚本里明文写、可以在多台机器间复用导致一处泄露全盘皆输而密钥是一对数学上关联的公钥和私钥私钥留在你本地公钥放到服务器登录时服务器用公钥验证你手里的私钥全程私钥不出本地。生成密钥的命令很标准ssh-keygen -t ed25519 -C your_emailexample.com这里我特意用ed25519而不是老掉牙的rsa。ed25519 密钥更短、生成更快、安全性也更好现在主流系统都支持。执行后会问你保存路径默认是~/.ssh/id_ed25519直接回车即可然后会问你要不要设 passphrase密码短语。我的建议是如果是个人机器设一个如果是自动化脚本用的机器可以不设但一定要保证这台机器本身的物理和账号安全。生成完之后把公钥推到服务器ssh-copy-id -i ~/.ssh/id_ed25519.pub userserver_ipssh-copy-id这个工具会自动帮你把公钥追加到远端~/.ssh/authorized_keys里并处理好权限。如果系统里没有这个命令比如某些精简版就手动来cat ~/.ssh/id_ed25519.pub | ssh userserver_ip mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys注意~/.ssh目录权限必须是 700authorized_keys必须是 600。这两个权限如果不对SSH 会直接拒绝密钥登录而且报错信息往往很含糊很多人卡在这里半天找不到原因。2.2 配置文件让多机登录不再靠记忆如果你只有一台服务器记个 IP 无所谓。但集群意味着多台机器靠脑子记 IP、端口、用户名迟早出错。正确做法是写~/.ssh/configHost node1 HostName 192.168.1.101 User deploy Port 22 IdentityFile ~/.ssh/id_ed25519 Host node2 HostName 192.168.1.102 User deploy Port 22 IdentityFile ~/.ssh/id_ed25519 Host cluster-* User deploy IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking accept-new配好之后你只需要ssh node1就能登录不用再敲一长串。StrictHostKeyChecking accept-new这个选项值得说一下默认情况下第一次连接一台新机器SSH 会问你要不要信任它的指纹交互式场景没问题但脚本里就会卡住。设成accept-new表示首次自动接受、之后如果指纹变了仍然报警兼顾了自动化和安全。2.3 免密登录在集群里的正确姿势单机免密只是第一步集群场景真正想要的是从跳板机免密到所有节点。常见做法是在一台管理节点上生成密钥然后把公钥分发到所有工作节点。手动一台台ssh-copy-id太慢用循环for ip in 192.168.1.101 192.168.1.102 192.168.1.103; do ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy$ip done如果节点多到几十上百台就该上配置管理工具了但对大多数中小规模集群一个 for 循环足够。分发完之后验证一下ssh -o BatchModeyes deploy192.168.1.101 echo okBatchModeyes的作用是禁止任何交互式提问如果免密没配好它会直接失败而不是弹密码提示。这个技巧在写自动化脚本时特别有用能帮你快速判断到底通没通。3. 让 codex 在远端真正跑起来3.1 远端环境的三道坎Node、路径、权限SSH 通了不代表 codex 能用。codex 这类 CLI 工具通常依赖 Node.js 运行时远端如果没有装或者版本太老第一步就卡住。先确认node -v npm -v如果版本低于工具要求一般需要较新的 LTS 版本别急着用系统包管理器装因为系统自带的往往版本偏旧。推荐用版本管理工具比如 nvmcurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install --lts nvm use --lts这里有个大坑通过 SSH 非交互式执行命令时~/.bashrc往往不会被加载。也就是说你手动登录时node能用但脚本里ssh node1 codex ...就报 command not found。原因是 nvm 的初始化写在.bashrc里而非交互式 shell 默认读的是.bash_profile或根本不读。解决办法有两个一是把 nvm 初始化那段也加到~/.bash_profile或~/.profile二是在命令里显式 sourcessh node1 source ~/.nvm/nvm.sh node -v我个人更推荐第一种一次配好后面所有脚本都省心。3.2 全局安装与 PATH 的隐形陷阱装 codex 一般用 npm 全局安装npm install -g package-name装完之后codex命令能不能直接用取决于 npm 的全局 bin 目录在不在 PATH 里。查一下npm config get prefix假设输出是/home/deploy/.nvm/versions/node/v20.11.0那可执行文件在它的bin子目录下。如果这个路径不在 PATH你就会遇到明明装成功了却找不到命令的经典问题。临时解决export PATH$PATH:$(npm config get prefix)/bin永久解决就是把这行写进.bashrc和.bash_profile。我踩过这个坑不止一次尤其是在切换 Node 版本之后prefix 变了PATH 却没更新命令就消失了。3.3 认证信息在远端怎么放才安全codex 要访问模型服务通常需要一个认证 token。这个 token 放哪、怎么传是安全上的关键点。绝对不要做的事把 token 硬编码进脚本、提交到 Git、或者写在会同步的 dotfile 里。推荐做法是用环境变量并且只在需要时注入。比如在远端的~/.bashrc里不写死而是放在一个单独的、权限 600 的文件里# ~/.codex_env export CODEX_TOKENyour_token_here然后chmod 600 ~/.codex_env在需要时source ~/.codex_env。这样即使别人能看到你的.bashrc也拿不到 token。提示如果你在本地已经登录过 codex很多工具会把凭证存在本地配置目录里。搬到远端时不要直接把这个目录整个复制过去最好在远端重新走一次登录流程让凭证在远端本地生成避免跨机器的凭证泄露风险。3.4 用 SSH 直接跑一条 codex 命令环境齐了之后最简单的验证方式是ssh node1 source ~/.bashrc codex --version能打印版本号说明通道和环境都通了。接下来跑实际任务ssh node1 cd /data/project source ~/.bashrc codex run 重构这个模块的错误处理注意cd必须在远端执行不能写成cd /data/project ssh node1 ...那样cd是在本地生效的远端还是在默认目录。这个顺序问题新手极容易搞反。如果任务时间长建议配合nohup或tmux避免 SSH 断线导致任务被杀ssh node1 cd /data/project source ~/.bashrc nohup codex run ... /data/logs/codex.log 21 这样即使你本地断网远端任务照跑回头tail -f看日志就行。4. 多节点批量操作与文件同步4.1 批量登录执行同一命令集群场景下在每台机器上做同一件事是高频需求。最朴素的方式还是 for 循环for host in node1 node2 node3; do echo $host ssh $host source ~/.bashrc codex --version done但这样有个问题如果某台机器卡住整个循环就停在那。加个超时for host in node1 node2 node3; do echo $host ssh -o ConnectTimeout5 -o BatchModeyes $host source ~/.bashrc codex --version || echo $host 失败 doneConnectTimeout5表示 5 秒连不上就放弃|| echo保证单台失败不影响整体。这套组合我在实际运维里用得最多简单可靠不需要额外依赖。4.2 用 rsync 做代码和产物的同步codex 在远端跑输入代码得先传上去输出产物得拉回来。scp能用但不够好推荐rsync因为它支持增量传输——只传变化的部分大项目下能省大量时间。推送到远端rsync -avz --exclude node_modules --exclude .git ./project/ node1:/data/project/-a是归档模式保留权限、时间戳等-v显示过程-z压缩传输。--exclude排除掉不需要同步的目录node_modules这种在远端重装往往比传过去更快。从远端拉回产物rsync -avz node1:/data/project/output/ ./local_output/注意rsync 的源路径结尾有没有斜杠含义完全不同。./project/表示同步目录里的内容./project表示同步目录本身。这个细节坑过无数人传完发现多套了一层目录就是这里的问题。4.3 多节点任务分发的思路如果任务本身可以并行比如把 100 个文件分给 5 台机器各处理 20 个那就该做任务分发。最简单的做法是按文件列表切片files(file_*.txt) total${#files[]} nodes(node1 node2 node3 node4 node5) per$(( (total ${#nodes[]} - 1) / ${#nodes[]} )) for i in ${!nodes[]}; do start$(( i * per )) chunk(${files[]:start:per}) printf %s\n ${chunk[]} /tmp/chunk_$i.txt scp /tmp/chunk_$i.txt ${nodes[$i]}:/tmp/ ssh ${nodes[$i]} source ~/.bashrc while read f; do codex run \处理 \$f\; done /tmp/chunk_$i.txt done wait这段脚本做了几件事算出每台机器分多少、切片、把清单传过去、让每台机器后台跑、最后wait等全部结束。加wait是 shell 里做并行的经典组合比装一堆调度框架轻量得多。当然生产环境更推荐用正经的作业调度系统但理解这个原理对排查问题很有帮助。5. 那些让人抓狂的报错与排查链路5.1 认证失败到底卡在哪一环Permission denied (publickey)是 SSH 最常见的报错但它背后可能有好几种原因得一层层排现象可能原因排查命令一直提示输密码公钥没进 authorized_keyscat ~/.ssh/authorized_keys直接拒绝目录或文件权限不对ls -ld ~/.ssh ~/.ssh/authorized_keys指定了密钥仍失败IdentityFile 路径错ssh -v node1看实际用的密钥只有某台机器失败该机 sshd 配置禁用了密钥查远端 sshd 配置排查的万能工具是-vverbosessh -vvv node1它会打印整个握手过程你能清楚看到用了哪个密钥服务器接受了还是拒绝了卡在认证的哪一步。我遇到认证问题时第一反应永远是加-vvv比瞎猜快十倍。5.2 命令找不到与环境变量丢失前面提过非交互式 SSH 不加载.bashrc导致node、codex找不到。判断方法ssh node1 which node ssh node1 bash -lc which node如果第一条空、第二条有输出就坐实了是 shell 初始化的问题。bash -lc会以登录 shell 方式执行加载 profile 类文件。长期方案还是把环境变量配到.bash_profile里。5.3 连接超时与网络抖动Connection timed out通常不是认证问题而是网络层没通。排查顺序先ping看主机是否可达再telnet server_ip 22或nc -zv server_ip 22看端口是否开放。如果 ping 通但端口不通多半是远端防火墙或 sshd 没监听在预期端口上。网络抖动导致的断连可以用 SSH 的心跳保活Host * ServerAliveInterval 30 ServerAliveCountMax 3意思是每 30 秒发一次心跳连续 3 次没响应就断开。这样能避免假连接——看着连着实际早断了命令发出去石沉大海。5.4 长任务被中断的补救如果任务跑到一半 SSH 断了进程被 SIGHUP 杀掉前面的工作全白费。补救手段就是前面说的nohup或tmux。我个人更偏爱tmux因为它能让你随时回到现场ssh node1 tmux new -s codex_task # 在 tmux 里跑任务 # 按 Ctrlb 然后 d 脱离 # 下次 ssh node1 后 tmux attach -t codex_task 回来tmux的好处是会话独立于 SSH 连接存在断线重连后任务还在输出也还在。跑长任务前先开 tmux已经成了我的肌肉记忆。6. 我踩过的坑和几条实用经验第一条经验永远先验证通道再验证环境最后才跑业务。很多人一上来就ssh node1 codex run ...报错了根本分不清是 SSH 的问题、环境的问题还是 codex 本身的问题。正确的顺序是三步走——先ssh node1 echo ok确认通道再ssh node1 codex --version确认环境最后才跑真正的任务。这样任何一步出错范围立刻缩小。第二条经验把远端的环境初始化写成一个脚本。比如在远端放一个~/init_env.sh里面 source nvm、设置 PATH、加载 token。之后所有 SSH 命令都先 source 它ssh node1 source ~/init_env.sh codex run ...这样环境配置只有一处改一次全生效比在每个命令里重复一堆 export 靠谱得多。第三条经验日志一定要落盘别只靠终端输出。SSH 会话一断终端里的输出就没了。所有长任务都重定向到文件配合tail -f实时看出问题还能回溯。日志文件建议按日期或任务名区分不然几天后一堆output.log你根本分不清哪个是哪个。第四条经验批量操作前先拿一台机器试。for 循环里如果命令写错几十台机器一起执行错误命令后果可能很严重。养成习惯先在单台上验证命令正确再放开到全集群。这个习惯帮我避免过好几次批量删错文件的惨剧。最后说个容易被忽略的点时区和编码。集群里不同节点如果时区不一致日志时间对不上排查问题时能把人逼疯。统一时区、统一 UTF-8 编码是集群环境的基本功。这些细节平时不起眼真出问题时才知道有多重要。
返回列表