Kubernetes之九---kubernetes資源清單定義入門

認識kubernetes資源

1.1 常用資源/對象

  •  workload工作負載型資源:pod,ReplicaSet,Deployment,StatefulSet,daemonset,job...
  •  服務器發現及均衡:Service,Lngress...
  •  配置與存儲:volume,CSI
    •  configmap,secret
    •  downwardAPI
  •  集羣級資源
    •  namespace,node,role,clusterrole,rolebinding,clusterrolebinding
  •  元數據型資源
    •  HPA,podtemplate,limitrange

 

1.2 創建資源的方法

  •  apiserver 僅接受JSON格式的資源定義;
  •  yaml格式提供配置清單,apiserver 可自動將其轉爲JSON格式,而後再提交;

 

1.3 大部分(主流)資源的配置清單:有5個一級字段組成

  •  apiserver:group/version
    •  查詢當前支持哪些apiserver:$ kubectl api-versions
  •  kind:資源類別
  •  metadata:元數據
    •  name:名稱
    •  namespace:名稱空間
    •  labels:標籤
    •  annotation:資源註解
    •  selfLink:每個資源的引用PATH,/api/GROUP/VERSION/namespaces/NAMESPACE/TYPE/NAME
  •  spec:期望的狀態(disired state),期望資源應該用於什麼特性
  •  status:當前狀態(current state),本字段由kubernetes集羣維護,用戶不能自己定義

Pod 對象的生命週期

 Pod 對象自從其創建開始至其終止退出的時間範圍稱爲其生命週期。 在這段時間中, Pod 會處於多種不同的狀態, 並執行一些操作;其中,創建主容器( main container)爲必 需的操作,其他可選的操作還包括運行初始化容器 ( init container)、容器啓動後鉤子( post start hook)、容器的存活性探測( liveness probe)、 就緒性探測( readiness probe)以及容器終 止前鉤子(pre stop hook)等,這些操作是否執行則取決於 Pod 的定義,如圖所示。

無論是類似前面幾節中的由用戶手動創建,還是通過 Deployment 等控制器創建, Pod 對象總是應該處於其生命進程中以下幾個相位(phase)之一。

  • Pending : API Serv巳r 創建了 Pod 資源對象並已存入巳etcd 中,但它尚未被調度完成, 或者仍處於從倉庫下載鏡像的過程中。
  • Running: Pod 已經被調度至某節點,並且所有容器都已經被 kubelet 創建完成o D Succeeded: Pod 中的所有容器都已經成功終止並且不會被重啓 。
  • Failed :所有容器都已經終止,但至少有一個容器終止失敗, 即容器返回了非 0 值的 退出狀態或已經被系統終止。
  • Unknown : API Server 無法正常獲取到 Pod 對象的狀態信息,通常是由於其無法與 所在工作節點的 kubelet 通信所致。

Pod 的相位是在其生命週期中的宏觀概述,而非對容器或 Pod 對象的綜合彙總,而且相 位的數量和含義被嚴格界定,它僅包含上面列舉的相位值。

1.4 使用kubectl explain查詢每個資源如何配置

(1)例如查詢如何定義pod資源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@master ~]# kubectl explain pod
KIND:     Pod
VERSION:  v1
 
DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.
 
FIELDS:
   apiVersion   <string>
... ...
   kind <string>
... ...
 
   metadata <Object>
... ...
   spec <Object>
... ...
   status   <Object>
... ...

  

(2)能一級一級進入查詢;如查詢定義pod 的metadata字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@master ~]# kubectl explain pod.spec
KIND:     Pod
VERSION:  v1
 
RESOURCE: spec <Object>
 
DESCRIPTION:
... ...
 
FIELDS:
... ..
   affinity <Object>
... ...
[root@master ~]# kubectl explain pod.spec.containers
KIND:     Pod
VERSION:  v1
 
RESOURCE: containers <[]Object>
 
DESCRIPTION:
... ...
FIELDS:
   args <[]string>
... ...
   command  <[]string>
... ...  

自己定義資源時,不清楚如何定義,可以進行快速的查詢

 

1.5 示例

(1)查詢集羣中的pod(上篇創建的pod)

1
2
3
4
5
6
[root@master ~]# kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
client                        1/1       Running   0          4h
myapp-848b5b879b-9slqg        1/1       Running   0          46m
myapp-848b5b879b-wtrjr        1/1       Running   0          46m
myapp-848b5b879b-z2sqc        1/1       Running   0          46m

