Note:

Activación del bloqueo de archivos de Terraform State con backend compatible con Amazon S3 en OCI

Introducción

En el dinámico mundo de la computación en la nube, la infraestructura como código (IaC) se ha convertido en un enfoque crucial para las organizaciones que buscan gestionar eficazmente su infraestructura. La ventaja clave de IaC radica en su capacidad para promover la consistencia, la automatización, el control de versiones y la colaboración, lo que la convierte en un elemento indispensable de las estrategias de TI nativas de la nube.

Terraform se destaca como una herramienta IaC destacada, almacena una representación de los objetos de infraestructura y sus dependencias en un archivo de configuración denominado terraform.tfstate. En un entorno de colaboración en el que varios miembros del equipo gestionan la infraestructura en la nube, almacenar terraform.tfstate localmente se convierte en un reto. Para solucionarlo, Terraform ofrece una función denominada "backend remoto" para permitir el almacenamiento del archivo de estado en una ubicación compartida. Algunos de los backends admiten el bloqueo tfstate mientras se ejecutan las operaciones plan o apply para garantizar la integridad de los datos y evitar conflictos.

Este tutorial se centrará en cómo configurar el backend compatible con S3 y la API compatible con DynamoDB de ScyllaDB para activar el bloqueo de archivos de estado.

Objetivos

Requisitos

Enfoque 1: Despliegue automático

Haga clic en Desplegar en Oracle Cloud, introduzca los detalles necesarios y haga clic en Aplicar.

Desplegar en OCI

Enfoque 2: Despliegue manual

Tarea 1: Configurar ScyllaDB y activar la API compatible con DynamoDB

Crearemos una instancia basada en ARM, instalaremos Docker y configuraremos y ejecutaremos ScyllaDB.

Tarea 1.1: Aprovisionamiento de una nueva instancia

  1. Navegue a la página Instancias de la consola de OCI y haga clic en Crear instancia.

  2. Escriba los parámetros de configuración necesarios teniendo en cuenta las siguientes recomendaciones.

    • Imagen: Oracle Linux 8

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

    • VNIC principal: Public Subnet & Assign Public IPv4 Address (se utilizará para la conectividad SSH)

    Anote la dirección IP pública y privada de la instancia.

Tarea 1.2: Instalación de Docker

  1. Conéctese a la instancia mediante SSH.

    $ ssh opc@<public-ip-address-of-the-instance>
    
  2. Instale el motor de Docker, containerd y 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. Vuelva a conectar la sesión SSH y valide la instalación correcta de 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.
    

Tarea 1.3: Generar clave secreta de cliente

La clave secreta de cliente es necesaria para acceder a OCI Object Storage mediante la API compatible con S3.

  1. Navegue a la página de perfil de usuario en la consola de OCI y seleccione Claves secretas de cliente.

  2. Genere una nueva clave, copie el valor de la clave secreta y haga clic en Cerrar.

  3. Copie el valor de la clave de acceso (la segunda columna de esta lista con las claves secretas del cliente).

Tarea 1.4: Configurar e iniciar ScyllaDB

  1. Cree el directorio de despliegue s3-lock.

    mkdir s3-lock
    cd s3-lock
    
  2. Cree .env con el siguiente comando.

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

    Nota: El archivo .env lo utilizará la redacción de Docker para configurar ScyllaDB.

  3. Cree un archivo scylladb.Dockerfile en el directorio scylladb mediante el siguiente comando.

    FROM scylladb/scylla:latest
    RUN echo "alternator_enforce_authorization: true" >> /etc/scylla/scylla.yaml
    ENTRYPOINT ["/docker-entrypoint.py"]
    
  4. Cree el archivo docker-compose.yaml en el directorio s3-lock mediante el siguiente comando.

    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. Revise la estructura de directorios.

    $ tree -a .
    .
    ├── docker-compose.yaml
    ├── .env
    └── scylladb
        └── scylladb.Dockerfile
    
    1 directory, 3 files
    
  6. Inicie el servicio ScyllaDB.

    docker compose up -d
    
  7. Permitir conexiones entrantes al puerto 8000.

    sudo firewall-cmd --add-port 8000/tcp --permanent
    
  8. Valide la conexión a ScyllaDB.

    • Instale el paquete python3-pip y boto3.

      sudo yum install -y python3-pip
      python3 -m pip install --user boto3
      
    • Cree el archivo script.py con el siguiente comando.

      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"])
      
    • Ejecute el script con el comando siguiente.

      python3 script.py
      

    Si la ejecución del script devuelve s3-locking-demo ACTIVE, funciona como se esperaba.

