使用Nginx 和Supervisor在Linux服务器上部署Tornado

Nginx 安装: sudo apt-get install nginx

Nginx 安装后用浏览器进入127.0.0.1就可以看到nginx的欢迎页了

nginx 常用命令

  1. sudo service nginx start 启动nginx
  2. sudo service nginx stop 停止nginx
  3. sudo service nginx restart 重启nginx
  4. sudo service nginx reload 重新加载配置文件

未分类

Supervisor 安装: sudo apt-get install supervisor

部署步骤:

  • Tornado项目路径 : /home/你的用户名/Tornado项目文件夹名称/main.py

  • 在/etc/nginx/下 创建nginx.conf配置文件
    这里我们使用8000-8003四个端口,进行端口转发 配置文件编写要注意main.py所在位置要写对,即下面配置文件中的中文

user root;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 768;
    multi_accept on;
    use epoll;
}
http {
    # Enumerate all the Tornado servers here
    upstream frontends {
        server 127.0.0.1:8000;
        server 127.0.0.1:8001;
        server 127.0.0.1:8002;
        server 127.0.0.1:8003;
    }

    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log /var/log/nginx/access.log;

    keepalive_timeout 65;
    proxy_read_timeout 200;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1000;
    gzip_proxied any;
    gzip_types text/plain text/html text/css text/xml
               application/x-javascript application/xml
               application/atom+xml text/javascript;
    proxy_next_upstream error;

    server {
        listen 80;

        # Allow file uploads
        client_max_body_size 50M;

        location ^~ /static/ {
            root /home/用户名/项目文件夹名/;
            if ($query_string) {
                expires max;
            }
        }
        location = /favicon.ico {
            rewrite (.*) /static/favicon.ico;
        }
        location = /robots.txt {
            rewrite (.*) /static/robots.txt;
        }

        location / {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_pass http://frontends;
        }
    }
}
  • 编写supervisor配置文件

  • 进入supervisor配置文件夹 cd /etc/supervisor/conf.d/

  • 创建tornados.conf

[group:tornadoes]
programs=tornado-8000,tornado-8001,tornado-8002,tornado-8003

[program:tornado-8000]
command=python /home/用户名/项目文件夹名/main.py --port=8000
directory=/home/用户名/项目文件夹名
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info

[program:tornado-8001]
command=python /home/用户名/项目文件夹名/main.py --port=8001
directory=/home/用户名/项目文件夹名
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info

[program:tornado-8002]
command=python /home/用户名/项目文件夹名/main.py --port=8002
directory=/home/用户名/项目文件夹名
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info

[program:tornado-8003]
command=python /home/用户名/项目文件夹名/main.py --port=8003
directory=/home/用户名/项目文件夹名
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info
  • 其中/var/log/tornado.log为日志文件目录
    然后先重载nginx的配置文件 sudo service nginx reload
    重启 nginx : sudo service nginx restart
    supervisor开启所有进程: sudo supervisorctrl restart all

再次打开127.0.0.1后可以看到项目已经成功部署。

Linux下的守护进程工具-Supervisor

简介:没有

安装

sudo apt-get install supervisor

(其实我是用Mint的软件管理器安装的……)

配置

参考帖子:

详解supervisor使用教程 http://www.jb51.net/article/128676.htm

supervisor 从安装到使用 http://www.jianshu.com/p/3658c963d28b

使用 Supervisor 进行进程管理 http://www.jianshu.com/p/2a48b2c987e0

执行命令

sudo supervisord
sudo supervisorctl start/stop/restart RROGRAM_NAME
sudo supervisorctl reload
sudo supervisorctl status

配置文件模板

我的配置文件在/etc/supervisor/supervisord.conf
我的配置模板:

[program:ssr]
command = python local.py -c /home/jerry/opt/shadowsocksr-manyuser/shadowsocks/shadowsocksr.json
stdout_logfile=/tmp/ssr.log
stdout_logfile_backups=2 ;默认为10 
stdout_capture_maxbytes=1MB 

[program:drcom]
command = python2 /home/jerry/opt/DRCOM_PY/latest-wired.py
stdout_logfile=/tmp/drcom.log
stdout_logfile_backups=2 ;默认为10 
stdout_capture_maxbytes=1MB 
autostart= false

通用模板

;[program:theprogramname]
;command=/bin/cat              ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1                    ; number of processes copies to start (def 1)
;directory=/tmp                ; directory to cwd to before exec (def no cwd)
;umask=022                     ; umask for process (default None)
;priority=999                  ; the relative start priority (default 999)
;autostart=true                ; start at supervisord start (default: true)
;startsecs=1                   ; # of secs prog must stay up to be running (def. 1)
;startretries=3                ; max # of serial start failures when starting (default 3)
;autorestart=unexpected        ; when to restart if exited after running (def: unexpected)
;exitcodes=0,2                 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT               ; signal used to kill process (default TERM)
;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                   ; setuid to this UNIX account to run the program
;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
;stdout_logfile=/a/path        ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false   ; emit events on stdout writes (default false)
;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false   ; emit events on stderr writes (default false)
;environment=A="1",B="2"       ; process environment additions (def no adds)
;serverurl=AUTO                ; override serverurl computation (childutils)

; 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.

CentOS Linux 6 / 7 离线编译安装 PHP 7

安装

1、先去官网下载最新版的 PHP:

http://php.net

未分类

目前 PHP 7.2 只是第一个版本( PHP 7.2.0 ),我们目前安装的是 PHP 7.1.12 这个较为稳定的版本。如下图所示,复制 PHP 的 tar.gz 包的下载链接:

未分类

2、进入你的 CentOS ,先使用 yum 安装 wget(如果已经安装过,可忽略这步):

[root@host ~]# yum -y install wget

3、Ricky 个人是喜欢把一些临时文件放到 /tmp 目录下,所以进入 /tmp 目录,使用 wget 命令把 PHP 的 tar.gz 包下载到这里,然后解压:

[root@host ~]# cd /tmp
[root@host tmp]# wget http://cn.php.net/distributions/php-7.1.12.tar.gz
[root@host tmp]# tar -zxvf php-7.1.12.tar.gz

4、在进行编译安装之前,还要先安装有关于 PHP 的依赖包:

[root@host tmp]# yum install -y libxml2-devel libtool* curl-devel libjpeg-devel libpng-devel freetype-devel

5、还要再编译安装一个名为 libmcrypt 的包:

[root@host tmp]# wget https://svwh.dl.sourceforge.net/project/mcrypt/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz
[root@host tmp]# tar -zxvf libmcrypt-2.5.8.tar.gz 
[root@host tmp]# cd libmcrypt-2.5.8
[root@host libmcrypt-2.5.8]# ./configure && make && make install

6、进入第 3 步解压出来的 php-7.1.12 文件夹,在这里执行编译安装操作:

[root@host libmcrypt-2.5.8]# cd ../php-7.1.12
[root@host php-7.1.12]# ./configure --prefix=/usr/local/php-7.1.12 --enable-fpm --enable-opcache --with-config-file-path=/usr/local/php-7.1.12/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --enable-static --enable-sockets --enable-wddx --enable-zip --enable-calendar --enable-bcmath --enable-soap --with-zlib --with-iconv --with-freetype-dir --with-gd --with-jpeg-dir --with-xmlrpc --enable-mbstring --with-sqlite3 --with-curl --enable-ftp --with-mcrypt --with-openssl --disable-safe-mode --with-gettext
[root@host php-7.1.12]# make && make install

7、设置一个软链接:

[root@host php-7.1.12]# cd /usr/local/
[root@host local]# ln -s php-7.1.12/ php

8、添加 PHP 命令到环境变量:

[root@host local]# vi /etc/profile

键入大写字母 G 定位到最后一行,再键入小写字母 o 进入编辑模式,将以下配置复制粘贴进最后一行:

export PATH=$PATH:/usr/local/php/bin

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。要使改动立即生效,请执行以下命令:

[root@host local]# ./etc/profile

[root@host local]# source /etc/profile

如需查看环境变量,请执行以下命令:

[root@host local]# echo $PATH

如需查看 PHP 版本,请执行以下命令:

[root@host local]# php -v
PHP 7.1.12 (cli) (built: Dec 14 2017 15:45:28) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

9、复制配置文件和 PHP 的启动脚本,再把 PHP 的启动脚本添加到开机自启动(还要给 php-fpm 和 rc.local 文件添加可执行权限),最后执行 PHP 的启动脚本即可:

(1)复制配置文件和 PHP 的启动脚本:
[root@host local]# cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
[root@host local]# cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf
[root@host local]# cp /tmp/php-7.1.12/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

(2)给 php-fpm 和 rc.local 文件添加可执行权限:
[root@host local]# chmod +x /etc/init.d/php-fpm
[root@host local]# chmod +x /etc/rc.d/rc.local
[root@host local]# chmod +x /etc/rc.local

(3)把 PHP 的启动脚本添加到开机自启动:
[root@host local]# echo "/etc/init.d/php-fpm start" >> /etc/rc.local

(4)执行 PHP 的启动脚本:
[root@host local]# /etc/init.d/php-fpm start

10、现在 php-7.1.12 和 libmcrypt-2.5.8 这两个文件夹可以删除了:

[root@host local]# cd /tmp
[root@host tmp]# rm -rf php-7.1.12/
[root@host tmp]# rm -rf libmcrypt-2.5.8/

至此,安装完成。

疑问及如何升级到更新版本的 PHP

为什么要设置这个软链接?主要是为了方便升级和记录当前 PHP 的版本号。具体请参见该篇文章 https://ccie.lol/knowledge-base/linux-centos-install-tomcat/ 的 “ 疑问及如何升级到更新版本的 Tomcat ” 部分。

测试

在代码根目录新建一个 index.php 的文本文件,文件内容为:

<?php
phpinfo();
?>

若 PHP 配置成功,浏览器打开该页面将会显示以下内容:

未分类

Linux安装Redis

1. 基本知识

Redis 的数据类型

  字符串 , 列表 (lists) , 集合 (sets) , 有序集合 (sorts sets) , 哈希表 (hashs)

Redis 和 memcache 相比的独特之处

  • redis可以用来做存储 (storge) , 而memcache是来做缓存 (cache) 。这个特点主要是因为其有 “持久化” 功能
  • 存储的数据有 “结构” , 对于memcache来说 , 存储的数据 , 只有一种类型—— “字符串” , 而redis则可以存储字符串 , 链表 , 集合 , 有序集合 , 哈序结构

持久化的两种方式

Redis 将数据存储于内存中 , 或被配置为使用虚拟内存 ; 实现数据持久化的两种方式 :

  • 使用截图的方式 , 将内存中的数据不断写入磁盘 (性能高 , 但可能会引起一定程度的数据丢失)
  • 使用类似 MySql l的方式 , 记录每次更新的日志

Redis 服务端的默认端口是 6379

2. 安装 Redis

先去官网下载 Redis 安装包 https://redis.io/download

放到 /usr/local/app_pack 目录下面 , 解压文件 :

# tar -zxvf redis-4.0.6.tar.g

进入到解压后的目录 :

# cd redis-4.0.6

编译程序 :

# make

安装到指定目录 :

# make install PREFIX=/usr/local/redis

这时候 Redis 程序已经被安装到 /usr/local/redis/bin 目录
将配置文件移动到安装目录 :

# mv redis.conf /usr/local/redis/etc/redis.conf

启动 Redis 服务器 :

# /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

这里是用 /usr/local/redis/etc/redis.conf 配置文件启动 Redis
关闭 Redis 服务器 :

# /usr/local/redis/bin/redis-cli shutdown

或者 # pkill redis-server

客户端连接 :

# /usr/local/redis/bin/redis-cli

设置一个 Redis 键值对 :

127.0.0.1:6379> set foo bar
OK

根据键获取值 :

127.0.0.1:6379> get foo
"bar"

默认情况 Redis 不是在后台运行 , 如果需要把 Redis 放在后台运行 , 编辑配置文件 :

# vim /usr/local/redis/etc/redis.conf

将 daemonize 的值改为 yes

让 Redis 开机启动 :

# vim /etc/rc.local

加入

/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis-conf

3. 文件及配置概述

/usr/local/redis/bin 目录下的几个文件用途 :

redis-benchmark : Redis 性能测试工具
redis-check-aof : 检查 aof 日志的工具
redis-check-dump : 检查 rdb 日志的工具
redis-cli : 连接用的客户端
redis-server : Redis 服务进程

Redis 的配置项解析 :

daemonize : 如需要在后台运行 , 把该项的值改为 yes
pdifile : 把 pid 文件放在 /var/run/redis.pid , 可以配置到其他地址
bind : 指定 Redis 只接收来自该 IP 的请求 , 如果不设置 , 那么将处理所有请求 , 在生产环节中最好设置该项
port : 监听端口 , 默认为 6379
timeout : 设置客户端连接时的超时时间 , 单位为秒
loglevel : 等级分为 4 级 , debug , revbose , notice 和 warning , 生产环境下一般开启 notice
logfile : 配置 log 文件地址 , 默认使用标准输出 , 即打印在命令行终端的端口上
database : 设置数据库的个数 , 默认使用的数据库是 0
save : 设置 redis 进行数据库镜像的频率
rdbcompression : 在进行镜像备份时 , 是否进行压缩
dbfilename : 镜像备份文件的文件名
dir : 数据库镜像备份的文件放置的路径
slaveof : 设置该数据库为其他数据库的从数据库
masterauth : 当主数据库连接需要密码验证时 , 在这里设定
requirepass : 设置客户端连接后进行任何其他指定前需要使用的密码
maxclients : 限制同时连接的客户端数量
maxmemory : 设置redis能够使用的最大内存
appendonly : 开启 appendonly 模式后 , Redis 会把每一次所接收到的写操作都追加到 appendonly.aof 文件中 , 当 Redis 重新启动时 , 会从该文件恢复出之前的状态
appendfsync : 设置 appendonly.aof 文件进行同步的频率
vm_enabled : 是否开启虚拟内存支持
vm_swap_file : 设置虚拟内存的交换文件的路径
vm_max_momery : 设置开启虚拟内存后 , Redis 将使用的最大物理内存的大小 , 默认为 0
vm_page_size : 设置虚拟内存页的大小
vm_pages : 设置交换文件的总的page数量
vm_max_thrrads : 设置 vm IO 同时使用的线程数量

