404频道

学习笔记

概要

netfilter与iptables的关系

linux在内核中对数据包过滤和修改的模块为netfilter,netfilter模块本身并不对数据包进行过滤,只是允许将过滤数据包或修改数据包的函数hook到内核网络协议栈的适当位置。

iptables是用户态的工具,用于向netfilter中添加规则从而实现报文的过滤和修改等功能,工作在ip层。ebtables工作在数据链路层,用于处理以太网帧。

图中绿色代表iptables的表,可以看到有部分位于了数据链路层,之所以产生这种奇怪的架构,原因是bridge_nf模块,因为bridge工作在数据链路层,不一定会经过网络层,但仍然需要iptables的功能。详细信息可以在ebtables/iptables interaction on a Linux-based bridge中了解。

概念:tables -> chains -> rules

iptabels介绍

chain

每个表都由一组内置的链,还可以添加用户自定义链,只是用户自定义链没有钩子可以触发,需要从其他链通过-j即JUMP进行触发。

  • INPUT 链:发往本机的报文
  • OUTPUT 链:由本机发出的报文
  • FORWARD 链:经由本机转发的报文
  • PREROUTING 链:报文到达本机,进行路由决策之前
  • POSTROUTING 链:报文由本机发出,进行路由决策之后

image

从chain的角度考虑数据包的流向:

  • 到本机某进程的报文:PREROUTING -> INPUT
  • 由本机转发的报文:PREROUTING -> FORWARD -> POSTROUTING
  • 由本机某进程发出的报文:OUTPUT -> POSTROUTING

当一个网络包进入一台机器的时候,首先拿下 MAC 头看看,是不是我的。如果是,则拿下 IP 头来。得到目标 IP 之后呢,就开始进行路由判断。在路由判断之前,这个节点我们称为 PREROUTING。如果发现 IP 是我的,包就应该是我的,就发给上面的传输层,这个节点叫作 INPUT。如果发现 IP 不是我的,就需要转发出去,这个节点称为 FORWARD。如果是我的,上层处理完毕完毕后,一般会返回一个处理结果,这个处理结果会发出去,这个节点称为 OUTPUT,无论是 FORWARD 还是 OUTPUT,都是路由判断之后发生的,最后一个节点是 POSTROUTING。

table

有了chain的概念后,为了便于chain中rule的管理,又引入了table的概念,用于存放相同功能的rule,不同功能的rule放到不同的table中。

包括:filter nat mangle raw

filter

默认表,管理本机数据包的进出,用于实现包的过滤,对应内核模块iptables_filter

input:想要进入linux主机的包
output:linux主机要发送的包
forward:传递包到后端计算机,与nat table关联较多

nat

管理后端主机进出,与linux主机没有关系,与linux后的主机有关

prerouting:进行路由判断之前的规则(dnat/redirect)
postrouting:路由判断之后执行的规则(snat/masquerade)
output:与发出去的包有关

mangle

较少使用,用于拆解报文,修改数据包,并重新封装。

raw

raw表的主要作用是允许我们给某些特定的数据包打上标记。

rule

包含了匹配条件和处理动作。

匹配条件包括:source ip、destination ip、source port、destination port

处理动作包括:

  • accept: 将包交给协议栈
  • drop:直接丢弃数据包,不给任何回应
  • reject:拒绝数据包通过,并给一个响应信息,客户端会收到拒绝消息
  • queue: 交个某个用户态进程处理
  • dnat:目的地址转换
  • snat:源地址转换,必须要指定SNAT地址,即–to-source参数,可以是单个ip,也可以是网段。用在POSTROUTING链上。
  • masquerade: 源地址伪装,跟snat类似,不需要指定SNAT地址,会自动从服务器上获取SNAT的ip地址。如果有多个网卡的情况下,会使用路由选择算法。
  • mark: 对数据包进行打标签操作

table filter rule的关系

这三者之间的关系还是相当的绕。

链中的规则存在的表

chain中存放了rule,某些chain中注定不包含某些rule。例如prerouting链中的rule仅存在于nat raw mangle三张表中。

prerouting链中的规则存在的表:raw mangle nat
input链中的规则存在的表:mangle filter nat
forward链中的规则存在的表:mangle filter
output链中的规则存在的表:raw mangle filter nat
postrouting链中的规则存在的表:mangle nat

表中的规则可以被哪些链使用

raw表中的规则可以被链使用:prerouting output

image

表的名字为小写,链的名字为大写

常用操作

查询

iptables 查询默认的表为filter,默认会列出表中所有链的规则

  • -t 用于指定要操作的表,支持raw mangle filter nat,省略-t选项,默认使用filter表
  • -L 列出rule
  • -v 可查看更详细的信息
  • -n 规则中以ip地址的形式进行显示
  • –line-number 显示规则的编号
  • -x 包的计数以精确数字显示

