Added support for swarm mode.

This commit is contained in:
Greyscale 2022-05-05 16:42:53 +02:00
parent 2655c76fe6
commit 428f477ba3
No known key found for this signature in database
GPG key ID: EF017054C3E9CD59
5 changed files with 172 additions and 50 deletions

View file

@ -1,4 +1,4 @@
FROM benzine/php:cli-8.0
FROM benzine/php:cli-8.0 as bouncer
LABEL maintainer="Matthew Baggett <matthew@baggett.me>" \
org.label-schema.vcs-url="https://github.com/benzine-framework/docker" \
org.opencontainers.image.source="https://github.com/benzine-framework/docker"
@ -47,4 +47,11 @@ COPY bouncer /app
COPY composer.* /app/
RUN composer install && \
chmod +x /app/bouncer && \
mkdir -p /var/log/bouncer
mkdir -p /var/log/bouncer
FROM benzine/php:nginx-8.0 as test-app-a
COPY ./test/public-web-a /app/public
FROM benzine/php:nginx-8.0 as test-app-b
COPY ./test/public-web-b /app/public

37
bouncer/Makefile Normal file
View file

@ -0,0 +1,37 @@
test-as-service: clean
docker build -t bouncer --target bouncer .
docker build -t test-app-a --target test-app-a .
docker build -t test-app-b --target test-app-b .
-docker network create --driver overlay bouncer-test
$(MAKE) start_bouncer
$(MAKE) start_test_a
$(MAKE) start_test_b
docker service logs -f bouncer
start_test_a:
docker service create \
--network bouncer-test \
--name test-app-a \
--env BOUNCER_DOMAIN=test-a.local \
--env BOUNCER_ALLOW_NON_SSL=yes \
--publish 8081:80 \
test-app-a
start_test_b:
docker service create \
--network bouncer-test \
--name test-app-b \
--env BOUNCER_DOMAIN=test-b.local \
--env BOUNCER_ALLOW_NON_SSL=yes \
--publish 8082:80 \
test-app-b
start_bouncer:
docker service create \
--network bouncer-test \
--name bouncer \
--publish 8080:80 \
--mount type=bind,destination=/var/run/docker.sock,source=/var/run/docker.sock \
bouncer
clean:
-docker service rm bouncer test-app-a test-app-b
#-docker network rm bouncer-test
#-docker image rm test-app-a test-app-b bouncer

View file

