Tomcat MySQL Docker: версии, Config.properties, подключение
Диагностика проблем с Tomcat и MySQL в Docker: определение версий, проверка Config.properties в WAR, исправление поиска в ROOT, логи и команды для JDBC-подключения к БД в docker-compose.
Как определить необходимые версии Tomcat и MySQL и одновременно диагностировать проблему с Config.properties и подключением к базе в Docker?
Описание проблемы:
- Пытаюсь поднять тестовое Java-приложение в Tomcat внутри Docker. Собираю docker-образ, подкладываю Config.properties в WebContent. Приложение стартует, страница отображается, но при попытке добавить данные приложение не может подключиться к контейнеру с БД. Контейнер и БД доступны по сети. Конфиг почему-то ищется не в каталоге приложения, а в ROOT. В логах контейнера отображена только попытка записать данные — как определить, что не нравится приложению?
Dockerfile:
FROM maven:3.6.3-openjdk-11 AS build
RUN git clone https://github.com/shephertz/App42PaaS-Java-MySQL-Sample.git /build
WORKDIR /build
COPY ./Config.properties ./WebContent/
RUN mvn package
FROM tomcat:8-jre8-alpine
WORKDIR /usr/local/tomcat/webapps
COPY --from=build /build/target/App42PaaS-Java-MySQL-Sample-0.0.1-SNAPSHOT.war ./App43PaaS.war
docker-compose.yml:
services:
tomcat:
build:
context: .
depends_on:
- db
restart: unless-stopped
ports:
- '8080:8080'
- '8443:8443'
networks:
- app42paas
db:
image: mysql:lts
restart: unless-stopped
env_file: .env
networks:
- app42paas
volumes:
- ./db:/var/lib/mysql
networks:
app42paas:
Конкретные вопросы, на которые прошу ответ:
- Как определить, какие версии Tomcat и MySQL необходимы для этого проекта (где смотреть: pom.xml, зависимости, требования проекта)?
- Как проверить, что Config.properties действительно попал в WAR и доступен приложению (как проверить содержимое WAR, classpath, WEB-INF)?
- Почему приложение может искать конфиг в ROOT (контекст приложения / имя WAR / относительные пути) и как это исправить — правильное имя WAR (ROOT.war) vs контекст, размещение файла конфигурации в Tomcat внутри контейнера)?
- Какие шаги по диагностике проблемы с подключением к MySQL в docker-compose: какие лог-файлы и команды смотреть (docker logs, catalina.out, стек-трейсы приложения, mysql logs), как проверить JDBC-URL/хост/пользователя/пароль и время инициализации MySQL?
- Какие практические проверки и команды выполнить (например, jar tf/ unzip для просмотра WAR, docker exec + tail, netcat/ telnet для проверки порта, mysql client) и какие изменения в Dockerfile/docker-compose рекомендованы, чтобы гарантировать попадание Config.properties и стабильное подключение к БД?
Подскажите пошаговые проверки и команды для локализации причины и корректной конфигурации.
Проблема чаще всего комбинированная: версия Java/Tomcat не совпадает с тем, на что собирается WAR, JDBC‑драйвер/версия MySQL не подходят, а Config.properties не попал в classpath или читается по относительному пути (поэтому приложение «ищет в ROOT»). Проверьте pom.xml на target/source и зависимость mysql‑connector, распакуйте WAR (jar tf/unzip -l) чтобы убедиться, где лежит Config.properties, и убедитесь, что JDBC‑URL в свойствах указывает на сервис Docker (например db:3306), а не на localhost (см. примеры по сетям в Docker и docker‑compose). Ниже — пошаговая диагностика, команды и рекомендуемые правки Dockerfile/docker‑compose.
Содержание
- Определение необходимых версий Tomcat и MySQL
- Проверка Config.properties в WAR и classpath
- Почему конфиг ищется в ROOT и как исправить
- Диагностика подключения к MySQL в docker-compose
- Практические команды и шаги для локализации проблемы
- Рекомендуемые изменения в Dockerfile и docker‑compose
- Источники
- Заключение
Определение версий Tomcat и MySQL
- Где смотреть (быстро):
- pom.xml проекта (версия компилятора, mysql‑connector‑java, web/servlet API). Откройте локально или в репозитории, например pom.xml на GitHub.
- web.xml (требования к версии Servlet/JSP).
- README/Документация проекта (есть ли указанные требования).
- Команды для проверки локально в корне проекта (или в /build после clone):
- Просмотреть поддерживаемую версию Java/target:
grep -n "<maven.compiler" -n pom.xml || mvn help:evaluate -Dexpression=maven.compiler.target -q -DforceStdout
- Проверить зависимость драйвера:
mvn dependency:tree | grep -i mysql
- Если вы уже получили WAR, то проверить, под какую Java его собрали (ошибки на старте Tomcat будут выдавать UnsupportedClassVersionError):
docker-compose exec tomcat sh -c "catalina.sh version"
# и смотреть логи на UnsupportedClassVersionError
docker-compose logs tomcat | grep -i UnsupportedClassVersionError
- Типичные несоответствия и как их решать (чеклист):
- Если maven собирает под Java 11, а образ Tomcat — на jre8 (как в вашем Dockerfile: build на openjdk‑11, runtime tomcat:8‑jre8), возможен UnsupportedClassVersionError. Решение: либо собрать с source/target 1.8, либо использовать образ Tomcat с JDK11 (например tomcat:9‑jdk11).
- Если в pom.xml стоит mysql‑connector‑java 5.x, а вы запускаете MySQL 8 — может возникнуть проблема с аутентификацией (caching_sha2_password). Решение: обновить драйвер до 8.x или задать опцию сервера
--default-authentication-plugin=mysql_native_passwordпри запуске контейнера MySQL. - Если web.xml требует Servlet 3.x/4.x — убедитесь, что Tomcat версии соответствует (Tomcat 8 ≈ Servlet 3.1, Tomcat 9 ≈ Servlet 4.0).
(Ссылки по поведению в сети и использованию имени сервиса вместо localhost: см. обсуждения на StackOverflow: использовать имя контейнера вместо localhost, и пример с jdbc URL в docker‑compose jdbc://db:3306/….)
Проверка Config.properties в WAR и classpath
Почему важно: приложение обычно загружает конфиг либо с classpath (WEB-INF/classes), либо через ServletContext (внутри webapp), либо из внешнего пути. Если Config.properties не в ожидаемой папке — приложение не найдёт его и продолжит работать частично.
- Как проверить содержимое WAR (локально перед деплоем):
# в корне проекта, после mvn package
jar tf target/*.war | grep -i Config.properties
# или
unzip -l target/*.war | grep -i Config.properties
Ожидаемые пути внутри WAR:
- WEB-INF/classes/Config.properties — файл на classpath (лучше всего для Properties).
- WEB-INF/Config.properties — доступен через ServletContext.getResourceAsStream(“/WEB-INF/Config.properties”).
- /Config.properties (в корне WAR) — доступен через ServletContext.getResourceAsStream(“/Config.properties”) или при развёртывании в ROOT.
- Если WAR уже в контейнере — проверить так:
docker-compose up -d --build
docker-compose exec tomcat sh -c "jar tf /usr/local/tomcat/webapps/App43PaaS.war | grep -i Config.properties || ls -l /usr/local/tomcat/webapps/App43PaaS/WEB-INF/classes | grep -i Config.properties"
Если WAR распакован:
docker-compose exec tomcat sh -c "ls -la /usr/local/tomcat/webapps/App43PaaS/WEB-INF/classes | grep -i Config.properties"
docker-compose exec tomcat sh -c "ls -la /usr/local/tomcat/webapps/ROOT | head -n 40"
- Почему файл может не попасть в WAR:
- Проект использует нестандартную структуру (WebContent вместо src/main/webapp) и maven не копирует. Проверьте pom.xml на webResources или resources.
- COPY в Dockerfile до mvn package: в вашем Dockerfile вы делаете git clone внутри build‑стадии и затем COPY ./Config.properties ./WebContent/ — учтите, что COPY берёт файл из build‑контекста (локальная машина), а git clone внутри контейнера создаёт /build. Это рабочая, но хрупкая схема — лучше положить Config.properties в src/main/resources или src/main/webapp/WEB-INF перед mvn package.
- Решение: поместите Config.properties в src/main/resources (тогда он попадёт в WEB‑INF/classes) или настройте maven‑war‑plugin/webResources для включения WebContent. Пример упрощения:
<!-- pom.xml: maven-compiler-plugin и webResources -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
- Если нужно быстро проверить и отладить: временно монтируйте файл в контейнер:
volumes:
- ./Config.properties:/usr/local/tomcat/webapps/ROOT/WEB-INF/classes/Config.properties:ro
Так можно гарантированно поставлять файл в ожидаемое место.
Почему приложение может искать конфиг в ROOT и как исправить
Причины:
- WAR переименован в ROOT.war — Tomcat разворачивает его в
webapps/ROOTи приложение доступно на/. Тогда относительные обращения к корню будут находить этот ROOT. - Код читает конфиг через абсолютный путь ServletContext.getRealPath(“/Config.properties”) или напрямую из файловой системы
/Config.properties— это может вести в системный корень или в корень развернутого приложения (ROOT). - Контекст приложения и имя WAR не совпадают с ожиданиями (вы собираете App42PaaS…, а копируете в образ как App43PaaS.war — несоответствие имени может влиять на места поиска в логике приложения).
Как исправить (варианты, зависимо от требований):
- Лучший подход — класть конфиг на classpath и читать через ClassLoader:
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("Config.properties");
- Если приложение ждёт файл в корне webapp, развёрните как ROOT (если хотите root‑контекст):
- Переименуйте при копировании:
COPY ... /usr/local/tomcat/webapps/ROOT.war
или - Создайте context.xml с нужным path (менее рекомендуемо).
- Если нужен внешний конфиг (удобно для Docker) — положите файл в conf и передайте путь через JVM‑опцию:
environment:
- JAVA_OPTS=-Dconfig.file=/usr/local/tomcat/conf/Config.properties
volumes:
- ./Config.properties:/usr/local/tomcat/conf/Config.properties:ro
и в коде читать System.getProperty("config.file").
- Проверьте текущие имена/пути в контейнере:
docker-compose exec tomcat sh -c "ls -la /usr/local/tomcat/webapps"
docker-compose exec tomcat sh -c "grep -R \"Config.properties\" /usr/local/tomcat/logs || true"
Коротко: не полагайтесь на относительные пути и имя WAR — либо используйте classpath, либо явный внешний путь.
Диагностика подключения к MySQL в docker-compose
- Быстрая проверка логов:
- Логи Tomcat/приложения:
docker-compose logs --tail=200 tomcat
docker-compose exec tomcat sh -c "tail -n 200 /usr/local/tomcat/logs/catalina.out"
Ищите строки: SQLException, Communications link failure, Access denied, Unknown database, No suitable driver, ClassNotFoundException для com.mysql.*.
- Логи MySQL:
docker-compose logs --tail=200 db
# или
docker-compose exec db sh -c "cat /var/log/mysql/error.log || echo 'no file'; mysql --version"
(офиц. образ mysql пишет в stdout — docker logs видны).
- Частые причины и их распознавание:
- Неправильный хост (localhost) — в контейнере Tomcat localhost = сам Tomcat. Ошибка: Communications link failure / Connection refused. Решение: в JDBC URL использовать
db:3306(имя сервиса в docker‑compose) — см. обсуждение прямо на StackOverflow. - База ещё не готова — вы видите ошибки соединения на старте; depends_on не ждёт готовности. Решение: healthcheck / wait‑for / retry. Пример healthcheck для MySQL ниже.
- Неправильные credentials — SQLException: Access denied for user. Проверьте .env и переменные MYSQL_USER/MYSQL_PASSWORD/MYSQL_DATABASE.
- Несовместимость аутентификации MySQL8 и старого драйвера — ошибки типа Authentication plugin not supported. Решение: обновить mysql‑connector‑java до 8.x или запустить MySQL с
--default-authentication-plugin=mysql_native_password. - Драйвера нет в classpath — No suitable driver / ClassNotFoundException. Проверьте WEB-INF/lib на mysql-connector или поместите драйвер в Tomcat/lib.
- Проверки изнутри Tomcat‑контейнера:
- Проверка TCP:
# если в образе есть netcat
docker-compose exec tomcat sh -c "nc -zv db 3306 && echo ok || echo fail"
# или пытаться mysql-клиентом (если установлен)
docker-compose exec tomcat sh -c "mysql -h db -u${MYSQL_USER} -p${MYSQL_PASSWORD} -e 'SELECT 1'"
Если nc отсутствует, установите временно: apk add --no-cache netcat-openbsd (alpine).
- Проверка JDBC‑URL и driver:
- Откройте Config.properties (или где хранятся): ищите
jdbc.url,jdbc.user,jdbc.password,driverClassName. Пример корректного URL для docker compose:
jdbc:mysql://db:3306/your_db?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
- Убедитесь, что driverClassName соответствует пакету драйвера (для Connector/J8:
com.mysql.cj.jdbc.Driver).
- Проверить инициализацию MySQL и время старта:
В логах MySQL ищите строки о “ready for connections” — пока их нет, сервис не готов. depends_on не ждёт готовности. Решение: добавить healthcheck или wait‑for.
Практические команды и шаги для локализации причины (чек‑лист)
- Пересобрать и запустить с логами:
docker-compose build --no-cache docker-compose up --force-recreate --remove-orphans docker-compose logs -f
- Проверить содержимое WAR до/после копирования:
# локально
jar tf target/*.war | sed -n '1,200p' | grep -i Config.properties || echo "not found in war"
# в контейнере
docker-compose exec tomcat sh -c "jar tf /usr/local/tomcat/webapps/App43PaaS.war | grep -i Config.properties || true"
docker-compose exec tomcat sh -c "ls -la /usr/local/tomcat/webapps | sed -n '1,200p'"
- Искать ошибки соединения/драйвера в логах:
docker-compose logs tomcat | grep -Ei "SQLException|No suitable|Communications link|Access denied|UnsupportedClassVersionError|ClassNotFoundException"
- Проверить, доступен ли MySQL с точки зрения Tomcat:
docker-compose exec tomcat sh -c "apk add --no-cache mysql-client netcat-openbsd || true"
docker-compose exec tomcat sh -c "nc -zv db 3306 && echo 'port ok' || echo 'cannot connect to port'"
docker-compose exec tomcat sh -c "mysql -h db -u${MYSQL_USER} -p${MYSQL_PASSWORD} -e 'SELECT VERSION(), DATABASE();' ${MYSQL_DATABASE}"
- Проверить наличие драйвера в WEB‑INF/lib:
docker-compose exec tomcat sh -c "jar tf /usr/local/tomcat/webapps/App43PaaS.war | grep -i mysql-connector || ls -la /usr/local/tomcat/webapps/App43PaaS/WEB-INF/lib | grep -i mysql"
- Проверить, где приложение пытается читать конфиг — искать в логах упоминания Config.properties, стек‑трэйсы:
docker-compose exec tomcat sh -c "grep -R \"Config.properties\" /usr/local/tomcat/logs || true"
docker-compose exec tomcat sh -c "grep -R \"Config.properties\" /usr/local/tomcat/webapps || true"
- Проверить .env и переменные окружения:
cat .env
docker-compose exec db env | grep MYSQL
- Быстрый grep в pom.xml на потенциальные проблемы:
grep -n "mysql" pom.xml || true
grep -n "maven.compiler" pom.xml || true
Рекомендуемые изменения в Dockerfile и docker‑compose
Ключевые принципы: согласовать версию Java, включить конфиг в classpath либо монтировать его как внешний файл, обеспечить ожидание готовности БД и включить драйвер в WAR.
- Исправленный пример Dockerfile (вариант: использовать Tomcat с JDK11):
# Build (с JDK11)
FROM maven:3.6.3-openjdk-11 AS build
WORKDIR /app
# Копируем код из build context вместо git clone
COPY pom.xml .
COPY src ./src
# Помещаем Config.properties в src/main/resources перед сборкой
COPY Config.properties src/main/resources/
RUN mvn -B -DskipTests package
# Runtime — Tomcat на Java11
FROM tomcat:9-jdk11
WORKDIR /usr/local/tomcat/webapps
# Разворачиваем как ROOT, если хотим root-контекст
COPY --from=build /app/target/*.war ./ROOT.war
Если по каким‑то причинам вам нужен Tomcat 8/JRE8 — то в build‑стадии явно ставьте maven‑compiler target 1.8 в pom.xml.
- docker-compose: добавить healthcheck в MySQL и примитивный wait‑for (вариант):
version: '3.8'
services:
db:
image: mysql:8.0
env_file: .env
command: --default-authentication-plugin=mysql_native_password
volumes:
- ./db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 10
networks:
- app42paas
tomcat:
build: .
ports:
- "8080:8080"
depends_on:
- db # учтите: в v3 depends_on не ждёт health, поэтому используйте wait-for скрипт
environment:
- JAVA_OPTS=-Dconfig.file=/usr/local/tomcat/conf/Config.properties
volumes:
- ./Config.properties:/usr/local/tomcat/conf/Config.properties:ro
networks:
- app42paas
networks:
app42paas:
Примечание: depends_on в версии 3 не ждёт состояния health. Чтобы действительно ждать — добавьте в образ tomcat утилиту ожидания (wait‑for.sh/wait‑for-it/dockerize) и в ENTRYPOINT запуск Tomcat после проверки доступности db:3306.
- Обязательные доработки в pom.xml:
- Включите mysql‑connector‑java в dependencies (версия 8.x для MySQL 8):
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
- Убедитесь, что maven‑compiler‑plugin target соответствует образу Tomcat.
- Если хотите быстро отлаживать: монтируйте Config.properties внутрь контейнера (volume) и делайте
tail -fлогов — тогда менять файл не нужно пересобирать WAR.
Источники
- https://github.com/shephertz/App42PaaS-Java-MySQL-Sample/blob/master/pom.xml
- https://stackoverflow.com/questions/35510964/unable-to-connect-mysql-container-to-tomcat-container-in-docker
- https://stackoverflow.com/questions/64101683/spring-boot-unable-to-communicate-with-mysql-db-in-docker-environment
- https://stackoverflow.com/questions/34024504/tomcats-spring-app-properties-with-docker-container-linking
- https://stackoverflow.com/questions/29389184/docker-connect-apache-tomcat-web-server-to-mysql-server
- https://stackoverflow.com/questions/51947553/cannot-connect-tomcat-container-to-mysql-container
- https://stackoverflow.com/questions/52011016/connect-tomcat-and-mysql-in-docker-compose
Заключение
Коротко: начните с проверки pom.xml (версия компилятора и mysql‑connector), проверьте содержимое WAR (jar tf/unzip -l) — должен быть Config.properties в WEB-INF/classes либо файл должен монтироваться в контейнер, и замените в конфиге jdbc‑host на имя сервиса в сети docker (db). Приведённые команды, правки Dockerfile и docker‑compose (healthcheck/wait‑for и согласование JDK/Tomcat) позволят локализовать проблему и обеспечить стабильное подключение Docker MySQL к Tomcat. Удачи — если пришлёте выводы команд (логи, результат jar tf, строки из pom.xml), помогу интерпретировать и предложить точечные правки.