Під час підключення MinIO локально я зіткнулася з дуже типовою Docker-плутаниною.
У браузері веб-консоль MinIO відкривалася через localhost:9001. Здавалося логічним, що Laravel теж зможе звернутися до MinIO через localhost.
Але з контейнера застосунку ця адреса вже означає зовсім інше місце.
У кожного контейнера свій localhost.
Коли я це розділила, схема стала простою: з комп’ютера використовую опублікований порт, а між контейнерами - ім’я сервісу в мережі Docker.
Чому localhost:9001 працює в браузері
Browser | | http://localhost:9001 vComputer running Docker | | published port 9001 vMinIO containerБраузер працює на комп’ютері, де запущено Docker.
Тому для браузера localhost означає саме цей комп’ютер.
Docker публікує порт контейнера назовні. Завдяки цьому звернення до localhost:9001 потрапляє у веб-консоль MinIO.
У цьому сценарії localhost працює правильно, тому що запит починається поза контейнерами.
Усередині контейнера localhost означає сам контейнер
Laravel container | | http://localhost:9000 vLaravel container itself MinIO container X Запит до ньогонавіть не дійшов.Контейнер має власний мережевий простір.
Якщо PHP усередині контейнера відкриває http://localhost:9000, він шукає порт 9000 у цьому ж контейнері.
MinIO працює в іншому контейнері, тому такий запит до нього не потрапляє.
Проблема тут не в Laravel і не в MinIO. Адреса просто вказує не на те мережеве середовище.
127.0.0.1 має ту саму проблему
Заміна localhost на 127.0.0.1 тут нічого не виправляє.
Обидві адреси вказують назад на поточне мережеве середовище.
У контейнері це знову буде сам контейнер, а не інший сервіс Docker.
Контейнери знаходять один одного за ім’ям сервісу
Laravel container | | http://minio:9000 vDocker network | | service name: minio vMinIO containerЯкщо обидва сервіси підключені до однієї мережі Docker, вони можуть звертатися один до одного за ім’ям сервісу.
Для MinIO такою адресою в моєму випадку стала:
http://minio:9000
Docker сам знаходить контейнер, який відповідає імені minio.
Мені не потрібно знати його поточну внутрішню IP-адресу.
Я не прив’язую конфігурацію до IP контейнера
http://172.20.0.7:9000http://minio:9000Внутрішня IP-адреса контейнера може змінитися після повторного створення контейнера.
Ім’я сервісу є значно стабільнішою межею.
Тому в конфігурації застосунку я використовую minio, а не адресу виду 172.x.x.x.
Один сервіс може мати різні адреси залежно від точки входу
З браузера: http://localhost:9001 для веб-консолі З контейнера Laravel: http://minio:9000 для S3-сумісного APIСпочатку це може виглядати як суперечність: MinIO ніби має дві різні адреси.
Насправді сервіс один. Відрізняється шлях до нього.
Комп’ютер входить через опублікований порт Docker, а інший контейнер використовує внутрішню мережу Docker.
Тому ці адреси не конкурують між собою. Вони належать різним мережевим контекстам.
Порт контейнера і порт на комп’ютері теж не обов’язково однакові
Host localhost:19001 | vDocker port mapping | vcontainer:9001Docker може опублікувати внутрішній порт контейнера під іншим номером на комп’ютері.
Наприклад, зовні це може бути 19001, а всередині контейнер усе одно слухає 9001.
Між контейнерами зазвичай використовується саме внутрішній порт сервісу, а не номер, опублікований для комп’ютера.
Я перевіряю доступ саме з контейнера застосунку
curl -i \ http://minio:9000/minio/health/liveЯкщо Laravel повинен звертатися до MinIO з PHP-контейнера, я перевіряю саме цей маршрут.
Відкрита веб-консоль у браузері доводить лише те, що працює шлях від комп’ютера до опублікованого порту.
Вона нічого не говорить про зв’язок між двома контейнерами.
Тому health check з середовища застосунку є набагато кориснішою перевіркою.
Ім’я сервісу можна перевірити окремо
getent hosts minioЯкщо ім’я minio не знаходиться, проблема може бути ще до HTTP.
Наприклад, контейнери можуть не мати спільної мережі або сервіс має інше ім’я.
Тому я відділяю дві перевірки: спочатку чи знаходиться сервіс за ім’ям, потім чи відповідає потрібний порт.
У Laravel endpoint має відповідати мережі самого застосунку
AWS_ENDPOINT=http://localhost:9000AWS_ENDPOINT=http://minio:9000AWS_USE_PATH_STYLE_ENDPOINT=trueЯкщо Laravel виконується всередині Docker, його S3 endpoint повинен бути доступний саме звідти.
Тому для локального MinIO я використовую ім’я сервісу minio.
Це не спеціальна вимога Laravel. Це наслідок того, де саме виконується PHP.
Працює в браузері не означає працює з контейнера
Це головна практична помилка, яку я тепер перевіряю першою.
Браузер, PHP-контейнер, черга, окремий сервіс і сам MinIO можуть знаходитися в різних мережевих контекстах.
Тому адреса, яка працює в одному місці, не зобов’язана працювати в іншому.
Перш ніж змінювати Laravel-конфігурацію або шукати проблему в S3-драйвері, я дивлюся, звідки саме виконується запит.
Модель, яка допомагає мені не плутатися
Якщо запит іде з комп'ютера: localhost-> сам комп'ютер Якщо запит іде з контейнера: localhost-> цей контейнер Якщо контейнеру потрібенінший контейнер: ім'я сервісуЩо я перевіряю, коли контейнер не бачить інший сервіс
Мій короткий список:
- Звідки насправді виконується запит?
- Що означає localhost саме в цьому середовищі?
- Чи запущений потрібний контейнер?
- Чи підключені обидва контейнери до спільної мережі Docker?
- Яке ім’я має потрібний сервіс?
- Чи знаходиться це ім’я всередині контейнера?
- Який внутрішній порт слухає сервіс?
- Чи не використовую я зовнішній порт там, де потрібен внутрішній?
- Чи відповідає сервіс при прямій перевірці з контейнера?
localhost завжди залежить від точки, з якої дивимося
Проблема з localhost між контейнерами стає набагато простішою, коли перестати сприймати його як універсальну адресу комп’ютера.
Для браузера localhost означає комп’ютер.
Для Laravel усередині контейнера localhost означає контейнер Laravel.
А щоб Laravel звернувся до MinIO в сусідньому контейнері, потрібне ім’я сервісу в спільній мережі Docker.
Тому питання "яку адресу використати?" я тепер починаю не з сервісу, а з іншого питання: "звідки саме виконується цей запит?".
Перед тим як міняти порт або конфігурацію, я спочатку визначаю, для кого саме localhost є localhost.



