# 知识库

# 科学上网

#### 参考网站

- github:[https://github.com/shadowsocks/go-shadowsocks2?tab=readme-ov-file](https://github.com/shadowsocks/go-shadowsocks2?tab=readme-ov-file)
- 官方帮助:[https://pkg.go.dev/github.com/shadowsocks/go-shadowsocks2#readme-install](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://github.com/netchx/netch/releases)
- 加速下载：[https://iovhm.com/uploads/software/tools/Netch.zip](https://iovhm.com/uploads/software/tools/Netch.zip)

**增加 shadowsocks服务器配置**

[![](https://iovhm.com/book/uploads/images/gallery/2026-04/scaled-1680-/FZRXb6uLijQsyZeH-image-1775182451414.png)](https://iovhm.com/book/uploads/images/gallery/2026-04/FZRXb6uLijQsyZeH-image-1775182451414.png)

**选择模式**

[![](https://iovhm.com/book/uploads/images/gallery/2026-04/scaled-1680-/oQouGWW40A2umLCC-image-1775182797927.png)](https://iovhm.com/book/uploads/images/gallery/2026-04/oQouGWW40A2umLCC-image-1775182797927.png)

**设置dns也代理**

[![](https://iovhm.com/book/uploads/images/gallery/2026-04/scaled-1680-/TJIUuTbnroYaejdJ-image-1775182924578.png)](https://iovhm.com/book/uploads/images/gallery/2026-04/TJIUuTbnroYaejdJ-image-1775182924578.png)

[![](https://iovhm.com/book/uploads/images/gallery/2026-04/scaled-1680-/9cnuN2Z2qaXAmgpo-image-1775182837013.png)](https://iovhm.com/book/uploads/images/gallery/2026-04/9cnuN2Z2qaXAmgpo-image-1775182837013.png)

**点击启动，启动成功后，会多出一个aicloud的虚拟网卡**

**也兼容socks5插件方式**

[![](https://iovhm.com/book/uploads/images/gallery/2026-04/scaled-1680-/mMpefC0S1loCib0M-image-1775183532023.png)](https://iovhm.com/book/uploads/images/gallery/2026-04/mMpefC0S1loCib0M-image-1775183532023.png)

---

- #### 客户端配置(socks5浏览器插件方式)

---

- **客户端下载**

windows 客户端下载: [https://iovhm.com/uploads/shadowsocks2/shadowsocks-4.4.1.0.zip](https://iovhm.com/book/attachments/3)

解压缩后，双击小火箭图标，会在任务栏出现小图标，右键任务栏小图标，填入配置信息

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/zC4e6oATJhURSUZZ-image-1691682136470.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/zC4e6oATJhURSUZZ-image-1691682136470.png)

- **填入服务器配置**

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/KIlDmFJ7JWvkJEbs-image-1691681748196.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/KIlDmFJ7JWvkJEbs-image-1691681748196.png)

- **浏览器插件下载**

[https://iovhm.com/uploads/shadowsocks2/extension\_2\_5\_21\_0.crx](https://iovhm.com/uploads/shadowsocks2/extension_2_5_21_0.crx)

- **安装浏览器扩展**

浏览器地址输入 chrome://extensions/ 或者找到扩展工具设置，将 **extension\_2\_5\_21\_0.crx** 拖到窗口进行安装。

- **让扩展程序显示在地址栏**

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/gZ5ZZ1WoxDKMUrLq-image-1691682632677.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/gZ5ZZ1WoxDKMUrLq-image-1691682632677.png)

- **设置浏览器扩展**

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/kJywxZahJ1DiaOAf-image-1691682671245.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/kJywxZahJ1DiaOAf-image-1691682671245.png)

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/mkT6kUa9KG8skTtT-image-1691682688044.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/mkT6kUa9KG8skTtT-image-1691682688044.png)

- 代理协议：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

```

- **选择浏览器使用代理方式连接到网络**

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/c3YpoyuMqalL27DQ-image-1691682858284.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/c3YpoyuMqalL27DQ-image-1691682858284.png)

- #### 注意事项

系统代理选择禁用，**不要选择PAC模式和全局模式**，此2个模式会将所有的网络流量全部走代理,浪费代理资源。比较科学的办法是将插件安装在edge浏览器上用于科学上网；正常访问使用chrome浏览器，便捷不用切来切去。

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/HDY4YgxFKU7Z6mok-image-1691683057918.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/HDY4YgxFKU7Z6mok-image-1691683057918.png)

#### linux使用方法

```shell
./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](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://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/97ycr8aAqAZ7LU2g-image-1693483356434.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/97ycr8aAqAZ7LU2g-image-1693483356434.png)

#### 安装和使用

参考网址：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](https://github.com/acmesh-official/acme.sh/wiki/dnsapi)

使用阿里云dns api时，需要先获取到Ali\_Key和Ali\_Secret,获取方法为

- 进入阿里云账号，点击 **AccessKey管理**[![](https://iovhm.com/book/uploads/images/gallery/2024-08/scaled-1680-/DpUL0DFhvQKnn7C3-image-1723436230975.png)](https://iovhm.com/book/uploads/images/gallery/2024-08/DpUL0DFhvQKnn7C3-image-1723436230975.png)
- 获取和创建你的api key

[![](https://iovhm.com/book/uploads/images/gallery/2024-08/scaled-1680-/x5OU5geXdLOZ4oK8-image-1723436364752.png)](https://iovhm.com/book/uploads/images/gallery/2024-08/x5OU5geXdLOZ4oK8-image-1723436364752.png)

```shell

# 参考网站：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

```

1. 将bind 127.0.0.1改为bind 0.0.0.0
2. protected-mode yes改为 protected no
3. logfile "" 改为 logfile "/var/log/redis.log"（方便日后排查redis故障）
4. databases 16 （可以根据需求该需要的数据库数量）
5. 如果使用RDB做持久化，可以修改下面的配置 save 900 1 save 300 10 save 60 10000
6. dbfilename dump.rdb指定rdb在保存的文件名（可以根据需要修改）
7. dir /data 指定dump.rdb存放的目录，需要与挂载目录保持一致。保证数据能正常持久化到硬盘中
8. 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

```

1. 在主节点的基础上加入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

```

1. logfile "" 改为 logfile "/var/log/redis-sentinel.log"（方便日后排查故障）
2. sentinel announce-ip 172.18.41.8 （用来告诉其他哨兵我的地址是多少）
3. sentinel announce-port 46379 (用来告诉其他哨兵我的端口是多少)
4. sentinel monitor mymaster 172.18.41.8 36379 2（配置监控的redis 名字、master的地址、端口、quorum） quorm计算公式：quorum = (n / 2) + 1 （n表示主机数量）
5. sentinel auth-pass mymaster 密码 配置主监控的认证密码

# elasticsearch

```yaml

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    



```

#### 测试服务状态

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

```

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

```

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

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

```CONF
# 增加到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经常被扫描的几个安全设置

```conf
# 增加到 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](http://qq829.cn/book/books/42e7a/page/traefik-gateway-dashboard)**

traefik 是一个优秀的反向代理软件，提供与nginx类似的功能。

与nginx对比，其优势在于，nginx需要编写配置文件后，重新启动nginx以生效。nginx不支持tcp代理（使用插件可以支持）

<table id="bkmrk-%E7%89%B9%E6%80%A7-nginx-traefik-%E5%8A%A8%E6%80%81%E9%85%8D"><thead><tr><th align="center">特性</th><th align="center">nginx</th><th align="center">traefik</th></tr></thead><tbody><tr><td align="center">动态配置</td><td align="center">不支持动态配置</td><td align="center">外部文件、redis、json、etcd</td></tr><tr><td align="center">修改配置重启</td><td align="center">需要重启</td><td align="center">不需要重启</td></tr><tr><td align="center">tcp代理</td><td align="center">不支持（需要重新编译源码和插件）</td><td align="center">支持</td></tr><tr><td align="center">web容器</td><td align="center">支持</td><td align="center">不支持</td></tr><tr><td align="center">反向代理自动携带HOST</td><td align="center">支持</td><td align="center">不支持，需要使用中间件</td></tr><tr><td align="center"></td><td align="center"></td><td align="center"></td></tr></tbody></table>

利用此traefik的一些特性，可以将其当做入口网关使用

### 官方网站

- \[官方网站\]：https://doc.traefik.io/traefik/

### 主要概念

- entryPoints： 入口点，监听地址，支持http、https、tcp、udp
- routers：路由（路径）
- services：后端服务
- middlewares：中间件，在执行反向代理前、后可以执行一些操作 [插件参考网址](https://doc.traefik.io/traefik/middlewares/http/overview/)
- 静态配置文件：traefik启动时需要的配置，入口点，服务发现驱动等
- 动态配置文件：路由、服务、中间件、ssl证书

### 静态配置示例

```yaml
# 静态配置

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"



```

### 动态配置示例

```yaml
# 动态配置
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配置文件

```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/](https://www.openssl.org/source/)

```shell

# 下载源代码
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 库，需要建立共享库搜寻路径

```shell

# 创建共享库搜索路径
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/](https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/)

```shell

# 查看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升级

```shell

# 查看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"

```shell

# 修改配置文件，是ssh，不是sshd
vi /etc/ssh/ssh_config

# 注释本行
# GSSAPIAuthentication yes


```

# traefik-gateway-dashboard

**本文为dashboard用法，基础用法请参考：[traefik-gateway](http://qq829.cn/book/books/42e7a/page/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-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
    > > - 增加服务监控
- **<s>dashboard面板</s>**
    
    
    - <s>swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/traefik-service-discovery-dashboard-front:stage</s>
    
    > <s>更新日志：2023-06-21</s>
    > 
    > > - <s>dashboard面板已经集成到动态服务发现,无需部署</s>

### 安装部署说明

- 部署: 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](https://iovhm.com/book/attachments/14)

- <s>traefik-service-discovery-dashboard-fron</s>

```shell

# 2023-06-21,本服务已经集成到traefik-service-discovery，不需要部署
# 端口开放:80
# 访问路径/front/


```

- 负载均衡配置

<table id="bkmrk-%E8%B7%AF%E5%BE%84-%E6%9C%8D%E5%8A%A1-%E8%AF%B4%E6%98%8E-%E5%A4%87%E6%B3%A8-%2F-traefi"><thead><tr><th>路径</th><th>服务</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>/</td><td>traefik</td><td>网站根目录</td><td>必须</td></tr><tr><td>/traefik-dashboard/</td><td>traefik</td><td>traefik内置的dashboard</td><td>非必须，如果没有配置网站根目录则需要</td></tr><tr><td>/traefikconfig-dashboard/</td><td>traefik</td><td>traefik动态路由dashboard</td><td>非必须，如果没有配置网站根目录则需要</td></tr><tr><td>/traefik-service-discovery/</td><td>traefik</td><td>动态路由api</td><td>非必须，如果没有配置网站根目录则需要</td></tr></tbody></table>

### 首页不是从根目录开始

如果你的首页不是从根目录开始，则需要做一些单独的配置，实现原理为，将dashboard需要用到的3个路径处理为所需要的目录。

复制下面的配置文件，挂载到traefik-gatewy的容器路径

```yaml

# 使用k8s集群时，建议使用配置映射卷

./index-not-root.yaml:/etc/traefik/conf.d/conf2/index-not-root.yaml


```

**需要在负载均衡加入配置**

<table id="bkmrk-%E8%B7%AF%E5%BE%84-%E6%9C%8D%E5%8A%A1-%E8%AF%B4%E6%98%8E-%E5%A4%87%E6%B3%A8-%2Fminio%2F-"><thead><tr><th>路径</th><th>服务</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>/minio/</td><td>traefik</td><td>业务路由入口</td><td>必须</td></tr><tr><td>/\_minio/</td><td>traefik</td><td>面板路由入口</td><td>如果配置了下面三个路径，则是非必须</td></tr><tr><td>/\_minio/traefik-dashboard/</td><td>traefik</td><td>traefik内置的dashboard</td><td>非必须，如果没有配置面板入口则需要</td></tr><tr><td>/\_minio/traefikconfig-dashboard/</td><td>traefik</td><td>动态路由dashboard</td><td>非必须，如果没有配置面板入口则需要</td></tr><tr><td>/\_minio/traefik-service-discovery/</td><td>traefik</td><td>动态路由api</td><td>非必须，如果没有配置面板入口则需要</td></tr></tbody></table>

```yaml

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/"




```

### 插件使用

[插件参考网址](https://doc.traefik.io/traefik/middlewares/http/overview/)

```yaml

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决定的，而不是这里的字符串的问题，需要注意。

需要转义的字符有：

- `\` 转义为 `\\`
- `"` 转义为`\"`
- `()` 转义为 `\\(\\)`

```yaml

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

```shell


# 保存位置
# /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 一键修改服务器密码脚本

# 脚本安装

1. 执行安装脚本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工具

2. 配置inithost,第一次连接服务器的用户名和密码 例：

```
hosts:
  #192.168.40.36:
  #  ansible_user: root
  #  ansible_password: 123456
  #192.168.40.37:
  #  ansible_user: root
  #  ansible_password: 123456

```

3. 配置ansible hosts文件 例：

```
#[test]
#192.168.40.36
#
#[test1]
#192.168.40.37

```

4.生成初始连接凭据

```
bash ./inithost.sh

```

注：初始文件生成后，可以删除inithost以保证安全

### 常见操作

1. **修改单个服务器密码**

```bash
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=192.168.40.36 user=root password=123456" changepasswd.yaml

```

2. **修改ansible组密码**

```bash
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=test user=root password=123456" changepasswd.yaml

```

3. **修改hosts文件里所有服务器的密码**

```bash
ansible-playbook -i hosts --vault-password-file=.vault_pass -e "target_hosts=all user=root password=123456" changepasswd.yaml

```

4. **修改其他用户密码**

```bash
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

#### 从源代码编译

```shell
# 设置环境变量，指定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制作自己的容器

##### 镜像地址

```shell

harbor.iovhm.com/public/busybox:latest


```

##### Dockerfile

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

```shell

#!/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客户不可用

[![](https://iovhm.com/book/uploads/images/gallery/2025-05/scaled-1680-/P4nWjoCsVxQTwdUy-image-1747401380798.png)](https://iovhm.com/book/uploads/images/gallery/2025-05/P4nWjoCsVxQTwdUy-image-1747401380798.png)

**增加不从服务器获取路由route-nopull**

2025年5月16日，已经在命令增加了 **route-nopull** ，不需要修改配置文件了

[![](https://iovhm.com/book/uploads/images/gallery/2025-05/scaled-1680-/fgfASgrK2XG6WF6Y-image-1747401587111.png)](https://iovhm.com/book/uploads/images/gallery/2025-05/fgfASgrK2XG6WF6Y-image-1747401587111.png)

```
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**

[![](https://iovhm.com/book/uploads/images/gallery/2025-04/scaled-1680-/PBMnftYcV9mbpUcH-image-1745893639413.png)](https://iovhm.com/book/uploads/images/gallery/2025-04/PBMnftYcV9mbpUcH-image-1745893639413.png)

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

[![](https://iovhm.com/book/uploads/images/gallery/2025-04/scaled-1680-/Jp7NR8ynOMiBsqwf-image-1745894155647.png)](https://iovhm.com/book/uploads/images/gallery/2025-04/Jp7NR8ynOMiBsqwf-image-1745894155647.png)

- VPN客户端秘钥文件：**ovpn\_configuration\_file.ovpn**

可以到pritunl中下载后用记事本打开后粘贴进去

[![](https://iovhm.com/book/uploads/images/gallery/2025-04/scaled-1680-/PBaWSCvoMSS3goOp-image-1745893923387.png)](https://iovhm.com/book/uploads/images/gallery/2025-04/PBaWSCvoMSS3goOp-image-1745893923387.png)

##### 对configmap进行挂载，挂载路径为/data/vpclub

[![](https://iovhm.com/book/uploads/images/gallery/2025-04/scaled-1680-/iK9PHF4VzgtCqp2N-image-1745893857316.png)](https://iovhm.com/book/uploads/images/gallery/2025-04/iK9PHF4VzgtCqp2N-image-1745893857316.png)

[![](https://iovhm.com/book/uploads/images/gallery/2025-04/scaled-1680-/zmHR3DSvNv8kaM6S-image-1745893806208.png)](https://iovhm.com/book/uploads/images/gallery/2025-04/zmHR3DSvNv8kaM6S-image-1745893806208.png)

# datart通过自定义视图查询数据源数据

#### 一、 功能概述

该功能可以实现动态注册数据源，自定义SQL就可以通过统一接口查询数据。减少开发工作量，提高效率。

#### 二、注册数据源

注册数据源如下图。 [![微信图片_20231207143758.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/5Bh4Fx0bjyhPSf9m-20231207143758.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/5Bh4Fx0bjyhPSf9m-20231207143758.png)

注意：**连接地址，一定要设置utf8编码，不然中文查询不出来**，示例： jdbc:mysql://121.37.7.167:33314/数据库名?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai&amp;nullCatalogMeansCurrent=true

#### 三、添加数据视图

数据视图-新建数据视图-SQL 视图，进入编写要查询数据库的SQL [![微信图片_20231207144517.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/ECRgXRCI2H7dcrkH-20231207144517.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/ECRgXRCI2H7dcrkH-20231207144517.png)

1.先选择数据源

2.编写查询SQL

3.执行SQL

4.保存视图

5.将数据视图关掉，重新进入，地址栏可以看到视图id [![微信图片_20231207153849.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/tZ7SPb2UE81vryEb-20231207153849.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/tZ7SPb2UE81vryEb-20231207153849.png)

#### 四、调用接口查询数据

##### 1.获取TOKEN

（1）接口地址：\[POST\] https://{url}/api/v1/users/login

（2）Body 请求参数:

```json
{
"username": "wangbo", //用户名
"password": "admin@2023!" //密码
}

```

（3）响应头Authorization中获取token [![微信图片_20231207160706.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/jMdxmZYfRxRFCKWd-20231207160706.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/jMdxmZYfRxRFCKWd-20231207160706.png)

##### 2.获取视图数据

(1)接口地址: \[POST\] https://{url}/api/v1/data-provider/view

(2)Header 请求参数

<table id="bkmrk-%E5%8F%82%E6%95%B0%E5%90%8D-%E5%8F%82%E6%95%B0%E5%80%BC-%E6%98%AF%E5%90%A6%E5%BF%85%E5%A1%AB-%E5%8F%82%E6%95%B0%E7%B1%BB%E5%9E%8B-%E5%8F%82%E6%95%B0"><thead><tr><th>参数名</th><th>参数值</th><th>是否必填</th><th>参数类型</th><th>参数说明</th></tr></thead><tbody><tr><td>Authorization</td><td>Bearer eyJhbGciOiJIUzI1xxxxx</td><td>是</td><td>string</td><td>token</td></tr></tbody></table>

(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  //是否成功
}

```

#### 五、自定义视图变量

(1)添加变量 [![微信图片_20231207163845.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/zjMEckj6hjwQXoo5-20231207163845.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/zjMEckj6hjwQXoo5-20231207163845.png)

（2）变量使用，变量格式：$变量名称$，使用如下:

```sql
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

```

#### 相关配置

```yaml
    volumes:
      - ./config:/config # 需要备份的数据库链接信息
      - ./data:/data #备份文件保存目录


```

#### 备份文件配置信息

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

```

#### 直接运行

```shell
#!/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 **创建服务**

[![](https://iovhm.com/book/uploads/images/gallery/2024-07/scaled-1680-/H95aABiTxgXEYrmJ-image-1720855399570.png)](https://iovhm.com/book/uploads/images/gallery/2024-07/H95aABiTxgXEYrmJ-image-1720855399570.png)

- 2 **挂载数据**

[![](https://iovhm.com/book/uploads/images/gallery/2024-07/scaled-1680-/U5NRljK1wRmNnsoF-image-1720855503074.png)](https://iovhm.com/book/uploads/images/gallery/2024-07/U5NRljK1wRmNnsoF-image-1720855503074.png)

# MYSQL维护指南

#### 1. **监控数据库性能**

##### 1.1. **主机监控**

定期监控和分析数据库的性能指标，包括查询速度、连接数、CPU和内存使用情况等。这有助于识别性能瓶颈，并采取相应的优化措施。

常用命令：**top** 、 **free -h** 、 **df -h** 检查mysql主机的性能，可用资源应该冗余30%以上。

##### 1.2. **状态监控**

```mysql

-- 这条命令用于查询当前有多少客户端线程连接到数据库。
-- 这个数字可以帮助你了解数据库的负载情况，如果连接数异常高，可能意味着你的数据库正面临过高的请求负载或者连接泄露问题
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）**

```mysql

-- 检查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. **分析慢日志**

分析查询日志和慢查询日志，找出执行时间长、效率低下的查询语句，并进行优化。

```conf

# 开启慢查询日志
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


```

```mysql

-- 查询是否开启慢日志
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
-- 备份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. 设置强密码与密码密码过期策略

为用户和数据库设置复杂且不易猜测的密码，并定期更换密码。设置密码过期策略

```mysql
-- 全局的密码生命周期
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服务，使配置生效。

```cnf

# 启用二进制日志
log-bin=master-binlog

# 二进制日志格式
binlog-format=row

#设置服务器ID，确保每个服务器的ID唯一。
server-id=1

# 数据库名（可选）：
# binlog-do-db=指定需要复制的数据库。

# 设置中继日志
relay-log=relay-bin


```

#### 创建复制用户

创建复制用户：在主服务器上创建一个复制用户，并授权复制权限

```sql

CREATE USER 'replicator'@'%' IDENTIFIED BY 'password';  
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';  
FLUSH PRIVILEGES;


```

#### 从服务器配置

修改配置文件，编辑从服务器的MySQL配置文件，设置完成后重启MySQL服务

```cnf


# 确保服务器ID与主服务器不同
# 其他相关配置可根据需要设置
server-id=2

# 启用二进制日志
log-bin=master-binlog

# 二进制日志格式
binlog-format=row

# 设置中继日志
relay-log=relay-bin

# 设置从库为指读模式
# read-only=1


```

#### 查看主服务二进制日志状态

在主服务器上执行以下命令，记录二进制日志文件名和位置，这些信息将用于从服务器的配置

```sql
SHOW MASTER STATUS;


```

#### 配置从服务器的主复制配置信息

在从服务器上将执行，将配置复制指向主服务器

```sql
CHANGE MASTER TO  
MASTER_HOST='10.64.100.91',  
MASTER_USER='replicator',  
MASTER_PASSWORD='password',  
MASTER_LOG_FILE='binlog.000009',  
MASTER_LOG_POS=154;

```

```sql
# 启动复制
START SLAVE;

# 验证复制状态：
SHOW SLAVE STATUS\G;

# 主要是2个

Slave_I0 Running，YES
Slave SqL Rurning，YES


```

# 不重新打包修改nginx-web镜像写死的API地址

由于各种原因前端的API地址被写死了，而临时需要修改地址，打包发布麻烦,不知道哪个版本的代码对应这个版本，可以这样做。

#### nginx.conf中写死的服务发现

- 方法一，使用主机别名，类似hosts功能

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/TuqseE0Eyb0mPVUv-image-1734747602297.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/TuqseE0Eyb0mPVUv-image-1734747602297.png)

- 方法二，使用下面的字符串替换

#### 页面中写死的API地址

- **编写一段API替换脚本和nginx启动脚本**

**注意第一句的#!/bin/bash , 根据镜像不同，可能是#!/bin/sh**

```shell
#!/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 请自行百度

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/7aLrlKhkog4WnKj3-image-1734733921783.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/7aLrlKhkog4WnKj3-image-1734733921783.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/itsb3yTwEgpLIqjQ-image-1734733960919.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/itsb3yTwEgpLIqjQ-image-1734733960919.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/YjEME16QwY115Kcx-image-1734734124040.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/YjEME16QwY115Kcx-image-1734734124040.png)

- **将容器的启动命令设置为自己的启动脚本**

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/atbTXHc9ztaC2iEH-image-1734734175536.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/atbTXHc9ztaC2iEH-image-1734734175536.png)

# 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安装部署

```yaml
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

- **增加组织**

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/La8rrKE7uLMZRpFQ-image-1698204255989.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/La8rrKE7uLMZRpFQ-image-1698204255989.png)

- **增加用户**

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/WjCtNUtS5bLpzLho-image-1698204273739.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/WjCtNUtS5bLpzLho-image-1698204273739.png)

- **增加服务**

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/3jJ0pF58vI4sJ1z4-image-1698204308032.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/3jJ0pF58vI4sJ1z4-image-1698204308032.png)

- **附加到组织**
- **启动服务**
- **路由配置**

增加服务时候，会自动增加一条路由**0.0.0.0/0**，该路由会接管所有的流量。应该将改路由删除，增加需要通过 vpn client访问的内网主机路由。例如要访问内网的**192.168.0.0/24**

- **windows客户端**

下载地址：https://client.pritunl.com/#install

- **获取用户配置文件**

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/G8hY4w3nJPf8irt3-image-1698204458714.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/G8hY4w3nJPf8irt3-image-1698204458714.png)

- **导入配置文件**

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/r0DnbcCrKerUkNF9-image-1698204487197.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/r0DnbcCrKerUkNF9-image-1698204487197.png)

- **linux客户端**

```shell

yum install openvpn

# 将下载的配置文件解压，既可得到ovpn配置文件
openvpn --config your.ovpn


```

```shell
# 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 访问远端网络，但是不能通过远端网络访问本地地址。

- 访问方式一，远端用户可以访问服务器所在的局域网

[![](https://iovhm.com/book/uploads/images/gallery/2023-10/scaled-1680-/CIvRDnSi2HEN2dKF-image-1698315086879.png)](https://iovhm.com/book/uploads/images/gallery/2023-10/CIvRDnSi2HEN2dKF-image-1698315086879.png)

- 访问方式二

# 在无GUI界面的Linux服务器上执行jmeter

#### 准备工作

首先在有GUI的电脑，例如windows上将测试计划参数配置好，先使用一个并发运行一次，保证测试计划可以成功运行，将测试计划保存为**jmx**

#### 在服务器上运行

**jmeter**是使用java编写的，需要安装jdk，将**apache-jmeter-5.6.3.zip** 复制到服务器并解压缩，将前面保存的**jmx**也上传到服务器，进入到 **/apache-jmeter-5.6.3/bin** 目录

```shell

# 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 强制删除现有结果，默认情况是追加到已有结果，可能造成数据干扰


```

然后把测试报告打包下载回来查看，

```shell

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://iovhm.com/book/uploads/images/gallery/2025-01/scaled-1680-/EFWzeLRvnssnD8e4-image-1737119279341.png)](https://iovhm.com/book/uploads/images/gallery/2025-01/EFWzeLRvnssnD8e4-image-1737119279341.png)

# 室内地图制作方法

#### 项目源代码

[https://gitee.com/vpclub-wh/indoor-map](https://gitee.com/vpclub-wh/indoor-map)

#### 室内地图制作

用threejs实现的室内地图展示，原理非常简单

- 使用正交相机创建2D效果
- 根据地图数据多边形形状
- 使用**OrbitControls**进行地图控制

所有的代码都在**src/components/indoor-map.vue** 中

#### 制作地图形状数据

要实现平面地图倒是很简单，绘制多边形形状与标记，以便于数据绑定，则需要借助一些外部工具，因此，我们使用**Tiled mapeditor** 制作地图形状与标记

Tiled下载：[https://www.mapeditor.org/](https://www.mapeditor.org/)

#### 最终效果图

[![](https://iovhm.com/book/uploads/images/gallery/2025-02/scaled-1680-/07Pxev1BMc1CXhA6-image-1740375398127.png)](https://iovhm.com/book/uploads/images/gallery/2025-02/07Pxev1BMc1CXhA6-image-1740375398127.png)

#### 约定

首先，我们约定一张地图有四个图层，需要在图层中定义

- 背景图：**bg**，类型为图像层,图片的长和宽要求是2的整数倍
- 地板或者公共空间:**floor**，类型为对象层
- 房间:**rooms**，类型为对象层
- 文本标记:**label**，类型为对象层

[![](https://iovhm.com/book/uploads/images/gallery/2025-02/scaled-1680-/oWGura5W1rrerADR-image-1740375410222.png)](https://iovhm.com/book/uploads/images/gallery/2025-02/oWGura5W1rrerADR-image-1740375410222.png)

#### 进行区块标记

[![](https://iovhm.com/book/uploads/images/gallery/2025-02/scaled-1680-/RlX5rAwN4ASDYcCO-image-1740375416334.png)](https://iovhm.com/book/uploads/images/gallery/2025-02/RlX5rAwN4ASDYcCO-image-1740375416334.png)

**约定**

- 所有的对象均使用多边形，既使用矩形绘制的区块，也需要转换为多边形，可以在对象上右键，转换为多边形
- 为便于协作，对象的name属性作为与数据绑定的唯一依赖关系
- 因为文字内容需要动态变化，而文字的长和宽需要计算，因此label使用点位置进行标记，再动态计算放到与此坐标点的居中位置
- 导出格式为**json**

#### tiledmap快捷键

- 放大：ctrl++
- 缩小：ctrl+-
- 默认大小：ctrl+0
- 平移：滚轮或者按住空格
- 填充到窗口正中间：ctrl+/

# linux密码规则设置

#### 安装密码策略工具

```shell

cat /etc/os-release

rpm -qa|grep libpwquality

yum -y install libpwquality


```

#### 配置密码复杂度策略

```shell


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



```

##### 一键运行脚本

```shell

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


```

#### 查看所有用户

```shell

awk -F: '($7 !~ /nologin|false/) && ($2 !~ /^[*!]/ || $2 ~ /^\$/ ) {printf "%-12s UID:%-5s Shell:%s\n", $1, $3, $7}' /etc/passwd /etc/shadow


```

# repmgr+PostgreSQL故障自动转移

1. 创建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"]

```

2. 打包成镜像

```
docker run build -t centos7-pgsql-repmgr:v1.0

```

3. 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时间同步

```shell

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文件修复

- [https://github.com/ddcw/ibd2sql](https://github.com/ddcw/ibd2sql)

```
# 下载
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使用手册

#### 非必要，不要开放端口

先上一张图。

[![try_password.png](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/XSpFnI5nge7Q0Fnt-try-password.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/XSpFnI5nge7Q0Fnt-try-password.png)

如图所示，网络上上有无数的人，使用工具扫描，探测默认密码、穷举简单密码。服务器一旦被攻击，轻则中木马挖矿，重则删库丢失数据，造成经济损失。

**重要的事情说三遍，非必要，不要开放端口，不要映射端口。**

**重要的事情说三遍，非必要，不要开放端口，不要映射端口。**

**重要的事情说三遍，非必要，不要开放端口，不要映射端口。**

**默认情况下，防火墙仅应该开放80,443端口，其他端口一定要思考是否有开放的必要性**

考虑到实际维护，需要进行各种调试，应该使用堡垒机进行维护，本文为自建堡垒机**jumpserver**使用手册，堡垒机安装管理请参看其他文章

- jumpserver安装手册
- jumpserver管理手册
- jumpserver使用手册

#### 使用

- 登录

登录地址: [https://jumpserver.devops.vppark.cn/](https://jumpserver.devops.vppark.cn/)

如果你还没有用户名，请联系管理员索取用户名密码

- 切换视图

如果你有多重身份角色，可以切换不同的视图使用不同的功能

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/1vv4W3f9aCx6abcH-image-1691390507701.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/1vv4W3f9aCx6abcH-image-1691390507701.png)

- 我的资产

可以使用的服务器、数据库等，如果列表没有出现你需要维护的服务器，请联系系统管理员进行授权

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/1pI176DNuuR0hS4p-image-1691390838758.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/1pI176DNuuR0hS4p-image-1691390838758.png)

- 远程登录

远程登录有两个入口，分别为左的web终端、右上角的图标

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/80lwUCADd95XBuhH-image-1691390923630.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/80lwUCADd95XBuhH-image-1691390923630.png)

- 选择你需要进行连接的的资产，连接方式

左边为资产列表，右边为远程面板。点击对应的资产可以弹出连接窗口，默认使用web cli方式。

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/EyOWiVAeHaePRODU-image-1691391899792.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/EyOWiVAeHaePRODU-image-1691391899792.png)

如果觉得右边的远程面板太小了，不方便，可以在资产上右键，在新窗口打开。

某些时候需要使用客户端连接、提高效率时，可以选择客户端连接方式。复制相关信息到你的客户端工具，既可以进行连接。

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/DllJsrSM6xhj2TE6-image-1691392272052.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/DllJsrSM6xhj2TE6-image-1691392272052.png)

- 安装客户端插件

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/wyPu9jLDgDPMMTSG-image-1691392642723.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/wyPu9jLDgDPMMTSG-image-1691392642723.png)

如果想使用使用客户端连接，需要安装插件

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/0AozWPjQy4QzuQfj-image-1691392761988.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/0AozWPjQy4QzuQfj-image-1691392761988.png)

下载对应的操作系统软件版本，进行安装

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/SG2mtJVEB5piulOA-image-1691392780837.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/SG2mtJVEB5piulOA-image-1691392780837.png)

**注意，安装过程非常快，一闪而过，且在桌面不会留下任何图标，安装成功与否，可以参看控制面板内已经安装程序是否出现**

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/XYIvAgCzyvMUoLTU-image-1691392879663.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/XYIvAgCzyvMUoLTU-image-1691392879663.png)

- 远程桌面

由于windows无法容器化，使用了ubuntu xface 容器化作为远程桌面。

中文输入法支持，启动fctix

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/iFpS6yaks7a1xEBB-image-1691393887715.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/iFpS6yaks7a1xEBB-image-1691393887715.png)

点击键盘图标，如果无法切换到中文输入法，则需要增加中文输入法

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/xiZ2rOzNlB8KEhNv-image-1691393952182.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/xiZ2rOzNlB8KEhNv-image-1691393952182.png)

添加google pinyin输入法

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/IhdJzz4IRAy1JjsR-image-1691394165582.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/IhdJzz4IRAy1JjsR-image-1691394165582.png)

双击桌面的 **navicat16** 图标，使用navicat 提高效率，如果桌面没有navicat图标，可以使用如下命令创建，或者打开终端，直接输入 **navicat16**,最新版本navicat已经支持连接redis，mongo，可以直接使用

```
# 打开终端，运行
ln -sf /usr/local/bin/navicat16 /home/ubuntu/Desktop/navicat16

```

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/kvOjyGJkIHOOncgJ-image-1691399985267.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/kvOjyGJkIHOOncgJ-image-1691399985267.png)

- 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](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台主机。

- **重置访问路径**

如果觉得系统自动生成的安全域名不好记忆，可以修改入口路径

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/YCxGxNDhPMCB3ygl-image-1691556059095.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/YCxGxNDhPMCB3ygl-image-1691556059095.png)

- **加入被监控主机**

输入正确的主控端IP地址，点击获取命令获取被控端安装命令，到每一台被控端执行

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/SXUHvAxRf4PTjKuF-image-1691556510302.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/SXUHvAxRf4PTjKuF-image-1691556510302.png)

- **对被监控主机授权**
    
    **bt-monitor**为收费软件，截止发文止，每个账号可以免费授权5台主机

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/1Zm2Zj4IwcluAXSC-image-1691556434367.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/1Zm2Zj4IwcluAXSC-image-1691556434367.png)

- **主机监控信息**

[![](https://iovhm.com/book/uploads/images/gallery/2023-08/scaled-1680-/GmbEHejXtADyOjJZ-image-1691998790253.png)](https://iovhm.com/book/uploads/images/gallery/2023-08/GmbEHejXtADyOjJZ-image-1691998790253.png)

- **设置企业微信接收告警**

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/W2gh7IEnNiweDpP3-image-1691557020716.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/W2gh7IEnNiweDpP3-image-1691557020716.png)

- **创建微信群组**

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/NpetoZW3HHYEDZsn-image-1691557102639.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/NpetoZW3HHYEDZsn-image-1691557102639.png)

- **添加聊天机器人**

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/5gLiU5iON55NszEh-image-1691557140193.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/5gLiU5iON55NszEh-image-1691557140193.png)

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/iuxU92ytl4ViBFxb-image-1691557156242.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/iuxU92ytl4ViBFxb-image-1691557156242.png)

将地址填入到告警设置窗口

- **添加业务监控**
    
    
    - URL监控
    - 端口监控
    - ping

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/xz7zOnD2uVLt0yyg-image-1691556679485.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/xz7zOnD2uVLt0yyg-image-1691556679485.png)

- **自定义监控**

如果业务监控提供的三种监控方式都不能满足你。可以使用shell进行自定义监控

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/TYR81bQ5vZEfgaGU-image-1691556896937.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/TYR81bQ5vZEfgaGU-image-1691556896937.png)

- **告警规则设置**

将不需要的告警规则去掉，一般只需要：

- 【主机 / 主机状态 / 主机上下线】 匹配 下线
- 【日志 / SSH登录日志 / \[所有\]用户最近1小时登录失败次数】 大于 5
- 【资源 / 磁盘-\[所有\] / 磁盘占用率】 大于 90%
- 【资源 / 内存 / 内存占用率】 大于 90%
- 【资源 / CPU / CPU使用率】 大于 90%

[![](http://qq829.cn/book/uploads/images/gallery/2023-08/scaled-1680-/ZXU7RbMoK9M7Vgx1-image-1691556829440.png)](http://qq829.cn/book/uploads/images/gallery/2023-08/ZXU7RbMoK9M7Vgx1-image-1691556829440.png)

# 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



```

[![](https://iovhm.com/book/uploads/images/gallery/2026-02/scaled-1680-/uUCvoh9Vm9qjD7j5-image-1772005038437.png)](https://iovhm.com/book/uploads/images/gallery/2026-02/uUCvoh9Vm9qjD7j5-image-1772005038437.png)

# 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文件，进入到容器查看

[![path.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/aHu70TllQrV373w4-path.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/aHu70TllQrV373w4-path.png)

#### 常见错误处理

日志报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】禁用即可。

[![Nexus.png](https://iovhm.com/book/uploads/images/gallery/2023-12/scaled-1680-/xc5tujtbsIjtPCRh-nexus.png)](https://iovhm.com/book/uploads/images/gallery/2023-12/xc5tujtbsIjtPCRh-nexus.png)

# 升级gcc

```shell

# 下载安装包

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/linuxnodejscodex)

#### 手工升级方式

[https://iovhm.com/book/books/cee63/page/openssh](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://<hostname>:<port>/&lt;bucket\_name&gt;/&lt;object\_name&gt;</port></hostname>** , 此时会出现无法下载，虽然下面的nginx路径使用程序替换掉了一部分，那意味着上传的时候，如果使用的是api，需要加上**minio**

注：以上内容未验证

```
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身份运行**

```yaml

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镜像使用的用户名密码凭据

系统管理 &gt; 凭据管理 &gt; 全局 &gt; Add Credentials

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/7fJmKnAy8of4Cc5X-image-1733720039741.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/7fJmKnAy8of4Cc5X-image-1733720039741.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/HgvPyKp0yenlJhv5-image-1733720102230.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/HgvPyKp0yenlJhv5-image-1733720102230.png)

#### 新建pipeline （流水线项目）

然后根据项目类型，将后面章节的pipeline脚本复制到项目配置

[![](https://iovhm.com/book/uploads/images/gallery/2025-05/scaled-1680-/qq51I1WULgfe9fRn-image-1747812657531.png)](https://iovhm.com/book/uploads/images/gallery/2025-05/qq51I1WULgfe9fRn-image-1747812657531.png)

#### 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脚本中设置的环境变量对应上。

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/4muvuafOCSJ2XHtY-image-1733721765397.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/4muvuafOCSJ2XHtY-image-1733721765397.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-12/scaled-1680-/LPkmjv2XvQF6ndbk-image-1733721740197.png)](https://iovhm.com/book/uploads/images/gallery/2024-12/LPkmjv2XvQF6ndbk-image-1733721740197.png)

#### 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的多阶段构建

```Dockerfile

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完成

# 达梦数据库

```yaml
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

```yaml
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>


```

#### 初始化数据库

```shell


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集群搭建

#### 单主机安装

```yaml
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

```yaml

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

```yaml

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

```yaml

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



```

#### 测试运行状态

```shell

# 进入到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，然后执行一键补全

[![](https://iovhm.com/book/uploads/images/gallery/2025-09/scaled-1680-/vY9Gh5qANE0EtbEs-image-1757034135067.png)](https://iovhm.com/book/uploads/images/gallery/2025-09/vY9Gh5qANE0EtbEs-image-1757034135067.png)

方法二：重建同步配置

```
# 清空如下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，因为启动文件里面写了，否则启动不了

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/UNYBqT3XJoB3GNm3-image-1742400155006.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/UNYBqT3XJoB3GNm3-image-1742400155006.png)

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/A83GKSX4lwkS8TQF-image-1742402728137.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/A83GKSX4lwkS8TQF-image-1742402728137.png)

#### 介绍

使用微服务架构进行服务拆分后，当某些服务需要用到基础数据的时候，往往粗暴的通过RPC调用，先到基础服务查询出数据，再把所有结果传入到相关服务进行例如**in**,**like** 查询。开发测试的时候因为数据量不够，通常没什么问题，当系统运行一段时间后，数据量增多后，就会出现性能问题。

诸如经常需要用到的基础数据，比较好的做法是进行数据表冗余。在同一个数据库内直接级联查询。但是，会将产生数据同步的问题。

#### 使用otter进行数据同步

otter与canal都是阿里开源出来的数据同步工具，因为阿里巴巴业务的特殊性，卖家主要在国内，买家主要在国外，为了提升用户体验，衍生出了杭州机房和美国机房双机房，既使用工具将数据同步为一致的。

#### 这不是重复造轮子

阿里的开源一贯作风是只管开源，不管维护，otter虽然好，但是BUG一堆，因此对安装部署脚本做了一些修改：

- 管理界面只能内网访问
- 服务一旦停止后就不能重新启动

因此重新打包了镜像，将一些参数移到外部进行启时候替换。

docker-compose方式如下，k8s方式可以参考相关配置。

**重要:一定要挂载持久化数据，否则重启后同步就全部消失了**

<s>**重要：这个软件的登录授权功能等于于无，因此，在设置OTTER\_MANAGER\_URL时，应该设置一个并不存在的域名后，然后使用host表来配置**</s>

**推荐使用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




```

```yaml
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进入到容器进行相关修改

```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](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 收信端口

下图说明了需要的端口的关系

[![](https://iovhm.com/book/uploads/images/gallery/2025-02/scaled-1680-/263U2Gw33oIrlBal-image-1739292729219.png)](https://iovhm.com/book/uploads/images/gallery/2025-02/263U2Gw33oIrlBal-image-1739292729219.png)

**常见问题：25端口无法开启，多半是被系统内置的postfix服务占用了**

```
# 关闭系统内置服务
systemctl disable postfix

```

#### 使用docker-compose

```yaml

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/](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，有一段时间闭源后重新开的一个分支，完全开源。包括一些插件也是开源的

#### 安装

```yaml


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://iovhm.com/book/attachments/15)

#### 安装

官方帮助：[https://www.mock-server.com/](https://www.mock-server.com/)

```yaml

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

#### 创建一个期望

```shell

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"
                }
            }
        }
    }
}'


```

#### 触发一个期望

```shell

curl --request GET \
  --url https://smart-ops-mock.saas.vppark.cn/hello 


```

#### 删除一个期望

```shell

curl --request PUT \
  --url https://smart-ops-mock.saas.vppark.cn/mockserver/clear \
  --data '{
    "path": "/hello"
}'


```

#### 创建一个期望，并转发到其他服务

```shell

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
        }
    }
}}'


```

#### 已经增加的期望列表

```shell

curl --request PUT \
  --url 'https://smart-ops-mock.saas.vppark.cn/mockserver/retrieve?type=active_expectations' 


```

#### 系统状态

```shell

curl --request PUT \
  --url https://smart-ops-mock.saas.vppark.cn/mockserver/status 


```

#### 重置一切

**既整个系统添加的内容全部没有了**

```shell

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://www.mock-server.com/mock_server/response_templates.html#velocity_templates)[https://velocity.apache.org/tools/3.1/tools-summary.html#JsonTool](https://velocity.apache.org/tools/3.1/tools-summary.html#JsonTool)

```js
// 直接转发，请求参数不支持动态替换
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/](https://maxwells-daemon.io/)
- canal ： [https://github.com/alibaba/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://github.com/alibaba/otter)

常见问题：[https://iovhm.com/book/books/42e7a/page/otter](https://iovhm.com/book/books/cee63/page/otter)

#### ninedata

因为otter偶尔会抽风，导致数据没有同步，需要一个数据比对工具，来保证需要同步的表的数据是一致的，ninedata提供的相关功能，基本能满足这个要求了，且提供了其他工具。社区版免费使用。

官方网址：[https://www.ninedata.cloud/](https://www.ninedata.cloud/)

##### 核心功能：

- **查询分析器**
- **慢sql分析**
- **数据对比**，主从不同步了也应该可以使用这个功能
- 数据复制（用于数据搬迁和数据归档）
- 数据库结构对比

##### 社区版安装方法

只能在主机上安装，不能在k8s里面安装。

```yaml

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** 默认密码登陆

- 配置数据源

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/pDdrHVicBVFBSVND-image-1750225857060.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/pDdrHVicBVFBSVND-image-1750225857060.png)

- 慢sql分析，需要先在mysql开启慢sql分析功能

```shell

# 临时有效
SET GLOBAL slow_query_log=1; 
SET GLOBAL long_query_time=30;
SET GLOBAL log_output='TABLE';


```

```cnf

# 开启慢查询日志
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


```

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/rIk3G0lZ9zjZhv2S-image-1750225909730.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/rIk3G0lZ9zjZhv2S-image-1750225909730.png)

- 数据比对

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/eO0yJY3eW86xehC2-image-1750225987658.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/eO0yJY3eW86xehC2-image-1750225987658.png)

- 数据比对明细

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/JNTEwCLEnhg8g1TP-image-1750226020739.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/JNTEwCLEnhg8g1TP-image-1750226020739.png)

- 查看两者的差异

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/hHoQFJPR9bdkrAlo-image-1750226049925.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/hHoQFJPR9bdkrAlo-image-1750226049925.png)

- 查看差异详情、生成SQL语句使差异一致（永远是以源为基准）

[![](https://iovhm.com/book/uploads/images/gallery/2025-06/scaled-1680-/jL4sapHUAFD4oIOr-image-1750226129239.png)](https://iovhm.com/book/uploads/images/gallery/2025-06/jL4sapHUAFD4oIOr-image-1750226129239.png)

# pull&&retag&&push

从源仓库拉取镜像，改名，推送，根据实际情况修改源地址和目标地址

```python

#!/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常用命令

```shell

# 查看所有的索引
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、阿里云对象存储 、 对象存储

官方帮助：[https://help.aliyun.com/zh/oss/developer-reference/install-ossfs-2-0?spm=a2c4g.11186623.help-menu-31815.d\_1\_2\_6\_0.3635d27dCalXKg](https://help.aliyun.com/zh/oss/developer-reference/install-ossfs-2-0?spm=a2c4g.11186623.help-menu-31815.d_1_2_6_0.3635d27dCalXKg)

```shell

# 下载
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](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