{
    "id": 74,
    "uuid": "dca13bd4-9c2d-4bfd-85b2-4db8d6a99b5f",
    "share_token": "9MUZaq4Ws9TR",
    "title": "PHP 项目部署方式",
    "plain_text": "You:\nphp web项目，当前线上代码更新用的还是FTP文件替换的方式更新代码，如果不上容器，有别的更好的方式吗？\n\n---\n\nChatGPT:\n可以。不上容器，也比 FTP 覆盖文件强很多。推荐按成熟度分三档：\n\n方案 1：Git 拉代码部署，最低改造\n\n适合：小团队、单机 PHP 项目、暂时不想引入复杂 CI\/CD。\n\n流程\n\n服务器上保留项目 Git 仓库：\n\nBash\ncd \/var\/www\/project\ngit fetch origin\ngit checkout main\ngit pull\ncomposer install --no-dev --optimize-autoloader\nphp artisan migrate --force   # Laravel 才需要\nphp artisan config:cache       # Laravel 才需要\nsudo systemctl reload php-fpm\n优点\n\n比 FTP 安全、可追踪\n\n能知道线上当前是什么 commit\n\n可以快速回滚：\n\nBash\ngit checkout <previous_commit>\ncomposer install --no-dev --optimize-autoloader\nsudo systemctl reload php-fpm\n问题\n\n部署过程中可能出现“半更新”状态\n\n多台机器同步麻烦\n\n权限、缓存、依赖安装容易出问题\n\n适合作为第一步替代 FTP，但不是最终形态。\n\n方案 2：CI\/CD 自动部署，推荐起点\n\n适合：希望团队规范化，不想手动登录服务器。\n\n可以用：\n\nGitHub Actions\n\nGitLab CI\n\nJenkins\n\nGitea Actions\n\nCoding \/ 阿里云云效 \/ 腾讯 CODING\n\n基本逻辑\n\n开发合并到 main 或打 tag 后自动执行：\n\n拉代码\n\n安装依赖\n\n跑测试\n\n打包产物\n\n上传到服务器\n\n执行部署脚本\n\nreload PHP-FPM \/ Nginx\n\n例如用 GitHub Actions + SSH：\n\nYAML\nname: Deploy\n\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v4\n\n      - name: Install dependencies\n        run: composer install --no-dev --optimize-autoloader\n\n      - name: Deploy via SSH\n        uses: appleboy\/ssh-action@v1.0.3\n        with:\n          host: ${{ secrets.SERVER_HOST }}\n          username: ${{ secrets.SERVER_USER }}\n          key: ${{ secrets.SERVER_KEY }}\n          script: |\n            cd \/var\/www\/project\n            git pull origin main\n            composer install --no-dev --optimize-autoloader\n            sudo systemctl reload php-fpm\n优点\n\n不再人工 FTP\n\n有部署日志\n\n权限可控\n\n可以加测试、审批、通知\n\n适合团队协作\n\n问题\n\n如果只是 git pull 覆盖线上目录，仍然存在短暂不一致风险。\n\n方案 3：Release 目录 + 软链接切换，强烈推荐\n\n这是不上容器时比较稳的 PHP 部署方式。\n\n目录结构\nBash\n\/var\/www\/project\/\n  releases\/\n    20260615120000\/\n    20260615123000\/\n  shared\/\n    .env\n    storage\/\n    uploads\/\n  current -> \/var\/www\/project\/releases\/20260615123000\n\nNginx 指向：\n\nNginx\nroot \/var\/www\/project\/current\/public;\n部署流程\n\n每次发布新建一个 release 目录：\n\nBash\nRELEASE=\/var\/www\/project\/releases\/$(date +%Y%m%d%H%M%S)\n\ngit clone git@github.com:xxx\/project.git $RELEASE\ncd $RELEASE\n\ncomposer install --no-dev --optimize-autoloader\n\nln -s \/var\/www\/project\/shared\/.env $RELEASE\/.env\nln -s \/var\/www\/project\/shared\/storage $RELEASE\/storage\n\nln -sfn $RELEASE \/var\/www\/project\/current\n\nsudo systemctl reload php-fpm\n回滚\n\n只需要把 current 指回上一个 release：\n\nBash\nln -sfn \/var\/www\/project\/releases\/20260615120000 \/var\/www\/project\/current\nsudo systemctl reload php-fpm\n优点\n\n发布接近原子切换\n\n回滚很快\n\n不会出现 FTP 那种文件传一半线上就混乱的问题\n\n很适合 PHP-FPM + Nginx 架构\n\n这个是我最推荐的非容器方案。\n\n可以直接用的工具\n\n如果不想自己写部署脚本，可以看这些：\n\n工具\t适合场景\nDeployer\tPHP 项目非常适合，支持 release、shared、rollback\nCapistrano\tRuby 生态老牌部署工具，也可部署 PHP\nAnsible\t多服务器、配置管理、发布流程统一\nJenkins\/GitLab CI\t公司内部规范 CI\/CD\nGitHub Actions\t轻量、成本低、上手快\n\n其中 Deployer 很适合 PHP：\n\nBash\ncomposer require deployer\/deployer --dev\nvendor\/bin\/dep init\nvendor\/bin\/dep deploy production\n\n它天然支持：\n\nreleases\n\nshared files\n\nsymlink\n\nrollback\n\ncomposer install\n\ndeploy hooks\n\n我的建议\n\n你们现在是 FTP 替换，建议按这个路线走：\n\n第一阶段：马上替换 FTP\n\n用：\n\nGit + SSH 部署脚本\n\n目标是先解决：\n\n文件不可追踪\n\n人工替换容易漏文件\n\n无部署日志\n\n回滚困难\n\n第二阶段：标准化发布\n\n升级成：\n\nCI\/CD + releases 目录 + current 软链接\n\n这是不上容器时的主力方案。\n\n第三阶段：补齐工程规范\n\n增加：\n\nGit 分支策略：dev \/ staging \/ main\n\n测试环境自动部署\n\n线上发布需要 tag 或手动审批\n\n部署前自动备份\n\n数据库 migration 谨慎执行\n\n发布失败自动回滚\n\n发布通知到 Slack \/ 飞书 \/ 企业微信\n\n注意几个坑\n1. 不要把用户上传文件放在代码目录里\n\n比如：\n\nBash\npublic\/uploads\nstorage\nruntime\n\n这些应该放到 shared，然后软链接进 release。\n\n否则每次发版可能覆盖或丢失用户文件。\n\n2. .env 不进 Git\n\n线上配置应该在服务器的 shared\/.env。\n\n代码发布时软链接进去。\n\n3. Composer 不建议在线上临时慢慢装\n\n更稳的方式是 CI 阶段装好依赖、打包上传。\n\n如果项目不大，也可以先在线上执行：\n\nBash\ncomposer install --no-dev --optimize-autoloader\n\n但要注意 composer 版本、PHP 扩展、网络稳定性。\n\n4. 数据库变更要单独谨慎处理\n\n代码回滚容易，数据库回滚不一定容易。\n\n建议：\n\nmigration 向前兼容\n\n大字段变更提前做\n\n删除字段延后\n\n关键发布前备份数据库\n\n结论\n\n不上容器，最推荐：\n\nGitLab CI \/ GitHub Actions + SSH + Deployer \/ Release 软链接部署\n\n这是比 FTP 稳定很多、成本又不高的方案。\n\n如果你们当前技术债不重，可以直接上 Deployer；如果团队想完全可控，就自己写 release 部署脚本。",
    "sanitized_html": "<div class=\"cv-conversation\"><div class=\"cv-message cv-role-user\"><div class=\"cv-role\">You<\/div><div class=\"cv-content\">php web项目，当前线上代码更新用的还是FTP文件替换的方式更新代码，如果不上容器，有别的更好的方式吗？<\/div><\/div>\n<div class=\"cv-message cv-role-assistant\"><div class=\"cv-role\">ChatGPT<\/div><div class=\"cv-content\"><p>可以。不上容器，也比 FTP 覆盖文件强很多。推荐按成熟度分三档：<\/p>\n<h2>方案 1：Git 拉代码部署，最低改造<\/h2>\n<p>适合：小团队、单机 PHP 项目、暂时不想引入复杂 CI\/CD。<\/p>\n<h3>流程<\/h3>\n<p>服务器上保留项目 Git 仓库：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span class=\"ͼl\">cd<\/span><span> \/var\/www\/project<\/span><br \/><span class=\"ͼl\">git<\/span><span> fetch origin<\/span><br \/><span class=\"ͼl\">git<\/span><span> checkout main<\/span><br \/><span class=\"ͼl\">git<\/span><span> pull<\/span><br \/><span>composer install <\/span><span class=\"ͼn\">--no-dev<\/span><span class=\"ͼn\">--optimize-autoloader<\/span><br \/><span>php artisan migrate <\/span><span class=\"ͼn\">--force<\/span><span class=\"ͼe\"># Laravel 才需要<\/span><br \/><span>php artisan config:cache       <\/span><span class=\"ͼe\"># Laravel 才需要<\/span><br \/><span class=\"ͼl\">sudo<\/span><span> systemctl reload php-fpm<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>优点<\/h3>\n<ul>\n<li>\n<p>比 FTP 安全、可追踪<\/p>\n<\/li>\n<li>\n<p>能知道线上当前是什么 commit<\/p>\n<\/li>\n<li>\n<p>可以快速回滚：<\/p>\n<\/li>\n<\/ul>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span class=\"ͼl\">git<\/span><span> checkout &lt;previous_commit&gt;<\/span><br \/><span>composer install <\/span><span class=\"ͼn\">--no-dev<\/span><span class=\"ͼn\">--optimize-autoloader<\/span><br \/><span class=\"ͼl\">sudo<\/span><span> systemctl reload php-fpm<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>问题<\/h3>\n<ul>\n<li>\n<p>部署过程中可能出现“半更新”状态<\/p>\n<\/li>\n<li>\n<p>多台机器同步麻烦<\/p>\n<\/li>\n<li>\n<p>权限、缓存、依赖安装容易出问题<\/p>\n<\/li>\n<\/ul>\n<p>适合作为第一步替代 FTP，但不是最终形态。<\/p>\n<hr \/>\n<h2>方案 2：CI\/CD 自动部署，推荐起点<\/h2>\n<p>适合：希望团队规范化，不想手动登录服务器。<\/p>\n<p>可以用：<\/p>\n<ul>\n<li>\n<p>GitHub Actions<\/p>\n<\/li>\n<li>\n<p>GitLab CI<\/p>\n<\/li>\n<li>\n<p>Jenkins<\/p>\n<\/li>\n<li>\n<p>Gitea Actions<\/p>\n<\/li>\n<li>\n<p>Coding \/ 阿里云云效 \/ 腾讯 CODING<\/p>\n<\/li>\n<\/ul>\n<h3>基本逻辑<\/h3>\n<p>开发合并到 <code>main<\/code> 或打 tag 后自动执行：<\/p>\n<ol>\n<li>\n<p>拉代码<\/p>\n<\/li>\n<li>\n<p>安装依赖<\/p>\n<\/li>\n<li>\n<p>跑测试<\/p>\n<\/li>\n<li>\n<p>打包产物<\/p>\n<\/li>\n<li>\n<p>上传到服务器<\/p>\n<\/li>\n<li>\n<p>执行部署脚本<\/p>\n<\/li>\n<li>\n<p>reload PHP-FPM \/ Nginx<\/p>\n<\/li>\n<\/ol>\n<p>例如用 GitHub Actions + SSH：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">YAML<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>name: Deploy<\/span><br \/><br \/><span>on:<\/span><br \/><span>  push:<\/span><br \/><span>    branches: [main]<\/span><br \/><br \/><span>jobs:<\/span><br \/><span>  deploy:<\/span><br \/><span>    runs-on: ubuntu-latest<\/span><br \/><br \/><span>    steps:<\/span><br \/><span>      - uses: actions\/checkout@v4<\/span><br \/><br \/><span>      - name: Install dependencies<\/span><br \/><span>        run: composer install --no-dev --optimize-autoloader<\/span><br \/><br \/><span>      - name: Deploy via SSH<\/span><br \/><span>        uses: appleboy\/ssh-action@v1.0.3<\/span><br \/><span>        with:<\/span><br \/><span>          host: ${{ secrets.SERVER_HOST }}<\/span><br \/><span>          username: ${{ secrets.SERVER_USER }}<\/span><br \/><span>          key: ${{ secrets.SERVER_KEY }}<\/span><br \/><span>          script: <\/span><span class=\"ͼk\">|<\/span><br \/><span>            cd \/var\/www\/project<\/span><br \/><span>            git pull origin main<\/span><br \/><span>            composer install --no-dev --optimize-autoloader<\/span><br \/><span>            sudo systemctl reload php-fpm<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>优点<\/h3>\n<ul>\n<li>\n<p>不再人工 FTP<\/p>\n<\/li>\n<li>\n<p>有部署日志<\/p>\n<\/li>\n<li>\n<p>权限可控<\/p>\n<\/li>\n<li>\n<p>可以加测试、审批、通知<\/p>\n<\/li>\n<li>\n<p>适合团队协作<\/p>\n<\/li>\n<\/ul>\n<h3>问题<\/h3>\n<p>如果只是 <code>git pull<\/code> 覆盖线上目录，仍然存在短暂不一致风险。<\/p>\n<hr \/>\n<h2>方案 3：Release 目录 + 软链接切换，强烈推荐<\/h2>\n<p>这是不上容器时比较稳的 PHP 部署方式。<\/p>\n<h3>目录结构<\/h3>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>\/var\/www\/project\/<\/span><br \/><span>  releases\/<\/span><br \/><span class=\"ͼj\">20260615120000<\/span><span>\/<\/span><br \/><span class=\"ͼj\">20260615123000<\/span><span>\/<\/span><br \/><span>  shared\/<\/span><br \/><span>    .env<\/span><br \/><span>    storage\/<\/span><br \/><span>    uploads\/<\/span><br \/><span>  current <\/span><span class=\"ͼn\">-<\/span><span>&gt; \/var\/www\/project\/releases\/20260615123000<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<p>Nginx 指向：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Nginx<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>root \/var\/www\/project\/current\/public;<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>部署流程<\/h3>\n<p>每次发布新建一个 release 目录：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span class=\"ͼm\">RELEASE<\/span><span class=\"ͼg\">=<\/span><span>\/var\/www\/project\/releases\/$(date +%Y%m%d%H%M%S)<\/span><br \/><br \/><span class=\"ͼl\">git<\/span><span> clone <\/span><span class=\"ͼl\">git<\/span><span>@github.com:xxx\/project.git <\/span><span class=\"ͼm\">$RELEASE<\/span><br \/><span class=\"ͼl\">cd<\/span><span class=\"ͼm\">$RELEASE<\/span><br \/><br \/><span>composer install <\/span><span class=\"ͼn\">--no-dev<\/span><span class=\"ͼn\">--optimize-autoloader<\/span><br \/><br \/><span class=\"ͼl\">ln<\/span><span class=\"ͼn\">-s<\/span><span> \/var\/www\/project\/shared\/.env <\/span><span class=\"ͼm\">$RELEASE<\/span><span>\/.env<\/span><br \/><span class=\"ͼl\">ln<\/span><span class=\"ͼn\">-s<\/span><span> \/var\/www\/project\/shared\/storage <\/span><span class=\"ͼm\">$RELEASE<\/span><span>\/storage<\/span><br \/><br \/><span class=\"ͼl\">ln<\/span><span class=\"ͼn\">-sfn<\/span><span class=\"ͼm\">$RELEASE<\/span><span> \/var\/www\/project\/current<\/span><br \/><br \/><span class=\"ͼl\">sudo<\/span><span> systemctl reload php-fpm<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>回滚<\/h3>\n<p>只需要把 <code>current<\/code> 指回上一个 release：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span class=\"ͼl\">ln<\/span><span class=\"ͼn\">-sfn<\/span><span> \/var\/www\/project\/releases\/20260615120000 \/var\/www\/project\/current<\/span><br \/><span class=\"ͼl\">sudo<\/span><span> systemctl reload php-fpm<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<h3>优点<\/h3>\n<ul>\n<li>\n<p>发布接近原子切换<\/p>\n<\/li>\n<li>\n<p>回滚很快<\/p>\n<\/li>\n<li>\n<p>不会出现 FTP 那种文件传一半线上就混乱的问题<\/p>\n<\/li>\n<li>\n<p>很适合 PHP-FPM + Nginx 架构<\/p>\n<\/li>\n<\/ul>\n<p>这个是我最推荐的非容器方案。<\/p>\n<hr \/>\n<h2>可以直接用的工具<\/h2>\n<p>如果不想自己写部署脚本，可以看这些：<\/p>\n<div class=\"TyagGW_tableContainer\"><div class=\"group TyagGW_tableWrapper flex flex-col-reverse w-fit\"><table><thead><tr><th>工具<\/th><th>适合场景<\/th><\/tr><\/thead><tbody><tr><td><strong>Deployer<\/strong><\/td><td>PHP 项目非常适合，支持 release、shared、rollback<\/td><\/tr><tr><td><strong>Capistrano<\/strong><\/td><td>Ruby 生态老牌部署工具，也可部署 PHP<\/td><\/tr><tr><td><strong>Ansible<\/strong><\/td><td>多服务器、配置管理、发布流程统一<\/td><\/tr><tr><td><strong>Jenkins\/GitLab CI<\/strong><\/td><td>公司内部规范 CI\/CD<\/td><\/tr><tr><td><strong>GitHub Actions<\/strong><\/td><td>轻量、成本低、上手快<\/td><\/tr><\/tbody><\/table><\/div><\/div>\n<p>其中 <strong>Deployer<\/strong> 很适合 PHP：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>composer require deployer\/deployer <\/span><span class=\"ͼn\">--dev<\/span><br \/><span>vendor\/bin\/dep init<\/span><br \/><span>vendor\/bin\/dep deploy production<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<p>它天然支持：<\/p>\n<ul>\n<li>\n<p>releases<\/p>\n<\/li>\n<li>\n<p>shared files<\/p>\n<\/li>\n<li>\n<p>symlink<\/p>\n<\/li>\n<li>\n<p>rollback<\/p>\n<\/li>\n<li>\n<p>composer install<\/p>\n<\/li>\n<li>\n<p>deploy hooks<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2>我的建议<\/h2>\n<p>你们现在是 FTP 替换，建议按这个路线走：<\/p>\n<h3>第一阶段：马上替换 FTP<\/h3>\n<p>用：<\/p>\n<p><strong>Git + SSH 部署脚本<\/strong><\/p>\n<p>目标是先解决：<\/p>\n<ul>\n<li>\n<p>文件不可追踪<\/p>\n<\/li>\n<li>\n<p>人工替换容易漏文件<\/p>\n<\/li>\n<li>\n<p>无部署日志<\/p>\n<\/li>\n<li>\n<p>回滚困难<\/p>\n<\/li>\n<\/ul>\n<h3>第二阶段：标准化发布<\/h3>\n<p>升级成：<\/p>\n<p><strong>CI\/CD + releases 目录 + current 软链接<\/strong><\/p>\n<p>这是不上容器时的主力方案。<\/p>\n<h3>第三阶段：补齐工程规范<\/h3>\n<p>增加：<\/p>\n<ul>\n<li>\n<p>Git 分支策略：<code>dev \/ staging \/ main<\/code><\/p>\n<\/li>\n<li>\n<p>测试环境自动部署<\/p>\n<\/li>\n<li>\n<p>线上发布需要 tag 或手动审批<\/p>\n<\/li>\n<li>\n<p>部署前自动备份<\/p>\n<\/li>\n<li>\n<p>数据库 migration 谨慎执行<\/p>\n<\/li>\n<li>\n<p>发布失败自动回滚<\/p>\n<\/li>\n<li>\n<p>发布通知到 Slack \/ 飞书 \/ 企业微信<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2>注意几个坑<\/h2>\n<h3>1. 不要把用户上传文件放在代码目录里<\/h3>\n<p>比如：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>public\/uploads<\/span><br \/><span>storage<\/span><br \/><span>runtime<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<p>这些应该放到 <code>shared<\/code>，然后软链接进 release。<\/p>\n<p>否则每次发版可能覆盖或丢失用户文件。<\/p>\n<hr \/>\n<h3><span>2. <code>.env<\/code> 不进 Git<\/span><\/h3>\n<p>线上配置应该在服务器的 <code>shared\/.env<\/code>。<\/p>\n<p>代码发布时软链接进去。<\/p>\n<hr \/>\n<h3>3. Composer 不建议在线上临时慢慢装<\/h3>\n<p>更稳的方式是 CI 阶段装好依赖、打包上传。<\/p>\n<p>如果项目不大，也可以先在线上执行：<\/p>\n<pre><\/pre><div class=\"relative w-full mt-4 mb-1\"><div><div class=\"contents\"><div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\"><div class=\"relative h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\"><div class=\"select-none sticky z-2 top-(--sticky-padding-top)\"><div class=\"flex w-full items-center justify-between py-1.5 ps-4 pe-1.5 font-sans md:ps-5 bg-token-bg-elevated-secondary\"><div class=\"flex max-w-[75%] min-w-0 cursor-default items-center text-sm font-medium justify-self-start text-token-text-primary\">Bash<\/div><\/div><\/div><div class=\"relative\"><div class=\"h-full min-h-0 min-w-0\"><div class=\"h-full min-h-0 min-w-0\"><div><div class=\"relative\"><div><div class=\"relative z-0 flex max-w-full\"><div class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼd ͼr\"><div class=\"cm-scroller\"><pre><code><span>composer install <\/span><span class=\"ͼn\">--no-dev<\/span><span class=\"ͼn\">--optimize-autoloader<\/span><\/code><\/pre><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n<p>但要注意 composer 版本、PHP 扩展、网络稳定性。<\/p>\n<hr \/>\n<h3>4. 数据库变更要单独谨慎处理<\/h3>\n<p>代码回滚容易，数据库回滚不一定容易。<\/p>\n<p>建议：<\/p>\n<ul>\n<li>\n<p>migration 向前兼容<\/p>\n<\/li>\n<li>\n<p>大字段变更提前做<\/p>\n<\/li>\n<li>\n<p>删除字段延后<\/p>\n<\/li>\n<li>\n<p>关键发布前备份数据库<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2>结论<\/h2>\n<p>不上容器，最推荐：<\/p>\n<blockquote>\n<p><strong>GitLab CI \/ GitHub Actions + SSH + Deployer \/ Release 软链接部署<\/strong><\/p>\n<\/blockquote>\n<p>这是比 FTP 稳定很多、成本又不高的方案。<\/p>\n<p>如果你们当前技术债不重，可以直接上 <strong>Deployer<\/strong>；如果团队想完全可控，就自己写 release 部署脚本。<\/p><\/div><\/div><\/div>",
    "markdown_content": null,
    "source_platform": "ChatGPT",
    "source_url": "https:\/\/chatgpt.com\/c\/6a2f5683-2a04-8330-bf53-4a7b996820d6",
    "model_name": null,
    "metadata": {
        "tags": [
            "chatgpt",
            "ai-conversation"
        ],
        "locale": "zh_CN",
        "language": "en-US",
        "exported_at": "2026-06-15T01:35:28.540Z",
        "message_count": 2,
        "plugin_version": "1.0.0",
        "conversation_id": "6a2f5683-2a04-8330-bf53-4a7b996820d6",
        "user_message_count": 1,
        "submission_token_type": "user",
        "assistant_message_count": 1
    },
    "views_count": 30,
    "unique_views_count": 14,
    "created_at": "2026-06-15T01:35:31+00:00",
    "url": "https:\/\/chat-view.com\/s\/9MUZaq4Ws9TR"
}