A container is a lightweight, stand-alone, and executable software package that includes everything needed to run a piece of software, including the code, runtime, system tools, libraries, and settings.
Even though Docker is used as a synonym for containers, the reality is that they have existed long before Docker was a thing. Unix and Linux have had containers in some form or another since the late 70s, when chroot was introduced. Chroot allowed system admins to run programs in a kind-but-not-really-isolated filesystem. Later, the idea was refined and enhanced into container engines such as FreeBSD Jails, OpenVZ, or Linux Containers (LXC).
But what are containers?
A container is a logical partition where we can run applications isolated from the rest of the system. Each application gets its own private network and a virtual filesystem that is not shared with other containers or the host.
Running containerized applications is a lot more convenient than installing and configuring software. For one thing, containers are portable; we can build in one server with the confidence that it will work in any server. Another advantage is that we can run multiple copies of the same program simultaneously without conflict or overlap, something really hard to do otherwise.
However, for all this to work, we need a container runtime, a piece of software capable of running containers.