L B T

记 录 过 去 的 经 验

Nextcloud All-in-One

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

使用 docker compose 部署

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

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:
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config
- /var/run/docker.sock:/var/run/docker.sock:ro

volumes:
nextcloud_aio_mastercontainer:
name: nextcloud_aio_mastercontainer
driver: local
driver_opts:
type: none
o: bind
device: /opt/Nextcloud/data/

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

  • name: nextcloud_aio_mastercontainer: Volume 名称必须是 nextcloud_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

根据页面提示登陆,跟随页面提示进行新实例初始化。

初始化过程中要提供域名,系统会自动为域名颁发证书(使用系统 443 端口映射到容器中的 8443 端口)

默认的 Nextcloud AIO 未部署反向代理,要使用反向代理请参考文档: Reverse Proxy Documentation

Nextcloud AIO 使用的端口说明

脚注

环境信息

  • 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
  • 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())

本文档主要做为需要安装或升级 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 官网文档

脚注

环境信息

  • 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
阅读全文 »

环境信息

  • Kubernetes 1.29

kubeadm 初始化集群

Network setup

组件的 advertise IP 地址选择

Kubernetes 中的组件(如 KubeadmKube-apiserver 等)在初始化启动时,都需要在节点(主机)上找到一个合适的 IP 作为组件的 advertising/listening 的地址。[1]

默认情况下,此 IP 是主机上默认路由对应的网卡所在的 IP。

# ip route show
default via 172.31.16.1 dev eth0
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink
10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.31.16.0/20 dev eth0 proto kernel scope link src 172.31.26.11

如果系统上有多个默认路由,组件会尝试使用第一个默认路由对应的网卡所在的 IP。如果系统上没有默认路由,并且未明确为组件配置 IP,组件会初始化失败并退出

组件选择的 IP 地址将会作为 X.509 证书的 SAN(Subject Alternative Name) 的一部分。因此在系统初始化运行之后修改 IP 需要重新签发证书并重启相关组件。

Pod 网段选择

Pod 的网段必须不能和主机网络重叠,如果选用的网络插件的网络和主机的网段重叠,必须为网络插件选择合适的和主机不同的网段,在集群初始化时通过参数 --pod-network-cidr 配置,并在安装网络插件时在其 YAML 文件中修改为对应的网段。 [4]

control-plane-endpoint 参数说明

--control-plane-endpoint 选项用来配置 api-server 的共享地址,可以是域名或者负载均衡器的 IP。 [2]

如果是单主节点的 Kubernetes 集群,想要扩展成为多个 Master 节点(高可用),初始化时没有 --control-plane-endpoint,是不被支持的。

因此为了后期可以由单个 Master 节点扩展为多个 Master 节点,即高可用,初始化节点时,建议加上此选项配置 api-server 的负载均衡地址。

<– more –>

admin.conf 的重要性

kubeadm init 初始化集群成功后,会产生一个 kubeconfig 文件 /etc/kubernetes/admin.conf,此配置中包含了一个绑定到 cluster-admin 的 ClusterRole 的组,其拥有对整个集群的控制权,不要将其共享给任何人[3]

kubeadm init 初始化集群成功后,同时会生成一个名为 super-admin.confkubeconfig 文件,其中配置的超级用户可以跳过 authorization layer,比如 RBAC。不要将其共享给任何人。建议将其保存在一个安全的位置。

Node

节点名称

在 Kubernetes 集群中,节点名称(Node Name)唯一的标识一个节点。在一个集群中,不能有多个名称一样的节点,Kubernetes 系统默认同一个 节点名(Node Name) 的实例将具有同样的一个状态和属性 [7]

配置节点为不可调度

将节点标记为 不可调度(unschedulable ) 会阻止系统调度新的 Pod 到节点上,但是不会影响节点上已有的 Pod。

Labels Selectors Annotations

Label 主要用来给 Kubernetes 中的对象添加 可识别属性。 Label 可以提供高效的查询和匹配以选择 Kubernetes 中的对象,Lables 可以在对象创建时或者运行过程中随时做变更。

不具有识别属性的信息 不建议用作 Labels,而是应该写入 Annotations 中。[5]

Label Key 由 2 部分构成:

  • (可选的)前缀。系统组件(如 kube-scheduler, kube-controller-manager, kube-apiserver, kubectl) 或者第三方自动化组件为对象添加 labels 时必须带有前缀(prefix)。kubernetes.io/ and k8s.io/ 是为 Kubernetes 核心系统保留的前缀
  • (必须的)名字。

    Annotations 的 Key 命名规则和 Label Key 基本一致 [6]

