写点什么

Docker 搭建项目环境实战

用户头像
书旅
关注
发布于: 2020 年 08 月 24 日
Docker搭建项目环境实战

如果站在Docker的角度,软件就是容器的组合:业务逻辑容器、数据库容器、储存容器、队列容器......Docker使得软件可以拆分成若干个标准化的容器,然后像搭积木一样将它们组合起来



维基百科了一下微服务的定义:微服务(Microservices)是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块(Small Building Blocks)为基础,利用模块化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关(Language-Independent/Language agnostic)的API集相互通信



从定义中也能看出来,微服务很适合用Docker容器实现,每个容器承载一个服务。一台计算机同时运行多个容器,从而就能很轻松地模拟出复杂的微服务架构。软件把任务外包出去,让各种外部服务完成这些任务,软件本身只是底层服务的调度中心和组装层



在之前的两篇文章中,对docker的基础使用做了总结。本篇文章会通过Docker在本地的电脑上构建一个不依赖本地电脑的任何开发套件的Laravel项目环境(所有依赖均安装在docker容器中)。其中会通过docker安装:

  • PHP环境

  • Nginx环境

  • MySQL环境

并使用docker-compose将它们编排到一起,三者组成Laravel项目的环境



有对比才能发现使用docker-compose的好处,因此下边会通过两种方法来搭建



方法一:逐一构建

构建PHP环境

首先我在我的根目录下创建一个demos目录,并进入目录。然后直接使用官方的PHP镜像启动容器

1、mkdir demos
2、cd demos
3、docker container run \
--rm \
--name php \
--volume "$PWD/":/var/www/html \
php:7.1-apache





这个命令的含义是,基于PHP的一个image启动一个容器。7.1-apache是该PHP镜像的标签,安装的是php7.1版本,并且镜像自带了Apache服务器

  • --rm:容器停止运行后,自动删除容器文件

  • --name php:容器的名字叫做php

  • --volume "$PWD/":/var/www/html:将当前目录($PWD)映射到容器的/var/www/html(Apache对外访问的默认目录)。此时对当前目录下的所有修改都会被同步到容器中,这样外部就可以访问到



命令执行完成之后,命令行会提示容器对外的IP地址,通过这个IP地址,就可以正常的访问容器。因为我当前的目录下什么都没有,因此我创建一个index.php文件,内容如下:

<?php
phpinfo();

此时访问容器对外的IP,就会看到下如下页面



下载Laravel框架代码

在demos目录下执行如下命令下载laravel框架源码,我这里下载的是Larave5.8

composer create-project --prefer-dist laravel/laravel laravel "5.8.*"

此时,在浏览器中输入上边的IP,可以看到如下页面



安装MySQL

在一个新的窗口中执行如下命令

docker container run \
-d \
--rm \
--name db \
--env MYSQL_ROOT_PASSWORD=123456 \
--env MYSQL_DATABASE=wordpress \
mysql:5.7

同上边的php一样,上边也是基于官方的MySQL镜像启动一个容器(版本5.7)

-d:容器启动后,在后台运行

--rm:容器终止运行后,自动删除容器文件

--name db:容器的名字叫做db

--env MYSQLROOTPASSWORD=123456:向容器进程传入一个环境变量MYSQLROOTPASSWORD,该变量会被用作 MySQL的根密码

--env MYSQLDATABASE=laravel:向容器进程传入一个环境变量MYSQLDATABASE,容器里面的MySQL 会根据该变量创建一个同名数据库(本例是laravel)



自定义PHP环境

现在PHP容器和MySQL容器都已经有了。接下来,要把PHP容器连接到MySQL容器了。但是,PHP的官方image不带有mysql扩展,必须自己新建image文件



在demos目录下创建一个Dockerfile文件,内容如下:

FROM php:7.1-apache
RUN docker-php-ext-install mysqli
CMD apache2-foreground

上面代码的意思,就是在原来PHP的image基础上,安装mysqli的扩展。然后,启动Apache



基于这个 Dockerfile 文件,新建一个名为phpwithmysql的image文件

docker build -t phpwithmysql .



PHP容器连接MySQL

现在基于phpwithmysql镜像,重新启动一个PHP容器

docker container run \
--rm \
--name php \
--volume "$PWD/":/var/www/html \
--link db:mysql \
phpwithmysql

跟上一次相比,上面的命令多了一个参数--link db:mysql,表示PHP容器要连到db容器,冒号表示该容器的别名是mysql



这时还要改一下项目(laravel)目录的权限,让容器可以将配置信息写入这个目录(容器内部写入的/var/www/html目录,会映射到这个目录)

chmod -R 777 wordpress

此时我们就可以在laravel项目中连接MySQL了



对于上边的方法,我们需要手动启动两个容器,启动的时候,还要在命令行提供容器之间的连接信息,比较麻烦。下边用docker-compose来管理多个容器的联动



方法二:使用docker-compose工具

简介

compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。Docker官方给Compose的定位是:Defining and running multi-container Docker applications



本文重点不是介绍docker-compose,关于docker-compose的详细内容,可以看我的这篇文章:《docker-compose详解》



使用我们自定义的Dockerfile模板文件,可以让我们很方便的定义一个容器。搭建laravel项目环境会用到PHP、Nginx、MySQL这三个环境,我们会通过docker-compose工具将这三个容器编排在一起,组成laravel的项目环境



创建基本的目录结构

我在我的local_www目录下创建一个project目录,在该目录下创建如下文件,具体文件结构:

├── Readme.md
├── docker-compose.yml
├── services
│ ├── mysql
│ │ └── Dockerfile
│ ├── nginx
│ │ ├── Dockerfile
│ │ ├── conf.d
│ │ │ └── vhost.conf
│ ├── php
│ │ └── Dockerfile
│ └── redis
│ └── Dockerfile
└── laravel
...





编写docker-compose.yml文件



这里先提供完整的docker-compose.yml文件,下边会进行逐一的解释其含义

######文件头 start######
version: '2'
services:
######文件头 nd######
######App服务 start######
app:
build:
context: ./services/php
working_dir: /var/www
volumes:
- ./laravel:/var/www
environment:
- "DB_PORT=3306"
- "DB_HOST=database"
######App服务 end######
######web服务 start######
web:
build:
context: ./services/nginx
working_dir: /var/www
volumes_from:
- app
ports:
- 8081:80
######web服务 end######
######数据库服务 start######
database:
image: mysql:5.6
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=data_search"
- "MYSQL_USER=root"
- "MYSQL_PASSWORD=123456"
- "MYSQL_ROOT_PASSWORD=123456"
ports:
- "33066:3306"
volumes:
dbdata:
######数据库服务 end######



文件头部分

在docker-compose.yml文件中,把每个容器叫做一个服务,services下定义整个应用中用到的所有服务(即容器)



App服务部分

######App服务 start######
app:
build:
context: ./services/php
working_dir: /var/www
volumes:
- ./laravel:/var/www
environment:
- "DB_PORT=3306"
- "DB_HOST=database"
######App服务 end######
  • app:我们指定的服务名称

  • build:指定Dockerfile所在文件夹的路径(可以是绝对路径,或者相对docker-compose.yml文件的路径),Compose将会利用它自动构建这个镜像,然后使用这个镜像。也可以使用context指令指定Dockerfile所在文件夹的路径。使用dockerfile指令指定Dockerfile文件名。如果没有指定Dockerfile的名字,则默认为context指定的目录下的Dockerfile文件

  • working_dir:把工作目录设置成了/var/www,在容器中项目代码将会被放在/var/www目录下面

  • volumes:是容器内数据卷所挂载路径设置,在这里我们只定义一个数据卷,把宿主机laravel项目目录挂到在容器中的/var/www上,这样我们在本地电脑对项目代码进行的更改就会马上同步到容器中去,反过来也是一样,容器中对代码做的更改也会及时反馈到本地电脑的项目中

  • environment:设置环境变量名,我这里设置了DBPORT和DBHOST这样就不用修改项目中的.env文件里关于这两项的值了,当然任何你需要在开发环境单独设置的环境变量都可以写到这里,Laravel读取配置使用的DotEnv会检测是否系统有指定环境变量的设置,有的话就不会在去读取.env文件了(environment等同于docker run --env的作用)



