EKS 挂载S3fs 文件系统

前言

对于原来使用本地目录访问数据的应用程序,比如使用本地磁盘或网络共享盘保存数据的应用系统,如果用户希望把数据放到S3上,则需要修改数据的访问方式,比如修改为使用AWS SDK 或CLI访问S3中存储的数据。

同时在AWS上实现kubernetes集群是很多用户的需求,无论是使用托管服务还是自建kubernetes集群,存储都是kubernetes集群搭建的重点。并且docker的部署方式也让客户程序减少了对于底层环境的依赖。为了让用户原来的应用系统能在不做修改的情况下直接使用Amazon S3服务,需要把S3存储桶作为目录挂载到用户EKS集群中的worker结点上。

这里主要介绍如何利用S3fs将S3存储桶在EKS平台上以sidecar方式挂载到Amazon EKS的worker实例上的pod中,挂载后需要读写此存储桶的pod都可以对此桶进行读写,以实现共享存储功能。

什么是 S3FS

S3fs是基于FUSE的文件系统,允许Linux和Mac Os X 挂载S3的存储桶在本地文件系统,S3fs能够保持对象原来的格式,S3FS是POSIX的大子集,包括读/写文件、目录、符号链接、模式、uid/gid和扩展属性,与AmazonS3、Google云存储和其他基于S3的对象存储兼容。关于S3fs的详细介绍,请参见:https://github.com/s3fs-fuse/s3fs-fuse

K8s pod 中使用S3FS架构图

图片1

前提条件

  1. 已经搭建完成k8s 集群;
  2. 镜像仓库,存放构建的kube-s3 镜像;
  3. 准备好S3存储桶;
  4. IAM用户具备S3桶读写权限。

实施过程

1.下载S3fs 代码

下载S3fs代码到kubectl 可与k8s master结点交互执行的机器上

1
# git clone https://github.com/freegroup/kube-s3.git

2.配置configmap

将configmap_secrets_template.yaml复制到configmap_secrets.yaml并将您的aksk,s3 backet放置在正确的位置

1
2
3
4
5
6
7
8
9
10
11
12
# cd kube-s3/yaml/
# vim configmap_secrets.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: s3-config
data:
S3_BUCKET: test-backut
AWS_KEY: AKIAU4PH111111111111
AWS_SECRET_KEY: +4HWAoYoRRAGawIy0RLE8pyyE205111111111111

# kubectp apply -f configmap_secrets.yaml

这里挂载的是test-backut存储桶的根目录,如果要挂载test-backut/data目录,可以写成test-backut:/data

3.构建镜像

如果懒得构建,也可以使用我构建好的镜像:chenzz/kube-s3:v1.0

3.1 构建镜像前修改Dockerfile 将 apk del git automake autoconf; 这行注示掉,否则镜像运行时会报错找不到aclocal命令

3.2 将apline镜像版本修改为alpine:3.13,默认latest 会报 make: /bin/sh: Operation not permitted 错误

3.3 根据情况修改build.sh脚本,允许执行哪些步骤,以及配置好镜像仓库地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env bash

VERSION=$1
PROJECT=kube-s3
REPOSITORY=docker.chenzz.local/public

set -e

echo '>>> Building new image'
docker build --no-cache=true -t $REPOSITORY/$PROJECT:$VERSION . | tee /tmp/docker_build_result.log
RESULT=$(cat /tmp/docker_build_result.log | tail -n 1)
if [[ "$RESULT" != *Successfully* ]];
then
exit -1
fi

echo '>>> Push new image'
docker push $REPOSITORY/$PROJECT:$VERSION

kubectl apply -f ./yaml/configmap_secrets.yaml
# Apply the YAML passed into stdin and replace the version string first
#cat ./yaml/daemonset.yaml | sed "s/$REPOSITORY\/$PROJECT/$REPOSITORY\/$PROJECT:$VERSION/g" | kubectl apply -f -

3.4 修改 docker-entrypoint.sh

经过测试,当需要把另一个存储桶挂载到集群中以替换test-backut存储桶时,会报错HTTP response code 403, returning EPERM. 根据错误google查找时可能有人会告诉你添加-o url=https://s3-us-west-1.amazonaws.com参数可以解决问题(s3-us-west-1是你AWS所在区域),但在我的环境中并没有得到解决,需要修改docker-entrypoint.sh文件添加-o passwd_file="/etc/passwd-s3fs"参数明确指定IAM key文件位置

1
/usr/bin/s3fs ${S3_BUCKET} ${MNT_POINT} -d -d -f -o passwd_file="/etc/passwd-s3fs" -o endpoint=${S3_REGION},allow_other,retries=5

3.5 构建镜像

1
# ./build.sh v1.0

4.部署

