Alternatively, you could map port 9200 from the Elasticsearch container to the host system, which the stack Compose also seems to do and then connect Metricbeat to the host network with network_mode: host.
With this approach, Metricbeat can be configured to talk to localhost (like you have now), because localhost is now the actual host machine and Elasticsearch has been bound to port 9200 on the host.
In your current configuration, localhost is just the Metricbeat container, so it's trying to talk to itself.