4. 错误解决办法

  • 客户端连接时 , 提示 DENIED Redis is running in protected mode because protected mode is enabled…

将配置文件里的 protected mode改为了 no , 原本是 yes

linux 下抓包命令 tcpdump tcpflow

tcpflow实际上也是一个抓包工具,这个抓包工具与tcpdump不同的是它是以流为单位显示数据内容,而cpdump以包为单位显示数据。我们平常会经常分析HTTP数据,用tcpflow会更便捷,且tcpflow看起来会更加直观些。

#截取本机(192.168.31.147)和主机114.114.114.114之间的数据
tcpdump -n -i eth0 host 192.168.31.147 and 114.114.114.114
#截取全部进入服务器的数据可以使用以下的格式
tcpdump -n -i eth0 dst 192.168.31.147
#服务器有多个IP 可以使用参数
tcpdump -n -i eth0 dst 192.168.31.147  or  192.168.31.157
#从本机出去的数据包
tcpdump -n -i eth0 src 192.168.31.147 or 192.168.31.157

-C
在将一个原始数据包写入一个保存文件之前,请检查该文件是否大于 file_size ,如果是,关闭当前的保存文件并打开一个新文件。第一个保存文件后的保存文件将具有用-w 标志指定的名称 ,后面跟着一个数字,从1开始并继续向上。file_size的单位 是数百万字节(1,000,000字节,而不是1,048,576字节)。

-w
将原始数据包写入 文件, 而不是解析并打印出来。他们以后可以用-r选项打印。如果文件 是“ – ”,则使用标准输出 。有关文件格式

-W
与-C 选项一起使用时 ,这会将创建的文件数量限制为指定的数字,并从头开始覆盖文件,从而创建“旋转”缓冲区。另外,它将命名带有足够前导0的文件来支持最大数量的文件,使它们能够正确排序。
与-G 选项一起使用时 ,这将限制创建的旋转转储文件的数量,在达到限制时以状态0退出。如果与-C一起使用 ,则行为将导致每个时间片的循环文件。

Linux文本过滤命令grep|awk|sed小结

grep的使用

一、grep一般格式

grep [选项] 基本正则表达式 [文件]

这里的正则表达式可以为字符串。在grep命令中输入字符串参数时,最好将其用双引号括起来。调用变量时也可以使用双引号。在调用模式匹配事,应使用单引号。

二、grep常用选项

-c 只输出匹配行的计数。
-i 不区分大小写(只用于单字符)。
-h 查询多文件时不显示文件名。
-l 查询多文件时只输出包含匹配字符的文件名。
-n 显示匹配行及行号。
-s 不显示不存在或无匹配文本的错误信息。
-v 显示不包含匹配文本的所有行。

三、常用选项的使用

1、 查询多个文件:

$grep  “hello” *.doc
num.doc:hello
sum.doc:hello world

说明在num.doc 和sum.doc中有字符串“hello“。

2、 行匹配:

$grep -c “123” *.doc
num.doc:2
sum.doc:2

说明在num.doc 和sum.doc中有字符串“123“各为两行。

3、 显示满足匹配模式的所有行数:

$grep –n “123” *.doc
num.doc:1:hao 123
num.doc:2:hao 123
sum.doc:2:hao 123
sum.doc:4:hao 1234

4、 显示所有不包含“123“的行:

$grep  -v “123” *.doc
num.doc:hello
num.doc:world
sum.doc:hello world
sum.doc:sui yue

5、 精确匹配:

如第三点,匹配“123”时,结果返回了“1234”的其它字符串。

$grep -n “123>” *.doc
num.doc:1:hao 123
num.doc:2:hao 123
sum.doc:2:hao 123

6、 大小写敏感:

缺省情况下,grep是大小写敏感的,如果要查询大小写不敏感的字符串,必须

使用-i选项。

$grep -i “just” *.doc 
num.doc:Just
num.doc:just

四、grep和正则表达式

1、模式范围:

$ grep  ‘48[34]’ mydata.doc

此命令在mydata.doc中匹配字符串“483”和“484”。

2、 不匹配行首:

$ grep  ‘^[^48]’ mydata.doc

此命令在mydata.doc中匹配除4或者8开头的行。

3、 设置大小写:

$ grep  ‘[Ss]ept’  mydata.doc

此命令在mydata.doc中匹配字符串“Sept”和“sept”。

4、匹配任意字符:

$ grep  ‘a…z’ mydata.doc

此命令在mydata.doc中匹配字符串以字符a开头和字符z结尾,中间为任意三个字符。

5、 模式出现的机率

$ grep ‘1{2,}’ mydata.doc

此命令说明数字1至少出现两次


awk的使用

一、awk的调用格式

awk的调用格式有三种:

第一种是命令行的方式,如下:

awk [-F field-separator] ‘commands’ input-file

[-F]域分隔符,是可选的,awk使用空格作为缺省的域分隔符。

第二种方法是将awk命令插入到一个文件,并使awk程序可以执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。

第三种方法是将所有的awk命令插入一个单独文件,然后调用:

awk –f awk-script-file input-file

二、动作和模式

任何awk语句都有模式和动作组成。模式部分决定动作语句何时触发及触发事件。

模式包括两个特殊字段BEGIN和END.,BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行,END语句用来在awk完成文本浏览动作后打印输出文本中输出和结尾状态标志。实际动作在大括号内指明。

三、域和记录

awk执行时,其浏览域标记为$1,$2 ….$n.。这种方法称为域标识。

1、保存awk输出

第一种方法:

$awk ‘{print $0}’ mydata.txt >newfile.txt

显示屏上不会输出结果。

第二种方法:

$awk ‘{print $0}’ mydata.txt | tee newfile.txt

输出到文件中,同时显示在显示器上。

2、使用标准输入

第一种方法:使用awk脚本输入文件格式

$myawk.awk inputfile.txt

第二种方法:使用重定向

$myawk.awk < inputfile.txt

第三种方法:使用管道

$inputfile.txt | myawk.awk

3、打印所有记录

$awk ‘{print $0}’ myfile.txt

由于没有模式部分,只有动作部分,动作必须用花括号括起来。

Lisi 25 wuhan
Wangjian 33 shanghai
Caiming 55 shenzhen

4、打印单独记录

要用逗号分隔域

$awk ‘{print $1,$2}’ myfile.txt
Lisi 25
Wangjian 33
Caiming 55

5、打印报告头

$awk ‘BEGIN {print “Name Agen******************“} {print $1”t”$2}’ mfile.txt

Name    Age
***************
Lisi      25
Wangjian 33
Caiming 55

6、打印信息尾

$awk ‘BEGIN {print “Name n****** “} {print $1 } END {“end of reportn”}’ mfile.txt

sed的使用

一、sed调用格式

sed调用格式有三种:

第一种命令行方式:

sed [option] ‘command’ inputfile

ommand要加单引号,也允许加双引号。

第二种使用sed脚本文件:

sed  [option]  –f  sed-script-file inputfile

第三种使用第一行具有sed命令解释器的sed脚本文件:

sed-script-file [option] inputfile

二、sed选项

n 不打印:sed不写编辑行到标准输出,缺省为打印所有行。P命令可以用来打印编辑行。
c 下一命令是编辑命令。使用多项编辑时加入选项。
f 如果正在调用sed脚本文件,使用此选项。此选项通知sed脚本文件支持所有的sed命令。

三、sed常规用法

1、 保存sed 输出

$ sed  ‘command’ inputfile > outfile

2、 sed在文件中查找文本的方式

sed浏览文件时,默认从第一行开始,有两种方法定位文本:
第一种:使用行号,可以用简单数字或是一个行号范围。
第二种:使用正则表达式。

Sed定位文本的一些方式,如下:

x x表示行号。 
x,y 表示行号范围从x到y。 
/pattern/ 查找包含模式的行。 
x,/pattern 通过行号和模式查询匹配行。 
x,y! 查找不包含指定行号x到y的行。 

3、 sed编辑命令

p 打印匹配行 
= 显示文件行号 
a 在定位行号后附加拳文本信息 
i 在定位行号后插入拳文本信息 
d 删除定位行 
c 用拳文本替换定位文本 
s 使用替换模式替换相应模式 
r 从另一个文件中读文本 
w 写文本到一个文件 
q 第一个模式匹配完成后推出或立即推出 
l 显示与八进制ASCII代码造价的控制字符 
{} 在定位行执行的命令组 
n 从另一个文件中读文本下一行,并附加在下一行 
g 将模式2粘贴到/pattern n/ 
y 传送字符 
n 延续到下一输入行;允许跨行的模式匹配语句。 

4、 使用p显示行

$sed -n ‘2p’ myfile.txt
Hello world!

-n选项,显示打印定位行。

5、 打印范围

$sed -n ‘1,3p’ myfile.txt
Hao 123
Hello world!
Ni hao ma?

打印1到3行。

6、 打印模式

$sed -n ‘/123/’p myfile.txt
Hao 123

匹配

7、 在指定行号匹配模式

$sed -n ‘5,/Hao/’p myfile.txt
Hao ma?

【Linux】cat、tail、head、grep、sed查看文件任意几行的数据

grep结果太多, 可否只取前面10行匹配的结果

grep ...... | head -10

一、使用cat、tail、head组合

1、查看最后1000行的数据

cat filename | tail -n 1000

2、查看1000到3000行的数据

cat filename | head -n 3000 | tail -n +1000

1、cat filename 打印文件所有内容
2、tail -n 1000 打印文件最后1000行的数据
3、tail -n +1000 打印文件第1000行开始以后的内容
4、head -n 1000 打印前1000的内容

二、使用sed命令

显示1000到300行的数据

sed -n '1000,3000p' filename

============================================
输入ps,它就是我们今天的主角,ps是linux操作系统中最基本同时也是非常强大的进程查看命令,如果你对此命令不是十分了解,我们可以输入ps
–help命令来查看此命令的帮助信息。
通过帮助信息我们可以看到,ps命令的相关参数有很多,很多初学的朋友可能会看的一头雾水,不知道该怎么组合这些参数,下面小编就举一些实际应用例子,来介绍一些比较常用的查看进程的固定命令组合。
我们先来看第一个命令,ps

-l命令。这个命令和直接使用ps效果类似,但是不同之处在于使用ps命令获得结果很短,而使用-l参数之后将会较长、较详细的列出该PID的的信息列出,由于参数较多,小编就不一一介绍各个参数的含义了,如果想要了解参数的具体含义可以上网查看相关信息。
接着我们来看第二个命令ps

aux,有“-”符号和没有两者是有区别的,这个命令应该是比较常用的一个命令,作用就是列出目前所有的正在内存当中的程序,其中a表示显示现行终端机下的所有程序,包括其他用户的程序,u表示以用户为主的格式来显示程序状况,x表示显示所有程序,不以终端机来区分,它的相关参数也不少,例如user,表示属于那个使用者账号的,%CPU表示使用掉的CPU资源百分比,其他的参数就不一一的介绍了,有兴趣可以自行查看。

============================================
rl+ 是什么状态 linux ?是串联和是并联。

Linux 下 Nginx + PHP 环境的配置

从我开始折腾 WordPress、Typecho 博客至今,我折腾了无数次 Nginx 的安装、配置与 PHP 环境的搭建,看过各种各样的教程,它们往往都有一个共同点,就是仅仅是给你一些现成的命令复制粘贴,它们大多从操作的角度出发,并没有太多原理上的阐述。就像之前我看到 火丁笔记博客的一篇文章 所说:“如果大家不求甚解,一味的拷贝粘贴,早晚有一天会为此付出代价。”所以我希望通过这篇文章,能够在一个不一样的角度去描述这个过程,希望能对看到这篇文章的你有所帮助。

本文假定你对 Linux 的命令、程序的文件IO、HTTP 协议、PHP 有一些大致的了解。

服务器后端做的事情

首先我们应该对网站的结构有一个基本的认识,通常来说一个网站的分为前端与后端两部分。它们相互依存,前端在用户的浏览器中,负责内容的显示和交互。后端负责处理浏览器发来的请求,根据不同的请求生成对应的响应返回给前端展示给用户。

浏览器与服务器之间需要通信,必然得遵循一些标准,一个必备的标准就是 HTTP 协议啦。(可能有的网站会用WebSocket等协议,但这篇文章只讨论HTTP的内容)

从本质的角度来看,后端的主要工作就是处理浏览器发来的 HTTP 请求,并根据服务器程序的业务逻辑返回这些请求对应的 HTTP 响应给浏览器。后端返回的响应,可以是一段 HTML/CSS/JavaScript 代码、也可以是一张图片、一段音频、或者是一个个不同类型文件。

