关于CGI和FastCGI的理解

[TOC]

关于CGI和FastCGI的理解

阅读目录

转载:关于CGI和FastCGI的理解


在搭建 LAMP/LNMP 服务器时,会经常遇到 PHP-FPM、FastCGI和CGI 这几个概念。如果对它们一知半解,很难搭建出高性能的服务器。

0.CGI的引入

在网站的整体架构中,Web Server(如nginx,apache)只是内容的分发者,对客户端的请求进行应答。

如果客户端请求的是index.html这类静态页面,那么Web Server就去文件系统中找对应的文件,找到返回给客户端(一般是浏览器),在这里Web Server分发的就是是静态数据。

整个过程如下图:

img

对于像index.php这类的动态页面请求,Web Server根据配置文件知道这个不是静态文件,则会调用PHP 解析器进行处理然后将返回的数据转发给客户端(浏览器)。

整个过程如下图:

img

在这个过程中,Web Server并不能直接处理静态或者动态请求,对于静态请求是直接查找然后返回数据或者报错信息,对于动态数据也是交付给其他的工具(这里的PHP解析器)进行处理。

那么Web Server和处理工具(这里的php-fpm)是怎样进行交互的呢?传输的是那些数据呢?这些数据的格式又是怎样的呢?

由此便引出了今天的主角:CGI

1.关于CGI

1.1.什么是CGI?

1)CGI(Common Gateway Interface)全称是“通用网关接口”,是一种让客户端(web浏览器)与Web服务器(nginx等)程序进行通信(数据传输)的协议。

用来规范web服务器传输到php解释器中的数据类型以及数据格式,包括URL、查询字符串、POST数据、HTTP header等,也就是为了保证web server传递过来的数据是标准格式的。

2)CGI可以用任何一种具有标准输入、输出和环境变量的语言编写,如php、perl、tcl等。

不同类型语言写的程序只要符合cgi标准,就能作为一个cgi程序与web服务器交互,早期的cgi大多都是c或c++编写的。

3)一般说的CGI指的是用各种语言编写的能实现该功能的程序。

1.2.CGI程序的工作原理

1)每次当web server收到index.php这种类型的动态请求后,会启动对应的CGI程序(PHP的解析器);
2)PHP解析器会解析php.ini配置文件,初始化运行环境,然后处理请求,处理完成后将数据按照CGI规定的格式返回给web server然后退出进程;
3)最后web server再把结果返回给浏览器。

1.3.CGI程序的特点

1)高并发时的性能较差:
​ CGI程序的每一次web请求都会有启动和退出过程,也就是最为人诟病的fork-and-execute模式(每次HTTP服务器遇到动态请求时都需要重新启动脚本解析器来解析php.ini,重新载入全部DLL扩展并重初始化全部数据结构,然后把结果返回给HTTP服务器),很明显,这样的接口方式会导致php的性能很差,在处理高并发访问时,几乎是不可用的。
2)传统的CGI接口方式安全性较差

3)CGI对php.ini的配置很敏感,在开发和调试的时候相当方便

1.4.CGI程序的应用领域

​ 因为CGI为每一次请求增加一个进程,效率很低,所以基本已经不在生产部署时采用。但由于CGI对php配置的敏感性,通常被用在开发和调试阶段

2.关于FastCGI

2.1.什么是FastCGI?

通过CGI程序的工作原理可以看出:CGI程序性能较差,安全性较低,为了解决这些问题产生了FastCGI。

1)FastCGI(Fast Common Gateway Interface)全称是“快速通用网关接口”
​ 是通用网关接口(CGI)的增强版本,由CGI发展改进而来,主要用来提高CGI程序性能,
​ 类似于CGI,FastCGI也是一种让交互程序与Web服务器通信的协议
2)FastCGI致力于减少网页服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的网页请求(提高并发访问)。

3)同样的,一般说的FastCGI指的也是用各种语言编写的能实现该功能的程序。

2.2.FastCGI程序的工作原理

1)Web Server启动同时,加载FastCGI进程管理器(nginx的php-fpm或者IIS的ISAPI或Apache的Module)
2)FastCGI进程管理器读取php.ini配置文件,对自身进行初始化,启动多个CGI解释器进程(php-cgi),等待来自Web Server的连接。
3)当Web Server接收到客户端请求时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server会将相关环境变量和标准输入发送到FastCGI子进程php-cgi进行处理
4)FastCGI子进程完成处理后将数据按照CGI规定的格式返回给Web Server,然后关闭FastCGI子进程或者等待下一次请求。

2.3.FastCGI对进程的管理方式

Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然提高。而且当worker不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是fastcgi的对进程的管理。

2.4.FastCGI的特点:

1)FastCGI具有语言无关性,支持用大多数语言进行编写,对应的程序也支持大多数主流的web服务器
​ FastCGI技术目前支持语言有:C/C++,Java,PHP,Perl,Tcl,Python,SmallTalk,Ruby等。
​ 支持FastCGI技术的主流web服务器有:Apache,Nginx,lighttpd等

2)FastCGI程序的接口方式采用C/S结构,可以将web服务器和脚本解析服务器分开,独立于web服务器运行,提高web服务器的并发性能和安全性:

​ 提高性能:这种方式支持多个web分发服务器和多个脚本解析服务器的分布式架构,同时可以在脚本解析服务器上启动一个或者多个脚本解析守护进程来处理动态请求,可以让web服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

​ 提高安全性:API方式把应用程序的代码与核心的web服务器链接在一起,这时一个错误的API的应用程序可能会损坏其他应用程序或核心服务器,恶意的API的应用程序代码甚至可以窃取另一个应用程序或核心服务器的密钥,采用这种方式可以在很大程度上避免这个问题

3)FastCGI的不依赖于任何Web服务器的内部架构,因此即使服务器技术的变化, FastCGI依然稳定不变

4)FastCGI程序在修改php.ini配置时可以进行平滑重启加载新配置

​ 所有的配置加载都只在FastCGI进程启动时发生一次,每次修改php.ini配置文件,只需要重启FastCGI程序(php-fpm等)即可完成平滑加载新配置,已有的动态请求会继续处理,处理完成关闭进程,新来的请求使用新加载的配置和变量进行处理

5)FAST-CGI是较新的标准,架构上和CGI大为不同,是用一个驻留内存的服务进程向网站服务器提供脚本服务。像是一个常驻(long-live)型的CGI,维护的是一个进程池,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式),速度和效率比CGI大为提高,是目前的主流部署方式。

6)FastCGI的不足:

​ 因为是在内存中同时运行多进程,所以会比CGI方式消耗更多的服务器内存,每个PHP-CGI进程消耗7至25兆内存,在进行优化配置php-cgi进程池的数量时要注意系统内存,防止过量

2.5.FastCGI程序的应用领域

​ 生产环境的主流部署方式

2.6.关于CGI和FastCGI的总结

1)CGI 和 FastCGI 都只是一种通信协议规范,不是一个实体,一般说的CGI指的是用各种语言编写的能实现该功能的程序
2)CGI 程序和FastCGI程序,是指实现这两个协议的程序,可以是任何语言实现这个协议的。(PHP-CGI 和 PHP-FPM就是实现FastCGI的程序)
3)CGI程序和FastCGI程序的区别:
关于CGI程序:
CGI使外部程序与Web服务器之间交互成为可能。CGI程序运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。

关于FastCGI程序:
与CGI程序为每个请求创建一个新的进程不同,FastCGI使用持续的进程(master)来处理一连串的请求。这些进程由FastCGI服务器管理,而不是web服务器。 当进来一个请求时,web服务器把环境变量和这个页面请求通过一个socket或者一个TCP connection传递给FastCGI进程。

3.关于PHP-CGI,PHP-FPM和Spawn-FCGI

3.1.PHP-CGI是什么?

很多地方说:PHP-CGI是PHP自带的FastCGI管理器,目前还没找到最原始的出处,以我的理解和经验来看这话有点毛病,我认为应该是:使用php实现CGI协议的CGI程序,可以用来管理php解释器,如果有异议可以和我探讨下。。。

