KubeEdge 1.3.0 部署

本文介紹瞭如何在兩臺 ubuntu 16.04 64 bit 雙核 CPU 虛擬機上從源碼開始部署 KubeEdge 1.3.0 集羣的過程,雲端 Kubernetes 版本爲 1.17.3,Golang 版本爲1.13.5。本文基於 KubeEdge 1.1.0 和 KubeEdge 1.2.0 部署文章,具有一定實踐參考意義。

一、概述

1.1 環境

雲端:ubuntu 16.04 64 bit,用戶名爲 ubuntu。IP 爲 192.168.0.102。
邊緣端:同上,IP 爲 192.168.0.140。
KubeEdge部署涉及兩端:

  • 雲端
    docker, kubernetes 集羣和 KubeEdge 雲端核心模塊。
  • 邊緣端
    docker, mqtt 和 KubeEdge 邊緣端核心模塊。

技術總結:
1、搭建編譯環境(建議自行編譯源碼),編譯系統內存需足夠大(如4GB)。
2、部署k8s,但只安裝k8s master節點即可,node不需要安裝。
3、編譯 KubeEdge,生成證書(注:針對 kubectl logs命令而言),創建crds。
4、先運行得到配置文件,再修改。注意配置文件位置,注意系統平臺框架。
5、檢查主機名稱,必須合規,否則註冊不了。
6、先運行雲端,獲取 token,再修改邊緣端配置。

1.2 依賴

依賴組件(含工具、編譯器)及版本:

  • golang
    版本 1.13.5,到 https://studygolang.com/dl 下載。編譯源碼需要,如果不編譯,則無須安裝。需要指出的是,KubeEdge 對 golang 版本有兼容性,當前官方已經支持 1.13 版本 golang。
  • k8s 版本
    1.17,具體部署參考其它文章。理論上1.18也會支持,但無暇測試,僅猜測。
  • mosquitto
    1.6.8, 到 https://mosquitto.org/download/ 下載。
  • KubeEdge 相關的(雲端、邊緣端)
    最新發布版本爲 v1.3.0, 到 https://github.com/kubeedge/kubeedge/releases/tag/v1.3.0 下載。代碼倉庫爲 https://github.com/kubeedge/kubeedge/ 。
    注意,github並不是一直都十分穩定,所以下載可能會較耗時。

本文部署時間約爲2020年5月下旬。KubeEdge目前在快速開發中,請注時效性,以官方文檔爲準,本文僅針對 KubeEdge 1.3.0 有效。

1.3 方法論

最權威的莫過於 KubeEdge 官方安裝文檔:https://docs.kubeedge.io/en/latest/。該文檔從源碼編譯到安裝,均有涉及。然作爲初學者,個人認爲官方的文檔還不夠細緻。網上也有很多安裝部署教程,雖然方法不同,但目的是一樣的。這裏根據筆者經驗簡述如下:

  • 步步爲營
    根據官方文檔安裝,先產生證書,再編譯源碼得到雲端和邊緣端核心程序。再修改配置文件,最後運行。

  • 事預則立
    下載官方編譯好的二進制文件(cloudcore、edgecore),或者自行編譯源碼得到這兩個二進制文件。準備好配置文件(並做適應性修改)。然後再運行。本文采用此方式。先準備好所有的文件,並統一放置到部署工程目錄。

需要注意的是,KubeEdge 官方文檔只涉及 KubeEdge 有關的部署,KubeEdge 還要依賴 docker 和 kubernetes(邊緣端還要依賴 mosquitto)。而這些需要實踐者額外進行,爲減少篇幅,本文不過多涉及,但給出部署文章鏈接。

1.4 新版本主要變化

相比1.2.0 版本,1.3.0版本有如下變化(僅涉及個人感興趣部分):
1、不需要手動產生證書,程序自動產生。
2、支持 kubectl logs 命令(據說未來會支持 kubectl exec)。
3、不需要手動分發證書,邊緣端自動獲取。
4、雲端高可用待有空閒有條件測試。

二、準備

