0%

前置条件,已经配置好k8s 及 dashboard

配置 metrics-server

根据dashboard的版本选择相应的版本

1
2
3
4
5
# updated: 2022 Feb 17
# 到github上找最新版
# https://github.com/kubernetes-sigs/metrics-server/releases
# 当前最新版是 v0.6.1
$ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.1/components.yaml

编辑 metrics-server 启动参数

1
2
3
4
5
# edit metric-server deployment to add the flags
# args:
# - --kubelet-insecure-tls=true
# - --kubelet-preferred-address-types=InternalIP
$ kubectl edit deploy -n kube-system metrics-server

追加args参数

1
2
3
args:
- --kubelet-insecure-tls=true
- --kubelet-preferred-address-types=InternalIP

查看运行状态

1
2
3
4
5
$ kubectl top node
error: metrics not available yet
$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
mtl-259577 314m 3% 7525Mi 23%

image-20210311233726160

进入dashboard,看到 metrics-server 生效了,pod的cpu、内存使用率能看到了。

  1. 通过docker-compose来构建
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
version: '3'
services:
# 这是一个mongod实例的配置
mongo-master:
container_name: mongo-master
image: mongo:4.4.3
environment:
MONGO_INITDB_ROOT_USERNAME: username
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: admin
volumes:
- ./data/db-master:/data/db
- ./data/key_file:/data/configdb/key_file
ports:
- 27017:27017
command: ["mongod", "--bind_ip_all", "--dbpath", "/data/db", "--journal", "--logappend", "--storageEngine", "wiredTiger", "--replSet", "rs", "--oplogSize", "128", "--auth", "--keyFile=/data/configdb/key_file"]
# 然后复制,一共三份配置,分别是主(master)/从(slave)/仲裁(arbiter)
mongo-slave:
container_name: mongo-slave
image: mongo:4.4.3
environment:
MONGO_INITDB_ROOT_USERNAME: username
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: admin
volumes:
- ./data/db-slave:/data/db
- ./data/key_file:/data/configdb/key_file
ports:
- 27017:27017
command: ["mongod", "--bind_ip_all", "--dbpath", "/data/db", "--journal", "--logappend", "--storageEngine", "wiredTiger", "--replSet", "rs", "--oplogSize", "128", "--auth", "--keyFile=/data/configdb/key_file"]
# arbiter
mongo-arbiter:
container_name: mongo-arbiter
image: mongo:4.4.3
environment:
MONGO_INITDB_ROOT_USERNAME: username
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: admin
volumes:
- ./data/db-arbiter:/data/db
- ./data/key_file:/data/configdb/key_file
ports:
- 27017:27017
command: ["mongod", "--bind_ip_all", "--dbpath", "/data/db", "--journal", "--logappend", "--storageEngine", "wiredTiger", "--replSet", "rs", "--oplogSize", "128", "--auth", "--keyFile=/data/configdb/key_file"]

注意:如果使用的是mac系统,因为mac系统是虚拟机的方式,是无法直接mount mongo的/data目录的,因此需要建立named volume方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '3'
services:
mongo-master:
# ... 省略
volumes:
- mongodata-master:/data/db
- ./data/key_file:/data/configdb/key_file
# for MacOSX
volumes:
mongodata-master:
driver: local
driver_opts:
o: bind
type: none
device: $PWD/data/db-master
  1. 创建key_file
1
2
openssl rand -base64 756 > key_file
chmod 400 key_file
  1. 运行服务及配置replica set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker-compose up
# 可以看到服务正常运行起来
# 然后通过命令进入到mongo控制台
docker exec -it mongo-master mongo
# 切换到admin
use admin
# 进行授权登录
db.auth({user: 'username', pwd: 'password'})
# 返回 1 代表授权成功,失败的情况会有错误信息
# 授权完成后,配置replica set
# 编写配置信息,因为这是在docker内部,hostname就是服务的名字,因此这里直接写 mongo-master这些就行。
config = {_id:"rs", version:1, members:[{_id:0, host:"mongo-master:27017", priority:5}, {_id:1, host:"mongo-slave:27017", priority:2}, {_id:2, host:"mongo-arbiter:27017", priority:3}]}
# 然后执行 rs.initiate 进行初始化
rs.initiate(config)
# 注意,如果初始化这里返回失败说auth fail什么,就是mongo服务器没有配置好auth参与与keyfile。群集的服务器之间通信,必须要配置好。