我们可以这么说,无论这个后端是什么程序编写的,只要它能依照 HTTP 协议返回符合协议规定格式的响应就可以满足要求。因此,与前端只能用 HTML/CSS/JavaScript 三件套不同,后端的语言,平台的选择可以有很多,例如 PHP, Python, Ruby, JavaScript(Node.js), Java, Go, C++, C 等等。PHP 只是其中的一种选择,由于 PHP 的部署简单,性能也不错,所以也十分流行。

Web服务器

后端处理 HTTP 请求,主要还是通过 Web 服务器来实现,Web 服务器返回响应的过程主要有两条途径:

  1. 返回请求的对应的磁盘文件
  2. 把请求分发给其他的程序处理,并把该程序处理的结果返回。

如图:

未分类

其中,这里的请求分发的过程,也叫作反向代理(Reverse Proxy)。

一般来说用的比较多的 Web 服务器软件有 Nginx、Apache 和 Lighttpd,它们都是 C 语言编写的,其中因为 Nginx 的配置较为简单,高性能且资源占用较少,在业界备受欢迎,这里我也选择了 Nginx 作为网站的 Web 服务器。

PHP的执行机制

PHP脚本的执行

PHP的全称是,PHP: Hypertext Preprocessor,中文名为“超文本预处理器”。也就是说,PHP主要还是为了处理文本而产生的,这从它的代码中也有体现,我们来尝试一个简单的例子:

新建一个文本文件,命名为 temp.php ,里面输入以下内容:

这是php标签外的内容
---------华丽丽的分割线---------
<?php
$a = 2333 + 6666;
echo "这是PHP标签内部的内容,将会返回一个运算结果n";
echo "2333+6666=" . $a;
?> 
---------又是一条华丽丽的分割线---------
这是php标签外部的内容
---------下一条华丽丽的分割线---------
<?php
$b = 12345;
echo "变量$b的值为" . $b;
?> 
---------最后一条华丽丽的分割线---------
php标签外部的内容

把 temp.php 交给PHP解释器执行,这里我以Linux命令行为例,下面是这段脚本运行后的结果:

未分类

也就是说,PHP 解释器在处理文本的时候,把 之外的内容直接输出到了标准输出流中,当遇到 之间的代码时,它运行了代码,再把代码中 echo 语句的结果输出到标准输出流中。

我们可以通过重定向的操作,把 PHP 解释器的标准输出流重定向到别的地方,例如,我这里把它的输出结果重定向到与代码同目录的 result.txt 里面。

这时候我们试一下把 result.txt cat出来,发现里面的内容也和之前的输出一样。

未分类

ps: 如果你对数据流的概念不熟悉,可以参考《鸟哥的 Linux 私房菜》关于流的描述

通过Web服务器运行PHP脚本

我们知道,PHP 这门语言主要应用在 Web 的领域中,所以一般 PHP 文件都是通过 Web 服务器来触发运行的。

为了方便测试,我们可以用下面的命令可以直接在 temp.php 所在的文件夹下启动一个临时的 PHP cli 开发服务器,就不用配环境那么麻烦了:

php -S 0.0.0.0:8888

未分类

在浏览器访问 http://0.0.0.0:8888/temp.php 或者 http://127.0.0.1:8888/temp.php ,查看源代码,我们可以看到如图所示的结果:

未分类

我们可以发现,浏览器中看到的内容,和我们之前用 PHP 运行这个脚本生成的输出结果是一样的。

忽略细节的话,在某种意义上我们也许也可以这么说,PHP 服务器程序在收到浏览器发过来的请求之后,运行脚本,把脚本的标准输出流重定向到了浏览器中,就像之前把命令行运行的结果重定向到了 result.txt 一般。

相比通过文件存储的静态网页,类似PHP每次接到请求后通过解释器执行,执行的结果来返回数据的页面,因为数据会根据实际情况而变化,通常也被称为“动态网页”。

CGI协议

随着动态网页的流行,这种服务器接收请求,执行程序,把执行程序的输出返回给浏览器的过程也逐渐成型。规范化这个过程也变得有必要起来,由此,CGI协议便诞生了。

CGI (Common Gateway Interface),中文名是“通用网关接口”,它定义了Web服务器与处理请求的程序之间传输数据需要遵循的标准。

一般来说,程序运行时,它与外界交互的途径是标准输入(stdin)、标准输出(stdout)和环境变量,有的可能涉及到其它的文件IO,CGI协议定义了HTTP请求、HTTP响应与程序运行的环境变量、输入流、输出流的对应关系,从而实现了通过后端程序来处理HTTP请求的功能,这个程序也被称为CGI中的“网关”(Gateway)。关于CGI协议的具体规定,我们可以参阅 RFC3875 的文档描述。 https://tools.ietf.org/html/rfc3875

CGI协议规定了,每一个HTTP请求交给一个网关程序的进程来处理。这个HTTP请求的请求头(Header),QueryString,以及其它关于客户端的信息,作为网关程序运行的环境变量,这个HTTP中的请求体(Body),作为网关程序运行的标准输入(stdin);网关程序执行过程中的标准输出(stdout),则作为这个HTTP请求的响应数据返回给Web服务器。

参考 http://www.php-internals.com/book/?p=chapt02/02-02-03-fastcgi 的介绍,引用一下:

CGI 的运行原理

  1. 客户端访问某个 URL 地址之后,通过 GET/POST/PUT 等方式提交数据,并通过 HTTP 协议向 Web 服务器发出请求。

  2. 服务器端的 HTTP Daemon(守护进程)启动一个子进程。然后在子进程中,将 HTTP 请求里描述的信息通过标准输入 stdin 和环境变量传递给 URL 指定的 CGI 程序,并启动此应用程序进行处理,处理结果通过标准输出 stdout 返回给 HTTP Daemon 子进程。

  3. 再由 HTTP Daemon 子进程通过 HTTP 协议返回给客户端。

上面的这段话理解可能还是比较抽象,下面我们就通过一次 GET 请求为例进行详细说明。

未分类

如图所示,本次请求的流程如下:

  1. 客户端访问 http://127.0.0.1:9003/cgi-bin/user?id=1
  2. 127.0.0.1 上监听 9003 端口的守护进程接受到该请求
  3. 通过解析 HTTP 头信息,得知是 GET 请求,并且请求的是 /cgi-bin/ 目录下的 user 文件。
  4. 将 uri 里的 id=1 通过存入 QUERY_STRING 环境变量。
  5. Web 守护进程 fork 一个子进程,然后在子进程中执行 user 程序,通过环境变量获取到id。
  6. 执行完毕之后,将结果通过标准输出返回到子进程。
  7. 子进程将结果返回给客户端。

基于PHP语言的Web程序,它的工作机制也类似于CGI的模型,但根据实际的情况,PHP 的具体实现会有些不一样。在PHP中,CGI协议所用到的程序,是通过它自带的 php-cgi 来支持的,这个后文会继续介绍。

SAPI 抽象层

刚刚我们可以注意到,我们可以通过浏览器发送HTTP请求的方式执行PHP,也可以在命令行通过运行文件的方式执行PHP,每种运行方式看起来很不一样,但是实际上它们内部的工作流程是一样的。这得益于PHP代码中的SAPI中间层。

那么,什么是SAPI呢?

首先我们来看看PHP的架构图(图片来自鸟哥的博客 ps: PHP的鸟哥和写 Linux 私房菜的鸟哥不是同一个人哦)

未分类

从图片中可以看出,PHP内部从下到上分为4层:

  • 负责 PHP 的执行的 Zend 引擎
  • Extensions 扩展层,各种基础的库和扩展都在这一层实现
  • SAPI:Server Application Programming Interface,服务端应用编程接口,它通过一些钩子函数,定义 PHP 与外部应用的交互,通过它可以实现 PHP 与上层应用的隔离,我们可以基于SAPI编写不同的应用适应不同的环境。
  • Application 层,这里代表了PHP应用的部分,如命令行下的脚本的执行,web服务器的脚本的执行等等

在这篇文章中,我们的关注点主要是在 Application 层与 SAPI 层的部分,理解了这些的话具体的部署自然就是水到渠成了。

对PHP的架构有了一些印象之后,我们可以知道,SAPI是一种不同的应用与PHP内核的交互方式,上层的应用通过SAPI定义的接口把代码和执行需要的环境变量,输入输出等数据交给PHP内核解析。

下面是一些常见的 SAPI 的应用实现:

  • Shell CLI: 通过命令行执行 PHP 程序用到的 SAPI。
  • Apache 2.0 Handler: 通过 Apache 服务器的 mod_php 模块部署 PHP 服务的运行方式
  • PHP 自带的 CGI/FastCGI 接口: PHP 本身实现了一个名为 php-cgi 的程序,它有 CGI、FastCGI 两种工作模式,专门处理 CGI/FastCGI 的请求
  • PHP-FPM: 这是一个 PHP 专用的 fastcgi 管理器,克服了 php-cgi 本身的一些问题,并且附加了许多适合大流量高并发网站的功能

早期的 PHP 为了适配多种多样的Web服务器环境,内置了许许多多的 SAPI ,到PHP 7以后,只保留了一部分重要的 SAPI,其它的都已经移除,下面是 PHP 7 以后移除的SAPI列表:(来自菜鸟教程)

aolserver, apache, apache_hooks, apache2filter, caudium, continuity, isapi, milter, nsapi, phttpd, pi3web, roxen, thttpd, tux, webjames

通过PHP的 php_sapi_name() 函数 我们可以获得当前PHP运行所使用的sapi的名字,实际的 PHP 代码编写中,我们可以根据这个函数的值判断程序所处的运行环境。

由于 SAPI 的多样,所以就有了许多不同的 PHP 部署方式,下面我会介绍一些常见的部署方式。

通过加载 Module 方式部署 PHP

Web 服务器除了可以通过 CGI 执行动态脚本外,还可以通过加载模块的方式来运行动态脚本,例如 Apache 的环境中是通过 mod_php 模块来实现运行PHP的,它利用了 Apache 2.0 Handler 这个 SAPI 与 PHP 解释器内核通信。

通过 Apache + mod_php 来部署 PHP 具有开箱即用,稳定成熟的特点,同时也有一些缺点:

  1. Web 服务器与 PHP 解释器之间是耦合的,程序出问题的时候不好定位是 Apache 的问题还是 PHP 这一层的问题
  2. 由于PHP的执行用户是与 Apache 相同的,这某些情况下可能有安全隐患
  3. 这种方式对于高并发大流量的场景下的性能消耗较大

所以我个人不太推荐通过这种方式在实际生产环境中部署PHP,当然,本地的开发环境还是挺适合的,尤其是Windows环境下。

FastCGI

上文提到了 FastCGI,它究竟是何方神圣呢?

首先我们回顾一下刚刚提到的CGI,它每次接到请求的时候,都需要根据请求的信息设置好参数,创建一个新的进程处理这个请求,处理完毕后退出程序,又叫 fork-and-execute 模式。进程的创建和销毁是一个耗费资源的过程,当系统的并发量一大,基于CGI的程序就撑不住了。

顾名思义,FastCGI = Fast + CGI,它是一种为了提高 CGI 程序性能的协议,是早期的 CGI 协议的改进版本。

参考 http://www.php-internals.com/book/?p=chapt02/02-02-03-fastcgi 的介绍,引用如下:

FastCGI是Web服务器和处理程序之间通信的一种协议, 是CGI的一种改进方案,FastCGI像是一个常驻(long-lived)型的CGI, 它可以一直执行,在请求到达时不会花费时间去fork一个进程来处理(这是CGI最为人诟病的fork-and-execute模式)。 正是因为他只是一个通信协议,它还支持分布式的运算,所以 FastCGI 程序可以在网站服务器以外的主机上执行,并且可以接受来自其它网站服务器的请求。

FastCGI 是与语言无关的、可伸缩架构的 CGI 开放扩展,将 CGI 解释器进程保持在内存中,以此获得较高的性能。 CGI 程序反复加载是 CGI 性能低下的主要原因,如果 CGI 程序保持在内存中并接受 FastCGI 进程管理器调度, 则可以提供良好的性能、伸缩性、Fail-Over 特性等。

FastCGI 工作流程如下:

  1. FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程,并等待来自 Web Server 的连接。
  2. Web 服务器与 FastCGI 进程管理器进行 Socket 通信,通过 FastCGI 协议发送 CGI 环境变量和标准输入数据给 CGI 解释器进程。
  3. CGI 解释器进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。
  4. CGI 解释器进程接着等待并处理来自 Web Server 的下一个连接。

未分类

FastCGI 与传统 CGI 模式的区别之一则是 Web 服务器不是直接执行 CGI 程序了,而是通过 Socket 与 FastCGI 响应器(FastCGI 进程管理器)进行交互,也正是由于 FastCGI 进程管理器是基于 Socket 通信的,所以也是分布式的,Web 服务器可以和 CGI 响应器服务器分开部署。Web 服务器需要将数据 CGI/1.1 的规范封装在遵循 FastCGI 协议包中发送给 FastCGI 响应器程序。

相比 CGI,这里又多了个需要我们考虑的程序 —— FastCGI 进程管理器。还有一个通信协议——FastCGI 协议。同时,HTTP 请求也不是 Web 服务器自己处理了,而是封装成 FastCGI 数据包发送到负责 FastCGI 的服务器。

PHP 本身实现了一个名为 php-cgi 的程序,它有 CGI、FastCGI 两种工作模式,专门处理 CGI/FastCGI 的请求。

