L B T

记 录 过 去 的 经 验

iptables 的底层实现是 netfilter,netfilter 的架构是在整个网络流程的若干位置放置一些钩子,并在每个钩子上挂载一些处理函数进行处理。

IP 层的 5 个钩子点的位置,对应就是 iptables 的 5 条内置链,分别是

  • PREROUTING
  • FORWARD
  • INPUT
  • OUTPUT
  • POSTROUTING


当网卡收到一个网络报文送达协议栈时,最先经过的 netfilter 钩子是 PREROUTING,此处常见的钩子函数是 目的地址转换 (DNAT)。无论 PREROUTING 是否存在钩子处理网络数据包,下一步内核都会通过查询本地路由表决定这个数据包的流向

  • 如果是发送给本地进程,则进入 INPUT 链传给本地进程
  • 如果是发送给其他机器(或者其他 network namespace),则经过 netfilter 的 FORWARD 钩子传送出去,相当于将本地机器当作路由器

所有马上要发送到网络协议栈之外的数据包,都会经过 POSTROUTING 钩子,这里常见的处理函数是 源地址转换(SNAT) 或者 源地址伪装(Masquerade, 简称 Masq)

除了 5 条内置的链,iptables 还有 5 张表,这 5 张表主要是用来给 iptables 中的规则(rule)分类,系统中所有的 iptables 规则都被划分到不同的表集合中。5 张表分别为

  • raw - iptables 是有状态的,即 iptables 对数据包有连接追踪 (connection trackong) 机制,而 raw 可以用来去除这种追踪机制
  • mangle - 用于修改数据包的 IP 头信息
  • nat - 用于修改数据包的源或者目的地址
  • filter - 用于控制到达某条链上面的数据包是继续放行、直接丢弃(drop)、或拒绝(reject)
  • security - 用于在数据包上面应用 SELinux

表是有优先级的,5 张表的优先级从高到低是: rawmanglenatfiltersecurity,iptables 不支持自定义表。不是每个链上都能挂表,iptables 表与链的对应关系如下图

- PREROUTING FORWARD INPUT OUTPUT POSTROUTING
raw Y N N Y N
mangle Y Y Y Y Y
nat (SNAT) N N Y N Y
nat (DNAT) Y N N Y N
filter N Y Y Y N
security N Y Y Y N

iptables 表和链的工作流程图如下

iptables 命令

常用选项说明

选项 说明 示例
-F ,--flush 清除所有规则,默认规则除外
-P ,--policy 设置默认规则
-t ,--table 指定要操作的表,默认为 filter iptables -t nat -P INPUT ACCEPT
--list ,-L [chain [rulenum]] 列出(指定的链或所有链)的规则 iptables -t nat -L -v -n --line-numbers
--verbose ,-v verbose mode
--numeric ,-n 不解析协议和端口号,以数字的形式显示
--line-numbers 显示规则的行号,可以根据行号对具体的规则进行操作
--jump ,-j 匹配的规则的处理 target iptables -A INPUT -j LOG
--append ,-A chain 像指定的链中追加规则 -A INPUT -i lo -j ACCEPT
--insert ,-I chain [rulenum] 向指定的链中指定的位置插入规则 iptables -I INPUT 10 -p tcp --dport 80 -j ACCEPT
--delete ,-D chain rulenum 删除指定链中的指定位置的规则 iptables -D INPUT 10
--replace ,-R chain rulenum 更新指定链中的指定位置的规则
-S, --list-rules [chain] 按照类似 iptables-save 的输出打印规则
阅读全文 »

Nextcloud All-in-One

Nextcloud All-in-One 在一个 Docker 容器中提供了方便部署和维护的 Nextcloud 方式。[1]

使用 docker compose 部署

为方便后期管理及迁移,建议使用 docker compose 方式部署。docker-compose.yml 参考文件如下:

docker-compose.yml
version: "3"

services:
nextcloud_aio_mastercontainer:
image: nextcloud/all-in-one:latest
container_name: nextcloud-aio-mastercontainer
ports:
- "8000:80"
- "8080:8080"
- "8443:8443"
volumes:
- aio_mastercontainer:/mnt/docker-aio-config
- /var/run/docker.sock:/var/run/docker.sock:ro

volumes:
aio_mastercontainer:
driver: local
driver_opts:
type: none
o: bind
device: ./data/

使用 docker compose 方式部署注意事项:

  • aio_mastercontainer: Volume 名称必须是 aio_mastercontainer,否则会报错找不到卷 nextcloud_aio_mastercontainer: It seems like you did not give the mastercontainer volume the correct name? (The 'nextcloud_aio_mastercontainer' volume was not found.). Using a different name is not supported since the built-in backup solution will not work in that case!

启动成功后,根据提示在浏览器中打开 Nextcloud AIO setup 页面并记录页面显示的密码

You should be able to open the Nextcloud AIO Interface now on port 8080 of this server!
E.g. https://internal.ip.of.this.server:8080

脚注

环境信息

  • Centos 7
  • Prometheus Server 2.4
  • Node Exporter v1.4.0
  • Grafana v9.2.5

安装

在 Docker 中安装 Prometheus Server

创建 Prometheus Server 配置文件,如 /root/prometheus/prometheus.yml,内容如下 [1]

/data/prometheus/prometheus.yml
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'

# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.

static_configs:
- targets: ['localhost:9090']

使用 Docker 启动时挂载此文件,作为 Prometheus Server 的配置文件,之后需要修改配置,可以直接修改此文件。

docker run -d -p 9090:9090 \
--name prometheus \
-v /root/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus

启动后,可以通过 $Prometheus_IP:9090 访问 Prometheus Server UI

阅读全文 »

Prometheus ValKey & Redis Metrics Exporter

Prometheus Redis Metrics Exporter 下载页面

redis_exporter 安装

wget https://github.com/oliver006/redis_exporter/releases/download/v1.59.0/redis_exporter-v1.59.0.linux-amd64.tar.gz
tar -xf redis_exporter-v1.59.0.linux-amd64.tar.gz
mv redis_exporter-v1.59.0.linux-amd64/redis_exporter /usr/bin/

redis_exporter 生成 systemd 服务配置文件 /usr/lib/systemd/system/redis_exporter.service

/usr/lib/systemd/system/redis_exporter.service
[Unit]
Description=redis_exporter
After=syslog.target
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/redis_exporter
Restart=always
RestartSec=10
StartLimitInterval=100

[Install]
WantedBy=multi-user.target

启动 redis_exporter 服务

# systemctl daemon-reload

# systemctl enable --now redis_exporter

