Archive for 网站开发

笔记:新MacOS Sierra 系统使用brew安装php、mysql(mariadb)、nginx等开发环境,也适合小白用户

安装brew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装完毕后,把源改成中科大的

cd "$(brew --repo)"
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git

cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git

cd "$(brew --repo)"/Library/Taps/caskroom/homebrew-cask
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git

# 2选1
# for bash user
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile
source ~/.bash_profile
# for zsh user
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.zshrc
source ~/.zshrc

安装 MySQL(MariaDB)

brew install mariadb

# 设置下,否则mysql_的命令不会出现
# ls /usr/local/Cellar/mariadb/ 看下是不是10.2.8,如果是别的版本记得更换下下面的路径
echo 'export PATH="$PATH:/usr/local/Cellar/mariadb/10.2.8/bin"' >> ~/.bash_profile

修改完必要的 /usr/local/etc/my.cnf 配置后(其实本地测试没什么好修改的,我就用的默认的)执行 mysql_install_db 命令初始化数据库(如果 /usr/local/var/mysql/ 目录已经有了,可以忽略这个)

启动服务器:

brew services start mariadb

下次开机会自动启动,看所有服务列表:

brew services list

如果要修改MySQL管理员密码,可以使用

mysql_secure_installation

命令(先把服务器启动起来)

安装PHP

先执行下面

brew install homebrew/php/php-install

这个命令,然后

brew search php

你会发现就有好多不同版本的 php 了,比如你要装 php7.1 版本的话,可以

brew search php71

,然后选择你需要的扩展模块安装就可以了,比如:

brew install homebrew/php/php71 homebrew/php/php71-redis homebrew/php/php71-swoole homebrew/php/php71-yaml

你可能没找到 mysql,php-fpm 等这些扩展,这个是 homebrew/php/php71 里就包含了的。

启动 php-fpm

在启动 php-fpm 前建议修改下配置

vim /usr/local/etc/php/7.1/php-fpm.d/www.conf

将 group 改成 staff,也可以将 user 改成自己的账户名。这样做可能会存在一点点安全风险,但是如果你用到php需要写一些文件的话,就会方便很多,否则连自己操作这个文件都没有权限,略麻烦。

启动的话非常简单,此时使用命令

brew services list

会看到 php71 是 stopped 的,执行

brew services start php71

就可以了。

安装nginx

执行执行

brew install nginx

就可以快速的安装好了,brew 安装好的 nginx 默认值监听 8080 端口的,这显然太麻烦了,果断改到80端口,但是由于 Mac 系统的限制,低于1000的端口不能由用户启动,所以需要再配置下。

执行

sudo vim /Library/LaunchDaemons/homebrew.mxcl.nginx.plist

输出密码,后把下面的内容写进去

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>homebrew.mxcl.nginx</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/opt/nginx/bin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/usr/local</string>
  </dict>
</plist>

保存,然后执行下面代码加载 nginx

sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist

这样重启nginx

sudo launchctl stop homebrew.mxcl.nginx
sudo launchctl start homebrew.mxcl.nginx

看看有没有启动成功:

ps -ef | grep nginx

,如果有进程就表示启动成功了。

小建议,brew 安装的 nginx 默认是8个线程,个人觉得测试有点浪费了,所以可以自己设置下,

vim /usr/local/etc/nginx/nginx.conf

将 worker_processes 改成你要的值(我的是4)

安装phpMyAdmin

本人比较懒,直接使用

brew install homebrew/php/phpmyadmin

安装了个 phpMyAdmin,然后将下列配置文件写入

/usr/local/etc/nginx/servers/phpmyadmin.conf

重启 nginx 即可

upstream php-handler {
    server 127.0.0.1:9000;
}
server {
    listen 80;
    server_name 127.0.0.1;
    root /usr/local/share/phpmyadmin/;
    charset utf-8;

    gzip off;
    proxy_buffering off;
    index index.html index.php;

    location ~ .php$ {
        fastcgi_pass php-handler;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        #fastcgi_buffering off;
        fastcgi_buffers 10 256k;
        fastcgi_buffer_size 512k;
        fastcgi_keep_conn on;
        #fastcgi_request_buffering off;
        client_max_body_size 128m;
        fastcgi_temp_file_write_size 512k;
        include fastcgi_params;
    }
}

另外,如果要用 phpMyAdmin 执行超长的任务需要注意,它默认是最大300秒的执行时限的,请修改

/usr/local/etc/phpmyadmin.config.inc.php

文件,在最后加入如下内容即可:

$cfg['ExecTimeLimit'] = 0;  # 或你认为的最大值

另外,关于php.ini的一些设置,