(2)-o yaml輸出爲yaml格式,查看pod創建的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@master ~]# kubectl get pod myapp-848b5b879b-9slqg -o yaml
apiVersion: v1  #api版本
kind: Pod  #資源類別
metadata:  #元數據
  annotations:
    cni.projectcalico.org/podIP: 10.244.1.60/32
  labels:
    pod-template-hash"4046164356"
    run: myapp
  name: myapp-848b5b879b-9slqg
  namespace: default
... ...
  selfLink: /api/v1/namespaces/default/pods/myapp-848b5b879b-9slqg
spec:  #規格、規範;期望資源應該用於什麼特性;期望目標狀態
... ...
status:  #當前狀態
... ...

1.6 演示:基於yaml格式文件,創建pod

1
2
[root@master ~]# mkdir manifests
[root@master ~]# cd manifests/

(1)編寫pod-demo.yaml文件

創建2個容器,一個運行nginx;一個在busybox中執行sleep命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@master manifests]# vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  #labels: {app:myapp, tier:frontend} #映射可以寫爲{}形式;
  labels: #也可以在下邊分級寫
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  - name: busybox
    image: busybox:latest
    #command: ["/bin/sh","-c","sleep 3600"]  #列表可以寫爲[]形式;
    command#也可以在下邊分級寫,要加-
    "/bin/sh"
    "-c"
    "sleep 3600"

(2)基於pod-demo.yaml 文件創建create pod

1
2
[root@master manifests]# kubectl create -f pod-demo.yaml
pod/pod-demo created

  

(3)驗證

① 查詢創建pod的信息

1
2
3
4
5
6
7
8
9
10
[root@master manifests]# kubectl create -f pod-demo.yaml
pod/pod-demo created
[root@master manifests]# kubectl get pods -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP            NODE
pod-demo   2/2       Running   0          1m        10.244.1.61   node1
---查看詳細信息
[root@master manifests]# kubectl describe pods pod-demo
Name:               pod-demo
Namespace:          default
... ...

② 訪問pod中的服務

1
2
3
4
5
[root@master manifests]# curl 10.244.1.61
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
---查詢pod產生的日誌
[root@master manifests]# kubectl logs pod-demo myapp
192.168.130.104 - - [23/Jan/2019:05:35:35 +0000] "GET / HTTP/1.1" 200 65 "-" "curl/7.29.0" "-"

③ 基於yaml文件刪除pod

1
2
3
4
[root@master manifests]# kubectl delete -f pod-demo.yaml
pod "pod-demo" deleted
[root@master manifests]# kubectl get pods
No resources found.

  

2、Pod資源

2.1 Pod資源常用選項

  •  metadata.label:標籤
    •  key=value
      •  key:字母、數字、_、-、.
      •  value:可以爲空,只能字母或數字開頭及結尾,中間可使用字母、數字、_、-、.
  •  metadata.annotations:資源註解
  •  spec.containers <[]object>
    •  - name:容器名稱
    •    image:鏡像
    •    imagePullPolicy:下載鏡像規則,若鏡像時latest標籤,默認是Always;否則默認IfNotPresen
      •  Always總是鏡像,Never不下載鏡像,IfNotPresent本地有則不下載
    •  ports:從容器中公開的端口列表
      •  containerPort:Pod中服務的端口號
      •  hostIP:暴露綁定在主機哪個IP上
      •  hostPort:暴露在主機的端口號
      •  name:暴露這個端口的名稱
    •  args:參數
    •  command:執行命令
  •  spec.nodeSelector:節點標籤選擇器

 

2.2 演示

(1)修改pod-demo.yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@master manifests]# vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  #labels: {app:myapp, tier:frontend} #映射可以寫爲{}形式;
  labels: #也可以在下邊分級寫
    app: myapp
    tier: frontend
  annotations:
    along.com/created-by"cluster admin"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    #command: ["/bin/sh","-c","sleep 3600"]  #列表可以寫爲[]形式;
    command#也可以在下邊分級寫,要加-
    "/bin/sh"
    "-c"
    "sleep 3600"
  nodeSelector:
    disktype: ssd

  

(2)將node1節點打上disktype=ssd的標籤

1
2
3
4
[root@master manifests]# kubectl label node node1 disktype=ssd
[root@master manifests]# kubectl get nodes node1 --show-labels
NAME      STATUS    ROLES     AGE       VERSION   LABELS
node1     Ready     <none>    140d      v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node1

