Cache images in custom module without caching entire render array
Blocks are cached by default in Drupal 8.  The image non-cachable warning isn't a drupal issue.  It's a false flag from chrome.  Because image derivatives in Drupal (those generated from an image style) have a query argument chrome thinks it's not cachable.

To test that your block plug-in is caching, you can put a die() statement inside the build method to test if the code runs on each page load.  If it is running on each page load, you can comment out elements until it does use cache.  Then it should run the code once and then on subsequent page loads it won't run the code.

To make sure your block is refreshed when certain nodes are cleared, you can add them to the cache tags in your build array.  This may happen automatically if you are pulling the render array from each image from the node it came from.  Otherwise you can add them in your code like this:

$build['image']['#cache']['tags'][] = 'node:123';


To test, clear the cache and reload the page.  Verify on the cache_render table that the block is cached and your tag is there.  

To test that editing that node updates the block, first update that node.  Then on the next page load of the page with your block, it should re-run the code for your block plugin.

To make sure the block is unique per user, you can add the user context to the cache contexts array

$build['image']['#cache']['contexts'][] = 'user';


To test, clear the cache, then reload the page with your block.  Check on the cache_render table that the cached item has user in the cid column.

Something like:

entity_view:block:my_block:[languages:language_interface]=en:[theme]=abilitylab_theme:[user]=123 


Then test the block with a different user, and there you go:  Unique block per user.