Zero downtime is usually tricky and will greatly depend on your architecture. I can I think of two overall paths to success, but there are more (and these may or may not work for your use case).
1) Prod A / Prod B flip. Your current code runs in prod, we will call Prod A. Bring up an entire copy of your stack with the new code version as prod B.. Once it is up and stable, you switch traffic over to it.. once all traffic is switched, you kill the old servers. Tricky part with this is state, do you need to worry about state loss? If you keep a constant database that both prod A and prod B hit, you can do this without too much trouble. Make sure you do no state on your individual app servers (session cache etc).
2) Slow roll. Say you have 10 web servers that are behind a load balancer. You take 1 down - upgrade it - then add it back. Repeat until all 10 are upgraded. The trick here is what happens if a user hits code version A then B then back to A? If it doesn't matter, easy. If it matters, you may need to lock clients to machines from the load balancer, so that no one that has seen the new B will ever switch to an old server still on A.