Label select 用法说明

集群间通讯流量说明

API Server 到 kubelet

API Server 请求 kubelet 的主要目的包括: [8]

  • 获取到 Pods 的日志
  • (比如使用 kubectl) Attach 到 Pods 中的容器
  • 提供 kubelet 的端口转发功能。

默认情况下,API 服务器不检查 kubelet 的服务证书。这使得此类连接容易受到中间人攻击, 在非受信网络或公开网络上运行也是 不安全的
为了对这个连接进行认证,使用 --kubelet-certificate-authority 标志给 API 服务器提供一个根证书包,用于 kubelet 的服务证书。

API Server 到 nodes, pods 和 services

从 API 服务器到节点、Pod 或服务的连接默认为纯 HTTP 方式,因此既没有认证,也没有加密。 这些连接可通过给 API URL 中的节点、Pod 或服务名称添加前缀 https: 来运行在安全的 HTTPS 连接上。 不过这些连接既不会验证 HTTPS 末端提供的证书,也不会提供客户端证书。 因此,虽然连接是加密的,仍无法提供任何完整性保证。 这些连接 目前还不能安全地 在非受信网络或公共网络上运行。 [8]

Kubernetes 支持使用 SSH tunnels 来加密从 API 到 nodes 的链接(目前已经处于废弃状态,推荐使用 Konnectivity service 替代)。

Leases

Kubernetes 使用 Lease API 来更新 kubelet 发送到 Kubernetes API Server 的心跳信息。Kubernetes 中的每一个节点在 kube-node-lease Namespace 中都有一个 Lease Object。kubelet 向系统上报心跳的过程,就是在更新对应的 Lease Object 的 spec.renewTime 字段,Kubernetes 控制平面会使用这个上报的时间戳来决定节点是否可用 [9]

# kubectl describe lease -n kube-node-lease k8s-admin
Name: k8s-node1
Namespace: kube-node-lease
Labels: <none>
Annotations: <none>
API Version: coordination.k8s.io/v1
Kind: Lease
Metadata:
Creation Timestamp: 2023-09-07T09:54:15Z
Managed Fields:
API Version: coordination.k8s.io/v1
Fields Type: FieldsV1
...
Owner References:
API Version: v1
Kind: Node
Name: k8s-node1
UID: db3dafee-1232-4c07-a1c6-524f86a96fbe
Resource Version: 46696081
UID: 053d9b79-81be-4f3f-801e-09192012c822
Spec:
Holder Identity: k8s-node1
Lease Duration Seconds: 40
Renew Time: 2024-02-03T02:30:11.760176Z
Events: <none>

Leases 也被用来确保 HA 的组件(类似 kube-controller-managerkube-scheduler)在同一时刻只有一个实例提供服务,其他实例处于备用状态 [9]

kubelet

串行和并行拉取镜像

Kubelet 默认(同一个节点)串行的拉取镜像,即同一时间只发送一个拉取镜像的请求到 Registry Server,其他的拉取镜像的请求保持等待,直到当前请求处理完成。多个节点上的镜像拉取请求是隔离的,即不同的节点在同一时间是并行的拉取镜像的。 [10]

如果要配置一个节点上的 Kubelet 并行的拉取镜像,可以在 kubelet 的配置中配置 serializeImagePullsfalse,前提是 Registry Server 和 CRI 支持并行拉取镜像。

Pod

Pod 状态

Pod 的状态字段(status) 是一个 PodStatus Object。其中包含 phase 字段。phase 字段描述/记录了 Pod 在其整个生命周期中所处的阶段 [11]

phase 字段可以有以下值:

Value Description
Pending Kubernetes 集群已经接收到创建 Pod 的请求,并且开始调度到节点、下载容器镜像并启动容器,但是有一个或多个容器还未正常运行
Running Pod 已经被调度到某个节点,并且 Pod 中所有的容器已经被创建,至少其中的一个容器已经处于运行状态或重启中
Succeeded Pod 中所有的容器都已经成功终止,并且不会再被重启
Failed Pod 中所有的容器都被终止,但至少有一个容器终止失败,也就是说,容器要么以非零状态码退出或者是被系统终止
Unknow 因为某些原因,Pod 的状态获取不到。这个经常发生在和 Pod 运行的节点通信异常时

