Hi @consideRatio, Thank you very much for these replies - everything is working now
Actually, I’ve been Googling around this topic for a few days, and several of your previous posts have helped me enormously, so thanks for those too 
I finally managed to get everything going by following the link in your first post above. I think my current solution may be unnecessarily complicated (the suggestion in your second post seems cleaner), but I think I’ll leave things as they are for now and revisit when I’ve gained more familiarity with Google Cloud and Kubernetes.
Regarding auto-scaling based on CPU usage, I take your point as to why this is difficult. I was pursuing this idea before I discovered the userProfiles
option, which I think will be fine for my use case. Most of my users have low resource requirements, and those wanting more will almost always know about it before they start their session. With my current setup, I can make a range of options available and users can choose. If they decide partway through that they need more power, they can simply log-out and then sign back in on a more powerful machine. I didn’t realise this was possible at first, which is why I was looking for some way of up-scaling to more powerful machines (not just more nodes) as resources become limited.
In case anyone else is having similar issues, below is an extract from my current configuration. However, please note the caveat that the solution from @consideRatio, above, is probably a better/simpler way to achieve the same thing.
Right, I’m off to play with my new Hub 
Setup overview
First, create the cluster and a default
pool with the core
label
gcloud container clusters create my-cluster-name \
--machine-type=n1-standard-4 \
--num-nodes=1 \
--zone=europe-west1-b \
--cluster-version=latest \
--node-labels=hub.jupyter.org/node-purpose=core
Then create the “standard” user
pool, as per the tutorial
gcloud container node-pools create user-pool \
--cluster=my-cluster-name \
--machine-type=n1-standard-4 \
--zone=europe-west1-b \
--num-nodes=0 \
--enable-autoscaling \
--min-nodes=0 \
--max-nodes=10 \
--node-labels=hub.jupyter.org/node-purpose=user \
--node-taints=hub.jupyter.org_dedicated=user:NoSchedule
Next, create a pool of more powerful machines for users needing more CPUs. Note that I’ve included the default JupyterHub label
and taint
, plus a new label
and taint
defining this particular group of users (user-hi-cpu
)
gcloud container node-pools create user-pool-hi-cpu \
--cluster=my-cluster-name \
--machine-type=n1-highcpu-16 \
--zone=europe-west1-b \
--num-nodes=0 \
--enable-autoscaling \
--min-nodes=0 \
--max-nodes=10 \
--node-labels=hub.jupyter.org/node-purpose=user,my-dedicated=user-hi-cpu \
--node-taints=hub.jupyter.org_dedicated=user:NoSchedule,my-dedicated=user-hi-cpu:NoSchedule
Then, in config.yaml
, I have the following
singleuser:
# ... Other singleuser config.
profileList:
- display_name: "Standard (default)"
description: |
<4 CPUs, <12 GB RAM, no GPU. Resources allocated dynamically. Use this unless you have special requirements.
default: True
kubespawner_override:
cpu_limit: 4
cpu_guarantee: 0.05
mem_limit: 15G
mem_guarantee: 512M
start_timeout: 900
- display_name: "High CPU (new node)"
description: |
Access to 16 CPUs, 12 GB RAM, no GPU. For CPU-heavy processing
kubespawner_override:
cpu_limit: 16
cpu_guarantee: 12
mem_limit: 15G
mem_guarantee: 8G
start_timeout: 900
tolerations:
- effect: NoSchedule
key: hub.jupyter.org_dedicated
operator: Equal
value: user
- effect: NoSchedule
key: my-dedicated
operator: Equal
value: user-hi-cpu
Finally, I also have this in config.yaml
to ensure that “user” and and “core” pods get allocated correctly
prePuller:
continuous:
enabled: true
scheduling:
userScheduler:
enabled: true
podPriority:
enabled: true
userPlaceholder:
enabled: true
replicas: 1
userPods:
nodeAffinity:
matchNodePurpose: require
corePods:
nodeAffinity:
matchNodePurpose: require
cull:
enabled: true
timeout: 3600
every: 300