Cara menggunakan php fpm worker

nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

Looks good! Now let's customize some stuff: - point the root to /var/www - place a "Hello world" index file in /var/www/index.html

sed -i "s#/usr/share/nginx/html#/var/www#" /etc/nginx/conf.d/default.conf
mkdir -p /var/www
echo "Hello world!" > /var/www/index.html

To make the changes become effective, we need to reload nginx via

nginx -s reload
[email protected]:/# nginx -s reload
2018/05/29 09:22:54 [notice] 351#351: signal process started

Check with curl, et voilá:

[email protected]:/# curl 127.0.0.1:80
Hello world!

With all that new information we can set up our nginx image with the following folder structure on the host machine:

C:\codebase\docker-php
+ nginx\
  + conf.d\
    - site.conf
  - Dockerfile
+ app\
  - index.html
  - hello-world.php

nginx\Dockerfile

FROM nginx:latest

nginx\conf.d\site.conf

server {
    listen      80;
    server_name localhost;
    root        /var/www;
}

app\index.html

Hello World

Clean up the "exploration" nginx container, cd into /c/codebase/docker-php/nginx and build the new image:

docker rm -f $[docker ps -aq]
cd /c/codebase/docker-php/nginx
docker build -t docker-nginx-image .
[email protected] MINGW64 /c/codebase/docker-php
$ docker rm -f $[docker ps -aq]
15c6b8d8a2bf
[email protected] MINGW64 /c/codebase/docker-php
$ cd nginx
[email protected] MINGW64 /c/codebase/docker-php/nginx
$ docker build -t docker-nginx-image .
Sending build context to Docker daemon  3.584kB
Step 1/1 : FROM nginx:latest
 ---> ae513a47849c
Successfully built ae513a47849c
Successfully tagged docker-nginx-image:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

And then run the "new" container via

docker run -di --name docker-nginx -p 8080:80 -v "C:\codebase\docker-php\nginx\conf.d":/etc/nginx/conf.d/ -v "C:\codebase\docker-php\app":/var/www docker-nginx-image

where

-p 8080:80                                                  // maps port 8080 on the windows host to port 80 in the container
-v "C:\codebase\docker-php\nginx\conf.d":/etc/nginx/conf.d/ // mounts the conf.d folder on the host to the correct directory in the container
-v "C:\codebase\docker-php\app":/var/www                    // mounts the "code" directory in the correct place

Thanks to the port mapping we can now simply open //127.0.0.1:8080/ in a browser on the host machine and see the content of our app\index.html file.

If you want some more information about running nginx on Docker, check out this tutorial.

Before we move on, let's clean up

docker stop docker-nginx

Setting up php-fpm

We are already familiar with the official docker PHP image but have only used the cli-only version so far. FPM ones can be pulled in by using the -fpm tags [e.g. like php:7.0-fpm]. As with nginx, let's explore the php-fpm image first:

docker run -di --name php-fpm-test php:7.0-fpm

The first thing to note is, that the image automatically exposes port 9000 as a docker ps reveals:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                  NAMES
c5d23b694563        php:7.0-fpm         "docker-php-entrypoi…"   4 hours ago         Up 4 hours                  9000/tcp               php-fpm-test

When we examine the Dockerfile that was used to build the image [click here and search for the "7.0-fpm" tag that currently [2018-05-31] links here], we can see that it contains an EXPOSE 9000 at the bottom.

What else we can we figure out...

winpty docker exec -it php-fpm-test bash

First, will check where the configuration files are located via php-fpm -i | grep config:

[email protected]:/var/www/html# php-fpm -i | grep config
Configure Command =>  './configure'  '--build=x86_64-linux-gnu' '--with-config-file-path=/usr/local/etc/php' '--with-config-file-scan-dir=/usr/local/etc/php/conf.d' '--enable-option-checking=fatal' '--disable-c
gi' '--with-mhash' '--enable-ftp' '--enable-mbstring' '--enable-mysqlnd' '--with-curl' '--with-libedit' '--with-openssl' '--with-zlib' '--with-libdir=lib/x86_64-linux-gnu' '--enable-fpm' '--with-fpm-user=www-da
ta' '--with-fpm-group=www-data' 'build_alias=x86_64-linux-gnu'
fpm.config => no value => no value
[...]

--with-config-file-path=/usr/local/etc/php is our suspect. So it is very likely, that we will find the global directives config file at /usr/local/etc/php-fpm.conf [unfortunately, we cannot resolve the location directly]. grep'ing this file for include= reveals the location for the pool directives config:

grep "include=" /usr/local/etc/php-fpm.conf
[email protected]:/var/www/html# grep "include=" /usr/local/etc/php-fpm.conf
include=etc/php-fpm.d/*.conf

Hm - a relative path. That looks kinda odd? Let's get a little more context with the -C option for grep:

grep -C 6 "include=" /usr/local/etc/php-fpm.conf
[email protected]:/var/www/html# grep -C 6 "include=" /usr/local/etc/php-fpm.conf
; Include one or more files. If glob[3] exists, it is used to include a bunch of
; files from a glob[3] pattern. This directive can be used everywhere in the
; file.
; Relative path can also be used. They will be prefixed by:
;  - the global prefix if it's been set [-p argument]
;  - /usr/local otherwise
include=etc/php-fpm.d/*.conf

Ah - that makes more sense. So we need to resolve etc/php-fpm.d/*.conf relative to /usr/local. Resulting in /usr/local/etc/php-fpm.d/*.conf [usually you'll at least find a www.conf file in there]. The pool config determines amongst other things how php-fpm listens for connections [e.g. via Unix socket or via TCP IP:port].

cat /usr/local/etc/php-fpm.d/www.conf
[email protected]:/var/www/html# cat /usr/local/etc/php-fpm.d/www.conf
[...]
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4 address on
;                            a specific port;
;   '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses
;                            [IPv6 and IPv4-mapped] on a specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000
[...]

php-fpm ist listening on port 9000 on 127.0.0.1 [localhost]. So it makes total sense to expose port 9000.

Installing xdebug

Since we probably also want to debug php-fpm, xdebug needs to be setup as well. The process is pretty much the same as for the cli image:

pecl install xdebug-2.6.0
docker-php-ext-enable xdebug
php-fpm -m | grep xdebug

Of course we'll also put that in its own Dockerfile:

C:\codebase\docker-php
+ php-fpm\
  - Dockerfile

php-fpm\Dockerfile

FROM php:7.0-fpm
RUN pecl install xdebug-2.6.0 \
    && docker-php-ext-enable xdebug

Clean up the test container and build the new image

docker rm -f php-fpm-test
cd /c/codebase/docker-php/php-fpm
docker build -t docker-php-fpm-image .

Connecting nginx and php-fpm

Now that we have containers for nginx and php-fpm, we need to connect them. To do so, we have to make sure that both containers are in the same network and can talk to each other [which is a common problem]. Docker provides so called user defined bridge networks allowing automatic service discovery. That basically means, that our nginx container can use the name of the php-fpm container to connect to it. Otherwise we would have to figure out the containers IP address in the default network every time we start the containers.

docker network ls

reveals a list of the current networks

[email protected] MINGW64 /
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
7019b0b37ba7        bridge              bridge              local
3820ad97cc92        host                host                local
03fecefbe8c9        none                null                loca

Now let's add a new one called web-network for our web stack via

docker network create --driver bridge web-network
[email protected] MINGW64 /
$ docker network create --driver bridge web-network
20966495e04e9f9df9fd64fb6035a9e9bc3aa6d83186dcd23454e085a0d97648

[email protected] MINGW64 /
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
7019b0b37ba7        bridge              bridge              local
3820ad97cc92        host                host                local
03fecefbe8c9        none                null                local
20966495e04e        web-network         bridge              local

Start the nginx container and connect it to the new network via

docker start docker-nginx
docker network connect web-network docker-nginx

Finally, we need to mount the local code folder app\ we mounted to the nginx container at /var/www also in the php-fpm container in the same location:

docker run -di --name docker-php-fpm -v "C:\codebase\docker-php\app":/var/www --network web-network docker-php-fpm-image

Note that we specified the network in the run command via the --network option. We can verify that both containers are connected to the web-network by running

docker network inspect web-network
[email protected] MINGW64 /c/codebase/docker-php/php-fpm
$ docker network inspect web-network
[
    {
        "Name": "web-network",
        "Id": "20966495e04e9f9df9fd64fb6035a9e9bc3aa6d83186dcd23454e085a0d97648",
        "Created": "2018-05-30T06:39:44.3107066Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "3358e813423165880d59c8ebc2cb4c563ee8ad1d401595f8bfcf763ff5db8f4a": {
                "Name": "docker-php-fpm",
                "EndpointID": "d2f1d6285a0932817e1fb8839bef3a6d178f5306a2116307dba200038ea2a3a3",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "eaa5c05942788985e90a80fa000723286e9b4e7179d0f6f431c0f5109e012764": {
                "Name": "docker-nginx",
                "EndpointID": "274fa9a6868aff656078a72e19c05fb87e4e86b83aaf12be9b943890140a421d",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

The "Containers" key reveals that the docker-php-fpm container has the IP address 172.18.0.3 and the docker-nginx container is reachable via 172.18.0.2. But can we actually connect from nginx to php-fpm? Let's find out:

Log into the nginx container

winpty docker exec -ti docker-nginx bash

and ping the IP

ping 172.18.0.3 -c 2
[email protected] MINGW64 /c/codebase/docker-php/php-fpm
$ winpty docker exec -ti docker-nginx bash
[email protected]:/# ping 172.18.0.3 -c 2
bash: ping: command not found

.. well, after we make the command available by installing iputils-ping:

apt-get update && apt-get install iputils-ping -y
ping 172.18.0.3 -c 2
[email protected]:/# apt-get update && apt-get install iputils-ping -y
[email protected]:/# ping 172.18.0.3 -c 2
PING 172.18.0.3 [172.18.0.3] 56[84] bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.142 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.162 ms

--- 172.18.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1071ms
rtt min/avg/max/mdev = 0.142/0.152/0.162/0.010 ms

We can ping the container - that's good. But we were also promised we could reach the container by its name docker-php-fpm:

ping docker-php-fpm -c 2
[email protected]:/# ping docker-php-fpm -c 2
PING docker-php-fpm [172.18.0.3] 56[84] bytes of data.
64 bytes from docker-php-fpm.web-network [172.18.0.3]: icmp_seq=1 ttl=64 time=0.080 ms
64 bytes from docker-php-fpm.web-network [172.18.0.3]: icmp_seq=2 ttl=64 time=0.131 ms

--- docker-php-fpm ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1045ms
rtt min/avg/max/mdev = 0.080/0.105/0.131/0.027 ms

And we can - awesome! Now we need to tell nginx to pass all PHP related requests to php-fpm by changing the nginx\conf.d\site.conf file on our windows host to

server {
    listen      80;
    server_name localhost;
    root        /var/www;

   location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass docker-php-fpm:9000;
        include fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Note the fastcgi_pass docker-php-fpm:9000; line that tells nginx how to reach our php-fpm service. Because we mounted the nginx\conf.d folder, we just need to reload nginx:

nginx -s reload

and open //127.0.0.1:8080/hello-world.php on a browser on your host machine.

Btw. there's also a good tutorial on geekyplatypus.com on how to Dockerise your PHP application with Nginx and PHP7-FPM. But since it's using docker-compose you might want to read the next chapter first :]

Putting it all together: Meet docker-compose

Lets sum up what we have do now to get everything up and running: 1. start php-cli 2. start nginx 3. start php-fpm

docker run -di --name docker-php -v "C:\codebase\docker-php\app":/var/www --network web-network docker-php-image
docker run -di --name docker-nginx -p 8080:80 -v "C:\codebase\docker-php\nginx\conf.d":/etc/nginx/conf.d/ -v "C:\codebase\docker-php\app":/var/www  --network web-network docker-nginx-image
docker run -di --name docker-php-fpm -v "C:\codebase\docker-php\app":/var/www --network web-network docker-php-fpm-image

Hm. That's alright I guess... but it also feels like "a lot". Wouldn't it be much better to have everything neatly defined in one place? I bet so! Let me introduce you to docker-compose

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services. Then, with a single command, you create and start all the services from your configuration.

Lets do this step by step, starting with the php-cli container. Create the file C:\codebase\docker-php\docker-compose.yml:

# tell docker what version of the docker-compose.yml we're using
version: '3'

# define the network
networks:
  web-network:

# start the services section
services:
  # define the name of our service
  # corresponds to the "--name" parameter
  docker-php-cli:
    # define the directory where the build should happened,
    # i.e. where the Dockerfile of the service is located
    # all paths are relative to the location of docker-compose.yml
    build: 
      context: ./php-cli
    # reserve a tty - otherwise the container shuts down immediately
    # corresponds to the "-i" flag
    tty: true
    # mount the app directory of the host to /var/www in the container
    # corresponds to the "-v" option
    volumes:
      - ./app:/var/www
    # connect to the network
    # corresponds to the "--network" option
    networks:
      - web-network

Before we get started, we're gonna clean up the old containers:

docker rm -f $[docker ps -aq]

To test the docker-compose.yml we need to run docker-compose up -d from C:\codebase\docker-php

cd "C:\codebase\docker-php"
docker-compose up -d
[email protected] MINGW64 /c/codebase/docker-php
$ docker-compose up -d
Creating network "docker-php_web-network" with the default driver
Building docker-php-cli
Step 1/2 : FROM php:7.0-cli
 ---> da771ba4e565
Step 2/2 : RUN pecl install xdebug-2.6.0     && docker-php-ext-enable xdebug
 ---> Using cache
 ---> 12be27256b12
Successfully built 12be27256b12
Successfully tagged docker-php_docker-php-cli:latest
Image for service docker-php-cli was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating docker-php_docker-php-cli_1 ... done

Note that the image is build from scratch when we run docker-compose up for the first time. A docker ps -a shows that the container is running fine, we can log in and execute source code from the host machine.

[email protected] MINGW64 /c/codebase/docker-php
$ docker ps -a
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
adf794f27315        docker-php_docker-php-cli   "docker-php-entrypoi…"   3 minutes ago       Up 2 minutes                            docker-php_docker-php-cli_1

Logging in

winpty docker exec -it docker-php_docker-php-cli_1 bash

and running

php /var/www/hello-world.php

works as before

[email protected]:/# php /var/www/hello-world.php
Hello World [php]

Now log out of the container and run

docker-compose down 

to shut the container down again:

[email protected] MINGW64 /c/codebase/docker-php
$ docker-compose down
Stopping docker-php_docker-php-cli_1 ... done
Removing docker-php_docker-php-cli_1 ... done
Removing network docker-php_web-network

Add the remaining services to the docker-compose.yml file:

# tell docker what version of the docker-compose.yml we're using
version: '3'

# define the network
networks:
  web-network:

# start the services section
services:
  # define the name of our service
  # corresponds to the "--name" parameter
  docker-php-cli:
    # define the directory where the build should happened,
    # i.e. where the Dockerfile of the service is located
    # all paths are relative to the location of docker-compose.yml
    build: 
      context: ./php-cli
    # reserve a tty - otherwise the container shuts down immediately
    # corresponds to the "-i" flag
    tty: true
    # mount the app directory of the host to /var/www in the container
    # corresponds to the "-v" option
    volumes:
      - ./app:/var/www
    # connect to the network
    # corresponds to the "--network" option
    networks:
      - web-network

  docker-nginx:
    build: 
      context: ./nginx
    # defines the port mapping
    # corresponds to the "-p" flag
    ports:
      - "8080:80"
    tty: true
    volumes:
      - ./app:/var/www
      - ./nginx/conf.d:/etc/nginx/conf.d
    networks:
      - web-network

  docker-php-fpm:
    build: 
      context: ./php-fpm
    tty: true
    volumes:
      - ./app:/var/www
    networks:
      - web-network

And up again...

docker-compose up -d
[email protected] MINGW64 /c/codebase/docker-php
$ docker-compose up -d
Building docker-nginx
Step 1/1 : FROM nginx:latest
 ---> ae513a47849c
Successfully built ae513a47849c
Successfully tagged docker-php_docker-nginx:latest
Image for service docker-nginx was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Building docker-php-fpm
Step 1/2 : FROM php:7.0-fpm
 ---> a637000da5a3
Step 2/2 : RUN pecl install xdebug-2.6.0     && docker-php-ext-enable xdebug
 ---> Running in 4ec27516df54
downloading xdebug-2.6.0.tgz ...
Starting to download xdebug-2.6.0.tgz [283,644 bytes]
[...]
---> 120c8472b4f3
Successfully built 120c8472b4f3
Successfully tagged docker-php_docker-php-fpm:latest
Image for service docker-php-fpm was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating docker-php_docker-nginx_1   ... done
Creating docker-php_docker-php-cli_1 ... done
Creating docker-php_docker-php-fpm_1 ... done

Only nginx and php-fpm needed to be built because the php-cli one already existed. Lets check if we can still open //127.0.0.1:8080/hello-world.php in a browser on the host machine:

Yes we can! So instead of needing to run 3 different command with a bunch of parameters we're now down to docker-compose up -d. Looks like an improvement to me ;]

The tl;dr

The whole article is a lot to take in and it is most likely not the most efficient approach when you "just want to get started". So in this section we'll boil it down to only the necessary steps without in depth explanations.

  • Download Docker for Windows
  • Install Docker
    • activate Hyper-V [Virtual Box will stop working]
    • enable Disk Sharing in the settings
  • Set up the following folder structure ```` C:\codebase\docker-php
    • nginx\
      • conf.d\
      • site.conf
      • Dockerfile
    • php-cli\
      • Dockerfile
    • php-fpm\
      • Dockerfile
    • app\
      • index.html
      • hello-world.html
    • docker-compose.yml ````
    • or simply git clone [email protected]:paslandau/docker-php-tutorial.git docker-php && git checkout part_1_setting-up-php-php-fpm-and-nginx-for-local-development-on-docker
  • Open a shell at C:\codebase\docker-php
  • run docker-compose up -d
  • check in browser via
    • 127.0.0.1:8080
    • 127.0.0.1:8080/hello-world.php
  • run docker-compose down

Your application code lives in the app\ folder and changes are automatically available to the containers. This setup denotes the end of the first tutorial. In the next part we will learn how to set up Docker in PHPStorm, especially in combination with xdebug.

Wrapping up

Congratulations, you made it! If some things are not completely clear by now, don't hesitate to leave a comment. Apart from that, you should now have a first idea on what docker is and how you can use it.

If you want to go deeper, please check out the remaining articles of the Docker PHP Tutorial series.

Please subscribe to the RSS feed or via email to get automatic notifications when this next part comes out :]

Wanna stay in touch?

Since you ended up on this blog, chances are pretty high that you're into Software Development [probably PHP, Laravel, Docker or Google Big Query] and I'm a big fan of feedback and networking.

So - if you'd like to stay in touch, feel free to shoot me an email with a couple of words about yourself and/or connect with me on LinkedIn or Twitter or simply subscribe to my RSS feed or go the crazy route and subscribe via mail and don't forget to leave a comment :]

Subscribe to posts via mail

Comments

Bài Viết Liên Quan

Toplist mới

Bài mới nhất

Chủ Đề