(3)基於yaml文件創建pod

1
2
[root@master manifests]# kubectl create -f pod-demo.yaml
pod/pod-demo created

(4)驗證

1
2
3
4
5
6
7
8
--- pod只會創建到node1節點上,因爲node1的disktype=ssd標籤
[root@master manifests]# kubectl get pod -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP            NODE
pod-demo   2/2       Running   0          11s       10.244.1.68   node1
--- -l 指定標籤,實現標籤過濾
[root@master manifests]# kubectl get pods --show-labels -l app
NAME       READY     STATUS    RESTARTS   AGE       LABELS
pod-demo   2/2       Running   0          30s       app=myapp,tier=frontend

  

3、Pod健康檢測

3.1 pod健康檢測介紹

  •  pod健康檢測分爲存活性探測、 就緒型探測;這在生產環境幾乎是必須配置的;
  •  如果沒有就緒型探測;pod一啓動就會被分配用戶流量;若pod中的服務像tomcat等,需要時間啓動;就會導致有一定時間,用戶訪問不到服務;
  •  如果沒有存活性探測:pod中服務一旦失敗,沒有檢測,不會將容器重啓關閉;也會導致用戶訪問服務失敗。

 目前, Kubernetes 的容器支持存活性探測的方法包含以下三種: ExecAction、 TCPSocketAction 和 HTTPGetAction

3.2 pod健康檢測選項

(1)在spec字段下、containers字段配置,可使用explain查看詳細用法

$ kubectl explain pod.spec.containers.

  •  livenessProbe 存活性探測
    •  exec:指定檢測的命令
    •  failureThreshold:連續失敗次數被認爲失敗,默認爲3,最小值爲1
    •  httpGet:指定要執行的http請求
    •  initialDelaySeconds:在容器啓動多少秒後再檢測
    •  periodSeconds:每隔多少秒探測一次;默認爲10秒。最低限度值是1
    •  successThreshold:連續成功次數認爲服務正常
    •  tcpSocket:定涉及TCP端口的操作
    •  timeoutSeconds:探測超時的秒數,默認爲1秒
  •  readinessProbe 就緒型探測(和livenessProbe 存活性探測選項一樣)

 

3.3 容器重啓策略

容器程序發生崩潰或容器申請超出限制的資源等原因都可能會導致 Pod 對象的終止, 此時是否應該重建該 Pod 對象則取決於其重啓策略(restartPolicy)屬性的定義。

  •  Always: 但凡 Pod 對象終止就將其重啓,此爲默認設定。
  • OnFailure :僅在 Pod 對象出現錯誤時方纔將其重啓 。
  • Never: 從不重啓 。

需要注意的是, restartPolicy 適用於 Pod 對象中的所有容器,而且它僅用於控制在同一 節點上重新啓動 Pod 對象的相關容器。 首次需要重啓的容器,將在其需要時立即進行重啓, 隨後再次需要重啓的操作將由 k:ubelet 延遲一段時間後進行,且反覆的重啓操作的延遲時長 依次爲 10 秒、 20 秒、 40 秒、 80 秒、 160 秒和 300 秒, 300 秒是最大延遲時長。 事實上, 一 旦綁定到一個節點, Pod 對象將永遠不會被重新綁定到另一個節點,它要麼被重啓,要麼終 止, 直到節點發生故障或被刪除。

 

$ kubectl explain pod.spec.restartPolicy.   

  • Always:總是重啓(默認)
  • OnFailure:只有容器狀態爲錯誤時,才重啓
  • Never:絕不重啓

 

3.4 演示:exec方式實現存活性探測

exec 類型的探針通過在目標容器中執行由用戶自定義的命令來判定容器的健康狀態, 若命令狀態返回值爲 0 則表示“成功”通過檢測,其值均爲“失敗”狀態。 “spec.containers. !iv巳nessProbe.exec”字段用於定義此類檢測,它只有一個可用屬性“"command ",用於指定 要執行的命令。

(1)編寫yaml文件

當探測到/tmp/healthy文件不存在時,認爲服務故障;

容器在30秒後執行刪除/tmp/healthy文件

[root@master manifests]# vim liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 3600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]

(2)創建運行pod

1
2
3
4
5
[root@master manifests]# kubectl create -f liveness-exec.yaml
pod/liveness-exec-pod created
[root@master manifests]# kubectl get pods
NAME                READY     STATUS        RESTARTS   AGE
liveness-exec-pod   1/1       Running       0          6s

  

