Docker LAMP Stack
Tags: docker, lamp, xampp, mysql
Published: 2021-03-27 17:56:41

**Good day!** 
This is my first post on the **DEV blog**, I'm so glad I joined this amazing resource! 

There are many things in development that may be of interest to specialists of different categories, but I decided to talk about cases from my practice of creating a web environment. 
_I apologize for some confusion in the text of this article, in view of the correction of the turns of the native speech through machine translation._

There was a time when I used **XAMPP** on a classic operating system. Then **Windows 10** came out and _small-soft corporation_ began to aggressively impose it even on lowest-level calculators. Software hangs and telemetry collection on an astronomical scale were concreted into the foundation of nature and all desire to seek further compromises in this madness disappeared. So I liked **Linux** as an endless expanse for learning the intricacies and nuances of system administration and data processing. A pleasant asset for me was the fact that the **Docker**, which became the basis for dynamic virtualization, was developed from a Linux-like OS. Based on this, I decided to combine the routine of daily work with continuous learning in the basics of data development and administration. 

Since changing the working environment, in a dense stream of routine tasks, was an unaffordable luxury for me, I decided to combine access to a single database from **XAMPP** and **Docker** at the same time. To be able to switch, if necessary, disks from **Windows** and **Linux**, keeping the program settings in a single way. 

-----
######DISCLAIMER: This article is an example of my experience, I do not urge you to repeat this in practice, maybe the steps described in this article will help someone to understand in more detail the nuances of setting up the required environment
> ###### * READ THE ARTICLE FIRST 
> ###### * THEN TRY IF YOU WANT BY YOURSELF
> ###### * DON'T FORGET TO MAKE BACKUPS!
> ######Thank you all for your attention)

-----
###On the **Windows** side (_ntfs HDD_) the **XAMPP** tool is used. 
Let's explore some defaults...

* MYSQL default DB location is:
```
C:\xampp\mysql\data
```
_You can find your actual DB folder by following command (ex. at phpmyadmin):_
```
SELECT @@datadir
```
_or check **datadir** parameter of my.ini file located in:_
```
C:\xampp\mysql
```

* htdocs default folder location is:
```
C:\xampp\htdocs
```
_This path is specified in **DocumentRoot** line of the httpd.conf file_

* To be able to use the **XAMPP** MYSQL database and htdocs folders on both **Windows** & **Linux** systems, I moved them to the other drive prudently correcting the above **datadir** & **DocumentRoot** parameters
```
D:\DB\mysql\data
D:\Projects\htdocs
```
I rebooted Windows to make sure the new settings were accepted without error, phpmyadmin are logged in successfully, I have no password for development use...

![Say Hello to Bill](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmd6zk4fz1ijv0k2vxjt.jpg)
![Say Bye to Windows 10](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugytekpcjhfib4ikwfpr.jpg)

-----
###On the **Linux** side (_ext4 HDD_) the **Docker** tool is used. 
* At first we need to install ntfs drives support and one handy utility "blkid" - if it not present in the system for some reasons:
```
$ sudo apt update && sudo apt install -y util-linux ntfs-3g
```
_Just keep in mind that Linux is widely open to solving many tasks in many different ways, so the commands used in my case are for my specific case, and you can use more preferable alternative ways to do the same_

* Create a mount point for our shared ntfs hard drive:
```
$ sudo mkdir /media/paste-your-username-here/point-folder 
$ cd /media/paste-your-username-here/ 
$ sudo chown paste-your-username-here point-folder
```
> Replace **paste-your-username-here** with your actual user name
> The first command creates a directory for the shared drive
> Then we go to the current user's disk mount directory
> and set the owner for this folder as the current user

* Now we need to discover UUID of our shared ntfs hard drive, to set its auto mounting options:
```
$ sudo blkid
```
_You should see something like this:_
```
/dev/sdb1: BLOCK_SIZE="512" UUID="BCBF273CHE84F05E" TYPE="ntfs" PARTUUID="8b2f7e1d-01"
```
> so the string between **UUID** and **TYPE** words is our needed combination...

