Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Creating a Minio S3 Blobstore on NXRM 3.13

Creating a Minio S3 Blobstore on NXRM 3.13

This is a quick guide on creating using the NXRM S3 blob store with Minio's S3 implementation. Disclaimer: Using Minio with NXRM is not officially supported by Sonatype.

Step 1: Start Minio locally

Follow the quick start guide here: https://github.com/minio/minio. For my testing, I started minio with docker with

docker pull minio/minio
docker run -p 9000:9000 -e "MINIO_ACCESS_KEY=mykey" -e "MINIO_SECRET_KEY=mysecret" minio/minio server data

Minio will helpfully print the credentials you need to the console once it has been started. Recent minio docker images don't print out credentials any more. Supply them as environment variables as above.

Obviously you can skip this step if you already have Minio running.

Step 2: Create a bucket

NXRM will create a bucket automatically on AWS S3, but it doesn't work quite right with Minio. Create the bucket in the Minio web console.

Step 3: Start a version of NXRM with the S3 blob store

Your best bet is the latest version of NXRM. NXRM 3.12 and later support S3 out of the box and is highly recommended. For older versions of NXRM 3, you can install the unbundled version of the plugin from https://github.com/sonatype/nexus-blobstore-s3, but this isn't being actively supported anymore. Use the bundled version and save yourself some headaches!

Step 4: Create the blob store

There are lots of configuration options here, and you have to get them just right for Minio to work.

S3 blob store configuration

  1. Name: Enter a name (e.g. test-blobstore)
  2. Bucket: Enter the name of the bucket you created in step 2 (e.g. test)
  3. Access Key ID: Enter the access key id you provided to docker (e.g. "mykey")
  4. Secret Access Key: Enter the secret access key you provided to docker (e.g. "mysecret")
  5. Session Token: leave blank
  6. Assume Role ARN: leave blank
  7. Region: Choose us-east-1
  8. Endpoint URL: Enter the Minio API URL (e.g. http://127.0.0.1:9000)
  9. Expiration Days: Enter -1
  10. Signature version: Leave as default

Thats it! Hope this helps.

@risik
Copy link

risik commented Feb 1, 2019

Minor note.

There is one more useful option:

Configure the client for use path-style access:
[x] Setting this flag will result in path-style access being used for all requests

I use one node in-house minio installation accessed by domain name with https: https://minio.corp.example.com. If leave this option turned off then S3 client use domain name based access to S3: bucket-name.minio.corp.example.com for example. So need to 1) tune DNS: all buckets point to minio node and 2) use wildcard site certificate like *.minio.corp.example.com for minio. I think it's works but I not tested this. Because it's seemed too complicated for me. So I used this option.

Nexus version: 3.15

@jonesbusy
Copy link

jonesbusy commented Feb 5, 2019

Sadly, deletion of blob doesn't seem to work (Minio doesn't support S3 lifecycle)

@lishaorui
Copy link

lishaorui commented Jul 7, 2019

