LJKのBlog

学无止境

http://kafka.apache.org/

阿里云兼容 kafka 消息队列:https://www.aliyun.com/product/ons

Kafka 是一种高吞吐量的分布式发布订阅消息系统,其具备分布式功能、并可以结合 zookeeper 可以实现动态扩容,用于构建实时数据管道和流应用程序
它具有水平可伸缩性、容错性、快速性

常用消息队列对比:

kafka 优势

kafka 为什么性能高?

  1. 顺序写入,kafka 数据写入磁盘,不是保存在内存,默认保存 168 小时
  • kafka 通过 O(1)的磁盘数据结构提供消息的持久化,即使数以 TB 的消息存储也能够保持长时间的稳定性能
    • O(1)是最低的时间复杂度,哈希算法就是典型的 O(1) 时间复杂度,无论数据规模多大,都可以在一次计算后找到目标
  • 高吞吐量,即使是非常普通的硬件 Kafka 也可以支持每秒数百万的消息
  • 支持通过 Kafka 服务器分区消息,可以将数据保存到不同的服务器
  • 支持 Hadoop 并行数据加载

kafka 角色

  • broker:中文直译“中间人”,实际就是消息代理,是生产者和消费者中间代理保存消息的中转站,集群中每个 kafka 的 broker 都有唯一的 id,由 server.properties 中的 broker.id 指定,可以把每个 kafka 节点抽象的看成是一个 broker,也可以把整个 kafka 集群抽象的看成是一个 broker

  • topic:话题,生产者和消费者监听同一个 topic,生产者往里写消息,消费者从里面读消息

  • partition:分区,也叫分片,物理上的概念,每个分区对应一个文件夹,topic 可以将其消息分片储存,提高性能,然后每个分片做多个副本,保证高可用

    注意:分片数量不要超过 kafka 节点数量;副本数量也不要超过 kafka 节点数量;

    • leader:分片副本的角色,主
    • follower:分片副本的角色,从

    对于一个分片,其副本只有一个是 leader,其他的都是 follower,leader 不能和 follower 在同一个节点,这样就失去了高可用的意义

    高可用:当一个节点故障,其他的 follower 会选举出一个作为 leader

    1
    2
    3
    4
    上图中 topic1 分了两片:topic1-part1、topic1-part2;
    上图中 topic2 只有一片:topic2-part1

    上图中 topic1 和 topic2 的分片都做了三个副本:topicX-part1、topicX-part2、topicX-part3
  • Producer:生产者,负责发布消息到 Kafka broker

  • Consumer:消费者,每个 consumer 属于一个特定的 consuer group(若不指定 group name 则属于默认 group),使用 consumer high level API 时,同一 topic 的一条消息只能被同一个 consumer group 内的一个 consumer 消费,但多个 consumer group 可同时消费这一消息

kafka 和 zookeeper 的关系:

kafka 自身无法实现集群和高可用,kafka 依赖 zookeeper 实现集群和高可用

zookeeper 和 kafka 都可以存储数据,zookeeper 储存单个数据在 1MB 以内,只用来保存服务的元数据,不保存业务信息

  1. Broker 依赖于 Zookeeper,每个 Broker 的 id 和 Topic、Partition 这些元数据信息都会写入 Zookeeper 的 ZNode 节点中
  2. Consumer 依赖于 Zookeeper,Consumer 在消费消息时,每消费完一条消息,会将产生的 offset 保存到 Zookeeper 中,下次消费在当前 offset 往后继续消费。注意:kafka0.9 之前 Consumer 的 offset 存储在 Zookeeper 中,kafka0,9 以后 offset 存储在本地
  3. Partition 依赖于 Zookeeper,Partition 完成 Replication 备份后,选举出一个 Leader,这个是依托于 Zookeeper 的选举机制实现的

kafka 部署

1
2
3
kakfa1.ljk.cn:10.0.1.101
kakfa2.ljk.cn:10.0.1.102
kakfa3.ljk.cn:10.0.1.103

快速部署:http://kafka.apache.org/quickstart

  1. 安装 zookeeper,这里就不配置集群了,安装单机 zookeeper

  2. 安装 kafka

    1
    2
    3
    4
    5
    6
    # kafka 下载页面:http://kafka.apache.org/downloads
    [root@kakfa1 src]$tar -xzf kafka_2.13-2.7.0.tgz
    [root@kakfa1 src]$mv kafka_2.13-2.7.0 /usr/local/kafka
    [root@kakfa1 src]$cd /usr/local/
    [root@kakfa1 local]$scp -r ./kafka/ 10.0.1.102:/usr/local
    [root@kakfa1 local]$scp -r ./kafka/ 10.0.1.103:/usr/local
  3. 配置 kafka

    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
    [root@kakfa1 ~]$vim /etc/hosts
    ...
    10.0.1.101 kafka1.ljk.cn
    10.0.1.102 kafka2.ljk.cn
    10.0.1.103 kafka3.ljk.cn
    10.0.1.101 zk1.ljk.cn # zookeeper地址域名解析

    [root@kakfa1 local]$vim kafka/config/server.properties
    21 broker.id=1 # 每个 broker 在集群中的唯一标识,正整数
    31 listeners=PLAINTEXT://kafka1.ljk.cn:9092
    60 log.dirs=/usr/local/kafka/kafka-logs # kakfa用于保存数据的目录,所有的消都会存储在该目录当中
    65 num.partitions=1 # 设置创建新topic的默认分区数量
    103 log.retention.hours=168 # 设置kafka中消息保留时间,默认为168小时,即7天
    # 指定连接的zookeeper的地址,zk中存储了broker的元数据信息,如果zk是集群,多个zk地址使用逗号分割,这里为了方便,使用单机zookeeper,推荐使用域名,如果使用ip可能无法启动,不知道为什么
    123 zookeeper.connect=zk1.ljk.cn:2181
    126 zookeeper.connection.timeout.ms=6000 # 设置连接zookeeper的超时时间,默认6s

    [root@kakfa2 local]$vim kafka/config/server.properties
    21 broker.id=2
    31 listeners=PLAINTEXT://10.0.1.102:9092
    60 log.dirs=/usr/local/kafka/kafka-logs
    103 log.retention.hours=168
    123 zookeeper.connect=zk1.ljk.cn:2181
    126 zookeeper.connection.timeout.ms=6000

    [root@kakfa3 local]$vim kafka/config/server.properties
    21 broker.id=3
    31 listeners=PLAINTEXT://10.0.1.103:9092
    60 log.dirs=/usr/local/kafka/kafka-logs
    103 log.retention.hours=168
    123 zookeeper.connect=zk1.ljk.cn:2181
    126 zookeeper.connection.timeout.ms=6000
  4. 启动 kafka

    1
    2
    3
    4
    5
    6
    7
    [root@kakfa1 bin]$pwd
    /usr/local/kafka/bin
    [root@kakfa1 bin]$./kafka-server-start.sh -daemon ../config/server.properties

    [root@kakfa2 bin]$./kafka-server-start.sh -daemon ../config/server.properties

    [root@kakfa3 bin]$./kafka-server-start.sh -daemon ../config/server.properties

测试 kafka 读写数据

http://kafka.apache.org/quickstart

创建 topic

1
2
3
4
5
6
[root@kakfa1 bin]$./kafka-topics.sh --create \
--zookeeper zk1.ljk.cn:2181 \
--partitions 3 \
--replication-factor 3 \
--topic lujinkai
Created topic lujinkai.
  • –create:创建 topic
  • –zookeeper:指定 zk 地址,虽然配置文件中已经指定了,但是命令行还要指定
  • –partitions:指定一个 topic 包含几个 partition,就是对 topic 分片,分片可以提高性能,但是一般不用分片,保持默认值 1 就可以,如果分片,也不要超过节点的数量
  • –replication-factor:指定 partition 的副本数量,kafka 实现高可用全靠 partition 的副本,如果设置 3,则一个 partition 就存储 3 份,注意不是 4 份
  • –topic:指定名称

假设集群有 4 个 broker,一个 topic 有 4 个 partition,每个 partition 有 3 个副本。下图是每个 broker 上的副本分配情况:

验证 topic

1
2
3
4
5
6
7
[root@kakfa1 bin]$./kafka-topics.sh --describe \
--zookeeper zk1.ljk.cn:2181 \
--topic lujinkai
Topic: lujinkai PartitionCount: 3 ReplicationFactor: 3 Configs:
Topic: lujinkai Partition: 0 Leader: 3 Replicas: 3,2,1 Isr: 3,2,1
Topic: lujinkai Partition: 1 Leader: 1 Replicas: 1,3,2 Isr: 1,3,2
Topic: lujinkai Partition: 2 Leader: 2 Replicas: 2,1,3 Isr: 2,1,3

说明:lujinkai 这个 topic 有三个分区分别为 0、1、2,分区 0 的 leader 是 3(broker.id),分区 0 有三个副本,并且状态都为 lsr(ln-sync,表示可以参加选举成为 leader)

获取所有 topic

1
2
[root@kakfa1 bin]$./kafka-topics.sh --list --zookeeper zk1.ljk.cn:2181
lujinkai

测试发送消息

1
2
3
4
5
[root@kakfa1 bin]$./kafka-console-producer.sh --topic lujinkai \
--broker-list kafka1.ljk.cn:9092,kafka2.ljk.cn:9092,kafka3.ljk.cn:9092
>hello
>word
>

测试获取消息

1
2
3
4
5
[root@kafka2 bin]$./kafka-console-consumer.sh --topic lujinkai \
--bootstrap-server kafka1.ljk.cn:9092,kafka2.ljk.cn:9092,kafka3.ljk.cn:9092 \
--from-beginning
hello
word
  • –bootstrap-server:kafak 集群的地址,实际只写一个地址也行
  • –from-beginning:从最开始的数据进行消费

删除 topic

1
2
3
4
5
[root@kakfa1 bin]$./kafka-topics.sh --delete \
--topic lujinkai \
--zookeeper zk1.ljk.cn:2181
Topic lujinkai is marked for deletion.
Note: This will have no impact if delete.topic.enable is not set to true.

https://zookeeper.apache.org/

ZooKeeper 是一个分布式服务框架,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:命名服务、状态同步、配置中心、集群管理等

命名服务

命名服务是分布式系统最基本的公共服务之一。在分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等,这些我们都可以统称它们为名字(Name),其中较为常见的就是一些分布式服务框架(如 RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据指定名字来获取资源的实体、服务地址和提供者的信息等

状态同步

每个节点除了存储数据内容和 node 节点状态信息之外,还存储了已经注册的 APP 的状态信息,当有些节点或 APP 不可用,就将当前状态同步给其他服务

配置中心

现在我们大多数应用都是采用的是分布式开发的应用,搭建到不同的服务器上,可以使用 ZooKeeper 来实现配置中心,ZooKeeper 采用的是推拉相结合的方式: 客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送 Watcher 事件通知,客户端接收到这个消息通知后,需要主动到服务端获取最新的数据

集群管理

所谓集群管理,包括 集群监控集群控制 两部分,前者侧重对集群运行时状态的收集,后者则是对集群进行操作与控制

  • 客户端如果对 ZooKeepe 的一个数据节点注册 Watcher 监听,那么当该数据节点的内容或是其子节点列表发生变更时,ZooKeeper 服务器就会向订阅的客户端发送变更通知
  • 对在 ZooKeeper 上创建的临时节点,一旦客户端与服务器之间的会话失效,该临时节点也就被自动除
1
Watcher(事件监听器):Zookeeper中很重要的特性。Zookeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是 Zookeeper实现分布式协调服务的重要特性

0、生产者启动
1、生产者注册至 zookeeper
2、消费者启动并订阅频道
3、zookeeper 通知消费者事件
4、消费者调用生产者
5、监控中心负责统计和监控服务状态

ZooKeeper 单机安装

ZooKeeper 依赖 Java 环境,要求 JDK1.7 及以上

  1. 配置 java 环境

    1
    2
    3
    4
    [root@zk1 ~]$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)
  2. 部署 ZooKeeper
    官网下载地址:https://archive.apache.org/dist/zookeeper/
    注意,要下载带 bin 的包,不带 bin 的只是源码包,不包含必要的 jar 包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@zk1 src]$tar zxf apache-zookeeper-3.5.9-bin.tar.gz
    [root@zk1 src]$mv apache-zookeeper-3.5.9-bin
    [root@zk1 src]$mv apache-zookeeper-3.5.9-bin /usr/local/zookeeper
    [root@zk1 src]$cd /usr/local/zookeeper/conf/
    [root@zk1 conf]$cp zoo_sample.cfg zoo.cfg
    [root@zk1 conf]$grep ^[a-Z] ./zoo.cfg
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/tmp/zookeeper
    clientPort=2181
    admin.serverPort=8180 # AdminServer监听的端口,默认8080,因为tomcat默认端口也是8080,为了避免冲突,这里修改为8180
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

关于AdminServer:https://zookeeper.apache.org/doc/r3.5.0-alpha/zookeeperAdmin.html#sc_adminserver_config

