Skip to content

Instantly share code, notes, and snippets.

@aimrbrto
Forked from andyshinn/GCLOUD-EXAMPLE.md
Created August 20, 2018 19:29
Show Gist options
  • Select an option

  • Save aimrbrto/f1cb1d6265c9cfe763dfe8eeff8ca62f to your computer and use it in GitHub Desktop.

Select an option

Save aimrbrto/f1cb1d6265c9cfe763dfe8eeff8ca62f to your computer and use it in GitHub Desktop.

Revisions

  1. Andy Shinn revised this gist Apr 30, 2017. 15 changed files with 486 additions and 0 deletions.
    57 changes: 57 additions & 0 deletions GCLOUD-EXAMPLE.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    # Postal on Kubernetes in Google Cloud Platform

    ## Requirements

    1. Google Cloud Platform (GCP) account.
    1. A project in the account (we're using `postal-165921` in our example commands).

    ## Infrastructure

    1. Set the defailt zone to use with our `gcloud` commands: `gcloud config set compute/zone us-central1-a`
    1. `gcloud config set container/cluster postal`
    1. Navigate to https://console.cloud.google.com/ and create a new project for Postal (our example one will be called Postal and has an ID of `postal-165921`).
    1. Navigate to the Container Engine section and create a new cluster called `postal`. The command we are using to create is `gcloud container --project "postal-165921" clusters create "postal" --zone "us-central1-a" --machine-type "n1-standard-1" --image-type "COS" --disk-size "100" --scopes "https://www.googleapis.com/auth/compute","https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" --num-nodes "3" --network "default" --enable-cloud-logging --no-enable-cloud-monitoring`
    1. In a moment you should be able to run `gcloud container clusters get-credentials postal --zone us-central1-a --project postal-165921` to set up your local `kubectl` credentials.
    1. Create a MySQL 5.7 instance in the SQL console. Our example is called `postal`.
    1. In IAM and Admin create a service account. Role will be SQL Client and check to Furnish new private key (JSON type). Save the key somewhere locally.
    1. Create the proxy user with `gcloud beta sql users create postal cloudsqlproxy~% --instance=postal --password=p0st4l`
    1. Run `gcloud sql instances describe postal` to get the connectionName. Our example is `postal-165921:us-central1:postal`.
    1. `kubectl create secret generic cloudsql-instance-credentials --from-file=credentials.json=/Users/andy/Documents/Postal-d609301cc404.json`
    1. `kubectl create secret generic cloudsql-db-credentials --from-literal=username=postal --from-literal=password=p0st4l`
    1. Create a `grants.sql` file with the following contents:
    ```
    CREATE DATABASE `postal` CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
    GRANT ALL ON `postal`.* TO `postal`@`cloudsqlproxy~%`;
    GRANT ALL PRIVILEGES ON `postal-%` . * to `postal`@`cloudsqlproxy~%`;
    ```
    1. Create a new storage bucket and upload the `grants.sql` file to it.
    1. Import this SQL file into the new `postal` database.
    1. Enable Google Cloud SQL API at https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview?project=postal-165921&duration=PT1H.
    1. Create two external IP addresses (`mx1` and `mx2`) for later use in SMTP load balancers.

    # Secrets

    1. Generate a SSL certificate using `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=postaldemo/O=postaldemo"`.
    1. `kubectl create secret tls postal-tls --key tls.key --cert tls.crt`

    # Application

    1. Create your configuration map using `kubectl create configmap postal-config --from-file ~/.postal --dry-run -o yaml`. This assumes you already have the files from the `postal initialize-config` command living at `~/.postal`.

    # DNS

    We are using the domain `postaldemo.com` for our demo. We are going to host it in GCP.

    1. Create the zone: `gcloud dns managed-zones create postaldemo --dns-name postaldemo.com --description "Postal demonstration domain"`.
    1. Get the name servers to use at the registrar using `gcloud dns managed-zones describe postaldemo` and update your registrar name server records.
    1. Set static IPs for MX1 and MX2 to the instances in the SMTP server pool: `gcloud compute addresses create mx1 mx2 --addresses $(gcloud compute instances list --filter='name:postal-smtp' --format='value[terminator=","](networkInterfaces[0].accessConfigs[0].natIP)')`
    1. Create MX1: `gcloud dns record-sets transaction add --zone postaldemo --type A --name mx1.postaldemo.com. --ttl 300 $(gcloud compute addresses describe mx1 --format "value(address)")`.
    1. Create MX2: `gcloud dns record-sets transaction add --zone postaldemo --type A --name mx2.postaldemo.com. --ttl 300 $(gcloud compute addresses describe mx2 --format "value(address)")`.
    1. Create SMTP endpoint: `gcloud dns record-sets transaction add --zone postaldemo --type CNAME --name smtp.postaldemo.com. --ttl 300 $(gcloud compute addresses describe mx2 --format "value(address)") $(gcloud compute addresses describe mx1 --format "value(address)")`.

    ## Future

    * Convert all the initial GCP stuff to Terraform.
    * Use https://github.com/kubernetes-incubator/external-dns instead of manual DNS.
    * Figure out if we can use a PersistentVolume in a job for read/write and then read-only in the pods (for assets). Currently using a gcePersistentDisk for this instead.
    * How to add 443 as Ingress controllers frontend ports.
    31 changes: 31 additions & 0 deletions assets-job.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    apiVersion: batch/v1
    kind: Job
    metadata:
    name: postal-assets
    spec:
    template:
    metadata:
    name: postal-assets
    labels:
    name: postal-assets
    spec:
    restartPolicy: Never
    containers:
    - image: andyshinn/postal-app
    name: assets
    command: ["bundle", "exec", "rake", "assets:precompile"]
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    - mountPath: /usr/src/app/public/assets
    name: assets
    readOnly: false
    restartPolicy: Never
    volumes:
    - name: config
    configMap:
    name: postal-config
    - name: assets
    gcePersistentDisk:
    pdName: postal-assets
    fsType: ext4
    29 changes: 29 additions & 0 deletions cron-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: cron
    spec:
    replicas: 1
    strategy:
    type: Recreate
    template:
    metadata:
    labels:
    postal.app: cron
    spec:
    containers:
    - image: andyshinn/postal-app
    name: cron
    command: ["bundle", "exec", "rake", "postal:cron"]
    ports:
    - containerPort: 2525
    resources: {}
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Always
    volumes:
    - name: config
    configMap:
    name: postal-config
    status: {}
    30 changes: 30 additions & 0 deletions fast-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: fast
    spec:
    replicas: 2
    strategy:
    type: Recreate
    template:
    metadata:
    labels:
    postal.app: fast
    spec:
    containers:
    - image: andyshinn/postal-app
    name: fast
    command: ["bundle", "exec", "rake", "postal:fast_server"]
    ports:
    - containerPort: 5010
    protocol: TCP
    - containerPort: 5011
    protocol: TCP
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Always
    volumes:
    - name: config
    configMap:
    name: postal-config
    17 changes: 17 additions & 0 deletions fast-service.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    postal.app: fast
    name: fast
    spec:
    clusterIP: None
    ports:
    - name: headless
    port: 55555
    targetPort: 0
    selector:
    postal.app: fast
    status:
    loadBalancer: {}
    45 changes: 45 additions & 0 deletions initialize-job.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    apiVersion: batch/v1
    kind: Job
    metadata:
    name: postal-initialize
    spec:
    template:
    metadata:
    name: postal-initialize
    labels:
    name: postal-initialize
    spec:
    restartPolicy: Never
    containers:
    - image: gcr.io/cloudsql-docker/gce-proxy:1.09
    name: cloudsql-proxy
    command: ["/cloud_sql_proxy", "--dir=/cloudsql",
    "-instances=postal-165921:us-central1:postal=tcp:3306",
    "-credential_file=/secrets/cloudsql/credentials.json"]
    volumeMounts:
    - name: cloudsql-instance-credentials
    mountPath: /secrets/cloudsql
    readOnly: true
    - name: ssl-certs
    mountPath: /etc/ssl/certs
    - name: cloudsql
    mountPath: /cloudsql
    - image: andyshinn/postal-app
    name: initialize
    command: ["bash", "-c", "sleep 10 && bundle exec rake db:schema:load db:seed"]
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Never
    volumes:
    - name: config
    configMap:
    name: postal-config
    - name: cloudsql-instance-credentials
    secret:
    secretName: cloudsql-instance-credentials
    - name: ssl-certs
    hostPath:
    path: /etc/ssl/certs
    - name: cloudsql
    emptyDir:
    27 changes: 27 additions & 0 deletions mx-services.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    apiVersion: v1
    kind: Service
    metadata:
    name: mx1
    spec:
    ports:
    - port: 25
    protocol: TCP
    targetPort: 2525
    selector:
    postal.app: smtp
    type: LoadBalancer
    loadBalancerIP: 130.211.122.23
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: mx2
    spec:
    ports:
    - port: 25
    protocol: TCP
    targetPort: 2525
    selector:
    postal.app: smtp
    type: LoadBalancer
    loadBalancerIP: 104.198.75.93
    56 changes: 56 additions & 0 deletions rabbitmq-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    creationTimestamp: null
    name: rabbitmq
    spec:
    replicas: 3
    strategy: {}
    template:
    metadata:
    creationTimestamp: null
    labels:
    postal.app: rabbitmq
    spec:
    containers:
    - image: andyshinn/postal-rabbitmq
    env:
    - name: MY_POD_NAME
    valueFrom:
    fieldRef:
    fieldPath: metadata.name
    - name: MY_POD_IP
    valueFrom:
    fieldRef:
    fieldPath: status.podIP
    - name: RABBITMQ_NODENAME
    value: "rabbit@$(MY_POD_IP)"
    - name: RABBITMQ_USE_LONGNAME
    value: "true"
    - name: AUTOCLUSTER_TYPE
    value: k8s
    - name: RABBITMQ_ERLANG_COOKIE
    value: PHIOCHASOUQUIAXUFIETH
    - name: RABBITMQ_DEFAULT_PASS
    valueFrom:
    secretKeyRef:
    name: postal-secrets
    key: rabbitpassword
    - name: RABBITMQ_DEFAULT_USER
    value: postal
    - name: RABBITMQ_DEFAULT_VHOST
    value: postal
    name: rabbitmq
    ports:
    - name: management
    containerPort: 15672
    - name: amqp
    containerPort: 5672
    resources: {}
    livenessProbe:
    exec:
    command: ["rabbitmqctl", "node_health_check"]
    initialDelaySeconds: 15
    periodSeconds: 15
    restartPolicy: Always
    status: {}
    19 changes: 19 additions & 0 deletions rabbitmq-service.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    postal.app: rabbitmq
    name: rabbitmq
    spec:
    ports:
    - name: "http"
    port: 15672
    targetPort: 15672
    - name: "amqp"
    port: 5672
    targetPort: 5672
    selector:
    postal.app: rabbitmq
    status:
    loadBalancer: {}
    47 changes: 47 additions & 0 deletions requeuer-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: requeuer
    spec:
    replicas: 1
    strategy:
    type: Recreate
    template:
    metadata:
    labels:
    postal.app: requeuer
    spec:
    containers:
    - image: gcr.io/cloudsql-docker/gce-proxy:1.09
    name: cloudsql-proxy
    command: ["/cloud_sql_proxy", "--dir=/cloudsql",
    "-instances=postal-165921:us-central1:postal=tcp:3306",
    "-credential_file=/secrets/cloudsql/credentials.json"]
    volumeMounts:
    - name: cloudsql-instance-credentials
    mountPath: /secrets/cloudsql
    readOnly: true
    - name: ssl-certs
    mountPath: /etc/ssl/certs
    - name: cloudsql
    mountPath: /cloudsql
    - image: andyshinn/postal-app
    name: requeuer
    command: ["bundle", "exec", "rake", "postal:requeuer"]
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Always
    volumes:
    - name: config
    configMap:
    name: postal-config
    - name: cloudsql-instance-credentials
    secret:
    secretName: cloudsql-instance-credentials
    - name: ssl-certs
    hostPath:
    path: /etc/ssl/certs
    - name: cloudsql
    emptyDir:
    status: {}
    34 changes: 34 additions & 0 deletions smtp-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    creationTimestamp: null
    name: smtp
    spec:
    replicas: 2
    strategy:
    type: Recreate
    template:
    metadata:
    creationTimestamp: null
    labels:
    postal.app: smtp
    spec:
    containers:
    - image: andyshinn/postal-app
    name: smtp
    command: ["bundle", "exec", "rake", "postal:smtp_server"]
    ports:
    - containerPort: 2525
    hostPort: 25
    resources: {}
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Always
    volumes:
    - name: config
    configMap:
    name: postal-config
    nodeSelector:
    cloud.google.com/gke-nodepool: smtp
    status: {}
    17 changes: 17 additions & 0 deletions smtp-service.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    apiVersion: v1
    kind: Service
    metadata:
    labels:
    postal.app: smtp
    name: smtp
    spec:
    ports:
    - name: smtp
    protocol: TCP
    port: 25
    targetPort: 2525
    externalIPs:
    - 130.211.185.93
    - 104.154.77.75
    selector:
    postal.app: smtp
    13 changes: 13 additions & 0 deletions web-ingress.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
    name: web-external
    annotations:
    #kubernetes.io/ingress.global-static-ip-name: "test-ip"
    kubernetes.io/ingress.class: "gce"
    spec:
    tls:
    - secretName: postal-secret
    backend:
    serviceName: web
    servicePort: 80
    17 changes: 17 additions & 0 deletions web-service.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    apiVersion: v1
    kind: Service
    metadata:
    creationTimestamp: null
    labels:
    postal.app: web
    name: web
    spec:
    ports:
    - name: "web"
    port: 80
    targetPort: 5000
    selector:
    postal.app: web
    type: NodePort
    status:
    loadBalancer: {}
    47 changes: 47 additions & 0 deletions worker-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: worker
    spec:
    replicas: 2
    strategy:
    type: Recreate
    template:
    metadata:
    labels:
    postal.app: worker
    spec:
    containers:
    - image: gcr.io/cloudsql-docker/gce-proxy:1.09
    name: cloudsql-proxy
    command: ["/cloud_sql_proxy", "--dir=/cloudsql",
    "-instances=postal-165921:us-central1:postal=tcp:3306",
    "-credential_file=/secrets/cloudsql/credentials.json"]
    volumeMounts:
    - name: cloudsql-instance-credentials
    mountPath: /secrets/cloudsql
    readOnly: true
    - name: ssl-certs
    mountPath: /etc/ssl/certs
    - name: cloudsql
    mountPath: /cloudsql
    - image: andyshinn/postal-app
    name: worker
    command: ["bundle", "exec", "rake", "postal:worker"]
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    restartPolicy: Always
    volumes:
    - name: config
    configMap:
    name: postal-config
    - name: cloudsql-instance-credentials
    secret:
    secretName: cloudsql-instance-credentials
    - name: ssl-certs
    hostPath:
    path: /etc/ssl/certs
    - name: cloudsql
    emptyDir:
    status: {}
  2. @andyshinn andyshinn created this gist Apr 30, 2017.
    77 changes: 77 additions & 0 deletions web-deployment.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    creationTimestamp: null
    name: web
    spec:
    replicas: 2
    strategy:
    type: Recreate
    template:
    metadata:
    creationTimestamp: null
    labels:
    postal.app: web
    spec:
    containers:
    - image: gcr.io/cloudsql-docker/gce-proxy:1.09
    name: cloudsql-proxy
    command: ["/cloud_sql_proxy", "--dir=/cloudsql",
    "-instances=postal-165921:us-central1:postal=tcp:3306",
    "-credential_file=/secrets/cloudsql/credentials.json"]
    volumeMounts:
    - name: cloudsql-instance-credentials
    mountPath: /secrets/cloudsql
    readOnly: true
    - name: ssl-certs
    mountPath: /etc/ssl/certs
    - name: cloudsql
    mountPath: /cloudsql
    - image: andyshinn/postal-app
    name: web
    command: ["bundle", "exec", "puma", "-C", "config/puma.rb"]
    env:
    - name: RAILS_SERVE_STATIC_FILES
    value: "true"
    ports:
    - containerPort: 5000
    resources: {}
    livenessProbe:
    httpGet:
    path: /login
    port: 5000
    initialDelaySeconds: 15
    periodSeconds: 15
    readinessProbe:
    httpGet:
    path: /login
    port: 5000
    initialDelaySeconds: 15
    periodSeconds: 15
    volumeMounts:
    - mountPath: /opt/postal/config
    name: config
    - mountPath: /usr/src/app/public/assets
    name: assets
    readOnly: true
    restartPolicy: Always
    nodeSelector:
    cloud.google.com/gke-nodepool: default-pool
    volumes:
    - name: assets
    gcePersistentDisk:
    pdName: postal-assets
    fsType: ext4
    readOnly: true
    - name: config
    configMap:
    name: postal-config
    - name: cloudsql-instance-credentials
    secret:
    secretName: cloudsql-instance-credentials
    - name: ssl-certs
    hostPath:
    path: /etc/ssl/certs
    - name: cloudsql
    emptyDir:
    status: {}