vim /usr/local/etc/php/7.1/php.ini

,可以根据自己的需要调节,比如:

max_execution_time 30  # 最大执行时间,可以根据自己的需要修改
memory_limit = 128M    # 内存限制
post_max_size = 8M     # 将会影响上传文件大小
date.timezone          # 默认时区,可以改成 PRC(中华人民共和国缩写),即 date.timezone = PRC

改好后,使用

brew services restart php71

重启 php-fpm 即可。

至此,php,nginx,mysql都已经安装好了。如果你是用PhpStorm的开发者,推荐我调的代码风格和配色,预览和下载地址:https://www.queyang.com/blog/archives/638

see

奉上自己的 PhpStorm 的黑色风格的配色和代码格式

二话不说先看配色,自己调的,整体风格是 Sublime 的御用配色样子,附上下载地址(内有说明) PhoStorm代码风格和配色

一步到位,开启https + http2

今天头条上看到自己网站可以免费获取证书,于是尝试了下,最终一步到位全部成功。

系统为:centos7 + nginx

第一步,获取证书 ps: 免费的哦,而且不像自签名的那种证书浏览器会不认
很简单,先安装certbot命令:

yum install certbot

然后使用此命令生成证书:

certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

这边说明下, /var/www/example 是你现在网站可以访问的根目录,在生成时它会创建一个 .well-known 文件夹去验证你是否有这个网站的权限,然后 -d 后面是证书匹配的域名,可以多个,example.com 请换成你的域名。
然后是一个引导窗口你就填个email然后确认基本上等待验证就能成功了(千万别在screen里运行,否则像我一开始一样全部是蓝屏看不到字)

生成证书后,证书在 /etc/letsencrypt/live/example.com/ 目录

server {
    listen      443 ssl http2;
    ssl         on;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_dhparam         /etc/nginx/ssl/dhparam.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';
}

以上是nginx的https相关的基本配置(其它参数不写了,自行加上,以上参数说明请见参阅内容第3个,ssl_protocols 主要用的安全协议,ssl_ciphers 是禁用的不安全的协议,这里不赘述了)

这里需要自己生成个 dhparam.pem 文件,执行如下命令即可:

mkdir /etc/nginx/ssl/
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

这是加强 HTTPS 安全性的,如果嫌麻烦,把 ssl_dhparam 参数删掉就可以了。

参阅:

  1. https://certbot.eff.org/
  2. https://ksmx.me/letsencrypt-ssl-https/
  3. https://aotu.io/notes/2016/08/16/nginx-https/

https 只要有证书启动起来是没什么问题的,如果对这些流程不是很熟悉可能需要找不少文章才能搞明白。
http2 则是需要 nginx 1.9.5+ 和 openssl 1.0.2+ 才支持,我一开始用的是系统自带的 nginx,版本才1.6,后来升级了 openssl 到最新版在自己装了个nginx repo 安装是新版本了,可是死活还是启动不了 http2,然后我这样猥琐的解决了:

nginx -V

先得到所有的 configure arguments: 参数。然后下载 openssl 和 nginx 对应版本的源码包,然后增加一个参数“–with-openssl=/root/openssl-1.0.2j” 这个路径就是对应 openssl 源码的目录,然后在nginx里重新编译安装,然后就可以了

注意,letsencrypt 的证书的有效期是3个月,届时务必要更新下,否则会导致网站出现安全提醒,更新证书非常简单,只要相同的命令重新运行下就可以,然后重启下 nginx

[笔记] centos 里使用 lvm 挂载多个硬盘

首先安装命令:

yum install -y lvm2

使用

parted -l

查看当前硬盘的信息,出来的结果类似:

警告: 无法以读写方式打开 /dev/sr0 (只读文件系统)。/dev/sr0 已按照只读方式打开。
错误: /dev/sr0: unrecognised disk label
Model: QEMU QEMU DVD-ROM (scsi)
Disk /dev/sr0: 432kB
Sector size (logical/physical): 2048B/2048B
Partition Table: unknown
Disk Flags:

Model: Virtio Block Device (virtblk)
Disk /dev/vda: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  标志
 1      1049kB  21.5GB  21.5GB  primary  xfs          启动


错误: /dev/vdb: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

错误: /dev/vdc: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

错误: /dev/vdd: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdd: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

使用

pvcreate /dev/vdb /dev/vdc /dev/vdd

创建,注意,这里面的”/dev/vdb”, “/dev/vdc”, “/dev/vdd” 就是前面看到信息的内容。

输出如下表示成功:

  Physical volume "/dev/vdb" successfully created
  Physical volume "/dev/vdc" successfully created
  Physical volume "/dev/vdd" successfully created