使用如下命令可以启动PHP-CGI:

1
php-cgi -b 127.0.0.1:9000

php-cgi的特点:

1)php-cgi变更php.ini配置后需重启php-cgi才能让新的配置生效,不可以平滑重启

2)直接杀死php-cgi进程php就不能运行了

3.2.关于php-fpm

PHP-FPM(FastCGI Process Manager)

针对PHP-CGI的不足,PHP-FPM和Spawn-FCGI应运而生,它们的守护进程会平滑从新生成新的子进程。

1)PHP-FPM使用PHP编写的PHP-FastCGI管理器,管理对象是PHP-CGI程序,不能说php-fpm是fastcgi进程的管理器,因为前面说了fastcgi是个协议

下载地址:http://php-fpm.org/download

早期的PHP-FPM是作为PHP源码的补丁而使用的,在PHP的5.3.2版本中直接整合到了PHP-FPM分支,目前主流的PHP5.5,PHP5.6,PHP5.7已经集成了该功能(被官方收录)

在配置时使用–enable-fpm参数即可开启PHP-FPM

2)修改php.ini之后,php-cgi进程的确是没办法平滑重启的。php-fpm对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度。

3.3.关于Spawn-FCGI

1)Spawn-FCGI是一个通用的FastCGI管理服务器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI进行FastCGI模式下的管理工作

2)Spawn-FCGI目前已经独成为一个项目,更加稳定一些,也给很多Web 站点的配置带来便利。已经有不少站点将它与nginx搭配来解决动态网页。

Spawn-FCGI的下载地址是http://redmine.lighttpd.net/projects/spawn-fcgi,目前(20190114)最新版本为1.6.4,在4年前更新的,有点凉凉的意思。。。

3.4.PHP-FPM与spawn-CGI对比

1)PHP-FPM的配置都是在php-fpm.ini的文件内,早些时候重启可以通过/usr/local/php/sbin/php-fpm reload进行,无需杀掉进程就可以完成php.ini的修改加载,被php官方收录以后需要指定参数进行重载配置,可以自己创建脚本进行管理
2)PHP-FPM控制的进程cpu回收的速度比较慢,内存分配的很均匀,比Spawn-FCGI要好,可以有效控制内存和进程,且不容易崩溃,很优秀
3)Spawn-FCGI控制的进程CPU下降的很快,而内存分配的比较不均匀。有很多进程似乎未分配到,而另外一些却占用很高。可能是由于进程任务分配的不均匀导致的。而这也导致了总体响应速度的下降。而PHP-FPM合理的分配,导致总体响应的提到以及任务的平均。

(摘录的,暂未实际验证)

4.PHP运行的5种模式

php目前比较常见的五大运行模式:包括cli、cgi 、fast-cgi、isapi、apache模块的DLL

4.1.cli模式

cli模式就是php的命令行运行模式,

例如:在linux下经常使用 “php -m”查找PHP安装了那些扩展就是PHP命令行运行模式

其他的可以输入php -h查看下

4.2.CGI模式

比较经典的使用方法,使用CGI程序将浏览器,web服务器,php解释器连接起来进行数据交换的工具,目前主要用来做开发或调试

CGI方式在遇到连接请求(用户 请求)先要创建cgi的子进程,激活一个CGI进程,然后处理请求,处理完后结束这个子进程。这就是fork-and-execute模式。所以用cgi方式的服务器有多少连接请求就会有多少cgi子进程,子进程反复加载是cgi性能低下的主要原因。都会当用户请求数量非常多时,会大量挤占系统的资源如内 存,CPU时间等,造成效能低下。

4.3.FastCGI模式

目前主流的使用方式,比CGI模式的工具效率高很多,大量用于分布式高并发的环境中

在Linux中,nginx加php-fpm是最主流的使用方式

4.4.ISAPI运行模式

ISAPI即Internet Server Application Program Interface,是微软提供的一套面向Internet服务的API接口,一个ISAPI的DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个DLL里设置多个用户请求处理函数,此外,ISAPI的DLL应用程序和WWW服务器处于同一个进程中,效率要显著高于CGI。(由于微软的排他性,只能运行于windows环境)

4.5.apache模块运行模式

此运行模式可以在Linux和windows环境下使用Apache,他们的共同点都是用 LoadModule 来加载相关模块,有两种类型

4.5.1.mod_php模块

Apache调用php的相关模块(php5_module),也就是把php作为apache的一个子模块来运行

当通过web访问php文件时,apache就会调用php5_module通过sapi将数据传给php解析器来解析php代码,整个过程如下图:

img

从上面图中,可以看出:

1)sapi就是这样的一个中间过程,SAPI提供了一个和外部通信的接口,有点类似于socket,使得PHP可以和其他应用进行交互数据(apache,nginx等)。

php默认提供了很多种SAPI,常见的提供给apache和nginx的php5_module、CGI、FastCGI,给IIS的ISAPI,以及Shell的CLI。

所以,以上的apache调用php执行的过程如下:

1
apache -> httpd -> php5_module -> sapi -> php

2)apache每接收一个请求,都会产生一个进程来连接php通过sapi来完成请求,如果用户过多,并发数过多,服务器就会承受不住了。

3)把mod_php编进apache时,出问题时很难定位是php的问题还是apache的问题,而且PHP是与Web服务器一起启动并运行的,当php模块出现问题可能会导致Apache一同挂掉

4.5.2.mod_cgi模块

在此种模式中Apache启动加载mod_cgi模块,使用CGI调用管理动态的php请求

更高级的是mod_fcgid模块,是apache的fastcgi实现,性能提高,在apache的2.4以后的版本中得到支持。

总结:

1)mod_php是apache的内置php解释模块,使用prefork方式,不需要额外的进程来做通讯和应用解释,显然mod_php比mod_cgi这样方式性能要好得多

2)缺点是把应用和HTTP服务器绑定在了一起,当php模块出现问题可能会导致Apache一同挂掉

3)另外每个Apache进程都需要加载mod_php而不论这个请求是处理静态内容还是动态内容,这样导致浪费内存,效率下降,

4)php.ini文件的变更需要重新启动apache服务器才能生效,这使得无法进行平滑配置变更。

4.6.总结一下

随着技术的不断升级,单纯的Apache加php模块的方式已不再主流,而是替换为Apache加php_cgi,以及后来的php_fcgi和nginx加php-fpm的方法,可以看下图:

img

5.参考文章:

CGI、FastCGI和PHP-FPM关系图解:https://www.awaimai.com/371.html

关于CGI 和 PHP-FPM需要弄清的:http://www.cleey.com/blog/single/id/848.html

浅谈PHP fastcgi和php-fpm:https://www.jianshu.com/p/d095cbcbcf1b

php五大运行模式CGI,FAST-CGI,CLI,ISAPI,APACHE模式浅谈:https://www.cnblogs.com/XACOOL/p/5619650.html

==== 完毕,呵呵呵呵 ====

感恩,互助,分享。

Celery简介以及与Redis的集成使用

[TOC]

Celery

1.什么是Clelery

Celery 是一个专注于实时处理和任务调度的分布式任务队列, 同时提供操作和维护分布式系统所需的工具.

Celery架构

2522678-d369b6a4c4265225

Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。

消息中间件

Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等

任务执行单元

Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。

任务结果存储

Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等

版本支持情况

1
2
3
4
5
6
7
8
9
10
11
12
13
Celery version 4.0 runs on
Python ❨2.7, 3.4, 3.5❩
PyPy ❨5.4, 5.5❩
This is the last version to support Python 2.7, and from the next version (Celery 5.x) Python 3.5 or newer is required.

If you’re running an older version of Python, you need to be running an older version of Celery:

Python 2.6: Celery series 3.1 or earlier.
Python 2.5: Celery series 3.0 or earlier.
Python 2.4 was Celery series 2.2 or earlier.

Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform.

2.使用场景

异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等

定时任务:定时执行某件事情,比如每天数据统计

3.Celery的安装配置

pip install celery

消息中间件:RabbitMQ/Redis

app=Celery(‘任务名’,backend=’xxx’,broker=’xxx’)

