Imagine a pipe that only fits dogs. The pipe has two ends. One side has person A pushing dogs into the pipe - the source. The other side has person B pulling dogs out of the pipe - the sink.
The source can push anything into the pipe that is a dog or more specialized than a dog, ie they can push in specific dog breeds like only terriers. So the source can treat the "Pipe of Dog" as a "Pipe of Terrier". It's okay because they're dogs and the pipe accepts dogs. The source cannot push in any animal that isn't a dog.
When the sink pulls something out of the pipe, they can expect to receive anything that's a dog or less specialized than a dog, eg they can expect that everything they receive will be an animal. So the sink can treat the "Pipe of Dog" as a "Pipe of Animal". The sink can expect not to receive anything that isn't an animal. The sink can't expect that they'll only receive terriers, since the source may decide to push bulldogs instead.
The source end of the pipe is contravariant on Dog. The sink end of the pipe is covariant on Dog.
Edit: To bridge this with programming: A function is a pipe. The input of the function is the source. The output of the function is the sink. A function that accepts a Dog could be given a Terrier, and a function that returns a Dog can be considered to return an Animal.
Data structures are equivalent to functions in this respect. A data structure that produces instances of Dog is the same as a function that produces a Dog - IEnumerable<Dog> in C# is also an IEnumerable<Animal>, Promise<Dog> in JavaScript is also a Promise<Animal>. A data structure that both accepts and returns instances of Dog is invariant on Dog - List<Dog> in C# can't be List<Animal>, Array<Dog> in JavaScript can't be Array<Animal>, because that would let someone push Cats into them.
Edit 2: By the same reasoning, a callback parameter flips the input-output-variance association for every level of nesting. For a first-level callback, the inputs to the callback are produced by the function, so the input of the callback is equivalent to an output of the function, and the output of the callback is equivalent to an input of the function. Eg consider a JS function `function foo(x: (y: A) => B): C { }` This is covariant on A, contravariant on B, covariant on C.