进程管理利器-supervisor部署记录

一、简单介绍

supervisor是用来管理进程的一个工具,止于为什么要用supervisor,是因为相对于linux传统的进程管理方式来说,它有很多的优势:

----简单----
通常管理linux进程的时候,一般来说都需要自己编写一个能够实现进程start/stop/restart/reload功能的脚本,然后丢到/etc/init.d/下面。这么做有很多不好的地方,第一我们要编写这个脚本,这就很耗时耗力了。
第二,当这个进程挂掉的时候,linux不会自动重启它的,想要自动重启的话,我们还要自己写一个监控重启脚本。而,supervisor则可以完美的解决这些问题。好,怎么解决的呢,其实supervisor管理进程,就是通过
fork/exec的方式把这些被管理的进程,当作supervisor的子进程来启动。这样的话,我们只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去就OK了。这样就省下了我们如同linux管理进程的时候,
自己写控制脚本的麻烦了。第二,被管理进程作为supervisor的子进程,当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,所以当然也就可以对挂掉的子进程进行自动重启了,当然重启还是不重启,也要看你的
配置文件里面有木有设置autostart=true了,这是后话。

----精确----
为啥说精确呢?因为linux对进程状态的反馈,有时候不太准确。为啥不准确?这个楼主也不知道啊,官方文档是这么说的,知道的告诉楼主一下吧,感激不尽。而supervisor监控子进程,得到的子进程状态无疑是准确的。
进程组supervisor可以对进程组统一管理,也就是说咱们可以把需要管理的进程写到一个组里面,然后我们把这个组作为一个对象进行管理,如启动,停止,重启等等操作。而linux系统则是没有这种功能的,我们想要停止
一个进程,只能一个一个的去停止,要么就自己写个脚本去批量停止。

-----集中式管理-----
supervisor管理的进程,进程组信息,全部都写在一个ini格式的文件里就OK了。而且,我们管理supervisor的时候的可以在本地进行管理,也可以远程管理,而且supervisor提供了一个web界面,我们可以在web界面上监控,
管理进程。 当然了,本地,远程和web管理的时候,需要调用supervisor的xml_rpc接口,这个也是后话。

---有效性----
当supervisor的子进程挂掉的时候,操作系统会直接给supervisor发信号。而其他的一些类似supervisor的工具,则是通过进程的pid文件,来发送信号的,然后定期轮询来重启失败的进程。显然supervisor更加高效。。。
至于是哪些类似supervisor工具,这个楼主就不太清楚了,楼主还听说过god,director,但是没用过。有兴趣的朋友可以玩玩

----可扩展性----
supervisor是个开源软件,牛逼点的,可以直接去改软件。不过咱们大多数人还是老老实实研究supervisot提供的接口吧,supervisor主要提供了两个可扩展的功能。一个是event机制,这个就是楼主这两天干的活要用到的
东西。再一个是xml_rpc,supervisor的web管理端和远程调用的时候,就要用到它了。

-----权限----
大伙都知道linux的进程,特别是侦听在1024端口之下的进程,一般用户大多数情况下,是不能对其进行控制的。想要控制的话,必须要有root权限。而supervisor提供了一个功能,可以为supervisord或者每个子进程,
设置一个非root的user,这个user就可以管理它对应的进程了。

----兼容性,稳定性----

二、组成部分

  • supervisord:服务守护进程

  • supervisorctl:命令行客户端

  • Web Server:提供与supervisorctl功能相当的WEB操作界面

  • XML-RPC Interface:XML-RPC接口

三、安装和配置

centos平台下可直接用过YUM源安装

[root@op-zhongkong ~]# yum install supervisor
[root@op-zhongkong ~]# chkconfig supervisord on

服务器启停

[root@op-zhongkong ~]# /etc/init.d/supervisord {start|stop|status|restart|reload|force-reload|condrestart}

日志

/var/log/supervisor/supervisord.log

配置文件

[root@op-zhongkong ~]# vim /etc/supervisord.conf

需要重点关注的是以部分

[program:x]中配置要监控的进程

配置文件详解

[unix_http_server]           
file=/tmp/supervisor.sock   ; socket文件的路径,supervisorctl用XML_RPC和supervisord通信就是通过它进行
                              的。如果不设置的话,supervisorctl也就不能用了 
                              不设置的话,默认为none。 非必须设置       
;chmod=0700                 ; 这个简单,就是修改上面的那个socket文件的权限为0700
                              不设置的话,默认为0700。 非必须设置
;chown=nobody:nogroup       ; 这个一样,修改上面的那个socket文件的属组为user.group
                              不设置的话,默认为启动supervisord进程的用户及属组。非必须设置
;username=user              ; 使用supervisorctl连接的时候,认证的用户
                               不设置的话,默认为不需要用户。 非必须设置
;password=123               ; 和上面的用户名对应的密码,可以直接使用明码,也可以使用SHA加密
                              如:{SHA}82ab876d1387bfafe46cc1c8a2ef074eae50cb1d
                              默认不设置。。。非必须设置

;[inet_http_server]         ; 侦听在TCP上的socket,Web Server和远程的supervisorctl都要用到他
                              不设置的话,默认为不开启。非必须设置
;port=127.0.0.1:9001        ; 这个是侦听的IP和端口,侦听所有IP用 :9001或*:9001。
                              这个必须设置,只要上面的[inet_http_server]开启了,就必须设置它
;username=user              ; 这个和上面的uinx_http_server一个样。非必须设置
;password=123               ; 这个也一个样。非必须设置

[supervisord]                ;这个主要是定义supervisord这个服务端进程的一些参数的
                              这个必须设置,不设置,supervisor就不用干活了
logfile=/tmp/supervisord.log ; 这个是supervisord这个主进程的日志路径,注意和子进程的日志不搭嘎。
                               默认路径$CWD/supervisord.log,$CWD是当前目录。。非必须设置
logfile_maxbytes=50MB        ; 这个是上面那个日志文件的最大的大小,当超过50M的时候,会生成一个新的日
                               志文件。当设置为0时,表示不限制文件大小
                               默认值是50M,非必须设置。             
logfile_backups=10           ; 日志文件保持的数量,上面的日志文件大于50M时,就会生成一个新文件。文件
                               数量大于10时,最初的老文件被新文件覆盖,文件数量将保持为10
                               当设置为0时,表示不限制文件的数量。
                               默认情况下为10。。。非必须设置
loglevel=info                ; 日志级别,有critical, error, warn, info, debug, trace, or blather等
                               默认为info。。。非必须设置项
pidfile=/tmp/supervisord.pid ; supervisord的pid文件路径。
                               默认为$CWD/supervisord.pid。。。非必须设置
nodaemon=false               ; 如果是true,supervisord进程将在前台运行
                               默认为false,也就是后台以守护进程运行。。。非必须设置
minfds=1024                  ; 这个是最少系统空闲的文件描述符,低于这个值supervisor将不会启动。
                               系统的文件描述符在这里设置cat /proc/sys/fs/file-max
                               默认情况下为1024。。。非必须设置
minprocs=200                 ; 最小可用的进程描述符,低于这个值supervisor也将不会正常启动。
                              ulimit  -u这个命令,可以查看linux下面用户的最大进程数
                              默认为200。。。非必须设置
;umask=022                   ; 进程创建文件的掩码
                               默认为022。。非必须设置项
;user=chrism                 ; 这个参数可以设置一个非root用户,当我们以root用户启动supervisord之后。
                               我这里面设置的这个用户,也可以对supervisord进行管理
                               默认情况是不设置。。。非必须设置项
;identifier=supervisor       ; 这个参数是supervisord的标识符,主要是给XML_RPC用的。当你有多个
                               supervisor的时候,而且想调用XML_RPC统一管理,就需要为每个
                               supervisor设置不同的标识符了
                               默认是supervisord。。。非必需设置
;directory=/tmp              ; 这个参数是当supervisord作为守护进程运行的时候,设置这个参数的话,启动
                               supervisord进程之前,会先切换到这个目录
                               默认不设置。。。非必须设置
;nocleanup=true              ; 这个参数当为false的时候,会在supervisord进程启动的时候,把以前子进程
                               产生的日志文件(路径为AUTO的情况下)清除掉。有时候咱们想要看历史日志,当
                               然不想日志被清除了。所以可以设置为true
                               默认是false,有调试需求的同学可以设置为true。。。非必须设置