4.Celery执行异步任务

基本使用

创建项目celerytest

创建py文件:celery_app_task.py

1
2
3
4
5
6
7
8
9
10
11
12
import celery
import time
# broker='redis://127.0.0.1:6379/2' 不加密码
backend='redis://:123456@127.0.0.1:6379/1'
broker='redis://:123456@127.0.0.1:6379/2'
cel=celery.Celery('test',backend=backend,broker=broker)
@cel.task
def add(x,y):
return x+y



创建py文件:add_task.py,添加任务

1
2
3
4
from celery_app_task import add
result = add.delay(4,5)
print(result.id)

创建py文件:run.py,执行任务,或者使用命令执行:celery worker -A celery_app_task -l info

注:windows下:celery worker -A celery_app_task -l info -P eventlet

1
2
3
4
5
from celery_app_task import cel
if __name__ == '__main__':
cel.worker_main()
# cel.worker_main(argv=['--loglevel=info')

创建py文件:result.py,查看任务执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from celery.result import AsyncResult
from celery_app_task import cel

async = AsyncResult(id="e919d97d-2938-4d0f-9265-fd8237dc2aa3", app=cel)

if async.successful():
result = async.get()
print(result)
# result.forget() # 将结果删除
elif async.failed():
print('执行失败')
elif async.status == 'PENDING':
print('任务等待中被执行')
elif async.status == 'RETRY':
print('任务异常后正在重试')
elif async.status == 'STARTED':
print('任务已经开始被执行')

执行 add_task.py,添加任务,并获取任务ID

执行 run.py ,或者执行命令:celery worker -A celery_app_task -l info

执行 result.py,检查任务状态并获取结果

多任务结构

1
2
3
4
5
6
7
8
pro_cel
├── celery_task# celery相关文件夹
│ ├── celery.py # celery连接和配置相关文件,必须叫这个名字
│ └── tasks1.py # 所有任务函数
│ └── tasks2.py # 所有任务函数
├── check_result.py # 检查结果
└── send_task.py # 触发任务

celery.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from celery import Celery

cel = Celery('celery_demo',
broker='redis://127.0.0.1:6379/1',
backend='redis://127.0.0.1:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_task.tasks1',
'celery_task.tasks2'
])

# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False

tasks1.py

1
2
3
4
5
6
7
8
import time
from celery_task.celery import cel

@cel.task
def test_celery(res):
time.sleep(5)
return "test_celery任务结果:%s"%res

tasks2.py

1
2
3
4
5
6
7
8
import time
from celery_task.celery import cel
@cel.task
def test_celery2(res):
time.sleep(5)
return "test_celery2任务结果:%s"%res


check_result.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from celery.result import AsyncResult
from celery_task.celery import cel

async = AsyncResult(id="08eb2778-24e1-44e4-a54b-56990b3519ef", app=cel)

if async.successful():
result = async.get()
print(result)
# result.forget() # 将结果删除,执行完成,结果不会自动删除
# async.revoke(terminate=True) # 无论现在是什么时候,都要终止
# async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终止。
elif async.failed():
print('执行失败')
elif async.status == 'PENDING':
print('任务等待中被执行')
elif async.status == 'RETRY':
print('任务异常后正在重试')
elif async.status == 'STARTED':
print('任务已经开始被执行')


send_task.py

1
2
3
4
5
6
7
8
9
10
from celery_task.tasks1 import test_celery
from celery_task.tasks2 import test_celery2

# 立即告知celery去执行test_celery任务,并传入一个参数
result = test_celery.delay('第一个的执行')
print(result.id)
result = test_celery2.delay('第二个的执行')
print(result.id)


添加任务(执行send_task.py),开启work:celery worker -A celery_task -l info -P eventlet,检查任务执行结果(执行check_result.py)

5.Celery执行定时任务

设定时间让celery执行一个任务

add_task.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from celery_app_task import add
from datetime import datetime

# 方式一
# v1 = datetime(2019, 2, 13, 18, 19, 56)
# print(v1)
# v2 = datetime.utcfromtimestamp(v1.timestamp())
# print(v2)
# result = add.apply_async(args=[1, 3], eta=v2)
# print(result.id)

# 方式二
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
from datetime import timedelta
time_delay = timedelta(seconds=10)
task_time = utc_ctime + time_delay

# 使用apply_async并设定时间
result = add.apply_async(args=[4, 3], eta=task_time)
print(result.id)


类似于contab的定时任务

多任务结构中celery.py修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab

cel = Celery('tasks', broker='redis://127.0.0.1:6379/1', backend='redis://127.0.0.1:6379/2', include=[
'celery_task.tasks1',
'celery_task.tasks2',
])
cel.conf.timezone = 'Asia/Shanghai'
cel.conf.enable_utc = False

cel.conf.beat_schedule = {
# 名字随意命名
'add-every-10-seconds': {
# 执行tasks1下的test_celery函数
'task': 'celery_task.tasks1.test_celery',
# 每隔2秒执行一次
# 'schedule': 1.0,
# 'schedule': crontab(minute="*/1"),
'schedule': timedelta(seconds=2),
# 传递参数
'args': ('test',)
},
# 'add-every-12-seconds': {
# 'task': 'celery_task.tasks1.test_celery',
# 每年4月11号,8点42分执行
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'args': (16, 16)
# },
}


启动一个beat:celery beat -A celery_task -l info

启动work执行:celery worker -A celery_task -l info -P eventlet

6.Django中使用Celery

在项目目录下创建celeryconfig.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import djcelery
djcelery.setup_loader()
CELERY_IMPORTS=(
'app01.tasks',
)
#有些情况可以防止死锁
CELERYD_FORCE_EXECV=True
# 设置并发worker数量
CELERYD_CONCURRENCY=4
#允许重试
CELERY_ACKS_LATE=True
# 每个worker最多执行100个任务被销毁,可以防止内存泄漏
CELERYD_MAX_TASKS_PER_CHILD=100
# 超时时间
CELERYD_TASK_TIME_LIMIT=12*30


在app01目录下创建tasks.py

1
2
3
4
5
6
7
8
from celery import task
@task
def add(a,b):
with open('a.text', 'a', encoding='utf-8') as f:
f.write('a')
print(a+b)


视图函数views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django.shortcuts import render,HttpResponse
from app01.tasks import add
from datetime import datetime
def test(request):
# result=add.delay(2,3)
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
from datetime import timedelta
time_delay = timedelta(seconds=5)
task_time = utc_ctime + time_delay
result = add.apply_async(args=[4, 3], eta=task_time)
print(result.id)
return HttpResponse('ok')


settings.py

1
2
3
4
5
6
7
8
9
#INSTALLED_APPS = [
# 'djcelery',
# 'app01'
#]

from djagocele import celeryconfig
BROKER_BACKEND='redis'
BOOKER_URL='redis://127.0.0.1:6379/1'
CELERY_RESULT_BACKEND='redis://127.0.0.1:6379/2'

Redis持久化

[TOC]

Redis支持RDB和AOF两种持久化机制,持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数 据恢复。理解掌握持久化机制对于Redis运维非常重要

1.RDB持久化

RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发

1)触发机制

手动触发分别对应save和bgsave命令

·save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用

·bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短

2)自动触发RDB的持久

1)使用save相关配置,如“save m n”。表示m秒内数据集存在n次修改 时,自动触发bgsave。

2)如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点,更多细节见6.3节介绍的复制原理。

3)执行debug reload命令重新加载Redis时,也会自动触发save操作。

4)默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则 自动执行bgsave。

bgsave是主流的触发RDB持久化方式

img

1)执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进 程,如RDB/AOF子进程,如果存在bgsave命令直接返回。

2)父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通 过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒

3)父进程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他命令。

4)子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后 对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的 时间,对应info统计的rdb_last_save_time选项。

5)进程发送信号给父进程表示完成,父进程更新统计信息,具体见 info Persistence下的rdb_*相关选项。

RDB文件的处理

保存:RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配 置指定。可以通过执行config set dir{newDir}和config set dbfilename{newFileName}运行期动态执行,当下次运行时RDB文件会保存到新目录。

