Table of Contents
Kubernetes is an excellent open-source container orchestration platform that brings automatic scaling, automatic recovery, observability, and many more features. Since it differs from traditional operations, it has changed the development and deployment workflows as well. Debugging an application on Kubernetes can be a challenge. DevSpace is a tool that helps you develop, deploy, troubleshoot simple or complex applications. We will use a Laravel project to demonstrate its features; Laravel is a popular framework used by the PHP community with great features like extensibility, inheritance, and reusability with high customization options.
Architecture
We will look at ways to deploy a Laravel based application into a Kubernetes cluster for development and production environments. We will develop our application while the application is running in Kubernetes as if we are developing locally. And we will be able to troubleshoot our application in real-time with ease.
The desired setup uses four containers, in three pods:
- PHP-FPM container, which processes all the PHP assets.
- Nginx container, which serves static files and acts as a reverse proxy for the PHP assets.
- MySQL container, as the database.
- Redis container, as session and cache.
Introduction to DevSpace
Continuous Delivery is a challenge while developing on Kubernetes. Without using a special tool, you need to build and deploy every time code or assets change. DevSpace handles this seamlessly either by synchronizing files and hot reload of the container in question or automatically rebuilding and deploying the image(s) required. DevSpace allows you to develop in a Kubernetes cluster as if you are developing in your local machine.
Feature Highlights
- Agile development on local and remote Kubernetes clusters. Execution of entire continuous development and deployment pipeline, and a single command to deploy all components of your application.
- Declarative configuration kept in source code, in the devspace.yaml file. All of the development, deployment, and pre/post-deployment actions are defined in a single file.
- Hot Reloading for faster feedback. Instead of building and re-deploying artifacts, DevSpace allows you to use high-performance and bi-directional file synchronization. This allows changes to trigger a hot-reload on the deployed container. All of these features are highly configurable.
- Extensibility. You can extend the functionality of DevSpace via the plugin system. Hooks and commands are also built-in constructs; you can expand the functionality heavily used in CI/CD pipelines.
- Easy clean Up. You can delete the resources created via `devspace purge` in a single simple step.
- Client Only. DevSpace doesn't require server/cluster side components. A single executable on a local machine is sufficient to develop, troubleshoot and deploy.
Requirements and Setting Up Development Environment
The following tools should be installed on your local development machine:
Developing with DevSpace
First, let's start with the code. Clone the repository to your local development machine as follows. This code includes a vanilla Laravel installation, a Dockerfile, and a devspace.yaml prepopulated.
$ git clone git@github.com:loft-sh/devspace-php-laravel-nginx.git
Copy .env.example to .env.
$ cp .env.example .env
Now open the .env file and modify where necessary, like adjusting port numbers if needed.
After this, we can generate the Laravel APP_KEY variable via the following command:
$ devspace run generate-key
Review the variables by running the devspace list vars
command, and set variables where necessary.
$ devspace list vars
DevSpace will ask you a few questions regarding the image repository and other variables not defined in the .env file, and then show the output of the variables.
Variable Value
APP_DEBUG true
APP_IMAGE leventogut/php-laravel-nginx-devspace
APP_KEY xxxxxxxxxxxxxxxx
ASSET_VOLUME_NAME static-asset-volume
ASSET_VOLUME_SIZE 1Gi
DB_DATABASE laravel
DB_HOST mysql
DB_MYSQL_VERSION 8.0.23
DB_PASSWORD xxxxxxxxxxxxxxxx
DB_PORT 3306
DB_ROOT_PASSWORD xxxxxxxxxxxxxxxx
DB_USERNAME laravel
NGINX_CONFIG_HASH 740941
NGINX_IMAGE_VERSION 1.9
REDIS_PASSWORD xxxxxxxxxxxxxxxx
REDIS_VERSION 6.0.12
Running devspace dev
DevSpace is context-aware; it follows your Kubernetes config to determine the Kubernetes cluster to deploy on. However, It is good practice to set the context and namespace to use with:
$ devspace use context docker-desktop
[info] Your kube-context has been updated to 'docker-desktop'
To revert this operation, run: devspace use context maple-staging
[done] √ Successfully set kube-context to 'docker-desktop'
$ devspace use namespace laravel
[info] The default namespace of your current kube-context 'docker-desktop' has been updated to 'laravel'
To revert this operation, run: devspace use namespace
[done] √ Successfully set default namespace to 'laravel'
Now that we have all the variables and the configs, we can start in-cluster development:
$ devspace dev
DevSpace will build the artifacts we have defined in the devspace.yaml
, deploy all components, and start log streaming from the configured containers. This might take a few minutes.
In a few minutes, DevSpace will open a browser window showing a login screen. Previously, we have installed the laravel/ui package to test MySQL and Redis. Simply register as a new user, and you will be redirected to the index page. The index page has several links, including a link to the ping/pong route we will use in a few steps.
Workflow
At this stage, we have deployed our application into the Kubernetes cluster, and DevSpace is watching any changes on the project directory.
Now, having started DevSpace in development mode, we can change our code and see the immediate effect on our application that is running in the Kubernetes cluster.
Open the web.php file under the routes directory with your favorite editor. And paste the following code.
Route::get('ping', function () {
return "pong";
})
Any asset added to the repo folder will also be synced (according to the sync rules defined) to the container.
At this stage, you can try adding controllers, routes, dependencies and observe the ease of development.
Port Forwarding and Reverse Port Forwarding
You can reach the application via port forwarding. These can be defined in the devspace.yaml
file. In the current configuration, the Nginx container's port 80 is forwarded to local port 8080. The browser will be automatically opened after a successful deployment and start of the containers.
You can configure reverse port forwarding as well, which is very useful for certain debugging tools.
Commands
Our sample devspace.yaml
includes some Laravel and MySQL-specific commands to ease development workflow.
You can run any artisan, composer, php, and npm commands and additionally drop into a MySQL shell with a single mysql command.
You can list available commands via:
$ devspace list commands
Name Command Description
artisan devspace enter -c php -- php artisan Entry point for artisan commands.
composer devspace enter -c php -- composer Entry point for composer commands.
php devspace enter -c php -- php Entry point for PHP commands.
npm devspace enter -c php -- npm Entry point for NPM commands.
generate-key TMP_FILE=.devspace/app_key.tmp && docker run --rm -v $PWD:/ap... Generate APP_KEY.
mysql devspace enter -c mysql -- mysql -h'mysql' -P'3306' -u'larave... Enter to MySQL shell.
Try these commands to get familiar with them in your workflow.
Hooks
Hooks are a quite valuable feature of DevSpace. With hooks, you can run commands before and after certain deployments.
We have defined several hooks in the devspace.yaml file, such as:
- Changing MySQL user and password
- Running
npm run watch
on the PHP container. - Reloading Nginx to re-read the configuration.
Deploying to Production
The devspace deploy
command will deploy our application into the environment we define. DevSpace configuration allows us to modify and alter our parameters based on profiles. This flexibility brings numerous configuration options for development, staging, production environments. DevSpace configuration can hold all different parameters and configurations for each environment. Generally speaking, it is a good practice to create a production profile for deployment, which will remove troubleshooting aids and set parameters accordingly.
Our prepared devspace.yaml
consists of a production profile that will remove the additions we have made to make developing and troubleshooting easy.
Deploy with production profile:
$ devspace deploy -p production
Troubleshooting with DevSpace
Troubleshooting and debugging are pretty straightforward with DevSpace. DevSpace provides aid for the following:
- Logging
- Entering into containers
- Running commands inside the containers
- Interactive mode
Entering to and Working with Containers
The devspace enter
command allows you to open a shell to any of the running containers by providing the container name, so you don't have to deal with the copy/paste of long pod names.
$ devspace enter -c php
[info] Using namespace 'default'
[info] Using kube context 'docker-desktop'
[info] Opening shell to pod:container app-0:php
root@app-0:/var/www/html#
If a container is not specified, a selector will be displayed, and you can choose from the available containers:
? Which pod do you want to open the terminal for?
[Use arrows to move, type to filter]
> redis-master-0:redis app-0:nginx
app-0:php
mysql-0:mysql
Testing
As you can execute any command in the container, running the tests you have for the application is a breeze.
You can easily run phpunit or artisan test commands for running your tests.
$ devspace run php ./vendor/bin/phpunit
[info] Using namespace 'default'
[info] Using kube context 'docker-desktop'
[info] Opening shell to pod:container app-0:php
PHPUnit 9.5.3 by Sebastian Bergmann and contributors.
.F 2 / 2 (100%)
Time: 00:00.122, Memory: 20.00 MB
There was 1 failure:
1) Tests\Feature\ExampleTest::testBasicTest
Expected status code 200 but received 302.
Failed asserting that 200 is identical to 302.
/var/www/html/vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:187
/var/www/html/tests/Feature/ExampleTest.php:19
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
Let's run artisan test
:
$ devspace run artisan test
[info] Using namespace 'default'
[info] Using kube context 'docker-desktop'
[info] Opening shell to pod:container app-0:php
PASS Tests\Unit\ExampleTest
✓ basic test
FAIL Tests\Feature\ExampleTest
⨯ basic test---• Tests\Feature\ExampleTest > basic test
Expected status code 200 but received 302.
Failed asserting that 200 is identical to 302.
at tests/Feature/ExampleTest.php:19
15▕ public function testBasicTest()
16▕ {
17▕ $response = $this->get('/');
18▕
➜ 19▕ $response->assertStatus(200);
20▕ }
21▕ }
22▕
Tests: 1 failed, 1 passed
Time: 0.19s
CI/CD
DevSpace configuration can hold many profiles and can be used for different deployment options. It is common to see developers use DevSpace for their CI/CD pipeline as well. Deploying your application into the CI/CD pipeline is relatively straightforward. The ability to choose from various profiles makes it a breeze to switch from development to staging and production.
Clean Up
You can easily clean your environment with the `devspace purge` command. This will be deleting all deployments. Please note that purge will not delete persistent storage(s).
Conclusion
We have seen DevSpace in action while developing, deploying, and troubleshooting. We have seen that once DevSpace is configured, it can encompass all deployment options within it. So it can be used for development and deployment to any environment. The ability to change profiles, add new commands, and execute any hooks is advantageous.
The second part of this series will delve into how to configure DevSpace, and we will go over the many possible configuration options.