Установка Jenkins с Docker

Установка Jenkins через Docker. Нюансы запуска docker-compose внутри контейнера. Организация сети распределённой сборки. Подготовка виртуальных машин для менеджера и агентов через Ansible.

  • 00:00:38 - Выбор системы автоматизации
  • 00:02:10 - Способы установки Jenkins
  • 00:04:20 - Создание проекта
  • 00:06:06 - Добавление Makefile
  • 00:07:06 - Доступ к Docker
  • 00:09:03 - Использование Docker в Docker
  • 00:10:14 - Использование стороннего docker-демона
  • 00:12:39 - Пробрасывание сертификатов
  • 00:14:51 - Пробрасывание рабочей директории
  • 00:16:02 - Сохранение кэша образов
  • 00:17:50 - Локальная установка
  • 00:19:54 - Интерфейс Blue Ocean
  • 00:20:36 - Создание демонстрационного Pipeline
  • 00:22:25 - Локальный прокси Nginx
  • 00:24:34 - Установка docker-compose
  • 00:26:50 - Конфигурация для production
  • 00:28:34 - Создание виртуальной машины
  • 00:29:26 - Создание плейбуков для Ansible
  • 00:35:18 - Запуск плейбуков для установки
  • 00:36:24 - Добавление команды деплоя
  • 00:38:22 - Установка Jenkins в production
  • 00:39:28 - Выполнение Ad-Hoc команд в Ansible
  • 00:42:34 - Добавление тестового Pipeline
  • 00:42:59 - Настройка числа исполнителей
  • 00:47:10 - Проблема последовательного запуска
  • 00:49:02 - Использование сети агентов
  • 00:51:53 - Добавление агента
  • 00:54:55 - Генерация SSH-ключа для агента
  • 00:58:25 - Добавление Node в Jenkins
Скрытый контент (код, слайды, ...) для подписчиков. Открыть →
Дмитрий Елисеев
elisdn.ru
Комментарии (61)
Руслан

Спасибо, отличное видео.

Ответить
Arunas

Спасибо, очень!

Ответить
Роман

Дмитрий спасибо за качественный материал, и за тот труд ,который вы проделали, чтоб доступно все изложить,но не кажется вам что мы сильно начинаем углубляться в девопсовские вещи

Ответить
Bondarenko Alexandr

А как без этого?

Ответить
Роман

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

Ответить
Bondarenko Alexandr

Ну уж и не знаю... Я программист и я регулярно настраиваю CI/CD. Для меня материал довольно полезен.

Ответить
Дмитрий Елисеев

Данные знание мало пригодятся в работе, т.к. в любой студии или компании программисту не дадут этим заниматься, для этого есть devops

Наоборот, понятие DevOps как раз подразумевает, что всем этим начинают заниматься сами программисты. А не классический вариант, когда в компании есть отдельный программист (dev) и отдельный сисадмин (ops).

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

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

Ответить
Bondarenko Alexandr

Спасибо, Дмитрий! Одно время попробовал запустить Jenkins в docker с маунтом докер сокета, но во время сборки приложений при поднятии контейнеров не маунтились тома. Теперь понимаю, как этого избежать.

Ответить
Дмитрий Елисеев

В таком случае нужно было монтировать рабочую папку по тому же абсолютному пути:

volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    - /var/jenkins_home:/var/jenkins_home

чтобы внешний docker-демон видел тот же /var/jenkins_home.

Ответить
Bondarenko Alexandr

Спасибо

Ответить
Руслан

Отличный урок, спасибо!

Ответить
fedot

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

Ответить
Arunas

согласен

Ответить
Bondarenko Alexandr

Дмитрий, а почему задача из authorize.yml запускается отдельно от задачи создания пользователя?

Ответить
Дмитрий Елисеев

Потому что она необязательна для запуска.

Ответить
Дмитрий

