PHP-FPM配置的优化

php-fpm默认安装后以下三个参数都是关闭的:

#表示在 emergency_restart_interval 所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果

#超过 emergency_restart_threshold 个php-fpm就会优雅重启。这两个选项一般保持默认值。

emergency_restart_threshold = 10
emergency_restart_interval = 1m

#设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.

process_control_timeout = 0

出于优化的目的,我们把它们打开

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s

有以下优点

在1分钟内,出现 SIGSEGV 或者 SIGBUS 错误的 PHP-CGI 进程数超到10个时,PHP-FPM 就会优雅的自动重启。

SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。
SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。

php-fpm 占用CPU过高,100%的解决方法

话说最近配置的LNMP还算稳定,正在暗自窃喜,但是从昨晚开始,就发现服务器的CPU占用过高,甚至到了100%。我的内存是1G的,正常情况下占用率应该在5%以下,最多不超10%。

阿里云最近的监控显示:

未分类

使用top命令查看,发现 php-fpm 占用内存过高,非常不正常:

未分类

我按照《Nginx使用的php-fpm的两种进程管理方式及优化》这篇文章,配置 php-fpm 进程数如下:

未分类

重启 php-fpm 后,还是没有彻底解决问题,依旧会出现占用 99以上,不知道哪位朋友知道如何分析和解决呢?小弟求助了!

后续进展

昨天(3月29日)找 @容哥 指导检查,后来发现是 eAccelerator 组件不知什么原因占用 CPU 过高,打开 php.ini

vi /usr/local/php/etc/php.ini

删除 eAccelerator 的配置信息,重启 lnmp

/root/lnmp restart

但还是没有彻底解决问题,几个小时后,我又重启了 lnmp ,居然发现,似乎已经越来越平稳啦。

可能用到的命令:

top                //查看CPU、内存使用信息,查看哪个进程占用CPU高以及它的PID

ll /proc/PID号/fd/             // 通过PID找到哪个文件操作的进程,进而知道问题所在

Nginx使用的php-fpm的两种进程管理方式及优化

这篇文章主要介绍了Nginx使用的php-fpm的两种进程管理方式及优化,需要的朋友可以参考下
PS:前段时间配置php-fpm的时候,无意中发现原来它还有两种进程管理方式。与Apache类似,它的进程数也是可以根据设置分为动态和静态的。

php-fpm目前主要又两个分支,分别对应于php-5.2.x的版本和php-5.3.x的版本。在5.2.x的版本中,php-fpm.conf使用的是xml格式,而在新的5.3.x版本中,则是和php.ini一样的配置风格。
在5.2.x版本中,php-fpm.conf中对于进程管理号称是有两种风格,一种是静态(static)的,一种是类似于apache风格(apache-like)的。

代码如下:

Process manager settings
<value name=”pm”>
Sets style of controling worker process count.
Valid values are 'static' and ‘apache-like'
<value name=”style”>static</value>

按照文档的说明,如果pm的style采用apache-like,启动的进程数应该是和StartServers指定的一样。不过经过数次的尝试,会发现,实际上在这里将pm的style配置成apache-like没有起任何作用。也就是说,这里的apache-like并没有被实现。
不过,在最新的5.3.x的配套php-fpm中,apache风格的进程管理已经被实现了。

代码如下:

; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives:
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; Note: This value is mandatory.
;pm = dynamic
pm = static

由上面一段文字可知,对于进程的管理存在两种风格——static和dynamic。和之前的版本的进程管理其实还是一样的,只是将apache-like改成了dynamic,这样更容易理解。

如果设置成static,php-fpm进程数自始至终都是pm.max_children指定的数量,不再增加或减少。
如果设置成dynamic,则php-fpm进程数是动态的,最开始是pm.start_servers指定的数量,如果请求较多,则会自动增加,保证空闲的进程数不小于pm.min_spare_servers,如果进程数较多,也会进行相应清理,保证多余的进程数不多于pm.max_spare_servers。

这两种不同的进程管理方式,可以根据服务器的实际需求来进行调整。

这里先说一下涉及到这个的几个参数,他们分别是pm、pm.max_children、pm.start_servers、pm.min_spare_servers和pm.max_spare_servers。
pm表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。

