It isn't explicitly explained, but the reason why it must be in this command and not separated out is because each command in a dockerfile creates a new "layer". Removing the files in another command will work, but it does nothing to decrease the overall image size: as far as whatever filesystem driver you're using is concerned, deleting files from earlier layers is just masking them, whereas deleting them before creating the layer prevents them from ever actually being stored.
https://www.docker.com/blog/introduction-to-heredocs-in-dock...
FROM foo AS builder
.. build steps
FROM foo
COPY --from=builder generated-file target
(I hope I got that right; on a phone and been a while since I did this from scratch, but you get the overall point)Caveat: it doesn't work on Fly.io. They seem to be having some issue with OCI manifests: https://github.com/containers/skopeo/issues/1881 . They're also having issues with new docker versions pushing from CI: https://community.fly.io/t/deploying-to-fly-via-github-actio... ... the timing of this post seems weird.
FWIW the article says
> create a Docker image, also known as an OCI image
I don't think this is quite right. From my investigation, Docker and OCI images are basically content addressed trees, starting with a root manifest that points to other files and their hashes (root -> images -> layers -> layer configs + files). The OCI manifests and configs are separate to Docker manifests and configs and basically Docker will support both side by side.
It is really weird looking at Dockerfiles for the first time seeing all of the `&% \` bash commands chained together.
https://docs.docker.com/develop/develop-images/dockerfile_be...
> Official Debian and Ubuntu images automatically run apt-get clean, so explicit invocation is not required.
1) a layer is essentially a docker image in itself and
2) a layer is as static as the image itself
3) Docker images ship with all layers
Thanks jchw!
But I've been really enjoying it as a way of just telling a PaaS "hey here's the compiler/runtime my code needs to run", and then mostly not having to worry about it from there. It means these services don't have to have a single list of blessed languages, while pretty much keeping the same PaaS user experience, which is great
It's worth noting that the Docker experience is very different across platforms. If you just run Docker on Linux, it's basically no different than just running any other binary on the machine. On macOS and Windows, you have the overhead of a VM and its RAM to contend with at minimum, but in many cases you also have to deal with sending files over the wire or worse, mounting filesystems across the two OSes, dealing with all of the incongruities of their filesystem and VFS layers and the limitations of taking syscalls and making them go over serialized I/O.
Honestly, Docker, Inc. has put entirely too much work into making it decent. It's probably about as good as it can be without improvements in the operating systems that it runs on.
I think this is unfortunate because a lot of the downsides of "Docker" locally are actually just the downsides of running a VM. (BTW, in case it's not apparent, this is the same with WSL2: WSL2 is a pretty good implementation of the Linux-in-a-VM thing, but it's still just that. Managing memory usage is, in particular, a sore spot for WSL2.)
(Obviously, it's not exactly like running binaries directly, due to the many different namespacing and security APIs docker uses to isolate the container from the host system, but it's not meaningfully different. You can also turn these things off at will, too.)
I don't totally understand the maintenance story. On heroku with buildpacks, I don't need to worry about OS-level security patches, patches to anything that was included in the base stack provided by the PaaS, they are responsible for. Which I consider part of the value proposition.
When I switch to using "Docker" containers to specify my environment to the PaaS... if there is a security patch to the OS that I loaded in one of my layers... it's up to me to notice and update my container? Or what? How is this actually handled in practice?
(And I agree with someone else in this thread that this fly.io series of essays on docker is _great_, it's helping me understand docker better whether or not I use fly.io!)
Currently, I'm working in 3 projects at once + my own. Even though it's all PHP, the projects are PHP 8.1 / Symfony 6.2, PHP 8.2 / Symfony 6.2, PHP 8.1 / Laravel 8 and the newest project I joined is PHP 7.4 / Symfony 4. I have to adhere to different standards and different setups; I don't want to switch the language and the yarn or npm versions or the Postgres or MySQL versions and remember each one. Some might use RabbitMQ, another Elasticsearch.
Docker helps me tremendously here.
Additionally, I add a Makefile for each project as well and include at least "make up" (to start the whole app), "make down", "make enter" (to enter the main container I'll be working with), "make test" (usually phpunit) and perhaps "make prepare" (for phpunit, phpstan, php-cs-fixer, database validate) as a final check before adding a new commit.
Locally, I had additional aliases on my shell. "t" for "make test", "up" for "make up".
So now I just have to go to a project folder, write "up" and am usually good to go!
If someone updates nodejs, just pull in the latest dockerfile. Someone updates to a new postgres version? Same thing. It's so much better than managing native dependencies.
Have you ever worked somewhere where in order to run the code locally on your machine, it's a blend of "install RabbitMQ on your machine, connect to MS-SQL in dev, use a local Redis, connect to Cassandra in dev, run 1 service that this service routes through/calls to locally, but then that service will call to 2 services in dev", etc?
It includes running Rails and also Sidekiq, Postgres, Redis, Action Cable and ties in esbuild and Tailwind too. It's all set up to use Hotwire as well. It's managed by Docker Compose. The post also includes a ~1h hour ad-free YouTube video. The example app is open source at https://github.com/nickjj/docker-rails-example and it's optimized for both development and production. No strings attached. The example app has been maintained and deployed a bunch over the years.
It's a super productive framework to develop in, but deploying an actuals Rails apps - after nearly 20 years of existance, still seems way more difficult than it should be.
Maybe it's just me.
- https://gitlab.com/sdwolfz/docker-projects/-/tree/master/rai...
Haven't spent the time to document it. But the general idea is to have a `make` target that orchestrates everything so `docker-compose` can just spin things up.
I've used this sort of thing for multiple types of projects, not just Rails, it can work with any framework granted you have the right docker images.
For deployment I have something similar, builds upon the same concepts (with ansible instead of make, and focused on multi-server deploys, terraform for setting up the cloud resources), but not open sourced yet.
Maybe I'll get to document it and post my own "Show HN" with this soon.
The downside of capistrano was that you'd be responsible for patching dependencies outside of the Gemfile and there would be occasional inconsistencies between environments.
It's fine for people who are just starting out or want a repeatable environment.
Also, if you want stability and fewer headaches long-term:
- use a RHEL-derived kernel and customize the userland (container or host) quay.io has a good cent 9 stream. Ubuntu isn't used at significant scale for multiple reasons, and migrating over later is a pain.
- consider podman over docker
- use packaging (nix, habitat, or rpms) rather than make install (and use site-wide sccache)
- container management (k8s or nomad)
- configuration management (chef) because you don't always have the luxury of 12factor ephemeral instances based on dockerfiles and need to make changes immediately without throwing away a database cluster or zookeeper ensemble
- Shard configuration and app changes, with a rollback capability
- Have CI/CD for infrastructure that runs before landing
- Monitoring and alerting
- Don't commit directly to production except for emergencies. Require a code review signoff by another engineer. And be able to back out changes.
- Have good, tested backups that aren't replication
- Don't sweat the small stuff, but get the big stuff right that doesn't compound tech debt
...but... it's set to True....
It's super cool rails 7.1 is including the Dockerfile by default - not that rails apps need more boilerplate though..
[1]: https://pragprog.com/titles/ridocker/docker-for-rails-develo...
Deploying it was still always a hassle and involved searching for existing Dockerfiles and blog posts to cobble together a working one. At the beginning I always thought I'm doing something wrong as it's supposed to be easy and do everything nicely out of the box.
And dhh apparently agrees (Now at least: https://dhh.dk/posts/30-myth-1-rails-is-hard-to-deploy) as there's now a default Dockerfile and also this project he's working on, this will make things a lot nicer and more polished: https://github.com/rails/mrsk
The idea is I could run a command like `bundle packages --manager=apt` and get a list of all the packages `apt` should install for the gems in my bundle.
Since I know almost nothing about the technicalities and community norms of package management in Linux, macOS, and Windows, I'm hoping to find people who do and care about making the Ruby deployment story even better to give feedback on the proposal.
My primary use case for this is PCI Compliance. While PCI DSS and/or HIPAA do not specifically rule out Docker, the principle of isolation leans heavily twoard the principle that web hosts must be running on a private virtual machine.
This rules out almost all docker based-PaaS (including Fly.io, Render.com, AWS App Runner, and Digital Ocean), as these run your containers on general Docker hosts. In fact, the only PaaS provider that I can find advertising PCI compliance is Heroku, which now charges +$1800/month plus for Heroku Private to achieve it.
I would love to share my configuration with anyone that needs it.
"Ruby on Whales: Dockerizing Ruby and Rails development"
https://evilmartians.com/chronicles/ruby-on-whales-docker-fo...
Previously posted to hn - but without any comments.
Still, I'd much rather use Nix/Guix over Dockerfile.
I can chose my hosting provider and switch each other.