Total Pageviews

Tuesday, 24 March 2026

在纯Docker里面,部署Https服务——以Nextcloud为例


背景

原先各种服务的部署方式都是直接部署的,换主机需要重新搭建各种环境非常麻烦,也容易遗漏。于是就想跟随潮流把这些服务都上docker,能够做到部署一把梭。

难点

一般的开源组件都会有官方docker镜像,部署起来其实都很方便。大部分对着官方文档改改配置再用docker-compose组合一下参数就能跑起来。比较麻烦的点就是网站要部署成 https 的话需要 SSL 证书。对于个人使用的小网站来说,云厂商的 SSL 证书又实在是比较贵(阿里云上通配符域名大概2000¥/年,单域名也要400¥/年)。

比较经济实惠的做法是使用letsencrypt的免费证书,不过代价就是他需要定时check你对这个域名的所有权。显然,我们肯定不能手动更新,那样简直要疯。一般我们会用 certbot( https://certbot.eff.org/)来定时进行站点所有权的确认。经常对接站长后台工具的人应该都知道,认证的方式一般有两种:一种是将给定的验证字符串写进 DNS 的 TXT 记录,从而确认你对这个域名的所有权;第二种是将给定的验证字符串写在网站的给定目录下,从而确认你对这个网站的所有权。

理论上最方便的做法是通过一个定时任务,定时调用 DNS 服务商的接口来改 TXT 记录。这样可以做到将 SSL 证书的校验和网站本身的部署分离开,使得校验所有权的逻辑不干扰正常的网站配置。不过尴尬的是 certbot 提供的 DNS插件( https://eff-certbot.readthedocs.io/en/stable/using.html)基本都不包含国内的运营商。而考虑到域名在国内解析的性能,我还是不太想把域名切到国外的服务商去。

因此事实上我只能采用 webroot 的方式来认证。这个方式比较万金油,不过缺点就是不支持通配符域名,如果需要同时验证多个子域名,则需要手动添加。同时,传统的 certbot 使用方式中一定还需要在系统中配置一个 crontab 任务来做更新,而我现在则希望将这个 crontab 任务也集成在 docker 中,尽量不要对除 docker 外的逻辑做任何感知。

步骤

逻辑上讲,步骤大致应当如下:

  1. 去 DNS 服务商提供的配置后台,将给定域名配置好A记录指向目的主机。
  2. 安装 docker(https://docs.docker.com/engine/install/)和  docker-compose( https://docs.docker.com/compose/install/),并设置好配置文件。
  3. 配置并启动 nginx 只开放 http 端口 ,准备 certbot 的认证环境。
  4. 启动 certbot 初始化,配合nginx,生成首个私钥和证书链。
  5. 利用 certbot 提供的私钥和证书,配置 nginx 的 https 端口。
  6. 配置 certbot 的自动 renew ,进行自动验证。

样例

下面以在 https://xyz.com 下配置 nextcloud( https://nextcloud.com/)为例。 DNS 配置、docker 和 docker-compose 安装等步骤略过。

初始化docker配置

./docker-compose.yml

version: "3.8"
services:
    nginx:
        container_name: nginx
        image: nginx:latest
        restart: always
        volumes:
            - ./nginx/logs:/var/log/nginx
            - ./nginx/conf.d:/etc/nginx/conf.d
            - ./certbot/conf:/etc/nginx/ssl
            - ./certbot/data:/var/www/certbot
        ports:
            - "80:80"
            - "443:443"
        command: ["/bin/sh", "-c", "while :;do sleep 24h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\""]

    certbot:
        container_name: certbot
        image: certbot/certbot:latest
        command: certonly --webroot --webroot-path=/var/www/certbot --agree-tos --email mythsman@foxmail.com -d  xyz.com
        # entrypoint: ["/bin/sh", "-c", "trap exit TERM;while :; do certbot renew --webroot -w /var/www/certbot; sleep 24h & wait $${!}; done;"]
        volumes:
            - ./certbot/conf:/etc/letsencrypt
            - ./certbot/logs:/var/log/letsencrypt
            - ./certbot/data:/var/www/certbot

(需要注意,certbot 的 latest 镜像不支持 arm,如果当前机器是 arm 架构则需要去 dockerhub 中自行选购.)

说明:

  1. ./certbot/conf 目录存放的即是 certbot 生成的证书等一大堆文件,因此需要挂载给 nginx 。
  2. ./certbot/logs 目录下存放的是证书创建相关的日志。
  3. ./certbot/data 目录是 certbot 用来存放校验字符串的,需要通过nginx对外网暴露,因此也需要挂在给 nginx。
  4. ./nginx/logs 目录存放的即是 nginx 的相关日志。
  5. ./nginx/conf.d 目录存放 nginx 的配置,下面会介绍。
  6. nginx 的 command 指令写了一个小脚本,用来让 nginx 定时自动 reload,方便 certbot 刷新证书后 nginx 能及时更新。
  7. certbot 的 command 指令中需要配置接受邮箱,用来接受一些通知消息,以及需要配置的域名xyz.com
  8. certbot 的 entrypoint 指令是用来后续 renew 的,暂时注释掉。

初始化nginx配置

./nginx/conf.d/default.conf

server {
     listen [::]:80 default_server;
     listen 80 default_server;

     server_name _;

     location ~ /.well-known/acme-challenge {
         allow all;
         root /var/www/certbot;
     }

     location / {
         if ( $host = "xyz.com" ){
             return 301 https://xyz.com$request_uri;
         }
         return 444;
     }

}

server {
       listen 443 default_server;
       listen [::]:443 default_server;
       server_name _;
       ssl_reject_handshake on;
       ssl_session_tickets off;
}

说明:

  1. 这里最关键的是 .well-known 行,用来关联 certbot 的校验文件。
  2. return 444 是用来拒绝掉一些未知域名的http访问。
  3. ssl_reject_handshake 是用来拒绝掉一些未知域名的https的访问。

生成证书

执行 docker-compose up -d ,证书生成成功后会在 ./certbot/logs/letsencrypt.log 打印相关日志:

2022-03-09 03:41:39,879:DEBUG:certbot._internal.storage:Creating directory /etc/letsencrypt/archive.
2022-03-09 03:41:39,879:DEBUG:certbot._internal.storage:Creating directory /etc/letsencrypt/live.
2022-03-09 03:41:39,879:DEBUG:certbot._internal.storage:Writing README to /etc/letsencrypt/live/README.
2022-03-09 03:41:39,879:DEBUG:certbot._internal.storage:Creating directory /etc/letsencrypt/archive/xyz.com.
2022-03-09 03:41:39,879:DEBUG:certbot._internal.storage:Creating directory /etc/letsencrypt/live/xyz.com.
2022-03-09 03:41:39,880:DEBUG:certbot._internal.storage:Writing certificate to /etc/letsencrypt/live/xyz.com/cert.pem.
2022-03-09 03:41:39,880:DEBUG:certbot._internal.storage:Writing private key to /etc/letsencrypt/live/xyz.com/privkey.pem.
2022-03-09 03:41:39,880:DEBUG:certbot._internal.storage:Writing chain to /etc/letsencrypt/live/xyz.com/chain.pem.
2022-03-09 03:41:39,880:DEBUG:certbot._internal.storage:Writing full chain to /etc/letsencrypt/live/xyz.com/fullchain.pem.
2022-03-09 03:41:39,880:DEBUG:certbot._internal.storage:Writing README to /etc/letsencrypt/live/xyz.com/README.
2022-03-09 03:41:39,909:DEBUG:certbot._internal.plugins.selection:Requested authenticator webroot and installer <certbot._internal.cli.cli_utils._Default object at 0x7f94027a1370>
2022-03-09 03:41:39,909:DEBUG:certbot._internal.cli:Var authenticator=webroot (set by user).
2022-03-09 03:41:39,909:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2022-03-09 03:41:39,909:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2022-03-09 03:41:39,909:DEBUG:certbot._internal.cli:Var webroot_map={'webroot_path'} (set by user).
2022-03-09 03:41:39,909:DEBUG:certbot._internal.storage:Writing new config /etc/letsencrypt/renewal/xyz.com.conf.
2022-03-09 03:41:39,911:DEBUG:certbot._internal.display.obj:Notifying user:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/xyz.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/xyz.com/privkey.pem
This certificate expires on 2022-06-07.
These files will be updated when the certificate renews.
2022-03-09 03:41:39,911:DEBUG:certbot._internal.display.obj:Notifying user: NEXT STEPS:
2022-03-09 03:41:39,912:DEBUG:certbot._internal.display.obj:Notifying user: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
2022-03-09 03:41:39,913:DEBUG:certbot._internal.display.obj:Notifying user: If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le

也可以通过 nginx 的访问日志 /nginx/logs/access.log  查看他在校验的过程中访问了哪些页面:

3.120.130.29 - - [09/Mar/2022:03:41:36 +0000] "GET /.well-known/acme-challenge/fNVMV-CtDCdq_eAdiaV0rF_J7I-ZW38M7Bo3UqEtOY4 HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" "-"
3.19.56.43 - - [09/Mar/2022:03:41:36 +0000] "GET /.well-known/acme-challenge/fNVMV-CtDCdq_eAdiaV0rF_J7I-ZW38M7Bo3UqEtOY4 HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" "-"
34.221.255.206 - - [09/Mar/2022:03:41:36 +0000] "GET /.well-known/acme-challenge/fNVMV-CtDCdq_eAdiaV0rF_J7I-ZW38M7Bo3UqEtOY4 HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" "-"
64.78.149.164 - - [09/Mar/2022:03:41:36 +0000] "GET /.well-known/acme-challenge/fNVMV-CtDCdq_eAdiaV0rF_J7I-ZW38M7Bo3UqEtOY4 HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" "-"

配置站点容器和证书更新

./docker-compose.yml

version: "3.8"
services:
    nginx:
        container_name: nginx
        image: nginx:latest
        restart: always
        volumes:
            - ./nginx/logs:/var/log/nginx
            - ./nginx/conf.d:/etc/nginx/conf.d
            - ./certbot/conf:/etc/nginx/ssl
            - ./certbot/data:/var/www/certbot
        ports:
            - "80:80"
            - "443:443"
        command: ["/bin/sh", "-c", "while :;do sleep 24h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\""]

    certbot:
        container_name: certbot
        image: certbot/certbot:latest
        # command: certonly --webroot --webroot-path=/var/www/certbot --agree-tos --email mythsman@foxmail.com -d xyz.com
        entrypoint: ["/bin/sh", "-c", "trap exit TERM;while :; do certbot renew --webroot -w /var/www/certbot; sleep 24h & wait $${!}; done;"]
        volumes:
            - ./certbot/conf:/etc/letsencrypt
            - ./certbot/logs:/var/log/letsencrypt
            - ./certbot/data:/var/www/certbot
    nextcloud:
        container_name: nextcloud
        image: nextcloud:latest
        volumes:
            - ./nextcloud:/var/www/html
        ports:
            - "8080:80"

说明:

  1. 这里将 cerbot 的 command 注释掉,更换了 entrypoint 的启动脚本,用来每天自动更新 cerbot 证书。
  2. 新增了 nextcloud 的镜像。

配置站点的Nginx

./nginx/conf.d/xyz.com-ssl.conf

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name xyz.com;

    ssl_certificate /etc/nginx/ssl/live/xyz.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/xyz.com/privkey.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://172.17.0.1:8080;
    }

    client_max_body_size 10G;

}

说明:

  1. proxy_pass 中的 172.17.0.1 为 host 主机在 docker 中访问的 ip,8080端口暴露的即为前文设置的 nextcloud 的服务。
  2. ssl_certificate 和 ssl_certificate_key 配置为前文 certbot 生成的文件。
  3. 如果申请证书的时候也填了一级域名,那么这里的证书文件中间的域名就是一级域名。
  4. client_max_body_size 10G; 配置是增大上传文件的大小限制。

重新部署

执行 docker-compose down 和  docker-compose up -d ,重新部署各个服务。部署完成后,可以查看 certbot 的日志:

2022-03-09 06:27:54,847:DEBUG:certbot._internal.main:certbot version: 1.24.0
2022-03-09 06:27:54,848:DEBUG:certbot._internal.main:Location of certbot entry point: /usr/local/bin/certbot
2022-03-09 06:27:54,848:DEBUG:certbot._internal.main:Arguments: ['--webroot', '-w', '/var/www/certbot']
2022-03-09 06:27:54,848:DEBUG:certbot._internal.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#manual,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
2022-03-09 06:27:54,863:DEBUG:certbot._internal.log:Root logging level set at 30
2022-03-09 06:27:54,865:DEBUG:certbot._internal.display.obj:Notifying user: Processing /etc/letsencrypt/renewal/xyz.com.conf
2022-03-09 06:27:54,883:DEBUG:certbot._internal.plugins.selection:Requested authenticator webroot and installer <certbot._internal.cli.cli_utils._Default object at 0x7f64a6694f10>
2022-03-09 06:27:54,883:DEBUG:certbot._internal.cli:Var authenticator=webroot (set by user).
2022-03-09 06:27:54,883:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2022-03-09 06:27:54,883:DEBUG:certbot._internal.cli:Var webroot_map={'webroot_path'} (set by user).
2022-03-09 06:27:54,883:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2022-03-09 06:27:54,907:DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): r3.o.lencr.org:80
2022-03-09 06:27:55,136:DEBUG:urllib3.connectionpool:http://r3.o.lencr.org:80 "POST / HTTP/1.1" 200 503
2022-03-09 06:27:55,137:DEBUG:certbot.ocsp:OCSP response for certificate /etc/letsencrypt/archive/xyz.com/cert1.pem is signed by the certificate's issuer.
2022-03-09 06:27:55,138:DEBUG:certbot.ocsp:OCSP certificate status for /etc/letsencrypt/archive/xyz.com/cert1.pem is: OCSPCertStatus.GOOD
2022-03-09 06:27:55,141:DEBUG:certbot._internal.display.obj:Notifying user: Certificate not yet due for renewal
2022-03-09 06:27:55,142:DEBUG:certbot._internal.plugins.selection:Requested authenticator webroot and installer None
2022-03-09 06:27:55,142:DEBUG:certbot._internal.display.obj:Notifying user:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-03-09 06:27:55,142:DEBUG:certbot._internal.display.obj:Notifying user: The following certificates are not due for renewal yet:
2022-03-09 06:27:55,142:DEBUG:certbot._internal.display.obj:Notifying user:   /etc/letsencrypt/live/xyz.com/fullchain.pem expires on 2022-06-07 (skipped)
2022-03-09 06:27:55,142:DEBUG:certbot._internal.display.obj:Notifying user: No renewals were attempted.
2022-03-09 06:27:55,143:DEBUG:certbot._internal.display.obj:Notifying user: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-03-09 06:27:55,143:DEBUG:certbot._internal.renewal:no renewal failures

