Latest Posts

Install Nginx, MariaDB, PHP (LEMP) and WordPress on Ubuntu 18.04

I was using LAMP for my WordPress blog for quite a few years. Because of Google Cloud Platform free tier VM's limitation (614M Memory and 1 vCPU), my website always suffering the performance issue if put all of components into one machine. Since last year, I keep trying using a different type of technologies to help. I tried to use Docker, also I separated Mysql db into a different vm. All those changes were working for certain conditions, but not a good enough for GCP f1.micro VM until I find Nginx and MariaDB. This combination has less resource usage and also providing me a possibility to squeeze all components into one GCP free tier f1.micro VM.

This post is to record all steps and commands I used. You will find a YouTube video at the end of post to present the installation process. The whole steps will take 30 minutes to 45 minutes to complete.

Note: same steps will apply to other cloud platform's Ubuntu. I have tested it with Ubuntu version 18.04.


1. Create Ubuntu VM and Update it to latest


sudo apt update && sudo apt dist-upgrade && sudo apt autoremove


2. Install Nginx

sudo apt install nginx



sudo systemctl stop nginx.service
sudo systemctl start nginx.service
sudo systemctl enable nginx.service


3. Install MariaDB

If you already has MariaDB installed, you might want to remove it first with command "sudo apt-get remove mariadb-server" .

Install software-properties-common:
sudo apt-get install software-properties-common

Import MariaDB gpg key:
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8

Add the apt repository:
sudo add-apt-repository "deb [arch=amd64,arm64,ppc64el] http://mariadb.mirror.liquidtelecom.com/repo/10.4/ubuntu $(lsb_release -cs) main"


Install MariaDB server:
sudo apt update
sudo apt -y install mariadb-server mariadb-client

Secure MariaDB
sudo mysql_secure_installation

When prompted, answer the questions below by following the guide.

Enter current password for root (enter for none): Just press Enter
Set root password? [Y/n]: Y
New password: Enter password (yyy123456)
Re-enter new password: Repeat password (yyy123456)
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]:  Y
Reload privilege tables now? [Y/n]:  Y


Note: you do not have to change root account password.
Also you do not have to switch to unix socket type authentication.

4. Create Wordpress Database and User for Connection

Logon to the MariaDB with root account and password you enter in previous step
sudo mysql -u root -p

create a blank database wp_db;
CREATE DATABASE wp_db;

create a new db user dedicated for WordPress connection.
CREATE USER 'wp_db_user'@'localhost' IDENTIFIED BY 'password1234';

Grant this new user with full access permission to new db wp_db
GRANT ALL ON wp_db.* TO 'wp_db_user'@'localhost' IDENTIFIED BY 'password1234' WITH GRANT OPTION;
FLUSH PRIVILEGES; 
EXIT;

Some other database commands to verify created db
show databases;
use wp_db;
show tables;


5. Install PHP
php7.2 will be used for this installation. 
sudo apt install php-fpm php-common php-mbstring php-xmlrpc php-soap php-gd php-xml php-intl php-mysql php-cli php-ldap php-zip php-curl

If you would like to change PHP configuration, use following command to edit it:
sudo nano /etc/php/7.2/fpm/php.ini
It is not necessary to do this step to make any change.

6. Install and Configure Wordpress
Download latest release to a local temp folder:
cd /tmp && wget https://wordpress.org/latest.tar.gz
tar -zxvf latest.tar.gz
sudo mv wordpress /var/www/html/wordpress


Set the right permissions for WordPress root folder
sudo chown -R www-data:www-data /var/www/html/wordpress/
sudo chmod -R 755 /var/www/html/wordpress/



Create a new WordPress wp-config.php file.
sudo mv /var/www/html/wordpress/wp-config-sample.php /var/www/html/wordpress/wp-config.php


Change configuration to connect to your local new MariaDB server:
sudo nano /var/www/html/wordpress/wp-config.php

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wp_db');

/** MySQL database username */
define('DB_USER', 'wp_db_user');

/** MySQL database password */
define('DB_PASSWORD', 'password1234');

/** MySQL hostname */
define('DB_HOST', 'localhost');

/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');

/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
Ctrl+W to save the file.

7. Configure Nginx for this new WordPress site
Create a new configuration file called wordpress
sudo nano /etc/nginx/sites-available/wordpress