执行

pvscan

输出:

  PV /dev/vdd         lvm2 [500.00 GiB]
  PV /dev/vdb         lvm2 [500.00 GiB]
  PV /dev/vdc         lvm2 [500.00 GiB]
  Total: 3 [1.46 TiB] / in use: 0 [0   ] / in no VG: 3 [1.46 TiB]

执行

vgcreate sata /dev/vdb /dev/vdc /dev/vdd

输出:

Volume group "sata" successfully created

PS:vgcreate 后的 sata 是一个名称,可以自己起,后面的3个参数是之前对应的盘符路径,按实际情况写。

执行

vgdisplay

看下结果:

  --- Volume group ---
  VG Name               sata
  System ID
  Format                lvm2
  Metadata Areas        3
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                3
  Act PV                3
  VG Size               1.46 TiB
  PE Size               4.00 MiB
  Total PE              383997
  Alloc PE / Size       0 / 0
  Free  PE / Size       383997 / 1.46 TiB
  VG UUID               C0PTEh-erdY-7gh3-fTAb-mwsE-3fMk-J8Tg5e

然后执行

lvcreate -l 383997 -n data sata

创建一个整个大盘,如果不要一次性全部创建,则可以用“L”参数。
其中 data 表示一个名称, sata 是之前创建的 VG Name,会输出:

  Logical volume "data" created.

表示创建成功。

执行

lvscan

输出如下:

  ACTIVE            '/dev/sata/data' [1.46 TiB] inherit

执行

mkfs.xfs /dev/sata/data

创建 XFS 格式的盘到 /dev/sata/data 上,输出如下:

meta-data=/dev/sata/data         isize=256    agcount=4, agsize=98303232 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0
data     =                       bsize=4096   blocks=393212928, imaxpct=5
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal log           bsize=4096   blocks=191998, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

执行

blkid /dev/sata/data

查看新盘的UUID,复制下来下面要用,比如我的是:

/dev/sata/data: UUID="e54fa05f-dc2f-460b-a573-e2817adf9f7b" TYPE="xfs"

编辑文件

vim /etc/fstab

, 加入内容:

UUID=e54fa05f-dc2f-460b-a573-e2817adf9f7b /data                   xfs     noatime,nobarrier 0 0

其中

/data

表示你要挂载的目录,加上 noatime 可以提高盘的性能(如果根目录的没有noatime,也可以加上,加上后记得执行

mount -o remount /

生效),完整内容如下:

UUID=6001f930-2d2b-4e57-b55b-3fde13756c7d /                       xfs     defaults,noatime  0 0
UUID=e54fa05f-dc2f-460b-a573-e2817adf9f7b /data                   xfs     noatime,nobarrier 0 0

注意,UUID应该对应自己的盘符名称。

执行

mkdir /data
mount -a

这样就把盘挂到了 /data 目录了,可

df -lh

看下:

文件系统               容量  已用  可用 已用% 挂载点
/dev/vda1               20G  2.4G   18G   12% /
devtmpfs               3.9G     0  3.9G    0% /dev
tmpfs                  3.9G     0  3.9G    0% /dev/shm
tmpfs                  3.9G  8.4M  3.9G    1% /run
tmpfs                  3.9G     0  3.9G    0% /sys/fs/cgroup
tmpfs                  783M     0  783M    0% /run/user/0
/dev/mapper/sata-data  1.5T   33M  1.5T    1% /data

这样就把3块500G的硬盘挂载到1个1.5T的 /data 目录了

利用 Grafana influxDB fluentd pidstat 对指定进程进行监控

有图有真相,先来看几张成果图:

QQ20160407-1@2x

QQ20160407-0@2x

我先介绍下这些东西都是做什么的

  • Grafana 是一个非常漂亮的可以自定义的监控web服务,如上图
  • InfluxDB 是一个高效的时间数据库,擅长用于记录按时间推进的数据,这里的数据都存在它里面
  • Fluentd 是一个数据(日志)接受和分发的服务,可以指定任意输入源后经过适当的处理后再分发到其它的接收端,这里收集数据都是通过它进入到 InfluxDB 的
  • pidstat 直接安装 sysstat 就有了,可以查看进程相关数据
【安装】

这里不赘述,只简单的说下(其实都很简单)

 

确定以上程序都安装好后,接下来就是进行配置了,Grafana 和 InfluxDB 实际上也没什么要配置的,装好后直接启动吧,启动命令

systemctl start influxd
systemctl start grafana-server

开启开机自动启动:

systemctl enable influxd.service
systemctl enable grafana-server.service
【InfluxDB 设置】