local_www/project/services/php/Dockerfile文件的具体内容如下:

FROM php:7.1.22-fpm
# 更新一些安装包
RUN apt-get update
# 安装PHP和composer依赖
RUN apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev
# 清除检索到的软件包文件的本地存储库
RUN apt-get clean
# 在这里,可以安装测试和部署过程中所需的任何其他扩展
RUN apt-get clean; docker-php-ext-install pdo pdo_mysql mcrypt zip gd pcntl opcache bcmath
# 安装Composer以轻松管理您的PHP依赖项
RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# 安装 Node
RUN apt-get update &&\
apt-get install -y --no-install-recommends gnupg &&\
curl -sL https://deb.nodesource.com/setup_10.x | bash - &&\
apt-get update &&\
apt-get install -y --no-install-recommends nodejs &&\
npm config set registry https://registry.npm.taobao.org --global &&\
npm install --global gulp-cli
# 设置容器启动后默认执行的命令和参数
CMD php-fpm

说明:我在这个Dockerfile中安装了node和composer。这个主要是因为我在后边做前后端分离项目的时候需要。如果发布到生产环境,一般是使用单独的composer对项目代码进行构建而不是放在运行应用的容器里,容器的核心思想之一就是保持单一,这样才能做到快速增加相同角色的容器



web服务部分

上边已经完成了app服务部分,web服务使用nginx,它在docker-compose.yml中的内容如下:

######web服务 start######
web:
build:
context: ./services/nginx
working_dir: /var/www
volumes_from:
- app
ports:
- 8081:80
######web服务 end######

指定服务名为web。另外出现了两个新的配置名:volumes_from和ports

volumes_from:用来复用在app服务中定义的数据卷路径

ports:通过ports将本地电脑的8081端口映射到web容器的80端口,这样在开发环境中我们就不用设置hosts文件,直接通过IP加端口就能访问服务了



nginx的Dockerfile文件内容如下:

FROM nginx:1.17.0-alpine
# 更新安装源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
# 设置时区为上海
RUN apk update && apk add --upgrade \
&& apk add tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
ADD ./conf.d/vhost.conf /etc/nginx/conf.d/default.conf

把本地的vhost.conf复制到了容器的/etc/nginx/conf.d/default.conf中,这样基本的nginx配置就配置好了,vhost.conf中的定义如下:

server {
listen 80;
index index.php index.html;
root /var/www/public;
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;
}
}

这里需要注意的就是,fastcgi_pass app:9000; nginx将对PHP的请求通过fastcgi传递给了app服务的9000端口,docker-compose会自动把services中定义的容器服务连接起来,各个服务相互之间使用服务名称引用



数据库服务部分

数据库服务选择MySQL,稍微和上边两个服务有所不同的是,在php和nginx容器中,配置了本地的文件可以和容器中的文件进行同步和访问,这样可以使得我们的修改能够快速的在容器中生效。因为我们知道,当容器被销毁时,容器内的文件也就都被删除了,但是我们肯定是希望数据库容器中的文件可以被持久化,因此可以通过Docker的数据间来实现以上功能,但是,我们不用再把本地的文件挂载到数据卷上,Docker客户端会管理创建的数据卷在本地电脑上具体存储的位置。数据库服务在docker-compose.yml中的内容如下:

######数据库服务 start######
database:
image: mysql:5.6
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=data_search"
- "MYSQL_USER=root"
- "MYSQL_PASSWORD=123456"
- "MYSQL_ROOT_PASSWORD=123456"
ports:
- "33066:3306"
volumes:
dbdata:
######数据库服务 end######
  • 在最下方通过volumes命令创建了一个名为dbdata的数据卷,dbdata后面的冒号,这是YML文件的一个语法限制,不用太关心

  • 上边的volumes使用<name>:<dir>的格式,通知Docker,将dbdata数据卷挂在到容器中的/var/lib/mysql目录上

  • environments:设置的是Mysql的docker镜像需要的四个必要参数

  • ports:端口映射,将本地电脑的33066端口映射到容器的3306端口,这样就能通过电脑上的数据库工具连接到docker内的Mysql了



