0%

Memcached 和 Redis 比较

比较类别    Redis memcached
数据结构 哈希、列表、集合、有序集合 纯kev-value
持久化 支持 不支持
高可用 主从复制、读写分离、sentinel、Redis Cluster 需二次开发
单value容量 最大512M 最大1M
内存分配 临时申请空间,可能导致碎片 预分配内存池的方式管理内存,能够省去内存分配时间
虚拟内存 有自己的VM机制,理论上能够存储比物理内存更多的数据,当数据超量时,会引发swap,把冷数据刷到磁盘上 所有的数据存储在物理内存里
网络模型 非阻塞IO复用模型,提供一些非KV存储之外的排序、聚合功能,在执行这些功能时,复杂的CPU计算,会阻塞整个IO调度 非阻塞IO复用模型
水平扩展的支持 redis cluster 可以横向扩展 暂无
多线程 Redis6.0之前是只支持单线程 Memcached支持多线程,CPU利用方面Memcache优于Redis
过期策略 有专门线程,清除缓存数据 懒淘汰机制:每次往缓存放入数据的时候,都会存一个时间,在读取的时候要和设置的时间做TTL比较来判断是否过期
单机QPS 约10W 约60W
源代码可读性 代码清爽简洁 可能是考虑了太多的扩展性,多系统的兼容性,代码不清爽
适用场景 复杂数据结构、有持久化、高可用需求、value存储内容较大 纯KV,数据量非常大,并发量非常大的业务

Memcached 集群

Memcached集群,称为基于客户端的分布式集群,即由客户端实现集群功能,即Memcached本身不支持集群

Memcached集群内部并不互相通信,一切都需要客户端连接到Memcached服务器后自行组织这些节点,并决定数据存储的节点

chown

chown 命令可以修改文件的属主, 也可以修改文件属组

1
2
3
4
# 修改文件的属主、属组
chown [OPTION]... [OWNER][:[GROUP]] FILE...
# 参考 rfile 修改file文件的属主和属组
chown [OPTION]... --reference=RFILE FILE...
  • -R 递归修改目录下的所有文件的属主和属组, 此项慎用, 非常危险

chgrp

chgrp 命令只用来修改属组

1
2
chgrp [OPTION]... GROUP FILE...
chgrp [OPTION]... --reference=RFILE FILE..
  • -R 递归

文件权限

u 属主
g 属组
o 其他

用户的最终权限, 从左向右进行顺序匹配, 一旦匹配权限立即生效, 不再向右查看其权限

rwx 读、写、执行

注意: 权限设置对 root 无效

目录和文件的权限区别

主要区别在执行权限 x
文件的x表示可以把文件提请内核启动一个进程, 即可以执行(运行)此文件(此文件必的内容必须是可执行)
目录的x是目录可访问的最小权限, 对文件进行读写操作的时候, 文件路径上经过的所有目录, 都必须先具有执行权限, 否则访问就无法进行下去, 定位不到目录文件

删除和新建文件, 需要有目录的执行(x)和写(w)权限: 这个很好理解, 因为删除和新建文件需要修改文件项, 而文件项就是目录的数据内容

chmod

1
2
3
4
5
6
7
8
9
chmod [OPTION]... MODE[,MODE]... FILE...
chmod [OPTION]... OCTAL-MODE FILE...
#参考RFILE文件的权限,将FILE的修改为同RFILE
chmod [OPTION]... --reference=RFILE FILE...

# MODE: who opt permission
# who: u g o a
# opt: + - =
# permission: r w x X

X 只针对目录 配合 -R 使用的时候可以只递归修改目录的权限, 不影响文件的权限

面试题

1
2
3
4
5
6
7
8
执行 cp /etc/issue /data/dir 所需要的最小权限?

/bin/cp x
/etc/ x
/etc/issue r
/data/ x
/data/dir w、x

新建文件和目录的默认权限

对于文件来说, 大部分文件的内容是不可执行的, 所以默认不给执行权限
对于目录来说, 执行权限是必备的, 所以默认给执行权限

1
2
3
4
5
6
[20:27:57 root@centos7.mageedu.org  data]#mkdir test
[20:28:04 root@centos7.mageedu.org data]#touch a.log
[20:28:07 root@centos7.mageedu.org data]#ll
total 0
-rw-r--r-- 1 root root 0 Aug 3 20:28 a.log
drwxr-xr-x 2 root root 6 Aug 3 20:28 test

umask

umask 可以设定文件创建时的缺省模式, 不是直接设置, 而是间接设置, 首先预设两组默认权限上限:
对于文件来说, 默认权限上限是666(不给执行权限), 666 减去 umask值 就是文件创建时的默认权限;
对于目录来说, 默认权限上限是777(不做任何限制), 777 减去 umask值, 就是目录创建时的默认权限.
所以, 如果umask的值为 022 那么创建文件默认的权限就是 644, 创建目录默认的权限就是 755

1
umask [-p] [-S] [mode]

可以在~/.bashrc 中设置 umask 的值, 永久生效

1
[20:46:48 root@centos7.mageedu.org  data]#echo `umask -p` >> ~/.bashrc 

但是一般不会修改默认的权限, 如果修改也是临时的

练习

  1. 当用户docker对/testdir 目录无执行权限时,意味着无法做哪些操作?

无法ls查看目录下的文件列表; 无法cd到目录; 无法对目录下的文件进行读写操作; 也无法在目录下新建文件, 删除目录下的文件

  1. 当用户mongodb对/testdir 目录无读权限时,意味着无法做哪些操作?

无法ls查看目录下的文件列表

  1. 当用户redis 对/testdir 目录无写权限时,该目录下的只读文件file1是否可修改和删除?

不可以

  1. 当用户zabbix对/testdir 目录有写和执行权限时,该目录下的只读文件file1是否可修改和删除?

不可以修改, 可以删除

  1. 复制/etc/fstab文件到/var/tmp下,设置文件所有者为tomcat读写权限,所属组为apps组有读写权限,其他人无权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[21:01:39 root@centos7.mageedu.org  data]#useradd -s /sbin/nologin -M tomcat
[21:01:53 root@centos7.mageedu.org data]#groupadd apps
[21:03:36 root@centos7.mageedu.org tmp]#ll
total 4
-rw-r--r-- 1 root root 595 Aug 3 21:03 fstab
[21:03:37 root@centos7.mageedu.org tmp]#chown tomact:apps ./fsta
[21:04:05 root@centos7.mageedu.org tmp]#chown tomcat:apps ./fstab
[21:04:16 root@centos7.mageedu.org tmp]#ll
total 4
-rw-r--r-- 1 tomcat apps 595 Aug 3 21:03 fstab
[21:08:20 root@centos7.mageedu.org tmp]#chmod 660 fstab
[21:08:50 root@centos7.mageedu.org tmp]#ll
total 4
-rw-rw---- 1 tomcat apps 595 Aug 3 21:03 fstab
  1. 误删除了用户git的家目录,请重建并恢复该用户家目录及相应的权限属性
1
2
3
4
5
6
mkdir /home/git
# 不要拷贝/etc/skel/.*因为.*包括..会把/etc目录内容也拷贝过去,就不对了,复制隐藏和非隐藏文件
# . 代表当前目录
cp -r /etc/skel/. /etc/skel/* /home/git
chmod 700 /home/git
chown -R git:git /home/git

Linux文件系统上的特殊权限

前面介绍了常见的三种权限 r w x, 还有三种特殊权限: SUID、SGID、Sticky

前提: 进程有属主和属组, 文件有属主和属组

SUID

SUID只对二进制可执行程序(后面称:bin文件)有效,SUID设置在目录上毫无意义。
如果bin文件设置了SUID,那么当前用户执行这个bin文件的时候,会自动继承这个bin文件的属主的身份。
例如:给/usr/bin/passwd设置SUID

1
2
[21:36:33 root@centos7.mageedu.org  home]#ll `which passwd`
-rwsr-xr-x. 1 root root 27856 Apr 1 11:57 /usr/bin/passwd

passwd的属主是root, 当前用户是wang, 那么当wang执行passwd的时候, 就等于root在执行passwd

1
2
3
4
5
6
# SUID权限的设定
chmod u+s file...
chmod u-s file...
chmod 4xxx file... # ugo 中u对应 100 是 4

-rwsr-xr-x. 1 root root 34928 May 11 2019 /usr/bin/passwd

SGID

类似SUID, 区别有两点:

  1. 任何一个可执行程序文件能不能启动为进程,取决于发起者对程序文件是否拥有执行权限,启动为进程后,其进程的属组为原程序文件的属组
  2. SUID作用在目录上无意义, SGID作用在目录上有意义
1
2
3
chmod g+s file...
chmod g-s file...
chmod 2xxx file... # ugo 中 g对应 010 是 2

目录上的SGID的权限功能:
默认情况下, 用户创建文件时, 其属组为此用户所属的主组, 一旦某目录被设置了SGID, 则对此目录有写权限的用户在此目录中创建的文件所属的组为此目录的属组, 通常用于创建一个协作目录

Sticky

具有写权限的目录, 用户通常可以删除该目录下的任何文件, 和文件的权限无关。当目录设置了Sticky权限,只有文件的所有者和root可以删除该文件。

1
2
3
4
5
chmod o+t DIR...
chmod o-t DIR...
chmod 1xxx DIR... # ugo 中 o对应 001 是 1

drwxrwxrwt. 15 root root 4096 Dec 12 20:16 /tmp

权限的数字表示

我们知道rwx可以用421来表示,修改文件权限可以这么写:chmod 755 file,其实755前面还有一个0。这个0就是特殊权限位,当权限位是0的时候,可以省略

suid、sgid、sticky
000 0
001 1
010 2
011 3
100 4
101 5
110 6
111 7

权限位映射

有suid时,属主位的x变成s,如果属主位没有x,则x位置上的-变成S
有sgid时,属组位的x变成s,如果属组位没有x,则x位置上的-变成S
有sticky时,OHTER位的x变成t,如果OHTER位没有x,则x位置上的-变成T

1
2
3
4
5
6
7
# 注意t和T
lujinkai@Z510:~/data/test$ chmod 1755 ./grep/
lujinkai@Z510:~/data/test$ ll -d grep/
drwxr-xr-t 2 lujinkai lujinkai 4096 Aug 13 17:27 grep//
lujinkai@Z510:~/data/test$ chmod o-x grep/
lujinkai@Z510:~/data/test$ ll -d grep/
drwxr-xr-T 2 lujinkai lujinkai 4096 Aug 13 17:27 grep//

设定文件的特殊属性 chattr

设置文件的特殊属性, 可以防止root用户删除或修改文件

1
2
3
4
5
6
7
8
9
# 不能删除、改名、更改
chattr +i files...

# 只能追加内容,不能删除、改名
chattr +a files...

# 显示特殊属性
lsattr

访问控制列表 ACL

Access Control List 实现灵活的权限管理
除了 u g o, 可以对更多用于设置权限
CentOS6可能不支持, 需要手动增加ACL功能

ACL生效顺序

owner > acl user > group > other

ACL相关命令

  • setfacl 为用户或群组添加针对某目录或文件的 ACL 权限

    1
    2
    setfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ...
    setfacl --restore=file
    • -m –modify-acl 更改文件的访问控制列表
    • -x –remove-acl
    • -b –remove-all
    • –set 选项会把原有的ACL项都删除, 用新的替代, 需要注意的是一定要包含u g o设置, 不能像-m一样只是添加ACL就可以
      1
      2
      setfacl --set u::rw,u:wang:rw,g::r,o::- file1
      # 设置u的权限为rw、设置用户wang的权限是rw、设置g的权限是r,清空o的所有权限
  • getfacl 查看设置ACL权限

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
# 设置test对a.log文件没有任何权限 可以用 - 或 0 或 000 表示
[08:57:42 root@centos7.mageedu.org test]#setfacl -m u:test:- a.log
# 查看对a.log文件设置的ACl权限
[08:58:30 root@centos7.mageedu.org test]#getfacl a.log
# 设置apps群组对a.log文件只有写权限
[08:58:33 root@centos7.mageedu.org test]#setfacl -m g:apps:w a.log
[08:59:06 root@centos7.mageedu.org test]#getfacl a.log
# file: a.log
# owner: root
# group: root
user::rw-
user:test:---
group::r--
group:apps:-w-
mask::rw-
other::r--
# 清除test用户, 对a.log文件acl权限
[08:59:07 root@centos7.mageedu.org test]#setfacl -x u:test a.log
[08:59:32 root@centos7.mageedu.org test]#getfacl a.log
# file: a.log
# owner: root
# group: root
user::rw-
group::r--
group:apps:-w-
mask::rw-
other::r--
# 清除a.log上的所有acl权限
[08:59:33 root@centos7.mageedu.org test]#setfacl -b a.log
[08:59:55 root@centos7.mageedu.org test]#getfacl a.log
# file: a.log
# owner: root
# group: root
user::rw-
group::r--
other::r--

mask

mask 只设置除u和o之外的人和组的最大权限

1
[09:28:44 root@centos7  test]#getfacl -m mask::r a.log 

设置mask之后, 如果再使用setfacl或者chmod更改文件的权限, mask的值会自动调整

备份和还原ACL

主要的文件操作命令cp和mv都支持ACL, cp命令需要加上-p参数。但是tar等常见的备份工具不会保留目录和文件的ACL信息

1
2
3
4
# 备份 先导出acl权限信息,然后再压缩备份
get -R /tmp/dir > acl.txt
# 恢复 先解压文件,然后导入acl权限信息
setfacl -R --set-file=acl.txt /tmp/dir

练习

  1. 在/testdir/dir里创建的新文件自动属于webs组,组apps的成员如:test能对这些新文件有读写权限,组dbs的成员如:mysql只能对新文件有读权限,其它用户(不属于webs,apps,dbs)不能访问这个文件夹

1.将apps、dbs中的成员加入到webs组中
2.注意setfacl设置apps和dbs的ACL时给x权限

  1. 误将 /bin/chmod 文件的执行权限删除,如何恢复?

方法一:从别的机器上拷贝一个过来
方法二:用系统中已有的各种脚本语言解释器修改权限
方法三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[14:24:31 root@centos7 data]#chmod -x /usr/bin/chmod 
[14:24:41 root@centos7 data]#ll /usr/bin/chmod
-rw-r--r--. 1 root root 58592 Aug 20 2019 /usr/bin/chmod
[14:24:48 root@centos7 data]#setfacl -m u:root:rwx /usr/bin/chmod
[14:25:18 root@centos7 data]#getfacl /usr/bin/chmod
getfacl: Removing leading '/' from absolute path names
# file: usr/bin/chmod
# owner: root
# group: root
user::rw-
user:root:rwx
group::r--
mask::rwx
other::r--

[14:25:30 root@centos7 data]#chmod +x /usr/bin/chmod
[14:25:43 root@centos7 data]#ll /usr/bin/chmod
-rwxrwxr-x+ 1 root root 58592 Aug 20 2019 /usr/bin/chmod
[14:25:48 root@centos7 data]#setfacl -b /usr/bin/chmod
[14:26:03 root@centos7 data]#ll /usr/bin/chmod
-rwxr--r-x. 1 root root 58592 Aug 20 2019 /usr/bin/chmod

7种文件类型

  1. - 普通文件
  2. d 目录文件 directory
  3. b 块设备block
  4. c 字符设备 character
  5. l 符号链接文件 link
  6. p 管道文件 pipe
  7. s 套接字文件 socket

普通文件

普通文件分为三类:

  1. 纯文本文件 ASCII
    使用cat可以查看纯文本文件
  2. 二进制文件 binary
    使用od或者hexdump命令以8进制或者16进制查看
  3. 数据格式文件 data
    有些程序运行的过程中会读取某些特定格式的文件, 那些特定格式的文件可以被成为数据文件(data file). 举例来说, 我们的Linux在使用登录的时候, 都会将登录的数据记录在 /var/log/wtmp 文件内, 该文件就是一个data file, 它可以被lastwho命令读取内容.

目录文件

块设备 和 字符设备

块设备是硬件设备, 以块(block, 在EXT4文件系统中, 一个block通常为4K)为单位, 应用程序通过随机访问的形式读取数据.
最常见的块设备是硬盘, 还有软盘等. 注意这些都是挂载文件系统的设备, 文件系统就像块设备的通用语言.

字符设备文件以字节流的方式进行访问,由字符设备驱动程序来实现这种特性。字符终端串口键盘等就是字符设备。可以顺序读取,但通常不支持随机存取。

区分块设备和字符设备最简单的方法就是看数据访问的方式, 能随机访问获取数据的是块设备, 必须按字节顺序访问的是字符设备.

符号链接文件

符号链接文件又叫软连接,软连接相当于一个快捷方式

软(symbolic 或 soft)链接

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的”软链接”(soft link)或者”符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:”No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此发生变化。

1
ln -s filename [linkname]
  • 软链接的内容是它引用文件的路径
  • 可以对目录创建软链接
  • 可以跨分区创建软链接

硬(hard)链接

硬链接本质上就给一个文件起一个新的名称,不是符号链接,写在这里主要是为了阅读方便

1
ln filename [linkname]

inode信息中有一项叫做”链接数”,记录指向该inode的文件名总数

  • 创建硬链接后,和原文件的inode还是一样的
  • 新建硬链接, inode中的”链接数” + 1
  • rm删除文件, inode中的”链接数” - 1,当”链接数”为0,才是真正删除此文件
  • 不能跨域驱动器或分区创建硬链接
  • 不支持对目录创建硬链接

管道文件 (FIFO文件)

管道文件主要用于进程间通信

管道都是一端写入、另一端读取,它们是单方向数据传输的,它们的数据都是直接在内存中传输的,管道是进程间通信的一种方式,例如父进程写,子进程读。

套接字文件

套接字用来实现两端通信,可以实现双向管道的进程间通信功能。不仅如此,套接字还能通过网络实现跨主机的进程间通信功能。

套接字需要成对才有意义,也就是分为两端,每一端都有用于读、写的文件描述符(或文件句柄),相当于两根双向通信的管道。

套接字根据协议族的方式分为两大类:网络套接字(AF_INET类型,根据ipv4和ipv6分为inet4和inet6)和Unix Domain套接字(AF_UNIX类型)。当然,从协议族往下,套接字可细分为很多种类型,例如INET套接字可以分为TCP套接字、UDP套接字、链路层套接字、Raw套接字等等。其中网络套接字是网络编程的基础和核心。

对于单机的进程间通信,使用Unix Domain套接字比Inet套接字更好,因为Unix Domain套接字没有网络通信组件,也就是少了很多网络功能,它更加轻量级。实际上,某些语言在某些操作系统平台上实现的管道功能就是通过Unix Domain来实现的,可想而知其高效率。例如:http://blog.lujinkai.cn/archives/9.html#H2_9,nginx和PHP-FPM的进程间通信有两种方式,一种是TCP,一种是UNIX Domain Socket.其中TCP是IP加端口,可以跨服务器.而UNIX Domain Socket不经过网络,只能用于Nginx跟PHP-FPM都在同一服务器的场景,效率更高。

文件元数据

每个文件的属性信息,比如:文件的大小、时间、类型等,称为文件的元数据(meta data)

atime、mtime、ctime

每个文件都有三个时间,atime、mtime、ctime,使用stat命令可以查看。

  • atime:access time 访问时间,当使用cat命令查看文件内容时atime不改变。使用wq退出vim时修改atime,使用q!退出vim时不改变atime。使用echo往文件写内容不改变atime。使用sed -i修改文件改变atime

  • mtime:modify time 修改时间,当文件内容被修改时,mtime发生改变。

  • ctime:change time 变化时间,文件的inode被修改时,ctime发生改变。注意:只要mtime发生改变,ctime肯定也同步发生改变,如果文件较大,ctime可能会延迟几毫秒。

文件节点表结构

广义上,一个文件由三部分组成:目录项、索引节点(inode)、数据块

  • 目录项:dirent,包含文件名和索引节点号,索引节点号指向索引节点表(inode table)中对应的索引节点
  • 索引节点:inode,包含文件的元数据以及数据块指针
  • 数据块:包含文件的具体内容

inode的大小

硬盘格式化的时候,操作系统自动将硬盘分为两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

inode有上图中的左边区域组成,数据块由右边区域构成。不同的文件大小,通过多层级的间接指针协同完成。

直接块指针有12个,一个块大小为4KB,所以直接指针可以保存48KB的文件

间接块指针:每个指针占用4个字节,一个块是4KB,所以可以将一个块拆分成1024个指针,那么它的存储数据1024*4KB=4MB

双重间接块指针:同理可得它可以存储的数据为1024*4MB=4GB

三级指针可以储存文件数据大小为1024*4GB=4TB

一个字节占用几个字节?

https://blog.csdn.net/IOSSHAN/article/details/88944637

目录也是文件,打开目录,实际上就是打开目录文件。

目录文件也是由目录项、inode、数据块组成。

目录的数据块中存储的是一系列目录项的列表,是其下的文件的所有目录项。每个目录项,由两部分组成:包含文件名,以及该文件名对应的inode号码。

打开一个文件分三步走

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

打开一个文件分三步走:名称与inode编号关联,首先,系统找到这个文件名对应的inode号码;其次, 通过inode号找到对应的inode,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

cp和inode

  1. 分配一个空闲的inode号,在inode table中生成新条目
  2. 在目录中创建一个目录项,将名称与inode编号关联
  3. 拷贝数据生成 新的文件

rm和inode

  1. 链接数递减,当减到0,就释放inode号
  2. 把数据块放在空闲列表中
  3. 删除目录项
  4. 数据实际上不会马上被删除,但是当另一个文件使用数据块时将被覆盖

根据3, 可以得出, 删除文件实际上是修改目录的内容, 所以删除文件必须要有目录的写权限, 所以删除文件和文件的权限没有关系, 和文件的父目录的权限有关, 同理:创建文件也是这样

mv和inode

如果mv命令的目标和源在相同的文件系统

  1. 创建新的目录项,新名称对应到inode号
  2. 删除旧的目录项
  3. 修改对应inode上的元数据(时间戳),不移动数据

如果mv命令的目标和源在不同的文件系统:mv相当于 cp + rm

1
2
# 删除大文件
[root@centos8 ~]#cat /dev/null > /var/log/huge.log

IO重定向和管道

IO的I是input,IO的O是ouput

Linux给程序提供了三种IO设备:

  • 标准输入(STDIN)0 默认接受来自终端窗口的输入
  • 标准输出(STDOUT)1 默认输出到终端窗口
  • 标准错误(STDERR)2 默认输出到终端窗口

STDOUT和STDERR可以被重定向到指定文件,而非默认的当前终端。

1
命令 操作符 文件名

2>&1

这里的&类似php中表示变量引用的用法, 放在>后面的&, 表示重定向的目标不是一个文件, 而是一个文件描述符

标准错误拷贝了标准输出的行为, 注意是行为,不仅仅是路径。如果标准输出是 > a.log,那么标准错误输出也是 > a.log,如果标准输出是 >> a.log,那么标准错误输出也是 >> a.log

而&>file是一种特殊的用法, 也可以写成>&file, 二者的意思完全相同, 都等价于1>file 2>&1, 此处的&>或者>&视作整体, 不能分开

标准输入重定向 单行重定向 <

利用<可以将标准输入重定向

1
2
3
4
5
6
7
8
9
10
[root@4710419222 test]# cat a.log 
a
# cat 如果没有指定文件, 或者指定文件为"-", 则从标准输入读取
[root@4710419222 test]# cat > b.log <a.log
[root@4710419222 test]# cat b.log
a
[root@4710419222 test]# echo b > b.log
[root@4710419222 test]# cat < a.log > b.log
[root@4710419222 test]# cat b.log
a

多行重定向 <<终止词

这个很好理解, 类似php的长字符串<<<EOT

1
2
3
4
5
6
7
8
9
[root@4710419222 test]# cat > a.log <<EOT
> 123
> 456
> 789
> EOT
[root@4710419222 test]# cat a.log
123
456
789

管道

1
命令1 | 命令2 | 命令3 | ...
  • 将命令1的STDOUT发送给命令2的STDIN, 命令2的STDOUT发送到命令3的STDIN
  • 所有命令都在当前shell进程的子进程中执行
    注意:管道符后面只能跟一个命令,如果跟多个命令,命令之间用分号间隔,那么,由于管道的右边会打开一个子进程,所以,后面的命令实际上是属于父进程的,解决办法就是用{}或者()把多个命令包裹起来,确保多个这多个命令是在同一个进程下执行
    1
    2
    3
    4
    5
    6
    [root@centos8 scripts]#echo 1 2 | read x y ; echo x=$x y=$y
    x= y=
    [root@centos8 ~]#echo 1 2 | ( read x y ; echo x=$x y=$y )
    x=1 y=2
    [root@centos8 ~]#echo 1 2 | { read x y ; echo x=$x y=$y; }
    x=1 y=2
  • 组合多种工具的功能

注意: STDERR默认通过管道转发, 可利用2>&1|&实现

1
2
3
4
5
6
7
8
[root@4710419222 test]# ll c.log
ls: cannot access c.log: No such file or directory
# 命令1 2>&1 | 命令2
[root@4710419222 test]# ll c.log 2>&1 | tr a-z A-Z
LS: CANNOT ACCESS C.LOG: NO SUCH FILE OR DIRECTORY
# 命令1 |& 命令2
[root@4710419222 test]# ll c.log |& tr a-z A-Z
LS: CANNOT ACCESS C.LOG: NO SUCH FILE OR DIRECTORY

重定向中的 - 符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 将下载文件内容输出到标准输出, 而不保存文件
[root@centos8 ~]#wget -qO - http://www.wangxiaochun.com/testdir/hello.sh
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 2017/06/01
# Author: wang
# Email: 29308620@qq.com
# Website: www.wangxiaochun.com
# Description: This is the first script
# Copyright: 2017 wang
# License: GPL
# ------------------------------------------
#经典写法
echo "hello, world"
#流行写法
echo 'Hello, world!'

# 将 /home 里面的文件打包,但打包的数据不是记录到文件,而是传送到 stdout,经过管道后,将 tar - cvf - /home 传送给后面的 tar -xvf - , 后面的这个 - 则是取前一个命令的 stdout, 因此,就不需要使用临时file了
tar -cvf - /home | tar -xvf -

文件描述符(fd)

Linux中一切都是文件。文件描述符fd是内核为了高效管理已被打开的文件所创建的索引,是一个非负整数,在文件open时产生。程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。

系统为每一个进程维护了一个文件描述符表,该表的值都是从0开始的,由于进程级文件描述符表的存在,不同的进程中会出现相同的文件描述符,它们可能指向同一个文件,也可能指向不同的文件

文件操作相关命令

pwd

1
2
3
4
5
# -P 显示真实的物理路径
[root@centos7 bin]# pwd
/bin
[root@centos7 bin]# pwd -P
/usr/bin

basename 和 dirname

1
2
3
4
5
6
7
8
9
10
[root@4710419222 vhost]# pwd
/usr/local/nginx/conf/vhost
# 显示文件名
[root@4710419222 vhost]# basename `pwd`
vhost
[root@4710419222 vhost]# basename `pwd`\/to2b.cn.conf
to2b.cn.conf
# 显示路径
[root@4710419222 vhost]# dirname $(pwd)
/usr/local/nginx/conf

cd

1
2
3
4
5
# 切换至物理路径, 而非软链接目录 -P
[root@4710419222 ~]# cd -P /bin/
[root@4710419222 bin]# pwd
/usr/bin
# 切换到来时的目录 cd -

ls

1
2
3
4
5
6
7
8
9
10
11
12
# 显示隐藏文件,但是不显示.和..
[root@47105171233 ~]# ll -A
# 按照mtime从新到旧排序
[root@4710419222 ~]# ll -t
# 按照mtime从旧到新排序
[root@4710419222 ~]# ll -tr
# 按照atime从新到旧排序
[root@4710419222 ~]# ll -ut /
# 按照从大到小排序
[root@4710419222 /]# ll -S
# 递归遍历目录下所有文件
[root@4710419222 ~]# ll -R

说明: ls 查看不同后缀文件时的颜色由 /etc/DIR_COLORS 和@LS_COLORS变量定义

stat

文件有三个时间

1
2
3
4
5
6
7
8
9
[root@4710419222 ~]# stat test.php
File: ‘test.php’
Size: 96 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 138598 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-07-26 18:48:51.780437746 +0800 # 文件最近一次被访问的时间
Modify: 2020-07-25 17:21:15.970280822 +0800 # 文件内容最近一次被修改的时间
Change: 2020-07-25 17:21:15.973280860 +0800 # 文件属性最近一次被修改的时间
Birth: -

file

判断文件的类型不能依靠后缀, 可以使用file命令判断文件的类型

hexdump

hexdump命令一般用来查看“二进制”文件的十六进制编码,但实际上它能查看任何文件,而不只限于二进制文件。

-C 输出规范的十六进制和ASCII码。

1
2
3
[root@4710419222 ~]# hexdump -C test.txt       
00000000 0a |.|
00000001

文件通配符 glob

文件通配符可以用来匹配符合条件的多个文件,方便批量管理文件

1
2
3
4
5
6
7
8
9
10
11
12
 # * 匹配零个或多个字符, 但不匹配隐藏文件
[root@4710419222 ~]# ll -a *
-rw-r--r-- 1 root root 863 Oct 17 2019 clear_history.php
-rw-r--r-- 1 root root 244205697 Oct 24 2018 oneinstack-full.tar.gz
lrwxrwxrwx 1 root root 27 Jul 18 2019 python-learn -> /data/wwwroot/python-learn/
-rw-r--r-- 1 root root 1073741824 Oct 31 2018 swapfile
-rw-r--r-- 1 root root 11195 Sep 27 2019 testparm-v.txt
# ? 匹配任何单个字符
# [0-9]
# [a-z] a、A、b、B...x、X、y、Y、z、Z
# [lujinkai]
# [^lujinkai]

此外, 还有预定义的字符类: man 7 glob

1
2
3
4
5
6
7
8
9
10
11
12
[:digit:]:任意数字,相当于0-9
[:lower:]:任意小写字母,表示 a-z
[:upper:]: 任意大写字母,表示 A-Z
[:alpha:]: 任意大小写字母
[:alnum:]:任意数字或字母
[:blank:]:水平空白字符
[:space:]:水平或垂直空白字符
[:punct:]:标点符号
[:print:]:可打印字符
[:cntrl:]:控制(非打印)字符
[:graph:]:图形字符
[:xdigit:]:十六进制字符

ps: 批量创建文件是用{}, 批量显示文件使用[]

文件通配符(glob)和正则表达式(regex)

文件通配符就是* ? [] 这三个,正则表达式则功能强大。

对于:文件通配符中,\匹配0个或多个字符,可以单独使用。而在正则表达式中,*是匹配前面的0次或多次,前面必须由内容,不能单独使用。

对于?:和*一样,文件通配符中,?可以单独使用,正则表达式中不可以。

对于[]:文件通配符[]中的内容是按照ASCII统计的,例如[a-z]会匹配a、A、b、B…y、Y、z,而正则表达式[]中的内容是按照人类的方式统计,例如[a-z]会匹配a、b、c、d、e…x、y、z

对于.:文件通配符中.就是.,没有别的意思。正则表达式中.匹配单个字符

touch

创建空文件和刷新文件时间

1
2
3
4
# -a 仅改变 atime和ctime
# -m 仅改变 mtime和ctime
# -t [[CC]YY]MMDDhhmm[.ss] 指定atime和mtime的时间戳
# -c 如果文件不存在,则不予创建

cp

  • -a 归档,相当于-dR –preserv=all,常用于备份功能
  • -u –update 只复制源比目标先更新文件或目标不存在的文件
  • -b 目标存在, 覆盖前先备份, 默认形式为filename~, 只保留最近一个备份
  • –backup=numbered 目标存在, 覆盖前先备份加数字后缀, 形式为filename.~#~, 可以保留多个版本

注意: 不同类型的文件不能覆盖, 例如普通文件可以覆盖普通文件, 但是不能覆盖目录

1
2
3
4
5
6
7
8
9
10
[root@4710419222 test]# ll
drwxr-xr-x 2 root root 12288 Jul 29 15:32 test
[root@4710419222 test]# \cp -a --backup=numbered /test ./xxx
[root@4710419222 test]# \cp -a --backup=numbered /test ./xxx
[root@4710419222 test]# \cp -a --backup=numbered /test ./xxx
[root@4710419222 test]# ll
drwxr-xr-x 2 root root 12288 Jul 29 15:32 test
-rw-r--r-- 1 root root 11 Jul 29 15:30 xxx
-rw-r--r-- 1 root root 11 Jul 29 15:30 xxx.~1~
-rw-r--r-- 1 root root 11 Jul 29 15:30 xxx.~2~

mv

1
2
3
mv [OPTION]... [-T] SOURCE DEST	# 移动并重命名
mv [OPTION]... SOURCE... DIRECTORY # 移动到目录
mv [OPTION]... -t DIRECTORY SOURCE... # 目的目录参数在前, 要移动的文件参数在后

mv命令可以移动和重命名文件, 同一分区移动会很快

-b 目标存在, 覆盖前先备份

但是mv命令只能一次重命名一个文件, 使用rename命令可以批量重命名文件

rename

1
2
3
4
5
6
7
8
9
10
11
# rename [options] <expression> <replacement> <file>...
# 修改后缀
[root@4710419222 test]# ll
total 4
-rw-r--r-- 1 root root 18 Jul 29 19:36 a.txt
-rw-r--r-- 1 root root 0 Jul 29 19:14 b.txt
[root@4710419222 test]# rename '.txt' '.log' ./*
[root@4710419222 test]# ll
total 4
-rw-r--r-- 1 root root 18 Jul 29 19:36 a.log
-rw-r--r-- 1 root root 0 Jul 29 19:14 b.log

这个命令好像可以使用正则, 但是我试了一下, 不行, 可能是版本的问题

rm

此命令非常危险, 建议使用mv替代rm , 当删除的时候, 实际上是移动到一个特定的目录

1
alias rm='DIR=/data/backup`date +%F%T`;mkdir $DIR;mv -t $DIR'

rm虽然删除了文件, 但是在安全场景要求较高的情况下, 可以使用shred安全删除文件

1
2
3
4
5
[root@centos8 ~]#shred -zvun 5 passwords.txt
# -z 最后一次覆盖添加0,以隐藏覆盖操作
# -v 能够显示操作进度
# -u 覆盖后截断并删除文件
# -n 指定覆盖文件内容的次数(默认值是3次)

tree

显示目录树

df

1
df [选项列表]... [文件列表]...
  • -a –all
  • -h –human-readable
  • -i –inodes 显示 inode 信息而非块使用量

关于 df 和 lsblk

lsblk 查看的是block device,也就是逻辑磁盘大小。df查看的是file system, 也就是文件系统层的磁盘大小。

显示磁盘占用情况和inode使用情况使用df。

mkdir

  • -p 目录如果不存在, 创建, 如果存在, 不报错
  • -m 创建目录时直接指定权限

rmdir

  • -p 递归删除父空目录

挂载点不能删除

tee

从标准输入读入并写往标准输出和文件

1
tee [选项]... [文件列表]...

把标准输入的数据复制到文件列表中的每一个文件,同时送往标准输出。

  • -a –append 追加到给出的文件, 而不是覆盖

功能: 保存不同阶段的输出; 复杂管道的故障排除; 同时查看和记录输出

1
2
3
4
5
6
[root@centos8 ~]#cat <<EOF | tee /etc/motd
> welcome to magedu
> happy new year
> EOF
welcome to magedu
happy new year

lsof

列出打开的文件,Linux下万物皆文件,网络也是文件。
lsof的参数巨多,下面列举常用的参数

1
lsof file	# 查看文件被哪些进程占用
1
2
lsof -i		# 列出所有的网络连接
lsof -i tcp # 列出所有的tcp网络连接

seq

打印数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@centos8 ~]# seq 0 9
0
1
2
3
4
5
6
7
8
9
[root@centos8 ~]# seq -s ' ' 0 9
0 1 2 3 4 5 6 7 8 9
[root@centos8 ~]# seq -s ' ' 0 2 9
0 2 4 6 8
[root@centos8 ~]# seq -s + 1 100 | bc
5050
[root@centos8 ~]# seq -s + 1 2 100 | bc
2500

练习题

1、将/etc/issue文件中的内容转换为大写后保存至/tmp/issue.out文件中

1
[root@centos7 test]# cat /etc/issue | tr 'a-z' 'A-Z' > /tmp/issue.out

2、将当前系统登录用户的信息转换为大写后保存至/tmp/who.out文件中

1
[root@centos7 test]# whoami | tr 'a-z' 'A-Z' > /tmp/who.out

3、一个linux用户给root发邮件,要求邮件标题为”help”,邮件正文如下:
Hello, I am 用户名,The system version is here,please help me to check it ,thanks!
操作系统版本信息

1
[root@centos8 ~]# echo -e "Hello, I am `whoami`,The system version is here,please help me to check it ,thanks! \n`cat /etc/os-release`" | mail -s hello root

4、将/root/下文件列表,显示成一行,并文件名之间用空格隔开

1
[root@4710419222 /]# ls -A /root | tr '\n' ' ' 

5、计算1+2+3+…+99+100的总和

1
2
[root@centos8 test]# seq -s + 1 100 | bc
5050

6、删除Windows文本文件中的回车字符 ,即“\r”

1
cat a.log | tr '\r' ' '

7、处理字符串“xt.,l 1 jr#!$mn 2 c*/fe 3 uz 4”,只保留其中的数字和空格