I have installed NEXUS OSS 3.17.0-01 and minio latest version locally. It's caught an error when I create a S3 Blob after a minio bucket created in the minio web console.
error stack info:
2019-07-07 13:26:27,410+0800 ERROR [qtp1843155730-147] admin org.sonatype.nexus.extdirect.internal.ExtDirectExceptionHandler - Failed to invoke action method: coreui_Blobstore.create, java-method: org.sonatype.nexus.coreui.BlobStoreComponent.create org.sonatype.nexus.blobstore.api.BlobStoreException: Unable to initialize blob store bucket: test, Cause: Failed to parse XML document with handler class com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser$BucketLifecycleConfigurationHandler at org.sonatype.nexus.blobstore.s3.internal.S3BlobStore.doInit(S3BlobStore.java:478) at org.sonatype.nexus.blobstore.BlobStoreSupport.init(BlobStoreSupport.java:219) at org.sonatype.nexus.repository.internal.blobstore.BlobStoreManagerImpl.create(BlobStoreManagerImpl.java:192) at org.sonatype.nexus.common.stateguard.MethodInvocationAction.run(MethodInvocationAction.java:39) at org.sonatype.nexus.common.stateguard.StateGuard$GuardImpl.run(StateGuard.java:272) at org.sonatype.nexus.common.stateguard.GuardedInterceptor.invoke(GuardedInterceptor.java:53) at org.sonatype.nexus.blobstore.api.BlobStoreManager$create$6.call(Unknown Source) at org.sonatype.nexus.coreui.BlobStoreComponent.create(BlobStoreComponent.groovy:166) at com.palominolabs.metrics.guice.ExceptionMeteredInterceptor.invoke(ExceptionMeteredInterceptor.java:23) at com.palominolabs.metrics.guice.TimedInterceptor.invoke(TimedInterceptor.java:26) at org.sonatype.nexus.validation.internal.ValidationInterceptor.invoke(ValidationInterceptor.java:53) at org.apache.shiro.guice.aop.AopAllianceMethodInvocationAdapter.proceed(AopAllianceMethodInvocationAdapter.java:49) at org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor.invoke(AuthorizingAnnotationMethodInterceptor.java:68) at org.apache.shiro.guice.aop.AopAllianceMethodInterceptorAdapter.invoke(AopAllianceMethodInterceptorAdapter.java:36) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.softwarementors.extjs.djn.router.dispatcher.DispatcherBase.invokeJavaMethod(DispatcherBase.java:142) at com.softwarementors.extjs.djn.router.dispatcher.DispatcherBase.invokeMethod(DispatcherBase.java:133) at org.sonatype.nexus.extdirect.internal.ExtDirectDispatcher.invokeMethod(ExtDirectDispatcher.java:82) at com.softwarementors.extjs.djn.router.dispatcher.DispatcherBase.dispatch(DispatcherBase.java:63) at com.softwarementors.extjs.djn.router.processor.standard.StandardRequestProcessorBase.dispatchStandardMethod(StandardRequestProcessorBase.java:73) at com.softwarementors.extjs.djn.router.processor.standard.json.JsonRequestProcessor.processIndividualRequest(JsonRequestProcessor.java:502) at com.softwarementors.extjs.djn.router.processor.standard.json.JsonRequestProcessor.processIndividualRequestsInThisThread(JsonRequestProcessor.java:150) at com.softwarementors.extjs.djn.router.processor.standard.json.JsonRequestProcessor.process(JsonRequestProcessor.java:133) at com.softwarementors.extjs.djn.router.RequestRouter.processJsonRequest(RequestRouter.java:83) at com.softwarementors.extjs.djn.servlet.DirectJNgineServlet.processRequest(DirectJNgineServlet.java:632) at com.softwarementors.extjs.djn.servlet.DirectJNgineServlet.doPost(DirectJNgineServlet.java:595) at org.sonatype.nexus.extdirect.internal.ExtDirectServlet.doPost(ExtDirectServlet.java:129) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at com.google.inject.servlet.ServletDefinition.doServiceImpl(ServletDefinition.java:286) at com.google.inject.servlet.ServletDefinition.doService(ServletDefinition.java:276) at com.google.inject.servlet.ServletDefinition.service(ServletDefinition.java:181) at com.google.inject.servlet.DynamicServletPipeline.service(DynamicServletPipeline.java:71) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:85) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:112) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) at org.sonatype.nexus.security.SecurityFilter.executeChain(SecurityFilter.java:85) at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) at org.sonatype.nexus.security.SecurityFilter.doFilterInternal(SecurityFilter.java:101) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at com.sonatype.nexus.licensing.internal.LicensingRedirectFilter.doFilter(LicensingRedirectFilter.java:108) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at com.codahale.metrics.servlet.AbstractInstrumentedFilter.doFilter(AbstractInstrumentedFilter.java:112) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at org.sonatype.nexus.internal.web.ErrorPageFilter.doFilter(ErrorPageFilter.java:68) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at org.sonatype.nexus.internal.web.EnvironmentFilter.doFilter(EnvironmentFilter.java:101) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at org.sonatype.nexus.internal.web.HeaderPatternFilter.doFilter(HeaderPatternFilter.java:98) at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:82) at com.google.inject.servlet.DynamicFilterPipeline.dispatch(DynamicFilterPipeline.java:104) at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:135) at org.sonatype.nexus.bootstrap.osgi.DelegatingFilter.doFilter(DelegatingFilter.java:73) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1602) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1700) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1345) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1667) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1247) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) at com.codahale.metrics.jetty9.InstrumentedHandler.handle(InstrumentedHandler.java:239) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:152) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) at org.eclipse.jetty.server.Server.handle(Server.java:505) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:370) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:267) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126) at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:698) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:804) at java.lang.Thread.run(Thread.java:748) Caused by: com.amazonaws.SdkClientException: Failed to parse XML document with handler class com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser$BucketLifecycleConfigurationHandler at com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.parseXmlInputStream(XmlResponsesSaxParser.java:156) at com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.parseBucketLifecycleConfigurationResponse(XmlResponsesSaxParser.java:391) at com.amazonaws.services.s3.model.transform.Unmarshallers$BucketLifecycleConfigurationUnmarshaller.unmarshall(Unmarshallers.java:270) at com.amazonaws.services.s3.model.transform.Unmarshallers$BucketLifecycleConfigurationUnmarshaller.unmarshall(Unmarshallers.java:266) at com.amazonaws.services.s3.internal.S3XmlResponseHandler.handle(S3XmlResponseHandler.java:62) at com.amazonaws.services.s3.internal.S3XmlResponseHandler.handle(S3XmlResponseHandler.java:31) at com.amazonaws.http.response.AwsResponseHandlerAdapter.handle(AwsResponseHandlerAdapter.java:70) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleResponse(AmazonHttpClient.java:1553) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1271) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1055) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:743) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:717) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:699) at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:667) at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:649) at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:513) at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4247) at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4194) at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4188) at com.amazonaws.services.s3.AmazonS3Client.getBucketLifecycleConfiguration(AmazonS3Client.java:2269) at com.amazonaws.services.s3.AmazonS3Client.getBucketLifecycleConfiguration(AmazonS3Client.java:2255) at org.sonatype.nexus.blobstore.s3.internal.BucketManager.prepareStorageLocation(BucketManager.java:71) at org.sonatype.nexus.blobstore.s3.internal.S3BlobStore.doInit(S3BlobStore.java:474) ... 107 common frames omitted Caused by: org.xml.sax.SAXParseException: 文件提前结束。 at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203) at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327) at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1472) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:1014) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:505) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:842) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:771) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213) at com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.parseXmlInputStream(XmlResponsesSaxParser.java:142) ... 129 common frames omitted