到此为止,配置完成。

  1. 使用golang,mongo-drive创建连接
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
func GetMongoClient(dbName string) *mongo.Client {
var uri string
if isDebug {
// debug下,是通过localhost访问replica set,通过参数connect=direct
uri := fmt.Sprintf("mongodb://%s:%[email protected]:27017/%s?connect=direct",
MONGO_USERNAME,
MONGO_PASSWORD,
dbName)
} else {
// 正式环境,MONGO_X_HOST 是服务器HOST或k8s/docker中的hostname。
uri := fmt.Sprintf("mongodb://%s:%[email protected]%s:%s,%s:%s,%s:%s/%s",
MONGO_USERNAME,
MONGO_PASSWORD,
MONGO_1_HOST,
MONGO_1_PORT,
MONGO_2_HOST,
MONGO_2_PORT,
MONGO_3_HOST,
MONGO_3_PORT,
dbName)
}
// 参数
clientOptions := options.Client().ApplyURI(uri)
clientOptions.SetMaxPoolSize(5)
clientOptions.SetAuth(options.Credential{
Username: MONGO_USERNAME,
Password: MONGO_PASSWORD,
})
clientOptions.SetReplicaSet("rs")
// 发起连接
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, clientOptions)
return client
}

k8s拉取带验证的镜像方式

  1. 创建验证信息
1
2
3
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
# 如果Pod是在namespace下面的,则要添加对应namespace
kubectl --namespace create secret ...
  1. 创建Pod增加使用验证信息
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: private-reg
spec:
containers:
- name: private-reg-container
image: <your-private-image>
imagePullSecrets:
- name: regcred

参考资料

https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/

前置说明,文章里有些配置依赖了或参考了之前部分文章的配置,不会展开详细说明,有不清楚的可以点击下面的文章看看。

Amazon EFS提供外部访问数据卷

Kubernetes部署NFS-Provisioner为NFS提供动态分配卷能力

安装与配置 Gitea + Drone

配置 Gitea

一、首先创建 gitea-deployment.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
# gitea-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: YOUR_NAMESPACE
spec:
replicas: 1
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:latest-rootless # 这里用rootless的镜像
ports:
- containerPort: 3000
name: http
volumeMounts:
- name: static-efs-pvc
mountPath: "/var/lib/gitea"
subPath: "gitea"
- name: static-efs-pvc
mountPath: "/etc/gitea"
subPath: "gitea/configdir"
restartPolicy: "Always"
volumes:
- name: static-efs-pvc
persistentVolumeClaim:
claimName: static-efs-pvc

注意的是

  1. 镜像用的是 rootless的,非rootless的其实也是可以的,但是要多配置一些。rootless的简单。
  2. 数据挂载两个地方,一个是 /var/lib/gitea ,gitea的数据库所在的地方。另一个是 /etc/gitea ,主要是存储里面 /etc/gitea/app.ini 配置文件。
  3. 毕竟gitea存的是代码,数据不能丢。所以作者存储在 Amazon 的 EFS 文件系统里面。通过NFS 方式挂载。然后设置回收策略是Retain。

二、配置 Service 与 Ingress

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
# gitea-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: YOUR_NAMESPACE
labels:
app: gitea
spec:
selector:
app: gitea
ports:
- port: 3000
targetPort: 3000
protocol: TCP
name: http
---
# gitea-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-ingress
namespace: YOUR_NAMESPACE
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec:
rules:
- host: YOUR_DOMAIN
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea
port:
number: 3000

三、配置运行及数据备份恢复

