botocoreをインストールしてEFSマウントヘルパーで別VPCのEFSファイルシステムをマウントしてみた

別アカウント、別VPCのEFSファイルシステムにEFSマウントヘルパーでマウントする場合はbotocoreをインストールしよう
2023.07.12

カスタムドメイン使うのに結局ファイルシステムIDを指定するのであれば

こんにちは、のんピ(@non____97)です。

皆さんは別アカウント、別VPCのEFSファイルシステムをマウントしたいなと思ったことはありますか? 私はあります。

以前、その対応をするためにマウントターゲットのカスタムドメインにEFSマウントヘルパーを使ってマウントできるか検証しました。

検証した結果、カスタムドメインを指定してマウントすることは可能ですが、一緒にファイルシステムIDを指定する必要がありました。

そのため、カスタムドメインを使うことによってEFSファイルシステムが変更になっても、EFSマウントヘルパーを使って継続してマウントできるということはありません。(正しくはIAM認証ができなくなります)

となると、EFSファイルシステムのマウントターゲットのIPアドレスに対してカスタムドメインを設定する場面はかなり薄れます。

EFSマウントヘルパーを使わずにNFSv4でマウントする場合はファイルシステムIDを指定は不要です。そのため、強いてカスタムドメインの利用シーンを挙げるとするならば、EFSマウントヘルパーを使わず、なるべく分かりやすいドメインで接続したい場合でしょうか。

EFSマウントヘルパーを使うのであれば、無理にカスタムドメインを用意する必要は薄いと考えます。

では、どうしたら良いのでしょうか。

EFSマウントヘルパーではbotocoreをインストールすることで、別アカウント、別VPCのEFSファイルシステムに接続することが可能です。

botocore をインストールしてください。EFS クライアントは、ファイルシステムを別の VPC にマウントするときにファイルシステムの DNS 名を解決できない場合に、ボトコアを使用してマウントターゲット IP アドレスを取得します。詳細については、amazon-efs-utils README ファイルの「botocore のインストール」を参照してください。

EFS マウントヘルパーを使用して EFS ファイルシステムをマウントする - Amazon Elastic File System

これによりカスタムドメインを用意せずに、ファイルシステムIDのみ指定するだけでEFSマウントヘルパーを使って接続することが可能になります。

実際に試してみたので紹介します。

いきなりまとめ

  • botocoreをインストールすることで、別アカウント、別VPCのEFSファイルシステムでも接続できる
  • 接続元にec2:DescribeAvailabilityZoneselasticfilesystem:DescribeMountTargetsの許可を渡す必要がある
  • 別アカウントからマウントする際は接続先のEFSファイルシステムのファイルシステムポリシーでelasticfilesystem:DescribeMountTargetを許可する必要がある
  • デフォルトだと接続元と同じAZのマウントターゲットに接続しようとする
    • 同じAZにマウントターゲットがなく、別AZにマウントターゲットがある場合、フェイルオーバーはせずに接続に失敗する
    • 明示的にマウントターゲットのAZを指定することで対応は可能

検証環境

検証環境は以下のとおりです。

botocoreをインストールしてEFSマウントヘルパーで別VPCのEFSファイルシステムをマウントしてみた検証環境構成図

EFSファイルシステムのマウントターゲットと、接続元のEC2インスタンスのAZは異なるようにしています。

やってみる

別VPCからマウントしてみる

まず、普通にマウントしようとしてみます。

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs
Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com" - check that your file system ID is correct, and ensure that the VPC has an EFS mount target for this file system ID.
See https://docs.aws.amazon.com/console/efs/mount-dns-name for more detail.
Attempting to lookup mount target ip address using botocore. Failed to import necessary dependency botocore, please install botocore first.

はい、接続できませんでした。

エラーメッセージをよくよく見ると、「botocoreを使ってマウントターゲットのIPアドレスを検出しようとしたが、botocoreがインポートされていない」とメッセージが出力されていますね。

botocoreのインストール

それではbotocoreをインストールします。

botocoreのインストール方法はEFSマウントヘルパーのREADMEに記載があります。

まず、get-pip.pyをダウンロードしてきます。

