One reason is that unlike MS-DOS, the OS is hardware independent, except that an 8080-compatible CPU was needed (8080, z80, some NEC chips etc). Other than that you could use just about anything. It wasn't hard to port (more like transport than port) CP/M to a new system which met the minimum requirements (cpu, some minimum amount of RAM, floppy disks at least, and a TTY or terminal). Very different from MS-DOS, which required a mostly exact PC clone, and it had no provision for moving to another architecture. CP/M came with, among other tools, "MOVECPM", a program to relocate the operating system to a different place in the memory map (and write the result to a new boot floppy or whatever), an Alteration Guide document, and there were various books detailing the process.
CP/M didn't require a floppy. You could boot it off ROMs, tape or even paper tape (ASR-33s were fun). There's even a RDR: device that was supposed to be used to load from a card reader, tho I confess I never saw that in the wild.
MS-DOS was structured the same as CP/M: there was a small-ish machine dependent part you ported (called an 'OEM Adaption Kit' at one time), and a hardware independent kernel. At least through 3.0 and probably later, it did not require a PC clone. It ran on all sorts of non-PC 8088/8086 machines. Ever run MS-DOS on an 80186 with 8" floppies? I have. Now, your apps might not run because of machine level assumptions (video, usually), but MS-DOS did.
I still have a British-made 8086 almost-PC in the attic. It ran MS-DOS - but harddisks didn't work because the interrupt vector was different. That could be fixed - I burned a new EPROM and got it working. The company which imported the machine told me how - I called them and the guy who took the call explained in detail about interrupts and addresses and whatnot, and the next day I removed the PROM, brought it to work, copied it, patched it up, and burned a new EPROM. I'm still shaking my head at that memory. Try calling a company today and ask a technical question.
But the real problem with almost-clones was (as you said) that in practice applications more often than not bypassed the "ABI" and went directly to the hardware, and admittedly that's not MS-DOS fault per se. The result was anyway that lots of software wouldn't run on almost-clones, especially if they moved around too many of the assumed interrupts (e.g. 80186-based PCs - they had some of the peripherals of the 8088/8086 integrated, just not exactly in an IBM PC layout). One system I ran across was an "enhanced" PC-like semi-clone, probably using an 8086 or something better - the local health services had just started a huge campaign where they tried to get everyone to volunteer for a health checkup, and they were followed up with a new one some years later, and on and on (it still goes on, to this day). Anyway, for that early start, they used this MS-DOS computer to collect and print information. But the printer didn't work - the PC was nonstandard. I went to the hospital and figured out why, and patched things and got it working (I don't recall anything today about what or how or even why I could).
CP/M applications though - they didn't, for the most part, bypass anything - maybe that's because people writing the software knew that the hardware could vary wildly. Screen-oriented apps like WordStar usually needed a machine-specific patch for that, but otherwise CP/M software was way more portable than MS-DOS software. PC software though - vendors just thought "IBM PC hardware" and that was it, for the most part.