使用 AWS EFS 和 Docker

13

我正在使用亚马逊提供的新弹性文件系统,在我的单容器EB部署中。我无法理解为什么挂载的EFS无法映射到容器中。

EFS挂载在主机上成功完成,路径为/efs-mount-point。

Dockerrun.aws.json中提供了相应的信息。

{
  "AWSEBDockerrunVersion": "1"
  "Volumes": [
    {
      "HostDirectory": "/efs-mount-point",
      "ContainerDirectory": "/efs-mount-point"
    }
  ]
}
容器启动后会在其中创建卷。但它已经映射了主机目录 /efs-mount-point,而不是实际的 EFS 挂载点。我无法弄清楚如何让 Docker 映射到挂载在 /efs-mount-point 上的 EFS 卷,而不是主机的目录。
NFS 卷是否与 Docker 兼容?

你的Docker容器托管在哪里?在EC2实例上吗?使用ECS?还是其他编排工具? - Olivier
@Olivier 我正在使用弹性 Beanstalk(EB),因此我的容器位于 EC2 上。 - user1658296
你有没有检查过这个项目(https://github.com/ContainX/docker-volume-netshare)?我从未尝试过,但似乎可以支持。 - Frederic Henri
3个回答

12

在将EFS卷挂载到主机EC2实例后,您需要重新启动docker

以下是一个示例:.ebextensions/efs.config

commands:
   01mkdir:
      command: "mkdir -p /efs-mount-point"
   02mount:
      command: "mountpoint -q /efs-mount-point || mount -t nfs4 -o nfsvers=4.1 $(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone).fs-fa35c253.efs.us-west-2.amazonaws.com:/ /efs-mount-point"
   03restart:
      command: "service docker restart"

今天还需要这种疯狂吗?Docker重启? - Scott Stensland

7
AWS有自动在弹性豆腐上创建和挂载EFS的说明。 它们可以在此处找到:这里 这些说明链接到两个配置文件,需要定制并放置在您的部署包的.ebextensions文件夹中。
1. storage-efs-createfilesystem.config 2. storage-efs-mountfilesystem.config 文件storage-efs-mountfilesystem.config需要进一步修改才能与Docker容器一起使用。 添加以下命令:
02_restart:
  command: "service docker restart"

对于多容器环境,弹性容器服务也必须重新启动(在上面重启docker时已被杀死):
03_start_eb:
  command: |
      start ecs
      start eb-docker-events
      sleep 120
  test: sh -c "[ -f /etc/init/ecs.conf ]"

因此,storage-efs-mountfilesystem.config 的完整 commands 部分如下:

commands:
  01_mount:
    command: "/tmp/mount-efs.sh"
  02_restart:
    command: "service docker restart"
  03_start_eb:
    command: |
        start ecs
        start eb-docker-events
        sleep 120
    test: sh -c "[ -f /etc/init/ecs.conf ]"

这种方法不能“开箱即用”的原因是,docker守护进程在EC2实例上启动时会先于.ebextensions中的命令运行。启动顺序如下:
  1. 启动docker守护进程
  2. 在多容器docker环境中启动弹性容器服务代理
  3. 运行.ebextensions中的命令
  4. 运行容器应用程序
第一步中,docker守护进程提供给容器的文件系统视图是固定的。因此,在第3步期间对主机文件系统所做的更改不会反映在容器的视图中。
一个奇怪的效果是,容器在文件系统被挂载到主机之前就能看到挂载点。主机可以看到已经挂载的文件系统。因此,容器写入的文件将被写入挂载目录下的主机目录中,而不是挂载的文件系统中。在EC2主机上卸载文件系统将公开写入挂载目录中的容器文件。

5
EFS和AWS Beanstalk - Multicontainer Docker将可以一起使用。但是,由于您必须在安装EFS后重新启动docker,因此许多内容将停止工作。
实例命令:
搜索后,您可能会发现需要在挂载EFS后执行“docker restart”。但事情并不简单。当自动缩放发生或部署应用程序的新版本时,您将遇到麻烦。 下面是我用于将EFS挂载到docker实例的脚本,需要按照以下步骤进行:
1. 停止 ECS 管理器。需要 60 秒。
2. 停止 Docker 服务。
3. 杀死剩余的 Docker 进程。
4. 删除先前的网络绑定。请参见:https://github.com/docker/docker/issues/7856#issuecomment-239100381 5. 挂载 EFS。
6. 启动 Docker 服务。
7. 启动 ECS 服务。
8. 等待 120 秒。确保 ECS 处于正确的 start/* 状态。否则,例如 00enact 脚本将失败。请注意,此显示是强制性的,并且很难找到任何有关其文档的信息。
以下是我的脚本:
以.ebextensions/commands.config为例:
commands:
  01stopdocker:
    command: "sudo stop ecs  > /dev/null 2>&1 || /bin/true && sudo service docker stop"
  02killallnetworkbindings:
    command: 'sudo killall docker  > /dev/null 2>&1 || /bin/true'
  03removenetworkinterface:
    command: "rm -f /var/lib/docker/network/files/local-kv.db"
    test: test -f /var/lib/docker/network/files/local-kv.db
  # Mount the EFS created in .ebextensions/media.config
  04mount:
    command: "/tmp/mount-efs.sh"
  # On new instances, delay needs to be added because of 00task enact script. It tests for start/ but it can be various states of start...
  # Basically, "start ecs" takes some time to run, and it runs async - so we sleep for some time.
  # So basically let the ECS manager take it's time to boot before going on to enact scritps and post deploy scripts.
  09restart:
    command: "service docker start && sudo start ecs && sleep 120s"

挂载脚本和环境变量

.ebextensions/mount-config.config

# efs-mount.config
# Copy this file to the .ebextensions folder in the root of your app source folder
option_settings:
  aws:elasticbeanstalk:application:environment:
    EFS_REGION: '`{"Ref": "AWS::Region"}`'
    # Replace with the required mount directory
    EFS_MOUNT_DIR: '/efs_volume'
    # Use in conjunction with efs_volume.config or replace with EFS volume ID of an existing EFS volume
    EFS_VOLUME_ID: '`{"Ref" : "FileSystem"}`'

packages:
  yum:
    nfs-utils: []
files:
  "/tmp/mount-efs.sh":
      mode: "000755"
      content : |
        #!/bin/bash

        EFS_REGION=$(/opt/elasticbeanstalk/bin/get-config environment | jq -r '.EFS_REGION')
        EFS_MOUNT_DIR=$(/opt/elasticbeanstalk/bin/get-config environment | jq -r '.EFS_MOUNT_DIR')
        EFS_VOLUME_ID=$(/opt/elasticbeanstalk/bin/get-config environment | jq -r '.EFS_VOLUME_ID')

        echo "Mounting EFS filesystem ${EFS_DNS_NAME} to directory ${EFS_MOUNT_DIR} ..."

        echo 'Stopping NFS ID Mapper...'
        service rpcidmapd status &> /dev/null
        if [ $? -ne 0 ] ; then
            echo 'rpc.idmapd is already stopped!'
        else
            service rpcidmapd stop
            if [ $? -ne 0 ] ; then
                echo 'ERROR: Failed to stop NFS ID Mapper!'
                exit 1
            fi
        fi

        echo 'Checking if EFS mount directory exists...'
        if [ ! -d ${EFS_MOUNT_DIR} ]; then
            echo "Creating directory ${EFS_MOUNT_DIR} ..."
            mkdir -p ${EFS_MOUNT_DIR}
            if [ $? -ne 0 ]; then
                echo 'ERROR: Directory creation failed!'
                exit 1
            fi
            chmod 777 ${EFS_MOUNT_DIR}
            if [ $? -ne 0 ]; then
                echo 'ERROR: Permission update failed!'
                exit 1
            fi
        else
            echo "Directory ${EFS_MOUNT_DIR} already exists!"
        fi

        mountpoint -q ${EFS_MOUNT_DIR}
        if [ $? -ne 0 ]; then
            AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
            echo "mount -t nfs4 -o nfsvers=4.1 ${AZ}.${EFS_VOLUME_ID}.efs.${EFS_REGION}.amazonaws.com:/ ${EFS_MOUNT_DIR}"
            mount -t nfs4 -o nfsvers=4.1 ${AZ}.${EFS_VOLUME_ID}.efs.${EFS_REGION}.amazonaws.com:/ ${EFS_MOUNT_DIR}
            if [ $? -ne 0 ] ; then
                echo 'ERROR: Mount command failed!'
                exit 1
            fi
        else
            echo "Directory ${EFS_MOUNT_DIR} is already a valid mountpoint!"
        fi

        echo 'EFS mount complete.'

资源和配置

您需要更改以下option_settings选项。要在下面的option_settings中定义必须的VPC和子网,请在AWS Web控制台下找到VPC,您必须找到默认VPC ID和3个默认子网ID。如果您的Beanstalk使用自定义VPC,则必须使用这些设置。

.ebextensions/efs-volume.config

# efs-volume.config
# Copy this file to the .ebextensions folder in the root of your app source folder
option_settings:
  aws:elasticbeanstalk:customoption: 
    EFSVolumeName: "EB-EFS-Volume"
    VPCId: "vpc-xxxxxxxx"
    SubnetUSWest2a: "subnet-xxxxxxxx"
    SubnetUSWest2b: "subnet-xxxxxxxx"
    SubnetUSWest2c: "subnet-xxxxxxxx"

Resources:
  FileSystem:
    Type: AWS::EFS::FileSystem
    Properties:
      FileSystemTags:
      - Key: Name
        Value:
          Fn::GetOptionSetting: {OptionName: EFSVolumeName, DefaultValue: "EB_EFS_Volume"}
  MountTargetSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for mount target
      SecurityGroupIngress:
      - FromPort: '2049'
        IpProtocol: tcp
        SourceSecurityGroupId:
          Fn::GetAtt: [AWSEBSecurityGroup, GroupId]
        ToPort: '2049'
      VpcId:
        Fn::GetOptionSetting: {OptionName: VPCId}
  MountTargetUSWest2a:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: {Ref: FileSystem}
      SecurityGroups:
      - {Ref: MountTargetSecurityGroup}
      SubnetId:
        Fn::GetOptionSetting: {OptionName: SubnetUSWest2a}
  MountTargetUSWest2b:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: {Ref: FileSystem}
      SecurityGroups:
      - {Ref: MountTargetSecurityGroup}
      SubnetId:
        Fn::GetOptionSetting: {OptionName: SubnetUSWest2b}
  MountTargetUSWest2c:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: {Ref: FileSystem}
      SecurityGroups:
      - {Ref: MountTargetSecurityGroup}
      SubnetId:
        Fn::GetOptionSetting: {OptionName: SubnetUSWest2c}

资源:


1
首先,谢谢。其次,哇,这是个疯狂的陷阱。谈论一下糟糕的用户体验。 - JonMR
这种玩意儿今天还需要吗?我有一个运行顺畅的 EC2 扩展设置,我们自己开发了一些技巧,看起来我没有迁移到 Elastic Beanstalk 或 AWS Autoscaling 的动力。 - Scott Stensland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接