Given that there is no point of centralization, where a node can ask 'who are my peers?', how does discovery work?
There is a default set of nodes in the monero source code that it will attempt to use. These default nodes are used to bootstrap the list of peers your node will connect to initially. Nodes are able to request a list of peers from the other nodes they are connected to, which produces a large set pretty quickly. This is helpful to let you get a node up and running very quickly without the daemon having to scan all IP addresses.
However, once the peerlist has been pulled from the default list it can connect to any other known node. In this sense, the initial node list is actually centralized in the monero daemon, but only at first. To see this in action use the
print_pl command in the daemon. It will print out a big list of all the peers your node knows about.
For those who are paranoid about the origin of the list, you can use the monero daemon
--add-exclusive-node during your first sync) to add some nodes to your local peerlist manually. You can also try out the
--seed-node parameter, which just connects to a node and pulls its peer addresses.