1
2
[root@centos8 test]# echo 'xt.,l 1 jr#!$mn 2 c*/fe 3 uz 4' | tr -dc '0-9 '
1 2 3 4[root@centos8 test]#

8、将PATH变量每个目录显示在独立的一行

1
[root@centos8 test]# echo $PATH | tr ':' '\n'

9、将指定文件中0-9分别替代成a-j

1
[root@centos8 test]# cat a.log | tr '0-9' 'a-j'

10、将文件/etc/centos-release中每个单词(由字母组成)显示在独立一行,并无空行

1
2
3
4
5
[root@centos8 test]# cat /etc/centos-release | tr -c "[:alpha:]" " " | tr -s " " "\n"
CentOS
Linux
release
Core

11、ls 输出的内容明明是分行的, 为什么显示出来就不分行了?

不单 ls 会这样,不少其他命令也会这样。它们会使用 isatty 函数查询输出是否指向终端,对输出到终端和非终端的处理,可能不一样

常见的比如:
1、输出到终端时,使用 color,非终端则不用;
2、输出到终端时,使用 text 方式,非终端则用 binary。

如果需要一致的输出,应当明确使用相关参数

這取決於 stdout 是不是終端。如果是終端就可以讀取終端的寬度,根據寬度排版。

Linux 安全模型

3A:

认证: Authentication (确认身份, 登录系统的用户名和密码)

授权: Authorization (对于文件和程序的权限)

审计: Accouting|Audition (监控日志)

认证和授权linux本身的软件实现, 审计通过特定的软件实现

用户登录成功时, 系统会自动分配令牌token, token中包含了用户标识和组成员等信息, 用户的每一步操作, 系统都会校验他的token, 验证他的权限。使用`id`可以查看token

用户

linux中每个用户是通过 uid 来唯一标识的 id -u

  • 管理员: root, 0
  • 普通用户
    • 系统用户: 1-499 (centos6及以前), 1-999 (centos7以后), 对守护进行获取资源进行权限分配
    • 登录用户: 500+ (centos6及以前), 1000+ (centos7以后), 给用户进行交互式登录使用

用户组

linux中可以将一个或多个用户加入用户组中, 用户组通过 gid 唯一标识 id -g

  • 管理员组: root, 0
  • 普通组:
    • 系统组
    • 普通组

用户和用户组的关系

  • 用户的主要组: 用户必须属于一个且只有一个主组, 默认创建用户时会自动创建和用户同名的组作为用户的主组, 由于此组中只有一个用户, 又称为私有组
  • 用户的附加组: 一个用户可以属于0个或多个辅助组, 附属组
1
2
[lujinkai@centos7 root]$ id postfix
uid=89(postfix) gid=89(postfix) groups=89(postfix),12(mail)

安全上下文

进程能够访问资源的权限取决于进程的运行者身份, 而非程序本身

用户和组的配置文件

  • /etc/passwd 系统用户配置文件,存储了系统中所有用户的基本信息,并且所有用户都可以对此文件执行读操作
  • /etc/shadow 存储 Linux 系统中用户的密码信息,又称为“影子文件”。
  • /etc/group 用户组配置文件,用户组的所有信息都存放在此文件中
  • /etc/gshadow 存储组用户的密码信息

/etc/passwd 文件格式

1
2
3
4
5
[root@centos7 ~]# whatis passwd
sslpasswd (1ssl) - compute password hashes
passwd (1) - update user's authentication tokens
passwd (5) - password file
[root@centos7 ~]# man 5 passwd
  1. name: 用户登录名
  2. password: 在很早之前, 这里确实存储密码, 后来因为安全问题, 密码就搬迁到/etc/shadow中了, 但是因为兼容性问题, 所以格式不能变, 就留了一个占位符, 用x或*占位
  3. UID: 用户的id
  4. GID: 用户主组的id
  5. GECOS: 存储用户全名和一些注释
  6. directory: 用户家目录
  7. shell: 用户默认使用的shell ( /bin/bash )

/etc/shadow 文件格式

/etc/passwd文件所有用户都可以读, /etc/shadow文件只有root用户才可以读

注意,如果这个文件的权限发生了改变,则需要注意是否是恶意攻击。

Linux /etc/shadow(影子文件)内容解析(超详细)

  1. 用户登录名
  2. 加密过的用户登录密码, 一般用sha512加密
  3. 最后一次修改密码的时间, 如果没有修改过, 则空置
  4. 密码再过几天可以被更改, 0表示随时都可以更改
  5. 密码再过几天必须被更改, 9999是300多年, 可以认为永不过期
  6. 密码过期前几天系统提醒用户, 默认是一周
  7. 密码过期后的宽限时间, 以天为单位, 也就是说密码过期后, 用户如果还没有修改密码, 则在此字段规定的宽限天数内, 用户还是可以登录系统
  8. 账号过期时间, 从1970.1.1开始算, 多少天后账号失效, 在此规定时间之外, 不论密码是否过期, 都无法登录! 该字段通常被使用在具有收费服务的系统中
  9. 保留, 这个字段目前没有使用, 等待新功能的加入

/etc/group 文件格式

1
2
3
4
root:x:0:
mail:x:12:postfix
lujinkai:x:1000:
# 组名:密码:GID:以当前组为附加组的用户列表(分割符为逗号)

/etc/gshadow 文件格式

1
2
3
4
root:::
mail:::postfix
lujinkai:!::
# 组名:组密码:组管理员
  • 组密码

    通常不设置组密码, 为空或者 ! 都表示该群组没有组密码

  • 组管理员列表

    可以更改组密码和添加用户到群组中, 组管理员列表用逗号分隔。这个功能现在不常用了。

  • 组管理员

    该字段显示这个用户组中有哪些附加用户,和 /etc/group 文件中附加组显示内容相同,多个用户也是用逗号分隔

相关命令

getent

用来察看系统的数据库中的相关记录,系统数据库包括:ahosts、ahostsv4、ahostsv6、aliases、ethers、group、gshadow、hosts、initgroups、netgroup、networks、passwd、protocols、rpc、services、shadow

1
2
# getent database [key ...]
# getent会通过key查询整条数据, 当然, 一条数据并非只能对应一个key, 例如passwd, 一条数据的key可以是用户名, 也可以是UID
1
2
3
4
5
6
7
8
# 查询passwd
[root@centos7 ~]# getent passwd lujinkai
lujinkai:x:1000:1000:lujinkai:/home/lujinkai:/bin/bash
[root@centos7 ~]# getent passwd 1000
lujinkai:x:1000:1000:lujinkai:/home/lujinkai:/bin/bash
# 查询shadow
[root@centos7 ~]# getent shadow lujinkai
lujinkai:$6$Kmenu.bK$sIXTEMYHCL9QJ9ZLEmkeybfF6NV3p6l2m3G1SLFSgomaDOPArKcbZuUHgIunOXjMqfJ48KYbW76YvEgg1hcXs0:18475:0:99999:7:::

文件操作 vipw、vigr、pwck、grpck

尽量不要直接修改/etc/passwd、/etc/group、/etc/shadow、/etc/gshadow这四个文件。

vipw可以用来编辑/etc/passwd,vigr可以用来编辑/etc/group,它俩都有语法检查功能。

pwck用来验证/etc/passwd和/etc/shadow的内容和格式的完整性,grpck用于验证组文件的完整性。

虽然用这四个命令会比直接使用vim修改这四个文件更安全,但是也不推荐使用,了解即可。

推荐使用:useradd、groupadd、chage等命令,当然本质还是修改这四个文件。

用户创建 useradd

注: adduser 等于 useradd

