# 在 Mesos 上運行 Spark
* [運行原理](#運行原理)
* [安裝 Mesos](#安裝-mesos)
* [從源碼安裝](#從源碼安裝)
* [第三方軟件包](#第三方軟件包)
* [驗證](#驗證)
* [連接 Spark 到 Mesos](#連接-spark-到-mesos)
* [上傳 Spark 包](#上傳-spark-包)
* [使用 Mesos Master URL](#使用-mesos-master-url)
* [Client Mode(客戶端模式)](#client-mode客戶端模式)
* [Cluster mode(集群模式)](#cluster-mode集群模式)
* [Mesos 運行模式](#mesos-運行模式)
* [Coarse-Grained(粗粒度)](#coarse-grained粗粒度)
* [Fine-Grained (deprecated)(細粒度,不推薦)](#fine-grained-deprecated細粒度不推薦)
* [Mesos Docker 支持](#mesos-docker-支持)
* [集成 Hadoop 運行](#集成-hadoop-運行)
* [使用 Mesos 動態分配資源](#使用-mesos-動態分配資源)
* [配置](#配置)
* [Spark 屬性](#spark-屬性)
* [故障排查和調試](#故障排查和調試)
Spark 可以運行在 [Apache Mesos](http://mesos.apache.org/) 管理的硬件集群上。
使用 Mesos 部署 Spark 的優點包括:
* Spark 與其他的 [frameworks(框架)](https://mesos.apache.org/documentation/latest/mesos-frameworks/) 之間的 dynamic partitioning(動態分區)
* 在 Spark 的多個實例之間的 scalable partitioning(可擴展分區)
# 運行原理
在一個 standalone 集群部署中,下圖中的集群的 manager 是一個 Spark master 實例。當使用 Mesos 時,Mesos master 會將 Spark master 替換掉,成為集群 manager。

現在當 driver 創建一個作業并開始執行調度任務時,Mesos 會決定什么機器處理什么任務。因為 Mesos 調度這些短期任務時會將其他的框架考慮在內,多個框架可以共存在同一個集群上,而不需要求助于一個 static partitioning of resources(資源的靜態分區)。
要開始,請按照以下步驟安裝 Mesos 并通過 Mesos 部署 Spark 作業。
# 安裝 Mesos
Spark 2.2.0 專門為 Mesos 1.0.0 或更新的版本并且不需要 Mesos 的任何特殊補丁。
如果您已經有一個 Mesos 集群正在運行著,您可以跳過這個 Mesos 安裝的步驟。
否則,安裝 Mesos for Spark 與安裝 Mesos for 其他框架是沒有區別的。您可以通過源碼或者 prebuilt packages(預構建軟件安裝包)來安裝 Mesos。
## 從源碼安裝
通過源碼安裝 Apache Mesos,請按照以下步驟:
1. 從鏡像網站 [mirror](http://www.apache.org/dyn/closer.lua/mesos/1.0.0/) 下載一個 Mesos 發行版。
2. 按照 Mesos 的 [快速開始](http://mesos.apache.org/gettingstarted) 頁面來編譯和安裝 Mesos。
**注意:** 如果您希望運行 Mesos 并且又不希望將其安裝在您的系統的默認路徑(例如,如果您沒有安裝它的管理權限),將 `--prefix` 選項傳入 `configure` 來告知它安裝在什么地方。例如,將 `--prefix=/home/me/mesos` 傳入。默認情況下,前綴是 `/usr/local`。
## 第三方軟件包
Apache Mesos 只發布了源碼的發行版本,而不是 binary packages(二進制包)。但是其他的第三方項目發布了 binary releases(二進制發行版本),可能對設置 Mesos 有幫助。
其中之一是 Mesosphere。使用 Mesosphere 提供的 binary releases(二進制發行版本)安裝 Mesos:
1. 從 [下載頁面](http://mesosphere.io/downloads/) 下載 Mesos 安裝包
2. 按照他們的說明進行安裝和配置
Mesosphere 安裝文檔建議安裝 ZooKeeper 來處理 Mesos master 故障切換,但是 Mesos 可以在沒有 ZooKeeper 的情況下使用 single master。
## 驗證
要驗證 Mesos 集群是否已經準備好用于 Spark,請導航到 Mesos master 的 webui 界面,端口是: `:5050` 來確認所有預期的機器都在 slaves 選項卡中。
# 連接 Spark 到 Mesos
要使用 Spark 中的 Mesos,您需要一個 Spark 的二進制包放到 Mesos 可以訪問的地方,然后一個 Spark driver 程序配置來連接 Mesos。
或者,您也可以將 Spark 安裝在所有 Mesos slaves 中的相同位置,并且配置 `spark.mesos.executor.home`(默認是 SPARK_HOME)來指向該位置。
## 上傳 Spark 包
當 Mesos 第一次在 Mesos slave 上運行任務的時候,這個 slave 必須有一個 Spark binary package(Spark 二進制包)用于執行 Spark Mesos executor backend(執行器后端)。Spark 軟件包可以在任何 Hadoop 可訪問的 URI 上托管,包括 HTTP 通過 `http://`,[Amazon Simple Storage Service](http://aws.amazon.com/s3) 通過 `s3n://`,或者 HDFS 通過 `hdfs://`。
要使用預編譯的包:
1. 從 Spark 的 [下載頁面](https://spark.apache.org/downloads.html) 下載一個 Spark binary package(Spark 二進制包)
2. 上傳到 hdfs/http/s3
要托管在 HDFS 上,使用 Hadoop fs put 命令:`hadoop fs -put spark-2.2.0.tar.gz /path/to/spark-2.2.0.tar.gz`
或者如果您正在使用著一個自定義編譯的 Spark 版本,您將需要使用 在 Spark 源碼中的 tarball/checkout 的 `dev/make-distribution.sh` 腳本創建一個包。
1. 按照說明 [這里](index.html) 來下載并構建 Spark。
2. 使用 `./dev/make-distribution.sh --tgz` 創建一個 binary package(二進制包)
3. 將歸檔文件上傳到 http/s3/hdfs
## 使用 Mesos Master URL
對于一個 single-master Mesos 集群,Mesos 的 Master URLs 是以 `mesos://host:5050` 的形式表示的,或者對于 使用 ZooKeeper 的 multi-master Mesos 是以 `mesos://zk://host1:2181,host2:2181,host3:2181/mesos` 的形式表示。
## Client Mode(客戶端模式)
在客戶端模式下,Spark Mesos 框架直接在客戶端機器上啟動,并等待 driver 輸出。
driver 需要在 `spark-env.sh` 中進行一些配置才能與 Mesos 進行交互:
1. 在 `spark-env.sh` 中設置一些環境變量:
* `export MESOS_NATIVE_JAVA_LIBRARY=<path to libmesos.so>`. 這個路徑通常是 `<prefix>/lib/libmesos.so` 默認情況下前綴是 `/usr/local`。請參閱上邊的 Mesos 安裝說明。在 Mac OS X 上,這個 library 叫做 `libmesos.dylib` 而不是 `libmesos.so`。
* `export SPARK_EXECUTOR_URI=<URL of spark-2.2.0.tar.gz uploaded above>`。
2. 還需要設置 `spark.executor.uri` 為 `<URL of spark-2.2.0.tar.gz>`。
現在,當針對集群啟動一個 Spark 應用程序時,在創建 `SparkContext` 時傳遞一個 `mesos://` URL 作為 master。例如:
```
val conf = new SparkConf()
.setMaster("mesos://HOST:5050")
.setAppName("My app")
.set("spark.executor.uri", "<path to spark-2.2.0.tar.gz uploaded above>")
val sc = new SparkContext(conf)
```
(您還可以在 [conf/spark-defaults.conf](configuration.html#loading-default-configurations) 文件中使用 [`spark-submit`](submitting-applications.html) 并且配置 `spark.executor.uri` )
運行 shell 的時候,`spark.executor.uri` 參數從 `SPARK_EXECUTOR_URI` 繼承,所以它不需要作為系統屬性冗余地傳入。
```
./bin/spark-shell --master mesos://host:5050
```
## Cluster mode(集群模式)
Spark on Mesos 還支持 cluster mode(集群模式),其中 driver 在集群中啟動并且 client(客戶端)可以在 Mesos Web UI 中找到 driver 的 results。
要使用集群模式,你必須在您的集群中通過 `sbin/start-mesos-dispatcher.sh` 腳本啟動 `MesosClusterDispatcher`,傳入 Mesos master URL(例如:mesos://host:5050)。這將啟動 `MesosClusterDispatcher` 作為在主機上運行的守護程序。
如果您喜歡使用 Marathon 來運行 `MesosClusterDispatcher`,您需要在 foreground(前臺)運行 `MesosClusterDispatcher`(即 `bin/spark-class org.apache.spark.deploy.mesos.MesosClusterDispatcher`)。注意,`MesosClusterDispatcher` 尚不支持 HA 的多個實例。
`MesosClusterDispatcher` 還支持將 recovery state(恢復狀態)寫入 Zookeeper。這將允許 `MesosClusterDispatcher` 能夠在重新啟動時恢復所有提交和運行的 containers(容器)。為了啟用這個恢復模式,您可以在 spark-env 中通過配置 `spark.deploy.recoveryMode` 來設置 SPARK_DAEMON_JAVA_OPTS 和相關的 spark.deploy.zookeeper.* 配置。有關這些配置的更多信息,請參閱配置 [doc](configurations.html#deploy)。
從客戶端,您可以提交一個作業到 Mesos 集群,通過執行 `spark-submit` 并指定 `MesosClusterDispatcher` 的 master URL(例如:mesos://dispatcher:7077)。您可以在 Spark cluster Web UI 查看 driver 的狀態。
例如:
```
./bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master mesos://207.184.161.138:7077 \
--deploy-mode cluster \
--supervise \
--executor-memory 20G \
--total-executor-cores 100 \
http://path/to/examples.jar \
1000
```
請注意,傳入到 spark-submit 的 jars 或者 python 文件應該是 Mesos slaves 可訪問的 URIs,因為 Spark driver 不會自動上傳本地 jars。
# Mesos 運行模式
Spark 可以以兩種模式運行 Mesos: “coarse-grained(粗粒度)”(默認)和 “fine-grained(細粒度)”(不推薦)。
## Coarse-Grained(粗粒度)
在 “coarse-grained(粗粒度)模式下,每個 Spark 執行器都作為單個 Mesos 任務運行。Spark 執行器的大小是根據下面的配置變量確定的:
* Executor memory(執行器內存):`spark.executor.memory`
* Executor cores(執行器核):`spark.executor.cores`
* Number of executors(執行器數量):`spark.cores.max`/`spark.executor.cores`
請參閱 [Spark 配置](configuration.html) 頁面來了解細節和默認值。
當應用程序啟動時,執行器就會高漲,直到達到 `spark.cores.max`。如果您沒有設置 `spark.cores.max`,Spark 應用程序將會保留 Mesos 提供的所有的資源,因此我們當然會敦促您在任何類型的多租戶集群上設置此變量,包括運行多個并發 Spark 應用程序的集群。
調度程序將會在提供的 Mesos 上啟動執行器循環給它,但是沒有 spread guarantees(傳播保證),因為 Mesos 不提供這樣的保證在提供流上。
在這個模式下 spark 執行器將遵守 port(端口)分配如果這些事由用戶提供的。特別是如果用戶在 Spark 配置中定義了 `spark.executor.port` 或者 `spark.blockManager.port`,mesos 調度器將檢查有效端口的可用 offers 包含端口號。如果沒有這樣的 range 可用,它會不啟動任何任務。如果用戶提供的端口號不受限制,臨時端口像往常一樣使用。如果用戶定義了一個端口,這個端口實現意味著 one task per host(每個主機一個任務)。在未來網絡中,isolation 將被支持。
粗粒度模式的好處是開銷要低得多,但是在應用程序的整個持續時間內保留 Mesos 資源的代價。要配置您的作業以動態調整資源需求,請參閱 [動態分配](#dynamic-resource-allocation-with-mesos)。
## Fine-Grained (deprecated)(細粒度,不推薦)
**注意:** Spark 2.0.0 中的細粒度模式已棄用。為了一些優點,請考慮使用 [動態分配](#dynamic-resource-allocation-with-mesos) 有關完整的解釋,請參閱 [SPARK-11857](https://issues.apache.org/jira/browse/SPARK-11857)。
在細粒度模式下,Spark 執行器中的每個 Spark 任務作為單獨的 Mesos 任務運行。這允許 Spark 的多個實例(和其他框架)以非常細的粒度來共享 cores(內核),其中每個應用程序在其上升和下降時獲得更多或更少的核。但是它在啟動每個任務時增加額外的開銷。這種模式可能不適合低延遲要求,如交互式查詢或者提供 web 請求。
請注意,盡管細粒度的 Spark 任務在它們終止時將放棄內核,但是他們不會放棄內存,因為 JVM 不會將內存回饋給操作系統。執行器在空閑時也不會終止。
要以細粒度模式運行,請在您的 [SparkConf](configuration.html#spark-properties) 中設置 `spark.mesos.coarse` 屬性為 false。:
```
conf.set("spark.mesos.coarse", "false")
```
您還可以使用 `spark.mesos.constraints` 在 Mesos 資源提供上設置基于屬性的約束。默認情況下,所有資源 offers 都將被接受。
```
conf.set("spark.mesos.constraints", "os:centos7;us-east-1:false")
```
例如,假設將 `spark.mesos.constraints` 設置為 `os:centos7;us-east-1:false`,然后將檢查資源 offers 以查看它們是否滿足這兩個約束,然后才會被接受以啟動新的執行器。
# Mesos Docker 支持
Spark 可以通過在您的 [SparkConf](configuration.html#spark-properties) 中設置屬性 `spark.mesos.executor.docker.image` 來使用 Mesos Docker 容器。
所使用的 Docker 圖像必須有一個適合的版本的 Spark 已經是圖像的一部分,也可以通過通常的方法讓 Mesos 下載 Spark。
需要 Mesos 的 0.20.1 版本或者更高版本。
請注意,默認情況下,如果 agent(代理程序中)的 Mesos 代理已經存在,則 Mesos agents 將不會 pull 圖像。如果您使用 mutable image tags(可變圖像標簽)可以將 `spark.mesos.executor.docker.forcePullImage` 設置為 `true`,以強制 agent 總是在運行執行器之前拉取 image。Force pulling images(強制拉取圖像)僅在 Mesos 0.22 版本及以上版本中可用。
# 集成 Hadoop 運行
您可以在現有的 Hadoop 集群集成運行 Spark 和 Mesos,只需要在機器上啟動他們作為分開的服務即可。要從 Spark 訪問 Hadoop 數據,需要一個完整的 `hdfs://` URL(通常為 `hdfs://<namenode>:9000/path`),但是您可以在 Hadoop Namenode web UI 上找到正確的 URL。
此外,還可以在 Mesos 上運行 Hadoop MapReduce,以便在兩者之間實現更好的資源隔離和共享。在這種情況下,Mesos 將作為統一的調度程序,將 Core 核心分配給 Hadoop 或 Spark,而不是通過每個節點上的 Linux 調度程序共享資源。請參考 [Hadoop on Mesos](https://github.com/mesos/hadoop)。
# 使用 Mesos 動態分配資源
Mesos 僅支持使用粗粒度模式的動態分配,這可以基于應用程序的統計信息調整執行器的數量。有關一般信息,請參閱 [Dynamic Resource Allocation](job-scheduling.html#dynamic-resource-allocation)。
要使用的外部 Shuffle 服務是 Mesos Shuffle 服務。它在 Shuffle 服務之上提供 shuffle 數據清理功能,因為 Mesos 尚不支持通知另一個框架的終止。要啟動它,在所有從節點上運 `$SPARK_HOME/sbin/start-mesos-shuffle-service.sh`,并將 `spark.shuffle.service.enabled` 設置為`true`。
這也可以通過 Marathon,使用唯一的主機約束和以下命令實現:`bin/spark-class org.apache.spark.deploy.mesos.MesosExternalShuffleService`。
# 配置
有關 Spark 配置的信息,請參閱 [配置頁面](configuration.html)。以下配置特定于 Mesos 上的 Spark。
#### Spark 屬性
| Property Name(屬性名稱)| Default(默認)| Meaning(含義)|
| --- | --- | --- |
| `spark.mesos.coarse` | true | 如果設置為`true`,則以 “粗粒度” 共享模式在 Mesos 集群上運行,其中 Spark 在每臺計算機上獲取一個長期存在的 Mesos 任務。如果設置為`false`,則以 “細粒度” 共享模式在 Mesos 集群上運行,其中每個 Spark 任務創建一個 Mesos 任務。['Mesos Run Modes'](running-on-mesos.html#mesos-run-modes) 中的詳細信息。 |
| `spark.mesos.extra.cores` | `0` | 設置執行程序公布的額外核心數。這不會導致分配更多的內核。它代替意味著執行器將“假裝”它有更多的核心,以便驅動程序將發送更多的任務。使用此來增加并行度。此設置僅用于 Mesos 粗粒度模式。 |
| `spark.mesos.mesosExecutor.cores` | `1.0` |(僅限細粒度模式)給每個 Mesos 執行器的內核數。這不包括用于運行 Spark 任務的核心。換句話說,即使沒有運行 Spark 任務,每個 Mesos 執行器將占用這里配置的內核數。該值可以是浮點數。 |
| `spark.mesos.executor.docker.image` | (none) | 設置 Spark 執行器將運行的 docker 映像的名稱。所選映像必須安裝 Spark,以及兼容版本的 Mesos 庫。Spark 在圖像中的安裝路徑可以通過 `spark.mesos.executor.home` 來指定; 可以使用 `spark.executorEnv.MESOS_NATIVE_JAVA_LIBRARY` 指定 Mesos 庫的安裝路徑。 |
| `spark.mesos.executor.docker.forcePullImage` | false | 強制 Mesos 代理拉取 `spark.mesos.executor.docker.image` 中指定的圖像。默認情況下,Mesos 代理將不會拉取已經緩存的圖像。 |
| `spark.mesos.executor.docker.parameters` | (none) | 在使用 docker 容器化器在 Mesos 上啟動 Spark 執行器時,設置將被傳遞到 `docker run` 命令的自定義參數的列表。此屬性的格式是逗號分隔的鍵/值對列表。例:<br>`key1=val1,key2=val2,key3=val3` |
| `spark.mesos.executor.docker.volumes` | (none) | 設置要裝入到 Docker 鏡像中的卷列表,這是使用 `spark.mesos.executor.docker.image` 設置的。此屬性的格式是以逗號分隔的映射列表,后面的形式傳遞到 `docker run -v`。這是他們采取的形式:<br>`[host_path:]container_path[:ro\|:rw]` |
| `spark.mesos.task.labels` | (none) | 設置 Mesos 標簽以添加到每個任務。標簽是自由格式的鍵值對。鍵值對應以冒號分隔,并用逗號分隔多個。Ex. key:value,key2:value2. |
| `spark.mesos.executor.home` | driver side `SPARK_HOME` | 在 Mesos 中的執行器上設置 Spark 安裝目錄。默認情況下,執行器將只使用驅動程序的 Spark 主目錄,它們可能不可見。請注意,這只有當 Spark 二進制包沒有通過 `spark.executor.uri` 指定時才是有意義的。 |
| `spark.mesos.executor.memoryOverhead` | executor memory * 0.10, with minimum of 384 | 以每個執行程序分配的額外內存量(以 MB 為單位)。默認情況下,開銷將大于 `spark.executor.memory` 的 384 或 10%。如果設置,最終開銷將是此值。 |
| `spark.mesos.uris` | (none) | 當驅動程序或執行程序由 Mesos 啟動時,要下載到沙箱的 URI 的逗號分隔列表。這適用于粗粒度和細粒度模式。 |
| `spark.mesos.principal` | (none) | 設置 Spark 框架將用來與 Mesos 進行身份驗證的主體。 |
| `spark.mesos.secret` | (none) | 設置 Spark 框架將用來與 Mesos 進行身份驗證的機密。 |
| `spark.mesos.role` | `*` | 設置這個 Spark 框架對 Mesos 的作用。角色在 Mesos 中用于預留和資源權重共享。 |
| `spark.mesos.constraints` | (none) | 基于屬性的約束對 mesos 資源提供。默認情況下,所有資源優惠都將被接受。有關屬性的更多信息,請參閱 [Mesos Attributes & Resources](http://mesos.apache.org/documentation/attributes-resources/)<br><li>標量約束與 “小于等于” 語義匹配,即約束中的值必須小于或等于資源提議中的值。<br><li>范圍約束與 “包含” 語義匹配,即約束中的值必須在資源提議的值內。<br><li>集合約束與語義的 “子集” 匹配,即約束中的值必須是資源提供的值的子集。<br><li>文本約束與 “相等” 語義匹配,即約束中的值必須完全等于資源提議的值。<br><li>如果沒有作為約束的一部分存在的值,則將接受具有相應屬性的任何報價(沒有值檢查)。 |
| `spark.mesos.containerizer` | `docker` | 這只影響 docker containers,而且必須是 "docker" 或 "mesos"。Mesos 支持兩種類型 docker 的 containerizer:"docker" containerizer,和首選 "mesos" containerizer。在這里閱讀更多:http://mesos.apache.org/documentation/latest/container-image/ |
| `spark.mesos.driver.webui.url` | `(none)` | 設置 Spark Mesos 驅動程序 Web UI URL 以與框架交互。如果取消設置,它將指向 Spark 的內部 Web UI。 |
| `spark.mesos.driverEnv.[EnvironmentVariableName]` | `(none)` | 這僅影響以群集模式提交的驅動程序。添加由EnvironmentVariableName指定的環境變量驅動程序進程。用戶可以指定多個這些設置多個環境變量。 |
| `spark.mesos.dispatcher.webui.url` | `(none)` | 設置 Spark Mesos 分派器 Web UI URL 以與框架交互。如果取消設置,它將指向 Spark 的內部 Web UI。 |
| `spark.mesos.dispatcher.driverDefault.[PropertyName]` | `(none)` | 設置驅動程序提供的默認屬性通過 dispatcher。例如,spark.mesos.dispatcher.driverProperty.spark.executor.memory=32g 導致在群集模式下提交的所有驅動程序的執行程序運行在 32g 容器中。 |
| `spark.mesos.dispatcher.historyServer.url` | `(none)` | 設置[history server](http://spark.apache.org/docs/latest/monitoring.html#viewing-after-the-fact)。然后,dispatcher 將鏈接每個驅動程序到其條目在歷史服務器中。 |
| `spark.mesos.gpus.max` | `0` | 設置要為此作業獲取的 GPU 資源的最大數量。請注意,當沒有找到 GPU 資源時,執行器仍然會啟動因為這個配置只是一個上限,而不是保證數額。 |
| `spark.mesos.network.name` | `(none)` | 將 containers 附加到給定的命名網絡。如果這個作業是在集群模式下啟動,同時在給定的命令中啟動驅動程序網絡。查看 [Mesos CNI 文檔](http://mesos.apache.org/documentation/latest/cni/)了解更多細節。 |
| `spark.mesos.fetcherCache.enable` | `false` | 如果設置為 `true`,則所有 URI(例如:`spark.executor.uri`,???? `spark.mesos.uris`)將被<a ????HREF = "http://mesos.apache.org/documentation/latest/fetcher/"> Mesos ???? Fetcher Cache</a> |
# 故障排查和調試
在調試中可以看的地方:
* Mesos Master 的端口:5050
* Slaves 應該出現在 Slaves 那一欄
* Spark 應用應該出現在框架那一欄
* 任務應該出現在在一個框架的詳情
* 檢查失敗任務的 sandbox 的 stdout 和 stderr
* Mesos 的日志
* Master 和 Slave 的日志默認在:`/var/log/mesos` 目錄
常見的陷阱:
* Spark assembly 不可達/不可訪問
* Slave 必須可以從你給的 `http://`,`hdfs://` 或者 `s3n://` URL 地址下載的到 Spark 的二進制包
* 防火墻攔截通訊
* 檢查信息是否是連接失敗
* 臨時禁用防火墻來調試,然后戳出適當的漏洞
- Spark 概述
- 編程指南
- 快速入門
- Spark 編程指南
- 構建在 Spark 之上的模塊
- Spark Streaming 編程指南
- Spark SQL, DataFrames and Datasets Guide
- MLlib
- GraphX Programming Guide
- API 文檔
- 部署指南
- 集群模式概述
- Submitting Applications
- 部署模式
- Spark Standalone Mode
- 在 Mesos 上運行 Spark
- Running Spark on YARN
- 其它
- 更多
- Spark 配置
- Monitoring and Instrumentation
- Tuning Spark
- 作業調度
- Spark 安全
- 硬件配置
- Accessing OpenStack Swift from Spark
- 構建 Spark
- 其它
- 外部資源
- Spark RDD(Resilient Distributed Datasets)論文
- 翻譯進度