Skip to content

Commit abb2f0b

Browse files
committed
Adding terraform and documentation for end-users
1 parent dd8ca23 commit abb2f0b

8 files changed

Lines changed: 572 additions & 0 deletions

File tree

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Redpanda Azure private link
2+
3+
4+
### Getting Started
5+
6+
#### Required tools:
7+
- rpk
8+
- terraform
9+
- Azure CLI
10+
- Curl
11+
- jq
12+
13+
Once all tools are installed make sure you have logged into to the azure CLI
14+
15+
```
16+
az login
17+
```
18+
19+
## Provisioning Redpanda with Private Link
20+
21+
To provision a redpanda with private link there is currently only API support with UX support coming soon.
22+
23+
### Environment setup
24+
25+
1. To start set the following env variables:
26+
```
27+
export API_ENDPOINT=https://api.cloud.redpanda.com
28+
export RPK_CLOUD_AUTH_URL=https://auth.prd.cloud.redpanda.com
29+
export RPK_CLOUD_AUTH_AUDIENCE=cloudv2-production.redpanda.cloud
30+
```
31+
32+
2. Next obtaion a client id and secret by going to Organizations in the Cloud UI. Navigate to Clients. Select or create a client you would like to use to create the network and cluster. You can copy the ID and secret to your clipboard by clicking the “Copy ID” and “Copy secret” buttons.
33+
```
34+
export CLOUD_CLIENT_ID=<client-ID>
35+
export CLOUD_CLIENT_SECRET=<client-secret>
36+
```
37+
38+
3. Set the Azure subscriptions you would like to use:
39+
40+
```
41+
# Where the redpanda cluster will be provisioned
42+
AZURE_SUBSCRIPTION_ID=60fc0bed-3072-4c53-906a-d130a934d520
43+
44+
# Where you will connect to the private link service fronting redpanda
45+
AZURE_PRIVATE_LINK_SUBSCRIPTION_ID=1b88eb19-4c80-4edd-870d-461e83ddcbb5
46+
```
47+
48+
### Provisioning
49+
50+
1. Obtain an auth token
51+
```
52+
export AUTH_TOKEN=`curl -s --request POST \
53+
--url "${RPK_CLOUD_AUTH_URL}/oauth/token" \
54+
--header 'content-type: application/x-www-form-urlencoded' \
55+
--data grant_type=client_credentials \
56+
--data client_id=$CLOUD_CLIENT_ID \
57+
--data client_secret=$CLOUD_CLIENT_SECRET \
58+
--data audience=$RPK_CLOUD_AUTH_AUDIENCE | jq -r '.access_token'`
59+
60+
# MAKE SURE THIS IS POUPULATED
61+
echo $AUTH_TOKEN
62+
63+
64+
declare -a HEADERS=(
65+
"-H" "Content-Type: application/json"
66+
"-H" "Authorization: Bearer $AUTH_TOKEN"
67+
)
68+
69+
```
70+
71+
1. Find the Resource Group where we will create the redpanda cluster
72+
73+
```
74+
RESOURCE_GROUP_ID=`curl -s -X GET "${HEADERS[@]}" \
75+
$API_ENDPOINT/v1beta2/resource-groups | jq -r ".resource_groups[0].id"`
76+
77+
# NOTE: this should have output
78+
echo $RESOURCE_GROUP_ID
79+
80+
```
81+
82+
1. Create the network that will be used by the redpanda cluster
83+
```
84+
NETWORK_POST_BODY=`cat << EOF
85+
{
86+
"cidr_block": "10.0.0.0/20",
87+
"cloud_provider": "CLOUD_PROVIDER_AZURE",
88+
"cluster_type": "TYPE_BYOC",
89+
"name": "azure-byoc-network",
90+
"resource_group_id": "$RESOURCE_GROUP_ID",
91+
"region": "uksouth"
92+
}
93+
EOF`
94+
95+
NETWORK_ID=`curl -s -X POST "${HEADERS[@]}" \
96+
-d "$NETWORK_POST_BODY" $API_ENDPOINT/v1beta2/networks |
97+
jq -r '.operation.metadata.network_id'`
98+
99+
# NOTE: this should have output
100+
echo $NETWORK_ID
101+
```
102+
1. Create the redpanda cluster with the allowed subscriptions we set before. NOTE: you can add as many other subscriptions as you would like under `allowed_subscriptions`
103+
104+
```
105+
CLUSTER_POST_BODY=`cat << EOF
106+
{
107+
"cloud_provider": "CLOUD_PROVIDER_AZURE",
108+
"connection_type": "CONNECTION_TYPE_PRIVATE",
109+
"name": "azure-byoc-pl",
110+
"resource_group_id": "$RESOURCE_GROUP_ID",
111+
"network_id": "$NETWORK_ID",
112+
"region": "uksouth",
113+
"throughput_tier": "tier-1-azure-beta",
114+
"type": "TYPE_BYOC",
115+
"zones": ["uksouth-az1", "uksouth-az2", "uksouth-az3"],
116+
"redpanda_version": "24.1",
117+
"azure_private_link": {
118+
"allowed_subscriptions": ["$AZURE_PRIVATE_LINK_SUBSCRIPTION_ID"],
119+
"enabled": true,
120+
"connect_console": true
121+
}
122+
}
123+
EOF`
124+
125+
RP_ID=`curl -s -X POST "${HEADERS[@]}" \
126+
-d "$CLUSTER_POST_BODY" $API_ENDPOINT/v1beta2/clusters | jq -r '.operation.metadata.cluster_id'`
127+
128+
# NOTE: this should have output
129+
echo $RP_ID
130+
```
131+
1. Run the rpk apply to create the infrastructure for the cluster. This will take about 45 minutes to complete.
132+
```
133+
rpk login --save --client-id=$CLOUD_CLIENT_ID --client-secret=$CLOUD_CLIENT_SECRET
134+
135+
rpk cloud byoc azure apply --redpanda-id=$RP_ID --subscription-id=$AZURE_SUBSCRIPTION_ID
136+
137+
```
138+
139+
## Connecting to Redpanda Private Link
140+
141+
Once the cluster has been created we can now run terraform to connect to the created cluster with private link. This terraform will provision a vm along with a private link endpoint that connects to the redpanda cluster.
142+
143+
144+
145+
1. First fetch credentials to the Redpanda cluster:
146+
147+
```
148+
az login
149+
az account set --subscription $AZURE_SUBSCRIPTION_ID
150+
az aks get-credentials -g rg-rpcloud-$RP_ID -n aks-rpcloud-$RP_ID
151+
secret=$(kubectl get secret redpanda-superusers -n redpanda -o jsonpath='{.data.users\.txt}' | base64 --decode)
152+
153+
IFS=':' read -r user password sasl <<< "$secret"
154+
155+
export USER=$user
156+
export PASS=$password
157+
158+
```
159+
1. Fetch outputs from the redpanda cluster that we will use as inputs to our terraform
160+
```
161+
DNS_RECORD=`curl -s -X GET "${HEADERS[@]}" \
162+
$API_ENDPOINT/v1beta2/clusters/$RP_ID | jq -r ".cluster.azure_private_link.status.dns_a_record"`
163+
164+
PRIVATE_SERVICE_ID=`curl -s -X GET "${HEADERS[@]}" \
165+
$API_ENDPOINT/v1beta2/clusters/$RP_ID | jq -r ".cluster.azure_private_link.status.service_id"`
166+
167+
CONSOLE_URL=`curl -s -X GET "${HEADERS[@]}" \
168+
$API_ENDPOINT/v1beta2/clusters/$RP_ID | jq -r ".cluster.redpanda_console.url"`
169+
170+
echo $DNS_RECORD
171+
echo $PRIVATE_SERVICE_ID
172+
echo $CONSOLE_URL
173+
174+
```
175+
1. Next make sure you are logged into the subscription that was allowed in the creation request:
176+
```
177+
az login
178+
az account set --subscription $AZURE_PRIVATE_LINK_SUBSCRIPTION_ID
179+
```
180+
1. Write our terraform variables out to a file so we can pick it up and use it
181+
```
182+
export MYGROUP=your-rg-group
183+
cat << EOF > terraform.tfvars
184+
region = "uksouth"
185+
resource_group_name = "$MYGROUP"
186+
rp_endpoint_service_id = "$PRIVATE_SERVICE_ID"
187+
rp_id = "$RP_ID"
188+
rp_domain = "$DNS_RECORD"
189+
rp_node_count = 3
190+
EOF
191+
```
192+
1. Initialize the terraform and create the needed resources
193+
```
194+
cd terraform
195+
terraform init
196+
terraform apply -var-file="terraform.tfvars"
197+
198+
```
199+
200+
1. When the terraform completes copy and paste the following block. This will give us another block we can copy once we have ssh'd into the vm.
201+
```
202+
VM_IP=$(terraform output -raw instance_public_ip)
203+
KEY_PATH=$(terraform output -raw private_key_file_path)
204+
205+
cat << EOF
206+
Copy and paste this block after sshing in:
207+
208+
export USER=$USER
209+
export PASS=$PASS
210+
export CONSOLE_URL=$CONSOLE_URL
211+
export BROKERS=brokers.${DNS_RECORD}:30292
212+
213+
EOF
214+
215+
```
216+
217+
1. Next ssh in and copy the output of the previous command into the vm to setup the environment variables needed to connect
218+
```
219+
ssh -i $KEY_PATH redpanda@$VM_IP
220+
221+
$ <paste in output from previous command>
222+
```
223+
224+
1. Finally you now can connect to the redpanda cluster over the private link endpoint
225+
```
226+
auth="-X user=$USER -X pass=$PASS -X sasl.mechanism=SCRAM-SHA-512 -X brokers=$BROKERS --tls-enabled"
227+
228+
rpk cluster info $auth
229+
rpk topic create mytopic $auth
230+
231+
cat <<EOF | rpk topic $auth produce mytopic -f '%k,%v\n'
232+
key1,value1
233+
key2,value2
234+
key3,value4
235+
EOF
236+
237+
rpk topic consume mytopic $auth
238+
239+
```
240+
1. Once you have completed testing and want to tear down the infrastructure you can run
241+
```
242+
terraform destroy -var-file="terraform.tfvars"
243+
244+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
if command -v rpk &> /dev/null
4+
then
5+
echo "found rpk, skip the installation"
6+
exit 0
7+
fi
8+
9+
cd /tmp; curl -LO https://github.com/redpanda-data/redpanda/releases/latest/download/rpk-linux-amd64.zip
10+
export DEBIAN_FRONTEND=noninteractive
11+
sudo apt-get update
12+
# Need NEEDRESTART_MODE=a for not having the prompt to restart services
13+
sudo NEEDRESTART_MODE=a apt install -y unzip
14+
sudo unzip /tmp/rpk-linux-amd64.zip -d /usr/local/bin/
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
terraform {
2+
required_providers {
3+
azurerm = {
4+
source = "hashicorp/azurerm"
5+
version = "=3.98.0"
6+
}
7+
}
8+
9+
}
10+
11+
resource "azurerm_resource_group" "rg" {
12+
name = var.resource_group_name
13+
location = var.region
14+
}
15+
16+
resource "azurerm_virtual_network" "vnet" {
17+
name = "${var.resource_prefix}_vnet"
18+
address_space = [var.vnet_cidr]
19+
location = var.region
20+
resource_group_name = azurerm_resource_group.rg.name
21+
}
22+
23+
resource "azurerm_subnet" "subnet" {
24+
name = "${var.resource_prefix}_subnet"
25+
resource_group_name = azurerm_resource_group.rg.name
26+
virtual_network_name = azurerm_virtual_network.vnet.name
27+
address_prefixes = [var.vnet_subnet] # Adjust the subnet CIDR block as needed
28+
}
29+
30+
resource "azurerm_private_endpoint" "service-endpoint" {
31+
name = "${var.resource_prefix}_service_endpoint"
32+
location = var.region
33+
resource_group_name = azurerm_resource_group.rg.name
34+
subnet_id = azurerm_subnet.subnet.id
35+
36+
37+
private_service_connection {
38+
name = "${var.resource_prefix}_service_connection"
39+
private_connection_resource_id = var.rp_endpoint_service_id
40+
is_manual_connection = false
41+
}
42+
private_dns_zone_group {
43+
name = "example-dns-zone-group"
44+
private_dns_zone_ids = [azurerm_private_dns_zone.example.id]
45+
}
46+
}
47+
48+
resource "azurerm_private_dns_zone" "example" {
49+
name = var.rp_domain
50+
resource_group_name = azurerm_resource_group.rg.name
51+
}
52+
53+
resource "azurerm_private_dns_a_record" "example" {
54+
name = "*"
55+
zone_name = azurerm_private_dns_zone.example.name
56+
resource_group_name = azurerm_resource_group.rg.name
57+
ttl = 300
58+
records = [azurerm_private_endpoint.service-endpoint.private_service_connection[0].private_ip_address]
59+
}
60+
61+
resource "azurerm_private_dns_zone_virtual_network_link" "example" {
62+
name = "${var.resource_prefix}_example_link"
63+
resource_group_name = azurerm_resource_group.rg.name
64+
private_dns_zone_name = azurerm_private_dns_zone.example.name
65+
virtual_network_id = azurerm_virtual_network.vnet.id
66+
}
67+
68+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
locals {
2+
kafka_api_seed_nlb_listener_port = 30292
3+
schema_registry_seed_nlb_listener_port = 30081
4+
rp_proxy_seed_nlb_listener_port = 30282
5+
6+
kafka_api_node_nlb_listener_port = 32092
7+
rp_proxy_node_nlb_listener_port = 31082
8+
9+
console_port = 443
10+
11+
resource_prefix = (var.resource_prefix) == "" ? "" : replace(var.resource_prefix, "_", "-")
12+
13+
14+
cert_file = format("/tmp/%s.pem", azurerm_public_ip.public_ip.ip_address)
15+
16+
}
17+
18+
19+
output "instance_public_ip" {
20+
value = azurerm_public_ip.public_ip.ip_address
21+
}
22+
23+
output "rp_kafka_api_node_endpoints" {
24+
description = "Redpanda Kafka service urls"
25+
value = var.resource_prefix == "" ? {
26+
for i in range(var.rp_node_count) : format("kafka_api_node%d_endpoint", i) => format("%s:%d", var.rp_domain, local.kafka_api_node_nlb_listener_port + i)
27+
} : null
28+
}
29+
30+
output "rp_rp_proxy_node_endpoints" {
31+
description = "Redpanda proxy service urls"
32+
value = var.resource_prefix == "" ? {
33+
for i in range(var.rp_node_count) : format("redpanda_proxy_node%d_endpoint", i) => format("%s:%d", var.rp_domain, local.rp_proxy_node_nlb_listener_port + i)
34+
} : null
35+
}
36+
37+
output "rp_seed_endpoints" {
38+
description = "Redpanda service seed urls"
39+
value = var.resource_prefix == "" ? {
40+
"kafka_api_seed" : format("%s:%d", var.rp_domain, local.kafka_api_seed_nlb_listener_port)
41+
"schema_registry_seed" : format("%s:%d", var.rp_domain, local.schema_registry_seed_nlb_listener_port)
42+
"redpanda_proxy_seed" : format("%s:%d", var.rp_domain, local.rp_proxy_seed_nlb_listener_port)
43+
} : null
44+
}
45+
46+
output "ssh_private_key" {
47+
description = "SSH key to EC instance"
48+
value = tls_private_key.key.private_key_pem
49+
sensitive = true
50+
}
51+
52+
output "private_key_file_path" {
53+
value = abspath(local_file.private_key.filename)
54+
}
55+
56+
output "ssh_to_ec2_commands" {
57+
description = "SSH to EC2 instance commands"
58+
# Terraform go code has issue to unmarshall the output. So no output if running inside the certification tests.
59+
value = var.resource_prefix == "" ? {
60+
"get_ssh_key_command" : <<EOF
61+
cat terraform.tfstate | jq .outputs.ssh_private_key.value | sed 's/"//g' | awk '{gsub(/\\n/,"\n")}1' > ${local.cert_file}; chmod 600 ${local.cert_file}
62+
EOF
63+
"ssh_command" : format("ssh -i %s redpanda@%s", local.cert_file, azurerm_public_ip.public_ip.ip_address)
64+
} : null
65+
}

0 commit comments

Comments
 (0)