配置PolarFS文件系统(内测中)

为了满足Sandbox内大规模代码编译、高频读写海量小文件或运行I/O密集型数据分析任务等特定需求,函数计算引入阿里云自研的PolarFS分布式文件系统。PolarFS基于RDMA、NVMe以及用户态I/O栈等核心技术,突破传统存储架构的内核瓶颈,实现了微秒级访问延迟,为这类负载提供更优的I/O路径。

使用说明

  • 挂载PolarFS文件系统功能目前处于内测阶段,如需试用请联系我们

  • 挂载PolarFS文件系统功能目前仅支持在中国香港地域下使用。

  • 目前仅支持挂载基础版 PolarFS。

  • 函数计算在同一地域下的同一个函数最多支持配置5PolarFS文件系统。

前提条件

  • 函数计算

    已创建函数并为函数配置VPC网络访问能力。具体操作,请参见创建函数配置网络

    说明

    目前只支持在私有的VPC环境内添加PolarFS文件系统,因此,在配置网络时需设置允许函数访问VPC内资源,并配置正确的VPC,才能访问指定的PolarFS。

  • PolarFS

    已创建PolarFS基础版实例。请确保PolarFS绑定的VPC网络和交换机与函数绑定的资源一致。

挂载PolarFS文件系统

  1. 登录函数计算控制台,在左侧导航栏,选择函数管理 > 函数列表

  2. 在顶部菜单栏,选择地域,然后在函数列表页面,单击目标函数。

  3. 在函数详情页面,选择配置页签,单击高级配置右侧的编辑,在高级配置面板,找到存储选项,启用挂载PolarFS文件系统开关,按照以下操作配置完成后单击部署

    配置项名称

    说明

    示例

    用户用户组

    设置文件的拥有者和相应的组,取值范围为[0, 65534],如果不填写,默认值均为0,即分别表示root用户和root用户组。

    • 用户:10002

    • 用户组:10002

    PolarFS 文件系统

    选择已创建的PolarFS文件系统,如需创建新的PolarFS文件系统,单击下方的创建新的文件系统,前往PolarFS控制台创建。

    挂载PolarFS文件系统的本质是创建一个从函数计算实例的本地目录到PolarFS远端目录的映射关系。请按照下方说明设置远端目录函数本地目录

    PolarFS实例

    选择已有PolarFS实例。只能选择与函数网络配置中同一VPC网络和交换机绑定的PolarFS实例。

    pfs-j6c160gg****

    远端目录

    远端目录是指位于PolarFS文件系统中的目录,若您配置的目录在远端NAS中不存在,函数计算将会为您自动创建该目录。目录所有者为上述配置的用户用户组,权限等级为777

    PolarFS 文件系统一:

    • /

    • /mnt/root

    PolarFS 文件系统二:

    • /non_root_python

    • /polarfs

    函数本地目录

    函数本地目录是指函数实例中的目录,建议使用/home/ 、/mnt/ 或 /data/ 的子目录。不能使用通用的LinuxUnix系统目录及其子目录,例如/bin/opt/var/dev等。

验证结果

1.准备访问PolarFS的函数代码

在函数详情页面,单击代码页签,在代码编辑器中编写代码,然后单击部署代码

# -*- coding: utf-8 -*-
import logging
import json
import subprocess
import os
import stat
import pwd