1
useradd [options] LOGIN
  • -u, –uid UID

    指定uid, 不过不加-o参数, 指定的uid必须是唯一非负整数

  • -o, –non-unique

    配合-u选项, 不检查UID的唯一性

  • -g, –gid GROUP

    指定用户所属基本组, 可以是组名, 也可以是GID

  • -c, –comment COMMENT

    用户的的注释, 要简短一些

  • -d, –home-dir HOME_DIR

    指定用户的家目录

  • -s, –shell SHELL

    指定用户的默认shell, 可从/etc/shells中选择, 如果是给服务用, 指定/sbin/nologin

  • -G, –groups GROUP1[,GROUP2,…[,GROUPN]]]

    指定用户的附加组, 组须是已存在的

  • -N, –no-user-group

    不创建私用组作为主组, 使用users组做主组

  • -r, –system

    创建系统用户 centos6之前: UID < 500, centos7之后: UID < 1000
    使用这个参数, 不会自动创建家目录和邮件目录

  • -m, –create-home

    创建家目录, 用于系统用户

  • -M, –no-create-home

    不创建家目录, 用于非系统用户

  • -p, –password PASSWORD

    指定密码, 必须是加密过后的

创建用户相关文件

  • /etc/default/useradd
  • /etc/skel/
  • /etc/login.defs

useradd命令默认值设置在 /etc/default/useradd

1
2
3
4
5
6
7
8
# useradd defaults file
GROUP=100 # 如果useradd没有指定组,并且/etc/login.defs中的USERGROUPS_ENAB为no或者useradd使用了-N选项时,此时该参数生效。
HOME=/home # 家目录放在此目录下
INACTIVE=-1 # 对应/etc/shadow文件第7列, 即用户密码过期的宽限期, -1表示不过期
EXPIRE= # 对应/etc/shadow文件第8列,即用户账号的有效期, 不设置表示不启用
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes

/etc/skel/ 是模板目录,骨架目录。每新建一个用户的家目录,就会把/etc/skel/下的文件拷贝一份到家目录下。

1
2
3
4
5
6
7
[root@centos7 skel]# ls -lia /etc/skel/
total 24
134321114 drwxr-xr-x. 2 root root 62 Jul 29 17:08 .
134320193 drwxr-xr-x. 73 root root 8192 Aug 1 17:49 ..
134360296 -rw-r--r--. 1 root root 18 Apr 1 10:17 .bash_logout
134360297 -rw-r--r--. 1 root root 193 Apr 1 10:17 .bash_profile
134360298 -rw-r--r--. 1 root root 231 Apr 1 10:17 .bashrc

/etc/login.defs

配合/etc/passwd和/etc/shadow来对用户进行一些限制 但是优先级低于/etc/passwd和/etc/shadow.

如果有冲突的地方,系统会以/etc/passwd和/etc/shadow为准.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@centos7 skel]# egrep -v '^[ ]*$|^#' /etc/login.defs
MAIL_DIR /var/spool/mail
PASS_MAX_DAYS 99999
PASS_MIN_DAYS 0
PASS_MIN_LEN 5
PASS_WARN_AGE 7
UID_MIN 1000
UID_MAX 60000
SYS_UID_MIN 201
SYS_UID_MAX 999
GID_MIN 1000
GID_MAX 60000
SYS_GID_MIN 201
SYS_GID_MAX 999
CREATE_HOME yes
UMASK 077
USERGROUPS_ENAB yes
ENCRYPT_METHOD SHA512

批量创建用户

1
newusers passwd 用户文件

用户文件:指定包含用户信息的文本文件,文件格式要与/etc/passwd相同

批量修改用户密码 chpasswd

1
echo username:passwd | chpasswd

生成密码:

  • CentOS 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# grub-crypt 是centos6中的命令, 可以对口令进行加密, 默认加密方式是 sha-512
[root@centos6 ~]# grub-crypt --help
Usage: grub-crypt [OPTION]...
Encrypt a password.

-h, --help Print this message and exit
-v, --version Print the version information and exit
--md5 Use MD5 to encrypt the password
--sha-256 Use SHA-256 to encrypt the password
--sha-512 Use SHA-512 to encrypt the password (default)

Report bugs to <bug-grub@gnu.org>.
EOF
[root@centos6 ~]# grub-crypt
Password:
Retype password:
$6$O4ufCj1vTKW/7Ko7$p9rV3m0dsEsr0ebCyOVN.togh1fVhQCCDBsoc.RkelfWnRrIm3W5vVPuB86oOKo1Yei0QKxO2MRMoX2qzHV7M1
[root@centos6 ~]# useradd -p '$6$O4ufCj1vTKW/7Ko7$p9rV3m0dsEsr0ebCyOVN.togh1fVhQCCDBsoc.RkelfWnRrIm3W5vVPuB86oOKo1Yei0QKxO2MRMoX2qzHV7M1' test
[root@centos6 ~]# getent shadow test
test:$6$O4ufCj1vTKW/7Ko7$p9rV3m0dsEsr0ebCyOVN.togh1fVhQCCDBsoc.RkelfWnRrIm3W5vVPuB86oOKo1Yei0QKxO2MRMoX2qzHV7M1:18475:0:99999:7:::

  • CentOS 7
1
2
3
4
5
# CentOS 7中没有直接命令能加密sha-512口令, 可以使用python
[root@centos7 ~]#python -c 'import
crypt,getpass;pw="magedu";print(crypt.crypt(pw))'
$6$pt0SFMf6YqKea3mh$.7Hkslg17uI.Wu7BcMJStVVtkzrwktXrOC8DxcMFC4JO1igrqR7VAi87H5PH
OuLTUEjl7eJqKUhMT1e9ixojn1
  • CentOS 8
1
2
3
4
5
6
7
8
# -6 表示sha-512加密方式, CentOS7中只有-1(md5)加密方式
[root@centos8 ~]# whatis passwd
openssl-passwd (1ssl) - compute password hashes
passwd (1) - update user's authentication tokens
passwd (5) - password file
[root@centos8 ~]# man openssl-passwd
[root@centos8 ~]# openssl passwd -6 123456
$6$vQQo/2Ie/NqmeTqp$o6kCsDtZwNtW7rLh/LEk8yA26sYW4Kmja7uk/pLp..cNe77btZf3tRxeqtSwgoGKzc5GgXJT9NIpXNShQ9L7r0
  • Ubuntu
1
2
3
4
5
6
7
8
9
10
[root@ubuntu1804 ~]#echo wang:centos |chpasswd
[root@ubuntu1804 ~]#passwd wang <<EOF
> centos
> centos
> EOF
Enter new UNIX password: Retype new UNIX password: passwd: password updated
successfully
[root@ubuntu1804 ~]#echo -e 'magedu\nmagedu' | passwd wang
Enter new UNIX password: Retype new UNIX password: passwd: password updated
successfully

修改用户 usermod

修改用户属性, 不过用户一旦创建完了, 很少会再修改, 所以usermod几乎不用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 格式
usermod [OPTION] login
# 常见选项
-u UID: 新UID
-g GID: 新主组
-G GROUP1[,GROUP2,...[,GROUPN]]]:新附加组,原来的附加组将会被覆盖;若保留原有,则要同时使用-a选项
-s SHELL:新的默认SHELL
-c 'COMMENT':新的注释信息
-d HOME: 新家目录不会自动创建;若要创建新家目录并移动原家数据,同时使用-m选项
-l login_name: 新的名字
-L: lock指定用户,在/etc/shadow 密码栏的增加 !
-U: unlock指定用户,将 /etc/shadow 密码栏的 ! 拿掉
-e YYYY-MM-DD: 指明用户账号过期日期
-f INACTIVE: 设定非活动期限,即宽限期

删除用户 userdel

1
2
3
4
5
# 格式
userdel [OPTIONS]... Login
# 常见选项
-f --force 强制
-r --remove 删除用户家目录和邮箱

查看用户相关ID信息

1
2
3
4
5
6
7
8
# id [OPTION]... [USER]
# -u
# -g
# -G
[root@centos8 ~]# id -u
0
[root@centos8 ~]# id -u lujinkai
1000

切换用户 su

su 即 switch user

两种切换的方式:

  • su UserName: 非登录式切换, 即不会读取目标用户的配置文件,不改变当前工作目录,即不完全切换
  • su - UserName: 登录式切换, 会读取目标用户的配置文件,切换至自已的家目录,即完全切换

注意:su 切换新用户后,使用 exit 退回至旧的用户,而不要再用 su 切换至旧用户,否则会生成很多的bash子进程,环境可能会混乱。

修改用户密码 passwd

1
passwd [OPTIONS] UserName
1
2
3
4
5
6
7
8
9
10
-d:删除指定用户密码
-l:锁定指定用户
-u:解锁指定用户
-e:强制用户下次登录修改密码
-f:强制操作
-n mindays:指定最短使用期限
-x maxdays:最大使用期限
-w warndays:提前多少天开始警告
-i inactivedays:非活动期限
--stdin:从标准输入接收用户密码,Ubuntu无此选项

修改用户密码策略 chage

1
chage [OPTION]... LOGIN
1
2
3
4
5
6
7
-d LAST_DAY #更改密码的时间
-m --mindays MIN_DAYS
-M --maxdays MAX_DAYS
-W --warndays WARN_DAYS
-I --inactive INACTIVE #密码过期后的宽限期
-E --expiredate EXPIRE_DATE #用户的有效期
-l 显示密码策略

用户相关的其他命令

  • chfn 指定个人信息, 这个命令修改的是/etc/passwd的第5个字段
  • chsh 指定shell, 相当于usermod -s
  • flnger 可以查看用户个人信息

这几个命令用的不多, 了解即可, flnger命令在CentOS8中已经没有了

创建组

1
groupadd [OPTIONS]... group_name
  • -g GID : 指明GID
  • -r : 创建系统组, CentOS 6之前: ID<500,CentOS 7以后: ID<1000

范例: groupadd -g 48 -r apache

修改组

groupmod 修改组的属性, 用的不多

1
groupmod [OPTIONS]... group
  • -n group_name : 新名字
  • -g GID : 新GID

删除组 groupdel

1
groupdel [options] GROUP

只有当组下没有用户后, 才能删除成功

更改组密码 和 添加删除附加组成员

gpasswd 命令, 可以更改密码, 也可以修改附加组的成员关系。

1
gpasswd [OPTIONS] GROUP
  • -a user : 将user添加到指定组中

  • -d user : 从指定附加组中移除用户user

  • -M user1,user2,user3… : 设置组成员列表, 会覆盖已有的组成员列表

  • -A user1, user2, user3 … : 设置有组管理员列表

    设置管理员的之前, 这些用户必须已经在组内了, 管理员有权限往组内添加用户

临时切换主组

newgrp 把用户的主组临时切换到附加组

这个命令用的不多, 了解一下就行

更改和查看组成员

  • groupmems 可以管理附加组的成员关系 (只有root用户可以执行这个命令)
1
2
3
4
5
6
7
8
# 格式
groupmems [options] [action]
# 常见选项
-g, --group groupname #更改为指定组 (只有root)
-a, --add username #指定用户加入组
-d, --delete username #从组中删除用户
-p, --purge #从组中清除所有成员
-l, --list #显示组成员列表

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@centos7 ~]# getent group lujinkai
lujinkai:x:1000:lujinkai
[root@centos7 ~]# groupmems -g lujinkai -a test
[root@centos7 ~]# getent group lujinkai
lujinkai:x:1000:lujinkai,test
[root@centos7 ~]# groupmems -g lujinkai -l
lujinkai test
[root@centos7 ~]# groupmems -g lujinkai -d test
[root@centos7 ~]# getent group lujinkai
lujinkai:x:1000:lujinkai
[root@centos7 ~]# groupmems -g lujinkai -p
[root@centos7 ~]# getent group lujinkai
lujinkai:x:1000:
  • groups 可以查看用户所属组的列表
1
2
3
# test用户所属的组有两个 test 和 lujinkai, test是主组 lujinkai是附加组
[root@centos7 ~]# groups test
test : test lujinkai

练习

1.创建用户gentoo,附加组为bin和root,默认shell为/bin/csh,注释信息为”Gentoo Distribution”

1
2
3
4
5
[root@centos7 ~]# useradd gentoo -G bin,root -s /bin/csh -c "Gentoo Distribution"
[root@centos7 ~]# getent passwd gentoo
gentoo:x:1003:1003:Gentoo Distribution:/home/gentoo:/bin/csh
[root@centos7 ~]# groups gentoo
gentoo : gentoo root bin

2.创建下面的用户、组和组成员关系:
名字为webs 的组
用户nginx,使用webs 作为附加组
用户varnish,使用webs 作为附加组
用户mysql,不可交互登录系统,且不是webs 的成员,nginx,varnish,mysql密码都是magedu

实验目的:搭建DNS实现internet dns架构

环境要求

需要8台主机

1
2
3
4
5
6
7
8
DNS客户端:10.0.0.6/24
本地DNS服务器(只缓存):10.0.0.8/24
转发目标DNS服务器:10.0.0.18/24
根DNS服务器:10.0.0.28/24
org域DNS服务器:10.0.0.38/24
magedu.org域主DNS服务器:10.0.0.48/24
magedu.org域从DNS服务器:10.0.0.58/24
www.magedu.org的WEB服务器:10.0.0.68/24

逻辑

客户端设置dns为本地dns服务器,本地dns服务器负责将所有dns解析请求转发到转发目标dns服务器,转发目标DNS服务器添加根dns服务器的ip到named.ca,根dns服务器将将解析org的dns请求转发到org域DNS服务器,org域DNS服务器将解析magedu.org的dns请求转发到magedu.org域DNS服务器(主从),magedu.org域DNS服务器将解析www.magedu.org的dns请求转发到目标**WEB服务器**,WEB服务器返回请求的数据

实现步骤

环境要求

1
2
3
4
5
6
7
DNS主服务器:10.0.0.175
WEB服务器1:10.0.0.8
WEB服务器2:10.0.0.7
WEB服务器3:10.0.0.6
客户端1:10.0.0.57
客户端2:10.0.0.79
客户端3:10.0.0.50

逻辑

在DNS主服务器中,设置三个acl,分别对应三台客户端的ip,设置三个view,分别服务三个acl,也建立三套zone数据库,当不同的客户端都发送 www.magedu.local 请求,根据不同的zone数据库,会被解析到不同的WEB服务器

步骤

实验目的:搭建DNS转发(缓存)服务器

环境要求:需要四台主机

1
2
3
4
DNS只缓存服务器:10.0.0.79
DNS主服务器:10.0.0.175
web服务器:10.0.0.8
DNS客户端:10.0.0.67

逻辑:客户端nameserver设置为DNS缓存服务器的ip,NDS缓存服务器负责将NDS请求转发至DNS主服务器,并将返回结果缓存

实现步骤

1. 实现转发(只缓存)DNS服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
yum install bind -y

vim /etc/named.conf
#注释掉两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };

# 全局转发
forward first;
forwarders { 10.0.0.175;};

#关闭dnsec功能
dnssec-enable no;
dnssec-validation no;

systemctl start named #第一次启动服务
rndc reload #不是第一次启动服务

2. 实现主DNS服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yum install bind -y
vim /etc/named.conf
# 注释掉两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };

vim /etc/named.rfc1912.zones
# 设置域
zone "magedu.local" {
type master;
file "magedu.local.zone";
};

vim /var/named/magedu.local.zone
$TTL 1D
@ IN SOA master admin.magedu.org. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
master A 10.0.0.175
www A 10.0.0.8

3. web服务器配置(参看前面案例,略)

4. 在客户端测试

1
2
3
4
5
6
7
8
9
# 修改dns服务器
[root@centos7 ~]$cat /etc/resolv.conf
# Generated by NetworkManager
search magedu.org
nameserver 10.0.0.79

# 测试
dig www.magedu.local
curl www.magedu.local

实现父域DNS服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vim /etc/named.conf
#注释掉下面两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };
#只允许从服务器进行区域传输
allow-transfer { 从服务器IP;};
#关闭加密验证
dnssec-enable no;
dnssec-validation no;

vim /etc/named.rfc1912.zones
#加上这段
zone "magedu.local" {
type master;
file "magedu.org.local";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vim /var/named/magedu.local.zone
$TTL 1D
@ IN SOA master admin.magedu.org. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
shanghai NS shanghains
master A 10.0.0.175
www A 10.0.0.8
slave A 10.0.0.6
shanghains A 10.0.0.18

# systemctl start named 第一次启动服务
# rndc reload 不是第一次启动服务

实现子域DNS服务器

1
2
3
4
5
6
7
8
9
10
11
vim /etc/named.conf
#注释掉下面两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };
allow-transfer { none;};

vim /etc/named.rfc1912.zones
zone "shanghai.magedu.local" {
type master;
file "shanghai.magedu.local.zone";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vim /var/named/shanghai.magedu.local.zone
$TTL 1D
@ IN SOA master admin.magedu.org. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
master A 10.0.0.18
www A 10.0.0.8 ; www.shanghai.magedu.local

# systemctl start named 第一次启动服务
# rndc reload 不是第一次启动服务

客户端测试

1
dig www.shanghai.magedu.org

实验目的

搭建DNS主从服务器架构,实现DNS服务冗余

环境要求

需要四台主机
DNS主服务器:10.0.0.175
DNS从服务器:10.0.0.79
web服务器:10.0.0.8
DNS客户端:10.0.0.57

前提准备

关闭SElinux
关闭防火墙
时间同步

实现步骤

主DNS服务器配置

参照前面案例的配置,修改只有两处:allow-transfer 和 slave

1. 配置 /etc/named.conf:

1
2
3
4
5
6
7
8
# allow-transfer { 从服务器IP;};	只允许从服务器进行区域传输
# 同步全部,将配置写入option{};同步区域,将配置写入zone "ZONE_NAME" {}

options {
...
allow-transfer {10.0.0.79;};
...
}

1.1 配置 /etc/named.rfc1912.zones

1
2
3
4
zone "magedu.local" IN {
type master;
file "magedu.local.zone";
};

2. 修改DNS区域数据库文件 /var/named/magedu.org.zone

1
2
3
4
5
6
7
8
9
10
11
$TTL 1D
@ IN SOA master admin.magedu.org. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
master A 10.0.0.175
www A 10.0.0.8
slave A 10.0.0.6

3. 检查并重启DNS主服务器

1
2
3
4
5
6
[root@centos8 ~]$named-checkconf 
[root@centos8 slaves]$named-checkzone magedu.loacl /var/named/magedu.local.zone
zone magedu.loacl/IN: loaded serial 0
OK
[root@centos8 ~]$rndc reload
server reload successful

从DNS服务器配置

1
yum install bind -y

修改 /etc/named.conf

1
2
3
4
5
6
7
8
options {
...
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };
#不允许其它主机进行区域传输
allow-transfer { none;};
...
}

配置 /etc/named.rfc1912.zones

1
2
3
4
5
zone "magedu.local" IN {
type slave;
masters {10.0.0.175;};
file "slaves/magedu.local.slave";
};

检查并启动

1
2
3
4
[root@centos6 ~]$named-checkconf
[root@centos6 slaves]$service named start
Generating /etc/rndc.key: [ OK ]
Starting named: [ OK ]

查看是否生成magedu.local.slave,注意,DNS主从服务器都要配置正确才能生成,一开始DNS主服务器的allow-transfer配置错了从DNS从服务器的地址,就怎么也生成不了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@centos6 named]$ll /var/named/slaves/magedu.local.slave 
-rw-r--r-- 1 named named 361 Sep 17 20:29 /var/named/slaves/magedu.local.slave
[root@centos6 named]$cat !$
cat /var/named/slaves/magedu.local.slave
$ORIGIN .
$TTL 86400 ; 1 day
magedu.local IN SOA master.magedu.local. admin.magedu.org. (
0 ; serial
86400 ; refresh (1 day)
3600 ; retry (1 hour)
604800 ; expire (1 week)
10800 ; minimum (3 hours)
)
NS master.magedu.local.
$ORIGIN magedu.local.
master A 10.0.0.175
slave A 10.0.0.6
www A 10.0.0.8

客户端测试主从DNS服务架构

1
2
3
4
5
6
7
8
9
10
# 添加DNS从服务器ip到/etc/resolv.conf
[root@centos7 ~]$cat /etc/resolv.conf
# Generated by NetworkManager
search magedu.org
nameserver 10.0.0.175
nameserver 10.0.0.79

# 测试,将DNS主服务器关掉服务,只保留从DNS从服务器服务,或者关掉DNS从服务器服务,只保留DNS主服务器服务,结果是都能访问web服务器的http服务,只有DNS主从服务器都关掉服务,才无法访问web服务器的http服务
[root@centos7 ~]$curl www.magedu.local
www.magedu.local

实验目的

搭建DNS正向主服务器,实现web服务器基于FQDN的访问

环境要求

需要三台主机
DNS服务端:10.0.0.175
web服务器:10.0.0.8
DNS客户端:10.0.0.57

提前准备

关闭SElinux
关闭防火墙
时间同步

实现步骤

1. 在DNS服务端安装bind

1
[root@centos8 ~]$yum -y install bind

2. 修改bind 配置文件

修改配置文件 /etc/named.conf

1
2
3
#注释掉下面两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };

修改配置文件 vim /etc/named.rfc1912.zones

1
2
3
4
5
#加上下面内容, IN 可以省略
zone "magedu.local" IN {
type master;
file "magedu.local.zone";
};

3. DNS区域数据库文件

