mirror of
https://github.com/pockethost/pockethost.git
synced 2025-11-24 06:25:48 +00:00
enh: dynamic port allocation
This commit is contained in:
parent
4d96492353
commit
7f63c48008
@ -41,7 +41,6 @@
|
||||
"express-async-errors": "^3.1.1",
|
||||
"express-sslify": "^1.2.0",
|
||||
"ftp-srv": "github:pockethost/ftp-srv#0fc708bae0d5d7a55ce948767f082d6fcfb2af59",
|
||||
"get-port": "^6.1.2",
|
||||
"glob": "^10.3.10",
|
||||
"gobot": "1.0.0-alpha.41",
|
||||
"gobot-pocketbase": "0.22.8-alpha.22",
|
||||
|
||||
@ -19,7 +19,6 @@ import {
|
||||
mkContainerHomePath,
|
||||
mkDocUrl,
|
||||
mkInstanceUrl,
|
||||
mkInternalUrl,
|
||||
mkSingleton,
|
||||
now,
|
||||
stringify,
|
||||
@ -29,7 +28,6 @@ import {
|
||||
InstanceLogger,
|
||||
MothershipAdminClientService,
|
||||
PocketbaseService,
|
||||
PortService,
|
||||
SpawnConfig,
|
||||
proxyService,
|
||||
} from '../../services'
|
||||
@ -121,24 +119,10 @@ export const instanceService = mkSingleton(
|
||||
updateInstanceStatus(id, InstanceStatus.Idle)
|
||||
})
|
||||
|
||||
/** Obtain empty port */
|
||||
dbg(`Obtaining port`)
|
||||
const [newPortPromise, releasePort] = PortService().alloc()
|
||||
const newPort = await newPortPromise
|
||||
shutdownManager.push(() => {
|
||||
dbg(`shut down: releasing port`)
|
||||
releasePort()
|
||||
})
|
||||
systemInstanceLogger.breadcrumb({ port: newPort })
|
||||
dbg(`Found port`)
|
||||
const internalUrl = mkInternalUrl(newPort)
|
||||
dbg(`internalUrl`, internalUrl)
|
||||
|
||||
/** Create spawn config */
|
||||
const spawnArgs: SpawnConfig = {
|
||||
subdomain: instance.subdomain,
|
||||
instanceId: instance.id,
|
||||
port: newPort,
|
||||
dev: instance.dev,
|
||||
extraBinds: flatten([
|
||||
globSync(join(INSTANCE_APP_MIGRATIONS_DIR(), '*.js')).map(
|
||||
@ -178,7 +162,7 @@ export const instanceService = mkSingleton(
|
||||
/** Spawn the child process */
|
||||
const childProcess = await pbService.spawn(spawnArgs)
|
||||
|
||||
const { exitCode, stopped, started } = childProcess
|
||||
const { exitCode, stopped, started, url: internalUrl } = childProcess
|
||||
|
||||
shutdownManager.push(() => {
|
||||
dbg(`killing ${id}`)
|
||||
|
||||
@ -18,14 +18,12 @@ import {
|
||||
} from '../../../core'
|
||||
import { GobotService } from '../GobotService'
|
||||
import { InstanceLogger } from '../InstanceLoggerService'
|
||||
import { PortService } from '../PortService'
|
||||
|
||||
export type Env = { [_: string]: string }
|
||||
export type SpawnConfig = {
|
||||
subdomain: string
|
||||
instanceId: string
|
||||
version?: string
|
||||
port?: number
|
||||
extraBinds?: string[]
|
||||
env?: Env
|
||||
stdout?: MemoryStream
|
||||
@ -65,16 +63,9 @@ export const createPocketbaseService = async (
|
||||
const cm = createCleanupManager()
|
||||
const logger = LoggerService().create('spawn')
|
||||
const { dbg, warn, error } = logger
|
||||
const port =
|
||||
cfg.port ||
|
||||
(await (async () => {
|
||||
const [defaultPort, freeDefaultPort] = await PortService().alloc()
|
||||
cm.add(freeDefaultPort)
|
||||
return defaultPort
|
||||
})())
|
||||
|
||||
const _cfg: Required<SpawnConfig> = {
|
||||
version: maxVersion,
|
||||
port,
|
||||
extraBinds: [],
|
||||
env: {},
|
||||
stderr: new MemoryStream(),
|
||||
@ -119,6 +110,7 @@ export const createPocketbaseService = async (
|
||||
const container = await new Promise<{
|
||||
on: EventEmitter['on']
|
||||
kill: () => Promise<void>
|
||||
portBinding: number
|
||||
}>((resolve, reject) => {
|
||||
const docker = new Docker()
|
||||
iLogger.info(`Starting instance`)
|
||||
@ -153,7 +145,7 @@ export const createPocketbaseService = async (
|
||||
HostConfig: {
|
||||
AutoRemove: true,
|
||||
PortBindings: {
|
||||
'8090/tcp': [{ HostPort: `${port}` }],
|
||||
'8090/tcp': [{ HostPort: `0` }],
|
||||
},
|
||||
Binds,
|
||||
Ulimits: [
|
||||
@ -225,17 +217,44 @@ export const createPocketbaseService = async (
|
||||
}
|
||||
},
|
||||
)
|
||||
.on('start', (container: Container) => {
|
||||
.on('start', async (container: Container) => {
|
||||
dbg(`Got started container`, container)
|
||||
started = true
|
||||
resolve({
|
||||
on: emitter.on.bind(emitter),
|
||||
kill: () =>
|
||||
container.stop({ signal: `SIGINT` }).catch((e) => {
|
||||
error(e)
|
||||
return container.stop({ signal: `SIGKILL` }).catch(error)
|
||||
}),
|
||||
})
|
||||
|
||||
try {
|
||||
// Get container info to retrieve the assigned port
|
||||
const containerInfo = await container.inspect()
|
||||
const ports = containerInfo.NetworkSettings?.Ports?.['8090/tcp']
|
||||
|
||||
if (!ports || !ports[0] || !ports[0].HostPort) {
|
||||
throw new Error('Could not get port binding from container')
|
||||
}
|
||||
|
||||
const portBinding = parseInt(ports[0].HostPort, 10)
|
||||
if (isNaN(portBinding)) {
|
||||
throw new Error(`Invalid port binding: ${ports[0].HostPort}`)
|
||||
}
|
||||
|
||||
resolve({
|
||||
on: emitter.on.bind(emitter),
|
||||
kill: () =>
|
||||
container.stop({ signal: `SIGINT` }).catch((e) => {
|
||||
error(e)
|
||||
return container.stop({ signal: `SIGKILL` }).catch(error)
|
||||
}),
|
||||
portBinding,
|
||||
})
|
||||
} catch (e) {
|
||||
error(`Failed to get port binding: ${e}`)
|
||||
reject(e)
|
||||
try {
|
||||
await container.stop()
|
||||
} catch (stopError) {
|
||||
error(
|
||||
`Failed to stop container after port binding error: ${stopError}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}).catch((e) => {
|
||||
error(`Error starting container: ${e}`)
|
||||
@ -255,7 +274,8 @@ export const createPocketbaseService = async (
|
||||
dbg(`Instance exited with ${code}`)
|
||||
cm.shutdown().catch(error)
|
||||
})
|
||||
const url = mkInternalUrl(port)
|
||||
|
||||
const url = mkInternalUrl(container.portBinding)
|
||||
logger.breadcrumb({ url })
|
||||
dbg(`Making exit hook for ${url}`)
|
||||
const unsub = asyncExitHook(async () => {
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import getPort from 'get-port'
|
||||
import { INITIAL_PORT_POOL_SIZE } from '../../core'
|
||||
import { Allocator, ResourceAllocator, ioc } from '../common'
|
||||
|
||||
export type Config = { maxPorts: number }
|
||||
|
||||
export const PortService = () => {
|
||||
try {
|
||||
return ioc<Allocator<number>>('portAllocator')
|
||||
} catch (e) {
|
||||
return ioc(
|
||||
'portAllocator',
|
||||
ResourceAllocator(INITIAL_PORT_POOL_SIZE(), getPort),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,5 @@ export * from './InstanceLoggerService'
|
||||
export * from './InstanceService'
|
||||
export * from './MothershipAdminClientService'
|
||||
export * from './PocketBaseService'
|
||||
export * from './PortService'
|
||||
export * from './ProxyService'
|
||||
export * from './RealtimeLog'
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -370,9 +370,6 @@ importers:
|
||||
ftp-srv:
|
||||
specifier: github:pockethost/ftp-srv#0fc708bae0d5d7a55ce948767f082d6fcfb2af59
|
||||
version: https://codeload.github.com/pockethost/ftp-srv/tar.gz/0fc708bae0d5d7a55ce948767f082d6fcfb2af59
|
||||
get-port:
|
||||
specifier: ^6.1.2
|
||||
version: 6.1.2
|
||||
glob:
|
||||
specifier: ^10.3.10
|
||||
version: 10.4.5
|
||||
@ -2968,10 +2965,6 @@ packages:
|
||||
resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
get-port@6.1.2:
|
||||
resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
get-stream@2.3.1:
|
||||
resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -7778,8 +7771,6 @@ snapshots:
|
||||
|
||||
get-port@3.2.0: {}
|
||||
|
||||
get-port@6.1.2: {}
|
||||
|
||||
get-stream@2.3.1:
|
||||
dependencies:
|
||||
object-assign: 4.1.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user