![](//img.to2b.cn/blog/ljk/1614092973758.png)

3. 启动 ZooKeeper

```bash
# zkServer.sh 用于启动、重启、停止 ZooKeeper
[root@zk1 bin]$pwd
/usr/local/zookeeper/bin
[root@zk1 bin]$./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
  1. 验证 Zookeeper 状态

    1
    2
    3
    4
    5
    [root@zk1 bin]$./zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
    Client port found: 2181. Client address: localhost. Client SSL: false.
    Mode: standalone

ZooKeeper 集群简介

集群架构:

集群角色:

Zookeeper 集群部署

整个集群中只要有超过集群数量一半的 zookeeper 工作只正常的,那么整个集群对外就是可用的

1
2
3
zk1.ljk.cn:10.0.1.101
zk2.ljk.cn:10.0.1.102
zk3.ljk.cn:10.0.1.103
  1. 各个节点安装 zookeeper,参考上面单机部署,但是先不要启动

  2. 各个节点创建数据目录

    1
    2
    3
    4
    5
    6
    7
    8
    [root@zk1 ~]$cd /usr/local/zookeeper/
    [root@zk1 zookeeper]$mkdir data

    [root@zk2 ~]$cd /usr/local/zookeeper/
    [root@zk2 zookeeper]$mkdir data

    [root@zk3 ~]$cd /usr/local/zookeeper/
    [root@zk3 zookeeper]$mkdir data
  3. 各个节点配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@zk1 conf]$grep ^[a-Z] ./zoo.cfg
    tickTime=2000 # 心跳检测周期,单位为毫秒
    initLimit=10 # leader与follower初始连接心跳次数,即多少个2000毫秒
    syncLimit=5 # leader与follower连接完成之后,后期检测发送和应答的心跳次数,即该follower在5*2000毫秒内不能与leader进行通信,那么此follower将被视为不可用
    dataDir=/usr/local/zookeeper/data # 自定义的zookeeper保存数据的目录
    clientPort=2181 # 客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求
    maxClientCnxns=128 # 单个客户端IP可以和zookeeper保持的连接数
    autopurge.snapRetainCount=3 # 保存快照的数量,启用后ZooKeeper只保存最新的几个快照,其余的会自动清除,最新快照和相应的事务日志分别保留在dataDir和dataLogDir中,默认值为 3。最小值为 3
    autopurge.purgeInterval=1 # 自动清理日志和快照文件的频率,单位小时,默认0,不开启自动清理
    server.1=10.0.1.101:2888:3888 # server.服务器编号=服务器IP:LF数据同步端口:LF选举端口
    server.2=10.0.1.102:2888:3888 # hostname也可以,只要dns能解析到就行
    server.3=10.0.1.103:2888:3888

    [root@zk1 conf]$scp ./zoo.cfg 10.0.1.102:/usr/local/zookeeper/conf
    [root@zk1 conf]$scp ./zoo.cfg 10.0.1.103:/usr/local/zookeeper/conf
  4. 集群 id

    1
    2
    3
    4
    5
    [root@zk1 data]$echo 1 > myid

    [root@zk2 data]$echo 2 > myid

    [root@zk3 data]$echo 3 > myid
  5. 启动 zookeeper

    1
    2
    3
    4
    5
    [root@zk1 bin]$./zkServer.sh start

    [root@zk2 bin]$./zkServer.sh start

    [root@zk3 bin]$./zkServer.sh start
  6. 验证 Zookeeper 状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    [root@zk1 bin]$./zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
    Client port found: 2181. Client address: localhost. Client SSL: false.
    Mode: follower # follower

    [root@zk2 bin]$./zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
    Client port found: 2181. Client address: localhost. Client SSL: false.
    Mode: follower # follower

    [root@zk3 bin]$./zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
    Client port found: 2181. Client address: localhost. Client SSL: false.
    Mode: leader # leader

zookeeper 集群选举过程

节点角色状态:

1
2
3
4
LOOKING   # 寻找 Leader 状态,处于该状态需要进入选举流程
LEADING # 领导者状态,处于该状态的节点说明是角色已经是 Leader
FOLLOWING # 跟随者状态,表示 Leader 已经选举出来,当前节点角色是 follower
OBSERVER # 观察者状态,表明当前节点角色是 observer

选举 ID:

1
2
ZXID # zookeeper transaction id,每个改变Zookeeper状态的操作都会形成一个对应的zxid
myid # 服务器的唯一标识(SID),通过配置myid文件指定,集群中唯一

zookeeper 数据增删改查

任意一台 zookeeper 节点进行以下操作:

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
[root@zk1 bin]$./zkCli.sh -server 10.0.1.102:2181
...
Welcome to ZooKeeper!
JLine support is enabled
[zk: 10.0.1.102:2181(CONNECTING) 0] help
ZooKeeper -server host:port cmd args
addauth scheme auth
close
config [-c] [-w] [-s]
connect host:port
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
delete [-v version] path
deleteall path
delquota [-n|-b] path
get [-s] [-w] path
getAcl [-s] path
history
listquota path
ls [-s] [-w] [-R] path
ls2 path [watch]
printwatches on|off
quit
reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]
redo cmdno
removewatches path [-c|-d|-a] [-l]
rmr path
set [-s] [-v version] path data
setAcl [-s] [-v version] [-R] path acl
setquota -n|-b val path
stat [-w] path
sync path

zookeeper 客户端 ZooInspector

Linux:https://github.com/zzhang5/zooinspector
Windows:https://www.cnblogs.com/weiyiming007/p/11951591.html

  1. 下载、解压、双击 build/zookeeper-dev-ZooInspector.jar
  2. 连接成功

阿里云消息队列:https://www.aliyun.com/product/ons

RabbitMQ 基于 erlang 语言开发,具有高并发优点、支持分布式具有消息确认机制、消息持久化机制,消息可靠性和集群可靠性高,简单易用、运行稳定、跨平台、多语言开源

Broker:接收和分发消息的应用,RabbitMQ Server 就是 Message Broker
Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念,当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个 vhost,每个用户在自己的 vhost 创建 exchange/queue 等
Connection:publisher/consumer 和 broker 之间的 TCP 连接。
Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection 的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯,AMQP method 包含了 channel id 帮助客户端和 message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销。
Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到 queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。
Queue:消息最终被送到这里等待 consumer 取走,先进先出,可以持久化到磁盘节点服务器
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据。

RabbitMQ 中的生产者消费者示例

生产者发送消息到 broker server(RabbitMQ),在 Broker 内部,用户创建 Exchange/Queue,通过 Binding 规则将两者联系在一起,Exchange 分发消息,根据类型/binding 的不同分发策略有区别,消息最后来到 Queue 中,等待消费者取走

RabbitMQ 单机部署

快速部署示例:https://www.rabbitmq.com/install-debian.html#apt-bintray-quick-start

  1. 主机名解析

    1
    2
    3
    4
    5
    6
    7
    [root@mq1 ~]$hostname
    mq1.ljk.cn
    [root@mq1 ~]$hostname -I
    10.0.1.101
    [root@mq1 ~]$vim /etc/hosts
    ...
    10.0.1.101 mq1.ljk.cn mq1
  2. 时间原因,选择 apt 安装,直接执行以下脚本

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

    ## If sudo is not available on the system,
    ## uncomment the line below to install it
    # apt-get install -y sudo

    sudo apt-get update -y

    ## Install prerequisites
    sudo apt-get install curl gnupg -y

    ## Install RabbitMQ signing key
    curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo apt-key add -

    ## Install apt HTTPS transport
    sudo apt-get install apt-transport-https

    $distribution='bionic' # ubuntu18.04
    sudo tee /etc/apt/sources.list.d/bintray.rabbitmq.list <<EOF
    ## 默认安装最新版本的 erlang 23.x,可以指定版本例如:erlang-22.x
    deb https://dl.bintray.com/rabbitmq-erlang/debian $distribution erlang
    ## Installs latest RabbitMQ release
    deb https://dl.bintray.com/rabbitmq/debian $distribution main
    EOF

    ## Update package indices
    sudo apt-get update -y

    ## Install rabbitmq-server and its dependencies
    sudo apt-get install rabbitmq-server -y --fix-missing
  3. 安装成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [root@zabbix src]$systemctl status rabbitmq-server.service
    ● rabbitmq-server.service - RabbitMQ broker
    Loaded: loaded (/lib/systemd/system/rabbitmq-server.service; enabled; vendor preset: enabled)
    Active: active (running) since Sun 2021-02-21 02:41:45 UTC; 1h 26min ago
    Main PID: 5992 (beam.smp)
    Status: "Initialized"
    Tasks: 22 (limit: 2289)
    CGroup: /system.slice/rabbitmq-server.service
    ├─5992 /usr/lib/erlang/erts-11.1.8/bin/beam.smp -W w -MBas ageffcbf -MHas ageffcbf -MBlmbcs 512 -MHlmbcs 512 -MMmcs 30 -P 1048576 -t 5000000 -stbt db -zd
    ├─6013 erl_child_setup 32768
    ├─6039 /usr/lib/erlang/erts-11.1.8/bin/epmd -daemon
    ├─6058 inet_gethost 4
    └─6059 inet_gethost 4
    ...
  4. 插件管理
    https://www.rabbitmq.com/management.html
    开启 web 管理界面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    [root@zabbix src]$which rabbitmq-plugins
    /usr/sbin/rabbitmq-plugins
    [root@zabbix src]$rabbitmq-plugins enable rabbitmq_management
    Enabling plugins on node rabbit@mq1:
    rabbitmq_management
    The following plugins have been configured:
    rabbitmq_management
    rabbitmq_management_agent
    rabbitmq_web_dispatch
    Applying plugin configuration to rabbit@mq1...
    The following plugins have been enabled:
    rabbitmq_management
    rabbitmq_management_agent
    rabbitmq_web_dispatch

    started 3 plugins.

    # 5672 消费者访问的 端口
    # 15672 web 管理端口
    # 25672 集群状态通信端口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@mq1 ~]$rabbitmqctl list_users
    Listing users ...
    user tags
    guest [administrator] # guest是初始化的用户,账号密码都是guest,但是只能使用localhost访问

    [root@mq1 ~]$rabbitmqctl add_user ljk 123456 # 添加用户
    Adding user "ljk" ...
    Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more.
    [root@mq1 ~]$rabbitmqctl set_user_tags ljk administrator # 用户授权
    Setting tags for user "ljk" to [administrator] ...
    [root@mq1 ~]$rabbitmqctl list_users
    Listing users ...
    user tags
    ljk [administrator] # 授权成功
    guest [administrator]

    使用账号 ljk,密码 123456,成功登录 web 管理界面:

    登录之后,我们很少进行更改,主要是查看 rabbitmq 的运行状态

RabbitMQ 集群部署

  • 普通模式:创建好 RabbitMQ 集群之后的默认模式
    所有的 mq 节点保存相同的元数据,即消息队列,但队列里的数据仅保存一份,消费者从 A 节点拉取消息,如果消息在 B 节点,那么 B 会将消息发送给 A 节点,然后消费者就能拉取到数据了
    缺点:单点失败
  • 镜像模式:把需要的队列做成镜像队列
    在普通模式的基础上,增加一些镜像策略,消息实体会主动在镜像节点间同步,而不是在 consumer 取数据时临时拉取,解决了单点失败的问题,但是性能下降,增加集群内部网络消耗一个
    队列想做成镜像队列,需要先设置 policy,然后客户端创建队列的时候,rabbitmq 集群根据“队列名称”自动设置是普通集群模式或镜像队列

推荐设计架构:

在一个 rabbitmq 集群里,有 3 台或以上机器,其中 1 台使用磁盘模式(数据保存到内存和磁盘),其它节点使用内存模式(数据只保存到内存),使用磁盘模式的节点,作为数据备份使用

Ubuntu 1804 安装集群版 RabbitMQ

1
2
3
10.0.1.101 mq1.ljk.cn mq1
10.0.1.102 mq1.ljk.cn mq2
10.0.1.103 mq1.ljk.cn mq3

Rabbitmq 的集群是依赖于 erlang 的集群来工作的,所以必须先构建起 erlang 的集群环境,而 Erlang 的集群中各节点是通过一个 magic cookie 来实现的,这个 cookie 存放在 /var/lib/rabbitmq/.erlang.cookie 中,文件是 400 的权限,所以必须保证各节点 cookie 保持一致,否则节点之间就无法通信

  1. 各节点安装 RabbitMQ 并安装插件,参考上面单机部署

  2. 各服务器关闭 RabbitMQ

    1
    2
    3
    [root@mq1 ~]$systemctl stop rabbitmq-server.service
    [root@mq2 ~]$systemctl stop rabbitmq-server.service
    [root@mq3 ~]$systemctl stop rabbitmq-server.service
  3. 在 mq1 同步.erlang.cookie 至其他两台服务器

    1
    2
    3
    [root@mq1 ~]$cd /var/lib/rabbitmq/
    [root@mq1 rabbitmq]$scp .erlang.cookie 10.0.1.102:/var/lib/rabbitmq
    [root@mq1 rabbitmq]$scp .erlang.cookie 10.0.1.103:/var/lib/rabbitmq
  4. 各服务器启动 RabbitMQ

    1
    2
    3
    [root@mq1 ~]$systemctl start rabbitmq-server.service
    [root@mq2 ~]$systemctl start rabbitmq-server.service
    [root@mq3 ~]$systemctl start rabbitmq-server.service
  5. 查看当前集群状态

    1
    2
    3
    [root@mq1 ~]$rabbitmqctl cluster_status
    [root@mq2 ~]$rabbitmqctl cluster_status
    [root@mq3 ~]$rabbitmqctl cluster_status
  6. 创建 RabbitMQ 集群:mq1、mq2 作为内存节点,mq3 作为磁盘节点

    1
    2
    3
    4
    [root@mq1 ~]$rabbitmqctl stop_app  #停止 app 服务
    [root@mq1 ~]$rabbitmqctl reset #清空元数据
    [root@mq1 ~]$rabbitmqctl join_cluster rabbit@mq3 --ram #将mq1添加到集群中,并成为内存节点,不加--ram默认是磁盘节点
    [root@mq1 ~]$rabbitmqctl start_app #启动 app 服务
    1
    2
    3
    4
    [root@mq2 ~]$rabbitmqctl stop_app
    [root@mq2 ~]$rabbitmqctl reset
    [root@mq2 ~]$rabbitmqctl join_cluster rabbit@mq3 --ram
    [root@mq2 ~]$rabbitmqctl start_app
  7. 将集群设置为镜像模式,任意节点执行以下命令

    1
    [root@mq1 ~]$rabbitmqctl set_policy ha-all "#" '{"ha-mode":"all"}'
  8. 查看集群状态

    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
    [root@mq1 ~]$rabbitmqctl cluster_status
    Cluster status of node rabbit@mq1 ...
    Basics

    Cluster name: rabbit@mq3.ljk.cn

    Disk Nodes

    rabbit@mq3

    RAM Nodes

    rabbit@mq1
    rabbit@mq2

    Running Nodes

    rabbit@mq1
    rabbit@mq2
    rabbit@mq3

    Versions

    rabbit@mq1: RabbitMQ 3.8.12 on Erlang 23.2.5
    rabbit@mq2: RabbitMQ 3.8.12 on Erlang 23.2.5
    rabbit@mq3: RabbitMQ 3.8.12 on Erlang 23.2.5

    Maintenance status

    Node: rabbit@mq1, status: not under maintenance
    Node: rabbit@mq2, status: not under maintenance
    Node: rabbit@mq3, status: not under maintenance

    Alarms

    (none)

    Network Partitions

    (none)

    Listeners

    Node: rabbit@mq1, interface: [::], port: 15672, protocol: http, purpose: HTTP API
    Node: rabbit@mq1, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
    Node: rabbit@mq1, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
    Node: rabbit@mq2, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
    Node: rabbit@mq2, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
    Node: rabbit@mq2, interface: [::], port: 15672, protocol: http, purpose: HTTP API
    Node: rabbit@mq3, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
    Node: rabbit@mq3, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
    Node: rabbit@mq3, interface: [::], port: 15672, protocol: http, purpose: HTTP API

    Feature flags

    Flag: drop_unroutable_metric, state: enabled
    Flag: empty_basic_get_metric, state: enabled
    Flag: implicit_default_bindings, state: enabled
    Flag: maintenance_mode_status, state: enabled
    Flag: quorum_queue, state: enabled
    Flag: user_limits, state: enabled
    Flag: virtual_host_metadata, state: enabled

RabbitMQ 常用命令

  • 创建 vhost

    1
    2
    [root@mq1 ~]$rabbitmqctl add_vhost test
    Adding vhost "test" ...
  • 列出所有 vhost

    1
    2
    3
    4
    5
    [root@mq1 ~]$rabbitmqctl list_vhosts
    Listing vhosts ...
    name
    /
    test
  • 列出所有队列

    1
    2
    3
    [root@mq1 ~]$rabbitmqctl list_queues
    Timeout: 60.0 seconds ...
    Listing queues for vhost / ...
  • 删除指定 vhost

    1
    2
    [root@mq1 ~]$rabbitmqctl delete_vhost test
    Deleting vhost "test" ...
  • 添加账户 jack 密码为 123456

    1
    2
    3
    [root@mq1 ~]$rabbitmqctl add_user jack 123456
    Adding user "jack" ...
    Done. Don't forget to grant the user permissions to some virtual hosts! See 'rabbitmqctl help set_permissions' to learn more.
  • 更改用户密码

    1
    2
    [root@mq1 ~]$rabbitmqctl change_password jack 654321
    Changing password for user "jack" ...
  • 设置 jack 用户对 test 的 vhost 有读写权限,三个点为配置正则、读和写

    1
    [root@mq1 ~]$rabbitmqctl set_permissions -p test jack ".*" ".*" ".*"

RabbitMQ API

RabbitMQ Cluster Monitor

集群状态监控

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
#!/bin/env python
# coding:utf-8
#Author: ZhangJie
import subprocess

running_list = []
error_list = []
false = "false"
true = "true"


def get_status():
obj = subprocess.Popen(
("curl - s - u guest: guest http: // localhost: 15672/api/nodes & > / dev/null"), shell=True, stdout=subprocess.PIPE)

data = obj.stdout.read()
data1 = eval(data)
for i in data1:
if i.get("running") == "true":
running_list.append(i.get("name"))
else:
error_list.append(i.get("name"))


def count_server():
if len(running_list) < 3: # 可以判断错误列表大于 0 或者运行列表小于 3,3未总计的节点数量
print(101) # 100 就是集群内有节点运行不正常了
else:
print(50) # 50 为所有节点全部运行正常


def main():
get_status()
count_server()


if __name__ == "__main__":
main()

内存使用监控

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
# cat rabbitmq_memory.py
#!/bin/env python
# coding:utf-8
#Author: ZhangJie
import subprocess
import sys

running_list = []
error_list = []
false = "false"
true = "true"


def get_status():
obj = subprocess.Popen(
("curl - s - u guest: guest http: // localhost: 15672/api/nodes & > / dev/null"), shell=True, stdout=subprocess.PIPE)

data = obj.stdout.read()
data1 = eval(data)
# print(data1)
for i in data1:
if i.get("name") == sys.argv[1]:
print(i.get("mem_used"))


def main():
get_status()


if __name__ == "__main__":
main()

root@mq-server3: ~ # python3 rabbitmq_memory.py rabbit@mq-server1
85774336
root@mq-server3: ~ # python3 rabbitmq_memory.py rabbit@mq-server2
91099136
root@mq-server3: ~ # python3 rabbitmq_memory.py rabbit@mq-server3
96428032

MQ:Message Queuing,消息队列

Message Queue 的需求由来已久,在 19 世纪 80 年代金融交易中,美国高盛等公司采用 Teknekron 公司的产品,当时的 Message queuing 软件叫做(the informationbus(TIB),后来 TIB 被电信和通讯等公司采用,然后路透社收购了 Teknekron 公司,再然后 IBM 公司开发了 MQSeries,并且微软也开发了 Microsoft Message Queue(MSMQ),但是这些商业 MQ 供应商的问题是厂商锁定及使用价格高昂,于是 2001 年,Java Message queuing 试图解决锁定和交互性的问题,但对应用来说反而更加麻烦了,于是 2004 年,摩根大通和 iMatrix 开始着手 Advanced MessageQueuing Protocol (AMQP)开放标准的开发,2006 年,AMQP 规范发布,2007 年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ1.0 发布。

消息队列的目的是实现各个 APP 之间的通信(包括跨网咯通信),APP 基于 MQ 实现消息的发送和接收,实现应用程序之间的通信,实现业务的解耦的异步机制

消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构,提升开发效率和系统稳定性。消息队列主要具有以下优势:

  • 削峰填谷:主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题
  • 系统解耦:解决不同重要程度、不同能力级别系统之间依赖导致一死全死
  • 提升性能:当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统
  • 蓄流压测:线上有些链路不好压测,可以通过堆积一定量消息再放开来压测

目前主流的消息队列软件有 RabbitMQ、kafka、ActiveMQ、RocketMQ 等,还有小众的消息队列软件如 ZeroMQ、Apache Qpid 等

微服务这个概念是从单体服务(单体应用)演化而来的

微服务:micro server,把单体服务拆分成多个小服务,这些小服务就是微服务,每个小服务运行在单独的运行环境,早期一般用虚拟机,现在都是容器(docker + k8s)

  • 微服务落地:容器,k8s + docker
  • 微服务发现对方:注册中心、服务发现,zookeeper
  • 微服务之间相互调用:API
  • 微服务扩容:服务治理,k8s 实现服务编排
  • 微服务监控:监控微服务的 API 等
  • 微服务代码升级和回滚:CI / CD jenkings + gitlab
  • 微服务日志查看:ELK,统一收集和展示

服务是相互调用的,一个服务可以即是服务提供方,同时又是服务消费方

微服务框架

这个开发比较关注微服务框架,作为运维,了解即可

spring boot

spring cloud

dubbo

阿里云微服务:https://promotion.aliyun.com/ntms/act/edasdubbo.html
dubbo 官网:https://dubbo.apache.org/zh/
阿里云 dubbo 简介:https://help.aliyun.com/document_detail/99299.html

dubbo 架构

https://dubbo.apache.org/zh/docs/v2.7/user/preface/architecture/

节点角色说明:

节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器

调用关系说明:

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。

连通性
  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
  • 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
  • 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
健壮性
  • 监控中心宕掉不影响使用,只是丢失部分采样数据
  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
  • 服务提供者无状态,任意一台宕掉后,不影响使用
  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
伸缩性
  • 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
  • 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
升级性

当服务集群规模进一步扩大,带动 IT 治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:

节点角色说明:

节点 角色说明
Deployer 自动部署服务的本地代理
Repository 仓库用于存储服务应用发布包
Scheduler 调度中心基于访问压力自动增减服务提供者
Admin 统一管理控制台
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心

示例

生产者示例

zk 配置:https://dubbo.apache.org/zh/docs/v2.7/user/references/registry/zookeeper/

注册中心除了 zookeeper,还有其他方式:Nacos、Multicast、Redis、Simple

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
[root@provider1 src]$tar zxf dubbo-demo-provider-2.1.5-assembly.tar.gz # 开发写的包
[root@provider1 src]$cd dubbo-demo-provider-2.1.5/
[root@provider1 dubbo-demo-provider-2.1.5]$ls
bin conf lib

[root@provider1 dubbo-demo-provider-2.1.5]$vim conf/dubbo.properties
dubbo.container=log4j,spring
dubbo.application.name=demo-provider
dubbo.application.owner=
# dubbo.registry.address=zookeeper://10.0.1.101:2181 | zookeeper://10.0.1.102:2181 | zookeeper://10.0.1.103:2181 # 注册中心:zookeeper,z集群k
dubbo.registry.address=zookeeper://10.0.1.101:2181 # 注册中心:zookeeper,单zk
dubbo.monitor.protocol=registry
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.log4j.file=logs/dubbo-demo-provider.log
dubbo.log4j.level=WARN

[root@provider1 dubbo-demo-provider-2.1.5]$./bin/start.sh # 启动provider,可能需要几分钟
Starting the demo-provider ........OK!
PID: 2813
STDOUT: logs/stdout.log

[root@provider1 dubbo-demo-provider-2.1.5]$tail -f logs/stdout.log # 查看日志
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: UseCMSCompactAtFullCollection is deprecated and will likely be removed in a future release.
log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[2021-02-23 13:25:48] Dubbo service server started! # 启动成功

消费者示例

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
[root@consumer1 src]$tar zxf dubbo-demo-consumer-2.1.5-assembly.tar.gz # 开发写的包
[root@consumer1 src]$cd dubbo-demo-consumer-2.1.5/
[root@consumer1 dubbo-demo-consumer-2.1.5]$vim conf/dubbo.properties
dubbo.container=log4j,spring
dubbo.application.name=demo-consumer
dubbo.application.owner=
dubbo.registry.address=zookeeper://10.0.1.101:2181
dubbo.monitor.protocol=registry
dubbo.log4j.file=logs/dubbo-demo-consumer.log
dubbo.log4j.level=WARN
[root@kafka3 dubbo-demo-consumer-2.1.5]$./bin/start.sh # 启动consumer
Starting the demo-consumer ....OK!
PID: 2498
STDOUT: logs/stdout.log

[root@consumer1 dubbo-demo-consumer-2.1.5]$tail -f logs/stdout.log # 查看日志
[13:33:22] Hello world23, response form provider: 10.0.1.101:20880
[13:33:24] Hello world24, response form provider: 10.0.1.101:20880
[13:33:26] Hello world25, response form provider: 10.0.1.101:20880
[13:33:28] Hello world26, response form provider: 10.0.1.101:20880


[root@provider1 dubbo-demo-provider-2.1.5]$tail -f logs/stdout.log
[13:34:18] Hello world51, request from consumer: /10.0.1.103:13243
[13:34:20] Hello world52, request from consumer: /10.0.1.103:13243
[13:34:22] Hello world53, request from consumer: /10.0.1.103:13243
[13:34:24] Hello world54, request from consumer: /10.0.1.103:13243
[13:34:26] Hello world55, request from consumer: /10.0.1.103:13243
[13:34:28] Hello world56, request from consumer: /10.0.1.103:13243

以上示例是一个 provider,如果是多个 provider,则 consumer 会轮询读取

zookeeper 验证

dubbo admin

基于 zookeeper 发现并管理 provider 和 consumer

1
2
3
4
5
6
[root@kakfa1 webapps]$systemctl stop tomcat.service  # 先关闭tomcat,否则自动解压会出错
[root@kakfa1 webapps]$unzip dubboadmin.war -d dubboadmin # 别人编译好的包,手动解压
[root@kakfa1 webapps]$vim dubboadmin/WEB-INF/dubbo.properties # 配置文件
dubbo.registry.address=zookeeper://10.0.1.101:2181 # zookeeper地址
dubbo.admin.root.password=root # root用户,账号密码都是root
dubbo.admin.guest.password=guest # 访客

这个 duboadmin 版本比较老,如果需要新版的 dubboadmin,需要手动编译

微服务编译

https://dubbo.apache.org/zh/docs/v2.7/dev/build/

maven

编译 c、c++,使用 make、cmake,编译 java,使用 maven

maven 是一个项目管理工具,可以对 Java 项目进行构建、解决打包依赖等

POM:Project Object Model,项目对象模型,是 Maven 工程的基本工作单元,是一个 XML 文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖等,在执行任务或目标时,Maven 会在当前目录中查找 pom 文件,通过读取 pom 文件获取所需的配置信息,然后执行目标

Pom 文件中可以指定以下配置:

1
2
3
4
5
6
7
项目依赖
插件
执行目标
项目构建 profile
项目版本
项目开发者列表
相关邮件列表信息

部署 Maven:

1
2
3
4
5
6
7
8
# 官方文档:http://maven.apache.org/install.html
# 官方源:https://archive.apache.org/dist/maven/maven-3/
# 清华源:https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/

[root@kakfa1 src]$wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
[root@kakfa1 src]$tar zxf apache-maven-3.6.3-bin.tar.gz
[root@kakfa1 src]$cd apache-maven-3.6.3/
[root@kakfa1 apache-maven-3.6.3]$export PATH=`pwd`/bin:$PATH

Maven 打包命令:

  1. 进入到包含有“pom.xml”的路径,执行:

    1
    mvn clean install package
  2. 有的时候受到测试的干扰,导致无法正在进行编译,这时候可以选择跳过测试:

    1
    2
    3
    4
    mvn clean install package -Dmaven.test.skip=true

    -Dmaven.test.skip=true # 跳过测试,并且不编译测试下的源代码;
    -DskipTests # 不执行测试,但是会进行测试代码的编译;
  3. 如果需要编译的代码异常庞大,需要考虑对编译环境做一些处理,提高编译效率

    1
    2
    3
    mvn -T 4 clean install package -Dmaven.test.skip=true  # 启动多线程编译
    mvn -T 2C clean install package -Dmaven.test.skip=true # 分配编译的CPU个数
    mvn clean install package -Dmaven.test.skip=true -Dmaven.compile.fork=true # 启用多线程编译
  4. 所有的 Maven 都是建立在 JVM 上的,所以进行编译的时候还需要考虑 JVM 参数优化

    1
    2
    3
    # bin/mvn是shell文件,配置参数:“MAVEN_OPTS”
    $ export MAVEN_OPTS="-Xmx6g -Xms6g" >> /etc/profile
    $ . /etc/profile

示例

以 dubbo admin 为例:https://github.com/apache/dubbo-admin/blob/develop/README_ZH.md

  1. 下载代码:

    1
    $ git clone https://github.com/apache/dubbo-admin.git
  2. 指定注册中心地址

    1
    2
    3
    4
    5
    6
    7
    $ vim dubbo-admin-server/src/main/resources/application.properties
    ...
    # centers in dubbo2.7
    admin.registry.address=zookeeper://10.0.1.101:2181
    admin.config-center=zookeeper://10.0.1.101:2181
    admin.metadata-report.address=zookeeper://10.0.1.101:2181
    ...
  3. 构建:

    1
    $ mvn clean package -Dmaven.test.skip=true
  4. 启动:

    1
    2
    3
    4
    $ mvn --projects dubbo-admin-server spring-boot:run
    # 或
    $ cd dubbo-admin-distribution/target;
    $ java -jar dubbo-admin-0.2.0.jar --server.port=8080 # 注意端口冲突
  5. 访问:浏览器访问 http://10.0.1.101:8080

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
# vim /apps/zabbix_proxy/etc/zabbix_proxy.conf
ProxyMode=1 #0为主动,1为被动
Server=10.0.58.101 #zabbix server服务器的地址或主机名
Hostname=magedu-jiege-proxy-active #代理服务器名称,与zabbix server添加代理时候的proxy name一致
ListenPort=10051 #zabbix proxy监听端口
LogFile=/tmp/zabbix_proxy.log
PidFile=/apps/zabbix_proxy/run/zabbix_proxy.pid
EnableRemoteCommands=1 #允许zabbix server执行远程命令
DBHost=10.0.58.104 #数据库服务器地址
DBName=zabbix_proxy_active #使用的数据库名称
DBUser=proxy #连接数据库的用户名称
DBPassword=123456 #数据库用户密码
DBPort=3306 #数据库端口
ProxyLocalBuffer=720 #已经提交到zabbix server的数据保留时间
ProxyOfflineBuffer=720 #未提交到zabbix server的时间保留时间
HeartbeatFrequency=60 #心跳间隔检测时间,默认60秒,范围0-3600秒,被动模式不使用
ConfigFrequency=5 #间隔多少秒从zabbix server获取监控项信息
DataSenderFrequency=5 #数据发送时间间隔,默认为1秒,范围为1-3600秒,被动模式不使用
StartPollers=20 #启动的数据采集器数量
JavaGateway=10.0.58.104 #java gateway服务器地址,当需要监控java的时候必须配置否则监控不到数据
JavaGatewayPort=10052 #Javagatewa服务端口
StartJavaPollers=20 #启动多少个线程采集数据
CacheSize=2G #保存监控项而占用的最大内存
HistoryCacheSize=2G #保存监控历史数据占用的最大内存
HistoryIndexCacheSize=128M #历史索引缓存的大小
Timeout=30 #监控项超时时间,单位为秒f
LogSlowQueries=3000 #毫秒,多久的数据库查询会被记录到日志
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
ProxyMode=0   # proxy模式,0:主动模式;1:被动模式
Server=10.0.1.21 # zabbix-server的ip地址,被动模式的监控项使用
ServerPort=10051 # zabbix-server的端口号,默认10051,被动模式下,此参数忽略
Hostname=proxy-qinagdao-active # 在zabbix-server上用于区分proxy,通常设置为ip或主机名
HostnameItem=system.hostname # 功能和hostname一样,可以设置zabbix的键值,所以更方便
ListenPort=10051 # zabbix-proxy监听的本机端口
SourceIP= # 当服务器上有多个ip地址,可以指定使用哪个ip发起请求,一般不用配置

# 日志相关
LogType=file
LogFile=/tmp/zabbix_proxy.log
LogFileSize=0
DebugLevel=3

EnableRemoteCommands=0 # 是否允许远程命令

PidFile=/tmp/zabbix_proxy.pid
SocketDir=/tmp

# 数据库相关
DBHost=localhost
DBName=zabbix_proxy
DBSchema=
DBUser=zabbix
DBPassword=
DBSocket=
DBPort=

######### PROXY SPECIFIC PARAMETERS #############
ProxyLocalBuffer=24 # 发送成功的数据在本地保留多久,单位小时,0-172,也就是最多保留30天
ProxyOfflineBuffer=720 # 未发送成功的数据在本地保留多久,单位小时,0-172,也就是最多保留30天

HeartbeatFrequency=60 # 主动模式下,心跳检测zabbxi-server是否在线,0-3600,单位秒
ConfigFrequency=300 # 主动模式下,,配置的更新周期,1-3600*24*7,单位秒,这个参数建议设置为5分钟
DataSenderFrequency=60 # 数据向zabbix-server的推送周期,1-3600,单位秒

############ 高级参数 ################ 以下参数和 zabbix_server.conf 类似
StartPollers=5

# StartIPMIPollers=0
# StartPollersUnreachable=1
# StartTrappers=5
# StartPingers=1
# StartDiscoverers=1
# StartHTTPPollers=1
# JavaGateway=
# JavaGatewayPort=10052
# StartJavaPollers=0
# StartVMwareCollectors=0
# VMwareFrequency=60
# VMwarePerfFrequency=60
# VMwareCacheSize=8M
# VMwareTimeout=10
# SNMPTrapperFile=/tmp/zabbix_traps.tmp
# StartSNMPTrapper=0
# ListenIP=0.0.0.0
# HousekeepingFrequency=1
# CacheSize=8M
# StartDBSyncers=4
# HistoryCacheSize=16M
# HistoryIndexCacheSize=4M
Timeout=30
# TrapperTimeout=300
# UnreachablePeriod=45
# UnavailableDelay=60
# UnreachableDelay=15
# ExternalScripts=${datadir}/zabbix/externalscripts
# FpingLocation=/usr/sbin/fping
# Fping6Location=/usr/sbin/fping6
# SSHKeyLocation=
# LogSlowQueries=0
LogSlowQueries=3000
# TmpDir=/tmp
# AllowRoot=0
# User=zabbix
# Include=/usr/local/etc/zabbix_proxy.general.conf
# Include=/usr/local/etc/zabbix_proxy.conf.d/
# Include=/usr/local/etc/zabbix_proxy.conf.d/*.conf
# SSLCertLocation=${datadir}/zabbix/ssl/certs
# SSLKeyLocation=${datadir}/zabbix/ssl/keys
# SSLCALocation=
# StatsAllowedIP=
# LoadModulePath=${libdir}/modules
# LoadModule=
# TLSConnect=unencrypted
# TLSAccept=unencrypted
# TLSCAFile=
# TLSCRLFile=
# TLSServerCertIssuer=
# TLSServerCertSubject=
# TLSCertFile=
# TLSKeyFile=
# TLSPSKIdentity=
# TLSPSKFile=
# TLSCipherCert13=
# TLSCipherCert=
# TLSCipherPSK13=
# TLSCipherPSK=
# TLSCipherAll13=
# TLSCipherAll=

大部分同 zabbix_server.conf,以下是不同的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
EnableRemoteCommands=0 # 0/1,是否允许执行远程命令
LogRemoteCommands=0 # 0/1,是否记录远程命令日志
Server=10.0.1.21 # 被动模式,设置为server或proxy的地址,或者都设置,允许10.0.1.21来请求数据
ListenPort=10050 # zabbix_agent监听的本地端口
StartAgents=3 # 0-100,预启动进程数,执行被动检查,默认3个足够了
ServerActive=10.0.1.21 # 主动模式,设置为server或proxy的地址,或者都设置,向10.0.1.21发送数据
Hostname=10.0.1.26 # 主机名称,zabbix_server添加主机时需要此项,通常设置为ip或主机名
HostnameItem=system.hostname # 主机名称,和Hostname冲突,该项是动态的,其支持通过key进行配置,只要是zabbix_get能正常取的数据的key理论都是支持的(包含特殊字符的不支持)
HostMetadata= # 0-255 characters,自动注册使用,静态配置
HostMetadataItem= # 自动注册使用,动态配置

RefreshActiveChecks=120 # 主动模式下,每120秒主动提供一次数据

BufferSend=5 # 数据缓冲的时间
BufferSize=100 # zabbix agent数据缓冲区的大小,当达到该值便会发送所有的数据到zabbix server
MaxLinesPerSecond=20 # zabbix agent发送给zabbix server最大的数据行,1-1000

####### 用户定义的监控参数 #######
UnsafeUserParameters=0 # 0/1,是否允许所有字符参数的传递
UserParameter= # 指定用户自定义参数,可以定义多个,如果要传参,使用[*]
UserParameter=tcp_status[*],/bin/bash /usr/local/zabbix/etc/UserParameter/tcp_conn.sh $1 $2
UserParameter=memcache_status[*],/usr/local/zabbix/etc/UserParameter/memcached_status.sh "$1" "$2" "$3"
UserParameter=redis_status[*],/usr/local/zabbix/etc/UserParameter/redis_status.sh $1 $2 $3
UserParameter=nginx_status[*],/usr/local/zabbix/etc/UserParameter/nginx_status.sh $1 $2 $3
UserParameter=linux41[*],/bin/bash /usr/local/zabbix/etc/UserParameter/linux41.sh $1 $2 $3
UserParameter=qinghe,/usr/bin/python3 /usr/local/zabbix/etc/UserParameter/linux41.py
UserParameter=tcp_status[*],/bin/bash /usr/local/zabbix/etc/UserParameter/tcp_conn.sh $1 $2
UserParameter=memcache_status[*],/usr/local/zabbix/etc/UserParameter/memcached_status.sh "$1" "$2" "$3"
UserParameter=redis_status[*],/usr/local/zabbix/etc/UserParameter/redis_status.sh $1 $2 $3
UserParameter=nginx_status[*],/usr/local/zabbix/etc/UserParameter/nginx_status.sh $1 $2 $3


####### TLS相关参数 #######
TLSConnect=unencrypted
TLSAccept=unencrypted
TLSCAFile=
TLSCRLFile=
TLSServerCertIssuer=
TLSServerCertSubject=
TLSCertFile=
TLSKeyFile=
TLSPSKIdentity=
TLSPSKFile=

####### For advanced users - TLS ciphersuite selection criteria #######
TLSCipherCert13=
TLSCipherCert=
TLSCipherPSK13=
TLSCipherPSK=
TLSCipherAll13=
TLSCipherAll=

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
# zabbix_server.conf
# This is a configuration file for Zabbix server daemon
# To get more information about Zabbix, visit http://www.zabbix.com

ListenPort=10051 # 监听端口
SourceIP= # 多IP地址情况下,与其他服务器通信使用的源IP地址,一般不用设置
PidFile=/tmp/zabbix_server.pid # PID文件路径
SocketDir=/tmp # socket文件路径,如果不涉及到本机通信,此参数意义不大

# 日志
LogType=file # 日志类型,支持system、file和console
LogFile=/tmp/zabbix_server.log # 日志路径
LogFileSize=0 # 指定日志滚动写入大小,当达到指定大小时,会删除就日志重新从头写入新日志,0-1024,最大1024M,0表示不限制,建议设置为0,因为这个文件一般不会太大
DebugLevel=3 #自定义日志级别,0-5,默认3 warning,可以设置为4 error

# 数据库
DBHost=192.168.7.104 # 数据库地址
DBName=zabbix_server # 数据库名称
DBSchema= # 数据库访问协议,默认myslq,如果使用其他数据库,需要设置
DBUser=zabbix # 数据库账号名
DBPassword=123456 # 数据库用户密码
DBSocket= # 数据库socket文件,如果mysql不在本机,此参数不需要配置
DBPort=3306 # 数据库端口,默认3306

# 历史数据
HistoryStorageURL= # elasticsearch服务器地址,保存zabbix历史数据到ES里面,优化zabbix性能,新版本zabbix的才支持
HistoryStorageTypes=uint,dbl,str,log,text # elasticsearch索引类型
HistoryStorageDateIndex=0 # 将历史数据保存到不同的elasticsearch索引

# 导出数据相关,一般不会导出数据,所以不需要配置
ExportDir= # 定义实时导出触发器事件,监控项采集值,趋势数据的目录
ExportFileSize=1G # 定义每个导出文件的最大大小

############ 高级参数 ################

StartPollers=16 # 预启动进程数量,收集数据,0-1000,建议此值设置与cpu核数相同
StartIPMIPollers=0 # 预启动进程数量,IPMI收集数据,0-1000,如果有物理服务器,可以打开
StartPreprocessors=3 # 预启动进程数量,用于处理zabbix agent数据,1-1000,建议设置高一些,如果使用proxy,server就不和agent直接接触了,这样可以设置的低一些
StartPollersUnreachable=1 # 预启动进程数量,轮询检查不可达主机,0-1000,设置1个进程就够了,毕竟服务器不会经常挂
StartTrappers=5 # 预启动进程数量,Trappers进程,处理报警信息,0-1000,报警多的话可以多开几个
StartPingers=3 # 预启动进程数量,用于ping检查主机,0-1000,建议设置多一
StartDiscoverers=1 # 自动发现主机的进程数量,0-250,此功能特别消耗资源,建议关闭
StartHTTPPollers=1 # http进程数量,处理web请求,设置1-2足够
StartTimers=1 # 计时器实例数量,记录报错、发邮件等行为的时间,1-1000,设置为2就足够
StartEscalators=1 # escalators进程的初始实例数量,用于处理动作中的自动步骤的进程的数量,0-100
StartAlerters=3 # 报警实例预启动数量,0-100,报警多的话可以开多一点

# 监控java,zabbix不能直监控java,而是通过javagateway间接监控
JavaGateway=192.168.7.101 # javagateway服务器地址,java pollers必须设置
JavaGatewayPort=10052 # javagateway端口,1024-32767
StartJavaPollers=20 # java轮训实例预启动数量,0-1000

# 监控VMware,很少用
StartVMwareCollectors=0 # 用于设置监控VMWARE Esxi主机实例时使用,0-250,若为0则不启用,若要监控ESXI主机,此值最少为1,根据监控ESXI数量设置对应数值
VMwareFrequency=60 # 监控vmware获取最新数据间隔频率,单位为秒,10-86400
VMwarePerfFrequency=60 # 监控vmware获取性能数据间隔
VMwareCacheSize=8M #vmware数据缓存大小,会占用zabbix server服务器内存
VMwareTimeout=10 #超时时间

SNMPTrapperFile=/tmp/zabbix_traps.tmp # snmp触发器临时文件路径
StartSNMPTrapper=0 # 是否启用 snmptrapper功能 ,默认0不启用(配合参数SNMPTrapperFile使用)

ListenIP=0.0.0.0 # 监听地址

# 历史数据相关,通过设置参数,删不干净,通常写sql脚本,直接删库
HousekeepingFrequency=1 # 多少小时清理一次代理端数据库的history数据,0-24,默认1小时,关于多久之前的数据是history数据,在模板的监控项中有设置
MaxHousekeeperDelete=5000 # 每次最多删除历史数据的行数,0-1000000

# 缓存相关,以下缓存空间都是隔离的
CacheSize=128M # zabbix初始化时占用多少系统共享内存用于存储配置信息,HOST,ITEM,TRIGGER数据,视监控主机数量和监控项调整,建议调整到32M或者更大
CacheUpdateFrequency=60 # Zabbix更新缓存数据的频率,单位为秒,范围是1-3600,若管理页面操作不频繁,可以考虑加大参数值
StartDBSyncers=8 # zabbix和数据库同步数据的进程数量,1-100,视数据库服务器I/O繁忙情况,和数据库写能力调整。数值越大,写能力越强。对数据库服务器I/O压力越大
HistoryCacheSize=512M # 历史数据的缓存大小,128K-2G
HistoryIndexCacheSize=128M # 历史数据索引缓存的大小,128K-2G
TrendCacheSize=4M # 划分多少系统共享内存用于存储计算出来的趋势数据,一定程度上可缓解数据库读压力
ValueCacheSize=64M # 历史值缓存的大小,用于缓存历史数据请求的共享内存大小

# 超时相关
Timeout=30 # 数据获取等待超时时间,1-30,建议加大此值,注意若此数值加大,应该考虑参数 StartPollers 是否有相应加大的必要。
TrapperTimeout=300 # 启用trapper功能,用于进程等待超时设置,1-300,单位是秒
UnreachablePeriod=45 # 当主机不可达多少秒后,设置为主机不可用,单位是秒,范围是1-3600
UnavailableDelay=60 # 当主机不可用了,多久检查一次该主机的可用性,单位为秒,范围是1-3600
UnreachableDelay=15 # 同 UnavailableDelay

# 脚本相关
AlertScriptsPath=${datadir}/zabbix/alertscripts # 监控报警脚本路径,取决于编译时datadir参数
ExternalScripts=${datadir}/zabbix/externalscripts # 自定义脚本存储路径

FpingLocation=/usr/bin/fping # fping命令的位置
Fping6Location=/usr/bin/fping6 # fping6命令的位置

SSHKeyLocation= # 用于SSH检查和操作的公钥和私钥的位置。

LogSlowQueries=0 # 设置慢日志查询时间(以毫秒为单位),仅当DebugLevel设置为3、4、5时才可用,0 - 不记录慢查询,范围是1-3600000,数据库自带慢日志查询功能,这里一般也不设置

TmpDir=/tmp # 临时文件目录

StartProxyPollers=1 # 启用多少子进程与proxy端通信,建议此值等于proxy数量,范围是0-250
ProxyConfigFrequency=60 # 被动模式下,被监控主机同步配置文件至proxy的周期,单位秒,1-3600*24*7
ProxyDataFrequency=60 # 被动模式下,zabbix server间隔多少秒向proxy请求历史数据,1-3600

AllowRoot=0 # 是否允许root启动zabbix,0/1

User=zabbix # 设置zabbix启动用户

Include=/usr/local/etc/zabbix_server.general.conf # 导入其他目录的配置文件
Include=/usr/local/etc/zabbix_server.conf.d/
Include=/usr/local/etc/zabbix_server.conf.d/*.conf

# web监控 SSL相关
SSLCertLocation=${datadir}/zabbix/ssl/certs # SSL证书公钥的位置,用于web监控
SSLKeyLocation=${datadir}/zabbix/ssl/keys # SSL客证书私钥位置,用于web监控
SSLCALocation= # SSL CA钥文件目录

StatsAllowedIP= # 允许访问zabbix server的IP地址列表,不过一般通过防火墙限制

# 第三方模块相关
LoadModulePath=${libdir}/modules # 第三方模块目录路径
LoadModule= #第三方模块路径,示例:LoadModule=<path/module.so>

# TLS相关,不过zabbix一般不用证书,即使用,配置在nginx也更合适
TLSCAFile= # CA文件
TLSCRLFile= # 包含已吊销证书的文件的完整路径名。
TLSCertFile= # 公钥文件路径
TLSKeyFile= # 私钥文件路径

以上所有参数的设置,可以先设置一个较小的保守的值,然后通过测试,一点点调整,最终优化到合适的值

监控服务介绍

逻辑布局

整体布局

常见的监控服务

开源监控软件:cacti、naglos、zabbix、smokeping、open-falcon 等

Cacti

官网:https://www.cacti.net/
github:https://github.com/Cacti/cacti

1
2
3
Cacti是基于LAMP平台展现的网络流量监测及分析工具,通过SNMP技术或自定义脚本从目标设备/主机获取监控指标信息;其次进行数据存储,调用模板将数据存到数据库,使用rrdtool存储和更新数据,通过rrdtool绘制结果图形;最后进行数据展现,通过Web方式将监控结果呈现出来,常用于在数据中心监控网络设备

目前还有很多 数据中心、IDC服务商 还在用Cacti

缺点:只针对物理设备、虚拟机这样有固定 IP 地址的设备,不适用于容器

Nagios

官网:https://www.nagios.org/

1
Nagios用来监视系统和网络的开源应用软件,利用其众多的插件实现对本机和远端服务的监控,当被监控对象发生异常时,会及时向管理员告警,提供一批预设好的监控插件,用户可以直接调用,也可以自定义Shell脚本来监控服务,适合各企业的业务监控,可通过Web页面显示对象状态、日志、告警信息,分层告警机制及自定义监控相对薄弱

缺点:报警机制比较简单,无法实现分组报警、递归报警等功能

SmokePing

官网地址:https://oss.oetiker.ch/smokeping/

1
Smokeping是一款用于网络性能监测的开源监控软件,主要用于对IDC的网络状况,网络质量,稳定性等做检测,通过rrdtool制图方式,图形化地展示网络的时延情况,进而能够清楚的判断出网络的即时通信情况

可以监测全国各地连接到网站的网速,类似 站长之家 的某些测速功能

Open-falcon

官网:https://www.open-falcon.org/
github:https://github.com/XiaoMi/open-falcon
文档:https://book.open-falcon.org/zh_0_2/contributing.html

1
2
小米公司开源出来的监控软件open-falcon(猎鹰),监控能力和性能较强
插件很多,部署复杂

夜莺

官网:https://n9e.didiyun.com/

1
滴滴出品,一款经过大规模生产环境验证的、分布式高性能的运维监控系统,基于 Open-falcon 二次开发

Zabbix

官网:https://www.zabbix.com/cn/

1
2
3
Zabbix是一个企业级解决方案,支持实时监控数千台服务器,虚拟机和网络设备,采集百万级监控指标,适用于任何IT基础架构、服务、应用程序和资源的解决方案

目前使用较多的开源监控软件,可横向扩展、自定义监控项、支持多种监控方式、可监控网络与服务等

缺点:zabbix 使用 mysql,数据库部分容易成为瓶颈

Prometheus

针对容器环境的开源监控软件

商业监控解决方案

Zabbix 使用场景及系统概述

https://www.zabbix.com/cn/features

使用场景

网络服务器虚拟机应用服务数据库Web安全

系统概述

主要功能

数据采集问题检测可视化告警 & 修复安全 & 认证轻松部署自动发现分布式监控Zabbix API

数据采集

所有监控系统的工作原理都类似,都需要采集被监控对象的数据

周期性时序数据

1
2
3
4
5
- 主机/对象:服务器、路由器、交换机、存储、防火墙、IP、PORT、URL、自定义监控对象…
- 服务器较少时,采集时间可以快点,两三分钟一次
- URL 就是 API
- 自定义监控对象,通过写脚本实现
- 采集目标:监控项,指标数据(metrics data)

数据采集方式:

  1. zabbix-agent 采集本机数据,然后发送给 zabbix-proxy
  2. zabbix-proxy 将数据发送给 zabbix-server
    注意:不是实时转发,zabbix-proxy 也搭配 mysql,负责临时存储数据
  3. zabbix-server 只和 zabbix-proxy 保持连接即可,将数据存储到 mysql,结束采集

如果不安装(或者无法安装)agent 客户端,可以使用特定的协议进行数据采集:

1
2
3
4
5
- SNMP:适用于路由器、交换机等网络设备
- Telnet
- ssh
- IPMI:物理机上如果有IPM接口,可以采集cpu温度、风扇传输等硬件信息
- JMX:监控java
数据存储

监控数据存储系统

1
2
3
4
5
6
7
- SQL: MySQL/MariaDB(Zabbix)
- NoSQL:Redis(Open-falcon)
- rrd: Round Robin Database(Cacti) 环形存储,始终保存一年的数据,一年前的覆盖掉

prometheus:时序数据库
zabbix:MySQL
cacti:rrd
数据类型
1
2
3
4
- 历史数据: 每个监控项采集到的每个监控值,查询时可能会造成数据库负载过大,开发和测试也会查看
- 趋势数据: 趋势表里主要保留某个监控项一个小时内历史数据的最大值、最小值和平均值以及该监控项一个小时内所采集到的数据个数

趋势数据是对历史数据的筛选,历史数据保存时间短一些,30天足以,趋势数据保存时间长一些,可以设置为一年
阈值

可按照预定义的阈值等级实现分层报警,可以 80%,不过要考虑基数,Zabbix 用的好不好,就看阈值设置的好不好,需要慢慢优化

告警机制

email、短信、微信、语音、故障自治愈(zabbix 通知服务器重启,需事先写好脚本,针对物理机和虚拟机)

初级运维 –> 中级运维/高级运维 –> 架构师 –> 总监 –> CTO

1
2
host (host groups) <- templates  # 从模板继承告警配置
host -> items -> triggers -> action (条件-conditions, 操作-operations) # 自定义告警配置
数据展示
1
2
- zabbix web:基于nginx+php
- grafana:以zabbix为数据源,展示更为绚丽的界面

Zabbix 规划及部署

目前最新的 LTS 版本是 5.0,5.0 和之前版本有明显区别,但是文档还是英文的,所以目前学习还是以 4.0 为准

下载页面:https://www.zabbix.com/cn/download

规划

  • 数据库:CPU 和磁盘 IO 一定要快,内存最好 32G,磁盘要大,zabbix 的监控数据会远大于业务数据量
  • zabbix server:zabbix server 直接装在物理机
    • CPU:16 核
    • 磁盘:RAID10、500G
    • 内存:8G(监控几十个节点),16G(监控几百个节点),32G(监控两千个节点)

实验:

部署环境:服务器系统 ubuntu 18.04.5 / Centos 7.x

主机类型 IP 地址
zabbix server 10.0.1.21
zabbix 主动代理 10.0.1.22
zabbix 被动代理 10.0.1.23
mysql master 10.0.1.24
mysql slave 10.0.1.25
web server1 10.0.1.26
web server2 10.0.1.27

yum/apt 安装

  1. Install Zabbix repository

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-3+bionic_all.deb
    $ls
    zabbix-release_4.0-3+bionic_all.deb
    $dpkg -c zabbix-release_4.0-3+bionic_all.deb #查看dep包的内容
    ...
    #zabbix.list:官方提供的源
    -rw-r--r-- root/root 118 2019-07-30 16:34 ./etc/apt/sources.list.d/zabbix.list
    ...
    $dpkg -i zabbix-release_4.0-3+bionic_all.deb #安装dep包
    $vim /etc/apt/sources.list.d/zabbix.list #修改zabbix源
    #deb http://repo.zabbix.com/zabbix/4.0/ubuntu bionic main
    #deb-src http://repo.zabbix.com/zabbix/4.0/ubuntu bionic main
    deb https://mirrors.aliyun.com/zabbix/zabbix/4.0/ubuntu bionic main #替换为阿里云的源
    deb-src https://mirrors.aliyun.com/zabbix/zabbix/4.0/ubuntu bionic main
    $apt update
  2. 安装 Zabbix server,Web 前端,agent

    1
    2
    3
    [root@zabbix-server ~]$apt install zabbix-server-mysql zabbix-frontend-php zabbix-agent
    [root@zabbix-server ~]$systemctl disable mysql.service # 数据库单独装,把自带的禁用掉
    [root@zabbix-server ~]$systemctl stop mysql.service
  3. 安装、配置 mysql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@mysql-master ~]$apt search mysql-server # 确保mysql版本不低于5.7
    [root@mysql-master ~]$apt install mysql-server mysql-client

    [root@mysql-master ~]$mysql -uroot -p
    # 创建 zabbix_server 数据库
    mysql> create database zabbix_server character set utf8 collate utf8_bin;
    # 创建 zabbix_server 用户
    mysql> create user zabbix_server@'10.0.%.%' identified by '123456';
    mysql> grant all privileges on zabbix_server.* to zabbix_server@'10.0.%.%';

    [root@mysql-master ~]$vim /etc/mysql/mysql.conf.d/mysqld.cnf
    [mysqld]
    bind-address = 0.0.0.0 # 修改监地址,默认监听127.0.0.1,修改为0.0.0.0

    [root@mysql-master ~]$systemctl restart mysql.service

    数据库初始化:

    1
    [root@zabbix-server ~]$zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix_server -p123456 -h10.0.1.24 zabbix_server
  4. 为 Zabbix server 配置数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 编辑配置文件 /etc/zabbix/zabbix_server.conf
    [root@zabbix-server ~]$vim /etc/zabbix/zabbix_server.conf
    DBHost=10.0.1.24
    DBName=zabbix_server
    DBUser=zabbix_server
    DBPassword=123456

    [root@zabbix-server ~]$grep '^[a-Z]' /etc/zabbix/zabbix_server.conf
    LogFile=/var/log/zabbix/zabbix_server.log
    LogFileSize=0
    PidFile=/var/run/zabbix/zabbix_server.pid
    SocketDir=/var/run/zabbix
    DBHost=10.0.1.24
    DBName=zabbix_server
    DBUser=zabbix_server
    DBPassword=123456
    SNMPTrapperFile=/var/log/snmptrap/snmptrap.log
    Timeout=4
    AlertScriptsPath=/usr/lib/zabbix/alertscripts
    ExternalScripts=/usr/lib/zabbix/externalscripts
    FpingLocation=/usr/bin/fping
    Fping6Location=/usr/bin/fping6
    LogSlowQueries=3000
  5. 为 Zabbix 前端配置 PHP

    1
    2
    3
    4
    5
    6
    7
    # 编辑配置文件 /etc/zabbix/apache.conf
    [root@zabbix-server ~]$vim /etc/zabbix/apache.conf
    # php_value date.timezone Europe/Riga
    php_value date.timezone Asia/Shanghai # 修改时区

    # 重启 zabbix 和 apache 服务
    [root@zabbix-server ~]$systemctl restart zabbix-server zabbix-agent apache2
  6. 浏览器访问 http://10.0.1.21/zabbix,然后根据提示一步步配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Database type MySQL
    Database server 10.0.1.24
    Database port 3306
    Database name zabbix_server
    Database user zabbix_server
    Database password 123456

    Zabbix server 10.0.1.21 # ip 和 主机名 均可
    Zabbix server port 10051
    Zabbix server name zabbix_server # 这里随便写

    # 如果以后更改配置,需要修改两个文件
    # /etc/zabbix/zabbix_server.conf
    # /usr/share/zabbix/conf/zabbix.conf.php # 生成的配置在此文件中

    默认的账号 Admin,密码 zabbix

  7. 默认只能显示英文,所以需要汉化

    1
    2
    3
    4
    5
    6
    7
    8
    # 安装简体中文语言环境
    [root@zabbix-server ~]$apt install language-pack-zh*
    # 增加中文语言环境变量
    [root@zabbix-server ~]$vim /etc/environment
    PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
    LANG="zh_CN.UTF-8" # 添加此行
    # 重新设置本地配置
    [root@zabbix-server ~]$dpkg-reconfigure locales

    1
    2
    # 配置好后,需要重启apacha、php-fpm
    [root@zabbix-server ~]$systemctl restart apache2 php-fpm

    但是,还有部分页面乱码,需要更换默认的字体包:

    从 windows(位于 C:\Windows\Fonts) 系统中挑一个顺眼的字体包,上传到服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@zabbix-server fonts]$pwd
    /usr/share/zabbix/assets/fonts # 注意目录
    [root@zabbix-server fonts]$ls | tr ' ' \n
    graphfont.ttf # 默认字体
    simhei.ttf # 刚上传的 黑体
    simkai.ttf # 刚上传的 楷体,这两个都可以,其他的不一定可以


    [root@zabbix-server include]$pwd
    /usr/share/zabbix/include # 注意目录
    # 修改默认字体为黑体,楷体也可以
    [root@zabbix-server include]$sed -i 's/graphfont/simkai/' defines.inc.php
    [root@zabbix-server include]$systemctl restart apache2 php-fpm

    编译安装

    官方文档:https://www.zabbix.com/documentation/4.0/zh/manual/installation/install

    把 apt 安装的 zabbix-server、zabbix-proxy、zabbix-agent 等软件的 service 文件拷贝出来备用,然后恢复快照

    具体详见脚本 script-apt

    注意:

    1. server 和 proxy 不能安装在同一台主机

    2. 如果是 centos,安装依赖参考:

      1
      yum install gcc libxml2-devel net-snmp net-snmp-devel curl curl-devel php phpbcmath php-mbstring mariadb mariadb-devel -y
    3. 汉化操作需要手动执行,脚本没有实现这个功能,不要忘记重启 php-fpm

Zabbix 监控入门基础

  1. 了解业务结构
  2. 监控
  3. 业务优化

zabbix 概念

配置

  • 模板:

    • 键值(key):最基础的指令,每个键值表示一项数据,比如 system.hostname 表示主机名,zabbix 内置了 一些,如果不够用,可以自定义(需要在 zabbix_agentd.conf 中指定)
    • 监控项(item):主要是由 键值 + 更新周期 组成
    • 应用集:对监控项分组,功能类似的监控项的合集
    • 触发器:据监控项的返回值对比预先设置的阈值,触发器会触发动作
      • 表达式:功能一般选择 last
    • 图形:
    • 自动发现:
    • web 场景/web 检测:
  • 主机群组

    • 1
  • 主机

    • d
  • 维护

    • w
  • 动作

    • 动作:动作由触发器触发,触发动作选项用来限制触发器的范围,一般选主机群组,只有该主机群组中的主机的触发器可以触发动作,多个触发条件可以选择和/或的关系

    • 操作:动作的操作会调用报警媒介,发送到指定用户,毕竟数据库出问题,需要发给 DBA,发给网络工程师也不管用

      • 默认操作步骤持续时间:发送周期,一般配置 60s,即 60 秒发一次

      • 默认标题 和 消息内容:用{}包裹的变量是宏,zabbix 所有内置的宏,参考:官方文档

        1
        2
        3
        4
        # 示例:
        {ALERT.SENDTO} # 报警脚本参数,值来自于用户报警媒介配置,可能是邮件、手机号等等
        {ALERT.SUBJECT} # 报警脚本参数,默认值由动作配置
        {ALERT.MESSAGE} # 报警脚本参数,默认值由动作配置
    • 恢复操作:解决完报警信息后,恢复信息也要发送

    • 更新操作:

  • 关联事件

    • 4
  • 自动发现:添加/移除/更改元素时执行自动操作

    https://www.zabbix.com/cn/auto_discovery
    https://www.zabbix.com/documentation/4.0/zh/manual/discovery/network_discovery

    • 网络发现:定期扫描、发现设备类型,IP,状态,运行时间/停机时间等,并采取预定义的操作。
    • 低级别发现(LLD):自动为设备上不同元素创建监控项,触发器和图形。
    • 主动 agent 自动注册 使用 Zabbix agent 自动开始监控新设备
  • 服务

    • 2

管理

  • agent 代理程序:即 proxy
  • 报警媒介类型:把报警信息通知给用户的方式,可以配置邮件、短信、微信(通过脚本实现)等方式,以 qq 邮件为例,将 qq 邮箱和用户关联起来,发生报警后,qq 邮箱作为发件人将报警信息发送到用户的邮箱中
  • 用户

zabbix server

官方文档:https://www.zabbix.com/documentation/4.0/zh/manual/concepts/server

zabbix server 是整个 zabbix 软件的核心程序。
zabbix server 是所有配置、统计和操作数据的中央存储中心,也是 zabbix 监控系统的告警中心。
基本的 zabbix server 的功能分解成为三个不同的组件。他们是:zabbix server、web 前端和数据库。

zabbix proxy

官方文档:https://www.zabbix.com/documentation/4.0/zh/manual/concepts/proxy

zabbix proxy 代表 zabbix server 采集监控数据,减轻 zabbix server 的 cpu 和 io 开销。
zabbix proxy 从受监控设备采集监控数据,先将数据缓存到本机数据库,然后传输到所属的 zabbix server。

zabbix agent

官方文档:https://www.zabbix.com/documentation/4.0/zh/manual/concepts/agent

proxy 和 agent 都是代理,但前者是 服务端,后者是客户端

zabbix agent 部署在被监控目标上,以主动监控本地资源和应用程序(硬盘、内存、处理器统计信息等)。

zabbix agent 收集本地的操作信息并将数据报告给 zabbix server 用于进一步处理。一旦出现异常 (例如硬盘空间已满或者有崩溃的服务进程),zabbix server 会主动警告管理员指定机器上的异常。

zabbix agents 的极高效率缘于它可以利用本地系统调用来完成统计数据的采集。

默认监听在 10051 端口。

sender

主要用于测试的命令,例如:如果 Zabbix agent 收集不到数据,可以用这个命令发一下测试数据,看一下是 Zabbix agent 的问题还是网络问题

get

主要用于测试的命令,可以与 Zabbix agent 进行通信,并从 Zabbix agent 那里获取所需的信息

注意:只有 zabbix_agentd.conf 中 Server 配置的 ip 才能从该主机上 get 到数据

监控 tomcat

所有的监控,配置步骤都是一样的:

  1. 创建主机
  2. 给主机添加模板

主动与被动监控模式

主动和被动都是相对 agent 来说的,被动模式配置简单,所以默认是被动模式

  • 被动检查:zabbix server(或 proxy)向 agent 要数据,agent 才工作,否则就不工作
  • 主动检查:agent 从 zabbix server 获取监控项列表,然后定期采集数据,主动发送给 zabbix server

被动模式

被动模式下,zabbix server 会根据主机关联的模板中的监控项和数据采集间隔时间,周期性的打开随机端口并向 zabbix agent 服务器的 10050 发起 tcp 连接,然后发送获取监控项数据的指令,即 zabbix server 发送什么指令,zabbix agent 就收集什么数据,zabbix server 什么时候发送 zabbix agent 就什么时候采集,zabbix server 不发送 zabbix agent 就闲着,所以 zabbix agent 也不用关心其监控项和数据采集周期间隔时间

优点就是配置简单,缺点是会加大 zabbix server 的工作量,在数百甚至数千台服务器的环境下会导致 zabbix server 需要轮训向每个 zabbix agent 发送数据采集指令,如果 zabbix server 负载很高还会导致不能及时获取到最新数据

主动模式

主动模式下,zabbix agent 主动向 zabbix server 的 10051 端口发起 tcp 连接请求(zabbix agent 配置文件中指定 zabbix server 的 IP 或者主机名),获取到自己的监控项和数据采集周期等信息,然后再周期性的采集指定的数返回给 zabbix server

主动模式可以有效减轻 zabbix server 的压力,推荐使用主动模式

配置主动模式:

  1. 修改 zabbix agent 配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # zabbix_agentd.conf
    PidFile=/usr/local/zabbix/run/zabbix_agentd.pid
    LogFile=/tmp/zabbix_agentd.log
    LogFileSize=0
    Server=10.0.1.21 # 被动模式下,指定zabbix server的ip,如果全部监控项都是主动模式,则可注释此项
    StartAgents=3
    ServerActive=10.0.1.21 # 重点,开启主动模式,指定zabbix server的ip
    Hostname=10.0.1.26 # zabbix agent本机的ip
    Timeout=30
    Include=/usr/local/etc/zabbix_agentd.conf.d/*.conf
  2. 生成主动模式模板
    完全克隆 zabbix 内置的 Template OS Linux 模板,命名为:Template OS Linux-active,修改监控项的类型,可以单独改,也可以批量更新

  3. 最后把主动模式模板添加到主机上

zabbix proxy

zabbix proxy 架构

zabbix proxy 对比 zbbbix server

功能 zabbxy proxy zabbix server
量级 请量级 相对重量级
图形 带图形控制界面
可以独立工作 是,可以独立采集数据并存储 是,即数据采集、存储、分析、展示于一体
易维护 是,配置完成后基本无需管理 维护也不难
独立数据库 保留少量最近数据 保留指定时间内的所有数据
报警通知 不支持发送邮件通知 支持邮件、短信等告警机制(基于调用脚本)

zabbix proxy 部署与使用

zabbix proxy 的大版本必须要和 zabbix server 版本一致,否则会出现兼容性问题

apt/yum 安装 zabbix proxy

https://www.zabbix.com/documentation/4.0/zh/manual/installation/install_from_packages/debian_ubuntu

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
# 安装软件仓库配置包
$wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-2+bionic_all.deb
$dpkg -i zabbix-release_4.0-2+stretch_all.deb
$sed -i 's@http://repo.zabbix.com@https://mirrors.aliyun.com/zabbix@' /etc/apt/sources.list.d/zabbix.list
$apt update
# 安装 SERVER/PROXY/前端
$apt install zabbix-proxy-mysql
$cat /lib/systemd/system/zabbix-proxy.service # 这里的service文件没有看懂
[Unit]
Description=Zabbix Proxy (MySQL/MariaDB)
Documentation=man:zabbix_proxy
After=network.target mysql.service

[Service]
Type=simple
User=zabbix
Group=zabbix
ExecStart=/usr/sbin/zabbix_proxy --foreground
Restart=on-abnormal

[Install]
WantedBy=multi-user.target

# 配置数据库
略...

编译安装 zabbix proxy

  1. 编译安装,参考脚本…
  2. 修改被监控主机的配置文件,Server 和 ServerActive
  3. 管理 -> agent 代理程序 -> 创建代理(注意名称要和 zabbix_proxy.conf 中的 hostname 对应起来) -> 修改主机的“由 agent 代理程序监测”参数

zabbix 监控案例实战

  1. 自定义监控项
  2. 通过脚本采集监控项数据
  3. zabbix agent 获取监控项数据
  4. 自定义模板和图形及触发器
  5. 验证数据

监控 Linux TCP 连接状态

  1. 监控 TCP 连接数脚本

    1
    2
    3
    [root@zabbix-web1 ~]#cat /usr/local/zabbix/data/tcp_conn.sh
    #!/bin/bash
    ss -ant | awk -v TCP_STAT=$1 -v s=0 '$1==TCP_STAT {++s} END {print s}'
  2. zabbix agent 添加自定义监控项

    1
    2
    3
    4
    5
    6
    [root@web1 etc]$vim zabbix_agentd.conf
    ...
    UserParameter=tcp_status[*],/bin/bash /usr/local/zabbix/data/tcp_conn.sh $1 $2
    ...
    [root@web1 etc]$chmod a+x zabbix_agentd.conf
    [root@web1 etc]$systemctl restart zabbix-agent
  3. zabbix server 测试监控项数据

    1
    2
    3
    4
    5
    6
    [root@zabbix bin]$./zabbix_get -s 10.0.1.26 -p 10050 -k tcp_status[TIME-WAIT]
    1
    [root@zabbix bin]$./zabbix_get -s 10.0.1.26 -p 10050 -k tcp_status[ESTAB]
    2
    [root@zabbix bin]$./zabbix_get -s 10.0.1.26 -p 10050 -k tcp_status[LISTEN]
    9
  4. zabbix web 导入模板
    配置-模板-导入

  5. 将 TCP 监控模板关联至主机

  6. 验证监控数据

监控 Memcached

  1. 安装 memcached

  2. 监控脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    [root@web1 data]$grep '^[^#]' /etc/memcached.conf
    -d
    logfile /var/log/memcached.log
    -m 64
    -p 11211
    -u memcache
    -l 127.0.0.1
    -P /var/run/memcached/memcached.pid

    [root@web1 data]$echo -e "stats\nquit" | nc 127.0.0.1 11211
    STAT pid 5590
    STAT uptime 725
    STAT time 1613618905
    STAT version 1.5.6 Ubuntu
    STAT libevent 2.1.8-stable
    STAT pointer_size 64
    STAT rusage_user 0.072870
    STAT rusage_system 0.048580
    STAT max_connections 1024
    STAT curr_connections 1
    STAT total_connections 4
    STAT rejected_connections 0
    STAT connection_structures 2
    STAT reserved_fds 20
    STAT cmd_get 0
    STAT cmd_set 0
    STAT cmd_flush 0
    STAT cmd_touch 0
    STAT get_hits 0
    STAT get_misses 0
    STAT get_expired 0
    STAT get_flushed 0
    STAT delete_misses 0
    STAT delete_hits 0
    STAT incr_misses 0
    STAT incr_hits 0
    STAT decr_misses 0
    STAT decr_hits 0
    STAT cas_misses 0
    STAT cas_hits 0
    STAT cas_badval 0
    STAT touch_hits 0
    STAT touch_misses 0
    STAT auth_cmds 0
    STAT auth_errors 0
    STAT bytes_read 35
    STAT bytes_written 3795
    STAT limit_maxbytes 67108864
    STAT accepting_conns 1
    STAT listen_disabled_num 0
    STAT time_in_listen_disabled_us 0
    STAT threads 4
    STAT conn_yields 0
    STAT hash_power_level 16
    STAT hash_bytes 524288
    STAT hash_is_expanding 0
    STAT slab_reassign_rescues 0
    STAT slab_reassign_chunk_rescues 0
    STAT slab_reassign_evictions_nomem 0
    STAT slab_reassign_inline_reclaim 0
    STAT slab_reassign_busy_items 0
    STAT slab_reassign_busy_deletes 0
    STAT slab_reassign_running 0
    STAT slabs_moved 0
    STAT lru_crawler_running 0
    STAT lru_crawler_starts 1275
    STAT lru_maintainer_juggles 774
    STAT malloc_fails 0
    STAT log_worker_dropped 0
    STAT log_worker_written 0
    STAT log_watcher_skipped 0
    STAT log_watcher_sent 0
    STAT bytes 0
    STAT curr_items 0
    STAT total_items 0
    STAT slab_global_page_pool 0
    STAT expired_unfetched 0
    STAT evicted_unfetched 0
    STAT evicted_active 0
    STAT evictions 0
    STAT reclaimed 0
    STAT crawler_reclaimed 0
    STAT crawler_items_checked 0
    STAT lrutail_reflocked 0
    STAT moves_to_cold 0
    STAT moves_to_warm 0
    STAT moves_within_lru 0
    STAT direct_reclaims 0
    STAT lru_bumps_dropped 0
    END

    [root@web1 data]$cat memcache_monitor.sh
    #!/bin/bash
    echo -e "stats\nquit" | nc 127.0.0.1 11211 | grep "STAT $1" | awk '{print $3}'
  3. zabbix agent 添加自定义监控项

  4. zabbix server 测试监控项数据

  5. zabbix web 制作模板

    1. 创建模板
    2. 创建监控项
    3. 创建触发器
    4. 创建图形
  6. 模板关联主机

  7. 验证监控项数据

监控 Redis

  1. 安装 Redis

  2. 监控脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    [root@web1 data]$redis-cli -h 127.0.0.1 -p 6379 info   # 所有info
    ...
    [root@web1 data]$redis-cli -h 127.0.0.1 -p 6379 info clients # 客户端相关info
    # Clients
    connected_clients:1 # 当前连接数
    client_longest_output_list:0
    client_biggest_input_buf:0
    blocked_clients:0
    [root@web1 data]$redis-cli -h 127.0.0.1 -p 6379 info memory # 内存相关info
    # Memory
    used_memory:839288 # 已用内存
    used_memory_human:819.62K
    used_memory_rss:3846144
    used_memory_rss_human:3.67M
    used_memory_peak:841288
    used_memory_peak_human:821.57K
    used_memory_peak_perc:99.76%
    used_memory_overhead:832134
    used_memory_startup:782504
    used_memory_dataset:7154
    used_memory_dataset_perc:12.60%
    total_system_memory:2065870848
    total_system_memory_human:1.92G
    used_memory_lua:37888
    used_memory_lua_human:37.00K
    maxmemory:0
    maxmemory_human:0B
    maxmemory_policy:noeviction
    mem_fragmentation_ratio:4.58
    mem_allocator:jemalloc-3.6.0
    active_defrag_running:0
    lazyfree_pending_objects:0
  3. zabbix agent 添加自定义监控项

  4. zabbix server 测试监控项数据

  5. zabbix web 模板制作

    1. 创建模板
    2. 创建触监控项:当前连接数监控项、已用内存监控项
    3. 创建触发器:当前连接数触发器、已用内存触发器
    4. 创建图形:Redis 当前连接数图形、Redis 已用内存图形
  6. 模板关联主机

  7. 验证监控项数据

监控 Nginx

  1. 安装 nginx

  2. 监控脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #!/bin/bash
    nginx_active() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | grep 'Active' | awk '{print $NF}'
    }
    nginx_reading() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | grep 'Reading' | awk '{print $2}'
    }
    nginx_writing() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | grep 'Writing' | awk '{print $4}'
    }
    nginx_waiting() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | grep 'Waiting' | awk '{print $6}'
    }
    nginx_accepts() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | awk NR==3 | awk '{print $1}'
    }
    nginx_handled() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | awk NR==3 | awk '{print $2}'
    }
    nginx_requests() {
    curl "http://127.0.0.1:/nginx_status/" 2>/dev/null | awk NR==3 | awk '{print $3}'
    }

    case $1 in
    active)
    nginx_active
    ;;
    reading)
    nginx_reading
    ;;
    writing)
    nginx_writing
    ;;
    waiting)
    nginx_waiting
    ;;
    accepts)
    nginx_accepts
    ;;
    handled)
    nginx_handled
    ;;
    requests)
    nginx_requests
    ;;
    esac
  3. zabbix agent 添加自定义监控项

    1
    2
    3
    4
    [root@web1 etc]$cat zabbix_agentd.conf
    ...
    UserParameter=nginx_status[*],/usr/local/zabbix/data/nginx_status.sh $1
    ...
  4. 导入 Nginx 监控模板

  5. 模板关联主机

  6. 验证监控数据

SNMP 监控

SNMP:Simple Network Management Protocol,简单网络管理协议,TCP/IP 协议簇的一个应用层协议

Linux 设备通过 agent 监控,网络设备通过 SNMP 协议监控,主要用于监控华为、思科等网络设备

SNMP 有三个版本:目前主要使用的是 v2c 版本

1
2
3
SNMP v1  # 团体名(Community Name)认证
SNMP v2c # 团体名认证,它在兼容SNMP v1的同时又扩充了SNMP v1的功能
SNMP v3 # 基于用户的安全模型(USM,User-Based Security Model)的认证机制

SNMP 数据交互

SNMP 管理进程与代理进程之间为了交互信息,定义了 5 种报文:

1
2
3
4
5
get-request   # 从代理进程处提取一个或多个参数值
get-response # 返回的一个或多个参数值。这个操作是由代理进程发出的
trap # 代理进程主动发出的报文,通知管理进程有某些事情发生
get-next-request # 从代理进程处提取一个或多个参数的下一个参数值
set-request # 设置代理进程的一个或多个参数值

SNMP 组织结构

SNMP 结构非常复杂,一套完整的 SNMP 系统主要包括以下几个方面:

  1. SNMP 报文协议
  2. 管理信息结构(SMI, Structure of Management Information),一套公用的结构和表示符号
  3. 管理信息库(MIB,Management Information Base),管理信息库包含所有代理进程的所有可被查询和修改的参数
  4. OID(Object Identifiers),一个 OID 是一个唯一的键值对,用于标识具体某一个设备的某个具体信息(对象标识),如端口信息、设备名称等

SNMP MIB

MIB 是 OID 的集合。MIB 是基于对象标识树的,对象标识是一个整数序列,中间以”.”分割,这些整数构成一个树型结构,类似于 DNS 或 Unix 的文件系统,MIB 被划分为若干个组,如 system、 interfaces、 a t(地址转换)和 ip 组等。iso. org.dod.internet.private.enterprises(1.3 .6 .1.4.1)这个标识,是给厂家自定义而预留的,比如华为:1.3.6.1.4.1.2011,华三:1.3.6.1.4.1.25506

SNMP OID

OID 就是对象标识

snmpwalk 工具可以使用 SNMP 的 get 请求查询指定 OID 入口的所有 OID 树信息。通过 snmpwalk 也可以查看支持 SNMP 协议(可网管)的设备的一些其他信息,比如 cisco 交换机或路由器 IP 地址、内存使用率等,也可用来协助开发 SNMP 功能

ubuntu 中版本较新,这里以 centos7 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$yum -y install net-snmp-utils
$snmpwalk -h
USAGE: snmpwalk [OPTIONS] AGENT [OID]
–h:显示帮助。
–v:指定snmp的版本, 1或者2c或者3。
–c:指定连接设备SNMP密码。 # 重点
–V:显示当前snmpwalk命令行版本。 # 重点,其实基本上也就只用这俩参数
–r:指定重试次数,默认为0次。
–t:指定每次请求的等待超时时间,单为秒,默认为3秒。
–l:指定安全级别:noAuthNoPriv|authNoPriv|authPriv。
–a:验证协议:MD5|SHA。只有-l指定为authNoPriv或authPriv时才需要。
–A:验证字符串。只有-l指定为authNoPriv或authPriv时才需要。
–x:加密协议:DES。只有-l指定为authPriv时才需要。
–X:加密字符串。只有-l指定为authPriv时才需要。

[root@zabbix-server ~]#snmpwalk -v 2c -c 123456 10.0.58.108 1.3.6.1.2.1.1.1
iso.3.6.1.2.1.1.1.0 = STRING: "Linux centos7 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64"

[root@zabbix-server ~]#snmpwalk -v 2c -c 123456 10.0.58.108 1.3.6.1.2.1.1.5
iso.3.6.1.2.1.1.5.0 = STRING: "centos7"

配置 zabbix

使用 zabbix 自带的 SNMP,要先修改团体名称,否则 会因为团体名不一致导致 zabbix 没有权限去获取被监控服务器的 snmp 利用率

监控 MySQL

监控 MySQL 连接数、主从同步、同步延迟等

percona-monitoring-plugins

percona 官方出了专门的监控管理软件,所以已经停止维护 percona-monitoring-plugins,以下使用旧的版本

  1. 监控脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [root@mysql-master src]$wget https://downloads.percona.com/downloads/percona-monitoring-plugins/percona-monitoring-plugins-1.1.8/binary/debian/artful/x86_64/percona-zabbix-templates_1.1.8-1.artful_all.deb

    $dpkg -c percona-zabbix-templates_1.1.8-1.artful_all.deb | awk '{print $NF}'
    ...
    ./var/lib/zabbix/percona/scripts/get_mysql_stats_wrapper.sh # 监控脚本,修改手动修改
    ./var/lib/zabbix/percona/scripts/ss_get_mysql_stats.php # 设置mysql的用户名和密码
    ./var/lib/zabbix/percona/templates/userparameter_percona_mysql.conf # userparameter
    ./var/lib/zabbix/percona/templates/zabbix_agent_template_percona_mysql_server_ht_2.0.9-sver1.1.8.xml # 模板文件,直接导入即可
    ...
    $dpkg -i percona-zabbix-templates_1.1.8-1.artful_all.deb

    $cp /var/lib/zabbix/percona/scripts/get_mysql_stats_wrapper.sh /usr/local/zabbix/data/
    $vim /var/lib/zabbix/percona/scripts/ss_get_mysql_stats.php
    <?php
    ...
    $mysql_user = 'root';
    $mysql_pass = '123456';
    ...
  2. 新建模板:直接导入zabbix_agent_template_percona_mysql_server_ht_2.0.9-sver1.1.8.xml即可

  3. 新建主机,并连接到刚创建的模板

自定义脚本监控 MySQL

编写脚本监控脚本 MySQL 主从同步及延迟

slave 机器上有两个关键的进程:

  • Slave_SQL_Running:负责自己的 slave mysql 进程
  • Slave_IO_Running:负责与主机的 io 通信
1
2
3
4
5
6
# 主从延迟,实际上只靠 Seconds_Behind_Master 这一个参数是不够的
mysql -uroot -p123456 -e "show slave status\G;" | grep "Seconds_Behind_Master:" | awk -F: '{print $2}'

mysql -uroot -e "show slave status\G;" | grep "Slave_IO_Running" | awk -F: '{print $2}' | sed 's/^[ \t]*//g'

mysql -uroot -e "show slave status\G;" | grep "Slave_SQL_Running:" | awk -F: '{print $2}' | sed 's/^[ \t]*//g'

自定义端口和进程监控

zabbix 内置相关 key:

1
2
3
4
5
6
7
8
# 检查 TCP 端口 是否处于侦听状态。返回 0 - 未侦听;1 - 正在侦听
net.tcp.listen[port]
# 检查是否能建立 TCP 连接到指定端口。返回 0 - 不能连接;1 - 可以连接
net.tcp.port[<ip>,port]
# 检查服务是否运行并接受 TCP 连接。返回 0 - 服务关闭;1 - 服务运行
net.tcp.service[service,<ip>,<port>]
# 检查 TCP 服务的性能,当服务 down 时返回 0,否则返回连接服务花费的秒数
net.tcp.service.perf[service,<ip>,<port>]

故障自治愈功能

当 zabbix 监控到指定的监控项异常的时候,通过指定的操作使故障自动恢复,通常是重启服务等一些简单的操作,也可以调用脚本执行比较复杂的操作

设置监控项和触发器,新建动作,在触发条件里面添加操作,在远程主机通过 zabbix 客户端执行命令

  1. zabbix agent 需要开启远程命令执行

    1
    2
    3
    # zabbix_agentd.conf
    EnableRemoteCommands=1 # 开启远程执行命令
    UnsafeUserParameters=1 # 允许远程执行命令的时候使用不安全的参数(特殊字符串)
  2. zabbix 用户授权

    1
    2
    # /etc/sudoers
    zabbix ALL = NOPASSWD: ALL # 授权指定用户执行特殊命令不再需要密码,比如sudo等
  3. 创建动作
    配置–动作–创建动作

  4. 执行远程操作

  5. 验证自治愈功能

    将被测试的服务手动停止运行,验证能否自动启动或重启,更多操作可以远程执行脚本
    手动将 Nginx、Tomcat 等 web 服务停止后,验证 zabbix agent 能否自动启动或重启

grafana 图形展示

grafana 本身只是一个图形显示工具,它独立于 zabbix 之外,通过 zabbix 提供的 api 获取数据,进行展示,其实不只是 zabbix,它还可以搭配 prometheus、elasticsearch、mysql 等等,只要你提供数据,它都能给你展示出来

grafana 相比 zabbix 自带的 web 界面,更加好看

  1. 安装 grafana 服务
    https://grafana.com/grafana/download?platform=linux

    1
    2
    3
    sudo apt-get install -y adduser libfontconfig1
    wget https://dl.grafana.com/oss/release/grafana_7.4.2_amd64.deb
    sudo dpkg -i grafana_7.4.2_amd64.deb
  2. grafana 安装并启用 zabbix 插件
    https://grafana.com/grafana/plugins/alexanderzobnin-zabbix-app

    1
    2
    3
    4
    5
    6
    7
    8
    grafana-cli plugins install alexanderzobnin-zabbix-app

    # 以上方式可能失败,推荐直接下载,然后移动到插件目录
    $wegt https://storage.googleapis.com/plugins-community/alexanderzobnin-zabbix-app/release/4.1.2/alexanderzobnin-zabbix-app-4.1.2.zip
    $unzip alexanderzobnin-zabbix-app-4.1.2.zip
    $mv alexanderzobnin-zabbix-app /var/lib/grafana/plugins/

    $systemctl restart grafana-server.service
  3. 启动 zabbix 插件

  4. 添加 zabbix 数据源

    1. 添加 mysql 数据源

    2. 添加 zabbix 数据源

  5. 添加 Dashboard

自定义基础监控模板

略…

结合 pyhton 脚本监控案例

kubernetes 集群状态监控脚本

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
[root@kubernetes-master1 ~]# cat /usr/local/zabbix/data/k8s_monitor.py
#!/usr/bin/env python
#coding:utf-8
#Author ZhangShiJie

import subprocess
success_list = []
error_list= []
def get_status():
obj = subprocess.Popen(("curl -sXGET http://10.20.15.209:8080/api/v1/nodes"),shell=True, stdout=subprocess.PIPE)
data = obj.stdout.read()
data1 = eval(data)
data2 = data1.get('items')
#print data2
for i in data2:
data3 = i.get('status')
for i in data3.get('conditions'):
if i.get('reason') == 'KubeletReady':
if i.get('type') == "Ready":
if i.get('status') == 'True':
success_list.append(i.get('status'))
elif i.get('status') == 'False':
error_list.append(i.get('status'))
else:
break
else:
error_list.append(i.get('status'))
#pass
else:
error_list.append(i.get('status'))

def count_status():
if len(error_list) == 0:
print 50
else:
print 100

def main():
get_status()
count_status()

if __name__ == "__main__":
main()

# chmod a+x /usr/local/zabbix/data/k8s_monitor.py

监控 MongodbDB 复制集状态

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
#cat /usr/local/zabbix/data/mongodb_cluster_monitor.py
#!/bin/env python
#coding:utf-8
#Author: ZhangShiJie

import subprocess
success_list = []
error_list= []

def get_mongodb_status():
obj = subprocess.Popen(("echo 'rs.status()' | /usr/local/mongodb/bin/mongo -u user -p wswd --authenticationDatabase admin \
| grep health | awk -F':' '{print $2}' | awk -F',' '{print $1}'"),shell=True, stdout=subprocess.PIPE)
restful = obj.stdout.read()
data = restful.split()
for i in data:
if i == "1":
success_list.append(i)
else:
error_list.append(i)

def count_status():
if len(error_list) > 0:
print 100
else:
print 50

def main():
get_mongodb_status()
count_status()

if __name__ == "__main__":
main()

#chmod a+x /usr/local/zabbix/data/redis_llen.py

监控 Redis 列表长度

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@redis ~]#cat /usr/local/zabbix/data/redis_llen.py
#!/usr/bin/env python
#coding:utf-8
#Author ZhangShijie
import redis
def redis_conn():
pool=redis.ConnectionPool(host="10.20.0.252",port=6379,db=0)
conn = redis.Redis(connection_pool=pool)
data = conn.llen('api4-nginx-accesslog')
print(data)
redis_conn()

[root@redis ~]# chmod a+x /usr/local/zabbix/data/redis_llen.py

监控 ELK 集群状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@elk-s1 ~]# vim /usr/local/zabbix/data/els_status.py

#!/usr/bin/env python
#coding:utf-8
#Author ZhangShijie

import subprocess
false="false"
obj = subprocess.Popen(("curl -sXGET http://10.20.3.128:9200/_cluster/health?
pretty=true"),shell=True, stdout=subprocess.PIPE)
data = obj.stdout.read()
data1 = eval(data)
status = data1.get("status")
if status == "green":
print "100"
else:
print "50"

[root@elk-s1 ~]# chmod a+x /usr/local/zabbix/data/els_status.py

监控 RabbitMQ 集群节点状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#cat /usr/local/zabbix/data/rabbit_cluster_monitor.py
#!/bin/env python
#coding:utf-8
#Author: ZhangShiJie

import subprocess
running_list = []
error_list = []
false="false"
true="true"
def get_status():
obj = subprocess.Popen(("curl -sXGET -u guest:guest http://10.20.3.171:15671/api/nodes"),shell=True,stdout=subprocess.PIPE)
data = obj.stdout.read()
data1 = eval(data)
for i in data1:
if i.get("running") == "true":
running_list.append(i.get("name"))
else:
error_list.append(i.get("name"))
def count_server():
if len(running_list) < 3: #可以判断错误列表大于0或者运行列表小于3,3未总计的节点数量
print 100 #100就是集群内有节点运行不正常了
else:
print 50 #50为所有节点全部运行正常
def main():
get_status()
count_server()
if __name__ == "__main__":
main()

#chmoa a+x /usr/local/zabbix/data/rabbit_cluster_monitor.py

Zabbix 事件通知机制

出现故障报警的时候,可以通过不同方式通知管理员进行故障处理,尽快恢复业务

1
2
3
# zabbix_server.conf
AlertScriptsPath=/usr/lib/zabbix/alertscripts #报警脚本路径
ExternalScripts=/usr/lib/zabbix/externalscripts #外部脚本路径

报警的最核心就是把报警媒介配置好,包括它前后的部分

  1. 添加报警媒介(邮件、短信、微信)
  2. 将报警媒介添加到动作的操作中,这样通过动作,就将触发器和报警媒介关联起来了
  3. 将报警媒介添加到用户,这样,当有触发器报警,就会通过报警媒介通知到用户

邮件通知

短信通知

微信通知

略..,和短信通知差不多

Zabbix 自动化运维

批量部署 Agent

使用 agent,实现 agent 的自动化批量安装,可以通过源码或 apt 等方式安装

API

通过 zabbix web 进行的操作,都提供对应的 API,可以通过编程的方式实现

API 简介:https://www.zabbix.com/documentation/4.0/zh/manual/api
API 列表:https://www.zabbix.com/documentation/4.0/zh/manual/api/reference

动态发现主机

http://blogs.studylinux.net/?p=705

Memcached 和 Redis 比较

比较类别&emsp;&emsp;&emsp; 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 服务器后自行组织这些节点,并决定数据存储的节点

文件权限

1
2
ugo:属主、属组、其他
rwx:读、写、执行

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

注意:权限设置对 root 用户无效

目录和文件的 x 权限:

  • 文件 x:把文件提请内核,启动一个进程,文件必的内容必须是可执行
  • 目录 x:目录可访问的最小权限,对文件进行读写操作的时候,文件路径上经过的所有目录,都必须先具有执行权限

删除和新建文件,需要有目录的执行(x)和写(w)权限

默认权限

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

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

chown

修改文件属主、属组

1
2
3
4
chown [OPTION]... [OWNER][:[GROUP]] FILE...
chown [OPTION]... --reference=RFILE FILE...

-R 递归修改目录下的所有文件的属主和属组

chgrp

chgrp 命令只用来修改属组

1
2
3
4
chgrp [OPTION]... GROUP FILE...
chgrp [OPTION]... --reference=RFILE FILE.. # 参考--reference指定的文件的属组

-R 递归

chmod

修改文件权限

1
2
3
4
5
6
7
8
9
10
chmod [OPTION]... MODE[,MODE]... FILE...
chmod [OPTION]... OCTAL-MODE FILE...
chmod [OPTION]... --reference=RFILE FILE... # 参考--reference指定的文件的权限

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

X:只针对目录,配合-R使用只递归修改目录的权限

umask

umask 通过 间接 的方式设定文件创建时的缺省模式

  • 对于文件来说,默认权限上限是 666(不给执行权限),666 减去 umask 值就是文件创建时的默认权限
  • 对于目录来说,默认权限上限是 777(不做任何限制),777 减去 umask 值,就是目录创建时的默认权限

注意:出于安全方面的考虑,普通文件的执行权限不能通过权限掩码来设置,必须手工修改(使用 chmod 命令)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
umask [-p] [-S] [mode]

# 示例
$umask # 查看当前umask
0022
$umask -S # 格式化打印当前umask
u=rwx,g=rx,o=rx
$umask -p # 输出可被调用,就是说输出一个可执行的命令
umask 0022
$echo `umask -p` >> ~/.bashrc # 设置 umask 的值,永久生效

umask 002 # 设置umask,这样创建文件默认的权限就是644,创建目录默认的权限就是755
umask u=rw,g=r,o=

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

特殊权限

r w x 是普通权限,还有三种特殊权限:suid、sgid、sticky

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

  • 任何一个可执行程序文件能不能启动为进程,取决发起者对程序文件是否拥有执行权限
  • 启动为进程之后,其进程的属主为发起者,进程的属组为发起者所属的组
  • 进程访问文件时的权限,取决于进程的发起者

suid

执行二进制文件,会启动进程,默认进程的属主是当前用户的属主,属组是当前用户的主组,如果执行设置 suid 的二进制文件,进程的属主会是此文件的属主

如果二进制可执行文件的属主是 root,那么设置了 suid 后,其他所有用户都可以运行此文件,并且是以 root 用户的身份运行

注意:

  • suid 只对二进制可执行程序(shell 脚本不是二进制可执行程序)有效
  • suid 设置在目录上毫无意义

设置 suid 权限:

1
2
3
4
5
chmod u+s file...
chmod u-s file...
chmod 4xxx file...

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

应用场景:

  1. 某些服务必须以 root 用户运行,可是每次启动都要运维 sudo 很不方便,此时就给可执行程序设置 suid
  2. 某些服务必须以 nologin 用户(例如 www)运行,也可以设置 suid

sgid

sgid 和 suid 类似,后者进程继承文件的属主,前者进程继承文件的属组

执行二进制文件,会启动进程,默认进程的属主是当前用户的属主,属组是当前用户的主组,如果执行设置 sgid 的二进制文件,进程的属组会是此文件的属组

suid 作用在目录上无意义,但是 sgid 作用在目录上有意义:

默认情况下,新建文件的属组为用户的主组,如果目录设置了 sgid,则在此目录下新建文件,文件的属组为此目录的属组,可以用于创建协作目录

设置 sgid 权限:

1
2
3
chmod g+s file...
chmod g-s file...
chmod 2xxx file...

sticky

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

1
2
3
4
5
chmod o+t DIR...
chmod o-t DIR...
chmod 1xxx DIR...

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

chattr

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

1
2
3
chattr +i files...   # 不能删除、改名、更改
chattr +a files... # 只能追加内容,不能删除、改名
lsattr # 显示特殊属性

权限的数字表示

rwx 前面还有一个特殊权限位,rwx 对应的数字 421,特殊权限位取值 0-7,默认 0 可省略

0-7,8 种情况能够表示 suid、sgid、sticky 这三个特殊权限:

1
2
3
4
5
6
7
8
9
suid sgid sticky
0 0 0 0
0 0 1 1
0 1 0 2
0 1 1 3
1 0 0 4
1 0 1 5
1 1 0 6
1 1 1 7

权限位映射

设置 suid,u 的 x 变成 s,如果 u 本来没有 x,则 x 位置上的 - 变成 S
设置 sgid,g 的 x 变成 s,如果 g 本来没有 x,则 x 位置上的 - 变成 S
设置 sticky,o 位的 x 变成 t,如果 o 本来没有 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//

ACL

Access Control List,访问控制列表,实现灵活的权限管理

除了 u g o,可以对更多的用户设置权限

CentOS6 可能不支持,需要手动增加 ACL 功能

ACL 生效顺序

1
owner(u) > acl user > group(g) > other(o)

ACL 相关命令

setfacl

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

1
2
setfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ...
setfacl --restore=file
1
2
3
4
- -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. 执行 cp /etc/issue /data/dir 所需要的最小权限?

    1
    2
    3
    4
    5
    /bin/cp  x
    /etc/ x
    /etc/issue r
    /data/ x
    /data/dir/ w、x
  2. 当用户 docker 对/testdir 目录无执行权限时,意味着无法做哪些操作?

    1
    无法ls查看目录下的文件列表; 无法cd到目录; 无法对目录下的文件进行读写操作; 也无法在目录下新建文件,删除目录下的文件
  3. 当用户 mongodb 对/testdir 目录无读权限时,意味着无法做哪些操作?

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

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

    1
    不可以修改,可以删除
  6. 复制/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
  7. 误删除了用户 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
  8. 在 /testdir/dir 里创建的新文件自动属于 webs 组,组 apps 的成员如:test 能对这些新文件有读写权限,组 dbs 的成员如:mysql 只能对新文件有读权限,其它用户(不属于 webs、apps、dbs)不能访问这个文件夹

    1
    2
    1.将apps、dbs中的成员加入到webs组中
    2.注意setfacl设置apps和dbs的ACL时给x权限
  9. 误将 /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

pwd

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

basename 和 dirname

1
2
3
4
5
6
7
8
[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
[root@4710419222 ~]# cd -P /bin/ # -P 切换至物理路径, 而非软链接目录
[root@4710419222 bin]# pwd
/usr/bin

切换到来时的目录:cd -

ls

1
2
3
4
5
6
[root@47105171233 ~]# ll -A  # 显示隐藏文件,但是不显示.和..
[root@4710419222 ~]# ll -t # 按照mtime从新到旧排序
[root@4710419222 ~]# ll -tr # 按照mtime从旧到新排序
[root@4710419222 ~]# ll -ut / # 按照atime从新到旧排序
[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

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

1
2
3
[root@4710419222 ~]# hexdump -C test.txt # -C 输出规范的十六进制和ASCII码
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:]:十六进制字符

文件通配符(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

1
2
3
4
-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
4
5
mv [OPTION]... [-T] SOURCE DEST       # 移动并重命名
mv [OPTION]... SOURCE... DIRECTORY # 移动到目录
mv [OPTION]... -t DIRECTORY SOURCE... # 目的目录参数在前, 要移动的文件参数在后

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

mvrename
mv 一次只能重命名一个文件,rename 可以批量重命名文件

rename

1
2
3
4
5
6
7
8
9
10
11
12
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

查看文件系统,显示磁盘占用情况和 inode 使用情况

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

关于 df 和 lsblk:

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

mkdir

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

rmdir

1
-p 递归删除父空目录

挂载点不能删除

tee

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

1
2
3
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 的作用是列出打开的文件

lsof 的参数巨多,下面列举常用的参数:

1
2
3
4
lsof file        # 查看文件被哪些进程占用
lsof -i # 列出所有的网络连接
lsof -i:port # 列出指定端口
lsof -i tcp # 列出所有的tcp网络连接

示例:

1
2
3
[root@k8s-master ~]$lsof -i:10252
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-cont 883 root 7u IPv4 32376 0t0 TCP localhost:10252 (LISTEN)

seq

https://www.zsythink.net/archives/128/

输出连续的数字,或者固定间隔的数字,或者指定格式的数字

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 # -s 指定输出的分隔符,默认为\n
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 是不是終端。如果是終端就可以讀取終端的寬度,根據寬度排版。

7 种文件类型

1
2
3
4
5
6
7
-    普通文件
d 目录文件 directory
b 块设备block
c 字符设备 character
l 符号链接文件 link
p 管道文件 pipe
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]
  • 软链接的内容是它引用文件的路径
  • 可以对目录创建软链接
  • 可以跨分区创建软链接

Linux 下使用ln -s设置软连接,Windows 下则是mklink /J

硬(hard)链接

硬链接本质上就给一个文件起一个新的名称,不是符号链接,写在这里主要是为了对照软连接

1
ln filename [linkname]

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

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

管道文件 (FIFO 文件)

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

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

管道分为 匿名管道 和 命令管道:

  • 匿名管道只能在有亲缘关系(父子或者兄弟)的进程之间创建
  • 命名管道可以使用 mkfifo 或者 mknod 命令创建
1
2
mkfifo pipe  # 创建一个名为pipe的命名管道
mknod pipe p # 等价与 `mkfifo pipe`

套接字文件

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

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

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

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

文件元数据

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

每个文件都有三个时间,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 结构

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

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

间接块指针:指向一个块(4KB),每个指针占用 4 个字节,所以这个块可以拆分成 1024 个指针,那么它可以保存数据 1024*4KB=4MB

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

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

一个指针占几个字节?

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

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

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

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

打开文件和 inode

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

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

打开一个文件分三步走:

1
2
3
1. 名称与inode编号关联,系统找到这个文件名对应的inode号码
2. 通过inode号找到对应的inode,获取inode信息
3. 根据inode信息,找到文件数据所在的block,读出数据

cp 和 inode

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

rm 和 inode

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

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

mv 和 inode

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

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

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

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

IO 重定向和管道

打开的文件都有一个 fd:file descriptor(文件描述符)

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

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

2>&1

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

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

&>file

&>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
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

注意: 标准错误默认通过管道转发, 可利用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 开始的,由于进程级文件描述符表的存在,不同的进程中会出现相同的文件描述符,它们可能指向同一个文件,也可能指向不同的文件

getent

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

getent 通过 key 查询整条数据,当然,一条数据并非只能对应一个 key,例如 passwd,一条数据的 key 可以是用户名, 也可以是 UID

1
getent database [key ...]
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/passwdvigr可以用来编辑/etc/group,它俩都有语法检查功能

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

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

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

useradd

用户创建,useradd 等于 adduser

1
useradd [options] LOGIN
1
2
3
4
5
6
7
8
9
10
11
12
-u, --uid UID                              # 指定uid, 不过不加-o参数, 指定的uid必须是唯一非负整数
-o, --non-unique # 配合-u选项, 不检查UID的唯一性
-g, --gid GROUP # 指定用户所属基本组, 可以是组名, 也可以是GID
-c, --comment COMMENT # 用户的的注释, 要简短一些
-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,使用这个参数, 不会自动创建家目录和邮件目录
-d, --home-dir HOME_DIR # 指定用户的家目录
-m, --create-home # 创建家目录, 用于系统用户
-M, --no-create-home # 不创建家目录, 用于非系统用户
-p, --password PASSWORD # 指定密码, 必须是加密过后的

创建用户相关文件

1
2
3
/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

newusers

批量创建用户

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
    # 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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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
userdel [OPTIONS]... Login

-f --force # 强制
-r --remove # 删除用户家目录和邮箱

id

查看用户相关 ID 信息

1
2
3
4
5
6
7
8
9
10
11
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 中已经没有了

groupadd

创建组,groupadd 等于 addgroup

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

范例: groupadd -g 48 -r apache

groupmod

修改组

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

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

groupdel

删除组

1
groupdel [options] GROUP

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

gpasswd

gpasswd 命令,可以更改组密码,也可以添加删除附加组成员

1
gpasswd [OPTIONS] GROUP
1
2
3
4
5
6
-a user                    # 将user添加到指定组中
-d user # 从指定附加组中移除用户user
-M user1,user2,user3... # 设置组成员列表, 会覆盖已有的组成员列表
-A user1, user2, user3 ... # 设置有组管理员列表

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

newgrp

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

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

groupmems

更改和查看组成员,管理附加组的成员关系(只有 root 用户可以执行这个命令)

1
2
3
4
5
6
7
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

Linux 安全模型

3A:

1
2
3
认证: Authentication (确认身份, 登录系统的用户名和密码)
授权: Authorization (对于文件和程序的权限)
审计: Accouting|Audition (监控日志)

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

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

用户

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

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

用户组

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

1
2
3
4
管理员组: root, 0
普通组:
系统组
普通组

用户和用户组的关系

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

安全上下文

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

用户和组的配置文件

1
2
3
4
/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
2
3
4
5
6
7
name      # 1 用户登录名
password # 2 在很早之前, 这里确实存储密码, 后来因为安全问题, 密码就搬迁到/etc/shadow中了, 但是因为兼容性问题, 所以格式不能变, 就留了一个占位符, 用x或*占位
UID # 3 用户的id
GID # 4 用户主组的id
GECOS # 5 存储用户全名和一些注释
directory # 6 用户家目录
shell # 7 用户默认使用的shell ( /bin/bash )

/etc/shadow 文件格式

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

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

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

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

/etc/group 文件格式

1
2
3
4
5
# 组名:密码:GID:以当前组为附加组的用户列表(分割符为逗号)

root:x:0:
mail:x:12:postfix
lujinkai:x:1000:

/etc/gshadow 文件格式

1
2
3
4
5
# 组名:组密码:组管理员

root:::
mail:::postfix
lujinkai:!::
1
2
3
组密码:通常不设置组密码, 为空或者 ! 都表示该群组没有组密码
组管理员列表:可以更改组密码和添加用户到群组中, 组管理员列表用逗号分隔。这个功能现在不常用了
组管理员:该字段显示这个用户组中有哪些附加用户,和`/etc/group` 文件中附加组显示内容相同,多个用户也是用逗号分隔

实验目的:搭建 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