中间件

三节点哨兵集群部署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


version: '3'
services:
  elasticsearch-1:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
    restart: always
    container_name: elasticsearch-1
    privileged: true
    ports:
      - 9211:9200
      - 9300:9300
    environment:
      - node.name=es01                                            # 节点名称,唯一
      - network.host=0.0.0.0                                      # 节点IP,单节点时候可以不需要
      - network.publish_host=172.16.2.106                          # 发布地址
      - cluster.name=vpclub-log                                   # 集群名称,集群一致
      - cluster.initial_master_nodes=es01,es02,es03               # 集群节点成员
      - discovery.seed_hosts=elasticsearch-2,elasticsearch-3      # 从指定主机发现
      - xpack.security.enabled=false                              # xpack 安全设置
      - xpack.security.http.ssl.enabled=false                     # xpack 安全设置	
      # - discovery.type=single-node # 单节点
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    volumes:
      - ./data1:/usr/share/elasticsearch/data
    extra_hosts:
      - "elasticsearch-1:172.16.2.106"
      - "elasticsearch-2:172.16.2.107" 
      - "elasticsearch-3:172.16.2.108"
    networks:
      - elasticsearch-bridge   

  elasticsearch-2:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
    restart: always
    container_name: elasticsearch-2
    privileged: true
    ports:
      - 9211:9200
      - 9300:9300
    environment:
      - node.name=es02                                            # 节点名称,唯一
      - network.host=0.0.0.0                                      # 节点IP,单节点时候可以不需要
      - network.publish_host=172.16.2.107                          # 发布地址
      - cluster.name=vpclub-log                                   # 集群名称,集群一致
      - cluster.initial_master_nodes=es01,es02,es03               # 集群节点成员
      - discovery.seed_hosts=elasticsearch-1,elasticsearch-3      # 从指定主机发现
      - xpack.security.enabled=false                              # xpack 安全设置
      - xpack.security.http.ssl.enabled=false                     # xpack 安全设置		
      # - discovery.type=single-node                              # 单节点
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    volumes:
      - ./data2:/usr/share/elasticsearch/data
    extra_hosts:
      - "elasticsearch-1:172.16.2.106"
      - "elasticsearch-2:172.16.2.107" 
      - "elasticsearch-3:172.16.2.108"
    networks:
      - elasticsearch-bridge   

  elasticsearch-3:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/elasticsearch:8.8.0
    restart: always
    privileged: true
    container_name: elasticsearch-3
    ports:
      - 9211:9200
      - 9300:9300
    environment:
      - node.name=es03                                            # 节点名称,唯一
      - network.host=0.0.0.0                                      # 节点IP,单节点时候可以不需要
      - network.publish_host=172.16.2.108                          # 发布地址
      - cluster.name=vpclub-log                                   # 集群名称,集群一致
      - cluster.initial_master_nodes=es01,es02,es03               # 集群节点成员
      - discovery.seed_hosts=elasticsearch-2,elasticsearch-1      # 从指定主机发现
      - xpack.security.enabled=false                              # xpack 安全设置
      - xpack.security.http.ssl.enabled=false                     # xpack 安全设置
      # - discovery.type=single-node                              # 单节点
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    volumes:
      - ./data3:/usr/share/elasticsearch/data
    extra_hosts:
      - "elasticsearch-1:172.16.2.106"
      - "elasticsearch-2:172.16.2.107" 
      - "elasticsearch-3:172.16.2.108"
    networks:
      - elasticsearch-bridge   

networks:
  elasticsearch-bridge:
    external:
      name: elasticsearch-bridge    


测试服务状态

# curl localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "wuxue-ny",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

# 查看集群信息
# curl localhost:9211/_cluster/health?pretty
# curl localhost:9211/_cat/nodes?v
# curl localhost:9211/_cat/indices?v


nginx

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
# touch default.conf

server {
    listen 80;
    #listen 443 ssl;
    server_name localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    #ssl_certificate     /home/ssl/server.crt;
    #ssl_certificate_key /home/ssl/server.key;
    #### 性能优化 ####
    # 开启gzip压缩
    gzip on;
    # 压缩哪些文件类型
    gzip_types text/plain text/html text/css application/json text/javascript application/javascript;
    # 最小压缩大小,小于这个大小不压缩,单位是字节
    gzip_min_length 1000;
    # 压缩率,1-9,数字越大压缩的越好,但是也越消耗CPU
    gzip_comp_level 6;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁止IE6使用gzip,因为这些浏览器不支持gzip压缩
    gzip_disable "MSIE [1-6]\.";
    # 在特定条件下对代理服务器的响应进行压缩
    gzip_proxied expired no-cache no-store private auth;
    # 缓冲区数量和大小,设置了16个8KB的缓冲区
    gzip_buffers 16 8k;
    # 最小http版本,低于这个版本不压缩
    gzip_http_version 1.1;
    #### 性能优化 ####

    #### 安全设置 ####
    # 禁止跨站脚本攻击
    add_header X-XSS-Protection "1; mode=block";
    # 禁止网页被嵌入到iframe或者frame中
    add_header X-Frame-Options "SAMEORIGIN";
    # 启用了 HSTS (HTTP 严格传输安全)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    # 防止浏览器对响应的内容类型进行 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff";
    #### 安全设置 ####

    root /usr/share/nginx/html;
    index index.html;

    location / {
        # 不缓存首页,解决VUE单页面发版后不生效
        add_header Cache-Control "no-cache no-store must-revalidate proxy-revalidate,max-age=0";
        add_header Last-Modified $date_gmt;
        # 这个有顺序,需要加在后面
        etag off;
    }

    location /api/ {
        proxy_pass http://project-manager:8080;
        proxy_set_header Host $http_host;
    }
}

性能优化之启用GZIP

