🗻
CVATでS3をファイルシステムとしてマウントする
背景
CVATのデータソースとしてS3を使用する際、CloudStorageで接続する方法だと以下の問題がある。
- bucketを紐づける際、毎回manifest.jsonlを作成しないといけないのが面倒
- 一つのCloudStorageにGUIからだと5つまでしかmanifest.jsonlを紐付けれらない(OSSなのでSelf-hostedの場合自分でリミットを外せるが、問題無いのかは未調査)
- manifest.jsonl 5つまでの制約を回避するために、新規画像群をアップロードする際bucketを新規作成してそれをCloudStorageに紐づける運用も考えられるが、bucketが乱立し見た目が悪い
そこでCVATのdocsを読んでいたら、S3をマウントしてファイルシステムとして Connected file share から使用できる方法があったので、それを試す。
やること
- TerraformでEC2とS3を立ち上げる
- CVATをEC2上に立てる
- s3fsを使ってS3をマウントしてCVATの Connected file share からアクセスできるようにする
参考
環境
- OS: macOS Ventura 13.5
- AWS CLI: aws-cli/2.8.3 Python/3.9.11 Darwin/22.6.0 exe/x86_64 prompt/off
- tfenv: tfenv 3.0.0
- terraform: Terraform v1.6.6 on darwin_amd64
1. TerraformでS3とEC2を立てる
terraform用のディレクトリを作成
コマンドを実行
mkdir terraform
コマンドを実行
cd terraform
IAMユーザーを作成
AWS Consoleをぽちぽちして作成する
(僕はtmp-for-create-ec2-and-s3
という名前で作りました)
新規プロファイルを登録
コマンドを実行
aws configure --profile <profile_name>
-
<profile_name>
:tmp-for-create-ec2-and-s3
値を入力
AWS Access Key ID [None]: <aws_access_key_id>
AWS Secret Access Key [None]: <aws_secret_access_key>
Default region name [None]: <region_name>
Default output format [None]:
-
<aws_access_key_id>
: 正しい値を入力 -
<aws_secret_access_key>
: 正しい値を入力 -
<region_name>
:ap-northeast-1
確認
コマンドを実行
cat ~/.aws/config
出力例
...
[profile tmp-for-create-ec2-and-s3]
region = ap-northeast-1
コマンドを実行
cat ~/.aws/credentials
出力例
...
[tmp-for-create-ec2-and-s3]
aws_access_key_id = *****
aws_secret_access_key = *****
versions.tf
を作成
コマンドを実行
touch versions.tf
versions.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.49.0"
}
}
required_version = "~> 1.6.0"
}
provider "aws" {
profile = var.profile
}
variables.tf
を作成
コマンドを実行
touch variables.tf
variables.tf
variable "profile" {
type = string
}
variable "project" {
type = string
}
variable "environment" {
type = string
}
variable "vpc_cidr" {
type = string
description = "vpc cidrblock"
}
variable "subnet_cidr" {
type = string
description = "public subnet cidr"
}
terraform.tfvars
を作成
コマンドを実行
touch terraform.tfvars
terraform.tfvars
project = <project_name>
environment = "dev"
vpc_cidr = "10.0.0.0/16"
subnet_cidr = "10.0.1.0/24"
profile = <profile_name>
-
<project_name>
:"tmp-for-create-ec2-and-s3"
-
<profile_name>
:"tmp-for-create-ec2-and-s3"
network.tf
を作成
コマンドを実行
touch network.tf
network.tf
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
instance_tenancy = "default"
assign_generated_ipv6_cidr_block = false
tags = {
Name = "${var.project}-${var.environment}-vpc"
Project = var.project
Env = var.environment
}
}
# Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
availability_zone = <availability_zone>
cidr_block = var.subnet_cidr
map_public_ip_on_launch = true
tags = {
Name = "${var.project}-${var.environment}-public-subnet"
Project = var.project
Env = var.environment
Type = "public"
}
}
# Route table
resource "aws_route_table" "rtb" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project}-${var.environment}-rtb"
Project = var.project
Env = var.environment
}
}
# Route table と subnet の関連付け
resource "aws_route_table_association" "public_rtb" {
route_table_id = aws_route_table.rtb.id
subnet_id = aws_subnet.public.id
}
# Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project}-${var.environment}-igw"
Project = var.project
Env = var.environment
}
}
# Route table と IGW の関連付け
resource "aws_route" "rtb_igw_route" {
route_table_id = aws_route_table.rtb.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
# Security Group
resource "aws_security_group" "sg" {
name = "${var.project}-${var.environment}-sg"
description = "security group"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project}-${var.environment}-sg"
Project = var.project
Env = var.environment
}
}
-
<availability_zone>
:"ap-northeast-1a"
data.tf
を作成
コマンドを実行
touch data.tf
data.tf
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
ec2.tf
を作成
コマンドを実行
touch ec2.tf
ec2.tf
resource "aws_instance" "ec2" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.large"
subnet_id = aws_subnet.public.id
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.sg.id]
root_block_device {
volume_size = 20
}
tags = {
Name = "${var.project}-${var.environment}-ec2"
Project = var.project
Env = var.environment
}
}
s3.tf
を作成
コマンドを実行
touch s3.tf
s3.tf
resource "aws_s3_bucket" "s3" {
bucket = "${var.project}-${var.environment}-bucket"
}
terraform init
コマンドを実行
terraform init
出力
...
Terraform has been successfully initialized!
...
yeah!
terraform plan
コマンドを実行
terraform plan
出力
...
Plan: 9 to add, 0 to change, 0 to destroy.
yeah!!
terraform apply
コマンドを実行
terraform apply
出力
...
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
yeah!!!
2. bucketに画像をアップロードする
AWSのConsleでぽちぽちアップロードする
3. EC2 Instance Connectで接続してターミナルを起動
黒い画面が出てきたらok
4. CVATをインストールする
Docker と Docker Compose をインストール
「EC2上で実行」とあるコマンドは全て、先ほどEC2に接続したターミナルで実行します。
EC2上で実行
sudo apt-get update
EC2上で実行
sudo apt-get --no-install-recommends install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
EC2上で実行
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
EC2上で実行
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
EC2上で実行
sudo apt-get update
EC2上で実行
sudo apt-get --no-install-recommends install -y \
docker-ce docker-ce-cli containerd.io docker-compose-plugin
root権限無しにdockerを実行できるように設定
EC2上で実行
sudo groupadd docker
EC2上で実行
sudo usermod -aG docker $USER
ここで、設定を反映させるため、一度セッションを閉じて再度Connectする
CVAT を clone する
EC2上で実行
git clone https://github.com/opencv/cvat
EC2上で実行
cd cvat
CVAT_HOST
環境変数をセットする
EC2上で実行
export CVAT_HOST=<your-ip-address>
-
<your-ip-address>
: EC2 の public IP address
5. S3をマウントするための設定をする
s3fs
をインストールする
EC2上にEC2上で実行
sudo apt install s3fs
認証情報を設定する
EC2上で実行
echo <ACCESS_KEY_ID>:<SECRET_ACCESS_KEY> > ${HOME}/.passwd-s3fs
-
<ACCESS_KEY_ID>
: 正しい値にする -
<SECRET_ACCESS_KEY>
: 正しい値にする
EC2上で実行
chmod 600 ${HOME}/.passwd-s3fs
user_allow_other
のコメントアウトを外す
EC2上で実行
sudo vi /etc/fuse.conf
/etc/fuse.conf
...
user_allow_other # <- ここのコメントアウトを外す
...
マウント先のフォルダを作成する
EC2上で実行
mkdir images
マウントのためのコマンドを実行する
EC2上で実行
s3fs <bucket_name> <mount_point> -o allow_other -o passwd_file=${HOME}/.passwd-s3fs -o url=https://s3-<region>.amazonaws.com -o use_path_request_style
-
<bucket_name>
:tmp-for-create-ec2-and-s3-dev-bucket
-
<mount_point>
:/home/ubuntu/cvat/images
-
<region>
:ap-northeast-1
※ -o url=https://s3-<region>.amazonaws.com -o use_path_request_style
が無いとエラーが出る。(参照: https://github.com/s3fs-fuse/s3fs-fuse/issues/666#issuecomment-475407515)
マウントできているか確認
EC2上で実行
cat /etc/mtab | grep 's3fs'
出力
s3fs /home/ubuntu/cvat/images fuse.s3fs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000,allow_other 0 0
docker-compose.share.yml
を作成する
EC2上で実行
touch docker-compose.share.yml
EC2上で実行
vi docker-compose.share.yml
docker-compose.share.yml
services:
cvat_server:
volumes:
- cvat_share:/home/django/share:ro
cvat_worker_import:
volumes:
- cvat_share:/home/django/share:ro
cvat_worker_export:
volumes:
- cvat_share:/home/django/share:ro
cvat_worker_annotation:
volumes:
- cvat_share:/home/django/share:ro
volumes:
cvat_share:
driver_opts:
type: none
device: <path_to_mount>
o: bind
-
<path_to_mount>
:/home/ubuntu/cvat/images
6. CVATを立ち上げる
EC2上で実行
docker compose -f docker-compose.yml -f docker-compose.share.yml up -d
7. superuser を設定する
EC2上で実行
docker exec -it cvat_server bash -ic 'python3 ~/manage.py createsuperuser'
8. connected file share 内の画像で task を作ってみる
http://<your-ip-address>:8080
にアクセス
成功🎉
9. お片付け
tmp-for-create-ec2-and-s3-dev-bucket
)を空にする
bucket(AWS Console でぽちぽちして空にする
Terraformで作った環境を削除
ローカルで実行
terraform destroy
作ったIAM userを削除
AWS Console でぽちぽちして削除する
登録したprofileを削除
config内の記述を削除する
ローカルで実行
vi ~/.aws/config
~/.aws/config (例)
...
[profile tmp-for-create-ec2-and-s3] # <- この行を削除
region = ap-northeast-1 # <- この行を削除
credentials内の記述を削除する
ローカルで実行
vi ~/.aws/credentials
~/.aws/credentials (例)
...
[tmp-for-create-ec2-and-s3] # <- この行を削除
aws_access_key_id = ***** # <- この行を削除
aws_secret_access_key = ***** # <- この行を削除
terraform
ディレクトリを削除
ローカルで実行
cd ../
ローカルで実行
rm -rf terraform
Discussion