在開始之前,必須先安裝 Docker 並部署k8s集羣
Docker 安裝可參考這裏,kubernetes 安裝可參考這裏。需要指出的是,kubernetes 只部署 master 即可,node 無須部署。但必須安裝網絡插件(此處存疑,如果不安裝,狀態不是 Ready)。

2.1 創建部署文件目錄

官方文檔建議創建單獨的目錄存儲二進制文件,如~/cmd/,在部署 1.1.0 版本時筆者也創建目錄,但新版本做了優化,筆者認爲無須部署目錄,爲方便測試,直接在新版本生成的目錄下執行程序,其目錄爲$GOPATH/src/github.com/kubeedge/kubeedge/_output/local/bin

2.2 KubeEdge 二進制

新版本漸漸使用 keadm 部署,對於不需要了解背後原理者而言,建議使用此方式。
本文使用的 KubeEdge 是從源碼編譯得到。

2.2.1 官方編譯好的文件

到 https://github.com/kubeedge/kubeedge/releases 下載官方編譯好的二進制。壓縮包名稱爲 kubeedge-v1.3.0-linux-amd64.tar.gz 。
也可以通過編譯源碼得到,方法參考文後。
另外,除了編譯好的二進制外,還需要下載源碼,地址爲: https://github.com/kubeedge/kubeedge 。因爲部分配置文件只在倉庫源碼中才可找到(當然,也可以直接在 github 倉庫上找到對應的文件下載)。

2.2.2 KubeEdge 源碼編譯

1、Golang 環境搭建
下載好 golang,並解壓:

# mkdir ~/tools
# tar xf go1.13.5.linux-amd64.tar.gz -C ~/tools

在 ~/.bashrc 文件最後添加:

export PATH=$PATH:/home/ubuntu/tools/go/bin
export GOROOT=/home/ubuntu/kubeedge
export GOPATH=/home/ubuntu/kubeedge

執行 source ~/.bashrc 生效。驗證:

# ubuntu@ubuntu:~/kubeedge$ go version
go version go1.13.5 linux/amd64

2、克隆倉庫:

# git clone https://github.com/kubeedge/kubeedge.git $GOPATH/src/github.com/kubeedge/kubeedge

如果克隆速度慢,可以直接下載zip包,並解壓源碼到 $GOPATH/src/github.com/kubeedge/kubeedge,注意,這個目錄一定是源碼所在目錄。
切換 1.3.0 分支:

# git checkout -b release-1.3 remotes/origin/release-1.3

3、檢測 gcc 版本:

# gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.

如果沒有安裝 gcc,則自行安裝。

編譯雲端:

# cd $GOPATH/src/github.com/kubeedge/kubeedge/
# make all WHAT=cloudcore

編譯邊緣端:

# cd $GOPATH/src/github.com/kubeedge/kubeedge
# make all WHAT=edgecore

生成的二進制位於_output/local/bin/目錄下。

2.3 生成證書

1.3.0 正式版本不需要手動生成證書,如果已經安裝了舊版本,則需要清除 /etc/kubeedge/ca 和 /etc/kubeedge/certs 目錄的證書。執行如下命令:

kubectl delete secret casecret -nkubeedge
kubectl delete secret cloudcoresecret -nkubeedge

如果是首次安裝,忽略此步驟即可。

生成證書以支持 kubectl logs 命令

確認 k8s 集羣正常運行,本文使用 kubeadmin 部署,其證書位於/etc/kubernetes/pki/目錄(注:生成證書腳本需要使用/etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.key文件)。
先設置雲端IP:

export CLOUDCOREIPS="192.168.0.102"
注:可同時設置多個,如:
export CLOUDCOREIPS="172.20.12.45 172.20.12.46"

生成證書:

$GOPATH/src/github.com/kubeedge/kubeedge/build/tools/certgen.sh stream 

確保如下目錄存在,如否創建之,否則證書無法生成:

mkdir -p /etc/kubeedge/ca
mkdir -p /etc/kubeedge/certs

注:

設置 iptables:

iptables -t nat -A OUTPUT -p tcp  --dport 10350 -j DNAT --to 192.168.0.102:10003

(注:設置 NAT 端口轉發)

2.4 創建設備模塊和設備CRD yaml 文件

