Docker Compose 可以让我们用一个文件来描述应用需要的环境,在上面定义应用需要的各种服务,比如 web,数据库,脚本解释,缓存等等,我们也可以配置服务需要的网络与数据卷。这篇文章会创建一个运行 PHP 应用的环境,代码可以在 github 上找到,参考宁皓网课程《Docker:容器化应用》。
定义了下面这些服务:
- db:使用 mariadb 作为应用的数据库
- php:解释 php 脚本,使用 php-fpm
- web:使用 NGINX 作为应用的 web 服务器
- console:常用工具
- redis:缓存
- phpmyadmin:管理数据库的 web 界面
准备
在本地安装 Docker for Mac 或 Docker for Windows ,然后启动 docker ,再打开你喜爱的文本编辑器与命令行工具。为项目创建一个目录,在根目录下创建一个 docker-compose.yml 文件。
cd ~/desktop mkdir nest cd nest atom docker-compose.yml
目录结构
你可以按自己的喜好组织项目,下面是我用的方法,app 目录放的是应用的代码,services 下面是创建服务需要用的东西,有些服务需要我们自己去创建镜像,在一个 Dockerfile 文件里说明一下你想要的镜像是什么样的。在创建自定义镜像的时候也可能需要用到一些额外的文件,比如一些配置文件,一般在创建镜像的时候会把这些配置文件复制到镜像里。
├── app │ ├── index.html │ └── phpinfo.php ├── docker-compose.yml └── services ├── console │ ├── Dockerfile │ └── composer-1.1.3.phar ├── web │ └── config │ └── default.conf └── php ├── Dockerfile └── config ├── opcache-recommended.ini └── php.ini
Compose 文件
Compose 文件用的是 yaml 格式,文件一开始说明一下使用的 compose 版本,这个文件分成三个主要部分,services(服务),networks(网络),volumes (数据卷)。在这三部分主要的部分下面列出你需要的东西,docker 会自动为 compose 的项目创建一个默认的网络,你可以添加自己想要的网络,然后分配给指定的服务使用。在 volumes 下可以列出创建的数据卷,在定义服务的时候,你可以指定使用的数据卷,还有它对应的要挂载到的容器里的位置。
version: '2' services: # 定义的服务 networks: # 服务需要用的网络 volumes: # 服务需要用的数据卷
db
db 是数据服务,我打算用 mariadb ,先去给这个服务添加一个数据卷,然后在这个服务里使用一下这个数据卷,把数据库生成的数据放到这里,这样即使我们删除容器,数据服务里的数据也会保留在主机上,下回创建容器的时候,可以继续使用已有的数据。在 volumes 下面,添加一个名字是 db 的数据卷:
services: # ... volumes: db: driver: local
然后再去添加一个名字是 db 的服务,指定一下服务使用的镜像,这个镜像我们也可以用自己创建的 Dockerfile 去创建一下,或者你不打算定制镜像,也可以直接使用现成的,这里我用了 mariadb:10.1 这个镜像。注意最好设置具体要使用的版本。
这个镜像里有一些环境变量,我们可以在定义服务的时候去设置一下它们的值,这里我设置了 root 用户的密码,要创建的数据库,用户名,还有密码,你可以 根据自己的需求去修改这些变量的值。
在 db 服务上我用了 volumes 为它指定了一个 db 数据卷,挂载的位置是容器的 /var/lib/mysql ,这是存储数据库的默认的地方。
services: db: image: mariadb:10.1 environment: MYSQL_ROOT_PASSWORD: "root" MYSQL_DATABASE: "app" MYSQL_USER: "app" MYSQL_PASSWORD: "123123" volumes: - db:/var/lib/mysql
测试
在我们的 compose 文件里现在已经有了一个叫 db 的服务,在命令行工具下,进入到项目所在的目录,执行:
docker-compose up -d
Docker 会按照 compose 文件里的指示,创建应用需要的网络,数据卷,还有一个名字是 db 的数据服务,你可以进入到这个服务容器里看一下:
docker-compose exec db bash
进入以后使用 mysql 登录到数据库检查一下:
mysql -u root -p # 输入 root 用户密码,密码是在定义 db 服务的时候使用 MYSQL_ROOT_PASSWORD 变量设置的。 show databases; # 你能看到一个名字是 app 的数据库,这个数据库是在定义 db 服务的时候使用 MYSQL_DATABASE 变量设置的。
php
定义一个解释 php 的服务,我打算自己创建这个服务用的镜像,所以用了 build,而不是 image 。我告诉了 docker 自己要创建的这个镜像要使用的那个 Dockerfile 文件的位置(./services/php/Dockerfile)。
volumes 下面是服务用的数据卷,我把 compose 文件所在目录下的 app 这个目录,挂载到了容器的 /mnt/app 这个位置上了。这样我可以直接修改项目的代码,然后立即看到结果。
services: db: # ... php: build: context: ./services/php dockerfile: Dockerfile volumes: - ./app:/mnt/app
Dockerfile
构建 php 服务的镜像用的 Dockerfile 是 ./services/php/Dockerfile。FROM,设置了一下这个镜像要基于哪个镜像去创建,我用了 php:7.0-fpm 这个官方提供的镜像。MAINTAINER 设置了一下维护的作者。RUN 了几行命令,主要是去安装一些软件包,比如一些额外的 php 扩展。在安装这些扩展之前 ,你需要先安装扩展依赖的一些其它的软件包,这里的 libpng12-dev,libjpeg-dev,就是 gd 这个 php 扩展需要的软件包。然后我又 COPY 了两个配置文件到镜像里面。
FROM php:7.0-fpm MAINTAINER wanghao <wanghao@ninghao.net> RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev \ && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install gd mysqli pdo_mysql zip opcache COPY ./config/php.ini /usr/local/etc/php/conf.d/ COPY ./config/opcache-recommended.ini /usr/local/etc/php/conf.d/
php.ini
自定义的 php 配置。把你想要的 php 配置放到这个文件里,重新 build 镜像,然后再次启动 php 服务的时候会用新的镜像重新创建容器,这样配置就会生效了。
memory_limit = 256M post_max_size = 100M upload_max_filesize = 100M
opcache-recommended.ini
这是 php 的 opcache 扩展的配置文件。我发现在开发 WordPress 的时候,如果使用了 opcache ,修改代码以后并不能立即看到结果。所以在开发阶段可以关掉 opcache 功能,在生产环境打开它,会提高代码的执行效率。
opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 # 关闭 opcache 可以让 opcache.enable=0,打开 opcache 可以让 opcache.enable=1 # 在开发应用的时候可以关掉 opcache 。 opcache.enable=0
web
web 服务我打算用 nginx ,用 image 指定了要使用的镜像,ports 设置了发布的端口号,让主机上的 8080 对应容器的 80 。depends_on 可以设置服务的依赖,我让 web 服务依赖之前定义的 php ,这样会先启动 php ,然后再启动 web 。volumes_from 设置了让这个 web 服务使用在 php 服务里定义的数据卷。
另外我又用了 volumes 自己设置了一个数据卷,让主机上包含 nginx 配置文件的目录,对应容器里的 nginx 服务的配置文件目录,这样我就可以直接在本机上修改 nginx 的配置文件,然后重新启动一下 web 服务,配置就可以生效了。这种方法适用在开发环境上,因为修改了配置不需要重新 build 镜像,在生产环境中,你需要自己 build 这个 nginx 镜像,把想要的配置直接复制到镜像里去。
services: db: # ... php: # ... web: image: nginx:1.11.1 ports: - "8080:80" depends_on: - php volumes_from: - php volumes: - ./images/nginx/config:/etc/nginx/conf.d
default.conf
./images/nginx/config/default.conf,这是一个最基本的 nginx 配置文件,设置了应用的 root 是在 /mnt/app 这里,我们已经把主机上的 app 目录挂载到了容器里的 /mnt/app 这个地方。
注意这条指令:fastcgi_pass php:9000; 这里的 php 是我们定义的 php 这个服务的名字,在 compose 文件里定义的服务,它们之间可以使用服务的名字相互进行沟通,因为 docker 内置了 DNS 功能。
server { listen 80; server_name localhost; root /mnt/app; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
console
在开发应用的时候还会用到一些小工具,比如 php 包管理 composer ,WordPress 项目管理 wp-cli,Drupal 项目管理 drush 等等。我想把它们单独放到一个服务里面,这个服务用的镜像我需要自己去创建。
services: db: # ... php: # ... web: # ... console: build: context: ./images/console dockerfile: Dockerfile volumes_from: - php tty: true
Dockerfile
./services/console/Dockerfile,这是为 console 服务准备的创建镜像用的文件。
FROM php:7.0 MAINTAINER wanghao <wanghao@ninghao.net> WORKDIR /mnt/app # 常用工具 RUN apt-get update && apt-get install -y git curl wget cron vim locales libfreetype6-dev mariadb-client \ && rm -rf /var/lib/apt/lists/* \ && pecl install zip \ && docker-php-ext-enable zip \ && docker-php-ext-install mysqli pdo_mysql # 把语言设置成简体中文 RUN dpkg-reconfigure locales && \ locale-gen C.UTF-8 && \ /usr/sbin/update-locale LANG=C.UTF-8 RUN echo 'zh_CN.UTF-8 UTF-8' >> /etc/locale.gen && \ locale-gen ENV LC_ALL C.UTF-8 ENV LANG zh_CN.UTF-8 ENV LANGUAGE zh_CN.UTF-8 # 配置 git RUN git config --global user.name "wanghao8080" \ && git config --global user.email "117663444@qq.com" # 安装与配置 composer # Composer 官方安装脚本 https://getcomposer.org/download/ # 因为在国内下载 composer 太慢,我们直接把下载下来的 composer 复制到容器里使用。 COPY ./composer-1.1.3.phar /usr/local/bin/composer RUN chmod +x /usr/local/bin/composer RUN echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc \ && . ~/.bashrc \ && composer config -g repo.packagist composer https://packagist.phpcomposer.com # 安装管理 WordPress 项目的 wp-cli RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ && chmod +x wp-cli.phar \ && mv wp-cli.phar /usr/local/bin/wp # 安装管理 Drupal 项目的 drush RUN php -r "readfile('http://files.drush.org/drush.phar');" > drush \ && chmod +x drush \ && mv drush /usr/local/bin
redis
redis 服务可以当缓存用,WordPress,Drupal,Laravel 这些应用都会用到它。定义这个服务只是简单的用了一个官方的镜像,设置了一个公布的端口号。
services: db: # ... php: # ... web: # ... console: # ... redis: image: redis:3.2.1 ports: - "6379:6379"
phpredis
我打算配置 Drupal 8 使用 Redis,除了需要 redis 服务以外,我还需要安装 phpredis 这个 php 扩展。打开创建 php 服务镜像用的 Dockerfile(./services/php/Dockerfile),下面这段代码是去下载指定版本的 phpredis 扩展:
ENV PHPREDIS_VERSION 3.0.0 RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \ && tar xfz /tmp/redis.tar.gz \ && rm -r /tmp/redis.tar.gz \ && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis
然后再用 php 官方镜像里的 docker-php-ext-install 这个脚本去安装 php 扩展:
&& docker-php-ext-install ... redis
现在这个 Dockerfile 看起来是这样的:
FROM php:7.0-fpm MAINTAINER wanghao <wanghao@ninghao.net> ENV PHPREDIS_VERSION 3.0.0 RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \ && tar xfz /tmp/redis.tar.gz \ && rm -r /tmp/redis.tar.gz \ && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev \ && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install gd mysqli pdo_mysql zip opcache redis COPY ./config/php.ini /usr/local/etc/php/conf.d/ COPY ./config/opcache-recommended.ini /usr/local/etc/php/conf.d/
phpmyadmin
phpmyadmin 是管理 mysql 或 mariadb 数据库的 web 界面。这个服务用了 phpmyadmin/phpmyadmin 这个镜像,发布了端口,让主机的 8081 对应容器的 80 端口,这样访问 主机:8081 的时候就会打开这个管理数据库的 web 界面,镜像里设置了一些环境变量,在这个服务里用了一些,主要是告诉 phpmyadmin 数据库的主机是谁,管理数据库使用的用户是谁,密码是什么。
services: db: # ... php: # ... web: # ... console: # ... redis: # ... phpmyadmin: image: phpmyadmin/phpmyadmin ports: - "8081:80" environment: PMA_HOST: "db" PMA_USER: "root" PMA_PASSWORD: "root"
启动
在 app 目录下创建一个 phpinfo.php ,内容是:
<?php phpinfo(); ?>
进入到项目下面,执行:
docker-compose up -d
打开浏览器,访问:
http://localhost:8080/phpinfo.php
你会看到 php 信息页面。
Docker
评论
剪藏到印象笔记里!
8 年 4 个月 以前
赞!nest 仓库越来越完善了!期待 docker swarm 教程和开发→测试→生产环境流程的视频教程~~
8 年 4 个月 以前
我觉得是不是应该把 nginx 与 php 放到一个容器里,然后再做一个 nginx 用来当代理,这样在一个集群里就可以同时运行多个在 80 端口的网站了。
8 年 4 个月 以前
我看了一下官方对于 Docker-compose 的最佳实践,里面写到建议每个服务都单独运行在一个容器中……所以其实我觉得,php 应该跟 nginx 分开……这样就只需要一个 nginx 就好了,也可以做负载均衡
8 年 4 个月 以前
嗯,再研究吧,先用用再说。
8 年 4 个月 以前
皓哥,Redis 直接安装了就能用?
8 年 4 个月 以前
是啊,我是按 Drupal 8 的要求配置的,我在 Drupal 8 上试了,可以用。
8 年 4 个月 以前
Windows 下无法构建 Console 镜像……
RUN apt-get update && apt-get install -y git curl wget cron vim libfreetype6-dev mariadb-client \
&& rm -rf /var/lib/apt/lists/* \
&& yum reinstall glibc-common -y \
&& pecl install zip \
&& docker-php-ext-enable zip \
&& docker-php-ext-install mysqli pdo_mysql
这段没法执行……貌似是 yum reinstall glibc-common -y 这段的问题
8 年 4 个月 以前
收到!
8 年 4 个月 以前
修改了一下设置容器支持的语言的方法,之前整错了。
8 年 4 个月 以前
git clone 下来, 然后
docker-compose build
提示要
db uses an image, skipping
Building php
ERROR: Couldn't connect to Docker daemon - you might need to run `docker-machine start default`.
但是我的 default 是启动运行的,已经运行过了docker-machine start default 这是咋回事呢?
8 年 4 个月 以前
通过命令 eval "$(docker-machine env default)" 解决了这个问题。
8 年 4 个月 以前
docker-machine ls 之后空空如也,default不见了,但是docker还好好的在运行是啥意思?
8 年 3 个月 以前
因为你安装了 docker for windows 。之前的 docker toolbox 里面在本机创建了一个默认的 machine(虚拟机),docker for windows 不再使用 virtualbox 虚拟机了。
8 年 3 个月 以前
楼主, 这个帮了我很大的忙, 目前您这个docker lnmp方案是我见过文件夹结构最合理的, 不过目前有一些问题向您反映 , 就是docker 官方默认的php-fpm, php-cli 镜像从 jessie 变成了stretch , 所以导致编译的时候偶尔会失败, 把dockerfile 里面的镜像源加上后缀就好了。
6 年 3 个月 以前
谢谢啊,这个项目只是为了在课程里介绍一下 Docker Compose 的使用方法。具体用的话,你还得自己根据实际情况改改:)
6 年 3 个月 以前