Skip to content

Commit 8cd6ed4

Browse files
authored
K3s traefik (#32)
* Change k3s.yaml from 127.0.0.1 to correct IP Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix chart version so artifacts are created Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix k3s-start.sh script Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Allow grafana host to be renamed Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add support for labeling node automatically Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Adding terraform scripts Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Update of terraform for running smarter on EC2 Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fixing documentation and some leftover varibles Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add support for traefik at nginx allowing use of letsencrypt Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fixes for uysing traefik for nginx k3s configuration Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix documentation to access nip.io for k3s-start.sh script Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix setting deployment-name Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Organizing files on directory to simplify user interface Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Put letsencrypt email a snot define so terraform ask for it Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add troubleshooting section Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fixes for traefik Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Graviton instance Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Anonymize Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * A little more info for debugging Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add letsencrypt_email as variable Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add timing and logging information Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add link to main README for terraform Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add more external variables Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add AWS_VPC_subnet_id as variable Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Changed domain to <IP>.nip.io so Let's encrypt applies to the whole domain Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add the extra variables Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * FIx _ and < characters on README (markdown) Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Syntax fixes Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add figure and more description Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix reference to Terraform script Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Rewrite main README to be clear on installation using terraform Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix README to put terraform first Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Add support for tfvars on README and a template Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Change to template.tfvars Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Change smarter-k3s-edge to be able to be embedded in another website Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Fix terraform to use grafana instead of k3s website Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> * Change to sslip.io and be a little more resilient Signed-off-by: Alexandre Peixoto Ferreira <[email protected]> --------- Signed-off-by: Alexandre Peixoto Ferreira <[email protected]>
1 parent efc7a45 commit 8cd6ed4

File tree

8 files changed

+499
-2
lines changed

8 files changed

+499
-2
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
# SMARTER Demo Deployment Instructions
22

33
[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/smarter)](https://artifacthub.io/packages/search?repo=smarter)
4-
## This demo makes the following assumptions about your environment
4+
5+
The demo can be deployed by using the terraform script on this repository [Terraform](terraform) and following the [readme](terraform/README.md). It is also described on the section "Deploy using terraform" below.
6+
7+
## Deploy using terraform
8+
9+
If you have an AWS account, a terraform script is available on this repository at [Terraform](terraform) and a [readme](terraform/README.md) describes how to use it. This script will allocate an AWS EC2 Graviton instance, install k3s and helm and install all the charts needed to run this demo. The only missing part is one or more edge nodes that the user needs to provide.
10+
11+
## Step by step deployment
12+
13+
### This demo makes the following assumptions about your environment if deployed using helm charts
514

615
In this guide we assume you have done the following:
716
- You should have an installed InfluxDB and Grafana instance in a separate kubernetes cluster (cloud or local).
@@ -28,7 +37,6 @@ In this guide we assume you have done the following:
2837
- You must be able to reach your edge node via IP on ports `22`(ssh) and `2520`(Webserver) from your dev machine for parts of this demo to work
2938
- The node must be able to reach your k3s-edge-server and cloud-data-node via IP
3039

31-
## Deploy demo
3240
- To deploy the base system components common to all edge nodes, as well as the demo applications, we opt to use **Helm v3**. To install helm on the device which you are managing your k3s edge cluster with, you can follow the guide [here](https://helm.sh/docs/intro/install/#from-script).
3341
- Ensure in your environment that your kubeconfig is set properly. As a quick sanity check you can run:
3442
```bash

terraform/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.terraform.lock.hcl
2+
.terraform
3+
ssh
4+
terraform.tfstate

terraform/README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Terraform script to install smarter on AWS EC2
2+
3+
This script installs SMARTER example using helm charts into one AWS EC2 instance.
4+
5+
This figure shows the components of the application and where they reside.
6+
![SMARTER](SMARTER_example.png)
7+
8+
It assumes that the environment variables AWS\_ACCESS\_KEY\_ID, AWS\_SECRET\_ACCESS\_KEY and AWS\_SESSION\_TOKEN are set correctly so Terraform can access AWS.
9+
Set the following variables to correct values:
10+
region (provider "aws"): AWS region to allocate an EC2 instance on.
11+
12+
Required variables:
13+
14+
* letsencrypt\_email
15+
16+
Optional variables:
17+
18+
* deployment\_name: Prefix to apply to object names.
19+
* AWS\_EC2\_instance\_type: instance type to be used
20+
* AWS\_VPC\_subnet\_id: subnet_id use the default of the VPC if this is not defined
21+
22+
## Running
23+
24+
An template.tfvars is provided that can be copied so all the variables are set in this file and referenced by the option -var-file="smarter-variables.tfvars" where smarter-variables.tfvars is the name of the file used to set the variables. Commented variables are ignored.
25+
26+
### Run the following commands if using the smarter-variables.tfvars optionn
27+
28+
```
29+
terraform init
30+
# optional: terraform plan -var-file="smarter-variables.tfvars"
31+
terraform apply -var-file="smarter-variables.tfvars"
32+
```
33+
34+
### Run the following commands if setting the variables on the command line
35+
36+
```
37+
terraform init
38+
# optional: terraform plan -var "letsencrypt_email=<valid email>"
39+
terraform apply -var "letsencrypt_email=<valid email>"
40+
```
41+
42+
## Checking status of installation
43+
44+
Please observe that the full installation of k3s, helm charts in the EC2 instance can take up to 15min (expected around 10min) with various parts of the system being available at different times. If it is desired to follow the installation the command below will print the current log and follow it
45+
46+
```bash
47+
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated> "tail -f /var/log/cloud-init-output.log"
48+
```
49+
50+
## Outputs
51+
52+
Terraform will output the name of EC2 instance allocated and password/ID generated by Terraform.
53+
54+
Grafana web interface can be accessed by https://grafana.\<External IP of EC2 separated with dash\>.sslip.io with user admin and password \<password/ID\>.
55+
56+
A ssh directory will be created locally containing a private/public SSH key that can be used to access the instance using the following command:
57+
58+
```bash
59+
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated>
60+
```
61+
62+
K3s cloud access on the instance (running the cloud containers) can be achieved by setting KUBECONFIG to /etc/rancher/k3s/k3s.yaml. It should be already be set for the ubuntu user at the end of the installation.
63+
K3s edge, that manages the edge devices and applications running on them, can be accessed by setting KUBECONFIG as $(pwd)/k3s.yaml.\<password/ID\>, that also will be available at the end of the installation.
64+
65+
Helm was used to install charts and can be used to manage them by setting the correct KUBECONFIG.
66+
67+
The edge devices can be installed (Raspberry pi4 for example) by running the following script. The script will install a k3s agent and configure that agent to be a node for k3s edge running on the EC2 instance.
68+
69+
```
70+
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/k3s-start.sh.<password/ID> | bash -s -
71+
```
72+
73+
Token and k3s.yaml file can be accessed by:
74+
75+
```
76+
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/token.<password/ID> | bash -s -
77+
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/k3s.yaml.<password/ID> | bash -s -
78+
```
79+
80+
# Troubleshooting
81+
82+
## AWS authentication
83+
84+
Use the AWS credentials provided in the "Get credentials for ProjAdmins" page.
85+
Terraform expects the following environment variables: AWS\_ACCESS\_KEY\_ID, AWS\_SECRET\_ACCESS\_KEY and AWS\_SESSION\_TOKEN.
86+
87+
## Networking
88+
89+
If an error is reported about "default subnet not found", a subnet was not defined as default for the VPC. A subnet can be set using the AWS\_VPC\_subnet\_id variable.
90+
91+
## Debugging information
92+
93+
Log in to the EC2 machine using the ssh command
94+
95+
```bash
96+
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated>
97+
```
98+
99+
Please take a look at the log at /var/log/cloud-init-output.log and /var/log/cloud-init.log at the EC2 machine to determine where the program failed
100+
The script that is executed is called part-002

terraform/SMARTER_example.png

75.8 KB
Loading

terraform/k3s/main.tf

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 4.16"
6+
}
7+
}
8+
9+
required_version = ">= 1.2.0"
10+
}
11+
12+
data "aws_ami" "ubuntu" {
13+
most_recent = true
14+
15+
filter {
16+
name = "name"
17+
#arm64
18+
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-arm64-server-*"]
19+
#x86_64
20+
#values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
21+
}
22+
owners = ["099720109477"]
23+
}
24+
25+
resource "random_string" "agent_token" {
26+
length = 24
27+
special = false
28+
}
29+
30+
resource "random_string" "k3s_edge_id" {
31+
length = 24
32+
special = false
33+
}
34+
35+
resource "aws_iam_instance_profile" "instance_profile" {
36+
name = "${var.deployment_name}-InstanceProfile"
37+
role = var.iam_role_name
38+
count = var.iam_role_name == null ? 0 : 1
39+
}
40+
41+
data "cloudinit_config" "userData" {
42+
part {
43+
content = <<EOF
44+
#cloud-config
45+
---
46+
hostname: "${var.deployment_name}"
47+
EOF
48+
content_type = "text/cloud-config"
49+
}
50+
51+
part {
52+
content = <<EOF
53+
#!/bin/bash
54+
echo "----- Installing k3s"
55+
echo K3S_KUBECONFIG_MODE=${var.kubeconfig_mode} K3S_TOKEN=${random_string.agent_token.result} email=${var.letsencrypt_email} > /tmp/variables
56+
curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=${var.kubeconfig_mode} K3S_TOKEN=${random_string.agent_token.result} sh -
57+
echo "----- updating ubuntu"
58+
apt-get update -y && apt-get upgrade -y && apt-get install awscli git -y
59+
echo "----- Adding helm"
60+
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
61+
echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /home/ubuntu/.profile
62+
echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /home/ubuntu/.bashrc
63+
export ADVERTISE_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4)
64+
export PUBLIC_HOSTNAME=$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f 1 | sed 's/^ec2-//')
65+
export LOCAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
66+
echo "----- Wating for k3s to start"
67+
until [ -f /etc/rancher/k3s/k3s.yaml ]
68+
do
69+
sleep 5
70+
done
71+
echo "----- Adding smarter-cloud to k3s"
72+
sudo su - ubuntu bash -c "helm repo add smarter https://smarter-project.github.io/documentation;helm install my-smartercloud smarter/smarter-cloud --set email=${var.letsencrypt_email} --set host=grafana --set domain=$PUBLIC_HOSTNAME.sslip.io --set prometheus.grafana.adminPassword=${random_string.k3s_edge_id.result} --wait"
73+
echo "----- Checking if TLS certificate was generated"
74+
until [ ! -z "$(kubectl get secret/my-smartercloud-grafana-tls 2>/dev/null)" ]
75+
do
76+
echo "Certificate not generated yet, wait 5 seconds and test again"
77+
sleep 5
78+
done
79+
echo "----- Adding smarter-edge to k3s"
80+
sudo su - ubuntu bash -c "helm install my-smartercloud-edge smarter/smarter-k3s-edge --set configuration.externalHostIP=$ADVERTISE_IP --set configuration.hostIP=$LOCAL_IP --set configuration.port=6444 --set configuration.portHTTP=80 --set configuration.id='${random_string.k3s_edge_id.result}' --set configuration.smarter_demo_labels=true --set configuration.host=grafana --set configuration.domain=$PUBLIC_HOSTNAME.sslip.io --set configuration.traefik=true --set configuration.certificateID=my-smartercloud-grafana-tls --set configuration.wwwpath=/k3s/ --wait"
81+
echo "----- Waiting for k3s.yaml from k3s-edge"
82+
until [ -f /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result} ]
83+
do
84+
sudo su - ubuntu bash -c "wget --no-check-certificate https://grafana.$PUBLIC_HOSTNAME.sslip.io/k3s/k3s.yaml.${random_string.k3s_edge_id.result}"
85+
if [ -z "$(grep 'kind: Config' /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result})" ]
86+
then
87+
echo "Received a file but it is not a k3s.yaml file, removing"
88+
rm /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result}
89+
fi
90+
sleep 5
91+
done
92+
echo "----- Adding smarter-edge to k3s-edge"
93+
sudo su - ubuntu bash -c "export KUBECONFIG=/home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result};helm install --create-namespace --namespace smarter my-smartercloud-edge smarter/smarter-edge --wait;helm install --create-namespace --namespace smarter --set global.domain=$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f 2-) --set smarter-fluent-bit.fluentd.host=$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f 1) my-smartercloud-demo smarter/smarter-demo --wait"
94+
echo "----- Finished installing"
95+
EOF
96+
content_type = "text/x-shellscript"
97+
}
98+
99+
part {
100+
content = var.manifest_bucket_path == "" ? "" : <<EOF
101+
#!/bin/bash
102+
aws s3 sync s3://${var.manifest_bucket_path} /var/lib/rancher/k3s/server/manifests/
103+
EOF
104+
content_type = "text/x-shellscript"
105+
}
106+
107+
# part {
108+
# content = <<EOF
109+
##!/bin/bash
110+
#apt-get update && \
111+
#apt-get install ec2-instance-connect -y
112+
#EOF
113+
# content_type = "text/x-shellscript"
114+
# }
115+
}
116+
117+
resource "aws_key_pair" "k3s_keypair" {
118+
key_name = var.deployment_name
119+
public_key = var.keypair_content
120+
count = 1
121+
}
122+
123+
resource "aws_instance" "k3s_instance" {
124+
ami = var.ami_id == null ? data.aws_ami.ubuntu.id : var.ami_id
125+
associate_public_ip_address = var.assign_public_ip
126+
instance_type = var.instance_type
127+
key_name = aws_key_pair.k3s_keypair[0].key_name
128+
iam_instance_profile = var.iam_role_name == null ? null : aws_iam_instance_profile.instance_profile[0].name
129+
subnet_id = var.subnet_id == "" ? "" : var.subnet_id
130+
vpc_security_group_ids = var.security_group_ids
131+
user_data = data.cloudinit_config.userData.rendered
132+
tags = {
133+
Name = "${var.deployment_name}-k3s"
134+
}
135+
}
136+
137+
output "instance" {
138+
value = aws_instance.k3s_instance
139+
}
140+
141+
output "k3s_edge" {
142+
value = random_string.k3s_edge_id
143+
}

terraform/k3s/variables.tf

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
variable "keypair_path" {
2+
type = string
3+
default = ""
4+
description = "The path to the public key to use for the instance."
5+
}
6+
7+
variable "keypair_content" {
8+
type = string
9+
default = ""
10+
description = "The raw data to be used for the public key for the instance. If this is used, no value must be specified for 'keypair_path'."
11+
}
12+
13+
variable "deployment_name" {
14+
type = string
15+
default = "k3s"
16+
description = "A unique name used to generate other names for resources, such as instance names."
17+
}
18+
19+
variable "iam_role_name" {
20+
type = string
21+
default = null
22+
description = "The name of an IAM Role to assign to the instance. If left blank, no role will be assigned."
23+
}
24+
25+
variable "subnet_id" {
26+
type = string
27+
default = ""
28+
description = "The ID of a VPC subnet to assign the instance to. If left blank, the instance will be provisioned in the default subnet of the default VPC."
29+
}
30+
31+
variable "security_group_ids" {
32+
type = list(string)
33+
description = "A list of Security Group IDs to assign to the instance."
34+
}
35+
36+
variable "assign_public_ip" {
37+
type = bool
38+
default = true
39+
description = "If set to 'true', a public IP address will be assigned to the instance."
40+
}
41+
42+
variable "instance_type" {
43+
type = string
44+
default = "t3.small"
45+
description = "The AWS EC2 Instance Type to provision the instance as."
46+
}
47+
48+
variable "manifest_bucket_path" {
49+
type = string
50+
default = ""
51+
description = "The AWS S3 bucket name and path that will be used to download manifest files for auto-installation as per [this documentation](https://rancher.com/docs/k3s/latest/en/advanced/). Should be specified as 'bucket name/folder name/'. The IAM Role assigned to the instance must have GetObject access to this bucket."
52+
}
53+
54+
variable "enable_worker_nodes" {
55+
type = bool
56+
description = "If set to 'true', a separate autoscaling group will be created for worker nodes."
57+
default = false
58+
}
59+
60+
variable "worker_node_min_count" {
61+
type = number
62+
description = "The minimum number of worker node instances to provision."
63+
default = 0
64+
}
65+
66+
variable "worker_node_max_count" {
67+
type = number
68+
description = "The maximum number of worker node instances to provsion."
69+
default = 0
70+
}
71+
72+
variable "worker_node_desired_count" {
73+
type = number
74+
description = "The desired number of worker nodes to provision."
75+
default = 0
76+
}
77+
78+
variable "kubeconfig_mode" {
79+
type = string
80+
description = "Sets the file mode of the generated KUBECONFIG file on the master k3s instance. Defaults to '600'."
81+
default = "600"
82+
}
83+
84+
variable "letsencrypt_email" {
85+
type = string
86+
description = "Email to be used in letsencrypt to generate certificates. No default"
87+
}
88+
89+
variable "ami_id" {
90+
type = string
91+
description = "The AMI ID to use when provisioning the instance. If left at the default null value, the latest Ubuntu server image is used."
92+
default = null
93+
}
94+

0 commit comments

Comments
 (0)