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 docs for opening ports on Kubernetes #2997

Merged
merged 5 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/source/reference/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ Available fields and semantics:
# is used as default if 'networking' is not specified.
networking: portforward

# The mode to use for opening ports on Kubernetes
#
# This must be either: 'ingress' or 'loadbalancer'. If not specified,
# defaults to 'loadbalancer'.
#
romilbhardwaj marked this conversation as resolved.
Show resolved Hide resolved
# loadbalancer: Creates services of type `LoadBalancer` to expose ports.
# See https://skypilot.readthedocs.io/en/latest/reference/kubernetes/kubernetes-setup.html#loadbalancer-service.
# This mode is supported out of the box on most cloud managed Kubernetes
# environments (e.g., GKE, EKS).
#
# ingress: Creates an ingress and a ClusterIP service for each port opened.
# Requires an Nginx ingress controller to be configured on the Kubernetes cluster.
# Refer to https://skypilot.readthedocs.io/en/latest/reference/kubernetes/kubernetes-setup.html#nginx-ingress
# for details on deploying the NGINX ingress controller.
romilbhardwaj marked this conversation as resolved.
Show resolved Hide resolved
ports: loadbalancer

# Additional fields to override the pod fields used by SkyPilot (optional)
#
# Any key:value pairs added here would get added to the pod spec used to
Expand Down
43 changes: 42 additions & 1 deletion docs/source/reference/kubernetes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,47 @@ To use images from private repositories (e.g., Private DockerHub, Amazon ECR, Go
If you use Amazon ECR, your secret credentials may expire every 12 hours. Consider using `k8s-ecr-login-renew <https://github.com/nabsul/k8s-ecr-login-renew>`_ to automatically refresh your secrets.


Opening Ports
-------------

Opening ports on SkyPilot clusters running on Kubernetes is supported through two modes:

1. `LoadBalancer services <https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer>`_ (default)
2. `Nginx IngressController <https://kubernetes.github.io/ingress-nginx/>`_

One of these modes must be supported and configured on your cluster. Refer to the :ref:`setting up ports on Kubernetes guide <kubernetes-ports>` on how to do this.

.. tip::

On Google GKE, Amazon EKS or other cloud-hosted Kubernetes services, the default LoadBalancer services mode is supported out of the box and no additional configuration is needed.

Once your cluster is configured, launch a task which exposes services on a port by adding :code:`ports` to the :code:`resources` section of your task YAML.

.. code-block:: yaml

# task.yaml
resources:
ports: 8888

run: |
python -m http.server 8888

After launching the cluster with :code:`sky launch -c myclus task.yaml`, you can get the URL to access the port using :code:`sky status --endpoints myclus`.

.. code-block:: bash

# List all ports exposed by the cluster
$ sky status --endpoints myclus
8888: 34.173.13.241:8888

# curl a specific port's endpoint
$ curl $(sky status --endpoint 8888 myclus)
...

.. tip::

To learn more about opening ports in SkyPilot tasks, see :ref:`Opening Ports <ports>`.

FAQs
----

Expand All @@ -187,7 +228,7 @@ Kubernetes support is under active development. Some features are in progress an
* Storage mounting - ✅ Available on x86_64 clusters
* Multi-node tasks - ✅ Available
* Custom images - ✅ Available
* Opening ports and exposing services - 🚧 In progress
* Opening ports and exposing services - ✅ Available
* Multiple Kubernetes Clusters - 🚧 In progress


Expand Down
93 changes: 93 additions & 0 deletions docs/source/reference/kubernetes/kubernetes-setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,99 @@ Once the cluster is deployed and you have placed your kubeconfig at ``~/.kube/co
$ sky check


.. _kubernetes-ports:

Setting up Ports on Kubernetes
-------------------------------


.. note::
This is a guide on how to configure an existing Kubernetes cluster (along with the caveats involved) to successfully expose ports and services externally through SkyPilot.

If you are a SkyPilot user and your cluster has already been set up to expose ports,
:ref:`Opening Ports <ports>` explains how to expose services in your task through SkyPilot.

SkyPilot clusters can :ref:`open ports <ports>` to expose services. For SkyPilot
clusters running on Kubernetes, we support either of two modes to expose ports:

* :ref:`LoadBalancer Service <kubernetes-loadbalancer>` (default)
* :ref:`Nginx Ingress <kubernetes-ingress>`


By default, SkyPilot creates a `LoadBalancer Service <https://kubernetes.io/docs/concepts/services-networking/service/>`__ on your Kubernetes cluster to expose the port.

If your cluster does not support LoadBalancer services, SkyPilot can also use `an existing Nginx IngressController <https://kubernetes.github.io/ingress-nginx/>`_ to create an `Ingress <https://kubernetes.io/docs/concepts/services-networking/ingress/>`_ to expose your service.

.. _kubernetes-loadbalancer:

LoadBalancer Service
^^^^^^^^^^^^^^^^^^^^
Michaelvll marked this conversation as resolved.
Show resolved Hide resolved

This mode exposes ports through a Kubernetes `LoadBalancer Service <https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer>`__. This is the default mode used by SkyPilot.


To use this mode, you must have a Kubernetes cluster that supports LoadBalancer Services:

* On Google GKE, Amazon EKS or other cloud-hosted Kubernetes services, this mode is supported out of the box and no additional configuration is needed.
* On bare metal and self-managed Kubernetes clusters, `MetalLB <https://metallb.universe.tf/>`_ can be used to support LoadBalancer Services.

When using this mode, SkyPilot will create a single LoadBalancer Service for all ports that you expose on a cluster.
Each port can be accessed using the LoadBalancer's external IP address and the port number. Use :code:`sky status --endpoints <cluster>` to view the external endpoints for all ports.

.. note::
In cloud based Kubernetes clusters, this will automatically create an external Load Balancer. GKE creates a (`pass-through load balancer <https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer>`__)
and AWS creates a `Network Load Balancer <https://docs.aws.amazon.com/eks/latest/userguide/network-load-balancing.html>`__). These load balancers will be automatically terminated when the cluster is deleted.

