Creating AWS RDS Instances manually using kubectl (experimental)

This topic is for users who want to understand the underlying APIs involved in making a bindable service instance using DBInstance and SecretTemplate resources. For a simpler user experience, see Creating an RDS service instance through a Carvel Package.

Prerequisite

Meet the prerequisites in Consuming AWS RDS on Tanzu Application Platform (TAP) with AWS Controllers for Kubernetes (ACK) and keep the following information to hand:

  • DB-SUBNET-GROUP-NAME - the name of the DBSubnetGroup resource previously created
  • SECURITY-GROUP-ID - the security group ID to use for this RDS instance

Create an RDS service instance by using kubectl

Follow these procedures to create an RDS service instance by using kubectl.

Create the DBInstance resource

This example uses secret-gen to generate a Password for the DBInstance. You can also provide an explicit password through a Secret.

  1. Create Kubernetes resources on your EKS cluster by using the following example. This YAML creates the DBInstance resource in the default namespace.

    # dbinstance.yaml
    ---
    apiVersion: secretgen.k14s.io/v1alpha1
    kind: Password
    metadata:
     name: rds-psql-password
     namespace: default
    spec:
     length: 64
     secretTemplate:
       type: Opaque
       stringData:
         password: $(value) # do not edit, this will auto generate a password.
    ---
    apiVersion: rds.services.k8s.aws/v1alpha1
    kind: DBInstance
    metadata:
     name: rds-psql-1
     namespace: default
    spec:
     allocatedStorage: 20
     dbInstanceClass: db.t3.micro
     dbInstanceIdentifier: rds-psql-1
     dbName: postgres
     engine: postgres
     engineVersion: "14.1"
     masterUsername: adminUser
     masterUserPassword:
       namespace: default
       name: rds-psql-password
       key: password
     vpcSecurityGroupIDs:
     - SECURITY-GROUP-ID                     # modify value
     dbSubnetGroupName: DB-SUBNET-GROUP-NAME # modify value
    
     # note: due to an issue in the RDS ACK controller, it is recommended to explicitly set the
     # following optional spec fields.
     # default values for the optional fields are provided below.
     # https://github.com/aws-controllers-k8s/community/issues/1346
     autoMinorVersionUpgrade: true
     backupRetentionPeriod: 1
     copyTagsToSnapshot: false
     deletionProtection: false
     licenseModel: postgresql-license
     monitoringInterval: 0
     multiAZ: false
     preferredBackupWindow: 23:00-23:30
     preferredMaintenanceWindow: wed:23:34-thu:00:04
     publiclyAccessible: false
     storageEncrypted: false
     storageType: gp2
    

    Where:

    • DB-SUBNET-GROUP-NAME is the name of the DBSubnetGroup resource previously created
    • SECURITY-GROUP-ID is the security group ID to use for this RDS instance
  2. Run:

    kubectl apply -f dbinstance.yaml
    
  3. Verify the creation status of the DBInstance by inspecting the conditions in the Kubernetes API. To do so, run:

    kubectl get DBInstance rds-psql-1 -o yaml -n default
    

Create a Binding Specification Compatible Secret

As mentioned in Creating service instances that are compatible with Tanzu Application Platform, for Tanzu Application Platform workloads to be able to claim and bind to services such as RDS, a resource compatible with Service Binding Specification must exist in the cluster.

This can take the form of either a ProvisionedService or a Kubernetes Secret with some known keys. Both are defined in the specification.

The RDS DBInstance you created does not adhere to ProvisionedService and does not create a spec-compatible secret. So, you must create one using the resources you have available.

In this topic, you create a Kubernetes secret in the necessary format using the secret-gen tooling. You do so by using the SecretTemplate API to extract values from the DBInstance resource and populate a new spec-compatible secret with the values.

Create a ServiceAccount for secret templating

As part of using the SecretTemplate API, a Kubernetes ServiceAccount must be provided. The ServiceAccount is used for reading the DBInstance resource and the Secret created from the Password resource.

  1. Create the following Kubernetes resources on your EKS cluster:

    # secrettemplate-sa.yaml
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
     name: rds-resources-reader
     namespace: default
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
     name: rds-resources-reading
     namespace: default
    rules:
    - apiGroups:
     - ""
     resources:
     - secrets
     verbs:
     - get
     - list
     - watch
     resourceNames:
     - rds-psql-password
    - apiGroups:
     - rds.services.k8s.aws
     resources:
     - dbinstances
     verbs:
     - get
     - list
     - watch
     resourceNames:
     - rds-psql-1
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
     name: rds-resources-reader-to-read
     namespace: default
    roleRef:
     apiGroup: rbac.authorization.k8s.io
     kind: Role
     name: rds-resources-reading
    subjects:
     - kind: ServiceAccount
     name: rds-resources-reader
     namespace: default
    
  2. Run:

    kubectl apply -f secrettemplate-sa.yaml
    

Create a SecretTemplate

In combination with the ServiceAccount you created, a SecretTemplate can be used to declaratively create a secret that is compatible with the service binding specification.

The .spec.inputResources fields list the resources with information needed to create the secret. The .spec.template field defines how that information is interpolated as a secret.

To specify fields on an input resource, you can use JSONPath syntax that is very similar to kubectl syntax. The only difference is the delimiters, which are \$( and ) instead of { and }.

For example, $(.rds.status.endpoint.address) produces the host address of an RDS instance if the input resource is an ACK controller DBInstance resource.

This syntax can currently be used in the following fields of the SecretTemplate API:

  • .spec.inputResource[].ref.name for dynamically loading input resources of the APIs of input resources previously in the list
  • .spec.template values for taking values from the input resources and interpolating them into the secret you create

In this case, you directly reference the DBInstance resource and then dynamically load the secret containing the password from its specification.

You then create a Secret conforming to the Postgres auto-configuration for Spring Cloud Bindings to enable a fully automated, end-to-end binding experience for application workloads on Tanzu Application Platform.

  1. Create the following Kubernetes resources on your EKS cluster:

    # bindable-rds-secrettemplate.yaml
    ---
    apiVersion: secretgen.carvel.dev/v1alpha1
    kind: SecretTemplate
    metadata:
     name: rds-bindable
     namespace: default
    spec:
     serviceAccountName: rds-resources-reader
     inputResources:
     - name: rds
       ref:
         apiVersion: rds.services.k8s.aws/v1alpha1
         kind: DBInstance
         name: rds-psql-1
     - name: creds
       ref:
         apiVersion: v1
         kind: Secret
         name: "$(.rds.spec.masterUserPassword.name)"
    template:
     metadata:
       labels:
         app.kubernetes.io/component: rds-postgres
         app.kubernetes.io/instance: "$(.rds.metadata.name)"
         services.apps.tanzu.vmware.com/class: rds-postgres
     type: postgresql
     stringData:
       type: postgresql
       port: "$(.rds.status.endpoint.port)"
       database: "$(.rds.spec.dbName)"
       host: "$(.rds.status.endpoint.address)"
       username: "$(.rds.spec.masterUsername)"
     data:
       password: "$(.creds.data.password)"
    
  2. Run:

    kubectl apply -f bindable-rds-secrettemplate.yaml
    

Verify

Find the name of the secret produced by reading the status of SecretTemplate. To do so, run:

kubectl get secrettemplate -n default rds-bindable -o jsonpath="{.status.secret.name}"

Delete an RDS service instance

Delete an RDS service instance by running:

kubectl delete DBInstance rds-psql-1 -n default
kubectl delete SecretTemplate rds-bindable -n default
kubectl delete Password rds-psql-password -n default
kubectl delete ServiceAccount rds-resources-reader -n default
kubectl delete RoleBinding rds-resources-reader-to-read -n default
kubectl delete Role rds-resources-reading -n default

Summary and Next Steps

You learned how to use Carvel’s SecretTemplate API to construct a secret that is compatible with the binding specification to create an AWS RDS service instance.

Now that you have this available in the cluster, you can learn how to make use of it by continuing where you left off in Consuming AWS RDS on Tanzu Application Platform (TAP) with AWS Controllers for Kubernetes (ACK).

check-circle-line exclamation-circle-line close-line
Scroll to top icon