[PYTHON] So machen Sie den Containernamen in Docker als Subdomain zugänglich

Dieser Artikel ist der 11. Tag von Recruit Lifestyle Adventskalender 2015 - Qiita. Ich bin moremagisch und für die Entwicklung bei Hot Pepper Beauty verantwortlich. Wir arbeiten im App-Infrastruktur-Team, um Entwicklern zu helfen.

Einführung

Benutzt ihr Docker? Ich habe vor kurzem angefangen, es zu benutzen, aber es ist wahnsinnig praktisch!

Mit zunehmender Anzahl von Containern ist es jedoch schwierig, sich die Portnummer zu merken. .. Es ist auch fantastisch, dass Sie nur anhand der Portnummer wissen können, auf welchen Dienst Sie zugreifen. Anstatt den Container auf den Zugriff nach Portnummer umzustellen Wäre es nicht einfacher zu verstehen, wenn der Containername den Domainnamen so wie er ist eingegeben hätte?

Dieses Mal werde ich über den Zugriff mit Docker mit dem Containernamen als Subdomain sprechen.

Was bedeutet es, den Containernamen zu einer Subdomain zu machen?

Zum Beispiel ,, Der Name des Servers, auf dem Docker ausgeführt wird, lautet "example.com" Containername dev-tomcat Angenommen, Port 8080 leitet an Port 49000 weiter

Normalerweise, wenn Sie auf Port 8080 des oben genannten dev-tomcat-Containers zugreifen möchten Weil Port 8080 an Port 49000 weiterleitet Zugriff wie folgt.

http://example.com:49000/

Wenn nur ein Container gestartet wird, ist dies nicht schwierig. Es ist schwer zu merken, wann die Anzahl der Container und Services zunimmt. Wenn Sie einen Fehler machen, stellen Sie eine Verbindung zu einem anderen Dienst her.

So ist es für Menschen leicht zu verstehen

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

Es ist eine Geschichte, um es wie oben beschrieben zugänglich zu machen. In diesem Fall können Sie darauf zugreifen, indem Sie sich den Containernamen und den Port merken, der tatsächlich im Container ausgeführt wird!

Strategie

Erstellen Sie einen Container mit Namensauflösung und starten Sie ihn, damit auf die Subdomäne für den Containernamen auf dem Server zugegriffen werden kann. Da dies über den http-Proxy realisiert wird, ist die Auflösung von Subdomains nur für das http-Protokoll möglich.

docker-proxy.png

Die Containerinformationen von Docker werden regelmäßig im Container gespeichert, indem der Container mit Namensauflösung gestartet wird. Sie können über den Containernamen zugreifen, ohne die Einstellungsdatei bei jedem Start des Containers neu zu schreiben.

Annahme

Es gibt eine Docker-Umgebung, und der Host, auf dem Docker ausgeführt wird, kann den Namen auflösen. Docker's RemoteAPI aktiviert

Verfahren

  1. Erstellen Sie ein Image mit Docker
  2. Starten Sie den Container über Image
  3. Genießen Sie die Namensauflösung

Erstellen Sie ein Bild mit Docker

Bereiten Sie die folgenden Dateien vor und erstellen Sie ein Docker-Image Legen Sie eine andere Datei als Dockerfile in einem geeigneten Ordner gemäß der Beschreibung von Dockerfile ab.

Docker-Datei

Das Dockerfile sieht so aus. Installieren Sie redis, python3, nginx basierend auf ubuntu: 14.04 Ich mache eine Start-Shell und drücke sie beim Start

Dockerfile


FROM ubuntu:14.04

~ Ausgelassen ~

# 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/

#Start-Shell erstellen
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 Der Schlüssel zum Vorgang, die Nginx-Konfigurationsdatei, sieht folgendermaßen aus. Der Server wartet auf 80.443 und ist auf Umschreiben → Prozess eingestellt.

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 Rufen Sie den Containernamen und die Portnummer vom Hostnamen ab. Wenden Sie sich an redis und geben Sie den eigentlichen Behälter zurück

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 Es ist ein Spieler hinter den Kulissen, der Containerinformationen in Redis speichert. Die Containerinformationen werden alle 3 Sekunden über die Remote-API von Docker aktualisiert. Ich habe es die ganze Zeit in einer Schleife ausgeführt, aber es kann noch ein bisschen mehr geben. .. ..

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)

Image erstellen

Gehen Sie zu dem Ordner, in dem die Docker-Datei gespeichert ist, und geben Sie den folgenden Befehl ein!

# docker build -t docker-discovery .

Starten Sie den Container über Image

Starten Sie das zuvor erstellte Image. Da die Containerinformationen mithilfe der DockerRemote-API von Docker erfasst werden, die auf dem Host im Container ausgeführt wird, werde ich sie beim Start buchstabieren.

# docker run -d -p 80:80 -p 443: 443 -e DOCKER_HOST = <IP-Adresse des Docker-Hosts>: <RemoteAPI-Port> --name Docker-Discovery Docker-Discovery

  1. Weiterleiten der Ports 80 und 443.
  2. Die Umgebungsvariable DOCKER_HOST wird angegeben