def handler(event, context):
    # evt = json.loads(event)
    logger = logging.getLogger()
    logger.info('hello world')
    
    # 1. 使用 ls -l 命令列出 /mnt/root 下的目录
    logger.info('=== 步骤1: 列出 /mnt/root 下的目录 ===')
    try:
        result = subprocess.run(['ls', '-l', '/mnt/root'], capture_output=True, text=True)
        if result.returncode == 0:
            logger.info(f'/mnt/root 目录内容:\n{result.stdout}')
        else:
            logger.error(f'执行 ls -l /mnt/root 失败: {result.stderr}')
    except Exception as e:
        logger.error(f'执行 ls -l 命令时发生错误: {str(e)}')
    
    # 2. 校验 /mnt/root/non_root_python 是一个目录,uid 和 gid 均为 10002,mode 为 777
    logger.info('=== 步骤2: 校验 /mnt/root/non_root_python ===')
    root_dir_path = '/mnt/root/non_root_python'
    try:
        if os.path.exists(root_dir_path) and os.path.isdir(root_dir_path):
            logger.info(f'{root_dir_path} 存在且为目录')
            
            stat_info = os.stat(root_dir_path)
            uid = stat_info.st_uid
            gid = stat_info.st_gid
            mode = stat.S_IMODE(stat_info.st_mode)
          
            logger.info(f'{root_dir_path} - UID: {uid}, GID: {gid}, Mode: {oct(mode)}')
                                
            if uid == 10002 and gid == 10002:
                logger.info('UID 和 GID 都是 10002')
            else:
                logger.warning(f'UID 或 GID 不是 10002: UID={uid}, GID={gid}')
            
            if mode == 0o777:
                logger.info('目录权限为 777')
            else:
                logger.warning(f'目录权限不是 777: {oct(mode)}')
        else:
            logger.error(f'{root_dir_path} 不存在或不是目录')
    except Exception as e:
        logger.error(f'校验 {root_dir_path} 时发生错误: {str(e)}')
    
    # 3. 校验主进程的 uid 和 gid 均为 10002
    logger.info('=== 步骤3: 校验主进程的 UID 和 GID ===')
    try:
        current_uid = os.getuid()
        current_gid = os.getgid()
        logger.info(f'当前进程 UID: {current_uid}, GID: {current_gid}')
        
        if current_uid == 10002 and current_gid == 10002:
            logger.info('主进程 UID 和 GID 都是 10002')
        else:
            logger.warning(f'主进程 UID 或 GID 不是 10002: UID={current_uid}, GID={current_gid}')
    except Exception as e:
        logger.error(f'校验主进程 UID/GID 时发生错误: {str(e)}')
    
    # 4. 在 /mnt/subdir 下创建子目录和子文件后,在 /mnt/root/non_root_python 中能看到
    logger.info('=== 步骤4: 以用户 10002 身份创建文件和目录 ===')
    subdir_path = '/mnt/subdir'
    try:
        # 确保 /mnt/subdir 目录存在
        os.makedirs(subdir_path, exist_ok=True)
        
        # 创建测试文件和目录
        test_file_path = os.path.join(subdir_path, 'user10002_test_file.txt')
        test_dir_path = os.path.join(subdir_path, 'user10002_test_dir')
        
        with open(test_file_path, 'w') as f:
            f.write('test content as user 10002')
        
        os.makedirs(test_dir_path, exist_ok=True)
        
        logger.info('以用户 10002 身份创建测试文件和目录成功')
        
        # 检查在 /mnt/root/non_root_python 中是否能看到
        root_file_path = os.path.join(root_dir_path, 'user10002_test_file.txt')
        root_dir_check_path = os.path.join(root_dir_path, 'user10002_test_dir')
        
        if os.path.exists(root_file_path):
            logger.info('在 /mnt/root/non_root_python 中能看到创建的文件')
            file_stat = os.stat(root_file_path)
            file_uid = file_stat.st_uid
            file_gid = file_stat.st_gid
            file_mode = stat.S_IMODE(file_stat.st_mode)
            
            logger.info(f'文件 - UID: {file_uid}, GID: {file_gid}, Mode: {oct(file_mode)}')
            
            if file_uid == 10002 and file_gid == 10002:
                logger.info('文件 UID 和 GID 都是 10002')
            else:
                logger.warning(f'文件 UID 或 GID 不是 10002: UID={file_uid}, GID={file_gid}')
            
            if file_mode == 0o644:
                logger.info('文件权限为 644')
            else:
                logger.warning(f'文件权限不是 644: {oct(file_mode)}')
        else:
            logger.warning('在 /mnt/root/non_root_python 中看不到创建的文件')
        
        if os.path.exists(root_dir_check_path):
            logger.info('在 /mnt/root/non_root_python 中能看到创建的目录')
            dir_stat = os.stat(root_dir_check_path)
            dir_uid = dir_stat.st_uid
            dir_gid = dir_stat.st_gid
            dir_mode = stat.S_IMODE(dir_stat.st_mode)
            
            logger.info(f'目录 - UID: {dir_uid}, GID: {dir_gid}, Mode: {oct(dir_mode)}')
            
            if dir_uid == 10002 and dir_gid == 10002:
                logger.info('目录 UID 和 GID 都是 10002')
            else:
                logger.warning(f'目录 UID 或 GID 不是 10002: UID={dir_uid}, GID={dir_gid}')
            
            if dir_mode == 0o755:
                logger.info('目录权限为 755')
            else:
                logger.warning(f'目录权限不是 755: {oct(dir_mode)}')
        else:
            logger.warning('在 /mnt/root/non_root_python 中看不到创建的目录')
            
    except Exception as e:
        logger.error(f'以用户 10002 身份创建文件/目录时发生错误: {str(e)}')
    
    # 5. 将主进程的 gid 切换为 10001
    logger.info('=== 步骤5: 将主进程的 GID 切换为 10001 ===')
    try:
        os.setgid(10001)
        logger.info('成功设置进程 GID 为 10001')
        
        new_gid = os.getgid()
        logger.info(f'设置后进程 GID: {new_gid}')
    except PermissionError:
        logger.warning('权限不足,无法设置 GID 为 10001')
    except OSError as e:
        logger.error(f'设置 GID 时发生系统错误: {str(e)}')
    except Exception as e:
        logger.error(f'设置进程 GID 时发生错误: {str(e)}')
    
    # 6. 将主进程的 uid 切换为 10001
    logger.info('=== 步骤6: 将主进程的 UID 切换为 10001 ===')
    try:
        os.setuid(10001)
        logger.info('成功设置进程 UID 为 10001')
        
        new_uid = os.getuid()
        logger.info(f'设置后进程 UID: {new_uid}')
    except PermissionError:
        logger.warning('权限不足,无法设置 UID 为 10001')
    except OSError as e:
        logger.error(f'设置 UID 时发生系统错误: {str(e)}')
    except Exception as e:
        logger.error(f'设置进程 UID 时发生错误: {str(e)}')
    
    # 7. 在 /mnt/subdir 下创建子目录和子文件后,在 /mnt/root/non_root_python 中能看到
    logger.info('=== 步骤7: 以用户 10001 身份创建文件和目录 ===')
    try:
        # 创建测试文件和目录
        test_file_path = os.path.join(subdir_path, 'user10001_test_file.txt')
        test_dir_path = os.path.join(subdir_path, 'user10001_test_dir')
        
        with open(test_file_path, 'w') as f:
            f.write('test content as user 10001')
        
        os.makedirs(test_dir_path, exist_ok=True)
        
        logger.info('以用户 10001 身份创建测试文件和目录成功')
        
        # 检查在 /mnt/root/non_root_python 中是否能看到
        root_file_path = os.path.join(root_dir_path, 'user10001_test_file.txt')
        root_dir_check_path = os.path.join(root_dir_path, 'user10001_test_dir')
        
        if os.path.exists(root_file_path):
            logger.info('在 /mnt/root/non_root_python 中能看到创建的文件')
            file_stat = os.stat(root_file_path)
            file_uid = file_stat.st_uid
            file_gid = file_stat.st_gid
            file_mode = stat.S_IMODE(file_stat.st_mode)
            
            logger.info(f'文件 - UID: {file_uid}, GID: {file_gid}, Mode: {oct(file_mode)}')
            
            if file_uid == 10002 and file_gid == 10002:
                logger.info('文件 UID 和 GID 都是 10002')
            else:
                logger.warning(f'文件 UID 或 GID 不是 10002: UID={file_uid}, GID={file_gid}')
            
            if file_mode == 0o644:
                logger.info('文件权限为 644')
            else:
                logger.warning(f'文件权限不是 644: {oct(file_mode)}')
        else:
            logger.warning('在 /mnt/root/non_root_python 中看不到创建的文件')
        
        if os.path.exists(root_dir_check_path):
            logger.info('在 /mnt/root/non_root_python 中能看到创建的目录')
            dir_stat = os.stat(root_dir_check_path)
            dir_uid = dir_stat.st_uid
            dir_gid = dir_stat.st_gid
            dir_mode = stat.S_IMODE(dir_stat.st_mode)
            
            logger.info(f'目录 - UID: {dir_uid}, GID: {dir_gid}, Mode: {oct(dir_mode)}')
            
            if dir_uid == 10002 and dir_gid == 10002:
                logger.info('目录 UID 和 GID 都是 10002')
            else:
                logger.warning(f'目录 UID 或 GID 不是 10002: UID={dir_uid}, GID={dir_gid}')
            
            if dir_mode == 0o755:
                logger.info('目录权限为 755')
            else:
                logger.warning(f'目录权限不是 755: {oct(dir_mode)}')
        else:
            logger.warning('在 /mnt/root/non_root_python 中看不到创建的目录')
            
    except Exception as e:
        logger.error(f'以用户 10001 身份创建文件/目录时发生错误: {str(e)}')
    
    return 'hello world'