启动后,就可以通过 http://serverip:8083/ 访问在线的InfluxDB管理功能了。
在Query栏里输入:

CREATE USER "grafana" WITH PASSWORD 'grafana'
CREATE DATABASE grafana WITH DURATION 2d REPLICATION 5

第一行是创建一个 grafana 的帐号和密码
第二行是创建一个 grafana 库,并且默认保留数据2d是2天(m表示月)的意思,可以自行设置,REPLICATION 5 是分5个片,如果想永久保留把 DURATION 2d 去掉就可以了,这些后期都可以修改

【Grafana 设置】

http://serverip:3000/ 是 Grafana 的管理界面。

进入 Grafana 页面(默认帐号是 admin/admin)点击 Data Sources 连接,在上面点击 Add new 连接,type选择 InfluxDB0.9.x(虽然我装的是0.12但是只有这个,选这个可以用)URL 填 http://serverip:8086/ 这个 8086 端口就是 influxdb 的默认http接口端口。注意,Access 要选择 proxy。
下面的 InfluxDB Details 全部填 grafana 就好了。

【Fluentd 设置】

InfluxDB 的源添加好了,现在开始收集数据吧。收集数据我用到了 fluentd 进行分发。默认装好后就有

td-agent

命令了,此时,需要安装2个插件:

td-agent-gem install fluent-plugin-influxdb
td-agent-gem install fluent-plugin-rewrite-tag-filter

安装成功后,将下面我修改的influx插件文件放在 /etc/td-agent/plugin/out_influx.rb 文件中(是用官方的改的,解决了些细节问题)

# encoding: UTF-8
require 'date'
require 'influxdb'

class Fluent::InfluxOutput < Fluent::BufferedOutput
  Fluent::Plugin.register_output('influx', self)

  include Fluent::HandleTagNameMixin

  config_param :host, :string,  :default => 'localhost',
               :desc => "The IP or domain of influxDB."
  config_param :port, :integer,  :default => 8086,
               :desc => "The HTTP port of influxDB."
  config_param :dbname, :string,  :default => 'fluentd',
               :desc => <<-DESC
The database name of influxDB.
You should create the database and grant permissions at first.
DESC
  config_param :user, :string,  :default => 'root',
               :desc => "The DB user of influxDB, should be created manually."
  config_param :password, :string,  :default => 'root', :secret => true,
               :desc => "The password of the user."
  config_param :time_precision, :string, :default => 's',
               :desc => <<-DESC
The time precision of timestamp.
You should specify either hour (h), minutes (m), second (s),
millisecond (ms), microsecond (u), or nanosecond (n).
DESC
  config_param :use_ssl, :bool, :default => false,
               :desc => "Use SSL when connecting to influxDB."
  config_param :tag_keys, :array, :default => [],
               :desc => "The names of the keys to use as influxDB tags."
  config_param :sequence_tag, :string, :default => nil,
               :desc => <<-DESC
The name of the tag whose value is incremented for the consecutive simultaneous
events and reset to zero for a new event with the different timestamp.
DESC
  config_param :uniqu, :bool, :default => true


  def initialize
    super
    @seq = 0
  end

  def configure(conf)
    super
    @uniqu = conf['uniqu']
    @influxdb = InfluxDB::Client.new @dbname, host: @host,
                                              port: @port,
                                              username: @user,
                                              password: @password,
                                              async: false,
                                              time_precision: "n",
                                              # time_precision: @time_precision,
                                              use_ssl: @use_ssl
  end

  def start
    super
  end

  FORMATTED_RESULT_FOR_INVALID_RECORD = ''.freeze

  def format(tag, time, record)
    # TODO: Use tag based chunk separation for more reliability
    if record.empty? || record.has_value?(nil)
      FORMATTED_RESULT_FOR_INVALID_RECORD
    else
      [tag, time, record].to_msgpack
    end
  end

  def shutdown
    super
  end

  def write(chunk)
    points = []

    # 自动调回0
    if @seq > 1000000
      @seq = 0
    end

    chunk.msgpack_each do |tag, time, record|
      timestamp = record.delete('time') || time

      # 使用参数里的毫秒
      if record["microtime"] && record["microtime"] > timestamp
        timestamp = record["microtime"]
      end

      if @uniqu
        timestamp = (timestamp * 1000000000).to_i
      else
        @seq += 1
        # 这里强制 time_precision 参数设定成 n, 所以乘以1000000000
        timestamp = (timestamp * 1000000000).to_i + @seq
      end

      if tag_keys.empty?
        values = record
        tags = {}
      else
        values = {}
        tags = {}
        record.each_pair do |k, v|
          if @tag_keys.include?(k)
            tags[k] = v
          else
            values[k] = v
          end
        end
      end

      point = {
        :timestamp => timestamp,
        :series    => tag,
        :values    => values,
        :tags      => tags,
      }
      points << point
    end

    @influxdb.write_points(points)
  end
