add
@@ -0,0 +1,22 @@
|
|||||||
|
## docker-compose.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
code-server:
|
||||||
|
image: linuxserver/code-server:latest
|
||||||
|
container_name: code-server
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1001
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- PASSWORD=Abc.123
|
||||||
|
- SUDO_PASSWORD=Abc.123
|
||||||
|
volumes:
|
||||||
|
- ./conf:/config
|
||||||
|
- ./workspace:/projects
|
||||||
|
ports:
|
||||||
|
- "48331:8443"
|
||||||
|
restart: on-failure:3
|
||||||
|
```
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
```
|
||||||
|
docker run \
|
||||||
|
-d \
|
||||||
|
-p 端口号:8080 \
|
||||||
|
-e APP_NAME=dpanel \
|
||||||
|
--name dpanel \
|
||||||
|
--restart=always \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v 替换成DPanel的存储目录:/dpanel \
|
||||||
|
registry.cn-hangzhou.aliyuncs.com/dpanel/dpanel:lite
|
||||||
|
```
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
```
|
||||||
|
docker run \
|
||||||
|
-d \
|
||||||
|
--name grafana-12.1.1 \
|
||||||
|
-p 宿主机端口号:3000 \
|
||||||
|
-v 替换成数据存储目录:/var/lib/grafana \
|
||||||
|
-v 替换成日志存储目录:/var/log/grafana \
|
||||||
|
-v 替换成插件存储目录:/var/lib/grafana/plugins \
|
||||||
|
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/grafana/grafana-oss:12.1.1
|
||||||
|
```
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
## 为什么要写这一篇
|
||||||
|
|
||||||
|
因为当时 vLLM 部署的 gpt-oss-120b 的模型,总是造成 vLLM 宕机,分析宕机时的崩溃日志是由于 vLLM 根据模型的回复内容调用结构化输出的工具,然后模型回复的内容跟结构后输出函数不兼容所以抛了 ValueError 导致 vLLM 的引擎进程退出,APIServer 与引擎的进程有心跳机制,发现引擎宕机了,所以自杀了。但是没有请求参数日志,不知道啥样的请求参数触发了结构化输出的功能。在 Open AI API 层面是有校验 response_format 的参数合法性的,不合法会直接拒绝。所以当务之急是先捕获请求参数,结合 vLLM 的宕机时间,尝试复现宕机的参数。
|
||||||
|
|
||||||
|
## 踩了很多坑
|
||||||
|
|
||||||
|
- 第一反应是看看能不能调整 vLLM 有没有开启打印请求参数的能力,vLLM 的版本是 v0.11.0,查看源码发现是没有的。
|
||||||
|
- 第二种就是使用抓包工具去实时抓包,选择了 tshark 它是 wireshark 的命令行版本。
|
||||||
|
- 在服务器后台运行了一晚上,天塌了!这叼东西会一直写临时文件,存储路径:/tmp/*.pcap。
|
||||||
|
- 它是抓包网卡的流量,在服务器部署了好几个大模型和 OpenAI API 端点。
|
||||||
|
- 而且有其他同事在压测,一直在并发调用 Open AI API,所以一晚上写了 300G+ 的临时文件,服务器直接告警了,运维挨批了。
|
||||||
|
|
||||||
|
## 终极方案
|
||||||
|
|
||||||
|
不修改 vLLM 的源码,也不用抓包工具,针对 vLLM 部署的 gpt-oss-120b 的 Open AI API 加一层反向代理,把请求参数写到 access_log 并轮转。
|
||||||
|
|
||||||
|
## OpenResty
|
||||||
|
|
||||||
|
用这玩意儿是因为它可以在 nginx.conf 里面写 Lua 脚本,还提供了一系列的增强能力,比直接用 nginx 更省心。
|
||||||
|
|
||||||
|
### nginx.conf
|
||||||
|
|
||||||
|
```
|
||||||
|
worker_processes auto;
|
||||||
|
error_log stderr warn;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 4096;
|
||||||
|
use epoll;
|
||||||
|
multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
lua_need_request_body on;
|
||||||
|
log_escape_non_ascii off;
|
||||||
|
|
||||||
|
# 注意:这里不再使用 $time_local,而是用自定义变量 $log_time
|
||||||
|
log_format llm_audit '[$log_time] | $request_uri | $raw_body';
|
||||||
|
|
||||||
|
upstream llm_backend {
|
||||||
|
server 127.0.0.1:8080; # 修改成你的服务地址
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 100M;
|
||||||
|
client_body_buffer_size 128k;
|
||||||
|
client_body_in_single_buffer on;
|
||||||
|
|
||||||
|
location /v1/chat/completions {
|
||||||
|
set $log_time "";
|
||||||
|
set $raw_body "";
|
||||||
|
|
||||||
|
rewrite_by_lua_block {
|
||||||
|
local now = ngx.time()
|
||||||
|
local tm = os.date("*t", now)
|
||||||
|
ngx.var.log_time = string.format(
|
||||||
|
"%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
tm.year, tm.month, tm.day,
|
||||||
|
tm.hour, tm.min, tm.sec
|
||||||
|
)
|
||||||
|
|
||||||
|
local body = ngx.var.request_body or ""
|
||||||
|
ngx.var.raw_body = body
|
||||||
|
|
||||||
|
-- 判断是否为流式请求
|
||||||
|
if body ~= "" then
|
||||||
|
local cjson = require "cjson.safe"
|
||||||
|
local ok, json = pcall(cjson.decode, body)
|
||||||
|
if ok and type(json) == "table" and json.stream == true then
|
||||||
|
ngx.exec("@stream")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ngx.exec("@normal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ========== 流式响应 ==========
|
||||||
|
location @stream {
|
||||||
|
internal;
|
||||||
|
access_log /hook/request.log llm_audit;
|
||||||
|
|
||||||
|
proxy_pass http://llm_backend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
send_timeout 600s;
|
||||||
|
proxy_connect_timeout 5s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
proxy_socket_keepalive on;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ========== 非流式响应 ==========
|
||||||
|
location @normal {
|
||||||
|
internal;
|
||||||
|
access_log /hook/request.log llm_audit;
|
||||||
|
|
||||||
|
proxy_pass http://llm_backend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_buffering on;
|
||||||
|
proxy_cache off;
|
||||||
|
send_timeout 600s;
|
||||||
|
proxy_connect_timeout 5s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
proxy_socket_keepalive on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### /hook/request.log
|
||||||
|
|
||||||
|
```
|
||||||
|
/hook/request.log {
|
||||||
|
daily
|
||||||
|
rotate 5
|
||||||
|
size 20M
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
copytruncate
|
||||||
|
su root root # 如果 nginx 以非 root 用户运行,需匹配权限
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-compose.yml
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
openresty:
|
||||||
|
image: docker.1ms.run/openresty/openresty:jammy
|
||||||
|
container_name: openresty
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- http_proxy=
|
||||||
|
- https_proxy=
|
||||||
|
- HTTP_PROXY=
|
||||||
|
- HTTPS_PROXY=
|
||||||
|
- no_proxy=
|
||||||
|
- NO_PROXY=
|
||||||
|
ports:
|
||||||
|
- "28000:8000"
|
||||||
|
volumes:
|
||||||
|
# 宿主机这个目录可以查看 request.log 也必须包含 nginx.conf
|
||||||
|
- ./hook:/hook
|
||||||
|
# 日志轮转配置文件
|
||||||
|
- ./hook/hook-nginx:/etc/logrotate.d/hook-nginx
|
||||||
|
command: ["/usr/local/openresty/bin/openresty", "-c", "/hook/nginx.conf", "-g", "daemon off;"]
|
||||||
|
restart: on-failure:3
|
||||||
|
```
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
## 目录结构
|
||||||
|
|
||||||
|
- pgsql-gis/
|
||||||
|
- docker-compose.yaml
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkdir pgsql-gis
|
||||||
|
```
|
||||||
|
|
||||||
|
## docker-compose.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
pgsql-gis-16:
|
||||||
|
# 支持全文检索的自编译镜像
|
||||||
|
image: pgsql-gis-fts:latest
|
||||||
|
container_name: pgsql-gis-16
|
||||||
|
ports:
|
||||||
|
- "35430:5432" # 替换
|
||||||
|
volumes:
|
||||||
|
- ./pgsql-gis:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: # 替换
|
||||||
|
POSTGRES_PASSWORD: # 替换
|
||||||
|
POSTGRES_DB: # 替换
|
||||||
|
POSTGRES_INITDB_ARGS: --encoding=UTF8
|
||||||
|
restart: on-failure:3
|
||||||
|
```
|
||||||
|
|
||||||
|
## 添加全文索引支持
|
||||||
|
|
||||||
|
需要从 git 克隆 zhparser 添加中文分词支持,执行 docker build 命令的目录结构长这样:
|
||||||
|
|
||||||
|
- Dockerfile
|
||||||
|
- zhparser/
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/amutu/zhparser.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dockerfile
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/postgis/postgis:16-3.5
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
|
||||||
|
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
git \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
wget \
|
||||||
|
postgresql-server-dev-16 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN pg_config --version
|
||||||
|
|
||||||
|
WORKDIR /tmp
|
||||||
|
|
||||||
|
RUN wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2 \
|
||||||
|
&& tar xjf scws-1.2.3.tar.bz2 \
|
||||||
|
&& cd scws-1.2.3 \
|
||||||
|
&& ./configure \
|
||||||
|
&& make && make install \
|
||||||
|
&& cd ..
|
||||||
|
|
||||||
|
COPY zhparser zhparser
|
||||||
|
|
||||||
|
RUN cd zhparser \
|
||||||
|
&& export PG_CONFIG=$(which pg_config) \
|
||||||
|
&& echo "Using pg_config: $PG_CONFIG" \
|
||||||
|
&& make USE_PGXS=1 \
|
||||||
|
&& make USE_PGXS=1 install \
|
||||||
|
&& cd ..
|
||||||
|
|
||||||
|
RUN rm -rf /tmp/*
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
```
|
||||||
|
|
||||||
|
## 编译
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t pgsql-gis-fts .
|
||||||
|
```
|
||||||
|
|
||||||
|
## 启用中文分词扩展
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE EXTENSION IF NOT EXISTS zhparser;
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TEXT SEARCH CONFIGURATION chinese_mix (PARSER = zhparser);
|
||||||
|
-- DROP TEXT SEARCH CONFIGURATION IF EXISTS chinese_mix;
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TEXT SEARCH CONFIGURATION chinese_mix ADD MAPPING FOR n,v,a,i,e,l,d,j,m,q,r,t,u,w,x,z WITH simple;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE articles (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
title TEXT,
|
||||||
|
content TEXT,
|
||||||
|
-- 生成列:自动维护 tsvector,避免每次查询都计算,提高性能
|
||||||
|
content_tsvector TSVECTOR GENERATED ALWAYS AS (to_tsvector('chinese_mix', content)) STORED
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 插入一些混合数据
|
||||||
|
INSERT INTO articles (title, content) VALUES
|
||||||
|
('AI 技术展望', '人工智能 (AI) 正在改变世界,PostgreSQL 是存储这些数据的首选数据库。'),
|
||||||
|
('PostgreSQL 16 新特性', 'PostgreSQL 16 带来了更好的性能,支持 JSON 增强和中文分词优化。'),
|
||||||
|
('日常开发笔记', 'Today I learned about zhparser. 它让中文搜索变得很简单。');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 搜索包含 "人工智能" 的文章
|
||||||
|
SELECT title, content
|
||||||
|
FROM articles
|
||||||
|
WHERE content_tsvector @@ to_tsquery('chinese_mix', '人工智能');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 搜索包含 "PostgreSQL" 的文章
|
||||||
|
SELECT title, content
|
||||||
|
FROM articles
|
||||||
|
WHERE content_tsvector @@ to_tsquery('chinese_mix', 'PostgreSQL');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 搜索既包含 "数据库" 又包含 "PostgreSQL" 的文章
|
||||||
|
SELECT title, content
|
||||||
|
FROM articles
|
||||||
|
WHERE content_tsvector @@ to_tsquery('chinese_mix', '数据库 & PostgreSQL');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 搜索包含 "AI" 或者 "改变" 的文章
|
||||||
|
SELECT title, content
|
||||||
|
FROM articles
|
||||||
|
WHERE content_tsvector @@ to_tsquery('chinese_mix', 'AI | 改变');
|
||||||
|
```
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
## 目录结构
|
||||||
|
|
||||||
|
- docker-compose.yaml
|
||||||
|
- redis.conf
|
||||||
|
- data/
|
||||||
|
|
||||||
|
## docker-compose.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:8.6 # 替换
|
||||||
|
container_name: redis
|
||||||
|
ports:
|
||||||
|
- "52358:6379" # 替换
|
||||||
|
volumes:
|
||||||
|
- ./redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
- ./data:/data
|
||||||
|
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||||
|
restart: on-failure:3
|
||||||
|
```
|
||||||
|
|
||||||
|
## redis.conf
|
||||||
|
|
||||||
|
```text
|
||||||
|
bind 0.0.0.0
|
||||||
|
port 6379
|
||||||
|
requirepass 1965589280@Jkw!
|
||||||
|
protected-mode no
|
||||||
|
|
||||||
|
dir /data
|
||||||
|
|
||||||
|
save 900 1
|
||||||
|
save 300 10
|
||||||
|
save 60 10000
|
||||||
|
dbfilename dump.rdb
|
||||||
|
rdbcompression yes
|
||||||
|
rdbchecksum yes
|
||||||
|
|
||||||
|
appendonly yes
|
||||||
|
appendfilename "appendonly.aof"
|
||||||
|
appendfsync everysec
|
||||||
|
|
||||||
|
timeout 0
|
||||||
|
tcp-keepalive 300
|
||||||
|
loglevel notice
|
||||||
|
databases 16
|
||||||
|
daemonize no
|
||||||
|
supervised no
|
||||||
|
always-show-logo no
|
||||||
|
```
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
## JDK21
|
||||||
|
```
|
||||||
|
FROM docker.1ms.run/eclipse-temurin:21-jdk
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
RUN echo "deb https://mirrors.aliyun.com/debian/ bullseye main contrib non-free" > /etc/apt/sources.list && \
|
||||||
|
echo "deb https://mirrors.aliyun.com/debian-security/ bullseye-security main contrib non-free" >> /etc/apt/sources.list && \
|
||||||
|
echo "deb https://mirrors.aliyun.com/debian/ bullseye-updates main contrib non-free" >> /etc/apt/sources.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y git maven && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## JDK8
|
||||||
|
```
|
||||||
|
FROM docker.1ms.run/maven:3.8.6-jdk-8
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
RUN echo "deb https://mirrors.aliyun.com/debian/ bullseye main contrib non-free" > /etc/apt/sources.list && \
|
||||||
|
echo "deb https://mirrors.aliyun.com/debian-security/ bullseye-security main contrib non-free" >> /etc/apt/sources.list && \
|
||||||
|
echo "deb https://mirrors.aliyun.com/debian/ bullseye-updates main contrib non-free" >> /etc/apt/sources.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y git && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用本地 JDK 与 Maven 构建
|
||||||
|
|
||||||
|
要先下载 OpenJDK 和 Maven,与 Dockerfile 同级目录,分别命名为 jdk-21 和 mvn-3,目录结构:
|
||||||
|
|
||||||
|
- Dockerfile
|
||||||
|
- jdk-21/
|
||||||
|
- mvn-3/
|
||||||
|
|
||||||
|
```shell
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive \
|
||||||
|
TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
locales \
|
||||||
|
tzdata \
|
||||||
|
ca-certificates \
|
||||||
|
git && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN locale-gen zh_CN.UTF-8 en_US.UTF-8
|
||||||
|
|
||||||
|
ENV LANG=zh_CN.UTF-8 \
|
||||||
|
LANGUAGE=zh_CN:zh \
|
||||||
|
LC_ALL=zh_CN.UTF-8
|
||||||
|
|
||||||
|
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||||
|
dpkg-reconfigure -f noninteractive tzdata
|
||||||
|
|
||||||
|
WORKDIR /opt
|
||||||
|
|
||||||
|
COPY jdk-21 jdk21
|
||||||
|
COPY mvn-3 mvn
|
||||||
|
|
||||||
|
ENV JAVA_HOME=/opt/jdk21 \
|
||||||
|
MAVEN_HOME=/opt/mvn \
|
||||||
|
PATH="/opt/jdk21/bin:/opt/mvn/bin:${PATH}"
|
||||||
|
```
|
||||||
|
|
||||||
|
执行命令开始构建
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker build -t ubuntu-jdk-21 .
|
||||||
|
```
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
```
|
||||||
|
FROM docker.1ms.run/library/mvn-java-8:latest
|
||||||
|
ENV PROJECT_ACTIVE=根据实际情况激活 application-xxx.yaml 配置文件,比如:prod
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE springboot 内嵌的 servlet 容器端口号
|
||||||
|
|
||||||
|
# 配置容器启动命令
|
||||||
|
RUN << 'EOF' cat > /entrypoint.sh
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd /app && rm -rf 源码目录
|
||||||
|
git clone "http://用户名:密码@远程仓库地址.git"
|
||||||
|
cd /app/源码目录 && mvn clean package
|
||||||
|
exec java -jar target/jar包名称.jar --spring.profiles.active=$PROJECT_ACTIVE
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
```
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
```
|
||||||
|
FROM docker.1ms.run/library/mvn-java-21:latest
|
||||||
|
ENV PROJECT_ACTIVE=根据实际情况激活 application-xxx.yaml 配置文件,比如:prod
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE springboot 内嵌的 servlet 容器端口号
|
||||||
|
|
||||||
|
# 配置容器启动命令
|
||||||
|
RUN << 'EOF' cat > /entrypoint.sh
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd /app && rm -rf 源码目录
|
||||||
|
git clone "http://用户名:密码@远程仓库地址.git"
|
||||||
|
cd /app/源码目录 && mvn clean package
|
||||||
|
exec java -jar target/jar包名称.jar --spring.profiles.active=$PROJECT_ACTIVE
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
```
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
```
|
||||||
|
FROM docker.1ms.run/node:16-alpine
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
|
RUN apk add --no-cache git nginx
|
||||||
|
```
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# 前提条件
|
||||||
|
|
||||||
|
- 系统:Ubuntu 22.04
|
||||||
|
- 架构:x86_x64
|
||||||
|
|
||||||
|
## 安装必要工具
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y ca-certificates curl gnupg lsb-release
|
||||||
|
```
|
||||||
|
|
||||||
|
## 添加 Docker 的官方 GPG 密钥
|
||||||
|
|
||||||
|
先检查是否包含相关目录
|
||||||
|
```bash
|
||||||
|
ls /etc/apt/keyrings
|
||||||
|
```
|
||||||
|
|
||||||
|
如果没有则创建
|
||||||
|
```bash
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
```
|
||||||
|
|
||||||
|
下载并保存
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
```
|
||||||
|
|
||||||
|
## 设置 Docker 仓库
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新并安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置 Docker 代理服务(可选)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /etc/systemd/system/docker.service.d
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
[Service]
|
||||||
|
Environment="HTTP_PROXY=http://192.168.31.9:7890"
|
||||||
|
Environment="HTTPS_PROXY=http://192.168.31.9:7890"
|
||||||
|
Environment="NO_PROXY=localhost,127.0.0.1,::1,docker-registry.somecorporation.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
验证代理配置是否生效
|
||||||
|
```bash
|
||||||
|
sudo systemctl show --property=Environment docker
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置 NVIDIA 容器环境(可选)
|
||||||
|
|
||||||
|
添加 NVIDIA 包仓库
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit.gpg
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
```
|
||||||
|
|
||||||
|
安装 NVIDIA 容器工具包
|
||||||
|
```bash
|
||||||
|
sudo apt install -y nvidia-container-toolkit
|
||||||
|
```
|
||||||
|
|
||||||
|
配置 NVIDIA 运行时
|
||||||
|
```bash
|
||||||
|
sudo nvidia-ctk runtime configure --runtime=docker
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
测试是否能识别
|
||||||
|
```bash
|
||||||
|
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
|
||||||
|
```
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
## Ubuntu 24.04
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 备份原文件(可选)
|
||||||
|
sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo sed -i 's|http://archive.ubuntu.com|http://mirrors.ustc.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo sed -i 's|http://security.ubuntu.com|http://mirrors.ustc.edu.cn|g' /etc/apt/sources.list.d/ubuntu.sources
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
```
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# 安装相关工具
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y pciutils util-linux
|
||||||
|
```
|
||||||
|
|
||||||
|
# 打印系统详情
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "=== 系统核心信息 ===" && lsb_release -a && echo -e "\n=== 内核版本 ===" && uname -r && echo -e "\n=== 硬件架构 ===" && uname -m && echo -e "\n=== CPU 信息 ===" && lscpu | grep -E "架构|CPU:|型号名称" && echo -e "\n=== 内存使用情况 ===" && free -h && echo -e "\n=== 磁盘空间 (根分区) ===" && df -h / && echo -e "\n=== 显卡信息 ===" && lspci -k | grep -A 2 -i vga && echo -e "\n=== 当前运行内核引导参数 ===" && cat /proc/cmdline
|
||||||
|
```
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
## 用户级别
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nano ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# Proxy Configuration
|
||||||
|
export HTTP_PROXY="http://192.168.31.9:7890"
|
||||||
|
export HTTPS_PROXY="http://192.168.31.9:7890"
|
||||||
|
export http_proxy="http://192.168.31.9:7890"
|
||||||
|
export https_proxy="http://192.168.31.9:7890"
|
||||||
|
|
||||||
|
# 可选:设置不走代理的地址 (本地地址和内部域名)
|
||||||
|
export NO_PROXY="localhost,127.0.0.1,::1,192.168.*,10.*"
|
||||||
|
export no_proxy="localhost,127.0.0.1,::1,192.168.*,10.*"
|
||||||
|
```
|
||||||
|
|
||||||
|
保存并退出(`Ctrl + O` `Enter` `Ctrl + X`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
## 对所有用户生效(需重启系统)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/environment
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP_PROXY="http://192.168.31.9:7890"
|
||||||
|
HTTPS_PROXY="http://192.168.31.9:7890"
|
||||||
|
http_proxy="http://192.168.31.9:7890"
|
||||||
|
https_proxy="http://192.168.31.9:7890"
|
||||||
|
NO_PROXY="localhost,127.0.0.1,::1"
|
||||||
|
no_proxy="localhost,127.0.0.1,::1"
|
||||||
|
```
|
||||||
|
|
||||||
|
保存并退出(`Ctrl + O` `Enter` `Ctrl + X`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# 文档
|
||||||
|
|
||||||
|
- 原作者:https://ehang-io.github.io/nps
|
||||||
|
- 维护者:https://my.feishu.cn/wiki/FmVVwDcEGiTZxekYJl5ccuFanlg
|
||||||
|
- Github:https://github.com/yisier/nps/releases
|
||||||
|
|
||||||
|
## 服务端
|
||||||
|
|
||||||
|
下载`linux_amd64_server.tar.gz`压缩包并解压,然后执行`sudo ./nps install`安装。修改配置文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
nano /etc/nps/conf/nps.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# 自定义,英文+数字,不超过16位
|
||||||
|
public_vkey=
|
||||||
|
# 自定义,英文+数字,不超过16位
|
||||||
|
auth_key=
|
||||||
|
# 自定义,英文+数字,固定16位
|
||||||
|
auth_crypt_key=
|
||||||
|
# 网页端管理员用户名
|
||||||
|
web_username=admin
|
||||||
|
# 网页端管理员账号的密码,英文+数字+特殊符号(.@!)
|
||||||
|
web_password=
|
||||||
|
|
||||||
|
appname = nps
|
||||||
|
#Boot mode(dev|pro)
|
||||||
|
runmode = pro
|
||||||
|
|
||||||
|
#HTTP(S) proxy port, no startup if empty
|
||||||
|
http_proxy_ip=0.0.0.0
|
||||||
|
http_proxy_port=26666
|
||||||
|
https_proxy_port=36666
|
||||||
|
https_just_proxy=true
|
||||||
|
|
||||||
|
#default https certificate setting
|
||||||
|
https_default_cert_file=conf/server.pem
|
||||||
|
https_default_key_file=conf/server.key
|
||||||
|
|
||||||
|
##bridge
|
||||||
|
bridge_type=tcp
|
||||||
|
bridge_port=28888
|
||||||
|
bridge_ip=0.0.0.0
|
||||||
|
|
||||||
|
#Traffic data persistence interval(minute)
|
||||||
|
#Ignorance means no persistence
|
||||||
|
flow_store_interval=1
|
||||||
|
|
||||||
|
# log level LevelEmergency->0 LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7
|
||||||
|
log_level=6
|
||||||
|
log_path=nps.log
|
||||||
|
|
||||||
|
#p2p
|
||||||
|
#p2p_ip=127.0.0.1
|
||||||
|
#p2p_port=6000
|
||||||
|
|
||||||
|
#web
|
||||||
|
web_host=a.o.com
|
||||||
|
web_port = 38888
|
||||||
|
web_ip=0.0.0.0
|
||||||
|
web_base_url=
|
||||||
|
web_open_ssl=false
|
||||||
|
web_cert_file=conf/server.pem
|
||||||
|
web_key_file=conf/server.key
|
||||||
|
|
||||||
|
# if web under proxy use sub path. like http://host/nps need this.
|
||||||
|
#web_base_url=/nps
|
||||||
|
#allow_ports=9001-9009,10001,11000-12000
|
||||||
|
|
||||||
|
#Web management multi-user login
|
||||||
|
allow_user_login=false
|
||||||
|
allow_user_register=false
|
||||||
|
allow_user_change_username=false
|
||||||
|
|
||||||
|
#extension
|
||||||
|
#流量限制
|
||||||
|
allow_flow_limit=true
|
||||||
|
#带宽限制
|
||||||
|
allow_rate_limit=true
|
||||||
|
#客户端最大隧道数限制
|
||||||
|
allow_tunnel_num_limit=true
|
||||||
|
allow_local_proxy=false
|
||||||
|
#客户端最大连接数
|
||||||
|
allow_connection_num_limit=true
|
||||||
|
#每个隧道监听不同的服务端端口
|
||||||
|
allow_multi_ip=true
|
||||||
|
system_info_display=true
|
||||||
|
|
||||||
|
#获取用户真实ip
|
||||||
|
http_add_origin_header=true
|
||||||
|
|
||||||
|
#cache
|
||||||
|
http_cache=false
|
||||||
|
http_cache_length=10
|
||||||
|
#get origin ip
|
||||||
|
#http_add_origin_header=false
|
||||||
|
|
||||||
|
#pprof debug options
|
||||||
|
#pprof_ip=0.0.0.0
|
||||||
|
#pprof_port=9999
|
||||||
|
|
||||||
|
#client disconnect timeout
|
||||||
|
disconnect_timeout=60
|
||||||
|
|
||||||
|
#管理面板开启验证码校验
|
||||||
|
open_captcha=false
|
||||||
|
|
||||||
|
# 是否开启tls
|
||||||
|
tls_enable=true
|
||||||
|
tls_bridge_port=48888
|
||||||
|
```
|
||||||
|
|
||||||
|
修改完成后,执行`sudo nps start`启动服务端。
|
||||||
|
|
||||||
|
| 端口号 | 用途 |
|
||||||
|
|:-----|:-----|
|
||||||
|
| 26666 | http代理端口 |
|
||||||
|
| 36666 | https代理端口 |
|
||||||
|
| 28888 | TCP隧道端口 |
|
||||||
|
| 38888 | WebUI的端口 |
|
||||||
|
| 48888 | TCP隧道 TLS 端口 |
|
||||||
|
|
||||||
|
|
||||||
|
## 客户端
|
||||||
|
|
||||||
|
下载`linux_amd64_client.tar.gz`压缩包并解压
|
||||||
|
|
||||||
|
```
|
||||||
|
# 安装
|
||||||
|
sudo ./npc install -server=服务端IP:28888 -vkey=<vkey>
|
||||||
|
# 启动
|
||||||
|
sudo npc start
|
||||||
|
# 停止
|
||||||
|
sudo npc stop
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
## 关键字搜索并排序
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -h '关键字' *.log | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打印一个时间范围的片段
|
||||||
|
|
||||||
|
按分钟级匹配,按秒的话结束时间不存在日志,会导致一直输出到文件末尾
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sed -n '/2026-03-26 10:15/, /2026-03-26 10:20/p' xxx.log
|
||||||
|
```
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# Intro
|
||||||
|
|
||||||
|
本文介绍如何把 NAS 主机目录挂载到 Linux 主机,并通过 Volume 挂载到 Docker 容器,借助 NAS 的阵列盘实现高可靠数据存储。
|
||||||
|
|
||||||
|
- NAS:"/vol1/workspace"
|
||||||
|
- Linux:"/mnt/nas/vol1/workspace"
|
||||||
|
- Docker:"/workspace"
|
||||||
|
|
||||||
|
假设 NAS 的用户名密码是"admin/123456",IP为"192.168.1.100",目录映射目标:"/vol1/workspace" -> "/mnt/nas/vol1/workspace" -> "/workspace"。
|
||||||
|
|
||||||
|
## 基于 NFS 协议挂载
|
||||||
|
|
||||||
|
1. 在 Linux 安装相关工具
|
||||||
|
|
||||||
|
Debian/Ubuntu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install nfs-common -y
|
||||||
|
```
|
||||||
|
|
||||||
|
CentOS/RHEL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yum install nfs-utils
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 创建目录
|
||||||
|
|
||||||
|
NAS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /vol1/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /mnt/nas/vol1/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 授权(UID=1000,GID=1000)
|
||||||
|
|
||||||
|
NAS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown -R 1000:1000 /vol1/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown -R 1000:1000 /mnt/nas/vol1/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 在 Linux 执行挂载
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount -t nfs -o rw,vers=4.2,noatime,rsize=1048576,wsize=1048576,timeo=1200,retrans=2,_netdev 192.168.1.100:/vol1/workspace /mnt/nas/vol1/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数名称 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| rw | 拥有读写权限 |
|
||||||
|
| vers | 使用 nfs 4.2 版本 |
|
||||||
|
| noatime | 不更新文件的访问时间戳,能显著减少磁盘 I/O 和网络开销,提升性能 |
|
||||||
|
| rsize/wsize | 读写块大小,1MB 是 NFS v4.2 推荐的尺寸,适合高速内网传输文件,有效利用网络带宽 |
|
||||||
|
| timeo | 客户端等待重传前的超时时间,给网络波动留的容忍度,单位是厘秒,1200=120秒 |
|
||||||
|
| retrans | 连接彻底失败之前,仅重传 2 次。配合长超时,客户端会很有耐心地等待,不会因短暂网络抖动而频繁报错|
|
||||||
|
| _netdev | 告诉系统等网络可用后再挂载,防止在启动初期,网络未就绪时挂载失败 |
|
||||||
|
|
||||||
|
5. 在 Linux 设置开机自动挂载(可选但强烈推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/fstab
|
||||||
|
```
|
||||||
|
|
||||||
|
在文件末尾追加一行:
|
||||||
|
|
||||||
|
```text
|
||||||
|
192.168.1.100:/vol1/workspace /mnt/nas/vol1/workspace nfs rw,vers=4.2,noatime,rsize=1048576,wsize=1048576,timeo=1200,retrans=2,_netdev 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
6. 在 Linux 运行 Docker 容器并挂载
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -v /mnt/nas/vol1/workspace:/workspace --user 1000:1000 <镜像名>
|
||||||
|
```
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# 客户端下载
|
||||||
|
|
||||||
|
| 系统 | 点击下载 |
|
||||||
|
|:-----|:-----|
|
||||||
|
| Windows |[x86](https://stf.jkwlstv.cn/app/cfw_x86.exe) [Arm](https://stf.jkwlstv.cn/app/cfw_arm.exe)|
|
||||||
|
| Mac |[Intel](https://stf.jkwlstv.cn/app/cfm_intel.dmg) [Arm](https://stf.jkwlstv.cn/app/cfm_arm.dmg)|
|
||||||
|
| Android |[通用](https://stf.jkwlstv.cn/app/cmfa.apk)|
|
||||||
|
|
||||||
|
## Android
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||||
|
<img src="./img/cfa-1.png" width="48%">
|
||||||
|
<img src="./img/cfa-2.png" width="48%">
|
||||||
|
<img src="./img/cfa-3.png" width="48%">
|
||||||
|
<img src="./img/cfa-4.png" width="48%">
|
||||||
|
<img src="./img/cfa-5.png" width="48%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## iOS
|
||||||
|
|
||||||
|
需要切换到美区 App Store 下载安装 **Potatso** 这款 APP,所以需要先注册一个美区的 Apple ID。
|
||||||
|
|
||||||
|
- 注册美区 Apple ID 教程:https://clashxpro.net/apple-id
|
||||||
|
- APP下载链接:https://apps.apple.com/us/app/potatso/id1239860606
|
||||||
|
- 使用教程:https://potatso.net **订阅链接见公告**
|
||||||
|
|
||||||
|
Potatso 是**免费**的,不差钱可以用 Shadowrocket。
|
||||||
|
|
||||||
|
## Mac/Win
|
||||||
|
|
||||||
|
### 添加订阅
|
||||||
|
<img src="./img/clash-pc-1.png" width="70%">
|
||||||
|
<img src="./img/clash-pc-2.png" width="70%">
|
||||||
|
|
||||||
|
### 配置文件链接见公告
|
||||||
|
<img src="./img/clash-pc-3.png" width="70%">
|
||||||
|
|
||||||
|
### 开启局域网代理(可选)
|
||||||
|
<img src="./img/clash-pc-4.png" width="70%">
|
||||||
|
<img src="./img/clash-pc-5.png" width="70%">
|
||||||
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 235 KiB |