Coder Social home page Coder Social logo

fly-laravel's Introduction

Fly-Laravel

Fly-Laravel was created by Fly.io and is a quick way to get a Laravel app running on Fly.io. It was built using Laravel Zero.

Disclaimer

Fly-Laravel assumes that you have flyctl installed, and that you have it connected to your Fly.io account. If you need help with this, check out https://fly.io/docs/speedrun/.

These commands will help you set up apps on Fly.io. Remember that running these apps can cost money!

You can find more about Fly.io's free allowance and pricing here.

Installation

Run composer require fly-apps/fly-laravel to install the latest version.

By default, commands are invoked using the vendor/bin/fly-laravel script. To avoid have to type all that for ecery command, you may configure a shell alias:

alias fly-laravel='vendor/bin/fly-laravel'

To make sure this is always available, you may add this to your shell configuration file in your home directory, such as ~/.zshrc or ~/.bashrc, and then restart your shell.

Usage

With this package, you can spin up Laravel, MySQL and/or Redis apps on Fly.io . There are two commands for every type of app: launch and deploy.

  • Launch will create a new application on Fly.io in the organization you choose.
  • Deploy will (re)deploy the app. This will update the app you've already created with launch.

Prerequisites

  • You have an account on Fly.io
  • You have created an organization on Fly.io
  • You have installed the flyctl agent.

Laravel

Launch

Run fly-laravel launch to create a new Laravel application. You will be able to pick the name, what organization to deploy in and what extra services you want to set up.

  • App name: What the app on Fly.io will be called. This can only contain alphanumeric characters and hyphens, for DNS reasons.
  • Primary Region: The primary region to deploy your app in. You should pick the region closest to your users. You can always add more regions, as specified in the Scaling Documentation
  • Organization: On Fly.io, apps can be grouped into organizations. Organizations are a great way to divide up apps, share access with team members and manage billing. If there's only one organization available we'll auto-select that one.
  • Services: you can pick if you want to run cron or a queue worker in the app. This will create a process group for each extra service, to scale independently.

To set up the app, a number of steps will occur to set up a basic Laravel app:

  • The locally installed Node and PHP versions are detected
  • The fly.toml app configuration file is generated. If you want to make changes to your app later on, this is where to do it.
  • Some folders and files are copied over, most notably the Dockerfile.
  • A randomly generated APP_KEY will be set as a secret on your app.

A note on the configured SESSION_DRIVER in the fly.toml file:

  • By default, your Laravel app will be configured with Cookie-based session storage. This allows sessions to work across multiple instances of your web app without the need of an external session service like Redis to make session data available to all the instances. Of course Cookie-based session storage has limits on how much session data it can store, so you might want to consider replacing this to allow storage of larger data.

After set up, your app will be ready to deploy! In your project root, a .fly folder will be added alongside a Dockerfile and a fly.toml file.

When launching databases you will need to deploy again so launch those before deploying the laravel app.

Deploy

Run fly-laravel deploy to deploy your Laravel app. This will update the running app (if any) to include your latest changes. Add the --open flag to open the app in your browser when it has been deployed.

MySQL

Launch

Run fly-laravel launch:mysql to create a new MySQL application. You will be able to pick the app name, what organization to deploy in, the MySQL username and the volume name. If a Laravel app is detected, you can opt to use the same organization and primary region.

  • App Name: What the app on Fly.io will be called. This can only contain alphanumeric characters and hyphens, for DNS reasons. By default, [laravel app name]-db will be proposed as the app name.
  • Organization: On Fly.io, apps can be grouped into organizations. Organizations are a great way to divide up apps, share access with team members and manage billing. If there's only one organization available we'll auto-select that one.
  • Primary Region: The primary region to deploy your app in. You should pick the region closest to your users. You can always add more regions, as specified in the Scaling Documentation
  • Volume Name: For data persistence, a volume will be needed for database applications. If there's a volume with this name available, we'll use that. If no volume with this name can be found, a 1GB volume will be created on deploy. More about volumes here: Volume Documentation.

Some notes when launching a MySQL database:

  • During the launch, some environment variables will be updated in the fly.toml configuration of the Laravel app. Redeploying the Laravel app will be necessary to reflect these changes.
  • The DB_CONNECTION env var in fly.toml will be set to 'mysql'
  • On deploy, a small scale machine will be provisioned with a 1x shared CPU and 256Mb of memory. Consider scaling up the database for better performance.
  • By default, the innodb buffer pool size will be set to 64MB. Consider optimizing this based on your performance requirements. You can find this in .fly/mysql/fly.toml, in the [processes] section.
  • For the networking to work properly, the Laravel app and MySQL app should be in the same organization.

Deploy