RDB的优缺点

RDB的优点:

·RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据 快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份, 并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。

·Redis加载RDB恢复数据远远快于AOF的方式。

RDB的缺点:

·RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运 行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。

·RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式 的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。

针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。

2.AOF持久化

AOF(append only file)持久化:以独立日志的方式记录每次写命令, 重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用 是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式

1)使用AOF

开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件名 通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致,通过dir配置指定。AOF的工作流程操作:命令写入 (append)、文件同步(sync)、文件重写(rewrite)、重启加载 (load)

img

1)所有的写入命令会追加到aof_buf(缓冲区)中。

2)AOF缓冲区根据对应的策略向硬盘做同步操作。

AOF为什么把命令追加到aof_buf中?Redis使用单线程响应命令,如 果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负 载。先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡

3)随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

重写后的AOF文件为什么可以变小?有如下原因:

1)进程内已经超时的数据不再写入文件。

2)旧的AOF文件含有无效命令,如del key1、hdel key2、srem keys、set a111、set a222等。重写使用进程内数据直接生成,这样新的AOF文件只保

留最终数据的写入命令。

3)多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢 出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。

AOF重写降低了文件占用空间,除此之外,另一个目的是:更小的AOF 文件可以更快地被Redis加载

AOF重写过程可以手动触发和自动触发:

·手动触发:直接调用bgrewriteaof命令。

·自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机

·auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认 为64MB。

·auto-aof-rewrite-percentage:代表当前AOF文件空间 (aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值。

自动触发时机=aof_current_size>auto-aof-rewrite-minsize&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage

其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。

img

4)当Redis服务器重启时,可以加载AOF文件进行数据恢复。

img

流程说明:

1)AOF持久化开启且存在AOF文件时,优先加载AOF文件,打印如下日志:

* DB loaded from append only file: 5.841 seconds

2)AOF关闭或者AOF文件不存在时,加载RDB文件,打印如下日志:

* DB loaded from disk: 5.586 seconds

3)加载AOF/RDB文件成功后,Redis启动成功。

4)AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。

本章重点回顾

1)Redis提供了两种持久化方式:RDB和AOF。

2)RDB使用一次性生成内存快照的方式,产生的文件紧凑压缩比更 高,因此读取RDB恢复速度更快。由于每次生成RDB开销较大,无法做到实时持久化,一般用于数据冷备和复制传输。

3)save命令会阻塞主线程不建议使用,bgsave命令通过fork操作创建子 进程生成RDB避免阻塞。

4)AOF通过追加写命令到文件实现持久化,通过appendfsync参数可以 控制实时/秒级持久化。因为需要不断追加写命令,所以AOF文件体积逐渐变大,需要定期执行重写操作来降低文件体积。

5)AOF重写可以通过auto-aof-rewrite-min-size和auto-aof-rewritepercentage参数控制自动触发,也可以使用bgrewriteaof命令手动触发。

6)子进程执行期间使用copy-on-write机制与父进程共享内存,避免内 存消耗翻倍。AOF重写期间还需要维护重写缓冲区,保存新的写入命令避免数据丢失。

7)持久化阻塞主线程场景有:fork阻塞和AOF追加阻塞。fork阻塞时间 跟内存量和系统有关,AOF追加阻塞说明硬盘资源紧张。

8)单机下部署多个实例时,为了防止出现多个子进程执行重写操作, 建议做隔离控制,避免CPU和IO资源竞争。

转自:https://www.jianshu.com/p/d3ba7b8ad964

MinIO

[TOC]

MinIO

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

docker install

官方强烈推荐使用docker安装。轻量服务嘛。

1
2
docker pull minio/minio
docker run -p 9000:9000 minio/minio server /data

其他方式参考官方咯, 安装由于很简单,这里就不写了。

MinIO SDK

minio有支持各种语言的SDK,你甚至可以用它来链接aws的s3, 下面一Python为例,客户端连接服务。

首先安装minio模块

1
pip install minio

链接minio server

1
2
3
4
5
6
7
from minio import Minio
from minio.error import ResponseError

minioClient = Minio('minio.domain.com',
access_key='xxxxx',
secret_key='xxxxx',
secure=True)

链接aws s3 server

1
2
3
4
5
6
7
from minio import Minio
from minio.error import ResponseError

s3Client = Minio('s3.amazonaws.com',
access_key='YOUR-ACCESSKEYID',
secret_key='YOUR-SECRETACCESSKEY',
secure=True)

操作的话主要分为四类: Bucket operationsObject operationsPresigned operationsBucket policy ,怎么样? 是不是跟aws的s3一样一样的。具体参考 python-client-api-reference

infiniband安装配置

[TOC]

基本概念

RDMA

Remote Direct Memory Access: 为了解决网络传输中服务器端数据处理的延迟而产生,通过网络把资料直接传入计算机的存储区,将数据从一个系统快速移动到远程系统存储器中,而不对操作系统造成任何影响,不需要用到多少计算机的处理功能。

IB

infiniband,是一个用于高性能计算的计算机网络通信标准,它具有极高的吞吐量和极低的延迟,用于计算机与计算机之间的数据互连。InfiniBand也用作服务器与存储系统之间的直接或交换互连,以及存储系统之间的互连,它用到了RDMA技术。

IPoIB

Internet Protocol over InfiniBand,利用物理IB网络通过IP协议进行连接,并进行数据传输。性能稍低于rdma,但是比普通的千兆网要快很多。目前在集群nfs上测试,可以10Gb/s

安装infiniband

安装包

infiniband驱动模块是需要在所有infiniband节点安装。

驱动模块,以下软件包之一:

1
2
以下软件包之一:infinipath-psm
、libcxgb3、libcxgb4、libehca、libipathverbs、libmthca、libmlx4、libmlx5、libnes 及 libocrdma

libibverbs 提供InfiniBand Verbs API

1
yum install libibverbs
1
2
yum install rdma  #RDMA栈内核初始化
systemctl start rdma

opensm

opensm可以搭建管理子网络。

1
yum install opensm

测试工具

infiniband-diags 或 ibutils 为 InfiniBand 结构管理提供大量有用的调试工具。这些工具只
为 iWARP 或 RoCE 提供有限功能,因为大多数工具可在 InfiniBank 链接层工作,但无法在 Verbs API 层
使用。

qperf和perftest 用于各种 RDMA 通讯类型的性能测试应用程序。

------------------------------------
查看所有node的配置信息
------------------------------------
    iblinkinfo 
        CA: node5 mlx5_0:
              0x248a07030058f544      1    1[  ] ==( 4X      25.78125 Gbps Active/  LinkUp)==>      11   34[  ] "MF0;switch-ab29a6:MSB7700/U1" ( )
        CA: node6 mlx5_0:
              0x248a07030058f650      8    1[  ] ==( 4X      25.78125 Gbps Active/  LinkUp)==>      11   32[  ] "MF0;switch-ab29a6:MSB7700/U1" ( )
                  11   32[  ] ==( 4X      25.78125 Gbps Active/  LinkUp)==>       8    1[  ] "node6 mlx5_0" ( )
                  11   34[  ] ==( 4X      25.78125 Gbps Active/  LinkUp)==>       1    1[  ] "node5 mlx5_0" ( )

