<p>I have a node-pool (default-pool) in a GKE cluster with 3 nodes, machine type n1-standard-1. They host 6 pods with a redis cluster in it (3 masters and 3 slaves) and 3 pods with an nodejs example app in it.</p>

<p>I want to upgrade to a bigger machine type (n1-standard-2) with also 3 nodes.</p>

<p>In the <a href="https://cloud.google.com/kubernetes-engine/docs/tutorials/migrating-node-pool" rel="nofollow noreferrer">documentation</a>, google gives an example to upgrade to a different machine type (in a new node pool).</p>

<p>I have tested it while in development, and my node pool was unreachable for a while while executing the following command:</p>

<pre><code>for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do
  kubectl cordon "$node";
done
</code></pre>

<p>In my terminal, I got a message that my connection with the server was lost (I could not execute kubectl commands). After a few minutes, I could reconnect and I got the desired output as shown in the documentation.</p>

<p>The second time, I tried leaving out the cordon command and I skipped to the following command:</p>

<pre><code>for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do
  kubectl drain --force --ignore-daemonsets --delete-local-data --grace-period=10 "$node";
done
</code></pre>

<p>This because if I interprete the kubernetes <a href="https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/" rel="nofollow noreferrer">documentation</a> correctly, the nodes are automatically cordonned when using the drain command. But I got the same result as with the cordon command: I lost connection to the cluster for a few minutes, and I could not reach the nodejs example app that was hosted on the same nodes. After a few minutes, it restored itself.</p>

<p>I found a workaround to upgrade to a new node pool with bigger machine types: I edited the deployment/statefulset yaml files and changed the nodeSelector. Node pools in GKE are tagged with:</p>

<pre><code>cloud.google.com/gke-nodepool=NODE_POOL_NAME
</code></pre>

<p>so I added the correct nodeSelector to the deployment.yaml file:</p>

<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deployment
  labels:
    app: example
spec:
  replicas: 3
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      nodeSelector:
            cloud.google.com/gke-nodepool:  new-default-pool
      containers:
      - name: example
        image: IMAGE
        ports:
        - containerPort: 3000
</code></pre>

<p>This works without downtime, but I'm not sure this is the right way to do in a production environment.</p>

<p>What is wrong with the cordon/drain command, or am I not using them correctly?</p>

<p>Cordoning a node will cause it to be removed from the load balancers backend list, so will a drain. The correct way to do it is to set up anti-affinity rules on the deployment so the pods are not deployed on the same node, or the same region for that matter. That will cause an even distribution of pods throught your node pool.</p>

<p>Then you have to disable autoscaling on the old node pool if you have it enabled, slowly drain 1-2 nodes a time and wait for them to appear on the new node pool, making sure at all times to keep one pod of the deployment alive so it can handle traffic.</p>
