微服务seata解析部署使用全流程

官网地址: Seata 是什么? | Apache Seata

1、Seata术语

用来管理分布式事务,由阿里巴巴出品。

【1、TC (Transaction Coordinator) - 事务协调者】

用来维护事务的,包括主事务和分支事务。

【2、TM (Transaction Manager) - 事务管理器】

管理事务的,决定了事务什么时候开启,什么时候结束,提交还是回滚。

【3、RM (Resource Manager) - 资源管理器】

监视分支事务的状态,和TC做数据交互,把分支事务状态告诉TC。

2、SEATA 的分布式事务解决方案

seata提供了4种方式,分别是AT、TCC、SAGA、XA

①AT----该模式是Seata中最常用的模式,使用于大多数业务场景,特别是对性能要求较高时,(通过记录数据的快照,来支持事务的回滚)

原理:事务执行中Seata会保存数据执行前的快照,提交时,如果发生异常时就会回滚

优点:实现简单,性能高,适用于读多写少的场景

缺点:对数据库要求高

②TCC---适用于业务逻辑复杂,需要严格控制使用场景

三个阶段:try(尝试)、confirm(确认)、Cancke(取消)

原理:try执行预留操作,锁定必要资源、confirm如果操作都成功,确认操作、Cancle如果某操作失败,则取消所有操作

优点:控制力强,适合需要一致性的复杂业务场景

缺点:实现复杂

③Saga---长事务处理方式

使用:适用于微服务架构中,将大事务分解成小事务,实现事务的一致性和可靠性,具有继承性,可靠性和强一致性

优点:性能高,无锁,参与者可异步,高吞吐,实现简单

缺点:没有锁,会哟脏写,时效性差

④AX----使用两个阶段提交,保证事务的一致性和可靠性

原理:所有参与的资源管理器再提交前准备好,记录事务状态

优点:一致性、可靠性、标准化

缺点:会引入额外的性能开销,导致延迟增加、有阻塞风险,影响系统的吞吐量和并发性、易发生单点故障

3、安装seata 【13分钟】

1、基于操作系统环境安装

Releases · apache/incubator-seata · GitHub

2、拉取docker镜像

docker pull seataio/seata-server:1.3.0

3、拷贝配置文件

先启动seata,将seata-server目录下的文件拷贝到虚拟机中

#执行命令1:

docker run -d --name seata -p 8091:8091 seataio/seata-server:1.3.0

虚拟机里,创建/usr/local/docker/seata目录。

然后执行拷贝任务,拷贝seata:/seata-server 里的资源到/usr/local/docker/seata目录下。

#执行命令2:

docker cp seata:/seata-server /usr/local/docker/seata

【file.conf文件】

file.conf 文件,用来告诉seata 数据存放再哪里的。

修改store.mode为db,修改db相关配置,保存该文件在宿主机本地

transaction log store, only used in seata-server

store {

store mode: file、db、redis

mode = "db"

file store property

file {

store location dir

dir = "sessionStore"

branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions

maxBranchSessionSize = 16384

globe session size , if exceeded throws exceptions

maxGlobalSessionSize = 512

file buffer size , if exceeded allocate new buffer

fileWriteBufferCacheSize = 16384

when recover batch read size

sessionReloadReadSize = 100

async, sync

flushDiskMode = async

}

database store property

db {

the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.

datasource = "druid"

mysql/oracle/postgresql/h2/oceanbase etc.

dbType = "mysql"

driverClassName = "com.mysql.cj.jdbc.Driver"

url = "jdbc:mysql://192.168.43.8:3306/seata"

user = "root"

password = "123456"

minConn = 5

maxConn = 30

globalTable = "global_table"

branchTable = "branch_table"

lockTable = "lock_table"

queryLimit = 100

maxWait = 5000

}

redis store property

redis {

host = "127.0.0.1"

port = "6379"

password = ""

database = "0"

minConn = 1

maxConn = 10

queryLimit = 100

}

}

