Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: PNGR – Docker-compose for a Postgres-Nginx-Golang-React stack (github.com/karlkeefer)
138 points by karlkeefer on Nov 23, 2019 | hide | past | favorite | 78 comments



The purpose of this project was to get a docker-compose setup with good developer ergonomics.

Basically you should just be able to `docker-compose up` and start hacking at front-end or back-end code and have hot reloading of both _just work_.

I aimed to keep things pretty minimal. With some effort you can swap Redux for Unstated, pick a different component library, swap my api router for gorilla mux, whatever you want.

Happy to make changes based on feedback. I'm _sure_ this could be improved with more eyes on it.

Enjoy :)


Is a requirement of this framework to only use PNG for graphics? On a more serious note this is awesome! Good job PNGR would be great for a new project.


I can't tell if it was a joke, but just in case someone else has problems:

Postres

Nginx

Golang

React

So .. nope! :)


It's in the title of this very submission =b


Yup! Which in some cases would get truncated, i.e. in the HN interface itself: "Show HN: PNGR – Docker-compose for a Postgres-Ngin...".


Neither web or the mobile site seems to truncate the title so not sure what "HN interface itself" you're referring to. Maybe you're using a third-part client?


Despite all my downvoters ... when you’re in single comment view the text most definitely is truncated on both desktop and mobile :).


That's absolutely true! Haven't noticed that before. I guess me and most people don't look for the title while responding to a comment.


Believe it or not, I generally find myself in these submissions based on viewing comments from https://news.ycombinator.com/newcomments . So yeah, just a bit jarring seeing so many people basically use the downvote button to call me a liar.


This is bad ergonomics because to use it the developer has to install Docker which is a monster made by commercial company and run with root privileges. It's privileges are enough to gather "telemetry" including you browser cookies and history, install and uninstall software or delete all files on your hard drive. Why would anyone want to run such dangerous daemon I don't understand.

Also on Windows and Mac Docker user virtual machines with worse performance.

It is OK to install Docker on a throwaway VPS, or in office, but not on your own machine.

Instead of this poor designed technology it is better to use portable programs (which can be downloaded in binary form to avoid compilation) and collection of bash scripts. Then you won't have to install the aforementioned proprietary daemon.


Bad ergonomics? Pretty much every developer I interact with has Docker already installed, which makes this very ergonomic to use.


Uses alpine images for small footprint

That's really become a docker meme. Please be aware you're sacrificing performance due to not using glibc.


Since it's not using a multi-stage build, go dev tools are clogging up the image, losing the benefit of using alpine in the first place.

https://link.medium.com/S2z0PtVHQ1

If you're just running a go binary you can use distroless as the base for even smaller images.


Thanks for linking this. Will implement.

Also like the "tags" within the dockerfile for dev and prod - much cleaner than the multi-file solution currently in the repo.


I hadn't heard this before, also sold by small image sizes. What is the big performance weakness of mscl vs glibc?

Anything beyond binary size vs shared pages?


glibc has thread-local pools for malloc, afaik musl uses locks.


I'm very weak when it comes to stuff at this level but this sounds like it might be something I really want to know. If I want to run Python or Ruby, what is the practical downside to Alpine? Am I understanding correctly that Python or Ruby interpreters built to run on Alpine use musl instead of glibc, and that might have very noticeable performance impacts?

This thread is opening my eyes, I kind of assumed as long as I was running the right version of those interpreters, I was getting more or less the same thing. That feels like a very bad assumption now.


As always when it comes to performance, you should measure the difference yourself and see if it's a switch worth doing for your use case. "very noticeable performance impacts" depends so much on the context that it's hard to answer in a general way.


> also sold by small image sizes.

Out of curiosity, what sold you on this? Has caching of a docker image been considered in this conclusion as well?


I was just a casual local user, and didn't have any production use of it. At the time, if my memory serves, images easily got into many GB locally. This was before Ubuntu and others had produced smaller official images of their own.


Alpine has problems with its network stack that make it unsuitable for use in some contexts.


Footprint (ie. of the built container image which must be transferred back and forth to repos) and runtime performance are two different things you may care to optimize differently or not.


