容器化技术与微服务结合---实操service并部署一个简单对外开放的springboot HelloWord服务(四)

系列

容器化技术与微服务结合—docker(一)
容器化技术与微服务结合—Kubernetes基本介绍(二)
容器化技术与微服务结合—Pod详解(三)
容器化技术与微服务结合—实操service并部署一个简单对外开放的springboot HelloWord服务(四)
容器化技术与微服务结合—结合springcloud微服务框架进行部署(含切换成阿里云docker仓库)(五)
容器化技术与微服务结合—SpringCloud框架与阿里云serverless k8s的结合(六)

Service理解

内容来自于官网: https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/

事实上,Pod(容器组)有自己的 生命周期。当 worker node(节点)故障时,节点上运行的 Pod(容器组)也会消失。然后,Deployment 可以通过创建新的 Pod(容器组)来动态地将群集调整回原来的状态,以使应用程序保持运行。

举个例子,假设有一个图像处理后端程序,具有 3 个运行时副本。这 3 个副本是可以替换的(无状态应用),即使 Pod(容器组)消失并被重新创建,或者副本数由 3 增加到 5,前端系统也无需关注后端副本的变化。由于 Kubernetes 集群中每个 Pod(容器组)都有一个唯一的 IP 地址(即使是同一个 Node 上的不同 Pod),我们需要一种机制,为前端系统屏蔽后端系统的 Pod(容器组)在销毁、创建过程中所带来的 IP 地址的变化。

Kubernetes 中的 Service(服务) 提供了这样的一个抽象层,它选择具备某些特征的 Pod(容器组)并为它们定义一个访问方式。Service(服务)使 Pod(容器组)之间的相互依赖解耦(原本从一个 Pod 中访问另外一个 Pod,需要知道对方的 IP 地址)。一个 Service(服务)选定哪些 Pod(容器组) 通常由 LabelSelector(标签选择器) 来决定。

在创建Service的时候,通过设置配置文件中的 spec.type 字段的值,可以以不同方式向外部暴露应用程序:

  • ClusterIP(默认)

在群集中的内部IP上公布服务,这种方式的 Service(服务)只在集群内部可以访问到

  • NodePort

使用 NAT 在集群中每个的同一端口上公布服务。这种方式下,可以通过访问集群中任意节点+端口号的方式访问服务 :。此时 ClusterIP 的访问方式仍然可用。

  • LoadBalancer

在云环境中(需要云供应商可以支持)创建一个集群外部的负载均衡器,并为使用该负载均衡器的 IP 地址作为服务的访问地址。此时 ClusterIP 和 NodePort 的访问方式仍然可用。

Service是一个抽象层,它通过 LabelSelector 选择了一组 Pod(容器组),把这些 Pod 的指定端口公布到到集群外部,并支持负载均衡和服务发现。

  • 公布 Pod 的端口以使其可访问
  • 在多个 Pod 间实现负载均衡
  • 使用 Label 和 LabelSelector

服务和标签

下图中有两个服务Service A(黄色虚线)和Service B(蓝色虚线) Service A 将请求转发到 IP 为 10.10.10.1 的Pod上, Service B 将请求转发到 IP 为 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。

在这里插入图片描述

Service 将外部请求路由到一组 Pod 中,它提供了一个抽象层,使得 Kubernetes 可以在不影响服务调用者的情况下,动态调度容器组(在容器组失效后重新创建容器组,增加或者减少同一个 Deployment 对应容器组的数量等)。

Service使用 Labels、LabelSelector(标签和选择器) 匹配一组 Pod。Labels(标签)是附加到 Kubernetes 对象的键/值对,其用途有多种:

  • 将 Kubernetes 对象(Node、Deployment、Pod、Service等)指派用于开发环境、测试环境或生产环境
  • 嵌入版本标签,使用标签区别不同应用软件版本
  • 使用标签对 Kubernetes 对象进行分类