# 增加到http区块(全局有效)或者server区块(单个监听有效)
server {
    #### 性能优化 ####
    # 开启gzip压缩
    gzip on;
    # 压缩哪些文件类型
    gzip_types text/plain text/html text/css application/json text/javascript application/javascript;
    # 最小压缩大小,小于这个大小不压缩,单位是字节
    gzip_min_length 1000;
    # 压缩率,1-9,数字越大压缩的越好,但是也越消耗CPU
    gzip_comp_level 6;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁止IE6使用gzip,因为这些浏览器不支持gzip压缩
    gzip_disable "MSIE [1-6]\.";
    # 在特定条件下对代理服务器的响应进行压缩
    gzip_proxied expired no-cache no-store private auth;
    # 缓冲区数量和大小,设置了16个8KB的缓冲区
    gzip_buffers 16 8k;
    # 最小http版本,低于这个版本不压缩
    gzip_http_version 1.1;
}

nginx经常被扫描的几个安全设置

# 增加到 server 区块
server {

    # 禁止跨站脚本攻击
    add_header X-XSS-Protection "1; mode=block";
    # 禁止网页被嵌入到iframe或者frame中
    add_header X-Frame-Options "SAMEORIGIN";
    # 启用了 HSTS (HTTP 严格传输安全)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    # 防止浏览器对响应的内容类型进行 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff";
}

mysql

docker pull mysql
# MYSQL_ROOT_PASSWORD root密码
/etc/mysql/my.cnf # 配置文件
/var/lib/mysql    # 存储挂载
version: '3'
services:
  mysql:
    image: mysql:5.7 # 一定要写清楚版本号,不同版本之间会出现不兼容
    container_name: mysql-1
    privileged: true
    restart: always # 自动重启   
    ports:
      - 33309:3306
    volumes:
      - ./mysql:/var/lib/mysql                  # 文件存储
      - ./my.cnf:/etc/mysql/mysql.conf.d/my.cnf # 配置文件
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456 # root密码

[mysql]
user=root
default_character_set=utf8



[mysqld]
# 不区分表名称大小写
lower_case_table_names=1
# 不在聚合函数列的字段的兼容的GROUP BY
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

# 最大连接数
max_connections=5000

# 等待连接超时时间(秒)
wait_timeout=600

# mysql 在关闭一个进程前要等待的秒数
interactive_timeout=600

# 一行记录的大小限制,最大为1G,默认值为4M
max_allowed_packet=100M

# 服务器ID,唯一
server_id=1

# 开启binlog
log_bin=binlog

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

# 开启行日志
binlog_format=ROW

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


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




use mysql;
# 创建用户
CREATE USER 'join_web_user'@'%' IDENTIFIED BY 'jo!n141421';

# 查看用户
select * from user

# 授权指定库和表
# 语法 GRANT [privileges] on [[databasename].[tablename] to ‘user’@‘host’
# privileges SELECT,INSERT,UPDATE,ALL
# databasename.tablename 如果全部授权则填写为*或者*.*,databasename.*
# ‘user’@‘host’ 登录用户名
GRANT all privileges ON *.* TO 'join_web_user'@'%'

# 刷新权限
flush privileges;

# 删除权限
# 语法 REVOKE [privileges]   ON [[databasename].[tablename] from 'join_web_user'@'%' 
# 其他操作参数的使用与授权一致
REVOKE all  ON *.* from 'join_web_user'@'%' 

# 删除用户
DROP USER 'join_web_user'@'%';

反向代理&&端口转发&&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 # 特权提升  

traefik-gateway

本文为traefik基础用法,dashboard用法请参考:traefik-gateway-dashboard

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

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

特性 nginx traefik
动态配置 不支持动态配置 外部文件、redis、json、etcd
修改配置重启 需要重启 不需要重启
tcp代理 不支持(需要重新编译源码和插件) 支持
web容器 支持 不支持
反向代理自动携带HOST 支持 不支持,需要使用中间件

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

官方网站

主要概念

静态配置示例

# 静态配置

global:
  checkNewVersion: true
  sendAnonymousUsage: true

entryPoints:
  http:
    address: :80
    # http:
    #   redirections: # http 自动跳转到 https
    #     entryPoint:
    #       to: https 
    #       scheme: https

             
#  tcp:
#    address: :9095/tcp  

  https:
    address: :443
    http:
      tls: {} # 开启 https


log:
  level: DEBUG
  format: json

# accessLog:
#   format: json

api:
  insecure: true  # 开启dashboard
  dashboard: true
  debug: true


providers:
  file:
    # filename: /etc/traefik/conf.d/conf.yaml 单个文件
    directory: /etc/traefik/conf.d/ # 监视文件夹
    watch: true
#  http:
#    endpoint: "http://192.168.64.1:3000/api"

# 插件支持
# experimental:
#   localPlugins:
#     rewritebody:
#       modulename: "github.com/traefik/plugin-rewritebody"
#       version: "v0.3.1"


动态配置示例

# 动态配置
http:
  routers:
    # 首页
    web-site:
      rule: "PathPrefix(`/`)"
      service: web-site     
      middlewares:
        - stripprefix-common
    iovhm-api:
      rule: "PathPrefix(`/iovhm-api/`)"
      service: iovhm-api
      # middlewares:
      #   - testHeader
############################################################################
  services:     
    web-site:
      loadBalancer:
        servers:
          - url: http://web-site:80
    iovhm-api:
      loadBalancer:
        servers:
          - url: http://iovhm-web-api.gxzszs.cn/
############################################################################
  middlewares:
    stripprefix-common:
      stripPrefix:
        prefixes:
          - "/foo"
          - "/home-admin"
    testHeader:
      headers:
        customRequestHeaders:
           host: "iovhm-web-api.gxzszs.cn"


#tcp:
#  routers:
#    abc:
#      entryPoints:
#        - "tcp"
#      rule: "HostSNI(`*`)"
#      service: my-service
#  services:
#    my-service:
#      loadBalancer:
#        servers:
#          - address: 139.9.93.117:80

  

# 证书列表,会根据域名自动匹配
tls:
  certificates:
    - certFile: /home/ssl/qq829cn.cer
      keyFile: /home/ssl/qq829cn.key

# 默认证书
# tls:
#   stores:
#     default:
#       defaultCertificate:
#         certFile: "/home/ssl/qq829cn.cer"
#         keyFile: "/home/ssl/qq829cn.key"

docker-compose.yaml配置文件

# docker-compose

