One place for hosting & domains

      How To Run Multiple PHP Versions on One Server Using Apache and PHP-FPM on CentOS 7


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      The Apache web server uses virtual hosts to manage multiple domains on a single instance. Similarly, PHP-FPM uses a daemon to manage multiple PHP versions on a single instance. Together, you can use Apache and PHP-FPM to host multiple PHP web-applications, each using a different version of PHP, all on the same server, and all at the same time. This is useful because different applications may require different versions of PHP, but some server stacks, like a regularly configured LAMP stack, can only manage one. Combining Apache with PHP-FPM is also a more cost-efficient solution than hosting each application on its own instance.

      PHP-FPM also offers configuration options for stderr and stdout logging, emergency restarts, and adaptive process spawning, which is useful for heavy-loaded sites. In fact, using Apache with PHP-FPM is one of the best stacks for hosting PHP applications, especially when it comes to performance.

      In this tutorial, you will set up two PHP sites on a single instance. Each site will use its own domain, and each domain will deploy its own version of PHP. The first, site1.your_domain, will deploy PHP 7.0. The second, site2.your_domain, will deploy PHP 7.2.

      Prerequisites

      Step 1 — Installing PHP Versions 7.0 and 7.2 with PHP-FPM

      With the prerequisites completed, you will now install PHP versions 7.0 and 7.2. The SCL (Software Collections) repository maintains numerous versions of the PHP stack for the CentOS 7 system. If you require the absolute newest version of PHP and it is not available on SCL, check the remi PPA (personal package archive) instead.

      Begin by installing the SCL repository to your system:

      • sudo yum install centos-release-scl -y

      First let’s discover what versions of PHP 7 are available on SCL:

      • sudo yum list rh-php7[0-9].x86_64

      You’ll see an output like this:

      Output

      Available Packages rh-php70.x86_64 2.3-1.el7 centos-sclo-rh rh-php71.x86_64 1-1.el7 centos-sclo-rh rh-php72.x86_64 1-2.el7 centos-sclo-rh rh-php73.x86_64 1-1.el7 centos-sclo-rh

      You will note that the newest version, PHP 7.3, is also available. For our examples, however, we will install versions 7.0 and 7.2.

      Lets begin with the older version. Install rh-php70 and rh-php70-php-fpm:

      • sudo yum install rh-php70 rh-php70-php-fpm -y
      • rh-php70 is a metapackage that runs PHP applications.
      • rh-php70-php-fpm provides the Fast Process Manager interpreter that runs as a daemon and receives Fast/CGI requests.

      Now repeat the process for PHP version 7.2. Install rh-php72 and rh-php72-php-fpm.

      • sudo yum install rh-php72 rh-php72-php-fpm -y

      Next, run the following commands to start using both software collections:

      • sudo scl enable rh-php70 bash
      • sudo scl enable rh-php72 bash

      By default, both PHP versions are listening on port 9000. But in this tutorial, we want to run two versions simultaneously. Therefore, let’s designate two new ports:

      To accomplish this, you can open /etc/opt/rh/rh-php70/php-fpm.d/www.conf in your favorite text editor and change every appearance of 9000 to 9002. Then save and close the file and repeat the process for /etc/opt/rh/rh-php72/php-fpm.d/www.conf, only now substitute 9000 with 9003. Alternately, you can use these two sed commands to make the replacements:

      • sudo sed -i 's/:9000/:9002/' /etc/opt/rh/rh-php70/php-fpm.d/www.conf
      • sudo sed -i 's/:9000/:9003/' /etc/opt/rh/rh-php72/php-fpm.d/www.conf

      You have now designated a dedicated port for each of your PHP services. Before these modifications will work, however, you must add the ports to your SELinux configuration.

      SELinux is short for Security Enhanced Linux, and it is enabled by default on CentOS 7. You must add your new ports of 9002 and 9003 to your SELinux database and assign them to your httpd services, or your applications will not run. Use the semanage command to perform this task:

      • sudo semanage port -a -t http_port_t -p tcp 9002
      • sudo semanage port -a -t http_port_t -p tcp 9003

      The -a flag specifies that you are adding an object to the database. The -t flag specifies the type of object, which in this case is http_port_t. And the -p flag designates the tcp protocol. You can learn more about SELinux and the semanage command in this tutorial, or by visiting the official SELinux documentation.

      Now you are ready to start and enable your PHP services. Begin with your rh-php70-php-fpm service and enable it to start at boot:

      • sudo systemctl start rh-php70-php-fpm
      • sudo systemctl enable rh-php70-php-fpm

      Next, verify the status of your rh-php70-php-fpm service:

      • sudo systemctl status rh-php70-php-fpm

      You’ll see an output like this:

      Output

      ● rh-php70-php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/rh-php70-php-fpm.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-04-18 04:49:59 UTC; 1min 6s ago Main PID: 1852 (php-fpm) Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec" CGroup: /system.slice/rh-php70-php-fpm.service ├─1852 php-fpm: master process (/etc/opt/rh/rh-php70/php-fpm.conf) ├─1853 php-fpm: pool www ├─1854 php-fpm: pool www ├─1855 php-fpm: pool www ├─1856 php-fpm: pool www └─1857 php-fpm: pool www Apr 18 04:49:59 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Starting The PHP FastCGI Process Manager... Apr 18 04:49:59 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Started The PHP FastCGI Process Manager.

      Repeating this process, start the rh-php72-php-fpm service and enable it to start at boot:

      • sudo systemctl start rh-php72-php-fpm
      • sudo systemctl enable rh-php72-php-fpm

      Next, verify the status of your rh-php72-php-fpm service:

      • sudo systemctl status rh-php72-php-fpm

      You’ll see another output like this:

      Output

      ● rh-php72-php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/rh-php72-php-fpm.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-04-18 04:50:04 UTC; 1min 59s ago Main PID: 1876 (php-fpm) Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec" CGroup: /system.slice/rh-php72-php-fpm.service ├─1876 php-fpm: master process (/etc/opt/rh/rh-php72/php-fpm.conf) ├─1877 php-fpm: pool www ├─1878 php-fpm: pool www ├─1879 php-fpm: pool www ├─1880 php-fpm: pool www └─1881 php-fpm: pool www Apr 18 04:50:04 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Starting The PHP FastCGI Process Manager... Apr 18 04:50:04 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Started The PHP FastCGI Process Manager.

      At this point you have installed two PHP versions on your server. Next, you will create a directory structure for each website you want to deploy.

      Step 2 — Creating Directory Structures for Both Websites

      In this section, you will create a document root directory and an index page for each of your two websites.

      First, create document root directories for both site1.your_domain and site2.your_domain:

      • sudo mkdir /var/www/site1.your_domain
      • sudo mkdir /var/www/site2.your_domain

      By default, the Apache webserver runs as an apache user and an apache group. So /var/www/ and all of its files and subdirectories should also be owned by them. Run the following commands to verify the correct ownership and permissions of your website root directories:

      • sudo chown -R apache:apache /var/www/site1.your_domain
      • sudo chown -R apache:apache /var/www/site2.your_domain
      • sudo chmod -R 755 /var/www/site1.your_domain
      • sudo chmod -R 755 /var/www/site2.your_domain

      The chown command changes the ownership of your two website directories to the apache user and the apache group. The chmod command changes the permissions associated with that user and group, as well as others.

      Next you will create an info.php file inside each website root directory. This will display each website’s PHP version information. Begin with site1:

      • sudo vi /var/www/site1.your_domain/info.php

      Add the following line:

      /var/www/site1.your_domain/info.php

      <?php phpinfo(); ?>
      

      Save and close the file. Now copy the info.php file you created to site2:

      • sudo cp /var/www/site1.your_domain/info.php /var/www/site2.your_domain/info.php

      Your web server now has the document root directories that each site requires to serve data to visitors. Next, you will configure your Apache web server to work with two different PHP versions.

      Step 3 — Configuring Apache for Both Websites

      In this section, you will create two virtual host configuration files. This will enable your two websites to work simultaneously with two different PHP versions.

      In order for Apache to serve this content, it is necessary to create a virtual host file with the correct directives. You’ll create two new virtual host configuration files inside the directory /etc/httpd/conf.d/.

      First create a new virtual host configuration file for the website site1.your_domain. Here you will direct Apache to render content using PHP 7.0:

      • sudo vi /etc/httpd/conf.d/site1.your_domain.conf

      Add the following content. Make sure the website directory path, server name, port, and PHP version match your setup:

      /etc/httpd/conf.d/site1.your_domain.conf

      
      <VirtualHost *:80>
           ServerAdmin admin@site1.your_domain
           ServerName site1.your_domain
           DocumentRoot /var/www/site1.your_domain
           DirectoryIndex info.php
           SetHandler "proxy:fcgi://127.0.0.1:9002
           ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
           AddHandler php70-fcgi .php
           Action php70-fcgi /cgi-bin/php70.fcgi
           ErrorLog /var/log/httpd/site1.your_domain_error.log
           CustomLog /var/log/httpd/site1.your_domain_access.log combined
      </VirtualHost>
      

      For DocumentRoot you are specifying the path of your website root directory. For ServerAdmin you are adding an email that the your_domain site administrator can access. For ServerName you are adding the url for your first subdomain. For SetHandler you are specifying port 9002. The remaining directives also configure your service to deploy PHP 7.0.

      Save and close the file.

      Next, create a new virtual host configuration file for the website site2.your_domain. You will specify this subdomain to deploy PHP 7.2:

      • sudo vi /etc/httpd/conf.d/site2.your_domain.conf

      Add the following content. Again, make sure the website directory path, server name, port, and PHP version match your unique information:

      /etc/httpd/conf.d/site2.your_domain.conf

      <VirtualHost *:80>
           ServerAdmin admin@site2.your_domain
           ServerName site2.your_domain
           DocumentRoot /var/www/site2.your_domain
           DirectoryIndex info.php
           SetHandler "proxy:fcgi://127.0.0.1:9003
           ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
           AddHandler php72-fcgi .php
           Action php72-fcgi /cgi-bin/php72.fcgi
           ErrorLog /var/log/httpd/site2.your_domain_error.log
           CustomLog /var/log/httpd/site2.your_domain_access.log combined
      </VirtualHost>
      

      Save and close the file when you are finished. Then check the Apache configuration file for any syntax errors:

      • sudo apachectl configtest

      You’ll see an output printing Syntax OK:

      Output

      Finally, restart the Apache service to implement your changes:

      • sudo systemctl restart httpd

      Now that you have configured Apache to serve each site, you will test them to make sure the proper PHP versions are running.

      Step 4 — Testing Both Websites

      At this point, you have configured two websites to run two different versions of PHP. Now test the results.

      Open your web browser and visit both sites http://site1.your_domain and http://site2.your_domain. You will see two pages that look like this:

      PHP 7.0 info page
      PHP 7.2 info page

      Note the titles. The first page indicates that site1.your_domain deployed PHP version 7.0. The second indicates that site2.your_domain deployed PHP version 7.2.

      Now that you’ve tested your sites, remove the info.php files. Because they contain sensitive information about your server and are accessible to unauthorized users, they pose a security vulnerability. Remove the files:

      • sudo rm -rf /var/www/site1.your_domain/info.php
      • sudo rm -rf /var/www/site2.your_domain/info.php

      You now have a single CentOS 7 server handling two websites with two different PHP versions. PHP-FPM, however, is not limited to this one application.

      Conclusion

      You have now combined virtual hosts and PHP-FPM to serve multiple websites and multiple versions of PHP on a single server. The only practical limit on the number of PHP sites and PHP versions that your Apache service can handle is the processing power of your instance.

      From here you might consider exploring PHP-FPM’s more advanced features, like its adaptive spawning process or how it can log sdtout and stderr Alternatively, you could now secure your websites. To accomplish this, you can follow our tutorial on how to secure your sites with free TLS/SSL certificates from Let’s Encrypt.



      Source link

      How To Run Multiple PHP Versions on One Server Using Apache and PHP-FPM on Debian 10


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      The Apache web server uses virtual hosts to manage multiple domains on a single instance. Similarly, PHP-FPM uses a daemon to manage multiple PHP versions on a single instance. Together, you can use Apache and PHP-FPM to host multiple PHP web-applications, each using a different version of PHP, all on the same server, and all at the same time. This is useful because different applications may require different versions of PHP, but some server stacks, like a regularly configured LAMP stack, can only manage one. Combining Apache with PHP-FPM is also a more cost-efficient solution than hosting each application on its own instance.

      PHP-FPM also offers configuration options for stderr and stdout logging, emergency restarts, and adaptive process spawning, which is useful for heavy-loaded sites. In fact, using Apache with PHP-FPM is one of the best stacks for hosting PHP applications, especially when it comes to performance.

      In this tutorial you will set up two PHP sites on a single instance. Each site will use its own domain, and each domain will deploy its own version of PHP. The first, site1.your_domain, will deploy PHP 7.0. The second, site2.your_domain, will deploy PHP 7.2.

      Prerequisites

      Step 1 — Installing PHP Versions 7.0 and 7.2 with PHP-FPM

      With the prerequisites completed, you will now install PHP versions 7.0 and 7.2, as well as PHP-FPM and several additional extensions. But to accomplish this, you will first need to add the sury php repository to your system.

      First install several required packages including curl, wget, and gnupg2:

      • sudo apt-get install curl wget gnupg2 ca-certificates lsb-release apt-transport-https -y

      The above packages will allow you to access the sury php repository, and to do so securely. sury php is a third-party repository or PPA (personal package archive). It offers PHP 7.4, 7.3, 7.2, 7.1, and 7.0 for the Debian operating system. It also offers more up-to-date versions of PHP than the official Debian 10 repositories, and will allow you to install multiple versions of PHP on the same system.

      Next, import the package’s key:

      • wget https://packages.sury.org/php/apt.gpg
      • sudo apt-key add apt.gpg

      Now add the sury php repository to your system:

      • echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php7.list

      Update the repository:

      Next, install php7.0, php7.0-fpm, php7.0-mysql, libapache2-mod-php7.0, and libapache2-mod-fcgid with the following commands:

      • sudo apt-get install php7.0 php7.0-fpm php7.0-mysql libapache2-mod-php7.0 libapache2-mod-fcgid -y
      • php7.0 is a metapackage that can be used to run PHP applications.
      • php7.0-fpm provides the Fast Process Manager interpreter that runs as a daemon and receives Fast/CGI requests.
      • php7.0-mysql connects PHP to the MySQL database.
      • libapahce2-mod-php7.0 provides the PHP module for the Apache webserver.
      • libapache2-mod-fcgid contains a mod_fcgid that starts a number of CGI program instances to handle concurrent requests.

      Now repeat the process for PHP version 7.2. Install php7.2, php7.2-fpm, php7.2-mysql, and libapache2-mod-php7.2.

      • sudo apt-get install php7.2 php7.2-fpm php7.2-mysql libapache2-mod-php7.2 -y

      After installing both PHP versions, start the php7.0-fpm service:

      • sudo systemctl start php7.0-fpm

      Next, verify the status of the php7.0-fpm service:

      • sudo systemctl status php7.0-fpm

      You’ll see the following output:

      Output

      ● php7.0-fpm.service - The PHP 7.0 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php7.0-fpm.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2020-04-04 08:51:47 UTC; 1min 17s ago Docs: man:php-fpm7.0(8) Main PID: 13016 (php-fpm7.0) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1149) Memory: 19.1M CGroup: /system.slice/php7.0-fpm.service ├─13016 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf) ├─13017 php-fpm: pool www └─13018 php-fpm: pool www Apr 04 08:51:47 debian10 systemd[1]: Starting The PHP 7.0 FastCGI Process Manager... Apr 04 08:51:47 debian10 systemd[1]: Started The PHP 7.0 FastCGI Process Manager.

      Repeating this process, now start the php7.2-fpm service:

      • sudo systemctl start php7.2-fpm

      And then verify the status of the php7.2-fpm service:

      • sudo systemctl status php7.2-fpm

      You’ll see the following output:

      Output

      ● php7.2-fpm.service - The PHP 7.2 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php7.2-fpm.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2020-04-04 08:52:52 UTC; 1min 32s ago Docs: man:php-fpm7.2(8) Process: 22207 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/7.2/fpm/pool.d/www.conf 72 (code=exite Main PID: 22204 (php-fpm7.2) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1149) Memory: 12.0M CGroup: /system.slice/php7.2-fpm.service ├─22204 php-fpm: master process (/etc/php/7.2/fpm/php-fpm.conf) ├─22205 php-fpm: pool www └─22206 php-fpm: pool www Apr 04 08:52:52 debian10 systemd[1]: Starting The PHP 7.2 FastCGI Process Manager... Apr 04 08:52:52 debian10 systemd[1]: Started The PHP 7.2 FastCGI Process Manager.

      Lastly, you must enable several modules so that your Apache2 service can work with multiple PHP versions:

      • sudo a2enmod actions fcgid alias proxy_fcgi
      • actions is used for executing CGI scripts based on media type or request method.

      • fcgid is a high performance alternative to mod_cgi that starts a sufficient number of instances of the CGI program to handle concurrent requests.

      • alias provides for the mapping of different parts of the host filesystem in the document tree, and for URL redirection.

      • proxy_fcgi allows Apache to forward requests to PHP-FPM.

      Now restart the Apache service to apply your changes:

      • sudo systemctl restart apache2

      At this point you have installed two PHP versions on your server. Next, you will create a directory structure for each website you want to deploy.

      Step 2 — Creating Directory Structures for Both Websites

      In this section, you will create a document root directory and an index page for each of your two websites.

      First, create document root directories for both site1.your_domain and site2.your_domain:

      • sudo mkdir /var/www/site1.your_domain
      • sudo mkdir /var/www/site2.your_domain

      By default, the Apache webserver runs as a www-data user and www-data group. To ensure that you have the correct ownership and permissions of your website root directories, execute the following commands:

      • sudo chown -R www-data:www-data /var/www/site1.your_domain
      • sudo chown -R www-data:www-data /var/www/site2.your_domain
      • sudo chmod -R 755 /var/www/site1.your_domain
      • sudo chmod -R 755 /var/www/site2.your_domain

      Next you will create an info.php file inside each website root directory. This will display each website’s PHP version information. Begin with site1:

      • sudo nano /var/www/site1.your_domain/info.php

      Add the following line:

      /var/www/site1.your_domain/info.php

      <?php phpinfo(); ?>
      

      Save and close the file. Now copy the info.php file you created to site2:

      • sudo cp /var/www/site1.your_domain/info.php /var/www/site2.your_domain/info.php

      Your web server should now have the document root directories that each site requires to serve data to visitors. Next, you will configure your Apache web server to work with two different PHP versions.

      Step 3 — Configuring Apache for Both Websites

      In this section, you will create two virtual host configuration files. This will enable your two websites to work simultaneously with two different PHP versions.

      In order for Apache to serve this content, it is necessary to create a virtual host file with the correct directives. Instead of modifying the default configuration file located at /etc/apache2/sites-available/000-default.conf, you’ll create two new ones inside the directory /etc/apache2/sites-available/.

      First create a new virtual host configuration file for the website site1.your_domain. Here you will direct Apache to render content using php7.0:

      • sudo nano /etc/apache2/sites-available/site1.your_domain.conf

      Add the following content. Make sure the website directory path, server name, and PHP version match your setup:

      /etc/apache2/sites-available/site1.your_domain.conf

      
      <VirtualHost *:80>
           ServerAdmin admin@site1.your_domain
           ServerName site1.your_domain
           DocumentRoot /var/www/site1.your_domain
           DirectoryIndex info.php
      
           <Directory /var/www/site1.your_domain>
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from all
           </Directory>
      
          <FilesMatch .php$>
            # For Apache version 2.4.10 and above, use SetHandler to run PHP as a fastCGI process server
            SetHandler "proxy:unix:/run/php/php7.0-fpm.sock|fcgi://localhost"
          </FilesMatch>
      
           ErrorLog ${APACHE_LOG_DIR}/site1.your_domain_error.log
           CustomLog ${APACHE_LOG_DIR}/site1.your_domain_access.log combined
      </VirtualHost>
      

      In this file you updated the DocumentRoot to your new directory and ServerAdmin to an email that the your_domain site administrator can access. You’ve also updated ServerName, which establishes the base domain for this virtual host configuration, and you’ve added a SetHandler directive to run PHP as a fastCGI process server.

      Save and close the file.

      Next, create a new virtual host configuration file for the website site2.your_domain. You will specify this subdomain to deploy php7.2:

      • sudo nano /etc/apache2/sites-available/site2.your_domain.conf

      Add the following content. Again, make sure the website directory path, server name, and PHP version match your unique information:

      /etc/apache2/sites-available/site2.your_domain.conf

      <VirtualHost *:80>
           ServerAdmin admin@site2.your_domain
           ServerName site2.your_domain
           DocumentRoot /var/www/site2.your_domain
           DirectoryIndex info.php  
      
           <Directory /var/www/site2.your_domain>
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from all
           </Directory>
      
          <FilesMatch .php$>
            # For Apache version 2.4.10 and above, use SetHandler to run PHP as a fastCGI process server
            SetHandler "proxy:unix:/run/php/php7.2-fpm.sock|fcgi://localhost"
          </FilesMatch>
      
           ErrorLog ${APACHE_LOG_DIR}/site2.your_domain_error.log
           CustomLog ${APACHE_LOG_DIR}/site2.your_domain_access.log combined
      </VirtualHost>
      

      Save and close the file when you are finished. Then check the Apache configuration file for any syntax errors:

      • sudo apachectl configtest

      You’ll see the following output:

      Output

      Syntax OK

      Next, enable both virtual host configuration files:

      • sudo a2ensite site1.your_domain
      • sudo a2ensite site2.your_domain

      Now disable the default site, since you won’t need it.:

      • sudo a2dissite 000-default.conf

      Finally, restart the Apache service to implement your changes:

      • sudo systemctl restart apache2

      Now that you have configured Apache to serve each site, you will test them to make sure the proper PHP versions are running.

      Step 4 — Testing Both Websites

      At this point, you have configured two websites to run two different versions of PHP. Now test the results.

      Open your web browser and visit both sites http://site1.your_domain and http://site2.your_domain. You will see two pages that look like this:

      PHP 7.0 info page
      PHP 7.2 info page

      Note the titles. The first page indicates that site1.your_domain deployed PHP version 7.0. The second indicates that site2.your_domain deployed PHP version 7.2.

      Now that you’ve tested your sites, remove the info.php files. Because they contain sensitive information about your server and are accessible to unauthorized users, they pose a security threat. To remove both files, run the following commands:

      • sudo rm -rf /var/www/site1.your_domain/info.php
      • sudo rm -rf /var/www/site2.your_domain/info.php

      You now have a single Debian 10 server handling two websites with two different PHP versions. PHP-FPM, however, is not limited to this one application.

      Conclusion

      You have now combined virtual hosts and PHP-FPM to serve multiple websites and multiple versions of PHP on a single server. The only practical limit on the number of PHP sites and PHP versions that your Apache service can handle is the processing power of your instance.

      From here you might consider exploring PHP-FPM’s more advanced features, like its adaptive spawning process or how it can log sdtout and stderr. Alternatively, you could now secure your websites. To accomplish this, you can follow our tutorial on how to secure your sites with free TLS/SSL certificates from Let’s Encrypt.



      Source link

      How To Run Serverless Functions Using OpenFaaS on DigitalOcean Kubernetes


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Typically, hosting a software application on the internet requires infrastructure management, planning, and monitoring for a monolithic system. Unlike this traditional approach, the serverless architecture (also known as function as a service, or FaaS) breaks down your application into functions. These functions are stateless, self contained, event triggered, functionally complete entities that communicate via APIs that you manage, instead of the underlying hardware and explicit infrastructure provisioning. Functions are scalable by design, portable, faster to set up and easier to test than ordinary apps. For the serverless architecture to work in principle, it requires a platform agnostic way of packaging and orchestrating functions.

      OpenFaaS is an open-source framework for implementing the serverless architecture on Kubernetes, using Docker containers for storing and running functions. It allows any program to be packaged as a container and managed as a function via the command line or the integrated web UI. OpenFaaS has excellent support for metrics and provides autoscaling for functions when demand increases.

      In this tutorial, you will deploy OpenFaaS to your DigitalOcean Kubernetes cluster at your domain and secure it using free Let’s Encrypt TLS certificates. You’ll also explore its web UI and deploy existing and new functions using the faas-cli, the official command line tool. In the end, you’ll have a flexible system for deploying serverless functions in place.

      Prerequisites

      • A DigitalOcean Kubernetes cluster with your connection configured as the kubectl default. The cluster must have at least 8GB RAM and 4 CPU cores available for OpenFaaS (more will be required in case of heavier use). Instructions on how to configure kubectl are shown under the Connect to your Cluster step when you create your cluster. To create a Kubernetes cluster on DigitalOcean, see Kubernetes Quickstart.
      • Docker installed on your local machine. Following Steps 1 and 2 for your distribution, see How To Install Docker.
      • An account at Docker Hub for storing Docker images you’ll create during this tutorial.
      • faas-cli, the official CLI tool for managing OpenFaaS, installed on your local machine. For instructions for multiple platforms, visit the official docs.
      • The Helm package manager installed on your local machine. To do this, complete Step 1 and add the stable repo from Step 2 of the How To Install Software on Kubernetes Clusters with the Helm 3 Package Manager tutorial.
      • The Nginx Ingress Controller and Cert-Manager installed on your cluster using Helm in order to expose OpenFaaS using Ingress Resources. For guidance, follow How to Set Up an Nginx Ingress on DigitalOcean Kubernetes Using Helm.
      • A fully registered domain name to host OpenFaaS, pointed at the Load Balancer used by the Nginx Ingress. This tutorial will use openfaas.your_domain throughout. You can purchase a domain name on Namecheap, get one for free on Freenom, or use the domain registrar of your choice.

      Note: The domain name you use in this tutorial must differ from the one used in the “How To Set Up an Nginx Ingress on DigitalOcean Kubernetes” prerequisite tutorial.

      Step 1 — Installing OpenFaaS using Helm

      In this step, you will install OpenFaaS to your Kubernetes cluster using Helm and expose it at your domain.

      As part of the Nginx Ingress Controller prerequisite, you created example Services and an Ingress. You won’t need them in this tutorial, so you can delete them by running the following commands:

      • kubectl delete -f hello-kubernetes-first.yaml
      • kubectl delete -f hello-kubernetes-second.yaml
      • kubectl delete -f hello-kubernetes-ingress.yaml

      Since you’ll be deploying functions as Kubernetes objects, it’s helpful to store them and OpenFaaS itself in separate namespaces in your cluster. The OpenFaaS namespace will be called openfaas, and the functions namespace will be openfaas-fn. Create them in your cluster by running the following command:

      • kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/namespaces.yml

      You’ll see the following output:

      Output

      namespace/openfaas created namespace/openfaas-fn created

      Next, you’ll need to add the OpenFaaS Helm repository, which hosts the OpenFaaS chart. To do this, run the following command:

      • helm repo add openfaas https://openfaas.github.io/faas-netes/

      Helm will display the following output:

      Output

      "openfaas" has been added to your repositories

      Refresh Helm’s chart cache:

      You’ll see the following output:

      Output

      Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "openfaas" chart repository ...Successfully got an update from the "jetstack" chart repository ...Successfully got an update from the "stable" chart repository Update Complete. ⎈ Happy Helming!⎈

      Before installing OpenFaaS, you’ll need to customize some chart parameters. You’ll store them on your local machine, in a file named values.yaml. Create and open the file with your text editor:

      Add the following lines:

      values.yaml

      functionNamespace: openfaas-fn
      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      First, you specify the namespace where functions will be stored by assigning openfaas-fn to the functionNamespace variable. By setting generateBasicAuth to true, you order Helm to set up mandatory authentication when accessing the OpenFaaS web UI and to generate an admin username and password login combination for you.

      Then, you enable Ingress creation and further configure it to use the Nginx Ingress Controller and serve the gateway OpenFaaS service at your domain.

      Remember to replace openfaas.your_domain with your desired domain from the prerequisites. When you are done, save and close the file.

      Finally, install OpenFaaS into the openfaas namespace with the customized values:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      You will see the following output:

      Output

      Release "openfaas" does not exist. Installing it now. NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      The output shows that the installation was successful. Run the following command to reveal the password for the admin account:

      • echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode) | tee openfaas-password.txt

      The decoded password is written to the output and to a file called openfaas-password.txt at the same time using tee. Note the output, which is your OpenFaaS password for the admin account.

      You can watch OpenFaaS containers become available by running the following command:

      • kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas"

      When all listed deployments become ready, type CTRL + C to exit.

      You can now navigate to the specified domain in your web browser. Input admin as the username and the accompanying password when prompted. You’ll see the OpenFaaS web UI:

      OpenFaaS - Empty Control Panel

      You’ve successfully installed OpenFaaS and exposed its control panel at your domain. Next, you’ll secure it using free TLS certificates from Let’s Encrypt.

      Step 2 — Enabling TLS for Your Domain

      In this step, you’ll secure your exposed domain using Let’s Encrypt certificates, provided by cert-manager.

      To do this, you’ll need to edit the ingress config in values.yaml. Open it for editing:

      Add the highlighted lines:

      values.yaml

      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: letsencrypt-prod
        tls:
          - hosts:
              - openfaas.your_domain
            secretName: openfaas-crt
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      The tls block defines in what Secret the certificates for your sites (listed under hosts) will store their certificates, which the letsencrypt-prod ClusterIssuer issues. Generally, the specified Secret must be different for every Ingress in your cluster.

      Remember to replace openfaas.your_domain with your desired domain, then save and close the file.

      Apply the changes to your cluster by running the following command:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      You’ll see the following output:

      Output

      Release "openfaas" has been upgraded. Happy Helming! NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      You’ll need to wait a few minutes for the Let’s Encrypt servers to issue a certificate for your domain. In the meantime, you can track its progress by inspecting the output of the following command:

      • kubectl describe certificate openfaas-crt -n openfaas

      The end of the output will look similar to this:

      Output

      Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal GeneratedKey 24m cert-manager Generated a new private key Normal Requested 16m cert-manager Created new CertificateRequest resource "openfaas-crt-1017759607" Normal Issued 16m cert-manager Certificate issued successfully

      When the last line of output reads Certificate issued successfully, you can exit by pressing CTRL + C. Refresh your domain in your browser to test. You’ll see the padlock to the left of the address bar in your browser, signifying that your connection is secure.

      You’ve secured your OpenFaaS domain using free TLS certificates from Let’s Encrypt. Now you’ll use the web UI and manage functions from it.

      Step 3 — Deploying Functions via the Web UI

      In this section, you’ll explore the OpenFaaS web UI and then deploy, manage, and invoke functions from it.

      The OpenFaaS web UI has two main parts: on the left-hand side, a column where the deployed functions will be listed, and the central panel, where you’ll see detailed info about a selected function and be able to interact with it.

      To deploy a new function, click the Deploy New Function button underneath the OpenFaaS logo on the upper left. You’ll see a dialog asking you to choose a function:

      OpenFaaS - Deploy a New Function dialog

      The FROM STORE tab lists pre-made functions from the official OpenFaaS function store that you can deploy right away. Each function is shown with a short description, and you can select the link icon on the right of a function to take a look at its source code. To deploy a store function from this list, select it, and then click the DEPLOY button.

      You can also supply your own function by switching to the CUSTOM tab:

      OpenFaaS - Deploy a Custom Function

      Here, you’d need to specify a Docker image of your function that is configured specifically for OpenFaaS and available at a Docker registry (such as Docker Hub). In this step, you’ll deploy a pre-made function from the OpenFaaS store, then in the next steps you’ll create and deploy custom functions to Docker Hub.

      You’ll deploy the NodeInfo function, which returns information about the machine it’s deployed on, such as CPU architecture, number of cores, total RAM memory available, and uptime (in seconds).

      From the list of store functions, select NodeInfo and click DEPLOY. It will soon show up in the list of deployed functions.

      OpenFaaS - NodeInfo Deployed

      Select it. In the central part of the screen, you’ll see basic information about the deployed function.

      OpenFaaS - Deployed Function Info

      The status of the function updates in real time, and should quickly turn to Ready. If it stays at Not Ready for longer periods of time, it’s most likely that your cluster lacks the resources to accept a new pod. You can follow How To Resize Droplets for information on how to fix this.

      Once Ready, the deployed function is accessible at the shown URL. To test it, you can navigate to the URL in your browser, or call it from the Invoke function panel located beneath the function info.

      OpenFaaS - Invoke Deployed Function

      You can select between Text, JSON, and Download to indicate the type of response you expect. If you want the request to be a POST instead of GET, you can supply request data in the Request body field.

      To call the nodeinfo function, click the INVOKE button. OpenFaaS will craft and execute a HTTP request according to the selected options and fill in the response fields with received data.

      OpenFaaS - nodeinfo Function Response

      The response status is HTTP 200 OK, which means that the request was executed successfully. The response body contains system information that the NodeInfo function collects, meaning that it’s properly accessible and working correctly.

      To delete a function, select it from the list and click the garbage can icon in the right upper corner of the page. When prompted, click OK to confirm. The function’s status will turn to Not Ready (which means it’s being removed from the cluster) and the function will soon vanish from the UI altogether.

      In this step, you’ve used the OpenFaaS web UI, as well as deploy and manage functions from it. You’ll now see how you can deploy and manage OpenFaaS functions using the command line.

      Step 4 — Managing Functions Using the faas-cli

      In this section, you’ll configure the faas-cli to work with your cluster. Then, you’ll deploy and manage your existing functions through the command line.

      To avoid having to specify your OpenFaaS domain every time you run the faas-cli, you’ll store it in an environment variable called OPENFAAS_URL, whose value the faas-cli will automatically pick up and use during execution.

      Open .bash_profile in your home directory for editing:

      Add the following line:

      ~/.bash_profile

      . . .
      export OPENFAAS_URL=https://openfaas.your_domain
      

      Remember to replace openfaas.your_domain with your domain, then save and close the file.

      To avoid having to log in again, manually evaluate the file:

      Now, ensure that you have faas-cli installed on your local machine. If you haven’t yet installed it, do so by following the instructions outlined in the official docs.

      Then, set up your login credentials by running the following command:

      • cat ~/openfaas-password.txt | faas-cli login --username admin --password-stdin

      The output will look like:

      Output

      Calling the OpenFaaS server to validate the credentials... credentials saved for admin https://openfaas.your_domain

      To deploy a function from the store, run the following command:

      • faas store deploy function_name

      You can try deploying nodeinfo by running:

      • faas store deploy nodeinfo

      You’ll see output like the following:

      Output

      Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/nodeinfo

      To list deployed functions, run faas list:

      Your existing functions will be shown:

      Output

      Function Invocations Replicas nodeinfo 0 1

      To get detailed info about a deployed function, use faas describe:

      The output will be similar to:

      Name:                nodeinfo
      Status:              Ready
      Replicas:            1
      Available replicas:  1
      Invocations:         0
      Image:               functions/nodeinfo-http:latest
      Function process:
      URL:                 https://openfaas.your_domain/function/nodeinfo
      Async URL:           https://openfaas.your_domain/async-function/nodeinfo
      Labels:              faas_function : nodeinfo
                           uid : 514253614
      Annotations:         prometheus.io.scrape : false
      

      You can invoke a function with faas invoke:

      You’ll get the following message:

      Output

      Reading from STDIN - hit (Control + D) to stop.

      You can then provide a request body. If you do, the method will be POST instead of GET. When you are done with inputting data, or want the request to be GET, press CTRL + D. The faas-cli will then execute the inferred request and output the response, similarly to the web UI.

      To delete a function, run faas remove:

      You’ll get the following output:

      Output

      Deleting: nodeinfo. Removing old function.

      Run faas list again to see that nodeinfo was removed:

      Output

      Function Invocations Replicas

      In this step, you’ve deployed, listed, invoked, and removed functions in your cluster from the command line using the faas-cli. In the next step, you’ll create your own function and deploy it to your cluster.

      Step 5 — Creating and Deploying a New Function

      Now you’ll create a sample Node.JS function using the faas-cli and deploy it to your cluster.

      The resulting function you’ll create will be packaged as a Docker container and published on Docker Hub. To be able to publish containers, you’ll need to log in by running the following command:

      Enter your Docker Hub username and password when prompted to finish the login process.

      You’ll store the sample Node.JS function in a folder named sample-js-function. Create it using the following command:

      Navigate to it:

      Populate the directory with the template of a JS function by running the following command:

      • faas new sample-js --lang node

      The output will look like this:

      Output

      2020/03/24 17:06:08 No templates found in current directory. 2020/03/24 17:06:08 Attempting to expand templates from https://github.com/openfaas/templates.git 2020/03/24 17:06:10 Fetched 19 template(s) : [csharp csharp-armhf dockerfile go go-armhf java11 java11-vert -x java8 node node-arm64 node-armhf node12 php7 python python-armhf python3 python3-armhf python3-debian ru by] from https://github.com/openfaas/templates.git Folder: sample-js created. ___ _____ ____ / _ _ __ ___ _ __ | ___|_ _ __ _/ ___| | | | | '_ / _ '_ | |_ / _` |/ _` ___ | |_| | |_) | __/ | | | _| (_| | (_| |___) | ___/| .__/ ___|_| |_|_| __,_|__,_|____/ |_| Function created in folder: sample-js Stack file written: sample-js.yml ...

      As written in the output, the code for the function itself is in the folder sample-js, while the OpenFaaS configuration for the function is in the file sample-js.yaml. Under the sample-js directory (which resembles a regular Node.JS project) are two files, handler.js and package.json.

      handler.js contains actual JS code that will return a response when the function is called. The contents of the handler look like the following:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "done"}
      }
      

      It exports a lambda function with two parameters, a context with request data and a callback that you can use to pass back response data, instead of just returning it.

      Open this file for editing:

      • nano sample-js/handler.js

      Change the highlighted line as follows:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "<h1>Hello Sammy!</h1>"}
      }
      

      When you are done, save and close the file. This OpenFaaS function will, when called, write Hello Sammy! to the response.

      Next, open the configuration file for editing:

      It will look like the following:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: https://openfaas.your_domain
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: sample-js:latest
      

      For the provider, it specifies openfaas and a default gateway. Then, it defines the sample-js function, specifies its language (node), its handler and the Docker image name, which you’ll need to modify to include your Docker Hub account username, like so:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: http://127.0.0.1:8080
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: your_docker_hub_username/sample-js:latest
      

      Save and close the file.

      Then, build the Docker image, push it to Docker Hub, and deploy it on your cluster, all at the same time by running the following command:

      There will be a lot of output (mainly from Docker), which will end like this:

      Output

      . . . [0] < Pushing sample-js [your_docker_hub_username/sample-js:latest] done. [0] Worker done. Deploying: sample-js. Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/sample-js

      Invoke your newly deployed function to make sure it’s working:

      Press CTRL + D. You’ll see the following output:

      Output

      <h1>Hello Sammy!</h1>

      This means that the function was packaged and deployed correctly.

      You can remove the function by running:

      You have now successfully created and deployed a custom Node.JS function on your OpenFaaS instance in your cluster.

      Conclusion

      You’ve deployed OpenFaaS on your DigitalOcean Kubernetes cluster and are ready to deploy and access both pre-made and custom functions. Now, you are able to implement the Function as a Service architecture, which can increase resource utilization and bring performance improvements to your apps.

      If you’d like to learn more about advanced OpenFaaS features, such as autoscaling for your deployed functions and monitoring their performance, visit the official docs.



      Source link