下图体现了 Labels(标签)和 LabelSelector(标签选择器)之间的关联关系
在这里插入图片描述

  • Deployment B 含有 LabelSelector 为 app=B 通过此方式声明含有 app=B 标签的 Pod 与之关联
  • 通过 Deployment B 创建的 Pod 包含标签为 app=B
  • Service B 通过标签选择器 app=B 选择可以路由的 Pod

使用标签,可以进行统一的设定,是非常方便的。Labels(标签)可以在创建 Kubernetes 对象时附加上去,也可以在创建之后再附加上去。任何时候都可以修改一个 Kubernetes 对象的 Labels(标签)

设置一个Service

之前我们已经启动了ngxin Deployment,重新创建一个deployment-label.yaml, 现在将它打上一个标签 app: nginx :

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
  labels: #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组
    app: nginx #为该Deployment设置key为app,value为nginx的标签
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

然后我们创建对应的nginx-service.yaml,对标签为 app: ngxin的Pod提供service服务:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service	#Service 的名称
  labels:     	#Service 自己的标签
    app: nginx	#为该 Service 设置 key 为 app,value 为 nginx 的标签
spec:	    #这是关于该 Service 的定义,描述了 Service 如何选择 Pod,如何被访问
  selector:	    #标签选择器
    app: nginx	#选择包含标签 app:nginx 的 Pod
  ports:
  - name: nginx-port	#端口的名字
    protocol: TCP	    #协议类型 TCP/UDP
    port: 80	        #集群内的其他容器组可通过 80 端口访问 Service
    nodePort: 32600   #通过任意节点的 32600 端口访问 Service
    targetPort: 80	#将请求转发到匹配 Pod 的 80 端口
  type: NodePort	#Serive的类型,ClusterIP/NodePort/LoaderBalancer

执行下面,更新之前的nginx Pod以及新创建对应的Service:

kubectl apply -f deployment-label.yaml
kubectl apply -f nginx-service.yaml
可以看到提示:service/nginx-service created

执行查看serivce命令 kubectl get services -o wide(列出所有 service 并显示详细信息),可以看到:
在这里插入图片描述

kubectl get nodes -o wide 查看节点详细信息
minikube ip 查看虚拟机的ip
minikube ssh 进入虚拟机

这个时候,访问任意节点23600端口就能看到啦。(博主因为某vpn被毙掉了。所以nginx镜像都没下载成功)

部署一个springboot单机helloword

创建一个helloword项目

这就过多赘述了,直接上简单的例子
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>k8s.demo</groupId>
    <artifactId>k8s-helloword</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <run.jvmArguments/>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.1.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.3.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

启动方法:

package k8s.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Boot {
    public static void main(String[] args) {
        SpringApplication.run(Boot.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "hello word";
    }
}

配置文件:

server:
  port: 9999

打包并运行测试

mvn clean install
mvn package spring-boot:repackage
java -jar target/k8s-helloword.jar

然后访问http://localhost:9999/hello,得到hello word, 应用程序成功

使用docket方式运行

博主第一篇章有docker方式

编写Dockerfile,然后利用文件打包镜像,将我们的jar打到镜像里

FROM frolvlad/alpine-oraclejdk8:slim
RUN mkdir /docker/demo
RUN mkdir /docker/demo/logs
ADD ./target/k8s-helloword.jar /docker/demo/k8s-helloword.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/docker/demo/k8s-helloword.jar"]

docker build -t codemon/k8s-hello . 进行打包镜像
docker run -d -p 9999:9999 --name k8s-hello codemon/k8s-hello 运行起来,然后访问http://localhost:9999/hello,得到hello word, docker运行成功

将镜像部署到k8s

内网模式

将k8s的docker地址修改为本地的太麻烦,博主直接将镜像打到了博主的docker hub上,然后公开即可。
编写Pod和Service的yaml文件:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloword
    labels:
        app: helloword
spec:
    replicas: 1
    selector:
        matchLabels:
            app: helloword
    template:
        metadata:
            labels:
                app: helloword
        spec:
            containers:
                - name: helloword
                  image: codemon/k8s-hello:latest
                  ports:
                      - containerPort: 9999
---
apiVersion: v1
kind: Service
metadata:
    name: helloword-service
    labels:
        app: helloword
spec:
    ports:
        - port: 9999
          protocol: TCP
    selector:
        app: helloword

执行kubectl apply -f helloword-deployment.yaml,发布pod和service。

可以使用
kubectl get pods -o wide
kubectl get services -o wide
查看具体信息,

可以看到,helloword-service已经成功,并且使用的是 默认的ClusterIP类型,这种方式只可以在内网进行访问,我们来尝试一下在这里插入图片描述
minikube ssh进入虚拟机后,curl http://10.97.23.8:9999/hello成功,获得返回值,但是在外面无论如何也访问不了

外网模式

将上面的service,加入 type: NodePort 开放公网

spec:
    ports:
        - port: 9999
          protocol: TCP
    type: NodePort
    selector:
        app: helloword

重新运行kubectl apply -f helloword-deployment.yaml, 这个时候,使用kubectl get services -o wide,可以看到,网络ip类型已经改变,并且未指定端口的情况下,随机对外映射了31125端口
在这里插入图片描述
使用kubectl get nodes -o wide查询node节点详细信息,可以看到node节点内网ip是: 10.0.2.15
在这里插入图片描述
minikube ssh进入虚拟机,使用service本身的ip10.97.23.8和node节点的ip 10.0.2.15访问:
curl http://10.97.23.8:9999/hello
curl http://10.0.2.15:9999/hello
都成功访问

我们再获取虚拟机的ip,minikube ip, 获取到虚拟机内部集群对外的ip是:192.168.99.110
打开浏览器调用http://192.168.99.110:31125/hello, 成功

滚动发布更新版本

首先我们来打两个镜像并发布到docker hub上:

  • 第一个叫codemon/k8s-hello:v1, 接口返回“v1”这个字符串
  • 第二个叫codemon/k8s-hello:v2, 接口返回“v2”这个字符串
  • 然后我们docker push codemon/k8s-hello, 会将两个镜像标签都push上去

我们修改一下pod配置

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloword
    labels:
        app: helloword
spec:
    replicas: 2 #这里改成两个副本
    selector:
        matchLabels:
            app: helloword
    template:
        metadata:
            labels:
                app: helloword
        spec:
            containers:
                - name: helloword
                  image: codemon/k8s-hello:v1 #这里改成v1版本的镜像
                  ports:
                      - containerPort: 9999

执行kubectl apply -f helloword-deployment.yaml
执行kubectl get --watch deployments 监控一下pod的变化

需要耐心等待一小会,毕竟要重新拉镜像重新启动pod

在这里插入图片描述
看到监控,pod已经全部好了。调用接口试试:http://192.168.99.110:31125/hello,返现返回v1字符串。成功~

然后上面步骤一样,将镜像标记改为v2,部署之后发现接口返回值是:v2

回滚应用版本

和上面发布新版本一样, 我们发现部署有问题,想立即回滚。我们可以直接修改镜像的名称,然后执行上面的步骤。返现已经回滚

其实k8s有自己的回滚方式,这里就不介绍了。随着后面博主的博客,会在企业级容器架构和微服务集合后,统一介绍

删除与恢复

我们来看一个巨牛逼的功能。直接把pod删掉。相当于把pod上面的docker服务也一起删掉了。这个时候会发生什么:

kubectl delete pod helloword-68874fb864-4k78x
在这里插入图片描述

你看到的没错。他又自动恢复了。这个过程中一直刷接口,并没有挂掉

结尾

可以看到,最终我们以NodePort方式开放出来固定ip,内部如何转发是k8s自己的事情。开放出来的ip可以在云方案中搭配内部负载均衡器和vpc使用,方便快捷,当然,还有另外一些方式开放
参考ip方面文章:
kubernetes service外部网络访问方式
kubernetes(k8s)Deployment滚动升级和回滚

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值