diff --git a/config/localhost.yaml b/config/localhost.yaml new file mode 100644 index 000000000..a5740294e --- /dev/null +++ b/config/localhost.yaml @@ -0,0 +1,54 @@ +# 127.0.0.1.nip.io will not work because when pods try to use it, +# the domain will resolve to the pod itself. +# The deploy.py overwrite the 127.0.0.1 to the user IP address. +binderhub: + config: + BinderHub: + # Use Kubernetes DNS + hub_url: http://jupyterhub.mybinder.127.0.0.1.nip.io + use_registry: false + image_prefix: dummy.io/localhost/binder- + + extraConfig: + # Disable send events to StackDriver on Google Cloud + 01-eventlog: + + registry: + username: "your-username" + # This is unsafe! Only se for local development + password: "your-password" + + ingress: + https: + # This is unsafe! Only se for local development + enabled: false + hosts: + - mybinder.127.0.0.1.nip.io + + jupyterhub: + ingress: + hosts: + - jupyterhub.mybinder.127.0.0.1.nip.io + +static: + ingress: + tls: + # This is unsafe! Only se for local development + enabled: false + hosts: + - static.127.0.0.1.nip.io + +analyticsPublisher: + enabled: false + +prometheus: + enabled: false + +grafana: + enabled: false + +cryptnono: + enabled: false + +cluster-autoscaler: + enabled: false diff --git a/deploy.py b/deploy.py index 48fdb8333..51c10a6ca 100755 --- a/deploy.py +++ b/deploy.py @@ -31,7 +31,7 @@ } # Projects using raw KUBECONFIG files -KUBECONFIG_CLUSTERS = {"ovh2", "hetzner-2i2c", "hetzner-2i2c-bare"} +KUBECONFIG_CLUSTERS = {"localhost", "ovh2", "hetzner-2i2c", "hetzner-2i2c-bare"} # Mapping of config name to cluster name for AWS EKS deployments AWS_DEPLOYMENTS = {"curvenote": "binderhub"} @@ -199,7 +199,14 @@ def get_config_files(release, config_dir="config"): return config_files -def deploy(release, name=None, dry_run=False, diff=False): +def deploy( + release, + name=None, + dry_run=False, + diff=False, + ip_address=None, + additional_helm_args=[], +): """Deploys a federation member to a k8s cluster. Waits for deployments and daemonsets to become Ready @@ -239,6 +246,20 @@ def deploy(release, name=None, dry_run=False, diff=False): for config_file in config_files: helm.extend(["-f", config_file]) + if release == "localhost": + helm.extend( + [ + "--set", + f"binderhub.config.BinderHub.hub_url=http://jupyterhub.mybinder.{ip_address}.nip.io", + "--set", + f"binderhub.ingress.hosts={{mybinder.{ip_address}.nip.io}}", + "--set", + f"binderhub.jupyterhub.ingress.hosts={{jupyterhub.mybinder.{ip_address}.nip.io}}", + "--set", + f"static.ingress.hosts={{static.{ip_address}.nip.io}}", + ].extend(additional_helm_args) + ) + check_call(helm, dry_run) print( BOLD + GREEN + f"SUCCESS: Helm {helm_commands[0]} for {release} completed" + NC, @@ -457,6 +478,10 @@ def main(): action="store_true", help="If the script is running locally, skip auth step", ) + argparser.add_argument( + "--local-ip", + help="IP address of the local machine", + ) argparser.add_argument( "--dry-run", action="store_true", @@ -474,9 +499,20 @@ def main(): default=stages[0], help="Stage to deploy, default all", ) + argparser.add_argument( + "--additional-helm-args", + action="append", + help="Additional argument for Helm. For example, '--additional-helm-args=--set=ingress-nginx.enabled=false' to disable the installation of ingress-nginx.", + ) args = argparser.parse_args() + if args.release == "localhost": + args.local = True + + if args.local_ip is None: + raise ValueError("Cluster localhost requires IP address.") + # if one argument given make cluster == release cluster = args.cluster or args.release @@ -518,6 +554,8 @@ def main(): setup_auth_gcloud(args.release, cluster, args.dry_run) elif cluster in AWS_DEPLOYMENTS: setup_auth_aws(cluster, args.dry_run) + elif cluster == "localhost": + pass else: raise Exception("Cloud cluster not recognised!") @@ -525,10 +563,17 @@ def main(): update_networkbans(cluster, args.dry_run) if args.stage in ("all", "system"): deploy_system_charts(args.release, args.name, args.dry_run, args.diff) - if args.stage in ("all", "certmanager"): + if args.stage in ("all", "certmanager") and cluster != "localhost": setup_certmanager(args.dry_run, args.diff) if args.stage in ("all", "mybinder"): - deploy(args.release, args.name, args.dry_run, args.diff) + deploy( + args.release, + args.name, + args.dry_run, + args.diff, + args.local_ip, + args.additional_helm_args, + ) if __name__ == "__main__": diff --git a/docs/source/getting_started/index.rst b/docs/source/getting_started/index.rst index f61b55744..10f32ec82 100644 --- a/docs/source/getting_started/index.rst +++ b/docs/source/getting_started/index.rst @@ -9,6 +9,7 @@ permissions, as well as contextual information about the mybinder.org deployment .. toctree:: :maxdepth: 3 + local_environment getting_started production_environment terminology diff --git a/docs/source/getting_started/local_environment.md b/docs/source/getting_started/local_environment.md new file mode 100644 index 000000000..d07e09544 --- /dev/null +++ b/docs/source/getting_started/local_environment.md @@ -0,0 +1,65 @@ +# Getting started with local development + +This page contains a starting point for people who want to know more about the BinderHub deployment by playing around with a local development instance. + +## Local Kubernetes + +You will need a local Kubernetes cluster. A few options are + +- [K3s](https://k3s.io/) + - [Kubernetes with Rancher Desktop](https://www.rancher.com/products/rancher-desktop) +- [minikube](https://minikube.sigs.k8s.io/docs/) +- [k3d](https://k3d.io/stable/) +- [kind](https://kind.sigs.k8s.io/) + - [Kubernetes with Docker Desktop](https://docs.docker.com/desktop/features/kubernetes/) + +### Install Docker Desktop + +Install Docker Desktop on [Mac](https://docs.docker.com/desktop/setup/install/mac-install/), [Windows](https://docs.docker.com/desktop/setup/install/windows-install/), or [Linux](https://docs.docker.com/desktop/setup/install/linux/). And [turn on Kubernetes](https://docs.docker.com/desktop/features/kubernetes/#install-and-turn-on-kubernetes). + +## Set up `kubectl` to connect to Kubernetes + +Once you have `kubectl` installed, you can connect it with your local Kubernetes. +To do so, run the following command: + +```bash +kubectl config use-context k8s-context-name +``` + +If using Docker Desktop, `k8s-context-name` is `docker-desktop`. + +You can test this out by running: + +```bash +kubectl get -A pods +``` + +and a list of all running pods should be printed. + +## Deploy mybinder.org to Kubernetes + +Run the following command: + +```bash +source cert-manager.env +``` + +```bash +for d in ./mybinder*/; do + helm dependency update "$d" +done +``` + +```bash +chartpress --skip-build +``` + +`deploy.py` requires your IP address (represented by `xxx.xxx.xxx.xxx` in the next command). + +```bash +python deploy.py localhost --local-ip xxx.xxx.xxx.xxx +``` + +## Access your mybinder.org + +Open http://xxx.xxx.xxx.xxx with your favourite web browser. diff --git a/mybinder/templates/static/ingress.yaml b/mybinder/templates/static/ingress.yaml index fd6126fd8..b7dfba8c4 100644 --- a/mybinder/templates/static/ingress.yaml +++ b/mybinder/templates/static/ingress.yaml @@ -30,9 +30,11 @@ spec: port: number: 80 {{- end }} + {{- if .Values.static.ingress.tls.enabled }} tls: - secretName: {{ .Values.static.ingress.tls.secretName }} hosts: {{- range $host := .Values.static.ingress.hosts }} - {{ $host }} {{- end }} + {{- end }} diff --git a/mybinder/values.yaml b/mybinder/values.yaml index 514428991..6c4cd4122 100644 --- a/mybinder/values.yaml +++ b/mybinder/values.yaml @@ -571,6 +571,7 @@ static: kubernetes.io/ingress.class: nginx kubernetes.io/tls-acme: "true" tls: + enabled: true secretName: kubelego-tls-static # values ref: https://github.com/grafana/helm-charts/blob/main/charts/grafana/values.yaml