@cognoz
Copy link

cognoz commented Aug 1, 2019

Have same error as @lishaorui

Have tried all nexus-oss versions from 3.14 to 3.18 :
Versions 3.14 - 3.16.2 - I can create blob storage, and use it for files (raw), but not for docker - than I push docker image to nexus I get this error -

(com.amazonaws.services.s3.model.AmazonS3Exception: A header you provided implies functionality that is not implemented (Service: Amazon S3; Status Code: 501; Error Code: NotImplemented; Request ID: /docker2/content/directpath/nexus-repository-docker/02205dd9-c1c3-4b88-801d-33238c6d5abf/0.bytes; S3 Extended Request ID: /docker2/content/directpath/nexus-repository-docker/02205dd9-c1c3-4b88-801d-33238c6d5abf/0.bytes)

Versions 3.17 - 3.18 - I can't even create blob storage -

unable to initialize blob store bucket: test, Cause: Failed to parse XML document with handler class com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser$BucketLifecycleConfigurationHandler

Im pretty sure in my settings (region us-east-1, keys, bukets, policies, etc) - I ran default docker registry, and it works with minio without errors, but customer needs nexus with blobs on minio

@himslm01
Copy link

himslm01 commented Aug 17, 2019

Exactly the same issue with Nexus OSS 3.18.1-01 and Minio version 2019-07-24T02:02:23Z.
I cannot even create the blob storage:

2019-08-17 15:51:26,853+0000 ERROR [qtp1790807893-158] admin org.sonatype.nexus.extdirect.internal.ExtDirectExceptionHandler - Failed to invoke action method: coreui_Blobstore.create, java-method: org.sonatype.nexus.coreui.BlobStoreComponent.create
org.sonatype.nexus.blobstore.api.BlobStoreException: Unable to initialize blob store bucket: nexus-repository, Cause: The XML you provided was not well-formed or did not validate against our published schema. (Service: Amazon S3; Status Code: 400; Error Code: MalformedXML; Request ID: 15BBC09F87971BF9; S3 Extended Request ID: 8be7ad1d-5411-4159-95f7-eb1f977be4c2)
	at org.sonatype.nexus.blobstore.s3.internal.S3BlobStore.doInit(S3BlobStore.java:478)
	at org.sonatype.nexus.blobstore.BlobStoreSupport.init(BlobStoreSupport.java:219)
	at org.sonatype.nexus.repository.internal.blobstore.BlobStoreManagerImpl.create(BlobStoreManagerImpl.java:192)

I was so hopeful this would work, it would solve a lot of issues for me - particularly for providing storage for running Nexus in Docker.

@binlab
Copy link

binlab commented Feb 27, 2020

The same issue with Sonatype Nexus Repository ManagerOSS 3.21.1-01 in Docker container from https://hub.docker.com/r/sonatype/nexus3/

@binlab
Copy link

binlab commented Feb 27, 2020

Does anyone know how to make this possible?

@lukasmrtvy
Copy link

lukasmrtvy commented Mar 25, 2020

Does not work with sonatype/nexus3:3.21.2 and minio/minio:RELEASE.2020-03-25T07-03-04Z.

Getting

Caused by: com.amazonaws.services.s3.model.AmazonS3Exception: A header you provided implies functionality that is not implemented (Service: Amazon S3; Status Code: 501; Error Code: NotImplemented; Request ID: 15FFA033A169F39B; S3 Extended Request ID: b77d0d1d-6b8b-4358-8fc3-b9b17d1db09e)

with path-style access enabled.
and

Caused by: com.amazonaws.services.s3.model.AmazonS3Exception: Not Found (Service: Amazon S3; Status Code: 404; Error Code: 404 Not Found; Request ID: 15FFA04B4944E112; S3 Extended Request ID: null)

with path-style access disabled. ( I had to add <bucket>.minio alias )

minio/minio#7200

@oogali
Copy link

oogali commented Jan 20, 2022

I was faced the same problem with an older version of Nexus Repository Manager (3.27 I believe).

After finding this thread, I upgraded to v3.37.3 and changed the signature version to S3SignerType. I already had path-style access enabled.

With that change, pushing Docker images to Nexus began to work.

Based on this, I think there's some incompatibility between Nexus, the AWS Java SDK, and Minio where the combination of them can't operate on v4 signatures (which is the default in Nexus), but is at least capable of using a v2 signature.

$ minio --version
minio version RELEASE.2021-04-06T23-11-00Z

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