------------------------------------
ibping test RDMA
------------------------------------
    首先启动opensm服务
    然后查看每个node节点的infiniband信息 ibv_devinfo 或者 ibstat都行
    [root@master rdma]#ibv_devinfo && ibstat
        hca_id: mlx5_0
            transport:          InfiniBand (0)
            fw_ver:             12.17.1010
            node_guid:          248a:0703:0058:f504
            sys_image_guid:         248a:0703:0058:f504
            vendor_id:          0x02c9
            vendor_part_id:         4115
            hw_ver:             0x0
            board_id:           MT_2180110032
            phys_port_cnt:          1
                port:   1
                    state:          PORT_ACTIVE (4)
                    max_mtu:        4096 (5)
                    active_mtu:     4096 (5)
                    sm_lid:         2
                    port_lid:       2
                    port_lmc:       0x00
                    link_layer:     InfiniBand

        CA 'mlx5_0'
            CA type: MT4115
            Number of ports: 1
            Firmware version: 12.17.1010
            Hardware version: 0
            Node GUID: 0x248a07030058f504
            System image GUID: 0x248a07030058f504
            Port 1:
                State: Active
                Physical state: LinkUp
                Rate: 100
                Base lid: 2
                LMC: 0
                SM lid: 2
                Capability mask: 0x2659e84a
                Port GUID: 0x248a07030058f504
                Link layer: InfiniBand

    在服务端
        ibping -S -C mlx5_0 -P 1 -L 2
        ibping -S -C mlx5_0 -P 1 -G 0x248a07030058f504
        -S 以server启动
        -C 就是指明CA的名称
        -P 指明port
        -L 指明port_lid/Base lid
        -G 指明GUID

    在客户端
        ibping -c 10000 -f -C mlx5_0 -P 1 -L 2
        指明你要ping的服务端的 -C -P -G -L等
        结果如下:
        [root@node2 ~]#ibping -c 10000 -f -C mlx5_0 -P 1 -L 2

        --- master.(none) (Lid 2) ibping statistics ---
        10000 packets transmitted, 10000 received, 0% packet loss, time 1364 ms
        rtt min/avg/max = 0.034/0.136/0.849 ms

IPoIB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
------------------------------------
IPoIB 配置
------------------------------------
#cat /etc/sysconfig/network-scripts/ifcfg-mlx5_ib0
# Generated by dracut initrd

DEVICE=mlx5_ib0 #这里必须对应/etc/udev/rules.d/70-persistent-ipoib.rules 中的 NAME="mlx5_ib0"
TYPE=InfiniBand #这里必须是InfiniBand
ONBOOT=yes
HARDWAR=80:00:00:66:fe:80:00:00:00:00:00:00:24:8a:07:03:00:58:f5:04
BOOTPROTO=none
IPADDR=172.16.20.10
NETMASK=255.255.255.0
BROADCAST=172.16.20.255
NAME=mlx5_ib0 #这里对应ifconfig中的网卡名称
MTU=65520 #设置连接模式最大MTU
STARTMODE='auto'
NM_CONTROLLED=no
CONNECTED_MODE=yes #设置连接模式

#ifup mlx5_ib0

通过IPoIB配置nfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
服务端:
yum install nfs-utils
modprobe svcrdma
systemctl start nfs
echo rdma 20049 > /proc/fs/nfsd/portlist
cat /proc/fs/nfsd/portlist
rdma 20049
udp 2049
tcp 2049

客户端:
modprobe xprtrdma
mount -o rdma,port=20049 172.16.20.10:/atlas /atlas
cat /etc/fstab
172.16.20.10:/atlas /atlas nfs defaults 0 0
/dev/sda1 /scratch ext4 defaults 0 0

mount | grep my_directory
...proto=rdma,port=20049,...

slurm 安装文档

[TOC]

SLURM是一款集群资源管理调度软件,适合深度学习集群管理调度.

清理软件

卸载已安装的软件

1
2
yum remove -y munge*
yum remove -y slurm*

清除自建文件和目录

  • log文件
  • /var/spool/ 下的目录和文件
    注意检查文件的权属

在master节点安装MariaDB 数据库

1
yum install mariadb-server mariadb-devel -y

安装 munge

1. 在 master 节点创建 UID 和 GID

1
2
3
4
5
6
export MUNGEUSER=1050
groupadd -g $MUNGEUSER munge
useradd -m -c "MUNGE Uid 'N' Gid Emporium" -d /var/lib/munge -u $MUNGEUSER -g munge -s /sbin/nologin munge
export SlurmUSER=1051
groupadd -g $SlurmUSER slurm
useradd -m -c "Slurm workload manager" -d /var/lib/slurm -u $SlurmUSER -g slurm -s /bin/bash slurm

2. 在所有计算节点创建相同的UID 和 GID

进行 munge 安装

1. 先安装最新的 epel-release RPM

1
yum install epel-release

2. 安装 MUNGE RPM 包

1
yum install munge munge-libs munge-devel -y

3. 检查加密方式

1
2
munge -C
munge -M

4. 在mater 节点创建所有 node 需要的秘钥。

1
2
yum install rng-tool -y
/usr/sbin/create-munge-key -r
1
2
3
dd if=/dev/urandom bs=1 count=1024 > /etc/munge/munge.key
chown munge: /etc/munge/munge.key
chmod 400 /etc/munge/munge.key

5. 将 /etc/munge/munge.key 拷贝到其他节点

1
2
3
export NODE=172.16.10.18
scp /etc/munge/munge.key root@login:/etc/munge
scp /etc/munge/munge.key root@node1:/etc/munge

6. 在所有节点上设置权限和所属 :

1
2
chown -R munge: /etc/munge/ /var/log/munge/
chmod 0700 /etc/munge/ /var/log/munge/

7. 在所有节点上运行 munge:

1
2
systemctl enable munge
systemctl start munge

8. 测试

1
2
3
4
munge -n
munge -n | unmunge # Displays information about the MUNGE key
munge -n | ssh somehost unmunge
remunge


二、安装 Slurm

1. 先安装一下支持的软件包:

1
2
3
yum install rpm-build gcc openssl openssl-devel pam-devel numactl numactl-devel \
hwloc hwloc-devel lua lua-devel readline-devel rrdtool-devel ncurses-devel \
gtk2-devel man2html libibmad libibumad perl-Switch perl-ExtUtils-MakeMaker

2. 下载最新的slurm 版本到存储节点NFS文件下

1
2
3
cd /gensoft/slurm-rpms
export VER=17.02.0
wget http://www.schedmd.com/download/latest/slurm-17.02.0.tar.bz2

3. 在所有节点上编译并安装

1
2
3
4
5
6
7
rpmbuild -ta slurm-$VER.tar.bz2
cd /root/rpmbuild/RPMS/x86_64
yum install slurm-$VER*rpm slurm-devel-$VER*rpm slurm-munge-$VER*rpm \
slurm-perlapi-$VER*rpm slurm-plugins-$VER*rpm slurm-torque-$VER*rpm \
slurm-seff-$VER*rpm
# OR
yum install slurm*rpm

4. 配置 slurm

访问网站 http://slurm.schedmd.com/configurator.html 进行配置填写,完成后下载文件

将配置文件复制到 /etc/slurm

1
cd /etc/slurm

5. 将配置文件复制到其他节点

1
2
scp slurm.conf root@node1.com/etc/slurm/slurm.conf
scp slurm.conf root@logo.com/etc/slurm/slurm.conf

6. 配置 master 节点

1
2
3
4
5
6
chown slurm:slurm /var/spool
mkdir /var/spool/slurmctld
chown slurm: /var/spool/slurmctld
chmod 755 /var/spool/slurmct
touch /var/log/slurmctld.log
chown slurm: /var/log/slurmctld.log

7. 配置其他节点

1
2
3
4
5
mkdir /var/spool/slurmd
chown slurm: /var/spool/slurmd
chmod 755 /var/spool/slurmd
touch /var/log/slurmd.log
chown slurm: /var/log/slurmd.log

8. 确认 master 节点配置是否正确

1
slurmd -C

9. 如果像下面这样表示正确

1
2
ClusterName=(null) NodeName=buhpc3 CPUs=4 Boards=1 SocketsPerBoard=2 CoresPerSocket=2 ThreadsPerCore=1 RealMemory=7822 TmpDisk=45753
UpTime=13-14:27:52

三、启动 slurm

1. 关闭所有计算节点防火墙

1
2
systemctl stop firewalld
systemctl disable firewalld

2. 在master节点上打开默认的 slurm 使用端口

1
2
3
4
5
6
7
firewall-cmd --permanent --zone=public --add-port=6817/udp
firewall-cmd --permanent --zone=public --add-port=6817/tcp
firewall-cmd --permanent --zone=public --add-port=6818/tcp
firewall-cmd --permanent --zone=public --add-port=6818/tcp
firewall-cmd --permanent --zone=public --add-port=7321/tcp
firewall-cmd --permanent --zone=public --add-port=7321/tcp
firewall-cmd --reload

