<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>longjuan's Blog - 龙卷coding</title><link>https://www.zway.top</link><atom:link href="https://www.zway.top/rss.xml" rel="self" type="application/rss+xml"/><description>longjuan's Blog - 龙卷coding</description><generator>Halo v2.24.0</generator><language>zh-cn</language><lastBuildDate>Wed, 22 Apr 2026 23:03:05 GMT</lastBuildDate><item><title><![CDATA[1Panel + 雷池 WAF 旁路接入 2.0 —— v2 + 8.0.0-LTS 部署]]></title><link>https://www.zway.top/archives/1panel-lei-chi-waf-pang-lu-jie-ru-2.0-v2-8.0.0-lts-bu-shu</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=1Panel%20%2B%20%E9%9B%B7%E6%B1%A0%20WAF%20%E6%97%81%E8%B7%AF%E6%8E%A5%E5%85%A5%202.0%20%E2%80%94%E2%80%94%20v2%20%2B%208.0.0-LTS%20%E9%83%A8%E7%BD%B2&amp;url=/archives/1panel-lei-chi-waf-pang-lu-jie-ru-2.0-v2-8.0.0-lts-bu-shu" width="1" height="1" alt="" style="opacity:0;">
<p style="">前情提要：</p>
<hyperlink-card target="_blank" href="https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li" theme="regular" style="margin-top: 0.75em; margin-bottom: 0;"><a href="https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li" target="_blank">https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li</a></hyperlink-card>
<h1 style="" id="%E5%8D%87%E7%BA%A7%E5%8A%A8%E6%9C%BA"><strong>升级动机</strong></h1>
<p style="">在前文<hyperlink-inline-card target="_blank" href="https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li" theme="inline"><a href="https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li" target="_blank">https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li</a></hyperlink-inline-card> 写完后，我一直保持着这WAF旁路架构，即使WAF炸了也不影响我网站及应用的访问，近两年的时间也验证了这一架构的健壮性。</p>
<p style="">在前文完成2年后，1Panel v2发布了，引入了多节点管理、更完善的 Docker 管理功能，以及支持 OpenResty 自行编译等特性。在1Panel v2的早期版本中，更新日志中包含了大量的bug修复，数量太多以至于没有动力从v1升级到v2，甚至在 2.0.5 版本中存在高危的无需权限 RCE 漏洞。此时v1处于lts版本，运行稳定且更新更慢减少打扰，所以保持了一段时间不升级到v2。随着 2.0.8 的发布，其 Release 日志中的 Bug 修复明显减少，Issues 中的反馈频率也有所降低，同时我也有1Panel的永久授权，不用白不用，最终在 2.0.9 发布前进行了升级。</p>
<p style="">在早期使用雷池 WAF 时，并不十分省心，在我文章的更新记录中可以看出，雷池 WAF 经常更新版本，不仅修改 Docker Compose 文件和 .env 配置，有时还会调整 Socket 文件位置，升级过程中耗费大量时间用于排查问题。准备升级1Panel时，我也顺带看了下雷池的更新日志，发现我之前使用的 latest-stream 版本已久未更新。在更新日志中发现了其有发布多个lts版本，因此决定将雷池升级到 LTS 版本。lts版本有多个，看了用户相关反馈，新的lts可能削了首页看板/防护日志等关键功能，不能追新版本，选择了一个优化了防护引擎且保留功能的 8.0.0-LTS 版本。</p>
<h1 style="" id="%E6%96%B0%E7%89%88%E6%9C%AC%E5%85%B3%E9%94%AE%E5%8F%98%E6%9B%B4"><strong>新版本关键变更</strong></h1>
<h2 style="" id="1panel-v2"><strong>1Panel v2</strong></h2>
<p style="">官方亮点介绍：</p>
<blockquote>
 <p style=""><hyperlink-inline-card target="_blank" href="https://github.com/1Panel-dev/1Panel/releases/tag/v2.0.0" theme="inline"><a href="https://github.com/1Panel-dev/1Panel/releases/tag/v2.0.0" target="_blank">https://github.com/1Panel-dev/1Panel/releases/tag/v2.0.0</a></hyperlink-inline-card></p>
 <ul>
  <li>
   <p style=""><strong>新增多机管理能力</strong></p>
  </li>
  <li>
   <p style=""><strong>支持自定义仓库，离线环境也能安装应用</strong></p>
  </li>
  <li>
   <p style=""><strong>新增文件对传功能</strong></p>
  </li>
  <li>
   <p style=""><strong>网站管理功能全面重构</strong></p>
  </li>
  <li>
   <p style=""><strong>新增网站负载均衡功能</strong></p>
  </li>
  <li>
   <p style=""><strong>新增脚本库功能</strong></p>
  </li>
  <li>
   <p style=""><strong>容器页面重构</strong></p>
  </li>
  <li>
   <p style=""><strong>快照机制重构</strong></p>
  </li>
 </ul>
</blockquote>
<p style="">以上我都不太需要，我认为有价值的点：</p>
<ul>
 <li>
  <p style="">Docker 管理功能更全面</p>
 </li>
 <li>
  <p style="">OpenResty支持自己编译</p>
 </li>
</ul>
<h2 style="" id="%E9%9B%B7%E6%B1%A0-waf-8.0.0-lts"><strong>雷池 WAF 8.0.0-LTS</strong></h2>
<p style="">摘取官方我认为有用的更新点：</p>
<blockquote>
 <p style=""><hyperlink-inline-card target="_blank" href="https://help.waf-ce.chaitin.cn/node/0197787e-b70e-756e-b9d5-69d0a707144a" theme="inline"><a href="https://help.waf-ce.chaitin.cn/node/0197787e-b70e-756e-b9d5-69d0a707144a" target="_blank">https://help.waf-ce.chaitin.cn/node/0197787e-b70e-756e-b9d5-69d0a707144a</a></hyperlink-inline-card></p>
 <ul>
  <li>
   <p style="">引擎（7.4.0）</p>
   <ul>
    <li>
     <p style="">新增</p>
     <ul>
      <li>
       <p style="">大幅优化漏洞检测模块，支持更多漏洞</p>
      </li>
      <li>
       <p style="">新增大量加强规则</p>
      </li>
     </ul>
    </li>
    <li>
     <p style="">优化</p>
     <ul>
      <li>
       <p style="">HTTP 协议解析逻辑</p>
      </li>
      <li>
       <p style="">SQL 注入检测逻辑</p>
      </li>
      <li>
       <p style="">Java 代码注入检测逻辑</p>
      </li>
      <li>
       <p style="">PHP 代码注入检测逻辑</p>
      </li>
      <li>
       <p style="">JSON 解析解码逻辑</p>
      </li>
      <li>
       <p style="">斜杠反转义解码逻辑</p>
      </li>
     </ul>
    </li>
    <li>
     <p style="">修复</p>
     <ul>
      <li>
       <p style="">攻击检测部分接口偶现失效问题</p>
      </li>
      <li>
       <p style="">部分 XFF 空值的场景中，检测异常的问题</p>
      </li>
     </ul>
    </li>
    <li>
     <p style="">重构</p>
     <ul>
      <li>
       <p style="">JSP 代码检测逻辑，支持检测更多绕过</p>
      </li>
      <li>
       <p style="">Java 反序列化检测逻辑</p>
      </li>
     </ul>
    </li>
   </ul>
  </li>
  <li>
   <p style="">防护应用支持配置 SSE 流式响应（8.0.0）</p>
  </li>
  <li>
   <p style="">修复 SDK 旁路接入时 QPS 不显示的问题（8.0.0）</p>
  </li>
 </ul>
</blockquote>
<h1 style="" id="%E6%97%81%E8%B7%AF%E6%8E%A5%E5%85%A5%E6%9E%B6%E6%9E%84">旁路接入架构</h1>
<text-diagram data-type="plantuml" data-content="@startuml
actor User as U
participant OpenResty as O
participant Safeline as S
participant &quot;1Panel WAF/网站监控&quot; as P
participant 应用 as A

U -&gt; O : HTTP 请求
group try
    O -&gt; S : 调用 t1k (sock) 进行检测
    S --&gt; O 
    note right of O
      Safeline挂了时跳过检测
    end note
end

O -&gt; P : 调用 1Panel 内置 WAF/监控 (waf.lua)
P --&gt; O

O -&gt; A : 请求继续到文件/应用
@enduml
" data-src="https://www.plantuml.com/plantuml/svg/LL71IiD05BplLpnwQW-rzEf1cejuIp0DdfUugQ4n2Sa6wKqgje95A1H5UX8F5KIjY2qqbFvCixkVONPJqRpJxfkPDszsMp754KtEFOHj6aHWnIG278E5mhplscw8VGfwIFnZ4jEUXFGIP6A7UAvF96AMa4hJm3xnu4GxRF3rcBy-ykcOtSqgacgKgDbgmYSligyXP47z07JOXwDssm3niMIVVTI9WYG46lKGvALBYfbJnFnA2ccp2zKuiBir49kfU1gnvqlsVQku9jJLF7Nn0qeWSZjd508dJmAgVZEmKN-x6h27XLX-YStmtn3YdoejoWy8ANlZptuND1jSy_LxaLSbXUe5TlQy1DUGKMonqsfIguBnTCRJj-ncpkw7stJHA5u2jN8V-Ia_"><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fsvg%2FLL71IiD05BplLpnwQW-rzEf1cejuIp0DdfUugQ4n2Sa6wKqgje95A1H5UX8F5KIjY2qqbFvCixkVONPJqRpJxfkPDszsMp754KtEFOHj6aHWnIG278E5mhplscw8VGfwIFnZ4jEUXFGIP6A7UAvF96AMa4hJm3xnu4GxRF3rcBy-ykcOtSqgacgKgDbgmYSligyXP47z07JOXwDssm3niMIVVTI9WYG46lKGvALBYfbJnFnA2ccp2zKuiBir49kfU1gnvqlsVQku9jJLF7Nn0qeWSZjd508dJmAgVZEmKN-x6h27XLX-YStmtn3YdoejoWy8ANlZptuND1jSy_LxaLSbXUe5TlQy1DUGKMonqsfIguBnTCRJj-ncpkw7stJHA5u2jN8V-Ia_&amp;size=m"></text-diagram>
<p style=""></p>
<h1 style="" id="%E6%97%81%E8%B7%AF%E6%8E%A5%E5%85%A5%E5%AE%9E%E7%8E%B0%E6%AD%A5%E9%AA%A4"><strong>旁路接入实现步骤</strong></h1>
<h2 style="" id="%E5%AE%89%E8%A3%851panel">安装1Panel</h2>
<p style="">1Panel按官方文档安装或升级即可，无特殊操作。</p>
<p style="">安装完成后，在 1Panel 应用商店安装最新版本的 OpenResty，安装后确认网站能正常访问。</p>
<h2 style="" id="%E9%83%A8%E7%BD%B2%E9%9B%B7%E6%B1%A0">部署雷池</h2>
<h3 style="" id=".env%E6%96%87%E4%BB%B6">.env文件</h3>
<p style=""><span style="font-size: 15px; color: rgb(0, 0, 0)">新建</span><code>/data/safeline</code><span style="font-size: 15px; color: rgb(0, 0, 0)">目录，创建</span><code>.env</code><span style="font-size: 15px; color: rgb(0, 0, 0)">文件，并添加以下内容：</span></p>
<pre><code>SAFELINE_DIR=/data/safeline
POSTGRES_PASSWORD=随机一个密码
MGT_PORT=9443
RELEASE=-lts
CHANNEL=-lts
REGION=
IMAGE_PREFIX=chaitin
# 如果从dockerhub拉不下来可以换下面这个
# IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com/chaitin-safeline
IMAGE_TAG=8.0.0-lts
SUBNET_PREFIX=192.168.199
ARCH_SUFFIX=</code></pre>
<p style="">配置文件的格式说明如下：</p>
<ul>
 <li>
  <p style=""><strong>SAFELINE_DIR</strong>: 雷池安装目录，例如 <code>/data/safeline</code>，请根据实际情况调整</p>
 </li>
 <li>
  <p style=""><strong>POSTGRES_PASSWORD</strong>: 雷池数据库的密码，随机生成一个</p>
 </li>
 <li>
  <p style=""><strong>MGT_PORT</strong>: 雷池控制台的端口</p>
 </li>
 <li>
  <p style=""><strong>RELEASE</strong>: 更新通道，已配置为 lts 版本更新通道</p>
 </li>
 <li>
  <p style=""><strong>IMAGE_PREFIX</strong>: 雷池镜像源的前缀，建议根据服务器地理位置选择合适的源</p>
 </li>
 <li>
  <p style=""><strong>IMAGE_TAG</strong>: 要安装的雷池版本， 格式为<code>版本号-lts</code></p>
 </li>
 <li>
  <p style=""><strong>SUBNET_PREFIX</strong>: 雷池内部网络的网段，我这里为了防止ip冲突用了一个C段IP，可按实际情况设置</p>
 </li>
</ul>
<h3 style="" id="docker-compose%E6%96%87%E4%BB%B6">Docker Compose文件</h3>
<p style=""><span style="font-size: 15px; color: rgb(0, 0, 0)">在</span><code>/data/safeline</code><span style="font-size: 15px; color: rgb(0, 0, 0)">目录下创建</span><code>docker-compose.yml</code><span style="font-size: 15px; color: rgb(0, 0, 0)">文件，以下是我修改的Docker Compose文件：</span></p>
<pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-pg
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    command: [postgres, -c, max_connections=600]
    healthcheck:
      test: pg_isready -U safeline-ce -d safeline-ce
  mgt:
    container_name: safeline-mgt
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mgt${REGION}${ARCH_SUFFIX}:${IMAGE_TAG:?image tag required}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/mgt:/app/data
      - ${SAFELINE_DIR}/logs/nginx:/app/log/nginx:z
      - ${SAFELINE_DIR}/resources/sock:/app/sock
      - /var/run:/app/run
    ports:
      - ${MGT_PORT:-9443}:1443
    healthcheck:
      test: curl -k -f https://localhost:1443/api/open/health
    environment:
      - MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    depends_on:
      postgres:
        condition: service_healthy
      fvm:
        condition: service_started
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
  detect:
    container_name: safeline-detector
    restart: always
    image: ${IMAGE_PREFIX}/safeline-detector${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
  tengine:
    container_name: safeline-tengine
    image: ${IMAGE_PREFIX}/safeline-tengine${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    restart: "no"
    entrypoint:
      - sh
      - -c
      - |
       (sleep 600; echo "[INFO] 10 minutes passed. Stopping tengine..." &gt;&amp;2; kill -TERM 1) &amp; \
       exec entrypoint.sh nginx -g 'daemon off;'
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/resolv.conf:/etc/resolv.conf:ro
      - ${SAFELINE_DIR}/resources/nginx:/etc/nginx
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/resources/chaos:/resources/chaos
      - ${SAFELINE_DIR}/logs/nginx:/var/log/nginx:z
      - ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
      - ${SAFELINE_DIR}/resources/sock:/app/sock
    environment:
      - TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
      - TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
      # deprecated
      - SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
      - CHAOS_ADDR=${SUBNET_PREFIX}.10
    ulimits:
      nofile: 131072
    network_mode: host
  luigi:
    container_name: safeline-luigi
    restart: always
    image: ${IMAGE_PREFIX}/safeline-luigi${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    environment:
      - MGT_IP=${SUBNET_PREFIX}.4
      - LUIGI_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/luigi:/app/data
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"
    depends_on:
      detect:
        condition: service_started
      mgt:
        condition: service_healthy
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.7
  fvm:
    container_name: safeline-fvm
    restart: always
    image: ${IMAGE_PREFIX}/safeline-fvm${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.8
  chaos:
    container_name: safeline-chaos
    restart: always
    image: ${IMAGE_PREFIX}/safeline-chaos${REGION}${ARCH_SUFFIX}:${IMAGE_TAG}
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10"
    environment:
      - DB_ADDR=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    volumes:
      - ${SAFELINE_DIR}/resources/sock:/app/sock
      - ${SAFELINE_DIR}/resources/chaos:/app/chaos
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.10</code></pre>
<p style="">基于官方原版Compose文件，我修改了如下内容：</p>
<ul>
 <li>
  <p style="">完善depends_on配置，等待前置容器完全启动/正常工作后才启动下一个容器</p>
 </li>
 <li>
  <p style="">safeline-tengine容器10分钟后自动退出，避免系统资源浪费（本部署方式无需tengine）</p>
 </li>
 <li>
  <p style="">postgres数据库采用官方镜像</p>
 </li>
</ul>
<p style="">因使用lts版本，理论上以后docker compose文件不会有大变动了。</p>
<h3 style="" id="%E5%90%AF%E5%8A%A8%E9%9B%B7%E6%B1%A0">启动雷池</h3>
<p style="">启动前确保如下2个文件已正确保存，.env文件可能会隐藏。</p>
<pre><code>/data/safeline
├── docker-compose.yaml
└── .env</code></pre>
<p style="">在<code>/data/safeline</code>目录下使用以下命令启动服务：</p>
<pre><code class="language-shell">docker compose up -d</code></pre>
<p style="">启动后理论上文件夹下的内容：</p>
<pre><code>/data/safeline
├── 1panel.env
├── docker-compose.yaml
├── .env
├── logs
└── resources</code></pre>
<h2 style="" id="%E7%BC%96%E8%AF%91%E5%B9%B6%E9%85%8D%E7%BD%AEopenresty%E5%BA%94%E7%94%A8">编译并配置openresty应用</h2>
<p style="">t1k 是雷池提供的用于 OpenResty 旁路接入的 Lua SDK。基于 1Panel v2 可自行编译 OpenResty 的特性，可以在启动容器前就安装好SDK，相比以往在容器启动后安装，能显著提升启动速度。</p>
<p style="">打开网站-网站-设置-模块-创建，输入以下配置。</p>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F92A989F0-2144-4ACE-8217-CD01C1F4B80F.png&amp;size=m" width="100%" height="100%" style="display: inline-block"></p>
<ul>
 <li>
  <p style=""><strong>名称</strong>：随便起一个名。</p>
 </li>
 <li>
  <p style=""><strong>参数</strong>：<code>--with-compat</code> ，理论上无需参数，但 1Panel 此处必须填写一个，只能重申一次启动动态模块兼容性。</p>
 </li>
 <li>
  <p style=""><strong>脚本</strong>：<code>echo -e "\nluarocks install lua-resty-t1k\n" &gt;&gt; /tmp/default.sh</code> ，目的是在安装 LuaRocks 后，通过 /tmp/default.sh 安装 lua-resty-t1k 。</p>
 </li>
</ul>
<p style="">确认后打开开关并点击构建，等待构建完成。</p>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F377C6853-0224-4219-8834-AF8379D6AA34.png&amp;size=m" width="100%" height="100%" style="display: inline-block"></p>
<h2 style="" id="%E9%85%8D%E7%BD%AEopenresty%E6%97%81%E8%B7%AF%E6%8E%A5%E5%85%A5%E9%9B%B7%E6%B1%A0">配置openresty旁路接入雷池</h2>
<h3 style="" id="%E6%8C%82%E8%BD%BD%E5%A5%97%E6%8E%A5%E5%AD%97%E6%96%87%E4%BB%B6%E7%9B%AE%E5%BD%95">挂载套接字文件目录</h3>
<p style="">点击应用商店-已安装-openresty-参数-高级设置-编辑compose文件，打开compose文件编辑框。</p>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FE317B5BA-94C3-4BFD-84B4-B593CB361564.png&amp;size=m" width="100%" height="100%" style="display: inline-block"></p>
<p style="">在volumes下增加一行 <code>- /data/safeline/resources/detector:/resources/detector</code></p>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F07ABF6AD-7A5B-4F2F-AB24-D3E12AAFA089.png&amp;size=m" width="60%" height="auto" style="display: inline-block"></p>
<p style="">保存并重建openresty，建议重新编译一次以确保生效。</p>
<h3 style="" id="%E7%AB%99%E7%82%B9%E9%85%8D%E7%BD%AE">站点配置</h3>
<h4 style="" id="%E5%88%9B%E5%BB%BA%E9%80%9A%E7%94%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6">创建通用配置文件</h4>
<p style="">在1Panel网站目录下创建common目录，网站目录可在<u>应用-OpenResty-参数</u>中查看，例如我的在 <code>/opt/1panel/www/common</code></p>
<p style="">创建文件并添加相应内容：</p>
<p style=""><strong>t1k.conf </strong><code>/opt/1panel/www/common/t1k.conf</code></p>
<pre><code class="language-nginx">access_by_lua_block {
    local t1k = require "resty.t1k"

    local t = {
        mode = "block", -- block or monitor or off, default off
        host = "unix:/resources/detector/snserver.sock",
        port = 8000,
        connect_timeout = 1000,
        send_timeout = 1000,
        read_timeout = 1000,
        req_body_size = 1024,
        keepalive_size = 256,
        keepalive_timeout = 60000,
        remote_addr = "http_x_forwarded_for: 1",
    }

    local ok, err, _ = t1k.do_access(t, true)
    if not ok then
        ngx.log(ngx.ERR, err)
    end
    
    local f, err = loadfile("/usr/local/openresty/1pwaf/waf.lua")
    if not f then
        ngx.log(ngx.ERR, "loadfile failed: ", err)
    else
        local ok, perr = pcall(f)
        if not ok then
            ngx.log(ngx.ERR, "pcall loadfile error: ", perr)
        end
    end
}

header_filter_by_lua_block {
    local t1k = require "resty.t1k"
    t1k.do_header_filter()
}</code></pre>
<p style=""><strong>t1k_static_stie_location_root.conf </strong><code>/opt/1panel/www/common/t1k_static_stie_location_root.conf</code></p>
<pre><code class="language-nginx">location ~ / {
    index index.html index.htm index.php default.php default.htm default.html; 
    include /www/common/t1k.conf;
}</code></pre>
<p style="">此配置提供了 Lua 脚本处理模板：请求先通过雷池检测，再经过 1Panel 自身的检测（网站监控 + WAF），这样能让雷池和1Panel的WAF共同工作。</p>
<h4 style="" id="%E7%BD%91%E7%AB%99%E6%8E%A5%E5%85%A5%E9%9B%B7%E6%B1%A0">网站接入雷池</h4>
<p style="">1Panel站点的反向代理配置文件中包含以下行以完成配置：</p>
<pre><code class="language-nginx">include /www/common/t1k.conf;</code></pre>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FC1F92976-5BFC-4D9A-8466-E6AD2C64FBA3.png&amp;size=m" alt="C1F92976-5BFC-4D9A-8466-E6AD2C64FBA3.png" width="100%" height="100%" style="display: inline-block"></p>
<p style="">或在静态站点的配置文件中添加：</p>
<pre><code class="language-nginx">include /www/common/t1k_static_stie_location_root.conf;</code></pre>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FC5C13FE5-D539-4589-BB7C-B5C8D6EA68AD.png&amp;size=m" alt="C5C13FE5-D539-4589-BB7C-B5C8D6EA68AD.png" width="100%" height="100%" style="display: inline-block"></p>
<h1 style="" id="%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88"><strong>问题与解决方案</strong></h1>
<h2 style="" id="halo%E6%AD%A3%E5%B8%B8%E6%8E%A5%E5%8F%A3%E8%AF%AF%E6%8A%A5">Halo正常接口误报</h2>
<p style=""><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F237D9448-A70D-4898-8F37-5786252A4940.png&amp;size=m" width="100%" height="100%" style="display: inline-block"></p>
<p style="">自定义规则中订阅在线规则就行。</p>
<h1 style="" id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p style="">升级这事儿，说到底就是个平衡。系统跑得稳是第一位的，但偶尔尝点新鲜也能让架构更顺手。1Panel 从 v1 换到 v2，雷池上 LTS，其实也就是在“别瞎折腾”和“别太保守”之间找个舒服的点。</p>
<p style="">折腾多了人会累，版本更新也追不完。挑准时机，动一次就够了，剩下的时间还是留给生活比较实在。</p>]]></description><guid isPermaLink="false">/archives/1panel-lei-chi-waf-pang-lu-jie-ru-2.0-v2-8.0.0-lts-bu-shu</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F573AEF02-1C1C-4B47-B59B-774617FEEF0E.png&amp;size=m" type="image/jpeg" length="26891"/><category>运维</category><pubDate>Sun, 31 Aug 2025 06:16:37 GMT</pubDate></item><item><title><![CDATA[Halo 2.10.2 不同 JVM 压测]]></title><link>https://www.zway.top/archives/halo-2.10.2-bu-tong-jvm-ya-ce</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=Halo%202.10.2%20%E4%B8%8D%E5%90%8C%20JVM%20%E5%8E%8B%E6%B5%8B&amp;url=/archives/halo-2.10.2-bu-tong-jvm-ya-ce" width="1" height="1" alt="" style="opacity:0;">
<h2 id="压测环境">压测环境</h2>
<h3 id="硬件配置">硬件配置</h3>
<ul>
 <li><strong>CPU:</strong> Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz (4核)</li>
 <li><strong>内存:</strong> 4GB</li>
</ul>
<h3 id="操作系统">操作系统</h3>
<ul>
 <li><strong>操作系统:</strong> Debian GNU/Linux 12 (bookworm)</li>
</ul>
<h3 id="容器配置">容器配置</h3>
<pre><code class="language-Plain">Server: Docker Engine - Community
 Engine:
  Version:          24.0.7
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.10
  Git commit:       311b9ff
  Built:            Thu Oct 26 09:08:02 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.24
  GitCommit:        61f9fd88f79f081d64d6fa3bb1a0dc71ec870523
 runc:
  Version:          1.1.9
  GitCommit:        v1.1.9-0-gccaecfc
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
</code></pre>
<h3 id="数据库">数据库</h3>
<ul>
 <li><strong>MySQL:</strong> 8.1.0 (Docker 部署)</li>
</ul>
<h3 id="压测工具">压测工具</h3>
<ul>
 <li><strong>Vegeta:</strong> 12.11.1</li>
</ul>
<h2 id="压测策略">压测策略</h2>
<h3 id="压测urls">压测URLs</h3>
<pre><code class="language-Plain"># 首页
GET http://10.0.12.12:8090/
GET http://10.0.12.12:8090/themes/theme-earth/assets/dist/style.css?v=1.6.0
GET http://10.0.12.12:8090/themes/theme-earth/assets/dist/main.iife.js?v=1.6.0
GET http://10.0.12.12:8090/plugins/PluginSearchWidget/assets/static/search-widget.iife.js
GET http://10.0.12.12:8090/themes/theme-earth/assets/images/default-avatar.svg
GET http://10.0.12.12:8090/themes/theme-earth/assets/images/default-background.png
GET http://10.0.12.12:8090/plugins/PluginSearchWidget/assets/static/style.css
# 文章页
GET http://10.0.12.12:8090/archives/ci-cd-shi-jian-ji-yu-hua-wei-yun-devcloud
GET http://10.0.12.12:8090/themes/theme-earth/assets/dist/style.css?v=1.6.0
GET http://10.0.12.12:8090/themes/theme-earth/assets/dist/main.iife.js?v=1.6.0
GET http://10.0.12.12:8090/plugins/PluginLightGallery/assets/static/css/lightgallery.min.css
GET http://10.0.12.12:8090/plugins/PluginLightGallery/assets/static/js/lightgallery.min.js
GET http://10.0.12.12:8090/plugins/PluginHighlightJS/assets/static/styles/default.min.css
GET http://10.0.12.12:8090/plugins/PluginHighlightJS/assets/static/highlight.min.js
GET http://10.0.12.12:8090/plugins/PluginSearchWidget/assets/static/search-widget.iife.js
GET http://10.0.12.12:8090/halo-tracker.js
GET http://10.0.12.12:8090/themes/theme-earth/assets/images/default-avatar.svg
GET http://10.0.12.12:8090/plugins/PluginCommentWidget/assets/static/comment-widget.iife.js?version=1.8.0
GET http://10.0.12.12:8090/plugins/PluginSearchWidget/assets/static/style.css
GET http://10.0.12.12:8090/plugins/PluginCommentWidget/assets/static/style.css?version=1.8.0
GET http://10.0.12.12:8090/actuator/globalinfo
GET http://10.0.12.12:8090/apis/api.console.halo.run/v1alpha1/users/-
GET http://10.0.12.12:8090/apis/api.halo.run/v1alpha1/comments?group=content.halo.run&amp;kind=Post&amp;name=405c9241-262c-478b-83b1-c95ef162a774&amp;page=1&amp;size=20&amp;version=v1alpha1
POST http://10.0.12.12:8090/apis/api.halo.run/v1alpha1/trackers/counter
Content-Type: application/json
@/root/vegeta/counter.json
</code></pre>
<h3 id="压测策略说明">压测策略说明</h3>
<p>每次压测后，容器将被重新启动。会等待CPU使用率降至正常值，然后等待至少1分钟，然后再次开始压测。</p>
<h3 id="使用的镜像">使用的镜像</h3>
<p>openj9：<code>ruibaby/halo:2.10.2-openj9</code></p>
<p>hotspot：<code>halohub/halo:2.10.2</code></p>
<h3 id="halo-主题及启动的插件">Halo 主题及启动的插件</h3>
<p>主题：Earth</p>
<p>插件</p>
<ul>
 <li>lightgallery.js 灯箱</li>
 <li>highlight.js 代码高亮</li>
 <li>站点迁移</li>
 <li>RSS</li>
 <li>应用市场</li>
 <li>Sitemap</li>
 <li>搜索组件</li>
 <li>评论组件</li>
</ul>
<h3 id="测试前置处理">测试前置处理</h3>
<p>使用站点迁移工具从各大优质rss源中迁移文章200篇</p>
<p>测试的文章拥有多张图片与多个代码块</p>
<h2 id="测试结果">测试结果</h2>
<h3 id="10s-100qps">10s-100qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         1000, 100.10, 87.59
Duration      [total, attack, wait]             11.417s, 9.99s, 1.427s
Latencies     [min, mean, 50, 90, 95, 99, max]  1.936ms, 370.937ms, 43.089ms, 296.51ms, 3.618s, 4.74s, 5.107s
Bytes In      [total, mean]                     67864000, 67864.00
Bytes Out     [total, mean]                     11603, 11.60
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:1000  
Error Set:
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         1000, 100.10, 60.64
Duration      [total, attack, wait]             16.491s, 9.99s, 6.501s
Latencies     [min, mean, 50, 90, 95, 99, max]  2.733ms, 1.118s, 69.941ms, 5.119s, 9.517s, 11.285s, 12.709s
Bytes In      [total, mean]                     67864000, 67864.00
Bytes Out     [total, mean]                     11603, 11.60
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:1000  
Error Set:
</code></pre>
<h3 id="10s-200qps">10s-200qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         1999, 199.92, 93.17
Duration      [total, attack, wait]             21.455s, 9.999s, 11.456s
Latencies     [min, mean, 50, 90, 95, 99, max]  40.332ms, 3.309s, 2.161s, 9.715s, 12.452s, 15.193s, 18.528s
Bytes In      [total, mean]                     135559327, 67813.57
Bytes Out     [total, mean]                     23489, 11.75
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:1999  
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         2000, 200.10, 60.40
Duration      [total, attack, wait]             33.111s, 9.995s, 23.116s
Latencies     [min, mean, 50, 90, 95, 99, max]  73.944ms, 4.882s, 2.811s, 15.635s, 21.857s, 26.002s, 29.572s
Bytes In      [total, mean]                     135604054, 67802.03
Bytes Out     [total, mean]                     23489, 11.74
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:2000  
</code></pre>
<h3 id="10s-500qps">10s-500qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         5000, 500.09, 116.97
Duration      [total, attack, wait]             39.541s, 9.998s, 29.542s
Latencies     [min, mean, 50, 90, 95, 99, max]  83.93ms, 14.817s, 13.988s, 28.724s, 30s, 30.001s, 30.002s
Bytes In      [total, mean]                     325536885, 65107.38
Bytes Out     [total, mean]                     58581, 11.72
Success       [ratio]                           92.50%
Status Codes  [code:count]                      0:375  200:4625  
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         5000, 500.09, 108.43
Duration      [total, attack, wait]             39.999s, 9.998s, 30.001s
Latencies     [min, mean, 50, 90, 95, 99, max]  46.311ms, 16.159s, 14.094s, 30s, 30.001s, 30.001s, 30.01s
Bytes In      [total, mean]                     320179817, 64035.96
Bytes Out     [total, mean]                     51223, 10.24
Success       [ratio]                           86.74%
Status Codes  [code:count]                      0:663  200:4337  
</code></pre>
<h3 id="60s-50qps">60s-50qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         3000, 50.02, 50.01
Duration      [total, attack, wait]             59.984s, 59.98s, 4.444ms
Latencies     [min, mean, 50, 90, 95, 99, max]  1.101ms, 20.5ms, 6.825ms, 39.627ms, 91.104ms, 180.021ms, 1.737s
Bytes In      [total, mean]                     203317750, 67772.58
Bytes Out     [total, mean]                     35375, 11.79
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:3000  
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         3000, 50.02, 50.01
Duration      [total, attack, wait]             59.991s, 59.981s, 10.475ms
Latencies     [min, mean, 50, 90, 95, 99, max]  1.914ms, 68.709ms, 11.285ms, 115.99ms, 200.417ms, 1.821s, 4.504s
Bytes In      [total, mean]                     203317875, 67772.62
Bytes Out     [total, mean]                     35375, 11.79
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:3000  
</code></pre>
<h3 id="60s-100qps">60s-100qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         6000, 100.02, 100.01
Duration      [total, attack, wait]             59.994s, 59.99s, 3.833ms
Latencies     [min, mean, 50, 90, 95, 99, max]  760.144µs, 15.17ms, 5.652ms, 26.923ms, 86.538ms, 122.81ms, 209.832ms
Bytes In      [total, mean]                     406635250, 67772.54
Bytes Out     [total, mean]                     70750, 11.79
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:6000  
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         6000, 100.02, 88.73
Duration      [total, attack, wait]             1m7s, 59.99s, 6.739s
Latencies     [min, mean, 50, 90, 95, 99, max]  1.093ms, 3.25s, 36.093ms, 14.195s, 20.233s, 30s, 30.003s
Bytes In      [total, mean]                     404125771, 67354.30
Bytes Out     [total, mean]                     70750, 11.79
Success       [ratio]                           98.68%
Status Codes  [code:count]                      0:79  200:5921  
</code></pre>
<h3 id="60s-200qps">60s-200qps</h3>
<p>hotspot</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         12000, 200.02, 152.98
Duration      [total, attack, wait]             1m15s, 59.995s, 15.02s
Latencies     [min, mean, 50, 90, 95, 99, max]  562.5µs, 4.459s, 88.189ms, 20.631s, 28.414s, 30.001s, 30.01s
Bytes In      [total, mean]                     795863770, 66321.98
Bytes Out     [total, mean]                     141217, 11.77
Success       [ratio]                           95.63%
Status Codes  [code:count]                      0:524  200:11476  
</code></pre>
<p>openj9</p>
<pre><code class="language-Plain">Requests      [total, rate, throughput]         12000, 200.02, 129.60
Duration      [total, attack, wait]             1m23s, 59.995s, 22.737s
Latencies     [min, mean, 50, 90, 95, 99, max]  40.828ms, 7.661s, 2.905s, 29.984s, 30.001s, 30.001s, 30.018s
Bytes In      [total, mean]                     777085785, 64757.15
Bytes Out     [total, mean]                     128765, 10.73
Success       [ratio]                           89.35%
Status Codes  [code:count]                      0:1278  200:10722  
</code></pre>]]></description><guid isPermaLink="false">/archives/halo-2.10.2-bu-tong-jvm-ya-ce</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F9D968FC4-C7DB-4C8D-AE1F-234EF5C491DB.webp&amp;size=m" type="image/jpeg" length="38550"/><category>开源</category><pubDate>Mon, 4 Dec 2023 02:58:00 GMT</pubDate></item><item><title><![CDATA[1Panel旁路接入雷池WAF（非反向代理套娃）]]></title><link>https://www.zway.top/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=1Panel%E6%97%81%E8%B7%AF%E6%8E%A5%E5%85%A5%E9%9B%B7%E6%B1%A0WAF%EF%BC%88%E9%9D%9E%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E5%A5%97%E5%A8%83%EF%BC%89&amp;url=/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li" width="1" height="1" alt="" style="opacity:0;">
<p>新文章： <a href="https://www.zway.top/archives/1panel-lei-chi-waf-pang-lu-jie-ru-2.0-v2-8.0.0-lts-bu-shu">1Panel + 雷池 WAF 旁路接入 2.0 —— v2 + 8.0.0-LTS 部署</a></p>
<h1 id="选型">选型</h1>
<p>在WAF选型过程中，考虑了两款WAF解决方案：南墙WAF和雷池WAF。通过GitHub社区活跃度指标来看，雷池WAF的社区活跃度明显领先于南墙WAF。然而，鉴于我缺乏进行拦截效果测试的能力，我无法确定哪款WAF在拦截效果方面更为优越。</p>
<p>在 1Panel 的问题反馈中，雷池WAF的开发人员推荐了一种旁路模式接入方式，使用 t1k Lua 模块，该方式可实现WAF接入，而无需进行大规模的网络架构修改。</p>
<p>本文实践了在保留 1Panel 的站点功能的同时，无需修改端口，无需在WAF里进行其他操作，即可接入WAF，从而实现无感接入，和正常使用 1Panel 的站点管理功能无异。</p>
<h1 id="雷池安装程序分析">雷池安装程序分析</h1>
<p>雷池的一键安装是以shell脚本的形式分发，将其下载下来进行分析。</p>
<pre><code class="language-bash">bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
</code></pre>
<p>将脚本内容交给ChatGPT分析，得到以下回应：</p>
<blockquote>
 <p>这个安装脚本的具体意图是安装雷池（SafeLine）WAF社区版。下面是脚本的主要步骤和功能：</p>
 <ol>
  <li>显示雷池（SafeLine）WAF的标志性图形。</li>
  <li>定义了一个名为<code>qrcode</code>的函数，用于显示雷池项目讨论组的二维码。</li>
  <li>定义了几个辅助函数：
   <ul>
    <li><code>command_exists</code>：检查命令是否存在。</li>
    <li><code>space_left</code>：检查指定目录的剩余存储空间。</li>
    <li><code>start_docker</code>：启动并启用Docker服务。</li>
    <li><code>confirm</code>：用于确认用户的输入，提示用户是否继续操作。</li>
    <li><code>info</code>：用于显示一般信息。</li>
    <li><code>warning</code>：用于显示警告信息。</li>
    <li><code>abort</code>：用于显示错误信息并退出脚本。</li>
   </ul></li>
  <li>检查CPU是否支持ssse3指令集，如果不支持则终止安装。</li>
  <li>设置安装目录为<code>/data/safeline</code>，用户可以选择自定义目录。</li>
  <li>检查脚本是否在Bash中运行，是否为标准输入设备，是否以root权限运行，并确认脚本调用方式正常。</li>
  <li>检查Docker环境是否已安装，如果没有安装，则提示用户是否自动安装Docker。</li>
  <li>检查Docker服务状态，如果异常则终止安装。</li>
  <li>检查Docker Compose是否已安装，如果没有安装则提示用户是否自动安装Docker Compose。</li>
  <li>提示用户输入雷池安装目录，默认为<code>/data/safeline</code>，并检查目录的存储空间是否足够。</li>
  <li>创建安装目录，并下载<code>compose.yaml</code>文件和<code>.env</code>文件。</li>
  <li>在<code>.env</code>文件中设置一些配置参数，如安装目录、镜像标签、管理端口等。</li>
  <li>开始下载Docker镜像，并启动Docker容器。</li>
  <li>显示雷池WAF社区版安装成功的二维码，并提供访问控制台的链接。</li>
 </ol>
 <p>总的来说，这个脚本的目的是帮助用户安装雷池WAF社区版，它会自动安装Docker和Docker Compose，然后下载所需的镜像和配置文件，并最终启动WAF服务。用户可以在安装过程中自定义安装目录等配置信息。</p>
</blockquote>
<p>可以看到脚本内容主要是以检查环境，创建文件夹及<code>.env</code>文件和启动docker-compose为主。</p>
<p>写入<code>.env</code>文件的内容如下：</p>
<pre><code class="language-shell">echo "SAFELINE_DIR=$safeline_path" &gt;&gt; .env
echo "IMAGE_TAG=latest" &gt;&gt; .env
echo "MGT_PORT=9443" &gt;&gt; .env
    echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 &lt;/dev/urandom | head -c 32)" &gt;&gt; .env
echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 &lt;/dev/urandom | head -c 32)" &gt;&gt; .env
echo "SUBNET_PREFIX=172.22.222" &gt;&gt; .env
</code></pre>
<p>这些内容后续可手动填写，无需使用安装脚本。</p>
<p>接着将<code>compose.yaml</code>文件下载下来进行分析。在这个Docker Compose文件中，定义了以下几个服务：</p>
<ol>
 <li><code>postgres</code>（PostgreSQL数据库服务）：用于存储SafeLine WAF社区版的数据，包括配置信息和日志。</li>
 <li><code>redis</code>（Redis服务）：用于作为缓存和消息队列，以提供快速的数据访问和通信。</li>
 <li><code>management</code>（SafeLine管理API服务）：这是SafeLine WAF的管理接口，允许用户配置和管理WAF的规则和策略。</li>
 <li><code>detector</code>（SafeLine Detector服务）：SafeLine WAF的核心组件，用于检测和拦截恶意流量。</li>
 <li><code>mario</code>（SafeLine Mario服务）：SafeLine WAF的一个组件，用于处理日志数据和性能监控。</li>
 <li><code>tengine</code>（SafeLine Tengine服务）：一个Web服务器，用于代理和处理网络流量，实现Web应用防火墙的功能。</li>
</ol>
<p>我想实现的是WAF的旁路接入，因此首先要干掉的是<code>tengine</code>，再在1Panel中的<code>OpenResty</code>安装<code>t1k</code>。</p>
<h1 id="1panel--雷池部署配置">1Panel + 雷池部署配置</h1>
<h2 id="env文件">.env文件</h2>
<p>在<code>/data/safeline</code>目录下创建<code>.env</code>文件，并添加以下内容：</p>
<pre><code class="language-properties">SAFELINE_DIR=/data/safeline
MGT_PORT=9443
POSTGRES_PASSWORD=postgres密码
REDIS_PASSWORD=redis密码
SUBNET_PREFIX=192.168.199
IMAGE_TAG=latest-stream
IMAGE_PREFIX=chaitin
</code></pre>
<p>您需要手动填写这些配置信息。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F983E4361-4677-43EE-8112-92802D2F21E0.png&amp;size=m" alt="983E4361-4677-43EE-8112-92802D2F21E0.png"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F6E00C1C6-270C-42E4-925F-ABC232C4FD55.png&amp;size=m" alt="6E00C1C6-270C-42E4-925F-ABC232C4FD55.png"></p>
<h2 id="docker-compose文件">Docker Compose文件</h2>
<p>在<code>/data/safeline</code>目录下创建<code>docker-compose.yml</code>文件，以下是我修改的Docker Compose文件：</p>
<details>
 <summary>docker-compose.yml</summary>
 <pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
      - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
        subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-postgres
    restart: always
    image: postgres:15.2
    volumes:
    - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
    - /etc/localtime:/etc/localtime:ro
    environment:
    - POSTGRES_USER=safeline-ce
    - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    cap_drop:
    - net_raw
    command: [postgres, -c, max_connections=200]
  redis:
    container_name: safeline-redis
    restart: always
    image: redis:7.0.10
    volumes:
      - ${SAFELINE_DIR}/resources/redis/data:/data
      - /etc/localtime:/etc/localtime:ro
    command: redis-server --appendonly yes --requirepass  ${REDIS_PASSWORD}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.3
    cap_drop:
      - net_raw
    sysctls:
      net.core.somaxconn: "511"
  management:
    container_name: safeline-mgt-api
    restart: always
    image: chaitin/safeline-mgt-api:${IMAGE_TAG:?image tag required}
    volumes:
    - ${SAFELINE_DIR?safeline dir required}/resources/management:/resources/management
    - ${SAFELINE_DIR}/resources/nginx:/resources/nginx
    - ${SAFELINE_DIR}/logs:/logs
    - /etc/localtime:/etc/localtime:ro
    ports:
    - ${MGT_PORT:-9443}:1443
    environment:
    - MANAGEMENT_RESOURCES_DIR=/resources/management
    - NGINX_RESOURCES_DIR=/resources/nginx
    - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
    - MARIO_URL=http://safeline-mario:3335
    - DETECTOR_URL=http://safeline-detector:8001
    - REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
    - MANAGEMENT_LOGS_DIR=/logs/management
    dns: 
      - 119.29.29.29
      - 223.5.5.5
      - 180.76.76.76
      - 1.2.4.8
      - 114.114.114.114
      - 8.8.8.8
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
    cap_drop:
    - net_raw
  detector:
    container_name: safeline-detector
    restart: always
    image: chaitin/safeline-detector:${IMAGE_TAG}
    volumes:
    - ${SAFELINE_DIR}/resources/detector:/resources/detector
    - ${SAFELINE_DIR}/logs/detector:/logs/detector
    - /etc/localtime:/etc/localtime:ro
    environment:
    - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
    cap_drop:
    - net_raw
  mario:
    container_name: safeline-mario
    restart: always
    image: chaitin/safeline-mario:${IMAGE_TAG}
    volumes:
    - ${SAFELINE_DIR}/resources/mario:/resources/mario
    - ${SAFELINE_DIR}/logs/mario:/logs/mario
    - /etc/localtime:/etc/localtime:ro
    environment:
    - LOG_DIR=/logs/mario
    - GOGC=100
    - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
    - REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.6
    cap_drop:
    - net_raw
</code></pre>
</details>
<blockquote>
 <p>compose 文件经常更新，详见下方文章更新记录。</p>
