Взаимодополняемые я бы сказал. Например можно использовать их так - gitlab-ci собственно как continious integration(скажем проект на Go, поэтому тут происходит сборка и различные тесты), после прогона ci запускается job, которая с помощью ansible автоматически обновляет софт на staging. Затем после проверки что на staging все ок, по нажатию кнопки(gitlab environments) происходит деплой, опять же используя ansible, на production.