AWS: EFS – Elastic File System

Автор: | 01/02/2017
 

Amazon Elastic File System (AWS EFS) представляет  собой гибкое хранилище данных для Amazon EC2.

EFS автоматически меняет размер хранилища, когда вы добавляете и/или удаляете данные.

EFS поддерживает протокол NFS v4.1 (Network File System), и предоставляет возможность одновременного доступа нескольким интсансам EC2.

Оплата EFS зависит только от размера хранимых в неё данных. Подробнее о ценах – тут>>>.

EFS разработан для поддержания высокой скорости доступа к данным, и может менять IOPS в зависимости от размера файловой системы и количества обращений к ней.

  • вы можете одновременно смонтировать EFS-шару к интансам только в одной VPC (но при этом – к любому кол-ву машин)
  • файловая система и VPC должны находиться в одном и том же регионе

Для доступа к EFS – вы должны создать т.н. mount target в VPC, после чего она монтируется к серверу.

Кроме того – вы можете смонтировать EFS к вашим собственным серверам в другом датацентре, если они подключены к VPC  с помощью AWS Direct Connect.

Подробнее об EFSв документации>>>.

Создание окружения

Далее мы создадим 2 EC2 интанса в разных подсетях одной VPC, добавим NFS-шару, и запустим Docker-контейнеры, которые будут использовать данные из этой шары – простой NGINX и тестовый документ.

Создание VPC

Создаём VPC:

$ aws ec2 create-vpc --cidr-block 11.0.0.0/16
{
    "Vpc": {
        "VpcId": "vpc-eb38138f",
        "State": "pending",
        "CidrBlock": "11.0.0.0/16",
        "DhcpOptionsId": "dopt-8645a9e3",
        "Tags": [],
        "InstanceTenancy": "default",
        "IsDefault": false,
        "Ipv6CidrBlockAssociationSet": []
    }
}

Добавляем теги:

$ aws ec2 create-tags --resources vpc-eb38138f --tags Key=Name,Value=rtfm_efs

Для работы EFS требуется включить DNS host names (хотя можно монтировать через IP).

Обновлем атрибуты VPC:

$ aws ec2 modify-vpc-attribute --vpc-id vpc-eb38138f --enable-dns-support "{\"Value\":true}"
$ aws ec2 modify-vpc-attribute --vpc-id vpc-eb38138f --enable-dns-hostnames "{\"Value\":true}"

Подсети

Создаём две посети:

$ aws ec2 create-subnet --vpc-id vpc-eb38138f --cidr-block 11.0.1.0/24
{
    "Subnet": {
        "SubnetId": "subnet-9c83baf8",
        "State": "pending",
        "VpcId": "vpc-eb38138f",
        "CidrBlock": "11.0.1.0/24",
        "Ipv6CidrBlockAssociationSet": [],
        "AssignIpv6AddressOnCreation": false,
        "AvailableIpAddressCount": 251,
        "AvailabilityZone": "eu-west-1c",
        "DefaultForAz": false,
        "MapPublicIpOnLaunch": false
    }
}

Теги:

$ aws ec2 create-tags --resources subnet-9c83baf8 --tags Key=Name,Value=rtfm_efs_net_A

Первая посеть создана в:

...
"AvailabilityZone": "eu-west-1c"
...

Создаём вторую в eu-west-1a:

$ aws ec2 create-subnet --vpc-id vpc-eb38138f --cidr-block 11.0.2.0/24 --availability-zone eu-west-1a
{
    "Subnet": {
        "SubnetId": "subnet-12b48864",
        "State": "pending",
        "VpcId": "vpc-eb38138f",
        "CidrBlock": "11.0.2.0/24",
        "Ipv6CidrBlockAssociationSet": [],
        "AssignIpv6AddressOnCreation": false,
        "AvailableIpAddressCount": 251,
        "AvailabilityZone": "eu-west-1a",
        "DefaultForAz": false,
        "MapPublicIpOnLaunch": false
    }
}

Теги:

$ aws ec2 create-tags --resources subnet-12b48864 --tags Key=Name,Value=rtfm_efs_net_B

В security группе этой VPC – открываем доступ по портам 80 и 22:

$ aws ec2 describe-security-groups --filters Name=vpc-id,Values=vpc-eb38138f --query '[SecurityGroups[*].GroupId]' --output text
sg-caf5d9ac
$ aws ec2 authorize-security-group-ingress --group-id sg-caf5d9ac --protocol tcp --port 80 --cidr 0.0.0.0/0
$ aws ec2 authorize-security-group-ingress --group-id sg-caf5d9ac --protocol tcp --port 22 --cidr 0.0.0.0/0