编排并启动服务

完整的docker-compose.yml内容如下:

version: '2'
services:
app:
build:
context: ./services/php
working_dir: /var/www
volumes:
- ./laravel:/var/www
environment:
- "DB_PORT=3306"
- "DB_HOST=database"
web:
build:
context: ./services/nginx
working_dir: /var/www
volumes_from:
- app
ports:
- 8081:80
database:
image: mysql:5.6
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=data_search"
- "MYSQL_USER=root"
- "MYSQL_PASSWORD=123456"
- "MYSQL_ROOT_PASSWORD=123456"
ports:
- "33066:3306"
volumes:
dbdata:

将三个应用容器关联在一起组成了项目的服务端环境,下边通过下面的命令启动服务,执行完后会启动上面文件里定义的三个服务。因为是第一次启动,docker客户端要下载上面提到的三个镜像并且构建服务所以启动速度会慢一些,等到下载完镜像并构建完成后,以后的启动都会很快

docker-compose up -d

报错X1

google一下,原因是国内网络问题,无法连接到docker hub

,修改一下自己的docker的配置,将docker的源改成:https://09sus5u2.mirror.aliyuncs.com。然后重新执行上边的命令

执行的稍微有些漫长...



报错X2(漫长的等待时这个结果...)

这个是因为第三步安装php和composer扩展的时候,一些扩展是需要访问国外的机器,因此我们需要换一下apt-get的源,在php的Dockerfile文件的第二行增加下边这行指令

RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list && rm -Rf /var/lib/apt/lists/* && cat /etc/apt/sources.list

OK,替换完之后,继续执行docker-compose up -d



报错X3(...)

google了一下,说是磁盘可能满了,我删了几个本地无用的镜像之后就可以了



OK,后边就顺利的完成了构建。如果你还没有初始化你的laravel框架,那么执行一下以下命令

docker-compose exec app composer install
docker-compose exec app php artisan key:generate

docker-compose exec就是将命令发送到指定的容器中去执行,当然你也可以直接在刚才的laravel目录下执行composer install和php artisan key:generate



此时在浏览器中输入localhost:8081就可以看见如下页面啦

我在docker-compose中没有专门对这三个服务做关联,这是因为默认docker-compose同一项目下的容器是在同一个网络中,无需映射可以直接互相访问



原理是这样的,当运行docker-compose up时,会分以下几步运行:

  • 创建一个名为project_default的网络(project为docker-compose.yml文件所在的目录名);

  • 使用app服务的配置创建容器,它以“app”这个名称加入网络project_default;

  • 使用web服务的配置创建容器,它以“web”这个名称加入网络project_default

  • 使用database服务的配置创建容器,它以“database”这个名称加入网络project_default



可以执行一下docker network ls,可以看到网络名为projectdefault的network ID,然后执行docker inspect 容器ID,查看docker-compose中编排的三个容器的network ID都是同一个,且和projectdefault的network ID是同一个



关于Dockerfile中的一些命令的含义以及docker-compose.yml中的一些配置和docker-compose相关的命令,会在下一篇文章进行整理



参考文章:



  • https://juejin.im/post/5cc665006fb9a0323e3ac68d

  • http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html

  • https://segmentfault.com/a/1190000018372614

  • https://www.bookstack.cn/read/docker_practice-1.2.0/compose-commands.md#99sj6t

  • https://segmentfault.com/a/1190000019423975

  • https://www.jianshu.com/p/410734e5b8eb

  • https://blog.csdn.net/qq_21127151/article/details/90549080





发布于: 2020 年 08 月 24 日阅读数: 167
用户头像

书旅

关注

公众号:IT猿圈 2019.04.11 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
老哥这篇文章《docker-compose详解》 链接是啥?
2020 年 10 月 04 日 19:27
回复
没有更多了
Docker搭建项目环境实战