Tychlog

使用S3FS将AWS S3挂载到EC2上

terminal

最近有一个客户想在AWS EC2上安装SFTP服务,其实之前已经给他设置了AWS Transfer Family服务,可以直接为S3开通SFTP服务。不过这个客户觉得太贵,所以还是想在已经存在的EC2服务器上安装SFTP服务,并且连接到S3。本来以为很简单的事情,没想到花了一天时间,还差点把服务器弄坏了。

在EC2上安装S3FS

EC2也就是linux云服务器,为其安装SFTP其实很简单。创建一个专门的用户,生成ssh key,配置SSH,分配权限就可以了。

创建SFTP用户

创建SFTP的用户和用户组,并分配用户到组。最后修改用户目录权限。

$ groupadd sftp
$ useradd -d /home/mysftpuser -s /bin/false -G sftp mysftpuser
$ sudo chmod 700 /home/mysftpuser

接着编辑/etc/ssh/sshd_config

注释掉Subsystem sftp /usr/lib/openssh/sftp-server,换成Subsystem sftp internal-sftp

再添加下面的部分到最后

Match user mysftpuser
X11Forwarding no
AllowTcpForwarding no
ChrootDirectory /home/mysftpuser
ForceCommand internal-sftp

然后用命令sshd -t service sshd restart重启ssh服务就完成了。

但因为最后这一段是我从网上找到复制的,刚好有问题。重启ssh之后,实际上ssh已经挂了。直到过了一会我退出再重新登录的时候,AWS的默认用户都无法通过ssh登录到服务器了。这就导致了一个致命的问题,我没法登录服务器,也无法修复sshd_config文件了。修复的方法很麻烦,不算搜索过程,花了1小时多才算搞定。修复方法后面再说。

安装S3FS

S3FS全称是s3fs-fuse。该工具基于FUSE,可以把AWS的S3(对象存储)目录映射到Linux,MacOS或FreeBCD上。这里我的EC2是用的Amazon Linux,所以按这个环境进行安装,其他环境略有不同,可以查看Github上S3FS项目的官方文档,这里不做赘述。

按文档上的命令安装

sudo amazon-linux-extras install epel
sudo yum install s3fs-fuse

然后失败了,因为这个EC2有点老,是Amazon Linux1,无法使用epel。不得已只好手动安装。

sudo git clone https://github.com/s3fs-fuse/s3fs-fuse.git
cd s3fs-fuse
sudo ./autogen.sh
sudo ./configure
make

验证一下是否安装成功

s3fs --version
安装FUSE

然后还需要安装fuse,也同样手动安装。

sudo wget https://github.com/libfuse/libfuse/releases/download/fuse-2.9.7/fuse-2.9.7.tar.gz
sudo tar -xzvf fuse-2.9.7.tar.gz
sudo rm -f fuse-2.9.7.tar.gz
sudo mv fuse-2.9.7 fuse
cd fuse/
sudo ./configure --prefix=/usr
sudo make
sudo make install
sudo export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/lib64/pkgconfig/
sudo ldconfig

验证一下是否安装成功

pkg-config --modversion fuse

挂载S3 Bucket

创建SFTP的AWS配置文件

S3FS需要创建一个${HOME}/.passwd-s3fs文件,作为保存AWS秘钥的文件。

echo [ACCESS_KEY_ID]:[SECRET_ACCESS_KEY] > ${HOME}/.passwd-s3fs
chmod 600 ${HOME}/.passwd-s3fs

把ID和KEY替换成你AWS S3的ID和KEY,写入该文件就行。还有设置正确的权限。

另外由于我使用默认用户ec2-user登录和安装,和SFTP不是同一个用户,所以需要配置fuse,允许其他用户访问。在/etc下创建fuse.conf文件,然后写入:

# /etc/fuse.conf - Configuration file for Filesystem in Userspace (FUSE)

# Set the maximum number of FUSE mounts allowed to non-root users.
# The default is 1000.
#mount_max = 1000

# Allow non-root users to specify the allow_other or allow_root mount options.
user_allow_other

主要是user_allow_other选项就可以开启。

挂载S3

最后在挂载命令中用参数指明AWS的秘钥文件路径就可以了。

s3fs my-bucket /home/mysftpuser/myfolder -o passwd_file=${HOME}/.passwd-s3fs -o --allow-root -o dbglevel=info -f -o curldbg

要注意的是不要直接挂载到用户根目录,所以我特别加了一层myfolder,因为可能还需要添加.ssh之类的文件到根目录,避免在SFTP中共享出来。

进入myfoler中查看一下是否出现S3对应bucket的文件,或者创建一个,看是否同步即可验证挂载是否成功。

撤销挂载

如果要撤销挂载,S3FS并不提供特别的命令,直接使用umount命令即可。

umount /home/mysftpuser/myfolder

修复EC2的SSH

现在来说说如何修复SSH受损的EC2。

sshd_conf文件受损后,我再次登录时会收到connection refused的错误。由于这是一个普通的EC2,我也没有启用过串行控制台,所以只能选择“救援实例”的方式。

简单来说,就是创建另外一个EC2实例服务器作为救援实例,然后把受损的EC2的主硬盘卷拿出来当做数据盘挂载到这个救援实例上,再通过救援实例SSH登录上去修改数据盘中的sshd_conf文件。这个方法的唯一缺点是需要停止实例一小段时间,如果有实例存储之类的会在停止后丢失的东西,那就不太适合。

创建救援实例
  1. 选择与受损EC2实例相同的AMI创建救援实例,或者直接从受损实例创建一个snapshot,再用snapshot创建实例,保证其基本环境一致,而不会对挂载卷产生其他影响。
  2. 停止受损的EC2实例
  3. 在Volumn中分离受损卷(/dev/xvda 或 /dev/sda1)。
  4. SSH登录到救援实例上
  5. lsblk查看一下当前的卷
  6. 创建一个文件夹用于挂载受损卷
sudo mkdir /mnt/rescue
  1. 挂载受损根卷,xvdf1是刚才查询到的卷名
sudo mount -t xfs -o nouuid /dev/xvdf1 /mnt/rescue/
  1. 再次lsblk查询一下
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdf 202:80 0 8G 0 disk
└─xvdf1 202:81 0 8G 0 part /mnt/rescue
  1. 编辑修复sshd_conf文档
sudo vi /mnt/rescue/etc/ssh/sshd_config
  1. 卸载修复的卷
sudo umount /mnt/rescue/
  1. 在AWS控制台上将修复的卷从救援实例上分离,再以根卷形式挂载到原受损实例上。
  2. 重新启动原实例,SSH登录测试是否成功。
  3. 停止救援实例。

这样就修复好了,以后还是应该谨慎一些,其实每次修改sshd_conf的时候,在退出前另外开一个终端登录一下就可以避免这种情况。DevOps还是需要一颗过度谨慎的心。