《docker+k8s教程》

kuernetes支持滚动更新部署策略,即一个接一个地以滚动更新方式发布新版本,本文使用滚动更新策略部署应用,探索使用kubernetes零宕机部署。

1、新建一个java web服务app3。

添加spring-boot-starter-actuator依赖,方便创建端点作为健康检查接口。

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

application.yaml配置如下:

server:
  port: 10013
  servlet:
    context-path: /app3

management:
  endpoints:
    web:
      exposure:
        include: '*'
      base-path: /
  server:
    servlet:
      context-path: ${server.servlet.context-path}
    port: ${server.port}
  endpoint:
    health:
      show-details: always

# 定义一个端点,用于健康检查。
# url:localhost:10013/app3/info
info: {status: ok}

创建一个controller测试类

@RequestMapping("/test")
@RestController
public class TestController {

    Logger logger = LoggerFactory.getLogger(TestController.class);

    @GetMapping("/get")
    public String get() throws Exception{
        Random random = new Random();
        int i = random.nextInt(500)+100;
        TimeUnit.MILLISECONDS.sleep(i);
        // v1、v2版本的打印信息不同
        String r = "版本V1,睡眠时间---"+i;
        //String r = "版本V2222,睡眠时间---"+i;
        logger.info(r);
        return r;
    }

}

2、将应用打成jar包,再将jar打成docker镜像。

Dockerfile如下:

FROM openjdk:8
ADD *.jar /app/app.jar
ADD entrypoint.sh /app/
# PORT="10013" 是项目端口号
ENV PORT="10013" JAVA_OPS="-Xmx256m -Xms256m -XX:+UseConcMarkSweepGC"
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT  ["/app/entrypoint.sh"]
EXPOSE $PORT
STOPSIGNAL SIGTERM

entrypoint.sh如下:

#!/bin/sh

# 修改时间
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

exec java ${JAVA_OPS} -jar /app/app.jar

jar包启动命令前加上exec是让java应用的进程取代shell命令进程,确保java应用进程作为容器的root进程,即java应用的pid是1。当kubernetes要关闭容器时,发送SIGTERM信号给容器,java应用作为root进程将接收到SIGTERM信号,关闭应用。如果root进程是shell命令,shell命令无法响应SIGTERM信号。导致kubernetes最终会发送SIGKILL给容器。

jar启动命令加上exec,容器内进程如下图

不加exec,容器进程如下图

在kubernetes集群副节点打两个docker镜像。

docker build -t codingsoldier/app3:v1 .

docker build -t codingsoldier/app3:v2 .

打好codingsoldier/app3:v1镜像后,修改controller方法的打印信息,再打成codingsoldier/app3:v2

3、编写两个Deployment。

deployment-app3-v1.yaml配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-app3
spec:
  # pod数量
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      # 超过期望的可用pod个数。
      maxSurge: 1
      # 在升级过程中不可用Pod的最大数量
      maxUnavailable: 0
  selector:
    matchLabels:
      app: app3
  template:
    metadata:
      labels:
        app: app3
    spec:
      # k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
      terminationGracePeriodSeconds: 100
      containers:
      - name: app3
        # 镜像版本
        image: codingsoldier/app3:v1
        ports:
        - name: http
          containerPort: 10013
        lifecycle:
          # 在容器被终止前睡眠一段时间,以便kube-proxy更新service的endpoint
          preStop:
            exec:
              command: ["/bin/bash", "-c", "sleep 35"]
        # 就绪探针。
        # docker容器启动成功,并不代表容器中的服务就能处理外部的请求,例如java应用启动需要一定时间
        # Kubernetes提供了readinessProbe来检测pod中的容器是否可以接受外部流量
        readinessProbe:
          httpGet:
            # 连接使用的schema,默认HTTP。
            scheme: HTTP
            # 访问的容器的端口名字或者端口号。端口号必须介于1和65525之间
            port: 10013
            # 访问的HTTP server的path
            path: /app3/info
          # 探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1。  
          failureThreshold: 3
          # 容器启动后第一次执行探测是需要等待多少秒。
          initialDelaySeconds: 20
          # 执行探测的频率。默认是10秒,最小1秒。
          periodSeconds: 10
          # 探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1。
          successThreshold: 1
          # 探测超时时间。默认1秒,最小1秒。
          timeoutSeconds: 1
        # 存活探针,用于判断容器是否存活。配置参数跟就绪探针相同
        livenessProbe:
          httpGet:
            scheme: HTTP
            port: 10013
            path: /app3/info
          failureThreshold: 3
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1

再新建一个deployment-app3-v2.yam,修改镜像  image: codingsoldier/app3:v2

部署应用 : kubectl  apply -f deployment-app3-v1.yaml 

4、编写一个service-nodeport.yaml,service使用NodePort类型暴露端口。

apiVersion: v1
kind: Service
metadata:
  name: service-app3
spec:
  selector:
    app: app3
  # 使用NodePort方式暴露一个外部端口供调试使用
  type: NodePort
  ports:
  - name: http
    port: 10013
    targetPort: 10013
    # 外部端口
    nodePort: 30013

部署service: kubectl  apply -f service-nodeport.yaml

5、升级应用版本: kubectl  apply -f deployment-app3-v2.yaml 

在升级期间使用jmeter进行并发测试,一秒钟发送50个请求,请求/app3/test/get接口。有可能会出现几个失败的请求,请求返回结果是  Connection reset 。

暂时无法解决 Connection reset的问题。有高手知道如何解决此问题还望赐教。

6、项目代码地址 https://github.com/CodingSoldier/java-learn/tree/master/project/app3

 

Logo

开源、云原生的融合云平台

更多推荐