Tarea 2: Configuración de OCI API Gateway para proteger las conexiones a la API compatible con ScyllaDB DynamoDB

En esta tarea, configuraremos OCI API Gateway para aprovechar el cifrado TLS entre los usuarios y ScyllaDB.

Tarea 2.1: Creación de una nueva instancia de API Gateway

  1. Vaya a la página Gateway de API en la consola de OCI y haga clic en Crear gateway.

    • Nombre: s3-locking

    • Tipo: public

    • Red: la misma VCN y subred que la instancia ScyllaDB

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

  2. Anote el nombre de host asociado al nuevo gateway de API creado. El nombre de host se muestra en el separador Información de gateway al hacer clic en el nuevo recurso de gateway de API creado.

    Por ejemplo: fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com

Tarea 2.2: Creación de un nuevo despliegue de API Gateway

  1. Crear un nuevo despliegue.

    1. Haga clic en el nuevo gateway de API creado y, en el menú de la izquierda, haga clic en Despliegues en Recursos.

    2. Haga clic en Crear despliegue y cree un nuevo despliegue con la siguiente información.

      • Información básica

        • Nombre: default

        • Prefijo de Ruta de Acceso: /

      • Autenticación: sin autenticación

      • Rutas

        • Ruta: /{requested_path*}

        • Métodos: ANY

        • Tipo de backend: HTTP

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

    3. Vaya a Políticas de solicitud de ruta, Transformaciones de cabecera y haga clic en Agregar.

      • Acción: Set

      • Comportamiento: Overwrite

      • Nombre de cabecera: Host

      • Valores: <API Gateway hostname> (por ejemplo: fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com)

    4. Revise los detalles del nuevo despliegue y haga clic en Crear.

  2. Configure la lista de seguridad de subred para permitir la conexión de entrada y salida al puerto 8000.

    1. Obtenga el bloque CIDR asignado a la subred en uso mediante los siguientes pasos.

    2. Identifique la lista de seguridad asociada a la subred que utiliza la instancia mediante los siguientes pasos.

    3. Haga clic en la lista de seguridad por defecto ya asociada a la subred y agregue las siguientes reglas.

      • Ingress

        • CIDR de origen: 0.0.0.0/0

        • Protocolo: TCP

        • Rango de puertos de destino: 443

        • Descripción: Ingress Access to the API Gateway

      • Ingress

        • Source CIDR: <subnet CIDR>

        • Protocolo: TCP

        • Destination Port Range: 8000

        • Descripción: Ingress connection to the ScyllaDB

      • Salida

        • Destination CIDR: <subnet CIDR>

        • Protocolo: TCP

        • Destination Port Range: 8000

        • Descripción: Egress connection from the API Gateway backend to ScyllaDB

Tarea 2.3: Validar la conexión a ScyllaDB mediante API Gateway

  1. Actualice endpoint_url en el archivo script.py para utilizar el nombre de host de API Gateway. Por ejemplo: endpoint_url = "https://fj4etyuvz3s57jdsadsadsadsa.apigateway.eu-frankfurt-1.oci.customer-oci.com"

  2. Ejecute el script para probar la conexión al punto final público.

    python3 script.py
    s3-locking-demo ACTIVE
    

Tarea 3: Probar el bloqueo de archivos terraform.tfstate al utilizar la API compatible con S3

Ejecutaremos los siguientes pasos en la instancia en la que queremos ejecutar el código terraform.

  1. Copie las siguientes líneas y cree el archivo 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. Configure el 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. Inicialice el directorio de trabajo con el 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. Ejecute 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. Pruebe el bloqueo de archivos terraform.tfstate. Si intenta ejecutar terraform plan o terraform apply durante la ejecución de la tarea 3.4, la solicitud se rechazará.

    $ 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. Gestione entradas en las tablas ScyllaDB mediante la API DynamoDB. Si necesita mostrar las entradas de la tabla DynamoDB o eliminar manualmente las entradas, puede agregar las siguientes líneas al final del archivo 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: Hay más funciones disponibles en este script.

Agradecimientos

Más recursos de aprendizaje

Explore otros laboratorios en docs.oracle.com/learn o acceda a más contenido de aprendizaje gratuito en el canal YouTube de Oracle Learning. Además, visite education.oracle.com/learning-explorer para convertirse en Oracle Learning Explorer.

Para obtener documentación sobre el producto, visite Oracle Help Center.