0x00 概述

通过阅读之前的小节,相信大家对 Docker 在开发中的应用已经有了一定的了解。作为一款实用的软件,我们必须回归到实践中来,这样才能更好地理解 Docker 的实用逻辑和背后的原理。在这一小节里,我们就举一个完整的例子,让大家跟随这个项目的脉络,熟悉如何通过 Docker 和 Docker Compose 来搭建应用开发环境。

0x01 设计项目的目录结构

在这一小节里,我们以一个由 MySQL、Redis、PHP-FPM 和 Nginx 组成的小型 PHP 网站为例,介绍通过 Docker 搭建运行这套程序运行环境的方法。

既然我们说到这个小型网站是由 MySQL、Redis、PHP-FPM 和 Nginx 四款软件所组成的,那么自然在 Docker 里,我们要准备四个容器分别来运行它们。而为了更好地管理这四个容器所组成的环境,我们这里还会使用到 Docker Compose。

与搭建一个软件开发项目类似,我们提倡将 Docker Compose 项目的组成内容聚集到一个文件目录中,这样更利于我们进行管理和迁移。

这里我已经建立好了一个目录结构,虽然我们在实践的过程中不一定要按照这样的结构,但我相信这个结构一定对你有所启发。

简单说明一下这个结构中主要目录和文件的功能和作用。在这个结构里,我们可以将根目录下的几个目录分为四类:

  • 第一类是 Docker 定义目录,也就是 compose 这个目录。在这个目录里,包含了 docker-compose.yml 这个用于定义 Docker Compose 项目的配置文件。此外,还包含了我们用于构建自定义镜像的内容。

  • 第二类是程序文件目录,在这个项目里是 mysql、nginx、phpfpm、redis 这四个目录。这些目录分别对应着 Docker Compose 中定义的服务,在其中主要存放对应程序的配置,产生的数据或日志等内容。

  • 第三类是代码目录,在这个项目中就是存放 Web 程序的 website 目录。我们将代码统一放在这个目录中,方便在容器中挂载。

  • 第四类是工具命令目录,这里指 bin 这个目录。我们在这里存放一些自己编写的命令脚本,我们通过这些脚本可以更简洁地操作整个项目。

0x02 编写 Docker Compose 配置文件

接下来我们就要编写 docker-compose.yml 文件来定义组成这个环境的所有 Docker 容器以及与它们相关的内容了。docker-compose.yml 规则和编写的方法在前两小节中已经谈到,这里我们就不再展开,直接来看看编写好的 docker-compose.yml 配置文件。

version: ""

networks:
frontend:
backend: services: redis:
image: redis:3.2
networks:
- backend
volumes:
- ../redis/redis.conf:/etc/redis/redis.conf:ro
- ../redis/data:/data
command: ["redis-server", "/etc/redis/redis.conf"]
ports:
- "6379:6379" mysql:
image: mysql:5.7
networks:
- backend
volumes:
- ../mysql/my.cnf:/etc/mysql/my.cnf:ro
- ../mysql/data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
ports:
- "3306:3306" phpfpm:
build: ./phpfpm
networks:
- frontend
- backend
volumes:
- ../phpfpm/php.ini:/usr/local/etc/php/php.ini:ro
- ../phpfpm/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
- ../phpfpm/php-fpm.d:/usr/local/etc/php-fpm.d:ro
- ../phpfpm/crontab:/etc/crontab:ro
- ../website:/website
depends_on:
- redis
- mysql nginx:
image: nginx:1.12
networks:
- frontend
volumes:
- ../nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ../nginx/conf.d:/etc/nginx/conf.d:ro
- ../website:/website
depends_on:
- phpfpm
ports:
- "80:80

使用合适的镜像是提高工作效率的途径之一,这里讲解一下我们在这个项目中选择镜像的原由。

在这个项目里,我们直接采用了 MySQL、Redis 和 Nginx 三个官方镜像,而对于 PHP-FPM 的镜像,我们需要增加一些功能,所以我们通过 Dockerfile 构建的方式来生成。

对于 MySQL 来说,我们需要为它们设置密码,所以原则上我们是需要对它们进行改造并生成新的镜像来使用的。而由于 MySQL 镜像可以通过我们之前在镜像使用方法一节所提到的环境变量配置的方式,来直接指定 MySQL 的密码及其他一些关键性内容,所以我们就无须单独构建镜像,可以直接采用官方镜像并配合使用环境变量来达到目的。

对于 Redis 来说,出于安全考虑,我们一样需要设置密码。Redis 设置密码的方法是通过配置文件来完成的,所以我们需要修改 Redis 的配置文件并挂载到 Redis 容器中。这个过程也相对简单,不过需要注意的是,在官方提供的 Redis 镜像里,默认的启动命令是 redis-server,其并没有指定加载配置文件。所以在我们定义 Redis 容器时,要使用 command 配置修改容器的启动命令,使其读取我们挂载到容器的配置文件。

