0%

原理

rbd通过内核的NBD驱动映射成网络设备,用户可以通过读写nbd0…等网络设备,来实现对rbd设备的读写。

优点:

  • nbd设备成功将客户端和rbd隔离开,不再依赖以往的内核rbd驱动;
  • nbd与librbd的读写在应用层完成,可使用rbd cache提高性能;
  • rbd的数据保护,可通过ceph命令完成,不需要nbd支持。

缺点:

  • 要求系统必须支持nbd驱动;
  • 内核nbd与librbd通信,需要通过文件socket通信,增加数据拷贝,会影响性能;

架构

rbd-nbd_frame

  • 客户端直接读写nbd设备(同步)
  • 内核nbd驱动,将客户端的读写信息转化成nbd_request,并通过socket发送给NBDServer(rbd-nbd实现)
  • NBDServer是守护进程,一个快设备对应一个nbd设备,对应一个守护进程
  • NBDServer收到内核nbd驱动的nbd_reqeust后,向librbd发起同步读写请求
  • librbd 向 Ceph集群读写数据
  • NBDServer接收到librbd同步读写结果后,向内核nbd驱动发送reply
  • 内核nbd驱动收到reply,向客户端return

关键数据结构

Reqeust & Reply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct nbd_request {
u32 magic;
u32 type; /* == READ || == WRITE */
char handle[8];
u64 from;
u32 len;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
;

struct nbd_reply {
u32 magic;
u32 error; /* 0 = ok, else error */
char handle[8]; /* handle you got from reqeust */
};

参考&鸣谢

搜索引擎

搜索引擎对于每一个码农来说都是一个互联网数据库,在这个数据库中有很多信息,我们在这个数据库中检索数据不需要指定数据库,也不需要数据表,只需要指定你需要检索的关键字即可。但这种搜索方式虽然搜索范围大,无漏网之鱼,可也搜到了很多于我们需要无关的内容。为了精确查找,筛选掉不需要的信息。搜索引擎还有如下用法,接下来,就让我们一探究竟吧。

特殊用法

由于天朝屏蔽Google,所以以下可在baidu搜索输入框中操作实现。

指定标题搜索

语法

1
intitle:"<网页title内容(标签上显示的内容)>"

举例

搜索title中含有“Bolog”的网页

1
intitle:"Bolog"

指定URL搜索

语法

1
inurl:<全部或部分URL信息>

举例

搜索url中包含nginx-1-11-6的网页

1
inurl:nginx-1-11-6

指定正文内容搜索

语法

1
intext:"<搜索关键字>"

此方法也是搜索引擎默认使用方法(猜的)

举例

搜索正文内容含有“python” 和“golang”关键字的网页

1
intext:"<python golang>"

指定网站(站点)搜索

语法

1
site:<网站域名> <搜索关键字>

举例

在oschina上搜索python关键字

1
site:oschina.net python

指定文件类型搜索

语法

1
filetype:<文件类型> <搜索关键字>

举例

搜索所有带有python关键字的PDF文档

1
filetype:pdf python

参考&鸣谢

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
# -*- coding: utf-8 -*-
import argparse
args = "-f hello.txt -n 1 2 3 -x 100 -y b -z a -q hello @args.txt i_am_bar -h".split()

# 使用@args.txt要求fromfile_prefix_chars="@"
# args.txt文件中应该一行一个参数,想改变行为参考convert_arg_line_to_args()
# ArgumentParser参数的简单说明
## description - 命令行帮助的开始文字,大部分情况下,我们只会用到这个参数
# epilog - 命令行帮助的结尾文字
# prog - (default: sys.argv[0])程序的名字,一般不需要修改,另外,如果你需要在help中使用到程序的名字,可以使用%(prog)s
# prefix_chars - 命令的前缀,默认是-,例如-f/--file。有些程序可能希望支持/f这样的选项,可以使用prefix_chars="/"
# fromfile_prefix_chars - (default: None)如果你希望命令行参数可以从文件中读取,就可能用到。例如,如果fromfile_prefix_chars='@',命令行参数中有一个为"@args.txt",args.txt的内容会作为命令行参数
# add_help - 是否增加-h/-help选项 (default: True),一般help信息都是必须的,所以不用设置啦。
## parents - 类型是list,如果这个parser的一些选项跟其他某些parser的选项一样,可以用parents来实现继承,例如parents=[parent_parser]
## formatter_class - 自定义帮助信息的格式(description和epilog)。默认情况下会将长的帮助信息进行<自动换行和消除多个连续空白>。
#三个允许的值:
# class argparse.RawDescriptionHelpFormatter 直接输出description和epilog的原始形式(不进行自动换行和消除空白的操作)
# class argparse.RawTextHelpFormatter 直接输出description和epilog以及add_argument中的help字符串的原始形式(不进行自动换行和消除空白的操作)
## class argparse.ArgumentDefaultsHelpFormatter 在每个选项的帮助信息后面输出他们对应的缺省值,如果有设置的话。这个最常用吧!
# argument_default - (default: None)设置一个全局的选项的缺省值,一般每个选项单独设置,所以这个参数用得少,不细说
# usage - (default: generated)如果你需要修改usage的信息(usage: PROG [-h] [--foo [FOO]] bar [bar ...]),那么可以修改这个,一般不要修改。
# conflict_handler - 不建议使用。这个在极端情况下才会用到,主要是定义两个add_argument中添加的选项的名字发生冲突时怎么处理,默认处理是抛出异常。
#注释一行有##表示这几个参数比较常用
parser = argparse.ArgumentParser(description="This is a description of %(prog)s", epilog="This is a epilog of %(prog)s", prefix_chars="-+", fromfile_prefix_chars="@", formatter_class=argparse.ArgumentDefaultsHelpFormatter)

# ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
# add_argument的参数是比较复杂的。。。
# name or flags - 指定参数的形式,想写几个写几个,不过我们一般就写两个,一个短参数,一个长参数,看下面的例子"-f", "--file"
# 可选的选项,位置不固定,想怎么写就怎么写,默认是可选的
parser.add_argument("-f", "--file", help="test test test")

# 位置固定的选项,例如"prog i_am_bar",这样子的话,i_am_bar就是bar选项的值啦,默认是必须有的
parser.add_argument("bar", help="test test test")

# nargs - 指定这个参数后面的value有多少个,例如,我们希望使用-n 1 2 3 4,来设置n的值为[1, 2, 3, 4]
parser.add_argument("-n", "--num", nargs="+", type=int)

# 这里nargs="+"表示,如果你指定了-n选项,那么-n后面至少要跟一个参数,+表示至少一个,?表示一个或0个,*0个或多个,
# default - 如果命令行没有出现这个选项,那么使用default指定的默认值
parser.add_argument("+g", "++gold", help="test test test", default="test_gold")#需要prefix_chars包含"+"

# type - 如果希望传进来的参数是指定的类型(例如 float, int or file等可以从字符串转化过来的类型),可以使用
parser.add_argument("-x", type=int)

# choices - 设置参数值的范围,如果choices中的类型不是字符串,记得指定type哦
parser.add_argument("-y", choices=['a', 'b', 'd'])

# required - 通常-f这样的选项是可选的,但是如果required=True那么就是必须的了
parser.add_argument("-z", choices=['a', 'b', 'd'], required=True)

# metavar - 参数的名字,在显示 帮助信息时才用到.
parser.add_argument("-o", metavar="OOOOOO")

# help - 设置这个选项的帮助信息
# dest - 设置这个选项的值就是解析出来后放到哪个属性中
parser.add_argument("-q", dest="world")

args = parser.parse_args(args) # 如果你没有args参数,那么就使用sys.argv,也就是命令行参数啦。有这个参数,就方便我们调试啊
# args.world就是-q的值啦
# action - The basic type of action to be taken when this argument is encountered at the command line.
# const - A constant value required by some action and nargs selections.
# 这两个自己看帮助文档啦,比较复杂
# http://docs.python.org/library/argparse.html
print args

背景

  • OS Version : rhel 6.4
  • MySQL Version : 5.6.31

改密权限

  • root可以修改所有用户密码
  • 所有用户在无任何权限情况下可修改自己密码
  • 获得“update所有库”权限的用户可修改任何用户密码(包括root)

(以上所诉,均使用set password方法)

改密三种方法

Set Password

set password for <用户名>@{localhost|%|...} =password('<密码>');

grant … identified by

grant <权限> on <数据库>.<数据表> to <user>@{localhost|%|...} identified by '<密码>';

update

update <库名>.<表名> set password=password('<密码>') where user='<用户名>' and host='{localhost | % | ...}';

vi命令

使用“xxd”或“od”工具转换十六进制
方法如下:

  • :%!xxd将当前文本转换为16进制格式

  • :%!xxd -c 12将当前文本转换为16进制格式,并每行显示12个字节

  • :%!xxd -r将当前文件转换回文本格式

  • 参考&鸣谢

    1. vi下以16进制查看二进制文件

背景

  • OS Version : rhel 6.4
  • MySQL Version : 5.6.31

权限

权限列表

Privilege Column Context
CREATE Create_priv databases, tables, or indexes
DROP Drop_priv database, tables, or views
GRANT OPTION Grant_priv database, tables, or stored routines
LOCK TABLES lock_tables_priv databases
REFERENCES References_priv databases or tables, columns
EVENT Event_priv databases
ALTER Alter_priv tables
DELETE Delete_priv tables
INDEX Index_priv tables
INSERT Insert_priv tables or columns
SELECT Select_priv tables or columns
UPDATE Update_priv tables or columns
CREATE TEMPORARY TABLES Create_tmp_table_priv tables
TRIGGER trigger_priv tables
CREATE VIEW Create_view_priv views
SHOW VIEW Show_view_priv views
ALTER ROUTINE alter_routine_priv stored routines
CREATE ROUTINE create_routine_priv stored routines
EXECUTE execute_priv stored routines
FILE file_priv file access on server host (Global)
CREATE TABLESPACE create_tablespace_priv server administration (Global)
CREATE USER create_user_priv server administration (Global)
PROCESS process_priv server administration (Global)
PROXY proxy_priv server administration (user to user)
RELOAD reload_priv server administration (Global)
REPLICATION CLIENT repl_client_priv server administration (Global)
REPLICATION SLAVE repl_slave_priv server administration (Global)
SHOW DATABASES show_db_priv server administration (Global)
SHUTDOWN shutdown_priv server administration (Global)
SUPER super_priv server administration (Global)
ALL [PRIVILEGES] server administration
USAGE server administration

按权限的种类分类

  • 数据访问相关权限
  • 库表结构相关权限
  • 服务相关权限

权限相关表单

MySQL权限信息存储在“mysql”数据中
其中:

  • “user”表负责存储用户信息,及用户本身所拥有的权限;
  • “db”表负责存储用户与db的权限关系;
  • “tables_priv”负责存在用户与table的权限关系;
  • “columns_priv”负责存储用户与column的权限关系;

user表结构

Field Type Null Key Default
Host char(60) NO PRI
User char(16) NO PRI
Password char(41) NO
Select_priv enum(‘N’,’Y’) NO N
Insert_priv enum(‘N’,’Y’) NO N
Update_priv enum(‘N’,’Y’) NO N
Delete_priv enum(‘N’,’Y’) NO N
Create_priv enum(‘N’,’Y’) NO N
Drop_priv enum(‘N’,’Y’) NO N
Reload_priv enum(‘N’,’Y’) NO N
Shutdown_priv enum(‘N’,’Y’) NO N
Process_priv enum(‘N’,’Y’) NO N
File_priv enum(‘N’,’Y’) NO N
Grant_priv enum(‘N’,’Y’) NO N
References_priv enum(‘N’,’Y’) NO N
Index_priv enum(‘N’,’Y’) NO N
Alter_priv enum(‘N’,’Y’) NO N
Show_db_priv enum(‘N’,’Y’) NO N
Super_priv enum(‘N’,’Y’) NO N
Create_tmp_table_priv enum(‘N’,’Y’) NO N
Lock_tables_priv enum(‘N’,’Y’) NO N
Execute_priv enum(‘N’,’Y’) NO N
Repl_slave_priv enum(‘N’,’Y’) NO N
Repl_client_priv enum(‘N’,’Y’) NO N
Create_view_priv enum(‘N’,’Y’) NO N
Show_view_priv enum(‘N’,’Y’) NO N
Create_routine_priv enum(‘N’,’Y’) NO N
Alter_routine_priv enum(‘N’,’Y’) NO N
Create_user_priv enum(‘N’,’Y’) NO N
Event_priv enum(‘N’,’Y’) NO N
Trigger_priv enum(‘N’,’Y’) NO N
Create_tablespace_priv enum(‘N’,’Y’) NO N
ssl_type enum(‘’,’ANY’,’X509’,’SPECIFIED’) NO
ssl_cipher blob NO NULL
x509_issuer blob NO NULL
x509_subject blob NO NULL
max_questions int(11) unsigned NO 0
max_updates int(11) unsigned NO 0
max_connections int(11) unsigned NO 0
max_user_connections int(11) unsigned NO 0
plugin char(64) YES mysql_native_password
authentication_string text YES NULL
password_expired enum(‘N’,’Y’) NO N

db表结构

Field Type Null Key Default
Host char(60) NO PRI
Db char(64) NO PRI
User char(16) NO PRI
Select_priv enum(‘N’,’Y’) NO N
Insert_priv enum(‘N’,’Y’) NO N
Update_priv enum(‘N’,’Y’) NO N
Delete_priv enum(‘N’,’Y’) NO N
Create_priv enum(‘N’,’Y’) NO N
Drop_priv enum(‘N’,’Y’) NO N
Grant_priv enum(‘N’,’Y’) NO N
References_priv enum(‘N’,’Y’) NO N
Index_priv enum(‘N’,’Y’) NO N
Alter_priv enum(‘N’,’Y’) NO N
Create_tmp_table_priv enum(‘N’,’Y’) NO N
Lock_tables_priv enum(‘N’,’Y’) NO N
Create_view_priv enum(‘N’,’Y’) NO N
Show_view_priv enum(‘N’,’Y’) NO N
Create_routine_priv enum(‘N’,’Y’) NO N
Alter_routine_priv enum(‘N’,’Y’) NO N
Execute_priv enum(‘N’,’Y’) NO N
Event_priv enum(‘N’,’Y’) NO N
Trigger_priv enum(‘N’,’Y’) NO N

tables_priv表结构

Field Type Null Key Default Extra
Host char(60) NO PRI
Db char(64) NO PRI
User char(16) NO PRI
Table_name char(64) NO PRI
Grantor char(77) NO MUL
Timestamp timestamp NO CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
Table_priv set(‘Select’,’Insert’,
‘Update’,’Delete’,
‘Create’,’Drop’,
‘Grant’,’References’,
‘Index’,’Alter’,
‘Create View’,
‘Show view’,’Trigger’)
NO
Column_priv set(‘Select’,’Insert’,
‘Update’,’References’)
NO

columns_priv表结构

Field Type Null Key Default Extra
Host char(60) NO PRI
Db char(64) NO PRI
User char(16) NO PRI
Table_name char(64) NO PRI
Column_name char(64) NO PRI
Timestamp timestamp NO CURRENT_TIMESTAMP on update
CURRENT_TIMESTAMP
Column_priv set(‘Select’,
‘Insert’,
‘Update’,
‘References’)
NO

验证流程

mysql_privilege_flow

问题发现

背景

  • uwsgi version: 2.0.13.1
  • python version: 2.6.6
  • 采用 Nginx + uWSGI + Python 架构,均拥有ROOT权限

现象

Python执行os.mkdir操作,提示“Permission Denied”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Traceback (most recent call last):
File "/cs/nginx/uwsgi/main.py", line 24, in application
result = process().entry(environ, start_response)
File "/cs/nginx/uwsgi/rds/base_mng.py", line 90, in entry
self.process_put_request(env)
File "/cs/nginx/uwsgi/rds/init_rds.py", line 149, in process_put_request
result = self.do_put_request(env)
File "/cs/nginx/uwsgi/rds/init_rds.py", line 140, in do_put_request
err_code = op.do()
File "/cs/nginx/uwsgi/rds/init_rds.py", line 70, in do
if 0 == init_create_mp(mp):
File "/cs/nginx/uwsgi/rds/init/main.py", line 44, in init_create_mp
os.mkdir(mp,0755)
OSError: [Errno 13] Permission denied: '/aaab'

解决

Python脚本的权限,有uWSGI来决定,所以该错误肯定跟uWSGI有关,通过调整uwsgi.ini文件中的用户和权限相关配置发现,将cap = setuid,setgid去掉,可以正常创建目录。

但:具体原因不明,可能跟setuid和setgid的使用有关,有可能是个Bug

问题发现

现象

在nginx+uwsgi+python环境中,启动mysqld服务,发生服务无法正常启动现象(闪退)。而在shell终端中可以正常启动mysqld服务。说明“nginx+uwsgi+python”这个环境限制了某些资源,导致mysqld服务启动失败。

uwsgi错误日志(/var/log/uwsgi.log)

1
Starting MySQL. ERROR! The server quit without updating PID file (/store/xxx.pid)

mysql错误日志(/{mysqldatadir}/{hostname}.err)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2016-09-22 16:21:41 28084 [Note] InnoDB: Using atomics to ref count buffer pool pages
2016-09-22 16:21:41 28084 [Note] InnoDB: The InnoDB memory heap is disabled
2016-09-22 16:21:41 28084 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-22 16:21:41 28084 [Note] InnoDB: Memory barrier is not used
2016-09-22 16:21:41 28084 [Note] InnoDB: Compressed tables use zlib 1.2.3
2016-09-22 16:21:41 28084 [Note] InnoDB: Using CPU crc32 instructions
2016-09-22 16:21:41 28084 [Note] InnoDB: Initializing buffer pool, size = 1.0G
InnoDB: mmap(137363456 bytes) failed; errno 12
2016-09-22 16:21:41 28084 [ERROR] InnoDB: Cannot allocate memory for the buffer pool
2016-09-22 16:21:41 28084 [ERROR] Plugin 'InnoDB' init function returned error.
2016-09-22 16:21:41 28084 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2016-09-22 16:21:41 28084 [ERROR] Unknown/unsupported storage engine: InnoDB
2016-09-22 16:21:41 28084 [ERROR] Aborting
2016-09-22 16:21:41 28084 [Note] Binlog end

分析

根据mysql错误日志提示,mysql在初始化InnoDB引擎时,申请“Buffer Pool”内存失败,此处的mmap是用来申请内存的(allocate申请内存时,小于128KB的在栈上进行分配内存,大于128K的,从堆上分配内存)。
使用shell终端正常启动mysqld服务,查看mysqld占用内存大小发现需要2GB左右的虚拟内存,如下所示:

1
2
3
[root@host]# ps aux | grep mysqld | grep -v grep
root 31635 0.0 0.1 11300 1520 ? S Sep22 0:00 /bin/sh /cs/mysql/bin/mysqld_safe --datadir=/store --pid-file=/store/zhoubo.pid
root 32165 0.0 25.5 2041620 259716 ? Sl Sep22 0:15 /cs/mysql/bin/mysqld --basedir=/cs/mysql --datadir=/store --plugin-dir=/cs/mysql/lib/plugin --user=root --log-error=/store/zhoubo.err --open-files-limit=65535 --pid-file=/store/zhoubo.pid --socket=/cs/mysql/data/mysql.sock --port=3306

由此可以看出,“nginx+uwsgi+python”这个组合在其中某个环节对内存做了内存限制导致内存不能正常分配;nginx父进程为init,uwsgi父进程也为init,二者之间使用管道进行通信,所以不可能由nginx限制uwsgi进程资源。而uwsgi以fork形式调用python,fork过程中可以限制资源,python进程的资源被限制后再启动mysqld服务,就会导致mysqld分配内存失败,从而出现上述的错误现象。

解决

根据上诉分析,找到了问题的原因出在uwsgi对进程资源进行了限制,所以将uwsgi.ini中关于资源限制的配置去掉,即可解决。
在uwsgi.ini中去掉limit-as 256选项。

原理

uwsgi.ini中资源限制项包括limit-aslimit-nproclimit-port等,他们对进程资源的限制的访问获取都是通过系统接口getrlimt和setrlimit来实现的(POSIX API)。

1
2
3
4
5
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit,struct rlimit *old_limit);