Run fly-laravel deploy:mysql to deploy the MySQL application. After the deployment we'll run a quick check of the machine resources, and show a warning if the memory is smaller than 1GB.

Redis

Launch

Run fly-laravel launch:redis to launch a Redis application. You will be able to pick the app name, what organization to deploy in and the volume name. If a Laravel app is detected, you can opt to use the same organization and primary region.

  • App Name: What the app on Fly.io will be called. This can only contain alphanumeric characters and hyphens, for DNS reasons. By default, [laravel app name]-db will be proposed as the app name.
  • Organization: On Fly.io, apps can be grouped into organizations. Organizations are a great way to divide up apps, share access with team members and manage billing. If there's only one organization available we'll auto-select that one.
  • Primary Region: The primary region to deploy your app in. You should pick the region closest to your users. You can always add more regions, as specified in the Scaling Documentation
  • Volume Name: For data persistence, a volume will be needed for database applications. If there's a volume with this name available, we'll use that. If no volume with this name can be found, a 1GB volume will be created on deploy. More about volumes here: Volume Documentation.

Some notes when launching a Redis application:

  • During the launch, some Laravel environment variables will be updated in its fly.toml configuration. Redeploying the Laravel app will be necessary to reflect these changes.
  • The CACHE_DRIVER and SESSION_DRIVER env vars in fly.toml will be set to 'redis'
  • On deploy, a small scale machine will be provisioned with a 1x shared CPU and 256Mb of memory. Consider scaling up the database for better performance.
  • For the networking to work properly, the Laravel app and Redis app should be in the same organization.

Deploy

Run fly-laravel deploy:redis to deploy the Redis application. After the deployment we'll run a quick check of the machine resources, and show a warning if the memory is smaller than 1GB.

Volume

Mount

Run fly-laravel mount:volume to mount Volume to your Laravel Fly app's storage directory and persist the files saved here! The command will create the necessary number of Volume(s) in the proper regions, matching the number of machines per region in your Fly app. It will then update your fly.toml file's mount section to use the Volume(s)' name, and, finally create a script necessary to complete set up of the storage folder for mounting.

After set up, your app will be ready to deploy with changes to mount the created Volume(s)! This is why there is a last prompt from the command asking whether to deploy the changes or not. You can confirm--this will deploy your changes, or decline--your application will be ready to mount the Volume(s) when you deploy manually.

Further Reading

For more information about fly.io, check out the Fly.io Docs.

For more Laravel-related content, check out the Laravel-Bytes blog.

License

Fly-Laravel is an open-source software licensed under the MIT license.

fly-laravel's People

Contributors

johannes-werbrouck avatar ktanaug21 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

fly-laravel's Issues

Sorting out commands and Docker info

Laravel Zero has you make your own command, so to my knowledge, you aren't building php artisan... commands, but instead your own self-contained binary (e.g. fly-laravel).

This project's command is simply application right now (bc of this file: :https://github.com/fly-apps/fly-laravel/blob/main/application ).

So, you'd run commands in development like this:

./application list
./application launch

Note that we see all our commands available here:

image