本文以Python事件函数代码为例,在代码中实现对PolarFS文件系统目录的读取,以及目录所有者和权限查询,并尝试切换用户。

2.查看结果

代码部署成功后,单击代码页签的测试函数。执行成功后,您可以选择代码 > 日志输出查看执行结果。

输出示例如下:

FunctionCompute python3 runtime inited.
FC Invoke Start RequestId: 1-68e9ecf8-14a2ddf2-fed64814f73a
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] hello world
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤1: 列出 /mnt/root 下的目录 ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] /mnt/root 目录内容:
total 32
drwxrwxrwx 2        99 nogroup 4096 Sep 23 12:36 10000_dir
drwxrwxrwx 2 user10001   10001 4096 Sep 24 01:37 10001_dir
drwxrwxrwx 4 user10002   10002 4096 Sep 25 02:46 non_root_image
drwxrwxrwx 4 user10002   10002 4096 Sep 25 02:30 non_root_python
drwxrwxrwx 4 root      root    4096 Sep 25 02:24 root_dir
drwxrwxrwx 4 root      root    4096 Sep 25 02:25 root_image
drwxrwxrwx 2        99 nogroup 4096 Sep 23 11:54 share
drwxrwxrwx 2 nobody    nogroup 4096 Sep 23 12:11 testdir

