AWS。Lambda - 将EC2标签复制到其EBS上,第一部分 - Python和boto3

291 阅读10分钟

我们有一个AWS Elastic Kubernetes Service集群,其中有几个WorkerNode Group,通过使用eksctl ,创建为AWS AutoScaling Group ,详情请看AWS Elastic Kubernetes Service:集群创建自动化,第二部分--Ansible,eksctl

eksctl 的WorkerNode Group配置保留了一组标签,这些标签被我们的团队用于AWS库存。

---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: "{{ eks_cluster_name }}"
  region: "{{ region }}"
  version: "{{ k8s_version }}"

nodeGroups:

### Common ###

  - name: "{{ k8s_common_nodegroup_name }}-{{ item }}-v2021-09"
    instanceType: "{{ k8s_common_nodegroup_instance_type }}"
    privateNetworking: true
    labels:
      role: common-workers
    ...
    tags:
      Tier: "Devops"
      Domain: "eks.devops.{{ region }}.{{ env | lower }}.bttrm.local"
      ServiceType: "EC2"
      Env: {{ env }}
      Function: "Kubernetes WorkerNode"
      NetworkType: "Private"
      DataClass: "Public"
      AssetOwner: "{{ asset_owner }}"
      AssetCustodian: "{{ asset_custodian }}"
      OperatingSystem: "Amazon Linux"
      JiraTicket: "{{ jira_ticket }}"
      ConfidentialityRequirement: "Med"
      IntegrityRequirement: "Med"
      AvailabilityRequirement: "Med"
...

这些标签被应用到AutoScale组。

然后被应用到从这个自动规模组创建的EC2实例上。

这里的问题是,这些标签并没有被复制到连接到EC2的Elastic Block Store设备上。

另外,除了Kubernetes WorkerNodes,在我们的AWS账户中,我们还有普通的EC2实例,其中一些实例可能只有一个根设备,而其他实例可能有一些额外的磁盘用于备份数据。

此外,我们不仅需要从其EC2中复制标签,而且我还想添加一个专门的标签,描述一个磁盘的功能--根卷数据卷和лиKubernetes PVC卷

我们如何做到这一点?好吧,就像AWS控制台没有涵盖的几乎所有东西一样,通过使用AWS Lambda服务:让我们创建一个函数,当一个新的EC2启动时,它将被触发,并将这个EC2的标签复制到所有EBS卷,连接到这个实例。

那么,在这个AWS Lambda函数的逻辑中,我们需要做什么。

  1. 当一个新的EC2被创建时 - 触发一个Lambda函数
  2. 该函数将接受一个EC2 ID,并找到所有相关的EBS卷
  3. 将从这个EC2复制AWS标签到它的所有EBS上
  4. 将添加一个名为Role的新标签。
    1. 如果一个EBS是由Kubernetes PVC创建的,并被挂载到EC2上,由Kubernetes WorkerNode AWS AutoScale Group启动,那么我们将设置标签。Role: "PvcVolume"
    2. 如果EBS是在普通的EC2创建过程中创建的,那么将检查其挂载点,并决定使用哪个值 -Role: "RootVolume" ,或Role: "DataVolume"

我们走吧。

Python脚本:复制AWS标签

首先,让我们写一个Pythion脚本,测试它,并将进入AWS Lambda,在这篇文章的第二部分。

当我们将适应一个Lambda函数时,我们将对其进行快速更新,使其可以使用一个专门的EC2 ID来代替。

boto3获取EC2实例及其EBS卷的列表

第一件事是在AWS账户中进行认证,获得特定区域内的所有EC2实例,然后获得每个EC2所连接的EBS卷的列表。

然后,有了这些信息,我们就可以使用它们的标签了。

脚本。

#!/usr/bin/env python

import os   
import boto3
            
ec2 = boto3.resource('ec2',
        region_name=os.getenv("AWS_DEFAULT_REGION"),
        aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
        aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY")
    )
    
def lambda_handler(event, context):
            
    base = ec2.instances.all()
                
    for instance in base:
            
        print("\n[DEBUG] EC2\n\t\tID: " + str(instance))
        print("\tEBS")

        for vol in instance.volumes.all():
    
            vol_id = str(vol)
            print("\t\tID: " + vol_id)
        
if __name__ == "__main__":
    lambda_handler(0, 0)

在这里,在ec2 变量中,我们正在创建一个具有 boto3.resourceec2类型,并将通过使用$AWS_ACCESS_KEY_ID$AWS_SECRET_ACCESS_KEY 变量在AWS账户中进行认证。之后,在我们的Lambda中,认证和授权将通过IAWS IAM角色完成。

