Nota

Abilita il blocco dei file di stato Terraform con il backend compatibile con Amazon S3 in OCI

Introduzione

Nel mondo dinamico del cloud computing, Infrastructure as Code (IaC) è emerso come un approccio cruciale per le organizzazioni che cercano di gestire efficacemente la propria infrastruttura. Il vantaggio principale di IaC risiede nella sua capacità di promuovere coerenza, automazione, controllo delle versioni e collaborazione, rendendolo un elemento indispensabile delle strategie IT cloud native.

Terraform si distingue come un importante strumento IaC e memorizza una rappresentazione degli oggetti dell'infrastruttura e delle relative dipendenze in un file di configurazione denominato terraform.tfstate. In un ambiente collaborativo in cui più membri del team gestiscono l'infrastruttura cloud, memorizzare localmente terraform.tfstate diventa difficile. Per risolvere questo problema, Terraform offre una funzione denominata "Backend remoto" per abilitare la memorizzazione del file di stato in una posizione condivisa. Alcuni backend supportano il blocco dello stato tf mentre vengono eseguite le operazioni plan o apply per garantire l'integrità dei dati e prevenire i conflitti.

Questa esercitazione si concentrerà su come impostare il backend compatibile con S3 e l'API compatibile con DynamoDB di ScyllaDB per abilitare il blocco dei file di stato.

Obiettivi

Prerequisiti

Approccio 1: Distribuzione automatica

Fare clic di seguito su Distribuisci in Oracle Cloud, immettere i dettagli richiesti e fare clic su Applica.

Distribuisci in OCI

Approccio 2: Distribuzione manuale

Task 1: impostare ScyllaDB e abilitare l'API compatibile con DynamoDB

Creeremo un'istanza basata su ARM, installeremo Docker ed eseguiremo ScyllaDB.

Task 1.1: eseguire il provisioning di una nuova istanza

  1. Passare alla pagina Istanze in OCI Console e fare clic su Crea istanza.

  2. Immettere i parametri di configurazione richiesti considerando i suggerimenti riportati di seguito.

    • Immagine: Oracle Linux 8

    • Forma: VM.Standard.A1.Flex (1 OCPU, 6 GB RAM)

    • VNIC primaria: Public Subnet & Assign Public IPv4 Address (verrà utilizzata per la connettività SSH)

    Prendere nota dell'indirizzo IP pubblico e privato dell'istanza.

Task 1.2: Installa Docker

  1. Connettersi all'istanza tramite SSH.

    $ ssh opc@<public-ip-address-of-the-instance>
    
  2. Installa Docker Engine, containerd e Docker Compose.

    sudo yum install -y yum-utils
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    sudo systemctl start docker.service
    sudo systemctl enable docker.service
    sudo usermod -aG docker opc
    
  3. Riconnettere la sessione SSH e convalidare l'installazione riuscita del Docker.

    docker run hello-world
    
    # Unable to find image 'hello-world:latest' locally
    # latest: Pulling from library/hello-world
    # 70f5ac315c5a: Pull complete
    # Digest: sha256:3155e04f30ad5e4629fac67d6789f8809d74fea22d4e9a82f757d28cee79e0c5
    # Status: Downloaded newer image for hello-world:latest
    
    # Hello from Docker!
    # This message shows that your installation appears to be working correctly.
    

Task 1.3: Genera chiave segreta cliente

La chiave segreta del cliente è necessaria per accedere allo storage degli oggetti OCI utilizzando l'API compatibile S3.

  1. Passare alla pagina del profilo utente in OCI Console e selezionare Chiavi segrete cliente.

  2. Generare una nuova chiave, copiare il valore della chiave segreta e fare clic su Chiudi.

  3. Copiare il valore della chiave di accesso (seconda colonna in questo elenco con le chiavi segrete del cliente).

