Docker+K8s 部署微服务:从搭建到运维的全流程指南(Java 老鸟实战版)
Docker+K8s 部署微服务:从搭建到运维的全流程指南(Java 老鸟实战版)
作为一名摸爬滚打八年的 Java 开发,从最初的单体应用 WAR 包扔 Tomcat,到后来的微服务集群部署,踩过的坑能绕公司机房三圈。其中最让人头疼的就是「环境一致性」和「运维复杂度」—— 开发环境跑的飞起,测试环境各种报错,生产环境突然雪崩,排查半天发现是配置不一致、端口冲突、依赖缺失…
直到 Docker+K8s 组合横空出世,才算彻底解决了这些痛点。这篇文章就从 Java 开发的视角,带大家走一遍「微服务容器化 + K8s 编排」的全流程,从环境搭建到生产运维,每一步都附实战代码和避坑指南,保证看完就能落地。
一、为什么选择 Docker+K8s?(八年经验的选型逻辑)
在聊技术细节前,先说说我为什么推荐这个组合。作为 Java 开发,我们可能接触过 Jenkins+Ansible、Docker Compose、甚至是云厂商的容器服务,但最终选择 Docker+K8s,核心原因有 3 点:
- 环境一致性:一次构建,到处运行以前部署微服务,要在每个服务器装 JDK、配置环境变量、解决依赖冲突,甚至还要注意 JVM 参数和服务器内核的兼容性。用 Docker 打包后,Java 应用、JDK、依赖库、配置文件全被封装在镜像里,开发、测试、生产环境完全一致,再也不用喊 “我这能跑啊”。
- 弹性伸缩:应对流量峰值不慌微服务的核心优势是可扩展,但手动扩容时,要先申请服务器、装环境、部署应用,等操作完流量可能已经过去了。K8s 能根据 CPU、内存使用率自动扩缩容,比如双 11 峰值时自动加 Pod,峰值过后自动缩容,省资源又省心。
- 运维自动化:解放双手,专注业务以前上线要手动停服务、传包、启动,还要担心回滚问题。K8s 支持滚动更新、灰度发布、一键回滚,配合 CI/CD 工具(比如 Jenkins、GitLab CI),可以实现代码提交后自动构建镜像、部署到 K8s,全程无需手动干预。
避坑提醒:很多中小团队会觉得 K8s 太重,其实现在 kubeadm 搭建集群已经很简单,而且 K8s 的核心功能(部署、扩缩容、服务发现)完全能满足微服务的需求,后续扩展也方便。如果团队规模特别小,也可以先从 Docker Compose 入手,但长远来看,K8s 是趋势。
二、环境准备(生产级配置,拒绝 “玩具集群”)
2.1 服务器配置(最少 3 节点,生产推荐 4 核 8G 起)
| 节点角色 | 数量 | 配置要求 | 操作系统 |
|---|---|---|---|
| Master 节点 | 1 | 4 核 8G,50G 硬盘 | CentOS 7.9 / Ubuntu 20.04 |
| Worker 节点 | 2+ | 4 核 8G,100G 硬盘 | CentOS 7.9 / Ubuntu 20.04 |
| 镜像仓库节点 | 1 | 2 核 4G,100G 硬盘 | CentOS 7.9 / Ubuntu 20.04 |
说明:我这里用的是 CentOS 7.9,因为生产环境中 CentOS 的稳定性和兼容性更好。如果用 Ubuntu,命令略有差异,但核心步骤一致。
2.2 必备软件版本(兼容性优先,避免踩版本坑)
- Docker:20.10.x(LTS 版本,稳定为主,不要用最新版)
- K8s:1.24.x(1.24 是比较成熟的版本,后续版本改动较大,新手不建议追新)
- 镜像仓库:Harbor 2.5.x(企业级镜像仓库,支持权限管理、镜像扫描)
- JDK:11(微服务推荐 JDK 11,长期支持版本)
2.3 环境初始化(所有节点执行,关键步骤)
1. 关闭防火墙和 SELinux(生产环境可配置白名单,这里简化操作)
# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭SELinux setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config 2. 关闭 swap 分区(K8s 要求,否则会影响性能和稳定性)
swapoff -a sed -i '/swap/s/^/#/' /etc/fstab 3. 配置内核参数(开启 IP 转发和桥接)
cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF # 生效配置 sysctl --system 4. 安装 Docker(所有节点)
# 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 配置Docker镜像源(阿里云,速度快) yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 安装指定版本Docker yum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io # 启动Docker并设置开机自启 systemctl start docker systemctl enable docker # 配置Docker镜像加速(阿里云,注册账号后获取专属加速地址) mkdir -p /etc/docker cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"] # 关键:K8s推荐使用systemd驱动 } EOF # 重启Docker systemctl daemon-reload systemctl restart docker 避坑提醒:Docker 的 cgroup 驱动必须设置为 systemd,否则 K8s 初始化会报错。这是我当年踩过的第一个大坑,记忆犹新。
三、Docker 实战:微服务容器化
作为 Java 开发,我们的核心目标是把 Spring Boot 微服务打包成 Docker 镜像。这一步要注意镜像瘦身、分层构建,避免镜像过大导致部署缓慢。
3.1 编写 Dockerfile(Java 微服务优化版)
以一个标准的 Spring Boot 微服务为例,Dockerfile 放在项目根目录:
# 第一阶段:构建应用(使用maven镜像编译打包) FROM maven:3.8.8-openjdk-11 AS builder # 设置工作目录 WORKDIR /app # 复制pom.xml和src目录 COPY pom.xml . COPY src ./src # 编译打包(跳过测试,加快构建速度) RUN mvn clean package -DskipTests # 第二阶段:运行应用(使用轻量级openjdk镜像) FROM openjdk:11-jre-slim # 设置工作目录 WORKDIR /app # 复制构建阶段的jar包到当前目录(改名为app.jar,简洁) COPY --from=builder /app/target/*.jar app.jar # 配置JVM参数(根据服务器配置调整,避免OOM) ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" # 暴露微服务端口(根据实际应用端口修改) EXPOSE 8080 # 启动命令(推荐用exec格式,保证容器退出信号能传递给应用) ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] 关键优化点:分层构建:第一阶段用 maven 镜像编译,第二阶段用 jre-slim 镜像运行,镜像大小从几百 MB 缩减到 100MB 左右。JVM 参数优化:-Xms 和 - Xmx 设置为相同值,避免频繁扩容;使用 G1GC 垃圾收集器,适合微服务场景。入口命令:用 exec 格式,确保docker stop能正常终止应用,而不是强制杀死进程。3.2 构建 Docker 镜像
# 进入项目根目录 cd /path/to/your/project # 构建镜像(格式:仓库地址/项目名/服务名:版本号) docker build -t harbor.example.com/microservice/user-service:1.0.0 . 3.3 搭建 Harbor 镜像仓库(企业级必备)
Docker Hub 限速,而且私有镜像需要付费,所以生产环境一定要搭建自己的 Harbor 仓库。
1. 安装 Docker Compose(Harbor 依赖)
curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose # 验证安装 docker-compose --version 2. 部署 Harbor
# 下载Harbor安装包(版本2.5.3,稳定版) wget https://github.com/goharbor/harbor/releases/download/v2.5.3/harbor-offline-installer-v2.5.3.tgz # 解压 tar -zxvf harbor-offline-installer-v2.5.3.tgz -C /usr/local/ # 进入Harbor目录 cd /usr/local/harbor # 复制配置文件 cp harbor.yml.tmpl harbor.yml # 修改配置文件(关键配置) vi harbor.yml 关键配置修改:
hostname: harbor.example.com # 改为你的服务器IP或域名 http: port: 80 # https: # 生产环境建议配置HTTPS,这里简化用HTTP # port: 443 # certificate: /path/to/cert # private_key: /path/to/key harbor_admin_password: Harbor12345 # 管理员密码,建议修改为复杂密码 data_volume: /data/harbor # 镜像存储目录,建议挂载大硬盘 3. 启动 Harbor
# 执行安装脚本 ./install.sh # 启动Harbor(后续重启用) docker-compose -f /usr/local/harbor/docker-compose.yml up -d 4. 推送镜像到 Harbor
# 登录Harbor(输入用户名admin,密码Harbor12345) docker login harbor.example.com # 推送镜像 docker push harbor.example.com/microservice/user-service:1.0.0 # 退出登录(可选) docker logout harbor.example.com 避坑提醒:如果 Harbor 用 HTTP 协议,需要在所有 K8s 节点的 Docker 配置中添加信任:在/etc/docker/daemon.json中添加"insecure-registries": ["harbor.example.com"],然后重启 Docker。
四、K8s 集群搭建(kubeadm 实战版)
4.1 安装 K8s 组件(所有节点)
# 配置K8s镜像源(阿里云) cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-$basearch/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg exclude=kubelet kubeadm kubectl EOF # 安装指定版本K8s组件 yum install -y kubelet-1.24.10 kubeadm-1.24.10 kubectl-1.24.10 --disableexcludes=kubernetes # 启动kubelet并设置开机自启 systemctl start kubelet systemctl enable kubelet 4.2 初始化 Master 节点
# 初始化Master(指定镜像源、Pod网段) kubeadm init \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.24.10 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 # 配置kubectl(当前用户) mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # 验证Master节点状态 kubectl get nodes 初始化成功后,会输出 Worker 节点加入集群的命令,类似:
kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 4.3 安装网络插件(Calico,必装)
K8s 集群需要网络插件才能让 Pod 之间通信,推荐用 Calico(稳定、性能好):
kubectl apply -f https://docs.projectcalico.org/v3.24/manifests/calico.yaml # 验证网络插件状态 kubectl get pods -n kube-system 4.4 Worker 节点加入集群
在所有 Worker 节点执行 Master 初始化时输出的kubeadm join命令。如果忘记了命令,可以在 Master 节点执行:
kubeadm token create --print-join-command 4.5 验证集群状态
# 查看所有节点(状态为Ready表示正常) kubectl get nodes # 查看集群核心组件 kubectl get pods -n kube-system 避坑提醒:如果 kubelet 启动失败,大概率是 Docker 的 cgroup 驱动配置错误,回去检查daemon.json中的native.cgroupdriver=systemd。Calico 安装后如果 Pod 一直处于 Pending 状态,可能是服务器内存不足(至少 4G),或者 Pod 网段与服务器网段冲突。
五、微服务部署到 K8s(实战 yaml 配置)
K8s 通过 yaml 文件定义资源(Pod、Service、Ingress 等),下面以 user-service 为例,编写完整的部署配置。
5.1 编写 Deployment.yaml(部署微服务)
apiVersion: apps/v1 kind: Deployment metadata: name: user-service # Deployment名称 namespace: microservice # 命名空间,建议按项目划分 spec: replicas: 2 # 副本数,生产环境至少2个 selector: matchLabels: app: user-service # 匹配Pod的标签 template: metadata: labels: app: user-service # Pod标签 spec: containers: - name: user-service # 容器名称 image: harbor.example.com/microservice/user-service:1.0.0 # 镜像地址 imagePullPolicy: IfNotPresent # 镜像拉取策略:本地有则不用拉取 ports: - containerPort: 8080 # 容器端口,与微服务端口一致 resources: # 资源限制,避免单个Pod占用过多资源 requests: cpu: "500m" # 最小CPU需求(0.5核) memory: "512Mi" # 最小内存需求 limits: cpu: "1000m" # 最大CPU限制(1核) memory: "1024Mi" # 最大内存限制 livenessProbe: # 存活探针:检测应用是否存活 httpGet: path: /actuator/health/liveness # Spring Boot Actuator端点 port: 8080 initialDelaySeconds: 60 # 启动后延迟60秒开始检测 periodSeconds: 10 # 检测间隔10秒 readinessProbe: # 就绪探针:检测应用是否准备好接收请求 httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 5 env: # 环境变量配置(也可以用ConfigMap/Secret) - name: SPRING_PROFILES_ACTIVE value: "prod" - name: DB_HOST value: "mysql-service" 关键说明:命名空间:用namespace: microservice划分项目,避免资源冲突。探针配置:Spring Boot 应用需要引入spring-boot-starter-actuator依赖,否则探针会失败。资源限制:根据微服务的实际需求调整,避免资源浪费或不足。
5.2 编写 Service.yaml(暴露微服务)
Deployment 部署后,Pod 的 IP 是动态变化的,需要用 Service 暴露服务,提供固定访问地址:
apiVersion: v1 kind: Service metadata: name: user-service namespace: microservice spec: selector: app: user-service # 匹配上面Deployment的Pod标签 ports: - port: 80 # Service端口 targetPort: 8080 # 映射到Pod的端口 type: ClusterIP # 集群内部访问,外部访问用Ingress 5.3 编写 Ingress.yaml(外部访问入口)
如果需要从集群外部访问微服务,用 Ingress 配置域名和路由:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: microservice-ingress namespace: microservice annotations: kubernetes.io/ingress.class: "nginx" # 指定Ingress控制器为Nginx spec: rules: - host: user.example.com # 访问域名 http: paths: - path: / pathType: Prefix backend: service: name: user-service # 映射到上面的Service port: number: 80 注意:需要先安装 Ingress Nginx 控制器:
bash
运行
5.4 执行部署命令
# 创建命名空间 kubectl create namespace microservice # 部署Deployment kubectl apply -f Deployment.yaml # 部署Service kubectl apply -f Service.yaml # 部署Ingress kubectl apply -f Ingress.yaml # 查看部署状态 kubectl get pods -n microservice kubectl get svc -n microservice kubectl get ingress -n microservice 5.5 验证访问
# 集群内部访问(Master节点执行) curl http://user-service.microservice.svc.cluster.local:80/health # 外部访问(需要在本地DNS或hosts文件中配置域名解析) curl http://user.example.com/health 六、运维实战:监控、日志、更新
6.1 监控:Prometheus+Grafana
1. 安装 Prometheus(监控指标收集)
# 用helm安装(推荐,简单快捷) helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install prometheus prometheus-community/prometheus -n monitoring --create-namespace 2. 安装 Grafana(可视化面板)
helm repo add grafana https://grafana.github.io/helm-charts helm install grafana grafana/grafana -n monitoring 3. 配置 Spring Boot 微服务监控
在微服务的 pom.xml 中添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> 在 application-prod.yml 中配置:
management: endpoints: web: exposure: include: health,info,prometheus # 暴露prometheus端点 metrics: tags: application: user-service # 增加应用标签,方便区分 endpoint: health: probes: enabled: true show-details: always 4. 配置 Prometheus 抓取微服务指标
修改 Prometheus 的配置文件,添加 Job:
scrape_configs: - job_name: 'microservice' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_label_app] action: keep regex: .*service # 匹配所有后缀为service的Pod - source_labels: [__meta_kubernetes_pod_container_port_number] action: keep regex: 8080 # 匹配8080端口 5. 导入 Grafana 面板
Grafana 默认用户名 admin,密码可以通过以下命令获取:
kubectl get secret grafana -n monitoring -o jsonpath="{.data.admin-password}" | base64 --decode 登录后导入 Spring Boot 的监控面板(面板 ID:12900),即可看到 JVM 内存、CPU、请求量、响应时间等指标。
6.2 日志收集:ELK Stack
1. 安装 Elasticsearch(存储日志)
helm repo add elastic https://helm.elastic.co helm install elasticsearch elastic/elasticsearch -n logging --create-namespace 2. 安装 Logstash(日志处理)
helm install logstash elastic/logstash -n logging 3. 安装 Kibana(日志可视化)
helm install kibana elastic/kibana -n logging 4. 配置微服务日志输出到文件
在 Spring Boot 的 logback-spring.xml 中配置:
<appender name="FILE"> <file>/var/log/user-service.log</file> <rollingPolicy> <fileNamePattern>/var/log/user-service.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="FILE" /> </root> 5. 配置 Filebeat 收集日志(每个 Worker 节点)
helm install filebeat elastic/filebeat -n logging 修改 Filebeat 配置,指定日志文件路径:
filebeat.inputs: - type: filestream paths: - /var/log/*.log # 匹配容器日志目录 processors: - add_kubernetes_metadata: host: ${NODE_NAME} matchers: - logs_path: logs_path: "/var/log" output.elasticsearch: hosts: ["elasticsearch-master:9200"] 6.3 滚动更新与回滚
1. 滚动更新(发布新版本)
# 修改镜像版本 kubectl set image deployment/user-service user-service=harbor.example.com/microservice/user-service:1.0.1 -n microservice # 查看更新状态 kubectl rollout status deployment/user-service -n microservice 2. 回滚(版本有问题时)
# 查看更新历史 kubectl rollout history deployment/user-service -n microservice # 回滚到上一版本 kubectl rollout undo deployment/user-service -n microservice # 回滚到指定版本 kubectl rollout undo deployment/user-service --to-revision=1 -n microservice 七、八年开发避坑指南(精华总结)
- Docker 镜像不要用 latest 标签:生产环境一定要指定具体版本,否则会导致不同环境镜像不一致,排查困难。
- K8s 资源配置要合理:JVM 的 - Xmx 不能超过 Pod 的 memory limits,否则会被 OOM 杀死;CPU limits 不要设置过高,避免资源浪费。
- 探针配置不能少:livenessProbe 和 readinessProbe 一定要配置,否则 K8s 无法判断应用状态,会导致流量分发到故障节点。
- 镜像仓库一定要搭建:不要依赖 Docker Hub,私有镜像用 Harbor,支持权限管理和镜像扫描,安全性更高。
- 网络插件选择 Calico:Flannel 功能简单,Calico 支持网络策略、Pod 间通信控制,生产环境更推荐。
- 监控和日志必须到位:没有监控就像开车没仪表盘,出现问题无法快速定位;日志要集中收集,方便排查问题。
- 避免单节点故障:Master 节点建议搭建高可用(至少 2 个),Worker 节点至少 2 个,避免单点故障导致服务不可用。
- 不要直接操作 Pod:Pod 是临时的,要通过 Deployment 管理,否则 Pod 重启后配置会丢失。
八、总结与展望
Docker+K8s 已经成为微服务部署的标准方案,本文从环境搭建、容器化、K8s 部署到运维监控,覆盖了全流程的实战细节。作为 Java 开发,我们不需要成为 K8s 专家,但必须掌握核心用法,才能让微服务真正落地生产。
后续可以进一步优化的方向:
- 集成 CI/CD:用 Jenkins 或 GitLab CI 实现代码提交后自动构建镜像、部署到 K8s,实现持续部署。
- 服务网格:引入 Istio,实现流量控制、熔断降级、链路追踪等高级功能。
- 存储方案:对于需要持久化存储的微服务(如数据库),可以使用 K8s 的 PV/PVC 机制,结合 NFS 或云存储。
如果大家在部署过程中遇到问题,欢迎在评论区留言,我会第一时间回复。也欢迎大家分享自己的实战经验,一起交流进步!