You can rename that via ./application app:rename <foo> (see here: https://laravel-zero.com/docs/installation )

After renaming it, you can then do things like:

# If we renamed it "fly-laravel"
./fly-laravel list

The thing that builds it "for production" is

# or whatever you re-named it to
./application app:build

Registering the command

You shouldn't need to register a command with a service provider because this project itself produces a binary that creates the command.

The trick, then, is to setup the project in a way that gets Composer to add the binary to ./vendor/bin when installed in a Laravel project or ~/.composer/vendor/bin if installed globally on someones machine.

To my knowledge, this is how: https://laravel-zero.com/docs/build-a-standalone-application

(I haven't done that process yet myself in my own projects to see!)

Docker Stuff

The Dockerfile references Octane - those files are built into the base Docker image, in this repo: https://github.com/fly-apps/laravel-docker

Initial Cookie Session Driver for Launching Web App

Hi Team!

SO, earlier today, I created a simple file uploader that worked locally but not on my fly.io web app.

I. Problem:

Whenever I uploaded a file in the fly.io app, I got an error from Laravel, with the "csrf token does not match" error.


II. Break down of solution:

1. Why am I getting the token mismatch error?
-There where initially 2 vms created for the web app. Since both were up, I think the session token got set in one vm, while the upload was requested to the other vm which didnt contain the session token in 1st vm.

2. I spent some time buffering on what was happening( because it locally works, I was checking if something was up with my code,etc )
-I at first didnt think there was an issue with session, but good thing I remembered the same issue being solved thanks to this. But not all incoming Laravel devs would be able to see it immediately. So they'll keep wondering why form submission is working locally but not on fly.

3. Hopefully they see https://fly.io/laravel-bytes/full-stack-laravel/ early on and immediately attach a redis app as session driver.
-But, in case they don't, it would be really helpful to set up the session driver to cookie by default to avoid the csrf token issue when there is more than 1 vm running for the web app.
-Once they run the redis launch and deploy, it should replace the cookie session driver with the redis driver


III. Solution:

Apply cookie session driver solution as described here! This configuration would of course be overriden once they apply redis as their session driver either manually or through the redis commands of fly-laravel

Web App: Detect Node/PHP version

  • Detect PHP and Node versions

Origin( Launch-c ): https://flyio.slab.com/posts/discussion-on-artisan-command-1p074z7a

PHP Version:

I think the scanner doesn't attempt to find the Node version, just PHP. The code is seen here:

https://github.com/superfly/flyctl/blob/master/scanner/laravel.go#L70

Basically it runs php -v and runs some regex against the response to find the version. The regex: PHP ([0-9]+\.[0-9]+)\.[0-9].

Example of doing that in PHP is here: https://www.phpliveregex.com/p/ICS

Could we autogenerate safe passwords for mysql?

Wondering what is the best UX: have the user enter the password and root password for the mysql database, or just autogenerating safe passwords for these. They get set automatically in the mysql secrets and in the laravel app secrets

Create php artisan migrate command

Right now, users have to ssh into their laravel machine and run php artisan migrate there. We could help them out by giving them a fly-laravel migrate command that does everything for them!

Adding a Volume to a Laravel Web App

Hi Team! I'm planning on adding an option to add volume to the Laravel Fly app:

Question:
Should we have a separate command for adding volume? Or should it be included in the launch command?
-I'm thinking it should be part of the launch command since it's not a separate app from the laravel web app, unlike mysql, redis, etc( which would be on a separate command right?)


Steps:
-The user will be prompted if they want to add a volume to the Laravel Fly App,
-Prompt for directory to attach the volume, with default being the storage folder
-Prompt for size of volume to attach, not sure about default size yet

Supporting Laravel 11

Does the package support Laravel 11? I'm getting this error when trying to install it:

  Problem 1
    - fly-apps/fly-laravel[0.0.1, ..., 0.0.7] require illuminate/view ^10.0 -> found illuminate/view[v10.0.0, ..., v10.48.10] but these were not loaded, likely because it conflicts with another require.
    - fly-apps/fly-laravel[v0.1, ..., v0.1.7] require illuminate/http ^10.0 -> found illuminate/http[v10.0.0, ..., v10.48.10] but these were not loaded, likely because it conflicts with another require.
    - Root composer.json requires fly-apps/fly-laravel * -> satisfiable by fly-apps/fly-laravel[0.0.1, ..., v0.1.7].

Here's my full composer.json:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The skeleton application for the Laravel framework.",
    "keywords": ["laravel", "framework"],
    "license": "MIT",
    "require": {
        "php": "^8.2",
        "bezhansalleh/filament-language-switch": "^3.1",
        "filament/filament": "^3.2",
        "kenepa/translation-manager": "^4.0",
        "laravel/framework": "^11.0",
        "laravel/sanctum": "^4.0",
        "laravel/tinker": "^2.9"
    },
    "require-dev": {
        "barryvdh/laravel-ide-helper": "^3.0",
        "fakerphp/faker": "^1.23",
        "laravel/pint": "^1.13",
        "laravel/sail": "^1.26",
        "mockery/mockery": "^1.6",
        "nunomaduro/collision": "^8.0",
        "phpunit/phpunit": "^11.0",
        "spatie/laravel-ignition": "^2.4"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi",
            "@php artisan filament:upgrade"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi",
            "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
            "@php artisan migrate --graceful --ansi"
        ]
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true,
        "allow-plugins": {
            "pestphp/pest-plugin": true,
            "php-http/discovery": true
        }
    },
    "minimum-stability": "stable",
    "prefer-stable": true
}

Web App: Generate APP_KEY secret

Origin( Launch-f ): https://flyio.slab.com/posts/discussion-on-artisan-command-1p074z7a

Flyctl secrets:

  1. The command: https://github.com/superfly/flyctl/blob/de50bb2804720b4800fe98b467e90a7bcdaf6ae8/internal/command/secrets/set.go
  2. The graphql query: https://github.com/superfly/flyctl/blob/de50bb2804720b4800fe98b467e90a7bcdaf6ae8/api/resource_secrets.go#L5

As far as I can tell, flyctl doesn't do any encryption - that's done server-side after graphql receives the secret. So we should be able to make a graphql api call for secrets!

Secrets generate a new release on already-deployed apps. This won't matter on a launch command but could in future uses of adding secrets. This may not matter to us at all, just a note.

fly.toml gets overwritten when running launch command twice

