projectmikey
projectmikey

Reputation: 129

Getting error that files written to directory in /tmp (within Dockerfile) do not exist while running AWS Lambda with container image from ECR

My goal is to build a docker image with certbot installed and use that as the container image for my AWS Lambda function. I am able to build the image successfully with docker and get it running locally with

docker build -t image-name:tag . && docker tag image-name:tag username/image-name:tag && docker push username/image-name:tag

and then to run image locally

docker run -p 9000:8080 image-name:tag

and to test the endpoint

curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'

Then after the successful test I run the push commands shown on the AWS ECR repository page to get my docker image moved to ECR, and copy the ECR image URI to use as my Lambda container image URI and deploy the function.

Nothing seems to work live on AWS. I get an error from the Lambda Test console. I know that the /tmp dir is the only writable one on AWS Lambda and I am trying to use that dir for the certbot installation in my Dockerfile but it doesn't seem to be working correctly.

ERROR   Error: Error: Command failed: /tmp/opt/certbot/certbot-auto --no-bootstrap --no-self-upgrade --help
/bin/sh: /tmp/opt/certbot/certbot-auto: No such file or directory

    at ChildProcess.exithandler (node:child_process:402:12)
    at ChildProcess.emit (node:events:513:28)
    at maybeClose (node:internal/child_process:1100:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) {
  code: 127,
  killed: false,
  signal: null,
  cmd: '/tmp/opt/certbot/certbot-auto --no-bootstrap --no-self-upgrade --help',
  stdout: '',
  stderr: '/bin/sh: /tmp/opt/certbot/certbot-auto: No such file or directory\n'
}

here is my Dockerfile

FROM public.ecr.aws/lambda/nodejs:16

ARG FUNCTION_DIR=${LAMBDA_TASK_ROOT}
ARG LAMBDA_TEMP_DIR=/tmp

# Install Certbot
ENV CERTBOT_DIR=""${LAMBDA_TEMP_DIR}"/opt/certbot"
ARG CERTBOT_VERSION=0.31.0
RUN mkdir -p ${CERTBOT_DIR}
RUN yum update -y && \
    yum install -y git && \
    git --version && \
    git clone https://github.com/certbot/certbot $CERTBOT_DIR && \
    if [ "${CERTBOT_VERSION}" != "latest" ]; then \
        cd $CERTBOT_DIR && git checkout tags/v${CERTBOT_VERSION} ; \
    fi && \
    $CERTBOT_DIR/certbot-auto --no-bootstrap --no-self-upgrade --help && \
    yum remove -y git && \
    yum clean all && \
    rm -rf /var/cache/yum/*

COPY app.js package*.json ./
RUN npm install

CMD [ "app.handler" ]

here is my app.js file with handler function

const util = require('util');
const exec = util.promisify(require('child_process').exec);

exports.handler = async (event, context) => {

  try {

    const tmpDirectory = '/tmp';

    const { stdout: lsCommand } = await exec(`ls -lah ${tmpDirectory}`);
    const lsCommandMessage = "ls command results: " + lsCommand;

    const { stdout: certbotCommand } = await exec(`${tmpDirectory}/opt/certbot/certbot-auto --no-bootstrap --no-self-upgrade --help`);
    const certbotCommandMessage = "certbot command results: " + certbotCommand;

    const resObj = {
      message: "Hello world from Docker!",
      lsCommandMessage: lsCommandMessage,
      certbotCommandMessage: certbotCommandMessage
    };

    const response = {
      statusCode: 200,
      body: resObj,
  };

  return response;

  } catch (error) {
    console.error('Error:', error);
  }
  
};

Now if i were to change my app.js file with the certbot command commented out and rebuild image and repush to ecr etc everything works and I dont get error in Lambda test

const util = require('util');
const exec = util.promisify(require('child_process').exec);

exports.handler = async (event, context) => {

  try {

    const tmpDirectory = '/tmp';

    const { stdout: lsCommand } = await exec(`ls -lah ${tmpDirectory}`);
    const lsCommandMessage = "ls command results: " + lsCommand;

    // const { stdout: certbotCommand } = await exec(`${tmpDirectory}/opt/certbot/certbot-auto --no-bootstrap --no-self-upgrade --help`);
    // const certbotCommandMessage = "certbot command results: " + certbotCommand;

    const resObj = {
      message: "Hello world from Docker!",
      lsCommandMessage: lsCommandMessage,
      // certbotCommandMessage: certbotCommandMessage
    };

    const response = {
      statusCode: 200,
      body: resObj,
  };

  return response;

  } catch (error) {
    console.error('Error:', error);
  }
  
};

Again again both versions of my app.js seem to work when tested on local endpoint. Version that executes certbotCommand in app.js does not work when deployed live to AWS Lambda with container image selected from private ECR repository.

Trying to figure out what is going on here or where I am messing this up. Any help would be great! Thanks in advance.

Upvotes: 1

Views: 756

Answers (1)

erik258
erik258

Reputation: 16302

/tmp is the location of ephemeral storage on Lambda, which means it masks anything in the container's /tmp.

Don't use /tmp for persistent stuff in any environment - it's purely for ephemeral stuff. Looking at your Dockerfile, there's no reason not to put your stuff somewhere else, like just /opt/certbot for example.

Upvotes: 1

Related Questions