🛠️ Demo thực tế: Làm việc với EBS & Instance Store Trên EC2 (script đầy đủ đi làm & đi thi)¶
Lesson Commands
Commands User¶
Instance 1¶
lsblk sudo file -s /dev/xvdf sudo mkfs -t xfs /dev/xvdf sudo file -s /dev/xvdf sudo mkdir /ebstest sudo mount /dev/xvdf /ebstest cd /ebstest sudo nano amazingtestfile.txt add a message save and exit ls -la
Reboot Instance 1¶
sudo reboot
Instance 1 After Reboot¶
df -k sudo blkid sudo nano /etc/fstab ADD LINE UUID=YOURUUIDHEREREPLACEME /ebstest xfs defaults,nofail sudo mount -a cd /ebstest ls -la
Instance 2¶
lsblk sudo file -s /dev/xvdf sudo mkdir /ebstest sudo mount /dev/xvdf /ebstest cd /ebstest ls -la
Instance 3¶
lsblk sudo file -s /dev/xvdf sudo mkdir /ebstest sudo mount /dev/xvdf /ebstest cd /ebstest ls -la
InstanceStoreTest¶
lsblk sudo file -s /dev/nvme1n1 sudo mkfs -t xfs /dev/nvme1n1 sudo file -s /dev/nvme1n1 sudo mkdir /instancestore sudo mount /dev/nvme1n1 /instancestore cd /instancestore sudo touch instancestore.txt
InstancStoreTest - After Restart¶
df -k its not there but we can mount it sudo mount /dev/nvme1n1 /instancestore cd /instancestore ls -la
InstanceStoreTest - After Stop/Start¶
sudo file -s /dev/nvme1n1
1. Bối cảnh & lưu ý chi phí¶
- Phần 1: Demo EBS & snapshot (trong Free Tier)
- Phần 2: Demo Instance Store (có thể mất phí, ~0.13$/giờ cho instance lớn, không Free Tier)
2. Chuẩn bị môi trường¶
- Đăng nhập AWS management account, chọn region N. Virginia (us-east-1)
- Dùng CloudFormation template để tạo sẵn 3 EC2 instances (2 ở AZ A, 1 ở AZ B)
- Mở EC2 Console > Instances, kiểm tra có 3 instance: instance-1a, instance-2a, instance-1ab
3. Kiểm tra EBS volumes¶
- EC2 Console > Elastic Block Store > Volumes
- Có 3 volume 8 GiB, mỗi cái gắn với 1 instance làm boot volume
4. Tạo EBS volume mới¶
- Create Volume
- Volume type: gp3
- Size: 10 GiB
- AZ: us-east-1a
- Tag: key =
Name, value =EBS test volume - Bấm Create volume, chờ trạng thái “available”
5. Attach volume vào EC2¶
- Chọn volume vừa tạo > Actions > Attach volume
- Chọn instance instance-1a (chỉ thấy instance cùng AZ)
- Device:
/dev/sdf(Linux có thể nhận là/dev/xvdf) - Bấm Attach
6. SSH/Connect vào EC2 instance¶
- EC2 Console > Instances > Chọn instance-1a > Connect > EC2 Instance Connect
- User:
ec2-user
7. Thao tác trên Linux (copy, paste từng dòng)¶
# Xem block devices
lsblk
# Kiểm tra file system (vd: trên xvdf)
sudo file -s /dev/xvdf
# Nếu trả về "data", chưa có file system
# Tạo file system (XFS)
sudo mkfs -t xfs /dev/xvdf
# Kiểm tra lại file system
sudo file -s /dev/xvdf
# Tạo mount point
sudo mkdir /EBS-test
# Mount volume vào thư mục
sudo mount /dev/xvdf /EBS-test
# Kiểm tra mount
df -h
# Chuyển vào thư mục mount
cd /EBS-test
# Tạo file test
sudo nano amazing-test-file.txt
# (Nhập nội dung, Ctrl+O để lưu, Ctrl+X để thoát)
# Hiển thị nội dung file
cat amazing-test-file.txt
# Liệt kê file
ls -l
# Reboot instance để test persistence
sudo reboot
# (Đợi vài phút, sau đó connect lại instance)
8. Kiểm tra lại sau reboot¶
- SSH lại instance, chạy:
- File vẫn còn, chứng minh EBS là persistent storage
9. Kiến thức bổ sung & mẹo đi thi¶
- Chỉ attach EBS volume vào instance cùng AZ
- EBS volume tách rời vòng đời EC2: Dừng/xóa instance, volume vẫn còn (trừ khi chọn delete on termination)
- EBS volume dùng được cho nhiều EC2 (sequential attach, không parallel)
- Instance Store: Chỉ add khi launch, mất data khi stop/start/terminate, chỉ dùng cho temp/cache
- Boot volume chỉ dùng EBS, không dùng instance store
- EBS volume có thể tạo từ snapshot, restore/migrate/clone sang AZ/region khác
10. Lệnh tham khảo khác¶
# Format ext4 thay cho xfs
sudo mkfs.ext4 /dev/xvdf
# Unmount volume
sudo umount /EBS-test
# Xóa volume khỏi EC2 (detach)
# AWS Console > Volumes > Actions > Detach
11. Lưu ý chi phí¶
- EBS gp3: ~$0.08/GB/tháng (10GB ~ $0.8/tháng)
- Instance store: Không tính riêng, đã include trong giá EC2
- Part 2 (instance store): Dùng instance lớn tốn ~0.13$/giờ
- Snapshot: ~$0.05/GB/tháng trên data thực dùng
Tóm tắt: - EBS: Persistent, attach/detach linh hoạt, backup được (snapshot), chỉ attach cùng AZ. - Instance store: Hiệu năng cao, ephemeral, chỉ add lúc launch, mất data khi stop/start, không backup. - Thực hành đủ quy trình: tạo, attach, mount, test persistence, reboot, detach, xóa...
stack cloudformation
Description: Animals4Life base VPC Template + 3 Public Instances
Parameters:
LatestAmiId:
Description: AMI for EC2 (default is latest AmaLinux2023)
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.16.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: a4l-vpc1
IPv6CidrBlock:
Type: AWS::EC2::VPCCidrBlock
Properties:
VpcId: !Ref VPC
AmazonProvidedIpv6CidrBlock: true
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: A4L-vpc1-igw
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
RouteTableWeb:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: A4L-vpc1-rt-web
RouteTableWebDefaultIPv4:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref RouteTableWeb
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableWebDefaultIPv6:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref RouteTableWeb
DestinationIpv6CidrBlock: '::/0'
GatewayId: !Ref InternetGateway
RouteTableAssociationWebA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetWEBA
RouteTableId: !Ref RouteTableWeb
RouteTableAssociationWebB:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetWEBB
RouteTableId: !Ref RouteTableWeb
RouteTableAssociationWebC:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref SubnetWEBC
RouteTableId: !Ref RouteTableWeb
SubnetReservedA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- !GetAZs ''
CidrBlock: 10.16.0.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 00::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-reserved-A
SubnetReservedB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 1
- !GetAZs ''
CidrBlock: 10.16.64.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 04::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-reserved-B
SubnetReservedC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 2
- !GetAZs ''
CidrBlock: 10.16.128.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 08::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-reserved-C
SubnetDBA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- !GetAZs ''
CidrBlock: 10.16.16.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 01::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-db-A
SubnetDBB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 1
- !GetAZs ''
CidrBlock: 10.16.80.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 05::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-db-B
SubnetDBC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 2
- !GetAZs ''
CidrBlock: 10.16.144.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 09::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-db-C
SubnetAPPA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- !GetAZs ''
CidrBlock: 10.16.32.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 02::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-app-A
SubnetAPPB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 1
- !GetAZs ''
CidrBlock: 10.16.96.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 06::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-app-B
SubnetAPPC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 2
- !GetAZs ''
CidrBlock: 10.16.160.0/20
AssignIpv6AddressOnCreation: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 0A::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-app-C
SubnetWEBA:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 0
- !GetAZs ''
CidrBlock: 10.16.48.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 03::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-web-A
SubnetWEBB:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 1
- !GetAZs ''
CidrBlock: 10.16.112.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 07::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-web-B
SubnetWEBC:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select
- 2
- !GetAZs ''
CidrBlock: 10.16.176.0/20
MapPublicIpOnLaunch: true
Ipv6CidrBlock: !Sub
- ${VpcPart}${SubnetPart}
- SubnetPart: 0B::/64
VpcPart: !Select
- 0
- !Split
- 00::/56
- !Select
- 0
- !GetAtt VPC.Ipv6CidrBlocks
Tags:
- Key: Name
Value: sn-web-C
IPv6WorkaroundSubnetWEBA:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBA
IPv6WorkaroundSubnetWEBB:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBB
IPv6WorkaroundSubnetWEBC:
Type: Custom::SubnetModify
Properties:
ServiceToken: !GetAtt IPv6WorkaroundLambda.Arn
SubnetId: !Ref SubnetWEBC
IPv6WorkaroundRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: !Sub ipv6-fix-logs-${AWS::StackName}
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- PolicyName: !Sub ipv6-fix-modify-${AWS::StackName}
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:ModifySubnetAttribute
Resource: '*'
IPv6WorkaroundLambda:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Code:
#import cfnresponse below required to send respose back to CFN
ZipFile: !Sub |
import cfnresponse
import boto3
def lambda_handler(event, context):
if event['RequestType'] is 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS)
return
responseValue = event['ResourceProperties']['SubnetId']
ec2 = boto3.client('ec2', region_name='${AWS::Region}')
ec2.modify_subnet_attribute(AssignIpv6AddressOnCreation={
'Value': True
},
SubnetId=responseValue)
responseData = {}
responseData['SubnetId'] = responseValue
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
Runtime: python3.9
Role: !GetAtt IPv6WorkaroundRole.Arn
Timeout: 30
Instance1:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: !Ref LatestAmiId
IamInstanceProfile: !Ref SessionManagerInstanceProfile
SubnetId: !Ref SubnetWEBA
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: A4L-EBS-INSTANCE1-AZA
Instance2:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: !Ref LatestAmiId
IamInstanceProfile: !Ref SessionManagerInstanceProfile
SubnetId: !Ref SubnetWEBA
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: A4L-EBS-INSTANCE2-AZA
Instance3:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: !Ref LatestAmiId
IamInstanceProfile: !Ref SessionManagerInstanceProfile
SubnetId: !Ref SubnetWEBB
SecurityGroupIds:
- !Ref InstanceSecurityGroup
Tags:
- Key: Name
Value: A4L-EBS-INSTANCE1-AZB
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupDescription: Enable SSH access via port 22 IPv4 & v6
SecurityGroupIngress:
- Description: Allow SSH IPv4 IN
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
- Description: Allow HTTP IPv4 IN
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- Description: Allow SSH IPv6 IN
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIpv6: '::/0'
SessionManagerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ssm:DescribeAssociation
- ssm:GetDeployablePatchSnapshotForInstance
- ssm:GetDocument
- ssm:DescribeDocument
- ssm:GetManifest
- ssm:GetParameter
- ssm:GetParameters
- ssm:ListAssociations
- ssm:ListInstanceAssociations
- ssm:PutInventory
- ssm:PutComplianceItems
- ssm:PutConfigurePackageResult
- ssm:UpdateAssociationStatus
- ssm:UpdateInstanceAssociationStatus
- ssm:UpdateInstanceInformation
Resource: '*'
- Effect: Allow
Action:
- ssmmessages:CreateControlChannel
- ssmmessages:CreateDataChannel
- ssmmessages:OpenControlChannel
- ssmmessages:OpenDataChannel
Resource: '*'
- Effect: Allow
Action:
- ec2messages:AcknowledgeMessage
- ec2messages:DeleteMessage
- ec2messages:FailMessage
- ec2messages:GetEndpoint
- ec2messages:GetMessages
- ec2messages:SendReply
Resource: '*'
SessionManagerInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !Ref SessionManagerRole
Outputs:
a4lvpc1:
Description: Animals4Life VPC1_ID
Value: !Ref VPC
Export:
Name: a4l-vpc1
a4lvpc1subnetweba:
Description: Animals4Life VPC1 SubnetWEBA
Value: !Ref SubnetWEBA
Export:
Name: a4l-vpc1-subnet-weba
a4lvpc1subnetwebb:
Description: Animals4Life VPC1 SubnetWEBB
Value: !Ref SubnetWEBB
Export:
Name: a4l-vpc1-subnet-webb
a4lvpc1subnetwebc:
Description: Animals4Life VPC1 SubnetWEBC
Value: !Ref SubnetWEBC
Export:
Name: a4l-vpc1-subnet-webc
a4lvpc1subnetappa:
Description: Animals4Life VPC1 SubnetAPPA
Value: !Ref SubnetAPPA
Export:
Name: a4l-vpc1-subnet-appa
a4lvpc1subnetappb:
Description: Animals4Life VPC1 SubnetAPPB
Value: !Ref SubnetAPPB
Export:
Name: a4l-vpc1-subnet-appb
a4lvpc1subnetappc:
Description: Animals4Life VPC1 SubnetAPPC
Value: !Ref SubnetAPPC
Export:
Name: a4l-vpc1-subnet-appc
a4lvpc1subnetdba:
Description: Animals4Life VPC1 SubnetDBA
Value: !Ref SubnetDBA
Export:
Name: a4l-vpc1-subnet-dba
a4lvpc1subnetdbb:
Description: Animals4Life VPC1 SubnetDBB
Value: !Ref SubnetDBB
Export:
Name: a4l-vpc1-subnet-dbb
a4lvpc1subnetdbc:
Description: Animals4Life VPC1 SubnetDBC
Value: !Ref SubnetDBC
Export:
Name: a4l-vpc1-subnet-dbc
a4lvpc1subnetreserveda:
Description: Animals4Life VPC1 SubnetReservedA
Value: !Ref SubnetReservedA
Export:
Name: a4l-vpc1-subnet-reserveda
a4lvpc1subnetreservedb:
Description: Animals4Life VPC1 SubnetReservedB
Value: !Ref SubnetReservedB
Export:
Name: a4l-vpc1-subnet-reservedb
a4lvpc1subnetreservedc:
Description: Animals4Life VPC1 SubnetReservedC
Value: !Ref SubnetReservedC
Export:
Name: a4l-vpc1-subnet-reservedc