node.jsamazon-web-servicesdockeramazon-efsaws-copilot

Can Docker containers running a node.js service on ECS in production share a node_modules volume mounted from EFS?


Is it good practice for node.js service containers running under AWS ECS to mount a shared node_modules volume persisted on EFS? If so, what's the best way to pre-populate the EFS before the app launches?

My front-end services run a node.js app, launched on AWS Fargate instances. This app uses many node_modules. Is it necessary for each instance to install the entire body of node_modules within its own container? Or can they all mount a shared EFS filesystem containing a single copy of the node_modules?

I've been migrating to AWS Copilot to orchestrate, but the docs are pretty fuzzy on how to pre-populate the EFS. At one point they say, "we recommend mounting a temporary container and using it to hydrate the EFS, but WARNING: we don't recommend this approach for production." (Storage: AWS Copilot Advanced Use Cases)


Solution

  • Thanks for the question! This is pointing out some gaps in our documentation that have been opened as we released new features. There is actually a manifest field, image.depends_on which mitigates the issue called out in the docs about prod usage.

    To answer your question specifically about hydrating EFS volumes prior to service container start, you can use a sidecar and the image.depends_on field in your manifest.

    For example:

    image:
      build: ./Dockerfile
      depends_on:
        bootstrap: success
    
    storage:
      volumes:
        common:
          path: /var/copilot/common
          read_only: true
          efs: true
    
    sidecars:
      bootstrap:
        image: public.ecr.aws/my-image:latest
        essential: false #Allows the sidecar to run and terminate without the ECS task failing 
        mount_points:
          - source_volume: common
            path: /var/copilot/common
            read_only: false
    

    On deployment, you'd build and push your sidecar image to ECR. It should include either your packaged data or a script to pull down the data you need, then move it over into the EFS volume at /var/copilot/common in the container filesystem.

    Then, when you next run copilot svc deploy, the following things will happen:

    1. Copilot will create an EFS filesystem in your environment. Each service will get its own isolated access point in EFS, which means service containers all share data but can't see data added to EFS by other services.
    2. Your sidecar will run to completion. That means that all currently running services will see the changes to the EFS filesystem whenever a new copy of the task is deployed unless you specifically create task-specific subfolders in EFS in your startup script.
    3. Once the sidecar exits successfully, the new service container will come up on ECS and operate as normal. It will have access to the EFS volume which will contain the latest copy of your startup data.

    Hope this helps.