IRPs are Windows kernel structures. They are used to represent an I/O request as it moves around the kernel system. A filesystem, for example, that needs to read from a block device generates an IRP that represents its read request and hands it to the block device. The block device processes the IRP, puts the results in the IRP, marks it complete, and tells the filesystem to look at the IRP to get the data. Devices can maintain queues of IRPs they are working on.
An IOCTL is a request to a device, typically received from a user-space application. They're generally used for requests that don't fit into a standard API. For example, you wouldn't normally use an IOCTL to open a file on a filesystem.
When a program issues an IOCTL to a device, an IRP (typically IRP_MJ_DEVICE_CONTROL) is created in kernel space to reflect that request.
In summary, an IOCTL is a particular type of "miscellaneous" request to a device driver. An IRP is a data structure for managing all kinds of requests inside the Windows driver kernel architecture.