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

Update Azure deployment guide #840

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
56 changes: 56 additions & 0 deletions docs/setup_guides/advanced/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Makefile for the recommended deploymet.
# Before running, make sure to install the prerequisites and update `secrets.yaml`
# with the secret tokens
.PHONY: group cluster dhub clean

LOCATION?=westeurope
GROUP?=pangeo
CLUSTER?=pangeoCluster
MAX_USER_NODE_COUNT?=10
MAX_WORKER_NODE_COUNT?=50


group:
az group create --name $(GROUP) --location $(LOCATION)


cluster:
az aks create --resource-group $(GROUP) --name $(CLUSTER) --generate-ssh-keys \
--node-count=1 \
--nodepool-name core \
--nodepool-labels hub.jupyter.org/node-purpose=core

# Add a node-pool: one for the users and Dask schedulers
az aks nodepool add \
--name users \
--cluster-name $(CLUSTER) \
--resource-group $(GROUP) \
--enable-cluster-autoscaler \
--node-count 1 \
--min-count 0 --max-count $(MAX_USER_NODE_COUNT) \
--node-vm-size Standard_D2s_v3 \
--labels hub.jupyter.org/node-purpose=user

# Add a node-pool for Dask workers.
az aks nodepool add \
--name workers \
--cluster-name $(CLUSTER) \
--resource-group $(GROUP) \
--enable-cluster-autoscaler \
--node-count 1 \
--min-count 0 --max-count $(MAX_WORKER_NODE_COUNT) \
--node-vm-size Standard_D2s_v3 \
--priority Spot \
--eviction-policy Delete \
--spot-max-price -1 \
--labels="k8s.dask.org/dedicated=worker"
az aks get-credentials --name $(CLUSTER) --resource-group $(GROUP)

dhub:
helm upgrade --wait --install --create-namespace \
dask dask/daskhub \
--namespace=dhub \
--values=config.yaml

clean:
az group delete -n $(GROUP)
130 changes: 130 additions & 0 deletions docs/setup_guides/advanced/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# file: config.yaml
# This file should not be stored in version control unless it's encrypted.
# Sensitive values are marked a comment "# Sensitive".
# You should update the following fields:
# - [ ] jupyterhub.proxy.secretToken
# - [ ] jupyterhub.proxy.https.hosts
# - [ ] jupyterhub.proxy.https.letsencrypt.contactEmail
# - [ ] jupyterhub.proxy.service.annotations.service.beta.kubernetes.io.azure-dns-label-name
# - [ ] jupyterhub.hub.config.AzureAdOauthenticator.oauth_callback_url
# - [ ] jupyterhub.hub.config.AzureAdOauthenticator.client_id
# - [ ] jupyterhub.hub.config.AzureAdOauthenticator.client_secret
# - [ ] jupyterhub.hub.config.AzureAdOauthenticator.tenant_id
# - [ ] jupyterhub.hub.services.dask-gateway.apiToken
# - [ ] dask-gateway.gateway.auth.jupyterhub.apiToken

jupyterhub:
proxy:
# output from openssl rand -hex 32
secretToken: "<token-1>" # Sensitive
https:
enabled: true
hosts:
# Update this with your hub's name.
# If using AKS to get a DNS name, the first section of this hostname must match the value of
# service.beta.kubernetes.io/azure-dns-label-name.
- "<hub-name>.<azure-region>.cloudapp.azure.com"
letsencrypt:
contactEmail: "<your-email>"
service:
annotations:
# Update this with your hub's name.
service.beta.kubernetes.io/azure-dns-label-name: "<hub-name>"

hub:
# Disable hub network Policy, so that the dask gateway server API can reach the hub directly
# Not required for dask-gateway>0.9.0
# https://github.com/dask/helm-chart/issues/142
networkPolicy:
enabled: false

services:
dask-gateway:
# output from openssl rand -hex 32. Must match dask-gateway.gateway.auth.jupyterhub.apiToken
apiToken: "<token-2>" # Sensitive

config:
# Use our Azure AD App Registration for authentication
# https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
JupyterHub:
authenticator_class: azuread
AzureAdOAuthenticator:
# Update this with the URL for your hub with /oauth_callback.
# If you're using Azure to obtain the DNS name from AKS, this must match the
oauth_callback_url: "https://<hub-name>.<hub-region>.cloudapp.azure.com/hub/oauth_callback"
# Update these with the values for your Azure AD app registration.
client_id: "<adapp-client-id>"
client_secret: "<adapp-client-secret>" # Sensitive
tenant_id: "<adapp-tenant-id>"

singleuser:
extraEnv:
DASK_GATEWAY__CLUSTER__OPTIONS__IMAGE: '{JUPYTER_IMAGE_SPEC}'
DASK_DISTRIBUTED__DASHBOARD_LINK: '/user/{JUPYTERHUB_USER}/proxy/{port}/status'
DASK_LABEXTENSION__FACTORY__MODULE: 'dask_gateway'
DASK_LABEXTENSION__FACTORY__CLASS: 'GatewayCluster'

dask-gateway:
gateway:
auth:
jupyterhub:
# output from openssl rand -hex 32. Must match jupyterhub.services.dask-gateway.apiToken
apiToken: "<token-2>" # Sensitive

backend:
worker:
# Ensure workers are scheduled on the worker pool
extraPodConfig:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "k8s.dask.org/dedicated"
operator: "In"
values:
- "worker"

tolerations:
# allow workers to be scheduled on the worker pool, which has preemptible nodes.
- key: "k8s.dask.org/dedicated"
operator: "Equal"
value: "worker"
effect: "NoSchedule"
- key: "k8s.dask.org_dedicated"
operator: "Equal"
value: "worker"
effect: "NoSchedule"
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"

extraConfig:
01-optionHandler: |
# Configure options to
# 1. Have the default worker image match the singleuser image
# 2. Place bounds on worker CPU and Memory requests
# 3. Accept a mapping of environment variables to pass to workers.
from dask_gateway_server.options import Options, Float, String, Mapping
def cluster_options(user):
def option_handler(options):
if ":" not in options.image:
raise ValueError("When specifying an image you must also provide a tag")

return {
"worker_cores": 0.88 * min(options.worker_cores / 2, 1),
"worker_cores_limit": options.worker_cores,
"worker_memory": "%fG" % (0.95 * options.worker_memory),
"worker_memory_limit": "%fG" % options.worker_memory,
"image": options.image,
"environment": options.environment,
}
return Options(
Float("worker_cores", 1, min=1, max=16, label="Worker Cores"),
Float("worker_memory", 2, min=1, max=32, label="Worker Memory (GiB)"),
String("image", default="pangeo/pangeo-notebook:latest", label="Image"),
Mapping("environment", {}, label="Environment Variables"),
handler=option_handler,
)
c.Backend.cluster_options = cluster_options
Empty file.
Loading