Loading

【WebWiki】17 · DevOps 与部署

09 DevOps 与部署

后端工程师不止要会写代码,还要能让代码稳定地「跑起来、跑得好、出问题能救」。本章覆盖日常最常用的 Linux 命令、容器化(Docker)、编排(Kubernetes)、反向代理(Nginx)与持续交付(CI/CD)全流程。

1. Linux 常用命令(后端生存包)

服务器上 90% 的问题都能用下面这几组命令定位。建议背熟。

进程与端口

# 查看所有进程(常用组合:按 CPU/内存排序)
ps aux | sort -k3 -rn | head        # 按 CPU 占用前 10
ps aux --sort=-%mem | head          # 按内存占用前 10

# 实时看进程资源(q 退出)
top
htop                                # 更友好(需安装)

# 根据端口反查进程(排查“端口被占用”)
netstat -tunlp | grep 8080          # 老机器常用
ss -tunlp | grep 8080              # 新版推荐,更快
lsof -i:8080                        # 查看占用 8080 的进程

# 根据进程名查
ps -ef | grep java

# 杀进程(温柔 → 强制)
kill 12345                          # SIGTERM,给进程清理机会
kill -9 12345                       # SIGKILL,强制(慎用,可能丢数据)
pkill -f 'java -jar app'           # 按名字批量杀

# 后台运行 + 日志重定向 + 脱离终端(生产启动 jar 常用)
nohup java -jar app.jar > app.log 2>&1 &

踩坑:netstat 在新版最小化系统可能没装,用 ss 替代;lsof 也常需单独安装(yum install lsof / apt install lsof)。

文件与日志

# 目录与文件
ls -lh                              # 人类可读大小
ll -ah                             # 含隐藏文件
du -sh /var/log/*                   # 各目录占用
df -h                              # 磁盘分区使用率(排查“磁盘满”)

# 查找
find /opt -name "*.log"            # 按名找
find /opt -size +100M               # 大于 100M 的文件
find /opt -mtime -1                 # 1 天内修改的
locate nginx.conf                   # 基于索引秒查(先 updatedb)

# 文本搜索(日志排错核心)
grep -n "ERROR" app.log             # 带行号
grep -rn "OutOfMemory" /opt/logs    # 递归目录
grep -C 5 "Exception" app.log       # 上下各 5 行上下文
grep -E "ERROR|WARN" app.log        # 正则多关键字
zgrep "timeout" app.log.1.gz        # 搜压缩日志

# 实时看日志尾部(线上排查首选)
tail -f app.log                     # 持续刷新
tail -1000f app.log                 # 先看最后 1000 行再跟随

# 查看/分页
cat file | less                    # 分页看大文件
head -20 file

# 编辑
vim app.yml                         # 常用:i 进入编辑,Esc 后 :wq 保存,:q! 强退

# 压缩与传输
tar -zcvf app.tar.gz /opt/app      # 打包
tar -zxvf app.tar.gz               # 解包
scp app.jar root@10.0.0.2:/opt/    # 本机→远程
rsync -avz /opt/app/ root@2:/opt/app/   # 增量同步(断点续传友好)

性能三连

# 1) CPU / 负载
uptime                            # 1/5/15 分钟平均负载(≈核数说明饱和)
top -H -p <pid>                   # 看某进程的线程级 CPU

# 2) 内存
free -h                           # 看 available,不要只看 free(cache 可回收)
cat /proc/meminfo

# 3) 磁盘 IO
iostat -x 1                       # 看 %util、await(磁盘是否瓶颈)
vmstat 1                          # 综合 CPU/内存/IO/上下文切换
df -h                             # 磁盘剩余

# 网络
ping host
telnet host 3306                  # 验证端口连通(无 telnet 用 nc)
nc -zv host 3306
iftop                             # 实时流量(需装)

排错思路:接口超时先 uptime/free/df 三连排除资源瓶颈;再 ss -tunlp 看端口监听;再 tail -f 看应用日志;最后 iostat 看是不是磁盘 IO 拖垮。

1.1、Shell 脚本实战(运维自动化)

把重复操作写成脚本,避免手抖。一个「自动备份 + 清理旧备份」的示例:

#!/bin/bash
# backup.sh —— 每日凌晨备份 MySQL 并保留 7 天
set -e                            # 任何命令失败立即退出
BACKUP_DIR=/data/backup
DATE=$(date +%Y%m%d_%H%M%S)
DB_USER=root
DB_PASS='YourPass'

mkdir -p "$BACKUP_DIR"
echo "[$(date)] 开始备份..."
mysqldump -u"$DB_USER" -p"$DB_PASS" --single-transaction --all-databases \
  | gzip > "$BACKUP_DIR/all_$DATE.sql.gz"

echo "[$(date)] 备份完成: $BACKUP_DIR/all_$DATE.sql.gz"

# 删除 7 天前的备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
echo "[$(date)] 清理 7 天前备份完成"
chmod +x backup.sh
# 加入定时任务:每天 2:30 执行
crontab -e
# 30 2 * * * /opt/scripts/backup.sh >> /data/backup/backup.log 2>&1

常用 crontab 格式:分 时 日 月 周 命令。如 0 */2 * * * 每两小时;*/5 * * * * 每 5 分钟。

