Skip to content

Commit

Permalink
[0.6.0.dev240115] (#215)
Browse files Browse the repository at this point in the history
repo-sync-2024-01-15T15:29:33+0800
  • Loading branch information
haha-zwx-ooo authored Jan 15, 2024
1 parent 6994ca0 commit 352ef97
Show file tree
Hide file tree
Showing 122 changed files with 2,993 additions and 1,284 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`Fixed` for any bug fixes.
`Security` in case of vulnerabilities.

## [0.6.0.dev240115] - 2023-01-15
### Added
- Add network error troubleshooting document.
- Add steps for pre creating data tables in the process of deploying kusica on K8s.

### Changed
- The token from lite to master supports rotation.
### Fixed
- When deploying using deploy.sh, no kuscia API client certificate was generated.

## [0.5.0b0] - 2024-1-8
### Added
- Support deploying kuscia on K8s.
- Support running algorithm images based on runp and runk modes.
- Support configuring Path prefix in domain public URL addresses.

### Changed
- Optimize deployment configuration and add configuration documentation.
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

# Image URL to use all building image targets
DATETIME = $(shell date +"%Y%m%d%H%M%S")
KUSCIA_VERSION_TAG = $(shell git describe --abbrev=7 --always)
COMMIT_ID = $(shell git log -1 --pretty="format:%h")
TAG = ${KUSCIA_VERSION_TAG}-${DATETIME}-${COMMIT_ID}
KUSCIA_VERSION_TAG = $(shell git describe --tags --always)
TAG = ${KUSCIA_VERSION_TAG}-${DATETIME}
IMG := secretflow/kuscia:${TAG}

# TEST_SUITE used by integration test
Expand Down
5 changes: 4 additions & 1 deletion cmd/kuscia/master/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error {
nlog.Info("Scheduler and controllers are all started")
// wait any controller failed
} else {
modules.RunK3s(runCtx, cancel, conf)
if err := modules.RunK3s(runCtx, cancel, conf); err != nil {
nlog.Errorf("k3s start failed: %s", err)
return err
}
// make clients after k3s start
conf.MakeClients()

Expand Down
2 changes: 2 additions & 0 deletions cmd/kuscia/modules/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ func (s *containerdModule) execPreCmds(ctx context.Context) error {

func (s *containerdModule) WaitReady(ctx context.Context) error {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
tickerReady := time.NewTicker(time.Second)
defer tickerReady.Stop()
for {
select {
case <-tickerReady.C:
Expand Down
2 changes: 2 additions & 0 deletions cmd/kuscia/modules/datamesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ func (m dataMeshModule) Run(ctx context.Context) error {

func (m dataMeshModule) WaitReady(ctx context.Context) error {
timeoutTicker := time.NewTicker(30 * time.Second)
defer timeoutTicker.Stop()
checkTicker := time.NewTicker(1 * time.Second)
defer checkTicker.Stop()
for {
select {
case <-checkTicker.C:
Expand Down
1 change: 1 addition & 0 deletions cmd/kuscia/modules/domainroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func (d *domainRouteModule) Run(ctx context.Context) error {

func (d *domainRouteModule) WaitReady(ctx context.Context) error {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
select {
case <-commands.ReadyChan:
return nil
Expand Down
2 changes: 2 additions & 0 deletions cmd/kuscia/modules/envoy.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ func (s *envoyModule) logRotate(ctx context.Context) {

func (s *envoyModule) WaitReady(ctx context.Context) error {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
tickerReady := time.NewTicker(time.Second)
defer tickerReady.Stop()
for {
select {
case <-ctx.Done():
Expand Down
44 changes: 39 additions & 5 deletions cmd/kuscia/modules/k3s.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ import (

pkgcom "github.com/secretflow/kuscia/pkg/common"
"github.com/secretflow/kuscia/pkg/utils/common"
"github.com/secretflow/kuscia/pkg/utils/datastore"
"github.com/secretflow/kuscia/pkg/utils/network"
"github.com/secretflow/kuscia/pkg/utils/nlog/ljwriter"
"github.com/secretflow/kuscia/pkg/utils/paths"
"github.com/secretflow/kuscia/pkg/utils/process"
tlsutils "github.com/secretflow/kuscia/pkg/utils/tls"

"github.com/secretflow/kuscia/pkg/utils/nlog"
Expand All @@ -59,8 +62,27 @@ type k3sModule struct {
}

func (s *k3sModule) readyz(host string) error {

// check k3s process
if !process.CheckExists("k3s") {
errMsg := "process [k3s] is not exists"
nlog.Error(errMsg)
return errors.New(errMsg)
}

cl := http.Client{}
caCertFile, err := os.ReadFile(filepath.Join(s.dataDir, "server/tls/server-ca.crt"))
// check file exist
serverCaFilePath := filepath.Join(s.dataDir, "server/tls/server-ca.crt")
clientAdminCrtFilePath := filepath.Join(s.dataDir, "server/tls/client-admin.crt")
clientAdminKeyFilePath := filepath.Join(s.dataDir, "server/tls/client-admin.key")

if fileExistError := paths.CheckAllFileExist(serverCaFilePath, clientAdminCrtFilePath, clientAdminKeyFilePath); fileExistError != nil {
err := fmt.Errorf("%s. Please check the k3s service is running successfully ", fileExistError)
nlog.Error(err)
return err
}

caCertFile, err := os.ReadFile(serverCaFilePath)
if err != nil {
nlog.Error(err)
return err
Expand All @@ -71,13 +93,13 @@ func (s *k3sModule) readyz(host string) error {
nlog.Error(msg)
return fmt.Errorf("%s", msg)
}
certPEMBlock, err := os.ReadFile(filepath.Join(s.dataDir, "server/tls/client-admin.crt"))
certPEMBlock, err := os.ReadFile(clientAdminCrtFilePath)
if err != nil {
nlog.Error(err)
return err
}

keyPEMBlock, err := os.ReadFile(filepath.Join(s.dataDir, "server/tls/client-admin.key"))
keyPEMBlock, err := os.ReadFile(clientAdminKeyFilePath)
if err != nil {
nlog.Error(err)
return err
Expand Down Expand Up @@ -194,14 +216,19 @@ func (s *k3sModule) Run(ctx context.Context) error {

envs := os.Environ()
envs = append(envs, "CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=3650")
if os.Getenv("KINE_SKIP_INIT_MYSQL") == "" {
envs = append(envs, "KINE_SKIP_INIT_MYSQL=true")
}
cmd.Env = envs
return cmd
})
}

func (s *k3sModule) WaitReady(ctx context.Context) error {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
tickerReady := time.NewTicker(time.Second)
defer tickerReady.Stop()
for {
select {
case <-ctx.Done():
Expand All @@ -220,7 +247,14 @@ func (s *k3sModule) Name() string {
return "k3s"
}

func RunK3s(ctx context.Context, cancel context.CancelFunc, conf *Dependencies) Module {
func RunK3s(ctx context.Context, cancel context.CancelFunc, conf *Dependencies) error {
// check DatastoreEndpoint
if err := datastore.CheckDatastoreEndpoint(conf.Master.DatastoreEndpoint); err != nil {
nlog.Error(err)
cancel()
return err
}

m := NewK3s(conf)
go func() {
if err := m.Run(ctx); err != nil {
Expand All @@ -247,7 +281,7 @@ func RunK3s(ctx context.Context, cancel context.CancelFunc, conf *Dependencies)
nlog.Info("k3s is ready")
}

return m
return nil
}

func applyCRD(conf *Dependencies) error {
Expand Down
2 changes: 2 additions & 0 deletions cmd/kuscia/modules/kusciaapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ func (m kusciaAPIModule) Run(ctx context.Context) error {

func (m kusciaAPIModule) WaitReady(ctx context.Context) error {
timeoutTicker := time.NewTicker(30 * time.Second)
defer timeoutTicker.Stop()
checkTicker := time.NewTicker(1 * time.Second)
defer checkTicker.Stop()
for {
select {
case <-checkTicker.C:
Expand Down
2 changes: 2 additions & 0 deletions cmd/kuscia/modules/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ func (t *transportModule) Name() string {

func (t *transportModule) WaitReady(ctx context.Context) error {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
tickerReady := time.NewTicker(time.Second)
defer tickerReady.Stop()
for {
select {
case <-ctx.Done():
Expand Down
13 changes: 8 additions & 5 deletions docs/deployment/K8s_deployment_kuscia/K8s_master_lite_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
> Tips:k8s 部署模式暂不支持训练,仅支持预测服务
## 部署 master
部署 master 需提前准备好 mysql 数据库,数据库帐号密码等信息配置在步骤三 Configmap 中(database 需要提前手动创建好并且 mysql 账户需要具有创建表的权限)

### 前置准备

部署 master 需提前准备好 mysql 数据库表并且符合[kuscia配置](../kuscia_config_cn.md#id3)中的规范,数据库帐号密码等信息配置在步骤三 configmap 中。

### 步骤一:创建 Namespace
> 创建 namespace 需要先获取 create 权限,避免出现 "namespaces is forbidden" 报错
Expand Down Expand Up @@ -63,15 +66,15 @@ kubectl create ns lite-alice

### 步骤二:创建 Service

获取 [service.yaml](https://github.com/secretflow/kuscia/blob/main/hack/k8s/lite/service.yaml) 文件,如果 master 与 lite 不在一个 k8s 集群内,可以将 master service 的端口暴露方式改为 LoadBalancer(公有云,例如:阿里云) 或者 NodePort,并在 configmap 的 masterEndpoint 字段改为可正常访问的地址,创建 service
获取 [service.yaml](https://github.com/secretflow/kuscia/blob/main/hack/k8s/lite/service.yaml) 文件,如果 master 与 lite 不在一个 k8s 集群内,可以将 master service 的端口暴露方式改为 [LoadBalancer](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#loadbalancer)(公有云,例如:[阿里云](https://help.aliyun.com/zh/ack/serverless-kubernetes/user-guide/use-annotations-to-configure-load-balancing)) 或者 [NodePort](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#type-nodeport),并在 configmap 的 masterEndpoint 字段改为可正常访问的地址,创建 service
```bash
kubectl create -f service.yaml
```

### 步骤三:创建 Configmap
ConfigMap 是用来配置 kuscia 的配置文件,详细的配置文件介绍参考[kuscia配置](../kuscia_config_cn.md)

部署 configmap 需要提前在 master 节点 pod 内生成 domainID 以及 token,并填写到 configmap 的 domainID 和 liteDeployToken 字段中,私钥可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh` 生成并填写到 domainKeyData 字段中
部署 configmap 需要提前在 master 节点 pod 内生成 domainID 以及 Token,并填写到 configmap 的 domainID 和 liteDeployToken 字段中,私钥可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh` 生成并填写到 domainKeyData 字段中
> 注意:<br>
1、目前节点私钥仅支持 pkcs#1 格式: "BEGIN RSA PRIVATE KEY/END RSA PRIVATE KEY"<br>
2、修改 Configmap 配置后,需执行 kubectl delete po &#36;{pod-name} -n &#36;{namespace} 重新拉起 pod 生效
Expand All @@ -82,9 +85,9 @@ lite-bob 配置与 lite-alice 一样,下面以 alice 为例:
```bash
kubectl exec -it ${master_pod_name} bash -n kuscia-master
scripts/deploy/add_domain_lite.sh alice
# 示例 token
# 示例 Token
BMC4xjNqa7uAmWmyXLuJ4rrZw6brZeax
# 如果token遗忘了,可以通过该命令重新获取
# 如果 Token 遗忘了,可以通过该命令重新获取
kubectl get domain alice -o=jsonpath='{.status.deployTokenStatuses[?(@.state=="unused")].token}' && echo
```

Expand Down
7 changes: 5 additions & 2 deletions docs/deployment/K8s_deployment_kuscia/K8s_p2p_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
> Tips:k8s 部署模式暂不支持训练,仅支持预测服务
## 部署 autonomy
部署 autonomy 需提前准备好 mysql 数据库,数据库帐号密码等信息配置在步骤三 Configmap 中(database 需要提前手动创建好并且 mysql 账户需要具有创建表的权限)

### 前置准备

部署 autonomy 需提前准备好 mysql 数据库表并且符合[kuscia配置](../kuscia_config_cn.md#id3)中的规范,数据库帐号密码等信息配置在步骤三 configmap 中。

### 步骤一:创建 Namespace
> 创建 namespace 需要先获取 create 权限,避免出现 "namespaces is forbidden" 报错
Expand Down Expand Up @@ -65,7 +68,7 @@ kubectl create -f deployment.yaml
alice 和 bob 授权之前可以先检测下相互之间的通信是否正常

建议使用 curl -kvvv http://kuscia-autonomy-bob.autonomy-bob.svc.cluster.local:1080;(此处以 http 为例,https 可以删除 configmap 里的 protocol: NOTLS 字段,重启 pod 生效。LoadBalancer 或者 NodePort 方式可以用 curl -kvvv http://ip:port)检查一下是否访问能通,正常情况下返回的 http 错误码是401,内容是:unauthorized
建议使用 curl -kvvv http://kuscia-autonomy-bob.autonomy-bob.svc.cluster.local:1080;(此处以 http 为例,https 可以删除 configmap 里的 protocol: NOTLS 字段,重启 pod 生效。[LoadBalancer](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#loadbalancer) 或者 [NodePort](https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#type-nodeport) 方式可以用 curl -kvvv http://ip:port)检查一下是否访问能通,正常情况下返回的 http 错误码是401,内容是:unauthorized

示例参考[这里](../K8s_deployment_kuscia/K8s_master_lite_cn.md#id6)

Expand Down
28 changes: 20 additions & 8 deletions docs/deployment/deploy_master_lite_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ docker run --rm --pull always $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/depl
启动 alice。默认会在当前目录下创建 kuscia-lite-alice-certs 目录用来存放 alice 的公私钥和证书。默认会在当前目录下创建 kuscia-lite-alice-data 目录用来存放 alice 的数据:
```bash
# -n 参数传递的是节点 ID
# -t 参数传递的是节点部署的 token
# -t 参数传递的是节点部署的 Token
# -m 参数传递的是 master 容器对外暴露的 https://IP:PORT,如上文中 master 的 ip 是1.1.1.1,端口是18080
# -p 参数传递的是节点容器映射到主机的端口,保证和主机上现有的端口不冲突即可
./deploy.sh lite -n alice -t abcdefg -m https://1.1.1.1:18080 -p 28080
Expand All @@ -137,8 +137,8 @@ docker run --rm --pull always $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/depl
#### 部署 lite 节点 bob
在部署 bob 节点之前,我们需要在 master 注册 bob 节点,并获取到部署时需要用到的 token
执行以下命令,完成节点注册并从返回中得到 token (下文将以hijklmn为例)。
在部署 bob 节点之前,我们需要在 master 注册 bob 节点,并获取到部署时需要用到的 Token
执行以下命令,完成节点注册并从返回中得到 Token (下文将以hijklmn为例)。
```bash
docker exec -it ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh bob
```
Expand All @@ -147,7 +147,7 @@ docker exec -it ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh bob
hijklmn
```
如果token遗忘了,可以通过该命令重新获取
如果 Token 遗忘了,可以通过该命令重新获取
```bash
docker exec -it ${USER}-kuscia-master kubectl get domain bob -o=jsonpath='{.status.deployTokenStatuses[?(@.state=="unused")].token}' && echo
```
Expand All @@ -173,7 +173,7 @@ docker run --rm --pull always $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/depl
启动 bob。默认会在当前目录下创建 kuscia-lite-bob-certs 目录用来存放 bob 的公私钥和证书。默认会在当前目录下创建 kuscia-lite-bob-data 目录用来存放 bob 的数据:
```bash
# -n 参数传递的是节点 ID
# -t 参数传递的是节点部署的 token
# -t 参数传递的是节点部署的 Token
# -m 参数传递的是 master 容器对外暴露的 https://IP:PORT,如上文中 master 的 ip 是1.1.1.1,端口是18080
# -p 参数传递的是节点容器映射到主机的端口,保证和主机上现有的端口不冲突即可
./deploy.sh lite -n bob -t hijklmn -m https://1.1.1.1:18080 -p 38080
Expand Down Expand Up @@ -243,13 +243,24 @@ docker exec -it ${USER}-kuscia-master scripts/deploy/create_domaindata_bob_table
登录到安装 alice 的机器上,为 alice 的测试数据创建 domaindatagrant
```bash
docker exec -it ${USER}-kuscia-lite-alice curl https://127.0.0.1:8070/api/v1/datamesh/domaindatagrant/create -X POST -H 'content-type: application/json' -d '{"author":"alice","domaindata_id":"alice-table","grant_domain":"bob"}' --cacert var/certs/ca.crt --cert var/certs/ca.crt --key var/certs/ca.key
docker exec -it root-kuscia-lite-alice curl -X POST 'https://127.0.0.1:8082/api/v1/domaindatagrant/create' --header "Token: $(cat kuscia-lite-alice-certs/token)" --header 'Content-Type: application/json' -d '{
"grant_domain": "bob",
"description": {"domaindatagrant":"alice-bob"},
"domain_id": "alice",
"domaindata_id": "alice-table"
}' --cacert /home/kuscia/var/certs/ca.crt --cert /home/kuscia/var/certs/ca.crt --key /home/kuscia/var/certs/ca.key
```
同理,登录到安装 bob 的机器上,为 bob 的测试数据创建 domaindatagrant
```bash
docker exec -it ${USER}-kuscia-lite-bob curl https://127.0.0.1:8070/api/v1/datamesh/domaindatagrant/create -X POST -H 'content-type: application/json' -d '{"author":"bob","domaindata_id":"bob-table","grant_domain":"alice"}' --cacert var/certs/ca.crt --cert var/certs/ca.crt --key var/certs/ca.key
docker exec -it root-kuscia-lite-bob curl -X POST 'https://127.0.0.1:8082/api/v1/domaindatagrant/create' --header "Token: $(cat kuscia-lite-bob-certs/token)" --header 'Content-Type: application/json' -d '{
"grant_domain": "alice",
"description": {"domaindatagrant":"bob-alice"},
"domain_id": "bob",
"domaindata_id": "bob-table"
}' --cacert /home/kuscia/var/certs/ca.crt --cert /home/kuscia/var/certs/ca.crt --key /home/kuscia/var/certs/ca.key
```
#### 执行测试作业
Expand All @@ -265,8 +276,9 @@ docker exec -it ${USER}-kuscia-master scripts/user/create_example_job.sh
```bash
docker exec -it ${USER}-kuscia-master kubectl get kj
```
任务运行遇到网络错误时,可以参考[这里](../reference/troubleshoot/networktroubleshoot.md)排查
### 部署 secretpad
> 注意:secretpad 的部署依赖 master 的证书与 token,必须与 master 部署在同一台物理机上
> 注意:secretpad 的部署依赖 master 的证书与 Token,必须与 master 部署在同一台物理机上
指定 secretpad 版本:
```bash
Expand Down
19 changes: 15 additions & 4 deletions docs/deployment/deploy_p2p_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

本教程帮助你在多台机器上使用 [点对点组网模式](../reference/architecture_cn.md#点对点组网模式) 来部署 Kuscia 集群。

当前 Kuscia 节点之间只支持 token 的身份认证方式,在跨机器部署的场景下流程较为繁琐,后续本教程会持续更新优化。
当前 Kuscia 节点之间只支持 Token 的身份认证方式,在跨机器部署的场景下流程较为繁琐,后续本教程会持续更新优化。

## 前置准备

Expand Down Expand Up @@ -162,7 +162,12 @@ docker exec -it ${USER}-kuscia-autonomy-alice scripts/deploy/create_domaindata_a
为 alice 的测试数据创建 domaindatagrant

```bash
docker exec -it ${USER}-kuscia-autonomy-alice curl https://127.0.0.1:8070/api/v1/datamesh/domaindatagrant/create -X POST -H 'content-type: application/json' -d '{"author":"alice","domaindata_id":"alice-table","grant_domain":"bob"}' --cacert var/certs/ca.crt --cert var/certs/ca.crt --key var/certs/ca.key
docker exec -it root-kuscia-autonomy-alice curl -X POST 'https://127.0.0.1:8082/api/v1/domaindatagrant/create' --header "Token: $(cat kuscia-autonomy-alice-certs/token)" --header 'Content-Type: application/json' -d '{
"grant_domain": "bob",
"description": {"domaindatagrant":"alice-bob"},
"domain_id": "alice",
"domaindata_id": "alice-table"
}' --cacert /home/kuscia/var/certs/ca.crt --cert /home/kuscia/var/certs/ca.crt --key /home/kuscia/var/certs/ca.key
```
- bob 节点准备测试数据

Expand All @@ -178,7 +183,12 @@ docker exec -it ${USER}-kuscia-autonomy-bob scripts/deploy/create_domaindata_bob
为 bob 的测试数据创建 domaindatagrant

```bash
docker exec -it ${USER}-kuscia-autonomy-bob curl https://127.0.0.1:8070/api/v1/datamesh/domaindatagrant/create -X POST -H 'content-type: application/json' -d '{"author":"bob","domaindata_id":"bob-table","grant_domain":"alice"}' --cacert var/certs/ca.crt --cert var/certs/ca.crt --key var/certs/ca.key
docker exec -it root-kuscia-autonomy-bob curl -X POST 'https://127.0.0.1:8082/api/v1/domaindatagrant/create' --header "Token: $(cat kuscia-autonomy-bob-certs/token)" --header 'Content-Type: application/json' -d '{
"grant_domain": "alice",
"description": {"domaindatagrant":"bob-alice"},
"domain_id": "bob",
"domaindata_id": "bob-table"
}' --cacert /home/kuscia/var/certs/ca.crt --cert /home/kuscia/var/certs/ca.crt --key /home/kuscia/var/certs/ca.key
```

### 执行作业
Expand All @@ -191,4 +201,5 @@ docker exec -it ${USER}-kuscia-autonomy-alice scripts/user/create_example_job.sh
查看作业状态
```bash
docker exec -it ${USER}-kuscia-autonomy-alice kubectl get kj
```
```
任务运行遇到网络错误时,可以参考[这里](../reference/troubleshoot/networktroubleshoot.md)排查
Loading

0 comments on commit 352ef97

Please sign in to comment.