Docker-Samenstellen van projecten met identieke servicenamen
Gebruik de servicenaam Docker-Compose alleen als de service zich alleen op het interne netwerk van het Docker-Compose project bevindt.
Als we identieke Docker-Compose projecten hebben met identieke servicenamen, verbonden door een Docker netwerk, moeten we ervoor zorgen dat we de juiste service openen. In een Docker netwerk zijn er twee manieren om een service te benaderen:
- Op servicenaam
- Op containernaam
Ik heb meerdere Docker-Compose projecten die bijna identiek zijn, elk project staat in zijn eigen directory en heeft zijn eigen omgeving.
Ik ging ervan uit dat een service in een Docker-Compose project altijd een andere service in hetzelfde Docker-Compose project zou gebruiken wanneer de servicenaam (niet de containernaam) wordt gebruikt. En dat het een fout zou geven als het niet beschikbaar was. Ik had het mis. Wat er feitelijk gebeurt, is dat je de service met dezelfde naam kunt openen in een ander Docker-Compose project.
Zoals altijd draai ik dit op Ubuntu 22.04.
Projectinstelling
We hebben twee mappen met onze (bijna) identieke projecten.
├── my_app1
│ ├── docker-compose.yml
│ └── .env
├── my_app2
│ ├── docker-compose.yml
│ └── .env
De twee 'docker-compose.yml' bestanden zijn identiek. De '.env' bestanden zijn anders, ze bevatten slechts één omgevingsvariabele, de 'COMPOSE_PROJECT_NAME'. Deze variabele wordt niet alleen gebruikt om de containernamen te maken, we gebruiken hem ook binnen onze containers om het Docker-Compose project te identificeren.
We gebruiken hier 'nicolaka/netshoot' Docker image . Het heeft veel nuttige hulpprogramma's, waaronder een goed werkende 'netcat', 'nc', die we gebruiken om luister daemon's te maken.
Hier zijn de bestanden:
# my_app1/.env
COMPOSE_PROJECT_NAME=my_app1
en
# my_app2/.env
COMPOSE_PROJECT_NAME=my_app2
en
# docker-compose.yml
version: '3.7'
x-service_defaults: &service_defaults
env_file:
- ./.env
restart: always
services:
app:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - app:\" | /usr/bin/nc -l 80"
networks:
- internal_network
- app_network
web:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - web:\" | /usr/bin/nc -l 81"
networks:
- internal_network
- app_network
networks:
internal_network:
app_network:
external:
name: my_app_network
Voordat we beginnen, maken we het externe Docker netwerk aan:
docker network create my_app_network
Enkele controles uitvoeren
Open enkele terminals en start beide projecten door dit commando in elke map uit te voeren:
docker-compose up
Controleer of de volgende containers zijn aangemaakt en draaien:
my_app1_app_1
my_app1_web_1
my_app2_app_1
my_app2_web_1
Voer nu in een andere terminal uit:
docker run -it --network=my_app_network --rm busybox
Open een service in onze containers met behulp van de containernaam:
telnet my_app_1_app_1 80
Het antwoord zal zijn:
Connected to my_app1_app_1
From my_app1 - app:
Dit werkt zoals verwacht. Type wat tekst. Dit zal worden herhaald. Je kunt dit ook controleren in de terminal van het 'my_app1' Docker-Compose project.
De fout genereren
Eerst gaan we naar de 'app' service van het 'my_app1' project:
docker exec -it my_app1_app_1 bash
Laten we nu de service 'web' in hetzelfde Docker-Compose project openen met de servicenaam:
telnet web 81
Het antwoord is:
Connected to web
From my_app1 - web:
Perfect, dit is wat we verwachtten.
Laten we nu de 'web' service in het 'my_app1' project (tijdelijk) laten verdwijnen. We doen dit door het naar beneden te halen.
In een andere terminal gaan we naar de 'my_app1' directory en typen:
docker-compose stop web
Nu openen we opnieuw de 'web' service in hetzelfde project:
telnet web 81
Het antwoord is:
Connected to web
From my_app2 - web:
Uh-oh. Nu openen we de 'web' service in 'my_app2' terwijl we de 'web' service in 'my_app1' wilden openen. Merk op dat er (veel) betere, maar ook veel complexere manieren zijn om dit te simuleren.
Oplossingen
Er zijn hier twee mogelijke oplossingen, afhankelijk van je situatie:
Oplossing 1. Als de 'web' service niet extern hoeft te zijn, verwijderen we deze uit het externe netwerk.
We doen dit voor beide projecten (onthoud dat de 'docker-compose.yml' bestanden identiek zijn):
web:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - web:\" | /usr/bin/nc -l 81"
networks:
- internal_network
#- app_network
Haal de containers naar beneden en weer naar boven, en stop de 'web' service van het 'my_app1' project weer.
Als je nu probeert toegang te krijgen tot de 'web' service, zie hierboven:
telnet web 81
zal het antwoord na enige tijd zijn:
telnet: bad address 'web'
Goed. We krijgen een foutmelding als de service niet beschikbaar is.
Oplossing 2. Als de 'web' service ook extern moet zijn, gebruiken we altijd de containernaam van de 'web' service als we ernaar verwijzen, zelfs als we in hetzelfde Docker-Compose project zitten.
Dit is niet echt een mooie oplossing, maar er is geen andere manier. Als de 'app' service toegang wil tot de 'web' service in hetzelfde Docker-Compose project, kunnen we de containernaam construeren met behulp van de 'COMPOSE_PROJECT_NAME' variabele:
container_name = <COMPOSE_PROJECT_NAME>_<service name>_1
Voor de 'web' service in 'my_app1':
web_container_name = my_app1_web_1
Samenvatting
Ik kwam dit tegen toen ik van één naar twee containers ging. Er begonnen vreemde dingen te gebeuren en gegevens werden verwisseld tussen de containers. Nadat ik het probleem had gevonden, simuleerde ik het met de bovenstaande code. Daarna heb ik het opgelost voor mijn project. Les geleerd: Nooit aannemen.
Links / credits
Docker - Change pre-defined environment variables in Docker Compose
https://docs.docker.com/compose/environment-variables/envvars
nicolaka / netshoot
https://github.com/nicolaka/netshoot
Lees meer
Docker Docker-compose
Recent
- Database UUID primaire sleutels van je webapplicatie verbergen
- Don't Repeat Yourself (DRY) met Jinja2
- SQLAlchemy, PostgreSQL, maximum aantal rijen per user
- Toon de waarden in SQLAlchemy dynamische filters
- Veilige gegevensoverdracht met Public Key versleuteling en pyNaCl
- rqlite: een alternatief voor SQLite met hoge beschikbaarheid en distributed
Meest bekeken
- Met behulp van Python's pyOpenSSL om SSL-certificaten die van een host zijn gedownload te controleren
- Gebruik van UUIDs in plaats van Integer Autoincrement Primary Keys met SQLAlchemy en MariaDb
- Maak verbinding met een dienst op een Docker host vanaf een Docker container
- PyInstaller en Cython gebruiken om een Python executable te maken
- SQLAlchemy: Gebruik van Cascade Deletes om verwante objecten te verwijderen
- Flask RESTful API verzoekparametervalidatie met Marshmallow-schema's