# get-pip.py のダウンロード
$ if [[ "$(python3 -V 2>&1)" =~ ^(Python 3.6.*) ]]; then
    sudo wget https://bootstrap.pypa.io/pip/3.6/get-pip.py -O /tmp/get-pip.py
elif [[ "$(python3 -V 2>&1)" =~ ^(Python 3.5.*) ]]; then
    sudo wget https://bootstrap.pypa.io/pip/3.5/get-pip.py -O /tmp/get-pip.py
elif [[ "$(python3 -V 2>&1)" =~ ^(Python 3.4.*) ]]; then
    sudo wget https://bootstrap.pypa.io/pip/3.4/get-pip.py -O /tmp/get-pip.py
else
    sudo wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py
fi
--2023-07-11 21:17:15--  https://bootstrap.pypa.io/get-pip.py
Resolving bootstrap.pypa.io (bootstrap.pypa.io)... 146.75.32.175, 2a04:4e42:78::175
Connecting to bootstrap.pypa.io (bootstrap.pypa.io)|146.75.32.175|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2578580 (2.5M) 
Saving to: ‘/tmp/get-pip.py’

/tmp/get-pip.py                                         100%[===============================================================================================================================>]   2.46M  --.-KB/s    in 0.05s

2023-07-11 21:17:15 (54.0 MB/s) - ‘/tmp/get-pip.py’ saved [2578580/2578580]

# get-pip.py がダウンロードできていることを確認
$ ls -l /tmp/get-pip.py
-rw-r--r--. 1 root root 2578580 Apr 26 09:45 /tmp/get-pip.py

ダウンロードしてきたget-pip.pyの中身は以下のとおりです。長すぎるので先頭150列のみ表示しています。DATAはBASE85でエンコードされた文字列のようです。

$ head -n150 /tmp/get-pip.py
#!/usr/bin/env python
#
# Hi There!
#
# You may be wondering what this giant blob of binary data here is, you might
# even be worried that we're up to something nefarious (good for you for being
# paranoid!). This is a base85 encoding of a zip file, this zip file contains
# an entire copy of pip (version 23.1.2).
#
# Pip is a thing that installs packages, pip itself is a package that someone
# might want to install, especially if they're looking to run this get-pip.py
# script. Pip has a lot of code to deal with the security of installing
# packages, various edge cases on various platforms, and other such sort of
# "tribal knowledge" that has been encoded in its code base. Because of this
# we basically include an entire copy of pip inside this blob. We do this
# because the alternatives are attempt to implement a "minipip" that probably
# doesn't do things correctly and has weird edge cases, or compress pip itself
# down into a single file.
#
# If you're wondering how this is created, it is generated using
# `scripts/generate.py` in https://github.com/pypa/get-pip.

import sys

this_python = sys.version_info[:2]
min_version = (3, 7)
if this_python < min_version:
    message_parts = [
        "This script does not work on Python {}.{}".format(*this_python),
        "The minimum supported Python version is {}.{}.".format(*min_version),
        "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python),
    ]
    print("ERROR: " + " ".join(message_parts))
    sys.exit(1)


import os.path
import pkgutil
import shutil
import tempfile
import argparse
import importlib
from base64 import b85decode


def include_setuptools(args):
    """
    Install setuptools only if absent and not excluded.
    """
    cli = not args.no_setuptools
    env = not os.environ.get("PIP_NO_SETUPTOOLS")
    absent = not importlib.util.find_spec("setuptools")
    return cli and env and absent


def include_wheel(args):
    """
    Install wheel only if absent and not excluded.
    """
    cli = not args.no_wheel
    env = not os.environ.get("PIP_NO_WHEEL")
    absent = not importlib.util.find_spec("wheel")
    return cli and env and absent


def determine_pip_install_arguments():
    pre_parser = argparse.ArgumentParser()
    pre_parser.add_argument("--no-setuptools", action="store_true")
    pre_parser.add_argument("--no-wheel", action="store_true")
    pre, args = pre_parser.parse_known_args()

    args.append("pip")

    if include_setuptools(pre):
        args.append("setuptools")

    if include_wheel(pre):
        args.append("wheel")

    return ["install", "--upgrade", "--force-reinstall"] + args