1
2
3
kubectl apply -f gitea-deployment.yaml
kubectl apply -f gitea-svc.yaml
kubectl apply -f gitea-ingress.yaml

打开域名,进入到首次安装的页面,git的配置默认参数就行。rootless的版本,使用的是内置git服务器。端口就是2222。其余的按自己需要填写。安装完成后,再次打开,看到主界面了。

进入磁盘看一下文件

image-20210104141655832

configdir目录里面会有 app.ini 的配置文件,这里后续可以自己去修改,然后重建pod就能生效。

data 目录存的是 gitea 的数据库

git 目录存的是仓库的数据。

数据备份与恢复

只需要整个数据目录复制走就行 , 注意,必须要使用 -p 参数,将目录权限也一并复制走。不然重建Pod后,文件权限不对无法加载。

1
cp -r -p static-pv/gitea backup/

数据恢复,或者修改设置后重新运行,只需要重建Pod就行,例如执行

1
2
kubectl delete -f gitea-deployment.yaml 
kubectl apply -f gitea-deployment.yaml

配置 Drone

一、配置drone服务器,作者将drone的数据目录放在与gitea的同级目录中。存储目录结构如下

static-efs-pv

|– gitea

|– drone

这样备份数据与一同恢复也看着方便,个人喜好。

下面是配置 drone-deployment.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
45
46
47
48
49
# drone-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone
namespace: YOUR_NAMESPACE
spec:
replicas: 1
selector:
matchLabels:
app: drone
template:
metadata:
labels:
app: drone
spec:
containers:
- name: drone
image: drone/drone:1.10.1
env:
- name: DRONE_GITEA_SERVER
value: https://YOUR_GITEA_HTTP_DOMAIN
- name: DRONE_GITEA_CLIENT_ID
value: GITEA里面创建应用的CLIENT_ID
- name: DRONE_GITEA_CLIENT_SECRET
value: GITEA里面创建应用的CLIENT_SECRET(可以使用 secret 方式加载)
- name: DRONE_RPC_SECRET
value: RPC通信的SECRET (参考前面配置Drone的文章)
- name: DRONE_SERVER_HOST
value: drone服务器的域名
- name: DRONE_SERVER_PROTO
value: https
# 下面是创建管理员权限的用户,按需设置
- name: DRONE_USER_CREATE
value: username:master,admin:true
ports:
- containerPort: 80
name: http
- containerPort: 443
name: https
volumeMounts:
- name: static-efs-pvc
mountPath: "/data"
subPath: "drone"
restartPolicy: "Always"
volumes:
- name: static-efs-pvc
persistentVolumeClaim:
claimName: static-efs-pvc

二、创建drone的rbac,以及 drone-runner

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
# drone-rbac.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: YOUR_NAMESPACE
name: drone
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- delete
- apiGroups:
- ""
resources:
- pods
- pods/log
verbs:
- get
- create
- delete
- list
- watch
- update

---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: drone
namespace: YOUR_NAMESPACE
subjects:
- kind: ServiceAccount
name: default
namespace: YOUR_NAMESPACE
roleRef:
kind: Role
name: drone
apiGroup: rbac.authorization.k8s.io
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
# drone-runner-deploymeny.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone-runner
namespace: YOUR_NAMESPACE
labels:
app.kubernetes.io/name: drone-runner
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: drone-runner
template:
metadata:
labels:
app.kubernetes.io/name: drone-runner
spec:
containers:
- name: runner
image: drone/drone-runner-kube:latest
ports:
- containerPort: 3000
env:
- name: DRONE_RPC_HOST
value: 你的drone服务器域名
- name: DRONE_RPC_PROTO
value: https
- name: DRONE_RPC_SECRET
value: RPC通信的SECRET
- name: DRONE_RUNNER_CAPACITY
value: "2"

三、最后就是 Service 与 Ingress

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
# drone-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: drone
namespace: YOUR_NAMESPACE
labels:
app: drone
spec:
selector:
app: drone
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
# drone-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: drone-ingress
namespace: YOUR_NAMESPACE
spec:
rules:
- host: drone服务器的域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: drone
port:
number: 80