另一个「检测服务存活,挂了自动拉起」的守护脚本:

#!/bin/bash
# watchdog.sh —— 检测 8080 端口,挂了就重启
PORT=8080
if ! ss -tunlp | grep -q ":$PORT"; then
    echo "[$(date)] 服务 $PORT 未监听,尝试重启"
    systemctl restart myapp
    # 也可发告警:curl -X POST https://hook/alert -d "服务宕机"
fi

2. Docker 核心用法

Docker 把应用及其依赖打包成镜像(Image),运行起来叫容器(Container),实现「一次构建,处处运行」。

2.1、核心概念

  • 镜像 Image:只读模板(类似安装盘),由一层层只读层组成;
  • 容器 Container:镜像的运行实例,可读写层在镜像之上;
  • 仓库 Registry:存镜像的地方(Docker Hub、阿里云 ACR、Harbor);
  • Volume:数据卷,把容器里数据持久化到宿主机,避免容器删了数据没;
  • Network:容器间通信网络(bridge/host/none)。

2.2、日常命令

# 镜像
docker images                      # 列出本地镜像
docker pull mysql:8.0              # 拉取
docker build -t myapp:1.0 .       # 当前目录 Dockerfile 构建
docker rmi myapp:1.0              # 删镜像
docker tag myapp:1.0 registry.com/myapp:1.0   # 打标签(推仓库用)

# 容器
docker run -d -p 8080:8080 --name myapp myapp:1.0   # 后台启动并映射端口
docker ps                          # 运行中的容器
docker ps -a                       # 含已退出的
docker logs -f myapp              # 看日志
docker exec -it myapp bash        # 进容器终端排错
docker stop myapp && docker rm myapp
docker rm -f $(docker ps -aq)     # 清掉所有容器(慎用)

# 资源与清理
docker stats                      # 实时看容器 CPU/内存
docker system df                  # 镜像/容器/卷占用
docker system prune -a            # 清掉无用镜像、容器(释放空间)

2.3、数据卷与网络

# 挂载数据卷(MySQL 数据持久化)
docker run -d -p 3306:3306 \
  -v /data/mysql:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  --name mysql mysql:8.0

# 自定义网络,让容器用名字互访
docker network create mynet
docker run -d --network mynet --name redis redis:7
docker run -d --network mynet --name app myapp:1.0   # app 内可直接 ping redis

docker-compose.yml —— 本地开发环境标准配置

一条 docker-compose up 拉起整套依赖(MySQL + Redis + 应用),免去找各安装包。

version: "3.8"
services:
  mysql:
    image: mysql:8.0
    container_name: dev-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: demo
    ports:
      - "3306:3306"
    volumes:
      - ./mysql-data:/var/lib/mysql      # 数据持久化到本地
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

  redis:
    image: redis:7
    container_name: dev-redis
    ports:
      - "6379:6379"
    volumes:
      - ./redis-data:/data

  app:
    build: ./app                         # 用 ./app/Dockerfile 构建
    container_name: dev-app
    depends_on:
      - mysql
      - redis
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo
      SPRING_REDIS_HOST: redis
    networks:
      - default