def monkeypatch_for_cert(tmpdir):
    """Patches `pip install` to provide default certificate with the lowest priority.

    This ensures that the bundled certificates are used unless the user specifies a
    custom cert via any of pip's option passing mechanisms (config, env-var, CLI).

    A monkeypatch is the easiest way to achieve this, without messing too much with
    the rest of pip's internals.
    """
    from pip._internal.commands.install import InstallCommand

    # We want to be using the internal certificates.
    cert_path = os.path.join(tmpdir, "cacert.pem")
    with open(cert_path, "wb") as cert:
        cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem"))

    install_parse_args = InstallCommand.parse_args

    def cert_parse_args(self, args):
        if not self.parser.get_default_values().cert:
            # There are no user provided cert -- force use of bundled cert
            self.parser.defaults["cert"] = cert_path  # calculated above
        return install_parse_args(self, args)

    InstallCommand.parse_args = cert_parse_args


def bootstrap(tmpdir):
    monkeypatch_for_cert(tmpdir)

    # Execute the included pip and use it to install the latest pip and
    # setuptools from PyPI
    from pip._internal.cli.main import main as pip_entry_point
    args = determine_pip_install_arguments()
    sys.exit(pip_entry_point(args))


def main():
    tmpdir = None
    try:
        # Create a temporary working directory
        tmpdir = tempfile.mkdtemp()

        # Unpack the zipfile into the temporary directory
        pip_zip = os.path.join(tmpdir, "pip.zip")
        with open(pip_zip, "wb") as fp:
            fp.write(b85decode(DATA.replace(b"\n", b"")))

        # Add the zipfile to sys.path so that we can import it
        sys.path.insert(0, pip_zip)

        # Run the bootstrap
        bootstrap(tmpdir=tmpdir)
    finally:
        # Clean up our temporary working directory
        if tmpdir:
            shutil.rmtree(tmpdir, ignore_errors=True)


