头部背景图片
今晚月色很美、你说是的 -- ZeMing |
今晚月色很美、你说是的 -- ZeMing |

nginx安装 Lua 笔记 + Cache + Image_filter + lua_status (待整理)

2018-05-16

#lua笔记记录

#lua经典的模块流转图,一图胜千言
image

#lua各模块兼容情况

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
init_by_lua            http
set_by_lua server, server if, location, location if
rewrite_by_lua http, server, location, location if
access_by_lua http, server, location, location if
content_by_lua location, location if
header_filter_by_lua http, server, location, location if
body_filter_by_lua http, server, location, location if
log_by_lua http, server, location, location if
{
set_by_lua: 流程分支处理判断变量初始化
rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua: IP准入、接口权限等情况集中处理(例如配合iptable完成简单防火墙)
content_by_lua: 内容生成
header_filter_by_lua: 应答HTTP过滤处理(例如添加头部信息)
body_filter_by_lua: 应答BODY过滤处理(例如完成应答内容统一成大写)
log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
}

1、output API(ngx.say和ngx.send_headers)

2、control API(ngx.exit和ngx.exec)

3、subrequest API(ngx.location.capture和ngx.location.capture_multi)

4、cosocket API(ngx.socket.tcp和ngx.req.socket)

header_filter_by_lua 不支持 1234
body_filter_by_lua 不支持 1234


#nginx_lua_doc
https://github.com/openresty/lua-nginx-module#status
#Nginx_lua_Doc_CN
https://moonbingbing.gitbooks.io/openresty-best-practices/openresty/sub_request.html


image_filter resize 20 20;
image_filter_jpeg_quality 75;
image_filter_buffer 500M;
proxy_set_header Host $http_host;
proxy_pass http://10.129.8.37/;
proxy_ignore_headers "Cache-Control" "Expires" "Set-Cookie" ;

ngx.req.get_headers()["Location"]

一个复杂lua 实例

image

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

location ~* /resize([\d-]+)/(.*) {
content_by_lua '
local request_uri = ngx.var.request_uri
local host = ngx.var.host
local var = string.format("%s%s", host, request_uri)
local args1 = ngx.var.args
local var1 = ""
local var2 = ""
local size = ""
if args1 == nil then
args1 = ""
size = ngx.var[1]
var1 = string.format("%s/%s", host, ngx.var[2])
var3 = string.format("/%s/%s", size, ngx.var[2])

else
size = ngx.var[1]
var1 = string.format("%s/%s?%s", host, ngx.var[2],args1)
var3 = string.format("/%s/%s?%s", size, ngx.var[2],args1)
end
local res = ngx.location.capture("/proxyto/" .. var1, { method = ngx.HTTP_HEAD})
if res.status == 200 then
ngx.exec(var3)
elseif res.status == 404 then
ngx.say(res.status)
ngx.say("test404 " , var1)
elseif res.status == 302 then
local location = res.header["Location"]
local m, err = ngx.re.match(location, "//([^/]+)(/.*)")
if not m then
ngx.exit(555)
end
host = m[1]
uri = m[2]
var2 = string.format("//%s/%s%s", host, size , uri)
ngx.redirect(var2)
else
ngx.exit(res.status)
end

';
}
#proxy to
location ~ /proxyto/(.*) {
proxy_pass http://$1$is_args$query_string;
}


#Aliyun lua介绍
https://zhuanlan.zhihu.com/p/28382165
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 方法和常量 
######################


ngx.arg[index] #ngx指令参数,当这个变量在set_by_lua或者set_by_lua_file内使用的时候是只读的,指的是在配置指令输入的参数.
ngx.var.varname #读写NGINX变量的值,最好在lua脚本里缓存变量值,避免在当前请求的生命周期内内存的泄漏
ngx.config.ngx_lua_version #当前ngx_lua模块版本号
ngx.config.nginx_version #nginx版本
ngx.worker.exiting #当前worker进程是否正在关闭
ngx.worker.pid #当前worker进程的PID
ngx.config.nginx_configure #编译时的./configure命令选项
ngx.config.prefix #编译时的prefix选项

core constans: #ngx_lua 核心常量
ngx.OK (0)
ngx.ERROR (-1)
ngx.AGAIN (-2)
ngx.DONE (-4)
ngx.DECLINED (-5)
ngx.nil
http method constans: #经常在ngx.location.catpure和ngx.location.capture_multi方法中被调用.
ngx.HTTP_GET
ngx.HTTP_HEAD
ngx.HTTP_PUT
ngx.HTTP_POST
ngx.HTTP_DELETE
ngx.HTTP_OPTIONS
ngx.HTTP_MKCOL
ngx.HTTP_COPY
ngx.HTTP_MOVE
ngx.HTTP_PROPFIND
ngx.HTTP_PROPPATCH
ngx.HTTP_LOCK
ngx.HTTP_UNLOCK
ngx.HTTP_PATCH
ngx.HTTP_TRACE
http status constans: #http请求状态常量
ngx.HTTP_OK (200)
ngx.HTTP_CREATED (201)
ngx.HTTP_SPECIAL_RESPONSE (300)
ngx.HTTP_MOVED_PERMANENTLY (301)
ngx.HTTP_MOVED_TEMPORARILY (302)
ngx.HTTP_SEE_OTHER (303)
ngx.HTTP_NOT_MODIFIED (304)
ngx.HTTP_BAD_REQUEST (400)
ngx.HTTP_UNAUTHORIZED (401)
ngx.HTTP_FORBIDDEN (403)
ngx.HTTP_NOT_FOUND (404)
ngx.HTTP_NOT_ALLOWED (405)
ngx.HTTP_GONE (410)
ngx.HTTP_INTERNAL_SERVER_ERROR (500)
ngx.HTTP_METHOD_NOT_IMPLEMENTED (501)
ngx.HTTP_SERVICE_UNAVAILABLE (503)
ngx.HTTP_GATEWAY_TIMEOUT (504)

Nginx log level constants: #错误日志级别常量 ,这些参数经常在ngx.log方法中被使用.
ngx.STDERR
ngx.EMERG
ngx.ALERT
ngx.CRIT
ngx.ERR
ngx.WARN
ngx.NOTICE
ngx.INFO
ngx.DEBUG

##################
#API中的方法:
##################
print() #与 ngx.print()方法有区别,print() 相当于ngx.log()
ngx.ctx #这是一个lua的table,用于保存ngx上下文的变量,在整个请求的生命周期内都有效,详细参考官方
ngx.location.capture() #发出一个子请求,详细用法参考官方文档。
ngx.location.capture_multi() #发出多个子请求,详细用法参考官方文档。
ngx.status #读或者写当前请求的相应状态. 必须在输出相应头之前被调用.
ngx.header.HEADER #访问或设置http header头信息,详细参考官方文档。
ngx.req.set_uri() #设置当前请求的URI,详细参考官方文档
ngx.set_uri_args(args) #根据args参数重新定义当前请求的URI参数.
ngx.req.get_uri_args() #返回一个LUA TABLE,包含当前请求的全部的URL参数
ngx.req.get_post_args() #返回一个LUA TABLE,包括所有当前请求的POST参数
ngx.req.get_headers() #返回一个包含当前请求头信息的lua table.
ngx.req.set_header() #设置当前请求头header某字段值.当前请求的子请求不会受到影响.
ngx.req.read_body() #在不阻塞ngnix其他事件的情况下同步读取客户端的body信息.[详细]
ngx.req.discard_body() #明确丢弃客户端请求的body
ngx.req.get_body_data() #以字符串的形式获得客户端的请求body内容
ngx.req.get_body_file() #当发送文件请求的时候,获得文件的名字
ngx.req.set_body_data() #设置客户端请求的BODY
ngx.req.set_body_file() #通过filename来指定当前请求的file data。
ngx.req.clear_header() #清求某个请求头
ngx.exec(uri,args) #执行内部跳转,根据uri和请求参数
ngx.redirect(uri, status) #执行301或者302的重定向。
ngx.send_headers() #发送指定的响应头
ngx.headers_sent #判断头部是否发送给客户端ngx.headers_sent=true
ngx.print(str) #发送给客户端的响应页面
ngx.say() #作用类似ngx.print,不过say方法输出后会换行
ngx.log(log.level,...) #写入nginx日志
ngx.flush() #将缓冲区内容输出到页面(刷新响应)
ngx.exit(http-status) #结束请求并输出状态码
ngx.eof() #明确指定关闭结束输出流
ngx.escape_uri() #URI编码(本函数对逗号,不编码,而php的urlencode会编码)
ngx.unescape_uri() #uri解码
ngx.encode_args(table) #将tabel解析成url参数
ngx.decode_args(uri) #将参数字符串编码为一个table
ngx.encode_base64(str) #BASE64编码
ngx.decode_base64(str) #BASE64解码
ngx.crc32_short(str) #字符串的crs32_short哈希
ngx.crc32_long(str) #字符串的crs32_long哈希
ngx.hmac_sha1(str) #字符串的hmac_sha1哈希
ngx.md5(str) #返回16进制MD5
ngx.md5_bin(str) #返回2进制MD5
ngx.today() #返回当前日期yyyy-mm-dd
ngx.time() #返回当前时间戳
ngx.now() #返回当前时间
ngx.update_time() #刷新后返回
ngx.localtime() #返回 yyyy-mm-dd hh:ii:ss
ngx.utctime() #返回yyyy-mm-dd hh:ii:ss格式的utc时间
ngx.cookie_time(sec) #返回用于COOKIE使用的时间
ngx.http_time(sec) #返回可用于http header使用的时间
ngx.parse_http_time(str) #解析HTTP头的时间
ngx.is_subrequest #是否子请求(值为 true or false)
ngx.re.match(subject,regex,options,ctx) #ngx正则表达式匹配,详细参考官网
ngx.re.gmatch(subject,regex,opt) #全局正则匹配
ngx.re.sub(sub,reg,opt) #匹配和替换(未知)
ngx.re.gsub() #未知
ngx.shared.DICT #ngx.shared.DICT是一个table 里面存储了所有的全局内存共享变量
ngx.shared.DICT.get
ngx.shared.DICT.get_stale
ngx.shared.DICT.set
ngx.shared.DICT.safe_set
ngx.shared.DICT.add
ngx.shared.DICT.safe_add
ngx.shared.DICT.replace
ngx.shared.DICT.delete
ngx.shared.DICT.incr
ngx.shared.DICT.flush_all
ngx.shared.DICT.flush_expired
ngx.shared.DICT.get_keys
ndk.set_var.DIRECTIVE #不懂
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
ngx_lua模块API说明
#Ngx指令
lua_code_cache on | off;
作用:打开或关闭 Lua 代码缓存,影响以下指令: set_by_lua_file , content_by_lua_file, rewrite_by_lua_file, access_by_lua_file 及强制加载或者reload Lua 模块等.缓存开启时修改LUA代码需要重启nginx,不开启时则不用。开发阶段一般关闭缓存。
作用域:main, server, location, location if

lua_regex_cache_max_entries 1024;
作用:未知(貌似是限定缓存正则表达式处理结果的最大数量)

lua_package_path .../path... ;
作用:设置用lua代码写的扩展库路径。
例:lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';

lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';
作用:设置C扩展的lua库路径。

set_by_lua $var '<lua-script>' [$arg1 $arg2];
set_by_lua_file $var <path-to-lua-script-file> [$arg1 $arg2 ...];
作用:设置一个Nginx变量,变量值从lua脚本里运算由return返回,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快.
另外可以将已有的ngx变量当作参数传进Lua脚本里去,由ngx.arg[1],ngx.arg[2]等方式访问。
作用域:main, server, location, server if, location if
处理阶段:rewrite

content_by_lua '<lua script>';
content_by_lua_file luafile;
作用域:location, location if
说明:内容处理器,接收请求处理并输出响应,content_by_lua直接在nginx配置文件里编写较短Lua代码后者使用lua文件。

rewrite_by_lua '<lua script>'
rewrite_by_lua_file lua_file;
作用域:http, server, location, location if
执行内部URL重写或者外部重定向,典型的如伪静态化的URL重写。其默认执行在rewrite处理阶段的最后.
注意,在使用rewrite_by_lua时,开启rewrite_log on;后也看不到相应的rewrite log。

access_by_lua 'lua code';
access_by_lua_file lua_file.lua;
作用:用于访问控制,比如我们只允许内网ip访问,可以使用如下形式。
access_by_lua '
if ngx.req.get_uri_args()["token"] ~= "123" then
return ngx.exit(403)
end ';
作用域:http, server, location, location if

header_filter_by_lua 'lua code';
header_filter_by_lua_file path_file.lua;
作用:设置header 和 cookie;

lua_need_request_body on|off;
作用:是否读请求体,跟ngx.req.read_body()函数作用类似,但官方不推荐使用此方法。

lua_shared_dict shared_data 10m;
作用:设置一个共享全局变量表,在所有worker进程间共享。在lua脚本中可以如下访问它:
例:local shared_data = ngx.shared.shared_data
10m 不知是什么意思。

init_by_lua 'lua code';
init_by_lua_file lua_file.lua;
作用域:http
说明:ginx Master进程加载配置时执行;通常用于初始化全局配置/预加载Lua模块

init_worker_by_lua 'lua code';
init_worker_by_lua_file luafile.lua;
作用域:http

说明:每个Nginx Worker进程启动时调用的计时器,如果Master进程不允许则只会在init_by_lua之后调用;通常用于定时拉取配置/数据,或者后端服务的健康检查。

######################
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
init_by_lua            http
set_by_lua server, server if, location, location if
rewrite_by_lua http, server, location, location if
access_by_lua http, server, location, location if
content_by_lua location, location if
header_filter_by_lua http, server, location, location if
body_filter_by_lua http, server, location, location if
log_by_lua http, server, location, location if
{
set_by_lua: 流程分支处理判断变量初始化
rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua: IP准入、接口权限等情况集中处理(例如配合iptable完成简单防火墙)
content_by_lua: 内容生成
header_filter_by_lua: 应答HTTP过滤处理(例如添加头部信息)
body_filter_by_lua: 应答BODY过滤处理(例如完成应答内容统一成大写)
log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
}

1、output API(ngx.say和ngx.send_headers)

2、control API(ngx.exit和ngx.exec)

3、subrequest API(ngx.location.capture和ngx.location.capture_multi)

4、cosocket API(ngx.socket.tcp和ngx.req.socket)

header_filter_by_lua 不支持 1234
body_filter_by_lua 不支持 1234



#nginx_lua_doc
https://github.com/openresty/lua-nginx-module#status
#Nginx_lua_Doc_CN
https://moonbingbing.gitbooks.io/openresty-best-practices/openresty/sub_request.html

#安装Nginx

初始化 环境

1.14 安装 Lua + Cache + Image_filter + lua_status

1
hostnamectl set-hostname tj-lin-nginx-60v101

安装lib库

1
yum install -y wget make cmake gcc gcc-c++ autoconf automake libpng-devel libjpeg-devel zlib libxml2-devel ncurses-devel bison libtool-ltdl-devel libiconv libmcrypt mhash mcrypt libmcrypt-devel pcre-devel openssl-devel freetype-devel libcurl-devel gd-devel   lua-devel libexif-devel

停防水墙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
systemctl stop firewalld
systemctl disable firewalld
systemctl disable firewalld

systemctl disable firewalld && systemctl stop firewalld && systemctl status firewalld


setenforce 0
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/selinux/config

getenforce

#导入yum资源

1
2
3
curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo 

curl -o /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo

#lua 安装lib

1
2
3
4
5
6
7
8
9
10
curl -O http://luajit.org/download/LuaJIT-2.0.5.tar.gz
tar xzvf LuaJIT-2.0.5.tar.gz
cd LuaJIT-2.0.5
make
make install
#注意环境变量!

export LUAJIT_LIB=/usr/local/luajit/lib

export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0

Ngx_dev_Lua

1
curl -O https://github.com/simplresty/ngx_devel_kit/archive/v0.3.1rc1.tar.gz

Ngx_lua_stats 监控模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 wget https://github.com/initial5/ngx-lua-stats/archive/master.zip
unzip master.zip
#
lua_shared_dict log_dict 20M;
lua_shared_dict result_dict 20M;
#
location / {
proxy_pass http://Node;
log_by_lua_file /usr/local/nginx/conf/lua/record.lua;
}
location /status {
content_by_lua_file /usr/local/nginx/conf/lua/output.lua;
}
location /empty_dict {
content_by_lua_file ./site-enable/empty_dict.lua;
}

Ngx_modele

1
wget https://github.com/openresty/lua-nginx-module/archive/v0.10.13.tar.gz

Nginx_image_modele

1
2
wget https://github.com/linsongze/ngx_http_image_filter_module/archive/master.zip
unzip master.zip

Ngx_Cache

1
2
wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
tar -zxvf ngx_cache_purge-2.3.tar.gz

nginx 1.14

1
2
3
4
5
6
7
8
9
10
11
12
wget http://nginx.org/download/nginx-1.14.0.tar.gz
tar -zxvf nginx-1.14.0.tar.gz


./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-debug --add-module=/root/nginx/ngx_devel_kit-0.3.1rc1 --add-module=/root/nginx/lua-nginx-module-0.10.13 --add-module=/root/nginx/ngx_http_image_filter_module-master
make
make install


./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-debug --add-module=/root/nginx/ngx_devel_kit-0.3.1rc1 --add-module=/root/nginx/lua-nginx-module-0.10.13 --add-module=/root/nginx/ngx_http_image_filter_module-master --add-module=/root/nginx//ngx_http_proxy_connect_module-master
make
make install

lua 错误处理

1
2
3
4
5
6
7
8
9
/usr/local/nginx-1.4.2/sbin/nginx -v
./objs/nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
解决方法:
# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

# /usr/local/nginx-1.4.2/sbin/nginx -v
./objs/nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
解决方法:
# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

启动脚本

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
74
75
cat >  /usr/local/nginx/sbin/nginx.sh <<-EOF 
#!/bin/bash
# nginx Startup script for the Nginx HTTP Server
# it is v.0.0.2 version.
# chkconfig: - 85 15
# description: Nginx is a high-performance web and proxy server.
# It has a lot of features, but it's not for everyone.
# processname: nginx
# pidfile: /usr/local/nginx/logs/nginx.pid
# config: /usr/local/nginx/conf/nginx.conf
nginxd=/usr/local/nginx/sbin/nginx
nginx_config=/usr/local/nginx/conf/nginx.conf
nginx_pid=/usr/local/nginx/logs/nginx.pid
RETVAL=0
prog="nginx"
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 0
[ -x $nginxd ] || exit 0
# Start nginx daemons functions.
start() {
if [ -e $nginx_pid ];then
echo "nginx already running...."
exit 1
fi
echo -n $"Starting $prog: "
daemon $nginxd -c ${nginx_config}
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch /var/lock/subsys/nginx
return $RETVAL
}
# Stop nginx daemons functions.
stop() {
echo -n $"Stopping $prog: "
killproc $nginxd
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx /usr/local/nginx/logs/nginx.pid
}
# reload nginx service functions.
reload() {
echo -n $"Reloading $prog: "
#kill -HUP `cat ${nginx_pid}`
killproc $nginxd -HUP
RETVAL=$?
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
restart)
stop
start
;;
status)
status $prog
RETVAL=$?
;;
*)
echo $"Usage: $prog {start|stop|restart|reload|status|help}"
exit 1
exit $RETVAL
EOF
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
一、安装Nginx
useradd www -M -s /sbin/nologin
1、安装依赖:
yum install -y make cmake gcc gcc-c++ autoconf automake libpng-devel libjpeg-devel zlib libxml2-devel ncurses-devel bison libtool-ltdl-devel libiconv libmcrypt mhash mcrypt libmcrypt-devel pcre-devel openssl-devel freetype-devel libcurl-devel
2、下载nginx:
wget http://nginx.org/download/nginx-1.12.1.tar.gz
3、编译安装:
./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-stream
添加第三方模块 --add-module=PATH

make make install clean

Nginx配置rsyslog

vim /usr/local/nginx/conf/nginx.conf
可以配置在http或者server段中,凡是可以配置access_log和error_log的位置都可以配置
# syslog表示使用syslog协议
# server=10.26.2.65 指明远程服务器地址,也可以指定本地
# facility=local7 指明设备管道使用local7
# tag=nginx 标签表示在日志文件中显示时候的标题
# severity=info 表示日志级别
access_log syslog:server=127.0.0.1,facility=local7,tag=nginx,severity=info;

