table of content
Table Of Content

    Hands-On Experience in Real-World Web Development Projects

    Share

    In real-world software projects, jumping into something new can be challenging, especially when it involves unfamiliar domains, frameworks, languages, or coding styles. So, how do you effectively approach these new challenges?

    I. Setting Up Development Environment & Project Structure

    Docker is a powerful tool for building, managing, and hosting services locally, which comes in handy when working on project with the Cognitive framework in PHP. In the Cognitive framework for PHP, when you generate project, you will find `doc_root` directory where you configure `.htaccess`. This setup can be used with docker pull httpd to run the application inside Docker container.

    flagtickgroup\
    ├── docker
    ├── app.flagtick\
    ├── cdn.flagtick\
    ├── listener.flagtick\
    └── docker\
        └── mysql\
            └── databases\
    

    For each of the directories such as `app.flagtick`, `cdn.flagtick`, and `listener.flagtick`. You will need to initialize Composer and install CodeIgniter 4. Follow these steps for each directory:

    app.flagtick/
    ├── application/
    ├── doc_root/
    │   └── .htaccess
    ├── vendor/
    ├── .gitignore
    ├── .gitlab-ci.yml
    ├── composer.json
    ├── composer.lock
    └── system/
    

    Now conduct to create Docker setup for your project using the httpd (Apache HTTP Server) image and configure it with a `.htaccess` file, you will need to create `docker-compose.yml` file and possibly Dockerfile if additional customization is needed.

    » `docker-compose.yml`

    fg_http:
        container_name: fg-http
        build: ./docker/php
        image: fg_httpd
        restart: always
        volumes:
          - ./app.flagtick:/var/www/app.flagtick:cached
          - ./cdn.flagtick:/var/www/cdn.flagtick:cached
          - ./listener.flagtick:/var/www/listener.flagtick:cached
          - ./docker/mysql/databases:/home/database:cached
        depends_on:
          - db1
        command:
          - /bin/sh
          - -c
          - |
              init.sh
              echo "-----------------------------------"
              echo "containers ready!!!!!"
              echo "--------------------------"
              echo "Can now fire up"
              echo "- https://local.flagtick.com"
              echo "- https://local-listener.app.com"
              echo "- https://local-cdn.flagtick.com"
              echo "-----------------------------------"
              echo "-----------------------------------"
              apache2-foreground
        networks:
          web_server:
            aliases:
              - local.flagtick.com
              - local-cdn.flagtick.com
    

    We can define two folders, such as resources and tools, to export files from a local machine to Linux environment in Docker by using volumes.

    flagtickgroup\
    ├── app.flagtick\
    ├── cdn.flagtick\
    ├── listener.flagtick\
    ├── docker\
    │   └── mysql\
    │       └── databases\
    ├── resources\
    └── tools\
    

    » `docker-compose.yml`

    volumes:
          - ./app.flagtick:/var/www/app.flagtick:cached
          - ./cdn.flagtick:/var/www/cdn.flagtick:cached
          - ./listener.flagtick:/var/www/listener.flagtick:cached
          - ./docker/mysql/databases:/home/database:cached
    	  - ./tools/bpmn:/usr/local/bin/bpmn:cached
          - ./resources/media:/home/httpd/sites/media:cached
    

    When you are working with Docker and need to tailor your setup, especially for PHP application with Apache, there are several key customization steps you might consider. Here is a breakdown of what you can do to make your Dockerfile fit your specific needs:

    • Use a custom base image
    • Copy PHP configuration
    • Copy SSL certificates
    • Copy Apache configuration files
    • Enable Apache modules
    • Configure Apache
    • Restart Apache to apply configuration changes
    • Enable Apache site and configuration
    • Set working directory
    • Update permissions for binaries
    • Create directories and set permissions
    • Install Xdebug for PHP debugging
    • Copy Xdebug configuration
    • Set working directory
    • Expose ports

    Customize your Dockerfile, first create `docker` folder with subfolder named `php`. Place all the necessary files for your local setup in the `php` folder before replicating them to your Docker host environment.

    build: ./docker/php
    

    The `php` folder looks like this:

    |_ flagtick.com.conf
    |_ Dockerfile
    |_ hosts
    |_ init.sh
    |_ php.ini
    |_ server.conf
    |_ xdebug.ini
    └───wild
        |_ flagtick.com.crt
        |_ flagtick.com.csr
        |_ flagtick.com.key
        |_ gd_bundle-g2-g1.crt
    

    If you need to run different PHP version for specific endpoints, you can create custom folder named `phpx` in your Docker directory. Then, use the build attribute to point to this `phpx` folder and set it up. 

    » `docker-compose.yml`

    fl_http:
        container_name: fl-http
        build: ./docker/phpx
        volumes:
          - ./api.flagtick:/var/www/api.flagtick
        command:
          - /bin/sh
          - -c
          - |
              init.sh
              echo "----------------------------------"
              echo "containers ready!!!!!"
              echo "----------------------------------"
              echo "Can now fire up"
              echo "- https://local-api.flagtick.com"
              echo "----------------------------------"
              apache2-foreground
        networks:
          web_server:
            aliases:
              - local-api.flagtick.com
    

    As you can see, we have set `depends_on` to only rely on db1. This means we need to make sure the database service (db1) is up and running before moving on to the httpd service.

    » `docker-compose.yml`

    db1:
        container_name: fg-mysql
        build: ./docker/mysql
        image: fg_mysql
        restart: always
        volumes:
          - ./resources/db:/usr/local/resources/db
        environment:
          MYSQL_ROOT_PASSWORD: "root"
        ports:
          - "3306:3306"
        networks:
          - web_server
    

    or another service using a different MySQL version.

    db8:
        container_name: fg-mysqlx
        build: ./docker/mysqlx
        restart: always
        volumes:
          - db_datax:/var/lib/mysql
        environment:
          MYSQL_ROOT_PASSWORD: "root"
        command:
          --default-authentication-plugin=mysql_native_password
        ports:
          - "9906:3306"
        networks:
          - web_server
    

    By mapping port 3306 in the container to the host, MySQL will be accessible on the host's port 3306. You can use `curl`, `telnet`, or `nc` to check if the service is running.

    curl localhost:3306
    

    Besides, you can test connectivity by using MySQL clients or other terminal tools:

    mysql -h 127.0.0.1 -P 3306 -u root -p
    

    You can check if the service is live using curl. Once your container is running, use curl to make a request to the service to verify it is up and accessible.

    curl -I https://local.flagtick.com
    

    In Docker Desktop, go to the Containers tab, click the menu overflow, select Exec, and run the commands shown in the screenshot below.

    This is what batch mode looks like:

    # curl -I https://local.flagtick.com        
    HTTP/1.1 200 OK
    Date: Mon, 09 Sep 2024 14:13:34 GMT
    Server: Apache/2.4.52 (Debian)
    X-Powered-By: PHP/7.3.33
    Set-Cookie: ci_session=i8c8poed5b5tsh2bikgje13p3t355iee; expires=Mon, 09-Sep-2024 16:13:35 GMT; Max-Age=7200; path=/; domain=local.flagtick.com; secure; HttpOnly
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate
    Pragma: no-cache
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=local.flagtick.com; secure; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.local.flagtick.com; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.flagtick.com; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.flagtick; HttpOnly
    Set-Cookie: sys_language=en; expires=Sun, 25-Jan-2088 17:46:55 GMT; Max-Age=2000000000; path=/; domain=local.flagtick.com; secure
    Set-Cookie: sys_c_timestamp=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=local.flagtick.com; secure
    Access-Control-Allow-Origin: *
    Content-Type: text/html; charset=UTF-8
    

    Running `curl -I https://local.flagtick.com` on your local machine will produce results like this:

    curl -I https://local.flagtick.com
    curl: (7) Failed to connect to local.flagtick.com port 443 after 2071 ms: Couldn't connect to server
    

    The problem is with copying Apache configuration files. To get `local.flagtick.com` running locally with Apache, just set up `servername.conf`, configure it, and then move it to Docker on your desktop.

    » `servername.conf`

    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName local.flagtick.com
        DocumentRoot /var/www/local.flagtick
    
        SSLEngine on
        SSLCertificateFile /etc/ssl/certs/flagtick.com.crt
        SSLCertificateKeyFile /etc/ssl/private/flagtick.com.key
    
        <Directory /var/www/local.flagtick>
            AllowOverride All
            Require all granted
        </Directory>
    
        ErrorLog ${APACHE_LOG_DIR}/local.flagtick_error.log
        CustomLog ${APACHE_LOG_DIR}/local.flagtick_access.log combined
    </VirtualHost>
    </IfModule>
    
    <VirtualHost *:80>
        ServerName local.flagtick.com
        Redirect permanent / https://local.flagtick.com/
    </VirtualHost>
    

    Use the `Dockerfile` in the docker/php or docker/phpx folder to move your local setup to the Docker environment. Just make sure your source code is properly defined.

    » `Dockerfile`

    ...
    COPY ./servername.conf  /etc/apache2/conf-available/
    
    ...
    RUN a2enconf servername.conf
    

    If you would prefer to go with the Nginx -> Apache -> Service setup instead of Apache -> Service, you might want to simplify things a bit. One easy step is to remove the configurations for servername.conf. This can help streamline your setup and make it easier to manage.

    By focusing on using Nginx as the front-end proxy, you let it handle the HTTPS connections, and Apache can then focus on serving your application.

    » `servername.conf`

    ServerName flagtick.com
    

    Once port 3306 is open, you can set up the `phpmyadmin/phpmyadmin` Docker container to provide visual UI for managing and interacting with your MySQL database easily.

    » `docker-compose.yml`

    phpmyadmin:
      container_name: fg-phpmyadmin
      image: phpmyadmin/phpmyadmin
      environment:
    	- PMA_ARBITRARY=1
      restart: always
      volumes:
    	- "./docker/phpmyadmin/config.user.inc.php:/etc/phpmyadmin/config.user.inc.php"
      networks:
    	- web_server
    

    Since you are not exposing any ports directly in the phpmyadmin service, you can use reverse proxy setup with Nginx (or another web server) to access phpMyAdmin by aliasing domain and forwarding the requests to the correct container. 

    Next, we will cover the final section of the `docker-compose.yml` file, which involves setting up the Nginx server.

    nginx:
        container_name: "fg-nginx"
        build: ./docker/nginx
        ports:
          - "80:80"
          - "443:443"
        depends_on:
          - fl_php
        volumes:
          - "./docker/nginx/timeout.conf:/etc/nginx/conf.d/timeout.conf"
          - "./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf"
        networks:
          - web_server
    

    You can access `localhost:80` or `localhost:443` from your local machine if the Docker container is running and the ports are correctly mapped.

    In Apache HTTP server, multiple aliases for multiple virtual hosts means configuring various domain names or subdomains to direct traffic to different virtual hosts, each serving distinct website or application.

    » `/etc/httpd/conf.d/` or `/etc/apache2/sites-available/`

    • 000-default.conf (for the default virtual host)
    • example.com.conf (for the specific domain)
    • vhosts.conf (for multiple virtual host configurations)

    Here is typical Nginx configuration that demonstrates how to set up reverse proxying and SSL for various upstream servers and domains.

    » `default.conf`

    # Define upstream servers
    upstream backend1 {
        server fg_http:443; 
    }
    
    upstream backend2 {
        server fl_http:443; 
    }
    
    upstream pma {
        server phpmyadmin:80; 
    }
    
    # HTTP Server Block for phpMyAdmin and similar services
    server {
        listen 80;
        server_name local-pma.flagtick.com pma.flagtick.local;
    
        location / {
            proxy_set_header Host $host;
            proxy_pass http://pma;
        }
    }
    
    # HTTPS Server Block for RPM and related services
    server {
        listen 443 ssl;
        client_max_body_size 64M;
        large_client_header_buffers 8 64k;
        server_name local-api.flagtick.com;
    
        location / {
            proxy_set_header Host $host;
            proxy_pass https://backend2/;
        }
    
        ssl_certificate /etc/ssl/flagtick.com.crt;
        ssl_certificate_key /etc/ssl/flagtick.com.key;
    }
    
    # HTTPS Server Block for wildcard domains
    server {
        listen 443 ssl;
        client_max_body_size 64M;
        large_client_header_buffers 8 64k;
        server_name *.flagtick.com;
    
        location / {
            proxy_set_header Host $host;
            proxy_pass https://backend1/;
        }
    
        ssl_certificate /etc/ssl/flagtick.com.crt;
        ssl_certificate_key /etc/ssl/flagtick.com.key;
    }
    

    After you have successfully reconfigured the Nginx server, run the command `curl -I https://local.flagtick.com` again to check if everything is working.

    # curl -I https://local.flagtick.com/
    HTTP/1.1 200 OK
    Server: nginx/1.18.0
    Date: Tue, 10 Sep 2024 15:21:38 GMT
    Content-Type: text/html; charset=UTF-8
    Connection: keep-alive
    X-Powered-By: PHP/7.3.33
    Set-Cookie: ci_session=9v1bi8lg9bjqn6au1ruia26j2ceqa7as; expires=Tue, 10-Sep-2024 17:21:38 GMT; Max-Age=7200; path=/; domain=local.flagtick.com; secure; HttpOnly
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate
    Pragma: no-cache
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=local.flagtick.com; secure; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.local.flagtick.com; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.flagtick.com; HttpOnly
    Set-Cookie: ci_session=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.clinic; HttpOnly
    Set-Cookie: sys_language=en; expires=Mon, 26-Jan-2088 18:54:58 GMT; Max-Age=2000000000; path=/; domain=local.flagtick.com; secure
    Set-Cookie: sys_c_timestamp=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=local.flagtick.com; secure
    Access-Control-Allow-Origin: *
    

    II.

    Go with phpmyadmin and nginx

    Flagtick Group
    Flagtick Group The individual is sociable and enjoys making friends, often sharing knowledge across various fields. |1 second ago
    Flagtick Group The individual is sociable and enjoys making friends, often sharing knowledge across various fields. 1 second ago
    You need to login to do this manipulation!