Спасибо за замечательные уроки! Вопрос, почему мне приходится в плейбуках уточнять путь к ролям? Вместо просто: - swap, надо писать: .../roles/swap У меня ubuntu 16.

Ответить
Дмитрий

Сам себе отвечаю, потому что плейбуки зачем-то поместил в отдельную папку, а надо в корень рядом с папкой /roles

Ответить
Дмитрий

Дмитрий, в моём jenkins такая беда, ветка master начала падать с ошибкой на npm-install причем падает только ветка master, другие ветки не падают

12:38:39  Successfully built 0686eba36227
12:38:39  Successfully tagged rfr_master_myapp:latest
12:38:39  docker-compose run --rm node-cli npm install
12:38:41  Creating network "rfr_master_default" with the default driver
12:38:41  Creating volume "rfr_master_mongodata" with default driver
12:40:55  
12:40:55  <--- Last few GCs --->
12:40:55  
12:40:55  [1:0x5562eb1b93e0]   122294 ms: Mark-sweep 247.8 (258.7) -> 246.8 (258.2) MB, 844.9 / 2.1 ms  (average mu = 0.811, current mu = 0.223) allocation failure scavenge might not succeed
12:40:55  [1:0x5562eb1b93e0]   123154 ms: Mark-sweep 247.9 (258.2) -> 247.5 (259.2) MB, 676.2 / 9.7 ms  (average mu = 0.711, current mu = 0.214) allocation failure scavenge might not succeed
12:40:55  
12:40:55  
12:40:55  <--- JS stacktrace --->
12:40:55  
12:40:55  FATAL ERROR: MarkCompactCollector: young object promotion failed Allocation failed - JavaScript heap out of memory
12:40:55  Makefile:34: recipe for target 'npm-install' failed
12:40:55  make: *** [npm-install] Error 139```
Ответить
Дмитрий

Что то не охото переустанавливать и настраивать заново jenkins. Может можно как то это поправить?

Ответить
Дмитрий Елисеев

А образ свежий FROM node:15-alpine?

А если зайти в var/lib/docker/volumes и в jenkins-data удалить папку node_modules в workspaces/xxx_master и потом перезапустить сборку?

Ответить
Дмитрий

Образ node:alpine - норм? вроде должен быть крайний.

Папку нашел, pipeline отработал успешно!

Спасибо Дима!

Ответить
Руслан

Спасибо!

Ответить
Дмитрий

Для информации: внезапно отвалилась дополнительная нода, вообще не хотела подключаться, полечил обновлением образа jenkins (docker-compose pull && docker-compose build --pull).

Ответить
Дмитрий

Дима, чем отличается?

docker-compose pull --include-deps && docker-compose build

от

docker-compose pull && docker-compose build --pull

Ответить
Дмитрий Елисеев

Правильнее использовать вариант:

docker-compose pull && docker-compose build --pull

Исправил это в репозитории и скоро исправлю в видео.

Ответить
elmut

добрый вечер а подскажите какие вы плагины поставили. я собрал Jenkins из alpine

Ответить
Дмитрий Елисеев

При установке выбрали рекомендуемые по умолчанию. Потом свми доставили SSH Agent.

Ответить
`

Выскакивает такая ошибка, при после команды make init

ERROR: Could not build wheels for cryptography which use PEP 517 and cannot be installed directly ERROR: Service 'jenkins' failed to build: The command '/bin/sh -c apk add --no-cache py-pip python3-dev libffi-dev openssl-dev gcc libc-dev make gettext && pip3 install docker-compose' returned a non-zero code: 1 make: *** [Makefile:15: docker-build] Error 1

Ответить
Дмитрий

да, кстати аналогично, потребовалось обновить докер и вот такая же ошибка появляется

Ответить
Дмитрий

https://docs.docker.com/compose/install/

For alpine, the following dependency packages are needed: py-pip, python3-dev, libffi-dev, openssl-dev, gcc, libc-dev, rust, cargo and make.

