Strona główna » Aplikacja Spring na infrastrukturze GKE z użyciem Terraform

Aplikacja Spring na infrastrukturze GKE z użyciem Terraform

by Grzegorz Sowa

Z użyciem Terraform możemy jedną komendą stawiać nawet duże infrastruktury chmurowe, a także przechowywać je i wersjonować jako kod. Zobaczmy więc, jak możemy wykorzystać to narzędzie, aby w szybki sposób postawić aplikację Spring na GKE z użyciem Terraform. Potem spróbujemy dodatkowo połączyć aplikację z bazą danych PostgreSQL. Baza, a także połączenie pomiędzy nią a aplikacją stworzymy za pomocą Terraform i Kubernetes Secrets. Zaczynajmy!

Kod zawarty w tym artykule znajdziecie tutaj.

W artykule postawimy za pomocą Terraform całą serię komponentów i działającą aplikację. Przedstawia je poglądowo poniższa ilustracja.

spring gke terraform schema

Setup

Na początek zakładamy projekt Spring i konto w Google Cloud. Do przetestowania kodu w projekcie polecam założyć sobie oddzielny projekt. Pomimo, że będziemy na koniec niszczyć całą infrastrukturę komendą terraform destroy, usunięcie projektu da nam pewność, że nie zostanie przypadkiem jakiś zasób, który będzie dalej generował koszty. Poza tym nowy projekt zapewni nam świeży start i powtarzalność wyników.

Potrzebujemy zatem napisać aplikację Spring, stworzyć obiekty w GKE i konfigurację Terraform. Zacznijmy więc od końca, od Terraforma.

Provisioning infrastruktury

W głównym folderze naszego springowego projektu utwórzmy sobie folder terraform.  W nim main.tf dla naszego głównego kodu i variables.tf do zmiennych. Zacznijmy od zdefiniowania providera google w main.tf

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.88.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}

Skąd bierzemy project_id, region i zone? Skoro to zmienne, definiujemy je w pliku variables.tf. Wartości domyślne możemy zdefiniować na region warszawski, id projektu dopiszemy w czasie wywołania.

variable "project_id" {
  type = string
}

variable "region" {
  type = string
  default = "europe-central2"
}

variable "zone" {
  type = string
  default = "europe-central2-a"
}

Teraz wracamy do pliku main.tf, aby za pomocą providera google utworzyć klaster Kubernetes w prywatnej sieci VPC. Ta sieć przyda się później do bezpiecznego połączenia aplikacji z bazą danych. Tworzymy więc prywatną sieć i jej podsieci dla serwisów i pod-ów Kubernetesowych:

resource "google_compute_network" "vpc" {
  name                    = "test-network"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "vpc_subnet" {
  name          = "test-subnetwork"
  ip_cidr_range = "10.2.0.0/16"
  region        = var.region
  network       = google_compute_network.vpc.id

  secondary_ip_range {
    range_name    = "services-range"
    ip_cidr_range = "192.168.1.0/24"
  }

  secondary_ip_range {
    range_name    = "pod-ranges"
    ip_cidr_range = "192.168.64.0/22"
  }
}

Utwórzmy jeszcze konto serwisowe, z którego będzie korzystał nasz Kubernetes. Musimy pamiętać, aby nadać mu rolę roles/containerregistry.ServiceAgent, która pozwoli na tworzenie nowych pod-ów z nasza aplikacją:

resource "google_service_account" "default" {
  account_id   = "k8s-service-account-id"
  display_name = "K8s Service Account"
}

resource "google_project_iam_member" "registry_reader_binding" {
  role   = "roles/containerregistry.ServiceAgent"
  member = "serviceAccount:${google_service_account.default.email}"
}

Teraz już możemy stworzyć klaster Kuberenetes z maszynami wirtualnymi tworzonymi w prywatnych podsieciach, które zdefiniowaliśmy, podpięty pod zarządzane przez nas konto serwisowe. Ponadto zamiast domyślnej puli node-ów, zdefiniujemy pulę zarządzaną przez nas, co pozwoli na większą nad nią kontrolę z poziomu Terraform.

resource "google_container_cluster" "vpc_native_cluster" {
  name                     = "my-gke-cluster"
  remove_default_node_pool = true
  initial_node_count       = 1

  network    = google_compute_network.vpc.id
  subnetwork = google_compute_subnetwork.vpc_subnet.id

  ip_allocation_policy {
    cluster_secondary_range_name  = google_compute_subnetwork.vpc_subnet.secondary_ip_range.0.range_name
    services_secondary_range_name = google_compute_subnetwork.vpc_subnet.secondary_ip_range.1.range_name
  }
}

resource "google_container_node_pool" "vpc_native_cluster_preemptible_nodes" {
  name       = "my-node-pool"
  cluster    = google_container_cluster.vpc_native_cluster.name
  node_count = 1

  node_config {
    preemptible  = true
    machine_type = "e2-medium"

    service_account = google_service_account.default.email
    oauth_scopes    = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}

Teraz w naszym nowo utworzonym projekcie musimy otworzyć konsolę Cloud Shell (przyciskiem w prawym górnym rogu – po wejściu na console.cloud.google.com). Gdy ta się otworzy, możemy przerzucić projekt na wirtualną maszynę, która została utworzona na potrzeby konsoli. Dla mnie najprościej było wrzucić kod do repozytorium na GitHubie i pobrać komendą git clone. Mając już projekt, musimy uruchomić odpowiednie Google Cloud API, aby nasz projekt zadziałał. Zrobimy to z poziomu konsoli serią komend:

gcloud services enable cloudapis.googleapis.com
gcloud services enable container.googleapis.com
gcloud services enable servicenetworking.googleapis.com

Teraz wchodzimy w nasz projekt, w folder terraform i wywołujemy:

terraform init -upgrade

W tym momencie Terraform pobrał potrzebnych providerów i jest gotowy do akcji. Aby postawić zaplanowaną przez nas infrastrukturę, wywołujemy komendę, uzupełniając o id nowo utworzonego projektu:

terraform apply -var project_id="<TWOJE-ID-PROJEKTU>"

Terraform powinien w tym momencie rozpisać zmiany zaplanowane na platformie i zapytać, czy rzeczywiście chcemy ich dokonać. Potwierdzamy, wpisując „yes” i czekamy kilka minut, aż infrastruktura zostanie postawiona.

Aplikacja Spring

W naszej aplikacji do testów potrzebujemy następujących dependencji w pom.xml:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

Teraz zdefiniujmy kontroler, zwracający testowego stringa:

@RestController
public class TestController {

    @GetMapping
    public String test() {
        return "TEST RESPONSE FROM K8S APP";
    }
}

Mając to, będziemy mogli łatwo zweryfikować, czy nasza aplikacja została poprawnie zdeployowana i ma połączenie z publicznym internetem. Teraz tworzymy plik deployment.yml w głównym folderze projektu. Będzie on przechowywał definicje zasobów, które utworzymy na Kubernetesie, aby wdrożyć naszą aplikację. Za samo wdrożenie odpowiada zasób Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  labels:
    app: test-app
    environment: test
spec:
  replicas: 1
  selector:
    matchLabels:
      environment: test
      app: test-app
  template:
    metadata:
      labels:
        environment: test
        app: test-app
    spec:
      containers:
        - name: test-app
          image: gcr.io/{{GCLOUD_PROJECT_ID}}/gke-terraform-demo:{{IMAGE_VERSION}}
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: 8080
            initialDelaySeconds: 120
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8080

Poza tym, tworzymy jeszcze serwis, który posłuży nam jak punkt wejścia dla pod-ów należących do deploymentu naszej aplikacji. Możemy zrobić to w tym samym pliku, oddzielając separatorem ---:

# …

---

apiVersion: v1
kind: Service
metadata:
  name: test-app
  labels:
    run: test-app
spec:
  type: LoadBalancer
  ports:
    - port: 8080
      protocol: TCP
  selector:
    app: test-app
    environment: test

Zwróćmy uwagę na pola {{GCLOUD_PROJECT_ID}} i {{IMAGE_VERSION}}. Uzupełnimy je dynamicznie podczas budowania obrazu dockerowego i wrzucimy gotowy plik do folderu target. Do tego musimy użyć skryptu, który umieścimy w scripts/prepare-deployment.sh:

rm target/deployment.yml
sed -e "s+{{GCLOUD_PROJECT_ID}}+$1+g" -e "s+{{IMAGE_VERSION}}+$2+g" deployment.yml >> target/deployment.yml

Aby wywołać skrypt na dowolnym etapie buildu, użyjemy pluginu org.codehaus.mojo.exec-maven-plugin, który skonfigurujemy w pom.xml w sekcji <plugins>:

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>Inject version and project id to deployment.yml</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
                <configuration>
                    <executable>${basedir}/scripts/prepare-deployment.sh</executable>
                    <arguments>
                        <argument>${gcloud.project.id}</argument>
                        <argument>${project.version}</argument>
                    </arguments>
                </configuration>
            </plugin>

Nie mamy już tylko zawartości zmiennej ${gcloud.project.id}, ale podamy ją w wywołaniu jako argument.

W deployment.yml zdefiniowaliśmy też livenessProbe i readinessProbe jako requesty GET na określoną ścieżkę. Jest to podstawowy mechanizm healthcheck Kubernetesa – pozytywna odpowiedź z tych endpointów świadczy o zdrowiu aplikacji i jej gotowości na przyjęcie ruchu. Jeśli aplikacja po uruchomieniu przez dłuższy czas nie da odpowiedzi pozytywnej, pod na którym działa zostanie zrestartowany. Wpisaliśmy tutaj domyślną ścieżkę, na której działa projekt Spring Aktuator – dostawca mechanizmu healthcheck. Teraz wystarczy już tylko zaimportować go w pom.xml i sprawdzanie gotowości aplikacji będzie zintegrowane z Kubernetesem.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

Deploy aplikacji

Teraz przerzucamy zmiany na naszą konsolę Google Cloud Shell (np. przez gita) i przystępujemy do wdrożenia. Aby to zrobić, łączymy się z nowo utworzonym klastrem k8s:

gcloud container clusters get-credentials my-gke-cluster --zone=europe-central2-a

Teraz za pomocą komendy kubectl będziemy mogli zlecać na naszym klastrze różne zadania i odpytywać o jego stan. Wchodzimy więc do głównego folderu projektu i budujemy dockerowy obraz naszej aplikacji:

mvn spring-boot:build-image -Dgcloud.project.id=<TWOJE-ID-PROJEKTU>

W międzyczasie w folderze target wylądował już gotowy plik deployment.yml, uzupełniony o nasze id projektu oraz wersję obrazu aplikacji. Teraz przechowujemy obraz lokalnie, tutaj jednak Kubernetes nie ma do niego dostępu. Dlatego musimy wypushować go do Google Container Registry, które wcześniej włączyliśmy. Dopiero stamtąd Kubernetes będzie mógł pobrać obraz.

docker push gcr.io/<TWOJE-ID-PROJEKTU>/gke-terraform-demo:0.0.1-SNAPSHOT

Teraz możemy już utworzyć nowy Deployment za pomocą kubectl:

kubectl apply -f target/deployment.yml

Sprawdżmy, czy nasze wdrożenie przebiegło pomyślnie, komendą:

kubectl get deployments

Jeśli wszystko się udało, powinniśmy otrzymać coś podobnego:

NAME       READY   UP-TO-DATE   AVAILABLE   AGE
test-app   1/1     1            1           30s

Zobaczmy teraz, jakie publiczne IP dostał nasz serwis:

kubectl describe service test-app

Powinniśmy dostać dość długi output, zawierający m.in. informację, która nas interesuje:

LoadBalancer Ingress:     <PUBLICZNY-ADRES-IP>

Ten adres IP to właśnie adres, pod którym możemy znaleźć naszą aplikację w publicznym Internecie. Spróbujmy więc to przetestować zapytaniem, wpisując skopiowany z poprzedniego polecenia adres IP:

GET / HTTP/1.1
Host: <PUBLICZNY-ADRES-IP>:8080

Jeśli wszystko się udało, powinniśmy otrzymać w odpowiedzi:

TEST RESPONSE FROM K8S APP

Mamy to, postawiliśmy aplikację Spring na GKE z użyciem Terraform. Pójdźmy jednak o krok dalej…

Aplikacja z bazą danych SQL

Spróbujmy teraz zrobić nieco bardziej realny use-case, dodając możliwość zapisu do SQL-owej bazy danych. Bazą będzie PostgreSQL dostarczany przez Google Cloud SQL, a wszystko postawimy używając Terraform.

Baza i k8s secret w Terraform

Zacznijmy od zdefiniowania w pliku terraform/main.tf definicji prywatnego IP w naszej wirtualnej sieci, pod które podłączymy bazę danych:

resource "google_compute_global_address" "private_ip" {
  name          = "private-ip-address"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 16
  network       = google_compute_network.vpc.id
}

resource "google_service_networking_connection" "service_vpc_connection" {
  network                 = google_compute_network.vpc.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip.name]
}

Teraz definiujemy już samą instancję bazy PostgreSQL:

resource "random_id" "postgres_suffix" {
  byte_length = 4
}

resource "google_sql_database_instance" "postgres" {
  name             = "postgres-${random_id.postgres_suffix.hex}"
  project          = var.project_id
  region           = var.region
  database_version = "POSTGRES_13"

  depends_on = [google_service_networking_connection.service_vpc_connection]

  settings {
    tier = "db-f1-micro"
    ip_configuration {
      ipv4_enabled = false
      private_network = google_compute_network.vpc.id
    }
  }
  deletion_protection = false #TODO: true in real application
}

Dla naszej aplikacji musimy jeszcze zdefiniować usera i bazę danych, z której będzie korzystać:

resource "google_sql_database" "database" {
  name     = "${var.app_name}-db"
  instance = google_sql_database_instance.postgres.name
}

resource "random_password" "postgres_password" {
  length  = 32
  special = true
}

resource "google_sql_user" "postgres_user" {
  name     = "${var.app_name}-user"
  instance = google_sql_database_instance.postgres.name
  password = random_password.postgres_password.result
}

Zmienną odpowiadającą za nazwę aplikacji zdefiniujemy sobie w terraform/variables.tf:

variable "app_name" {
  type = string
  default = "test-app"
}

Mając już instancję PostgreSQL, a na niej zdefiniowaną bazę danych i użytkownika, musimy już tylko przekazać aplikacji dane do połączenia. Najlepiej nadaje się do tego obiekt Kubernetes Secret. Aby utworzyć go w Terraform, definiujemy providera kubernetes w sekcji required_providers:

terraform {
  required_providers {
    # …
    kubernetes = {
      source = "hashicorp/kubernetes"
      version = "2.5.1"
    }
  }
}

Konfigurujemy providera do połączenia z naszym klastrem:

data "google_client_config" "default" {}

provider "kubernetes" {
  host                   = "https://${google_container_cluster.vpc_native_cluster.endpoint}"
  cluster_ca_certificate = base64decode(google_container_cluster.vpc_native_cluster.master_auth[0].cluster_ca_certificate)
  token                  = data.google_client_config.default.access_token
}

Dzięki temu providerowi definiujemy sekret, który zostanie utworzony w naszym klastrze Kubernetes. Wstrzykujemy do niego użytkownika i hasło do bazy danych, jej nazwę oraz adres IP instancji PostgreSQL:

resource "kubernetes_secret" "postgres_credentials" {
  metadata {
    name = "postgres-credentials"
  }

  data = {
    host = google_sql_database_instance.postgres.first_ip_address
    db_name = google_sql_database.database.name
    username = google_sql_user.postgres_user.name
    password = google_sql_user.postgres_user.password
  }

  type = "kubernetes.io/basic-auth"
}

Deployment w k8s

W Kubernetes możemy łatwo podpiąć wartości z naszego sekretu do obiektu Deployment. Zrobimy to, modyfikując jego definicję w deployment.yml, aby wyglądała ona tak:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  labels:
    app: test-app
    environment: test
spec:
  replicas: 1
  selector:
    matchLabels:
      environment: test
      app: test-app
  template:
    metadata:
      labels:
        environment: test
        app: test-app
    spec:
      containers:
        - name: test-app
          image: gcr.io/{{GCLOUD_PROJECT_ID}}/gke-terraform-demo:{{IMAGE_VERSION}}
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: 8080
            initialDelaySeconds: 120
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8080
          env:
            - name: POSTGRES_HOST
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: host
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: db_name
            - name: POSTGRES_USERNAME
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: password
            - name: SPRING_PROFILES_ACTIVE
              value: "deployment"

W rzeczywistości podpięliśmy wartości z sekretu do zmiennych środowiskowych które zostaną zdefiniowane wewnątrz kontenera, w którym będzie uruchamiana nasza aplikacja. Kolejna zmienna, SPRING_PROFILES_ACTIVE zostanie automatycznie odebrana przez Springa jako profile uruchomieniowe. Użyjemy jej, aby włączyć automatyczne tworzenie tabel (ddl-auto) dopiero po uruchomieniu aplikacji w kontenerze. Ze zmiennych środowiskowych możemy skorzystać w application.yml, definiując parametry połączenia do bazy danych:

spring:
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQL10Dialect
  datasource:
    platform: postgres
    url: jdbc:postgresql://${POSTGRES_HOST:127.0.0.1}:5432/${POSTGRES_DB:test}
    username: ${POSTGRES_USERNAME:postgres}
    password: ${POSTGRES_PASSWORD:postgres}
    driverClassName: org.postgresql.Driver

---

spring:
  config.activate.on-profile: deployment
  jpa.hibernate.ddl-auto: update

Testowa tabela w bazie i kontroler

W naszej apce będziemy potrzebować dodatkowych dependencji aby połączyć się z bazą PostgrSQL:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.2.24</version>
        </dependency>

Teraz możemy zdefiniować encję bazodanową Book, dla której utworzymy w bazie danych tabelę oraz będziemy mogli wykonać testowo odczyt i zapis:

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;

    static Book of(String title) {
        final Book book = new Book();
        book.title = title;
        return book;
    }
}