vim /etc/rsyslog.conf
默认有下面的设置,会读取 /etc/rsyslog.d/*.conf 目录下的配置文件
$IncludeConfig /etc/rsyslog.d/*.conf
为nginx创建一个独立的配置文件
vim /etc/rsyslog.d/nginx.conf
$ModLoad imudp
$UDPServerRun 514
local7.* /var/log/nginx/access.log
&~
#如果不加下面的的&~则除了在/var/log/nginx/access.log中写入日志外,也会写入message文件 配置rsyslog的主配置文件,开启远程日志
# vim /etc/sysconfig/rsyslog
SYSLOGD_OPTIONS=”-c 2 -r -m 0″
#-c 2 使用兼容模式,默认是 -c 5
#-r 开启远程日志
#-m 0 标记时间戳。单位是分钟,为0时,表示禁用该功能

配置完成后重启nginx和rsyslog即可


系统优化:
user www;
worker_processes 8;
worker_rlimit_nofile 1048576;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;



events {
worker_connections 102400;
multi_accept on;
use epoll;
}


http {
server_tokens off;
include mime.types;
default_type application/octet-stream;
client_header_buffer_size 32k;

keepalive_timeout 300s 300s;
keepalive_requests 10000;

# log_format main '$remote_addr - $remote_user [$time_local] $http_host "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"'
# '$upstream_addr $upstream_response_time $request_time ';


# log_format json '{"@timestamp":"$time_iso8601",'
# '"@version":"1",'
# '"host":"$server_addr",'
# '"client":"$remote_addr",'
# '"size":"$body_bytes_sent",'
# '"responsetime":"$request_time",'
# '"domain":"$http_host",'
# '"request":"$request",'
# '"status":"$status",'
# '"upstream_addr":"$upstream_addr",'
# '"upstream_response_time":"$upstream_response_time",'
# '"request_time":"$request_time"'
# '}';
log_format main '$time_local $remote_addr $status $server_addr $http_host "$request" $body_bytes_sent $http_referer $http_user_agent $upstream_addr $request';
log_format main '$time_local $server_addr $status $http_host $request_uri $args $http_referer $http_user_agent $remote_addr $body_bytes_sent $request_method $upstream_addr $server_port';




# access_log /var/log/nginx/access.log main;
access_log syslog:server=127.0.0.1,facility=local1,tag=nginx,severity=info main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;

#reset_timeout_connection on;
#limit_conn_zone $binary_remote_addr zone=addr:5m;
#limit_conn addr 100;
gzip on;
gzip_disable "msie6";
# gzip_static on;
gzip_proxied any;
gzip_min_length 1000;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

open_file_cache max=100000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffer_size 64k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_ignore_client_abort on;

server {
listen 80;
server_name localhost;



location /nginx_status {
stub_status on;
access_log off;
}

location / {
root html;
index index.html index.htm;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}


}
include /usr/local/nginx/conf/conf.d/*.conf;
}

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600

net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_max_tw_buckets = 6000

net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 20

net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 873200
net.core.wmem_max = 873200

net.ipv4.tcp_rmem = 32768 436600 873200
net.ipv4.tcp_wmem = 8192 436600 873200

net.core.netdev_max_backlog = 32768

net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_retries2 = 5

net.ipv4.tcp_mem = 41943040 73400320 94371840
net.ipv4.tcp_max_orphans = 3276800
fs.file-max = 1300000
net.ipv4.ip_nonlocal_bind=1



二、安装confd
直接拷贝confd到/usr/local/bin/
修改权限 chmod 755 /usr/local/bin/confd
mkdir -p /etc/confd/{conf.d,templates}
confd -version
vim /etc/confd/templates/account.tmpl
1) 80 443共存
{{range $dir := lsdir "/web/account.italent.cn"}}
upstream {{base $dir}} {
{{$custdir := printf "/web/account.italent.cn/%s/*" $dir}}{{range gets $custdir}}
server {{$data := json .Value}}{{$data.IP}}:80 {{$data.WEIGHT}};
{{end}}
}

server {
listen 80;
listen 443 ssl;
server_name {{base $dir}};
ssl_certificate /usr/local/nginx/certs/{{base $dir}}.crt;
ssl_certificate_key /usr/local/nginx/certs/{{base $dir}}.key;
access_log syslog:server=127.0.0.1,facility=local1,tag=nginx,severity=info;
access_log /var/log/nginx/{{base $dir}}.access.log;
error_log /var/log/nginx/{{base $dir}}.log;
location / {
proxy_pass http://{{base $dir}};
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
{{end}}
2) 80 强制跳转443
{{range $dir := lsdir "/web/account.italent.cn"}}
upstream {{base $dir}} {
{{$custdir := printf "/web/account.italent.cn/%s/*" $dir}}{{range gets $custdir}}
server {{$data := json .Value}}{{$data.IP}}:80 {{$data.WEIGHT}};
{{end}}
}

server {
listen 80;
server_name {{base $dir}};
return 301 https://$server_name$request_uri;
access_log syslog:server=127.0.0.1,facility=local1,tag=nginx,severity=info;
access_log /var/log/nginx/{{base $dir}}.access.log;
error_log /var/log/nginx/{{base $dir}}.log;
}

server {
listen 443 ssl;
server_name {{base $dir}};
ssl_certificate /usr/local/nginx/certs/{{base $dir}}.crt;
ssl_certificate_key /usr/local/nginx/certs/{{base $dir}}.key;
access_log syslog:server=127.0.0.1,facility=local1,tag=nginx,severity=info;
access_log /var/log/nginx/{{base $dir}}.access.log;
error_log /var/log/nginx/{{base $dir}}.log;
location / {
proxy_pass http://{{base $dir}};
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
{{end}}

vim /etc/confd/conf.d/account.toml
[template]
keys = [
"/web/account.italent.cn",
]
owner = "nginx"
mode = "0644"
src = "account.tmpl"
dest = "/usr/local/nginx/conf/conf.d/account.conf"


三、证书拷贝
/usr/local/nginx/certs

2018我们一起出发,新希望,新起点

2018-02-22

2018 充满了挑战与感恩的一年

1
仰望星空 脚踏实地

[高可用架构]----新数据中心高可用整理

2018-02-06

新数据中心网络项目背景

1
2
3
4
#项目概述
随着我们公司业务发展,异地旧数据中心,容量不足,已经无法满足,
随着我们公司业务对稳定性高可用冗余性等更高要求,后期异地灾备,多活需要
所以我们需要去构建 新的数据中心,面向未来

旧数据中心-问题概述

  • 容量不足,机柜无法满足业务部署
  • 网络质量不佳,影响客户体验
  • 历史遗留,由于历史部署原因,维护不便
  • 设备老化,技术落后,
  • 单点风险,VPN设备和出口单点
  • 安全需求,没有整网硬件防火墙
  • 千兆性能,万兆网络虚拟机需求
  • 多活机房,灾备机房需求

新数据中心,建设思路

  • 高可用 – 拒绝单点,冗余可靠
  • 高性能 – 越快越好,保障底层
  • 可拓展 – 方便扩容,模块部署
  • 易操作 – 方便维护,故障排错
  • 先进性 – 成熟可靠,实用领先
  • 安全性 – 安全可控,保障数据
  • 简单化 – 力求简单,结构清晰
  • 低延迟 – 万兆专线,高质低延

高可用网络要求

高可用性的定义

1
2
3
4
5
传统意义上的可用性(AVAILABILITY )定义为系统正常运行时间占总运行时间的比例(当前的总运行时间包括出问题以后修复所耗费的时间)。
高可用性由两个基本概念组成:
  MTBF(Mean Time Between Failure),系统平均正常运行时间
  MTTR(Mean Time to Repair),系统平均恢复时间
  可用性的计算公式: AVAILABILITY = MTBF / ( MTBF + MTTR ) × 100%

SLA可用率 与 故障时间 关联表

可用性(每年) 故障时间(每年) 备注
98.000000% 7.3天
99.000000% 3.65天
99.900000% 8.76小时 AWS EC2 和阿里云ECS RDS 99.95%可用性
99.990000% 52.56分钟 AWS S3 99.99%可用性和99.999999999%持久性
99.999000% 5.256分钟 即”5个9的可用性”
99.999900% 31.536秒
99.999990% 3.1537秒
99.999999% 0.31538秒
  • 网络节点与网络链路的故障无法完全避免,所以提升网络可用性的重要方法之一是尽量降低系统的故障恢复时间。

高可用技术

数据中心网络高可用涉及技术:
分类分层 | 高可用技术 | 备注
—|—|—|—
物理设备 | 冗余电源、冗余风扇、冗余主控、板卡支持热插拔; |
链路层面 | 以太网链路聚合(手工聚合、LACP); |
二层多路径 | STP、MSTP、SmartLink, VSS,VPC; |
三层多路径 | VRRP、ECMP、动态路由协议多路径; |
故障检测 | NQA、BFD、OAM、DLDP,FailOver; |
不间断转发 | GR、热补丁升级; |
L7多路径 | 状态热备、非对称路径转发; |
数据中心服务高可用涉及技术:
存储冗余 | RAID,DRBD , San , NAS , GFS , CEPH; |
二层冗余 | Bond 链路绑定 lvs-dr
三层冗余 | Keepalived, ECMP
四层冗余 | LVS (NAT TUNNEL Full-NAT,
七层冗余 | Nginx,Haproxy
应用冗余 | Dubbox, spring cloud
容器冗余 | Swarm、Mesos ,Kubernetes
虚机冗余 | Vmware Vcenter , Openstack , Kvm

在传统的数据中心三层网络结构中(如图1所示),由于存在多设备多路径冗余,就会存在环路结构,因此MSTP/VRRP/OSPF等处理多路径冗余的协议部署至关重要。一般在数据中心的核心到汇聚层间部署OSPF等动态路由协议,在汇聚层网关设备上部署VRRP处理多网关冗余,在汇聚层到接入层之间部署STP/MSTP解决二层网络环路冗余。当在汇聚层或核心层部署防火墙等基于4-7层的状态处理设备时,同时需要在多台设备间部署状态热备等特性。

为了保证数据中心的设备和链路发生故障时业务流量能够快速收敛,还需要部署NQA、BFD、OAM和DLDP等路径检测协议以降低故障检测响应时间。同时可以部署GR等不间断转发协议协助动态路由协议在主备板切换情况下快速完成业务流量收敛。

网络方案

网络拓扑

image

网络划分层

  • 运营商汇聚层
  • 客户侧互联网接入层
  • 客户侧出口层
  • 客户侧核心层
  • 客户侧接入层
  • 客户侧服务器

网络分层冗余方式

分层名称 冗余方式 备注
运营商汇聚层 3种互联网接入 VRRP冗余和双静态路由和OSPF接入 关于运营商接入感觉可以专门梳理一个小章节
客户侧互联网接入层 互联网双线,两台冗余交换级联
客户侧出口层 出口主备模式部署,用于NAT路由和防火墙
客户侧核心层 双核心,双主,双万兆 VPC协议互联
客户侧接入层 双万兆冗余交叉上联 核心交换
客户侧服务器 双线上联双交换机 ,主备线路或者双线hash捆绑

网络冗余测试方式

分层名称 测试方法 验证冗余 结果
运营商汇聚层 上联接入运营商线故障拔线测试 验证内部互联网是否正常 验证火墙主备 验证Vrrp切换 OK
客户侧互联网接入层 接入交换机断电故障测试 验证内部互联网是否正常 验证火墙主备 验证Vrrp切换 OK
客户侧出口层 主备设备主备断电测试 验证内部互联网是否正常 验证火墙主备 OK
客户侧核心层 主备设备断电切换测试 验证互联网和内网是否正常 OK
客户侧接入层 设备故障断电切换测试 验证互联网和内网是否正常 OK
客户侧服务器 线路故障断电切换测试绑 验证互联网和内网是否正常 OK
1
2
#1 测试结论,网络各层全冗余
#2 切换时间测试 断电任意节点设备,网络均可以可以恢复正常通信 ,会有设备切换产生 1-3个切换丢包

网段划分

Vlan ID 描述 IP地址段 vPC ID Port-Channel ID 备注
10 WEB 10.123.10.0/24 —- 用于WEB服务器
20 APP 10.123.10.0/24 —- 用APP服务器
30 DB 10.123.10.0/24 —- 用于DB服务器
100 ESXi 10.123.10.0/24 —- 用于Esxi服务器
110 OPS 10.123.10.0/24 —- 用于OPS服务器
200 MGT 10.123.10.0/24 —- 用于管理服务器
254 Connect 10.123.10.0/24 254 253 254 253 用于互联设备

技术概要

VPC 交换技术(冗余性能)

image

1
2
3
4
vPC(virtual PortChannel,虚拟链路聚合)技术
允许将连接到两个Nexus系列交换机的物理链路视作一条逻辑端口聚合链路,再连接到其他设备。
其他设备包括:Nexus 2000 系列的 Fabric Extender(后文暂且翻译为结构扩展器),交换机,服务器等其他任何的网络设备。
vPC技术可以提供二层的多路径选择,让用户可以在有多条可选路径的情况下增加网络冗余度,同时实现提升带宽、在多个节点之间启用多条并行路径和对流量进行负载均衡。

VPC技术与传统交换网络优势对比

image

  • 跨机箱多链路捆绑;
  • 避免以太网环路;
  • 增加链路带宽;
  • 双活的工作机制,支持双机箱双引擎性能;
  • 故障时可以实现快速收敛;
  • 网络架构简单、更可靠;
  • 有效使用 2 层带宽;

FailOver 故障转移技术(防火墙)

1
2
3
4
Failover

Cisco防火墙一种高可用技术,能在防火墙发生故障时数秒内转移
配置到另一台设备,使网络保持畅通,达到设备级冗余的目的。
  • 故障转移 心跳线
1
故障转移配置的技术,需要两台完全一样的设备,通过一个连接,连接到对方(这个连接也叫心跳线)。该技术用到的两台设备分为 主用和备用,备用处于待机状态。当主用设备故障后,备用设备可启用,并设置为主用,运行自主用设备复制过来的配置(配置是跟随主用设备移动的)。
  • 故障转移 状态线
1
同步NAT, TCP ,HTTP ,DHCP SSLvPN,等状态信息,保障长连接不丢失

Vrrp 虚拟路由器冗余网关协议

1
2
虚拟路由冗余协议(VRRP,Virtual Router Redundancy Protocol)是一种容错协议,保证当主机的下一跳路由器失效时,及时的由另一台路由器来替代,从而保持通信的连续性和可靠性。
为了使VRRP工作,要在路由器中配置VRRP虚拟网关,使得网络上的主机与虚拟路由器通信,无需了解这个网络上物理路由器的任何信息。

相关资料文档

1
2
3
4
5
#Nexus 交换机配置手册
https://www.cisco.com/c/en/us/support/switches/nexus-5000-series-switches/products-installation-and-configuration-guides-list.html

#ASA 防火墙配置手册
https://www.cisco.com/c/en/us/td/docs/security/asa/asa94/config-guides/cli/firewall/asa-94-firewall-config.html

注释

1
ip相关信息均已修改。

[运维平台自建之路]OneOps运维平台,心路历程随想回望 -- 开篇

2018-01-24

- (待补充补充)

题记:

1
2
3
4
5
一晃过去,二年有余,一路走路来,
从呀呀学语到蹒跚学步,从未知探索到小成一体,仍待完善
这一路走来,酸甜尽有,受益良多,需要感谢
感觉走的久了,有时候我们就可能背离了初衷,渐行渐远
所以趁着年终,想去做下总结,思考,以便回眸,调整,提高

关于:

  • 考虑想写点东西,做些小结,以便整理完善,反思提高吧,运维有益
  • 运维范围:中小团队平台

构想图:

  • 这是最初计划原型 平台架构图,
  • 原则一:尽量使用开源,二开组装(人力有限,1-3人ops,无开发)
  • 原则二: 尽量简单,减少复杂
  • image

运维平台图:

  • (待补充完善,回来补充。。)
    image

功能原型图

涉及原型项目

[K8s 1.9实践]Kubeadm HA 1.9 高可用 集群 本地离线部署-V20180226

2018-01-19

Kubeadm HA 1.9 高可用 集群 本地离线部署

image

k8s介绍

  • k8s 发展速度很快,目前很多大的公司容器集群都基于该项目,如京东,腾讯,滴滴,瓜子二手车,易宝支付,北森等等。
    image
  • kubernetes1.9版本发布2017年12月15日,每三个月一个迭代版本, Workloads API成为稳定版本,这消除了很多潜在用户对于该功能稳定性的担忧。还有一个重大更新,就是测试支持了Windows了,这打开了在kubernetes中运行Windows工作负载的大门。
  • CoreDNS alpha可以使用标准工具来安装CoreDNS
  • kube-proxy的IPVS模式进入beta版,为大型集群提供更好的可扩展性和性能。
  • kube-router的网络插件支持,更方便进行路由控制,发布,和安全策略管理

k8s 核心架构

image
如架构图

  • k8s 高可用2个核心 ==apiserver master== and ==etcd==
  • ==apiserver master==:(需高可用)集群核心,集群API接口、集群各个组件通信的中枢;集群安全控制;
  • ==etcd== :(需高可用)集群的数据中心,用于存放集群的配置以及状态信息,非常重要,如果数据丢失那么集群将无法恢复;因此高可用集群部署首先就是etcd是高可用集群;
  • kube-scheduler:调度器 (内部自选举)集群Pod的调度中心;默认kubeadm安装情况下–leader-elect参数已经设置为true,保证master集群中只有一个kube-scheduler处于活跃状态;
  • kube-controller-manager: 控制器 (内部自选举)集群状态管理器,当集群状态与期望不同时,kcm会努力让集群恢复期望状态,比如:当一个pod死掉,kcm会努力新建一个pod来恢复对应replicas set期望的状态;默认kubeadm安装情况下–leader-elect参数已经设置为true,保证master集群中只有一个kube-controller-manager处于活跃状态;
  • kubelet: agent node注册apiserver
  • kube-proxy: 每个node上一个,负责service vip到endpoint pod的流量转发,老版本主要通过设置iptables规则实现,新版1.9基于kube-proxy-lvs 实现 更好性能和负载拓展

部署示意图

image

  • 集群ha方案,我们力求简单,使用keepalive 监听一个vip来实现,(当节点不可以后,会有vip漂移的切换时长,取决于我们设置timeout切换时长,测试会有10s空档期,如果对高可用更高要求 可以用lvs或者nginx做 4层lb负载 更佳完美,我们力求简单够用,可接受10s的api不可用)
  • etcd和master 测试采用 3节点共用部署,(生成环境可以考虑etcd和master分离或者等kubeadm ha正式版推出)

部署环境

1
2
最近在部署k8s 1.9集群遇到一些问题,整理记录,或许有助需要的朋友。
因为kubeadm 简单便捷,所以集群基于该项目部署,目前bete版本不支持ha部署,github说2018年预计发布ha 版本,可我们等不及了 呼之欲来。。。

环境 版本
Centos CentOS Linux release 7.3.1611 (Core)
Kernel Linux etcd-host1 3.10.0-514.el7.x86_64
yum base repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum epel repo http://mirrors.aliyun.com/repo/epel-7.repo
kubectl v1.9.0
kubeadmin v1.9.0
docker 1.12.6
docker localre devhub.beisencorp.com

主机名称 相关信息 备注
etcd-host1 10.129.6.211 master和etcd
etcd-host2 10.129.6.212 master和etcd
etcd-host3 10.129.6.213 master和etcd
Vip-keepalive 10.129.6.220 vip用于高可用

环境部署 (我们使用本地离线镜像)

环境预初始化

  • Centos Mini安装 每台机器root
  • 设置机器名
1
hostnamectl set-hostname etcd-host1
  • 停防火墙
1
2
3
systemctl stop firewalld
systemctl disable firewalld
systemctl disable firewalld
  • 关闭Swap
1
2
swapoff -a 
sed 's/.*swap.*/#&/' /etc/fstab
  • 关闭防火墙