在主目录(默认是/var/named/)下新建 DNS区域数据库文件 magedu.local.zone

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
#如果没有加-p选项,需要修改所有者或权限。chgrp named magedu.local.zone
[root@centos8 ~]$cd /var/named/
[root@centos8 named]$cp -p named.localhost magedu.local.zone
[root@centos8 named]$ll
total 32
drwxrwx--- 2 named named 4096 Sep 17 22:55 data
drwxrwx--- 2 named named 4096 Sep 17 21:49 dynamic
-rw-r----- 1 root named 152 Jul 7 22:14 magedu.local.zone
-rw-r----- 1 root named 2253 Jul 7 22:14 named.ca
-rw-r----- 1 root named 152 Jul 7 22:14 named.empty
-rw-r----- 1 root named 152 Jul 7 22:14 named.localhost
-rw-r----- 1 root named 168 Jul 7 22:14 named.loopback
drwxrwx--- 2 named named 4096 Jul 7 22:14 slaves
[root@centos8 named]$vim magedu.local.zone
# 修改magedu.local.zone为以下内容:
$TTL 1D
@ IN SOA master admin.magedu.org. ( ;master 会自动补全 master.magedu.local
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS master
master A 10.0.0.175
www A 10.0.0.8

4. 检查配置文件和数据库文件格式,并启动服务

1
2
3
4
5
6
7
8
[root@centos8 named]$named-checkconf
[root@centos8 named]$named-checkzone magedu.local.zone /var/named/magedu.local.zone
zone magedu.local.zone/IN: loaded serial 0
OK
# systemctl start named 第一次启动服务
# rndc reload 不是第一次启动服务
[root@centos8 named]$rndc reload
server reload successful

5. 实现WEB服务

在web服务器10.0.0.8执行以下命令:

1
echo 'www.magedu.local' > /var/www/html/index.html

6. 在客户端实现测试

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
# 添加DNS从服务器ip到/etc/resolv.conf
[root@centos7 ~]$cat /etc/resolv.conf
# Generated by NetworkManager
search magedu.org
nameserver 10.0.0.175

# dig
[root@centos7 ~]$dig www.magedu.local

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.6 <<>> www.magedu.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48252
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.magedu.local. IN A

;; ANSWER SECTION:
www.magedu.local. 86400 IN A 10.0.0.8

;; AUTHORITY SECTION:
magedu.local. 86400 IN NS master.magedu.local.

;; ADDITIONAL SECTION:
master.magedu.local. 86400 IN A 10.0.0.175

;; Query time: 1 msec
;; SERVER: 10.0.0.175#53(10.0.0.175)
;; WHEN: Thu Sep 17 19:11:55 CST 2020
;; MSG SIZE rcvd: 98

# 能ping通
[root@centos7 ~]$ping www.magedu.local
PING www.magedu.local (10.0.0.8) 56(84) bytes of data.
64 bytes from 10.0.0.8 (10.0.0.8): icmp_seq=1 ttl=64 time=0.549 ms
64 bytes from 10.0.0.8 (10.0.0.8): icmp_seq=2 ttl=64 time=0.664 ms
64 bytes from 10.0.0.8 (10.0.0.8): icmp_seq=3 ttl=64 time=0.838 ms
^C
--- www.magedu.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.549/0.683/0.838/0.122 ms

# 能访问
[root@centos7 ~]$curl www.magedu.local
www.magedu.local

查看web服务器查的http访问记录:

1
2
3
[root@centos8 httpd]$cat /dev/null > access_log 
[root@centos8 httpd]$tail -f access_log
10.0.0.57 - - [17/Sep/2020:07:47:55 -0400] "GET / HTTP/1.1" 200 17 "-" "curl/7.29.0"

DNS相关概念和技术

DNS查询类型

  • 递归查询:一般客户机和本地DNS服务器之间属于递归查询。如果本地DNS服务器本身不能解析,则本地DNS服务器向其他DNS服务器发起发出查询请求。
  • 迭代查询:一般情况下(有例外)本地的DNS服务器向其它DNS服务器的查询属于迭代查询。如果其他DNS服务器也不能解析,则本地DNS服务器再向下一个DNS服务器发出查询请求。

名称服务器

Name Server:域内负责解析本域内的名称的DNS服务器

完整的查询请求经过的流程

1
Client -->hosts文件 --> Client DNS Service Local Cache --> DNS Server (递归) --> DNS Server Cache -->DNS iteration(迭代) --> 根--> 顶级域名DNS-->二级域名DNS…

DNS服务器类型

  • 主DNS服务器

    管理和维护所负责解析的域内解析库的服务器

  • 从DNS服务器

    从主服务器或从服务器“复制”(区域传输)解析库副本。

    • 序列号:解析库版本号,主服务器解析库变化时,其序列递增
    • 刷新时间间隔:从服务器从主服务器请求同步解析的时间间隔
    • 重试时间间隔:从服务器请求同步失败时,再次尝试时间间隔
    • 过期时长:从服务器联系不到主服务器时,多久后停止服务
    • 通知机制:主服务器解析库发生变化时,会主动通知从服务器
  • 缓存DNS服务器(转发器)

区域传输

  • 完全传输:传送整个解析库
  • 增量传输:传递解析库变化的那部分内容

解析类型

  • FQDN( Fully Qualified Domain Name) –> IP:正向解析
  • IP –> FQDN:反向解析

注意:正反向解析是两个不同的名称空间,是两棵不同的解析树

负责本地域名的正向和反向解析库

  • 正向区域
  • 反向区域

解析答案

  • 肯定答案:存在对应的查询结果
  • 否定答案:请求的条目不存在等原因导致无法返回结果
  • 权威答案:直接由存有此查询结果的DNS服务器(权威服务器)返回的答案
  • 非权威答案:由其它非权威服务器返回的查询答案

资源记录定义格式

1
name [TTL] IN rr_type value
  • name:zone的名称,是一个FQDN,通常使用@来表示;zone的名称可以定义变量$ORIGIN来表示,优先继承$ORIGIN
  • TTL:可从全局继承
  • IN:表示记录类型属于Internet类别
  • rr_type:resource record 资源记录
  • value:根据资源记录类型的不同,value也不同

同一个名字可以通过多条记录定义多个不同的值;此时DNS服务器会以轮询方式响应

同一个值也可能有多个不同的定义名字;通过多个不同的名字指向同一个值进行定义;此仅表示通过多个不同的名字可以找到同一个主机

各种资源记录

参考:https://blog.csdn.net/qq_26711103/article/details/82873550

区域解析库:由众多资源记录RR(Resource Record)组成

SOA

Start Of Authority:起始授权记录,一个区域解析库有且仅能有一个SOA记录,必须位于解析库的第一条记录

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
; zone file fragment for mytest.cn
;$TTL 600
$ORIGIN mytest.cn.
; SOA record
; owner-name ttl class rr name-server email-addr (sn ref ret ex min)
@ IN SOA ns1.mytest.cn. root.mytest.cn. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum

# M:分钟;H:小时;D:天;W:周
  • owner-name:当前域的名称,通常用 @ 来表示,优先继承$ORIGIN

  • value:可以设置多个值,本例中设置了三个值,依次为:

    1. name-server:当前域的主DNS服务器的FQDN,形式为xxx.ower-name,ower-name可以省略,例如这里ns1.mytest.cn.还可以写成ns1,下面得配合一条A记录把它解析成ip

    2. email-addr:负责此区域的人员的电子邮件地址,因为@在这里有特殊意义,所以用.替代。所以上面的root.mytest.cn.实际上是root.mytest@cn.

    3. 主从服务区域传输相关定义

      1. serial
        序列号 – Serial,每次变更区域内容时数值+1,以通知slave同步数据。值范围1 ~ 4294967295,最大增量 2147483647
      2. refresh
        更新频率 – Refresh,slave主动向master更新。建议 1200 ~ 43200 秒
      3. retry
        重试时间 – Retry,当slave同步数据失败,多少时间内会再次重试同步。典型值为180(3分钟)至900(15分钟)或更高。
      4. expire
        失效时间(Expire),一直尝试的失败时间,持续到这个设定值,指示区域数据不再具有权威性。建议 1209600 ~ 2419200 秒 (2-4 weeks)
      5. minimum
        bind9开始将此值重新定义为负缓存时间。任何解析器都可以缓存 NAME ERROR = NXDOMAIN 结果的时间。允许的最大值是 3 hours (10800 seconds).

NS

Name Server,专用于标明当前区域的DNS服务器,范例:

1
2
3
4
5
# name [TTL] IN rr_type value
# name: 当前区域的名字
# value: 当前区域的某DNS服务器的名字,例如ns.magedu.org
magedu.org. IN NS ns1.magedu.org.
magedu.org. IN NS ns2.magedu.org.

注意:

  1. 相邻的两个资源记录的name相同时,后续的可省略
  2. 对NS记录而言,任何一个ns记录后面的服务器名字,都应该在后续有一个A记录
  3. 一个区域可以有多个NS记录

MX

Mail eXchanger,邮件交换器,范例:

1
2
3
4
5
6
7
# name [TTL] IN rr_type value
# name: 当前区域的名字
# value: 当前区域的某邮件服务器(smtp服务器)的主机名
magedu.org. IN MX 10 mx1.magedu.org.
IN MX 20 mx2.magedu.org.
mx1 A 10.0.0.100
mx2 A 10.0.0.200

注意:

  1. 一个区域内,MX记录可有多个;但每个记录的value之前应该有一个数字(0-99),表示此服务器的优先级;数字越小优先级越高
  2. 对MX记录而言,任何一个MX记录后面的服务器名字,都应该在后续有一个A记录

A

internet Address,作用:FQDN –> IP,范例:

1
2
3
4
5
6
7
8
9
10
# name: 某主机的FQDN,例如:www.magedu.org.
# value: 主机名对应主机的IP地址
# 避免用户写错名称时给错误答案,可通过泛域名解析进行解析至某特定地址
www.magedu.org. IN A 1.1.1.1
www.magedu.org. IN A 2.2.2.2
mx1.magedu.org. IN A 3.3.3.3
mx2.magedu.org. IN A 4.4.4.4
$GENERATE 1-254 HOST$ IN A 1.2.3.$
*.magedu.org. IN A 5.5.5.5
magedu.org. IN A 6.6.6.6

AAAA

FQDN –> IPv6

1
2
name: FQDN
value: IPv6

PTR

PoinTeR,IP –> FQDN,范例:

1
2
3
4
5
# name: IP,有特定格式,把IP地址反过来写,1.2.3.4,要写作4.3.2.1;而有特定后缀:in-addr.arpa.,所以完整写法为:4.3.2.1.in-addr.arpa.
# value: FQDN
4.3.2.1.in-addr.arpa. IN PTR www.magedu.org.
# 网络地址及后缀可省略;主机地址依然需要反着写,如1.2.3为网络地址,可简写成:
4 IN PTR www.magedu.org.

CNAME

Canonical Name,别名记录,范例:

1
2
3
# name: 别名的FQDN
# value: 真正名字的FQDN
www.magedu.org. IN CNAME websrv.magedu.org.

TXT

对域名进行标识和说明的一种方式,一般做验证记录时会使用此项,如:SPF(反垃圾邮件)记录,https验证等。范例:

1
_dnsauth TXT 2012011200000051qgs69bwoh4h6nht4n1h0lr038x

子域授权

每个域的名称服务器,都是通过其上级名称服务器在解析进行授权。

glue record:粘合记录,父域授权子域的记录。

范例:

1
2
3
4
.com. IN NS ns1.com.
.com. IN NS ns2.com.
ns1.com. IN A 2.2.2.1
ns2.com. IN A 2.2.2.

互联网域名

注册,备案,解析。。。

DNS软件bind

DNS服务器软件:bind、powerdns、dnsmasq、unbound、coredns

bind相关程序包

  • bind:服务端
  • bind-libs:相关库
  • bind-utils:客户端
  • bind-chroot:安全包,默认将dns相关文件放至/var/named/chroot
1
[root@centos8 ~]#dnf -y install bind bind-utils

bind相关文件

  • bind主程序:/usr/sbin/named。bind的主程序是named,不同名,有点小奇怪
  • 服务脚本和Unit名称:/etc/rc.d/init.d/named、/usr/lib/systemd/system/named.service
  • 主配置文件:/etc/named.conf、etc/named.rfc1912.zones、/etc/rndc.key
  • 管理工具:/usr/sbin/rndc:remote name domain controller,默认与bind安装在同一主机,且只能通过127.0.0.1连接name进程,提供辅助性的管理功能,953/tcp
  • 解析库文件:/var/named/ZONE_NAME.ZONE
    • 一台物理服务器可同时为多个区域提供解析
    • 必须要有根区域文件:named.ca
    • 应该有两个(如果是ipv6,则更多)实现localhost和本地回环地址的解析库

主配置文件

  • 全局配置:options {}

  • 日志子系统配置:logging {}

  • 区域配置:本机能够为哪些zone进行解析,就要被定义哪些zone

    1
    zone “ZONE_NAME” IN {}

注意:

  • 任何服务器程序如果期望能够通过网络被其他主机访问,至少应该监听在一个能与外部主机通信的IP地址上
  • 缓存名称服务器配置:监听外部地址即可
  • dnssec:建议关闭dnssc,设为no

实现DNS服务器

主DNS服务器配置

1. 定义区域

主DNS服务器配置文件:/etc/named.conf,示例:

1
2
3
4
5
6
7
8
9
#注释掉下面两行
// listen-on port 53 { 127.0.0.1; };
// allow-query { localhost; };

# 添加区域
zone "ZONE_NAME" IN {
type {master| slave | hint | forward};
file "ZONE_NAME.zone";
};
  • listen-on port:监听的地址和端口。注释掉和将127.0.0.1;修改为localhost;效果是一样的,设置127.0.0.1则只会监听地址127.0.0.1,而注释掉或者设置为localhost则监听所有本机地址
  • allow-query:允许谁向此DNS进行查询。注释掉和修改localhost;any;效果是一样的,设置localhost则允许本机的ip地址列表

2. 定义区域解析库文件

默认位置:/var/named/目录下;

内容包括两部分:宏定义 和 资源记录,范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$TTL 86400
$ORIGIN magedu.org.
@ IN SOA ns1.magedu.org. admin.magedu.org (
2015042201
1H
5M
7D
1D )
IN NS ns1
IN NS ns2
IN MX 10 mx1
IN MX 20 mx2
ns1 IN A 172.16.100.11
ns2 IN A 172.16.100.12
mx1 IN A 172.16.100.13
mx2 IN A 172.16.100.14
websrv IN A 172.16.100.11
websrv IN A 172.16.100.12
www IN CNAME websrv

3. 主配置文件语法检查

1
[root@centos8 ~]$named-checkconf

4. 主配置文件语法检查

1
named-checkzone [options] zonename filename
1
[root@centos8 ~]$named-checkzone "magedu.org" /var/named/magedu.org.zone

5. 配置生效

三种方式:

  • rndc reload
  • systemctl reload named
  • service named reload

测试和管理工具

dig

dig只用于测试dns系统,不会查询hosts文件进行解析

1
dig [-t type] name [@SERVER] [query options]
  • -t:要查询的资源记录类型。默认A,除了AAAA、NS这一类,还可以是 axfr(全区域传输) 和 ixfr(增量区域传输)。

  • query options:

    +[no]trace:跟踪解析过程 : dig +trace magedu.org
    +[no]recurse:进行递归解析

host
1
host [-t type] name [SERVER]

范例:

1
2
3
4
5
host -t NS magedu.org 172.16.0.1
host -t soa magedu.org
host -t mx magedu.org
host -t axfr magedu.org
host 1.2.3.4
nslookup

nslookup 可以支持交互和非交互式两种方式执行

1
nslookup [-option] [name | -] [server]

交互模式:

1
2
3
4
nslookup>
server IP: 指明使用哪个DNS server进行查询
set q=RR_TYPE: 指明查询的资源记录类型
NAME: 要查询的名称
rndc

利用rndc工具可以实现管理DNS功能,rndc 监听端口: 953/tcp

1
rndc COMMAND
  • status: 查看状态
  • reload: 重载主配置文件和区域解析库文件
  • reload zonename: 重载区域解析库文件
  • retransfer zonename: 手动启动区域传送,而不管序列号是否增加
  • notify zonename: 重新对区域传送发通知
  • reconfig: 重载主配置文件
  • querylog: 开启或关闭查询日志文件/var/log/message
  • trace: 递增debug一个级别
  • trace LEVEL: 指定使用的级别
  • notrace:将调试级别设置为 0
  • flush:清空DNS服务器的所有缓存记录

实战案例:实现DNS正向服务器

允许动态更新

动态更新:可以通过远程更新区域数据库的资源记录,例如添加删除解析记录

实现动态更新,需要在指定的zone语句块中添加以下指令:

1
Allow-update {any;};

实现反向解析区域

实现从服务器

只有一台主DNS服务器,存在单点失败的问题,可以建立主DNS服务器的备份服务器,即从服务器来实现DNS服务的容错机制。从服务器可以自动和主服务器进行单向的数据同步,从而和主DNS服务器一样,也可以对外提供查询服务,但从服务器不提供数据更新服务。

DNS从服务器

  1. 应该为一台独立的名称服务器
  2. 主服务器的区域解析库文件中必须有一条NS记录指向从服务器
  3. 从服务器只需要定义区域,而无须提供解析库文件;解析库文件应该放置于/var/named/slaves/目录中
  4. 主服务器得允许从服务器作区域传送
  5. 主从服务器时间应该同步,可通过ntp进行
  6. bind程序的版本应该保持一致;否则,应该从高,主低

定义从区域

格式:

1
2
3
4
5
zone "ZONE_NAME" IN {
type slave;
masters { MASTER_IP; };
file "slaves/ZONE_NAME.zone";
};

实战案例:实现DNS从服务器

实现子域

将子域委派给其他主机管理,实现分布DNS数据库

正向解析DNS区域子域方法,很简单,只需要配置两条资源记录,一条NS,一条A,范例:

1
2
3
4
5
6
7
8
shanghai.magedu.local. 	      IN  NS  ns1.shanghai.magedu.local.
shanghai.magedu.local. IN NS ns2.shanghai.magedu.local.
zhengzhou.magedu.local. IN NS ns1.zhengzhou.magedu.local.
zhengzhou.magedu.local. IN NS ns2.zhengzhou.magedu.local.
ns1.shanghai.magedu.local. IN A 1.1.1.1
ns2.shanghai.magedu.local. IN A 1.1.1.2
ns1.zhengzhou.magedu.local. IN A 1.1.1.3
ns2.zhengzhou.magedu.local. IN A 1.1.1.4

范例:实现DNS父域和子域服务

实现DNS转发(缓存)服务器

利用DNS转发,可以将用户的DNS请求,转发至指定的DNS服务器,而非默认的根DNS服务器,并将指定服务器查询的返回结果进行缓存,提高效率

注意:被转发的服务器需要能够为请求者做递归,否则转发请求不予执行

转发方式有两种:全局转发 和 特定区域转发

全局转发:对非本机所负责解析区域的请求,全转发给指定的服务器

特定区域转发:仅转发对特定的区域的请求,比全局转发优先级高

1
2
3
4
5
# 无论全局转发还是特定区域转发,都需要关闭dnssec功能
options {
dnssec-enable no;
dnssec-validation no;
};
1
2
3
4
5
6
7
8
9
10
11
12
# 全局转发
options {
forward first|only;
forwarders { ip;};
};

# 特定区域转发
zone "ZONE_NAME" IN {
type forward;
forward first|only;
forwarders { ip;};
};
  • first:先转发至指定DNS服务器,如果无法解析查询请求,则本服务器再去根服务器查询
  • only:先转发至指定DNS服务器,如果无法解析查询请求,则本服务器将不再去根服务器查询

[实战案例:实现DNS forward(缓存)服务器](实验4:DNS forward(缓存)服务器.md)

智能DNS

智能DNS相关技术

ACL

ACL:把一个或多个地址归并为一个集合,并通过一个统一的名称调用,简单说就是对发起dns解析请求的客户端的ip进行分类,通常会根据省市地区进行分类

注意:只能先定义后使用;因此需要定义在options前面

格式:

1
2
3
4
5
6
7
8
acl acl_name {
# 可以是具体ip,也可以网段
ip;
net/prelen;
...
};
...
options {...}

bind 内置4个 acl:none、any、localhost、localnet

  • none:没有一个主机
  • any:任意主机
  • localhost:本机
  • localnet:本机的IP同掩码运算后得到的网络地址

访问控制指令

  • allow-query {}: 允许查询的主机;白名单
  • allow-transfer {}:允许区域传送的主机;白名单
  • allow-recursion {}: 允许递归的主机,建议全局使用
  • allow-update {}: 允许更新区域数据库中的内容

{}中填入的就是acl,范例:

1
allow-query { localhost; };		# 只处理来自本机的dns解析请求

view

视图,将ACL和区域数据库实现对应关系,以实现智能DNS

view格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 一个bind服务器可定义多个view,每个view只能匹配一个acl
# 客户端请求到达时,自上而下检查view中的acl
# 当请求者的ip能被acl匹配到,则使用所在view中的zone数据库,处理其dns解析请求
view VIEW_NAME {
match-clients { acl_name; }; # match-clients 只能匹配一个acl
zone "ZONE_NAME" {
type master;
file "ZONE_FILE";
};
include "/etc/named.rfc1912.zones";
};

# 来自上海的ip,则匹配到对应的zone数据库
view VIEW_NAME {
match-clients { shanghainet; }; # shanghainet是已经定义的acl
zone "magedu.local" {
type master;
file "magedu.local.zone.sh";
};
include "/etc/named.rfc1912.zones";
};
  • ACL定义在options之前,view定义在options之后
  • 一旦启用了view,所有的zone都只能定义在view中
  • 仅在允许递归请求的客户端所在view中定义根区域

实战案例:利用view实现智能DNS

借助智能DNS实现的技术

GSLB

Global Server Load Balance 全局负载均衡

GSLB是对服务器和链路进行综合判断来决定由哪个地点的服务器来提供服务,实现异地服务器群服务质量的保证

GSLB主要的目的是在整个网络范围内将用户的请求定向到最近的节点(或者区域)

GSLB分为基于DNS实现、基于重定向实现、基于路由协议实现,其中最通用的是基于DNS解析方式

CDN

  1. 用户向浏览器输入www.a.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求

  2. 网站的DNS域名解析器设置了CNAME,指向了www.a.tbcdn.com,请求指向了CDN网络中的智能DNS负载均衡系统

  3. 智能DNS负载均衡系统解析域名,把对用户响应速度最快的IP节点返回给用户;

  4. 用户向该IP节点(CDN服务器)发出请求

  5. 由于是第一次访问,CDN服务器会通过Cache内部专用DNS解析得到此域名的原web站点IP,向原站点服务器发起请求,并在CDN服务器上缓存内容

  6. 请求结果发给用户

DNS排错

1
2
SERVFAIL:The nameserver encountered a problem while processing the query.
# 可使用dig +trace排错,可能是网络和防火墙导致
1
2
NXDOMAIN:The queried name does not exist in the zone.
# 可能是CNAME对应的A记录不存在导致
1
2
REFUSED:The nameserver refused the client's DNS request due to policy restrictions.
# 可能是DNS策略导致

实战案例

综合案例:实现inertnet的DNS服务架构

引用计数

  1. 每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1
  2. 当变量引用撤掉后,计数器-1
  3. 定期检查各内存对象的计数器,如果计数器=0,表明内存对象没有被使用,该内存对象则进行销毁
  4. 垃圾回收完成

四色标记

示例:

1
2
3
4
5
<?php
$a = []; # 计数器 = 1
$a[] = &$a; # 计数器 = 2

unset($a); # 计数器 = 1

对象和数组,有循环引用的问题,针对这两种数据类型,采用四色标记法进行垃圾回收

黑色:正常数据
紫色:疑似垃圾
灰色:
白色:垃圾

  1. 将计数器 -1 过并且目前不为 0 的数组放到缓冲区,并将其标记为紫色
  2. 对缓冲区中的数组进行深度遍历,将紫色的元素标记为灰色,并计数器 -1
    注意:只对元素进行标灰、-1操作
  3. 再次深度扫描,检查灰色的元素,如果计数器不为0,将其标记为黑色并计数器+1(上一步有-1操作),如果计数器为0,将其标记为白色
  4. 扫描数组,黑色的元素从缓冲区中移除,白色的元素计数器+1并移到 to_free 列表
  5. 清除 to_free,完成垃圾回收

简单说:将疑似垃圾的数组的元素的计数器-1,=0说明为垃圾,≠0说明不是垃圾

01.prepare

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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
$ansible-playbook ./01.prepare.yml 
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [kube-master,kube-node,etcd,ex-lb,chrony] ******************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [10.0.2.1]
ok: [10.0.2.2]
ok: [10.0.1.1]
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]
ok: [10.0.2.3]

