文章目录
- [Docker & Kubernetes 面试完全指南(Go 校招向)](#Docker & Kubernetes 面试完全指南(Go 校招向))
-
- [Part 1:Docker](#Part 1:Docker)
- [一、Docker 是什么](#一、Docker 是什么)
-
- [容器 vs 虚拟机](#容器 vs 虚拟机)
- 二、核心概念
- [三、Dockerfile 最佳实践](#三、Dockerfile 最佳实践)
-
- 基础写法
- [Dockerfile 优化要点](#Dockerfile 优化要点)
- [四、ENTRYPOINT vs CMD(面试必考)](#四、ENTRYPOINT vs CMD(面试必考))
- [五、容器核心技术原理(Namespace + Cgroups)](#五、容器核心技术原理(Namespace + Cgroups))
-
- [Namespace(命名空间)------ 隔离](#Namespace(命名空间)—— 隔离)
- [Cgroups(控制组)------ 限制资源](#Cgroups(控制组)—— 限制资源)
- [六、Docker 网络模式](#六、Docker 网络模式)
- 七、数据卷(Volume)
- 八、常用命令速查
- [Part 2:Kubernetes(K8s)](#Part 2:Kubernetes(K8s))
- [八、K8s 是什么,解决什么问题](#八、K8s 是什么,解决什么问题)
- 九、核心架构
- 十、核心对象详解
-
- [Pod ------ 最小调度单元](#Pod —— 最小调度单元)
- [Deployment ------ 无状态应用管理](#Deployment —— 无状态应用管理)
- [Service ------ 服务发现与负载均衡](#Service —— 服务发现与负载均衡)
- [Ingress ------ 七层路由](#Ingress —— 七层路由)
- [ConfigMap & Secret ------ 配置管理](#ConfigMap & Secret —— 配置管理)
- [PV & PVC ------ 持久化存储](#PV & PVC —— 持久化存储)
- [十一、Pod 生命周期与健康检查](#十一、Pod 生命周期与健康检查)
-
- [Pod 状态流转](#Pod 状态流转)
- 三种健康检查
- 十二、调度机制
-
- [Pod 调度流程](#Pod 调度流程)
- 常用调度策略
- 十三、自动扩缩容(HPA)
- [十四、StatefulSet ------ 有状态应用](#十四、StatefulSet —— 有状态应用)
- [十五、DaemonSet ------ 每节点一个 Pod](#十五、DaemonSet —— 每节点一个 Pod)
- [十六、Job & CronJob ------ 一次性与定时任务](#十六、Job & CronJob —— 一次性与定时任务)
-
- [Job ------ 运行到完成](#Job —— 运行到完成)
- [CronJob ------ 定时任务](#CronJob —— 定时任务)
- [十七、RBAC ------ 权限控制(面试加分)](#十七、RBAC —— 权限控制(面试加分))
- 十八、命名空间(Namespace)与资源隔离
- [十九、常用 kubectl 命令](#十九、常用 kubectl 命令)
- [二十、Go 应用容器化最佳实践](#二十、Go 应用容器化最佳实践)
- 二十一、面试高频问题速答
- 二十二、一张图记住核心
Docker & Kubernetes 面试完全指南(Go 校招向)
Part 1:Docker
一、Docker 是什么
Docker 是一个容器化平台 ,将应用及其依赖打包成标准化的容器,实现"一次构建,到处运行"。
容器 vs 虚拟机
虚拟机:
┌──────────────────────────────────┐
│ App A │ App B │ App C │
│ 依赖A │ 依赖B │ 依赖C │
│ Guest OS│ Guest OS│ Guest OS │ ← 每个VM有完整操作系统
│ Hypervisor │
│ Host OS │
│ 硬件 │
└──────────────────────────────────┘
容器:
┌──────────────────────────────────┐
│ App A │ App B │ App C │
│ 依赖A │ 依赖B │ 依赖C │
│ Docker Engine │ ← 共享宿主机OS内核
│ Host OS │
│ 硬件 │
└──────────────────────────────────┘
| 对比项 | 虚拟机 | 容器 |
|---|---|---|
| 启动时间 | 分钟级 | 秒级(毫秒级) |
| 磁盘占用 | GB 级 | MB 级 |
| 隔离性 | 强(完整OS) | 弱(共享内核) |
| 性能损耗 | 高(硬件虚拟化) | 低(进程级) |
| 资源利用率 | 低 | 高 |
面试答法 :容器本质是宿主机上的一个进程,通过 Linux 的 Namespace(隔离视图)和 Cgroups(限制资源)实现了进程间的隔离和资源管控,并不是真正的虚拟化。
二、核心概念
三大核心对象
镜像(Image) ─── 构建 ──→ 容器(Container)
↑ │
│ │ 运行时
Dockerfile ↓
Registry(仓库)
Docker Hub / 私有仓库
- 镜像(Image) :只读的模板,包含应用代码、运行时、依赖、配置。类比:Java 的
.class文件 - 容器(Container):镜像的运行实例,有独立的文件系统、网络、进程空间。类比:Java 的运行进程
- 仓库(Registry):存储和分发镜像的服务,如 Docker Hub、阿里云 ACR
分层存储与联合文件系统(面试高频)
Docker 镜像采用分层结构,每条 Dockerfile 指令产生一个只读层:
Dockerfile:
FROM ubuntu:20.04 → Layer 1: ubuntu 基础层(只读)
RUN apt install go → Layer 2: 安装 Go(只读)
COPY . /app → Layer 3: 复制代码(只读)
RUN go build → Layer 4: 编译产物(只读)
运行容器时:
Layer 4(只读)
Layer 3(只读)
Layer 2(只读)
Layer 1(只读)
Container Layer ← 可写层(容器停止后默认丢失)
联合文件系统(OverlayFS):将多个只读层叠加,加上一个可写层,呈现为统一的文件系统视图。
核心优势:
- 多个容器共享相同的基础层,节省磁盘空间
- 拉取镜像时,已有的层不重复下载,加速部署
- 修改只读层中的文件时,采用 Copy-on-Write:先复制到可写层再修改,原只读层不变
三、Dockerfile 最佳实践
基础写法
dockerfile
# 多阶段构建(面试加分项)
# 阶段一:编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download # 单独拷贝依赖,利用缓存
COPY . .
RUN CGO_ENABLED=0 go build -o server . # 静态编译
# 阶段二:运行
FROM alpine:3.18 # 最小化运行镜像
RUN apk add --no-cache ca-certificates # 只装必要工具
WORKDIR /app
COPY --from=builder /app/server . # 只拷贝编译产物
EXPOSE 8080
CMD ["./server"]
多阶段构建的好处 :编译镜像包含 Go SDK(600MB),运行镜像只需二进制文件(15MB),镜像体积缩小 40 倍。
Dockerfile 优化要点
dockerfile
# ❌ 坏写法:每次代码改动都要重新下载依赖
COPY . .
RUN go mod download
# ✅ 好写法:依赖单独一层,代码改动不影响依赖层缓存
COPY go.mod go.sum ./
RUN go mod download ← 这层被缓存,只要 go.mod 不变就不重新执行
COPY . .
原则 :变化少的层放前面,变化多的层放后面,充分利用构建缓存。
其他要点:
- 用
.dockerignore排除无用文件(.git、node_modules、本地配置) - 合并
RUN命令减少层数:RUN apt update && apt install -y xx && rm -rf /var/lib/apt/lists/* - 非必要不用
root用户运行(安全)
四、ENTRYPOINT vs CMD(面试必考)
两者都用于指定容器启动时执行的命令,但行为不同:
| 对比项 | CMD | ENTRYPOINT |
|---|---|---|
| 作用 | 提供默认命令(可被覆盖) | 定义容器的入口点(通常不覆盖) |
docker run 追加参数 |
替换整个 CMD | 追加到 ENTRYPOINT 之后 |
| 配合使用 | 作为 ENTRYPOINT 的默认参数 | 固定执行的主程序 |
dockerfile
# 只有 CMD:docker run myimage ls 会替换掉 CMD,执行 ls
FROM ubuntu
CMD ["echo", "hello"]
# 只有 ENTRYPOINT:docker run myimage --help 会变成 /server --help
FROM alpine
ENTRYPOINT ["/server"]
# 最佳实践:ENTRYPOINT 定主程序,CMD 提供默认参数
FROM alpine
ENTRYPOINT ["/server"]
CMD ["--port=8080"]
# docker run myimage → /server --port=8080
# docker run myimage --port=9090 → /server --port=9090(CMD 被覆盖)
两种格式:
dockerfile
# Shell 格式:由 /bin/sh -c 执行,PID 不是 1,收不到 SIGTERM → 无法优雅退出
CMD echo hello
ENTRYPOINT /server
# Exec 格式(推荐):直接执行,PID=1,能收到 SIGTERM → 优雅退出
CMD ["echo", "hello"]
ENTRYPOINT ["/server"]
面试答法 :ENTRYPOINT 定义容器主程序,
docker run追加的参数会传给它;CMD 是默认参数,可以被docker run后面的命令完全替换。生产中用 Exec 格式的 ENTRYPOINT,确保进程 PID=1 能正确接收 SIGTERM,实现优雅退出。
五、容器核心技术原理(Namespace + Cgroups)
Namespace(命名空间)------ 隔离
Linux Namespace 让每个容器看到独立的系统视图:
| Namespace | 隔离内容 |
|---|---|
| PID | 进程 ID,容器内 PID=1 |
| NET | 网络接口、路由表、端口 |
| MNT | 文件系统挂载点 |
| UTS | 主机名和域名 |
| IPC | 进程间通信 |
| USER | 用户和用户组 ID |
Cgroups(控制组)------ 限制资源
Cgroups 限制容器能使用的资源上限:
bash
docker run -m 512m --cpus=2 nginx
# 限制该容器:最多 512MB 内存,最多使用 2 个 CPU 核
面试答法:Docker 容器本质是用 Namespace 隔离进程视图(让容器以为自己独占系统),用 Cgroups 限制资源使用量(防止单个容器耗尽宿主机资源),用 OverlayFS 提供分层文件系统。三者结合构成"轻量级虚拟化"。
六、Docker 网络模式
| 模式 | 说明 | 使用场景 |
|---|---|---|
| bridge(默认) | 容器接入虚拟网桥,通过 NAT 访问外网 | 单机多容器通信 |
| host | 容器直接使用宿主机网络,无隔离 | 高性能,需要直接绑定宿主机端口 |
| none | 无网络,完全隔离 | 纯计算任务 |
| overlay | 跨宿主机的容器网络(Docker Swarm) | 多机容器通信 |
bash
# bridge 模式下容器间通信
docker network create mynet
docker run --network mynet --name serviceA app-a
docker run --network mynet --name serviceB app-b
# serviceB 可以直接用 serviceA 作为域名访问容器A
七、数据卷(Volume)
容器的可写层在容器停止后数据丢失,Volume 解决持久化问题:
bash
# 命名卷(推荐):Docker 管理存储位置
docker run -v mysql_data:/var/lib/mysql mysql
# 绑定挂载:直接挂载宿主机目录(开发常用)
docker run -v /host/path:/container/path nginx
# 只读挂载(防止容器修改配置文件)
docker run -v /config:/config:ro nginx
八、常用命令速查
bash
# 镜像操作
docker build -t myapp:v1 . # 构建镜像
docker images # 列出本地镜像
docker pull nginx:alpine # 拉取镜像
docker push registry/myapp:v1 # 推送镜像
docker rmi myapp:v1 # 删除镜像
# 容器操作
docker run -d -p 8080:80 --name web nginx # 后台运行,端口映射
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器
docker logs -f web # 实时查看日志
docker exec -it web /bin/sh # 进入容器
docker stop web && docker rm web # 停止并删除
# 资源清理
docker system prune -a # 清除所有未使用资源
Part 2:Kubernetes(K8s)
八、K8s 是什么,解决什么问题
Kubernetes 是 Google 开源的容器编排平台,解决大规模容器集群的管理问题。
没有 K8s 时的痛点:
容器多了 → 手动部署太繁琐
容器挂了 → 需要人工重启
流量多了 → 手动扩容太慢
发版本了 → 停服更新,有停机时间
K8s 解决:
- 自动调度:决定容器跑在哪台机器上
- 自愈:容器崩溃自动重启,节点故障自动迁移
- 水平扩缩容:一条命令扩容,或根据 CPU 自动扩缩
- 滚动更新:零停机发布新版本,失败自动回滚
- 服务发现:内置 DNS,服务间通过名称互相访问
九、核心架构
┌──────────────── Master Node ────────────────┐
│ │
kubectl ───────────▶│ API Server ← 所有操作的唯一入口 │
│ │ │
│ etcd(持久化存储所有集群状态) │
│ │ │
│ Scheduler ← 决定 Pod 调度到哪个 Node │
│ │ │
│ Controller Manager ← 维持期望状态 │
└──────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Worker Node 1 Worker Node 2 Worker Node 3
┌──────────┐ ┌──────────┐ ┌──────────┐
│ kubelet │ │ kubelet │ │ kubelet │ ← 管理本节点Pod
│ kube-proxy │ kube-proxy │ kube-proxy ← 维护网络规则
│ Pod │ │ Pod │ │ Pod │
│ Pod │ │ Pod │ │ │
└──────────┘ └──────────┘ └──────────┘
核心组件说明
| 组件 | 职责 |
|---|---|
| API Server | 集群的统一入口,所有操作(kubectl/内部组件)都通过它;RESTful API + 认证授权 |
| etcd | 分布式 KV 存储,保存集群全部状态(节点、Pod、配置等),是集群的"数据库" |
| Scheduler | 监听未调度的 Pod,根据资源、亲和性等策略,将 Pod 分配到合适的 Node |
| Controller Manager | 运行各种控制器(Deployment/ReplicaSet/Node 等),持续对比期望状态和实际状态并修正 |
| kubelet | 每个 Node 上的代理,负责启动/停止容器,向 API Server 上报节点和 Pod 状态 |
| kube-proxy | 每个 Node 上维护 iptables/IPVS 规则,实现 Service 的负载均衡和转发 |
十、核心对象详解
Pod ------ 最小调度单元
Pod 是 K8s 中最小的部署单元,可以包含一个或多个容器。
yaml
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: myapp:v1
ports:
- containerPort: 8080
resources:
requests: # 调度时保证的资源
cpu: "100m" # 0.1 个 CPU 核
memory: "128Mi"
limits: # 最大可用资源(超出被限制/OOM Kill)
cpu: "500m"
memory: "512Mi"
- name: sidecar # 同一 Pod 的 sidecar 容器
image: log-collector:v1
同一 Pod 内的容器:
- 共享 Network Namespace(相同 IP,通过 localhost 互访)
- 共享 存储卷(可挂载同一 Volume)
- 共享 生命周期(一起创建、一起销毁)
面试答法:Pod 是对"紧密协作的容器组"的抽象,一个 Pod 内的容器共享网络和存储,适合主应用 + sidecar(日志采集、代理)的部署模式。K8s 永远不直接操作容器,只操作 Pod。
Deployment ------ 无状态应用管理
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3 # 期望 3 个 Pod 副本
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新时最多多出 1 个 Pod
maxUnavailable: 0 # 更新时不允许有不可用的 Pod(零停机)
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v2
层级关系:
Deployment(期望状态管理)
└── ReplicaSet(副本数量保障)
└── Pod × 3(实际运行的容器)
滚动更新过程:
初始: [Pod-v1] [Pod-v1] [Pod-v1]
第1步: 创建新Pod → [Pod-v1] [Pod-v1] [Pod-v1] [Pod-v2]
第2步: 旧Pod就绪 → [Pod-v1] [Pod-v1] [Pod-v2]
第3步: 继续替换 → [Pod-v1] [Pod-v2] [Pod-v2]
第4步: 完成 → [Pod-v2] [Pod-v2] [Pod-v2]
bash
kubectl rollout undo deployment/myapp # 回滚到上一版本
kubectl rollout undo deployment/myapp --to-revision=2 # 回滚到指定版本
kubectl rollout history deployment/myapp # 查看历史版本
Service ------ 服务发现与负载均衡
Pod 的 IP 是不固定的(重启后变化),Service 提供稳定的访问入口。
yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
selector:
app: myapp # 选中带此 Label 的 Pod
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
type: ClusterIP # 服务类型
四种 Service 类型:
| 类型 | 访问范围 | 说明 |
|---|---|---|
| ClusterIP(默认) | 集群内部 | 分配虚拟 IP,仅集群内可访问 |
| NodePort | 集群外部 | 在每个 Node 上开放固定端口(30000-32767) |
| LoadBalancer | 集群外部 | 云厂商提供外部负载均衡器(AWS ELB 等) |
| ExternalName | 集群内访问外部 | 将 Service 映射到外部域名 |
Service 的实现原理(kube-proxy):
请求 → Service VIP(虚拟IP)
→ kube-proxy 维护的 iptables/IPVS 规则
→ 随机选择一个健康的 Pod IP 转发
Ingress ------ 七层路由
Service 是四层(TCP/UDP)负载均衡,Ingress 提供七层(HTTP/HTTPS)路由:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /user
backend:
service:
name: user-svc
port: 80
- path: /order
backend:
service:
name: order-svc
port: 80
常用 Ingress Controller:Nginx Ingress、Traefik、Istio Gateway
ConfigMap & Secret ------ 配置管理
yaml
# ConfigMap:非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
config.yaml: |
server:
port: 8080
---
# Secret:敏感信息(base64 编码存储)
apiVersion: v1
kind: Secret
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM= # base64("password123")
JWT_SECRET: c2VjcmV0a2V5 # base64("secretkey")
在 Pod 中使用:
yaml
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config # 全部 ConfigMap key 注入为环境变量
- secretRef:
name: app-secret
volumeMounts:
- name: config-vol
mountPath: /etc/config # 挂载为文件
volumes:
- name: config-vol
configMap:
name: app-config
注意 :Secret 只是 base64 编码,不是加密。生产环境应配合 Vault 或云厂商 KMS 加密存储。
PV & PVC ------ 持久化存储
PV(Persistent Volume) ← 管理员预先创建,描述实际存储(NFS/云盘等)
PVC(Persistent Volume Claim) ← 开发者按需申请存储资源
yaml
# 开发者只需关心 PVC(我需要多少存储)
apiVersion: v1
kind: PersistentVolumeClaim
spec:
accessModes:
- ReadWriteOnce # 单节点读写
resources:
requests:
storage: 10Gi # 申请 10GB
storageClassName: ssd # 存储类型
# Pod 中挂载 PVC
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
十一、Pod 生命周期与健康检查
Pod 状态流转
Pending(调度中)→ Running(运行中)→ Succeeded(正常退出)
→ Failed(异常退出)
→ Unknown(节点失联)
Pending 时间过长的原因:
- 资源不足(CPU/Memory requests 无法满足)
- 节点选择器/亲和性规则无法匹配
- 镜像拉取失败(ImagePullBackOff)
三种健康检查
yaml
containers:
- name: app
livenessProbe: # 存活探针:失败则重启容器
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30 # 启动后等 30s 再开始探测
periodSeconds: 10 # 每 10s 探测一次
failureThreshold: 3 # 连续失败 3 次才重启
readinessProbe: # 就绪探针:失败则从 Service 摘流
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
startupProbe: # 启动探针:专为慢启动应用,期间不执行其他探针
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10 # 最多等 5 分钟启动
| 探针 | 失败后果 | 用途 |
|---|---|---|
| livenessProbe | 重启容器 | 检测容器是否陷入死锁/僵死 |
| readinessProbe | 从 Service 摘除(不接收流量) | 检测容器是否准备好接收流量 |
| startupProbe | 重启容器 | 给慢启动应用更长的初始化时间 |
面试答法:三种探针解决不同问题。liveness 保证容器"活着",readiness 保证容器"能处理请求",startup 避免慢启动应用被 liveness 误杀。生产中三者配合使用:startup 确保启动完成,readiness 控制流量接入,liveness 长期保活。
十二、调度机制
Pod 调度流程
1. kubectl apply → API Server 写入 etcd(Pod 状态=Pending,nodeName 为空)
2. Scheduler watch 到未调度的 Pod
3. 过滤(Filtering):排除不满足条件的 Node(资源不足、污点不匹配等)
4. 打分(Scoring):对剩余 Node 打分(资源均衡、亲和性等)
5. 选出得分最高的 Node,更新 Pod 的 nodeName
6. 对应 Node 上的 kubelet watch 到分配给自己的 Pod,启动容器
常用调度策略
yaml
spec:
# 节点选择器(硬性要求)
nodeSelector:
disk: ssd
# 节点亲和性(软硬均支持)
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬性
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values: ["zone-a", "zone-b"]
# Pod 反亲和性(避免同一应用的 Pod 集中在一个节点)
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # 软性
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: myapp
topologyKey: kubernetes.io/hostname
# 污点容忍(允许调度到有污点的节点)
tolerations:
- key: "gpu"
operator: "Exists"
effect: "NoSchedule"
十三、自动扩缩容(HPA)
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 使用率超 70% 时扩容
HPA 工作原理:
Metrics Server 采集各 Pod 的 CPU/Memory 使用率
↓
HPA Controller 计算:期望副本数 = ceil(当前副本数 × 当前指标/目标指标)
↓
当 CPU=140%,目标70%,当前2副本:期望 = ceil(2 × 140/70) = 4
↓
更新 Deployment 的 replicas=4,触发扩容
十四、StatefulSet ------ 有状态应用
Deployment 管理无状态应用(Pod 可以随意替换),StatefulSet 用于有状态应用(如 MySQL、Kafka、ZooKeeper):
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机(app-7d4f9-xxx) | 有序固定(app-0, app-1, app-2) |
| 启动顺序 | 并行启动 | 按序启动(0→1→2) |
| 存储 | 共享或临时 | 每个 Pod 独立的 PVC |
| 网络标识 | 不固定 | 固定 DNS(app-0.svc) |
| 删除顺序 | 随机 | 逆序删除(2→1→0) |
十五、DaemonSet ------ 每节点一个 Pod
DaemonSet 确保集群中每个(或指定的)Node 上都运行一个 Pod 副本,常用于:
- 日志采集:每个 Node 上运行 Fluentd/Filebeat,收集该节点的日志
- 监控 Agent:每个 Node 上运行 Prometheus Node Exporter,采集节点指标
- 网络插件:如 Calico、Flannel,需要在每个 Node 上运行
- 存储插件:如 Ceph 客户端
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
selector:
matchLabels:
app: log-collector
template:
metadata:
labels:
app: log-collector
spec:
containers:
- name: fluentd
image: fluentd:v1.14
volumeMounts:
- name: varlog
mountPath: /var/log # 读取宿主机日志
volumes:
- name: varlog
hostPath:
path: /var/log
与 Deployment 的区别:
- Deployment:指定副本数量(如 3 个),由调度器决定分布在哪些 Node
- DaemonSet:不指定副本数,每个符合条件的 Node 自动运行一个
新 Node 加入集群时,DaemonSet Controller 自动在其上创建 Pod;Node 删除时,对应 Pod 也被清理。
十六、Job & CronJob ------ 一次性与定时任务
Job ------ 运行到完成
Job 保证一个或多个 Pod 成功完成,适合数据库迁移、批量处理等一次性任务:
yaml
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
spec:
completions: 1 # 需要成功完成的 Pod 数
parallelism: 1 # 并行运行的 Pod 数
backoffLimit: 3 # 失败后最多重试 3 次
template:
spec:
restartPolicy: Never # Job Pod 必须设置为 Never 或 OnFailure
containers:
- name: migration
image: myapp:v2
command: ["./migrate", "--up"]
CronJob ------ 定时任务
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: report-generator
spec:
schedule: "0 2 * * *" # Cron 表达式:每天凌晨 2 点
concurrencyPolicy: Forbid # 上一次未完成时,不创建新 Job(防重叠)
successfulJobsHistoryLimit: 3 # 保留最近 3 个成功 Job 的记录
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: report-tool:v1
command: ["./generate-report"]
concurrencyPolicy 三种策略:
Allow(默认):允许并发运行多个 JobForbid:上一次未完成则跳过Replace:用新 Job 替换运行中的旧 Job
十七、RBAC ------ 权限控制(面试加分)
K8s 默认开启 RBAC(Role-Based Access Control),控制谁 能对哪些资源 执行哪些操作。
核心概念
Subject(主体) Role(角色) Resource(资源)
ServiceAccount ──绑定──▶ Role/ClusterRole ──定义──▶ pods/deployments/...
User verbs: get/list/create/delete
Group
- Role:命名空间级别的权限定义
- ClusterRole:集群级别的权限定义(跨 Namespace)
- RoleBinding:将 Role 绑定到 Subject(在某个 Namespace 生效)
- ClusterRoleBinding:将 ClusterRole 绑定到 Subject(全集群生效)
实际案例
yaml
# 1. 创建 ServiceAccount(应用的身份)
apiVersion: v1
kind: ServiceAccount
metadata:
name: go-service-sa
namespace: production
---
# 2. 创建 Role(定义权限:只能读取 ConfigMap 和 Secret)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: config-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"] # 只读,不能创建/删除
---
# 3. 绑定:把 Role 赋给 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: go-service-config-reader
namespace: production
subjects:
- kind: ServiceAccount
name: go-service-sa
roleRef:
kind: Role
name: config-reader
apiGroup: rbac.authorization.k8s.io
---
# 4. Pod 使用这个 ServiceAccount
spec:
serviceAccountName: go-service-sa
containers:
- name: go-service
...
常见 verbs
| 操作 | verbs |
|---|---|
| 查看列表 | list |
| 查看详情 | get |
| 监听变化 | watch |
| 创建 | create |
| 更新 | update, patch |
| 删除 | delete |
| 全部 | * |
面试答法:K8s RBAC 通过 Role/ClusterRole 定义权限(能对哪些资源做哪些操作),通过 RoleBinding/ClusterRoleBinding 将权限绑定给 ServiceAccount(应用身份)或 User(人)。微服务中每个服务用独立的 ServiceAccount,遵循最小权限原则,只授予必要的权限,避免容器逃逸后横向攻击。
十八、命名空间(Namespace)与资源隔离
bash
# 不同团队/环境用不同 Namespace 隔离
kubectl create namespace dev
kubectl create namespace prod
# 给 Namespace 设置资源配额
apiVersion: v1
kind: ResourceQuota
metadata:
namespace: dev
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
十九、常用 kubectl 命令
bash
# 查看资源
kubectl get pods -n default # 查看 Pod
kubectl get pods -o wide # 显示 Node 信息
kubectl describe pod myapp-xxx # 查看 Pod 详情(排查问题首选)
kubectl logs myapp-xxx -c app -f # 查看容器日志(-c 指定容器)
kubectl top pod # 查看 Pod 资源使用
# 操作资源
kubectl apply -f deploy.yaml # 应用配置(创建/更新)
kubectl delete -f deploy.yaml # 删除资源
kubectl scale deployment myapp --replicas=5 # 手动扩缩容
kubectl set image deployment/myapp app=myapp:v3 # 更新镜像
# 调试
kubectl exec -it myapp-xxx -- /bin/sh # 进入容器
kubectl port-forward pod/myapp-xxx 8080:8080 # 本地端口转发
kubectl get events --sort-by=.lastTimestamp # 查看集群事件
# 滚动更新
kubectl rollout status deployment/myapp # 查看更新状态
kubectl rollout pause deployment/myapp # 暂停更新(灰度)
kubectl rollout resume deployment/myapp # 恢复更新
kubectl rollout undo deployment/myapp # 回滚
二十、Go 应用容器化最佳实践
dockerfile
# 生产级 Go 应用 Dockerfile
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o server .
# -w -s:去掉调试信息,进一步缩小体积
FROM scratch # 极致精简,比 alpine 更小
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
K8s 部署 Go 应用的完整配置:
yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-service
spec:
replicas: 3
selector:
matchLabels:
app: go-service
template:
spec:
containers:
- name: go-service
image: registry/go-service:v1
ports:
- containerPort: 8080
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # 优雅退出:等待流量切走
二十一、面试高频问题速答
Q:Docker 容器和虚拟机的区别?
虚拟机通过 Hypervisor 模拟硬件,每个 VM 有完整 OS,启动慢、占用资源多。容器共享宿主机内核,用 Namespace 隔离进程视图,用 Cgroups 限制资源,本质是宿主机上的进程,启动秒级、资源开销小。
Q:Docker 镜像为什么分层?有什么好处?
镜像分层基于 OverlayFS,每条 Dockerfile 指令产生一个只读层。好处:① 多镜像共享相同基础层,节省存储;② 构建时利用缓存,只重建变化的层;③ 拉取镜像时已有的层不重复下载,加速部署。
Q:K8s 中 Pod、Deployment、Service 的关系?
Pod 是最小运行单元,但直接管理 Pod 不可靠(挂了不会自动重建)。Deployment 通过 ReplicaSet 维持 Pod 副本数量,实现滚动更新和回滚。Service 为一组 Pod 提供稳定的访问入口,解决 Pod IP 变化的问题,并提供负载均衡。
Q:K8s 如何实现零停机发布?
Deployment 的滚动更新:先创建新版本 Pod,健康检查通过后将其加入 Service,再逐步替换旧版本 Pod。配合
maxUnavailable=0(不允许不可用 Pod),确保始终有足够的 Pod 处理流量。readinessProbe确保新 Pod 真正就绪后才接收流量。
Q:liveness 和 readiness 探针有什么区别?
liveness 探针失败会重启容器,用于检测容器是否进入无法自愈的状态(如死锁)。readiness 探针失败不重启,只是把 Pod 从 Service 的 Endpoints 中摘除,停止接收新请求,用于检测容器是否准备好处理流量(如初始化中、依赖不可用)。
Q:K8s 中如何管理配置和敏感信息?
非敏感配置用 ConfigMap,敏感信息用 Secret(base64 编码)。两者都可以以环境变量或文件形式注入 Pod。注意 Secret 的 base64 不是加密,生产环境需配合外部密钥管理系统(如 Vault)。
Q:K8s 的 Service 是如何实现负载均衡的?
kube-proxy 监听 Service 和 Endpoints 变化,在每个 Node 上维护 iptables(或 IPVS)规则。请求到达 Service VIP 后,由内核的 iptables/IPVS 规则随机/轮询转发到健康的 Pod IP。IPVS 模式性能更好,支持更多负载均衡算法。
Q:Pod 一直处于 Pending 状态怎么排查?
执行
kubectl describe pod <name>查看 Events 部分。常见原因:① 资源不足(Insufficient cpu/memory);② 节点选择器/亲和性不匹配;③ 污点未容忍(Taint);④ PVC 未绑定存储。
Q:K8s 如何实现服务发现?
K8s 内置 CoreDNS,为每个 Service 自动注册 DNS 记录:
<service-name>.<namespace>.svc.cluster.local。Pod 通过 Service 名称直接访问,CoreDNS 解析到 Service 的 ClusterIP,再由 kube-proxy 转发到具体 Pod。
Q:Deployment 和 StatefulSet 的区别?
Deployment 管理无状态应用,Pod 可随意替换,名称随机,存储可共享。StatefulSet 管理有状态应用,Pod 名称有序固定(app-0/1/2),每个 Pod 有独立 PVC,有序启动和删除,Pod 重建后网络标识不变。适用于 MySQL、Kafka 等需要稳定标识和独立存储的应用。
Q:ENTRYPOINT 和 CMD 有什么区别?
CMD 提供默认命令,
docker run时传入参数会完全替换 CMD。ENTRYPOINT 定义容器主程序,docker run传入的参数会追加到 ENTRYPOINT 后。最佳实践:用 ENTRYPOINT 固定主程序,CMD 提供可覆盖的默认参数。同时必须使用 Exec 格式(["cmd"])而非 Shell 格式,确保进程 PID=1 能正确收到 SIGTERM 实现优雅退出。
Q:DaemonSet 和 Deployment 有什么区别?什么场景用 DaemonSet?
Deployment 指定副本数,由调度器决定 Pod 分布;DaemonSet 不指定副本数,在每个符合条件的 Node 上运行一个 Pod。用于需要在每台机器上运行的基础设施组件:日志采集(Fluentd)、监控 Agent(Node Exporter)、网络插件(Calico)等。Node 加入集群时自动部署,Node 移除时自动清理。
Q:Job 和 Deployment 的区别?CronJob 怎么用?
Deployment 管理持续运行的服务(不退出);Job 管理运行完即退出的一次性任务,保证 Pod 成功完成指定次数,失败会重试。CronJob 按 Cron 表达式定期创建 Job,适合报表生成、数据清理等定时任务。注意 Job 的 Pod 必须设置
restartPolicy: Never或OnFailure,CronJob 需要设置concurrencyPolicy控制并发行为。
Q:什么是 K8s RBAC?在生产中怎么用?
RBAC 是基于角色的访问控制。通过 Role/ClusterRole 定义权限(对哪些资源做哪些操作),通过 RoleBinding/ClusterRoleBinding 将权限绑定给 ServiceAccount(应用身份)或用户。生产实践:每个微服务使用独立的 ServiceAccount,只授予运行所需的最小权限(比如只读 ConfigMap/Secret),遵循最小权限原则,防止容器被攻破后横向扩散。
Q:如何给一个 K8s 集群中的 Go 服务做优雅退出?
① 监听 SIGTERM 信号,停止接收新请求,等待现有请求处理完成;② 在 Pod spec 中配置
preStop hook加 sleep(等待 Service 摘流完成,因为 K8s 摘流和发 SIGTERM 是并行的);③ 设置合理的terminationGracePeriodSeconds(默认30s)。
二十二、一张图记住核心
Docker 核心:
┌─────────────────────────────────────────────────────────────┐
│ Namespace(隔离视图)+ Cgroups(限制资源)= 轻量级容器 │
│ OverlayFS 分层存储 = 共享基础层,节省空间,加速构建 │
│ 多阶段构建 = 编译环境不进最终镜像,体积最小化 │
└─────────────────────────────────────────────────────────────┘
K8s 核心:
┌─────────────────────────────────────────────────────────────┐
│ Pod(最小单元)← Deployment(无状态)/ StatefulSet(有状态) │
│ DaemonSet(每节点一个)/ Job(一次性)/ CronJob(定时) │
│ Service(稳定访问)← ClusterIP / NodePort / LoadBalancer │
│ ConfigMap / Secret(配置与密钥分离) │
│ liveness + readiness(健康检查 + 流量控制) │
│ HPA(自动扩缩容)+ 滚动更新(零停机发布) │
│ RBAC(ServiceAccount + Role + RoleBinding = 最小权限) │
└─────────────────────────────────────────────────────────────┘
排查问题三板斧:
kubectl describe pod <name> → 看 Events(调度/启动失败原因)
kubectl logs <name> -f → 看应用日志(运行时错误)
kubectl get events → 看集群事件(全局异常)
总结:Docker 面试核心是"容器 vs 虚拟机"、"镜像分层原理"、"ENTRYPOINT vs CMD";K8s 面试核心是"核心对象的职责与关系"(Pod/Deployment/StatefulSet/DaemonSet/Job/Service/Ingress)、"零停机发布流程"、"健康检查三种探针"、"RBAC 权限控制"。把这些讲清楚,Docker + K8s 面试基本稳了。