The new year saw me refreshing one of my popular development stacks. My main system is Windows 10 (2004) and for some project work, I use Docker to enable the use of specific versions of PHP with MariaDB and Apache. I decided to update one of my stacks to more recent versions, including an updated docker base image.
For this build my Dockerfile now looks like this: –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # Create image with PHP, Apache and MariaDB installed # Start with base image FROM mcr.microsoft.com/windows/servercore:2004 # PHP, Apache and Visual C runtime # Note: Apache requires thread-safe version of PHP - this version of PHP comes with php7apache2_4.dll COPY .\\scripts\\installPHP.ps1 C:\\install\\installPHP.ps1 COPY .\\packages\\php-7.4.13-Win32-vc15-x64.zip C:\\install\\php-7.4.13-Win32-vc15-x64.zip COPY .\\packages\\httpd-2.4.46-win64-VS16.zip C:\\install\\httpd-2.4.46-win64-VS16.zip supported-visual-c-downloads COPY .\\packages\\VC_redist.x64.exe C:\\install\\VC_redist.x64.exe # MariaDB COPY .\\scripts\\installMariaDB.ps1 C:\\install\\installMariaDB.ps1 COPY .\\packages\\mariadb-10.5.8-winx64.msi C:\\install\\mariadb-10.5.8-winx64.msi # Miscellaneous configuration COPY .\\scripts\\systemConfig.ps1 C:\\install\\systemConfig.ps1 # Launch scripts on image RUN powershell c:\install\installPHP.ps1 RUN powershell c:\install\installMariaDB.ps1 RUN powershell c:\install\systemConfig.ps1 |
Directory Structure
This is the contents of the directory containing the docker file.
- scripts\installMariaDB.ps1
- scripts\installPHP.ps1
- scripts\systemConfig.ps1
- packages\httpd-2.4.46-win64-VS16.zip
- packages\mariadb-10.5.8-winx64.msi
- packages\php-7.4.13-Win32-vc15-x64.zip
- packages\VC_redist.x64.exe
- mounted\my_web_site\apache_conf\httpd.conf
- mounted\my_web_site\php_ini\php.ini
- remove_temp_container.ps1
- grab_blank_db.ps1
- Dockerfile
- create_my_web_site_container.ps1
- buildImage.ps1
Build Image
This script simply contains the docker command to build the image: –
1 | docker build --rm=true --force-rm=true -t php_mdb:1.0 . |
The thread-safe version of PHP is being used as this is required by Apache and comes with php7apache2_4.dll.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | cd \install # Intsall Visual C Runtime Write "Installing Visual C runtime" Start-Process VC_redist.x64.exe -wait -argumentlist '/install','/quiet','/noretart','/log ./vcinstall.log' # Install PHP files Write "Installing PHP - Extracting " Expand-Archive -Path php-7.4.13-Win32-vc15-x64.zip -DestinationPath /PHP # Install Apache files Write "Installing Apache - Extracting" Expand-Archive -Path httpd-2.4.46-win64-VS16.zip -DestinationPath c:\ Write "Installing Apache Service" cd C:\Apache24\bin .\httpd.exe -k install Write "Config Apache Service Startup" set-service -Name Apache2.4 -StartupType Manual Write "Creating PHP.ini file location" mkdir \PHPini Write "Adding C:\PHPini to path - current session" $env:path += ";C:\PHPini" Write "Adding C:\PHPini to path - future sessions" $oldpath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path $newPath=$oldPath+";C:\PHPini" Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath |
After installing Apache we are setting the service so it will not automatically start. Instead, we will be launching Apache when the container starts so we can specify a mounted location for the httpd.conf file. This slows down the starting of the container however, for development work once the stack is up and running it stays so for a while. As such I can afford a minor delay.
MariaDB
MariaDB binaries can be obtained from https://downloads.mariadb.org/mariadb/+releases/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Write "Create directory for database files" cd \install mkdir \DBData Write "Installing MariaDB" # https://mariadb.com/kb/en/installing-mariadb-msi-packages-on-windows/ Start-Process -Wait -FilePath msiexec -ArgumentList @("/i", "mariadb-10.5.8-winx64.msi", "/quiet", "/log mariaDB.log", "PASSWORD=1234", "DATADIR=C:\DBData", "SERVICENAME=mariaDBService") Write "Starting MariaDB Service" Start-Service mariaDBService Write "Adding MariaDB bin directory to path - current session" $env:path += ";C:\Program Files\MariaDB 10.5\bin" Write "Adding MariaDB bin directory to path - future sessions" $oldpath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path $newPath=$oldPath+";C:\Program Files\MariaDB 10.5\bin" Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath Write "Adding 'root' user with wildcard to allow remote connections" mysql --user=root --password=1234 --execute="CREATE USER 'root'@'%' IDENTIFIED BY '1234';GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;" |
In the above example, I have set the password to 1234. Make sure you use something more secure and imaginative.
MariaDB is configured to store its databases within the image in directory C:\DBData. When the container is created MariaDB will create its default databases within the container at this location. However, I prefer the database to be stored on my host PC so it can be used by different containers. Doing so also simplifies my back-up strategy.
If I am doing this for the first time, or wish to start again with a fresh MariaDB database, I create a temporary container called temp_container and copy the database files from the container to the host. In this example, my host location is g:\mariadb_databases\10.5\DBData\.
1 2 | # create temp container and copy mariaDB files to a mounted location docker run --stop-signal SIGKILL --mount type=bind,source="g:/mariadb_databases/10.5/DBData",target="c:/tempDB" --name temp_container -p 8080:80 -p 13306:3306 php_mdb:1.0 xcopy c:\\dbdata\. /s/e c:\tempdb\. |
Once the files are copied to the host we no longer need the container which can be deleted: –
1 2 | # removes container created by grab_blank_db.ps1 docker rm temp_container |
systemConfig.ps1
The container we are creating will be serving web pages that are accessible from the host’s via port 8080. E.g. http://localhost:8080/index.php
The following command resolve issues with loopbacks. Port 8080 is only visible on the host, if any PHP code, which is executing within the container, uses port 8080 it will fail. For example, the PHP script may look at the incoming URL and use the port number specified there.
This command instructs Windows running within the container to map port 8080 to port 80 for localhost requests as this is the port Apache, within the container, is listening on.
1 2 | # This script is run within the docker image during the build netsh interface portproxy add v4tov4 listenport=8080 listenaddress=127.0.0.1 connectport=80 connectaddress=127.0.0.1 |
Creating the Main Container
The following script creates the container that we will be working with. In this example, the web site is located on my host at G:\my_web_site which contains a simple index.php file.
1 2 | # create container docker run --stop-signal SIGKILL --mount type=bind,source="G:\docker\builds\phpMDB\mounted\my_web_site\php_ini",target="c:/PHPini" --mount type=bind,source="G:\docker\builds\phpMDB\mounted\my_web_site\apache_conf",target="c:/HTTPDconf" --mount type=bind,source="G:\my_web_site",target="C:\Apache24\htdocs" --mount type=bind,source="g:/mariadb_databases/10.5/DBData",target="c:/DBData" --name myWebSite -p 8080:80 -p 13306:3306 -dt php_mdb:1.0 powershell -NoExit -Command C:\Apache24\bin\httpd.exe -k start -f c:\HTTPDconf\httpd.conf |
The image’s name is called myWebSite and as you can see were are using 4 mounts to point to host locations where we store: –
- Web site files and scripts to serve
- Database storage
- PHP’s configuration file php.ini
- Apache’s configuration file httpd.conf
I usually have serveral slightly different configs and containers to support different web sites and testing configurations.
The above script is using’s docker’s run command to manually start Apache with a specified httpd.conf file to use. C:\HTTPConf\ is mounted to my host’s G:\docker\builds\phpMDB\mounted\my_web_site\apache_conf\ directory.