We have to decide if this is wanted behaviour or not. I'd propose printing an error and stopping the command when there's already a fly.toml file present, or perhaps ask if the user just wants to deploy instead? Curious to see what you guys think.

tar create archive completed with code 1

Following command completed with code 1 when executed in php. (www-data user)
tar -czvf archive.tar.gz folder --warning=no-file-changed
When I run command with root shell it runs fine.

Let users select their organization

To get user's organizations, we can make a graphql call - just like getting the user's location.

A sticky point MIGHT be that we need the user's fly token. In theory this is in ~/.fly/config.yml, or via fly auth token.

Here's what the GraphQL call looks like tho:

curl \
    -H "Authorization: Bearer `fly auth token`" \
    -H "Accept: application/json" \
    -H "Content-Type: application/json" 
    https://api.fly.io/graphql \
    -d '{"query": "query {currentUser {email} organizations {nodes{id slug name type viewerRole}}}"}'

The response looks something like this:

{
  "data": {
    "currentUser": {
      "email": "[email protected]"
    },
    "organizations": {
      "nodes": [
        {
          "id": "xxxyyy",
          "slug": "personal",
          "name": "Chris Fidao",
          "type": "PERSONAL",
          "viewerRole": "admin"
        },
        {
          "id": "yyyxxx",
          "slug": "chipper-ci",
          "name": "Chipper CI",
          "type": "SHARED",
          "viewerRole": "admin"
        }
      ]
    }
  }
}

Let users add process groups

There are 2 process groups that seem useful to add (for a total of 3 if running the app normally as well):

[processes]
  app = ""
  cron = "cron -f"
  worker = "php artisan queue:work"

Dockerfile Generator

Similar to what Sam did for Ruby, I think creating a Dockerfile generator would be interesting.

  1. It's useful for anyone who wants to Dockerize Laravel
  2. There are "flavors" of Docker container that we can allow people to choose from:
    • Nginx + PHP-FPM vs Nginx Unit vs Nginx + Laravel Octane
    • Whether we should build static assets or not (NodeJS build step)
    • PHP Version
    • Node Version
  3. It de-couples flyctl from the Laravel stuff generated

My proposed command would be something like:

./fly dockerize --php 8.2 --node 16 --unit 

Potential flags:

--php=8.2   - (version) Set the PHP version
--node=16   - (version) Set the Node version
--no-static - (bool) Do not build static assets (--node is ignored)
--unit      - (bool) Install container with just Nginx Unit (no Nginx / PHP-FPM)
--fpm       - (bool) Install container with Nginx + PHP-FPM (default)
--octane    - (bool) Install container with Nginx + Octane support (swoole and roadrunner)

These settings could perhaps also be saved to a local .fly.yaml file or similar so when users run fly launch we could pre-populate PHP_VERSION / NODE_VERSION OR those numbers could be hard-coded in the resulting Dockerfile so we don't need to care about those Docker build arguments (currently we have to set Docker build args).

Should we ask for the organization when launching MySQL and Redis?

The networking between Laravel and the database applications is based on the .internal address, which will require the apps to be in the same organization. Does it still make sense to ask for the organization when launching a MySQL or Redis app if they will not work with the Laravel app if the organization differs?

We could just get the organization from the Laravel app and always launch in the same organization.

What do you guys think? @fideloper @KTanAug21

Consolidate all user input commands

I'd like to see all the questions directed at the user grouped before we do anything. This way, the user can just fill in everything needed to make the setup they want, and we can get going after all the questions are filled in. Here's my proposal:
We put all the answers the user gives into an array, that has an array for every service the user wants to run. So the array could look like this:

$userInput = [
    'laravel' => [
        'appname' => 'app',
        'phpVersion' => 18
    ],
    'cron' => [],
    'mysql' => [
        'appname' => 'app-mysql',
        'database_name' => 'app' 
    ] 
]

We can set up our questions in 2 steps: first we'll ask everything laravel-related since that's definitely what the user wants. Then we can ask for extra laravel-processes like cron and queue workers, and then we can ask for extra services like MySQL or Redis. For each process or service we could create a function that just adds extra stuff into the userInput array. After that, we could have a function for every service that just executes the tasks needed to set it up.

So for each service we would have a input function that adds to the userInput array, and a setUp function that executes the tasks needed. Every task would have its own function just like it is right now for the laravel app setup. When the user answered all the questions, we can just run over the keys in the $userInput array and execute the tasks we need.

I believe this would provide a good user experience since the user can just answer all the questions and let the command handle everything after that, and it would provide a great structure for us to build upon because every service follows the same pattern. If we want to add LiteFS later on for example, we just add it to the 'select services' prompt and add inputLitefs() and setupLitefs() methods.

Let me know what you guys think of this!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.