(3)等30s,容器就會檢測失敗,重啓pod;使用describe可以查看詳細信息

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@master manifests]# kubectl describe pods liveness-exec-pod
... ...
    State:          Running
      Started:      Wed, 23 Jan 2019 16:58:09 +0800
    Last State:     Terminated  #上次狀態爲終止
      Reason:       Error
      Exit Code:    137
      Started:      Wed, 23 Jan 2019 16:57:01 +0800
      Finished:     Wed, 23 Jan 2019 16:58:09 +0800
    Ready:          True
    Restart Count:  1  #重啓次數1次
    Liveness:       exec [test -e /tmp/healthy] delay=1s timeout=1s period=3s #success=1 #failure=3
... ...

  

3.5 演示:httpget方式實現存活性探測

基於 HTTP 的探測 ( HTTPGetAction) 向 目標容器發起一個 HTTP 請求,根據其響應碼進行結果判定,響應碼形如 2xx 或 3xx 時表示檢測通過。 “ spec.containers.livenessProbe. httpGet” 字段用於定義此類檢測,它的可用配置字段包括如下幾個。 

  • host <string>:請求的主機地址, 默認爲 Pod IP;也可以在 httpHeaders 中使用“Host:” 來定義。 
  • port <string>: 請求的端口 ,必選宇段。 
  • bttpHeaders <[]Object> : 自定義的請求報文首部。
  • path <string> : 請求的 HTTP 資源路徑,即 URL path。
  • scheme: 建立連接使用的協議,僅可爲 HTTP 或 HTTPS , 默認爲 HTTP。

下面是一個定義在資源清單文件 liveness佔ttp.yarr讓中的示例,它通過 lifecycle 中的 postSta此 book 創建了一個專用於 httpGet 測試的頁面文件 bealthz:

(1)編寫yaml文件,創建並運行pod

當探測不到容器內80端口,和提供80端口的/index.html文件時,認爲服務故障;

[root@master manifests]# cat  liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      httpGet:               # 注意Get要大寫
        port: http
        path: /index.html    # 訪問此默認頁
      initialDelaySeconds: 1  # 在容器啓動後1秒開始檢測
      periodSeconds: 3        # 每隔3秒時間探測一次
      failureThreshold: 2  # 成功狀態下連續檢測兩次失敗將會發生重啓
  restartPolicy: Always

(2)手動連入容器,刪除index.html文件

1
2
[root@master manifests]# kubectl exec -it liveness-httpget-pod -- /bin/sh
# rm -f /usr/share/nginx/html/index.html

(3)容器會檢測失敗,重啓pod;使用describe可以查看詳細信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master manifests]# kubectl describe pods liveness-httpget-pod
... ...
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 23 Jan 2019 17:10:03 +0800
    Last State:     Terminated  #上次狀態爲終止
      Reason:       Completed
      Exit Code:    0
      Started:      Wed, 23 Jan 2019 17:08:22 +0800
      Finished:     Wed, 23 Jan 2019 17:10:03 +0800
    Ready:          True
    Restart Count:  1  #重啓次數1次
    Liveness:       http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3
... ...

3.6 演示:exec方式實現就緒性探測

  • Pod 對象啓動後,容器應用通常需要一段時間才能完成其初始化過程,例如加載配置或 數據,甚至有些程序需要運行某類的預熱過程,若在此階段完成之前即接人客戶端的請求, 勢必會因爲等待太久而影響用戶體驗。 因此,應該避免於 Pod 對象啓動後立即讓其處理客 戶端請求,而是等待容器初始化工作執行完成並轉爲“就緒”狀態,尤其是存在其他提供相 同服務的 Pod 對象的場景更是如此。
  • 與存活性探測機制類似,就緒性探測是用來判斷容器就緒與否的週期性(默認週期爲 10 秒鐘)操作, 它用於探測容器是否已經初始化完成並可服務於客戶端請求,探測操作返 回“success”狀態時,即爲傳遞容器已經“就緒”的信號。
  • 與存活性探測機制相同,就緒性探測也支持 Ex町、 HTTP GET 和 TCP Socket 三種探測 方式,且各自的定義機制也都相同。 但與存活性探測觸發的操作不同的是,探測失敗時,就緒性探測不會殺死或重啓容器以保證其健康性,而是通知其尚未就緒,並觸發依賴於其就 緒狀態的操作(例如,從 Service 對象中移除此 Pod 對象)以確保不會有客戶端請求接入此 Pod 對象。 不過,即便是在運行過程中, Pod 就緒性探測依然有其價值所在,例如 Pod A 依 賴到的 Pod B 因網絡故障等原因而不可用時, Pod A 上的服務應該轉爲未就緒狀態,以免無 法向客戶端提供完整的響應。
  • 將容器定義中的 livenessProbe 字段名替換爲 readinessProbe 即可定義出就緒性探測的配 置, 一個簡單的示例如下面的配置清單( readiness-exec. yam!)所示,它會在 Pod 對象創建完成 3秒鐘後使用 test -e/tmp/healthy 命令來探測容器的就緒性,命令執行成功即爲就緒,探 測週期爲 3秒鐘:

(1)編寫yaml文件,創建啓動容器

當探測到/tmp/healthy文件不存在時,就認爲服務就緒不成功;pod啓動失敗;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@master manifests]# vim readiness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
  name: readiness-exec-pod
  namespace: default
spec:
  containers:
  - name: readiness-exec-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 3600"]
    command: ["sleep 3600"]
    readinessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]
      periodSeconds: 3  # 探測週期we爲3秒鐘
  restartPolicy: Always
[root@master manifests]# kubectl create -f readiness-exec.yaml
pod/readiness-exec-pod created

  

(2)查看,pod啓動就緒失敗

1
2
3
[root@master ~]# kubectl get pods
NAME                 READY     STATUS              RESTARTS   AGE
readiness-exec-pod   0/1       RunContainerError   1          12s

  

4、Pod啓動前/後鉤子

4.1 介紹

  •  pod在啓動前後都可以設置鉤子hook;在spec.containers.lifecycle字段下設置;
  •  postStart:創建容器後立即調用PostStart操作;如果失敗,根據重啓策略終止;
  •  preStop:在容器終止之前立即調用PreStop操作,該容器在處理程序完成後終止

 生命週期鉤子函數( lifecycle hook)是編程語言(如 Angular)中常用的生命週期管理的 組件,它實現了程序運行週期中的關鍵時刻的可見性,並賦予用戶爲此採取某種行動的能 力。 類似地, 容器生命週期鉤子使它能夠感知其自身生命週期管理中的事件,並在相應的時 刻到來時運行由用戶指定的處理程序代碼。 Kubernetes 爲容器提供了兩種生命週期鉤子。

  •  postStart :於容器創建完成之後立即運行的鉤子處理器( handler),不過 Kubernetes 無法確保它一定會於容器中的 ENTRYPOINT 之前運行。
  •  preStop :於容器終止操作之前立即運行的鉤子處理器,它以同步的方式調用,因此 在其完成之前會阻塞刪除容器的操作的調用。

鉤子處理器的實現方式有“Exec ”和“HTTP ”兩種,前一種在鉤子事件觸發時直接在 當前容器中運行由用戶定義的命令,後一種則是在當前容器中向某 URL 發起 HTTP 請求。

postStart 和 preStop 處理器定義在容器的 spec.lifecycle 嵌套字段中,其使用方法如下面 的資源清單所示,讀者可自行創建相關的 Pod 資源對象,井驗證其執行結果:

4.2 語法

$ kubectl explain pod.spec.containers.lifecycle

  •  postStart
    •  exec:指定了要採取的命令;
    •  httpGet:指定要執行的http請求;
    •  tcpSocket:指定涉及TCP端口的操作
  •  preStop (和postStart命令一樣)

 

4.3 演示:使用exec設置pod啓動前鉤子

(1)編寫yaml文件,創建啓動容器

啓動容器前,先創建準備一個httpd服務的主頁面文件/tmp/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master manifests]# vim poststart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: poststart-pod
  namespace: default
spec:
  containers:
  - name:  poststart-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    lifecycle:
      postStart:
        exec:
          command: ['/bin/sh','-c','echo hello > /tmp/index.html']
    command: ['/bin/sh','-c','/bin/httpd -f -h /tmp']
[root@master manifests]# kubectl create -f poststart-pod.yaml
pod/poststart-pod created

(2)驗證,訪問服務

[root@master ~]# kubectl get pods -o wide
NAME            READY     STATUS    RESTARTS   AGE       IP            NODE
poststart-pod   1/1       Running   0          26s       10.244.2.69   node2
[root@master ~]# curl 10.244.2.69
hello

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章