Task 1.4: configurare e avviare ScyllaDB

  1. Creare la directory di distribuzione s3-lock.

    mkdir s3-lock
    cd s3-lock
    
  2. Creare .env utilizzando il comando seguente.

    AWS_ACCESS_KEY_ID='<ACCESS_KEY>'
    AWS_SECRET_ACCESS_KEY='<SECRET_KEY>'
    TF_STATE_TABLE='s3-locking-demo'
    

    Nota: il file .env verrà utilizzato dalla composizione Docker per impostare ScyllaDB.

  3. Creare un file scylladb.Dockerfile nella directory scylladb utilizzando il comando seguente.

    FROM scylladb/scylla:latest
    RUN echo "alternator_enforce_authorization: true" >> /etc/scylla/scylla.yaml
    ENTRYPOINT ["/docker-entrypoint.py"]
    
  4. Creare il file docker-compose.yaml nella directory s3-lock utilizzando il comando seguente.

    version: "3.3"
    
    services:
      scylladb:
        build:
          dockerfile: scylladb.Dockerfile
          context: ./scylladb
        image: "local-scylla:latest"
        container_name: "scylladb"
        restart: always
        command: ["--alternator-port=8000", "--alternator-write-isolation=always"]
        ports:
          - "8000:8000"
          - "9042:9042"
    
      scylladb-load-user:
        image: "scylladb/scylla:latest"
        container_name: "scylladb-load-user"
        depends_on:
          - scylladb
        entrypoint: /bin/bash -c "sleep 60 && echo loading cassandra keyspace && cqlsh scylladb -u cassandra -p cassandra \
                                  -e \"INSERT INTO system_auth.roles (role,can_login,is_superuser,member_of,salted_hash) \
                                  VALUES ('${AWS_ACCESS_KEY_ID}',True,False,null,'${AWS_SECRET_ACCESS_KEY}');\""
    
      scylladb-create-table:
        image: "amazon/aws-cli"
        container_name: "create_table"
        depends_on:
          - scylladb
        env_file: .env
        entrypoint: /bin/sh -c "sleep 70 && aws dynamodb create-table --table-name ${TF_STATE_TABLE} \
                                --attribute-definitions AttributeName=LockID,AttributeType=S \
                                --key-schema AttributeName=LockID,KeyType=HASH --billing-mode=PAY_PER_REQUEST \
                                --region 'None' --endpoint-url=http://scylladb:8000"
    
  5. Esaminare la struttura della directory.

    $ tree -a .
    .
    ├── docker-compose.yaml
    ├── .env
    └── scylladb
        └── scylladb.Dockerfile
    
    1 directory, 3 files
    
  6. Avviare il servizio ScyllaDB.

    docker compose up -d
    
  7. Consentire connessioni in entrata alla porta 8000.

    sudo firewall-cmd --add-port 8000/tcp --permanent
    
  8. Convalida la connessione a ScyllaDB.

    • Installare i pacchetti python3-pip e boto3.

      sudo yum install -y python3-pip
      python3 -m pip install --user boto3
      
    • Creare il file script.py utilizzando il comando seguente.

      import boto3
      
      endpoint_url = 'http://localhost:8000'
      aws_access_key_id = '<ACCESS_KEY>'
      aws_secret_access_key = '<SECRET_KEY>'
      table_name = "s3-locking-demo"
      
      client = boto3.client('dynamodb', endpoint_url=endpoint_url, region_name="None",
                      aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key)
      
      response = client.describe_table(TableName=table_name)
      
      print(response["Table"]["TableName"], response["Table"]["TableStatus"])
      
    • Eseguire lo script utilizzando il comando seguente.

      python3 script.py
      

    Se l'esecuzione dello script restituisce s3-locking-demo ACTIVE, funziona come previsto.

Task 2: configurare il gateway API OCI per proteggere le connessioni all'API compatibile con ScyllaDB DynamoDB

In questo task, configureremo OCI API Gateway per sfruttare la cifratura TLS tra gli utenti e ScyllaDB.

Task 2.1: creare un nuovo gateway API

  1. Passare alla pagina Gateway API nella console OCI e fare clic su Crea gateway.

    • Nome: s3-locking

    • Tipo: public

    • Rete: la stessa VCN e la stessa subnet dell'istanza ScyllaDB

    • Certificato: Default (*.oci.oci-customer.com)

  2. Prendere nota del nome host associato al nuovo gateway API creato. Il nome host viene visualizzato nella scheda Informazioni gateway quando si fa clic sulla nuova risorsa gateway API creata.

    Ad esempio: fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com