核心结构体

1
2
3
4
struct rlimit{
rlim_t rlim_cur; // soft limit
rlim_t rlim_max; // hard limit
}

rlim_cur

为soft limit是指内核所能支持的资源上限。比如对于RLIMIT_NOFILE(一个进程能打开的最大文件数,内核默认是1024),soft limit最大也只能达到1024。对于RLIMIT_CORE(core文件的大小,内核不做限制),soft limit最大能是unlimited。

rlim_max

为hard limit在资源中只是作为soft limit的上限。当你设置hard limit后,你以后设置的soft limit只能小于hard limit。要说明的是,hard limit只针对非特权进程,也就是进程的有效用户ID(effective user ID)不是0的进程。具有特权级别的进程(具有属性CAP_SYS_RESOURCE),soft limit则只有内核上限。

可限制资源列表

资源 描述
RLIMIT_AS 进程的最大虚拟内存空间,字节为单位
RLIMIT_CORE 内核转存文件的最大长度
RLIMIT_CPU 最大允许的CPU使用时间,秒为单位
RLIMIT_DATA 进程数据段的最大
RLIMIT_FSIZE 进程可建立的文件的最大长度
RLIMIT_LOCKS 进程可建立的锁和租赁的最大值
RLIMIT_MEMLOCK 进程可锁定在内存中的最大数据量,字节为单位
RLIMIT_MSGQUEUE 进程可为POSIX消息队列分配的最大字节数
RLIMIT_NICE 进程可通过setpriority() 或 nice()调用设置的最大完美值
RLIMIT_NOFILE 指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误
RLIMIT_NPROC 用户可拥有的最大进程数
RLIMIT_RTPRIO 进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级
RLIMIT_SIGPENDING 用户可拥有的最大挂起信号数
RLIMIT_STACK 最大的进程堆栈,以字节为单位

参考及鸣谢

背景

使用python2.7

依赖库

  • gcc
    需要安装gcc编译器,用于编译_mysql.c

  • python
    安装python-devel解决找不到“Python.h”问题

  • mysql
    安装 mysql-community-devel

环境

OS

RHEL7.2

步骤

1、安装mysql,详细见mysql官网

2、启动mysql service

rhel7.2中,使用service mysqld start启动mysql服务。【status:查服务状态;stop:停止服务;restart:重启服务】

3、修改root密码

  • 首先使客户端登陆掉过密码检测,然后重启mysql服务
    echo "skip-grant-tables" >> /etc/my.cnf

  • 使用mysql -uroot -p登陆,然后修改mysql库中的user
    update user set password_expired="N" where user="root";
    update user set authentication_string=password("hnagroup") where user="root";

  • 最后删除/etc/my.cnf中的skip-grant-tables,去掉客户端跳过密码检测设置