[PYTHON] Comment rendre le nom du conteneur accessible dans Docker en tant que sous-domaine

Cet article est le 11ème jour du Calendrier de l'Avent Recruit Lifestyle 2015 - Qiita. Je suis moremagic, qui est en charge du développement chez Hot Pepper Beauty. Nous travaillons dans l'équipe de l'infrastructure des applications pour aider les développeurs.

introduction

Utilisez-vous Docker? J'ai récemment commencé à l'utiliser, mais c'est incroyablement pratique!

Cependant, à mesure que le nombre de conteneurs augmente, il est difficile de se souvenir du numéro de port. .. De plus, il est génial que vous ne puissiez savoir à quel service vous accédez par numéro de port. Au lieu de changer le conteneur pour accéder par numéro de port Ne serait-il pas plus facile de comprendre si le nom du conteneur saisissait le nom de domaine tel quel?

Donc, cette fois, je parlerai de l'accès avec Docker avec le nom du conteneur comme sous-domaine.

Que signifie faire du nom de conteneur un sous-domaine?

Par exemple ,, Le nom du serveur exécutant Docker est ʻexample.com Nom du conteneurdev-tomcat` En supposant que le port 8080 est transféré vers le port 49000

Normalement, si vous souhaitez accéder au port 8080 du conteneur dev-tomcat ci-dessus Parce que le port 8080 est redirigé vers le port 49000 Accédez comme suit.

http://example.com:49000/

S'il n'y a qu'un seul conteneur démarré, ce n'est pas difficile. Il est difficile de se souvenir quand le nombre de conteneurs et de services augmente. Si vous faites une erreur, vous vous connecterez à un autre service.

Il est donc facile pour les humains de comprendre

http://dev-tomcat-8080.example.com

C'est une histoire pour la rendre accessible comme décrit ci-dessus. Dans ce cas, vous pouvez y accéder en mémorisant le nom du conteneur et le port qui fonctionne réellement dans le conteneur!

Stratégie

Créez et lancez un conteneur de résolution de nom qui rend le sous-domaine de nom de conteneur accessible au sein du serveur. Puisqu'il est réalisé par proxy http, la résolution de sous-domaine n'est possible que pour le protocole http.

docker-proxy.png

Les informations du conteneur de Docker sont stockées périodiquement dans le conteneur simplement en démarrant le conteneur de résolution de nom. Vous pouvez accéder par nom de conteneur sans réécrire le fichier de paramètres chaque fois que vous démarrez le conteneur.

supposition

Il existe un environnement Docker et l'hôte sur lequel Docker s'exécute peut résoudre le nom. RemoteAPI de Docker activé

procédure

  1. Créez une image avec Docker
  2. Lancez le conteneur depuis Image
  3. Profitez de la résolution de noms

Créer une image avec Docker

Préparez les fichiers suivants et créez une image Docker Placez autre que Dockerfile dans un dossier approprié selon la description de Dockerfile.

Fichier Docker

Le Dockerfile ressemble à ceci. Installez redis, python3, nginx basé sur ubuntu: 14.04 Je crée un shell de démarrage et je le frappe au démarrage

Dockerfile


FROM ubuntu:14.04

~ Omis ~

# python3 install
RUN apt-get install -y python3 python3-pip && apt-get clean
RUN pip3 install redis
ADD redis/regist.py /usr/sbin/regist.py
RUN chmod +x  /usr/sbin/regist.py

# redis
RUN apt-get update && apt-get install -fy redis-server
ADD redis/redis.conf /etc/redis/redis.conf

# nginx install
RUN apt-get -y install nginx lua-nginx-redis && apt-get clean
ADD nginx/default /etc/nginx/sites-available/
ADD nginx/rewrite.lua /etc/nginx/sites-available/
ADD nginx/cert/ /etc/nginx/cert/

#Créer un shell de lancement
RUN printf '#!/bin/bash \n\
/usr/bin/redis-server & \n\
/usr/sbin/regist.py > /dev/null & \n\
/etc/init.d/nginx start \n\
/etc/init.d/nginx reload \n\
/usr/sbin/sshd -D \n\
tail -f /var/null \n\
' >> /etc/service.sh \
    && chmod +x /etc/service.sh

EXPOSE 22 6379 80 443
CMD /etc/service.sh

nginx La clé de l'opération, le fichier de configuration nginx ressemble à ceci. Le serveur attend à 80 443 et est configuré pour réécrire → processus.

default


server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    server_name localhost;
    location / {
        set $upstream "";
        rewrite_by_lua_file /etc/nginx/sites-available/rewrite.lua;

        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://$upstream;

        client_max_body_size 200M;
    }
}

server {
    listen 443;
    server_name localhost;

    ssl on;
    ssl_certificate /etc/nginx/cert/ssl.crt;
    ssl_certificate_key /etc/nginx/cert/ssl.key;
    ssl_session_timeout 5m;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        set $upstream "";
        rewrite_by_lua_file /etc/nginx/sites-available/rewrite.lua;

        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass https://$upstream;

        client_max_body_size 200M;
    }
}

rewrite.lua Obtenez le nom du conteneur et le numéro de port à partir du nom d'hôte. Contactez redis et renvoyez le conteneur réel

rewrite.lua


local routes = _G.routes

if routes == nil then
    routes = {}
    ngx.log(ngx.ALERT, "[[[Route cache is empty.]]")
end

local container_name = string.sub(ngx.var.http_host, 1, string.find(ngx.var.http_host, "%.")-1)
local route = routes[container_name]
if route == nil then
    local Redis  = require "nginx.redis"
    local client = Redis:new()

    client:set_timeout(1000)
    local ok, err = client:connect("127.0.0.1", 6379)
    if not ok then
        ngx.log(ngx.ERR, "************ Redis connection failure: " .. err)
        return
     end

    route = client:get(container_name)
end

ngx.log(ngx.ALERT, route)

-- fallback to redis for lookups
if route ~= nil then
    ngx.var.upstream = route
    routes[container_name] = route
else
    ngx.log(ngx.ALERT, "=ng=[[[route null]]]")
    ngx.exit(ngx.HTTP_NOT_FOUND)
end

python C'est un joueur en coulisses qui stocke les informations sur les conteneurs dans Redis. Il met à jour les informations du conteneur toutes les 3 secondes via l'API distante de Docker. Je l'ai exécuté en boucle tout le temps, mais il y a peut-être un peu plus de chemin. .. ..

regist.py


#!/usr/bin/python3

import os
import sys
import time
import json
import redis
import urllib.request

DOCKER_HOST = os.getenv('DOCKER_HOST')
REDIS_ADDR = '127.0.0.1'
REDIS_PORT = 6379


def redisDump():
  conn = redis.Redis(host=REDIS_ADDR, port=REDIS_PORT)
  for key in conn.keys():
    print(key)
    print(conn.get(key))
  return conn.keys()

def addData(datas):
  conn = redis.Redis(host=REDIS_ADDR, port=REDIS_PORT)
  for key in set(list(datas.keys()) + list(conn.keys())):
    if isinstance(key, bytes):
      key = key.decode('utf-8')
    if key in datas:
      conn.set(key, datas[key])
    else:
      conn.delete(key)

def getContainers():
  response = urllib.request.urlopen('http://' + DOCKER_HOST + '/containers/json?all=1')
  jsonData = json.loads(response.read().decode('utf-8'))

  datas = {}
  for con in jsonData:
    name = con['Names'][-1][1:]
    con_ip = getIpAddress(con['Id'])

    for port in con['Ports']:
      key = name + '-' + str(port['PrivatePort'])
      value=con_ip + ':' + str(port['PrivatePort'])
      datas[key] = value

  return datas


def getIpAddress(con_id):
  response = urllib.request.urlopen('http://' + DOCKER_HOST + '/containers/' + con_id + '/json')
  jsonData = json.loads(response.read().decode('utf-8'))
  #print(json.dumps(jsonData))
  ret = jsonData['NetworkSettings']['IPAddress']
  return ret

while True:
  addData(getContainers())
  print( redisDump() )
  sys.stdout.flush()
  time.sleep(3)

Construire l'image

Allez dans le dossier où le Dockerfile est stocké et tapez la commande suivante!

# docker build -t docker-discovery .

Lancer le conteneur depuis Image

Démarrez l'image que vous avez créée précédemment. Étant donné que les informations sur le conteneur sont acquises à l'aide de l'API DockerRemote de Docker s'exécutant sur l'hôte depuis l'intérieur du conteneur, je vais l'épeler au démarrage.

# docker run -d -p 80:80 -p 443: 443 -e DOCKER_HOST = <Adresse IP de l'hôte Docker>: <Port RemoteAPI> --name docker-discovery docker-discovery

  1. Transférez les ports 80 et 443.
  2. La variable d'environnement DOCKER_HOST est spécifiée

C'est tout ce dont vous avez besoin pour la résolution de noms!

Profitez de la résolution de noms

Supposons que l'hôte exécutant le docker soit example.com Vous pouvez vous connecter au conteneur correspondant avec l'URL suivante.

http: // {nom du conteneur} - {numéro de port dans le conteneur public} .example.com

Commençons par le conteneur TeamCity à titre d'essai. docker run -dP --name teamcity moremagic/teamcity

Ce conteneur expose le port 8111 au monde extérieur, vous pouvez donc y accéder à l'URL suivante. http://teamcity-8111.example.com

image

Profitez de Docker!

Recommended Posts

Comment rendre le nom du conteneur accessible dans Docker en tant que sous-domaine
Comment utiliser Decorator dans Django et comment le créer
Comment supprimer un conteneur Docker
Je veux créer un fichier pip et le refléter dans le menu fixe
Comment obtenir un nom de colonne et un nom d'index spécifiques avec Pandas DataFrame
Flutter dans Docker - Comment créer et utiliser un environnement de développement Flutter dans un conteneur Docker
[Python] Comment nommer les données de table et les sortir avec csv (méthode to_csv)
Comment lire les fichiers de numéros de série en boucle, les traiter et les représenter graphiquement
Comment exécuter une application Django sur un conteneur Docker (environnement de développement et de production)
Vous pouvez le faire en 3 minutes! Comment créer un code QR (GIF) fonctionnel!
Comment utiliser Docker pour conteneuriser votre application et comment utiliser Docker Compose pour exécuter votre application dans un environnement de développement
Les étrangers parlent: Comment nommer les classes et les méthodes en anglais
Comment implémenter Python EXE pour Windows avec le conteneur Docker
Comment faire une traduction japonais-anglais
Comment dessiner de manière interactive un pipeline d'apprentissage automatique avec scikit-learn et l'enregistrer au format HTML
Comment créer un bot slack
Comment créer un robot - Avancé
Comment créer une fonction récursive
[Blender] Comment créer un plug-in Blender
Comment créer un robot - Basic
Comment se connecter à Docker + NGINX
Comment installer OpenCV sur Cloud9 et l'exécuter en Python
Comment comparer des listes et récupérer des éléments communs dans une liste
Comment diviser et enregistrer un DataFrame
Qiita (1) Comment écrire un nom de code
[Python] Comment rendre une classe itérable
Comment obtenir stacktrace en python
Comment créer un indicateur personnalisé Backtrader
Comment créer un plan de site Pelican
Comment utiliser is et == en Python
Comment faire un modèle pour la détection d'objets avec YOLO en 3 heures
Pour trouver le nom de la vue avec l'espace de noms à partir de l'URL (path_info) dans Django
Comment arrêter le programme jusqu'à une date et une heure spécifiques en python
Comment saisir une chaîne de caractères en Python et la sortir telle quelle ou dans la direction opposée.
Comment créer un système de dialogue dédié aux débutants
Comment générer un code QR et un code à barres en Python et le lire normalement ou en temps réel avec OpenCV
Comment effacer un taple dans une liste (Python)
Comment incorporer des variables dans des chaînes python
[Introduction à Pandas] Lire un fichier csv sans nom de colonne et lui donner un nom de colonne
Comment créer un fichier JSON en Python
Comment créer un dictionnaire avec une structure hiérarchique.
Comment refléter ImageField dans Django + Docker (oreiller)
Comment compter le nombre d'éléments dans Django et sortir dans le modèle
Créez un chat bot et entraînez-vous à être populaire.
Comment créer un plug-in QGIS (génération de package)
Comment enregistrer les informations de point caractéristique de l'image dans un fichier et l'utiliser pour la mise en correspondance
Comment notifier les canaux Discord en Python
J'ai lu "Comment créer un laboratoire de piratage"
[Python] Comment dessiner un histogramme avec Matplotlib
Comment créer une API Rest dans Django
Comment écrire un document tuple nommé en 2020
Une commande pour spécifier un fichier avec un nom spécifique dans un répertoire avec find et mv, cp ou gzip (linux)
Comment compter les nombres dans une plage spécifique
[Pandas] Comment vérifier les doublons dans un tableau et supprimer les doublons (équivalent à supprimer les doublons dans Excel)
Comment installer le détecteur Cascade et comment l'utiliser
Comment lire des fichiers dans différents répertoires