Cloud people!
In the last Episode of this series, we covered the steps to configure karpenter using helm charts within Kubernetes to scale the cluster to meet demand.
Scaling an AWS EKS with Karpenter using Helm Provider with Terraform. Kubernetes Series - Episode 4
Javier Sepúlveda ・ Jan 17
In this episode, the focus is on deploying dynamic volumes with efs using Helm and terraform.
Requirements
Let's see how we can do this using terraform and raw manifest.
Reference Architecture
The reference architecture operates within a node.
Let's see how we can do this using terraform.
Step 1.
Is necessary using the providers for this deployment, you can see configurations providers in the file versions.tf.
This is the link for all code terraform in branch episode5.
Step 2.
In this step the EFS and the role are deployed, the Helm charts need the EFS ID and the role for creating the storageclass correctly.
Link terraform module of EFS for deploy the resource.
Registry
module "efs" {
source = "terraform-aws-modules/efs/aws"
version = "1.6.0"
name = "efs-testing"
encrypted = true
performance_mode = "generalPurpose"
throughput_mode = "provisioned"
provisioned_throughput_in_mibps = 25
enable_backup_policy = false
create_backup_policy = false
attach_policy = true
policy_statements = [
{
sid = "connect"
Effect = "Allow"
actions = ["elasticfilesystem:ClientMount",
"elasticfilesystem:ClientRootAccess",
"elasticfilesystem:ClientWrite"]
principals = [
{
type = "AWS"
identifiers = ["*"]
}
]
}
]
lifecycle_policy = {
transition_to_ia = "AFTER_90_DAYS"
}
mount_targets = {
for i in range(length(module.vpc.private_subnets)) :
module.vpc.private_subnets[i] => {
subnet_id = module.vpc.private_subnets[i]
# security_groups = [module.security-group.security_group_id]
}
}
security_group_description = "EFS security group"
security_group_vpc_id = module.vpc.vpc_id
security_group_rules = {
vpc = {
# relying on the defaults provdied for EFS/NFS (2049/TCP + ingress)
description = "NFS ingress from VPC private subnets"
cidr_blocks = module.vpc.private_subnets_cidr_blocks
}
}
}
Additionally, is necessary a role with the permissions for creating the dynamic volumes.
resource "aws_iam_role" "efs_controller_role" {
name = "role-efsdriver-${module.eks.cluster_name}"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${local.cleaned_issuer_url}"
},
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringEquals = {
"${local.cleaned_issuer_url}:sub" = "system:serviceaccount:kube-system:efs-csi-controller-sa"
"${local.cleaned_issuer_url}:aud" = "sts.amazonaws.com"
}
}
}
]
})
tags = local.tags
}
resource "aws_iam_role_policy_attachment" "efs_controller_policy_attachment" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEFSCSIDriverPolicy"
role = aws_iam_role.efs_controller_role.name
}
Step 3.
With the EFS and the role created, in this step the configuration of the aws module eks_blueprints_addons is performed, this module allows the helm deployment and the creation of the EFS controller. In this case, the values.yaml receives as parameter the role_arn or service account with the necessary permissions and the EFS ID that will be associated to the storageclass.
In this case, the helm is downloaded from the repository.
module "eks_blueprints_addons" {
source = "aws-ia/eks-blueprints-addons/aws"
version = "~> 1.0" #ensure to update this to the latest/desired version
cluster_name = module.eks.cluster_name
cluster_endpoint = module.eks.cluster_endpoint
cluster_version = module.eks.cluster_version
oidc_provider_arn = module.eks.oidc_provider_arn
helm_releases = {
efs-csi-driver = {
name = "efs-csi-driver"
namespace = "kube-system"
create_namespace = true
chart = "./helm-charts/aws-efs-csi-driver"
values = [
templatefile("./helm-charts/aws-efs-csi-driver/values.yaml", {
role_arn = aws_iam_role.efs_controller_role.arn,
efs_id = module.efs.id
})
]
}
}
}
In the past I was have problems with GID allocator, something related to this problem.
That is why I prefer to assign Gid ranges for the storage class, taking into account that the accesspoint has a quota of 1000.
Piece of code of values.yaml
storageClasses:
- name: efs-sc
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: ${efs_id}
directoryPerms: "700"
gidRangeEnd: "2000"
gidRangeStart: "1000"
reclaimPolicy: Delete
volumeBindingMode: Immediate
Step 4.
Great, For this moment all the components are deployed.
SA of EFS.
SC of EFS.
Step 5.
Great, For this moment all the components are deployed. For testing reason the namespace it is created manually.
kubectl create ns episode5
Step 6.
Now that the namespace is created, it will create the app that will use the pvc.
Statefulset
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-db
namespace: episode5
spec:
serviceName: mysql-svc
replicas: 1
selector:
matchLabels:
app: mysql-db
template:
metadata:
labels:
app: mysql-db
spec:
securityContext:
runAsUser: 2000
runAsGroup: 2000
fsGroup: 2000
containers:
- name: mysql-db
image: mysql:latest
ports:
- containerPort: 3306
volumeMounts:
- name: statefulset-dynamicstorage
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "segoja7secure!" #This is not recommend, use secrets!
- name: MYSQL_USER
value: "user" #This is not recommend, use secrets!
- name: MYSQL_PASSWORD
value: "episode2" #This is not recommend, use secrets!
volumes:
- name: statefulset-dynamicstorage
persistentVolumeClaim:
claimName: statefulset-dynamicstorage
In the moment that I was make proves, I facing an error related to posix users, this is special case for some applications that need to configuring her directory with the permissions required.
Please check those links for a better understand.
Link1
Link2
Link3
spec:
securityContext:
runAsUser: 2000
runAsGroup: 2000
fsGroup: 2000
In conclusion, the pod is running all process with the userid:2000
PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: statefulset-dynamicstorage
namespace: episode5
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
Step 7.
Now you can perform a storage validation, create a database.
Applying port forward for connect database using k9s.
Now, you can access from the client, in this case the client is heidi, you can use any client.
Step 8.
Recreating pods of statefulset and enabling again, port forward.
Connecting in the new pod.
By connecting back on a Heidi client to the mysql pod, you can see that the data is persistent and the database, table is stored in the efs dynamic volume, which shows that the data is being persisted beyond the lifecycle of the pod.
In this phase, a statefulset database pod was created using the efs dynamic provisioning volume.
The following addons have been installed:
• efs-csi
If you have any questions, please leave them in the comments!
Successful!!
Top comments (0)