version: "3"
services:
  mobile-office-web:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/digital-base/traefik:latest
    restart: always # 自动重启
    privileged: true
    ports:
      # - 8080:80
      # - 8443:443
      - 80:80
      - 443:443
      - 8081:8080
      # - 9095:9095
    volumes:
      - ./traefik.yml:/etc/traefik/traefik.yml
      - ./conf.d/:/etc/traefik/conf.d/
      - ./ssl:/home/ssl
      # - ./plugins:/plugins-local
    environment:
      - TZ=Asia/Shanghai

traefik-gateway-dashboard

本文为dashboard用法,基础用法请参考:traefik-gateway

重要bug提醒:截止至2023-06-22,如果动态创建的服务发现,少于1个中间件,则会出错,既需要保证整个项目至少要有一个中间件。可以随便写一个,但是必须要有一个。

三个镜像

2023-06-21:无需再部署dashboard

安装部署说明

# 建议服务名:traefik-gateway
# 端口开放
# 80,http端口
# 443,https端口
# 8080,traefik自带的dashboard端口,可以不开放
# 服务名必须为 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


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

路径 服务 说明 备注
/ traefik 网站根目录 必须
/traefik-dashboard/ traefik traefik内置的dashboard 非必须,如果没有配置网站根目录则需要
/traefikconfig-dashboard/ traefik traefik动态路由dashboard 非必须,如果没有配置网站根目录则需要
/traefik-service-discovery/ traefik 动态路由api 非必须,如果没有配置网站根目录则需要

首页不是从根目录开始

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

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


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

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

需要在负载均衡加入配置

路径 服务 说明 备注
/minio/ traefik 业务路由入口 必须
/_minio/ traefik 面板路由入口 如果配置了下面三个路径,则是非必须
/_minio/traefik-dashboard/ traefik traefik内置的dashboard 非必须,如果没有配置面板入口则需要
/_minio/traefikconfig-dashboard/ traefik 动态路由dashboard 非必须,如果没有配置面板入口则需要
/_minio/traefik-service-discovery/ traefik 动态路由api 非必须,如果没有配置面板入口则需要

http:
  routers:
    # 首页不是根目录
    index-not-root:
    # 注意路由匹配前缀,填写你的实际路径,为了保证一致性,建议本服务使用 _ 开头
    # 注意中间件中的路径前缀,应该和路由路径一致,
    # 注意重写header的前缀,应该和路由路径一致。
    # 根据本路由规则,可以看出是将服务又重定向到traefik-gateway,即多了一层不必要的代理,会带来性能损失。
    # 由此,这个额外的路由,只应该用于处理traefik控制面板
    # 真正的服务不应该由此处理,而应该直接指向到traefik-gateway
    # 既你需要配置面板路由路径,应该与你的服务路径,使用不通的名称
    # 本次使用的是 _minio ,请将本文的_minio 替换为你需要的
      # rule: "PathPrefix(`/_minio/`)" 
    # 2023-06-22,:只处理dashboard相关的3个路径,以开启面板,业务路由在面板进行配置,以解决多了一层代理问题   
      rule: "PathPrefix(`/_minio/traefik-dashboard/`) || PathPrefix(`/_minio/traefikconfig-dashboard/`) || PathPrefix(`/_minio/traefik-service-discovery/`)"  
      service: index-not-root
      middlewares:
        - stripPrefix-index-not-root
        - rewritebody-index-not-root
        - rewriteHeaders-index-not-root
  services:
    index-not-root:
      loadBalancer:
        servers:
          - url: http://traefik-gateway:80    # 部署的traefik主程序必须叫这个名字或者修改为实际部署的名字
  middlewares:
  # 去除前缀  
    stripPrefix-index-not-root:
      stripPrefix:
        prefixes:
          - "/_minio/"
  # 正文路径改写
    rewritebody-index-not-root:
      plugin:
        plugin-rewritebody:                   # 要和静态配置文件的插件名称对应上
          rewrites:
            - regex: "/traefik-service-discovery/api/"
              replacement: "/_minio/traefik-service-discovery/api/"
            - regex: "/traefik-dashboard/api"
              replacement: "/_minio/traefik-dashboard/api"
            - regex: "/_minio/dashboard/"
              replacement: "/_minio/traefik-dashboard/dashboard/"
  # Header重定向改写
    rewriteHeaders-index-not-root:
      plugin:
        plugin-rewriteHeaders:                # 要和静态配置文件的插件名称对应上
          rewrites:
            - header: "Location"
              regex: "/_minio/dashboard/"
              replacement: "/_minio/traefik-dashboard/dashboard/"



插件使用

插件参考网址


middlewares:
# 删除路径前前缀,traefik默认会把整个URL路径全部传过来,会导致与后端路径不一致,需要进行路径删除处理
  stripPrefix-traefikconfig-dashboard:
    stripPrefix:
      prefixes:
        - "/traefikconfig-dashboard/"

# 增加路径前缀,对应与后端服务,即使在填写URL路径,traefik也不会处理后端路径,会导致与后端路径不一致,需要增加实际路径
  addPrefixFront-traefikconfig-dashboard:
    addPrefix:
      prefix: "/front/"

  # 正文路径改写
    rewritebody-traefikconfig-dashboard:
      plugin:
      # 要和静态配置文件的插件名称对应上
        plugin-rewritebody: 
          rewrites:
            - regex: "/api/"
              replacement: "/traefik-service-discovery/api/"   

# 基础身份验证
  basicAuth-traefikconfig-dashboard:
      basicAuth:
        removeHeader: true
        users:
          - "admin:" # 在线生成 http://www.jsons.cn/htpasswd/


# 发送到后端时修改header,例如后端限制了域名
  headers-peisong:
    headers:
      customRequestHeaders:
        host: "peisong.gxzszs.cn"

# 重写 header,对响应的head进行处理,例如写死的重定向
  rewriteHeaders-peisong:
    plugin:
      plugin-rewriteHeaders: # 要和静态配置文件的插件名称对应上,本镜像仅能使用这个名字
        rewrites:
          - header: "Location"
            regex: "peisong.gxzszs.cn"
            replacement: "peisong.gxfusui.com"
          - header: "Location"
            regex: "http://peisong.gxfusui.com/plugin.php"
            replacement: "/plugin.php"




在plugin-rewritebody使用正则表达式需要注意的事项

如下面的代码所示,一个使用了双反斜杠,一个使用的是单反斜杠,实际上取决于最外围的引号,这是json决定的,而不是这里的字符串的问题,需要注意。

需要转义的字符有:


middlewares:
  rewritebody-iams-pis:
    plugin:
      plugin-rewritebody:
        rewrites:
          - regex: '\bpis.guizhougas.cn\b'
            replacement: 'iams-pis.shulianhuiyun.com'

  rewritebody-iams-iot:
    plugin:
      plugin-rewritebody:
        rewrites:
          - regex: "\\biot.shulianhuiyun.com\\b"
            replacement: "iams-iot.shulianhuiyun.com"

BookStack

bookstack是一个简单、易用的文章发布软件

  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

pritunl-vpn

pritunl-vpn 是开源软件open vpn的一个实现,适合企业级使用

参考网址

使用docker-compose安装部署

version: "3"
services:
  pritunl-vnp:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/pritunl:latest
    container_name: pritunl-vpn
    privileged: true
    restart: always # 自动重启
    ports:
      - 33312:80
      - 33313:443
      - 33314:33314/tcp
      - 33314:33314/udp
    volumes:
      - ./pritunl:/var/lib/pritunl
      - ./mongodb:/var/lib/mongodb


# 80端口,http访问,事实上他会自己重定向到https
# 443端口,https访问
# 33314端口,既你在新增服务时设置的端口,需要开启防火墙

配置

默认用户名密码: pritunl / pritunl

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

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


yum install openvpn

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

# vi /etc/systemd/system/openvpn-client.service
# 编写为服务
[Unit]
Description=OpenVPN Client Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/sbin/openvpn --config /data/vpclub/openvpn/your_ovpn_configuration_file.ovpn
Restart=on-failure


[Install]
WantedBy=multi-user.target

pritunl仅能点对点访问,既,可以通过vpn client 访问远端网络,但是不能通过远端网络访问本地地址。

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"]
  1. 打包成镜像
docker run build -t centos7-pgsql-repmgr:v1.0
  1. 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

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

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

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

常见错误处理

日志报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

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=



redis单节点部署

redis单节点yaml文件

version: "3"
services:
  redis:
    image: redis
    restart: always # 自动重启
    ports:
      - 6379:6379
    command: redis-server --appendonly yes --requirepass <password> #redis的密码
    networks:
      - redis-bridge

networks: 
  redis-bridge:
    external:
      name: redis-bridge

redis服务启动

docker-compose -f docker-compse.yaml up -d

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

这个是用来访问控制台的,可以使用代理

    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 控制台的地址和端口
    }
}



API端点,console也需要用这个端点访问API,如果这个端点地址不能被console访问,console会出现无法登录或者白屏,如果你的服务器不能回路,那需要做主机解析。如果你确实不能把API地址设置一个单独的域名,可以设置代理,但是minio要兼容s3协议,要求的路径是http://:/<bucket_name>/<object_name> , 此时会出现无法现在,虽然下面的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


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

一、 功能概述

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

二、注册数据源

注册数据源如下图。 微信图片_20231207143758.png

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

三、添加数据视图

数据视图-新建数据视图-SQL 视图,进入编写要查询数据库的SQL 微信图片_20231207144517.png

1.先选择数据源

2.编写查询SQL

3.执行SQL

4.保存视图

5.将数据视图关掉,重新进入,地址栏可以看到视图id 微信图片_20231207153849.png

四、调用接口查询数据

1.获取TOKEN

(1)接口地址:[POST] https://{url}/api/v1/users/login

(2)Body 请求参数:

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

(3)响应头Authorization中获取token 微信图片_20231207160706.png

2.获取视图数据

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

(2)Header 请求参数

参数名 参数值 是否必填 参数类型 参数说明
Authorization Bearer eyJhbGciOiJIUzI1xxxxx string token

(3)Body 请求参数

{
	"page": 1,//当前页
	"limit": 20,//每页条数
	"viewId": "a961d8dc8af4496aad3dfe71d9668416",//视图id
	"filters": [  //查询条件,接口执行时会拼在sql语句的where部分
		{
			"column": [  //字段名字
				"create_date"
			],
			"sqlOperator": "GT",  //运算符(EQ:等于,NE:不等于,GTE:大于等于,LTE:小于等于,LT:小于,GT:大于)
			"values": [
				{
					"value": "2023-06-08",  //参数值
					"valueType": "DATE"  //参数类型
				}
			]
		}
	]
}

(4)响应示例

{
	"data": {  //返回数据
		"list": [  返回数据列表,字段由查询数据决定
			{
				"request_time": 154,
				"ip": "127.0.0.1",
				"id": 1667084925162176513,
				"request_method": "POST",
				"create_date": "2023-06-09 16:23:14",
				"operation": "保存",
				"request_uri": "/renren-admin/kong/kongapp/save",
				"status": 0
			}
		],
		"page": 1,   //当前页
		"limit": 20,  //每页面条数
		"total": 56   //总条数
	},
	"errCode": 0,
	"message": null,
	"success": true  //是否成功
}

五、自定义视图变量

(1)添加变量 微信图片_20231207163845.png

(2)变量使用,变量格式:$变量名称$,使用如下:

select 
    *
from sys_log_operation where create_date between $START_DATE$ and $END_DATE$

(3)接口调用时添加参数

{
	"page": 1,//当前页
	"limit": 20,//每页条数
	"viewId": "a961d8dc8af4496aad3dfe71d9668416",//视图id
	"params": {  //视图自定义变量
		"START_DATE": [  //变量名称
			"2023-06-12"  //变量值
		],
		"END_DATE": [
			"2023-06-13"
		]
	}
}

定时备份mysql

相关容器

swr.cn-south-1.myhuaweicloud.com/vp-public/mysql-backup:v1.0

相关配置

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

备份文件配置信息

# 5-11.yaml

- host: 192.168.5.11
  user: 
  password: 
  name: mysql    # 没有实际意义,用来区分备份文件名称,按时间生成{name}-20240713-141847-732950.tar.gz 这样的文件
  port: 3306


- host: 192.168.5.12
  user: 
  password: 
  name: mysql2  # 没有实际意义,用来区分备份文件名称,按时间生成{name}-20240713-141847-732950.tar.gz 这样的文件
  port: 3306

