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