Thanks - will update the prod recipe.

I saved hundreds of MB on the front-end images switching to alpine so I want a little alpine crazy :)


Performance of what? Building an image?


Runtime performance. Musl has different performance characteristics to glibc (glibc uses thread-local pools for malloc, while musl uses locks).


> Starting with 1.1.9, musl’s dlerror state is thread-local and now matches the glibc behavior.

https://wiki.musl-libc.org/functional-differences-from-glibc...


dlerror is entirely irrelevant here.


In terms of what? Performance? Musl is more correct. Most people aren't serving Google or Facebook levels of requests. My only issue is with people choosing software purely based on performance.


No, it's just that dlerror and malloc are two different functions.


I think that's self-evident, thank you.


musl libc has been robust and fast in my experience. What performance issues have you encountered?


I strongly recommend against using the Postgres alpine image, sorting is irrevocably broken on it. Cf: https://stackoverflow.com/questions/58135353/postgres-seems-...


Thanks - I haven't really put any of this "through the motions" so hadn't encountered that yet.

Thankfully it's a one word change :)


An acronym that’s pronounceable is more likely to become popular, e.g. LAMP, CRUD, etc. I guess you could say PNGR like “pinger”, but it’s a bit awkward. So I suggest a slightly modified acronym: PRNGL, pronounced as “Pringle”. The L of course stands for Linux. So now you can tell people “it’s running on the PRNGL stack.” One downside is that PRNG is already a well-known acronym. But so is PNG, so I don’t think it’s any worse than PNGR in that respect. On the plus side, the PRNGL stack sounds pretty tasty.


I’m not sure what you are on about there. PNGR is as pronounceable as PRNGL is, in fact they even have the exact same dropped vowels (i followed by an e). You even have to force in an unnecessary extra word which devalues your acronym imho.

Personally, my issue with PRNGL is that it makes me think it has something to do with random numbers (PRNG should make most think of pseudo random number generator). The same argument could of course validly be made by someone about PNG being a common acronym too, but for some reason PNGR doesn’t make me immediately think it has to do with image formats, where as PRNGL does make me think random numbers. My own mental biases are likely a factor there.


How about PLNGR (plunger)?


I liked PNGR, sounds like Pee-eN-Gee-Er in my head.


Very cool. I’ve been thinking of doing the same for a .net core app and maybe a couple other common frameworks. Might be PITA to keep up to date though.


Thanks!

I actually made this over a year ago, and just this week updated all the deps in order to make it public.

There were thankfully few breaking changes, though I did burn like an hour debugging a weird dependency that wasn't compatible with golang 1.13 modules. So it goes!

So I guess only a slight PITA so far haha


Go for it! I made my own cookiecutter template for Django: https://github.com/clintonb/cookiecutter-django. My main goal was to get projects running faster. Updates every 6-12 months are fine. Alternatively, update when you are ready to start the next project, or shortly after you’ve made significant changes to the most recent project.

For example, my latest project has a React frontend. I’ll incorporate that back into my cookiecutter after I’ve worked out various issues.


Nginx seems like one more layer of trouble. Why not just use Go?


I almost always use a reverse proxy for some or all of the following:

- Serving static files

- Not needing to care about binding your application to a privileged port (below 1024)

- SSL certificate handling

- Hosting several applications on the same IP

For a minimal development setup you might not need it though, depends on what you want to do I guess.


> "I almost always use a reverse proxy for some or all of the following:

- Serving static files

- Not needing to care about binding your application to a privileged port (below 1024)

- SSL certificate handling

- Hosting several applications on the same IP"

All of this should be handled at a higher level with load balancers doing your SSL termination, and CDN for static files. There's no reason your application server should care about any of that stuff.


I mostly work on small scale stuff. There is nothing to load balance if you only have one machine :D

And for development you probably don't want to use a CDN.

Edit: it just occurred to me that a load balancer is nothing but a reverse proxy anyway. Running on a different machine usually, but not really different from nginx on the local machine from the point of view of the application software.


Which is why s/he is using Nginx to simulate that in the dev environment if I’m not misunderstanding?


Yes nginx is great to bundle in early