3. 如果防火墙设置不成功,在所有节点上检查时钟是否同步

1
2
3
4
yum install ntp -y
chkconfig ntpd on
ntpdate pool.ntp.org
systemctl start ntpd

4. 如果时钟同步,在所有计算节点上启动 slurm:

1
2
3
systemctl enable slurmd.service
systemctl start slurmd.service
systemctl status slurmd.service

5. 在master 节点上启动 slurm

1
2
3
systemctl enable slurmctld.service
systemctl start slurmctld.service
systemctl status slurmctld.service

6. 检查运行情况

1
sinfo

7. 如果有问题,查看日志

1
2
Compute node bugs: tail /var/log/slurmd.log
Server node bugs: tail /var/log/slurmctld.log

8. 如果节点 DOWN 了更改 node 状态为 IDLE

1
2
3
scontrol: update NodeName=node1 State=DOWN Reason="undraining"
scontrol: update NodeName=node1 State=RESUME
scontrol:

参考

http://www.slothparadise.com/how-to-install-slurm-on-centos-7-cluster/
http://blog.csdn.net/datuqiqi/article/details/50827040
http://blog.csdn.net/kongxx/article/details/48173829
http://wildflower.diablonet.net/~scaron/slurmsetup.html
https://slurm.schedmd.com/slurm_ug_2011/Basic_Configuration_Usage.pdf

高堂

[TOC]

高堂

高堂:原意是房屋的正室厅堂,后来也指对父母的敬称。

《后汉书·马融传》:“常坐高堂,施绛纱帐,前授生徒,后列女乐。

例:

君不见,高堂明镜悲白发,朝如青丝暮成雪。

一拜天地,二拜高堂,夫妻对拜,送入洞房

1
2
3
4
一要感恩天地,珍惜美好姻缘
二要感恩父母,共同孝敬老人
三要互相感恩,珍惜对方,尊重对方,
最后礼拜结束入洞房。

openhpc

[TOC]

什么是OpenHPC

OpenHPC是一个聚合了众多常见部署应用和管理的系统框架.简单来说就是,集群中想要部署什么应用只需要一次制作好,集群中各个节点运行相同的image,完全统一,适合做高性能计算集群的搭建 .官方原话如下:

1
Welcome to the OpenHPC site. OpenHPC is a collaborative, community effort that initiated from a desire to aggregate a number of common ingredients required to deploy and manage High Performance Computing (HPC) Linux clusters including provisioning tools, resource management, I/O clients, development tools, and a variety of scientific libraries. Packages provided by OpenHPC have been pre-built with HPC integration in mind with a goal to provide re-usable building blocks for the HPC community. 

我这是在CentOS7上面搭建的,官方给出了相应的安装文档,不过里面东西很多,没告诉你为什么这么做,照着做也会出很多问题.下面我就结合自己制作的操作过程记录如下:

1.master配置

a.master节点配置本地yum源

首先创建目录存放节点所需文件,以及配置好yum源,我在集群制作了一个本地yum源,方便快速下载安装.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mkdir -pv /atlas/os_images/compute_node_v0.2.0
mkdir /atlas/os_images/tftpboot_v0.2.0

cat /etc/yum.repos.d/base.repo
[development]
name=development
baseurl=ftp://172.16.10.10/centos7.2
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

[epel]
name=epel
baseurl=ftp://172.16.10.10/epel
gpgcheck=0

[local-slurm]
name=slurm
baseurl=ftp://172.16.10.10/slurm
gpgcheck=0

yum clean all
yum repolist

b.配置master的openhpc服务

在master上安装所需软件

1
2
3
4
5
6
7
8
9
10
yum -y groupinstall ohpc-base
yum -y groupinstall ohpc-warewulf
yum -y groupinstall "InfiniBand Support"
yum -y install infinipath-psm

systemctl enable ntpd.service #节点与master之间通信,时间准确度要求比较高.
systemctl restart ntpd
systemctl start rdma
systemctl status rdma

配置pxe启动目录,以及所使用的网卡.最后启动pxe服务

1
2
3
4
5
6
7
8
9
10
vim /etc/warewulf/provision.conf
network device = enp129s0f0 #master上面网卡名
tftpdir = /atlas/os_images/tftpboot_v0.2.0
vim /etc/xinetd.d/tftp
server_args = -s /atlas/os_images/tftpboot_v0.2.0
disable = no
systemctl restart xinetd
systemctl start tftp.socket
systemctl start tftp.service

系统启动后会针对系统完成一些配置,而这些配置是通过http从master服务器上面down下来的.down下来的文件放在 /warewulf 目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
export MODFILE=/etc/httpd/conf.d/warewulf-httpd.conf
perl -pi -e "s/cgi-bin>\$/cgi-bin>\n Require all granted/" $MODFILE
perl -pi -e "s/Allow from all/Require all granted/" $MODFILE
perl -ni -e "print unless /^\s+Order allow,deny/" $MODFILE


cat /etc/httpd/conf.d/warewulf-httpd.conf
LoadModule perl_module modules/mod_perl.so
PerlSwitches -w
PerlSwitches -T
PerlSwitches -I/var/www/stage/cgi-bin

# This is disabled as RHEL6 perl_mod seems to be missing this support
#PerlPreConnectionHandler Apache2::Reload

Alias /WW/static /usr/share/warewulf/www

ScriptAlias /WW/file /usr/libexec/warewulf/cgi-bin/file.pl
ScriptAlias /WW/script /usr/libexec/warewulf/cgi-bin/script.pl
ScriptAlias /WW/nodeconfig /usr/libexec/warewulf/cgi-bin/nodeconfig.pl
ScriptAlias /WW/vnfs /usr/libexec/warewulf/cgi-bin/vnfs.pl

<Directory /usr/libexec/warewulf/cgi-bin>
Require all granted
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
</Directory>

<Directory /usr/share/warewulf/www>
Options Indexes MultiViews
AllowOverride None
Require all granted
</Directory>

集群针对不同节点的mac地址设置不同的IP,主机名等信息都是保存在数据库中,因此要用到mariadb

1
2
3
4
5
systemctl enable mariadb.service
systemctl restart mariadb
systemctl enable httpd.service
systemctl restart httpd

2.制作node节点所需配置

首先要制作的是node节点的系统文件,然后再安装所需要的应用

安装系统文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vim  /usr/libexec/warewulf/wwmkchroot/centos-7.tmpl
YUM_MIRROR="ftp://172.16.10.10/centos7.2" #这里是启动最小系统所必须用到的软件安装源
#如果不修改这个,系统会从官方源去下载,由于我自己做这个要反复重新安装测试.为了快速所以这样设置
#如果要在线下载,由于centos系统已经到7.3了,上面原有了路径要自己更新

export CHROOT=/atlas/os_images/compute_node_v0.2.0
wwmkchroot centos-7 $CHROOT #这一步就是将系统文件安装到$CHROOT中

cp -p /etc/resolv.conf $CHROOT/etc/resolv.conf

cd /atlas/os_images/compute_node_v0.2.0/etc/yum.repos.d/
rm -f CentOS-*
cat base.repo
cat OpenHPC.repo.bak
#这里我同样把节点安装软件的源改到了本地,方便快捷(包括openhpc的也已经下载到了本地)

接下来就安装节点所需应用了

安装常用基础应用

1
2
3
4
5
6
7
yum -y --installroot=$CHROOT install ntp kernel gcc make grub2-tools environment-modules
yum -y --installroot=$CHROOT groupinstall "InfiniBand Support"
yum -y --installroot=$CHROOT install infinipath-psm
chroot $CHROOT systemctl enable rdma
chroot $CHROOT systemctl enable ntpd
echo "server 172.16.10.10" >> $CHROOT/etc/ntp.conf #这个IP是对应master的IP,同步时间用

安装munge