end

插件保存后,添加一个 td-agent(fluentd)的配置,将以下文件放在

/etc/td-agent/monitor-collect.conf

文件中

# CPU
<source>
  type tcp
  port 5170
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,usr:float,system:float,guest:float,cpu:float,cpu_num:integer
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<usr>[^ ]+)[ ]+(?<system>[^ ]+)[ ]+(?<guest>[^ ]*)[ ]+(?<cpu>[^ ]*)[ ]+(?<cpu_num>[\d]+)[ ]+(?<command>.*)$/
</source>

# 内存
<source>
  type tcp
  port 5171
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,minflt:float,majflt:float,vsz:integer,rss:integer,mem:float
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<minflt>[^ ]+)[ ]+(?<majflt>[^ ]+)[ ]+(?<vsz>[^ ]+)[ ]+(?<rss>[^ ]+)[ ]+(?<mem>[^ ]+)[ ]+(?<command>.*)$/
</source>

# IO
<source>
  type tcp
  port 5172
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,kprd:float,kbwr:float,kbccwr:float
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<kprd>[^ ]+)[ ]+(?<kbwr>[^ ]+)[ ]+(?<kbccwr>[^ ]+)[ ]+(?<command>.*)$/
</source>


<match collect.log>
  type rewrite_tag_filter
  rewriterule1 command ^influxd$ monitor.influxd
  rewriterule2 command ^java$ monitor.elasticsearch
  rewriterule3 command ^td-agent$ monitor.td-agent
  rewriterule4 command ^td-agent-(.*)$ monitor.td-agent-$1
  rewriterule5 command ^([^.]+)$ monitor.$1
</match>


<match monitor.*>
  type influx
  host 10.1.37.3
  port 8086
  dbname grafana
  user influxdb
  password influxdb
  remove_tag_prefix monitor.
  flush_interval 1
  tag_keys ["pid", "parent_pid", "command", "client"]
  uniqu false

  retry_wait 3
  buffer_type memory
  buffer_queue_limit 10
  buffer_chunk_limit 1m
  retry_limit 5
</match>

然后使用

td-agent -c /etc/td-agent/monitor-collect.conf

启动即可,如果需要放后台启动可以加 -d 参数,如果需要记录log,则可以加参数 -o logpath.log 完整的如下:

td-agent -d -c /etc/td-agent/monitor-collect.conf -o /var/log/td-agent/monitor-collsole.log

它监听了3个tcp端口,分别用来收集cpu,内存,io的。

tail -f /var/log/td-agent/monitor-collect.log

看看是否成功启动了

【收集指定程序的数据】

成功启动后,接下来就是去需要收集进程的其它服务器上操作了,这边需要用到我写的另外一个bash文件,文件保存在

/usr/local/bin/monitor-collect.sh

文件里(如果是别的文件名,则请修改下代码里的 “monitor-collect.sh” 为同名文件),另外,注意修改第4行的IP为 Fluentd 运行的服务器IP

#!/bin/bash
# 监控收集脚本

# 请修改成自已实际机器的IP
IP=127.0.0.1

MYNAME=$1

if [ "$MYNAME" = "" ]; then
  echo "缺少1个参数,请加关键字"
  echo "参数为进程匹配grep -E 关键字。使用方法如下: "
  echo "    monitor-collect.sh influxd"
  echo "    monitor-collect.sh td-agent mysql  表示匹配 td-agent mysql"
  echo "    monitor-collect.sh -p 123,456      表示只读123,456这2个进程"
  exit
fi

if [ "$1" = "-p" ]; then
  if [ "$2" = "" ]; then
    echo "-p 参数后必须指定进程ID,多个用,隔开"
    exit
  fi
fi

PID1=0
PID2=0
PID3=0
HAVE_PID1=""
HAVE_PID2=""
HAVE_PID3=""
RUN=true

function TaskClean()
{
  echo "Now do task clean..."
  RUN=false
}

function TaskExit()
{
  if [ $PID1 > 0 ]; then
    echo "Now exit pidstat1: $PID1"
    kill $PID1
    echo "done."
  fi
  if [ $PID2 > 0 ]; then
    echo "Now exit pidstat2: $PID2"
    kill $PID2
    echo "done."
  fi
  if [ $PID3 > 0 ]; then
    echo "Now exit pidstat3: $PID3"
    kill $PID3
    echo "done."
  fi
}