;childlogdir=/tmp            ; 当子进程日志路径为AUTO的时候,子进程日志文件的存放路径。
                               默认路径是这个东西,执行下面的这个命令看看就OK了,处理的东西就默认路径
                               python -c "import tempfile;print tempfile.gettempdir()"
                               非必须设置
;environment=KEY="value"     ; 这个是用来设置环境变量的,supervisord在linux中启动默认继承了linux的
                               环境变量,在这里可以设置supervisord进程特有的其他环境变量。
                               supervisord启动子进程时,子进程会拷贝父进程的内存空间内容。 所以设置的
                               这些环境变量也会被子进程继承。
                               小例子:environment=name="haha",age="hehe"
                               默认为不设置。。。非必须设置
;strip_ansi=false            ; 这个选项如果设置为true,会清除子进程日志中的所有ANSI 序列。什么是ANSI
                               序列呢?就是我们的n,t这些东西。
                               默认为false。。。非必须设置

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]    ;这个选项是给XML_RPC用的,当然你如果想使用supervisord或者web server 这
                              个选项必须要开启的
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]              ;这个主要是针对supervisorctl的一些配置 
serverurl=unix:///tmp/supervisor.sock ; 这个是supervisorctl本地连接supervisord的时候,本地UNIX socket
                                        路径,注意这个是和前面的[unix_http_server]对应的
                                        默认值就是unix:///tmp/supervisor.sock。。非必须设置
;serverurl=http://127.0.0.1:9001 ; 这个是supervisorctl远程连接supervisord的时候,用到的TCP socket路径
                                   注意这个和前面的[inet_http_server]对应
                                   默认就是http://127.0.0.1:9001。。。非必须项

;username=chris              ; 用户名
                               默认空。。非必须设置
;password=123                ; 密码
                              默认空。。非必须设置
;prompt=mysupervisor         ; 输入用户名密码时候的提示符
                               默认supervisor。。非必须设置
;history_file=~/.sc_history  ; 这个参数和shell中的history类似,我们可以用上下键来查找前面执行过的命令
                               默认是no file的。。所以我们想要有这种功能,必须指定一个文件。。。非
                               必须设置

; The below sample program section shows all possible program subsection values,
; create one or more 'real' program: sections to be able to control them under
; supervisor.

;[program:theprogramname]      ;这个就是咱们要管理的子进程了,":"后面的是名字,最好别乱写和实际进程
                                有点关联最好。这样的program我们可以设置一个或多个,一个program就是
                                要被管理的一个进程
;command=/bin/cat              ; 这个就是我们的要启动进程的命令路径了,可以带参数
                                例子:/home/test.py -a 'hehe'
                                有一点需要注意的是,我们的command只能是那种在终端运行的进程,不能是
                                守护进程。这个想想也知道了,比如说command=service httpd start。
                                httpd这个进程被linux的service管理了,我们的supervisor再去启动这个命令
                                这已经不是严格意义的子进程了。
                                这个是个必须设置的项
;process_name=%(program_name)s ; 这个是进程名,如果我们下面的numprocs参数为1的话,就不用管这个参数
                                 了,它默认值%(program_name)s也就是上面的那个program冒号后面的名字,
                                 但是如果numprocs为多个的话,那就不能这么干了。想想也知道,不可能每个
                                 进程都用同一个进程名吧。


;numprocs=1                    ; 启动进程的数目。当不为1时,就是进程池的概念,注意process_name的设置
                                 默认为1    。。非必须设置
;directory=/tmp                ; 进程运行前,会前切换到这个目录
                                 默认不设置。。。非必须设置
;umask=022                     ; 进程掩码,默认none,非必须
;priority=999                  ; 子进程启动关闭优先级,优先级低的,最先启动,关闭的时候最后关闭
                                 默认值为999 。。非必须设置
;autostart=true                ; 如果是true的话,子进程将在supervisord启动后被自动启动
                                 默认就是true   。。非必须设置
;autorestart=unexpected        ; 这个是设置子进程挂掉后自动重启的情况,有三个选项,false,unexpected
                                 和true。如果为false的时候,无论什么情况下,都不会被重新启动,
                                 如果为unexpected,只有当进程的退出码不在下面的exitcodes里面定义的退
                                 出码的时候,才会被自动重启。当为true的时候,只要子进程挂掉,将会被无
                                 条件的重启
;startsecs=1                   ; 这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启
                                 动成功了
                                 默认值为1 。。非必须设置
;startretries=3                ; 当进程启动失败后,最大尝试启动的次数。。当超过3次后,supervisor将把
                                 此进程的状态置为FAIL
                                 默认值为3 。。非必须设置
;exitcodes=0,2                 ; 注意和上面的的autorestart=unexpected对应。。exitcodes里面的定义的
                                 退出码是expected的。
;stopsignal=QUIT               ; 进程停止信号,可以为TERM, HUP, INT, QUIT, KILL, USR1, or USR2等信号
                                  默认为TERM 。。当用设定的信号去干掉进程,退出码会被认为是expected
                                  非必须设置
;stopwaitsecs=10               ; 这个是当我们向子进程发送stopsignal信号后,到系统返回信息
                                 给supervisord,所等待的最大时间。 超过这个时间,supervisord会向该
                                 子进程发送一个强制kill的信号。
                                 默认为10秒。。非必须设置
;stopasgroup=false             ; 这个东西主要用于,supervisord管理的子进程,这个子进程本身还有
                                 子进程。那么我们如果仅仅干掉supervisord的子进程的话,子进程的子进程
                                 有可能会变成孤儿进程。所以咱们可以设置可个选项,把整个该子进程的
                                 整个进程组都干掉。 设置为true的话,一般killasgroup也会被设置为true。
                                 需要注意的是,该选项发送的是stop信号
                                 默认为false。。非必须设置。。
;killasgroup=false             ; 这个和上面的stopasgroup类似,不过发送的是kill信号
;user=chrism                   ; 如果supervisord是root启动,我们在这里设置这个非root用户,可以用来
                                 管理该program
                                 默认不设置。。。非必须设置项
;redirect_stderr=true          ; 如果为true,则stderr的日志会被写入stdout日志文件中
                                 默认为false,非必须设置
;stdout_logfile=/a/path        ; 子进程的stdout的日志路径,可以指定路径,AUTO,none等三个选项。
                                 设置为none的话,将没有日志产生。设置为AUTO的话,将随机找一个地方
                                 生成日志文件,而且当supervisord重新启动的时候,以前的日志文件会被
                                 清空。当 redirect_stderr=true的时候,sterr也会写进这个日志文件
;stdout_logfile_maxbytes=1MB   ; 日志文件最大大小,和[supervisord]中定义的一样。默认为50
;stdout_logfile_backups=10     ; 和[supervisord]定义的一样。默认10
;stdout_capture_maxbytes=1MB   ; 这个东西是设定capture管道的大小,当值不为0的时候,子进程可以从stdout
                                 发送信息,而supervisor可以根据信息,发送相应的event。
                                 默认为0,为0的时候表达关闭管道。。。非必须项
;stdout_events_enabled=false   ; 当设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将
                                 触发supervisord发送PROCESS_LOG_STDOUT类型的event
                                 默认为false。。。非必须设置
;stderr_logfile=/a/path        ; 这个东西是设置stderr写的日志路径,当redirect_stderr=true。这个就不用
                                 设置了,设置了也是白搭。因为它会被写入stdout_logfile的同一个文件中
                                 默认为AUTO,也就是随便找个地存,supervisord重启被清空。。非必须设置
;stderr_logfile_maxbytes=1MB   ; 这个出现好几次了,就不重复了
;stderr_logfile_backups=10     ; 这个也是
;stderr_capture_maxbytes=1MB   ; 这个一样,和stdout_capture一样。 默认为0,关闭状态
;stderr_events_enabled=false   ; 这个也是一样,默认为false
;environment=A="1",B="2"       ; 这个是该子进程的环境变量,和别的子进程是不共享的
;serverurl=AUTO                ;

