Deploying the Docker practice environment developed by PHP has always been a big problem. whether it is a development environment or a production environment, Docker packages the development environment and production environment in a lightweight manner, provides a consistent environment. This greatly improves the consistency of development and deployment. Of course, the actual situation is not that simple, because the production environment and development environment configuration are completely different, such as logs and other issues need to be configured separately, but it is at least simpler and more convenient than before. here we use PHP development as an example to explain how to deploy the development environment in Docker.
Generally, the following tools are required for a PHP project:
-
Web server: Nginx/Tengine
-
Web application: PHP-FPM
-
Database: MySQL/PostgreSQL
-
Cache service: Redis/Memcache
This is the simplest architecture. in the early stages of Docker development, Docker was abused. for example, when multiple services were started in an image, log collection still follows Syslog or other old methods, the size of the image is very huge, and the basic image can reach 80 Mb, which is totally different from the idea proposed by Docker at the beginning. the Alpine Linux release is a lightweight Linux environment, as a basic Docker image, Docker officially recommends Alpine instead of Debian as the basic image. in the future, a large number of existing official images will be migrated to Alpine. All images in this article use Alpine as the basic image.
Nginx/Tengine
I have explained Tengine's Docker practices in another article on Nginx practices of Docker containers and provided Dockerfile, in addition, the official Nginx alpine image has been provided, so Tengine is used here. I have uploaded the image to the official DockerHub.
docker pull chasontang/tengine:2.1.2_f
To obtain the image, see Dockerfile.
PHP-FPM
Docker officially provides PHP 7.0.7-fpm-alpine Images. the Dockerfile is as follows:
FROM alpine:3.4# persistent / runtime depsENV PHPIZE_DEPS \ autoconf \ file \ g++ \ gcc \ libc-dev \ make \ pkgconf \ re2cRUN apk add --no-cache --virtual .persistent-deps \ ca-certificates \ curl# ensure www-data user existsRUN set -x \ && addgroup -g 82 -S www-data \ && adduser -u 82 -D -S -G www-data www-data# 82 is the standard uid/gid for "www-data" in Alpine# http://git.alpinelinux.org/cgit/aports/tree/main/apache2/apache2.pre-install?h=v3.3.2# http://git.alpinelinux.org/cgit/aports/tree/main/lighttpd/lighttpd.pre-install?h=v3.3.2# http://git.alpinelinux.org/cgit/aports/tree/main/nginx-initscripts/nginx-initscripts.pre-install?h=v3.3.2ENV PHP_INI_DIR /usr/local/etc/phpRUN mkdir -p $PHP_INI_DIR/conf.d####ENV PHP_EXTRA_CONFIGURE_ARGS --enable-fpm --with-fpm-user=www-data --with-fpm-group=www-data####ENV GPG_KEYS 1A4E8B7277C42E53DBA9C7B9BCAA30EA9C0D5763ENV PHP_VERSION 7.0.7ENV PHP_FILENAME php-7.0.7.tar.xzENV PHP_SHA256 9cc64a7459242c79c10e79d74feaf5bae3541f604966ceb600c3d2e8f5fe4794RUN set -xe \ && apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ curl-dev \ gnupg \ libedit-dev \ libxml2-dev \ openssl-dev \ sqlite-dev \ && curl -fSL "http://php.net/get/$PHP_FILENAME/from/this/mirror" -o "$PHP_FILENAME" \ && echo "$PHP_SHA256 *$PHP_FILENAME" | sha256sum -c - \ && curl -fSL "http://php.net/get/$PHP_FILENAME.asc/from/this/mirror" -o "$PHP_FILENAME.asc" \ && export GNUPGHOME="$(mktemp -d)" \ && for key in $GPG_KEYS; do \ gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ done \ && gpg --batch --verify "$PHP_FILENAME.asc" "$PHP_FILENAME" \ && rm -r "$GNUPGHOME" "$PHP_FILENAME.asc" \ && mkdir -p /usr/src \ && tar -Jxf "$PHP_FILENAME" -C /usr/src \ && mv "/usr/src/php-$PHP_VERSION" /usr/src/php \ && rm "$PHP_FILENAME" \ && cd /usr/src/php \ && ./configure \ --with-config-file-path="$PHP_INI_DIR" \ --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \ $PHP_EXTRA_CONFIGURE_ARGS \ --disable-cgi \# --enable-mysqlnd is included here because it's harder to compile after the fact than extensions are (since it's a plugin for several extensions, not an extension in itself) --enable-mysqlnd \# --enable-mbstring is included here because otherwise there's no way to get pecl to use it properly (see https://github.com/docker-library/php/issues/195) --enable-mbstring \ --with-curl \ --with-libedit \ --with-openssl \ --with-zlib \ && make -j"$(getconf _NPROCESSORS_ONLN)" \ && make install \ && { find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; } \ && make clean \ && runDeps="$( \ scanelf --needed --nobanner --recursive /usr/local \ | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ | sort -u \ | xargs -r apk info --installed \ | sort -u \ )" \ && apk add --no-cache --virtual .php-rundeps $runDeps \ && apk del .build-depsCOPY docker-php-ext-* /usr/local/bin/####WORKDIR /var/www/htmlRUN set -ex \ && cd /usr/local/etc \ && if [ -d php-fpm.d ]; then \ # for some reason, upstream's php-fpm.conf.default has "include=NONE/etc/php-fpm.d/*.conf" sed 's!=NONE/!=!g' php-fpm.conf.default | tee php-fpm.conf > /dev/null; \ cp php-fpm.d/www.conf.default php-fpm.d/www.conf; \ else \ # PHP 5.x don't use "include=" by default, so we'll create our own simple config that mimics PHP 7+ for consistency mkdir php-fpm.d; \ cp php-fpm.conf.default php-fpm.d/www.conf; \ { \ echo '[global]'; \ echo 'include=etc/php-fpm.d/*.conf'; \ } | tee php-fpm.conf; \ fi \ && { \ echo '[global]'; \ echo 'error_log = /proc/self/fd/2'; \ echo; \ echo '[www]'; \ echo '; if we send this to /proc/self/fd/1, it never appears'; \ echo 'access.log = /proc/self/fd/2'; \ echo; \ echo 'clear_env = no'; \ echo; \ echo '; Ensure worker stdout and stderr are sent to the main error log.'; \ echo 'catch_workers_output = yes'; \ } | tee php-fpm.d/docker.conf \ && { \ echo '[global]'; \ echo 'daemonize = no'; \ echo; \ echo '[www]'; \ echo 'listen = [::]:9000'; \ } | tee php-fpm.d/zz-docker.confEXPOSE 9000CMD ["php-fpm"]####
First, the image inherits from the alpine: 3.4 image, uses the apk command to install the minimum php dependency, and adds www-data as the php-fpm running user, specify the php configuration file to/usr/local/etc/php, and then download php-src for compilation and installation. for details, refer to the php compilation and installation document written by the author. All parameters are satisfactory. The installation directory is specified to/usr/local. Then, use scanelf to obtain the list of dependent runtime libraries and delete other installation packages. Copy docker-php-ext-configure, docker-php-ext-enable, and docker-php-ext-install to the container. these three files are used for subsequent installation and extension. Then copy the php-fpm.conf to the configuration directory and specify error_log and access_log to the terminal standard output. daemonize = no indicates that the service process is not running. Port EXPOSE 9000 is used to communicate with other containers, and then CMD ["php-fpm"] runs php-fpm. In addition, the working directory is specified to/var/www/html.
Docker-compose
After the basic image is completed, we can use the basic image to configure the container, but it is very troublesome to start the container by using the manual docker command. But fortunately the official has provided the docker-compose command to orchestrate the container, just need to write a docker-compose.yaml file on the line, specific can refer to the official documentation.
version: '2'services: php-fpm: image: php:7.0.7-fpm-alpine volumes: - "./src:/var/www/html" restart: always tengine: depends_on: - php-fpm links: - php-fpm image: chasontang/tengine:2.1.2_f volumes: - "./nginx.vh.default.conf:/etc/nginx/conf.d/default.conf" ports: - "80:80" restart: always
It is easy to understand. two services are defined here. php-fpm depends on php: 7.0.7-fpm-alpine image, and the src folder is mapped to the/var/www/html folder, the tengine service depends on the php-fpm service and links the php-fpm service to communicate with the php-fpm container over the network. the tengine service is based on the chasontang/tengine: 2.1.2 _ f image, and set nginx. VL. default. the conf file is mapped to/etc/nginx/conf. d/default. conf file. Next let's take a look at nginx. VL. default. conf.
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; fastcgi_pass php-fpm:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #}}
The tengine image actually uses two configuration files:/etc/nginx. conf, and/etc/nginx/conf. d/all files in the directory, because/etc/nginx. use include/etc/nginx/conf in conf. d /*. conf; contains this directory, that is, you do not need to worry about other nginx configurations. Instead, you only need to replace the default virtual host configuration with your own nginx virtual host configuration, or add the virtual host configuration.
As shown above, default. the conf file defines a location matching include. php URL, and then split it into The PATH_INFO parameter to pass these variables to the php-fpm: 9000 php-fpm service.
It should be noted that, because Nginx and the PHP-FPM are not on the same host, so Nginx only static file processing and routing forwarding, the actual PHP file execution occurs in the PHP-FPM container. Therefore, the SCRIPT_FILENAME variable must use the directory in the PHP-FPM container, so hard encoding is used here. Of course, two containers can also share the same data volume. However, I believe that this is only for convenience of container orchestration, and there is no benefit to other containers.