当前位置: 首页 > news >正文

FastDFS分布式储存

介绍

随着文件数据的越来越多,通过tomcat或nginx虚拟化的静态资源文件在单一的一个服务器节点内是存不下的,如果用多个节点来存储也可以,但是不利于管理和维护,所以我们需要一个系统来管理多台计算机节点上的文件数据,这就是分布式文件系统。
分布式文件系统是一个允许文件通过网络在多台节点上分享的文件系统,多台计算机节点共同组成一个整体,为更多的用户提供分享文件和存储空间。比如常见的网盘,本质就是一个分布式的文件存储系统。虽然我们是一个分布式的文件系统,但是对用户来说是透明的,用户使用的时候,就像是访问本地磁盘一样。
分布式文件系统可以提供冗余备份,所以容错能力很高。 系统中有某些节点宕机,但是整体文件服务不会停止,还是能够为用户提供服务,整体还是运作的,数据也不会丢失。分布式文件系统的可扩展性强,增加或减少节点都很简单,不会影响线上服务,增加完毕后会发布到线上,加入到集群中为用户提供服务。
分布式文件系统可以提供负载均衡能力,在读取文件副本的时候可以由多个节点共同提供服务,而且可以通过横向扩展来确保性能的提升与负载。

FastDFS 是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

FastDFS 原理

FastDFS 是一个开源的轻量级分布式文件系统,纯 C 实现,目前提供了 C、Java 和 PHP API。功能包括:文件存储,文件同步,文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。
FastDFS 系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。client 请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载,在底层存储上通过逻辑的分组概念,使得通过在同组内配置多个 Storage,从而实现软 RAID10。

 常见术语

tracker:追踪者服务器,主要用于协调调度,可以起到负载均衡的作用,记录storage的相关状态信息。
storage:存储服务器,用于保存文件以及文件的元数据信息。
group:组,同组节点提供冗余备份,不同组用于扩容。
mata data:文件的元数据信息,比如长宽信息,图片后缀,视频的帧数等。

FastDFS 架构

Tracker server

跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 Storage server 和 group,

每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。tracker 上的元信息都是由 storage 汇报的信息生成的,本身不需要持久化任何数据,这样使得 tracker 非常容易扩展,直接增加 tracker 机器即可扩展为 tracker cluster 来服务,cluster 里每个 tracker 之间是完全对等的,所有的 tracker 都接受 stroage 的心跳信息,生成元数据信息来提供读写服务,tracker 根据 storage 的心跳信息,建立 group=>[storage server list] 的映射表。

Storage server

存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内部可以有多台 storage server,数据互为备份。客户端上传的文件最终存储在 storage 服务器上,Storage server 没有实现自己的文件系统,而是利用操作系统的文件系统来管理文件,可以将 storage 称为存储服务器。storage 可配置多个数据存储目录,比如有 10 块磁盘,分别挂载在 /data/disk1 - /data/disk10,则可将这 10 个目录都配置为 storage 的数据存储目录。

 Client

客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。FastDFS 向使用者提供基本文件访问接口,比如 upload、download、append、delete 等,以客户端库的方式提供给用户使用。

文件上传

Storage server 会连接集群中所有的 Tracker server,定时向他们报告自己的状态,包括

磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。

上传的内部机制如下:

选择 tracker server

当集群中不止一个 tracker server 时,由于 tracker 之间是完全对等的关系,客户端在 upload 文件时可以任意选择一个 trakcer。当 tracker 接收到 upload file 的请求时,会为该文件分配一个可以存储该文件的 group,支持如下选择 group 的规则:

  • Round robin,所有的 group 间轮询
  • Specified group,指定某一个确定的 group
  • Load balance,剩余存储空间多 group 优先

选择 storage server