; The below sample eventlistener section shows all possible
; eventlistener subsection values, create one or more 'real'
; eventlistener: sections to be able to handle event notifications
; sent by supervisor.

;[eventlistener:theeventlistenername] ;这个东西其实和program的地位是一样的,也是suopervisor启动的子进
                                       程,不过它干的活是订阅supervisord发送的event。他的名字就叫
                                       listener了。我们可以在listener里面做一系列处理,比如报警等等
                                       楼主这两天干的活,就是弄的这玩意
;command=/bin/eventlistener    ; 这个和上面的program一样,表示listener的可执行文件的路径
;process_name=%(program_name)s ; 这个也一样,进程名,当下面的numprocs为多个的时候,才需要。否则默认就
                                 OK了
;numprocs=1                    ; 相同的listener启动的个数
;events=EVENT                  ; event事件的类型,也就是说,只有写在这个地方的事件类型。才会被发送


;buffer_size=10                ; 这个是event队列缓存大小,单位不太清楚,楼主猜测应该是个吧。当buffer
                                 超过10的时候,最旧的event将会被清除,并把新的event放进去。
                                 默认值为10。。非必须选项
;directory=/tmp                ; 进程执行前,会切换到这个目录下执行
                                 默认为不切换。。。非必须
;umask=022                     ; 淹没,默认为none,不说了
;priority=-1                   ; 启动优先级,默认-1,也不扯了
;autostart=true                ; 是否随supervisord启动一起启动,默认true
;autorestart=unexpected        ; 是否自动重启,和program一个样,分true,false,unexpected等,注意
                                  unexpected和exitcodes的关系
;startsecs=1                   ; 也是一样,进程启动后跑了几秒钟,才被认定为成功启动,默认1
;startretries=3                ; 失败最大尝试次数,默认3
;exitcodes=0,2                 ; 期望或者说预料中的进程退出码,
;stopsignal=QUIT               ; 干掉进程的信号,默认为TERM,比如设置为QUIT,那么如果QUIT来干这个进程
                                 那么会被认为是正常维护,退出码也被认为是expected中的
;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false             ; send stop signal to the UNIX process group (default false)
;killasgroup=false             ; SIGKILL the UNIX process group (def false)
;user=chrism                   ;设置普通用户,可以用来管理该listener进程。
                                默认为空。。非必须设置
;redirect_stderr=true          ; 为true的话,stderr的log会并入stdout的log里面
                                默认为false。。。非必须设置
;stdout_logfile=/a/path        ; 这个不说了,好几遍了
;stdout_logfile_maxbytes=1MB   ; 这个也是
;stdout_logfile_backups=10     ; 这个也是
;stdout_events_enabled=false   ; 这个其实是错的,listener是不能发送event
;stderr_logfile=/a/path        ; 这个也是
;stderr_logfile_maxbytes=1MB   ; 这个也是
;stderr_logfile_backups        ; 这个不说了
;stderr_events_enabled=false   ; 这个也是错的,listener不能发送event
;environment=A="1",B="2"       ; 这个是该子进程的环境变量
                                 默认为空。。。非必须设置
;serverurl=AUTO                ; override serverurl computation (childutils)

; The below sample group section shows all possible group values,
; create one or more 'real' group: sections to create "heterogeneous"
; process groups.

;[group:thegroupname]  ;这个东西就是给programs分组,划分到组里面的program。我们就不用一个一个去操作了
                         我们可以对组名进行统一的操作。 注意:program被划分到组里面之后,就相当于原来
                         的配置从supervisor的配置文件里消失了。。。supervisor只会对组进行管理,而不再
                         会对组里面的单个program进行管理了
;programs=progname1,progname2  ; 组成员,用逗号分开
                                 这个是个必须的设置项
;priority=999                  ; 优先级,相对于组和组之间说的
                                 默认999。。非必须选项

; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.

;[include]                         ;这个东西挺有用的,当我们要管理的进程很多的时候,写在一个文件里面
                                    就有点大了。我们可以把配置信息写到多个文件中,然后include过来
