【问题标题】:Using AWS EFS with Docker将 AWS EFS 与 Docker 结合使用
【发布时间】:2016-11-05 22:43:19
【问题描述】:

我在我的单容器 EB 部署中使用亚马逊提供的新弹性文件系统。我不知道为什么挂载的 EFS 不能映射到容器中。

在 /efs-mount-point 的主机上成功执行 EFS 挂载。

提供给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 我正在使用 Elastic Beanstalk (EB),所以我的容器在 EC2 上。
  • 你检查了吗this project 我从未尝试过,但似乎可以支持

标签: amazon-web-services docker nfs


【解决方案1】:

在主机 EC2 实例中挂载 EFS 卷后,您需要 restart 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"

【讨论】:

  • 今天还需要这种精神错乱吗?码头反弹?
【解决方案2】:

AWS 提供了在弹性 beanstalk 上自动创建和挂载 EFS 的说明。他们可以找到here

这些说明链接到两个要自定义的配置文件,并放置在部署包的 .ebextensions 文件夹中。

  1. storage-efs-createfilesystem.config
  2. storage-efs-mountfilesystem.config

需要进一步修改文件 storage-efs-mountfilesystem.config 以使用 Docker 容器。添加以下命令:

02_restart:
  command: "service docker restart"

对于多容器环境,Elastic Container Service 也必须重启(在上面重启 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 守护程序是在运行 .ebextensions 中的命令之前由 EC2 实例启动的。启动顺序为:

  1. 启动 docker 守护进程
  2. 在多容器 docker 环境中启动 Elastic Container Service Agent
  3. 在 .ebextensions 中运行命令
  4. 运行容器应用

在第一步,docker 守护进程提供给容器的文件系统视图是固定的。因此,在第 3 步中对主机文件系统所做的更改不会反映在容器的视图中。

一个奇怪的效果是容器在文件系统被挂载到主机之前看到一个挂载点。主机看到挂载的文件系统。因此,容器写入的文件将写入挂载目录下的主机目录,而不是挂载的文件系统。卸载 EC2 主机上的文件系统将暴露写入挂载目录的容器文件。

【讨论】:

    【解决方案3】:

    EFS 与 AWS Beanstalk - 多容器 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}
    

    资源:

    【讨论】:

    • 首先,谢谢。其次,哇,这是一个疯狂的老鼠夹。谈论糟糕的用户体验。
    • 今天还需要这种诡计多端的扑克吗?我有一个平稳运行的 ec2 扩展了我们自己推出的一组技巧,看起来我没有动力迁移到弹性 beanstalk 或 aws 自动缩放
    猜你喜欢
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-30
    • 2018-02-13
    • 2017-02-17
    • 2021-01-13
    • 2016-07-13
    相关资源
    最近更新 更多