PHP-FPM

刚刚我们有提到,PHP 可以通过内置的 php-cgi 程序的 FastCGI 模式实现 FastCGI 进程管理器的功能,解决了 CGI 的资源占用和并发的性能问题,同时也支持了分布式计算。然而,在需求日益增长的时候,php-cgi也暴露出了一些问题。最大的问题是,php-cgi 的配置不够人性化,主要体现在其修改 php.ini 后,不支持平滑重启,每次都要先停止服务再启动才能更新配置,这在某些场景下显然是很致命的。

为了解决上面的问题,一位名叫 Andrei Nigmatulin 的大神开发了 PHP-FPM,打破了这个尴尬的局面。

PHP-FPM 是一个为 PHP 量身打造的专用 FastCGI 进程管理器,它解决了上面的问题,并且在许多方面表现十分出色,因此在 PHP 5.3 版本以后,PHP-FPM 已经正式内置在 PHP 中了。官网关于 PHP-FPM 的介绍

综上,需要部署 PHP 环境的话,Apache/Nginx + PHP-FPM 是优于CGI 和 Module 加载的一个很好的选择,下面我就以 Nginx 为例,介绍一下 Nginx 和 PHP-FPM 的配置方法。

Nginx与PHP-FPM的配置

首先我们得装好 Nginx,PHP 和 PHP-FPM,具体安装过程可以参考其它的教程。

由于不同的发行版的安装后的文件路径不太一样,所以这里只会提到一些比较关键的配置部分。

我们需要明确 Nginx、PHP-FPM 各自的角色,Nginx 本身可以是一个提供静态文件分发的Web服务器、也可以是一个反向代理服务器,它的工作模式十分灵活,取决于我们怎么配置Nginx。在这里我的预期是,当 Nginx 收到请求以后,如果请求的是静态文件,那么将这个静态文件返回;如果它是一个要执行 PHP 程序的请求,Nginx 需要将其转发到 PHP-FPM 处理,PHP-FPM 收到请求以后,调用 PHP 内核执行 PHP 脚本,把脚本的输出返回给 Nginx,Nginx 再把响应通过 HTTP 响应的方式返回给用户。

上文我们提到了,FastCGI 的请求、响应是遵循 FastCGI 协议的,而 Web 服务器处理的是 HTTP 协议,所以 Nginx 与 PHP-FPM 一起工作时,就涉及到了 HTTP 协议的请求、响应与 FastCGI 协议的请求、响应的互相转换。这个转换的工作,是通过 Nginx 内置的 fastcgi 模块来实现的。所以,我们需要解决的问题是,如何配置 Nginx,调用 fastcgi 模块来让需要执行PHP的请求正确地转发到 PHP-FPM 中运行呢?

这里我们需要关注两个配置文件,一个是 Nginx 的 nginx.conf ,另一个是 PHP-FPM 的 php-fpm.conf

PHP-FPM 的配置文件

首先是 php-fpm.conf,这是 PHP-FPM 主要的配置文件,不同的安装方式的路径可能不一样,用 Ubuntu 16.04 的 apt 安装的 PHP-FPM,路径位于 /etc/php/7.0/fpm/php-fpm.conf ,如果是手动编译安装的话,假设 prefix 是 /usr/local/php,它一般会位于 /usr/local/php/etc/php-fpm.conf 。

php-fpm.conf 里面默认是 PHP-FPM 的基本配置,这里一般没有多少需要改动的地方,我们的目光放在最后一行的 include=xxxxx ,apt 安装的 PHP-FPM 默认是 include=/etc/php/7.0/fpm/pool.d/.conf ,手动编译安装的是 include=/usr/local/php/etc/php-fpm.d/.conf ,顾名思义,这是一个 include 操作,就是引入 pool.d 或者是 php-fpm.d 目录下所有的 .conf 文件的意思。这个目录下的文件主要的功能是定义了 php-fpm 监听端口等的信息。

定位到 pool.d (也可能是 php-fpm.d ) 目录下可以发现,它里面一般只有一个 www.conf,打开里面的内容,我们可以看到,里面每个配置项前面,都有一大堆详细的注释。这个文件是我们要配置 PHP-FPM 如何处理 PHP 的关键,它定义了 PHP-FPM 监听哪个端口或是 unix socket 的 FastCGI 请求,脚本执行环境的用户,用户组,权限等等。

一般如果对权限没有特殊要求的话我们不需要对它进行修改。这里我们需要记下里面 listen 选项的内容,listen 的值可以分为两种,一种是 TCP socket 地址,另一种是 unix socket 地址。如果我们这里看到的 listen 的值可能是 127.0.0.1:9000,它是 TCP socket,如果它是一个具体的文件路径类似 /run/php/php7.0-fpm.sock 的值,那么它是一个 unix socket,unix socket 是一种进程间的通信方式,它的操作类似网络套接字,但它实际的数据传输是不用经过网络层的,具体的内容可以查找相关的资料。

Nginx.conf 的配置

找到了 PHP-FPM 监听的 socket 之后,我们下一个目标就是配置 Nginx 让 .php 的请求转发到这个 socket 上了。

关于 Nginx 配置,推荐阅读官方文档 https://www.nginx.com/resources/admin-guide/nginx-web-server/#virtual-server 下面解释几个关键的部分

一般来说 Nginx 配置的基本结构是这样的,把 Nginx 用作 Web 服务器,则需要配置 nginx.conf 中的 http 块:

http {
    server {
        # Server configuration
        location / {

        }
        location ~ .php$ {

        }
    }
}

一个 http 块中可以有多个 server 块,每个 server 块代表了一个 Web 服务器,负责不同的域名、端口的请求的处理。在 server 块中,我们还可以配置不同的 location 块,每个 location 块都设置了它所匹配的 request URI 规则,符合规则的 request URI 将会跳到相应的 location 块中来处理。

location 语句块

利用 location 的强大功能,我们可以把所有 request URI 后缀为 .php 的请求交给一个 location 来处理,查询 Nginx 文档中关于 location 的描述,我们可以发现,location 的匹配 request URI 有两种方式:前缀匹配(prefix)和正则表达式匹配。.php 是后缀,所以需要正则表达式来处理,这里的正则表达式是 .php$ location 后的~ 代表匹配规则是一个正则表达式。

# 匹配 .php 后缀的 location
location ~ .php$ {

}

fastcgi 模块

启用 fastcgi 转发,需要用到 fastcgi 模块,在 location 块中配置与 fastcgi 相关的指令就可以了,官方文档对 fastcgi 模块的各个配置指令有很详细的介绍 http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html

一般我们使用的时候,我们只需用到 fastcgi 模块的其中两条指令,分别是 fastcgi_pass, fastcgi_param.

  • fastcgi_pass 这个参数是最最关键的,设置请求通过 fastcgi 协议转发的地址,需要设置为我们刚刚找到的 PHP-FPM 监听的地址。具体用法参考官网文档
  • fastcgi_param 这条指令描述了请求的一些关键参数的设定,比如要执行的脚本文件路径,请求的 QueryString,请求方法(GET还是POST),请求是否为 HTTPS 等等。详情继续参考官网文档

fastcgi_params

fastcgi_param 一般的用法如下

fastcgi_param parameter value [if_not_empty];

parameter 为当前 fastcgi 协议请求的参数名,value 为 Nginx 要设定此参数的参数值。这个value可以是一个固定的值,也可以是一个变量。我们可以根据实际的需要,来设置不同的 paramter 的参数值 http://nginx.org/en/docs/varindex.html

