转载自:https://cloud.tencent.com/developer/article/1477003

我们知道 Kubernetes 是 Google 开源的容器集群管理系统,它构建在目前流行的 Docker 技术之上,为容器化的应用提供资源调度、部署运行、服务发现、扩容缩容等一整套功能,用于容器集群的自动化部署、扩容以及运维的开源平台。Spring Boot 是 Spring 框架的集成,通过该框架可以大大简化应用的搭建、开发及部署过程,从而深受大家喜爱。之前 Spring Boot 项目大多以传统虚拟机或物理机部署方式,转容器化 K8S 集群部署的话,也是非常简单的,这里给大家分享一下我在工作中,实际操作中的一些使用经验。

1、服务配置文件处理方式

对于各个项目分环境部署,最麻烦的就是配置文件的问题,不同的环境需要加载不同的配置,好在 Spring Boot 框架加载配置是非常方便的,具体如何加载配置文件可以参考 这里,我们可以针对不同的环境分别配置不同的配置文件,这里有两个地方要注意一下:

  • 构建镜像的时候,尽量实现一个镜像支持所有环境(即所有配置都打到一个镜像里面去),在容器启动时指定加载哪个环境配置即可,例如:在部署 Deployment 时指定 args: ["--spring.profiles.active=prod"] 参数启动。
  • 尽量不要每个环境打出来一个镜像版本,传统方式在构建的时候指定 -D prod 配置 Profile 来指定加载哪个配置,来生成不同的产物 jar,容器化部署后不需要这样,那样后期控制各镜像版本发布会比较麻烦。

2、服务镜像相关配置

容器化部署服务,肯定少不了镜像制作这一步,镜像可以分为基础镜像和应用镜像。

2.1 基础镜像

基础镜像要求体积尽量小,方便拉取,同时安装一些必要的软件,方便后期进入容器内排查问题,我们需要准备好服务运行的底层系统镜像,比如 Centos、Ubuntu 等常见 Linux 操作系统,然后基于该系统镜像,构建服务运行需要的环境镜像,比如一些常见组合:Centos + JdkCentos + Jdk + TomcatCentos + nginx 等,由于不同的服务运行依赖的环境版本不一定一致,所以还需要制作不同版本的环境镜像,例如如下基础镜像版本。

  • Centos6.5 + Jdk1.8: registry.docker.com/baseimg/centos-jdk:6.5_1.8
  • Centos7.5 + Jdk1.8: registry.docker.com/baseimg/centos-jdk:7.5_1.8
  • Centos7.5 + Jdk1.7: registry.docker.com/baseimg/centos-jdk:7.5_1.7
  • Centos7 + Tomcat8 + Jdk1.8: registry.docker.com/baseimg/centos-tomcat-jdk:7.5_8.5_1.8
  • Centos7 + Nginx: registry.docker.com/baseimg/centos-tomcat-jdk:7.5_1.10.2

这样,就可以标识该基础镜像的系统版本及软件版本,方便后边选择对应的基础镜像来构建应用镜像。基础镜像的制作方法之一,可以参考 使用 febootstrap 制作自定义基础镜像 方式。

2.2 应用镜像

有了上边的基础镜像后,就很容易构建出对应的应用镜像了,例如一个简单的应用镜像 Dockerfile 如下:

FROM registry.docker.com/baseimg/centos-jdk:7.5_1.8

COPY app-name.jar /opt/project/app.jar
EXPOSE 8080
ENTRYPOINT ["/java", "-jar", "/opt/project/app.jar"]

当然,这里我建议使用另一种方式来启动服务,将启动命令放在统一 shell 启动脚本执行,例如如下Dockerfile 示例:

FROM registry.docker.com/baseimg/centos-jdk:7.5_1.8

COPY app-name.jar /opt/project/app.jar
COPY entrypoint.sh /opt/project/entrypoint.sh
EXPOSE 8080
ENTRYPOINT ["/bin/sh", "/opt/project/entrypoint.sh"]

将服务启动命令配置到 entrypoint.sh,这样我们可以扩展做很多事情,比如启动服务前做一些初始化操作等,还可以向容器传递参数到脚本执行一些特殊操作,而且这里变成脚本来启动,这样后续构建镜像基本不需要改 Dockerfile 了。

#!/bin/bash
# do other things here
java -jar $JAVA_OPTS /opt/project/app.jar $1 > /dev/null 2>&1

上边示例中,我们就注入 $JAVA_OPTS 环境变量,来优化 JVM 参数,还可以传递一个变量,这个变量大家应该就猜到了,就是服务启动加载哪个配置文件参数,例如:--spring.profiles.active=prod 那么,在 Deployment 中就可以通过如下方式配置了:

...
spec:
containers:
- name: project-name
image: registry.docker.com/project/app:v1.0.0
args: ["--spring.profiles.active=prod"]
env:
- name: JAVA_OPTS
value: "-XX:PermSize=512M -XX:MaxPermSize=512M -Xms1024M -Xmx1024M..."
...

是不是很方便,这里可扩展做的东西还很多,根据项目需求来配置。

3、服务日志输出处理

对于日志处理,之前我们一般会使用 Log4jLogstash 等日志框架将日志输出到服务器指定目录,容器化部署后,日志会生成到容器内某个配置的目录上,外部是没法访问的,所以需要将容器内日志挂载到宿主机某个目录 (例如:/opt/logs 目录),这样方便直接查看,或者配置 FilebeatFluent 等工具抓取到 Elasticsearch 来提供日志查询分析。在 Deployment 配置日志挂载方式也很简单,配置如下:

...
volumeMounts:
- name: app-log
mountPath: /data/logs/serviceA #log4j 配置日志输出到指定目录
...
volumes:
- name: app-log
hostPath:
path: /opt/logs #宿主机指定目录

这里有个地方需要特别注意一下:服务日志要关闭 Console 输出,避免直接输出到控制台。默认 Docker 会记录控制台日志到宿主机指定目录,日志默认输出到 /var/lib/docker/containers/<container_id>/<container_id>-json.log,为了避免出现日志太多,占用磁盘空间,需要关闭 Console 输出并定期清理日志文件。

4、容器服务访问处理

4.1、配置容器服务暴露目标端口

首先需要提供容器服务需要暴露的目标端口号,例如 HttpHttpsGrpc 等服务端口,创建 Service 时需要指定匹配的容器端口号,Deployment 中配置容器暴露端口配置如下:

	ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
- containerPort: 18989
name: dubbo
protocol: TCP

4.2、服务对内对外访问方式选择

K8S Service 暴露服务类型有三种:ClusterIPNodePortLoadBalancer,三种类型分别有不同的应用场景。

  • 对内服务发现,可以使用 ClusterIP 方式对内暴露服务,因为存在 Service 重新创建 IP 会更改的情况,所以不建议直接使用分配的 ClusterIP 方式来内部访问,可以使用 K8S DNS 方式解析,DNS 命名规则为:<svc_name>.<namespace_name>.svc.cluster.local,按照该方式可以直接在集群内部访问对应服务。
  • 对外服务暴露,可以采用 NodePortLoadBalancer 方式对外暴露服务,NodePort 方式使用集群固定 IP,但是端口号是指定范围内随机选择的,每次更新 Service 该 Port 就会更改,不太方便,当然也可以指定固定的 NodePort,但是需要自己维护 Port 列表,也不方便。LoadBalancer 方式使用集群固定 IPNodePort,会额外申请申请一个负载均衡器来转发到对应服务,但是需要底层平台支撑。如果使用 AliyunGCE 等云平台商,可以使用该种方式,他们底层会提供 LoadBalancer 支持,直接使用非常方便。

以上方式或多或少都会存在一定的局限性,所以建议如果在公有云上运行,可以使用 LoadBalancerIngress 方式对外提供服务,私有云的话,可以使用 Ingress 通过域名解析来对外提供服务。Ingress 配置使用,可以参考 初试 Kubernetes 暴漏服务类型之 Nginx Ingress初试 Kubernetes 集群中使用 Traefik 反向代理 文章。

5、服务健康监测配置

K8s 提供存活探针和就绪探针,来实时检测服务的健康状态,如果健康检测失败,则会自动重启该 Pod 服务,检测方式支持 exechttpGettcpSocket 三种。对于 Spring Boot 后端 API 项目,建议采用 httpGet 检测接口的方式,服务提供特定的健康检测接口,如果服务正常则返回 200 状态码,一旦检测到非 200 则会触发自动重启机制。K8S 健康监测配置示例如下:

 livenessProbe: # 是否存活检测
failureThreshold: 3
httpGet:
path: /api/healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 300
periodSeconds: 60
successThreshold: 1
timeoutSeconds: 2
readinessProbe: # 是否就绪检测
failureThreshold: 1
httpGet:
path: /api/healthz
port: 8080
scheme: HTTP
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 2

其中一些参数的作用,这里就不解释了,可以参照 官网文档 来了解。

6、服务 CPU & Mem 请求/最大值配置

K8S 在部署 Deployment 时,可以为每个容器配置最小及最大 CPU & Mem 资源限制,这个是很有必要的,因为不配置资源限制的话,那么默认该容器服务可以无限制使用系统资源,这样如果服务异常阻塞或其他原因,导致占用系统资源过多而影响其他服务的运行,同时 K8S 集群资源不足时,会优先干掉那些没有配置资源限制的服务。当然,请求资源量和最大资源量要根据服务启动实际需要来配置,如果不清楚需要配置多少,可以先将服务部署到 K8S 集群中,看正常调用时监控页面显示的请求值,在合理配置。

