Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serve: add s3 server #6461

Closed
wants to merge 14 commits into from
Closed

serve: add s3 server #6461

wants to merge 14 commits into from

Conversation

Mikubill
Copy link
Contributor

@Mikubill Mikubill commented Sep 22, 2022

What is the purpose of this change?

Implements s3 server for rclone - #3382

Built with gofakes3 and serve/httplib. Some integration tests have passed so far, but still more work to do:

  • auth support - requires auth middleware
  • hash - might not work
  • pager - needs fix
  • more features - any suggestions?
  • docs and examples

Problems

  • To comply with s3 (no empty folder), empty directories will be removed while deleting objects, which may cause unexpected/unwanted operations.
  • Listobjects needs to list all files to apply the prefix filter. In the implementation I used walk.ListR(ctx,fs), but this seems to be an expensive operation, especially when there are thousands of files in the directory
  • The failures in integration tests are mainly caused by metadata. It seems that different backends implement metadata r/w func separately... Is there any generic way to handle it and work with vfs?

Checklist

  • I have read the contribution guidelines.
  • I have added tests for all changes in this PR if appropriate.
  • I have added documentation for the changes if appropriate.
  • All commit messages are in house style.
  • I'm done, this Pull Request is ready for review :-)

@Mikubill

This comment was marked as outdated.

@ncw
Copy link
Member

ncw commented Sep 22, 2022

This is great work :-)

Built with gofakes3 and serve/httplib. Some integration tests have passed so far, but still more work to do:

  • auth support - requires auth middleware
  • hash - might not work
  • pager - needs fix
  • more features - any suggestions?
  • docs and examples

If you can get the s3 integration tests more or less passing then this will be a useful feature.

I would have thought you could do auth with the existing httplib auth?

  • To comply with s3 (no empty folder), empty directories will be removed while deleting objects, which may cause unexpected/unwanted operations.

Sounds OK to me. Note you only need to do this on backends with the Feature features.CanHaveEmptyDirectories which will save useless work if serving a bucket based backend.

  • Listobjects needs to list all files to apply the prefix filter. In the implementation I used walk.ListR(ctx,fs), but this seems to be an expensive operation, especially when there are thousands of files in the directory

Hmm...

Assuming for a moment that we only support listing with a prefix which is a directory boundary (which is all rclone uses) then you can use the max depth controls on listing.

ListR will be listing all the things so if you are doing it for prefix searches then it will be expensive.

It would probably be more efficient to set up listing filters and just use List to emulate it properly. I think that would give a proper implementation at not too much computational cost. And I think if you assume that prefixes are always directory boundaries then you won't even need the filters.

  • The failures in integration tests are mainly caused by metadata. It seems that different backends implement metadata r/w func separately... Is there any generic way to handle it and work with vfs?

Metadata is an optional feature. If the backend doesn't support it that is fine.

You can probably disable the Metadata feature for the duration of the tests - add "Metadata" to this []string{}

DisableFeatures []string

If this is serving a bucket based backend then it could potentially be more efficient but we don't expose the prefix listings that they have - maybe we should?

@Mikubill
Copy link
Contributor Author

Mikubill commented Sep 22, 2022

s3 integration tests

Passed with the latest commit (on my end). Note that I used a small cache to store metadata, which is volatile and won't be transferred to any backend. Maybe change later to adapt with fs/vfs.

features.CanHaveEmptyDirectories

added

auth with the existing httplib

serve/httplib doesn't support per-request auth like AWS-Signature-V4 etc, so make a separate one for s3 serve.

only support listing with a prefix which is a directory boundary

I've modified the prefixParser to assume that slash is the default delimiter when not specified. That should make major requests goes to directory-based handler rather than fuzzy search.

bucket based backend

Good idea but might need some works on fs/vfs interface?

@ncw
Copy link
Member

ncw commented Sep 24, 2022

This is looking good - well done :-)

Do you want me to review the code yet?

I should probably give it a go and try to break it!

@Mikubill Mikubill marked this pull request as ready for review October 4, 2022 11:12
@Mikubill
Copy link
Contributor Author

Mikubill commented Oct 4, 2022

Almost done, I think you can start reviewing the code.

Copy link
Member

@ncw ncw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking very nice :-)

See inline for comments.

cmd/serve/s3/s3_test.go Outdated Show resolved Hide resolved
cmd/serve/s3/backend.go Outdated Show resolved Hide resolved
"github.com/rclone/rclone/cmd/serve/s3/signature"
)

func (p *Server) authMiddleware(handler http.Handler) http.Handler {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to make the auth proxy work with this server?

https://github.com/rclone/rclone/blob/master/cmd/serve/proxy/proxy.go

}

// newBackend creates a new SimpleBucketBackend.
func newBackend(fs *vfs.VFS, opt *Options) gofakes3.Backend {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to implement this without using the VFS, using the lower level fs commands only.

However the VFS provides caching etc which could be useful. It will introduce inefficiencies though of course... For example you can run ListR directly on bucket backed backends, but via the VFS it will list each directory one by one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the high request rate of S3, it should be better to use VFS on some backends with strict rate limits. I'd added some logic for the BucketBased backend to execute direct fs.ListR, hope this will work.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to also have a VFS passthrough mode or disable VFS entirely? I may need it because I will load balance the rclone-based S3 server using multiple nodes (they restrict it by IP traffic) so having a VFS layer may cause cache invalidation problem.

Please note that some clients may require HTTPS endpoints.
See [#SSL](#ssl-tls) for SSL configuration.

Use ` + `--host-bucket` + ` if you want to use bucket name as a part of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what we call path style vs virtual host style in the s3 backend? If so we should probably name it similarly

  --s3-force-path-style   If true use path style access if false use virtual hosted style (default true)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed. Currently using --force-path-style.

cmd/serve/s3/s3.go Outdated Show resolved Hide resolved
cmd/serve/s3/signature/signature-errors.go Outdated Show resolved Hide resolved
@ncw
Copy link
Member

ncw commented Oct 7, 2022

Do you want to merge this for 1.60 which I want to release in the next week or two? We could merge it with beta in the docs.

@Mikubill
Copy link
Contributor Author

Seems that some race issues occurred with lib/http (see https://github.com/Mikubill/rclone/actions/runs/3339975457/jobs/5529550702)

@blaineam
Copy link

blaineam commented Dec 1, 2022

Seems that some race issues occurred with lib/http (see https://github.com/Mikubill/rclone/actions/runs/3339975457/jobs/5529550702)

Im not sure what caused that to error there, I forked your repo and ran the action myself and it originally errored out somewhere completely different and I tried to just update the repo with the 100+ commits on the main upstream branch and running the tests again in GitHub seems promising.

The Github Actions don't seem to fail on the same test every time which makes me think it has nothing to do with the code changes but has everything to do with the Github Action job runners maybe deprioritizing these long running tasks for free GitHub accounts like my own.

Also running make racequicktest locally your serve tests pass just fine on my MacBook and it just complains about osxfuse not being installed(I never installed it so it makes sense for the mount tests in macOS)

I would say this should be fine even ignoring the random failing GitHub action tests that don't even pertain to the changes in this pr.

In any case I am super excited to have this feature and plan to test it with some of my hobby projects when it makes it to beta or release. I will be able to test it with a few different cloud backends and can use it for large "buckets" with 4mil "objects" easily whenever this pr lands.

@blaineam
Copy link

blaineam commented Dec 1, 2022

I can confirm that re-running the failed job with zero code changes it eventually passed (3rd try) https://github.com/blaineam/rclone/actions/runs/3589303971/jobs/6041966413
I think it is Github Actions being suspicious.

@ncw
Copy link
Member

ncw commented Dec 3, 2022

@Mikubill do you want me to merge this for rclone 1.61? I think it is done isn't it +/- a few test failures.

@blaineam
Copy link

blaineam commented Dec 3, 2022

I setup a docker container to test it with one of my backends and I could not get it to work with my mountain duck client at least not fully. It loaded the list of "buckets" but all the spaces were replaced with + signs and I couldn't open any of the buckets. I think it may have been a misconfiguration of my mountain duck because the s3 tests seem to be passing but I thought I should report this concern.

Thinking on this I don't think buckets on aws s3 can have spaces I'm going to try a different root but this is probably fine

@oniumy
Copy link

oniumy commented Mar 12, 2023

Thank you for testing writes @blaineam

I did some more testing locally and figured out that it only affects uploads with v4 authorization. After tracing the issue through the code, I ended up in gofakes3, which had missing support for chunked multipart uploads as well as a bug in the chunked reader implementation. I created a pull request rclone/gofakes3#1 with the required fixes.

With these fixes applied, everything works now in my limited testing with minio client (with and without v4 authorization).

@blaineam
Copy link

blaineam commented Mar 14, 2023

Thank you for testing writes @blaineam

I did some more testing locally and figured out that it only affects uploads with v4 authorization. After tracing the issue through the code, I ended up in gofakes3, which had missing support for chunked multipart uploads as well as a bug in the chunked reader implementation. I created a pull request Mikubill/gofakes3#1 with the required fixes.

With these fixes applied, everything works now in my limited testing with minio client (with and without v4 authorization).

That explains why my 1.4MB pdf doesn't have any issues because it likely fits within the size of a single chunk. Nice work patching the gofakes3 fork!

@individual-it
Copy link
Contributor

This looks amazing!
We are currently researching the possibility to have an S3 to WebDAV proxy and this does most of what we need. rclone can access a WebDAV storage and with this PR serve a S3 front-end. The only thing that is missing is the option to access different WebDAV accounts. Would it be somehow possible to use the S3 AccessKey and forward it to the WebDAV side as Bearer Token?

@ncw ncw modified the milestones: v1.62, v1.63 May 9, 2023
@dan3o3
Copy link

dan3o3 commented May 23, 2023

@ncw Does your milestone add mean this feature will come finally? Like many others I really love to see this coming but it seems a bit this PR is stalled. Unfortunately MinIO with rclone via Fuse seems to be not really stable. Just curious if I can hope on this? :)

@oniumy
Copy link

oniumy commented May 24, 2023

I would love to help getting this PR merged, but I'm not sure what issues need to get fixed to get there. All the issues I discovered in my testings are fixed and I'm successfully running this branch on a staging environment for a few weeks now without any issues.

@dalbani
Copy link

dalbani commented May 24, 2023

Github says that there are merge conflicts to begin with.

@dan3o3
Copy link

dan3o3 commented May 24, 2023

I would love to help getting this PR merged, but I'm not sure what issues need to get fixed to get there. All the issues I discovered in my testings are fixed and I'm successfully running this branch on a staging environment for a few weeks now without any issues.

You probably can share your parameters? I compiled the PR successfully and started the s3 server but were not able to add buckets etc.

@individual-it
Copy link
Contributor

This seems to work in general, but I have trouble to use it with authentication.
When I try to start the server like rclone serve s3 local: --s3-authkey aaa,bbbbbbbbbbbb

and then upload something with mc, having set the same key & secret rclone spits out

2023/06/13 14:41:36 ERROR : expected integer
2023/06/13 14:41:36 ERROR : expected integer
2023/06/13 14:41:36 ERROR : expected integer

@footlooseboss
Copy link

footlooseboss commented Jun 13, 2023 via email

@individual-it
Copy link
Contributor

The issue I've faced was the one described and fixed by @oniumy , it just never ended up in this PR. So a go get github.com/Mikubill/gofakes3@723f1854b42fb1d141eb2893d21d8ed74169d1d4 fixed it

@individual-it
Copy link
Contributor

I've created a PR that solves the conflicts: #7062
Should we close this and keep on working in the new one?

@ncw ncw modified the milestones: v1.63, v1.64 Jul 1, 2023
@tomyl
Copy link

tomyl commented Jul 26, 2023

Hi, I gave this a try and it seems to work but the process gets OOM killed after a while. How to reproduce:

  1. Create a large file e.g. dd if=/dev/zero of=/tmp/largefile bs=1M count=5000
  2. Add add an rclone backend like
[local]
type = local
  1. Run the s3 server: `./rclone serve s3 local:/tmp/rclone --addr localhost:8081 --s3-authkey test,test --no-modtime --nochecksum
  2. Upload the large file a few times e.g. with mc.
$ cat ~/.mc/config.json
....
        "rclone": {
            "url": "http://localhost:8081",
            "accessKey": "test",
            "secretKey": "test",
            "api": "S3v4",
            "path": "auto"
        }
...
$ mc mb rclone/foo
$ mc cp /tmp/largefile rclone/foo/largefile                            
$ mc cp /tmp/largefile rclone/foo/largefile           
...    

It looks like each upload consumes about 2x the file size in memory and the memory is not released (fast enough?) after upload and eventually you get

[12127.265358] Out of memory: Killed process 131176 (rclone) total-vm:32901932kB, anon-rss:22842736kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:45196kB oom_score_adj:0

The branch from #7062 behaves the same.

@individual-it
Copy link
Contributor

@tomyl hi could you try to set GOMEMLIMIT and see if it fixes the problem? https://weaviate.io/blog/gomemlimit-a-game-changer-for-high-memory-applications

@tomyl
Copy link

tomyl commented Aug 9, 2023

@individual-it Thanks, that indeed fixes the OOM panic for me. I tried GOMEMLIMIT=1GiB and the rclone process still consumes 2x file size during the upload (RSS goes up to 10G in my example above) but once the upload is done, RSS drops down to 1.7G each time.

@ncw
Copy link
Member

ncw commented Aug 9, 2023

Hmm, this really needs fixing so the file gets streamed and not held in memory.

}

hasher := md5.New()
w := io.MultiWriter(f, hasher)
Copy link

@sychan sychan Sep 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This md5 hash object gets initialized, and then it gets a copy of all the bytes from the input. However hasher.Sum(nil) is never called, and the md5 results seem to be never used at all. There is a report that PUT operations use up memory equal to twice the input filesize. Could the md5 hasher be grabbing extra memory until .Sum() called, but then when the hasher object goes out of scope the memory gets released?

And if nothing is ever done with the md5sum, why not just remove it entirely and avoid using up any memory for the md5 calculation?

@ncw ncw modified the milestones: v1.64, v1.65 Sep 11, 2023
@sychan sychan mentioned this pull request Sep 12, 2023
5 tasks
@ncw
Copy link
Member

ncw commented Nov 16, 2023

I've merged this to master now which means it will be in the latest beta in 15-30 minutes and released in v1.65

Thank you everyone for contributing - it is a great feature.

@Mikubill would you like to move your gofakes3 repo to the rclone org and become a member of the rclone project?

I think we need to do major surgery on it to stop it caching multipart uploads in memory so it would be good if it was in the rclone project at that point.

@ncw ncw closed this Nov 16, 2023
@Mikubill
Copy link
Contributor Author

Great to hear and wow - thank you everyone for making this feature come true. Also i am happy to move gofakes3 to rclone and help strengthening codebase in the future.

@ncw
Copy link
Member

ncw commented Nov 20, 2023

@Mikubill great to hear that :-) Drop me an email to nick@craig-wood.com and we can discuss mechanics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet