系列

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

更换成阿里云仓库

首先,k8s默认的是docker hub的源。网络实在太卡,我们先切换成国内的。

开通阿里云镜像服务

这个服务免费的

进入控制台,开通镜像服务
在这里插入图片描述

创建仓库

点击创建仓库,然后填写完。这里博主选择公开,毕竟用于本地和云上测试(企业级必须私有哦。并且云上k8s保持同一个vpc内)
在这里插入图片描述
这里选择本地仓库。你可以使用阿里云的自动化构建。不过这里博主选择自己手动搞
在这里插入图片描述
在这里插入图片描述
可以看到。只要在本地登录docker之后。便可以轻松操作了。
密码在访问凭证里可以设定
在这里插入图片描述
随便打几个镜像上传到阿里云仓库把。发现已经上传完毕:
在这里插入图片描述

本地k8s切换成阿里云的镜像仓库

创建访问阿里云docker的secret对象,这里是阿里云docker的账号密码

kubectl create secret docker-registry aliyun-docker-test \
  --docker-server=registry.cn-shanghai.aliyuncs.com \
  --docker-username=登陆阿里云docker的用户名 \
  --docker-password=登陆阿里云docker的密码 \
  --docker-email=选填

请注意上线命令中的命名:aliyun-docker-test 如果需要使用这个docker源。那么就要在pod里面指定是用这个名称获取镜像

使用kubectl get secrets查看所有的镜像仓库。可以看到刚才我们创建的aliyun-docker-test已经好了在这里插入图片描述

测试阿里云镜像

我们将pod配置修改为:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloword
    labels:
        app: helloword
spec:
    minReadySeconds: 30 #启动pod之后延迟多少时间
    strategy:
        # indicate which strategy we want for rolling update
        type: RollingUpdate #滚动发布
        rollingUpdate:
            maxSurge: 1
            maxUnavailable: 1
    replicas: 2
    selector:
        matchLabels:
            app: helloword
    template:
        metadata:
            labels:
                app: helloword
        spec:
            containers:
                - name: helloword
                  image: registry.cn-shanghai.aliyuncs.com/emper_test/k8s-test:v1
                  imagePullPolicy: Always
                  ports:
                      - containerPort: 9999
            imagePullSecrets:
                - name: default/aliyun-docker-test #指定是阿里云docker的资源,斜杠前面是命名空间,创建不指定的话默认是default命名空间, 这里不加命名空间会拉不到镜像
---
apiVersion: v1
kind: Service
metadata:
    name: helloword-service
    labels:
        app: helloword
spec:
    ports:
        - port: 9999
          protocol: TCP
    type: NodePort
    selector:
        app: helloword

注意imagePullSecrets的名称一定要加命名空间,最开始博主没加,结果各种获取不了镜像

  • minReadySeconds:
    这个值默认是0,Kubernetes会假设该容器启动起来后就可以提供服务了,会导致用户访问异常
    这里需要估一个比较合理的值,从容器启动到应用正常提供服务
    如果没有设置该值,Kubernetes会假设该容器启动起来后就提供服务了
    如果没有设置该值,在某些极端情况下可能会造成服务不正常运行
  • maxSurge:
    升级过程中最多可以比原先设置多出的POD数量
    例如:maxSurage=1,replicas=5,则表示Kubernetes会先启动1一个新的Pod后才删掉一个旧的POD,整个升级过程中最多会有5+1个POD。
  • maxUnavaible:
    升级过程中最多有多少个POD处于无法提供服务的状态
    当maxSurge不为0时,该值也不能为0
    例如:maxUnavaible=1,则表示Kubernetes整个升级过程中最多会有1个POD处于无法服务的状态。
  • 注意如果实在不想过于复杂的滚动发布,那么将minReadySeconds设置一个合理的值(比如确认1分钟之内应用都能启动),这样可以让应用无间断发布

再次执行kubectl apply -f helloword-deployment.yaml,发现成功,访问http://192.168.99.110:31125/hello,得到“aliyun docker version:1”返回。
将镜像版本改为v2. 也成功得到“aliyun docker version:2”。
切换成阿里云docker成功

准备简单的微服务

我们来准备三个服务:eureka、demo-a、demo-b。

demo-a提供两个接口:

  • /demo/a,返回I am demo-a
  • /demo/a/from/b,调用demo-b服务返回feign: from demo-b

demo-b提供两个接口:

  • /demo/b,返回I am demo-b
  • /demo/b/from/a,调用demo-a服务返回feign: from demo-a

eureka

应用配置

<?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>eureka</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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</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.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

配置文件:

server:
  port: 8761