# 启动:docker-compose up -d   停止:docker-compose down   看日志:docker-compose logs -f app

depends_on 只保证启动顺序,不保证 MySQL 已「就绪可连」。应用需自身带重试连接;或用 healthcheck + depends_on: condition: service_healthy 做就绪等待。

3. Java 应用镜像化

Dockerfile —— 多阶段构建,产物小、层次缓存友好

# ---- 阶段一:构建 ----
FROM maven:3.8-openjdk-17 AS build
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline          # 先下依赖,利用层缓存(改代码不重下)
COPY src ./src
RUN mvn package -DskipTests -o

# ---- 阶段二:运行(只拿 jar,镜像不含 maven/jdk 编译环境)----
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /build/target/*.jar app.jar
# 用非 root 用户跑,安全
RUN useradd -m appuser && chown -R appuser /app
USER appuser
EXPOSE 8080
# 加上时区、JVM 调优
ENV TZ=Asia/Shanghai
ENTRYPOINT ["java", "-Xms256m", "-Xmx512m", "-Duser.timezone=Asia/Shanghai", "-jar", "app.jar"]

多阶段构建好处:最终镜像只含 JRE + jar,体积从 1G+ 降到 200M 左右,且不含源码与构建工具,更安全。
配合 .dockerignore 跳过无关文件,加速构建、减小上下文:

# .dockerignore
target
.git
*.md
Dockerfile

构建与运行:

docker build -t myapp:1.0 .
docker run -d -p 8080:8080 --name myapp myapp:1.0

镜像优化小贴士:基础镜像用 slim/alpine;COPY 顺序把不常变的放前面以复用缓存层;尽量 RUN 合并、清理 apt 缓存;生产可考虑 distroless 或 jlink 定制极小运行时。

4. Kubernetes 核心对象

当容器多了、要扩缩容、要自愈,就用 Kubernetes(K8s)做编排。它把多个 Docker 主机组成一个集群统一调度。

4.1、核心对象

对象 作用
Pod 最小调度单位,包含一个或多个紧密协作的容器(共享网络/存储)
Deployment 管理 Pod 副本数与滚动更新(声明式:要 3 个副本,K8s 维持)
Service 给一组 Pod 提供稳定访问入口与负载均衡(ClusterIP/NodePort/LoadBalancer)
Ingress 七层路由,把外部域名/路径转发到 Service(替代手写 Nginx)
ConfigMap 配置(非敏感),挂载为文件或环境变量
Secret 敏感配置(密码/token),base64 存储
Namespace 资源逻辑隔离(dev/test/prod)
PersistentVolume 持久化存储

4.2、示例:部署一个 Java 应用

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: prod
spec:
  replicas: 3                      # 3 个 Pod 副本
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: registry.com/myapp:1.0
          ports:
            - containerPort: 8080
          resources:
            requests: { cpu: "100m", memory: "256Mi" }
            limits:   { cpu: "500m", memory: "512Mi" }
          readinessProbe:          # 就绪探针:能接流量才进 Service
            httpGet: { path: /health, port: 8080 }
            initialDelaySeconds: 10
          livenessProbe:           # 存活探针:失败则重启 Pod
            httpGet: { path: /health, port: 8080 }
            periodSeconds: 15
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc
  namespace: prod
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP
kubectl apply -f deployment.yaml -f service.yaml
kubectl get pods -n prod
kubectl logs -f deploy/myapp -n prod
kubectl scale deploy/myapp --replicas=5 -n prod     # 扩缩容
kubectl set image deploy/myapp myapp=registry.com/myapp:2.0 -n prod  # 滚动更新
kubectl rollout undo deploy/myapp -n prod           # 回滚

探针很重要:没有 readinessProbe,Pod 一启动就接流量,可能还没初始化完就报错;没有 livenessProbe,假死(线程卡死但进程在)不会被重启。

5. Nginx 常用配置

Nginx 是高性能 Web 服务器 / 反向代理 / 负载均衡器。后端最常用它做「反向代理 + 负载均衡 + 静态资源 + HTTPS」。

5.1、反向代理(把请求转发给后端)

server {
    listen 80;
    server_name www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;     # 转发到本地 Java 应用
        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_set_header X-Forwarded-Proto $scheme;
    }
}

5.2、负载均衡(upstream 多节点)

upstream backend {
    # 默认轮询;weight 加权;ip_hash 按 IP 粘滞(会话保持)
    server 10.0.0.1:8080 weight=2;
    server 10.0.0.2:8080 weight=1;
    server 10.0.0.3:8080 backup;        # 备份节点,主全挂才上
    # server 10.0.0.4:8080 down;        # 临时摘流
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

负载均衡策略:round-robin(轮询,默认)、weight(加权)、ip_hash(同 IP 落同节点,解决无状态 Session 问题)、least_conn(转给连接最少的)、hash $request_uri(按 URL 一致性哈希,配合缓存命中)。

5.3、HTTPS / SSL 配置

server {
    listen 443 ssl;
    server_name www.example.com;
    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://backend;
    }
}
# HTTP 强制跳 HTTPS
server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

5.4、静态资源、缓存与 gzip

server {
    listen 80;
    location /static/ {
        root /data/www;                 # 实际路径 /data/www/static/...
        expires 30d;                    # 浏览器缓存 30 天
        add_header Cache-Control "public, immutable";
    }
    location / {
        proxy_pass http://backend;
    }
    # 开启 gzip,省流量
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
    gzip_min_length 1k;
}

5.5、限流与 location 匹配

# 限流:每 IP 每秒 10 次
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
    location /api/ {
        limit_req zone=one burst=20 nodelay;   # 突发 20 允许,其余 503
        proxy_pass http://backend;
    }
}

location 匹配优先级(高频考点):= 精确 > ^~ 前缀 > ~/~* 正则 > 普通前缀。例如 location = / 只匹配根,location /api 前缀匹配,location ~ \.php$ 正则。

改完配置:nginx -t 校验语法 → nginx -s reload 热加载(不中断连接)。

6. CI/CD 流水线

CI(持续集成):频繁合代码 + 自动构建测试;CD(持续交付/部署):自动打包、部署到各环境。目标是「小步快跑、出问题快回滚」。

CI/CD 流水线

6.1、Jenkins 流水线(Pipeline as Code)

Jenkinsfile 把流水线写成代码,随仓库走:

pipeline {
    agent any
    environment {
        REGISTRY = 'registry.com'
    }
    stages {
        stage('拉代码') {
            steps { git 'https://github.com/team/app.git' }
        }
        stage('构建测试') {
            steps {
                sh 'mvn clean package -DskipTests=false'
            }
        }
        stage('单元测试') {
            steps { sh 'mvn test' }
        }
        stage('构建镜像') {
            steps {
                sh "docker build -t $REGISTRY/myapp:${BUILD_NUMBER} ."
                sh "docker push $REGISTRY/myapp:${BUILD_NUMBER}"
            }
        }
        stage('部署到测试环境') {
            steps {
                sh "kubectl set image deploy/myapp myapp=$REGISTRY/myapp:${BUILD_NUMBER} -n test"
            }
        }
        stage('人工确认') {
            steps { input message: '确认发布到生产?' }   // 卡点,需人点
        }
        stage('部署生产') {
            steps {
                sh "kubectl set image deploy/myapp myapp=$REGISTRY/myapp:${BUILD_NUMBER} -n prod"
            }
        }
    }
    post {
        failure { echo '流水线失败,发钉钉/邮件告警' }
        success { echo '发布成功' }
    }
}

Jenkins 关键概念:Master/ Agent(构建节点)、Stage/Step、凭据管理(Credentials 存密码/Token,不要在 Jenkinsfile 明文写)、Webhook(Git 推送触发构建)。

6.2、GitHub Actions 示例(更轻量)

# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Build & Test
        run: mvn clean verify
      - name: Login & Push Image
        run: |
          docker build -t registry.com/myapp:${{ github.sha }} .
          docker push registry.com/myapp:${{ github.sha }}

6.3、完整 CI/CD 流程示例

开发者 push 代码
   → Git 仓库 Webhook 触发 Jenkins
   → 阶段1 拉代码 + Maven 编译 + 单测(失败即红,阻断)
   → 阶段2 打 Docker 镜像 + 推镜像仓库(标签用 commit id / 构建号)
   → 阶段3 部署到 test 命名空间(K8s)
   → 自动化接口测试 / 人工验收
   → 阶段4 人工卡点确认
   → 阶段5 滚动更新到 prod(kubectl set image,零停机)
   → 阶段6 健康检查 + 监控观察;异常一键回滚(kubectl rollout undo)

7. 监控告警体系

「能跑」还不够,要「看得清、出了事能马上知道」。

  • 指标监控:Prometheus 定时拉取各组件 /metrics 指标,Grafana 画图看板(CPU、QPS、RT、JVM、容器资源)。
  • 日志:ELK(Elasticsearch + Logstash + Kibana)或 Loki 集中收集,按 level=ERROR 检索、TraceId 串联链路。
  • 链路追踪:SkyWalking / Zipkin,定位跨服务慢调用。
  • 告警:Prometheus Alertmanager / 夜莺 / Grafana Alert 触发,通知到钉钉、企业微信、电话。常见阈值:CPU > 85% 持续 5min、接口错误率 > 1%、P99 > 1s、磁盘 > 90%、容器重启次数突增。
  • 健康检查:应用暴露 /health(liveness/readiness),K8s 探针 + Nginx 健康检查联动。

黄金四指标:延迟(Latency)、流量(Traffic)、错误(Errors)、饱和度(Saturation)——所有监控看板围绕这四件事设计。

8. 一个典型上线流程

把前面所有能力串起来,一次规范的互联网服务上线长这样:

  1. 开发:本地 docker-compose 起依赖,写完代码 + 单测,push 到 Git 分支并发 MR;
  2. CI:MR 合入 main 触发流水线,编译 + 单测 + 代码扫描,挂了打回;
  3. 镜像:通过后打镜像推仓库,镜像扫描(Trivy)查漏洞;
  4. 预发/测试环境:自动部署到 test 命名空间,跑接口/回归测试;
  5. 审批:测试通过后,流水线在「生产发布」阶段人工卡点,值班确认;
  6. 生产发布:kubectl set image 滚动更新(先起新 Pod,就绪探针通过后切流量,再下旧 Pod),零停机;
  7. 观察:Grafana 看 RT/错误率/资源,日志查异常;前 30 分钟重点盯盘;
  8. 回滚预案:异常时 kubectl rollout undo 秒级回退,或切 Nginx upstream 摘流;
  9. 复盘:如有故障,记录时间线、根因、改进项。

上线三原则:可回滚(任何发布都必须能快速回退)、可灰度(先小流量再全量)、可观测(发布前后指标对比)。没有这三样的发布就是「裸奔」。

9. 小结

  • Linux:ps/ss/lsof/top/free/df/tail/grep + crontab 脚本化是后端日常;
  • Docker:镜像/容器/卷/网络四件套,多阶段 Dockerfile 让 Java 镜像又小又安全;
  • K8s:Deployment/Service/Ingress/ConfigMap 编排,探针保障自愈,滚动更新零停机;
  • Nginx:反向代理 + upstream 负载均衡 + SSL + 限流 + gzip,是流量入口标准答案;
  • CI/CD:Jenkins/GitHub Actions 把「构建-测试-打包-部署-回滚」自动化,配合监控告警形成闭环;
  • 上线本质是「可回滚、可灰度、可观测」的工程纪律,工具只是手段。
posted @ 2026-09-19 05:41  Stellan7  阅读(15)  评论(0)    收藏  举报