Skip to content

Commit

Permalink
Closes #40 to update terraform and k8s to use workload identity. (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtest authored Jan 19, 2023
1 parent bb73db5 commit e2743a9
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 107 deletions.
12 changes: 1 addition & 11 deletions docs/GKE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,6 @@ export PROJECT_ID=<YOUR_PROJECT_ID>

Once the images have been built, it is time to deploy the [kubernetes manifests](../kubernetes-manifests). Each backend application provides a LoadBalance service and a deployment.

Create the secret for the service account that will connect to the Spanner instance:

```
export SERVICE_ACCOUNT=sample-game-backend
gcloud iam service-accounts keys create backend_sa_key.json \
--iam-account=${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com
kubectl create secret generic sample-game-backend-sa-key \
--from-file=backend_sa_key.json=./backend_sa_key.json
```

Create a config map for the Spanner instance

```
Expand All @@ -62,6 +51,7 @@ sed -e "s/PROJECT_ID/$PROJECT_ID/" \
kubectl apply -f spanner_config.yaml
```
> Note: [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity) manages credentials to ensure access to Cloud Spanner
### Deploy the manifests
Once you have the kubernetes secret and config map established to connec to Cloud Spanner, the only thing left is to deploy the manifests. A [`scripts/deploy.sh`](../scripts/deploy.sh) file has been created to assist with this process.
Expand Down
54 changes: 31 additions & 23 deletions infrastructure/gke.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

variable "gke_config" {
type = object({
cluster_name = string
location = string
resource_labels = map(string)
})

description = "The configuration specifications for a GKE Autopilot cluster"
}

resource "google_compute_network" "vpc" {
name = "cymbal-game-staging-vpc"
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "subnet" {
name = "cymbal-game-subnet"
ip_cidr_range = "10.1.0.0/16"
region = "us-central1"
network = google_compute_network.vpc.id
}

resource "google_container_cluster" "sample-game-gke" {
name = var.gke_config.cluster_name
location = var.gke_config.location
Expand All @@ -44,6 +22,36 @@ resource "google_container_cluster" "sample-game-gke" {
# See issue: https://github.com/hashicorp/terraform-provider-google/issues/10782
ip_allocation_policy {}

# Enabling Autopilot for this cluster
# Enabling Autopilot for this cluster
enable_autopilot = true
}

resource "google_service_account" "backend_sa" {
account_id = var.backend_sa_config.name
display_name = var.backend_sa_config.description
project = var.gcp_project
}

resource "kubernetes_service_account" "k8s-service-account" {
metadata {
name = var.k8s_service_account_id
namespace = "default"
annotations = {
"iam.gke.io/gcp-service-account" : "${google_service_account.backend_sa.email}"
}
}
}

data "google_iam_policy" "spanner-policy" {
binding {
role = "roles/iam.workloadIdentityUser"
members = [
"serviceAccount:${var.gcp_project}.svc.id.goog[default/${kubernetes_service_account.k8s-service-account.metadata[0].name}]"
]
}
}

resource "google_service_account_iam_policy" "backend-service-account-iam" {
service_account_id = google_service_account.backend_sa.name
policy_data = data.google_iam_policy.spanner-policy.policy_data
}
26 changes: 24 additions & 2 deletions infrastructure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.

variable "gcp_project" {
type = string
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "4.44.1"
}
}

required_version = ">= 0.14"
}

provider "google" {
project = var.gcp_project
}

data "google_client_config" "provider" {}

data "google_container_cluster" "gke-provider" {
name = var.gke_config.cluster_name
location = var.gke_config.location
}

provider "kubernetes" {
host = "https://${data.google_container_cluster.gke-provider.endpoint}"
token = data.google_client_config.provider.access_token
cluster_ca_certificate = base64decode(
data.google_container_cluster.gke-provider.master_auth[0].cluster_ca_certificate,
)
}
35 changes: 0 additions & 35 deletions infrastructure/spanner.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

variable "spanner_config" {
type = object({
instance_name = string
database_name = string
configuration = string
display_name = string
processing_units = number
environment = string
})
description = "The configuration specifications for the Spanner instance"

validation {
condition = length(var.spanner_config.display_name) >= 4 && length(var.spanner_config.display_name) <= "30"
error_message = "Display name must be between 4-30 characters long."
}

validation {
condition = (var.spanner_config.processing_units <= 1000) && (var.spanner_config.processing_units%100) == 0
error_message = "Processing units must be 1000 or less, and be a multiple of 100."
}
}

variable backend_sa_config {
type = object({
name = string
description = string
})
description = "The configuration specifications for the backend service account"
}