Task 2.2: Creare una nuova distribuzione del gateway API

  1. Consente di creare una nuova distribuzione.

    1. Fare clic sul nuovo gateway API creato e nel menu a sinistra fare clic su Distribuzioni in Risorse.

    2. Fare clic su Crea distribuzione e creare una nuova distribuzione utilizzando le informazioni riportate di seguito.

      • Informazioni di base

        • Nome: default

        • Prefisso percorso: /

      • Autenticazione: nessuna autenticazione

      • Instradamenti

        • Percorso: /{requested_path*}

        • Metodi: ANY

        • Tipo di backend: HTTP

        • URL: http://<private_ip_address_of_the_instance>:8000/${request.path[requested_path]}

    3. Andare a Criteri richiesta percorso, Trasformazioni intestazione e fare clic su Aggiungi.

      • Azione: Set

      • Comportamento: Overwrite

      • Nome intestazione: Host

      • Valori: <API Gateway hostname> (ad esempio: fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com)

    4. Rivedere i dettagli della nuova distribuzione e fare clic su Crea.

  2. Impostare la lista di sicurezza della subnet per consentire la connessione in entrata e in uscita alla porta 8000.

    1. Recupera il blocco CIDR allocato alla subnet in uso, utilizzando i passi riportati di seguito.

    2. Identificare la lista di sicurezza associata alla subnet utilizzata dall'istanza utilizzando i passi riportati di seguito.

    3. Fare clic sulla lista di sicurezza predefinita già associata alla subnet e aggiungere le regole riportate di seguito.

      • Ingresso

        • CIDR di origine: 0.0.0.0/0

        • Protocollo: TCP

        • Intervallo di porte di destinazione: 443

        • Descrizione: Ingress Access to the API Gateway

      • Ingresso

        • CIDR di origine: <subnet CIDR>

        • Protocollo: TCP

        • Intervallo di porte di destinazione: 8000

        • Descrizione: Ingress connection to the ScyllaDB

      • Uscita

        • CIDR di destinazione: <subnet CIDR>

        • Protocollo: TCP

        • Intervallo di porte di destinazione: 8000

        • Descrizione: Egress connection from the API Gateway backend to ScyllaDB

Task 2.3: convalidare la connessione a ScyllaDB tramite il gateway API

  1. Aggiornare endpoint_url nel file script.py per utilizzare il nome host del gateway API. Ad esempio: endpoint_url = "https://fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com"

  2. Eseguire lo script per eseguire il test della connessione all'endpoint pubblico.

    python3 script.py
    s3-locking-demo ACTIVE
    

Task 3: eseguire il test del blocco del file terraform.tfstate quando si utilizza l'API compatibile con S3