直接运行

#!/bin/bash

docker run -it \
        -v $(pwd)/config:/config -v $(pwd)/data:/data \
        --privileged=true \
        swr.cn-south-1.myhuaweicloud.com/vp-public/mysql-backup:v1.0

在k8s创建定时容器

MYSQL维护指南

1. 监控数据库性能

1.1. 主机监控

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

常用命令:topfree -hdf -h 检查mysql主机的性能,可用资源应该冗余30%以上。

1.2. 状态监控

-- 这条命令用于查询当前有多少客户端线程连接到数据库。
-- 这个数字可以帮助你了解数据库的负载情况,如果连接数异常高,可能意味着你的数据库正面临过高的请求负载或者连接泄露问题
SHOW GLOBAL STATUS LIKE 'Threads_connected';

-- 这条命令用于显示自数据库启动以来执行的查询总数。
-- 这个指标可以帮助你评估数据库的工作量,了解数据库的使用频率
SHOW GLOBAL STATUS LIKE 'Queries';

-- 这条命令查询排序缓冲区的大小。
-- 排序缓冲区是MySQL用于排序操作的内存区域。调整这个值可以影响排序操作的性能,特别是在处理大量数据时
SHOW VARIABLES LIKE 'sort_buffer_size';

-- 这条命令用于查询InnoDB缓冲池的大小。InnoDB缓冲池是存储InnoDB表数据和索引的内存区域。
-- 这个值的大小直接影响到数据库的性能,特别是在处理大型数据库时
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- 这条命令显示当前MySQL服务器上所有运行的进程信息。
-- 这可以帮助你识别当前正在执行的查询,检查是否有长时间运行的查询或者锁竞争情况
SHOW FULL PROCESSLIST;


1.3. 利用MySQL的性能模式(Performance Schema)

-- 检查performance_schema这个系统变量的状态,如果performance_schema=ON,说明performance_schema已经启用
SHOW VARIABLES LIKE 'performance_schema';

-- 如果没有启用performance_schema,可以通过修改my.cnf文件来启用performance_schema,添加如下配置
-- [mysqld]  
-- performance_schema=ON
-- 所有已执行SQL语句的性能摘要,返回了执行次数最多的前20个查询模板,这有助于识别出数据库中最频繁执行的查询,这些查询往往是性能优化的重点目标
use performance_schema;

SELECT
    SCHEMA_NAME,
    DIGEST_TEXT,
    COUNT_STAR as `执行次数`,
    SUM_TIMER_WAIT / COUNT_STAR / 1e12 AS `平均执行时间(秒)`,
    MAX_TIMER_WAIT / 1e12 as `最大执行时间(秒)`,
    SUM_LOCK_TIME / COUNT_STAR / 1e12 AS `平均锁定时间(秒)`,
    SUM_ROWS_SENT / COUNT_STAR AS `平均返回的行数`,
    SUM_ROWS_EXAMINED / COUNT_STAR AS `平均扫描的行数`,
    SUM_ROWS_AFFECTED / COUNT_STAR AS `平均影响的行数`,
    SUM_CREATED_TMP_DISK_TABLES / COUNT_STAR AS `平均创建的临时磁盘表`,
    SUM_SORT_MERGE_PASSES / COUNT_STAR AS `序操作的效率`,
    SUM_NO_INDEX_USED / COUNT_STAR AS `没有使用索引的次数`,
    SUM_NO_GOOD_INDEX_USED / COUNT_STAR AS `没有更好的使用索引的次数`
FROM
    events_statements_summary_by_digest
WHERE
    SCHEMA_NAME NOT IN (
        'mysql',
        'performance_schema',
        'information_schema'
    )
ORDER BY
    `平均执行时间(秒)` DESC
LIMIT
    20;
 
1.4. 分析慢日志

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


# 开启慢查询日志
slow_query_log=1

# 慢查询日志文件路径
# slow_query_log_file=/var/log/mysql/mysql-slow.log
# 如果未设置,可以使用 SHOW VARIABLES LIKE 'slow_query_log_file'查看

# 记录查询执行时间超过30秒的查询
long_query_time=10


-- 查询是否开启慢日志
SHOW VARIABLES LIKE 'slow_query_log';

-- 临时开启慢日志,重启后失效
SET  GLOBAL slow_query_log = 'ON';

-- 查看慢查询阈值
SHOW VARIABLES LIKE 'long_query_time';

-- 设置超过阈值的查询时间为慢查询
SET  GLOBAL long_query_time = 10;

-- 慢日志存储路径
SHOW VARIABLES LIKE 'slow_query_log_file';

-- 查看慢查询日志
SELECT
  *
FROM
  mysql.slow_log
ORDER BY
  start_time DESC
LIMIT
  20;


2. 备份数据库

根据数据库的重要性和数据变动频率,制定合理的备份策略。通常建议每天或每周进行一次全备份,并结合增量备份以减少存储空间和时间消耗。

2.1. 备份方式

可以选择逻辑备份(如使用mysqldump工具)或物理备份(如直接复制数据文件)。逻辑备份生成SQL脚本文件,便于在不同版本的MySQL之间迁移;物理备份则速度更快,恢复时更接近原始状态。

-- 备份mysql数据库
mysqldump - u root - p --all-databases > all_databases.sql

-- 恢复mysql数据库
mysql - u root - p < all_databases.sql
2.2. 验证备份

定期验证备份数据的完整性和可恢复性,确保在需要时能够成功恢复数据。


3. 优化查询语句和表结构

3.1. 优化查询语句

使用合适的索引、避免全表扫描、减少不必要的JOIN操作等,以提高查询效率。

3.2. 优化表结构

定期检查并优化表结构,删除不必要的字段和表,合并相似的表以减少冗余数据。对于大型表,可以使用OPTIMIZE TABLE命令进行优化,以减少磁盘空间使用和提高查询性能。

3.3. 归档历史数据

将超过一定历史时限的数据库进行归档,并从实时数据库进行删除,以降低数据量提高数据库性能。


4. 更新和维护

4.1. 更新数据库软件:

定期更新MySQL服务器和相关组件的软件版本,以修复已知的漏洞、提高性能和增加新功能。

4.2. 安装安全补丁:

及时安装MySQL的安全补丁,以防止黑客攻击和数据泄露。


5. 安全管理

5.1. 设置强密码与密码密码过期策略

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

-- 全局的密码生命周期
SET GLOBAL default_password_lifetime = 180;

 
-- 设置密码过期时间
ALTER USER 'your_username' @'localhost' PASSWORD EXPIRE INTERVAL 180 DAY;

5.2. 最小权限原则:

6. 其他维护任务

6.1. 检查磁盘空间

确保数据库服务器有足够的磁盘空间来存储数据和日志文件。

6.2. 清理无用数据

定期清理无用的数据、临时表和日志文件等,以释放磁盘空间和提高性能。

6.3. 配置和优化MySQL参数

根据监控结果和实际需求,调整MySQL的配置参数,如缓冲区大小、连接数等,以优化数据库性能。

mysql主从复制&&读写分离

主服务器配置

修改配置文件,编辑MySQL的配置文件(通常是/etc/my.cnf或/etc/mysql/my.cnf),重启MySQL服务,使配置生效。


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

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

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

# 数据库名(可选):
# binlog-do-db=指定需要复制的数据库。

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

创建复制用户

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


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

从服务器配置

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



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

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

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

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

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

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

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

SHOW MASTER STATUS;

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

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

CHANGE MASTER TO  
MASTER_HOST='10.64.100.91',  
MASTER_USER='replicator',  
MASTER_PASSWORD='password',  
MASTER_LOG_FILE='binlog.000009',  
MASTER_LOG_POS=154;
# 启动复制
START SLAVE;

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

# 主要是2个

Slave_I0 Running,YES
Slave SqL Rurning,YES

jenkins&&docker安装

核心点是3个主要挂载的,socker.sock需要根据情况定,需要以root身份运行


version: '3'
services:
  jenkins:
    image: harbor.iovhm.com/hub/jenkins/jenkins:2.488-jdk17
    container_name: jenkins
    restart: always
    privileged: true
    user: root
    ports:
      - "8080:8080"
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - ./m2:/root/.m2
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
    environment:
      - TZ=Asia/Shanghai


准备工作

需要先建立拉取代码和推送docker镜像使用的用户名密码凭据

系统管理 > 凭据管理 > 全局 > Add Credentials

springboot 多项目

当项目有多个子项目时,一个一个建确实比较麻烦,可以使用如下代码示例