parameter 中有一个参数是最关键的,它就是 SCRIPT_FILENAME,这个参数定义了这个请求让 PHP-FPM 运行的 php 文件的完整路径,如果没有它,PHP-FPM 就不知道该运行什么脚本,将会返回一个内容为空白的 200 响应(https://nginx.org/en/docs/beginners_guide.html#fastcgi)。(实测在 PHP 7 环境中缺少 REQUEST_METHOD 也会有这种情况)

这个最关键的参数一般是这么设置的,意思是把 SCRIPT_FILENAME 设置为之前用 root 或 alias 设定的路径 + 脚本的相对路径:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

举个例子,假如当前的网站根路径设置为 /var/www/html,我们访问的 .php 文件的地址是 http://example.com/test/test.php ,那么,这时候的 $document_root 的值为 /var/www/html ,$fastcgi_script_name 的值就是 /test/test.php 了。此时的 SCRIPT_FILENAME 将会被设置为 /var/www/html/test/test.php ,PHP-FPM 就会按照这个路径读取 php 代码了。

特殊情况下,我们可以直接把一个确定的路径代替 $document_root:

fastcgi_param  SCRIPT_FILENAME    /var/www/html$fastcgi_script_name;

除了最关键的参数外还有一系列的参数需要设置,参考 Nginx 的文档 PHP FastCGI Example | NGINX

大部分时候,fastcgi_param 的设置是固定的,由于太多人瞎粘贴配置,各种混乱,所以 Nginx 后来为我们提供了一个现成的 fastcgi_param 配置的集合,位于 nginx.conf 同目录的 fastcgi_params 和 fastcgi.conf 中,fastcgi_params 的内容如下:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

所以,我们在配置 location 时,在 fastcgi_param 的设定上,我们设置完最关键的 SCRIPT_FILENAME 以后,只需要直接 include fastcgi_params; 就能完成任务了,下面是一个 location 配置 PHP 的例子。

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

这时候又衍生出了一个问题,fastcgi.conf 是做什么的呢?

fastcgi.conf 和 fastcgi_params 的内容相比,多了刚刚我们提到的最最关键的一行,其它完全一致。

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

因为我们平常配置 PHP 环境的时候,直接指出文件的根路径反而会造成许多麻烦,配置文件也不灵活,所以Nginx 为了缓解这种情况,引入了一个 fastcgi.conf 来作为最一般的配置。

有了 fastcgi.conf ,我们的配置又可以更简单一些了~

location ~ .php$ {
    fastcgi_pass 127.0.0.1:9000;
    include fastcgi.conf;
}

关于 fastcgi_params 和 fastcgi.conf 的关系,可以参考这篇文章 FASTCGI_PARAMS VERSUS FASTCGI.CONF – NGINX CONFIG HISTORY

一些安全的因素

参考 https://huoding.com/2013/10/23/290 火丁笔记 的描述,我们还需要在 nginx 这一层判断一下访问的 PHP 文件是否存在,避免出现因为 php.ini 开启了 cgi.fix_pathinfo=1 导致任意后缀文件都能通过 PHP 解释器解析产生的可能引发的安全问题,虽然这个漏洞在高版本 PHP (>=5.3.9) 已经被补上,但是还是需要注意。这时候我们可以在负责 php 的 location 块增加一个 try_files 来解决。修改后的配置如下:

location ~ .php$ {
    try_files $uri =404;
    fastcgi_pass 127.0.0.1:9000;
    include fastcgi.conf;
}

PATH_INFO 的配置

许多著名的 PHP 程序、框架如 WordPress、Typecho、Moodle、ThinkPHP 等,会支持形如 /xxxx.php/archives/2333 的 request URI,比如说,Moodle许多文件的路径就是 http://xxx.com/lib/javascript.php/1512059879/lib/requirejs/jquery-private.js 这种类型。这样的URL看起来比较神奇,仿佛 php 文件就是一个文件夹一样,看起来也更加友好一些。

一般 PHP 程序要处理这样的 request URI ,是通过超全局变量 $_SERVER 的一个参数 $_SERVER[‘PATH_INFO’] 来实现的,这是一个很实用的参数,PHP 文档对它的描述如下:

包含由客户端提供的、跟在真实脚本名称之后并且在查询语句(query string)之前的路径信息,如果存在的话。例如,如果当前脚本是通过 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar 被访问,那么 $_SERVER[‘PATH_INFO’] 将包含 /some/stuff。

我们可以看见,PATH_INFO 的信息必须是“客户端提供的”,也就是说需要由 Web 服务器提供,并且它的内容是跟在脚本名称后,在查询语句(QueryString)前的路径信息。Nginx 默认不会提供 PHP_INFO,因此,如果需要这个功能,我们需要为 Nginx 的 fastcgi_param 设置关于 PATH_INFO 的信息。

首先第一步我们要知道,面对 /xxx.php/xxxx 这样的链接,其实 Nginx 会把它当做一个文件夹来解析,而我们之前的配置是必须保证 request URI 是 .php 结尾的,所以我们第一个目标,就是把 /xxx.php/xxxx 能交给处理 PHP 的 location 块。我们变通一下,增加一种匹配 xxx.php/ 的情况:

至于为什么 .php 后一定要有 / 或者是 $(代表文件结尾),主要是考虑到一种风险,有的网站会有上传功能,若用户上传了一个 xxx.php.jpg ,配置不当的时候可能导致这个 xxx.php.jpg 传入 PHP 解释器,产生挂马的可能性。

location ~ .php(/|$) {

}

这时候请求已经可以转到这个 location 来处理了,但是,这时候,对于 /xxx.php/xxx,Nginx 转发给 PHP-FPM 的 fastcgi 请求中,SCRIPT_FILENAME 就成了 /xxx.php/xxx 。PHP 实际执行脚本的路径就需要依靠 PHP 内部去解析了,而且 PHP 并不知道 PATH_INFO 是什么。所以我们需要进一步操作,把正确的 SCRIPT_FILENAME 和 PATH_INFO 交给 PHP ,这里就用到了 Nginx 的 fastcgi_split_path_info 了。

fastcgi_split_path_info 在 Nginx 官方文档的描述是这样的 : http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info

Defines a regular expression that captures a value for the $fastcgi_path_info variable. The regular expression should have two captures: the first becomes a value of the $fastcgi_script_name variable, the second becomes a value of the $fastcgi_path_info variable. For example, with these settings
location ~ ^(.+.php)(.*)$ {
    fastcgi_split_path_info       ^(.+.php)(.*)$;
    fastcgi_param SCRIPT_FILENAME /path/to/php$fastcgi_script_name;
    fastcgi_param PATH_INFO       $fastcgi_path_info;

and the “/show.php/article/0001” request, the SCRIPT_FILENAME parameter will be equal to “/path/to/php/show.php”, and the PATH_INFO parameter will be equal to “/article/0001”.

使用 fastcgi_split_path_info ,需要设置一个含有两对括号正则表达式,第一个括号匹配的值将放入 $fastcgi_script_name 变量中,第二个括号的值是 $fastcgi_path_info 变量中,结合之前提到的 fastcgi_param 设置,SCRIPT_FILE_NAME 就已经正确设置啦,不用劳烦 PHP 去解析了,同时,我们再设置一个 fastcgi_param PATH_INFO 就能把 PATHINFO 设置好了。

这种情况的 location 配置如下:

location ~ .php(/|$) {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_split_path_info ^(.+?.php)(.*)$; #加入 ? 是为了避免类似 /test.php/t.php 匹配出错
    fastcgi_param PATH_INFO $fastcgi_path_info;
    include fastcgi.conf;
}

但这个配置还有一些问题,它没有考虑当访问请求的PHP不存在的时候的情况,访问文件不存在的 PHP 请求还是交给了 PHP-FPM 处理,仍然存在一些风险,一般是可以忽略不计的,但是为了完美还是要改进改进。

之前的配置我们用了 try_files $uri =404; 由于这时候的 request URI 并没有一个文件与之对应,所以使用 try_files $uri =404; 的话,肯定是直接返回 404 Not Found 。或许我们会想,request URI 没有对应的文件,那我们改成 try_files $fastcgi_script_name =404; 不就好啦!

这时候的配置类似这样,值得注意的是,Nginx 的配置并不会像一门编程语言一样有先后执行顺序,经过测试,fastcgi_split_path_info 有着更高的优先级,所以 try_files 不管加在哪一个位置,都能得到正确的结果,这里放在 fastcgi_split_path_info 后面是为了符合我们的直觉。

location ~ .php(/|$) {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_split_path_info ^(.+?.php)(.*)$;
    try_files $fastcgi_script_name =404;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    include fastcgi.conf;
}

但这样的配置又衍生出了新的问题,使用这个配置的时候,虽然 PHP 可以正常执行,但PHP脚本是获取不到 PATH_INFO 信息,这是为什么呢?

我们需要理解一下 try_files 指令的执行流程。根据 Nginx 关于 try_files 的文档 :

Checks the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context. The path to a file is constructed from the *file* parameter according to the root and alias directives. It is possible to check directory’s existence by specifying a slash at the end of a name, e.g. “$uri/”. If none of the files were found, an internal redirect to the *uri* specified in the last parameter is made.

try_files 会依次检测传入的参数对应的文件路径是否存在对应的文件,若存在,则按参数所在 location 对应的 request URI 来处理,正如我标注的第一句加粗字体所述,try_files 的处理是取决于这个语句所在的上下文的,当这个 location 设置了 fastcgi_pass ,则这个请求会交给 fastcgi 模块发到后端 PHP-FPM 处理,如果这个 location 内部什么都没有,则会按照默认的文件读取返回来处理;如果前面所有的参数都不匹配的话,则会跳转到最后一个参数描述的内部重定向请求,跳到其他的 location 或返回状态码。

参考科大LUG老板踩过的 https://servers.ustclug.org/2014/09/nginx-try_files-fallacy/

回到刚刚的 try_files $fastcgi_script_name =404; 这里首先尝试的是 $fastcgi_script_name ,也就是说,跳到这一步以后,交回给这个 location 处理的 request URI 变成了 $fastcgi_script_name 这个变量所代表的,到了 .php 以后就结束。原本的 PATH_INFO 已经丢失了,这时候再 fastcgi_split_path_info 显然,$fastcgi_pathinfo 的值也变成了空白。

但是,我们第一步我们已经拿到了正确的 PATH_INFO 了,我们有没有什么办法可以把这个变量临时保存下来而不是在下一次匹配的时候就被覆盖掉呢?答案当然是有的~

这里就需要用到 Nginx 的 rewrite 模块的变量机制了,关于变量,我找到一篇讲解十分详细的文章 Nginx 变量漫谈(一)agentzh新浪博客http://blog.sina.com.cn/s/blog_6d579ff40100wi7p.html

这里用到的指令是 set ,变量需要注意的一点是,变量设置以后,变量名的可见范围是整个 Nginx 配置,在不同的请求中,变量值是独立的。所以,我们可以定义一个变量取名为 $real_path_info ,用来暂时存放这个值,增加了 set 指令以后的配置如下:

location ~ .php(/|$) {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_split_path_info ^(.+?.php)(.*)$;
    set $real_path_info $fastcgi_path_info;
    try_files $fastcgi_script_name =404;
    fastcgi_param PATH_INFO $real_path_info;
    include fastcgi.conf;
}

有些通用的程序为了兼容各种各样的服务器主机环境,它会使用多种不同手段来获取 .php 后面还存在的内容,所以它们也不一定用到 PATH_INFO 的方式,可能不需要配置得那么详细,但不代表我们不需要知道这些。只有在了解这些的情况下,遇到故障,我们也可以更容易更好地排查问题所在。

针对单入口程序的设置

许多 PHP 框架如 Laravel 采用了统一入口的方式,即它是通过 index.php 作为程序的入口,当我把 index 设置为 index.php 以后, /archives/2333 和 /index.php/archives/2333 就是等价的了,显然前者更加美观一些,这样还能实现“URI路由”操作,让不同 controller 来处理不同的 request URI,也就是我们常说的“伪静态”啦。

那么,“伪静态”该怎么实现呢,这里也用到了 Nginx 的 try_files 指令,方法是加一个 location / 的块:

location / {
  try_files $uri $uri/ /index.php$is_args$args
}
location ~ .php$ {
  fastcgi_pass 127.0.0.1:9000;
  include fastcgi.conf;
}

当访问的文件或目录不存在时,程序将重定向到 /index.php 处理,后面的 $is_args$args 是因为重定向以后 QueryString 丢失了,需要加回来。

我们可以发现,按照这样的配置重定向以后,request URI 变成了 /index.php?xxxx 了, 传给 PHP 的 PATHINFO 信息也丢失了 (https://trac.nginx.org/nginx/ticket/321) ,所以一般这种单入口的程序或框架会有许多判断,一般这些程序会通过 REQUEST_URI 参数来获得 index.php 后的路径信息,实现路由。

需要把路径信息传递到 PATH_INFO 的话,还有一种方案,就是通过 rewrite 来实现,假若文件不存在,则在 request URI 之前加上 /index.php 再继续解析。

参考 Typecho 的源码 (https://github.com/typecho/typecho/blob/master/tools/default_site) :

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

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

    location ~ .php(/|$) {
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_split_path_info ^(.+?.php)(.*)$;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      include fastcgi.conf;
    }

}

这时候也不需要 try_files 了。

可以复制粘贴的 server 配置总结

注意把 fastcgi_pass 设定为服务器中 PHP-FPM 监听的连接。

如果只是想单纯地配一个 .php 的解析

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi.conf;
    }

}

如果想让脚本支持 PATH_INFO

类似 Moodle 的文件下载那种

如果程序是通过 REQUEST_URI 来获取路径而不需要 PATH_INFO 的话:

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

    location ~ .php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_split_path_info ^(.+?.php)(.*)$;
        try_files $fastcgi_script_name =404;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        include fastcgi.conf;
    }
}

如果一定要完整获取 PATH_INFO 可以用这个完美支持的版本:

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

    location ~ .php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_split_path_info ^(.+?.php)(.*)$;
        set $real_path_info $fastcgi_path_info;
        try_files $fastcgi_script_name =404;
        fastcgi_param PATH_INFO $real_path_info;
        include fastcgi.conf;
    }
}

如果部署的是一个单入口程序,并且对 PATH_INFO 没有要求

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

    location / {
          try_files $uri $uri/ /index.php$is_args$args
    }

    location ~ .php$ {
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi.conf;
    }

}

如果部署的是一个单入口程序,需要支持 PATH_INFO 的话

server {
    listen 80 default_server;
    server_name _;

    root /www;
    index index.html index.php;

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

    location ~ .php(/|$) {
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_split_path_info ^(.+?.php)(.*)$;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      include fastcgi.conf;
    }

}

关于 Nginx 配置文件的内部执行机理,博主目前还不懂=_=,有些问题也难以解释,现在只能是从外部的表现,官网文档一点点推敲和纠正,待博主有时间有能力搞清楚它的时候,博主会不断地修正这里的描述的,也希望能与各位高手多多交流完善。

以上是博主的一些理解与实践的经验,由于博主的水平有限,可能有一些地方的描述不太妥当,若你发现了本文有不妥甚至错误之处,希望可以尽快在评论区中指出。要深入地理解 Nginx + PHP 配置,还得多参考一下官方的文档、源代码和一些高质量的博客文章。

Linux下安装MongoDB的实现步骤

Linux下安装MongoDB的实现步骤

Mongo DB 是目前在IT行业非常流行的一种非关系型数据库(NoSql),其灵活的数据存储方式备受当前IT从业人员的青睐。Mongo DB很好的实现了面向对象的思想(OO思想),在Mongo DB中 每一条记录都是一个Document对象。Mongo DB最大的优势在于所有的数据持久操作都无需开发人员手动编写SQL语句,直接调用方法就可以轻松的实现CRUD操作。本文介绍了如何快速安装mongodb供大家参考。

一、安装配置mongodb

Step 1: 设置系统环境及确保缺省端口27107可用

###当前环境
# cat /etc/issue
Red Hat Enterprise Linux Server release 6.5 (Santiago)

# vi /etc/selinux/config
SELINUX=disabled

Step 2: 下载安装文件

下载地址: https://www.mongodb.org/downloads. 
或者直接在命令提示符下使用curl命令下载
curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz

Step 3: 解压下载的文件

# pwd
/usr/local/src
# tar -xvf mongodb-linux-x86_64-rhel62-3.0.6.gz ###注,本文直接从网站下载,所以文件为.gz

Step 4: 复制解压文件到运行目录

# mkdir -p /var/lib/mongodb
# cp -R -n /usr/local/src/mongodb-linux-x86_64-rhel62-3.0.6/. /var/lib/mongodb/

Step 5: 设置环境变量

e.g. export PATH=<mongodb-install-directory>/bin:$PATH
# vi ~/.bash_profile 
 export PATH=/var/lib/mongodb/bin:$PATH
# source ~/.bash_profile

Step 6: 创建数据目录

# mkdir -p /data/mongodata

二、启动及验证mongodb

###启动mongo
# mongod --dbpath /data/mongodata

###以下内容为启动后输出的相关信息
2015-10-28T10:03:33.100+0800 I JOURNAL [initandlisten] journal dir=/data/mongodata/journal
2015-10-28T10:03:33.101+0800 I JOURNAL [initandlisten] recover : no journal files present, no recovery needed
2015-10-28T10:03:33.264+0800 I JOURNAL [initandlisten] preallocateIsFaster=true 2.18
2015-10-28T10:03:33.398+0800 I JOURNAL [durability] Durability thread started
2015-10-28T10:03:33.398+0800 I JOURNAL [journal writer] Journal writer thread started
2015-10-28T10:03:33.401+0800 I CONTROL [initandlisten] MongoDB starting : pid=10191 port=27017 dbpath=/data/mongodata 64-bit host=java_2
2015-10-28T10:03:33.401+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2015-10-28T10:03:33.401+0800 I CONTROL [initandlisten] 
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] 
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] **    We suggest setting it to 'never'
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] 
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] **    We suggest setting it to 'never'
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] 
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] db version v3.0.6
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] git version: 1ef45a23a4c5e3480ac919b28afcba3c615488f2
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] build info: Linux ip-10-67-194-123 2.6.32-220.el6.x86_64 #1 SMP Wed Nov 9 08:03:13 EST 2011 x86_64 BOOST_LIB_VERSION=1_49
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] allocator: tcmalloc
2015-10-28T10:03:33.402+0800 I CONTROL [initandlisten] options: { storage: { dbPath: "/data/mongodata" } }
2015-10-28T10:03:33.404+0800 I INDEX  [initandlisten] allocating new ns file /data/mongodata/local.ns, filling with zeroes...
2015-10-28T10:03:33.491+0800 I STORAGE [FileAllocator] allocating new datafile /data/mongodata/local.0, filling with zeroes...
2015-10-28T10:03:33.491+0800 I STORAGE [FileAllocator] creating directory /data/mongodata/_tmp
2015-10-28T10:03:33.497+0800 I STORAGE [FileAllocator] done allocating datafile /data/mongodata/local.0, size: 64MB, took 0.001 secs
2015-10-28T10:03:33.511+0800 I NETWORK [initandlisten] waiting for connections on port 27017