下面4个参数的意思分别为:

  • pm.max_children:静态方式下开启的php-fpm进程数量。
  • pm.start_servers:动态方式下的起始php-fpm进程数量。
  • pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
  • pm.max_spare_servers:动态方式下的最大php-fpm进程数量。

如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为dynamic,那么pm.max_children参数失效,后面3个参数生效。系统会在php-fpm运行开始的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

那么,对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。
对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到,比如8GB内存可以设置为100,那么php-fpm耗费的内存就能控制在 2G-3G的样子。如果内存稍微小点,比如1G,那么指定静态的进程数量更加有利于服务器的稳定。这样可以保证php-fpm只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M 得到。比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比较合适的值在5~10之间。

php-fpm 搭建

php-fpm 即 php-Fastcgi Process Manager。
php-fpm 是 FastCGI 的实现,并提供了进程管理的功能。
进程包含 master 进程和 worker 进程两种进程。
master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个 (具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

配置 nginx 和 php-fpm 交互

  • 配置 nginx 文件
  location ~* .php$ {
    root           html;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }

当请求的 url 能匹配以.php 结尾时,将反向代理给 php-fpm 处理动态信息。
root 是配置 php 程序放置的根目录。
fastcgi_pass 指令表示将该 url 请求代理至 nginx fastcgi 进程监听的 IP 地址和端口。
fastcgiparam 表示读取的是 $document_root(网站根目录)下的.php 文件;如果没有配置这一配置项,nginx 不会访问根目录的 php。

fastcgi_params 文件中含有各个 nginx 常量的定义,默认情况 SCRIPT_FILENAME=$fastcgi_script_name

openresty+redis拦截高频访问IP

CC攻击

网站受到攻击通常是黑客通过几个甚至多个IP地址,在短时间内进行超高频率访问,从而让服务器在短时间内增加巨大的计算量,导致负载增加,降低响应能力,甚至直接宕机停止服务。
通常这类情况我们只能通过查看分析网站日志,从而获得攻击者的IP地址,再通过防火墙进行拦截。
但一般而言这只会发生在监控系统已经开始报警之后,也就是网站或服务已经遭受到了攻击,并造成影响之后。并且在日志中搜寻到攻击者的IP并不是十分简单的事情,通常当我们找到了攻击者,攻击者可能已经停止了攻击,我们也只能预防他下次可能的攻击。

自动拦截

经历了黑客们深夜的骚扰和攻击,如何让那些短时间内大量访问的地址被自动拦截变成了努力的方向。
云服务商提供了WAF等商业化产品,协助我们处理这些威胁。
相比较于这些高昂价格的产品,开源软件同样在灵活性和可整合性上有很大的优势,接下来就介绍一下我是如何使用openresty和redis实现拦截高频访问的地址。

安装环境

之前的文章已经介绍过:Openresty+Redis 动态切换upstream (http://learn-learn.top/archives/169.html)
大致按照官方介绍就可以轻松安装。

nginx配置

nginx在初始化时建立一个redis的链接,并且在每次访问前需要执行block.lua进行验证

init_by_lua_block {
    redis = require "redis"
    client = redis.connect('127.0.0.1', 6379)
}
server {
    listen 8080;
    location  / {
        access_by_lua_file /usr/local/nginx/conf/lua/block.lua; 
        proxy_pass http://192.168.1.102:8000;
    }
}

lua脚本:

function isConnected()
    return client:ping()
end
function createRedisConnection()
        return redis.connect('127.0.0.1', 6379)
end

if pcall(isConnected)then --如果发生redis连接失败,将停止拦截。
    --
else
    if pcall(createRedisConnection)then     --断开重连会发送每次访问都需要重连redis
        client = createRedisConnection();       --如果访问量大的情况下,建议关闭重连,if pcall不执行,直接ngx.exit
    else
        ngx.exit(ngx.OK);
    end 
end


local ttl = 60;     --监测周期
local bktimes = 30; --在监测周期内达到触发拦截的访问量
block_ttl = 600;    --触发拦截后拦截时间
ip = ngx.var.remote_addr
ipvtimes = client:get(ip)

if(ipvtimes)then
    if(ipvtimes == "-1")then
        --ngx.say("blocked")
        return ngx.exit(403);
    else
        last_ttl = client:ttl(ip)
        --ngx.say("key exist.ttl is ",last_ttl);
        if(last_ttl==-1)then
            client:set(ip,0)
            client:expire(ip,ttl)
            --ngx.say("ttl & vtimes recount")
            return ngx.exit(ngx.OK);
        end
        vtimes = tonumber(client:get(ip))+1;
        if(vtimes<bktimes)then
            client:set(ip,vtimes);
            client:expire(ip,last_ttl)
            --ngx.say(ip," view ",vtimes," times");
            return ngx.exit(ngx.OK);
        else
            --ngx.say(ip," will be block noext time.")
            client:set(ip,-1);
            client:expire(ip,block_ttl)
            return ngx.exit(ngx.OK);
        end
    end
else
    --ngx.say("key do not exist")
    client:set(ip,1)
    --ngx.say(ip," view 1 times")
    client:expire(ip,ttl)
    return ngx.exit(ngx.OK)
end

脚本说明:

1.重要参数:

ttl = 60; –监测周期
bktimes = 30; –在监测周期内达到触发拦截的访问量
block_ttl = 600; –触发拦截后拦截时间

以上参数表示,一个IP地址在60秒内访问超过30次将被拦截600秒。

2.逻辑说明:

a)检测初始化的redis连接是否能够正常运行,如果连接失败或已经断开,将会重新建立连接,如果仍旧无法连接,将直接放行。这里是为了避免redis宕机导致nginx无法正常响应。当然如果初始连接中断,将会导致每次访问都会创建redis连接。

b)当某个IP首次访问时,将在redis中新建一个以IP地址为KEY的键(如果需要多个站点,修改下key的命名规则即可),value为1,并设置expire时间。当这个地址再次访问且key尚未过期前,将会每次递增key的value数,直到到达达到bktimes,或者key到期从而消亡。(本人用的redis5.0,到期key会直接不存在,可能部分版本到期后value为-1)