@ -162,6 +162,25 @@ class Bouncer
private Logger $logger;
private string $instanceStateHash = '';
private array $fileHashes;
private bool $swarmMode = false;
/**
* @return bool
*/
public function isSwarmMode(): bool
{
return $this->swarmMode;
}
/**
* @param bool $swarmMode
* @return Bouncer
*/
public function setSwarmMode(bool $swarmMode): Bouncer
{
$this->swarmMode = $swarmMode;
return $this;
}
public function __construct()
{
@ -220,9 +239,10 @@ class Bouncer
*
* @return BouncerTarget[]
*/
public function findContainers(): array
public function findContainersContainerMode(): array
{
$bouncerTargets = [];
$containers = json_decode($this->client->request('GET', 'containers/json')->getBody()->getContents(), true);
foreach ($containers as $container) {
$envs = [];
@ -241,31 +261,7 @@ class Bouncer
$bouncerTarget = (new BouncerTarget())
->setId($inspect['Id'])
;
foreach ($envs as $eKey => $eVal) {
switch ($eKey) {
case 'BOUNCER_DOMAIN':
$domains = explode(',', $eVal);
array_walk($domains, function (&$domain, $key): void { $domain = trim($domain); });
$bouncerTarget->setDomains($domains);
break;
case 'BOUNCER_LETSENCRYPT':
$bouncerTarget->setLetsEncrypt(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
case 'BOUNCER_TARGET_PORT':
$bouncerTarget->setPort($eVal);
break;
case 'BOUNCER_ALLOW_NON_SSL':
$bouncerTarget->setAllowNonSSL(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
}
}
$bouncerTarget = $this->parseContainerEnvironmentVariables($envs, $bouncerTarget);
if (isset($inspect['NetworkSettings']['IPAddress']) && !empty($inspect['NetworkSettings']['IPAddress'])) {
// As per docker service
@ -282,6 +278,38 @@ class Bouncer
$bouncerTargets[] = $bouncerTarget;
}
}
return $bouncerTargets;
}
public function findContainersSwarmMode(): array
{
$bouncerTargets = [];
$services = json_decode($this->client->request('GET', 'services')->getBody()->getContents(), true);
if(isset($services['message'])){
$this->logger->debug(sprintf('Something happened while interrogating services.. This node is not a swarm node, cannot have services: %s', $services['message']));
}else{
foreach($services as $service){
$envs = [];
foreach($service['Spec']['TaskTemplate']['ContainerSpec']['Env'] as $env){
list($eKey, $eVal) = explode("=", $env,2);
$envs[$eKey] = $eVal;
}
if(isset($envs['BOUNCER_DOMAIN'])) {
$bouncerTarget = (new BouncerTarget())
->setId($service['ID']);
$bouncerTarget = $this->parseContainerEnvironmentVariables($envs, $bouncerTarget);
$bouncerTarget->setIp("172.17.0.1");
$bouncerTarget->setPort($service['Endpoint']['Ports'][0]['PublishedPort']);
$bouncerTarget->setTargetPath(sprintf('http://%s:%d/', $bouncerTarget->getIp(), $bouncerTarget->getPort()));
$this->logger->debug(sprintf('Decided that %s has the target path %s', $bouncerTarget->getName(), $bouncerTarget->getTargetPath()));
$bouncerTargets[] = $bouncerTarget;
}
}
}
return $bouncerTargets;
}
@ -335,7 +363,11 @@ class Bouncer
if ($this->s3Enabled()) {
$this->getCertificatesFromS3();
}
$targets = $this->findContainers();
$determineSwarmMode = json_decode($this->client->request('GET', 'swarm')->getBody()->getContents(), true);
$this->setSwarmMode(!isset($determineSwarmMode['message']));
$this->logger->info(sprintf("Swarm mode is %s.", $this->isSwarmMode() ? 'enabled' : 'disabled'));
$targets = $this->isSwarmMode() ? $this->findContainersSwarmMode() : $this->findContainersContainerMode();
$this->logger->info(sprintf('%s Found %d services with BOUNCER_DOMAIN set', Emoji::CHARACTER_MAGNIFYING_GLASS_TILTED_LEFT, count($targets)));
foreach ($targets as $target) {
$this->generateNginxConfig($target);
@ -494,6 +526,44 @@ class Bouncer
$this->logger->info(sprintf('%s Restarting nginx', Emoji::CHARACTER_TIMER_CLOCK));
$shell->run($command);
}
/**
* @param array $envs
* @param BouncerTarget $bouncerTarget
* @return BouncerTarget
*/
public function parseContainerEnvironmentVariables(array $envs, BouncerTarget $bouncerTarget): BouncerTarget
{
foreach ($envs as $eKey => $eVal) {
switch ($eKey) {
case 'BOUNCER_DOMAIN':
$domains = explode(',', $eVal);
array_walk($domains, function (&$domain, $key): void {
$domain = trim($domain);
});
$bouncerTarget->setDomains($domains);
break;
case 'BOUNCER_LETSENCRYPT':
$bouncerTarget->setLetsEncrypt(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
case 'BOUNCER_TARGET_PORT':
$bouncerTarget->setPort($eVal);
break;
case 'BOUNCER_ALLOW_NON_SSL':
$bouncerTarget->setAllowNonSSL(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
}
}
return $bouncerTarget;
}
}
(new Bouncer())->run();

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
echo "Starting Bouncer"
/app/bouncer
sleep 60;
sleep 30;

View file

@ -2,36 +2,44 @@ version: "3.4"
services:
bouncer:
image: benzine/bouncer
build: .
image: bouncer
build:
context: .
target: bouncer
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./:/app
environment:
- BOUNCER_LETSENCRYPT_MODE=staging
- BOUNCER_LETSENCRYPT_EMAIL=matthew@baggett.me
- BOUNCER_S3_ENDPOINT=http://grey.ooo:9000
- BOUNCER_S3_KEY_ID=geusebio
- BOUNCER_S3_KEY_SECRET=changeme
- BOUNCER_S3_BUCKET=bouncer-certificates
- BOUNCER_S3_USE_PATH_STYLE_ENDPOINT="yes"
# environment:
# - BOUNCER_LETSENCRYPT_MODE=staging
# - BOUNCER_LETSENCRYPT_EMAIL=matthew@baggett.me
# - BOUNCER_S3_ENDPOINT=http://grey.ooo:9000
# - BOUNCER_S3_KEY_ID=geusebio
# - BOUNCER_S3_KEY_SECRET=changeme
# - BOUNCER_S3_BUCKET=bouncer-certificates
# - BOUNCER_S3_USE_PATH_STYLE_ENDPOINT="yes"
ports:
- 127.0.99.100:80:80
- 127.0.99.100:443:443
web-a:
image: benzine/php:nginx
image: test-app-a
build:
context: .
target: test-app-a
volumes:
- ./test/public-web-a:/app/public
environment:
- BOUNCER_DOMAIN=a.web.grey.ooo
- BOUNCER_LETSENCRYPT=true
# - BOUNCER_LETSENCRYPT=true
web-b:
image: test-app-b
build:
context: .
target: test-app-b
volumes:
- ./test/public-web-b:/app/public
environment:
- BOUNCER_DOMAIN=b.web.grey.ooo
# - BOUNCER_LETSENCRYPT=true
# web-b:
# image: benzine/php:nginx
# volumes:
# - ./test/public-web-b:/app/public
# environment:
# - BOUNCER_DOMAIN=b.web.grey.ooo
#
#