在第二篇文章中,我们学会了使用 dockerfile 构建 docker 镜像,看起来已经能够满足我们的日常需求了。无论需要什么环境,在 dockerfile 里逐步构建,然后 build、run,就 ok 了,也满足了我们docker 隔离性、快速部署的要求,那为什么还会有这一节?
来看一个网站开发最常见的场景:我们要有数据库,网站应用,nginx,互相配合才是完整的环境。是的,我们完全可以以 ubuntu 为基础镜像,把这些一股脑全装进去,然后运行。但是这样有很多缺点,比如我们每次都要重新装 mysql 而不是直接利用 mysql 官方的基础镜像,升级维护不方便;如果我们的应用要扩展也很难,因为每个应用都连接的自己内部的数据库,无法共享数据;事实上,这种方式是典型的虚拟机的使用方式,不是 docker 的正确打开方式。
docker 是轻量化的应用程序,docker 官方推荐每个 docker 容器中只运行一个进程(下篇文章你将明白这是为什么),那么就是说,我们需要分别为我们的应用、数据库、nginx 创建单独的 docker 容器,然后分别启动它。想象一下,构建好 docker 之后,每次启动我们的网站,都要至少 docker run 三次,是不是很繁琐?而且此时这几个 docker 是分散独立的,很不方便管理。既然这几个 docker 都是为了同一个网站服务,是不是应该把它们放到一起?这就引出了 docker-compose 项目。
docker-compose是 docker 官方的开源项目,使用 python 编写,实现上调用了 Docker 服务的 API 进行容器管理。其官方定义为为 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)),其实就是上面所讲的功能。
一:安装
默认情况下,windows 和 mac 下的 docker 已经自带了 docker-compose 工具,可以使用 `docker-compose -v` 命令查看。
对于 linux 系统,需要自己手动安装,可以直接下载二进制文件安装,参考官方文档,更酷的是,可以直接使用 pip 安装,甚至可以使用 docker-compose 容器(比较复杂,不推荐~)。
二:简介
类似 docker 的Dockerfile文件,docker-compose使用 YAML 文件对容器进行管理。
对于 docker-compose 有两个基本的概念:
- 服务(service):一个应用容器,即 docker 容器,比如之前所说的mysql 容器、nginx 容器
- 项目(project):由一组关联的应用容器组成的一个完整业务单元,比如上面所讲的由 mysql、web app、nginx 容器组成的网站。docker-compose 面向项目进行管理。
再简单说下 YAML 文件格式。
1.大小写敏感,缩进表示表示层级关系
2.缩进空格数不重要,相同层级左侧对齐即可。(不允许使用 tab 缩进!)
3.由冒号分隔的键值对表示对象;一组连词线开头的行,构成一个数组;字符串默认不使用引号
这些基本够我们使用了,详细的格式说明可参考这篇YAML 语言教程-阮一峰。
三:使用
接下来我们使用 docker-compose 构建一个php 网站项目,并逐步讲解其使用。
- 我们需要一个网站项目,这里以 summerblue 的larabbs论坛系统为例,这个项目使用了 mysql、redis,我们可以验证搭建是否成功。从远程仓库把其克隆到本地,然后开始我们今天的工作。
2. 在项目文件夹下创建 docker-compose.yml文件。
3. 先在 docker-compose.yml 文件里添加如下代码,构建我们的 php 应用。
version: '2'
services:
# our web application
app:
build:
context: ./
dockerfile: app.dockerfile
volumes:
- ./:/var/www
working_dir: /var/www
environment:
- DB_HOST=database
- REDIS_HOST=redis
下面解释下我们的代码。
- version: 表示我们的compose文件的版本,目前有1,2,3,每个版本语法不尽相同,这里以版本2为例,具体可参考 docker-compose 文档。
- services: 即我们要开始定义服务,每个docker容器为一个服务。
- app: 这里我们定义了第一个服务,app 为其名字
- build: 指定该容器构建参数
- volumes: 与 dockerfile 中 -v 参数相似,这里是将当前文件夹挂载到容器的/var/www 目录下
- working_dir: 指定容器工作目录
- environment: 设置环境变量。由于 laravel 框架在环境变量已有值的情况下不会加载.env 配置,这里 DB_HOST和 REDIS_HOST 就是.env 文件中配置数据库连接的参数,我们设置它以便连接docker 的数据库,database 和 redis 是接下来定义的服务名称。
这是 app.dockerfile 文件的内容,之前都讲过,不再细说。
FROM php:7.1.22-fpm
# 安装必要的 php 依赖包
RUN apt-get update \
&& apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev \
&& apt-get clean
# 安装 php 扩展
RUN docker-php-ext-install pdo pdo_mysql mcrypt zip gd
4. 创建一个 composer 服务以安装 composer 依赖,docker-compose.yml 文件添加以下内容
# install dependencies
composer:
image: prooph/composer:7.1
volumes:
- ./:/var/www
working_dir: /var/www
command: install
注意这里同样将当前文件夹挂载到了容器中,因此对文件所做的更改都直接作用于本地文件,而我们的 app 容器也挂载了当前文件夹,这里安装的包文件都能生效。
5. 创建一个 nginx 服务,docker-compose.yml 文件添加以下内容
# web server
nginx:
build:
context: ./
dockerfile: nginx.dockerfile
volumes:
- ./public:/var/www/public
ports:
- 80:80
- 这里将 public 文件夹挂载到了容器中, nginx 直接返回了静态文件,否则你将看到网站格式乱了,因为获取不到 css 等文件,如果是单纯后端可以不挂载此文件夹
- ports 将nginx容器的80端口映射到本机80端口
nginx.dockerfile 文件内容,为了添加默认配置文件
FROM nginx:1.10
ADD vhost.conf /etc/nginx/conf.d/default.conf
vhost.conf 文件,nginx 配置文件
server {
listen 80;
server_name www.larabbs.test
index index.php index.html;
root /var/www/public;
error_log /var/log/nginx/error.log notice;
access_log /var/log/nginx/access.log main;
location / {
try_files $uri /index.php?$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
- 这里配置了 server_name,因为 larabbs 使用了sudo-su包,不配置域名访问会有权限问题。
- fastcgi_pass app:9000 这里将动态请求转发给了 app 容器的9000端口,即 php-fpm 服务的端口,默认 docker-compose 同一项目下的容器是在同一个网络中,无需映射可以直接互相访问。
6. 创建数据库服务,docker-compose.yml 文件添加以下内容
# mysql database
database:
image: mysql:5.7
environment:
- "MYSQL_ROOT_PASSWORD=root"
- "MYSQL_DATABASE=larabbs"
- "MYSQL_USER=homestead"
- "MYSQL_PASSWORD=secret"
# redis database
redis:
image: redis:5
- 这里 database 和 redis 是服务名,也即在 app 服务的 environment 变量设置的参数,因此 app 才能访问到我们容器中的数据库,当然你也可以使用其它名字。
- environment 这里我们设置了 mysql 的数据库及用户名密码,即是 env 文件中配置的用户名密码,mysql 容器会自动初始化,然后应用才有权限连接。
到目前为止配置文件已经写完了,完整的文件可以在我的 github 看到。
7. 启动项目
docker-compose 最常用的命令就是 docker-compose up 了,该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。
因此,在项目文件夹下执行此命令。注意要在项目文件夹下,否则 docker-compose 找不到 docker-compose.yml 文件,也不知道你是如何配置的。
时间可能较长,请耐心等待。如果遇到网络故障,可以重试。
简单介绍下输出:
可以看到 此时app 服务已启动,等待连接。
redis 服务已启动。
database 服务已启动,在3306端口等待连接。
composer 服务准备开始安装依赖。
composer 服务完成任务退出。
当然顺序可能不同,但正常情况下nginx、mysql、redis、app 服务都已经启动,运行正常。
另外,你可以给命令加上-d 参数,以忽略输出,当然第一次运行还是仔细观察输出为好,将来使用时可以这么做。
8. 项目使用
首先执行下 docker ps,可以看到目前有4个容器,容器名都加上了项目前缀。
此时你可以使用 docker exec 命令进入相应容器执行初始化操作,因为 docker-compose 本身也是调用的 docker api,但是既然使用了 docker-compose 来管理项目,我们必然有更方便的方式。
执行 docker-compose ps,可以至查看当前项目的容器状态。
可以看到,四个服务正在运行,而 composer 服务已经执行完任务退出。
如果要容器执行命令,直接 docker-compose exec service_name command 更方便。
比如,进入我们的 nginx 容器,nginx 即是 YAML 文件里定义的服务名。
接下来,我们进行网站的初始化工作。即生成秘钥、初始化数据库等。
app 也是 YAML 文件里定义的服务名。
一切都顺利进行。
对了,因为上面提到 sudo-su的原因,你必须使用域名访问网站,在 host 文件添加 larabbs.test到127.0.0.1的映射即可。
然后,你应该就可以正常访问 http://larabbs.test 网站了。
8. 容器的停止
直接 control-c 或者 docker-compose stop 即可。
注意下次docker-compose up 默认仍会继续使用之前的容器和数据。
必要时你可以添加--build 参数重新构建镜像,或者--force-recreate参数重新创建容器。
更多资料:docker 三剑客-compose
闲言
这个系列写了三篇了,算是把 docker 的基本使用介绍完了,更准确的说,应该是把我会用的介绍完了。因为本身是开发工程师,更着重于日常开发过程中使用,经验不多,对于运维视角的 docker不甚了解,而 docker 更大的意义应该是在运维层,所以有介绍的不合适甚至错误的地方,欢迎指出,或者分享你对 docker 的理解与使用。
另外,接下来会介绍 docker 与虚拟机的区别,docker 的原理等,写起来会比较难,但我会尽快完成。相信看了之后,会对 docker 的实质有更深的理解,就算是吹牛逼也会更有自信,欢迎关注~