c)当key过期后,对于系统而言就是第一次访问,重新创建value为1的新key

d)当达到bktimes后,会将对应的IP的key的value设置为-1,且过期时间为block_ttl。

e)当访问到value为-1的key,即某个IP达到了我们设定的访问频次,我们将直接拦截,返回403.

3.完善方向:

a)在访问前添加黑白名单功能(这个在redis中新建立两个key即可)
b)拦截IP段(根据访问的IP地址建立以IP段为key的字段即可)
c)redis断开重连后,每次访问都要建立连接问题。

Openresty的同步输出与流式响应

默认情况下, ngx.say和ngx.print都是异步输出的,先来看一个例子:

location /test {
    content_by_lua_block {
        ngx.say("hello")
        ngx.sleep(3)
        ngx.say("the world")
    }
}

执行测试,可以发现首先, /test 响应内容是在触发请求 3s 后一起接收到响应体,第一个ngx.say好像是被“绕过”,先执行sleep,然后和最后一个ngx.say的内容一起输出。

location /test {
    content_by_lua_block {
        ngx.say("hello")
        ngx.flush() -- 显式的向客户端刷新响应输出
        ngx.sleep(3)
        ngx.say("the world")
    }
}

首先输出”hello”,然后停顿3秒,最后输出”the world”——正如我们想象的那样。ngx.flush执行显示的输出,前一个ngx.say被“阻塞”住,执行完输出后方往下执行。

再看一个例子:

server {
    listen 80;
    lua_code_cache off;
    location /test {
        content_by_lua_block {
            ngx.say(string.rep("hello", 4000))
            ngx.sleep(3)
            ngx.say("the world")
        }
    }
}

这个例子和第一个例子相比,唯一不同就是ngx.say输出内容长了不少,我们发现浏览器先收到所有的hello,接着又收到了”the world” 。然而如果我们把4000改为小一点的值如2000(不同配置这个相对大小或有不同),那么仍然会出现先停顿3s,然后所有”hello”连同最后”the world”一起输出的情况。

通过以上三个例子,我们可以得出下面的结论:

ngx.say和ngx.print的同步和异步

  • nginx有个输出缓冲(system send buffer),如16k。ngx.say和ngx.print默认是向这个输出缓冲写入数据,如果没有显示的调用ngx.flush,那么在content阶段结束后输出缓冲会写入客户端;

  • 如果没有ngx.flush也没有到结束阶段,但如果输出缓冲区满了,那么也会输出到客户端;

