You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

731 lines
27 KiB

  1. # Kubernetes on OpenStack with Terraform
  2. Provision a Kubernetes cluster with [Terraform](https://www.terraform.io) on
  3. OpenStack.
  4. ## Status
  5. This will install a Kubernetes cluster on an OpenStack Cloud. It should work on
  6. most modern installs of OpenStack that support the basic services.
  7. ### Known compatible public clouds
  8. - [Auro](https://auro.io/)
  9. - [Betacloud](https://www.betacloud.io/)
  10. - [CityCloud](https://www.citycloud.com/)
  11. - [DreamHost](https://www.dreamhost.com/cloud/computing/)
  12. - [ELASTX](https://elastx.se/)
  13. - [EnterCloudSuite](https://www.entercloudsuite.com/)
  14. - [FugaCloud](https://fuga.cloud/)
  15. - [Open Telekom Cloud](https://cloud.telekom.de/) : requires to set the variable `wait_for_floatingip = "true"` in your cluster.tfvars
  16. - [OVH](https://www.ovh.com/)
  17. - [Rackspace](https://www.rackspace.com/)
  18. - [Ultimum](https://ultimum.io/)
  19. - [VexxHost](https://vexxhost.com/)
  20. - [Zetta](https://www.zetta.io/)
  21. ## Approach
  22. The terraform configuration inspects variables found in
  23. [variables.tf](variables.tf) to create resources in your OpenStack cluster.
  24. There is a [python script](../terraform.py) that reads the generated`.tfstate`
  25. file to generate a dynamic inventory that is consumed by the main ansible script
  26. to actually install kubernetes and stand up the cluster.
  27. ### Networking
  28. The configuration includes creating a private subnet with a router to the
  29. external net. It will allocate floating IPs from a pool and assign them to the
  30. hosts where that makes sense. You have the option of creating bastion hosts
  31. inside the private subnet to access the nodes there. Alternatively, a node with
  32. a floating IP can be used as a jump host to nodes without.
  33. #### Using an existing router
  34. It is possible to use an existing router instead of creating one. To use an
  35. existing router set the router\_id variable to the uuid of the router you wish
  36. to use.
  37. For example:
  38. ```ShellSession
  39. router_id = "00c542e7-6f46-4535-ae95-984c7f0391a3"
  40. ```
  41. ### Kubernetes Nodes
  42. You can create many different kubernetes topologies by setting the number of
  43. different classes of hosts. For each class there are options for allocating
  44. floating IP addresses or not.
  45. - Master nodes with etcd
  46. - Master nodes without etcd
  47. - Standalone etcd hosts
  48. - Kubernetes worker nodes
  49. Note that the Ansible script will report an invalid configuration if you wind up
  50. with an even number of etcd instances since that is not a valid configuration. This
  51. restriction includes standalone etcd nodes that are deployed in a cluster along with
  52. master nodes with etcd replicas. As an example, if you have three master nodes with
  53. etcd replicas and three standalone etcd nodes, the script will fail since there are
  54. now six total etcd replicas.
  55. ### GlusterFS shared file system
  56. The Terraform configuration supports provisioning of an optional GlusterFS
  57. shared file system based on a separate set of VMs. To enable this, you need to
  58. specify:
  59. - the number of Gluster hosts (minimum 2)
  60. - Size of the non-ephemeral volumes to be attached to store the GlusterFS bricks
  61. - Other properties related to provisioning the hosts
  62. Even if you are using Flatcar Container Linux by Kinvolk for your cluster, you will still
  63. need the GlusterFS VMs to be based on either Debian or RedHat based images.
  64. Flatcar Container Linux by Kinvolk cannot serve GlusterFS, but can connect to it through
  65. binaries available on hyperkube v1.4.3_coreos.0 or higher.
  66. ## Requirements
  67. - [Install Terraform](https://www.terraform.io/intro/getting-started/install.html) 0.12 or later
  68. - [Install Ansible](http://docs.ansible.com/ansible/latest/intro_installation.html)
  69. - you already have a suitable OS image in Glance
  70. - you already have a floating IP pool created
  71. - you have security groups enabled
  72. - you have a pair of keys generated that can be used to secure the new hosts
  73. ## Module Architecture
  74. The configuration is divided into three modules:
  75. - Network
  76. - IPs
  77. - Compute
  78. The main reason for splitting the configuration up in this way is to easily
  79. accommodate situations where floating IPs are limited by a quota or if you have
  80. any external references to the floating IP (e.g. DNS) that would otherwise have
  81. to be updated.
  82. You can force your existing IPs by modifying the compute variables in
  83. `kubespray.tf` as follows:
  84. ```ini
  85. k8s_master_fips = ["151.101.129.67"]
  86. k8s_node_fips = ["151.101.129.68"]
  87. ```
  88. ## Terraform
  89. Terraform will be used to provision all of the OpenStack resources with base software as appropriate.
  90. ### Configuration
  91. #### Inventory files
  92. Create an inventory directory for your cluster by copying the existing sample and linking the `hosts` script (used to build the inventory based on Terraform state):
  93. ```ShellSession
  94. cp -LRp contrib/terraform/openstack/sample-inventory inventory/$CLUSTER
  95. cd inventory/$CLUSTER
  96. ln -s ../../contrib/terraform/openstack/hosts
  97. ln -s ../../contrib
  98. ```
  99. This will be the base for subsequent Terraform commands.
  100. #### OpenStack access and credentials
  101. No provider variables are hardcoded inside `variables.tf` because Terraform
  102. supports various authentication methods for OpenStack: the older script and
  103. environment method (using `openrc`) as well as a newer declarative method, and
  104. different OpenStack environments may support Identity API version 2 or 3.
  105. These are examples and may vary depending on your OpenStack cloud provider,
  106. for an exhaustive list on how to authenticate on OpenStack with Terraform
  107. please read the [OpenStack provider documentation](https://www.terraform.io/docs/providers/openstack/).
  108. ##### Declarative method (recommended)
  109. The recommended authentication method is to describe credentials in a YAML file `clouds.yaml` that can be stored in:
  110. - the current directory
  111. - `~/.config/openstack`
  112. - `/etc/openstack`
  113. `clouds.yaml`:
  114. ```yaml
  115. clouds:
  116. mycloud:
  117. auth:
  118. auth_url: https://openstack:5000/v3
  119. username: "username"
  120. project_name: "projectname"
  121. project_id: projectid
  122. user_domain_name: "Default"
  123. password: "password"
  124. region_name: "RegionOne"
  125. interface: "public"
  126. identity_api_version: 3
  127. ```
  128. If you have multiple clouds defined in your `clouds.yaml` file you can choose
  129. the one you want to use with the environment variable `OS_CLOUD`:
  130. ```ShellSession
  131. export OS_CLOUD=mycloud
  132. ```
  133. ##### Openrc method
  134. When using classic environment variables, Terraform uses default `OS_*`
  135. environment variables. A script suitable for your environment may be available
  136. from Horizon under *Project* -> *Compute* -> *Access & Security* -> *API Access*.
  137. With identity v2:
  138. ```ShellSession
  139. source openrc
  140. env | grep OS
  141. OS_AUTH_URL=https://openstack:5000/v2.0
  142. OS_PROJECT_ID=projectid
  143. OS_PROJECT_NAME=projectname
  144. OS_USERNAME=username
  145. OS_PASSWORD=password
  146. OS_REGION_NAME=RegionOne
  147. OS_INTERFACE=public
  148. OS_IDENTITY_API_VERSION=2
  149. ```
  150. With identity v3:
  151. ```ShellSession
  152. source openrc
  153. env | grep OS
  154. OS_AUTH_URL=https://openstack:5000/v3
  155. OS_PROJECT_ID=projectid
  156. OS_PROJECT_NAME=username
  157. OS_PROJECT_DOMAIN_ID=default
  158. OS_USERNAME=username
  159. OS_PASSWORD=password
  160. OS_REGION_NAME=RegionOne
  161. OS_INTERFACE=public
  162. OS_IDENTITY_API_VERSION=3
  163. OS_USER_DOMAIN_NAME=Default
  164. ```
  165. Terraform does not support a mix of DomainName and DomainID, choose one or the other:
  166. - provider.openstack: You must provide exactly one of DomainID or DomainName to authenticate by Username
  167. ```ShellSession
  168. unset OS_USER_DOMAIN_NAME
  169. export OS_USER_DOMAIN_ID=default
  170. ```
  171. or
  172. ```ShellSession
  173. unset OS_PROJECT_DOMAIN_ID
  174. set OS_PROJECT_DOMAIN_NAME=Default
  175. ```
  176. #### Cluster variables
  177. The construction of the cluster is driven by values found in
  178. [variables.tf](variables.tf).
  179. For your cluster, edit `inventory/$CLUSTER/cluster.tfvars`.
  180. |Variable | Description |
  181. |---------|-------------|
  182. |`cluster_name` | All OpenStack resources will use the Terraform variable`cluster_name` (default`example`) in their name to make it easier to track. For example the first compute resource will be named`example-kubernetes-1`. |
  183. |`az_list` | List of Availability Zones available in your OpenStack cluster. |
  184. |`network_name` | The name to be given to the internal network that will be generated |
  185. |`network_dns_domain` | (Optional) The dns_domain for the internal network that will be generated |
  186. |`dns_nameservers`| An array of DNS name server names to be used by hosts in the internal subnet. |
  187. |`floatingip_pool` | Name of the pool from which floating IPs will be allocated |
  188. |`k8s_master_fips` | A list of floating IPs that you have already pre-allocated; they will be attached to master nodes instead of creating new random floating IPs. |
  189. |`external_net` | UUID of the external network that will be routed to |
  190. |`flavor_k8s_master`,`flavor_k8s_node`,`flavor_etcd`, `flavor_bastion`,`flavor_gfs_node` | Flavor depends on your openstack installation, you can get available flavor IDs through `openstack flavor list` |
  191. |`image`,`image_gfs` | Name of the image to use in provisioning the compute resources. Should already be loaded into glance. |
  192. |`ssh_user`,`ssh_user_gfs` | The username to ssh into the image with. This usually depends on the image you have selected |
  193. |`public_key_path` | Path on your local workstation to the public key file you wish to use in creating the key pairs |
  194. |`number_of_k8s_masters`, `number_of_k8s_masters_no_floating_ip` | Number of nodes that serve as both master and etcd. These can be provisioned with or without floating IP addresses|
  195. |`number_of_k8s_masters_no_etcd`, `number_of_k8s_masters_no_floating_ip_no_etcd` | Number of nodes that serve as just master with no etcd. These can be provisioned with or without floating IP addresses |
  196. |`number_of_etcd` | Number of pure etcd nodes |
  197. |`number_of_k8s_nodes`, `number_of_k8s_nodes_no_floating_ip` | Kubernetes worker nodes. These can be provisioned with or without floating ip addresses. |
  198. |`number_of_bastions` | Number of bastion hosts to create. Scripts assume this is really just zero or one |
  199. |`number_of_gfs_nodes_no_floating_ip` | Number of gluster servers to provision. |
  200. | `gfs_volume_size_in_gb` | Size of the non-ephemeral volumes to be attached to store the GlusterFS bricks |
  201. |`supplementary_master_groups` | To add ansible groups to the masters, such as `kube_node` for tainting them as nodes, empty by default. |
  202. |`supplementary_node_groups` | To add ansible groups to the nodes, such as `kube_ingress` for running ingress controller pods, empty by default. |
  203. |`bastion_allowed_remote_ips` | List of CIDR allowed to initiate a SSH connection, `["0.0.0.0/0"]` by default |
  204. |`master_allowed_remote_ips` | List of CIDR blocks allowed to initiate an API connection, `["0.0.0.0/0"]` by default |
  205. |`k8s_allowed_remote_ips` | List of CIDR allowed to initiate a SSH connection, empty by default |
  206. |`worker_allowed_ports` | List of ports to open on worker nodes, `[{ "protocol" = "tcp", "port_range_min" = 30000, "port_range_max" = 32767, "remote_ip_prefix" = "0.0.0.0/0"}]` by default |
  207. |`master_allowed_ports` | List of ports to open on master nodes, expected format is `[{ "protocol" = "tcp", "port_range_min" = 443, "port_range_max" = 443, "remote_ip_prefix" = "0.0.0.0/0"}]`, empty by default |
  208. |`wait_for_floatingip` | Let Terraform poll the instance until the floating IP has been associated, `false` by default. |
  209. |`node_root_volume_size_in_gb` | Size of the root volume for nodes, 0 to use ephemeral storage |
  210. |`master_root_volume_size_in_gb` | Size of the root volume for masters, 0 to use ephemeral storage |
  211. |`master_volume_type` | Volume type of the root volume for control_plane, 'Default' by default |
  212. |`gfs_root_volume_size_in_gb` | Size of the root volume for gluster, 0 to use ephemeral storage |
  213. |`etcd_root_volume_size_in_gb` | Size of the root volume for etcd nodes, 0 to use ephemeral storage |
  214. |`bastion_root_volume_size_in_gb` | Size of the root volume for bastions, 0 to use ephemeral storage |
  215. |`use_server_group` | Create and use openstack nova servergroups, default: false |
  216. |`use_access_ip` | If 1, nodes with floating IPs will transmit internal cluster traffic via floating IPs; if 0 private IPs will be used instead. Default value is 1. |
  217. |`k8s_nodes` | Map containing worker node definition, see explanation below |
  218. ##### k8s_nodes
  219. Allows a custom definition of worker nodes giving the operator full control over individual node flavor and
  220. availability zone placement. To enable the use of this mode set the `number_of_k8s_nodes` and
  221. `number_of_k8s_nodes_no_floating_ip` variables to 0. Then define your desired worker node configuration
  222. using the `k8s_nodes` variable.
  223. For example:
  224. ```ini
  225. k8s_nodes = {
  226. "1" = {
  227. "az" = "sto1"
  228. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  229. "floating_ip" = true
  230. },
  231. "2" = {
  232. "az" = "sto2"
  233. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  234. "floating_ip" = true
  235. },
  236. "3" = {
  237. "az" = "sto3"
  238. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  239. "floating_ip" = true
  240. }
  241. }
  242. ```
  243. Would result in the same configuration as:
  244. ```ini
  245. number_of_k8s_nodes = 3
  246. flavor_k8s_node = "83d8b44a-26a0-4f02-a981-079446926445"
  247. az_list = ["sto1", "sto2", "sto3"]
  248. ```
  249. And:
  250. ```ini
  251. k8s_nodes = {
  252. "ing-1" = {
  253. "az" = "sto1"
  254. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  255. "floating_ip" = true
  256. },
  257. "ing-2" = {
  258. "az" = "sto2"
  259. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  260. "floating_ip" = true
  261. },
  262. "ing-3" = {
  263. "az" = "sto3"
  264. "flavor" = "83d8b44a-26a0-4f02-a981-079446926445"
  265. "floating_ip" = true
  266. },
  267. "big-1" = {
  268. "az" = "sto1"
  269. "flavor" = "3f73fc93-ec61-4808-88df-2580d94c1a9b"
  270. "floating_ip" = false
  271. },
  272. "big-2" = {
  273. "az" = "sto2"
  274. "flavor" = "3f73fc93-ec61-4808-88df-2580d94c1a9b"
  275. "floating_ip" = false
  276. },
  277. "big-3" = {
  278. "az" = "sto3"
  279. "flavor" = "3f73fc93-ec61-4808-88df-2580d94c1a9b"
  280. "floating_ip" = false
  281. },
  282. "small-1" = {
  283. "az" = "sto1"
  284. "flavor" = "7a6a998f-ac7f-4fb8-a534-2175b254f75e"
  285. "floating_ip" = false
  286. },
  287. "small-2" = {
  288. "az" = "sto2"
  289. "flavor" = "7a6a998f-ac7f-4fb8-a534-2175b254f75e"
  290. "floating_ip" = false
  291. },
  292. "small-3" = {
  293. "az" = "sto3"
  294. "flavor" = "7a6a998f-ac7f-4fb8-a534-2175b254f75e"
  295. "floating_ip" = false
  296. }
  297. }
  298. ```
  299. Would result in three nodes in each availability zone each with their own separate naming,
  300. flavor and floating ip configuration.
  301. The "schema":
  302. ```ini
  303. k8s_nodes = {
  304. "key | node name suffix, must be unique" = {
  305. "az" = string
  306. "flavor" = string
  307. "floating_ip" = bool
  308. },
  309. }
  310. ```
  311. All values are required.
  312. #### Terraform state files
  313. In the cluster's inventory folder, the following files might be created (either by Terraform
  314. or manually), to prevent you from pushing them accidentally they are in a
  315. `.gitignore` file in the `terraform/openstack` directory :
  316. - `.terraform`
  317. - `.tfvars`
  318. - `.tfstate`
  319. - `.tfstate.backup`
  320. You can still add them manually if you want to.
  321. ### Initialization
  322. Before Terraform can operate on your cluster you need to install the required
  323. plugins. This is accomplished as follows:
  324. ```ShellSession
  325. cd inventory/$CLUSTER
  326. terraform init ../../contrib/terraform/openstack
  327. ```
  328. This should finish fairly quickly telling you Terraform has successfully initialized and loaded necessary modules.
  329. ### Provisioning cluster
  330. You can apply the Terraform configuration to your cluster with the following command
  331. issued from your cluster's inventory directory (`inventory/$CLUSTER`):
  332. ```ShellSession
  333. terraform apply -var-file=cluster.tfvars ../../contrib/terraform/openstack
  334. ```
  335. if you chose to create a bastion host, this script will create
  336. `contrib/terraform/openstack/k8s_cluster.yml` with an ssh command for Ansible to
  337. be able to access your machines tunneling through the bastion's IP address. If
  338. you want to manually handle the ssh tunneling to these machines, please delete
  339. or move that file. If you want to use this, just leave it there, as ansible will
  340. pick it up automatically.
  341. ### Destroying cluster
  342. You can destroy your new cluster with the following command issued from the cluster's inventory directory:
  343. ```ShellSession
  344. terraform destroy -var-file=cluster.tfvars ../../contrib/terraform/openstack
  345. ```
  346. If you've started the Ansible run, it may also be a good idea to do some manual cleanup:
  347. - remove SSH keys from the destroyed cluster from your `~/.ssh/known_hosts` file
  348. - clean up any temporary cache files: `rm /tmp/$CLUSTER-*`
  349. ### Debugging
  350. You can enable debugging output from Terraform by setting
  351. `OS_DEBUG` to 1 and`TF_LOG` to`DEBUG` before running the Terraform command.
  352. ### Terraform output
  353. Terraform can output values that are useful for configure Neutron/Octavia LBaaS or Cinder persistent volume provisioning as part of your Kubernetes deployment:
  354. - `private_subnet_id`: the subnet where your instances are running is used for `openstack_lbaas_subnet_id`
  355. - `floating_network_id`: the network_id where the floating IP are provisioned is used for `openstack_lbaas_floating_network_id`
  356. ## Ansible
  357. ### Node access
  358. #### SSH
  359. Ensure your local ssh-agent is running and your ssh key has been added. This
  360. step is required by the terraform provisioner:
  361. ```ShellSession
  362. eval $(ssh-agent -s)
  363. ssh-add ~/.ssh/id_rsa
  364. ```
  365. If you have deployed and destroyed a previous iteration of your cluster, you will need to clear out any stale keys from your SSH "known hosts" file ( `~/.ssh/known_hosts`).
  366. #### Metadata variables
  367. The [python script](../terraform.py) that reads the
  368. generated`.tfstate` file to generate a dynamic inventory recognizes
  369. some variables within a "metadata" block, defined in a "resource"
  370. block (example):
  371. ```ini
  372. resource "openstack_compute_instance_v2" "example" {
  373. ...
  374. metadata {
  375. ssh_user = "ubuntu"
  376. prefer_ipv6 = true
  377. python_bin = "/usr/bin/python3"
  378. }
  379. ...
  380. }
  381. ```
  382. As the example shows, these let you define the SSH username for
  383. Ansible, a Python binary which is needed by Ansible if
  384. `/usr/bin/python` doesn't exist, and whether the IPv6 address of the
  385. instance should be preferred over IPv4.
  386. #### Bastion host
  387. Bastion access will be determined by:
  388. - Your choice on the amount of bastion hosts (set by `number_of_bastions` terraform variable).
  389. - The existence of nodes/masters with floating IPs (set by `number_of_k8s_masters`, `number_of_k8s_nodes`, `number_of_k8s_masters_no_etcd` terraform variables).
  390. If you have a bastion host, your ssh traffic will be directly routed through it. This is regardless of whether you have masters/nodes with a floating IP assigned.
  391. If you don't have a bastion host, but at least one of your masters/nodes have a floating IP, then ssh traffic will be tunneled by one of these machines.
  392. So, either a bastion host, or at least master/node with a floating IP are required.
  393. #### Test access
  394. Make sure you can connect to the hosts. Note that Flatcar Container Linux by Kinvolk will have a state `FAILED` due to Python not being present. This is okay, because Python will be installed during bootstrapping, so long as the hosts are not `UNREACHABLE`.
  395. ```ShellSession
  396. $ ansible -i inventory/$CLUSTER/hosts -m ping all
  397. example-k8s_node-1 | SUCCESS => {
  398. "changed": false,
  399. "ping": "pong"
  400. }
  401. example-etcd-1 | SUCCESS => {
  402. "changed": false,
  403. "ping": "pong"
  404. }
  405. example-k8s-master-1 | SUCCESS => {
  406. "changed": false,
  407. "ping": "pong"
  408. }
  409. ```
  410. If it fails try to connect manually via SSH. It could be something as simple as a stale host key.
  411. ### Configure cluster variables
  412. Edit `inventory/$CLUSTER/group_vars/all/all.yml`:
  413. - **bin_dir**:
  414. ```yml
  415. # Directory where the binaries will be installed
  416. # Default:
  417. # bin_dir: /usr/local/bin
  418. # For Flatcar Container Linux by Kinvolk:
  419. bin_dir: /opt/bin
  420. ```
  421. - and **cloud_provider**:
  422. ```yml
  423. cloud_provider: openstack
  424. ```
  425. Edit `inventory/$CLUSTER/group_vars/k8s_cluster/k8s_cluster.yml`:
  426. - Set variable **kube_network_plugin** to your desired networking plugin.
  427. - **flannel** works out-of-the-box
  428. - **calico** requires [configuring OpenStack Neutron ports](/docs/openstack.md) to allow service and pod subnets
  429. ```yml
  430. # Choose network plugin (calico, weave or flannel)
  431. # Can also be set to 'cloud', which lets the cloud provider setup appropriate routing
  432. kube_network_plugin: flannel
  433. ```
  434. - Set variable **resolvconf_mode**
  435. ```yml
  436. # Can be docker_dns, host_resolvconf or none
  437. # Default:
  438. # resolvconf_mode: docker_dns
  439. # For Flatcar Container Linux by Kinvolk:
  440. resolvconf_mode: host_resolvconf
  441. ```
  442. - Set max amount of attached cinder volume per host (default 256)
  443. ```yml
  444. node_volume_attach_limit: 26
  445. ```
  446. ### Deploy Kubernetes
  447. ```ShellSession
  448. ansible-playbook --become -i inventory/$CLUSTER/hosts cluster.yml
  449. ```
  450. This will take some time as there are many tasks to run.
  451. ## Kubernetes
  452. ### Set up kubectl
  453. 1. [Install kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) on your workstation
  454. 2. Add a route to the internal IP of a master node (if needed):
  455. ```ShellSession
  456. sudo route add [master-internal-ip] gw [router-ip]
  457. ```
  458. or
  459. ```ShellSession
  460. sudo route add -net [internal-subnet]/24 gw [router-ip]
  461. ```
  462. 1. List Kubernetes certificates & keys:
  463. ```ShellSession
  464. ssh [os-user]@[master-ip] sudo ls /etc/kubernetes/ssl/
  465. ```
  466. 1. Get `admin`'s certificates and keys:
  467. ```ShellSession
  468. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/admin-kube-master-1-key.pem > admin-key.pem
  469. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/admin-kube-master-1.pem > admin.pem
  470. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/ca.pem > ca.pem
  471. ```
  472. 1. Configure kubectl:
  473. ```ShellSession
  474. $ kubectl config set-cluster default-cluster --server=https://[master-internal-ip]:6443 \
  475. --certificate-authority=ca.pem
  476. $ kubectl config set-credentials default-admin \
  477. --certificate-authority=ca.pem \
  478. --client-key=admin-key.pem \
  479. --client-certificate=admin.pem
  480. $ kubectl config set-context default-system --cluster=default-cluster --user=default-admin
  481. $ kubectl config use-context default-system
  482. ```
  483. 1. Check it:
  484. ```ShellSession
  485. kubectl version
  486. ```
  487. ## GlusterFS
  488. GlusterFS is not deployed by the standard `cluster.yml` playbook, see the
  489. [GlusterFS playbook documentation](../../network-storage/glusterfs/README.md)
  490. for instructions.
  491. Basically you will install Gluster as
  492. ```ShellSession
  493. ansible-playbook --become -i inventory/$CLUSTER/hosts ./contrib/network-storage/glusterfs/glusterfs.yml
  494. ```
  495. ## What's next
  496. Try out your new Kubernetes cluster with the [Hello Kubernetes service](https://kubernetes.io/docs/tasks/access-application-cluster/service-access-application-cluster/).
  497. ## Appendix
  498. ### Migration from `number_of_k8s_nodes*` to `k8s_nodes`
  499. If you currently have a cluster defined using the `number_of_k8s_nodes*` variables and wish
  500. to migrate to the `k8s_nodes` style you can do it like so:
  501. ```ShellSession
  502. $ terraform state list
  503. module.compute.data.openstack_images_image_v2.gfs_image
  504. module.compute.data.openstack_images_image_v2.vm_image
  505. module.compute.openstack_compute_floatingip_associate_v2.k8s_master[0]
  506. module.compute.openstack_compute_floatingip_associate_v2.k8s_node[0]
  507. module.compute.openstack_compute_floatingip_associate_v2.k8s_node[1]
  508. module.compute.openstack_compute_floatingip_associate_v2.k8s_node[2]
  509. module.compute.openstack_compute_instance_v2.k8s_master[0]
  510. module.compute.openstack_compute_instance_v2.k8s_node[0]
  511. module.compute.openstack_compute_instance_v2.k8s_node[1]
  512. module.compute.openstack_compute_instance_v2.k8s_node[2]
  513. module.compute.openstack_compute_keypair_v2.k8s
  514. module.compute.openstack_compute_servergroup_v2.k8s_etcd[0]
  515. module.compute.openstack_compute_servergroup_v2.k8s_master[0]
  516. module.compute.openstack_compute_servergroup_v2.k8s_node[0]
  517. module.compute.openstack_networking_secgroup_rule_v2.bastion[0]
  518. module.compute.openstack_networking_secgroup_rule_v2.egress[0]
  519. module.compute.openstack_networking_secgroup_rule_v2.k8s
  520. module.compute.openstack_networking_secgroup_rule_v2.k8s_allowed_remote_ips[0]
  521. module.compute.openstack_networking_secgroup_rule_v2.k8s_allowed_remote_ips[1]
  522. module.compute.openstack_networking_secgroup_rule_v2.k8s_allowed_remote_ips[2]
  523. module.compute.openstack_networking_secgroup_rule_v2.k8s_master[0]
  524. module.compute.openstack_networking_secgroup_rule_v2.worker[0]
  525. module.compute.openstack_networking_secgroup_rule_v2.worker[1]
  526. module.compute.openstack_networking_secgroup_rule_v2.worker[2]
  527. module.compute.openstack_networking_secgroup_rule_v2.worker[3]
  528. module.compute.openstack_networking_secgroup_rule_v2.worker[4]
  529. module.compute.openstack_networking_secgroup_v2.bastion[0]
  530. module.compute.openstack_networking_secgroup_v2.k8s
  531. module.compute.openstack_networking_secgroup_v2.k8s_master
  532. module.compute.openstack_networking_secgroup_v2.worker
  533. module.ips.null_resource.dummy_dependency
  534. module.ips.openstack_networking_floatingip_v2.k8s_master[0]
  535. module.ips.openstack_networking_floatingip_v2.k8s_node[0]
  536. module.ips.openstack_networking_floatingip_v2.k8s_node[1]
  537. module.ips.openstack_networking_floatingip_v2.k8s_node[2]
  538. module.network.openstack_networking_network_v2.k8s[0]
  539. module.network.openstack_networking_router_interface_v2.k8s[0]
  540. module.network.openstack_networking_router_v2.k8s[0]
  541. module.network.openstack_networking_subnet_v2.k8s[0]
  542. $ terraform state mv 'module.compute.openstack_compute_floatingip_associate_v2.k8s_node[0]' 'module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes["1"]'
  543. Move "module.compute.openstack_compute_floatingip_associate_v2.k8s_node[0]" to "module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes[\"1\"]"
  544. Successfully moved 1 object(s).
  545. $ terraform state mv 'module.compute.openstack_compute_floatingip_associate_v2.k8s_node[1]' 'module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes["2"]'
  546. Move "module.compute.openstack_compute_floatingip_associate_v2.k8s_node[1]" to "module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes[\"2\"]"
  547. Successfully moved 1 object(s).
  548. $ terraform state mv 'module.compute.openstack_compute_floatingip_associate_v2.k8s_node[2]' 'module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes["3"]'
  549. Move "module.compute.openstack_compute_floatingip_associate_v2.k8s_node[2]" to "module.compute.openstack_compute_floatingip_associate_v2.k8s_nodes[\"3\"]"
  550. Successfully moved 1 object(s).
  551. $ terraform state mv 'module.compute.openstack_compute_instance_v2.k8s_node[0]' 'module.compute.openstack_compute_instance_v2.k8s_node["1"]'
  552. Move "module.compute.openstack_compute_instance_v2.k8s_node[0]" to "module.compute.openstack_compute_instance_v2.k8s_node[\"1\"]"
  553. Successfully moved 1 object(s).
  554. $ terraform state mv 'module.compute.openstack_compute_instance_v2.k8s_node[1]' 'module.compute.openstack_compute_instance_v2.k8s_node["2"]'
  555. Move "module.compute.openstack_compute_instance_v2.k8s_node[1]" to "module.compute.openstack_compute_instance_v2.k8s_node[\"2\"]"
  556. Successfully moved 1 object(s).
  557. $ terraform state mv 'module.compute.openstack_compute_instance_v2.k8s_node[2]' 'module.compute.openstack_compute_instance_v2.k8s_node["3"]'
  558. Move "module.compute.openstack_compute_instance_v2.k8s_node[2]" to "module.compute.openstack_compute_instance_v2.k8s_node[\"3\"]"
  559. Successfully moved 1 object(s).
  560. $ terraform state mv 'module.ips.openstack_networking_floatingip_v2.k8s_node[0]' 'module.ips.openstack_networking_floatingip_v2.k8s_node["1"]'
  561. Move "module.ips.openstack_networking_floatingip_v2.k8s_node[0]" to "module.ips.openstack_networking_floatingip_v2.k8s_node[\"1\"]"
  562. Successfully moved 1 object(s).
  563. $ terraform state mv 'module.ips.openstack_networking_floatingip_v2.k8s_node[1]' 'module.ips.openstack_networking_floatingip_v2.k8s_node["2"]'
  564. Move "module.ips.openstack_networking_floatingip_v2.k8s_node[1]" to "module.ips.openstack_networking_floatingip_v2.k8s_node[\"2\"]"
  565. Successfully moved 1 object(s).
  566. $ terraform state mv 'module.ips.openstack_networking_floatingip_v2.k8s_node[2]' 'module.ips.openstack_networking_floatingip_v2.k8s_node["3"]'
  567. Move "module.ips.openstack_networking_floatingip_v2.k8s_node[2]" to "module.ips.openstack_networking_floatingip_v2.k8s_node[\"3\"]"
  568. Successfully moved 1 object(s).
  569. ```
  570. Of course for nodes without floating ips those steps can be omitted.