1
systemctl disable firewalld && systemctl stop firewalld && systemctl status firewalld
  • 关闭Selinux
1
2
3
4
5
6
7
setenforce  0 
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/selinux/config

getenforce
  • 增加DNS
1
echo nameserver 114.114.114.114>>/etc/resolv.conf
  • 设置内核
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl -p /etc/sysctl.conf

#若问题
执行sysctl -p 时出现:
sysctl -p
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory

解决方法:
modprobe br_netfilter
ls /proc/sys/net/bridge

配置keepalived

  • VIP Master 通过控制VIP 来HA高可用(常规方案)
  • 到目前为止,三个master节点 相互独立运行,互补干扰. kube-apiserver作为核心入口, 可以使用keepalived 实现高可用, kubeadm join暂时不支持负载均衡的方式,所以我们
  • 安装
1
yum install -y keepalived
  • 配置keepalived.conf
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
cat >/etc/keepalived/keepalived.conf  <<EOL
global_defs {
router_id LVS_k8s
}

vrrp_script CheckK8sMaster {
script "curl -k https://10.129.6.220:6443"
interval 3
timeout 9
fall 2
rise 2
}

vrrp_instance VI_1 {
state MASTER
interface ens32
virtual_router_id 61
# 主节点权重最高 依次减少
priority 120
advert_int 1
#修改为本地IP
mcast_src_ip 10.129.6.211
nopreempt
authentication {
auth_type PASS
auth_pass sqP05dQgMSlzrxHj
}
unicast_peer {
#注释掉本地IP
#10.129.6.211
10.129.6.212
10.129.6.213
}
virtual_ipaddress {
10.129.6.220/24
}
track_script {
CheckK8sMaster
}

}
EOL
  • 启动
