in VHDL:
"<=" is used in assignments of signals.
":=" is used for assignment of variables.
Signals can exist in an architecture, process and procedures.
Variables can exist in process and functions.
A signal at an architecture level is basically a wire. It connects signals together with perhaps a few gates, like:
ARCHITECTURE ... OF ... IS
SIGNAL a, b, c : STD_LOGIC;
BEGIN
a <= b AND c;
END ARCHITECTURE;
This way you can compute new values within an entity (not shown in example).
Using a process you could compute new values of a at the rising edge of a clock, i.e. sequential logic:
ARCHITECTURE ... OF ... IS
SIGNAL a, b, c : STD_LOGIC;
BEGIN
PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
a <= b AND c;
END IF;
END PROCESS;
END ARCHITECTURE;
Why have variables when we have signals? Because if you assign a new value to a signal, it's new value will not take action immediately. Only after the process is finished running the new value is used.
A variable however is updated instantly, so you can assign a value and then read that new value from it. A variable does hold it's value after you "exit" the process as well. But you cannot use them in an architecture, so they are best used as intermediate values.
In terms of simulation this is a key difference. Signals are simulated using delta delays. That means that if a new value is assigned to a signal, it's delayed to take that value at t+1 'delta'. If new values for other signals need to be computed (e.g. b or c changed in first example) it will happen at t+2 delta, t+3 delta, etc. Delta is an arbitrary time stamp, just to differentiate it will happen slightly later in the future.
Because all statements in a process happen at a 1 timestamp, time can only be advanced when the process is left or a wait statement has been hit (unusual to do if you target hardware, especially using free tools).
In terms of synthesis onto real hardware, either a signal or a variable in a process can result in a wire or D-flip flop.. This is dependent if the value is first written and then read (=wire) or first read and then written (= flip flop).
I'm sure this has high similarities to Verilog blocking and non-blocking statements, but I haven't programmed much Verilog, mostly read code. Both languages are very similar, VHDL is strongly typed , Verilog is loosely typed. Verilog has some unique features, but so does VHDL...