spring:
  profiles:
    active: dev
  application:
    name: eureka

eureka:
  server:
    #是否开启自我保护(运行期间spring会统计信条失败的比例在15分钟之内是否低于85%,如果不低于85%,Eureka会将实例注册信息保护起来,让这些实例不会过期)
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 3000      #3秒钟自动剔除失效的节点
    response-cache-update-interval-ms: 3000  #eureka server刷新readCacheMap的时间
    #注意,client读取的是readCacheMap,这个时间决定了多久会把readWriteCacheMap的缓存更新到readCacheMap上
  instance:
    hostname: server-eureka-server

  client:
    register-with-eureka: false #自己也注册到注册中心
    fetch-registry: false #需要从其他eureka节点获取注册信息
    service-url:
      default-zone: http://${eureka.instance.hostname}:${server.port}/eureka #设置与eureka交互的地址

这里注意: hostname: server-eureka-server 是用于k8s中service名称匹配。

k8s配置:

创建一个文件k8s-eureka-server.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: deployment-eureka-server
spec:
    # 注册中心部署一个即可,集群的话,比较麻烦
    replicas: 1
    selector:
        matchLabels:
            app: eureka-server
    template:
        metadata:
            labels:
                app: eureka-server
        spec:
            containers:
                - name: eureka-server
                  image: registry.cn-shanghai.aliyuncs.com/emper_test/eureka-server:v2
                  imagePullPolicy: Always
                  ports:
                      - containerPort: 8761
            imagePullSecrets:
                - name: default/aliyun-docker-test
---
apiVersion: v1
kind: Service
metadata:
    # service的名字非常重要,必须跟application.properties的eureka.client.serviceUrl.defaultZone=http://server-eureka-server:8761/eureka/ 中的域名相同
    name: server-eureka-server
spec:
    selector:
        app: eureka-server
    # 使用NodePort类型的Service,绑定service的10001端口到宿主机,以便在物理机浏览器上看注册中心的信息
    type: NodePort
    ports:
        - port: 8761
          targetPort: 8761

demo-a

应用配置

Pom文件就不多做展示了,比较简单,引入springcloud以及feign等jar即可

我们下一个路由:

package k8s.demo.a;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Router {
    @Autowired
    private Client client;

    /**
     * demo-a服务的接口
     * @return
     */
    @GetMapping("/demo/a")
    public String hello() {
        return "I am demo-a";
    }

    /**
     * demo-a请求demo-b的feign接口返回
     * @return
     */
    @GetMapping("/demo/a/from/b")
    public String fromB() {
        return client.feign();
    }

    /**
     * 提供给demo-b的feign内部接口
     * @return
     */
    @GetMapping("/demo/a/feign")
    public String feign() {
        return "feign: from demo-a";
    }
}

再加入feign接口用于调用demo-b服务:

package k8s.demo.a;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("demo-b")
public interface Client {
    @GetMapping("/demo/b/feign")
    public String feign();
}

yaml配置文件如下:

server:
  port: 9998

spring:
  application:
    name: demo-a

eureka:
  instance:
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 20   #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除(默认90秒)
    lease-renewal-interval-in-seconds: 5       #服务刷新时间配置,每隔这个时间会主动心跳一次(默认30秒)
  client:
    service-url:
      defaultZone: http://server-eureka-server:8761/eureka/
    registry-fetch-interval-seconds: 10  #重新刷新服务地址的时间

这里注意defaultZone: http://server-eureka-server:8761/eureka/ 这个连接是k8s的service名称

k8s配置

Dockerfile:

FROM openjdk:8-jre-alpine
RUN mkdir -p /docker/demo
RUN mkdir -p /docker/demo/logs
ADD ./target/demo-a.jar /docker/demo/demo-a.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/docker/demo/demo-a.jar"]

k8s的pod配置:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: deployment-demo-a
    labels:
        app: deployment-demo-a
spec:
    minReadySeconds: 30
    strategy:
        # indicate which strategy we want for rolling update
        type: RollingUpdate
        rollingUpdate:
            maxSurge: 1
            maxUnavailable: 1
    replicas: 1
    selector:
        matchLabels:
            app: demo-a
    template:
        metadata:
            labels:
                app: demo-a
        spec:
            containers:
                - name: demo-a
                  image: registry.cn-shanghai.aliyuncs.com/emper_test/demo-a:v1
                  imagePullPolicy: Always
                  ports:
                      - containerPort: 9998
            imagePullSecrets:
                - name: default/aliyun-docker-test #指定是阿里云docker的资源,斜杠前面是命名空间,创建不指定的话默认是default命名空间, 这里不加命名空间会拉不到镜像