【registry.conf】

registry.conf 告诉seata,我们的注册中心和配置中心用的是什么,保存该文件在宿主机本地。

修改类型为nacos,修改nacos相关配置,包括registry和config的两组。在nacos中为seata创建一个属于自己的命名空间,它的配置比较多,避免和其他配置公用。

[Linux启动nacos,配置新的命名空间]

registry {

file 、nacos 、eureka、redis、zk、consul、etcd3、sofa

type = "nacos"

nacos {

application = "seata-server"

serverAddr = "192.168.43.8:8848"

group = "SEATA_GROUP"

namespace = "6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8"

cluster = "default"

username = "nacos"

password = "nacos"

}

eureka {

serviceUrl = "http://localhost:8761/eureka"

application = "default"

weight = "1"

}

redis {

serverAddr = "localhost:6379"

db = 0

password = ""

cluster = "default"

timeout = 0

}

zk {

cluster = "default"

serverAddr = "127.0.0.1:2181"

sessionTimeout = 6000

connectTimeout = 2000

username = ""

password = ""

}

consul {

cluster = "default"

serverAddr = "127.0.0.1:8500"

}

etcd3 {

cluster = "default"

serverAddr = "http://localhost:2379"

}

sofa {

serverAddr = "127.0.0.1:9603"

application = "default"

region = "DEFAULT_ZONE"

datacenter = "DefaultDataCenter"

cluster = "default"

group = "SEATA_GROUP"

addressWaitTime = "3000"

}

file {

name = "file.conf"

}

}

config {

file、nacos 、apollo、zk、consul、etcd3

type = "nacos"

nacos {

serverAddr = "192.168.43.8:8848"

group = "SEATA_GROUP"

namespace = "6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8"

username = "nacos"

password = "nacos"

}

consul {

serverAddr = "127.0.0.1:8500"

}

apollo {

appId = "seata-server"

apolloMeta = "http://192.168.1.204:8801"

namespace = "application"

}

zk {

serverAddr = "127.0.0.1:2181"

sessionTimeout = 6000

connectTimeout = 2000

username = ""

password = ""

}

etcd3 {

serverAddr = "http://localhost:2379"

}

file {

name = "file.conf"

}

}

4、创建seata在nacos配置中心的配置文件

官方文件模板所在路径: https://github.com/seata/seata/blob/develop/script/config-center/config.txt

创建一个config.txt文件,将以下内容改成自己的信息,粘贴进去保存。

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html

#Transport configuration, for client and server

transport.type=TCP

transport.server=NIO

transport.heartbeat=true

transport.enableTmClientBatchSendRequest=false

transport.enableRmClientBatchSendRequest=true

transport.enableTcServerBatchSendResponse=false

transport.rpcRmRequestTimeout=30000

transport.rpcTmRequestTimeout=30000

transport.rpcTcRequestTimeout=30000

transport.threadFactory.bossThreadPrefix=NettyBoss

transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker

transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler

transport.threadFactory.shareBossWorker=false

transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector

transport.threadFactory.clientSelectorThreadSize=1

transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread

transport.threadFactory.bossThreadSize=1

transport.threadFactory.workerThreadSize=default

transport.shutdown.wait=3

transport.serialization=seata

transport.compressor=none

#Transaction routing rules configuration, only for the client

---------------------------------------------------

service.vgroupMapping.my_tx_group=default

---------------------------------------------------

#If you use a registry, you can ignore it

service.default.grouplist=127.0.0.1:8091

service.enableDegrade=false

service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client

client.rm.asyncCommitBufferLimit=10000

client.rm.lock.retryInterval=10

client.rm.lock.retryTimes=30

client.rm.lock.retryPolicyBranchRollbackOnConflict=true

client.rm.reportRetryCount=5

client.rm.tableMetaCheckEnable=true

client.rm.tableMetaCheckerInterval=60000

