Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Alluxio auto mount feature #5925

Merged
merged 24 commits into from
Jul 26, 2022

Conversation

GaryShen2008
Copy link
Collaborator

@GaryShen2008 GaryShen2008 commented Jun 28, 2022

Mount the cloud bucket to Alluxio when driver converts FileSourceScanExec to GPU plan
The Alluxio master should be the same node as Spark driver node when using this feature
Introduce new configs:
spark.rapids.alluxio.automount.enabled
spark.rapids.alluxio.bucket.regex
spark.rapids.alluxio.cmd

Signed-off-by: Gary Shen gashen@nvidia.com

Close #5872
Close #5890

@GaryShen2008 GaryShen2008 marked this pull request as draft June 28, 2022 06:41
@GaryShen2008 GaryShen2008 force-pushed the alluxio-auto-mount branch 2 times, most recently from eeebe92 to ba58787 Compare June 30, 2022 08:00
@GaryShen2008
Copy link
Collaborator Author

GaryShen2008 commented Jun 30, 2022

This change is to simplify the usage of Alluxio with Spark-Rapids when the Alluxio master node is the same node which runs Spark Driver app. E.g. When you run the notebook on Databricks, the spark driver will be ran on the master node and the Alluxio master is also installed on the master node.
The previous design for Alluxio usage, it requires users to run alluxio mount manually and provides a list of replacement rules like s3://bucket-foo/file -> alluxio://0.1.2.3:19998/bucket-foo/file. It's not easy to use.
This change introduces 3 new configs:
spark.rapids.alluxio.automount.enabled - enable the Alluxio auto mount feature
spark.rapids.alluxio.bucket.regex - the regex to decide which bucket to be mounted to Alluxio. Default value to match all the buckets starting with s3:// or s3a://. (Optional)
spark.rapids.alluxio.cmd - the alluxio command for a customized installation of Alluxio (Optional)

Prerequisites:
Alluxio master node is the same node which runs Spark Driver app. (So, it can call the command "alluxio fs mount" to mount buckets).

For a quick way to use this feature, you just need to set spark.rapids.alluxio.automount.enabled=true (and don't set spark.rapids.alluxio.pathsToReplace).
The workflow looks as below.
Normally, you'll set spark.hadoop.fs.s3a.access.key and spark.hadoop.fs.s3a.secret.key in spark config to read dataset on AWS S3. When you write df = spark.read.parquet("s3a://some-bucket/files"), we'll check the path of "s3a://some-bucket/files" with the spark.rapids.alluxio.bucket.regex. if it matches, we'll try to mount the bucket "some-bucket" to Alluxio's "/some-bucket" by calling a command line "alluxio fs mount --readonly --option s3a.accessKeyId=*** --option s3a.secretKey=*** /some-bucket s3a://some-bucket".
We'll read the ALLUXIO_HOME environment variable to figure out where Alluxio installed and find out the Alluxio's master IP and port from ALLUXIO_HOME/conf/alluxio-site.properties.
We'll find the access key and secret key from spark config.
Finally, we'll replace the path in Spark plans from "s3a://some-bucket/files" to "alluxio://master_ip:port/some-bucket/files" then the spark job will read data through Alluxio to cache the data to local.

@GaryShen2008 GaryShen2008 added the feature request New feature or request label Jun 30, 2022
@GaryShen2008 GaryShen2008 marked this pull request as ready for review June 30, 2022 11:55
@GaryShen2008 GaryShen2008 marked this pull request as draft July 2, 2022 11:45
@GaryShen2008
Copy link
Collaborator Author

Forgot to consider the case when driver context restarted, and the bucket has been mounted, it'll fail when mounting again.
I don't find a parameter to mount a mounted path, it'll return 255. Need to find a way to understand the mounted point.

Here we can still use command line, another option may use Alluxio java class but it'll depend on Alluxio jar.
I'll first try command line since it's simple.

@GaryShen2008
Copy link
Collaborator Author

build

@GaryShen2008
Copy link
Collaborator Author

Forgot to consider the case when driver context restarted, and the bucket has been mounted, it'll fail when mounting again. I don't find a parameter to mount a mounted path, it'll return 255. Need to find a way to understand the mounted point.

Here we can still use command line, another option may use Alluxio java class but it'll depend on Alluxio jar. I'll first try command line since it's simple.

Get mounted point by parsing the output of "alluxio fs mount"

docs/configs.md Outdated
<a name="alluxio.pathsToReplace"></a>spark.rapids.alluxio.pathsToReplace|List of paths to be replaced with corresponding alluxio scheme. Eg, when configureis set to "s3:/foo->alluxio://0.1.2.3:19998/foo,gcs:/bar->alluxio://0.1.2.3:19998/bar", which means: s3:/foo/a.csv will be replaced to alluxio://0.1.2.3:19998/foo/a.csv and gcs:/bar/b.csv will be replaced to alluxio://0.1.2.3:19998/bar/b.csv|None
<a name="alluxio.automount.enabled"></a>spark.rapids.alluxio.automount.enabled|Enable the feature of auto mounting the cloud storage to Alluxio. It requires the Alluxio master is the same node of Spark driver node. When it's true, it requires an environment variable ALLUXIO_HOME be set properly. The Alluxio master's host and port will be read from alluxio.master.hostname and alluxio.master.rpc.port(default: 19998) from ALLUXIO_HOME/conf/alluxio-site.properties, then replace a cloud path which matches spark.rapids.alluxio.bucket.regex like "s3://bar/b.csv" to "alluxio://0.1.2.3:19998/bar/b.csv", and the bucket "s3://bar" will be mounted to "/bar" in Alluxio automatically.|false
<a name="alluxio.bucket.regex"></a>spark.rapids.alluxio.bucket.regex|A regex to decide which bucket should be auto-mounted to Alluxio. E.g. when setting as "^s3://bucket.*", the bucket which starts with "s3://bucket" will be mounted to Alluxio and the path "s3://bucket-foo/a.csv" will be replaced to "alluxio://0.1.2.3:19998/bucket-foo/a.csv". It's only valid when setting spark.rapids.alluxio.automount.enabled=true. The default value matches all the buckets in "s3://" or "s3a://" scheme.|^s3a{0,1}://.*
<a name="alluxio.cmd"></a>spark.rapids.alluxio.cmd|Provide the Alluxio command, which is used to mount or get information. E.g. "su,ubuntu,-c,/opt/alluxio-2.8.0/bin/alluxio", it means: run Process(Seq("su", "ubuntu", "-c", "/opt/alluxio-2.8.0/bin/alluxio fs mount --readonly /bucket-foo s3://bucket-foo")), to mount s3://bucket-foo to /bucket-foo. the delimiter "," is used to convert to Seq[String] when you need to use a special user to run the mount command.|None
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows to define a customized command. We may remove it for a security concern.

@GaryShen2008 GaryShen2008 marked this pull request as ready for review July 6, 2022 05:43
@wbo4958
Copy link
Collaborator

wbo4958 commented Jul 6, 2022

We'll find the access key and secret key from spark config

Will these configurations display on the Spark UI? and if yes, is it ok for that?

docs/configs.md Outdated Show resolved Hide resolved
var alluxio_master: String = null
var buffered_source: Source = null
try {
buffered_source = Source.fromFile(alluxio_home + "/conf/alluxio-site.properties")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, maybe we can use the withResources in Arm to wrap a closable object

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considered once, but seems not worth to create an AutoCloseable class just for using here.

// This function will only read once from ALLUXIO/conf.
private def initAlluxioInfo(conf: RapidsConf): Unit = {
this.synchronized {
alluxio_home = scala.util.Properties.envOrElse("ALLUXIO_HOME", "/opt/alluxio-2.8.0")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any reason why not to put the alluxio_home/alluxio_Cmd into the if (!isInit) block?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just consider for the case that user changed the spark.rapids.alluxio.cmd at runtime.
So, it can be read in and use the new command. Mainly easier for debug case.

alluxioMasterHost = Some(alluxio_master + ":" + alluxio_port)
// load mounted point by call Alluxio mount command.
// We also can get from REST API http://alluxio_master:alluxio_web_port/api/v1/master/info.
val (ret, output) = runAlluxioCmd(" fs mount")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw some exceptions will be thrown when failed to detect alluxio configuration or something else. But here if the "fs mount" failed to run, do we need to throw the exception also?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I want to get the mounted path from alluxio command, but if failed to get, I regard it as no mounted path, and the exception will be threw when mounting a new path later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its weird to have space in front of "fs"... have the runAlluxioCmd do it if needed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alluxio command seq is like ("su", "ubuntu", "-c", "/opt/alluxio-2.8.0/bin/alluxio"),
The string of " fs mount" is supposed to append to the last item in the seq to generate like ("su", "ubuntu", "-c", "/opt/alluxio-2.8.0/bin/alluxio fs mount").
The original command should be su ubuntu -c "/opt/alluxio-2.8.0/bin/alluxio fs mount".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of tests done in local workstation,here are my thoughts:

  1. The error is like java.lang.RuntimeException: Mount bucket s3a://mybucket/ to /mybucket failed 1. Here the 1 is the stdout which does not give much debug info. I hope we can print stderr and stdout to show the reason why mount failed.
  2. In my env, there is no need to "su" because the Alluxio cluster and Spark users are the same.
    So if I tried to set:
spark.conf.set("spark.rapids.alluxio.cmd", "/home/xxx/alluxio-2.8.0/bin/alluxio")

Then it will fail with :

java.io.IOException: Cannot run program "/home/xxx/alluxio-2.8.0/bin/alluxio fs mount --readonly --option s3a.accessKeyId=xxx --option s3a.secretKey=yyy /mybucket s3a://mybucket/": error=2, No such file or directory

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the latest code has fixed above issue.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think runAlluxioCmd should deal with putting a space in between it rather then having caller do it. we can do it later as followup though too

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to add the space in runAlluxioCmd.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GaryShen2008 I just confirmed that now the issue regarding spark.rapids.alluxio.cmd got fixed based on my test on my local workstation. Now it does not need to su

docs/configs.md Show resolved Hide resolved
docs/get-started/getting-started-alluxio.md Show resolved Hide resolved
``` shell
--conf spark.rapids.alluxio.automount.enabled=true
```
If Alluxio is not installed in /opt/alluxio-2.8.0, you should set the environment variable `ALLUXIO_HOME`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It hink we should rephrase this just to state alluxio must be installed and ALLUXIO_HOME must be set to the installation location.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just hope the user doesn't need to set it explicitly when alluxio is installed in /opt/alluxio-2.8.0.
Otherwise, the user must remember to set it when creating the DB cluster.
More user-friendly, I think.
@viadea What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with the current setting.

In the future when the latest alluxio is 2.9 for example, we can update the default value+docs as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this line, since now we only read the alluxio command path from the alluxio.cmd config.
The ALLUXIO_HOME is only used for reading Alluxio configurations.

@wbo4958
Copy link
Collaborator

wbo4958 commented Jul 8, 2022

one question,

val df = spark.read.parquet("s3:/bucket1/xxx")
df.show()


val df2 = spark.read.parquet("s3:/bucket2/xxx")
df2.show()

will this PR auto-mount s3:/bucket1 and s3:/bucket2 ?

@tgravescs
Copy link
Collaborator

is this all working and ready for another review?

@GaryShen2008
Copy link
Collaborator Author

is this all working and ready for another review?

Yes, I think so.

docs/configs.md Show resolved Hide resolved
docs/configs.md Outdated Show resolved Hide resolved
docs/get-started/getting-started-alluxio.md Show resolved Hide resolved
docs/configs.md Outdated Show resolved Hide resolved
docs/configs.md Outdated
@@ -29,7 +29,10 @@ scala> spark.conf.set("spark.rapids.sql.incompatibleOps.enabled", true)

Name | Description | Default Value
-----|-------------|--------------
<a name="alluxio.pathsToReplace"></a>spark.rapids.alluxio.pathsToReplace|List of paths to be replaced with corresponding alluxio scheme. Eg, when configureis set to "s3:/foo->alluxio://0.1.2.3:19998/foo,gcs:/bar->alluxio://0.1.2.3:19998/bar", which means: s3:/foo/a.csv will be replaced to alluxio://0.1.2.3:19998/foo/a.csv and gcs:/bar/b.csv will be replaced to alluxio://0.1.2.3:19998/bar/b.csv|None
<a name="alluxio.automount.enabled"></a>spark.rapids.alluxio.automount.enabled|Enable the feature of auto mounting the cloud storage to Alluxio. It requires the Alluxio master is the same node of Spark driver node. When it's true, it requires an environment variable ALLUXIO_HOME be set properly. The Alluxio master's host and port will be read from alluxio.master.hostname and alluxio.master.rpc.port(default: 19998) from ALLUXIO_HOME/conf/alluxio-site.properties, then replace a cloud path which matches spark.rapids.alluxio.bucket.regex like "s3://bar/b.csv" to "alluxio://0.1.2.3:19998/bar/b.csv", and the bucket "s3://bar" will be mounted to "/bar" in Alluxio automatically.|false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how should one set ALLUXIO_HOME? for instance does it need to be in the spark-env.sh or can be set on command line when launching spark-submit/spark-shell. On yarn cluster mode might need to use spark.yarn.appMasterEnv..... I'm fine with leaving these details about but woudl be nice to put in docs at some point later

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think both can work for ALLUXIO_HOME. We read it in driver side. So, ALLUXIO_HOME should be set in driver environment. On Databricks, it's added into the environment variables under spark config. Let me update the doc for the suggested way to set it.

alluxioMasterHost = Some(alluxio_master + ":" + alluxio_port)
// load mounted point by call Alluxio mount command.
// We also can get from REST API http://alluxio_master:alluxio_web_port/api/v1/master/info.
val (ret, output) = runAlluxioCmd(" fs mount")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think runAlluxioCmd should deal with putting a space in between it rather then having caller do it. we can do it later as followup though too

// And we'll append --option to set access_key and secret_key if existing.
// Suppose the key doesn't exist when using like Databricks's instance profile
private def autoMountBucket(scheme: String, bucket: String,
access_key: Option[String],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation should be 4 spaces

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Check both access key and secret
Update document to refer to auto mount section
Explain more about limitation
Use /bucket in mountedBucket to match fs mount output
Use camel case to name variable
Use URI to parse the fs mount output

Signed-off-by: Gary Shen <gashen@nvidia.com>
Use logDebug
Write new functions to return the replaceFunc
Use URI to parse the scheme and bucket

Signed-off-by: Gary Shen <gashen@nvidia.com>
Support to run the alluxio command without su by Process(String)

Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
Update docs
Add a space in runAlluxioCmd

Signed-off-by: Gary Shen <gashen@nvidia.com>
Signed-off-by: Gary Shen <gashen@nvidia.com>
@GaryShen2008 GaryShen2008 force-pushed the alluxio-auto-mount branch 2 times, most recently from cf06c7a to e0174b1 Compare July 26, 2022 06:36
Fix a bug in scheme replacement

Signed-off-by: Gary Shen <gashen@nvidia.com>
correct indent

Signed-off-by: Gary Shen <gashen@nvidia.com>
@GaryShen2008
Copy link
Collaborator Author

build

// And we'll append --option to set access_key and secret_key if existing.
// Suppose the key doesn't exist when using like Databricks's instance profile
private def autoMountBucket(scheme: String, bucket: String,
access_key: Option[String],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation still off here, shoudl be 4 spaces from left

}

private def genFuncForPathReplacement(replaceMapOption: Option[Map[String, String]]
) : Option[Path => Path] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing off here as well

}

private def genFuncForAutoMountReplacement(conf: RapidsConf, relation: HadoopFsRelation,
alluxioBucketRegex: String) : Option[Path => Path] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s;acing off

@tgravescs tgravescs merged commit 98f2571 into NVIDIA:branch-22.08 Jul 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request
Projects
None yet
4 participants