V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
dzdh
V2EX  ›  问与答

聊聊 PHP 容器应用的最佳实践

  •  
  •   dzdh · 2022-08-16 09:48:11 +08:00 · 2965 次点击
    这是一个创建于 590 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近公司正在推进容器化建设,主要是扩容方便。
    然后有几个问题,问一下。

    项目是 laravel 。那么附带的还有他的 queue 和 cron artisan schedule:run 。

    是都放在一个容器里吗?弄个 supervisor 启动 nginx 、fpm 、cron -f 、和 queue:work ?

    总觉得哪里不对,有啥最佳实践推荐一下吗?
    38 条回复    2023-09-19 15:42:25 +08:00
    cooper
        1
    cooper  
       2022-08-16 09:49:24 +08:00
    我们就是一个 docker 中,没啥不好。已经用三年了。
    XiLingHost
        2
    XiLingHost  
       2022-08-16 09:49:37 +08:00
    ss098
        3
    ss098  
       2022-08-16 10:00:16 +08:00
    可以构建成一个容器,但不要在一个容器内使用 supervisor 或其他进程管理工具启动,要作为多个容器启动。
    dzdh
        4
    dzdh  
    OP
       2022-08-16 10:02:28 +08:00
    @ss098

    emmm.. 构建成 1 个容器 .... 多个容器启动。 啥。

    一个 image ,根据参数启动不同的进程作为独立 container 是吧。
    shoaly
        5
    shoaly  
       2022-08-16 10:06:24 +08:00
    @XiLingHost 这个是啥? readme 里面光做了一个 logo , 有点蒙蒙的
    LexLuth0r
        6
    LexLuth0r  
       2022-08-16 10:26:01 +08:00
    一个 Pod 中启动多个 container ,如 nginx 、php-fpm 服务、queue 服务。如果 queue 启动较多防止单个 Pod 资源不足,queue 服务也可以单独部署在一个 Pod 中,cron 同理。如果某一个 container 会频繁扩容就单独放到一个 Pod 中方便增加副本数。
    dzdh
        7
    dzdh  
    OP
       2022-08-16 10:29:46 +08:00
    @LexLuth0r 不是 k8s 呢
    zyh94946
        8
    zyh94946  
       2022-08-16 10:35:36 +08:00
    不是 k8s 用什么?
    xuelu520
        9
    xuelu520  
       2022-08-16 10:38:09 +08:00
    我们的 laravel 项目,queue 和 console 的脚本都有单独的容器来。
    Rache1
        10
    Rache1  
       2022-08-16 10:38:17 +08:00
    镜像都是同一个,queue 和 schedule 这些可以放到一个单独的容器里面去跑,使用 queue 使用 supervisor 守护
    dusu
        11
    dusu  
       2022-08-16 10:51:15 +08:00 via iPhone
    肯定拆开来用 docker compose
    redis mysql 这种放一个容器
    升级起来不是要命么
    而且基本上他们升级频率不高吧

    另外 简单说两个坑 可自行试试
    1. network 如果用 bridge ,性能可能会比裸跑低 30%
    2. 不用 volume ,用挂载代码,性能可能会比裸跑扣一半以上
    dusu
        12
    dusu  
       2022-08-16 10:57:39 +08:00 via iPhone
    @dusu 哦 看错了 没有 redis/mysql😥
    nginx 的话基本上看业务会不会后期扩展了
    如果会就拆出来 如果不考虑就放一块
    queue 和 cron 可以考虑同镜像不同容器
    DrX
        13
    DrX  
       2022-08-16 11:12:52 +08:00
    说一下我的做法,目前感觉用起来非常舒服:

    web 一个容器,里面用 supervisor 同时跑 nginx + php-fpm
    queue 、cron 单独一个容器

    #3 三楼所说的「不要在一个容器内使用 supervisor 」不适用于传统 php 项目
    leo108
        14
    leo108  
       2022-08-16 11:22:09 +08:00
    #13 传统 PHP 项目也是可以把 PHP 和 Nginx 拆开成两个容器的
    shuimugan
        15
    shuimugan  
       2022-08-16 11:44:21 +08:00 via Android
    上 nginx unit 就可以把 php 和 nginx 二合一了
    goodSleep
        16
    goodSleep  
       2022-08-16 11:46:40 +08:00 via Android
    我们没用容器,但是 artisan queue supervisor 都是单独部了一套业务
    dzdh
        17
    dzdh  
    OP
       2022-08-16 11:52:48 +08:00
    @shuimugan #14 一直没研究明白这个东西。这个东西没有配置文件。必须只能是 http post 配置然后存在内存里吗?
    dzdh
        18
    dzdh  
    OP
       2022-08-16 11:53:18 +08:00
    @dusu #11

    compose 一个 service 的存储是共享的吗?
    reter
        19
    reter  
       2022-08-16 11:55:37 +08:00   ❤️ 1
    没有人规定容器里面不能使用 supervisor 。

    但是容器只能监控第一个启动进程,如果使用了 supervisor ,那么容器监控到的是 supervisor 状态,而不是具体服务的状态。
    在容器内使用 supervisor 带来比较明显的缺点:
    - 进程监控( docker 只能看到 supervisor 的存在,无法感知 supervisor 背后启动的服务, 比如 docker ps )
    - 日志管理(如果服务直接往 stdout 输出日志,日期可以被 docker 收集,如果加了 supervisor 显然不能直接收集)
    - 信号处理+服务重启 (docker 重启服务时,会先向进程发送信号 SIGTERM ,此时服务可进行后续处理,超时才发送 SIGKILL. 使用 supervisor 可能需要进行额外配置,同时重启 supervisor 会将后面所有的服务都重启)

    同时一个服务一个容器,可以很好的利用现有的容器监控方案,然而使用 supervisor 就失去了这些优势。

    没有人规定容器里面不能使用 supervisor 。
    DrX
        20
    DrX  
       2022-08-16 12:07:23 +08:00
    @leo108
    #14
    分是能分,但是呢,传统 php 项目经常在 nginx 、fpm 共用 public 文件目录的。

    典型情况是 fpm 保存文件到 public/uploads 目录,nginx 提供静态文件服务,分开了会很麻烦。
    XiLingHost
        21
    XiLingHost  
       2022-08-16 12:29:30 +08:00
    @shoaly 这就是一个基于 docker 的 php laravel 项目
    gesse
        22
    gesse  
       2022-08-16 13:43:27 +08:00
    容器里应该少用 supervisor ,用 s6 多
    iyaozhen
        23
    iyaozhen  
       2022-08-16 13:46:30 +08:00
    容器里面不要用 supervisor ,挂了就让容器自己重启

    nginx 可以放进去,但应该轻量点,只是本地和 php 配合,把更多的功能做到网关层

    cron 啥的单独建集群和接受 http 请求的分开
    superchijinpeng
        24
    superchijinpeng  
       2022-08-16 13:47:06 +08:00
    拆到多个容器中
    superchijinpeng
        25
    superchijinpeng  
       2022-08-16 13:47:25 +08:00
    富容器没有意义
    toomoy
        26
    toomoy  
       2022-08-16 14:21:03 +08:00
    https://github.com/imbossa/docker_dev
    这个就包含 supervisor
    luyaolu
        27
    luyaolu  
       2022-08-16 16:22:08 +08:00
    我现在的就是一个 docker 中 大半年了 没啥问题
    shuimugan
        28
    shuimugan  
       2022-08-16 16:30:04 +08:00
    @dzdh nginx unit 说白了就是更容易编程化的 nginx ,你也可以把 json 写到文件,然后给 post json 文件的 curl 命令做个 alias ,就能达到修改配置文件 + nginx -s reload 的效果
    dzdh
        29
    dzdh  
    OP
       2022-08-16 17:08:41 +08:00
    @shuimugan #28 那我的配置一辈子不变就不能直接读取个文件吗? docker file 也好写啊

    CMD ["unitd","-c","/etc/service.json"] 他不香吗。。。。
    dzdh
        30
    dzdh  
    OP
       2022-08-16 17:08:57 +08:00
    @shuimugan 我在容器里为啥要 reload 啊
    xiaochong0302
        31
    xiaochong0302  
       2022-08-16 20:41:53 +08:00
    酷瓜云课堂的实践: https://gitee.com/koogua/course-tencent-cloud-docker

    php + cron + workerman (supervisor)
    nginx
    mysql
    redis
    xunsearch
    wangningkai
        32
    wangningkai  
       2022-08-16 23:15:34 +08:00 via iPhone
    GitHub 搜 dnmp ,用 docker- compose 编排
    shuimugan
        33
    shuimugan  
       2022-08-17 05:55:53 +08:00
    @dzdh 我觉得主要看你们是否把调整配置当成一次发版(更新容器)进行操作,如果不会线上 reload 那的确可以直接单个文件一把梭。

    reload 用于一般用于蓝绿部署,nginx unit 的条件路由可以很好的控制流量,比如通过特定 header 字段和值进行流量切换,比如把测试账号引流到特定节点。

    另外不建议把 nginx 和 php-fpm 分开部署,因为 php 的 curl 功能过于强大,如果代码逻辑不严谨或者缺乏安全团队测试,不恰当使用 curl 的话,会被和 gopher 组合构造出任意代码给 php-fpm 执行(相当于后门)。
    julyclyde
        34
    julyclyde  
       2022-08-17 12:14:14 +08:00
    在容器内就没必要 supervisord 或者 systemd 了

    该出错的时候能正确的出错,是一种较佳实践
    julyclyde
        35
    julyclyde  
       2022-08-17 12:16:17 +08:00
    @shuimugan 换个说法就是,把容器当作短期的“运行”还是当作长期的“机器”
    如果是后者,各种操作产生的结果会累积起来形成状态,这个状态不一定是期望的
    gaoxu387
        36
    gaoxu387  
       192 天前
    @reter 很赞同你的观点,容器的 12 要素的也提到了一个关键的点,一个进程: https://12factor.net/zh_cn/processes
    而 supervisor 的模式是违反了这种理念,虽然也可以用但不是最佳的方法。

    对于 PHP 的容器化特别是 Laravel 的容器化的个人建议:
    1 、打包成一个镜像
    2 、nginx 运行 pod 、php-fpm 运行一个 pod 、laravel 的 schedule:run 运行一个 cron 的镜像

    业务流量是 Ingress (不配置规则)-> service -> nginx (具体规则) -> php-fpm (中间件不要部署在 k8s 里)
    cron 单独运行一个常驻的 pod ,启动 crond ,并配置一分钟运行一次 schedule:run
    dzdh
        37
    dzdh  
    OP
       192 天前
    @gaoxu387 nginx 运行 pod 、php-fpm 运行一个 pod 代码在放在哪里。

    nginx 有个 try_files , 必须要能访问到.php 文件。

    php-fpm fcgi 协议是 nginx 传递过来的 filepath 。

    那么程序代码放在哪里?
    gaoxu387
        38
    gaoxu387  
       191 天前
    @dzdh 代码运行在 php-fpm 这个 pod 里啊,nginx 通过服务名称转发 proxy_pass 到 php-fpm 的容器. nginx 一般用这个配置转发:
    location ~ \.php$ {
    fastcgi_pass php-fpm:9000;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    include fastcgi_params;
    }
    如果你需要用 try_files 那你把代码也打包一份在 nginx 的 pod 也是可以的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1093 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 18:52 · PVG 02:52 · LAX 11:52 · JFK 14:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.