实战kubenertes

原创
2020/05/23 19:54
阅读数 51




前面我们费尽心思研究了一番k8s的架构原理和核心概念,然后又千辛万苦搭建了k8s集群,现在是时候实战一下了,下面先简单描述下实战需求:


  创建一个SpringBoot Web业务应用,该应用基于Restful 对外提供一个API,该API获取当前所在容器的hostnameip,通过KV方式存到redis,下次直接通过hostname从缓存取出ip,如果redis不可用,则重新获取容器的ip返回。


了解需求后再正式开始使用k8s前,先用一段话来描述使用k8s的习惯和思路:


我们的实际业务应用程序运行在容器中,容器运行在Pod中,Pod运行在虚拟机或物理机中,Pod提供一个容器运行的环境,同一个Pod可以运行单个或多个容器实例,同一个Pod容器实例之间共享资源(端口、VolumeCgroupIP等),不同Pod之间资源是隔离的。同时Pod也是k8s主要的管理目标,通常的管理方式是通过为每个Pod打上各种KV标签(lables),然后可以创建各种控制器或其它约束规则对象(比如副本数ReplicaSet、负载均衡Service)通过标签选择器(selector.matchLabels)来匹配Pod的标签,对匹配到的Pod进行控制管理,使用过程中这些都是通过yamljson配置文件的方式来描述的。通常我们很少单独编写定义Podyaml,而是通过定义控制器Deployment对象的时在templagte部分去同时定义Pod


下图是Pod逻辑描述图:



学习完本文之后,你可以掌握以下技能:


1、重温搭建docker私服,并设置私服账号和密码技能。

2、重温docker镜像Dockerfile的编写。

3、掌握k8s命名空间的创建以及为命名空间和命名空间的Pod容器设定内存、CPU资源限制。

4、掌握k8s控制器对象Deployment的配置文件定义和部署。

5、掌握k8s基于CPU使用率自动扩容技能。

6、掌握k8s手动扩容技能。

7、掌握k8s滚动更新技能。

8、掌握k8sReplicaSetPodDeployment的运维命令技能。

9、掌握k8s集成docker镜像私服技能。

10、理解k8s网络知识。

11、掌握k8s实现负载均衡的技能。

12、理解k8s服务注册和发现。

13、理解k8s数据卷Volume的简单使用。

14、掌握如何将应用运行在指定的node上。

15、初步理解无状态和有状态概念。


本文基本涵盖了使用k8s普遍常用的核心技能,值得你认真去阅读,下面我们开始吧!

 

创建SpringBoot Web应用

项目关键包结构如下:


pom.xml代码如下:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.3.0.RELEASE</version>    <relativePath/> <!-- lookup parent from repository -->  </parent>  <groupId>com.lazy.demo</groupId>  <artifactId>demo-k8s</artifactId>  <version>0.0.1-SNAPSHOT</version>  <name>demo-k8s</name>  <description>Demo project for Spring Boot</description>
<properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<build> <finalName>demo-k8s</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>



application.properties配置文件如下:

##端口号server.port=8888# servlet context pathserver.servlet.context-path=/v1/demo-k8s# tomcat accesslog configserver.tomcat.accesslog.enabled=trueserver.tomcat.accesslog.rotate=trueserver.tomcat.accesslog.suffix=.logserver.tomcat.accesslog.prefix=access_logserver.tomcat.accesslog.file-date-format=.yyyy-MM-ddserver.tomcat.accesslog.directory=logs  # Redis数据库索引(默认为0)spring.redis.database=0# Redis服务器地址spring.redis.host=redis-host# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=#连接池最大连接数(使用负值表示没有限制)spring.redis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.pool.max-wait=-1# 连接池中的最大空闲连接spring.redis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=300


 

HelloController控制器代码如下:

package com.lazy.demo.controller;
import java.net.InetAddress;import java.net.UnknownHostException;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import com.lazy.demo.utils.RedisUtil;
@RestController@RequestMapping("/hello")public class HelloController { private final static Logger LOG = LoggerFactory.getLogger(HelloController.class);
@GetMapping("/hostname") public String hostname() { InetAddress ia = null; try { ia = InetAddress.getLocalHost(); String hostname = ia.getHostName(); long time = System.currentTimeMillis(); //尝试操作redis try { Object cacheIp = RedisUtil.getSelf().get(hostname); if (cacheIp == null) { String ip = ia.getHostAddress(); RedisUtil.getSelf().set(hostname, ip); cacheIp = RedisUtil.getSelf().get(hostname); } //fromcache_ 标识从缓存获取 String hostnameIp = "fromcache_" + cacheIp + "_" + time; return hostnameIp; } catch(Exception e) { e.printStackTrace(); LOG.error("redis opt err ", e); } // redis 不可用,则直接查询ip String ip = ia.getHostAddress(); String hostnameIp = ip + "_" + time; return hostnameIp; } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); LOG.error("sys err ", e); return "sys err " + (e.getMessage() == null ? "" : e.getMessage()); } } }


其它几个关于redis配置和工具类不再详细给出,读者可以自行实现或查阅网络资料。


编写Dockerfile配置文件如下:

FROM adoptopenjdk/openjdk8 MAINTAINER lazy RUN mkdir -p /opt/apps COPY demo-k8s.jar /opt/apps EXPOSE 8888 CMD ["java", "-jar", "/opt/apps/demo-k8s.jar"]

 

构建docker镜像:

1、通过下面命令在node4节点上创建目录: mkdir -p /opt/docker/build/demo-k8s2、将Dockerfile、demo-k8s.jar文件上传到/opt/docker/build/demo-k8s目录下。 3、执行下面命令构建docker镜像: cd /opt/docker/build/demo-k8sdocker build -t demo-k8s:v1.0.0 -f Dockerfile . 4、测试镜像是否可正常运行: docker run -it --rm --name demo-k8s -p 8888:8888 demo-k8s:v1.0.0 5、通过浏览器访问以下地址测试是否正常: http://10.68.212.104:8888/v1/demo-k8s/hello/hostname


 

创建k8s命名空间(非必须)

 

1、创建k8s命名空间(如果当前团队有自己的命名空间则使用当前的,不必重新创建): kubectl create namespace my-namespacekubectl get namespace 2、在node4节点上执行下面命令创建目录: mkdir -p /opt/k8s-mgr/my-namespace 3、配置命名空间的Pod容器资源限制、命名空间资源配额: cd /opt/k8s-mgr/my-namespacevi resource-limit.yaml,内容如下(---表示分隔符): # 配置Pod容器内存请求和上限限制apiVersion: v1kind: LimitRangemetadata:  name: mem-limit-rangespec:  limits:  - default:      memory: 512Mi    defaultRequest:      memory: 256Mi    type: Container --- # 配置Pod容器CPU请求和上限限制apiVersion: v1kind: LimitRangemetadata:  name: cpu-limit-rangespec:  limits:  - default:      cpu: 1    defaultRequest:      cpu: 0.5    type: Container --- # 配置整个命名空间内存和CPU请求和上限配额apiVersion: v1kind: ResourceQuotametadata:  name: mem-cpu-demospec:  hard:    requests.cpu: "2"    requests.memory: 2Gi    limits.cpu: "4"    limits.memory: 4Gi 4、执行下面命令创建资源配额控制: # 执行kubectl apply -f resource-limit.yaml --namespace=my-namespace# 查看命名空间资源配额配置kubectl get resourcequota mem-limt-range --output=yaml --namespace=my-namespace# 查看Pod容器内存请求和上限限制kubectl get limitrange mem-limit-range --output=yaml --namespace=my-namespace# 查看Pod容器CPU请求和上限限制kubectl get limitrange cpu-limit-range --output=yaml --namespace=my-namespace 后续如果需要修改资源限制或配额只需要修改配置文件,再次执行下面命令即可实: kubectl apply -f resource-limit.yaml --namespace=my-namespace


部署Web应用


Deployment对象是部署无状态应用程序最佳的选择,也是使用最多的一个对象,下面我们就通过Deployment控制对象来部署Web我们的应用。


1、简单搭建一个本地docker镜像私服,在node4节点上执行下面命令:

 

# 创建一个目录mkdir -p /opt/docker/registry/auth# 通过docker registry htpasswd工具创建私服账号和密码docker run --rm \--entrypoint htpasswd  \registry:2 -Bbn lazy 123456 > /opt/docker/registry/auth/htpasswd# 启动私服docker run -d --name registry  \--restart=always \-v /opt/docker/registry/auth:/auth  \-v /var/docker/registry:/var/lib/registry \-e "REGISTRY_AUTH=htpasswd"  \-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"  \-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd  \-p 5000:5000 registry:2 # 测试私服,可能提示权限不足UNAUTHORIZED,表示成功curl http://node4:5000/v2/_catalog


 

2、所有节点/etc/docker/daemon.json配置文件加入如下配置(不然会后面会报错:http: server gave HTTP response to HTTPS client):

 

"insecure-registries": ["node4:5000"]# 刷新配置并重启dockersystemctl daemon-reloadsystemctl restart docker

 

3、登录docker

 

docker login node4:5000# 输入前面设置的账号lazy和密码123456。


4、将镜像重新tag为私服仓库地址,并推送到本地私服:

 

docker tag demo-k8s:v1.0.0 node4:5000/demo-k8s:v1.0.0docker push node4:5000/demo-k8s:v1.0.0


5、配置k8s访问私服权限:


# docker login后才会创建该文件cat ~/.docker/config.json# 通过创建k8s Secret对象的方式将docker私服登录凭证复制到k8skubectl create secret generic regcred     \--from-file=.dockerconfigjson=/root/.docker/config.json     \--type=kubernetes.io/dockerconfigjson \--namespace=my-namespace


# 查看创建secretkubectl get secret regcred --namespace=my-namespace \--output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode


6、编写创建Deployment对象yaml配置文件(demo-k8s-deployment.yaml):

 

cd /opt/k8s-mgr/my-namespace

vi demo-k8s-deployment.yaml,文件内容如下:


# 指定Kubenertes API对象版本为v1.10.xapiVersion: apps/v1# 指定创建对象类型为Deploymentkind: Deployment# 定义Deployment对象元数据metadata:  # 名称  name: demo-k8s-deployment  # 标签  labels:    app: demo-k8s# 定义Deployment对象期望状态spec:  # 通过创建ReplicaSet对象来控制Pod副本数量为2  replicas: 2  # 定义Deployment对象的选择器,匹配标签为app=demo-k8s的Pod  selector:    matchLabels:      app: demo-k8s  # spec.template部分开始定义Pod  template:    # 定义Pod元数据    metadata:      # 定义Pod标签为app-demo-k8s      labels:        app: demo-k8s    # 定义Pod对象期望状态    spec:      # 容器信息定义      containers:        # 容器实例名称      - name: demo-k8s        # 容器实例镜像        image: node4:5000/demo-k8s:v1.0.0        ports:          # 容器运行端口        - containerPort: 8888      # 前面创建的私服令牌Secret对象名称      imagePullSecrets:      # 这里的name对应前面创建的Secret对象的名称      - name: regcred


7、通过下面命令创建并运行Deployment对象:

 

kubectl apply -f demo-k8s-deployment.yaml --namespace my-namespace


8、通过下面命令查看Deployment以及相关对象的列表信息:

 

# 查询my-namespace命名空间Deployment对象列表信息kubectl get deployments --namespace my-namespace --show-labels# 查询my-namespace命名空间ReplicaSet对象列表信息kubectl get rs --namespace my-namespace --show-labels# 查询my-namespace命名空间Pods对象列表信息kubectl get pods --namespace my-namespace --show-labels -o wide



从列表可以看到所有相关对象正常运行(Runnning)且可用(AVAILABLE,到这一步,已经通过k8s将我们的应用部署起来了,有个细节需要说明,我们通过Deployment对象的yaml配置定义了replicas: 2,那么Deployment就会通过创建ReplicaSet对象来控制副本的管理,从前面的kubectl get rs命令就可以证实这一点,而sepc.template部分是Pod的定义。

 

下面继续介绍DeploymentReplicaSetPod的运维命令。

 

9、通过下面命令查看对象详细信息:

 

# kubectl describe xxx命令对排除错误很有用,必须要会用# 通过前面查询得到的deployments列表NAME值查询对象的详情kubectl describe deployment demo-k8s-deployment --namespace my-namespace# 通过前面查询得到的rs列表NAME值查询对象的详情kubectl describe rs demo-k8s-deployment-6c6bc78cb5 --namespace my-namespace# 通过前面查询得到的pods列表NAME值查询对象的详情kubectl describe pods demo-k8s-deployment-6c6bc78cb5-6kzfw --namespace my-namespacekubectl describe pods demo-k8s-deployment-6c6bc78cb5-c6wh2 --namespace my-namespace


10、删除Deployment

 

kubectl delete deployment demo-k8s-deployment --namespace my-namespace


11、滚动更新Deployment

 

# 滚动更新镜像版本kubectl --record deployment.apps/demo-k8s-deployment \set image deployment.v1.apps/demo-k8s-deployment \demo-k8s=node4:5000/demo-k8s:v2.0.0 \--namespace my-namespace # 查看滚动更新状态 successfully rolled out表示成功kubectl rollout status deployment.v1.apps/demo-k8s-deployment --namespace my-namespace# 查看滚动更新历史kubectl rollout history deployment.v1.apps/demo-k8s-deployment --namespace my-namespace# 回滚上次的滚动更新kubectl rollout undo deployment.v1.apps/demo-k8s-deployment --namespace my-namespace


12、缩放Deployment副本数:

 

# 手动将副本数修改为1个kubectl scale deployment.v1.apps/demo-k8s-deployment \--replicas=1 --namespace my-namespace# 配置为基于CPU使用率百分比(超过80%)来自动缩放副本数kubectl autoscale deployment.v1.apps/demo-k8s-deployment \--min=2 --max=5 --cpu-percent=80 --namespace my-namespace


13、暂停和恢复Deployment:

 

# 暂停,暂停后可以做一些滚动更新动作,然后确保更新成功后再恢复kubectl rollout pause deployment.v1.apps/demo-k8s-deployment --namespace my-namespace# 例如更新容器资源限制(这里只是示例,本实战没有配置limits参数,这行命令会报错)kubectl set resources deployment.v1.apps/demo-k8s-deployment \-c=demo-k8s -limits=cpu=200m,memory=512Mi --namespace my-namespace# 可以观察滚动更新是否完成kubectl get rs --namespace my-namespace -w# 更新后再恢复kubectl rollout resume deployment.v1.apps/demo-k8s-deployment --namespace my-namespace


14、将应用部署到指定的节点上:

 

# 查看节点列表信息,展示标签kubectl get nodes  --show-labels --namespace my-namespace# 给node2打上一个标签demo-k8s-app=truekubectl label nodes node2 demo-k8s-app=true --namespace my-namespace


# 然后在yaml配置文件通过nodeSelector指定demo-k8s-app=true

apiVersion: apps/v1kind: Deploymentmetadata:  name: demo-k8s-deployment  labels:    app: demo-k8sspec:  replicas: 2  selector:    matchLabels:      app: demo-k8s  template:    spec:      nodeSelector:      - demo-k8s-app: true


15、查看Pod中某个容器的stdoutstderr日志

 

kubectl get pods --namespace my-namespacekubectl --namespace my-namespace \# 指定容器名称-c demo-k8s \# 类似tail -f-f \# get pods查询结果得到logs demo-k8s-deployment-67469f5694-bf8zs


16、进入Pod:

 

kubectl --namespace my-namespace exec -it demo-k8s-deployment-67469f5694-bf8zs /bin/sh


k8s网络


网络是k8s集群的核心部分,k8s网络部分通过插件的形式对外提供接口,目前支持k8s的网络插件有好几种,下面是常见的k8s网络插件:


  • Calico

  • Flannel

  • ACI

  • Antrea

  • AOS from Apstra

  • AWS VPC CNI for Kubernetes

  • Azure CNI for Kubenrnetes

  • Google Compute Engine(GCE)

  • Cilium

  • Contiv-VPP

  • Kube-router

  • Weave Net

 

在前面我们按照k8s集群时,我们选择并安装了Calico网络插件,可以通过下面命令看到Calico相关的Pod

 

kubectl get pods --namespace kube-system | grep calico


k8s集群网络原则如下:

 

1、每个Pod都有它自己的IP地址,同一个Pod内的容器之间可以通过localhost来互相访问。

 

2、同一个k8s集群的Pod之间可以直接通过Pod IP进行访问,通过下面命令可以查到指定命名空间PodIP信息:

 

kubectl get pods --namespace my-namespacekubectl describe pod demo-k8s-deployment-67469f5694-bf8zs --namespace my-namespace | grep IP


3、Pod IP默认情况下对外部是不可用的,可以通过部署标签选择器类型的Service对象将单个或一组Pod以负载均衡的方式对外提供访问(Service内部通过创建Endpoint对象来表示后端Pod负载地址列表)。标签选择器类型Service对象创建后由kube-proxy进程默认根据配置(service-cluster-ip-range)的网段范围分配一个ClusterIPService对象,同时由kube-proxy进程安装该Service对象的iptablesiptables代理模式)规则来将该Service对象的clusterIP和端口流量转发到后端负载的一组Pod IP上,kube-proxy默认使用随机负载均衡算法(如果要使用其它算法可以考虑使用IPVS的模式,该模式自行研究这里不做详细介绍),iptables代理模式如下图:



4、其它的Pod可以直接通过ServiceName.NameSpace的格式(例如demo-k8s-service.my-namespace)访问另外一组Service负载均衡服务,内部由CoreDNS负责解析DNS的工作。

 

5、注意,默认情况下,Service对象创建后ClusterIP只能在k8s集群内部访问,如果需要对外暴露,则需要将spec.type配置为非默认值(ClusterIPNodePort或其它。

 

实现负载均衡(外部访问Pod


1、验证Pod IP访问范围:

 

在同一个k8s集群节点上可以直接访问PodIP,我们通过下面命令尝试访问我们前面部署的应用:

 

# 查询节点列表信息kubectl get pods --namespace my-namespace# 查看节点描述信息中的IP信息kubectl describe pod demo-k8s-deployment-67469f5694-bf8zs --namespace my-namespace | grep IP# 在集群节点中访问应用curl http://192.168.104.11:8888/v1/demo-k8s/hello/hostname


可以看到能够正常响应:



2、配置Service对象负载Pod,并对外暴露该负载服务:

 

但是我们离开集群,直接在同一个节点局域网内的其它节点上则无法访问,此时可以通过创建Service对象来使应用对外部暴露服务,同时Service对象还可以实现一组Pod的负载均衡,这些都是通过yaml配置文件来实现的,具体配置如下:

 

# 指定Kubenertes API对象版本为v1.10.xapiVersion: v1# 指定创建对象类型为Servicekind: Service# 定义对象元数据metadata:  # 名称,集群内部DNS域名  name: demo-k8s-servicespec:  # 指定type为NodePort,使Service服务对外暴露  type: NodePort  selector:    # 负载匹配带有标签app=demo-k8s的Pod    app: demo-k8s  ports:      # 指定协议为TCP    - protocol: TCP      # 指定节点暴露的对外代理端口,默认30000-32767范围随机      # iptables将节点该端口的流量转发给clusterip:port(下面指定的)上      nodePort: 30000      # 指定clusterIp的端口      port: 8888      # 指定负载后端Pod的端口      targetPort: 8888


3、部署Service对象:

 

kubectl -n my-namespace apply -f demo-k8s-service.yamlkubectl -n my-namespace get service


查询结果如下,表示成功创建Ser'vice对象,对外暴露端口为30000,限制



4、在集群外的其他局域网节点测试访问:

 

curl http://10.68.212.104:30000/v1/demo-k8s/hello/hostname #或浏览器直接访问 http://10.68.212.104:30000/v1/demo-k8s/hello/hostname


可以看到能够正常访问,且不断刷新IP会发生变化(浏览器有缓存,curl可以看出变化):



 

部署redis缓存(Pod访问Service


前面部署的SpringBoot Web应用控制器代码中会尝试访问redis服务,且在application.properties的配置文件中配置redishostredis-host,关键代码如下:

 

// HelloController.java控制器代码片段 Object cacheIp = RedisUtil.getSelf().get(hostname);if (cacheIp == null) {String ip = ia.getHostAddress();RedisUtil.getSelf().set(hostname, ip);cacheIp = RedisUtil.getSelf().get(hostname);} // application.properties的配置文件片段 # Redis服务器地址spring.redis.host=redis-host# Redis服务器连接端口spring.redis.port=6379


我们可以通过将redis部署为k8s管理的Pod,同时创建一个NDS名称为redis-hostService对象,具体部署如下:

 

1、构建docker镜像,推送到私服node4:5000上:

 

docker pull redisdocker tag redis node4:5000/redisdocker push node4:5000/redis


2、编写部署redisredis-server-deployment.yaml配置文件,内容如下:

 

cd /opt/k8s-mgr/my-namespace

vi redis-server-deployment.yaml,内容如下:

 

# 指定Kubenertes API对象版本为v1.10.xapiVersion: apps/v1# 指定创建对象类型为Deploymentkind: Deployment# 定义Deployment对象元数据metadata:  # 名称  name: redis-server-deployment  # 标签  labels:    app: redis-server# 定义Deployment对象期望状态spec:  # 定义Deployment对象的选择器,匹配标签为app=redis-server的Pod  selector:    matchLabels:      app: redis-server  # 通过创建ReplicaSet对象来控制Pod副本数量为1  replicas: 1  # spec.template部分开始定义Pod  template:    # 定义Pod元数据    metadata:      # 定义Pod标签为redis-server      labels:        app: redis-server    # 定义Pod对象期望状态    spec:      # 容器信息定义      containers:        # 容器实例名称      - name: redis        # 容器实例镜像        image: node4:5000/redis        ports:          # 容器运行端口        - containerPort: 6379      # 前面创建的私服令牌Secret对象名称      imagePullSecrets:      # 这里的name对应前面创建的Secret对象的名称      - name: regcred


3、部署Deployment对象:

 

kubectl --namespace my-namespace apply -f redis-server-deployment.yaml# 查看部署情况kubectl --namespace my-namespace get deployments# 如果失败,可以通过下面命名查到原因kubectl --namespace my-namespace describe deployment redis-server-deploymentkubectl --namespace my-namespace get rskubectl --namespace my-namespace describe rs xxxxxkubectl --namespace my-namespace get podskubectl --namespace my-namespace describe pod xxxxx


4、编写部署redisredis-server-service.yaml配置文件,内容如下:

 

# 指定Kubenertes API对象版本为v1.10.xapiVersion: v1# 指定创建对象类型为Servicekind: Service# 定义对象元数据metadata:  # 名称,集群内部DNS域名,匹配application.properties的spring.redis.host配置  name: redis-hostspec:  selector:    # 负载匹配带有标签app=redis-server的Pod    app: redis-server  ports:      # 指定协议为TCP    - protocol: TCP      # 指定clusterIp的端口      port: 6379      # 指定负载后端Pod的端口      targetPort: 6379


5、部署Service对象:

 

kubectl --namespace my-namespace apply -f redis-server-service.yaml


5、稍等片刻,确认全部启动成功后,最后可以通过curl或浏览器访问下面的地址确认redis是否部署成功且能正常被应用通过DNS访问到:



fromcache前缀表示从缓存拿到的数据,证明redis部署成功且正常被应用使用。

 

k8s数据卷Volume


在容器化部署运维架构下,由于容器实例的文件系统生命周期和容器实例相同,容器实例退出那么该容器实例的文件系统内保存的所有数据都将被销毁,下次重新创建并运行新的容器实例时会重新创建一个新的干净的文件系统,这对于部署有状态的服务组件时(例如:MysqlRedis等)变得不可行,Volume的出现就是为了解决诸如类似这种情况的存储的问题,为容器化部署提供了存储方面的解决方案,docker为容器实例自身提供了一套volumes解决方案,而k8sPod也提供了一套volumes解决方案,k8s支持的volume类型如下:

 

  • awsElasticBlockStore

  • azureDisk

  • azureFile

  • cephfs

  • cinder

  • configMap

  • csi

  • downwardAPI

  • emptyDir

  • fc (fibre channel)

  • flexVolume

  • flocker

  • gcePersistentDisk

  • gitRepo (deprecated)

  • glusterfs

  • hostPath

  • iscsi

  • local

  • nfs

  • persistentVolumeClaim

  • projected

  • portworxVolume

  • quobyte

  • rbd

  • scaleIO

  • secret

  • storageos

  • vsphereVolume

 

下面我们简单演示下通过hostPathvolume类型将容器应用日志目录映射到宿主机的某个目录上,配置方式如下:

 

1、备份demo-k8s-deployment.yaml配置:


cp demo-k8s-deployment.yaml demo-k8s-deployment-v1.yaml


2、修改demo-k8s-deployment.yaml配置如下:

 

# 指定Kubenertes API对象版本为v1.10.xapiVersion: apps/v1# 指定创建对象类型为Deploymentkind: Deployment# 定义Deployment对象元数据metadata:  # 名称  name: demo-k8s-deployment  # 标签  labels:    app: demo-k8s# 定义Deployment对象期望状态spec:  # 通过创建ReplicaSet对象来控制Pod副本数量为2  replicas: 2  # 定义Deployment对象的选择器,匹配标签为app=demo-k8s的Pod  selector:    matchLabels:      app: demo-k8s  # spec.template部分开始定义Pod  template:    # 定义Pod元数据    metadata:      # 定义Pod标签为app-demo-k8s      labels:        app: demo-k8s    # 定义Pod对象期望状态    spec:      # 容器信息定义      containers:        # 容器实例名称      - name: demo-k8s        # 容器实例镜像        image: node4:5000/demo-k8s:v3.0.0        ports:          # 容器运行端口        - containerPort: 8888        volumeMounts:        - mountPath: /opt/apps/demo-k8s/logs          name: demo-k8s-log-volume      volumes:      - name: demo-k8s-log-volume        hostPath:          path: /logs/demo-k8s          type: DirectoryOrCreate      # 前面创建的私服令牌Secret对象名称      imagePullSecrets:      # 这里的name对应前面创建的Secret对象的名称      - name: regcred


3、重新部署demo-k8s-deployment.yaml


kubectl --namespace my-namespace apply -f demo-k8s-deployment.yaml


4、切换到node2node3上,现在可以直接在宿主机上查看日志了。

 

tail -f /logs/demo-k8s/log_info.log


5、此时我们停掉所有Pod,日志文件仍然存在宿主机上,这样我们可以配套通过logstash + es的架构对每一台宿主机的日志进行收集,但是k8s自身提供了日志收集的解决方案,不需要专门将日志映射到宿主机的方式提供给logstash,关于k8s的日志架构后面再编写相关文章讲解。

 

接下来我们会讲解部署有状态应用的知识,到时会详细介绍persistentVolumeClaim持久化共享数据volume类型的使用。


部署有状态应用


k8s中将部署的应用类型根据是否需要持久化数据将之分为两类:

 

有状态:需要共享持久化数据的应用,例如MysqlRedisMongoDBES等。

无状态:不需要共享持久化数据的应用,例如会话数据进行分布式缓存或允许丢失的普通的Web应用。

 

1、可见有状态类型应用跟持久化数据特点绑在一起,一般通过Deployment + Service对象组合的方式来部署无状态应用,而有状态应用通常使用StatefulSet + Service对象组合的方式来部署,前面我们创建的SpringBoot Web应用和Redis服务都是通过Deployment + Service对象组合的方式部署为无状态应用,实际上前面部署的Redis服务存在数据丢失的风险,当容器重启后Redis的数据将丢失,每次都需要重建缓存,这对高并发的应用来说,可能会导致应用瞬间崩溃,关于通过StatefulSet + Service对象组合的方式重新将Redis应用部署为有状态类型应用的文章后面再重新写一篇来详细介绍。



---------------------- 正文结束 ------------------------


长按扫码关注微信公众号


Java软件编程之家


本文分享自微信公众号 - Java软件编程之家(gh_b3a87885f8f5)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部