1. 程式人生 > >Rainbond叢集的安裝和運維的原理

Rainbond叢集的安裝和運維的原理

本文將解讀Rainbond叢集的安裝和運維的原理,使使用者基本瞭解Rainbond的安裝機制和運維重點,便於使用者搭建大型Rainbond叢集。

1.Rainbond叢集節點概述

1.1 節點分類

屬性型別說明
manage管理節點集結平臺自身元件,提供應用構建、排程、管理等功能,提供資料中心基礎服務與API介面,充當控制叢集的角色。
gateway閘道器節點叢集內應用被外網訪問的流量入口和負載均衡器,提供HTTP, HTTPs路由, TCP/UDP服務, 負載均衡器, 高階路由(A/B測試, 灰度釋出)等功能。
compute計算節點提供應用執行的計算資源,N個計算節點組成計算資源池供給管理節點靈活排程。

1.2 節點部署主要服務元件概述

角色元件說明
rbd-dns提供本地dns服務,服務於叢集內應用的DNS解析。
etcd管理節點etcd
kube-controller-managerKubernetes管理元件之一, Pod編排器
rbd-webcli提供應用web方式進入容器命令列的服務
nfs_server遠端儲存掛載
rbd-hub基於Docker Registry封裝,提供Docker映象儲存服務,服務於資料中心內部
kube-schedulerKubernetes管理元件之一,Pod排程器
docker應用容器引擎
rbd-mq訊息佇列服務
calico叢集SDN服務,為應用提供網路支援
rbd-chaos應用構建服務,提供原始碼,Docker映象等方式持續構建應用。
rbd-worker應用執行控制器
kube-apiserverKubernetes管理元件之一, 提供API服務
rbd-eventlogRainbond 事件處理與日誌匯聚服務
rbd-monitorRainbond 監控管理服務,基於Prometheus封裝
rbd-apiRainbond API服務,資料中心控制層面的入口。
rbd-dbRainbond 資料庫服務,支援MySQL,Tidb與CockroachDB
rbd-app-ui應用控制檯web服務
rbd-repo原始碼構建倉庫服務,基於Artifactory OSS封裝
nodeRainbond 叢集和節點控制器服務
etcd-proxy計算節點etcd-proxy
rbd-dnsRainbond內部dns服務,與管理節點DNS服務共同對當前節點的應用提供DNS解析
kubeletKubernetes 計算負載節點元件
docker應用容器引擎
calico叢集SDN服務,為應用提供網路支援
nodeRainbond節點控制器,提供服務守護、自動運維、日誌收集、服務發現等服務。
閘道器節點docker應用容器引擎
calico叢集SDN服務,為應用提供網路支援
rbd-dnsRainbond內部dns服務,可作為叢集dns服務使用
rbd-gateway

1.3 節點規劃

一個完整的Rainbond叢集中必須包含manage、gateway、compute角色的節點和暫不作為Rainbond安裝支援的儲存節點,當然三種屬性可以在同一個節點上組成單節點的Rainbond叢集。安裝Rainbond之前需要根據企業自身需求合理的規劃計算資源,這裡主要是指物理機或虛擬機器節點。

從上文中列舉的主要Rainbond服務元件來綜合分析,管理節點的合理規劃是關鍵。

Rainbond的主要資料儲存元件是:

  • Etcd

    根據Etcd叢集組建特性,其必須部署為1,3,5奇數節點。

  • Mysql

    Mysql資料庫的部署模式主要有主從、多主等模式,

  • Rbd-monitor(Prometheus)

    Prometheus具有單機自治特性,因此每一個Rbd-monitor節點都是獨立的資料採集和儲存,基本上可以認為多節點資料是一致的。

Rainbond安裝指令碼對Etcd,Rbd-monitor做了較好的自動安裝支援,對於Mysql資料庫,我們更建議使用者獨立安裝Mysql資料庫並提供給Rainbond安裝指令碼。管理節點其他的元件基本上可以認為是無狀態的,或有狀態的元件都自身實現了良好的工作節點選舉。對部署節點數無關鍵要求。因此我們推薦的管理節點數量是3個及以上。

閘道器節點處理流量入口,每一個Rainbond節點目前都獨立提供了所有訪問策略的支援,因此上層可以採用4層負載均衡策略或VIP策略,因此我們推薦的節點數量是2個及以上。