四、配置运行

1
2
3
4
5
kubectl apply -f drone-rbac.yaml
kubectl apply -f drone-deployment.yaml
kubectl apply -f drone-svc.yaml
kubectl apply -f drone-ingress.yaml
kubectl apply -f drone-runner-deployment.yaml

观察Pod是否成功创建

image-20210104143638181

简单创建一个任务,看看是否能正确运行。Pipeline的方式需要使用Kubernetes的。

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
# Pipeline
---
kind: pipeline
type: kubernetes
name: test-pipeline
metadata:
namespace: YOUR_NAMESPACE
service_account_name: default

workspace:
path: /homespace/src

volumes:
- name: outdir
temp: {}

steps:

- name: Step-1
image: appleboy/drone-ssh
settings:
host: myhost
ssh_username: root
ssh_key:
from_secret: secret
script:
- echo 'hello'

加载运行,能看到Cluster中,会自动创建一个drone的Pod来处理任务。任务结束后,会自动销毁这个Pod。不过这个kube-runner的通信效率,只能说比 docker-runner 要慢得不少。不过还行,自动化关注的是本身任务的执行过程与链路是否顺畅,kube-runner能提供更加稳定安全的环境才是更重要的。

配置 Registry

一、配置私有Docker Registry,htpasswd方式身份验证

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
45
46
47
48
49
50
51
# docker-registry-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry
namespace: YOUR_NAMESPACE
spec:
replicas: 1
selector:
matchLabels:
app: docker-registry
template:
metadata:
labels:
app: docker-registry
spec:
containers:
- name: docker-registry
image: registry:2.7.1
env:
- name: REGISTRY_HTTP_ADDR
value: ":5000"
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: "/var/lib/registry"
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true"
# yum install httpd-tools, htpasswd -b2B htpasswd user passwd
- name: REGISTRY_AUTH
value: "htpasswd"
- name: REGISTRY_AUTH_HTPASSWD_REALM
value: "Registry Realm"
- name: REGISTRY_AUTH_HTPASSWD_PATH
value: "/auth/htpasswd"
ports:
- containerPort: 5000
name: http
volumeMounts:
- name: static-efs-pvc
mountPath: "/var/lib/registry"
subPath: "docker-registry"
- name: secret-htpasswd
mountPath: "/auth"
restartPolicy: "Always"
volumes:
- name: static-efs-pvc
persistentVolumeClaim:
claimName: static-efs-pvc
- name: secret-htpasswd
secret:
secretName: docker-registry-htpasswd
defaultMode: 0400

注意的地方

  1. 数据挂载目录有两个,一个是 /var/lib/registry 数据存储的地方,另一个是 /auth 可以自己配置,用来指定 htpasswd 文件
  2. 作者这里通过本机创建 htpasswd 文件后设置在 Secret 中,通过通过挂载到文件
  3. htpasswd的生成注意加密的方式,不然会报密码错误。
1
2
3
4
5
# 在centos中创建 htpasswd
yum install -y httpd-tools
htpasswd -b2B htpasswd 用户名 密码
# 然后将文件设置到 secret 中
kubectl --namespace YOUR_NAMESPACE create secret generic docker-registry-htpasswd --from-file=htpasswd

注意,如果是Debian系统,使用htpasswd注意加密方式,作者试过安装 apache2-utils中的htpasswd,加密方式是不同的,无法正常使用。

二、配置 Service 与 Ingress

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
# docker-registry-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: docker-registry
namespace: YOUR_NAMESPACE
labels:
app: docker-registry
spec:
selector:
app: docker-registry
ports:
- name: http
port: 5000
targetPort: 5000
---
# docker-registry-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docker-registry-ingress
namespace: YOUR_NAMESPACE
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "500m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec:
tls:
- hosts:
- registry的域名
secretName: secret-host-tls
rules:
- host: registry的域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: docker-registry
port:
number: 5000