Zdefiniujmy repozytorium dla encji Book:

public interface BookRepository extends JpaRepository<Book, Long> {
}

I podepnijmy je w kontrolerze pozwalającym na odczyt/zapis:

@RestController
@RequestMapping("books")
@RequiredArgsConstructor
public class BookController {

    private final BookRepository repository;

    @GetMapping
    public List<Book> getBooks() {
        return repository.findAll();
    }

    @PostMapping
    public ResponseEntity<Book> addNewBook(@RequestBody AddNewBookRequest request) {
        return ResponseEntity.status(CREATED).body(
                repository.save(Book.of(request.getTitle()))
        );
    }
}

@Data
public class AddNewBookRequest {
    private String title;
}

Wdrożenie aplikacji

Teraz, aby wdrożyć aplikację, przerzucamy cały kod do Cloud Shella. Następnie wchodzimy do folderu terraform i wywołujemy:

terraform init -upgrade

Komenda powinna pobrać nam nowego providera kubernetes, którego przed chwilą zdefiniowaliśmy. Teraz możemy już zaaplikować zmiany do naszej infrastruktury:

terraform apply -var project_id="<TWOJE-ID-PROJEKTU>"

Teraz wracamy do głównego folderu i budujemy nową wersję naszej aplikacji:

mvn spring-boot:build-image -Dgcloud.project.id=<TWOJE-ID-PROJEKTU>