resources:
limits:
cpu: "1000m"
memory: "1024Mi"
requests:
cpu: "500m"
memory: "512Mi"

7、K8S 集群部署其它注意事项

7.1、部署前的一些准备工作

K8S 在部署服务前,需要做一些准备工作,例如提前创建好对应的 Namespace,避免首次直接创建 Deployment 出现 Namespace 不存在而创建失败。如果我们使用的私有镜像仓库,那么还需要生成 Docker Repository 登录认证 Secret,用来注入到 Pod 内拉取镜像时认证需要。

# 包含登录认证信息的 Secret
apiVersion: v1
kind: Secret
metadata:
name: docker-regsecret
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: InN1bmRhbmRhbjE4MDUyOEBjcmVkaXRoYdfe3JhdXRocyI6eyJyZWdpc3RyeS1pb # Deployment 中注入该 Secret
imagePullSecrets:
- name: docker-regsecret

Secret 的生成方式可参考 官网文档

7.2、灵活使用 ConfigMap 资源类型

K8S 提供 ConfigMap 资源类型来方便灵活控制配置信息,我们可以将服务需要的一些 ENV 信息或者配置信息放到 ConfigMap 中,然后注入到 Pod 中即可使用,非常方便。ConfigMap 使用方式有很多种,这里建议大家可以将一些经常更改的配置放到 ConfigMap 中,例如我在实际操作中,就发现有的项目 nginx.conf 配置,还有配置的 ENV 环境变量信息经常变动,那么就可以放在 ConfigMap 中配置,这样 Deployment 就不需要重新部署了。

# 包含 nginx.conf 配置的 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf-configmap
data:
www.conf: |
server {
listen 80;
server_name 127.0.0.1 root /opt/project/nginx/html/;
error_page 405 =200 $uri; access_log /opt/project/nginx/logs/http_accesss.log main;
error_log /opt/project/nginx/logs/http_error.log;
} # 将 ConfigMap 挂载到容器指定目录
volumes:
- name: nginx-config
configMap:
defaultMode: 420
name: nginx-conf-configmap

这里有一个使用 ConfigMap 优雅加载 Spring Boot 配置文件实现方式的示例,可以参考 这里

7.3、Deployment 资源部署副本数及滚动更新策略

K8S 建议使用 Deployment 资源类型启动服务,使用 Deployment 可以很方便的进行滚动更新、扩缩容/比例扩容、回滚、以及查看更新版本历史记录等。所以建议副本数至少 2 个,保证服务的可用性,要根据服务实际访问量,来合理配置副本数,过多造成资源浪费,过少造成服务负荷高响应慢的问题,当然也可以根据服务访问量,灵活扩缩容副本数。

Deployment 更新策略有 RecreateRollingUpdate 两种,Recreate 方式在创建出新的 Pod 之前会先杀掉所有已存在的 Pod,这种方式不友好,会存在服务中断,中断的时间长短取决于新 Pod 的启动就绪时间。RollingUpdate 滚动更新方式,通过配合指定 maxUnavailablemaxSurge 参数来控制更新过程,使用该策略更新时会新启动 replicas 数量的 Pod,新 Pod 启动完毕后,在干掉旧 Pod,如果更新过程中,新 Pod 启动失败,旧 Pod 依旧可以提供服务,直到启动完成,服务才会切到新 Pod,保证服务不会中断,建议使用该策略。

replicas: 2
strategy:
rollingUpdate:
maxSurge: 1 #也可以按比例配置,例如:20%
maxUnavailable: 0 #也可以按比例配置,例如:20%
type: RollingUpdate

7.4、要保证 K8S 资源 CPU & Mem & Disk 资源够用

要时刻关注 K8S 集群资源使用情况,保证系统资源够集群使用,否则会出现因为 CPU 、Mem、Disk 不够用导致 Deployment 调度失败的情况。

7.5、K8S 集群配置项优化

K8S 集群创建每个 Namespaces 时默认会创建一个名称为 default 的 ServiceAccount,该 ServiceAccount 包含了名称为 default-token-xxxx 的 Secret,该 Secret 包含集群 api-server 使用的根 CA 证书以及认证用的令牌 Token,而且默认新创建 Pod 时会自动将该 ServiceAccount 包含的信息自动注入到 Pod 中,在 Pod 中可以直接使用这些认证信息连接集群执行 api 相关操作,这样会存在一定的风险,所以建议使用 automountServiceAccountToken: false 配置来关闭自动注入。

另一个配置 progressDeadlineSeconds,该配置用来指定在升级或部署时,由于各种原因导致卡住(还没有表明升级或部署失败),等待的 deadline 秒数,如果超过该 deadline 时间,那么将上报并标注 Deployment 状态为 False 并注明失败原因,然后 Deployment 继续执行后续操作。默认为 600 秒,如果觉得改时间太长,可以按照可接受的时间来修改配置,例如配置为 120 秒 progressDeadlineSeconds: 120

