Migrate the router floating IP from Puppet LBs to cloudscale LBaaS
Migrating the router floating IP from the Puppet LBs to a cloudscale LBaaS instance requires some manual steps.
Prerequisites
-
You need to be able to execute the following CLI tools locally:
-
commodore
-
kapitan
-
docker
-
yq
yq YAML processor (Version 4 or newer) -
jq
-
vault
Vault CLI
-
-
Admin access to the cluster you want to migrate
-
Admin access to the cluster’s Puppet-managed LBs
-
The cluster is already updated to use component version v9.4.0 or newer
We recommend installing Commodore with Using |
Setup environment
-
Access to API
# For example: https://api.syn.vshn.net # IMPORTANT: do NOT add a trailing `/`. Commands below will fail. export COMMODORE_API_URL=<lieutenant-api-endpoint> export COMMODORE_API_TOKEN=<lieutenant-api-token> export CLUSTER_ID=<lieutenant-cluster-id> # Looks like: c-<something> export TENANT_ID=$(curl -sH "Authorization: Bearer ${COMMODORE_API_TOKEN}" ${COMMODORE_API_URL}/clusters/${CLUSTER_ID} | jq -r .tenant) # From https://git.vshn.net/-/profile/personal_access_tokens, "api" scope is sufficient export GITLAB_TOKEN=<gitlab-api-token> export GITLAB_USER=<gitlab-user-name>
-
Connect with Vault
export VAULT_ADDR=https://vault-prod.syn.vshn.net vault login -method=ldap username=<your.name>
-
Fetch the hieradata repo token from Vault
export HIERADATA_REPO_SECRET=$(vault kv get \ -format=json "clusters/kv/lbaas/hieradata_repo_token" | jq '.data.data') export HIERADATA_REPO_USER=$(echo "${HIERADATA_REPO_SECRET}" | jq -r '.user') export HIERADATA_REPO_TOKEN=$(echo "${HIERADATA_REPO_SECRET}" | jq -r '.token')
-
Fetch the cluster cloudscale API token from Vault
cloudscale_token=$(vault kv get \ -format=json "clusters/kv/${TENANT_ID}/${CLUSTER_ID}/cloudscale") export CLOUDSCALE_API_TOKEN=$(echo $cloudscale_token | jq -r '.data.data.token')
-
Set the floaty API key Terraform variable from Vault
floaty_key=$(vault kv get \ -format=json "clusters/kv/${TENANT_ID}/${CLUSTER_ID}/floaty") export TF_VAR_lb_cloudscale_api_secret=$(echo $floaty_key | jq -r '.data.data.iam_secret')
-
Compile the cluster catalog to create a local working directory
commodore catalog compile "${CLUSTER_ID}"
Deploy cloudscale LBaaS instance and move floating IP
-
Disable ArgoCD auto sync
kubectl --as=cluster-admin -n syn patch apps root --type=json \ -p '[{"op":"replace", "path":"/spec/syncPolicy", "value": {}}]'
-
Deploy the cloudscale-loadbalancer-controller and update the Terraform config
pushd inventory/classes/${TENANT_ID} yq eval -i '.applications += ["cloudscale-loadbalancer-controller"]' ${CLUSTER_ID}.yml yq eval -i ".parameters.openshift4_terraform.terraform_variables.allocate_router_vip_for_lb_controller = true" \ ${CLUSTER_ID}.yml git commit -a -m "Prepare ingress migration to cloudscale LBaaS for ${CLUSTER_ID}" git push popd
-
Compile catalog again
commodore catalog compile ${CLUSTER_ID}
-
Deploy cloudscale-loadbalancer-controller
kubectl --as=cluster-admin apply -f catalog/manifests/cloudscale-loadbalancer-controller/00_namespace.yaml kapitan refs --reveal --refs-path catalog/refs -f catalog/manifests/cloudscale-loadbalancer-controller/10_secrets.yaml | \ kubectl --as=cluster-admin apply -f - kubectl --as=cluster-admin apply -Rf catalog/manifests/cloudscale-loadbalancer-controller/10_kustomize kubectl -n appuio-cloudscale-loadbalancer-controller \ wait --for condition=available \ deploy cloudscale-loadbalancer-controller-controller-manager
-
Deploy ingress loadbalancer
yq 'del(.spec.floatingIPAddresses)' catalog/manifests/cloudscale-loadbalancer-controller/20_loadbalancers.yaml | \ kubectl --as=cluster-admin apply -f -
-
Wait for the cluster ingress to become reachable via the loadbalancer
export LB_IP="" while [ "$LB_IP" == "" ]; do export LB_IP=$(kubectl --as=cluster-admin -n appuio-cloudscale-loadbalancer-controller \ get loadbalancer ingress -oyaml | \ yq '(.status.virtualIPAddresses[]|select(.address|contains("2a06")|not)|.address)//""') if [ "$LB_IP" == "" ]; then echo -n "." sleep 5 fi done && echo -e "\nLoadbalancer available at ${LB_IP}" export APPS_DOMAIN=$(kapitan inventory -t cluster --inventory-backend=reclass-rs | \ yq .parameters.openshift.appsDomain) curl --resolve console-openshift-console.${APPS_DOMAIN}:80:${LB_IP} \ http://console-openshift-console.${APPS_DOMAIN} -vI (1)
1 This command should return HTTP/1.1 302 Found
Update Terraform state
-
Configure Terraform environment
cat <<EOF > ./terraform.env CLOUDSCALE_API_TOKEN TF_VAR_ignition_bootstrap TF_VAR_lb_cloudscale_api_secret TF_VAR_control_vshn_net_token GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL HIERADATA_REPO_TOKEN EOF
-
Setup Terraform
# Set terraform image and tag to be used tf_image=$(\ yq eval ".parameters.openshift4_terraform.images.terraform.image" \ dependencies/openshift4-terraform/class/defaults.yml) tf_tag=$(\ yq eval ".parameters.openshift4_terraform.images.terraform.tag" \ dependencies/openshift4-terraform/class/defaults.yml) # Generate the terraform alias base_dir=$(pwd) alias terraform='touch .terraformrc; docker run -it --rm \ -e REAL_UID=$(id -u) \ -e TF_CLI_CONFIG_FILE=/tf/.terraformrc \ --env-file ${base_dir}/terraform.env \ -w /tf \ -v $(pwd):/tf \ --ulimit memlock=-1 \ "${tf_image}:${tf_tag}" /tf/terraform.sh' export GITLAB_REPOSITORY_URL=$(curl -sH "Authorization: Bearer $(commodore fetch-token)" ${COMMODORE_API_URL}/clusters/${CLUSTER_ID} | jq -r '.gitRepo.url' | sed 's|ssh://||; s|/|:|') export GITLAB_REPOSITORY_NAME=${GITLAB_REPOSITORY_URL##*/} export GITLAB_CATALOG_PROJECT_ID=$(curl -sH "Authorization: Bearer ${GITLAB_TOKEN}" "https://git.vshn.net/api/v4/projects?simple=true&search=${GITLAB_REPOSITORY_NAME/.git}" | jq -r ".[] | select(.ssh_url_to_repo == \"${GITLAB_REPOSITORY_URL}\") | .id") export GITLAB_STATE_URL="https://git.vshn.net/api/v4/projects/${GITLAB_CATALOG_PROJECT_ID}/terraform/state/cluster" pushd catalog/manifests/openshift4-terraform/
-
Initialize Terraform
terraform init \ "-backend-config=address=${GITLAB_STATE_URL}" \ "-backend-config=lock_address=${GITLAB_STATE_URL}/lock" \ "-backend-config=unlock_address=${GITLAB_STATE_URL}/lock" \ "-backend-config=username=${GITLAB_USER}" \ "-backend-config=password=${GITLAB_TOKEN}" \ "-backend-config=lock_method=POST" \ "-backend-config=unlock_method=DELETE" \ "-backend-config=retry_wait_min=5"
-
Move floating IP Terraform state
terraform state mv "module.cluster.module.lb.cloudscale_floating_ip.router_vip[0]" \ "module.cluster.cloudscale_floating_ip.router_vip[0]"
-
Get router floating IP from Terraform state
terraform refresh export INGRESS_FLOATING_IP=$(terraform output -raw router_vip)
-
Grab LB hostnames from Terraform state
declare -a LB_FQDNS for id in 1 2; do LB_FQDNS[$id]=$(terraform state show "module.cluster.module.lb.cloudscale_server.lb[$(expr $id - 1)]" | grep fqdn | awk '{print $2}' | tr -d ' "\r\n') done
-
Disable Puppet on LBs
for lb in "${LB_FQDNS[@]}"; do ssh $lb sudo puppetctl stop "Migrating router floating IP" done
-
Remove router floating IP from Floaty and restart keepalived on LBs
for lb in "${LB_FQDNS[@]}"; do ssh $lb sudo sed -i "/${INGRESS_FLOATING_IP}/d" /etc/floaty/global.yaml ssh $lb sudo systemctl restart keepalived done
-
Run Terraform
terraform apply
-
Merge Hieradata MR
This won’t have an immediate effect since we’ve disabled Puppet on the LBs. Review this MR very carefully. The MR must remove the router floating IP in
profile_openshift4_gateway::floating_addresses
, if it was previously present. Additionally the MR will remove the router backends.Please be aware that in some setups, such as clusters which have the router exposed on internal IPs, the router backends may still be required after migrating the public ingress to a cloudscale LB instance.
-
Switch back to Commodore working directory
popd
Move floating IP to LBaaS instance
-
Set router floating IP in cluster config
pushd inventory/classes/${TENANT_ID} yq eval -i '.parameters.openshift.cloudscale.ingress_floating_ip_v4 = "'$INGRESS_FLOATING_IP'"' \ ${CLUSTER_ID}.yml git commit -a -m "Migrate ingress floating IP to cloudscale LBaaS for ${CLUSTER_ID}" git push popd
-
Compile and push catalog
commodore catalog compile ${CLUSTER_ID} --push -i
-
Enable ArgoCD sync
kubectl --as=cluster-admin -n syn patch apps root --type=json \ -p '[{ "op":"replace", "path":"/spec/syncPolicy", "value": {"automated": {"prune": true, "selfHeal": true}} }]'
-
Wait until cloudscale floating IP is attached to cloudscale LBaaS instance
export AUTH_HEADER="Authorization: Bearer ${TF_VAR_lb_cloudscale_api_secret}" while [ "$(curl -sH"$AUTH_HEADER" https://api.cloudscale.ch/v1/floating-ips/${INGRESS_FLOATING_IP} | jq -r '.load_balancer')" == "null" ]; do echo -n '.' sleep 1 done && echo -e "\nFloating IP attached to LBaaS instance"
-
Verify that cluster console is accessible
curl https://console-openshift-console.${APPS_DOMAIN} -vI
-
Enable Puppet on LBs
for lb in "${LB_FQDNS[@]}"; do ssh $lb sudo puppetctl start done
-
Verify that cluster console is still accessible
curl https://console-openshift-console.${APPS_DOMAIN} -vI