Ich wollte mehrere Apps zum Entwickeln und Testen starten, deshalb habe ich ein einfaches ALB-Tool erstellt. (Umgebung ist AWS) Ich habe mich aufgrund der folgenden Einschränkungen für ein Tool entschieden.
Zwang:
Da wir Docker verwenden, werden wir als Ansatz die Verwendung des Standardcontainers von nginx in Betracht ziehen. Ursprünglich möchte ich mehrere Apps parallel auf dem Host ausführen, daher werde ich jedem unterschiedliche Ports zuweisen und sie starten und ein Formular anstreben, das mit nginx nach Hostnamen sortiert wird.
Da das Verteilungsziel aus Sicht von nginx ein anderer Container ist, der auf demselben Host ausgeführt wird, wird eine Verbindung zum Host innerhalb des Containers hergestellt.
Da es sich um eine vereinfachte Version als Tool handelt, werden wir fortfahren, solange wir kein neues Docker-Image erstellen oder ein Plug-In hinzufügen.
Ich habe das Tool, das ich erstellt habe, auf GitHub gestellt. https://github.com/batatch/simple-alb
Obwohl es sich um eine vereinfachte Version handelt, ähneln die Einstellungen denen von AWS ALB, es wird eine Definitionsdatei erstellt und daraus werden Nginx-Einstellungen generiert → mit dem Ziel einer Konfiguration wie der Docker-Ausführung.
Verfahren:
$ make build #Generieren Sie eine Nginx-Konfigurationsdatei aus der Definitionsdatei
$ make up # docker-Weisen Sie die Konfigurationsdatei mit compose zu und starten Sie den Nginx-Container
$ make down # docker-Stoppen Sie den Nginx-Container mit Compose
Definitionsdatei:
alb.yml
---
http:
listen: 80 #Listener, diesmal nur HTTP
rules:
- if: #IF-Bedingung von ALB
host: app01.example.com #Übereinstimmender Hostname
pathes: [ "/" ] #Pfadübereinstimmung
then: #ALB DANN Aussage
forward: #Einstellungen übertragen
name: tg-app01
targets: #Weiterleitungsziel(Mehrere), Bild wie Zielgruppe
- target: http://docker0:21080
weight: 30
- target: http://docker0:22080
stickiness: true
:
Die Prozedur ist in einem Makefile zusammengefasst. Ich mag es, aber ich denke, das ist am einfachsten und am einfachsten zu verstehen. Erstellen Sie dann basierend auf der obigen Definitionsdatei die folgende Nginx-Konfigurationsdatei.
nginx/conf.d/default.conf
upstream target1 {
server http://docker0:21080;
server http://docker0:22080;
}
server {
listen 80;
server_name app01.example.com;
:
location / {
proxy_pass http://target1;
}
}
Das Framework sieht so aus.
Da die Einstellungsdatei automatisch generiert wird, versuchen Sie es mit Jinja2 von Python, das in Ansible usw. verwendet wird, wenn Sie eine Art Template-Engine möchten.
Ich habe es möglich gemacht, das Konvertierungsergebnis aus der Vorlagendatei und der YAML-Datei der Konfigurationsdatei mit einem einfachen Python-Skript wie dem folgenden abzurufen.
j2.py
import sys
import yaml
from jinja2 import Template, Environment, FileSystemLoader
def _j2(templateFile, configFile):
env = Environment(loader=FileSystemLoader('.', encoding='utf_8'))
tpl = env.get_template(templateFile)
with open(configFile) as f:
conf = yaml.load(f)
ret = tpl.render(conf)
print(ret)
if __name__ == '__main__':
if (len(sys.argv) <= 2):
print("Usage: j2.pl <template file> <config file>")
sys.exit(-1)
_j2(sys.argv[1], sys.argv[2])
Die Kommandozeile sieht so aus.
$ python j2.pl template.conf.j2 param.yml > output.conf
Dies ist ziemlich problematisch, und in der Docker-Umgebung von Windows oder Mac scheint es, dass Sie mit host.docker.internal eine Verbindung vom Container zum Host herstellen können, Linux verfügt jedoch nicht über eine solche Methode.
Unter Linux scheint der Host / Container über eine Schnittstelle namens docker0 verbunden zu sein. Daher habe ich die IP-Adresse, die docker0 auf der Hostseite zugewiesen wurde, als Umgebungsvariable festgelegt und beim Start von Docker übergeben.
$ env DOCKER0_ADDRESS=$( ip route | awk '/docker0/ {print $9}' ) \
docker-compose up -d
docker-compose.yml
version: '3'
services:
alb:
image: nginx:stable
:
extra_hosts:
- "docker0:${DOCKER0_ADDRESS}"
Wenn Sie die Zuordnung in extra_hosts in docker-compose.yml schreiben, wird die Zuordnung von Hostname und IP-Adresse zu / etc / hosts im Container hinzugefügt, wenn der Container gestartet wird, sodass Sie anscheinend in den Nginx-Einstellungen auf den Namen verweisen können.
/etc/hosts
----
172.17.0.1 docker0
Um einen Load Balancer mit nginx einzurichten, definieren Sie eine Gruppe von Zielen in http / upstream und geben Sie den Upstream-Namen in proxy_pass in http / server / location an. .. Ich habe es erwartet, aber es beginnt nicht mit einem Fehler.
Anscheinend erlaubt die kostenlose Version von nginx keinen DNS-Resolver für Hostnamen, die in Upstream geschrieben wurden. (Ich habe zum ersten Mal erfahren, dass Nginx eine kostenpflichtige / kostenlose Version hat.)
Dies ist ein Artikel, der diese Angelegenheit zusammenfasst. Der folgende Qiita-Artikel zeigt, wie UNIX-Sockets verwendet werden. Ich habe mich für dieses entschieden, weil es die Einschränkungen erfüllt, keine Plug-Ins zu verwenden oder Docker-Images zu erstellen. (Obwohl die Einstellungsdatei lang wird)
Zusammenfassung der Nginx-Namensauflösung https://ktrysmt.github.io/blog/name-specification-of-nginx/ Dynamische DNS-Auflösung im Nginx-Upstream-Kontext ohne kostenpflichtige Auflösungsoption https://qiita.com/minamijoyo/items/183e51a28a3a9d79182f
nginx/conf.d/default.conf
upstream tg-app01 {
server unix:/var/run/nginx_tg-app01_1; # (2-1) tg-Erstes Ziel von app01
server unix:/var/run/nginx_tg-app01_2; # (2-2) tg-Zweites Ziel für app01
}
server {
listen 80;
server_name app01.example.com;
:
location / {
proxy_pass http://tg-app01; # (1) upstream tg-Siehe App01
}
}
server {
listen unix:/var/run/nginx_tg-app01_1; # (2-1)Referenz für das erste Ziel
server_name app01.example.com;
:
location / {
proxy_pass http://docker0:21080;
}
}
server {
listen unix:/var/run/nginx_tg-app01_2; # (2-2)Referenz für das zweite Ziel
server_name app01.example.com;
:
location / {
proxy_pass http://docker0:22080;
}
}
Nehmen Sie die folgenden Einstellungen vor, da diesmal Websocket durchlaufen werden musste. Scheint für jeden Serverblock benötigt zu werden.
nginx/conf.d/default.conf
#Mit diesem
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
:
server {
listen 80;
server_name app02.example.com;
#von hier
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
#Bisher
location / {
proxy_pass http://tg-app02;
}
}
Basierend auf dem bisherigen Inhalt lautet die Vorlagendefinition wie folgt. Es ist in Ordnung, aber + α enthält auch die Standardmusterspezifikation und die feste Antworteinstellung.
src/default.conf.j2
## http listener settings
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
{% for rule in http.rules %}
{%- if rule.then.forward %}
upstream {{ rule.then.forward.name }} {
{%- if rule.then.forward.stickiness %}
ip_hash;
{%- endif %}
{%- for tg in rule.then.forward.targets %}
server {{ 'unix:/var/run/nginx_%s_%d' % (rule.then.forward.name, loop.index) }}{{ ' weight=%d' % tg.weight if tg.weight else '' }};
{%- endfor %}
}
{%- endif %}
server {
listen {{ http.listen }}{{ ' default_server' if rule.if.default_server }};
server_name {{ rule.if.host }};
{% if rule.then.forward %}
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
{%- for path in rule.if.pathes %}
location {{ path }} {
{%- if rule.if.headers %}
{%- for header in rule.if.headers %}
if ($http_{{ header|replace('-','_')|lower() }} = "{{ rule.if.headers[header] }}") {
proxy_pass http://{{ rule.then.forward.name }};
break;
}
{%- endfor %}
{%- else %}
proxy_pass http://{{ rule.then.forward.name }};
{%- endif %}
}
{%- endfor %}
{%- if rule.then.response %}
location / {
{%- if rule.then.response.content_type %}
default_type {{ rule.then.response.content_type }};
{%- endif %}
return {{ rule.then.response.code }}{{ ' \'%s\'' % rule.then.response.message if rule.then.response.message }};
}
{%- endif %}
}
{%- if rule.then.forward %}
{%- for tg in rule.then.forward.targets %}
server {
listen {{ 'unix:/var/run/nginx_%s_%d' % (rule.then.forward.name, loop.index) }};
server_name {{ rule.if.host }};
{% if rule.then.forward %}
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
location / {
proxy_pass {{ tg.target }};
}
}
{%- endfor %}
{%- endif %}
{% endfor %}
Die Docker-Compose-Einstellungen sind wie folgt. Hängen Sie den Ordner mit den Nginx-Einstellungen auf ein externes Volume, damit die Einstellungen wirksam werden.
docker-compose.yml
version: '3'
services:
alb:
image: nginx:stable
ports:
- "80:80"
volumes:
- ./conf.d:/etc/nginx/conf.d
extra_hosts:
- "docker0:${DOCKER0_ADDRESS}"
restart: always
Die Funktionsprüfung ist wie folgt.
$ make build #Konvertierung der Einstellungsdatei
$ make up #Start des Nginx-Containers
$ curl http://localhost:80 -i -H "Host:app01.example.com" #Zugriff durch Festlegen des Hostnamens
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sat, 29 Aug 2020 17:26:23 GMT
Content-Type: text/html
Content-Length: 1863
Connection: keep-alive
Last-Modified: Wed, 11 Mar 2020 05:22:13 GMT
ETag: "747-5a08d6b34ab40"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
:
Ich denke, es wird einfacher zu verwalten sein, da Sie keine AWS ALB-ähnlichen Komponenten in die YAML-Definition schreiben und komplizierte Nginx-Einstellungen schreiben müssen. Sie müssen keine Plugins erstellen oder installieren, sodass Sie sich leicht wie ALB fühlen können, solange Sie das Nginx-Image von DockerHub erhalten.
Ich habe auch gelernt, wie man Nginx einrichtet. (Ich habe auch etwas über die kostenlose / kostenpflichtige Version gelernt.)
Wenn Sie weiter nachforschen und Ihr Bestes geben, können Sie möglicherweise das Einstellungsmuster der ALB-Kopffamilie erkennen. (Dieses Mal wollte ich es so einfach wie möglich machen, also habe ich es nicht gemacht)
Recommended Posts