知识库
- 科学上网
- 免费申请ssl证书
- 三节点哨兵集群部署redis
- elasticsearch
- VUE前端缓存不刷新问题
- nginx
- git使用指南
- centos 配置telnet登陆
- 反向代理&&端口转发&&klipper&&proxy
- ansible使用
- traefik-gateway
- openssh升级
- traefik-gateway-dashboard
- 制作离线yum源
- centos7 阿里源
- BookStack
- 手工编译qt6.7.1 WebEngine
- ansible 一键修改服务器密码脚本
- qt集成cef(Chromium Embedded Framework) 实现自己的浏览器
- docker centos 支持中文
- busybox&&docker镜像支持systemd
- datart通过自定义视图查询数据源数据
- 定时备份mysql
- MYSQL维护指南
- mysql主从复制&&读写分离
- 不重新打包修改nginx-web镜像写死的API地址
- pritunl-vpn
- 在无GUI界面的Linux服务器上执行jmeter
- 室内地图制作方法
- linux密码规则设置
- repmgr+PostgreSQL故障自动转移
- EFK的安装部署
- linux时间同步
- windows下安装wget
- ibd2mysql&&mysql数据库idb文件修复
- 堡垒机jumpserver使用手册
- rabbitmq
- tmux终端复用&&后台运行终端
- 云监控bt-monitor
- mogodb
- linux时间同步
- nexus
- 升级gcc
- gitlab
- linux安装nodejs和codex
- redis单节点部署
- 使用codex修复服务器漏洞升级openssh
- minio
- 打包多平台docker镜像
- jenkins&&docker安装
- 达梦数据库
- 单机运行postgresql&& 安装kong-gateway
- kafka集群搭建
- otter&&数据库实时同步工具
- dbeaver&&mysql可视化web管理工具
- percona-toolkit
- 自建邮件服务器&&mailserver
- opensearch
- mockserver&&模拟测试工具
- ninedata&&数据库工具
- pull&&retag&&push
- elastach-search常用命令
- 阿里云oss&&ossfs
- windows 安装codex
- 前端开发常见坑处理
- 新页面
科学上网
参考网站
-
github:https://github.com/shadowsocks/go-shadowsocks2?tab=readme-ov-file
-
官方帮助:https://pkg.go.dev/github.com/shadowsocks/go-shadowsocks2#readme-install
linux使用 proxychains4 程序级代理
apt install proxychains4
vi /etc/proxychains4.conf
# 注释掉这行
# dynamic_chain
# 取消注释(关闭日志输出,可能干扰TUI,也可用 -q)
quiet_mode
# 增加代理服务器
[ProxyList]
socks5 127.0.0.1 1080
使用
# proxychains4 <命令> <参数>
proxychains4 curl cip.cc
# 增加别名
vi ~/.bashrc
alias proxy4="proxychains4"
alias pcurl="proxychains4 -q curl"
alias pwget="proxychains4 -q wget"
# 刷新配置
source ~/.bashrc
# 日志干扰 TUI
proxy4 -q curl cip.cc
# 使用短别名
pcurl cip.cc
# 注意docker不适用此方法,因为docker只是一个 接口命令行,真正运行的是dockerd,应该修改docker服务文件(未验证)
-
客户端配置(netch 虚拟网卡模式)
下载软件
- 官方下载:https://github.com/netchx/netch/releases
- 加速下载:https://iovhm.com/uploads/software/tools/Netch.zip
增加 shadowsocks服务器配置
选择模式
设置dns也代理
点击启动,启动成功后,会多出一个aicloud的虚拟网卡
也兼容socks5插件方式
-
客户端配置(socks5浏览器插件方式)
- 客户端下载
windows 客户端下载: https://iovhm.com/uploads/shadowsocks2/shadowsocks-4.4.1.0.zip
解压缩后,双击小火箭图标,会在任务栏出现小图标,右键任务栏小图标,填入配置信息
- 填入服务器配置
- 浏览器插件下载
https://iovhm.com/uploads/shadowsocks2/extension_2_5_21_0.crx
- 安装浏览器扩展
浏览器地址输入 chrome://extensions/ 或者找到扩展工具设置,将 extension_2_5_21_0.crx 拖到窗口进行安装。
- 让扩展程序显示在地址栏
- 设置浏览器扩展
- 代理协议:SOCKS5
- 代理服务器: 127.0.0.1
- 代理端口:1080 ,既第一步中,配置的本地监听端口
- 设置不代理地址列表,国内地址不经过代理
填入以下地址,选择右边的应用选项保存
*.baidu.com
*.jd.com
*.taobao.com
*.zhihu.com
*.qq.com
*.cn
*.huaweicloud.com
*.alipay.com
*.aliyun.com
*.alicdn.com
*.csdn.net
*.csdn.com
*.douyin.com
::1
127.0.0.1
localhost
- 选择浏览器使用代理方式连接到网络
-
注意事项
系统代理选择禁用,不要选择PAC模式和全局模式,此2个模式会将所有的网络流量全部走代理,浪费代理资源。比较科学的办法是将插件安装在edge浏览器上用于科学上网;正常访问使用chrome浏览器,便捷不用切来切去。
linux使用方法
./shadowsocks2-linux -c 'ss://aes-256-gcm:<password>@<server_ip>:8080' -socks :1080 -verbose
# 设置环境变量
export http_proxy=socks5://127.0.0.1:1080
export https_proxy=socks5://127.0.0.1:1080
export all_proxy=socks5://127.0.0.1:1080
# 取消设置环境变量
unset http_proxy
unset https_proxy
unset all_proxy
docker使用代理上网
-
参见docker使用代理上网:https://iovhm.com/book/books/k8s/page/docker
-
翻墙违法
- 仅限于工作需要的 google、github 等
- 请不要通过代理搜索、查看、传阅敏感话题
- 请不要通过代理观看、下载视频
- 请不要通过代理浏览非法网站、娱乐网站、社交网站等
- 没有访问外网需要时,请选择直连或者系统代理,节约资源
-
自建服务器安装
找一台海外linux服务器,推荐使用阿里云的轻应用服务器,节点选香港或者新加坡
- 安装服务器端
# 服务器端下载
wget http://iovhm.com/uploads/shadowsocks2/shadowsocks2-linux
cp ./shadowsocks2-linux /usr/local/bin
chmod 777 /usr/local/bin/shadowsocks2-linux
- 编写服务配置文件
# vi /usr/lib/systemd/system/shadowsocks2.service
[Unit]
Description=shadowsocks2
After=network.target
Wants=network-online.target
[Service]
Restart=always
Type=simple
ExecStart=/usr/local/bin/shadowsocks2-linux -s 'ss://aes-256-gcm:<password>@:8080' -verbose
# 工作于client模式
# ExecStart=/usr/local/bin/shadowsocks2-linux -c 'ss://aes-256-gcm:<password>@<server_ip>:8080' -socks :1080 -verbose
[Install]
WantedBy=multi-user.target
免费申请ssl证书
注册账号
在网站 https://zerossl.com/ 注册账号,按下图步骤获取acme key , 将EAB KID 和 EAB HMAC key记录下来
安装和使用
参考网址:https://github.com/acmesh-official/acme.sh
# 安装
wget -O - https://get.acme.sh | sh
# 增加别名(非必要)
alias acme.sh=~/.acme.sh/acme.sh
# 进入到执行目录(非必要)
cd /root/.acme.sh
# 2023-1-1 更新,更换了默认证书提供商为zerossl
# https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA
# 绑定和配置账号,填入你前面记录的EAB KID和EAB HMAC key
./acme.sh --register-account --server zerossl \
--eab-kid <your kid> \
--eab-hmac-key <your key>
# 配置默认证书提供商
./acme.sh --set-default-ca --server zerossl
# 第一步
./acme.sh --debug --issue --dns -d *.saas.vppark.cn --yes-I-know-dns-manual-mode-enough-go-ahead-please --server zerossl
# 增加DNS txt验证记录
# 申请证书,多了一个参数 --renew
./acme.sh --debug --issue --renew --dns -d *.saas.vppark.cn --yes-I-know-dns-manual-mode-enough-go-ahead-please --server zerossl
# 查看本地证书
./acme.sh --list
# 更新所有的证书
acme.sh --renew-all --server zerossl
# 安装证书, 主要是两个文件,私匙(key)和证书(cer)
key: *.saas.vppark.cn.key
cer: fullchain.cer
# 查看证书过期日期
openssl x509 -enddate -noout -in fullchain.cer
openssl x509 -text -noout -in fullchain.cer
# 如果后续进行更新,使用的是zerossl,需要到zerossl网站下载更新的证书。并且将两个证书进行合并,否则会提示证书链不完整。
# 升级软件
acme.sh --upgrade
使用 阿里云 dnsapi自定添加域名认证
更多其他域名提供商api使用方法:https://github.com/acmesh-official/acme.sh/wiki/dnsapi
使用阿里云dns api时,需要先获取到Ali_Key和Ali_Secret,获取方法为
# 参考网站:https://github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ali
# 设置阿里云dns api需要的key和secret
export Ali_Key="<key>"
export Ali_Secret="<secret>"
# 申请证书
./acme.sh --issue --dns dns_ali -d *.iovhm.com -d iovhm.com
# --dns dns_ali 使用阿里云dns api
# key保存于 /root/.acme.sh/account.conf
# 保存的key和secret被重命名为 SAVED_Ali_Key='<key>'和SAVED_Ali_Secret='<secret>'
# 如果你的dns提供商不支持acme api或者你没有权限管理主域名,可以使用dns cname模式
# 使用dns别名自动验证域名并申请证书
./acme.sh --issue --dns dns_ali -d vppark.cn -d *.vppark.cn -d *.saas.vppark.cn -d *.ihg.vppark.cn -d *.devops.vppark.cn --challenge-alias acme-vppark-cn.iovhm.com --force
# --challenge-alias acme-vppark-cn.iovhm.com 指定dns别名,acme 将自动在别名域名上添加相关认证记录,使认证域名记录变成 _acme-challenge.acme-vppark-cn.iovhm.com
# 既主域名设置的别名于别名记录有一定的匹配关系
# 主域名的别名设置应该是 _acme-challenge.saas.vppark.cn => _acme-challenge.acme-vppark-cn.iovhm.com
# 而不是 _acme-challenge.saas.vppark.cn => acme-vppark-cn.iovhm.com
# 证书列表
./acme.sh --list
# 重新申请证书
./acme.sh --renew -d iovhm.com --force
# 更新证书
# 只需要写主域名既可以自动将所有证书全部更新
./acme.sh --renew -d iovhm.com --force
# 查看证书过期日期
openssl x509 -enddate -noout -in fullchain.cer
openssl x509 -text -noout -in fullchain.cer
三节点哨兵集群部署redis
docker-compose.yaml
version: '3.1'
services:
redis01:
image: swr.cn-southwest-2.myhuaweicloud.com/vp-guiyang/redis:5.0.0
command: sh -c "redis-server /usr/local/redis/redis.conf; redis-sentinel /usr/local/redis/sentinel.conf"
ports:
- 36379:6379
- 46379:26379
volumes:
- ./redis01/data:/data
- ./redis01/redis.conf:/usr/local/redis/redis.conf
- ./redis01/sentinel.conf:/usr/local/redis/sentinel.conf
restart: always
redis02:
image: swr.cn-southwest-2.myhuaweicloud.com/vp-guiyang/redis:5.0.0
command: sh -c "redis-server /usr/local/redis/redis.conf; redis-sentinel /usr/local/redis/sentinel.conf"
ports:
- 36380:6379
- 46380:26379
volumes:
- ./redis02/data:/data
- ./redis02/redis.conf:/usr/local/redis/redis.conf
- ./redis02/sentinel.conf:/usr/local/redis/sentinel.conf
restart: always
redis03:
image: swr.cn-southwest-2.myhuaweicloud.com/vp-guiyang/redis:5.0.0
command: sh -c "redis-server /usr/local/redis/redis.conf; redis-sentinel /usr/local/redis/sentinel.conf"
ports:
- 36381:6379
- 46381:26379
volumes:
- ./redis03/data:/data
- ./redis03/redis.conf:/usr/local/redis/redis.conf
- ./redis03/sentinel.conf:/usr/local/redis/sentinel.conf
restart: always
redis配置文件需要修改的地方
redis主节点
redis 配置文件
bind 0.0.0.0
protected-mode no
port 36379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile "/var/run/redis_6379.pid"
loglevel notice
logfile "/var/log/redis.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/data"
masterauth "fkabcd@124"
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
replica-announce-ip "172.18.41.8"
replica-announce-port 36379
requirepass "fkabcd@124"
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync ye
- 将bind 127.0.0.1改为bind 0.0.0.0
- protected-mode yes改为 protected no
- logfile "" 改为 logfile "/var/log/redis.log"(方便日后排查redis故障)
- databases 16 (可以根据需求该需要的数据库数量)
- 如果使用RDB做持久化,可以修改下面的配置 save 900 1 save 300 10 save 60 10000
- dbfilename dump.rdb指定rdb在保存的文件名(可以根据需要修改)
- dir /data 指定dump.rdb存放的目录,需要与挂载目录保持一致。保证数据能正常持久化到硬盘中
- requirepass 配置redis的密码
redis从节点
bind 0.0.0.0
protected-mode no
port 36380
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile "/var/run/redis_6379.pid"
loglevel notice
logfile ""
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/data"
masterauth "fkabcd@124"
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
replica-announce-ip "172.18.41.8"
replica-announce-port 36380
requirepass "fkabcd@124"
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
replicaof 172.18.41.8 36379
- 在主节点的基础上加入replica-serve-stale-data yes
哨兵的配置修改
bind 0.0.0.0
protected-mode no
port 46379
daemonize no
pidfile "/var/run/redis-sentinel.pid"
logfile "/var/log/redis-sentinel.log"
dir "/tmp"
sentinel myid 4f0cb523cf658cf7f88bd34907ff6e7f889b45cf
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 172.18.41.8 36379 2
sentinel auth-pass mymaster fkabcd@124
sentinel config-epoch mymaster 3
sentinel leader-epoch mymaster 3
sentinel known-replica mymaster 172.18.41.8 36380
sentinel known-replica mymaster 172.18.41.8 36381
sentinel known-replica mymaster 192.168.96.1 36380
sentinel known-sentinel mymaster 192.168.96.7 46381 cf930cbf3f06c6601eb294639ed446890e73ada3
sentinel known-sentinel mymaster 192.168.96.3 46380 7dcc1193de11e5adf5f31c340ca4bc14db909b1c
sentinel current-epoch 3
- logfile "" 改为 logfile "/var/log/redis-sentinel.log"(方便日后排查故障)
- sentinel announce-ip 172.18.41.8 (用来告诉其他哨兵我的地址是多少)
- sentinel announce-port 46379 (用来告诉其他哨兵我的端口是多少)
- sentinel monitor mymaster 172.18.41.8 36379 2(配置监控的redis 名字、master的地址、端口、quorum) quorm计算公式:quorum = (n / 2) + 1 (n表示主机数量)
- sentinel auth-pass mymaster 密码 配置主监控的认证密码
elasticsearch
version: '3'
services:
elasticsearch-1:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
restart: always
container_name: elasticsearch-1
privileged: true
ports:
- 9211:9200
- 9300:9300
environment:
- node.name=es01 # 节点名称,唯一
- network.host=0.0.0.0 # 节点IP,单节点时候可以不需要
- network.publish_host=172.16.2.106 # 发布地址
- cluster.name=vpclub-log # 集群名称,集群一致
- cluster.initial_master_nodes=es01,es02,es03 # 集群节点成员
- discovery.seed_hosts=elasticsearch-2,elasticsearch-3 # 从指定主机发现
- xpack.security.enabled=false # xpack 安全设置
- xpack.security.http.ssl.enabled=false # xpack 安全设置
# - discovery.type=single-node # 单节点
- ES_JAVA_OPTS=-Xms512m -Xmx512m
volumes:
- ./data1:/usr/share/elasticsearch/data
extra_hosts:
- "elasticsearch-1:172.16.2.106"
- "elasticsearch-2:172.16.2.107"
- "elasticsearch-3:172.16.2.108"
networks:
- elasticsearch-bridge
elasticsearch-2:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
restart: always
container_name: elasticsearch-2
privileged: true
ports:
- 9211:9200
- 9300:9300
environment:
- node.name=es02 # 节点名称,唯一
- network.host=0.0.0.0 # 节点IP,单节点时候可以不需要
- network.publish_host=172.16.2.107 # 发布地址
- cluster.name=vpclub-log # 集群名称,集群一致
- cluster.initial_master_nodes=es01,es02,es03 # 集群节点成员
- discovery.seed_hosts=elasticsearch-1,elasticsearch-3 # 从指定主机发现
- xpack.security.enabled=false # xpack 安全设置
- xpack.security.http.ssl.enabled=false # xpack 安全设置
# - discovery.type=single-node # 单节点
- ES_JAVA_OPTS=-Xms512m -Xmx512m
volumes:
- ./data2:/usr/share/elasticsearch/data
extra_hosts:
- "elasticsearch-1:172.16.2.106"
- "elasticsearch-2:172.16.2.107"
- "elasticsearch-3:172.16.2.108"
networks:
- elasticsearch-bridge
elasticsearch-3:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
restart: always
privileged: true
container_name: elasticsearch-3
ports:
- 9211:9200
- 9300:9300
environment:
- node.name=es03 # 节点名称,唯一
- network.host=0.0.0.0 # 节点IP,单节点时候可以不需要
- network.publish_host=172.16.2.108 # 发布地址
- cluster.name=vpclub-log # 集群名称,集群一致
- cluster.initial_master_nodes=es01,es02,es03 # 集群节点成员
- discovery.seed_hosts=elasticsearch-2,elasticsearch-1 # 从指定主机发现
- xpack.security.enabled=false # xpack 安全设置
- xpack.security.http.ssl.enabled=false # xpack 安全设置
# - discovery.type=single-node # 单节点
- ES_JAVA_OPTS=-Xms512m -Xmx512m
volumes:
- ./data3:/usr/share/elasticsearch/data
extra_hosts:
- "elasticsearch-1:172.16.2.106"
- "elasticsearch-2:172.16.2.107"
- "elasticsearch-3:172.16.2.108"
networks:
- elasticsearch-bridge
networks:
elasticsearch-bridge:
external:
name: elasticsearch-bridge
测试服务状态
# curl localhost:9200/_cluster/health?pretty
{
"cluster_name" : "wuxue-ny",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 0,
"active_shards" : 0,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
# 查看集群信息
# curl localhost:9211/_cluster/health?pretty
# curl localhost:9211/_cat/nodes?v
# curl localhost:9211/_cat/indices?v
VUE前端缓存不刷新问题
nginx配置增加
location / {
# 不缓存首页,解决VUE单页面发版后不生效
add_header Cache-Control "no-cache no-store must-revalidate proxy-revalidate,max-age=0";
add_header Last-Modified $date_gmt;
# 这个有顺序,需要加在最后面
etag off;
}
index.html增加
<meta http-equiv="Cache-Control" content="no-store" />
增加一个新的入口页面,并在APP或者微信公众号设置入口为这个新的页面,例如取名为root.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>数字营业厅</title>
<script type="text/javascript">
function generateRandomChar() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return chars[Math.floor(Math.random() * chars.length)];
}
function generateRandomString() {
const length = 3;
let str = '';
for (let i = 0; i < length; i++) {
str += generateRandomChar();
}
return str;
}
var rnd = generateRandomString();
// 需要根据前端项目是URL模式还是history模式自行修改
location.assign(`./?${rnd}`);
</script>
</head>
<body>
</body>
</html>
nginx
- docker-compose.yaml
version: "3"
services:
mobile-office-web:
image: docker.io/nginx:latest
restart: always # 自动重启
privileged: true
ports:
- 9095:80
# volumes:
# - ./html:/usr/share/nginx/html
# - ./default.conf:/etc/nginx/conf.d/default.conf
environment:
- TZ=Asia/Shanghai
- default.conf
# touch default.conf
server {
listen 80;
#listen 443 ssl;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
#ssl_certificate /home/ssl/server.crt;
#ssl_certificate_key /home/ssl/server.key;
#### 性能优化 ####
# 开启gzip压缩
gzip on;
# 压缩哪些文件类型
gzip_types text/plain text/html text/css application/json text/javascript application/javascript;
# 最小压缩大小,小于这个大小不压缩,单位是字节
gzip_min_length 1000;
# 压缩率,1-9,数字越大压缩的越好,但是也越消耗CPU
gzip_comp_level 6;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁止IE6使用gzip,因为这些浏览器不支持gzip压缩
gzip_disable "MSIE [1-6]\.";
# 在特定条件下对代理服务器的响应进行压缩
gzip_proxied expired no-cache no-store private auth;
# 缓冲区数量和大小,设置了16个8KB的缓冲区
gzip_buffers 16 8k;
# 最小http版本,低于这个版本不压缩
gzip_http_version 1.1;
#### 性能优化 ####
#### 安全设置 ####
# 禁止跨站脚本攻击
add_header X-XSS-Protection "1; mode=block";
# 禁止网页被嵌入到iframe或者frame中
add_header X-Frame-Options "SAMEORIGIN";
# 启用了 HSTS (HTTP 严格传输安全)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
# 防止浏览器对响应的内容类型进行 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff";
#### 安全设置 ####
root /usr/share/nginx/html;
index index.html;
location / {
# 不缓存首页,解决VUE单页面发版后不生效
add_header Cache-Control "no-cache no-store must-revalidate proxy-revalidate,max-age=0";
add_header Last-Modified $date_gmt;
# 这个有顺序,需要加在后面
etag off;
}
location /api/ {
proxy_pass http://project-manager:8080;
proxy_set_header Host $http_host;
}
}
性能优化之启用GZIP
# 增加到http区块(全局有效)或者server区块(单个监听有效)
server {
#### 性能优化 ####
# 开启gzip压缩
gzip on;
# 压缩哪些文件类型
gzip_types text/plain text/html text/css application/json text/javascript application/javascript;
# 最小压缩大小,小于这个大小不压缩,单位是字节
gzip_min_length 1000;
# 压缩率,1-9,数字越大压缩的越好,但是也越消耗CPU
gzip_comp_level 6;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁止IE6使用gzip,因为这些浏览器不支持gzip压缩
gzip_disable "MSIE [1-6]\.";
# 在特定条件下对代理服务器的响应进行压缩
gzip_proxied expired no-cache no-store private auth;
# 缓冲区数量和大小,设置了16个8KB的缓冲区
gzip_buffers 16 8k;
# 最小http版本,低于这个版本不压缩
gzip_http_version 1.1;
}
nginx经常被扫描的几个安全设置
# 增加到 server 区块
server {
# 禁止跨站脚本攻击
add_header X-XSS-Protection "1; mode=block";
# 禁止网页被嵌入到iframe或者frame中
add_header X-Frame-Options "SAMEORIGIN";
# 启用了 HSTS (HTTP 严格传输安全)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
# 防止浏览器对响应的内容类型进行 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff";
}
git使用指南
# Git global setup
git config --global user.name "你的名字"
git config --global user.email "your email"
# Create a new repository
git clone http://10.0.0.43:10080/tjxt/htc-dashboard.git
cd htc-dashboard
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
# Existing folder
cd existing_folder
git init
git remote add origin http://10.0.0.43:10080/tjxt/htc-dashboard.git
git add .
git commit -m "Initial commit"
git push -u origin master
# Existing Git repository
cd existing_repo
git remote rename origin old-origin
git remote add origin http://10.0.0.43:10080/tjxt/htc-dashboard.git
git push -u origin --all
git push -u origin --tags
# 增加文件
git add .
# 提交更改
git commit -m [comment]
# 推送到远程
git push origin loacl:remote
# 获取
git pull origin branch-name
# 修改文件
git status
# 删除文件
git rm
# 移动文件
git mv
# 列出本地分之
git branch
# 列出远程分之
git branch -r
# 列出所有分之
git branch -a
# 新建分支
git branch [branch-name]
# 切换分之
git checkout [branch-name]
# 从指定分之合并
git merge [branch-name]
# 删除分之
git branch -d [branch-name]
# 删除远程分之
git push origin --delete branch-name
# 递归获取
git fetch [remote]
centos 配置telnet登陆
相关软件安装
yum install telnet telnet-server xinetd
修改 pam 模块 telnet 权限
将etc/pam.d/remote中的auth required pam_securetty.so注释掉
修改 pam 模块 telnet 权限
# 修改前
[root@localhost ~]# grep 'pam_securetty.so' /etc/pam.d/remote
auth required pam_securetty.so
# 修改后 -- 注释掉
[root@localhost ~]# grep 'pam_securetty.so' /etc/pam.d/remote
# auth required pam_securetty.so
新建配置文件
[root@localhost ~]# cat << EOF > /etc/xinetd.d/telnet
service telnet
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
log_on_failure += USERID
disable = no
}
EOF
启动服务
systemctl start telnet.socket
systemctl start xinetd
反向代理&&端口转发&&klipper&&proxy
docker pull rancher/klipper-lb:v0.1.2
# 环境变量
SRC_PORT=3306
DEST_PROTO=TCP
DEST_PORT=33306
DEST_IP=192.168.0.10
增加内核功能 NET_ADMIN,或者特权提升
version: "3"
services:
klipper-proxy-80:
image: rancher/klipper-lb:v0.1.2
restart: always # 自动重启
# network_mode: host
ports:
- 33306:3306
environment:
- SRC_PORT=80
- DEST_PROTO=TCP
- DEST_PORT=8880
- DEST_IP=139.9.199.54
privileged: true # 特权提升
ansible使用
-
安装
# 安装扩展源
yum install epel-release
yum install ansible
-
编写配置文件
# 编辑配置文件
vi /etc/ansible/hosts
# 加入主机
[groupName]
192.168.0.10
10.vpclub.io
192.168.0.11 ansible_user=<name>,ansible_password=<passwrd>
# 支持的参数有
# ansible_port 端口
# ansible_user 用户名
# ansible_password 密码
-
配置免密登录
如果不想在ansible填写用户密码,可以配置免密登录
# 生产RSA密匙对
ssh-keygen -t rsa
# 使用命令将密公钥复制到目标主机
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.2
-
使用命令
ansible all -m <模块名> -a "参数"
ansible groupName -m <模块名> -a "参数"
ansible 10.vpclub.io -m <模块名> -a "参数"
traefik-gateway
本文为traefik基础用法,dashboard用法请参考:traefik-gateway-dashboard
traefik 是一个优秀的反向代理软件,提供与nginx类似的功能。
与nginx对比,其优势在于,nginx需要编写配置文件后,重新启动nginx以生效。nginx不支持tcp代理(使用插件可以支持)
| 特性 | nginx | traefik |
|---|---|---|
| 动态配置 | 不支持动态配置 | 外部文件、redis、json、etcd |
| 修改配置重启 | 需要重启 | 不需要重启 |
| tcp代理 | 不支持(需要重新编译源码和插件) | 支持 |
| web容器 | 支持 | 不支持 |
| 反向代理自动携带HOST | 支持 | 不支持,需要使用中间件 |
利用此traefik的一些特性,可以将其当做入口网关使用
官方网站
- [官方网站]:https://doc.traefik.io/traefik/
主要概念
- entryPoints: 入口点,监听地址,支持http、https、tcp、udp
- routers:路由(路径)
- services:后端服务
- middlewares:中间件,在执行反向代理前、后可以执行一些操作 插件参考网址
- 静态配置文件:traefik启动时需要的配置,入口点,服务发现驱动等
- 动态配置文件:路由、服务、中间件、ssl证书
静态配置示例
# 静态配置
global:
checkNewVersion: true
sendAnonymousUsage: true
entryPoints:
http:
address: :80
# http:
# redirections: # http 自动跳转到 https
# entryPoint:
# to: https
# scheme: https
# tcp:
# address: :9095/tcp
https:
address: :443
http:
tls: {} # 开启 https
log:
level: DEBUG
format: json
# accessLog:
# format: json
api:
insecure: true # 开启dashboard
dashboard: true
debug: true
providers:
file:
# filename: /etc/traefik/conf.d/conf.yaml 单个文件
directory: /etc/traefik/conf.d/ # 监视文件夹
watch: true
# http:
# endpoint: "http://192.168.64.1:3000/api"
# 插件支持
# experimental:
# localPlugins:
# rewritebody:
# modulename: "github.com/traefik/plugin-rewritebody"
# version: "v0.3.1"
动态配置示例
# 动态配置
http:
routers:
# 首页
web-site:
rule: "PathPrefix(`/`)"
service: web-site
middlewares:
- stripprefix-common
iovhm-api:
rule: "PathPrefix(`/iovhm-api/`)"
service: iovhm-api
# middlewares:
# - testHeader
############################################################################
services:
web-site:
loadBalancer:
servers:
- url: http://web-site:80
iovhm-api:
loadBalancer:
servers:
- url: http://iovhm-web-api.gxzszs.cn/
############################################################################
middlewares:
stripprefix-common:
stripPrefix:
prefixes:
- "/foo"
- "/home-admin"
testHeader:
headers:
customRequestHeaders:
host: "iovhm-web-api.gxzszs.cn"
#tcp:
# routers:
# abc:
# entryPoints:
# - "tcp"
# rule: "HostSNI(`*`)"
# service: my-service
# services:
# my-service:
# loadBalancer:
# servers:
# - address: 139.9.93.117:80
# 证书列表,会根据域名自动匹配
tls:
certificates:
- certFile: /home/ssl/qq829cn.cer
keyFile: /home/ssl/qq829cn.key
# 默认证书
# tls:
# stores:
# default:
# defaultCertificate:
# certFile: "/home/ssl/qq829cn.cer"
# keyFile: "/home/ssl/qq829cn.key"
docker-compose.yaml配置文件
# docker-compose
version: "3"
services:
mobile-office-web:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/digital-base/traefik:latest
restart: always # 自动重启
privileged: true
ports:
# - 8080:80
# - 8443:443
- 80:80
- 443:443
- 8081:8080
# - 9095:9095
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml
- ./conf.d/:/etc/traefik/conf.d/
- ./ssl:/home/ssl
# - ./plugins:/plugins-local
environment:
- TZ=Asia/Shanghai
openssh升级
重要,使用新的客户端成功连接前,千万不要断开ssh连接,否则就接不上了;确定升级完成、新的客户端可以连接后才断开,请确保电脑连接了电源,网络畅通
升级openssl
openssl 源代码网站:https://www.openssl.org/source/
# 下载源代码
wget https://www.openssl.org/source/openssl-3.3.1.tar.gz
# 解压缩
tar -xvzf openssl-3.3.1.tar.gz
# 安装依赖
yum install perl-IPC-Cmd
# 配置
./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl
# --prefix=/usr/local/openssl 安装目录
# --openssldir=/usr/local/openssl 配置目录
# 编译
make -j$(nproc)
# 测试,非必要,测试失败不意味着编译安装会失败
make test
# 安装,openssl将被安装到 /usr/local/openssl
make install
# 验证版本
/usr/local/openssl/bin/openssl version
openssl将被安装到 /usr/local/openssl 。可能提示找不到 libcrypto.so.3 库,需要建立共享库搜寻路径
# 创建共享库搜索路径
echo "/usr/local/openssl/lib64" | sudo tee /etc/ld.so.conf.d/openssl.conf
# 更新动态链接库缓存
ldconfig
openssh升级
openssh 源代码发布网站:https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/
# 查看sshd版本
ssh -V
sshd -V
# 下载最新的ssh源代码
# https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/
# 2025年9月23日
wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-10.0p2.tar.gz
# 解压源代码
tar -xvzf openssh-10.0p2.tar.gz
# 安装GCC编译器
yum install gcc
# 安装zlib依赖
yum install zlib-devel
# 安装pam依赖
yum install pam-devel
# 备份原配置文件
cp -a /etc/ssh/ /etc/ssh_bak/
# 配置与编译(通常情况下升级SSH都会要求升级ssl,请直接看后面部分)
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-md5-passwords --with-pam --with-zlib
此时还可能提示openssl不存在、版本太低,如果本机没有openssl,则参见openssl升级
# 查看openssl版本
openssl version
# 查看openssl位置
which openssl
查看openssl库文件位置
ldd /usr/bin/openssl
# 指定openssh需要使用的openssl库文件位置
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-md5-passwords --with-pam --with-zlib --with-ssl-dir=/usr/local/openssl
# 编译 并 安装
make -j$(nproc)
make install
# 验证ssh版本
ssh -V
sshd -V
# 验证配置文件正确性
/usr/sbin/sshd -t -f /etc/ssh/sshd_config
# 修改配置文件权限,如果验证配置文件正确性语句出错的话
chmod 0600 /etc/ssh/ssh_host_rsa_key
chmod 0600 /etc/ssh/ssh_host_ecdsa_key
chmod 0600 /etc/ssh/ssh_host_ed25519_key
# 修改配置文件,如果验证配置文件正确性语句出错的话
vi /etc/ssh/sshd_config
# 修个sshd_config配置文件,启用以下两个配置
UsePAM yes
PermitRootLogin yes
# 修个sshd_config配置文件,禁用以下四个配置
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials no
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
#GSSAPIEnablek5users no
# 重启sshd服务,
systemctl restart sshd
# 此时可能重启失败,使用journalctl -xe 查看错误信息,一般是提示找不到 libcrypto.so.3 库
# 将库建立软连接到共享库目录
ln -s /usr/local/openssl/lib64/libcrypto.so.3 /usr/lib64/libcrypto.so.3
# 重启sshd服务卡主,修改服务通知类型为simple
vi /usr/lib/systemd/system/sshd.service
# Type=simple
systemctl daemon-reload
# 重启sshd服务
systemctl restart sshd
# 禁用ssh客户端
vi /etc/ssh/ssh_config
升级完成后,旧版的xshell提示,找不到匹配的host key算法 , 这是旧版xhsell的bug,需要升级xshell
直接在服务器使用ssh命令连接到其他机器报错:/etc/ssh/ssh_config line 59: Unsupported option "gssapiauthentication"
# 修改配置文件,是ssh,不是sshd
vi /etc/ssh/ssh_config
# 注释本行
# GSSAPIAuthentication yes
traefik-gateway-dashboard
本文为dashboard用法,基础用法请参考:traefik-gateway
重要bug提醒:截止至2023-06-22,如果动态创建的服务发现,少于1个中间件,则会出错,既需要保证整个项目至少要有一个中间件。可以随便写一个,但是必须要有一个。
三个镜像
2023-06-21:无需再部署dashboard
-
traefik
- swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/traefik:v1.4.20231227
- 内置插件:rewritebody
- 内置插件:rewriteHeaders
更新日志:2023-12-27
- 增加环境变量: "INDEX_ROOT=/root/"
以支持动态创建首页不是从根目录开始。既不再需要单独挂载配置以设置首页不是从根目录开始,注意路径需要 /dir/ 的前后2个反斜杠(/)都需要。因为内部方式为字符串替换,
sed -i 's|/traefik-dashboard/|'${INDEX_ROOT}'traefik-dashboard/|g' /etc/traefik/conf.d/traefik-dashboard.yaml应用程序不会处理任何额外的内容。你也可以继续使用外挂文件自行处理首页不是根目录(不推荐)。- 增加环境变量:'PASSWRD=admin:httpwd'
可以在外部设置用户名和密码,密码使用
htpasswd方式是生成,在线生成网址:http://www.jsons.cn/htpasswd/ 注意,由于设置放置于环境变量,需要将($)特殊符号进行处理,($)使用($$)更新日志:2023-06-26
- 修改内置插件偶尔会失效的问题
- swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/traefik:v1.4.20231227
-
动态服务发现
- swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/traefik-service-discovery:v1.4.20231227
更新日志:2023-12-27
- 增加监控和告警功能
- 修改配置文件值
spring.profiles.active = dev|prod|sqlite-dev|sqlite-prod dev和prod为mysql数据库,sqlite-dev和sqlite-prod为sqlite数据库。由于sqlite数据不支持某些特性,将被弃用。
更新日志:2023-06-28
- 动态服务发现静态资源添加服务监控前端代码
- 修复镜像包运行entrypoint.sh文件不存在问题
- mysql数据脚本调整
更新日志:2023-06-21:
- 打包dashboard面板到一个服务,无需再部署dashboard
- 增加服务监控
-
dashboard面板-
swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/traefik-service-discovery-dashboard-front:stage
更新日志:2023-06-21-
dashboard面板已经集成到动态服务发现,无需部署
-
安装部署说明
- 部署: traefik
# 建议服务名:traefik-gateway
# 端口开放
# 80,http端口
# 443,https端口
# 8080,traefik自带的dashboard端口,可以不开放
- 部署traefik-service-discovery
# 服务名必须为 traefik-service-discovery
# 端口开放
# 8080,非必须
# 环境变量
spring.profiles.active = prod|sqlite-prod
# prod,使用mysql数据库,sqlite-prod,使用sqlite数据库,sqlite不支持服务监控,不推荐使用,在未来的版本中,sqlite数据库将被弃用。
# 如果使用sqlite数据库,需要注意数据库持久化路径,容器目录/data,容器初始化时会自动复制初始化数据库文件。
# 设置mysql数据库配置
app.datasource.host =
app.datasource.name=
app.datasource.password =
app.datasource.username =
- 数据库初始化脚本
数据库初始化脚本下载:https://iovhm.com/book/attachments/14
-
traefik-service-discovery-dashboard-fron
# 2023-06-21,本服务已经集成到traefik-service-discovery,不需要部署
# 端口开放:80
# 访问路径/front/
- 负载均衡配置
| 路径 | 服务 | 说明 | 备注 |
|---|---|---|---|
| / | traefik | 网站根目录 | 必须 |
| /traefik-dashboard/ | traefik | traefik内置的dashboard | 非必须,如果没有配置网站根目录则需要 |
| /traefikconfig-dashboard/ | traefik | traefik动态路由dashboard | 非必须,如果没有配置网站根目录则需要 |
| /traefik-service-discovery/ | traefik | 动态路由api | 非必须,如果没有配置网站根目录则需要 |
首页不是从根目录开始
如果你的首页不是从根目录开始,则需要做一些单独的配置,实现原理为,将dashboard需要用到的3个路径处理为所需要的目录。
复制下面的配置文件,挂载到traefik-gatewy的容器路径
# 使用k8s集群时,建议使用配置映射卷
./index-not-root.yaml:/etc/traefik/conf.d/conf2/index-not-root.yaml
需要在负载均衡加入配置
| 路径 | 服务 | 说明 | 备注 |
|---|---|---|---|
| /minio/ | traefik | 业务路由入口 | 必须 |
| /_minio/ | traefik | 面板路由入口 | 如果配置了下面三个路径,则是非必须 |
| /_minio/traefik-dashboard/ | traefik | traefik内置的dashboard | 非必须,如果没有配置面板入口则需要 |
| /_minio/traefikconfig-dashboard/ | traefik | 动态路由dashboard | 非必须,如果没有配置面板入口则需要 |
| /_minio/traefik-service-discovery/ | traefik | 动态路由api | 非必须,如果没有配置面板入口则需要 |
http:
routers:
# 首页不是根目录
index-not-root:
# 注意路由匹配前缀,填写你的实际路径,为了保证一致性,建议本服务使用 _ 开头
# 注意中间件中的路径前缀,应该和路由路径一致,
# 注意重写header的前缀,应该和路由路径一致。
# 根据本路由规则,可以看出是将服务又重定向到traefik-gateway,即多了一层不必要的代理,会带来性能损失。
# 由此,这个额外的路由,只应该用于处理traefik控制面板
# 真正的服务不应该由此处理,而应该直接指向到traefik-gateway
# 既你需要配置面板路由路径,应该与你的服务路径,使用不通的名称
# 本次使用的是 _minio ,请将本文的_minio 替换为你需要的
# rule: "PathPrefix(`/_minio/`)"
# 2023-06-22,:只处理dashboard相关的3个路径,以开启面板,业务路由在面板进行配置,以解决多了一层代理问题
rule: "PathPrefix(`/_minio/traefik-dashboard/`) || PathPrefix(`/_minio/traefikconfig-dashboard/`) || PathPrefix(`/_minio/traefik-service-discovery/`)"
service: index-not-root
middlewares:
- stripPrefix-index-not-root
- rewritebody-index-not-root
- rewriteHeaders-index-not-root
services:
index-not-root:
loadBalancer:
servers:
- url: http://traefik-gateway:80 # 部署的traefik主程序必须叫这个名字或者修改为实际部署的名字
middlewares:
# 去除前缀
stripPrefix-index-not-root:
stripPrefix:
prefixes:
- "/_minio/"
# 正文路径改写
rewritebody-index-not-root:
plugin:
plugin-rewritebody: # 要和静态配置文件的插件名称对应上
rewrites:
- regex: "/traefik-service-discovery/api/"
replacement: "/_minio/traefik-service-discovery/api/"
- regex: "/traefik-dashboard/api"
replacement: "/_minio/traefik-dashboard/api"
- regex: "/_minio/dashboard/"
replacement: "/_minio/traefik-dashboard/dashboard/"
# Header重定向改写
rewriteHeaders-index-not-root:
plugin:
plugin-rewriteHeaders: # 要和静态配置文件的插件名称对应上
rewrites:
- header: "Location"
regex: "/_minio/dashboard/"
replacement: "/_minio/traefik-dashboard/dashboard/"
插件使用
middlewares:
# 删除路径前前缀,traefik默认会把整个URL路径全部传过来,会导致与后端路径不一致,需要进行路径删除处理
stripPrefix-traefikconfig-dashboard:
stripPrefix:
prefixes:
- "/traefikconfig-dashboard/"
# 增加路径前缀,对应与后端服务,即使在填写URL路径,traefik也不会处理后端路径,会导致与后端路径不一致,需要增加实际路径
addPrefixFront-traefikconfig-dashboard:
addPrefix:
prefix: "/front/"
# 正文路径改写
rewritebody-traefikconfig-dashboard:
plugin:
# 要和静态配置文件的插件名称对应上
plugin-rewritebody:
rewrites:
- regex: "/api/"
replacement: "/traefik-service-discovery/api/"
# 基础身份验证
basicAuth-traefikconfig-dashboard:
basicAuth:
removeHeader: true
users:
- "admin:" # 在线生成 http://www.jsons.cn/htpasswd/
# 发送到后端时修改header,例如后端限制了域名
headers-peisong:
headers:
customRequestHeaders:
host: "peisong.gxzszs.cn"
# 重写 header,对响应的head进行处理,例如写死的重定向
rewriteHeaders-peisong:
plugin:
plugin-rewriteHeaders: # 要和静态配置文件的插件名称对应上,本镜像仅能使用这个名字
rewrites:
- header: "Location"
regex: "peisong.gxzszs.cn"
replacement: "peisong.gxfusui.com"
- header: "Location"
regex: "http://peisong.gxfusui.com/plugin.php"
replacement: "/plugin.php"
在plugin-rewritebody使用正则表达式需要注意的事项
如下面的代码所示,一个使用了双反斜杠,一个使用的是单反斜杠,实际上取决于最外围的引号,这是json决定的,而不是这里的字符串的问题,需要注意。
需要转义的字符有:
-
\转义为\\ -
"转义为\" -
()转义为\\(\\)
middlewares:
rewritebody-iams-pis:
plugin:
plugin-rewritebody:
rewrites:
- regex: '\bpis.guizhougas.cn\b'
replacement: 'iams-pis.shulianhuiyun.com'
rewritebody-iams-iot:
plugin:
plugin-rewritebody:
rewrites:
- regex: "\\biot.shulianhuiyun.com\\b"
replacement: "iams-iot.shulianhuiyun.com"
制作离线yum源
在有网的机器制作离线仓库包
# 1. 准备目录
mkdir -p /data
# 2. 装工具(一次就够)
yum install -y yum-utils createrepo
# 3. 下载 gcc 及所有递归依赖
yumdownloader --resolve --destdir=/data/offline-repo gcc zlib-devel pam-devel perl-IPC-Cmd createrepo
# 连同依赖一起下载
repotrack -a x86_64 -p /data/offline-repo/ gcc zlib-devel pam-devel perl-IPC-Cmd createrepo
# 4. 生成仓库元数据(repodata)
createrepo /data/offline-repo
# 5. 打包(rpm + repodata 一起带走)
cd /data
tar -cvzf offline-repo.tar.gz ./offline-repo
在没网的机器制作本地仓库
# 6. 上传并解压到固定目录
mkdir -p /data
tar -xvzf offline-repo.tar.gz -C /data/offline-repo
# 7. 写本地仓库文件
tee /etc/yum.repos.d/offline.repo <<'EOF'
[offline]
name=Local Offline Repo
baseurl=file:///data/offline-repo
enabled=1
gpgcheck=0
EOF
# 8. 清缓存 & 识别新仓库
yum clean all
yum makecache
# 9. 升级gcc(非必须)
yum install -y gcc
# 10. 安装其他组件
yum install -y zlib-devel pam-devel perl-IPC-Cmd perl perl-Time-Piece
centos7 阿里源
CentOS-Base.repo
# 保存位置
# /etc/yum.repos.d/CentOS-Base.repo
# CentOS-Base
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
# 替换阿里ecs内网环境
sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
# epel.repo
curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo
# docker源
curl -o /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
BookStack
bookstack是一个简单、易用的文章发布软件
- 使用PHP开发、开源
- 可以搜索
- 容器化部署
- 多语言
- markdown编辑器支持
bookstack:
image: linuxserver/bookstack:23.05.2
environment:
- APP_URL=http://qq829.cn/book
- APP_LANG=zh_CN
- DB_HOST=
- DB_PORT=
- DB_USER=
- DB_PASS=
- DB_DATABASE=
volumes:
- ./bookstack-data:/config
privileged: true
networks:
- iovhm-net
手工编译qt6.7.1 WebEngine
QWebEngine默认不支持html video播放
QWebEngine默认不支持html video播放,原因是默认编译没有包括视频解码部分
源代码编译
# 下载源代码并解压缩
# 下载地址:https://download.qt.io/official_releases/qt/6.7/6.7.1/single/qt-everywhere-src-6.7.1.zip
# 安装vistudi 2019或者更高
# 安装qt creatro
# 下载几个需要依赖的文件,在qt5安装文件有,将这个路径加入到path
# D:\Qt\Qt5.14.2\5.14.2\Src\gnuwin32\bin
# 或者使用我从qt5目录复制出来的
# https://iovhm.com/book/attachments/7
# 找到 qtcreatro 安装后自带的 Qt 6.7.1 (MSVC 2019 64-bit) 快捷方式,或者使用如下命令命令行启动命令窗口
C:\Windows\System32\cmd.exe /A /Q /K D:\Qt\Qt6.7.1\6.7.1\msvc2019_64\bin\qtenv2.bat
# 加载vc编译器,具体需要看你的visual studio位置
"C:\Program Files\Microsoft Visual Studio\2022\community\VC\Auxiliary\Build\vcvarsall.bat" amd64
# 设置环境变量
Set PATH=D:\Qt\Qt6.7.1\Tools\mingw1120_64\bin;%PATH%
# 配置编译选项
configure.bat -prefix d:/qt6.7.1 -debug-and-release -webengine-proprietary-codecs -opensource -confirm-license -platform win32-msvc -nomake examples -nomake tests
# 编译
cmake --build . --parallel
# 安装
cmake --install .
ansible 一键修改服务器密码脚本
脚本安装
- 执行安装脚本setup.sh 安装ansible和yq命令
#!/bin/bash
# 安装ansible
yum install ansible-2.9.27-1.el7.noarch.rpm -y
# 安装yq
cp -a yq /usr/local/bin/
注: yq是解析yaml工具
- 配置inithost,第一次连接服务器的用户名和密码 例:
hosts:
#192.168.40.36:
# ansible_user: root
# ansible_password: 123456
#192.168.40.37:
# ansible_user: root
# ansible_password: 123456
- 配置ansible hosts文件 例:
#[test]
#192.168.40.36
#
#[test1]
#192.168.40.37
4.生成初始连接凭据
bash ./inithost.sh
注:初始文件生成后,可以删除inithost以保证安全
常见操作
- 修改单个服务器密码
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=192.168.40.36 user=root password=123456" changepasswd.yaml
- 修改ansible组密码
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=test user=root password=123456" changepasswd.yaml
- 修改hosts文件里所有服务器的密码
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=all user=root password=123456" changepasswd.yaml
- 修改其他用户密码
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=192.168.40.36 user=test password=123456" changepasswd.yaml
- 提示: 在执行上述操作前,需要确保服务器上已存在对应的用户。
常见的问题
如果遇到以下错误信息:"Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host."
解决方法:先使用ssh连接目标主机,获取主机的key。
附录1: inithost.sh脚本
#!/bin/bash
# 获取从命令行传入的第一个参数作为vault_password
vault_password="3d$R7#X9"
# 创建connect目录,如果它还不存在
mkdir -p connect
# 读取主机名列表
hosts=$(yq e '.hosts | keys' inithost.yaml | sed 's/- //')
# 遍历每个主机名
for host in $hosts; do
# 检查主机名是否为空
if [ -z "$host" ]; then
continue
fi
# 删除引号,以便获取干净的主机名
host=${host//\"/}
# 从yaml文件中读取ansible_user和ansible_password
user=$(yq e .hosts.\"$host\".ansible_user inithost.yaml)
password=$(yq e .hosts.\"$host\".ansible_password inithost.yaml)
# 检查用户名和密码是否为空
if [ -z "$user" ] || [ -z "$password" ]; then
continue
fi
# 删除引号,以便获取干净的用户名和密码
user=${user//\"/}
password=${password//\"/}
# 创建新的yaml文件,文件名为IP地址,并写入ansible_user和ansible_password
# 新文件将在connect目录下创建
echo "ansible_user: $user" > connect/$host.yaml
echo "ansible_password: $password" >> connect/$host.yaml
# 创建秘密文件
echo "$vault_password" > .vault_pass
# 使用ansible vault对文件进行加密,使用传入的vault_password作为密码
echo $vault_password | ansible-vault encrypt connect/$host.yaml --vault-password-file=.vault_pass
# rm -fr .vault_pass
done
附录2:changepasswd.yaml的playbook
- hosts: "{{ target_hosts }}"
vars_files:
- "connect/{{ inventory_hostname }}.yaml"
tasks:
- name: debug
debug:
msg: "connect/{{ inventory_hostname }}.yaml"
- name: 修改服务器密码
user:
name: "{{ user }}"
password: "{{ password | password_hash('sha512') }}"
become: yes
become_user: "{{ ansible_user }}"
- name: 修改ansible连接root密码文件
local_action:
module: script
args: ansible_rootpasswd_change.sh connect/{{ inventory_hostname }}.yaml {{ password }}
when: user == 'root'
附录3 修改root密码的脚本
#!/bin/bash
# 定义你的 vault 密码
#vault_password=$1
# 定义 inventory_hostname
vault_file=$1
# 定义新的 ansible_password
new_ansible_password=$2
# 定义 vault 文件路径
#vault_file="${vault_file_path}"
# 创建一个临时文件
temp_file=$(mktemp)
# 使用 ansible-vault 解密 vault 文件并将内容写入临时文件
#echo $vault_password | ansible-vault decrypt $vault_file --output $temp_file
ansible-vault decrypt $vault_file --output $temp_file --vault-password-file=.vault_pass
# 修改 ansible_password
yq eval ".ansible_password = \"$new_ansible_password\"" $temp_file
# 使用 ansible-vault 重新加密临时文件并覆盖原来的 vault 文件
#echo $vault_password | ansible-vault encrypt $temp_file --output $vault_file
ansible-vault encrypt $temp_file --output $vault_file --vault-password-file=.vault_pass
# 删除临时文件
rm $temp_file
qt集成cef(Chromium Embedded Framework) 实现自己的浏览器
虽然QT自带的有WebEngine也是基于chrome的。简单使用没有问题,但是默认没有对视频播放的支持,虽然网上有很多关于编译QT源码以用于支持视频播放。但是耗时耗力,且不一定能成功,想要追随chrome内核更新相当麻烦
几经周折,发现有一款基于cef的包装也可以支持视频播放,在此记录一下
参考网址
- https://github.com/cefview/qcefview
- https://github.com/wodeguaiguai/cef-build-surport-for-mp3-mp4
从源代码编译
# 设置环境变量,指定QT套件目录
set QTDIR=D:\Qt\Qt6.7.12\6.7.1\msvc2019_64
# 克隆源代码
git clone --recursive -b v1.1.0 https://github.com/CefView/QCefView.git
# 加载VC编译器
"C:\Program Files\Microsoft Visual Studio\2022\community\VC\Auxiliary\Build\vcvarsall.bat" amd64
# 生成项目
generate-win-x86_64.bat
# 编译
cmake --build .build/windows.x86_64
# 替换热心网友修改过的支持视频播放的cef
# 替换到 QCefView\CefViewCore\dep 这里
# 重新执行
generate-win-x86_64.bat
cmake --build .build/windows.x86_64
在看看,播放器就支持视频播放了
在qt creator调试的时候的注意事项
需要把所有的依赖的dll拷贝到输出目录,使用环境变量设置依赖文件跑不起来
docker centos 支持中文
FROM centos:7
EXPOSE 8080
RUN yum install -y https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm && \
yum install -y nscd kde-l10n-Chinese && \
yum reinstall -y glibc-common && \
yum clean all
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
ENV LC_ALL zh_CN.utf8
ENV LANG zh_CN.utf8
ENV LANGUAGE zh_CN.utf8
COPY ./ffmpeg/linux/** /usr/local/bin/
COPY ./entrypoint.sh /root/entrypoint.sh
COPY ./target/srs-push-1.0-SNAPSHOT.jar /root/srs-push-1.0-SNAPSHOT.jar
WORKDIR /root
ENTRYPOINT ["/root/entrypoint.sh"]
busybox&&docker镜像支持systemd
k8s环境中,经常需要一个工具容器进行一些调试,特意打包了常用小工具。你可以可以使用下面的dockerfile制作自己的容器
镜像地址
harbor.iovhm.com/public/busybox:latest
Dockerfile
FROM harbor.iovhm.com/hub/centos:7
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \
sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo && \
curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo && \
yum makecache && \
yum install -y which wget openvpn telnet net-tools mariadb openssh-clients && \
yum clean all && \
curl -o /usr/bin/systemctl https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl.py && \
chmod +x /usr/bin/systemctl && \
true
COPY ./entrypoint.sh /root/entrypoint.sh
RUN chmod +x /root/entrypoint.sh
CMD [ "/root/entrypoint.sh" ]
WORKDIR /root
entrypoint.sh
#!/bin/bash
set -x
echo "busybox ..."
# 判断环境变量 OPENVPN 是否存在并且值为 1,决定是否开启 OpenVPN 客户端
if [ "${OPENVPN}" == "1" ]; then
echo "start openvpn-client ..."
/usr/sbin/openvpn --config /data/vpclub/ovpn_configuration_file.ovpn &
# 等待 OpenVPN 启动
sleep 10
else
echo "OPENVPN environment variable not set or not equal to 1. Skipping OpenVPN client setup."
fi
# 判断环境变量 IP_FORWARD 是否存在且值为 1,决定是否开启 IP 转发
if [ "${IP_FORWARD}" == "1" ]; then
echo "Enabling IP forwarding..."
echo 1 >/proc/sys/net/ipv4/ip_forward
echo "start ip_forward ..."
# 从文件读取 IP_FORWARD_RULES
if [ -f "/data/vpclub/IP_FORWARD_RULES.txt" ]; then
IP_FORWARD_RULES=$(cat /data/vpclub/IP_FORWARD_RULES.txt)
else
echo "IP_FORWARD_RULES file not found. Skipping batch IP forwarding setup."
continue
fi
# 批量设置 IP 转发规则
if [ -n "${IP_FORWARD_RULES}" ]; then
IFS=$'\n' read -r -d '' -a rules <<<"${IP_FORWARD_RULES}"
for rule in "${rules[@]}"; do
# 修改解析逻辑,支持新的格式
IFS=',' read -r SRC_IP_PORT DEST_IP_PORT DEST_PROTO <<<"$rule"
IFS=':' read -r SRC_IP SRC_PORT <<<"$SRC_IP_PORT"
IFS=':' read -r DEST_IP DEST_PORT <<<"$DEST_IP_PORT"
DEST_PROTO=${DEST_PROTO:-tcp} # 默认协议为tcp
# 添加PREROUTING规则
iptables -t nat -I PREROUTING ! -s ${DEST_IP}/32 -p ${DEST_PROTO} --dport ${SRC_PORT} -j DNAT --to ${DEST_IP}:${DEST_PORT}
# 添加POSTROUTING规则
iptables -t nat -I POSTROUTING -d ${DEST_IP}/32 -p ${DEST_PROTO} -j MASQUERADE
done
else
echo "No IP_FORWARD_RULES provided. Skipping batch IP forwarding setup."
fi
else
echo "IP_FORWARD environment variable not set or not equal to 1. Skipping IP forwarding setup."
fi
echo "success ..."
tail -f /dev/null
隐藏秘籍,内置了openvpn-client
注意事项,如果开启内置的openvpn-client,如果服务器设置的路由与本地网络冲突,需要再配置文件中增加 route-nopull
例如,本机网络为192.168.0.0/24,VPN上也进行了这个设置,则会导致路由冲突,可能导致vpn客户不可用
增加不从服务器获取路由route-nopull
2025年5月16日,已经在命令增加了 route-nopull ,不需要修改配置文件了
version: "3"
services:
busybox:
image: harbor.iovhm.com/public/busybox:latest
container_name: busybox
network_mode: host # 注意一定要是host模式,因为他默认监听在容器内,并不能与主机直接通讯
privileged: true
restart: always
volumes:
- ./ovpn_configuration_file.ovpn:/data/vpclub/ovpn_configuration_file.ovpn
command: "/usr/sbin/openvpn --config /data/vpclub/ovpn_configuration_file.ovpn --route-nopull"
链接到集群内
增加两个环境变量
- 开启内置的VPN: OPENVPN=1
- 开启端口转发:IP_FORWARD=1
编写configmap
重要:两个配置的名称要对应上,因为启动脚本里面写死了
- 端口转发规则 IP_FORWARD_RULES.txt
格式为:SRC_IP:SRC_PORT,DST_IP:DST_PORT , 一行一个
0.0.0.0:6388,10.100.156.69:6379
0.0.0.0:5672,10.103.194.230:5672
0.0.0.0:15672,10.103.194.230:15672
其中的IP为服务发现的IP
- VPN客户端秘钥文件:ovpn_configuration_file.ovpn
可以到pritunl中下载后用记事本打开后粘贴进去
对configmap进行挂载,挂载路径为/data/vpclub
datart通过自定义视图查询数据源数据
一、 功能概述
该功能可以实现动态注册数据源,自定义SQL就可以通过统一接口查询数据。减少开发工作量,提高效率。
二、注册数据源
注意:连接地址,一定要设置utf8编码,不然中文查询不出来,示例: jdbc:mysql://121.37.7.167:33314/数据库名?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
三、添加数据视图
数据视图-新建数据视图-SQL 视图,进入编写要查询数据库的SQL
1.先选择数据源
2.编写查询SQL
3.执行SQL
4.保存视图
四、调用接口查询数据
1.获取TOKEN
(1)接口地址:[POST] https://{url}/api/v1/users/login
(2)Body 请求参数:
{
"username": "wangbo", //用户名
"password": "admin@2023!" //密码
}
2.获取视图数据
(1)接口地址: [POST] https://{url}/api/v1/data-provider/view
(2)Header 请求参数
| 参数名 | 参数值 | 是否必填 | 参数类型 | 参数说明 |
|---|---|---|---|---|
| Authorization | Bearer eyJhbGciOiJIUzI1xxxxx | 是 | string | token |
(3)Body 请求参数
{
"page": 1,//当前页
"limit": 20,//每页条数
"viewId": "a961d8dc8af4496aad3dfe71d9668416",//视图id
"filters": [ //查询条件,接口执行时会拼在sql语句的where部分
{
"column": [ //字段名字
"create_date"
],
"sqlOperator": "GT", //运算符(EQ:等于,NE:不等于,GTE:大于等于,LTE:小于等于,LT:小于,GT:大于)
"values": [
{
"value": "2023-06-08", //参数值
"valueType": "DATE" //参数类型
}
]
}
]
}
(4)响应示例
{
"data": { //返回数据
"list": [ 返回数据列表,字段由查询数据决定
{
"request_time": 154,
"ip": "127.0.0.1",
"id": 1667084925162176513,
"request_method": "POST",
"create_date": "2023-06-09 16:23:14",
"operation": "保存",
"request_uri": "/renren-admin/kong/kongapp/save",
"status": 0
}
],
"page": 1, //当前页
"limit": 20, //每页面条数
"total": 56 //总条数
},
"errCode": 0,
"message": null,
"success": true //是否成功
}
五、自定义视图变量
(2)变量使用,变量格式:$变量名称$,使用如下:
select
*
from sys_log_operation where create_date between $START_DATE$ and $END_DATE$
(3)接口调用时添加参数
{
"page": 1,//当前页
"limit": 20,//每页条数
"viewId": "a961d8dc8af4496aad3dfe71d9668416",//视图id
"params": { //视图自定义变量
"START_DATE": [ //变量名称
"2023-06-12" //变量值
],
"END_DATE": [
"2023-06-13"
]
}
}
定时备份mysql
相关容器
swr.cn-south-1.myhuaweicloud.com/vp-public/mysql-backup:v1.0
相关配置
volumes:
- ./config:/config # 需要备份的数据库链接信息
- ./data:/data #备份文件保存目录
备份文件配置信息
# 5-11.yaml
- host: 192.168.5.11
user:
password:
name: mysql # 没有实际意义,用来区分备份文件名称,按时间生成{name}-20240713-141847-732950.tar.gz 这样的文件
port: 3306
- host: 192.168.5.12
user:
password:
name: mysql2 # 没有实际意义,用来区分备份文件名称,按时间生成{name}-20240713-141847-732950.tar.gz 这样的文件
port: 3306
直接运行
#!/bin/bash
docker run -it \
-v $(pwd)/config:/config -v $(pwd)/data:/data \
--privileged=true \
swr.cn-south-1.myhuaweicloud.com/vp-public/mysql-backup:v1.0
在k8s创建定时容器
- 1 创建服务
- 2 挂载数据
MYSQL维护指南
1. 监控数据库性能
1.1. 主机监控
定期监控和分析数据库的性能指标,包括查询速度、连接数、CPU和内存使用情况等。这有助于识别性能瓶颈,并采取相应的优化措施。
常用命令:top 、 free -h 、 df -h 检查mysql主机的性能,可用资源应该冗余30%以上。
1.2. 状态监控
-- 这条命令用于查询当前有多少客户端线程连接到数据库。
-- 这个数字可以帮助你了解数据库的负载情况,如果连接数异常高,可能意味着你的数据库正面临过高的请求负载或者连接泄露问题
SHOW GLOBAL STATUS LIKE 'Threads_connected';
-- 这条命令用于显示自数据库启动以来执行的查询总数。
-- 这个指标可以帮助你评估数据库的工作量,了解数据库的使用频率
SHOW GLOBAL STATUS LIKE 'Queries';
-- 这条命令查询排序缓冲区的大小。
-- 排序缓冲区是MySQL用于排序操作的内存区域。调整这个值可以影响排序操作的性能,特别是在处理大量数据时
SHOW VARIABLES LIKE 'sort_buffer_size';
-- 这条命令用于查询InnoDB缓冲池的大小。InnoDB缓冲池是存储InnoDB表数据和索引的内存区域。
-- 这个值的大小直接影响到数据库的性能,特别是在处理大型数据库时
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 这条命令显示当前MySQL服务器上所有运行的进程信息。
-- 这可以帮助你识别当前正在执行的查询,检查是否有长时间运行的查询或者锁竞争情况
SHOW FULL PROCESSLIST;
1.3. 利用MySQL的性能模式(Performance Schema)
-- 检查performance_schema这个系统变量的状态,如果performance_schema=ON,说明performance_schema已经启用
SHOW VARIABLES LIKE 'performance_schema';
-- 如果没有启用performance_schema,可以通过修改my.cnf文件来启用performance_schema,添加如下配置
-- [mysqld]
-- performance_schema=ON
-- 所有已执行SQL语句的性能摘要,返回了执行次数最多的前20个查询模板,这有助于识别出数据库中最频繁执行的查询,这些查询往往是性能优化的重点目标
use performance_schema;
SELECT
SCHEMA_NAME,
DIGEST_TEXT,
COUNT_STAR as `执行次数`,
SUM_TIMER_WAIT / COUNT_STAR / 1e12 AS `平均执行时间(秒)`,
MAX_TIMER_WAIT / 1e12 as `最大执行时间(秒)`,
SUM_LOCK_TIME / COUNT_STAR / 1e12 AS `平均锁定时间(秒)`,
SUM_ROWS_SENT / COUNT_STAR AS `平均返回的行数`,
SUM_ROWS_EXAMINED / COUNT_STAR AS `平均扫描的行数`,
SUM_ROWS_AFFECTED / COUNT_STAR AS `平均影响的行数`,
SUM_CREATED_TMP_DISK_TABLES / COUNT_STAR AS `平均创建的临时磁盘表`,
SUM_SORT_MERGE_PASSES / COUNT_STAR AS `序操作的效率`,
SUM_NO_INDEX_USED / COUNT_STAR AS `没有使用索引的次数`,
SUM_NO_GOOD_INDEX_USED / COUNT_STAR AS `没有更好的使用索引的次数`
FROM
events_statements_summary_by_digest
WHERE
SCHEMA_NAME NOT IN (
'mysql',
'performance_schema',
'information_schema'
)
ORDER BY
`平均执行时间(秒)` DESC
LIMIT
20;
1.4. 分析慢日志
分析查询日志和慢查询日志,找出执行时间长、效率低下的查询语句,并进行优化。
# 开启慢查询日志
slow_query_log=1
# 慢查询日志文件路径
# slow_query_log_file=/var/log/mysql/mysql-slow.log
# 如果未设置,可以使用 SHOW VARIABLES LIKE 'slow_query_log_file'查看
# 记录查询执行时间超过30秒的查询
long_query_time=10
-- 查询是否开启慢日志
SHOW VARIABLES LIKE 'slow_query_log';
-- 临时开启慢日志,重启后失效
SET GLOBAL slow_query_log = 'ON';
-- 查看慢查询阈值
SHOW VARIABLES LIKE 'long_query_time';
-- 设置超过阈值的查询时间为慢查询
SET GLOBAL long_query_time = 10;
-- 慢日志存储路径
SHOW VARIABLES LIKE 'slow_query_log_file';
-- 查看慢查询日志
SELECT
*
FROM
mysql.slow_log
ORDER BY
start_time DESC
LIMIT
20;
2. 备份数据库
根据数据库的重要性和数据变动频率,制定合理的备份策略。通常建议每天或每周进行一次全备份,并结合增量备份以减少存储空间和时间消耗。
2.1. 备份方式
可以选择逻辑备份(如使用mysqldump工具)或物理备份(如直接复制数据文件)。逻辑备份生成SQL脚本文件,便于在不同版本的MySQL之间迁移;物理备份则速度更快,恢复时更接近原始状态。
-- 备份mysql数据库
mysqldump - u root - p --all-databases > all_databases.sql
-- 恢复mysql数据库
mysql - u root - p < all_databases.sql
2.2. 验证备份
定期验证备份数据的完整性和可恢复性,确保在需要时能够成功恢复数据。
3. 优化查询语句和表结构
3.1. 优化查询语句
使用合适的索引、避免全表扫描、减少不必要的JOIN操作等,以提高查询效率。
3.2. 优化表结构
定期检查并优化表结构,删除不必要的字段和表,合并相似的表以减少冗余数据。对于大型表,可以使用OPTIMIZE TABLE命令进行优化,以减少磁盘空间使用和提高查询性能。
3.3. 归档历史数据
将超过一定历史时限的数据库进行归档,并从实时数据库进行删除,以降低数据量提高数据库性能。
4. 更新和维护
4.1. 更新数据库软件:
定期更新MySQL服务器和相关组件的软件版本,以修复已知的漏洞、提高性能和增加新功能。
4.2. 安装安全补丁:
及时安装MySQL的安全补丁,以防止黑客攻击和数据泄露。
5. 安全管理
5.1. 设置强密码与密码密码过期策略
为用户和数据库设置复杂且不易猜测的密码,并定期更换密码。设置密码过期策略
-- 全局的密码生命周期
SET GLOBAL default_password_lifetime = 180;
-- 设置密码过期时间
ALTER USER 'your_username' @'localhost' PASSWORD EXPIRE INTERVAL 180 DAY;
5.2. 最小权限原则:
-
遵循最小权限原则:为用户和应用程序分配必要的最小权限集合,以减少安全风险。
-
定期审计:定期审计数据库的访问日志和权限分配情况,及时发现并处理潜在的安全问题。
-
不允许使用ROOT直接:不允许使用ROOT用户直连数据库,应该为每个应用程序单独设置连接用户。
6. 其他维护任务
6.1. 检查磁盘空间
确保数据库服务器有足够的磁盘空间来存储数据和日志文件。
6.2. 清理无用数据
定期清理无用的数据、临时表和日志文件等,以释放磁盘空间和提高性能。
6.3. 配置和优化MySQL参数
根据监控结果和实际需求,调整MySQL的配置参数,如缓冲区大小、连接数等,以优化数据库性能。
mysql主从复制&&读写分离
主服务器配置
修改配置文件,编辑MySQL的配置文件(通常是/etc/my.cnf或/etc/mysql/my.cnf),重启MySQL服务,使配置生效。
# 启用二进制日志
log-bin=master-binlog
# 二进制日志格式
binlog-format=row
#设置服务器ID,确保每个服务器的ID唯一。
server-id=1
# 数据库名(可选):
# binlog-do-db=指定需要复制的数据库。
# 设置中继日志
relay-log=relay-bin
创建复制用户
创建复制用户:在主服务器上创建一个复制用户,并授权复制权限
CREATE USER 'replicator'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;
从服务器配置
修改配置文件,编辑从服务器的MySQL配置文件,设置完成后重启MySQL服务
# 确保服务器ID与主服务器不同
# 其他相关配置可根据需要设置
server-id=2
# 启用二进制日志
log-bin=master-binlog
# 二进制日志格式
binlog-format=row
# 设置中继日志
relay-log=relay-bin
# 设置从库为指读模式
# read-only=1
查看主服务二进制日志状态
在主服务器上执行以下命令,记录二进制日志文件名和位置,这些信息将用于从服务器的配置
SHOW MASTER STATUS;
配置从服务器的主复制配置信息
在从服务器上将执行,将配置复制指向主服务器
CHANGE MASTER TO
MASTER_HOST='10.64.100.91',
MASTER_USER='replicator',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='binlog.000009',
MASTER_LOG_POS=154;
# 启动复制
START SLAVE;
# 验证复制状态:
SHOW SLAVE STATUS\G;
# 主要是2个
Slave_I0 Running,YES
Slave SqL Rurning,YES
不重新打包修改nginx-web镜像写死的API地址
由于各种原因前端的API地址被写死了,而临时需要修改地址,打包发布麻烦,不知道哪个版本的代码对应这个版本,可以这样做。
nginx.conf中写死的服务发现
- 方法一,使用主机别名,类似hosts功能
- 方法二,使用下面的字符串替换
页面中写死的API地址
- 编写一段API替换脚本和nginx启动脚本
注意第一句的#!/bin/bash , 根据镜像不同,可能是#!/bin/sh
#!/bin/bash
# 根据基础镜像的不同,可能是/bin/sh,而且这句要是第一句
# 如果环境变量存在则用环境变了,如果环境变量不存在,则使用默认值,注意前面的减号语法(-)
OLD_URL=${OLD_URL:-"https://park.wvpark.com:802/"}
NEW_URL=${NEW_URL:-"http://weipin.hmncloud.top/"}
# 文件名称很确定,直接替换
sed -i "s|${OLD_URL}|${NEW_URL}|g" /usr/share/nginx/html/assets/iframe-view-4c8b16ca.js
# 不管什么文件,直接替换
# grep -rl "ak=${OLD_AK}" /usr/share/nginx/html | xargs sed -i "s|ak=${OLD_AK}|ak=${NEW_AK}|g"
# 文件名称相对确定,查找替换
# SEARCH_DIR="/usr/share/nginx/html/assets"
# FILE_PREFIX="iframe-view*.js"
# for FILE in $(find "$SEARCH_DIR" -type f -name "${FILE_PREFIX}"); do
# # 使用 sed 进行替换操作,-i 选项表示直接编辑文件
# sed -i "s|${OLD_URL}|${NEW_URL}|g" "$FILE"
# # 打印已处理的文件名
# echo "Processed file: $FILE"
# done
# 启动nginx
nginx -g 'daemon off;'
- 将配置文件放入configmap并挂载或则直接挂载进容器,并设置挂载文件为777权限
docker和docker-compose挂载为777 请自行百度
- 将容器的启动命令设置为自己的启动脚本
pritunl-vpn
pritunl-vpn 是开源软件open vpn的一个实现,适合企业级使用
参考网址
- git 地址:https://github.com/pritunl/pritunl
- docker版:https://github.com/jippi/docker-pritunl
- docker hub:https://hub.docker.com/r/jippi/pritunl
使用docker-compose安装部署
version: "3"
services:
pritunl-vnp:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/pritunl:latest
container_name: pritunl-vpn
privileged: true
restart: always # 自动重启
ports:
- 33312:80
- 33313:443
- 33314:33314/tcp
- 33314:33314/udp
volumes:
- ./pritunl:/var/lib/pritunl
- ./mongodb:/var/lib/mongodb
# 80端口,http访问,事实上他会自己重定向到https
# 443端口,https访问
# 33314端口,既你在新增服务时设置的端口,需要开启防火墙
配置
- 登录
默认用户名密码: pritunl / pritunl
- 增加组织
- 增加用户
- 增加服务
-
附加到组织
-
启动服务
-
路由配置
增加服务时候,会自动增加一条路由0.0.0.0/0,该路由会接管所有的流量。应该将改路由删除,增加需要通过 vpn client访问的内网主机路由。例如要访问内网的192.168.0.0/24
- windows客户端
下载地址:https://client.pritunl.com/#install
- 获取用户配置文件
- 导入配置文件
- linux客户端
yum install openvpn
# 将下载的配置文件解压,既可得到ovpn配置文件
openvpn --config your.ovpn
# vi /etc/systemd/system/openvpn-client.service
# 编写为服务
[Unit]
Description=OpenVPN Client Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/openvpn --config /data/vpclub/openvpn/your_ovpn_configuration_file.ovpn
Restart=on-failure
[Install]
WantedBy=multi-user.target
pritunl仅能点对点访问,既,可以通过vpn client 访问远端网络,但是不能通过远端网络访问本地地址。
- 访问方式一,远端用户可以访问服务器所在的局域网
- 访问方式二
在无GUI界面的Linux服务器上执行jmeter
准备工作
首先在有GUI的电脑,例如windows上将测试计划参数配置好,先使用一个并发运行一次,保证测试计划可以成功运行,将测试计划保存为jmx
在服务器上运行
jmeter是使用java编写的,需要安装jdk,将apache-jmeter-5.6.3.zip 复制到服务器并解压缩,将前面保存的jmx也上传到服务器,进入到 /apache-jmeter-5.6.3/bin 目录
# cd /apache-jmeter-5.6.3/bin
./jmeter -n -f -t /data/test.jmx -l /data/test.jtl -e -o /data/report
# -n 无GUI界面
# -t /data/test.jmx 测试计划文件
# -l /data/test.jtl 执行结果路径
# -e 测试完成后生成报告
# -o 测试报告生成路径
# -f 强制删除现有结果,默认情况是追加到已有结果,可能造成数据干扰
然后把测试报告打包下载回来查看,
tar -cvf report.tar ./report/
使用docker-compose运行,不推荐,但是可以快速验证,内置一个nginx,可以使用浏览器查看测试报告
version: "3"
services:
jmeter:
image: harbor.iovhm.com/hub/openjdk:17
container_name: jmeter
privileged: true
volumes:
- ./apache-jmeter-5.6.3:/apache-jmeter-5.6.3
- ./data:/data
command: "tail -f /dev/null"
jmeter-nginx:
image: harbor.iovhm.com/hub/nginx:1.21.3
container_name: jmeter-nginx
ports:
- "5500:80"
volumes:
- ./data:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
# 进入到容器
# docker exec -it jmeter /bin/bash
# 增加ll别名
# alias ll='ls -l --color=auto'
# 进入到jmeter的bin目录
# cd /apache-jmeter-5.6.3/bin
# 执行jmeter
# ./jmeter -n -f -t /data/test.jmx -l /data/test.jtl -e -o /data/report
- 开启nginx目录浏览
server {
listen 80;
#listen 443 ssl;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
autoindex on; # 开启目录浏览功能
autoindex_localtime on; # 可选,显示文件修改时间为服务器本地时间
}
}
查看测试报告二
如果你不想使用浏览器查看结果,可以将生成的jtl文件下载回来,在有界面的jmeter查看
室内地图制作方法
项目源代码
https://gitee.com/vpclub-wh/indoor-map
室内地图制作
用threejs实现的室内地图展示,原理非常简单
- 使用正交相机创建2D效果
- 根据地图数据多边形形状
- 使用OrbitControls进行地图控制
所有的代码都在src/components/indoor-map.vue 中
制作地图形状数据
要实现平面地图倒是很简单,绘制多边形形状与标记,以便于数据绑定,则需要借助一些外部工具,因此,我们使用Tiled mapeditor 制作地图形状与标记
Tiled下载:https://www.mapeditor.org/
最终效果图
约定
首先,我们约定一张地图有四个图层,需要在图层中定义
- 背景图:bg,类型为图像层,图片的长和宽要求是2的整数倍
- 地板或者公共空间:floor,类型为对象层
- 房间:rooms,类型为对象层
- 文本标记:label,类型为对象层
进行区块标记
约定
- 所有的对象均使用多边形,既使用矩形绘制的区块,也需要转换为多边形,可以在对象上右键,转换为多边形
- 为便于协作,对象的name属性作为与数据绑定的唯一依赖关系
- 因为文字内容需要动态变化,而文字的长和宽需要计算,因此label使用点位置进行标记,再动态计算放到与此坐标点的居中位置
- 导出格式为json
tiledmap快捷键
- 放大:ctrl++
- 缩小:ctrl+-
- 默认大小:ctrl+0
- 平移:滚轮或者按住空格
- 填充到窗口正中间:ctrl+/
linux密码规则设置
安装密码策略工具
cat /etc/os-release
rpm -qa|grep libpwquality
yum -y install libpwquality
配置密码复杂度策略
vi /etc/security/pwquality.conf
cat /etc/security/pwquality.conf
# 最小长度为8位
minlen = 8
# 至少包含1个数字
dcredit = -1
# 至少包含1个大写字母
ucredit = -1
# 至少包含1个小写字母
lcredit = -1
# 至少包含1个特殊符号
ocredit = -1
# 至少需要满足多少个条件(数字,大写,小写,特殊符号)
minclass = 4
# 禁止使用常见弱密码(如"123456")
dictpath = /usr/share/cracklib/pw_dict
一键运行脚本
sudo cp /etc/security/pwquality.conf{,.bak.$(date +%F-%T)} && \
sudo tee /etc/security/pwquality.conf >/dev/null <<'EOF'
# 最小长度为8位
minlen = 8
# 至少包含1个数字
dcredit = -1
# 至少包含1个大写字母
ucredit = -1
# 至少包含1个小写字母
lcredit = -1
# 至少包含1个特殊符号
ocredit = -1
# 至少需要满足多少个条件(数字,大写,小写,特殊符号)
minclass = 4
EOF
验证是否起效
echo "123456" | pwscore
echo "12345678" | pwscore
echo "123456#Abc" | pwscore
echo "cZ2{sX" | pwscore
echo "oY0{dA5]" | pwscore
查看所有用户
awk -F: '($7 !~ /nologin|false/) && ($2 !~ /^[*!]/ || $2 ~ /^\$/ ) {printf "%-12s UID:%-5s Shell:%s\n", $1, $3, $7}' /etc/passwd /etc/shadow
repmgr+PostgreSQL故障自动转移
- 创建dockerfile
# 使用 CentOS 7 作为基础镜像
FROM centos:7
# 设置环境变量
ENV PG_MODE=primary \
PG_USER=postgres \
PG_PASSWORD=postgres \
NODE_ROLE=master \
MASTER_NAME=master \
MASTER_PORT=5432 \
RE_USER=repmgr \
NODE_ID=1 \
#NET_SEGMENT=192.168.0 \
NODE_NAME=master1 \
PG_DATADIR=/home/postgres/pgdata \
PG_REPMGR_CONF=/home/postgres/repmgr.conf \
PG_BINDIR=/usr/pgsql-12/bin \
PG_CONFIGDIR=/home/postgres/pgdata/postgresql.conf \
PRIORITY=1 \
CONNINFO_HOST=master \
CONNINFO_PORT=5432 \
PATH="/usr/pgsql-12/bin:${PATH}"
# 创建postgres用户和组
RUN groupadd -r postgres \
&& useradd -r -g postgres postgres
# 安装所需的软件和工具
RUN yum -y install epel-release \
&& yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm \
&& yum -y install postgresql12 postgresql12-server postgresql12-contrib repmgr12 \
&& yum -y install which sudo iproute hostname \
&& yum -y clean all
# 创建脚本存放目录
RUN mkdir -p /home/runtime/ \
&& chown -R postgres:postgres /home/runtime/
# 将 functions 文件和 entrypoint.sh 添加到镜像中
#COPY functions /home/postgres/runtime/functions
COPY functions /home/runtime/functions
#COPY entrypoint.sh /home/postgres/runtime/entrypoint.sh
COPY entrypoint.sh /home/runtime/entrypoint.sh
# 修改文件权限与属性
RUN mkdir -p /home/postgres/pgdata \
&& chown -R postgres:postgres /home/postgres \
#&& chown -R postgres:postgres /home/postgres/pgdata \
#&& chown 777 /home/postgres/pgdata \
#&& chmod +x /home/postgres/runtime/entrypoint.sh \
&& chmod +x /home/runtime/entrypoint.sh \
#&& chmod +x /home/postgres/runtime/functions
&& chmod +x /home/runtime/functions
#&& usermod -a -G root postgres \
#&& chmod 770 /home/postgres
RUN echo 'postgres ALL=(ALL) NOPASSWD: /bin/chown' >> /etc/sudoers
# 切换到 postgres 用户
USER postgres
# 初始化 PostgreSQL 数据目录
#RUN initdb -D ${PG_DATADIR} -U${PG_USER}
# 暴露 PostgreSQL 端口
EXPOSE 5432
# 设置启动脚本
#ENTRYPOINT ["/home/postgres/runtime/entrypoint.sh"]
ENTRYPOINT ["/home/runtime/entrypoint.sh"]
- 打包成镜像
docker run build -t centos7-pgsql-repmgr:v1.0
- docker-compose部署 master节点的dockercompose.yaml
version: "3"
services:
pg-master:
image: centos7-pgsql-repmgr:v1.9.27
hostname: master1
container_name: pg-master
environment:
PG_MODE: primary
PG_USER: postgres
PG_PASSWORD: postgres
NODE_ROLE: master
NODE_ID: 1
NODE_NAME: master1
#MASTER_NAME: 172.18.41.8
#MASTER_PORT: 25432
CONNINFO_HOST: 172.18.41.8
CONNINFO_PORT: 25432
PRIORITY: 10
POSTGRES_DB: repmgr
ports:
- "25432:5432"
volumes:
- ./pg-data:/home/postgres
slave节点的dockercompose.yaml
version: "3"
services:
pg-slave:
image: centos7-pgsql-repmgr:v1.9.27
container_name: pg-salve
hostname: salve1
#network_mode: host
environment:
PG_MODE: salve
PG_USER: postgres
PG_PASSWORD: postgres
PGPASSWORD: postgres
RE_USER: repmgr
NODE_ROLE: slave
MASTER_NAME: 172.18.41.8
MASTER_PORT: 25432
NODE_ID: 2
NODE_NAME: salve1
CONNINFO_HOST: 172.18.41.2
CONNINFO_PORT: 25432
PRIORITY: 15
POSTGRES_DB: repmgr
ports:
- "25432:5432"
volumes:
- ./pg-data:/home/postgres
~
master故障修复后已salve启动的dockercompose.yaml
version: "3"
services:
pg-slave:
image: centos7-pgsql-repmgr:v1.9.27
hostname: salve1
container_name: pg-salve
environment:
MASTER_NAME: 172.18.41.8
PGPASSWORD: postgres
MASTER_PORT: 25432
CONNINFO_HOST: 172.18.41.2
CONNINFO_PORT: 25432
NODE_ID: 1
PRIORITY: 15
POSTGRES_DB: repmgr
ports:
- "25432:5432"
volumes:
- ./pg-data:/home/postgres
注:恢复前请做好数据备份,防止数据丢失
附:functions脚本函数
#!/bin/bash
# 容器调用入口函数,根据传入命令不同,执行注册或启动主库、注册或启动备库,从新加入集群的操作。
configure_repmgr()
{
case ${NODE_ROLE} in
master)
echo 'master'
initialize_master
;;
slave)
echo 'slave'
initialize_slave
;;
# rejoin)
# echo 'rejoin'
# rejoin_node
# ;;
master_slave)
echo 'master_slave'
master_slave
;;
esac
}
# 用于设置 PostgreSQL 配置文件 'postgresql.conf' 中的参数。
# 该函数接受两个参数:参数名 (param_name) 和参数值 (param_value),
# 并使用 'sed' 命令在 'postgresql.conf' 文件中查找以参数名开头的行
# (行前可能有一个 # 注释符号),然后用新的参数值替换该行。
set_postgresql_param() {
param_name="$1"
param_value="$2"
sed -i "/^#${param_name} =/c ${param_name} = ${param_value}" ${PG_DATADIR}/postgresql.conf
}
# 这个函数用来向 PostgreSQL 的 pg_hba.conf 文件中添加一行新的配置
# ${1}:你想要添加的配置行
# ${PG_DATADIR}:pg_hba.conf 文件的位置
set_hba_param() {
# 判断是否传入了配置行
if [ -z "${1}" ]; then
echo "No configuration line provided."
return 1
fi
# 检查 pg_hba.conf 文件是否存在
if [ ! -e "${PG_DATADIR}/pg_hba.conf" ]; then
echo "${PG_DATADIR}/pg_hba.conf does not exist."
return 1
fi
# 检查是否已经拥有写权限,如果没有则尝试获取
if [ ! -w "${PG_DATADIR}/pg_hba.conf" ]; then
chmod u+w ${PG_DATADIR}/pg_hba.conf || {
echo "Could not set write permissions on ${PG_DATADIR}/pg_hba.conf"
return 1
}
fi
# 添加配置行到 pg_hba.conf 文件
echo "${1}" >> ${PG_DATADIR}/pg_hba.conf || {
echo "Could not write to ${PG_DATADIR}/pg_hba.conf"
return 1
}
return 0
}
master_slave() {
# 检查pgdata-bak是否存在,如果存在则警告并直接启动pgsql
if [ -d "${PG_DATADIR}-bak" ]; then
echo "Warning: ${PG_DATADIR}-bak already exists. Starting pgsql directly."
pg_ctl -D $PG_DATADIR start
else
#export PGPASSFILE=~/.pgpass
#write_pgpass
IP=`ping ${MASTER_NAME} -c 1 -w 1 | sed '1{s/[^(]*(//;s/).*//;q}'`
# 1. 备份pgsql数据
mv $PG_DATADIR ${PG_DATADIR}-bak
# 2. 创建pgsql数据目录
mkdir $PG_DATADIR
# 3. 从现在的主库获取备份数据
#repmgr -f $PG_REPMGR_CONF -h ${MASTER_NAME} -U repmgr -d repmgr -D $PG_DATADIR standby clone
echo "repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone --dry-run"
repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone --dry-run
echo "repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone"
repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone
# 4. 启动pgsql
echo "pg_ctl -D $PG_DATADIR start"
pg_ctl -D $PG_DATADIR start
# 5. 重新注册节点
echo "repmgr -f $PG_REPMGR_CONF standby register --force"
repmgr -f $PG_REPMGR_CONF standby register --force
fi
echo "repmgrd -f ${PG_REPMGR_CONF} --pid-file /tmp/repmgrd.pid --daemonize=false"
repmgrd -f ${PG_REPMGR_CONF} --pid-file /tmp/repmgrd.pid --daemonize=false
}
# 注册和执行主库操作,包括修改配置文件、创建用户、数据库及插件等,并设置守护进程用于自动故障转移。
initialize_master()
{
if [[ ! -f ${PG_DATADIR}/PG_VERSION ]]; then
initdb -D /home/${PG_USER}/pgdata -U${PG_USER}
write_postgresql_config
write_pg_hba_conf
write_pgpass
pg_ctl -D /home/${PG_USER}/pgdata -w start >/dev/null
psql -U ${PG_USER} -d postgres -h localhost -c "ALTER USER ${PG_USER} WITH PASSWORD '${PG_PASSWORD}';" >/dev/null
psql -U ${PG_USER} -d postgres -h localhost -c "create database repmgr;" >/dev/null
psql -U ${PG_USER} -d postgres -h localhost -c "create extension repmgr;" >/dev/null
psql -U ${PG_USER} -d repmgr -h localhost -c "create user repmgr with superuser;" >/dev/null
psql -U ${PG_USER} -d repmgr -h localhost -c "alter user repmgr password '${PG_PASSWORD}';" >/dev/null
write_repmgr_conf
repmgr -f ${PG_REPMGR_CONF} primary register
else
pg_ctl -D /home/${PG_USER}/pgdata -w start >/dev/null
fi
repmgrd -f ${PG_REPMGR_CONF} --pid-file /tmp/repmgrd.pid --daemonize=false
}
# 注册和执行备库操作,克隆主库,加入repmgr集群,并设置守护进程用于自动故障转移。
initialize_slave()
{
if [[ ! -f ${PG_DATADIR}/PG_VERSION ]]; then
write_repmgr_conf
write_pgpass
IP=`ping ${MASTER_NAME} -c 1 -w 1 | sed '1{s/[^(]*(//;s/).*//;q}'`
repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone --dry-run
repmgr -h ${IP} -p ${MASTER_PORT} -U repmgr -d repmgr -f ${PG_REPMGR_CONF} standby clone
pg_ctl -D ${PG_DATADIR} -w start >/dev/null
repmgr -f ${PG_REPMGR_CONF} standby register
repmgrd -f ${PG_REPMGR_CONF} --pid-file /tmp/repmgrd.pid --daemonize=false
else
pg_ctl -D ${PG_DATADIR} -w start >/dev/null
repmgrd -f ${PG_REPMGR_CONF} --pid-file /tmp/repmgrd.pid --daemonize=false
fi
}
# 将已有节点重新加入到集群,启动守护进行用于自动切换。
rejoin_node()
{
if [[ -f ${PG_DATADIR}/PG_VERSION ]]; then
IP=`ping ${MASTER_NAME} -c 1 -w 1 | sed '1{s/[^(]*(//;s/).*//;q}'`
if [[ -d /home/${PG_USER}/pgdata-bak ]];then
rm -fr /home/${PG_USER}/pgdata-bak
fi
cp -a /home/${PG_USER}/pgdata /home/${PG_USER}/pgdata-bak
rm -fr /home/postgres/pgdata/postmaster.pid
# pg_resetwal -f /home/${PG_USER}/pgdata
repmgr node rejoin -d "host=${IP} dbname=repmgr user=repmgr" --force-rewind --config-files="postgresql.conf,postgresql.auto.conf" -f ${PG_REPMGR_CONF} --verbose --dry-run
repmgr node rejoin -d "host=${IP} dbname=repmgr user=repmgr" --force-rewind --config-files="postgresql.conf,postgresql.auto.conf" -f ${PG_REPMGR_CONF} --verbose
fi
}
# 修改postgresql.conf文件
write_postgresql_config()
{
set_postgresql_param "wal_log_hints" "on"
set_postgresql_param "archive_mode" "on"
set_postgresql_param "archive_command" "\'test ! -f /home/${PG_USER}/pgarch/%f && cp %p /home/${PG_USER}/pgarch/%f\'"
set_postgresql_param "wal_level" "hot_standby"
set_postgresql_param "listen_addresses" "\'*\'"
set_postgresql_param "hot_standby" "on"
set_postgresql_param "max_wal_senders" "10"
set_postgresql_param "wal_keep_segments" "10"
set_postgresql_param "port" "${PG_PORT:-5432}"
set_postgresql_param "max_connections " "100"
set_postgresql_param "superuser_reserved_connections" "10"
set_postgresql_param "full_page_writes" "on"
set_postgresql_param "max_replication_slots" "10"
set_postgresql_param "synchronous_commit" "on"
set_postgresql_param "shared_preload_libraries" "repmgr"
set_postgresql_param "log_destination" "csvlog"
set_postgresql_param "logging_collector" "on"
set_postgresql_param "log_directory" "on"
set_postgresql_param "log_filename" "postgresql-%Y-%m-%d_%H%M%S"
set_postgresql_param "log_rotation_age" "1d"
set_postgresql_param "log_rotation_size" "10MB"
set_postgresql_param "log_statement" "mod"
#set_postgresql_param "data_directory" "/home/pgsqlData"
}
# 修改repmgr.conf文件
write_repmgr_conf()
{
echo "node_id=${NODE_ID}" > ${PG_REPMGR_CONF}
echo "node_name='${NODE_NAME}'" >> ${PG_REPMGR_CONF}
#echo "conninfo='host=${CONNINFO_HOST} user=repmgr dbname=repmgr connect_timeout=2'" >> ${PG_REPMGR_CONF}
echo "conninfo='host=${CONNINFO_HOST} port=${CONNINFO_PORT} user=repmgr dbname=repmgr connect_timeout=2'" >> ${PG_REPMGR_CONF}
echo "data_directory='${PG_DATADIR}'" >> ${PG_REPMGR_CONF}
echo "config_directory='${PG_CONFIGDIR}'" >> ${PG_REPMGR_CONF}
echo "use_replication_slots=true" >> ${PG_REPMGR_CONF}
echo "reconnect_attempts=4" >> ${PG_REPMGR_CONF}
echo "reconnect_interval=5" >> ${PG_REPMGR_CONF}
echo "monitor_interval_secs=2" >> ${PG_REPMGR_CONF}
echo "retry_promote_interval_secs=300" >> ${PG_REPMGR_CONF}
echo "pg_bindir='${PG_BINDIR}'" >> ${PG_REPMGR_CONF}
echo "log_level='INFO'" >> ${PG_REPMGR_CONF}
echo "log_status_interval=300" >> ${PG_REPMGR_CONF}
echo "log_facility='STDERR'" >> ${PG_REPMGR_CONF}
#echo "event_notification_command='${PG_EVENT_NOTIFICATION_SCRIPT}'" >> ${PG_REPMGR_CONF}
echo "promote_command='repmgr standby promote -f ${PG_REPMGR_CONF}'" >> ${PG_REPMGR_CONF}
echo "follow_command='repmgr standby follow -f ${PG_REPMGR_CONF} -W --log-to-file'" >> ${PG_REPMGR_CONF}
echo "failover='automatic'" >> ${PG_REPMGR_CONF}
echo "priority=${PRIORITY}" >> ${PG_REPMGR_CONF}
echo "degraded_monitoring_timeout=-1" >> ${PG_REPMGR_CONF}
}
# 修改pg_hba.conf文件
write_pg_hba_conf()
{
set_hba_param " local replication ${PG_USER} trust "
set_hba_param " host replication ${PG_USER} 127.0.0.1/32 trust "
set_hba_param " local repmgr ${PG_USER} trust "
set_hba_param " host repmgr ${PG_USER} 127.0.0.1/32 trust "
#set_hba_param " host replication ${PG_USER} ${NET_SEGMENT}/24 md5 "
#set_hba_param " host repmgr ${PG_USER} ${NET_SEGMENT}/24 md5 "
#set_hba_param " host repmgr repmgr ${NET_SEGMENT}/24 md5 "
set_hba_param " host replication ${RE_USER} 0.0.0.0/0 md5 "
set_hba_param " host repmgr ${PG_USER} 0.0.0.0/0 md5 "
set_hba_param " host repmgr repmgr 0.0.0.0/0 md5 "
set_hba_param " host all all 0.0.0.0/0 md5 "
}
# 修改.pgpass文件
write_pgpass()
{
if [ -f ~/.pgpass ]
then
rm -f ~/.pgpass
fi
echo "*:*:*:${PG_USER}:${PG_PASSWORD}" >> ~/.pgpass
echo "*:*:repmgr:repmgr:${PG_PASSWORD}" >> ~/.pgpass
chmod 600 ~/.pgpass
}
附2:entrypoint.sh
#!/bin/bash
set -e
# shellcheck source=runtime/functions
#source "/home/postgres/runtime/functions"
source "/home/runtime/functions"
sudo chown -R postgres:postgres /home/postgres
if [ ! -d /home/postgres/pgarch/ ];then
mkdir -p /home/postgres/pgarch/
fi
if [ -f /tmp/repmgrd.pid ];then
rm -fr /tmp/repmgrd.pid
fi
#运行repmgr
configure_repmgr
EFK的安装部署
elasticsearch的docker-compose文件
version: '3.1'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
container_name: elasticsearch
environment:
- node.name=elasticsearch
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- "ELASTIC_PASSWORD=iyfbvr1EM19jqjq"
- "xpack.security.enabled=true"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./es_data:/usr/share/elasticsearch/data
ports:
- 9200:9200
fluentd k8s部署文件
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
spec:
selector:
matchLabels:
k8s-app: fluentd-logging
version: v1
template:
metadata:
labels:
k8s-app: fluentd-logging
version: v1
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
env:
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch-logging"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
# Option to configure elasticsearch plugin with self signed certs
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERIFY
value: "true"
# Option to configure elasticsearch plugin with tls
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERSION
value: "TLSv1_2"
# X-Pack Authentication
# =====================
- name: FLUENT_ELASTICSEARCH_USER
value: "elastic"
- name: FLUENT_ELASTICSEARCH_PASSWORD
value: "changeme"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# mountPath: /var/lib/docker/containers
# readOnly: true
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
mountPath: /var/log/pods
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# hostPath:
# path: /var/lib/docker/containers
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
hostPath:
path: /var/log/pods
注: 挂载目录dockercontainerlogdirectory 要根据docker部署的目录进行修改
kibana的docke-compose部署
version: '3'
services:
kibana:
image: docker.elastic.co/kibana/kibana:7.6.2
ports:
- 5601:5601
volumes:
- ./kibana.yml:/usr/share/kibana/config/kibana.yml
kibana配置文件
#
# ** THIS IS AN AUTO-GENERATED FILE **
#
# Default Kibana configuration for docker target
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://192.168.5.23:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
elasticsearch.username: elastic
elasticsearch.password: iyfbvr1EM19jqjq
linux时间同步
sudo tee -a /etc/chrony.conf << 'EOF'
server ntp.aliyun.com iburst
server ntp1.tencent.com iburst
EOF
systemctl restart chronyd
# 验证时间源
chronyc sources -v
# 立即同步
chronyc -a makestep
windows下安装wget
- 下载地址:http://downloads.sourceforge.net/gnuwin32/wget-1.11.4-1-setup.exe
- 加入到环境变量的path:C:\Program Files (x86)\GnuWin32\bin
ibd2mysql&&mysql数据库idb文件修复
# 下载
wget https://github.com/ddcw/ibd2sql/archive/refs/tags/v2.0.tar.gz
# 解压
# 生成建表语句和insert语句
python main.py /data/mysql/bad_db/bigdata_viproomdb/ods_od_work.ibd --ddl --sql --output bigdata_viproomdb
# 恢复
堡垒机jumpserver使用手册
非必要,不要开放端口
先上一张图。
如图所示,网络上上有无数的人,使用工具扫描,探测默认密码、穷举简单密码。服务器一旦被攻击,轻则中木马挖矿,重则删库丢失数据,造成经济损失。
重要的事情说三遍,非必要,不要开放端口,不要映射端口。
重要的事情说三遍,非必要,不要开放端口,不要映射端口。
重要的事情说三遍,非必要,不要开放端口,不要映射端口。
默认情况下,防火墙仅应该开放80,443端口,其他端口一定要思考是否有开放的必要性
考虑到实际维护,需要进行各种调试,应该使用堡垒机进行维护,本文为自建堡垒机jumpserver使用手册,堡垒机安装管理请参看其他文章
- jumpserver安装手册
- jumpserver管理手册
- jumpserver使用手册
使用
- 登录
登录地址: https://jumpserver.devops.vppark.cn/
如果你还没有用户名,请联系管理员索取用户名密码
- 切换视图
如果你有多重身份角色,可以切换不同的视图使用不同的功能
- 我的资产
可以使用的服务器、数据库等,如果列表没有出现你需要维护的服务器,请联系系统管理员进行授权
- 远程登录
远程登录有两个入口,分别为左的web终端、右上角的图标
- 选择你需要进行连接的的资产,连接方式
左边为资产列表,右边为远程面板。点击对应的资产可以弹出连接窗口,默认使用web cli方式。
如果觉得右边的远程面板太小了,不方便,可以在资产上右键,在新窗口打开。
某些时候需要使用客户端连接、提高效率时,可以选择客户端连接方式。复制相关信息到你的客户端工具,既可以进行连接。
- 安装客户端插件
如果想使用使用客户端连接,需要安装插件
下载对应的操作系统软件版本,进行安装
注意,安装过程非常快,一闪而过,且在桌面不会留下任何图标,安装成功与否,可以参看控制面板内已经安装程序是否出现
- 远程桌面
由于windows无法容器化,使用了ubuntu xface 容器化作为远程桌面。
中文输入法支持,启动fctix
点击键盘图标,如果无法切换到中文输入法,则需要增加中文输入法
添加google pinyin输入法
# 打开终端,运行
ln -sf /usr/local/bin/navicat16 /home/ubuntu/Desktop/navicat16
-
sudo 密码,如果部分命令提示没有权限,需要sudo,请咨询管理员索要 sudo 密码
-
连接到k8s集群内的mysql或redis, 资产名称带有out-k8s的,与主机网络一致,资产名称带有in-k8s的,与k8s网络一致,连接字符串填写服务发现既可
rabbitmq
docker-compse.yaml
version: '3.3'
services:
rabbitmq:
image: rabbitmq:3.8-management
container_name: rabbitmq #容器的名字
restart: always
environment:
- RABBITMQ_DEFAULT_USER=rabbitmq # 默认用户名
- RABBITMQ_DEFAULT_PASS=<your password> #默认密码
ports:
- 15672:5672
- 25672:15672
volumes:
- ./rabbitmq_data:/var/lib/rabbitmq
启动rabbitmq
docker-compose -f docker-compse.yaml up -d
tmux终端复用&&后台运行终端
有时候需要长时间执行一个shell,但是网络断开后,终端也随之关闭,可以使用tmux来新开一个持续运行的终端,不受ssh断开影响。
# 安装
yum install tmux
# 创建一个新的会话终端
tmux
# 创建一个新的会话终端,自定义名字
tmux new -s <name>
# 执行成功后,ctrl+b,d可以关闭终端窗口,但是shell程序可以继续在后台运行
# 查看运行中的复用终端
tmux ls
# 链接到终端
tmux attach -t 0
# 强杀某个终端
云监控bt-monitor
及时发现问题和预警,在最终用户发现问题前,解决掉问题,是保证业务连续运行的唯一手段。
本文为宝塔出品的bt-monitor安装使用手册,由于宝塔没有提供docker版本,因此自己制作了docker版本
主要功能:主机性能监控、服务器漏洞监控、主机安全监控、URL监控、自定义脚本返回特定字符串监控
官方帮助:https://www.bt.cn/bbs/thread-116143-1-1.html
- 更新日志
2023-10-27:更新版本到v2.2.9
swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/bt-monitor:v2.2.9
2023-08-09:更新版本到v2.2.8
swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/bt-monitor:20230809
docker-compose文件
version: '3'
services:
bt-monitor:
image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/bt-monitor:20230708
restart: always # 自动重启
ports:
- 806:806
volumes:
- ./data:/www/server/bt-monitor/data
- ./config:/www/server/bt-monitor/config
environment:
- TZ=Asia/Shanghai
privileged: true
# bt-monitor默认是https访问方式,而k8s默认是http访问后端
# 如果在K8S部署,会提示协议不匹配
# 也由于bt-monitor默认使用的是自签名证书,会提示证书错误,需要根据ingress类型自行解决
# 如果是 traefik ingress则按如下处理
# traefik 增加跳过HTTPS验证
# - --serverstransport.insecureskipverify=true
# 服务发现增加注释,注意,是服务发现,不是POD,提示traefik使用https访问后端
# traefik.ingress.kubernetes.io/service.serversscheme: https
# 主控端命令
# btm status
# btm 根据提示
# 被控端命令
# btmonitoragent status
# systemctl status btmonitoragent
# systemctl daemon-reload
# systemctl restart btmonitoragent
# tail -n200 /usr/local/btmonitoragent/logs/logs.log
# 如果被控老莫名其妙下线,查看服务状态命令 btmonitoragent status 提示服务停止,可以将服务配置改为自动重启
# 本修改不是必须的,需要根据实际情况来
# 2023年7月8日,服务不会自动重连,需要修改
# vi /usr/lib/systemd/system/btmonitoragent.service
# [Service]
# Type=forking
# Restart=always
系统设置
# 进入到容器
docker exec -it d026bd353eab /bin/bash
# 输入btm,根据提示操作
# 查看安全登录地址
btm 2
# 重置管理员密码
btm 6
# 1) 重启主控服务
# 2) 查看登录地址及安全入口
# 3) 停止主控服务
# 4) 查看运行状态
# 5) 查看错误日志
# 6) 修改管理员密码(自动生成随机密码)
# 7) 关闭basic_auth
# 8) 修改管理员密码(手动输入密码)
# 9) 取消域名绑定
# 10) 修改安全入口(手动输入)
# 11) 取消IP绑定限制
# 12) 修改端口(手动输入)
# 13) 修复主控(检查错误并更新云安全监控到最新版)
# 14) 无法访问?(云安全监控故障检测自动修复程序-超强力版)
# 15) 关闭动态口令认证
# 0) 退出或按组合键[ctrl+c]
安装使用手册
根据前面的系统设置提示,得到登录地址,默认用户名为admin , 密码为btm 8 或者 btm 6 设置的密码。
- 用户绑定
根据提示操作,截止发文前,一个账号可以免费使用5台主机。
- 重置访问路径
如果觉得系统自动生成的安全域名不好记忆,可以修改入口路径
- 加入被监控主机
输入正确的主控端IP地址,点击获取命令获取被控端安装命令,到每一台被控端执行
-
对被监控主机授权
bt-monitor为收费软件,截止发文止,每个账号可以免费授权5台主机
- 主机监控信息
- 设置企业微信接收告警
- 创建微信群组
- 添加聊天机器人
将地址填入到告警设置窗口
-
添加业务监控
- URL监控
- 端口监控
- ping
- 自定义监控
如果业务监控提供的三种监控方式都不能满足你。可以使用shell进行自定义监控
- 告警规则设置
将不需要的告警规则去掉,一般只需要:
- 【主机 / 主机状态 / 主机上下线】 匹配 下线
- 【日志 / SSH登录日志 / [所有]用户最近1小时登录失败次数】 大于 5
- 【资源 / 磁盘-[所有] / 磁盘占用率】 大于 90%
- 【资源 / 内存 / 内存占用率】 大于 90%
- 【资源 / CPU / CPU使用率】 大于 90%
mogodb
docker-copose.yam
version: "3"
services:
mongodb:
image: mongo:4.2.8
container_name: mongo-baise #容器的名字
restart: always # 自动重启
privileged: true
environment:
- MONGO_INITDB_ROOT_USERNAME=admin #初始的用户名
- MONGO_INITDB_ROOT_PASSWORD=fk12345 #初始用户名的密码
ports:
- 27017:27017
volumes:
- "./dbs:/data/db"
启动mongodb
docker-compose -f docker-compse.yaml up -d
开启身份认证
# 不启用身份验证,默认为不验证
mongod --noauth
# 启用身份验证
mongod --auth
配置身份认证
# 先以无身份认证启动容器,默认为无身份认证,或者加入启动参数
mongod --noauth
# 进入到容器
docker exec -it <容器ID> /bin/bash
# 连接到mongo数据库
mongo
# 创建管理员用户
use admin
db.createUser({user:"admin",pwd:"123456",roles:[{role:"root","db":"admin"}]});
# 追加权限
db.grantRolesToUser("admin",["dbOwner"])
db.grantRolesToUser("admin",["readWrite"])
# 数据库用户角色:read、readWrite;
# 数据库管理角色:dbAdmin、dbOwner、userAdmin;
# 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
# 备份恢复角色:backup、restore;
# 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、# dbAdminAnyDatabase
# 超级用户角色:root
# 查看系统用户
db.system.users.find()
show users
# 授权,如果提示去权限时可能需要如下操作
db.auth("admin","123456")
# 创建指定库用户,创建之前需要先切换到admin库进行授权,创建指定库用户,需要先切换到这个库下面
use dev_join_logdb
db.createUser({user:"join_admin",pwd:"Vp141421",roles:[{role:"dbOwner","db":"dev_join_logdb"}]});
db.auth("join_admin","Vp141421")
# 追加权限
db.grantRolesToUser("join_admin",["dbOwner"])
db.grantRolesToUser("join_admin",["readWrite"])
# 重启启动容器,并加入启动参数开启mongo的身份验证
mongod --auth
# 重新连接时则需要,附加多个参数进行身份验证
mongo -u admin -p 123456 --authenticationDatabase admin
mongo -u join_admin -p Vp141421 --authenticationDatabase dev_join_logdb
# 删除用户
db.system.users.remove({"_id" : "dev_join_logdb.join_admin"})
linux时间同步
# 安装
yum install chrony -y
# 启用
systemctl enable chronyd --now
# 查看状态
systemctl status chronyd
# 填入配置
vi /etc/chrony.conf
server ntp.myhuaweicloud.com minpoll 4 maxpoll 10 iburst
server ntp.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp1.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp2.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp3.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp4.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp5.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp6.aliyun.com minpoll 4 maxpoll 10 iburst
# 重启服务使配置生效
systemctl restart chronyd
# 查看时间同步源的状态
chronyc sources -v
# 查看时间同步的总体统计
chronyc tracking
chronyc tracking
Reference ID : CB6B0658 (203.107.6.88)
Stratum : 3
Ref time (UTC) : Wed Feb 25 07:36:29 2026
System time : 0.000241572 seconds slow of NTP time
Last offset : +0.000208973 seconds
RMS offset : 0.002260513 seconds
Frequency : 6.410 ppm slow
Residual freq : -14.559 ppm
Skew : 3.548 ppm
Root delay : 0.051426787 seconds
Root dispersion : 0.002829272 seconds
Update interval : 16.3 seconds
Leap status : Normal
nexus
docker-compose.yaml
version: '3'
services:
nexus:
image: sonatype/nexus3
ports:
- 8081:8081
volumes:
- ./nexus-data:/nexus-data
启动服务
docker-compose -f docker-compse.yaml up -d
设置admin密码
默认用户名是admin,密码在nexus-data/admin.password文件,进入到容器查看
常见错误处理
日志报org.apache.http.conn.ConnectTimeoutException: Connect to sonatype-download.global.ssl.fastly.net:443 [sonatype-download.global.ssl.fastly.net/67.15.100.252] failed: connect timed out
处理方法: 登录账号,打开【System】--》【Capabilities】,将【Outreach:Management】禁用即可。
升级gcc
# 下载安装包
wget http://mirrors.aliyun.com/centos/7/os/x86_64/Packages/scl-utils-20130529-19.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/os/x86_64/Packages/scl-utils-build-20130529-19.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/Packages/d/devtoolset-9-binutils-2.32-16.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/Packages/d/devtoolset-9-gcc-9.3.1-2.2.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/Packages/d/devtoolset-9-gcc-c++-9.3.1-2.2.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/Packages/d/devtoolset-9-libstdc++-devel-9.3.1-2.2.el7.x86_64.rpm
wget http://mirrors.aliyun.com/centos/7/sclo/x86_64/rh/Packages/d/devtoolset-9-runtime-9.1-0.el7.x86_64.rpm
# 打包(如果需要离线安装)
tar -czvf devtoolset9-vault.tar.gz ./
# 安装
yum install -y ./*.rpm
# 设置
scl enable devtoolset-9 bash
# 验证
gcc --version
# 写入全局环境变量
echo "source /opt/rh/devtoolset-9/enable" >> /etc/profile
source /etc/profile
gitlab
docker-compose.yaml
version: '3'
services:
redis:
restart: always
image: sameersbn/redis:latest
command:
- --loglevel warning
volumes:
- ./redis:/var/lib/redis:Z
postgresql:
restart: always
image: sameersbn/postgresql:9.6-2
volumes:
- ./postgresql:/var/lib/postgresql:Z
environment:
- DB_USER=gitlab
- DB_PASS=password
- DB_NAME=gitlabhq_production
- DB_EXTENSION=pg_trgm
gitlab:
restart: always
image: sameersbn/gitlab:10.6.4
depends_on:
- redis
- postgresql
ports:
- "10080:80"
# - "10081:80"
- "10022:22"
volumes:
- ./gitlab:/home/git/data:Z
environment:
- DEBUG=false
- DB_ADAPTER=postgresql
- DB_HOST=postgresql
- DB_PORT=5432
- DB_USER=gitlab
- DB_PASS=password
- DB_NAME=gitlabhq_production
- REDIS_HOST=redis
- REDIS_PORT=6379
- TZ=Asia/Shanghai
- GITLAB_TIMEZONE=Beijing
- GITLAB_HTTPS=false
- SSL_SELF_SIGNED=false
- GITLAB_HOST=git.whdev.vpclub.cn
- GITLAB_PORT=10081
- GITLAB_SSH_PORT=10022
- GITLAB_RELATIVE_URL_ROOT=
- GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alphanumeric-string
- GITLAB_ROOT_PASSWORD=
- GITLAB_ROOT_EMAIL=
- GITLAB_NOTIFY_ON_BROKEN_BUILDS=true
- GITLAB_NOTIFY_PUSHER=false
- GITLAB_EMAIL=
- GITLAB_EMAIL_REPLY_TO=
- GITLAB_INCOMING_EMAIL_ADDRESS=
- GITLAB_BACKUP_SCHEDULE=daily
- GITLAB_BACKUP_TIME=01:00
- SMTP_ENABLED=false
- SMTP_DOMAIN=exmail.qq.com
- SMTP_HOST=smtp.exmail.qq.com
- SMTP_PORT=465
- SMTP_USER=
- SMTP_PASS=
- SMTP_STARTTLS=true
- SMTP_AUTHENTICATION=login
- IMAP_ENABLED=false
- IMAP_HOST=imap.gmail.com
- IMAP_PORT=993
- IMAP_USER=mailer@example.com
- IMAP_PASS=password
- IMAP_SSL=true
- IMAP_STARTTLS=false
- OAUTH_ENABLED=false
- OAUTH_AUTO_SIGN_IN_WITH_PROVIDER=
- OAUTH_ALLOW_SSO=
- OAUTH_BLOCK_AUTO_CREATED_USERS=true
- OAUTH_AUTO_LINK_LDAP_USER=false
- OAUTH_AUTO_LINK_SAML_USER=false
- OAUTH_EXTERNAL_PROVIDERS=
- OAUTH_CAS3_LABEL=cas3
- OAUTH_CAS3_SERVER=
- OAUTH_CAS3_DISABLE_SSL_VERIFICATION=false
- OAUTH_CAS3_LOGIN_URL=/cas/login
- OAUTH_CAS3_VALIDATE_URL=/cas/p3/serviceValidate
- OAUTH_CAS3_LOGOUT_URL=/cas/logout
- OAUTH_GOOGLE_API_KEY=
- OAUTH_GOOGLE_APP_SECRET=
- OAUTH_GOOGLE_RESTRICT_DOMAIN=
- OAUTH_FACEBOOK_API_KEY=
- OAUTH_FACEBOOK_APP_SECRET=
- OAUTH_TWITTER_API_KEY=
- OAUTH_TWITTER_APP_SECRET=
- OAUTH_GITHUB_API_KEY=
- OAUTH_GITHUB_APP_SECRET=
- OAUTH_GITHUB_URL=
- OAUTH_GITHUB_VERIFY_SSL=
- OAUTH_GITLAB_API_KEY=
- OAUTH_GITLAB_APP_SECRET=
- OAUTH_BITBUCKET_API_KEY=
- OAUTH_BITBUCKET_APP_SECRET=
- OAUTH_SAML_ASSERTION_CONSUMER_SERVICE_URL=
- OAUTH_SAML_IDP_CERT_FINGERPRINT=
- OAUTH_SAML_IDP_SSO_TARGET_URL=
- OAUTH_SAML_ISSUER=
- OAUTH_SAML_LABEL="Our SAML Provider"
- OAUTH_SAML_NAME_IDENTIFIER_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- OAUTH_SAML_GROUPS_ATTRIBUTE=
- OAUTH_SAML_EXTERNAL_GROUPS=
- OAUTH_SAML_ATTRIBUTE_STATEMENTS_EMAIL=
- OAUTH_SAML_ATTRIBUTE_STATEMENTS_NAME=
- OAUTH_SAML_ATTRIBUTE_STATEMENTS_FIRST_NAME=
- OAUTH_SAML_ATTRIBUTE_STATEMENTS_LAST_NAME=
- OAUTH_CROWD_SERVER_URL=
- OAUTH_CROWD_APP_NAME=
- OAUTH_CROWD_APP_PASSWORD=
- OAUTH_AUTH0_CLIENT_ID=
- OAUTH_AUTH0_CLIENT_SECRET=
- OAUTH_AUTH0_DOMAIN=
- OAUTH_AZURE_API_KEY=
- OAUTH_AZURE_API_SECRET=
- OAUTH_AZURE_TENANT_ID=
linux安装nodejs和codex
安装nodejs
# 下载
# https://nodejs.org/zh-cn/download
wget https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz
# 解压缩
tar -xvf node-v24.18.0-linux-x64.tar.xz
# 移动
mv node-v24.18.0-linux-x64 /usr/local/lib
# 写入环境变量并刷新
echo 'export PATH=/usr/local/lib/node-v24.18.0-linux-x64/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
安装codex
# 安装
npm install -g @openai/codex --registry=https://registry.npmmirror.com
配置 config.toml
mkdir -p /root/.codex
touch /root/.codex/config.toml
vi /root/.codex/config.toml
model_provider = "custom"
model = "qwen3.7-plus"
disable_response_storage = true
network_access = "enabled"
[model_providers.custom]
name = "custom"
wire_api = "responses"
requires_openai_auth = true
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
配置 auth.json
mkdir -p /root/.codex
touch /root/.codex/auth.json
vi /root/.codex/auth.json
{
"OPENAI_API_KEY": "sk-*************************8"
}
redis单节点部署
redis单节点yaml文件
version: "3"
services:
redis:
image: harbor.iovhm.com/hub/redis:5
container_name: redis
restart: always # 自动重启
privileged: true
ports:
- 6379:6379
command: redis-server --appendonly yes --requirepass <password> #redis的密码
volumes:
- ./data:/data
networks:
- redis-bridge
networks:
redis-bridge:
external:
name: redis-bridge
redis服务启动
docker-compose -f docker-compse.yaml up -d
使用codex修复服务器漏洞升级openssh
安装并配置codex
https://iovhm.com/book/books/cee63/page/linuxnodejscodex
手工升级方式
https://iovhm.com/book/books/cee63/page/openssh
先把源文件下载回来
wget http://cdn-oss.vppark.cn/software/openssl-openssh/openssl-3.6.3.tar.gz
wget http://cdn-oss.vppark.cn/software/openssl-openssh/openssh-10.0p2.tar.gz
给codex下指令
当前文件夹下有openssh和openssl的源代码,帮我升级到对应的版本,需要注意先备份原有配置。
openssl的相关配置为
./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl
openssh的相关配置为
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-md5-passwords --with-pam --with-zlib --with-ssl-dir=/usr/local/openssl
安装完成后先不要重启sshd,待我手工重启
minio
minio的docker-compose的文件
# vi docker-compose.yaml
version: '3'
services:
velero-minio:
image: minio/minio:latest
restart: always # 自动重启
privileged: true
ports:
- 33900:9000 # client 端口
- 33901:9001 # console 端口
volumes:
- ./data:/data
command: server /data --console-address :9001 --address :9000
environment:
- MINIO_ROOT_USER=
- MINIO_ROOT_PASSWORD=!
# 如果你的minio使用容器启动的,不是独立主机、正式使用的端口与部署的端口不一致,可能显示的地址错误
- MINIO_SERVER_URL=http://minio.abc.com
# 如果你的minio使用容器启动的,不是独立主机、正式使用的端口与部署的端口不一致,可能显示的地址错误
- MINIO_BROWSER_REDIRECT_URL=http://minio-console.abc.com
MINIO_SERVER_URL 和 MINIO_BROWSER_REDIRECT_URL
- MINIO_BROWSER_REDIRECT_URL
这个是用来访问控制台的,可以使用代理
location /minio-console/ {
rewrite ^/minio-console/(.*) /$1 break; # 地址重新,必须
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # 开启websocket,必须
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:9001; # MinIO 控制台的地址和端口
}
}
- MINIO_SERVER_URL
API端点,console也需要用这个端点访问API,如果这个端点地址不能被console访问,console会出现无法登录或者白屏,如果你的服务器不能回路,那需要做主机解析。如果你确实不能把API地址设置一个单独的域名,可以设置代理,但是minio要兼容s3协议,要求的路径是http://
注:以上内容未验证
location /minio/ {
rewrite ^/minio/(.*) /$1 break;
proxy_pass http://192.168.65.2:9000;
}
启动服务
docker-compose -f docker-compse.yaml up -d
网络共享
yum install epel-release
yum install s3fs-fuse
echo <USER>:<PASSWORD> > /etc/passwd-s3fs
chmod 640 /etc/passwd-s3fs
#测试一下
# s3fs -o passwd_file=/etc/passwd-s3fs upload /data/nfs -o use_path_request_style -o url=http://172.130.100.91:9000
# 开机自动挂载
upload /data/nfs fuse.s3fs _netdev,allow_other,use_path_request_style,url=http://172.130.100.91:9000 0 0
打包多平台docker镜像
先再不同的平台下载对应的镜像并改名后推送
# x86_64架构
docker pull openjdk:17.0.1
docker tag openjdk:17 harbor.iovhm.com/public/openjdk:17-amd64
docker push harbor.iovhm.com/public/openjdk:17-arm64
# arm64架构
docker pull openjdk:17.0.1
docker tag openjdk:17 harbor.iovhm.com/public/openjdk:17-arm64
docker push harbor.iovhm.com/public/openjdk:17-arm64
docker manifest create harbor.iovhm.com/public/openjdk:17 \
--amend harbor.iovhm.com/public/openjdk:17-amd64 \
--amend harbor.iovhm.com/public/openjdk:17-arm64
docker manifest annotate harbor.iovhm.com/public/openjdk:17 \
harbor.iovhm.com/public/openjdk:17-amd64 --os linux --arch amd64
docker manifest annotate harbor.iovhm.com/public/openjdk:17 \
harbor.iovhm.com/public/openjdk:17-arm64 --os linux --arch arm64
docker manifest push harbor.iovhm.com/public/openjdk:17
jenkins&&docker安装
核心点是3个主要挂载的,socker.sock需要根据情况定,需要以root身份运行
version: '3'
services:
jenkins:
image: harbor.iovhm.com/hub/jenkins/jenkins:2.488-jdk17
container_name: jenkins
restart: always
privileged: true
user: root
ports:
- "8080:8080"
volumes:
- ./jenkins_home:/var/jenkins_home
- ./m2:/root/.m2
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
environment:
- TZ=Asia/Shanghai
准备工作
需要先建立拉取代码和推送docker镜像使用的用户名密码凭据
系统管理 > 凭据管理 > 全局 > Add Credentials
新建pipeline (流水线项目)
然后根据项目类型,将后面章节的pipeline脚本复制到项目配置
springboot 多项目
当项目有多个子项目时,一个一个建确实比较麻烦,可以使用如下代码示例
特别注意:sh代码或者代码块,当使用三个单引号(''')时候,无法传入局部变量,需要换成三个双引号("""),使用双引号的时候需要对sh代码块特殊字符进行转义
- 既三个单引号不会对sh代码块的内容处理,其中可以使用的参数只有环境变量参数(通过环境变量中转参数);
- 三个双引号会对sh代码块做字符串替换,所以需要处理特殊字符串,例如原本就需要引用的环境变量符号($)需要修改成(\${var})
pipeline {
agent any
// 环境变量,全局可用
environment {
// GIT仓库地址
GIT_URL = 'https://g.vpclub.cn/park/park-huahai/admin-java.git'
// GIT 仓库凭证
GIT_IDENTITY = 'git-szdev-donglietao'
// 项目名称,将生成docker镜像的名称的一部分
PROJECT_NAME ="gzxfzd"
// docker仓库的分组名称
PROJECT_NS ="vp-park"
// docker仓库地址
DOCKER_REGISTRY ="swr.cn-south-1.myhuaweicloud.com"
// 这三个参数将组合成你的最终docker镜像名称为
// swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo
// DOCKER 仓库凭证
DOCKER_IDENTITY = 'docker-huaweicloud'
}
// 构建过程参数
parameters {
// 代码分支
choice(name: 'GIT_BRANCH', choices: ['development', 'release'],description: '代码分支 测试dev,生产release')
// 镜像TAG,将最被添加到docker镜像标签上
// 这个参数将被组合到镜像的最终名称
// swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo:test
choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
choice(
name: 'PUSH_SERVICE',
choices: ['ALL',
'jfast-service/jfast-auth',
'jfast-service/jfast-basic',
'jfast-service/jfast-cloud-docking',
'jfast-devops-center',
'jfast-gateway',
'jfast-oa-services',
'jfast-service/jfast-research-center'
],
description: '需要推送的服务'
)
}
tools {
// 这个地方与系统设置的mvn工具包对应
maven "M3"
// JDK版本,与全局jdk工具对应
jdk "JDK-8"
}
stages {
stage('Build') {
steps {
// 注意credentialsId参数,填入你前面创建的git仓库用户名IM
// 注意url参数,填入你的git仓库地址
git branch: "${GIT_BRANCH}", credentialsId: "${GIT_IDENTITY}", url: "${GIT_URL}"
// 构建脚本默认在代码仓库根目录运行
// 如果你的pom.xml文件不在根目录,需要使用dir指令进入到对应的目录
// 或者使用mvn clean compile package -f ./source/pom.xml 指定pom文件位置
// 如果你的pom文件是在源代码的根目录,可以删除dir代码块包裹
dir("./"){
// 注意这里的多行脚本引号
// 如果是三个单引号,则无法引用局部变量
// 如果是三个双引号,可以引用局部变量,但是需要对特殊字符进行转义
sh """
pwd
java -version
mvn clean compile package
"""
}
}
}
stage("docker"){
steps{
script{
def SERVICE_MAP=[
"jfast-service/jfast-auth":"jfast-auth",
"jfast-service/jfast-basic":"jfast-basic",
"jfast-service/jfast-cloud-docking":"jfast-cloud-docking",
"jfast-devops-center":"jfast-devops-center",
"jfast-gateway":"jfast-gateway",
"jfast-oa-services":"jfast-oa-services",
"jfast-service/jfast-research-center":"jfast-research-center"
]
// 使用docker凭据,credentialsId为你前面创建的docker仓库凭据
withCredentials([usernamePassword(credentialsId: "${DOCKER_IDENTITY}", passwordVariable: 'docker_password', usernameVariable: 'docker_username')]) {
def conditionVal= "${PUSH_SERVICE}"
def branch = "${GIT_BRANCH}"
if(conditionVal=="ALL"){
for(item in SERVICE_MAP.entrySet()){
def serviceName = "${item.value}"
def serverPath = "${item.key}"
dir("./${serverPath}"){
// 注意这里的多行脚本引号
// 如果是三个单引号,则无法引用局部变量
// 如果是三个双引号,可以引用局部变量,但是需要对特殊字符进行转义
sh """
pwd
echo ${serviceName}
docker login -u ${docker_username} -p ${docker_password} ${DOCKER_REGISTRY}
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${IMAGE_TAG}-${BUILD_NUMBER} .
docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${IMAGE_TAG}-${BUILD_NUMBER}
"""
}
}
}else{
def serviceName =SERVICE_MAP[conditionVal]
def serverPath = conditionVal
dir("./"){
// 注意这里的多行脚本引号
// 如果是三个单引号,则无法引用局部变量
// 如果是三个双引号,可以引用局部变量,但是需要对特殊字符进行转义
sh """
pwd
echo ${serviceName}
docker login -u ${docker_username} -p ${docker_password} ${DOCKER_REGISTRY}
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${IMAGE_TAG}-${BUILD_NUMBER} .
docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${IMAGE_TAG}-${BUILD_NUMBER}
"""
}
}
}
}
}
}
}
}
springboot maven Pipeline
pipeline {
agent any
// 环境变量,全局可用
environment {
GIT_URL = 'https://g.vpclub.cn/park/crabapple-drink/crabapple-drink.git'
// GIT 仓库凭证
GIT_IDENTITY = 'git-szdev-donglietao'
// 项目名称,将生成docker镜像的名称的一部分
PROJECT_NAME = 'crabapple-drink'
// docker仓库的分组名称
PROJECT_NS = 'vp-park'
// docker仓库地址
DOCKER_REGISTRY = 'swr.cn-south-1.myhuaweicloud.com'
// 这三个参数将组合成你的最终docker镜像名称为
// swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo
// DOCKER 仓库凭证
DOCKER_IDENTITY = 'docker-huaweicloud'
}
// 构建过程参数
parameters {
// 代码分支
choice(name:"GIT_BRANCH",choices:["development","release"],description:"代码分支")
// 镜像TAG,将最被添加到docker镜像标签上
// 这个参数将被组合到镜像的最终名称
// swr.cn-south-1.myhuaweicloud.com/vp-park/springboot-demo:test
choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
}
tools {
// 这个地方与系统设置的mvn工具包对应
maven "M3"
// jdk版本,与全局jdk工具对应
// jdk "JDK-8"
}
stages {
stage('Build') {
steps {
// Get some code from a gitlab
// 注意credentialsId参数,填入你前面创建的git仓库用户名IM
// 注意url参数,填入你的git仓库地址
git branch: "${GIT_BRANCH}", credentialsId: "${GIT_IDENTITY}", url: "${GIT_URL}"
// Run Maven on a Unix agent.
// 构建脚本默认在代码仓库根目录运行
// 如果你的pom.xml文件不在根目录,需要使用dir指令进入到对应的目录
// 或者使用mvn clean compile package -f ./source/pom.xml 指定pom文件位置
// 如果你的pom文件是在源代码的根目录,可以删除dir代码块包裹
dir("./renren-cloud-tenant/"){
sh """
echo \$(pwd)
mvn clean compile package
"""
}
}
}
stage("docker"){
steps{
// docker build 默认在命令行运行的根目录查找Dockerfile
// 如果你的Dockerfile不在根目录,需要使用dir指令进入到对应的目录
// 如果你的Dockerfile在根目录下,可以删除dir代码块包裹
dir('./renren-cloud-tenant/park-admin/park-admin-server/') {
// 使用docker凭据,credentialsId为你前面创建的docker仓库凭据
withCredentials([usernamePassword(credentialsId: "${DOCKER_IDENTITY}", passwordVariable: 'docker_password', usernameVariable: 'docker_username')]) {
sh """
echo \$(pwd)
docker login -u ${docker_username} -p ${docker_password} ${DOCKER_REGISTRY}
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${IMAGE_TAG}-${BUILD_NUMBER} .
docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${IMAGE_TAG}-${BUILD_NUMBER}
"""
}
}
}
}
}
}
点击运行的时候没有出现构造参数界面
在项目配置选项里面,勾选参数化,然后与Pipeline脚本中设置的环境变量对应上。
nodejs Pipeline
pipeline {
agent any
// 环境变量,全局可用
environment {
// GIT仓库地址
GIT_URL = "https://g.vpclub.cn/park/sdyk/front-web-admin.git"
// GIT 仓库凭证
GIT_IDENTITY = "donglietao-vpclub-sz-git"
// 项目名称,将生成docker镜像的名称的一部分
PROJECT_NAME ="nodejs-demo"
// docker仓库的分组名称
PROJECT_NS ="vp-whdev"
// docker仓库地址
DOCKER_REGISTRY ="swr.cn-south-1.myhuaweicloud.com"
// 这三个参数将组合成你的最终docker镜像名称为
// swr.cn-south-1.myhuaweicloud.com/vp-whdev/nodejs-demo
// DOCKER 仓库凭证
DOCKER_IDENTITY = "docker-huaweicloud"
// nodejs专用,nodejs内存溢出错误时候需要
NODE_OPTIONS = "--max-old-space-size=4096"
}
// 构建过程参数
parameters {
// 代码分支
choice(name:"BRANCH",choices:["development","release"],description:"代码分支")
// 镜像TAG,将被添加到docker镜像标签上
choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
// 这个参数将被组合到镜像的最终名称
// swr.cn-south-1.myhuaweicloud.com/vp-whdev/nodejs-demo:test
}
stages {
stage('Build') {
steps {
// Get some code from a gitlab
// 注意credentialsId参数,填入你前面创建的git仓库用户名密码
// 注意url参数,填入你的git仓库地址
git branch: "${BRANCH}", credentialsId: "${GIT_IDENTITY}", url: "${GIT_URL}"
// build
// 需要使用的nodejs版本,与全局设置的nodejs工具包名称对应
nodejs('node-18.20') {
sh """
echo \$(pwd)
node -v
npm -v
# 非必须,有时候npm install卡主
# npm cache clean --force
# rm -rf node_modules
# rm package-lock.json
# rm yarn.lock
npm add yarn --registry=https://registry.npmmirror.com
./node_modules/.bin/yarn -v
./node_modules/.bin/yarn install --registry https://registry.npmmirror.com
# 如果不使用yarn
# npm install --registry=https://registry.npmmirror.com
if [ "${IMAGE_TAG}" = "prod" ]; then
./node_modules/.bin/yarn run build:prod
elif [ "${IMAGE_TAG}" = "test" ]; then
./node_modules/.bin/yarn run build:test
else
echo "IMAGE_TAG is neither 'prod' nor 'test'. No build script executed."
fi
"""
}
}
}
stage("docker"){
steps{
// docker build 默认在命令行运行的根目录查找Dockerfile
// 如果你的Dockerfile不在根目录,需要使用dir指令进入到对应的目录
// 如果你的Dockerfile在根目录下,可以删除dir代码块包裹
withCredentials([usernamePassword(credentialsId: "${DOCKER_IDENTITY}", passwordVariable: 'docker_password', usernameVariable: 'docker_username')]) {
sh """
echo \$(pwd)
docker login -u ${docker_username} -p ${docker_password} ${DOCKER_REGISTRY}
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${IMAGE_TAG}-${BUILD_NUMBER} .
docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${IMAGE_TAG}-${BUILD_NUMBER}
"""
}
}
}
}
}
nodejs第二种方法,多阶段构建
由于某些依赖需要比较复杂的环境,例如gyp,早期的sass,需要很多依赖,导致整个jenkins环境配置异常复杂,这个时候可以使用docker的多阶段构建
FROM harbor.iovhm.com/hub/node:14.21.3 AS build
WORKDIR /data
COPY ./ /data
RUN cd /data && \
yarn install --registry=https://registry.npmmirror.com && \
yarn run build:prod && \
true
FROM harbor.iovhm.com/hub/nginx:1.14.2
COPY --from=build /data/dist/ /usr/share/nginx/html/management
- 注意第一个构建阶段的AS build,类似与给这个阶段取了个名字
- 注意第二个阶段的 --from=build,可以实现内容共享
相对于第一种方法,则只需要获取代码,打包全部有docker完成
达梦数据库
version: '3'
services:
park-talent-dm:
image: dm8:dm8_20240605_rev215128_x86_rh6_64
container_name: park-talent-dm8
restart: always
privileged: true
ports:
- 33066:5236
volumes:
- ./data:/opt/dmdbms/data
environment:
- LD_LIBRARY_PATH=/opt/dmdbms/bin
- PAGE_SIZE=16
- EXTENT_SIZE=32
- LOG_SIZE=1024
- UNICODE_FLAG=1
- LENGTH_IN_CHAR=1
- CASE_SENSITIVE=0
- INSTANCE_NAME=dm8_test
- BLANK_PAD_MODE=1
- SYSDBA_PWD=<ppassword>
- ADMIN_PWD=<password>
单机运行postgresql&& 安装kong-gateway
单机运行postgresql
version: '3'
services:
kong-postgres:
image: harbor.iovhm.com/hub/postgres:9.6.24
restart: always
container_name: kong-postgres
privileged: true
ports:
- 5432:5432
volumes:
- ./postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=<kong user>
- POSTGRES_DB=k<kong db>
- POSTGRES_PASSWORD=<kong password>
初始化数据库
docker run --rm \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=<PG HOST>" \
-e "KONG_PG_PORT=<PG PORT>" \
-e "KONG_PG_PASSWORD=<kong pass>" \
harbor.iovhm.com/hub/kong:3.0 kong migrations bootstrap
kafka集群搭建
单主机安装
version: '3'
services:
zookeeper:
image: harbor.iovhm.com/hub/wurstmeister/zookeeper:3.4.6
restart: always
hostname: zookeeper
container_name: zookeeper
ports:
- "2181:2181"
volumes:
- ./zookeeper/data:/data
- ./zookeeper/datalog:/datalog
kafka-1:
image: harbor.iovhm.com/hub/wurstmeister/kafka:2.13-2.8.1
restart: always
hostname: kafka-1
container_name: kafka-1
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.18.32.61:9092 # 消费者需要用此服务码链接,需要做服务发现或者修改为主机ip
KAFKA_LISTENERS: PLAINTEXT://:9092
KAFKA_CREATE_TOPICS: "topic.kafkapartition:compact"
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_BROKER_ID: 1
volumes:
- ./kafka-1/logs:/kafka/logs
kafka-2:
image: wurstmeister/kafka
restart: always
hostname: kafka-2
container_name: kafka-2
ports:
- "9093:9093"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.18.32.61:9093 # 消费者需要用此服务码链接,需要做服务发现或者修改为主机ip
KAFKA_LISTENERS: PLAINTEXT://:9093
KAFKA_CREATE_TOPICS: "topic.kafkapartition:compact"
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 # 集群方式
KAFKA_BROKER_ID: 2
volumes:
- ./kafka-2/logs:/kafka/logs
多主机安装
- 节点1
version: '3'
services:
zookeeper-1:
image: harbor.iovhm.com/hub/wurstmeister/zookeeper:3.4.6
restart: always
hostname: zookeeper-1
container_name: zookeeper-1
network_mode: host
ports:
- "2181:2181"
volumes:
- ./zookeeper/data:/data
- ./zookeeper/datalog:/datalog
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=172.29.1.81:2888:3888 server.2=172.29.1.82:2888:3888 server.3=172.29.1.83:2888:3888
kafka-1:
image: harbor.iovhm.com/hub/wurstmeister/kafka:2.13-2.8.1
restart: always
hostname: kafka-1
container_name: kafka-1
network_mode: host
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.29.1.81:9092
KAFKA_LISTENERS: PLAINTEXT://:9092
KAFKA_CREATE_TOPICS: "topic.kafkapartition:compact"
KAFKA_ZOOKEEPER_CONNECT: 172.29.1.81:2181,172.29.1.82:2181,172.29.1.83:2181
KAFKA_BROKER_ID: 1
volumes:
- ./kafka/logs:/kafka/logs
- 节点2
version: '3'
services:
zookeeper-2:
image: harbor.iovhm.com/hub/wurstmeister/zookeeper:3.4.6
restart: always
hostname: zookeeper-2
container_name: zookeeper-2
network_mode: host
ports:
- "2181:2181"
volumes:
- ./zookeeper/data:/data
- ./zookeeper/datalog:/datalog
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=172.29.1.81:2888:3888 server.2=172.29.1.82:2888:3888 server.3=172.29.1.83:2888:3888
kafka-2:
image: harbor.iovhm.com/hub/wurstmeister/kafka:2.13-2.8.1
restart: always
hostname: kafka-2
container_name: kafka-2
network_mode: host
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.29.1.82:9092
KAFKA_LISTENERS: PLAINTEXT://:9092
KAFKA_CREATE_TOPICS: "topic.kafkapartition:compact"
KAFKA_ZOOKEEPER_CONNECT: 172.29.1.81:2181,172.29.1.82:2181,172.29.1.83:2181
KAFKA_BROKER_ID: 2
volumes:
- ./kafka/logs:/kafka/logs
- 节点3
version: '3'
services:
zookeeper-3:
image: harbor.iovhm.com/hub/wurstmeister/zookeeper:3.4.6
restart: always
hostname: zookeeper-3
container_name: zookeeper-3
network_mode: host
ports:
- "2181:2181"
volumes:
- ./zookeeper/data:/data
- ./zookeeper/datalog:/datalog
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=172.29.1.81:2888:3888 server.2=172.29.1.82:2888:3888 server.3=172.29.1.83:2888:3888
kafka-3:
image: harbor.iovhm.com/hub/wurstmeister/kafka:2.13-2.8.1
restart: always
hostname: kafka-3
container_name: kafka-3
network_mode: host
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://172.29.1.83:9092
KAFKA_LISTENERS: PLAINTEXT://:9092
KAFKA_CREATE_TOPICS: "topic.kafkapartition:compact"
KAFKA_ZOOKEEPER_CONNECT: 172.29.1.81:2181,172.29.1.82:2181,172.29.1.83:2181
KAFKA_BROKER_ID: 3
volumes:
- ./kafka/logs:/kafka/logs
测试运行状态
# 进入到kafka工具集
/opt/kafka_2.12-2.3.0
# broker能够响应请求,通常意味着它正在运行
bin/kafka-broker-api-versions.sh --bootstrap-server kafka1:9092,kafka2:9092
# 查询消费组信息,能够响应请求并返回,通常意味着他正在运行
bin/kafka-consumer-groups.sh --bootstrap-server 172.18.32.61:9093 --list
otter&&数据库实时同步工具
常见问题(避坑指南)
-
无法访问管理页面或者重定向到内部IP了
请查看后面的文章
-
打开channel_list.html报错
方法一:访问:http://ip:port/system_reduction.htm,然后执行一键补全
方法二:重建同步配置
# 清空如下5个表后重新添加,因此,需要将将要同步的表配置用保存好,否则就傻逼了
# 已知问题,channel id不能从1开始
delete from data_media;
delete from data_media_pair;
delete from pipeline;
delete from pipeline_node_relation;
delete from channel;
# 重置自增 ID 非必须
ALTER TABLE `channel` AUTO_INCREMENT = 2;
ALTER TABLE `data_media` AUTO_INCREMENT = 1;
ALTER TABLE `data_media_pair` AUTO_INCREMENT = 1;
ALTER TABLE `pipeline` AUTO_INCREMENT = 1;
ALTER TABLE `pipeline_node_relation` AUTO_INCREMENT = 1;
-
otter启动不了
注意,数据库的node id必须为1,因为启动文件里面写了,否则启动不了
介绍
使用微服务架构进行服务拆分后,当某些服务需要用到基础数据的时候,往往粗暴的通过RPC调用,先到基础服务查询出数据,再把所有结果传入到相关服务进行例如in,like 查询。开发测试的时候因为数据量不够,通常没什么问题,当系统运行一段时间后,数据量增多后,就会出现性能问题。
诸如经常需要用到的基础数据,比较好的做法是进行数据表冗余。在同一个数据库内直接级联查询。但是,会将产生数据同步的问题。
使用otter进行数据同步
otter与canal都是阿里开源出来的数据同步工具,因为阿里巴巴业务的特殊性,卖家主要在国内,买家主要在国外,为了提升用户体验,衍生出了杭州机房和美国机房双机房,既使用工具将数据同步为一致的。
这不是重复造轮子
阿里的开源一贯作风是只管开源,不管维护,otter虽然好,但是BUG一堆,因此对安装部署脚本做了一些修改:
- 管理界面只能内网访问
- 服务一旦停止后就不能重新启动
因此重新打包了镜像,将一些参数移到外部进行启时候替换。
docker-compose方式如下,k8s方式可以参考相关配置。
重要:一定要挂载持久化数据,否则重启后同步就全部消失了
重要:这个软件的登录授权功能等于于无,因此,在设置OTTER_MANAGER_URL时,应该设置一个并不存在的域名后,然后使用host表来配置
推荐使用nginx-ingress注解认证或者nginx提供的basic认证,也可以使用IP限制
# 使用ip限制
nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.0.0/16,10.0.0.0/8"
# 使用basic认证
# 创建一个键值对(opaque)密钥,
# 键为 auth
# 值为 htpasswd -c auth username 生成
# 在线生成 https://www.bejson.com/encrypt/htpasswd/
# 为ingress增加注解
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
version: '3'
services:
otter-server:
image: swr.cn-south-1.myhuaweicloud.com/vp-public/otter-server:4.2.18
container_name: otter-server
restart: always
privileged: true
tty: true
ports:
- "8080:8080"
environment:
- OTTER_MANAGER_MYSQL=IP:PORT
- MYSQL_USER=user
- MYSQL_USER_PASSWORD=password
- MYSQL_DBNAME=otter
- OTTER_MANAGER_URL=https://domain:port # 不支持后面带路径,即使填了没用
volumes:
- ./zkData:/home/admin/zkData:rw # 重要,一定要挂载持久化数据,否则重启后同步就全部消失了。
或者你已经在使用otter,但是并不想更换镜像,则可以使用shell进入到容器进行相关修改
# vi manager/webapp/WEB-INF/common/uris.xml
# <serverURI>http://${otter.domainName}:${otter.port}/</serverURI>
# 修改上面的管理地址为你可以访问的管理地址
# /home/admin/manager/bin/stop.sh
# /home/admin/manager/bin/startup.sh
初始化数据库脚本
先用 /bin/bash 命令行启动容器
# 找到数据库初始化文件
cd /home/admin/bin
# 使用mysql链接到数据库并创建数据库
create database otter;
# 恢复数据
mysql -h127.0.0.1 -uroot -ppassword < ddl.sql
# 更新2个配置
update autokeeper_cluster set SERVER_LIST='["127.0.0.1:2181"]'
update node set ip = '127.0.0.1'
dbeaver&&mysql可视化web管理工具
官方参考
https://github.com/dbeaver/cloudbeaver/wiki/Run-Docker-Container
percona-toolkit
下载
https://www.percona.com/percona-toolkit
自建邮件服务器&&mailserver
端口检查
邮件系统需要使用到25、587、465、143、993 端口,请先检查端口是否被占用
- 25 SMTP 默认端口
- 587 SMTP 客户端发信端口
- 465 SMTP SSL 发信端口
- 143 IMAP 收信端口
- 993 IMAP SSL 收信端口
下图说明了需要的端口的关系
常见问题:25端口无法开启,多半是被系统内置的postfix服务占用了
# 关闭系统内置服务
systemctl disable postfix
使用docker-compose
version: "3"
services:
smtp-server:
image: harbor.iovhm.com/hub/mailserver/docker-mailserver:latest
container_name: mailserver
hostname: mail.iovhm.com
network_mode: host
ports:
- "25:25" # STMP
- "587:587" # STMP CLIENT
- "465:465" # STMP SSL
- "143:143" # IMAP
- "993:993" # IMAP SSL
volumes:
- ./data/mail-data:/var/mail
- ./data/mail-state:/var/mail-state
- ./data/mail-logs:/var/log/mail
- ./data/mail-config:/tmp/docker-mailserver
- /etc/localtime:/etc/localtime:ro
environment:
# 如果在不允许设置主机名的环境中运行,设置邮件服务器的主机名
- OVERRIDE_HOSTNAME=mail.iovhm.com
- SPOOF_PROTECTION=1
需要在120秒内增加第一个用户,否则系统自动退出
# 进入到容器后执行
docker exec -it mailserver /bin/bash
# 增加第一个用户
setup email add admin@iovhm.com <password>
# 增加2个约定的管理邮箱(可以设置自动转发到外部邮箱,也可以设置转发到本域邮箱)
setup alias add postmaste@iovhm.com your@163.com
# 没错,就是abuse,表示乱用举报邮箱
setup alias add abuse@iovhm.com your@163.com
# 查看用户列表
setup email list
setup alias list
# 生成 DKIM 密钥
setup config dkim
配置DNS记录
mail IN A 10.11.12.13
@ IN MX 10 mail.example.com
@ IN TXT "v=spf1 mx ~all"
_dmarc IN TXT "v=DMARC1; p=none; sp=none; adkim=r; aspf=r"
增加DNS
# 查看挂载目录生成的DKIM文本字符串
cat ./data/mail-config/opendkim/keys/iovhm.com
# 填入DNS,使之成为一行
mail._domainkey IN TXT "v=DKIM1; h=sha256; k=rsa;p=XXXXXXXXXXXXXXX"
DNS配置检查网站:https://www.helloinbox.email/
其他命令
# 进入到容器
docker exec -it mailserver /bin/bash
# 根据提示
setup help
# 修改密码
setup email update airdict@iovhm.com <new password>
25出站端口被封,使用中继
配置为中继后,则不能使用直接出站了
增加中继配置
# 增加中继说明,既所有来自*@iovhm.com的邮件使用smtp.163.com发送
setup relay add-domain iovhm.com smtp.163.com 465
# 增加中继账号
setup relay add-auth iovhm.com your@163.com <your password>
# 增加中继排除,既这个域使用默认中继
setup relay exclude-domain vpclub.cn
需要增加3个配置文件
- ./data/mail-config/postfix-main.cf
# 开启SMTP TLS
smtp_use_tls=yes
# SASL 认证配置
relayhost = [smtp.163.com]:465
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/tmp/docker-mailserver/postfix-sasl-password.cf
sender_canonical_maps = regexp:/tmp/docker-mailserver/sender_canonical.cf
smtp_sasl_security_options = noanonymous
smtp_sasl_type=cyrus
# TLS 配置
smtp_tls_security_level = encrypt
smtp_tls_loglevel = 1
smtp_tls_wrappermode = yes
# smtp_tls_CApath = /etc/ssl/certs
# smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
- ./data/mail-config/postfix-relaymap.cf
此处可以使用(强烈建议)内置的命令行增加
# 发向iovhm.com的邮件使用163中继
# 因为现在发邮件欺诈,几乎所有的中继邮箱都不允许修改发件人地址
# 收件方收到的邮件中发件人显示未中继邮箱
setup relay add-domain iovhm.com smtp.163.com 587
# 设置域使用默认中继发送
setup relay exclude-domain vpclub.cn
配置中继映射规则
@domain1.com [smtp.mailgun.org]:587
@domain2.com [smtp.sendgrid.net]:2525
@domain3.com
-
当发件人为 @domain1.com 时,使用 smtp.mailgun.org:587 中继发送
-
当发件人为 @domain2.com 时,使用 smtp.sendgrid.net:2525 中继发送
-
当发件人为 @domain3.com 时,不使用中继出站,但是会受默认RELAY_HOST影响
-
./data/mail-config/postfix-sasl-password.cf
# 中继邮箱密码凭据
[smtp.163.com]:465 airdict@163.com:<your password>
使用postmap postfix-sasl-password.cf 生成db
- ./data/mail-config/postfix-sender-canonical.cf
# 中继邮箱密码凭据规则
/.+/ your@163.com
使用postmap postfix-sender-canonical.cf 生成db
opensearch
介绍
OpenSearch 是 自 从 Elasticsearch OSS 7.10.2,有一段时间闭源后重新开的一个分支,完全开源。包括一些插件也是开源的
安装
version: '3'
services:
opensearch-node1:
image: harbor.iovhm.com/hub/opensearchproject/opensearch:2.19.1
container_name: opensearch-node1
privileged: true
restart: always
ports:
- 9200:9200
- 9300:9300
- 9600:9600
volumes:
- ./data1:/usr/share/opensearch/data
environment:
# - discovery.type=single-node # 单节点
- node.name=opensearch-node1 # 节点名称,唯一
# - network.host=0.0.0.0 # 节点IP
# - network.publish_host=172.16.2.106 # 发布地址
- cluster.name=opensearch-cluster # 集群名称
- discovery.seed_hosts=opensearch-node1,opensearch-node2 # 从指点主机发现节点
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # 集群节点成员
- bootstrap.memory_lock=true # 启用内存锁定
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # 设置JVM堆内存大小
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=EMWPpRJ7NkVW # 设置初始管理员密码,需要2.12 版本后支持
networks:
- opensearch-net
opensearch-node2:
image: harbor.iovhm.com/hub/opensearchproject/opensearch:2.19.1
container_name: opensearch-node2
privileged: true
restart: always
ports:
- 9201:9200
- 9301:9300
- 9601:9600
volumes:
- ./data2:/usr/share/opensearch/data
environment:
- node.name=opensearch-node2 # 节点名称,唯一
# - network.host=0.0.0.0 # 节点IP
# - network.publish_host=172.16.2.107 # 发布地址
- cluster.name=opensearch-cluster # 集群名称
- discovery.seed_hosts=opensearch-node1,opensearch-node2 # 从指点主机发现节点
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # 集群节点成员
- bootstrap.memory_lock=true # 启用内存锁定
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # 设置JVM堆内存大小
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=EMWPpRJ7NkVW # 设置初始管理员密码,需要2.12 版本后支持
networks:
- opensearch-net
opensearch-dashboards:
image: harbor.iovhm.com/hub/opensearchproject/opensearch-dashboards:2.19.1
container_name: opensearch-dashboards
restart: always
privileged: true
ports:
- 5601:5601
expose:
- "5601"
environment:
OPENSEARCH_HOSTS: '["https://opensearch-node1:9200","https://opensearch-node2:9201"]'
networks:
- opensearch-net
networks:
opensearch-net:
name: opensearch-net
external: true
# https://opensearch.org/docs/latest/getting-started/quickstart/
mockserver&&模拟测试工具
源代码下载
源代码下载:https://iovhm.com/book/attachments/15
安装
官方帮助:https://www.mock-server.com/
version: "3"
services:
mock-server:
image: mockserver/mockserver:mockserver-5.15.0
container_name: mockserver
restart: always
privileged: true
ports:
- "1080:1080"
environment:
- TZ=Asia/Shanghai
- MOCKSERVER_INITIALIZATION_JSON_PATH=/config/expectations.json # 系统启动时从配置文件加载期望
- MOCKSERVER_PERSISTED_EXPECTATIONS_PATH=/config/expectations.json # 持久化期望到配置文件
- MOCKSERVER_PERSIST_EXPECTATIONS=true # 开启期望配置持久化,默认情况是存放在内存的
volumes:
- ./config:/config
mock-dify:
image: harbor.iovhm.com/public/mock-dify:1.0.0
container_name: mock-dify
restart: always
privileged: true
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- DIFY_API_URL=https://youdify/v1/chat-messages
- DIFY_API_KEY=Bearer your-api-key
打开dashboard查看工作情况
host:port/mockserver/dashboard
创建一个期望
curl --request PUT \
--url https://smart-ops-mock.saas.vppark.cn/mockserver/expectation \
--data '{
"id":"/hello",
"httpRequest": {
"method": "GET",
"path": "/hello"
},
"httpResponse": {
"headers": {
"Content-Type": [
"application/json"
]
},
"statusCode": 200,
"body": {
"code": 200,
"message": "ok",
"data": {
"list": {
"a": "b",
"c": "d"
}
}
}
}
}'
触发一个期望
curl --request GET \
--url https://smart-ops-mock.saas.vppark.cn/hello
删除一个期望
curl --request PUT \
--url https://smart-ops-mock.saas.vppark.cn/mockserver/clear \
--data '{
"path": "/hello"
}'
创建一个期望,并转发到其他服务
curl --request PUT \
--url https://smart-ops-mock.saas.vppark.cn/mockserver/expectation \
--data '{
"id": "/baidu",
"httpRequest": {
"method": "POST",
"path": "/baidu"
},
"httpOverrideForwardedRequest": {
"httpRequest": {
"method": "GET",
"path": "/",
"headers": {
"Host": [
"www.baidu.com"
]
},
"secure": true
}
}
}}'
已经增加的期望列表
curl --request PUT \
--url 'https://smart-ops-mock.saas.vppark.cn/mockserver/retrieve?type=active_expectations'
系统状态
curl --request PUT \
--url https://smart-ops-mock.saas.vppark.cn/mockserver/status
重置一切
既整个系统添加的内容全部没有了
curl --request PUT \
--url https://smart-ops-mock.saas.vppark.cn/mockserver/reset
转发到dify(deepseek)
转发到其他服务的时候,想要动态修下游的请求body,需要使用模板语,模板语法里面只能填写字符串,特殊字符需要转义,很不方便,编写了一个JS脚本来完成。
mockserver模板介绍
https://www.mock-server.com/mock_server/response_templates.html#velocity_templates https://velocity.apache.org/tools/3.1/tools-summary.html#JsonTool
// 直接转发,请求参数不支持动态替换
var request_static = {
"id": "/api/visitor/list",
"httpRequest": {
"method": "GET",
"path": "/api/visitor/list"
},
"httpOverrideForwardedRequest": {
"requestOverride": {
"method": "POST",
"path": "/dify/v1/chat-messages",
"headers": {
"Host": [
"mock-dify:8080"
],
},
// "secure": true, // https访问下游
"body": {
"query": "数据结构为{\"personIds\": [\"d7228950bf29490484bc845dff756b95\"]},在原数据上增加开始时间和结束时间,将时间设置为明天,只输出纯数据内容,不添加任何解释、说明、格式标记、代码块或多余符号",
}
}
}
}
// 模板转发,请求参数动态替换,但是template写法很恶心,可以使用下面的方法进行转换
// 模板变量参考:https://www.mock-server.com/mock_server/response_templates.html#velocity_templates
// 模板变量参考:https://velocity.apache.org/tools/3.1/tools-summary.html#JsonTool
var request_dynamic = {
"id": "/api/visitor/list2",
"httpRequest": {
"method": "GET",
"path": "/api/visitor/list2"
},
"httpForwardTemplate": {
"templateType": "VELOCITY",
"template": "#set($jsonBody=$json.parse($!request.body))\n{\"method\":\"POST\",\"path\":\"/v1/chat-messages\",\"secure\":true,\"headers\":{\"Host\":[\"api-ai-agent.vppark.cn\"],\"Authorization\":[\"Bearer app-rWhiquJ6eMwsdCUQkaVJf9Dx\"]},\"body\":{\"inputs\":{},\"query\":\"数据结构为{\\\"personIds\\\": [\\\"d7228950bf29490484bc845dff756b95\\\"]},在原数据上增加开始时间和结束时间,当前时间为$!now_iso_8601,将时间设置为明天,只输出纯数据内容,不添加任何解释、说明、格式标记、代码块或多余符号\",\"response_mode\":\"blocking\",\"user\":\"abc-123\",\"username\":\"abc-123\",\"ext_replace\": \"$!request.body\"}}"
}
}
// 模板JSON写法
var dynamic_template = {
"method": "POST",
"path": "/dify/v1/chat-messages",
"headers": {
"Host": [
"mock-dify:8080"
],
},
// "secure": true, // https访问下游
"body": {
"query": "数据结构为{\"code\":0,\"msg\":\"success\",\"data\":{\"successes\":[{\"clientId\":\"$!jsonBody.clientId\",\"personId\":\"$!jsonBody.personId\"}]}},获取请求参数中的clientId、personId如果personId为空自动生成19位的id不允许重复",
}
}
// 转换为字符串
let formattedJson = JSON.stringify(dynamic_template);
formattedJson = formattedJson.replace(/\\/g, '\\\\')
formattedJson = formattedJson.replace(/"/g, '\\"')
console.log(formattedJson)
ninedata&&数据库工具
项目微服务化后,势必出现一些基础数据的冗余,常见方法有字段冗余、整表冗余。为降低难度,我们优选了整表冗余。在通过同步工具进行表同步。
常见的表同步工具有:
- maxwell: https://maxwells-daemon.io/
- canal : https://github.com/alibaba/canal
这两个工具原理都一样,把自己伪装成一个slave,订阅binlog日志,将更改推送到消息队列,包括了数据是如何变化的,例如增删改、原始数据、修改后的数据;开发者对更改订阅处理。需要多个组件、需要代码实现、适合复杂场景。例如数据库更改修改缓存。
第二个工具是,otter,基于canal,实现了修改订阅,直接进行库(表)对库(表)同步。简单易用,但是暂时不支持 mysql 8,且更新至2019年即没有在更新了。不过对于mysql 5.7的支持尚可,虽然有一些bug,基本能满足要求,一些常见问题也积累了相关经验,有源代码,进行一些改造也可以适应mysql 8。
otter相关文档
github:https://github.com/alibaba/otter
常见问题:https://iovhm.com/book/books/42e7a/page/otter
ninedata
因为otter偶尔会抽风,导致数据没有同步,需要一个数据比对工具,来保证需要同步的表的数据是一致的,ninedata提供的相关功能,基本能满足这个要求了,且提供了其他工具。社区版免费使用。
官方网址:https://www.ninedata.cloud/
核心功能:
- 查询分析器
- 慢sql分析
- 数据对比,主从不同步了也应该可以使用这个功能
- 数据复制(用于数据搬迁和数据归档)
- 数据库结构对比
社区版安装方法
只能在主机上安装,不能在k8s里面安装。
version: "3"
services:
ninedata:
image: harbor.iovhm.com/public/ninedata:20250618
container_name: ninedata
restart: always
privileged: true
ports:
- "9999:9999"
volumes:
- ./data:/u01
使用截图
安装完成后使用admin / admin 默认密码登陆
- 配置数据源
- 慢sql分析,需要先在mysql开启慢sql分析功能
# 临时有效
SET GLOBAL slow_query_log=1;
SET GLOBAL long_query_time=30;
SET GLOBAL log_output='TABLE';
# 开启慢查询日志
slow_query_log=1
# 慢查询日志文件路径
# slow_query_log_file=/var/log/mysql/mysql-slow.log
# 如果未设置,可以使用 SHOW VARIABLES LIKE 'slow_query_log_file'查看
# 记录查询执行时间超过30秒的查询
long_query_time=30
- 数据比对
- 数据比对明细
- 查看两者的差异
- 查看差异详情、生成SQL语句使差异一致(永远是以源为基准)
pull&&retag&&push
从源仓库拉取镜像,改名,推送,根据实际情况修改源地址和目标地址
#!/usr/bin/env python3
import subprocess
import sys
def pull_rename_push_image(image_name, target_repo):
# 拉取镜像
subprocess.check_call(["docker", "pull", image_name])
# 获取镜像ID
result = subprocess.Popen(
["docker", "images", "-q", image_name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
image_id = result.communicate()[0].strip()
# 生成新的镜像名称
new_image_name = "{}/{}".format(target_repo, image_name)
# 给镜像打标签
subprocess.check_call(["docker", "tag", image_id, new_image_name])
# 推送镜像到私有仓库
subprocess.check_call(["docker", "push", new_image_name])
print("镜像 {} 已成功推送到 {}".format(image_name, target_repo))
def main():
if len(sys.argv) != 2:
print("Usage: python pull.py <image_name>")
sys.exit(1)
image_name = sys.argv[1]
# 选择目标仓库
print("请选择目标仓库:")
print("1. harbor.iovhm.com/public")
print("2. swr.cn-south-1.myhuaweicloud.com/vp-public")
choice = input("请输入选项编号 (1 或 2): ")
if choice == "1":
target_repo = "harbor.iovhm.com/public"
elif choice == "2":
target_repo = "swr.cn-south-1.myhuaweicloud.com/vp-public"
else:
print("无效的选项")
sys.exit(1)
# 执行拉取、改名、推送操作
pull_rename_push_image(image_name, target_repo)
if __name__ == "__main__":
main()
# python3 pull.py nginx:latest
elastach-search常用命令
# 查看所有的索引
curl -u elastic:elastic http://localhost:9211/_cat/indices?v
# 删除索引
curl -u elastic:elastic -XDELETE 'http://localhost:9211/gas-*-2024.05*'
# 查看节点磁盘分配情况
curl -u elastic:elastic http://localhost:9211/_cat/allocation?v
阿里云oss&&ossfs
ossfs、oss、阿里云对象存储 、 对象存储
# 下载
wget https://gosspublic.alicdn.com/ossfs/ossfs2_2.0.6_linux_x86_64.rpm
# 安装
yum install ossfs2_2.0.6_linux_x86_64.rpm -y
# 版本验证
ossfs2 --version
# 配置
touch /etc/ossfs2.conf
# 挂载
ossfs2 mount /data/ossfs/iovhmcom -c /etc/ossfs2.conf
# 开机自动挂载
iovhmcom /data/ossfs/iovhmcom/ ossfs2 _netdev,nofail,x-systemd.after=networkd-dispatcher.service,conf=/root/ossfs2.conf 0 0
配置文件示例
更多配置请参考:https://help.aliyun.com/zh/oss/developer-reference/configure-ossfs-2-0
# Bucket所处Endpoint(地域节点)
--oss_endpoint=https://oss-cn-hangzhou-internal.aliyuncs.com
# Bucket名称
--oss_bucket=bucketName
# 访问密钥AccessKey ID和AccessKey Secret
--oss_access_key_id=AAAI************
--oss_access_key_secret=AAA8x*************************
# OSS文件目录路径(可选)
--oss_bucket_prefix=folder-test/folder-prefix/
windows 安装codex
安装nodejs
# 下载
# https://nodejs.org/zh-cn/download
安装
# 安装
npm install -g @openai/codex --registry=https://registry.npmmirror.com
配置 config.toml
文件路径 C:\Users\你的用户名\.codex\config.toml
model_provider = "custom"
model = "qwen3.7-plus"
disable_response_storage = true
network_access = "enabled"
[model_providers.custom]
name = "custom"
wire_api = "responses"
requires_openai_auth = true
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
配置 auth.json
文件路径 C:\Users\你的用户名\.codex\auth.json
{
"OPENAI_API_KEY": "sk-*************************8"
}
前端开发常见坑处理
各种环境需要的地址类的处理
- .env 文件
# 新建
# .env.development 、 .env.production 、 .env.test
# 主站API地址
VITE_APP_API=/api
# 文件服务地址
VITE_FILE_API=/oss/file
- 环境变量读取工具类
/**
* 获取环境变量
*/
export const getEnvByKey = (key: string) => {
const val = window.SITE_CONFIG?.[key] || import.meta.env[key];
if (val) {
return val;
} else {
console.warn(SITE_CONFIG);
throw new Error("未找到环境变量");
}
};
- nginx 运时时外部修改
#!/bin/bash
cat > /usr/share/nginx/html/env.js <<EOF
window.SITE_CONFIG = {
VITE_APP_API: "${VITE_APP_API:-/api}",
VITE_FILE_API: "${VITE_FILE_API:-https://smart.saas.vppark.cn/file}",
VITE_VISITOR_URL: "${VITE_VISITOR_URL:-https://smart-home.saas.vppark.cn/h5}"
};
EOF
cp -r /tmp/icon/* /home/upload_file
nginx -g 'daemon off;'
- index.html
<script src="./env.js"></script>
<script>
//全局钩子
window.SITE_CONFIG = {
//api
apiURL: "<%=apiURL%>",
...window.SITE_CONFIG
};
</script>
内网图片、协议不一致处理
https不能调用http,后端返回的内网图片不能正常显示,开发环境https证书错误无法加载
1、修改代码处理图片路径走内网代理
/** 内网 / 第三方图片走同源反代,避免混合内容与跨域问题 */
export const INTRANET_IMAGE_PROXY_PREFIX = "/intranet-image-proxy";
/**
* 与 nginx default.conf 的 intranet-image-proxy location 正则保持一致:
* host 必须是 10.x.x.x / 172.x.x.x / 192.x.x.x(可带端口)。
* nginx 只放行这三段,这里不匹配就走原地址,避免拼出 nginx 404 的代理路径。
*/
const INTRANET_HOST_PATTERN = /^(?:10|172|192)\.\d+\.\d+\.\d+(?::\d+)?$/;
/**
* 内网 IP 的 http(s) 全路径图片包一层 /intranet-image-proxy/;
* 其余(空值、相对路径、外网地址)原样返回。
*/
export function toInternetImage(src: string) {
// 空、null、undefined、纯空白:返回空串
const raw = String(src ?? "").trim();
if (!raw) {
return raw;
}
// 协议相对地址(//10.1.2.3:6120/a.jpg)补全 http:
let url = raw;
if (url.startsWith("//")) {
url = `http:${url}`;
}
// 相对路径或其他协议不是全路径,不处理
if (!/^https?:\/\//i.test(url)) {
return raw;
}
// host 不是 10/172/192 段的内网 IP 不处理,规则与 nginx 放行一致
const host = new URL(url).host;
if (!INTRANET_HOST_PATTERN.test(host)) {
return raw;
}
// 内网全路径:包一层同源代理,规避混合内容拦截
return `${location.origin}${INTRANET_IMAGE_PROXY_PREFIX}/${url}`;
}
2、增加nginx图片代理配置
# 顶层区块
map $http_x_forwarded_proto $origin_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
# 内网代理目标地址:从原始请求行($request_uri 是客户端发来的原文)提取,
# 内嵌地址 http://10.x... 里的 // 不受归一化影响;白名单(10/172/192 段)由这里的正则把守,
# 未命中时变量为空,location 里显式 404
map $request_uri $intranet_target {
~^/intranet-image-proxy/(https?://(?:10|172|192)\.\d+\.\d+\.\d+(?::\d+)?/.*)$ $1;
}
# 内网代理目标 host(proxy_set_header Host 用,与目标地址分开抓)
map $request_uri $intranet_target_host {
~^/intranet-image-proxy/https?://((?:10|172|192)\.\d+\.\d+\.\d+(?::\d+)?)/ $1;
}
# location 区块
# 内网图片代理(设备抓拍等内网 http/https 图片统一走这里)
# 归一化会把 URI 里内嵌地址 http://10.x... 的 // 合并掉,所以 location 只做前缀匹配,
# 目标地址从 $request_uri 原文经 map 提取,proxy_pass 变量式原样转发,// 不被动过
location /intranet-image-proxy/ {
# 未命中白名单(map 没抓到目标地址)直接 404
if ($intranet_target = "") { return 404; }
proxy_pass $intranet_target;
proxy_set_header Host $intranet_target_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_verify off;
# 处理上游返回的重定向,避免跳转到内网地址;改写出的地址同样带内嵌 //,
# 浏览器跟随重定向的二次请求仍走上面的 map 提取,链路闭环
proxy_redirect ~^(?<redirect_scheme>https?)://(?<redirect_host>(?:10|172|192)\.\d+\.\d+\.\d+(?::\d+)?)(?<redirect_path>/.*)$ $origin_scheme://$http_host/intranet-image-proxy/$redirect_scheme://$redirect_host$redirect_path;
}
新页面
/plugin install frontend-design@claude-plugins-official