---
apiVersion: v1
kind: Service
metadata:
    name: demo-a-service
    labels:
        app: demo-a-service
spec:
    ports:
        - port: 9998
    selector:
        app: demo-a #选择所有为demo-a的pod

demo-b

和demo-a基本一样

应用配置

写一个路由:

package k8s.demo.b;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Router {
    @Autowired
    private Client client;

    /**
     * demo-b服务的接口
     * @return
     */
    @GetMapping("/demo/b")
    public String hello() {
        return "I am demo-b";
    }

    /**
     * demo-b请求demo-a的feign接口返回
     * @return
     */
    @GetMapping("/demo/b/from/a")
    public String fromA() {
        return client.feign();
    }

    /**
     * 提供给demo-a的feign内部接口
     * @return
     */
    @GetMapping("/demo/b/feign")
    public String feign() {
        return "feign: from demo-b";
    }
}

再来一个feign:

package k8s.demo.b;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("demo-a")
public interface Client {
    @GetMapping("/demo/a/feign")
    String feign();
}

应用配置文件如下:

server:
  port: 9999
spring:
  application:
    name: demo-b

eureka:
  instance:
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 20   #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除(默认90秒)
    lease-renewal-interval-in-seconds: 5       #服务刷新时间配置,每隔这个时间会主动心跳一次(默认30秒)
  client:
    service-url:
      defaultZone: http://server-eureka-server:8761/eureka/
    registry-fetch-interval-seconds: 10  #重新刷新服务地址的时间

k8s配置

Dockerfile:

FROM openjdk:8-jre-alpine
RUN mkdir -p /docker/demo
RUN mkdir -p /docker/demo/logs
ADD ./target/demo-b.jar /docker/demo/demo-b.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/docker/demo/demo-b.jar"]

k8s的pod配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: deployment-demo-b
    labels:
        app: deployment-demo-b
spec:
    minReadySeconds: 30
    strategy:
        # indicate which strategy we want for rolling update
        type: RollingUpdate
        rollingUpdate:
            maxSurge: 1
            maxUnavailable: 1
    replicas: 1
    selector:
        matchLabels:
            app: demo-b
    template:
        metadata:
            labels:
                app: demo-b
        spec:
            containers:
                - name: demo-b
                  image: registry.cn-shanghai.aliyuncs.com/emper_test/demo-b:v1
                  imagePullPolicy: Always
                  ports:
                      - containerPort: 9999
            imagePullSecrets:
                - name: default/aliyun-docker-test #指定是阿里云docker的资源,斜杠前面是命名空间,创建不指定的话默认是default命名空间, 这里不加命名空间会拉不到镜像
---
apiVersion: v1
kind: Service
metadata:
    name: demo-b-service
    labels:
        app: demo-b-service
spec:
    ports:
        - port: 9999
    selector:
        app: demo-b #选择所有为demo-b的pod

打成镜像docker镜像并上传到阿里云镜像服务

首先对每个服务进行mvn package spring-boot:repackage 打包操作。

然后我们使用docker进行build:

docker build -t 你的阿里云镜像路径:版本号 .

然后使用docker push 镜像:版本号,将其push到阿里云镜像仓库

部署k8s

部署eureka

在eureka的k8s配置文件目录下执行:

kubectl apply -f k8s-eureka-server.yaml

略微等待,因为我们对eureka开启了外部ip,启动完毕之后,我们使用

minikube ip, 查看到虚拟机ip是:http://192.168.99.110

使用kubectl get service,查看到所有service的ip和端口:
在这里插入图片描述
我们在浏览器输入:http://192.168.99.110:31095/,返现eureka启动,并且两个服务已注册上:
在这里插入图片描述

部署demo-a和demo-b

使用:

kubectl apply -f k8s-demo-a.yaml
kubectl apply -f k8s-demo-b.yaml
分别创建对应的service、pod
两个服务都是内网ip,不对外部提供ip

根据上面的图,发现:

  • demo-a内部ip是:10.100.194.238:9998
  • demo-b内部ip是:10.96.126.63:9998

使用minikube ssh进入虚拟机内。分别用两个服务的地址调用接口:

curl http://10.100.194.238:9998/demo/a
curl http://10.100.194.238:9998/demo/a/from/b
curl http://10.96.126.63:9998/demo/b
curl http://10.96.126.63:9998/demo/b/from/a
在这里插入图片描述
发现调用成功无误

结尾

由于k8s判定服务是否可用是基于pod,但是有可能出现容器内部的应用还没启动,所以会造成短暂的不可用。
参考:Kubernetes–k8s—滚动更新–零停机不停服发布服务

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