trap 'TaskClean; exit' SIGINT
trap 'TaskExit; exit' EXIT

echo "collect script pid: $$";

LANG=en_US.UTF-8
while $RUN; do
  if [ "$1" = "-p" ]; then
    PIDS=$2
  else
    PIDS=$(ps -eo pid,ppid,user,pcpu,command | grep -v '/bin/sh -c' | grep -v 'monitor-collect.sh' | grep -E "$MYNAME" | grep -v 'grep' | gawk '{print $1}')
    PIDS=$(echo $PIDS | sed -e s'/ /,/g')
  fi

  if [ "$PIDS" != "" ]; then
    echo "found pids: $PIDS"

    # CPU
    if [ "$HAVE_PID1" = "" ]; then
      pidstat -p $PIDS 1 > /dev/tcp/$IP/5170 &
      PID1=$!
      echo "pidstat pid: $PID1"
    fi

    # 内存
    if [ "$HAVE_PID2" = "" ]; then
      pidstat -r -p $PIDS 1 > /dev/tcp/$IP/5171 &
      PID2=$!
      echo "pidstat pid: $PID2"
    fi

    # IO
    if [ "$HAVE_PID3" = "" ]; then
      pidstat -d -p $PIDS 1 > /dev/tcp/$IP/5172 &
      PID3=$!
      echo "pidstat pid: $PID3"
    fi

    # 检查进程是否退出
    while true; do
      HAVE_PID1=$(ps $PID1 | awk '{ print $1 }' | grep $PID1)
      HAVE_PID2=$(ps $PID2 | awk '{ print $1 }' | grep $PID2)
      HAVE_PID3=$(ps $PID3 | awk '{ print $1 }' | grep $PID3)

      if [ "$HAVE_PID1" = "" ]; then
        echo "pidstat1 is exit. now restart"
        break
      fi

      if [ "$HAVE_PID2" = "" ]; then
        echo "pidstat2 is exit. now restart"
        break
      fi

      if [ "$HAVE_PID3" = "" ]; then
        echo "pidstat3 is exit. now restart"
        break
      fi
      sleep 3
    done
  fi
  sleep 1
done

保存好后,记得增加可执行权限:

chmod +x /usr/local/bin/monitor-collect.sh

这个脚本的用法很简单,比如你现在需要收集 mysql 的数据,只要运行如下代码即可:

monitor-collect.sh mysql

如果需要放在后台运行,则这样运行

nohup monitor-collect.sh mysql &

如果要同时监控 mysql,php-fpm,mongodb 的,则可以这样

nohup monitor-collect.sh "mysql|php-fpm|mongod" &

也可以监听指定进程id:

nohup monitor-collect.sh -p 123,456 &

表示监听123 和 456 两个进程。

需要停掉收集程序,直接

ps -ef | grep monitor-collect.sh

得到进程id后直接kill即可。

【确保数据是否都推送到了 InfluxDB 里了】

可以在网页版里,也可以在 influx 终端里执行

use grafana
show measurements

类似出现下面的内容则表示收集来了对应的进程数据

name: measurements
------------------
name
elasticsearch
influxd
mongod

select * from mongod where time > now() - 10s

查看最新10秒的数据,如果有的话则推送过来了,如果没有,请查看 fluntd 的log。

【Grafana 添加监控项目】

Grafana这个工具对于新手来说还是很难用的(至少我一开始不知道怎么下手),所以这边就简单的普及下吧。

先添加一个Dashboard,然后按 ctrl + h 键可以调出/隐藏绿色的控制按钮,点那个绿色的小条条(这设计的真是无语)会出来一个菜单,然后选 Add Panel-> Graph,会出来一个对话框,General 就是常规设置,这个名字就好了。
Metrics里添加一个查询,From选择对应的库,比如 mysql(这里它会自动把有的列出来,很人性化),添加一个WHERE,选择client值是对应服务器IP
SLECEC 选择一个字段(比如cpu)
GROUP BY 选择按1s配置,如下图

QQ20160407-2@2x

如果是CPU项目,则在 Axes & Grid 标签里,Left Y 的 Unit 选择 percent(0-100)
在 Display Styles 标签里,Chart Options 参数可以选择 Bars,如下图:
QQ20160407-3@2x

修改好后,点右边的 “Back to dashboard”,我一开始都不知道这里点,晕呀。下次要修改,只要点击标题,就有菜单可以修改了。

所有修改完毕后,记得点上面的保存图标。

做了一个 FirePHP For Chrome 的插件

