There's no reason to have full EC2 instances running a single container—EC2 instances are containers. (And Docker images are AMIs, and fig.yml files are CloudFormation templates, and...)
You can treat EC2 instances exactly the same as you treat docker containers—attaching EBS volumes to them in the way you'd attach data volume containers, attaching ENIs like you'd publish ports, etc. You can get exactly your "atomic, full-system rollback" just by having a CF template with a template parameter for the DB AMI to start in an Autoscaling Group, and then pushing a change to that variable. (Effectively, this gives you the same semantics as using Heroku's "config:set" CLI subcommand.)
But people don't do things this way. Why? Because the different pricing models create a different system of incentives around the two ecosystems. EC2 instances are thought of, fundamentally, as machines, rather than as application containers. There are adapters meant to make their usability for such more clear (e.g. Elastic Beanstalk), but in doing so they expose the relative absurdity of the VM pricing model.
You can treat EC2 instances exactly the same as you treat docker containers—attaching EBS volumes to them in the way you'd attach data volume containers, attaching ENIs like you'd publish ports, etc. You can get exactly your "atomic, full-system rollback" just by having a CF template with a template parameter for the DB AMI to start in an Autoscaling Group, and then pushing a change to that variable. (Effectively, this gives you the same semantics as using Heroku's "config:set" CLI subcommand.)
But people don't do things this way. Why? Because the different pricing models create a different system of incentives around the two ecosystems. EC2 instances are thought of, fundamentally, as machines, rather than as application containers. There are adapters meant to make their usability for such more clear (e.g. Elastic Beanstalk), but in doing so they expose the relative absurdity of the VM pricing model.