saltstack配置文件详解

软件依赖

  • Python版本大于2.6或版本小于3.0: 对Python版本要求
  • msgpack-python: SalStack消息交换库
  • YAML: SaltStack配置解析定义语法
  • Jinja2: SaltStack states配置模板
  • MarkupSafe: Python unicode转换库
  • apache-libcloud: SaltStack对云架构编排库
  • Requests: HTTP Python库
  • ZeroMQ: SaltStack消息系统
  • pyzmq: ZeroMQ Python库
  • PyCrypto: Python密码库
  • M2Crypto: Openssl Python包装库

Master配置文件

主要配置

未分类

安全配置

未分类

salt-ssh配置

未分类

state系统配置

未分类

文件服务配置

未分类

pillar系统配置

未分类

syndic配置

未分类

日志配置

未分类

Minion配置文件

未分类

模块管理配置

未分类

长连接配置

未分类

通过rsync+inotify实现服务器之间数据的实时备份

前言

背景:有A、B两台服务器,A为生产服务器,B为备份服务器
需求:A服务器上的目录/home/chancel一有修改,实时同步到备份服务器B上的/home/chancel_backup/目录

rsync+inotify介绍

rsync介绍

rsync是类unix系统下的数据镜像备份工具——remote sync。一款快速增量备份工具 Remote Sync,远程同步 支持本地复制,或者与其他SSH、rsync主机同步。(来自百度百科)

inotify介绍

Inotify 是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。学习如何将 inotify 集成到您的应用程序中,并发现一组可用来进一步自动化系统治理的命令行工具。(来自百度百科)

示例图

未分类

实现

B服务器部署

  • 安装rsync
yum -y install rsync
  • 编辑/etc/rsyncd.conf文件,下面为参考示例
# /etc/rsyncd: configuration file for rsync daemon mode
# See rsyncd.conf man page for more options.
# configuration example:
 uid = nobody
 gid = nobody
 use chroot = no
 max connections = 10
 strict modes = no
 # pid文件,如无法再次启动可删除
 pid file = /var/run/rsyncd.pid
 lock file = /var/run/rsyncd.lock
 log file = /var/run/rsyncd.log
# exclude = lost+found/
# transfer logging = yes
# timeout = 900
# ignore nonreadable = yes
# dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
# [ftp]
#        path = /home/ftp
#        comment = ftp export area
[betterlife]
# 本地存放的目录
path = /home/chancel
comment = betterlife file
ignore errors
read only = no
write only = no
hosts allow = *
list = false
# 可自定义
uid = root
gid = root
auth users = backup
# 密码文件,内容为“账户:密码”
secrets file = /etc/server.pass
  • 增加/etc/server.pass文件,内容如下
backup:密码
  • 修改/etc/server.pass权限
chmod 600 /etc/server.pass
  • 启动rsync守护进程,并将其写入开机项中
rsync --daemonecho "rsync --daemon" >>/etc/rc.local

备份服务器设置到此完毕

A服务器部署

  • 安装rsync和inotify-tools
yum -y install rsyncyum install inotify-tools
  • 如果inotify-tools无法通过yum安装(已安装跳过此步骤),可从github下载自行编译安装,下面给出参考
wget http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz 
tar -zxf inotify-tools-3.14.tar.gz
cd inotify-tools-3.14
# 配置inotify,并指定安装路径为/usr/local/inotify-3.14
./configure --prefix=/usr/local/inotify-tools-3.14 
make && make install
# 将inotify加入系统环境
vim /etc/profile
# 添加入如下语句
export PATH=$PATH:/usr/local/inotify-tools-3.14/bin
# 保存并退出,使环境生效
source /etc/profile
  • 创建/etc/server.pass文件,内容如下
密码
  • 修改脚本文件权限
chmod 755 /var/www/inotifyrsync.sh
  • 测试是否能够通过rsync同步文件(注意防火墙、模块名称一致、账户密码一致、密码文件读取权限设置)
rsync -vzrtopg --delete --progress --password-file=/etc/server.pass /home/chancel   [email protected]::betterlife
  • 如测试不通过,通过返回错误信息排错,注意模块名称,账户密码,权限问题,防火墙问题,测试如通过,则创建backup脚本
# 创建backup脚本并命名为backup.shvim /home/backup.sh# 写入脚本#!/bin/bashinotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f%e' -e close_write,delete,create,attrib 监测目录 | while read files        do        rsync -vzrtopg --delete --progress --password-file=/etc/server.pass 同步文件目录   backup@服务器地址::模块名                echo "${files} was rsynced" >>/tmp/rsync.log 2>&1        done
  • 后台运行该脚本,并加入启动项
./backup.sh >>/var/www/backup.log &echo "./backup.sh >>/var/www/backup.log &" >>/etc/rc.local

使用pipenv管理你的项目

一、前言

刚才使用pipenv发现了一个bug, 顺手提了个的PR。无聊之下翻了下贡献者列表,貌似没有一个我国的开发者!我的普及工作任重而道远啊,我写篇文章给大家介绍下这个终极大杀器。

Python开发者应该听过pip、easy_install和virtualenv,如果看过我的书应该还知道 virtualenvwrapper、virtualenv-burrito和autoenv,再加上pyvenv、venv(Python 3标准库)、pyenv…

额,是不是有种发懵的感觉?

那么现在有个好消息,你可以只使用终极方案: pipenv + autoenv(可选)。

「终极方案」,听起来好噱头呀。给出我的理由之前我们先了解一下Python虚拟环境和包管理的历史吧。

二、历史

在Python发展史上出现了很多创建和发布包的工具。 当你想要把你的项目分享出去,放到PYPI或者其他托管服务上的时候,就需要借助这样的工具来构建和分发项目。早在1998年Python标准库内置了模块distutils,但是只提供了有限的支持,之后社区选择通过setuptools这个包实现构建和发布,它自带easy_install,能帮助你找到、下载、安装以及更新需要使用的包。不过依然功能很有限,比如不能删除包。