* Once UUID is found we need to write mounting rule for this drive, mount options are specified in the file called **fstab** it is located in etc directory...
```
$ sudo vim /etc/fstab
```
You will see a current attached drives table. You need to go to the end of file and add one string such following:
```
UUID=BCBF273CHE84F05E   /media/username/point-folder   ntfs-3g         auto,defaults,permissions,uid=1000,gid=1000,umask=000,nofail,errors=continue,exec 0 0
```
> * **IMPORTANT:** read & modify this file very carefully, don't forget to make backups
> * replace **UUID** value with yours obtained with **blkid** command
> * replace **username** to yours
> * **uid** & **gid** tags are setting access to the shared drive
> * **nofail**, **errors=continue** will skip any mount errors on system boot in case of missing drive or other cases
> * **exec** tag allows to perform scripts execution on the shared drive
> * you can also set **umask** tag with **022** value - it will reset user rights of all drive contents to unix **0755** (_rwx-rx-rx_) format, but in this case I prefer to make it simpler, as **000** value which corresponds to **0777** (_rwx_) full access format

* Refresh mounted drives can be done with following command:
```
$ sudo mount -a
```
> But I prefer to reboot the system and see how it starts at all, _it is very exciting step because inaccurate **fstab** modifying can bring you blank screen on boot ^____^_ 
At last, once system is started... 

![cat being trapped](https://www.meme-arsenal.com/memes/f55b986a2181613c4547268ec7e55fa9.jpg)

-----
###We begin to create our docker project
_assume that you're already set up the **Docker** & **docker-compose** utilities. Otherwise, the description of this step will take too long and distract attention from the original topic of the article_
Let's check installed components by command:
```
$ docker -v && docker-compose -v
```
In my case the stdout looks like so:
```
Docker version 20.10.4+dfsg1, build d3cb89e
docker-compose version 1.25.0, build unknown
```

* As we mentioned before, our shared drive contains following folders:
```
ls -lah /media/username/point-folder 
drwxrwxrwx  1 username username 4.0K Sep  7  2020 'DB'
drwxrwxrwx  1 username username 4.0K Sep  7  2020 'Projects'
```
> _We will attach them into our docker containers, so the **Docker LAMP Stack** will use real **XAMPP** folders_

* Create & Get in to our project folder:
```
$ cd /media/username/point-folder/Projects
$ mkdir lamp && cd $_
```

* as the base of my **Docker LAMP Stack** image I've use such an awesome github project:
```
$ git clone https://github.com/sprintcube/docker-compose-lamp .
```
> ##### I've made some changes on it

* Let's take a first look on the lamp stack project structure:
```
ls /media/username/point-folder/Projects/lamp
bin  config  data  docker-compose.yml  logs  sample.env  www
```
> * **bin** folder contains _Dockerfiles_ which would be used to build our services:
```
./bin/mysql/Dockerfile:
FROM mariadb
```

```
./bin/webserver/Dockerfile:
FROM php:7.3-apache

RUN apt-get -y update --fix-missing
RUN apt-get -y upgrade

RUN apt-get install -y apt-utils nano wget dialog

RUN apt-get install -y --fix-missing apt-utils build-essential git curl libcurl4 libcurl4-nss-dev zip libzip-dev

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

RUN pecl install xdebug
RUN docker-php-ext-enable xdebug

RUN apt-get install -y libmcrypt-dev
#RUN docker-php-ext-install mcrypt
RUN pecl install mcrypt && docker-php-ext-enable mcrypt

RUN apt-get install -y libsqlite3-dev libsqlite3-0 default-mysql-client
RUN docker-php-ext-install pdo_mysql 
RUN docker-php-ext-install pdo_sqlite
RUN docker-php-ext-install mysqli

RUN docker-php-ext-install curl
RUN docker-php-ext-install tokenizer
RUN docker-php-ext-install json

RUN apt-get install -y zlib1g-dev
RUN docker-php-ext-install zip

RUN apt-get install -y libicu-dev
RUN docker-php-ext-install -j$(nproc) intl

RUN docker-php-ext-install mbstring

RUN apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ 
RUN docker-php-ext-install -j$(nproc) gd

RUN a2enmod rewrite headers

```
> * **config** folder contains _specific user configurations_ which represents Windows serices values from XAMPP (for instance)
> * **logs** folder for error & server-access reports
> * **data** folder are linked to the _XAMPP MYSQL database_ in our case it corresponds to: 
```
/media/username/point-folder/DB/mysql/data
```
> * **www** corresponds to _XAMPP htdocs folder_: 
```
/media/username/point-folder/Projects/htdocs
```
> * **sample.env** is a template where defined some very necessary system paths
> * and the main guest of the project... **docker-compose.yml** file, which contains all set of the instructions to assemble _LAMP Stack_ properly:
```
version: "3"

services:
  webserver:
    build: 
      context: ./bin/webserver
    container_name: 'lamp-webserver'
    restart: 'on-failure'
    ports:
      - "80:80"
      - "443:443"
    links: 
      - mysql
    volumes: 
      - ${DOCUMENT_ROOT-./www}:/var/www/html
      - ${PHP_INI-./config/php/php.ini}:/usr/local/etc/php/php.ini
      - ${VHOSTS_DIR-./config/vhosts}:/etc/apache2/sites-enabled
      - ${LOG_DIR-./logs/apache2}:/var/log/apache2
  mysql:
    build: ./bin/mysql
    container_name: 'lamp-mysql'
    restart: 'on-failure'
    ports:
      - "3306:3306"
    volumes: 
      - ${MYSQL_DATA_DIR-./data/mysql}:/var/lib/mysql
      - ${MYSQL_LOG_DIR-./logs/mysql}:/var/log/mysql
      - ./config/mysql/my.cnf:/etc/mysql/my.cnf
      - /etc/localtime:/etc/localtime:ro
    environment:
      # MYSQL_ROOT_PASSWORD: ''
      MYSQL_DATABASE: <place-your-db-name-here>
      MYSQL_USER: root
      # MYSQL_PASSWORD: ''
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: 'lamp-phpmyadmin'
    links:
      - mysql
    environment:
      PMA_HOST: mysql
      PMA_PORT: 3306
    ports:
      - '8080:80'
    volumes: 
      - /sessions
      - ./config/phpmyadmin/phpmyadmin-misc.ini:/usr/local/etc/php/conf.d/php-phpmyadmin.ini
  redis:
    container_name: 'lamp-redis'
    image: redis:latest
    ports:
      - "6379:6379"
```
> **Read this file carefully**
> * notice how user-configuration & log files are attached to the stack containers
> * MYSQL passwords are commented out, because I'm not using them on development
> * VARIABLES of this file are defined in env file

-----
###Assemble the LAMP Stack
```
$ docker-compose up --build -d
```
Check the execution result on container status:
```
$ docker ps -a
```
My stdout is:
```
5cf0a5b46b18   lamp_webserver            "docker-php-entrypoi…"   1 minute ago   Up 8 seconds   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   lamp-webserver
538243f42ab2   phpmyadmin/phpmyadmin         "/docker-entrypoint.…"   1 minute ago   Up 6 seconds   0.0.0.0:8080->80/tcp                       lamp-phpmyadmin
5fe638335f1a   377ad5bc26fe                  "docker-entrypoint.s…"   1 minute ago   Up 1 second    0.0.0.0:3306->3306/tcp                     lamp-mysql
31e73c59abaf   redis:latest                  "docker-entrypoint.s…"   1 minute ago   Up 7 seconds   0.0.0.0:6379->6379/tcp                     lamp-redis

```

---
![Victory is My!](https://i.gifer.com/M4ZJ.gif)
#####If everything is fine you could go to the browser and type:
```
http://localhost:8080
```
**PHPMyAdmin** interface will appear, you can use root account to login, we're not provide any password in our file
```
http://localhost/site/index.php
```
**Apache** also ready to use on port 80, replace **site** with name of your early created project, and happy to coding!

---
![debugging](https://i.gifer.com/1tQe.gif)
#####if something goes wrong you can:
- Inspect containers for its own settings:
   ```
   $ docker inspect 5fe6
   ```
   > _**5fe6** in this case is the beginning of the hash of the mysql container_

- View logs of returned errors:
   ```
   $ docker logs 5fe6
   ```

- Login into container to manually set necessary modifications:
   ```
   $ docker exec -it 5fe6 /bin/bash
   ```
   > The _5fe6 container shell_ will appear, use **exit** command to logout 

- If you're making changes in the structure of any Dockerfiles or docker-compose.yml do not forget to delete previously created containers and in some cases unused images, to gain some free space:
   ```
   $ docker image prune -a
   $ docker container prune
   ```
   > **Be careful with this command** it will removes all unused images and stopped containers!

-----
#Conclusion
While I was practicing on this project, it was not as big as it looks now. I hope you guys will succeed in development and you will be able to gain the necessary experience and create great projects! Thanks to everyone who read this article to the end! Good luck!