posted in DevOps 

  • 常驻分支:mainrelease/*(每期一个)、临时 hotfix/*。没有长期 dev
  • 环境:每个 PR 一个预览环境;Staging 只跟随当前 release/*;Prod 只接“已签名的 release tag”。
  • 构建哲学:构建一次(在 release 上),Staging 和 Prod 都晋级同一份产物,绝不重建。

Sprint 第 1–8 天:开发期

  • 开发分支 → PR → 合 main
  • main 合并依赖 merge queue:单测、契约测、集成测、静态扫描、迁移脚本干跑全绿才合。
  • 每个 PR 自动起预览环境给前端/QA点验,通过就销毁。
  • 可选最小开关:全局 Kill Switch + 环境级开关两颗,够用就行。

产出:一堆通过所有检查的提交在 main,但还没承诺“本期必上”。


第 9 天:冻结日(切候选版本)

  • main 切出 release/2025.08.S12(名字随你,关键是唯一)。
  • release/*构建一次产物:镜像/包打不可变 tag(如 app:2025.08.S12-rc.1),记录 digest、SBOM,并签名
  • 部署到 Staging,开始全链路验证(E2E、回放/影子流量、性能冒烟、迁移脚本干跑或小流量实跑)。

注意:冻结后 release/* 只收 fix 类 PR。带 refactor/deps/migration/public-api-change 标签的一律被 CI 拒收。


第 10–14 天:收敛期(只收修)

  • 需要修的 bug:

    • 方案 A(推荐):在 main 修,打 backport:release/2025.08.S12 标签,机器人自动 cherry-pick -xrelease/*,重跑 Staging 验证。
    • 方案 B(紧急):直接在 release/* 修,同时 反向合回 main,保持主线干净。
  • 若发现“混了重构”的提交:在 release/*git revert -m 1 <merge-commit>,重出 rc.2,继续测。主线不动

  • DB 迁移遵循 expand/contract:

    • expand(加列/兼容写)只能在前一班或本班早期就位;
    • contract(删旧列/切流)排到下一班。冻结期不做 contract。

发版日:晋级,不重建

  • release/* 当前 commit 打签名 tagv2025.08.S12,指向已在 Staging 验证过的同一产物
  • Prod 流水线只接受这个签名 tag,直接晋级同一 digest 到生产。
  • 打产物不可变别名:app:2025.08.S12 → 指向那个 digest。记录 SBOM、变更单、审批人,审计闭环。

Hotfix:两列车互不污染

  • 从“线上最近一次生产 tag”切 hotfix/XYZ,修完:

    1. 在 hotfix 分支构建 v2025.08.S12-p1,可先在短暂 Staging 或金丝雀验证;
    2. 晋级到生产;
    3. 回填到正在维护的 release/*main
  • 不去动任何“下一班”的 release/*。候选车和现网车各开各的。


回滚:一键退回上个版本

  • 直接把生产晋级回上一个签名 tag(同一 digest)。
  • 若需要“功能层面”止血,拉下 Kill Switch。
  • DB 回滚只在使用了可逆脚本时执行;否则依赖 expand/contract 的前向兼容性先熬过去。

权限与护栏(防作死三件套)

  • 生产流水线的部署来源只允许 release/* 的签名 tag,技术上禁止从任意分支 HEAD 部署。
  • 冻结后 release/* 的 CI Gate:仅放 type=fix 且触达面与行数低于阈值的 PR,其他一律 fail。
  • main 的合并必须经 merge queue,合并后不会自动部署到 Staging;Staging 只吃 release/*

最小工具清单(够用即可)

  • 镜像/包签名与 SBOM:cosign/slsa + 你们的制品库。
  • Backport 机器人:现成的 backport-bot 或 GitHub Actions 模板。
  • 影子流量/回放:一条读路径回放管道;写请求在 Staging NOP。
  • 开关:自建 DB 表都行,至少有“全局关”和“按环境关”。

常见情景对照

  • “第 11 天线上爆 P1”:按 hotfix 流走,不碰下一班 release/*;发完补回。
  • “冻结后发现某 PR 混重构”:在 release/* 上 revert,另起干净 fix;main 不动。
  • “A/B 契约要成对上”:Gate 强制 A、B 都绿才放 release/*,没齐就下一班。
  • “审计要证据链”:产物 digest、签名 tag、Staging 与 Prod 的部署记录一致,截图即证。

就这么简单:主线做演化,release 做交付,产物做真相。别再把 prod 当工作台,别再让提交当发布单位。你要的是确定性,不是惊喜。

posted in DevOps 

0) 前提

已可用密钥登录当前服务器(ed25519)
具备 root 权限(首次执行用 root 登录)

1) 备份并加固 SSH 配置

### 备份
sudo cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%s)

### 禁用密码类登录,仅允许密钥
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?KbdInteractiveAuthentication.*/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config || true
sudo sed -i 's/^#\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config || true

### root 仅允许密钥登录(若后续要彻底禁用 root 远程登录,可改为 PermitRootLogin no)
if grep -q '^PermitRootLogin' /etc/ssh/sshd_config; then
  sudo sed -i 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
