github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/cloud/kubernetes/multiregion/setup.py (about) 1 #!/usr/bin/env python 2 3 import distutils.spawn 4 import json 5 import os 6 from subprocess import check_call,check_output 7 from sys import exit 8 from time import sleep 9 10 # Before running the script, fill in appropriate values for all the parameters 11 # above the dashed line. 12 13 # Fill in the `contexts` map with the zones of your clusters and their 14 # corresponding kubectl context names. 15 # 16 # To get the names of your kubectl "contexts" for each of your clusters, run: 17 # kubectl config get-contexts 18 # 19 # example: 20 # contexts = { 21 # 'us-central1-a': 'gke_cockroach-alex_us-central1-a_my-cluster', 22 # 'us-central1-b': 'gke_cockroach-alex_us-central1-b_my-cluster', 23 # 'us-west1-b': 'gke_cockroach-alex_us-west1-b_my-cluster', 24 # } 25 contexts = { 26 } 27 28 # Fill in the `regions` map with the zones and corresponding regions of your 29 # clusters. 30 # 31 # Setting regions is optional, but recommended, because it improves cockroach's 32 # ability to diversify data placement if you use more than one zone in the same 33 # region. If you aren't specifying regions, just leave the map empty. 34 # 35 # example: 36 # regions = { 37 # 'us-central1-a': 'us-central1', 38 # 'us-central1-b': 'us-central1', 39 # 'us-west1-b': 'us-west1', 40 # } 41 regions = { 42 } 43 44 # Paths to directories in which to store certificates and generated YAML files. 45 certs_dir = './certs' 46 ca_key_dir = './my-safe-directory' 47 generated_files_dir = './generated' 48 49 # Path to the cockroach binary on your local machine that you want to use 50 # generate certificates. Defaults to trying to find cockroach in your PATH. 51 cockroach_path = 'cockroach' 52 53 # ------------------------------------------------------------------------------ 54 55 # First, do some basic input validation. 56 if len(contexts) == 0: 57 exit("must provide at least one Kubernetes cluster in the `contexts` map at the top of the script") 58 59 if len(regions) != 0 and len(regions) != len(contexts): 60 exit("regions not specified for all kubectl contexts (%d regions, %d contexts)" % (len(regions), len(contexts))) 61 62 try: 63 check_call(["which", cockroach_path]) 64 except: 65 exit("no binary found at provided path '" + cockroach_path + "'; please put a cockroach binary in your path or change the cockroach_path variable") 66 67 for zone, context in contexts.items(): 68 try: 69 check_call(['kubectl', 'get', 'pods', '--context', context]) 70 except: 71 exit("unable to make basic API call using kubectl context '%s' for cluster in zone '%s'; please check if the context is correct and your Kubernetes cluster is working" % (context, zone)) 72 73 # Set up the necessary directories and certificates. Ignore errors because they may already exist. 74 try: 75 os.mkdir(certs_dir) 76 except OSError: 77 pass 78 try: 79 os.mkdir(ca_key_dir) 80 except OSError: 81 pass 82 try: 83 os.mkdir(generated_files_dir) 84 except OSError: 85 pass 86 87 check_call([cockroach_path, 'cert', 'create-ca', '--certs-dir', certs_dir, '--ca-key', ca_key_dir+'/ca.key']) 88 check_call([cockroach_path, 'cert', 'create-client', 'root', '--certs-dir', certs_dir, '--ca-key', ca_key_dir+'/ca.key']) 89 90 # For each cluster, create secrets containing the node and client certificates. 91 # Note that we create the root client certificate in both the zone namespace 92 # and the default namespace so that it's easier for clients in the default 93 # namespace to use without additional steps. 94 # 95 # Also create a load balancer to each cluster's DNS pods. 96 for zone, context in contexts.items(): 97 check_call(['kubectl', 'create', 'namespace', zone, '--context', context]) 98 check_call(['kubectl', 'create', 'secret', 'generic', 'cockroachdb.client.root', '--from-file', certs_dir, '--context', context]) 99 check_call(['kubectl', 'create', 'secret', 'generic', 'cockroachdb.client.root', '--namespace', zone, '--from-file', certs_dir, '--context', context]) 100 check_call([cockroach_path, 'cert', 'create-node', '--certs-dir', certs_dir, '--ca-key', ca_key_dir+'/ca.key', 'localhost', '127.0.0.1', 'cockroachdb-public', 'cockroachdb-public.default' 'cockroachdb-public.'+zone, 'cockroachdb-public.%s.svc.cluster.local' % (zone), '*.cockroachdb', '*.cockroachdb.'+zone, '*.cockroachdb.%s.svc.cluster.local' % (zone)]) 101 check_call(['kubectl', 'create', 'secret', 'generic', 'cockroachdb.node', '--namespace', zone, '--from-file', certs_dir, '--context', context]) 102 check_call('rm %s/node.*' % (certs_dir), shell=True) 103 104 check_call(['kubectl', 'apply', '-f', 'dns-lb.yaml', '--context', context]) 105 106 # Set up each cluster to forward DNS requests for zone-scoped namespaces to the 107 # relevant cluster's DNS server, using load balancers in order to create a 108 # static IP for each cluster's DNS endpoint. 109 dns_ips = dict() 110 for zone, context in contexts.items(): 111 external_ip = '' 112 while True: 113 external_ip = check_output(['kubectl', 'get', 'svc', 'kube-dns-lb', '--namespace', 'kube-system', '--context', context, '--template', '{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}']) 114 if external_ip: 115 break 116 print 'Waiting for DNS load balancer IP in %s...' % (zone) 117 sleep(10) 118 print 'DNS endpoint for zone %s: %s' % (zone, external_ip) 119 dns_ips[zone] = external_ip 120 121 # Update each cluster's DNS configuration with an appropriate configmap. Note 122 # that we have to leave the local cluster out of its own configmap to avoid 123 # infinite recursion through the load balancer IP. We then have to delete the 124 # existing DNS pods in order for the new configuration to take effect. 125 for zone, context in contexts.items(): 126 remote_dns_ips = dict() 127 for z, ip in dns_ips.items(): 128 if z == zone: 129 continue 130 remote_dns_ips[z+'.svc.cluster.local'] = [ip] 131 config_filename = '%s/dns-configmap-%s.yaml' % (generated_files_dir, zone) 132 with open(config_filename, 'w') as f: 133 f.write("""\ 134 apiVersion: v1 135 kind: ConfigMap 136 metadata: 137 name: kube-dns 138 namespace: kube-system 139 data: 140 stubDomains: | 141 %s 142 """ % (json.dumps(remote_dns_ips))) 143 check_call(['kubectl', 'apply', '-f', config_filename, '--namespace', 'kube-system', '--context', context]) 144 check_call(['kubectl', 'delete', 'pods', '-l', 'k8s-app=kube-dns', '--namespace', 'kube-system', '--context', context]) 145 146 # Create a cockroachdb-public service in the default namespace in each cluster. 147 for zone, context in contexts.items(): 148 yaml_file = '%s/external-name-svc-%s.yaml' % (generated_files_dir, zone) 149 with open(yaml_file, 'w') as f: 150 check_call(['sed', 's/YOUR_ZONE_HERE/%s/g' % (zone), 'external-name-svc.yaml'], stdout=f) 151 check_call(['kubectl', 'apply', '-f', yaml_file, '--context', context]) 152 153 # Generate the join string to be used. 154 join_addrs = [] 155 for zone in contexts: 156 for i in range(3): 157 join_addrs.append('cockroachdb-%d.cockroachdb.%s' % (i, zone)) 158 join_str = ','.join(join_addrs) 159 160 # Create the cockroach resources in each cluster. 161 for zone, context in contexts.items(): 162 if zone in regions: 163 locality = 'region=%s,zone=%s' % (regions[zone], zone) 164 else: 165 locality = 'zone=%s' % (zone) 166 yaml_file = '%s/cockroachdb-statefulset-%s.yaml' % (generated_files_dir, zone) 167 with open(yaml_file, 'w') as f: 168 check_call(['sed', 's/JOINLIST/%s/g;s/LOCALITYLIST/%s/g' % (join_str, locality), 'cockroachdb-statefulset-secure.yaml'], stdout=f) 169 check_call(['kubectl', 'apply', '-f', yaml_file, '--namespace', zone, '--context', context]) 170 171 # Finally, initialize the cluster. 172 print 'Sleeping 30 seconds before attempting to initialize cluster to give time for volumes to be created and pods started.' 173 sleep(30) 174 for zone, context in contexts.items(): 175 check_call(['kubectl', 'create', '-f', 'cluster-init-secure.yaml', '--namespace', zone, '--context', context]) 176 # We only need run the init command in one zone given that all the zones are 177 # joined together as one cluster. 178 break