使用 Docker 制作 LNMP环境

by kingzcheung on June 16, 2018

为什么要使用docker 打包环境?

主要解决 lnmp 安装繁琐,浪费时间,同时又能解决开发与部署的一致性问题。

安装 docker 和 docker-compose

制作LNMP 环境之前,我们需要安装 docker 和 docker-compose,docker-compose 主要为多容器做编排,因为我们需要多个容器共同合作。

#docker
sudo yum install docker-io #centos

#docker-compose 
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

#check version
docker --version
docker-compose --version

构建

创建构建目录,我们需要一个放置项目代码的目录,比如命名为apps;还需要放置构建镜像的目录,比如命名为images;LNMP中,我们平台还要查看一些软件的日志文件,我们不可能非常麻烦的跑到容器中去查看,因此需要一个共享卷来共享日志,比如命名为 logs.

NGINX

通过 docker-compose.yml 来编排各个容器。默认暂不需要为nginx添加额外的东西,我们可以使用官方镜像即可(如果需要添加额外的插件,则要自己根据宋子文镜像构建)。

我们需要把nginx的一些配置通过共享卷的方式共享方便配置。

nginx:
    image: nginx:1.15.0
    container_name: dnmp-nginx
    ports:
      # 共享 80,443 端口
      - "80:80"
      - "443:443"
    depends_on:
      - php
    # 同 php 一起共享 ./apps 目录
    volumes_from:
      - php
    # 创建一个数据卷,由 ./images/nginx/config => /etc/nginx/conf.d
    volumes:
      - ./images/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./images/nginx/config:/etc/nginx/conf.d:ro
      - ./images/nginx/certificate/:/etc/nginx/ssl/:ro
      - ./images/nginx/logs/:/var/log/dnmp/:rw
      - ./apps:/var/www/html/:rw
    restart: always
    networks:
      - net-php

PHP-FPM

PHP-FPM 镜像需要自己额外构建,因为官方的镜像带的扩展很少,基本无法满足一个复杂项目的需求。以php7.1为例子,在images中添加php71目录,在php71中添加Dockerfile,内容如下:

FROM php:7.1-fpm

MAINTAINER kingzcheung <i@kingzcheung.com>

COPY ./sources.list /etc/apt/sources.list

RUN apt-get update --fix-missing && apt-get install -y \
        g++ autoconf bash git apt-utils libxml2-dev libcurl3-dev pkg-config telnet \
        libxml2 \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng-dev \
        libmemcached-dev zlib1g-dev \
    && echo "Asia/Shanghai" > /etc/timezone \
    && docker-php-ext-install iconv curl mbstring xml json mcrypt mysqli pdo pdo_mysql zip soap \
    && docker-php-ext-configure gd \
            --with-gd \
            --with-freetype-dir=/usr/include/ \
            --with-png-dir=/usr/include/ \
            --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install gd \
    && docker-php-ext-enable gd \
    && pecl install memcached \
    && docker-php-ext-enable memcached \
    && apt-get autoremove -y \
    && apt-get clean \
    && rm -rf /var/cache/apt/* \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /pecl \
    && mkdir -p /usr/local/bin \

因为容器默认源太慢了,这里使用了163的源,sources.list内容如下:

deb http://mirrors.163.com/debian/ stretch main non-free contrib
deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib
deb http://mirrors.163.com/debian/ stretch-backports main non-free contrib
deb http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib

deb-src http://mirrors.163.com/debian/ stretch main non-free contrib
deb-src http://mirrors.163.com/debian/ stretch-updates main non-free contrib
deb-src http://mirrors.163.com/debian/ stretch-backports main non-free contrib
deb-src http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib

这里添加了一些常见的扩展。注意的是,Dockerfile 的RUN 命令最好使用&&连接而不是添加多个RUN。

最后在docker-compose.yml 使用此镜像进行构建。

php:
    # image: php:7.1-fpm
    env_file:
      - .env
    environment:
      MEMCACHE_HOST: memcache
    build: ./images/php71/ #如果使用php7.2 请修改php71为php72
    container_name: dnmp-php
    expose:
      - "9000"
    volumes:
    # 我们同样要把php的配置文件共享出来,方便配置
      - ./images/php71/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
      - ./images/php71/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
      - ./apps:/var/www/html/:rw
      - ./logs/:/var/log/dnmp/:rw
    networks:
      - net-php
    depends_on:
      - memcache
    links:
      - memcache:${MEMCACHE_HOST}

MySQL

同样的,mysql 只需要使用官方镜像就ok了,相关配置直接参照官方配置。

大项目一般数据库都是另外的服务器,项目只要远程连接就行,因此也可以把此配置去除。

# MySQL 5.7: https://hub.docker.com/_/mysql/
  database:
    image: mysql:5.7
    container_name: dnmp-mysql
    environment:
      MYSQL_ROOT_PASSWORD: "root"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: ""
    volumes:
      - ./images/mysql57/databases:/var/lib/mysql:rw
    expose:
      - "3306"
    ports:
      - "3306"

MEMCACHE

缓存方面一样只要官方镜像就能满足需求:

memcache:
    image: memcached:1.4
    container_name: dnmp-memcache
    networks:
      - net-php
    expose:
      - "11211"
    ports:
      - "11211:11211"

最终目录如下:

├── apps
│   ├── index.html
│   └── test.php
├── docker-compose.yml
├── images
│   ├── memcache
│   │   └── Dockerfile # 如果不需要额外配置,可以不使用
│   ├── nginx
│   │   ├── certificate #证书目录
│   │   │   ├── ca.crt
│   │   │   ├── ca.key
│   │   │   ├── full_chain.pem
│   │   │   └── private.key
│   │   └── config #虚拟主机配置目录
│   │       └── default.conf
│   └── php71
│       ├── Dockerfile
│       └── sources.list
├── logs
│   └── nginx.error.log 
└── README.md

最终配置如下:

version: "2"
services:
  # 配置缓存
  memcache:
    image: memcached:1.4
    container_name: dnmp-memcache
    networks:
      - net-php
    expose:
      - "11211"
    ports:
      - "11211:11211"

  nginx:
    image: nginx:1.15.0
    container_name: dnmp-nginx
    ports:
      # 将主机的 8080 端口映射到容器的 80 端口
      - "80:80"
      - "443:443"
    depends_on:
      - php
    # 同 php 一起共享 ./apps 目录
    volumes_from:
      - php
    # 创建一个数据卷,由 ./images/nginx/config => /etc/nginx/conf.d
    volumes:
      - ./images/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./images/nginx/config:/etc/nginx/conf.d:ro
      - ./images/nginx/certificate/:/etc/nginx/ssl/:ro
      - ./images/nginx/logs/:/var/log/dnmp/:rw
      - ./apps:/var/www/html/:rw
    restart: always
    networks:
      - net-php

  # PHP 7.1-fpm: https://hub.docker.com/_/php/
  php:
    # image: php:7.1-fpm
    env_file:
      - .env
    environment:
      MEMCACHE_HOST: memcache
    build: ./images/php71/ #如果使用php7.2 请修改php71为php72
    container_name: dnmp-php
    expose:
      - "9000"
    volumes:
      - ./images/php71/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
      - ./images/php71/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
      - ./apps:/var/www/html/:rw
      - ./logs/:/var/log/dnmp/:rw
    networks:
      - net-php
    depends_on:
      - memcache
    links:
      - memcache:${MEMCACHE_HOST}

  # MySQL 5.7: https://hub.docker.com/_/mysql/
  database:
    image: mysql:5.7
    container_name: dnmp-mysql
    environment:
      MYSQL_ROOT_PASSWORD: "root"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: ""
    volumes:
      - ./images/mysql57/databases:/var/lib/mysql:rw
    expose:
      - "3306"
    ports:
      - "3306"

networks:
  net-php:

最后运行 以下命令构建:

sudo docker-compose up -d --build

如果你要进入某一个容器,只要运行下面的命令:

sudo docker exec -it dnmp-nginx bash
#dnmp-nginx 就是上面定义的container_name,当然,你也可以使用id,
#bash 表示进入容器所使用的shell

需要注意的地方

  1. 所有的共享卷共享的配置目录时都是以容器为准,而不是宿主机的目录。
  2. 项目的权限问题,项目在宿主机中权限会完全带到容器中,因此需要注意权限问题可能导致无法运行。
  3. 如果配置出现问题,构建时是不会提示的,需要自己通过 sudo docker ps 来确认容器是否已经构建成功。
  4. 各个容器之间的通信问题,比如正常情况下,php 代码中是无法通过127.0.0.1 来连接memcache的,因为memcache 并不存在于php或者nginx容器中。这里我们需要通过容器互联来做到容器之间的通信 。https://yeasy.gitbooks.io/docker_practice/content/network/linking.html