在脚本的最后,我们要调用lambda_handler() 函数,如果脚本是作为一个专门的程序执行的,详情请看Python:什么是if __name__ == "__main__" ?

lambda_handler() ,我们将调用 ec2.instances.all()方法来获取一个区域内的所有实例,然后在for 循环中通过调用 instance.volumes.all()我们将获得连接到这个EC2的EBS卷的列表。

现在,lambda_handler() 的参数以 "0*, 0*"的形式传递,以后在Lambda中,我们将把eventcontext 对象。

设置AWS认证变量。

export AWS_ACCESS_KEY_ID=AKI***D4Q

export AWS_SECRET_ACCESS_KEY=QUC***BTI

出口AWS_DEFAULT_REGION=eu-west-3

运行该脚本。

./ec2_tags.py

[DEBUG] EC2

ID: ec2.Instance(id='i-0df2fe9ec4b5e1855')

EBS

ID: ec2.Volume(id='vol-0d11fd27f3702a0fc')

[DEBUG] EC2

ID: ec2.Instance(id='i-023529a843d02f680')

EBS

ID: ec2.Volume(id='vol-0f3548ae321cd040c')

[DEBUG] EC2

ID: ec2.Instance(id='i-02ab1438a79a3e475')

EBS

ID: ec2.Volume(id='vol-09b6f60396e56c363')

ID: ec2.Volume(id='vol-0d75c44a594e312a1')

...

很好,开始工作了!我们已经得到了eu-west-3 AWS 区域的所有ЕС2,并且每个 EC2 都得到了它的 EBS 列表。

接下来是什么?下一步是确定EC2中的卷是如何挂载的,知道它的挂载点,我们就能知道这个盘是根卷还是一些额外的数据卷。

这可以通过获取 attachments()属性来完成,该属性保留了Device 关键的值。

在脚本中设置一个新的变量,叫做device_id

...
        for vol in instance.volumes.all():
            
            vol_id = str(vol)
            device_id = "ec2.vol.Device('" + str(vol.attachments[0]['Device']) + "')"
            
            print("\t\tID:  " + vol_id + "\n\t\tDev: " + device_id + "\n")
...

再次运行该脚本。

./ec2_tags.py

[DEBUG] EC2

ID: ec2.Instance(id='i-0df2fe9ec4b5e1855')

EBS

ID: ec2.Volume(id='vol-0d11fd27f3702a0fc')

Dev: ec2.vol.Device('/dev/xvda')

[DEBUG] EC2

ID: ec2.Instance(id='i-023529a843d02f680')

EBS

ID: ec2.Volume(id='vol-0f3548ae321cd040c')

Dev: ec2.vol.Device('/dev/xvda')

[DEBUG] EC2

ID: ec2.Instance(id='i-02ab1438a79a3e475')

EBS

ID: ec2.Volume(id='vol-09b6f60396e56c363')

Dev: ec2.vol.Device('/dev/xvda')

ID: ec2.Volume(id='vol-0d75c44a594e312a1')

Dev: ec2.vol.Device('/dev/xvdbm')

...

这里我们有一个ID为i-02ab1438a79a3e475的ЕС2,这个EC2有两个EBS卷被安装 -vol-09b6f60396e56c363作为/dev/xvdavol-0d75c44a594e312a1作为/dev/xvdbm

/dev/xvda 显然是一个根卷,而 - 一些额外的数据。/dev/xvdbm

boto3:向EBS添加AWS标签

现在,让我们创建一个Role Tag,它将从以下值中保留一个。

  1. 如果一个EBS有一个带有kubernetes.io/created-for/pvc/name 关键的标签,那么将设置Role: "PvcVolume"
  2. 如果它不是一个PVC卷,那么需要检查它的挂载点,如果device == "/dev/xvda",那么设置Role: "RootVolume"
  3. 最后,如果device 变量有任何其他的值,那么只需在EBS上标上Role: "DataDisk"

为了做到这一点,让我们添加另一个函数,它将被用来设置标签,还有一个小函数叫is_pvc() ,它将检查一个EBS是否有kubernetes.io/created-for/pvc/name 标签。

...
def is_pvc(vol):

    try:
        for tag in vol.tags:
            if tag['Key'] == 'kubernetes.io/created-for/pvc/name':
                return True
                break
    except TypeError:
            return False

def set_role_tag(vol):

    device = vol.attachments[0]['Device']
    tags_list = []
    values = {}

    if is_pvc(vol):
        values['Key'] = "Role"
        values['Value'] = "PvcDisk"
        tags_list.append(values)
    elif device == "/dev/xvda":
        values['Key'] = "Role"
        values['Value'] = "RootDisk"
        tags_list.append(values)
    else:
        values['Key'] = "Role"
        values['Value'] = "DataDisk"
        tags_list.append(values)

    return tags_list