因此ngx.say和ngx.print的默认向客户端的输出都是异步的,非实时性的,改变这一行为的是ngx.flush,可以做到同步和实时输出。这在流式输出,比如下载大文件时非常有用。

ngx.flush的同步和异步

lua-nginx也提到了ngx.flush的同步和异步。某一个ngx.say或者ngx.print调用后,这部分输出内容会写到输出缓冲区,同步的方式ngx.flush(true)会等到内容全部写到缓冲区再输出到客户端,而异步的方式ngx.flush()会将内容一边写到缓冲区,而缓冲区则一边将这些内容输出到客户端。

openresty和nginx流式输出的比较

流式输出,或者大文件的下载,nginx的upstream模块已经做得非常好,可以通过proxy_buffering|proxy_buffer_size|proxy_buffers 等指令精细调控,而且这些指令的默认值已经做了妥善处理。我们来看看这些指令以及默认值:

proxy_buffering on;
proxy_buffer_size 4k|8k; 
proxy_buffers 8 4k|8k; 
proxy_busy_buffers_size 8k|16k;
proxy_temp_path proxy_temp;
  • proxy_buffering on表示内存做整体缓冲,内存不够时多余的存在由proxy_temp_path指定的临时文件中,off表示每次从上游接收proxy_buffer_size响应的内容然后直接输出给客户端,不会试图缓冲整个响应
  • proxy_buffer_size和proxy_buffers都是指定内存缓冲区的大小,proxy_buffer_size通常缓冲响应头,proxy_buffers缓冲响应内容,默认为一页的大小,proxy_buffers还可以指定这样的缓冲区的个数
  • proxy_busy_buffers_size nginx在试图缓冲整个响应过程中,可以让缓冲区proxy_busy_buffers_size大小的已经写满的部分先行发送给客户端。于此同时,缓冲区的另外部分可以继续读。如果内存缓冲区不够用了,还可以写在文件缓冲区
  • proxy_temp_path 使用文件作为接受上游请求的缓冲区buffer,当内存缓冲区不够用时启用

