Either way, your client has to know what pod (group of Docker containers running your app) to send traffic to. The pods run on hosts and ports that aren't fixed in time. So if you want some kind of client-resolving load balancing without involving a central bottleneck on the server, you'd have to transport that inventory of pod-host-ports to the client, and then either provide a host-based proxy or open the ports on the nodes themselves. And of course you'd have to build the balancing/failover logic into the client (which would either have to be randomly distributed, or rely on resource utilization info fetched from Kubernetes).
That may be great for some narrow use cases, but for most applications, the load balancing pattern is much simpler.
Your load balancer definitely shouldn't be a bottleneck - you can deploy multiple LBs to handle the traffic and provide redundancy, though you need to use other methods to balance between them (DNSRR, anycast, etc).
kubernetes pretty much mandates either an LB because it is offloading the ingress job to a machine which can talk to the outside world and the VPC simultaneously. Otherwise you either get into complex hostport/nodeport/hostnetwork semantics... or use an ingress.
In the k8s world, there is not many simpler alternatives.