screenshot-to-code icon indicating copy to clipboard operation
screenshot-to-code copied to clipboard

Better docker deployment strategy

Open wearzdk opened this issue 2 years ago • 6 comments

Hi Creator,

The current Docker deployment strategy requires manual building and relies on docker-compose, which also results in significant disk space usage and runtime memory occupation. Perhaps we could optimize this by building the code before generating the image and combining the frontend and backend parts.

I have already done some local testing. The updated Dockerfile could look something like this:

FROM node:20-alpine as build-frontend
WORKDIR /app
RUN corepack enable

COPY ./frontend /app/
RUN yarn install && yarn build


FROM thehale/python-poetry as build-backend
RUN apt update && apt install -y binutils
WORKDIR /app
COPY ./backend /app/
RUN poetry install --no-interaction
RUN poetry run pyinstaller --clean --onefile --name backend main.py


FROM debian:bookworm-slim
ENV FASTAPI_ENV=production
WORKDIR /app
COPY --from=build-frontend /app/dist /app/static
COPY --from=build-backend /app/dist/backend /app/backend

EXPOSE 8000
CMD ["/app/backend"]

The primary goal here is to package the backend into an executable file using pyinstaller, and prepare the frontend build resources, all happening within the building phase. Then we put the executable file and webpage resource files into the final image for execution.

The resulting image has a size of only about 90MB, which can be pushed to Docker Hub, facilitating easier deployment. Furthermore, we can also extend the approach of using Pyinstaller to release directly executable files.

I have already completed most of this work. If you agree with this proposal, I can submit a PR.

wearzdk avatar Dec 06 '23 15:12 wearzdk

I don't understand Docker super well but this overall sounds good to me.

What happens when the repo gets updated?

abi avatar Dec 06 '23 16:12 abi

And do we still get to see all the logs for front-end and back-end as they are running? Want to make sure it's easy for people to debug as well as to update env vars, etc.

abi avatar Dec 06 '23 16:12 abi

In terms of development experience, there will be virtually no changes, although there will indeed be modifications to some sections of the code.

The primary changes in the frontend are: In a production environment, the backend URL is changed to a relative path (because it has become part of the setup). This can be handled using environment variables. For instance, /frontend/.env.development - this environment variable will only be used in development:

VITE_WS_BACKEND_URL=ws://localhost:7001
VITE_HTTP_BACKEND_URL=http://localhost:7001

So, the frontend's /frontend/src/config.ts needs to be modified as follows:

// Default to false if set to anything other than "true" or unset
export const IS_RUNNING_ON_CLOUD =
  import.meta.env.VITE_IS_DEPLOYED === "true" || false;

export const WS_BACKEND_URL =
  import.meta.env.VITE_WS_BACKEND_URL || `ws://${location.host}`;

export const HTTP_BACKEND_URL =
  import.meta.env.VITE_HTTP_BACKEND_URL || location.origin;

export const PICO_BACKEND_FORM_SECRET =
  import.meta.env.VITE_PICO_BACKEND_FORM_SECRET || null;

This means that if these environment variables are not specified, then use the current access URL.

The backend also needs to add new code, such as:

# Web UI
if os.path.exists("static"):
    app.mount("/webui", StaticFiles(directory="static", html=True), name="webui")

If the current path exists as "static", it is considered to be the production environment (of course, this should also be improved, it is best to access the webpage via /). Lastly:

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Since the final production environment runs the executable file packaged by pyinstaller, the server is launched at the entrance point.

The code changes roughly like that, though there would be some minor modifications for error corrections. As a developer, you can run:

docker buildx build -t screenshot-to-code .

to build the image, ultimately generating a 90MB production environment image. Run it locally with:

docker run -p 8000:8000 -e OPENAI_API_KEY=sk-xxx screenshot-to-code:latest

Afterward, you can push it to Docker Hub for others to use. This task could also potentially be automated under GitHub Action.

The original development work is not affected, and regular functionality updates will not impact the Docker configuration. For logs, users can view them through the Docker Desktop, Portainer, or other Docker panels, or by running the 'docker logs' command. Also, Docker container reduce the potential for problems, which means that users are unlikely to be troubled by runtime issues.

wearzdk avatar Dec 07 '23 01:12 wearzdk

All that sounds good except for the front end config changes, can't we just pass in a environment var during docker setup. Removing the current fixed fallback "local host:7001" will break the repo for lots of people currently using it locally without docker and without env vars.

abi avatar Dec 07 '23 13:12 abi

You're correct, introducing this feature shouldn't result in any breaking changes. I will attempt to overwrite the configuration by passing environment variables during the Docker build. After testing it out, I'll submit a PR.

wearzdk avatar Dec 07 '23 13:12 wearzdk

Thanks @wearzdk!!

abi avatar Dec 07 '23 14:12 abi