;files = relative/directory/*.ini

配置样例

[supervisord] 
http_port=/var/tmp/supervisor.sock ; (default is to run a UNIX domain socket server) 
;http_port=127.0.0.1:9001  ; (alternately, ip_address:port specifies AF_INET) 
;sockchmod=0700              ; AF_UNIX socketmode (AF_INET ignore, default 0700) 
;sockchown=nobody.nogroup     ; AF_UNIX socket uid.gid owner (AF_INET ignores) 
;umask=022                   ; (process file creation umask;default 022) 
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log) 
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB) 
logfile_backups=10          ; (num of main logfile rotation backups;default 10) 
loglevel=info               ; (logging level;default info; others: debug,warn) 
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) 
nodaemon=false              ; (start in foreground if true;default false) 
minfds=1024                 ; (min. avail startup file descriptors;default 1024) 
minprocs=200                ; (min. avail process descriptors;default 200) 

;nocleanup=true              ; (don't clean up tempfiles at start;default false) 
;http_username=user          ; (default is no username (open system)) 
;http_password=123           ; (default is no password (open system)) 
;childlogdir=/tmp            ; ('AUTO' child log dir, default $TEMP) 
;user=chrism                 ; (default is current user, required if root) 
;directory=/tmp              ; (default is not to cd during start) 
;environment=KEY=value       ; (key value pairs to add to environment) 

[supervisorctl] 
serverurl=unix:///var/tmp/supervisor.sock ; use a unix:// URL  for a unix socket 
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket 
;username=chris              ; should be same as http_username if set 
;password=123                ; should be same as http_password if set 
;prompt=mysupervisor         ; cmd line prompt (default "supervisor") 

; The below sample program section shows all possible program subsection values, 
; create one or more 'real' program: sections to be able to control them under 
; supervisor. 

;[program:example] 
;command=/bin/echo; the program (relative uses PATH, can take args) 
;priority=999                ; the relative start priority (default 999) 
;autostart=true              ; start at supervisord start (default: true) 
;autorestart=true            ; retstart at unexpected quit (default: true) 
;startsecs=10                ; number of secs prog must stay running (def. 10) 
;startretries=3              ; max # of serial start failures (default 3) 
;exitcodes=0,2               ; 'expected' exit codes for process (default 0,2) 
;stopsignal=QUIT             ; signal used to kill process (default TERM) 
;stopwaitsecs=10             ; max num secs to wait before SIGKILL (default 10) 
;user=chrism                 ; setuid to this UNIX account to run the program 
;log_stdout=true             ; if true, log program stdout (default true) 
;log_stderr=true             ; if true, log program stderr (def false) 
;logfile=/var/log/supervisor.log    ; child log path, use NONE for none; default AUTO 
;logfile_maxbytes=1MB        ; max # logfile bytes b4 rotation (default 50MB) 
;logfile_backups=10          ; # of logfile backups (default 10) 

“;”为注释。各参数的含义都很明确。可以根据官方手册结合实验来进一步深入了解。重点说几个[program:example]中的参数

;command=/bin/echo;         supervisor启动时将要开启的进程。相对或绝对路径均可。若是相对路径则会从supervisord的$PATH变中查找。命令可带参数。 
;priority=999                   指明进程启动和关闭的顺序。低优先级表明进程启动时较先启动关闭时较后关闭。高优先级表明进程启动时启动时较后启动关闭时较先关闭。 
;autostart=true                 是否随supervisord启动而启动 
;autorestart=true               进程意外退出后是否自动重启 
;startsecs=10                   进程持续运行多久才认为是启动成功 
;startretries=3                 重启失败的连续重试次数 
;exitcodes=0,2                  若autostart设置为unexpected且监控的进程并非因为supervisord停止而退出,那么如果进程的退出码不在exitcode列表中supervisord将重启进程 
;stopsignal=QUIT                杀进程的信号 
;stopwaitsecs=10                向进程发出stopsignal后等待OS向supervisord返回SIGCHILD 的时间。若超时则supervisord将使用SIGKILL杀进程 

如下看一个使用supervisor监控的配置情况(配置中的其他默认内容在此省略)

[program:worker_for_summary] 
command=/home/op1/scripts/rabbitmqclient/worker_for_summary.py 
priority=1 
log_stderr=true             ; if true, log program stderr (def false) 

[program:worker_for_detail_all] 
command=/home/op1/scripts/rabbitmqclient/worker_for_detail_all.py 
priority=1 
log_stderr=true             ; if true, log program stderr (def false) 

[program:worker_for_detail_recent_list] 
command=/home/op1/scripts/rabbitmqclient/worker_for_detail_recent_list.py 
priority=1 
log_stderr=true             ; if true, log program stderr (def false) 

[program:worker_for_detail_recent_sset] 
command=/home/op1/scripts/rabbitmqclient/worker_for_detail_recent_sset.py 
priority=1 
log_stderr=true             ; if true, log program stderr (def false) 

[program:publisher_for_summary] 
command=/home/op1/scripts/rabbitmqclient/publisher_for_summary.py 
priority=999 
log_stderr=true             ; if true, log program stderr (def false) 

[program:publisher_for_summary_nt] 
command=/home/op1/scripts/rabbitmqclient/publisher_for_summary_nt.py 
priority=999 
log_stderr=true             ; if true, log program stderr (def false) 

[program:publisher_for_detail] 
command=/home/op1/scripts/rabbitmqclient/publisher_for_detail.py 
priority=999 
log_stderr=true             ; if true, log program stderr (def false) 

[program:publisher_for_detail_nt] 
command=/home/op1/scripts/rabbitmqclient/publisher_for_detail_nt.py 
priority=999 
log_stderr=true             ; if true, log program stderr (def false) 

配置完成后启动supervisord

[root@op-zhongkong ~]# /etc/init.d/supervisord start

可以看到配置的各个进程在后台运行了起来。停掉某个进程后supervisor会马上重启该进程

停止supervisor

[root@op-zhongkong ~]# /etc/init.d/supervisord stop

可以看到配置的各个进程都停止运行了。可以通过supervisorctl查看管理监控的进程情况

[root@op-zhongkong ~]# sudo supervisorctl 
publisher_for_detail RUNNING    pid 27557, uptime 0:00:45 
publisher_for_detail_nt RUNNING    pid 27567, uptime 0:00:45 
publisher_for_summary RUNNING    pid 27566, uptime 0:00:45 
publisher_for_summary_nt RUNNING    pid 27568, uptime 0:00:45 
worker_for_detail_all RUNNING    pid 27581, uptime 0:00:45 
worker_for_detail_recent RUNNING    pid 27582, uptime 0:00:45 
worker_for_summary RUNNING    pid 27559, uptime 0:00:45 

#可通过help了解命令的更多用法 
supervisor> help 

Documented commands (type help <topic>): 
======================================== 
EOF    exit  maintail  quit    restart   start   stop 
clear  help  open      reload  shutdown  status  tail 

supervisor> help stop 
stop <processname>            Stop a process. 
stop <processname> <processname>    Stop multiple processes 
stop all                Stop all processes 
  When all processes are stopped, they are stopped in 
  reverse priority order (see config file) 
supervisor> help status 
status          Get all process status info. 
status <name>     Get status on a single process by name. 
status <name> <name>    Get status on multiple named processes. 

#停止某个进程 
supervisor> stop publisher_for_summary 
publisher_for_summary: stopped 

#查看此时此刻的状态 
supervisor> status 
publisher_for_detail RUNNING    pid 27557, uptime 0:05:41 
publisher_for_detail_nt RUNNING    pid 27567, uptime 0:05:41 
publisher_for_summary STOPPED    Feb 27 02:48 PM 
publisher_for_summary_nt RUNNING    pid 27568, uptime 0:05:41 
worker_for_detail_all RUNNING    pid 27581, uptime 0:05:41 
worker_for_detail_recent RUNNING    pid 27582, uptime 0:05:41 
worker_for_summary RUNNING    pid 27559, uptime 0:05:41 
#发现被supervisorctl停掉的进程不会被自动重启 

#开启刚才停掉的进程 
supervisor> start publisher_for_summary 
publisher_for_summary: started 
supervisor> status 
publisher_for_detail RUNNING    pid 27557, uptime 0:08:02 
publisher_for_detail_nt RUNNING    pid 27567, uptime 0:08:02 
publisher_for_summary RUNNING    pid 3035, uptime 0:00:04 
publisher_for_summary_nt RUNNING    pid 27568, uptime 0:08:02 
worker_for_detail_all RUNNING    pid 27581, uptime 0:08:02 
worker_for_detail_recent RUNNING    pid 27582, uptime 0:08:02 
worker_for_summary RUNNING    pid 27559, uptime 0:08:02 

#停掉所有进程 
supervisor> stop all 
worker_for_detail_recent: stopped 
worker_for_detail_all: stopped 
publisher_for_summary_nt: stopped 
publisher_for_detail_nt: stopped 
publisher_for_summary: stopped 
worker_for_summary: stopped 
publisher_for_detail: stopped 
supervisor> status 
publisher_for_detail STOPPED    Feb 27 02:51 PM 
publisher_for_detail_nt STOPPED    Feb 27 02:51 PM 
publisher_for_summary STOPPED    Feb 27 02:51 PM 
publisher_for_summary_nt STOPPED    Feb 27 02:51 PM 
worker_for_detail_all STOPPED    Feb 27 02:51 PM 
worker_for_detail_recent STOPPED    Feb 27 02:51 PM 
worker_for_summary STOPPED    Feb 27 02:51 PM 

#开启所有进程 
supervisor> start all 
publisher_for_detail: started 
worker_for_summary: started 
publisher_for_summary: started 
publisher_for_detail_nt: started 
publisher_for_summary_nt: started 
worker_for_detail_all: started 
worker_for_detail_recent: started 
supervisor> status 
publisher_for_detail RUNNING    pid 5111, uptime 0:00:15 
publisher_for_detail_nt RUNNING    pid 5141, uptime 0:00:15 
publisher_for_summary RUNNING    pid 5135, uptime 0:00:15 
publisher_for_summary_nt RUNNING    pid 5147, uptime 0:00:15 
worker_for_detail_all RUNNING    pid 5153, uptime 0:00:15 
worker_for_detail_recent RUNNING    pid 5159, uptime 0:00:14 
worker_for_summary RUNNING    pid 5112, uptime 0:00:15 

————————————下面是线上用过的supervisor监控python程序的一个配置———————-

[program:xcspam]
command=/usr/bin/python /app/web/xcspam/bin/main.py --port=93%(process_num)02d
process_name=%(program_name)s_%(process_num)02d
numprocs=16
numprocs_start=1
directory=/app/web/xcspam
user=work
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/data/log/xcspam/xcspam.log
stderr_logfile=/data/log/xcspam/xcspam_error.log
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0
environment=PYTHONPATH=/app/web/xcspam, ZUIYOU_ENV=production

[program:report]
command=/usr/bin/celery -A worker worker -Q report --loglevel=INFO
directory=/app/web/xcspam/bin
user=work
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/data/log/xcspam/celery-report.log
stderr_logfile=/data/log/xcspam/celery-report_error.log
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0
environment=PYTHONPATH=/app/web/xcspam, ZUIYOU_ENV=production

[program:antiwater]
command=/usr/bin/celery -A worker worker -Q antiwater --loglevel=INFO
directory=/app/web/xcspam/bin
user=work
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/data/log/xcspam/celery-antiwater.log
stderr_logfile=/data/log/xcspam/celery-antiwater_error.log
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0
environment=PYTHONPATH=/app/web/xcspam, ZUIYOU_ENV=production

[program:celery-flower]
command=/usr/bin/celery flower -A worker --address=10.31.51.232 --port=9330
directory=/app/web/xcspam/bin
user=work
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/data/log/xcspam/celery-flower.log
stderr_logfile=/data/log/xcspam/celery-flower_error.log
stdout_logfile_maxbytes=0
stderr_logfile_maxbytes=0
environment=PYTHONPATH=/app/web/xcspam, ZUIYOU_ENV=production

由于默默使用supervisorctl命令查看监控的程序运行状态是交互式的,所以自己写了个简单的脚本命令superctl,可以直接查看

[root@op-zhongkong ~]# cat /usr/local/bin/superctl
/usr/bin/python /usr/local/bin/superctl -c /etc/supervisord.conf $1 $2
[root@op-zhongkong ~]# chmod 755 /usr/local/bin/superctl

[root@op-zhongkong ~]#  superctl status
antiwater                        RUNNING   pid 17172, uptime 0:21:23
celery-flower                    RUNNING   pid 20507, uptime 0:18:52
report                           RUNNING   pid 16752, uptime 0:21:35
xcspam:xcspam_01                 RUNNING   pid 14594, uptime 0:23:00
xcspam:xcspam_02                 RUNNING   pid 14526, uptime 0:23:01
xcspam:xcspam_03                 RUNNING   pid 14551, uptime 0:23:01
xcspam:xcspam_04                 RUNNING   pid 14641, uptime 0:22:59
xcspam:xcspam_05                 RUNNING   pid 14642, uptime 0:22:58
xcspam:xcspam_06                 RUNNING   pid 14608, uptime 0:22:59
xcspam:xcspam_07                 RUNNING   pid 14618, uptime 0:22:59
xcspam:xcspam_08                 RUNNING   pid 14674, uptime 0:22:58
xcspam:xcspam_09                 RUNNING   pid 14690, uptime 0:22:58
xcspam:xcspam_10                 RUNNING   pid 14498, uptime 0:23:02
xcspam:xcspam_11                 RUNNING   pid 14462, uptime 0:23:03
xcspam:xcspam_12                 RUNNING   pid 14463, uptime 0:23:03
xcspam:xcspam_13                 RUNNING   pid 14579, uptime 0:23:00
xcspam:xcspam_14                 RUNNING   pid 14749, uptime 0:22:58
xcspam:xcspam_15                 RUNNING   pid 14669, uptime 0:22:58
xcspam:xcspam_16                 RUNNING   pid 14512, uptime 0:23:02

PostgreSQL日志分析工具——pgBadger

摘要

之前曾介绍过PostgreSQL的日志审计,这会将执行的SQL输出到服务日志(pg_log),势必会带来性能问题,真正的问题是,这些记录对我们有多少帮助?所以我们来看一下如何使用增加的日志记录来做一些有用的事情。

pg_stat_statements 不仅输出SQL语句,还可以输出执行时间等,通过分析此服务器日志,可以知道执行什么样的SQL,以及缩小范围找到哪些SQL有可能出现性能问题。

SQL日志分析工具pgBadger

pgBadger 主页:home 下载地址:download

pgBadger是在Perl中创建的一个脚本,与PHP脚本中开发的名为pgFouine的程序具有相同的功能。 在命令行上指定并执行服务器日志时,会生成一个将服务器日志分析为HTML文件的报告。

此外,它不仅收集SQL,还收集服务器日志中的错误日志等。

环境

CentOS 6.9、PostgreSQL9.5.9、pgBadger 9.2

安装pgBadger

tar zxvf pgbadger-9.2.tar.gz 
cd pgbadger-9.2 
[root@localhost pgbadger-9.2]# ll 
total 1416 
drwxr-xr-x. 8 root root    4096 Sep 11 01:13 blib 
-rw-rw-r--. 1 root root   94609 Jul 28 07:45 ChangeLog 
-rw-rw-r--. 1 root root     347 Jul 28 07:45 CONTRIBUTING.md 
drwxrwxr-x. 2 root root    4096 Jul 28 07:45 doc 
-rw-rw-r--. 1 root root     903 Jul 28 07:45 LICENSE 
-rw-r--r--. 1 root root   24018 Sep 11 01:13 Makefile 
-rw-rw-r--. 1 root root    1400 Jul 28 07:45 Makefile.PL 
-rw-rw-r--. 1 root root      81 Jul 28 07:45 MANIFEST 
-rw-rw-r--. 1 root root     334 Jul 28 07:45 META.yml 
-rw-rw-r--. 1 root root 1256247 Jul 28 07:45 pgbadger 
-rw-r--r--. 1 root root       0 Sep 11 01:13 pm_to_blib 
-rw-rw-r--. 1 root root   30799 Jul 28 07:45 README 
drwxrwxr-x. 3 root root    4096 Jul 28 07:45 resources 
drwxrwxr-x. 2 root root    4096 Jul 28 07:45 tools 

perl Makefile.PL 

make; 

make install;  

查看安装版本

[root@localhost pgbadger-9.2]# which pgbadger 
/usr/local/bin/pgbadger 
[root@localhost pgbadger-9.2]# pgbadger -V 
pgBadger version 9.2  

PostgreSQL配置

logging_collector = on 
log_filename = 'postgresql-%w.log' 
log_file_mode = 0640 
log_truncate_on_rotation = on 
log_rotation_age = 1d 
log_min_duration_statement = 0 
log_checkpoints = on 
log_connections = on 
log_disconnections = on 
log_duration = on 
log_lock_waits = on 
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d '  

安装httpd、php

yum install httpd 
chkconfig httpd on 
service httpd start 
yum install php  

查看/var/www

[root@localhost www]# pwd 
/var/www 
[root@localhost www]# ll 
total 20 
drwxr-xr-x. 2 root root 4096 Aug 15 15:45 cgi-bin 
drwxr-xr-x. 3 root root 4096 Sep 11 02:23 error 
drwxr-xr-x. 2 root root 4096 Sep 11 03:43 html 
drwxr-xr-x. 3 root root 4096 Sep 11 02:23 icons 
drwxr-xr-x. 3 root root 4096 Sep 11 04:33 pgbadger  

日志分析

./pgbench -i 
./pgbench -c 10 -t 1000 

[root@localhost ~]# pgbadger /opt/postgres/db/pgsql-9.5.9/data/pg_log/*.log -o /var/www/pgbadger/out.html -f stderr 
[========================>] Parsed 10485802 bytes of 10485802 (100.00%), queries: 65341, events: 2 
[========================>] Parsed 755640 bytes of 755640 (100.00%), queries: 70036, events: 2 
LOG: Ok, generating html report... 
[root@localhost ~]#   

在浏览器打开/var/www/pgbadger/out.html

未分类

未分类

未分类

连续日志分析(增量模式)

增量模式用于连续获取分析结果。

以增量模式执行pgBadger会在第二天创建下一个报告(增量报告)。

[root@localhost ~]# pgbadger -I /opt/postgres/db/pgsql-9.5.9/data/pg_log/*.log -o /var/www/pgbadger/out.html -f stderr 
[========================>] Parsed 1499636 bytes of 1499636 (100.00%), queries: 0, events: 0 
[========================>] Parsed 10485846 bytes of 10485846 (100.00%), queries: 65379, events: 0 
[========================>] Parsed 743891 bytes of 743891 (100.00%), queries: 4625, events: 0 
LOG: Ok, generating HTML daily report into /var/www/pgbadger//2017/09/11/... 
LOG: Ok, generating HTML daily report into /var/www/pgbadger//2017/09/12/... 
LOG: Ok, generating HTML weekly report into /var/www/pgbadger//2017/week-38/... 
LOG: Ok, generating global index to access incremental reports... 

[root@localhost ~]# ll /var/www/pgbadger/ 
total 692 
drwxr-xr-x. 4 root root   4096 Sep 11 21:09 2017 
-rw-r--r--. 1 root root 695474 Sep 12 21:15 index.html 
-rw-r--r--. 1 root root    187 Sep 12 21:15 LAST_PARSED 
drwxrwxrwx. 6 1107 1107   4096 Aug 28 17:44 postgresql-9.5.9 
[root@localhost ~]# ll /var/www/pgbadger/2017/ 
total 8 
drwxr-xr-x. 4 root root 4096 Sep 12 21:15 09 
drwxr-xr-x. 2 root root 4096 Sep 11 21:09 week-38 
[root@localhost ~]#   

这里创建一个具有年份的目录,链接所有报告的索引页面以及保存最后一个解析行的文件。

以增量模式创建报表时,会创建链接每个报表页面的索引页(index.html)。

未分类

总结

pgBadger是一个完美的分析工具,旨在提高数据库的性能,因为它具有丰富的统计结果。

当你不知道PostgreSQL性能坏的原因,如果你认为查询执行时间是可疑的,可以尝试使用它。

postgresql导入导出数据库

一、postgresql 导出的数据库, 标准语句

pg_dump --host [**地址**] --port [**端口**] --username [**数据库的用户名**] > [**导出的文件**] [**数据库名字**]

例子:

pg_dump --host xxxxx.com --port 3434 --username cs  > cs.sql cs

我们从http://xxxxx.com的地址上, 通过postgresql开放端口3434,并使用的cs的用户,导出了命为cs数据库, 存到了cs.sq文件中。

二、postgresql导入数据库, 标准语句

psql -d [**数据库名字**] -f [**文件名**] [**用户名**]

例子:

psql -d cs  -f cs.sql cs

我们首先要将cs.sql导入目标主机上, 在运行上条命令。

我们将cs.sql文件中的数据库导入cs用户下的cs数据库中。

三、导入docker容器中的postgresql中

sudo docker exec -i [**容器id**] psql -U [**用户名**] -d [**数据库名**] < [文件路径]

例子:

sudo docker exec -i xxxxxxx psql -U cs  -d cs < /home/lixiang/cs.sql

提示:

mac启动数据库

pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start

创建用户

CREATE USER [**db_name**] WITH PASSWORD [**password**]     

修改角色权限:

alter user [**用户名**] superuser

ubuntu17.04使用systemd设置开机启动

ubuntu从16.04开始不再使用initd管理系统,改用systemd。
然而systemd很难用,刚开始接触有点烦,改变太大,跟之前的完全不同。

吐槽完开始正题:

使用systemd设置开机启动

为了像以前一样,在/etc/rc.local中设置开机启动程序,需要以下几步:

1、systemd默认读取/etc/systemd/system下的配置文件,该目录下的文件会链接/lib/systemd/system/下的文件。一般系统安装完/lib/systemd/system/下会有rc-local.service文件,即我们需要的配置文件。

链接过来:

ln -fs /lib/systemd/system/rc-local.service /etc/systemd/system/rc-local.service
cd /etc/systemd/system/
vim rc-local.service

rc-local.service内容:

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.local
After=network.target

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no

[Install]
WantedBy=multi-user.target
Alias=rc-local.service

2、创建/etc/rc.local文件

touch /etc/rc.local

3、赋可执行权限

chmod 755 /etc/rc.local

4、编辑rc.local,添加需要开机启动的任务

#!/bin/bash

echo "test test " > /var/test_boot_up.log

5、执行reboot重启系统验证OK。

最后,说一下/etc/systemd/system/下的配置文件(XXXX.service),
其中有三个配置项,[Unit] / [Service] / [Install]

  • [Unit] 区块:启动顺序与依赖关系。
  • [Service] 区块:启动行为,如何启动,启动类型。
  • [Install] 区块,定义如何安装这个配置文件,即怎样做到开机启动。

ceph运维中常见的pg状态

pg ( placement group ) 是数据存储的重要单位。

在使用 ceph 的时候, pg 会经常发生状态的变化,例如:

  • 当创建池的时候, 将会创建相应的 pg, 那么可以看到 pg creating 状态
  • 当部分 pg 创建成功后, 将会发现 pg 会进入 peering 状态
  • 当所有 pg peering 完成后, 将可见到状态变成 active+clean

常见的 pg 状态

creating (创建中)

PG 正在被创建, 通常当存储池正在卑创建或增加一个存储池的 PG 数量时, PG 会呈现这个状态

Down (失效)

PG 处于失效状态, PG 应该处于离线状态

repair(修复)

PG 正在被检查, 被发现的任何不一致都将尽可能被修复.

peering (等待互联)

  1. 当 ceph peering pg, ceph 将会把 pg 副本协定导入 osd, 当 ceph 完成 peering, 意味着 osd 同意当前 PG 状态, 并允许写入
  2. PG 处于 peering 过程中, peering 由主 osd 发起的使存放 PG 副本的所有 OSD 就 PG 的所有对象和元素数据的状态达成一致的过程, peering 过程完成后, 主 OSD 就可以接受客户端写请求.

Active (活动)

  1. 当 ceph 完成 peering 过程, pg 将会变成 active, active 状态意味着 pg 中的数据变得可用, 主 pg 将可执行读写操作
  2. PG 是活动的, 意味着 PG 中的数据可以被读写, 对该 PG 的操作请求都讲会被处理.

Clean (干净)

  1. 当 pg 显示 clean 状态, 主 osd 与副本 osd 成功同步并且没有异步复制, ceph 在 pg 中所有对象具有正确的副本数量
  2. PG 中的所有对象都已经卑复制了规定的副本数量.

Replay (重做)

某 OSD 崩溃后, PG 正在等待客户端重新发起操作

Degraded (降级)

  1. 当客户端写对象到主 osd, 主 OSD 会把数据写复制到对应复制 OSD, 在主 OSD 把对象写入存储后, PG 会显示为 degraded 状态, 直到主 osd 从复制 OSD 中接收到创建副本对象完成信息

  2. PG 处于 active+degraded 原因是因为 OSD 是处于活跃, 但并没有完成所有的对象副本写入, 假如 OSD DOWN, CEPH 标记每个 PG 分配到这个相关 OSD 的
    状态为 degraded, 当 OSD 重新上线, OSD 将会重新恢复,

  3. 假如 OSD DOWN 并且 degraded 状态持续, CEPH 会标记 DOWN OSD, 并会对集群迁移相关 OSD 的数据, 对应时间由 mon osd down out interval 参数决定

  4. PG 可以被北极为 degraded, 因为 ceph 在对应 PG 中无法找到一个或者多个相关的对象, 你不可以读写 unfound 对象, 你仍然可以访问标记为 degraded PG 的其他数据

  5. PG 中部分对象的副本数量未达到规定的数量

Inconsistent (不一致)

PG副本出现不一致, 对象大小不正确或者恢复借宿后某个副本出现对象丢失现象

recoverying (恢复中)

ceph 设备故障容忍在一定范围的软件与硬件问题, 当 OSD 变 DOWN, 那么包含该 OSD 的 PG 副本都会有问题, 当 OSD 恢复, OSD 对应的 PG 将会更新
并反映出当前状态, 在一段时间周期后, OSD 将会恢复 recoverying 状态

recovery 并非永远都有效, 因为硬件故障可能会导致多个 OSD 故障, 例如, 网络交换机故障, 可以导致集群中的多个主机及主机包含的 OSD 故障
当网络恢复之后, 每个 OSD 都必须执行恢复

CEPH 提供一定数量的设定在新服务请求与恢复 PG 中数据对象时的资源平衡,
osd recovery delay start 设定允许 osd 重启, re-peer 并在启动 恢复之前处理一些回应请求,
osd recovery threads 设定了恢复过程中线程限制 (默认 1 )
osd recovery thread timeout 设定线程超时, 因为可能出现多个 osd 故障, 重启后在 re-peer 过程中可能出现污染
osd recovery max active 设定限制对一个 osd 从故障后, 恢复请求并发数量
osd recovery max chunk 限制恢复时的数据 chunk 大小, 预防网络堵塞

PG 正在迁移或者同步对象及其副本, 一个 OSD 停止服务(DOWN), 其内容将会落后与 PG 内的其他副本, 这时 PG 将会进入 recoverying 状态, 该 OSD 上的对象将从其他副本同步过来

BACK FILLING (回填)

当新 OSD 加入集群, CRUSH 将会为集群新添加的 OSD 重新分配 PG, 强制新的 OSD 接受重新分配的 PG 并把一定数量的负载转移到新 OSD 中
back filling OSD 会在后台处理, 当 backfilling 完成, 新的 OSD 完成后, 将开始对请求进行服务

在 backfill 操作期间, 你可以看到多种状态,
backfill_wait 表示 backfill 操作挂起, 但 backfill 操作还没有开始 ( PG 正在等待开始回填操作 )
backfill 表示 backfill 操作正在执行
backfill_too_full 表示在请求 backfill 操作, 由于存储能力问题, 但不可以完成,

ceph 提供设定管理装载重新分配 PG 关联到新的 OSD
osd_max_backfills 设定最大数量并发 backfills 到一个 OSD, 默认 10
osd backfill full ratio 当 osd 达到负载, 允许 OSD 拒绝 backfill 请求, 默认 85%,
假如 OSD 拒绝 backfill 请求, osd backfill retry interval 将会生效, 默认 10 秒后重试
osd backfill scan min , osd backfill scan max 管理检测时间间隔

一个新 OSD 加入集群后, CRUSH 会把集群先有的一部分 PG 分配给他, 该过程称为回填, 回填进程完成后, 新 OSD 准备好了就可以对外提供服务

REMAPPED (重映射)

当 pg 改变, 数据从旧的 osd 迁移到新的 osd, 新的主 osd 应该请求将会花费一段时间, 在这段时间内, 将会继续向旧主 osd 请求服务, 直到
PG 迁移完成, 当数据迁移完成, mapping 将会使用新的 OSD 响应主 OSD 服务

当 PG 的 action set 变化后, 数据将会从旧 acting set 迁移到新 action set, 新主 OSD 需要过一段时间后才能提供服务, 因此它会让老的主 OSD 继续提供服务, 知道 PG 迁移完成, 数据迁移完成后, PG map 将会使用新 acting set 中的主 OSD

参考例子

[root@hh-yun-ceph-cinder015-128055 ~]# ceph osd map volumes rbd_id.volume-1421625f-a9a2-41d0-8023-4cec54b33a57
osdmap e5276 pool 'volumes' (1) object 'rbd_id.volume-1421625f-a9a2-41d0-8023-4cec54b33a57' -> pg 1.2cdd8028 (1.28) -> up ([32,26,41], p32) acting ([32,26,41], p32)

STALE (旧)

当 ceph 使用 heartbeat 确认主机与进程是否运行, ceph osd daemon 可能由于网络临时故障, 获得一个卡住状态 (stuck state) 没有得到心跳回应
默认, osd daemon 会每 0.5 秒报告 PG, up 状态, 启动与故障分析,
假如 PG 中主 OSD 因为故障没有回应 monitor 或者其他 OSD 报告 主 osd down, 那么 monitor 将会标记 PG stale,
当你重启集群, 通常会看到 stale 状态, 直到 peering 处理完成,
在集群运行一段时候, 看到 stale 状态, 表示主 osd PG DOWN 或者没有主 osd 没有报告 PG 信息到 monitor 中

PG 处于未知状态, monitors 在 PG map 改变后还没有收到过 PG 的更新, 启用一个集群后, 常常会看到主 peering 过程结束前 PG 处于该状态

Scrubbing (清理中)

PG 在做不一至性校验

有问题的 PG

inactive

PG 很长时间没有显示为 acitve 状态, (不可执行读写请求), PG 不可以执行读写, 因为等待 OSD 更新数据到最新的备份状态

unclean

PG 很长时间都不是 clean 状态 (不可以完成之前恢复的操作), PG 包含对象没有完成相应的复制副本数量, 通常都要执行恢复操作

stale

PG 状态很长时间没有被 ceph-osd 更新过, 标识存储在该 GP 中的节点显示为 DOWN, PG 处于 unknown 状态, 因为 OSD 没有报告 monitor 由

反向代理之haproxy安装及简单配置tcp代理

一、简介

HAProxy提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、快速并且
可靠的一种解决方案。

HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。

HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单
安全的整合进您当前的架构中,同时可以保护你的web服务器不被暴露到网络上。

HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接
事件驱动模型因为在有更好的资源和时间管理的用户空间(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。

这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。

二、安装

1. 环境准备

环境设置:

web-node1:
[root@web-node1 src]# uname -r
3.10.0-229.el7.x86_64
[root@web-node1 src]# uname -m
x86_64
[root@web-node1 src]# cat /etc/hostname 
web-node1
[root@web-node1 src]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.0.0.64    zabbix_master
10.0.0.65    web-node1
10.0.0.66    web-node2
web-node2:
[root@web-node2 src]# uname -r
3.10.0-229.el7.x86_64
[root@web-node2 src]# uname -m
x86_64
[root@web-node2 src]# cat /etc/hostname 
web-node2
[root@web-node2 src]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.0.0.64    zabbix_master
10.0.0.65    web-node1
10.0.0.66    web-node2

2. 安装haproxy

目前haproxy最高版本是1.7.8,本次实验使用1.7.5

下载地址:http://www.haproxy.org/download/1.7/

web-node1:

[root@web-node1 haproxy-1.7.5]#    cd /usr/local/src
[root@web-node1 ]# wget http://www.haproxy.org/download/1.7/src/haproxy-1.7.5.tar.gz
[root@web-node1 haproxy-1.7.5]#  tar -zxf  haproxy-1.7.5.tar.gz 
[root@web-node1 haproxy-1.7.5]#  cd haproxy-1.7.5
[root@web-node1 haproxy-1.7.5]#   make TARGET=linux2628 PREFIX=/usr/local/haproxy-1.7.5
[root@web-node1 haproxy-1.7.5]# make install PREFIX=/usr/local/haproxy-1.7.5
[root@web-node1 haproxy-1.7.5]# cp /usr/local/sbin/haproxy /usr/sbin/
[root@web-node1 local]# ln -s /usr/local/haproxy-1.7.5 /usr/local/haproxy
[root@web-node1 haproxy-1.7.5]# haproxy -v
HA-Proxy version 1.7.5 2017/04/03
Copyright 2000-2017 Willy Tarreau <[email protected]>
#参数说明
TARGET=linux26 #内核版本,使用uname -r查看内核,如:2.6.18-371.el5,此时该参数就为linux26;kernel 大于2.6.28的用:TARGET=linux2628
PREFIX=/usr/local/haprpxy #/usr/local/haprpxy为haprpxy安装路径

3. 编辑Haproxy启动脚本

[root@web-node1 haproxy-1.7.5]# cp /usr/local/src/haproxy-1.7.5/examples/haproxy.init /etc/init.d/haproxy
[root@web-node1 haproxy-1.7.5]# chmod +x /etc/init.d/haproxy

4. 创建haproxy相关目录

[root@web-node1 haproxy-1.7.5]# useradd -r haproxy  ##创建系统用户
[root@web-node1 haproxy-1.7.5]# mkdir /etc/haproxy
[root@web-node1 haproxy-1.7.5]# mkdir /var/lib/haproxy
[root@web-node1 haproxy-1.7.5]# mkdir /var/run/haproxy

5. 修改配置文件

[root@web-node1 haproxy-1.7.5]# cat /etc/haproxy/haproxy.cfg 
global
   log 127.0.0.1 local3 warning
   chroot /var/lib/haproxy
   user haproxy
   group haproxy
   nbproc  1
   maxconn 65535 
   daemon
defaults
   log global
   option dontlognull
   timeout connect 5000
   timeout client 50000
   timeout server 50000
listen test
 bind 10.0.0.65:8080
 mode tcp
 #balance roundrobin
 timeout server 15s
 timeout connect 15s
 server web01 10.0.0.66:22 check port 22 inter 5000 fall 5 

6. 启动haproxy

检查语法:
/usr/sbin/haproxy  -f /etc/haproxy/haproxy.cfg -c
[root@web-node1 haproxy-1.7.5]# /etc/init.d/haproxy start
查看进程:
[root@web-node1 haproxy-1.7.5]# ps -ef|grep haproxy
haproxy   11126      1  0 16:20 ?        00:00:00 /usr/sbin/haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid
root      11183   2633  0 16:32 pts/1    00:00:00 grep --color=auto haproxy

三、测试tcp代理是否成功

未分类

上述可以看出已经成功了。

搭建自己的ngrok服务器-debian版

作为一个Web开发者,我们有时候会需要临时地将一个本地的Web网站部署到外网,以供他人体验评价或协助调试等等,通常我们会这么做:

  1. 找到一台运行于外网的Web服务器。
  2. 服务器上有网站所需要的环境,否则自行搭建
  3. 将网站部署到服务器上
  4. 调试结束后,再将网站从服务器上删除

只不过是想向朋友展示一下网站而已,要不要这么麻烦,累感不爱。

安装go lang环境

wget http://www.golangtc.com/static/go/1.7.3/go1.7.3.linux-amd64.tar.gz

常见的不同版本根据下方来匹配(可以到这里下载):

mac: darwin-amd64
ubuntu: linux-amd64
centos: linux-386

或者使用命令安装:

apt-get install golang-go

安装git

apt-get install git

git clone ngrok源码,编译

ngrok源码:https://github.com/inconshreveable/ngrok.git

进入/usr/local目录

git clone https://github.com/inconshreveable/ngrok.git

引入临时的全局环境变量,此次登录有效

# 这个等会编译的时候要用
export GOPATH=/usr/local/ngrok/
# 这个是你自己的域名,可以是二级或三级域名
# 注意,这边ngrok.gabin.top和它的所有子域名都必须指向中转服务器,我最开始就是没有注意这点,导致各种没报错,但是就是不能用
export NGROK_DOMAIN="atecher.net"

替换域名证书,注意到了吗,NGROK_DOMAIN这个环境变量是我们刚刚设置的。

#生成证书
cd /usr/local/ngrok
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csr
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 5000
#替换证书
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp server.crt assets/server/tls/snakeoil.crt
cp server.key assets/server/tls/snakeoil.key

开始生成服务端执行文件

# 自己注意下,不同操作系统“GOARCH”是不一样的参数,上面也有写到了
GOOS=linux GOARCH=amd64 make release-server

成功之后在/usr/local/ngrok/bin目录下会生成一个ngrokd的文件,这就是服务端的启动执行文件了

生成客户端可执行文件

#--mac
cd /usr/local/ngrok
GOOS=darwin GOARCH=amd64 make release-client
#--window
cd /usr/local/ngrok
GOOS=windows GOARCH=amd64 make release-client
#成功之后在/usr/local/ngrok/bin目录下会生成对应的目录,一般是darwin_amd64和window_64,前一个是mac的,后一个是window的

替换掉引用(国内被墙了,没法用)

vim /usr/local/ngrok/src/ngrok/log/logger.go
# 替换掉import中log的引用,记得删除旧的,别注释了,会报错哈哈
log "github.com/keepeye/log4go"

调试

  • 启动服务端,这边使用的是80端口。一般都需要用这个,原本想用反向代理,发现好像是不行。如果有发现可以的朋友,可以分享一下。

如果需要在后台执行的话,参考nohup命令

# 由于NGROK_DOMAIN是临时的环境变量,所以如果要重复使用的话,这个变量最好保存起来,否则下次登录就失效了。
/usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":80"
  • 启动客户端

先设置好配置文件:同目录下创建一个ngrok.cfg

server_addr: "blog.atecher.net:4443"
trust_host_root_certs: false
# 通过配置文件启动,这边的端口代表的是自己本地调试程序启用的端口,一般是8080
./ngrok -config=./ngrok.cfg -subdomain=blog 80

好了,可以用了。访问以下 blog.atecher.net

PS:

  1. 其实主要就是装好go环境,如果想学习新的程序语言,可以考虑下这个最近正火的语言
  2. 需要知晓基础的一些知识:环境变量、证书、make(虽然我也不是很懂,总之做多了会有点感觉,就感觉这么做是对的…)
  3. 如果没有测试环境可以用的话,可以购买特价的国际域名,一般一年不要十几二十块的,然后申请个像是华为企业云的服务器(本人就申请了一个1块钱15天的试用服务),就可以自己动手尝试了
  4. 其实自己没有服务器资源的可以使用国人分享出来的,百度搜索一下,最近看着还蛮多的

修复阿里云 Debian 源

经常重装的我,每次重装完忘记修改阿里云的默认源,update 之后每次都会报错。

下面是修复步骤。

1. 先删除默认的源

rm /etc/apt/sources.list.d/sources-aliyun*

2. 在修改源

vim /etc/apt/sources.list.d/sources-aliyun-0.list

Debian 7 修改成这样:

deb http://mirrors.aliyun.com/debian wheezy main contrib non-free
deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free
deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free
deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free
deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free
deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free

Debian 8 修改成这样:

deb http://mirrors.cloud.aliyuncs.com/debian/ jessie main contrib non-free
deb-src http://mirrors.cloud.aliyuncs.com/debian/ jessie main contrib non-free
deb http://mirrors.cloud.aliyuncs.com/debian/ jessie-proposed-updates main non-free contrib
deb-src http://mirrors.cloud.aliyuncs.com/debian/ jessie-proposed-updates main non-free contrib
deb http://mirrors.cloud.aliyuncs.com/debian/ jessie-updates main contrib non-free
deb-src http://mirrors.cloud.aliyuncs.com/debian/ jessie-updates main contrib non-free

最后:

apt-get update

Debian/Ubuntu 更新内核开启 TCP BBR 拥塞控制算法

BBR (Bottleneck Bandwidth and RTT) 是 Google 提供的 TCP 拥塞控制算法,适用于复杂网络环境下的 TCP 加速。

首先需要准备的条件

  • Debian 8.x 或者 Debian 9.x 系统,当然也适合 Ubuntu 14.04 或 Ubuntu 16.04
  • 如果是虚拟机,那么得使用 KVM 或 Xen 等可以修改内核的平台
  • 如果不是新的机器,请事先做好备份,因为内核万一挂了机器启动不起来是一件及其麻烦的事情

升级内核

BBR 只支持 4.9.x 以上的内核,所以我们需要更新升级以下

如果你使用的是 Debian 9.x,那么这一步可以直接跳过,其他三个内核版本较旧的系统,我们可以使用 Ubuntu 打包好的内核安装包

首先,找到 4.9.x 以上版本的稳定内核,这里我们推荐使用 LTS 版本,目前最新的是 4.9.40 下载安装即可

mkdir kernel-tmp && cd kernel-tmp
wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.40/linux-headers-4.9.40-040940_4.9.40-040940.201707271932_all.deb
wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.40/linux-headers-4.9.40-040940-generic_4.9.40-040940.201707271932_amd64.deb
wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.40/linux-image-4.9.40-040940-generic_4.9.40-040940.201707271932_amd64.deb
sudo dpkg -i *.deb

安装完以后直接 reboot 重启,一切顺利的话请检查以下当前的内核版本

root@debian ~ # uname -r
4.9.0-3-amd64

写入配置文件

直接修改 /etc/sysctl.conf 文件即可

cat >> /etc/sysctl.conf << EOF
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF

然后使用 sysctl -p 命令让内核配置生效,不出意外,应该会提示

root@debian ~ # sysctl -p
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

此时可以使用 lsmod | grep bbr 命令检查 BBR 是否已正确开启

root@debian ~ # lsmod | grep bbr
tcp_bbr                16384  61

如果出现 tcp_bbr 字样则说明没有问题。

Debian快速安装MariaDB Server教程

简介

MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。

MariaDB、MySQL这样的数据库编译非常耗时而且编译很容易出错,所以使用编译好的版本无疑是最好的,设置页非常的方便。

安装

MariaDB的软件源由官方提供,程序的质量和安全绝对是毋庸置疑的。

本教程适用于:Debian 8(Jessie)支持安装10.0~最新,Debian 9(stretch)支持安装10.1~最新。

教程以10.2版本为例,其他版本只需将下面的10.2修改为10.1 10.0即可。

apt-get install software-properties-common
apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8add-apt-repository 'deb [arch=amd64] http://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/10.2/debian stretch main'
apt update
apt install mariadb-server

安装过程中,这里会提示设置数据库Root密码,需要连续输入两次。

设置

输入下面的命令,关闭一些不安全的设置:

mysql_secure_installation 

首先输入密码,然后一路y即可。

Enter current password for root (enter for none):
解释:输入当前 root 用户密码,默认为空,直接回车。
Set root password? [Y/n]  y
解释:要设置 root 密码吗?输入 y 表示愿意。
Remove anonymous users? [Y/n]  y
解释:要移除掉匿名用户吗?输入 y 表示愿意。
Disallow root login remotely? [Y/n]  y
解释:不想让 root 远程登陆吗?输入 y 表示愿意。
Remove test database and access to it? [Y/n]  y
解释:要去掉 test 数据库吗?输入 y 表示愿意。
Reload privilege tables now? [Y/n]  y
解释:想要重新加载权限吗?输入 y 表示愿意。

管理

systemctl restart mysql #重启
systemctl start mysql #启动
systemctl stop mysql #关闭
systemctl status mysql #检查状态

更新

运行下面的命令系统就会更新所有可以更新的软件包括MariaDB

apt update
apt upgrade -y