.. note::
The default LoadBalancer implementation in EKS selects a random port from the list of opened ports for the
`LoadBalancer's health check <https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-health-checks.html>`_. This can cause issues if the selected port does not have a service running behind it.


For example, if a SkyPilot task exposes 5 ports but only 2 of them have services running behind them, EKS may select a port that does not have a service running behind it and the LoadBalancer will not pass the healthcheck. As a result, the service will not be assigned an external IP address.

To work around this issue, make sure all your ports have services running behind them.

.. note::
LoadBalancer services are not supported on kind clusters created using :code:`sky local up`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just a quick thought, maybe we can do it in the future, but is it possible that we can have sky local up with the nginx ingress setup by default, so the user can directly try the open ports on the cluster that is local up'ed

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good idea! tracking here - #3112 : )



.. _kubernetes-ingress:

Nginx Ingress
^^^^^^^^^^^^^
Michaelvll marked this conversation as resolved.
Show resolved Hide resolved

This mode exposes ports by creating a Kubernetes `Ingress <https://kubernetes.io/docs/concepts/services-networking/ingress/>`_ backed by an existing `Nginx Ingress Controller <https://kubernetes.github.io/ingress-nginx/>`_.

To use this mode:

1. Install the Nginx Ingress Controller on your Kubernetes cluster. Refer to the `documentation <https://kubernetes.github.io/ingress-nginx/deploy/>`_ for installation instructions specific to your environment.
2. Update the :ref:`SkyPilot config <config-yaml>` at :code:`~/.sky/config` to use the ingress mode.

.. code-block:: yaml

kubernetes:
ports: ingress

When using this mode, SkyPilot creates an ingress resource and a ClusterIP service for each port opened. The port can be accessed externally by using the Ingress URL plus a path prefix of the form :code:`/skypilot/{pod_name}/{port}`.

Use :code:`sky status --endpoints <cluster>` to view the full endpoint URLs for all ports.

.. code-block::

$ sky status --endpoints mycluster
8888: http://34.173.152.251/skypilot/test-2ea4/8888

.. note::

When exposing a port under a sub-path such as an ingress, services expecting root path access, (e.g., Jupyter notebooks) may face issues. To resolve this, configure the service to operate under a different base URL. For Jupyter, use `--NotebookApp.base_url <https://jupyter-notebook.readthedocs.io/en/5.7.4/config.html>`_ flag during launch. Alternatively, consider using :ref:`LoadBalancer <kubernetes-loadbalancer>` mode.


.. note::

Currently, SkyPilot does not support opening ports on a Kubernetes cluster using the `Gateway API <https://kubernetes.io/docs/concepts/services-networking/gateway/>`_.
If you are interested in this feature, please `reach out <https//slack.skypilot.co>`_.


.. _kubernetes-observability:

Observability for Administrators
Expand Down
2 changes: 1 addition & 1 deletion sky/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1803,7 +1803,7 @@ def _get_services(service_names: Optional[List[str]],
required=False,
default=None,
type=int,
help=('Get the endpoint URL for the specified port number on the'
help=('Get the endpoint URL for the specified port number on the '
'cluster. This option will override all other options.'))
@click.option('--show-spot-jobs/--no-show-spot-jobs',
default=True,
Expand Down
6 changes: 3 additions & 3 deletions sky/provision/kubernetes/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def _open_ports_using_ingress(
) -> None:
if not network_utils.ingress_controller_exists():
raise Exception(
'Ingress controller not found.'
'Please install ingress controller first.'
'See https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/index.md for more details.' # pylint: disable=line-too-long
'Ingress controller not found. '
'Install Nginx ingress controller first: '
'https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/index.md.' # pylint: disable=line-too-long
)

for port in ports:
Expand Down
Loading