因为作者是通过NodePort方式将Ingress暴露的。Ingress的443端口映射到本机的30443端口。因此外层的Proxy需要设置使用https的方式。(作者用的Cloudflare CDN -> Nginx -> Ingress)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 443 ssl http2;
server_name registry的域名;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
ssl_certificate 域名证书.pem;
ssl_certificate_key 域名证书.key;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1.2 TLSv1.3;

location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass https://127.0.0.1:30443;
}
}

同样, 通过kubectl 加载配置

1
2
3
kubectl apply -f docker-registry-deployment.yaml
kubectl apply -f docker-registry-svc.yaml
kubectl apply -f docker-registry-ingress.yaml

通过浏览器打开 registry的域名,看到提示输入密码,输入密码后通过,就是成功了。

然后可以试试通过 docker login 后,push镜像 ~

结语

逐渐将所有的服务,数据都迁移到k8s的自建群集中。数据统一存储在EFS中,通过群集的PV来控制分配。这样对数据迁移扩展来说都非常方便。而且统一接入后,服务的互通互访变得更加方便。

参考资料:

https://hub.docker.com/r/gitea/gitea

https://hub.docker.com/r/jkarlos/git-server-docker/

https://kubernetes.io/zh/docs/tasks/configmap-secret/managing-secret-using-kubectl/

数据是最重要的电子资产,但是在持有及维护的过程,需要消耗不少的成本。于是,不得不去探索廉价又安全的数据存储方案。

背景

在云时代,不论是G家,还是AWS等这些大厂,都提供了非常可靠好用的服务。但是,整套使用下来,即便是按量计费,也是一个比较大的花销。

而非大厂的服务提供商,往往能给出价格实惠的虚拟主机,独立主机。但便宜的机器多多少少会有不稳定的问题,而且数据安全上,往往我们无法去信任小商家。

因此,我们是否能使用大厂的存储服务,使用其他商家的主机呢?

经过初步的检索,大致得出这样一套方案去尝试。

数据存储:Amazon EFS。提供可伸缩的数据存储。

接入EFS的代理机:Lightsail 。使用Lightsail而不选 EC2,也是基于成本考虑。EC2更优,5刀/月。

主力机器:独服 4C 32G。25刀/月。

需要数据卷持久化的数据都存储在EFS中,这样基本机器损坏,被搬走等各种原因,数据在AWS上还是可靠的。

安装 amazon-efs-utils

非 Amazon Linux,需要安装 amazon-efs-utils,才能使用efs。

1
2
3
4
5
6
yum -y install git
git clone https://github.com/aws/efs-utils
yum -y install make rpm-build
cd efs-utils
make rpm
yum -y install ./build/amazon-efs-utils*rpm

创建 EFS

image-20201229235242942

通过管理面板创建EFS

image-20201229235334335

在Network面板中,能看到EFS的IP address,创建完成。

配置 VPC,建立对等链接

因为使用的不是EC2,而是Lightsail。默认情况下是无法直接连接Amazon的服务的。需要建立对等连接。

在Lightsail的面板里面,操作建立对等连接。

image-20201229235638796

勾选 “启用 VPC 对等” 即可

在 VPC 面板中,查看对等连接是否建立成功。

image-20201229235829671

配置 EFS 入站规则

image-20201230000452803

新增一个安全组,单独给EFS访问使用。

出站规则默认全放行。

入站规则,因为Lightsail的内网IP与EC2的不同,需要单独放行。添加 172.26.0.0/16 所有流量放行。

到此所有准备工作完成,可以挂载数据卷了。

Lightsail 挂载数据卷

1
2
3
mkdir -p /mnt/efs
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 172.31.22.169:/ /mnt/efs
# 如果没有返回错误,通过mount查看挂载情况,成功。

image-20201230000918942

配置 Nginx 代理 NFS TCP端口

1
2
3
4
5
6
7
8
9
10
stream {
upstream backend {
server 172.31.22.169:2049;
}
server {
listen 2049;
# 有需要的话,这里也可以做安全策略
proxy_pass backend;
}
}