没有报错说明流程就是通的,不过由于这个证书是刚申请的,离过期还很远,因此在renew的时候显示不需要更新。如果是想强制更新的话,可以在 certbot 的参数中加 --renew-by-default 选项。如果想确认证书的过期时间,可以通过 openssl 命令查看:

$ openssl x509 -in cert.pem -noout -dates
notBefore=Jan 30 03:00:32 2022 GMT
notAfter=Apr 30 03:00:31 2022 GMT

这时候访问xyz.com 即可发现 https 证书已经OK了。

新增子域名

有时候,我们可能想在 nginx 里新增个子域名,这时候就需要把整个 certbot/ 文件夹删除,然后全部重新来一遍

这里有一个坑,就是如果直接删除 certbot/ 文件夹,重新跑初始化一定会有报错。这是因为 nginx/conf.d/ 下的 *-ssl.conf 文件引用了原先在 certbot/ 下生成的证书文件。

简单的做法是在重新跑 certbot 的初始化指令前先把 nginx/conf.d/*-ssl.conf 文件挪走,等初始化完后再挪回来即可。

Nextcloud的其他配置

配置好 https 后,nextcloud有时还不认得自己的scheme已经是https了,这里最好需要修改一下配置。

./nextcloud/config/config.php 新增一行:

'overwriteprotocol' => 'https',

参考资料

Nginx & Certbot (Letsencrypt) via Docker

Certbot User Guide

Can’t use webroot authenticator needed for wildcard domain

记一次SSH下无法umount磁盘的问题


问题

最近在用 吃灰树莓派 + 外接硬盘盒 搭建 NAS ,由于硬盘默认的文件系统是 NTFS ,出于种种原因(参考这里),Linux 下的写入速度特别慢(我这里的酷鱼1T写入只有33MB/s)。于是我打算把硬盘格式化成 ext4

但是在测完速后准备 umount 当前磁盘(/media/pi/Seagate)时,却发生了报错:

$ dd bs=1M count=256 if=/dev/zero of=/media/pi/Seagate/test conv=fdatasync
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 8.04687 s, 33.4 MB/s

$ sudo umount /media/pi/Seagate
umount: /media/pi/Seagate: target is busy.

非常奇怪,我当前的 pwd 并不在挂载盘下,按理说应该没有进程会访问这个文件夹才对,可是他依然报了设备被占用。之前出现过这个问题时尝试过重新插入磁盘、重启机器等等操作,有时候也能莫名其妙的恢复了,但是这次正好有空,我希望能找到问题的原因。

分析

既然是进程占用了文件资源,那我就先来看下到底是谁:(注意,这里一定要加 sudo  ,否则可能会假装查不到)

$ sudo lsof /media/pi/Seagate/
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sftp-serv 1242   pi    4r   DIR    8,0     4096    5 /media/pi/Seagate
sftp-serv 1242   pi    6r   DIR    8,0     4096    5 /media/pi/Seagate

看上去是一个叫 sftp-serv 啥啥的进程,拿到进程号 1242 ,看下到底是什么:

$ ps -ef|grep 1242 | grep -v grep
pi        1242  1241  0 00:10 ?        00:00:00 /usr/lib/openssh/sftp-server

原来是 sftp-server 进程,但是我并没有手动启这个进程,systemd 下也没有这个服务,那么这个进程是谁拉起的呢?

$ pstree
systemd─┬─agetty
        ...
        ├─ssh-agent
        ├─sshd─┬─sshd───sshd───bash───pstree
        │      └─sshd───sshd───sftp-server

原来是 sshd 拉起的,毕竟我是通过 ssh 登录到机器上的,访问机器上文件的功能应当就是通过 sftp 来实现的。

那么 sftp 为啥需要加载我的这个文件么?难道他会默认拉起所有的文件?我们回头查看下:

 $ sudo lsof |grep sftp
sftp-serv  1242                              pi  cwd       DIR              179,2         4096       1407 /home/pi
sftp-serv  1242                              pi  rtd       DIR              179,2         4096          2 /
sftp-serv  1242                              pi  txt       REG              179,2       105664      13203 /usr/lib/openssh/sftp-server
sftp-serv  1242                              pi  mem       REG              179,2        51640      25196 /usr/lib/aarch64-linux-gnu/libnss_files-2.28.so
sftp-serv  1242                              pi  mem       REG              179,2      1439544      11838 /usr/lib/aarch64-linux-gnu/libc-2.28.so
sftp-serv  1242                              pi  mem       REG              179,2       136992      11834 /usr/lib/aarch64-linux-gnu/ld-2.28.so
sftp-serv  1242                              pi    0r     FIFO               0,11          0t0     300723 pipe
sftp-serv  1242                              pi    1w     FIFO               0,11          0t0     300724 pipe
sftp-serv  1242                              pi    2w     FIFO               0,11          0t0     300725 pipe
sftp-serv  1242                              pi    4r      DIR                8,0         4096          5 /media/pi/Seagate
sftp-serv  1242                              pi    5r      DIR              179,2         4096     260625 /media/pi
sftp-serv  1242                              pi    6r      DIR                8,0         4096          5 /media/pi/Seagate
sftp-serv  1242                              pi    7r      DIR              179,2         4096       1407 /home/pi

看起来 sftp 也只占用了他自己的链接库以及我当前访问的、以及曾经访问过的页面。。。

解决

既然原因找到了,解决起来就很轻松了。看似最简单的方法就是干掉占用文件的进程 sftp-server 。事实上这个方案也的确能 work ,不过 kill 进程这种事情实在不优雅,万一出锅了呢?

最简单的方法其实就是:登出 ssh,再重新登录毕竟 sftp 进程是依附于当前会话的sshd进程,只要退出这个 ssh 进程,sftp 进程也就能正常终止了

Monday, 23 March 2026

git-practical-application

 一些工作上遇到git操作的问题的优雅解决方案。

 

实际场景下使用git的一些简单总结.

只总结一些高频常用操作,不涉及一些骚操作.

Question1

公司内部有代码仓库和 github 仓库邮箱不一致。例如已经全局配置了公司内的信息

git config --global user.name "xxx"
git config --global user.email "xxx@tencent.com"

使用该配置推送到自己的 github 仓库后,那里是看不到 contribute 记录的

解决:用户.ssh 目录下新建 config 文件,配置参考如下

# tencent
   Host 变量
   HostName 变量
   PreferredAuthentications publickey
   IdentityFile ~/.ssh/tencent_id_rsa

# github
   Host github.com
   HostName github.com
   PreferredAuthentications publickey
   IdentityFile ~/.ssh/id_rsa

IdentityFile 指定生成的每个账号 ssh key 文件路径 Host git 服务器 host Hostname 自己定义。git@xxx

Question2

分支开发亢余 commit 信息处理。例如自己有分支上一个小阶段 commit 一个东西,但是在合 master 的时候这些是不被允许的,需要处理

git log // 查看commit记录

例如,如下。需要将 3 条 step x 记录合并成一条提交。我们找到需要合并的最前那条 commit 记录的前一条(此处是 9b37084eec4e045fc0cdf218a846ec2e43a2812f

git rebase -i 9b37084eec4e045fc0cdf218a846ec2e43a2812f
pick c67c886 step1
pick 0df8493 step2
pick 1ae6ab8 step 3

# Rebase 9b37084..1ae6ab8 onto 9b37084 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.

注释已经写得很清楚。vim 下修改

pick c67c886 step1
s 0df8493 step2
s 1ae6ab8 step 3

保存退出,重新输入新的 commit 纪录再保存退出,这 3 条既可以合并成一条记录。(注意,不能修改master上已有的commit记录;由于重写了分支记录,推到远程分支必须push -f覆盖远程)

Question3

如何优雅合并主干,遇到冲突如何处理。

合主干,假设之前在 feature/something 上开发

git checkout master
git pull --rebase orgin master
git merge --no-ff feature/something // 用非fast-forward进行合并,这样git网络比较清晰
这里使用pull --rebase而不是pull的原因是因为,如果直接使用pull有可能产生多余多余的Merge master into xxx这样无用的commit信息

如果遇到了冲突。(通常命令行和 vscode 都会有提示)

解决冲突后

git add .
git rebase --continue
合并主干完成

对于提 merge request 的

在 feature/something 上开发

git pull --rebase origin master 若冲突,解决后

git add .
git rebase --continue
git push -f origin feature/something # 由于进行了rebase,必须使用-f覆盖远程,只适用于当前分支是自己一个人开发的情况

提 merge request

实际情况 master 也有可能是 dev,看团队习惯

(同理,所有分支上使用过rebase的,推送到远程分支必须使用-f)

个人的最佳实践简单总结就是:分支拉取master更新永远使用rebase,合并到master使用merge --no-ff

Question4 feature 分支上开发到一半。遇到其它问题从 master 分支上切新分支处理

可以git stash push暂存 然后 git stash pop。但是个人更喜欢commit一个tmp。最后合并的时候把commit信息重写

Question5

写了一会代码,发现自己是在本地 master 上直接写并且已经 commit 了几次了,本来应该是分支的。

git reset --soft 回退到的commit Question6 错误的 merge 后需要修复,这里分两种情况:

1、master 本地刚合了 feature 分支代码,但是没有推上远程。分支上的多次 commit 时间很久了,插在 master 上很多了。然后需要撤销合并。需要还原 merge 前的 master

// git log信息如下,需要撤销test分支过来的两次合并,test合并测试1 test合并测试2。
commit a0674976b94d17465eb63e799a334dd12a5ab553 (HEAD -> master)
Merge: 1f8c9ac 69e88d3
Date:   Wed Nov 7 15:56:39 2018 +0800

    Merge branch 'test'

commit 1f8c9aceb78677c2dc8169f463382fc64ec8a514
Date:   Wed Nov 7 15:56:30 2018 +0800

    master上的

commit 69e88d3fdd64547e34e248b7b77a612cae3dbe37 (test)
Date:   Wed Nov 7 15:56:05 2018 +0800

    test合并测试2

commit 1e9761a4b233137c8640129d74894d51dcb38a78
Date:   Wed Nov 7 15:55:56 2018 +0800

    test合并测试1

可见 a0674976b94d17465eb63e799a334dd12a5ab553 是合并过来的 commit 记录,我们直接

git reset --hard 1f8c9aceb78677c2dc8169f463382fc64ec8a514 即可。即回退到合并分支的上一条状态。之前我以为这样 test 合并测试那两条还在。实际上它们也在 commit 记录上没了。这种操作只适用于本地没有上远程的时候有用。

2、master 合并分支的代码已经推到了远程,需要撤销这次的提交

已经推到远程的不能像本地那么做,因为会把别人后来的代码弄没了。需要使用 revert 命令。还是上面的例子

git revert -m 1 a0674976b94d17465eb63e799a334dd12a5ab553 这里是放弃到合并的这次提交。注意 revert 是会生成一次新的 commit 记录,而不是把历史中的问题 commit 从 log 中清除

如果冲突,解决后

git add .
git revert --continue

重新推上远程,这样不会影响别人的提交,只是把自己的问题 commit 去除掉

Question7

代码误上 master 和 6 的情况 2 类似

Question8

仓库上一个文件需要删除

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch 文件路径' \
--prune-empty --tag-name-filter cat -- --all
## 远程所有该文件记录删掉

git push origin -f --all 注意:需要master也要-f,很多风险 提交敏感文件还是小心为妙

Question9

提交了敏感文件。。就是类似于8

Question10

追加提交。把这次的提交追加到上次的 commit。适合分支使用,push 的时候使用-f

git add . git commit —amend

Question11

将一个已经添加到git中的文件ignore掉

1、先在.gitignore中添加规则

2、git rm --cache — file

revert 实用姿势

revert 反提交 //将 head^^^到 head 范围内的提交反转 git revert head^^^..head (3 次) Todo 查看 revert 一些回滚某几个分支的做法 Or git rebase -I xxx

撤销某一个文件的修改,还没有 add 的 git checkout [file] 撤销某一个文件的 add git reset HEAD [file]

Cherry-pick 将 feature 分支的某个提交合进 develop。sha 值是不一样的 git checkout develop`` git log feature git cherry-pick <C 的 SHA1>

from  https://github.com/flytam/git-practical-application

vercel-reverse-proxy

 

本项目是vercel反向代理。完全免费,万能代理,可代理全网一切接口,包括openai、github、google等等。http和https接口、单页面均可代理,在网络环境不好的情况下均可用。(直接浏览器打开代理页面的情况下,由于某些js和css的路径可能会不对造成访问不到,页面样式有点小问题。)

vercel现在每月有100GB的免费流量。本人又基于cloudflare搞了一个反向代理,仓库地址:cloudflare-reverse-proxy供大家参考,也作为一个备份吧。 我自己的vercel项目部署失败,疑似被ban,各位别直接一键部署了,先fork到自己的仓库改改项目名 删掉readme试试吧。实在不行 移步上面cloudflare项目,cloudflare的格局比较大.

部署

Vercel

使用方法

1 部署。部署有两种方法,一是直接点击上方按钮一键部署,二是可以先fork本项目,再登录vercel网站新建。vercel可以通过github一键注册只需要绑定手机号支持cn 新建项目

2 绑定自己的域名(不是必须,使用vercel自带的子域名也可以,但是自带的域名vercel.app在国内网络环境不好的情况下不可用) 可以申请tk免费域名或者薅小域名注册商首年免费羊毛 绑定域名 绑定域名时按照vercel上的说明配置即可,其实就是在你的域名上配了一个子域名,cname到vercel服务器

3 访问 域名/proxy/url 或者/httpproxy/url即可。 映射规则为/proxy/url映射到https接口,/httpproxy/url映射到http接口

示例

例1 访问https://替换为你自己的域名.com/proxy/api.openai.com/v1/chat/completions 实际上会替换为https://api.openai.com/v1/chat/completions demo1

如何在一些常见的开源项目中使用? 一般开源项目都是引用的openai的库,可以看到里面有一个属性是api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1")

所以使用的时候只需要设置一下openai.api_base="https://你的域名/proxy/api.openai.com/v1" 就可以了

例2 访问https://替换为你自己的域名.com/proxy/raw.githubusercontent.com/gaboolic/vercel-reverse-proxy/main/vercel.json 实际上会替换为https://raw.githubusercontent.com/gaboolic/vercel-reverse-proxy/main/vercel.json demo2 映射规则为/proxy/url映射到https接口,/httpproxy/url映射到http接口

也可用于github下载加速。假如原始链接是https://objects.githubusercontent.com/github-production-release-asset-2e65be/xxxxxx 改为https://替换为你自己的域名.com/proxy/objects.githubusercontent.com/github-production-release-asset-2e65be/xxxxxx 就可以加速了。下图是github原始链接和加速后对比。可以看到效果杠杠的,每秒50kb小水管变成了每秒2mb的大水管 github-download

例3 访问https://替换为你自己的域名.com/proxy/www.google.com/search?q=vercel-reverse-proxy 实际上会替换为https://www.google.com/search?q=vercel-reverse-proxy demo3 代理google搜索结果页面。

from  https://github.com/gaboolic/vercel-reverse-fast/tree/del

Sunday, 22 March 2026

中华民族是一个优秀民族吗?——一个令所有中国人倍感沉重的话题

 (我的回答是:某种意义上,是的。)

老狼对"中华民族是一个是优秀民族"的说法却有太多的疑问,现提出来就教于广大网友。

评判一个民族是否优秀的标准是什么? 

有网友把一场战争的胜败作为衡量民族优劣的标准,说什么:德国两次世界大战战败,不算优秀,法国在二战时一个月就亡国了,不算优秀,……这样说,只能说明他对优秀民族的概念是模糊的。其实以战争的胜败来判定民族的优劣是完全错误的。当年拿破伦横扫欧洲时,法兰西民族并不比欧洲各民族优秀一等,德意志民族两次世界大战中一败再败,也不能说日耳曼人就不优秀。东晋时期,鲜卑族一统中国北方,可你要说鲜卑族是优秀的,连他的皇帝也不敢承认,却要彻底汉化。成吉思汗铁蹄横扫欧亚大陆,忽必烈屠灭南宋时,游牧部落的蒙古族难道真的就比战败的欧亚各民族和中华民族优秀?……打个比方:一个博士生被一个市井无赖按在地上痛打一顿,你能说这个市井无赖是优秀的?

有网友说在三百年以前的几千年中华民族是优秀的民族,只是后来落伍了。这种观点也不一定恰当。一个优秀民族应具有多方面的优秀素质,而优秀素质的形成,有一个必要的条件:统治者必须给予人民一个宽松的政治环境,让这个民族的知识分子们有独立思考的权力春秋时期/民国时期就是这样的一个宽松的政治环境。中华民族几千年来有这样一个宽松的政治环境吗?读书人有独立思考的权力吗?自秦始皇焚书坑儒后,中国的知识分子们被统治者的残忍吓破了胆,从此闭上了鸟嘴,谨言慎行。汉武帝独尊儒术后,知识分子们就丧失了独立人格,其最大的追求是为皇帝们当差,去帮皇帝治国平天下。倒是中华民族的农民们有种,敢喊什么"皇帝轮流做,明年到我家。"可这样一来,中华民族又进入了历史恶性循环的怪圈……。

网友们都是学生出身,在学校评"优秀学生"要讲个"条件",要满足什么"德、智、体、美、劳"的全面发展。开放的世界流行"选美",世界选美委员会给未来的世界小姐制定了什么三围、模样、气质、言谈举止、教育程度、服装搭配……等多个标准。同理,一个民族要想成为一个"优秀民族"也应具备一些基本的素质。老狼闲来无事,在家中闭门造车,单方面给"优秀民族"制定了二十多条"标准"。这二十多条标准有无"可操作性",请网友们自鉴。

一、优秀的民族不应有压制不同观点的权力构架。中国自秦始皇以来就是这样一个权力构架。在这个权力构架下,子民们就老老实实地吃饭睡觉,不得胡思乱想,更不能发杂音。在这样的体制下,一个智商高的人活着就是一种痛苦,因为他只有四条路可走:要么考状元去给皇帝当个差,为皇帝老儿镇压有不同思想的人。但皇帝招奴才的名额很少,只有极少数人有当皇帝奴才的命;要么一天无所事事地提一壶酒,对着风花雪月"啊!啊!"地一咏三叹,然后大醉一番;要么就"采菊东篱下,悠然见南山",作个世外高人;要么就在哪个权贵的屋檐下当个师爷之类的东西,去为五斗米折腰。那些想给皇帝当差又考不上状元的人,只有一辈子皓首穷经,寻章摘句,注释子集,盼望来世去给皇帝做个奴才。

二、优秀的民族在自然科学和社会科学领域有创新的精神。中华民族自秦始皇焚书坑儒,和汉武帝"独尊儒术"后,没有任何创新精神。自然科学领域在"四大发明"以后,没有下文。社会科学领域在战国以后,只有儒家。"孔家店"被皇家封为"千年老店"后,再也没有一个人敢开"新店",皇帝更不准谁去开个"新店"。这孔丘"独家垄断"中华民族思想领域几千年,不成"圣人"那才是怪事一桩。

三、优秀的民族不自高自大,"好汉不提当年勇。"中华民族在世人面前开口闭口"我的祖先如何如何,"引为自豪,从没想过面对今天的世界自己该做些什么,该怎么做。其实中华民族的祖先并不怎样,没有多少可值得骄傲的东西。几千年历史能给后世子孙挣点脸面的只有一些被后人冠之为"文学艺术"的东西,如呤风弄月的才情、怀才不遇的感叹、闺情怨妇的惆怅、市井生活的百态等等。再就是祖坟山里挖出的一些坛坛罐罐。其它就没有什么了。那几千年历史上留下的几百次的战争、流血、杀戮、自相残杀、无数冤狱和无数次文明被毁灭的记录,不知后世子孙们是否也感到脸上有光?抬出祖宗来炫耀,只能说明后世儿孙们自卑感太强,觉得在世人面前抬不起头来,万般无奈,出此下策,给自己长脸。 

四、优秀的民族有一个"人人平等"的社会氛围,他的子民们没有高低贵贱之分。中国社会的有形等级和无形等级;成文的等级和不成文的等级之森严,在这世界上不算"老大"也算"老二"。从古至今的中国是尊卑有序:官场上小官在大官面前只能低头唯唯诺诺,不得抬头正眼看人。子民们在当官面前只能下跪,不能站着。这次万州事件就是官员们不把百姓当人而引发的。就是普通的子民们也分几等:北京人等级最高,这个城市的人在说"咱是北京人"几个字时,那洋洋得意的神情,足令人恶心十年。其次是上海人、各大省会城市人、中等城市人、县城人、镇上人,最底层就是农民们。农民们出个门,他的"身份证"都没用,得花钱买那个劳什子的"暂住证"。小小老百姓想提高自己的身价的话,只有往大城市"钻"。这样一来,公仆们又发现了一个圈钱的"新大陆":"户口变钱卖。"哇噻!实实在在的中华民族一大"怪"!

五、优秀的民族发现自己走错路时会及时回头。可中华民族是明知道错了,决不认错,要一错到底。一条道走到黑,决不回头。五八年的大跃进,五九年就知道错了,但决不改正,要再错三年再说。有时也想改正,可又顾及脸面,总是羞羞答答,象个娘们儿,生怕被人耻笑。一边扭扭捏捏地慢慢改,一边嘴硬:"我没有错,我是在调整!"结果是"调整"几十年,也调整不过来。

六、优秀的民族对其他民族的长处不排斥,而是虚心学习。中华民族是总觉自己什么都比别人好,对别的民族的长处是不屑一顾,从来不学,学别人的东西就是丢人现眼。有时也想学,但得发个声明:别臭美啦!我不是在学你们,我是在"摸屁股走路",自创"样板",到时你们得来学我!有了这个声明后,在学别人长处的同时,总要把自己的私货往里塞,结果搞了个不中不西的、不土不洋的、不伦不类的、不知道是个什么的东东出来!问题比没"摸屁股"前还要严重得多。

七、优秀的民族顺应历史潮流,决不逆历史潮流而动。中华民族面对世界浩浩荡荡的民主进步潮流视而不见,依然故我……,不敢细说,犯忌了。

八、优秀的民族不护短,能正视自己的不足。中华民族却报喜不报忧,饿死几千万时,还是形势一片大好。有了什么问题,怎一个"捂"字了得,报纸、电视从来没说过民族有什么危机、国家有什么问题,只是一个"好"。前天中央台新闻播报了一个好消息:农民们今年的人均收入预计可达二千二百多元,比去年增长百分之四。这就是中国现阶段的"小康"?这就是宋祖英女士唱的"好日子"?这就是人均GDP一千美元时的生活水准?

九、优秀的民族有开拓进取精神。皇帝们大多数只想守好自己的一亩三分地,少有想过要扩大疆域。汉武帝的主动出击是在被多次骚扰的情况下不得已而为之,仗打胜了后,树个碑:"汉武帝某年某月到此一游。"又撤回原来防地。郑和下西洋只想扬大明国威,没想过要去发现新大陆开辟新领地。倒是中华民族中的少数民族的开拓进取精神令汉族汗颜。他们数次地把自己的牧场扩大到整个汉族版图。不过他们入主中原后,一但汉化,又与汉族一样没有了开拓进取精神,变成了一个只想守"一亩三分地"的地主老财。等到别人打上门时,再把土地、儿女拱手相送。

十、优秀民族具有理性。从古至今中华民族讲的不是一个"公理",而是"人情世故"。一讲人情世故,这就复杂了。本该公事公办的,成了公事"情"办,公事"钱"办。现状是:学校老师教学生是正理,不给老师送礼,老师就不会关照你;医院病员看病天经地义,可做手术不送红包就别想医生会理睬你;国家建设工程招标是公理,可你不送钱给领导,这工程就不会给你;……这样一来,腐败的滋生就是理所当然的事。一个讲人情世故不讲理性的民族,与"优秀"二字相去十万八千里。

十一、优秀民族是一个开放的民族。除了张骞出使西域外,没有那个朝代的皇帝想派人主动出去了解世界。倒是其他国家多次"遣使来朝",对中华民族的了解更多些。就是东邻的小日本从古到今还多次派人到中华,可中华何曾派过人到日本去刺探过一点"情报"?鉴真和尚东渡还是个"自费出国",而非"公派"。自我的封闭,造成了对外界的一无所知。我们引为自豪的康、乾盛世只是人口和农业的"盛世",在其他方面大清朝与当时欧洲列强的差距是很大了,但我们不知道,仍以天朝大国自居,英国使者来了,还要别人对自己行跪拜大礼。结果几千英国鬼子远渡重洋,把四亿人的大清朝打了个屁滚尿流,这才知道原来自己是个"劣等"的。现在的中国,上层对世界算是有一个大概的了解。但世界的真相有些还得对老百姓保守秘密、慎之又慎,总是藏着掖着的。这样又产生了愤青一族:总以为中华民族不得了的了不得,老子天下第一,可以见谁灭谁。可悲!

十二、优秀的民族做事都雷厉风行。中华民族一件事几十年、几百年、甚至几千年都干不完,干不好。中国的改革已二十多年了,但还没改好,要"深化改革"。同一件事在东欧和前苏联,就那几年,一下改完,现在不讲深化改革,而是讲的发展。中国的政治体制改革从光绪皇帝开始,一百多年了,还在起点上。更令人叫绝的是土地改革。老狼在史书上见两千多年前的王莽皇帝就在搞土改;后来的历朝历代新皇帝也在搞土改;孙中山先生闹革命也喊"耕者有其田";红色革命时也在"打土豪、分田地";可直到现在,这土地还不是农民的……。真让人哭笑不得。不过,这样也好,免得公务员们没事干了会失业。

十三、优秀的民族不会在历史灾难的怪圈中反复循环。可我们的中华民族两千多年来,一直在"束缚人民的思想、社会制度难以变革,贪官污吏层出不穷,农民造反绵绵不断,经济、文化惨遭破坏,再建立新的王朝。再束缚人们的思想……"的历史怪圈中无始无终地、没有尽头地循环。

十四、优秀的民族闻过则喜。中华民族闻过则骂:"诽谤、诬蔑、丑画伟大的中华民族、""卖国贼、""反动派。"如果有哪个外国人赞扬了中国人一句,马上就乐得屁颠屁颠。其实赞扬中华民族的外国人有些是因为做客中国,不说几句好话,脸面上过不去。有的是不怀好意,例如当年中国从日本新日制铁引进宝钢全套设备,参与谈判的日方代表对中国大大赞扬一翻,中方代表高兴得忘乎所以,日本利用当时的中国人不懂业务,狠狠的啃了中国一口,等到中国人反应过来后,悔之晚也。后来只有毁约,又赔了几十亿美元才了结。去年拳王经纪人唐金到中国,一下飞机就举个小旗旗儿喊:"爱拉乌解纳。"搞得中国记者们为他发疯。后来才晓得他是在"爱拉乌解纳银行保险柜里的美元。"

十五、优秀民族一个错误只犯一次,最多两次。中华民族同一个错误一犯再犯。老皇帝犯了一个错误后,小皇帝接着犯,因为没有人敢对小皇帝说老皇帝是错的。他的孙皇帝再去"重复昨天的故事";重孙皇帝又"涛声依旧",……就象挖山不止的愚公,子子孙孙没有穷尽。

十六、优秀民族脚踏实地,不幕虚荣,不为了脸面什么都不顾。中华民族把一个脸面看得比什么都重。广大农村儿童没钱读书没关系,得每年花几十个亿去挣几个劳什子金牌回来长脸……。

十七、优秀民族会把世界和本国的真相告诉他的人民。可中华民族的子民们对国家的事、世界的事知道多少?别说国家大事了,老狼在国营企业上班,除了知道每天上班下班外,什么都不知道。天知道那一天有人突然宣布:这个公司破产了。老狼也只有去喝西北风。

十八、优秀民族有优胜劣汰的竞争机制。中华民族有一个劣胜优汰的机制,……不敢细说,犯忌。

十九、优秀民族的教育目标是培养高素质的人才,提高全民族的文化水平。中华民族的教育目标一是培养奴才,让中国人从娃娃起就听话,也不管所听的话是对是错!二是把孩子当成一个人质,以便从家长身上敲诈出更多的钱财!

二十、优秀民族的医疗体系是以治病救人、保障全体国民身体健康为第一要务。中华民族的医疗体系是以保证官员(特别是大的官员)们的身体健康为第一要务。至于小民们如果要看病,先得要让他倾家荡产再说。不把小民们的钱财掏空,就不给他看病。如果遇见没钱的主,哪怕是死在医院的大门口,也不准他进医院的大门。

二十一、优秀民族各级大小官员是由公民们一人一票选举出来。所选出的官员都是为人民办事。因为大官认为中国人是属于低素质的人,不适合一人一票的选举,所以大小官员是由上级任命。这样一来,谁想做官,只要给手中有乌纱帽的官员们送钱就行。买来的官办事时都只想讨上级官员的好。决不会为广大的人民群众办事。

二十二、优秀的民族有言论、集会、结社、出版的自由。因为统治者认为中华民族素质太低,不配拥有这些自由!

……别说啦,越说心里越气!

中华民族因其历史的悠久而伟大,但"伟大"不等于"优秀"。中国人民是勤劳、勇敢、纯朴、善良的,但这些美德又成了封建专制在中国一统几千年的理由。中华民族对人为制造的苦难的忍耐程度是世界第一的,可忍受的结果却是中华民族前进的步伐出奇的缓慢……。

努力吧!在世界各民族面前"挣点表现"吧!也许若干年后,联合国选美委员会就把"优秀民族"的桂冠戴在中华民族的头上。

注:本文2004年发表在网易论坛上引起轰动。几乎国内所有网站都转载。老狼在百度上搜索,发现台湾、德国、加拿大……等几乎所有海外中文网站都转载.

一个维系伊朗政权生存了47年的全球性的庞大地缘政治体系


伊朗伊斯兰共和国之所以能够在革命之后经历制裁、战争、外交孤立和经济危机,却仍然顽强存活近半个世纪,并不是因为它自身多么强大,而是因为在其背后存在着一张横跨多个大洲的利益网络

在这张网络中,各种力量——国家、金融机构、能源贸易集团、代理武装甚至意识形态联盟——在不同层面上为德黑兰政权提供支持

有些是公开的战略合作,有些是灰色地带的经济交易,还有一些则是更隐蔽的政治与意识形态保护伞。

真正让这个政权能够生存47年的,是背后的整套生态系统。

第一层:军事与战略联盟网络

首先,是最直接的军事合作体系。

长期以来,伊朗与普京领导下的俄罗斯建立了深度军事合作关系。双方在武器研发、情报共享和联合演习等领域形成了长期协作。这种关系并非单向依赖,而是一种互利结构:

  • 伊朗获得先进军事技术和战略支持
  • 莫斯科则获得无人机和部分战场技术
  • 同时利用伊朗在中东牵制西方力量

这种战略协同使伊朗政权成为更大地缘棋局中的一枚关键棋子。

第二层:影子能源贸易体系

如果说军事合作提供的是安全保障,那么能源贸易网络(主要来自中国)则为伊朗政权提供了维持生存的经济氧气

在长期制裁之下,伊朗石油并没有真正消失,而是通过一整套复杂的影子贸易体系进入全球市场。这一体系包括:

  • 老旧油轮组成的“影子船队”
  • 关闭AIS定位信号
  • 公海船对船转运

这些操作使大量折扣的伊朗原油得以进入市场。结果的局面:

全球能源市场继续获得石油供应,而德黑兰政权则获得维持统治的资金

第三层:代理人战争体系

在中东地区,伊朗最成功的战略工具,是代理人战争网络。通过这一体系,德黑兰能够在不直接参战的情况下,对整个地区投射影响力。

主要代理力量包括:

  • 黎巴嫩真主党:是最成熟、战斗力最强的代理武装,长期获得训练、资金和火箭武器。
  • 哈马斯:在加沙地区获得火箭技术、隧道工程和战术支持。
  • 伊拉克什叶派民兵:在军事与政治层面同时扩张影响力。
  • 胡塞武装:控制也门大片地区,并通过无人机和导弹威胁红海航运。

这一体系被称为“抵抗轴心”。通过这一结构,伊朗可以在多个战线制造压力,同时保持战略纵深。

第四层:最隐蔽的力量——西方极左意识形态网络

然而,在整个体系中,最令人震惊、也最具破坏性的力量,却来自西方内部。

几十年来,一部分极左意识形态力量——包括政府势力、学术精英、媒体和非政府组织——在政治和外交层面为伊朗政权提供了间接保护。他们以和平、多边主义、反战等口号为名,不断削弱对伊朗的压力。(这是好事)

最典型的例子,就是联合全面行动计划。这一协议在理论上旨在限制伊朗核计划,但在现实中却为伊朗解冻了数百亿美元资产,给伊朗带来了巨大的贸易收益。

这些资金随后被伊朗政权用于:

  • 扩展导弹计划
  • 资助地区代理武装
  • 加强安全机构

与此同时,在全球金融领域,一些政治力量持续推动伊朗重新接入SWIFT国际银行系统,并反对更严格的制裁机制。结果是一个极具讽刺意味的现实:

西方经济体系中的资金,最终转化为攻击西方利益的武器。

一个持续47年的生态系统

当把这些因素拼接在一起时,一个清晰的结构浮现出来:

  • 莫斯科提供军事协同
  • 影子能源网络提供资金
  • 中东代理武装扩大战略纵深
  • 西方极左意识形态削弱制裁压力

正是这四个层面共同构成了维持伊朗政权47年的生态系统。

因此,当川普对德黑兰采取强硬行动时,他真正触动的并不是一个国家,而是一整套利益交织的国际体系。

这也解释了为什么每当对伊朗采取强硬措施时,总会出现来自多个方向的强烈阻力。

因为太多政治势力在这个体系中获利。

搭建基于github issue的静态博客程序itb

 首先访问https://github.com/liangdongye999/issues-to-blog ,点击'use this template'-

'create a new repository'. 我创建的新仓库是 https://github.com/ymbrite/itb/,访问

 https://github.com/ymbrite/itb/settings,勾选issues. 

访问 https://github.com/ymbrite/itb/settings/pages。在

Build and deployment


文章不会自动生成描述,你可以在写文章时,插入:

<!--more-->来手动截断文章,实现描述。