<p>I am attempting to complete the following tutorial for deploying Wordpress on GKE:
<a href="https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk" rel="nofollow noreferrer">https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk</a></p>
<p>I have used terraform for provisioning the gcp resources,  instead of gcp as the tutorial recommends. Here is the deployment that is resulting in a CrashLoopBackOff state.</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: 127.0.0.1:3306
          # These secrets are required to start the pod.
          - name: WORDPRESS_DB_USER
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: username
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: cloudsql-db-credentials
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
        # Change archtek-wordpress:us-west1:archtek-wordpress-postgres-instance here to include your GCP
        # project, the region of your Cloud SQL instance and the name
        # of your Cloud SQL instance. The format is
        # ::
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.11
          command: [&quot;/cloud_sql_proxy&quot;,
                    &quot;-instances=archtek-wordpress:us-west1:archtek-wordpress-mysql-instance=tcp:3306&quot;,
                    # If running on a VPC, the Cloud SQL proxy can connect via Private IP. See:
                    # https://cloud.google.com/sql/docs/mysql/private-ip for more info.
                    # &quot;-ip_address_types=PRIVATE&quot;,
                    &quot;-credential_file=/secrets/cloudsql/key.json&quot;]
          securityContext:
            runAsUser: 2  # non-root user
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: cloudsql-instance-credentials
              mountPath: /secrets/cloudsql
              readOnly: true
          imagePullPolicy: Always
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wordpress-volumeclaim
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
</code></pre>
<p>When I describe the pod, I see the following in the logs:</p>
<pre><code>wordpress-54c68dbf59-5djfx wordpress MySQL Connection Error: (2002) Connection refused
</code></pre>
<p>To rule out the idea that the credentials are invalid, I took the username and password used to create <code>cloudsql-db-credentials</code>, the k8s secret referenced in my deployment yaml, and ran this.</p>
<pre><code>$: gcloud sql connect archtek-wordpress-mysql-instance -u wordpress
</code></pre>
<p>I can connect, no problem. But what I discovered I also cannot do is this:</p>
<pre><code>$: mysql -u wordpress -p'$CLOUD_SQL_PASSWORD' \                                                                    ()
    -h 35.197.7.98       -P 3306 \
    -D archtek-wordpress:us-west1:archtek-wordpress-mysql-instance -v
</code></pre>
<p>which returns:</p>
<pre><code>ERROR 2003 (HY000): Can't connect to MySQL server on '35.197.7.98' (60)
</code></pre>
<p>I know that when using the <code>gcloud</code> client to connect to a cloudsql database, it whitelists for ip for a 5 minute period prior to authentication, which might explain why the <code>mysql</code> client fails to authenticate. However, I'm not sure if this rationale holds up for my deployment in the cluster. Does it also need to be whitelisted for cloudsql to accept auth requests?</p>
<p>Here is the terraform file for provisioning the cloudsql instance:</p>
<pre><code>resource &quot;google_sql_database_instance&quot; &quot;postgres&quot; {
  name             = &quot;archtek-wordpress-mysql-instance&quot;
  database_version = &quot;MYSQL_5_7&quot;
  settings {
    tier              = &quot;db-f1-micro&quot;
    availability_type = &quot;ZONAL&quot;
  }
}
</code></pre>

<p>The error you encountered when trying to connect outside from <code>GKE</code> cluster:</p>
<blockquote>
<p>ERROR 2003 (HY000): Can't connect to MySQL server on '35.197.7.98' (60)</p>
</blockquote>
<p>It's because the site (IP) you are connecting from is not authorized to do that. Using:</p>
<ul>
<li><code>$ gcloud sql connect ...</code></li>
</ul>
<p>is allowing to connect to a <code>SQL</code> instance for a 5 minute period.</p>
<p>You don't need to authorize network you are connecting from when using <code>CloudSQL proxy</code> within <code>GKE</code>.</p>
<p>You can see the authorization part in <code>GCP -&gt; SQL -&gt; Instance -&gt; Connections</code>.</p>
<p>Also, you can see the logs of your pods (<code>wordpress</code> and <code>cloudsql-proxy</code>) to determine which pod is causing problems (other than <code>$ kubectl describe</code>):</p>
<ul>
<li><code>$ kubectl logs POD_NAME -c wordpress</code></li>
</ul>
<hr />
<p>More reference on <code>CloudSQL</code> and <code>sql-proxy</code>:</p>
<ul>
<li><p><em><a href="https://cloud.google.com/sql/docs/mysql/diagnose-issues" rel="nofollow noreferrer">Cloud.google.com: SQL: MySQL: Diagnose issues</a></em></p>
</li>
<li><p><em><a href="https://cloud.google.com/sql/docs/mysql/sql-proxy#troubleshooting" rel="nofollow noreferrer">Cloud.google.com: SQL: MySQL: sql-proxy: Troubleshooting</a></em></p>
</li>
</ul>
<hr />
<p>Assuming that you created your user for <code>CloudSQL</code> instance with <code>.tf</code> file from your github page, there could be a reason why it's failing. The part responsible for creating a user <code>wordpress</code> has wrong host parameter (below example is edited):</p>
<pre><code>resource &quot;google_sql_user&quot; &quot;users&quot; {
  name     = &quot;wordpress&quot;
  instance = google_sql_database_instance.postgres.name
  # host     = &quot;*&quot; &lt;- BAD
  host      = &quot;%&quot; # &lt;- GOOD
  password = random_password.password.result
}
</code></pre>
<p>I couldn't connect to the server with a following parameter: <code>host = &quot;*&quot;</code>. Changing it from <code>&quot;*&quot;</code> to <code>&quot;%&quot;</code> solved my issue.</p>
<hr />
<p>I've managed to create <code>.tf</code> files which are similar to the parts of the official <code>GKE</code> guide:</p>
<ul>
<li><em><a href="https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk" rel="nofollow noreferrer">Cloud.google.com: Kubernetes Engine: Tutorials: Persistent disk </a></em></li>
</ul>
<p>Guide to connect Terraform to the <code>GCP</code> project:</p>
<ul>
<li><em><a href="https://www.terraform.io/docs/providers/google/guides/getting_started.html" rel="nofollow noreferrer">Terraform.io: Google: Guides: Getting started</a></em></li>
</ul>
<p>Files used:</p>
<ul>
<li><code>main.tf</code></li>
<li><code>vpc.tf</code> - create a <code>VPC</code> (basing on github linked in the comment)</li>
<li><code>gke.tf</code> - create a <code>GKE</code> cluster in new <code>VPC</code></li>
<li><code>mysql.tf</code> - create a <code>CloudSQL</code> instance and a user <code>wordpress</code></li>
<li><code>pvc.tf</code> - create a <code>PVC</code> for <code>Wordpress</code> deployment</li>
<li><code>sa.tf</code> - create a <code>ServiceAccount</code> and bind it with permission required to access <code>CloudSQL</code> instances</li>
<li><code>secret.tf</code> - create a key for above <code>ServiceAccount</code> and create <code>Kubernetes</code> secrets for <code>Wordpress</code> and <code>CloudSQL</code> pods</li>
<li><code>deployment.tf</code> - create a deployment that will run <code>Wordpress</code> and <code>cloudsql-proxy</code></li>
</ul>
<p>I ran below commands each time I added a new file (in above order):</p>
<ul>
<li><code>$ terraform init</code></li>
<li><code>$ terraform apply</code></li>
</ul>
<p><code>main.tf</code>:</p>
<pre><code>provider &quot;google&quot; {
  project = &quot;ENTER-YOUR-PROJECT-ID&quot;
  region  = &quot;europe-west3&quot;
  zone    = &quot;europe-west3-c&quot;
}

variable project {
  type = string
  default = &quot;ENTER-YOUR-PROJECT-ID&quot;
}

variable zone {
  type = string
  default = &quot;europe-west3-c&quot;
}

variable region {
  type = string
  default = &quot;europe-west3&quot;
}
</code></pre>
<p><code>vpc.tf</code>:</p>
<pre><code>resource &quot;google_compute_network&quot; &quot;terraform-network&quot; {
  name                    = &quot;terraform-network&quot;
  auto_create_subnetworks = &quot;false&quot;
}

resource &quot;google_compute_subnetwork&quot; &quot;terraform-subnet&quot; {
  name          = &quot;terraform-subnet&quot;
  region        = var.region
  network       = google_compute_network.terraform-network.name
  ip_cidr_range = &quot;10.0.0.0/24&quot;

}
</code></pre>
<p><code>gke.tf</code>:</p>
<pre><code>resource &quot;google_container_cluster&quot; &quot;gke-terraform&quot; {
  name     = &quot;gke-terraform&quot;
  location = var.zone
  initial_node_count = 1

  network    = google_compute_network.terraform-network.name
  subnetwork = google_compute_subnetwork.terraform-subnet.name
}
</code></pre>
<p>I also ran:</p>
<ul>
<li><code>$ gcloud container clusters get-credentials gke-terraform --zone=europe-west3-c</code></li>
</ul>
<p><code>mysql.tf</code></p>
<pre><code>resource &quot;google_sql_database_instance&quot; &quot;cloudsql&quot; {
  name             = &quot;cloudsql-terraform&quot;
  database_version = &quot;MYSQL_5_7&quot;
  settings {
    tier              = &quot;db-f1-micro&quot;
    availability_type = &quot;ZONAL&quot;
  }
}

data &quot;google_sql_database_instance&quot; &quot;cloudsql&quot; {
    name = &quot;cloudsql-terraform&quot;
}

resource &quot;random_password&quot; &quot;wordpress-cloudsql-password&quot; {
  length           = 18
  special          = true
  override_special = &quot;_%@&quot;
}


resource &quot;local_file&quot; &quot;password-file&quot; {
  content  = random_password.wordpress-cloudsql-password.result
  filename = &quot;./password-file&quot;
}

resource &quot;google_sql_user&quot; &quot;cloudsql-wordpress-user&quot; {
  name     = &quot;wordpress&quot;
  instance = google_sql_database_instance.cloudsql.name
  host     = &quot;%&quot;
  password = random_password.wordpress-cloudsql-password.result
}
</code></pre>
<p><code>pvc.tf</code>:</p>
<pre><code>resource &quot;google_compute_disk&quot; &quot;terraform-pd&quot; {
  name  = &quot;terraform-disk&quot;
  type  = &quot;pd-standard&quot;
  zone  = &quot;europe-west3-c&quot;
}

resource &quot;kubernetes_persistent_volume&quot; &quot;terraform-pv&quot; {

  metadata {
    name = &quot;wordpress-pv&quot;
  }
  spec {
    capacity = {
      storage = &quot;10Gi&quot;
    }
    storage_class_name = &quot;standard&quot;
    access_modes = [&quot;ReadWriteOnce&quot;]
    persistent_volume_source {
      gce_persistent_disk {
        pd_name = google_compute_disk.terraform-pd.name
      }
    }
  }
}

resource &quot;kubernetes_persistent_volume_claim&quot; &quot;terraform-pvc&quot; {
  metadata {
    name = &quot;wordpress-pvc&quot;
  }
  spec {
    access_modes = [&quot;ReadWriteOnce&quot;]
    storage_class_name = &quot;standard&quot;
    resources {
      requests = {
        storage = &quot;10Gi&quot;
      }
    }
    volume_name = kubernetes_persistent_volume.terraform-pv.metadata.0.name
  }
}
</code></pre>
<p><code>sa.tf</code>:</p>
<pre><code>resource &quot;google_service_account&quot; &quot;cloudsql-proxy-terraform&quot; {
  account_id   = &quot;cloudsql-proxy-terraform&quot;
  display_name = &quot;cloudsql-proxy-terraform&quot;
}

data &quot;google_service_account&quot; &quot;cloudsql-proxy-terraform&quot; {
  account_id = &quot;cloudsql-proxy-terraform&quot;
}