client.rm.sqlParserType=druid

client.rm.reportSuccessEnable=false

client.rm.sagaBranchRegisterEnable=false

client.rm.sagaJsonParser=fastjson

client.rm.tccActionInterceptorOrder=-2147482648

client.tm.commitRetryCount=5

client.tm.rollbackRetryCount=5

client.tm.defaultGlobalTransactionTimeout=60000

client.tm.degradeCheck=false

client.tm.degradeCheckAllowTimes=10

client.tm.degradeCheckPeriod=2000

client.tm.interceptorOrder=-2147482648

client.undo.dataValidation=true

client.undo.logSerialization=jackson

client.undo.onlyCareUpdateColumns=true

server.undo.logSaveDays=7

server.undo.logDeletePeriod=86400000

client.undo.logTable=undo_log

client.undo.compress.enable=true

client.undo.compress.type=zip

client.undo.compress.threshold=64k

#For TCC transaction mode

tcc.fence.logTableName=tcc_fence_log

tcc.fence.cleanPeriod=1h

#Log rule configuration, for client and server

log.exceptionRate=100

#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.

---------------------------------------------------

store.mode=db

store.lock.mode=db

store.session.mode=db

---------------------------------------------------

#Used for password encryption

store.publicKey=

#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.

store.file.dir=file_store/data

store.file.maxBranchSessionSize=16384

store.file.maxGlobalSessionSize=512

store.file.fileWriteBufferCacheSize=16384

store.file.flushDiskMode=async

store.file.sessionReloadReadSize=100

#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.

store.db.datasource=druid

store.db.dbType=mysql

---------------------------------------------------

store.db.driverClassName=com.mysql.cj.jdbc.Driver

store.db.url=jdbc:mysql://192.168.43.8:3306/seata?useUnicode=true&rewriteBatchedStatements=true

store.db.user=root

store.db.password=123456

---------------------------------------------------

store.db.minConn=5

store.db.maxConn=30

store.db.globalTable=global_table

store.db.branchTable=branch_table

store.db.distributedLockTable=distributed_lock

store.db.queryLimit=100

store.db.lockTable=lock_table

store.db.maxWait=5000

#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.

store.redis.mode=single

store.redis.single.host=127.0.0.1

store.redis.single.port=6379

store.redis.sentinel.masterName=

store.redis.sentinel.sentinelHosts=

store.redis.maxConn=10

store.redis.minConn=1

store.redis.maxTotal=100

store.redis.database=0

store.redis.password=

store.redis.queryLimit=100

#Transaction rule configuration, only for the server

server.recovery.committingRetryPeriod=1000

server.recovery.asynCommittingRetryPeriod=1000

server.recovery.rollbackingRetryPeriod=1000

server.recovery.timeoutRetryPeriod=1000

server.maxCommitRetryTimeout=-1

server.maxRollbackRetryTimeout=-1

server.rollbackRetryTimeoutUnlockEnable=false

server.distributedLockExpireTime=10000

server.xaerNotaRetryTimeout=60000

server.session.branchAsyncQueueSize=5000

server.session.enableBranchAsyncRemove=false

server.enableParallelRequestHandle=false

#Metrics configuration, only for the server

metrics.enabled=false

metrics.registryType=compact

metrics.exporterList=prometheus

metrics.exporterPrometheusPort=9898

修改的内容

service.vgroupMapping.my_tx_group=default

store.mode=db

store.lock.mode=db

store.session.mode=db

store.db.driverClassName=com.mysql.cj.jdbc.Driver

store.db.url=jdbc:mysql://192.168.43.8:3306/seata?useUnicode=true&rewriteBatchedStatements=true

store.db.user=root

store.db.password=123456

5、创建导入config.txt到nacos的脚本

官方文件模板所在路径: https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh

创建 nacos-config.sh 文件,粘贴以下内容,没有内容更改。

#!/bin/sh