当你做一个专职的Python开发,独立的虚拟环境也是一个开发中迫切需要的功能,在这里请大家记住一个 Ian Bicking(下称ianb) 的开发者,08年,他开发了virtualenv。

社区一些Python核心开发者和知名项目(如Django)核心开发者也在支持和推动这件事,后来成立了pypa(Python Packaging Authority),pypa早期做的就是pip – 现在最主流的安装包的工具。再提一下,ianb也是pip的早期核心开发者。

不过非常遗憾,由于和社区产生了一些矛盾,ianb很早之前就不再写Python项目(可见这矛盾多大呀 ????),virtualenv也转给了其他开发者,这是Python社区一个极大的损失。

现在pip和virtualenv已经被大家所熟知,甚至可以说是Python官方的包管理和虚拟环境选择。不过其实还是有问题,我举几个例子:

  1. 必须手动安装或删除某些特定版本的包,并记得定期更新requirements.txt文件,以保持项目环境的一致

  2. 有时项目中需要有多个requirements.txt文件,比如开发时应该用dev-requirements.txt,现有的模式不能满足这些复杂的需要

  3. 卸载包的时候只是卸载包自己,不能处理相关依赖,时间久了项目环境就混乱了

三、pipenv 是什么?

Pipenv 是 Python 项目的依赖管理器。其实它不是什么先进的理念和技术,如果你熟悉 Node.js 的 npm/yarn 或 Ruby 的 bundler,那么就非常好理解了,它在思路上与这些工具类似。尽管pip可以安装Python包,但仍推荐使用Pipenv,因为它是一种更高级的工具,可简化依赖关系管理的常见使用情况。

主要特性包含:

  1. 根据 Pipfile 自动寻找项目根目录。

  2. 如果不存在,可以自动生成 Pipfile 和 Pipfile.lock。

  3. 自动在项目目录的 .venv 目录创建虚拟环境。(当然这个目录地址通过设置WORKON_HOME改变)

  4. 自动管理 Pipfile 新安装和删除的包。

  5. 自动更新 pip。

对于新手和老手来说都是更好的选择。

四、pipenv 都包含什么?

pipenv 是 Pipfile 主要倡导者、requests 作者 Kenneth Reitz 写的一个命令行工具,主要包含了Pipfile、pip、click、requests和virtualenv。Pipfile和pipenv本来都是Kenneth Reitz的个人项目,后来贡献给了pypa组织。Pipfile是社区拟定的依赖管理文件,用于替代过于简陋的 requirements.txt 文件。

Pipfile的基本理念是:

  1. Pipfile 文件是 TOML 格式而不是 requirements.txt 这样的纯文本。

  2. 一个项目对应一个 Pipfile,支持开发环境与正式环境区分。默认提供 default 和 development 区分。

  3. 提供版本锁支持,存为 Pipfile.lock。

click是Flask作者 Armin Ronacher 写的命令行库,现在Flask已经集成了它。

接下来,我们看看怎么使用它吧

五、入门

pipenv兼容Python 2/3,我们这里以Mac下Python 3为例:

5.1 安装pipenv

❯ brew install python3  # 如果已经安装了可以忽略
❯ python3 -m pip install --upgrade --force-reinstall pip
❯ pip3 install pipenv --user  # 推荐安装在个人目录下
❯ export PATH="/Users/dongweiming/Library/Python/3.6/bin:$PATH"  # 把用户目录下bin放在最前面,这样可以直接使用pipenv了

5.2 使用pipenv

用一个空目录体验一下:

❯ mkdir test_pipenv
❯ cd test_pipenv
❯ pipenv install  # 创建一个虚拟环境
Creating a virtualenv for this project…
...
Installing setuptools, pip, wheel...done.
Virtualenv location: /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5
Creating a Pipfile for this project…
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (c23e27)!
Installing dependencies from Pipfile.lock (c23e27)…
  ????   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
To activate this project's virtualenv, run the following:
 $ pipenv shell
❯ which python3
/usr/local/Cellar/python3/3.6.1/bin/python3  # 还是mac自带的Python
❯ pipenv shell  # 激活虚拟环境
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
source /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/activate
❯ which python3  # 已经在虚拟环境里了
/Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/python3
❯ exit  # 退出虚拟环境
❯ which python3
/usr/local/Cellar/python3/3.6.1/bin/python3

以上就是原来virtualenv的基本用法了。我们看一下当前目录现在是什么样子的:

❯ ls
Pipfile  Pipfile.lock

这个环境下目前什么都没有。我们安装2个包:

❯ pipenv install elasticsearch-dsl requests
Installing elasticsearch-dsl…
...
Adding elasticsearch-dsl to Pipfile's [packages]…
Installing requests…
...
Adding requests to Pipfile's [packages]…
  PS: You have excellent taste! ✨ ???? ✨
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (8d307d)!

现在Pipfile.lock已经更新了,包含了 elasticsearch-dsl、requests 和相关依赖的包信息。

另外如果你添加–two或–three标志到上面的最后一个命令,它分别使用Python 2或3来初始化你的项目。 否则将使用默认版本的Python。

可以看一下依赖关系:

❯ pipenv graph
elasticsearch-dsl==6.1.0
  - elasticsearch [required: <7.0.0,>=6.0.0, installed: 6.1.1]
    - urllib3 [required: <1.23,>=1.21.1, installed: 1.22]
  - ipaddress [required: Any, installed: 1.0.19]
  - python-dateutil [required: Any, installed: 2.6.1]
    - six [required: >=1.5, installed: 1.10.0]
  - six [required: Any, installed: 1.10.0]