iptables -t filter -L:从表的角度查询规则,用于查看filter表中的所有规则

iptables -L INPUT: 从链的角度查询规则,用于查看INPUT链中的所有规则

iptables -vL INPUT: 从链的角度查询规则,用于查看INPUT链中的所有规则,可查看更详细信息,包含了规则的匹配信息

iptables -nvL:以精确数字显示

修改

  • -F: 清空规则
  • -I: 表示插入规则
  • -A: 表示以追加的方式插入规则
  • --dport: 目的端口
  • --sport: 源端口
  • -s: 源ip
  • -d: 目的ip
  • --match-set:匹配ipset

iptables -F INPUT:清空filter表中的INPUT链中的所有规则。

删除

  • -D: 删除规则,iptables -D 链名 规则编号,其中规则编号可以通过--line-number查看到。
  • -F: 清空规则,iptables -F 链名 -t 表名
  • -X: 删除链 iptables -X 链名 -t 表名

trace

开启trace功能

1
2
3
4
5
# centos7 系统下有效,centos6下内核模块为ipt_LOG
$ modprobe nf_log_ipv4

# 用来验证module是否加载成功
$ sysctl net.netfilter.nf_log.2

要开启icmp协议的追踪,执行如下的命令

1
2
iptables -t raw -A OUTPUT -p icmp -m comment --comment "TRACE" -j TRACE
iptables -t raw -A PREROUTING -p icmp -m comment --comment "TRACE" -j TRACE

可以通过如下的命令看到插入的iptabels规则:

1
iptables -t raw -nvL --line-number

追踪日志最终会在/var/log/message或者/var/log/kern下看到:

1
Feb  6 11:22:04 c43k09006.cloud.k09.am17 kernel: TRACE: raw:PREROUTING:policy:3 IN=docker0 OUT= PHYSIN=bond0.9 MAC=02:42:30:fb:43:94:5c:c9:99:de:c4:8b:08:00 SRC=10.45.8.10 DST=10.45.4.99 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=25550 DF PROTO=ICMP TYPE=0 CODE=0 ID=24191 SEQ=2

格式这块的含义如下:

“TRACE: tablename:chainname:type:rulenum “ where type can be “rule” for plain rule, “return” for implicit rule at the end of a user defined chain and “policy” for the policy of the built in chains.

环境清理,删除刚刚创建的规则即可,其中1为规则的编号:

1
2
3
4
5
# 可以通过此来查询之前创建的规则编号
iptables -t raw --line-number -nvL
# 删除规则
iptables -t raw -D PREROUTING 1
iptables -t raw -D OUTPUT 1

实战

试验1 基本规则管理

插入规则

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
# 清空filter表中的input链规则
[vagrant@localhost ~]$ sudo iptables -F INPUT

# 查看filter表中的详细规则,此时从其他机器上ping该ip是通的
[vagrant@localhost ~]$ sudo iptables -nvL INPUT
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
pkts bytes target prot opt in out source destination

# 增加规则,拒绝192.168.33.1上的请求
# -I:表示插入
# INPUT为要插入的链
# -s:表示源ip地址
# -j:表示要执行的动作
[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT -s 192.168.33.1 -j DROP

# 再次查询filter表中的规则,此时192.168.33.1上的报文已经不通
[vagrant@localhost ~]$ sudo iptables -t filter -nvL
Chain INPUT (policy ACCEPT 107 packets, 6170 bytes)
pkts bytes target prot opt in out source destination
0 0 DROP all -- * * 192.168.33.1 0.0.0.0/0

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 56 packets, 4355 bytes)
pkts bytes target prot opt in out source destination

# appent一条接收192.168.33.1的请求规则
[vagrant@localhost ~]$ sudo iptables -t filter -A INPUT -s 192.168.33.1 -j ACCEPT
# 新增加的序号为2,192.168.33.1的包匹配到1后就停止往下走,因此192.168.33.1还是ping不通当前主机
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line-number
Chain INPUT (policy ACCEPT 65 packets, 3572 bytes)
num pkts bytes target prot opt in out source destination
1 9 756 DROP all -- * * 192.168.33.1 0.0.0.0/0
2 0 0 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0