自定义镜像

相比较于 MySQL、Redis 这样可以通过简单配置即可直接使用的镜像不同,PHP 的镜像中缺乏了一些我们程序中必要的元素,而这些部分我们推荐使用自定义镜像的方式将它们加入其中。

在这个例子里,因为需要让 PHP 连接到 MySQL 数据库中,所以我们要为镜像中的 PHP 程序安装和开启 pdo_mysql 这个扩展。

了解如何安装扩展,这就要考验我们之前在 Docker Hub 镜像使用一节中学到的知识了。我们通过阅读 PHP 镜像的介绍页面,可以找到 PHP 镜像中已经为我们准备好了扩展的安装和启用命令,这让我们可以很轻松地在镜像中加入扩展。

在准备好这些使用方法之后,我们就可以开始编写构建 PHP 镜像的 Dockerfile 文件了。这里我已经编写好了一份,供大家参考。

FROM php:7.2-fpm

MAINTAINER You Ming <youming@funcuter.org>

RUN apt-get update \
&& apt-get install -y --no-install-recommends cron RUN docker-php-ext-install pdo_mysql COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh ENTRYPOINT ["docker-entrypoint.sh"] CMD ["php-fpm"]

由于 Docker 官方所提供的镜像比较精简,所以在这个 Dockerfile 里,我们还执行了 cron 的安装命令,来确保我们可以使用定时任务。

大家注意到,这里除了我们进行功能安装外,还将一个脚本拷入了镜像中,并将其作为 ENTRYPOINT 启动入口。这个文件的作用主要是为了启动 cron 服务,以便我们在容器中可以正常使用它。

#!/bin/bash

service cron start

exec "$@"

在 docker-entrypoint.sh 里,除了启动 cron 服务的命令外,我们在脚本的最后看到的是 exec "$@"这行命令。$@ 是 shell 脚本获取参数的符号,这里获得的是所有传入脚本的参数,而 exec 是执行命令,直接执行这些参数。

如果直接看这条命令大家会有些疑惑,参数怎么拿来执行,这不是有问题么?

请大家回顾一下,我们之前提到的,如果在镜像里同时定义了 ENTRYPOINT 和 CMD 两个指令,CMD 指令的内容会以参数的形式传递给 ENTRYPOINT 指令。所以,这里脚本最终执行的,是 CMD 中所定义的命令。

目录挂载

在这个例子里,我们会把项目中的一些目录或文件挂载到容器里,这样的挂载主要有三种目的:

  • 将程序的配置通过挂载的方式覆盖容器中对应的文件,这让我们可以直接在容器外修改程序的配置,并通过直接重启容器就能应用这些配置;

  • 把目录挂载到容器中应用数据的输出目录,就可以让容器中的程序直接将数据输出到容器外,对于 MySQL、Redis 中的数据,程序的日志等内容,我们可以使用这种方法来持久保存它们;

  • 把代码或者编译后的程序挂载到容器中,让它们在容器中可以直接运行,这就避免了我们在开发中反复构建镜像带来的麻烦,节省出大量宝贵的开发时间。

上述的几种方法,对于线上部署来说都是不适用的,但在我们的开发过程中,却可以为我们免去大量不必要的工作,因此建议在开发中使用这些挂载结构。

0x03 编写辅助脚本

我们知道,虽然 Docker Compose 简化了许多操作流程,但我们还是需要使用 docker-compose 命令来管理项目。对于这个例子来说,我们要启动它就必须使用这样的 docker-compose 命令来管理项目。对于这个例子来说,我们要启动它就必须使用这样的:

$ sudo docker-compose -p website up -d

而执行的目录必须是 docker-compose.yml 文件所在的目录,这样才能正确地读取 Docker Compose 项目的配置内容。

我编写了一个 compose 脚本,用来简化 docker-compose 的操作命令。

#!/bin/bash

root=$(cd `dirname $`; dirname `pwd`)

docker-compose -p website -f ${root}/compose/docker-compose.yml "$@"

在这个脚本里,我把一些共性的东西包含进去,这样我们就不必每次传入这些参数或选项了。同时,这个脚本还能自适应调用的目录,准确找到 docker-compose.yml 文件,更方便我们直接调用。

通过这个脚本来操作项目,我们的命令就可以简化为:

$ sudo ./bin/compose up -d

$ sudo ./bin/compose logs nginx

$ sudo ./bin/compose down

当然,我们还可以编写像代码部署、服务重启等脚本,来提高我们的开发效率。