I pushujemy ją do registry:

docker push gcr.io/<TWOJE-ID-PROJEKTU>/gke-terraform-demo:0.0.1-SNAPSHOT

Jeszcze raz pobieramy dane uwierzytelnienia z naszego klastra Kubernetes:

gcloud container clusters get-credentials my-gke-cluster --zone=europe-central2-a

Teraz możemy usunąć stary Deployment i wrzucić jego zaktualizowaną definicję (usuwamy aby zmusić Kubernetesa do pobrania nowej wersji obrazu, mimo że ma taką samą nazwę).

kubectl delete deployment test-app
kubectl apply -f target/deployment.yml

Sprawdźmy, czy wszystko wstało:

kubectl get deployments

Powinniśmy otrzymać coś podobnego:

NAME       READY   UP-TO-DATE   AVAILABLE   AGE
test-app   1/1     1            1           30s

Teraz już możemy przetestować naszą apkę i połączenie z bazą danych. Utwórzmy sobie parę książek:

POST /books HTTP/1.1
Host: <PUBLICZNY-ADRES-IP>:8080
Content-Type: application/json

{
    "title": "Game of Thrones"
}

Powinniśmy dostać w odpowiedzi:

HTTP/1.1 201
Content-Type: application/json

{
  "id": 2,
  "title": "Game of Thrones"
}

