YGoetschel opened a new pull request, #787:
URL: https://github.com/apache/spark-kubernetes-operator/pull/787

   ### What changes were proposed in this pull request?
   
   This PR adds a second ingress peer to the worker `NetworkPolicy` generated 
for every
   `SparkCluster`, so that driver pods in the same namespace are admitted 
alongside the cluster's
   own pods.
   
   ### Why are the changes needed?
   
   We run per-user Spark Connect sessions as SparkApplications attached to a 
shared standalone
   `SparkCluster`. Most jobs are fine, but every so often one dies like this:
   
   ```
   Job aborted due to stage failure: Task 11 in stage 2.0 failed 4 times, most 
recent failure:
   Lost task 11.3 in stage 2.0 (TID 86) (<executor-ip> executor 3):
   TaskResultLost (result lost from block manager)
   ```
   
   What made this confusing is that the tasks themselves succeed. The History 
Server lists them as
   SUCCESS with TaskResultLost sitting in the Errors column, so the compute 
finished and only the
   result transfer failed. It also does not hit every task: in the run above, 3 
of 25 tasks in the
   stage died this way and the other 22 completed normally.
   
   The driver log explains why only some of them:
   
   ```
   java.io.IOException: Connecting to /<executor-ip>:<ephemeral-port> timed out 
(120000 ms)
        at 
org.apache.spark.network.client.TransportClientFactory.createClient(...)
        at 
org.apache.spark.network.netty.NettyBlockTransferService$$anon$2.createAndStart(...)
        at 
org.apache.spark.network.shuffle.RetryingBlockTransferor.transferAllOutstanding(...)
        at org.apache.spark.storage.BlockManager.fetchRemoteManagedBuffer(...)
        at org.apache.spark.scheduler.TaskResultGetter$$anon$3.run(...)
   ```
   
   The driver cannot reach the executors' block manager. That only matters once 
a task result goes
   over `spark.task.maxDirectResultSize` (1 MB): below it the result rides 
along inline in the RPC,
   above it the executor parks the result in its block manager and hands the 
driver an
   `IndirectTaskResult` to go and fetch. The tasks that stayed under the limit 
never noticed
   anything. The three that went over timed out, retried three times and gave 
up, and the stage
   aborted on the fourth attempt.
   
   The culprit is the worker NetworkPolicy generated in
   `SparkClusterResourceSpec#buildWorkerNetworkPolicy`:
   
   ```yaml
   podSelector:
     matchLabels: {spark-role: worker, spark.operator/spark-cluster-name: 
<cluster>}
   ingress:
   - from:
     - podSelector:
         matchLabels: {spark.operator/spark-cluster-name: <cluster>}
   policyTypes: [Ingress]
   ```
   
   Only pods carrying the cluster label may talk to the workers. But a 
SparkApplication attached to
   the cluster is a separate resource: its driver is labelled `spark-role: 
driver` and
   `spark.operator/spark-app-name: <app>`, never the cluster label. So the 
driver is locked out of
   its own executors.
   
   None of this is visible from the outside. Worker egress is unrestricted and 
no policy selects
   driver pods, so the executor-to-driver direction works perfectly well — 
executors register,
   tasks get dispatched, tasks run. Only the return path for large results is 
broken, so the
   cluster looks healthy right up until a job happens to cross the 1 MB 
threshold.
   
   There is no way around it from the client side either. Executors read
   `spark.task.maxDirectResultSize` from the driver SparkConf when they start, 
so a Connect client
   cannot raise it partway through a session.
   
   Ports are left unrestricted on the new peer because block manager ports are 
ephemeral, and
   pinning `spark.blockManager.port` is not workable when several executors 
share a worker pod.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes. The generated worker `NetworkPolicy` now also admits driver pods in the 
same namespace.
   Since NetworkPolicies are additive this can only widen ingress, so it cannot 
deny traffic that
   was previously allowed. Clusters running on a CNI that does not enforce 
NetworkPolicy are
   unaffected either way.
   
   ### How was this patch tested?
   
   Extended `SparkClusterResourceSpecTest#testWorkerNetworkPolicy` to assert 
both ingress peers.
   
   ```
   ./gradlew :spark-submission-worker:test --tests 
'*SparkClusterResourceSpecTest*'
   ./gradlew :spark-submission-worker:spotlessCheck
   ```
   
   Both pass. We also applied the equivalent policy by hand against operator 
1.0.0, and the job
   that reproduced this now completes.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Opus 5
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to