A namespace, as defined in the Linux namespaces(7) manual page, “wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource.” Changes a process makes to that resource are visible to other processes inside the same namespace but remain invisible outside it. The page states plainly that this mechanism enables the implementation of containers.
The namespaces(7) page documents eight namespace types, each isolating one category of system resource: the PID namespace isolates process IDs, the network namespace isolates network devices, stacks, and ports, the mount namespace isolates mount points, the user namespace isolates user and group IDs, the UTS namespace isolates the hostname and NIS domain name, the IPC namespace isolates System V IPC and POSIX message queues, the cgroup namespace isolates the cgroup root directory, and the time namespace isolates the boot and monotonic clocks.
By combining these namespaces, a process group can be made to behave as if it has its own private machine: its own process tree starting at PID 1, its own network interfaces, and its own filesystem mounts. This is the “what it can see” half of a container.
The other half is cgroups, which constrain how much of the host’s resources the isolated process group may consume. A container runtime such as runc creates a container precisely by entering a fresh set of namespaces and placing the process under cgroup limits.