Pod 中的容器的状态

Kubernetes 会跟踪 Pod 中每个容器的状态。一旦通过 Scheduler 将 Pod 分配到某个 Node,Kubernetes 会通过 CRI 创建 Pod 中的容器。

在 Kubernetes 中,容器可以有以下 3 种状态 [11]

Value Description
Waiting 表示容器正在进行必要的操作以正常启动,比如拉取镜像、应用 Secret 数据等。当容器处于此状态时,可以通过 kubectl describe pod 查看输出中对应容器的信息,一般会包含一个 Reason 字段简述 Waiting 的原因
Running 表示容器在正常运行
Terminated 表示容器运行结束或者因为某些原因失败。详细信息可以通过 kubectl describe pod 查看

QoS

Kubernetes 会根据 Pod 定义中的 resource requests and limits 将 Pod 划归到某一个 QoS 类中,Kubernetes 将会根据 QoS 类来决定在节点资源不足的情况下,哪些 Pod 将会被优先驱逐(Evict)。目前存在的 QoS 类主要有以下三个 [12]

QoS Class 说明 被驱逐权重
Guaranteed Pod 中的 每个容器必须定义 CPU & Mem requestlimit,并且 request 的值等于 limit 的值,确保系统会预留定义的资源给 Pod 和其中的容器 最后被驱逐
Burstable Pod 中 至少有一个容器定义了 CPU Mem 的 request。可以不定义 limit如果未定义 limit,默认可以使用节点上所有可用的资源 中间级别
BestEffort 未定义任何 CPU Mem 的 requestlimit 最先被驱逐

Memory QoS 基于系统内核的 cgroup v2 实现 [12]

Pod 中如果有个容器因为 Limit 被 kill,系统只会 kill 掉这一个容器并重启之,不会影响其他容器

脚注

环境信息

  • Centos7 5.4.212-1
  • Docker 20.10.18
  • containerd.io-1.6.8
  • kubectl-1.25.0
  • kubeadm-1.25.0
  • kubelet-1.25.0

几乎所有的 Kubernetes 对象都包含两个嵌套的对象字段:

  • spec - 对象的期望状态(desired state)的描述信息
  • status - 对象当前的状态(current state

Kubernetes 系统的目标就是不停的调整对象的当前状态(current state)直到和期望状态(desired state)匹配。

常用字段说明

必需字段

在想要创建的 Kubernetes 对象所对应的 .yaml 文件中,必须配置的字段如下:[1]

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace
  • spec - 你所期望的该对象的状态

metadata

帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace

集群中的每一个对象都有一个 名称name)来标识在同类资源中的唯一性。name 也用来作为 url 中的资源名称 [2]

每个 Kubernetes 对象也有一个 UIDuid)来标识在整个集群中的唯一性。

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

标签和选择算符

标签(labels) 是附加到 Kubernetes 对象(比如 Pod)上的键值对。 标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。[3]

通过 标签选择算符,客户端/用户可以识别一组对象。标签选择算符 是 Kubernetes 中的核心分组原语。

对于某些 API 类别(例如 ReplicaSet)而言,两个实例的标签选择算符不得在命名空间内重叠, 否则它们的控制器将互相冲突,无法确定应该存在的副本个数。

比较新的资源,例如 JobDeploymentReplicaSetDaemonSet, 也支持基于集合的需求。

selector:
matchLabels:
component: redis
matchExpressions:
- {key: tier, operator: In, values: [cache]}
- {key: environment, operator: NotIn, values: [dev]}

matchLabels 是由 {key,value} 对组成的映射。 matchLabels 映射中的单个 {key,value} 等同于 matchExpressions 的元素, 其 key 字段为 keyoperatorIn,而 values 数组仅包含 valuematchExpressions 是 Pod 选择算符需求的列表。 有效的运算符包括 InNotInExistsDoesNotExist。 在 InNotIn 的情况下,设置的值必须是非空的。 来自 matchLabelsmatchExpressions 的所有要求都按 逻辑与 的关系组合到一起 – 它们必须都满足才能匹配

字段选择器

字段选择器(Field selectors) 允许你根据一个或多个资源字段的值 筛选 Kubernetes 资源

下面是一些使用字段选择器查询的例子:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

下面这个 kubectl 命令将筛选出 status.phase 字段值为 Running 的所有 Pod:

kubectl get pods --field-selector status.phase=Running