配置安装munge,为后面slurm安装做好基础准备,这里注意几点,muster和node的munge和slurm用户名的id号码要一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
export MUNGEUSER=1050
groupadd -g $MUNGEUSER munge
useradd -m -c "MUNGE Uid 'N' Gid Emporium" -d /var/lib/munge -u $MUNGEUSER -g munge -s /sbin/nologin munge
export SlurmUSER=1051
groupadd -g $SlurmUSER slurm
useradd -m -c "Slurm workload manager" -d /var/lib/slurm -u $SlurmUSER -g slurm -s /bin/bash slurm
cat /etc/passwd |grep munge
cat /etc/passwd |grep slurm
cat /etc/group |grep slurm
cat /etc/group |grep munge
yum install munge munge-libs munge-devel -y
dd if=/dev/urandom bs=1 count=1024 > /etc/munge/munge.key
chown munge: /etc/munge/munge.key
chmod 400 /etc/munge/munge.key
systemctl enable munge
systemctl start munge
chroot $CHROOT groupadd -g 1050 munge
chroot $CHROOT useradd -m -c "MUNGE Uid 'N' Gid Emporium" -d /var/lib/munge -u 1050 -g munge -s /sbin/nologin munge
chroot $CHROOT groupadd -g 1051 slurm
chroot $CHROOT useradd -m -c "Slurm workload manager" -d /var/lib/slurm -u 1051 -g slurm -s /bin/bash slurm
yum install --installroot=$CHROOT munge munge-libs munge-devel -y
cp -a /etc/munge/munge.key $CHROOT/etc/munge/munge.key
chroot $CHROOT systemctl enable munge
chroot $CHROOT chown -R munge: /etc/munge/ /var/log/munge/
chroot $CHROOT chmod 0700 /etc/munge/ /var/log/munge/
vim /etc/warewulf/vnfs.conf #这里注意一下,好多服务的日志都会记录在/var/log目录下
#exclude += /var/log/* #这个exclude 就是制作node镜像的时候那些目录不要做进去,很明显我们需要这个目录,所以要屏蔽掉他.不然munge服务会启动不成功.
#exclude += /usr/src #这个在后面nvidia显卡安装驱动的时候需要用到dkms
#hybridize += /usr/lib/locale #字符集编码格式需要用到该文件目录

安装slurm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
tar xf slurm.tar
cd rpmbuild/RPMS/x86_64/
yum install ./slurm-*
yum --installroot=$CHROOT install slurm-devel slurm-contribs slurm-munge slurm-openlava slurm-pam_slurm slurm-perlapi slurm-plugins slurm-slurmdbd slurm-sql slurm-torque -y

vim /etc/slurm/slurm.conf #slurm的配置文档

cp -a /etc/slurm/slurm.conf /atlas/os_images/compute_node_v0.2.0/etc/slurm/slurm.conf
chown slurm:slurm /var/spool
mkdir /var/spool/slurmctld
chown slurm: /var/spool/slurmctld
chmod 755 /var/spool/slurmctld
touch /var/log/slurmctld.log
chown slurm: /var/log/slurmctld.log
chroot /atlas/os_images/compute_node_v0.2.0/
mkdir /var/spool/slurmd
chown slurm: /var/spool/slurmd
chmod 755 /var/spool/slurmd
touch /var/log/slurmd.log
chown slurm: /var/log/slurmd.log
chroot $CHROOT systemctl enable slurmd.service


[yhu@master etc]$sudo chmod +x rc.local
[yhu@master etc]$cat rc.local
...
nvidia-smi >> /dev/null
systemctl restart slurmd
# 调用gpu的时候没有设备文件/dev/nvidia0,通过openhpc启动起来的系统的确没有这个设备文件,所以我在 /etc.rc.local 文件中加入两行代码,创建出来这个设备文件.并重新启动slurmd服务

singularity

1
2
3
4
5
6
7
8
9
10
11
yum -y --installroot=$CHROOT install gcc make
cp singularity-2.2.1.tar.gz /atlas/os_images/compute_node_v0.2.0/tmp/
chroot /atlas/os_images/compute_node_v0.2.0
cd /tmp
tar xf singularity-2.2.1.tar.gz
cd singularity-2.2.1
./configure
make
make install
cd ..
rm -rf singularity-2.2.1*

environment-modules

1
2
3
4
5
yum -y --installroot=$CHROOT install environment-modules
chroot $CHROOT vi /usr/share/Modules/init/.modulespath
/atlas/gensoft/public_modules #只留这一个其他都屏蔽掉


nvidia显卡驱动,cuda,cudnn

