City's first defeat in any competition since April ended their 11-match winning streak away from home, although Pep Guardiola named an experimental team with an eye on Sunday's Premier League derby at Manchester United.
Teenager Phil Foden was given his full debut, becoming the youngest Englishman to start a Champions League game, with Gabriel Jesus, Leroy Sane, Ederson and Fernandinho the only regular starters included, and City's lack of cohesion proved costly.
Shakhtar knew a draw would be enough to send them into the knockout rounds, regardless of Napoli's result at Feyenoord, but they took the lead after 25 minutes, Bernard beating Danilo and bending a fine shot beyond Ederson after Marlos did superbly to create space.
Right-back Danilo was at fault again six minutes later when Shakhtar extended their lead, a ball over the top from Marlos sending Ismaily clear and, when Ederson raced out of his box without getting the ball, his finish into the gaping goal was straightforward.
City improved after the break - Sergio Aguero netting an injury-time penalty after Jesus was tripped - but Shakhtar closed out their third home win of the group stage, denying Guardiola's men the chance to qualify with a perfect record from Group F.
17 - Phil Foden is the youngest player to start a Champions League match for Manchester City (17y 191d). Promise. pic.twitter.com/gLujaXkIW3— OptaJoe (@OptaJoe) December 6, 2017
Taison fired a warning shot and Facundo Ferreyra had an effort blocked early on his 100th Shakhtar appearance as the hosts started on top against a much-changed City side.
A Shakhtar break saw Marlos bend an effort straight at Ederson and the hosts took the lead after 25 minutes thanks to a tremendous strike from Bernard.
Shakhtar moved the ball swiftly from their goalkeeper to the other end of the pitch, Marlos finding space to drive away from three City players to tee up Bernard, who sent Danilo the wrong way before whipping a glorious finish into Ederson's top-left corner.
Bernard almost added a second moments later, Taison this time creating the space with a clever run, but Ederson was equal to the Brazilian's effort.
But the second goal duly arrived after 32 minutes, Marlos sending a sensational ball in behind City's sloppy defensive line for Ismaily, who capitalised on indecision from Ederson to slot home into an open goal.
City finally created an opening of note when a superb Foden throughball sent Jesus in on goal, but the Brazilian hesitated and goalkeeper Andriy Pyatov made the tackle.
Leroy Sane forced a smart save from Pyatov seconds after the restart, Bernardo Silva's cross turned goalwards on the volley by the winger as City aimed to avoid a first loss of the season.
Guardiola replaced Sane with another youngster, Brahim Diaz - who had an effort turned onto the post - in a clear sign of where his priorities lay, although Aguero was sent on for former Shakhtar star Fernandinho for the last 20 minutes.
Taison fired wide and Yaroslav Rakitskiy fired a free-kick at Ederson as Shakhtar sought to make the result even more emphatic, but City grabbed a consolation from 12 yards in stoppage-time as Aguero smashed in a penalty.