iFixit / react-commerce

A work in progress prototype for iFixit e-commerce functionalities.
https://react-commerce.vercel.app
2 stars 0 forks source link

[RFC] Production deployment for Next.js + Strapi #160

Closed dhmacs closed 2 years ago

dhmacs commented 2 years ago

This discussion started from a group chat created by @lithobraking:

Hey guys, I'm making this group chat so we can hash out the details of how we we're going to handle images for the Next.js site. Particularly how we're handling stuff like hosting, caching, image sizing, etc

This turned out to be a discussion on how we're going to deploy Next.js + Strapi so here I will detail what we come up with as well as what are the things that needs to be done.

Proposed solution

The proposed deployment environment for Next.js is AWS Amplify, while Strapi will be deployed to the existing iFixit kubernetes cluster. The architecture will look something like this:

ifixit-infra-v1

Basically Cloudfront will act as a reverse proxy, and as we migrate more pages to Next.js rewrites will gradually be added to Cloudfront. A few notes on the infrastructure:

Serving images

Since Next.js will be served through Cloudfront we can use Next.js image optimization and let the CDN cache the resized images. Next.js can automatically resize images from any source, so that means that not matter where the image comes from it'll be optimized on demand according to deviceSizes and imageSizes config.

It is also possible to generate responsive images with Strapi (https://docs.strapi.io/developer-docs/latest/plugins/upload.html#responsive-images) but that would require to implement a custom Next.js image loader (https://nextjs.org/docs/api-reference/next/image#loader).

Alternative solution that we considered

We've also taken into consideration to deploy Strapi using AWS backend functionalities (https://docs.amplify.aws/cli/usage/containers/) but decided to postpone this for now.

ifixit-infra

Todos

djmetzle commented 2 years ago

Intercepting our TLS with CloudFront will be a little bit of work. We'll want to iron out our certificates and get them into ACM.

lithobraking commented 2 years ago

CC: @kwiens @sterlinghirsh @danielbeardsley @evannoronha

danielbeardsley commented 2 years ago

Both of these approaches look like a lot of moving pieces and a lot of changes to existing infrastructure. My gut is to integrate this new service in the same way we've integrated all other services, have the load balancers (or varnish) proxy the traffic to the new service.

Cloudfront is cool tech and we use it 100% for all non-html assets, putting the entire app behind cloudfront is an interesting idea, but I don't see the need to combine that potential idea with this one.

Regarding deployment, I remember that strapi deployment has it's own quirks and requirements and perhaps that's a totally different problem.

dhmacs commented 2 years ago

@danielbeardsley My understanding of the current architecture is that only js, css and media assets are cached and served through CDN, where as html pages are generated for every request, right? If that's the case, it means that in general we'll have worse TTFB because every request will need to make a round trip to the various APIs (Strapi, iFixit, ...). For current iFixit site, are you using a server side cache like Redis to avoid hitting the db on every request? These architectural decisions have ramifications on the effort to implement certain aspects of the new commerce stack. Next.js is designed to run behind a CDN, so certain features like image resizing are designed with that in mind, so if we're not behind a CDN we'll have to handle that differently

danielbeardsley commented 2 years ago

My understanding of the current architecture is that only js, css and media assets are cached and served through CDN, where as html pages are generated for every request, right?

Pretty close, we do have varnish in-front of the web-server caching html pages (but only for logged out users).

If that's the case, it means that in general we'll have worse TTFB because every request will need to make a round trip to the various APIs (Strapi, iFixit, ...).

Are you saying "if we don't have a caching layer in front of next-js, it'll be worse? I agree that caching wherever possible is important. We use varnish for request caching and I'd want to put next-js behind that. Having one server (next-js) dependent on querying content from another server (ifixit api) implicitly means it can't have a lower TTFB, this was always the trade off. Our pages on the whole are made up of pieces that change per-user and thus aren't really cacheable and pieces that are the same for everyone and thus very cacheable. Putting all of next-js behind an html page-cache is worthwhile for logged out users, but we'd need a strategy to only SSR the pieces that don't depend on the current user, leaving user-specific stuff for the browser to fetch.

In short, we have a html page cache that is more configurable than cloudfront (varnish) and I definitely want to make use of it for next.js where possible. Putting the whole app behind cloudfront doesn't increase features (though does increase POPs) and is a significant increase in complexity.

dhmacs commented 2 years ago

Are you saying "if we don't have a caching layer in front of next-js, it'll be worse? I agree that caching wherever possible is important. We use varnish for request caching and I'd want to put next-js behind that. Having one server (next-js) dependent on querying content from another server (ifixit api) implicitly means it can't have a lower TTFB, this was always the trade off. Our pages on the whole are made up of pieces that change per-user and thus aren't really cacheable and pieces that are the same for everyone and thus very cacheable. Putting all of next-js behind an html page-cache is worthwhile for logged out users, but we'd need a strategy to only SSR the pieces that don't depend on the current user, leaving user-specific stuff for the browser to fetch.

We've already started to wire up content that is user dependent (right now user menu and cart) and the way this is done is by fetching data client side. This way the html served by Next.js is the same for both logged and logged out users, thus every Next.js page can be cached. This mean that if we cache it and have more POPs thanks to a CDN, the users will receive the html page faster (which is especially important for LCP core web vital).

djmetzle commented 2 years ago

Yeah, i think we'll want to stick with CloudFront, as we get Lambda@Edge SSR. It would be nearly impossible to achieve the same webvitals performance through our EIP routing. We should be looking to set up domain fronting with CF in order to get the best UX from nextjs.

danielbeardsley commented 2 years ago

We've already started to wire up content that is user dependent (right now user menu and cart) and the way this is done is by fetching data client side. This way the html served by Next.js is the same for both logged and logged out users, thus every Next.js page can be cached.

Great! Glad you're thinking this through.

I'm hesitant to block the deployment of this new tech / project on revamping a large portion of our routing infrastructure. I see the benefits of caching at the edge nodes. Though it should provide a performance boost, caching the html just on the east coast (with varnish) will still be much better than doing everything client-side.

I think we should make moving the entire site behind cloudfront a V2 project.

we'll want to stick with CloudFront, as we get Lambda@Edge SSR

I haven't spent enough time with these technologies to know exactly what this means, but I remember lambda being more like single-shot tasks triggered by external events and not really appropriate for firing up one task per web request. But perhaps I don't know what your suggesting.

Either way, running next.js in us-east and putting it behind varnish seems like a doable target for a V1 deployment. We can do the more involved and more performant deployment / routing as V2.

danielbeardsley commented 2 years ago

Any more thoughts here?

djmetzle commented 2 years ago

@dhmacs i think we should update the sketch in the description to not include strapi.ifixit.com. We've got that service up and running, and deployed, behind varnish, on a subdomain. I dont think we need to change that, beyond setting up some kind of deployment automation (which we do not have for our core app stack time now).

We've got two nextjs projects in flight now, so i think we need to be careful to refer to the products/projects by name. This is react-ecommerce nextjs deploy? And unrelated to the community pages rework? I think we'd prefer to deploy the community stack with the same plan (domain fronting with CF, on serverless) though folding community into our monorepo means we're no longer able to leverage Amplify there for previews any longer.

Its worth referencing his TF module we found: https://registry.terraform.io/modules/dealmore/next-js/aws/latest#architecture

That should be very close to the same deployment model as Amplify and Vercel, and i strongly recommend we follow that deployment model.

lithobraking commented 2 years ago

We've got two nextjs projects in flight now, so i think we need to be careful to refer to the products/projects by name. This is react-ecommerce nextjs deploy? And unrelated to the community pages rework? I think we'd prefer to deploy the community stack with the same plan (domain fronting with CF, on serverless) though folding community into our monorepo means we're no longer able to leverage Amplify there for previews any longer.

@djmetzle We could call this project eCommerce-next and the community page Community-next if that's simpler.

lithobraking commented 2 years ago

Also I want to double check and make sure we're all in agreement to move forward with this, because I'd like to have all the necessary issues for this written up.

dhmacs commented 2 years ago

@djmetzle

@dhmacs i think we should update the sketch in the description to not include strapi.ifixit.com. We've got that service up and running, and deployed, behind varnish, on a subdomain. I dont think we need to change that, beyond setting up some kind of deployment automation (which we do not have for our core app stack time now).

You mean that Strapi is behind Varnish and not behind Cloudfront? I can update it to reflect that if you want

We've got two nextjs projects in flight now, so i think we need to be careful to refer to the products/projects by name. This is react-ecommerce nextjs deploy? And unrelated to the community pages rework?

I'm not sure what's the reason that community and commerce sit on two different Next.js projects. @lithobraking do you know?

I think we'd prefer to deploy the community stack with the same plan (domain fronting with CF, on serverless) though folding community into our monorepo means we're no longer able to leverage Amplify there for previews any longer.

Why previews would not work on the monorepo?

Its worth referencing his TF module we found: https://registry.terraform.io/modules/dealmore/next-js/aws/latest#architecture

That should be very close to the same deployment model as Amplify and Vercel, and i strongly recommend we follow that deployment model.

Looks interesting, but will probably require more work to setup (CI, preview urls, etc), right?

djmetzle commented 2 years ago

@dhmacs i have a few answers here for you.

You mean that Strapi is behind Varnish and not behind Cloudfront? I can update it to reflect that if you want

strapi.ifixit.com is up and running in our "traditional" infrastructure. Behind varnish, manual deployments. I think we should stick with that strategy in the short term, but we can change that. But strapi.ifixit.com (being up and running) shouldnt be a concern (yet) for the NextJS deployment.

I'm not sure what's the reason that community and commerce sit on two different Next.js projects. @lithobraking do you know?

nextjs.ifixit.com was a separate project to rebuild our community pages, and we're currently working to move it back into our main monorepo.

Why previews would not work on the monorepo?

Amplify gives us automatic previews and deployments. Moving away from Amplify (back into the main monorepo) removes that. Its not that previews would not work, more that they do not currently work. We would have to build that.

dhmacs commented 2 years ago

strapi.ifixit.com is up and running in our "traditional" infrastructure. Behind varnish, manual deployments. I think we should stick with that strategy in the short term, but we can change that. But strapi.ifixit.com (being up and running) shouldnt be a concern (yet) for the NextJS deployment.

We need to coordinate deployment though if they happen manually. That is, any time PR is ready to be merged and it contains changes to Strapi, we need to deploy it to Varnish first, and then merge into main and let Amplify re-deploy the frontend.

nextjs.ifixit.com was a separate project to rebuild our community pages, and we're currently working to move it back into our main monorepo.

But are we going to integrate commerce and community under the same next.js project? If not, what are the reasons to keep them separate?

Moving away from Amplify (back into the main monorepo) removes that.

Can't we use Amplify even if the code stays in the monorepo? Seems like it should be possible https://docs.aws.amazon.com/amplify/latest/userguide/monorepo-configuration.html

djmetzle commented 2 years ago

We may be able to continue using Ampify against the main monorepo. It might be slower because of the size of the repo. But we can test that.

Its possible that we automate the backend (strapi) deployment from Amplify as well. But we dont currently have any of that IaC/automation set up currently.

andyg0808 commented 2 years ago

We had a meeting about this, and we hashed out a deployment plan: https://github.com/iFixit/ifixit/issues/42670 I'm gonna close this RFC in favor of the plan.