特别注意:sh代码或者代码块,当使用三个单引号(''')时候,无法传入局部变量,需要换成三个双引号("""),使用双引号的时候需要对sh代码块特殊字符进行转义




pipeline {
    agent any

    // 环境变量,全局可用
    environment {
        // 项目名称,将生成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
        
        
    }
    
    // 构建过程参数
    parameters {

        // 代码分支
        choice(name:"BRANCH",choices:["development","release"],description:"代码分支")
        // 版本号,将被添加到docker镜像标签上
        string(name:"VERSION",defaultValue:"v1",description:"版本号")
        // 镜像TAG,将最被添加到docker镜像标签上
        choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
        // 这两个参数将和环境变量的参数组合成镜像的最终名称
        // swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo:v1-latest
        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: '${BRANCH}', credentialsId: 'donglietao-vpclub-sz-git', url: 'https://g.vpclub.cn/gzxfzhdw/back.git'

                // 构建脚本默认在代码仓库根目录运行
                // 如果你的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-huaweicloud', passwordVariable: 'docker_password', usernameVariable: 'docker_username')]) {
                        def conditionVal= "${PUSH_SERVICE}"
                        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}:${VERSION}-${IMAGE_TAG} .
                        docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${VERSION}-${IMAGE_TAG}
                                    """
                                }

                            }
                        }else{
                            def serviceName =SERVICE_MAP[conditionVal]
                            def serverPath = conditionVal
                            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}:${VERSION}-${IMAGE_TAG} .
                        docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}/${serviceName}:${VERSION}-${IMAGE_TAG}
                                """
                            }
                        }
                    }
                }
            }
            
        }
    }
}


springboot maven Pipeline


pipeline {
    agent any

    // 环境变量,全局可用
    environment {
        // 项目名称,将生成docker镜像的名称的一部分
        PROJECT_NAME    ="springboot-demo"
        // docker仓库的分组名称
        PROJECT_NS      ="vp-whdev"
        // docker仓库地址
        DOCKER_REGISTRY ="swr.cn-south-1.myhuaweicloud.com"
        // 这三个参数将组合成你的最终docker镜像名称为
        // swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo
        
        
    }
    
    // 构建过程参数
    parameters {

        // 代码分支
        choice(name:"BRANCH",choices:["development","release"],description:"代码分支")
        // 版本号,将被添加到docker镜像标签上
        string(name:"VERSION",defaultValue:"v1",description:"版本号")
        // 镜像TAG,将最被添加到docker镜像标签上
        choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
        // 这两个参数将和环境变量的参数组合成镜像的最终名称
        // swr.cn-south-1.myhuaweicloud.com/vp-whdev/springboot-demo:v1-latest
    }

    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: '${BRANCH}', credentialsId: 'donglietao-vpclub-sz-git', url: 'https://git.whdev.vpclub.cn:10081/park-weihai/backend-api.git'

                // 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-huaweicloud', 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}:${VERSION}-${IMAGE_TAG} .
                        docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${VERSION}-${IMAGE_TAG}
                        
                        """
                    }
                }
            }
        }
    }
}



点击运行的时候没有出现构造参数界面

在项目配置选项里面,勾选参数化,然后与Pipeline脚本中设置的环境变量对应上。

nodejs Pipeline



pipeline {
    agent any
    
    // 环境变量,全局可用
    environment {
        // GIT仓库地址
        GIT_URL         = "https://g.vpclub.cn/park/sdyk/front-web-admin.git"
        // GIT 仓库凭证
        GIT_IDENTITY    = "donglietao-vpclub-sz-git"
        // 项目名称,将生成docker镜像的名称的一部分
        PROJECT_NAME    ="nodejs-demo"
        // docker仓库的分组名称
        PROJECT_NS      ="vp-whdev"
        // docker仓库地址
        DOCKER_REGISTRY ="swr.cn-south-1.myhuaweicloud.com"
        // 这三个参数将组合成你的最终docker镜像名称为
        // swr.cn-south-1.myhuaweicloud.com/vp-whdev/nodejs-demo
        // DOCKER 仓库凭证
        DOCKER_IDENTITY = "docker-huaweicloud"
        // nodejs专用,nodejs内存溢出错误时候需要
        NODE_OPTIONS    = "--max-old-space-size=4096"
        
    }

    // 构建过程参数
    parameters {
        // 代码分支
        choice(name:"BRANCH",choices:["development","release"],description:"代码分支")
        // 版本号,将最被添加到docker镜像标签上
        string(name:"VERSION",defaultValue:"v1",description:"版本号")
        // 镜像TAG,将被添加到docker镜像标签上
        choice(name:"IMAGE_TAG",choices:["test","prod"],description:"镜像的tag")
        // 这两个参数将和环境变量的参数组合成镜像的最终名称
        // swr.cn-south-1.myhuaweicloud.com/vp-whdev/nodejs-demo:v1-latest
    }
    
    
    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 --registry=https://registry.npmmirror.com
                    npm run build:test
                    
                    """
                }
            }
        }
        
        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}:${VERSION}-${IMAGE_TAG} .
                    docker push ${DOCKER_REGISTRY}/${PROJECT_NS}/${PROJECT_NAME}:${VERSION}-${IMAGE_TAG}
                    
                    """
                    
                }

            }
        }
    }
}









nodejs第二种方法,多阶段构建

由于某些依赖需要比较复杂的环境,例如gyp,早期的sass,导致需要很多依赖,导致整个jenkins环境配置异常复杂,这个时候可以使用docker的多阶段构建


FROM harbor.iovhm.com/hub/node:14.21.3 AS build
WORKDIR /data
COPY ./ /data
RUN cd /data && \
    yarn install --registry=https://registry.npmmirror.com && \ 
    yarn run build:prod && \
    true

FROM harbor.iovhm.com/hub/nginx:1.14.2
COPY --from=build /data/dist/ /usr/share/nginx/html/management




相对于第一种方法,则只需要获取代码,打包全部有docker完成

达梦数据库

version: '3'
services:
  park-talent-dm:
    image: dm8:dm8_20240605_rev215128_x86_rh6_64
    container_name: park-talent-dm8
    restart: always
    privileged: true
    ports:
      - 33066:5236
    volumes:
      - ./data:/opt/dmdbms/data
    environment:
      - LD_LIBRARY_PATH=/opt/dmdbms/bin
      - PAGE_SIZE=16
      - EXTENT_SIZE=32
      - LOG_SIZE=1024
      - UNICODE_FLAG=1
      - LENGTH_IN_CHAR=1
      - CASE_SENSITIVE=0
      - INSTANCE_NAME=dm8_test
      - BLANK_PAD_MODE=1
      - SYSDBA_PWD=<ppassword>
      - ADMIN_PWD=<password>



单机运行postgresql&& 安装kong-gateway

单机运行postgresql

version: '3'
services:
  kong-postgres:
    image: harbor.iovhm.com/hub/postgres:9.6.24
    restart: always
    container_name: kong-postgres
    privileged: true
    ports:
      - 5432:5432
    volumes:
      - ./postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=<kong user>
      - POSTGRES_DB=k<kong db>
      - POSTGRES_PASSWORD=<kong password>

初始化数据库



docker run --rm \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=<PG HOST>" \
-e "KONG_PG_PORT=<PG PORT>" \
-e "KONG_PG_PASSWORD=<kong pass>" \
harbor.iovhm.com/hub/kong:3.0 kong migrations bootstrap

kafka集群搭建

docker-compose.yaml

version: '3.9'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    restart: always
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    volumes:
      - ./zookeeper/data:/data
      - ./zookeeper/datalog:/datalog

  kafka1:
    image: wurstmeister/kafka
    restart: always
    hostname: kafka1
    container_name: kafka1
    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:
      - ./kafka1/logs:/kafka/logs

  kafka2:
    image: wurstmeister/kafka
    restart: always
    hostname: kafka2
    container_name: kafka2
    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:
      - ./kafka2/logs:/kafka/logs


测试运行状态


# 进入到kafka工具集
/opt/kafka_2.12-2.3.0

# broker能够响应请求,通常意味着它正在运行
bin/kafka-broker-api-versions.sh --bootstrap-server kafka1:9092,kafka2:9092

# 查询消费组信息,能够响应请求并返回,通常意味着他正在运行
bin/kafka-consumer-groups.sh --bootstrap-server 172.18.32.61:9093 --list

otter

介绍

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

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

使用otter进行数据同步

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

这不是重复造轮子

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

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

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

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

重要:这个软件的登录授权功能等于于无,因此,在设置OTTE_MANAGER_URL时,应该设置一个并不存在的域名后,然后使用host表来配置

推荐使用nginx-ingress注解认证或者nginx提供的basic认证,也可以使用IP限制

# 使用ip限制

nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.0.0/16,10.0.0.0/8"

# 使用basic认证
# 创建一个键值对(opaque)密钥,
# 键为 auth
# 值为 htpasswd -c auth username 生成
# 在线生成 https://www.bejson.com/encrypt/htpasswd/

# 为ingress增加注解
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth



version: '3'
services:
  otter-server:
    image: swr.cn-south-1.myhuaweicloud.com/vp-park/otter-server:latest
    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
      - OTTE_MANAGER_URL=https://domain:port
    volumes:
      - ./zkData:/home/admin/zkData:rw # 重要,一定要挂载持久化数据,否则重启后同步就全部消失了。

或者你已经在使用otter,确并不想更换镜像则可以


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

打开channel_list.html报错

# 清空如下5个表后重新添加,因此,需要将将要同步的表配置用保存好,否则就傻逼了

delete from data_media_pair;
delete from data_media;
delete from pipeline_node_relation;
delete from pipeline;
delete from channel;




注意,数据库的node id必须为1,因为启动文件里面写了,否则启动不了

dbeaver&&mysql可视化web管理工具

官方参考

https://github.com/dbeaver/cloudbeaver/wiki/Run-Docker-Container

percona-toolkit

下载

https://www.percona.com/percona-toolkit

自建邮件服务器&&mailserver

端口检查

邮件系统需要使用到25、587、465、143、993 端口,请先检查端口是否被占用

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

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

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

使用docker-compose


version: "3"
services:
  smtp-server:
    image: harbor.iovhm.com/hub/mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail.iovhm.com
    network_mode: host
    ports:
      - "25:25"   # STMP
      - "587:587" # STMP CLIENT
      - "465:465" # STMP SSL
      - "143:143" # IMAP
      - "993:993" # IMAP SSL
    volumes:
      - ./data/mail-data:/var/mail  
      - ./data/mail-state:/var/mail-state
      - ./data/mail-logs:/var/log/mail
      - ./data/mail-config:/tmp/docker-mailserver
      - /etc/localtime:/etc/localtime:ro
    environment:
      # 如果在不允许设置主机名的环境中运行,设置邮件服务器的主机名
      - OVERRIDE_HOSTNAME=mail.iovhm.com
      - SPOOF_PROTECTION=1

需要在120秒内增加第一个用户,否则系统自动退出


# 进入到容器后执行
docker exec -it mailserver /bin/bash
# 增加第一个用户
setup email add admin@iovhm.com <password>

# 增加2个约定的管理邮箱(可以设置自动转发到外部邮箱,也可以设置转发到本域邮箱)
setup alias add postmaste@iovhm.com  your@163.com
# 没错,就是abuse,表示乱用举报邮箱
setup alias add abuse@iovhm.com  your@163.com

# 查看用户列表
setup email list
setup alias list

# 生成 DKIM 密钥
setup config dkim

配置DNS记录


mail      IN  A      10.11.12.13
@         IN  MX  10 mail.example.com
@         IN  TXT    "v=spf1 mx ~all"
_dmarc    IN  TXT    "v=DMARC1; p=none; sp=none; adkim=r; aspf=r"


增加DNS


# 查看挂载目录生成的DKIM文本字符串
cat ./data/mail-config/opendkim/keys/iovhm.com

# 填入DNS,使之成为一行

mail._domainkey    IN  TXT  "v=DKIM1; h=sha256; k=rsa;p=XXXXXXXXXXXXXXX"

DNS配置检查网站:https://www.helloinbox.email/

其他命令

# 进入到容器
docker exec -it mailserver /bin/bash

# 根据提示
setup help

# 修改密码

setup email update airdict@iovhm.com <new password>

25出站端口被封,使用中继

配置为中继后,则不能使用直接出站了

增加中继配置

# 增加中继说明,既所有来自*@iovhm.com的邮件使用smtp.163.com发送
setup relay add-domain iovhm.com smtp.163.com 465

# 增加中继账号
setup relay add-auth iovhm.com your@163.com <your password>

# 增加中继排除,既这个域使用默认中继
setup relay exclude-domain vpclub.cn

需要增加3个配置文件
# 开启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


此处可以使用(强烈建议)内置的命令行增加


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


# 中继邮箱密码凭据
[smtp.163.com]:465 airdict@163.com:<your password>


使用postmap postfix-sasl-password.cf 生成db


# 中继邮箱密码凭据规则

/.+/	your@163.com


使用postmap postfix-sender-canonical.cf 生成db

室内地图制作方法

项目源代码

https://gitee.com/vpclub-wh/indoor-map

室内地图制作

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

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

制作地图形状数据

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

Tiled下载:https://www.mapeditor.org/

最终效果图

约定

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

进行区块标记

约定

tiledmap快捷键

opensearch

介绍

OpenSearch 是 自 从 Elasticsearch OSS 7.10.2,有一段时间闭源后重新开的一个分支,完全开源。包括一些插件也是开源的

安装



version: '3'
services:
  opensearch-node1: 
    image: harbor.iovhm.com/hub/opensearchproject/opensearch:2.19.1
    container_name: opensearch-node1
    privileged: true
    restart: always
    ports:
      - 9200:9200
      - 9300:9300
      - 9600:9600
    volumes:
      - ./data1:/usr/share/opensearch/data
    environment:
      # - discovery.type=single-node # 单节点
      - node.name=opensearch-node1 # 节点名称,唯一
      # - network.host=0.0.0.0 # 节点IP
      # - network.publish_host=172.16.2.106 # 发布地址
      - cluster.name=opensearch-cluster # 集群名称
      - discovery.seed_hosts=opensearch-node1,opensearch-node2 # 从指点主机发现节点
      - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # 集群节点成员
      - bootstrap.memory_lock=true # 启用内存锁定
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # 设置JVM堆内存大小
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=EMWPpRJ7NkVW # 设置初始管理员密码,需要2.12 版本后支持
    networks:
      - opensearch-net
  opensearch-node2:
    image: harbor.iovhm.com/hub/opensearchproject/opensearch:2.19.1
    container_name: opensearch-node2
    privileged: true
    restart: always
    ports:
      - 9201:9200
      - 9301:9300
      - 9601:9600
    volumes:
      - ./data2:/usr/share/opensearch/data
    environment:
      - node.name=opensearch-node2  # 节点名称,唯一
      # - network.host=0.0.0.0 # 节点IP
      # - network.publish_host=172.16.2.107 # 发布地址
      - cluster.name=opensearch-cluster # 集群名称
      - discovery.seed_hosts=opensearch-node1,opensearch-node2 # 从指点主机发现节点
      - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 # 集群节点成员
      - bootstrap.memory_lock=true # 启用内存锁定
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # 设置JVM堆内存大小
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=EMWPpRJ7NkVW # 设置初始管理员密码,需要2.12 版本后支持
    networks:
      - opensearch-net
  opensearch-dashboards:
    image: harbor.iovhm.com/hub/opensearchproject/opensearch-dashboards:2.19.1 
    container_name: opensearch-dashboards
    restart: always
    privileged: true
    ports:
      - 5601:5601
    expose:
      - "5601"
    environment:
      OPENSEARCH_HOSTS: '["https://opensearch-node1:9200","https://opensearch-node2:9201"]'
    networks:
      - opensearch-net

networks:
  opensearch-net:
    name: opensearch-net
    external: true

# https://opensearch.org/docs/latest/getting-started/quickstart/