不同的 Kubernetes 资源类型支持不同的字段选择器。 所有资源类型都支持 metadata.namemetadata.namespace 字段。 使用不被支持的字段选择器会产生错误

你可在字段选择器中使用 ===!==== 的意义是相同的)操作符。 例如,下面这个 kubectl 命令将筛选所有不属于 default 命名空间的 Kubernetes 服务:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

同标签和其他选择器一样, 字段选择器可以通过使用逗号分隔的列表组成一个选择链。 下面这个 kubectl 命令将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于 Always 的所有 Pod:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

你能够跨多种资源类型来使用字段选择器。 下面这个 kubectl 命令将筛选出所有不在 default 命名空间中的 StatefulSetService

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default
阅读全文 »

环境信息

  • Filebeat 8.8

Filebeat 启动之后,会根据配置文件中配置的数据收集路径启动 1 个或者多个 inputs。针对匹配到的每个文件,Filebeat 都会启动一个对应的 harvester,每个 harvester 读取一个单独的文件内容,并将其中的新内容发送到 libbeatlibbeat 会整合数据并将其发送到配置的 output [1]

如果 input 类型为 loginput 会负责找到配置中匹配到的文件,并为每个文件启动一个 harvester。每个 input 都运行在自己独立的 Go routine (例程) 中。 [4]

Filebeat 工作原理

Filebeat 会将收割(harvest) 的每个文件的状态存储到 registry。因此如果要让 Filebeat 从头开始收集数据,只需要删除 registry 文件即可。可以使用命令 filebeat export config 找到 data 目录,registry 位于其中。

安装

Filebeat 官方安装文档

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.8.2-x86_64.rpm
rpm -vi filebeat-8.8.2-x86_64.rpm

systemctl enable --now filebeat

filebeat command-line interface

filebeat 命令常用功能 [3]

子命令 说明 示例
help 查看命令帮助信息
filebeat help COMMAND_NAME [FLAGS]
export 导出 配置 等信息 export 使用
modules 模块管理命令。用来 enable 或者 disable 位于 modules.d 中的 模块
filebeat modules disable MODULE
filebeat modules enable MODULE
filebeat modules list
Data collection modules
run 启动 filebeat
test 测试配置文件 或者到 output 的连接
filebeat test config
filebeat test output
version 显示当前版本信息
阅读全文 »

和田玉的分类

山料和籽料

按照矿产分类,主要分为籽料与山料两种。

山料,是籽料的前身

山料

和田玉山料 产自昆仑山脉之中,属于原生矿,被称为 璞玉

从质地上说,有的山料的结构较为粗糙,颗粒比较明显或带石性,不带皮色,油性差,有的山料靠近外部有红糖的颜色。也有结构细密,油润度极好的优质山料。

山料因为没有受到河流的冲刷,自始至终都深藏在岩体中,缺乏外力的打磨,看起来比较干涩,所以外形大多平淡无奇,有棱有角,表面也比较干涩,绺裂现象明显。

籽料

和田玉籽料 是采自河床或者河床泥土中,经过几十万年的侵蚀打磨而得到的玉石。有时又被称为 水籽

籽料是由山料在地质活动时冲刷或滚落到河里,由河水冲刷(还有风化、风干等物理反应一起作用)形成的。籽料产自 河流中下游,是次生矿。产自河流上游的被称为 山流水料。籽料因为长久受到河流的冲刷,一般为卵石形状,表面很光滑圆润,带皮色。

和山料的存在环境不同,导致籽料有了以下特性:

  • 籽料常年在河水中浸泡,水流的压力使它的肉质变得更加紧密,水流的冲刷使它变的更加温润油腻,形成了它独特的质地和油润感;
  • 河水中存在很多的矿物质和微量元素,长时间的水流冲刷和加压过程中,部分矿物质会沉淀到籽料中,形成各种颜色的籽料皮子。带有皮子的籽料,价格有大大提升

籽料开采出来,外观上看就与鹅卵石一般无二,各个颜色都有,只不过他是和田玉再形成的鹅卵石。仔细观察籽料表面会发现上面密密麻麻都是一些小孔,因为这些小孔跟我们皮肤上的毛孔相似被称之为毛孔或者皮孔。和田玉籽料一般有皮色和皮孔,就算没有皮色,籽料上还是能够找到均匀的毛细孔,这是籽料独有的标志。而山料则没有皮色皮孔。