Verrà eseguita la seguente procedura sull'istanza in cui si desidera eseguire il codice terraform.

  1. Copiare le righe seguenti e creare il file main.tf.

    resource "null_resource" "hello_world" {
      provisioner "local-exec" {
        command = "echo Hello World"
      }
    
      provisioner "local-exec" {
        command = "echo 'sleeping for 30 seconds';sleep 30;echo 'done';"
      }
    
      triggers = {
        run_always = "${timestamp()}"
      }
    }
    
  2. Configurare il backend S3.

    terraform {
      backend "s3" {
        bucket = "<bucket-name>" # e.g.: bucket = "sample-bucket"
        region = "<oci-region>"  # e.g.: region = "eu-frankfurt-1"
    
        skip_region_validation      = true
        skip_credentials_validation = true
        skip_metadata_api_check     = true
        # skip_requesting_account_id  = true
        # skip_s3_checksum            = true
    
        force_path_style = true
        # use_path_style = true
        # insecure       = true
    
        # For best practice on how to set credentials access: https://developer.hashicorp.com/terraform/language/settings/backends/s3#access_key
    
        access_key = "<ACCESS_KEY>"
        secret_key = "<SECRET_KEY>"
    
        # endpoints = {
        #   # To determine <objectostrage_namespace> access: https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/understandingnamespaces.htm
        #   s3       = "https://<objectstorage_namespace>.compat.objectstorage.<oci-region>.oraclecloud.com"
        #   # e.g.: s3 = https://axaxnpcrorw5.compat.objectstorage.eu-frankfurt-1.oraclecloud.com
    
        #   # ScyllaDB TLS endpoint, configured using the API Gateway:
        #   dynamodb = "https://<API_Gateway_hostname>"
        #   # e.g.: dynamodb = "https://fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com"
        # }
    
        # ScyllaDB TLS endpoint, configured using the API Gateway:
        dynamodb_endpoint = "https://<API_Gateway_hostname>"
        # e.g.: dynamodb_endpoint = "https://fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com"
        key            = "demo.tfstate" # the name of the tfstate file
        dynamodb_table = "s3-locking-demo" # the name of the table in the ScyllaDB
      }
    }
    
  3. Inizializzare la directory di lavoro con il comando terraform init.

    $ terraform init
    
    Initializing the backend...
    
    Successfully configured the backend "s3"! Terraform will automatically
    use this backend unless the backend configuration changes.
    
    Initializing provider plugins...
    - Finding latest version of hashicorp/null...
    - Installing hashicorp/null v3.2.2...
    - Installed hashicorp/null v3.2.2 (signed by HashiCorp)
    
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    
    Terraform has been successfully initialized!
    
  4. Eseguire terraform apply.

    $ terraform apply
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # null_resource.hello_world will be created
      + resource "null_resource" "hello_world" {
          + id       = (known after apply)
          + triggers = {
              + "run_always" = (known after apply)
            }
        }
    
    Plan: 1 to add, 0 to change, 0 to destroy.
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    null_resource.hello_world: Creating...
    null_resource.hello_world: Provisioning with 'local-exec'...
    null_resource.hello_world (local-exec): Executing: ["/bin/sh" "-c" "echo Hello World"]
    null_resource.hello_world (local-exec): Hello World
    null_resource.hello_world: Provisioning with 'local-exec'...
    null_resource.hello_world (local-exec): Executing: ["/bin/sh" "-c" "echo 'sleeping for 30 seconds';sleep 30;echo 'done';"]
    null_resource.hello_world (local-exec): sleeping for 30 seconds
    null_resource.hello_world: Still creating... [10s elapsed]
    null_resource.hello_world: Still creating... [20s elapsed]
    null_resource.hello_world: Still creating... [30s elapsed]
    null_resource.hello_world (local-exec): done
    null_resource.hello_world: Creation complete after 30s [id=5722520729023050684]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    
  5. Eseguire il test del blocco del file terraform.tfstate. Se si tenta di eseguire terraform plan o terraform apply durante l'esecuzione del task 3.4, la richiesta verrà rifiutata.

    $ terraform apply
    ╷
    │ Error: Error acquiring the state lock
    │
    │ Error message: operation error DynamoDB: PutItem, https response error StatusCode: 400, RequestID: ,
    │ ConditionalCheckFailedException: Failed condition.
    │ Lock Info:
    │   ID:        69309f13-d9fc-8c6b-9fbe-73639b340539
    │   Path:      sample-bucket/demo.tfstate
    │   Operation: OperationTypeApply
    │   Who:       use
    │   Version:   1.6.4
    │   Created:   2023-12-14 11:31:30.291168816 +0000 UTC
    │   Info:
    │
    │
    │ Terraform acquires a state lock to protect the state from being written
    │ by multiple users at the same time. Please resolve the issue above and try
    │ again. For most commands, you can disable locking with the "-lock=false"
    │ flag, but this is not recommended.
    
  6. Gestire le voci nelle tabelle ScyllaDB utilizzando l'API DynamoDB. Se è necessario elencare le voci nella tabella DynamoDB o rimuovere manualmente le voci, è possibile aggiungere le righe seguenti alla fine del file script.py.

    scan_response = client.scan(
        TableName=table_name,
    )
    
    print(scan_response)
    
    entry_to_delete = input("what is the LockID value you would like to delete? ")
    
    delete_response = client.delete_item(
        Key={
            'LockID': {
                'S': f'{entry_to_delete}',
            },
        },
        TableName=table_name
        )
    
    print(delete_response)
    

    Nota: ulteriori funzioni sono disponibili in questo script.

Conferme

Altre risorse di apprendimento

Esplora altri laboratori su docs.oracle.com/learn o accedi a più contenuti gratuiti sulla formazione su Oracle Learning YouTube channel. Inoltre, visita education.oracle.com/learning-explorer per diventare Oracle Learning Explorer.

Per la documentazione del prodotto, visitare Oracle Help Center.