K8S 部署指南
Byzer-lang 作为云原生设计的编程语言,其引擎以及 Byzer Notebook 是支持 K8S 云原生方式部署的,本文主要介绍如何使用 K8S 进行 Byzer 引擎的部署。 在 K8S 上部署,要求您了解并熟悉 K8S 相关的部署以及运维等基本知识请先阅读了解部署的方式以及部署前提条件 部署方式 (推荐)基于 Hel
Byzer-lang 作为云原生设计的编程语言,其引擎以及 Byzer Notebook 是支持 K8S 云原生方式部署的,本文主要介绍如何使用 K8S 进行 Byzer 引擎的部署。
- 在 K8S 上部署,要求您了解并熟悉 K8S 相关的部署以及运维等基本知识
- 请先阅读了解部署的方式以及部署前提条件
部署方式
- (推荐)基于 Helm Chart 的部署方式,可以参考在 Minikube 和 Azure AKS 上部署示例
- 通过 Byzer 社区贡献的 Byzer-K8S 工具来进行部署体验,详情可参考 通过 Byzer-K8S 工具部署 一节
部署前置条件
请前往 K8S 部署前置条件 一节进行环境和安装包的准备工作。
部署示例
K8S 部署前置条件
准备 K8S 服务
Byzer 引擎目前没有对 K8S 版本做要求,但一般推荐云厂商提供的 K8S 服务来进行部署,比如 Azure AKS, AWS EKS 等服务。
下载 Byzer 引擎 Helm 模板
您可以前往官方下载站点 ,该路径存放 Byzer 社区部署组件的模板文件,包含如下内容:
|-- k8s-helm
|-- byzer-lang
|-- {released-version}
|-- byzer-notebook
|-- {released-version}
|-- dolphinscheduler
|-- {released-version}
|-- nginx-ingress
|-- {released-version}
Helm chart 简化了 K8S 部署, 不需要手写 deployment.yaml
, service.yaml
configmap.yaml
等 yaml 文件,。部署 Byzer 引擎只需要下载对应版本的 byzer-lang 以及 nginx-ingress 的 helm-chart,解压后修改 values.yaml
中的参数后,进行部署即可。
准备文件存储系统
在 K8S 上部署 Byzer 引擎时,由于是分布式架构,所以需要一个中心化的分布式存储:
- 在 On Prem 环境中,可以使用 HDFS 或私有对象存储
- 在公有云环境上,一般需要使用该公有云提供的对象存储,比如 Azure Blob Storage,或 AWS S3
- 您也可以使用 JuiceFS 作为统一的文件系统,来对底层的存储进行统一的抽象
注意:
- 由于 Byzer 文件读写使用的是 Hadoop Compatible 的
FileSystem
接口,所以如果使用厂商提供的对象存储,需要下载该对象存储提供的 SDK Jar 包,并基于byzer/byzer-lang-k8s
重新打包镜像,具体的操作请参考不同云厂商的部署章节- 本地测试时,可以使用开源的 minio 来创建一个对象存储
- 一般情况下,建议使用 (JuiceFS + 对象存储) 的组合,Byzer 引擎对接 JuiceFS, JuiceFS 负责对接对象存储
配置 VM Client
该 VM 是作为客户机,来进行和 K8S 服务的交互,进行部署和运维的操作。
- 该虚拟机需要能够和 K8S 服务进行网络连通
- 建议的操作系统为 Ubuntu 18.04+ 或 CentOS 7.x +
- 需要安装
kubectl
以及helm
等工具 - (optional),在使用公有云环境时,还需要安装公有云提供的命令行工具,并在虚拟机中配置公有云托管 K8S 服务的连接信息
在 VM Client 中安装 kubectl
您需要在虚拟机上安装 kubectl,用于和 AKS 服务进行交互。您可以通过下述命令在虚拟机上安装
## Download kubectl
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
## copy kubectl to a folder which inside PATH
$ chmod +x kubectl
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
执行 kubectl version --client
验证 kubectl 是否安装成功,当您看到输出的 Json 信息说明 kubectl 工具已经成功安装至您的环境中
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.4", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-02-16T12:38:05Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}
您可以访问 k8s 官方手册来查看如何在您的操作系统中安装 kubectl 工具
在 VM Client 中安装 Helm
可自行参考 helm 官方手册 来自行在 VM 中安装 Helm,您可以选择使用包管理器(如 brew 或 apt)进行安装,也可以下载 helm 的二进制包来进行安装。
我们以 helm 3.8.1 作为示例来进行二进制包的安装
## download helm 3.8.1 binary package
$ wget https://get.helm.sh/helm-v3.8.1-linux-amd64.tar.gz
## unarchive the tar
$ tar -zxvf helm-v3.8.1-linux-amd64.tar.gz
## move the executable file to /usr/local/bin
$ sudo mv linux-amd64/helm /usr/local/bin
安装完毕后,在 Terminal 中执行 helm version
命令,当您看到输出如下时,说明安装成功
$ helm version
version.BuildInfo{Version:"v3.8.1", GitCommit:"5cb9af4b1b271d11d7a97a71df3ac337dd94ad37", GitTreeState:"clean", GoVersion:"go1.17.5"}
在 K8S 中创建 Namespace 和 Service Account
1. 创建命名空间
K8S 一般使用 namespace 隔离多个部门/业务/应用。管理员可以在不同的 namespace 之间设置资源隔离;不同 namespace 之间可以存在同名的资源。
通过下述命令来创建 byzer
的命名空间
## 创建 byzer namespace
$ kubectl create namespace byzer
## 将 byzer namespace 设置为默认空间
$ kubectl config set-context --current --namespace=byzer
2. 创建 ServiceAccount 并绑定权限
ServiceAccount、 Role、RoleBinding 是 K8S 中的重要概念,主要用于权限管控。在这里我们创建一个名为 byzer-sa
的 ServiceAccount 用于部署, 并将其绑定到名为 byzer-admin
的 Role。byzer-admin
这个 Role 需要具有较高权限,这样 Byzer 引擎应用可以有权限来创建各类 K8S 对象,比如启动 spark executor 的 pod。
通过执行下述命令创建名为 byzer-sa
的 Service Account
$ kubectl create serviceaccount byzer-sa -n byzer
创建一个 role.yaml
文件, 用于定义名为 byzer-admin
的 Role,内容如下
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: byzer-admin
rules:
- apiGroups: [""]
resources: ["pods","deployments", "replicas", "secrets", "configmaps","services","ingresses"]
verbs: ["*"]
执行如下命令根据上述 role.yaml
来创建 Role,替换命令中的路径为文件的真实路径
$ kubectl apply /path/to/role.yaml
绑定 Service Account byzer-sa
到 Role byzer-admin
kubectl create rolebinding byer-role-binding --role=byzer-admin --serviceaccount=byzer:byzer-sa --namespace=byzer
Byzer 引擎 K8S 镜像说明
Byzer 社区在 DockerHub 上托管了 Byzer 引擎的 K8s 镜像,您可以前往 Byzer Docker Hub 来查看 Byzer 社区托管的镜像文件。
镜像地址
Byzer 引擎的 K8S 镜像地址为 https://hub.docker.com/r/byzer/byzer-lang-k8s/tags
你也可以通过 docker pull 命令来将镜像拉取到本地
docker pull byzer/byzer-lang-k8s:3.1.1-latest
镜像版本说明
在 byzer/byzer-lang-k8s
镜像中的 tag 中标识着该镜像的版本,关于 Byzer 引擎的版本,请参考 Byzer 引擎部署指引 中的 Byzer 引擎版本说明 一节。
注意:
- 当前 K8S 部署只支持 Spark 3 版本的 Byzer 镜像,Spark 2.+ 不支持
- 一般情况我们推荐使用已发布的正式版本,如 tag 为
3.1.1-2.2.2
,该镜像为正式发布版本2.2.2
,如果是测试使用,您也可以使用 Nightly Build 版本,如3.1.1-latest
- 该镜像内置了 JDK,SPARK 以及 Byzer 的插件
在 Minikube 部署 Byzer 引擎
本章节会以 minikube 作为 K8S 服务来演示如何通过 kubectl + yaml 配置文件的方式来部署 Byzer-lang 引擎,您可以将 minikube 替换为任意 K8S 服务 操作都相同
I. 环境准备
需要提前准备操作系统, Docker 的安装,以及 minikube 的安装
1.1 操作系统
推荐使用 Linux 系统或 MacOS 系统。
本文基于 CentOS 7 minimal 版本进行安装,其他的操作系统在安装时可以根据自己的系统版本进行 Docker 和 K8S 的安装。
1.2 安装 Docker
在 CentOS 中设置镜像源
$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
> --add-repo \
> https://download.docker.com/linux/centos/docker-ce.repo
安装 Docker Engine
$ sudo yum install docker-ce docker-ce-cli containerd.io
将 Docker 服务设置随机自启动并启动服务
$ sudo systemctl enable docker
$ sudo systemctl start docker
测试 Docker 服务是否正常工作
$ sudo docker run hello-world
当您看到 Terminal 中输出如下内容时,说明 Docekr 服务已正常运行
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
注意:
- 请访问 Docker 用户手册 https://docs.docker.com/ 获取更多关于 Docker 的信息
- 如果你使用的用户是非 root 账户,并不想通过 sudo 权限来使用 Docker,你需要将当前用户加入
docker
的用户组,此处我们使用的用户是byzer
, 你可以在下面的代码中提换为你需要使用的用户名,执行完毕后重启 VM 来使设置生效
$ sudo groupadd docker
$ sudo usermod -aG docker byzer
1.3 安装 minikube
下载 minikube stable 版本的安装包并安装
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-latest.x86_64.rpm
$ sudo rpm -Uvh minikube-latest.x86_64.rpm
在您的操作系统中安装 minikube 可以访问 https://minikube.sigs.k8s.io/docs/start/ 获取更多信息
为了使用 kubectl 来和 minikube 交互,你可以通过使用 minikube kubectl
来代替 kubectl
命令,也可以安装 kubectl 工具
通过 curl 下载 kubectl
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
安装 minikube
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
执行 kubectl version --client
验证 kubectl 是否安装成功,当您看到输出的 Json 信息说明 kubectl 工具已经成功安装至您的环境中
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.4", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-02-16T12:38:05Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}
您可以访问 k8s 官方手册来查看如何在您的操作系统中安装 kubectl 工具
1.4 启动 minikube 环境
启动 minikube,启动成功后可以看到如下输出,可以看到当前的 minikube 版本为 v1.25.2
,kubernetes 版本为 v1.23.3
,docker 版本为 20.10.12
$ minikube start
😄 minikube v1.25.2 on Centos 7.9.2009
✨ Using the docker driver based on existing profile
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
🔄 Restarting existing docker container for "minikube" ...
🐳 Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...
▪ kubelet.housekeeping-interval=5m
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: default-storageclass, storage-provisioner
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
执行 kubectl get po -A
可以查看到当前 minikube 环境中 Pod 的信息
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-64897985d-2l765 1/1 Running 0 11m
kube-system etcd-minikube 1/1 Running 0 11m
kube-system kube-apiserver-minikube 1/1 Running 0 11m
kube-system kube-controller-manager-minikube 1/1 Running 0 11m
kube-system kube-proxy-snldn 1/1 Running 0 11m
kube-system kube-scheduler-minikube 1/1 Running 0 11m
kube-system storage-provisioner 1/1 Running 1 (11m ago) 11m
II. 在 minikube 中部署 Byzer 引擎
接下来我们介绍如何通过 kubectl 和 yaml 的方式来在安装好的 minikube 环境中部署 byzer 引擎。
注意:此处我们使用 minikube 和 本地存储进行部署仅作为演示使用,真实开发、测试以及生产环境,请将 minikube 替换为生产可用的 K8S 服务或云服务,并选择使用对象存储或 JuiceFS 作为存储
2.1 选择 Byzer 引擎 K8S 镜像
前往 Byzer 社区 Docker Hub 根据自己的需求选择对应版本的 byzer/byzer-lang-k8s
镜像, 地址是 [https://hub.docker.com/r/byzer/byzer-lang-k8s/tags],这里我们选择的版本为 3.1.1-2.2.1
作为示例
2.2 创建命名空间和 Service Account
首先需要通过 kubectl 工具创建 namespace 以及 service account,分别为 byzer
和 spark
,并赋予其在 byzer
namespace 中的 edit
权限。这样 Byzer Engine 就拥有可以在 byzer namespace 中用于创建,管理和删除 executor pod 的权限。
$ kubectl create ns byzer
namespace/byzer created
$ kubectl create serviceaccount spark -n byzer
serviceaccount/spark created
$ kubectl create clusterrolebinding spark-role --clusterrole=edit --serviceaccount=byzer:spark --namespace=byzer
clusterrolebinding.rbac.authorization.k8s.io/spark-role created
2.3 准备 YAML 部署配置文件
在通过 YAML 配置文件部署 Byzer 引擎时,我们需要创建 secret.yaml
, configmap.yaml
, deployment.yaml
以及 service.yaml
文件进行配置的编写
创建 byzer-engine-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: byzer-engine-secret
namespace: byzer
labels:
app: byzer-engine
type: Opaque
data:
CLUSTER_URL: ${cluster-url}
byzer-engine-secret.yaml
主要是为了传入一些敏感信息,比如对象存储的 AKSK(Access Key & Secret Key),CLUSTER_URL 等,也可以根据自己的需要来定义一些敏感信息的 Key-Value Pair,声明在此文件中。
声明的 Key-Value 键值对会被 byzer-engine-deployment.yaml
文件中通过 env
的方式进行取值操作,在部署的时候声明进环境变量当中。
注意:
- Data 中的值要进行 base64 加密
- 获取 CLUSTER_URL 的方式可以通过执行命令
kubectl config view --output=jsonpath='{.clusters[].cluster.server}'
获得,然后通过 base64 加密后,替换上述文件的${cluster-url}
创建 byzer-engine-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: byzer-engine-configmap
namespace: byzer
data:
core-site-xml: |
<configuration>
</configuration>
byzer-engine-cofnigmap.yaml
文件主要是用来配置文件存储系统对应配置和参数,主要是对象存储或 JuiceFS 可读的 core-site.xml
配置文件。Byzer Engine 本质上就是一个 Spark Application,这里我们配置本地文件系统作为 spark 的读写目录(实际上是 pod 内的本地存储),所以此处 core-site-xml
中属性留空,不需要做任何的配置。
注意:
- 如果需要使用宿主机本地文件系统,配置信息可以参考 Spark 的官方文档 Running Spark on Kubernetes - Spark 3.2.1 Documentation - 不同的文件存储信息的配置是不同的,请参考所使用对象存储或其他文件系统的文档,比如如果是使用 HDFS 或者是对象存储,那么这里需要在 data 中配置为相应的 core-site.xml 的内容,详情可以参考其他云平台的 K8S 部署章节
创建 byzer-engine-service.yaml
apiVersion: v1
kind: Service
metadata:
name: byzer-engine-service
namespace: byzer
spec:
ports:
- name: http
port: 9003
protocol: TCP
targetPort: 9003
- name: spark-ui
port: 4040
protocol: TCP
targetPort: 4040
selector:
app: byzer-engine
sessionAffinity: ClientIP
byzer-engine-service.yaml
文件主要定义了 Byzer Engine 启动的服务端口和指定的协议,在这个文件中,我们主要暴露出两个 http 的服务:
- Byzer Engine 的 HTTP 服务,端口为
9003
- Spark UI 的 Web 服务,端口为
4040
创建 byzer-engine-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: byzer-engine
namespace: byzer
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app: byzer-engine
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
app: byzer-engine
spec:
serviceAccountName: spark
imagePullSecrets:
- name: dockerhub
containers:
- name: byzer-engine
image: byzer/byzer-lang-k8s:3.1.1-2.2.1
imagePullPolicy: Always
args:
- echo "/work/spark-3.1.1-bin-hadoop3.2/bin/spark-submit --master k8s://$(CLUSTER_URL) --deploy-mode client --driver-memory 1024m --driver-cores 1 --executor-memory 1024m --executor-cores 1 --driver-library-path "local:///home/deploy/mlsql/libs/ansj_seg-5.1.6.jar:local:///home/deploy/mlsql/libs/nlp-lang-1.7.8.jar" --class streaming.core.StreamingApp --conf spark.kubernetes.container.image=byzer/byzer-lang-k8s:3.1.1-2.2.1 --conf spark.kubernetes.container.image.pullPolicy=Always --conf spark.kubernetes.namespace=$(EXCUTOR_NAMESPACE) --conf spark.kubernetes.executor.request.cores=1 --conf spark.kubernetes.executor.limit.cores=1 --conf spark.executor.instances=1 --conf spark.driver.host=$(POD_IP) --conf spark.sql.adaptive.enabled=true --conf spark.driver.maxResultSize=2g --conf spark.serializer=org.apache.spark.serializer.KryoSerializer --conf spark.kryoserializer.buffer.max=200m --conf "\"spark.executor.extraJavaOptions=-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+UseContainerSupport -Dio.netty.tryReflectionSetAccessible=true\"" --conf "\"spark.driver.extraJavaOptions=-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+UseContainerSupport -Dio.netty.tryReflectionSetAccessible=true\"" --conf "\"spark.executor.extraLibraryPath=local:///home/deploy/mlsql/libs/ansj_seg-5.1.6.jar:local:///home/deploy/mlsql/libs/nlp-lang-1.7.8.jar\"" --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark --conf \"spark.kubernetes.file.upload.path=file:///byzer-demo/byzer-upload\" local:///home/deploy/mlsql/libs/streamingpro-mlsql-spark_3.0_2.12-2.2.1.jar -streaming.name byzer-engine -streaming.rest true -streaming.thrift false -streaming.platform spark -streaming.enableHiveSupport true -streaming.spark.service true -streaming.job.cancel true -streaming.driver.port 9003\" -streaming.datalake.path\" \"/byzer/admin\" " | bash
command:
- /bin/sh
- -c
env:
- name: CLUSTER_URL
valueFrom:
secretKeyRef:
name: byzer-engine-secret
key: CLUSTER_URL
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: EXCUTOR_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MAX_EXECUTOR
value: "5"
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: "1"
memory: 1Gi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- name: spark-conf
mountPath: /work/spark-3.1.1-bin-hadoop3.2/conf
volumes:
- name: spark-conf
configMap:
name: byzer-engine-configmap
items:
- key: core-site-xml
path: core-site.xml
restartPolicy: Always
byzer-deployment-deployment.yaml
文件定义了 Byzer Engine 部署所使用的 Docker image, service account name (见 2.3 节中解释),镜像启动参数,环境变量,使用资源,配置文件等。
- Docker Image: 位于
containers.image
, 此处使用的值为byzer/byzer-lang-k8s:3.1.1-2.2.1
- Byzer 引擎启动参数:位于
container.args
, 由于当前 Byzer 引擎是通过 spark-submit 命令提交启动,在启动的时候需要传入 Spark 参数 / Byzer 参数等, 所以需要在此处填入对应的参数
注意:
- 受限于当前 Byzer 引擎的启动方式,当前的 deployment 还存在优化的空间, Byzer引擎的配置信息都需要写在启动参数中,我们已计划在 2.3.0 版本中进行 byzer 引擎的启动重构,解耦启动命令和配置
- 关于 Byzer 引擎的启动参数说明,可以参考 Byzer Server 二进制版本安装和部署 的说明
- 此处是因为在 minikube 上部署,所以 driver 和 executor 的配置都设置的比较小,对于在生产环境上部署,driver 的资源至少要
8 core 16 gb mem
以上,executor 的cpu:mem
比例建议设置到1:4
2.4 使用 kubectl 工具进行部署
将 2.3 一节中创建的的 4 个 YAML 文件放入一个文件夹 byzer-yaml
中,接下来我们通过 kubectl
来进行部署, 执行下述命令:
$ kubectl apply -f byzer-engine-secret.yaml
secret/byzer-engine-secret created
$ kubectl apply -f byzer-engine-configmap.yaml
configmap/byzer-engine-configmap created
$ kubectl apply -f byzer-engine-deployment.yaml
deployment.apps/byzer-engine created
$ kubectl apply -f byzer-engine-service.yaml
service/byzer-engine-service created
部署的过程中,我们可以通过查看 pod 或者 event 的方式来看 pod 的部署状态
$ kubectl get pods -n byzer
$ kubectl get event -n byzer
当我们看到 Byzer Engine Pod 的状态为 Running 时,说明 Pod 已经成功创建并正常运行。
2.5 部署验证
我们在 2.3 一节创建的 byzer-engine-service.yaml
文件中定义了 Byzer 引擎的服务端口,分别是
- Byzer Engine 自带的 Console UI,端口为
9003
- Spark UI,端口为
4040
当 Pod 成功启动后,我们来检查一下 Pod 和 Service 的状态
通过 kubectl get pods -n byzer
命令,可以查看到当前正在运行的 Byzer Engine Pod
$ kubectl get pods -n byzer
NAME READY STATUS RESTARTS AGE
byzer-engine-6fdc89b549-z4t2p 1/1 Running 0 42m
通过 kubectl get services -n byzer
命令,可以查看到当前正在运行的 Byzer Engine Service 的状态和信息
$ kubectl get services -n byzer
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
byzer-engine-service ClusterIP 10.109.213.196 <none> 9003/TCP,4040/TCP 6h57m
但此时我们是没有办法通过浏览器进行访问的,如果想要通过浏览器进行访问 Byzer Engine Console 和 Spark UI,可以通过使用 Ingress 将服务进行暴露,或者通过 Service port forwarding 的方式直接暴露服务端口来进行访问 :
$ kubectl port-forward pods/byzer-engine-6fdc89b549-z4t2p 9003:9003 -n byzer
此时 Byzer Engine 的 Console 9003
端口就被转发至 http://localhost:9003/ , 通过浏览器打开后,可以看到 Byzer Engine Console 界面,如下图所示
点击运行按钮后,可以直接查看到上述语句执行的结果。
同理,可通过同样的方式来进行转发 Spark UI 的服务端口
$ kubectl port-forward pods/byzer-engine-6fdc89b549-z4t2p 4040:4040 -n byzer
在浏览器中访问 http://localhost:4040/ ,可以看到 Spark UI
至此,Byzer Engine 已成功的部署至 minikube 中。
在 Azure 通过 K8S 部署 Byzer 引擎
Azure 提供了一个托管的 K8S 服务 AKS,用户可以直接使用 AKS 来部署管理应用。
本章节以 Azure CN 公有云为例,来介绍如何在云上部署 Byzer 引擎。请确保在开始部署之前,您有阅读并根据 K8S 部署前置条件 完成了前置条件中的准备工作
其他准备工作
在 Azure CN 公有云上您的账户需要满足以下条件:
- 资源组及该资源组的权限
- 您的账户里有足够的云资源配额(quota)
在您的资源组中,创建
- AKS 服务, 要求最少有 24 GB 内存,6 核 CPU,用于部署 Byzer 引擎
- 1 个 Azure Database for MySQL 实例
- 1 个 Azure Blob Container 实例,用做 Byzer 引擎的存储系统
对于个人用户来说,Azure 提供了部分免费额度,你可以通过使用这些额度来进行使用和创建上述资源,过程请参考微软云文档。
在 VM Client 中安装 Azure CLI 工具
在 Azure 上部署时,VM Client 可以直接在 Azure 的资源组中创建,也可以选择一台可访问到您资源组中的虚拟网络的任一 Linux 虚拟机,除了在 K8S 部署前置条件 一节中安装的 kubectl
以及 helm
工具外,我们需要在 VM Client 中安装 Azure CLI 用于访问 Azure 的资源。
在 Linux 上安装 Azure CLI 请参考 Azure 官方文档 Install the Azure CLI on Linux。安装完毕后,接下来我们需要在 VM Client 里配置 AKS 集群的连接信息。
执行下面命令配置 AKS 连接信息。使用 az login
命令进行认证时,会通过 Azure 的 SSO 跳转到 azure web 页面进行认证。
可参考如下命令, 设置
subscription-id
, 值是 Azure 订阅的 idresource-group
, 资源组名称aks-name
为 AKS 集群名称
上述这些值都可以通过访问 Azure Portal 中在您的订阅下查询到。
$ az cloud set --name AzureChinaCloud
$ az login
$ az account set --subscription <subscription-id>
$ az aks get-credentials --resource-group <resource-group> --name <aks-name>
配置好上述集群信息后,在命令行中执行下述命令来检查集群信息。
$ kubectl cluster-info
通过 Helm 部署 Byzer 引擎
完成上述的准备工作后,我们以 Byzer-lang 2.3.0.1
为例在 AKS 上进行部署,在该示例中,我们直接使用对象存储 Azure Blob 作为文件存储系统。
Byzer engine 作为执行引擎,一般不对公网暴露,一般在公网暴露的是类似于 Byzer Notebook 等产品应用服务,Byzer Notebook 和 Byzer engine 的 API 交互发送在 K8S 集群的内网之内。
1.下载 helm chart
前往下载站获取于 Byzer-lang 2.3.0.1 版本的 helm chart 下载地址。我们使用目录 /home/byzer/server
作为工作路径,你可以自行创建目录进行替换。
$ wget https://download.byzer.org/k8s-helm/byzer-lang/2.3.0.1/byzer-lang-helm-charts-2.3.0.1.tgz
$ tar -xf byzer-lang-helm-charts-2.3.0.1.tgz -C /home/byzer/server
$ cd /home/byzer/server/byzer-lang
解压后目录结构如下
|-- byzer-lang
|-- Chart.yaml
|-- values.yaml
|-- VERSION
|-- templates
|-- _helpers.tpl
|-- deployment.yaml
|-- secret.yaml
|-- serviceaccount.yaml
|-- configmap.yaml
|-- hpa.yaml
|-- service.yaml
|-- tests
|-- test-connection.yaml
在 templates 目录包含 deployment
, service
, configmap
, secret
等模板,在部署时,会使用 values.yaml
文件的中来渲染上述模板,然后将应用部署至 K8S。
2.通过 values.yaml 修改配置
按照部署平台的实际情况,修改 values.yaml
中以下配置。
注意
- CPU 内存不要超过 AKS 每个 worker 节点可分配数,否则 Pod 无法启动。
- 在配置文件系统时,可能需要修改镜像,目前 Byzer 官方的 byzer-lang-k8s 镜像中集成的是 Azure Blob Jar,不需要改动镜像即可在 Azure 上进行部署和访问 Blob。
参数 | 说明 |
---|---|
fs.defaultFS | 预先申请的 Azure Blob 地址,Byzer-lang 基于 HDFS API 访问它。格式为 wasb://<container_name>@<account_name>.blob.core.chinacloudapi.cn/ 。其中 core.chinacloudapi.cn 表示中国区微软云,其他区域有不同地址。Azure Blob jar 已经集成在 byzer 镜像 |
clusterUrl | Base 64 编码的AKS APIServer 地址。可以在 AKS overview 页面查到,例如 https://hello-k8s-dns.hcp.chinanorth2.cx.prod.service.azk8s.cn:443 ,如下图所示 |
storage.SecretKey | Base 64 编码的 Azure Blob Secret Key; 可以在 Azure Portal 查到. 如图所示 |
spark.driver.memory | Driver 内存,例如 16g ;由于 Byzer Driver 承担的负载较重,建议 16g 起步 |
spark.driver.cores | Driver CPU 核数,例如 4 ;在生产上建议 cpu/mem 比为 1:4 |
spark.driver.maxResultSize | Driver 端结果集上限,例如 2g |
spark.executor.memory | Executor 内存,例如 4g |
spark.executor.cores | Executor CPU 核数,例如 1 ;在生产上建议 cpu/mem 比为 1:4 |
spark.executor.instances | Executor 数量,例如 2 ;此值建议根据生产负载来规划,建议 4 个起步 |
streaming.datalake.path | Byzer Engine 内置的 DeltaLake 的工作目录,引擎会在 Azure Blob 上自动创建,例如 /byzer/_delta , 无需事先创建。 |
其他的参数配置修改,请参考 Byzer 引擎参数配置说明 一节的参数说明
3. 通过 helm install 进行部署
当我们修改完 values.yaml
文件后,就可以通过执行下述命令来进行部署了
$ cd /home/byzer/server/byzer-lang
$ helm install byzer-lang .
该命令中, byzer-lang
是 helm release 的名称, .
表示使用当前目录中的 helm chart。 执行后,可以在 AKS 页面看到新的 deployment, pod, service。
$ kubectl get pod -l app.kubernetes.io/instance=byzer-lang
NAME READY STATUS RESTARTS AGE
byzer-lang-deployment-686f555dc8-l7x99 1/1 Running 0 2d5h
可以通过下述命令使用 kubectl 查看 Byzer 引擎部署的日志信心
$ kubectl logs $(kubectl get pod -l app.kubernetes.io/name=byzer-lang -o name | head -1)
4. 验证部署是否成功
当您检查 AKS 上的 pod 就绪后,您可以通过使用 Ingress 或者通过 Service port forwarding 的方式来将 Byzer Engine Console 和 Spark UI 服务进行暴露,然后通过浏览器进行访问。默认的 Byzer 引擎的端口为 9003
, 您也可以在 values.yaml
中进行修改; Spark UI 的默认端口为 4040
在 AWS 通过 EKS 部署 Byzer 引擎
AWS 提供了一个托管的 K8S 服务 EKS,用户可以直接使用 EKS 来部署管理应用。
本章节以 AWS CN 为例,来介绍如何在云上部署 Byzer 引擎。请确保在开始部署之前,您有阅读并根据 K8S 部署前置条件 完成了前置条件中的准备工作
1. 其他环境准备
- eksctl
- kubectl
- aws cli
- helm
2. 部署 Byzer 套件
i. 安装 Helm
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
查看 kubectl 信息
kubectl cluster-info
ii. 创建 Namespace
# kubectl create namespace <namespace>
kubectl create namespace byzer-eks-demo-yaml
iii. 创建 service account
## kubectl create serviceaccount <service_account_name> -n <namespace>
kubectl create serviceaccount byzer -n byzer-eks-demo-yaml
iv. 创建 Role 和 Rolebinding
创建 Role byzer-admin 并赋予它在 byzer-eks-demo-yaml 的 namespace 高权限
创建一个 yarml 文件
vim byzer-k8s-role.yaml
文本示例
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: byzer-aws-admin
rules:
- apiGroups: [""]
resources: ["pods","deployments", "replicas", "secrets", "configmaps","services","ingresses"]
verbs: ["*"]
运行这个 yaml 文件
kubectl apply -f ./byzer-k8s-role.yaml
绑定 Service Account byzer 到 byzer-eks-demo-yaml namespace 的 role byzer-aws-admin
kubectl create rolebinding byzer-aws-role-binding --role=byzer-aws-admin --serviceaccount=byzer-eks-demo-yaml:byzer --namespace=byzer-eks-demo-yaml
v. 部署 Byzer 引擎
下载并解压 Byzer-lang 2.3.0.1 helm chart
curl https://download.byzer.org/k8s-helm/byzer-lang/2.3.0.1/byzer-lang-helm-charts-2.3.0.1.tgz | tar -xz
解压出目录 byzer-lang 的子目录中有一个 values.yaml 文件,这是部署的配置文件。
values.yaml
包含 Byzer-lang 镜像 ,Byzer-lang Driver 和Executor 资源大小,对象存储等。我们给出一个例子:
需要注意的是
-
storage 和 SecretKey 即使没用也不能删掉,且 value 必须是 base64 编码的
-
clusterUrl 的值为
kubectl cluster-info
获取的Kubernetes master
base64 编码后的值
参数 | 说明 |
---|---|
fs.defaultFS | 预先申请的 AWS S3 的地址,Byzer-lang 基于 HDFS API 访问它。格式为 **s3a://<bucket>** 。 |
fs.AbstractFileSystem.s3a.impl | S3 为 org.apache.hadoop.fs.s3a.S3A |
fs.s3a.access.key | AWS 的 accessKey |
fs.s3.secret.key | AWS 的 secretKey |
fs.s3a.endpoint | S3 的 endpoint |
fs.s3a.region | S3 的 bucket 的所在区域 |
clusterUrl | AWS EKS 的 API Server 地址,在 EKS 管理页面查询,需要将对应地址经过 base 64 编码 |
storage.SecretKey | Azure Blob 的 Secret key,S3 不用,但不能删除这一行配置 |
spark.driver.memory | Driver 内存,例如 16g ;由于 Byzer Driver 承担的负载较重,建议 16g 起步 |
spark.driver.cores | Driver CPU 核数,例如 4 ;在生产上建议 cpu/mem 比为 1:4 |
spark.driver.maxResultSize | Driver 端结果集上限,例如 2g |
spark.executor.memory | Executor 内存,例如 4g |
spark.executor.cores | Executor CPU 核数,例如 1 ;在生产上建议 cpu/mem 比为 1:4 |
spark.executor.instances | Executor 数量,例如 2 ;此值建议根据生产负载来规划,建议 4 个起步 |
streaming.datalake.path | Byzer Engine 内置的 DeltaLake 的工作目录,引擎会在 Azure Blob 上自动创建,例如 /byzer/_delta , 无需事先创建。 |
测试 values.yaml
-
install 表示安装一个 helm release
-
dry-run 表示仅解析values ,生成 deployment 等,但不真正部署
## 需要进入到 byzer-lang chart 目录
cd ./byzer-lang
helm upgrade -i --dry-run byzer-lang ./ -f values.yaml --namespace byzer-eks-demo-yaml
测试无误后执行以下命令部署
helm upgrade -i byzer-lang ./ -f values.yaml --namespace byzer-eks-demo-yaml
vi. 安装 nginx ingress
Get Repo Info
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
Install Chart
Important: only helm3 is supported
helm install [RELEASE_NAME] ingress-nginx/ingress-nginx
安装完成后执行 kubectl -n ingress-nginx get svc
获取 EXTERNAL-IP
,在 负载均衡中搜索对应名称。
vii. 部署 Dolphinscheduler
下载并解压缩 DolphinScheduler helm chart
wget -c https://download.byzer.org/k8s-helm/dolphinscheduler/1.3.0/dolphinscheduler-1.3.0.tgz -O - | tar -xz
进入 DolphinScheduler helm charts 目录
cd dolphinscheduler/
values.yaml 配置项
参数 | 说明 |
---|---|
externalDatabase.host | AWS RDS 的 Endpoint |
externalDatabase.port | 数据库端口号 |
externalDatabase.username | 数据库用户名 |
externalDatabase.password | 数据库密码 |
externalDatabase.params | 数据库参数,如 useUnicode=true&characterEncoding=UTF-8&useSSL=false |
测试 values.yaml
helm upgrade -i --dry-run dolphinscheduler ./ -f values.yaml --namespace byzer-eks-demo-yaml
测试成功后开始部署
helm upgrade -i dolphinscheduler ./ -f values.yaml --namespace byzer-eks-demo-yaml
根据 使用手册 的「对接 Byzer Notebook」章节使用一下命令在本机访问 dolphinscheduler,访问地址:http://localhost:12345/dolphinscheduler
kubectl port-forward svc/dolphinscheduler-api 12345:12345
kubectl port-forward -n byzer-eks-demo-yaml svc/dolphinscheduler-api 12345:12345
vii. 部署 Byzer Notebook
下载 Notebook helm chart 并解压
curl https://download.byzer.org/k8s-helm/byzer-notebook/1.2.0/byzer-notebook-helm-charts-1.2.0.tgz | tar -xz
进入 Notebook helm chart 目录
cd byzer-notebook/
values.yaml 配置项
参数 | 说明 |
---|---|
notebook.security.key | 私钥,用胡注册时用这个 token 进行加解密 |
notebook.mlsql.engine-url | 引擎地址,这里使用 k8s 命名规则,http://<service_name>.<namespace>:<port> 。如:**http://byzer-lang-service.byzer-eks-demo-yaml:9003** |
notebook.scheduler.scheduler-url | 调度 api 地址,同样是 k8s 命名规则,如:**http://dolphinscheduler-api.byzer-eks-demo-yaml:12345/dolphinscheduler** |
notebook.scheduler.auth-token | 调度的 token,获取方法见 对接 Byzer Notebook,使用如下命令在本机访问 dolphinscheduler,访问地址:http://localhost:12345/dolphinscheduler - kubectl port-forward svc/dolphinscheduler-api 12345:12345 - kubectl port-forward -n byzer-eks-demo-yaml svc/dolphinscheduler-api 12345:12345 |
notebook.scheduler.callback-url | 调度的 notebook 回调地址,同样是 k8s 命名规则,如:**http://byzer-notebook.byzer-eks-demo-yaml:9002** |
notebook.database.* | notebook 的 mysql 相关配置 |
mlsql.engine-url
的 value 的规则:
类似的都是该规则
http://{service name}.{namespace}:{port}
http://byzer-lang-service.byzer-eks-demo-yaml:9003
测试
helm upgrade -i --dry-run byzer-notebook ./ -f values.yaml --namespace byzer-eks-demo-yaml
测试完成后去掉 dry-run
部署
helm upgrade -i byzer-notebook ./ -f values.yaml --namespace byzer-eks-demo-yaml
查看 pod 是否正常运行
kubectl -n byzer-eks-demo-yaml get pod
4. 验证 Byzer 套件
## 查看 EXTERNAL-IP,通过 EXTERNAL-IP 让用户自己配 DNS
kubectl -n ingress-nginx get svc
登录对应地址查看 Byzer Notebook 的各个功能
FAQ:
1. Kubernetes 平台 常用命令
环境准备
安装kubectl 参考 kubectl Install Doc
常用操作
i. 查看 pod/configmap/service/ingress 等资源
kubectl -n <NAMESPACE_NAME> get <RESOURCE_NAME>
ii. 查看 pod log4
# 查看pod所有log
kubectl -n <NAMESPACE_NAME> logs <POD_NAME>
# 查看最新50行
kubectl -n <NAMESPACE_NAME> logs -tail=50 <POD_NAME>
iii. 进入pod
kubectl -n <NAMESPACE_NAME> exec -ti <POD_NAME> -- /bin/bash
# 如果pod中存在多个container
kubectl -n <NAMESPACE_NAME> exec -ti <POD_NAME> -c <CONTAINER_NAME> -- /bin/bash
iv. 查看pod无法正常启动原因
kubectl -n <NAMESPACE_NAME> describe pod/<POD_NAME>
v. 本地调试时,端口转发
kubectl -n <NAMESPACE_NAME> port-forward service/<SERVICE_NAME> <LOCAL_PORT>:<REMOTE_PORT>
# 例子
kubectl get service -n mongo
##############
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mongo ClusterIP 10.96.41.183 <none> 27017/TCP 11s
##############
kubectl -n mongo port-forward service/mongo 28015:27017
# 本地连接mongo
mongosh --port 28015
2. Dolphinscheduler 部署后链接不上 MySQL
values.yaml 增加配置 useSSL=false
# 请使用 Byzer 维护的镜像,支持 MySQL。
image:
repository: "byzer/dolphinscheduler"
tag: "1.3.9-mysql-driver"
# MySQL
externalDatabase:
type: "mysql"
driver: "com.mysql.jdbc.Driver"
host: ""
port: "3306"
username: ""
password: ""
database: "dolphinscheduler_k8s"
params: "useUnicode=true&characterEncoding=UTF-8&useSSL=false"
3. 多个 Namespace 部署 Notebook 后,如何配置 ingress?
- 暂不支持
4. 如何在部署的过程中进行 Debug 排查
[byzer@localhost ~]$ kubectl describe pod -n <namepsace>
## 重试重启
[byzer@localhost ~]$ kubectl rollout restart deploy byzer-engine -n <namepsace>
[byzer@localhost ~]$ kubectl get event -n <namepsace>
[byzer@localhost ~]$ kubectl get pods -n <namepsace>
## 删除一个 pod
[byzer@localhost ~]$ kubectl delete pod <pod_name> -n <namepsace>
## 进入 pod
[byzer@localhost ~]$ kubectl exec -ti <pod_name> -n <namepsace> -- /bin/sh
## 查看 deployment 详情
[byzer@localhost ByzerDemo]$ kubectl describe deployment -n <namepsace>
通过 Byzer-K8S 工具部署
Byzer 社区贡献了一个使用 go 语言实现的 Byzer K8S 工具,对 Byzer 的 K8S 做了封装,可以让使用者快速的进行在 K8S 上部署和试用。
项目源代码:byzer-org/byze-k8s
注意
- 当前此工具处于开发测试阶段,并不推荐在生产环境上使用
- 操作系统建议使用 Linux
准备工作
1. 安装 Golang
您可以前往 Golang 的官方网站,根据您的操作系统,下载并安装 Go 语言环境
- 您可以根据您的操作系统来自行选择安装方式
- 如果您的机器位于 CN 区域,可以设置 goproxy 来使用 CN 区镜像
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
$ export GO111MODULE=on
$ export GOPROXY=https://goproxy.cn
2. 编译 Byzer K8S 部署工具
通过 git clone 来拉取部署工具到本地
$ git clone https://github.com/byzer-org/byzer-k8s.git
Clone 完毕后,进入到项目的根目录,通过执行下述命令来编译 Byzer K8S 工具
$ cd byzer-k8s
$ make all
编译完成后会在项目根目录中生成一个可执行文件 byzer-k8s-deploy
3. 准备 K8S 环境
此处我们使用 minikube
来作为 K8S 服务,关于 minikube 的安装,可以参考 在 Minikube 部署 Byzer 引擎 一文。
由于此部署工具最后通过 ingress 来暴露 byzer 的服务,所以在 minikube 中需要使用下述命令来启用 ingress 插件
$ minikube addons enable ingress
4. 准备 JuiceFS
Byzer Engine 作为一个 Spark 应用,可以兼容对象存储,HDFS 以及本地存储磁盘。这里我们使用 JuiceFS 作为存储系统,通过 JuiceFS 来管理不同的对象存储。
- 准备 Redis,JuiceFS 社区版使用 Redis 作为元数据管理工具,所以需要提前准备好 Redis,可通过 docker 来启动一个 Redis 服务
$ docker run -itd --name redis-server -p 6379:6379 redis
- 准备对象存储,JuiceFS 支持多种对象存储,此处我们选择使用 minio 作为示例,minio 是一个开源的兼容 Amazon S3 协议的对象存储,在本机可以通过 docker 来启动一个 minio 服务,需要指定本地挂载的网存储目录,这里我们以
/home/byzer/minio_data
为例
$ docker run -d --name minio \
-v /home/byzer/minio_data:/data \
-p 9000:9000 \
-p 9001:9001 \
--restart unless-stopped \
minio/minio server /data --console-address ":9001"
服务启动后可以前往 http://127.0.0.1:9001
访问 minio console, 用户名和密码分别为 minioadmin/minioadmin
- 安装 JuiceFS 并初始化
前往 JuiceFS Release 下载 JuiceFS 的安装包,下载后将其解压,并进入解压后的目录,执行如下命令对 JuiceFS 进行初始化,该命令会连接 redis 来管理元数据,并在 minio 中创建一个 bucket 名为 jfs
$ ./juicefs format \
--storage minio \
--bucket http://127.0.0.1:9000/jfs \
--access-key minioadmin \
--secret-key minioadmin \
redis://127.0.0.1:6379/1 \
jfs
创建完毕后,您可以前往 minio console 查看到该 bucket
进行 Byzer 引擎的部署
上述准备工作完成后,可以通过执行第二步编译后产生的可执行文件 byzer-k8s-depoly
进行 Byzer Engine 在 K8S 上的部署,此处我们使用的镜像是 byzer/byzer-lang-k8s:3.1.1-2.2.2
,关于镜像的选择可以参考 K8S 镜像部署指南 一节
./byzer-k8s-deploy run \
--kube-config ~/.kube/config \
--engine-name byzer-engine \
--engine-image byzer/byzer-lang-k8s:3.1.1-2.2.2 \
--engine-executor-core-num 1 \
--engine-executor-num 1 \
--engine-executor-memory 1024 \
--engine-driver-core-num 1 \
--engine-driver-memory 1024 \
--engine-jar-path-in-container local:///home/deploy/mlsql/libs/streamingpro-mlsql-spark_3.0_2.12-2.2.2.jar \
--storage-name jfs \
--storage-meta-url redis://192.168.0.121:6379/1
上述部分参数说明如下: 参数说明如下:
参数名 | 说明 |
---|---|
kube-config | K8S 配置文件。Byzer-k8S 会读取 K8S ApiServer 地址 |
engine-name | K8S Deployment 名称,请取一个有实际意义的名字 |
engine-image | 请不要改,这是 K8S 从 docker hub 拉取的镜像名 |
engine-executor-core-num | 每个 Spark Executor 核数 |
engine-executor-num | Spark executor 数量 |
engine-executor-memory | Spark executor 堆内存,单位 MB |
engine-driver-core-num | Spark driver 核数 |
engine-driver-memory | Spark driver 堆内存, 单位 MB |
engine-jar-path-in-container | Byzer-lang jar 在容器内路径,请不要修改。启动 Spark Driver 需要它。 |
storage-name | 执行 JuiceFS format命令时,指定的名称 |
storage-meta-url | JuiceFS 的元数据库连接串 |
执行完毕后,Byzer Engine 就部署到 K8S 上了。部署的过程,会经历如下的几个过程:
- 创建 byzer engine 的 configmap
- 在 k8s 中创建 role 并进行绑定
- 创建 byzer engine 的 deployment
- 创建 byzer engine service,将 byzer engine pod 的服务进行端口暴露
- 创建 ingress,将 byzer engine 的端口和服务通过 IP 对外暴露
待部署完成后,你可以通过 kubectl get ingress
来查看到 Byzer engine 绑定的 host 和端口, 此时你可以通过在浏览器上访问该 host 和端口进行 Byzer Engine 的访问和体验。
更多推荐
所有评论(0)