Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
650dcdb
Change k3s.yaml from 127.0.0.1 to correct IP
alexandref75 Feb 16, 2023
a62665f
Fix chart version so artifacts are created
alexandref75 Feb 16, 2023
2d80457
Merge branch 'main' into test-k3s-edge
alexandref75 Feb 23, 2023
e91a93a
Fix k3s-start.sh script
alexandref75 Feb 23, 2023
eec846c
Merge branch 'main' into test-k3s-edge
alexandref75 Feb 23, 2023
9261c86
Allow grafana host to be renamed
alexandref75 Feb 24, 2023
dc9ade6
Merge branch 'main' into test-k3s-edge
alexandref75 Feb 24, 2023
eec371a
Add support for labeling node automatically
alexandref75 Feb 24, 2023
2a0e6e7
Merge branch 'main' into test-k3s-edge
alexandref75 Feb 25, 2023
aa7ed85
Adding terraform scripts
alexandref75 Feb 25, 2023
29f9489
Update of terraform for running smarter on EC2
alexandref75 Feb 27, 2023
76a8227
Fixing documentation and some leftover varibles
alexandref75 Feb 27, 2023
5d18bb7
Add support for traefik at nginx allowing use of letsencrypt
alexandref75 Feb 28, 2023
c163da3
Fixes for uysing traefik for nginx k3s configuration
alexandref75 Feb 28, 2023
c29dab3
Fix documentation to access nip.io for k3s-start.sh script
alexandref75 Feb 28, 2023
812e90c
Fix setting deployment-name
alexandref75 Feb 28, 2023
dbf1bdc
Organizing files on directory to simplify user interface
alexandref75 Feb 28, 2023
999c894
Put letsencrypt email a snot define so terraform ask for it
alexandref75 Feb 28, 2023
59b37e1
Add troubleshooting section
alexandref75 Mar 23, 2023
6bf1ce0
Fixes for traefik
alexandref75 Mar 23, 2023
31fa2c5
Merge branch 'main' into k3s-traefik
alexandref75 Mar 23, 2023
0d6e65c
Graviton instance
alexandref75 Mar 24, 2023
584f059
Merge branch 'main' into k3s-traefik
alexandref75 Mar 24, 2023
9ac5462
Anonymize
alexandref75 Mar 24, 2023
9b95c7b
A little more info for debugging
alexandref75 Mar 24, 2023
77378f1
Add letsencrypt_email as variable
alexandref75 Mar 29, 2023
67d8c64
Add timing and logging information
alexandref75 Mar 29, 2023
f5316d8
Add link to main README for terraform
alexandref75 Mar 29, 2023
93643ef
Add more external variables
alexandref75 Mar 29, 2023
4bd83f8
Add AWS_VPC_subnet_id as variable
alexandref75 Mar 29, 2023
3e9c112
Changed domain to <IP>.nip.io so Let's encrypt applies to the whole
alexandref75 Mar 30, 2023
154c03c
Add the extra variables
alexandref75 Mar 30, 2023
8109c18
FIx _ and < characters on README (markdown)
alexandref75 Mar 31, 2023
fde5a18
Syntax fixes
alexandref75 Mar 31, 2023
904b9fb
Add figure and more description
alexandref75 Apr 4, 2023
c102349
Fix reference to Terraform script
alexandref75 Apr 4, 2023
fb715d5
Rewrite main README to be clear on installation using terraform
alexandref75 Apr 4, 2023
94c6dfc
Fix README to put terraform first
alexandref75 Apr 4, 2023
0e74195
Add support for tfvars on README and a template
alexandref75 Apr 4, 2023
3e728cd
Change to template.tfvars
alexandref75 Apr 4, 2023
6fe2cd7
Change smarter-k3s-edge to be able to be embedded in another website
alexandref75 Apr 4, 2023
61cb5b2
Fix terraform to use grafana instead of k3s website
alexandref75 Apr 4, 2023
6612fcf
Merge branch 'main' into k3s-traefik
alexandref75 Apr 4, 2023
eb83670
Change to sslip.io and be a little more resilient
alexandref75 Apr 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# SMARTER Demo Deployment Instructions

[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/smarter)](https://artifacthub.io/packages/search?repo=smarter)
## This demo makes the following assumptions about your environment

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.

## Deploy using terraform

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.

## Step by step deployment

### This demo makes the following assumptions about your environment if deployed using helm charts

In this guide we assume you have done the following:
- You should have an installed InfluxDB and Grafana instance in a separate kubernetes cluster (cloud or local).
Expand All @@ -28,7 +37,6 @@ In this guide we assume you have done the following:
- 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
- The node must be able to reach your k3s-edge-server and cloud-data-node via IP

## Deploy demo
- 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).
- Ensure in your environment that your kubeconfig is set properly. As a quick sanity check you can run:
```bash
Expand Down
4 changes: 4 additions & 0 deletions terraform/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.terraform.lock.hcl
.terraform
ssh
terraform.tfstate
100 changes: 100 additions & 0 deletions terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Terraform script to install smarter on AWS EC2

This script installs SMARTER example using helm charts into one AWS EC2 instance.

This figure shows the components of the application and where they reside.
![SMARTER](SMARTER_example.png)

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.
Set the following variables to correct values:
region (provider "aws"): AWS region to allocate an EC2 instance on.

Required variables:

* letsencrypt\_email

Optional variables:

* deployment\_name: Prefix to apply to object names.
* AWS\_EC2\_instance\_type: instance type to be used
* AWS\_VPC\_subnet\_id: subnet_id use the default of the VPC if this is not defined

## Running

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.

### Run the following commands if using the smarter-variables.tfvars optionn

```
terraform init
# optional: terraform plan -var-file="smarter-variables.tfvars"
terraform apply -var-file="smarter-variables.tfvars"
```

### Run the following commands if setting the variables on the command line

```
terraform init
# optional: terraform plan -var "letsencrypt_email=<valid email>"
terraform apply -var "letsencrypt_email=<valid email>"
```

## Checking status of installation

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

```bash
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated> "tail -f /var/log/cloud-init-output.log"
```

## Outputs

Terraform will output the name of EC2 instance allocated and password/ID generated by Terraform.

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\>.

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:

```bash
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated>
```

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.
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.

Helm was used to install charts and can be used to manage them by setting the correct KUBECONFIG.

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.

```
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/k3s-start.sh.<password/ID> | bash -s -
```

Token and k3s.yaml file can be accessed by:

```
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/token.<password/ID> | bash -s -
wget https://grafana.<External IP of EC2 separated with dash>.sslip.io//k3s/k3s.yaml.<password/ID> | bash -s -
```

# Troubleshooting

## AWS authentication

Use the AWS credentials provided in the "Get credentials for ProjAdmins" page.
Terraform expects the following environment variables: AWS\_ACCESS\_KEY\_ID, AWS\_SECRET\_ACCESS\_KEY and AWS\_SESSION\_TOKEN.

## Networking

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.

## Debugging information

Log in to the EC2 machine using the ssh command

```bash
ssh -i ssh/<deployment-name>-prod-k3s.pem ubuntu@<EC2 instance allocated>
```

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
The script that is executed is called part-002
Binary file added terraform/SMARTER_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
143 changes: 143 additions & 0 deletions terraform/k3s/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}

required_version = ">= 1.2.0"
}

data "aws_ami" "ubuntu" {
most_recent = true

filter {
name = "name"
#arm64
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-arm64-server-*"]
#x86_64
#values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
owners = ["099720109477"]
}

resource "random_string" "agent_token" {
length = 24
special = false
}

resource "random_string" "k3s_edge_id" {
length = 24
special = false
}

resource "aws_iam_instance_profile" "instance_profile" {
name = "${var.deployment_name}-InstanceProfile"
role = var.iam_role_name
count = var.iam_role_name == null ? 0 : 1
}

data "cloudinit_config" "userData" {
part {
content = <<EOF
#cloud-config
---
hostname: "${var.deployment_name}"
EOF
content_type = "text/cloud-config"
}

part {
content = <<EOF
#!/bin/bash
echo "----- Installing k3s"
echo K3S_KUBECONFIG_MODE=${var.kubeconfig_mode} K3S_TOKEN=${random_string.agent_token.result} email=${var.letsencrypt_email} > /tmp/variables
curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=${var.kubeconfig_mode} K3S_TOKEN=${random_string.agent_token.result} sh -
echo "----- updating ubuntu"
apt-get update -y && apt-get upgrade -y && apt-get install awscli git -y
echo "----- Adding helm"
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /home/ubuntu/.profile
echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> /home/ubuntu/.bashrc
export ADVERTISE_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4)
export PUBLIC_HOSTNAME=$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f 1 | sed 's/^ec2-//')
export LOCAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
echo "----- Wating for k3s to start"
until [ -f /etc/rancher/k3s/k3s.yaml ]
do
sleep 5
done
echo "----- Adding smarter-cloud to k3s"
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"
echo "----- Checking if TLS certificate was generated"
until [ ! -z "$(kubectl get secret/my-smartercloud-grafana-tls 2>/dev/null)" ]
do
echo "Certificate not generated yet, wait 5 seconds and test again"
sleep 5
done
echo "----- Adding smarter-edge to k3s"
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"
echo "----- Waiting for k3s.yaml from k3s-edge"
until [ -f /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result} ]
do
sudo su - ubuntu bash -c "wget --no-check-certificate https://grafana.$PUBLIC_HOSTNAME.sslip.io/k3s/k3s.yaml.${random_string.k3s_edge_id.result}"
if [ -z "$(grep 'kind: Config' /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result})" ]
then
echo "Received a file but it is not a k3s.yaml file, removing"
rm /home/ubuntu/k3s.yaml.${random_string.k3s_edge_id.result}
fi
sleep 5
done
echo "----- Adding smarter-edge to k3s-edge"
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"
echo "----- Finished installing"
EOF
content_type = "text/x-shellscript"
}

part {
content = var.manifest_bucket_path == "" ? "" : <<EOF
#!/bin/bash
aws s3 sync s3://${var.manifest_bucket_path} /var/lib/rancher/k3s/server/manifests/
EOF
content_type = "text/x-shellscript"
}

# part {
# content = <<EOF
##!/bin/bash
#apt-get update && \
#apt-get install ec2-instance-connect -y
#EOF
# content_type = "text/x-shellscript"
# }
}

resource "aws_key_pair" "k3s_keypair" {
key_name = var.deployment_name
public_key = var.keypair_content
count = 1
}

resource "aws_instance" "k3s_instance" {
ami = var.ami_id == null ? data.aws_ami.ubuntu.id : var.ami_id
associate_public_ip_address = var.assign_public_ip
instance_type = var.instance_type
key_name = aws_key_pair.k3s_keypair[0].key_name
iam_instance_profile = var.iam_role_name == null ? null : aws_iam_instance_profile.instance_profile[0].name
subnet_id = var.subnet_id == "" ? "" : var.subnet_id
vpc_security_group_ids = var.security_group_ids
user_data = data.cloudinit_config.userData.rendered
tags = {
Name = "${var.deployment_name}-k3s"
}
}

output "instance" {
value = aws_instance.k3s_instance
}

output "k3s_edge" {
value = random_string.k3s_edge_id
}
94 changes: 94 additions & 0 deletions terraform/k3s/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
variable "keypair_path" {
type = string
default = ""
description = "The path to the public key to use for the instance."
}

variable "keypair_content" {
type = string
default = ""
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'."
}

variable "deployment_name" {
type = string
default = "k3s"
description = "A unique name used to generate other names for resources, such as instance names."
}

variable "iam_role_name" {
type = string
default = null
description = "The name of an IAM Role to assign to the instance. If left blank, no role will be assigned."
}

variable "subnet_id" {
type = string
default = ""
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."
}

variable "security_group_ids" {
type = list(string)
description = "A list of Security Group IDs to assign to the instance."
}

variable "assign_public_ip" {
type = bool
default = true
description = "If set to 'true', a public IP address will be assigned to the instance."
}

variable "instance_type" {
type = string
default = "t3.small"
description = "The AWS EC2 Instance Type to provision the instance as."
}

variable "manifest_bucket_path" {
type = string
default = ""
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."
}

variable "enable_worker_nodes" {
type = bool
description = "If set to 'true', a separate autoscaling group will be created for worker nodes."
default = false
}

variable "worker_node_min_count" {
type = number
description = "The minimum number of worker node instances to provision."
default = 0
}

variable "worker_node_max_count" {
type = number
description = "The maximum number of worker node instances to provsion."
default = 0
}

variable "worker_node_desired_count" {
type = number
description = "The desired number of worker nodes to provision."
default = 0
}

variable "kubeconfig_mode" {
type = string
description = "Sets the file mode of the generated KUBECONFIG file on the master k3s instance. Defaults to '600'."
default = "600"
}

variable "letsencrypt_email" {
type = string
description = "Email to be used in letsencrypt to generate certificates. No default"
}

variable "ami_id" {
type = string
description = "The AMI ID to use when provisioning the instance. If left at the default null value, the latest Ubuntu server image is used."
default = null
}

Loading