</blockquote>
<h2 id="启动">启动</h2>
<p>在<code>/data/safeline</code>目录下使用以下命令启动服务：</p>
<pre><code class="language-shell">docker compose up -d
</code></pre>
<h2 id="1panel的openresty替换tengine">1Panel的OpenResty替换tengine</h2>
<p>在正常安装并配置1Panel后，在应用商店安装<code>OpenResty</code>。</p>
<p>请注意，应用商店安装的<code>OpenResty</code>可能不包含<code>t1k</code>模块，因此您可以在<code>docker-compose</code>文件中添加安装命令，如下所示：</p>
<pre><code class="language-yaml">        entrypoint:
            - /bin/sh
            - -c
            - |
              luarocks install lua-resty-t1k --server https://luarocks.cn
              ln -s /usr/local/openresty/luajit/share/lua/5.1/resty/* /usr/local/openresty/lualib/resty/
              /usr/local/openresty/bin/openresty -g "daemon off;"
        volumes:
            - /data/safeline/resources/detector:/resources/detector
</code></pre>
<p>这将在启动<code>OpenResty</code>之前安装<code>t1k</code>模块，以避免lua文件找不到的问题。</p>
<p>同时，将<code>/data/safeline/resources/detector</code>映射到容器内部，以建立t1k与WAF之间的通讯。</p>
<blockquote>
 <p>OpenResty镜像无论怎么更新，都是在compose文件上加上面的内容。</p>
</blockquote>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F61DC7CA1-8CFB-4ED8-95EA-AFA565F81025.png&amp;size=m" alt="61DC7CA1-8CFB-4ED8-95EA-AFA565F81025.png"></p>
<h2 id="站点配置">站点配置</h2>
<p>创建站点的反向代理配置文件，例如<code>/opt/1panel/apps/openresty/openresty/www/common/t1k.conf</code>，并将以下内容添加到配置文件中：</p>
<pre><code class="language-nginx">access_by_lua_block {
    local t1k = require "resty.t1k"

    local t = {
        mode = "block",
        host = "unix:/resources/detector/snserver.sock",
        port = 8000,
        connect_timeout = 1000,
        send_timeout = 1000,
        read_timeout = 1000,
        req_body_size = 1024,
        keepalive_size = 256,
        keepalive_timeout = 60000,
        remote_addr = "http_x_forwarded_for: 1",
    }

    local ok, err, _ = t1k.do_access(t, true)
    if not ok then
        ngx.log(ngx.ERR, err)
    end
}

header_filter_by_lua_block {
    local t1k = require "resty.t1k"
    t1k.do_header_filter()
}
</code></pre>
<p>由于 1Panel 的静态站点不使用 location 块，所以无法直接使用上面的 conf。</p>
<p>如需对静态站点也套上WAF，可以新建<code>t1k_static_stie_location_root.conf</code>，添加如下内容：</p>
<pre><code class="language-nginx">location ~ / {
    index index.html index.htm index.php default.php default.htm default.html; 
    include /www/common/t1k.conf;
}
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FB20B9718-8993-45F7-B6CB-89FA0C4E1A7E.png&amp;size=m" alt="B20B9718-8993-45F7-B6CB-89FA0C4E1A7E.png"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F915CA82A-6B62-45F5-B3FF-7E41D2AA7BC0.png&amp;size=m" alt="915CA82A-6B62-45F5-B3FF-7E41D2AA7BC0.png"></p>
<p>最后，在1Panel站点的反向代理配置文件中包含以下行以完成配置：</p>
<pre><code class="language-nginx">include /www/common/t1k.conf;
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FC1F92976-5BFC-4D9A-8466-E6AD2C64FBA3.png&amp;size=m" alt="C1F92976-5BFC-4D9A-8466-E6AD2C64FBA3.png"></p>
<p>或在静态站点的配置文件中添加：</p>
<pre><code class="language-nginx">include /www/common/t1k_static_stie_location_root.conf;
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FC5C13FE5-D539-4589-BB7C-B5C8D6EA68AD.png&amp;size=m" alt="C5C13FE5-D539-4589-BB7C-B5C8D6EA68AD.png"></p>
<h2 id="其他设置">其他设置</h2>
<p>新版本的需要在<code>/data/safeline/resources/detector/detector.yml</code>中修改</p>
<pre><code>bind_addr: unix:///resources/detector/snserver.sock
</code></pre>
<p>确保文件中<code>bind_addr</code>未被注释且值正确</p>
<h2 id="验证">验证</h2>
<p>访问您的站点的<code>/shell.php</code>页面，如果看到<code>blocked by Chaitin SafeLine Web Application Firewall</code>的消息并且在9443端口的管理界面中有相应的记录，则说明您已成功实现了WAF的防护。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F3F37C57B-FA6E-43CE-83CF-F470D4B499E9.png&amp;size=m" alt="3F37C57B-FA6E-43CE-83CF-F470D4B499E9.png"></p>
<h1 id="后记">后记</h1>
<p>尽管按照这种方法能够完美地利用 1Panel 的站点管理功能以及雷池的WAF，但在使用几天后，我发现了一些麻烦。</p>
<p>在撰写这篇博客时，我发现 Halo 一直在频繁报错（403），导致我无法编辑和保存博客。这可能是因为博客中包含了大量代码，被WAF误认为是恶意请求而触发了阻拦。至于雷池，目前我还没有找到如何将其加入白名单的方法，只能将所有检测功能设为“仅观察”。</p>
<p>在之后的使用中，我可能会根据被误拦截的情况来决定是否继续使用这种接入方法，或许需要更换其他的WAF。</p>
<h2 id="20231130-更新">2023.11.30 更新</h2>
<p>用着先把，加上白名单又不是不能用，我很少发和修改我的博客。</p>
<p>另外 3.14.1 版本更新好大，yaml 文件要改很多：</p>
<details>
 <summary>点击查看YAML</summary>
 <pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-postgres
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    cap_drop:
      - net_raw
    command: [postgres, -c, max_connections=200]
  management:
    container_name: safeline-mgt-api
    restart: always
    image: chaitin/safeline-mgt-api:${IMAGE_TAG:?image tag required}
    volumes:
      - ${SAFELINE_DIR?safeline dir required}/resources/management:/resources/management
      - ${SAFELINE_DIR}/resources/nginx:/resources/nginx
      - ${SAFELINE_DIR}/logs:/logs
      - /etc/localtime:/etc/localtime:ro
    ports:
      - ${MGT_PORT:-9443}:1443
    environment:
      - MANAGEMENT_RESOURCES_DIR=/resources/management
      - NGINX_RESOURCES_DIR=/resources/nginx
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
      - MARIO_URL=http://safeline-mario:3335
      - FVM_MANAGER_URL=safeline-fvm-manager:9004
      - MANAGEMENT_LOGS_DIR=/logs/management
    dns:
      - 119.29.29.29
      - 223.5.5.5
      - 180.76.76.76
      - 1.2.4.8
      - 114.114.114.114
      - 8.8.8.8
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
    cap_drop:
      - net_raw
  detector:
    container_name: safeline-detector
    restart: always
    image: chaitin/safeline-detector:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
    cap_drop:
      - net_raw
  mario:
    container_name: safeline-mario
    restart: always
    image: chaitin/safeline-mario:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/mario:/resources/mario
      - ${SAFELINE_DIR}/logs/mario:/logs/mario
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/mario
      - GOGC=100
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.6
    cap_drop:
      - net_raw
  fvm-manager:
    container_name: safeline-fvm-manager
    restart: always
    image: chaitin/safeline-fvm-manager:${IMAGE_TAG}
    environment:
      - FVM_LOGS_DIR=/logs/management
      - DETECTOR_URL=http://safeline-detector:8001
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/logs:/logs
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.8
    cap_drop:
      - net_raw
</code></pre>
</details>
<h2 id="2024229-更新">2024.2.29 更新</h2>
<details>
 <summary>点击查看YAML</summary>
 <pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-pg
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    command: [postgres, -c, max_connections=200]
  mgt:
    container_name: safeline-mgt
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mgt:${IMAGE_TAG:?image tag required}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/mgt:/app/data
    ports:
      - ${MGT_PORT:-9443}:1443
    environment:
      - MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    depends_on:
      - postgres
      - fvm
    dns:
      - 119.29.29.29
      - 223.5.5.5
      - 180.76.76.76
      - 1.2.4.8
      - 114.114.114.114
      - 8.8.8.8
    logging:
      options:
        max-size: "100m"
        max-file: "10"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
  detect:
    container_name: safeline-detector
    restart: always
    image: ${IMAGE_PREFIX}/safeline-detector:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
  mario:
    container_name: safeline-mario
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mario:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/mario:/resources/mario
      - ${SAFELINE_DIR}/logs/mario:/logs/mario
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/mario
      - GOGC=100
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.6
  luigi:
    container_name: safeline-luigi
    restart: always
    image: ${IMAGE_PREFIX}/safeline-luigi:${IMAGE_TAG}
    environment:
      - MGT_IP=${SUBNET_PREFIX}.4
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/luigi:/app/data
    depends_on:
      - detect
      - mgt
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.7
  fvm:
    container_name: safeline-fvm
    restart: always
    image: ${IMAGE_PREFIX}/safeline-fvm:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.8
  bridge:
    container_name: safeline-bridge
    restart: always
    image: ${IMAGE_PREFIX}/safeline-bridge:${IMAGE_TAG}
    command:
      - /app/bridge
      - serve
      - -n
      - unix
      - -a
      - /app/run/safeline.sock
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run:/app/run
    logging:
      options:
        max-size: "100m"
        max-file: "10"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.9
    depends_on:
      - mgt
</code></pre>
</details>
<h2 id="2024312-更新">2024.3.12 更新</h2>
<p>目前新版本控制 t1k 插件是否拦截的方式需要修改<code>t1k.conf</code>。</p>
<pre><code class="language-nginx">    local t = {
        mode = "block", -- block or monitor or off, default off
        host = "unix:/resources/detector/snserver.sock",
</code></pre>
<p>把<code>mode</code>的<code>block</code>换成<code>monitor</code>或者<code>off</code>，记得要重载nginx配置，搞完记得换回来。</p>
<h2 id="2024531更新">2024.5.31更新</h2>
<details>
 <summary>点击查看YAML</summary>
 <pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-pg
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    command: [postgres, -c, max_connections=600]
    healthcheck:
      test: pg_isready -U safeline-ce -d safeline-ce
  mgt:
    container_name: safeline-mgt
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mgt:${IMAGE_TAG:?image tag required}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/mgt:/app/data
    ports:
      - ${MGT_PORT:-9443}:1443
    healthcheck:
      test: curl -k -f https://localhost:1443/api/open/health
    environment:
      - MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    depends_on:
      - postgres
      - fvm
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
  detect:
    container_name: safeline-detector
    restart: always
    image: ${IMAGE_PREFIX}/safeline-detector:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
  mario:
    container_name: safeline-mario
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mario:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/mario:/resources/mario
      - ${SAFELINE_DIR}/logs/mario:/logs/mario
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/mario
      - GOGC=100
      - DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.6
  tengine:
    container_name: safeline-tengine
    restart: always
    image: ${IMAGE_PREFIX}/safeline-tengine:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/resolv.conf:/etc/resolv.conf:ro
      - ${SAFELINE_DIR}/resources/nginx:/etc/nginx
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/nginx:/var/log/nginx
      - ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
    environment:
      - TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
      - TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
      # deprecated
      - SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
    ulimits:
      nofile: 131072
    network_mode: host
  luigi:
    container_name: safeline-luigi
    restart: always
    image: ${IMAGE_PREFIX}/safeline-luigi:${IMAGE_TAG}
    environment:
      - MGT_IP=${SUBNET_PREFIX}.4
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/luigi:/app/data
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    depends_on:
      - detect
      - mgt
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.7
  fvm:
    container_name: safeline-fvm
    restart: always
    image: ${IMAGE_PREFIX}/safeline-fvm:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.8
  bridge:
    container_name: safeline-bridge
    restart: always
    image: ${IMAGE_PREFIX}/safeline-bridge:${IMAGE_TAG}
    command:
      - /app/bridge
      - serve
      - -n
      - unix
      - -a
      - /app/run/safeline.sock
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run:/app/run
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.9
    depends_on:
      - mgt
  chaos:
    container_name: safeline-chaos
    restart: always
    image: ${IMAGE_PREFIX}/safeline-chaos:${IMAGE_TAG}
    logging:
      options:
        max-size: "100m"
        max-file: "10"
    ports:
      - 23333:23333
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.10
</code></pre>
</details>
<h2 id="2024928更新">2024.9.28更新</h2>
<pre><code class="language-yaml">networks:
  safeline-ce:
    name: safeline-ce
    driver: bridge
    ipam:
      driver: default
      config:
        - gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
          subnet: ${SUBNET_PREFIX}.0/24
    driver_opts:
      com.docker.network.bridge.name: safeline-ce

services:
  postgres:
    container_name: safeline-pg
    restart: always
    image: postgres:15.2
    volumes:
      - ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
      - /etc/localtime:/etc/localtime:ro
    environment:
      - POSTGRES_USER=safeline-ce
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.2
    command: [postgres, -c, max_connections=600]
    healthcheck:
      test: pg_isready -U safeline-ce -d safeline-ce
  mgt:
    container_name: safeline-mgt
    restart: always
    image: ${IMAGE_PREFIX}/safeline-mgt:${IMAGE_TAG:?image tag required}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/mgt:/app/data
      - ${SAFELINE_DIR}/logs/nginx:/app/log/nginx:z
      - ${SAFELINE_DIR}/resources/sock:/app/sock
    ports:
      - ${MGT_PORT:-9443}:1443
    healthcheck:
      test: curl -k -f https://localhost:1443/api/open/health
    environment:
      - MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    depends_on:
      - postgres
      - fvm
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.4
  detect:
    container_name: safeline-detector
    restart: always
    image: ${IMAGE_PREFIX}/safeline-detector:${IMAGE_TAG}
    volumes:
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/logs/detector:/logs/detector
      - /etc/localtime:/etc/localtime:ro
    environment:
      - LOG_DIR=/logs/detector
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.5
  tengine:
    container_name: safeline-tengine
    restart: always
    image: ${IMAGE_PREFIX}/safeline-tengine:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/resolv.conf:/etc/resolv.conf:ro
      - ${SAFELINE_DIR}/resources/nginx:/etc/nginx
      - ${SAFELINE_DIR}/resources/detector:/resources/detector
      - ${SAFELINE_DIR}/resources/chaos:/resources/chaos
      - ${SAFELINE_DIR}/logs/nginx:/var/log/nginx:z
      - ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
      - ${SAFELINE_DIR}/resources/sock:/app/sock
    environment:
      - TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
      - TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
      # deprecated
      - SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
    ulimits:
      nofile: 131072
    network_mode: host
  luigi:
    container_name: safeline-luigi
    restart: always
    image: ${IMAGE_PREFIX}/safeline-luigi:${IMAGE_TAG}
    environment:
      - MGT_IP=${SUBNET_PREFIX}.4
      - LUIGI_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${SAFELINE_DIR}/resources/luigi:/app/data
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    depends_on:
      - detect
      - mgt
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.7
  fvm:
    container_name: safeline-fvm
    restart: always
    image: ${IMAGE_PREFIX}/safeline-fvm:${IMAGE_TAG}
    volumes:
      - /etc/localtime:/etc/localtime:ro
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.8
  bridge:
    container_name: safeline-bridge
    restart: always
    image: ${IMAGE_PREFIX}/safeline-bridge:${IMAGE_TAG}
    command:
      - /app/bridge
      - serve
      - -n
      - unix
      - -a
      - /app/run/safeline.sock
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run:/app/run
    logging:
      options:
        max-size: "100m"
        max-file: "5"
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.9
    depends_on:
      - mgt
  chaos:
    container_name: safeline-chaos
    restart: always
    image: ${IMAGE_PREFIX}/safeline-chaos:${IMAGE_TAG}
    logging:
      options:
        max-size: "100m"
        max-file: "10"
    volumes:
      - ${SAFELINE_DIR}/resources/chaos:/app/chaos
    networks:
      safeline-ce:
        ipv4_address: ${SUBNET_PREFIX}.10
</code></pre>]]></description><guid isPermaLink="false">/archives/1panel-jie-ru-lei-chi-waf-fei-fan-xiang-dai-li</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F573AEF02-1C1C-4B47-B59B-774617FEEF0E.png&amp;size=m" type="image/jpeg" length="26891"/><category>运维</category><category>遇到的坑</category><pubDate>Sat, 14 Oct 2023 15:35:00 GMT</pubDate></item><item><title><![CDATA[算法小记]]></title><link>https://www.zway.top/archives/suan-fa-xiao-ji</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=%E7%AE%97%E6%B3%95%E5%B0%8F%E8%AE%B0&amp;url=/archives/suan-fa-xiao-ji" width="1" height="1" alt="" style="opacity:0;">
<h1>DataStructure&amp;Algorithm</h1>
<h1>字符串相关</h1>
<h2>统计某字符数量</h2>
<ul>
 <li>HashMap存Character的数量</li>
 <li>int[]或boolean[]存byte的数量或状态，<code>如int[97] += 1 // a的ascii</code></li>
 <li>位运算存byte的状态，ascii(128 个)需要两个long，a-z只需一个int，<code>如int b = 0;b ^= (1 &lt;&lt; (int)byte -'a')</code></li>
</ul>
<h2>字符串变换</h2>
<ul>
 <li>注意转换前后结构</li>
 <li>是否需要双指针</li>
 <li>是否能原地变换</li>
</ul>
<h2>字符串相似/缺少</h2>
<ul>
 <li>分情况+双指针</li>
 <li>编辑距离</li>
</ul>
<h1>矩阵</h1>
<h2>矩阵旋转变换</h2>
<ul>
 <li>思考如何用多次for简单变换完成</li>
 <li>深入思考一次for变换+限定变换的元素</li>
</ul>
<h2>矩阵存储</h2>
<ul>
 <li>运用矩阵本身+固定数量的标志存储稀疏信息（如用第一行、第一列和两个标志位）</li>
</ul>
<h1>链表</h1>
<h2>删除</h2>
<ul>
 <li>注意删除要指到前一个节点上，判断要是<code>node.next != null</code></li>
</ul>
<h3>无头删除</h3>
<ul>
 <li>把这个节点改成下个节点</li>
</ul>
<h2>倒数第x个节点</h2>
<ul>
 <li>双指针</li>
 <li>递归</li>
 <li>栈</li>
</ul>
<h2>分割</h2>
<ul>
 <li>无顺序就扔到前面</li>
 <li>有顺序就维护两个新链表</li>
</ul>
<h2>回文/相交/回环</h2>
<ul>
 <li>快慢指针（快走两步慢走一步）求中心点或等待相交（追及问题，两个指针p1, p2分别从head和相遇的点出发，他们最终一定会在环入口点相遇）</li>
 <li>反转另一部分来避免额外内存空间</li>
 <li>注意数学关系，（a+c和b+c，c是否为0，[a+c+b+<em><strong>c</strong></em>]==[b+c+a+<em><strong>c</strong></em>]）（我走完我的路，就去走你的路；你走完你的路，再走我的路；如果我们有缘，就会在我们两条路的某一点相遇，然后走完剩下的路。）</li>
</ul>
<h1>栈</h1>
<h2>通用</h2>
<ul>
 <li>使用多栈保存</li>
 <li>栈来回倒</li>
 <li>注意栈空的情况，栈空不要pop和peek</li>
</ul>
<h1>图</h1>
<h2>BFS</h2>
<ul>
 <li>建邻接表（可用List数组 或~~ HashMap+Set~~）+队列</li>
 <li>需要数组记录是否访问过，防止死环问题</li>
 <li>不推荐
  <del>不建邻接表直接递归，因为时间复杂度太大</del></li>
</ul>
<h2>DFS</h2>
<ul>
 <li>建邻接表（可用List数组 或~~ HashMap+Set~~）+递归</li>
 <li>需要数组记录是否访问过，防止死环问题</li>
 <li>以终点为导向从后往前DFS</li>
 <li>不推荐
  <del>不建邻接表直接递归，因为时间复杂度太大</del></li>
</ul>
<h1>树</h1>
<blockquote>
 <p>递归</p>
</blockquote>
<h2>二叉搜索数</h2>
<ul>
 <li>注意中间点下标是<strong>start</strong><code>+ (end - start) / 2</code> ，要加上start</li>
 <li>可利用二叉搜索树的结构特性避免全部递归</li>
</ul>
<h2>前序序列</h2>
<ul>
 <li>加上null标识的前序序列能唯一标识一个树的结构，但是用字符串进行判断效率低</li>
</ul>
<h2>路径和</h2>
<ul>
 <li>使用前缀路径和</li>
</ul>
<pre><code class="language-vim">求距离为5
a  b  c  d  e  f
|&lt; x &gt;|
|&lt;     y   &gt;|
当y-x=5时,c到e的距离为5
</code></pre>
<h2>树状数组</h2>
<blockquote>
 <p>单点/区间修改，单点/区间查询</p>
</blockquote>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FUALen8jnJd1GjSR4Zv39DG7zZ5qx-vL3zPFKY2h0zaY.png&amp;size=m" alt="image"></p>
<ul>
 <li>t[x]的节点长度等于lowbit(x)</li>
 <li>t[x]节点的父节点为t[x+lowbit(x)]</li>
</ul>
<h1>回溯</h1>
<h2>模板</h2>
<pre><code class="language-java">void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择：本层集合中元素) {
        处理结点;
        backtracking(路径，选择列表)); // 递归
        回溯，撤销处理结果;
    }
}
</code></pre>
<h1>位运算</h1>
<ul>
 <li>位运算记得及时加括号，一步一个括号，因为位运算优先级最低</li>
 <li>可以用在左移标记或者右移标记的地方,当然也能用来存储下标</li>
</ul>
<h2>全1的int是-1</h2>
<p><code>-1</code></p>
<h2>获得n个1</h2>
<p> <code>((1&lt;&lt;n) - 1)</code></p>
<h2>lowbit</h2>
<blockquote>
 <p>非负整数n在二进制表示下最低位1及后面的0构成的数值 lowbit(44) = lowbit((101<strong>100</strong>)2) = (100)2 = 4 原二进制：101100</p>
</blockquote>
<ol start="2">
 <li>取反：010011</li>
 <li>加一：010100</li>
 <li>与原二进制按位与：</li>
</ol>
<p>101100</p>
<p>010100</p>
<p>000100 =&gt; 4</p>
<ul>
 <li>取反加一结合起来就是负的这个数</li>
 <li><strong>结果：lowbit(n) = n &amp; -n</strong></li>
</ul>
<h1>快速乘法</h1>
<p>20＊14　＝　20＊(1110)2 = 20＊(2^3)＊1 + 20＊(2^2)＊1+20＊(2^1)＊１+20＊(2^0)＊0 = 160+80+40=2</p>
<pre><code class="language-java">    while (b) {
        result += (b &amp; 1) * a;
        a *= 2;
        b /= 2;
    }
</code></pre>
<h1>快速幂</h1>
<p>2^10 = (2^5) * (2^5)</p>
<p>2^5 = (2^2)* (2^2) *2</p>
<p>2^2 = 2*2</p>
<pre><code class="language-java">private int fm(int x,int m){
    int result = 1;
    int nm = m/2;
    result *= fm(x,nm);
    result *= fm(x,nm);
    if(m%2 != 0){
        result *= x;
    }
    return result;
}
</code></pre>
<h1>取模(余)</h1>
<h3>运算规则</h3>
<p>模运算与基本四则运算有些相似，但是除法例外。其规则如下：</p>
<ol>
 <li>(a + b) % p = (a % p + b % p) % p</li>
 <li>(a - b) % p = (a % p - b % p) % p <code>在java中要(a - b) % p = (a % p</code><strong>+ p</strong><code>- b % p) % p ，因为java里 -9%5=-4</code></li>
 <li>(a * b) % p = (a % p * b % p) % p</li>
 <li>a ^ b % p = ((a % p)^b) % p</li>
</ol>
<p>结合律：</p>
<ol start="5">
 <li>((a+b) % p + c) % p = (a + (b+c) % p) % p</li>
 <li>((a*b) % p * c)% p = (a * (b*c) % p) % p</li>
</ol>
<p>交换律：</p>
<ol start="7">
 <li>(a + b) % p = (b+a) % p</li>
 <li>(a * b) % p = (b * a) % p 分配律：</li>
 <li>((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p</li>
</ol>
<h3>重要定理</h3>
<ol start="10">
 <li>若a≡b (% p)，则对于任意的c，都有(a + c) ≡ (b + c) (%p)；</li>
 <li>若a≡b (% p)，则对于任意的c，都有(a * c) ≡ (b * c) (%p)；</li>
 <li>若a≡b (% p)，c≡d (% p)，则 (a + c) ≡ (b + d) (%p)，(a - c) ≡ (b - d) (%p)，(a * c) ≡ (b * d) (%p)，(a / c) ≡ (b / d) (%p)；<code>唯一有除法的</code></li>
</ol>
<h1>动态规划</h1>
<ul>
 <li>想第一步有哪几种情况，缩小问题规模，一般为第一步的几种情况对应的小规模问题结果相加</li>
</ul>
<h1>动规-背包问题</h1>
<h2>01背包问题</h2>
<h3>暴力</h3>
<p>f[i][j] 表示只按前i个物品，总体积是j的情况下，总价值最大是多少</p>
<p>result = max(f[n][V])<code>可无</code></p>
<ol>
 <li>不选第i个物品 f[i][j] = f[i-1]f[j]</li>
 <li>选第i个物品 f[i][j] = f[i-1][j - v[i]] + w[i]</li>
</ol>
<p>f[i][j] = max{1. 2.}</p>
<blockquote>
 <p>如果想做到东西的空间恰好是V，就要切断从f[0][1~V]的转移途径，让所有数据都从f[0][0]转移过来，不是恰好的数据就不能从f[i-1]f[j]继承数据（因为继承了还是负数，取max时肯定不会取）</p>
</blockquote>
<p>f[0][0] = 0 ， f[0][1~V] = -INF</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F977BAZ2aeFl8WScAn-Hr_CO4dYJldWE7Bd5bpanwsdc.png&amp;size=m" alt="image"></p>
<h3>优化</h3>
<blockquote>
 <p>f[i]只和f[i-1]有关，可以使用一维数组</p>
</blockquote>
<p>注意需要从后往前循环，要不然前面的会比后面的更新更快，读取不了f[j - v[i]]</p>
<h2>完全背包问题</h2>
<h3>暴力</h3>
<p>f[i][j] 表示只按前i个物品，总体积是j的情况下，总价值最大是多少</p>
<p>result = max(f[n][V])<code>可无</code></p>
<ol>
 <li>不选第i个物品 f[i][j] = f[i-1]f[j]</li>
 <li>选一次第i个物品 f[i][j] = f[i-1][j - v[i]] + w[i]</li>
 <li>选两次第i个物品 f[i][j] = f[i-1][j - 2*v[i]] + 2*w[i] k+1. 选k次第i个物品 f[i][j] = f[i-1][j - k*v[i]] + k*w[i]</li>
</ol>
<p>f[i][j] = max{1. 2. 3. ... k+1.}</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FUEsEKKXwNAENYq4DRvbvQNMG-5eiR69lyIpks2Tu6Bk.png&amp;size=m" alt="image"></p>
<h3>优化</h3>
<blockquote>
 <p>初级：从后往前简化为一维dp</p>
</blockquote>
<p>高级：</p>
<p>上面的k+1.情况在最空间小的时候算过了</p>
<p>f[i-1][9-4] + 2 = f[i-1][5]+2</p>
<p>f[i-1][9-8] + 4 = f[i-1][1]+4</p>
<p>当j取到13时，还要计算f[i-1][5]+2+2 、 f[i-1][1]+4+2的最大值，可见前面已经算过了</p>
<p>于是递推公式变成f[i][j] = max{f[i-1][j], f[i][j-v[i]]}</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FkwsogTYTnjrLQW4ToXtOSA1sf7nJ12igi-Uu3XzhFrY.png&amp;size=m" alt="image"></p>
<p>然后把二维dp变成一维dp</p>
<h2>多重背包问题</h2>
<h3>暴力</h3>
<p>如完全背包问题的暴力，只需把k限制到s[i]就行了，即向左上方的箭头不是无限多的</p>
<h3>优化</h3>
<blockquote>
 <p>初级1.0：从后往前简化为一维dp</p>
</blockquote>
<h4>2.0</h4>
<p>给定一个数s=7</p>
<p>只需要log2(s)↑个数就可以表示</p>
<p>其中数字为2的等比数列，但最后一个为s减去前几个能表示的最大的数</p>
<pre><code class="language-vim">{1,2,4}
0=0
1=1
2=2
3=1+2
4=4
5=1+4
6=2+4
7=1+2+4
</code></pre>
<p>若s=10</p>
<p>1,2,4,3</p>
<p>{1,2,4}===0~7</p>
<p>{1,2,4,3}===0~10</p>
<p><code>方法有会重复表示，如1+4===2+3，但本题的max可以忽略这个问题</code></p>
<pre><code class="language-java">for(int k = 1;k &lt;= s;k *= 2){
    s -= k;
    // k即为表示组中的一个数
}
if(s &gt; 0){
    // s为剩余的最后一项，如上面的3
}
</code></pre>
<p>即可转换成01背包问题（<code>10个体积为3价值为2的物品</code>可以转成<code>一个体积为3*1价值为2*1的物品 + 一个体积为3*2价值为2*2的物品 + 一个体积为3*4价值为2*4的物品 + 一个体积为3*3价值为2*3的物品 【共四件】</code>）</p>
<blockquote>
 <p>把 O(ivs) 降为 O(ivlogs)</p>
</blockquote>
<h4>3.0</h4>
<p>∵ f[j] = max{f[j-v]+w, f[j-2*v]+2*w, f[j-3*v]+3*w, ... , f[j-k*v]+k*w}</p>
<p>∴ f[j + v] = max{f[j]+w, f[j-v]+2*w, f[j-2*v]+3*w, ... , f[j-(k-1)*v]+k*w}</p>
<p>用单调队列（滑动窗口）解决（leetcode239）</p>
<h2>混合背包问题</h2>
<ul>
 <li>把多重背包问题用log2(s)分解成多个01背包问题，如上文优化2.0</li>
 <li>循环每个背包</li>
 <li>是01背包就从后往前转移，见01背包优化</li>
 <li>是完全背包就从前往后转移，见完全背包优化</li>
</ul>
<h2>二维费用的背包问题</h2>
<p>一维费用：i表示体积</p>
<p>二维费用：i表示体积，j表示重量</p>
<p>两个维度都从后往前循环：f[i][j] = max(f[i][j], f[i-a][j-b] + c)</p>
<h2>分组背包问题</h2>
<p>转化成类似01背包问题</p>
<p>i表示前i个物品组，j表示容量</p>
<p>f[i][j] = max(f[i-1][j], f[i-1][j-v[0]] + w[0], f[i-1][j-v[1]] + w[1], ... , f[i-1][j-v[s-1]] + w[s-1])</p>
<p>意思是在这个组中要么不选，要么选第一个或第二个或...或最后一个，取最后价值最大的</p>
<p>优化成一维</p>
<h2>有依赖的背包问题</h2>
<p>f[i][j]为选节点i，体积是j的情况下，i为根的整棵子树的最大价值</p>
<h1>二分查找</h1>
<h2>左右界</h2>
<ul>
 <li>while无等于</li>
 <li>求左边界：向下取整，等号归右，左加一</li>
 <li>求右边界：向上取整，等号归左，右减一</li>
 <li>总是右侧为所求</li>
</ul>
<pre><code class="language-java">    private int findLeft(int[] nums,int target){
        if (nums.length == 0) return -1;
        int left = 0 , right = nums.length-1;
        while (left &lt; right){
            int mid = (left+right)&gt;&gt;1;
            if (nums[mid] &gt;= target) right = mid;
            else left = mid+1;
        }
        return nums[right]==target?right:-1;
    }
    private int findRight(int[] nums,int target){
        if (nums.length == 0)return -1;
        int left = 0 , right =  nums.length-1;
        while (left &lt; right){
            int mid = (left+right+1)&gt;&gt;1;
            if (nums[mid] &lt;= target) left = mid;
            else right = mid-1;
        }
        return nums[right]==target?right:-1;
    }
</code></pre>
<h2>第一个大于/小于</h2>
<ul>
 <li>while有等于</li>
 <li>向下取整</li>
 <li>都超过mid</li>
 <li>大于左为所求，小于右为所求</li>
 <li>注意结果会越大小界</li>
</ul>
<pre><code class="language-java">        int n = nums.size();
        if (n == 0) return 0;
        int l = 0, r = n - 1;
        while (l &lt;= r) {
            int mid = (l + r) &gt;&gt; 1;
            if (nums.get(mid) &lt;= x) l = mid + 1;
            else r = mid - 1;
        }
        return l;

        int n = nums.size();
        if (n == 0) return 0;
        int l = 0, r = n - 1;
        while (l &lt;= r) {
            int mid = (l + r) &gt;&gt; 1;
            if (nums.get(mid) &gt;= x) r = mid - 1;
            else l = mid + 1;
        }
        return r;

</code></pre>
<h2>第一个小于等于/大于等于</h2>
<p>上面的结果加减一</p>
<h1>排序</h1>
<table>
 <thead>
  <tr>
   <th>冒泡</th>
   <th>每两个之间比较交换一次，然后位置+1，循环n次</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>选择</td>
   <td>每次选择一个最小的放到最左边</td>
  </tr>
  <tr>
   <td>插入</td>
   <td>麻将，摸到下一个就把它插到按顺序排好的地方</td>
  </tr>
  <tr>
   <td>希尔</td>
   <td>基于插入，在外面加一层step/=2的，让插入的1变成step</td>
  </tr>
  <tr>
   <td>快排</td>
   <td>以某一个元素为基准，小的放左边，大的放右边，递归左右</td>
  </tr>
  <tr>
   <td>归并</td>
   <td>分治，放到新数组中，双指针</td>
  </tr>
  <tr>
   <td>堆排序</td>
   <td> </td>
  </tr>
 </tbody>
</table>]]></description><guid isPermaLink="false">/archives/suan-fa-xiao-ji</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2FUEsEKKXwNAENYq4DRvbvQNMG-5eiR69lyIpks2Tu6Bk.png&amp;size=m" type="image/jpeg" length="21842"/><category>后端</category><pubDate>Sat, 22 Jan 2022 04:17:00 GMT</pubDate></item><item><title><![CDATA[CI/CD 实践 基于华为云 DevCloud]]></title><link>https://www.zway.top/archives/ci-cd-shi-jian-ji-yu-hua-wei-yun-devcloud</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=CI%2FCD%20%E5%AE%9E%E8%B7%B5%20%E5%9F%BA%E4%BA%8E%E5%8D%8E%E4%B8%BA%E4%BA%91%20DevCloud&amp;url=/archives/ci-cd-shi-jian-ji-yu-hua-wei-yun-devcloud" width="1" height="1" alt="" style="opacity:0;">
<h1>协作开发、CI/CD实践</h1>
<h2>项目背景</h2>
<p>本项目为基于Spring Boot + VUE的便利店内外部综合管理系统。采用前后端分离式独立开发，前后端事先约定请求接口。前端先用假接口如fastmock开发，待相应接口上线服务后再接入api接口。</p>
<h3>api接口</h3>
<p>本项目采用<code>Apifox</code>作为api接口文档平台。Apifox 是接口管理、开发、测试全流程集成工具，集成了 Postman + Swagger + Mock+ JMeter 等常用接口开发软件的功能。通过一套系统、一份数据，解决多个系统之间的数 据同步问题。只要定义好接口文档，接口调试、数据 Mock、接口测试就可以直接使用，无 需再次定义；接口文档和接口开发调试使用同一个工具，接口调试完成后即可保证和接口文 档定义完全一致。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201212916356.png&amp;size=m" alt="image-20230201212916356"></p>
<h3>后端</h3>
<p>后端使用的技术栈包括Spring Boot+Mybatis为基础web框架，Redis做缓存、Spring Security和JWT做认证授权、对象存储OSS存储文件 。</p>
<h3>前端</h3>
<p>前端使用 VueCli 进行开发，使用 AntDesign 组件库进行前端页面需求布局，独立封装 axios ，使用 typescript 进行逻辑处理，能更好地进行模块化开发，使用vuex和loaclStorage进行用户信息和token的存储。</p>
<h2>协作开发</h2>
<p>华为云中提供了代码托管服务。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201212927424.png&amp;size=m" alt="image-20230201212927424"></p>
<h3>配置密钥</h3>
<p>华为云代码托管首先要配置ssh密钥，要不然拒绝你的push找不到原因。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201212938570.png&amp;size=m" alt="image-20230201212938570"></p>
<h3>保护分支</h3>
<p>我们的项目采用master保护分支模式，在功能开发中统一使用功能分支+合并请求的方式进行功能的开发与合并。需要在保护分支设置中设置了master分支为所有人只能合并、不能提交的模式。如下图。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201212947956.png&amp;size=m" alt="image-20230201212947956"></p>
<h3>开发新功能</h3>
<p>当开发新功能时，需要创建新的分支，所有修改的代码均提交在新建的分支中。也可以在本地维护自己的开发分支，并确保开发新功能时自己的开发分支与最新的master分支相同。在此处使用华为云web界面进行新建分支演示。</p>
<ul>
 <li><strong>点击分支--新建分支，基于master分支，分支名称 华为云推荐采用<code>feature工作项编号</code>的名称（注意：不要有+号、空格等特殊符号），点击确定。</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213000968.png&amp;size=m" alt="image-20230201213000968"></p>
<ul>
 <li><strong>在ide中切换到开发分支，进行开发。</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213009743.png&amp;size=m" alt="image-20230201213009743"></p>
<ul>
 <li><strong>开发完成后填写提交注释信息，华为云推荐格式为<code>fix #工作项编码 更多信息</code>，push到远端仓库。</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213015979.png&amp;size=m" alt="image-20230201213015979"></p>
<ul>
 <li>在开发完成并测试验证完毕后，进行分支合并。<strong>点击合并请求 --&gt; 新建合并请求 --&gt; 选择待合并的分支 --&gt; 下一步 --&gt; 完善相关信息（可选合入后删除源分支） --&gt; 确定 --&gt; 完善相关信息（可选） --&gt; 合入</strong>。</li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213028363.png&amp;size=m" alt="image-20230201213028363"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213033676.png&amp;size=m" alt="image-20230201213033676"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213037751.png&amp;size=m" alt="image-20230201213037751"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213044759.png&amp;size=m" alt="image-20230201213044759"></p>
<p><strong>注意</strong>: 在合并分支之前必须保证源分支代码能正确运行</p>
<h2>持续集成</h2>
<h3>后端</h3>
<p>本项目后端采用maven为构建工具，由于需要部署到服务器，为了方便操作，将打包成两份成品。一份为单纯的jar包放在华为云的发布仓库中做备份，另一份打包成docker镜像并推送到阿里云镜像仓库。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213055740.png&amp;size=m" alt="image-20230201213055740"></p>
<h4>打包jar包</h4>
<p>华为云中提供了打包jar包并发布到发布仓库的模板。</p>
<p>只需<strong>点击编译构建 --&gt; 新建任务 --&gt; 选择好代码源 --&gt; 下一步</strong>。就出现maven模板。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213101362.png&amp;size=m" alt="image-20230201213101362"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213107324.png&amp;size=m" alt="image-20230201213107324"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213112207.png&amp;size=m" alt="image-20230201213112207"></p>
<p>在构建步骤中可以自己调整一些选项，华为云模板的注释可以帮助我们理解步骤有什么用。我们在实践中此没有修改他的配置。</p>
<pre><code class="language-bash"># 参数说明：
#		-Dmaven.test.skip=true：跳过单元测试
#		-U：每次构建检查依赖更新，可避免缓存中快照版本依赖不更新问题，但会牺牲部分性能
#		-e -X ：打印调试信息，定位疑难构建问题时建议使用此参数构建
#		-B：以batch模式运行，可避免日志打印时出现ArrayIndexOutOfBoundsException异常
# 使用场景： 打包项目且不需要执行单元测试时使用
mvn package -Dmaven.test.skip=true -U -e -X -B

#功能：打包;执行单元测试，但忽略单元测试用例失败，每次构建检查依赖更新
#使用场景： 需要执行单元测试，且使用构建提供的单元测试报告服务统计执行情况
# 使用条件：在”单元测试“中选择处理单元测试结果，并正确填写测试结果文件路径
#mvn package -Dmaven.test.failure.ignore=true -U -e -X -B

#功能：打包并发布依赖包到私有依赖库
#使用场景： 需要将当前项目构建结果发布到私有依赖仓库以供其他maven项目引用时使用
#注意事项： 此处上传的目标仓库为Devcloud私有依赖仓库，注意与软件发布仓库区分
#mvn deploy -Dmaven.test.skip=true -U -e -X -B
</code></pre>
<h4>打包docker镜像</h4>
<p>为了服务部署的方便和易迁移性，本项目采用docker镜像方式进行部署，因此需要打包docker镜像。</p>
<p>在实践中发现，若使用华为云容器镜像服务swr作为镜像仓库，他人（非仓库所有人）触发构建功能时不能把打包好的镜像上传到仓库，提示无权限，且swr不能在华为云构建服务器上设置静态密码。因此我们选择使用阿里云容器镜像服务。</p>
<p>docker基本操作和容器镜像仓库基本操作在此不再赘述。</p>
<ul>
 <li><strong>在阿里云容器镜像服务设置固定密码</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213123497.png&amp;size=m" alt="image-20230201213123497"></p>
<ul>
 <li><strong>创建镜像仓库（可能需要创建命名空间，在此不赘述）</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213130110.png&amp;size=m" alt="image-20230201213130110"></p>
<ul>
 <li><strong>学习操作指南（很重要，下面大量用到这些命令）</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213138448.png&amp;size=m" alt="image-20230201213138448"></p>
<ul>
 <li><strong>在项目根目录下添加<code>Dockerfile</code></strong></li>
</ul>
<p>本项目的Dockerfile如下，仅供参考。</p>
<pre><code class="language-dockerfile"># 以java8为基础镜像
FROM java:8
# 把./target/*.jar复制到/home/springboot/app.jar里
ADD ./target/*.jar /home/springboot/app.jar
# 设置时区，防止Calendar与北京时间差8小时
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' &gt;/etc/timezone
# 指定端口
CMD ["--server.port=8080"]
# 暴露端口
EXPOSE 8080
# 运行jar包的命令 --spring.profiles.active=sit激活sit的配置文件
ENTRYPOINT ["java","-jar","/home/springboot/app.jar","--spring.profiles.active=sit"]
</code></pre>
<ul>
 <li><strong>在华为云中新建构建任务，可以选择maven模板（同上小节）</strong></li>
 <li><strong>删除<code>上传软件包到软件发布库</code>步骤，添加<code>执行docker命令</code>步骤</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213148280.png&amp;size=m" alt="image-20230201213148280"></p>
<ul>
 <li><strong>添加如下命令</strong></li>
</ul>
<pre><code class="language-bash">#[build]
-t registry.cn-xxxxx.aliyuncs.com/username/xxx:xxx .
#该命令是用于打包并打上标签，需要项目根目录下的Dockerfile正确
#注意命令最后面的点不是多余的
#标签为阿里云容器镜像服务的标准格式
#具体-t后面的内容可以参考上面的阿里云操作指南
#注意版本号最好是固定的
########################################
#[login]
--username=阿里云用户名 registry.cn-xxxx.aliyuncs.com -p ${aliyun_docker_pw}
#该命令是用于登录阿里云容器镜像服务仓库的
#详细命令参考上面的阿里云操作指南
#${aliyun_docker_pw}是取变量的意思，变量可以在参数设置中设置好上面的固定密码，保护隐私
########################################
#[push]
registry.cn-xxxx.aliyuncs.com/xxx/xxx:xxx
#该命令是用于推送镜像到阿里云镜像仓库
#详细命令参考上面的阿里云操作指南
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213158754.png&amp;size=m" alt="image-20230201213158754"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213204532.png&amp;size=m" alt="image-20230201213204532"></p>
<ul>
 <li>
  <p>完成，测试</p>
 </li>
</ul>
<h3>前端</h3>
<ul>
 <li><strong>创建构建任务，使用npm模板</strong>（同后端的打包jar包）</li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213209744.png&amp;size=m" alt="image-20230201213209744"></p>
<ul>
 <li><strong>调整npm构建步骤</strong></li>
</ul>
<p>把最后一行<code>#tar -zcvf demo.tar.gz ./</code>换成<code>tar -zcvf dist.tar.gz ./dist</code></p>
<p>目的是为了把dist文件夹完整地打包起来</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213215191.png&amp;size=m" alt="image-20230201213215191"></p>
<ul>
 <li><strong>调整上传软件包到软件发布库步骤</strong></li>
</ul>
<p>把构建包路径改为刚刚打包出来的文件<code>./dist.tar.gz</code></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213227615.png&amp;size=m" alt="image-20230201213227615"></p>
<h2>持续部署</h2>
<h3>后端</h3>
<p>本项目使用docker部署，持续集成步骤中已经将镜像推送至阿里云。</p>
<p>docker基本操作和linux基本操作在此不再赘述。</p>
<ul>
 <li>添加主机组，<strong>设置 --&gt; 通用设置 --&gt; 主机组管理 --&gt; 新建主机组 --&gt; 按要求填写信息 --&gt; 添加主机 --&gt; 按要求进一步填写主机信息</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213234210.png&amp;size=m" alt="image-20230201213234210"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213238983.png&amp;size=m" alt="image-20230201213238983"></p>
<ul>
 <li><strong>新建部署任务（使用空白模板）</strong></li>
 <li><strong>编辑部署步骤（新增执行shell命令）</strong></li>
</ul>
<p>下为我的部署shell命令，仅供参考。</p>
<pre><code class="language-bash"># 停止之前运行的服务 cvcserver可以自己改docker的基础
docker stop cvcserver
# 删除之前运行的服务 也可以用-f一步代替两部
docker rm cvcserver
# 删除之前的镜像
docker rmi registry.cn-xxxx.aliyuncs.com/xxx/xxx:sit
# 登录阿里云容器镜像服务 参考阿里云的操作指南 ${aliyun_pw}同样要在参数设置里设置
docker login --username=xxxxx registry-vpc.cn-xxx.aliyuncs.com -p ${aliyun_pw}
# 拉取镜像
docker pull registry.cn-xxx.aliyuncs.com/xxx/xxx:sit
# 运行镜像 --network myapps是我自己服务内网络
docker run --network myapps -p 8080:8080 --name cvcserver -d registry.cn-xxx.aliyuncs.com/xxx/xxx:sit
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213247245.png&amp;size=m" alt="image-20230201213247245"></p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213251619.png&amp;size=m" alt="image-20230201213251619"></p>
<ul>
 <li>测试，完成</li>
</ul>
<h3>前端</h3>
<ul>
 <li><strong>创建部署任务，选择空模板</strong>（参考后端部署）</li>
 <li>添加步骤<strong>选择部署来源</strong></li>
</ul>
<p>源选择构建任务，构建序号Lastest，下载到自己主机的一个可以临时存放的位置</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213257381.png&amp;size=m" alt="image-20230201213257381"></p>
<ul>
 <li>添加步骤<strong>解压文件</strong></li>
</ul>
<p>把上一步下载下来的<code>dist.tar.gz</code>解压到临时目录</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213304999.png&amp;size=m" alt="image-20230201213304999"></p>
<ul>
 <li>添加步骤<strong>删除文件</strong></li>
</ul>
<p>删除原来的前端文件（nginx配置好的目录）</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213312795.png&amp;size=m" alt="image-20230201213312795"></p>
<ul>
 <li>添加步骤<strong>拷贝文件</strong></li>
</ul>
<p>从临时文件夹中解压的dist内容复制到nginx配置好的目录</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213319357.png&amp;size=m" alt="image-20230201213319357"></p>
<ul>
 <li>添加步骤<strong>执行shell命令</strong></li>
</ul>
<p>给前端文件授权777全开，防止出现nginx403错误</p>
<pre><code class="language-shell">chmod -R 777 /home/docker/nginx/www
</code></pre>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213326113.png&amp;size=m" alt="image-20230201213326113"></p>
<ul>
 <li>添加步骤<strong>删除文件</strong></li>
</ul>
<p>删除临时文件夹里的文件</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213331994.png&amp;size=m" alt="image-20230201213331994"></p>
<ul>
 <li>所有的步骤的图放一下在这</li>
</ul>
<p>其实这些步骤完全可以编成一个shell命令，不过这样更清晰，好调试</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213339591.png&amp;size=m" alt="image-20230201213339591"></p>
<h2>流水线</h2>
<h3>后端</h3>
<ul>
 <li><strong>新建流水线 --&gt; 选择代码源和空白模板</strong></li>
 <li><strong>编辑符合自己需求的工作流</strong></li>
</ul>
<p>我的工作流如下，先经过代码检查，然后执行两个构建任务，再执行部署任务，最后测试url连通性。</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213346688.png&amp;size=m" alt="image-20230201213346688"></p>
<ul>
 <li>开启流水线自动触发，<strong>流水线编辑界面 --&gt; 代码源 --&gt; 更多设置 --&gt; 触发事件 --&gt; master分支在代码提交时触发</strong></li>
</ul>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213352098.png&amp;size=m" alt="image-20230201213352098"></p>
<ul>
 <li>**重要：**记得关闭检查任务、构建任务、部署任务里的自动触发，要不然流水线自动执行一次，任务本身也自动执行一次，造成冲突，搞出奇奇怪怪的bug。</li>
</ul>
<h3>前端</h3>
<p>同上，在这只放一下图</p>
<p><img src="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213358243.png&amp;size=m" alt="image-20230201213358243"></p>
<h2>总结</h2>
<p>​ 要多与后端沟通，确定好每个接口的数据类型和请求类型。</p>
<p>​ 无论是否有使用js框架，还是不太建议使用jsp进行页面开发。合作性的项目前后端要坚持分离，不然耦合性太大，前后端可能会互相影响导致项目迭代速度太慢。</p>]]></description><guid isPermaLink="false">/archives/ci-cd-shi-jian-ji-yu-hua-wei-yun-devcloud</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fimage-20230201213346688.png&amp;size=m" type="image/jpeg" length="70292"/><category>遇到的坑</category><category>DevOps</category><pubDate>Wed, 12 May 2021 04:45:00 GMT</pubDate></item><item><title><![CDATA[Redis 笔记]]></title><link>https://www.zway.top/archives/redis-bi-ji</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=Redis%20%E7%AC%94%E8%AE%B0&amp;url=/archives/redis-bi-ji" width="1" height="1" alt="" style="opacity:0;">
<h1>Redis</h1>
<h2>基本类型</h2>
<p><code>DEL key1</code> 删除k-v</p>
<h3>String</h3>
<p>value不仅仅可以放值，还可以放数字等</p>
<h4>基本</h4>
<pre><code class="language-shell">SET key1 v1				#设置值
GET key1   				#获取值
EXPIRE key1 60			#设置key的过期时间
TTL key1				#查看key的过期剩余时间
EXISTS key1				#是否存在
APPEND key1 lahaasdw	#追加字符串，没有key1就自动set
STRLEN key1   			#获取字符串长度
</code></pre>
<h4>自增</h4>
<pre><code class="language-shell">INCR key1				#让key自增1
DECR key1				#让key自减1    （这两个不存在都会自己set一个）
INCRBY key1 10			#让key自增任意步长
DECRBY key1 10			#让key自增任意步长
</code></pre>
<h4>范围</h4>
<pre><code class="language-shell">GETRANGE key1 0 3		#截取字符串范围 [start , end] （GETRANGE key1 0 -1 就可以查全部字符）
SETRANGE key1 2 xx		#offset value 把offset开始替换value个字节
</code></pre>
<h4>条件</h4>
<pre><code class="language-shell">SETEX key1 60 asd		#set with expire 带过期时间的设置
SETNX key1 redisss		#set if not exist 如果不存在就set
</code></pre>
<h4>批量</h4>
<pre><code class="language-shell">MSET k1 v1 k2 v2 k3 v3	#批量set
MGET k1 k2 k3			#批量get
MSETNX k1 v1 k4 v4		#如果全部不存在就批量set *原子性操作* 全部成功或全部失败
</code></pre>
<h4>对象</h4>
<pre><code class="language-shell">SET user:1 {name:zhangsan,age:10}			#设置user:1对象 值为字符串 来保存对象
MSET user:1:name zhangsan user:1:age 2		#巧妙保存
MGET user:1:name user:1:age
</code></pre>
<h4>其它</h4>
<pre><code class="language-shell">GETSET key1 redis							#先get出来再set出来
</code></pre>
<h3>List</h3>
<p>可以当成栈或队列，实际是一个双向链表，效率参考双向链表</p>
<p><strong>所以有的list命令用L开头</strong>表示从头部（左）</p>
<pre><code class="language-shell">LPUSH list one								#把一个值插入列表头部
LPUSH list two
LPUSH list three
LRANGE list 0 -1							#取出列表按头部插入顺序的 [start , end] 0 -1为全部
RPUSH list right							#插入队列尾部 注意是R
LPOP list									#从头部移除一个
LPOP list									#从尾部移除一个
Lindex list 1								#根据下标获取值
LLEN list									#获取长度 
LREM list 1 one								#count element 移除count个element值
LTRIM mlist 1 2								#通过下标修剪只剩下（1,2）的范围
RPOPLPUSH mlist list						#从一个列表尾部取出放到另外一个列表左边
EXISTS list									#看是否存在
LSET list 0 ite								#设置置顶下标 不存在下标或列表就报错
LINSERT list before wr xx					#在哪里插入
before是指L端
 
127.0.0.1:6379&gt; LRANGE list 0 -1
1) "wr"
2) "he"
127.0.0.1:6379&gt; LINSERT list before wr ,
(integer) 3
127.0.0.1:6379&gt; LRANGE list 0 -1
1) ","
2) "wr"
3) "he"
127.0.0.1:6379&gt; LINSERT list before he ,
(integer) 4
127.0.0.1:6379&gt; LRANGE list 0 -1
1) ","
2) "wr"
3) ","
4) "he"
</code></pre>
<h3>Set</h3>
<pre><code class="language-shell">SADD myset hello							#元素加入set
SMEMBERS myset								#查看set所有元素
SISMEMBER myset hello						#查看set中是否存在
SCARD myset									#查看set中的个数
SREM myset hello							#移除set中的指定元素
SRANDMEMBER myset							#随机显示一个元素
SPOP myset									#随机移除一个元素
SMOVE myset set hello7						#转移一个指定的元素
SDIFF myset set								#第一个set和第二个set求差集
SINTER myset set							#求交集
SUNION myset set							#求并集
</code></pre>
<h3>Hash</h3>
<p>hash更适合对象的存储！</p>
<pre><code class="language-shell">HSET myhash field1 v1						#设置键值对
HGET myhash field1							#获取键值对
HGETALL myhash								#获取所有键值对
HMSET myhash field1 hello field2 he			#批量set
HMGET myhash field1 field2					#批量get
HDEL myhash field1							#获取长度
HEXISTS myhash field1						#判断是否存在
HKEYS myhash								#获取所有key
HVALS myhash								#获取所有value
HINCRBY myhash field9 2						#自增 还有hdecrby
HSETNX myhash field1 aaa					#如果不存在set
</code></pre>
<h3>Zset</h3>
<p>在set基础上增加了一个值</p>
<pre><code class="language-shell">ZADD myset 2 two 3 three
ZRANGE myset 0 -1
ZRANGEBYSCORE salary -inf +inf				#只能最小到最大排序
ZRANGEBYSCORE salary -inf +inf withscores	#带上scores
ZREM salary xiaohong						#移除
ZCARD salary								#数量
ZREVRANGE salary 0 -1						#从大到小
ZCOUNT salary 0 100000						#计数
</code></pre>
<h2>特殊</h2>
<h3>Geospatial地理位置</h3>
<pre><code class="language-shell">GEOADD city 113.565 22.248 zhuhai 114.109 22.544 shenzhen				#添加
GEODIST city zhuhai shenzhen km											#获取两地地方距离
GEOHASH city shenzhen zhuhai											#获取哈希值
GEOPOS city zhuhai														#获取精度和纬度
GEORADIUS city 105.2 30.01 1000 km withdist withcoord count 1			#寻找半径内指定数量并显示距离和经纬度
GEORADIUSBYMEMBER city guangzou 1000 km									#以某个为中心
</code></pre>
<p>底层是zset 可以用zset的命令</p>
<h3>Hyperloglog</h3>
<pre><code class="language-shell">PFADD hll a b c d e f g							#添加
PFCOUNT hll										#统计不同元素的个数
PFMERGE hll2 hll my2							#合并两个到新的
 
</code></pre>
<h3>Bitmaps</h3>
<pre><code class="language-shell">SETBIT sign 0 1
SETBIT sign 2 0									#设置
GETBIT sign 3									#获取
BITCOUNT sign 0 -1								#统计1的个数
</code></pre>
<h2>事务</h2>
<p>redis事务是保证按顺序执行</p>
<p>一次性、顺序性、排他性</p>
<p>没有隔离级别 不保证原子性</p>
<h3>开启事务MULTI</h3>
<pre><code>MULTI
</code></pre>
<h3>执行事务EXEC</h3>
<pre><code class="language-shell">EXEC
</code></pre>
<h3>放弃事务DISCARD</h3>
<pre><code class="language-shell">DISCARD
</code></pre>
<h3>异常</h3>
<h4>“编译型异常”所有都不会执行</h4>
<pre><code class="language-shell">127.0.0.1:6379&gt; MULTI
OK
127.0.0.1:6379(TX)&gt; set v3
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)&gt; set k4 v4
QUEUED
127.0.0.1:6379(TX)&gt; get k5 
QUEUED
127.0.0.1:6379(TX)&gt; set 
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)&gt; exec
</code></pre>
<h4>“运行时异常”其它命令可以正常执行</h4>
<pre><code class="language-shell">127.0.0.1:6379&gt; MULTI
OK
127.0.0.1:6379(TX)&gt; set k1 vvv
QUEUED
127.0.0.1:6379(TX)&gt; incr k1
QUEUED
127.0.0.1:6379(TX)&gt; set k2 qqq
QUEUED
127.0.0.1:6379(TX)&gt; exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379&gt; get k2
"qqq"
</code></pre>
<h2>锁WATCH</h2>
<pre><code class="language-shell">127.0.0.1:6379&gt; WATCH money 
OK
127.0.0.1:6379&gt; multi
OK
127.0.0.1:6379(TX)&gt; DECRBY money 20
QUEUED
127.0.0.1:6379(TX)&gt; INCRBY out 20
QUEUED

#
#在这另外一个线程修改money
#

127.0.0.1:6379(TX)&gt; exec
(nil)
</code></pre>
<p><code>UNWATCH</code>解锁</p>]]></description><guid isPermaLink="false">/archives/redis-bi-ji</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2F20230201212253.png&amp;size=m" type="image/jpeg" length="240879"/><category>后端</category><pubDate>Wed, 17 Mar 2021 11:50:00 GMT</pubDate></item><item><title><![CDATA[Docker 部署前后端分离应用]]></title><link>https://www.zway.top/archives/docker-bu-shu-qian-hou-duan-fen-chi-ying-yong</link><description><![CDATA[<img src="https://www.zway.top/plugins/feed/assets/telemetry.gif?title=Docker%20%E9%83%A8%E7%BD%B2%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E5%BA%94%E7%94%A8&amp;url=/archives/docker-bu-shu-qian-hou-duan-fen-chi-ying-yong" width="1" height="1" alt="" style="opacity:0;">
<h1>Docker</h1>
<p>前提：关闭防火墙，开放端口！！ 创建docker网络</p>
<pre><code class="language-shell">docker network create myapps #默认创建的就是bridge
</code></pre>
<h2>MySQL</h2>
<h3>安装</h3>
<pre><code class="language-shell">docker run -p 3306:3306 --name mysql \
-v /home/docker/mysql/conf:/etc/mysql \
-v /home/docker/mysql/logs:/var/log/mysql \
-v /home/docker/mysql/data:/var/lib/mysql \
--network myapps \
-e MYSQL_ROOT_PASSWORD=mysql密码 \
-d mysql:5.7
</code></pre>
<p>注意修改mysql密码</p>
<h2>Nginx</h2>
<h3>安装</h3>
<pre><code class="language-shell"># 拷贝配置文件
docker run --name nginx01 -d nginx:latest
docker cp nginx01:/etc/nginx/nginx.conf /home/docker/nginx/conf/nginx.conf  #把容器中的nginx.conf文件复制到conf目录下
docker rm -f nginx01 #删除镜像
 
docker run --rm -d -p 80:80 --name nginx \
  -v /home/docker/nginx/www:/usr/share/nginx/html \
  -v /home/docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
  -v /home/docker/nginx/conf/conf.d:/etc/nginx/conf.d \
  -v /home/docker/nginx/logs:/var/log/nginx \
  --network myapps \
  nginx
</code></pre>
<h3>配置nginx</h3>
<p>在conf.d中添加文件</p>
<pre><code># client.test.zway.top 前端
server {
    listen    80;
    server_name    client.test.zway.top;
    root    /usr/share/nginx/html;
 
    charset utf-8;
 
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
 
}
# server.test.zway.top 后端
server {
    listen    80;
    server_name    server.test.zway.top;
 
    charset utf-8;
 
    location / {
        expires 0;
 
        proxy_pass https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js;
 
        # proxy_set_header Host $host:$proxy_port;
        proxy_set_header x-forwarded-for  $remote_addr;  
    }
 
}
</code></pre>
<h2>Redis</h2>
<h3>配置文件</h3>
<p>从6.2.1的官方包中取出<code>redis.conf</code>并修改</p>
<pre><code class="language-shell">bind 127.0.0.1 #要注释掉这部分，这是限制redis只能本地访问
protected-mode no #默认yes，开启保护模式，限制为本地访问
daemonize no#默认no，改为yes意为以守护进程方式启动，可后台运行，除非kill进程，改为yes会使配置文件方式启动redis失败
dir /data #输入本地redis数据库存放文件夹（可选）
————————————————
版权声明：本文为CSDN博主「祗是辉哥哥」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/weixin_42456466/article/details/87270959
</code></pre>
<h3>安装</h3>
<pre><code class="language-shell">mkdir /home/docker/redis/conf
cd /home/docker/redis/conf
vim redis.conf   # 写入配置文件
 
docker run -it -p 6379:6379 --network myapps \
-v /home/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /home/docker/redis/data:/data \
--name redis:6.2.1 \
redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
</code></pre>]]></description><guid isPermaLink="false">/archives/docker-bu-shu-qian-hou-duan-fen-chi-ying-yong</guid><dc:creator>longjuan</dc:creator><enclosure url="https://www.zway.top/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.cos.zway.top%2Fcover645523.png&amp;size=m" type="image/jpeg" length="85743"/><category>遇到的坑</category><category>DevOps</category><category>前端</category><category>后端</category><pubDate>Tue, 16 Mar 2021 08:29:00 GMT</pubDate></item></channel></rss>