Таким образом в список установки надо добавить rust и cargo

Ответить
`

Спасибо, помогло :)

Ответить
`

Здравствуйте, у меня не получается одновременно задеплоить registry и jenkins. Пишет что порт 443 уже занят другим сервисом. Если я деплою сначало registry, после этого jenkins не получается задеплоить из за того, что nginx registry занимает порт, что посоветуете?

Ответить
Дмитрий Елисеев

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

Можете совместить исходники из двух проектов ​в один, сделав общий файл docker-compose.yml.

Либо можете отдельныйм docker-compose.yml поставить общий входной прокси-сервер Traefik и эти два проекта деплоить в его сеть.

Ответить
Дмитрий

Я так делал, как Дима пишет с общим docker-compose.yml, работает. Сначала, поднял реестр, потом остальное. Но в итоге отказался от этой затеи, потому что виртуалка не тянет и постоянные проблемы. И в конце концов у меня отдельные виртуалки на реестр, дженкинс (1GB памяти - вот это прям обязательно) и сайт.

Ответить
drakulitka

Не могу задеплоить:

Building wheel for cryptography (PEP 517): finished with status 'done'
Created wheel for cryptography: filename=cryptography-3.4.7-cp39-cp39-linux_x86_64.whl size=1360625 sha256=8e0a86c52e60f66df3b5962daa6d74002785d6f798d199e9306b473fc9283c12
Stored in directory: /root/.cache/pip/wheels/f2/58/46/ac4602d8ff0e011d239d12aac740d0af2c766debc7aee8b274
Building wheel for pynacl (PEP 517): started
Building wheel for pynacl (PEP 517): finished with status 'error'
ERROR: Command errored out with exit status 1:
    command: /usr/bin/python3 /usr/lib/python3.9/site-packages/pip/_vendor/pep517/_in_process.py build_wheel /tmp/tmpu4vkqblw
    cwd: /tmp/pip-install-1ytovj84/pynacl_c2f3e6c147a043bb93319775b39d1d7c
...
ERROR: Failed building wheel for pynacl
Building wheel for pyrsistent (PEP 517): started
Building wheel for pyrsistent (PEP 517): finished with status 'done'
Created wheel for pyrsistent: filename=pyrsistent-0.18.0-cp39-cp39-linux_x86_64.whl size=115743 sha256=e386566112a4e50aa8394e7ddf2c1b4bb02c199b951eac555a4ae84a86edcb73
Stored in directory: /root/.cache/pip/wheels/dd/c8/61/04c6d218b3691f75353d7f74fed3fbd40e0ee9e2d1e2ce24c6
Building wheel for PyYAML (PEP 517): started
Building wheel for PyYAML (PEP 517): finished with status 'done'
Created wheel for PyYAML: filename=PyYAML-5.4.1-cp39-cp39-linux_x86_64.whl size=45654 sha256=6fa1aca36f08f93f42d482f65d7a25ab0a81c50ba5fb3ea27b025f2fdc3a1638
Stored in directory: /root/.cache/pip/wheels/b7/a5/c4/504d913c2a55bb09c607541578ec5f844d1ff33467abe93ba5
Successfully built bcrypt cryptography pyrsistent PyYAML
Failed to build pynacl
ERROR: Could not build wheels for pynacl which use PEP 517 and cannot be installed directly
Service 'jenkins' failed to build : The command '/bin/sh -c apk add --no-cache py-pip python3-dev libffi-dev openssl-dev gcc libc-dev rust cargo make gettext     && pip3 install docker-compose' returned a non-zero code: 1
make: *** [Makefile:27: deploy] Ошибка 1
Ответить
Ярослав

Та же фигня. С той же ошибкой не могу собрать образы на локалке. Даже с rust и cargo.

Ответить
Ярослав