###停止mongo,直接使用ctrl + c
^C2015-10-28T10:09:21.510+0800 I CONTROL [signalProcessingThread] got signal 2 (Interrupt), will terminate after current cmd ends
2015-10-28T10:09:21.511+0800 I CONTROL [signalProcessingThread] now exiting
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] shutdown: going to close listening sockets...
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] closing listening socket: 5
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] closing listening socket: 6
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] removing socket file: /tmp/mongodb-27017.sock
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] shutdown: going to flush diaglog...
2015-10-28T10:09:21.511+0800 I NETWORK [signalProcessingThread] shutdown: going to close sockets...
2015-10-28T10:09:21.512+0800 I STORAGE [signalProcessingThread] shutdown: waiting for fs preallocator...
2015-10-28T10:09:21.512+0800 I STORAGE [signalProcessingThread] shutdown: final commit...
2015-10-28T10:09:21.512+0800 I JOURNAL [signalProcessingThread] journalCleanup...
2015-10-28T10:09:21.512+0800 I JOURNAL [signalProcessingThread] removeJournalFiles
2015-10-28T10:09:21.515+0800 I JOURNAL [signalProcessingThread] Terminating durability thread ...
2015-10-28T10:09:21.615+0800 I JOURNAL [journal writer] Journal writer thread stopped
2015-10-28T10:09:21.615+0800 I JOURNAL [durability] Durability thread stopped
2015-10-28T10:09:21.615+0800 I STORAGE [signalProcessingThread] shutdown: closing all files...
2015-10-28T10:09:21.618+0800 I STORAGE [signalProcessingThread] closeAllFiles() finished
2015-10-28T10:09:21.618+0800 I STORAGE [signalProcessingThread] shutdown: removing fs lock...
2015-10-28T10:09:21.618+0800 I CONTROL [signalProcessingThread] dbexit: rc: 0

###修复启动过程中的两个警告,关于使用root用户启动mongo的警告先忽略
# echo "never" > /sys/kernel/mm/transparent_hugepage/enabled
# echo "never" > /sys/kernel/mm/transparent_hugepage/defrag

###再次重启,后置于后台进程,
# mongod --dbpath /data/mongodata &

###查看启动后的进程
# ps -ef|grep mongo |grep -v grep
root   11115 27956 0 10:11 pts/2  00:00:00 mongod --dbpath /data/mongodata
# lsof -i:27017
COMMAND  PID USER  FD  TYPE  DEVICE SIZE/OFF NODE NAME
mongod 11115 root  5u IPv4 50567119   0t0 TCP *:27017 (LISTEN)

###使用mongo连接到mongod
# mongo
MongoDB shell version: 3.0.6
connecting to: test
2015-10-28T10:14:30.685+0800 I NETWORK [initandlisten] connection accepted from 127.0.0.1:53907 #1 (1 connection now open)
Server has startup warnings: 
2015-10-28T10:11:49.217+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2015-10-28T10:11:49.217+0800 I CONTROL [initandlisten] 
> help
    db.help()          help on db methods
    db.mycoll.help()       help on collection methods
    sh.help()          sharding helpers
    rs.help()          replica set helpers
    help admin          administrative help
    help connect         connecting to a db help
    help keys          key shortcuts
    help misc          misc things to know
    help mr           mapreduce


    show dbs           show database names
    show collections       show collections in current database
    show users          show users in current database
    show profile         show most recent system.profile entries with time >= 1ms
    show logs          show the accessible logger names
    show log [name]       prints out the last segment of log in memory, 'global' is default
    use <db_name>        set current database
    db.foo.find()        list objects in collection foo
    db.foo.find( { a : 1 } )   list objects in foo where a == 1
    it              result of the last line evaluated; use to further iterate
    DBQuery.shellBatchSize = x  set default number of items to display on shell
    exit             quit the mongo shell
> db.getCollection("version");
test.version
> exit
bye

三、mongodb相关工具

###在安装文件下有README,描述了常用的mongodb相关命令行工具
# more /usr/local/mongodb/README 
MongoDB README
Welcome to MongoDB!
COMPONENTS

 bin/mongod - The database process.
 bin/mongos - Sharding controller.
 bin/mongo - The database shell (uses interactive javascript).

UTILITIES

 bin/mongodump     - MongoDB dump tool - for backups, snapshots, etc..
 bin/mongorestore   - MongoDB restore a dump
 bin/mongoexport    - Export a single collection to test (JSON, CSV)
 bin/mongoimport    - Import from JSON or CSV
 bin/mongofiles    - Utility for putting and getting files from MongoDB GridFS
 bin/mongostat     - Show performance statistics

RUNNING

 For command line options invoke:

  $ ./mongod --help

 To run a single server database:

  $ mkdir /data/db
  $ ./mongod
  $
  $ # The mongo javascript shell connects to localhost and test database by default:
  $ ./mongo 
  > help

DRIVERS

 Client drivers for most programming languages are available at mongodb.org. Use the 
 shell ("mongo") for administrative tasks.

###获取单个命令用法
# <command> --help
# mongod --help|more
Options:

General options:
 -h [ --help ]        show this usage information
 --version          show version information

# mongod --version
db version v3.0.6
git version: 1ef45a23a4c5e3480ac919b28afcba3c615488f2

linux磁盘管理系列三:LVM的使用

LVM是什么

LVM是Linux操作系统的逻辑卷管理器。 现在有两个Linux版本的LVM,分别是 LVM1,LVM2。LVM1是一种已经被认为稳定了几年的成熟产品,LVM2 是最新最好的LVM版本。 LVM2几乎完全向后兼容使用LVM1创建的卷。此例外是快照(在升级到LVM 2之前,必须删除快照卷)。

LVM简介

逻辑卷管理提供了比传统的磁盘和分区视图更高级别的计算机系统上磁盘存储的视图。 这使得系统管理员可以更灵活地将存储分配给应用程序和用户。

在逻辑卷管理器的控制下创建的存储卷可以随意调整大小和移动,尽管这可能需要对文件系统工具进行一些升级。

逻辑卷管理器还允许管理用户定义组中的存储卷,允许系统管理员处理明显命名的卷组(如“development”和“sales”),而不是物理磁盘名称,例如“sda”和“sdb”

LVM基本术语

volume group (VG)

卷组是LVM中使用的最高级别的抽象。 它将逻辑卷和物理卷集合在一起成为一个管理单元。

physical volume (PV)

一个物理卷通常是一个硬盘,虽然它可能只是一个看起来像一个硬盘(如软件RAID设备)的设备。

logical volume (LV)

相当于非LVM系统中的磁盘分区。 LV作为标准块设备可见; 因此LV可以包含文件系统(例如/ home)。

physical extent (PE)

每个物理卷都被划分为数据块(称为物理扩展盘区),这些扩展盘区与卷组的逻辑盘区具有相同的大小。

logical extent (LE)

每个逻辑卷都被分割成数据块,称为逻辑盘区。 卷组中的所有逻辑卷的区段大小都是相同的。

几个概念的关系

一个具体的例子将有助于:

假设我们有一个名为VG1的卷组,这个卷组的物理盘区大小为4MB。 在这个卷组中,我们引入了2个硬盘分区/dev/hda1和/dev/hdb1。
这些分区将成为物理卷PV1和PV2(管理员可以自行决定是否提供更有意义的名称)。
PV被分成4MB块,因为这是卷组的大小。磁盘大小不同,我们在PV1中获得99个扩展盘区,在PV2中获得248个扩展盘区。
我们现在可以创建自己的逻辑卷,它可以是1到347(248 + 99)范围之间的任何大小。
当创建逻辑卷时,在逻辑盘区和物理盘区之间定义映射,例如, 逻辑盘区1可以映射到PV1的物理盘区51上,写入逻辑盘的第一个4MB的数据实际上被写入到PV1的第51盘区。

映射模式

线性映射 : 将分配一定范围的PE到LV的一个区域,例如LE 1 – 99映射到PV1,LE 100 – 347映射到PV2。

分条映射 : 将跨多个物理卷交织逻辑盘区的块,这个方式一定程度上提高了性能,具体如下

  1st chunk of LE[1] -> PV1[1], #第一个逻辑块在PV1设备上, 第二个在PV2设备上,等等。
  2nd chunk of LE[1] -> PV2[1],
  3rd chunk of LE[1] -> PV3[1],
  4th chunk of LE[1] -> PV1[2],

Snapshots(快照):

这是LVM提供的一个非常棒的功能就是快照了,他允许管理员创建一个新的块设备来呈现一个逻辑卷的的精确副本,在某个时间冻结,管理员可以及时备份指定一致状态下的数据,备份完毕就可以删除设备了。

快照最初包含有关快照的一些元数据,但没有来自源逻辑卷的实际数据。快照使用写时复制(copy on write)技术来检测数据在原始数据块中何时更改。它将拍摄快照时保存的值复制到快照卷中的块中,然后允许将新数据存储在源块中。

随着更多块从源逻辑卷上的原始值发生更改,快照大小也会增加。如果快照卷变满,它将被丢弃,所以分配足够的空间是非常重要的,具体取决于你数据的变换情况,如果快照大小和原始数据一样大,它将永远不会溢出的。

安装LVM

[root@centos7 ~]$ rpm -q lvm2                           # 查看lvm2是否安装了, 我使用的centos7,这是安装的了
lvm2-2.02.171-8.el7.x86_64
[root@centos7 ~]$ # yum -y install lvm2                 # 没有安装的话使用yum安装

常用命令简介

[root@centos7 ~]$ pv                                                   # 使用tab按键获取pv相关命令
pvchange   pvck       pvcreate   pvdisplay  pvmove     pvremove   pvresize   pvs        pvscan
[root@centos7 ~]$ vg                                                   # 使用tabl按键获取vg相关的命令
vgcfgbackup    vgck           vgdisplay      vgimport       vgmknodes      vgrename       vgsplit
vgcfgrestore   vgconvert      vgexport       vgimportclone  vgreduce       vgs            
vgchange       vgcreate       vgextend       vgmerge        vgremove       vgscan         
[root@centos7 ~]$ lv                                                   # 使用tabl按键获取lv先关的命令
lvchange     lvdisplay    lvmconf      lvmdump      lvmsadc      lvremove     lvs          
lvconvert    lvextend     lvmconfig    lvmetad      lvmsar       lvrename     lvscan       
lvcreate     lvm          lvmdiskscan  lvmpolld     lvreduce     lvresize   

上面的命令比较多,简单介绍几个常用的命令。

  • pv打头的:代表pv相关的命令
  • vg带头的:代表vg相关的命令
  • lv带头的: 代表lv相关的命令
  • create:创建相关
  • remove:移除相关
  • display:显示相关
  • import:导入 相关
  • export:导出相关
  • rename:重命名
  • vgchange:改变状态相关
  • extend:扩展相关
  • reduce:缩进相关

使用LVM

pv创建

pv作用:初始化磁盘和磁盘分区

在将磁盘或者磁盘分区用作物理卷(PV)之前,你需要对其进行初始化工作。

[root@centos7 ~]$ pvcreate /dev/sdb1

注意:

  • 不建议对整个磁盘做PV初始化工作。因为其他操作系统不能识别LVM元数据,导致我们认为磁盘是空闲的,重新设置分区覆盖原有数据
  • 如果是分区需要调整分区的类型为LVM(代码为8e)
    分区并设置分区类型
[root@centos7 ~]$ fdisk -l

Disk /dev/sda: 214.7 GB, 214748364800 bytes, 419430400 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000b0b8a

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     2099199     1048576   83  Linux
/dev/sda2         2099200   270534655   134217728   83  Linux
/dev/sda3       270534656   372934655    51200000   83  Linux
/dev/sda4       372934656   419430399    23247872    5  Extended
/dev/sda5       372938752   413898751    20480000   83  Linux

Disk /dev/sdb: 107.4 GB, 107374182400 bytes, 209715200 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x93d380cf

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048     2099199     1048576   8e  Linux LVM
/dev/sdb2         2099200     4196351     1048576   8e  Linux LVM

Disk /dev/sde: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x73afb36f

   Device Boot      Start         End      Blocks   Id  System
/dev/sde1            2048     2099199     1048576   8e  Linux LVM
/dev/sde2         2099200     6293503     2097152   8e  Linux LVM

Disk /dev/sdd: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x7e0900d8

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1            2048     2099199     1048576   8e  Linux LVM
/dev/sdd2         2099200    12584959     5242880   8e  Linux LVM

Disk /dev/sdc: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0xc56b90d8

   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1            2048     2099199     1048576   8e  Linux LVM
/dev/sdc2         2099200     4196351     1048576   8e  Linux LVM

Disk /dev/sdf: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

[root@centos7 ~]$ partprobe 
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
[root@centos7 ~]$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0  200G  0 disk 
├─sda1   8:1    0    1G  0 part /boot
├─sda2   8:2    0  128G  0 part 
├─sda3   8:3    0 48.8G  0 part /
├─sda4   8:4    0  512B  0 part 
└─sda5   8:5    0 19.5G  0 part /app
sdb      8:16   0  100G  0 disk 
├─sdb1   8:17   0    1G  0 part 
└─sdb2   8:18   0    1G  0 part 
sdc      8:32   0   20G  0 disk 
├─sdc1   8:33   0    1G  0 part 
└─sdc2   8:34   0    1G  0 part 
sdd      8:48   0   20G  0 disk 
├─sdd1   8:49   0    1G  0 part 
└─sdd2   8:50   0    5G  0 part 
sde      8:64   0   20G  0 disk 
├─sde1   8:65   0    1G  0 part 
└─sde2   8:66   0    2G  0 part 
sdf      8:80   0   20G  0 disk 
sr0     11:0    1  8.1G  0 rom  /run/media/root/CentOS 7 x86_64

