# Cost Analysis using AWS Lambda


## Introduction

Lambda 上运行了以下操作
1. aws_cost_analysis_resources.py 获取 AWS 资源列表, 目前获取了 EC2, EBS, ELB 等资源信息, 每个资源包括了 Tag 等信息. 这个 Lambda 由 CloudEvent 每小时触发一次, 执行成功后依次执行其他 Lambda. 这个 Lambda 会结合 Meta DB 环境下的 CronJob 获得的数据和 AWS 资源记录, 进行分析, 生成 JSON 和 CSV 的记录, 表头为:
    
```json
{
   "project":"null",
   "product":"null",
   "id":"null",
   "name":"null",
   "k8s":"null",
   "k8s_type":"null",
   "shoot_id":"null",
   "tenant_id":"null",
   "cluster_id":"null",
   "usage":"null",
   "ec2_instance_type":"null",
   "ec2_instance_state":"null",
   "ec2_wg_id":"null",
   "ebs_pvc_name":"null",
   "ebs_state":"null",
   "ebs_real_k8s":"null",
   "ebs_ec2_mount":"null",
   "elb_service_name":"null",
   "stakeholder_cluster":"null",
   "stakeholder_tenant":"null",
   "region":"null",
   "is_leak":"null",
   "hold_time":0
}
```

2. aws_cost_analysis_billing_report.py 获取了 AWS Cost Explorer 的 Billing 记录, 按 Tenant ID 和服务类型进行分类, 获得的是 Daily 的信息

3. aws_cost_analysis_dump_to_google_sheet.py, aws_cost_analysis_lark_bot.py, 将上面的分析结果输出到 Google Sheet, 并生成最近关注的信息到飞书 Bot.

MetaDB CronJob 执行的操作
1. 执行 SQL, 执行一些 Join SQL 操作, 对 Cluster, Shoot, Tenant, User, Billing, ClusterProfile 等信息, 并且处理成 Cluster, Shoot, Billing 三者信息, 做好此信息范围内的信息采集.

## Deployment

Lambda 部署主要操作为:
1. 上传代码
2. 创建 S3 bucket, 设置超时和环境变量
3. 设置 IAM 权限

### 上传 cost-analysis-lambda 里的代码

上传 cost-analysis-lambda 里面的代码, 选择 Python 3, 选择创建新 Role, 后面再授权.

![Lambda List](image/lambda_list.png)

### 创建 S3 Bucket, 并设置 Lambda 环境变量和超时

一般 Lambda 需要设置超时 5 分钟, 然后需要一个 Bucket 用来存中间结果, 存 JSON 和 CSV 数据.

创建 Bucket 之后, 需要设置环境变量

aws_cost_analysis_resources 需要设置以下环境变量
1. REPORT_BUCKET 指向刚才创建的 bucket

aws-cost-analysis-billing-report 需要设置以下环境变量
1. REPORT_BUCKET 指向刚才创建的 bucket

aws-cost-analysis-to-google-sheet 需要设置以下环境变量
1. REPORT_BUCKET 指向刚才创建的 bucket, 里面需要存放 Google Sheet API token
2. SOC_GROUP_USER,TIDBCLOUD_GROUP_USER 指向允许访问账单的邮件组

aws-cost-analysis-feishu-bot 需要设置以下环境变量
1. REPORT_BUCKET 指向刚才创建的 bucket

### 授权

aws_cost_analysis_resources 需要 EC2, ELB, NAT 的 Describe 相关权限, 并且需要把结果要输出到 S3. 在结合 MetaDB 信息分析的部分, 需要 List MetaDB 上传各个 Project 文件的目录, 所以也需要 ListBucket 权限

Example 如下, 需要替换 `<REPORT_BUCKET>`:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:DescribeLoadBalancers",
                "ec2:DescribeInstances",
                "elasticloadbalancing:DescribeTags",
                "ec2:DescribeRegions",
                "ec2:DescribeVolumes",
                "ec2:DescribeNatGateways"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectTagging",
                "s3:ListBucket",
                "s3:PutObjectTagging"
            ],
            "Resource": [
                "arn:aws:s3:::<REPORT_BUCKET>/*",
                "arn:aws:s3:::<REPORT_BUCKET>"
            ]
        }
    ]
}
```

aws-cost-analysis-billing-report 需要 Cost Explorer 权限, 并且需要把结果写到 S3

Example 如下, 需要替换 `<REPORT_BUCKET>`:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ce:GetCostAndUsageWithResources",
                "ce:GetCostAndUsage"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<REPORT_BUCKET>/*"
        }
    ]
}
```

aws-cost-analysis-google-sheet 需要读 S3 的结果, 需要读 S3 里存的 Google Sheet API token.

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<REPORT_BUCKET>/*"
        }
    ]
}
```

aws-cost-analysis-metadb 需要将结果写入 S3.

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<REPORT_BUCKET>/*"
        }
    ]
}
```

此外 MetaDB CronJob 需要使用 serviceaccount 获取 IAM 权限, 所以需要配置 aws-cost-analysis-metadb role 的 trust relationship

Example 如下, 需要提供 ACCOUNT_ID, 然后从 EKS 界面得到 OIDC_PROVIDER, 例如 oidc.eks.us-west-2.amazonaws.com/id/00****************************C0, 另外需要标明 EKS 下 CronJob 使用的 serviceaccount 所在的 namespace 和自己的 name.

对于多个 EKS 使用该 IAM, 可以配置多个 trust relationship, 配置多个 EKS OIDC Provider 就行.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:<OIDC_PROVIDER>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "<OIDC_PROVIDER>:sub": "system:serviceaccount:<namespace>:aws-cost-analysis"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:<OIDC_PROVIDER>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "<OIDC_PROVIDER>:sub": "system:serviceaccount:<namespace>:aws-cost-analysis"
        }
      }
    }
  ]
}
```

### 部署 MetaDB 信息获取 CronJob

部署 CronJob cost-analysis/cost-analysis-meta/analysis_meta_job.yaml, 使用的 Docker 镜像主要是为了安装 PyMySQL 和 Boto3 的依赖, 脚本在 ConfigMap 内, Dockerfile 在同一目录下.

需要把该 YAML 部署在 MetaDB 同一 namespace 下, 需要修改 YAML 中 `eks.amazonaws.com/role-arn` `SECRET_PASSWORD` 的 MetaDB Password Secret, `REPORT_BUCKET` 为 S3 中存储结果的 Bucket 

## 用途

### Lambda
### Meta DB CronJob
### Google Sheet and Lark Bot
