diff --git a/modules/ROOT/images/zdm-ansible-container-ls3.png b/modules/ROOT/images/zdm-ansible-container-ls3.png deleted file mode 100644 index 2fd59a1b..00000000 Binary files a/modules/ROOT/images/zdm-ansible-container-ls3.png and /dev/null differ diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index 6119675e..9cc4ed72 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -17,9 +17,8 @@ ** xref:ROOT:phase1.adoc[] ** xref:ROOT:setup-ansible-playbooks.adoc[] ** xref:ROOT:deploy-proxy-monitoring.adoc[] -** xref:ROOT:tls.adoc[] -** xref:ROOT:connect-clients-to-proxy.adoc[] ** xref:ROOT:metrics.adoc[] +** xref:ROOT:connect-clients-to-proxy.adoc[] ** xref:ROOT:manage-proxy-instances.adoc[] * xref:ROOT:migrate-and-validate-data.adoc[] * xref:ROOT:enable-async-dual-reads.adoc[] diff --git a/modules/ROOT/pages/connect-clients-to-proxy.adoc b/modules/ROOT/pages/connect-clients-to-proxy.adoc index b1af5465..985ef1a4 100644 --- a/modules/ROOT/pages/connect-clients-to-proxy.adoc +++ b/modules/ROOT/pages/connect-clients-to-proxy.adoc @@ -5,168 +5,163 @@ This means that your client applications connect to {product-proxy} in the same You can communicate with {product-proxy} using the same xref:cql:ROOT:index.adoc[CQL] statements used in your existing client applications. It understands the same messaging protocols used by {cass-short}, {dse-short}, {hcd-short}, and {astra-db}. +As a result, most client applications cannot distinguish between connections to {product-proxy} and direct connections to a {cass-short}-based cluster. -As a result, most client applications won't be able to distinguish between connections to {product-proxy} and direct connections to a {cass-short}-based cluster. +This page explains how to connect client applications to a {cass-short}-based cluster using {cass-short} drivers, and then describes the how you need to modify your client applications to connect to {product-proxy}. +It also explains how to connect `cqlsh` to {product-proxy}, which is often used in conjunction with {cass-short} drivers and during the migration process for certain validation tasks. -This page explains how to connect your client applications to a {cass-short}-based cluster, compares this process to connections to {product-proxy}, provides realistic examples of client applications that work effectively with {product-proxy}, and, finally, explains how to connect `cqlsh` to {product-proxy}. -You can use the provided sample client applications, in addition to your own, as a quick way to validate that the deployed {product-proxy} is reading and writing data from the expected origin and target clusters. +== Connect applications to {cass-short} clusters -== {company}-compatible drivers +A common way to connect client applications to {cass-short} clusters is with {cass-short} drivers. +These drivers can connect to {cass-short}-based clusters, execute queries, iterate through results, access metadata about the clusters, and perform other related activities. +For available drivers and driver documentation, see xref:datastax-drivers:compatibility:driver-matrix.adoc[]. -You can use {cass-short} drivers to connect your client applications to {cass-short}, {dse-short}, {hcd-short}, and {astra-db}. -With drivers, you can allow execute queries, iterate through results, access metadata about your cluster, and perform other related activities. +The following pseudocode provides simplified examples of the way a {cass-short} driver interacts with {cass-short} clusters. +Specifically, these examples are for {astra-db} and self-managed {dse-short}, {hcd-short}, and open-source {cass-short} clusters. -For available drivers and driver documentation, see xref:datastax-drivers:compatibility:driver-matrix.adoc[]. +This pseudocode is for illustration purposes only; the exact syntax depends on your driver language and version. +For specific instructions and examples, see xref:datastax-drivers:connecting:connect-cloud.adoc[]. -[[_connecting_company_drivers_to_cassandra]] -== Connect drivers to {cass-short} +[tabs] +====== +Self-managed {cass-short} clusters:: ++ +-- +[source,pseudocode] +---- +// Create an object to represent a Cassandra cluster +// This example listens for connections at 10.20.30.40 on the default port 9042 +// Username and password are required only if authentication is enabled on the cluster +Cluster my_cluster = Cluster.build_new_cluster( + contact_points = "10.20.30.40", + username="cluster_username", + password="cluster_password" +) + +// Connect the Cluster object to the Cassandra cluster, returning a Session +Session my_session = my_cluster.connect() -Perhaps the simplest way to demonstrate how to use the drivers to connect your client application to a {cass-short} cluster is an example in the form of some sample code. -But there's a bit of a problem: the drivers are independent projects implemented natively in the relevant programming language. +// Execute a query, returning a ResultSet +ResultSet my_result_set = my_session.execute("select release_version from system.local") + +// Retrieve a specific column from the first row of the result set +String release_version = my_result_set.first_row().get_column("release_version") -This approach offers the benefit of allowing each project to provide an API that makes the most sense for the language or platform on which it's implemented. -Unfortunately it also means there is some variation between languages. -With that in mind, the following pseudocode provides reasonable guidance for understanding how a client application might use one of the drivers. +// Close the Session and Cluster +my_session.close() +my_cluster.close() -[source] +// Print the data retrieved from the result set +print(release_version) ---- -// Create an object to represent a Cassandra cluster listening for connections at -// 10.20.30.40 on the default port (9042). The username and password are necessary -// only if your Cassandra cluster has authentication enabled. -Cluster my_cluster = Cluster.build_new_cluster(contact_points = "10.20.30.40", username="myusername", password="mypassword") +-- -// Connect our Cluster object to our Cassandra cluster, returning a Session +{astra-db}:: ++ +-- +[source,text] +---- +// Create an object to represent a Cassandra cluster (an Astra database) +// Don't specify contact points when connecting to Astra DB +// All connection information is implicitly passed in the SCB +Cluster my_cluster = Cluster.build_new_cluster( + username="token", + password="AstraCS:...", + secure_connect_bundle="/path/to/scb.zip" +) + +// For legacy applications that must use client ID and secret authentication: +// Cluster my_cluster = Cluster.build_new_cluster(username="my_AstraDB_client_ID", password="my_AstraDB_client_secret", secure_connect_bundle="/path/to/scb.zip") + +// Connect the Cluster object to the database, returning a Session Session my_session = my_cluster.connect() // Execute a query, returning a ResultSet ResultSet my_result_set = my_session.execute("select release_version from system.local") -// Retrieve the "release_version" column from the first row of our result set +// Retrieve a specific column from the first row of the result set String release_version = my_result_set.first_row().get_column("release_version") -// Close our Session and Cluster +// Close the Session and Cluster my_session.close() my_cluster.close() -// Display the release version to the user +// Print the data retrieved from the result set print(release_version) ---- +-- +====== -As noted, you'll see some differences in individual drivers: +Review your client application's code to understand how it connects to your existing {cass-short}-based clusters. +Then, proceed to <> to learn how to modify that code to connect to {product-proxy} instead. -* New versions of the Java driver no longer define a Cluster object. -Client programs create a Session directly. -* The Node.js driver has no notion of a Cluster or Session at all, instead using a Client object to represent this functionality. +[#connect-applications-to-zdm-proxy] +== Connect applications to {product-proxy} -The details may vary but you'll still see the same general pattern described in the pseudocode in each of the drivers. +By design, {product-proxy} should be transparent to your client application. +This means that connecting your client application to {product-proxy} should be seamless and nearly identical to the direct cluster connection. +However, there are some considerations and adjustments you might need to make for {product-proxy}. -This topic does not describe details or APIs for any of the drivers mentioned above. -All the drivers come with a complete set of documentation for exactly this task. -For driver documentation, see xref:datastax-drivers:ROOT:index.adoc[]. +=== Understand how {product-proxy} handles certain client behaviors -== Connect applications to {product-proxy} +If you haven't done so already, thoroughly review the xref:ROOT:feasibility-checklists.adoc[compatibility requirements for {product-proxy}]. +This ensures that your client application's code is compatible with {product-proxy}, and you understand how certain operations and behaviors are handled by {product-proxy}. +For example: + +* xref:ROOT:feasibility-checklists.adoc#driver-retry-policy-and-query-idempotence[Driver retry policy and query idempotence] +* xref:ROOT:feasibility-checklists.adoc#client-compression[Client compression] +* xref:ROOT:feasibility-checklists.adoc#zdm-proxy-ignores-token-aware-routing[Token-aware routing] -We mentioned above that connecting to a {product-proxy} should be almost indistinguishable from connecting directly to your {cass-short} cluster. -This design decision means there isn't much to say here; everything we discussed in the section above also applies when connecting your driver to {product-proxy}. -There are a few extra considerations to keep in mind, though, when using the proxy. +=== Set connection points -[[_client_application_credentials]] -=== Client application credentials +Connect your client application to {product-proxy} instead of directly to the origin cluster by modifying the client's connection points. -Client applications provide cluster credentials to authenticate requests. +Typically, this involves setting the client's connection points to the IP addresses or hostnames of the local {product-proxy} instances. +The specific connection points for your migration depend on where and how you configured your xref:ROOT:deployment-infrastructure.adoc[{product-proxy} infrastructure]. -Client applications connect to {product-proxy} in the same way that they connect to a cluster: by providing a set of credentials. +[#provide-authentication-credentials] +=== Provide authentication credentials -Clients have no awareness of the {product-proxy} architecture or the existence of the two separate clusters (the origin and target). -Therefore, a client only provides a single set of credentials when connecting to {product-proxy}, the same as it would when connecting directly to a cluster. +Make sure {product-proxy} receives the correct authentication credentials from your client application. +You might need to change the credentials set in your client application depending on your origin and target cluster configuration. -{product-proxy} uses the credentials provided by the client to forward requests to the cluster that corresponds with those credentials, which is usually the target cluster. -If necessary, {product-proxy} uses the credentials defined in `xref:ROOT:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[zdm_proxy_cluster_config.yml]` to forward requests to the other cluster, which is usually the origin cluster. +With or without {product-proxy}, client applications must provide cluster credentials to authenticate requests. +When a client connects directly to a cluster, it provides the authentication credentials for that cluster. +When a client connects to {product-proxy}, the client still provides only one set of credentials because {product-proxy} is designed to be invisible to the client application. + +{product-proxy} uses the credentials provided by the client to forward requests to one of the clusters. +To forward requests to the other cluster, {product-proxy} uses the credentials defined in `xref:ROOT:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[zdm_proxy_cluster_config.yml]`. .Credential usage by {product-proxy} when authentication is required for both clusters image::zdm-proxy-credential-usage.png[{product-proxy} credentials usage when authentication is required for both clusters, 550] -==== Determine which credentials to provide - -The credentials your client must provide depend on the authentication requirements of the origin and target clusters: +The credentials that {product-proxy} expects to receive from the client depend on the authentication requirements of the origin and target clusters. +Make sure the client passes the correct credentials for your cluster configuration: * *Authentication required for both clusters*: Your client application must supply credentials for the target cluster. * *Authentication required for target cluster only*: Your client application must supply credentials for the target cluster. * *Authentication required for origin cluster only*: Your client application must supply credentials for the origin cluster. * *No authentication required for either cluster*: Your client application doesn't need to supply any cluster credentials. -==== Expected authentication credentials for self-managed clusters - -For a self-managed clusters that require authentication, your client application must provide valid `username` and `password` values to access the cluster. - -For information about self-managed cluster credentials in your {product-proxy} configuration, see xref:ROOT:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[Cluster and core configuration]. - -[#expected-authentication-credentials-for-astra-db] -==== Expected authentication credentials for {astra-db} - -For {astra-db} databases, your client application can provide either application token credentials or a {scb}. - -[tabs] -====== -Application token:: -+ --- -With token-based authentication, the {product-proxy} automatically calls the {astra} DevOps API to download the {scb-short}. -The provided application token is used to authenticate this request. - -To use token-based authentication, xref:astra-db-serverless:administration:manage-application-tokens.adoc[generate an application token] with the *Organization Administrator* role. -This role is recommended to avoid permissions-related request failures during the migration. -After the migration, you can configure your client applications to use tokens with reduced permissions. - -The token has three keys: `clientId`, `secret`, and `token`. -Using these keys, you must specify one of the following sets of credentials in your {product-proxy} configuration: +After you determine which cluster's credentials are required, set the appropriate credentials in your client application: -* Token-only authentication (Recommended): +* **Self-managed clusters**: Provide a valid `username` and `password` to access the cluster. + -** Set `username` to the literal string `token`. -** Set `password` to the actual application token value (`AstraCS:...`). +Make sure the user has the appropriate permissions to perform the operations requested by the client application during the migration. -* Legacy authentication for older applications and drivers: -+ -** Set `username` to the `clientId` value generated with the token. -** Set `password` to the `secret` value generated with the token. --- - -{scb-short}:: -+ --- -To use {scb-short} authentication, xref:astra-db-serverless:databases:secure-connect-bundle.adoc[download your database's {scb-short}], and then provide the path to the {scb-short} zip file in your {product-proxy} configuration. --- -====== - -For information about setting {astra-db} credentials in your {product-proxy} configuration, see xref:ROOT:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[Cluster and core configuration]. - - -. xref:astra-db-serverless:administration:manage-application-tokens.adoc[Generate an application token] with the *Organization Administrator* role. - - -. Download your database's xref:astra-db-serverless:databases:secure-connect-bundle.adoc[{scb-short}]. +* **{astra-db}**: xref:astra-db-serverless:administration:manage-application-tokens.adoc[Generate an application token] with a role that has sufficient permissions to perform the operations requested by the client application during the migration. +Then, in your client application, set the `username` to the literal string `token`, and set the `password` to the actual application token value (`AstraCS:...`). + [IMPORTANT] ==== -The {scb-short} contains sensitive information that establishes a connection to your database, including key pairs and certificates. -Treat it as you would any other sensitive values, such as passwords or tokens. -==== - -=== Disable client-side compression with {product-proxy} +_Don't_ provide a xref:astra-db-serverless:databases:secure-connect-bundle.adoc[{scb}] when connecting your client application to {product-proxy}. -Client applications must not enable client-side compression when connecting through {product-proxy}, as this is not currently supported. -This is disabled by default in all drivers, but if it was enabled in your client application configuration, it will have to be temporarily disabled when connecting to {product-proxy}. - -=== {product-proxy} ignores token-aware routing - -Token-aware routing isn't enforced when connecting through {product-proxy} because these instances don't hold actual token ranges in the same way as database nodes. -Instead, each {product-proxy} instance has a unique, non-overlapping set of synthetic tokens that simulate token ownership and enable balanced load distribution across the instances. - -Upon receiving a request, a {product-proxy} instance routes the request to appropriate origin and target database nodes, independent of token ownership. +If you include the {scb-short}, the driver ignores the {product-proxy} connection points and connects exclusively to your {astra-db} database instead of {product-proxy}. +==== ++ +For connections to early {astra} databases with long-lived tokens created prior to the unified `token` approach, you can set the `username` to the token's `clientId` and the `password` to the token's `secret`. -If your clients have token-aware routing enabled, you don't need to disable this behavior while using {product-proxy}. -Clients can continue to operate with token-aware routing enabled without negative impacts to functionality or performance. +For more information about cluster credentials for {product-proxy}, see xref:ROOT:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[Cluster and core configuration]. == Sample client applications @@ -184,25 +179,24 @@ The following sample client applications demonstrate how to use the Java driver See your driver's documentation for code samples that are specific to your chosen driver, including cluster connection examples and statement execution examples. -=== {product-demo} +You can use the provided sample client applications, in addition to your own client applications, to validate that your {product-proxy} deployment is orchestrating read and write requests as expected between the origin cluster, target cluster, and your client applications. +{product-demo}:: https://github.com/alicel/zdm-demo-client/[{product-demo}] is a minimal Java web application which provides a simple, stripped-down example of an application built to work with {product-proxy}. After updating connection information you can compile and run the application locally and interact with it using HTTP clients such as `curl` or `wget`. - ++ You can find the details of building and running {product-demo} in the https://github.com/alicel/zdm-demo-client/blob/master/README.md[README]. [[_themis_client]] -=== Themis client - +Themis client:: https://github.com/absurdfarce/themis[Themis] is a Java command-line client application that allows you to write randomly generated data directly to the origin cluster, directly to the target cluster, or indirectly to both clusters through {product-proxy}. - Then, you can use the client application to query the data and confirm that {product-proxy} is reading and writing data from the expected sources. - ++ Configuration details for the clusters and {product-proxy} are defined in a YAML file. For more information, see the https://github.com/absurdfarce/themis/blob/main/README.md[Themis README]. - -In addition to any utility as a validation tool, Themis also serves as an example of a larger client application which uses the Java driver to connect to a {product-proxy} -- as well as directly to {cass-short} clusters or {astra-db} -- and perform operations. -The configuration logic as well as the cluster and session management code have been cleanly separated into distinct packages to make them easy to understand. ++ +In addition to being a validation tool, Themis also provides an example of a larger client application that uses the Java driver to connect to {product-proxy}, or directly to a {cass-short} cluster, and perform operations. +The configuration logic, as well as the cluster and session management code, are separated into distinct packages to make them easy to understand. == Connect cqlsh to {product-proxy} @@ -231,7 +225,7 @@ Replace the following: * `**ZDM_PROXY_IP**`: The IP address of your {product-proxy} instance. * `**PORT**`: The port on which the {product-proxy} instance listens for client connections. If you are using the default port, 9042, you can omit this argument. -* `**USERNAME**` and `**PASSWORD**`: Valid xref:_client_application_credentials[client connection credentials], depending on the authentication requirements for your origin and target clusters: +* `**USERNAME**` and `**PASSWORD**`: Valid cluster authentication credentials, depending on the authentication requirements for your origin and target clusters: + ** *Authentication required for both clusters*: Provide credentials for the target cluster. ** *Authentication required for target cluster only*: Provide credentials for the target cluster. @@ -239,13 +233,19 @@ If you are using the default port, 9042, you can omit this argument. ** *No authentication required for either cluster*: Omit the `-u` and `-p` arguments. + +-- +For a self-managed cluster, provide a valid `username` and `password` to access the cluster. + +For {astra-db}, the username is the literal string `token`, and the password is an xref:astra-db-serverless:administration:manage-application-tokens.adoc[{astra} application token], such as `-u token -p AstraCS:...`. + [IMPORTANT] ==== -If you need to provide credentials for an {astra-db} database, don't use the {scb-short} when attempting to connect `cqlsh` to {product-proxy}. -Instead, use the token-based authentication option explained in <>. +For {astra-db}, _don't_ use the {scb-short} to connect `cqlsh` to {product-proxy}. +Only use an application token. If you include the {scb-short}, `cqlsh` ignores all other connection arguments and connects exclusively to your {astra-db} database instead of {product-proxy}. ==== +-- == Next steps diff --git a/modules/ROOT/pages/connect-clients-to-target.adoc b/modules/ROOT/pages/connect-clients-to-target.adoc index c8d732e5..d4bd855e 100644 --- a/modules/ROOT/pages/connect-clients-to-target.adoc +++ b/modules/ROOT/pages/connect-clients-to-target.adoc @@ -20,7 +20,8 @@ Be sure that you have thoroughly xref:ROOT:migrate-and-validate-data.adoc[valida == Connect a driver to a generic CQL cluster -If your origin and target clusters are both generic CQL clusters ({cass-short}, {dse-short}, or {hcd-short}), then the driver connection strings can be similar, requiring only minor changes to connect to the target cluster. +If your origin and target clusters are both generic CQL clusters, such as {cass-short}, {dse-short}, or {hcd-short}, then the driver connection strings can be extremely similar. +It's possible that your code requires only minor changes to connect to the target cluster. . At minimum, update your driver configuration to use the appropriate contact points for your target cluster. + @@ -54,7 +55,7 @@ You must specify one of the following sets of credentials in your driver configu *** Set `username` to the literal string `token`. *** Set `password` to the actual application token value (`AstraCS:...`). + -** Legacy authentication for earlier drivers: +** Legacy authentication (Earlier drivers only): + *** Set `username` to the `clientId` value generated with the token. *** Set `password` to the `secret` value generated with the token. @@ -112,64 +113,118 @@ a| Required. * Client ID and secret authentication (Legacy): Set to the `secret` generated with your token. |=== -=== Verify driver compatibility and update connection strings +.Driver pseudocode comparison +[%collapsible] +==== +The two pseudocode examples provide a simplified comparison of the way a {cass-short} driver interacts with {astra-db} and self-managed {cass-short} clusters. +This pseudocode is for illustration purposes only; the exact syntax depends on your driver language and version. -Verify that your driver version is compatible with {astra-db} and the features that you want to use in {astra-db}, such as the vector data type. -For more information, see xref:datastax-drivers:compatibility:driver-matrix.adoc[]. +The first pseudocode example illustrates the connection to a self-managed {cass-short} cluster. +This should be somewhat familiar to you based on your current {cass-short} driver. -If your client application uses an earlier driver version without built-in {scb-short} support, {company} strongly recommends upgrading to a compatible driver to simplify configuration and get the latest features and bug fixes. -If you prefer to make this change after the migration, or you must support a legacy application that relies on an earlier driver, you can connect to {astra-db} with https://github.com/datastax/cql-proxy[CQL Proxy], or by extracting the {scb-short} archive and using the individual files to enable mTLS in your driver's configuration. +[source,pseudocode] +---- +// Create an object to represent a Cassandra cluster +// This example listens for connections at 10.20.30.40 on the default port 9042 +// Username and password are required only if authentication is enabled on the cluster +Cluster my_cluster = Cluster.build_new_cluster( + contact_points = "10.20.30.40", + username="cluster_username", + password="cluster_password" +) + +// Connect the Cluster object to the Cassandra cluster, returning a Session +Session my_session = my_cluster.connect() -If your driver has built-in support for the {astra-db} {scb-short}, the changes to enable your application to connect to {astra-db} are minimal. -The following example demonstrates an {astra-db} connection through the Python driver using an {scb-short} and application token. -For more information and examples, see <> and xref:datastax-drivers:connecting:connect-cloud.adoc[]. +// Execute a query, returning a ResultSet +ResultSet my_result_set = my_session.execute("select release_version from system.local") -[source,python] ----- -import os -from cassandra.cluster import Cluster -from cassandra.auth import PlainTextAuthProvider -import json +// Retrieve a specific column from the first row of the result set +String release_version = my_result_set.first_row().get_column("release_version") -cloud_config= { - 'secure_connect_bundle': '/path/to/scb.zip' - } -auth_provider = PlainTextAuthProvider("token", os.environ["APPLICATION_TOKEN"]) -cluster = Cluster(cloud=cloud_config, auth_provider=auth_provider) -session = cluster.connect() +// Close the Session and Cluster +my_session.close() +my_cluster.close() + +// Print the data retrieved from the result set +print(release_version) ---- -.Driver pseudocode to connect to {astra-db} -[%collapsible] -==== -The following pseudocode provides guidance on how you might change your driver's code to connect directly to {astra-db}. -This is for illustration purposes only; the exact syntax depends on your driver and programming language. +The second example modifies the same pseudocode to connect to {astra-db}. +The primary difference lies in the initial connection string. +After that point, the interaction through the root object (`Cluster`/`Session`) is typical. [source,text] ---- -// Create an object to represent a Cassandra cluster -// Note: there is no need to specify contact points when connecting to Astra DB. +// Create an object to represent a Cassandra cluster (an Astra database) +// Don't specify contact points when connecting to Astra DB // All connection information is implicitly passed in the SCB -Cluster my_cluster = Cluster.build_new_cluster(username="my_AstraDB_client_ID", password="my_AstraDB_client_secret", secure_connect_bundle="/path/to/scb.zip") - -// Connect our Cluster object to our Cassandra cluster, returning a Session +Cluster my_cluster = Cluster.build_new_cluster( + username="token", + password="AstraCS:...", + secure_connect_bundle="/path/to/scb.zip" +) + +// For legacy applications that must use client ID and secret authentication: +// Cluster my_cluster = Cluster.build_new_cluster( +// username="my_AstraDB_client_ID", +// password="my_AstraDB_client_secret", +// secure_connect_bundle="/path/to/scb.zip"] +// ) + +// Connect the Cluster object to the database, returning a Session Session my_session = my_cluster.connect() // Execute a query, returning a ResultSet ResultSet my_result_set = my_session.execute("select release_version from system.local") -// Retrieve the "release_version" column from the first row of our result set +// Retrieve a specific column from the first row of the result set String release_version = my_result_set.first_row().get_column("release_version") -// Close our Session and Cluster +// Close the Session and Cluster my_session.close() my_cluster.close() -// Display the release version to the user +// Print the data retrieved from the result set print(release_version) ---- ==== +=== Verify driver compatibility and update connection strings + +Verify that your driver version is compatible with {astra-db} and the features that you want to use in {astra-db}, such as the vector data type. +For more information, see xref:datastax-drivers:compatibility:driver-matrix.adoc[]. + +If your driver version is fully compatible with {astra-db}, then it has built-in support for the {astra-db} {scb-short}. +The changes required to connect your application to {astra-db} are minimal. + +If your client application uses an earlier driver version without built-in {scb-short} support, {company} strongly recommends upgrading to a compatible driver to simplify configuration and get the latest features and bug fixes. +If you prefer to make this change after the migration, or you must support a legacy application that relies on an earlier driver, you can connect to {astra-db} with https://github.com/datastax/cql-proxy[CQL Proxy], or you must extract the {scb-short} archive and provide the individual files to enable mTLS in your driver's configuration. + +The following example demonstrates how the {cass-short} Python driver connects to {astra-db} using an application token and {scb-short}: + +[source,python] +---- +import os +from cassandra.cluster import Cluster +from cassandra.auth import PlainTextAuthProvider +import json + +# Provide the path to the SCB +cloud_config= { + 'secure_connect_bundle': '/path/to/scb.zip' + } + +# The username is 'token' and the password is the application token value +auth_provider = PlainTextAuthProvider("token", os.environ["APPLICATION_TOKEN"]) + +# Create the Cluster and Session objects with Astra credentials +cluster = Cluster(cloud=cloud_config, auth_provider=auth_provider) +session = cluster.connect() +---- + +For more information and examples of {astra-db} connections, see <> and xref:datastax-drivers:connecting:connect-cloud.adoc[]. + === Other code changes for {astra-db} In addition to updating connection strings, you might also need to make the following code changes: diff --git a/modules/ROOT/pages/deploy-proxy-monitoring.adoc b/modules/ROOT/pages/deploy-proxy-monitoring.adoc index e8905d2e..44193388 100644 --- a/modules/ROOT/pages/deploy-proxy-monitoring.adoc +++ b/modules/ROOT/pages/deploy-proxy-monitoring.adoc @@ -1,69 +1,70 @@ -= Deploy {product-proxy} and monitoring += Deploy {product-proxy} +:page-aliases: ROOT:tls.adoc -This topic explains how to use the Ansible automation playbooks that you set up in the xref:setup-ansible-playbooks.adoc[prior topic] to deploy {product-proxy} and its monitoring stack. +After you xref:setup-ansible-playbooks.adoc[set up the jumphost and {product-automation}], use the Ansible playbooks to deploy your {product-proxy} instances. +Then, xref:ROOT:metrics.adoc[deploy the monitoring stack] to complete your {product-proxy} deployment. -Once completed, you will have a working and fully monitored {product-proxy} deployment. +Aside from xref:ROOT:deployment-infrastructure.adoc[preparing the infrastructure], you don't need to install any {product-proxy} dependencies on the {product-proxy} machines. The playbook automatically installs all required software packages. -== Prerequisites - -You must have completed the Ansible setup as described in the xref:setup-ansible-playbooks.adoc[prior topic]. - -No other prerequisites or dependencies are needed. The playbooks will automatically install all the required software packages as part of their operation. - -== Access the Ansible Control Host Docker container - -You can connect to the Ansible Control Host Docker container by opening a shell on it: +== Access the Control Host and locate the configuration files +. Connect to the Ansible Control Host Docker container. +You can do this from the jumphost machine by running the following command: ++ [source,bash] ---- docker exec -it zdm-ansible-container bash ---- - -You're now connected to the container, at a prompt such as this: - ++ +.Result +[%collapsible] +==== [source,bash] ---- ubuntu@52772568517c:~$ ---- +==== -You can `ls` to see the resources in the Ansible Control Host Docker container. The most important resource is the `zdm-proxy-automation`. +. List (`ls`) the contents of the Ansible Control Host Docker container, and then find the `zdm-proxy-automation` directory. -Now, `cd` into `zdm-proxy-automation/ansible` and `ls`. Example: +. Change (`cd`) to the `zdm-proxy-automation/ansible` directory. -image::zdm-ansible-container-ls3.png[Contents of the Ansible Control Host container] +. List the contents of the `ansible` directory, and then find the following YAML configuration files: ++ +* `zdm_proxy_container_config.yml`: Internal configuration for the proxy container itself. +* `zdm_proxy_cluster_config.yml`: Configuration properties to connect {product-proxy} to the origin and target clusters. +This is always required. +* `zdm_proxy_core_config.yml`: Important configuration properties that are commonly used and changed during the migration. +* `zdm_proxy_advanced_config.yml`: Advanced configuration properties that aren't always required. +Leave these to their default values unless you have a specific use case that requires changing them. +* `zdm_proxy_custom_tls_config.yml`: Optional TLS encryption properties. + +The following sections explain how to configure each of these files before deploying your {product-proxy} instances. [[_configure_the_zdm_proxy]] == Configure {product-proxy} -The {product-proxy} configuration is composed of five files: - -* `zdm_proxy_container_config.yml`: Contains the internal configuration of the proxy container itself. -* `zdm_proxy_cluster_config.yml`: Contains the configuration that allows the proxy to connect to origin and target clusters. -This is always required. -* `zdm_proxy_core_config.yml`: Contains important configuration that is commonly used and changed during the migration. -* `zdm_proxy_advanced_config.yml`: Contains advanced configuration that is required in some scenarios, but often left to the default values. -* `zdm_proxy_custom_tls_config.yml`: Configures TLS encryption, if needed. +After you locate the configuration files on the Control Host, you must prepare the configuration properties for your {product-proxy} deployment. === Container configuration -The first step of the proxy container configuration is to open the `zdm_proxy_container_config.yml` file. -Configure the desired {product-proxy} version and create a strategy to inject configuration parameters. -All versions of {product-proxy} support the ability to provide configuration parameters using environment variables. -Starting with {product-short} 2.3.0, you can inject the configuration with the YAML file generated from automation scripts. -=== Cluster and core configuration +. Edit the `zdm_proxy_container_config.yml` file. -The next step is to edit the `zdm_proxy_cluster_config.yml` file in the Docker container. -You will need to provide values like your {cass-short}/{dse-short} username, password, and other connection credentials. +. Set your {product-proxy} version. -. Get connection credentials for your origin and target clusters. +. Create a strategy to inject configuration parameters. + -* Self-managed clusters with authentication enabled: You need a valid username and password for the cluster. -* {astra-db} databases: xref:astra-db-serverless:administration:manage-application-tokens.adoc[Generate an application token] with a role that can read and write to your database, such as the *Database Administrator* role, and then store the token values (`clientId`, `secret`, and `token`) securely. - -. In the container shell, `cd` to `~/zdm-proxy-automation/ansible/vars` and edit `zdm_proxy_cluster_config.yml`. -The `vi` and `nano` text editors are available in the container. +In all versions of {product-proxy}, you can use environment variables to set the {product-proxy} configuration. + -.If you are on {product-automation} version 2.1.0 or earlier +In versions 2.3.0 and later, you can inject the configuration with a YAML file generated from automation scripts. + +. Save and close the `zdm_proxy_container_config.yml` file. + +=== Cluster and core configuration + +For the cluster and core configuration, you need to provide connection credentials and details for both the origin and target clusters. + +.{product-automation} version 2.1.0 or earlier [%collapsible] ==== Starting in version 2.2.0 of {product-automation}, all origin and target cluster configuration variables are stored in `zdm_proxy_cluster_config.yml`. @@ -71,53 +72,104 @@ In earlier versions, these variables are in the `zdm_proxy_core_config.yml` file This change is backward compatible. If you previously populated the variables in `zdm_proxy_core_config.yml`, these variables are honored and take precedence over any variables in `zdm_proxy_cluster_config.yml`, if both files are present. -However, consider updating your configuration to use the new file to take advantage of new features in later releases. +However, consider updating your configuration to use the new file and take advantage of new features in later releases. ==== +. Get connection credentials for your origin and target clusters. ++ +[tabs] +====== +Self-managed clusters:: ++ +-- +For self-managed clusters with authentication enabled, you need a valid username and password for the cluster. + +If authentication isn't enabled, no credentials are required. +-- + +{astra-db}:: ++ +-- +For {astra-db} databases, xref:astra-db-serverless:administration:manage-application-tokens.adoc[generate an application token] with a role that can read and write to your database, such as the *Database Administrator* role, and then store the token securely. + +At minimum, store the core token that is prefixed by `AstraCS:...`. + +For legacy authentication to earlier {astra-db} databases with an older token generated prior to the unified `AstraCS` token, you can use the `clientId` and `secret` instead of the core token. +-- +====== + +. Edit the `zdm_proxy_cluster_config.yml` file. +The `vi` and `nano` text editors are available in the container. + . In the `ORIGIN CONFIGURATION` and `TARGET CONFIGURATION` sections, uncomment and configure all variables that are required for {product-proxy} to connect to the origin and target clusters. + -The variables for the origin cluster are prefixed with `origin`, and the variables for the target cluster are prefixed with `target`. -You must provide connection details in both sections, otherwise {product-proxy} won't be able to connect to both clusters. -+ -* `*_username` and `*_password`: -** Self-managed cluster with authentication enabled: Provide a valid username and password to access the cluster. -** Self-managed cluster without authentication: Leave both values unset. -** {astra-db} token-only authentication: -*** Set `username` to the literal string `token`. -*** Set `password` to your {astra-db} application token value (`AstraCS:...`). -** {astra-db} legacy authentication for earlier drivers: -*** Set `username` to the `clientId` value generated with your {astra-db} application token. -*** Set `password` to the `secret` value generated with your {astra-db} application token. -* `*_contact_points`: -** For a self-managed cluster, provide a comma-separated list of IP addresses for the cluster's seed nodes. -** For an {astra-db} database, leave this unset. -* `*_port`: -** For a self-managed cluster, provide the port on which the cluster listens for client connections. -The default is 9042. -** For an {astra-db} database, leave this unset. -* `*_astra_secure_connect_bundle_path`, `*_astra_db_id`, and `*_astra_token`: -** For a self-managed cluster, leave all of these unset. -** For an {astra-db} database, provide either `*_astra_secure_connect_bundle_path` or both `*_astra_db_id` and `*_astra_token`. -*** If you want {product-automation} to automatically download your database's {scb}, use `*_astra_db_id` and `*_astra_token`. -Set `*_astra_db_id` to your xref:astra-db-serverless:databases:create-database.adoc#get-db-id[database's ID], and set `*_astra_token` to your application token, which is prefixed by `AstraCS:`. -*** If you want to manually upload your database's {scb-short} to the jumphost, use `*_astra_secure_connect_bundle_path`. -+ -.Manually upload the {scb-short} to the jumphost -[%collapsible] +[IMPORTANT] ==== -. xref:astra-db-serverless:databases:secure-connect-bundle.adoc[Download your database's {scb-short}]. -. Upload it to the jumphost. -. Open a new shell on the jumpost, and then run `docker cp /path/to/scb.zim zdm-ansible-container:/home/ubuntu` to copy the {scb-short} to the container. -. Set `*_astra_secure_connect_bundle_path` to the path to the {scb-short} on the jumphost. +You must provide connection details for both `ORIGIN CONFIGURATION` and `TARGET CONFIGURATION`. +If either set is missing, {product-proxy} cannot connect to both clusters. ==== -*** Make sure that you leave the unused credential unset. -For example, if you use `target_astra_db_id` and `target_astra_token`, leave `target_astra_secure_connect_bundle_path` unset. + -.Example: Cluster configuration variables +The variables are the same in both sections, but they are set separately for each cluster. +The origin cluster variables are prefixed with `origin`, and the target cluster variables are prefixed with `target`. +For example, `origin_username` and `target_username`. ++ +The expected values depend on the type of cluster (self-managed or {astra-db}). +For example, if your target cluster is an {astra-db} database, provide the {astra-db} connection details in the `TARGET CONFIGURATION` section. ++ +[tabs] +====== +Origin/target configuration for a self-managed cluster:: ++ +-- +The following configuration is required to connect to a self-managed cluster: + +* `*_username` and `*_password`: For a self-managed cluster with authentication enabled, provide a valid username and password to access the cluster. +If authentication isn't enabled, leave both variables unset. + +* `*_contact_points`: Provide a comma-separated list of IP addresses for the cluster's seed nodes. + +* `*_port`: Provide the port on which the cluster listens for client connections. +The default is 9042. + +* `*_astra_secure_connect_bundle_path`, `*_astra_db_id`, and `*_astra_token`: All of these must be unset. +-- + +Origin/target configuration for {astra-db}:: ++ +-- +The following configuration is required to connect to an {astra-db} database: + +* `*_username` and `*_password`: Set `username` to the literal string `token`, and set `password` to your {astra-db} application token (`AstraCS:...`). ++ +For legacy authentication to earlier {astra-db} databases with an older token generated prior to the unified `token` approach, set the `username` to the token's `clientId`, and set the `password` to the token's `secret`. + +* `*_contact_points`: Must be unset. + +* `*_port`: Must be unset. + +* `*_astra_secure_connect_bundle_path`, `*_astra_db_id`, and `*_astra_token`: Provide either `*_astra_secure_connect_bundle_path` only, or both `*_astra_db_id` and `*_astra_token`. ++ +** If you want {product-automation} to automatically download your database's {scb}, use `*_astra_db_id` and `*_astra_token`. +Set `*_astra_db_id` to your xref:astra-db-serverless:databases:create-database.adoc#get-db-id[database's ID], and set `*_astra_token` to your application token (`AstraCS:...`). +** If you want to manually provide database's {scb-short} to the jumphost, use `*_astra_secure_connect_bundle_path`, and manually upload the {scb-short} to the jumphost: ++ +.. xref:astra-db-serverless:databases:secure-connect-bundle.adoc[Download your database's {scb-short}]. +.. Upload it to the jumphost. +.. Open a new shell on the jumpost, and then run `docker cp /path/to/scb.zim zdm-ansible-container:/home/ubuntu` to copy the {scb-short} to the container. +.. Set `*_astra_secure_connect_bundle_path` to the path to the {scb-short} on the jumphost. + ++ +The unused option must be unset. +For example, if you use `target_astra_db_id` and `target_astra_token`, then `target_astra_secure_connect_bundle_path` must be unset. +-- +====== ++ +.Example: Cluster configuration [%collapsible] ==== -The following example `zdm_proxy_cluster_config.yml` file shows the configuration for a migration from a self-managed origin cluster to an {astra-db} target: +The following example shows the cluster configuration for a migration from a self-managed origin cluster to an {astra-db} target: +.zdm_proxy_cluster_config.yml [source,yml] ---- ############################## @@ -147,254 +199,355 @@ target_astra_token: "AstraCS:dUTGnRs...jeiKoIqyw:01...29dfb7" ---- ==== -. Save and close the file. +. Save and close the `zdm_proxy_cluster_config.yml` file. -. Open the `zdm_proxy_core_config.yml` file in the same directory. -This file contains some global variables that are used in subsequent steps during the migration. -Familiarize yourself with these values, but don't change any of them yet: +. Edit the `zdm_proxy_core_config.yml` file. ++ +This file contains global variables that are referenced and modified throughout the migration. +Take time to familiarize yourself with these values, but don't change any of them yet: + * `primary_cluster`: The cluster that serves as the primary source of truth for read requests during the migration. For the majority of the migration, leave this set to the default value of `ORIGIN`. ++ At the end of the migration, when you're preparing to switch over to the target cluster permanently, you can change it to `TARGET` after migrating all data from the origin cluster. -* `read_mode`: Leave this set to the default value of `PRIMARY_ONLY`. -For more information, see xref:enable-async-dual-reads.adoc[]. -* `log_level`: Leave this set to the default value of `INFO`. +* `read_mode`: Controls the xref:enable-async-dual-reads.adoc[asynchronous dual reads] feature. +Until you reach Phase 3, leave this set to the default value of `PRIMARY_ONLY`. +* `log_level`: You might need to xref:ROOT:troubleshooting-tips.adoc#proxy-logs[modify the log level] when troubleshooting issues. +Unless you are investigating an issue, leave this set to the default value of `INFO`. -=== Enable TLS encryption (optional) - -If you want to enable TLS encryption between the client application and {product-proxy}, or between {product-proxy} and one or both self-managed clusters, you will need to specify some additional configuration. -For instructions, see xref:ROOT:tls.adoc[]. +[IMPORTANT] +==== +The origin credentials, target credentials, and the `primary_cluster` variable are mutable variables that you can change after deploying {product-proxy}. +All other cluster connection configuration variables are immutable; the only way to change these values is by completely recreating the {product-proxy} deployment. +For more information, see xref:ROOT:manage-proxy-instances.adoc[]. +==== [[_advanced_configuration_optional]] -=== Advanced configuration (optional) +=== Advanced configuration -There are additional configuration variables in `vars/zdm_proxy_advanced_config.yml` that you might want to change _at deployment time_ in specific cases. - -All advanced configuration variables that aren't listed here are mutable and can be changed later without recreating the entire deployment. -For more information, see xref:manage-proxy-instances.adoc[]. +Typically the advanced configuration variables don't need to be changed. +Only modify the variables in `zdm_proxy_advanced_config.yml` if you have a specific use case that requires changing them. -==== Multi-datacenter clusters +If the following advanced configuration variables need to be changed, only do so _before_ deploying {product-proxy}: +Multi-datacenter clusters:: For xref:ROOT:deployment-infrastructure.adoc#multiple-datacenter-clusters[multi-datacenter origin clusters], specify the name of the datacenter that {product-proxy} should consider local. To do this, set the `origin_local_datacenter` property to the local datacenter name. Similarly, for multi-datacenter target clusters, set the `target_local_datacenter` property to the local datacenter name. -These two variables are stored in `vars/zdm_proxy_advanced_config.yml`. - +These two variables are stored in `zdm_proxy_advanced_config.yml`. ++ This configuration isn't necessary for multi-region {astra-db} databases, which specify the local datacenter through each region's specific {scb}. For information about downloading a region-specific {scb-short}, see xref:astra-db-serverless:databases:secure-connect-bundle.adoc[]. [#ports] -==== Ports - +Ports:: Each {product-proxy} instance listens on port 9042 by default, like a regular {cass-short} cluster. This can be overridden by setting `zdm_proxy_listen_port` to a different value. This can be useful if the origin nodes listen on a port that is not 9042 and you want to configure {product-proxy} to listen on that same port to avoid changing the port in your client application configuration. - ++ {product-proxy} exposes metrics on port 14001 by default. This port is used by Prometheus to scrape the application-level proxy metrics. This can be changed by setting `metrics_port` to a different value if desired. -== Use Ansible to deploy {product-proxy} +All other advanced configuration variables in `zdm_proxy_advanced_config.yml` are mutable. +You can seamlessly change them after deploying {product-proxy} with a rolling restart. +Immutable variables require you to recreate the entire deployment and result in downtime for your {product-proxy} deployment. +For more information, see xref:manage-proxy-instances.adoc[]. -Now you can run the playbook that you've configured above. -From the shell connected to the container, ensure that you are in `/home/ubuntu/zdm-proxy-automation/ansible` and run: +[#enable-tls-encryption] +=== Enable TLS encryption -[source,bash] ----- -ansible-playbook deploy_zdm_proxy.yml -i zdm_ansible_inventory ----- +Transportation Layer Security (TLS) encryption is optional and disabled by default. -That's it! A {product-proxy} container has been created on each proxy host. +{product-proxy} supports TLS encryption between {product-proxy} and either or both clusters, and between {product-proxy} and your client application. -[[_indications_of_success_on_origin_and_target_clusters]] -== Indications of success on the origin and target clusters +To enable TLS encryption, you must provide the necessary files and configure TLS settings in the `zdm_proxy_custom_tls_config.yml` file. -The playbook will create one {product-proxy} instance for each proxy host listed in the inventory file. -It will indicate the operations that it is performing and print out any errors, or a success confirmation message at the end. +[tabs] +====== +Proxy-to-cluster TLS:: ++ +-- +Use these steps to enable TLS encryption between {product-proxy} and one or both clusters if required. -Confirm that the {product-proxy} instances are up and running by using one of the following options: +Each cluster has its own TLS configuration. +One-way TLS and Mutual TLS (mTLS) are both supported, and they can be enabled as needed for each cluster's requirements. -* Call the `liveness` and `readiness` HTTP endpoints for the {product-proxy} instances. -* Check the {product-proxy} instances via docker logs. +In this case, {product-proxy} acts as the TLS client and the cluster acts as the TLS server. -=== Call the `liveness` and `readiness` HTTP endpoints +[TIP] +==== +This is required for self-managed clusters only. +For {astra-db}, {product-proxy} uses mTLS automatically with the xref:astra-db-serverless:databases:secure-connect-bundle.adoc[{scb-short}]. +==== -{product-short} metrics provide `/health/liveness` and `/health/readiness` HTTP endpoints, which you can call to determine the state of the {product-proxy} instances. -It's often fine to simply submit the `readiness` check to return the proxy's state. +. Find the required files for each cluster where you want to enable TLS encryption. +All files must be in plain-text, non-binary format. ++ +* **One-way TLS**: Find the server CA. +* **Mutual TLS**: Find the server CA, the client certificate, and the client key. -The format: ++ +If your client application and origin cluster already use TLS encryption, then the required files should already be used in the client application's configuration (TLS client files) and the origin cluster's configuration (TLS Server files). + +. For each self-managed cluster (origin or target) where you want to enable TLS encrypted {product-proxy} connections, do the following: ++ +.. If your TLS files are in a JKS keystore, you must extract them as plain text because {product-proxy} cannot accept a JKS keystore. +You must provide the raw files. ++ +... Get the files contained in your JKS keystore and their aliases: ++ +[source,bash,subs="+quotes"] +---- +keytool -list -keystore **PATH/TO/KEYSTORE.JKS** +---- ++ +Replace `**PATH/TO/KEYSTORE.JKS**` with the path to your JKS keystore. -[source,plaintext,subs="+quotes"] +... Extract each file from your JKS keystore: ++ +[source,bash,subs="+quotes"] ---- -http://**ZDM_PROXY_PRIVATE_IP**:**METRICS_PORT**/health/liveness -http://**ZDM_PROXY_PRIVATE_IP**:**METRICS_PORT**/health/readiness +keytool -exportcert -keystore **PATH/TO/KEYSTORE.JKS** -alias **FILE_ALIAS** -file **PATH/TO/DESTINATION/FILE** -rfc ---- ++ +Replace the following: ++ +** `**PATH/TO/KEYSTORE.JKS**`: The path to your JKS keystore +** `**FILE_ALIAS**`: The alias of the file you want to extract +** `**PATH/TO/DESTINATION/FILE**`: The path where you want to save the extracted file -Readiness expanded GET format: ++ +The `-rfc` option extracts the files in non-binary PEM format. +For more information, see the https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html[keytool syntax documentation]. -[source,bash] ++ +.. Upload the required TLS files to the jumphost: ++ +** **One-way TLS**: Upload the server CA. +** **mTLS**: Upload the server CA, the client certificate, and the client key. + +.. From a shell on the jumphost, copy each file to the `origin_tls_files` or `target_tls_files` directory in the Ansible Control Host container: ++ +** Copy origin files to the `origin_tls_files` directory, replacing `**TLS_FILE**` with the path to each required files: ++ +[source,bash,subs="+quotes"] ---- -curl -G "http://{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:{{ metrics_port }}/health/readiness" +docker cp **TLS_FILE** zdm-ansible-container:/home/ubuntu/origin_tls_files ---- -The default port for metrics collection is `14001`. -You can override this port if you deploy {product-proxy} with `metrics_port` set to a non-default port. -For more information, see <>. +** Copy target files to the `target_tls_files` directory, replacing `**TLS_FILE**` with the path to each required files: ++ +[source,bash,subs="+quotes"] +---- +docker cp **TLS_FILE** zdm-ansible-container:/home/ubuntu/target_tls_files +---- -Readiness example: +.. If you want to enable TLS encryption for both clusters, make sure that you complete the previous steps for both clusters, copying the appropriate files to the appropriate directory for each cluster. +. Open a shell to the Ansible Control Host container if you don't already have one: ++ [source,bash] ---- -curl -G "http://172.18.10.40:14001/health/readiness" +docker exec -it zdm-ansible-container bash ---- -.Result -[%collapsible] +. From this shell, edit the `zdm_proxy_tls_config.yml` file at `zdm-proxy-automation/ansible/vars/zdm_proxy_custom_tls_config.yml`. + +. Uncomment and populate the TLS configuration variables for the clusters where you want to enable TLS encryption. +For example, if you want to enable TLS encryption for both clusters, configure both sets of variables: `origin_tls_{asterisk}` and `target_tls_{asterisk}`. ++ +In the proxy-to-cluster configuration, the word `server` in the variable names refers to the cluster, which acts as the TLS server, and the word `client` refers to {product-proxy}, which acts as the TLS client. ++ +[tabs] ==== -[source,json] ----- -{ - "OriginStatus":{ - "Addr":"", - "CurrentFailureCount":0, - "FailureCountThreshold":1, - "Status":"UP" - }, - "TargetStatus":{ - "Addr":"", - "CurrentFailureCount":0, - "FailureCountThreshold":1, - "Status":"UP" - }, - "Status":"UP" -} ----- +Origin cluster TLS encryption variables:: ++ +* `origin_tls_user_dir_path`: Use the default value of `/home/ubuntu/origin_tls_files`. +* `origin_tls_server_ca_filename`: Required. Provide the filename (without the path) of the server CA. +* `origin_tls_client_cert_filename`: Required for mTLS only. Provide the filename (without the path) of the client cert. +Must be unset for one-way TLS. +* `origin_tls_client_key_filename`: Required for mTLS only. Provide the filename (without the path) of the client key. +Must be unset for one-way TLS. + +Target cluster TLS encryption variables:: ++ +* `target_tls_user_dir_path`: Use the default value of `/home/ubuntu/target_tls_files`. +* `target_tls_server_ca_filename`: Required. Provide the filename (without the path) of the server CA. +* `target_tls_client_cert_filename`: Required for mTLS only. Provide the filename (without the path) of the client cert. +Must be unset for one-way TLS. +* `target_tls_client_key_filename`: Required for mTLS only. Provide the filename (without the path) of the client key. +Must be unset for one-way TLS. ==== +-- -=== Check {product-proxy} instances via docker logs +Client application-to-proxy TLS:: ++ +-- +Use these steps to enable TLS encryption between your client application and {product-proxy} if required. -After running the playbook, you can `ssh` into one of the servers where one of the deployed {product-proxy} instances is running. -You can do so from within the Ansible container, or directly from the jumphost machine: +In this case, your client application is the TLS client, and {product-proxy} is the TLS server. -[source,bash] ----- -ssh @ ----- +One-way TLS and Mutual TLS (mTLS) are both supported. -Then, use the `docker logs` command to view the logs of this {product-proxy} instance. +. Get the server CA, server certificate, and server key files. +All files must be in plain-text, non-binary format. ++ +If your client application and origin cluster already use TLS encryption, then the required files should already be used in the client application's configuration (TLS client files) and the origin cluster's configuration (TLS Server files). -[source,bash] +. If your TLS files are in a JKS keystore, extract them as plain text. ++ +{product-proxy} cannot accept a JKS keystore. +You must provide the raw files. ++ +.. Get the files contained in your JKS keystore and their aliases: ++ +[source,bash,subs="+quotes"] ---- - . - . - . -ubuntu@ip-172-18-10-111:~$ docker logs zdm-proxy-container - . - . - . -time="2023-01-13T22:21:42Z" level=info msg="Initialized origin control connection. Cluster Name: OriginCluster, Hosts: map[3025c4ad-7d6a-4398-b56e-87d33509581d:Host{addr: 191.100.20.61, -port: 9042, host_id: 3025c4ad7d6a4398b56e87d33509581d} 7a6293f7-5cc6-4b37-9952-88a4b15d59f8:Host{addr: 191.100.20.85, port: 9042, host_id: 7a6293f75cc64b37995288a4b15d59f8} 997856cd-0406-45d1-8127-4598508487ed:Host{addr: 191.100.20.93, port: 9042, host_id: 997856cd040645d181274598508487ed}], Assigned Hosts: [Host{addr: 191.100.20.61, port: 9042, host_id: 3025c4ad7d6a4398b56e87d33509581d}]." +keytool -list -keystore **PATH/TO/KEYSTORE.JKS** +---- ++ +Replace `**PATH/TO/KEYSTORE.JKS**` with the path to your JKS keystore. -time="2023-01-13T22:21:42Z" level=info msg="Initialized target control connection. Cluster Name: cndb, Hosts: map[69732713-3945-4cfe-a5ee-0a84c7377eaa:Host{addr: 10.0.79.213, -port: 9042, host_id: 6973271339454cfea5ee0a84c7377eaa} 6ec35bc3-4ff4-4740-a16c-03496b74f822:Host{addr: 10.0.86.211, port: 9042, host_id: 6ec35bc34ff44740a16c03496b74f822} 93ded666-501a-4f2c-b77c-179c02a89b5e:Host{addr: 10.0.52.85, port: 9042, host_id: 93ded666501a4f2cb77c179c02a89b5e}], Assigned Hosts: [Host{addr: 10.0.52.85, port: 9042, host_id: 93ded666501a4f2cb77c179c02a89b5e}]." -time="2023-01-13T22:21:42Z" level=info msg="Proxy connected and ready to accept queries on 172.18.10.111:9042" -time="2023-01-13T22:21:42Z" level=info msg="Proxy started. Waiting for SIGINT/SIGTERM to shutdown." +.. Extract each file from your JKS keystore: ++ +[source,bash,subs="+quotes"] ---- +keytool -exportcert -keystore **PATH/TO/KEYSTORE.JKS** -alias **FILE_ALIAS** -file **PATH/TO/DESTINATION/FILE** -rfc +---- ++ +Replace the following: ++ +** `**PATH/TO/KEYSTORE.JKS**`: The path to your JKS keystore +** `**FILE_ALIAS**`: The alias of the file you want to extract +** `**PATH/TO/DESTINATION/FILE**`: The path where you want to save the extracted file -In the logs, the important information to notice is: ++ +The `-rfc` option extracts the files in non-binary PEM format. +For more information, see the https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html[keytool syntax documentation]. -[source,bash] +. Upload the files to the jumphost. + +. From a shell on the jumphost, copy each file to the `zdm_proxy_tls_files` directory in the Ansible Control Host container: ++ +[source,bash,subs="+quotes"] ---- -time="2023-01-13T22:21:42Z" level=info msg="Proxy connected and ready to accept queries on 172.18.10.111:9042" -time="2023-01-13T22:21:42Z" level=info msg="Proxy started. Waiting for SIGINT/SIGTERM to shutdown." +docker cp **TLS_FILE** zdm-ansible-container:/home/ubuntu/zdm_proxy_tls_files ---- ++ +Replace `**TLS_FILE**` with the path to each of your TLS files. -Also, you can check the status of the running Docker image. -Here's an example with {product-proxy} 2.1.0: - +. Open a shell to the Ansible Control Host container if you don't already have one: ++ [source,bash] ---- -ubuntu@ip-172-18-10-111:~$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -02470bbc1338 datastax/zdm-proxy:2.1.x "/main" 2 hours ago Up 2 hours zdm-proxy-container +docker exec -it zdm-ansible-container bash ---- -If the {product-proxy} instances fail to start up due to mistakes in the configuration, you can simply rectify the incorrect configuration values and run the deployment playbook again. +. From this shell, edit the `zdm_proxy_tls_config.yml` file at `zdm-proxy-automation/ansible/vars/zdm_proxy_custom_tls_config.yml`. -[IMPORTANT] -==== -The origin credentials, target credentials, and the `primary_cluster` variable are mutable variables that you can change after deploying {product-proxy}. -All other cluster connection configuration variables are immutable; the only way to change these values is by completely recreating the {product-proxy} deployment. -For more information, see xref:ROOT:manage-proxy-instances.adoc[]. -==== - -[[_setting_up_the_monitoring_stack]] -== Setting up the Monitoring stack +. Uncomment and populate the following TLS configuration variables, which are prefixed with `zdm_proxy_*`. +The word `server` in the variable names refers to {product-proxy}, which acts as the TLS server in this configuration. ++ +* `zdm_proxy_tls_user_dir_path_name`: Use the default value of `/home/ubuntu/zdm_proxy_tls_files`. +* `zdm_proxy_tls_server_ca_filename`: Required. Provide the filename (without the path) of the server CA that the proxy must use. +* `zdm_proxy_tls_server_cert_filename`: Required. Provide the filename (without the path) of the server certificate that the proxy must use. +* `zdm_proxy_tls_server_key_filename` : Required. Provide the filename (without the path) of the server key that the proxy must use. +* `zdm_proxy_tls_require_client_auth`: Set to `false` (default) for one-way TLS between the application and proxy. +Set to `true` to enable mTLS between the application and the proxy. +-- +====== -{product-automation} enables you to easily set up a self-contained monitoring stack that is preconfigured to collect metrics from your {product-proxy} instances and display them in ready-to-use Grafana dashboards. +When you deploy the {product-proxy} instances with {product-automation}, the deployment playbook automatically distributes the TLS files and applies the TLS configuration to all {product-proxy} instances. +If you want to enable TLS after the initial deployment, you must rerun the deployment playbook to redeploy the instances with the new TLS configuration. -The monitoring stack is deployed entirely on Docker. -It includes the following components, all deployed as Docker containers: +[#run-the-deployment-playbook] +== Run the deployment playbook -* Prometheus node exporter, which runs on each {product-proxy} host and makes OS- and host-level metrics available to Prometheus. -* Prometheus server, to collect metrics from {product-proxy}, its Golang runtime, and the Prometheus node exporter. -* Grafana, to visualize all these metrics in three preconfigured dashboards (see xref:ROOT:metrics.adoc[]). +After modifying all necessary configuration variables, you are ready to deploy your {product-proxy} instances. -After running the playbook described here, you will have a fully configured monitoring stack connected to your {product-proxy} deployment. +. From your shell connected to the Control Host, make sure you are in the `ansible` directory at `/home/ubuntu/zdm-proxy-automation/ansible`. -[NOTE] -==== -There are no additional prerequisites or dependencies for this playbook to execute. -If it is not already present, Docker will automatically be installed by the playbook on your chosen monitoring server. -==== +. Run the deployment playbook: ++ +[source,bash] +---- +ansible-playbook deploy_zdm_proxy.yml -i zdm_ansible_inventory +---- -=== Connect to the Ansible Control Host +. Wait while {product-proxy} containers are deployed to each of your {product-proxy} machines. ++ +The playbook creates one {product-proxy} instance for each proxy host listed in the inventory file. +While the playbook runs, activity is printed to the shell along with any errors. +If the entire operation is successful, the final output is a confirmation message. -Make sure you are connected to the Ansible Control Host docker container. -As above, you can do so from the jumphost machine by running: +. Confirm that the {product-proxy} instances are running by checking the Docker logs. ++ +Alternatively, after you xref:ROOT:metrics.adoc[deploy the monitoring stack], you can xref:ROOT:metrics.adoc#call-the-liveliness-and-readiness-endpoints[call the `liveliness` and `readiness` endpoints] to confirm that each {product-proxy} instance is running. ++ +To check the Docker logs, do the following: ++ +.. SSH into one of the servers where a deployed {product-proxy} instance is running. +You can do this from within the Ansible Control Host Docker container, or directly from the jumphost machine: ++ +[source,bash,subs="+quotes"] +---- +ssh **USER**@**ZDM_PROXY_IP_ADDRESS** +---- +.. Use the `docker logs` command to view the logs for the {product-proxy} instance: ++ [source,bash] ---- -docker exec -it zdm-ansible-container bash +docker logs zdm-proxy-container +---- ++ +.Result +[%collapsible] +==== +[source,console] ---- +time="2023-01-13T22:21:42Z" level=info msg="Initialized origin control connection. Cluster Name: OriginCluster, Hosts: map[3025c4ad-7d6a-4398-b56e-87d33509581d:Host{addr: 191.100.20.61, +port: 9042, host_id: 3025c4ad7d6a4398b56e87d33509581d} 7a6293f7-5cc6-4b37-9952-88a4b15d59f8:Host{addr: 191.100.20.85, port: 9042, host_id: 7a6293f75cc64b37995288a4b15d59f8} 997856cd-0406-45d1-8127-4598508487ed:Host{addr: 191.100.20.93, port: 9042, host_id: 997856cd040645d181274598508487ed}], Assigned Hosts: [Host{addr: 191.100.20.61, port: 9042, host_id: 3025c4ad7d6a4398b56e87d33509581d}]." -You will see a prompt like: +time="2023-01-13T22:21:42Z" level=info msg="Initialized target control connection. Cluster Name: cndb, Hosts: map[69732713-3945-4cfe-a5ee-0a84c7377eaa:Host{addr: 10.0.79.213, +port: 9042, host_id: 6973271339454cfea5ee0a84c7377eaa} 6ec35bc3-4ff4-4740-a16c-03496b74f822:Host{addr: 10.0.86.211, port: 9042, host_id: 6ec35bc34ff44740a16c03496b74f822} 93ded666-501a-4f2c-b77c-179c02a89b5e:Host{addr: 10.0.52.85, port: 9042, host_id: 93ded666501a4f2cb77c179c02a89b5e}], Assigned Hosts: [Host{addr: 10.0.52.85, port: 9042, host_id: 93ded666501a4f2cb77c179c02a89b5e}]." +time="2023-01-13T22:21:42Z" level=info msg="Proxy connected and ready to accept queries on 172.18.10.111:9042" +time="2023-01-13T22:21:42Z" level=info msg="Proxy started. Waiting for SIGINT/SIGTERM to shutdown." +---- +==== +.. In the logs, look for messages containing `Proxy connected` and `Proxy started`: ++ [source,bash] ---- -ubuntu@52772568517c:~$ +time="2023-01-13T22:21:42Z" level=info msg="Proxy connected and ready to accept queries on 172.18.10.111:9042" +time="2023-01-13T22:21:42Z" level=info msg="Proxy started. Waiting for SIGINT/SIGTERM to shutdown." ---- -=== Configure the Grafana credentials - -Edit the file `zdm_monitoring_config.yml`, stored at `zdm-proxy-automation/ansible/vars`: - -* `grafana_admin_user`: leave unchanged (defaults to `admin`) -* `grafana_admin_password`: set to the password of your choice - -=== Run the monitoring playbook - -Ensure that you are in `/home/ubuntu/zdm-proxy-automation/ansible` and then run the following command: - +. Optional: Check the status of the running Docker image. ++ [source,bash] ---- -ansible-playbook deploy_zdm_monitoring.yml -i zdm_ansible_inventory +docker ps ---- ++ +.Result +[%collapsible] +==== +[source,console] +---- +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +02470bbc1338 datastax/zdm-proxy:2.1.x "/main" 2 hours ago Up 2 hours zdm-proxy-container +---- +==== -=== Check the Grafana dashboard - -In a browser, open \http://:3000 - -Login with: +== Troubleshoot deployment issues -* *username*: admin -* *password*: the password you configured +If the {product-proxy} instances fail to start due to errors in the configuration, edit the configuration files and then rerun the deployment playbook. -[TIP] -==== -Details about the metrics you can observe are available in xref:ROOT:metrics.adoc[]. -==== +For specific troubleshooting scenarios, see xref:ROOT:troubleshooting-tips.adoc[]. == Next steps -To continue Phase 1 of the migration, xref:ROOT:connect-clients-to-proxy.adoc[connect your client applications to {product-proxy}]. \ No newline at end of file +To continue Phase 1 of the migration, xref:ROOT:metrics.adoc[deploy the {product-proxy} monitoring stack]. \ No newline at end of file diff --git a/modules/ROOT/pages/faqs.adoc b/modules/ROOT/pages/faqs.adoc index 1f3507ff..50e81836 100644 --- a/modules/ROOT/pages/faqs.adoc +++ b/modules/ROOT/pages/faqs.adoc @@ -118,7 +118,7 @@ Yes, see `https://github.com/datastax/zdm-proxy/blob/main/CONTRIBUTING.md[CONTRI == Does {product-proxy} support Transport Layer Security (TLS)? -Yes, see xref:tls.adoc[]. +Yes, see xref:ROOT:deploy-proxy-monitoring.adoc#enable-tls-encryption[Enable TLS encryption]. == How does {product-proxy} handle Lightweight Transactions (LWTs)? diff --git a/modules/ROOT/pages/feasibility-checklists.adoc b/modules/ROOT/pages/feasibility-checklists.adoc index 24f1ee74..4f33d3a3 100644 --- a/modules/ROOT/pages/feasibility-checklists.adoc +++ b/modules/ROOT/pages/feasibility-checklists.adoc @@ -79,16 +79,6 @@ It is your responsibility to determine whether your workloads can tolerate these [[_lightweight_transactions_and_the_applied_flag]] === Lightweight Transactions and the applied flag -//TODO: Align with the write request language on components.adoc - -//// -The ZDM proxy can bifurcate Lightweight Transactions to the ORIGIN and TARGET clusters. -However, it only returns the applied flag from one cluster, whichever cluster is the source of truth. -Given that there are two separate clusters involved, the state of each cluster may be different. -For conditional writes, this may create a divergent state for a time. -It may not make a difference in many cases, but if Lightweight Transactions are used, we would recommend a reconciliation phase in the migration before switching reads to rely on the TARGET cluster. -//// - {product-proxy} handles LWTs in the same way as other xref:ROOT:components.adoc#how-zdm-proxy-handles-reads-and-writes[write requests]: It sends the request to both clusters concurrently, and then it waits for both to respond. {product-proxy} returns a `success` status to the client _only_ if both clusters send successful acknowledgements; otherwise, it returns a `failure` status. @@ -167,6 +157,7 @@ Most drivers treat all statements as non-idempotent by default, and they don't a This means that you must explicitly mark statements as idempotent to trigger retries after connection errors. For more information, see xref:datastax-drivers:developing:query-idempotence.adoc[] and xref:datastax-drivers:connecting:retry-policies.adoc[]. +[#client-compression] == Client compression The binary protocol used by {astra}, {dse-short}, {hcd-short}, and open-source {cass-short} supports optional compression of transport-level requests and responses that reduces network traffic at the cost of CPU overhead. @@ -178,6 +169,17 @@ The compression algorithm configured in your {company}-compatible driver must ma This isn't related to storage compression, which you can configure on specific tables with the `compression` table property. Storage/table compression doesn't affect the client application or {product-proxy} in any way. +[#zdm-proxy-ignores-token-aware-routing] +== {product-proxy} ignores token-aware routing + +Token-aware routing isn't enforced when connecting through {product-proxy} because these instances don't hold actual token ranges in the same way as database nodes. +Instead, each {product-proxy} instance has a unique, non-overlapping set of synthetic tokens that simulate token ownership and enable balanced load distribution across the instances. + +Upon receiving a request, a {product-proxy} instance routes the request to appropriate origin and target database nodes, independent of token ownership. + +If your clients have token-aware routing enabled, you don't need to disable this behavior while using {product-proxy}. +Clients can continue to operate with token-aware routing enabled without negative impacts to functionality or performance. + == Authenticator and authorizer configuration A cluster's _authorizer_ doesn't affect client applications or {product-proxy}, which means that you can use any kind of authorizer configuration on your clusters, and they can use different authorizers. diff --git a/modules/ROOT/pages/manage-proxy-instances.adoc b/modules/ROOT/pages/manage-proxy-instances.adoc index e132e1c8..e6ed30a7 100644 --- a/modules/ROOT/pages/manage-proxy-instances.adoc +++ b/modules/ROOT/pages/manage-proxy-instances.adoc @@ -62,7 +62,7 @@ The playbook performs the following actions automatically: . {product-automation} stops one container gracefully, and then waits for it to shut down. . {product-automation} recreates the container, and then starts it. -. {product-automation} calls the xref:deploy-proxy-monitoring.adoc#_indications_of_success_on_origin_and_target_clusters[readiness endpoint] to check the container's status: +. {product-automation} calls the xref:ROOT:metrics.adoc#call-the-liveliness-and-readiness-endpoints[readiness endpoint] to check the container's status: + * If the status check fails, {product-automation} repeats the check up to six times at 5-second intervals. If all six attempts fail, {product-automation} interrupts the entire rolling restart process. @@ -211,7 +211,7 @@ If `true`, the credentials from the client application were used to connect to t + This deprecated variable is no longer functional. Instead, the expected credentials are based on the authentication requirements of the origin and target clusters. -For more information, see xref:ROOT:connect-clients-to-proxy.adoc#_client_application_credentials[Client application credentials]. +For more information, see xref:ROOT:connect-clients-to-proxy.adoc#connect-applications-to-zdm-proxy[Connect applications to {product-proxy}]. == Change immutable configuration variables @@ -370,5 +370,4 @@ Additionally, if you need to restart {product-proxy} instances, and there is onl == See also -* xref:ROOT:troubleshooting-tips.adoc[] -* xref:deploy-proxy-monitoring.adoc#_indications_of_success_on_origin_and_target_clusters[Indications of success on origin and target clusters] \ No newline at end of file +* xref:ROOT:troubleshooting-tips.adoc[] \ No newline at end of file diff --git a/modules/ROOT/pages/metrics.adoc b/modules/ROOT/pages/metrics.adoc index e7b9784d..c11d4071 100644 --- a/modules/ROOT/pages/metrics.adoc +++ b/modules/ROOT/pages/metrics.adoc @@ -1,91 +1,255 @@ -= Leverage metrics provided by {product-proxy} += Monitor {product-proxy} -This topic provides detailed information about the metrics captured by {product-proxy} and explains how to interpret the metrics. +{product-proxy} can gather a large number of metrics that provide you with insight into its health and operations, communication with client applications and clusters, and request handling. -== Benefits +This visibility is important to your migration. +It builds confidence in the health of your deployment, and it helps you investigate errors or performance degradation. -{product-proxy} gathers a large number of metrics, which allows you to gain deep insights into how it is operating with regard to its communication with client applications and clusters, as well as its request handling. +{company} strongly recommends that you use {product-automation} to deploy the {product-proxy} monitoring stack, or import the pre-built Grafana dashboards into your own monitoring infrastructure. -Having visibility on all aspects of {product-proxy}'s behavior is extremely important in the context of a migration of critical client applications, and is a great help in building confidence in the process and troubleshooting any issues. -For this reason, we strongly encourage you to monitor {product-proxy}, either by deploying the self-contained monitoring stack provided by {product-automation} or by importing the pre-built Grafana dashboards in your own monitoring infrastructure. +Do this after you xref:ROOT:setup-ansible-playbooks.adoc[set up {product-automation}] and xref:ROOT:deploy-proxy-monitoring.adoc[deploy the {product-proxy} instances]. -== Retrieving the {product-proxy} metrics +== Deploy the monitoring stack -{product-proxy} exposes an HTTP endpoint that returns metrics in the Prometheus format. +{product-automation} enables you to easily set up a self-contained monitoring stack that is preconfigured to collect metrics from your {product-proxy} instances and display them in ready-to-use Grafana dashboards. + +The monitoring stack is deployed entirely on Docker. +It includes the following components, all deployed as Docker containers: + +* Prometheus node exporter, which runs on each {product-proxy} host and makes OS- and host-level metrics available to Prometheus. +* Prometheus server, to collect metrics from {product-proxy}, its Golang runtime, and the Prometheus node exporter. +* Grafana, to visualize the metrics in preconfigured dashboards. + +After running the monitoring playbook, you will have a fully configured monitoring stack connected to your {product-proxy} deployment. + +Aside from xref:ROOT:deployment-infrastructure.adoc[preparing the infrastructure], you don't need to install any {product-proxy} dependencies on the monitoring host machine. The playbook automatically installs all required software packages. + +. Connect to the Ansible Control Host Docker container. +You can do this from the jumphost machine by running the following command: ++ +[source,bash] +---- +docker exec -it zdm-ansible-container bash +---- ++ +.Result +[%collapsible] +==== +[source,bash] +---- +ubuntu@52772568517c:~$ +---- +==== + +. To configure the Grafana credentials, edit the `zdm_monitoring_config.yml` file that is located at `zdm-proxy-automation/ansible/vars`: ++ +* `grafana_admin_user`: Leave unset or as the default value `admin`. +If unset, it defaults to `admin`. +* `grafana_admin_password`: Provide a password. +Make note of this password because you need it later. + +. Optional: Use the `metrics_port` variable to change the xref:ROOT:deploy-proxy-monitoring.adoc#ports[metrics collection port]. +The default port for metrics collection is `14001`. + +. Edit any other configuration settings as needed. ++ +[IMPORTANT] +==== +These steps assume you will deploy the monitoring stack on the jumphost machine. +If you plan to deploy the monitoring stack on another machine, you must set the configuration accordingly so the playbook runs against the correct machine from the Ansible Control Host. +==== + +. Make sure you are in the `ansible` directory at `/home/ubuntu/zdm-proxy-automation/ansible`, and then run the monitoring playbook: ++ +[source,bash] +---- +ansible-playbook deploy_zdm_monitoring.yml -i zdm_ansible_inventory +---- + +. Wait while the playbook runs. + +. To verify that the stack is running, check the Grafana dashboard at `http://**MONITORING_HOST_PUBLIC_IP**:3000`. ++ +If you deployed the monitoring stack on the jumphost machine, replace `**MONITORING_HOST_PUBLIC_IP**` with the public IP address or hostname of the jumphost. ++ +If you deployed your monitoring stack on another machine, replace `**MONITORING_HOST_PUBLIC_IP**` with the public IP address or hostname of that machine. + +. Login with your Grafana `username` and `password`. + +[#call-the-liveliness-and-readiness-endpoints] +== Call the `liveness` and `readiness` HTTP endpoints + +{product-short} metrics provide `/health/liveness` and `/health/readiness` HTTP endpoints, that you can call to determine the state of the {product-proxy} instances. + +For example, after deploying the {product-proxy} monitoring stack, you can use the `liveness` and `readiness` HTTP endpoints to confirm that your {product-proxy} instances are running. + +[tabs] +====== +Liveliness endpoint:: ++ +-- +[source,plaintext,subs="+quotes"] +---- +http://**ZDM_PROXY_PRIVATE_IP**:**METRICS_PORT**/health/liveness +---- + +Replace the following: + +* `**METRICS_PORT**`: The `metrics_port` you set in the `zdm_monitoring_config.yml` file. +The default port is 14001. + +* `**ZDM_PROXY_PRIVATE_IP**`: The private IP address, hostname, or other valid identifier for the {product-proxy} instance you want to check. + +Example request with variables: + +[source,bash] +---- +curl -G "http://{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:{{ metrics_port }}/health/liveliness" +---- -{product-automation} can deploy Prometheus and Grafana, configuring them automatically, as explained xref:deploy-proxy-monitoring.adoc#_setting_up_the_monitoring_stack[here]. -The Grafana dashboards are ready to go with metrics that are being scraped from the {product-proxy} instances. +Example request with plaintext values: + +[source,bash] +---- +curl -G "http://172.18.10.40:14001/health/liveliness" +---- + +-- + +Readiness endpoint:: ++ +-- +[source,plaintext,subs="+quotes"] +---- +http://**ZDM_PROXY_PRIVATE_IP**:**METRICS_PORT**/health/readiness +---- + +Replace the following: + +* `**METRICS_PORT**`: The `metrics_port` you set in the `zdm_monitoring_config.yml` file. +The default port is 14001. + +* `**ZDM_PROXY_PRIVATE_IP**`: The private IP address, hostname, or other valid identifier for the {product-proxy} instance you want to check. + +Example request with variables: + +[source,bash] +---- +curl -G "http://{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:{{ metrics_port }}/health/readiness" +---- + +Example request with plaintext values: + +[source,bash] +---- +curl -G "http://172.18.10.40:14001/health/readiness" +---- + +Example result: + +[source,json] +---- +{ + "OriginStatus":{ + "Addr":"", + "CurrentFailureCount":0, + "FailureCountThreshold":1, + "Status":"UP" + }, + "TargetStatus":{ + "Addr":"", + "CurrentFailureCount":0, + "FailureCountThreshold":1, + "Status":"UP" + }, + "Status":"UP" +} +---- + +-- +====== + +== Inspect {product-proxy} metrics + +{product-proxy} exposes an HTTP endpoint that returns metrics in the Prometheus format. -If you already have a Grafana deployment then you can import the dashboards from the two {product-short} dashboard files from this {product-automation-repo}/tree/main/grafana-dashboards[{product-automation} GitHub location]. +After you deploy the monitoring stack, the {product-proxy} Grafana dashboards automatically start displaying these metrics as they are scraped from the instances. -== Grafana dashboard for {product-proxy} metrics +If you already have a Grafana deployment, then you can import the dashboards from the {product-automation-repo}/tree/main/grafana-dashboards[{product-short} dashboard files] in the {product-short} GitHub repository. -There are three groups of metrics in this dashboard: +=== Grafana dashboard for {product-proxy} metrics -* Proxy level metrics -* Node level metrics -* Asynchronous read requests metrics +The {product-proxy} metrics dashboard includes proxy level metrics, node level metrics, and asynchronous read requests metrics. image::zdm-grafana-proxy-dashboard1.png[Grafana dashboard shows three categories of {product-short} metrics for the proxy.] +This dashboard can help you identify issues such as the following: + +* If the number of client connections is near 1000 per {product-proxy} instance, be aware that {product-proxy} will start rejecting client connections after accepting 1000 connections. +* If Prepared Statement cache metrics are always increasing, check the **entries** and **misses** metrics. +* If you have error metrics reporting for specific error types, evaluate these issues on a case-by-case basis. + [#proxy-level-metrics] -=== Proxy-level metrics +==== Proxy-level metrics -* Latency +* **Latency**: + -** Read Latency: Total latency measured by {product-proxy} per read request, including post-processing, such as response aggregation. +** **Read Latency**: Total latency measured by {product-proxy} per read request, including post-processing, such as response aggregation. This metric has two labels: `reads_origin` and `reads_target`. The label that has data depends on which cluster is receiving the reads, which is the current xref:ROOT:faqs.adoc#what-are-origin-target-primary-and-secondary-clusters[primary cluster]. -** Write Latency: Total latency measured by {product-proxy} per write request, including post-processing, such as response aggregation. +** **Write Latency**: Total latency measured by {product-proxy} per write request, including post-processing, such as response aggregation. This metric is measured as the total latency across both clusters for a single xref:ROOT:components.adoc#how-zdm-proxy-handles-reads-and-writes[bifurcated write request]. -* Throughput (same structure as the previous latency metrics): -** Read Throughput -** Write Throughput +* **Throughput**: Follows the same structure as the latency metrics but measures **Read Throughput** and **Write Throughput**. -* In-flight requests +* **In-flight requests** -* Number of client connections +* **Number of client connections** -* Prepared Statement cache: -** Cache Misses: If a prepared statement is sent to {product-proxy} but the statement's `preparedID` isn't present in the node's cache, then {product-proxy} sends an `UNPREPARED` response to the client to reprepare the statement. +* **Prepared Statement cache**: ++ +** **Cache Misses**: If a prepared statement is sent to {product-proxy} but the statement's `preparedID` isn't present in the node's cache, then {product-proxy} sends an `UNPREPARED` response to the client to reprepare the statement. This metric tracks the number of times this happens. -** Number of cached prepared statements. +** **Number of cached prepared statements** -* Request Failure Rates: the number of request failures per interval. -You can set the interval in the `Error Rate interval` dashboard variable at the top. -** Connect Failure Rate: one `cluster` label with two settings, `origin` and `target`, which represent the cluster to which the connection attempt failed. -** Read Failure Rate: one `cluster` label with two settings, `origin` and `target`. +* **Request Failure Rates**: The number of request failures per interval. +You can set the interval with the **Error Rate interval** dashboard setting. ++ +** **Connect Failure Rate**: One `cluster` label with two settings (`origin` and `target`) that represent the cluster to which the connection attempt failed. +** **Read Failure Rate**: One `cluster` label with two settings (`origin` and `target`). The label that contains data depends on which cluster is currently considered the primary, the same as the latency and throughput metrics explained above. -** Write Failure Rate: one `failed_on` label with three settings, `origin`, `target`, and `both`. -*** `failed_on=origin`: the write request failed on the origin only. -*** `failed_on=target`: the write request failed on the target only. -*** `failed_on=both`: the write request failed on both the origin and target clusters. +** **Write Failure Rate**: One `failed_on` label with three settings, `origin`, `target`, and `both`: ++ +*** `failed_on=origin`: The write request failed on the origin only. +*** `failed_on=target`: The write request failed on the target only. +*** `failed_on=both`: The write request failed on both the origin and target clusters. -* Request Failure Counters: Number of total request failures (resets when the {product-proxy} instance is restarted) -** Connect Failure Counters: the same labels as the connect failure rate. -** Read Failure Counters: the same labels as the read failure rate. -** Write Failure Counters: the same labels as the write failure rate. +* **Request Failure Counters**: Number of total request failures. +Resets if the {product-proxy} instance restarts. ++ +** **Connect Failure Counters**: Has the same labels as the connect failure rate. +** **Read Failure Counters**: Has the same labels as the read failure rate. +** **Write Failure Counters**: Has the same labels as the write failure rate. -To see error metrics by error type, see the node-level error metrics on the next section. +For error metrics by error type, see the <<_node_level_metrics,node-level error metrics>>. [[_node_level_metrics]] -=== Node-level metrics +==== Node-level metrics -* Latency: Node-level latency metrics report combined read and write latency per cluster, not per request. +* **Latency**: Node-level latency metrics report combined read and write latency per cluster, not per request. For latency by request type, see <>. + -** Origin: Latency, as measured by {product-proxy}, up to the point that it received a response from the origin connection. -** Target: latency, as measured by {product-proxy}, up to the point it received a response from the target connection. +** **Origin**: Latency, as measured by {product-proxy}, up to the point that it received a response from the origin connection. +** **Target**: Latency, as measured by {product-proxy}, up to the point it received a response from the target connection. -* Throughput: same as node level latency metrics, reads and writes are mixed together. +* **Throughput**: Same as the node-level latency metrics. +Reads and writes are combined. -* Number of connections per origin node and per target node. +* **Number of connections per origin node and per target node** -* Number of Used Stream Ids: -** Tracks the total number of used xref:manage-proxy-instances.adoc#zdm_proxy_max_stream_ids[stream ids] ("request ids") per connection type (`Origin`, `Target`, and `Async`). +* **Number of Used Stream Ids**: Tracks the total number of used xref:manage-proxy-instances.adoc#zdm_proxy_max_stream_ids[stream IDs] (request IDs) per connection type (`Origin`, `Target`, and `Async`). -* Number of errors per error type per origin node and per target node. +* **Number of errors per error type per origin node and per target node**: Possible values for the `error` type label: + ** `error=client_timeout` @@ -98,39 +262,39 @@ Possible values for the `error` type label: ** `error=unprepared` [[_asynchronous_read_requests_metrics]] -=== Asynchronous read requests metrics +==== Asynchronous read requests metrics -These metrics are only recorded if you xref:ROOT:enable-async-dual-reads.adoc[enable asynchronous dual reads]. +These metrics are recorded only if you xref:ROOT:enable-async-dual-reads.adoc[enable asynchronous dual reads]. These metrics track the following information for asynchronous read requests: -* Latency -* Throughput -* Number of dedicated connections per node for the cluster receiving the asynchronous read requests -* Number of errors per node, separated by error type +* *Latency* +* *Throughput* +* *Number of dedicated connections per node for the cluster receiving the asynchronous read requests* +* *Number of errors per node, separated by error type* -=== Insights from the {product-proxy} metrics +=== Go runtime metrics dashboard -Some examples of problems manifesting on these metrics: +The Go runtime metrics dashboard is used less often than the {product-proxy} metrics dashboard. -* Number of client connections close to 1000 per {product-proxy} instance: by default, {product-proxy} starts rejecting client connections after having accepted 1000 of them. -* Always increasing Prepared Statement cache metrics: both the **entries** and **misses** metrics. -* Error metrics depending on the error type: these need to be evaluated on a per-case basis. - -== Go runtime metrics dashboard and system dashboard - -This dashboard in Grafana is not as important as the {product-proxy} dashboard. However, it may be useful to troubleshoot performance issues. -Here you can see memory usage, Garbage Collection (GC) duration, open fds (file descriptors - useful to detect leaked connections), and the number of goroutines: +This dashboard can be helpful for troubleshooting {product-proxy} performance issues. +It provides metrics for memory usage, Garbage Collection (GC) duration, open FDs (file descriptors), and the number of goroutines. image::zdm-golang-dashboard.png[Golang metrics dashboard example is shown.] -Some examples of problem areas on these Go runtime metrics: +Watch for the following problem areas on the Go runtime metrics dashboard: + +* An always increasing **open fds** metric; this can indicate leaked connections. +* GC latencies that are frequently near or above hundreds of milliseconds. +* Persistently increasing memory usage. +* Persistently increasing number of goroutines. -* An always increasing “open fds” metric. -* GC latencies in (or close to) the triple digits of milliseconds frequently. -* Always increasing memory usage. -* Always increasing number of goroutines. +=== System-level metrics dashboard -The {product-short} monitoring stack also includes a system-level dashboard collected through the Prometheus Node Exporter. +The {product-short} monitoring stack's system-level dashboard metrics are collected through the Prometheus Node Exporter. This dashboard contains hardware and OS-level metrics for the host on which the proxy runs. This can be useful to check the available resources and identify low-level bottlenecks or issues. + +== Next steps + +To continue Phase 1 of the migration, xref:ROOT:connect-clients-to-proxy.adoc[connect your client applications to {product-proxy}]. \ No newline at end of file diff --git a/modules/ROOT/pages/phase1.adoc b/modules/ROOT/pages/phase1.adoc index ab913321..88335137 100644 --- a/modules/ROOT/pages/phase1.adoc +++ b/modules/ROOT/pages/phase1.adoc @@ -7,8 +7,7 @@ image::migration-phase1ra.png[In migration Phase 1, you deploy {product-proxy} i To complete Phase 1, do the following: . xref:setup-ansible-playbooks.adoc[]. -. xref:deploy-proxy-monitoring.adoc[] with optional xref:tls.adoc[TLS]. +. xref:deploy-proxy-monitoring.adoc[]. +. Deploy the xref:metrics.adoc[{product-proxy} monitoring stack] and learn about {product-proxy} metrics. . xref:connect-clients-to-proxy.adoc[]. - -During the migration you will modify {product-proxy} configuration settings and monitor your {product-proxy} instances. -Before you proceed to Phase 2, make sure you understand how to xref:manage-proxy-instances.adoc[manage {product-proxy} instances] and xref:metrics.adoc[use {product-proxy} metrics]. \ No newline at end of file +. Learn how to xref:manage-proxy-instances.adoc[manage {product-proxy} instances], including scaling, upgrading, reconfiguring, and restarting them. \ No newline at end of file diff --git a/modules/ROOT/pages/setup-ansible-playbooks.adoc b/modules/ROOT/pages/setup-ansible-playbooks.adoc index 905b6bab..7961af8b 100644 --- a/modules/ROOT/pages/setup-ansible-playbooks.adoc +++ b/modules/ROOT/pages/setup-ansible-playbooks.adoc @@ -3,7 +3,7 @@ {product-automation} uses Ansible to deploy and configure the {product-proxy} instances and the associated monitoring stack. Specifically, these configuration processes are defined in Ansible playbooks that you execute from the Ansible Control Host container. -First, you must use {product-utility} to set up the Ansible Control Host container, and then you can xref:deploy-proxy-monitoring.adoc[use {product-automation} to deploy your {product-proxy} instances and the monitoring stack]. +First, you must use {product-utility} to set up the Ansible Control Host container, and then you can xref:deploy-proxy-monitoring.adoc[use {product-automation} to deploy your {product-proxy} instances] and the xref:ROOT:metrics.adoc[monitoring stack]. After you complete both of these procedures, you will have an active and fully monitored {product-proxy} deployment. {product-utility} is a Golang (Go) executable program that runs anywhere. @@ -235,4 +235,4 @@ image::zdm-go-utility-success3.png[Ansible Docker container success messages] == Next steps -After you use {product-utility} to set up the Ansible Control Host container, you can xref:deploy-proxy-monitoring.adoc[use {product-automation} to deploy your {product-proxy} instances and the monitoring stack]. \ No newline at end of file +After you use {product-utility} to set up the Ansible Control Host container, xref:deploy-proxy-monitoring.adoc[use {product-automation} to deploy your {product-proxy} instances]. \ No newline at end of file diff --git a/modules/ROOT/pages/tls.adoc b/modules/ROOT/pages/tls.adoc deleted file mode 100644 index 84e99ecf..00000000 --- a/modules/ROOT/pages/tls.adoc +++ /dev/null @@ -1,162 +0,0 @@ -= Configure Transport Layer Security (TLS) -:navtitle: Configure Transport Layer Security - -{product-proxy} supports proxy-to-cluster and application-to-proxy TLS encryption. - -The TLS configuration is an optional part of the initial {product-proxy} configuration, which includes xref:setup-ansible-playbooks.adoc[] and xref:deploy-proxy-monitoring.adoc[]. - -You can enable TLS between {product-proxy} and any cluster that requires it. -You can also enable it between your client application and {product-proxy}, if required. - -* Proxy-to-cluster TLS can be configured between {product-proxy} and either or both the origin and target clusters, as needed. -Each set of configurations is independent of the other. -+ -When using proxy-to-cluster TLS, {product-proxy} acts as the TLS client, and the cluster acts as the TLS server. -One-way TLS and Mutual TLS are both supported, and they can be enabled as needed for each cluster's requirements. - -* When using application-to-proxy TLS, your client application is the TLS client, and {product-proxy} is the TLS server. -One-way TLS and Mutual TLS are both supported. - -* When {product-proxy} connects to {astra-db}, it always implicitly uses Mutual TLS. -This is done through the xref:astra-db-serverless:databases:secure-connect-bundle.adoc[{scb}] and does not require any extra configuration. - -[[_retrieving_files_from_a_jks_keystore]] -== Retrieving files from a JKS keystore - -If you are already using TLS between your client application and the origin cluster, then the files needed to configure TLS will already be used in the client application's configuration (TLS client files) and the origin's configuration (TLS Server files). -In some cases, these files may be contained in a JKS keystore. - -{product-proxy} does not accept a JKS keystore, requiring the raw files instead. - -To view the files contained in a JKS keystore and their aliases: - -[source,bash] ----- -keytool -list -keystore ----- - -To extract a file from a JKS keystore: - ----- -keytool -exportcert -keystore -alias -file -rfc ----- - -The `-rfc` option extracts the files in non-binary PEM format. - -For more details, see the https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html[keytool syntax documentation]. - -== Proxy to self-managed cluster TLS - -Here's how to configure TLS between {product-proxy} and a self-managed cluster ({cass} or {dse-short}). -In this case {product-proxy} acts as the TLS client and the cluster acts as the TLS server. - -The files required to configure proxy-to-cluster TLS are: - -* Server CA: needed for one-way and Mutual TLS -* Client certificate: only needed for Mutual TLS -* Client key: only needed for Mutual TLS - -[TIP] -==== -If your origin cluster requires TLS, your client application will already be using these files in its configuration to connect to it. -==== - -[NOTE] -==== -All files must be in plain-text, non-binary format. -==== - -=== Prepare the TLS files and copy them to the Ansible Control Host container - -For each self-managed origin or target cluster that requires TLS, do the following: - -. If your TLS files are in a JKS keystore, extract them as plain text (see xref:tls.adoc#_retrieving_files_from_a_jks_keystore[]). - -. Upload the following files to the jumphost: -+ -* For one-way TLS, uploda only the server CA. -* For Mutual TLS, upload the server CA, the client cert, and the client key. - -. From a shell on the jumphost, copy the files to the relevant TLS directory into the Ansible Control Host container: -+ -* For origin clusters, run: `docker cp zdm-ansible-container:/home/ubuntu/origin_tls_files` -* For target clusters, run: `docker cp zdm-ansible-container:/home/ubuntu/target_tls_files` - -=== Configure TLS - -There are separate TLS configuration variables for origin and target clusters so that you can configure these independently, if needed. - -. Open a shell to the container: -+ -[source,bash] ----- -docker exec -it zdm-ansible-container bash ----- - -. Find the custom TLS configuration file at `zdm-proxy-automation/ansible/vars/zdm_proxy_custom_tls_config.yml`. - -. Uncomment and set the following variables in the custom TLS configuration file for the proxy-to-origin TLS configuration: -+ -* `origin_tls_user_dir_path`: uncomment and leave to its preset value of `/home/ubuntu/origin_tls_files`. -* `origin_tls_server_ca_filename`: filename (without path) of the Server CA. -* `origin_tls_client_cert_filename`: filename (without path) of the Client cert. This is for Mutual TLS only, leave unset otherwise. -* `origin_tls_client_key_filename`: filename (without path) of the Client key. -For Mutual TLS only, leave unset otherwise. - -. Uncomment and set the variables for the proxy-to-target TLS configuration: -+ -* `target_tls_user_dir_path`: uncomment and leave to its preset value of `/home/ubuntu/target_tls_files`. -* `target_tls_server_ca_filename`: filename (without path) of the Server CA. -* `target_tls_client_cert_filename`: filename (without path) of the Client cert. -This is for Mutual TLS only, leave unset otherwise. -* `target_tls_client_key_filename`: filename (without path) of the Client key. -For Mutual TLS only, leave unset otherwise. - -== Application-to-proxy TLS - -Here are the steps to enable TLS between your client application and {product-proxy}, if required. -In this case, your client application is the TLS client and {product-proxy} is the TLS server. - -The files required by the proxy to configure application-to-proxy TLS are: - -* Server CA -* Server certificate -* Server key - -All these files are required for one-way and Mutual TLS. - -[TIP] -==== -If your origin cluster currently requires TLS, it will already be using these files for its own TLS configuration. - -All files must be in plain-text, non-binary format. -==== - -Here are the steps to configure application-to-proxy TLS: - -* If your TLS files are in a JKS keystore, extract them as plain text (see xref:tls.adoc#_retrieving_files_from_a_jks_keystore[]). -* Upload the required files to the jumphost: Server CA, Server certificate and Server key. -* From a shell on the jumphost, copy the files to the `zdm_proxy_tls_files` TLS directory into the Ansible Control Host container: `docker cp zdm-ansible-container:/home/ubuntu/zdm_proxy_tls_files`. -* Ensure that you have a shell open to the container. -If you do not, you can open it with `docker exec -it zdm-ansible-container bash`. -* From this shell, edit the file `zdm-proxy-automation/ansible/vars/zdm_proxy_custom_tls_config.yml`, uncommenting and populating the relevant configuration variables. -These are in the bottom section of `vars/proxy_custom_tls_config_input.yml` and are all prefixed with `zdm_proxy`: -** `zdm_proxy_tls_user_dir_path_name`: uncomment and leave to its preset value of `/home/ubuntu/zdm_proxy_tls_files`. -** `zdm_proxy_tls_server_ca_filename`: filename (without path) of the server CA that the proxy must use. -Always required. -** `zdm_proxy_tls_server_cert_filename` and `zdm_proxy_tls_server_key_filename` : filenames (without path) of the server certificate and server key that the proxy must use. -Both always required. -** `zdm_proxy_tls_require_client_auth`: whether you want to enable Mutual TLS between the application and the proxy. -Optional: defaults to `false` ( = one-way TLS ), can be set to `true` to enable Mutual TLS. - -[TIP] -==== -Remember that in this case, {product-proxy} is the TLS server; thus the word `server` in these variable names. -==== - -== Apply the configuration - -This is all that is needed at this point. -As part of its normal execution, the proxy deployment playbook will automatically distribute all TLS files and apply the TLS configuration to all {product-proxy} instances. - -Just go back to xref:deploy-proxy-monitoring.adoc#_advanced_configuration_optional[Optional advanced configuration] to finalize the {product-proxy} configuration and then execute the deployment playbook. \ No newline at end of file diff --git a/modules/ROOT/pages/troubleshooting-tips.adoc b/modules/ROOT/pages/troubleshooting-tips.adoc index afc284d2..50b75650 100644 --- a/modules/ROOT/pages/troubleshooting-tips.adoc +++ b/modules/ROOT/pages/troubleshooting-tips.adoc @@ -519,7 +519,7 @@ Connection failure: Remote end closed connection without response", "redirected" ---- This can indicate that the {astra} DevOps API is temporarily unavailable. -You can either wait to retry the operation, or you can xref:astra-db-serverless:databases:secure-connect-bundle.adoc[download your database's {scb} from the {astra-ui}], and then provide the path the {scb-short} zip file in the xref:deploy-proxy-monitoring.adoc#_core_configuration[{product-automation} configuration]. +You can either wait to retry the operation, or you can xref:astra-db-serverless:databases:secure-connect-bundle.adoc[download your database's {scb} from the {astra-ui}], and then provide the path the {scb-short} zip file in the xref:deploy-proxy-monitoring.adoc#cluster-and-core-configuration[{product-automation} configuration]. === Metadata service returned not successful status code (4xx or 5xx)