City led 1-0 on the night and 5-3 on aggregate when Riyad Mahrez scored after 73 minutes, and looked set to progress to an all English final with Liverpool.
Real Madrid substitute Rodrygo equalised in the 89th minute, before scoring again just one minute later to level the tie at 5-5 and send it to extra time.
Karim Benzema converted a penalty awarded for a foul on him by Ruben Dias five minutes into extra time, much to the delight of the boisterous crowd in Madrid.
The record 13-times European champions held on, keeping the shell-shocked City at bay to reach their 16th final.
Real will play Liverpool on 28 May in Paris, a repeat of the 2018 final in Kyiv which the Spaniards won 3-1.
Photo: Â© Action Plus Caption: Benzema celebrates