6.9.5. AWS EFS CSI クロスアカウントのサポート
クロスアカウントのサポートにより、1 つの AWS アカウントに OpenShift Container Platform クラスターを配置し、AWS Elastic File System (EFS) Container Storage Interface (CSI) ドライバーを使用して別の AWS アカウントにファイルシステムをマウントできます。
前提条件
- 管理者権限を持つ OpenShift Container Platform クラスターへのアクセス
- 2 つの有効な AWS アカウントがある。
- EFS CSI Operator がインストールされている。EFS CSI Operator のインストール方法は、AWS EFS CSI Driver Operator のインストール セクションを参照してください。
- OpenShift Container Platform クラスターと EFS ファイルシステムの両方が同じ AWS リージョンに配置されている。
- 次の手順で使用する 2 つの仮想プライベートクラウド (VPC) が異なるネットワーク Classless Inter-Domain Routing (CIDR) 範囲を使用していることを確認する。
-
OpenShift Container Platform CLI (
oc) へのアクセス。 - AWS CLI へアクセスできる。
-
jqコマンドライン JSON プロセッサーへアクセスできる。
手順
次の手順では、以下のアカウントを設定する方法を説明します。
- OpenShift Container Platform AWS アカウント A: VPC 内にデプロイされた Red Hat OpenShift Container Platform クラスター (v4.16 以降) が含まれている。
- AWS アカウント B: VPC (サブネット、ルートテーブル、ネットワーク接続を含む) が含まれている。この VPC に EFS ファイルシステムを作成します。
アカウント間で AWS EFS を使用するには:
環境をセットアップします。
次のコマンドを実行して環境変数を設定します。
export CLUSTER_NAME="<CLUSTER_NAME>"1 export AWS_REGION="<AWS_REGION>"2 export AWS_ACCOUNT_A_ID="<ACCOUNT_A_ID>"3 export AWS_ACCOUNT_B_ID="<ACCOUNT_B_ID>"4 export AWS_ACCOUNT_A_VPC_CIDR="<VPC_A_CIDR>"5 export AWS_ACCOUNT_B_VPC_CIDR="<VPC_B_CIDR>"6 export AWS_ACCOUNT_A_VPC_ID="<VPC_A_ID>"7 export AWS_ACCOUNT_B_VPC_ID="<VPC_B_ID>"8 export SCRATCH_DIR="<WORKING_DIRECTORY>"9 export CSI_DRIVER_NAMESPACE="openshift-cluster-csi-drivers"10 export AWS_PAGER=""11 - 1
- 任意のクラスター名。
- 2
- 任意の AWS リージョン。
- 3
- AWS アカウント A の ID。
- 4
- AWS アカウント B の ID。
- 5
- アカウント A の VPC の CIDR 範囲。
- 6
- アカウント B の VPC の CIDR 範囲。
- 7
- アカウント A (クラスター) の VPC ID
- 8
- アカウント B の VPC ID (EFS クロスアカウント)
- 9
- 一時ファイルを保存するために使用する任意の書き込み可能なディレクトリー。
- 10
- ドライバーがデフォルト以外の namespace にインストールされている場合は、この値を変更します。
- 11
- AWS CLI のすべての出力を stdout に直接出力させます。
次のコマンドを実行して作業ディレクトリーを作成します。
mkdir -p $SCRATCH_DIROpenShift Container Platform CLI で次のコマンドを実行して、クラスターの接続を確認します。
$ oc whoamiOpenShift Container Platform クラスターのタイプを決定し、ノードセレクターを設定します。
EFS クロスアカウント機能を使用するには、EFS CSI コントローラー Pod が稼働しているノードに AWS IAM ポリシーを割り当てる必要があります。ただし、割り当て方法は OpenShift Container Platform タイプによって異なります。
クラスターが Hosted Control Plane (HyperShift) としてデプロイされている場合は、次のコマンドを実行して、ワーカーノードのラベルを格納するように
NODE_SELECTOR環境変数を設定します。export NODE_SELECTOR=node-role.kubernetes.io/workerそれ以外の OpenShift Container Platform タイプの場合は、次のコマンドを実行して、マスターノードのラベルを格納するように
NODE_SELECTOR環境変数を設定します。export NODE_SELECTOR=node-role.kubernetes.io/master
次のコマンドを実行して、アカウント切り替え用の環境変数として AWS CLI プロファイルを設定します。
export AWS_ACCOUNT_A="<ACCOUNT_A_NAME>" export AWS_ACCOUNT_B="<ACCOUNT_B_NAME>"次のコマンドを実行して、両方のアカウントで AWS CLI のデフォルト出力形式が JSON に設定されていることを確認します。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_A} aws configure get output export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B} aws configure get output上記のコマンドが次の結果を返す場合:
- 値なし: デフォルト出力形式がすでに JSON に設定されており、変更は必要ありません。
- 何らかの値: JSON 形式を使用するように AWS CLI を再設定します。出力形式の変更方法は、AWS ドキュメントの Setting the output format in the AWS CLI を参照してください。
次のコマンドを実行して、
AWS_DEFAULT_PROFILEとの競合を防ぐために、シェルでAWS_PROFILEを設定解除します。unset AWS_PROFILE
AWS アカウント B の IAM ロールとポリシーを設定します。
次のコマンドを実行して、アカウント B のプロファイルに切り替えます。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B}次のコマンドを実行して、EFS CSI Driver Operator の IAM ロール名を定義します。
export ACCOUNT_B_ROLE_NAME=${CLUSTER_NAME}-cross-account-aws-efs-csi-operator次のコマンドを実行して、IAM 信頼ポリシーファイルを作成します。
cat <<EOF > $SCRATCH_DIR/AssumeRolePolicyInAccountB.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::${AWS_ACCOUNT_A_ID}:root" }, "Action": "sts:AssumeRole", "Condition": {} } ] } EOF次のコマンドを実行して、EFS CSI Driver Operator の IAM ロールを作成します。
ACCOUNT_B_ROLE_ARN=$(aws iam create-role \ --role-name "${ACCOUNT_B_ROLE_NAME}" \ --assume-role-policy-document file://$SCRATCH_DIR/AssumeRolePolicyInAccountB.json \ --query "Role.Arn" --output text) \ && echo $ACCOUNT_B_ROLE_ARN次のコマンドを実行して、IAM ポリシーファイルを作成します。
cat << EOF > $SCRATCH_DIR/EfsPolicyInAccountB.json { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "ec2:DescribeNetworkInterfaces", "ec2:DescribeSubnets" ], "Resource": "*" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "elasticfilesystem:DescribeMountTargets", "elasticfilesystem:DeleteAccessPoint", "elasticfilesystem:ClientMount", "elasticfilesystem:DescribeAccessPoints", "elasticfilesystem:ClientWrite", "elasticfilesystem:ClientRootAccess", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:CreateAccessPoint", "elasticfilesystem:TagResource" ], "Resource": "*" } ] } EOF次のコマンドを実行して、IAM ポリシーを作成します。
ACCOUNT_B_POLICY_ARN=$(aws iam create-policy --policy-name "${CLUSTER_NAME}-efs-csi-policy" \ --policy-document file://$SCRATCH_DIR/EfsPolicyInAccountB.json \ --query 'Policy.Arn' --output text) \ && echo ${ACCOUNT_B_POLICY_ARN}次のコマンドを実行して、ポリシーをロールにアタッチします。
aws iam attach-role-policy \ --role-name "${ACCOUNT_B_ROLE_NAME}" \ --policy-arn "${ACCOUNT_B_POLICY_ARN}"
AWS アカウント A の IAM ロールとポリシーを設定します。
次のコマンドを実行して、アカウント A のプロファイルに切り替えます。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_A}次のコマンドを実行して、IAM ポリシードキュメントを作成します。
cat << EOF > $SCRATCH_DIR/AssumeRoleInlinePolicyPolicyInAccountA.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "${ACCOUNT_B_ROLE_ARN}" } ] } EOFAWS アカウント A で、次のコマンドを実行して、AWS 管理ポリシー "AmazonElasticFileSystemClientFullAccess" を OpenShift Container Platform クラスターのマスターロールにアタッチします。
EFS_CLIENT_FULL_ACCESS_BUILTIN_POLICY_ARN=arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess declare -A ROLE_SEEN for NODE in $(oc get nodes --selector="${NODE_SELECTOR}" -o jsonpath='{.items[*].metadata.name}'); do INSTANCE_PROFILE=$(aws ec2 describe-instances \ --filters "Name=private-dns-name,Values=${NODE}" \ --query 'Reservations[].Instances[].IamInstanceProfile.Arn' \ --output text | awk -F'/' '{print $NF}' | xargs) MASTER_ROLE_ARN=$(aws iam get-instance-profile \ --instance-profile-name "${INSTANCE_PROFILE}" \ --query 'InstanceProfile.Roles[0].Arn' \ --output text | xargs) MASTER_ROLE_NAME=$(echo "${MASTER_ROLE_ARN}" | awk -F'/' '{print $NF}' | xargs) echo "Checking role: '${MASTER_ROLE_NAME}'" if [[ -n "${ROLE_SEEN[$MASTER_ROLE_NAME]:-}" ]]; then echo "Already processed role: '${MASTER_ROLE_NAME}', skipping." continue fi ROLE_SEEN["$MASTER_ROLE_NAME"]=1 echo "Assigning policy ${EFS_CLIENT_FULL_ACCESS_BUILTIN_POLICY_ARN} to role ${MASTER_ROLE_NAME}" aws iam attach-role-policy --role-name "${MASTER_ROLE_NAME}" --policy-arn "${EEFS_CLIENT_FULL_ACCESS_BUILTIN_POLICY_ARN}" done
ロールの引き受けを許可するために、IAM エンティティーにポリシーをアタッチします。
この手順はクラスターの設定によって異なります。次のどちらの場合も、EFS CSI Driver Operator はエンティティーを使用して AWS に対して認証します。このエンティティーには、アカウント B でロールを引き受けるための権限を付与する必要があります。
クラスターで:
- STS が有効でない場合: EFS CSI Driver Operator は、AWS 認証に IAM ユーザーエンティティーを使用します。「ロールの引き受けを許可するために IAM ユーザーにポリシーをアタッチする」ステップに進んでください。
- STS が有効な場合: EFS CSI Driver Operator は、AWS 認証に IAM ロールエンティティーを使用します。「ロールの引き受けを許可するために IAM ロールにポリシーをアタッチする」ステップに進んでください。
ロールの引き受けを許可するために IAM ユーザーにポリシーをアタッチします。
次のコマンドを実行して、EFS CSI Driver Operator によって使用されている IAM ユーザーを特定します。
EFS_CSI_DRIVER_OPERATOR_USER=$(oc -n openshift-cloud-credential-operator get credentialsrequest/openshift-aws-efs-csi-driver -o json | jq -r '.status.providerStatus.user')次のコマンドを実行して、その IAM ユーザーにポリシーをアタッチします。
aws iam put-user-policy \ --user-name "${EFS_CSI_DRIVER_OPERATOR_USER}" \ --policy-name efs-cross-account-inline-policy \ --policy-document file://$SCRATCH_DIR/AssumeRoleInlinePolicyPolicyInAccountA.json
ロールの引き受けを許可するために IAM ロールにポリシーをアタッチします。
次のコマンドを実行して、EFS CSI Driver Operator によって現在使用されている IAM ロール名を特定します。
EFS_CSI_DRIVER_OPERATOR_ROLE=$(oc -n ${CSI_DRIVER_NAMESPACE} get secret/aws-efs-cloud-credentials -o jsonpath='{.data.credentials}' | base64 -d | grep role_arn | cut -d'/' -f2) && echo ${EFS_CSI_DRIVER_OPERATOR_ROLE}次のコマンドを実行して、EFS CSI Driver Operator によって使用されている IAM ロールにポリシーをアタッチします。
aws iam put-role-policy \ --role-name "${EFS_CSI_DRIVER_OPERATOR_ROLE}" \ --policy-name efs-cross-account-inline-policy \ --policy-document file://$SCRATCH_DIR/AssumeRoleInlinePolicyPolicyInAccountA.json
VPC ピアリングを設定します。
次のコマンドを実行して、アカウント A からアカウント B へのピアリングリクエストを開始します。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_A} PEER_REQUEST_ID=$(aws ec2 create-vpc-peering-connection --vpc-id "${AWS_ACCOUNT_A_VPC_ID}" --peer-vpc-id "${AWS_ACCOUNT_B_VPC_ID}" --peer-owner-id "${AWS_ACCOUNT_B_ID}" --query VpcPeeringConnection.VpcPeeringConnectionId --output text)次のコマンドを実行して、アカウント B でピアリングリクエストを承認します。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B} aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id "${PEER_REQUEST_ID}"次のコマンドを実行して、アカウント A のルートテーブル ID を取得し、アカウント B の VPC にルートを追加します。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_A} for NODE in $(oc get nodes --selector=node-role.kubernetes.io/worker | tail -n +2 | awk '{print $1}') do SUBNET=$(aws ec2 describe-instances --filters "Name=private-dns-name,Values=$NODE" --query 'Reservations[*].Instances[*].NetworkInterfaces[*].SubnetId' | jq -r '.[0][0][0]') echo SUBNET is ${SUBNET} ROUTE_TABLE_ID=$(aws ec2 describe-route-tables --filters "Name=association.subnet-id,Values=${SUBNET}" --query 'RouteTables[*].RouteTableId' | jq -r '.[0]') echo Route table ID is $ROUTE_TABLE_ID aws ec2 create-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${AWS_ACCOUNT_B_VPC_CIDR} --vpc-peering-connection-id ${PEER_REQUEST_ID} done次のコマンドを実行して、アカウント B のルートテーブル ID を取得し、アカウント A の VPC にルートを追加します。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B} for ROUTE_TABLE_ID in $(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=${AWS_ACCOUNT_B_VPC_ID}" --query "RouteTables[].RouteTableId" | jq -r '.[]') do echo Route table ID is $ROUTE_TABLE_ID aws ec2 create-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${AWS_ACCOUNT_A_VPC_CIDR} --vpc-peering-connection-id ${PEER_REQUEST_ID} done
アカウント A から EFS への NFS トラフィックを許可するように、アカウント B のセキュリティーグループを設定します。
次のコマンドを実行して、アカウント B のプロファイルに切り替えます。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B}次のコマンドを実行して、EFS アクセス用の VPC セキュリティーグループを設定します。
SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values="${AWS_ACCOUNT_B_VPC_ID}" | jq -r '.SecurityGroups[].GroupId') aws ec2 authorize-security-group-ingress \ --group-id "${SECURITY_GROUP_ID}" \ --protocol tcp \ --port 2049 \ --cidr "${AWS_ACCOUNT_A_VPC_CIDR}" | jq .
アカウント B にリージョン全体の EFS ファイルシステムを作成します。
次のコマンドを実行して、アカウント B のプロファイルに切り替えます。
export AWS_DEFAULT_PROFILE=${AWS_ACCOUNT_B}次のコマンドを実行して、リージョン全体の EFS ファイルシステムを作成します。
CROSS_ACCOUNT_FS_ID=$(aws efs create-file-system --creation-token efs-token-1 \ --region ${AWS_REGION} \ --encrypted | jq -r '.FileSystemId') \ && echo $CROSS_ACCOUNT_FS_ID次のコマンドを実行して、EFS のリージョン全体のマウントターゲットを設定します。
for SUBNET in $(aws ec2 describe-subnets \ --filters "Name=vpc-id,Values=${AWS_ACCOUNT_B_VPC_ID}" \ --region ${AWS_REGION} \ | jq -r '.Subnets.[].SubnetId'); do \ MOUNT_TARGET=$(aws efs create-mount-target --file-system-id ${CROSS_ACCOUNT_FS_ID} \ --subnet-id ${SUBNET} \ --region ${AWS_REGION} \ | jq -r '.MountTargetId'); \ echo ${MOUNT_TARGET}; \ doneこれにより、VPC の各サブネットにマウントポイントが作成されます。
クロスアカウントアクセス用に EFS Operator を設定します。
次のコマンドを実行して、後のステップで作成するシークレットとストレージクラスのカスタム名を定義します。
export SECRET_NAME=my-efs-cross-account export STORAGE_CLASS_NAME=efs-sc-crossOpenShift Container Platform CLI で次のコマンドを実行して、アカウント B のロール ARN を参照するシークレットを作成します。
oc create secret generic ${SECRET_NAME} -n ${CSI_DRIVER_NAMESPACE} --from-literal=awsRoleArn="${ACCOUNT_B_ROLE_ARN}"OpenShift Container Platform CLI で次のコマンドを実行して、新しく作成したシークレットへのアクセス権を CSI ドライバーコントローラーに付与します。
oc -n ${CSI_DRIVER_NAMESPACE} create role access-secrets --verb=get,list,watch --resource=secrets oc -n ${CSI_DRIVER_NAMESPACE} create rolebinding --role=access-secrets default-to-secrets --serviceaccount=${CSI_DRIVER_NAMESPACE}:aws-efs-csi-driver-controller-saOpenShift Container Platform CLI で次のコマンドを実行して、アカウント B の EFS ID と以前に作成したシークレットを参照する新しいストレージクラスを作成します。
cat << EOF | oc apply -f - kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ${STORAGE_CLASS_NAME} provisioner: efs.csi.aws.com parameters: provisioningMode: efs-ap fileSystemId: ${CROSS_ACCOUNT_FS_ID} directoryPerms: "700" gidRangeStart: "1000" gidRangeEnd: "2000" basePath: "/dynamic_provisioning" csi.storage.k8s.io/provisioner-secret-name: ${SECRET_NAME} csi.storage.k8s.io/provisioner-secret-namespace: ${CSI_DRIVER_NAMESPACE} EOF