Dies ist alles, was Sie zur Namensauflösung benötigen!

Genießen Sie die Namensauflösung

Angenommen, der Host, auf dem Docker ausgeführt wird, ist example.com Sie können mit der folgenden URL eine Verbindung zum entsprechenden Container herstellen.

http: // {Containername} - {Portnummer im öffentlichen Container} .example.com

Beginnen wir den TeamCity-Container als Testversion. docker run -dP --name teamcity moremagic/teamcity

Dieser Container stellt Port 8111 der Außenwelt zur Verfügung, sodass Sie unter der folgenden URL darauf zugreifen können. http://teamcity-8111.example.com

image

Viel Spaß mit Docker!

Recommended Posts

So machen Sie den Containernamen in Docker als Subdomain zugänglich
Wie man Decorator in Django benutzt und wie man es macht
So löschen Sie einen Docker-Container
Ich möchte eine Pipfile erstellen und im Docker wiedergeben
So erhalten Sie mit pandas DataFrame einen bestimmten Spaltennamen und Indexnamen
Flutter in Docker - Erstellen und Verwenden einer Flutter-Entwicklungsumgebung in einem Docker-Container
[Python] So benennen Sie Tabellendaten und geben sie mit csv aus (to_csv-Methode)
So lesen Sie Seriennummerndateien in einer Schleife, verarbeiten sie und zeichnen sie grafisch auf
So führen Sie eine Django-Anwendung auf einem Docker-Container aus (Entwicklungs- und Produktionsumgebung)
Sie können es in 3 Minuten tun! So erstellen Sie einen funktionierenden QR-Code (GIF)!
Verwendung von Docker zum Containerisieren Ihrer Anwendung und Verwenden von Docker Compose zum Ausführen Ihrer Anwendung in einer Entwicklungsumgebung
Ausländer sprechen: Wie man Klassen und Methoden auf Englisch benennt
So implementieren Sie Python EXE für Windows mit Docker-Container
Wie erstelle ich eine japanisch-englische Übersetzung?
So zeichnen Sie interaktiv eine Pipeline für maschinelles Lernen mit scikit-learn und speichern sie in HTML
Wie man einen lockeren Bot macht
Wie erstelle ich einen Crawler?
So erstellen Sie eine rekursive Funktion
[Blender] So erstellen Sie ein Blender-Plug-In
Wie erstelle ich einen Crawler?
So melden Sie sich bei Docker + NGINX an
So installieren Sie OpenCV in Cloud9 und führen es in Python aus
So vergleichen Sie Listen und rufen allgemeine Elemente in einer Liste ab
So teilen und speichern Sie einen DataFrame
Qiita (1) Wie schreibe ich einen Codenamen?
[Python] Wie man eine Klasse iterierbar macht
Wie bekomme ich Stacktrace in Python?
So erstellen Sie einen benutzerdefinierten Backtrader-Indikator
Wie erstelle ich eine Pelican Site Map?
Verwendung ist und == in Python
So erstellen Sie mit YOLO in 3 Stunden ein Modell für die Objekterkennung
So finden Sie den Ansichtsnamen mit dem Namespace über die URL (path_info) in Django
So stoppen Sie das Programm bis zu einem bestimmten Datum und einer bestimmten Uhrzeit in Python
So geben Sie eine Zeichenfolge in Python ein und geben sie unverändert oder in die entgegengesetzte Richtung aus.
Wie man ein Dialogsystem für Anfänger erstellt
So generieren Sie QR-Code und Barcode in Python und lesen ihn normal oder in Echtzeit mit OpenCV
So löschen Sie einen Taple in einer Liste (Python)
Einbetten von Variablen in Python-Strings
[Einführung in Pandas] Lesen Sie eine CSV-Datei ohne Spaltennamen und geben Sie ihr einen Spaltennamen
So erstellen Sie eine JSON-Datei in Python
So erstellen Sie ein Wörterbuch mit einer hierarchischen Struktur.
So reflektieren Sie ImageField in Django + Docker (Kissen)
So zählen Sie die Anzahl der Elemente in Django und geben sie in die Vorlage aus
Machen Sie einen Chat-Bot und üben Sie, beliebt zu sein.
So erstellen Sie ein QGIS-Plug-In (Paketerzeugung)
So speichern Sie die Feature-Point-Informationen des Bildes in einer Datei und verwenden sie zum Abgleichen
So benachrichtigen Sie Discord-Kanäle in Python
Ich las "Wie man ein Hacking Lab macht"
[Python] Wie zeichnet man mit Matplotlib ein Histogramm?
So erstellen Sie eine Rest-API in Django
Wie schreibe ich ein benanntes Tupeldokument im Jahr 2020?
Ein Befehl zum Angeben einer Datei mit einem bestimmten Namen in einem Verzeichnis mit find und mv, cp oder gzip (Linux)
So zählen Sie Zahlen in einem bestimmten Bereich
[Pandas] So überprüfen Sie Duplikate in einer Tabelle und löschen Duplikate (entspricht dem Löschen von Duplikaten in Excel)
So installieren Sie den Cascade-Detektor und wie verwenden Sie ihn
So lesen Sie Dateien in verschiedenen Verzeichnissen