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.

406 lines
14 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. ## Approach
  8. The terraform configuration inspects variables found in
  9. [variables.tf](variables.tf) to create resources in your OpenStack cluster.
  10. There is a [python script](../terraform.py) that reads the generated`.tfstate`
  11. file to generate a dynamic inventory that is consumed by the main ansible script
  12. to actually install kubernetes and stand up the cluster.
  13. ### Networking
  14. The configuration includes creating a private subnet with a router to the
  15. external net. It will allocate floating-ips from a pool and assign them to the
  16. hosts where that makes sense. You have the option of creating bastion hosts
  17. inside the private subnet to access the nodes there.
  18. ### Kubernetes Nodes
  19. You can create many different kubernetes topologies by setting the number of
  20. different classes of hosts. For each class there are options for allocating
  21. floating ip addresses or not.
  22. - Master Nodes with etcd
  23. - Master nodes without etcd
  24. - Standalone etcd hosts
  25. - Kubernetes worker nodes
  26. Note that the ansible script will report an invalid configuration if you wind up
  27. with an even number of etcd instances since that is not a valid configuration.
  28. ### Gluster FS
  29. The terraform configuration supports provisioning of an optional GlusterFS
  30. shared file system based on a separate set of VMs. To enable this, you need to
  31. specify
  32. - the number of gluster hosts
  33. - Size of the non-ephemeral volumes to be attached to store the GlusterFS bricks
  34. - Other properties related to provisioning the hosts
  35. Even if you are using Container Linux by CoreOS for your cluster, you will still
  36. need the GlusterFS VMs to be based on either Debian or RedHat based images,
  37. Container Linux by CoreOS cannot serve GlusterFS, but can connect to it through
  38. binaries available on hyperkube v1.4.3_coreos.0 or higher.
  39. ## Requirements
  40. - [Install Terraform](https://www.terraform.io/intro/getting-started/install.html)
  41. - [Install Ansible](http://docs.ansible.com/ansible/latest/intro_installation.html)
  42. - you already have a suitable OS image in glance
  43. - you already have a floating-ip pool created
  44. - you have security-groups enabled
  45. - you have a pair of keys generated that can be used to secure the new hosts
  46. ## Module Architecture
  47. The configuration is divided into three modules:
  48. - Network
  49. - IPs
  50. - Compute
  51. The main reason for splitting the configuration up in this way is to easily
  52. accommodate situations where floating IPs are limited by a quota or if you have
  53. any external references to the floating IP (e.g. DNS) that would otherwise have
  54. to be updated.
  55. You can force your existing IPs by modifying the compute variables in
  56. `kubespray.tf` as
  57. ```
  58. k8s_master_fips = ["151.101.129.67"]
  59. k8s_node_fips = ["151.101.129.68"]
  60. ```
  61. ## Terraform
  62. Terraform will be used to provision all of the OpenStack resources. It is also
  63. used to deploy and provision the software requirements.
  64. ### Prep
  65. #### OpenStack
  66. No provider variables are hard coded inside `variables.tf` because Terraform
  67. supports various authentication method for OpenStack, between identity v2 and
  68. v3 API, `openrc` or `clouds.yaml`.
  69. These are examples and may vary depending on your OpenStack cloud provider,
  70. for an exhaustive list on how to authenticate on OpenStack with Terraform
  71. please read the [OpenStack provider documentation](https://www.terraform.io/docs/providers/openstack/).
  72. ##### Recommended method : clouds.yaml
  73. Newer recommended authentication method is to use a `clouds.yaml` file that can be store in :
  74. * `Current Directory`
  75. * `~/.config/openstack`
  76. * `/etc/openstack`
  77. `clouds.yaml` :
  78. ```
  79. clouds:
  80. mycloud:
  81. auth:
  82. auth_url: https://openstack:5000/v3
  83. username: "username"
  84. project_name: "projectname"
  85. project_id: projectid
  86. user_domain_name: "Default"
  87. password: "password"
  88. region_name: "RegionOne"
  89. interface: "public"
  90. identity_api_version: 3
  91. ```
  92. If you have multiple clouds defined in your `clouds.yaml` file you can choose
  93. the one you want to use with the environment variable `OS_CLOUD` :
  94. ```
  95. export OS_CLOUD=mycloud
  96. ```
  97. ##### Deprecated method : openrc
  98. When using classic environment variables, Terraform uses default `OS_*`
  99. environment variables :
  100. With identity v2 :
  101. ```
  102. source openrc
  103. env | grep OS
  104. OS_AUTH_URL=https://openstack:5000/v2.0
  105. OS_PROJECT_ID=projectid
  106. OS_PROJECT_NAME=projectname
  107. OS_USERNAME=username
  108. OS_PASSWORD=password
  109. OS_REGION_NAME=RegionOne
  110. OS_INTERFACE=public
  111. OS_IDENTITY_API_VERSION=2
  112. ```
  113. With identity v3 :
  114. ```
  115. source openrc
  116. env | grep OS
  117. OS_AUTH_URL=https://openstack:5000/v3
  118. OS_PROJECT_ID=projectid
  119. OS_PROJECT_NAME=username
  120. OS_PROJECT_DOMAIN_ID=default
  121. OS_USERNAME=username
  122. OS_PASSWORD=password
  123. OS_REGION_NAME=RegionOne
  124. OS_INTERFACE=public
  125. OS_IDENTITY_API_VERSION=3
  126. OS_USER_DOMAIN_NAME=Default
  127. ```
  128. Terraform does not support a mix of DomainName and DomainID, choose one or the
  129. other :
  130. ```
  131. * provider.openstack: You must provide exactly one of DomainID or DomainName to authenticate by Username
  132. ```
  133. ```
  134. unset OS_USER_DOMAIN_NAME
  135. export OS_USER_DOMAIN_ID=default
  136. or
  137. unset OS_PROJECT_DOMAIN_ID
  138. set OS_PROJECT_DOMAIN_NAME=Default
  139. ```
  140. ### Terraform Variables
  141. The construction of the cluster is driven by values found in
  142. [variables.tf](variables.tf).
  143. The best way to set these values is to create a file in the project's root
  144. directory called something like`my-terraform-vars.tfvars`. Many of the
  145. variables are obvious. Here is a summary of some of the more interesting
  146. ones:
  147. |Variable | Description |
  148. |---------|-------------|
  149. |`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`. |
  150. |`network_name` | The name to be given to the internal network that will be generated |
  151. |`dns_nameservers`| An array of DNS name server names to be used by hosts in the internal subnet. |
  152. |`floatingip_pool` | Name of the pool from which floating IPs will be allocated |
  153. |`external_net` | UUID of the external network that will be routed to |
  154. |`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`nova flavor-list` |
  155. |`image`,`image_gfs` | Name of the image to use in provisioning the compute resources. Should already be loaded into glance. |
  156. |`ssh_user`,`ssh_user_gfs` | The username to ssh into the image with. This usually depends on the image you have selected |
  157. |`public_key_path` | Path on your local workstation to the public key file you wish to use in creating the key pairs |
  158. |`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|
  159. |`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 |
  160. |`number_of_etcd` | Number of pure etcd nodes |
  161. |`number_of_k8s_nodes`, `number_of_k8s_nodes_no_floating_ip` | Kubernetes worker nodes. These can be provisioned with or without floating ip addresses. |
  162. |`number_of_bastions` | Number of bastion hosts to create. Scripts assume this is really just zero or one |
  163. |`number_of_gfs_nodes_no_floating_ip` | Number of gluster servers to provision. |
  164. | `gfs_volume_size_in_gb` | Size of the non-ephemeral volumes to be attached to store the GlusterFS bricks |
  165. ### Terraform files
  166. In the root folder, the following files might be created (either by Terraform
  167. or manually), to prevent you from pushing them accidentally they are in a
  168. `.gitignore` file in the `terraform/openstack` directory :
  169. * `.terraform`
  170. * `.tfvars`
  171. * `.tfstate`
  172. * `.tfstate.backup`
  173. You can still add them manually if you want to.
  174. ## Initializing Terraform
  175. Before Terraform can operate on your cluster you need to install required
  176. plugins. This is accomplished with the command
  177. ```bash
  178. $ terraform init contrib/terraform/openstack
  179. ```
  180. ## Provisioning Cluster with Terraform
  181. You can apply the terraform config to your cluster with the following command
  182. issued from the project's root directory
  183. ```bash
  184. $ terraform apply -state=contrib/terraform/openstack/terraform.tfstate -var-file=my-terraform-vars.tfvars contrib/terraform/openstack
  185. ```
  186. if you chose to create a bastion host, this script will create
  187. `contrib/terraform/openstack/k8s-cluster.yml` with an ssh command for ansible to
  188. be able to access your machines tunneling through the bastion's ip adress. If
  189. you want to manually handle the ssh tunneling to these machines, please delete
  190. or move that file. If you want to use this, just leave it there, as ansible will
  191. pick it up automatically.
  192. ## Destroying Cluster with Terraform
  193. You can destroy a config deployed to your cluster with the following command
  194. issued from the project's root directory
  195. ```bash
  196. $ terraform destroy -state=contrib/terraform/openstack/terraform.tfstate -var-file=my-terraform-vars.tfvars contrib/terraform/openstack
  197. ```
  198. ## Debugging Cluster Provisioning
  199. You can enable debugging output from Terraform by setting
  200. `OS_DEBUG` to 1 and`TF_LOG` to`DEBUG` before runing the terraform command
  201. ## Terraform output
  202. Terraform can output useful values that need to be reused if you want to use Kubernetes OpenStack cloud provider with Neutron/Octavia LBaaS or Cinder persistent Volume provisioning:
  203. - `private_subnet_id`: the subnet where your instances are running, maps to `openstack_lbaas_subnet_id`
  204. - `floating_network_id`: the network_id where the floating IP are provisioned, maps to `openstack_lbaas_floating_network_id`
  205. # Running the Ansible Script
  206. Ensure your local ssh-agent is running and your ssh key has been added. This
  207. step is required by the terraform provisioner:
  208. ```
  209. $ eval $(ssh-agent -s)
  210. $ ssh-add ~/.ssh/id_rsa
  211. ```
  212. Make sure you can connect to the hosts:
  213. ```
  214. $ ansible -i contrib/terraform/openstack/hosts -m ping all
  215. example-k8s_node-1 | SUCCESS => {
  216. "changed": false,
  217. "ping": "pong"
  218. }
  219. example-etcd-1 | SUCCESS => {
  220. "changed": false,
  221. "ping": "pong"
  222. }
  223. example-k8s-master-1 | SUCCESS => {
  224. "changed": false,
  225. "ping": "pong"
  226. }
  227. ```
  228. if you are deploying a system that needs bootstrapping, like Container Linux by
  229. CoreOS, these might have a state`FAILED` due to Container Linux by CoreOS not
  230. having python. As long as the state is not`UNREACHABLE`, this is fine.
  231. if it fails try to connect manually via SSH ... it could be something as simple as a stale host key.
  232. ## Configure Cluster variables
  233. Edit `inventory/sample/group_vars/all.yml`:
  234. - Set variable **bootstrap_os** according selected image
  235. ```
  236. # Valid bootstrap options (required): ubuntu, coreos, centos, none
  237. bootstrap_os: coreos
  238. ```
  239. - **bin_dir**
  240. ```
  241. # Directory where the binaries will be installed
  242. # Default:
  243. # bin_dir: /usr/local/bin
  244. # For Container Linux by CoreOS:
  245. bin_dir: /opt/bin
  246. ```
  247. - and **cloud_provider**
  248. ```
  249. cloud_provider: openstack
  250. ```
  251. Edit `inventory/sample/group_vars/k8s-cluster.yml`:
  252. - Set variable **kube_network_plugin** according selected networking
  253. ```
  254. # Choose network plugin (calico, weave or flannel)
  255. # Can also be set to 'cloud', which lets the cloud provider setup appropriate routing
  256. kube_network_plugin: flannel
  257. ```
  258. > flannel works out-of-the-box
  259. > calico requires allowing service's and pod's subnets on according OpenStack Neutron ports
  260. - Set variable **resolvconf_mode**
  261. ```
  262. # Can be docker_dns, host_resolvconf or none
  263. # Default:
  264. # resolvconf_mode: docker_dns
  265. # For Container Linux by CoreOS:
  266. resolvconf_mode: host_resolvconf
  267. ```
  268. For calico configure OpenStack Neutron ports: [OpenStack](/docs/openstack.md)
  269. ## Deploy kubernetes:
  270. ```
  271. $ ansible-playbook --become -i contrib/terraform/openstack/hosts cluster.yml
  272. ```
  273. ## Set up local kubectl
  274. 1. Install kubectl on your workstation:
  275. [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
  276. 2. Add route to internal IP of master node (if needed):
  277. ```
  278. sudo route add [master-internal-ip] gw [router-ip]
  279. ```
  280. or
  281. ```
  282. sudo route add -net [internal-subnet]/24 gw [router-ip]
  283. ```
  284. 3. List Kubernetes certs&keys:
  285. ```
  286. ssh [os-user]@[master-ip] sudo ls /etc/kubernetes/ssl/
  287. ```
  288. 4. Get admin's certs&key:
  289. ```
  290. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/admin-[cluster_name]-k8s-master-1-key.pem > admin-key.pem
  291. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/admin-[cluster_name]-k8s-master-1.pem > admin.pem
  292. ssh [os-user]@[master-ip] sudo cat /etc/kubernetes/ssl/ca.pem > ca.pem
  293. ```
  294. 5. Configure kubectl:
  295. ```
  296. kubectl config set-cluster default-cluster --server=https://[master-internal-ip]:6443 \
  297. --certificate-authority=ca.pem
  298. kubectl config set-credentials default-admin \
  299. --certificate-authority=ca.pem \
  300. --client-key=admin-key.pem \
  301. --client-certificate=admin.pem
  302. kubectl config set-context default-system --cluster=default-cluster --user=default-admin
  303. kubectl config use-context default-system
  304. ```
  305. 7. Check it:
  306. ```
  307. kubectl version
  308. ```
  309. If you are using floating ip addresses then you may get this error:
  310. ```
  311. Unable to connect to the server: x509: certificate is valid for 10.0.0.6, 10.0.0.6, 10.233.0.1, 127.0.0.1, not 132.249.238.25
  312. ```
  313. You can tell kubectl to ignore this condition by adding the
  314. `--insecure-skip-tls-verify` option.
  315. ## GlusterFS
  316. GlusterFS is not deployed by the standard`cluster.yml` playbook, see the
  317. [glusterfs playbook documentation](../../network-storage/glusterfs/README.md)
  318. for instructions.
  319. Basically you will install gluster as
  320. ```bash
  321. $ ansible-playbook --become -i contrib/terraform/openstack/hosts ./contrib/network-storage/glusterfs/glusterfs.yml
  322. ```
  323. # What's next
  324. [Start Hello Kubernetes Service](https://kubernetes.io/docs/tasks/access-application-cluster/service-access-application-cluster/)