├── .gitignore ├── LICENSE ├── README.md ├── examples └── terraform │ ├── .gitignore │ ├── README.md │ ├── aws │ ├── README.md │ ├── main.tf │ ├── modules │ │ ├── common │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── master │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── msr │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── vpc │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── windows_worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── run.sh │ ├── ssh_keys │ │ └── .gitkeep │ ├── terraform.tfvars.example │ ├── variables.tf │ └── versions.tf │ ├── azure │ ├── README.md │ ├── main.tf │ ├── modules │ │ ├── common │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── master │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── vnet │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── windows_worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── ssh_keys │ │ └── .gitkeep │ ├── terraform.tfvars.example │ ├── variables.tf │ └── versions.tf │ ├── gcp │ ├── README.md │ ├── main.tf │ ├── modules │ │ ├── common │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── manager │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── msr │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── networklb │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── variables.tf │ │ │ └── versions.tf │ │ ├── vpc │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── windows_worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── worker │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── run.sh │ ├── terraform.tfvars.example │ ├── variables.tf │ └── versions.tf │ ├── hetzner │ ├── README.md │ ├── main.tf │ └── terraform.tfvars.example │ ├── openstack │ ├── README.md │ ├── main.tf │ ├── modules │ │ ├── masters │ │ │ ├── lb.tf │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── security_group.tf │ │ │ ├── variables.tf │ │ │ └── volumes.tf │ │ ├── network │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── security_group.tf │ │ │ └── variables.tf │ │ └── workers │ │ │ ├── lb.tf │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── security_group.tf │ │ │ ├── variables.tf │ │ │ └── volumes.tf │ ├── output.tf │ ├── provider_config │ │ └── cloud_provider.conf.example │ ├── terraform.tfvars.example │ └── variables.tf │ └── vmware │ ├── README.md │ ├── main.tf │ ├── modules │ └── virtual_machine │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── outputs.tf │ ├── terraform.tfvars.example │ └── variables.tf └── images ├── launchpad-basic-setup.png └── launchpad-screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mirantis Launchpad Software Evaluation License Agreement 2 | 3 | Published: May 27, 2020 4 | 5 | PLEASE READ THIS AGREEMENT CAREFULLY BEFORE USING ACCESSING OR CONSUMING THE SOFTWARE OR SERVICES. BY CLICKING YOUR ASSENT OR USING, ACCESSING OR CONSUMING SOFTWARE OR SERVICES, YOU SIGNIFY YOUR ASSENT TO AND ACCEPTANCE OF THIS AGREEMENT AND ACKNOWLEDGE YOU HAVE READ AND UNDERSTAND THE TERMS. AN INDIVIDUAL ACTING ON BEHALF OF AN ENTITY REPRESENTS THAT THEY HAVE THE AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT ACCEPT THE TERMS OF THIS AGREEMENT, THEN DO NOT USE THE SOFTWARE OR SERVICES. 6 | 7 | This Mirantis Launchpad Software Evaluation License Agreement (“Agreement”) is entered into by and between Mirantis, Inc., a Delaware corporation, with offices at 900 E. Hamilton Ave., Ste. 650, Campbell, CA 95008, USA (“Mirantis”) and the person or entity downloading or using the software or services (“Customer”). 8 | 9 | 1. License 10 | 11 | 1.1. Mirantis Launchpad Software License. Subject to Customer’s compliance with the terms and conditions of the Agreement, Mirantis hereby grants Customer a limited, non-exclusive, non-sublicensable, revocable license to use Mirantis Launchpad Software solely for Evaluation Use. “Mirantis Launchpad Software” means the Mirantis Launchpad Software command line interface tool made available by Mirantis, primarily from www.mirantis.com/launchpad, for the deployment and upgrade of Docker Enterprise Software. “Evaluation Use” means Customer’s non-production and internal evaluation purposes. Please note that “non-production” purposes does not include development if for production purposes. “Docker Enterprise Software” means the Licensed Software and Open Source Software provided under the Docker Enterprise Software Evaluation Agreement at https://www.mirantis.com/wp- 12 | content/uploads/2020/05/Mirantis_Docker-Enterprise-Software-Evaluation-Agreement-Launchpad_052020.pdf (“Docker Enterprise Software Evaluation Agreement”) and deployed by Mirantis Launchpad Software. 13 | 14 | 1.2. Docker Enterprise Software License. Docker Enterprise Software deployed and upgraded using Mirantis Launchpad Software is subject to the terms and conditions of the separate Docker Enterprise Software Evaluation Agreement. Customer agrees to the terms and conditions of the Docker Enterprise Software Evaluation Agreement. 15 | 16 | 2. Usage Data and Health Metrics. Mirantis Launchpad Software monitors and collects usage data and health metrics to support Mirantis product improvement and enhance the Customer experience. By using the software you acknowledge and agree that Mirantis will process your personal data according to the Privacy Policy at https://www.mirantis.com/company/privacy-policy/. IF YOU DO NOT AGREE OR “OPT-OUT”, DO NOT DOWNLOAD OR USE THE SOFTWARE. 17 | 18 | 3. Records, Reporting, and Inspections. During the term of the Agreement and for at least two (2) years thereafter, Customer will keep and maintain commercially reasonable written records and accounts regarding Customer’s use of Mirantis Launchpad Software ("Records"). During the Agreement term and for 2 years thereafter, Mirantis may inspect Customer’s records and facilities to verify Customer’s compliance with the Agreement during normal business hours and with at least 15 days’ notice. Mirantis may charge fees to Customer for any noncompliance. 19 | 20 | 4. Term and Termination 21 | 22 | 4.1. Term. This Agreement commences on the day Customer assents to this Agreement or uses, access, or consumes the software or services (“Effective Date”) and ends the first of (i) Termination under Section 4.2, or (ii) Customer’s noncompliance with Section 1 (“Term”). 23 | 24 | 4.2. Termination. Mirantis may terminate this Agreement with 10 days’ notice to Customer. Either party may terminate this Agreement immediately upon notice to the other party if the other party materially breaches an obligation hereunder that has not been cured within 15 days after receipt of written notice from the non-defaulting party provided, except that such notice and cure will not be required for a breach of Section 5 (Mirantis Launchpad Software License), 5 ( Confidentiality) or 8.3 (Export). 25 | 26 | 4.3. Effect of Termination. Sections 3, 4.3, & 5 through 7 will survive the termination or expiration of this Agreement. Termination of this Agreement by either party will not act as a waiver of any breach of this Agreement and will not act as a release of either party from any liability for breach of such party’s obligations under this Agreement. Neither party will be liable to the other for damages of any kind solely as a result of terminating this Agreement in accordance with its terms, and termination of this Agreement by a party will be without prejudice to any other right or remedy of such party under this Agreement or applicable law. Upon the expiration or termination of the Agreement all licenses granted herein will automatically terminate and Customer will discontinue all use of the software and will return to Mirantis any materials (including any copies of software) provided by Mirantis to Customer. 27 | 28 | 5. Confidentiality. Each party receiving Confidential Information (“Recipient”) from the party disclosing such information (“Discloser”) shall use Confidential Information solely for the purpose of providing and receiving Software and services under this Agreement. "Confidential Information" means information provided by the Discloser that is reasonably marked as “confidential”, identified as confidential at the time of disclosure, or reasonably known by Recipient to be confidential or should reasonably be expected to be known as confidential. Recipient acknowledges and agrees that the disclosure of the Confidential Information does not confer any license, interest, or rights of any kind in or to the Confidential Information except as provided herein. For 2 years after the termination of this Agreement Recipient shall hold Confidential Information in confidence and not disclose or use the Confidential Information, directly or indirectly, in any form, by any means, or for any purpose. Recipient shall only disclose the Confidential Information to its employees, contractors, and Affiliates to the extent such persons have a need to know such information for the purposes described in this Agreement, and provided such parties shall be obligated in writing to comply with terms and conditions no less protective than those set forth in this Section. “Affiliate” means an entity that owns or controls (has the direct or indirect power to direct or cause the direction of management and policies of an entity), is owned or controlled by, or is under the common control or ownership with a part. Recipient shall protect the Confidential Information using the same degree of care, but no less than a reasonable degree of care, to prevent the unauthorized use or disclosure of the Confidential Information as Recipient uses to protect its own confidential information. Recipient shall notify the Discloser in writing immediately upon discovery of any unauthorized use or disclosure of Confidential Information or any other breach of these confidentiality obligations and shall reasonably cooperate with the Discloser to regain possession of such Confidential Information and prevent further unauthorized use and disclosure. Confidential Information does not include information that: (a) is or becomes generally publicly available through no fault of Recipient, (b) was known to Recipient, free of any confidentiality obligations, before its disclosure, (c) becomes known to Recipient, free of any confidentiality obligations, from a source other than Discloser, (d) is independently developed by Recipient without use of Confidential Information, (e) is licensed under an open source license, or (f) is disclosed by Recipient pursuant to a requirement of a governmental agency or by operation of law, provided that Recipient shall notify Discloser prior to disclosure (if it can do so without violating any law or rule) in order to give Discloser a reasonable opportunity to seek an appropriate protective order or similar protection(s). 29 | 30 | 6. Disclaimers 31 | 32 | 6.1. Disclaimer of Warranty. THE SERVICES AND SOFTWARE ARE PROVIDED “AS IS” AND WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. MIRANTIS DOES NOT GUARANTEE OR WARRANT THAT THE USE OF THE SERVICES OR SOFTWARE WILL BE UNINTERRUPTED, COMPLY WITH REGULATORY REQUIREMENTS, BE ERROR FREE OR THAT MIRANTIS WILL CORRECT ALL SOFTWARE ERRORS. FOR THE BREACH OF THE WARRANTIES SET FORTH IN THIS SECTION CUSTOMER'S EXCLUSIVE REMEDY, AND MIRANTIS’ ENTIRE LIABILITY, WILL BE THE REPERFORMANCE OF DEFICIENT SERVICES, OR IF MIRANTIS CANNOT SUBSTANTIALLY CORRECT A BREACH IN A COMMERCIALLY REASONABLE MANNER, CUSTOMER MAY TERMINATE THE RELEVANT SERVICES AND RECEIVE A PRO-RATA REFUND OF THE FEES PAID FOR THE DEFICIENT SERVICES AS OF THE EFFECTIVE DATE OF TERMINATION. Without limiting the generality of the foregoing disclaimer, Customer acknowledges that the Software and Services are not specifically designed, manufactured or intended for use in any life support systems; planning, construction, maintenance, control, or direct operation of nuclear facilities; or navigation, control or communication systems, or weapons systems. 33 | 34 | 6.2. Disclaimer of Damages. IN NO EVENT SHALL EITHER PARTY BE LIABLE FOR SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND RELATING TO THIS AGREEMENT, SOFTWARE, SERVICES OR THE USE THEREOF, INCLUDING, WITHOUT LIMITATION, ANY INCIDENTAL, CONSEQUENTIAL, SPECIAL, INDIRECT, EXEMPLARY OR PUNITIVE DAMAGES, WHETHER ARISING IN TORT, CONTRACT, OR OTHERWISE; OR ANY DAMAGES ARISING OUT OF OR IN CONNECTION WITH ANY MALFUNCTIONS, REGULATORY NONCOMPLIANCE, DELAYS, LOSS OF DATA, LOST PROFITS, LOST SAVINGS, INTERRUPTION OF SERVICE, LOSS OF BUSINESS OR ANTICIPATORY PROFITS, WHETHER OR NOT FORESEEABLE, EVEN IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 35 | 36 | 7. Limitation of Liability. IN NO EVENT WILL MIRANTIS CUMULATIVE AND AGGREGATE LIABILITY UNDER THIS AGREEMENT EXCEED THE GREATER OF $1,000.00 OR THE AMOUNTS PAID BY CUSTOMER UNDER THIS AGREEMENT DURING THE TWELVE (12) MONTH PERIOD IMMEDIATELY PRECEDING THE EVENT GIVING RISE TO THE LIABILITY. THE LIMITATIONS HEREIN WILL REMAIN IN FULL FORCE AND EFFECT, REGARDLESS OF WHETHER EITHER PARTY’S REMEDIES HEREUNDER ARE DETERMINED TO HAVE FAILED OF THEIR ESSENTIAL PURPOSE. 37 | 38 | 8. General Provisions 39 | 40 | 8.1. Assignment. Neither party may assign this Agreement or any of its rights or obligations hereunder without the prior written consent of the other party, which shall not be unreasonably withheld; provided that either party may assign this Agreement or rights granted hereunder without the consent of the other party (i) to its Affiliate, and (ii) the transfer of this Agreement or rights granted hereunder to a successor entity in the event of a merger, corporate reorganization, or acquisition of all or substantially all the assets of a party. In no event shall the Agreement be transferred or assigned to a direct competitor of the other party. Any attempted assignment or transfer in violation of this Section 8.1 shall be null and void. 41 | 42 | 8.2. Governing Law. This Agreement will be governed by and construed in accordance with the laws of the State of California, without reference to its conflicts of law provisions. Any dispute regarding this Agreement will be subject to the exclusive jurisdiction of the state courts in and for Santa Clara County, California, U.S.A. (or, if there is federal jurisdiction, the United States District Court for the Northern District of California). This Agreement will not be governed by the United Nations Convention on Contracts for the International Sale of Goods, the application of which is hereby expressly excluded. The Uniform Computer Information Transactions Act (UCITA) or any similar laws or regulations do not apply to this Agreement. Any claim or action, regardless of form, arising out of this Agreement or an order form will be made against Mirantis, Inc. alone. 43 | 44 | 8.3. Export. Any and all materials provided to Customer under this Agreement, including technical data relating thereto, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and sanctions control regimes of the United States and may be subject to export or import laws or regulations in other countries. Customer represents, warrants and covenants that it (i) is not a prohibited party identified on any government export exclusion lists or a member of a government of any other export-prohibited countries pursuant to applicable export and import laws and regulations, (ii) will not transfer software, technology, and other technical data to export-prohibited parties or countries otherwise in violation of U.S. or other applicable export or import laws, or use the Mirantis Portal, Software or any other Mirantis products or documentation for military, nuclear, missile, chemical or biological weaponry end uses in violation of U.S. export laws. Additionally, each Party agrees that it will not engage in any illegal, unfair, deceptive, or unethical business practices whatsoever, including, but not limited to, any act that would constitute a violation of the U.S. Foreign Corrupt Practices Act, U.K. Bribery Act, or other similar anti-corruption laws. 45 | 46 | 8.4. Waiver. The waiver or failure of Mirantis to exercise in any respect any right provided for in this Agreement will not be deemed a waiver of any further right under this Agreement. 47 | 48 | 8.5. Severability. If any provision of this Agreement is held by a court of competent jurisdiction to be unenforceable because it is invalid or in conflict with any law of any relevant jurisdiction, the validity of the remaining provisions will not be affected, and the rights and obligations of the parties will be construed and enforced as if this Agreement did not contain the particular provisions held to be unenforceable. 49 | 50 | 8.6. Integration; Amendment. This Agreement sets forth the entire agreement between Customer and Mirantis relating to the subject matter hereof and supersedes all prior or contemporaneous oral or written communications, proposals, and representations relating to the subject matter hereof. The Agreement prevails over any conflicting or additional terms of any quote, order, purchase order, acknowledgment or similar communication between the parties prior to or during the term of this Agreement. This Agreement may not be modified except pursuant to a written agreement signed by a duly authorized representative of each party. 51 | 52 | 8.7. Notices. Notices to Mirantis will be sent to the address set forth at the top of this Agreement (or as later designated in writing by Mirantis) to Mirantis Finance with a copy to Mirantis Legal, and notices to Customer will be sent to the address provided below or to such other addresses (including via electronic communications) as it may give Mirantis. 53 | 54 | 8.8. Force Majeure. Neither party will be liable to the other for any failure to perform any of its obligations (except payment obligations) under this Agreement during any period in which the performance is delayed by circumstances beyond its reasonable control, such as systemic, electrical, telecommunications, or other utility failures, earthquake, storms, fire, flood or other elements of nature, pandemic, embargo, strike, riot, terrorism, change in law or policy, or the intervention of any governmental authority. 55 | 56 | 8.9. U.S. Government End Users. The services, software, and documentation are "Commercial items," "Commercial computer software" and "Computer software documentation" as defined by the Federal Acquisition Regulations (“FAR”) and Defense Federal Acquisition Regulations Supplement (“DFARS”). Pursuant to FAR 12.211, FAR 12.212, DFARS, 227.7202-1 through 227.7202-4, and their successors, the U.S. Government acquires the services, software, and documentation to the terms of this Agreement. 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mirantis Launchpad 2 | 3 | Mirantis Launchpad CLI tool ("**launchpad**") simplifies and automates deploying [Mirantis Container Runtime](https://docs.mirantis.com/welcome/mcr), [Mirantis Kubernetes Engine](https://docs.mirantis.com/welcome/mke) and [Mirantis Secure Registry](https://docs.mirantis.com/welcome/msr) on public clouds (like AWS or Azure), private clouds (like OpenStack or VMware), virtualization platforms (like VirtualBox, VMware Workstation, Parallels, etc.), or bare metal. 4 | 5 | Launchpad can also provide full cluster lifecycle management. Multi-manager, high availability clusters, defined as having sufficient node capacity to move active workloads around while updating, can be upgraded with no downtime. 6 | 7 | ## Documentation 8 | 9 | Launchpad documentation can be browsed on the [Mirantis Documentation site](https://docs.mirantis.com/mke/3.7/launchpad.html). 10 | 11 | ## Example 12 | 13 | Launchpad reads a YAML configuration file which lists cluster hosts with their connection addresses and product settings. It will then connect to each of the hosts, make the necessary preparations and finally install, upgrade or uninstall the cluster to match the desired state. 14 | 15 | An example configuration: 16 | 17 | ```yaml 18 | apiVersion: launchpad.mirantis.com/mke/v1.3 19 | kind: mke 20 | spec: 21 | hosts: 22 | - role: manager 23 | ssh: 24 | address: 10.0.0.1 25 | user: root 26 | - role: worker 27 | ssh: 28 | address: 10.0.0.2 29 | user: ubuntu 30 | mke: 31 | version: 3.7.7 32 | ``` 33 | 34 | Installing a cluster: 35 | 36 | ``` 37 | $ launchpad apply --config launchpad.yaml 38 | 39 | 40 | ..,,,,,.. 41 | .:i1fCG0088@@@@@880GCLt;, .,,::::::,,... 42 | ,;tC0@@@@@@@@@@@@@@@@@@@@@@@@@0:, .,:ii111i;:,,.. 43 | ,;1ttt1;;::::;;itfCG8@@@@@@@@@i @@@@0fi1t111i;,. 44 | .,. .:1L0@@ @8GCft111ii1; 45 | :f0CLft1i;i1tL . @8Cti:. .,:,. 46 | .:;i1111i;itC; @@@@@@@@@@@80GCLftt11ttfLLLf1:. 47 | .,:;ii1111i:,. , G8@@@@@@@@@@@@@@@@@@@@@@@0Lt;, 48 | ...,,::;;;;::,. ,;itfLCGGG0GGGCLft1;:. 49 | 50 | 51 | 52 | ;1: i1, .1, .11111i: .1i :1; ,1, i11111111: ;i ;1111; 53 | G@GC: 1G0@i ;@1 ;@t:::;G0. .0G8f L@GC: i@i :;;;@G;;;, C@ .80i:,:; 54 | C8 10CGC::@i :@i :@f:;;;CG. .0G ,@L f@.iGL, ;@; @L L@. tLft1;. 55 | G8 1; ;@i ;@i :@L11C@t ,08fffL@L L@. 10fi@; .@L L@. .:t@1 56 | C0 ;@i :@i :@i ;Gf..0C ,8L f@. .f0@; .8L L8 fft11fG; 57 | .. . . .. ,.., , .. .. .. .. .,:::, 58 | 59 | Mirantis Launchpad (c) 2021 Mirantis, Inc. 60 | 61 | INFO ==> Running phase: Open Remote Connection 62 | INFO ==> Running phase: Detect host operating systems 63 | INFO [ssh] 10.0.0.2:22: is running Ubuntu 18.04.5 LTS 64 | INFO [ssh] 10.0.0.1:22: is running Ubuntu 18.04.5 LTS 65 | INFO ==> Running phase: Gather Facts 66 | INFO [ssh] 10.0.0.1:22: gathering host facts 67 | INFO [ssh] 10.0.0.2:22: gathering host facts 68 | INFO [ssh] 10.0.0.1:22: internal address: 172.17.0.2 69 | INFO [ssh] 10.0.0.1:22: gathered all facts 70 | INFO [ssh] 10.0.0.2:22: internal address: 172.17.0.3 71 | INFO [ssh] 10.0.0.2:22: gathered all facts 72 | ... 73 | ... 74 | INFO Cluster is now configured. You can access your admin UIs at: 75 | INFO MKE cluster admin UI: https://test-mke-cluster.example.com 76 | INFO You can also download the admin client bundle with the following command: launchpad client-config 77 | ``` 78 | 79 | ## Support, Reporting Issues & Feedback 80 | 81 | Please use Github [issues](https://github.com/Mirantis/launchpad/issues) to report any issues, provide feedback, or request support. 82 | -------------------------------------------------------------------------------- /examples/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | cluster.yaml 2 | launchpad.yaml 3 | ssh_keys/ 4 | .terraform/ 5 | *terraform.tfstate* 6 | *.tfvars 7 | *.tfvars.json 8 | .terraform.lock.hcl 9 | -------------------------------------------------------------------------------- /examples/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Terraform with Mirantis Launchpad examples 2 | 3 | Working examples for using Terraform with Mirantis Launchpad. The scripts are provided as examples; you can change them to fit your specific configuration requirements or create your own. 4 | 5 | 6 | * [AWS](aws/README.md), a complete infrastructure example including VPC, LB, security groups, and other settings. 7 | * [Azure](azure/README.md), a complete example including VNET, LB, network security rules, and other settings. 8 | * [GCP](gcp/README.md), a complete example including VPC, Network/Subnetwork, LB, Firewall Rules and other settings. 9 | * [Hetzner](hetzner/README.md), a simple example with just a couple of VMs provisioned for an MKE cluster. 10 | * [OpenStack](openstack/README.md), a simple example with basic settings. 11 | * [VMware](vmware/README.md), a simple example using existing vSphere network. 12 | -------------------------------------------------------------------------------- /examples/terraform/aws/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping MKE cluster on AWS 2 | 3 | This directory provides an example flow for using Mirantis Launchpad with Terraform and AWS. 4 | 5 | ## Prerequisites 6 | 7 | * An account and credentials for AWS. 8 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 9 | 10 | ## Steps 11 | 12 | 1. Create terraform.tfvars file with needed details. You can use the provided terraform.tfvars.example as a baseline. 13 | 2. `terraform init` 14 | 3. `terraform apply` 15 | 4. `terraform output --raw mke_cluster | launchpad apply --config -` 16 | -------------------------------------------------------------------------------- /examples/terraform/aws/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.aws_region 3 | shared_credentials_files = [var.aws_shared_credentials_file] 4 | profile = var.aws_profile 5 | } 6 | 7 | module "vpc" { 8 | source = "./modules/vpc" 9 | cluster_name = var.cluster_name 10 | host_cidr = var.vpc_cidr 11 | } 12 | 13 | module "common" { 14 | source = "./modules/common" 15 | cluster_name = var.cluster_name 16 | vpc_id = module.vpc.id 17 | } 18 | 19 | module "masters" { 20 | source = "./modules/master" 21 | master_count = var.master_count 22 | vpc_id = module.vpc.id 23 | cluster_name = var.cluster_name 24 | subnet_ids = module.vpc.public_subnet_ids 25 | security_group_id = module.common.security_group_id 26 | master_type = var.master_type 27 | master_volume_size = var.master_volume_size 28 | image_id = module.common.image_id 29 | kube_cluster_tag = module.common.kube_cluster_tag 30 | ssh_key = var.cluster_name 31 | instance_profile_name = module.common.instance_profile_name 32 | } 33 | 34 | module "msrs" { 35 | source = "./modules/msr" 36 | msr_count = var.msr_count 37 | vpc_id = module.vpc.id 38 | cluster_name = var.cluster_name 39 | subnet_ids = module.vpc.public_subnet_ids 40 | security_group_id = module.common.security_group_id 41 | image_id = module.common.image_id 42 | msr_type = var.msr_type 43 | msr_volume_size = var.msr_volume_size 44 | kube_cluster_tag = module.common.kube_cluster_tag 45 | ssh_key = var.cluster_name 46 | instance_profile_name = module.common.instance_profile_name 47 | } 48 | 49 | module "workers" { 50 | source = "./modules/worker" 51 | worker_count = var.worker_count 52 | vpc_id = module.vpc.id 53 | cluster_name = var.cluster_name 54 | subnet_ids = module.vpc.public_subnet_ids 55 | security_group_id = module.common.security_group_id 56 | image_id = module.common.image_id 57 | worker_type = var.worker_type 58 | worker_volume_size = var.worker_volume_size 59 | kube_cluster_tag = module.common.kube_cluster_tag 60 | ssh_key = var.cluster_name 61 | instance_profile_name = module.common.instance_profile_name 62 | } 63 | 64 | module "windows_workers" { 65 | source = "./modules/windows_worker" 66 | worker_count = var.windows_worker_count 67 | vpc_id = module.vpc.id 68 | cluster_name = var.cluster_name 69 | subnet_ids = module.vpc.public_subnet_ids 70 | security_group_id = module.common.security_group_id 71 | image_id = module.common.windows_2019_image_id 72 | worker_type = var.worker_type 73 | worker_volume_size = var.worker_volume_size 74 | kube_cluster_tag = module.common.kube_cluster_tag 75 | instance_profile_name = module.common.instance_profile_name 76 | windows_administrator_password = var.windows_administrator_password 77 | } 78 | 79 | locals { 80 | managers = [ 81 | for host in module.masters.machines : { 82 | ssh = { 83 | address = host.public_ip 84 | user = "ubuntu" 85 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 86 | } 87 | role = host.tags["Role"] 88 | privateInterface = "ens5" 89 | } 90 | ] 91 | msrs = [ 92 | for host in module.msrs.machines : { 93 | ssh = { 94 | address = host.public_ip 95 | user = "ubuntu" 96 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 97 | } 98 | role = host.tags["Role"] 99 | privateInterface = "ens5" 100 | } 101 | ] 102 | workers = [ 103 | for host in module.workers.machines : { 104 | ssh = { 105 | address = host.public_ip 106 | user = "ubuntu" 107 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 108 | } 109 | role = host.tags["Role"] 110 | privateInterface = "ens5" 111 | } 112 | ] 113 | windows_workers = [ 114 | for host in module.windows_workers.machines : { 115 | winRM = { 116 | address = host.public_ip 117 | user = "Administrator" 118 | password = var.windows_administrator_password 119 | useHTTPS = true 120 | insecure = true 121 | } 122 | role = host.tags["Role"] 123 | privateInterface = "Ethernet 2" 124 | } 125 | ] 126 | mke_launchpad_tmpl = { 127 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 128 | kind = "mke" 129 | spec = { 130 | mke = { 131 | version = var.mke_version 132 | adminUsername = "admin" 133 | adminPassword = var.admin_password 134 | installFlags : [ 135 | "--default-node-orchestrator=kubernetes", 136 | "--san=${module.masters.lb_dns_name}", 137 | ] 138 | } 139 | msr = {} 140 | hosts = concat(local.managers, local.msrs, local.workers, local.windows_workers) 141 | } 142 | } 143 | 144 | 145 | msr_launchpad_tmpl = { 146 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 147 | kind = "mke+msr" 148 | spec = { 149 | mke = { 150 | version = var.mke_version 151 | adminUsername = "admin" 152 | adminPassword = var.admin_password 153 | installFlags : [ 154 | "--default-node-orchestrator=kubernetes", 155 | "--san=${module.masters.lb_dns_name}", 156 | ] 157 | } 158 | msr = { 159 | installFlags : [ 160 | "--ucp-insecure-tls", 161 | "--dtr-external-url ${module.msrs.lb_dns_name}", 162 | ] 163 | } 164 | hosts = concat(local.managers, local.msrs, local.workers, local.windows_workers) 165 | } 166 | } 167 | 168 | launchpad_tmpl = var.msr_count > 0 ? local.msr_launchpad_tmpl : local.mke_launchpad_tmpl 169 | } 170 | 171 | 172 | output "mke_cluster" { 173 | value = yamlencode(local.launchpad_tmpl) 174 | } 175 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/common/main.tf: -------------------------------------------------------------------------------- 1 | 2 | data "aws_availability_zones" "available" {} 3 | 4 | resource "tls_private_key" "ssh_key" { 5 | algorithm = "RSA" 6 | rsa_bits = "4096" 7 | } 8 | 9 | resource "local_file" "ssh_public_key" { 10 | content = tls_private_key.ssh_key.private_key_pem 11 | filename = "ssh_keys/${var.cluster_name}.pem" 12 | provisioner "local-exec" { 13 | command = "chmod 0600 ${local_file.ssh_public_key.filename}" 14 | } 15 | } 16 | 17 | resource "aws_key_pair" "key" { 18 | key_name = var.cluster_name 19 | public_key = tls_private_key.ssh_key.public_key_openssh 20 | } 21 | 22 | data "aws_ami" "ubuntu" { 23 | most_recent = true 24 | 25 | filter { 26 | name = "name" 27 | values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"] 28 | } 29 | 30 | filter { 31 | name = "virtualization-type" 32 | values = ["hvm"] 33 | } 34 | 35 | owners = ["099720109477"] # Canonical 36 | } 37 | 38 | data "aws_ami" "windows_2019" { 39 | most_recent = true 40 | 41 | filter { 42 | name = "name" 43 | values = ["Windows_Server-2019-English-Core-ContainersLatest-*"] 44 | } 45 | 46 | filter { 47 | name = "virtualization-type" 48 | values = ["hvm"] 49 | } 50 | 51 | owners = ["801119661308"] # Amazon 52 | } 53 | 54 | resource "aws_security_group" "common" { 55 | name = "${var.cluster_name}-common" 56 | description = "mke cluster common rules" 57 | vpc_id = var.vpc_id 58 | 59 | ingress { 60 | from_port = 0 61 | to_port = 0 62 | protocol = "-1" 63 | self = true 64 | } 65 | 66 | ingress { 67 | from_port = 22 68 | to_port = 22 69 | protocol = "tcp" 70 | cidr_blocks = ["0.0.0.0/0"] 71 | } 72 | 73 | egress { 74 | from_port = 0 75 | to_port = 0 76 | protocol = "-1" 77 | cidr_blocks = ["0.0.0.0/0"] 78 | } 79 | } 80 | 81 | resource "aws_iam_role" "role" { 82 | name = "${var.cluster_name}_host" 83 | 84 | assume_role_policy = < /etc/hostname 54 | sed -i "s|\(127\.0\..\.. *\)localhost|\1$HOSTNAME|" /etc/hosts 55 | hostname $HOSTNAME 56 | EOF 57 | 58 | lifecycle { 59 | ignore_changes = [ami] 60 | } 61 | 62 | root_block_device { 63 | volume_type = "gp2" 64 | volume_size = var.master_volume_size 65 | } 66 | } 67 | 68 | resource "aws_lb" "mke_master" { 69 | name = "${var.cluster_name}-master-lb" 70 | internal = false 71 | load_balancer_type = "network" 72 | subnets = var.subnet_ids 73 | 74 | tags = { 75 | Cluster = var.cluster_name 76 | } 77 | } 78 | 79 | resource "aws_lb_target_group" "mke_master_api" { 80 | name = "${var.cluster_name}-api" 81 | port = 443 82 | protocol = "TCP" 83 | vpc_id = var.vpc_id 84 | } 85 | 86 | resource "aws_lb_listener" "mke_master_api" { 87 | load_balancer_arn = aws_lb.mke_master.arn 88 | port = 443 89 | protocol = "TCP" 90 | 91 | default_action { 92 | target_group_arn = aws_lb_target_group.mke_master_api.arn 93 | type = "forward" 94 | } 95 | } 96 | 97 | resource "aws_lb_target_group_attachment" "mke_master_api" { 98 | count = var.master_count 99 | target_group_arn = aws_lb_target_group.mke_master_api.arn 100 | target_id = aws_instance.mke_master[count.index].id 101 | port = 443 102 | } 103 | 104 | resource "aws_lb_target_group" "mke_kube_api" { 105 | name = "${var.cluster_name}-kube-api" 106 | port = 6443 107 | protocol = "TCP" 108 | vpc_id = var.vpc_id 109 | } 110 | 111 | resource "aws_lb_listener" "mke_kube_api" { 112 | load_balancer_arn = aws_lb.mke_master.arn 113 | port = 6443 114 | protocol = "TCP" 115 | 116 | default_action { 117 | target_group_arn = aws_lb_target_group.mke_kube_api.arn 118 | type = "forward" 119 | } 120 | } 121 | 122 | resource "aws_lb_target_group_attachment" "mke_kube_api" { 123 | count = var.master_count 124 | target_group_arn = aws_lb_target_group.mke_kube_api.arn 125 | target_id = aws_instance.mke_master[count.index].id 126 | port = 6443 127 | } 128 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/master/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_dns_name" { 2 | value = aws_lb.mke_master.dns_name 3 | } 4 | 5 | output "public_ips" { 6 | value = aws_instance.mke_master.*.public_ip 7 | } 8 | 9 | output "private_ips" { 10 | value = aws_instance.mke_master.*.private_ip 11 | } 12 | 13 | output "machines" { 14 | value = aws_instance.mke_master 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/master/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "vpc_id" {} 4 | 5 | variable "instance_profile_name" {} 6 | 7 | variable "security_group_id" {} 8 | 9 | variable "subnet_ids" { 10 | type = list(string) 11 | } 12 | 13 | variable "image_id" {} 14 | 15 | variable "kube_cluster_tag" {} 16 | 17 | variable "ssh_key" { 18 | description = "SSH key name" 19 | } 20 | 21 | variable "master_count" { 22 | default = 3 23 | } 24 | 25 | variable "master_type" { 26 | default = "m5.large" 27 | } 28 | 29 | variable "master_volume_size" { 30 | default = 100 31 | } 32 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/msr/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "msr" { 2 | name = "${var.cluster_name}-msr" 3 | description = "mke cluster msrs" 4 | vpc_id = var.vpc_id 5 | 6 | ingress { 7 | from_port = 443 8 | to_port = 443 9 | protocol = "tcp" 10 | cidr_blocks = ["0.0.0.0/0"] 11 | } 12 | 13 | } 14 | 15 | locals { 16 | subnet_count = length(var.subnet_ids) 17 | } 18 | 19 | 20 | resource "aws_instance" "msr" { 21 | count = var.msr_count 22 | 23 | tags = tomap({ 24 | "Name" = "${var.cluster_name}-msr-${count.index + 1}", 25 | "Role" = "msr", 26 | (var.kube_cluster_tag) = "shared" 27 | }) 28 | 29 | instance_type = var.msr_type 30 | iam_instance_profile = var.instance_profile_name 31 | ami = var.image_id 32 | key_name = var.ssh_key 33 | vpc_security_group_ids = [var.security_group_id, aws_security_group.msr.id] 34 | subnet_id = var.subnet_ids[count.index % local.subnet_count] 35 | ebs_optimized = true 36 | user_data = < /etc/hostname 41 | sed -i "s|\(127\.0\..\.. *\)localhost|\1$HOSTNAME|" /etc/hosts 42 | hostname $HOSTNAME 43 | EOF 44 | 45 | lifecycle { 46 | ignore_changes = [ami] 47 | } 48 | 49 | root_block_device { 50 | volume_type = "gp2" 51 | volume_size = var.msr_volume_size 52 | } 53 | } 54 | 55 | resource "aws_lb" "msr" { 56 | name = "${var.cluster_name}-msr-lb" 57 | internal = false 58 | load_balancer_type = "network" 59 | subnets = var.subnet_ids 60 | 61 | tags = { 62 | Cluster = var.cluster_name 63 | } 64 | } 65 | 66 | resource "aws_lb_target_group" "mke_msr_api" { 67 | name = "${var.cluster_name}-msr-api" 68 | port = 443 69 | protocol = "TCP" 70 | vpc_id = var.vpc_id 71 | } 72 | 73 | resource "aws_lb_listener" "mke_msr_api" { 74 | load_balancer_arn = aws_lb.msr.arn 75 | port = 443 76 | protocol = "TCP" 77 | 78 | default_action { 79 | target_group_arn = aws_lb_target_group.mke_msr_api.arn 80 | type = "forward" 81 | } 82 | } 83 | 84 | resource "aws_lb_target_group_attachment" "mke_msr_api" { 85 | count = var.msr_count 86 | target_group_arn = aws_lb_target_group.mke_msr_api.arn 87 | target_id = aws_instance.msr[count.index].id 88 | port = 443 89 | } 90 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/msr/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_dns_name" { 2 | value = aws_lb.msr.dns_name 3 | } 4 | 5 | output "public_ips" { 6 | value = aws_instance.msr.*.public_ip 7 | } 8 | 9 | output "private_ips" { 10 | value = aws_instance.msr.*.private_ip 11 | } 12 | 13 | output "machines" { 14 | value = aws_instance.msr 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/msr/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "vpc_id" {} 4 | 5 | variable "instance_profile_name" {} 6 | 7 | variable "security_group_id" {} 8 | 9 | variable "subnet_ids" { 10 | type = list(string) 11 | } 12 | 13 | variable "image_id" {} 14 | 15 | variable "kube_cluster_tag" {} 16 | 17 | variable "ssh_key" { 18 | description = "SSH key name" 19 | } 20 | 21 | variable "msr_count" { 22 | default = 3 23 | } 24 | 25 | variable "msr_type" { 26 | default = "m5.large" 27 | } 28 | 29 | variable "msr_volume_size" { 30 | default = 100 31 | } 32 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/vpc/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_availability_zones" "all" {} 2 | 3 | # Network VPC, gateway, and routes 4 | 5 | resource "aws_vpc" "network" { 6 | cidr_block = var.host_cidr 7 | assign_generated_ipv6_cidr_block = true 8 | enable_dns_support = true 9 | enable_dns_hostnames = true 10 | 11 | tags = tomap({"Name" = var.cluster_name}) 12 | } 13 | 14 | resource "aws_internet_gateway" "gateway" { 15 | vpc_id = aws_vpc.network.id 16 | 17 | tags = tomap({"Name" = var.cluster_name}) 18 | } 19 | 20 | resource "aws_route_table" "default" { 21 | vpc_id = aws_vpc.network.id 22 | 23 | route { 24 | cidr_block = "0.0.0.0/0" 25 | gateway_id = aws_internet_gateway.gateway.id 26 | } 27 | 28 | tags = tomap({"Name" = var.cluster_name}) 29 | } 30 | 31 | locals { 32 | kube_cluster_tag = "kubernetes.io/cluster/${var.cluster_name}" 33 | } 34 | 35 | 36 | # Subnets (one per availability zone) 37 | resource "aws_subnet" "public" { 38 | count = 2 39 | 40 | vpc_id = aws_vpc.network.id 41 | availability_zone = data.aws_availability_zones.all.names[count.index] 42 | 43 | cidr_block = cidrsubnet(var.host_cidr, 8, count.index) 44 | map_public_ip_on_launch = true 45 | 46 | tags = tomap({(local.kube_cluster_tag) = "true"}) 47 | } 48 | 49 | resource "aws_route_table_association" "public" { 50 | count = length(aws_subnet.public) 51 | route_table_id = aws_route_table.default.id 52 | subnet_id = element(aws_subnet.public.*.id, count.index) 53 | } 54 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = aws_vpc.network.id 3 | } 4 | 5 | output "public_subnet_ids" { 6 | value = aws_subnet.public.*.id 7 | } 8 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "host_cidr" { 4 | description = "CIDR IPv4 range to assign to EC2 nodes" 5 | default = "172.31.0.0/16" 6 | } 7 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/windows_worker/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "worker" { 2 | name = "${var.cluster_name}-win-workers" 3 | description = "mke cluster windows workers" 4 | vpc_id = var.vpc_id 5 | 6 | ingress { 7 | from_port = 5985 8 | to_port = 5986 9 | protocol = "tcp" 10 | cidr_blocks = ["0.0.0.0/0"] 11 | } 12 | } 13 | 14 | locals { 15 | subnet_count = length(var.subnet_ids) 16 | } 17 | 18 | resource "aws_instance" "mke_worker" { 19 | count = var.worker_count 20 | 21 | tags = tomap({ 22 | "Name" = "${var.cluster_name}-win-worker-${count.index + 1}", 23 | "Role" = "worker", 24 | (var.kube_cluster_tag) = "shared" 25 | }) 26 | 27 | instance_type = var.worker_type 28 | iam_instance_profile = var.instance_profile_name 29 | ami = var.image_id 30 | vpc_security_group_ids = [var.security_group_id, aws_security_group.worker.id] 31 | subnet_id = var.subnet_ids[count.index % local.subnet_count] 32 | ebs_optimized = true 33 | user_data = < 35 | $admin = [adsi]("WinNT://./administrator, user") 36 | $admin.psbase.invoke("SetPassword", "${var.windows_administrator_password}") 37 | 38 | # Snippet to enable WinRM over HTTPS with a self-signed certificate 39 | # from https://gist.github.com/TechIsCool/d65017b8427cfa49d579a6d7b6e03c93 40 | Write-Output "Disabling WinRM over HTTP..." 41 | Disable-NetFirewallRule -Name "WINRM-HTTP-In-TCP" 42 | Disable-NetFirewallRule -Name "WINRM-HTTP-In-TCP-PUBLIC" 43 | Get-ChildItem WSMan:\Localhost\listener | Remove-Item -Recurse 44 | 45 | Write-Output "Configuring WinRM for HTTPS..." 46 | Set-Item -Path WSMan:\LocalHost\MaxTimeoutms -Value '1800000' 47 | Set-Item -Path WSMan:\LocalHost\Shell\MaxMemoryPerShellMB -Value '1024' 48 | Set-Item -Path WSMan:\LocalHost\Service\AllowUnencrypted -Value 'false' 49 | Set-Item -Path WSMan:\LocalHost\Service\Auth\Basic -Value 'true' 50 | Set-Item -Path WSMan:\LocalHost\Service\Auth\CredSSP -Value 'true' 51 | 52 | New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP" ` 53 | -DisplayName "Windows Remote Management (HTTPS-In)" ` 54 | -Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" ` 55 | -Group "Windows Remote Management" ` 56 | -Program "System" ` 57 | -Protocol TCP ` 58 | -LocalPort "5986" ` 59 | -Action Allow ` 60 | -Profile Domain,Private 61 | 62 | New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP-PUBLIC" ` 63 | -DisplayName "Windows Remote Management (HTTPS-In)" ` 64 | -Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" ` 65 | -Group "Windows Remote Management" ` 66 | -Program "System" ` 67 | -Protocol TCP ` 68 | -LocalPort "5986" ` 69 | -Action Allow ` 70 | -Profile Public 71 | 72 | $Hostname = [System.Net.Dns]::GetHostByName((hostname)).HostName.ToUpper() 73 | $pfx = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName $Hostname 74 | $certThumbprint = $pfx.Thumbprint 75 | $certSubjectName = $pfx.SubjectName.Name.TrimStart("CN = ").Trim() 76 | 77 | New-Item -Path WSMan:\LocalHost\Listener -Address * -Transport HTTPS -Hostname $certSubjectName -CertificateThumbPrint $certThumbprint -Port "5986" -force 78 | 79 | Write-Output "Restarting WinRM Service..." 80 | Stop-Service WinRM 81 | Set-Service WinRM -StartupType "Automatic" 82 | Start-Service WinRM 83 | 84 | EOF 85 | 86 | 87 | lifecycle { 88 | ignore_changes = [ami] 89 | } 90 | 91 | root_block_device { 92 | volume_type = "gp2" 93 | volume_size = var.worker_volume_size 94 | } 95 | 96 | connection { 97 | type = "winrm" 98 | user = "Administrator" 99 | password = var.administrator_password 100 | timeout = "10m" 101 | https = "true" 102 | insecure = "true" 103 | port=5986 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/windows_worker/outputs.tf: -------------------------------------------------------------------------------- 1 | output "private_ips" { 2 | value = aws_instance.mke_worker.*.private_ip 3 | } 4 | output "machines" { 5 | value = aws_instance.mke_worker.* 6 | } 7 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/windows_worker/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "vpc_id" {} 4 | 5 | variable "instance_profile_name" {} 6 | 7 | variable "security_group_id" {} 8 | 9 | variable "subnet_ids" { 10 | type = list(string) 11 | } 12 | 13 | variable "image_id" {} 14 | 15 | variable "kube_cluster_tag" {} 16 | 17 | variable "worker_count" { 18 | default = 0 19 | } 20 | 21 | variable "worker_type" { 22 | default = "m5.large" 23 | } 24 | 25 | variable "worker_volume_size" { 26 | default = 100 27 | } 28 | 29 | variable "windows_administrator_password" { 30 | } 31 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/worker/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "worker" { 2 | name = "${var.cluster_name}-workers" 3 | description = "mke cluster workers" 4 | vpc_id = var.vpc_id 5 | } 6 | 7 | locals { 8 | subnet_count = length(var.subnet_ids) 9 | } 10 | 11 | 12 | resource "aws_instance" "mke_worker" { 13 | count = var.worker_count 14 | 15 | tags = tomap({ 16 | "Name" = "${var.cluster_name}-worker-${count.index + 1}", 17 | "Role" = "worker", 18 | (var.kube_cluster_tag) = "shared" 19 | }) 20 | 21 | instance_type = var.worker_type 22 | iam_instance_profile = var.instance_profile_name 23 | ami = var.image_id 24 | key_name = var.ssh_key 25 | vpc_security_group_ids = [var.security_group_id, aws_security_group.worker.id] 26 | subnet_id = var.subnet_ids[count.index % local.subnet_count] 27 | ebs_optimized = true 28 | user_data = < /etc/hostname 33 | sed -i "s|\(127\.0\..\.. *\)localhost|\1$HOSTNAME|" /etc/hosts 34 | hostname $HOSTNAME 35 | EOF 36 | 37 | lifecycle { 38 | ignore_changes = [ami] 39 | } 40 | 41 | root_block_device { 42 | volume_type = "gp2" 43 | volume_size = var.worker_volume_size 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/worker/outputs.tf: -------------------------------------------------------------------------------- 1 | output "private_ips" { 2 | value = aws_instance.mke_worker.*.private_ip 3 | } 4 | output "machines" { 5 | value = aws_instance.mke_worker.* 6 | } 7 | -------------------------------------------------------------------------------- /examples/terraform/aws/modules/worker/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "vpc_id" {} 4 | 5 | variable "instance_profile_name" {} 6 | 7 | variable "security_group_id" {} 8 | 9 | variable "subnet_ids" { 10 | type = list(string) 11 | } 12 | 13 | variable "image_id" {} 14 | 15 | variable "kube_cluster_tag" {} 16 | 17 | variable "ssh_key" { 18 | description = "SSH key name" 19 | } 20 | 21 | variable "worker_count" { 22 | default = 3 23 | } 24 | 25 | variable "worker_type" { 26 | default = "m5.large" 27 | } 28 | 29 | variable "worker_volume_size" { 30 | default = 100 31 | } 32 | -------------------------------------------------------------------------------- /examples/terraform/aws/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Check if file exist terraform.tfvars 4 | if [ ! -f ./terraform.tfvars ]; then 5 | echo "File terraform.tfvars not found!" 6 | echo "Please create one from terraform.tfvars.example!" 7 | exit 2 8 | fi 9 | terraform init 10 | terraform apply -auto-approve 11 | mv -i ./launchpad.yaml ./launchpad.$(date +"%Y-%m-%d_%H-%M").yaml 12 | terraform output --raw mke_cluster > ./launchpad.yaml 13 | echo "Your launchpad.yaml sample is stored in your current directory" && ls ./launchpad.yaml 14 | echo "Apply your configuration via launchpad apply" 15 | 16 | exit 0 -------------------------------------------------------------------------------- /examples/terraform/aws/ssh_keys/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirantis/launchpad_legacy/b13a0cb855e7161ec7938be1cf5c71fb26880409/examples/terraform/aws/ssh_keys/.gitkeep -------------------------------------------------------------------------------- /examples/terraform/aws/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cluster_name = "my-mke-cluster" 2 | master_count = 1 3 | msr_count = 1 4 | worker_count = 3 5 | windows_worker_count = 0 6 | admin_password = "orcaorcaorca" 7 | -------------------------------------------------------------------------------- /examples/terraform/aws/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | default = "mke" 3 | } 4 | 5 | variable "aws_region" { 6 | default = "eu-central-1" 7 | } 8 | 9 | variable "aws_shared_credentials_file" { 10 | default = "~/.aws/credentials" 11 | } 12 | 13 | variable "aws_profile" { 14 | default = "kaas" 15 | } 16 | 17 | variable "vpc_cidr" { 18 | default = "172.31.0.0/16" 19 | } 20 | 21 | variable "admin_password" { 22 | default = "dockeradmin" 23 | } 24 | 25 | 26 | variable "master_count" { 27 | default = 1 28 | } 29 | 30 | variable "worker_count" { 31 | default = 3 32 | } 33 | 34 | variable "windows_worker_count" { 35 | default = 0 36 | } 37 | 38 | variable "msr_count" { 39 | default = 0 40 | } 41 | 42 | variable "master_type" { 43 | default = "m5.large" 44 | } 45 | 46 | variable "worker_type" { 47 | default = "m5.large" 48 | } 49 | 50 | variable "msr_type" { 51 | default = "m5.large" 52 | } 53 | variable "master_volume_size" { 54 | default = 100 55 | } 56 | 57 | variable "worker_volume_size" { 58 | default = 100 59 | } 60 | 61 | variable "msr_volume_size" { 62 | default = 100 63 | } 64 | 65 | variable "windows_administrator_password" { 66 | default = "w!ndozePassw0rd" 67 | } 68 | 69 | variable "mke_version" { 70 | default = "3.5.5" 71 | } 72 | -------------------------------------------------------------------------------- /examples/terraform/aws/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/terraform/azure/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping MKE cluster on Azure 2 | 3 | This directory provides an example flow for using Mirantis Launchpad with Terraform and Azure. 4 | 5 | ## Prerequisites 6 | 7 | * An account and credentials for Azure. 8 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 9 | * The Terraform `azurerm` provider requires a number of environment variables to be set. Please refer to the [Terraform Azure Provider](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs) documentation for more details. The minimum required environment variables for this example are: 10 | 11 | * ARM_CLIENT_ID 12 | * ARM_CLIENT_SECRET 13 | * ARM_SUBSCRIPTION_ID 14 | * ARM_TENANT_ID 15 | 16 | ## Steps 17 | 18 | 1. Create terraform.tfvars file with needed details. You can use the provided terraform.tfvars.example as a baseline. 19 | 2. `terraform init` 20 | 3. `terraform apply` 21 | 4. `terraform output mke_cluster | launchpad apply --config -` 22 | 23 | ## Notes 24 | 25 | 1. If any Windows workers are created then a random password will be generated for the admin account `DockerAdmin` that is created. 26 | 2. Only Linux workers are added to the LoadBalancer created for workers. 27 | 3. Both RDP and WinRM ports are opened for Windows workers. 28 | 4. A default storage account is created for kubernetes. 29 | 5. The number of Fault & Update Domains varies depending on which Azure Region you're using. A list can be found [here](https://github.com/MicrosoftDocs/azure-docs/blob/master/includes/managed-disks-common-fault-domain-region-list.md). The Fault & Update Domain values are used in the Availability Set definitions 30 | 6. Windows worker nodes may be rebooted after engine install 31 | -------------------------------------------------------------------------------- /examples/terraform/azure/main.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | environment = var.azure_environment 4 | } 5 | 6 | module "vnet" { 7 | source = "./modules/vnet" 8 | location = var.azure_region 9 | cluster_name = var.cluster_name 10 | host_cidr = var.vnet_cidr 11 | subnet_cidr = var.address_space 12 | virtual_network_name = var.vnet_name 13 | tags = var.tags 14 | } 15 | 16 | module "common" { 17 | source = "./modules/common" 18 | location = var.azure_region 19 | cluster_name = var.cluster_name 20 | rg = module.vnet.rg 21 | vnet_id = module.vnet.id 22 | subnet_id = module.vnet.subnet_id 23 | tags = var.tags 24 | } 25 | 26 | module "masters" { 27 | source = "./modules/master" 28 | master_count = var.master_count 29 | vnet_id = module.vnet.id 30 | rg = module.vnet.rg 31 | cluster_name = var.cluster_name 32 | location = var.azure_region 33 | subnet_id = module.vnet.subnet_id 34 | ssh_key = module.common.ssh_key 35 | image = var.image_ubuntu1804 36 | master_type = var.master_type 37 | tags = var.tags 38 | fault_domain_count = var.fault_domain_count 39 | update_domain_count = var.update_domain_count 40 | 41 | } 42 | 43 | module "workers" { 44 | source = "./modules/worker" 45 | worker_count = var.worker_count 46 | vnet_id = module.vnet.id 47 | rg = module.vnet.rg 48 | cluster_name = var.cluster_name 49 | location = var.azure_region 50 | subnet_id = module.vnet.subnet_id 51 | ssh_key = module.common.ssh_key 52 | image = var.image_ubuntu1804 53 | worker_type = var.worker_type 54 | tags = var.tags 55 | fault_domain_count = var.fault_domain_count 56 | update_domain_count = var.update_domain_count 57 | } 58 | 59 | module "windows_workers" { 60 | source = "./modules/windows_worker" 61 | worker_count = var.windows_worker_count 62 | vnet_id = module.vnet.id 63 | rg = module.vnet.rg 64 | cluster_name = var.cluster_name 65 | location = var.azure_region 66 | subnet_id = module.vnet.subnet_id 67 | ssh_key = module.common.ssh_key 68 | image = var.image_windows2019 69 | worker_type = var.worker_type 70 | username = var.windows_admin_username 71 | tags = var.tags 72 | fault_domain_count = var.fault_domain_count 73 | update_domain_count = var.update_domain_count 74 | } 75 | 76 | locals { 77 | managers = [ 78 | for ip in module.masters.public_ips : { 79 | ssh = { 80 | address = ip 81 | user = "ubuntu" 82 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 83 | } 84 | privateInterface = "eth0" 85 | role = "manager" 86 | } 87 | ] 88 | workers = [ 89 | for ip in module.workers.public_ips : { 90 | ssh = { 91 | address = ip 92 | user = "ubuntu" 93 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 94 | } 95 | privateInterface = "eth0" 96 | role = "worker" 97 | } 98 | ] 99 | windows_workers = [ 100 | for ip in module.windows_workers.public_ips : { 101 | winRM = { 102 | address = ip 103 | user = var.windows_admin_username 104 | password = module.windows_workers.windows_password 105 | useHTTPS = true 106 | insecure = true 107 | } 108 | privateInterface = "Ethernet" 109 | role = "worker" 110 | } 111 | ] 112 | } 113 | 114 | locals { 115 | launchpad_tmpl = { 116 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 117 | kind = "mke" 118 | metadata = { 119 | name = var.cluster_name 120 | } 121 | spec = { 122 | mke = { 123 | version = var.mke_version 124 | adminUsername = "admin" 125 | adminPassword = var.admin_password 126 | installFlags : [ 127 | "--default-node-orchestrator=kubernetes", 128 | "--san=${module.masters.lb_dns_name}", 129 | ] 130 | } 131 | hosts = concat(local.managers, local.workers, local.windows_workers) 132 | } 133 | } 134 | } 135 | 136 | output "mke_cluster" { 137 | value = yamlencode(local.launchpad_tmpl) 138 | } 139 | 140 | output "loadbalancers" { 141 | value = { 142 | MasterLB = module.masters.lb_dns_name 143 | WorkersLB = module.workers.lb_dns_name 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/common/main.tf: -------------------------------------------------------------------------------- 1 | 2 | ##### 3 | # Create new SSH Key 4 | # 5 | resource "tls_private_key" "ssh_key" { 6 | algorithm = "RSA" 7 | rsa_bits = "4096" 8 | } 9 | 10 | resource "local_file" "ssh_public_key" { 11 | content = tls_private_key.ssh_key.private_key_pem 12 | filename = "ssh_keys/${var.cluster_name}.pem" 13 | provisioner "local-exec" { 14 | command = "chmod 0600 ${local_file.ssh_public_key.filename}" 15 | } 16 | } 17 | 18 | ##### 19 | # Cluster wide NSG 20 | ##### 21 | resource azurerm_network_security_group "cluster_nsg" { 22 | name = "${var.cluster_name}-cluster-nsg" 23 | location = var.location 24 | resource_group_name = var.rg 25 | 26 | tags = merge( 27 | tomap({ 28 | "Name" = format("%s-cluster-nsg", var.cluster_name), 29 | "Environment" = format("%s", var.rg) 30 | }), 31 | var.tags 32 | ) 33 | } 34 | 35 | # associate network security group for the cluster to the subnet 36 | resource azurerm_subnet_network_security_group_association "cluster_subnet_nsg" { 37 | subnet_id = var.subnet_id 38 | network_security_group_id = azurerm_network_security_group.cluster_nsg.id 39 | } 40 | 41 | ##### 42 | # Rules for master NSG 43 | ##### 44 | resource "azurerm_network_security_rule" "cluster" { 45 | name = "allow" 46 | description = "Allow all" 47 | priority = 100 48 | direction = "Inbound" 49 | access = "Allow" 50 | protocol = "*" 51 | source_address_prefix = "*" 52 | source_port_range = "*" 53 | destination_port_range = "*" 54 | destination_address_prefix = "*" 55 | resource_group_name = var.rg 56 | network_security_group_name = azurerm_network_security_group.cluster_nsg.name 57 | } 58 | 59 | ##### 60 | # Storage account for kubernetes 61 | ##### 62 | resource "azurerm_storage_account" "kubernetes_storage" { 63 | name = format("%skube", substr(replace(lower(var.cluster_name), "-", ""), 0, 23)) 64 | resource_group_name = var.rg 65 | location = var.location 66 | account_tier = "Standard" 67 | account_replication_type = "LRS" 68 | 69 | tags = merge( 70 | tomap({ 71 | "Name" = format("%s-kube-storage", var.cluster_name), 72 | "Environment" = format("%s", var.rg) 73 | }), 74 | var.tags 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/common/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_nsg_id" { 2 | value = azurerm_network_security_group.cluster_nsg.id 3 | } 4 | 5 | output "ssh_key" { 6 | value = tls_private_key.ssh_key 7 | } 8 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/common/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "rg" {} 4 | 5 | variable "vnet_id" {} 6 | 7 | variable "subnet_id" {} 8 | 9 | variable "location" {} 10 | 11 | variable tags { 12 | description = "Additional tags to apply to all resources" 13 | type = map 14 | default = {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/master/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "pub_ip_salt" { 2 | count = 1 3 | length = 4 4 | special = false 5 | 6 | keepers = { 7 | deployment = var.cluster_name 8 | } 9 | } 10 | 11 | ##### 12 | # NSG for Masters 13 | ##### 14 | resource azurerm_network_security_group "master_nsg" { 15 | name = "${var.cluster_name}-master-nsg" 16 | location = var.location 17 | resource_group_name = var.rg 18 | 19 | tags = merge( 20 | tomap({ 21 | "Name" = format("%s-master-nsg", var.cluster_name), 22 | "Environment" = format("%s", var.rg) 23 | }), 24 | var.tags 25 | ) 26 | } 27 | 28 | ##### 29 | # Rules for master NSG 30 | ##### 31 | resource "azurerm_network_security_rule" "master_22" { 32 | name = "port_22_tcp" 33 | description = "Allow 22 tcp for master" 34 | priority = 100 35 | direction = "Inbound" 36 | access = "Allow" 37 | protocol = "*" 38 | source_address_prefix = "*" 39 | source_port_range = "*" 40 | destination_port_range = "22" 41 | destination_address_prefix = "*" 42 | resource_group_name = var.rg 43 | network_security_group_name = azurerm_network_security_group.master_nsg.name 44 | } 45 | 46 | resource "azurerm_network_security_rule" "master_443" { 47 | name = "port_443_tcp" 48 | description = "Allow 443 tcp for master" 49 | priority = 110 50 | direction = "Inbound" 51 | access = "Allow" 52 | protocol = "*" 53 | source_address_prefix = "*" 54 | source_port_range = "*" 55 | destination_port_range = "443" 56 | destination_address_prefix = "*" 57 | resource_group_name = var.rg 58 | network_security_group_name = azurerm_network_security_group.master_nsg.name 59 | } 60 | 61 | resource "azurerm_network_security_rule" "master_6443" { 62 | name = "port_6443_tcp" 63 | description = "Allow 6443 tcp for master" 64 | priority = 120 65 | direction = "Inbound" 66 | access = "Allow" 67 | protocol = "*" 68 | source_address_prefix = "*" 69 | source_port_range = "*" 70 | destination_port_range = "6443" 71 | destination_address_prefix = "*" 72 | resource_group_name = var.rg 73 | network_security_group_name = azurerm_network_security_group.master_nsg.name 74 | } 75 | 76 | ##### 77 | # Master LB 78 | ##### 79 | resource "azurerm_lb" "master_public_lb" { 80 | name = format("%s-mke-LB", var.cluster_name) 81 | location = var.location 82 | resource_group_name = var.rg 83 | 84 | frontend_ip_configuration { 85 | name = "mke-LB-FrontendIP" 86 | public_ip_address_id = join("", azurerm_public_ip.mke_lb_pub_ip.*.id) 87 | } 88 | 89 | tags = merge( 90 | tomap({ 91 | "Name" = format("%s-mke-LB", var.cluster_name), 92 | "Environment" = format("%s", var.rg) 93 | }), 94 | var.tags 95 | ) 96 | } 97 | 98 | # create the load balancer backend pool 99 | resource "azurerm_lb_backend_address_pool" "mke_lb_be_pool" { 100 | name = "${var.cluster_name}-mke-be-pool" 101 | resource_group_name = var.rg 102 | loadbalancer_id = azurerm_lb.master_public_lb.id 103 | } 104 | 105 | # Associate the provided network interfaces with this backend pool 106 | resource "azurerm_network_interface_backend_address_pool_association" "mke_lb_be_pool_assoc" { 107 | count = length(azurerm_network_interface.netif_public) 108 | network_interface_id = azurerm_network_interface.netif_public[count.index].id 109 | ip_configuration_name = format("%s-master-Net-%s", var.cluster_name, count.index + 1) 110 | backend_address_pool_id = azurerm_lb_backend_address_pool.mke_lb_be_pool.id 111 | } 112 | 113 | # Add a health check probe for the backend instances 114 | resource "azurerm_lb_probe" "mke_lb_probe_443" { 115 | resource_group_name = var.rg 116 | loadbalancer_id = azurerm_lb.master_public_lb.id 117 | name = "probe_mke_443" 118 | protocol = "TCP" 119 | port = 443 120 | interval_in_seconds = 5 121 | number_of_probes = 2 122 | } 123 | 124 | resource "azurerm_lb_probe" "mke_lb_probe_6443" { 125 | resource_group_name = var.rg 126 | loadbalancer_id = azurerm_lb.master_public_lb.id 127 | name = "probe_mke_6443" 128 | protocol = "TCP" 129 | port = 6443 130 | interval_in_seconds = 5 131 | number_of_probes = 2 132 | } 133 | 134 | # Add rules for the master loadbalancer 135 | resource "azurerm_lb_rule" "mke_lb_rule_443" { 136 | resource_group_name = var.rg 137 | loadbalancer_id = azurerm_lb.master_public_lb.id 138 | name = format("%s-mke-443-443", var.cluster_name) 139 | protocol = "TCP" 140 | frontend_port = 443 141 | backend_port = 443 142 | frontend_ip_configuration_name = "mke-LB-FrontendIP" 143 | enable_floating_ip = false 144 | backend_address_pool_id = azurerm_lb_backend_address_pool.mke_lb_be_pool.id 145 | idle_timeout_in_minutes = 5 146 | probe_id = azurerm_lb_probe.mke_lb_probe_443.id 147 | } 148 | 149 | resource "azurerm_lb_rule" "mke_lb_rule_6443" { 150 | resource_group_name = var.rg 151 | loadbalancer_id = azurerm_lb.master_public_lb.id 152 | name = format("%s-mke-6443-6443", var.cluster_name) 153 | protocol = "TCP" 154 | frontend_port = 6443 155 | backend_port = 6443 156 | frontend_ip_configuration_name = "mke-LB-FrontendIP" 157 | enable_floating_ip = false 158 | backend_address_pool_id = azurerm_lb_backend_address_pool.mke_lb_be_pool.id 159 | idle_timeout_in_minutes = 5 160 | probe_id = azurerm_lb_probe.mke_lb_probe_6443.id 161 | } 162 | 163 | # Add public ip for master loadbalancer 164 | resource "azurerm_public_ip" "mke_lb_pub_ip" { 165 | name = "mke-LB-FrontendIP" 166 | location = var.location 167 | resource_group_name = var.rg 168 | 169 | allocation_method = "Static" 170 | domain_name_label = format("mke-%s-%s", lower(replace(var.rg, "/[^a-zA-Z0-9]/", "")), lower(random_string.pub_ip_salt[0].result)) 171 | 172 | tags = merge( 173 | tomap({ 174 | "Name" = "mke-LB-FrontendIP", 175 | "Environment" = format("%s", var.rg) 176 | }), 177 | var.tags 178 | ) 179 | } 180 | 181 | ##### 182 | # Network Interfaces for VMs 183 | ##### 184 | resource "azurerm_network_interface" "netif_public" { 185 | count = var.master_count 186 | name = format("%s-master-Net-%s", var.cluster_name, count.index + 1) 187 | location = var.location 188 | resource_group_name = var.rg 189 | 190 | ip_configuration { 191 | name = format("%s-master-Net-%s", var.cluster_name, count.index + 1) 192 | subnet_id = var.subnet_id 193 | 194 | public_ip_address_id = azurerm_public_ip.master_public_ips[count.index].id 195 | private_ip_address_allocation = "Dynamic" 196 | primary = true 197 | } 198 | 199 | lifecycle { 200 | ignore_changes = [ 201 | ip_configuration, 202 | ] 203 | } 204 | 205 | tags = merge( 206 | tomap({ 207 | "Name" = format("%s-master-Net-%s", var.cluster_name, count.index + 1), 208 | "Environment" = format("%s", var.rg) 209 | }), 210 | var.tags 211 | ) 212 | } 213 | 214 | resource "azurerm_network_interface_security_group_association" "master" { 215 | count = length(azurerm_network_interface.netif_public) 216 | network_interface_id = azurerm_network_interface.netif_public[count.index].id 217 | network_security_group_id = azurerm_network_security_group.master_nsg.id 218 | } 219 | 220 | ##### 221 | # Public IP addressing for VMs 222 | ##### 223 | resource "azurerm_public_ip" "master_public_ips" { 224 | count = var.master_count 225 | name = format("%s-master-PublicIP-%d", var.cluster_name, count.index + 1) 226 | location = var.location 227 | resource_group_name = var.rg 228 | allocation_method = "Static" 229 | 230 | tags = merge( 231 | tomap({ 232 | "Name" = format("%s-master-PublicIP-%d", var.cluster_name, count.index + 1), 233 | "Environment" = format("%s", var.rg) 234 | }), 235 | var.tags 236 | ) 237 | } 238 | 239 | ##### 240 | # AVSet for master 241 | # NOTE: The number of Fault & Update Domains varies depending on which Azure Region you're using. 242 | ##### 243 | resource "azurerm_availability_set" "master_avset" { 244 | name = "${var.cluster_name}-master" 245 | location = var.location 246 | resource_group_name = var.rg 247 | platform_fault_domain_count = var.fault_domain_count 248 | platform_update_domain_count = var.update_domain_count 249 | managed = true 250 | tags = merge( 251 | tomap({ 252 | "Name" = format("%s-master-avset", var.cluster_name), 253 | "Environment" = format("%s", var.rg) 254 | }), 255 | var.tags 256 | ) 257 | } 258 | 259 | ##### 260 | # Master VMs 261 | ##### 262 | resource "azurerm_virtual_machine" "mke_master" { 263 | count = var.master_count 264 | 265 | name = format("%s%03d", "master-", (count.index + 1)) 266 | location = var.location 267 | resource_group_name = var.rg 268 | vm_size = var.master_type 269 | 270 | network_interface_ids = [azurerm_network_interface.netif_public[count.index].id] 271 | primary_network_interface_id = azurerm_network_interface.netif_public[count.index].id 272 | 273 | availability_set_id = azurerm_availability_set.master_avset.id 274 | 275 | # Uncomment this line to delete the data disks automatically when deleting the VM 276 | delete_data_disks_on_termination = true 277 | delete_os_disk_on_termination = true 278 | 279 | storage_image_reference { 280 | publisher = var.image["publisher"] 281 | offer = var.image["offer"] 282 | sku = var.image["sku"] 283 | version = var.image["version"] 284 | } 285 | 286 | storage_os_disk { 287 | name = format("%s%03d-OSDisk", "master-", (count.index + 1)) 288 | create_option = "FromImage" 289 | caching = "None" 290 | disk_size_gb = var.master_os_volume_size 291 | managed_disk_type = "Premium_LRS" 292 | } 293 | 294 | storage_data_disk { 295 | name = format("%s%03d-DataDisk", "master-", (count.index + 1)) 296 | create_option = "Empty" 297 | lun = 0 298 | caching = "None" 299 | disk_size_gb = var.master_data_volume_size 300 | managed_disk_type = "Standard_LRS" 301 | } 302 | 303 | os_profile { 304 | computer_name = format("%s%03d", "master-", (count.index + 1)) 305 | admin_username = "ubuntu" 306 | custom_data = <<-EOF 307 | #cloud-config 308 | bootcmd: 309 | - > 310 | echo 'network: {config: disabled}' > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg 311 | EOF 312 | } 313 | 314 | os_profile_linux_config { 315 | disable_password_authentication = true 316 | 317 | ssh_keys { 318 | path = "/home/ubuntu/.ssh/authorized_keys" 319 | key_data = var.ssh_key.public_key_openssh 320 | } 321 | } 322 | 323 | lifecycle { 324 | ignore_changes = [ 325 | tags 326 | ] 327 | } 328 | 329 | tags = merge( 330 | tomap({ 331 | "Name" = format("%s%03d", "master-", (count.index + 1)), 332 | "Environment" = format("%s", var.rg), 333 | "Role" = "master", 334 | }), 335 | var.tags 336 | ) 337 | } 338 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/master/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_dns_name" { 2 | value = azurerm_public_ip.mke_lb_pub_ip.fqdn 3 | } 4 | 5 | output "public_ips" { 6 | value = azurerm_public_ip.master_public_ips.*.ip_address 7 | } 8 | 9 | output "machines" { 10 | value = azurerm_virtual_machine.mke_master 11 | } 12 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/master/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "location" {} 4 | 5 | variable "rg" {} 6 | 7 | variable "vnet_id" {} 8 | 9 | variable "subnet_id" {} 10 | 11 | variable "ssh_key" {} 12 | 13 | variable "master_count" { 14 | default = 3 15 | } 16 | 17 | variable "master_type" { 18 | default = "Standard_DS3_v2" 19 | } 20 | 21 | variable "master_data_volume_size" { 22 | default = 100 23 | } 24 | 25 | variable "master_os_volume_size" { 26 | default = 40 27 | } 28 | 29 | variable "tags" { 30 | description = "Additional tags to apply to all resources" 31 | } 32 | 33 | variable "image" {} 34 | 35 | variable "fault_domain_count" {} 36 | 37 | variable "update_domain_count" {} 38 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/vnet/main.tf: -------------------------------------------------------------------------------- 1 | ##### 2 | # Resource Group 3 | ##### 4 | resource "azurerm_resource_group" "rg" { 5 | name = "${var.cluster_name}-rg" 6 | location = var.location 7 | 8 | tags = merge( 9 | tomap({ 10 | "Name" = format("%s-rg", var.cluster_name) 11 | }), 12 | var.tags 13 | ) 14 | 15 | lifecycle { 16 | ignore_changes = [tags] 17 | } 18 | } 19 | 20 | ##### 21 | # Network VNET, Subnet 22 | ##### 23 | resource "azurerm_virtual_network" "vnet" { 24 | name = "${var.cluster_name}-vnet" 25 | location = var.location 26 | address_space = [var.host_cidr] 27 | resource_group_name = azurerm_resource_group.rg.name 28 | 29 | tags = merge( 30 | tomap({ 31 | "Name" = format("%s-vnet", var.cluster_name), 32 | "Environment" = format("%s", azurerm_resource_group.rg.name) 33 | }), 34 | var.tags 35 | ) 36 | } 37 | 38 | resource "azurerm_subnet" "subnet" { 39 | name = "${var.cluster_name}-subnet" 40 | virtual_network_name = azurerm_virtual_network.vnet.name 41 | resource_group_name = azurerm_resource_group.rg.name 42 | address_prefixes = [var.subnet_cidr] 43 | } 44 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/vnet/outputs.tf: -------------------------------------------------------------------------------- 1 | output "rg" { 2 | value = azurerm_resource_group.rg.name 3 | } 4 | 5 | output "id" { 6 | value = azurerm_virtual_network.vnet.id 7 | } 8 | 9 | output "subnet_name" { 10 | value = azurerm_subnet.subnet.name 11 | } 12 | 13 | output "subnet_id" { 14 | value = azurerm_subnet.subnet.id 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/vnet/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "location" {} 4 | 5 | variable "virtual_network_name" {} 6 | 7 | variable "host_cidr" { 8 | description = "CIDR IPv4 range to assign to VMs" 9 | default = "172.31.0.0/16" 10 | } 11 | 12 | variable "subnet_cidr" { 13 | description = "The address prefix to use for the subnet." 14 | default = "172.31.0.0/16" 15 | } 16 | 17 | variable "tags" { 18 | description = "Additional tags to apply to all resources" 19 | } 20 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/windows_worker/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "windows_password" { 2 | length = 16 3 | special = false 4 | min_upper = 1 5 | min_lower = 1 6 | min_numeric = 1 7 | 8 | keepers = { 9 | deployment = var.cluster_name 10 | } 11 | } 12 | 13 | locals { 14 | password = "${random_string.windows_password.result}mT4!" 15 | } 16 | 17 | ##### 18 | # NSG for window workers 19 | ##### 20 | resource azurerm_network_security_group "win_worker_nsg" { 21 | name = "${var.cluster_name}-win-worker-nsg" 22 | location = var.location 23 | resource_group_name = var.rg 24 | 25 | tags = merge( 26 | tomap({ 27 | "Name" = format("%s-win-worker-nsg", var.cluster_name), 28 | "Environment" = format("%s", var.rg) 29 | }), 30 | var.tags 31 | ) 32 | } 33 | 34 | ##### 35 | # Rules for window worker NSG 36 | ##### 37 | resource "azurerm_network_security_rule" "win_worker_3389" { 38 | name = "port_3389_tcp" 39 | description = "Allow 3389 tcp for win-worker" 40 | priority = 100 41 | direction = "Inbound" 42 | access = "Allow" 43 | protocol = "*" 44 | source_address_prefix = "*" 45 | source_port_range = "*" 46 | destination_port_range = "3389" 47 | destination_address_prefix = "*" 48 | resource_group_name = var.rg 49 | network_security_group_name = azurerm_network_security_group.win_worker_nsg.name 50 | } 51 | 52 | resource "azurerm_network_security_rule" "win_worker_5986" { 53 | name = "port_5986_tcp" 54 | description = "Allow 5986 tcp for win-worker" 55 | priority = 110 56 | direction = "Inbound" 57 | access = "Allow" 58 | protocol = "*" 59 | source_address_prefix = "*" 60 | source_port_range = "*" 61 | destination_port_range = "5986" 62 | destination_address_prefix = "*" 63 | resource_group_name = var.rg 64 | network_security_group_name = azurerm_network_security_group.win_worker_nsg.name 65 | } 66 | 67 | ##### 68 | # Network Interfaces for VMs 69 | ##### 70 | resource "azurerm_network_interface" "netif_public" { 71 | count = var.worker_count 72 | name = format("%s-win-worker-Net-%s", var.cluster_name, count.index + 1) 73 | location = var.location 74 | resource_group_name = var.rg 75 | 76 | ip_configuration { 77 | name = format("%s-win-worker-Net-%s", var.cluster_name, count.index + 1) 78 | subnet_id = var.subnet_id 79 | 80 | public_ip_address_id = azurerm_public_ip.win_worker_public_ips[count.index].id 81 | private_ip_address_allocation = "Dynamic" 82 | primary = true 83 | } 84 | 85 | lifecycle { 86 | ignore_changes = [ 87 | ip_configuration, 88 | ] 89 | } 90 | 91 | tags = merge( 92 | tomap({ 93 | "Name" = format("%s-win-worker-Net-%s", var.cluster_name, count.index + 1), 94 | "Environment" = format("%s", var.rg) 95 | }), 96 | var.tags 97 | ) 98 | } 99 | 100 | resource "azurerm_network_interface_security_group_association" "win-worker" { 101 | count = length(azurerm_network_interface.netif_public) 102 | network_interface_id = azurerm_network_interface.netif_public[count.index].id 103 | network_security_group_id = azurerm_network_security_group.win_worker_nsg.id 104 | } 105 | 106 | ##### 107 | # Public IP addressing for VMs 108 | ##### 109 | resource "azurerm_public_ip" "win_worker_public_ips" { 110 | count = var.worker_count 111 | name = format("%s-win-worker-PublicIP-%d", var.cluster_name, count.index + 1) 112 | location = var.location 113 | resource_group_name = var.rg 114 | allocation_method = "Static" 115 | 116 | tags = merge( 117 | tomap({ 118 | "Name" = format("%s-win-worker-PublicIP-%d", var.cluster_name, count.index + 1), 119 | "Environment" = format("%s", var.rg) 120 | }), 121 | var.tags 122 | ) 123 | } 124 | 125 | ##### 126 | # AVSet for win-worker 127 | # NOTE: The number of Fault & Update Domains varies depending on which Azure Region you're using. 128 | ##### 129 | resource "azurerm_availability_set" "win_worker_avset" { 130 | name = "${var.cluster_name}-win-worker" 131 | location = var.location 132 | resource_group_name = var.rg 133 | platform_fault_domain_count = var.fault_domain_count 134 | platform_update_domain_count = var.update_domain_count 135 | managed = true 136 | tags = merge( 137 | tomap({ 138 | "Name" = format("%s-win-worker-avset", var.cluster_name), 139 | "Environment" = format("%s", var.rg) 140 | }), 141 | var.tags 142 | ) 143 | } 144 | 145 | ##### 146 | # Windows Worker VMs 147 | ##### 148 | resource "azurerm_virtual_machine" "win_worker" { 149 | count = var.worker_count 150 | 151 | name = format("%s%03d", "win-worker-", (count.index + 1)) 152 | location = var.location 153 | resource_group_name = var.rg 154 | vm_size = var.worker_type 155 | 156 | network_interface_ids = [azurerm_network_interface.netif_public[count.index].id] 157 | primary_network_interface_id = azurerm_network_interface.netif_public[count.index].id 158 | 159 | availability_set_id = azurerm_availability_set.win_worker_avset.id 160 | 161 | # Uncomment this line to delete the data disks automatically when deleting the VM 162 | delete_data_disks_on_termination = true 163 | delete_os_disk_on_termination = true 164 | 165 | storage_image_reference { 166 | publisher = var.image["publisher"] 167 | offer = var.image["offer"] 168 | sku = var.image["sku"] 169 | version = var.image["version"] 170 | } 171 | 172 | storage_os_disk { 173 | name = format("%s%03d-OSDisk", "win-worker-", (count.index + 1)) 174 | create_option = "FromImage" 175 | caching = "None" 176 | managed_disk_type = "Premium_LRS" 177 | } 178 | 179 | storage_data_disk { 180 | name = format("%s%03d-DataDisk", "win-worker-", (count.index + 1)) 181 | create_option = "Empty" 182 | lun = 0 183 | caching = "None" 184 | disk_size_gb = var.worker_data_volume_size 185 | managed_disk_type = "Standard_LRS" 186 | } 187 | 188 | os_profile { 189 | computer_name = format("%s%03d", "win-worker-", (count.index + 1)) 190 | admin_username = var.username 191 | admin_password = local.password 192 | custom_data = < 310 | echo 'network: {config: disabled}' > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg 311 | EOF 312 | } 313 | 314 | os_profile_linux_config { 315 | disable_password_authentication = true 316 | 317 | ssh_keys { 318 | path = "/home/ubuntu/.ssh/authorized_keys" 319 | key_data = var.ssh_key.public_key_openssh 320 | } 321 | } 322 | 323 | lifecycle { 324 | ignore_changes = [ 325 | tags 326 | ] 327 | } 328 | 329 | tags = merge( 330 | tomap({ 331 | "Name" = format("%s%03d", "worker-", (count.index + 1)), 332 | "Environment" = format("%s", var.rg), 333 | "Role" = "worker", 334 | }), 335 | var.tags 336 | ) 337 | } 338 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/worker/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_dns_name" { 2 | value = azurerm_public_ip.worker_lb_pub_ip.fqdn 3 | } 4 | 5 | output "public_ips" { 6 | value = azurerm_public_ip.worker_public_ips.*.ip_address 7 | } 8 | 9 | output "machines" { 10 | value = azurerm_virtual_machine.worker 11 | } 12 | -------------------------------------------------------------------------------- /examples/terraform/azure/modules/worker/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "location" {} 4 | 5 | variable "rg" {} 6 | 7 | variable "vnet_id" {} 8 | 9 | variable "subnet_id" {} 10 | 11 | variable "ssh_key" {} 12 | 13 | variable "worker_count" { 14 | default = 3 15 | } 16 | 17 | variable "worker_type" { 18 | default = "Standard_DS3_v2" 19 | } 20 | 21 | variable "worker_data_volume_size" { 22 | default = 100 23 | } 24 | 25 | variable "worker_os_volume_size" { 26 | default = 40 27 | } 28 | 29 | variable "tags" { 30 | description = "Additional tags to apply to all resources" 31 | } 32 | 33 | variable "image" {} 34 | 35 | variable "fault_domain_count" {} 36 | 37 | variable "update_domain_count" {} 38 | -------------------------------------------------------------------------------- /examples/terraform/azure/ssh_keys/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirantis/launchpad_legacy/b13a0cb855e7161ec7938be1cf5c71fb26880409/examples/terraform/azure/ssh_keys/.gitkeep -------------------------------------------------------------------------------- /examples/terraform/azure/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cluster_name = "my-mke-cluster" 2 | master_count = 1 3 | worker_count = 3 4 | windows_worker_count = 0 5 | admin_password = "orcaorcaorca" 6 | -------------------------------------------------------------------------------- /examples/terraform/azure/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | default = "mke" 3 | } 4 | 5 | variable "azure_region" { 6 | default = "uksouth" 7 | } 8 | 9 | variable "azure_environment" { 10 | default = "public" 11 | } 12 | 13 | variable "vnet_name" { 14 | default = "virtualNet" 15 | } 16 | 17 | variable "vnet_cidr" { 18 | default = "172.31.0.0/16" 19 | } 20 | 21 | variable "address_space" { 22 | default = "172.31.0.0/16" 23 | } 24 | 25 | variable "admin_password" { 26 | default = "orcaorcaorca" 27 | } 28 | 29 | variable "master_count" { 30 | default = 1 31 | } 32 | 33 | variable "worker_count" { 34 | default = 3 35 | } 36 | 37 | variable "windows_worker_count" { 38 | default = 0 39 | } 40 | 41 | variable "master_type" { 42 | default = "Standard_DS3_v2" 43 | } 44 | 45 | variable "worker_type" { 46 | default = "Standard_DS3_v2" 47 | } 48 | 49 | variable "master_volume_size" { 50 | default = 100 51 | } 52 | 53 | variable "worker_volume_size" { 54 | default = 100 55 | } 56 | 57 | variable "image_ubuntu1804" { 58 | description = "Default Ubuntu 18.04 LTS Image" 59 | type = map 60 | default = { 61 | "offer" = "UbuntuServer" 62 | "publisher" = "Canonical" 63 | "sku" = "18.04-LTS" 64 | "version" = "latest" 65 | } 66 | } 67 | 68 | variable "image_windows2019" { 69 | description = "Default Windows 2019 Server Image" 70 | type = map 71 | default = { 72 | "offer" = "WindowsServer" 73 | "publisher" = "MicrosoftWindowsServer" 74 | "sku" = "2019-Datacenter" 75 | "version" = "latest" 76 | } 77 | } 78 | 79 | variable "windows_admin_username" { 80 | default = "DockerAdmin" 81 | } 82 | 83 | variable "tags" { 84 | type = map 85 | default = { 86 | "Owner" = "Launchpad" 87 | } 88 | } 89 | 90 | variable "fault_domain_count" { 91 | description = "Specifies the number of fault domains that are used" 92 | default = 2 93 | } 94 | 95 | variable "update_domain_count" { 96 | description = "Specifies the number of update domains that are used" 97 | default = 2 98 | } 99 | 100 | variable "mke_version" { 101 | default = "3.5.5" 102 | } 103 | -------------------------------------------------------------------------------- /examples/terraform/azure/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | 5 | required_providers { 6 | azurerm = "= 2.32.0" 7 | local = "= 2.0.0" 8 | random = "= 3.0.0" 9 | tls = "= 3.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/terraform/gcp/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping MKE cluster on GCP 2 | 3 | This directory provides an example flow for using Mirantis Launchpad with Terraform and GCP. 4 | 5 | ## Prerequisites 6 | 7 | * An account and credentials for GCP. 8 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 9 | 10 | ## Authentication 11 | 12 | The Terraform `google` provider uses JSON key file for authentication. Download the JSON key file for a service account and place it in a secure location on your workstation. 13 | See [here](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started#adding-credentials) for more information. 14 | 15 | The authentication credentials can be passed to `google` provider in two ways: 16 | * Setting environment variable GOOGLE_APPLICATION_CREDENTIALS with JSON key location. 17 | * Setting `gcp_service_credential` variable in `terraform.tfvars` file. 18 | 19 | ## Steps 20 | 21 | 1. Create terraform.tfvars file with needed details. You can use the provided terraform.tfvars.example as a baseline. 22 | 2. `terraform init` 23 | 3. `terraform apply` 24 | 4. `terraform output --raw mke_cluster | launchpad apply --config -` 25 | 26 | ## Notes 27 | 28 | 1. Both RDP and WinRM ports are opened for Windows workers. 29 | -------------------------------------------------------------------------------- /examples/terraform/gcp/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | google = { 4 | source = "hashicorp/google" 5 | version = "4.11.0" 6 | } 7 | } 8 | } 9 | 10 | provider "google" { 11 | credentials = var.gcp_service_credential 12 | 13 | project = var.project_id 14 | region = var.gcp_region 15 | zone = var.gcp_zone 16 | } 17 | 18 | data "google_compute_zones" "available" { 19 | } 20 | 21 | # If the zone is not specified, use the first zone from the available zones 22 | locals { 23 | zone = var.gcp_zone != "" ? var.gcp_zone : data.google_compute_zones.available.names[0] 24 | } 25 | 26 | module "vpc" { 27 | source = "./modules/vpc" 28 | project_id = var.project_id 29 | cluster_name = var.cluster_name 30 | host_cidr = var.vpc_cidr 31 | gcp_region = var.gcp_region 32 | vpc_mtu = var.vpc_mtu 33 | } 34 | 35 | module "common" { 36 | source = "./modules/common" 37 | project_id = var.project_id 38 | cluster_name = var.cluster_name 39 | vpc_name = module.vpc.vpc_name 40 | } 41 | 42 | module "managers" { 43 | source = "./modules/manager" 44 | manager_count = var.manager_count 45 | gcp_region = var.gcp_region 46 | gcp_zone = local.zone 47 | cluster_name = var.cluster_name 48 | image_name = module.common.image_name 49 | vpc_name = module.vpc.vpc_name 50 | subnetwork_name = module.vpc.subnet_name 51 | ssh_key = module.common.ssh_key 52 | service_account_email = module.common.service_account_email 53 | } 54 | 55 | module "msrs" { 56 | source = "./modules/msr" 57 | msr_count = var.msr_count 58 | gcp_region = var.gcp_region 59 | gcp_zone = local.zone 60 | cluster_name = var.cluster_name 61 | image_name = module.common.image_name 62 | vpc_name = module.vpc.vpc_name 63 | subnetwork_name = module.vpc.subnet_name 64 | ssh_key = module.common.ssh_key 65 | } 66 | 67 | module "workers" { 68 | source = "./modules/worker" 69 | worker_count = var.worker_count 70 | gcp_region = var.gcp_region 71 | gcp_zone = local.zone 72 | cluster_name = var.cluster_name 73 | vpc_name = module.vpc.vpc_name 74 | subnetwork_name = module.vpc.subnet_name 75 | image_name = module.common.image_name 76 | ssh_key = module.common.ssh_key 77 | worker_type = var.worker_type 78 | service_account_email = module.common.service_account_email 79 | } 80 | 81 | module "windows_workers" { 82 | source = "./modules/windows_worker" 83 | worker_count = var.windows_worker_count 84 | gcp_zone = local.zone 85 | cluster_name = var.cluster_name 86 | vpc_name = module.vpc.vpc_name 87 | subnetwork_name = module.vpc.subnet_name 88 | image_name = module.common.windows_2019_image_name 89 | ssh_key = module.common.ssh_key 90 | worker_type = var.worker_type 91 | windows_user = var.windows_user 92 | windows_password = var.windows_password 93 | service_account_email = module.common.service_account_email 94 | } 95 | 96 | locals { 97 | managers = [ 98 | for host in module.managers.machines : { 99 | ssh = { 100 | address = host.network_interface.0.access_config.0.nat_ip 101 | user = "ubuntu" 102 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 103 | } 104 | role = host.metadata["role"] 105 | privateInterface = "ens4" 106 | } 107 | ] 108 | 109 | msrs = [ 110 | for host in module.msrs.machines : { 111 | ssh = { 112 | address = host.network_interface.0.access_config.0.nat_ip 113 | user = "ubuntu" 114 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 115 | } 116 | role = host.metadata["role"] 117 | privateInterface = "ens4" 118 | } 119 | ] 120 | 121 | workers = [ 122 | for host in module.workers.machines : { 123 | ssh = { 124 | address = host.network_interface.0.access_config.0.nat_ip 125 | user = "ubuntu" 126 | keyPath = "./ssh_keys/${var.cluster_name}.pem" 127 | } 128 | role = host.metadata["role"] 129 | privateInterface = "ens4" 130 | } 131 | ] 132 | 133 | windows_workers = [ 134 | for host in module.windows_workers.machines : { 135 | winRM = { 136 | address = host.network_interface.0.access_config.0.nat_ip 137 | user = var.windows_user 138 | password = var.windows_password 139 | useHTTPS = true 140 | insecure = true 141 | } 142 | role = host.metadata["role"] 143 | privateInterface = "Ethernet" 144 | } 145 | ] 146 | 147 | mke_launchpad_tmpl = { 148 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 149 | kind = "mke" 150 | spec = { 151 | mke = { 152 | version = var.mke_version 153 | adminUsername = "admin" 154 | adminPassword = var.admin_password 155 | installFlags : [ 156 | "--default-node-orchestrator=kubernetes", 157 | "--san=${module.managers.lb_public_ip_address}", 158 | ] 159 | } 160 | msr = {} 161 | hosts = concat(local.managers, local.msrs, local.workers, local.windows_workers) 162 | } 163 | } 164 | 165 | msr_launchpad_tmpl = { 166 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 167 | kind = "mke+msr" 168 | spec = { 169 | mke = { 170 | version = var.mke_version 171 | adminUsername = "admin" 172 | adminPassword = var.admin_password 173 | installFlags : [ 174 | "--default-node-orchestrator=kubernetes", 175 | "--san=${module.managers.lb_public_ip_address}", 176 | ] 177 | } 178 | msr = { 179 | installFlags : [ 180 | "--ucp-insecure-tls", 181 | "--dtr-external-url ${module.msrs.lb_public_ip_address}", 182 | ] 183 | } 184 | hosts = concat(local.managers, local.msrs, local.workers, local.windows_workers) 185 | } 186 | } 187 | 188 | launchpad_tmpl = var.msr_count > 0 ? local.msr_launchpad_tmpl : local.mke_launchpad_tmpl 189 | } 190 | 191 | 192 | output "mke_cluster" { 193 | value = yamlencode(local.launchpad_tmpl) 194 | } 195 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/common/main.tf: -------------------------------------------------------------------------------- 1 | resource "tls_private_key" "ssh_key" { 2 | algorithm = "RSA" 3 | rsa_bits = "4096" 4 | } 5 | 6 | resource "local_file" "ssh_public_key" { 7 | content = tls_private_key.ssh_key.private_key_pem 8 | filename = "ssh_keys/${var.cluster_name}.pem" 9 | provisioner "local-exec" { 10 | command = "chmod 0600 ${local_file.ssh_public_key.filename}" 11 | } 12 | } 13 | 14 | 15 | data "google_compute_image" "ubuntu" { 16 | family = "ubuntu-2004-lts" 17 | project = "ubuntu-os-cloud" 18 | } 19 | 20 | data "google_compute_image" "windows_2019" { 21 | family = "windows-2019-core" 22 | project = "windows-cloud" 23 | } 24 | 25 | resource "google_service_account" "default" { 26 | account_id = "${var.cluster_name}-service-account-id" 27 | display_name = "Service Account" 28 | } 29 | 30 | resource "google_project_iam_member" "default" { 31 | project = var.project_id 32 | member = "serviceAccount:${google_service_account.default.email}" 33 | role = "roles/compute.admin" 34 | } 35 | 36 | resource "google_compute_firewall" "common_internal" { 37 | name = "${var.cluster_name}-internal" 38 | description = "mke cluster common rule to allow all internal traffic" 39 | network = var.vpc_name 40 | direction = "INGRESS" 41 | allow { 42 | protocol = "all" 43 | } 44 | 45 | target_tags = ["allow-internal"] 46 | source_tags = ["allow-internal"] 47 | } 48 | 49 | resource "google_compute_firewall" "common_ssh" { 50 | name = "${var.cluster_name}-ssh" 51 | description = "mke cluster common rule" 52 | network = var.vpc_name 53 | direction = "INGRESS" 54 | allow { 55 | protocol = "tcp" 56 | ports = ["22"] 57 | } 58 | target_tags = ["allow-ssh"] 59 | source_ranges = ["0.0.0.0/0"] 60 | } 61 | 62 | resource "google_compute_firewall" "common_all_egress" { 63 | name = "${var.cluster_name}-all-egress" 64 | description = "mke cluster common rule" 65 | network = var.vpc_name 66 | direction = "EGRESS" 67 | allow { 68 | protocol = "all" 69 | } 70 | destination_ranges = ["0.0.0.0/0"] 71 | } 72 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/common/outputs.tf: -------------------------------------------------------------------------------- 1 | output "image_name" { 2 | value = data.google_compute_image.ubuntu.name 3 | } 4 | 5 | output "windows_2019_image_name" { 6 | value = data.google_compute_image.windows_2019.name 7 | } 8 | 9 | output "ssh_key" { 10 | value = tls_private_key.ssh_key 11 | } 12 | 13 | output "service_account_email" { 14 | value = google_service_account.default.email 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/common/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "project_id" {} 4 | 5 | variable "vpc_name" {} 6 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/manager/main.tf: -------------------------------------------------------------------------------- 1 | data "google_client_openid_userinfo" "me" {} 2 | 3 | resource "google_compute_firewall" "manager_internal" { 4 | name = "${var.cluster_name}-managers-internal" 5 | description = "mke cluster managers nodes internal traffic" 6 | network = var.vpc_name 7 | direction = "INGRESS" 8 | allow { 9 | protocol = "tcp" 10 | ports = ["2379-2380"] 11 | } 12 | 13 | target_tags = ["allow-manager"] 14 | source_tags = ["allow-manager"] 15 | } 16 | 17 | resource "google_compute_firewall" "manager" { 18 | name = "${var.cluster_name}-managers" 19 | description = "mke cluster managers ingress traffic" 20 | network = var.vpc_name 21 | direction = "INGRESS" 22 | allow { 23 | protocol = "tcp" 24 | ports = ["443", "6443"] 25 | } 26 | 27 | source_ranges = ["0.0.0.0/0"] 28 | target_tags = ["allow-manager"] 29 | } 30 | 31 | resource "google_compute_instance" "mke_manager" { 32 | count = var.manager_count 33 | name = "${var.cluster_name}-manager-${count.index + 1}" 34 | machine_type = var.manager_type 35 | zone = var.gcp_zone 36 | 37 | metadata = tomap({ 38 | "role" = "manager" 39 | ssh-keys = "ubuntu:${var.ssh_key.public_key_openssh}" 40 | }) 41 | 42 | boot_disk { 43 | initialize_params { 44 | image = var.image_name 45 | type = var.manager_volume_type 46 | size = var.manager_volume_size 47 | } 48 | } 49 | 50 | network_interface { 51 | network = var.vpc_name 52 | subnetwork = var.subnetwork_name 53 | access_config { 54 | } 55 | } 56 | 57 | tags = [ 58 | var.cluster_name, 59 | "allow-ssh", 60 | "allow-manager", 61 | "allow-internal" 62 | ] 63 | 64 | service_account { 65 | email = var.service_account_email 66 | scopes = [ 67 | "https://www.googleapis.com/auth/cloud-platform" 68 | ] 69 | } 70 | } 71 | 72 | resource "google_compute_instance_group" "default" { 73 | name = "${var.cluster_name}-manager-group" 74 | description = "Manager nodes instances group" 75 | zone = var.gcp_zone 76 | instances = [for i in google_compute_instance.mke_manager : i.self_link] 77 | 78 | named_port { 79 | name = "api" 80 | port = 443 81 | } 82 | 83 | named_port { 84 | name = "kubeapi" 85 | port = 6443 86 | } 87 | } 88 | 89 | module "load_balancer_manager" { 90 | source = "../networklb" 91 | region = var.gcp_region 92 | network = var.vpc_name 93 | name = "${var.cluster_name}-manager-lb" 94 | service_ports = [443, 6443] 95 | health_check_port = 443 96 | target_instance_group = google_compute_instance_group.default.self_link 97 | } 98 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/manager/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_public_ip_address" { 2 | value = module.load_balancer_manager.external_ip 3 | } 4 | 5 | output "public_ips" { 6 | value = google_compute_instance.mke_manager.*.network_interface.0.access_config.0.nat_ip 7 | } 8 | 9 | output "private_ips" { 10 | value = google_compute_instance.mke_manager.*.network_interface.0.network_ip 11 | } 12 | 13 | output "machines" { 14 | value = google_compute_instance.mke_manager 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/manager/variables.tf: -------------------------------------------------------------------------------- 1 | variable "gcp_region" {} 2 | 3 | variable "gcp_zone" {} 4 | 5 | variable "cluster_name" {} 6 | 7 | variable "vpc_name" {} 8 | 9 | variable "subnetwork_name" {} 10 | 11 | variable "image_name" {} 12 | 13 | variable "ssh_key" {} 14 | 15 | variable "service_account_email" {} 16 | 17 | variable "manager_count" { 18 | default = 3 19 | } 20 | 21 | variable "manager_type" { 22 | default = "e2-standard-4" 23 | } 24 | 25 | variable "manager_volume_type" { 26 | default = "pd-balanced" 27 | } 28 | 29 | variable "manager_volume_size" { 30 | default = 100 31 | } 32 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/msr/main.tf: -------------------------------------------------------------------------------- 1 | resource "google_compute_firewall" "worker" { 2 | name = "${var.cluster_name}-msr" 3 | description = "mke cluster msrs" 4 | network = var.vpc_name 5 | direction = "INGRESS" 6 | allow { 7 | protocol = "tcp" 8 | ports = ["80", "443"] 9 | } 10 | source_ranges = ["0.0.0.0/0"] 11 | target_tags = ["allow-msr", "allow-lb-service-msr"] 12 | } 13 | 14 | resource "google_compute_instance" "mke_msr" { 15 | count = var.msr_count 16 | name = "${var.cluster_name}-msr-${count.index + 1}" 17 | machine_type = var.msr_type 18 | zone = var.gcp_zone 19 | 20 | metadata = tomap({ 21 | "role" = "msr" 22 | ssh-keys = "ubuntu:${var.ssh_key.public_key_openssh}" 23 | }) 24 | 25 | boot_disk { 26 | initialize_params { 27 | image = var.image_name 28 | type = var.msr_volume_type 29 | size = var.msr_volume_size 30 | } 31 | } 32 | 33 | network_interface { 34 | network = var.vpc_name 35 | subnetwork = var.subnetwork_name 36 | access_config { 37 | } 38 | } 39 | tags = [ 40 | var.cluster_name, 41 | "allow-ssh", 42 | "allow-msr", 43 | "allow-internal" 44 | ] 45 | } 46 | 47 | resource "google_compute_instance_group" "default" { 48 | name = "${var.cluster_name}-msr-group" 49 | description = "MSR nodes instances group" 50 | zone = var.gcp_zone 51 | instances = [for i in google_compute_instance.mke_msr : i.self_link] 52 | } 53 | 54 | module "load_balancer_msr" { 55 | source = "../networklb" 56 | region = var.gcp_region 57 | name = "${var.cluster_name}-msr-lb" 58 | service_ports = [443] 59 | health_check_port = 443 60 | network = var.vpc_name 61 | target_instance_group = google_compute_instance_group.default.self_link 62 | } 63 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/msr/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_public_ip_address" { 2 | value = module.load_balancer_msr.external_ip 3 | } 4 | 5 | output "public_ips" { 6 | value = google_compute_instance.mke_msr.*.network_interface.0.access_config.0.nat_ip 7 | } 8 | 9 | output "private_ips" { 10 | value = google_compute_instance.mke_msr.*.network_interface.0.network_ip 11 | } 12 | 13 | output "machines" { 14 | value = google_compute_instance.mke_msr 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/msr/variables.tf: -------------------------------------------------------------------------------- 1 | variable "gcp_region" {} 2 | 3 | variable "gcp_zone" {} 4 | 5 | variable "cluster_name" {} 6 | 7 | variable "vpc_name" {} 8 | 9 | variable "subnetwork_name" {} 10 | 11 | variable "image_name" {} 12 | 13 | variable "ssh_key" {} 14 | 15 | variable "msr_count" { 16 | default = 3 17 | } 18 | 19 | variable "msr_type" { 20 | default = "e2-standard-4" 21 | } 22 | 23 | variable "msr_volume_type" { 24 | default = "pd-balanced" 25 | } 26 | 27 | variable "msr_volume_size" { 28 | default = 100 29 | } 30 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/networklb/main.tf: -------------------------------------------------------------------------------- 1 | resource "google_compute_forwarding_rule" "default" { 2 | project = var.project 3 | name = var.name 4 | region = var.region 5 | ports = var.service_ports 6 | backend_service = google_compute_region_backend_service.default.self_link 7 | } 8 | 9 | resource "google_compute_region_backend_service" "default" { 10 | name = "${var.name}-backend-service" 11 | project = var.project 12 | protocol = "TCP" 13 | load_balancing_scheme = "EXTERNAL" 14 | 15 | health_checks = [google_compute_region_health_check.default.self_link] 16 | 17 | backend { 18 | group = var.target_instance_group 19 | } 20 | } 21 | 22 | resource "google_compute_region_health_check" "default" { 23 | project = var.project 24 | name = "${var.name}-hc" 25 | 26 | https_health_check { 27 | port = var.health_check_port 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/networklb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "external_ip" { 2 | description = "The external ip address of the load balancer" 3 | value = google_compute_forwarding_rule.default.ip_address 4 | } 5 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/networklb/variables.tf: -------------------------------------------------------------------------------- 1 | variable "project" { 2 | type = string 3 | default = "" 4 | } 5 | 6 | variable "region" { 7 | type = string 8 | default = "" 9 | } 10 | 11 | variable "network" { 12 | type = string 13 | } 14 | 15 | variable "name" { 16 | type = string 17 | } 18 | 19 | variable "service_ports" { 20 | type = list(number) 21 | description = "List of TCP port your service is listening on." 22 | } 23 | 24 | 25 | variable "health_check_port" { 26 | type = number 27 | description = "Health check port for the service" 28 | } 29 | 30 | variable "target_instance_group" { 31 | type = string 32 | description = "Target instance group for the network load balancer" 33 | } 34 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/networklb/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/vpc/main.tf: -------------------------------------------------------------------------------- 1 | # Network VPC and subnets 2 | resource "google_compute_network" "vpc_network" { 3 | project = var.project_id 4 | name = var.cluster_name 5 | auto_create_subnetworks = false 6 | routing_mode = "REGIONAL" 7 | mtu = var.vpc_mtu 8 | } 9 | 10 | resource "google_compute_subnetwork" "subnetwork" { 11 | ip_cidr_range = var.host_cidr 12 | name = format("subnet-%s-%s", var.gcp_region, var.cluster_name) 13 | network = google_compute_network.vpc_network.id 14 | region = var.gcp_region 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_name" { 2 | value = google_compute_network.vpc_network.name 3 | } 4 | 5 | output "subnet_name" { 6 | value = google_compute_subnetwork.subnetwork.name 7 | } 8 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | variable "project_id" {} 3 | variable "gcp_region" {} 4 | 5 | variable "vpc_mtu" {} 6 | 7 | variable "host_cidr" { 8 | description = "CIDR IPv4 range to assign to GCE nodes" 9 | default = "172.31.0.0/16" 10 | } 11 | -------------------------------------------------------------------------------- /examples/terraform/gcp/modules/windows_worker/main.tf: -------------------------------------------------------------------------------- 1 | resource "google_compute_firewall" "winrm" { 2 | name = "${var.cluster_name}-win-worker" 3 | description = "winrm access for windows workers" 4 | network = var.vpc_name 5 | direction = "INGRESS" 6 | allow { 7 | protocol = "tcp" 8 | ports = ["5985-5990"] 9 | } 10 | source_ranges = ["0.0.0.0/0"] 11 | target_tags = ["allow-winrm"] 12 | } 13 | 14 | resource "google_compute_firewall" "rdp" { 15 | name = "${var.cluster_name}-win-rdp" 16 | description = "rdp access for windows workers" 17 | network = var.vpc_name 18 | direction = "INGRESS" 19 | allow { 20 | protocol = "tcp" 21 | ports = ["3389"] 22 | } 23 | source_ranges = ["0.0.0.0/0"] 24 | target_tags = ["allow-rdp"] 25 | } 26 | 27 | resource "google_compute_instance" "mke_win_worker" { 28 | count = var.worker_count 29 | 30 | name = "${var.cluster_name}-win-worker-${count.index + 1}" 31 | machine_type = var.worker_type 32 | zone = var.gcp_zone 33 | metadata = tomap({ 34 | role = "worker" 35 | windows-startup-script-ps1 = < ./launchpad.yaml 13 | echo "Your launchpad.yaml sample is stored in your current directory" && ls ./launchpad.yaml 14 | echo "Apply your configuration via launchpad apply" 15 | 16 | exit 0 -------------------------------------------------------------------------------- /examples/terraform/gcp/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cluster_name = "my-mke-cluster" 2 | manager_count = 1 3 | msr_count = 1 4 | worker_count = 3 5 | windows_worker_count = 0 6 | admin_password = "orcaorcaorca" -------------------------------------------------------------------------------- /examples/terraform/gcp/variables.tf: -------------------------------------------------------------------------------- 1 | variable "project_id" { 2 | default = "orchestration-348616" 3 | } 4 | 5 | variable "cluster_name" { 6 | default = "mke" 7 | } 8 | 9 | variable "gcp_region" { 10 | default = "us-central1" 11 | } 12 | 13 | variable "gcp_zone" { 14 | default = "us-central1-a" 15 | } 16 | 17 | variable "gcp_service_credential" { 18 | default = "" 19 | } 20 | 21 | variable "vpc_mtu" { 22 | default = 1500 23 | description = "MTU for the VPC. GCP support two MTU values for the VPC: 1460 or 1500" 24 | } 25 | 26 | variable "vpc_cidr" { 27 | default = "172.31.0.0/16" 28 | } 29 | 30 | variable "admin_password" { 31 | default = "dockeradmin" 32 | } 33 | 34 | variable "manager_count" { 35 | default = 1 36 | } 37 | 38 | variable "worker_count" { 39 | default = 3 40 | } 41 | 42 | variable "windows_worker_count" { 43 | default = 0 44 | } 45 | 46 | variable "msr_count" { 47 | default = 0 48 | } 49 | 50 | variable "manager_type" { 51 | default = "e2-standard-4" 52 | } 53 | 54 | variable "worker_type" { 55 | default = "e2-standard-4" 56 | } 57 | 58 | variable "msr_type" { 59 | default = "e2-standard-4" 60 | } 61 | 62 | variable "manager_volume_type" { 63 | default = "pd-balanced" 64 | } 65 | 66 | variable "manager_volume_size" { 67 | default = 100 68 | } 69 | 70 | variable "worker_volume_size" { 71 | default = 100 72 | } 73 | 74 | variable "msr_volume_size" { 75 | default = 100 76 | } 77 | 78 | variable "windows_user" { 79 | default = "winadmin" 80 | } 81 | 82 | variable "windows_password" { 83 | default = "w!ndozePassw0rd" 84 | } 85 | 86 | variable "mke_version" { 87 | default = "3.5.5" 88 | } 89 | -------------------------------------------------------------------------------- /examples/terraform/gcp/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/terraform/hetzner/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping MKE cluster on Hetzner 2 | 3 | This directory provides an example flow with Mirantis Launchpad tool together with Terraform using [Hetzner](https://www.hetzner.com/cloud) as the cloud provider. 4 | 5 | 6 | ## Prerequisites 7 | 8 | * You need an account and API token for Hetzner 9 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 10 | 11 | ## Steps 12 | 13 | 1. Create terraform.tfvars file with needed details. You can use the provided terraform.tfvars.example as a baseline. 14 | 2. `terraform init` 15 | 3. `terraform apply` 16 | 4. `terraform output mke_cluster | launchpad apply --config -` 17 | -------------------------------------------------------------------------------- /examples/terraform/hetzner/main.tf: -------------------------------------------------------------------------------- 1 | variable "hcloud_token" { 2 | description = "Hetzner API token" 3 | } 4 | 5 | provider "hcloud" { 6 | token = var.hcloud_token 7 | } 8 | 9 | variable "ssh_keys" { 10 | default = [] 11 | } 12 | 13 | variable "ssh_user" { 14 | default = "root" 15 | } 16 | 17 | variable "cluster_name" { 18 | default = "mke" 19 | } 20 | 21 | variable "location" { 22 | default = "hel1" 23 | } 24 | 25 | variable "image" { 26 | default = "ubuntu-18.04" 27 | } 28 | 29 | variable "master_type" { 30 | default = "cx31" 31 | } 32 | 33 | variable "worker_count" { 34 | default = 1 35 | } 36 | 37 | variable "master_count" { 38 | default = 1 39 | } 40 | 41 | variable "worker_type" { 42 | default = "cx31" 43 | } 44 | 45 | resource "hcloud_server" "master" { 46 | count = var.master_count 47 | name = "${var.cluster_name}-master-${count.index}" 48 | image = var.image 49 | server_type = var.master_type 50 | ssh_keys = var.ssh_keys 51 | location = var.location 52 | labels = { 53 | role = "manager" 54 | } 55 | } 56 | 57 | resource "hcloud_server" "worker" { 58 | count = var.worker_count 59 | name = "${var.cluster_name}-worker-${count.index}" 60 | image = var.image 61 | server_type = var.worker_type 62 | ssh_keys = var.ssh_keys 63 | location = var.location 64 | labels = { 65 | role = "worker" 66 | } 67 | } 68 | 69 | locals { 70 | launchpad_tmpl = { 71 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 72 | kind = "mke" 73 | spec = { 74 | hosts = [ 75 | for host in concat(hcloud_server.master, hcloud_server.worker) : { 76 | ssh = { 77 | address = host.ipv4_address 78 | user = "root" 79 | } 80 | role = host.labels.role 81 | } 82 | ] 83 | } 84 | } 85 | } 86 | 87 | output "mke_cluster" { 88 | value = yamlencode(local.launchpad_tmpl) 89 | } 90 | -------------------------------------------------------------------------------- /examples/terraform/hetzner/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | ssh_keys = ["you@domain.com"] 2 | hcloud_token = "your-api-token" 3 | cluster_name = "mke" 4 | location = "hel1" 5 | image = "ubuntu-18.04" 6 | master_type = "cx31" 7 | master_count = 1 8 | worker_type = "cx31" 9 | worker_count = 2 -------------------------------------------------------------------------------- /examples/terraform/openstack/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrap MKE clusters on OpenStack 2 | 3 | This directory provides an example flow with Mirantis Launchpad together with Terraform and OpenStack. 4 | 5 | ## Prerequisites 6 | 7 | * You need an account and credentials for an OpenStack Tenant. 8 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 9 | * [Generate SSH key](https://help.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key) 10 | 11 | ## Steps 12 | 13 | 1. Create terraform.tfvars file with needed details. You can use the provided terraform.tfvars.example as a baseline. 14 | 2. `terraform init` 15 | 3. Create SSH key and configure path 16 | 4. Create Cloud Provider config file and configure path 17 | 5. Configure .tfvars file with all necessary parameters 18 | 6. `terraform apply` 19 | 7. `terraform output mke_cluster | launchpad apply --config -` 20 | 21 | ## Related topics 22 | 23 | 1. [How to generate a SSH key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) 24 | 25 | 2. Configure [OpenStack Cloud Provider](https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/getting-started-provider-dev.md) config 26 | -------------------------------------------------------------------------------- /examples/terraform/openstack/main.tf: -------------------------------------------------------------------------------- 1 | provider "openstack" { 2 | use_octavia = "true" 3 | region = var.region 4 | } 5 | 6 | 7 | resource "openstack_compute_keypair_v2" "key-pair" { 8 | name = "launchpad-key" 9 | public_key = file("${var.ssh_key_path}.pub") 10 | } 11 | 12 | module "network" { 13 | source = "./modules/network" 14 | cluster_name = var.cluster_name 15 | external_network_id = var.external_network_id 16 | dns_ips = var.dns_ip_list 17 | } 18 | 19 | module "masters" { 20 | source = "./modules/masters" 21 | master_count = var.master_count 22 | cluster_name = var.cluster_name 23 | ssh_key = "launchpad-key" 24 | master_image_name = var.master_image_name 25 | master_flavor = var.master_flavor 26 | external_network_name = var.external_network_name 27 | internal_network_name = module.network.network_name 28 | internal_subnet_id = module.network.subnet_id 29 | base_sec_group_id = module.network.base_security_group_id 30 | } 31 | 32 | module "workers" { 33 | source = "./modules/workers" 34 | worker_count = var.worker_count 35 | cluster_name = var.cluster_name 36 | ssh_key = "launchpad-key" 37 | worker_image_name = var.worker_image_name 38 | worker_flavor = var.worker_flavor 39 | external_network_name = var.external_network_name 40 | internal_network_name = module.network.network_name 41 | internal_subnet_id = module.network.subnet_id 42 | base_sec_group_id = module.network.base_security_group_id 43 | } 44 | 45 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/lb.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_lb_loadbalancer_v2" "lb_mke" { 2 | name = "${var.cluster_name}-mke" 3 | vip_subnet_id = var.internal_subnet_id 4 | } 5 | 6 | resource "openstack_lb_listener_v2" "lb_list_mke" { 7 | name = "${var.cluster_name}-mke-443" 8 | protocol = "TCP" 9 | protocol_port = 443 10 | loadbalancer_id = openstack_lb_loadbalancer_v2.lb_mke.id 11 | } 12 | 13 | resource "openstack_lb_pool_v2" "lb_pool_mke" { 14 | name = "${var.cluster_name}-mke-443" 15 | protocol = "TCP" 16 | lb_method = "ROUND_ROBIN" 17 | listener_id = openstack_lb_listener_v2.lb_list_mke.id 18 | } 19 | 20 | resource "openstack_lb_member_v2" "lb_member_mke" { 21 | count = var.master_count 22 | pool_id = openstack_lb_pool_v2.lb_pool_mke.id 23 | protocol_port = 443 24 | address = element(openstack_compute_instance_v2.docker-master.*.network.0.fixed_ip_v4, count.index) 25 | subnet_id = var.internal_subnet_id 26 | } 27 | 28 | resource "openstack_networking_floatingip_v2" "master_lb_vip" { 29 | pool = var.external_network_name 30 | } 31 | 32 | resource "openstack_networking_floatingip_associate_v2" "master_vip" { 33 | floating_ip = openstack_networking_floatingip_v2.master_lb_vip.address 34 | port_id = openstack_lb_loadbalancer_v2.lb_mke.vip_port_id 35 | } 36 | 37 | 38 | resource "openstack_lb_listener_v2" "lb_list_mke2" { 39 | protocol = "TCP" 40 | name = "${var.cluster_name}-mke-6443" 41 | protocol_port = 6443 42 | loadbalancer_id = openstack_lb_loadbalancer_v2.lb_mke.id 43 | } 44 | 45 | resource "openstack_lb_pool_v2" "lb_pool_mke2" { 46 | protocol = "TCP" 47 | name = "${var.cluster_name}-mke-6443" 48 | lb_method = "ROUND_ROBIN" 49 | listener_id = openstack_lb_listener_v2.lb_list_mke2.id 50 | } 51 | 52 | resource "openstack_lb_member_v2" "lb_member_mke2" { 53 | count = var.master_count 54 | pool_id = openstack_lb_pool_v2.lb_pool_mke2.id 55 | protocol_port = 6443 56 | address = element(openstack_compute_instance_v2.docker-master.*.network.0.fixed_ip_v4, count.index) 57 | subnet_id = var.internal_subnet_id 58 | } 59 | 60 | # TODO: Change to http check (GA) 61 | resource "openstack_lb_monitor_v2" "mke" { 62 | pool_id = openstack_lb_pool_v2.lb_pool_mke.id 63 | type = "PING" 64 | delay = 20 65 | timeout = 10 66 | max_retries = 3 67 | } 68 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/main.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_compute_instance_v2" "docker-master" { 2 | count = var.master_count 3 | 4 | name = "docker-master-${count.index}" 5 | image_name = var.master_image_name 6 | flavor_name = var.master_flavor 7 | key_pair = var.ssh_key 8 | security_groups = [var.base_sec_group_id,openstack_networking_secgroup_v2.docker-master.name] 9 | network { 10 | name = var.internal_network_name 11 | } 12 | user_data = < /etc/hostname 17 | sed -i "s|\(127\.0\..\.. *\)localhost|\1$HOSTNAME|" /etc/hosts 18 | hostname $HOSTNAME 19 | EOF 20 | 21 | } 22 | 23 | resource "openstack_networking_floatingip_v2" "master" { 24 | count = var.master_count 25 | pool = var.external_network_name 26 | } 27 | 28 | resource "openstack_compute_floatingip_associate_v2" "master" { 29 | count = var.master_count 30 | floating_ip = element(openstack_networking_floatingip_v2.master.*.address, count.index) 31 | instance_id = element(openstack_compute_instance_v2.docker-master.*.id, count.index) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_ip" { 2 | value = openstack_networking_floatingip_v2.master_lb_vip.address 3 | } 4 | 5 | output "public_ips" { 6 | value = openstack_compute_floatingip_associate_v2.master.*.floating_ip 7 | } 8 | 9 | output "private_ips" { 10 | value = openstack_compute_instance_v2.docker-master.*.network.0.fixed_ip_v4 11 | } 12 | 13 | output "machines" { 14 | value = openstack_compute_instance_v2.docker-master 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/security_group.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_networking_secgroup_v2" "docker-master" { 2 | name = "${var.cluster_name}-master" 3 | description = "Open ingress ports to master nodes" 4 | } 5 | 6 | 7 | resource "openstack_networking_secgroup_rule_v2" "master_https" { 8 | direction = "ingress" 9 | ethertype = "IPv4" 10 | port_range_min = 443 11 | port_range_max = 443 12 | protocol = "tcp" 13 | remote_ip_prefix = "0.0.0.0/0" 14 | security_group_id = openstack_networking_secgroup_v2.docker-master.id 15 | } 16 | resource "openstack_networking_secgroup_rule_v2" "master_https_k8s" { 17 | direction = "ingress" 18 | ethertype = "IPv4" 19 | port_range_min = 6443 20 | port_range_max = 6443 21 | protocol = "tcp" 22 | remote_ip_prefix = "0.0.0.0/0" 23 | security_group_id = openstack_networking_secgroup_v2.docker-master.id 24 | } 25 | 26 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "master_count" {} 4 | 5 | variable "ssh_key" {} 6 | 7 | variable "master_image_name" {} 8 | 9 | variable "master_flavor" {} 10 | 11 | variable "master_volume_size" { 12 | default = 50 13 | } 14 | 15 | variable "external_network_name" {} 16 | 17 | variable "internal_network_name" {} 18 | 19 | variable "internal_subnet_id" {} 20 | 21 | variable "base_sec_group_id" {} 22 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/masters/volumes.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_blockstorage_volume_v2" "master" { 2 | count = var.master_count 3 | name = "#{var.cluster_name}-master-volume-${count.index}" 4 | size = var.master_volume_size 5 | } 6 | 7 | resource "openstack_compute_volume_attach_v2" "master" { 8 | count = var.master_count 9 | instance_id = element(openstack_compute_instance_v2.docker-master.*.id, count.index) 10 | volume_id = element(openstack_blockstorage_volume_v2.master.*.id, count.index) 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/network/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | 3 | docker-int-net = { 4 | name = "${var.cluster_name}-int-net" 5 | subnet_name = "${var.cluster_name}-net-sub01", 6 | cidr = var.cidr 7 | } 8 | } 9 | 10 | resource "openstack_networking_router_v2" "generic" { 11 | name = "${var.cluster_name}-router" 12 | external_network_id = var.external_network_id 13 | } 14 | 15 | resource "openstack_networking_network_v2" "docker-int" { 16 | name = local.docker-int-net["name"] 17 | } 18 | 19 | resource "openstack_networking_subnet_v2" "docker-int-subnet" { 20 | name = local.docker-int-net["subnet_name"] 21 | network_id = openstack_networking_network_v2.docker-int.id 22 | cidr = local.docker-int-net["cidr"] 23 | dns_nameservers = var.dns_ips 24 | } 25 | 26 | resource "openstack_networking_router_interface_v2" "docker-int" { 27 | router_id = openstack_networking_router_v2.generic.id 28 | subnet_id = openstack_networking_subnet_v2.docker-int-subnet.id 29 | } 30 | 31 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/network/outputs.tf: -------------------------------------------------------------------------------- 1 | output "base_security_group_id" { 2 | value = openstack_networking_secgroup_v2.docker-base.id 3 | } 4 | 5 | output "subnet_id" { 6 | value = openstack_networking_subnet_v2.docker-int-subnet.id 7 | } 8 | 9 | output "network_name" { 10 | value = openstack_networking_network_v2.docker-int.name 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/network/security_group.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_networking_secgroup_v2" "docker-base" { 2 | name = "${var.cluster_name}-base" 3 | description = "Open all external TCP ports to worker nodes" 4 | } 5 | 6 | #TODO: check if necessarry (Istio) 7 | resource "openstack_networking_secgroup_rule_v2" "docker-base-ssh" { 8 | direction = "ingress" 9 | ethertype = "IPv4" 10 | port_range_min = 22 11 | port_range_max = 22 12 | protocol = "tcp" 13 | remote_ip_prefix = "0.0.0.0/0" 14 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 15 | } 16 | 17 | resource "openstack_networking_secgroup_rule_v2" "docker-base-int-all-tcp" { 18 | direction = "ingress" 19 | ethertype = "IPv4" 20 | port_range_min = 1 21 | port_range_max = 65535 22 | protocol = "tcp" 23 | remote_ip_prefix = openstack_networking_subnet_v2.docker-int-subnet.cidr 24 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 25 | } 26 | 27 | resource "openstack_networking_secgroup_rule_v2" "docker-base-int-icmp" { 28 | direction = "ingress" 29 | ethertype = "IPv4" 30 | port_range_min = 1 31 | port_range_max = 1 32 | protocol = "icmp" 33 | remote_ip_prefix = openstack_networking_subnet_v2.docker-int-subnet.cidr 34 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 35 | } 36 | 37 | 38 | resource "openstack_networking_secgroup_rule_v2" "docker-base-int-icmp-out" { 39 | direction = "egress" 40 | ethertype = "IPv4" 41 | port_range_min = 1 42 | port_range_max = 1 43 | protocol = "icmp" 44 | remote_ip_prefix = openstack_networking_subnet_v2.docker-int-subnet.cidr 45 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 46 | } 47 | resource "openstack_networking_secgroup_rule_v2" "docker-base-int-all-udp" { 48 | direction = "ingress" 49 | ethertype = "IPv4" 50 | port_range_min = 1 51 | port_range_max = 65535 52 | protocol = "udp" 53 | remote_ip_prefix = openstack_networking_subnet_v2.docker-int-subnet.cidr 54 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 55 | } 56 | 57 | 58 | resource "openstack_networking_secgroup_rule_v2" "default_ipv4_encupsulation" { 59 | direction = "ingress" 60 | ethertype = "IPv4" 61 | protocol = "4" 62 | remote_ip_prefix = openstack_networking_subnet_v2.docker-int-subnet.cidr 63 | security_group_id = openstack_networking_secgroup_v2.docker-base.id 64 | } 65 | 66 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/network/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "external_network_id" {} 4 | 5 | variable "cidr" { 6 | default = "10.0.100.0/24" 7 | } 8 | 9 | variable "dns_ips" { 10 | type = list(string) 11 | } 12 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/lb.tf: -------------------------------------------------------------------------------- 1 | ##### Worker nodes => Istio ingress port ######## 2 | resource "openstack_lb_loadbalancer_v2" "lb_worker" { 3 | name = "${var.cluster_name}-worker-lb" 4 | vip_subnet_id = var.internal_subnet_id 5 | } 6 | 7 | resource "openstack_lb_listener_v2" "lb_list_worker" { 8 | protocol = "TCP" 9 | name = "${var.cluster_name}-worker-listener" 10 | protocol_port = 33000 11 | loadbalancer_id = openstack_lb_loadbalancer_v2.lb_worker.id 12 | } 13 | 14 | resource "openstack_lb_pool_v2" "lb_pool_worker" { 15 | protocol = "TCP" 16 | name = "${var.cluster_name}-worker-pool" 17 | lb_method = "ROUND_ROBIN" 18 | listener_id = openstack_lb_listener_v2.lb_list_worker.id 19 | } 20 | 21 | resource "openstack_lb_member_v2" "lb_member_worker" { 22 | count = var.worker_count 23 | pool_id = openstack_lb_pool_v2.lb_pool_worker.id 24 | protocol_port = 33000 25 | address = element(openstack_compute_instance_v2.docker-worker.*.network.0.fixed_ip_v4, count.index) 26 | subnet_id = var.internal_subnet_id 27 | } 28 | 29 | resource "openstack_networking_floatingip_v2" "worker_lb_vip" { 30 | pool = var.external_network_name 31 | } 32 | 33 | resource "openstack_networking_floatingip_associate_v2" "worker_vip" { 34 | floating_ip = openstack_networking_floatingip_v2.worker_lb_vip.address 35 | port_id = openstack_lb_loadbalancer_v2.lb_worker.vip_port_id 36 | } 37 | 38 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/main.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_compute_instance_v2" "docker-worker" { 2 | count = var.worker_count 3 | 4 | name = "docker-worker-${count.index}" 5 | image_name = var.worker_image_name 6 | flavor_name = var.worker_flavor 7 | key_pair = var.ssh_key 8 | security_groups = [var.base_sec_group_id ,openstack_networking_secgroup_v2.docker-worker.name] 9 | network { 10 | name = var.internal_network_name 11 | } 12 | 13 | user_data = < /etc/hostname 18 | sed -i "s|\(127\.0\..\.. *\)localhost|\1$HOSTNAME|" /etc/hosts 19 | hostname $HOSTNAME 20 | EOF 21 | 22 | } 23 | 24 | resource "openstack_networking_floatingip_v2" "worker" { 25 | count = var.worker_count 26 | pool = var.external_network_name 27 | } 28 | 29 | resource "openstack_compute_floatingip_associate_v2" "worker" { 30 | count = var.worker_count 31 | floating_ip = element(openstack_networking_floatingip_v2.worker.*.address, count.index) 32 | instance_id = element(openstack_compute_instance_v2.docker-worker.*.id, count.index) 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "lb_ip" { 2 | value = openstack_networking_floatingip_v2.worker_lb_vip.address 3 | } 4 | 5 | output "public_ips" { 6 | value = openstack_networking_floatingip_v2.worker.*.address 7 | } 8 | 9 | output "private_ips" { 10 | value = openstack_compute_instance_v2.docker-worker.*.network.0.fixed_ip_v4 11 | } 12 | 13 | output "machines" { 14 | value = openstack_compute_instance_v2.docker-worker 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/security_group.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_networking_secgroup_v2" "docker-worker" { 2 | name = "${var.cluster_name}-worker" 3 | description = "Open all external TCP ports to worker nodes" 4 | } 5 | 6 | #TODO: check if necessarry (Istio) 7 | resource "openstack_networking_secgroup_rule_v2" "worker_all" { 8 | direction = "ingress" 9 | ethertype = "IPv4" 10 | port_range_min = 1 11 | port_range_max = 65534 12 | protocol = "tcp" 13 | remote_ip_prefix = "0.0.0.0/0" 14 | security_group_id = openstack_networking_secgroup_v2.docker-worker.id 15 | } 16 | 17 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" {} 2 | 3 | variable "worker_count" {} 4 | 5 | variable "ssh_key" {} 6 | 7 | variable "worker_image_name" {} 8 | 9 | variable "worker_flavor" {} 10 | 11 | variable "worker_volume_size" { 12 | default = 50 13 | } 14 | 15 | variable "external_network_name" {} 16 | 17 | variable "internal_network_name" {} 18 | 19 | variable "internal_subnet_id" {} 20 | 21 | variable "base_sec_group_id" {} 22 | -------------------------------------------------------------------------------- /examples/terraform/openstack/modules/workers/volumes.tf: -------------------------------------------------------------------------------- 1 | resource "openstack_blockstorage_volume_v2" "worker" { 2 | count = var.worker_count 3 | name = "${var.cluster_name}-worker-volume-${count.index}" 4 | size = var.worker_volume_size 5 | } 6 | 7 | resource "openstack_compute_volume_attach_v2" "worker" { 8 | count = var.worker_count 9 | instance_id = element(openstack_compute_instance_v2.docker-worker.*.id, count.index) 10 | volume_id = element(openstack_blockstorage_volume_v2.worker.*.id, count.index) 11 | } 12 | -------------------------------------------------------------------------------- /examples/terraform/openstack/output.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | managers = [ 3 | for ip in module.masters.public_ips : { 4 | ssh: { 5 | address = ip 6 | user = "ubuntu" 7 | keyPath = "./ssh_keys/${var.cluster_name}" 8 | port = 22 9 | } 10 | role = "manager" 11 | privateInterface = "ens3" 12 | mcrConfig: { 13 | bip: var.docker_int_net 14 | default-address-pools: { 15 | base: var.docker_default_address_pool 16 | } 17 | } 18 | environment : { 19 | http_proxy = var.http_proxy 20 | HTTPS_PROXY = var.https_proxy 21 | } 22 | } 23 | ] 24 | workers = [ 25 | for ip in module.workers.public_ips : { 26 | ssh: { 27 | address = ip 28 | user = "ubuntu" 29 | keyPath = "./ssh_keys/${var.cluster_name}" 30 | port = 22 31 | } 32 | role = "worker" 33 | privateInterface = "ens3" 34 | mcrConfig: { 35 | bip: var.docker_int_net 36 | default-address-pools: { 37 | base: var.docker_default_address_pool 38 | } 39 | } 40 | environment : { 41 | http_proxy = var.http_proxy 42 | HTTPS_PROXY = var.https_proxy 43 | } 44 | } 45 | ] 46 | launchpad_tmpl = { 47 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 48 | kind = "mke" 49 | metadata = { 50 | name = "mkecluster" 51 | } 52 | spec = { 53 | mke = { 54 | version = var.docker_enterprise_version 55 | imageRepo = var.docker_image_repo 56 | adminUsername = "admin" 57 | adminPassword = var.admin_password 58 | installFlags: [ 59 | "--default-node-orchestrator=kubernetes", 60 | "--san=${module.masters.lb_ip}", 61 | ] 62 | cloud = { 63 | provider = "openstack" 64 | configFile = var.provider_config_file_path 65 | } 66 | } 67 | mcr = { 68 | version = var.docker_engine_version 69 | channel = "stable" 70 | repoURL = "https://repos.mirantis.com" 71 | } 72 | hosts = concat(local.managers, local.workers) 73 | } 74 | } 75 | } 76 | 77 | output "mke_cluster" { 78 | value = yamlencode(local.launchpad_tmpl) 79 | } 80 | -------------------------------------------------------------------------------- /examples/terraform/openstack/provider_config/cloud_provider.conf.example: -------------------------------------------------------------------------------- 1 | [Global] 2 | username= 3 | password= 4 | auth-url=http:///v3 5 | tenant-id= 6 | project-id= 7 | user-domain-name=default 8 | region-name=RegionOne 9 | -------------------------------------------------------------------------------- /examples/terraform/openstack/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # K8s and MKE admin password 2 | admin_password = "wale!wale" 3 | 4 | # SSH Private Key 5 | ssh_key_path = "./ssh_keys/test" 6 | 7 | # Openstack provider file to access the API 8 | provider_config_file_path="./provider_config/cloud_provider.conf" 9 | 10 | # Docker network settings 11 | docker_int_net = "10.0.200.1/24" 12 | docker_default_address_pool = "10.0.201.1/24" 13 | 14 | 15 | docker_enterprise_version = "3.4.0-latest" 16 | docker_image_repo = "docker.io/dockereng" 17 | 18 | 19 | cluster_name = "test" 20 | 21 | # Node counts 22 | master_count = 3 23 | worker_count = 3 24 | 25 | # ID of the available and shared public network 26 | external_network_id = "bf6b85a1-39db-4582-b0d1-f4291dddb9cf" 27 | external_network_name = "public" 28 | 29 | 30 | # Machine images 31 | master_image_name = "bionic-server-cloudimg-amd64-20190612" 32 | worker_image_name = "bionic-server-cloudimg-amd64-20190612" 33 | 34 | # Machine types/flavors 35 | master_flavor = "dev.cfg" 36 | worker_flavor = "dev.cfg" 37 | 38 | -------------------------------------------------------------------------------- /examples/terraform/openstack/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cluster_name" { 2 | default = "mke" 3 | } 4 | 5 | variable "docker_enterprise_version" { 6 | type = string 7 | default = "3.3.1" 8 | } 9 | 10 | variable "docker_engine_version" { 11 | type = string 12 | default = "19.03.8" 13 | } 14 | 15 | variable "docker_image_repo" { 16 | type = string 17 | default = "docker.io/docker" 18 | } 19 | 20 | variable "provider_config_file_path" { 21 | type = string 22 | } 23 | 24 | variable "http_proxy" { 25 | type = string 26 | default = "" 27 | } 28 | 29 | variable "https_proxy" { 30 | type = string 31 | default = "" 32 | } 33 | 34 | variable dns_ip_list { 35 | type = list(string) 36 | default = [] 37 | } 38 | 39 | variable "docker_default_address_pool" { 40 | } 41 | 42 | variable "ssh_key_path" { 43 | default = "./ssh_keys/my_rsa" 44 | } 45 | 46 | variable "external_network_name" {} 47 | variable "external_network_id" {} 48 | 49 | variable "region" { 50 | default = "RegionOne" 51 | } 52 | 53 | variable "admin_password" { 54 | default = "mke-ftw!!!" 55 | } 56 | 57 | variable "docker_int_net" { 58 | type = string 59 | description = "This is the internal docker network CIDR" 60 | } 61 | 62 | variable "master_count" { 63 | default = 1 64 | } 65 | 66 | variable "worker_count" { 67 | default = 3 68 | } 69 | 70 | variable "master_image_name" { 71 | default = "ubuntu18.04" 72 | } 73 | 74 | variable "worker_image_name" { 75 | default = "ubuntu18.04" 76 | } 77 | 78 | variable "master_flavor" { 79 | default = "m5.large" 80 | } 81 | 82 | variable "worker_flavor" { 83 | default = "m5.large" 84 | } 85 | 86 | variable "master_volume_size" { 87 | default = 100 88 | } 89 | 90 | variable "worker_volume_size" { 91 | default = 100 92 | } 93 | -------------------------------------------------------------------------------- /examples/terraform/vmware/README.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping MKE cluster on VMware vSphere 2 | 3 | This directory provides an example flow with Mirantis Launchpad tool together with Terraform using [VMware vSphere](https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs) as the cloud provider. 4 | 5 | 6 | ## Prerequisites 7 | 8 | * You need VMware vSphere credentials for API operations 9 | * Terraform [installed](https://learn.hashicorp.com/terraform/getting-started/install) 10 | 11 | ## Steps 12 | 13 | 1. Create a terraform.tfvars file with the necessary details. You can use the provided terraform.tfvars.example as a baseline. 14 | 2. `terraform init` 15 | 3. `terraform apply` 16 | 4. `terraform output mke_cluster | launchpad apply --config -` 17 | -------------------------------------------------------------------------------- /examples/terraform/vmware/main.tf: -------------------------------------------------------------------------------- 1 | provider "vsphere" { 2 | version = "~> 1.21" 3 | vsphere_server = var.vsphere_server 4 | user = var.vsphere_user 5 | password = var.vsphere_password 6 | 7 | # Enable this if your vSphere server has a self-signed certificate 8 | # allow_unverified_ssl = true 9 | } 10 | 11 | data "vsphere_datacenter" "dc" { 12 | name = var.datacenter 13 | } 14 | 15 | data "vsphere_resource_pool" "resource_pool" { 16 | name = var.resource_pool 17 | datacenter_id = data.vsphere_datacenter.dc.id 18 | } 19 | 20 | data "vsphere_datastore_cluster" "datastore_cluster" { 21 | name = var.datastore_cluster 22 | datacenter_id = data.vsphere_datacenter.dc.id 23 | } 24 | 25 | data "vsphere_network" "network" { 26 | name = var.network 27 | datacenter_id = data.vsphere_datacenter.dc.id 28 | } 29 | 30 | data "vsphere_virtual_machine" "template_vm_linux" { 31 | name = var.template_vm_linux 32 | datacenter_id = data.vsphere_datacenter.dc.id 33 | } 34 | 35 | # data "vsphere_virtual_machine" "template_vm_windows" { 36 | # name = "" 37 | # datacenter_id = data.vsphere_datacenter.dc.id 38 | # } 39 | 40 | module "managers" { 41 | source = "./modules/virtual_machine" 42 | quantity = var.quantity_managers 43 | name_prefix = "manager" 44 | resource_pool_id = data.vsphere_resource_pool.resource_pool.id 45 | datastore_cluster_id = data.vsphere_datastore_cluster.datastore_cluster.id 46 | folder = var.folder 47 | network_id = data.vsphere_network.network.id 48 | template_vm = data.vsphere_virtual_machine.template_vm_linux 49 | disk_size = 16 50 | } 51 | 52 | module "workers" { 53 | source = "./modules/virtual_machine" 54 | quantity = var.quantity_workers 55 | name_prefix = "worker" 56 | resource_pool_id = data.vsphere_resource_pool.resource_pool.id 57 | datastore_cluster_id = data.vsphere_datastore_cluster.datastore_cluster.id 58 | folder = var.folder 59 | network_id = data.vsphere_network.network.id 60 | template_vm = data.vsphere_virtual_machine.template_vm_linux 61 | disk_size = 16 62 | } 63 | 64 | # module "workers_windows" { 65 | # source = "./modules/virtual_machine" 66 | # quantity = var.quantity_workers_windows 67 | # name_prefix = "worker" 68 | # resource_pool_id = data.vsphere_resource_pool.resource_pool.id 69 | # datastore_cluster_id = data.vsphere_datastore_cluster.datastore_cluster.id 70 | # folder = "Launchpad team" 71 | # network_id = data.vsphere_network.network.id 72 | # template_vm = data.vsphere_virtual_machine.template_windows 73 | # disk_size = 16 74 | # } 75 | -------------------------------------------------------------------------------- /examples/terraform/vmware/modules/virtual_machine/main.tf: -------------------------------------------------------------------------------- 1 | resource "vsphere_virtual_machine" "vm" { 2 | count = var.quantity 3 | 4 | name = "${var.name_prefix}${count.index}" 5 | resource_pool_id = var.resource_pool_id 6 | datastore_cluster_id = var.datastore_cluster_id 7 | 8 | folder = var.folder 9 | 10 | guest_id = var.template_vm.guest_id 11 | 12 | network_interface { 13 | network_id = var.network_id 14 | } 15 | 16 | disk { 17 | label = "${var.name_prefix}${count.index}" 18 | size = var.disk_size 19 | thin_provisioned = var.template_vm.disks.0.thin_provisioned 20 | } 21 | 22 | clone { 23 | template_uuid = var.template_vm.id 24 | 25 | customize { 26 | network_interface{} 27 | 28 | # Hmm, the linux and windows options might make it tricky to have 29 | # a single module for handling virtual machines. 30 | linux_options { 31 | host_name = "${var.name_prefix}${count.index}" 32 | domain = "test.internal" 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/terraform/vmware/modules/virtual_machine/outputs.tf: -------------------------------------------------------------------------------- 1 | output "machines" { 2 | value = vsphere_virtual_machine.vm 3 | } 4 | -------------------------------------------------------------------------------- /examples/terraform/vmware/modules/virtual_machine/variables.tf: -------------------------------------------------------------------------------- 1 | variable "quantity" { 2 | description = "Number of VMs to create" 3 | } 4 | 5 | variable "name_prefix" { 6 | description = "The name of the VMs will be this plus a counter value" 7 | } 8 | 9 | variable "resource_pool_id" { 10 | description = "ID of the resource pool to create the VMs in" 11 | } 12 | 13 | variable "datastore_cluster_id" { 14 | description = "ID of the datastore cluster to create the VMs in" 15 | } 16 | 17 | variable "folder" { 18 | description = "Subfolder in the datacenter at which to create the VMs" 19 | } 20 | 21 | variable "network_id" { 22 | description = "ID of the network to attach the VMs to" 23 | } 24 | 25 | variable "template_vm" { 26 | description = "The template VM which will be cloned as the base for the new VMs" 27 | } 28 | 29 | variable "disk_size" { 30 | description = "Size of the disk drive for the VMs" 31 | } 32 | -------------------------------------------------------------------------------- /examples/terraform/vmware/outputs.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | managers = [ 3 | for host in module.managers.machines : { 4 | role = "manager" 5 | privateInterface = "ens5" # Is this supposed to be a constant? 6 | ssh = { 7 | address = host.default_ip_address 8 | user = "ubuntu" # "TODO: Probably make this a variable" 9 | keyPath = var.ssh_private_key_file 10 | } 11 | } 12 | ] 13 | workers = [ 14 | for host in module.workers.machines : { 15 | role = "worker" 16 | privateInterface = "ens5" # Is this supposed to be a constant? 17 | ssh = { 18 | address = host.default_ip_address 19 | user = "ubuntu" # "TODO: Probably make this a variable" 20 | keyPath = var.ssh_private_key_file 21 | } 22 | } 23 | ] 24 | # workers_windows = [ 25 | # for host in module.workers_windows.machines : { 26 | # role = "worker" 27 | # privateInterface = "Ethernet 2" # Is this supposed to be a constant? 28 | # ssh = { 29 | # address = host.public_ip 30 | # user = "administrator" # "TODO: Probably make this a variable" 31 | # keyPath = var.ssh_private_key_file 32 | # } 33 | # } 34 | # ] 35 | launchpad_tmpl = { 36 | apiVersion = "launchpad.mirantis.com/mke/v1.3" 37 | kind = "mke" 38 | spec = { 39 | mke = { 40 | adminUsername = var.mke_admin_username 41 | adminPassword = var.mke_admin_password 42 | installFlags : [ 43 | "--default-node-orchestrator=kubernetes", 44 | "--san=${var.mke_lb_dns_name}", 45 | ] 46 | } 47 | hosts = concat(local.managers, local.workers) #, local.windows_workers) 48 | } 49 | } 50 | } 51 | 52 | output "mke_cluster" { 53 | value = yamlencode(local.launchpad_tmpl) 54 | } 55 | -------------------------------------------------------------------------------- /examples/terraform/vmware/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # You may wish to populate some of the below variables from sources other than 2 | # this file (e.g. passwords). Other options include sourcing from the command 3 | # line or from environment variables. See the following page for more info: 4 | # https://www.terraform.io/docs/configuration/variables.html#assigning-values-to-root-module-variables 5 | 6 | vsphere_server = "vcenter.example.com" 7 | vsphere_user = "dennis.nedry" 8 | vsphere_password = "theM@gicW0rd" 9 | 10 | datacenter = "" 11 | resource_pool = "security-resources" 12 | folder = "main-ops" 13 | datastore_cluster = "main-datastore" 14 | network = "primary_network" 15 | template_vm_linux = "ubnutu-18.04" 16 | 17 | ssh_private_key_file = "./ssh_keys/id_rsa" 18 | mke_admin_username = "admin" 19 | mke_admin_password = "ButterF1nger$" 20 | mke_lb_dns_name = "mke.example.com" 21 | 22 | quantity_managers = 3 23 | quantity_workers = 10 24 | 25 | -------------------------------------------------------------------------------- /examples/terraform/vmware/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vsphere_server" { 2 | description = "URL of vSphere server" 3 | } 4 | 5 | variable "vsphere_user" { 6 | description = "Username for connecting to vSphere" 7 | } 8 | 9 | variable "vsphere_password" { 10 | description = "Password for vSphere connection" 11 | } 12 | 13 | variable "datacenter" { 14 | default = "" 15 | } 16 | 17 | variable "resource_pool" { 18 | } 19 | 20 | variable "folder" { 21 | default = "" 22 | } 23 | 24 | variable "datastore_cluster" { 25 | } 26 | 27 | variable "network" { 28 | } 29 | 30 | variable "template_vm_linux" { 31 | description = "VM to use as a template for the linux nodes (managers, workers)" 32 | } 33 | 34 | # variable "template_vm_windows" { 35 | # description = "VM to use as a template for the Windows nodes (Windows workers)" 36 | # } 37 | 38 | variable "ssh_private_key_file" { 39 | description = "Private key for SSH connections to created virtual machines; currently all machines must use the same key" 40 | } 41 | 42 | variable "mke_admin_username" { 43 | description = "Desired username for the MKE admin account" 44 | default = "admin" 45 | } 46 | 47 | variable "mke_admin_password" { 48 | description = "Desired password for the MKE admin account" 49 | } 50 | 51 | variable "mke_lb_dns_name" { 52 | description = "DNS name of the MKE load balancer" 53 | } 54 | 55 | variable "quantity_managers" { 56 | description = "Number of MKE manager VMs to create" 57 | default = 3 58 | } 59 | 60 | variable "quantity_workers" { 61 | description = "Number of MKE worker VMs to create" 62 | default = 3 63 | } 64 | 65 | # variable "quantity_workers_windows" { 66 | # description = "Number of MKE worker VMs to create (Windows)" 67 | # default = 0 68 | # } 69 | -------------------------------------------------------------------------------- /images/launchpad-basic-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirantis/launchpad_legacy/b13a0cb855e7161ec7938be1cf5c71fb26880409/images/launchpad-basic-setup.png -------------------------------------------------------------------------------- /images/launchpad-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirantis/launchpad_legacy/b13a0cb855e7161ec7938be1cf5c71fb26880409/images/launchpad-screenshot.png --------------------------------------------------------------------------------