# cd $GOPATH/src/github.com/kubeedge/kubeedge/build/crds/devices
# kubectl create -f devices_v1alpha1_devicemodel.yaml
# kubectl create -f devices_v1alpha1_device.yaml

# cd $GOPATH/src/github.com/kubeedge/kubeedge/build/crds/reliablesyncs
# kubectl create -f cluster_objectsync_v1alpha1.yaml
# kubectl create -f objectsync_v1alpha1.yaml

注:新版本的 yaml 文件有兩類,在 devices 和 reliablesyncs 目錄。可用kubectl get crds查看。

2.6 配置雲端節點

新版本配置文件由 cloudcore 程序生成,執行:

# cd $GOPATH/src/github.com/kubeedge/kubeedge/_output/local/bin
# mkdir -p /etc/kubeedge/config/ 
# ./cloudcore --minconfig > /etc/kubeedge/config/cloudcore.yaml 

注1:cloudcore --minconfig生成最小配置,類似有cloudcore --defaultconfig
注2:cloudcore 默認使用的配置文件爲/etc/kubeedge/config/cloudcore.yaml
邊緣端類似,下省略。

接着修改配置文件:

# vim /etc/kubeedge/config/cloudcore.yaml 

官方默認爲kubeconfig: "/root/.kube/config",本文改爲 kubeconfig: "/home/ubuntu/.kube/config"。其它保持默認。注:具體的路徑,取決於部署 k8s 時的選擇,就前2個路徑而言,前者一般由 root 權限運行,後者是普通權限(普通用戶)運行。

2.7 配置邊緣節點

新版本配置文件由 edgecore 程序生成,因此,需要在邊緣端機器上執行。具體參考下文。

2.8 mqtt

mqtt 只有邊緣端需要
如果邊緣端爲 ubuntu 系統,直接使用源安裝:

# add-apt-repository ppa:mosquitto-dev/mosquitto-ppa // 添加源
# apt-get update // 更新
# apt-get install mosquitto // 安裝mqtt服務端
# apt-get install mosquitto-clients // 如果需要測試,則安裝mqtt客戶端

另外也可以使用源碼編譯。

在 ubuntu 系統,安裝 mosquitto 成功後會自動啓動服務。由於 KubeEdge 使用多個端口,故需用配置文件。服務端添加多端口:

vim /etc/mosquitto/conf.d/port.conf
port 1883
listener 1884

此處指定 1883 和 1884 端口,從 KubeEdge 生成配置文件可知。沒有指定協議,默認使用 mqtt。修改配置後需要重啓:

/etc/init.d/mosquitto restart

或者手動啓動:

/usr/sbin/mosquitto -d -c /etc/mosquitto/mosquitto.conf

建議使用系統級別服務,預防漏掉此步驟,導致 KubeEdge 測試失敗。

可用如下命令驗證服務是否正常:

mosquitto_pub -h <mqtt服務IP> -p 1884 -t "hello" -m "this is hello world"

如果出現Error: Connection refused表示服務(及對應的端口)未啓動。

題外話:
在嵌入式 ARM Linux 環境中,Buildroot 已包含 mosquitto,可直接勾選。此處略,根據筆者實驗,Buildroot 的 mosquitto 所有配置均在文件 /etc/mosquitto/mosquitto.conf 中。使用如下命令操作:

systemctl restart mosquitto  // 重啓
systemctl stop mosquitto     // 停止

三、部署

3.1 雲端

3.1.1 查看 k8s 集羣

查看節點狀態:

# kubectl get node
NAME           STATUS   ROLES        AGE    VERSION
latelee-master Ready    master       3m     v1.17.3

此刻只有雲端節點就緒。

3.1.3 運行雲端核心

可以另建目錄運行程序,也可以在程序生成目錄,此處選擇後者,方便調試。

# cd $GOPATH/src/github.com/kubeedge/kubeedge/_output/local/bin
# ./cloudcore   // 建議先如此,方便觀察日誌
也可以:
# nohup ./cloudcore > cloudcore.log 2>&1 &