2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤2: 校验 /mnt/root/non_root_python ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] /mnt/root/non_root_python 存在且为目录
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] /mnt/root/non_root_python - UID: 10002, GID: 10002, Mode: 0o777
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] UID 和 GID 都是 10002
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] 目录权限为 777
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤3: 校验主进程的 UID 和 GID ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] 当前进程 UID: 10002, GID: 10002
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] 主进程 UID 和 GID 都是 10002
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤4: 以用户 10002 身份创建文件和目录 ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [ERROR] 以用户 10002 身份创建文件/目录时发生错误: [Errno 13] Permission denied: '/mnt/subdir'
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤5: 将主进程的 GID 切换为 10001 ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [WARNING] 权限不足,无法设置 GID 为 10001
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤6: 将主进程的 UID 切换为 10001 ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [WARNING] 权限不足,无法设置 UID 为 10001
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [INFO] === 步骤7: 以用户 10001 身份创建文件和目录 ===
2025-10-11 13:37:06 1-68e9ecf8-14a2ddf2-fed64814f73a [ERROR] 以用户 10001 身份创建文件/目录时发生错误: [Errno 2] No such file or directory: '/mnt/subdir/user10001_test_file.txt'
FC Invoke End RequestId: 1-68e9ecf8-14a2ddf2-fed64814f73a

结果分析:

  • 根据步骤1执行结果,确认函数已成功读取PolarFS文件系统下/mnt/root目录内容。

  • 根据步骤2~4执行结果,确认/mnt/root目录的拥有者为挂载PolarFS文件系统时设置的用户,以及权限为777。

  • 根据步骤5~7执行结果,确认切换用户身份的局限性,在实际多租户场景中,只能通过会话隔离与存储粘性实现安全隔离,而非进程级UID切换。

相关文档

  • 函数计算支持的存储类型包括PolarFS、文件存储NAS、对象存储OSS、临时硬盘和层,如果您希望了解这些存储类型的适用场景及差异,请参见函数存储选型

  • 如果您需要存储大量图片、视频和文档等非结构化数据,建议您挂载OSS对象存储系统来实现。更多信息,请参见配置OSS对象存储

  • 您还可以使用Serverless Devs为函数挂载PolarFS文件系统。具体操作,请参见Serverless Devs常用命令