openresty的怎么做到过大响应的输出呢? (https://moonbingbing.gitbooks.io/openresty-best-practices/content/index.html) 提到了两种情况:

  • 输出内容本身体积很大,例如超过 2G 的文件下载
  • 输出内容本身是由各种碎片拼凑的,碎片数量庞大

前面一种情况非常常见,后面一种情况比如上游已经开启Chunked的传输方式,而且每片chunk非常小。笔者就遇到了一个上游服务器通过Chunked分片传输日志,而为了节省上游服务器的内存将每片设置为一行日志,一般也就几百字节,这就太“碎片”了,一般日志总在几十到几百M,这么算下来chunk数量多大10w+。笔者用了resty.http来实现文件的下载,文件总大小48M左右。

local http = require "resty.http"
local httpc = http.new()

httpc:set_timeout(6000)
httpc:connect(host, port)

local client_body_reader, err = httpc:get_client_body_reader()

local res, err = httpc:request({
    version = 1.1,
    method = ngx.var.request_method,
    path = ngx.var.app_uri,
    headers = headers,
    query = ngx.var.args,
    body = client_body_reader
})

if not res then
    ngx.say("Failed to request ".. ngx.var.app_name .." server: ", err)
    return
end

-- Response status
ngx.status = res.status

-- Response headers
for k, v in pairs(res.headers) do
    if k ~= "Transfer-Encoding" then  --必须删除上游Transfer-Encoding响应头
        ngx.header[k] = v
    end
end

-- Response body
local reader = res.body_reader
repeat
    local chunk, err = reader(8192)
    if err then
        ngx.log(ngx.ERR, err)
        break
    end

    if chunk then
        ngx.print(chunk)
        ngx.flush(true)  -- 开启ngx.flush,实时输出
    end
until not chunk

local ok, err = httpc:set_keepalive()
if not ok then
    ngx.say("Failed to set keepalive: ", err)
    return
end

多达10w+的”碎片”的频繁的调用ngx.pirnt()和ngx.flush(true),使得CPU不堪重负,出现了以下的问题:

  • CPU轻轻松松冲到到100%,并保持在80%以上
  • 由于CPU的高负荷,实际的下载速率受到显著的影响
  • 并发下载及其缓慢。笔者开启到第三个下载连接时基本就没有反应了

未分类

这是开启了ngx.flush(true)的情况(ngx.flush()时差别不大),如果不开启flush同步模式,则情况会更糟糕。CPU几乎一直维持在100%左右:

未分类

可见,在碎片极多的流式传输上,以上官方所推荐的openresty使用方法效果也不佳。

于是,回到nginx的upstream模块,改content_by_lua_file为proxy_pass再做测试,典型的资源使用情况为:

未分类

无论是CPU还是内存占用都非常低,开启多个下载链接后并无显著提升,偶尔串升到30%但迅速下降到不超过10%。

因此结论是,涉及到大输出或者碎片化响应的情况,最好还是采用nginx自带的upstream方式,简单方便,精确控制。而openresty提供的几种方式,无论是异步的ngx.say/ngx.print还是同步的ngx.flush,实现效果都不理想。

NFS问题:clnt_create: RPC: Port mapper failure – Unable to receive: errno 113 (No route to host)

摘要:NFS问题:nfs与rpcbind都正常启动了,并且已经发布共享目录/tmp。

在客户端查看时,出现如下错误提示:

[[email protected]~]# showmounte192.168.122.10
clnt_create:RPC:Portmapperfailure-Unabletoreceive:errno113(Noroutetohost)

解决方法:关闭被访问的NFS服务器上的防火墙和selinux(当然只适合个人测试环境),执行systemctlstopfire

NFS问题:

nfs与rpcbind都正常启动了,并且已经发布共享目录/tmp。

在客户端查看时,出现如下错误提示:

[[email protected] ~]# showmount -e 192.168.122.10
clnt_create: RPC: Port mapper failure - Unable to receive: errno 113 (No route to host)

解决方法:

关闭被访问的NFS服务器上的防火墙和selinux(当然只适合个人测试环境)

执行

systemctl stop firewalld
iptables -F
setenforce 0

在客户端重新查看showmount -e 192.168.122.10

在客户端showmount -e 192.168.122.10

遇到以下错误提示“clnt_create: RPC: Program not registered”

解决方法:

服务端[[email protected] /]# rpc.mountd 即可

客户端查看

[[email protected] ~]# showmount -e 192.168.122.10

Export list for server.example.com:/tmp *(rw,sync,no_root_squash)

nfs挂载根文件系统

1.安装NFS

     
nfs原理是通过网络,将远程主机共享的文件系统,挂载到本机。Ubuntu 12.04上默认是没有安装NFS服务器的,首先要安装NFS服务程序:

先执行 # apt-get update

未分类

然后执行 #  sudo apt-get install nfs-kernel-server

未分类

(安装nfs-kernel-server时,apt会自动安装nfs-common和portmap) 这样,宿主机就相当于NFS Server。

2.宿主机NFS的配置

   

2.1 修改配置文件/etc/exports

          
在终端下用 # vi  /etc/exports打开exports文件。

如果你没有配置过这个文件的话此文件应该是空的。
      
在开始部分写入  /home/topeet/targetNFS/   * (rw,sync,no_root_squash)

未分类

/home/topeet/targetNFS/ -- 与客户机共享的目录,在这里我的共享目录为  /home/topeet/targetNFS/;

其中:

*      -- 表示允许任意用户使用,也可以使用具体IP;
(rw,sync,no_root_squash)  -- rw,挂载此目录的客户机对此目录有读写权利;sync,……;no_root_squash,挂载此目录的客户机享有主机root的权利;

我是将主机的根目录设置为共享目录  /  *(rw,sync,no_root_squash)
OK,保存,退出。
修改完成之后输入:#  exportfs –rv来使配置文件生效
——————— 
 

3.重启nfs(每一次修改exports都要重启nfs)

/etc/init.d/nfs-kernel-server restart
/etc/init.d/portmap restart

4.本地测试

现在可以在本机上试一下:

#sudo mount -t nfs localhost:/home/kevin /mnt

注:localhost为本机linux的IP地址

这样就把共享目录挂到了/mnt目录,取消挂载用:

#sudo umount /mnt

如果用在嵌入式设备上挂载,要加上参数-o nolock

我在开发板上使用的挂载命令:

mount -t nfs -o nolock 192.168.1.8:/home/kevin /mnt

CentOS 7中搭建NFS文件共享存储服务的完整步骤

如果仅仅只是希望搭建一个linux之间进行文件共享的服务器,而不是所有异构的系统之间共享的话,nfs是一个不错的选择。下面这篇文章主要给大家介绍了关于在CentOS 7中搭建NFS文件共享存储服务的完整步骤,需要的朋友可以参考下

前言

NFS(Network File System)意为网络文件系统,它最大的功能就是可以通过网络,让不同的机器不同的操作系统可以共享彼此的文件。简单的讲就是可以挂载远程主机的共享目录到本地,就像操作本地磁盘一样,非常方便的操作远程文件。

本文将给大家讲解如何在CentOS7上安装和配置NFS服务器。

下面话不多说了,来一起看看详细的介绍吧

准备

我们需要两台CentOS7机器,我们用虚拟机做测试,分别做NFS服务器和客户端,配置如下:

  • NFS服务器ip:192.168.11.31

  • 客户端ip:192.168.11.34

我们要实现的目标是:在NFS服务器上共享一个目录,在客户端上可以直接操作NFS服务器上的这个共享目录下的文件。

NFS服务器配置

1.安装NFS服务

首先使用yum安装nfs服务:

yum -y install rpcbind nfs-utils

2.创建共享目录

在服务器上创建共享目录,并设置权限。

mkdir /data/share/
chmod 755 -R /data/share/

3.配置NFS

nfs的配置文件是 /etc/exports ,在配置文件中加入一行:

/data/share/ 192.168.11.34(rw,no_root_squash,no_all_squash,sync)

这行代码的意思是把共享目录/data/share/共享给192.168.11.34这个客户端ip,后面括号里的内容是权限参数,其中:

  • rw 表示设置目录可读写。

  • sync 表示数据会同步写入到内存和硬盘中,相反 rsync 表示数据会先暂存于内存中,而非直接写入到硬盘中。

  • no_root_squash NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,也拥有root权限。

  • no_all_squash 不论NFS客户端连接服务端时使用什么用户,对服务端分享的目录来说都不会拥有匿名用户权限。

如果有多个共享目录配置,则使用多行,一行一个配置。保存好配置文件后,需要执行以下命令使配置立即生效:

exportfs -r

4.设置防火墙

如果你的系统没有开启防火墙,那么该步骤可以省略。

NFS的防火墙特别难搞,因为除了固定的port111、2049外,还有其他服务如rpc.mounted等开启的不固定的端口,这样对防火墙来说就比较麻烦了。为了解决这个问题,我们可以设置NFS服务的端口配置文件。

修改/etc/sysconfig/nfs文件,将下列内容的注释去掉,如果没有则添加:

RQUOTAD_PORT=1001
LOCKD_TCPPORT=30001
LOCKD_UDPPORT=30002
MOUNTD_PORT=1002

保存好后,将端口加入到防火墙允许策略中。执行:

firewall-cmd --zone=public --add-port=111/tcp --add-port=111/udp --add-port=2049/tcp --add-port=2049/udp --add-port=1001/tcp --add-port=1001/udp --add-port=1002/tcp --add-port=1002/udp --add-port=30001/tcp --add-port=30002/udp --permanent
firewall-cmd --reload

5.启动服务

按顺序启动rpcbind和nfs服务:

systemctl start rpcbind
systemctl start nfs

加入开机启动:

systemctl enable rpcbind 
systemctl enable nfs

nfs服务启动后,可以使用命令 rpcinfo -p 查看端口是否生效。

服务器的后,我们可以使用 showmount 命令来查看服务端(本机)是否可连接:

[root@localhost ~]# showmount -e localhost
Export list for localhost:
/data/share 192.168.11.34

出现上面结果表明NFS服务端配置正常。

客户端配置

1.安装rpcbind服务

客户端只需要安装rpcbind服务即可,无需安装nfs或开启nfs服务。

yum -y install rpcbind

2.挂载远程nfs文件系统

查看服务端已共享的目录:

[root@localhost ~]# showmount -e 192.168.11.31
Export list for 192.168.11.31:
/data/share 192.168.11.34

建立挂载目录,执行挂载命令:

mkdir -p /mnt/share
mount -t nfs 192.168.11.34:/data/share /mnt/share/ -o nolock,nfsvers=3,vers=3

如果不加 -onolock,nfsvers=3 则在挂载目录下的文件属主和组都是nobody,如果指定nfsvers=3则显示root。

如果要解除挂载,可执行命令:

umount /mnt/share

3.开机自动挂载

如果按本文上面的部分配置好,NFS即部署好了,但是如果你重启客户端系统,发现不能随机器一起挂载,需要再次手动操作挂载,这样操作比较麻烦,因此我们需要设置开机自动挂载。我们不要把挂载项写到/etc/fstab文件中,因为开机时先挂载本机磁盘再启动网络,而NFS是需要网络启动后才能挂载的,所以我们把挂载命令写入到/etc/rc.d/rc.local文件中即可。

[root@localhost ~]# vim /etc/rc.d/rc.local
#在文件最后添加一行:
mount -t nfs 192.168.11.34:/data/share /mnt/share/ -o nolock,nfsvers=3,vers=3

保存并重启机器看看。

测试验证

查看挂载结果,在客户端输入 df -h

文件系统    容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root   18G 5.0G 13G 29% /
devtmpfs      904M  0 904M 0% /dev
tmpfs       916M  0 916M 0% /dev/shm
tmpfs       916M 9.3M 906M 2% /run
tmpfs       916M  0 916M 0% /sys/fs/cgroup
/dev/sda1      497M 164M 334M 33% /boot
tmpfs       184M  0 184M 0% /run/user/0
192.168.11.31:/data/share  18G 1.7G 16G 10% /mnt/share

看到最后一行了没,说明已经挂载成功了。接下来就可以在客户端上进入目录/mnt/share下,新建/删除文件,然后在服务端的目录/data/share查看是不是有效果了,同样反过来在服务端操作在客户端对应的目录下看效果。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

NFS企业上机实战

1.在NFS服务器A上共享 /data/w_shared/data/r_shared 两个文件目录,允许从NFS客户端B,C上分别挂载共享目录后实现从B,C上只读/data/r_shared ,可写/data/w_shared

2.NFS客户端B上的挂载点为/data/b_w(写),/data/b_r(读)

3.NFS客户端C上的挂载点为/data/w_qzc(写),/data/r_qzc(读)

4.从NFS客户端B上的NFS可写挂载耟目录创建任意文件,从C上可以删除这个创建的文件,反之也可以。

#服务端配置
[root@zcjscl ~]# vim /etc/exports 
添加
/data/w_shared 0.0.0.0/24(rw,sync)
/data/r_shared 0.0.0.0/24(ro)

[root@zcjscl ~]# mkdir /data/w_shared /data/r_shared -p
[root@zcjscl ~]# chown -R nfsnobody /data/w_shared/ /data/r_shared 
[root@zcjscl ~]# /etc/init.d/rpcbind restart
[root@zcjscl ~]# /etc/init.d/nfs restart
[root@zcjscl ~]#chkconfig nfs on
[root@zcjscl ~]#chkconfig rpcbind on
[root@zcjscl ~]#/etc/init.d/iptables stop
[root@zcjscl ~]#getenforce
[root@zcjscl ~]#showmount -e 127.0.0.1   //测试
#B客户端配置,挂载

[root@zcjscl ~]#mkdir /data/{b_w,b_r} -p
[root@zcjscl ~]#showmount -e 192.168.1.99
[root@zcjscl ~]#/etc/init.d/rpcbind restart
[root@zcjscl ~]#chkconfig rpcbind on
[root@zcjscl ~]#mount -t nfs 192.168.1.99:/data/w_shared /data/b_w/
[root@zcjscl ~]#mount -t nfs 192.168.1.99:/data/r_shared /data/b_r/
[root@zcjscl ~]#df -h //检查
#C客户端配置,挂载

[root@zcjscl ~]#mkdir /data/{w_qzc,r_qzc} -p
[root@zcjscl ~]#showmount -e 192.168.1.99
[root@zcjscl ~]#/etc/init.d/rpcbind restart
[root@zcjscl ~]#chkconfig rpcbind on
[root@zcjscl ~]#mount -t nfs 192.168.1.99:/data/w_shared /w_qzc/
[root@zcjscl ~]#mount -t nfs 192.168.1.99:/data/r_shared /r_qzc/
[root@zcjscl ~]#df -h //检查
#挂载优化

 读取限制       写入限制     不更新时间
rsize=32768,wsize=32768,intr,noatime

mount -t nfs -o rsize=32768,wsize=32768,intr,noatime 192.168.1.99:/data/w_shared /w_qzc/