Dev containers: вскрытие
Tags: development, containers, docker, vscode
Published: 2022-08-21 08:25:27

---
title: Dev containers: вскрытие
published: true
date: 2022-07-04 08:31:03 UTC
tags: development, containers, docker, vscode
canonical_url: https://blog.rnds.pro/029-dev-containers-uncovering?utm_source=teletype&utm_medium=feed_rss&utm_campaign=rnds
---

![](https://img2.teletype.in/files/1f/20/1f20612a-6d37-4536-b0d2-cda4b477a1c7.jpeg)

_Оригинал: [https://blog.rnds.pro/029-dev-containers-uncovering](https://blog.rnds.pro/029-dev-containers-uncovering?utm_source=devto&utm_medium=feed_rss&utm_campaign=rnds)_

В этой статье хотелось бы рассказать про разработку внутри docker-контейнера: зачем это нужно, что предоставляет для этого замечательная IDE VSCode, и как это работает.

Итак, docker - это не только отличная технология для контейнеризации и деплоя приложений, но и возможность развернуть специфическую среду разработки в одно касание. 

Для тех, кто ещё (внезапно!) не знаком с Docker, очень рекомендую сразу пойти и ознакомиться с этой технологией ([https://en.wikipedia.org/wiki/Docker\_(software)](https://en.wikipedia.org/wiki/Docker_(software))).

## Для чего же это нужно?

Давайте рассмотрим существующие проблемы, которые, как правило, возникают при работе над проектом.

### Проблема 1

Вы - **новый разработчик в компании** , Вам дали свежеустановленный ПК, дали доступ в репозиторий проекта, с которым придется иметь дело. Предположим, что этот проект на Rails. Для запуска проекта необходимо:

- установить системные dev пакеты
- установить rvm
- установить правильную версию ruby, bundle
- установить nvm
- установить node
- установить и настроить БД, например, Postgres
- возможно, что-то ещё, что требует проект (Redis, Consul и т.д.)

В процессе установки могут возникнуть следующие проблемы:

- у вас Windows: это сразу +100 к сложности и боли
- у вас слишком новая версия операционки, а в проекте используются устаревшие библиотеки, и при установке ruby пакетов, либо scss (когда же его наконец перепишут на JS) могут возникнуть ошибки компиляции
- возможно, необходимы какие-то ещё дополнительные шаги, которые забыли описать в README, например, это может быть установка корневых сертификатов, прописывание каких-то переменных среды

В целом видно, что процесс первого запуска проекта непростой. Скорее всего, его придется повторить, когда захочется поработать с домашнего компьютера, а там как раз больше вероятность встретить Windows =)

### Проблема 2

Вы - опытный разработчик и уже давно работаете в компании, **у вас много проектов** , на разных языках, фреймворках и приходится часто переключаться. Согласитесь, не очень хочется проводить существенное время на развертывание окружения, вспоминая, как это делать.

### Проблема 3

Бывает так, что **проект уже очень старый** , порос мхом и обновлять его нерентабельно, но поддерживать надо. Там всё очень старое, и на своей современной операционке это просто не запустить.

### Проблема 4

Ещё одна проблема - это **замусоренность хостовой системы** , когда мы долго уже работаем, много всего разного поставили себе в систему, у нас появляются странные глюки, а остальные разработчики говорят нам, что [УМВР](https://wikireality.ru/wiki/%D0%A3%D0%9C%D0%92%D0%A0).

### Решение

Итак, эти все проблемы решает **контейнеризированное** окружение разработки. Мы получаем:

- повторяемость среды разработки
- быстрое развертывание
- независимость от хостовой системы, её типа и версии
- возможность полностью очистить свою систему от ненужных рудиментов старых проектов

Конечно же есть и недостатки:

- **Повышается сложность понимания что происходит**. Надо не забывать, что мы находимся внутри контейнера, и некоторые внешние ресурсы могут быть недоступны, такие как файлы, либо сторонние сервисы, запущенные на localhost.
- **Требуется больше системных ресурсов**. В основном, это дополнительное пространство на диске под контейнеры и образы docker, а в случае с MacOS ещё и дополнительный расход оперативной памяти под виртуальную машину с докером.
- Необходимо учитывать, что **ID пользователя внутри контейнера может быть другим** , и может возникнуть проблема с доступом к файлам проекта. Забегая вперед, скажу, что эта проблема вполне решаемая.

Существует множество вариантов реализовать такое контейнеризированное окружение:

- написать Dockerfile с нужной версией ОС, установкой всего необходимого, зайти в контейнер и разрабатывать в VIM.
- можно смонтировать исходники в запущенный контейнер и разрабатывать в своей IDE, а запускать в контейнере. Правда, у IDE скорее всего будут проблемы с дополнением кода.
- можно установить IDE внутрь контейнера и пробросить X-ы с хостовой машины (работает только с Linux).
- можно поставить sshd в контейнер и заходить внутрь по ssh из VSCode, IDEA или любой другой IDE, которая поддерживает удалённую разработку.
- либо воспользоваться специализированным решением, которое нам предлагает VSCode. Предлагаю в этой статье подробно рассмотреть это решение как самое, на мой взгляд, продвинутое из того, что я видел.

## VS Code Dev container

Полная документация находится здесь: [https://code.visualstudio.com/docs/remote/containers](https://code.visualstudio.com/docs/remote/containers)

Вкратце, это работает следующим образом:

- в проект добавляется конфигурация в папке .devcontainer
- при открытии такого проекта VS Code предлагает переоткрыть его внутри контейнера
- если мы соглашаемся, то после непродолжительных магических действий со стороны VS Code проект открывается
- мы оказываемся внутри контейнера и получаем ровно то окружение, которое нам необходимо для разработки

Вот шаги, которые нужно выполнить для добавления к себе в проект конфигурации:

- Docker уже должен быть установлен
- устанавливаем плагин в VS Code [https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- добавляем поддержку dev container в наш проект

Для этого открываем палитру команд в VS Code, выбираем пункт “Remote-Containers: Add Development Configuration Files”.

Нам предоставляется выбор:

1. воспользоваться готовыми конфигурациями (Node.js, Ruby on Rails, C++, Go, PHP и ещё много других)
2. добавить существующий в проекте Dockerfile или docker-compose.yml

Если сильно спешим, то можно воспользоваться готовыми конфигурациями. Но, на мой взгляд, лучше написать свою конфигурацию с нуля, так мы получаем больше контроля над происходящим, и это же так интересно!

**Разбираемся в магии**

Отдельно хотелось бы рассмотреть, что же делает VS Code с нашим контейнером, прежде чем его открыть.

Существует ряд нюансов, на которые следует обратить внимание:

- при монтировании исходников в контейнер ID пользователя и ID группы внутри и снаружи должны совпадать, иначе мы получим в своих исходниках новые файлы, созданные от имени другого пользователя (возможно от рута)
- у Git есть глобальные конфигурационные файлы, в которых задается ряд параметров, таких как имя пользователя, email, тип переноса строк и т.д., желательно, чтобы git внутри контейнера использовал такие же настройки
- также необходимо скопировать некоторые пользовательские настройки для того, чтобы вы чувствовали себя как дома (.ssh/authorized\_hosts, gpg конфигурацию)

Именно это делает VS Code при открытии devcontainer:

- он изучает версию операционной системы, которая используется в Dockerfile
- оборачивает ваш Dockerfile своим с дополнительными командами по пробросу UID и GID внутрь контейнера
- патчит /etc/passwd, синхронизируя UID и GID внутреннего пользователя с хостовым пользователем
- копирует в него все необходимые конфигурационные файлы из папки пользователя (git, ssh, gpg)
- устанавливает свой бэкенд на ноде (это порядка 450Мб) внутрь контейнера, который будет связываться с хостовым VSCode по рандомному TCP порту
- устанавливает внутрь контейнера все необходимые плагины для VS Code, прописанные в .devcontainer/devcontainer.json#extensions
- монтирует исходники проекта в папку /workspaces

Вуаля! Проект открыт, и можно кодить как обычно.

В итоге, чтобы открыть любой проект с поддержкой devcontainer и начать быстро кодить, Вам нужна лишь машина на Linux, Windows, MacOS с установленным Docker и VS Code.

## Уже не терпится? Попробуем в деле!

Я заготовил пример проекта, который можно открыть в VSCode и посмотреть, как это работает.

Для этого я форкнул первый найденный проект “Блог” на Rails ([https://github.com/navrocky/rails-blog-sample-devcontainers](https://github.com/navrocky/rails-blog-sample-devcontainers) ).

Как нельзя кстати оказалось, что он уже немного “подзасох”, последний коммит 5 лет назад. Соответственно, в те годы люди сидели на Ruby 2.3, Debian 8 (Jessie). Вот и попробуем всё это завести в нашем 2К22.

Dev container поддерживает два варианта окружения:

- один **Dockerfile**. Этот вариант проще и подходит, когда нам не нужны дополнительные сервисы для работы, такие как БД.
- **docker-compose.yml**. Этот вариант позволяет нам запустить как само окружение, так и все необходимые дополнительные сервисы. В примере я использовал именно этот вариант, так как нам нужна ещё сконфигурированная база данных.

Так выглядит типичная конфигурация в проекте:

![](https://img2.teletype.in/files/97/65/97653f9b-a2dc-4b57-8243-76b0e479d6b2.png)

Всё находится внутри папки **.devcontainer**. Файл **devcontainer.json** является основным и описывает конфигурацию для плагина VSCode.

**devcontainer.json**

```
{
	// Имя конфигурации
	"name": "Ruby",

	// указание файла docker-compose.yml и сервиса в нем, 
	// который будет использоваться для разработки
	"dockerComposeFile": "./docker-compose.yml",
	"service": "dev",

	// папка внутри контейнера, в которую монтируются исходники проекта
	"workspaceFolder": "/workspace",

	// действие при завершении работы с проектом
	"shutdownAction": "stopContainer",

	// дополнительные расширения VSCode, которые необходимо установить 
	// внутрь контейнера перед началом работы
	"extensions": [
		"castwide.solargraph", "eamodio.gitlens"
	],
	
	// пользователь внутри контейнера, под которым будет происходить вход 
	// в контейнер
	"remoteUser": "user"
}
```

**Dockerfile**

```
# берем нужную версию базового дистрибутива. Debian 8 из 2015 года, нам подходит.
FROM debian:8

# устанавливаем все необходимые утилиты
RUN apt-get update && apt-get install -y gnupg2 ca-certificates curl procps sudo git mc libpq-dev

# добавляем пользователя, под которым будем работать внутри контейнера, заодно даем ему возможность делать sudo
RUN useradd -m -d /home/user -s /bin/bash user && adduser user sudo && \
    echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# дальше все действия производим уже от пользователя
USER user 

# устанавливаем rvm
RUN gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB && \
    curl -ksSL https://get.rvm.io | bash -s stable

# устанавливаем nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

# подгружаем все инициализационные скрипты и переменные среды в bash, чтобы заработал установленный rvm
SHELL ["/bin/bash", "-c", "-l"]

# устанавливаем нужную версию Ruby и Bundler
RUN rvm install ruby-2.4 && gem install bundler:1.17.3

# создаем пользовательский каталог .bundle, чтобы были правильные права на эту папку при монтировании volume
RUN mkdir -p /home/user/.bundle

# устанавливаем node v5.3.0 и делаем её по умолчанию
RUN source $HOME/.nvm/nvm.sh && nvm install v5.3.0 && nvm alias default v5.3.0

# просто висим в бесконечном ожидании из /dev/null, это нужно чтобы контейнер не закрылся после запуска
ENTRYPOINT ["tail", "-f", "/dev/null"]
```

Открываем проект, VSCode предлагает нам открыть его внутри контейнера - соглашаемся:

![](https://img4.teletype.in/files/78/7f/787f534a-12bb-4b22-aaad-acb6bc445088.png)

Ждем некоторое время, пока все образы будут загружены, и будет произведена сборка контейнера:

![](https://img2.teletype.in/files/1e/e7/1ee7dbe3-6331-42f3-bb97-1b6ecc86b1c9.png)

Пока ждем, можно нажать show log и посмотреть, что там происходит.

Всё - проект открыт, и можно приступать к работе:

![](https://img1.teletype.in/files/8f/14/8f14721f-03e9-4b11-aedd-9ebfbf1dbee0.png)

Выполняем в терминале VSCode следующие команды:

```
rake db:setup
rails s -b 0.0.0.0
```

Открываем в браузере наш блог ([http://localhost:3000/blog](http://localhost:3000/blog)):

![](https://img3.teletype.in/files/6f/4d/6f4d9b67-e7c9-48c5-9ab9-b303f5061497.png)

Можно зайти в административную панель ([http://localhost:3000/admin](http://localhost:3000/admin)) и добавить контент. Пользователь и пароль для входа: **admin@example.com** / **123456**

**Вот так просто и быстро можно открыть незнакомый проект и приступить к работе!**

## А что же IDEA и её производные, спросите вы?

Там не всё так радужно, но есть свет в конце туннеля.

Не так давно JetBrains добавила поддержку remote development в свои платные IDE. Community варианты её, к сожалению, не получили. И называется этот продукт JetBrains Gateway ([https://www.jetbrains.com/remote-development/gateway/](https://www.jetbrains.com/remote-development/gateway/)). Данная штука позволяет зайти на удалённую машину, в нашем случае в контейнер с поднятым ssh демоном, и загрузить туда выбранную вами IDE и запустить её в режиме сервера, а на вашем хосте будет запущен IDE Client, который будет по ssh туннелю общаться с серверной IDE. В целом всё выглядит примерно так же, как и в VS Code, но менее автоматизированно. При открытии проекта необходимо руками поднимать контейнер или docker-compose конфигурацию, настраивать соединение внутри контейнера. Серверная IDE, которая грузится в контейнер, ничем не отличается от десктопной, и в размерах тоже (это примерно 1,5 ГБ), что весьма печально. Интерфейс IDE клиента ощущается тормознее по сравнению с десктопной IDE и местами подглючивает. Но в целом, всё довольно работоспособно.

Стоит заметить, что эта система пока находится в бете, поэтому держим кулачки и внимательно наблюдаем за дальнейшим развитием событий.