resource &quot;google_project_iam_binding&quot; &quot;cloudsql-proxy-binding&quot; {
  project = var.project
  role    = &quot;roles/cloudsql.client&quot;

  members = [
    &quot;serviceAccount:${google_service_account.cloudsql-proxy-terraform.email}&quot;,
  ]
}
</code></pre>
<p><code>secret.tf</code>:</p>
<pre><code>resource &quot;google_service_account_key&quot; &quot;cloudsql-proxy-key&quot; {
  service_account_id = google_service_account.cloudsql-proxy-terraform.name
}

resource &quot;kubernetes_secret&quot; &quot;cloudsql-instance-credentials-terraform&quot; {
  metadata {
    name = &quot;cloudsql-instance-credentials-terraform&quot;
  }
  data = {
    &quot;key.json&quot; = base64decode(google_service_account_key.cloudsql-proxy-key.private_key)
  }
}

resource &quot;kubernetes_secret&quot; &quot;cloudsql-db-credentials-terraform&quot; {
  metadata {
    name = &quot;cloudsql-db-credentials-terraform&quot;
  }
  data = {
    &quot;username&quot; = &quot;wordpress&quot;
    &quot;password&quot; = random_password.wordpress-cloudsql-password.result
  }
}
</code></pre>
<p><code>deployment.tf</code>:</p>
<pre><code>resource &quot;kubernetes_deployment&quot; &quot;wordpress-deployment&quot; {
  metadata {
    name = &quot;wordpress-deployment&quot;
    labels = {
      app = &quot;wordpress&quot;
    }
  }

  spec {
    
    replicas = 1

    selector {
      match_labels = {
        app = &quot;wordpress&quot;
      }
    }

    template {
      metadata {
        labels = {
          app = &quot;wordpress&quot;
        }
      }

      spec {

          container {

            image = &quot;wordpress&quot;
            name  = &quot;wordpress&quot;

            env {
                  name = &quot;WORDPRESS_DB_HOST&quot;
                  value = &quot;127.0.0.1:3306&quot;
            }

            env {
                  name = &quot;WORDPRESS_DB_USER&quot;
                  value_from {
                      secret_key_ref {
                          name = kubernetes_secret.cloudsql-db-credentials-terraform.metadata.0.name
                          key = &quot;username&quot;
                      }  
                  }
            }

            env {
                  name = &quot;WORDPRESS_DB_PASSWORD&quot;
                  value_from  {
                      secret_key_ref  {
                          name = kubernetes_secret.cloudsql-db-credentials-terraform.metadata.0.name
                          key = &quot;password&quot;
                      }  
                  }
            }

            port {
              name           = &quot;http&quot;
              container_port = 80
              protocol       = &quot;TCP&quot;
            }

            volume_mount {
              mount_path = &quot;/var/www/html&quot;
              name       = &quot;wordpress-persistent-storage&quot;
            }
            
            

          }
          
          container { 
            image = &quot;gcr.io/cloudsql-docker/gce-proxy:1.11&quot;
            name  = &quot;cloudsql-proxy&quot;

            command = [&quot;/cloud_sql_proxy&quot;, 
            &quot;-instances=${google_sql_database_instance.cloudsql.connection_name}=tcp:3306&quot;,
            &quot;-credential_file=/secrets/cloudsql/key.json&quot;]
            
            security_context {
                run_as_user = 2 
                allow_privilege_escalation = &quot;false&quot;
            }

            volume_mount {
                mount_path = &quot;/secrets/cloudsql&quot;
                name       = &quot;cloudsql-instance-credentials-terraform&quot;
                read_only  = &quot;true&quot;
            }



          }

        volume {
            name = &quot;wordpress-persistent-storage&quot;
            persistent_volume_claim {
              claim_name = &quot;wordpress-pvc&quot;
            } 
        }

        volume {
            name = &quot;cloudsql-instance-credentials-terraform&quot;
            secret {
                secret_name = &quot;cloudsql-instance-credentials-terraform&quot;
            }
        }
        
        }
      }
}
}
</code></pre>
<p>After checking if the resources created correctly ( <code>$ kubectl logs POD_NAME -c CONTAINER_NAME</code>) you can expose your <code>Wordpress</code> with:</p>
<ul>
<li><code>$ kubectl expose deployment wordpress-deployment --type=LoadBalancer --port=80</code></li>
</ul>