Copyright 1999-2019 Seata.io Group.

Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at、

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

while getopts ":h:p:g:t:u:w:" opt

do

case $opt in

h)

host=$OPTARG

;;

p)

port=$OPTARG

;;

g)

group=$OPTARG

;;

t)

tenant=$OPTARG

;;

u)

username=$OPTARG

;;

w)

password=$OPTARG

;;

?)

echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] "

exit 1

;;

esac

done

if [ -z ${host} ]; then

host=localhost

fi

if [ -z ${port} ]; then

port=8848

fi

if [ -z ${group} ]; then

group="SEATA_GROUP"

fi

if [ -z ${tenant} ]; then

tenant=""

fi

if [ -z ${username} ]; then

username=""

fi

if [ -z ${password} ]; then

password=""

fi

nacosAddr=host:port

contentType="content-type:application/json;charset=UTF-8"

echo "set nacosAddr=$nacosAddr"

echo "set group=$group"

urlencode() {

length="${#1}"

i=0

while [ $length -gt $i ]; do

char="{1:i:1}"

case $char in

[a-zA-Z0-9.~_-]) printf $char ;;

*) printf '%%%02X' "'$char" ;;

esac

i=`expr $i + 1`

done

}

failCount=0

tempLog=$(mktemp -u)

function addConfig() {

dataId=`urlencode $1`

content=`urlencode $2`

curl -X POST -H "{contentType}" "http://nacosAddr/nacos/v1/cs/configs?dataId=dataId\&group=group&content=content\&tenant=tenant&username=username\&password=password" >"${tempLog}" 2>/dev/null

if [ -z (cat "{tempLog}") ]; then

echo " Please check the cluster status. "

exit 1

fi

if [ "(cat "{tempLog}")" == "true" ]; then

echo "Set $1=$2 successfully "

else

echo "Set $1=$2 failure "

failCount=`expr $failCount + 1`

fi

}

count=0

COMMENT_START="#"