一般情况下,籽料都会拥有或深或浅的皮色,表面还会有明显的小凹坑(也就是俗称的皮孔),这是玉石长时间在水中冲刷、浸泡和磕碰造成的。而山料由于生长环境固定,不存在皮色和皮孔。

同时,由于籽料长年累月泡在水中,水里的微生物和微量元素会渗入到玉石中,所以会造成局部呈现或黄或红或黑的颜色,也就是 沁色。而这一点在山料身上,就非常不明显。

籽料的水润度是明显高于山料的,如果比较籽料和山料制作的成品,会很容易发现籽料表面质地非常润泽、光滑、细腻。而山料作品,即使经过抛光处理,表面仍会干涩,缺乏光滑的油润感,这一点用手触摸时尤为明显。

由于 和田玉籽料的密度比山料大,所以同等体积的情况下,籽料相对山料比较重

山料也有很多可以跟顶级籽料相媲美的。比如 1995 年于田县开采的 8 吨和田玉山料,白度细度等各个方面都是顶级。因品质极佳,入市后引起了轰动,后被称之位“95于田料”。时至今日,“95于田料” 在业内也是传说中的料子,被很多业内人士公认为 羊脂级。著名的和氏璧就是一块山料。

和田玉器的市场价格中羊脂玉最高、籽料高、山流水次之、山料更次之。

和田玉结构

和田玉籽料内部结构致密,用肉眼几乎看不到内部结构;而再好的山料,还是能看到其松软质地的结构。

籽料由于有外力的挤压、打磨,所以密度要明显高过山料。籽料的话,无论是青籽还是白籽,内部结构都是致密的,用肉眼几乎看不到内部结构。而山料,无论结构多致密,在强光手电筒下,总是能看到松软的质地结构。

广义的籽料

今天的籽料已不是传统意义上的和田籽料了。在广义和田玉的范围内,籽料也有了更多种类。

新疆产的岫玉籽料

新疆除了有和田玉籽料,还出产一种颜色或绿或黑绿色等,里面也有黑点存在的岫玉。岫玉中品质好的,外观与和田玉籽料很相似,并且在市场上的和田籽料中也能看到相当数量的岫玉籽料,大家要留意。

来自俄罗斯的“籽料”

俄罗斯玉,近年来随着和田料的减少而逐渐成为软玉舞台的有力竞争者。

从玉材来看,俄料95%都是山料,但也有籽料,只是俄料中的“籽料”并未达到籽料定义的产状玉料:籽料由于长期的冲刷搬运碰撞,玉质形态韧性都达到了特别的质感,而俄料中的“籽料”其实没有这个长期的过程,严格意义上只能算和田玉的山流水料。

辽宁产的河磨料

河磨料是国内确切命名的两种籽料之一,另一种是和田籽料。“河磨”,从河里磨出来的玉,名字起得很传神,可能因为河水的冲击力较小,河磨料多见厚皮料。

另外,近几年贵州罗甸也出产白度尚可的软玉,透闪石含量达95%,也是广义和田玉中的一种。罗甸玉石性很重,料子干涩,缺乏油性,其实所谓的“籽料”也只能用来骗骗小白玩友;广西大化玉则是这几年才出现的玉种,也是广义和田玉中的一种,大化玉里有籽料,质地也很细腻,有兴趣的宝友可以玩一玩。

环境信息

  • Centos7 5.4.212-1
  • Docker 20.10.18
  • containerd.io-1.6.8
  • kubectl-1.25.0
  • kubeadm-1.25.0
  • kubelet-1.25.0

POD 状态异常

CrashLoopBackOff

错误场景

Pod 状态显示 CrashLoopBackOff

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
test-centos7-7cc5dc6987-jz486 0/1 CrashLoopBackOff 8 (111s ago) 17m

查看 Pod 详细信息

$ kubectl describe pod test-centos7-7cc5dc6987-jz486
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 18m default-scheduler Successfully assigned default/test-centos7-7cc5dc6987-jz486 to ops-kubernetes3
Normal Pulled 16m (x5 over 18m) kubelet Container image "centos:centos7.9.2009" already present on machine
Normal Created 16m (x5 over 18m) kubelet Created container centos7
Normal Started 16m (x5 over 18m) kubelet Started container centos7
Warning BackOff 3m3s (x71 over 18m) kubelet Back-off restarting failed container

结果显示,ReasonBackOffMessage 显示 Back-off restarting failed container

可能原因