計算節點提供計算負載,節點越多,叢集計算容量越大,因此計算節點的規劃取決於叢集需要執行的應用數量,隨時可以增加或下線節點。因此我們推薦的節點數量是2個及以上。

2. 安裝原理說明

Rainbond-Ansible 專案是Rainbond子專案之一,提供Rainbond叢集便捷的安裝支援,採用Ansible自動化部署框架實現。其具有安裝簡單、工作原理簡單、模組化、生態完善等特點。

早期我們採用了SaltStack 實現,其工作模式複雜,不透明的節點通訊機制。Rainbond安裝過程受限於SaltStack的穩定性,因此我們從5.0版本後對安裝指令碼進行了重構。

2.1 安裝指令碼結構

.
├── callback_plugins                  # 任務失敗時列印幫助訊息回撥外掛
│   └── help.py                       # 回撥外掛示例
├── hack                              # 部署本地資原始檔目錄
│   ├── chinaos                       # 作業系統的安裝包源
│   │   ├── CentOS-Base.repo          # CentOS的源
│   │   ├── centos-release            # CentOS的全域性配置
│   │   ├── sources.list              # Ubuntu的源
│   │   ├── ubuntu-lsb-release        # Ubuntu的版本配置
│   │   └── ubuntu-release            # Ubuntu的全域性配置
│   ├── docker                        # Docker部署資原始檔目錄
│   │   ├── get-docker.sh             # 快速部署Docker指令碼
│   │   └── rainspray.list            # 快速部署Docker的Ubuntu源
│   ├── files                         # 好雨工具包
│   │   ├── bin                       # grctl的二進位制檔案
│   │   ├── health                    # 健康監測指令碼
│   │   ├── ssh                       # ssh配置指令碼
│   │   └── ssl                       # 好雨加密證書
│   ├── manifests                     # 應用配置檔案
│   │   ├── dashboard                 # 儀表盤-配置
│   │   ├── efk                       # efk-配置
│   │   ├── es-cluster                # es叢集-配置
│   │   ├── heapster                  # heapster-配置
│   │   ├── ingress                   # ingress-配置
│   │   ├── jenkins                   # jenkins-配置
│   │   ├── metrics-server            # metrics-配置
│   │   ├── prometheus                # prometheus-配置
│   │   └── storage                   # storage-配置
│   ├── step                          # Ansible安裝步驟劇本
│   │   ├── 00.prepare.yml            # 安裝前檢測
│   │   ├── 01.docker.yml             # docker-配置
│   │   ├── 02.image.yml              # image-配置
│   │   ├── 10.etcd.yml               # etcd-配置
│   │   ├── 11.kube-master.yml        # kube-master-配置
│   │   ├── 12.kube-worker.yml        # kube-worker-配置
│   │   ├── 13.network.yml            # network-配置
│   │   ├── 20.db.yml                 # database-配置
│   │   ├── 21.storage.yml            # storage-配置
│   │   ├── 22.lb.yml                 # lb-配置
│   │   ├── 23.node.yml               # node-配置
│   │   └── 90.setup.yml              # setup-配置
│   ├── thirdparty                    # 第三方服務對接
│   │   ├── addmaster.yml             # 增加master-role
│   │   ├── addnode.yml               # 增加node-role
│   │   └── setup.yaml                # 配置安裝
│   ├── tools                         # 工具包目錄
│   │   ├── get_images.sh             # 拉取docker映象
│   │   ├── update-domain.sh          # 更換域名
│   │   └── yc-ssh-key-copy.sh        # 批量部署伺服器ssh-key
│   ├── upgrade                       # 升級配置目錄
│   │   └── upgrade.yml               # 升級配置檔案
│   ├── vagrant                       # vagrant服務配置目錄
│   │   ├── README.md                 # 說明檔案
│   │   ├── Vagrantfile               # ruby獲取系統資訊
│   │   ├── install.sh                # 安裝檔案
│   │   └── setup.sh                  # 配置檔案
│   └── windows                       # windows節點配置目錄
│       ├── cni                       # 配置檔案目錄
│       ├── scripts                   # 指令碼目錄
│       │   ├── helper.psm1           # 幫助資訊指令碼
│       │   ├── hns.psm1              # hns配置指令碼
│       │   ├── start-flannel.        # 開啟flannel指令碼
│       │   ├── start-kubelet.        # 開始kubelet指令碼
│       │   └── start-node.ps1        # 開始node服務指令碼
│       ├── README.md                 # 說明檔案
│       ├── daemon.json               # 域名配置
│       ├── net-conf.json             # 網路配置
│       └── win.yaml                  # Windows配置
├── inventory                         # Ansible劇本執行主機
│   ├── hosts.all                     # 主機模版
│   └── hosts.master                  # 主機模版
├── log                               # 日誌檔案目錄
├── offline                           # 離線安裝配置檔案目錄
│   ├── image                         # 離線包製作指令碼目錄
│   │   ├── download.sh               # 快取docker離線映象指令碼
│   │   ├── image.txt                 # rainbond映象列表
│   │   ├── load.sh                   # 載入離線快取映象包指令碼
│   │   └── offimage.sh               # 壓縮理想快取映象包指令碼
│   └── pkgs                          # 離線包儲存目錄
│       ├── Dockerfile.centos         # 構建離線CentOS映象
│       ├── Makefile                  # 構建離線CentOS映象
│       ├── README.md                 # 說明文件
│       ├── download.centos           # 建立本地CentOS源
│       └── rbd.repo                  # Centos源
├── scripts                           # 部署指令碼存放目錄
│   ├── installer                     # 安裝指令碼目錄
│   │   ├── default.sh                # 預設網路配置指令碼
│   │   ├── functions.sh              # 安裝的示例庫指令碼
│   │   └── global.sh.example         # 全域性變數示例指令碼
│   ├── op                            # 網路配置目錄
│   │   ├── README.md                 # 說明檔案
│   │   ├── lb.sh                     # 配置lb服務指令碼
│   │   └── network.sh                # 配置網路指令碼
│   ├── upgrade                       # 升級指令碼目錄
│   │   └── upgrade.sh                # 升級指令碼檔案
│   ├── yaml                          # 網路配置劇本目錄
│   │   ├── init_network.yaml         # 初始化網路劇本
│   │   └── reset_network.yaml        # 重置網路劇本
│   └── node.sh                       # 用於管理節點指令碼
├── test                              # 測試劇本語法指令碼目錄
│   ├── hosts.ini                     # 主機配置資訊
│   ├── k8s-master.role.1.j2          # k8s-master配置資訊
│   ├── k8s-worker.role.1.j2          # k8s-worker配置資訊
│   ├── kubelet.sh.1.j2               # kubelet配置資訊
│   └── test.sh                       # 檢測Ansible劇本語法指令碼
├── roles                             # Ansible部署規則配置檔案目錄
│   ├── bootstrap                     # bootstrap服務規則配置
│   ├── db                            # database服務規則配置
│   ├── docker                        # docker服務規則配置
│   ├── etcd                          # etcd服務規則配置
│   ├── k8s                           # k8s服務規則配置
│   ├── lb                            # lb服務規則配置
│   ├── monitor                       # monitor服務規則配置
│   ├── network_plugin                # network_plugin服務規則配置
│   ├── node                          # node服務規則配置
│   ├── prepare                       # prepare服務規則配置
│   ├── rainvar                       # rainvar服務規則配置
│   ├── storage                       # storage服務規則配置
│   ├── thirdparty                    # thirdparty服務規則配置
│   └── upgrade                       # upgrade服務規則配置
├── docs                              # 說明文件資料夾
├── CHANGELOG.md                      # 版本迭代說明
├── Dockerfile                        # 建立rainbond-ansible的Ubuntu映象源
├── LICENSE                           # 開發協議
├── Makefile                          # 語法檢測配置
├── README.md                         # 說明檔案
├── addmaster.yml                     # 增加master節點劇本
├── addnode.yml                       # 增加node節點劇本
├── ansible.cfg                       # Ansible程式配置優化
├── lb.yml                            # 增加lb節點劇本
├── setup.sh                          # 主安裝指令碼入口
├── setup.yml                         # Ansible本地安裝劇本
├── upgrade.yml                       # Ansible升級劇本
└── version                           # 安裝包版本