for line in $(cat (dirname "PWD")/config.txt | sed s/[[:space:]]//g); do

if [[ "line" =\~ \^"{COMMENT_START}".* ]]; then

continue

fi

count=`expr $count + 1`

key=${line%%=*}

value=${line#*=}

addConfig "{key}" "{value}"

done

echo "========================================================================="

echo " Complete initialization parameters, total-count:count , failure-count:failCount "

echo "========================================================================="

if [ ${failCount} -eq 0 ]; then

echo " Init nacos config finished, please start seata-server. "

else

echo " init nacos config fail. "

fi

6、导入nacos配置

#循环 不需要确认 删除 某一目录以及该目录下的文件和子目录。

rm -rf seata-server

将 config.txt文件添加到seata文件夹里,同时创建子目录nacos;将nacos-config.sh文件放到nacos目录里。

导入前要启动nacos并关闭防火墙,执行如下命令:为该文件分配一个执行的权限。

复制代码
#命令1:为该文件分配一个执行的权限。
chmod +x nacos-config.sh

修改下面命令里的内容,包括ip和Id。

#命令2:

sh nacos-config.sh -h 192.168.43.8 -p 8848 -g SEATA_GROUP -t 6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8 -u nacos -w nacos

报错说含/r的错误,是因为windows下文件的换行符合Linux不同导致的,执行以下命令:

#命令3:

sed -i 's/\r//' nacos-config.sh

#然后再重新执行一遍命令2:

sh nacos-config.sh -h 192.168.43.8 -p 8848 -g SEATA_GROUP -t 6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8 -u nacos -w nacos

7、复制配置文件

seata目录下原资源可以全部删掉了。

将file.conf和registry.conf文件复制到/usr/local/docker/seata/config下,为再次创建seata容器做配置准备。

为config目录授权:

复制代码
chmod 777 config

8、关闭并删除之前的seata容器

停掉刚才启动的seata,而且它以后也用不到了,可以删除。

9、数据库配置

启动seata需要有以下几张表的基础支持,所以在启动前,先创建如下数据库表。启动mysql数据库。

seata需要创建三张表:

全局事务---global_table

分支事务---branch_table

全局锁-----lock_table

建表语句

DROP TABLE

IF

EXISTS `global_table`;

CREATE TABLE `global_table` (

`xid` VARCHAR ( 128 ) NOT NULL,

`transaction_id` BIGINT,

`status` TINYINT NOT NULL,

`application_id` VARCHAR ( 32 ),

`transaction_service_group` VARCHAR ( 32 ),

`transaction_name` VARCHAR ( 64 ),

`timeout` INT,

`begin_time` BIGINT,

`application_data` VARCHAR ( 2000 ),

`gmt_create` datetime,

`gmt_modified` datetime,

PRIMARY KEY ( `xid` ),

KEY `idx_gmt_modified_status` ( `gmt_modified`, `status` ),

KEY `idx_transaction_id` ( `transaction_id` )

);

DROP TABLE

IF

EXISTS `branch_table`;

CREATE TABLE `branch_table` (

`branch_id` BIGINT NOT NULL,

`xid` VARCHAR ( 128 ) NOT NULL,

`transaction_id` BIGINT,

`resource_group_id` VARCHAR ( 32 ),

`resource_id` VARCHAR ( 256 ),

`lock_key` VARCHAR ( 128 ),

`branch_type` VARCHAR ( 8 ),

`status` TINYINT,

`client_id` VARCHAR ( 64 ),

`application_data` VARCHAR ( 2000 ),

`gmt_create` datetime,

`gmt_modified` datetime,

PRIMARY KEY ( `branch_id` ),

KEY `idx_xid` ( `xid` )

);

DROP TABLE

IF

EXISTS `lock_table`;

CREATE TABLE `lock_table` (

`row_key` VARCHAR ( 128 ) NOT NULL,

`xid` VARCHAR ( 96 ),

`transaction_id` LONG,

`branch_id` LONG,

`resource_id` VARCHAR ( 256 ),

`table_name` VARCHAR ( 100 ),

`pk` VARCHAR ( 36 ),

`gmt_create` datetime,

`gmt_modified` datetime,

PRIMARY KEY ( `row_key` )

);

10、启动seata

在执行启动命令前要先配置seata数据库

docker run --name seata \

-p 8091:8091 \

-e SEATA_IP=192.168.43.8 \

-e SEATA_PORT=8091 \

-e SEATA_CONFIG_NAME=file:/root/seata-config/registry \

-v /usr/local/docker/seata/config:/root/seata-config \

-d seataio/seata-server:1.3.0

查看日志信息,能看到端口号8091 就是启动成功了。

#查看上面的启动命令日志:

docker logs -f seata

4、应用seata

1.准备项目:

创建两个seata工程,分别是seata1 和 seata2,还是以openfeign工程做为例子,实现整合mybatis连接数据库两张表的添加功能。

2.准备数据库表:

#建表代码

CREATE TABLE `score` (

`id` varchar(50) NOT NULL,

`name` varchar(50) NULL,

`score` double(7, 2) NULL ,

PRIMARY KEY (`id`) USING BTREE

)

CREATE TABLE `user` (

`id` varchar(50) NOT NULL,

`name` varchar(50) NULL,

`password` varchar(50) NULL,

PRIMARY KEY (`id`) USING BTREE

)

##undo_log表的创建语句,在下面的4.2里。

3.准备阶段,运行项目

【正常运行】

没有异常,成功运行,两张表里都有数据。

【添加异常】

添加异常,失败运行,两张表都添加数据成功了居然

【添加Spring事务】

两个工程添加事务注解,成绩添加数据成功,用户添加数据失败

===以上是分布式的情况下,事务没有统一管理。

4.使用seata

以下操作,每个工程项目里都操作一遍。

1、添加依赖

<dependency>

<groupId>com.alibaba.cloud</groupId>

<artifactId>spring-cloud-starter-alibaba-seata</artifactId>

</dependency>

2、各自数据库中创建UNDO_LOG表

给每个微服务的数据库都创建这张表才能应用seata

要求:具有InnoDB引擎的MySQL。

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log

CREATE TABLE `undo_log` (

`id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT,

`branch_id` BIGINT ( 20 ) NOT NULL,

`xid` VARCHAR ( 100 ) NOT NULL,

`context` VARCHAR ( 128 ) NOT NULL,

`rollback_info` LONGBLOB NOT NULL,

`log_status` INT ( 11 ) NOT NULL,

`log_created` datetime NOT NULL,

`log_modified` datetime NOT NULL,

`ext` VARCHAR ( 100 ) DEFAULT NULL,

PRIMARY KEY ( `id` ),

UNIQUE KEY `ux_undo_log` ( `xid`, `branch_id` )

) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

3、修改启动类

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

4、添加配置文件

在配置文件application.yml中,添加如下配置信息:

seata:

application-id: ${spring.application.name}

enabled: true

tx-service-group: my_tx_group

registry:

type: nacos

nacos:

server-addr: 172.19.186.247:8848

namespace: c05aaf00-2701-4d2e-8710-318752481731

group: SEATA_GROUP

username: nacos

password: nacos

config:

type: nacos

nacos:

server-addr: 172.19.186.247:8848

namespace: c05aaf00-2701-4d2e-8710-318752481731

group: SEATA_GROUP

username: nacos

password: nacos

service:

vgroup-mapping:

my_tx_group: default

feign:

httpclient:

connection-timeout: 600000

connection-timer-repeat: 30000

tx-service-group: my_tx_group

service: vgroup-mapping: my_tx_group: default

与nacos的配置要一致,nacos的配置是tx-service-group.my_tx_group=default

5、添加数据源配置

com.jr.config包下,创建下面的配置文件类。

import com.alibaba.druid.pool.DruidDataSource;

import io.seata.rm.datasource.DataSourceProxy;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

@Configuration

public class DataSourceProxyConfig {

@Bean

@ConfigurationProperties(prefix = "spring.datasource")

public DruidDataSource druidDataSource() {

return new DruidDataSource();

}

@Bean

@Primary

public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {

return new DataSourceProxy(druidDataSource);

}

}

6、添加事务注解

更改之前ServletImpl类方法里的事务注解@Transactional(rollbackFor = Exception.class),改为下面注解:

@GlobalTransactional(rollbackFor = Exception.class,timeoutMills = 300000)

7、完成上述配置,运行结果

【有bug代码时】

两张表里都没有添加进数据,事务回滚。

【没有bug代码时】

数据添加成功

8、解决报错

相关推荐
Smile丶凉轩6 分钟前
微服务即时通讯系统的实现(客户端)----(1)
微服务·云原生·架构
南慕小白8 分钟前
云原生后端
云原生
小安运维日记2 小时前
CKA认证 | Day3 K8s管理应用生命周期(上)
运维·云原生·容器·kubernetes·云计算·k8s
politeboy3 小时前
关于k8s中镜像的服务端口被拒绝的问题
云原生·容器·kubernetes
weixin_438197383 小时前
K8S创建云主机配置docker仓库
linux·云原生·容器·eureka·kubernetes
问道飞鱼13 小时前
【微服务知识】开源RPC框架Dubbo入门介绍
微服务·rpc·开源·dubbo
白总Server14 小时前
JVM解说
网络·jvm·物联网·安全·web安全·架构·数据库架构
ggaofeng14 小时前
通过命令学习k8s
云原生·容器·kubernetes
CodingBrother14 小时前
软考之面向服务架构SOA
微服务·架构
qq_道可道17 小时前
K8S升级到1.24后,切换运行时导致 dind 构建镜像慢根因定位与解决
云原生·容器·kubernetes