1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-16 00:36:47 +01:00

feat: microservices be gone (#9551)

* feat: microservices be gone and api is a worker now too

* chore: remove very old startup scripts, surely nobody is using these anymore, right?

right?....
This commit is contained in:
Zack Pollard 2024-05-17 14:44:30 +01:00 committed by GitHub
parent ff52300624
commit 85aca2bb54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 206 additions and 190 deletions

View file

@ -4,32 +4,29 @@
name: immich-dev
x-server-build: &server-common
image: immich-server-dev:latest
build:
context: ../
dockerfile: server/Dockerfile
target: dev
restart: always
volumes:
- ../server:/usr/src/app
- ../open-api:/usr/src/open-api
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
- /usr/src/app/node_modules
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ulimits:
nofile:
soft: 1048576
hard: 1048576
services:
immich-server:
container_name: immich_server
command: ['/usr/src/app/bin/immich-dev', 'immich']
<<: *server-common
command: ['/usr/src/app/bin/immich-dev']
image: immich-server-dev:latest
build:
context: ../
dockerfile: server/Dockerfile
target: dev
restart: always
volumes:
- ../server:/usr/src/app
- ../open-api:/usr/src/open-api
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
- /usr/src/app/node_modules
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ulimits:
nofile:
soft: 1048576
hard: 1048576
ports:
- 3001:3001
- 9230:9230
@ -37,19 +34,6 @@ services:
- redis
- database
immich-microservices:
container_name: immich_microservices
command: ['/usr/src/app/bin/immich-dev', 'microservices']
<<: *server-common
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
ports:
- 9231:9230
depends_on:
- database
- immich-server
immich-web:
container_name: immich_web
image: immich-web-dev:latest

View file

@ -1,40 +1,24 @@
name: immich-prod
x-server-build: &server-common
image: immich-server:latest
build:
context: ../
dockerfile: server/Dockerfile
volumes:
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
restart: always
services:
immich-server:
container_name: immich_server
command: ['start.sh', 'immich']
<<: *server-common
image: immich-server:latest
build:
context: ../
dockerfile: server/Dockerfile
volumes:
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
restart: always
ports:
- 2283:3001
depends_on:
- redis
- database
immich-microservices:
container_name: immich_microservices
command: ['start.sh', 'microservices']
<<: *server-common
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
depends_on:
- redis
- database
- immich-server
immich-machine-learning:
container_name: immich_machine_learning
image: immich-machine-learning:latest

View file

@ -12,7 +12,6 @@ services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: ['start.sh', 'immich']
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
@ -25,23 +24,6 @@ services:
- database
restart: always
immich-microservices:
container_name: immich_microservices
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
# extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/hardware-transcoding
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
command: ['start.sh', 'microservices']
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
depends_on:
- redis
- database
restart: always
immich-machine-learning:
container_name: immich_machine_learning
# For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag.

View file

@ -2,38 +2,30 @@ version: '3.8'
name: immich-e2e
x-server-build: &server-common
image: immich-server:latest
build:
context: ../
dockerfile: server/Dockerfile
environment:
- DB_HOSTNAME=database
- DB_USERNAME=postgres
- DB_PASSWORD=postgres
- DB_DATABASE_NAME=immich
- IMMICH_MACHINE_LEARNING_ENABLED=false
- IMMICH_METRICS=true
volumes:
- upload:/usr/src/app/upload
- ./test-assets:/test-assets
depends_on:
- redis
- database
services:
immich-server:
container_name: immich-e2e-server
command: ['./start.sh', 'immich']
<<: *server-common
command: ['./start.sh']
image: immich-server:latest
build:
context: ../
dockerfile: server/Dockerfile
environment:
- DB_HOSTNAME=database
- DB_USERNAME=postgres
- DB_PASSWORD=postgres
- DB_DATABASE_NAME=immich
- IMMICH_MACHINE_LEARNING_ENABLED=false
- IMMICH_METRICS=true
volumes:
- upload:/usr/src/app/upload
- ./test-assets:/test-assets
depends_on:
- redis
- database
ports:
- 2283:3001
immich-microservices:
container_name: immich-e2e-microservices
command: ['./start.sh', 'microservices']
<<: *server-common
redis:
image: redis:6.2-alpine@sha256:84882e87b54734154586e5f8abd4dce69fe7311315e2fc6d67c29614c8de2672

View file

@ -61,3 +61,4 @@ ENV PATH="${PATH}:/usr/src/app/bin"
VOLUME /usr/src/app/upload
EXPOSE 3001
ENTRYPOINT ["tini", "--", "/bin/bash"]
CMD ["start.sh"]

View file

@ -1,67 +1,8 @@
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { json } from 'body-parser';
import cookieParser from 'cookie-parser';
import { CommandFactory } from 'nest-commander';
import { existsSync } from 'node:fs';
import { Worker } from 'node:worker_threads';
import sirv from 'sirv';
import { ApiModule, ImmichAdminModule } from 'src/app.module';
import { ImmichAdminModule } from 'src/app.module';
import { LogLevel } from 'src/config';
import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
import { ApiService } from 'src/services/api.service';
import { otelSDK } from 'src/utils/instrumentation';
import { useSwagger } from 'src/utils/misc';
const host = process.env.HOST;
async function bootstrapApi() {
otelSDK.start();
const port = Number(process.env.SERVER_PORT) || 3001;
const app = await NestFactory.create<NestExpressApplication>(ApiModule, { bufferLogs: true });
const logger = await app.resolve(ILoggerRepository);
logger.setAppName('ImmichServer');
logger.setContext('ImmichServer');
app.useLogger(logger);
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
app.set('etag', 'strong');
app.use(cookieParser());
app.use(json({ limit: '10mb' }));
if (isDev) {
app.enableCors();
}
app.useWebSocketAdapter(new WebSocketAdapter(app));
useSwagger(app, isDev);
app.setGlobalPrefix('api', { exclude: excludePaths });
if (existsSync(WEB_ROOT)) {
// copied from https://github.com/sveltejs/kit/blob/679b5989fe62e3964b9a73b712d7b41831aa1f07/packages/adapter-node/src/handler.js#L46
// provides serving of precompressed assets and caching of immutable assets
app.use(
sirv(WEB_ROOT, {
etag: true,
gzip: true,
brotli: true,
setHeaders: (res, pathname) => {
if (pathname.startsWith(`/_app/immutable`) && res.statusCode === 200) {
res.setHeader('cache-control', 'public,max-age=31536000,immutable');
}
},
}),
);
}
app.use(app.get(ApiService).ssr(excludePaths));
const server = await (host ? app.listen(port, host) : app.listen(port));
server.requestTimeout = 30 * 60 * 1000;
logger.log(`Immich Server is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
}
import { getWorkers } from 'src/utils/workers';
const immichApp = process.argv[2] || process.env.IMMICH_APP;
if (process.argv[2] === immichApp) {
@ -73,11 +14,12 @@ async function bootstrapImmichAdmin() {
await CommandFactory.run(ImmichAdminModule);
}
function bootstrapMicroservicesWorker() {
const worker = new Worker('./dist/workers/microservices.js');
function bootstrapWorker(name: string) {
console.log(`Starting ${name} worker`);
const worker = new Worker(`./dist/workers/${name}.js`);
worker.on('exit', (exitCode) => {
if (exitCode !== 0) {
console.error(`Microservices worker exited with code ${exitCode}`);
console.error(`${name} worker exited with code ${exitCode}`);
process.exit(exitCode);
}
});
@ -85,23 +27,22 @@ function bootstrapMicroservicesWorker() {
function bootstrap() {
switch (immichApp) {
case 'immich': {
process.title = 'immich_server';
if (process.env.INTERNAL_MICROSERVICES === 'true') {
bootstrapMicroservicesWorker();
}
return bootstrapApi();
}
case 'microservices': {
process.title = 'immich_microservices';
return bootstrapMicroservicesWorker();
}
case 'immich-admin': {
process.title = 'immich_admin_cli';
return bootstrapImmichAdmin();
}
case 'immich': {
process.title = 'immich_server';
return bootstrapWorker('api');
}
case 'microservices': {
process.title = 'immich_microservices';
return bootstrapWorker('microservices');
}
default: {
throw new Error(`Invalid app name: ${immichApp}. Expected one of immich|microservices|immich-admin`);
for (const worker of getWorkers()) {
bootstrapWorker(worker);
}
}
}
}

View file

@ -0,0 +1,49 @@
import { getWorkers } from 'src/utils/workers';
describe('getWorkers', () => {
beforeEach(() => {
process.env.IMMICH_WORKERS_INCLUDE = '';
process.env.IMMICH_WORKERS_EXCLUDE = '';
});
it('should return default workers', () => {
expect(getWorkers()).toEqual(['api', 'microservices']);
});
it('should return included workers', () => {
process.env.IMMICH_WORKERS_INCLUDE = 'api';
expect(getWorkers()).toEqual(['api']);
});
it('should excluded workers from defaults', () => {
process.env.IMMICH_WORKERS_EXCLUDE = 'api';
expect(getWorkers()).toEqual(['microservices']);
});
it('should exclude workers from include list', () => {
process.env.IMMICH_WORKERS_INCLUDE = 'api,microservices,randomservice';
process.env.IMMICH_WORKERS_EXCLUDE = 'randomservice,microservices';
expect(getWorkers()).toEqual(['api']);
});
it('should remove whitespace from included workers before parsing', () => {
process.env.IMMICH_WORKERS_INCLUDE = 'api, microservices';
expect(getWorkers()).toEqual(['api', 'microservices']);
});
it('should remove whitespace from excluded workers before parsing', () => {
process.env.IMMICH_WORKERS_EXCLUDE = 'api, microservices';
expect(getWorkers()).toEqual([]);
});
it('should remove whitespace from included and excluded workers before parsing', () => {
process.env.IMMICH_WORKERS_INCLUDE = 'api, microservices, randomservice,randomservice2';
process.env.IMMICH_WORKERS_EXCLUDE = 'randomservice,microservices, randomservice2';
expect(getWorkers()).toEqual(['api']);
});
it('should throw error for invalid workers', () => {
process.env.IMMICH_WORKERS_INCLUDE = 'api,microservices,randomservice';
expect(getWorkers).toThrowError('Invalid worker(s) found: api,microservices,randomservice');
});
});

View file

@ -0,0 +1,21 @@
const WORKER_TYPES = new Set(['api', 'microservices']);
export const getWorkers = () => {
let workers = ['api', 'microservices'];
const includedWorkers = process.env.IMMICH_WORKERS_INCLUDE?.replaceAll(/\s/g, '');
const excludedWorkers = process.env.IMMICH_WORKERS_EXCLUDE?.replaceAll(/\s/g, '');
if (includedWorkers) {
workers = includedWorkers.split(',');
}
if (excludedWorkers) {
workers = workers.filter((worker) => !excludedWorkers.split(',').includes(worker));
}
if (workers.some((worker) => !WORKER_TYPES.has(worker))) {
throw new Error(`Invalid worker(s) found: ${workers}`);
}
return workers;
};

68
server/src/workers/api.ts Normal file
View file

@ -0,0 +1,68 @@
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { json } from 'body-parser';
import cookieParser from 'cookie-parser';
import { existsSync } from 'node:fs';
import { isMainThread } from 'node:worker_threads';
import sirv from 'sirv';
import { ApiModule } from 'src/app.module';
import { envName, excludePaths, isDev, serverVersion, WEB_ROOT } from 'src/constants';
import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
import { ApiService } from 'src/services/api.service';
import { otelSDK } from 'src/utils/instrumentation';
import { useSwagger } from 'src/utils/misc';
const host = process.env.HOST;
async function bootstrap() {
otelSDK.start();
const port = Number(process.env.SERVER_PORT) || 3001;
const app = await NestFactory.create<NestExpressApplication>(ApiModule, { bufferLogs: true });
const logger = await app.resolve(ILoggerRepository);
logger.setAppName('ImmichServer');
logger.setContext('ImmichServer');
app.useLogger(logger);
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
app.set('etag', 'strong');
app.use(cookieParser());
app.use(json({ limit: '10mb' }));
if (isDev) {
app.enableCors();
}
app.useWebSocketAdapter(new WebSocketAdapter(app));
useSwagger(app, isDev);
app.setGlobalPrefix('api', { exclude: excludePaths });
if (existsSync(WEB_ROOT)) {
// copied from https://github.com/sveltejs/kit/blob/679b5989fe62e3964b9a73b712d7b41831aa1f07/packages/adapter-node/src/handler.js#L46
// provides serving of precompressed assets and caching of immutable assets
app.use(
sirv(WEB_ROOT, {
etag: true,
gzip: true,
brotli: true,
setHeaders: (res, pathname) => {
if (pathname.startsWith(`/_app/immutable`) && res.statusCode === 200) {
res.setHeader('cache-control', 'public,max-age=31536000,immutable');
}
},
}),
);
}
app.use(app.get(ApiService).ssr(excludePaths));
const server = await (host ? app.listen(port, host) : app.listen(port));
server.requestTimeout = 30 * 60 * 1000;
logger.log(`Immich Server is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `);
}
if (!isMainThread) {
bootstrap().catch((error) => {
console.error(error);
process.exit(1);
});
}

View file

@ -8,7 +8,7 @@ import { otelSDK } from 'src/utils/instrumentation';
const host = process.env.HOST;
export async function bootstrapMicroservices() {
export async function bootstrap() {
otelSDK.start();
const port = Number(process.env.MICROSERVICES_PORT) || 3002;
@ -25,7 +25,7 @@ export async function bootstrapMicroservices() {
}
if (!isMainThread) {
bootstrapMicroservices().catch((error) => {
bootstrap().catch((error) => {
console.error(error);
process.exit(1);
});

View file

@ -1,3 +0,0 @@
#!/usr/bin/env bash
./start.sh microservices

View file

@ -1,3 +0,0 @@
#!/usr/bin/env bash
./start.sh immich