Then copy and paste the content below into the file and save it. Replace example.com with your own domain name. You might also want to add your wordpress site's public ip into servername list for your testing before you switched to domain.
server {
    listen 80;
    listen [::]:80;
    root /var/www/html/wordpress;
    index  index.php index.html index.htm;
    server_name  54.32.104.91 51sec.org www.51sec.org;

     client_max_body_size 100M;

    location / {
        try_files $uri $uri/ /index.php?$args;        
    }

    location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass             unix:/var/run/php/php7.2-fpm.sock;
    fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
Enable the new wordpress site
sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/


Restart your Nginx and PHP-FPM to load the new settings.
sudo systemctl restart nginx.service
sudo systemctl restart php7.2-fpm.service


After the restarted those two services, you can browse to your website's domain name or public ip to start WordPress default configuration wizard:

WordPress default setup page
If you got a network error page or website is not reachable, you might want to check your iptable rule and add one to permit port 80 traffic in. You can find out command from the bottom of this post. 


8. Performance Review
The CPU utilization was between 5% and 15% most of time in one hour diagram.

The CPU was spiked up for 10 minutes during one day's performance diagram. Other time, CPU was mostly between 10% and 20%.

For same installation on CentOS, there are some different commands although process is same.



YouTube:

Some other steps or troubleshooting WordPress issue, please refer my another post at:
https://blog.51sec.org/2017/09/troubleshooting-wordpress-out-of-memory.html


Performance Tuning on GCP Free Tier

There were some CPU high issue when apt update task was running. I found out a couple of commands to disable those scheduled tasks, which made my GCP Free Tier VM much more stable now.  On Ubuntu 18.04 (and up) there may be up to two services involved in random scheduled apt updating/upgrading. The first apt-daily.service refreshes the list of packages. However there can be a second apt-daily-upgrade.service which actually installs security critical packages.


Sep 10 06:28:28 ubuntu-min-nginx systemd[1]: Starting Daily apt upgrade and clean activities...
Sep 10 06:28:40 ubuntu-min-nginx systemd[1]: Started Daily apt upgrade and clean activities.
Sep 10 18:32:28 ubuntu-min-nginx systemd[1]: Starting Daily apt download activities...
Sep 10 18:32:28 ubuntu-min-nginx systemd[1]: Started Daily apt download activities.


Basically we can systemctl disable both services AND their associated timers (i.e. apt-daily.timer and apt-daily-upgrade.timer).

sudo systemctl stop apt-daily.timer
sudo systemctl stop apt-daily-upgrade.timer

sudo systemctl disable apt-daily.service
sudo systemctl disable apt-daily.timer

sudo systemctl disable apt-daily-upgrade.timer
sudo systemctl disable apt-daily-upgrade.service
sudo systemctl mask apt-daily.service apt-daily-upgrade.service
sudo systemctl daemon-reload

  mv /usr/lib/apt/apt.systemd.daily /usr/lib/apt/apt.systemd.daily.DISABLED

Notes: IPTables problem. Ubuntu by default has enabled their own built-in firewall - iptable.
[email protected]:/tmp# sudo iptables --list --verbose
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
17500   70M ACCEPT     all  --  any    any     anywhere             anywhere             state RELATED,ESTABLISHED
    0     0 ACCEPT     icmp --  any    any     anywhere             anywhere
  251 24833 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 ACCEPT     udp  --  any    any     anywhere             anywhere             udp spt:ntp
    6   316 ACCEPT     tcp  --  any    any     anywhere             anywhere             state NEW tcp dpt:ssh
  146  8480 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT 14314 packets, 2627K bytes)
 pkts bytes target     prot opt in     out     source               destination
  371 32653 InstanceServices  all  --  any    any     anywhere             link-local/16