PLAY [localhost] *************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [localhost]

TASK [deploy : prepare some dirs] ******************************************************************************
changed: [localhost] => (item=/etc/ansible/.cluster/ssl)
changed: [localhost] => (item=/etc/ansible/.cluster/backup)

TASK [deploy : 本地设置 bin 目录权限] ******************************************************************************
changed: [localhost]

TASK [deploy : 读取ca证书stat信息] ******************************************************************************
ok: [localhost]

TASK [deploy : 准备CA配置文件和签名请求] ******************************************************************************
changed: [localhost] => (item=ca-config.json)
changed: [localhost] => (item=ca-csr.json)

TASK [deploy : 生成 CA 证书和私钥] ******************************************************************************
changed: [localhost]

TASK [deploy : 删除原有kubeconfig] ******************************************************************************
ok: [localhost]

TASK [deploy : 准备kubectl使用的admin证书签名请求] ******************************************************************************
changed: [localhost]

TASK [deploy : 创建admin证书与私钥] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置集群参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置客户端认证参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置上下文参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 选择默认上下文] ******************************************************************************
changed: [localhost]

TASK [deploy : 准备kube-proxy 证书签名请求] ******************************************************************************
changed: [localhost]

TASK [deploy : 创建 kube-proxy证书与私钥] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置集群参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置客户端认证参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置上下文参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 选择默认上下文] ******************************************************************************
changed: [localhost]

TASK [deploy : 准备kube-controller-manager 证书签名请求] ******************************************************************************
changed: [localhost]

TASK [deploy : 创建 kube-controller-manager证书与私钥] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置集群参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置认证参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 设置上下文参数] ******************************************************************************
changed: [localhost]

TASK [deploy : 选择默认上下文] ************************************************************************************
changed: [localhost]

TASK [deploy : 准备kube-scheduler 证书签名请求] ************************************************************************************
changed: [localhost]

TASK [deploy : 创建 kube-scheduler证书与私钥] ***********************************************************************************
changed: [localhost]

TASK [deploy : 设置集群参数] ***********************************************************************************
changed: [localhost]

TASK [deploy : 设置认证参数] ***********************************************************************************
changed: [localhost]

TASK [deploy : 设置上下文参数] ************************************************************************************
changed: [localhost]

TASK [deploy : 选择默认上下文] ************************************************************************************
changed: [localhost]

TASK [deploy : 本地创建 easzctl 工具的软连接] ************************************************************************************
changed: [localhost]

TASK [deploy : ansible 控制端创建 kubectl 软链接] ************************************************************************************
changed: [localhost]

TASK [deploy : 注册变量以判断是否容器化运行ansible控制端] ***********************************************************************************
changed: [localhost]

TASK [deploy : ansible 控制端写入环境变量$PATH] ***********************************************************************************
changed: [localhost]

TASK [deploy : ansible 控制端添加 kubectl 自动补全] ************************************************************************************
changed: [localhost]

TASK [deploy : pip install netaddr] ************************************************************************************
changed: [localhost]

PLAY [kube-master,kube-node,etcd] ***********************************************************************************

TASK [prepare : apt更新缓存刷新] ************************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]
ok: [10.0.1.1]
ok: [10.0.2.1]
ok: [10.0.2.3]
changed: [10.0.2.2]

TASK [prepare : 删除ubuntu默认安装] ************************************************************************************
changed: [10.0.1.3] => (item=ufw)
changed: [10.0.1.4] => (item=ufw)
changed: [10.0.1.2] => (item=ufw)
changed: [10.0.2.1] => (item=ufw)
changed: [10.0.1.1] => (item=ufw)
changed: [10.0.1.3] => (item=lxd)
changed: [10.0.1.4] => (item=lxd)
changed: [10.0.1.2] => (item=lxd)
changed: [10.0.2.1] => (item=lxd)
changed: [10.0.1.3] => (item=lxd-client)
changed: [10.0.1.4] => (item=lxd-client)
changed: [10.0.1.1] => (item=lxd)
changed: [10.0.1.2] => (item=lxd-client)
changed: [10.0.2.1] => (item=lxd-client)
changed: [10.0.1.3] => (item=lxcfs)
changed: [10.0.1.3] => (item=lxc-common)
changed: [10.0.1.4] => (item=lxcfs)
changed: [10.0.1.2] => (item=lxcfs)
changed: [10.0.1.1] => (item=lxd-client)
changed: [10.0.1.4] => (item=lxc-common)
changed: [10.0.1.2] => (item=lxc-common)
changed: [10.0.2.1] => (item=lxcfs)
changed: [10.0.2.1] => (item=lxc-common)
changed: [10.0.2.2] => (item=ufw)
changed: [10.0.2.3] => (item=ufw)
changed: [10.0.1.1] => (item=lxcfs)
changed: [10.0.1.1] => (item=lxc-common)
changed: [10.0.2.2] => (item=lxd)
changed: [10.0.2.3] => (item=lxd)
changed: [10.0.2.2] => (item=lxd-client)
changed: [10.0.2.3] => (item=lxd-client)
changed: [10.0.2.2] => (item=lxcfs)
changed: [10.0.2.2] => (item=lxc-common)
changed: [10.0.2.3] => (item=lxcfs)
changed: [10.0.2.3] => (item=lxc-common)

TASK [prepare : 安装 ubuntu/debian基础软件] ************************************************************************************
changed: [10.0.1.3]
changed: [10.0.2.1]
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 准备 journal 日志相关目录] ************************************************************************************
changed: [10.0.1.2] => (item=/etc/systemd/journald.conf.d)
changed: [10.0.1.3] => (item=/etc/systemd/journald.conf.d)
changed: [10.0.2.1] => (item=/etc/systemd/journald.conf.d)
changed: [10.0.1.4] => (item=/etc/systemd/journald.conf.d)
changed: [10.0.1.1] => (item=/etc/systemd/journald.conf.d)
ok: [10.0.1.3] => (item=/var/log/journal)
ok: [10.0.1.2] => (item=/var/log/journal)
ok: [10.0.2.1] => (item=/var/log/journal)
ok: [10.0.1.4] => (item=/var/log/journal)
ok: [10.0.1.1] => (item=/var/log/journal)
changed: [10.0.2.2] => (item=/etc/systemd/journald.conf.d)
changed: [10.0.2.3] => (item=/etc/systemd/journald.conf.d)
ok: [10.0.2.2] => (item=/var/log/journal)
ok: [10.0.2.3] => (item=/var/log/journal)

TASK [prepare : 优化设置 journal 日志] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 重启 journald 服务] ***********************************************************************************
changed: [10.0.1.3]
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.1]
changed: [10.0.2.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 禁用系统 swap] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]
changed: [10.0.2.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 删除fstab swap 相关配置] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.2.2]
changed: [10.0.1.1]
changed: [10.0.2.3]

TASK [prepare : 转换内核版本为浮点数] ***********************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]
ok: [10.0.2.1]
ok: [10.0.2.2]
ok: [10.0.2.3]

TASK [prepare : 设置 nf_conntrack_ipv4 模块名] ************************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]
ok: [10.0.2.1]
ok: [10.0.2.2]
ok: [10.0.2.3]

TASK [prepare : 加载内核模块] ************************************************************************************
changed: [10.0.1.2] => (item=br_netfilter)
changed: [10.0.2.1] => (item=br_netfilter)
changed: [10.0.1.3] => (item=br_netfilter)
ok: [10.0.1.1] => (item=br_netfilter)
changed: [10.0.1.4] => (item=br_netfilter)
changed: [10.0.1.2] => (item=ip_vs)
changed: [10.0.1.3] => (item=ip_vs)
changed: [10.0.2.1] => (item=ip_vs)
changed: [10.0.1.2] => (item=ip_vs_rr)
changed: [10.0.2.1] => (item=ip_vs_rr)
changed: [10.0.1.3] => (item=ip_vs_rr)
changed: [10.0.1.4] => (item=ip_vs)
changed: [10.0.1.1] => (item=ip_vs)
changed: [10.0.1.2] => (item=ip_vs_wrr)
changed: [10.0.1.3] => (item=ip_vs_wrr)
changed: [10.0.2.1] => (item=ip_vs_wrr)
changed: [10.0.1.4] => (item=ip_vs_rr)
changed: [10.0.1.1] => (item=ip_vs_rr)
changed: [10.0.1.2] => (item=ip_vs_sh)
changed: [10.0.1.4] => (item=ip_vs_wrr)
changed: [10.0.2.1] => (item=ip_vs_sh)
changed: [10.0.1.3] => (item=ip_vs_sh)
changed: [10.0.1.2] => (item=nf_conntrack_ipv4)
changed: [10.0.1.4] => (item=ip_vs_sh)
changed: [10.0.2.1] => (item=nf_conntrack_ipv4)
changed: [10.0.1.3] => (item=nf_conntrack_ipv4)
changed: [10.0.1.1] => (item=ip_vs_wrr)
changed: [10.0.1.4] => (item=nf_conntrack_ipv4)
changed: [10.0.2.2] => (item=br_netfilter)
changed: [10.0.2.3] => (item=br_netfilter)
changed: [10.0.1.1] => (item=ip_vs_sh)
changed: [10.0.2.2] => (item=ip_vs)
changed: [10.0.2.3] => (item=ip_vs)
changed: [10.0.2.2] => (item=ip_vs_rr)
changed: [10.0.2.3] => (item=ip_vs_rr)
ok: [10.0.1.1] => (item=nf_conntrack_ipv4)
changed: [10.0.2.2] => (item=ip_vs_wrr)
changed: [10.0.2.3] => (item=ip_vs_wrr)
changed: [10.0.2.2] => (item=ip_vs_sh)
changed: [10.0.2.3] => (item=ip_vs_sh)
changed: [10.0.2.2] => (item=nf_conntrack_ipv4)
changed: [10.0.2.3] => (item=nf_conntrack_ipv4)

TASK [prepare : 启用systemd自动加载模块服务] ******************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]
ok: [10.0.2.1]
ok: [10.0.1.1]
ok: [10.0.2.3]
ok: [10.0.2.2]

TASK [prepare : 增加内核模块开机加载配置] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 设置系统参数] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 生效系统参数] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 创建 systemd 配置目录] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 设置系统 ulimits] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 把SCTP列入内核模块黑名单] ******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.2.1]
changed: [10.0.1.4]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : prepare some dirs] ************************************************************************************
ok: [10.0.1.2] => (item=/usr/local/bin)
ok: [10.0.1.3] => (item=/usr/local/bin)
ok: [10.0.1.4] => (item=/usr/local/bin)
ok: [10.0.2.1] => (item=/usr/local/bin)
ok: [10.0.1.1] => (item=/usr/local/bin)
changed: [10.0.1.2] => (item=/etc/kubernetes/ssl)
changed: [10.0.1.3] => (item=/etc/kubernetes/ssl)
changed: [10.0.2.1] => (item=/etc/kubernetes/ssl)
changed: [10.0.1.4] => (item=/etc/kubernetes/ssl)
changed: [10.0.1.2] => (item=/root/.kube)
changed: [10.0.1.4] => (item=/root/.kube)
changed: [10.0.1.3] => (item=/root/.kube)
changed: [10.0.2.1] => (item=/root/.kube)
changed: [10.0.1.1] => (item=/etc/kubernetes/ssl)
ok: [10.0.2.2] => (item=/usr/local/bin)
ok: [10.0.2.3] => (item=/usr/local/bin)
changed: [10.0.2.2] => (item=/etc/kubernetes/ssl)
ok: [10.0.1.1] => (item=/root/.kube)
changed: [10.0.2.3] => (item=/etc/kubernetes/ssl)
changed: [10.0.2.2] => (item=/root/.kube)
changed: [10.0.2.3] => (item=/root/.kube)

TASK [prepare : 分发证书工具 CFSSL] ************************************************************************************
changed: [10.0.1.2] => (item=cfssl)
changed: [10.0.1.3] => (item=cfssl)
changed: [10.0.1.4] => (item=cfssl)
changed: [10.0.2.1] => (item=cfssl)
changed: [10.0.1.1] => (item=cfssl)
changed: [10.0.1.2] => (item=cfssl-certinfo)
changed: [10.0.1.3] => (item=cfssl-certinfo)
changed: [10.0.1.4] => (item=cfssl-certinfo)
changed: [10.0.2.1] => (item=cfssl-certinfo)
changed: [10.0.1.2] => (item=cfssljson)
changed: [10.0.1.3] => (item=cfssljson)
changed: [10.0.1.4] => (item=cfssljson)
changed: [10.0.2.1] => (item=cfssljson)
changed: [10.0.1.1] => (item=cfssl-certinfo)
changed: [10.0.2.2] => (item=cfssl)
changed: [10.0.2.3] => (item=cfssl)
changed: [10.0.1.1] => (item=cfssljson)
changed: [10.0.2.2] => (item=cfssl-certinfo)
changed: [10.0.2.3] => (item=cfssl-certinfo)
changed: [10.0.2.2] => (item=cfssljson)
changed: [10.0.2.3] => (item=cfssljson)

TASK [prepare : 写入环境变量$PATH] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.2.1]
changed: [10.0.1.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [prepare : 分发证书相关] ************************************************************************************
changed: [10.0.1.2] => (item=admin.pem)
changed: [10.0.1.3] => (item=admin.pem)
changed: [10.0.1.4] => (item=admin.pem)
changed: [10.0.1.2] => (item=admin-key.pem)
changed: [10.0.1.3] => (item=admin-key.pem)
changed: [10.0.1.1] => (item=admin.pem)
changed: [10.0.1.4] => (item=admin-key.pem)
changed: [10.0.1.2] => (item=ca.pem)
changed: [10.0.1.3] => (item=ca.pem)
changed: [10.0.1.4] => (item=ca.pem)
changed: [10.0.1.2] => (item=ca-key.pem)
changed: [10.0.1.1] => (item=admin-key.pem)
changed: [10.0.1.3] => (item=ca-key.pem)
changed: [10.0.1.4] => (item=ca-key.pem)
changed: [10.0.1.2] => (item=ca-config.json)
changed: [10.0.1.3] => (item=ca-config.json)
changed: [10.0.1.4] => (item=ca-config.json)
changed: [10.0.1.1] => (item=ca.pem)
changed: [10.0.1.1] => (item=ca-key.pem)
changed: [10.0.1.1] => (item=ca-config.json)

TASK [prepare : 添加 kubectl 命令自动补全] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
ok: [10.0.1.1]

TASK [prepare : 分发 kubeconfig配置文件] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
ok: [10.0.1.1]

TASK [prepare : 分发 kube-proxy.kubeconfig配置文件] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]

changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [prepare : 分发 kube-scheduler.kubeconfig配置文件] ************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

PLAY RECAP ************************************************************************************
10.0.1.1 : ok=28 changed=21 unreachable=0 failed=0 skipped=55 rescued=0 ignored=0
10.0.1.2 : ok=28 changed=23 unreachable=0 failed=0 skipped=55 rescued=0 ignored=0
10.0.1.3 : ok=26 changed=21 unreachable=0 failed=0 skipped=57 rescued=0 ignored=0
10.0.1.4 : ok=26 changed=21 unreachable=0 failed=0 skipped=57 rescued=0 ignored=0
10.0.2.1 : ok=22 changed=17 unreachable=0 failed=0 skipped=61 rescued=0 ignored=0
10.0.2.2 : ok=22 changed=18 unreachable=0 failed=0 skipped=61 rescued=0 ignored=0
10.0.2.3 : ok=22 changed=17 unreachable=0 failed=0 skipped=61 rescued=0 ignored=0
localhost : ok=37 changed=34 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0

02.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
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
$ansible-playbook ./02.etcd.yml 
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [etcd] ************************************************************************************

TASK [Gathering Facts] ************************************************************************************
ok: [10.0.2.2]
ok: [10.0.2.1]
ok: [10.0.2.3]

TASK [etcd : prepare some dirs] ***************************************************************************
ok: [10.0.2.3] => (item=/usr/local/bin)
ok: [10.0.2.1] => (item=/usr/local/bin)
ok: [10.0.2.2] => (item=/usr/local/bin)
ok: [10.0.2.3] => (item=/etc/kubernetes/ssl)
ok: [10.0.2.2] => (item=/etc/kubernetes/ssl)
ok: [10.0.2.1] => (item=/etc/kubernetes/ssl)
changed: [10.0.2.2] => (item=/etc/etcd/ssl)
changed: [10.0.2.3] => (item=/etc/etcd/ssl)
changed: [10.0.2.1] => (item=/etc/etcd/ssl)
changed: [10.0.2.2] => (item=/var/lib/etcd)
changed: [10.0.2.3] => (item=/var/lib/etcd)
changed: [10.0.2.1] => (item=/var/lib/etcd)

TASK [etcd : 下载etcd二进制文件] *********************************************************************************
changed: [10.0.2.1] => (item=etcd)
changed: [10.0.2.3] => (item=etcd)
changed: [10.0.2.2] => (item=etcd)
changed: [10.0.2.1] => (item=etcdctl)
changed: [10.0.2.3] => (item=etcdctl)
changed: [10.0.2.2] => (item=etcdctl)

TASK [etcd : 分发证书相关] ***********************************************************************************
changed: [10.0.2.1] => (item=ca.pem)
changed: [10.0.2.2] => (item=ca.pem)
changed: [10.0.2.3] => (item=ca.pem)
changed: [10.0.2.1] => (item=ca-key.pem)
changed: [10.0.2.2] => (item=ca-key.pem)
changed: [10.0.2.3] => (item=ca-key.pem)
changed: [10.0.2.1] => (item=ca-config.json)
changed: [10.0.2.3] => (item=ca-config.json)
changed: [10.0.2.2] => (item=ca-config.json)

TASK [etcd : 创建etcd证书请求] **********************************************************************************
changed: [10.0.2.2]
changed: [10.0.2.1]
changed: [10.0.2.3]

TASK [etcd : 创建 etcd证书和私钥] ********************************************************************************
changed: [10.0.2.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

TASK [etcd : 创建etcd的systemd unit文件] ***********************************************************************
changed: [10.0.2.2]
changed: [10.0.2.1]
changed: [10.0.2.3]

TASK [etcd : 开机启用etcd服务] **********************************************************************************
changed: [10.0.2.2]
changed: [10.0.2.1]
changed: [10.0.2.3]

TASK [etcd : 开启etcd服务] ************************************************************************************
changed: [10.0.2.3]
changed: [10.0.2.1]
changed: [10.0.2.2]

TASK [etcd : 以轮询的方式等待服务同步完成] *************************************************************************
changed: [10.0.2.1]
changed: [10.0.2.2]
changed: [10.0.2.3]

PLAY RECAP **************************************************************************************
10.0.2.1 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.2.2 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.2.3 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

03.docker

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
$ansible-playbook 03.docker.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [kube-master,kube-node] ***************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [10.0.1.3]
ok: [10.0.1.1]
ok: [10.0.1.2]
ok: [10.0.1.4]

TASK [docker : 获取是否已经安装docker] **********************************************************************************
changed: [10.0.1.4]
changed: [10.0.1.3]
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [docker : 获取是否已经安装containerd] ***************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]

TASK [docker : 获取docker版本信息] *****************************************************************************************
changed: [10.0.1.1]

TASK [docker : debug info] ***************************************************************************************
ok: [10.0.1.1] => {
"docker_ver": {
"changed": true,
"cmd": "/etc/ansible/bin/dockerd --version|cut -d' ' -f3",
"delta": "0:00:00.444500",
"end": "2021-01-28 17:33:03.103154",
"failed": false,
"rc": 0,
"start": "2021-01-28 17:33:02.658654",
"stderr": "",
"stderr_lines": [],
"stdout": "19.03.14,",
"stdout_lines": [
"19.03.14,"
]
}
}
ok: [10.0.1.2] => {
"docker_ver": {
"changed": true,
"cmd": "/etc/ansible/bin/dockerd --version|cut -d' ' -f3",
"delta": "0:00:00.444500",
"end": "2021-01-28 17:33:03.103154",
"failed": false,
"rc": 0,
"start": "2021-01-28 17:33:02.658654",
"stderr": "",
"stderr_lines": [],
"stdout": "19.03.14,",
"stdout_lines": [
"19.03.14,"
]
}
}
ok: [10.0.1.3] => {
"docker_ver": {
"changed": true,
"cmd": "/etc/ansible/bin/dockerd --version|cut -d' ' -f3",
"delta": "0:00:00.444500",
"end": "2021-01-28 17:33:03.103154",
"failed": false,
"rc": 0,
"start": "2021-01-28 17:33:02.658654",
"stderr": "",
"stderr_lines": [],
"stdout": "19.03.14,",
"stdout_lines": [
"19.03.14,"
]
}
}
ok: [10.0.1.4] => {
"docker_ver": {
"changed": true,
"cmd": "/etc/ansible/bin/dockerd --version|cut -d' ' -f3",
"delta": "0:00:00.444500",
"end": "2021-01-28 17:33:03.103154",
"failed": false,
"rc": 0,
"start": "2021-01-28 17:33:02.658654",
"stderr": "",
"stderr_lines": [],
"stdout": "19.03.14,",
"stdout_lines": [
"19.03.14,"
]
}
}

TASK [docker : 转换docker版本信息为浮点数] **************************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.2]
ok: [10.0.1.3]
ok: [10.0.1.4]

