- Запуск PHP приложения на Docker контейнерах (PHP-FPM, Nginx, PostgreSQL)
- 1. Установка Docker
- 2. Создание образов
- DockerFileNginx
- DockerFilePostgresql
- DockerFile
- Создание образов на основе DockerFile’ов
- Запуск образов на сервере
- Очередная статья про Docker для новичка [nginx + php-fpm + postgresql + mongodb]
Запуск PHP приложения на Docker контейнерах (PHP-FPM, Nginx, PostgreSQL)
За последний год программное обеспечение для автоматизации развертывания в среде виртуализации на уровне операционной системы набирает большие обороты. Эта статья послужит новичкам в этой сфере примером, как нужно упаковывать свое приложение в Docker контейнеры.
В классическом виде, PHP приложение представляет из себя следующие составляющие:
1. Установка Docker
Для начала работы, нам потребуется Docker. Скачать его можно на официальном сайте Docker.
2. Создание образов
Docker создает образы на основе DockerFile файлов, в котором описывается функционал. Мы создадим 3 образа для наших составляющих.
DockerFileNginx
FROM nginx:mainline-alpine RUN set -ex \ && addgroup -g 82 -S www-data \ && adduser -u 82 -D -S -G www-data www-data \ && mkdir -p /etc/pki/nginx/ \ && apk update \ && apk --no-cache add --update openssl \ && openssl dhparam -out /etc/pki/nginx/dhparams.pem 4096 \ && sed -i -e 's/user\s*nginx;/user www-data www-data;/g' /etc/nginx/nginx.conf \ && sed -i -e 's/worker_processes\s*1;/worker_processes auto;/g' /etc/nginx/nginx.conf \ && rm -rf /var/cache/apk/* COPY config/website.conf /etc/nginx/conf.d/website.conf
В данном DockerFile мы создаем пользователя www-data с группой 82 и устанавливаем Nginx. Последняя строка COPY предполагает, что у вас конфигурация приложения лежит в папке config/website.conf. Она скопируется в /etc/nginx/conf.d/website.conf.
DockerFilePostgresql
FROM postgres:9.5.2 RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 ENV LANG en_US.utf8
В этом образе, мы будем отталкиваться от образа postgres:9.5.2 и выполним команду для определения локали и языка.
DockerFile
FROM alpine:edge # Timezone ENV TIMEZONE Europe/Moscow ENV PHP_MEMORY_LIMIT 1024M ENV MAX_UPLOAD 128M ENV PHP_MAX_FILE_UPLOAD 128 ENV PHP_MAX_POST 128M RUN set -ex \ && addgroup -g 82 -S www-data \ && adduser -u 82 -D -S -G www-data www-data \ && echo "@testing http://dl-4.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ && apk update \ && apk upgrade \ && apk add --update tzdata \ && cp /usr/share/zoneinfo/$ /etc/localtime \ && echo "$" > /etc/timezone \ && apk --update add --no-cache php7-fpm@testing php7-mcrypt@testing php7-openssl@testing php7-json@testing php7-mysqli@testing php7-session@testing php7-gd@testing php7-xmlreader@testing php7-xmlrpc@testing \ php7-zip@testing php7-iconv@testing php7-curl@testing php7-zlib@testing php7@testing php7-ctype@testing php7-pgsql@testing php7-pdo_pgsql@testing bash rsync \ && sed -i -e "s/;daemonize\s*=\s*yes/daemonize = no/g" /etc/php7/php-fpm.conf \ && sed -i -e "s/listen\s*=\s*127.0.0.1:9000/listen = [::]:9000/g" /etc/php7/php-fpm.d/www.conf \ && sed -i -e "s/;chdir\s*=\s*\/var\/www/chdir = \/usr\/src\/app/g" /etc/php7/php-fpm.d/www.conf \ && sed -i -e "s/user\s*=\s*nobody/user = www-data/g" /etc/php7/php-fpm.d/www.conf \ && sed -i -e "s/group\s*=\s*nobody/group = www-data/g" /etc/php7/php-fpm.d/www.conf \ && sed -i -e "s/;clear_env\s*=\s*no/clear_env = no/g" /etc/php7/php-fpm.d/www.conf \ && sed -i -e "s/;catch_workers_output\s*=\s*yes/catch_workers_output = yes/g" /etc/php7/php-fpm.d/www.conf \ && sed -i "s|;date.timezone =.*|date.timezone = $|" /etc/php7/php.ini \ && sed -i "s|memory_limit =.*|memory_limit = $|" /etc/php7/php.ini \ && sed -i "s|upload_max_filesize =.*|upload_max_filesize = $|" /etc/php7/php.ini \ && sed -i "s|max_file_uploads =.*|max_file_uploads = $|" /etc/php7/php.ini \ && sed -i "s|post_max_size =.*|post_max_size = $|" /etc/php7/php.ini \ && sed -i "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/" /etc/php7/php.ini \ && apk del tzdata \ && rm -rf /var/cache/apk/* COPY . /usr/src/app RUN chown -R www-data:www-data /usr/src/app EXPOSE 9000 CMD ["php-fpm7"]
Этот образ послужит нам основным образом для нашего приложения. Сначала мы устанавливаем все необходимое для PHP и PHP-FPM. Далее, мы копируем текущую папку приложения в /usr/src/app, где будет распологаться наше приложение. В самом конце мы запускаем PHP-FPM.
Создание образов на основе DockerFile’ов
И так, у нас есть есть DockerFile’ы, на основе которых мы должны создать образы. Образы создаются очень просто. Достаточно выполнить следующие команды:
docker build -t myusername/myproject-nginx:latest -f DockerfileNginx .
docker build -t myusername/myproject-postgresql:latest -f DockerfilePostgreSql .
docker build -t myusername/myproject:latest .
В дальнейшем советую добавить к этим командам —no-cache, чтобы каждый раз не компилировать составляющие.
Мы создаем образы, прикрепляем их к нашему аккаунту на Docker Hub. Теперь, нам нужно отправить наши образы на репозиторий в Docker Hub. Выполняем следующие команды:
docker push myusername/myproject-nginx:latest
docker push myusername/myproject-postgresql:latest
docker push myusername/myproject:latest
Запуск образов на сервере
Мы почти у цели! Нам осталось загрузить образы из репозитория и запустить их. Загружаем их с помощью следующих команд:
docker pull myusername/myproject-nginx:latest
docker pull myusername/myproject-postgresql
docker pull myusername/myproject
Осталось их запустить. Делается это так же просто.
docker run —name myproject-nginx -d -p 80:80 myusername/myproject-nginx:latest
docker run —name myproject-postgresql9.5.2 -d -p 5432:5432 myusername/myproject-postgresql9.5.2:latest
docker run —name myproject -d -p 9000:9000 myusername/myproject:latest
Вуаля! Наше приложение запущено на Docker контейнерах. И тем не менее, всем читателям-новичкам я бы обязательно ознакомиться с документацией Docker.
Всем желаю успехов в освоении новых технологий!
Очередная статья про Docker для новичка [nginx + php-fpm + postgresql + mongodb]
Всем доброго времени суток. Вдохновленный целым набором статей на тему поднятия окружения на докере, я решил поделиться своим опытом по данному вопросу.
Сразу оговорюсь, эта статья так сказать «от новичка новичку», поэтому постараюсь подробно рассказать обо всех сложностях и вопросах, которые у меня возникли в процессе настройки окружения в Docker.
Добро пожаловать под кат!
Итак, все что я опишу ниже, я буду делать на ноутбуке известной «фруктовой компании», но так как ранее я делал тоже самое на VDS под управлением Centos 7, то я буду делать небольшие лирические отступления с описание, как я это делал на VDS.
Начнем мы естественно в регистрации на docker hub, который будет выступать в качестве системы контроля версий, но только для наших контейнеров. Docker hub при бесплатном использовании позволяет иметь только 1 приватный репозиторий, поэтому мы будем каждый отдельный образ помечать соответствующими тегами — nginx, php7-fpm. Я не буду описывать создание репозитория, думаю, ни у кого с этим проблем не возникнет.
Теперь мы можем установить сам docker на нашу рабочую станцию — в моем случае это описание находится здесь.
При установке Docker на Mac у нас сразу устанавливается docker toolbox, в котором есть необходимый нам инструмент docker-compose. Мы будем использовать его для объединения наших контейнеров о общее окружение.
yum -y install python-pip pip install docker-compose
Далее логинимся в нашем Докере:
Теперь нам доступны наши приватные репозитории (правда там пока пусто), там правда сейчас пусто, но мы это скоро исправим:)
Для своего проекта я сделал следующую структуру файлов:
├── contaners # Директория с кастомными образами для docker │ ├── fpm │ │ ├── Dockerfile │ │ └── conf │ │ └── fpm.conf # Необходимые настройки fpm, у меня файл пустой:) │ └── nginx │ ├── Dockerfile │ └── conf │ └── nginx.conf ├── database # Директоря для хранения баз данных ├── docker-compose.yml ├── logs # Директоря для хранения логов └── php-code # Директоря с php кодом ├── html └── index.php
При отсутствии директории html образ контейнера nginx не запуститься, где-то я находил, что проблема решается указанием WORKDIR в Dockerfile, но эта директория все-равно понадобится.
Для проекта я буду использовать кастомные образы для nginx и fpm, поэтому их я вынес в отдельные директории. Кастомные образы описываются при помощи Dockerfile. Вот мои:
FROM php:fpm MAINTAINER nickname RUN apt-get update && apt-get install -y \ libmcrypt-dev \ && apt-get install -y libpq-dev \ && docker-php-ext-install -j$(nproc) mcrypt \ && pecl install mongodb && docker-php-ext-enable mongodb RUN docker-php-ext-install mbstring RUN docker-php-ext-install exif RUN docker-php-ext-install opcache RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ && docker-php-ext-install pgsql pdo_pgsql COPY conf/ /usr/local/etc/php-fpm.d/ CMD ["php-fpm"]
FROM nginx:latest MAINTAINER nickname COPY ./conf /etc/nginx/conf.d/
docker-php-ext-enable # в случае, если расширение уже установлено (как у меня через pecl)
Вся подготовительная работа проведена проведена, теперь надо описать как наши контейнеры будут взаимодействовать. Как я уже писал, делать это мы будем через docker-compose и правила взаимодействия нужно описать в файле docker-compose.yml. Вот мой:
nginx: dockerfile: ./Dockerfile # путь до докер файла указываем относительно директории в build build: ./contaners/nginx ports: - 80:80 volumes: - ./logs:/etc/logs/nginx volumes_from: - fpm:rw environment: - NGINX_HOST=localhost - NGINX_PORT=80 command: nginx -g "daemon off;" # Можно было указать в докер-фале, но можно и здесь) links: - fpm fpm: dockerfile: ./Dockerfile build: ./contaners/fpm volumes: - ./php-code:/var/www:rw
Если мы указываем относительные пути к файлам и деректориям, то они должны обязательно начинаться с точки (как указание текущей директории), т.е., например, такой путь contaners/nginx/Dockerfile — будет интерпретирован не верно.
Теперь можно запустить наши контейнеры:
аргумент -d указываем для того, чтобы отвязать работу контейнеров от консоли, т.е. запустить в режиме демона
Все, наши контейнеры работают и линкуются, но кое-что я сделал не так, а именно — мы для запуска контейнера всегда ссылаемся на Dockerfile, это не очень удобно. Сделаем так:
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2d6263b52380 test_nginx "nginx -g 'daemon off" 8 minutes ago Up 8 minutes 443/tcp, 0.0.0.0:8080->80/tcp test_nginx_1 04370a9e1c73 test_fpm "php-fpm" 8 minutes ago Up 8 minutes 9000/tcp test_fpm_1 docker tag 2d6263b52380 my-login/repo:nginx docker tag 2d6263b52380 my-login/repo:fpm docker push my-login/repo:nginx docker push my-login/repo:php7-fpm
Теперь наши контейнеры управляются через docker hub и Dockerfile’ы нам больше не нужны.
Исправим docker-compose.yml:
nginx: image: my-login/repo:nginx ports: - 80:80 volumes: - ./logs:/etc/logs/nginx volumes_from: - fpm:rw environment: - NGINX_HOST=localhost - NGINX_PORT=80 command: nginx -g "daemon off;" # Можно было указать в докер-фале, но можно и здесь) links: - fpm fpm: image: my-login/repo:php7-fpm volumes: - ./php-code:/var/www:rw
А вот теперь я понял, что забыл добавить расширение pcntl для php. Но это легко поправить.
Для начала подключимся к нужному контейнеру:
docker exec -it 04370a9e1c73 bash
И добавить необходимое расширение:
docker-php-ext-install pcntl
Отлично, в контейнер добавили, но мы же хотели использовать docker hub в качестве VCS — значит надо закомитить изменения:
docker commit -m "added pcntl ext" 04370a9e1c73 my-login/repo:php7-fpm
docker push my-login/repo:php7-fpm
Добавим еще контейнеры баз данных (postgresql и mongodb):
nginx: image: my-login/repo:nginx ports: - 80:80 volumes: - ./logs:/etc/logs/nginx volumes_from: - fpm:rw environment: - NGINX_HOST=localhost - NGINX_PORT=80 command: nginx -g "daemon off;" links: - fpm fpm: image: my-login/repo:php7-fpm volumes: - ./php-code:/var/www:rw links: - mongo - postgres mongo: image: mongo ports: - 27017:27017 # Проброс портов для внешнего доступа volumes: - ./database/mongo:/data/db postgres: image: postgres:latest ports: - 5432:5432 # Проброс портов для внешнего доступа volumes: - ./database/postgres:/data/postgres environment: POSTGRES_PASSWORD: POSTGRES_USER: postgres PGDATA : /data/postgres
Докер добавит нам новые контейнеры к уже запущенным. Но я открыли порты для внешнего доступа, но мы указали пароль только для PostgreSql, нужно тоже самое сделать и для mongodb — как это сделать (и не только) подробно описано здесь.
docker exec -it some-mongo mongo admin connecting to: admin > db.createUser( < user: 'jsmith', pwd: 'some-initial-password', roles: [ < role: "userAdminAnyDatabase", db: "admin" >] >); Successfully added user: < "user" : "jsmith", "roles" : [ < "role" : "userAdminAnyDatabase", "db" : "admin" >] >
Базы данных из php теперь доступны по хостам postgres и mongo соответственно, т.е. для подключения, например, к mongodb мы должны написать следующее:
$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");
На этом все, спасибо за внимание. Прошу учесть, что все описанное является исключительно моим собственным опытом и не претендует на звание идеального подхода к настройке окружения через docker.