1
2
3
4
5
6
7
8
9
10
chroot /atlas/os_images/compute_node_v0.2.0/
# nvidia-kmod安装之后,并不会立即替换掉so动态加载库,所以需要删除原来的,加载新的。这样就不需要重启了。
yum remove nvidia-kmod
yum install nvidia-kmod xorg-x11-drv-nvidia*
rm -rf /var/lib/dkms/nvidia/375.39/
rm -rf /usr/src/nvidia-375.26
rm /usr/lib64/nvidia/*375.26
cp /var/lib/dkms/nvidia/387.26/3.10.0-327.el7.x86_64/x86_64/module/nvidia* /lib/modules/3.10.0-327.el7.x86_64/extra/
yum reinstall nvidia-kmod xorg-x11-drv-nvidia*
yum clean all

配置信息保存数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat ~/.ssh/cluster.pub >> $CHROOT/root/.ssh/authorized_keys #把秘钥保存到node节点方便直接远程
wwinit ssh_keys
wwsh file import /etc/passwd #
wwsh file import /etc/group #--------------------------
wwsh file import /etc/shadow #--------------------------

export WW_CONF=/etc/warewulf/bootstrap.conf
echo "drivers += updates/kernel/" >> $WW_CONF
wwbootstrap `uname -r` #制作bootstrap
echo "GATEWAYDEV=eth0" > /tmp/network.$$
wwsh -y file import /tmp/network.$$ --name network
wwsh -y file set network --path /etc/sysconfig/network --mode=0644 --uid=0
wwvnfs -y --chroot $CHROOT #将做好的node启动目录制作成vnfs镜像文件
#配置每个node的IP等
wwsh -y node new node5 --ipaddr=172.16.10.15 --hwaddr=0c:c4:7a:85:18:da --network=255.255.255.0 --gateway=172.16.10.1 -D eth0
wwsh -y node new node7 --ipaddr=172.16.10.17 --hwaddr=0c:c4:7a:82:c5:d8 --network=255.255.255.0 --gateway=172.16.10.1 -D eth0
#----------wwsh -y node delete node7 写错了可以删除,重新添加
#系统会根据这些配置,生成一个dhcp.conf文件 在pxe启动时分配IP等信息

#下面这个是系统启动后,要为每个node配置修改哪些file
wwsh -y provision set "node5" --vnfs=compute_node_v0.2.0 --bootstrap=`uname -r` --files=dynamic_hosts,passwd,group,shadow,network #--------------------------
wwsh -y provision set "node7" --vnfs=compute_node_v0.2.0 --bootstrap=`uname -r` --files=dynamic_hosts,passwd,group,shadow,network #--------------------------

3.启动服务

1
2
3
4
5
systemctl restart dhcpd
wwsh pxe update
systemctl enable slurmctld.service
systemctl start slurmctld.service
systemctl status slurmctld.service

4.重启node节点

这里我直接操作ipmi重启

1
2
3
4
5
ipmitool -I lanplus -H 172.16.10.107 -U ADMIN -P ADMIN chassis bootdev pxe options=persistent
ipmitool -I lanplus -H 172.16.10.107 -U ADMIN -P ADMIN chassis power reset
ipmitool -I lanplus -H 172.16.10.105 -U ADMIN -P ADMIN chassis bootdev pxe options=persistent
ipmitool -I lanplus -H 172.16.10.105 -U ADMIN -P ADMIN chassis power reset

启动后简单的检测

这里我直接使用slurm命令检测

1
2
3
4
5
6
7
8
9
10
11
12
13
sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
test* up infinite 1 down* node5

scontrol
scontrol: update nodename=node5 state=resume
scontrol: exit

sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
test* up infinite 1 idle node5

#slurm

5.不同node节点配置

1
2
3
4
5
6
7
8
9
10
11
wwvnfs -y --chroot /atlas/os_images/compute_node_v0.2.2 -o /atlas/os_images/vnfs/compute_node_v0.2.2.vnfs

wwsh vnfs import /atlas/os_images/vnfs/compute_node_v0.2.2.vnfs --name=compute_node_v0.2.2 --chroot=/atlas/os_images/compute_node_v0.2.2 -y

wwsh provision set node1 -V compute_node_v0.2.2 -y
wwsh provision set node2 -V compute_node_v0.2.2 -y
wwsh provision set node3 -V compute_node_v0.2.2 -y
wwsh provision set node4 -V compute_node_v0.2.2 -y
wwsh provision set node5 -V compute_node_v0.2.2 -y
wwsh provision set node6 -V compute_node_v0.2.2 -y
wwsh provision set node7 -V compute_node_v0.2.2 -y

wwsh是warewulf的命令,可以直接输入wwsh进入交互.功能主要是,设置node节点启动的时候,pxe的启动设置,网络以及主机名,还有每个节点应该有哪些配置文件,等等.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#wwsh 
Warewulf> help
Warewulf command line shell interface

Welcome to the Warewulf shell interface. This application allows you
to interact with the Warewulf backend database and modules via a
single interface.

bootstrap Manage your bootstrap images
dhcp Manage DHCP service and configuration
events Control how events are handled
exit Exit/leave the Warewulf shell
file Manage files within the Warewulf data store
node Node manipulation commands
object Generically manipulate all Warewulf data store entries
output Set the output verbosity level
provision Node provision manipulation commands
pxe Manage PXE configuration
quit Exit/leave the Warewulf shell
ssh Spawn parallel ssh connections to nodes.
vnfs Manage your VNFS images

Warewulf>

munge编译安装

[TOC]

master 节点

创建文档

1
2
3
sudo mkdir -p /atlas/configs/munge_v0.5.12/munge
sudo mkdir -p /atlas/gensoft/munge_v0.5.12/lib/munge
sudo mkdir -p /atlas/gensoft/munge_v0.5.12/sbin/nologin

创建 munge ID

1
2
3
export MUNGEUSER=2001
sudo groupadd -g $MUNGEUSER munge
sudo useradd -m -c "MUNGE Uid 'N' Gid Emporium" -d /atlas/gensoft/munge_v0.5.12/lib/munge -u $MUNGEUSER -g munge -s /atlas/gensoft/munge_v0.5.12/sbin/nologin munge

munge下载

1
2
3
4
5
到 https://dun.github.io/munge/ 下载 tar.gz 最新版本
文件放置到atlas/backup/munge_v0.5.12 中
解压
sudo tar zxvf dun-munge-munge-0.5.12-24-gd11b5fe.tar.gz
cd dun-munge-d11b5fe

安装

1
2
3
sudo ./configure --prefix=/atlas/gensoft/munge_v0.5.12 --sysconfdir=/atlas/configs/munge_v0.5.12 --localstatedir=/atlas/gensoft/munge_v0.5.12 
sudo make -j 4
sudo make install 

配置环境变量

1
2
3
4
vim /etc/profile.d/munge.sh
export PATH=/atlas/gensoft/munge_v0.5.12/usr/bin:$PATH
export PATH=/atlas/gensoft/munge_v0.5.12/usr/sbin:$PATH
. /etc/profile.d/munge.sh

检查加密方式

1
2
munge -C
munge -M

在mater 节点创建所有 node 需要的秘钥

1
2
3
dd if=/dev/urandom bs=1 count=1024 > /atlas/configs/munge_v0.5.12/munge/munge.key
chown munge: /atlas/configs/munge_v0.5.12/munge/munge.key
chmod 400 /atlas/configs/munge_v0.5.12/munge/munge.key

将/atlas/configs/munge_v0.5.12 /munge/munge.key 拷贝到其他节点

1
2
3
4
login
scp /atlas/configs/munge_v0.5.12/munge/munge.key root@172.16.10.18:/atlas/configs/munge_v0.5.12/munge
node5
scp /atlas/configs/munge_v0.5.12/munge/munge.key root@172.16.10.15:/atlas/configs/munge_v0.5.12/munge


在所有节点上设置权限和所属

1
2
chown -R munge: /atlas/configs/munge_v0.5.12/munge/ /atlas/gensoft/munge_v0.5.12/log/munge/
chmod 0700 /atlas/configs/munge_v0.5.12/munge/ /atlas/gensoft/munge_v0.5.12/log/munge/

在所有节点上运行 munge

1
/atlas/configs/munge_v0.5.12/rc.d/init.d/munge start

基于singularity的deepchem-GPU镜像制作

[TOC]

准备前提

首先你的pc要安装了singularity,下载好了cuda,cudnn,anaconda.而在集群上这些都已经准备好了.

制作基础系统镜像

1
2
3
mkdir ~/deepchem-build && cd ~/deepchem-build
cp /atlas/backup/images/centos.def ./
singularity build --sandbox centos centos.def

准备下载好的应用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cp /atlas/backup/software/{Anaconda3-5.1.0-Linux-x86_64.sh, cudnn-9.1-linux-x64-v7.1.tgz } ./
cp /etc/yum.repos.d/base.repo ./
# base.repo 中是在安装软件过程中所依赖的绝大部分安装包和cuda, 用本地源提高速度节省时间.
cat /etc/yum.repos.d/base.repo
[development]
name=development
baseurl=ftp://172.16.10.10/centos7.2
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

[epel]
name=epel
baseurl=ftp://172.16.10.10/epel
gpgcheck=0

[cuda]
name=cuda
baseurl=ftp://172.16.10.10/cuda/cuda
gpgcheck=0

在镜像中安装应用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
singularity shell -w centos
cp base.repo /etc/yum.repos.d/
yum repolist
yum -y install vim wget perl git curl bzip2
export LANG=en_US.UTF-8 #设定系统字符编码

# install Anaconda3
bash Anaconda3-5.1.0-Linux-x86_64.sh -f -b -p -s /usr/local/anaconda3
export PATH=/usr/local/anaconda3/bin:$PATH

# install nvidia-driver cuda
yum install cuda
tar xf cudnn-9.1-linux-x64-v7.1.tgz -C /usr/local/
# test
nvidia-smi
nvcc --version

# if you can not get gpu information by nvidia-smi
# reload the nvidia modules
lsmod | grep nvidia
systemctl stop gmond
rmmod nvidia_drm
rmmod nvidia_modeset
rmmod nvidia_uvm
rmmod nvidia
nvidia-smi

#or cannot to reboot , can change the nvidia kernel module by hand
rm -rf /lib/modules/3.10.0-327.el7.x86_64/extra/nvidia*
cp /var/lib/dkms/nvidia/387.26/3.10.0-327.el7.x86_64/x86_64/module/nvidia* /lib/modules/3.10.0-327.el7.x86_64/extra/
nvidia-smi

# add environment value
cat /environment
export LANG=en_US.UTF-8
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
export PATH=/usr/local/anaconda3/bin:/usr/local/cuda/bin:$PATH

# install tensorflow-gpu and keras and other modules can install by conda
cat ~/.condarc
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/mro/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/
- defaults
show_channel_urls: true

conda install tensorflow-gpu==1.6.0
conda install keras-gpu
conda install pytest pytest-html pytest-cov pydicom gevent numba autobahn pymongo redis rq txaio twisted

conda install -c menpo opencv3
# install deepchem
conda install -c deepchem -c rdkit -c conda-forge -c omnia deepchem-gpu=2.0.0

# install openslide and autobahn_autoreconnect etc. can only install by pip
pip install openslide-python autobahn_autoreconnect
wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz && tar xf libiconv-1.15.tar.gz && cd libiconv-1.15
./configure && make -j 12 && make install
ln -sv /usr/local/lib/libiconv.so.2 /usr/lib/libiconv.so.2
ldconfig

cd .. && rm -rf libiconv-1.15

conda clean -a
yum clean all
exit

生成只读镜像

1
singularity build centos_gpu_tf6_n387_deepchem.simg centos