В этой статье будет рассмотрен встроенный в Node.js метод os.cpus() и то, как наша команда использовала его для устранения ошибки.

Сначала немного предыстории о том, как я наткнулся на этот метод. Я работаю над проектом с небольшой командой, в которой мы используем контейнер Docker для выполнения ненадежного кода. При попытке запустить контейнер Docker в среде разработки, в зависимости от ядра процессора разработчика, некоторые из нас получат сообщение об ошибке, подобное этому:

docker: Error response from daemon: image with reference dockerUser/dockerImageName:dockerImageTag 
was found but does not match the specified platform: wanted linux/arm64/v8, actual: linux/amd64.
See 'docker run --help'.

Мы поняли, что получили эту ошибку, потому что половина нашей команды использовала компьютер с чипом Intel, а другая половина — с кремниевым чипом. В приведенной выше ошибке мы узнаем, что загружаем образ с linux/amd64 (Intel), но единственная доступная архитектура процессора — linux/arm64/v8 (Silicon).

Итак, как мы решили эту ошибку? С помощью встроенного в Node.js метода os.cpus().

Во-первых, что делает os.cpus()?

Os.cpus() — это встроенный метод Node.js, предоставляющий информацию о ядре процессора используемого вами компьютера. Он возвращает объект со следующей информацией:

  • model: строка, указывающая модель ядра процессора.
  • скорость: число, указывающее скорость ядра ЦП (в МГц).
  • times: объект, который содержит информацию о времени, которое ЦП провел в различных режимах (пользовательский, приятный, системный, бездействующий, прерывание) в миллисекундах.

Для наших целей нам понадобилась информация, содержащаяся в свойстве модели.

Во-вторых, как мы используем os.cpus()?

// import 'os' module
const os = require('os');
  
// log os.cpus() object
console.log(os.cpus());

После выполнения файла, содержащего приведенный выше код, ваш терминал должен напечатать объект, который выглядит так:

Example 1:

[
  {
    model: 'Apple M1 Pro',
    speed: 24,
    times: { user: 5923420, nice: 0, sys: 6578150, idle: 78493650, irq: 0 }
  },
  {
    model: 'Apple M1 Pro',
    speed: 24,
    times: { user: 5989680, nice: 0, sys: 6034680, idle: 78970000, irq: 0 }
  },
  {
    model: 'Apple M1 Pro',
    speed: 24,
    times: { user: 4231150, nice: 0, sys: 1405030, idle: 85358180, irq: 0 }
  },
  {
    model: 'Apple M1 Pro',
    speed: 24,
    times: { user: 2766490, nice: 0, sys: 960700, idle: 87267170, irq: 0 }
  },
  {
    model: 'Apple M1 Pro',
    speed: 24,
    times: { user: 1364820, nice: 0, sys: 534220, idle: 89095320, irq: 0 }
  }
]

Example 2:

[
  {
    model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times: { user: 252020, nice: 0, sys: 30340, idle: 1070356870, irq: 0 }
  },
  {
    model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times: { user: 306960, nice: 0, sys: 26980, idle: 1071569080, irq: 0 }
  },
  {
    model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times: {
      user: 248450, nice: 0, sys: 21750, idle: 1070919370, irq: 0 }
  },
  {
    model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times: { user: 256880, nice: 0, sys: 19430, idle: 1070905480, irq: 20 }
  }
]

Ваш вывод может содержать больше элементов в объекте или меньше, в зависимости от ядра вашего процессора. Половина нашей команды напечатала Intel… вместо Apple…, как в примерах выше. Название модели — это вся информация, необходимая для решения нашей проблемы, но если вы хотите узнать больше о свойствах скорости и времени, ознакомьтесь с этой ссылкой (также источником для Примера 2).

Теперь при выполнении команды docker run для запуска контейнера Docker мы можем добавить следующий флаг:

docker run --platform ${platform} <all other flags/information>

Но откуда взялась переменная платформы? Проверьте код ниже:

  let platform = os.cpus()[0].model.includes("Intel") ? "linux/arm64/v8" : "linux/amd64";

Используя тернарный оператор, скобочную нотацию, метод include и метод os.cpus(), мы создали условный оператор, который меняет назначение переменной платформы в зависимости от ядра процессора пользователя. Окончательный код выглядит так:

const os = require('os');

let platform = os.cpus()[0].model.includes("Intel") ? "linux/arm64/v8" : "linux/amd64";

exec(`docker run --platform ${platform} <all other flags/information>`)

Если в вашей команде члены вашей команды используют между собой более двух разных ядер ЦП, то вместо тернарного оператора вы можете использовать что-то вроде оператора switch и добавить все необходимые параметры.

Если, как и я, вы также никогда не слышали об этом конкретном методе Node.js, я надеюсь, что вы нашли эту статью информативной и будете использовать ее, если когда-либо столкнетесь с подобной ошибкой.

Спасибо за прочтение!