# systemctl status redis_exporter
● redis_exporter.service - redis_exporter
Loaded: loaded (/usr/lib/systemd/system/redis_exporter.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2024-04-30 10:45:58 CST; 5s ago
Main PID: 12126 (redis_exporter)
CGroup: /system.slice/redis_exporter.service
└─12126 /usr/bin/redis_exporter

Apr 30 10:45:58 ip-172-31-26-219.us-west-1.compute.internal systemd[1]: Started redis_exporter.
Apr 30 10:45:58 ip-172-31-26-219.us-west-1.compute.internal redis_exporter[12126]: time="2024-04-30T10:45:58+08:00" level=info msg="Redis Metrics Exp...md64"
Apr 30 10:45:58 ip-172-31-26-219.us-west-1.compute.internal redis_exporter[12126]: time="2024-04-30T10:45:58+08:00" level=info msg="Providing metrics...rics"
Hint: Some lines were ellipsized, use -l to show in full.

redis_exporter 服务启动后,默认启动 9121 端口提供 Metrics 数据供 Prometheus 抓取。

redis_exporter 配置

redis 实例及认证信息配置

如果要通过一个 redis_exporter 实例监控多个 Redis 实例,可以参照以下配置文件配置 Redis 实例及其认证信息,如果无需密码认证,则保留密码项为空。

/etc/redis_exporter_pwd_file.json

{
"redis://localhost:7380": "paswd12",
"redis://localhost:7381": "paswd12",
"redis://localhost:7382": "paswd12",
"redis://172.31.19.9:7380": "paswd12",
"redis://172.31.19.9:7381": "paswd12",
"redis://172.31.19.9:7382": ""
}

修改 redis_exporter 启动参数,使其读取上面配置的实例和其认证信息

/usr/bin/redis_exporter -redis.password-file /etc/redis_exporter_pwd_file.json

配置 Prometheus 抓取 redis_exporter 指标

参考以下配置使用文件发现的方式配置被监控的 Redis 实例


scrape_configs:
- job_name: 'redis_exporter_targets'
file_sd_configs:
- files:
- targets-redis-instances.yml
metrics_path: /scrape
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: <<REDIS-EXPORTER-HOSTNAME>>:9121

## config for scraping the exporter itself
- job_name: 'redis_exporter'
static_configs:
- targets:
- <<REDIS-EXPORTER-HOSTNAME>>:9121

targets-redis-instances.yml 文件内容包含 Targets 内容:

targets-redis-instances.yml
- labels:
label1: value1
targets: [ "redis://redis-host-01:6379", "redis://redis-host-02:6379"]
阅读全文 »

环境信息

  • Centos 7
  • Prometheus 2.44.0
  • node_exporter-1.6.0

node_exporter 安装

安装 node_exporter 官方文档

以下步骤演示安装 node_exporter 并使用 systemd 管理服务

wget https://github.com/prometheus/node_exporter/releases/download/v1.6.0/node_exporter-1.6.0.linux-amd64.tar.gz

tar -xf node_exporter-1.6.0.linux-amd64.tar.gz

cp node_exporter-1.6.0.linux-amd64/node_exporter /usr/bin/

生成 systemd 服务配置文件 /usr/lib/systemd/system/node_exporter.service

/usr/lib/systemd/system/node_exporter.service
[Unit]
Description=node_exporter
After=syslog.target
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/node_exporter
Restart=always
RestartSec=10
StartLimitInterval=100

[Install]
WantedBy=multi-user.target

执行以下命令管理服务

$ systemctl daemon-reload

$ systemctl status node_exporter
● node_exporter.service - node_exporter
Loaded: loaded (/usr/lib/systemd/system/node_exporter.service; disabled; vendor preset: disabled)
Active: inactive (dead)

$ systemctl enable --now node_exporter
Created symlink from /etc/systemd/system/multi-user.target.wants/node_exporter.service to /usr/lib/systemd/system/node_exporter.service.

$ systemctl status node_exporter
● node_exporter.service - node_exporter
Loaded: loaded (/usr/lib/systemd/system/node_exporter.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2023-06-23 10:34:08 CST; 2s ago
Main PID: 28578 (node_exporter)
CGroup: /system.slice/node_exporter.service
└─28578 /usr/bin/node_exporter

node_exporter 配置信息

启动参数详解

参数 说明 示例
--web.listen-address=":9100" node_exporter 监听端口,默认 9100
--web.telemetry-path="/metrics" prometheus 获取 Metrics 访问的 url,默认 /metrics
--log.level="info" 日志级别
--log.format="logger:stderr" 日志打印格式
--collector.disable-defaults 关闭默认的采集项
--no-collector.${item} 关闭某一项默认开启的采集项 --no-collector.cpu
--collector.systemd.unit-include="(docker'|'sshd).service" 收集指定服务的指标

可以配置通过正则表达式屏蔽或者选择某些监控项 [1]

参数 说明 示例
--collector.diskstats.ignored-devices="^(ram'|'loop'|')\\d+$" 忽略某些磁盘的信息收集
--collector.filesystem.ignored-mount-points="^/(dev'|'proc'|')($'|'/)" 忽略某些文件系统挂载点的信息收集
--collector.filesystem.ignored-fs-types="^(autofs'|'proc)$" 忽略某些文件系统类型的信息收集
--collector.netclass.ignored-devices="^$" 忽略某些网络类的信息收集
--collector.netdev.ignored-devices="^$" 忽略某些网络设备的信息收集
--collector.netstat.fields="^$" 配置需要获取的网络状态信息
--collector.vmstat.fields="^(oom_kill'|'pgpg'|'pswp).*" 配置 vmstat 返回信息中需要收集的选项

功能对照表

默认开启的功能

默认开启的功能 [1]

名称 说明 系统
arp /proc/net/arp 中收集 ARP 统计信息 Linux
conntrack /proc/sys/net/netfilter/ 中收集 conntrack 统计信息 Linux
cpu 收集 cpu 统计信息 Darwin, Dragonfly, FreeBSD, Linux
diskstats /proc/diskstats 中收集磁盘 I/O 统计信息 Linux
edac 错误检测与纠正统计信息 Linux
entropy 可用内核熵信息 Linux
exec execution 统计信息 Dragonfly, FreeBSD
filefd /proc/sys/fs/file-nr 中收集文件描述符统计信息 Linux
filesystem 文件系统统计信息,例如磁盘已使用空间 Darwin, Dragonfly, FreeBSD, Linux, OpenBSD
hwmon /sys/class/hwmon/ 中收集监控器或传感器数据信息 Linux
infiniband 从 InfiniBand 配置中收集网络统计信息 Linux
loadavg 收集系统负载信息 Darwin, Dragonfly, FreeBSD, Linux, NetBSD, OpenBSD, Solaris
mdadm /proc/mdstat 中获取设备统计信息 Linux
meminfo 内存统计信息 Darwin, Dragonfly, FreeBSD, Linux
netdev 网口流量统计信息,单位 bytes Darwin, Dragonfly, FreeBSD, Linux, OpenBSD
netstat /proc/net/netstat 收集网络统计数据,等同于 netstat -s Linux
sockstat /proc/net/sockstat 中收集 socket 统计信息 Linux
stat /proc/stat 中收集各种统计信息,包含系统启动时间,forks, 中断等 Linux
textfile 通过 --collector.textfile.directory 参数指定本地文本收集路径,收集文本信息 any
time 系统当前时间 any
uname 通过 uname 系统调用, 获取系统信息 any
vmstat /proc/vmstat 中收集统计信息 Linux
wifi 收集 wifi 设备相关统计数据 Linux
xfs 收集 xfs 运行时统计信息 Linux (kernel 4.4+)
zfs 收集 zfs 性能统计信息 Linux

默认关闭的功能

默认关闭的功能 [1]

名称 说明 系统
bonding 收集系统配置以及激活的绑定网卡数量 Linux
buddyinfo /proc/buddyinfo 中收集内存碎片统计信息 Linux
devstat 收集设备统计信息 Dragonfly, FreeBSD
drbd 收集远程镜像块设备(DRBD)统计信息 Linux
interrupts 收集更具体的中断统计信息 Linux,OpenBSD
ipvs /proc/net/ip_vs 中收集 IPVS 状态信息,从 /proc/net/ip_vs_stats 获取统计信息 Linux
ksmd /sys/kernel/mm/ksm 中获取内核和系统统计信息 Linux
logind logind 中收集会话统计信息 Linux
meminfo_numa /proc/meminfo_numa 中收集内存统计信息 Linux
mountstats /proc/self/mountstat 中收集文件系统统计信息,包括 NFS 客户端统计信息 Linux
nfs /proc/net/rpc/nfs 中收集 NFS 统计信息,等同于 nfsstat -c Linux
qdisc 收集队列推定统计信息 Linux
runit 收集 runit 状态信息 any
supervisord 收集 supervisord 状态信息 any
systemd systemd 中收集设备系统状态信息 Linux
tcpstat /proc/net/tcp/proc/net/tcp6 收集 TCP 连接状态信息 Linux

配置示例

采集白名单配置

关闭默认的采集项,只开启指定的采集项(白名单)

node-exporter --collector.disable-defaults --collector.cpu --collector.meminfo
阅读全文 »

环境信息

  • Centos 7
  • Python 3

在 Telegram 中生成 Bot

  1. 首先在 telegram 中搜索 @BotFather,和其对话,根据提示创建 机器人,记录下生成的 token 信息

  2. 创建新的 Channel 或者 Group 或者将刚刚新建的 Bot 加入已有的 Channel/Group。

  3. 获取 ChatGroup ID,可以使用以下方法之一

    1. 添加机器人 @get_id_bot 到 Channel,会自动显示 Chat ID

    2. 使用以下代码获取

      >>> import requests
      >>> response = requests.get(f'https://api.telegram.org/bot{token}/getUpdates')
      >>> data = response.json()
      >>> chat_id = data['result'][0]['message']['chat']['id']
      >>> chat_id
      -992754669

使用 curl 向 telegram 发送消息

$ curl -v "https://api.telegram.org/bot{token}/sendMessage?text=sa&chat_id=-992754669"

> GET /bot{token}/sendMessage?text=sa&chat_id=-992754669 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: api.telegram.org
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Fri, 02 Jun 2023 03:03:58 GMT
< Content-Type: application/json
< Content-Length: 276
< Connection: keep-alive
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, OPTIONS
< Access-Control-Expose-Headers: Content-Length,Content-Type,Date,Server,Connection
<
* Connection #0 to host api.telegram.org left intact
{"ok":true,"result":{"message_id":12,"from":{"id":5683237521,"is_bot":true,"first_name":"AlertManager","username":"AlertManager_Bot"},"chat":{"id":-992754669,"title":"AlertManager Test","type":"group","all_members_are_administrators":true},"date":1685675038,"text":"sa"}}

使用 python 向 telegram 发送消息

使用 requests 库

>>> import requests
>>> response = requests.get(f'https://api.telegram.org/bot{token}/sendMessage?text=sa&chat_id=-992754669')

>>> response.text
'{"ok":true,"result":{"message_id":13,"from":{"id":5683237521,"is_bot":true,"first_name":"AlertManager","username":"AlertManager_Bot"},"chat":{"id":-992754669,"title":"AlertManager Test","type":"group","all_members_are_administrators":true},"date":1685675769,"text":"sa"}}'

使用 telegram 库

需要安装 python-telegram-bot

pip install --upgrade python-telegram-bot

发送消息代码

>>> import telegram
>>> import asyncio
>>> bot = telegram.Bot(token='5683231111:AAHzaGf0oRg8A')
>>> async def send_telegram_message():
... response = await bot.send_message(chat_id=-992754669, text="la")
... print(response)
...
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(send_telegram_message())
Message(channel_chat_created=False, chat=Chat(api_kwargs={'all_members_are_administrators': True}, id=-992754669, title='AlertManager Test', type=<ChatType.GROUP>), date=datetime.datetime(2023, 6, 2, 3, 39, 16, tzinfo=datetime.timezone.utc), delete_chat_photo=False, from_user=User(first_name='AlertManager', id=5683237521, is_bot=True, username='MS_AlertManager_Bot'), group_chat_created=False, message_id=14, supergroup_chat_created=False, text='la')

如果需要在非异步环境中(例如 Django 试图函数) 运行以上异步代码,会报错: RuntimeError: There is no current event loop in thread 'Thread-1'。需要特殊处理,可以使用 asyncio.run() 函数来运行异步代码,它可以在非异步环境中创建一个新的事件循环并运行异步函数。

Django 视图中参考代码如下

def send_message_to_tg(chat_id: int, text: str):
''' 发送消息到 tg'''
bot = telegram.Bot(token=tg_bot_token)

async def send_telegram_message():
response = await bot.send_message(chat_id=chat_id, text=text)
print(response)
# loop = asyncio.get_event_loop()
# loop.run_until_complete(send_telegram_message())
asyncio.run(send_telegram_message())

Grafana 是一款用 GO 语言开发的开源数据可视化工具,可以做数据监控和数据统计,带有告警功能。

基础概念

组织(Organization) 与用户(User)

Organization 相当于一个 Namespace,一个 Organization 完全独立于另一个 Organization,包括 datasourcedashboard 等,创建一个 Organization 就相当于打开了一个全新的视图,所有的 datasourcedashboard 等都需要重新创建。一个用户(User) 可以属于多个 Organization。

User 是 Grafana 里面的用户,用户可以有以下 角色

  • admin - 管理员权限,可以执行任何操作。
  • editor - p不可以创建用户不可以新增 Datasource可以创建 Dashboard**
  • viewer - 仅可以查看 Dashboard
  • read only editor - 允许用户修改 Dashboard,但是 不允许保存

数据源 Datasource

Grafana 中操作的数据集、可视化数据的来源

Dashboard

在 Dashboard 页面中,可以组织可视化数据图表。

  • Panel - 在一个 Dashboard 中,Panel 是最基本的可视化单元。通过 Panel 的 Query Editor 可以为每一个 Panel 添加查询的数据源以及数据查询方式。每一个 Panel 都是独立的,可以选择一种或者多种数据源进行查询。一个 Panel 中可以有多个 Query Editor 来汇聚多个可视化数据集
  • Row - 在 Dashboard 中,可以定义一个 Row,来组织和管理一组相关的 Panel

Variables

在 Dashboard 的设置页面中,有 Variables 页面,在其中可以为 Dashboard 配置变量,之后可以在 Panel 的 Query Editor 中使用这些预定义的变量。变量的值也可以是通过表达式获取的值。也可以在 Panel 的标题中使用变量

例如以下 Variables 配置

Node    label_values(kubernetes_io_hostname)

在 Dashboard 中定义了这些变量后,可以在 Panel 的 Query Editor 中使用,在 Query Editor 中使用了 Variables 中定义的变量后,在 Dashboard 的顶部下拉菜单中可以选择预定义的变量的值(需要在定义 Variables 时配置 Show on dashboardLabel and Value 以使在 Dashboard 顶部显示下拉菜单),Panel 中的 Query 表达式就会使用这些变量的值进行计算以及显示图表。

阅读全文 »

环境信息

  • centos7
  • python3
  • Django 4

ModuleNotFoundError: No module named ‘MySQLdb’

ModuleNotFoundError: No module named ‘MySQLdb’

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.

解决方法

pip3 install pymysql

编辑文件./python36/lib/python3.6/site-packages/django/db/backends/mysql/__init__.py, 输入以下内容

import pymysql 
pymysql.install_as_MySQLdb()
阅读全文 »

本文档主要做为需要安装或升级 Nginx 版本或者需要重新编译 Nginx 为其添加新模块时的参考。Nginx 服务常用配置说明

环境信息

  • Centos 7 5.4.225-1
  • nginx stable 1.24.0

编译安装 Nginx

编译安装 Nginx 之前,首先需要安装依赖包 [1]

pcre

cd /tmp
wget github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.gz
tar -zxf pcre2-10.42.tar.gz
cd pcre2-10.42
./configure
make
sudo make install

zlib

cd /tmp
wget http://zlib.net/zlib-1.2.13.tar.gz
tar -zxf zlib-1.2.13.tar.gz
cd zlib-1.2.13
./configure
make
sudo make install

openssl

cd /tmp
wget --no-check-certificate http://www.openssl.org/source/openssl-1.1.1t.tar.gz
tar -zxf openssl-1.1.1t.tar.gz
cd openssl-1.1.1t
./config shared zlib
make install

编译安装 Nginx

下载 Nginx stable 版本编译安装

wget https://nginx.org/download/nginx-1.24.0.tar.gz
tar zxf nginx-1.24.0.tar.gz
cd nginx-1.24.0

要启用或者停用指定的 Nginx 自带模块,参考 Nginx 编译配置选项说明

此处编译配置添加第三方模块 nginx-module-vts 以支持 Prometheus。执行以下命令编译 Nginx 并添加第三方模块 nginx-module-vtsNginx 和 nginx-module-vts 版本兼容列表

wget https://github.com/vozlt/nginx-module-vts/archive/refs/tags/v0.2.2.tar.gz
tar -xf v0.2.2.tar.gz

./configure --prefix=/usr/local/nginx-1.24.0 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream --with-stream_ssl_module \
--with-openssl=/tmp/openssl-1.1.1t \
--with-http_v2_module \
--add-module=nginx-module-vts-0.2.2/

make
make install

编译安装后的软件包,只需要安装好依赖,便可以迁移到其他机器上面使用,本文档编译安装后的软件包下载链接

为 Nginx 配置 systemd 管理配置文件

为了能使用 systemctl 管理源码编译安装的 nginx,可以为其使用以下配置文件将其托管到 systemd

/usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/local/nginx-1.24.0/sbin/nginx -t
ExecStart=/usr/local/nginx-1.24.0/sbin/nginx
ExecReload=/usr/local/nginx-1.24.0/sbin/nginx -s reload
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Prometheus 采集 Nginx 监控数据

参考步骤安装 nginx-module-vts 模块,以支持 Prometheus 采集 Nginx 统计数据。

如果要统计 Nginx 所有的 vhost 数据,则将 nginx-module-vts 模块相关配置放置于 http 模块内,否则可以在只想要监控(统计)的 vhost (server 配置段) 中添加配置。

nginx-module-vts 模块相关配置命令说明:

命令 说明 用法示例
vhost_traffic_status_zone 定义 vhost_traffic_status 模块使用的共享内存区域。用于存储虚拟主机的流量统计信息 vhost_traffic_status_zone shared_memory_name size;
shared_memory_name 是共享内存区域的名称,size 是共享内存区域的大小。
vhost_traffic_status_filter_by_host 按主机名过滤虚拟主机状态信息
默认会将流量全部计算到第一个 server_name 上;启用后,只会显示与请求的主机名匹配的虚拟主机状态信息。
vhost_traffic_status_filter_by_host on;
vhost_traffic_status_filter_by_set_key 根据自定义键值对来过滤虚拟主机的状态信息 vhost_traffic_status_filter_by_set_key $host$request_uri;
vhost_traffic_status_filter_by_set_zone 过滤器使用的共享内存区域
vhost_traffic_status_display 用于显示虚拟主机状态信息的格式
支持 jsonCSVhtml
vhost_traffic_status_display_format html;
vhost_traffic_status_display_format 用于显示虚拟主机状态信息的字段格式。
可以选择显示的字段有:request、status、request_time、request_length、request_method、request_uri、request_length、request_time、request_time_counter、request_time_counter_overflows、request_time_min、request_time_max、request_time_avg、request_time_median、request_time_percentile。
vhost_traffic_status_display_format field1 field2 ...;

为了获取所有域名的统计信息,在 Nginx 的 http 模块内添加以下配置:

nginx.con
http {
...
vhost_traffic_status_zone;

vhost_traffic_status_filter_by_host on;

server {
listen 8888;
server_name localhost;
location /status {
vhost_traffic_status_display;
vhost_traffic_status_display_format json;
}

}
}

重载配置后查看请求内容:

# curl localhost:8888/status
{"hostName":"testhost","moduleVersion":"v0.2.2","nginxVersion":"1.24.0","loadMsec":1713418497362,"nowMsec":1713418504352,"connections":{"active":7,"reading":0,"writing":1,"waiting":6,"accepted":7,"handled":7,"requests":14},"sharedZones":{"name":"ngx_http_vhost_traffic_status","maxSize":1048575,"usedSize":10587,"usedNode":3},"serverZones":{"api.testdomain.com":{"requestCounter":12,"inBytes":9330,"outBytes":6249,"responses":{"1xx":0,"2xx":12,"3xx":0,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0},"requestMsecCounter":11,"requestMsec":0,"requestMsecs":{"times":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1713418503612,1713418503612,1713418503612,1713418503612,1713418503612,1713418503962,1713418503965,1713418503965,1713418503966,1713418503966,1713418504330,1713418504331],"msecs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,3,3,0,0]},"requestBuckets":{"msecs":[],"counters":[]},"overCounts":{"maxIntegerSize":18446744073709551615,"requestCounter":0,"inBytes":0,"outBytes":0,"1xx":0,"2xx":0,"3xx":0,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0,"requestMsecCounter":0}},"src.testdomain.ph":{"requestCounter":1,"inBytes":393,"outBytes":309,"responses":{"1xx":0,"2xx":0,"3xx":1,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0},"requestMsecCounter":0,"requestMsec":0,"requestMsecs":{"times":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1713418500180],"msecs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"requestBuckets":{"msecs":[],"counters":[]},"overCounts":{"maxIntegerSize":18446744073709551615,"requestCounter":0,"inBytes":0,"outBytes":0,"1xx":0,"2xx":0,"3xx":0,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0,"requestMsecCounter":0}},"*":{"requestCounter":13,"inBytes":9723,"outBytes":6558,"responses":{"1xx":0,"2xx":12,"3xx":1,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0},"requestMsecCounter":11,"requestMsec":0,"requestMsecs":{"times":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1713418500180,1713418503612,1713418503612,1713418503612,1713418503612,1713418503612,1713418503962,1713418503965,1713418503965,1713418503966,1713418503966,1713418504330,1713418504331],"msecs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,3,3,0,0]},"requestBuckets":{"msecs":[],"counters":[]},"overCounts":{"maxIntegerSize":18446744073709551615,"requestCounter":0,"inBytes":0,"outBytes":0,"1xx":0,"2xx":0,"3xx":0,"4xx":0,"5xx":0,"miss":0,"bypass":0,"expired":0,"stale":0,"updating":0,"revalidated":0,"hit":0,"scarce":0,"requestMsecCounter":0}}},"upstreamZones":{"::nogroups":[{"server":"127.0.0.1:12000","requestCounter":5,"inBytes":4154,"outBytes":3351,"responses":{"1xx":0,"2xx":5,"3xx":0,"4xx":0,"5xx":0},"requestMsecCounter":11,"requestMsec":2,"requestMsecs":{"times":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1713418503962,1713418503965,1713418503965,1713418503966,1713418503966],"msecs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,3,3]},"requestBuckets":{"msecs":[],"counters":[]},"responseMsecCounter":11,"responseMsec":2,"responseMsecs":{"times":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1713418503962,1713418503965,1713418503965,1713418503966,1713418503966],"msecs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,3,3]},"responseBuckets":{"msecs":[],"counters":[]},"weight":0,"maxFails":0,"failTimeout":0,"backup":false,"down":false,"overCounts":{"maxIntegerSize":18446744073709551615,"requestCounter":0,"inBytes":0,"outBytes":0,"1xx":0,"2xx":0,"3xx":0,"4xx":0,"5xx":0,"requestMsecCounter":0,"responseMsecCounter":0}}]}}

统计信息输出结果支持多种格式:

  • localhost:8888/status/format/json - Json
  • localhost:8888/status/format/html - Html
  • localhost:8888/status/format/jsonp - Jsonp
  • localhost:8888/status/format/prometheus - Prometheus
  • localhost:8888/status/format/control - control

在 Prometheus 中添加以下 Targets 配置抓取 nginx-module-vts 模块暴露出的统计信息

- job_name: "Nginx"
metrics_path: '/status/format/prometheus'
static_configs:
- targets: ['IPADDRESS:8888']
labels:
Department : 'OP'

在 Prometheus 中检查抓取到的数据

常见错误

error while loading shared libraries

Nginx 编译安装成功后,启动报错

$ /usr/local/nginx-1.24.0/sbin/nginx -t
/usr/local/nginx-1.24.0/sbin/nginx: error while loading shared libraries: libpcre2-8.so.0: cannot open shared object file: No such file or directory

问题原因 为 Nginx 在系统的库文件路径中未找到已经安装的 libpcre2-8.so.0 库文件。可以通过以下方式验证

  1. 搜索 libpcre2-8.so.0,可以看到系统上已经存在此库文件 /usr/local/lib/libpcre2-8.so.0
    $ find / -name libpcre2-8.so.0
    /usr/local/lib/libpcre2-8.so.0
  2. 检查此库文件是否在系统已加载的库文件中。执行以下命令搜索系统已经加载的库文件,发现没有 /usr/local/lib/libpcre2-8.so.0
    $ ldconfig -p | grep libpcre
    libpcre32.so.0 (libc6,x86-64) => /lib64/libpcre32.so.0
    libpcre16.so.0 (libc6,x86-64) => /lib64/libpcre16.so.0
    libpcreposix.so.0 (libc6,x86-64) => /lib64/libpcreposix.so.0
    libpcrecpp.so.0 (libc6,x86-64) => /lib64/libpcrecpp.so.0
    libpcre.so.1 (libc6,x86-64) => /lib64/libpcre.so.1
  3. 检查系统共享库文件的查找路径的配置文件 /etc/ld.so.conf,发现其中不包括路径 /usr/local/lib/,因此位于此路径下的共享库文件无法被搜索到
    $ cat /etc/ld.so.conf
    include ld.so.conf.d/*.conf


要解决此问题,可以使用以下方法之一:

  • 添加 /usr/local/lib/ 到系统共享库查找路径配置文件 /etc/ld.so.conf

    /etc/ld.so.conf
    include ld.so.conf.d/*.conf
    /usr/local/lib/

    执行以下命令,使配置生效

    ldconfig
  • 设置系统环境变量 LD_LIBRARY_PATH,这个变量定义了系统共享库的查找目录。将 /usr/local/lib 添加到此变量的值中,要永久生效需要将其写入配置文件,如 ~/.bash_profile

    export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
  • 创建符号链接

    ln -s /usr/local/lib/libpcre2-8.so.0 /usr/local/nginx-1.24.0/sbin/libpcre2-8.so.0

    或者

    ln -s /usr/local/lib/libpcre2-8.so.0 /lib64/libpcre2-8.so.0

SSL modules require the OpenSSL library

执行以下命令执行编译前配置时报错 ./configure: error: SSL modules require the OpenSSL library.

# ./configure --prefix=/usr/local/nginx-1.24.0 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream --with-stream_ssl_module \
--with-http_v2_module \
--add-module=nginx-module-vts-0.2.2/

checking for PCRE2 library ... found
checking for OpenSSL library ... not found
checking for OpenSSL library in /usr/local/ ... not found
checking for OpenSSL library in /usr/pkg/ ... not found
checking for OpenSSL library in /opt/local/ ... not found

./configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.

此报错原因为未找到 OpenSSL 的库文件。

针对此场景,可以通过在编译配置时指定 OpenSSL 的源码中库文件的具体位置(--with-openssl=/tmp/openssl-1.1.1t),参考以下命令

./configure --prefix=/usr/local/nginx-1.24.0 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream --with-stream_ssl_module \
--with-openssl=/tmp/openssl-1.1.1t \
--with-http_v2_module \
--add-module=nginx-module-vts-0.2.2/

Error 127

Nginx 执行 make 命令时报错: /bin/sh: line 2: ./config: No such file or directory

# ./configure --prefix=/usr/local/nginx-1.24.0 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream --with-stream_ssl_module \
--with-openssl=/usr/local/lib64/ \
--with-http_v2_module \
--add-module=nginx-module-vts-0.2.2/

# make
make -f objs/Makefile
make[1]: Entering directory `/root/nginx-1.24.0'
cd /usr/local/lib64/ \
&& if [ -f Makefile ]; then make clean; fi \
&& ./config --prefix=/usr/local/lib64//.openssl no-shared no-threads \
&& make \
&& make install_sw LIBDIR=lib
/bin/sh: line 2: ./config: No such file or directory
make[1]: *** [/usr/local/lib64//.openssl/include/openssl/ssl.h] Error 127
make[1]: Leaving directory `/root/nginx-1.24.0'
make: *** [build] Error 2

错误信息表明在编译 nginx 时,make 命令无法找到 OpenSSL 的配置脚本 config。此脚本位于 OpenSSL 的源码目录中。可以通过 --with-openssl=/tmp/openssl-1.1.1t 指定。
修改编译前的配置命令如下:

./configure --prefix=/usr/local/nginx-1.24.0 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-stream --with-stream_ssl_module \
--with-openssl=/tmp/openssl-1.1.1t \
--with-http_v2_module \
--add-module=nginx-module-vts-0.2.2/

make
make install

getpwnam(“nginx”) failed

nginx 报错

nginx: the configuration file /usr/local/nginx-1.24.0/conf/nginx.conf syntax is ok
nginx: [emerg] getpwnam("nginx") failed
nginx: configuration file /usr/local/nginx-1.24.0/conf/nginx.conf test failed

问题原因nginx 用户不存在,创建 nginx 用户或者修改配置文件,使用已有的用户运行 nginx

参考链接

Nginx 官网文档

脚注

Certbot 是 Let’s Encrypt SSL 官方推荐的 ACME 协议客户端,它是一个 Python 程序,且包含模块化插件支持。Let’s Encrypt 的根证书浏览器支持广泛,且支持泛域名。但单个证书的有效期为 90 天,以防止滥用。

安装 Certbot

官方安装步骤参考

以下步骤演示在 Python3 环境中安装 Certbot 及其相关依赖

  1. 安装 certbot
    pip install certbot
  2. 申请证书时,要使用 DNS 方式验证域名所有权并且 DNS 使用 Cloudflare 的情况下,可以安装 certbot-dns-cloudflare 插件实现自动验证,参考以下命令安装 certbot-dns-cloudflare,此模块需要 cloudflare 模块的支持
    pip install cloudflare
    pip install certbot-dns-cloudflare
    安装完成后检查相关模块和版本。其中 cloudflare 版本需要最低为 2.3.1 [1]
    # pip list
    certbot 2.10.0
    certbot-dns-cloudflare 2.10.0
    cloudflare 2.19.2
    以上模块安装完成后,即可使用 certbot 申请域名证书,并支持 Cloudflare DNS 的自动验证。

基于 Cloudflare DNS 的自动验证申请域名证书

参考步骤安装 certbot 及 Cloudflare DNS 插件后 即可使用 certbot 自动请求 Cloudflare DNS 创建申请证书时需要的 DNS 记录自动完成域名归属权的验证过程。

certbot 支持的 Cloudflare 相关的参数如下

参数 说明 示例
--dns-cloudflare 使用 Cloudflare 的 DNS 插件自动验证域名归属权
--dns-cloudflare-credentials 请求 Cloudflare 的授权配置文件
--dns-cloudflare-propagation-seconds 请求 Cloudflare DNS 添加相关 DNS 记录后,让 ACME 服务等待多少秒再验证 DNS 记录。主要用来防止 DNS 记录添加后,缓存 DNS 服务器未来得及更新最新记录。
默认为 10

Cloudflare Credentials 说明

假设有 Cloudflare 账号的 Global API Key,则 Credentials 配置文件内容参考如下

cloudflare.ini
# Cloudflare API credentials used by Certbot
dns_cloudflare_email = cloudflare@example.com
dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234

申请证书的具体命令如下,如果是第一次申请,需要根据提示填写自己的邮箱信息并同意许可协议,邮箱用于接受之后系统发送的错误或者域名证书过期等信息

certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
--dns-cloudflare-propagation-seconds 60 \
-d example.com \
-d www.example.com

如果是非交互式环境,可以使用参数 --email your-email@example.com--agree-tos 自动绑定邮箱并同意许可

阅读全文 »

Nginx 服务配置

全局通用配置

nginx.conf
user nginx nginx;    

# 建议设置为 cpu 核心数或者 cpu 核心数的 2 倍,进程会包含一个 `master process`,多个 `worker process`
# master process 负责绑定端口、调度进程等,不负责业务的处理
# worker process 是业务进程,负责业务的处理
worker_processes auto;

# 一个 worker 进程可以打开的最大的 fd 个数,受 Linux 内核限制
# 理论值应该是系统最多打开文件数(ulimit -n)与 nginx 进程数相除
# 可通过 ulimit 设置或修改系统文件:`/etc/securit/limits.conf`
worker_rlimit_nofile 1024;

# cpu 亲和性设置
worker_cpu_affinity 0001 0010 0100 1000;

# 工作进程调度优先级,-20 到 19 之间的值,值越小越优先调用。
# 如果系统同时运行多个任务,你可能需要提高 nginx 的工作进程的优先级
worker_priority 0;

# ssl 硬件加速服务器,需要硬件支持
# ssl_engine ssl_engine device;

# nginx 是否以守护进程运行,是否让 nignx 运行于后台;调试时可为 off,使得所有信息直接输出在控制台
daemon on | off;

# events 模块中包含 nginx 中所有处理连接的设置。
events {
# 每个 worker 进程允许的最多连接数,
# nginx 服务最大连接数:worker_processes * worker_connections (受 worker_rlimit_nofile 限制)
worker_connections 1024;
use epoll;

# 是否允许一次性地响应多个用户请求
multi_accept on;

# 是否打开 nginx 的 accept 锁;此锁能够让多个 worker 进行轮流地、序列化地与新的客户端建立连接;
# 而通常当一个 worker 进程的负载达到其上限的 7/8,master 就尽可能不将请求调度至 worker.
accept_mutex on | off;
}

# HTTP 模块控制着 nginx http 处理的所有核心特性
http {
include mime.types;
default_type application/octet-stream;

# 是否在错误页面中显示和响应头字段中发出 nginx 版本号。
# 安全考虑建议关闭
server_tokens on | off | string;

# 是否启用 sendfile 内核复制模式功能。作为静态服务器可以提供最大的 IO 访问速度。
sendfile on | off;

# 尽快发送数据,否则会在数据包达到一定大小后再发送数据。这样会减少网络通信次数,降低阻塞概率,但也会影响响应及时性。
# 比较适合于文件下载这类的大数据通信场景。
tcp_nodelay on|off;

# 单位s,适当降低此值可以提高响应连接数量
keepalive_timeout 65;

# 一次长连接上允许的最大请求数
keepalive_requests 100;

# 禁止指定浏览器使用 keepalive
keepalive_disable msie6|none;

# 读取 http 请求首部的超时时长。如果客户端在此时间内未传输整个头,则会向客户端返回 408(请求超时)错误
client_header_timeout 1;

# 读取 http 请求包体的超时时间。
client_body_timeout 2;

# 发送响应的超时时长。超时后连接将关闭。
send_timeout 5;

# http 请求包体的最大值,常用于限定客户端所能够请求的最大包体,根据请求首部中的 Content-Length 来检查,以避免无用的传输。
client_max_body_size 1m;

# 限制客户端每秒传输的字节数,默认为0,表示没有限制。单位 Byte/s
limit_rate 0;

# nginx 向客户端发送响应报文时,如果大小超过了此处指定的值,则后续的发送过程开始限速,单位 Byte
limit_rate_after 0;

# 是否忽略不合法的 http 首部,默认为 on,off 意味着请求首部中出现不合规的首部将拒绝响应。
ignore_invalid_headers on|off;

# 用户访问的文件不存在时,是否将其记录到错误日志中。
log_not_found on|off;

# nginx 使用的 dns 地址,及缓存解析结果的时间
resolver 8.8.8.8 [valid=time] [ipv6=on|off];

# dns 解析超时时间
resolver_timeout 2;

# 是否打开文件缓存功能,max:用于缓存条目的最大值,
# inactive:某缓存条目在指定时长内没有被访问过时,将自动被删除,即缓存有效期,通常默认为 60s。
open_file_cache off;
open_file_cache max=N [inactive=time];

# 是否缓存文件找不到或没有权限访问等相关信息。
open_file_cache_errors on | off;

# 多长时间检查一次缓存中的条目是否超出非活动时长。
# 建议值:小于等于 open_file_cache inactive
open_file_cache_valid 60;

# 在 open_file_cache inactive指 定的时长内被访问超过此处指定的次数时,才不会被删除(删除低命中率的缓存)。
open_file_cache_min_uses 2;

# 开启内容压缩,可以有效降低客户端的访问流量和网络带宽
gzip on | off;

# 内容超过最少长度后才开启压缩,太短的内容压缩效果不佳,且会浪费系统资源。
# 压缩长度会作为 http 响应头 Content-Length 字段返回给客户端。 建议值:64
gzip_min_length length;

# 压缩级别,默认值为 1。范围为1~9级,压缩级别越高压缩率越高,但对系统性能要求越高。建议值:4
gzip_comp_level 1~9;

# 压缩内容类型,默认为 text/html;。只压缩 html 文本,一般我们都会压缩 js、css、json 之类的,可以把这些常见的文本数据都配上。
如:text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_types mime-type …;

# 自动显示目录
autoindex on;

# off : 以人类易读的方式显示文件大小,on:以 bytes 显示文件大小
autoindex_exact_size off;

# 定义限制区域,imit_req_zone 只能放在 http {} 内,使用此限制可以在 http {} (对服务器内所有的网站生效)、server {} (对具体的一个网站生效)或 location {} (对具体的一个网址生效)
# 此区域名称为 test,可根据需求自定义; 10m 表示此区域存储 key($binary_remote_addr)使用的大小
# 存储大小,一般 1m 空间大约能保存 1.6 万条 IP 地址,空间满了新数据会覆盖旧数据
# rate=1r/m ,限制访问请求频率为每分钟 1 次,可根据需要自行设置,1r/s 是 1 秒 1 次,时间单位只能选择 s (秒)或 m (分),最低频率限制是每分钟 1 次访问请求
# rate=10r/m,1分钟最多访问 10 次,同时不能超过 1r/6s,即 6s 内最多访问 1 次。超过 1r/6s 返回 503
limit_req_zone $binary_remote_addr zone=test:10m rate=1r/m;

# 定义日志格式
log_format main '{ time: $time_iso8601|'
'http_host:$http_host|'
'cdn_ip:$remote_addr|'
'request:$request|'
'request_method:$request_method|'
'http_user_agent:$http_user_agent|'
'size:$body_bytes_sent|'
'responsetime:$request_time|'
'upstreamtime:$upstream_response_time|'
'upstreamhost:$upstream_addr|'
'upstreamstatus:$upstream_status|'
'url:$http_host$uri|'
'http_x_forwarded_for:$clientRealIp|'
'status:$status}';

# server 负责具体的 http 服务器实现
server {
listen 80 [default_server] [rcvbuf=SIZE] [sndbuf=SIZE] [ssl];

# 可使用通配符*或正则表达式(~开头),多个域名先精确匹配,再通配,再正则,'_'表示空主机头
server_name _ ;

access_log logs/access.log main;
error_log logs/access.err.log;

# 跨域配置
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers 'Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With';
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header Access-Control-Allow-Credentials: true;

location / {
# web 资源路径
root html;

# 定义默认页面,从左往右匹配
index index.html index.htm;

# 自左向右读取指定路径,找到即停止,如果都不存在,返回一个错误码
try_files $uri $uri.html $uri/index.html =404;

# 自左向右读取指定路径,找到即停止,如果都不存在,返回一个 uri
try_files $uri $uri.html $uri/index.html /404.html;
}

location /i/ {
# 路径别名,只能用于 location 中。
# 访问 http://a.com/i/a.html, 资源路径为:/data/www/html/a.html
# 若是root指令,访问 http://a.com/i/a.html,资源路径为:/data/www/html/i/a.html
alias /data/www/html/;
}

# 对于某个请求发生错误,如果匹配到错误码,重定向到新的 url
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

# 对于某个请求发生错误,如果匹配到错误码,重定向到新的 url,同时可以更改返回码
error_page 404 =200 /404.html;
}

# 包含其他配置文件
include vhosts/*.conf;
}


阅读全文 »

环境信息

  • Prometheus 2.44.0
  • Grafana 9.5.2
  • Kubernetes 1.24

Kubernetes 相关指标

Kubernetes 中部署并监控 Kubernetes 集群参考

配置 Prometheus 监控 Kubelet 之后可以采集到 Kubelet 监控指标。

配置 Prometheus 读取 cAdvisor 之后可以通过 cAdvisor 采集到容器相关的监控指标。

指标名称 类型 说明 示例
kubelet_pod_start_duration_seconds_count Pod 启动的时间
kubelet_pod_start_duration_seconds_bucket Pod 启动的时间的延迟直方图数据 kubelet_pod_start_duration_seconds_bucket{le="0.5"}
kubelet_running_pods 运行的 Pod 的数量
kubelet_running_containers 运行的 Containers 的数量
kubelet_runtime_operations_errors_total Kubelet 和 CRI 交互产生的错误(类型)
kubelet_started_containers_total Kubelet 启动的 Container 总数
kubelet_started_pods_total Kubelet 启动的 Pod 总数
kubelet_volume_stats_available_bytes PV Volume 可以使用的磁盘空间
kube_node_status_allocatable
kube_node_status_capacity
节点的可分配的 资源 数量 kube_node_status_allocatable{resource="pods"}
节点可分配的 Pod 的数量
kubelet_started_pods_total Counter 已启动的 Pod 数量
container_cpu_usage_seconds_total Counter Container 使用的 CPU
container_memory_usage_bytes Gauge Pod 使用的内存 container_memory_usage_bytes{namespace="default"}
kube_pod_container_status_restarts_total Counter Pod 的重启次数

nginx-ingress-controller 相关指标

配置 Prometheus 监控 Ingress-Nginx-Controller 指标 后,Prometheus 可以读取到 Ingress-Nginx-Controller 暴露的监控指标。

Grafana 中配置使用 Ingress-Nginx-Controller 指标示例

指标名称 类型 说明 示例
nginx_ingress_controller_requests Counter Ingress Nginx Controller 接收到的所有请求数,,包括各个状态码 irate(nginx_ingress_controller_requests[2m]) - 请求速率
nginx_ingress_controller_nginx_process_connections 连接数,包括各个状态码
nginx_ingress_controller_request_duration_seconds_sum 请求持续时间的总和
请求持续时间是从请求进入 Ingress 控制器开始,到响应返回给客户端结束的整个时间
nginx_ingress_controller_request_duration_seconds_count 请求持续时间的计数。 计算平均请求持续时间:平均请求持续时间 = 请求持续时间总和 / 请求持续时间计数
nginx_ingress_controller_ingress_upstream_latency_seconds_sum upstream 占用时间的总和
upstream 占用时间是指请求从 Ingress 到达 upstream(backend)服务器的时间
nginx_ingress_controller_ingress_upstream_latency_seconds_count upstream 上游占用时间的计数 计算平均上游占用时间:平均上游占用时间 = 上游占用时间总和 / 上游占用时间计数。

node 相关指标

主机信息

包括 CPU 架构、内核版本、操作系统类型、主机名等,集中在指标 node_uname_info 中。

CPU

指标名称 类型 说明 示例
node_cpu_seconds_total Counter CPU 使用时间 node_cpu_seconds_total{mode="idle"} - CPU 空闲时间

统计节点 CPU 使用率

100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Memory

指标名称 类型 说明 示例
node_memory_MemTotal_bytes Gauge 总的内存
node_memory_MemFree_bytes Gauge 空闲内存
node_memory_Cached_bytes Gauge Cache 内存
node_memory_Buffers_bytes Gauge Buffers 内存

统计节点的 内存使用率

(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Cached_bytes - node_memory_Buffers_bytes) / node_memory_MemTotal_bytes * 100

Network

指标名称 类型 说明 示例
node_network_receive_bytes_total Counter 网卡接收的流量
node_network_transmit_bytes_total Counter 网卡发送的流量

网卡流量带宽

irate(node_network_receive_bytes_total{device!~"cni0|docker.*|flannel.*|veth.*|virbr.*|lo",kubernetes_io_hostname=~"$node"}[$prometheusTimeInterval])

irate(node_network_transmit_bytes_total{device!~"cni0|docker.*|flannel.*|veth.*|virbr.*|lo",kubernetes_io_hostname=~"$node"}[$prometheusTimeInterval])

环境信息

  • Python 3.10
  • Django 4.1

在 Project/App 的 models.py 文件中创建 model,当 model 定义完成,Django 会自动生产一个后台管理接口,允许认证用户添加、更改和删除对象,只需在管理站点上注册模型即可 [1]

创建 model

在 Project/App 的 models.py 文件中创建 model

models.py
from django.db import models

# 公司部门
class Department(models.Model):
name = models.CharField(max_length=24, unique=True, help_text="部门名称",verbose_name='名称')
shortName = models.CharField(max_length=8, unique=True, help_text="部门名称简称",verbose_name='简称')
manager = models.ForeignKey('Emplyee', on_delete=models.CASCADE, help_text="部门老大")
comment = models.CharField(max_length=256, blank=True, help_text="备注信息",verbose_name='备注')

def __str__(self):
return self.shortName

class Meta:
# 管理后台显示的 model 名,最后面没有 's'
verbose_name_plural = "部门"

# 管理后台显示的 model 名,最后面有 's',显示为 '部门s'
verbose_name = "部门"

# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"

Django 模型字段参考

对修改后的 model 进行 migrate,以使在数据库中变更更改。

python manage.py makemigrations
python manage.py migrate

model 注册到后台

在 Project/App 的 admin.py 文件中注册 model

admin.py
from django.contrib import admin

# 假如 app 为 servers,导入 models
from servers import models

admin.site.site_header = "My Admin"
admin.site.site_title = "My Admin"

@admin.register(models.Department)
class DepartmentAdmin(admin.ModelAdmin):
list_display = ('id','name','shortName','manager','comment')
list_display_links = ('id','name','shortName','manager','comment')

更多有关 admin 配置方法,请参考 Django admin 配置

阅读全文 »

环境信息

  • xshell7

常用配置

字体配置

(菜单栏)查看 > 工具栏 > 标准按钮

中文字体和英文字体需要单独配置,建议配置默认会话属性

(菜单栏)文件 > 默认会话属性,找到 外观 分别配置 字体 以及 亚洲字体:

阅读全文 »

伴手礼选择

伴娘伴手礼

类别 产品类别推荐 详情参考
香薰、香水 香薰礼盒
化妆品
花果茶
喜糖礼盒 佳偶良缘 喜糖礼盒

参考链接:
20款百元左右的高级感伴手礼

伴郎伴手礼

类别 产品类别推荐 详情参考
过宋小湯四季养生礼盒
UMTEA 关心茶礼盒
葡萄酒 名庄95分 蓝风铃甜白葡萄酒
点心 马卡龙甜点糕点铁盒

根据更新的比特币价格数据和计算,如果每年元旦买入BTC,次年元旦卖出,直到2024年,每年的投资回报率(ROI)如下:

  • 2013年买入,2014年卖出的ROI:约 5390.75%
  • 2014年买入,2015年卖出的ROI:约 -57.01%
  • 2015年买入,2016年卖出的ROI:约 37.21%
  • 2016年买入,2017年卖出的ROI:约 131.78%
  • 2017年买入,2018年卖出的ROI:约 1243.49%
  • 2018年买入,2019年卖出的ROI:约 -72.10%
  • 2019年买入,2020年卖出的ROI:约 92.07%
  • 2020年买入,2021年卖出的ROI:约 308.63%
  • 2021年买入,2022年卖出的ROI:约 62.51%
  • 2022年买入,2023年卖出的ROI:约 -58.27%
  • 2023年买入,2024年卖出的ROI:约 166.06%

环境信息

  • aws-cli/2.9.15

aws cli 安装更新说明

aws cli 基础配置及说明

常见用法

查看 aws 帮助信息

aws help

查看子命令帮助信息

aws ec2 help

配置认证信息时指定鉴权信息对应的 Profile

aws configure --profile source-account

aws 命令常用选项

选项 说明 示例
--region 指定区域,aws 区域列表 aws --region ap-east-1 ec2 describe-instances
--profile 指定配置文件名称。当需要同时操作多个账号上面的 S3 目标时,可以为每个账号指定 profile 名称,在后续操作时使用选项 --profile 指定要使用的 Profile
阅读全文 »

Kubernetes 官网文档

环境信息

  • Centos 7 5.4.212-1
  • Docker 20.10.18
  • containerd.io-1.6.8
  • kubectl-1.24.7
  • kubeadm-1.24.7
  • kubelet-1.24.7

kubernetes 环境安装前配置

升级内核版本

Centos 7 默认的内核版本 3.10 在运行 kubernetes 时存在不稳定性,建议升级内核版本到新版本

Centos 7 升级内核
  • Centos 7 默认的内核版本 3.10 使用的 cgroup 版本为 v1,Kubernetes 的部分功能必须使用 cgroup v2 来进行增强的资源管理和隔离 [13]

    使用以下命令检查系统使用的 cgroup 版本

    stat -fc %T /sys/fs/cgroup/

    如果输出是 cgroup2fs表示使用 cgroup v2

    如果输出是 tmpfs表示使用 cgroup v1

  • User Namespaces 功能需要 Linux 6.3 以上版本,tmpfs 才能支持 idmap 挂载。并且其他功能(如 ServiceAccount 的挂载)也需要此功能的支持 [14]

关闭 SELinux

kubernetes 目前未实现对 SELinux 的支持,因此必须要关闭 SELinux

sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

集群中所有计算机之间具有完全的网络连接

配置集群所有节点的防火墙,确保所有集群节点之间具有完全的网络连接。

  • 放通节点之间的通信
  • 确保防火墙允许 FORWARD 链的流量
    /etc/sysconfig/iptables
    *filter
    :INPUT DROP [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [4:368]

    -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -i lo -j ACCEPT

    # kubernetes nodes
    -A INPUT -m comment --comment "kubernetes nodes" -s 172.31.5.58 -j ACCEPT
    -A INPUT -m comment --comment "kubernetes nodes" -s 172.31.5.68 -j ACCEPT
    -A INPUT -m comment --comment "kubernetes nodes" -s 172.31.0.230 -j ACCEPT

    -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT -m comment --comment "k8s ingress http,https"


    ...

    -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
    -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
    -A INPUT -j REJECT --reject-with icmp-host-prohibited
    COMMIT
阅读全文 »

在 Ubuntu 上安装软件包主要通过使用 apt 命令来完成。apt 是高级包装工具(Advanced Package Tool)的缩写,提供了一个易用的命令行界面,用于处理软件包的安装、更新和删除等操作。

环境信息

  • Ubuntu 22

apt

查看软件包信息

  • 查看系统已安装的软件包

    apt list --installed

    apt list 列出系统上所有可用的软件包,包括已安装的软件包可供安装的软件包

  • 列出特定的软件包

    apt list <package-name>
  • 搜索特定软件包是否已安装

    apt list --installed  <package-name>
  • 查看软件包的依赖关系

    apt depends <package_name>

下载软件包

  • 下载软件包但不安装
    apt download <package_name>

安装软件包

  • 更新软件包列表。在安装新软件包之前,最好先更新本地软件包列表,以确保你安装的是最新版本的软件包。

    sudo apt update

    此命令会从配置的源中检索新的软件包列表。

  • 安装软件包。安装软件包的基本命令格式为:

    sudo apt install <package_name>
  • 安装特定版本的软件包

    如果你需要安装软件包的特定版本,可以通过指定版本号来完成安装。首先,使用 apt policy 命令查找可用版本

    apt policy <package_name>

    然后,安装特定版本的软件包

    sudo apt install <package_name>=<version>

    sudo apt install nginx=1.18.0-0ubuntu1
  • 安装推荐的软件包。

    当安装某些软件包时,APT 可能会建议安装一些推荐的软件包以增强功能。默认情况下,apt install 命令会安装推荐的软件包。 如果你不想安装推荐的软件包,可以使用 --no-install-recommends 选项

    sudo apt install --no-install-recommends <package_name>

卸载软件包

  • 卸载软件包但保留配置文件

    sudo apt remove <package_name>
  • 卸载软件包并删除配置文件。如果相关目录不为空,将不会删除,会输出提示

    sudo apt purge <package_name>

    或者

    sudo apt remove --purge <package_name>
  • 清理未使用的依赖包

    当你卸载一个软件包时,它可能会留下一些不再需要的依赖软件包。为了清理这些不再使用的依赖,可以执行:

    sudo apt autoremove

    这个命令会检查并自动删除那些被安装为其他软件包依赖但现在不再被任何已安装软件包需要的软件包。

dpkg

列出系统上已安装的软件包

dpkg -l

查找文件所属的软件包

dpkg -S /path/to/file

APT 仓库管理

APT 通过读取配置文件(主要是 /etc/apt/sources.list/etc/apt/sources.list.d/*.list)来获取软件包仓库(repository)的信息。

APT 的软件源配置文件是 /etc/apt/sources.list ,此外还可以包含 /etc/apt/sources.list.d/ 目录下的 .list 文件。这些文件定义了 APT 从哪里下载软件包和更新信息。

一个典型的 sources.list 条目格式如下:

deb [options] url distribution component1 component2 component3

  • deb:表示这是一个二进制软件包的仓库,对应的 deb-src 表示源代码仓库。
  • options:可选项,例如可以指定架构。
  • url:仓库的 URL。
  • distribution:发行版的代号,如 focalbuster 等。
  • component :仓库中的组成部分,如 mainrestricted 等。

Repository 的类型

在 APT 的上下文中,软件包仓库是网络或本地的存储位置,它们存储了软件包及其元数据。主要有以下几种类型的仓库:

  • Main:官方支持的免费软件。
  • Universe:社区维护的免费软件。
  • Restricted:官方支持的非自由软件。
  • Multiverse:非自由软件,不包括官方支持。

添加新的软件源

要添加新的软件源,你可以直接编辑 sources.list 文件或在 sources.list.d/ 目录下创建一个新的 .list 文件。例如,添加一个新的 PPA(Personal Package Archive):

sudo add-apt-repository ppa:<repository_name>

这个命令不仅会添加软件源,还会自动导入仓库的公钥,确保软件包的安全性。

删除软件源

要删除软件源,可以直接编辑 sources.list 文件或删除 sources.list.d/ 目录下相应的 .list 文件。之后,运行 sudo apt update 来更新软件包列表。