如果使用系統服務方式,啓動腳本爲build/tools/cloudcore.service,需修改ExecStart爲真實值。
示例如下:

[Unit]
Description=cloudcore.service

[Service]
Type=simple
Restart=always
ExecStart=/etc/kubeedge/cloudcore

[Install]
WantedBy=multi-user.target

添加服務命令:

cp build/tools/cloudcore.service /etc/systemd/system/cloudcore.service
sudo systemctl daemon-reload
sudo systemctl start cloudcore

注意,1.3版本不再需要手動生成證書,改爲用 k8s secret 方式,因此,必須先運行雲端,纔會生成 secret(至少成功運行一次,以產生 secret),否則無法得到 token,無法配置邊緣端。

3.2 邊緣端

3.2.1 分發

前面已經準備好了文件,直接部署就方便很多。注意,需要將邊緣端可執行文件拷貝到邊緣機器上。方式多種,建議使用 scp 命令。前提是安裝了 SSH 協議。在邊緣端機器上執行拷貝(也稱爲分發)示例

# mkdir -p /etc/kubeedge/config ~/kubeedge/
# cd ~/kubeedge/
# scp -r 192.168.0.102:/home/ubuntu/kubeedge/src/github.com/kubeedge/kubeedge/_output/local/bin/edgecore ~/kubeedge/