Back-off restarting failed container 的原因,通常是因为,容器内 PID 为 1 的进程退出导致(通常用户在构建镜像执行 CMD 时,启动的程序,均是 PID 为1)[1]

容器进程退出(命令执行结束或者进程异常结束),则容器生命周期结束。kubernetes 控制器检查到容器退出,会持续重启容器。针对此种情况,需要检查镜像,是否不存在常驻进程,或者常驻进程异常。

针对此种情况,可以单独使用 docker 客户端部署镜像,查看镜像的运行情况,如果部署后,容器中的进程立马结束或退出,则容器也会随之结束。

定位中也可以使用 kubectl describe pod 命令检查 Pod 的退出状态码。Kubernetes 中的 Pod ExitCode 状态码是容器退出时返回的退出状态码,这个状态码通常用来指示容器的执行结果,以便 Kubernetes 和相关工具可以根据它来采取后续的操作。以下是一些常见的 ExitCode 状态码说明:

  • ExitCode 0 : 这表示容器正常退出,没有错误。这通常是期望的结果。
  • ExitCode 1 : 通常表示容器以非正常方式退出,可能是由于应用程序内部错误或异常导致的。通常是容器中 pid 为 1 的进程错误而失败
  • ExitCode 非零 : 任何非零的状态码都表示容器退出时发生了错误。ExitCode 的具体值通常是自定义的,容器内的应用程序可以根据需要返回不同的状态码来表示不同的错误情况。你需要查看容器内应用程序的文档或日志来了解具体的含义。
  • ExitCode 137 : 通常表示容器因为被操作系统终止(例如,OOM-killer)而非正常退出。这可能是由于内存不足等资源问题导致的。
  • ExitCode 139 : 通常表示容器因为接收到了一个信号而非正常退出。这个信号通常是 SIGSEGV(段错误),表示应用程序试图访问无效的内存。
  • ExitCode 143 : 通常表示容器因为接收到了 SIGTERM 信号而正常退出。这是 Kubernetes 在删除 Pod 时发送的信号,容器应该在接收到该信号后做一些清理工作然后退出。
  • ExitCode 130 : 通常表示容器因为接收到了 SIGINT 信号而正常退出。这是当用户在命令行中按下 Ctrl+C 时发送的信号。
  • ExitCode 255 :通常表示未知错误,或者容器无法启动。这个状态码通常是容器运行时的问题,比如容器镜像不存在或者启动命令有问题。
阅读全文 »

环境信息

  • Centos-7 3.10.0-1062.9.1
  • Docker 19.03.15
  • containerd.io-1.4.13
  • kubectl-1.25.0
  • kubeadm-1.25.0
  • kubelet-1.25.0

kubeadm 常用命令

kubeadm 命令官方参考文档

创建集群

kubeadm init --pod-network-cidr=10.244.0.0/16 --cri-socket=unix:///var/run/cri-dockerd.sock
选项 说明 示例
--pod-network-cidr 指定 pod 的 cidr,安装 CNI 插件时,配置的 CIDR 要和此处一致
--service-cidr service 使用的 CIDR
--cri-socket 配置集群使用的 CRI,不指定时系统会扫描主机,如果有多个可用 CRI,会出现提示
--apiserver-advertise-address 手动配置 api-server 的 Advertise IP 地址。
不配置的情况下,系统默认选择主机上的默认路由对应网卡上面的 IP
--control-plane-endpoint 配置 api-server 的共享地址,可以是域名或者负载均衡器的 IP
单节点的 Master 后期需要扩展为多节点(高可用)时,需要有此配置,否则不支持(kubeadm)扩展

添加节点到集群

kubeadm join 172.31.10.19:6443 --token 8ca35s.butdpihinkdczvqb --discovery-token-ca-cert-hash sha256:b2793f9a6bea44a64640f99042f11c4ff6 \ 
--cri-socket=unix:///var/run/cri-dockerd.sock

其中的 token 可以在 master 上使用以下命令查看

$ kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
8ca35s.butdpihinkdczvqb 19h 2022-09-14T02:54:55Z authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token

默认情况下,令牌会在 24 小时后过期。如果要在当前令牌过期后将节点加入集群, 则可以通过在控制平面节点上运行以下命令来创建新令牌:

kubeadm token create

如果你没有 --discovery-token-ca-cert-hash 的值,则可以通过在控制平面节点上执行以下命令链来获取它[1]

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
阅读全文 »