当选定 group 后,tracker 会在 group 内选择一个 storage server 给客户端,支持如下选择 storage 的规则:

  • Round robin,在 group 内的所有 storage 间轮询
  • First server ordered by ip,按 ip 排序
  • First server ordered by priority,按优先级排序(优先级在 storage 上配置

选择 storage path

当分配好 storage server 后,客户端将向 storage 发送写文件请求,storage 将会为文件分配一个数据存储目录,支持如下规则:

  • Round robin,多个存储目录间轮询
  • 剩余存储空间最多的优先

生成 Fileid(文件标识)

选定存储目录之后,storage 会为文件生一个 Fileid,由 storage server ip、文件创建时间、文件大小、文件 crc32 和一个随机数拼接而成,然后将这个二进制串进行 base64 编码,转换为可打印的字符串。选择两级目录 当选定存储目录之后,storage 会为文件分配一个 fileid,每个存储目录下有两级 256*256 的子目录,storage 会按文件 fileid 进行两次 hash(猜测),路由到其中一个子目录,然后将文件以 fileid 为文件名存储到该子目录下。

生成文件名

当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由 group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

文件下载

跟 upload file 一样,在 download file 时客户端可以选择任意 tracker server。tracker发送 download 请求给某个 tracker,必须带上文件名信息,tracke 从文件名中解析出文件的 group、大小、创建时间等信息,然后为该请求选择一个 storage 服务器用来读请求。

定位文件

客户端上传文件后存储服务器将文件 ID返回给客户端,此文件 ID用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

  • 组名:文件上传后所在的 storage 组名称,在文件上传成功后有 storage 服务器返回,需要客户端自行保存。
  • 虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项 store_path * 对应。如果配置了 store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
  • 数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
  • 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

知道 FastDFS FID 的组成后,我们来看看 FastDFS 是如何通过这个精巧的 FID 定位到需要访问的文件:

定位文件所在的 group

通过组名 tracker 能够很快的定位到客户端需要访问的存储服务器组,并将选择合适的存储服务器提供客户端访问

定位文件位置

存储服务器根据 “文件存储虚拟磁盘路径” 和 “数据文件两级目录” 可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件

同步时间管理

当一个文件上传成功后,客户端马上发起对该文件下载请求(或删除请求)时,tracker 是如何选定一个适用的存储服务器呢? 其实每个存储服务器都需要定时将自身的信息上报给 tracker,这些信息就包括了本地同步时间(即,同步到的最新文件的时间戳)。而 tracker 根据各个存储服务器的上报情况,就能够知道刚刚上传的文件,在该存储组中是否已完成了同步。同步信息上报如下图:

写文件时,客户端将文件写至 group 内一个 storage server 即认为写文件成功,storage server 写完文件后,会由后台线程将文件同步至同 group 内其他的 storage server。

每个 storage 写文件后,同时会写一份 binlog,binlog 里不包含文件数据,只包含文件名等元信息,这份 binlog 用于后台同步,storage 会记录向 group 内其他 storage 同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有 server 的时钟保持同步。

storage 的同步进度会作为元数据的一部分汇报到 tracker 上,tracke 在选择读 storage 的时候会以同步进度作为参考。比如一个 group 内有 A、B、C 三个 storage server,A 向 C 同步到进度为 T1(T1 以前写的文件都已经同步到 B 上了),B 向 C 同步到时间戳为 T2(T2 > T1),tracker 接收到这些同步进度信息时,就会进行整理,将最小的那个做为 C 的同步时间戳,本例中 T1 即为 C 的同步时间戳为 T1(即所有 T1 以前写的数据都已经同步到 C 上了);同理,根据上述规则,tracker 会为 A、B 生成一个同步时间戳。

集成 Nginx

FastDFS 通过 Tracker 服务器,将文件放在 Storage 服务器存储,但是同组存储服务器

之间需要进入文件复制,有同步延迟的问题。

假设 Tracker 服务器将文件上传到了 192.168.4.125,上传成功后文件 ID 已经返回给客户端。此时 FastDFS 存储集群机制会将这个文件同步到同组存储 192.168.4.126,在文件还没有复制完成的情况下,客户端如果用这个文件 ID 在 192.168.4.126 上取文件,就会出现文件无法访问的错误。

而 fastdfs-nginx-module 可以重定向文件连接到文件上传时的源服务器取文件,避免客户端由于复制延迟导致的文件无法访问错误。
另外,使用 nginx 反向代理后,后端可以以 HTTP 请求的方式来访问文件资源。访问 nginx 反向代理 + 上传文件时的 ID

FastDFS 部署

FastDFS 下载地址:https://github.com/happyfish100
(1)fastdfs
(2)libfastcommon
(3)fastdfs-nginx-module
(4)nginx-1.18.0

资源列表

操作系统配置主机名IP备注
OpenEuler242C4Gtracker01192.168.10.101跟踪服务器 1
OpenEuler242C4Gtracker02192.168.10.102跟踪服务器 2
OpenEuler242C4Gstorage1192.168.10.103存储服务器 1
OpenEuler242C4Gstorage2192.168.10.104存储服务器 2
OpenEuler242C4Gnginx192.168.10.105代理服务器

基础环境

关闭防火墙

systemctl stop firewalld
systemctl disable firewalld

关闭内核安全机制

setenforce 0
sed -i "s/SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config

安装 libfastcommon

在所有节点安装(代理服务器除外)

yum -y install  zlib-devel gcc* libtool  pcre-devel
yum -y install libevent
tar zxvf libfastcommon-1.0.36.tar.gz
cd libfastcommon-1.0.36./make.sh  # 编译
./make.sh install  # 安装cd /usr/lib64
ll libfast*  # 查看所有以libfast开头的内容
cp libfastcommon.so /usr/lib  #覆盖原有模块文件

 安装编译 FastDFS

在所有节点安装(代理服务器除外)

tar zxvf fastdfs-5.11.tar.gz
cd fastdfs-5.11./make.sh
./make.sh install

 配置 tracker 服务器

Tracker 节点操作
修改配置文件

cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf
vi /etc/fdfs/tracker.conf
# 修改的内容如下:
disabled=false           # 启用配置文件
port=22122               # tracker服务器端口(默认22122)
base_path=/fastdfs/tracker  # 存储日志和数据的根目录
store_group=group1       #设置存储组名称

创建 base_path 指定的目录

mkdir -p /fastdfs/tracker

启动 tracker 服务器

/etc/init.d/fdfs_trackerd start

初次启动,会在 /fastdfs/tracker 目录下生成 logs、data 两个目录
备注:
重启服务

fdfs_trackerd /etc/fdfs/tracker.conf restart  # 重启服务

配置 Storage 服务

修改配置文件

cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf
vi /etc/fdfs/storage.conf
# 修改的内容如下:
disabled=false                  # 启用配置文件
port=23000                      # storage服务端口
base_path=/fastdfs/storage      # 数据和日志文件存储根目录
store_path0=/fastdfs/storage    # 第一个存储目录
tracker_server=192.168.10.101:22122  # tracker服务器IP和端口
tracker_server=192.168.10.102:22122  # tracker服务器IP和端口
group_name=group1               #需要和tracker中的组名保持一致
http.server_port=8888           # http访问文件的端口

创建基础数据目录

mkdir -p /fastdfs/storage

启动 storage 服务器,并检查进程

/etc/init.d/fdfs_storaged start
ps -ef | grep fdfs_storaged

备注:
重启用如下命令

fdfs_storaged /etc/fdfs/storage.conf restart

配置 client

在任意一个 Fastdfs 节点上配置

cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
mkdir -p /fastdfs/tracker
vi /etc/fdfs/client.conf

在任意一个 Fastdfs 节点上配置

cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
mkdir -p /fastdfs/tracker
vi /etc/fdfs/client.conf
# 修改以下配置,其它保持默认
base_path=/fastdfs/tracker
tracker_server=192.168.10.101:22122  # tracker服务器IP和端口
tracker_server=192.168.10.102:22122  # tracker服务器IP和端口

测试上传文件

[root@localhost ~]# fdfs_upload_file /etc/fdfs/client.conf  logo.jpg
group1/M00/00/00/wKgkaGadD5aAZbIjAAFMnKMUrMI833.jpg
dfs_test /etc/fdfs/client.conf upload logo.jpg
# FastDFS 客户端测试程序版本等信息
This is FastDFS client test program v5.11
Copyright (C) 2008, Happy Fish / YuQing
FastDFS may be copied only under the terms of the GNU General
Public License V3, which may be found in the FastDFS source kit.
Please visit the FastDFS Home Page http://www.csource.org/
for more detail.# 调试信息,包含基础路径、超时时间等配置
[2025-05-06 20:27:21] DEBUG - base_path=/fastdfs/tracker, connect_timeout=30,
network_timeout=60,             tracker_server_count=2,             anti_steal_token=0,
anti_steal_secret_key           length=0,             use_connection_pool=0,
g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0# 查询存储服务器列表信息
tracker_query_storage_store_list_without_group:server 1. group_name=, ip_addr=192.168.10.103, port=23000server 2. group_name=, ip_addr=192.168.10.104, port=23000# 存储上传(主服务器)相关信息,含组名、远程文件名、源IP、文件时间戳、大小、CRC32 等
group name=group1, ip addr=192.168.10.103, port=23000
storage_upload_by_filename
group_name=group1, remote_filename=M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895.jpg
source ip address: 192.168.10.103
file timestamp=2025-05-06 20:27:21
file size=85148
file crc32=2736041154
example file url: http://192.168.10.103/group1/M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895.jpg# 存储上传(从服务器,这里看逻辑是生成了带 big 后缀的文件 )相关信息 
storage_upload_slave_by_filename
group_name=group1,
remote_filename=M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895_big.jpg
source ip address: 192.168.10.103
file timestamp=2025-05-06 20:27:21
file size=85148
file crc32=2736041154
example file url: http://192.168.10.103/group1/M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895_big.jpg

注意:

group1/M00: 此目录是生成的虚拟目录,

文件存储的目录为: /fastdfs/storage/data/00/00

[root@localhost ~]# ll /fastdfs/storage/data/00/00
总用量 260
-rw-r--r--. 1 root root 85148 7月  21 21:39 wKgKaGadD5aAZBj1AAFmKMuMrMI833.jpg
-rw-r--r--. 1 root root 85148 7月  21 21:38 wKgKZ2gaACmAb6raAAFmKMuMrMI895_big.jpg
-rw-r--r--. 1 root root    49 7月  21 21:38 wKgKZ2gaACmAb6raaAAFmKMuMrMI895_big-m
-rw-r--r--. 1 root root 85148 7月  21 21:38 wKgKZ2adD0aAY5f4AAFmKMuMrMI096.jpg

在所有 storage 节点安装 fastdfs-nginx-module 和 nginx

FastDFS 通过 Tracker 服务器,将文件放在 Storage 服务器存储,但是同组存储 服务器之间需要进入文件复制,有同步延迟的问题。假设 Tracker 服务器将文件上传到 了 storagel,上传成功后文件 ID 已经返回给客户端。此时 FastDFS 存储集群机制 会将这个文件同步到同组存储 storage02,在文件还没有复制完成的情况下,客户端如果用 这个文件 ID 在 storage02 上取文件,就会出现文件无法访问的错误。 而 fastdfs-nginx-module 可以重定向文件连接到源服务器取文件,避免客户端由于复制 延迟导致的文件无法访问错误。(解压后的 fastdfs-nginx-module 在 nginx 安装时使 用) storage 上的 nginx 是用于访问具体的文件,并且需要使用 fastdfs 模块。

解压并修改 fastdfs-nginx-module 的配置文件

[root@localhost ~]# ll /fastdfs/storage/data/00/00
tar xvf fastdfs-nginx-module_v1.16.tar.gz
cd fastdfs-nginx-module/src
vi config
ngx_addon_name=ngx_http_fastdfs_module
HTTP_MODULES="$HTTP_MODULES ngx_http_fastdfs_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $addon_dir/ngx_http_fastdfs_module.c"
CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"
CORE_LIBS="$CORE_LIBS -L/usr/lib -lfastcommon -lfastdfsclient"
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FI

编译安装 Nginx

tar zxvf nginx - 1.19.5.tar.gz
useradd nginx
cd nginx - 1.19.5
cp /usr/include/fastcommon/* /usr/include/fastdfs/
./configure \
--prefix=/usr/local/nginx \
--add - module=/root/fastdfs - nginx - module/src/ \
--with - cc - opt="-Wno - error=format - truncation"

注意:注意:这里的 --add - module 后面的值就是刚刚拷贝的 fastdfs - nginx - module 的 src 目录

make
make installln -s /usr/local/nginx/sbin/* /usr/local/sbin/

配置 FastDFS

将 fastdfs-nginx-module 模块下的 mod_fastdfs.conf 文件拷贝到 /etc/fdfs/ 目录

# 拷贝 mod_fastdfs.conf 文件到 /etc/fdfs/ 目录
cp /root/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/
# 编辑 mod_fastdfs.conf 文件
vi /etc/fdfs/mod_fastdfs.conf
# 修改的参数配置(在 mod_fastdfs.conf 文件内调整)
connect_timeout=10
base_path=/tmp  # 用户存储日志的
tracker_server=192.168.10.101:22122  # tracker服务器IP和端口
tracker_server=192.168.10.102:22122  # tracker服务器IP和端口
url_have_group_name=true  # 这个表示是否需要增加一个上述自定义名字的需要,改为true,url中包含group名称 
group_name=group1
storage_server_port=23000
store_path_count=1
store_path0=/fastdfs/storage

复制 FastDFS 的部分配置文件到 /etc/fdfs 目录

cp /root/fastdfs-5.11/conf/http.conf /etc/fdfs
cp /root/fastdfs-5.11/conf/mime.types /etc/fdfs

在 /fastdfs/storage 文件存储目录下创建软连接,将其链接到实际存放数据的目录

ln -s /fastdfs/storage/data /fastdfs/storage/M00

配置 Nginx

修改 nginx.conf 文件,在 http 指令块中新增加下配置

vi /usr/local/nginx/conf/nginx.conf
# nginx.conf 文件中 http 指令块内新增的配置
http {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;server {listen       80;server_name  localhost;# 后续可能还有其他配置,图中未完整显示}
}
location / {root  html;index index.html index.htm;
}
location /group1/M00 {ngx_fastdfs_module;
}
/usr/local/nginx/sbin/nginx

浏览器访问图片

http://192.168.10.103/group1/M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895_big.jpg
或
http://192.168.10.104/group1/M00/00/00/wKgK22gaACmAb6raAAFmKMuMrM1895_big.jpg

部署代理

浏览器访问图片示例:
http://192.168.10.105/group1/M00/00/00/wKgK22gaACmAb6r

yum -y install pcre-devel zlib-devel gcc*
# 解压 Nginx 源码包
tar zxvf nginx-1.19.5.tar.gz
# 进入源码目录
cd nginx-1.19.5
# 创建 Nginx 系统用户
useradd nginx
# 配置编译参数
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx
# 编译并安装
make && make install
# 创建 Nginx 命令软链接 
ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/
# 编辑 Nginx 主配置文件
vi /usr/local/nginx/conf/nginx.confhttp {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;# 定义 upstream 集群,配置存储服务器upstream storage_server_group1{server 192.168.10.103:80 weight=10;server 192.168.10.104:80 weight=10;}server {listen       80;server_name  localhost;location / {# 代理转发到 upstream 集群proxy_pass http://storage_server_group1;}}
}

http://www.lryc.cn/news/578157.html

相关文章:

  • 华为云Flexus+DeepSeek征文 | ​​接入华为云ModelArts Studio大模型 —— AI智能法务解决方案革新法律实践​
  • 38.docker启动python解释器,pycharm通过SSH服务直连
  • ERP系统Bug记录
  • 前端Vue面试八股常考题(一)
  • 中证500股指期货一手多少钱呢?风险如何?
  • HTML5 实现的圣诞主题网站源码,使用了 HTML5 和 CSS3 技术,界面美观、节日氛围浓厚。
  • 华为云 Flexus+DeepSeek 征文|基于 Dify 平台开发智能客服 AI Agent 的完整实战指南
  • 【STM32HAL-第1讲 基础篇-单片机简介】
  • 前端开发面试题总结-原生小程序部分
  • 《从量子奇境到前端优化:解锁卡西米尔效应的隐藏力量》
  • 《用奥卡姆剃刀原理,为前端开发“减负增效”》
  • 【软考高项论文】论信息系统项目的整体管理
  • 【Java面试】10GB,1GB内存,如何排序?
  • PHP WebSocket服务器搭建指南
  • 从入门到精通:npm、npx、nvm 包管理工具详解及常用命令
  • Springboot + vue + uni-app小程序web端全套家具商场
  • 【Spring】——事务、整合、注解
  • 设计模式-观察者模式(发布订阅模式)
  • UE5 - 制作《塞尔达传说》中林克的技能 - 17 - 遥控炸弹(二)
  • 键盘第一下无反应
  • 基于Spring Boot的绿园社区团购系统的设计与实现
  • 磁悬浮轴承位移信号的高精度估计:卡尔曼滤波算法深度解析
  • MySQL复杂SQL性能优化实战:多表联查与子查询的高效方法
  • 【数据清洗与预处理】-文本采集与处理
  • LoRA 问答微调与部署全流程:基于 LLaMA-Factory + DeepSeek + FastAPI 打造专属大模型
  • Hive SQL 实战:电商销售数据分析全流程案例
  • 大数据轻量化流批一体架构探索实践(一)
  • 【数据分析】环境数据降维与聚类分析教程:从PCA到可视化
  • [特殊字符]【联邦学习实战】用 PyTorch 从 0 搭建一个最简单的联邦学习系统(含完整代码)
  • ubuntu下免sudo执行docker