A teraz spróbujmy pobrać sobie wszystkie książki:

GET /books HTTP/1.1
Host: <PUBLICZNY-ADRES-IP>:8080

Jeśli wszystko się udało, powinniśmy otrzymać:

HTTP/1.1 200
Content-Type: application/json

[
  {
    "id": 1,
    "title": "Game of Thrones"
  },
  {
    "id": 2,
    "title": "Harry Potter and the Chamger of Secrets"
  }
]

Cool, mamy teraz w pełni działającą aplikację Spring na GKE z bazą SQL, wszystko postawione z użyciem Terraform!

Zagadnienia bezpieczeństwa

Przede wszystkim miejcie świadomość, że to tylko projekt poglądowy – nie wszystkie opisane tu kroki spełniają zalecenia bezpieczeństwa i powinny być stosowane w systemach produkcyjnych. To temat na zupełnie inny artykuł (a może i kilka), ale wymienię chociaż dwa:

  • Stan Terraforma jest tutaj trzymany lokalnie. Ponieważ jednak zawiera on hasła do bazy danych, powinniśmy traktować go jako daną wrażliwą. Czyli co najmniej przechowywać na dedykowanym serwerze, w postaci zaszyfrowanej.
  • Baza danych jest dostępna tylko z prywatnego adresu IP i to dobrze, ale najbezpieczniejszą i rekomendowaną formą połączenia aplikacji z nią jest Cloud SQL Auth Proxy. Możemy go uruchomić jako sidecar, w tym samym podzie, obok naszej aplikacji i kierować ruch do bazy danych przez niego.

Czas na sprzątanie

Całą infrastrukturę możesz teraz złożyć za pomocą komendy, wywołanej w folderze terraform:

terraform apply -var project_id="<TWOJE-ID-PROJEKTU>"

Polecam po tym również usunąć dla pewności projekt, na którym pracowałeś.

Podsumowanie

Jeśli przeszedłeś cały artykuł, powinieneś już mieć działającą aplikację Spring na GKE, podniesioną z użyciem Terraform i z podpiętą do niej bazą danych SQL. Co najważniejsze jednak, wszystko działa w chmurze a Ty możesz podnieść lub zniszczyć to wszystko za pomocą kilku komend. Mam nadzieję, że pokazuje to moc możliwości, jakie oferuje Terraform.

Leave a Comment