/dev/console
https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/serial-console.rst
On Linux, the kernel console can be configured using the console= boot option. Kernel code which calls printk() may write messages to it, e.g. when a device is loaded or an error occurs. These messages are also buffered by the kernel. When a console device is found and started, it receives all the previously buffered messages.
You can pass console= multiple times to configure multiple consoles, and messages will be written to all of them. Apparently you can only select one console of each "type": you can't use both console=ttyS0 and console=ttyS1.
The kernel documentation specifies /dev/console as a character device numbered 5:1. Opening this character device opens the "main" console, which is the last console specified and capable of acting as a tty. The initial non-kernel process init, aka PID 1, is started with /dev/console connected to standard output, standard error, and standard input.
If there are no tty consoles, opening /dev/console returns the error ENODEV. The kernel will print log a message, and start init regardless.
You can also see a list of tty consoles by reading /sys/class/tty/console/active. systemd documentation points out that the first device shown is the main console. The list is actually in reverse order of the kernel command line. The current kernel documentation incorrectly states that the last device shown is the main or "active" console. For some reason it is possible to poll this file for changes (in case console devices are removed?).
For an example of a kernel consoles which do not qualify as tty devices for /dev/console, see netconsole, or my favourite console the line printer.
Inside a systemd-nspawn container, the file /dev/console is replaced with a pseudo-terminal device, aka PTY. These would be best described as virtual terminal devices. These are created dynamically and are also used to implement graphical terminal emulators like GNOME Terminal, and for remote access like ssh.
/dev/tty0
The Linux TTY device nodes tty1 through tty63 are always virtual terminals. They are also referred to as VTs, or as virtual consoles. They simulate multiple consoles on top of the physical console device driver. Only one virtual console is shown and controlled at a time. The active terminal can be switched, e.g. using chvt, or Ctrl+Alt+F1 through however many function keys you have.
You can also read and write to the current VT using /dev/tty0. tty0 is the usual kernel console, e.g. if you did not select one explicitly. "At this time, the system first looks for a VGA card" (which is what VTs run on). The most well-known alternative to this is a "serial console" (e.g. ttyS0). "If you don't have a VGA card in your system, the first serial port will automatically become the console." It is not possible to use the VT system on top of a serial console.
/dev/tty
/dev/tty is one of the three standard device files specified by POSIX (/dev/ being one of the three standard directories). Opening it is equivalent to opening the controlling terminal of the current process. The controlling terminal is set when a process first opens a terminal. For example, in init, it would refer to /dev/console.
Detaching from the controlling terminal is one of the steps traditionally required to start a background process, for example a system logging daemon. In more modern systems this is performed by the init system, e.g. systemd, when it starts the service. For more technical details see man daemon).