在Lightsail的防火墙规则,放开2049端口。仅放开目标机器的ip。

image-20201230003254277

外部系统挂载数据卷

因为我的外部系统是Debian 10,也是需要安装 amazon-efs-utils。

1
2
3
4
5
apt-get -y install git binutils
git clone https://github.com/aws/efs-utils
cd efs-utils
./build-deb.sh
apt-get -y install ./build/amazon-efs-utils*deb

然后执行mount进行挂载, 只是将IP改成Lightsail的公网IP

1
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport $PUBLICIP:/ /mnt/efs

设置开机启动自动挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建目录
mkdir -p /aws-efs

# Edit /etc/fstab 追加
$PUBLICIP:/ /aws-efs nfs defaults,_netdev 0 0

# 可以用 mount 指令检测下是否写对
mount -fav
# mount -fav
/ : ignored
mount.nfs: timeout set for Tue Dec 29 17:36:13 2020
mount.nfs: trying text-based options 'vers=4.2,addr=$PUBLICIP,clientaddr=$PUBLICIP'
/aws-efs : successfully mounted

性能测试

通过dd指令读写2G数据进行对比

1
2
3
4
5
# write
time dd if=/dev/zero of=/mnt/efs/2g-dd-data bs=1M count=2048 conv=fsync

# read
time dd if=/mnt/efs/2g-dd-data of=/dev/null bs=1M count=2048 conv=fsync
  1. Lightsail 本地 IO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # time dd if=/dev/zero of=/root/2g-dd-data bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 33.3782 s, 64.3 MB/s

    real 0m33.386s
    user 0m0.011s
    sys 0m1.049s

    # time dd if=/mnt/efs/2g-dd-data of=/dev/null bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 32.5695 s, 65.9 MB/s

    real 0m32.578s
    user 0m0.000s
    sys 0m0.944s
  1. 本机EFS IO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # time dd if=/dev/zero of=/mnt/efs/2g-dd-data bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 19.2469 s, 112 MB/s

    real 0m19.254s
    user 0m0.000s
    sys 0m1.078s

    # time dd if=/mnt/efs/2g-dd-data of=/dev/null bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 19.3482 s, 111 MB/s

    real 0m19.352s
    user 0m0.005s
    sys 0m0.760s
  1. 远程主机 EFS IO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # time dd if=/dev/zero of=/mnt/efs/2g-dd-data bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB, 2.0 GiB) copied, 67.4047 s, 31.9 MB/s

    real 1m10.265s
    user 0m0.005s
    sys 0m1.225s

    # time dd if=/mnt/efs/2g-dd-data of=/dev/null bs=1M count=2048 conv=fsync
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB, 2.0 GiB) copied, 0.266611 s, 8.1 GB/s

    real 0m0.274s
    user 0m0.000s
    sys 0m0.266s

结语

整个流程下来,可以预期的是,在外网访问EFS是可行的。

然后从测试数据来看,即便在一个Lightsail性能非常差的机器上面,网络IO也会比本地IO性能优越很多。

但是在远程机器IO的读写测试就变得不准确了,而且写入速度来说,确实会有非常大的损失。这里的性能损失有多方面的原因。

即便我的另一个机房的主机,与AWS Lightsail的主机ping值是低于10ms的网络情况下。网络的读写也不高。这里nginx的反向代理参数没配置对,机器的性能等等也是原因。

从测试结果来看,即便选用低配的 EC2,这里的性能也会有可观的增长。那为什么选用LIghtsail,那还是为了省钱。。。

其实原本这套方案是想接入G家的GCP的Cloud Filestore的。但是G家的数据卷,最低配1T存储定量,不是按可用来算。月费用惊人。。。

最后, 测试数据文件赶紧删掉。占用是要花钱的 XD

image-20201230011852390


下期预告:Amazon-EFS提供外部访问数据卷(二)—— 配置传输过程数据加密