Кто-то решил эту проблему с установкой docker-compose в jenkins dockerfile? А то я весь инет уже облазил, даже тему создал здесь https://stackoverflow.com/questions/69098128/cant-install-docker-compose-into-jenkins-dockerfile Но пока безуспешно. Я надеюсь Дмитрий прочитает и ответит мне. Хоть что-нибудь. Может у всех образ собирается и это у меня какая-то специфическая проблема. Может докер обновить или еще что-то.

Ответить
Дмитрий Елисеев

Выполните с обновлением:

docker-compose pull
docker-compose build --pull
docker-compose up -d
Ответить
Ярослав

Пробовал сделать такое. "docker system prune", "docker-compose pull". Но после выполнения команды "docker-compose build --pull " выбивает ту же ошибку. Причем при сборке спулился новый образ blueocean. А остальным такое помогло?

Ответить
Руслан

RUN apk add --no-cache py-pip musl-dev python3-dev libffi-dev openssl-dev gcc libc-dev rust cargo \ && pip3 install --upgrade pip \ && pip3 install docker-compose

Ответить
slo_nik

Добрый день, Дмитрий.

Следовал указаниям из видео - всё получилось. Jenkins запустился, открылся и даже получилось создать проверочный pipeline.

Но при более подробной "проверке" обнаружились проблемы.

1) Память практически вся загружена.

2) Постоянно используется swap.

                    total        used        free      shared  buff/cache   available
Mem:           481M        425M        7.8M        200K         48M         42M
Swap:          961M         92M        869M

Попробовал через reboot перегрузить виртуалку. С памятью и swap стало лучше

                    total        used        free      shared  buff/cache   available
Mem:           481M        129M         37M        5.6M        314M        333M
Swap:          961M          0B        961M

Но на сайте 502, кроме контейнера nginx все "упали". Запуск контейнеров не помогает.

В логах ngix какая ошибка

2021/08/24 12:53:25 [warn] 21#21: no resolver defined to resolve r3.o.lencr.org while requesting certificate status, responder: r3.o.lencr.org, certificate: "/etc/letsencrypt/live/jenkins.domain.info/fullchain.pem"

В остальных логах ничего подозрительного не нашёл.

Помогает только полная переустановка виртуальной машины и новая установка jenkins.

Виртуалки на vscale.io. Память 512, диск 20Gb., ubuntu 18.04.

Как я понял из видео, swap будет использоваться по требованию. Но даже после установки, не запуская никаких pipeline-ов, виртуалка получается загружена.

Как победить данную проблему?

p.s. Построчно сверил все файлы, перепроверю ещё раз, но думаю, что проблема в чём-то другом.

p.s.s После некоторого времени память опять загружена и swap используется

                    total        used        free      shared  buff/cache   available
Mem:           481M        372M        7.4M        1.2M        101M         94M
Swap:          961M         36M        925M
Ответить
slo_nik

C 502 ответом сервера вроде бы разобрался.

В docker-compose-production.yml для сервисов jenkins и docker добавил restart: always

Теперь после reboot они запускаются.

Дмитрий, но в Вашем docker-compose-production.yml для этих сервисов не установлен restart. Так нужно его указывать или нет?

И что делать с постоянным ростом использования swap и самой памяти виртуалки?

Ответить
Дмитрий Елисеев

Да, можно поставить restart: always, чтобы после reboot они запускались снова.

Ответить
Дмитрий Елисеев

Память 512, диск 20Gb., ubuntu 18.04.

Просто для Jenkins нужна более мощная виртуалка. В идеале двухъядерная с 2GB.

Ответить
slo_nik

Просто для Jenkins нужна более мощная виртуалка. В идеале двухъядерная с 2GB.

Всё сделал как Вы и показали, чтобы меньше разногласий было)))

В общем проблему победил при помощи restart:always. Из-за этого возникала 502 ошибка.

Пока всё работает и на минимальных, проект пока не сильно большой.

Ответить
Сергей