...

在这里,在set_role_tag() 函数中,我们首先将vol 的值作为参数传给is_pvc() 函数,该函数检查*'kubernetes.io/created-for/pvc/name'*键的标签。这里的try/except ,是用来知道一个EBS是否有标签。

如果找到了*'kubernetes.io/created-for/pvc/name'*标签,那么is_pvc() 将返回True ,如果没有找到,那么False

然后,在if/elif/else 条件中,我们要检查EBS是否是PVC卷,如果是,那么它将被标记为Role: "PvcVolume" ,如果不是--将检查其挂载点,如果它被挂载为"/dev/xvda",那么设置Role: "RootVolume" ,如果是--将使用Role: "DataDisk".

Добавляем вызовset_role_tag() вlambda_handler() - передаём еёргументом вvol.create_tags()

...
def lambda_handler(event, context):
            
    base = ec2.instances.all()

    for instance in base:

        print("\n[DEBUG] EC2\n\t\tID:  " + str(instance))
        print("\tEBS")
            
        for vol in instance.volumes.all():
                
            vol_id = str(vol)
            device_id = "ec2.vol.Device('" + str(vol.attachments[0]['Device']) + "')"
            print("\t\tID:  " + vol_id + "\n\t\tDev: " + device_id)

            role_tag = vol.create_tags(Tags=set_role_tag(vol))
            print("\t\tTags set:\n\t\t\t" + str(role_tag))
...

再次运行该脚本。

./ec2_tags.py

[DEBUG] EC2

ID: ec2.Instance(id='i-0df2fe9ec4b5e1855')

EBS

ID: ec2.Volume(id='vol-0d11fd27f3702a0fc')

Dev: ec2.vol.Device('/dev/xvda')

标签设置。

[ec2.Tag(resource_id='vol-0d11fd27f3702a0fc', key='Role', value='RootDisk') ]

...

[DEBUG] EC2

ID: ec2.Instance(id='i-02ab1438a79a3e475')

EBS

ID: ec2.Volume(id='vol-09b6f60396e56c363')

Dev: ec2.vol.Device('/dev/xvda')

标签设置。

[ec2.Tag(resource_id='vol-09b6f60396e56c363', key='Role', value='RootDisk') ]

ID: ec2.Volume(id='vol-0d75c44a594e312a1')

Dev: ec2.vol.Device('/dev/xvdbm')

标签设置。

[ec2.Tag(resource_id='vol-0d75c44a594e312a1', key='Role', value='PvcDisk') ]

让我们检查i-02ab1438a79a3e47EC2实例的卷。

它的根卷vol-09b6f60396e56c363

还有一个Kubernetes PVC -vol-0d75c44a594e312a1

很好,我们已经添加了Role 标签的创建,现在需要添加将AWS标签从EC2复制到其EBS的能力。

boto3: 从EC2复制AWS标签到其EBS

标签复制也可以移到一个专门的函数中,让我们把它命名为copy_ec2_tags() ,它将接受一个参数,在这里我们将传递一个EC2 ID。

...
def copy_ec2_tags(instance):
            
    tags_list = []
    values = {} 
        
    for instance_tag in instance.tags:

        if instance_tag['Key'] == 'Env':
            tags_list.append(instance_tag)
        elif instance_tag['Key'] == 'Tier':
            tags_list.append(instance_tag)
        elif instance_tag['Key'] == 'DataClass':
            tags_list.append(instance_tag)

    return tags_list
...

在这个函数中,在一个循环中,我们正在检查实例的所有标签,如果找到我们函数中指定的三个标签中的任何一个,它们将被附加到列表tags_list[] ,之后将被传递给vol.create_tags()

copy_ec2_tags() 执行添加到lambda_handler()

...
def lambda_handler(event, context):

    base = ec2.instances.all()

    for instance in base:

        print("\n[DEBUG] EC2\n\t\tID:  " + str(instance))
        print("\tEBS")

        for vol in instance.volumes.all():

            vol_id = str(vol)
            device_id = "ec2.vol.Device('" + str(vol.attachments[0]['Device']) + "')"
            print("\t\tID:  " + vol_id + "\n\t\tDev: " + device_id)

            role_tag = vol.create_tags(Tags=set_role_tag(vol))
            ec2_tags = vol.create_tags(Tags=copy_ec2_tags(instance))
            print("\t\tTags set:\n\t\t\t" + str(role_tag) + "\n\t\t\t" + str(ec2_tags))
...

运行。

