The simplest way to understand MD5 is to implement it from the specification, which is rather simple.
In very crude words:
The data to hash is a sequence of bits. Let's keep things simple, and let's assume that it is a sequence of bytes. A few extra bytes (the "padding") are appended to that sequence, so that the number of extra bytes is between 9 and 72 (inclusive) and the total length after padding is a multiple of 64. The specification explains the contents of the padding; basically a lot of zeros, and an encoding of the input data length.
The padded data is split into blocks of 64 bytes. The blocks will be processed one by one. Processing of each block (64 bytes) takes as input a 128-bit value (16 bytes) which is the output of the processing of the previous block, and outputs a new 128-bit value.
Since the first block has no previous block, a conventional fixed value is used to start the process. The MD5 specification details that value.
The complete MD5 output is the 128-bit value you get after processing the last block.
The processing of a single block splits both the 128-bit value obtained from the previous block, and the new block to process, into 32-bit words (4 words for the previous value, 16 words for the block). All computations are done with these 32-bit words. The overall structure has been described as akin to an encryption algorithm lying on its side: the 64-byte block is used as a kind of key to encrypt the 128-bit running state, in a generalized Feistel scheme. I am aware that such an assertion does not really explain things -- to really get what is going on in the algorithm, use your favourite programming language and try to implement it.
(Any language should be fine for such a task, since it is merely about learning, but some are less fine than others. For instance, Javascript's numbers are really floating point values, which is cumbersome for implementing MD5. Java and C# are good for such tasks, notably because they have integer types with a guaranteed 32-bit length, exactly what you need for MD5.)
You might also want to read this answer, which tries to explain why hash functions are "one-way", and takes MD5 as an example, so it includes a description of MD5.