A quick-and-dirty way that doesn't require rewriting is just to use the experimental --squash flag[1] (i.e. add: --experimental --squash) when running docker (you can also turn on --experimental permanently by putting { "experimental": true } into docker's /etc/docker/daemon.json file. And, on a Mac, you can open the Docker app and there's a checkbox to turn on experimental features and then you can just use --squash in the terminal).
Anecdotally, I had a 4.67GB docker file that --squash reduced to a little over 1GB. However, a rewrite of the docker file to do a multi-stage build took that 4.67GB file down to just under 0.5GB.
P.S. Pro-tip: if you want to speed up your docker builds, if you're using 18.06 or higher, you can use use buildkit [2]. Again, anecdotally, on a 6-core Macbook from 2019 I think I got a 30% speedup or so -- your mileage may vary (a colleague with an 8-core machine I think got a 50% speedup -- it's been a while, so these percentages may be a bit off, but you get the idea).
It was also an experimental feature early on, so you needed to have experimental features turned on (as described above with --squash). With later versions you don't need to do that -- check your version's documentation.
Anyway, you can either set an environment variable to use it i.e. export DOCKER_BUILDKIT=1 or you can set this permanently by adding: {"features":{"buildkit": true}} to /etc/docker/daemon.json
[0] https://blog.logrocket.com/reduce-docker-image-sizes-using-m...
[1] https://docs.docker.com/develop/develop-images/multistage-bu...
[2] https://www.giantswarm.io/blog/container-image-building-with...