K8S实战基础篇:一文带你深入了解K8S实战部署SpringBoot项目
K8S实战基础篇:一文带你深入了解K8S实战部署SpringBoot项目
- 1.前言
- 2.简介
- 2.1.为什么写这篇文章
- 2.2.需求描述
- 2.3.需求分析
- 3. 部署实战
- 3.1 环境准备
- 3.2 image准备
- 3.3 部署2个实例
- 3.3.1 编写yaml文件
- 3.3.2 启动
- 3.3.3 引入Ingress
- 3.3.3.1 Ingress简介
- 3.3.3.2 Ingress 安装
- 3.3.3.3 Ingress 配置启动
- 3.3.3.4 验证
- 3.3.3.4 自动扩缩
- 4. 总结
1.前言
云原生可以说是当下互联网行业最火爆的概念和技术,云原生从字面意思上来看可以分成云和原生两个部分。
云是和本地相对的,传统的应用必须跑在本地服务器上,现在流行的应用都跑在云端,云包含了IaaS,、PaaS和SaaS。
原生就是土生土长的意思,我们在开始设计应用的时候就考虑到应用将来是运行云环境里面的,要充分利用云资源的优点,比如️云服务的弹性和分布式优势。
聊到云原生,避不开的就是容器技术,而docker作为最流行的容器技术,已经经过很多年的线上实战。今天我们不深入聊云原生,docker这些技术概念,今天我们聊一聊时下最火的容器编排技术:K8S-实战部署SpringBoot项目。
2.简介
2.1.为什么写这篇文章
前言中提到云原生、docker、K8S,我是18年第一次docker,也是在18年接触K8S,相对这门技术来说,我接触的时候已经有些晚了,因为在之后的面试中,已经感受到这些技术在大厂已经用的很成熟了,之前都在小公司,并不了解这些技术是什么,干什么用,加上国内这方面的资料又比较少,学起来是相当吃力。而到大厂之后,发现这些技术无处不在,并且基础设施建设已经很完备,一键部署云端的骚操作,让开发只需要关心业务而无需关心安装部署等繁琐的工作。祸兮福之所倚;福兮祸之所伏,大厂的技术设施完备的同时,另一方面也消弱了你去了解基础设施背后的技术原理能力。正是认识到这一点,今天才写这篇文章,为迷途中的孩子找到回家的路。废话不多,撸起袖子,干就完了!
这里没有任何马后炮套话,只有粗暴的干货。写大家看得懂、用得着、赚得到的文章是唯一宗旨!
2.2.需求描述
我有一个简单的Springboot项目,想部署在K8S集群中,能够实现扩缩容,负载均衡,同时我有一个互联网域名,我想把这个域名绑定在这个服务上,能够在有网络的地方访问。
2.3.需求分析
这个需求我想在很多刚开始接触docker,k8s等技术的老铁身上都会遇到过,真正实现起来,并不是那么容易,听我一一道来:
- image—Springboot项目一般是以jar包的形式跑在像centos等服务器上,运行
nohup java -jar xxx.jar &
命令就能启动起来。但是在k8s中,运行起来的的并不是jar,而是image
,因此我们需要把jar打包成image; - 自动扩缩—最基础的image有了,接下来就要考虑的是自动扩缩:顾名思义,比如说就是在服务访问量大的时候,我可以添加实例,来减少每个服务实例的压力,在访问量小的时候,我可以删除一部分实例,来实现资源的高效利用。
- 负载均衡—当我们的实例越来越多,我并不希望把所有的请求都落在一个实例上,如若不然,我们自动扩缩也就没了意义,传统方法我们可以用Nginx等实现负载均衡,待会来看看K8S能做些什么
- 域名绑定—这个就没什么好说的了。
3. 部署实战
3.1 环境准备
工欲善其事,必先利其器:
- Springboot jar包
- K8S集群环境
K8S集群环境部署我就不在这里展开讲了,我们准备一个最简单的Springboot项目,里面只有一个接口,访问localhost:8088
,返回服务器的hostname
,当整个部署工作完成之后,我们通过域名访问这个接口,返回的应该是不同的container
的hostname
,那我们的任务就完成了。
@GetMapping("/")public String sayHello() throws UnknownHostException {String hostname = "Unknown";InetAddress address = InetAddress.getLocalHost();hostname = address.getHostName();return hostname;}
3.2 image准备
我们都知道,所有image的生成都离不开Dockerfile
技术,我们有了一个jar包,要利用Dockerfile技术生成一个image。废话不多,上代码:
#使用jdk8作为基础镜像
FROM java:8
#指定作者
MAINTAINER ***
#暴漏容器的8088端口
#EXPOSE 8088
#将复制指定的docker-demo-0.0.1-SNAPSHOT.jar为容器中的job.jar,相当于拷贝到容器中取了个别名
ADD docker-demo-0.0.1-SNAPSHOT.jar /job.jar
#创建一个新的容器并在新的容器中运行命令
RUN bash -c 'touch /job.jar'
#设置时区
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#相当于在容器中用cmd命令执行jar包 指定外部配置文件
ENTRYPOINT ["java","-jar","/job.jar"]
Dockerfile文件里面有注释,具体的每一行代码什么意思我就不展开多讲了,这不是今天的重点,接下来,我们把docker-demo-0.0.1-SNAPSHOT.jar
,Dockerfile
文件放在同一个目录,上传到K8S的master 节点上,在目录内执行如下命令生成images
$ docker build .
我们可以看到生成image的过程,通过docker images
查看镜像
生成一个 docker-demo:latest的image镜像。
注意:我们部署的是集群,要想K8S集群中都能拉到这个镜像,那我们有以下两种方式:
- 方法一:我们把这个docker-demo:latest上传到远端仓库,这个仓库可以是我们自己的,或者是像我一样注册一个阿里云的账号,上传到阿里云自己的容器镜像服务仓库,如下图:
具体步骤
:
1.1 docker登陆阿里云容器镜像服务,需要输入密码
$ docker login --username=24k不怕(写自己的用户名) registry.cn-hangzhou.aliyuncs.com
1.2 在阿里云上创建命名空间:例:cuixhao-docker-demo
1.3 镜像打标签
$ docker tag docker-demo:latest registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
1.4 push到阿里云
$ docker push registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
1.5 删除掉docker-demo:latest
$ docker rmi docker-demo:latest
- 方法二 把刚才创建image的过程,在集群中每一台节点上都执行一遍,保证集群中每一台都有这个镜像。我采用的是二者的结合:先在master上把镜像生成,上传到阿里云,然后在另外的节点上,通过
docker pull registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
命令,从阿里云上拉到本地,然后在通过docer tag registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest docker-demo:latest
命令打标签,然后删掉拉取到的镜像:docker rmi registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
,
为什么这么做?因为我阿里云建的命名空间中的image都是私有,K8S拉取image的时候是需要集群中都配置ca证书的,如果设置为公开则不存在这个问题。所以我用docker-demo:latest这个镜像,直接用本地的,部署的时候不用再去阿里云拉取。
3.3 部署2个实例
3.3.1 编写yaml文件
基础镜像准备好了,那我们就开始部署吧。我们知道,k8s有deployment
,service
等概念,这里不详细讲,简单描述一下:deployment,管理pod集群,service
,管理pod中的服务。我们在master 节点编辑一个 ingress-docker-docker-deployment.yaml
文件
$ vi ingress-docker-docker-deployment.yaml
键入以下内容
apiVersion: apps/v1
kind: Deployment
metadata:name: ingress-docker-demo-deploymentlabels:app: ingress-docker-demo
spec:replicas: 2selector:matchLabels:app: ingress-docker-demotemplate:metadata:labels:app: ingress-docker-demospec:containers:- name: docker-demoimage: docker-demoimagePullPolicy: Neverports:- containerPort: 8088
---
apiVersion: v1
kind: Service
metadata:name: ingress-docker-demo-service
spec:ports:- port: 80protocol: TCPtargetPort: 8088selector:app: ingress-docker-demo
由yaml文件内容我们可以读出:我们创建了一个 名字我为ingress-docker-demo-deployment的deployment,里面有2个 docker-demo 的pod (replicas: 2
),和一个名字为ingress-docker-demo-service的service,管理名字为ingress-docker-demo的pod服务,目标端口8088,即我们的springboot服务所用端口,对外提供80端口。
3.3.2 启动
在master执行如下命令启动deployment 和service:
$ kubectl apply -f ingress-docker-docker-deployment.yaml
查看启动结果:
$ kubectl get pod -o wide
我们可以看到启动结果,两个container
分别在 worker01,worker02这两个节点上,可以看到,K8S集群给我们分配了两个IP:192.168.14.11
,192.168.221.73
。我们思考以下三个问题:
a. 在集群中,我们通过以上访问这两个服务,能访问通吗,是通过8088端口还是80端口?
我们不妨尝试一下,在集群中任何一个节点执行如下命令:
$ curl 192.168.14.11:8088
$ curl 192.168.221.73:8088
我们可以看到,接口返回了各自container的hostname,说明我们的服务是部署启动成功了,访问80端口是不通的,有兴趣的老铁可以试一下,因为80是我们对外的端口,所以用container ip是访问不通的。
b. 在集群内部访问,我们如何做到负载均衡?
有老铁可能会考虑一个问题,K8S集群中的pod有可能销毁或者重启,每次重启之后的ip不能保证一致,那以上访问方式肯定是不可采用的。想法很对,我们想访问一个固定的地址,不管pod如何重启,ip如何变化,我只访问这一个ip,这岂不美哉?那我们能不能做到呢?且看如下骚操作:
$ kubectl get svc
通过以上命令,我们找到了ingress-docker-docker-deployment.yaml
中定义的名字为 ingress-docker-demo-service
的service,它有一个 CLUSTER-IP
,PORT
为80,那我们根据K8S中的service的作用,做一个大胆的猜测:我们是不是可以固定的通过 10.103.19.71
(省略默认80端口)或者 10.103.19.71:80
来永久访问这两个服务呢?
$ curl 10.103.19.71
$ curl 10.103.19.71:80
答案是肯定的!,它给我们做了负载均衡!
c. 在集群外部我们如何访问这两个服务并且负载均衡?
集群内访问服务,负载均衡都已经做好了。有的老铁会问:集群外服想访问集群内的服务,该如何做呢?别急,还没完!
3.3.3 引入Ingress
3.3.3.1 Ingress简介
我们传统的集群负载均衡做法是在一台机器上安装Nginx,把我们的服务配置在Nginx上,外部直接访问你Nginx,它帮我们做负载,做限流,但是今天我们玩了K8S,就不能在用这种方法了,我们大胆的想一下,我们把所有的东西都在K8S做了,岂不美哉!想法很好,"好事者"已经替我们想到了,并且替我们做到了。
kubernetes ingress 文档
我来简单介绍一下:
如图:在K8S中,Ingress 提供 controller接口,由各个负载均衡厂家实现,传统Nginx是配置在nginx.conf 中,在K8S中,我们只需要配置Ingress 资源yaml就可以,听起来是不是方便多了,我们可以像管理deployment
,service
,pod
一样管理Ingress
3.3.3.2 Ingress 安装
我们使用 Nginx Ingress Controller
来一波骚操作:
编写 ingress-nginx.yaml
$ vi ingress-nginx.yaml
键入以下内容:
apiVersion: v1
kind: Namespace
metadata:name: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx---kind: ConfigMap
apiVersion: v1
metadata:name: nginx-configurationnamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx---
kind: ConfigMap
apiVersion: v1
metadata:name: tcp-servicesnamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx---
kind: ConfigMap
apiVersion: v1
metadata:name: udp-servicesnamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx---
apiVersion: v1
kind: ServiceAccount
metadata:name: nginx-ingress-serviceaccountnamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:name: nginx-ingress-clusterrolelabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx
rules:- apiGroups:- ""resources:- configmaps- endpoints- nodes- pods- secretsverbs:- list- watch- apiGroups:- ""resources:- nodesverbs:- get- apiGroups:- ""resources:- servicesverbs:- get- list- watch- apiGroups:- ""resources:- eventsverbs:- create- patch- apiGroups:- "extensions"- "networking.k8s.io"resources:- ingressesverbs:- get- list- watch- apiGroups:- "extensions"- "networking.k8s.io"resources:- ingresses/statusverbs:- update---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:name: nginx-ingress-rolenamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx
rules:- apiGroups:- ""resources:- configmaps- pods- secrets- namespacesverbs:- get- apiGroups:- ""resources:- configmapsresourceNames:# Defaults to "<election-id>-<ingress-class>"# Here: "<ingress-controller-leader>-<nginx>"# This has to be adapted if you change either parameter# when launching the nginx-ingress-controller.- "ingress-controller-leader-nginx"verbs:- get- update- apiGroups:- ""resources:- configmapsverbs:- create- apiGroups:- ""resources:- endpointsverbs:- get---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:name: nginx-ingress-role-nisa-bindingnamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: nginx-ingress-role
subjects:- kind: ServiceAccountname: nginx-ingress-serviceaccountnamespace: ingress-nginx---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:name: nginx-ingress-clusterrole-nisa-bindinglabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: nginx-ingress-clusterrole
subjects:- kind: ServiceAccountname: nginx-ingress-serviceaccountnamespace: ingress-nginx---apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-ingress-controllernamespace: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginx
spec:replicas: 1selector:matchLabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginxtemplate:metadata:labels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/part-of: ingress-nginxannotations:prometheus.io/port: "10254"prometheus.io/scrape: "true"spec:# wait up to five minutes for the drain of connectionsterminationGracePeriodSeconds: 300serviceAccountName: nginx-ingress-serviceaccounthostNetwork: truenodeSelector:name: ingresskubernetes.io/os: linuxcontainers:- name: nginx-ingress-controllerimage: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1args:- /nginx-ingress-controller- --configmap=$(POD_NAMESPACE)/nginx-configuration- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services- --udp-services-configmap=$(POD_NAMESPACE)/udp-services- --publish-service=$(POD_NAMESPACE)/ingress-nginx- --annotations-prefix=nginx.ingress.kubernetes.iosecurityContext:allowPrivilegeEscalation: truecapabilities:drop:- ALLadd:- NET_BIND_SERVICE# www-data -> 33runAsUser: 33env:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespaceports:- name: httpcontainerPort: 80- name: httpscontainerPort: 443livenessProbe:failureThreshold: 3httpGet:path: /healthzport: 10254scheme: HTTPinitialDelaySeconds: 10periodSeconds: 10successThreshold: 1timeoutSeconds: 10readinessProbe:failureThreshold: 3httpGet:path: /healthzport: 10254scheme: HTTPperiodSeconds: 10successThreshold: 1timeoutSeconds: 10lifecycle:preStop:exec:command:- /wait-shutdown---
这个文件并不是我胡编乱造自己写的,是"好事者"帮我们做好了,我只是稍作修改:设置网络模式为hostNetwork:true
,我希望我在集群中一台机器上开一个80端口,用这台机器作为负载均衡入口,因此:nodeSelector: name: ingress
。这是节点选择器配置参数,设置这个,ingress服务会在节点名字为ingress的机器上部署。
接下来我们在集群中的除master节点之外的一个机器上执行下个命令:给这台hostname为worker01-kubeadm-k8s的机器取个别名ingress
$ kubectl label node worker01-kubeadm-k8s name=ingress
接下来,我们在master节点执行安装ingress操作
$ kubectl apply -f ingress-nginx.yaml
安装过程有点儿慢,因为有个镜像比较难拉取:quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1
,建议执行 docker pull 先拉取到本地,上传到阿里云,然后在各个节点从阿里云拉取,然后在打tag的骚操作,都是有经验的程序员,你们知道我在说什么!
3.3.3.3 Ingress 配置启动
编写Ingress yaml资源:
$ vi nginx-ingress.yaml
键入以下内容:
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: nginx-ingress
spec:rules:- host: test.test.comhttp:paths:- path: /backend:serviceName: ingress-docker-demo-serviceservicePort: 80
文件定义了一种Ingress的资源,配置host为:test.test.com
,代理K8S中名字为ingress-docker-demo-service
的 Service, Service端口为80.看起来是不是和Nginx配置有点儿类似?
最后一步:启动
$ kubectl apply -f nginx-ingress.yaml
3.3.3.4 验证
因为test.test.com是不存在的域名,我们都是有经验的开发人员,很自然的想到去修改本地host : 添加 ip test.test.com
,ip为K8S节点中设置的别名为ingress的ip地址
浏览器访问:
完美!
3.3.3.4 自动扩缩
使用如下命令,可以然服务实现自动扩缩:
$ kubectl autoscale ingress-docker-docker-deployment.yaml --min=2 --max=5 --cpu-percent=80
还有很多自动扩缩的规则,老铁们自己探讨!
4. 总结
K8S实战还有很多玩法,我今天只是讲了最简单的服务部署,在不同的生产环境中,需求也是不一样的,比如说:一个简单的web应用,有mysql数据库,有redis,有springboot应用,都要在K8S中实践,这又是另一种部署方法,但万变不离其宗,核心都是要深入了解K8S网络
,只有网络打通了,各个组件才会畅通无阻的运行。有兴趣的老铁可以关注一波,一起Hello World!
下一篇:K8S实战进阶篇:一文带你深入了解K8S持久化存储解决方案
K8S实战基础篇:一文带你深入了解K8S实战部署SpringBoot项目相关推荐
- Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛
Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览. ...
- Nginx实战基础篇一 源码包编译安装部署web服务器
Nginx实战基础篇一 源码包编译安装部署web服务器 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览.表演.放映.广播或通 ...
- Centos7云服务器部署SpringBoot项目(手动配置环境篇)
文章目录 前言 一.部署Tomcat服务器 1.安装JDK1.8 2.安装与启动tomcat 配置安全组(8080端口) 二.安装JDK8 三.Mysql5.7安装 1.下载mysql 2.启动MyS ...
- SpringBoot2零基础到项目实战-基础篇
springboot2零基础到项目实战-基础篇 课程内容说明 课程单元 学习目标 基础篇 能够创建SpringBoot工程 基于SpringBoot实现ssm/ssmp整合 应用篇 能够掌握Sprin ...
- ASP.NET Google Maps Javascript API V3 实战基础篇一检测用户位置
ASP.NET Google Maps Javascript API V3 实战基础篇一检测用户位置 对于一些基本的东西,google maps JavaScript api v3 文档已经讲解得足够 ...
- Xamarin.Forms开发实战基础篇大学霸内部资料
Xamarin.Forms开发实战基础篇大学霸内部资料 介绍:本教程是国内第一本Xamarin.Forms开发专向教程.本教程针对Xamarin.Forms初学用户,全面细致的讲解Xmarin.For ...
- ASP.NET Google Maps Javascript API V3 实战基础篇一获取和设置事件处理程序中的属性...
ASP.NET Google Maps Javascript API V3 实战基础篇一获取和设置事件处理程序中的属性 <%@ Page Language="C#" Auto ...
- Lua与c++交互实战基础篇-夏曹俊-专题视频课程
Lua与c++交互实战基础篇-10018人已学习 课程介绍 本课程从实战角度讲解了流行的高性能脚本Lua与c++的联合开发,这套方案已经被大量的对性能由要求的系统使用,成为了高性能脚本 ...
- k8s集群部署springboot项目
一.前言 本篇,我们将基于k8s集群,模拟一个比较接近实际业务的使用场景,使用k8s集群部署一个springboot的项目,我们的需求是: 部署SpringBoot项目到阿里云服务器 : 基于容器打包 ...
最新文章
- IPSec ××× 在企业网中的应用
- 利用string 字符串拷贝
- poj 1860 Currency Exchange (最短路bellman_ford思想找正权环 最长路)
- ASP.NET CORE WEBAPI文件下载
- 【数据库原理及应用】经典题库附答案(14章全)——第九章:数据库安全性
- 什么是指利用计算机和现代,现代计算机一般指什么计算机?
- linux 多线程客户端服务端通信,[转载]多线程实现服务器和客户端、客户端和客户端通信;需要代码,留言...
- Prometheus is an open source monitoring
- 暴力破解之NTscan+密码字典工具
- 基于6U VPX架构的6槽标准VPX机箱
- android 手机 打印 图片,Mopria打印PDF、TXT文档或图片(适用于Android安卓系统)
- python中element什么意思_什么是Python中等效的’nth_element’函数?
- 黑帽SEO强势技术大纲
- 性能测试实战--计划测试(一)
- Python爬虫+颜值打分,5000+图片找到你的Mrs. Right
- 薛定谔 | 诱导契合对接(结合位点柔性)
- 优秀案例 | 长江鲲鹏中地数码:打造智慧城市“数字底座”
- vue 更改 element-ui 中 el-table 默认的暂无数据样式
- poj 3904 求四元互质集合
- windows系统开机自动进行NTP时间同步和系统时间修正
热门文章
- 【文献阅读】VAQF: Fully Automatic Software-Hardware Co-Design Framework for Low-Bit Vision Transformer
- Substance的置换效果
- 16MnDR是什么材料
- 基于SSM实现的求职招聘系统【附源码】(毕设)
- mysql数据库实验3查询_mysql数据库(3)-查询
- alexa是什么_Alexa的简要模式是什么?如何打开(或关闭)它?
- 365天挑战LeetCode1000题——Day 117 矩形区域不超过 K 的最大数值和
- 循环经济与可持续发展企业——章节测试1
- android swf webview,android webview播放swf文件
- Windows Store apps开发[8]处理Fullscreen, Snapped和Filled状态