This topic describes how to deploy an Alibaba Cloud Container Service for Kubernetes cluster in a VPC and deploy WordPress within the cluster by using Terraform.

Prerequisites

Before you begin, ensure that you have completed the following operations:

Step 1: Download the Terraform template for Container Service for Kubernetes

You can download the Terraform template for creating Kubernetes clusters from GitHub. The template contains the following files:

  • main.tf

    It is the main file of Terraform and defines the resources to be deployed. This template uses conditions to reference existing network resources and create multiple Kubernetes clusters at the same time. This file defines the following resources:

    • Region
      Defines the region where resources will be created.
      provider "alicloud" {
        access_key = "${var.alicloud_access_key}"
        secret_key = "${var.alicloud_secret_key}"
        region = "${var.region}"
      }
      
      data "alicloud_zones" "default" {
        available_instance_type = data.alicloud_instance_types.default.instance_types[0].id
      }
    • Instance type
      data "alicloud_instance_types" "default" {
        cpu_core_count = var.cpu_core_count
        memory_size    = var.memory_size
      }
    • VPC
      You can use an existing VPC by specifying vpc_id.
      resource "alicloud_vpc" "vpc" {
        count      = var.vpc_id == "" ? 1 : 0
        cidr_block = var.vpc_cidr
        name       = var.vpc_name == "" ? var.example_name : var.vpc_name
      }                                 
    • VSwitch
      You can use existing VSwitches by specifying vswitch_ids.
      resource "alicloud_vswitch" "vswitches" {
        count             = length(var.vswitch_ids) > 0 ? 0 : length(var.vswitch_cidrs)
        vpc_id            = var.vpc_id == "" ? join("", alicloud_vpc.vpc. *.id) : var.vpc_id
        cidr_block        = element(var.vswitch_cidrs, count.index)
        availability_zone = data.alicloud_zones.default.zones[count.index % length(data.alicloud_zones.default.zones)]["id"]
        name = var.vswitch_name_prefix == "" ? format(
          "%s-%s",
          var.example_name,
          format(var.number_format, count.index + 1),
          ) : format(
          "%s-%s",
          var.vswitch_name_prefix,
          format(var.number_format, count.index + 1),
        )
      }                                
    • NAT gateway
      You can determine whether to create a NAT gateway for the VPC defined in the template by specifying new_nat_gateway.
      resource "alicloud_nat_gateway" "default" {
        count  = var.new_nat_gateway == "true" ? 1 : 0
        vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc. *.id) : var.vpc_id
        name   = var.example_name
      }                                
    • ENI
      resource "alicloud_eip" "default" {
        count     = var.new_nat_gateway == "true" ? 1 : 0
        bandwidth = 10
      }
    • ENI attaching
      resource "alicloud_eip_association" "default" {
        count         = var.new_nat_gateway == "true" ? 1 : 0
        allocation_id = alicloud_eip.default[0].id
        instance_id   = alicloud_nat_gateway.default[0].id
      }
    • SNAT entry
      You can add a SNAT entry for the NAT gateway defined in the template.
      resource "alicloud_snat_entry" "default" {
        count         = var.new_nat_gateway == "false" ? 0 : length(var.vswitch_ids) > 0 ? length(var.vswitch_ids) : length(var.vswitch_cidrs)
        snat_table_id = alicloud_nat_gateway.default[0].snat_table_ids
        source_vswitch_id = length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids))[count.index % length(split(",", join(",", var.vswitch_ids)))] : length(var.vswitch_cidrs) < 1 ? "" : split(",", join(",", alicloud_vswitch.vswitches. *.id))[count.index % length(split(",", join(",", alicloud_vswitch.vswitches. *.id)))]
        snat_ip = alicloud_eip.default[0].ip_address
      }
    • Kubernetes cluster
      You can create multiple Kubernetes clusters at the same time by specifying k8s_number.
      resource "alicloud_cs_kubernetes" "k8s" {
        count = var.k8s_number
        name = var.k8s_name_prefix == "" ? format(
          "%s-%s",
          var.example_name,
          format(var.number_format, count.index + 1),
          ) : format(
          "%s-%s",
          var.k8s_name_prefix,
          format(var.number_format, count.index + 1),
        )
        vswitch_ids = [length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids))[count.index % length(split(",", join(",", var.vswitch_ids)))] : length(var.vswitch_cidrs) < 1 ? "" : split(",", join(",", alicloud_vswitch.vswitches. *.id))[count.index % length(split(",", join(",", alicloud_vswitch.vswitches. *.id)))]]
        new_nat_gateway       = false
        master_instance_types = [var.master_instance_type == "" ? data.alicloud_instance_types.default.instance_types[0].id : var.master_instance_type]
        worker_instance_types = [var.worker_instance_type == "" ? data.alicloud_instance_types.default.instance_types[0].id : var.worker_instance_type]
        worker_numbers        = [var.k8s_worker_number]
        master_disk_category  = var.master_disk_category
        worker_disk_category  = var.worker_disk_category
        master_disk_size      = var.master_disk_size
        worker_disk_size      = var.master_disk_size
        password              = var.ecs_password
        pod_cidr              = var.k8s_pod_cidr
        service_cidr          = var.k8s_service_cidr
        enable_ssh            = true
        install_cloud_monitor = true
        depends_on = [alicloud_snat_entry.default]
      }                                
      Note You can download the kube config content and save it in the ~/.kube/config file by specifying kube_config = "~/.kube/config".
  • outputs.tf
    This file defines output parameters. These output parameters are generated from the resources created as part of the execution, similar to the output parameters specified in the ROS template. For example, a template for deploying a Kubernetes cluster must contain the following output parameters such as the cluster ID and other resource parameters:
    // Output VPC
    output "vpc_id" {
      description = "The ID of the VPC."
      value       = alicloud_cs_kubernetes.k8s[0].vpc_id
    }
    
    output "vswitch_ids" {
      description = "List ID of the VSwitches."
      value       = [alicloud_cs_kubernetes.k8s. *.vswitch_ids]
    }
    
    output "nat_gateway_id" {
      value = alicloud_cs_kubernetes.k8s[0].nat_gateway_id
    }
    
    // Output kubernetes resource
    output "cluster_id" {
      description = "ID of the kunernetes cluster."
      value       = [alicloud_cs_kubernetes.k8s. *.id]
    }
    
    output "security_group_id" {
      description = "ID of the Security Group used to deploy kubernetes cluster."
      value       = alicloud_cs_kubernetes.k8s[0].security_group_id
    }
    
    output "worker_nodes" {
      description = "List worker nodes of cluster."
      value       = [alicloud_cs_kubernetes.k8s. *.worker_nodes]
    }
    
    output "master_nodes" {
      description = "List master nodes of cluster."
      value       = [alicloud_cs_kubernetes.k8s. *.master_nodes]
    }
  • variables.tf
    This file contains variables that can be transferred to main.tf. These variables can help you customize the environment.
    # common variables
    variable "availability_zone" {
      description = "The available zone to launch ecs instance and other resources."
      default     = ""
    }
    
    variable "number_format" {
      description = "The number format used to output."
      default     = "%02d"
    }
    
    variable "example_name" {
      default = "tf-example-kubernetes"
    }
    
    # Instance typs variables
    variable "cpu_core_count" {
      description = "CPU core count is used to fetch instance types."
      default     = 2
    }
    
    variable "memory_size" {
      description = "Memory size used to fetch instance types."
      default     = 4
    }
    
    # VPC variables
    variable "vpc_name" {
      description = "The vpc name used to create a new vpc when 'vpc_id' is not specified. Default to variable `example_name`"
      default     = ""
    }
    
    variable "vpc_id" {
      description = "A existing vpc id used to create several vswitches and other resources."
      default     = ""
    }
    
    variable "vpc_cidr" {
      description = "The cidr block used to launch a new vpc when 'vpc_id' is not specified."
      default     = "10.1.0.0/21"
    }
    
    # VSwitch variables
    variable "vswitch_name_prefix" {
      description = "The vswitch name prefix used to create several new vswitches. Default to variable `example_name`"
      default     = ""
    }
    
    variable "vswitch_ids" {
      description = "List of existing vswitch id."
      type        = list(string)
      default     = []
    }
    
    variable "vswitch_cidrs" {
      description = "List of cidr blocks used to create several new vswitches when 'vswitch_ids' is not specified."
      type        = list(string)
      default     = ["10.1.2.0/24"]
    }
    
    variable "new_nat_gateway" {
      description = "Whether to create a new nat gateway. In this template, a new nat gateway will create a nat gateway, eip and server snat entries."
      default     = "true"
    }
    
    # Cluster nodes variables
    variable "master_instance_type" {
      description = "The ecs instance type used to launch master nodes. Default from instance typs datasource."
      default     = ""
    }
    
    variable "worker_instance_type" {
      description = "The ecs instance type used to launch worker nodes. Default from instance typs datasource."
      default     = ""
    }
    
    variable "master_disk_category" {
      description = "The system disk category used to launch one or more master nodes."
      default     = "cloud_efficiency"
    }
    
    variable "worker_disk_category" {
      description = "The system disk category used to launch one or more worker nodes."
      default     = "cloud_efficiency"
    }
    
    variable "master_disk_size" {
      description = "The system disk size used to launch one or more master nodes."
      default     = "40"
    }
    
    variable "worker_disk_size" {
      description = "The system disk size used to launch one or more worker nodes."
      default     = "40"
    }
    
    variable "ecs_password" {
      description = "The password of instance."
      default     = "Abc12345"
    }
    
    variable "k8s_number" {
      description = "The number of kubernetes cluster."
      default     = 1
    }
    
    variable "k8s_worker_number" {
      description = "The number of worker nodes in each kubernetes cluster."
      default     = 3
    }
    
    variable "k8s_name_prefix" {
      description = "The name prefix used to create several kubernetes clusters. Default to variable `example_name`"
      default     = ""
    }
    
    variable "k8s_pod_cidr" {
      description = "The kubernetes pod cidr block. It cannot be equals to vpc's or vswitch's and cannot be in them."
      default     = "172.20.0.0/16"
    }
    
    variable "k8s_service_cidr" {
      description = "The kubernetes service cidr block. It cannot be equals to vpc's or vswitch's or pod's and cannot be in them."
      default     = "172.21.0.0/20"
    }