Добрый день.
На локальной машине поднять jenkins удалось без проблем
А вот на серверве - никак. При билде ругается на weels для pynalc

ERROR: Could not build wheels for pynacl which use PEP 517 and cannot be installed directly

для сервера использую Debian 10 64bit 512 МБ RAM 20 ГБ SSD 1 CPU
Никак не могу понять в чем соль?

Ответить
Богдан

https://wiki.alpinelinux.org/wiki/Docker

'docker-compose' is in the 'Community' repository starting with Alpine Linux 3.10.

apk add docker-compose

В самом видосе показана установка для старой версии, могут лететь ошибки, хоть и не должны. =_=

Ответить
Ярослав

Лично у меня заработало. Спасибо тебе, добрый человек.

Ответить
bogdan

супер, спасибо

Ответить
slo_nik

Добрый вечер.

Попробуйте установить из этого образа.

FROM jenkinsci/blueocean:1.24.7

512 МБ RAM

Крайне мало, сам убедился на собственном опыте. Берите не меньше 1Gb RAM

Ответить
Сергей

Спасибо я понял две вещи. Нужна версия ядра ubuntu не меньше 20 и оперативная память лучше 2 Гб. Версия влияет на работу пакетов, а память нужна для работы jenkins. Java прожорливая ))))))

Ответить
slo_nik

Да, влияет на работу пакетов при установке.

Посмотрите здесь же комментарии выше. Эта проблема воникала и у меня, пока не сменил версию образа.

Ответить
Yaroslav

Всем привет! Подскажите, пожалуйста, кто решил - как обновиться до 2й версии docker compose?

Ответить
Владимир Перепеченко

Благодарю Дмитрий! Прекрасный урок!

Установил Jenkins согласно этого урока. docker-compose-production.yml взял из вашего jenkins репозитория. Сделал на его основе. Соответственно, версия jenkins: FROM jenkins/jenkins:alpine (v2.380) В docker-compose отдельно стоит сервис jenkins + Отдельный сервис nginx для него.

Вроде интерфейс работает, но при установке любого jenkins плагина (плагин из Вашего урока или плагин для Gitlab), когда нажимаешь на приглашение "restart" после завершения установки плагина, интерфейс в броузере навечно зависает в стадии перезагрузки: https://i.imgur.com/FEpvM7J.png на адресе https://jenkins.gigakassa.ru/manage/pluginManager/updates/

В логах - ничего критичного: https://i.imgur.com/qSJQCsL.png , Разве что пара странных варнингов при старте контейнера: https://i.imgur.com/vBDGcxH.png

После обновления зависшей страницы получаем 502: https://i.imgur.com/RiNUM6C.png

Если рестартовать docker-compose jenkinsa (down+up), то все ОК и вновь установленные плагины - на месте.

RAM на виртуалке: 16 GB

Дмитрий, подскажите пожалуйста, это баг или фича (error 502)? и куда копать?

Ответить
Владимир Перепеченко

Выяснил что плагины тут ни при чём. Зависание рестарта + 502 возникают и при простом ручном рестарте Jenkins: (jenkins_url)/safeRestart

Ответить
Владимир Перепеченко

Если я после попытки рестарта Jenkins через web интерфейс и получения 502 bad gateway делаю затем рестарт nginx прокси

docker restart jenkins-nginx-1

, то получается успешно подключиться к Jenkins, просто перегрузив его страницу.

Поэтому, промежуточный вывод: Jenkins рестартует нормально, просто прокси его перестаёт видеть после растарта (рестара Jenkins-a).

Что-то не так с прокси..., хотя он типовой, из урока (что в prod что в dev):

server {
    listen 8000;
    server_tokens off;

    location / {
        proxy_pass http://jenkins:8080;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 900;
    }
}
Ответить
Владимир Перепеченко

Да, без прокси (localhost:8080) Jenkins рестартует из интерфейса отлично.