./ec2_tags.py

[DEBUG] EC2

ID: ec2.Instance(id='i-0df2fe9ec4b5e1855')

EBS

ID: ec2.Volume(id='vol-0d11fd27f3702a0fc')

Dev: ec2.vol.Device('/dev/xvda')

标签设置。

[ec2.Tag(resource_id='vol-0d11fd27f3702a0fc', key='Role', value='RootDisk') ]

[ec2.Tag(resource_id='vol-0d11fd27f3702a0fc', key='DataClass', value='Public'), ec2.Tag(resource_id='vol-0d11fd27f3702a0fc', key='Env', value='Dev'), ec2.Tag(resource_id='vol-0d11fd27f3702a0fc', key='Tier', value='Devops')

...

[DEBUG] EC2

ID: ec2.Instance(id='i-02ab1438a79a3e475')

EBS

ID: ec2.Volume(id='vol-09b6f60396e56c363')

Dev: ec2.vol.Device('/dev/xvda')

标签设置。

[ec2.Tag(resource_id='vol-09b6f60396e56c363', key='Role', value='RootDisk') ]

[ec2.Tag(resource_id='vol-09b6f60396e56c363', key='Env', value='Dev'), ec2.Tag( resource_id='vol-09b6f60396e56c363', key='DataClass', value='Public'), ec2.Tag( resource_id='vol-09b6f60396e56c363', key='Tier', value='Devops') ]

ID: ec2.Volume(id='vol-0d75c44a594e312a1')

Dev: ec2.vol.Device('/dev/xvdbm')

标签设置。

[ec2.Tag(resource_id='vol-0d75c44a594e312a1', key='Role', value='PvcDisk') ]

[ec2.Tag(resource_id='vol-0d75c44a594e312a1', key='Env', value='Dev'), ec2.Tag( resource_id='vol-0d75c44a594e312a1', key='DataClass', value='Public'), ec2.Tag( resource_id='vol-0d75c44a594e312a1', key='Tier', value='Devops') ]

...

然后检查。

整个脚本现在看起来像下图:。

#!/usr/bin/env python

import os
import boto3

ec2 = boto3.resource('ec2',
        region_name=os.getenv("AWS_DEFAULT_REGION"),
        aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
        aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY")
    )

def lambda_handler(event, context):

    base = ec2.instances.all()

    for instance in base:

        print("[DEBUG] EC2\n\t\tID:  " + str(instance))
        print("\tEBS")

        for vol in instance.volumes.all():

            vol_id = str(vol)
            device_id = "ec2.vol.Device('" + str(vol.attachments[0]['Device']) + "')"
            print("\t\tID:  " + vol_id + "\n\t\tDev: " + device_id)

            role_tag = vol.create_tags(Tags=set_role_tag(vol))
            ec2_tags = vol.create_tags(Tags=copy_ec2_tags(instance))
            print("\t\tTags set:\n\t\t\t" + str(role_tag) + "\n\t\t\t" + str(ec2_tags) + "\n")

def is_pvc(vol): 

    try:
        for tag in vol.tags:
            if tag['Key'] == 'kubernetes.io/created-for/pvc/name':
                return True
                break
    except TypeError:
            return False

def set_role_tag(vol):
    
    device = vol.attachments[0]['Device']
    tags_list = []
    values = {}
        
    if is_pvc(vol):
        values['Key'] = "Role"
        values['Value'] = "PvcDisk"
        tags_list.append(values)
    elif device == "/dev/xvda":
        values['Key'] = "Role"
        values['Value'] = "RootDisk"
        tags_list.append(values)
    else:   
        values['Key'] = "Role"
        values['Value'] = "DataDisk"
        tags_list.append(values)

    return tags_list

def copy_ec2_tags(instance):
            
    tags_list = []
    values = {} 
        
    for instance_tag in instance.tags:

        if instance_tag['Key'] == 'Env':
            tags_list.append(instance_tag)
        elif instance_tag['Key'] == 'Tier':
            tags_list.append(instance_tag)
        elif instance_tag['Key'] == 'DataClass':
            tags_list.append(instance_tag)
        elif instance_tag['Key'] == 'JiraTicket':
            tags_list.append(instance_tag)

    return tags_list

if __name__ == "__main__":
    lambda_handler(0, 0)

我们在这里就完成了,现在可以进行AWS Lambda函数了。请看AWS中的下一部分。Lambda--将EC2标签复制到其EBS,第二部分--创建Lambda函数一文。

类似的帖子

The postAWS:Lambda - copy EC2 tags to its EBS, part 1 - Python and boto3first appeared onRTFM: Linux, DevOps, and system administration.