It’s time to take the training wheels off (Replit.com) and create a local environment for our web application. We’re going to use a very useful tool called Docker to develop locally, and we’ll set up the PHP and Nginx part.
DevOps (development operations) is a very important part of web development. DevOps involves the configuration and setup of the environment for your application, whether that’s on your local computer or on the server that hosts your website.
Don’t be worried that this knowledge will go to waste. Many companies are starting to use Docker for local development and live.
The Docker setup in this lesson closely resembles the setup in this article with a couple major differences.
https://medium.com/@chewysalmon/laravel-docker-development-setup-an-updated-guide-72842dfe8bdf
The first step is to install Docker for Mac. After that’s done, create a folder called appone. This is where our web app and Docker files will live.
mkdir appone
cd appone
I like to put all docker-related files in a separate folder from my application files. Create another folder inside of the appone folder called docker.
mkdir docker
Create a docker-compose.yaml file inside of the appone folder.
touch docker-compose.yaml
Now, add the following code to the docker-compose.yaml file.
version: '3.8' services: # Application phpapp: build: context: ./docker dockerfile: phpapp.dockerfile working_dir: /var/www volumes: - ./:/var/www # Web Server webserver: build: context: ./docker dockerfile: webserver.dockerfile working_dir: /var/www volumes: - ./:/var/www depends_on: - "phpapp" ports: - 80:80
The docker-compose.yaml file has all of the “services” that our application needs to run. A service is just a container. In Docker, there are Dockerfiles, images, and containers. You can use a Dockerfile to build an image, and you use an image to create a container. Containers are instances of images with an extra writable layer.
version: ‘3.8’
This is the version of Docker that we want to use.
services:
Services are just containers. Each service is a container. I’m not sure why they decided to call them services instead of containers. They probably called them services because each container represents a service that an application needs.
phpapp:
The first “service” that our application needs is PHP. It needs to be able to run PHP files. Again, the service is a container. The PHP container (service) is responsible for running PHP and a couple packages (like Image Magick for manipulating images).
build:
Each container needs an image. The container uses the image to create the environment that it needs to run the application. You can specify an existing image in the YAML file, or you can specify a Dockerfile under the “build” option.
context: ./docker
The location (context) of the build file is in the docker folder. I like putting docker-specific files in a separate folder, away from my application code.
dockerfile: phpapp.dockerfile
The name of the dockerfile that we will use to build the image that the container needs is called phpapp-dockerfile.
working_dir: /var/www
This sets the directory where commands will be run. It’s equivalent to using “cd” to go this directory.
volumes:
Use volumes to persist data.
– ./:/var/www
The data in the folder where the docker-compose.yaml file is will be connected to the www folder in the container.
# Web Server webserver: build: context: ./docker dockerfile: webserver-dockerfile working_dir: /var/www volumes: - ./:/var/www depends_on: - "phpapp" ports: - 80:80
The only difference between the phpapp container and this container is the ports option. ports: Ports let us connect our host to the container. – 80:80 In this case, it connects our local port 80 to the container’s port 80.
The next step is to create the Dockerfiles for our phpapp and webserver containers.
Create the phpapp-dockerfile inside of the docker folder and paste the following code into it.
cd docker
touch phpapp.dockerfile
FROM php:8.0-fpm # Install ImageMagick and MySQL driver RUN apt-get update && apt-get install -y \ --no-install-recommends \ libmagickwand-dev \ && pecl install imagick \ && docker-php-ext-enable imagick \ && docker-php-ext-install pdo_mysql \ && apt-get install -y --no-install-recommends zlib1g-dev libzip-dev unzip \ && docker-php-ext-install zip # Install Composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
FROM php:8.0-fpm
Each command in our Dockerfile is a layer in the image. We start by using an image for php that has already been created.
RUN apt-get update && apt-get install -y \ --no-install-recommends \ libmagickwand-dev \
apt-get update && apt-get install -y –no-install-recommends libmagickwand-dev
The two commands above are equivalent. I just put everything on the same line to make it easier to read.
&& apt-get install -y --no-install-recommends zlib1g-dev libzip-dev unzip \ && docker-php-ext-install zip
These packages and extensions are important for using composer in your container.
If you try running “composer install”, you’ll get an error if you don’t have these tools (zlib1g-dev libzip-dev unzip) installed.
Failed to download kylekatarnls/update-helper from dist: The zip extension and unzip/7z commands are both missing, skipping.
Your command-line PHP is using multiple ini files. Run php --ini
to show them.
Now trying to download from source
If we were running a normal server, “apt install zip unzip php-zip” probably would’ve been enough, but since this is in a Dockerfile, we have to use the specific “docker-php-ext-install” command to install the zip extension.
You might also get an error when trying to install tinker. Just run the following command. I’m not totally sure why that worked, but it did.
composer clearcache
After that, we install ImageMagick and an MySQL driver for later on when we create a database container.
RUN curl -sS https://getcomposer.org/installer | php — –install-dir=/usr/local/bin –filename=composer
I like to install composer in the container to make it easier to run composer commands.
Alternatively, you could use this command to run composer without having to install composer in our image.
docker run –rm -v “$(pwd)”:/app composer install
This uses the composer image to run composer, and then it deletes it.
We still have to create the image for NginX, our web server.
Create another file called webserver.dockerfile in the docker folder.
cd docker
touch webserver.dockerfile
FROM nginx:1.21 COPY vhost.conf /etc/nginx/conf.d/default.conf RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log
COPY vhost.conf /etc/nginx/conf.d/default.conf
In this command, we’re copying the config file to the Nginx’s default config file. Since the context was already set to the docker folder, we don’t need to change directories. It will be able to find the vhost.conf file in the docker folder and copy it to the right directory in the container.
We’ll create the Nginx configuration file in the next lesson.