else
  echo 'PermitRootLogin prohibit-password' | sudo tee -a /etc/ssh/sshd_config
fi

### 锁定 root 密码(无密码登录)
sudo passwd -l root

### 重载 SSH 服务
sudo systemctl reload sshd || sudo service ssh reload

2) 创建最小权限部署用户

# 创建用户并加入 docker 组
sudo useradd -m -s /bin/bash deploy || true
sudo getent group docker >/dev/null || sudo groupadd docker
sudo usermod -aG docker deploy

# 安装公钥(二选一)

# A) 复用 root 的 authorized_keys
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
sudo chmod 600 /home/deploy/.ssh/authorized_keys

# B) 或者手动粘贴你的公钥
# sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
# echo 'ssh-ed25519 AAAA... your_key' | sudo tee /home/deploy/.ssh/authorized_keys >/dev/null
# sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
# sudo chmod 600 /home/deploy/.ssh/authorized_keys

# 配置最小 sudo 权限(仅允许 docker/docker-compose)
echo 'deploy ALL=(root) NOPASSWD:/usr/bin/docker,/usr/local/bin/docker-compose,/usr/bin/docker-compose' \
| sudo tee /etc/sudoers.d/deploy >/dev/null
sudo chmod 440 /etc/sudoers.d/deploy

3) 验证

# 使用密钥登录 deploy
ssh -i ~/.ssh/id_ed25519 deploy@<SERVER_IP> 'whoami; id'

# 验证最小 sudo 权限可用
ssh -i ~/.ssh/id_ed25519 deploy@<SERVER_IP> \
  'sudo docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" || true'

# 可选:验证 root 密码登录已禁用(不要在生产上做危险测试)
# ssh root@<SERVER_IP>    # 应拒绝密码,密钥仍可用

4) 可选加强项

# 若后续决定完全禁用 root 远程登录(推荐在确认 deploy 完全可用后执行)
sudo sed -i 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo systemctl reload sshd || sudo service ssh reload

5) (可选).env 本地文件权限

# 保留 /opt/echoflow/.env 便于 docker-compose 使用,确保权限最小
sudo chmod 600 /opt/echoflow/.env
sudo chown root:root /opt/echoflow/.env

# 若采用“零驻留”,部署成功后清理(下次部署/重启前需先解密再生成)
# sudo shred -u /opt/echoflow/.env || sudo rm -f /opt/echoflow/.env

已完成配置的状态应满足:

  • 服务器仅支持密钥登录,root 无法用密码登录
  • 日常部署用 deploy 用户,具备 docker/docker-compose 的 NOPASSWD 最小 sudo 权限

简要验证清单:

  • 使用 deploy 登录成功
  • sudo docker ps 正常
  • root 密码登录不可用(保留 root 密钥或彻底禁用 root 远程登录按需选择)
posted in DevOps 

mysql -u root -p
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'slow_query_log_file';
SHOW VARIABLES LIKE 'long_query_time';
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/path/filename';

typically, the slow query log file is located at /var/lib/mysql/{hostname}-slow.log.

SELECT SLEEP(X);

test whether slow query log is working

SET GLOBAL slow_query_log = 'OFF';

turn off slow query log when you are done troubleshooting

posted in DevOps 

Try these useful commands:

1. list all transactions in progress

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX\G

Output:

Normally, the output should be empty.

Kill locked process

mysql> kill {trx_mysql_thread_id}

2. list all running processes

mysql> show processlist;

Output:

Kill the locked process

mysql> kill {id}

3. check whether there are locked tables

mysql> show OPEN TABLES where In_use > 0;

Output:

Normally, the output should be empty.

Hope these helpful commands will help you recover your locked MySQL. Good luck!

posted in DevOps 

lsof -i:port

根据监听端口号找进程PID

COMMAND   PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
node    14374 root   12u  IPv6 376449654      0t0  TCP *:8957 (LISTEN)

lsof |grep deleted

如果一个文件正在被一个运行中的进程读取,此时删除次文件,此文件空间不会被释放,使用du -sh无法统计此文件占用的空间;可以通过此命令查看已经被删除过仍然占用空间的文件清单,然后通过kill进程释放文件占用空间

Determine which MySQL configuration file is being used

$ which mysqld
/usr/sbin/mysqld

$ /usr/sbin/mysqld --verbose --help | grep -A 1 "Default options"
Default options are read from the following files in the given order:
/etc/mysql/my.cnf ~/.my.cnf /usr/etc/my.cnf

test nginx configuration

nginx -t

docker command

lsns -t net|pid
ls -la /proc/{pid}/ns

posted in DevOps 

由于众所周知的原因官方minikube在中国大陆地区运行会碰到网络问题。

阿里云对官方minikube做了修改,使它默认使用国内镜像仓库。安装和启动参考文档如下:

Minikube - Kubernetes本地实验环境

但是文中的参数任然有问题,如下命令可以正常启动,操作系统Mac

minikube start --image-mirror-country cn \
    --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.5.1.iso \
    --registry-mirror=https://xxxxxx.mirror.aliyuncs.com \
    --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

其中--registry-mirror参数中的"xxxxxx"是个人阿里云镜像加速地址,查询地址:

https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors