-
Notifications
You must be signed in to change notification settings - Fork 118
In-cluster client mode #456
base: branch-2.2-kubernetes
Are you sure you want to change the base?
In-cluster client mode #456
Conversation
Can we add basic usage example in the docs: https://github.com/apache-spark-on-k8s/spark/blob/branch-2.2-kubernetes/docs/running-on-kubernetes.md? |
@tristanz added docs. Did I leave anything out? |
The docs are actually published separately out of https://github.com/apache-spark-on-k8s/userdocs @mccheah, I think creating a separate |
docs/running-on-kubernetes.md
Outdated
This will open up a shell into the specified driver pod from which you can run client mode applications. In order to appropriately configure | ||
these in-cluster applications, be sure to set the following configuration value, which essentially tells the cluster manager to refer back to the current driver pod as the driver for any applications you submit: | ||
|
||
spark.kubernetes.driver.pod.name=$HOSTNAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason spark.kubernetes.driver.pod.name
is being set prior to the spark-submit
command, instead of using --conf ...
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way I worded it makes it seem like that is the case. I was going for making the user aware that spark.kubernetes.driver.pod.name
must be set for all client mode applications executed in-cluster.
Perhaps appending to "be sure to set the following configuration value" with "in all client-mode applications you run, either through --conf
or spark-defaults.conf
" would help clarify the point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is also in the example command, so maybe it is clear enough. Possibly add in "as in the following example spark-submit command"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amended for clarity.
@@ -344,8 +344,8 @@ object SparkSubmit extends CommandLineUtils { | |||
|
|||
// The following modes are not supported or applicable | |||
(clusterManager, deployMode) match { | |||
case (KUBERNETES, CLIENT) => | |||
printErrorAndExit("Client mode is currently not supported for Kubernetes.") | |||
case (KUBERNETES, CLIENT) if !inK8sCluster() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's probably best to limit this to in-cluster now, but generally speaking I don't see why we shouldn't allow client mode with the appropriate networking caveats. Some network configurations allow the pod network to be fully routable (e.g. Calico).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how difficult it would be to detect pod network connectivity upon application submission. The logic would probably have to abstracted out to the cluster manager or one of the initial steps, and in that case, we would have to throw a validation error much later on in the process as we would need to allow client mode through unobstructed in SparkSubmit. Definitely feasible though.
docs/running-on-kubernetes.md
Outdated
@@ -240,6 +240,38 @@ the command may then look like the following: | |||
|
|||
## Advanced | |||
|
|||
### Running in-cluster client mode applications | |||
|
|||
While Spark on Kubernetes does not officially support client mode applications, such as the PySpark shell, there is a workaround that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest rephrasing as:
"While Spark on Kubernetes does not support client mode applications, such as the PySpark shell, when launched from outside Kubernetes, Spark on Kubernetes does support client mode applications launched within the cluster. This in-cluster..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
docs/running-on-kubernetes.md
Outdated
|
||
This will open up a shell into the specified driver pod from which you can run client mode applications. In order to appropriately configure | ||
these in-cluster applications, be sure to set the following configuration value for all applications, as in the following `spark-submit` example, | ||
which essentially tells the cluster manager to refer back to the current driver pod as the driver for any applications you submit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit; drop "essentially"
docs/running-on-kubernetes.md
Outdated
With that set, you should be able to run the following example from within the pod: | ||
|
||
bin/spark-submit \ | ||
--class org.apache.spark.examples.SparkPi \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is --deploy-mode client
? If this is the default, I would add it explicitly in case the user has a global spark.conf that defaults to cluster mode instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is default, but I'll include it to be more explicit.
docs/running-on-kubernetes.md
Outdated
these in-cluster applications, be sure to set the following configuration value for all applications, as in the following `spark-submit` example, | ||
which essentially tells the cluster manager to refer back to the current driver pod as the driver for any applications you submit: | ||
|
||
spark.kubernetes.driver.pod.name=$HOSTNAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this parameter special compared to other spark kubernetes options you use below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifying the driver pod's name tells the cluster manager that the application being submitted with this configuration should refer back to a pod in the k8s cluster with the provided name. See the validation and reference logic in the cluster scheduler backend class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By setting the driver pod name to the hostname of the user's pod, every application uses the user's pod as it's driver in client mode, thus meaning that a new driver pod isn't allocated.
docs/running-on-kubernetes.md
Outdated
|
||
While Spark on Kubernetes does not officially support client mode applications, such as the PySpark shell, there is a workaround that | ||
allows for execution of these apps from within an existing Kubernetes cluster. This _in-cluster_ client mode bypasses some of the networking and | ||
dependency issues inherent to running a client from outside of a cluster while allowing much of the same functionality in terms of interactive use cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interactive use cases like PySpark shell and Jupyter.
this needs to be rebased |
Method of finding if we're running in the cluster LGTM; there may be a better method using the fabric8 client, but that isn't the best fit for spark submit code. |
An alternate method of finding if we're running in-cluster is to check for |
@foxish adding the extra check and including a lightweight An alternative might be to determine whether the pod network is routable (as per @tristanz's earlier comment). I'm not sure how best to do this, but it might cover more cases than the other option. |
|
||
In order to run in client mode, use `kubectl attach` to attach to an existing driver pod on the cluster, or the following to run a new driver: | ||
|
||
kubectl run -it --image=<driver image> --restart=Never -- /bin/bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use kubespark/spark-driver:latest
as image in the commandline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did not want to specify, but I see the benefit.
--conf spark.kubernetes.driver.docker.image=kubespark/spark-driver:latest \ | ||
--conf spark.kubernetes.executor.docker.image=kubespark/spark-executor:latest \ | ||
--conf spark.dynamicAllocation.enabled=true \ | ||
--conf spark.shuffle.service.enabled=true \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel perhaps we should leave
`--conf spark.dynamicAllocation.enabled=true \
-
--conf spark.shuffle.service.enabled=true
` out of the example? these are not default or required when running k8s right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
--conf spark.dynamicAllocation.enabled=true \ | ||
--conf spark.shuffle.service.enabled=true \ | ||
--conf spark.kubernetes.shuffle.namespace=default \ | ||
--conf spark.kubernetes.shuffle.labels="app=spark-shuffle-service,spark-version=2.1.0" \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2.2.0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, will change later today.
I'm not sure how to use this correctly, or it doesn't seem to be working for me. I tried submitting jobs from the jupyter webUI, as well as
And it repeats that last message indefinitely. I also do not see any executor pods created, through either method. The same command works in |
It's been a while since I've looked at this, but can you try enabling dynamic allocation when running in client mode? |
Oh! I tried just now with everything in the |
It all works now, and is glorious. Two important things I needed (my bad), I was using a The second thing is that I thought/hoped those environment variables would be expanded, in a k8s Deployment |
I should note that dynamic allocation + shuffle service also works in this setup. The only restriction I can see so far, is I had to locate all the resources within the same namespace as jupyter (I would have preferred the spark executors to be in a separate isolated namespace). |
@paulreimer Let me know if there are any documentation changes that can be made to make the process of starting up with "client" mode easier! As for scheduling executors in a separate namespace than the Jupyter notebook, I am not sure how to go about resolving that. If you make any progress on this front, let me know! |
--master k8s://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT \ | ||
--kubernetes-namespace default \ | ||
--conf spark.app.name=spark-pi \ | ||
--conf spark.kubernetes.driver.pod.name=$HOSTNAME \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if pods can infer their own name via the environment?
Was thinking about this a little more and it makes sense. But there's some more food for thought here. I recently learned that TCP connections in Spark are only initiated from executor->executor and executor->driver. There is no connection from driver->executor. That means that theoretically, if the driver is reachable over a hostname or IP address from the executor pods, then client mode can be supported as long as the driver is not behind a firewall. So it may not be the case that the driver has to be running from within the context of a pod. In this case, the idea of client mode would be a lot closer to the notion of client mode in the other cluster managers. We should adjust this accordingly so that:
And then we should try to test this on real clusters where the driver is just on a host that is in the cluster, not in a container. |
I also think that the cluster-mode scheduler backend has some conventions that are specific to cluster mode, such as the init container that downloads dependencies that the submission client sent over. A |
I have tried this branch running in I have run interactive spark-shell, spark-submit and Zeppelin with 3 executors (will open a PR to document the needed configuration for Zeppelin). It works globally well for what I have done (basic dataframe processing in scala, not additional external dependencies). Three questions (mainly around pod naming):
This leads to "AlreadyExists" exception if you want to instantiate multiple Spark REPL on the same K8S cluster.
It is just a warning, and the REPL is then correctly created - Just mentioning as the behavior is different in cluster mode. |
PS: We can forget my 3rd feedback (Error_outline Internal Server Error (500) - If I click via the page showing the list of pods, I receive the logs - If I click on the logs icon via the detail page, I receive the 500 error - I guess this is a dashboard or k8s issue. |
@erikerlandson @mccheah I have recompiled to relax the condition on the k8s client mode and made a few (unsuccessful) tests with submitting to a remote spark-k8s from my laptop (out of pod, spark-submit + spark-shell). I receive:
I have teste with/without |
Are we still continuing work on this? I still have some concerns - see #456 (comment) |
@mccheah I agree with your comment, it is a very good point. @sahilprasad can speak for himself, but I know he's back in school so may not have bandwidth to pick this up. Is anybody else interested? |
I know @echarles was working on related things - would you be willing to take over? |
Yep, this is listed in my todos, just after #463 |
@sahilprasad enjoy and success for your exams (if any? I think it is the period for these). The good news is that I have been able to quickly hack the code to have a running spark k8s client The bad news is that the executor initiates connection to the driver. When I run from my laptop to a cluster in the cloud, the executor pod is created but fails directly trying to connect to my local IP address (
I have then created a separated local cluster on my laptop and, from my IDE, I can run SparkPI and Spark Shell in k8s client mode. I haven't tried on a separated machine, but if the cluster nodes have ip:port reachability, I guess it will be ok (need to test...) As first iteration, I'd like to open a PR based on your feedbacks on those questions:
|
What you mean by passing a null driver pod?
Yes, the client can be pointed to There's one thing that's essential for in-cluster client mode, the driver headless service for the executors to connect to the driver. In current implementation, the submission client is solely responsible for creating the service. We need to think about who's in charge of creating the service in in-cluster client mode. |
In case of client mode, there is no driver pod indeed, so yes, I simply skip the current logic ( I will default to For the |
The In the in-cluster client mode as described in this PR, the headless service won't be created because the submission client is not invoked. This is problematic and should be addressed. |
I see now your point. I guess when gave have 2 different step-paths: the current one for |
Don't think we should have to do anything special here, it's up to the JVM that runs the Spark application as to how to expose it, but we can't enforce anything here AFAIK.
I'm worried that introducing too many of these - edit: "these" being properties that are present or null based on cluster/client mode - will make it opaque as to what properties are specific to cluster mode and which properties are specific to client mode. Having the separate
Are you referring specifically to how we should configure the Kubernetes client? If so, I think we should just configure everything using Spark properties. The naming of these properties is tricky, because cluster mode namespaces properties between Edit: We should also support loading from on-disk, and it's similar to
Kubernetes clusters can support OAuth tokens for identity verification and authorization. We need to allow users to pass in either a token file or token text itself. The latter should be redacted somehow so that we don't print it in e.g.
We should support loading configuration from both the Spark config and the on-disk kube configs. This is similar to how Spark allows passing Hadoop properties via the files in
Why does this have to change? It should contain the app id and the executor id. |
To take a step back here, I don't think we should be creating a headless service at all for client mode. The idea behind client mode is that there's the inherent assumption that the driver is available over some hostname that is routable from all of the executors. @echarles - to your point that this code is unusable from a laptop - if we think about it, if one had an equivalent YARN cluster with the same firewall settings and your laptop was sitting in the same place with its own firewalls, then I'd expect our very same client mode applications that don't work with that YARN cluster to also not work in Kubernetes. For client mode our measure of success should be: If I colocated my Kubernetes kubelets with existing YARN nodemanagers that I've been using for my client mode YARN applications, would I also be able to run my client mode Kubernetes applications from the same host where I run my YARN client mode applications? Conversely, if there was a networking setup that would have prohibited us from running in YARN client mode, then we also don't have to be concerned with the analogous scenarios in Kubernetes - such as e.g. the laptop->cluster case. The situation I was concerned about before I found out what I did in #456 (comment) was that the driver would need to reach out to the executors to hand them work. That would create a discrepancy between YARN and Kubernetes, because executors running in YARN usually are running on a fleet of hosts that have a unified firewall setting and are either all exposed at once to the driver or none are. In the latter case, it's expected that the client mode application shouldn't work anyways, but one could deploy their application inside the YARN cluster to get inside the firewall and work from there. In Kubernetes that would have been extremely difficult to do because any given individual pod is not routable from outside the cluster by default. But given our findings, the expectation is now only that the driver needs to be reachable from the pods, which is far more manageable. How that driver would be exposed I would expect to be different case by case, considering a laptop versus deploying onto a server, vs. running the application inside a pod that exposes a headless service, etc. Thus the user that submits the application should have decided a-priori how their driver would be reachable by the executors. It's not immediately obvious that Spark itself has to determine that connectivity mechanism. |
The
+1
I imagine the list of
So this is optional and should be passed via property.
Back to the property list...
It should, but it does not - All executors created in |
Think my idea here is similar to the spirit of #456 (comment) - though I did see we almost posted at the same time =). I don't think Spark itself should need to determine if the application is in-cluster vs. out-of-cluster, but it just says that the driver running in client mode needs to be reachable by the executor pods, and it's up to the user to determine how to resolve that connectivity. |
Yes, I meant this only applies to in-cluster client mode. And I think for in-cluster client mode, users should not need to worry about how to setup the connectivity between the executors and the driver. |
But do we need to distinguish between in-cluster client mode? Again, if we treat client mode as the contract that "the driver needs to be reachable by its hostname", it's left up to the user to determine how to do that. |
Ideally yes. In case of the headless service in the in-cluster client mode, are you suggesting users to manually create that as part of starting the client pod? |
It would be a first for Spark in client mode to determine its own connectivity. I don't think it's strictly necessary for Spark to do this. Users can create the headless service themselves. |
OK, I can buy that. This needs to be documented clearly. |
fyi I made a first step a few days ago but had no time to finalize (you know, end of year...). It works for me in branch-2.2-kubernetes...datalayer-contrib:client-mode-datalayer-2 |
Thanks @echarles. |
I have a working version that covers the 6 scenarios:
This can be viewed on branch-2.2-kubernetes...datalayer-contrib:client-mode-datalayer-2 Note to myself:
Questions to the community:
|
That sounds awesome. Thanks Eric. A brief design doc would help us all get
on the same page wrt the mechanics of it I think. Then we can use the
weekly meeting to discuss that and then you can send a PR against upstream
and start a JIRA. That seems like a good way to proceed here. Open to other
ideas from others though.
…On Jan 12, 2018 1:16 AM, "Eric Charles" ***@***.***> wrote:
I have a working version that covers the 6 scenarios:
1. spark-submit cluster-mode in-cluster
2. spark-submit cluster-mode out-cluster
3. spark-submit client-mode in-cluster
4. spark-submit client-mode out-cluster
5. spark-shell client-mode in-cluster
6. spark-shell client-mode out-cluster
This can be viewed on branch-2.2-kubernetes...
datalayer-contrib:client-mode-datalayer-2
<branch-2.2-kubernetes...datalayer-contrib:client-mode-datalayer-2>
Note to myself:
- Get rid of the hardcoded path for cert and key in case of out-cluster
- Polish format
- Document to ensure understanding of the behavior.
- Unit test
- Integration test
Questions to the community:
1. It works without any change on the Headless driver. I would say
that if it does the job, we don't have to worry about this... This may
sound a bit naive, so I will try to bring light and evidence in the
document I will write.
2. Happy to get feedback on the already developed code.
3. Once I will get something more final, should I open a PR on
apache-spark-on-k8s/spark to we can prepare something or directly engage on
the apache jira, repos and mailing lists?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#456 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA3U55XWoK2qcyVlgGZoLo6jKGnXax-eks5tJyLRgaJpZM4O_t1e>
.
|
@foxish The initial shot of the doc can be read on apache-spark-on-k8s/userdocs#25. It is a bit dense but reviewed topics are IMHO needed. I will update based on your feedback (bring comment the PR or reply here). Thx. |
Just pushed an update to the doc apache-spark-on-k8s/userdocs#25 |
@echarles Thanks for providing the codes and I'm studying it. As far as my team concerned, it's necessary to implement and have a good test on the client mode codes. Our proposal is to make a huge production-level transformation from yarn to k8s which may involve thousands of machines. |
* Author @sahilprasad * Enables spark applications to be submitted in 'in-cluster client' mode.
## Upstream SPARK-XXXXX ticket and PR link (if not applicable, explain) Not filed in upstream, touches code for conda. ## What changes were proposed in this pull request? rLibDir contains a sequence of possible paths for the SparkR package on the executor and is passed on to the R daemon with the SPARKR_RLIBDIR environment variable. This PR filters rLibDir for paths that exist before setting SPARKR_RLIBDIR, retaining existing functionality to preferentially choose a YARN or local SparkR install over conda if both are present. See daemon.R: https://github.com/palantir/spark/blob/master/R/pkg/inst/worker/daemon.R#L23 Fixes apache-spark-on-k8s#456 ## How was this patch tested? Manually testing cherry picked on older version Please review http://spark.apache.org/contributing.html before opening a pull request.
What changes were proposed in this pull request?
As per the discussion on my original PR (#402), this allows client mode if the submission environment is within a Kubernetes cluster. I erroneously stated in the above PR that the application resolves to the submission client at
org.apache.spark.deploy.kubernetes.Client
. However, this is not the case, and submitted applications resolve to the user-submitted class in the case of Java/Scala execution, andorg.apache.spark.deploy.PythonRunner
for Python execution. Since execution involves being inside a Kubernetes driver pod viakubectl run
, I was able to get this to work after setting thespark.kubernetes.driver.pod.name
to theHOSTNAME
environment variable within the pod. Due to this configuration, once theKubernetesClusterSchedulerBackend
class is invoked by the application class, the driver pod that the user submitted the application from is recognized as the driver of the application and execution proceeds as normal, with no extra driver pod being unnecessarily constructed.The in-cluster use case is more of a stopgap for actual client mode support on this project, but is something that allows for client-mode applications like the PySpark and Scala shells, Jupyter, etc. The logic that checks whether the application was submitted from within a Kubernetes cluster just checks if
KUBERNETES_SERVICE_HOST
andKUBERNETES_SERVICE_PORT
are provided.Would definitely appreciate comments or questions, especially around how to better detect in-cluster execution!
How was this patch tested?
Ran provided unit and integration tests. Manual testing done through spark-submit, standalone PySpark scripts, PySpark on Jupyter, and both shells.