{"id":790,"date":"2019-02-26T13:15:31","date_gmt":"2019-02-26T10:15:31","guid":{"rendered":"https:\/\/artem.services\/?p=778"},"modified":"2019-02-26T14:15:45","modified_gmt":"2019-02-26T11:15:45","slug":"778-2","status":"publish","type":"post","link":"https:\/\/artem.services\/?p=790&lang=en","title":{"rendered":"Terraform &#8212; Kubernetes cluster on AWS EC2"},"content":{"rendered":"<p><img loading=\"lazy\" class=\"size-full wp-image-99 aligncenter\" src=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform.png\" alt=\"\" width=\"1210\" height=\"418\" srcset=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform.png 1210w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-300x104.png 300w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-768x265.png 768w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-1024x354.png 1024w, https:\/\/artem.services\/wp-content\/uploads\/2018\/10\/terraform-954x330.png 954w\" sizes=\"(max-width: 1210px) 100vw, 1210px\" \/><\/p>\n<p><span class=\"tlid-translation translation\"><span title=\"\"><strong>Terraform<\/strong> configuration example that creates <strong>Kubernetes<\/strong> cluster (Bare Metal) on <strong>AWS EC2<\/strong>.<\/span> <span title=\"\">Creates <strong>Ingress<\/strong> with <strong>NodePort<\/strong>.<\/span> <span class=\"\" title=\"\"><strong>IP<\/strong> addresses Ingress nodes.<\/span><\/p>\n<p><span class=\"\" title=\"\">This template creates the following <strong>EC2<\/strong> instances:<\/span><\/span><\/p>\n<ul>\n<li>1 manager<\/li>\n<li>2 workers<\/li>\n<li>2 ingresses<\/li>\n<\/ul>\n<h4>variables.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nvariable &quot;REGION&quot; {\r\n  default = &quot;us-east-1&quot;\r\n}\r\n\r\nvariable &quot;PROJECT_NAME&quot; {\r\n  default = &quot;artem_k8s&quot;\r\n}\r\n\r\nvariable &quot;SSH_USER&quot; { \r\n  default = &quot;ubuntu&quot;\r\n}\r\n\r\nvariable &quot;SSH_KEY_NAME&quot; { \r\n  default = &quot;artem.gatchenko&quot;\r\n}\r\n\r\nvariable &quot;SSH_KEY_PATH&quot; { \r\n  default = &quot;\/home\/artem\/.ssh\/id_rsa&quot;\r\n}\r\n\r\nvariable &quot;VPC_SUBNET&quot; { \r\n  default = &quot;192.168.1.0\/24&quot;\r\n}\r\n\r\nvariable &quot;INSTANCE_TYPE&quot; {\r\n  default = &quot;t2.micro&quot;\r\n}\r\n\r\nvariable &quot;WORKER_NUMBER&quot; {\r\n  default = &quot;2&quot;\r\n}\r\n<\/pre>\n<h4><\/h4>\n<p><!--more--><\/p>\n<h4>main.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nprovider &quot;aws&quot; {\r\n  region = &quot;us-east-1&quot;\r\n}\r\n\r\nprovider &quot;aws&quot; {\r\n  alias = &quot;vpc&quot;\r\n  region = &quot;${var.REGION}&quot;\r\n}\r\n<\/pre>\n<h4>vpc.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\/\/ CREATE VPC\r\nresource &quot;aws_vpc&quot; &quot;vpc&quot; {\r\n  cidr_block = &quot;${var.VPC_SUBNET}&quot;\r\n  enable_dns_hostnames = &quot;true&quot;\r\n  enable_dns_support = &quot;true&quot;\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}&quot;\r\n  }\r\n}\r\n\r\n\/\/ CREATE GATEWAY\r\nresource &quot;aws_internet_gateway&quot; &quot;vpc&quot; {\r\n  vpc_id = &quot;${aws_vpc.vpc.id}&quot;\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}&quot;\r\n  }\r\n}\r\n\r\n\/\/ CREATE ROUTE TABLE\r\nresource &quot;aws_route_table&quot; &quot;vpc&quot; {\r\n  vpc_id = &quot;${aws_vpc.vpc.id}&quot;\r\n  route {\r\n    cidr_block = &quot;0.0.0.0\/0&quot;\r\n    gateway_id = &quot;${aws_internet_gateway.vpc.id}&quot;\r\n  }\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}&quot;\r\n  }\r\n}\r\n\r\n\/\/ CREATE SUBNET\r\nresource &quot;aws_subnet&quot; &quot;vpc&quot; {\r\n  vpc_id     = &quot;${aws_vpc.vpc.id}&quot;\r\n  cidr_block = &quot;${var.VPC_SUBNET}&quot;\r\n\r\n  map_public_ip_on_launch = &quot;true&quot;\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}&quot;\r\n  }\r\n}\r\n\r\nresource &quot;aws_route_table_association&quot; &quot;vpc&quot; {\r\n  subnet_id      = &quot;${aws_subnet.vpc.id}&quot;\r\n  route_table_id = &quot;${aws_route_table.vpc.id}&quot;\r\n}\r\n\r\n\/\/ CREATE SECURITY GROUP\r\nresource &quot;aws_security_group&quot; &quot;vpc&quot; {\r\n  vpc_id      = &quot;${aws_vpc.vpc.id}&quot;\r\n\r\n  ingress {\r\n    from_port   = 22\r\n    to_port     = 22\r\n    protocol    = &quot;tcp&quot;\r\n    cidr_blocks = [&quot;0.0.0.0\/0&quot;]\r\n    description = &quot;Allow input SSH&quot;\r\n  }\r\n\r\n  ingress {\r\n    from_port   = 80\r\n    to_port     = 80\r\n    protocol    = &quot;tcp&quot;\r\n    cidr_blocks = [&quot;0.0.0.0\/0&quot;]\r\n    description = &quot;Allow input HTTP&quot;\r\n  }\r\n\r\n  ingress {\r\n    from_port   = 443\r\n    to_port     = 443\r\n    protocol    = &quot;tcp&quot;\r\n    cidr_blocks = [&quot;0.0.0.0\/0&quot;]\r\n    description = &quot;Allow input HTTPS&quot;\r\n  }\r\n\r\n  ingress {\r\n    from_port   = 0\r\n    to_port     = 0\r\n    protocol    = &quot;-1&quot;\r\n    self = true\r\n    description = &quot;Allow triffic between instances in SG&quot;\r\n  }\r\n\r\n  egress {\r\n    from_port   = 0\r\n    to_port     = 0\r\n    protocol    = &quot;-1&quot;\r\n    cidr_blocks = [&quot;0.0.0.0\/0&quot;]\r\n    description = &quot;Allow all ouput traffic&quot;\r\n  }\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}&quot;\r\n    Description = &quot;${var.PROJECT_NAME}&quot;\r\n  }\r\n\r\n}\r\n<\/pre>\n<h4>instance_master.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\/\/ CREATE INSTANCE FOR KUBERNETES MASTER\r\nresource &quot;aws_instance&quot; &quot;master&quot; {\r\n  ami = &quot;ami-0ac019f4fcb7cb7e6&quot;\r\n  instance_type = &quot;${var.INSTANCE_TYPE}&quot;\r\n  key_name      = &quot;${var.SSH_KEY_NAME}&quot;\r\n  vpc_security_group_ids = [&quot;${aws_security_group.vpc.id}&quot;]\r\n  subnet_id = &quot;${aws_subnet.vpc.id}&quot;\r\n  associate_public_ip_address = true\r\n  source_dest_check = false\r\n\r\n  connection {\r\n    type     = &quot;ssh&quot;\r\n    user     = &quot;${var.SSH_USER}&quot;\r\n    private_key = &quot;${file(&quot;${var.SSH_KEY_PATH}&quot;)}&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;k8s.sh&quot;\r\n    destination = &quot;\/tmp\/k8s.sh&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;ingress-nginx.yml&quot;\r\n    destination = &quot;\/tmp\/ingress-nginx.yml&quot;\r\n  }\r\n\r\n  provisioner &quot;remote-exec&quot; {\r\n    inline = [\r\n      &quot;sudo chmod +x \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo kubeadm init --apiserver-advertise-address=${aws_instance.master.private_ip} --pod-network-cidr=10.244.0.0\/16 --ignore-preflight-errors SystemVerification --ignore-preflight-errors NumCPU&quot;,\r\n      &quot;mkdir -p $HOME\/.kube&quot;,\r\n      &quot;sudo cp -i \/etc\/kubernetes\/admin.conf $HOME\/.kube\/config&quot;,\r\n      &quot;sudo chown $(id -u):$(id -g) $HOME\/.kube\/config&quot;,\r\n      &quot;echo 'source &amp;lt;(kubectl completion bash)' &amp;gt;&amp;gt; $HOME\/.bashrc&quot;,\r\n      &quot;kubectl apply -f https:\/\/raw.githubusercontent.com\/coreos\/flannel\/master\/Documentation\/kube-flannel.yml&quot;\r\n    ]\r\n  }\r\n\r\n  provisioner &quot;local-exec&quot; {\r\n    command = &quot;ssh -o StrictHostKeyChecking=no ubuntu@${aws_instance.master.public_ip} -i ${var.SSH_KEY_PATH} 'kubeadm token create --print-join-command' &amp;gt; token&quot;\r\n  }\r\n\r\n  provisioner &quot;local-exec&quot; {\r\n    command = &quot;if [ -e ip_ingresses ]; then rm -rf ip_ingresses; fi&quot;\r\n  }\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}_master&quot;\r\n  }\r\n\r\n}\r\n<\/pre>\n<h4>instance_workers.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\/\/ CREATE INSTANCE FOR KUBERNETES WORKERS\r\nresource &quot;aws_instance&quot; &quot;worker&quot; {\r\n  depends_on = [&quot;aws_instance.master&quot;]\r\n  count = &quot;${var.WORKER_NUMBER}&quot;\r\n  ami = &quot;ami-0ac019f4fcb7cb7e6&quot;\r\n  instance_type = &quot;${var.INSTANCE_TYPE}&quot;\r\n  key_name      = &quot;${var.SSH_KEY_NAME}&quot;\r\n  vpc_security_group_ids = [&quot;${aws_security_group.vpc.id}&quot;]\r\n  subnet_id = &quot;${aws_subnet.vpc.id}&quot;\r\n  associate_public_ip_address = true\r\n  source_dest_check = false\r\n\r\n  connection {\r\n    type     = &quot;ssh&quot;\r\n    user     = &quot;${var.SSH_USER}&quot;\r\n    private_key = &quot;${file(&quot;${var.SSH_KEY_PATH}&quot;)}&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;k8s.sh&quot;\r\n    destination = &quot;\/tmp\/k8s.sh&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;token&quot;\r\n    destination = &quot;\/tmp\/token&quot;\r\n  }\r\n\r\n  provisioner &quot;remote-exec&quot; {\r\n    inline = [\r\n      &quot;sleep 15&quot;,\r\n      &quot;sudo chmod +x \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo `cat \/tmp\/token` --ignore-preflight-errors=SystemVerification&quot;\r\n    ]\r\n  }\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}_worker&quot;\r\n  }\r\n}\r\n<\/pre>\n<h4>instance_ingresses.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\/\/ CREATE INSTANCE FOR KUBERNETES WORKERS\r\nresource &quot;aws_instance&quot; &quot;ingress&quot; {\r\n  depends_on = [&quot;aws_instance.master&quot;]\r\n  # depends_on = [\r\n  #   &quot;aws_instance.master&quot;,\r\n  #   &quot;aws_instance.ingress&quot;,\r\n  # ]\r\n  count = &quot;2&quot;\r\n  ami = &quot;ami-0ac019f4fcb7cb7e6&quot;\r\n  instance_type = &quot;${var.INSTANCE_TYPE}&quot;\r\n  key_name      = &quot;${var.SSH_KEY_NAME}&quot;\r\n  vpc_security_group_ids = [&quot;${aws_security_group.vpc.id}&quot;]\r\n  subnet_id = &quot;${aws_subnet.vpc.id}&quot;\r\n  associate_public_ip_address = true\r\n  source_dest_check = false\r\n\r\n  connection {\r\n    type     = &quot;ssh&quot;\r\n    user     = &quot;${var.SSH_USER}&quot;\r\n    private_key = &quot;${file(&quot;${var.SSH_KEY_PATH}&quot;)}&quot;\r\n  }\r\n\r\n  provisioner &quot;local-exec&quot; {\r\n    command = &quot;echo ${self.private_ip} &amp;gt;&amp;gt; ip_ingresses&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;k8s.sh&quot;\r\n    destination = &quot;\/tmp\/k8s.sh&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;token&quot;\r\n    destination = &quot;\/tmp\/token&quot;\r\n  }\r\n\r\n  provisioner &quot;remote-exec&quot; {\r\n    inline = [\r\n      &quot;sleep 15&quot;,\r\n      &quot;sudo chmod +x \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo \/tmp\/k8s.sh&quot;,\r\n      &quot;sudo `cat \/tmp\/token` --ignore-preflight-errors=SystemVerification&quot;\r\n    ]\r\n  }\r\n\r\n  tags {\r\n    Name = &quot;${var.PROJECT_NAME}_ingress&quot;\r\n  }\r\n}\r\n<\/pre>\n<h4>ingress.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nresource &quot;null_resource&quot; &quot;ingress&quot; {\r\n\r\n  triggers = {\r\n    instance_id = &quot;${aws_instance.master.id}&quot;\r\n  }\r\n\r\n  connection {\r\n    type     = &quot;ssh&quot;\r\n    user     = &quot;${var.SSH_USER}&quot;\r\n    host = &quot;${aws_instance.master.public_ip}&quot;\r\n    agent = false\r\n    private_key = &quot;${file(&quot;${var.SSH_KEY_PATH}&quot;)}&quot;\r\n  }\r\n\r\n  provisioner &quot;file&quot; {\r\n    source      = &quot;ip_ingresses&quot;\r\n    destination = &quot;\/tmp\/ip_ingresses&quot;\r\n  }\r\n\r\n  provisioner &quot;remote-exec&quot; {\r\n    inline = [\r\n      &quot;sleep 15&quot;,\r\n      &quot;kubectl apply -f https:\/\/raw.githubusercontent.com\/kubernetes\/ingress-nginx\/master\/deploy\/mandatory.yaml&quot;,\r\n      &quot;IP_INGRESS_1=`sed -n 1p \/tmp\/ip_ingresses`&quot;,\r\n      &quot;IP_INGRESS_2=`sed -n 2p \/tmp\/ip_ingresses`&quot;,\r\n      &quot;sed -i \\&quot;s\/IP_INGRESS_1\/$IP_INGRESS_1\/g\\&quot; \/tmp\/ingress-nginx.yml&quot;,\r\n      &quot;sed -i \\&quot;s\/IP_INGRESS_2\/$IP_INGRESS_2\/g\\&quot; \/tmp\/ingress-nginx.yml&quot;,\r\n      &quot;INGRESS_NAME_1=ip-$(cat \/tmp\/ip_ingresses | sed -n 1p | sed 's\/\\\\.\/-\/g')&quot;,\r\n      &quot;INGRESS_NAME_2=ip-$(cat \/tmp\/ip_ingresses | sed -n 2p | sed 's\/\\\\.\/-\/g')&quot;,\r\n      &quot;kubectl label node $INGRESS_NAME_1 node-role.kubernetes.io\/ingress=ingress&quot;,\r\n      &quot;kubectl label node $INGRESS_NAME_2 node-role.kubernetes.io\/ingress=ingress&quot;,\r\n      &quot;kubectl apply -f \/tmp\/ingress-nginx.yml&quot;\r\n    ]\r\n  }\r\n  depends_on = [\r\n    &quot;aws_instance.master&quot;,\r\n    &quot;aws_instance.ingress&quot;,\r\n  ]\r\n}\r\n<\/pre>\n<h4>ingress-nginx.yml<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n  name: ingress-nginx\r\n  namespace: ingress-nginx\r\n  labels:\r\n    app.kubernetes.io\/name: ingress-nginx\r\n    app.kubernetes.io\/part-of: ingress-nginx\r\nspec:\r\n  type: NodePort\r\n  ports:\r\n    - name: http\r\n      port: 80\r\n      targetPort: 80\r\n      protocol: TCP\r\n    - name: https\r\n      port: 443\r\n      targetPort: 443\r\n      protocol: TCP\r\n  externalIPs:\r\n    - IP_INGRESS_1\r\n    - IP_INGRESS_2\r\n  selector:\r\n    app.kubernetes.io\/name: ingress-nginx\r\n    app.kubernetes.io\/part-of: ingress-nginx\r\n<\/pre>\n<h4>k8s.sh<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n#!\/bin\/bash\r\n\r\n# THIS SCRIPT FOR INSTALL K8S. SCRIPT FOR UBUNTU\r\n\r\n# INSTALL DOCKER\r\napt update\r\napt install -y apt-transport-https ca-certificates curl software-properties-common\r\n\r\ncurl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg | apt-key add -\r\n\r\nadd-apt-repository &quot;deb [arch=amd64] https:\/\/download.docker.com\/linux\/ubuntu $(lsb_release -cs) stable&quot;\r\n\r\napt update\r\napt install -y docker-ce\r\n\r\nsystemctl enable docker\r\nsystemctl start docker\r\n\r\n# INSTALL KUBERNETES\r\n\r\ncurl -s https:\/\/packages.cloud.google.com\/apt\/doc\/apt-key.gpg | apt-key add -\r\n \r\ncat &amp;lt;&amp;lt;EOF &amp;gt;\/etc\/apt\/sources.list.d\/kubernetes.list\r\ndeb http:\/\/apt.kubernetes.io\/ kubernetes-xenial main\r\nEOF\r\n \r\napt update\r\napt install -y kubelet kubeadm kubectl\r\n\r\nsystemctl enable kubelet\r\nsystemctl start kubelet\r\n<\/pre>\n<h4>output.tf<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\noutput &quot;aws-k8s-ingresses-ip&quot; {\r\n  value = &quot;${aws_instance.ingress.*.public_ip}&quot;\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span class=\"tlid-translation translation\"><span class=\"\" title=\"\">Download all one archive can be <a href=\"https:\/\/artem.services\/wp-content\/uploads\/2019\/02\/Terraform-K8s.zip\">here<\/a>.<\/span><\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span class=\"tlid-translation translation\"><span class=\"\" title=\"\">How to run the <strong>Terraform<\/strong> template:<\/span><\/span><\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nterraform init\r\nterraform plan\r\nterraform apply\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Terraform configuration example that creates Kubernetes cluster (Bare Metal) on AWS EC2. Creates Ingress with NodePort. IP addresses Ingress nodes. This template creates the following EC2 instances: 1 manager 2 workers 2 ingresses variables.tf<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[541],"tags":[543,545,547,549,551,553,555],"_links":{"self":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/790"}],"collection":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=790"}],"version-history":[{"count":2,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/790\/revisions"}],"predecessor-version":[{"id":792,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/790\/revisions\/792"}],"wp:attachment":[{"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=790"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=790"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=790"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}