Дмитрий, а нет ли у Вас мыслей как подкрутить прокси?

Ответить
Владимир Перепеченко

Так, кажется разобрался.

Результат: прокси теперь пере-подключается к контейнеру Jenkins, даже после перезагрузки Jenkins. Следовательно, можно в интерфейсе Jenkins выполнять операции, требующие перезагрузки Jenkins.

Добавил две новые строчки:

# 1
#     resolver 127.0.0.11 ipv6=off valid=10s; # for dns - needed for letsencript certs update
# также решает проблему с warning в логе прокси о том, что certbot не может зарезолвить публичный хост Letsencrypt по имени, чтобы обновить сертификаты

# 2
#  set $jenkins "http://jenkins:8080";
#  proxy_pass $jenkins; 
# Устанавливает proxy_pass не строкой, а переменной. Это, типа, поволяет proxy переподключиться к upsream даже если ip address у  upstream изменился.

Итого, рабочая конфигурация Дмитрия с моими двумя новыми строчками:

server {
    listen 80;
    server_name jenkins.domain.ru;
    server_tokens off;

    location ~ /\.well\-known {
        root /var/www/html;
    }

    rewrite ^(.*) https://jenkins.domain.ru$1 permanent;
}

server {
    listen 443 ssl http2;
    server_name jenkins.domain.ru;
    server_tokens off;

    resolver 127.0.0.11 ipv6=off valid=10s; # for dns - needed for letsencript certs update

    ssl_certificate /etc/letsencrypt/live/jenkins.domain.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jenkins.domain.ru/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/jenkins.domain.ru/chain.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    add_header Strict-Transport-Security "max-age=31536000";
    add_header Content-Security-Policy "block-all-mixed-content";    

    location ~ /\.well\-known {
        root /var/www/html;
    }

    location / {
        # from deworker:
        # proxy_pass http://jenkins:8080;
        set $jenkins "http://jenkins:8080";
        proxy_pass $jenkins;        
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 900;        
        
    }

}

Как думаете, Дмитрий, это корректное решение проблемы?

PS: У Jenkins есть официальный пример конфига прокси.

Там очень много всего, включая поддержку веб сокетов для Jenkins.

Но и без этого пока у меня интерфейс работает. Может не всё еще щупал в функционале.

Я применял эту конфигурацию, но она не решала вышеописанной проблемы с перезагрузкой.

Ответить
Dzianis

Что-то не удается мне подружить Jenkins + docker:dind в Swarm... Кто-то пробовал запустить?

Ignoring unsupported options: build, privileged, restart
$ docker service logs -f --raw jenkins_docker
---------------------
Certificate request self-signature ok
subject=CN = docker:dind server
/certs/server/cert.pem: OK
Certificate request self-signature ok
subject=CN = docker:dind client
/certs/client/cert.pem: OK
ip: can't find device 'ip_tables'
ip_tables              36864  2 iptable_nat,iptable_filter
x_tables               53248 12 iptable_filter,xt_ipvs,xt_nat,xt_policy,xt_mark,xt_u32,xt_tcpudp,xt_conntrack,xt_MASQUERADE,xt_addrtype,nft_compat,ip_tables
modprobe: can't change directory to '/lib/modules': No such file or directory
mount: permission denied (are you root?)
Could not mount /sys/kernel/security.
AppArmor detection and --privileged mode might break.
mount: permission denied (are you root?)
Ответить
Сергей

Дмитрий, в видео описывается процесс пробрасывая порта 8000 для Jenkins, у меня не заработало. Зашел на офф сайт там указанно пробрасывать порт 8080 https://www.jenkins.io/doc/book/installing/docker/

Ответить
Дмитрий Елисеев

В видео прокидывается 8080 порт из Jenkins на 8000 порт компьютера:

ports:
    - "8000:8080"

Так что противоречий нет.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Google
GitHub
Yandex
MailRu