본문 바로가기
WIKI 기술 지식 베이스

백엔드 플러그인을 새 백엔드 시스템으로 마이그레이션하기

원문 보기 위키 갱신

기존 백엔드 플러그인을 새 백엔드 시스템으로 마이그레이션하는 것은 상당히 간단해요.

출처: 문서

본문

기존 백엔드 플러그인을 새 백엔드 시스템으로 마이그레이션하는 것은 상당히 간단해요. 이 과정은 백엔드의 index.ts 파일에서 배선되는 Router를 반환하는 대부분의 플러그인에서 유사해요. 우리가 해야 할 핵심은 플러그인이 필요로 하는 의존성이 사용 가능하도록 하고, 라우터를 HTTP 라우터 서비스에 등록하는 것이에요.

Kubernetes 백엔드 플러그인을 마이그레이션하는 예시를 살펴보아요. 기존(구) 시스템에서 kubernetes 백엔드는 다음과 같이 구성돼요:

// @backstage/plugin-kubernetes-backend/src/service/router.tsimport { KubernetesBuilder } from './KubernetesBuilder';export interface RouterOptions {  logger: Logger;  config: Config;  catalogApi: CatalogApi;  clusterSupplier?: KubernetesClustersSupplier;  discovery: PluginEndpointDiscovery;}export async function createRouter(  options: RouterOptions,): Promise<express.Router> {  const { router } = await KubernetesBuilder.createBuilder(options)    .setClusterSupplier(options.clusterSupplier)    .build();  return router;}

새 백엔드 시스템에서 KubernetesBuilder가 만든 router를 재사용할 수 있어요. 위 RouterOptions에 지정된 의존성이 사용 가능한지만 확인하면 돼요. 그것들은 모두 coreServices의 일부이므로 마이그레이션이 쉬워요.

import {  coreServices,  createBackendPlugin,} from '@backstage/backend-plugin-api';import { catalogServiceRef } from '@backstage/plugin-catalog-node';import { Router } from 'express';import { KubernetesBuilder } from './KubernetesBuilder';export const kubernetesPlugin = createBackendPlugin({  pluginId: 'kubernetes',  register(env) {    env.registerInit({      deps: {        logger: coreServices.logger,        config: coreServices.rootConfig,        catalogApi: catalogServiceRef,        discovery: coreServices.discovery,        // The http router service is used to register the router created by the KubernetesBuilder.        http: coreServices.httpRouter,      },      async init({ config, logger, catalogApi, discovery, http }) {        const { router } = await KubernetesBuilder.createBuilder({          config,          logger,          catalogApi,          discovery,        }).build();        // We register the router with the http service.        http.use(router);      },    });  },});

마지막으로 src/index.ts에서 플러그인 인스턴스를 패키지의 기본 내보내기로 다시 내보내는지 확인하세요:

export { kubernetesPlugin as default } from './plugin.ts';

완료! 이 플러그인의 사용자는 이제 플러그인 패키지를 가져와 백엔드에 등록할 수 있어요:

// packages/backend/src/index.tsbackend.add(import('@backstage/plugin-kubernetes-backend'));

날카로운 독자들이 알아차렸을 한 가지가 빠져 있어요: clusterSupplier 옵션이 원래 플러그인에서 빠져 있어요. 그것을 추가하고 대안을 논의해 보아요.

한 가지 대안은 정적 구성을 사용해 클러스터 공급자를 구축할 수 있게 하는 것이에요. 예를 들어 선택할 수 있는 내장 구현 모음이 있거나, ClusterSupplier가 어떻게 작동해야 하는지의 로직이 전부 구성으로 결정되거나, 둘의 조합일 수 있어요. 사용자 지정에는 항상 가능할 때마다 정적 구성을 사용하는 것이 선호돼요. 이 경우 예를 들어 클러스터 공급자를 다음과 같이 구성할 수 있다고 상상할 수 있어요:

/* omitted imports but they remain the same as above */const kubernetesPlugin = createBackendPlugin({  pluginId: 'kubernetes',  register(env) {    env.registerInit({      deps: {        /* omitted dependencies but they remain the same as above */      },      async init({ config, logger, catalogApi, discovery, http }) {        // Note that in a real implementation this would be done by the `KubernetesBuilder` instead,        // but here we've extracted it into a separate call to highlight the example.        const configuredClusterSupplier = readClusterSupplierFromConfig(config);        const { router } = await KubernetesBuilder.createBuilder({          config,          logger,          catalogApi,          discovery,        })          .setClusterSupplier(configuredClusterSupplier)          .build();        http.use(router);      },    });  },});

그러나 정적 구성으로는 할 수 없는 많은 종류의 사용자 지정이 있어요. 이 경우 통합자가 ClusterSupplier 인터페이스의 임의 구현을 만들 수 있게 하고 싶은데, 이는 결국 코드를 통한 구현을 요구해요. 이때 새 백엔드 시스템의 확장 지점이 유용하게 쓰여요.

새 확장 지점 API는 모듈이 백엔드 플러그인 자체에 기능을 추가할 수 있게 해요. 이 경우 추가 ClusterSupplier를 추가할 수 있어요. 확장 지점을 사용해 커스텀 공급자 설치 지원을 어떻게 추가할 수 있는지 살펴보아요. 이렇게 하면 통합자가 커스텀 ClusterSupplier 구현을 가진 자체 내부 모듈을 만들 수 있어요.

먼저 확장 지점을 정의할 수 있는 @backstage/plugin-kubernetes-node 패키지를 만들겠어요. 별도 패키지는 플러그인 패키지 자체에 직접 의존하지 않기 위해 사용돼요. 새 패키지를 만든 뒤 확장 지점을 다음과 같이 정의해요:

import { createExtensionPoint } from '@backstage/backend-plugin-api';export interface KubernetesClusterSupplierExtensionPoint {  setClusterSupplier(supplier: KubernetesClustersSupplier): void;}/** * An extension point that allows other plugins to set the cluster supplier. */export const kubernetesClustersSupplierExtensionPoint =  createExtensionPoint<KubernetesClusterSupplierExtensionPoint>({    id: 'kubernetes.cluster-supplier',  });

확장 지점을 설계하는 방법에 대한 자세한 내용은 확장 지점 문서를 참고하세요.

다음으로 Kubernetes 백엔드 플러그인 자체에 이 확장 지점 지원을 추가해야 해요:

/* omitted other imports but they remain the same as above */import { kubernetesClustersSupplierExtensionPoint } from '@backstage/plugin-kubernetes-node';export const kubernetesPlugin = createBackendPlugin({  pluginId: 'kubernetes',  register(env) {    let clusterSupplier: KubernetesClustersSupplier | undefined = undefined;    // We register the extension point with the backend, which allows modules to    // register their own ClusterSupplier.    env.registerExtensionPoint(kubernetesClustersSupplierExtensionPoint, {      setClusterSupplier(supplier) {        if (clusterSupplier) {          throw new Error('ClusterSupplier may only be set once');        }        clusterSupplier = supplier;      },    });    env.registerInit({      deps: {        /* omitted dependencies but they remain the same as above */      },      async init({ config, logger, catalogApi, discovery, http }) {        const { router } = await KubernetesBuilder.createBuilder({          config,          logger,          catalogApi,          discovery,        })          .setClusterSupplier(clusterSupplier)          .build();        http.use(router);      },    });  },});

그리고 그게 전부예요! 이제 kubernetes 백엔드 플러그인에 클러스터를 추가하는 모듈을 만들 수 있어요. 다음은 GoogleContainerEngineSupplier를 kubernetes 백엔드에 추가하는 모듈의 예시예요:

import { kubernetesClustersSupplierExtensionPoint } from '@backstage/plugin-kubernetes-node';// This is a custom implementation of the ClusterSupplier interface.import { GoogleContainerEngineSupplier } from './GoogleContainerEngineSupplier';export default createBackendModule({  pluginId: 'kubernetes',  moduleId: 'gke-supplier',  register(env) {    env.registerInit({      deps: {        supplier: kubernetesClustersSupplierExtensionPoint,      },      async init({ supplier }) {        supplier.setClusterSupplier(new GoogleContainerEngineSupplier());      },    });  },});

위 모듈은 통합자가 kubernetes 백엔드 플러그인과 함께 설치할 수 있어요:

backend.add(import('@backstage/plugin-kubernetes-backend'));backend.add(import('@internal/gke-cluster-supplier'));

개발 서버

로컬 개발 서버에서 마이그레이션한 플러그인을 실행하려면 아래 단계를 따르세요:

  • 먼저, 존재한다면 src/run.ts와 src/service/standaloneServer.ts 파일을 삭제하세요. (backstage-cli는 이전에 이 파일들을 사용해 레거시 백엔드 플러그인을 로컬에서 실행했지만, 더 이상 필요하지 않아요.)

  • 다음으로 dev/index.ts 파일에 새 개발 백엔드를 만드세요. 개발 서버는 주로 플러그인을 로컬에서 실행하는 데 사용되는 백엔드 앱의 라이트 버전이므로, 간단한 kubernetes 백엔드 로컬 개발 서버는 다음과 같을 거예요:

in dev/index.js

// This package should be installed as a `dev` dependencyimport { createBackend } from '@backstage/backend-defaults';const backend = createBackend();// Path to the file where the plugin is export as defaultbackend.add(import('../src'));backend.start();

위에서 만든 개발 서버는 기본 의존성 팩토리로 자동 구성되지만, 플러그인이 의존하는 rootConfig 서비스 같은 일부 서비스를 mock해야 한다면 mockServices 팩토리 중 하나를 사용할 수 있어요:

in dev/index.js

//...// This package should be installed as `devDependencies`import { mockServices } from '@backstage/backend-test-utils';const backend = createBackend();// ...backend.add(  mockServices.rootConfig.factory({    data: {      // your config mocked values goes here    },  }),);// ...

하나 이상의 서비스에 대한 자체 커스텀 mock 팩토리를 만들고 싶다면 커스텀 서비스 구현 문서와 핵심 서비스 구성 페이지를 확인하세요.

  • 이제 마지막으로 플러그인 루트 폴더에서 yarn start를 실행해 플러그인을 로컬에서 시작할 수 있어요.

구 백엔드 시스템 지원 제거

위 가이드를 따라 새 백엔드 플러그인을 내보냈다면 구 백엔드 플러그인을 폐기하고 제거하는 단계는 다음과 같아요:

기본 내보내기 외의 공용 내보내기 폐기

먼저 createRouter와 routerOptions가 폐기(deprecated)로 표시되어 사용자가 새 시스템으로 마이그레이션할 시간과 신호를 얻도록 하세요(한 릴리스에서 폐기하고 다음 릴리스에서 폐기 표시를 제거하는 것을 권장해요). 이는 레거시 내보내기에 @deprecated 주석을 추가해 수행돼요. 플러그인이 내부적으로 createRouter를 계속 사용할 수는 있지만 공용 API의 일부로 내보내서는 안 된다는 점에 주목할 만해요. 마이그레이션한 플러그인에서 create router와 상대 import를 재사용한다면, createRouter 내보내기가 삭제된 뒤 폐기된 import를 제거하도록 내부 코드를 리팩터링해야 해요. 마이그레이션한 플러그인에서 @backstage/backend-common과 @backstage/backend-tasks의 사용을 피하는 것이 권장되는데, 레거시 시스템 지원 종료와 함께 삭제될 것이기 때문이에요. 대부분의 폐기된 import에는 새 백엔드 시스템으로 마이그레이션한 뒤 사용을 중단하는 방법에 대한 지침이 있어요.

@backstage/plugin-kubernetes-backend/src/service/router.ts

import { KubernetesBuilder } from './KubernetesBuilder';/*** @public* @deprecated Please migrate to the new backend system.*/export interface RouterOptions {  logger: Logger;  config: Config;  catalogApi: CatalogApi;  clusterSupplier?: KubernetesClustersSupplier;  discovery: PluginEndpointDiscovery;}/*** @public* @deprecated Please migrate to the new backend system.*/export async function createRouter(  options: RouterOptions,): Promise<express.Router> {  const { router } = await KubernetesBuilder.createBuilder(options)    .setClusterSupplier(options.clusterSupplier)    .build();  return router;}

플러그인에 api-report.md 파일이 있다면 이후 yarn build:api-reports를 실행하세요. API 리포트를 검사해 새 백엔드 플러그인 외의 다른 내보내기를 찾는 것을 권장해요. 새 백엔드 시스템의 플러그인은 옵션을 전달하는 대신 확장 지점으로 확장되므로 대부분 폐기되어야 할 거예요. 백엔드 플러그인과 함께 사용되는 모든 종류의 빌더나 헬퍼 메서드는 해당 플러그인 전용 라이브러리 패키지(예: plugin-kubernetes-backend-node 패키지, 자세한 내용은 패키지 역할 문서 참조)로 이동해야 해요.

폐기 제거 후 index.ts에 남아 있어야 할 것은 기본 내보내기뿐이에요:

@backstage/plugin-kubernetes-backend/src/index.ts

export { kubernetesPlugin as default } from './plugin';

/alpha 서브패스가 있으면 폐기

이전에 alpha 내보내기로 새 백엔드 시스템을 지원한 경우 alpha 내보내기를 폐기하고 index.ts에서 다시 내보내세요.

@backstage/-backend/src/alpha.ts

/*** @alpha* @deprecated Please import from the root path instead.*/export default createPlugin({  //...});

더 알아보기 (Learn more)