FirePHP(http://www.firephp.org/)是一个php里不错的用于调试的工具,我在MyQEE里内置了这个功能,在线调试非常有用,只可惜做这个的作者好久没更新版本了,官方也只出了一个在Firefox里的插件。

而Chrome里的插件都是第三方做的,实际上很难用。这些天下狠心改了别人一款,改到了基本自己满意了,媲美FireFox里的插件,掌声在哪~~~~

本来是要公开发布出来的,可无奈google的应用商店第一次发布必须要付$5才行,我尝试的去付了下,可是支付失败,好像是不支持中国的信用卡,唉~~~~ 于是只好发布成私有的应用,私有的应用和公开的应用的差别在于只能通过指定的URL访问到,在google的应用市场里搜索不到,也罢~~~

应用的地址是: https://chrome.google.com/webstore/detail/firephp/gkkcbhnljobgoehijcpenalbacdmhaff?hl=zh-CN&utm_source=chrome-ntp-launcher&authuser=1

注:这个地址可能无法打开(被墙,你懂的)浏览是可能需要借助翻墙软件。

附截图:

如果无法打开地址,可以在这下载:

gkkcbhnljobgoehijcpenalbacdmhaff_main.crx

下载后安装方法:
首先解压缩压缩包,得到 gkkcbhnljobgoehijcpenalbacdmhaff_main.crx 文件,然后打开Chrome的扩展程序界面(入下图操作):

然后把刚刚解压得到的crx文件拖拽到这个“扩展程序”里(注意,只能是拖到这个页面,拖到别的页面是没用的),如下图操作:

这样就可以安装上了。

nginx+php-fpm环境下php输出图片、js、css等文件出现异常问题的原因及解决办法

【背景】
在我实际使用的环境中很少直接使用 nginx + php-fpm 方式搭建环境,大部分还都是使用apache,即便用到nginx,还只是用它监听80端口再代理apache的php做负载均衡器。
这次偶尔机会自己搭建了 nginx + php-fpm 环境,发现自己开发的MyQEE输出js、css以及image图片时会出现异常关闭的问题。

nginx配置了类似这样的rewrite

if (!-e $request_filename) {
    rewrite ^/.* /index.php last;
}

rewrite的意思是当请求的实际文件不存在时rewrite到index.php上

【那么问题来了】
由于有那个rewrite的存在,所以当请求类似 http://127.0.0.1/test.js 的URL,如果服务器上没有test.js,此时就会重定向导index.php,并不会直接返回404错误,而是由index.php来决定。

然后由这个index.php根据一些参数(例如uri=test.js)载入了另外一个js,并模拟页面输出,输出了一个200的头信息。
代码类似如下:

// index.php 文件,实际上的代码可能比这个复杂多,这里只是为了简单的说明问题
$file = 'my_other.js'; // 假设服务器上有另外一个my_other.js的文件
header('Content-Type: application/x-javascript');
header('Content-Length: '. filesize($file));
readfile($file);    // 把文件直接输出

这个看上去似乎没有任何问题,但是在nginx里却出现了奇怪问题。

浏览器再接受到部分内容输出后直接被关闭了。打开调试输出可以看到类似:
NET:ERR_CONTENT_LENGTH_MISMATCH
这样的错误。

如果在命令行里使用curl请求时,则会看到类似

curl: (18) transfer closed with 56097 bytes remaining to read

这样的错误。

【问题分析】
首先来看 nginx 的配置,类似如下:

location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

这个实际上是nginx通过代理php-fpm来实现的输出,当一个css、js、图片等正常请求因为rewrite的原因被rewrite到了php上,nginx内部会认为是一个文档处理,然后对文档进行压缩获得了压缩后的内容长度,在输出达到这个长度后就错误的关闭了tcp连接,但是返回的header头信息的长度却是压缩前的长度,这样就导致了之前的错误。

【问题解决】
我尝试过把gzip的功能关闭但是实际上没有启到任何作用,最后我是通过这样的方法解决的,在gzip的gzip_types参数把对应的文件都加上这样就解决问题了,代码如下:

location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
   
    # 以下解决用php输出js,css等文件导致出错的问题
    gzip on;
    gzip_min_length 1100;
    gzip_buffers 4 8k;
    gzip_types text/plain application/x-javascript text/css image;
}

2015-09-25更新
本人另外一个服务器实测也遇到这个问题,需要在加上才可以

proxy_buffering off

在nginx的默认配置里 gzip_types就只有 text/plain

【总结】
实际上的解决就是把对应输出的文件类型让nginx再压缩下即可,但是如果php输出的是图片或一些已经压缩过的二进制文件,那么这样做实际上是增加了服务器的负担。
所以如果有大量的图片、swf、zip等文件,建议还是通过sendfile的方式进行转发。
sendfile可参考:

使用git的subtree将已有项目的某个目录分离成独立项目

git在1.8版本后加入了subtree的功能,这个功能比之前的submodule功能强大很多,而且很好用,还是老版本的赶紧升级下吧。

一些使用方法可参见这个页面: http://aoxuis.me/posts/2013/08/07/git-subtree/

当一个项目在开发若干时间后,希望将某个目录单独出一个项目来开发,此时就可以利用这个subtree的功能分离里。

然而直接用git subtree add 的命令会出现 prefix ‘***’ already exists. 这样的错误提示,
这是因为对应的目录已经存在,不能直接添加,需要按下面的方式把对应的目录剥离开然后再加入subtree

具体的操作方式是这样的:
1. 首先cd到需要处理的项目的目录:

pushd <big-repo>
git subtree split -P <name-of-folder> -b <name-of-new-branch>    # 将需要分离的目录的提交日志分离成一个独立的临时版本
popd

2. 创建一个新的repo

mkdir <new-repo>
pushd <new-repo>

git init
git pull </path/to/big-repo> <name-of-new-branch>

3. 连接的github

git remote add origin <git@github.com:my-user/new-repo.git>
git push origin -u master

其中第2, 3两步也可以直接clone已有的hub,这样做:

git clone <git@github.com:my-user/new-repo.git> <dir-of-tmp-repo>
git pull </path/to/big-repo> <name-of-new-branch>
git push origin -u master

4. 清理数据

popd # get out of <new-repo>
pushd <big-repo>

git rm -rf <name-of-folder>
git commit -m '移除相应模块'            # 提交删除申请
git branch -D <name-of-new-branch>     # 删除临时分支,也可以不移除,自己看情况

5. 添加subtree

git subtree add -P <name-of-folder> <git@github.com:my-user/new-repo.git> master
git push origin master

执行到第3步时,对应的目录已经剥离出来形成独立的项目了。第4,5步主要是把当前项目的对应的文件给删除,可以不执行,以后可以通过

# 更新
git subtree pull -P <name-of-folder> <git@github.com:my-user/new-repo.git> master

# 提交
git subtree push -P <name-of-folder> <git@github.com:my-user/new-repo.git> master

来执行相应的subtree操作

Emmet LiveStyle – 适合前端攻城狮使用的实时在线调试CSS的工具,怎么好用?看了官方视频你肯定会心动

二话不说,先看 Emmet LiveStyle 官方视频:

顺便吐槽下万恶的TC网络,为了下载官方被河蟹的视频,我搞了1个小时才把视频拉下来

官方网站地址 http://livestyle.emmet.io/

这边稍微介绍下安装方法:
首先,你需要使用 Sublime ,这个编辑器是一款非常强悍的编辑器,在编辑器里装上LiveStyle的扩展,安装方法:
按ctrl+shift+p,然后输入controll后选择Install Package,再在弹出的对话框里输出 LiveStyle 确定后即开始安装了,骚等几秒钟即可安装好,若再不会请google。

然后,给浏览器装上插件,Chrome直接在应用商店里即可安装,地址 https://chrome.google.com/webstore/detail/diebikgmpmeppiilkaijjbdgciafajmg

至此,就可以开始使用了。

我这里简单说下使用方法:
在需要调试的页面上打开“开发人员工具”,window下F12键,Mac下option+command+j键,打开后你会发现在原来的控制台最后多了一个LiveStyle的标签,切换到这个标签,然后勾选启用。

这时候你会看到它列出很多CSS喝对于给你选择的下拉框,注意,下拉框里列出的就是你Sublime开启的所有CSS的文件列表。
选择对应关系后(当然也可以自行添加)你就可以开始用它了,整个插件的使用还是很简单的,最关健的就是它是双向实时的,和控制台结合的也非常完美。

挖出来的好东西,Glyphicons Pro Icons 字体图标打包提供下载了,含ttf, woff, eof, svg格式,是矢量的哦

Glyphicons (http://glyphicons.com/) 图标是一个非常好的图标组合。

本图标可以直接替换掉 bootstrap 的icon,详情查看 http://twitter.github.io/bootstrap/base-css.html#icons
也可与著名的 Font-Awesome 配合使用

DEMO在线演示
https://www.queyang.com/demo/glyphicons/

打包下载
https://www.queyang.com/demo/glyphicons/glyphicons.zip

使用方法:
HEAD里加入CSS

<link href="css/glyphicons.css" rel="stylesheet" />

需要使用图标时加入样式即可,比如

<i class="icon-glass"></i>

上一个PNG图
glyphicons