Step 2: Run the Kubernetes Terraform script

  1. In the path where the preceding files are stored, run the terraform init command to initialize the workspace.
    $ terraform init
    
    Initializing the backend...
    
    Initializing provider plugins...
    - Checking for available provider plugins...
    - Downloading plugin for provider "alicloud" (hashicorp/alicloud) 1.62.0...
    
    The following providers do not have any version constraints in configuration,
    so the latest version was installed.
    
    To prevent automatic upgrades to new major versions that may contain breaking
    changes, it is recommended to add version = "..." constraints to the
    corresponding provider blocks in configuration, with the constraint strings
    suggested below.
    
    * provider.alicloud: version = "~> 1.62"
    
    Terraform has been successfully initialized!
    						
  2. Run the terraform apply command to create a Kubernetes cluster.
    $ terraform apply
    
    data.alicloud_instance_types.default: Refreshing state...
    data.alicloud_zones.default: Refreshing state...
    
    An execution plan has been generated and is shown below.
    Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    ...
    
    Plan: 7 to add, 0 to change, 0 to destroy.
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    alicloud_vpc.vpc: Creating...
    ...
    
    Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
    
    Outputs:    
    
    cluster_id = [
        c0f2e04c77e234******
    ]
    ......
    vswitch_ids = [
        vsw-bp1c3hfcd6l80izqc3tbx
    ]
    						

    After you run the terraform apply command, the cluster ID and other parameters are displayed and the kube config file is stored in the ~ /.kube directory.

    Now you can log on to the Container Service console to view the Kubernetes cluster created from Terraform and its related cluster, node, log, and container information.

