ARTICLE DETAIL

资讯详情

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

UE在Windows交叉编译Linux打包:工具链配置与RunUAT命令详解

UE在Windows交叉编译Linux打包:工具链配置与RunUAT命令详解 手上只有一台Windows打包机项目却要出Linux版本——这个局面我在几个团队里都碰到过。运维不想再养一台Linux构建机发行版升级还容易把老构建环境搞崩最后能落地的方案基本只有一个在Windows上装UE自带的Linux交叉编译工具链用同一套RunUAT命令行把Linux包打出来。这条路本身不清奇但它有一套自己的脾气SDK版本对不上会直接报错退出路径大小写混用要等到运行期才炸产物拷到Linux上还会因为可执行权限丢了根本起不来。下面我把整条链路拆开讲从工具链装在哪、Target怎么配到RunUAT参数逐条说明再到实际踩过的坑和排查顺序尽量把每一步的为什么讲透。已经在Windows上跑通Windows包、准备加一个Linux平台输出的同学或者打算把Linux打包接进流水线的人都可以照着走一遍。1. 先想清楚为什么是交叉编译而不是再开一台Linux构建机1.1 交叉编译在UE里指的是什么交叉编译这个词拆开看就是在A平台上生成B平台能跑的程序。放到UE的场景里A是WindowsB是Linux x86_64或者Linux Arm64。你在Windows上敲一条命令行UnrealBuildTool会调起一套专门为Linux目标编译的clang/lld把C源码编成ELF格式的可执行文件再用Windows上的cooker把资源烘焙成Linux能读的pak最后整包落到一个目录里。这里有个概念必须先说清楚UE的交叉编译工具链只覆盖运行时Target也就是Game、Client、Server这几类。你不能指望用它在Windows上编出一个Linux版Editor。工具链里带的是目标的libc、libc头文件和运行库没有editor那套依赖UBT在Target类型上就会把你挡回去。所以如果你的工作流需要在Linux上开编辑器改关卡这套方案帮不了你得老老实实装Linux版引擎。但只要你的流程是策划美术在Windows上做内容CI出包交叉编译就是最省事的那条路。另一个容易被忽略的点是UE的Linux工具链用的是clang lld 自带libc不是系统gcc。这是它敢跨平台的前提——目标侧的C运行库是跟着工具链一起分发的不依赖目标机装了什么版本的libstdc。代价就是你得接受它挑clang版本不能随便换成gcc-arm那一套自己搭的链子。1.2 三种出Linux包的方案横向对比我在不同团队里试过三种做法各自的适用场景差别挺大列个表更直观方案首次搭建成本构建速度环境一致性适用场景Windows交叉编译低装个工具链即可中等受限于Windows磁盘IO高工具链随引擎版本锁定CI已在Windows团队无Linux运维Linux原生构建中要装引擎源码版高Linux文件系统和并行IO更友好中容易受发行版升级影响团队本身用Linux开发或需要编辑器容器化Linux构建高要维护镜像和GPU直通高高大型团队构建量大要求可复现交叉编译真正的优势不在速度而在你不需要多一台机器、也不需要多一套运维知识。项目规模在两三百人天以内、一周出包几次的节奏这条路完全够用。真正让它变痛苦的是构建量上来之后UE的Linux链接阶段本身就是单线程的Windows的NTFS在小文件读写上也确实不如ext4出一次全量Shipping包一个多小时很常见。1.3 哪些情况下我会劝你别走这条路有几种场景我踩过之后会直接建议换方案。第一项目里有大量依赖Linux原生扩展的插件比如要调系统级的库或者自定义的so交叉编译链里没有对应的sysroot包你会陷在补依赖的循环里。第二需要频繁出服务器包并且要做真实压力测试的交叉编译出来的server包只能验证能否启动真实性能表现还是要在目标环境跑。第三团队已经在用Linux做CI的硬拐回Windows等于把已有的缓存和流水线全推翻不划算。判断标准其实很简单如果你现在的痛点是没机器交叉编译如果痛点是构建太慢那得解决机器和缓存不是换平台能救的。2. 工具链装在哪、装哪个版本、UBT怎么找到它2.1 工具链包的命名规则与版本对照Epic把Linux交叉编译工具链单独发布命名规则是v序号_clang-版本-宿主基线.zip。这个命名里三个信息都有用序号是Epic自己的迭代号clang版本决定你能用的C特性宿主基线centos7或者后来的rockylinux8决定它能编出兼容多老glibc的二进制。几个常见引擎版本对应的工具链大致是这样但以你本地引擎为准引擎版本工具链包名UE 4.27v19_clang-11.0.1-centos7UE 5.0 - 5.2v20_clang-13.0.1-centos7UE 5.3v22_clang-16.0.6-centos7UE 5.4 及以后v23_clang-18.1.0-rockylinux8权威来源不是这张表是引擎源码里Engine/Source/Programs/UnrealBuildTool/Platform/Linux/LinuxPlatformSDK.cs中那个ExpectedSDKVersion常量。打开文件搜一下写的是什么版本你就去下什么版本一个字符都不能差。工具链包从Epic的CDN拉文件名就是包名加.zip。解压位置建议统一放一个专门目录比如C:\UnrealToolchains\因为zip里自带一层同名的顶层目录解压完路径就是C:\UnrealToolchains\v22_clang-16.0.6-centos7。别解压到引擎目录里引擎升级的时候你的工具链就跟着一起被覆盖或者被清掉了这坑我见过两次。2.2 LINUX_MULTIARCH_ROOT与SDKVersion的双向对齐UBT找工具链走的是环境变量LINUX_MULTIARCH_ROOT。设置成工具链顶层目录结尾不要带反斜杠路径里别带空格和中文。setx LINUX_MULTIARCH_ROOT C:\UnrealToolchains\v22_clang-16.0.6-centos7setx写完要新开一个终端才生效这点很坑——很多人设完在同一个cmd里继续跑报找不到SDK然后开始怀疑人生。验证方法echo %LINUX_MULTIARCH_ROOT% dir %LINUX_MULTIARCH_ROOT%\x86_64-unknown-linux-gnu\bin另一边是引擎里的期望版本。位置在Engine/Config/Windows/WindowsEngine.ini的[/Script/LinuxTargetPlatform.LinuxTargetSettings]段下的SDKVersion。这个值和工具链包名必须一致。改了引擎里的这个值UBT在启动时会先做一次校验不一致直接报SDK version mismatch连编译都不会开始。为什么会有这个双重校验因为Epic要在引擎升级时强制你换工具链。clang大版本变了libc的ABI、默认的C标准、链接行为都可能变混着用会有各种诡异的运行期崩溃。所以这个校验看着烦实际上是在帮你避一个很难查的坑。2.3 装完先跑一个最小验证别急着开大项目先做两件事。第一件直接调工具链里的clang看目标三元组%LINUX_MULTIARCH_ROOT%\x86_64-unknown-linux-gnu\bin\clang.exe --version输出里能看到Target: x86_64-unknown-linux-gnu就说明链子本身是完整的。如果这个目录里还带aarch64-unknown-linux-gnueabi说明这个版本的工具链同时支持Linux Arm64后面要用-platformLinuxArm64的时候就不用再折腾了。第二件拿引擎自带的模板工程做一次最小打包Development配置、不带pak只要-build这一步能过就说明编译链通了Engine\Build\BatchFiles\RunUAT.bat BuildCookRun ^ -projectD:\Work\MyProject\MyProject.uproject ^ -noP4 -platformLinux -clientconfigDevelopment -build这一步的意义是把环境问题和项目问题隔离开。模板工程过不了一定是你工具链或者引擎配置的问题模板工程过了、自己的项目过不了那就是项目侧有Windows专属的东西去第3节找。3. 项目侧要改的东西其实不多但每一处都不能漏3.1 Target.cs与Build.cs里的平台相关开关UE的Target.cs里有个PlatformAllowList/PlatformDenyList但实际操作中更常见的坑是bOverrideBuildEnvironment和自定义的LinkerSubsystem。如果你在Target里硬写了Windows的子系统或者附加链接参数Linux构建会直接失败。检查方式就是全局搜一遍.Target.cs和.Build.cs里的Target.Platform UnrealTargetPlatform.Win64这类判断确认每个分支都有Linux的对应处理。Build.cs里需要重点看的几个字段PublicAdditionalLibraries和PublicDelayLoadDLLsWindows的.lib和.dll在Linux下无效不能用同一个模块规则简单套。要么按平台分支给.a/.so要么这个模块干脆不进Linux构建。RuntimeDependencies这里列的文件会被原样拷进产物路径大小写在Windows上不敏感、在Linux上敏感见第5节。bEnableExceptions、bUseRTTI跨平台能不能用异常和RTTI得看你在Linux侧有没有对应的运行库支持改动前先确认工具链里libc是编了异常支持的。我一般的做法是在Build.cs里先加一个平台判断把Linux的依赖先留空跑一次构建UBT报什么缺什么再一个个补。这比一次性把Windows那堆库名换成猜出来的Linux库名要快得多。3.2 插件与第三方库的筛选交叉编译最容易被插件卡住。判断一个插件能不能进Linux构建看三点它的Build.cs里有没有平台白名单它依赖的第三方库有没有Linux预编译版本它的二进制资源是不是只针对Windows烤的。如果某个插件确实只服务Windows最干净的做法是在打包命令行里把它排除而不是去改插件源码-PlatformLinux -ClientConfigShipping -Cook -Build ^ -SkipCook -IterativeCooking排除插件的参数是-DisablePlugins多个用加号分隔-DisablePluginsSomeWinOnlyPluginAnotherOne这个参数写在-build之前UBT和cooker都会遵守。我踩过一次坑只在cook阶段禁了插件编译阶段没禁结果编到一半报找不到模块。所以编译和cook两阶段用的是同一份清单一次写全。第三方库方面能走源码的最省事——把源码编进模块里跟着工具链走不用担心目标机缺so。实在只能给预编译的优先选静态库.a其次是.so并且确认它是用不高于你工具链基线glibc编出来的。用较新的glibc编的.so放到较老的发行版上会报GLIBC_2.xx not found这个问题在交叉编译场景里特别隐蔽因为你本机根本没有那个老发行版可以测。3.3 默认RHI与Shader格式的提前配置这一步不做包能出来但跑到目标机上大概率是黑屏或者直接崩。Linux桌面默认走Vulkan你得保证Vulkan的shader被烤进pak里。在DefaultEngine.ini里加[/Script/LinuxTargetPlatform.LinuxTargetSettings] TargetedRHIsSF_VULKAN_SM6 TargetedRHIsSF_VULKAN_SM5在编辑器里也有对应入口Project Settings → Platforms → Linux → Targeted RHIs。两个都留是因为目标机驱动千奇百怪某些老显卡只支持到SM5只烤SM6的包在那台机器上就是黑屏。多烤一份shader会让cook时间变长但对上线包来说是值得的。同时确认Project Settings → Packaging里Use Pak File和Use IoStore都开着。IoStore在UE5里是默认它把资源打成.utoc.ucas文件数大幅下降启动更快也顺带把文件数量太多导致大小写问题暴露的概率降下来。4. RunUAT一次完整打包参数逐条拆开看4.1 BuildCookRun的必选参数一条能用的Linux Shipping打包命令长这样我把它拆成几行方便看Engine\Build\BatchFiles\RunUAT.bat BuildCookRun ^ -projectD:\Work\MyProject\MyProject.uproject ^ -noP4 ^ -platformLinux ^ -clientconfigShipping ^ -cook -allmaps -build -stage -pak -compressed ^ -compressionformatsOodle ^ -unattended -nop4 -utf8output -nocompileeditor ^ -archive -archivedirectoryD:\Builds\MyProject逐条解释几个容易搞混的-noP4和-nop4前者告诉UAT不要碰Perforce后者是给编辑器层的。两个都写没坏处CI环境里尤其要写不然UAT会尝试连P4连不上就卡在那等超时。-platformLinux决定编译和cook的目标平台。注意它不决定-serverplatform服务端要单独指定。-cook -allmaps全量烤所有关卡。想增量就把-allmaps去掉配合-iterate。-stage把产物整理到Saved\StagedBuilds\Linux下这一步会组装出MyProject.sh、MyProject\、Engine\三个顶层项。-pak打pak。不加的话产物是一堆散文件方便调试但启动慢。-archive -archivedirectory把staged结果复制一份到你指定的目录。建议永远加-archive因为Saved\StagedBuilds下一次打包会被覆盖CI里拿产物必须从archive目录取。-unattended不弹任何对话框CI必须加否则报错时进程挂着等输入流水线超时半小时才失败。-nocompileeditor跳过编辑器模块编译节省大量时间。4.2 服务器包与客户端包分开出的写法服务器包和客户端包往往要分开出甚至只出服务端。这时候用-noclient或者-noserver控制Engine\Build\BatchFiles\RunUAT.bat BuildCookRun ^ -projectD:\Work\MyProject\MyProject.uproject ^ -noP4 -platformLinux -serverplatformLinux ^ -clientconfigShipping -serverconfigShipping ^ -server -noclient ^ -cook -build -stage -pak -archive -archivedirectoryD:\Builds\Server这里有个细节要注意-platform是客户端平台-serverplatform是服务端平台。如果你只出服务端理论上-platform也要写UAT用它来决定cook出来的资源格式。两个都写Linux最保险。另外服务端配置下Target.cs里的TargetType.Server会走一套不同的模块裁剪逻辑很多只在客户端跑的系统渲染、音频设备会被排除。这意味着服务端能编过不代表客户端能编过反过来也一样CI里两条链路都要跑一遍。4.3 Cooking卡住时先看这三处痛点排行榜第一名是cook卡住。日志里刷一堆LogCook: Display: Cooked...之后突然长时间没输出先按顺序看这三处第一看Saved\Logs\Cook-*.log末尾有没有ShaderCompileWorker相关行。Vulkan shader的首次编译非常耗时几千个材质变体编十几分钟很正常。加了TargetedRHIsSF_VULKAN_SM6和SM5之后时间会翻倍这是预期行为不是卡死。第二看是不是在等网络。项目里如果引用了不在本地的资源或者有插件的Build.cs里写了RuntimeDependencies指向网络路径cooker会尝试访问然后超时。把-verbose加上再跑一次日志里能直接看到具体文件。第三看DDC。-ddc参数不写的话走默认的本地DDC路径在%LOCALAPPDATA%\UnrealEngine\Common\DerivedDataCache。如果DDC目录被清过或者权限有问题cooker会从头全量重烤表现同样是卡住。团队多人协作的话把DDC指到共享盘会好很多但共享盘的IO要是跟不上反而比本地慢这个得实测。5. 产物落地Linux之前这几个坑基本都会遇到5.1 从Windows拷到Linux最先丢的是可执行权限和行尾这是最经典的一个。你在Windows上打包成功把Linux目录压缩传到Linux服务器解压然后执行./MyProject.sh报Permission denied。原因是zip、tar打包在Windows侧不会保留Unix的可执行位。修复就两条命令chmod x *.sh chmod x MyProject/Binaries/Linux/MyProject第二个chmod容易被忘因为.sh脚本只是壳真正启动的二进制是Binaries/Linux/下那个没有扩展名的文件它没执行权限脚本里那行$DIR/MyProject/Binaries/Linux/MyProject就会失败。行尾问题更隐蔽。.sh脚本如果是CRLF行尾Linux执行时报/bin/sh^M: bad interpreter: No such file or directory这个报错信息很有误导性看着像找不到/bin/sh其实是/bin/sh后面跟了个\r。批量修sed -i s/\r$// *.shCI里更省事的做法是在归档那一步就用一条脚本把权限和行尾一起处理掉别留给部署的人手工做。5.2 路径大小写Windows不报Linux不认完整排查链路是这样的。你先看到的现象是包在Windows上开发跑得好好的拷到Linux上启动后某个资源加载失败日志里是LogStreaming: Warning: Failed to load file ...。第一步把日志里那个路径抄出来去产物目录里ls一遍。如果ls能找到同名的但大小写不同基本就确诊了。第二步回到源码里定位这个路径是从哪来的。常见的三个来源Build.cs的RuntimeDependencies列表、C里的#include、配置ini里写的相对路径。第三步为什么Windows上没报因为NTFS不区分大小写#include myheader.h在文件实际叫MyHeader.h的时候照样能找到编译一点问题没有。UBT在Windows上也不会去校验大小写。等这个包跑到Linux上区分大小写的文件系统一查就找不到。第四步修法是在源码侧把大小写改成和磁盘文件完全一致。别指望用工具自动修因为自动修没法判断是文件名写错了还是引用名写错了改错一边更麻烦。我一般用IDE的重命名重构功能改这样引用会跟着一起更新。第五步验证方式是在Windows上做一次全量cook加-pak然后在Linux上跑起来走一遍所有关卡。别只测主菜单测试关卡、存档读取、DLC资源这些边缘路径才是重灾区。还有一个变体.so文件的依赖名。如果你用了第三方.soLinux加载器对文件名是严格区分的libFoo.so和libfoo.so是两个东西。5.3 非ASCII路径和空格引擎路径、项目路径、工具链路径这三个里面任何一个带中文或者空格交叉编译都会出问题。原因不是UE不支持Unicode路径是中间那层工具——clang的driver、链接脚本、shader编译器的临时文件处理——在处理非ASCII时会出各种意料之外的截断。排查顺序很简单看日志里第一条报错的文件路径如果有??或者乱码就是这个问题。解决方案就是把整条链路挪到纯ASCII路径下比如D:\UE\Engine、D:\Work\MyProject、C:\UnrealToolchains。这不只是交叉编译的要求Linux原生构建同样建议这么做。5.4 体积Debug符号和strip第一次出Linux Shipping包的人往往会惊讶于产物的体积。Windows下Shipping包可能三四百兆Linux这边一个二进制就奔着二百兆去。原因是Linux的Shipping配置默认仍然带调试符号。瘦身用工具链自带的objcopy%LINUX_MULTIARCH_ROOT%\x86_64-unknown-linux-gnu\bin\objcopy.exe ^ --strip-debug MyProject MyProject.stripped用--strip-debug而不是--strip-all。区别在于--strip-all会把.symtab也去掉崩溃时拿到的堆栈就只剩地址了--strip-debug只去掉.debug_*段.symtab保留符号化还能做。操作建议把原始二进制和strip后的都留着原始的那个用于日后符号化崩溃堆栈。strip这一步能不能接进UAT可以在-build之后加一个PostBuild步骤或者干脆在CI的归档脚本里做。我第一次做的时候是手工strip的第二次就写进脚本了因为手工做一定会忘。6. 在目标机上跑起来依赖、启动脚本与自检清单6.1 目标机需要哪些系统库交叉编译出来的包不是完全自包含的。引擎自带的第三方soSDL、OpenAL、PhysX这些都在Engine/Binaries/ThirdParty/Linux/下被staged进产物了但下面这些必须由目标机的系统提供图形相关libX11、libXcursor、libXrandr、libXi、libGL、libvulkan1、libfontconfig1音频libasound2、libpulse0基础运行库libcglibc、libstdc、libm、libpthread、libdl、librt在Debian系上一条命令能装齐sudo apt-get install -y libx11-6 libxcursor1 libxrandr2 libxi6 libgl1 \ libfontconfig1 libasound2 libpulse0 libvulkan1关键约束是glibc版本。工具链是以某个较老的glibc为基线编的所以编出来的二进制在较新的发行版上一般能跑反过来不行。目标机的glibc版本如果低于工具链基线启动会直接报version GLIBC_2.xx not found。确认方法ldd --version | head -1拿到版本号后和工具链的基线对一下。这也是为什么我一直建议在CI里保一台和目标环境一致的验证机别等到客户那边才炸。6.2 启动方式与常见启动失败信号启动就走staged目录下的脚本cd /opt/myproject chmod x MyProject.sh ./MyProject.sh -log加-log能直接在终端看到日志输出排查阶段非常有用。几个典型失败信号和对应方向现象大概率原因处理方向Permission denied少了可执行位chmod xbad interpreter: ^M脚本是CRLF行尾sed -i s/\r$//error while loading shared libraries: libX11.so.6系统缺库按6.1安装version GLIBC_2.xx not found目标机glibc过老换工具链基线或升级目标系统启动后黑屏、有声音Vulkan shader缺失或驱动不支持检查TargetedRHIs配置启动即崩无输出崩溃处理器拦截了找Saved/Crashes目录下的dump黑屏那个我要多提一句。表现是进程活着能听到声音窗口是黑的。九成是Vulkan shader没烤进去或者目标机的Vulkan驱动版本太老。加-vulkan参数强制走Vulkan可以确认是不是RHI选择的问题。6.3 上线前的自检清单出包之后我会按这个清单过一遍几分钟能省掉一轮沟通成本检查项方法通过标准可执行位ls -l *.sh MyProject/Binaries/Linux/MyProject有x权限行尾格式file *.sh显示ASCII text依赖完整性ldd MyProject/Binaries/Linux/MyProject | grep not found无输出启动冒烟./MyProject.sh -log -nullrhi进程起来且日志正常关卡加载走一遍主要关卡无Failed to load file存档读写新建、读取、删除存档均成功分辨率与全屏切换窗口模式和全屏无异常-nullrhi参数很实用它跳过渲染初始化用来验证非图形部分是否正常特别快。如果-nullrhi能起来、正常模式黑屏那问题一定在渲染侧如果-nullrhi都起不来那是资源或运行库的问题跟显卡无关。这个二分法能砍掉一半排查时间。7. 接进流水线参数化、增量与产物归档7.1 把命令行收敛成一个可复用脚本命令行参数一多就没人记得住也没人敢改。我的做法是写一个.bat把变化的部分抽成变量echo off set UE_ROOTD:\UE\Engine set PROJECTD:\Work\MyProject\MyProject.uproject set OUTD:\Builds\MyProject\%BUILD_NUMBER% set TOOLCHAINC:\UnrealToolchains\v22_clang-16.0.6-centos7 set LINUX_MULTIARCH_ROOT%TOOLCHAIN% set PATH%TOOLCHAIN%\x86_64-unknown-linux-gnu\bin;%PATH% call %UE_ROOT%\Build\BatchFiles\RunUAT.bat BuildCookRun ^ -project%PROJECT% ^ -noP4 -platformLinux -clientconfigShipping ^ -cook -build -stage -pak -compressed ^ -unattended -nop4 -utf8output -nocompileeditor ^ -archive -archivedirectory%OUT% if errorlevel 1 exit /b 1注意脚本里显式设了LINUX_MULTIARCH_ROOT和PATH。CI的agent进程环境变量经常和新开的交互式终端不一样靠系统级环境变量有时候读不到脚本里写死最稳。7.2 增量Cook与DDC的配合全量cook一次几十分钟一天出五次包就是好几个小时。能优化的点有三个。第一个是-iterate它让cooker复用上一次的cook结果。适合只改了几个资源的场景。但要注意改了引擎版本、改了shader格式配置、改了插件清单都不能用-iterate一定要全量。我有一次改了TargetedRHIs之后用增量结果部分资源还是旧的shader跑起来画面闪烁查了大半天。第二个是DDC共享。多人协作时把UE-SharedDataCachePath指向一个网络盘能省掉每个人的重复烘焙。但如果构建机的网络IO比本地机械盘还慢反而更差这个必须实测。第三个是只出变更平台。如果这次只改了一个默认值而两端都要出包可以考虑先出Linux验证完再出Windows不要无脑全平台全量。7.3 产物命名与版本追溯归档目录的命名带上构建号、平台、配置一眼能看出是什么D:\Builds\MyProject\20240612-137\Linux\Shipping\同时在产物里放一个buildinfo.txt记录引擎commit、项目commit、工具链版本、打包时间、打包机名。看起来是小事但线上出问题要回溯这个包到底是用哪版代码打的时没有这个文件你就得靠翻CI日志翻不到就只能重打一个包去对齐。这里有个细节工具链版本一定要记。因为工具链升级会改变生成的二进制同样的源码用不同工具链编出来的包行为可能不一样。我之前查一个只在特定客户端上复现的崩溃最后发现是两个包用了不同版本的工具链一个带断言一个不带白白多花了两天。8. 我自己踩过之后总结的几条经验工具链版本这件事我的做法是把它和引擎版本绑定管理在项目根目录放一个toolchain.txt写明当前用的包名CI启动时先比对不一致直接失败。这个检查只花两行脚本但能挡住某台构建机的环境变量被别人改过这类最难查的问题。第二件事是关于第一版验证顺序。我现在打Linux包的第一版一定是Development配置加-nopak产物是一堆散文件方便直接ls看目录结构、检查权限和大小写。等这个版本在目标机跑通了再切Shipping加-pak。反过来做的话一出问题你就得先解包再看效率差很多。第三件事验证机不要只准备一台。至少准备一台较新的发行版和一台较老的发行版因为glibc和Vulkan驱动这两个变量在真实用户环境里分布很广。交叉编译的最大风险就是我只在自己这台机器上测过一台验证机给不了你任何信心。最后提一个容易被忽略的小技巧./MyProject.sh脚本里能加自定义参数比如指定日志目录、指定配置文件路径。如果你的部署脚本需要传环境相关的参数改这个sh比改代码里读环境变量要灵活得多而且它不进二进制改完直接生效不用重新打包。
返回列表