Введение.

В основном, Docker привыкли использовать для запуска сервисов или процессов не предполагающих визуальной составляющей. Однако могут быть ситуации, когда в контейнере возникает необходимость открыть среду разработки и на месте продебажить один сервис или два сервиса, каждый в своем контейнере. В прошлой статье я писал, как запускать графические приложения в wsl, в текущей расскажу, как можно запустить разными способами в контейнере qtcreator. Аналогично можно будет поступить с любой средой разработки или программой.

Подготовка.

Включаем wsl на windows, если он отключен, говорим ему работать в режиме wsl2 – ровно так как описано в документации Майкрософт.  Устанавливаем Docker.
Создаем для примера контейнер simpleimg_0 на основе ubuntu:18.04 и тут же запускаем его в интерактивном режиме:

docker run --name simpleimg_0 -it ubuntu:18.04 /bin/bash

Поставим mc по желанию для своего удобства:

apt update && apt install mc

Согласимся доставить все зависимости и получим:

Установка qtcreator.

Теперь устанавливаем qtcreator:

apt install qtcreator

Соглашаемся добавить 1,6гб зависимостей:

При попытке запуска видим, что ничего не получится. Теперь решим этот вопрос.

Запуск qtcreator.

Для того чтобы запустить графическое приложение из консоли линукс нужно на хостовой системе запустить х-сервер, в нашем случае это Vcxsrv.
Указываем при его запуске номер дисплея и запоминаем его:

На следующей оставляем все по умолчанию:

На последней снимаем средний флажок и устанавливаем последний:

Теперь видим значек х-сервера в трее около часов.

Теперь нам нужно узнать заветный адрес х-сервера, который нужно нам указать в контейнере. Для этого открываем консоль на хостовой ОС и запрашиваем информацию по сети:

ipconfig

Находим в выводе информацию по сети wsl:

Берем этот адрес и вносим его в переменную окружения DISPLAY в ОС контейнера, вспомнив про номер дисплея:

export DISPLAY=172.30.144.1:0

Повторно запускаем qtcreator и ура, он запускается в окне на заднем плане за консолью:

Заходим в настройки, проверяем, что компиляторы уже стоят:

Есть дебаггер, но нет Cmake и нет комплекта Qt.

Закрываем окно программы, переходим в консоль и доставляем Cmake:

apt install cmake

Проверяем и видим, что при следующем запуске Cmake уже подтянулся в настройках:

Любые зависимости таким образом можно самостоятельно доставить.

Посмотрим сколько весит наш контейнер для отладки – не очень то и много, т.к в несколько раз меньше чем виртуальная машина на основе убунты:

После того как мы завершили работу с контейнером, то запустить его снова в интерактивном режиме можно командой:

docker container start simpleimg_0 –i

Dockerfile, автоматизация.

Все то, что мы выше проделали руками, можно прописать в Dockerfile и создать все одной командой из консоли.

Qt в dockerfile можно установить несколькими способами – через apt install, через python утилиту aqt, через офиц. установщики qt.

Первый способ в ручном режиме разобрали в первой части статьи, предлагаю ниже разобрать установку через python. Создаем Dockerfile со следующим содержимым и переходим в консоли к нему на один уровень:

FROM ubuntu:18.04

RUN apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get -y install \
    git \
    cmake \
    python3 \
    python3-pip \
    build-essential \
    libdbus-1-3 \
    libpulse-mainloop-glib0

RUN pip3 install aqtinstall

ARG QT=5.11.0
ARG QT_TOOLS=tools_qtcreator
ARG QT_HOST=linux
ARG QT_TARGET=desktop
ARG QT_ARCH=gcc_64
RUN aqt install-qt --outputdir /opt/qt ${QT_HOST} ${QT_TARGET} ${QT} ${QT_ARCH}
RUN aqt install-tool -O /opt/qtcreator ${QT_HOST} ${QT_TARGET} ${QT_TOOLS}

ENV PATH /opt/qt/${QT}/gcc_64/bin:/opt/qtcreator/Tools/QtCreator/bin:$PATH
ENV QT_PLUGIN_PATH /opt/qt/${QT}/gcc_64/plugins/
ENV QML_IMPORT_PATH /opt/qt/${QT}/gcc_64/qml/
ENV QML2_IMPORT_PATH /opt/qt/${QT}/gcc_64/qml/

