├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── devops ├── .terraform.lock.hcl ├── cluster.tf ├── locals.tf ├── main.tf └── variables.tf ├── k8s ├── deployment.yaml └── service.yaml └── terraforming-kubernetes.code-workspace /.gitignore: -------------------------------------------------------------------------------- 1 | devops/backend 2 | devops/*.tfvars 3 | .kube/kubeconfig.yaml 4 | 5 | *.code-workspace 6 | 7 | # Local .terraform directories 8 | **/.terraform/* 9 | 10 | # .tfstate files 11 | *.tfstate 12 | *.tfstate.* 13 | 14 | # Crash log files 15 | crash.log 16 | 17 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 18 | # .tfvars files are managed as part of configuration and so should be included in 19 | # version control. 20 | # 21 | # example.tfvars 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # 32 | # !example_override.tf 33 | 34 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 35 | # example: *tfplan* 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Coding For Entrepreneurs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | init: 2 | terraform -chdir=./devops init --backend-config=backend 3 | 4 | plan: 5 | terraform -chdir=./devops plan 6 | 7 | tf_apply: 8 | terraform -chdir=./devops apply 9 | 10 | tf_destroy: 11 | terraform -chdir=./devops apply -destroy 12 | 13 | console: 14 | terraform -chdir=./devops console 15 | 16 | k8s_apply: 17 | kubectl apply -f k8s/ 18 | 19 | k8s_delete: 20 | kubectl delete -f k8s/ 21 | 22 | cfe_nginx_ip: 23 | kubectl get service cfe-nginx-service -o "jsonpath={.status.loadBalancer.ingress[0].ip}" 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Terraforming Kubernetes Image](https://static.codingforentrepreneurs.com/media/courses/terraforming-kubernetes-on-linode/5d16a50c-a2f3-40e5-8906-eb375d696d81.jpg)](https://www.codingforentrepreneurs.com/courses/terraforming-kubernetes-on-linode/) 2 | # Terraforming Kubernetes 3 | What if starting a remote virtual machine instance was as simple as changing a number on a document and running a command? That is how simple terraform can be and why it's so powerful. 4 | 5 | Using documents to change what server we need running is a feature of Infrastructure as Code and can be a paradigm shift for many developers since it's so simple yet effective. One of the biggest advantages of IaC and terraform: version control and CI/CD pipeline automation. 6 | 7 | Terraform will turn on (and off) the servers we need. Kubernetes will manage how we allocate the resources on those servers to run the applications we want and need. The pair is a juggernaut of automation. This post will teach you exactly what to do to use them both. 8 | 9 | Want a minimal and rapid-fire version of this post? Check out this [repo](https://github.com/codingforentrepreneurs/terraforming-kubernetes-rapid). 10 | 11 | ### Watch the course [here](https://www.codingforentrepreneurs.com/courses/terraforming-kubernetes-on-linode/) 12 | 13 | 14 | __Other Resources__ 15 | - [Blog Post](https://www.codingforentrepreneurs.com/blog/terraforming-kubernetes-on-linode/) 16 | - [Github Repo](https://github.com/codingforentrepreneurs/terraforming-kubernetes) 17 | - Recommended Course: [Docker & Docker Compose](https://www.codingforentrepreneurs.com/courses/docker-and-docker-compose/) 18 | - [Terraform Install Guide](https://developer.hashicorp.com/terraform/downloads) 19 | - [Kubernetes Command Line Tool Install Guide](https://kubernetes.io/docs/tasks/tools/) 20 | 21 | 22 | 23 | ### Using this Repo 24 | 25 | #### Clone Repo 26 | ```bash 27 | git clone https://github.com/codingforentrepreneurs/terraforming-kubernetes 28 | cd terraforming-kubernetes 29 | ``` 30 | 31 | #### Create Linode API Key 32 | 1. Get an account with a $100 credit [here](https://www.linode.com/cfe) 33 | 2. Create an API Personal Access Key on Linode [here](https://cloud.linode.com/profile/tokens) 34 | 35 | ```bash 36 | echo "linode_api_token=\"YOUR_API_KEY\"" >> devops/terraform.tfvars 37 | ``` 38 | 39 | ### Configure Cloud-based Terraform State on a Object Storage Bucket 40 | 41 | 1. Create an [Object Storage Bucket](https://cloud.linode.com/object-storage/buckets) 42 | 2. Create an [Access Key for Object Storage](https://cloud.linode.com/object-storage/access-keys) 43 | 3. Update the terraform backend. 44 | 45 | Below is an example backend that you need to modify to fit your Object Storage bucket. This backend will store your Terraform statefiles in Object Storage instead of locally. 46 | 47 | In `devops/backend` add: 48 | ```bash 49 | skip_credentials_validation=true 50 | skip_region_validation=true 51 | bucket="YOUR_CUSTOM_OBJECT_STORAGE_BUCKET_NAME" 52 | key="terraforming-kubernetes.tfstate" 53 | region="us-southeast-1" 54 | endpoint="us-southeast-1.linodeobjects.com" 55 | access_key="YOUR_CUSTOM_S3_ACCESS_KEY" 56 | secret_key="YOUR_CUSTOM_S3_SECRET_KEY" 57 | ``` 58 | Replace: 59 | - `YOUR_CUSTOM_OBJECT_STORAGE_BUCKET_NAME` with the bucket you created 60 | - `YOUR_CUSTOM_S3_ACCESS_KEY` and `YOUR_CUSTOM_S3_SECRET_KEY` with the object storage access keys you created 61 | - Replace any other attributes to fit your specific bucket and project (i.e. region, endpoint, key) 62 | 63 | #### Terraform commands 64 | 65 | ``` 66 | terraform -chdir=./devops init --backend-config=backend 67 | terraform -chdir=./devops apply 68 | ``` 69 | After running apply, type `yes` to agree. 70 | 71 | ### Kubernetes Commands 72 | Once Terraform completes, the relative folder `.kube` will be created with a `kubeconfig.yaml` file that you can use. If you're on VSCode, your `KUBECONFIG` environment variable will already be set for you so you can: 73 | 74 | ``` 75 | kubecl get nodes 76 | ``` 77 | If you have any issues here, consider watching [the course](https://www.codingforentrepreneurs.com/courses/terraforming-kubernetes-on-linode/) or reading [the blog post](https://www.codingforentrepreneurs.com/blog/terraforming-kubernetes-on-linode/). 78 | -------------------------------------------------------------------------------- /devops/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/local" { 5 | version = "2.2.3" 6 | hashes = [ 7 | "h1:FvRIEgCmAezgZUqb2F+PZ9WnSSnR5zbEM2ZI+GLmbMk=", 8 | "zh:04f0978bb3e052707b8e82e46780c371ac1c66b689b4a23bbc2f58865ab7d5c0", 9 | "zh:6484f1b3e9e3771eb7cc8e8bab8b35f939a55d550b3f4fb2ab141a24269ee6aa", 10 | "zh:78a56d59a013cb0f7eb1c92815d6eb5cf07f8b5f0ae20b96d049e73db915b238", 11 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 12 | "zh:8aa9950f4c4db37239bcb62e19910c49e47043f6c8587e5b0396619923657797", 13 | "zh:996beea85f9084a725ff0e6473a4594deb5266727c5f56e9c1c7c62ded6addbb", 14 | "zh:9a7ef7a21f48fabfd145b2e2a4240ca57517ad155017e86a30860d7c0c109de3", 15 | "zh:a63e70ac052aa25120113bcddd50c1f3cfe61f681a93a50cea5595a4b2cc3e1c", 16 | "zh:a6e8d46f94108e049ad85dbed60354236dc0b9b5ec8eabe01c4580280a43d3b8", 17 | "zh:bb112ce7efbfcfa0e65ed97fa245ef348e0fd5bfa5a7e4ab2091a9bd469f0a9e", 18 | "zh:d7bec0da5c094c6955efed100f3fe22fca8866859f87c025be1760feb174d6d9", 19 | "zh:fb9f271b72094d07cef8154cd3d50e9aa818a0ea39130bc193132ad7b23076fd", 20 | ] 21 | } 22 | 23 | provider "registry.terraform.io/linode/linode" { 24 | version = "1.29.4" 25 | hashes = [ 26 | "h1:pdhSuNr3SQWvHHI+C1zF8J8tJetxGJoCQNxgsg1QHQs=", 27 | "zh:06ccda35d968429a1184aaf981c8104394fa1d719de86b718c56d93c27c1fcd6", 28 | "zh:1fb2497917094e77bde90fe6ee781e20cee739142b891391480c1b3376d81dbb", 29 | "zh:27960e9c07e995aad07a9c5ebfd7fe0304fffd4cb159fd215e82932b798c6d55", 30 | "zh:4ed29807c423c77aab1338972aa1ec3cc16c6b14f4c25c86f4427e8a86bfc467", 31 | "zh:7a39103dc0dc8538f5258d3b64db1e6c91335640763bd05da0478e99748a4949", 32 | "zh:95b3e418e6fcb4b826be9b289a834f1b9893977bd330ac418e0285e56a4644c1", 33 | "zh:ac69c992a5cbaaa6ed9bb65206309ab2c71b5eb17740b7a5295532f9840c67fd", 34 | "zh:ae943e8975075cd9664f00a028838566fdf879c772e518b7adcc82e757916a67", 35 | "zh:b3a85a52489bc3777b5e8c4428b8ea42ae8e0f2398077699c1eb99acea931a34", 36 | "zh:c1a2e945f5691ed97b9cf01351dd3a99c2f9871f172bd71ba0c8a810c75740cd", 37 | "zh:ce86a03d73ee3d2ed58c6fe853cd2a9d0974710d94a0aeb4c195a9d1e78a3481", 38 | "zh:d34afbbf848d8b541a068d64fa04ace13c3bd37ad19fd8b0796662f553ca9652", 39 | "zh:e13b4847098d295cd8216eeec55d940cfc4544672fdc89e0048dd067e69b63f8", 40 | "zh:fc62e9f8fc5d37d28aba2077db10355839cae6d7770eaf8711f97877bac046ab", 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /devops/cluster.tf: -------------------------------------------------------------------------------- 1 | resource "linode_lke_cluster" "terraform-k8s-resource" { 2 | label = var.k8s_label 3 | k8s_version = var.k8s_version 4 | region = var.k8s_region 5 | tags = var.k8s_tags 6 | 7 | pool { 8 | type = var.k8s_node_type 9 | count = var.k8s_node_count 10 | } 11 | } 12 | 13 | 14 | resource "local_file" "k8s_config" { 15 | content = "${nonsensitive(base64decode(linode_lke_cluster.terraform-k8s-resource.kubeconfig))}" 16 | filename = "${local.k8s_config_file}" 17 | file_permission = "0600" 18 | } -------------------------------------------------------------------------------- /devops/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | root_dir = "${dirname(abspath(path.root))}" 3 | k8s_config_dir = "${local.root_dir}/.kube/" 4 | k8s_config_file = "${local.root_dir}/.kube/kubeconfig.yaml" 5 | } -------------------------------------------------------------------------------- /devops/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.15" 3 | required_providers { 4 | linode = { 5 | source = "linode/linode" 6 | # version = "..." 7 | } 8 | } 9 | backend "s3" {} 10 | } 11 | 12 | 13 | 14 | provider "linode" { 15 | token = var.linode_api_token 16 | } -------------------------------------------------------------------------------- /devops/variables.tf: -------------------------------------------------------------------------------- 1 | variable "linode_api_token" { 2 | sensitive = true 3 | } 4 | 5 | variable "k8s_label" { 6 | default = "terraform-k8s-cluster" 7 | } 8 | 9 | variable "k8s_version" { 10 | default = "1.25" 11 | } 12 | 13 | variable "k8s_region" { 14 | default = "us-east" 15 | } 16 | 17 | variable "k8s_tags" { 18 | type = list(string) 19 | default = ["terraform-k8s"] 20 | } 21 | 22 | 23 | variable "k8s_node_count" { 24 | type = number 25 | default = 3 26 | } 27 | 28 | variable "k8s_node_type" { 29 | default = "g6-standard-1" 30 | } 31 | -------------------------------------------------------------------------------- /k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: cfe-nginx-deployment 6 | spec: 7 | replicas: 3 8 | selector: 9 | matchLabels: 10 | app: cfe-nginx-deployment 11 | template: 12 | metadata: 13 | labels: 14 | app: cfe-nginx-deployment 15 | spec: 16 | containers: 17 | - name: cfe-nginx-container 18 | image: codingforentrepreneurs/cfe-nginx:latest 19 | imagePullPolicy: Always 20 | ports: 21 | - name: cfe-nginx-port 22 | containerPort: 80 -------------------------------------------------------------------------------- /k8s/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cfe-nginx-service 5 | spec: 6 | selector: 7 | app: cfe-nginx-deployment 8 | type: LoadBalancer # external IP 9 | ports: 10 | - protocol: TCP 11 | port: 80 # http -> 80 12 | targetPort: cfe-nginx-port 13 | -------------------------------------------------------------------------------- /terraforming-kubernetes.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.autoSave": "afterDelay", 9 | "terminal.integrated.env.osx": { 10 | "KUBECONFIG": "${workspaceFolder}/.kube/kubeconfig.yaml" 11 | }, 12 | "terminal.integrated.env.windows": { 13 | "KUBECONFIG": "${workspaceFolder}\\.kube\\kubeconfig.yaml" 14 | }, 15 | "terminal.integrated.env.linux": { 16 | "KUBECONFIG": "${workspaceFolder}/.kube/kubeconfig.yaml" 17 | }, 18 | } 19 | } 20 | --------------------------------------------------------------------------------