I recently tried to dockerise an old hobby project and unsurprisingly, a couple of things broke. Some of these were fairly simple fixes so I won’t go into their details - but I will go into a fairly obscure one, which was caused by puppeteer. Regarding the application itself, it does a few things, but for the purposes of this article, let’s just say that it renders some reports using Puppeteer and NodeJS (while running as a .NET app).
Dance monkey, dance monkey, dance…wait, what?
If you haven’t heard of Puppeteer, it’s basically a NodeJS library that allows you to run (and control) an instance of headless chrome. i.e - an instance of Google chrome without a UI. In this project, I was using it to render PDF exports of reports. Running natively, it worked like a charm. But under docker, well…I’m writing this article after all, aren’t I?
Hold up, let’s see your docker setup first.
I thought you might say that! Well, first here’s the dockerfile.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyApplication.API/MyApplication.API.csproj", "MyApplication.API/"] COPY ["MyApplication.Common/MyApplication.Common.csproj", "MyApplication.Common/"] COPY ["MyApplication.Data/MyApplication.Data.csproj", "MyApplication.Data/"] COPY ["MyApplication.Logic/MyApplication.Logic.csproj", "MyApplication.Logic/"] RUN dotnet restore "MyApplication.API/MyApplication.API.csproj" COPY . . WORKDIR "/src/MyApplication.API" RUN dotnet build "MyApplication.API.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyApplication.API.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyApplication.API.dll", "--environment", "Docker"] # Install node and npm RUN apt-get update RUN apt-get install nodejs=12.22.5~dfsg-2~11u1 -y RUN apt-get install npm=7.5.2+ds-2 -y ENV NODE_ENV=production # Install node modules for reports WORKDIR "/app/Reports/jsreport" RUN npm install # Set the directory back so dotnet is able to run the application WORKDIR "/app"
Basically I load the ASP.NET 6 base image, build the solution, publish it, install require node modules and set the
dotnet program as the entry point of the container.
Then I build an image using
docker build -t myimage -f ./MyApplication.API/Dockerfile .
Note that in order for the build step in the docker file to work, the image has to be created at the solution level and not the project level - since all of the projects need to be built. Hence why I’m using the solution directory when we build the image.
Then I run a container using
docker run -p 127.0.0.1:80:80/tcp myimage.
So far so good! But after invoking one of the reports yields an internal server error.
Show me the errors!
Well, since this is running under docker (and this is just a hobby app, so I’m using old-school rolling file logs), I actually need to
sh into the container first to see the logs. So first to get the id of the container:
docker ps, and once we have the id of the container:
docker exec -it CONTAINER_ID /bin/bash. Opening the error log reveals:
Interesting! Following the link provided and scrolling down to Running Puppeteer under Docker tells you that we basically need to add a step in our dockerfile to install the necessary libraries (particularly
libnss3.so) in order for headless chrome to run. The dockerfile in their example is a bit verbose with some unnecessary steps, but the real magic is basically just:
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others) # Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer # installs, work. RUN apt-get update \ && apt-get install -y wget gnupg \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ && apt-get update \ && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/*
And voila! Puppeteer is now working under Docker. Hope this helped someone out, or was at least an interesting read :)