East PaaS
Create a custom PaaS manager following the #East programming philosophy.
Package, to use with East Foundation, to implement custom Platform as a Service / as a Code manager to deploy easily containerized applications on container-orchestration system like Kubernetes.
This package was inspired by commercial solutions such as Platform.sh, Symfony Cloud or Heroku.
This library is able to fetch a project on a source repository (like git) in a temporary folder, reads a deployment file (by default, called .paas.yaml) run some hooks to install vendors (with composer, npm, pip, etc..), compiles (make, symfony console), warmup cache, creates OCI image (with buildah or docker build) and deploy the project them in a cluster.
The library is provider free. Thanks to interfaces defined in this library, DI and Teknoo/Recipe you can adapt this library to any orchestration system.
However, a Docker client and a Kubernetes client are bundle with the library.
You can use directly this library on your personal Docker registry and Kubernetes cluster or use any commercial solution.
A demo deployed by the project above is available here
Features
Flexible
PaaS adapts to your projects
Independent
Not require any cloud provider or on-premise solution
Docker / Kubernetes
Docker and Kubernetes clients are natively bundled
Extendable
Can be extendable only via the DI's configuration or thanks to Recipe.
Fork the project on GitHub
It is open source! Hosted, developed, and maintained on GitHub.
View GitHub Project
Support this project on Patreon
This project is free and will remain free, but its development is not. If you like it and help us maintain it and evolve it, don't hesitate to support us on Patreon.
Support it
File deployment example
paas: #Dedicated to compiler version: v1 #Config maps: map1: key1: value1 key2: ${FOO} map2: foo: bar bar: R{foo} #Secrets provider secrets: map-vault: provider: map #Internal secrets, must be passed in this file options: key1: value1 key2: ${FOO} map-vault2: provider: map #Internal secrets, must be passed in this file options: hello: R{world} volume-vault: provider: map type: foo options: foo: bar bar: foo #Custom image, not available in the library images: foo: build-name: foo tag: latest path: '/images/${FOO}' #Hook to build the project before container, Called in this order builds: composer-build: #Name of the step composer: action: install #Hook to call arguments: - 'no-dev' - 'optimize-autoloader' - 'classmap-authoritative' custom-hook: hook-id: foo bar #Volume to build to use with container volumes: extra: #Name of the volume local-path: "/foo/bar" #optional local path where store data in the volume add: #folder or file, from .paas.yaml where is located to add to the volume - 'extra' other-name: #Name of the volume add: #folder or file, from .paas.yaml where is located to add to the volume - 'vendor' #Pods (set of container) pods: php-pods: #podset name replicas: 2 #instance of pods requires: - 'x86_64' - 'avx' upgrade: max-upgrading-pods: 2 max-unavailable-pods: 1 containers: php-run: #Container name image: registry.teknoo.software/php-run #Container image to use version: 7.4 listen: #Port listen by the container - 8080 volumes: #Volumes to link extra: from: 'extra' mount-path: '/opt/extra' #Path where volume will be mount app: mount-path: '/opt/app' #Path where data will be stored add: #folder or file, from .paas.yaml where is located to add to the volume - 'src' - 'var' - 'vendor' - 'composer.json' - 'composer.lock' - 'composer.phar' writables: - 'var/*' data: #Persistent volume, can not be pre-populated mount-path: '/opt/data' persistent: true storage-size: 3Gi data-replicated: #Persistent volume, can not be pre-populated mount-path: '/opt/data-replicated' persistent: true storage-provider: 'replicated-provider' storage-size: 3Gi map: mount-path: '/map' from-map: 'map2' vault: mount-path: '/vault' from-secret: 'volume-vault' variables: #To define some environment variables SERVER_SCRIPT: '${SERVER_SCRIPT}' from-maps: KEY0: 'map1.key0' import-maps: - 'map2' from-secrets: #To fetch some value from secret/vault KEY1: 'map-vault.key1' KEY2: 'map-vault.key2' import-secrets: - 'map-vault2' healthcheck: initial-delay-seconds: 10 period-seconds: 30 probe: command: ['ps', 'aux', 'php'] shell: replicas: 1 containers: sleep: image: registry.hub.docker.com/bash version: alpine demo: replicas: 1 upgrade: strategy: recreate security: fs-group: 1000 containers: nginx: image: registry.hub.docker.com/library/nginx version: alpine listen: #Port listen by the container - 8080 - 8181 volumes: www: mount-path: '/var' add: - 'nginx/www' config: mount-path: '/etc/nginx/conf.d/' add: - 'nginx/conf.d/default.conf' healthcheck: initial-delay-seconds: 10 period-seconds: 30 probe: http: port: 8080 path: '/status' is-secure: true threshold: success: 3 failure: 2 waf: image: registry.hub.docker.com/library/waf version: alpine listen: #Port listen by the container - 8181 healthcheck: initial-delay-seconds: 10 period-seconds: 30 probe: tcp: port: 8181 blackfire: image: 'blackfire/blackfire' version: '2' listen: - 8307 variables: BLACKFIRE_SERVER_ID: 'foo' BLACKFIRE_SERVER_TOKEN: 'bar' #Pods expositions services: php-service: #Service name pod: "php-pods" #Pod name, use service name by default internal: false #If false, a load balancer is use to access it from outside protocol: 'TCP' #Or UDP ports: - listen: 9876 #Port listened target: 8080 #Pod's port targeted demo: #Service name ports: - listen: 8080 #Port listened target: 8080 #Pod's port targeted - listen: 8181 #Port listened target: 8181 #Pod's port targeted #Ingresses configuration ingresses: demo: #rule name host: demo-paas.teknoo.software tls: secret: "demo-vault" #Configure the orchestrator to fetch value from vault service: #default service name: demo port: 8080 meta: letsencrypt: true annotations: foo2: bar aliases: - demo-paas.teknoo.software - alias1.demo-paas.teknoo.software - alias1.demo-paas.teknoo.software - alias2.demo-paas.teknoo.software paths: - path: /php service: name: php-service port: 9876 demo-secure: #rule name host: demo-secure.teknoo.software https-backend: true tls: secret: "demo-vault" #Configure the orchestrator to fetch value from vault service: #default service name: demo port: 8181
Manager implementation example
//config/packages/di_bridge.yaml: di_bridge: definitions: - '%kernel.project_dir%/config/di.php' //config/packages/east_foundation.yaml: di_bridge: definitions: - '%kernel.project_dir%/vendor/teknoo/east-foundation/src/di.php' - '%kernel.project_dir%/vendor/teknoo/east-foundation/infrastructures/symfony/Resources/config/di.php' - '%kernel.project_dir%/vendor/teknoo/east-foundation/infrastructures/symfony/Resources/config/laminas_di.php' import: Psr\Log\LoggerInterface: 'logger' //config/packages/east_common_di.yaml: di_bridge: definitions: - '%kernel.project_dir%/vendor/teknoo/east-common/src/di.php' - '%kernel.project_dir%/vendor/teknoo/east-common/infrastructures/doctrine/di.php' - '%kernel.project_dir%/vendor/teknoo/east-common/infrastructures/symfony/Resources/config/di.php' - '%kernel.project_dir%/vendor/teknoo/east-common/infrastructures/symfony/Resources/config/laminas_di.php' - '%kernel.project_dir%/vendor/teknoo/east-common/infrastructures/di.php' import: Doctrine\Persistence\ObjectManager: 'doctrine_mongodb.odm.default_document_manager' //config/packages/east_paas_di.yaml: di_bridge: definitions: - '%kernel.project_dir%/src/di.php' - '%kernel.project_dir%/infrastructures/Doctrine/di.php' - '%kernel.project_dir%/infrastructures/Flysystem/di.php' - '%kernel.project_dir%/infrastructures/Git/di.php' - '%kernel.project_dir%/infrastructures/Kubernetes/di.php' - '%kernel.project_dir%/infrastructures/Image/di.php' - '%kernel.project_dir%/infrastructures/ProjectBuilding/di.php' - '%kernel.project_dir%/infrastructures/PhpSecLib/di.php' - '%kernel.project_dir%/infrastructures/Symfony/Components/di.php' - '%kernel.project_dir%/infrastructures/Laminas/di.php' //bundles.php ... Teknoo\DI\SymfonyBridge\DIBridgeBundle::class => ['all' => true], Teknoo\East\FoundationBundle\EastFoundationBundle::class => ['all' => true], Teknoo\East\CommonBundle\TeknooEastCommonBundle::class => ['all' => true], Teknoo\East\Paas\Infrastructures\EastPaasBundle\TeknooEastPaasBundle::class => ['all' => true], //In doctrine config doctrine_mongodb: connections: default: server: "%env(MONGODB_SERVER)%" options: {} default_database: '%env(MONGODB_NAME)%' document_managers: default: auto_mapping: true mappings: TeknooEastPaas: type: 'xml' dir: '%kernel.project_dir%/vendor/teknoo/east-paas/infrastructures/Doctrine/config/universal' is_bundle: false prefix: 'Teknoo\East\Paas\Object' TeknooEastPaasInfrastructuresDoctrine: type: 'xml' dir: '%kernel.project_dir%/vendor/teknoo/east-paas/infrastructures/Doctrine/config/odm' is_bundle: false prefix: 'Teknoo\East\Paas\Infrastructures\Doctrine\Object\ODM' AppObjectPersisted: type: 'xml' dir: '%kernel.project_dir%/config/doctrine' is_bundle: false prefix: 'App\Object\Persisted' //In security.yaml security: //.. providers: main: id: 'teknoo.east.website.bundle.user_provider' //In routing.yaml paas_admin_account: resource: '@TeknooEastPaasBundle/Resources/config/routing_admin_account.yaml' prefix: '/admin' schemes: [https] paas_admin_job: resource: '@TeknooEastPaasBundle/Resources/config/routing_admin_job.yaml' prefix: '/admin' schemes: [https] //in config/di.php return [ //Hook HooksCollectionInterface::class => ... //Message MessageFactoryInterface::class => get(MessageFactory::class), //OCI libraries 'teknoo.east.paas.conductor.images_library' => [...] //variables 'teknoo.east.paas.root_dir' => ..., 'teknoo.east.paas.default_storage_provider' => ..., 'teknoo.east.paas.worker.tmp_dir' => ..., 'teknoo.east.paas.worker.global_variables' => [...], 'teknoo.east.paas.composer.phar.path' => ..., 'teknoo.east.paas.img_builder.cmd' => ..., 'teknoo.east.paas.img_builder.build.timeout' => ..., 'teknoo.east.paas.img_builder.build.platforms' => ..., 'teknoo.east.paas.kubernetes.ssl.verify' => ..., ];