上面我们在/dev/sd{b,c,d,e}这4个盘每个都有2个类型为lvm的盘。接下来使用pvcreat初始化为物理卷

[root@centos7 ~]$ pvcreate /dev/sd{b1,b2,c1,c2,d1,d2,e1,e2} -y        # 这里加入了-y选项,之前的盘有文件系统的。 我这里强制创建了
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Wiping xfs_external_log signature on /dev/sdb2.
  Physical volume "/dev/sdb1" successfully created.
  Physical volume "/dev/sdb2" successfully created.
  Physical volume "/dev/sdc1" successfully created.
  Physical volume "/dev/sdc2" successfully created.
  Physical volume "/dev/sdd1" successfully created.
  Physical volume "/dev/sdd2" successfully created.
  Physical volume "/dev/sde1" successfully created.
  Physical volume "/dev/sde2" successfully created.

pv信息查看

[root@centos7 ~]$ pvs                                           # 详细信息可以使用pvdisplay查看
  PV         VG         Fmt  Attr PSize    PFree   
  /dev/sdb1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sdb2             lvm2 ---     1.00g    1.00g
  /dev/sdc1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sdc2             lvm2 ---     1.00g    1.00g
  /dev/sdd1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sdd2             lvm2 ---     5.00g    5.00g
  /dev/sde1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sde2             lvm2 ---     2.00g    2.00g

vg创建

vg的创建是比较简单的。

[root@centos7 ~]$ vgcreate  vg_test_01 /dev/sd{b1,c1,d1,e1}    # 上面我们创建了8个pv,这里我们使用4个1的设备去创建vg,创建后默认vg是激活状态的
  Volume group "vg_test_01" successfully created

vg信息的查看

[root@centos7 ~]$ vgs                                 # 查看简短的信息
  VG         #PV #LV #SN Attr   VSize VFree
  vg_test_01   4   0   0 wz--n- 3.98g 3.98g
[root@centos7 ~]$ vgdisplay                           # 查看详细的信息
  --- Volume group ---
  VG Name               vg_test_01                    # vg的名字
  System ID             
  Format                lvm2                          # lvm格式, 这里是lvm2的
  Metadata Areas        4
  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                4
  Act PV                4
  VG Size               3.98 GiB                       # 这是我们4个pv的总大小
  PE Size               4.00 MiB                       # pe的大小
  Total PE              1020
  Alloc PE / Size       0 / 0                          # 分配的大小,这里我们还没有创建lv,所以为0
  Free  PE / Size       1020 / 3.98 GiB                # 分配后剩余的空间,第一个数是剩余的个数,第二个数是剩余的空间大小,2个都是剩余的只是单位表示不一样而已
  VG UUID               Y5NjjP-e3Aq-3u6E-gw6P-fLyy-Y5pN-ryyN9R  # vg设备的uuid

vg的激活

[root@centos7 ~]$ vgchange -a y vg_test_01                               # 我们上面就是激活状态的,如果我们重启系统,或者vgchange -y n命令关闭了,就需要这个命令启动下
  0 logical volume(s) in volume group "vg_test_01" now active

vg的移除

[root@centos7 ~]$ vgchange -a n vg_test_01                       # 要想移除vg,需要先关闭vg才能移除,这里先关闭
  0 logical volume(s) in volume group "vg_test_01" now active
[root@centos7 ~]$ vgremove  vg_test_01                           # 移除vg,

vg的添加成员

[root@centos7 ~]$ vgcreate  vg_test_01 /dev/sd{b1,c1,d1,e1}       # 上面我把vg删了,这里重新添加进来
  Volume group "vg_test_01" successfully created
[root@centos7 ~]$ vgextend vg_test_01  /dev/sdb2                  # 添加一个pv2到vg中来
  Volume group "vg_test_01" successfully extended

vg的删除成员

这个操作需要使用vgdisplay查看vg的使用情况和删除的pv上没有数据。如果有使用pvmove迁移完毕数据才能移除。

[root@centos7 ~]$ vgreduce  vg_test_01  /dev/sdb2
  Removed "/dev/sdb2" from volume group "vg_test_01"

lv的创建

[root@centos7 ~]$ vgdisplay                        # lv逻辑卷创建之前, 需要查看下vg信息
  --- Volume group ---
  VG Name               vg_test_01
  System ID             
  Format                lvm2
  Metadata Areas        4
  Metadata Sequence No  3
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                4
  Act PV                4
  VG Size               3.98 GiB
  PE Size               4.00 MiB
  Total PE              1020
  Alloc PE / Size       0 / 0   
  Free  PE / Size       1020 / 3.98 GiB
  VG UUID               sZwHea-LabA-SJaV-Wz40-qFp3-Um2w-pvmJAY

[root@centos7 ~]$ lvcreate -L 2G  -n lv_test_01 vg_test_01                 # 我们的vg是近4G的,我这里创建一个2G的lv,之前磁盘使用了文件系统慈爱有如下警告。输入y即可。
WARNING: linux_raid_member signature detected on /dev/vg_test_01/lv_test_01 at offset 4096. Wipe it? [y/n]: y
  Wiping linux_raid_member signature on /dev/vg_test_01/lv_test_01.
WARNING: ext4 signature detected on /dev/vg_test_01/lv_test_01 at offset 1080. Wipe it? [y/n]: y
  Wiping ext4 signature on /dev/vg_test_01/lv_test_01.
  Logical volume "lv_test_01" created.

lv的移除

[root@centos7 ~]$ lvremove  /dev/vg_test_01/lv_test_01     
# 这个移除我们上面创建lv_test_01这个逻辑卷, 下面提示这个是活动的逻辑卷,输入y就可以了
Do you really want to remove active logical volume vg_test_01/lv_test_01? [y/n]: y
  Logical volume "lv_test_01" successfully removed

注意: 如果这个lv被挂载了。 请先umount

挂载文件系统

[root@centos7 ~]$ lvcreate -L 2G  -n lv_test_01 vg_test_01                             # 上面我们删除了lv,这里需要创建出来
  Logical volume "lv_test_01" created.
[root@centos7 ~]$ mkfs.ext4  /dev/vg_test_01/lv_test_01                                  # 这里我使用ext4 文件系统了。centos7默认使用的文件系统是xfs,但是xfs不支持缩减的

[root@centos7 ~]$ mkdir /mnt/test_01                                                    # 创建挂载点目录
[root@centos7 ~]$ mount /dev/vg_test_01/lv_test_01  /mnt/test_01/                       # 挂载逻辑卷

扩展LVM

上面我们的测试是vg是4G,我们的创建的lv是2G的,我们想给创建的lv扩展1G空间。

[root@centos7 ~]$ lvextend -L +1G /dev/vg_test_01/lv_test_01                 # 我们扩大1G, +1G表示在原有基础上扩大1G,我们这里是有vg是有空间的,没有的话就需要创建pv,扩大vg在做
  Size of logical volume vg_test_01/lv_test_01 changed from 2.00 GiB (512 extents) to 3.00 GiB (768 extents).
  Logical volume vg_test_01/lv_test_01 successfully resized.
[root@centos7 ~]$ df -h                                                      # 上面的命令提示lv已经到3G了, 但是使用df-h 查看还是2g
Filesystem                         Size  Used Avail Use% Mounted on
/dev/sda3                           48G  4.9G   41G  11% /
devtmpfs                           979M  4.0K  979M   1% /dev
tmpfs                              993M     0  993M   0% /dev/shm
tmpfs                              993M   22M  971M   3% /run
tmpfs                              993M     0  993M   0% /sys/fs/cgroup
/dev/sda1                          976M  130M  780M  15% /boot
/dev/sda5                           20G  307M   18G   2% /app
tmpfs                              199M   32K  199M   1% /run/user/0
/dev/sr0                           8.1G  8.1G     0 100% /run/media/root/CentOS 7 x86_64
/dev/mapper/vg_test_01-lv_test_01  2.0G   33M  2.0G   2% /mnt/test_01
[root@centos7 ~]$ resize2fs /dev/mapper/vg_test_01-lv_test_01              # 需要增长下文件系统的,让文件系统和lv大小匹配下
meta-data=/dev/mapper/vg_test_01-lv_test_01 isize=512    agcount=4, agsize=131072 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=524288, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal               bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 524288 to 786432
[root@centos7 ~]$ df -h                                                    # 再次查看就是一致的了
Filesystem                         Size  Used Avail Use% Mounted on
/dev/sda3                           48G  4.9G   41G  11% /
devtmpfs                           979M  4.0K  979M   1% /dev
tmpfs                              993M     0  993M   0% /dev/shm
tmpfs                              993M   22M  971M   3% /run
tmpfs                              993M     0  993M   0% /sys/fs/cgroup
/dev/sda1                          976M  130M  780M  15% /boot
/dev/sda5                           20G  307M   18G   2% /app
tmpfs                              199M   32K  199M   1% /run/user/0
/dev/sr0                           8.1G  8.1G     0 100% /run/media/root/CentOS 7 x86_64
/dev/mapper/vg_test_01-lv_test_01  3.0G   33M  3.0G   2% /mnt/test_01

注意:

  • ext系列可以使用resieze2fs去调整。其他的文件系统可以参考http://tldp.org/HOWTO/LVM-HOWTO/extendlv.html
      
  • lvextentd 有个-r选项,自动去调整大小,匹配lv和文件系统大小的。

缩减LVM

逻辑卷的大小可以减小,也可以增加。 但是,在缩小卷本身之前,请记住缩小文件系统的大小或存在于卷中的任何文件是非常重要的,否则可能会丢失数据。

注意: xfs,jsf文件系统是没法缩减lvm的,因为这2个文件系统没法缩减文件系统大小的。参考http://tldp.org/HOWTO/LVM-HOWTO/reducelv.html

主要步骤:

  1. 检查空间使用情况和分析
      
  2. umount设备
      
  3. 缩减文件系统大小
      
  4. 缩减lv大小
      
  5. 恢复挂载设备

上面我们的逻辑卷是3G的大小,在缩减之前我们需要确保缩减的空间不能影响数据的存放的。

检查工作

[root@centos7 ~]$ cp -a /etc/ /mnt/test_01/                              # 我这里模拟下,只能目录有文件的情况 
[root@centos7 ~]$ du -sh /mnt/test_01/                                   # 查看下大小
42M    /mnt/test_01/
[root@centos7 ~]$ df -h                                                   # 查看逻辑卷分区利用情况 ,数据才存放76M,我们的逻辑卷都3G呢, 如果我们想缩减1G,完全没有问题的。
Filesystem                         Size  Used Avail Use% Mounted on
/dev/sda3                           48G  4.9G   41G  11% /
devtmpfs                           979M  4.0K  979M   1% /dev
tmpfs                              993M     0  993M   0% /dev/shm
tmpfs                              993M   22M  971M   3% /run
tmpfs                              993M     0  993M   0% /sys/fs/cgroup
/dev/sda1                          976M  130M  780M  15% /boot
/dev/sda5                           20G  307M   18G   2% /app
tmpfs                              199M   32K  199M   1% /run/user/0
/dev/sr0                           8.1G  8.1G     0 100% /run/media/root/CentOS 7 x86_64
/dev/mapper/vg_test_01-lv_test_01  3.0G   76M  3.0G   3% /mnt/test_01

缩减空间

[root@centos7 ~]$ umount /dev/vg_test_01/lv_test_01                           # 取消挂载
[root@centos7 ~]$ resize2fs  /dev/vg_test_01/lv_test_01  1G                   # resieze2fs定义下文件系统大小
resize2fs 1.42.9 (28-Dec-2013)
Please run 'e2fsck -f /dev/vg_test_01/lv_test_01' first.

[root@centos7 ~]$ e2fsck -f /dev/vg_test_01/lv_test_01                        #上面的命令要检查,那就检查下
e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/vg_test_01/lv_test_01: 3614/196608 files (0.1% non-contiguous), 42361/786432 blocks
[root@centos7 ~]$ resize2fs  /dev/vg_test_01/lv_test_01  1G                   # 检查完,那就执行缩减文件系统大小
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/vg_test_01/lv_test_01 to 262144 (4k) blocks.
The filesystem on /dev/vg_test_01/lv_test_01 is now 262144 blocks long.
[root@centos7 ~]$ lvreduce  -L 1G /dev/vg_test_01/lv_test_01                  # 缩减下逻辑卷到1G,这里有提示,缩减可能会丢失数据,输入y即可
  WARNING: Reducing active logical volume to 1.00 GiB.
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce vg_test_01/lv_test_01? [y/n]: yG                 
  Size of logical volume vg_test_01/lv_test_01 changed from 3.00 GiB (768 extents) to 1.00 GiB (256 extents).
  Logical volume vg_test_01/lv_test_01 successfully resized.