注1:此操作在邊緣端機器上,非雲端。假設部署工程目錄爲~/kubeedge
注2:1.3.0 版本無須手動拷貝證書,在運行邊緣端時會自動從雲端獲取並存儲在/etc/kubeedge/目錄下,從結果看,依然生成 /etc/kubeedge/ca 和 /etc/kubeedge/certs ,亦即與前面版本保持一致。
注3:如果以其它登陸用戶身份拷貝,可在IP地址前加用戶名,如sudo scp -r [email protected]:/etc/kubeedge/* /etc/kubeedge

3.2.2 獲取 token

前文已經運行了雲端,會自動產生token,這裏要先切換到雲端機器,獲取 secret,再將 tokendata 內容解碼得到 token。示例如下:

# kubectl get secret tokensecret -n kubeedge -oyaml
輸出:
apiVersion: v1
data:
  tokendata: ZWE1NDg3YWNhYjZlMWEwNmE2OGI5OTNkOTMxNGVlMzA5OTg2YzJkM2MyOTkzMmNlNGI2NTE2MzI0NzljMDlhOC5leUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKbGVIQWlPakUxT1RFeE1USXhNamg5LlVoUHBBdnR6YmhMZkcycUNaZmtqX3Zoak9qbEw5VEFQdElGWkJQTlpuZ0E=
kind: Secret
...

解碼:
# echo ZWE1NDg3YWNhYjZlMWEwNmE2OGI5OTNkOTMxNGVlMzA5OTg2YzJkM2MyOTkzMmNlNGI2NTE2MzI0NzljMDlhOC5leUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKbGVIQWlPakUxT1RFeE1USXhNamg5LlVoUHBBdnR6YmhMZkcycUNaZmtqX3Zoak9qbEw5VEFQdElGWkJQTlpuZ0E= | base64 -d

輸出結果爲:
ea5487acab6e1a06a68b993d9314ee309986c2d3c29932ce4b651632479c09a8.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExMTIxMjh9.UhPpAvtzbhLfG2qCZfkj_vhjOjlL9TAPtIFZBPNZngA      // 注意解碼後字符串沒有換行,要仔細覈對

再次強調,本小節在雲端機器執行。

3.2.3 配置

生成配置文件:

# ./edgecore --minconfig > /etc/kubeedge/config/edgecore.yaml

修改配置文件:

# vim /etc/kubeedge/config/edgecore.yaml

注意3處地方。

  1. 修改websocket下的server,默認爲127.0.0.1:10000,需改爲實際雲端 IP 地址,此處爲192.168.0.102:10000
  2. 修改(確認)podSandboxImage,X86平臺爲podSandboxImage: kubeedge/pause:3.1(默認),ARM 平臺根據位數不同,可設爲kubeedge/pause-arm:3.1ubeedge/pause-arm64:3.1
  3. 填寫 edgeHub 下的 token 值,見 3.2.2 小節生成的結果。
    其它要點:cgroup 驅動默認值爲:cgroupDriver: cgroupfs,無須改 Docker 配置。網絡設備接口名稱及 IP 地址,會運行上述命令時自動檢測獲取,無須修改(注:在一臺虛擬機中,網卡爲 enp0s3,但配置文件中依然爲 eth0)。

3.2.4 運行

接上,運行邊緣端核心:

# ./edgecore // 建議先如此,方便觀察日誌
也可以:
# nohup ./edgecore > edgecore.log 2>&1 &

如果使用系統服務方式,啓動腳本爲build/tools/edgecore.service,需修改ExecStart爲真實值。
示例如下:

[Unit]
Description=edgecore.service

[Service]
Type=simple
Restart=always
ExecStart=/etc/kubeedge/edgecore

[Install]
WantedBy=multi-user.target

添加服務:

cp build/tools/edgecore.service /etc/systemd/system/edgecore.service
sudo systemctl daemon-reload
sudo systemctl start edgecore

這裏再強調一次 mqtt,另起終端,運行命令:

/usr/sbin/mosquitto -d -c /etc/mosquitto/mosquitto.conf

3.3 驗證

在雲端查看狀態:

# kubectl get node
NAME           STATUS   ROLES        AGE    VERSION
latelee-master Ready    master       24m    v1.17.3
latelee-node   Ready    agent,edge   2m9s   v1.17.1-kubeedge-v1.3.0-beta.0.49+5bfca35b2d99a5-dirty

雲端和邊緣端均爲 Ready 狀態。

嘗試部署官方的 deployment:

kubectl apply -f $GOPATH/src/github.com/kubeedge/kubeedge/build/deployment.yaml

輸出示例:

# kubectl get pod -owide
NAME                                           READY   STATUS    RESTARTS   AGE     IP       NODE         NOMINATED NODE   READINESS GATES
nginx-deployment-77698bff7d-zf5c6              1/1     Running   0          110s   <none>   latelee-node    <none>           <none>

擴容測試:

kubectl scale deployment nginx-deployment --replicas=4

預期效果:有4個pod出現,但只有1個正常運行,因爲該 deployment 導出了節點端口,前面的 pod 已經佔用,無法再分配。理論上,如果有4個節點,則會自動將 deployment 調度到4個節點上。輸出示例:

# kubectl get pod 
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-77698bff7d-b9mlc   0/1     Pending   0          6s
nginx-deployment-77698bff7d-ddvl2   0/1     Pending   0          6s
nginx-deployment-77698bff7d-p6k8t   0/1     Pending   0          7s
nginx-deployment-77698bff7d-zf5c6   1/1     Running   0          2m27s

刪除:

kubectl delete -f $GOPATH/src/github.com/kubeedge/kubeedge/build/deployment.yaml

測試小記:

  1. 初步測試,可以正常刪除 pod了。
  2. 再次運行雲端程序,查看 tokensecret,發現值不同,但邊緣端使用舊的 token 可以成功連接。

四、ARM部署

ARM的部署十分簡單,將 edgecore 交叉編譯即可,其它與 X86 環境是一致的,這裏再列一次:創建對應的目錄,拷貝邊緣端可執行二進制文件,啓動mqtt(一般作爲系統服務隨系統啓動而啓動),運行邊緣端。
安裝交叉編譯器:

sudo apt-get install gcc-arm-linux-gnueabihf

設置環境變量並編譯:

export GOARCH=arm
export GOOS="linux"
export GOARM=7 
export CGO_ENABLED=1
export CC=arm-linux-gnueabihf-gcc
export GO111MODULE=off
make all WHAT=edgecore

注:KubeEdge 已經將依賴包納入代碼倉庫,直接編譯即可,不需要下載額外的包,爲安全,可暫時禁止 GO111MODULE。

五、清除

kubeedge運行文件:
1、/etc/kubeedge/: 證書、配置文件(雲邊均有)。
2、/var/lib/kubeedge/: 雲端有socket文件kubeedge.sock ,邊緣端有數據庫文件edgecore.db。

如果要完全清理kubeedge環境,上述目錄需要刪除。

六、問題

在測試時發現的問題及解決方法。

1、
雲端運行時出現:

# ./cloudcore 
[address: Invalid value: "unix:///var/lib/kubeedge/kubeedge.sock": unixSocketAddress unix:///var/lib/kubeedge/kubeedge.sock dir /var/lib/kubeedge not exist , need create it]

解決:此目錄保存socket文件,需要手動創建目錄

mkdir -p /var/lib/kubeedge

備註:1.3.0 版本似乎無須手動創建了。

2、
雲端無法獲取請求的資源:

./cloudcore
...
github.com/kubeedge/kubeedge/cloud/pkg/synccontroller/synccontroller.go:162: Failed to list *v1alpha1.ObjectSync: the server could not find the requested resource (get objectsyncs.reliablesyncs.kubeedge.io)

沒有執行$GOPATH/src/github.com/kubeedge/kubeedge/build/crds/reliablesyncs目錄的yaml文件,參見 2.4 小節。

3、
Cgroup 驅動不匹配:

[CGroupDriver: Invalid value: "groupfs": CGroupDriver value error]

如果 Docker 使用的驅動方式爲 systemd,則需要修改 yaml 文件爲 systemd,如使用 cgroupfs,Docker 也要保持一致。

4、
邊緣端機器的配置文件的主機名稱、IP,必須與真實主機一致。否則會註冊不成功。

5、
節點註冊失敗:

create node LATE-LEE error: Node "LATE-LEE" is invalid: metadata.name: Invalid value: "LATE-LEE": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') , register node failed

主機名稱不合法,必須是小寫字母、數字,其它字符只能是-.(下劃線也不行),而且名稱的開頭和結尾必須是小寫字母。(注:這是k8s dns命名的一個規範)。

6、
清理相關。

Failed to check the running environment: Kubelet should not running on edge node when running edgecore

一般出現於k8s和kubeedge混用情況,需要完全清理k8s方可。相似問題有:

Orphan pod: Orphaned pod "8685b805-a1c7-4687-8ce8-c77d24af5828" found, but volume paths are still present on disk

如果要重新運行edgecore,需要刪除/var/lib/kubeedge/edgecore.db

7、
邊緣端有配置 token:

Error: token are in the wrong format

需要在雲端生成 token,並填寫邊緣端配置文件的 token 字段。

8、
mqtt 服務未運行。提示:

connect error: Network Error : dial tcp 127.0.0.1:1883: connect: connection refused

根據前文運行 mqtt。

9、
無法查看邊緣端 pod 日誌:

# kubectl logs nginx-deployment-77698bff7d-wmqfx
Error from server: Get https://192.168.0.140:10350/containerLogs/default/nginx-deployment-77698bff7d-zf5c6/nginx: dial tcp 192.168.0.140:10350: connect: connection refused

在邊緣端查看端口:

# netstat -ntpl | grep 10350
tcp        0      0 127.0.0.1:10350         0.0.0.0:*               LISTEN      5690/edgecore   

該問題目前還沒排查

七、小結

KubeEdge 在2020年5月中旬(即本文發表前16天)發佈了1.3.0。筆者本想在發佈時進行測試驗證(主要想看看新特性),但宥於家庭瑣事,既想研究技術又想把家庭照料得很好是兩難全之事。但最終還是下定決心,利用幾個深夜時間慢慢摸索,總算有了本文。

參考

  • 官方源碼倉庫: https://github.com/kubeedge/kubeedge
  • 官方鏡像: https://hub.docker.com/u/kubeedge
  • 壓縮包: https://github.com/kubeedge/kubeedge/releases
  • 官方安裝文檔:https://docs.kubeedge.io/en/latest/setup/setup.html
  • KubeEdge環境を構築してみた by AWS EC2: https://qiita.com/S-dwinter/items/f1e92f21d4b23fbbba80
  • KubeEdge 部署: https://www.latelee.org/kubeedge/kubeedge-deploy.html
  • KubeEdge 1.2.0 部署: https://www.latelee.org/kubeedge/kubeedge-deploy-v1.2.0.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章