Spring Boot 项目转容器化 K8S 部署实用经验分享
转载自: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 + Jdk
、Centos + Jdk + Tomcat
、Centos + 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、服务日志输出处理
对于日志处理,之前我们一般会使用 Log4j
或 Logstash
等日志框架将日志输出到服务器指定目录,容器化部署后,日志会生成到容器内某个配置的目录上,外部是没法访问的,所以需要将容器内日志挂载到宿主机某个目录 (例如:/opt/logs
目录),这样方便直接查看,或者配置 Filebeat
、Fluent
等工具抓取到 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、配置容器服务暴露目标端口
首先需要提供容器服务需要暴露的目标端口号,例如 Http
、Https
、Grpc
等服务端口,创建 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 暴露服务类型有三种:ClusterIP
、NodePort
、LoadBalancer
,三种类型分别有不同的应用场景。
- 对内服务发现,可以使用
ClusterIP
方式对内暴露服务,因为存在 Service 重新创建 IP 会更改的情况,所以不建议直接使用分配的ClusterIP
方式来内部访问,可以使用 K8S DNS 方式解析,DNS 命名规则为:<svc_name>.<namespace_name>.svc.cluster.local
,按照该方式可以直接在集群内部访问对应服务。 - 对外服务暴露,可以采用
NodePort
、LoadBalancer
方式对外暴露服务,NodePort
方式使用集群固定IP
,但是端口号是指定范围内随机选择的,每次更新 Service 该Port
就会更改,不太方便,当然也可以指定固定的NodePort
,但是需要自己维护Port
列表,也不方便。LoadBalancer
方式使用集群固定IP
和NodePort
,会额外申请申请一个负载均衡器来转发到对应服务,但是需要底层平台支撑。如果使用Aliyun
、GCE
等云平台商,可以使用该种方式,他们底层会提供LoadBalancer
支持,直接使用非常方便。
以上方式或多或少都会存在一定的局限性,所以建议如果在公有云上运行,可以使用 LoadBalancer
、 Ingress
方式对外提供服务,私有云的话,可以使用 Ingress
通过域名解析来对外提供服务。Ingress
配置使用,可以参考 初试 Kubernetes 暴漏服务类型之 Nginx Ingress 和 初试 Kubernetes 集群中使用 Traefik 反向代理 文章。
5、服务健康监测配置
K8s 提供存活探针和就绪探针,来实时检测服务的健康状态,如果健康检测失败,则会自动重启该 Pod 服务,检测方式支持 exec
、httpGet
、tcpSocket
三种。对于 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 更新策略有 Recreate
和 RollingUpdate
两种,Recreate
方式在创建出新的 Pod 之前会先杀掉所有已存在的 Pod,这种方式不友好,会存在服务中断,中断的时间长短取决于新 Pod 的启动就绪时间。RollingUpdate
滚动更新方式,通过配合指定 maxUnavailable
和 maxSurge
参数来控制更新过程,使用该策略更新时会新启动 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 部署实用经验分享的更多相关文章
- spring boot项目如何测试,如何部署
有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投产上线. 开发阶段 单元 ...
- Spring Boot项目微信云托管入门部署
微信云托管本身是一个服务器,里面的软件都已经配置好了,直接使用即可,适用于一些简单部署的项目.直接把项目直接上传到服务器即可.无需各种繁琐的软件配置和打包,微信云托管统统给你搞定.而且系统会根据使用量 ...
- 【IntelliJ IDEA】spring boot项目在idea实现自动部署
转载参考自:https://www.cnblogs.com/winner-0715/p/6666579.html spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能 ...
- Spring框架学习笔记(6)——阿里云服务器部署Spring Boot项目(jar包)
最近接外包,需要部署服务器,便是参考了网上的几篇博文,成功在阿里云服务器成功部署了Spring Boot项目,特记下本篇笔记 Spring Boot项目打包 这里说一下部署的一些问题 1.mysql驱 ...
- spring boot系列01--快速构建spring boot项目
最近的项目用spring boot 框架 借此学习了一下 这里做一下总结记录 非常便利的一个框架 它的优缺点我就不在这背书了 想了解的可以自行度娘谷歌 说一下要写什么吧 其实还真不是很清楚,只是想记录 ...
- 将spring boot项目部署到tomcat容器中
一. 我这里用的环境 tomcat: tomcat 8 jdk: jdk 7 spring boot 版本: 1.5 二. 将创建好的spring boot项目做如下修改 2.1. 修改打包形式 在p ...
- 从零开始通过idea插件将一个spring boot项目部署到docker容器里运行
实操:将一个spring boot项目部署到docker容器里运行 实验需要的环境: 腾讯云+Ubuntu 16.04 x64+idea+插件docker integration+daocloud 第 ...
- 多个Spring Boot项目部署在一个Tomcat容器无法启动
转自https://www.cnblogs.com/tomxin7/p/9434085.html 业务介绍 最近用Spring Boot开发了一个翻译的小项目,但是服务器上还跑着其他项目,包括一个同样 ...
- Docker 部署Spring Boot 项目并连接mysql、redis容器(记录过程)
Spring Boot 项目配置 将写好的Spring Boot 项目通过maven 进行package打包获得可执行Jar 再src/main/docker(放哪都行)下编写创建Dockerfile ...
随机推荐
- Template -「整体二分」
写的简单.主要是留给自己做复习资料. 「BZOJ1901」Dynamic Rankings. 给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作: Q ...
- 【PostgreSQL 15】PostgreSQL 15对UNIQUE和NULL的改进
用一句话来总结这种改进就是: 支持唯一性约束和索引将null值视为相同的值.之前是将null值索引成不同的值,现在可以通过使用unique nulls not distinct创建约束,将null值视 ...
- NOIP提高组模拟赛26
A. LCIS 蓝书原题,CF10D 弱化版 首先直接把 LIS 和 LCS 合起来设计一个 DP . 设 \(dp_{i,j}\) 表示 \(A_{1\dots i}\) 和 \(B_{1\dots ...
- MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界
MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...
- Python3.7+jieba(结巴分词)配合Wordcloud2.js来构造网站标签云(关键词集合)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_138 其实很早以前就想搞一套完备的标签云架构了,迫于没有时间(其实就是懒),一直就没有弄出来完整的代码,说到底标签对于网站来说还是 ...
- Taurus.MVC WebAPI 入门开发教程3:路由类型和路由映射。
系列目录 1.Taurus.MVC WebAPI 入门开发教程1:框架下载环境配置与运行. 2.Taurus.MVC WebAPI 入门开发教程2:添加控制器输出Hello World. 3.Tau ...
- 技术分享 | Prometheus+Grafana监控MySQL浅析
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简介 Prometheus 一套开源的监控&报警&时间序列数据库的组合,通常 Kubernetes 中都会 ...
- 技术分析 | 通过DML语句浅谈binlog和redo log
欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 1 ...
- ceph 004 纠删码池 修改参数 cephx认证
复习ceph003 存储池为逻辑概念,存储池可以占用整个集群的所有空间 [root@ceph01 ~]# ceph osd pool create pool1 pool 'pool1' created ...
- BZOJ3262/Luogu3810 陌上花开 (三维偏序,CDQ)
一个下午的光阴之死,凶手是细节与手残. 致命的一枪:BIT存权值时: for(; x <= maxx; x += x&-x) t[x] += w; //for(; x <= n; ...