resource "google_spanner_instance" "instance" {
name = var.spanner_config.instance_name # << be careful changing this in production
config = var.spanner_config.configuration
Expand All @@ -57,11 +27,6 @@ resource "google_spanner_database" "database" {
}

# Create IAM service account for locked down cloud run container to access Spanner service
resource "google_service_account" "backend_sa" {
account_id = var.backend_sa_config.name
display_name = var.backend_sa_config.description
}

resource "google_spanner_database_iam_binding" "backend_iam_spanner" {
instance = google_spanner_instance.instance.name
database = google_spanner_database.database.name
Expand Down
3 changes: 3 additions & 0 deletions infrastructure/terraform.tfvars.sample
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ backend_sa_config = {
description = "Sample Game backend service account"
}

k8s_service_account_id = "k8s-service-account"

gke_config = {
cluster_name = "sample-game-gke"
location = "us-central1"
Expand All @@ -22,3 +24,4 @@ gke_config = {
}
}


61 changes: 61 additions & 0 deletions infrastructure/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

variable "gcp_project" {
type = string
}

variable "spanner_config" {
type = object({
instance_name = string
database_name = string
configuration = string
display_name = string
processing_units = number
environment = string
})
description = "The configuration specifications for the Spanner instance"

validation {
condition = length(var.spanner_config.display_name) >= 4 && length(var.spanner_config.display_name) <= "30"
error_message = "Display name must be between 4-30 characters long."
}

validation {
condition = (var.spanner_config.processing_units <= 1000) && (var.spanner_config.processing_units%100) == 0
error_message = "Processing units must be 1000 or less, and be a multiple of 100."
}
}

variable "gke_config" {
type = object({
cluster_name = string
location = string
resource_labels = map(string)
})

description = "The configuration specifications for a GKE Autopilot cluster"
}

variable backend_sa_config {
type = object({
name = string
description = string
})
description = "The configuration specifications for the backend service account"
}

variable "k8s_service_account_id" {
description = "The kubernetes service account that will impersonate the IAM service account to access Cloud Spanner. This account will be created."
}
25 changes: 25 additions & 0 deletions infrastructure/vpc.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

resource "google_compute_network" "vpc" {
name = "cymbal-game-staging-vpc"
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "subnet" {
name = "cymbal-game-subnet"
ip_cidr_range = "10.1.0.0/16"
region = "us-central1"
network = google_compute_network.vpc.id
}
10 changes: 1 addition & 9 deletions kubernetes-manifests/item-service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,13 @@ spec:
labels:
app: cymbal-games-item
spec:
volumes:
- name: backend-sa-key
secret:
secretName: sample-game-backend-sa-key
serviceAccountName: k8s-service-account
containers:
- name: item-service
image: gcr.io/GCP_PROJECT_ID/item-service:latest # EDIT: Your GCP Project ID
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/secrets
name: backend-sa-key
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/backend_sa_key.json
- name: SPANNER_PROJECT_ID
valueFrom:
configMapKeyRef:
Expand Down
10 changes: 1 addition & 9 deletions kubernetes-manifests/matchmaking-service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,13 @@ spec:
labels:
app: cymbal-games-matchmaking
spec:
volumes:
- name: backend-sa-key
secret:
secretName: sample-game-backend-sa-key
serviceAccountName: k8s-service-account
containers:
- name: matchmaking-service
image: gcr.io/GCP_PROJECT_ID/matchmaking-service:latest # EDIT: Your GCP Project ID
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/secrets
name: backend-sa-key
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/backend_sa_key.json
- name: SPANNER_PROJECT_ID
valueFrom:
configMapKeyRef:
Expand Down
10 changes: 1 addition & 9 deletions kubernetes-manifests/profile-service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,13 @@ spec:
labels:
app: cymbal-games-profile
spec:
volumes:
- name: backend-sa-key
secret:
secretName: sample-game-backend-sa-key
serviceAccountName: k8s-service-account
containers:
- name: profile-service
image: gcr.io/GCP_PROJECT_ID/profile-service:latest # EDIT: Your GCP Project ID
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/secrets
name: backend-sa-key
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/backend_sa_key.json
- name: SPANNER_PROJECT_ID
valueFrom:
configMapKeyRef:
Expand Down
10 changes: 1 addition & 9 deletions kubernetes-manifests/tradepost-service.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,13 @@ spec:
labels:
app: cymbal-games-tradepost
spec:
volumes:
- name: backend-sa-key
secret:
secretName: sample-game-backend-sa-key
serviceAccountName: k8s-service-account
containers:
- name: tradepost-service
image: gcr.io/GCP_PROJECT_ID/tradepost-service:latest # EDIT: Your GCP Project ID
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/secrets
name: backend-sa-key
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/backend_sa_key.json
- name: SPANNER_PROJECT_ID
valueFrom:
configMapKeyRef:
Expand Down

0 comments on commit e2743a9

Please sign in to comment.