Step 3: Download the Terraform template for WordPress

After creating a Kubernetes cluster and downloading the kube config file, you can deploy WordPress in Kubernetes. You can download the Terraform template for creating WordPress from GitHub. The template defines the resources and configurations for creating WordPress in a Kubernetes cluster. For more information about Terraform Kubernetes, see Kubernetes documentation.

This template contains the following files:
  • localvolumes.tf
    This file defines the persistent volume for storing MySQL persistent data.
    resource "kubernetes_persistent_volume" "mysql" {
      metadata {
        name = "local-pv-mysql"
        labels {
          type = "local"
        }
      }
      spec {
        capacity {
          storage = "20Gi"
        }
        access_modes = ["ReadWriteOnce"]
        persistent_volume_source {
          host_path {
            path = "/tmp/data/pv-mysql"
          }
        }
      }
    }
  • mysql.tf
    This file defines how to create a secret for storing the administrator password of MySQL and how to deploy MySQL.
    • Secret
      resource "kubernetes_secret" "mysql" {
        metadata {
          name = "mysql-pass"
        }
      
        data {
          password = "${var.mysql_password}"
        }
      }
    • Deployment
      resource "kubernetes_service" "mysql" {
        metadata {
          name = "wordpress-mysql"
          labels {
            app = "wordpress"
          }
        }
        spec {
          port {
            port = 3306
          }
          selector {
            app = "wordpress"
            tier = "${kubernetes_replication_controller.mysql.spec.0.selector.tier}"
          }
          cluster_ip = "None"
        }
      }
      
      resource "kubernetes_replication_controller" "mysql" {
        metadata {
          name = "wordpress-mysql"
          labels {
            app = "wordpress"
          }
        }
        spec {
          selector {
            app = "wordpress"
            tier = "mysql"
          }
          template {
            container {
              image = "mysql:${var.mysql_version}"
              name  = "mysql"
      
              env {
                name = "MYSQL_ROOT_PASSWORD"
                value_from {
                  secret_key_ref {
                    name = "${kubernetes_secret.mysql.metadata.0.name}"
                    key = "password"
                  }
                }
              }
      
              port {
                container_port = 3306
                name = "mysql"
              }
      
              volume_mount {
                name = "mysql-persistent-storage"
                mount_path = "/var/lib/mysql"
              }
            }
      
            volume {
              name = "mysql-persistent-storage"
              persistent_volume_claim {
                claim_name = "${kubernetes_persistent_volume_claim.mysql.metadata.0.name}"
              }
            }
          }
        }
      }
      									
  • wordpress.tf
    This file defines how to deploy WordPress.
    resource "kubernetes_service" "wordpress" {
      metadata {
        name = "wordpress"
        labels {
          app = "wordpress"
        }
      }
      spec {
        port {
          port = 80
        }
        selector {
          app = "wordpress"
          tier = "${kubernetes_replication_controller.wordpress.spec.0.selector.tier}"
        }
        type = "LoadBalancer"
      }
    }
    
    resource "kubernetes_replication_controller" "wordpress" {
      metadata {
        name = "wordpress"
        labels {
          app = "wordpress"
        }
      }
      spec {
        selector {
          app = "wordpress"
          tier = "frontend"
        }
        template {
          container {
            image = "wordpress:${var.wordpress_version}-apache"
            name  = "wordpress"
    
            env {
              name = "WORDPRESS_DB_HOST"
              value = "wordpress-mysql"
            }
            env {
              name = "WORDPRESS_DB_PASSWORD"
              value_from {
                secret_key_ref {
                  name = "${kubernetes_secret.mysql.metadata.0.name}"
                  key = "password"
                }
              }
            }
    
            port {
              container_port = 80
              name = "wordpress"
            }
    
            volume_mount {
              name = "wordpress-persistent-storage"
              mount_path = "/var/www/html"
            }
          }
    
          volume {
            name = "wordpress-persistent-storage"
            persistent_volume_claim {
              claim_name = "${kubernetes_persistent_volume_claim.wordpress.metadata.0.name}"
            }
          }
        }
      }
    }
  • outputs.tf
    This file defines output parameters. The public IP address of the Server Load Balancer (SLB) instance can be used to access the deployed WordPress application.
    output "slb_ip" {
      value = "${kubernetes_service.wordpress.load_balancer_ingress.0.ip}"
    }
  • variables.tf
    This file contains the parameters for deploying MySQL and WordPress.
    variable "wordpress_version" {
      description = "The version of wordpress. Default to 4.7.3."
      default = "4.7.3"
    }
    variable "mysql_password" {
      description = "Please input mysql password."
    }
    variable "mysql_version" {
      description = "The version of mysql which wordpress used. Default to 5.6."
      default = "5.6"
    }

Step 4: Run the WordPress Terraform script

Find the directory where the preceding files are stored, such as /root/terraform/kuberneters-wordpress. Run the terraform apply command to deploy MySQL and WordPress in the created Kubernetes cluster. You must specify the mysql_password variable in the command because it has no default value defined in the variable file.

$ terraform apply -var 'mysql_password=Abc1234'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
...

Plan: 9 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

kubernetes_secret.mysql: Creating...
  data.%:                      "" => "1"
  data.password:               "<sensitive>" => "<sensitive>"
  metadata.#:                  "" => "1"
  metadata.0.generation:       "" => "<computed>"
  metadata.0.name:             "" => "mysql-pass"

......

Apply complete! Resources: 9 added, 0 changed, 0 destroyed.

Outputs:

slb_ip = 47.99.xx.xx

Step 5: Access WordPress

Enter the public IP address of the SLB instance in the browser to access the deployed WordPress application.

  1. Go to the welcome page of WordPress. Select a language and click Continue.wp_step1
  2. Enter a site name and the administrator username and password. Click Install WordPress.wp_step2
  3. After installing WordPress, click Log On. Enter the administrator username and password to access WordPress.wp_step3