我这里将构建和部署分开了,部署的时候需要修改yaml/daemonset.yaml文件中的镜像名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# cat yaml/daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: s3-provider
name: s3-provider
spec:
selector:
matchLabels:
app: s3-provider
template:
metadata:
labels:
app: s3-provider
spec:
containers:
- name: s3fuse
image: docker.chenzz.local/public/kube-s3:v1.0
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","umount -f /var/s3"]
securityContext:
privileged: true
capabilities:
add:
- SYS_ADMIN
# use ALL entries in the config map as environment variables
envFrom:
- configMapRef:
name: s3-config
volumeMounts:
- name: devfuse
mountPath: /dev/fuse
- name: mntdatas3fs
mountPath: /var/s3:shared
volumes:
- name: devfuse
hostPath:
path: /dev/fuse
- name: mntdatas3fs
hostPath:
path: /mnt/data-s3-fs
1
# kubectl apply -f yaml/daemonset.yaml

检查pod是否正常运行

1
2
3
4
5
6
# kubectl get pod |grep s3
s3-provider-4h2bv 1/1 Running 0 17m
s3-provider-6k28f 1/1 Running 0 17m
s3-provider-75vln 1/1 Running 0 17m
s3-provider-b5qzv 1/1 Running 0 17m
s3-provider-c5lk4 1/1 Running 0 17m

也可以去任意node节点查看S3fs挂载情况

1
2
3
4
# df -hT|egrep -v "tmpfs|overlay"
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs 20G 11G 9.6G 52% /
s3fs fuse.s3fs 256T 0 256T 0% /mnt/data-s3-fs

S3fs已经挂载到了/mnt/data-s3-fs目录

注:如果在配置和挂载过程中遇到问题,可以使用docker方式运行进行调试

1
# docker run -it --name=s3 --cap-add SYS_ADMIN --device /dev/fuse -v /mnt/data-s3-fs:/var/s3:shared docker.chenzz.local/public/kube-s3:v1.0 /bin/bash

进入容器声明挂载所需参数

1
2
3
4
5
6
7
8
# export AWS_KEY=***********
# export AWS_SECRET_KEY=************
# export S3_BUCKET='test-backut'
# export AWSACCESSKEYID=${AWSACCESSKEYID:-$AWS_KEY}
# export AWSSECRETACCESSKEY=${AWSSECRETACCESSKEY:-$AWS_SECRET_KEY}

# echo "${AWS_KEY}:${AWS_SECRET_KEY}" > /etc/passwd-s3fs
# chmod 0400 /etc/passwd-s3fs
1
# /usr/bin/s3fs ${S3_BUCKET} ${MNT_POINT} -d -d -f -o f2 -o passwd_file=/etc/passwd-s3fs,allow_other,retries=5

5.验证S3fs

创建demo pod来验证S3fs是否可以正常工作

1
2
3
# kubectl apply -f yaml/example_pod.yaml
# kubectl get pod |grep test
test-pd 1/1 Running 0 15m
1
2
3
4
5
6
7
8
9
10
# kubectl exec -ti test-pd sh
# df -hT
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 100G 5.3G 95G 6% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 7.8G 0 7.8G 0% /sys/fs/cgroup
s3fs fuse.s3fs 256T 0 256T 0% /var/s3
/dev/nvme0n1p1 xfs 100G 5.3G 95G 6% /etc/hosts
shm tmpfs 64M 0 64M 0% /dev/shm
tmpfs tmpfs 7.8G 12K 7.8G 1% /run/secrets/kubernetes.io/serviceaccount

S3fs挂载到了pod中的/var/s3目录下,下面来创建文件测试S3fs

1
2
3
4
5
6
7
8
9
# dd if=/dev/zero of=/var/s3/data1/test1G-3 bs=1M count=1000
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB) copied, 8.80683 s, 119 MB/s

# dd if=/dev/zero of=/var/s3/data1/test4G-3 bs=1M count=4000
4000+0 records in
4000+0 records out
4194304000 bytes (4.2 GB) copied, 63.0417 s, 66.5 MB/s

因为k8s运行在AWS,所以挂载S3fs可以得到不错的写入速度,至于AWS之外的环境就没有做测试了,感兴趣的朋友可以测试一下

然后去S3页面查看文件是否存在

图片2

S3fs 的不足

通常,S3 无法提供与本地文件系统相同的性能或语义。进一步来说:

  • 随机写入或附加到文件需要重写整个对象,优化多部分上传副本
  • 由于网络延迟,元数据操作(例如列出目录)性能不佳
  • 非 AWS 提供商可能具有最终一致性,因此读取可以暂时产生陈旧数据(AWS自 2020 年 12 月起提供先写后读一致性)
  • 没有文件或目录的原子重命名
  • 安装同一个存储桶的多个客户端之间没有协调
  • 没有硬链接
  • inotify 仅检测本地修改,而不检测其他客户端或工具的外部修改
-------------本文结束感谢您的阅读-------------