Chain InstanceServices (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.0.2          owner UID match root tcp dpt:iscsi-target /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.2.0/24       owner UID match root tcp dpt:iscsi-target /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.0.2          tcp dpt:http /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
   76  6865 ACCEPT     udp  --  any    any     anywhere             169.254.169.254      udp dpt:domain /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.169.254      tcp dpt:domain /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.0.3          owner UID match root tcp dpt:http /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     tcp  --  any    any     anywhere             169.254.0.4          tcp dpt:http /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
  274 24312 ACCEPT     tcp  --  any    any     anywhere             169.254.169.254      tcp dpt:http /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     udp  --  any    any     anywhere             169.254.169.254      udp dpt:bootps /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 ACCEPT     udp  --  any    any     anywhere             169.254.169.254      udp dpt:tftp /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    6   456 ACCEPT     udp  --  any    any     anywhere             169.254.169.254      udp dpt:ntp /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */
    0     0 REJECT     tcp  --  any    any     anywhere             link-local/16        tcp /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */ reject-with tcp-reset
    0     0 REJECT     udp  --  any    any     anywhere             link-local/16        udp /* See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or removing this rule */ reject-with icmp-port-unreachable

[email protected]:/tmp# tcpdump -i ens3 port not 22
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes
18:58:34.659044 IP 160.32.196.7.2738 > ububtu18-www.http: Flags [S], seq 3097328628, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
18:58:34.659069 IP ububtu18-www > 160.32.196.7: ICMP host ububtu18-www unreachable - admin prohibited, length 60
18:58:34.861780 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [S], seq 3494796026, win 26880, options [mss 8960,sackOK,TS val 2643816767 ecr 0,nop,wscale 7], length 0
18:58:34.862132 IP 140.204.0.165.https > ububtu18-www.52840: Flags [S.], seq 2394784264, ack 3494796027, win 28320, options [mss 1428,sackOK,TS val 1449190772 ecr 2643816767,nop,wscale 7], length 0
18:58:34.864180 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [.], ack 1, win 210, options [nop,nop,TS val 2643816769 ecr 1449190772], length 0
18:58:34.872447 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [P.], seq 1:387, ack 1, win 210, options [nop,nop,TS val 2643816777 ecr 1449190772], length 386
18:58:34.874664 IP 140.204.0.165.https > ububtu18-www.52840: Flags [.], ack 387, win 230, options [nop,nop,TS val 1449190785 ecr 2643816777], length 0
18:58:34.889798 IP 140.204.0.165.https > ububtu18-www.52840: Flags [P.], seq 1:4366, ack 387, win 230, options [nop,nop,TS val 1449190800 ecr 2643816777], length 4365
18:58:34.889818 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [.], ack 4366, win 350, options [nop,nop,TS val 2643816795 ecr 1449190800], length 0
18:58:34.890841 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [P.], seq 387:513, ack 4366, win 350, options [nop,nop,TS val 2643816796 ecr 1449190800], length 126
18:58:34.897268 IP 140.204.0.165.https > ububtu18-www.52840: Flags [P.], seq 4366:4417, ack 513, win 230, options [nop,nop,TS val 1449190807 ecr 2643816796], length 51
18:58:34.898512 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [.], seq 513:7593, ack 4417, win 350, options [nop,nop,TS val 2643816803 ecr 1449190807], length 7080
18:58:34.898763 IP 140.204.0.165.https > ububtu18-www.52840: Flags [.], ack 3345, win 274, options [nop,nop,TS val 1449190809 ecr 2643816803], length 0
18:58:34.899712 IP 140.204.0.165.https > ububtu18-www.52840: Flags [.], ack 6177, win 319, options [nop,nop,TS val 1449190809 ecr 2643816803], length 0
18:58:34.899718 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [P.], seq 7593:9789, ack 4417, win 350, options [nop,nop,TS val 2643816803 ecr 1449190807], length 2196
18:58:34.899852 IP 140.204.0.165.https > ububtu18-www.52840: Flags [.], ack 9009, win 363, options [nop,nop,TS val 1449190810 ecr 2643816803], length 0
18:58:34.940814 IP 140.204.0.165.https > ububtu18-www.52840: Flags [.], ack 9789, win 385, options [nop,nop,TS val 1449190851 ecr 2643816803], length 0
18:58:34.976152 IP 140.204.0.165.https > ububtu18-www.52840: Flags [P.], seq 4417:4693, ack 9789, win 385, options [nop,nop,TS val 1449190886 ecr 2643816803], length 276
18:58:34.976879 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [F.], seq 9789, ack 4693, win 372, options [nop,nop,TS val 2643816882 ecr 1449190886], length 0
18:58:34.977175 IP 140.204.0.165.https > ububtu18-www.52840: Flags [F.], seq 4693, ack 9790, win 385, options [nop,nop,TS val 1449190887 ecr 2643816882], length 0
18:58:34.977206 IP ububtu18-www.52840 > 140.204.0.165.https: Flags [.], ack 4694, win 372, options [nop,nop,TS val 2643816882 ecr 1449190887], length 0




[email protected]:/tmp# sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT
[email protected]:/tmp# sudo iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
2    ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
3    ACCEPT     icmp --  anywhere             anywhere
4    ACCEPT     all  --  anywhere             anywhere
5    ACCEPT     udp  --  anywhere             anywhere             udp spt:ntp
6    ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:ssh
7    REJECT     all  --  anywhere             anywhere             reject-with icmp-host-prohibited






References:




No comments