TASK [docker : debug info] *****************************************************************************************
ok: [10.0.1.1] => {
"DOCKER_VER": "19.03"
}
ok: [10.0.1.2] => {
"DOCKER_VER": "19.03"
}
ok: [10.0.1.3] => {
"DOCKER_VER": "19.03"
}
ok: [10.0.1.4] => {
"DOCKER_VER": "19.03"
}

TASK [docker : 准备docker相关目录] *****************************************************************************************
ok: [10.0.1.4] => (item=/usr/local/bin)
ok: [10.0.1.3] => (item=/usr/local/bin)
ok: [10.0.1.2] => (item=/usr/local/bin)
changed: [10.0.1.2] => (item=/etc/docker)
changed: [10.0.1.4] => (item=/etc/docker)
changed: [10.0.1.3] => (item=/etc/docker)

TASK [docker : 下载 docker 二进制文件(>= 18.09.x)] ****************************************************************************************
changed: [10.0.1.2] => (item=containerd)
changed: [10.0.1.4] => (item=containerd)
changed: [10.0.1.3] => (item=containerd)
changed: [10.0.1.4] => (item=containerd-shim)
changed: [10.0.1.2] => (item=containerd-shim)
changed: [10.0.1.3] => (item=containerd-shim)
changed: [10.0.1.4] => (item=docker-init)
changed: [10.0.1.2] => (item=docker-init)
changed: [10.0.1.3] => (item=docker-init)
changed: [10.0.1.4] => (item=runc)
changed: [10.0.1.3] => (item=runc)
changed: [10.0.1.2] => (item=runc)
changed: [10.0.1.4] => (item=docker)
changed: [10.0.1.2] => (item=docker)
changed: [10.0.1.3] => (item=docker)
changed: [10.0.1.3] => (item=ctr)
changed: [10.0.1.4] => (item=ctr)
changed: [10.0.1.2] => (item=ctr)
changed: [10.0.1.4] => (item=dockerd)
changed: [10.0.1.3] => (item=dockerd)
changed: [10.0.1.2] => (item=dockerd)
changed: [10.0.1.2] => (item=docker-proxy)
changed: [10.0.1.4] => (item=docker-proxy)
changed: [10.0.1.3] => (item=docker-proxy)

TASK [docker : docker命令自动补全] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]

TASK [docker : docker国内镜像加速] **********************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]

TASK [docker : flush-iptables] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]

TASK [docker : 创建docker的systemd unit文件] *****************************************************************************************
changed: [10.0.1.4]
changed: [10.0.1.3]
changed: [10.0.1.2]

TASK [docker : 开机启用docker 服务] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]

TASK [docker : 开启docker 服务] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.3]

TASK [docker : 轮询等待docker服务运行] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.3]

TASK [docker : 配置 docker 命令软链接] ****************************************************************************************
changed: [10.0.1.4]
changed: [10.0.1.2]
changed: [10.0.1.3]

TASK [docker : 下载 docker-tag] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]

PLAY RECAP *****************************************************************************************
10.0.1.1 : ok=8 changed=4 unreachable=0 failed=0 skipped=11 rescued=0 ignored=0
10.0.1.2 : ok=17 changed=13 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
10.0.1.3 : ok=17 changed=13 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
10.0.1.4 : ok=17 changed=13 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

04.kube-master

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
$ansible-playbook 04.kube-master.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [kube-master] *****************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.2]

TASK [kube-master : 下载 kube-master 二进制] *****************************************************************************************
ok: [10.0.1.2] => (item=kube-apiserver)
ok: [10.0.1.1] => (item=kube-apiserver)
ok: [10.0.1.2] => (item=kube-controller-manager)
ok: [10.0.1.1] => (item=kube-controller-manager)
ok: [10.0.1.2] => (item=kube-scheduler)
ok: [10.0.1.1] => (item=kube-scheduler)
ok: [10.0.1.2] => (item=kubectl)
ok: [10.0.1.1] => (item=kubectl)

TASK [kube-master : 创建 kubernetes 证书签名请求] *****************************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-master : 创建 kubernetes 证书和私钥] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-master : 创建 aggregator proxy证书签名请求] ************************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-master : 创建 aggregator-proxy证书和私钥] *******************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-master : 创建 basic-auth.csv] ****************************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-master : 替换 kubeconfig 的 apiserver 地址] ********************************************************************************
ok: [10.0.1.2] => (item=/root/.kube/config)
ok: [10.0.1.1] => (item=/root/.kube/config)
ok: [10.0.1.2] => (item=/etc/kubernetes/kube-controller-manager.kubeconfig)
ok: [10.0.1.1] => (item=/etc/kubernetes/kube-controller-manager.kubeconfig)
ok: [10.0.1.2] => (item=/etc/kubernetes/kube-scheduler.kubeconfig)
ok: [10.0.1.1] => (item=/etc/kubernetes/kube-scheduler.kubeconfig)

TASK [kube-master : 创建 master 服务的 systemd unit 文件] ****************************************************************************************
ok: [10.0.1.2] => (item=kube-apiserver.service)
ok: [10.0.1.1] => (item=kube-apiserver.service)
ok: [10.0.1.2] => (item=kube-controller-manager.service)
ok: [10.0.1.1] => (item=kube-controller-manager.service)
ok: [10.0.1.2] => (item=kube-scheduler.service)
ok: [10.0.1.1] => (item=kube-scheduler.service)

TASK [kube-master : enable master 服务] ******************************************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-master : 启动 master 服务] *************************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-master : 以轮询的方式等待master服务启动完成] *****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-master : 配置admin用户rbac权限] *****************************************************************************************
ok: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-master : 创建admin用户rbac权限] **********************************************************************************************
changed: [10.0.1.1]

TASK [kube-node : 创建kube-node 相关目录] ****************************************************************************************
changed: [10.0.1.2] => (item=/var/lib/kubelet)
changed: [10.0.1.2] => (item=/var/lib/kube-proxy)
ok: [10.0.1.1] => (item=/var/lib/kubelet)
changed: [10.0.1.2] => (item=/etc/cni/net.d)
ok: [10.0.1.1] => (item=/var/lib/kube-proxy)
ok: [10.0.1.1] => (item=/etc/cni/net.d)

TASK [kube-node : 下载 kubelet,kube-proxy 二进制和基础 cni plugins] ****************************************************************************************
ok: [10.0.1.2] => (item=kubectl)
ok: [10.0.1.1] => (item=kubectl)
ok: [10.0.1.1] => (item=kubelet)
ok: [10.0.1.1] => (item=kube-proxy)
ok: [10.0.1.1] => (item=bridge)
changed: [10.0.1.2] => (item=kubelet)
ok: [10.0.1.1] => (item=host-local)
ok: [10.0.1.1] => (item=loopback)
changed: [10.0.1.2] => (item=kube-proxy)
changed: [10.0.1.2] => (item=bridge)
changed: [10.0.1.2] => (item=host-local)
changed: [10.0.1.2] => (item=loopback)

TASK [kube-node : 替换 kubeconfig 的 apiserver 地址] ****************************************************************************************
ok: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 准备kubelet 证书签名请求] ****************************************************************************************
changed: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 创建 kubelet 证书与私钥] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 设置集群参数] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 设置客户端认证参数] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 设置上下文参数] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 选择默认上下文] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 准备 cni配置文件] ****************************************************************************************
changed: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 注册变量 TMP_VER] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 获取 kubernetes 主版本号] ****************************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.2]

TASK [kube-node : debug info] *****************************************************************************************
ok: [10.0.1.1] => {
"KUBE_VER": "1.2"
}
ok: [10.0.1.2] => {
"KUBE_VER": "1.2"
}

TASK [kube-node : 创建kubelet的配置文件] ****************************************************************************************
changed: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 创建kubelet的systemd unit文件] ****************************************************************************************
changed: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 开机启用kubelet 服务] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 开启kubelet 服务] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 替换 kube-proxy.kubeconfig 的 apiserver 地址] ****************************************************************************************
changed: [10.0.1.2]
ok: [10.0.1.1]

TASK [kube-node : 创建kube-proxy 服务文件] ****************************************************************************************
ok: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 开机启用kube-proxy 服务] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 开启kube-proxy 服务] ************************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 轮询等待kubelet启动] ***************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [kube-node : 轮询等待node达到Ready状态] **************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [kube-node : 设置node节点role] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [Making master nodes SchedulingDisabled] *************************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.2]

TASK [Setting master role name] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.1]

PLAY RECAP ***************************************************************************************
10.0.1.1 : ok=40 changed=21 unreachable=0 failed=0 skipped=24 rescued=0 ignored=0
10.0.1.2 : ok=39 changed=29 unreachable=0 failed=0 skipped=23 rescued=0 ignored=0

05.kube-node

06.network

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
$ansible-playbook 06.network.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [kube-master,kube-node] ****************************************************************************************

TASK [Gathering Facts] ****************************************************************************************
ok: [10.0.1.1]
ok: [10.0.1.3]
ok: [10.0.1.2]
ok: [10.0.1.4]

TASK [calico : 在节点创建相关目录] ****************************************************************************************
changed: [10.0.1.4] => (item=/etc/calico/ssl)
changed: [10.0.1.2] => (item=/etc/calico/ssl)
changed: [10.0.1.3] => (item=/etc/calico/ssl)
changed: [10.0.1.1] => (item=/etc/calico/ssl)
ok: [10.0.1.4] => (item=/etc/cni/net.d)
ok: [10.0.1.2] => (item=/etc/cni/net.d)
ok: [10.0.1.3] => (item=/etc/cni/net.d)
changed: [10.0.1.4] => (item=/opt/kube/images)
changed: [10.0.1.2] => (item=/opt/kube/images)
changed: [10.0.1.3] => (item=/opt/kube/images)
ok: [10.0.1.1] => (item=/etc/cni/net.d)
changed: [10.0.1.4] => (item=/opt/kube/kube-system)
changed: [10.0.1.2] => (item=/opt/kube/kube-system)
changed: [10.0.1.3] => (item=/opt/kube/kube-system)
changed: [10.0.1.1] => (item=/opt/kube/images)
changed: [10.0.1.1] => (item=/opt/kube/kube-system)

TASK [calico : 创建calico 证书请求] ****************************************************************************************
changed: [10.0.1.4]
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.1]

TASK [calico : 创建 calico证书和私钥] ****************************************************************************************
changed: [10.0.1.4]
changed: [10.0.1.3]
changed: [10.0.1.2]
changed: [10.0.1.1]

TASK [calico : get calico-etcd-secrets info] ****************************************************************************************
changed: [10.0.1.1]

TASK [calico : 创建 calico-etcd-secrets] ***************************************************************************************
changed: [10.0.1.1]

TASK [calico : 配置 calico DaemonSet yaml文件] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]

TASK [calico : 检查是否已下载离线calico镜像] ****************************************************************************************
changed: [10.0.1.1]

TASK [calico : 尝试推送离线docker 镜像(若执行失败,可忽略)] ****************************************************************************************
changed: [10.0.1.3] => (item=pause.tar)
changed: [10.0.1.2] => (item=pause.tar)
changed: [10.0.1.4] => (item=pause.tar)
changed: [10.0.1.1] => (item=pause.tar)
changed: [10.0.1.3] => (item=calico_v3.15.3.tar)
changed: [10.0.1.4] => (item=calico_v3.15.3.tar)
changed: [10.0.1.2] => (item=calico_v3.15.3.tar)
changed: [10.0.1.1] => (item=calico_v3.15.3.tar)

TASK [calico : 获取calico离线镜像推送情况] ****************************************************************************************
changed: [10.0.1.3]
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.1]

TASK [calico : 导入 calico的离线镜像(若执行失败,可忽略)] ****************************************************************************************
failed: [10.0.1.1] (item=pause.tar) => {"ansible_loop_var": "item", "changed": true, "cmd": "/usr/local/bin/docker load -i /opt/kube/images/pause.tar", "delta": "0:00:00.005860", "end": "2021-01-29 16:13:17.463162", "item": "pause.tar", "msg": "non-zero return code", "rc": 127, "start": "2021-01-29 16:13:17.457302", "stderr": "/bin/sh: 1: /usr/local/bin/docker: not found", "stderr_lines": ["/bin/sh: 1: /usr/local/bin/docker: not found"], "stdout": "", "stdout_lines": []}
changed: [10.0.1.2] => (item=pause.tar)
changed: [10.0.1.3] => (item=pause.tar)
failed: [10.0.1.1] (item=calico_v3.15.3.tar) => {"ansible_loop_var": "item", "changed": true, "cmd": "/usr/local/bin/docker load -i /opt/kube/images/calico_v3.15.3.tar", "delta": "0:00:00.012269", "end": "2021-01-29 16:13:17.962847", "item": "calico_v3.15.3.tar", "msg": "non-zero return code", "rc": 127, "start": "2021-01-29 16:13:17.950578", "stderr": "/bin/sh: 1: /usr/local/bin/docker: not found", "stderr_lines": ["/bin/sh: 1: /usr/local/bin/docker: not found"], "stdout": "", "stdout_lines": []}
...ignoring
changed: [10.0.1.4] => (item=pause.tar)
changed: [10.0.1.3] => (item=calico_v3.15.3.tar)
changed: [10.0.1.2] => (item=calico_v3.15.3.tar)
changed: [10.0.1.4] => (item=calico_v3.15.3.tar)

TASK [calico : 运行 calico网络] ****************************************************************************************
changed: [10.0.1.1]

TASK [calico : 删除默认cni配置] ****************************************************************************************
changed: [10.0.1.2]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.1]

TASK [calico : 下载calicoctl 客户端] ****************************************************************************************
changed: [10.0.1.3] => (item=calicoctl)
changed: [10.0.1.4] => (item=calicoctl)
changed: [10.0.1.2] => (item=calicoctl)
changed: [10.0.1.1] => (item=calicoctl)

TASK [calico : 准备 calicoctl配置文件] ****************************************************************************************
changed: [10.0.1.3]
changed: [10.0.1.2]
changed: [10.0.1.4]
changed: [10.0.1.1]

TASK [calico : 轮询等待calico-node 运行,视下载镜像速度而定] ****************************************************************************************
changed: [10.0.1.1]
changed: [10.0.1.3]
changed: [10.0.1.4]
changed: [10.0.1.2]

PLAY RECAP ****************************************************************************************
10.0.1.1 : ok=16 changed=15 unreachable=0 failed=0 skipped=49 rescued=0 ignored=1
10.0.1.2 : ok=12 changed=11 unreachable=0 failed=0 skipped=41 rescued=0 ignored=0
10.0.1.3 : ok=12 changed=11 unreachable=0 failed=0 skipped=41 rescued=0 ignored=0
10.0.1.4 : ok=12 changed=11 unreachable=0 failed=0 skipped=41 rescued=0 ignored=0

JDK

JRE:Java Runtime Environment,指Java运行时环境, 包含 JVM + Java核心类库
JDK:Java Development Kit,即 Java 语言的软件开发工具包,JDK协议基于 JRL(JavaResearch License)协议

JDK 包含 JRE

两次编译:源码 -> 字节码 -> 机器码

Oracle JDK

JDK 有很多版本,Oracle JDK是官方版,也是用的最多的

Oracle JDK 早期称为 J2SE(标准版)、J2EE(企业版)、J2ME(微型版),现在叫做 JavaEE

早期的 Oracle JDK 的版本以 1.x 的方式命令:JDK 1.0、JDK 1.1、JDK 1.2、JDK 1.3、JDK 1.4

2004年9月30日18:00PM,J2SE1.5发布,是Java语言的发展史上的又一里程碑事件。为了表示这个版本的重要性,J2SE1.5更名为J2SE5.0

2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名以取消其中的数字”2”:J2EE更名为Java EE, J2SE更名为Java SE,J2ME更名为Java ME

版本 发行日期
Java SE 5.0 (1.5.0) 2004-09-30
Java SE 6.0 (1.6.0) 2006-12-11
Java SE 7.0 (1.7.0) 2011-07-28
Java SE 8.0 (1.8.0) 2014-03-18
Java SE 9 2017-09-21
Java SE 10 2018-03-14
Java SE 11 2018-09-25
Java SE 12 2019-03-19
Java SE 13 2019-09-17
Java SE 14 2020-03-17

Open JDK

Oracle JDK 收费,OpenJDK是第三方版本的JDK中最常用的,CentOS 和 Ubuntu 都内置

OpenJDK 7是基于JDK7的beta版开发,但为了也将Java SE 6开源,从OpenJDK7的b20构建反向分支开发,从中剥离了不符合Java SE 6规范的代码,发布OpenJDK 6。所以OpenJDK6和JDK6没什么关系,只是API兼容而已

相对来说,Oracle jDK具有更好的响应能力和JVM性能,更加稳定

安装

1
# 略...

Tomcat 基础

  • tomcat 即是轻量级web服务器,可以处理 http 请求,又是 servlet 容器,可以处理 java 程序
  • tomcat 使用 java 开发,所以依赖 JRE 或者 JDK 环境

servlet

不太懂 java,servlet 可以认为类似 php-fpm,只是 servlet 性能更好,功能更加强大

浏览器访问 jsp 文件,tomcat会将 jsp 文件转为 java 文件(servlet类文件),然后把 servlet 类文件转为字节码文件(class文件)

安装

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
# 下载二进制包
[root@c71 src]$wget https://mirrors.bfsu.edu.cn/apache/tomcat/tomcat-8/v8.5.61/bin/apache-tomcat-8.5.61.tar.gz
# 无需编译,解压即可使用
[root@c71 src]$tar zxf apache-tomcat-8.5.61.tar.gz
[root@c71 src]$mv apache-tomcat-8.5.61 /usr/local/tomcat
# 创建tomcat用户
[root@c71 src]$useradd -r -s /sbin/nologin tomcat
[root@c71 src]$chown -R tomcat:tomcat /usr/local/tomcat/
[root@c71 src]$cd /usr/local/tomcat/
[root@c72 bin]$ll catalina.sh startup.sh shutdown.sh
-rwxr-x--- 1 tomcat tomcat 25121 Jan 30 17:19 catalina.sh # 管理tomcat脚本
-rwxr-x--- 1 tomcat tomcat 1902 Jan 30 17:19 shutdown.sh # 停止tomcat脚本
-rwxr-x--- 1 tomcat tomcat 1904 Jan 30 17:19 startup.sh # 启动tomcat脚本
# 提供tomcat所需的环境变量
[root@c71 tomcat]$echo "JAVA_HOME=/usr/local/jdk" >/usr/local/tomcat/conf/tomcat.conf
# 创建tomcat.service文件
[root@c71 tomcat]$vim /usr/lib/systemd/system/tomcat.service
[Unit]
Description=Tomcat
#After=syslog.target network.target remote-fs.target nss-lookup.target
After=syslog.target network.target

[Service]
Type=forking
EnvironmentFile=/usr/local/tomcat/conf/tomcat.conf
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
PrivateTmp=true
User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target
# 设置开机自启
[root@c71 tomcat]$systemctl daemon-reload
[root@c71 tomcat]$systemctl enable --now tomcat

tomcat的文件结构和组成

目录 说明
bin 服务启动、停止等相关程序和文件
conf 配置文件
lib 库目录
logs 日志目录
webapps 应用程序,应用部署目录
work jsp编译后的结果文件,建议提前预热访问

配置文件

文档:http://tomcat.apache.org/tomcat-8.5-doc/index.html

文件名 说明
server.xml 主配置文件
web.xml 每个webapp只有“部署”后才能被访问,它的部署方式通常由web.xml进行定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认部署相关的配置,每个web应用也可以使用专用配置文件,来覆盖全局文件
context.xml 用于定义所有web应用均需加载的Context配置,此文件为所有的webapps提供默认配置,每个web应用也可以使用自已专用的配置,它通常由专用的配置文件context.xml来定义,其存放位置为WEB-INF/目录中,覆盖全局的文件
tomcat-users.xml 用户认证的账号和密码文件
catalina.policy 当使用security选项启动tomcat时,用于为tomcat设置安全策略
catalina.properties Tomcat 环境变量的配置,用于设定类加载器路径,以及一些与JVM调优相关参数
logging.properties Tomcat 日志系统相关的配置,可以修改日志级别和日志路径等

注意:配置文件大小写敏感

日志

参考文档: https://cwiki.apache.org/confluence/display/TOMCAT/Logging
日志格式: https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_Logging

日志文件位于 logs 目录下,日志的格式在conf/server.xml 中定义

1
2
3
4
5
6
[root@c72 tomcat]$vim conf/server.xml 
...
164 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
165 prefix="localhost_access_log" suffix=".txt"
166 pattern="%h %l %u %t &quot;%r&quot; %s %b" />
...

组件

tomcat 内部组成:

组件 说明
Server 代表整个Tomcat容器,一台主机可以启动多tomcat实例,需要确保端口不要产生冲突。一个Server中可以有多个service,但通常就一个
Service 实现组织Engine和Connector,建立两者之间关联关系, service 里面只能包含一个Engine;简单说,Service的作用就是把Container和Engine关联起来
Connector 连接器,负责客户端的HTTP、HTTPS、AJP等协议连接。一个Connector只属于某一个Engine
Engine 引擎,真正的处理请求的入口,其内部定义多个虚拟主机Host。一个Engine上可以绑定多个Connector
Host 虚拟主机,可以实现多虚拟主机
Context 应用的上下文,配置特定url路径映射和目录的映射关系:url => directory

以下是 /conf/server.xml 的部分内容

1
2
3
4
5
6
7
8
9
10
11
<Service>
<Connector />
<Engine>
<Realm>
<Realm />
</Realm>
<Host>
<Valve />
</Host>
</Engine>
</Service>

每一个组件都由一个Java“类”实现,这些组件大体可分为以下几个类型:

1
2
3
4
5
6
顶级组件:Server
服务类组件:Service
连接器组件:http, https, ajp(apache jserv protocol)
容器类:Engine, Host, Context
被嵌套类:valve, logger, realm, loader, manager, ...
集群类组件:listener, cluster, ...

tomcat处理请求过程

假设来自客户的请求为:http://localhost:8080/test/index.jsp

  1. 浏览器端的请求被发送到服务端端口8080,Tomcat进程监听在此端口上。通过侦听的HTTP/1.1 Connector获得此请求。
  2. Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的响应
  3. Engine获得请求localhost:8080/test/index.jsp,遍历它所有虚拟主机Host
  4. Engine匹配到名为localhost的Host。如果匹配不到,就把请求交给该Engine中的defaultHost处理
  5. localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
  6. Host匹配到路径为/test的Context
  7. path=/test的Context获得请求index.jsp,在它的mapping table中寻找对应的servlet
  8. Context匹配到URL PATTERN为 *.jsp 的servlet,对应于JspServlet类构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法。
  9. Context把执行完了之后的HttpServletResponse对象返回给Host
  10. Host把HttpServletResponse对象返回给Engine
  11. Engine把HttpServletResponse对象返回给Connector
  12. Connector把HttpServletResponse对象返回给浏览器端