[root@centos7 ~]$ mount /dev/vg_test_01/lv_test_01  /mnt/test_01/            # 恢复挂载
[root@centos7 ~]$ ll /mnt/test_01/
total 28
drwxr-xr-x. 140 root root 12288 Dec  7 13:54 etc
drwx------.   2 root root 16384 Dec  7 15:47 lost+found
[root@centos7 ~]$ df -h                                                     # 检查下
Filesystem                         Size  Used Avail Use% Mounted on
/dev/sda3                           48G  4.9G   41G  11% /
devtmpfs                           979M  4.0K  979M   1% /dev
tmpfs                              993M     0  993M   0% /dev/shm
tmpfs                              993M   22M  971M   3% /run
tmpfs                              993M     0  993M   0% /sys/fs/cgroup
/dev/sda1                          976M  130M  780M  15% /boot
/dev/sda5                           20G  307M   18G   2% /app
tmpfs                              199M   32K  199M   1% /run/user/0
/dev/sr0                           8.1G  8.1G     0 100% /run/media/root/CentOS 7 x86_64
/dev/mapper/vg_test_01-lv_test_01  944M   52M  825M   6% /mnt/test_01

迁移LVM的vg

移动vg到另外一个机器上

主要步骤:

  1. 检查和分析

  2. 一些预处理工作

  3. umount文件系统
      

  4. 设置卷组非活动
      
  5. 导出卷组
      
  6. 取出设备,插入到新机器
      
  7. 导入卷组
      
  8. 激活卷组
      
  9. 挂载文件系统

检查分析

[root@centos7 ~]$ vgs
  VG         #PV #LV #SN Attr   VSize VFree
  vg_test_01   4   1   0 wz--n- 3.98g 2.98g
[root@centos7 ~]$ pvs
l  PV         VG         Fmt  Attr PSize    PFree   
  /dev/sdb1  vg_test_01 lvm2 a--  1020.00m       0 
  /dev/sdb2             lvm2 ---     1.00g    1.00g
  /dev/sdc1  vg_test_01 lvm2 a--  1020.00m 1016.00m
  /dev/sdc2             lvm2 ---     1.00g    1.00g
  /dev/sdd1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sdd2             lvm2 ---     5.00g    5.00g
  /dev/sde1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sde2             lvm2 ---     2.00g    2.00g
[root@centos7 ~]$ lvs
  LV         VG         Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  lv_test_01 vg_test_01 -wi-ao---- 1.00g                                                    

我们使用lvs,vgs,pvs分别查看信息。发现我们vg是3G的大小。lv是1G的大小。但是vg的pv来自不同的磁盘的。我们要迁移vg。不能把包含pv都迁移过去把。 那这一下子需要4个盘的。

我们把pv数据迁移下,到时候摘盘的时候只那一个就可以多好。 发现/dev/sdd2是一个5G的盘。 打算使用/dev/sdd这个盘作为迁移盘来迁移数据。

一些预处理工作

[root@centos7 ~]$ umount /mnt/test_01/                                    # 去除挂载
[root@centos7 ~]$ vgchange  -an vg_test_01                                # 设置vg不激活
  0 logical volume(s) in volume group "vg_test_01" now active
[root@centos7 ~]$ pvmove /dev/sd{b1,c1,e1}                                 # 迁移pv数据
  /dev/sdb1: Moved: 0.00%
  /dev/sdb1: Moved: 100.00%
[root@centos7 ~]$ pvs                                                      # 确保pv都是空的,没有数据存放了。
  PV         VG         Fmt  Attr PSize    PFree   
  /dev/sdb1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sdb2             lvm2 ---     1.00g    1.00g
  /dev/sdc1  vg_test_01 lvm2 a--  1020.00m 1016.00m
  /dev/sdc2             lvm2 ---     1.00g    1.00g
  /dev/sdd1  vg_test_01 lvm2 a--  1020.00m       0 
  /dev/sdd2  vg_test_01 lvm2 a--    <5.00g   <5.00g
  /dev/sde1  vg_test_01 lvm2 a--  1020.00m 1020.00m
  /dev/sde2             lvm2 ---     2.00g    2.00g

[root@centos7 ~]$ vgreduce vg_test_01  /dev/sd{b1,c1,e1}                   # 缩减vg 去除b1,c1,e1
  Removed "/dev/sdb1" from volume group "vg_test_01"
  Removed "/dev/sdc1" from volume group "vg_test_01"
  Removed "/dev/sde1" from volume group "vg_test_01"
[root@centos7 ~]$ pvremove /dev/sd{b1,c1,e1}                               # 移除pv,这里提示,
  Lables on physical volume "/dev/sdb1" sucdessfully wiped.
  Labels on physical volume "/dev/sdc1" successfully wiped.
  Labels on physical volume "/dev/sde1" successfully wiped.

导出vg

[root@centos7 ~]$ vgdisplay                                       # 导出前的确认
  --- Volume group ---
  VG Name               vg_test_01
  System ID             
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  24
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                1
  Open LV               0
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               5.99 GiB
  PE Size               4.00 MiB
  Total PE              1534
  Alloc PE / Size       256 / 1.00 GiB
  Free  PE / Size       1278 / 4.99 GiB
  VG UUID               sZwHea-LabA-SJaV-Wz40-qFp3-Um2w-pvmJAY

[root@centos7 ~]$ vgexport vg_test_01                                    # 导出vg发现有激活的设备,需要关闭
  Volume group "vg_test_01" has active logical volumes
[root@centos7 ~]$ vgchange -an vg_test_01                                # 设置为非激活
  0 logical volume(s) in volume group "vg_test_01" now active
[root@centos7 ~]$ vgexport vg_test_01                                    # 导出, 要看到成功导出才可以,这里注意了, 如果这个vg名字和目标机器有重名的话, 这里使用rename改下在导出
  Volume group "vg_test_01" successfully exported

[root@centos7 ~]$ sync                                                   # 多执行几次sync。 防止内存和磁盘没有同步的问题。
[root@centos7 ~]$ sync
[root@centos7 ~]$ sync
[root@centos7 ~]$ sync

我这里把这个磁盘取出来安装到另外一个机器上面。下面的一部分操作在新机器上执行。注意啦。

识别硬件

[root@centos6 ~]$ /git/Bash/other/scan_scsi_device.sh                             # 执行下扫描硬件脚本,我自己写的,后面把这个bash脚本贴出来
start scan
/sys/devices/pci0000:00/0000:00:07.1/host0/scsi_host/host0/scan scan finished.
/sys/devices/pci0000:00/0000:00:07.1/host1/scsi_host/host1/scan scan finished.
/sys/devices/pci0000:00/0000:00:10.0/host2/scsi_host/host2/scan scan finished.
end  scan
please use lsblk check
[root@centos6 ~]$ lsblk                                                           # 使用lsblk查看,可以发现,识别出来就是那个/dev/sdc
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0     11:0    1  3.7G  0 rom  /media/CentOS_6.9_Final
sr1     11:1    1  8.1G  0 rom  /media/CentOS 7 x86_64
sda      8:0    0  200G  0 disk 
├─sda1   8:1    0    1G  0 part /boot
├─sda2   8:2    0 48.8G  0 part /
├─sda3   8:3    0 19.5G  0 part /app
├─sda5   8:5    0    2G  0 part [SWAP]
├─sda4   8:4    0    2M  0 part 
└─sda6   8:6    0    1G  0 part 
sdb      8:16   0  100G  0 disk 
├─sdb1   8:17   0    1G  0 part 
├─sdb2   8:18   0   20G  0 part 
├─sdb3   8:19   0    1G  0 part 
├─sdb4   8:20   0    1K  0 part 
├─sdb5   8:21   0   20G  0 part 
├─sdb6   8:22   0    1G  0 part 
└─sdb7   8:23   0    1G  0 part 
sr2     11:2    1  3.7G  0 rom  /media/CentOS_6.9_Final_
sr3     11:3    1    2G  0 rom  /media/CentOS_6.9_Final__
sdc      8:32   0   20G  0 disk 
├─sdc1   8:33   0    1G  0 part 
└─sdc2   8:34   0    5G  0 part 

脚本内容如下

[root@centos6 ~]$ cat /git/Bash/other/scan_scsi_device.sh 
#!/bin/bash
#================================================
#FileName   :scan_scsi_device
#Author     :zhaojiedi
#Description:如果你添加了一个新的硬盘设备, 但是没有被系统识别,使用如下脚本可以识别出来, 
#DateTime   :2017-11-30 18:03:27
#Version    :V1.0
#Other      :最新版本建议从这里获取https://github.com/zhaojiedi1992/Bash
#================================================
#!/bin/bash
echo "start scan"
for i in `find /sys/devices/ -name scan`; do 
    echo "- - -" > $i
    echo  $i scan finished.
done
echo "end  scan"
echo -e "33[32mplease use lsblk check33[0m"

导入并挂载

[root@centos6 ~]$ vgscan                                               # vg扫描
  Reading all physical volumes.  This may take a while...
  Found exported volume group "vg_test_01" using metadata type lvm2
[root@centos6 ~]$ pvscan                                               # pv扫描
  PV /dev/sdc1    is in exported VG vg_test_01 [1020.00 MiB / 0    free]
  PV /dev/sdc2    is in exported VG vg_test_01 [5.00 GiB / 4.99 GiB free]
  Total: 2 [5.99 GiB] / in use: 2 [5.99 GiB] / in no VG: 0 [0   ]
[root@centos6 ~]$ vgimport vg_test_01                                  # 确认无错误的就导入。
  Volume group "vg_test_01" successfully imported
[root@centos6 ~]$ vgs                                                  # 查看下vg信息
  VG         #PV #LV #SN Attr   VSize VFree
  vg_test_01   2   1   0 wz--n- 5.99g 4.99g
[root@centos6 ~]$ vgchange -ay vg_test_01                              # 激活vg
  1 logical volume(s) in volume group "vg_test_01" now active
[root@centos6 ~]$ mkdir /mnt/new_test                                  # 创建挂载点
[root@centos6 ~]$ mount /dev/vg_test_01/lv_test_01  /mnt/new_test/     # 挂载
[root@centos6 ~]$ du -sh /mnt/new_test/                                # 确认文件没有丢失
45M    /mnt/new_test/

快照LVM

分析下当前环境,我把vg移动到centos6上了。一下操作在centos6上执行的。

[root@centos6 ~]$ vgs                                        # 查看下vg信息, 一共6G,还有5G呢。
  VG         #PV #LV #SN Attr   VSize VFree
  vg_test_01   2   1   0 wz--n- 5.99g 4.99g
[root@centos6 ~]$ pvs                                       # 有2个pv
  PV         VG         Fmt  Attr PSize    PFree
  /dev/sdc1  vg_test_01 lvm2 a--u 1020.00m    0 
  /dev/sdc2  vg_test_01 lvm2 a--u    5.00g 4.99g

我们刚才给/mnt/new_test目录挂载逻辑卷vg_test_01。假如我们的/mnt/new_test的内容一直在变化。想去备份数据就需要快照卷了。

创建快照

[root@centos6 etc]$ lvcreate  -L 500M -s -p r -n vg_test_01_snapshot /dev/vg_test_01/lv_test_01       # -L指定大小, -s 表示创建快照而不是普通逻辑卷,-p r 指定权限为只读的, -n指定名字
  Logical volume "vg_test_01_snapshot" created.

注意: 如果使用的xfs文件系统, 挂载的时候需要加入nouuid,ro选项,且创建快照的时候不能指定-p r选项。具体参考: http://tldp.org/HOWTO/LVM-HOWTO/snapshots_backup.html

模拟一些数据修改操作

[root@centos6 etc]$ echo "zhaojiedi1992" >>/mnt/new_test/zhaojiedi1992.txt                            # 制作一些修改情况
[root@centos6 etc]$ vim /mnt/new_test/etc/motd

监控快照情况

[root@centos6 ~]$ lvdisplay  /dev/vg_test_01/vg_test_01_snapshot 
  --- Logical volume ---
  LV Path                /dev/vg_test_01/vg_test_01_snapshot
  LV Name                vg_test_01_snapshot
  VG Name                vg_test_01
  LV UUID                pZ8x2u-lqPA-uLef-P816-dS8V-wZ0y-PKWWwL
  LV Write Access        read only                                         #只读的
  LV Creation host, time centos6.magedu.com, 2017-12-02 06:11:56 +0800
  LV snapshot status     active destination for lv_test_01                 # 这是lv_test_01的快照
  LV Status              available
  # open                 0
  LV Size                1.00 GiB                                          # 快照的总大小
  Current LE             256
  COW-table size         500.00 MiB
  COW-table LE           125
  Allocated to snapshot  0.01%                                             # 这个地方可以看当前快照占用情况。 
  Snapshot chunk size    4.00 KiB
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:3

挂载快照并备份

[root@centos6 etc]$ mkdir /mnt/test_bak                                            # 创建一个快照挂载目录
[root@centos6 etc]$ mount /dev/vg_test_01/vg_test_01_snapshot  /mnt/test_bak/      # 挂载,因为我们上面创建快照的时候指定-pr只读的
mount: block device /dev/mapper/vg_test_01-vg_test_01_snapshot is write-protected, mounting read-only
[root@centos6 etc]$ cat /mnt/test_bak/                                             # 快照后的zhaojiedi1992.txt没有的。
etc/        lost+found/ 
[root@centos6 etc]$ cat /mnt/test_bak/etc/motd                                     # 这个地方的文件也是快照前的样子。没有问题
Welcom you this system
[root@centos6 etc]$ tar -jcvf /root/test.tar.bz --acl --selinux  --xattrs /mnt/test_bak/   # 备份数据赶紧的

移除快照

[root@centos6 etc]$ umount /mnt/test_bak/                                    # 取消挂载
[root@centos6 etc]$ lvremove /dev/vg_test_01/vg_test_01_snapshot             # 移除了快照卷
Do you really want to remove active logical volume vg_test_01_snapshot? [y/n]: y
  Logical volume "vg_test_01_snapshot" successfully removed