1
systemctl enable keepalived && systemctl restart keepalived
  • 结果
1
2
3
4
5
6
7
8
9
10
11
12
[root@etcd-host1 k8s]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availabilitymonitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2018-01-19 10:27:58 CST; 8h ago
Main PID: 1158 (keepalived)
CGroup: /system.slice/keepalived.service
├─1158 /usr/sbin/keepalived -D
├─1159 /usr/sbin/keepalived -D
└─1161 /usr/sbin/keepalived -D

Jan 19 10:28:00 etcd-host1 Keepalived_vrrp[1161]: Sending gratuitous ARP on ens32 for 10.129.6.220
Jan 19 10:28:05 etcd-host1 Keepalived_vrrp[1161]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on ens32 for 10.129.6.220
  • 依次配置 其他2台从节点master 配置 修改对应节点 ip
  • master01 priority 120
  • master02 priority 110
  • master03 priority 100

Etcd https 集群部署

Etcd 环境准备

1
2
3
4
5
6
7
8
9
10
11
#机器名称
etcd-host1:10.129.6.211
etcd-host2:10.129.6.212
etcd-host3:10.129.6.213

#部署环境变量
export NODE_NAME=etcd-host3 #当前部署的机器名称(随便定义,只要能区分不同机器即可)
export NODE_IP=10.129.6.213 # 当前部署的机器 IP
export NODE_IPS="10.129.6.211 10.129.6.212 10.129.6.213" # etcd 集群所有机器 IP
# etcd 集群间通信的IP和端口
export ETCD_NODES=etcd-host1=https://10.129.6.211:2380,etcd-host2=https://10.129.6.212:2380,etcd-host3=https://10.129.6.213:2380

Etcd 证书创建(我们使用https方式)

创建 CA 证书和秘钥

  • 安装cfssl, CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Authority (CA) 证书和秘钥文件
  • 如果不希望将cfssl工具安装到部署主机上,可以在其他的主机上进行该步骤,生成以后将证书拷贝到部署etcd的主机上即可。本教程就是采取这种方法,在一台测试机上执行下面操作。
1
2
3
4
5
6
7
8
9
10
11
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
chmod +x cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

生成ETCD的TLS 秘钥和证书

  • 为了保证通信安全,客户端(如 etcdctl) 与 etcd 集群、etcd 集群之间的通信需要使用 TLS 加密,本节创建 etcd TLS 加密所需的证书和私钥。

  • 创建 CA 配置文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     cat >  ca-config.json <<EOF
    {
    "signing": {
    "default": {
    "expiry": "8760h"
    },
    "profiles": {
    "kubernetes": {
    "usages": [
    "signing",
    "key encipherment",
    "server auth",
    "client auth"
    ],
    "expiry": "8760h"
    }
    }
    }
    }
    EOF
  • ==ca-config.json==:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;

  • ==signing==:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;
  • ==server auth==:表示 client 可以用该 CA 对 server 提供的证书进行验证;
  • ==client auth==:表示 server 可以用该 CA 对 client 提供的证书进行验证;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    cat >  ca-csr.json <<EOF
    {
    "CN": "kubernetes",
    "key": {
    "algo": "rsa",
    "size": 2048
    },
    "names": [
    {
    "C": "CN",
    "ST": "BeiJing",
    "L": "BeiJing",
    "O": "k8s",
    "OU": "System"
    }
    ]
    }
    EOF
  • “CN”:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;

  • “O”:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
  • ==生成 CA 证书和私钥==:
1
2
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
ls ca*