2.2 ansible-playbook各角色劇本

角色劇本說明
managerainvar初始化私有資料中心的一些預設配置(資料庫、埠、安裝路徑、安裝版本等)
bootstrap對本節點的核心進行優化(tcp_tw_recycle、core.somaxconn、syncookies、file-max等)
prepare對本節點安裝條件進行檢查(系統版本、CPU、記憶體、磁碟、核心等)
storage/nfs/client以nfs方式掛載本節點的儲存卷
storage/nas以nas方式掛載本節點的儲存卷
storage/gfs以gfs方式掛載本節點的儲存卷
docker/install在本節點上安裝Docker服務
k8s/manage在本節點上安裝k8s服務的管理端
etcd/manage在本節點上安裝etcd服務的管理端
gateway在本節點上安裝負載均衡元件
monitor在本節點上安裝監控元件
network_plugin/calico切換docker網路為calico
network_plugin/flannel切換docker網路為flannel
node/exm安裝基礎依賴包(python-pip、ansible)
node/core在本節點安裝node核心元件
gatewayrainvar初始化私有資料中心的一些預設配置(資料庫、埠、安裝路徑、安裝版本等)
bootstrap對本節點的核心進行優化(tcp_tw_recycle、core.somaxconn、syncookies、file-max等)
prepare對本節點安裝條件進行檢查(系統版本、CPU、記憶體、磁碟、核心等)
storage/nfs/client以nfs方式掛載本節點的儲存卷
storage/nas以nas方式掛載本節點的儲存卷
storage/gfs以gfs方式掛載本節點的儲存卷
docker/install在本節點上安裝Docker服務
network_plugin/calico切換docker網路為calico
network_plugin/flannel切換docker網路為flannel
gateway在本節點上安裝負載均衡元件
node/exlb在本節點安裝node負載元件
computerainvar初始化私有資料中心的一些預設配置(資料庫、埠、安裝路徑、安裝版本等)
bootstrap對本節點的核心進行優化(tcp_tw_recycle、core.somaxconn、syncookies、file-max等)
prepare對本節點安裝條件進行檢查(系統版本、CPU、記憶體、磁碟、核心等)
storage/nfs/client以nfs方式掛載本節點的儲存卷
storage/nas以nas方式掛載本節點的儲存卷
storage/gfs以gfs方式掛載本節點的儲存卷
docker/install在本節點上安裝Docker服務
k8s/compute在本節點上安裝k8s服務的客戶端
etcd/compute在本節點上安裝etcd服務的客戶端
network_plugin/calico切換docker網路為calico
network_plugin/flannel切換docker網路為flannel
gateway在本節點上安裝負載均衡元件
node/core在本節點安裝node核心元件

2.3 安裝指令碼部署流程

2.3.1 叢集初始化

叢集初始化包括三個重要步驟,安裝指令碼獲取、安裝環境構建和第一個節點的安裝。

./grctl init 各種引數
  • 安裝指令碼獲取

    grctl init 命令從github倉庫獲取指定版本的ansible程式碼,如果離線安裝沒有此步驟。

  • 安裝環境構建

    grctl init 命令根據使用者指定的引數和預設值生成ansible global.sh 全域性配置檔案。

    • 配置檔案: /opt/rainbond/rainbond-ansible/scripts/installer/global.sh
    • 主要配置:
      • INSTALL_TYPE # 安裝型別(離線/聯網)
      • DEPLOY_TYPE # 節點型別
      • DOMAIN # 域名
      • VERSION # 版本
      • STORAGE # 儲存型別
      • STORAGE_ARGS # 掛載引數
      • NETWORK_TYPE # 網路型別
      • ROLE # 第一個節點角色(預設manage、gateway、compute)

    這裡的引數主要是指定Rainbond叢集在儲存、網路、安裝模式等關鍵引數。

  • 第一個節點安裝

    單一節點的安裝根據傳入role角色屬性,傳遞屬性給主安裝指令碼setup.sh

    主安裝指令碼在進行本地節點系統優化之後呼叫ansible-playbook使用setup.yml劇本進行第一個節點部署

    劇本主要根據master主機組的role進行配置裝機(系統優化、元件部署)

2.3.2 compute、gateway節點擴容安裝

  1. 傳入需要安裝的role角色屬性(compute,gateway),傳遞給主安裝指令碼setup.sh
  2. 主安裝指令碼在進行遠端節點系統優化之後呼叫ansible-playbook使用角色對應的劇本進行部署
    • manage 角色屬性呼叫 addmaster.yml
    • compute 角色屬性呼叫 addnode.yml
    • gateway 角色屬性呼叫 gateway.yml
  3. 劇本主要根據主機組所使用的role進行配置裝機(系統優化、元件部署)

3. 叢集安裝流程

graph LR
    subgraph 初始化過程
        id1(grctl)==>id2(setup.sh)
        id2(setup.sh)==>id3(ansible-playbook)
    end

3.1 grctl init 初始化過程

grctl init 命令首先獲取安裝包,然後根據傳入的引數以鍵值對的方式轉換為shell指令碼變數,以全域性變數的方式對後續操作進行引數的傳遞,後續步驟讀取全域性變數,達到安裝過程中對可變因素的掌控。

在未來的版本中,grctl命令列進一步控制ansible的主機列表,準確的為ansible提供叢集主機序列。

3.2 shell 初始化過程

grctl 命令完成引數配置後呼叫安裝指令碼/opt/rainbond/rainbond-ansible/setup.sh 進行第一個節點初始化。

指令碼首先會對作業系統進行優化。這裡是安裝過程使用網路的主要點,線上安裝模式下,作業系統的更新和配置,安裝包的下載通過網路進行。離線安裝模式下使用事先準備的本地安裝源對作業系統進行基礎環境安裝,然後使用事先下載好的安裝包。後續的節點安裝過程將不再使用網路。

最後會調取ansible-play使用setup.yml劇本進行初始化安裝。

3.3 ansible-playbook 初始化過程

ansible-playbook使用setup.yml進行初始化,首先會找到當前主機所在的主機組,之後根據role的設定到不同的元件資料夾中根據pre_task -> roles -> tasks -> post-tasks 的順序依次執行資料夾下面的main.yml達到元件安裝的作用

3.4 其他角色節點擴容安裝

  1. grctl node add --host <計算節點主機名> --iip <計算節點內網ip> --root-pass <計算節點root密碼> --role gateway,compute指定新增節點的主機名、內網地址、連線密碼、角色 , grctl命令列首先將節點資料加入叢集元資料。通過grctl node list命令即可查詢節點狀態。

  2. 使用grctl node install host-uuid命令安裝節點,grclt從API中讀取相應的主機資訊傳遞給node.sh指令碼進行節點的安裝。

  3. node.shscript/node.sh中,主要獲取以下幾個引數:

    • node_role # 新增節點的角色
    • node_hostname # 新增節點的主機名
    • node_ip # 新增節點的網路地址
    • login_type # 新增節點的登陸方式
    • login_key # 新增節點的連線密碼
    • node_uuid # 新增節點的uuid
  4. node.sh指令碼首先會判斷node_role中傳遞的角色屬性,迴圈角色屬性判斷inventory/hosts中相應的主機組中是否存在對應的主機,沒有根據不同的角色屬性加入到相應的主機組中進行裝機,在維護inventory/hosts之後會進行連線檢測通過login_type、login_key、node_uuid、node_ip、node_hostname引數進行主機連線檢測、通過之後會呼叫ansible-playbook -i inventory/hosts -e $node_role role.yml進行不同角色的裝機:

    • -i 指定裝機主機
    • -e 將grctl傳遞給setup.shnode_role引數傳遞給ansible-playbook生成對應的node元件角色配置檔案
    • role.yml 不同角色對應不同的yml配置檔案
      • addmaster.yml # master role
      • addnode.yml # compute role
      • gateway.yml # gateway role

    在5.1.6版本中hosts檔案的維護將移交到grctl命令列工具中,根據叢集節點狀態實時生成。

4. 節點服務運維

Rainbond叢集安裝的所有元件有兩種執行方式: node元件和docker元件是直接二進位制執行,其他元件全部採用容器化執行。兩種執行方式都是直接採用systemd守護程序進行守護。因此能夠安裝Rainbond的作業系統必須具有systemd。

在叢集自動化運維的需求下,我們需要對節點(特別是計算節點)進行實時全面的健康檢查,以確認節點是否可用。這個工作由node服務進行,它會根據/opt/rainbond/conf目錄下配置對當前節點的配置檢查項進行監控,如果出現故障彙報到叢集管理端,如果是計算節點則會由叢集管理端決策是否暫時禁止排程或下線該節點。

graph LR
    subgraph 服務運維流程
        id1(systemd)==>id2(node.service)
        id2(node.service)==>id3(健康檢測)
        id2(node.service)==>id4(守護程序)
    end

4.1 systemd的配置檔案生成(node.service)

  • 在叢集初始化完成之後ansible會在/etc/systemd/system/node.service目錄下生成node.service的配置檔案,node服務在systemd中以守護程序方式啟動執行。

  • node服務啟動後將讀取/opt/rainbond/conf目錄下的配置生成每一個需要啟動服務的systemd配置檔案並呼叫systemctl工具啟動服務。

    配置檔案分為需求啟動服務和只是健康檢查專案,比如以下配置:

    - name: rbd-mq
      endpoints:
      - name: MQ_ENDPOINTS
        protocol: http
        port: 6301
      health:
        name: rbd-mq
        model: http
        address: 127.0.0.1:6301/health
        max_errors_num: 3
        time_interval: 5
      after:
        - docker
      type: simple
      pre_start: docker rm rbd-mq
      start: >-
        docker run --name rbd-mq
        --network host
        -i goodrain.me/rbd-mq:V5.1-dev
        --log-level=debug --etcd-endpoints=${ETCD_ENDPOINTS} --hostIP=192.168.195.1
      stop: docker stop rbd-mq
      restart_policy: always
      restart_sec: 10
    

    該檔案配置了rbd-mq服務的啟動方式、健康檢查方式和服務註冊資訊。

4.2 node元件的健康檢測機制

每一個安裝服務的健康檢查配置見文件: 詳細配置

若某項檢查專案標識為不健康狀態,當前節點將被標識為不健康狀態。

  • 對於不健康的節點Rainbond提供兩級自動處理機制:
    • 檢測到異常的服務一段時間依然未恢復(取決於配置的時間段)將自動重啟服務。
    • 若計算節點被標註為不健康,節點控制器將會自動將其禁止應用排程直到節點恢復健康。
  • 配置檔案: /opt/rainbond/conf/health.yaml
    • name # 需要檢測的服務名稱
    • model # 以什麼方式檢測(tcp/http/cmd)
    • address # 被檢測服務的地址
    • max_errors_num # 最大錯誤次數
    • time_interval # 每次檢測次數
  • 目前檢測方式有3種
    • cmd # 使用指令碼或者命令列
    • tcp # 使用ip:port模式
    • http # 使用http協議檢測

4.3 叢集故障查詢和處理

根據整個叢集節點的健康檢查機制,使用者在管理節點通過grctl cluster 命令即可查詢整個叢集的故障點,或使用監控報警系統及時發現叢集故障。當叢集某個節點出現問題時首先定位故障的服務,並檢視其執行日誌處理故障。如果有未完善的健康檢測專案,使用者可以通過上訴節點健康檢測配置方式自定義檢測專案。

5. 常見安裝問題解決思路

  • 埠被佔用無法安裝

Rainbond是一個完整的PaaS平臺解決方案,所以強烈建議使用乾淨的物理機或虛擬機器安裝Rainbond。 Rainbond閘道器節點直接承接應用訪問流量,因此其預設佔用80\443等關鍵埠。另外Rainbond安裝Ansible預設使用的SSH埠是22,嚴格運維時需要設定。

  • 資料庫安裝初始化失敗

Rainbond 5.1.5及之前版本預設安裝的mysql資料庫版本是mariadb 10,其所需的記憶體資源是較大的。如果你的機器資源有限很大可能導致安裝失敗。後續的版本中我們將預設安裝的資料庫版本升級到Mysql 5.7系列。

  • 高可用安裝怎麼做

本文主要描述了整個安裝原理,因此你閱讀本文應該可以總結出Rainbond高可用安裝的關鍵,我們近期也會再次更新高可用安裝操作指南。

  • Rainbond能否安裝在Mac或Windows系統

Rainbond計算節點可以支援Windows作業系統來執行Windows應用,目前Windows的支援是企業版功能之一,因此開源安裝指令碼暫不支援Windows 節點的快速安裝。Mac作業系統不適合安裝Rainbond。開發者可以將部分元件執行在Mac下執行開發。

  • 遇到其他安裝問題怎麼辦?

移步 https://github.com/goodrain/rainbond-ansible/issues 查詢或提交你的問題。

Rain