Здесь мы сначала ставить необходимые зависимости для сборки, после этого ставим утилиту aqtinstall и выполняем ей две команды:
aqt install-qt – устанавливает библиотеки qt
aqt install-tool – устанавливает qtcreator.

Теперь создаем образ из этого файла:

docker build -f Dockerfile -t simpleimg_qtcr .

Запускаем, и вспоминаем, что нужно прописать актуальный адрес DISPLAY.

Минус этого способа – долгая установка, т.к все стягивается с офис.репозитория Qt. Документация для утилиты aqt.

Разберем еще один способ, лишенного этого недостатка – установка с использованием оффлайн-установщика с офиц. сайта. Качаем с офиц. репозитория установщик. Качаем скрипт не интерактивного установщика qt-installer-noninteractive.qs. Открываем последний скрипт в редакторе и кое-что правим.
Вписываем данные авторизации:

Controller.prototype.CredentialsPageCallback = function() {
  var page = gui.pageWidgetByObjectName("CredentialsPage");
  page.loginWidget.EmailLineEdit.setText("email");
  page.loginWidget.PasswordLineEdit.setText("pass");
  gui.clickButton(buttons.NextButton);
}

Вписываем куда установить фреймворк:

Controller.prototype.TargetDirectoryPageCallback = function()
{
    //dev is the user in our docker image
    gui.currentPageWidget().TargetDirectoryLineEdit.setText(installer.value("/opt/Qt5.11.0"));
    gui.clickButton(buttons.NextButton);
}

В разделе Controller.prototype.ComponentSelectionPageCallback = function() указываем какие компоненты установить, а какие нет:

widget.deselectAll();
    widget.selectComponent("qt.tools.qtcreator");
    widget.selectComponent("qt.qt5.5110");
    widget.selectComponent("qt.qt5.5110.gcc_64");
    
    widget.deselectComponent("qt.qt5.5110.android_x86");
    widget.deselectComponent("qt.qt5.5110.android_armv7");
    widget.deselectComponent("qt.qt5.5110.android_armv7");
    widget.deselectComponent("qt.qt5.5110.doc");
    widget.deselectComponent("qt.qt5.5110.examples");
    widget.deselectComponent("qt.qt5.5110.src");

На одном уровне с установщиком создаем dockerfile:

# run as:
# docker build -t offline_qtcr -f Dockerfile path_to_distrib
FROM ubuntu:18.04
MAINTAINER Chesnochenko <ss@ss.org>

ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && \
    apt -o Dpkg::Options::="--force-confold" install -q -y mc build-essential \
    mesa-common-dev libglu1-mesa-dev libdbus-1-dev x11-xkb-utils sudo curl \
    libfontconfig1 libxrender1 libxi6 libgconf-2-4 libxcb-xinerama0 gdb cmake && \
    apt clean && rm -rf /var/lib/apt/lists/*

RUN useradd -G users,video -ms /bin/bash user && \
	echo 'user:12345678' | chpasswd -e && \
	echo 'user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
	mkdir /run/user && \
    chown user:user /run/user && \	
	apt update && apt install -y locales language-pack-ru libfontconfig1 && \
    update-locale LANG=ru_RU.UTF-8 && \
	echo 'LANG=ru_RU.UTF-8' >> /etc/default/locale && \
	echo 'export LC_ALL=ru_RU.UTF-8' >> /home/user/.bashrc && \
	echo 'export LANG=ru_RU.UTF-8' >> /home/user/.bashrc && \
	locale-gen ru_RU.UTF-8 && \
	apt install -y --reinstall locales && \	
	apt clean && rm -rf /var/lib/apt/lists/* 

WORKDIR /tmp/
COPY qt-opensource-linux-x64-5.11.0.run .
COPY qt-installer-noninteractive.qs .
RUN chmod a+x qt-opensource-linux-x64-5.11.0.run \
    && chmod a+x qt-installer-noninteractive.qs 

RUN curl https://www.openssl.org/source/openssl-1.0.2l.tar.gz | tar xz && cd openssl-1.0.2l && \
    ./config && make -j $(nproc) && make install && \
    ln -sf /usr/local/ssl/bin/openssl `which openssl`

RUN ./qt-opensource-linux-x64-5.11.0.run -platform minimal --verbose --script ./qt-installer-noninteractive.qs
RUN rm -f qt-opensource-linux-x64-5.11.0.run qt-installer-noninteractive.qs

ENV DISPLAY=172.30.144.1:0
RUN echo 'export DISPLAY=172.30.144.1:0' >> /home/user/.bashrc
#RUN setxkbmap -model pc105 -layout us,ru -option grp:alt_shift_toggle


ARG QT=5.11.0
ARG QT_HOST=linux
ARG QT_TARGET=desktop
ARG QT_ARCH=gcc_64
ENV PATH /opt/Qt${QT}/${QT}/gcc_64/bin:/opt/Qt${QT}/Tools/QtCreator/bin/:$PATH
ENV QT_PLUGIN_PATH /opt/Qt${QT}/${QT}/gcc_64/plugins/
ENV QML_IMPORT_PATH /opt/Qt${QT}/${QT}/gcc_64/qml/
ENV QML2_IMPORT_PATH /opt/Qt${QT}/${QT}/gcc_64/qml/

ENV SHELL=/bin/bash
CMD bash
	
USER user
WORKDIR /home/user
CMD bash

В эту сборку добавили отдельного пользователя user, поддержку ввода на русском языке и возможность переключаться по alt+ctrl.

Два файла: qt-opensource-linux-x64-5.11.1.run и qt-installer-noninteractive.qs скопировали в контейнер, в папку /tmp. После установки подчистили за собой место. Сразу сохранили в контейнер информацию об адресе х-сервера. В случае, когда после перезагрузки компьютера адрес изменится, мы его установим через параметры запуска контейнера.

Создаем образ, в конце добавляем путь к каталогу где лежат дистрибутив qt и скрипт .qs:

docker build -t offline_qtcr -f Dockerfile d:\Distrib\Qt\single\qreator_linux\

Запускаем:

docker run --name offqtcont -it offline_qtcr /opt/Qt5.11.0/Tools/QtCreator/bin/qtcreator.sh

Запустился qtcreator:

Можем при запуске не указывать команду запуска самого qtcreator, тогда откроется консоль и в ней уже можем ввести путь до исполняемого файла.

Заключение.

Вы познакомились с тремя способами установить и запустить qtcreator в докере. Если вам не нужен сам qtcreator, то по такому же принципу можно в докере развернуть любое графическое приложение.

Комментарии (8)


  1. Sazonov
    29.03.2022 12:17

    А вы можете пояснить, зачем выключать поддержку OpenGL? Я бы давно ушёл с VMware pro в пользу связки wsl2+docker для текущего проекта. Но у нас всё как раз уперлось в невозможность запуска приложения с поддержкой OpenGL.


    1. lehiss Автор
      30.03.2022 11:26

      Выключали поддержку OpenGL из-за того, что иначе были проблемы с запуском.


  1. amarao
    29.03.2022 12:31

    А звук? После перехода большинства дистрибутивов с pulseaudio на pipewire - это крайне интересный вопрос.


    1. lehiss Автор
      30.03.2022 10:42

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


  1. zorn_v
    29.03.2022 12:50
    +2

    После установки подчистили за собой место

    На самом деле нет, оно все осталось в предыдущем слое. Каждую команду в Dockerfile можно рассматривать как коммит в гит.

    Думаете просто так делают длинючие `RUN`ы с очисткой в конце ?


    1. lehiss Автор
      30.03.2022 10:46

      да, вы правы: в слое докера место не освободилось, правильнее объединить команды установки и удаления в одну. В статье писал о том, что подчистили место внутри самого образа, удалив инсталлятор.


      1. zorn_v
        30.03.2022 13:08

        В статье писал о том, что подчистили место внутри самого образа, удалив инсталлятор.

        Опять мимо ))

        Образ состоит из слоев. Место которое он занимает зависит от места которое занимают его слои.

        Вы не почистили а просто "скрыли".
        Попробуйте git init . && dd if=/dev/random of=my.tmp bs=10M count=10 && git add . && git commit -am 'big file' && rm -f my.tmp && git commit -am 'remove big file' и посмотрите сколько весит папка .git

        Хотя по факту то файла нет ) Вот тоже самое с докер образами.

        Не понимаю что вы подразумеваете под "место внутри самого образа". Там нет какого то "своего" места, он жрет тот же диск (и сетевой трафик при скачивании) что все остальные.


  1. tarekd
    29.03.2022 17:01

    пользуюсь https://github.com/osrf/rocker, жалоб пока нет. OpenGL работает, и с Nvidia тоже.