==创建 etcd 证书签名请求:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat > etcd-csr.json <<EOF
{
"CN": "etcd",
"hosts": [
"127.0.0.1",
"10.129.6.211",
"10.129.6.212",
"10.129.6.213"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}
EOF
  • hosts 字段指定授权使用该证书的 etcd 节点 IP;
  • 每个节点IP 都要在里面 或者 每个机器申请一个对应IP的证书

生成 etcd 证书和私钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cfssl gencert -ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes etcd-csr.json | cfssljson -bare etcd

ls etcd*

mkdir -p /etc/etcd/ssl
cp etcd.pem etcd-key.pem ca.pem /etc/etcd/ssl/
#

#其他node
rm -rf /etc/etcd/ssl/*
scp -r /etc/etcd/ssl root@10.129.6.211:/etc/etcd/

scp -r root@10.129.6.211:/root/k8s/etcd/etcd-v3.3.0-rc.1-linux-amd64.tar.gz /root

将生成好的etcd.pem和etcd-key.pem以及ca.pem三个文件拷贝到目标主机的/etc/etcd/ssl目录下。

下载二进制安装文件

https://github.com/coreos/etcd/releases 页面下载最新版本的二进制文件:

1
2
3
4
5
wget http://github.com/coreos/etcd/releases/download/v3.1.10/etcd-v3.1.10-linux-amd64.tar.gz

tar -xvf etcd-v3.1.10-linux-amd64.tar.gz

mv etcd-v3.1.10-linux-amd64/etcd* /usr/local/bin

创建 etcd 的 systemd unit 文件

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
mkdir -p /var/lib/etcd  # 必须先创建工作目录
cat > etcd.service <<EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
ExecStart=/usr/local/bin/etcd \\
--name=${NODE_NAME} \\
--cert-file=/etc/etcd/ssl/etcd.pem \\
--key-file=/etc/etcd/ssl/etcd-key.pem \\
--peer-cert-file=/etc/etcd/ssl/etcd.pem \\
--peer-key-file=/etc/etcd/ssl/etcd-key.pem \\
--trusted-ca-file=/etc/etcd/ssl/ca.pem \\
--peer-trusted-ca-file=/etc/etcd/ssl/ca.pem \\
--initial-advertise-peer-urls=https://${NODE_IP}:2380 \\
--listen-peer-urls=https://${NODE_IP}:2380 \\
--listen-client-urls=https://${NODE_IP}:2379,http://127.0.0.1:2379 \\
--advertise-client-urls=https://${NODE_IP}:2379 \\
--initial-cluster-token=etcd-cluster-0 \\
--initial-cluster=${ETCD_NODES} \\
--initial-cluster-state=new \\
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF
  • 指定 etcd 的工作目录和数据目录为 /var/lib/etcd,需在启动服务前创建这个目录;
  • 为了保证通信安全,需要指定 etcd 的公私钥(cert-file和key-file)、Peers 通信的公私钥和 CA 证书(peer-cert-file、peer-key-file、peer-trusted-ca-file)、客户端的CA证书(trusted-ca-file);
  • –initial-cluster-state 值为 new 时,–name 的参数值必须位于 –initial-cluster 列表中;

启动 etcd 服务

1
2
3
4
5
mv etcd.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
systemctl status etcd

验证服务

1
2
3
4
5
6
etcdctl \
--endpoints=https://${NODE_IP}:2379 \
--ca-file=/etc/etcd/ssl/ca.pem \
--cert-file=/etc/etcd/ssl/etcd.pem \
--key-file=/etc/etcd/ssl/etcd-key.pem \
cluster-health

预期结果:

1
2
3
4
5
[root@node02 ~]# etcdctl   --endpoints=https://${NODE_IP}:2379    --ca-file=/etc/etcd/ssl/ca.pem   --cert-file=/etc/etcd/ssl/etcd.pem   --key-file=/etc/etcd/ssl/etcd-key.pem   cluster-health
member 18699a64c36a7e7b is healthy: got healthy result from https://10.129.6.213:2379
member 5dbd6a0b2678c36d is healthy: got healthy result from https://10.129.6.211:2379
member 6b1bf02f85a9e68f is healthy: got healthy result from https://10.129.6.212:2379
cluster is healthy

若有失败 或 重新配置

1
2
3
4
5
6
7
8
9
systemctl stop etcd

rm -Rf /var/lib/etcd

rm -Rf /var/lib/etcd-cluster

mkdir -p /var/lib/etcd

systemctl start etcd

k8s 安装

提取k8s rpm 包

  • 默认由于某某出海问题
  • 我们离线导入下rpm 仓库
  • 安装官方YUM 仓库
1
2
# kubeadm 1.9.0版本RPM离线包,有朋友说翻墙困难
链接:https://pan.baidu.com/s/1snaAVpV 密码:rcwo
1
2
3
4
5
6
7
8
9
10
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

安装kubeadm kubectl cni

  • 下载镜像(自行搬梯子先获取rpm)
1
2
3
4
5
6
7
8
9
mkdir -p  /root/k8s/rpm
cd /root/k8s/rpm

#安装同步工具
yum install -y yum-utils

#同步本地镜像
yumdownloader kubelet kubeadm kubectl kubernetes-cni docker
scp root@10.129.6.224:/root/k8s/rpm/* /root/k8s/rpm
  • 离线安装
1
2
3
mkdir -p  /root/k8s/rpm
scp root@10.129.6.211:/root/k8s/rpm/* /root/k8s/rpm
yum install /root/k8s/rpm/*.rpm -y
  • 启动k8s
1
2
3
#restart
systemctl enable docker && systemctl restart docker
systemctl enable kubelet && systemctl restart kubelet

镜像获取方法

  • 加速器获取 gcr.io k8s镜像 ,导出,导入镜像 或 上传本地仓库
1
2
3
4
5
6
7
8
9
10
11
12
#国内可以使用daocloud加速器下载相关镜像,然后通过docker save、docker load把本地下载的镜像放到kubernetes集群的所在机器上,daocloud加速器链接如下:
https://www.daocloud.io/mirror#accelerator-doc

#pull 获取
docker pull gcr.io/google_containers/kube-proxy-amd64:v1.9.0

#导出
mkdir -p docker-images
docker save -o docker-images/kube-proxy-amd64 gcr.io/google_containers/kube-proxy-amd64:v1.9.0

#导入
docker load -i /root/kubeadm-ha/docker-images/kube-proxy-amd64
  • 代理或vpn获取 gcr.io k8s镜 ,导出,导入镜像 或 上传本地仓库
1
自谋生路,天机屋漏

kubelet 指定本地镜像

kubelet 修改 配置以使用本地自定义pause镜像
devhub.beisencorp.com/google_containers/pause-amd64:3.0 替换你的环境镜像

1
2
3
4
5
6
cat > /etc/systemd/system/kubelet.service.d/20-pod-infra-image.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--pod-infra-container-image=devhub.beisencorp.com/google_containers/pause-amd64:3.0"
EOF
systemctl daemon-reload
systemctl restart kubelet

Kubeadm Init 初始化

  • 我们使用config 模板方式来初始化集群,便于我们指定etcd 集群
  • devhub.beisencorp.com 使我们的 测试镜像仓库 可以改成自己或者手动导入每个机器镜像
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
cat <<EOF > config.yaml 
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
etcd:
endpoints:
- https://10.129.6.211:2379
- https://10.129.6.212:2379
- https://10.129.6.213:2379
caFile: /etc/etcd/ssl/ca.pem
certFile: /etc/etcd/ssl/etcd.pem
keyFile: /etc/etcd/ssl/etcd-key.pem
dataDir: /var/lib/etcd
networking:
podSubnet: 10.244.0.0/16
kubernetesVersion: 1.9.0
api:
advertiseAddress: "10.129.6.220"
token: "b99a00.a144ef80536d4344"
tokenTTL: "0s"
apiServerCertSANs:
- etcd-host1
- etcd-host2
- etcd-host3
- 10.129.6.211
- 10.129.6.212
- 10.129.6.213
- 10.129.6.220
featureGates:
CoreDNS: true
imageRepository: "devhub.beisencorp.com/google_containers"
EOF
  • 初始化集群
1
kubeadm init --config config.yaml
  • 结果
1
2
3
4
5
6
7
8
To start using your cluster, you need to run the following as a regular user:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
as root:

kubeadm join --token b99a00.a144ef80536d4344 10.129.6.220:6443 --discovery-token-ca-cert-hash sha256:ebc2f64e9bcb14639f26db90288b988c90efc43828829c557b6b66bbe6d68dfa
  • 查看node
1
2
3
4
5
6
7
8
9
10
[root@etcd-host1 k8s]# kubectl get node
NAME STATUS ROLES AGE VERSION
etcd-host1 noReady master 5h v1.9.0
[root@etcd-host1 k8s]# kubectl get cs
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-1 Healthy {"health": "true"}
etcd-2 Healthy {"health": "true"}
etcd-0 Healthy {"health": "true"}
  • 问题记录
1
2
3
4
5
6
7
8
9
10
11
如果使用kubeadm初始化集群,启动过程可能会卡在以下位置,那么可能是因为cgroup-driver参数与docker的不一致引起
[apiclient] Created API client, waiting for the control plane to become ready
journalctl -t kubelet -S '2017-06-08'查看日志,发现如下错误
error: failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: "systemd"
需要修改KUBELET_CGROUP_ARGS=--cgroup-driver=systemd为KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs

vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
#Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=systemd"
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs"

systemctl daemon-reload && systemctl restart kubelet

安装网络组件 podnetwork

  • 我们选用kube-router
1
2
3
wget https://github.com/cloudnativelabs/kube-router/blob/master/daemonset/kubeadm-kuberouter.yaml

kubectl apply -f kubeadm-kuberouter.yaml
  • 结果
1
2
3
4
5
6
7
8
9
[root@etcd-host1 k8s]# kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-546545bc84-zc5dx 1/1 Running 0 6h
kube-system kube-apiserver-etcd-host1 1/1 Running 0 6h
kube-system kube-controller-manager-etcd-host1 1/1 Running 0 6h
kube-system kube-proxy-pfj7x 1/1 Running 0 6h
kube-system kube-router-858b7 1/1 Running 0 37m
kube-system kube-scheduler-etcd-host1 1/1 Running 0 6h
[root@etcd-host1 k8s]#

部署其他Master 节点

  • 拷贝master01 配置 master02 master03
1
2
3
4
5
6
#拷贝pki 证书
mkdir -p /etc/kubernetes/pki
scp -r root@10.129.6.211:/etc/kubernetes/pki /etc/kubernetes

#拷贝初始化配置
scp -r root@10.129.6.211://root/k8s/config.yaml /etc/kubernetes/config.yaml
  • 初始化 master02 master03
1
2
#初始化
kubeadm init --config /etc/kubernetes/config.yaml

部署成功 验证结果

为了测试我们把master 设置为 可部署role

默认情况下,为了保证master的安全,master是不会被调度到app的。你可以取消这个限制通过输入:

1
kubectl taint nodes --all node-role.kubernetes.io/master-

录制终端验证 结果

asciicast

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
-验证
[zeming@etcd-host1 k8s]$ kubectl get node
NAME STATUS ROLES AGE VERSION
etcd-host1 Ready master 6h v1.9.0
etcd-host2 Ready master 5m v1.9.0
etcd-host3 Ready master 49s v1.9.0
[zeming@etcd-host1 k8s]$ kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default nginx01-d87b4fd74-2445l 1/1 Running 0 1h
default nginx01-d87b4fd74-7966r 1/1 Running 0 1h
default nginx01-d87b4fd74-rcbhw 1/1 Running 0 1h
kube-system coredns-546545bc84-zc5dx 1/1 Running 0 3d
kube-system kube-apiserver-etcd-host1 1/1 Running 0 3d
kube-system kube-apiserver-etcd-host2 1/1 Running 0 3d
kube-system kube-apiserver-etcd-host3 1/1 Running 0 3d
kube-system kube-controller-manager-etcd-host1 1/1 Running 0 3d
kube-system kube-controller-manager-etcd-host2 1/1 Running 0 3d
kube-system kube-controller-manager-etcd-host3 1/1 Running 0 3d
kube-system kube-proxy-gk95d 1/1 Running 0 3d
kube-system kube-proxy-mrzbq 1/1 Running 0 3d
kube-system kube-proxy-pfj7x 1/1 Running 0 3d
kube-system kube-router-bbgpq 1/1 Running 0 3h
kube-system kube-router-v2jbh 1/1 Running 0 3h
kube-system kube-router-w4cbb 1/1 Running 0 3h
kube-system kube-scheduler-etcd-host1 1/1 Running 0 3d
kube-system kube-scheduler-etcd-host2 1/1 Running 0 3d
kube-system kube-scheduler-etcd-host3 1/1 Running 0 3d
[zeming@etcd-host1 k8s]$

主备测试

  • 关闭 主节点 master01 观察切换到 master02 机器
  • master03 一直不管获取node信息 测试高可用
1
while true; do  sleep 1; kubectl get node;date; done

观察主备VIP切换过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#观察当Master01主节点关闭后,被节点VIP状态  BACKUP  切换到 MASTER 
[root@etcd-host2 net.d]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2018-01-22 13:54:17 CST; 21s ago

Jan 22 13:54:17 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) Transition to MASTER STATE
Jan 22 13:54:17 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) Received advert with higher priority 120, ours 110
Jan 22 13:54:17 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) Entering BACKUP STATE

#切换到 MASTER
[root@etcd-host2 net.d]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2018-01-22 13:54:17 CST; 4min 6s ago

Jan 22 14:03:02 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) Transition to MASTER STATE
Jan 22 14:03:03 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) Entering MASTER STATE
Jan 22 14:03:03 etcd-host2 Keepalived_vrrp[15908]: VRRP_Instance(VI_1) setting protocol VIPs.
Jan 22 14:03:03 etcd-host2 Keepalived_vrrp[15908]: Sending gratuitous ARP on ens32 for 10.129.6.220

验证集群高可用

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
#观察 master01 关机后状态变成NotReady
[root@etcd-host3 ~]# while true; do sleep 1; kubectl get node;date; done
Tue Jan 22 14:03:16 CST 2018
NAME STATUS ROLES AGE VERSION
etcd-host1 Ready master 19m v1.9.0
etcd-host2 Ready master 3d v1.9.0
etcd-host3 Ready master 3d v1.9.0
Tue Jan 22 14:03:17 CST 2018
NAME STATUS ROLES AGE VERSION
etcd-host1 NotReady master 19m v1.9.0
etcd-host2 Ready master 3d v1.9.0
etcd-host3 Ready master 3d v1.9.0



#恢复Master主节点后,出现VIP偏移过来,api恢复
The connection to the server 10.129.6.220:6443 was refused - did you specify the right host or port?
Tue Jan 23 14:14:05 CST 2018
The connection to the server 10.129.6.220:6443 was refused - did you specify the right host or port?
Tue Jan 23 14:14:07 CST 2018
Tue Jan 23 14:14:18 CST 2018
NAME STATUS ROLES AGE VERSION
etcd-host1 NotReady master 29m v1.9.0
etcd-host2 Ready master 3d v1.9.0
etcd-host3 Ready master 3d v1.9.0
Tue Jan 23 14:14:20 CST 2018
NAME STATUS ROLES AGE VERSION
etcd-host1 Ready master 29m v1.9.0
etcd-host2 Ready master 3d v1.9.0
etcd-host3 Ready master 3d v1.9.0

参观文档

1
2
3
4
5
6
7
8
9
#k8s 官方文档
https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/
#kubeadm ha 项目文档
https://github.com/indiketa/kubeadm-ha
https://github.com/cookeem/kubeadm-ha/blob/master/README_CN.md
https://medium.com/@bambash/ha-kubernetes-cluster-via-kubeadm-b2133360b198
#kubespray 之前的kargo ansible项目
https://github.com/kubernetes-incubator/kubespray/blob/master/docs/ha-mode.md
#若有问题或转载请注明出处 By Zeming

http://xuzeming.top/2018/01/19/K8s-1-9%E5%AE%9E%E8%B7%B5-Kubeadm-HA-1-9-%E9%AB%98%E5%8F%AF%E7%94%A8-%E9%9B%86%E7%BE%A4-%E6%9C%AC%E5%9C%B0%E7%A6%BB%E7%BA%BF%E9%83%A8%E7%BD%B2/

[Kube-router 实践]K8s 1.9 Kube-router BGP外部对接发布内部路由 和 高可用

2018-01-09

实验背景

新版本特性:

  • k8s 1.9 做了很大组件性能改进 ,
  • 本版本用kube-router组件取代kube-proxy,用lvs做svc负载均衡,更快稳定。
  • 用coredns取代kube-dns,DNS更稳定。
  • 经过测试1.9版,消除了以往的kubelet docker狂报错误日志的错误 ,更完美
  • 支持 add动态插件

功能需求:

  • 发布内部k8s网络,到机房全网
  • cluster-ip,external-ip 全网路由
  • 解决iptables 性能和负载聚合问题
  • 还有iptables 负载NAT 丢失源ip问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
k8s测试版本
kubeadm version: &version. GitVersion:"v1.9.0", BuildDate:"2017-12-15T20:55:30Z"
网络设备
Cisco 7200
R1 10.129.6.91
R2 10.129.6.92
Vrrp 10.129.6.8

K8s Node网络
node01 10.129.6.211
node03 10.129.6.213
K8s 网络
10.244.0.0/16
SVC 网络
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d
nginx01 ClusterIP 10.110.133.162 10.33.1.11 80/TCP 6d

网络拓扑如图:

image

  • 网络高可用,目前我的方案是
  • 双核心交换机跑VRRP
  • 模拟器,模拟网络设备拓扑 R1 R2 主备路由器
    image
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
#R1 Cisco Config

interface FastEthernet0/0
ip address 10.129.6.91 255.255.255.0
vrrp 1 ip 10.129.6.8
vrrp 1 priority 150

router bgp 64513
no synchronization
bgp log-neighbor-changes
neighbor 10.129.6.92 remote-as 64513
neighbor 10.129.6.211 remote-as 64512
neighbor 10.129.6.213 remote-as 64512
maximum-paths 2
no auto-summary

ip route 0.0.0.0 0.0.0.0 10.129.6.1

###############################
#R2 Cisco Config
interface FastEthernet0/0
ip address 10.129.6.92 255.255.255.0
vrrp 1 ip 10.129.6.8
vrrp 1 priority 110

router bgp 64513
no synchronization
bgp log-neighbor-changes
neighbor 10.129.6.92 remote-as 64513
neighbor 10.129.6.211 remote-as 64512
neighbor 10.129.6.213 remote-as 64512
maximum-paths 2
no auto-summary

ip route 0.0.0.0 0.0.0.0 10.129.6.1

部署k8s 1.9

此处省略,测试使用所以用
kubeadm 部署简单快速
具体可以参看官方文档

1
https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/

部署 kube-router

体系结构

Kube路由器是围绕观察者和控制器的概念而建立的。 观察者使用Kubernetes监视API来获取与创建,更新和删除Kubernetes对象有关的事件的通知。 每个观察者获取与特定API对象相关的通知。 在从API服务器接收事件时,观察者广播事件。 控制器注册以获取观察者的事件更新,并处理事件。
Kube-router由3个核心控制器和多个观察者组成,如下图所示。
image
主要改进

  • 使用ipvs 替代 Iptables 方案 -性能更好
  • 使用BGP 组网 更易发布和扩展对接 之前方案要使用caclio 其他方案
  • 集成police 网络策略 之前网络层控制很弱
  • 负载均衡 更多可选功能 如rr sip hash ip回话保持
    更多信息关注https://cloudnativelabs.github.io
deployment部署Yaml文件

在Kubernetes上部署Kube-router的最好的入门方法是使用集群安装程序.

  • kubeadm 部署Kube-router,比较简单就是导入yaml文件即可
    我们使用的是如下,为了对接Cisco 网络设备发布路由
1
2
3
wget https://github.com/cloudnativelabs/kube-router/blob/master/daemonset/kube-router-all-service-daemonset-advertise-routes.yaml

kubectl apply -f kube-router-all-service-daemonset-advertise-routes.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#我们根据实际环境修改下
containers:
- args:
- --run-router=true
#启用Pod网络 - 通过iBGP发布并学习到Pod的路由。 (默认为true)
- --run-firewall=true
#启用网络策略 - 设置iptables为pod提供入口防火墙。 (默认为true)
- --run-service-proxy=true
#启用服务代理 - 为Kubernetes服务设置IPVS。 (默认为true)
- --advertise-cluster-ip=true
#将该服务的集群IP添加到RIB,以便通告给BGP peers.
- --advertise-external-ip=true
#将服务的外部IP添加到RIB,以便将其通告给BGP peers.
- --cluster-asn=64512
#集群自身节点运行iBGP的ASN编号.
- --peer-router-ips=10.129.6.8
#所有节点将对等的外部路由器的IP地址,并通告集群ip和pod cidr。 (默认[])
- --peer-router-asns=64513
#集群节点将向其通告集群ip和节点的pid cidr的BGP peers的ASN编号。 (默认[])
  • 更多部署请自行查询
1
https://github.com/cloudnativelabs/kube-router/tree/master/daemonset

部署的nginx 测试服务

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
#
apiVersion: v1
kind: Service
metadata:
annotations:
kube-router.io/service.scheduler: sh
labels:
app: nginx01
group: default
role: master
tier: backend
name: nginx01
namespace: default
spec:
clusterIP: 10.110.133.162
externalIPs:
- 10.33.1.11
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx01
group: default
role: master
tier: backend
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx01
# these labels can be applied automatically
# from the labels in the pod template if not set
# labels:
# app: redis
# role: master
# tier: backend
namespace: default
spec:
# this replicas value is default
# modify it according to your case
replicas: 1
# selector can be applied automatically
# from the labels in the pod template if not set
# selector:
# matchLabels:
# app: guestbook
# role: master
# tier: backend
template:
metadata:
labels:
app: nginx01
role: master
tier: backend
group: default
spec:
containers:
- name: nginx01
image: devhub.beisencorp.com/test/nginx:v3 # or just image: redis
resources:
requests:
cpu: 100m
memory: 80Mi
limits:
cpu: 500m
memory: 200M
ports:
- containerPort: 80

测试路由

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
74
75
76
77
78
79
80
81
82
83
84
[root@node01 k8s]#kubectl get svc            
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d
nginx01 ClusterIP 10.110.133.162 10.33.1.11 80/TCP 6d
[root@node01 k8s]# curl 10.33.1.11
< !DOCTYPE html>
< html>
< head>
< meta charset="UTF-8">
< title> 主标题 | 副标题< /title>
< /head>
< body>
<p>Hello, world! 我是版本V1
My V2 e WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>ZX
< /body>
< /html>


## 我们查看下 网络设备路由器信息是否学习过来
#BGP邻居已建立
R1#sh bgp sum
BGP router identifier 10.129.6.91, local AS number 64513
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
10.129.6.92 4 64513 147 146 25 0 0 02:16:05 6
10.129.6.211 4 64512 306 269 0 0 0 01:28:40 Active
10.129.6.213 4 64512 314 270 0 0 0 01:28:40 Active
R1#sh ip route
10.0.0.0/8 is variably subnetted, 7 subnets, 2 masks
B 10.33.1.11/32 [200/0] via 10.129.6.92, 01:28:33
B 10.110.133.162/32 [200/0] via 10.129.6.92, 01:28:33
B 10.96.0.10/32 [200/0] via 10.129.6.92, 01:28:33
B 10.96.0.1/32 [200/0] via 10.129.6.92, 01:28:33
C 10.129.6.0/24 is directly connected, FastEthernet0/0
B 10.244.0.0/24 [200/0] via 10.129.6.211, 01:28:33
B 10.244.1.0/24 [200/0] via 10.129.6.213, 01:28:33
S* 0.0.0.0/0 [1/0] via 10.129.6.1

#R2 同上类同 R2备节点从R1主节点 学习全网路由


### 我们在看下 kube-router邻居和路由是否学习过来
Here's a quick look at what's happening on this Node
--- BGP Server Configuration ---
AS: ======64512======
Router-ID: 10.129.6.211
Listening Port: 179, Addresses: 0.0.0.0, ::

--- BGP Neighbors ---
Peer AS Up/Down State |#Received Accepted
10.129.6.8 64513 00:00:15 Establ | 6 0
10.129.6.213 64512 03:40:40 Establ | 1 1

--- BGP Route Info ---
Network Next Hop AS_PATH Age Attrs
*> 10.33.1.11/32 10.129.6.211 00:00:58 [{Origin: i}]
*> 10.96.0.1/32 10.129.6.211 00:00:58 [{Origin: i}]
*> 10.96.0.10/32 10.129.6.211 00:00:58 [{Origin: i}]
*> 10.110.133.162/32 10.129.6.211 00:00:58 [{Origin: i}]
*> 10.244.0.0/24 10.129.6.211 00:00:58 [{Origin: i}]
*> 10.244.1.0/24 10.129.6.213 03:40:40 [{Origin: i} {LocalPref: 100}]

--- IPVS Services ---
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.33.1.11:80 sh
-> 10.244.1.16:80 Masq 1 0 0
TCP 10.96.0.1:443 rr persistent 10800
-> 10.129.6.211:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.0.13:53 Masq 1 0 0
TCP 10.110.133.162:80 sh
-> 10.244.1.16:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.0.13:53 Masq 1 0 0


NAME

我们找台机器加下静态路由 指向网络设备测试下 访问

1
2
3
4
5
6
7
8
# 获取 SVC 和pod ip
[root@node01 k8s]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d <none>
nginx01 ClusterIP 10.110.133.162 10.33.1.11 80/TCP 6d app=nginx01,group=default,role=master,tier=backend
[root@node01 k8s]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx01-d87b4fd74-7tw2x 1/1 Running 0 6d 10.244.1.16 node03

ClusterIP 路由

1
2
3
4
5
6
7
#ClusterIP 路由
route add -net 10.96.0.0 netmask 255.255.0.0 gw 10.129.6.8

ping 10.96.0.1
64 bytes from 10.96.0.1: icmp_seq=2 ttl=64 time=38.2 ms
64 bytes from 10.96.0.1: icmp_seq=3 ttl=64 time=0.258 ms
64 bytes from 10.96.0.1: icmp_seq=4 ttl=64 time=0.374 ms

external-ip 路由

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
#external-ip 路由
route add -net 10.33.1.11 netmask 255.255.255.255 gw 10.129.6.8

[root@haproxy02 zeming]# ping 10.33.1.11
PING 10.33.1.11 (10.33.1.11) 56(84) bytes of data.
From 10.129.6.8: icmp_seq=1 Redirect Host(New nexthop: 10.129.6.211)
64 bytes from 10.33.1.11: icmp_seq=1 ttl=64 time=41.4 ms
^C
--- 10.33.1.11 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 755ms
rtt min/avg/max/mdev = 41.459/41.459/41.459/0.000


# 获取页面
[root@haproxy02 zeming]# curl 10.33.1.11
< !DOCTYPE html>
< html>
< head>
< meta charset="UTF-8">
< title> 主标题 | 副标题< /title>
< /head>
< body>
<p>Hello, world! 我是版本V1
My V2 e WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>
<p>Hello, world! ^ ^ ^ ^ ^ ^ V1
My V2 Bye WorldZ</p>ZX
< /body>
< /html>

pod 路由

1
2
3
4
5
6
7
#pod 路由
route add -net 10.244.0.0 netmask 255.255.0.0 gw 10.129.6.8

[root@haproxy02 xuzeming]# ping 10.244.0.13
PING 10.244.0.13 (10.244.0.13) 56(84) bytes of data.
64 bytes from 10.244.0.13: icmp_seq=1 ttl=63 time=41.8 ms
64 bytes from 10.244.0.13: icmp_seq=2 ttl=63 time=1.15 ms

主备网络设备BGP 切换测试

  • 测试方式 关闭R1主节点网络设备 互联网口 制造 R1离线
  • 观察 Vrrp 状态转移到备机
  • 观察 BGP 备机 建立所有mesh 邻居状态
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

R2#
*Mar 1 02:58:09.451: %VRRP-6-STATECHANGE: Fa0/0 Grp 1 state Backup -> Master
R10#
*Mar 1 02:58:46.415: %BGP-5-ADJCHANGE: neighbor 10.129.6.211 Up
R10#
*Mar 1 02:58:53.007: %BGP-5-ADJCHANGE: neighbor 10.129.6.213 Up

#
R10#sh ip rou
Codes: C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route

Gateway of last resort is 10.129.6.1 to network 0.0.0.0

10.0.0.0/8 is variably subnetted, 7 subnets, 2 masks
B 10.33.1.11/32 [20/0] via 10.129.6.213, 00:00:19
[20/0] via 10.129.6.211, 00:00:25
B 10.110.133.162/32 [20/0] via 10.129.6.213, 00:00:19
[20/0] via 10.129.6.211, 00:00:25
B 10.96.0.10/32 [20/0] via 10.129.6.213, 00:00:19
[20/0] via 10.129.6.211, 00:00:25
B 10.96.0.1/32 [20/0] via 10.129.6.213, 00:00:19
[20/0] via 10.129.6.211, 00:00:27
C 10.129.6.0/24 is directly connected, FastEthernet0/0
B 10.244.0.0/24 [20/0] via 10.129.6.211, 00:00:27
B 10.244.1.0/24 [20/0] via 10.129.6.213, 00:00:21
S* 0.0.0.0/0 [1/0] via 10.129.6.1

R10#sh bgp sum
BGP router identifier 10.129.6.92, local AS number 64513
BGP table version is 69, main routing table version 69
6 network entries using 702 bytes of memory
10 path entries using 520 bytes of memory
4 multipath network entries and 8 multipath paths
3/1 BGP path/bestpath attribute entries using 372 bytes of memory
1 BGP AS-PATH entries using 24 bytes of memory
0 BGP route-map cache entries using 0 bytes of memory
0 BGP filter-list cache entries using 0 bytes of memory
BGP using 1618 total bytes of memory
BGP activity 6/0 prefixes, 64/54 paths, scan interval 60 secs

Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
10.129.6.91 4 64513 187 194 0 0 0 00:00:33 Active
10.129.6.211 4 64512 683 364 69 0 0 00:02:38 5
10.129.6.213 4 64512 688 362 69 0 0 00:02:32 5

补充拓展

kube-router 参数一栏

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
Usage of ./kube-router:
--advertise-cluster-ip 将该服务的集群IP添加到RIB,以便通告给BGP peers.
--advertise-external-ip 将服务的外部IP添加到RIB,以便将其通告给BGP peers.
--cleanup-config 清理iptables规则,ipvs,ipset配置并退出.
--cluster-asn uint 集群节点运行iBGP的ASN编号.
--cluster-cidr string 群集中的CIDR范围。它被用来识别pods的范围.
--config-sync-period duration apiserver配置同步之间的延迟(例如“5s”,“1m”)。必须大于0.(默认1m0s)
--enable-overlay 当enable-overlay设置为true时,IP-in-IP隧道将用于跨不同子网中节点的pod-pod联网。如果设置为false,则不使用隧道,并且路由基础架构预计为不同子网中的节点之间的pod-pod联网路由流量(默认值为true)
--enable-pod-egress 从Pod到群集外的SNAT流量。 (默认为true)
--hairpin-mode 为每个服务端点添加iptable规则以支持流量管控.
-h, --help 打印使用信息.
--hostname-override string 覆盖节点的NodeName。如果kube-router无法自动确定您的NodeName,请设置此项.
--iptables-sync-period duration iptables规则同步之间的延迟(例如'5s','1m')。必须大于0.(默认1m0s)
--ipvs-sync-period duration ipvs config同步之间的延迟(例如'5s','1m','2h22m')。必须大于0.(默认1m0s)
--kubeconfig string 具有授权信息的kubeconfig文件的路径(主位置由主标志设置)。
--masquerade-all SNAT所有流量到群集IP /节点端口。
--master string Kubernetes API服务器的地址(覆盖kubeconfig中的任何值)。
--nodeport-bindon-all-ip 对于NodePort类型的服务,创建监听节点的所有IP的IPVS服务.
--nodes-full-mesh 集群中的每个节点都将建立与其他节点的BGP对等关系。 (默认为true)
--peer-router-asns uintSlice 集群节点将向其通告集群ip和节点的pid cidr的BGP peers的ASN编号。 (默认[])
--peer-router-ips ipSlice 所有节点将对等的外部路由器的IP地址,并通告集群ip和pod cidr。 (默认[])
--peer-router-passwords stringSlice 用“--peer-router-ips”定义的BGP peers进行认证的密码。
--routes-sync-period duration 路线更新与广播之间的延迟(例如“5s”,“1m”,“2h22m”)。必须大于0.(默认1m0s)
--run-firewall 启用网络策略 - 设置iptables为pod提供入口防火墙。 (默认为true)
--run-router 启用Pod网络 - 通过iBGP发布并学习到Pod的路由。 (默认为true)
--run-service-proxy 启用服务代理 - 为Kubernetes服务设置IPVS。 (默认为true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

### DSR模式


请阅读以下博客,了解如何结合使用DSR和“–advertise-external-ip”构建高度可扩展和可用的入口。 https://cloudnativelabs.github.io/post/2017-11-01-kube-high-available-ingress/
您可以为每个服务启用DSR(直接服务器返回)功能。当启用的服务端点将直接响应客户端通过签署服务代理。启用DSR时,Kube-router将使用LVS的隧道模式来实现此功能。
要启用DSR,您需要使用kube-router.io/service.dsr = tunnel注释来注释服务。例如,

kubectl annotate service my-service "kube-router.io/service.dsr=tunnel"
在当前的实现中,当在服务上应用注释时,DSR将仅适用于外部IP。
此外,当使用DSR时,当前的实现不支持端口重新映射。所以你需要使用相同的端口和目标端口的服务
你需要在kube-router守护进程清单中启用hostIPC:true和hostPID:true。并且必须将主路径/var/run/docker.sock设置为kube-router的一个volumemount。
上述更改需要kube-router输入pod namespace,并在pod中创建ipip隧道,并将外部IP分配给VIP。
对于示例清单,请查看启用DSR要求的[manifest](../ daemonset / kubeadm-kuberouter-all-features-dsr.yaml).


### 负载均衡调度算法
kube-router使用LVS作为服务代理。 LVS支持丰富的调度算法。您可以为该服务添加注释以选择一个调度算法。当一个服务没有注释时,默认情况下选择“轮询”调度策略

For least connection scheduling use:
kubectl annotate service my-service “kube-router.io/service.scheduler=lc”
For round-robin scheduling use:
kubectl annotate service my-service “kube-router.io/service.scheduler=rr”
For source hashing scheduling use:
kubectl annotate service my-service “kube-router.io/service.scheduler=sh”
For destination hashing scheduling use:
kubectl annotate service my-service “kube-router.io/service.scheduler=dh”
```
参考文献

Today

2017-12-21

Today 今天是美好一天,愿我们好好的

预见你 遇见爱

Nice To Meet U

2017-12-21

How are you

Hello World!

2015-01-22

发送失败测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

优美胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于难懂。
扁平胜于嵌套。
稀疏胜于紧密。
可读性应当被重视。
尽管实用性会打败纯粹性,特例也不能凌驾于规则之上。
不要忽略任何错误,除非你确认要这么做
面对不明确的定义,拒绝猜测的诱惑。
找到一种最好唯一的一种方法去解决问题。
虽然一开始这种方法并不是显而易见,因为你不是 Python 之父。
做好过不做,但没有思考的做还不如不做
如果实现很难说明,那它是个坏想法。
如果实现容易解释,那它有可能是个好想法。
命名空间是个绝妙的想法,请多加利用

  • « 上一页
  • 1
  • 下一页 »