Spring Boot 项目转容器化 K8S 部署实用经验分享的更多相关文章

  1. spring boot项目如何测试,如何部署

    有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投产上线. 开发阶段 单元 ...

  2. Spring Boot项目微信云托管入门部署

    微信云托管本身是一个服务器,里面的软件都已经配置好了,直接使用即可,适用于一些简单部署的项目.直接把项目直接上传到服务器即可.无需各种繁琐的软件配置和打包,微信云托管统统给你搞定.而且系统会根据使用量 ...

  3. 【IntelliJ IDEA】spring boot项目在idea实现自动部署

    转载参考自:https://www.cnblogs.com/winner-0715/p/6666579.html spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能 ...

  4. Spring框架学习笔记(6)——阿里云服务器部署Spring Boot项目(jar包)

    最近接外包,需要部署服务器,便是参考了网上的几篇博文,成功在阿里云服务器成功部署了Spring Boot项目,特记下本篇笔记 Spring Boot项目打包 这里说一下部署的一些问题 1.mysql驱 ...

  5. spring boot系列01--快速构建spring boot项目

    最近的项目用spring boot 框架 借此学习了一下 这里做一下总结记录 非常便利的一个框架 它的优缺点我就不在这背书了 想了解的可以自行度娘谷歌 说一下要写什么吧 其实还真不是很清楚,只是想记录 ...

  6. 将spring boot项目部署到tomcat容器中

    一. 我这里用的环境 tomcat: tomcat 8 jdk: jdk 7 spring boot 版本: 1.5 二. 将创建好的spring boot项目做如下修改 2.1. 修改打包形式 在p ...

  7. 从零开始通过idea插件将一个spring boot项目部署到docker容器里运行

    实操:将一个spring boot项目部署到docker容器里运行 实验需要的环境: 腾讯云+Ubuntu 16.04 x64+idea+插件docker integration+daocloud 第 ...

  8. 多个Spring Boot项目部署在一个Tomcat容器无法启动

    转自https://www.cnblogs.com/tomxin7/p/9434085.html 业务介绍 最近用Spring Boot开发了一个翻译的小项目,但是服务器上还跑着其他项目,包括一个同样 ...

  9. Docker 部署Spring Boot 项目并连接mysql、redis容器(记录过程)

    Spring Boot 项目配置 将写好的Spring Boot 项目通过maven 进行package打包获得可执行Jar 再src/main/docker(放哪都行)下编写创建Dockerfile ...

随机推荐

  1. Template -「整体二分」

    写的简单.主要是留给自己做复习资料. 「BZOJ1901」Dynamic Rankings. 给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作: Q ...

  2. 【PostgreSQL 15】PostgreSQL 15对UNIQUE和NULL的改进

    用一句话来总结这种改进就是: 支持唯一性约束和索引将null值视为相同的值.之前是将null值索引成不同的值,现在可以通过使用unique nulls not distinct创建约束,将null值视 ...

  3. NOIP提高组模拟赛26

    A. LCIS 蓝书原题,CF10D 弱化版 首先直接把 LIS 和 LCS 合起来设计一个 DP . 设 \(dp_{i,j}\) 表示 \(A_{1\dots i}\) 和 \(B_{1\dots ...

  4. MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界

    MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...

  5. Python3.7+jieba(结巴分词)配合Wordcloud2.js来构造网站标签云(关键词集合)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_138 其实很早以前就想搞一套完备的标签云架构了,迫于没有时间(其实就是懒),一直就没有弄出来完整的代码,说到底标签对于网站来说还是 ...

  6. Taurus.MVC WebAPI 入门开发教程3:路由类型和路由映射。

    系列目录 1.Taurus.MVC WebAPI  入门开发教程1:框架下载环境配置与运行. 2.Taurus.MVC WebAPI 入门开发教程2:添加控制器输出Hello World. 3.Tau ...

  7. 技术分享 | Prometheus+Grafana监控MySQL浅析

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简介 Prometheus 一套开源的监控&报警&时间序列数据库的组合,通常 Kubernetes 中都会 ...

  8. 技术分析 | 通过DML语句浅谈binlog和redo log

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1 ...

  9. ceph 004 纠删码池 修改参数 cephx认证

    复习ceph003 存储池为逻辑概念,存储池可以占用整个集群的所有空间 [root@ceph01 ~]# ceph osd pool create pool1 pool 'pool1' created ...

  10. BZOJ3262/Luogu3810 陌上花开 (三维偏序,CDQ)

    一个下午的光阴之死,凶手是细节与手残. 致命的一枪:BIT存权值时: for(; x <= maxx; x += x&-x) t[x] += w; //for(; x <= n; ...