Docker学习笔记之编写 Docker Compose 项目的更多相关文章

  1. Docker学习笔记之搭建 Java Web 项目运行环境

    0x00 概述 Java Web 泛指以 Java 程序为基础向外提供 Web 服务的技术及相关工具,狭义上来说,我们也可以说 Java Web 是由 Servlet 程序提供的 Web 服务. 对我 ...

  2. Docker学习笔记之使用 Docker Compose 管理容器

    0x00 概述 通过之前的介绍,我们已经基本掌握了构建.运行容器的方法,但这还远远不够,由于 Docker 采用轻量级容器的设计,每个容器一般只运行一个软件,而目前绝大多数应用系统都绝不是一个软件所能 ...

  3. Docker学习笔记之搭建Docker运行环境

    0x00 概述 既然 Docker 是一款实用软件,我们就不得不先从它的安装说起,只有让 Docker 运行在我们的计算机上,才能更方便我们对 Docker 相关知识和使用方式的学习.得益于与商业性的 ...

  4. Docker学习笔记六:Docker搭建企业级私有仓库

    前言 Docker不仅是一个强大的服务器部署工具,而且它还有一个官方的Docker Hub registry用于储存Docker镜像.上传镜像到Docker Hub是免费的,上传的镜像文件同时也对公共 ...

  5. Docker学习笔记之了解 Docker 的核心组成

    0x00 概述 在掌握 Docker 的一些背景知识后,我们还不得不花费一节的篇幅来简单介绍有关 Docker 核心的一些知识.当然,大家不要觉得有“核心”这类的词,我们就要在这一节中深入 Docke ...

  6. docker 学习笔记21:docker连接网络的设置

    1.如果docker主机不需要通过代理连接外网 则docker的相关命令(如docker search)或docker容器与网络相关的操作都可以正常进行,不需要特殊设置. 2.当docker主机 是通 ...

  7. docker 学习笔记20:docker守护进程的配置与启动

    安装好docker后,需要启动docker守护进程.有多种启动方式. 一.服务的方式 因为docker守护进程被安装成服务.所以,可以通过服务的方式启停docker守护进程,包括查看状态. sudo ...

  8. Docker学习笔记二:Docker常用命令及提升拉取镜像的速度

    一.Docker命令: 1.docker images   //命令用来查看docker中所包含的镜像信息 2.docker ps -a    //命令用来查看docker中所包含所有容器信息(运行状 ...

  9. Docker学习笔记——1.2 Docker组件

    Docker的核心组件包括: Docker客户端和服务器,也称为Docker引擎: Docker镜像: Registry: Docker容器. 1.Docker客户端和服务器 Docker是一个客户端 ...

随机推荐

  1. (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m

    在linux环境下配置项目运行环境时,部署的人员都会分配一下内存,以保证程序正常的运行.其实在开发的时候(window系统),就已经涉及到内存分配了,只是这些参数有默认值,因此一直没有去重视它. 以M ...

  2. Oracle查看用户密码过期,修改永不过期

    01.查看当前open用户 select username,account_status,expiry_date,profile from dba_users; 02.查看目前的密码过期策略 sele ...

  3. 天气服务API文档 第1版

    HTTP接口设计文档 此文档为开发HTTP接口的设计文档,目前用于提供天气查询的相关接口. 测试的时候使用 URL=http://www.dennisthink.com/test/api/weathe ...

  4. mac nginx compile

    编译 ./configure \ --prefix=/usr/local/services/nginx-1.14.0 \ --with-openssl=/Users/gavin/Downloads/s ...

  5. 5.C#释放非托管资源1

    释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托 ...

  6. iOS 设计模式-NSNotificationCenter 通知中心

    通知介绍 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发布通知(NSNotification),描述 ...

  7. vue中兄弟组件间 的传值 bus(可以理解为公共交通)

    点击大儿子(头部组件的年月日,下面的都要变化) 就相当于点击了年月日之后,下面的组件重新调接口,参数变化       1.首先随便哪儿写个bus.js 内容如下: import Vue from 'v ...

  8. matlab 字符串处理函数

    https://www.cnblogs.com/emanlee/archive/2012/09/13/2683912.html % 字符串处理 a='  a';b='b  b';c='cccc';m= ...

  9. MySQL.配置MariaDB的字符集

    配置MariaDB的字符集 环境: 操作系统:CentOS Linux release 7.x mariadb安装及配置 yum install mariadb-server mariadb #安装 ...

  10. Linux SSH 免秘钥登录

    SSH 免秘钥登录 ssh:是一种安全加密协议 ssh  username@hostname     ssh gongziyuan.com:以当前用户登录该机器(如果不是当前用户,需要这么干:ssh ...