# 插入一条ACCEPT rule,此时192.168.33.1可以ping通当前主机,新插入的规则优先
[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT -s 192.168.33.1 -j ACCEPT
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line-number
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0
2 10 840 DROP all -- * * 192.168.33.1 0.0.0.0/0
3 0 0 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0

# 新插入一条accept 192.168.33.2的规则,插入位置为2,可以看到插入到2的位置了
[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT 2 -s 192.168.33.2 -j ACCEPT
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line-number
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
num pkts bytes target prot opt in out source destination
1 1 84 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0
2 0 0 ACCEPT all -- * * 192.168.33.2 0.0.0.0/0
3 10 840 DROP all -- * * 192.168.33.1 0.0.0.0/0
4 0 0 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0

删除规则

接下在上面实验的基础上测试删除规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 删除刚刚创建的规则2
[vagrant@localhost ~]$ sudo iptables -t filter -D INPUT 2
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line-number
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
num pkts bytes target prot opt in out source destination
1 1 84 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0
2 10 840 DROP all -- * * 192.168.33.1 0.0.0.0/0
3 0 0 ACCEPT all -- * * 192.168.33.1 0.0.0.0/0

# 删除source为192.168.33.1,动作为ACCEPT的规则,实际此时执行一次命令仅能删除一条
[vagrant@localhost ~]$ sudo iptables -t filter -D INPUT -s 192.168.33.1 -j ACCEPT
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line
Chain INPUT (policy ACCEPT 13 packets, 736 bytes)
num pkts bytes target prot opt in out source destination
1 11 936 DROP all -- * * 192.168.33.1 0.0.0.0/0

修改规则

在上面实验的基础上修改规则

1
2
3
4
5
6
7
8
9
10
# 将规则动作从REJECT更改为REJECT
[vagrant@localhost ~]$ sudo iptables -t filter -R INPUT 1 -s 192.168.33.1 -j REJECT
[vagrant@localhost ~]$ sudo iptables -nvL INPUT --line
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT all -- * * 192.168.33.1 0.0.0.0/0 reject-with icmp-port-unreachable

# 每个链都有一个默认规则,当前INPUT链中的默认为ACCEPT
# 以下可以修改INPUT链的默认规则为DROP,远程连接慎用,不要问我为什么
[vagrant@localhost ~]$ sudo iptables -t filter -P INPUT DROP

保存规则

防火墙的所有修改都是临时的,重启系统后会失效。iptables会读取/etc/sysconfig/iptables中的规则。

1
2
3
4
5
# iptables-save命令仅会打印当前的规则,需要使用重定向当前规则到文件中
[root@localhost system]# iptables-save > /etc/sysconfig/iptables

# 可以从规则文件中载入规则
[root@localhost system]# iptables-restore < /etc/sysconfig/iptables

实验二 各类匹配条件的使用

匹配条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 可一次性插入两条规则
[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT -s 192.168.33.1,192.168.33.2 -j DROP
[vagrant@localhost ~]$ sudo iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 31 packets, 1744 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -- * * 192.168.33.2 0.0.0.0/0
2 0 0 DROP all -- * * 192.168.33.1 0.0.0.0/0

# 可指定ip网段
[vagrant@localhost ~]$ sudo iptables -t filter -F INPUT
[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT -s 192.168.33.0/24 -j DROP
[vagrant@localhost ~]$ sudo iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 7 packets, 388 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -- * * 192.168.33.0/24 0.0.0.0/0

[vagrant@localhost ~]$ sudo iptables -t filter -I INPUT ! -s 192.168.33.0/24 -j DROP
[vagrant@localhost ~]$ sudo iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 19 packets, 1048 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -- * * !10.0.2.0/24 0.0.0.0/0

协议类型

使用-p来指定协议类型,支持tcp udp icmp等,不指定时默认匹配所有协议

网卡接口

-i来指定从某个网卡进入的流量,仅使用于PREROUTING INPUT FORWARD三条链。

-o来指定从某个网络流出的流量,仅适用于FORWARD OUTPUT POSTROUTING三条链。

实验三 扩展模块

端口

使用了扩展模块tcp udp,默认可以省略

--dport来匹配报文的目的端口,使用时必须指定协议,即-p选项。
--sport来匹配报文的源端口,使用时必须指定协议,即-p选项。

端口可以指定范围,例如22:25表示22-25之间的所有端口,22,25表示22和25端口,还可以配合起来使用,比如22,80:88表示22和80-88之间的端口。

1
2
3
4
5
6
# 可以指定目的端口的范围
[root@localhost vagrant]# iptables -t filter -I INPUT -s 192.168.33.1 -p tcp --dport 22:25 -j REJECT
[root@localhost vagrant]# iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 70 packets, 4024 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT tcp -- * * 192.168.33.1 0.0.0.0/0 tcp dpts:22:25 reject-with icmp-port-unreachable

iprange扩展模块

iprange扩展模块可以指定一段连续的ip地址范围。

--src-range--dst-range用来指定源地址和目的范围。

1
2
3
4
5
[root@localhost vagrant]# iptables -t filter -I INPUT -m iprange --src-range 192.168.33.1-192.168.33.10 -j DROP
[root@localhost vagrant]# iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 17 packets, 968 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 source IP range 192.168.33.1-192.168.33.10

string扩展模块

匹配报文中包含的字符串

1
2
3
4
5
6
# 匹配报文中包含XXOO的报文
[root@localhost vagrant]# iptables -t filter -I INPUT -m string --algo bm --string "XXOO" -j REJECT
[root@localhost vagrant]# iptables -t filter -nvL INPUT --line
Chain INPUT (policy ACCEPT 15 packets, 852 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 STRING match "XXOO" ALGO name bm TO 65535 reject-with icmp-port-unreachable

其他扩展

time扩展用来根据时间段进行匹配

connlimit用来对ip的并发连接数进行限制

limit模块限制单位时间内进出包的数量

tcp扩展中可以使用--tcp-flags可根据tcp flag进行匹配

state扩展可根据tcp的连接状态进行匹配

实验四 自定义链

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
# 创建自定义链 IN_WEB
[root@localhost vagrant]# iptables -t filter -N IN_WEB
[root@localhost vagrant]# iptables -nvL
Chain INPUT (policy ACCEPT 31 packets, 1780 bytes)
pkts bytes target prot opt in out source destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 16 packets, 1216 bytes)
pkts bytes target prot opt in out source destination

Chain IN_WEB (0 references)
pkts bytes target prot opt in out source destination

[root@localhost vagrant]# iptables -t filter -I IN_WEB -s 192.168.33.1 -j REJECT
[root@localhost vagrant]# iptables -t filter -I IN_WEB -s 192.168.33.2 -j REJECT

[root@localhost vagrant]# iptables -t filter -nvL IN_WEB --line
Chain IN_WEB (0 references)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT all -- * * 192.168.33.2 0.0.0.0/0 reject-with icmp-port-unreachable
2 0 0 REJECT all -- * * 192.168.33.1 0.0.0.0/0 reject-with icmp-port-unreachable

# 将IN_WEB自定义链添加到INPUT链上
[root@localhost vagrant]# iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB
# 可以看到INPUT链中多出了IN_WEB链
[root@localhost vagrant]# iptables -nvL
Chain INPUT (policy ACCEPT 35 packets, 2012 bytes)
pkts bytes target prot opt in out source destination
0 0 IN_WEB tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 18 packets, 1408 bytes)
pkts bytes target prot opt in out source destination

Chain IN_WEB (1 references)
pkts bytes target prot opt in out source destination
0 0 REJECT all -- * * 192.168.33.2 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- * * 192.168.33.1 0.0.0.0/0 reject-with icmp-port-unreachable

# 重新定义自定链名字
[root@localhost vagrant]# iptables -E IN_WEB WEB
[root@localhost vagrant]# iptables -nvL
Chain INPUT (policy ACCEPT 39 packets, 2244 bytes)
pkts bytes target prot opt in out source destination
0 0 WEB tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 20 packets, 1520 bytes)
pkts bytes target prot opt in out source destination

Chain WEB (1 references)
pkts bytes target prot opt in out source destination
0 0 REJECT all -- * * 192.168.33.2 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- * * 192.168.33.1 0.0.0.0/0 reject-with icmp-port-unreachable

# 由于iptables有自定义链,不能删除
[root@localhost vagrant]# iptables -X WEB
iptables: Too many links.
# 将INPUT链引用的WEB链删除
[root@localhost vagrant]# iptables -D INPUT 1
# 此时仍不能删除自定义链,因为自定义链删除,需要上面没有任何规则
[root@localhost vagrant]# iptables -X WEB
iptables: Directory not empty.

# 先清空自定义链的规则后可以删除
[root@localhost vagrant]# iptables -F WEB
[root@localhost vagrant]# iptables -X WEB

ref

为了其他主机可访问docker registry,必须采用https协议。

1
2
3
4
mkdir -p ~/docker_registry/certs
signdomain=103-17-184-lg-201-k08
openssl req -nodes -subj "/C=CN/ST=BeiJing/L=BeiJing/CN=$signdomain" -newkey rsa:4096 -keyout ~/docker_registry/certs/$signdomain.key -out ~/docker_registry/certs/$signdomain.csr
openssl x509 -req -days 3650 -in ~/docker_registry/certs/$signdomain.csr -signkey ~/docker_registry/certs/$signdomain.key -out ~/docker_registry/certs/$signdomain.crt

从docker hub拉取registry镜像,并启动镜像

1
2
3
4
5
6
docker run -d -p 5000:5000 --restart=always --name registry \
-v /data/docker_registry:/var/lib/registry \
-v /home/worker/docker_registry/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/103-17-184-lg-201-k08.yidian.com.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/103-17-184-lg-201-k08.yidian.com.key \
registry:2

停止registry镜像并删除的命令为

1
docker stop registry && docker rm -v registry

下载最新的centos7镜像

1
docker pull centos:7.3.1611

将centos7镜像增加tag

1
2
3
4
5
6
7
8
docker tag centos:7.3.1611 103-17-184-lg-201-k08.yidian.com:5000/centos:7.3

# 可以看到列表中会多出一个镜像
[root@103-17-184-lg-201-k08 data]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/registry 2 047218491f8c 4 weeks ago 33.17 MB
103-17-184-lg-201-k08.yidian.com:5000/centos 7.3 67591570dd29 3 months ago 191.8 MB
docker.io/centos 7.3.1611 67591570dd29 3 months ago 191.8 MB

docker push命令仅支持https协议,签名已经启动了自签名的https协议的registry,为了能够让docker能够信任registry,需要在/etc/docker/certs.d/目录下增加相应的crt文件,增加后的目录结构为/etc/docker/certs.d/103-17-184-lg-201-k08.yidian.com:5000/103-17-184-lg-201-k08.yidian.com.crt,添加完成后需要重启docker服务。

将image push到registry

1
docker push 103-17-184-lg-201-k08.yidian.com:5000/centos:7.3 

api

可以直接通过curl命令来访问api:curl --cacert 103-17-184-lg-201-k08.yidian.com.crt -v https://103-17-184-lg-201-k08.yidian.com:5000/v2

ref

某些情况下需要搭建自己的yum源,比如维持特定的软件包版本等,只需要从网上下载合适的rpm包,即可构建yum源。

repodata数据

创建/data/yum.repo目录用来存放rpm包。

可以使用yumdownloader命令来下载rpm包到本地,并且不安装。这里以安装mesos为例,在/data/yum.repo目录下执行yumdownloader mesos即可下载mesos的rpm包到本地。

安装createrepo:yum install createrepo,用来根据rpm包产生对应的包信息。

每加入一个rpm包需要更新下repo的信息,执行createrepo --update /data/yum.repo。会自动产生repodata目录。

搭建web服务

需要对外提供web服务,通常会使用nginx或者apache来对外提供服务,这里使用python SimpleHTTPServer来对外提供服务,执行cd /data/yum.repo && python -m SimpleHTTPServer 1080

客户端的repo文件设置

安装yum优先级插件,用来设置yum源的优先级: yum install -y yum-plugin-priorities

每个需要使用该yum源的客户端需要在/etc/yum.repo.d/目录下增加devops.repo文件。

1
2
3
4
5
6
[devops]
name=dev-ops
baseurl=http://10.103.17.184:1080/
enabled=1
gpgcheck=0
priority=1

本次grafana的升级从版本3.1.1,变更为4.4.3,涉及到一个大的版本跨度。同时之前在使用的存储为sqlite,趁着这次升级更改为mysql。

grafana升级

直接从官网下载对应的4.4.3版本的二进制包,修改部分配置即可,该部分没任何难度。

sqlite to mysql

由于grafana使用的表结构在3.1.1到4.4.3之间有变更,不能直接将3.1.1版本的sqlite中的数据导入到4.4.3的mysql中。我的方法为先使用3.1.1版grafana将数据从sqlite导入到mysql中,然后再升级grafana的版本,grafana可以自动修改表结构。

在的mysql中创建grafana的数据库,并修改数据库的编码为utf-8.

修改grafana 3.1.1配置文件conf/defaults.ini如下:

1
2
3
4
5
6
7
8
9
10
11
[database]
# You can configure the database connection by specifying type, host, name, user and password
# as separate properties or as on string using the url property.

# Either "mysql", "postgres" or "sqlite3", it's your choice
type = mysql
host = xx.xx.xx.xx:3306
name = grafana
user = dev
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
password = dev

启动grafana后会自动在grafana数据库中创建相应的表结构,接下来就是将sqlite中的数据导入到mysql中。

在data目录下增加如下脚本sqlitedump.sh,并执行sh sqlitedump.sh grafana.db > grafana.sql

1
2
3
4
5
6
7
8
9
#!/bin/sh
DB=$1
TABLES=$(sqlite3 $DB .tables | grep -v migration_log)
for t in $TABLES; do
echo "TRUNCATE TABLE $t;"
done
for t in $TABLES; do
echo -e ".mode insert $t\nselect * from $t;"
done | sqlite3 $DB

然后将grafana.sql导入到新创建的mysql。

将grafana 3.1.1版本停掉,将grafana 4.4.3版本的配置指向到mysql数据库,启动grafana 4.4.3后,mysql中的表结构会自动变更。

至此,grafana的升级完成。

由于不允许通过ssh直接连接服务器,即服务器的22端口是不开放的,但是其他端口号可以访问。这就造成了往服务器上传输文件会特别麻烦,需要通过relay中转一下。

rsync命令有shell模式和daemon模式,为了解决该问题,可以通过rsync的daemon模式,rysnc的daemon模式会默认使用873端口,不使用ssh协议,以此来绕过ssh的22端口限制。

最终可以实现在本地通过rsync一条命令直接同步文件或文件夹到服务器的指定目录下。

首先在服务器上搭建rsync的服务端,rsync的安装不再介绍。

修改服务器的rsync配置文件/etc/rsyncd.conf如下:

1
2
3
4
5
6
[worker]
path = /home/worker
list = true
uid = worker
gid = worker
read only = false

这里为了简便,并未设置rsync的用户名和密码。

客户端同步文件的命令如下:

1
rsync -avz $SRC worker@$HOST::worker --exclude=target --exclude=.git --exclude=.idea --delete

命令中的第一个worker为HOST的登录用户名,第二个worker为rysncd配置文件中配置的组名。–exclude选项可以用来屏蔽需要同步的文件夹。–delete选项用来同步删除的文件或文件夹。

daemon模式跟ssh模式相比,无法指定服务器的具体某一个路径,使用不够灵活,但也基本可以满足需求。只能通过daemon配置文件中配置的组中的path参数,同步时仅能通过::组名的形式来指定。

最近公司需要首先登录跳板机relay,然后通过跳板机才能登录服务器,操作上略显麻烦。为了节省登录服务器的时间,我编写了一个简单的脚本来简化登录操作。

实现效果为在本地terminal下,执行wrelay $host,即可自动登录到相应的主机。

在relay服务器上增加对其他服务器的免登录命令

在relay服务器上ssh到其他主机时需要输入密码,使用expect命令来登录到其他主机时通过expect脚本来实现自动输入密码并登录的功能。

在/home/$user目录下新建mybin文件夹,并将mybin文件夹添加到$PATH环境变量中,具体修改方法不展开。

在mybin目录下增加gw脚本,内容如下:

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
#!/usr/bin/expect

if {$argc < 1} {
puts "Usage:cmd <host>"
exit 1
}

set host [lindex $argv 0]
# 在这里填写要登录的用户
set username "worker"
# 在这里填写要登录的密码
set password "worker"

spawn ssh $username@$host
set timeout 2
expect {
"*password:" {
send "$password\n"
}
"Are you sure you want to continue connecting (yes/no)?" {
send "yes\r"
exp_continue
}
}
expect "*#"
interact

执行gw 10.1.1.8,即可登录到对应的主机上。

本地主机免登录relay服务器,并自动登录到对应的服务器

在本地自动登录relay主机同样使用expect的方式,脚本名称为wrelay,内容如下:

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
#!/usr/bin/expect

if {$argc < 1} {
puts "Usage:cmd <remote_host>"
exit 1
}

# 下面指定relay主机
set host "relay.name"
# 这里输入relay的用户名
set username ""
# 这里输入relay的密码
set password ""
set remote_host [lindex $argv 0]

spawn ssh $username@$host
set timeout 2
expect {
"*password:" {
send "$password\n"
}
"Are you sure you want to continue connecting (yes/no)?" {
send "yes\r"
exp_continue
}
}
expect "*#"

sleep 0.1
# 在relay上自动登录到其他服务器主机
send "gw $remote_host\n"
interact

清明节假期突然想起了我好久不更的blog,看到Farbox官网上的《2016,终结了几个产品》,说明Farbox已经停止更新了。虽然我挺喜欢Farbox这个项目,也见证了Farbox的成长及作者做产品的思考,在这里也向作者致敬。

我当时开始准备启用Farbox之前试用过jekyll,翻遍了整个github,也没找到个合我心意的theme。幸好是Farbox的出现,让我眼前一亮,这就是我想好的blog系统了。Farbox的停更使我不得不考虑重新换个blog,虽然2016的文章数量仅为罕见的个位数,但有可能今年有时间会多写一些。

近几年hexo特别的火,在试看了官方文档了解功能及考虑了blog的迁移成本后,心想,这就是我想要的blog系统了。hexo该有的功能全都有,甚至比Farbox要强大很多。Farbox的很多设计思想跟hexo相仿,但hexo显的更加自由,blog需要自己一手搭建完成。

当然hexo要想使用的好,做一些全面的了解及折腾是必不可少的,毕竟最终利用的Github pages是个静态的系统。早已没有了想当年翻遍整个github上jekyll theme的精力了,我这次的基调是能少折腾就少折腾,毕竟blog我也不是经常写,访问量也更是少的可怜,就当全面了解下当前最火的hexo就好了。

theme

本着不折腾原则,直接启用了很火的hexo-theme-next,文档比较全,维护比较及时。基本上按照文档走一遍,该配置的就都可以配置上了。

代码同步

代码通过git同步是必备技能。

hexo项目代码同步

hexo采用的是node.js环境,而Github pages是静态的,因此Github pages上仅能存储的是hexo编译后的静态文件,这些静态文件直接通过hexo d部署到kuring.github.com仓库中就可以了。

而对于项目中的_config.yml、md文件我直接用git同步到Github上另外一个项目hexo_bak中了。网上还有思路是同步到kuring.github.com上的另外一个分支,我感觉太啰嗦,容易出错,还不如直接分开来的简便。

theme项目的代码同步

theme项目中也包含了部分自己的配置及修改,我这里选择的同步策略为从github上fork对应的theme项目,然后clone fork下来的项目到本地,然后直接在theme的项目中通过git命令同步到github fork的项目中。

网上也有思路是通过git subtree的方式来解决,我仍然感觉太啰嗦,不采用。

但这样一个blog项目需要多个git仓库,git push起来会比较麻烦,好在theme一般不怎么修改。

评论系统

之前用多说的时候也没几个评论的,用起来还不错,至少比被墙了的disqus要好很多,可是多说这么好的项目要关闭了。我直接使用了国内的网易云跟帖来满足评论的需求。

站内搜索

站内搜索是必不可少的功能,next主题提供了多种选择,我直接使用了hexo-generator-searchdb通过本地搜索来完成,生成的xml文件目前还比较小,效果还可以。

常用命令

  • 启用本地server端:hexo clean && hexo g && hexo s
  • 部署到github:hexo d
  • 发布文章:hexo new 文章url

使用hexo new draft test会在source/_drafts目录下创建对应文件,此时文件不会生成页面,用于存放未写完的文章。hexo publish draft test命令可将_drafts下的文章移动到_posts目录下,并添加创建时间等信息。

收个尾

blog总算迁移完成了,期望今年能多写上几篇。

目前php-fpm的服务部署在了docker中,对php-fpm的log和php error log可以通过syslog协议的形式发送出去,而php-fpm的slow log却不能配置为syslog协议,只能输出到文件中,因为一条slow log的是有多行组成的。

在docker中使用时发现fpm-slowlog不能正常输出,后经发现是docker默认没有ptrace系统调用的权限,而slow log的产生需要该系统调用。通过在docker启动的时候增加”–cap-add SYS_PTRACE”启动项可修正该问题。

为了收集slow log,可以通过logstash、flume等工具进行收集,本文采用logstash对slow log进行收集,并将收集的log写入到kafka中,便于后续的处理。logstash的input采用读取文件的方式,即跟tail -f的原理类似。为了能够将多行日志作为一行,采用了filter中的multiline来对多行日志进行合并操作。logstash的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
input {
file {
path => [“/var/log/php-fpm/fpm-slow.log"]
}
}

filter {
multiline {
pattern => "^$"
negate => true
what => "previous"
}
}

output {
stdout{codec => rubydebug}
kafka {
codec => plain {
format => “tag|%{host}%{message}"
}
topic_id => "fpm-slowlog"
bootstrap_servers => “kafka1.hostname:8082,kafka2.hostname:8082"
}
}

ELK解析nginx日志

最近使用ELK搭建了一个nginx的日志解析环境,中间遇到一些挫折,好不容易搭建完毕,有必要记录一下。

nginx

nginx配置文件中的日志配置如下:

1
2
3
4
5
6
error_log /var/log/nginx/error.log;
log_format main '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

logstash

由于是测试环境,我这里使用logstash读取nginx日志文件的方式来获取nginx的日志,并且仅读取了nginx的access log,对于error log没有关心。

使用的logstash版本为2.2.0,在log stash程序目录下创建conf文件夹,用于存放解析日志的配置文件,并在其中创建文件test.conf,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
input {
file {
path => ["/var/log/nginx/access.log"]
}
}
filter {
grok {
match => {
"message" => "%{IPORHOST:clientip} \[%{HTTPDATE:time}\] \"%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:httpversion}\" %{NUMBER:http_status_code} %{NUMBER:bytes} \"(?<http_referer>\S+)\" \"(?<http_user_agent>\S+)\" \"(?<http_x_forwarded_for>\S+)\""
}
}
}
output {
elasticsearch {
hosts => ["10.103.17.4:9200"]
index => "logstash-nginx-test-%{+YYYY.MM.dd}"
workers => 1
flush_size => 1
idle_flush_time => 1
template_overwrite => true
}
stdout{codec => rubydebug}
}

需要说明的是,filter字段中的grok部分,由于nginx的日志是格式化的,logstash解析日志的思路为通过正则表达式来匹配日志,并将字段保存到相应的变量中。logstash中使用grok插件来解析日志,grok中message部分为对应的grok语法,并不完全等价于正则表达式的语法,在其中增加了变量信息。

具体grok语法不作过多介绍,可以通过logstash的官方文档中来了解。但grok语法中的变量类型如IPORHOST并未找到具体的文档,只能通过在logstash的安装目录下通过grep -nr "IPORHOST" .来搜索具体的含义。

配置文件中的stdout部分用于打印grok解析结果的信息,在调试阶段一定要打开。

可以通过这里来验证grok表达式的语法是否正确,编写grok表达式的时候可以在这里编写和测试。

对于elasticsearch部分不做过多介绍,网上容易找到资料。

kibana

kibana不做过多介绍,使用可以查看官方文档和自己摸索。

reference

logstash中的grok插件介绍

曾经使用过多种科学上网方式,​最近尝试了使用aws的免费试用一年的功能搭建shadowsocks,访问google的速度非常不错,比很多收费的服务要好用,amazon真是良心企业!

本文用于记录在aws上搭建服务的步骤及其中的一些注意事项,步骤不会太详细,aws上关于主机的功能需要读者自己在试验的过程中去自己探索。

注册aws账号

为了能够搭建搭建aws服务,拥有一个amazon账号是必须的,在aws免费套餐的页面点击『创建免费账号』按钮即可按照步骤创建aws账号。

值得一提的是,注册aws的账号需要一张信用卡。

开启EC2主机实例

该步骤的目的是开启aws上的主机实例。​

进入aws的控制面板,在左上角的服务中选择EC2,aws提供了多种类型的主机,这里选择EC2即可。

在EC2控制面板界面中需要选择右上角的区域,这个用于选择EC2主机所在的机房,不同机房之间主机是不可以共享的。我这里选择了『美国西部(俄勒冈)』,感觉速度还不错,没有试验过亚洲地区的,新加坡的速度是不是会更好些。后续经过验证,首尔的服务器确实速度更快一些。

下面即可创建EC2的实例了,点击界面上的『启动实例』按钮即可按照步骤创建EC2实例了,创建实例的时候一定要选择免费的EC2主机,否则就会悲剧了。我选择了ubuntu14.04的主机,redhat7.2的主机yum源不太全,没有选择使用。

最终会得到ssh登录用的pem文件,用于ssh远程登录主机。并在界面上启动刚刚创建的实例。

按照shadowsocks

接下来就是在EC2实例上安装sock5代理工具了。

登录刚刚启动的EC2,需要pem文件。可以通过ssh -i "key.pem" ubuntu@ec2-52-26-2-14.us-west-2.compute.amazonaws.com命令来登录到远程主机,其他工具请自行google。

使用命令pip install shadowsocks来安装shadowssocks,pip命令的安装自行解决。

在ubuntu的home目录下执行mkdir shadowsocks创建保存配置文件的文件夹,并创建配置文件config.json,内容如下:

1
2
3
4
5
6
7
8
{
"server":"0.0.0.0",
"server_port":10001,
"local_port":1080,
"password":"xxx",
"timeout":600,
"method":"bf-cfb"
}

需要说明的是最好配置一下server_port选项,更改shadowsocks的默认端口号。method选项用于控制加密方式,我这里更改为了bf-cfb。

执行nohup ssserver -c config.json &命令即可启动shadowsocks服务。

由于对外增加了10001端口号,aws的默认安全策略为仅对外提供22端口,需要在EC2主机的安全策略中增加外放访问tcp端口10001的权限。

脚本

为了安装方便,我简单写了个脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
yum -y install epel-release
#yum update -y
yum install python2-pip -y
pip install shadowsocks
mkdir ~/shadowsocks
echo '{
"server":"0.0.0.0",
"server_port":10001,
"local_port":1080,
"password":"xxx",
"timeout":600,
"method":"aes-256-cfb"
}' > ~/shadowsocks/config.json
systemctl disable firewalld.service
systemctl stop firewalld.service
nohup ssserver -c ~/shadowsocks/config.json &

在某些云主机的CentOS7系统发现无法使用yum install python2-pip进行安装,原因是有些源被禁用了,可以使用yum repolist disabled来查看被禁用的源,其中会包含epel源。可以使用yum install python2-pip -y --enablerepo=epel的方式来安装。

安装shadowsocks客户端

这里是支持的客户端列表,​我这里仅使用的mac客户端ShadowsocksX,支持Auto Proxy Mode和Global Mode两种方式,其中Auto方式会自动下载使用sock5代理的列表,非常方便。

kcptun

为了加快访问速度,推荐使用kcp + shadowsocks

kcp的服务端配置如下,即启用20001端口,该端口会将流量导入到127.0.0.1:10001端口,即本机的shadowsocks端口

1
2
3
4
cd ~ && mkdir kcptun && cd kcptun
wget https://github.com/xtaci/kcptun/releases/download/v20190109/kcptun-linux-amd64-20190109.tar.gz
tar zvxf kcptun-linux-amd64-20190109.tar.gz
nohup ./server_linux_amd64 -l :20001 -t 127.0.0.1:10001 -key xxx -mode fast2 --log ~/kcptun/20001.log &

配置了kcptun的shadowsocks客户端仅需要配置代理为远程的kcpdun端口即可,不再需要指定shadowsocks的端口,相当于shadowsocks是透明的。

监控

为了避免aws产生额外的费用,一定要设置一下费用报警,否则被扣费了就麻烦了。

另外,可定期查看下aws的费用。试用期为一年,一年后一定要记得停掉aws服务。

最后,祝你玩的愉快!

0%