应用部署

默认网站根目录是 webapps,其下的每个目录都是一个 WebApp

1
2
3
4
5
6
7
8
9
[root@c72 tomcat]$pwd
/usr/local/tomcat
[root@c72 tomcat]$ll webapps/ # 默认网站根目录
total 24
drwxr-x--- 2 tomcat tomcat 4096 Jan 30 22:50 docs
drwxr-x--- 7 tomcat tomcat 4096 Jan 30 22:50 examples
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 host-manager
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 manager
drwxr-x--- 3 tomcat tomcat 4096 Jan 30 22:52 ROOT # 网站默认根目录

JSP WebApp目录结构

目录结构一般由开发用工具自动生成

1
2
3
4
5
index.html	# 主页配置:默认按以下顺序查找主页文件 index.html,index.htm、index.jsp
WEB-INF\ # 当前目录WebApp的私有资源路径,通常存储当前应用使用的web.xml和context.xml配置文件
META-INF\ # 类似于WEB-INF,也是私有资源的配置信息,和WEB-INF/目录一样浏览器无法访问
classes\ # 类文件,当前webapp需要的类
lib\ # 当前应用依赖的jar包

主页设置

  • 全局配置:修改 conf/web.xml 中的 <welcome-file-list> 标签,配置修改后,需要重启 tomcat

    1
    # 略...
  • WebApp的专用配置文件:以ROOT为例,将上面主配置文件conf/web.xml中的 <welcome-file-list> 标签,复制到 webapps/ROOT/WEB-INF/web.xml 中,配置修改后,无需重启tomcat服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@c72 tomcat]$vim webapps/ROOT/WEB-INF/web.xml
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true">
    <display-name>Welcome to Tomcat</display-name>
    <description>Welcome to Tomcat</description>
    <welcome-file-list>
    <welcome-file>index.html</welcome-file> <!-- 调整三个文件顺序 -->
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    [root@c72 tomcat]$chown -R tomcat:tomcat ./webapps/ROOT/* # 不要忘记修改属主

应用部署实现

WebApp应用的归档格式.war.jar

  • .war:WebApp打包,类zip格式文件,通常包括一个应用的所有资源,比如jsp、html、配置文件等
  • .jar:EJB类文件的打包压缩类zip格式文件,包括很多的class文件,网景公司发明

部署方式:自动部署 和 手动部署

  • 自动部署:Tomcat一旦发现多了一个web应用APP.war包,默认会自动把它解压缩,加载并启动起来

    1
    2
    # conf/server.xml中文件配置
    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
  • 手动部署:冷部署 和 热部署

    • 冷部署:将webapp放到指定目录,才去启动Tomcat服务
    • 热部署:Tomcat服务不停止,需要依赖manager、ant脚本、tcd(tomcat client deployer)等工具
  • 反部署undeploy:停止webapp运行,并从JVM上清除已经加载的类,从Tomcat应用目录中移除部署的文件

  • 启动start:是webapp能够访问

  • 停止stop:webapp不能访问,不能提供服务,但是JVM并不清除它

部署基于JAVA的博客系统 JPress
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@c72 webapps]$pwd
/usr/local/tomcat/webapps
[root@c72 webapps]$mv /usr/local/src/jpress-v3.3.0.war ./
[root@c72 webapps]$ln -s jpress-v3.3.0 jpress # 创建软连接,方便以后升级
[root@c72 webapps]$ll
total 70024
drwxr-x--- 2 tomcat tomcat 4096 Jan 30 22:50 docs
drwxr-x--- 7 tomcat tomcat 4096 Jan 30 22:50 examples
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 host-manager
lrwxrwxrwx 1 root root 13 Jan 31 11:35 jpress -> jpress-v3.3.0
drwxr-x--- 6 tomcat tomcat 4096 Jan 31 11:35 jpress-v3.3.0
-rw-r--r-- 1 root root 71677863 Jan 31 11:31 jpress-v3.3.0.war
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 manager
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 23:01 ROOT

基于WEB的管理Server status和Manager APP实现应用部署

默认的管理页面被禁用,启用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
# server.xml中Resource的pathname参数指定了授权用户信息的文件:conf/tomcat-users.xml

[root@c72 conf]$vim tomcat-users.xml
#加下面两行,指定用户和密码
<role rolename="manager-gui"/>
<user username="admin" password="123456" roles="manager-gui"/>

[root@c72 manager]$pwd
/usr/local/tomcat/webapps/manage
[root@c72 manager]$vim META-INF/context.xml
# 当前访问地址是10.0.0.72,可以修改正则表达式为
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|10\.0\.0\.\d+"

基于WEB应用程序管理器实现APP的部署

访问 /manager 即可

常见配置详解

端口8005/tcp 安全配置管理
1
2
$ss -ntlp | grep 8005
LISTEN 0 1 [::ffff:127.0.0.1]:8005 [::]:* users:(("java",pid=3117,fd=66))

tomcat 默认监听8005端口,这是一个后门,可通过这个端口关闭 tomcat 服务

1
2
$nc 127.0.0.1 8005		# 连接到本机的8005端口
SHUTDOWN # 输入SHUTDOWN,关闭tomcat
1
2
# server.xml 
<Server port="8005" shutdown="SHUTDOWN"> # 配置后门的端口,和关闭服务的指令

此行不能被注释,否则无法启动tomcat服务

此管理功能建议禁用:

  • 将SHUTDOWN改为一串猜不出的字符串实现
  • port修改成 0, 会使用随机端口,如:36913
  • port设为-1等无效端口,将关闭此功能
显示指定的http服务器版本信息

默认不显示 tomcat 的 http 的 Server 头信息, 可以指定 tomcat 的 http 的 Server 头信息

示例:冒充 nginx 服务器

1
2
3
4
# 给Connector标签添加Server参数
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" Server="nginx" />
# 重启tomcat
systemctl restart tomcat

其他配置

server.xml 中可以配置service、connector、Engine、Host等

1
2
3
4
5
6
7
8
# 一般情况下,一个Server实例配置一个Service,name属性相当于该Service的ID
<Service name="Catalina">
# redirectPort:如果访问HTTPS协议,自动转向这个连接器。但大多数时候,Tomcat并不会开启HTTPS,因为Tomcat往往部署在内部,HTTPS性能较差
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
# 引擎配置
<Engine name="Catalina" defaultHost="localhost">
# defaultHost配置,默认localhost。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
多虚拟主机配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# name:相当于 nginx 的 server_name
# appBase:网站根目录,可以使用相对路径或绝对路径
# unpackWARs:是否自动解压 war 格式
# autoDeploy:热部署,自动加载并运行应用
<Host name="test.ljk.cn" appBase="/data/wwwroot/test.ljk.cn" unpackWARs="true" autoDeploy="true">
# className
# directory:日志目录,可以使用相对路径或绝对路径
# 日志文件:prefix.2021-01-31.suffix
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/wwwlogs" prefix="test.ljk.cn_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

# 注意日志的权限问题
chown -R tomcat:tomcat /data/wwwlogs

[root@c72 test.ljk.cn]$pwd
/data/wwwroot/test.ljk.cn
[root@c72 test.ljk.cn]$cat ./ROOT/index.html # 注意目录结构
hello test.ljk.cn

基于web方式的Host Manager虚拟主机管理

略…

Context 配置

Context 类似 Nginx 中的 location

1
2
3
4
5
6
7
8
9
# path
# docBase:相对、绝对路径均可
# reloadable:WEB-INF/classes或META-INF/lib目录下.class文件有改动,自动重载,生产建议关闭
<Context path="/test" docBase="/data/test" reloadable="true" />

# 相当于 Nginx 中:
location /test {
alias /data/test;
}

还可以单独添加日志配置:

1
2
3
<Context path="/test" docBase="/data/test" reloadable="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_test_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Context>

Valve组件

valve(阀门)组件可以定义日志

1
2
3
4
5
6
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />

# className
# 定义访问日志 org.apache.catalina.valves.AccessLogValve
# 定义访问控制 org.apache.catalina.valves.RemoteAddrValve
<Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="10\.0\.0\.\d+"/>

反向代理和负载均衡

tomcat 的网络并发性能比较差,所以通常只用 tomcat 解析动态页面

两种常见的部署方式:

nginx 监听80端口,tomcat监听8080端口,nginx负责将请求转发给tomcat处理

反向代理:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name to2b.cn;
access_log off;

location / {
proxy_pass http://to2b.cn:8080; # 反向代理
}
}

负载均衡:

略…

Tomcat Session Replication Cluster

Tomcat 官方实现了 Session 的复制集群,将每个Tomcat的Session进行相互的复制同步,从而保证所有Tomcat都有相同的Session信息

这种同步 session 的方式缺点很明显,就是占用内存,所以不适用于 tomcat 服务器太多的情况,如果只有两三台,可以使用

session共享服务器 msm

msm:memcached session manager,将Tomcat的session保持到 memcached 或 redis,实现高可用

github网站链接: https://github.com/magro/memcached-session-manager

安装

参考链接:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration

  1. 下载各种包

    注意:直接点击会报错501,这时因为maven仓库废弃了http访问,手动更改为https即可

  2. 将相关的jar文件都放到Tomcat的lib目录中即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # Tomcat的Session管理类
    memcached-session-manager-2.3.2.jar
    memcached-session-manager-tc8-2.3.2.jar

    # 驱动类,memcached(spymemcached.jar)、Redis(jedis.jar)
    spymemcached-2.12.3.jar

    # Session数据的序列化、反序列化类,官方推荐 kryo
    msm-kryo-serializer-2.3.2.jar
    kryo-serializers-0.45.jar
    kryo-3.0.3.jar
    minlog-1.3.1.jar
    reflectasm-1.11.9.jar
    asm-5.2.jar
    objenesis-2.6.jar
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@c72 lib]$pwd
    /usr/local/tomcat/lib
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/2.3.2/memcached-session-manager-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager-tc8/2.3.2/memcached-session-manager-tc8-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/net/spy/spymemcached/2.12.3/spymemcached-2.12.3.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/msm-kryo-serializer/2.3.2/msm-kryo-serializer-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/kryo-serializers/0.45/kryo-serializers-0.45.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/kryo/3.0.3/kryo-3.0.3.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/org/ow2/asm/asm/5.2/asm-5.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/reflectasm/1.11.9/reflectasm-1.11.9.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/minlog/1.3.1/minlog-1.3.1.jar

sticky 模式

前端tomcat和后端memcached**有关联(粘性)**关系

略…

non-sticky 模式

前端tomcat和后端memcached**无关联(无粘性)**关系,msm 1.4.0之后版本开始支持

Tomcat session为中转Session,对每一个SessionID随机选中后端的memcached节点n1(或者n2)为主session,而另一个memcached节点n2(或者是n1)为备session。产生的新的Session会发送给主、备memcached,并清除本地Session

如果n1下线,n2则转正。n1再次上线,n2依然是主Session存储节点

实战案例: memcached 实现non-sticky模式

ip 服务 安装软件
10.0.0.71 tomcat1、memcached jdk8、tomcat8、memcached
10.0.0.72 tomcat1、memcached jdk8、tomcat8、memcached
10.0.0.73 负载均衡调度器 haproxy 或者 nginx

负载均衡调度器的安装配置过程:略…

10.0.0.71 和 10.0.0.72 做以下配置:

  1. 安装配置 memcached

    1
    2
    3
    4
    5
    6
    7
    $yum install memcached
    $vim /etc/sysconfig/memcached
    PORT="11211"
    USER="memcached"
    MAXCONN="1024"
    CACHESIZE="64"
    OPTIONS=""
  2. 配置 tomcat 的 context.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <Context>
    ...
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
    memcachedNodes="n1:10.0.0.72:11211,n2:10.0.0.73:11211"
    sticky="false"
    sessionBackupAsync="false"
    lockingMode="uriPattern:/path1|/path2"
    requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
    transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
    </Context>

实战案例: redis 实现 non-sticky 模式

参考:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration#example-for-non-sticky-sessions--kryo--redis

Session 问题方案总结

  1. session绑定,基于IP或session cookie的。其部署简单,尤其基于session黏性的方式,粒度小,对负载均衡影响小。但一旦后端服务器有故障,其上的session丢失
  2. session复制集群,基于tomcat实现多个服务器内共享同步所有session。此方法可以保证任意一台后端服务器故障,其余各服务器上还都存有全部session,对业务无影响。但是它基于多播实现心跳,TCP单播实现复制,当设备节点过多,这种复制机制不是很好的解决方案。且并发连接多的时候,单机上的所有session占据的内存空间非常巨大,甚至耗尽内存
  3. session服务器,将所有的session存储到一个共享的内存空间中,使用多个冗余节点保存session,这样做到session存储服务器的高可用,且占据业务服务器内存较小。是一种比较好的解决session持久的解决方案

以上的方法都有其适用性。生产环境中,应根据实际需要合理选择

Tomcat 性能优化

tomcat 是运行在 jvm 虚拟机上的一个程序,所以tomcat的性能和jvm密切相关,所以要想优化tomcat的性能,除了优化tomcat本身的设置,还需要优化jvm

JVM 组成

1
2
3
4
$java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

JVM:Java Virtual Machine,java 虚拟机

HotSpot 实现了 Java 虚拟机规范,java虚拟机有好几种,HotSpot 是最主流的一种

  • 类加载子系统:将java代码编译成字节码文件,将所需所有类加载到内存,必要时将类实例化成实例
  • 运行时数据区:最消耗内存的空间,需要优化
  • 执行引擎:包括 JIT (JustInTimeCompiler)即时编译器,GC (Garbage Collector)垃圾回收器
  • 本地方法接口:通过 JNI(Java Native Interface)调用 C、C++ 库其他语言,扩展Java功能

运行时数据区的heap(堆)是我们关注的重点,这部分可以通过修改设置进行优化,其他部分没有优化的途径

  • MetaSpace:元空间,线程共享,存放已加载的类信息(构造方法、接口定义)、常量(final)、静态变量(static)、运行时常量池等。但实例变量存放在堆内存中。**JDK8 之前叫 Method Area(方法区) **
  • heap:堆,线程共享,虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出OOM异常。堆是靠GC垃圾回收器管理的,通过 -Xmx -Xms 指定最大堆和最小堆空间大小
  • Java stack:java栈,线程私有,每个线程会分配一个栈,存放java中8大基本数据类型、对象引用、实例的本地变量、方法参数和返回值等,基于FILO()(First In Last Out),每个方法为一个栈帧
  • Program Counter Register:PC寄存器,线程私有,就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令。因为线程需要切换,当一个线程被切换回来需要执行的时候,需要知道执行到哪里
  • Native Method stack:本地方法栈,线程私有,为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等
    所谓本地方法,即使用native 关健字修饰的方法,比如 Thread.sleep方法。简单的说是非Java实现的方法,例如操作系统的C编写的库提供的本地方法,Java调用这些本地方法接口执行。但是要注意,本地方法应该避免直接编程使用,因为Java可能跨平台使用,如果用了Windows API,换到了Linux平台部署就有了问题

GC (Garbage Collection) 垃圾收集器

垃圾回收算法

  • 标记-清除 Mark-Sweep
  • 标记-压缩 Mark-Compact
  • 复制 Copying

分代堆内存GC策略

JVM 内存空间划分为 heap 区和 非 heap 区

heap 内存空间:

  • Eden Space:伊甸园区
  • Survivor Space:幸存者区,分为两部分,一个是from区,一个是to区。大小相等、地位相同、可互换
    • from:指的是本次复制数据的源区
    • to:指的是本次复制数据的目标区
  • Tenured Gen:养老区 或 老年代

规律:一般情况99%的对象都是临时对象

默认空间大小比例:

非 heap 区内存划分:

  • Code Cache
  • Compressed Class Space
  • Metaspace

Minor GC

通常场景下,大多数对象都不会存活很久,而且创建活动非常多,伊甸园区和幸存者区的垃圾回收就非常频繁,所以采用 复制 Copying 算法

  1. 所有新建对象(特大对象直接进入养老区)都出生在eden
  2. 当eden满了,启动GC。这个称为 Young GC 或者 Minor GC,标记eden存活对象,将存活对象复制到幸存者者区(两个幸存者区之一),GC完成
  3. 当eden满了,再次启动GC,标记eden和幸存者区的存活对象,将存活对象复制到另一个幸存者区,GC完成

以后就重复上面的步骤,每当eden满了,就将其中的存货对象和幸存者区(from区)的存货对象复制到另一个幸存者区(to区)

但是,如果一个对象一直存活,它最后就在from、to来回复制,如果from区中对象复制次数达到阈值(默认15次,CMS为6次,可通过java的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到养老区

Major GC

进入养老区的数据较少,所以养老区被占满的速度较慢,所以垃圾回收也不频繁,所以较常采用 标记-压缩 Mark-Compact 算法

如果养老区也满了,会触养老区GC,称为 Old GC 或者 Major GC

因为 Major GC 会对整个 heap 内存空间进行垃圾回收,影响所有的区域,所以 Major GC 又叫 FullGC

java 内存调整相关参数

Java 命令行参考文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

选项分类:

1
2
3
-选项名称		# 标准选项,所有HotSpot都支持
-X选项名称 # 稳定的非标准选项
-XX:选项名称 # 非标准的不稳定选项,下一个版本可能会取消

常用选项:

1
2
3
4
5
6
7
8
-Xms				# 设置堆内存初始大小,示例:-Xms2g
-Xmx # 设置堆内存上限,示例:-Xms4g
-XX:NewSize # 设置伊甸园区和幸存者区初始大小,示例:-XX:NewSize=128m
-XX:MaxNewSize # 设置伊甸园区和幸存者区上限,示例:-XX:MaxNewSize=256m
-Xmnsize # 同时设置-XX:NewSize 和 -XX:MaxNewSize,示例:-Xmn1g
-XX:NewRatio # 以比例方式设置新生代和老年代,示例:-XX:NewRatio=2,即new/old=1/2
-XX:SurvivorRatio # 以比例方式设置eden和survivor,示例:-XX:SurvivorRatio=6,即eden/survivor=6/1、new/survivor=8/1
-Xss # 设置每个线程私有的栈空间大小,依据具体线程大小和数量,示例:-Xss256k

注意:Xms 和 Xmx 通常设置为相同的值,防止内存碎片化

垃圾收集方式

回收线程 数:GC 线程串行(serial)还是并行(parallel)

  • 串行垃圾回收器:一个 GC 线程完成回收工作
  • 并行垃圾回收器:多个 GC 线程同时一起完成回收工作,充分利用CPU资源

工作模式 不同:GC 线程是否和工作线程一起运行

  • 独占垃圾回收器:只有 GC 在工作,STW 一直进行到回收完毕,工作线程才能继续执行

  • 并发垃圾回收器:GC 线程垃圾回收某些阶段可以和工作线程一起进行,如标记阶段并行,回收阶段仍然串行

调整策略

对JVM调整策略应用极广

  • 在WEB领域中Tomcat等

  • 在大数据领域Hadoop生态各组件

  • 在消息中间件领域的Kafka等

  • 在搜索引擎领域的ElasticSearch、Solr等

注意:在不同领域和场景对JVM需要不同的调整策略

  • 减少 STW 时长,串行变并行
  • 减少 GC 次数,要分配合适的内存大小

一般情况下,我们大概可以使用以下原则:

  • 客户端或较小程序,内存使用量不大,可以使用串行回收
  • 对于服务端大型计算,可以使用并行回收
  • 大型WEB应用,用户端不愿意等待,尽量少的STW,可以使用并发回收

垃圾回收器

常用垃圾回收器

ParNew:parallel new,并行处理新生代

按分代设置不同垃圾回收器:

新生代:

  • 新生代串行收集器 Serial
  • 新生代并行回收收集器 PS(Parallel Scavenge)
  • 新生代并行收集器 ParNew

老年代:

  • 老年代串行收集器 Serial Old
  • 老年代并行回收收集器 Parallel Old
  • CMS(Concurrent Mark Sweep 并发标记清除算法)收集器

以下收集器不再按明确的分代单独设置:

  • G1(Garbage First)收集器
    • 最新垃圾回收器,从JDK1.6实验性提供,JDK1.7发布,其设计目标是在多处理器、大内存服务器端提供优于CMS收集器的吞吐量和停顿控制的回收器。JDK9将G1设为默认的收集器,建议 JDK9版本以后使用
    • 基于 标记压缩 算法,不会产生大量的空间碎片,有利于程序的长期执行
    • 分为4个阶段:初始标记、并发标记、最终标记、筛选回收。并发标记并发执行,其它阶段 STW 只有GC线程并行执行
    • G1收集器是面向服务端的收集器,它的思想就是首先回收尽可能多的垃圾(这也是Garbage-First名字的由来)
    • G1能充分的利用多CPU,多核环境下的硬件优势,使用多个CPU来缩短STW停顿的时间 (10ms以内
    • 可预测的停顿:这是G1相对于CMS的另一大优势,G1和CMS一样都是关注于降低停顿时间,但是G1能够让使用者明确的指定在一个M毫秒的时间片段内,消耗在垃圾收集的时间不得超过N毫秒
    • 通过此选项指定: +UseG1GC
  • ZGC收集器:减少SWT时长(1ms以内),可以媲美 C++ 的效率,目前实验阶段
  • Shenandoah收集器:和ZGC竞争关系,目前实验阶段
  • Epsilon收集器:调试 JDK 使用,内部使用,不用于生产环境
垃圾收集器设置

优化调整Java 相关参数的目标:尽量减少 FullGC 和 STW

通过以下选项可以单独指定新生代、老年代的垃圾收集器

  • -server
    指定为Server模式,也是默认值,一般使用此工作模式
  • -XX:+UseSerialGC
    运行在Client模式下,新生代是Serial, 老年代使用SerialOld
  • -XX:+UseParNewGC
    新生代使用ParNew,老年代使用SerialOld
  • -XX:+UseParallelGC
    运行于server模式下,新生代使用Serial Scavenge, 老年代使用SerialOld
  • -XX:+UseParallelOldGC
    新生代使用Paralell Scavenge, 老年代使用Paralell Old
    -XX:ParallelGCThreads=N 在关注吞吐量的场景使用此选项增加并行线程数
  • -XX:+UseConcMarkSweepGC
    新生代使用ParNew, 老年代优先使用CMS,备选方式为Serial Old
    响应时间要短,停顿短使用这个垃圾收集器
    -XX:CMSInitiatingOccupancyFraction=N 达到老年代的大小的百分比多少时触发回收,默认68
    -XX:+UseCMSCompactAtFullCollection 开启此值,在CMS收集后,进行内存碎片整理
    -XX:CMSFullGCsBeforeCompaction=N 设定多少次CMS后,进行一次内存碎片整理
    -XX:+CMSParallelRemarkEnabled 降低标记停顿

JAVA参数总结

参数名称 含义 默认值 备注
-Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制
-Xmn 年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4
-Xss -Xss JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
-XX:ThreadStackSize Thread StackSize (0 means use defaultstack size) [Sparc: 512;Solaris x86: 320 (was 256prior in 5.0 and earlier);Sparc 64 bit: 1024; Linuxamd64: 1024 (was 0 in 5.0and earlier); all others 0.]
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小 =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化
-XX:+DisableExplicitGC 关闭System.gc() 这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行GC时才有效
-XX:+AggressiveOpts 加快编译
-XX:+UseBiasedLocking 锁机制的性能改善
-Xnoclassgc 禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 可达的对象在上次被引用后将保留一段时间。 缺省值是堆中每个空闲兆字节的生命周期的一秒钟
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效 另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象
-XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%
-XX:+CollectGen0First FullGC时是否先YGC false

并行收集器相关参数:

参数名称 含义 默认值 备注
-XX:+UseParallelGC Full GC采用parallelMSC 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
-XX:+UseParNewGC 设置年轻代为并行收集 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads 并行收集器的线程数 此值最好配置与处理器数目相等 同样适用于CMS
-XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(ParallelCompacting) 这个是JAVA 6出现的参数选项
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间) 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
-XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比 公式为1/(1+n)
-XX:+ScavengeBeforeFullGC Full GC前调用YGC true

CMS相关参数:

参数名称 含义 默认值 备注
-XX:+UseConcMarkSweepGC 使用CMS内存收集 测试中配置这个以后,-XX:NewRatio=4的配置可能失效,所以,此时年轻代大小最好用-Xmn设置
-XX:+AggressiveHeap 试图是使用大量的物理内存 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要256MB内存 大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升)
-XX:CMSFullGCsBeforeCompaction 多少次后进行内存压缩 由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生”碎片”,使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理
-XX:+CMSParallelRemarkEnabled 降低标记停顿
-XX+UseCMSCompactAtFullCollection 在FULLGC的时候,对年老代的压缩 CMS是不会移动内存的, 因此, 这个非常容易产生碎片,导致内存不够用, 因此, 内存的压缩这个时候就会被启用。增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始CMS收集 禁止hostspot自行触发CMSGC
-XX:CMSInitiatingOccupancyFraction=70 使用cms作为垃圾回收使用70%后开始CMS收集 92
-XX:+UseConcMarkSweepGC 使用CMS内存收集 测试中配置这个以后,-XX:NewRatio=4的配置可能失效,所以,此时年轻代大小最好用-Xmn设置
-XX:CMSInitiatingPermOccupancyFraction 设置PermGen使用到达多少比率时触发 92
-XX:+CMSIncrementalMode 设置为增量模式 用于单CPU情况
-XX:+CMSClassUnloadingEnabled

辅助信息:

参数名称 含义 默认值 备注
-XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
-XX:+PrintGCDetails 输出形式:[GC [DefNew:8614K->781K(9088K),0.0123035 secs] 118250K->113543K(130112K),0.0124633 secs] [GC[DefNew: 8614K->8614K(9088K), 0.0000665secs] 121376K->10414K(130112K),0.0436268 secs]
-XX:+PrintGCTimeStamps
-XX:+PrintGC:PrintGCTimeStamps 可与-XX:+PrintGC -XX:+PrintGCDetails混合使用输出形式:11.851: [GC98328K->93620K(130112K),0.0082960 secs]
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用 输出形式:Total time forwhich application threadswere stopped: 0.0468229seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用 输出形式:Application time:0.5291524 seconds
-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息
-Xloggc:filename 把相关日志信息记录到文件以便分析. 与上面几个配合使用
-XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
-XX:+PrintClassHistogram garbagecollectsbeforeprintingthehistogram
-XX:+PrintTLAB 查看TLAB空间的使用情况
-XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值

JVM 相关工具

$JAVA_HOME/bin下

命令 说明
jps 查看所有jvm进程
jinfo 查看进程的运行环境参数,主要是jvm命令行参数
jstat 对jvm应用程序的资源和性能进行实时监控
jstack 查看所有线程的运行状态,程序员常用堆栈情况查看工具
jmap 查看jvm占用物理内存的状态
jhat +UseParNew
jconsole 图形工具
jvisualvm 图形工具

JMX

JMX(Java Management Extensions,即 Java 管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX 可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
JMX最常见的场景是监控 Java 程序的基本信息和运行情况,任何 Java 程序都可以开启 JMX,然后使用 JConsole 或Visual VM 进行预览。

为Java程序开启 JMX 很简单,只要在运行 Java 程序的命令后面指定如下命令即可

在 tomcat 开启远程 JMX 如下配置:

1
2
3
4
5
-Dcom.sun.management.jmxremote	#启用远程监控JMX
-Djava.rmi.server.hostname=10.0.0.71 #tomcat主机自己的IP地址,不要写zabbix服务器的地址
-Dcom.sun.management.jmxremote.port=1100 #默认启动的JMX端口号,要和zabbix添加主机时候的端口一致
-Dcom.sun.management.jmxremote.ssl=false #不启用ssl认证
-Dcom.sun.management.jmxremote.authenticate=false #不启用用户名密码认证

jps

JVM 进程状态工具

jinfo

输出给定的 java 进程的所有配置信息

jstat

输出指定的 java 进程的统计信息

jstack

查看指定的java进程的线程栈的相关信息,程序员常用堆栈情况查看工具

jmap

Memory Map,用于查看堆内存的使用状态

jhat

Java Heap Analysis Tool 堆分析工具

jconsole

查看 Java 进程信息的图形化工具

  1. 配置JMX,在服务器10.0.0.71上做如下配置

    1
    2
    3
    4
    5
    6
    $vim /usr/local/tomcat/bin/catalina.sh
    # OS specific support. $var _must_ be set to either true or false.
    # 添加以下配置,注意位置,如果添加在文件末尾,则无法生效
    JAVA_OPTS="-Djava.rmi.server.hostname=10.0.0.71 -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

    $systemctl restart tomcat.service
  2. 在windows上配置好jdk,去 jdk/bin 目录下,启动 jconsole.exe

jvisualvm

监控 jvm 的图形化工具,可以本机监控,也可以远程监控,下面以远程监控为例:

  1. 配置JMX,在服务器10.0.0.71上做如下配置

    1
    2
    3
    4
    5
    6
    $vim /usr/local/tomcat/bin/catalina.sh
    # OS specific support. $var _must_ be set to either true or false.
    # 添加以下配置,注意位置,如果添加在文件末尾,则无法生效
    JAVA_OPTS="-Djava.rmi.server.hostname=10.0.0.71 -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

    $systemctl restart tomcat.service
  2. 在windows上配置好jdk,去 jdk/bin 目录下,启动 jvisualvm.exe,添加远程主机 -> 选择添加 jmx 连接

使用 jvisualvm的 Visual GC 插件

安装完成后,重启即可,不过因为是远程连接,还需要做其他配置,这个问题百度解决,或者选择使用本机监控

Jprofiler

JProfiler是一款功能强大的Java开发分析工具,它可以快速的帮助用户分析出存在的错误,软件还可对需要的显示类进行标记,包括了内存的分配情况和信息的视图等

这个工具是收费的,可以用来定位OOM的问题原因

JProfiler官网:http://www.ej-technologies.com/products/jprofiler/overview.html

Tomcat 性能优化常用配置

内存空间优化

1
2
3
4
5
6
7
JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "

-server:服务器模式,VM运行在server模式,为在服务器端最大化程序运行速度而优化
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值

生产案例:

1
2
3
4
5
6
7
8
[root@centos8 ~]#vim /usr/local/tomcat/bin/catalina.sh
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -
XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -
XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -
XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -
XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -
XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -
XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"

一台 tomcat 服务器并发连接数不高,生产建议分配物理内存通常4G到8G较多,如果需要更多连接,一般会利用虚拟化技术实现多台 tomcat

线程池调整

1
2
3
4
5
[root@centos8 ~]#vim /usr/local/tomcat/conf/server.xml
......
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" />
......

常用属性:

1
2
3
4
5
6
7
8
9
10
connectionTimeout		# 连接超时时长,单位ms
maxThreads # 最大线程数,默认200
minSpareThreads # 最小空闲线程数
maxSpareThreads # 最大空闲线程数
acceptCount # 当启动线程满了之后,等待队列的最大长度,默认100
URIEncoding # URI 地址编码格式,建议使用 UTF-8
enableLookups # 是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
compression # 是否启用传输压缩机制,建议 "on",CPU和流量的平衡
compressionMinSize # 启用压缩传输的数据流最小值,单位是字节
compressableMimeType # 定义启用压缩功能的 MIME 类型text/html、text/css、text/javascript

语法:

  • 扩展名通常为 yml 或 yaml
  • 在单一文件第一行,用连续三个连字号”-“ 开始,还有选择性的连续三个点号( … )用来表示文件的结尾
  • 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
  • 使用#号注释代码
  • 缩进必须是统一的,不能空格和tab混用
  • 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
  • YAML文件内容是区别大小写的,key/value的值均需大小写敏感
  • 多个key/value可同行写也可换行写,同行使用,分隔
  • key后面冒号要加一个空格 比如: key: value
  • value可是个字符串,也可是另一个列表

支持的数据类型

  • 标量:单个的、不可再分的值
  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

scalar 标量

标量是最基本的,不可再分的值,包括:字符串、布尔值、整数、浮点数、Null、时间、日期

1
2
3
4
5
# 一行
name: lujinkai
# 使用缩进
name:
wang

dictionary 字典

字典由多个键值对组成,键值用: 分割,注意:后面有一个空格;值可以是标量、列表或其他字典

1
2
3
4
5
6
# 一行
account: { name: lujinkai, age: 26 }
# 使用缩进
account:
name: lujinkai
age: 26

list 列表

列表由多个元素组成,元素可以是标量、字典或其他列表

1
2
3
4
5
6
7
# 一行
skills: [ linux, golang, python ]
# 使用缩进
skills:
- linux
- golang
- python

范例:使用yaml表示一个家庭

1
2
3
4
5
6
7
8
9
10
11
12
13
name: John Smith
age: 41
gender: Male
spouse:
name: Jane Smith
age: 37
gender: Female
children:
- name: Jimmy Smith
age: 17
gender: Male
- {name: Jenny Smith, age: 13, gender: Female}
- {name: hao Smith, age: 20, gender: Male }

vim

三种模式:

  • 命令模式 或 普通模式
  • 插入模式 或 编辑模式
  • 扩展命令模式 或 命令行模式 或 末行模式

命令模式

光标跳转

字符间跳转
  • j 下
  • k 上
  • h 左
  • l 右
单词间跳转
  • w 定位下一个单词
当前页跳转
  • H 定位页首
  • M 定位中间行
  • L 定位页末
  • zt 滚动屏幕, 使当前行置顶 top
  • zz 滚动屏幕, 使当前行置中
  • zb 滚动屏幕, 使当前行置底 bottom
行首行尾跳转
  • ^ 定位行首第一个非空字符
  • 0 定位行尾
  • $ 定位行首
行间移动
  • gg 定位文首
  • G 定位文末
  • #G 定位指定行行首, 例如定位第5行: `5G`
句间和段落间移动
  • ) 下一句
  • ( 上一句
  • } 下一段
  • { 上一段
翻屏操作
  • ctrl+f 向后翻一屏
  • ctrl+d 向后翻半屏
  • ctrl+b 向前翻一屏
  • ctrl+u 向前翻半屏

字符编辑

  • x 剪切光标处字符
  • p 黏贴
  • ~ 转换大小写, 然后光标向后移一位
  • J 删除当前行前后的换行符

替换 replace

  • r 替换光标所在处的一个字符, 按下r,然后再按替换后的字符
  • R 切换成REPLACE模式, ESC回到命令模式, 按下r, 然后再输入替换后的内容

删除 delete

  • d 删除, 可结合光标跳转, 实现范围删除
  • d$ 删除到行尾
  • d0 删除到行首
  • d^ 删除到非空行首
  • dd 删除当前行
  • #dd 删除多行, 从当前行开始, 删除#行
  • D 删除到行尾, 等同于d$

复制 yank

  • y 复制, 行为相似于d命令
  • Y 复制整行

粘贴 paste

  • p 整行, 则粘贴到下一行, 否则粘贴到光标后
  • P 整行, 则粘贴到上一行, 否则粘贴到光标后

命令模式 切换到 插入模式

以下快捷键, 进入插入模式

  • i 不动
  • I 定位行首
  • a 向后移一位
  • A 定位行尾
  • o 定位下一行行首, 并开辟新行
  • O 定位上一行行首, 并开辟新行

插入模式

从光标前输入数据

末行模式

  • :wq 存盘并退出
  • :q! 不存盘强制退出
  • :r file 将file中的内容写入到当前文件, 例如`:r a.log`就把a.log中的内容写入到当前文件
  • :w file 将当前文件内容写入file文件, 如果file已存在, 需要`:w! file`强制覆盖文件
  • :!command 不退出文件,执行命令
  • :r!command 将执行命令的输出写入到当前文件
  • 地址定界
1
2
3
4
5
6
7
8
9
10
11
# 		第几行, 例如2表示第2行
#,# 第#行到到第#行, 例如 2,5 表示2到5行
#,+# 第#行加#行, 例如 2,+3 表示2到5行
. 当前行
$ 最后一行, .,$-1 表示当前行到倒数第二行
% 全文, 相当于1、$

/pattern/ 从当前行开始,直到第一次匹配到的行
/pattern1/,/pattern2/ 从pattern1第一次匹配到行, 到pattern2第一次匹配到的行
#,/pattern/ 从指定行开始, 到第一次pattern匹配到行
/pattern/,$ 从pattern第一次匹配到的行, 到最后一行

地址定界后跟一个编辑命令

1
2
3
4
d 		删除
y 复制
w file 将范围内的行另存到file文件中
r file 在指定位置插入file文件中的所有内容
  • 查找并替换
1
2
3
4
5
6
7
8
9
s/要查找的内容/替换为的内容/修饰符		# 当前行查找替换
%s/要查找的内容/替换为的内容/修饰符 # 全文查找替换

# 要查找的内容:可使用正则
# 替换为的内容:不能使用正则,但可以使用\1, \2, ...等后向引用符号;还可以使用“&”引用前面查找时查找到的整个内容
# 修饰符
# i 忽略大小写
# g 全局替换
# gc 全局替换, 每次替换前询问
  • u 撤销, 相当于windows中的ctrl+z
  • #u 撤销多步
  • U 撤销此行的所有操作
  • ctrl+r 取消撤销, 相当于windows中的ctrl+y

高级用法

范例: 粘贴”wang”100次

1
100iwang [ESC]

di” 光标在” “之间,则删除” “之间的内容
yi( 光标在()之间,则复制()之间的内容
vi[ 光标在[]之间,则选中[]之间的内容
dtx 删除字符直到遇见光标之后的第一个 x 字符
ytx 复制字符直到遇见光标之后的第一个 x 字符

定制vim的工作特性

配置文件:

  • /etc/vimrc 全局
  • /.vimrc 个人

可视化模式

如何进入可视化模式

  • v 面向字符
  • V 面向整行
  • ctrl + v 面向块(列)

选中的文字可以被删除、复制、变更、过滤、搜索、替换等

范例: 在文件每一行行首插入#, 然后再删除

1
2
3
4
5
6
7
8
9
10
1. gg
2. ctrl + v
3. G
4. I
5. 输入#
6. ESC退出 插入#成功

7. ctrl + v
8. d 删除成功
9 ESC退出

vim总结



cat

如果没有指定文件,或者指定文件为“-”,则从标准输入读取

  • -n 对输出内容标行号
  • -b 对输出内容非空行标行号
  • -E 显示行结束符$
  • -A 显示所有控制符
  • -s 压缩连续的空行成一行

nl

相当于 cat -b

tac

cat倒着写tac, 功能也是恰如其名: 逆向显示文本内容

tr

转换和删除字符 translate

1
tr [OPTION]... SET1 [SET2]

转换、删除、去重标准输入中的字符,将结果写入到标准输出。

范例: 将a换成b

1
tr a b

tr类似于sed命令,但是比sed简单,所以tr能实现的功能,sed都能实现。

  • -d 删除
  • -c 取补集
  • -s 对set1进行去重操作
  • -t
1
2
3
4
5
6
7
8
# 生成8位随机数
[root@4710419222 ~]# cat /dev/urandom | tr -dc A-Za-z0-9 | head -c8;echo
1imvyPoH
# 将a.log中大写输出为小写
[root@4710419222 test]# cat a.log
HELLO WORD
[root@4710419222 test]# tr A-z a-z < a.log
hello word

rev

将每行的内容逆向显示

  • -c 指定前几个字符
  • -n 指定前几行
    当n是负数(-#),表示舍弃最后#行

tail

  • -f –follow
  • -c 指定后几个字符
  • -n 指定后几行
    当n是显示整数(+#),表示从第#行开始到结束

less 和 more

分页查看文件内容 less 和 more

一般搭配管道符使用,less和more差不多,区别在于more只能向下翻页

hexdump、od、xxd

查看非文本内容

最常的是hexdump,常用选项 -C

cut

提取文本文件或STDIN数据的指定列

1
cut [OPTION]... [FILE]...
  • -d delimiter 指明分隔符
  • -f fileds
    • # 第#个字段, 例如: 3
    • #,#[,#] 离散的多个字段, 例如: 1,3,6
    • #-# 连续的多个字段, 例如: 1-6
    • 混合使用: 1-3,7
  • -c 按字符切割
  • –output-delimiter 指定输出分隔符
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
[19:22:09 root@centos7 data]#cut -d : -f1-3 --output-delimiter=. passwd   
root.x.0
bin.x.1
daemon.x.2
adm.x.3
lp.x.4
sync.x.5
shutdown.x.6
halt.x.7
mail.x.8
operator.x.11
games.x.12
ftp.x.14
nobody.x.99
systemd-network.x.192
dbus.x.81
polkitd.x.999
sshd.x.74
postfix.x.89
lujinkai.x.1000
test.x.1001
test2.x.1002
gentoo.x.1003
tomcat.x.1004
mysql.x.1005
[14:49:02 root@centos7 data]#cut -c1-3 passwd
roo
bin
dae
adm
lp:
syn
shu
hal
mai
ope
gam
ftp
nob
sys
dbu
pol
ssh
pos
luj
tes
tes
gen
tom
mys

paste

合并多个文件,合并多个文件同行号的列到一行

1
paste [OPTION]... [FILE]...
  • -d 分隔符, 指定分隔符, 默认分隔符TAB
  • -s 所有行合成一行显示

wc

文本统计,用于统计文件或STDIN的行总数、单词总数、字节总数和字符总数

  • -l –lines 统计行数
  • -w –words 统计单词数 (规则是有空格或者换行分隔才是单词)
  • -m –chars 打印字符数 (空格和换行和tab都是字符)
  • -L –max-line-length 统计文件中最长行的长度

sort

字典排序, 从小到大

  • -r 反方向
  • -R 随机排序
  • -n 自然排序
  • -h 人类可读排序, 如: 2k 1G
  • -f 忽略大小写
  • -u 合并重复项, 即去重
  • -t 指定字段界定符
  • -k 配合-t使用, 指定使用第几个字符来排序

范例: 按照UID进行排序

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
[21:18:38 root@centos7 data]#cat /etc/passwd | sort -n -t: -k 3    
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
lujinkai:x:1000:1000:lujinkai:/home/lujinkai:/bin/bash
test:x:1001:1001::/home/test:/bin/bash
test2:x:1002:1002::/home/test2:/bin/bash
gentoo:x:1003:1003:Gentoo Distribution:/home/gentoo:/bin/csh
tomcat:x:1004:1006::/home/tomcat:/sbin/nologin
mysql:x:1005:1009::/home/mysql:/bin/bash

uniq

去重,从输入中删除前后相接的重复的行

1
uniq [OPTION]... [FILE]...
  • -c 显示每行重复出现的次数, 重复的行必须相邻, 否则会单独统计
  • -d 仅显示重复的行
  • -u 仅显示不重复的行

uniq常和sort命令一起配合使用

1
sort userlist.txt | uniq -c	

diff和path

比较文本

diff

diff命令比较两个文件之间的区别

常用选项 -u

patch

复制在其他文件中进行的改变 (要谨慎使用)

1
2
diff -u foo.conf foo2.conf > foo.patch
patch -b foo.conf foo.patch

cmp

比较二进制文件,查看二进制文件的不同

练习

1、找出ifconfig “网卡名” 命令结果中本机的IPv4地址

1
2
3
4
5
6
7
8
9
10
11
[root@47105171233 wwwlogs]# cut -d' ' -f1 lujinkai.cn_nginx.log | sort -t' ' -k1 | uniq -c | sort -nr | head -n10       
276 112.64.64.36
186 183.136.190.62
184 112.64.64.47
184 112.64.64.40
184 112.64.64.33
121 176.9.32.203
94 47.92.207.41
92 112.64.64.43
92 112.64.64.39
92 112.64.64.35

2、查出分区空间使用率的最大百分比值
3、查出用户UID最大值的用户名、UID及shell类型
4、查出/tmp的权限,以数字方式显示
5、统计当前连接本机的每个远程主机IP的连接数,并按从大到小排序

参考:https://kubernetes.io/zh/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/

CNI:Container Network Interface,容器网络接口

kubernetes 的网络插件遵从 CNI 规范的 v0.4.0 版本

网络插件有很多,最常用的是 flannel 和 Project Calico,生产环境用后者的最多

跨主机 pod 之间通信,有两种虚拟网络,有两种:overlay 和 underlay

  • overlay:叠加网络 ,搭建隧道
  • underlay:承载网络,设置路由表

overlay

以 flannel 为例,flannel 默认配置的网段是 10.224.1.0/16,flannel 在每个节点创建一个网卡 flannel.1,这是一个隧道,ip 为 10.2441.0/32 - 10.244.255/32,而每个节点上的容器的 ip 为10.244.x.1/24 - 10.244.x.254/24,也就是说 flannel 默认支持最多 256 个节点,每个节点上又最多支持 256 个容器

underlay