requests==2.18.4
  - certifi [required: >=2017.4.17, installed: 2017.11.5]
  - chardet [required: <3.1.0,>=3.0.2, installed: 3.0.4]
  - idna [required: <2.7,>=2.5, installed: 2.6]
  - urllib3 [required: <1.23,>=1.21.1, installed: 1.22]

可以看到,他俩都依赖了urllib3。虽然现在pipenv不能直接卸载包及其依赖,但是由于它提供了良好的接口,我们还是可以实现:

❯ pipenv uninstall `pipenv graph --json |python3 depends.py requests`
Un-installing certifi…
Uninstalling certifi-2017.11.5:
  Successfully uninstalled certifi-2017.11.5
No package certifi to remove from Pipfile.
Un-installing requests…
Uninstalling requests-2.18.4:
  Successfully uninstalled requests-2.18.4
Removing requests from Pipfile…
Un-installing idna…
Uninstalling idna-2.6:
  Successfully uninstalled idna-2.6
No package idna to remove from Pipfile.
Un-installing chardet…
Uninstalling chardet-3.0.4:
  Successfully uninstalled chardet-3.0.4
No package chardet to remove from Pipfile.
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (c05ac4)!

其中depends.py脚本会解析依赖关系,排除其他包依赖的项目然后删除:

❯ cat depends.py
import sys
import json
package = sys.argv[1]
other_dependencies = set()
removing_dependencies = set([package])
for i in json.load(sys.stdin):
    for p in i['dependencies']:
        key = p['key']
        if i['package']['key'] == package:
            removing_dependencies.add(key)
        else:
            other_dependencies.add(key)
print(' '.join(removing_dependencies - other_dependencies))

再看一下现在环境中的包依赖关系:

❯ pipenv graph
elasticsearch-dsl==6.1.0
  - elasticsearch [required: >=6.0.0,<7.0.0, installed: 6.1.1]
    - urllib3 [required: >=1.21.1,<1.23, installed: 1.22]
  - ipaddress [required: Any, installed: 1.0.19]
  - python-dateutil [required: Any, installed: 2.6.1]
    - six [required: >=1.5, installed: 1.10.0]
  - six [required: Any, installed: 1.10.0]

是不是很干净呢?

5.3 其他功能

除了上述基本功能以外,pipenv还有很多附加的功能,我举几个日常比较常用的例子:

❯ pipenv run which python # 「pipenv run」可以激活虚拟环境,并使用shell命令
/Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/python
❯ pipenv check  # 检查装的包的安全性
Checking PEP 508 requirements…
Passed!
Checking installed package safety…
All good!
❯ pipenv --man  # 传统的看文档的方法
❯ pipenv check --style depends.py  # 代码Flake8检查,在这里我修改了depends.py让它故意有问题
/Users/dongweiming/test_pipenv/depends.py:1:1: F401 'os' imported but unused

另外由于autoenv也是Kenneth Reitz写的,所以pipenv默认也包含了对.env文件的支持。

是不是方便很多呢?

mysql慢查询和php-fpm慢日志

MySQL慢查询

在web开发中,我们经常会写出一些SQL语句,一条糟糕的SQL语句可能让你的整个程序都非常慢,超过10秒一般用户就会选择关闭网页,如何优化SQL语句将那些运行时间 比较长的SQL语句找出呢?MySQL给我们提供了一个很好的功能,那就是慢查询!所谓的慢查询就是通过设置来记录超过一定时间的SQL语句!

PHP-FPM慢日志

php-fpm慢日志slowlog设置可以让开发者很好的查找哪些php进程速度过慢而导致的网站问题,让开发者方便的找到问题的所在。该方法同样适用于排查nginx的500、502问题根源,当nginx收到如上错误码时,可以确定后端php-fpm解析php出了某种问题,比如,执行错误,执行超时。

MySQL慢查询的不足

  • MySQL5.0版本, long_query_time时间粒度不够细,最小值为1秒。对于高并发性能的网页脚本而言,1秒出现的意义不大。即出现1秒的查询比较少。直到mysql5.1.21才提供更细粒度的long_query_time设定.

  • 不能将服务器执行的所有查询记录到慢速日志中。虽然MySQL普通日志记录了所有查询,但是它们是解析查询之前就记录下来了。这意味着普通日志没办法包含诸如执行时间,锁表时间,检查行数等信息。

  • 如果开启了log_queries_not_using_indexes选项,slow query日志会充满过多的垃圾日志记录,这些快且高效的全表扫描查询(表小)会冲掉真正有用的slow queries记录。比如select * from category这样的查询也会被记录下来。

1、如何开启慢查询?  

首先我们先查看MYSQL服务器的慢查询状态是否开启.

mysql>show variables like '%quer%';

    
开启慢查询非常简单, 操作如下:

方法一

vi  /etc/my.cnf       注:my.cnf是mysql的配置文件

      
在mysqld下方加入慢查询的配置语句(一定要在[mysqld]的下方加入)

[mysqld]

    log-slow-queries = /var/lib/mysql/mysql-slow.log

    long_query_time = 1

保存退出   重启mysql

log-slow-queries : 代表MYSQL慢查询的日志存储目录, 此目录文件一定要有写权限。

long_query_time: sql最长执行时间。

方法二

在mysql命令行下执行如下操作:

set global slow_query_log=ON;

未分类

set global long_query_time=1;

未分类

测试

1、查看生成的慢日志记录 此时是空的没有记录

cat mysql-slow.log 

未分类
        
2、执行一条超过设置时间的sql语句 在查看是否被记录

如:select sleep(1); 再去查看慢日子记录

未分类

到此mysql慢查询就OK了。

2、 如何开启php-fpm慢日志?

php-fpm.conf的配置文件中有一个参数request_slowlog_timeout是这样描述的:

未分类 

当request_slowlog_timeout 设为一个具体秒时request_slowlog_timeout =5,表示如果哪个脚本执行时间大于5秒,会记录这个脚本到慢日志文件中

request_slowlog_timeout =0表示关闭慢日志输出。

慢日志文件位置默认在php的安装目录下的log文件夹中,可以通过修改slowlog = log/$pool.log.slow参数来指定。

php-fpm慢日志的例子,慢日志会记录下进程号,脚本名称,具体哪个文件哪行代码的哪个函数执行时间过长。

request_slowlog_timeout 和 slowlog需要同时设置,开启request_slowlog_timeout的同时需要开启 slowlog

慢日志路径需要手动创建 (slowlog)

具体开启步骤如下:

未分类

结束。

linux的php-fpm的pool、慢执行日志、进程管理及open_basedir介绍

php-fpm的pool

Nignx可以配置多个虚拟主机,php-fpm同样也支持配置多个pool,每一个pool可以监听一个端口,也可以监听一个socket。

php-fpm配置说明:

和LAMP不同的是,在LNMP架构中,php-fpm作为独立的一个服务存在,既然是独立服务,那么它必然有自己的配置文件。php-fpm的配置文件为/usr/local/php-fpm/etc/php-fpm.conf,它同样也支持include语句,类似于nginx.conf里面的include。

1. 编辑配置文件

[root@gary-tao ~]# cd /usr/local/php-fpm/etc/
[root@gary-tao etc]# ls
pear.conf  php-fpm.conf  php-fpm.conf.default  php.ini
[root@gary-tao etc]# cat php-fpm.conf
[global]
pid = /usr/local/php-fpm/var/run/php-fpm.pid
error_log = /usr/local/php-fpm/var/log/php-fpm.log
[www]
listen = /tmp/php-fcgi.sock
listen.mode = 666
user = php-fpm
group = php-fpm
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
rlimit_files = 1024
[root@gary-tao etc]# vim php-fpm.conf  

//增加一个以下配置:

include = etc/php-fpm.d/*.conf

//include的这一行比较特殊,请注意等号后面的路径,必须写上etc目录,然后需要创建配置文件目录和子配置文件。

编辑好后把php-fpm.conf配置文件里的www以下pool删除,只留下[global]部分

未分类

2. 创建php-fpm.d目录及目录下文件

[root@gary-tao etc]# mkdir php-fpm.d
[root@gary-tao etc]# cd php-fpm.d
[root@gary-tao php-fpm.d]# vim www.conf
[root@gary-tao php-fpm.d]# vim aming.conf
[root@gary-tao php-fpm.d]# ls
aming.conf  www.conf

//两个文件分别增加内容如下:

[www]
listen = /tmp/php-fcgi.sock
listen.mode = 666
user = php-fpm
group = php-fpm
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
rlimit_files = 1024

[aming.com]
listen = /tmp/aming.sock
listen.mode = 666
user = php-fpm
group = php-fpm
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
rlimit_files = 1024

3. 如何把两个站点隔开

  • 原有站点test.com.conf配置如下:

未分类

  • 增加一个aaa.com.conf站点信息,操作如下:
[root@gary-tao php-fpm.d]# cd /usr/local/nginx/conf/vhost/
[root@gary-tao vhost]# ls
aaa.com.conf  ld.conf  proxy.conf  ssl.conf  test.com.conf
[root@gary-tao vhost]# vim aaa.com.conf 

增加如下配置内容:

location ~ .php$
    {
        include fastcgi_params;
        fastcgi_pass unix:/tmp/aming.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /data/wwwroot/default$fastcgi_script_name;
    }

未分类

4. 测试语法

[root@gary-tao etc]# /usr/local/php-fpm/sbin/php-fpm -t
[09-Jan-2018 16:49:00] NOTICE: configuration file /usr/local/php-fpm/etc/php-fpm.conf test is successful

5. 重启php-fpm

[root@gary-tao etc]# /etc/init.d/php-fpm restart  //或/etc/init.d/php-fpm reload
Gracefully shutting down php-fpm . done
Starting php-fpm  done

6. 查看php-fpm启动状态

输入ps aux |grep php-fpm查看,显示的就有两个pool

未分类

php-fpm慢执行日志

通过php-fpm的慢执行日志,我们可以非常清晰地了解到PHP的脚本哪里执行时间长,它可以定位到具体的行。

1. 编辑配置文件

[root@gary-tao vhost]# vim /usr/local/php-fpm/etc/php-fpm.d/www.conf //针对www文件做一个测试

增加如下内容:

request_slowlog_timeout = 1  //超过一秒中就会记录日志
slowlog = /usr/local/php-fpm/var/log/www-slow.log  //日志存放的路径

1. 测试语法

[root@gary-tao vhost]# /usr/local/php-fpm/sbin/php-fpm -t
[09-Jan-2018 19:15:14] NOTICE: configuration file /usr/local/php-fpm/etc/php-fpm.conf test is successful

2. 重启php-fpm

[root@gary-tao vhost]# /etc/init.d/php-fpm restart
Gracefully shutting down php-fpm . done
Starting php-fpm  done

3. 配置nginx的虚拟主机test.com.conf,把unix:/tmp/php-fcgi.sock改为unix:/tmp/www.sock

4. 编写测试脚本

[root@gary-tao php-fpm.d]# vim /data/wwwroot/test.com/sleep.php

增加如下配置内容:

<?php
echo "test slow log";
sleep(2);
echo "done";
?>

5. 使用curl做测试

[root@gary-tao php-fpm.d]# curl -x127.0.0.1:80 test.com/sleep.php -I  //访问的时候出现500,说明有语法错误。 
HTTP/1.1 500 Internal Server Error
Server: nginx/1.12.1
Date: Tue, 09 Jan 2018 11:23:17 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.6.30

报错处理:

  • 进入vim /usr/local/php-fpm/etc/php.ini

  • 找到display_error,改为On,然后访问有错误主就会有输出信息

未分类

  • 记得reload重新加载配置文件

  • 测试出报错信息,因符号问题导致。

[root@gary-tao php-fpm.d]# /etc/init.d/php-fpm reload
Reload service php-fpm  done
[root@gary-tao php-fpm.d]# curl -x127.0.0.1:80 test.com/sleep.php
<br />
<b>Parse error</b>:  syntax error, unexpected 'slow' (T_STRING), expecting ',' or ';' in <b>/data/wwwroot/test.com/sleep.php</b> on line <b>2</b><br />
  • 重新去更改配置文件vim /data/wwwroot/test.com/sleep.php的符号问题

  • 查看慢执行日志

[root@gary-tao php-fpm.d]# cat /usr/local/php-fpm/var/log/www-slow.log  //慢执行日志里指出了哪个脚本运行慢,也指出了是哪一行执行慢 

[09-Jan-2018 19:39:54]  [pool www] pid 81864
script_filename = /data/wwwroot/test.com/sleep.php
[0x00007f5987a07290] sleep() /data/wwwroot/test.com/sleep.php:3
[root@gary-tao php-fpm.d]# cat /data/wwwroot/test.com/sleep.php 
<?php 
echo "test slow log";
sleep(2);       //这个脚本超过1秒
echo "done";
?>

定义open_basedir

open_basedir的目的就是安全,只要在对应的Nginx虚拟主机配置文件中调用对应的pool,就可以使用open_basedir来物理隔离多个站点,从而达到安全目的。

1. 配置虚拟主机文件

[root@gary-tao php-fpm.d]# vim /usr/local/php-fpm/etc/php-fpm.d/www.conf 

增加如下配置内容:

php_admin_value[open_basedir]=/data/wwwroot/test.com:/tmp/

2. 使用curl测试

[root@gary-tao php-fpm.d]# curl -x127.0.0.1:80 test.com/3.php -I  //正常访问
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Tue, 09 Jan 2018 12:32:50 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.6.30

3. 配置错误日志路径测试

  • 增加错误路径
[root@gary-tao php-fpm.d]# vim /usr/local/php-fpm/etc/php.ini //增加错误日志路径及定义日志级别,如下图

未分类

未分类

  • 进入到www.conf配置文件里面把test.com改为aming.com,再次测试,
    查看错误日志

  • 查看错误日志

[root@gary-tao php-fpm.d]# grep error_log /usr/local/php-fpm/etc/php.ini  //查看日志路径
; server-specific log, STDERR, or a location specified by the error_log
; Set maximum length of log_errors. In error_log information about the source is
;error_log = php_errors.log
;error_log = syslog
error_log = /usr/local/php-fpm/var/log/php_errors.log
; OPcache error_log file name. Empty string assumes "stderr".
;opcache.error_log=
[root@gary-tao php-fpm.d]# ls /usr/local/php-fpm/var/log/
php-errors.log  php-fpm.log  www-slow.log

[root@gary-tao php-fpm.d]# ls /usr/local/php-fpm/var/log/ //查看日志路径是否存在
php-fpm.log  www-slow.log
[root@gary-tao php-fpm.d]# touch /usr/local/php-fpm/var/log/php_errors.log  //手动建立一个日志文件
[root@gary-tao php-fpm.d]# chmod 777 /usr/local/php-fpm/var/log/php_errors.log  //更改成所有权限,防止不能写入日志
[root@gary-tao php-fpm.d]# !curl
curl -x127.0.0.1:80 test.com/3.php -I
HTTP/1.1 404 Not Found
Server: nginx/1.12.1
Date: Tue, 09 Jan 2018 13:05:05 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.6.30

[root@gary-tao php-fpm.d]# cat /usr/local/php-fpm/var/log/php_errors.log  //因为open_basedir限制的地址写错了,所有就会报错,访问出现404
[09-Jan-2018 13:05:05 UTC] PHP Warning:  Unknown: open_basedir restriction in effect. File(/data/wwwroot/test.com/3.php) is not within the allowed path(s): (/data/wwwroot/aming.com:/tmp/) in Unknown on line 0
[09-Jan-2018 13:05:05 UTC] PHP Warning:  Unknown: failed to open stream: Operation not permitted in Unknown on lin

php-fpm进程管理

未分类

  1. pm = dynamic //定义php-fpm的子进程启动模式,dynamic为动态进程管理,一开始只启动少量的子进程,根据实际需求,动态地增加或者减少子进程,最多不会超过pm.max_children定义的数值。另外一种模式是static,这种模式下子进程数量由pm.max_children决定,一次性启动这么多,不会减少也不会增加。

  2. pm.max_children = 50 //最大子进程数,ps aux可以查看

  3. pm.start_servers = 20 //针对dynamic模式,它定义php-fpm服务在启动服务时产生的子进程服务时产生的子进程数量。

  4. pm.min_spare_servers = 5 //针对dynamic模式,定义在空闲时段,子进程数的最少数量,如果达到这个数值时,php-fpm服务会自动派生新的子进程。

  5. pm.max_spare_servers = 35 //针对dynamic模式,定义在空闲时段,子进程数的最大值,如果高于这个数值就开始清理空闲的子进程。

  6. pm.max_requests = 500 //针对dynamic模式,定义一个子进程最多处理的请求数,也就是说在一个php-fpm的子进程最多可以处理这么多请求,当达到这个数值时,它会自动退出。

rsync服务器的配置

安装

  • Linux

参考 rsync的基本概念、命令、和使用(http://jiangyuesong.me/2018/01/09/rsync) 安装rsync

apt-get install xinetd 安装xinetd

  • Windows参考

配置

一下配置文件,若不存在则新增即可.

  • 修改/etc/default/rsync
RSYNC_ENABLE=inted   #false改true或者inted
  • 修改/etc/xinetd.d/rsync

将disable=yes改成disable=no

service rsync
{
    disable = no 
    socket_type = stream
    wait = no
    user = root
    server = /usr/bin/rsync
    server_args = --daemon
    log_on_failure += USERID
}
  • 修改/etc/rsyncd.conf

修改/etc/rsyncd.conf,主要包括账号密码文件路径、同步路径等内容,分为全局配置和模块配置,每个需要同步的文件夹可以配置为一个模块。可以使用rsync –daemon的方式运行守护进程,这里使用xinetd

#全局配置
#  = 后面的值可根据自己的实际情况更改
#  pid file 守护进程pid文件
#  port 守护进程监听端口,可更改,由xinetd允许rsyncd时忽略此参数
#  address 守护进程监听ip,由xinetd允许rsyncd时忽略此参数
#  我们使用xinetd 运行守护进程
pid file = /usr/local/var/run/rsyncd.pid
# port = 873
# address = 192.168.1.2
#rsyncd 守护进程运行系统用户全局配置,也可在具体的块中独立配置,
uid = root
gid = root
#允许 chroot,提升安全性,客户端连接模块,首先chroot到模块path参数指定的目录下
#chroot为yes时必须使用root权限,且不能备份path路径外的链接文件
use chroot = no
#只读
read only = no
#只写
write only = no
#允许访问rsyncd服务的ip,ip端或者单独ip之间使用空格隔开
hosts allow = *
#不允许访问rsyncd服务的ip,*是全部(不涵盖在hosts allow中声明的ip,注意和hosts allow的先后顺序)
#hosts deny = *
#客户端最大连接数
max connections = 5
#欢迎文件路径,可选的,在客户端链接成功后会显示欢迎信息
#motd file = /etc/rsyncd/rsyncd.motd
#日志相关
#  log file 指定rsync发送消息日志文件,而不是发送给syslog,如果不填这个参数默认发送给syslog
#  transfer logging 是否记录传输文件日志
#  log format 日志文件格式,格式参数请google
#  syslog facility rsync发送消息给syslog时的消息级别,
#  timeout连接超时时间
log file = /usr/local/logs/rsyncd.log
transfer logging = yes
log format = %t %a %m %f %b
syslog facility = local3
timeout = 300

#模块 模块名称必须使用[]环绕,客户端要访问data1,则地址应该是data1user@address::data1
[data1]
#模块根目录,必须指定
path=/home/test
#是否允许列出模块里的内容
list=yes
#忽略错误
#ignore errors
#模块验证用户名称,可使用空格或者逗号隔开多个用户名
auth users = data1user
#模块验证密码文件 可放在全局配置里
secrets file=/etc/rsyncd.secrets
#注释
comment = some description about this moudle
#排除目录,多个之间使用空格隔开
#exclude = test1/ test2/
  • 配置密码文件

新建密码文件/etc/rsyncd.secrets(与上述文件的secret file一致)

data1user:password
  • 配置文件授权

密码文件必须赋予600的权限

chmod 600 /etc/rsyncd.*
  • 启动服务
#重启守护进程
service xinted restart
#检查是否启动成功
netstat -an | grep 873
#显示 tcp6       0      0 :::873                  :::*                    LISTEN

使用

根据上述配置,可以本地建立密码文件pwd.secrets(随便命名),内容为

password
  • 获取远程文件
rsync -avz --password-file=pwd.secrets [email protected]::data1 /home/data

上述代码将远程的/home/test/中的内容拷贝至/home/data中,注意:服务器配置文件中path无论是否以/结尾,都不会复制目录本身,只会复制文件夹内的内容

模块名可以加子目录,data1/subdir 可以复制整个subdir目录,subdir必须是位于配置文件的path指定的目录中

  • 传输本地文件到远程机器
rsync -avz --password-file=pwd.secrets /home/data/ [email protected]::data1

本地的data内的文件被传输到/home/test/中。若本地路径写为/home/data没有/,则整个data目录上传至/home/test/

  • 列出远程文件
rsync -v --password-file=pwd.secrets rsync://[email protected]/data1/

or

rsync -v --password-file=pwd.secrets [email protected]::data1/

rsync设置只同步某些目录或文件

rsync的配置方法请参考(请先阅读这篇文章)https://blog.whsir.com/post-1501.html

现在想只同步某些目录或文件,这里就用到了–include和–exclude参数,–include是只同步某些目录或文件,但是必须和–exclude配合使用。

只需要在原来客户端的参数上做一些更改,原来客户端命令如下:

rsync --size-only -azvP --password-file=/opt/mima [email protected]::www /home/wwwroot

例如:现在我只想同步服务端/data/www/js/目录下所有文件,其他的目录或者文件我都不想同步,更改后的参数如下

rsync --size-only -azvP --include=js/ --exclude=/* --password-file=/opt/mima [email protected]::www /home/wwwroot

好了,上面的参数已经实现了我的目的,只同步js目录,那么又有一个新问题出现了,如果我想要只同步多个目录怎么办?我要写多个–include吗?当然如果不嫌麻烦也是可以的,不过我这里再介绍一个参数–include-frome,我可以把要同步的目录全部写到一个文件中,然后通过–include-frome来读取这个文件,参数如下:

rsync --size-only -azvP --include-from="/etc/include" --exclude=/* --password-file=/opt/mima [email protected]::www /home/wwwroot
cat /etc/include
js/
css/

这样就实现了只同步js和css两个目录

rsync概念、命令与配置

Rsync是Linux下的一款增量备份工具,使用特殊的算法和压缩传输,可以进行大量文件的高效率备份。本文主要介绍了rsync的基本概念,常用命令,和客户端的配置

安装

在使用rsync备份时,要求备份的源和目标机器都需要安装rsync。目前大部分Linux发行版中都安装了rsync,若未安装,Ubuntu可以运行

sudo apt-get install rsync

其它系统请自行查询安装命令。

Windows系统的安装请参考 rsync专题(http://jiangyuesong.me/tags/rsync) 中rsync在Windows中的使用

命令

rsync有六种工作模式:

  1. rsync [OPTION]… SRC DEST
  2. rsync [OPTION]… SRC [USER@]HOST:DEST
  3. rsync [OPTION]… [USER@]HOST:SRC DEST
  4. rsync [OPTION]… [USER@]HOST::SRC DEST
  5. rsync [OPTION]… SRC [USER@]HOST::DEST
  6. rsync [OPTION]… rsync://[USER@]HOST[:PORT]/SRC [DEST]

option为可选的参数(见后文) 从SRC 拷贝到DESC,USER用户名,HOST目标机器Ip地址,PORT目标机器端口

1中SRC/DEST均为本地目录
2-3中SRC/DEST为本地和远程目录,USER为ssh用户名
4-6中使用rsync服务器,::后紧跟的是服务器模块名,非目录名,但是模块名可以加子目录,USER为服务器配置文件中的用户名

1、拷贝本地文件

rsync -avz /data /backup

注意:/data会把整个data目录copy到backup目录下,形成/backup/data的结构,如果只需copy data内的内容而不复制data目录本身,应当使用rsync -avz /data/ /backup

2、通过ssh实现本地拷贝到远程机器

rsync -avz /data [email protected]:/backup

将通过root用户登陆远程机器,登陆方式是使用密钥的方式,所以在本地机器上,运行ssh-keygen -t rsa,一路回车(不要使用密码)会在你的用户目录 ~/.ssh/生成id_rsa和id_rsa.pub文件(若已经存在不需要再次生成),复制.pub文件到远程机器~/.ssh/ 目录,运行

cat id_rsa.pub >> authorized_keys
chmod 700 authorized_keys

3、通过ssh将远程内容copy到本地机器

rsync -avz [email protected]:/backup/data/ /data

同样是ssh登陆,需要完成模式2的配置才可以

4、通过rsync服务将远程内容拷贝到本地

rsync -avz [email protected]::modulename /data

rsync和ssh方式的区别在于使用了双冒号,使用这种方式需要先在远程机器上配置rsync服务器,请参考rsync专题中rsync服务器的配置

可以通过–password-file=xx 指定密码文件,客户端密码文件仅需写入对应的密码即可

这种模式中,用户名datauser和模块modulename是在服务端配置文件中配置的

5、通过rsync服务将本地文件拷贝到远程机器

rsync -avz /data/ [email protected]::modulename

同上,需要配置rsync服务器

6、列出远程主机的文件列表

rsync -v rsync://[email protected]/modulename

or

rsync -v [email protected]::modulename

可选参数

-v, –verbose 详细模式输出
-q, –quiet 精简输出模式
-c, –checksum 打开校验开关,强制对文件传输进行校验
-a, –archive 归档模式,表示以递归方式传输文件,并保持所有文件属性,等于-rlptgoD
-r, –recursive 对子目录以递归模式处理
-R, –relative 使用相对路径信息
-b, –backup 创建备份,也就是对于目的已经存在有同样的文件名时,将老的文件重新命名为~filename。可以使用–suffix选项来指定不同的备份文件前缀。
–backup-dir 将备份文件(如~filename)存放在在目录下。
-suffix=SUFFIX 定义备份文件前缀
-u, –update 仅仅进行更新,也就是跳过所有已经存在于DST,并且文件时间晚于要备份的文件。(不覆盖更新的文件)
-l, –links 保留软链结
-L, –copy-links 想对待常规文件一样处理软链结
–copy-unsafe-links 仅仅拷贝指向SRC路径目录树以外的链结
–safe-links 忽略指向SRC路径目录树以外的链结
-H, –hard-links 保留硬链结
-p, –perms 保持文件权限
-o, –owner 保持文件属主信息
-g, –group 保持文件属组信息
-D, –devices 保持设备文件信息
-t, –times 保持文件时间信息
-S, –sparse 对稀疏文件进行特殊处理以节省DST的空间
-n, –dry-run现实哪些文件将被传输
-W, –whole-file 拷贝文件,不进行增量检测
-x, –one-file-system 不要跨越文件系统边界
-B, –block-size=SIZE 检验算法使用的块尺寸,默认是700字节
-e, –rsh=COMMAND 指定使用rsh、ssh方式进行数据同步
–rsync-path=PATH 指定远程服务器上的rsync命令所在路径信息
-C, –cvs-exclude 使用和CVS一样的方法自动忽略文件,用来排除那些不希望传输的文件
–existing 仅仅更新那些已经存在于DST的文件,而不备份那些新创建的文件
–delete 删除那些DST中SRC没有的文件
–delete-excluded 同样删除接收端那些被该选项指定排除的文件
–delete-after 传输结束以后再删除
–ignore-errors 即使出现IO错误也进行删除
–max-delete=NUM 最多删除NUM个文件
–partial 保留那些因故没有完全传输的文件,以是加快随后的再次传输
–force 强制删除目录,即使不为空
–numeric-ids 不将数字的用户和组ID匹配为用户名和组名
–timeout=TIME IP超时时间,单位为秒
-I, –ignore-times 不跳过那些有同样的时间和长度的文件
–size-only 当决定是否要备份文件时,仅仅察看文件大小而不考虑文件时间
–modify-window=NUM 决定文件是否时间相同时使用的时间戳窗口,默认为0
-T –temp-dir=DIR 在DIR中创建临时文件
–compare-dest=DIR 同样比较DIR中的文件来决定是否需要备份
-P 等同于 –partial
–progress 显示备份过程
-z, –compress 对备份的文件在传输时进行压缩处理
–exclude=PATTERN 指定排除不需要传输的文件模式
–include=PATTERN 指定不排除而需要传输的文件模式
–exclude-from=FILE 排除FILE中指定模式的文件
–include-from=FILE 不排除FILE指定模式匹配的文件
–version 打印版本信息
–address 绑定到特定的地址
–config=FILE 指定其他的配置文件,不使用默认的rsyncd.conf文件
–port=PORT 指定其他的rsync服务端口
–blocking-io 对远程shell使用阻塞IO
-stats 给出某些文件的传输状态
–progress 在传输时现实传输过程
–log-format=formAT 指定日志文件格式
–password-file=FILE 从FILE中得到密码
–bwlimit=KBPS 限制I/O带宽,KBytes per second
-h, –help 显示帮助信息

一般都使用azv选项

总结

阅读完本文,应当可以使用rsync在本地机器进行备份,和通过ssh方式与远程机器进行文件传输。其简要步骤如下:

  1. 本地和远程机器 安装rsync
  2. 本地生成ssh密钥,复制公钥到远程机器并配置
  3. 参考命令中的1~3进行备份

提示

  • 注意命令中的空格
  • 通过–exclude-from=/data/ignore.list 指定忽略列表文件,文件中,通过文件夹名,或者通配符指定忽略不传输的文件,一个典型的list可以为:
*.jar
*.bak
*.sw?
.#*
*#
*~
node_modules
Data
.git
target
temp
tmp
.idea
log

OpenResty之单元测试

当项目功能开发完毕时,我们常常为了保证自己代码的功能正常,符合自己的预期结果而编写单元测试,并且以后当我们修改代码后,只需要跑一边单元测试便可以看我们有没有把代码给改残了。所以单元测试的重要性不言而喻。

使用不同的编程语言编写代码时,通常都有不同的单元测试框架。而当我们编写OpenResty项目时,比较好用的的测试框架还是比较少等,因为之前一直在看《OpenResty 最佳实践》一书,里面作者自己开发了一套测试框架lua-resty-test,使用后感觉还不错,便在项目中使用了一下。下面大概总结下使用心得。

Nginx 配置添加测试服务器

在配置文件中新增一个专门用于跑单元测试的服务器,并且将它的错误日志专门用另一个test_error.log文件存储。然后新建一个test文件夹用于放置单元测试代码,设置执行单元测试的主入口文件为main.lua,
这里别忘了设置下lua_package_path的值,把lua-resty-test的路径加上去。因为我吧项目的一些公共库都放置在了lualib下,所以我的lua_package_path设置如下

# conf/nginx.conf
lua_package_path '${prefix}project/?.lua;${prefix}lualib/?.lua;;';
# 新增的单元测试服务器配置
server {
    listen  8090;
    access_log off;
    # 此处修改单元测试服务器的错误日志为test_error.log,有别于开发项目的 error.log文件
    error_log logs/test_error.log error;
    # 主入口文件为 mian.lua
    location /test {
        content_by_lua_file 'test/main.lua';
    }
}

在主入口文件中加载各个单元测试代码

因为我这里是按照一个文件编写一个单元测试的,所以在主入口文件中进行加载每一个要被测试的代码,代码如下

# test/main.lua
local iresty_test    = require "resty.iresty_test"
-- 测试项目中的utils.lua文件
require("test.test_utils.test_utils")(iresty_test)
-- 测试项目中的transac.lua文件
-- require("test.test_controller.test_transac")(iresty_test)

utils 文件的单元测试代码大致如下

 # test/test_utils/test_utils.lua

 local utils = require("utils.utils")
 local cjson = require("cjson")

 local function test_utils( iresty_test )

     -- 设置此模块的单元测试名称
     local test_utils = iresty_test.new({unit_name="test_utils"})

     -- init方法可以在跑单元测试的时候执行一些初始化的操作
     --function test_utils:init(  )
     --    self:log("init complete")
     --end

     -- 测试utils模块的isDigit函数
     function test_utils:test_isDigit()
         -- case one
         local result = utils.isDigit()
         local expect = false
         assert(expect == result, "isDigit function should return false")

         -- case two 
         local result = utils.isDigit("")
         local expect = false
         assert(expect == result, "isDigit function should return false")

         -- case three 
         local result = utils.isDigit("12345")
         local expect = true
         assert(expect == result, "isDigit function should return true")

         -- case four 
         local result = utils.isDigit("123abc")
         local expect = false
         assert(expect == result, "isDigit function should return true")
     end

     -- 测试utils模块的trim函数
     function test_utils:test_trim()
         -- log方法用于记录单元测试的时候,一些日志数据
         -- self:log("ok")
     end

     -- 运行此文件的单元测试
     test_utils:run()
end

return test_utils

运行单元测试代码

建议在命令行中使用curl命令,这样的话可以比较明显的看出不同颜色的输出日志

curl http://10.100.157.198:8090/test

输出效果如下

0.000 [test_utils] unit test start
0.000   _[test_isDigit] PASS
0.000   _[test_trim] PASS
0.000 [test_utils] unit test complete

nginx如何配置兼容ThinkPHP各种url模式

我们知道ThinkPHP是有多种访问模式的,比如较常用的普通模式以及rewrite模式,也就是网址 /index.php?m=Zhonglian&c=Index&a=register 以及 网址 /DailiUser/alipay_notify_url,这两种模式用得比较多了。

为什么要做多种兼容呢,因为最近做了支付宝的回调,而支付宝是不认第一种模式的,只能使用 rewrite 模式,而我本地使用的是 nginx 服务器,所以需要在 nginx里面去做一下兼容配置了,配置的方法也很简单,在linux或者windows+nginx的环境下配置 nginx.conf 文件。

location / {    
     root /var/www;    
     index index.html index.htm index.php;    
     if (!-e $request_filename) {    
         rewrite ^/index.php(.*)$ /index.php?s=$1 last;    
         rewrite ^(.*)$ /index.php?s=$1 last;    
         break;    
     }    
 }    

好了,配置成功之后,Thinkphp的三种模式都可以兼容使用了,在本地的话就可以使用普通的模式,如果使用支付宝或者微信支付等回调的话就可以使用 rewrite 类的模式。