I came up on this issue, I think the main problem is as React-Router is being used its a SPA; all front end routes need to resolve to the same index.html. Totally do-able with Go but it was a bit of a pain to get it working, I ended up wrapping the "route not found" handler with some logic to serve index for given frontend routes. nginx gets you this very quickly so I ended up using this instead.


If it was my case, I still undecided which reverse proxy can replace Nginx, however, there are no known performance benchmarks for Caddy 2 I could refer to.

Nginx was designed to handle backend services easily and programmers from different background may not be well verse in Golang at the beginning.


It is generally repeated that the Go stdlib webserver is internet-facing-ok and production-ready (with appropriate timeouts being set on the http.Server), making a reverse proxy unnecessary.

There are also dead-simple TLS libraries, some that will even get LE certs automatically.


Agreed, I have recently built a web server entirely (with timeouts) in Go too and acme.sh for LE certs but I still haven't find a way to support Wordpress from Go to PHP-FPM, I'm not well verse to fix the only this part, if you have any suggestion is greatly appreciate.


Ssl certificate

Reverse Proxy for possible load balancing?


"write some tests, you animal", issue created a year ago


9 hours ago this issue was update to capitalise the W. So there’s progress!


Hear, hear! I'm 90% of the way there, now.


Guilty as charged. In my defense the repo sat unchanged for almost the entire year haha


Docker with the TCP proxy absolutely destroys network performance (unless you use the host network). Just beware. Hot-reloading is really neat! Good work.


I looped back and setup a docker-compose.prod file that utilizes host networking (and addresses some of the other issues raised in these comments). Thanks for the heads up :)


I've been meaning to benchmark but NGINX in a container like this kinda wrecks performance in most cases.

The situations you stand up a container infrastructure these days you usually always already have a cloud load balancer like an ALB in AWS in front of it.


I have some sketch notes in the readme outlining production strategy that doesn't depend on using containerized nginx (or postgres), but I should make that more clear.


The other day, I just made my first, something a lot similar: postgres+nginx+python+php, but yours is much more proper than mine. Yours is the example I really needed, thank you


Glad it's helpful for you!


Dammit, I spent the other weekend cobbling together something similar for my side project!

I'm definitely going to have a close look and migrate most of the components over. Thanks!


If you find anything silly please open an issue or pull request! This thing does not have all the kinks worked out.


Thanks for sharing! Having a tool like this makes me a lot more willing to start new projects.

Created a couple GitHub issues for you with ideas to make this project even better :)


Appreciate that! Will try to implement some of the improvements that popped out in this thread, as well.


I don't get it. Maybe I'm just the fish asking what water is but this doesn't look like anything special. What is there to fawn over and swoon? Maybe I don't see it because I use Docker 4 Development daily?


The whole repo is largely just a shortcut for folks that haven't set something like this up before. Nothing special - just aiming for useful.


You might not need nginx if you are using Go. I would only add it if neccessary.


It's really useful (to me) to be able to split the api traffic and front-end traffic and the nginx configs make that really clear. Agree that it could be a lot smaller footprint, though, if I built front-end files into the golang app and had a single docker image that _did it all_ (besides the database).


are you seriously telling people that they should run their application as root?

you do know that ports <1000 (http: 80, https: 443) require root, right?


These are containers, why on earth would you assume an app needs to run on a privileged port? You just run the app in the container on say 8080 or 8443 inside the container and expose it outside the container as 80 or 443 respectively.


Ha, i literally forgot about that. Despite using docker every other day. Thanks for correcting me


sudo apt-get install libcap2-bin # Debian/Ubuntu/etc sudo setcap 'cap_net_bind_service=+ep' /path/to/program

will allow program to bind low numbered ports by non-root user.

Just learned this deploying a new rails app behind caddy2 (which is running as the deploy user I setup).


You only need root to open the port. Once you do that you can drop privileges.


I'm not and what does it have to do with what I said? :) Do you know that nginx must be running as root, right?


The language is called Go not golang [1].

[1] https://twitter.com/rob_pike/status/886054143235719169?lang=...


Good luck searching for "go" in the internet.


I also use 'golang' exclusively when searching for go related stuff :) doesn't take away my point.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: