Supervisor简介

Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。

它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动,这样只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去即可。

也实现当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,可以选择是否自己启动和报警。supervisor还提供了一个功能,可以为supervisord或者每个子进程,设置一个非rootuser,这个user就可以管理它对应的进程。

事件背景

给公司用 docker 部署新的 dnmp 开发环境,想用 supervisor 实现对 php 进程和 swoole 进程的监控。但是部署了 dnmp 后,发现对于存活的 php 进程无法正确的监控到,始终报 fatal ,致命错误。

image-20210914112634470

错误分析

dnmp 中,由于 supervisor 作为一个独立的容器存在,所以对于监控配置文件 supervisor/conf.d/php-fpm.ini 中运行的 php 命令并无法正常执行(因为只有在php容器中才可执行 php-cli 命令)。

所以,接下来的改写,目的就是吧 supervisor 移植到 php 容器中运行。

参考文献

已修改的配置下载:https://github.com/jefferyjob/dnmp_supervisor

配置修改过程

php的dockerfile改写

ARG PHP_VERSION
FROM ${PHP_VERSION}

ARG TZ
ARG PHP_EXTENSIONS
ARG CONTAINER_PACKAGE_URL

# 配置清华镜像
RUN if [ $CONTAINER_PACKAGE_URL ] ; then sed -i "s/dl-cdn.alpinelinux.org/${CONTAINER_PACKAGE_URL}/g" /etc/apk/repositories ; fi

# 安装php扩展
COPY ./extensions /tmp/extensions
WORKDIR /tmp/extensions
RUN chmod +x install.sh \
    && sh install.sh \
    && rm -rf /tmp/extensions

# 添加扩展安装执行脚本
ADD ./extensions/install-php-extensions  /usr/local/bin/

# 赋予可执行权限
RUN chmod uga+x /usr/local/bin/install-php-extensions

# 安装必要的软件
# supervisor
# bash
# 设置时区
RUN apk update \
    && apk upgrade \
    && apk add supervisor \
    && apk add bash \
    && apk --no-cache add tzdata \
    && cp "/usr/share/zoneinfo/$TZ" /etc/localtime \
    && echo "$TZ" > /etc/timezone \
    && rm -rf /var/cache/apk/*

# Fix: https://github.com/docker-library/php/issues/240
RUN apk add gnu-libiconv libstdc++ --no-cache --repository http://${CONTAINER_PACKAGE_URL}/alpine/edge/community/ --allow-untrusted
ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so php


# 安装 composer 并更改它的缓存主页
RUN curl -o /usr/bin/composer https://mirrors.aliyun.com/composer/composer.phar \
    && chmod +x /usr/bin/composer
ENV COMPOSER_HOME=/tmp/composer

# 配置用户组
RUN apk --no-cache add shadow && usermod -u 1000 www-data && groupmod -g 1000 www-data

# 设置工作目录
WORKDIR /var/www

主要修改的内容是:

RUN apk update \
    && apk upgrade \
    && apk add supervisor \
    && apk add bash \
    && apk --no-cache add tzdata \
    && cp "/usr/share/zoneinfo/$TZ" /etc/localtime \
    && echo "$TZ" > /etc/timezone \
    && rm -rf /var/cache/apk/*

docker-composer中 php 的容器编排修改

# php7
php:
  build:
    context: ./services/php
    args:
      PHP_VERSION: php:7.4.7-fpm-alpine
      CONTAINER_PACKAGE_URL: mirrors.ustc.edu.cn
      PHP_EXTENSIONS: pdo_mysql,mysqli,mbstring,gd,curl,opcache,apcu,zip,opcache,redis,swoole
      TZ: "Asia/Tokyo"
  container_name: php
  ports:
    - "9001:9001"
  expose:
    - 9000 # php-fpm
    - 9001 # supervisor
    - 5200 # swoole laravels
  #extra_hosts:
  #  - "www.site1.com:172.17.0.1"
  volumes:
    - ..:/var/www/:rw
    - ./services/php/php.ini:/usr/local/etc/php/php.ini:ro
    - ./services/php/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf:rw
    - ./logs/php:/var/log/php
    - ./data/composer:/tmp/composer
    # supervisor 文件映射配置
    - ./services/supervisor/supervisord.conf:/etc/supervisord.conf:rw
    - ./services/supervisor/conf.d:/etc/supervisor/conf.d:rw
    - ./logs/supervisor:/var/log/supervisor:rw
  restart: always
  command:
    - /bin/sh
    - -c
    - |
      supervisord -n -c /etc/supervisord.conf
  cap_add:
    - SYS_PTRACE
  environment:
    APP_ENV: "developer"
  networks:
    - default

主要增加了 9001:9001的端口映射,然后在容器内部通过 expose 暴露 9001 的端口。

在 volumes 里面映射了 supervisor 的相关配置文件。

在 command 中加入了supervisor启动脚本:

command:
      - /bin/sh
      - -c
      - |
        supervisord -n -c /etc/supervisord.conf

修改supervisor对于php的监听配置

services/supervisor/conf.d/php-fpm.ini

[program:php]
command=/usr/local/bin/php -S 0.0.0.0:80
directory=/var/www/

然后重新 build 容器,最后 up 启动容器就成功了!

测试结果

image-20210914132415422

总结

当前处理方式的弊端

本篇文章处理的方式是将 supervisor 安装进 php 容器中,然后运行命令 command=/usr/local/bin/php -S 0.0.0.0:80 来监听 php 的相关进程。

所以这样做的弊端就是,如果 php 的容器宕机后,由于 supervisor 存在于php容器中,那么 supervisor 也将无法使用,所以supervisor失去了他监控健康进程的意义。

基于当前的环境要求如何改写

可以将 supervisor 继续作为一个独立的容器运行,然后在配置文件command中,通过 docker exec - it xxx xx 等相关配置,来监听别的容器中的相关进程是否健康。