Spring Boot应用在kubernetes的sidecar设计与实战

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: kubernetes官方的sidecar,与SpringBoot能有关系?请随本文一同探究

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos
  • 基于Spring Boot框架的应用如何在kubernetes环境部署,是沿用Docker容器的做法还是另有其他?接下来咱们从理论到设计再到实战,一起来感受传统后台应用在容器环境的微妙变化;

Spring Boot应用

  • 基于Spring Boot框架的应用,通常会构建成XXX.jar文件,执行java -jar XXX.jar来运行该应用;

Docker下的Spring Boot应用镜像

  • Docker环境下,通常用Maven的docker-maven-plugin插件将应用打包成镜像,例如以java:8u111-jdk作为基础镜像再加入jar文件,这样容器启动的时候执行java -jar XXX.jar就能将应用运行起来了;

传统思路

  • Spring Boot应用镜像在kubernetes声明为Pod,即可正常运行;
  • 但是,这是合适的做法么?去K8S官网需要一些理论上的指导吧;

寻找官方的理论依据

image.png

  • 上图红框中提到一个容器基于共享资源对外提供服务,另一个"sidecar"容器负责更新这些共享资源;
  • 在Kubernetes中文社区的文档中也对此作了阐述,地址是:http://docs.kubernetes.org.cn/312.html

image.png

  • 在提到Pod中的sidecar模式时,官方文档用到"relatively advanced"来形容,进一步证实了当下该模式的正确性,如下图:

image.png

  • 具体的实现模型如下图:

image.png

Spring Boot应用的sidecar设计

  • 根据kubernetes官方文档的指导,再结合SpringBoot应用的特点,我设计出的sidecar部署方式如下:

image.png

  • 该应用的业务服务被封装在一个Pod定义中,该Pod由两个容器组成;
  • 绿色容器是来自OpenJDK 官方镜像:openjdk:8u181-jre-alpine3.8,用docker history命令查看体积,几十兆不算大:
[root@localhost work]# docker history openjdk:8u181-jre-alpine3.8
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
2e01f547f003        12 days ago         /bin/sh -c set -x  && apk add --no-cache  ...   78.6 MB             
<missing>           12 days ago         /bin/sh -c #(nop)  ENV JAVA_ALPINE_VERSION...   0 B                 
<missing>           12 days ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u181       0 B                 
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV PATH=/usr/local/sbi...   0 B                 
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/...   0 B                 
<missing>           7 weeks ago         /bin/sh -c {   echo '#!/bin/sh';   echo 's...   87 B                
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0 B                 
<missing>           7 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0 B                 
<missing>           7 weeks ago         /bin/sh -c #(nop) ADD file:25c10b1d1b41d46...   4.41 MB
  • 之所以要用jre-alpine版本,是因为8u181-jdk版本相比之下大了很多,如下所示:
[root@localhost work]# docker history openjdk:8u181-jdk
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
954739b8bdfb        7 days ago          /bin/sh -c /var/lib/dpkg/info/ca-certifica...   355 kB              
<missing>           7 days ago          /bin/sh -c set -ex;   if [ ! -d /usr/share...   348 MB              
<missing>           7 days ago          /bin/sh -c #(nop)  ENV CA_CERTIFICATES_JAV...   0 B                 
<missing>           7 days ago          /bin/sh -c #(nop)  ENV JAVA_DEBIAN_VERSION...   0 B                 
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u181       0 B                 
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_HOME=/docker-j...   0 B                 
<missing>           3 weeks ago         /bin/sh -c ln -svT "/usr/lib/jvm/java-8-op...   33 B                
<missing>           3 weeks ago         /bin/sh -c {   echo '#!/bin/sh';   echo 's...   87 B                
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0 B                 
<missing>           3 weeks ago         /bin/sh -c apt-get update && apt-get insta...   2.21 MB             
<missing>           3 weeks ago         /bin/sh -c apt-get update && apt-get insta...   142 MB              
<missing>           3 weeks ago         /bin/sh -c set -ex;  if ! command -v gpg >...   7.8 MB              
<missing>           3 weeks ago         /bin/sh -c apt-get update && apt-get insta...   23.2 MB             
<missing>           3 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0 B                 
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:b3598c18dc39584...   101 MB  
  • 红色容器的镜像是用Spring Boot应用构建出来的,稍后再详细整个过程;
  • 在kubernetes环境,这两个容器会挂载同一个Volume,红色容器将jar放在此处,绿色容器使用此处的jar;
  • 红色容器用来提供jar,没有进程需要保持运行状态,很适合设置为Init Container类型;
  • 绿色容器的java进程是长久运行的;
  • 以上就是整体设计思路,接下来咱们就来实战吧,分三步完成:

实战步骤列举

  • 本次实战分为以下几部分组成:
  • 开发Spring Boot应用;
  • 制作Docker镜像,做两个版本,以便验证升级;
  • 编写yaml文件;
  • 在kubernetes环境部署,验证;
  • 升级版本,验证;

环境和版本信息

  1. 编译构建的jdk和运行的jre都用1.8版本;
  2. maven:3.3.3;
  3. spring boot:2.1.0.RELEASE;
  4. docker:1.13.1;
  5. kubernetes:1.12.2;
  • 本次实战一共有四台CentOS7机器,基本信息如下:
hostname IP地址 身份 配置
localhost 192.168.119.157 master,主控节点 双核,2G内存
node1 192.168.119.156 node,一号业务节点 双核,4G内存
node2 192.168.119.159 node,二号业务节点 双核,2G内存
maven 192.168.119.155 负责编译构建Spring Boot应用 双核,2G内存
  • kubernetes环境由localhost、node1、node2三台机器组成,maven负责编译构建、生成镜像、上传到镜像仓库等操作;

开发Spring Boot应用

  • 这是个简单的Spring Boot应用,对外提供一个http接口,返回一个字符串;
  • 您可以选择直接从GitHub下载这个工程的源码,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本章源码在springbootsidecardemo这个文件夹下,如下图红框所示:

image.png

  • 您也可以随本文一起来开发这个应用:
  • 应用名为springbootsidecardemo,是用maven构建的,JDK使用1.8,Spring Boot版本2.1.0.RELEASE;
  • 应用的pom.xml如下,为了构建Docker镜像使用了docker-maven-plugin插件,该插件具体的配置请参照下面的注释:
<?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>com.bolingcavalry</groupId>
    <artifactId>springbootsidecardemo</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>springbootsidecardemo</name>
    <description>Demo project for Spring Boot sidecard demo in K8S</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

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

    <dependencies>
        <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>
        </dependency>
    </dependencies>

    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!--新增的docker maven插件-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.4.12</version>
                <!--docker镜像相关的配置信息-->
                <configuration>
                    <!--镜像名,这里用工程名-->
                    <imageName>bolingcavalry/${project.artifactId}</imageName>
                    <!--TAG,这里用工程版本号-->
                    <imageTags>
                        <imageTag>${project.version}</imageTag>
                    </imageTags>
                    <!--镜像的FROM,使用busybox-->
                    <baseImage>busybox</baseImage>
                    <!--构建镜像的配置信息-->
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>app.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • 上面的配置有一处需要注意,就是基础镜像的选择(就是baseImage节点中的内容),我用了busybox,用它是因为够小,来看docker镜像仓库中的描述,地址是https://hub.docker.com/_/busybox/

image.png

  • 看到这里,可能会有朋友问"为什么不用scratch?它比busybox更小",scratch虽小,但不带基本的shell命令,例如cp命令,而后容器启动时要用cp命令对文件做复制操作,因此只能选择busybox了;
  • Controller类的代码也很简单:
package com.bolingcavalry.springbootsidecardemo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @Description: 一个最普通的Controller,hello接口返回一个字符串并带上当前时间
 * @author: willzhao E-mail: zq2599@gmail.com
 * @date: 2018/11/6 14:15
 */
@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    public String hello(){
        return "Hello version 1.0 " + new Date();
    }
}

制作应用的Docker镜像

  • 请确保您当前环境的maven和Docker都已经OK了;
  • 在应用的pom.xml所在目录执行如下命令即可构建Docker镜像:
mvn clean package -U -DskipTests docker:build
  • 构建成功的控制台输出如下:
[INFO] Building image bolingcavalry/springbootsidecardemo
[INFO] Building image bolingcavalry/springbootsidecardemo
Step 1/2 : FROM busybox
 ---> 59788edf1f3e
Step 2/2 : ADD /app.jar //
 ---> 8105c9ac033b
Removing intermediate container fdc62513abf6
Successfully built 8105c9ac033b
[INFO] Built bolingcavalry/springbootsidecardemo
[INFO] Tagging bolingcavalry/springbootsidecardemo with 0.0.1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.354 s
[INFO] Finished at: 2018-11-06T05:07:08-08:00
[INFO] Final Memory: 42M/225M
[INFO] ------------------------------------------------------------------------
  • 构建成功后用docker history命令查看镜像,如下,三个layer组成:
root@maven:~# docker history bolingcavalry/springbootsidecardemo:0.0.1
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
8105c9ac033b        46 minutes ago      /bin/sh -c #(nop) ADD file:909ca8e9c8898cd...   16.6 MB             
59788edf1f3e        4 weeks ago         /bin/sh -c #(nop)  CMD ["sh"]                   0 B                 
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:63eebd629a5f755...   1.15 MB
  • 去工程的target目录下看看构建的app.jar文件,如下所示,也是16兆,所以这个镜像已经被做到最小了,相对于以前那种JAVA环境+jar文件的镜像,这个镜像更易于下载和上传:
root@maven:/usr/local/work/github/blog_demos/springbootsidecardemo/target# ls -al
total 16276
drwxr-xr-x 9 root root     4096 Nov  6 05:08 .
drwxr-xr-x 5 root root     4096 Nov  6 05:29 ..
-rw-r--r-- 1 root root 16621351 Nov  6 05:08 app.jar
-rw-r--r-- 1 root root     4432 Nov  6 05:08 app.jar.original
drwxr-xr-x 3 root root     4096 Nov  6 05:08 classes
drwxr-xr-x 2 root root     4096 Nov  6 05:08 docker
drwxr-xr-x 3 root root     4096 Nov  6 05:08 generated-sources
drwxr-xr-x 3 root root     4096 Nov  6 05:08 generated-test-sources
drwxr-xr-x 2 root root     4096 Nov  6 05:08 maven-archiver
drwxr-xr-x 3 root root     4096 Nov  6 05:08 maven-status
drwxr-xr-x 3 root root     4096 Nov  6 05:08 test-classes
  • 执行docker push命令,将镜像推送到镜像仓库中,我这里是推送到了hub.docker.com,您可以根据实际情况来执行,例如私有仓库、阿里云等都可以,当然了,如果当前机器就是K8S的机器就不用推送了,毕竟此镜像就是在K8S环境用的;
  • 如果觉得推送到仓库太慢,或者从仓库下载太慢,也可以使用文件导入导出的方式,具体操作如下:
#将镜像导出为tar文件
docker save 2e01f547f003 > 1.tar

###将tar文件还原为镜像
docker load < 1.tar

###还原后的镜像的名称和tag都不对,要用tag命令来设置
docker tag 8105c9ac033b bolingcavalry/springbootsidecardemo:0.0.1

制作应用升级版的Docker镜像

  • 为了验证K8S下的应用升级,做好tag为0.0.1的镜像之后,我们改动应用代码,把pom.xml中的版本改成0.0.2,然后再做个镜像,这样稍后在K8S就能验证Pod升级了;
  • 修改HelloController.java的源码,hello方法返回的字符串,之前是Hello version 1.0,现在改成Hello version 2.0
  • 修改pom.xml中的version节点,之前是0.0.1,现在改成0.0.2,由于我们已配置了镜像tag就是工程版本,因此新构建的镜像tag会是0.0.2;
  • 再次执行maven命令构建,然后推送到镜像仓库;
  • 此时我们有两个镜像了:
root@maven:~# docker images | grep sidecar
bolingcavalry/springbootsidecardemo                                 0.0.2        f6ba01c33388        11 hours ago        17.8 MB
bolingcavalry/springbootsidecardemo                                 0.0.1        8105c9ac033b        11 hours ago        17.8 MB
  • 现在镜像已经OK,该准备部署到kubernetes环境了;

编写yaml文件

  • 在可以执行kubectl命令的机器上编写配置文件,Pod的配置javaweb-deploy.yaml文件内容如下:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: javaweb
spec:
  replicas: 1
  template:
    metadata:
     labels:
      name: javaweb
    spec:
     initContainers:
     - image: bolingcavalry/springbootsidecardemo:0.0.1
       name: appjar
       command: ["cp", "/app.jar", "/app"]
       volumeMounts:
       - mountPath: /app
         name: app-volume
     containers:
     - image: openjdk:8u181-jre-alpine3.8
       name: openjdk8u181
       command: ["java","-jar","/webapp/app.jar"]
       volumeMounts:
       - mountPath: /webapp
         name: app-volume
       ports:
       - containerPort: 8080
     volumes:
     - name: app-volume
       emptyDir: {}
  • 从上述配置中,有两处需要注意:
  • 第一,应用镜像被设置为initConteiners类型的容器,被设置执行cp命令将自己的app.jar文件复制到共享的 Volume;
  • 第二,一直运行用于提供服务的容器,来自openjdk镜像的java进程,该进程加载的jar文件就是共享的 Volume中的app.jar;
  • 为了能在浏览器上访问该应用,再部署个service,其配置文件javaweb-svc.yaml的内容如下:
apiVersion: v1
kind: Service
metadata:
  name: javaweb
spec:
  type: NodePort
  ports:
       - port: 8080
         nodePort: 30008
  selector:
    name: javaweb
  • 启动pod和service,在前面的yaml文件所在目录执行命令如下:
kubectl create -f javaweb-deploy.yaml \
&& kubectl create -f javaweb-svc.yaml
  • service的类型是NodePort,因此可以通过Node节点的IP地址访问服务,我这边Node地址为192.168.119.159,因此访问地址就是:http://192.168.119.159:30008/hello ,在浏览器访问返回如下内容:

image.png

  • 符合预期,证明主容器加sidecar容器的组合方式是可以正常工作的,接下来试试升级;

升级版本

  • 接下来模拟生产环境的应用升级,前面准好了两个版本的应用镜像:0.0.1和0.0.2,现在K8S环境运行的是0.0.1,咱们将其升级为0.0.2:
  • 修改javaweb-deploy.yaml文件中镜像的tag,从0.0.1改成0.0.2,如下图红框所示:

image.png

  • 在javaweb-deploy.yaml文件所在目录执行如下命令即可完成升级:
kubectl apply -f javaweb-deploy.yaml
  • 在浏览器访问http://192.168.119.159:30008/hello,返回如下内容,可见的确是修改后的应用逻辑:

image.png

  • 升级成功,符合预期;

小结

  • sidecar模式下,仅需更新应用jar打包的镜像,这个镜像可以做到极小;
  • 提供java进程的镜像是固定的,在K8S环境下,一个Node上实际运行着多种pod,如果他们的java进程都由一个镜像提供,其好处是不言而喻的;
  • Spring Boot应用的运行,是由java进程与应用jar文件组成的,很适合sidecar模式,通过容器解耦,通过pod对外服务;
  • 至此,Spring Boot应用在kubernetes的sidecar设计与实战就全部完成了,希望此文能助您将应用以sidecar模式部署在kubernetes,如果您发现我对官网的指导内容理解有误或有偏差,欢迎您的热心指正,谢谢!

参考文档

欢迎关注华为云博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
3月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
631 3
|
1月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
2月前
|
消息中间件 Ubuntu Java
SpringBoot整合MQTT实战:基于EMQX实现双向设备通信
本教程指导在Ubuntu上部署EMQX 5.9.0并集成Spring Boot实现MQTT双向通信,涵盖服务器搭建、客户端配置及生产实践,助您快速构建企业级物联网消息系统。
972 1
|
8月前
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
720 79
|
6月前
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
613 4
|
7月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
702 5
|
9月前
|
Java Spring
SpringBoot 实战 不同参数调用不同实现
本文介绍了如何在实际工作中根据不同的入参调用不同的实现,采用`map+enum`的方式实现优雅且严谨的解决方案。通过Spring Boot框架中的工厂模式或策略模式,避免了使用冗长的`if...else...`语句。文中详细展示了定义接口、实现类、枚举类以及控制器调用的代码示例,确保用户输入的合法性并简化了代码逻辑。
290 1
SpringBoot 实战 不同参数调用不同实现
|
10月前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
2485 17
Spring Boot 两种部署到服务器的方式
|
9月前
|
Kubernetes 持续交付 开发工具
阿里云协同万兴科技落地ACK One GitOps方案,全球多机房应用自动化发布,效率提升50%
阿里云协同万兴科技落地ACK One GitOps方案,全球多机房应用自动化发布,效率提升50%
324 2
|
8月前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
236 0
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明

推荐镜像

更多