DATA = b"""
P)h>@6aWAK2ms(pnpOdJ9~Srk003nH000jF003}la4%n9X>MtBUtcb8c|B0UO2j}6z0X&KUUXrd;wrc
n6ubz6s0VM$QfAw<4YV^ulDhQoop$MlK*;0e<?$L01LzdVw?IP-tnf*qTlkJj!Mom=viw7qw3H>hK(>
3Z_jZ>VV`^+*aO7_tw^Cd$4zs{Pl#j>6{|X*AaQ6!2wJ?w>%d+2&1X4Rc!^r6h-hMtH_<n)`omXfA!z
c)+2_nTCfpGRv1uvmTkcug)ShEPeC#tJ!y1a)P)ln~75Jc!yqZE1Gl6K?CR$<8F6kVP)a}pU*@~6k=y
<MFxvzbFl3|p@5?5Ii7qF0_`NT{r7m1lM_B44a9>d5{IF3D`nKTt~p1QY-O00;o!N}5(98{@xi0ssK6
1ONaJ0001RX>c!JUu|J&ZeL$6aCu!*!ET%|5WVviBXU@FMMw_qp;5O|rCxIBA*z%^Qy~|I#aghDZI*1
mzHbcdCgFtbH*em&nbG}VT_EcdJ^%Uh<#$rfXmjvMazjtt+Y{4fL(0@tjn1(F!nz|6RBOjou<lHavpt
2DsnN~{0?3^aZW|#k1{K<zbVGw<F9gAoI$2%Q=!IwHz3?Ga8yfULmF;_^_Efc89djgN{>LCQKB%tCsn

それでは、botocoreをインストールします。

# pipとwheelのインストール
$ sudo python3 /tmp/get-pip.py
Collecting pip
  Downloading pip-23.1.2-py3-none-any.whl (2.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 27.5 MB/s eta 0:00:00
Collecting wheel
  Downloading wheel-0.40.0-py3-none-any.whl (64 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 64.5/64.5 kB 14.6 MB/s eta 0:00:00
Installing collected packages: wheel, pip
Successfully installed pip-23.1.2 wheel-0.40.0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

# botocoreのインストール
$ sudo pip3 install botocore || sudo /usr/local/bin/pip3 install botocore
Collecting botocore
  Downloading botocore-1.31.2-py3-none-any.whl (11.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.0/11.0 MB 79.5 MB/s eta 0:00:00
Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /usr/lib/python3.9/site-packages (from botocore) (0.10.0)
Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /usr/lib/python3.9/site-packages (from botocore) (2.8.1)
Requirement already satisfied: urllib3<1.27,>=1.25.4 in /usr/lib/python3.9/site-packages (from botocore) (1.25.10)
Requirement already satisfied: six>=1.5 in /usr/lib/python3.9/site-packages (from python-dateutil<3.0.0,>=2.1->botocore) (1.15.0)
Installing collected packages: botocore
Successfully installed botocore-1.31.2
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

# インストールされているパッケージ一覧の確認
$ pip3 list
Package              Version
-------------------- --------
attrs                20.3.0
aws-cfn-bootstrap    2.0
awscli               2.9.19
awscrt               0.16.7
Babel                2.9.1
botocore             1.31.2
cffi                 1.14.5
chardet              4.0.0
chevron              0.13.1
cloud-init           22.2.2
colorama             0.4.4
configobj            5.0.6
cryptography         36.0.1
dbus-python          1.2.18
distro               1.5.0
docutils             0.16
ec2-hibinit-agent    1.0.2
gpg                  1.15.1
idna                 2.10
Jinja2               2.11.3
jmespath             0.10.0
jsonpatch            1.21
jsonpointer          2.0
jsonschema           3.2.0
libcomps             0.1.18
lockfile             0.12.2
MarkupSafe           1.1.1
netifaces            0.10.6
oauthlib             3.0.2
pip                  23.1.2
ply                  3.11
prettytable          0.7.2
prompt-toolkit       3.0.24
pycparser            2.20
pyrsistent           0.17.3
pyserial             3.4
PySocks              1.7.1
python-daemon        2.3.0
python-dateutil      2.8.1
pytz                 2022.7.1
PyYAML               5.4.1
release-notification 1.2
requests             2.25.1
rpm                  4.16.1.3
ruamel.yaml          0.16.6
ruamel.yaml.clib     0.1.2
selinux              3.4
sepolicy             3.4
setools              4.4.1
setuptools           59.6.0
six                  1.15.0
support-info         1.0
urllib3              1.25.10
wcwidth              0.2.5
wheel                0.40.0

botocore 1.31.2 がインストールできましたね。

別VPCからマウントしてみる (2回目)

それでは、再度別VPCのEC2インスタンスからEFSマウントヘルパーを使ってマウントしてみます。

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs
Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
User: arn:aws:sts::<AWSアカウントID>:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-0db2e26bb837c93b1 is not authorized to perform: elasticfilesystem:DescribeMountTargets on the specified resource

アタッチしているインスタンスプロファイルにelasticfilesystem:DescribeMountTargetsが足りないようですね。

IAMロールに以下ポリシーをアタッチします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "elasticfilesystem:DescribeMountTargets"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

再チャレンジです。

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs
Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
Unauthorized to perform operation DescribeAvailabilityZones.

次はDescribeAvailabilityZonesの権限が足りないと怒られました。

以下のようにec2:DescribeAvailabilityZonesを許可するようなIAMポリシーに修正します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "elasticfilesystem:DescribeMountTargets",
        "ec2:DescribeAvailabilityZones"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

再々チャレンジです。

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs
Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
No matching mount target in the az us-east-1d. Please create one mount target in us-east-1d, or try the mount target in another AZ by passing the availability zone name option. Available mount target(s) are in az ['us-east-1a']

今度は「us-east-1dのマウントターゲットがない」と怒られました。

同じAZにマウントターゲットがなく、別AZにマウントターゲットがある場合、フェイルオーバーはせずに接続に失敗するようです。

EFSファイルシステムのマウントターゲットがあるAZであるus-east-1aを指定して、再々々チャレンジです。

$ sudo mount -t efs -o tls,iam,az=us-east-1a fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

マウントできましたね。

EFSマウントヘルパーのログも確認してみましょう。

$ tail -n40 /var/log/amazon/efs/mount.log
2023-07-04 04:40:34 UTC - INFO - binding 20363
2023-07-04 04:40:34 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 04:40:34 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573ea.mount.efs.20363"
2023-07-04 04:40:34 UTC - INFO - Started TLS tunnel, pid: 2541
2023-07-04 04:40:34 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20363" with 15 sec time limit.
2023-07-04 04:40:34 UTC - INFO - Successfully mounted fs-0e211df86678573ea.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-11 21:15:59 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-11 21:15:59 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:15:59 UTC - ERROR - Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com" - check that your file system ID is correct, and ensure that the VPC has an EFS mount target for this file system ID.
See https://docs.aws.amazon.com/console/efs/mount-dns-name for more detail.
Attempting to lookup mount target ip address using botocore. Failed to import necessary dependency botocore, please install botocore first.
2023-07-11 21:19:57 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-11 21:19:57 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:19:57 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:19:57 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:19:57 UTC - ERROR - Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
User: arn:aws:sts::<AWSアカウントID>:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-0db2e26bb837c93b1 is not authorized to perform: elasticfilesystem:DescribeMountTargets on the specified resource
2023-07-11 21:23:09 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-11 21:23:09 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:23:09 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:23:10 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:23:10 UTC - ERROR - Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
Unauthorized to perform operation DescribeAvailabilityZones.
2023-07-11 21:25:23 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-11 21:25:23 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:25:23 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:25:23 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:25:23 UTC - ERROR - Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com". The file system mount target ip address cannot be found, please pass mount target ip address via mount options.
No matching mount target in the az us-east-1d. Please create one mount target in us-east-1d, or try the mount target in another AZ by passing the availability zone name option. Available mount target(s) are in az ['us-east-1a']
2023-07-11 21:28:55 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'az': 'us-east-1a'}
2023-07-11 21:28:55 UTC - INFO - Failed to resolve us-east-1a.fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:28:55 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:28:55 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:28:55 UTC - INFO - Found fall back mount target ip address 10.0.0.206 for file system fs-0e211df86678573e6
2023-07-11 21:28:55 UTC - INFO - binding 20244
2023-07-11 21:28:56 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-11 21:28:56 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20244"
2023-07-11 21:28:56 UTC - INFO - Started TLS tunnel, pid: 2606
2023-07-11 21:28:56 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20244" with 15 sec time limit.
2023-07-11 21:28:56 UTC - INFO - Successfully mounted us-east-1a.fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

フォールバックIPアドレス10.0.0.206を取得できていますね。

接続元と同じAZにマウントターゲットを作成して接続してみる

次に、接続元と同じAZにマウントターゲットを作成して接続してみます。

まず、接続元と同じAZであるus-east-1dにマウントターゲットを作成します。

マウントターゲット一覧

us-east-1dにマウントターゲット作成後、AZを指定せずにEFSマウントヘルパーで接続します。

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

はい、マウントできました。

EFSマウントヘルパーのログも確認してみましょう。

$ tail -n11 /var/log/amazon/efs/mount.log
2023-07-11 21:43:35 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-11 21:43:35 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-11 21:43:35 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:43:35 UTC - INFO - Found credentials from IAM Role: AmazonSSMRoleForInstancesQuickSetup
2023-07-11 21:43:35 UTC - INFO - Found fall back mount target ip address 10.1.1.8 for file system fs-0e211df86678573e6
2023-07-11 21:43:35 UTC - INFO - binding 20994
2023-07-11 21:43:35 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-11 21:43:35 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20994"
2023-07-11 21:43:35 UTC - INFO - Started TLS tunnel, pid: 4105
2023-07-11 21:43:35 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20994" with 15 sec time limit.
2023-07-11 21:43:35 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

us-east-1dのマウントターゲットのIPアドレスである10.1.1.8がフォールバックIPアドレスとして認識されていますね。

ちなみに、DNS名やファイルシステムIDから名前解決できない際にフォールバックIPアドレスを取得する場合は、EFSマウントヘルパーの設定ファイル/etc/amazon/efs/efs-utils.conffall_back_to_mount_target_ip_address_enabledtrueになっている必要があります。

$ cat /etc/amazon/efs/efs-utils.conf
#
# Copyright 2017-2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
#
# Licensed under the MIT License. See the LICENSE accompanying this file
# for the specific language governing permissions and limitations under
# the License.
#

[DEFAULT]
logging_level = INFO
logging_max_bytes = 1048576
logging_file_count = 10
# mode for /var/run/efs and subdirectories in octal
state_file_dir_mode = 750

[mount]
dns_name_format = {az}.{fs_id}.efs.{region}.{dns_name_suffix}
dns_name_suffix = amazonaws.com
#The region of the file system when mounting from on-premises or cross region.
#region = us-east-1
stunnel_debug_enabled = false
#Uncomment the below option to save all stunnel logs for a file system to the same file
#stunnel_logs_file = /var/log/amazon/efs/{fs_id}.stunnel.log
stunnel_cafile = /etc/amazon/efs/efs-utils.crt

# Validate the certificate hostname on mount. This option is not supported by certain stunnel versions.
stunnel_check_cert_hostname = true

# Use OCSP to check certificate validity. This option is not supported by certain stunnel versions.
stunnel_check_cert_validity = false

# Set to true to use FIPS-mode for stunnel. Enabling this will change the AWS SDK client to use FIPS as well.
fips_mode_enabled = false

# Define the port range that the TLS tunnel will choose from
port_range_lower_bound = 20049
port_range_upper_bound = 21049

# Optimize read_ahead_kb for Linux 5.4+
optimize_readahead = true

# By default, we enable the feature to fallback to mount with mount target ip address when dns name cannot be resolved
fall_back_to_mount_target_ip_address_enabled = true

# By default, we use IMDSv2 to get the instance metadata, set this to true if you want to disable IMDSv2 usage
disable_fetch_ec2_metadata_token = false

# By default, we enable efs-utils to retry failed mount.nfs command that due to (1) connection reset by peer (2) the
# mount.nfs is not finished within 'retry_nfs_mount_command_timeout_sec'. If the retry count is set as N, initial N - 1
# mount attempts will timeout if the command does not finish within 'retry_nfs_mount_command_timeout_sec' sec.
# The last mount attempt will keep the existing behavior of mount.nfs.
#
retry_nfs_mount_command = true
retry_nfs_mount_command_count = 3
retry_nfs_mount_command_timeout_sec = 15

[mount.cn-north-1]
dns_name_suffix = amazonaws.com.cn


[mount.cn-northwest-1]
dns_name_suffix = amazonaws.com.cn


[mount.us-iso-east-1]
dns_name_suffix = c2s.ic.gov
stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

[mount.us-iso-west-1]
dns_name_suffix = c2s.ic.gov
stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

[mount.us-isob-east-1]
dns_name_suffix = sc2s.sgov.gov
stunnel_cafile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

[mount-watchdog]
enabled = true
poll_interval_sec = 1
unmount_count_for_consistency = 5
unmount_grace_period_sec = 30

# Set client auth/access point certificate renewal rate. Minimum value is 1 minute.
tls_cert_renewal_interval_min = 60

# Periodically check the health of stunnel to make sure the connection is fully established
stunnel_health_check_enabled = true
stunnel_health_check_interval_min = 5
stunnel_health_check_command_timeout_sec = 30

[cloudwatch-log]
# enabled = true
log_group_name = /aws/efs/utils

# Possible values are : 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653
# Comment this config to prevent log deletion
retention_in_days = 14

別アカウント、別VPCのEFSファイルシステムにEFSマウントヘルパーでマウントする場合はbotocoreをインストールしよう

botocoreをインストールしてEFSマウントヘルパーで別VPCのEFSファイルシステムをマウントしてみました。

botocoreをインストールして、必要な権限を渡すだけなので非常に簡単ですね。

なお、今回は行いませんでしたが、別アカウントにあるEFSファイルシステムに接続する場合は、EFSファイルシステムのファイルシステムポリシーで、接続元のAWSアカウントからのelasticfilesystem:DescribeMountTargetsを許可してあげる必要があります。

{
    "Id": "access-point-example03",
    "Statement": [
        {
            "Sid": "access-point-statement-example03",
            "Effect": "Allow",
            "Principal": {"AWS": "arn:aws:iam::555555555555"},
            "Action": "elasticfilesystem:DescribeMountTargets",
            "Resource": "arn:aws:elasticfilesystem:us-east-2:111122223333:file-system/fs-12345678",
        }
    ]
}

これら前提条件は以下AWS公式ドキュメントに記載があるので、ぜひ確認してみてください。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!