feat: Databases on ARM

This commit is contained in:
Andras Bacsai 2022-08-12 11:48:38 +02:00
parent 5d5a478cd1
commit 8d3ca92fe9
6 changed files with 115 additions and 62 deletions

View File

@ -1,10 +1,11 @@
import { parentPort } from 'node:worker_threads';
import { prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, executeDockerCmd } from '../lib/common';
import { prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, executeDockerCmd, listSettings } from '../lib/common';
import { checkContainer } from '../lib/docker';
(async () => {
if (parentPort) {
try {
const { arch } = await listSettings();
// Coolify Proxy local
const engine = '/var/run/docker.sock';
const localDocker = await prisma.destinationDocker.findFirst({
@ -30,7 +31,7 @@ import { checkContainer } from '../lib/docker';
for (const database of databasesWithPublicPort) {
const { destinationDockerId, destinationDocker, publicPort, id } = database;
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
const { privatePort } = generateDatabaseConfiguration(database);
const { privatePort } = generateDatabaseConfiguration(database, arch);
// Remove HAProxy
const found = await checkContainer({
dockerId: localDocker.id, container: `haproxy-for-${publicPort}`

View File

@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs';
import * as serviceFields from './serviceFields'
export const version = '3.2.3';
export const version = '3.3.0';
export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr';
@ -462,26 +462,46 @@ export const supportedDatabaseTypesAndVersions = [
baseImage: 'bitnami/mongodb',
versions: ['5.0', '4.4', '4.2']
},
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
{
name: 'mysql',
fancyName: 'MySQL',
baseImage: 'bitnami/mysql',
baseImageARM: 'mysql',
versions: ['8.0', '5.7'],
versionsARM: ['8.0', '5.7']
},
{
name: 'mariadb',
fancyName: 'MariaDB',
baseImage: 'bitnami/mariadb',
versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
baseImageARM: 'mariadb',
versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'],
versionsARM: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
},
{
name: 'postgresql',
fancyName: 'PostgreSQL',
baseImage: 'bitnami/postgresql',
versions: ['14.4.0', '13.6.0', '12.10.0', '11.15.0', '10.20.0']
baseImageARM: 'postgres',
versions: ['14.5.0', '13.8.0', '12.12.0', '11.17.0', '10.22.0'],
versionsARM: ['14.5', '13.8', '12.12', '11.17', '10.22']
},
{
name: 'redis',
fancyName: 'Redis',
baseImage: 'bitnami/redis',
versions: ['7.0', '6.2', '6.0', '5.0']
baseImageARM: 'redis',
versions: ['7.0', '6.2', '6.0', '5.0'],
versionsARM: ['7.0', '6.2', '6.0', '5.0']
},
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.2'] }
{
name: 'couchdb',
fancyName: 'CouchDB',
baseImage: 'bitnami/couchdb',
baseImageARM: 'couchdb',
versions: ['3.2.2', '3.1.2', '2.3.1'],
versionsARM: ['3.2.2', '3.1.2', '2.3.1']
}
];
export async function getFreeSSHLocalPort(id: string): Promise<number> {
@ -674,10 +694,11 @@ export function generatePassword(length = 24, symbols = false): string {
});
}
export function generateDatabaseConfiguration(database: any):
export function generateDatabaseConfiguration(database: any, arch: string):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -691,6 +712,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -701,6 +723,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -714,6 +737,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -726,6 +750,19 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
POSTGRES_USER: string;
POSTGRES_PASSWORD: string;
POSTGRES_DB: string;
};
}
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -736,6 +773,7 @@ export function generateDatabaseConfiguration(database: any):
| {
volume: string;
image: string;
command?: string;
ulimits: Record<string, unknown>;
privatePort: number;
environmentVariables: {
@ -754,9 +792,9 @@ export function generateDatabaseConfiguration(database: any):
type,
settings: { appendOnly }
} = database;
const baseImage = getDatabaseImage(type);
const baseImage = getDatabaseImage(type, arch);
if (type === 'mysql') {
return {
const configuration = {
privatePort: 3306,
environmentVariables: {
MYSQL_USER: dbUser,
@ -768,9 +806,13 @@ export function generateDatabaseConfiguration(database: any):
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/mysql/data`,
ulimits: {}
};
}
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
}
return configuration
} else if (type === 'mariadb') {
return {
const configuration = {
privatePort: 3306,
environmentVariables: {
MARIADB_ROOT_USER: rootUser,
@ -783,6 +825,10 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/mariadb`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
}
return configuration
} else if (type === 'mongodb') {
return {
privatePort: 27017,
@ -795,7 +841,7 @@ export function generateDatabaseConfiguration(database: any):
ulimits: {}
};
} else if (type === 'postgresql') {
return {
const configuration = {
privatePort: 5432,
environmentVariables: {
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
@ -806,10 +852,15 @@ export function generateDatabaseConfiguration(database: any):
image: `${baseImage}:${version}`,
volume: `${id}-${type}-data:/bitnami/postgresql`,
ulimits: {}
};
}
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
}
return configuration
} else if (type === 'redis') {
return {
const configuration = {
privatePort: 6379,
command: undefined,
environmentVariables: {
REDIS_PASSWORD: dbUserPassword,
REDIS_AOF_ENABLED: appendOnly ? 'yes' : 'no'
@ -818,8 +869,13 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/redis/data`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/data`;
configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no'} --requirepass ${dbUserPassword}`;
}
return configuration
} else if (type === 'couchdb') {
return {
const configuration = {
privatePort: 5984,
environmentVariables: {
COUCHDB_PASSWORD: dbUserPassword,
@ -829,20 +885,35 @@ export function generateDatabaseConfiguration(database: any):
volume: `${id}-${type}-data:/bitnami/couchdb`,
ulimits: {}
};
if (!isARM(arch)) {
configuration.volume = `${id}-${type}-data:/opt/couchdb/data`;
}
return configuration
}
}
export function getDatabaseImage(type: string): string {
export function isARM(arch) {
if (arch === 'arm' || arch === 'arm64') {
return true
}
return false
}
export function getDatabaseImage(type: string, arch: string): string {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
if (!isARM(arch)) {
return found.baseImageARM || found.baseImage
}
return found.baseImage;
}
return '';
}
export function getDatabaseVersions(type: string): string[] {
export function getDatabaseVersions(type: string, arch: string): string[] {
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
if (found) {
if (!isARM(arch)) {
return found.versionsARM || found.versions
}
return found.versions;
}
return [];

View File

@ -3,7 +3,7 @@ import type { FastifyRequest } from 'fastify';
import { FastifyReply } from 'fastify';
import yaml from 'js-yaml';
import fs from 'fs/promises';
import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, isARM, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { checkContainer } from '../../../../lib/docker';
import { day } from '../../../../lib/dayjs';
@ -93,14 +93,15 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
if (!database) {
throw { status: 404, message: 'Database not found.' }
}
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const configuration = generateDatabaseConfiguration(database);
const configuration = generateDatabaseConfiguration(database, arch);
const settings = await listSettings();
return {
privatePort: configuration?.privatePort,
database,
versions: await getDatabaseVersions(database.type),
versions: await getDatabaseVersions(database.type, arch),
settings
};
} catch ({ status, message }) {
@ -137,8 +138,10 @@ export async function getVersions(request: FastifyRequest<OnlyId>) {
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
const versions = getDatabaseVersions(type, arch);
return {
versions: supportedDatabaseTypesAndVersions.find((name) => name.name === type).versions
versions
}
} catch ({ status, message }) {
return errorHandler({ status, message })
@ -219,6 +222,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const {
@ -228,8 +232,8 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
publicPort,
settings: { isPublic }
} = database;
const { privatePort, environmentVariables, image, volume, ulimits } =
generateDatabaseConfiguration(database);
const { privatePort, command, environmentVariables, image, volume, ulimits } =
generateDatabaseConfiguration(database, arch);
const network = destinationDockerId && destinationDocker.network;
const volumeName = volume.split(':')[0];
@ -243,6 +247,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
[id]: {
container_name: id,
image,
command,
networks: [network],
environment: environmentVariables,
volumes: [volume],
@ -270,6 +275,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
}
}
};
const composeFileDestination = `${workdir}/docker-compose.yaml`;
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
try {
@ -282,6 +288,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
return {};
} catch (error) {
console.log(error)
throw {
error
};
@ -440,11 +447,12 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
const { arch } = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
const { privatePort } = generateDatabaseConfiguration(database);
const { privatePort } = generateDatabaseConfiguration(database, arch);
if (destinationDockerId) {
if (isPublic) {

View File

@ -232,35 +232,6 @@ export function changeQueryParams(buildId: string) {
return history.pushState(null, null, '?' + queryParams.toString());
}
export const supportedDatabaseTypesAndVersions = [
{
name: 'mongodb',
fancyName: 'MongoDB',
baseImage: 'bitnami/mongodb',
versions: ['5.0', '4.4', '4.2']
},
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
{
name: 'mariadb',
fancyName: 'MariaDB',
baseImage: 'bitnami/mariadb',
versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
},
{
name: 'postgresql',
fancyName: 'PostgreSQL',
baseImage: 'bitnami/postgresql',
versions: ['14.2.0', '13.6.0', '12.10.0 ', '11.15.0', '10.20.0']
},
{
name: 'redis',
fancyName: 'Redis',
baseImage: 'bitnami/redis',
versions: ['6.2', '6.0', '5.0']
},
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
];
export const getServiceMainPort = (service: string) => {
const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
if (serviceType) {

View File

@ -58,7 +58,9 @@
}
async function changeSettings(name: any) {
if (publicLoading || !$status.database.isRunning) return;
if (name !== 'appendOnly') {
if (publicLoading || !$status.database.isRunning || name !== 'appendOnly') return;
}
publicLoading = true;
let data = {
isPublic,
@ -111,7 +113,7 @@
<button
type="submit"
class="btn btn-sm"
class:loading={loading}
class:loading
class:bg-databases={!loading}
disabled={loading}>{$t('forms.save')}</button
>

View File

@ -1,7 +1,7 @@
{
"name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.2.3",
"version": "3.3.0",
"license": "Apache-2.0",
"repository": "github:coollabsio/coolify",
"scripts": {