Создаём Internet Gateway для VPC:

$ aws ec2 create-internet-gateway
{
    "InternetGateway": {
        "InternetGatewayId": "igw-5d0cac39",
        "Attachments": [],
        "Tags": []
    }
}

Подключаем к VPC:

$ aws ec2 attach-internet-gateway --internet-gateway-id igw-5d0cac39 --vpc-id vpc-eb38138f

Находим таблицу маршрутизации этой VPC:

$ aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-eb38138f --query '[RouteTables[*].RouteTableId]' --output text
rtb-1d24e47a

Добавляем маршрут в 0.0.0.0/0 через созданный IGW:

$ aws ec2 create-route --route-table-id rtb-1d24e47a --destination-cidr-block 0.0.0.0/0 --gateway-id igw-5d0cac39
{
    "Return": true
}

Сеть готова. Быстро проверим.

Запускаем инстанс:

$ aws ec2 run-instances --image-id ami-cbfcd2b8 --count 1 --instance-type t2.nano --key-name my-cluster --security-group-ids sg-caf5d9ac --subnet-id subnet-12b48864

Подключаем публичный IP:

$ aws ec2 allocate-address --domain vpc
{
    "PublicIp": "34.250.70.64",
    "Domain": "vpc",
    "AllocationId": "eipalloc-dde2eab9"
}
$ aws ec2 associate-address --public-ip 34.250.70.64 --instance-id i-010c8401c6e5f2374
{
    "AssociationId": "eipassoc-956cb3ed"
}

Ждём, подключаемся, проверяем сеть:

$ ssh [email protected] -i my-cluster.pem 
...
ubuntu@ip-11-0-2-210:~$ ping ya.ru -c 1
PING ya.ru (93.158.134.3) 56(84) bytes of data.
64 bytes from www.yandex.ru (93.158.134.3): icmp_seq=1 ttl=45 time=62.7 ms

--- ya.ru ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 62.730/62.730/62.730/0.000 ms

Готово.

Создание EFS

Следующим шагом – создадим EFS, добавим ещё одну машину в VPC и примонтируем шару к обоим интансам.

Создаём файловую систему:

$ aws efs create-file-system --creation-token rtfm_efs
{
    "OwnerId": "264418146286",
    "CreationToken": "rtfm_efs",
    "FileSystemId": "fs-41e32788",
    "CreationTime": 1485946711.0,
    "LifeCycleState": "creating",
    "NumberOfMountTargets": 0,
    "SizeInBytes": {
        "Value": 0
    },
    "PerformanceMode": "generalPurpose"
}

Добавляем теги:

$ aws efs create-tags --file-system-id fs-41e32788 --tags Key=Name,Value=rtfm_efs

Создаём две mount target – по одной в каждой из подсетей VPC:

$ aws efs create-mount-target --file-system-id fs-41e32788 --subnet-id subnet-9c83baf8 --security-groups sg-caf5d9ac
{
    "OwnerId": "264418146286",
    "MountTargetId": "fsmt-097cbcc0",
    "FileSystemId": "fs-41e32788",
    "SubnetId": "subnet-9c83baf8",
    "LifeCycleState": "creating",
    "IpAddress": "11.0.1.235",
    "NetworkInterfaceId": "eni-7da7f501"
}
$ aws efs create-mount-target --file-system-id fs-41e32788 --subnet-id subnet-12b48864 --security-groups sg-caf5d9ac
{
    "OwnerId": "264418146286",
    "MountTargetId": "fsmt-0e7cbcc7",
    "FileSystemId": "fs-41e32788",
    "SubnetId": "subnet-12b48864",
    "LifeCycleState": "creating",
    "IpAddress": "11.0.2.86",
    "NetworkInterfaceId": "eni-35f54474"
}

Пока создаются точки монтирования – запускаем второй интанс, в зоне eu-west-1с, вторая посеть:

$ aws ec2 run-instances --image-id ami-cbfcd2b8 --count 1 --instance-type t2.nano --key-name my-cluster --security-group-ids sg-caf5d9ac --subnet-id subnet-9c83baf8

Повторяем подключение EIP, и всё готово (ага) к монтированию:

$ aws ec2 allocate-address --domain vpc
{
    "PublicIp": "34.249.197.248",
    "Domain": "vpc",
    "AllocationId": "eipalloc-b9e5eddd"
}
$ aws ec2 associate-address --public-ip 34.249.197.248 --instance-id i-06b49d6e27aceef6f
{
    "AssociationId": "eipassoc-7261be0a"
}

На обоих машинах создаём каталог /efs:

$ ssh [email protected] -i my-cluster.pem
...
ubuntu@ip-11-0-2-82:~$ sudo -s
root@ip-11-0-2-82:~# mkdir /efs

Проверяем состояние точек монтирования:

$ aws efs describe-mount-targets --file-system-id fs-41e32788
{
    "MountTargets": [
        {
            "OwnerId": "264418146286",
            "MountTargetId": "fsmt-097cbcc0",
            "FileSystemId": "fs-41e32788",
            "SubnetId": "subnet-9c83baf8",
            "LifeCycleState": "available",
            "IpAddress": "11.0.1.235",
            "NetworkInterfaceId": "eni-7da7f501"
        },
        {
            "OwnerId": "264418146286",
            "MountTargetId": "fsmt-0e7cbcc7",
            "FileSystemId": "fs-41e32788",
            "SubnetId": "subnet-12b48864",
            "LifeCycleState": "available",
            "IpAddress": "11.0.2.86",
            "NetworkInterfaceId": "eni-35f54474"
        }
    ]
}

Монтируем файловую систему в /efs:

root@ip-11-0-1-177:~# mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 fs-41e32788.efs.eu-west-1.amazonaws.com:/ /efs
mount: wrong fs type, bad option, bad superblock on fs-41e32788.efs.eu-west-1.amazonaws.com:/,
       missing codepage or helper program, or other error
       (for several filesystems (e.g. nfs, cifs) you might
       need a /sbin/mount.<type> helper program)

       In some cases useful info is found in syslog - try
       dmesg | tail or so.

Ooops. Ответ – тут>>>. Данный AMI не имеет предустановленного NFS-клиента.

Устанавливаем nfs-common:

# apt-get update && apt-get install nfs-common

Монтируем:

root@ip-11-0-1-177:~# mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 fs-41e32788.efs.eu-west-1.amazonaws.com:/ /efs
root@ip-11-0-1-177:~# ls -al /efs/
total 8
drwxr-xr-x  2 root root 4096 Feb  1 10:58 .
drwxr-xr-x 24 root root 4096 Feb  1 11:21 ..

Повторяем:

root@ip-11-0-2-210:~# mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 fs-41e32788.efs.eu-west-1.amazonaws.com:/ /efs
root@ip-11-0-2-210:~# ls -la /efs/
total 8
drwxr-xr-x  2 root root 4096 Feb  1 10:58 .
drwxr-xr-x 24 root root 4096 Feb  1 11:11 ..

Создаём файлы:

root@ip-11-0-1-177:~# echo $(ip a s eth0 | grep inet | head -n 1 | awk '{print $2}') > /efs/ip1.tx
root@ip-11-0-2-210:~# echo $(ip a s eth0 | grep inet | head -n 1 | awk '{print $2}') > /efs/ip2.txt

Проверяем:

root@ip-11-0-2-210:~# ls -la /efs/
total 16
drwxr-xr-x  2 root root 4096 Feb  1 11:31 .
drwxr-xr-x 24 root root 4096 Feb  1 11:11 ..
-rw-r--r--  1 root root   14 Feb  1 11:31 ip1.txt
-rw-r--r--  1 root root   14 Feb  1 11:31 ip2.txt
root@ip-11-0-2-210:~# cat /efs/*.txt
11.0.1.177/24
11.0.2.210/24

Docker

Последний штрих – использовать расшаренные ресурсы из Docker-контейнера (для AWS ECS).

Устанавливаем Docker:

# apt-get install curl linux-image-extra-$(uname -r) linux-image-extra-virtual
# apt-get install apt-transport-https ca-certificates
# curl -fsSL https://yum.dockerproject.org/gpg | sudo apt-key add -
# add-apt-repository "deb https://apt.dockerproject.org/repo/ ubuntu-$(lsb_release -cs) main"
# apt-get update && apt-get install docker-engine

Запускаем контейнер с NGINX, пробрасываем порты 80 <=> 80, и подключаем каталог /efs в контейнер по пути /usr/share/nginx/html:

root@ip-11-0-2-210:~# docker run -tid -v /efs/:/usr/share/nginx/html -p 80:80 nginx
e1a8eb6f1a263b3da4c6156fe8141751ce5bf757b684bc279f2c7a98b7fa0b24

Проверяем:

$ curl 34.250.70.64/ip1.txt
11.0.1.177/24
$ curl 34.250.70.64/ip2.txt
11.0.2.210/24

И на второй машине:

$ curl 34.249.197.248/ip1.txt
11.0.1.177/24
$ curl 34.249.197.248/ip2.txt
11.0.2.210/24

Готово.