宿主机环境
系统:ubuntu 18.04 docker版本:18.03.1-ce
docker的安装请自行参考官方文档。
前言
这篇文章用于介绍如何使用docker搭建一个LNMP开发环境,下面我们将会编写Dockerfile,基于官方docker hub镜像,分别构建我们自己的镜像,然后运行。要构建的镜像有:
- PHP-FPM 7.4
- Nginx 1.19
- MySQL 5.7
- Redis 6.2
为什么不直接使用官方镜像,而是自己再构建一个? 因为官方镜像一般安装的东西都很少,例如PHP-FPM的官方镜像,像redis、mysqli、pdo等等这些常用的扩展都是没有的,还有一些常用的系统命令也没有安装,例如ping、netstat、ps等等。
目录结构
我的项目路径是/home/rockyliang/mydocker ,目录结构如下:
html目录用于存放网页文件和PHP文件,这个目录后面会通过挂载的方式,挂载到 PHP-FPM容器 和 nginx容器 里面去,以便它们可以访问这些文件。
redis目录下有一个redis.conf文件,这是从网上下载的redis配置文件,因为官方的redis镜像默认是没有配置文件的,后面构建redis镜像时,我们会把这个下载的配置文件复制进去。这个文件的下载地址是: https://raw.githubusercontent.com/antirez/redis/6.2/redis.conf
注意URL里的版本号,因为我们要构建的redis镜像版本是6.2,因此配置文件版本也要用6.2的。
创建网络
因为各个容器之间需要通信,例如PHP-FPM容器需要跟MySQL、Redis容器连通,这样PHP才能查询数据库数据,因此我们先创建一个名为mynet 的网络:
sudo docker create mynet
创建好网络后,后续把各个容器加入到这个网络里面来就可以。
构建PHP-FPM镜像
Dockerfile文件
FROM php:7.4-fpm-buster
LABEL maintainer="ljfrocky" \
version="2020.03.30" \
description="PHP-FPM 7.4"
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib' > /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian-security buster/updates main' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib' >> /etc/apt/sources.list \
&& apt-get update
RUN apt-get install -y --no-install-recommends vim procps iputils-ping less net-tools iproute2
RUN chmod +x /usr/local/bin/install-php-extensions \
&& install-php-extensions redis mysqli pdo_mysql pcntl gd mcrypt bcmath sockets zip
RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
上面Dockerfile里有一个步骤是修改Debian系统的软件源,目的是为了加快apt-get 命令在国内下载软件的速度。修改的时候要注意源地址要跟基础镜像的系统版本相匹配,例如php:7.4-fpm-buster 这个镜像是基于Debian 10(代号buster)系统构建的,那么源地址里的系统代号也要保持一致,否则使用apt-get 命令的时候会报错:
如何查询镜像的系统版本、代号等信息? 执行命令sudo docker run {镜像ID或名字} cat /etc/os-release ,例如sudo docker run php:7.4-fpm-buster cat /etc/os-release
开始构建
执行docker build 命令构建FPM镜像,镜像名字设置为img-php-fpm ,标签(TAG)设置为7.4 :
cd /home/rockyliang/mydocker/php-fpm
sudo docker build -t img-php-fpm:7.4 .
因为国内访问github不太稳定,构建过程可能会在“安装install-php-extensions”这一步骤发生错误,一般重试构建多几次就可以解决。
构建完成后,执行docker images 命令,已经能看到新的镜像:
sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
img-php-fpm 7.4 d511ba9f4c3d 2 minutes ago 456MB
php 7.4-fpm-buster c1bb99843706 3 days ago 389MB
执行docker run 命令运行新镜像,容器名字设置为DOCKER_FPM ,为了让容器能访问到宿主机的html目录,还需要把宿主机的html目录挂载到容器里的/var/www/html 目录:
sudo docker run --name DOCKER_FPM \
-p 9000:9000 \
--network mynet \
-v /home/rockyliang/mydocker/html:/var/www/html:ro \
-d img-php-fpm:7.4
构建nginx镜像
Dockerfile文件
FROM nginx:1.19
LABEL maintainer="ljfrocky" \
version="2020.03.30" \
description="Nginx 1.19"
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib' > /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian-security buster/updates main' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib' >> /etc/apt/sources.list \
&& apt-get update
RUN apt-get install -y --no-install-recommends vim procps iputils-ping less net-tools iproute2
开始构建
执行docker build 命令构建nginx镜像,镜像名字设置为img-nginx ,标签(TAG)设置为1.19 :
cd /home/rockyliang/mydocker/nginx
sudo docker build -t img-nginx:1.19 .
构建完成后,执行docker run 命令运行新镜像,容器名字设置为DOCKER_NGINX ,为了让容器能访问到宿主机的html目录,还需要把宿主机的html目录挂载到容器里的/usr/share/nginx/html 目录:
sudo docker run --name DOCKER_NGINX \
-p 80:80 \
--network mynet \
-v /home/rockyliang/mydocker/html:/usr/share/nginx/html:ro \
-d img-nginx:1.19
容器运行起来后,进入容器修改nginx的配置文件,让nginx可以转发PHP请求:
sudo docker exec -it DOCKER_NGINX bash
vim /etc/nginx/conf.d/default.conf
将配置文件里的这一段 去除前面的注释,内容替换成:
在容器里重启nginx:nginx -s reload
测试
至此,nginx和PHP容器都搭建好了,测试一下看nginx能不能处理PHP请求,在宿主机的html目录里新建一个phpinfo.php文件:
<?php
phpinfo();
在浏览器访问一下http://localhost/phpinfo.php,内容显示正常:
构建MySQL镜像
Dockerfile文件
FROM mysql:5.7
LABEL maintainer="ljfrocky" \
version="2020.03.31" \
description="MySQL 5.7"
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib' > /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian-security buster/updates main' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib' >> /etc/apt/sources.list \
&& apt-get update
RUN apt-get install -y --no-install-recommends vim procps iputils-ping less net-tools iproute2
开始构建
执行docker build 命令构建MySQL镜像,镜像名字设置为img-mysql ,标签(TAG)设置为5.7 :
cd /home/rockyliang/mydocker/mysql
sudo docker build -t img-mysql:5.7 .
构建完成后,执行docker run 命令运行新镜像,容器名字设置为DOCKER_MYSQL ,root账号密码设置为123456 :
sudo docker run --name DOCKER_MYSQL \
-p 3306:3306 \
--network mynet \
-e MYSQL_ROOT_PASSWORD=123456 \
-d img-mysql:5.7
构建Redis镜像
Dockerfile文件
FROM redis:6.2-buster
LABEL maintainer="ljfrocky" \
version="2020.03.31" \
description="Redis 6.2"
COPY ./etc/redis.conf /usr/local/etc/redis/redis.conf
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib' > /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian-security buster/updates main' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib' >> /etc/apt/sources.list \
&& echo 'deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib' >> /etc/apt/sources.list \
&& apt-get update
RUN apt-get install -y --no-install-recommends vim procps iputils-ping less net-tools iproute2
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
开始构建
执行docker build 命令构建Redis镜像,镜像名字设置为img-redis ,标签(TAG)设置为6.2 :
cd /home/rockyliang/mydocker/redis
sudo docker build -t img-redis:6.2 .
构建完成后,执行docker run 命令运行新镜像,容器名字设置为DOCKER_REDIS :
sudo docker run --name DOCKER_REDIS \
-p 6379:6379 \
--network mynet \
-d img-redis:6.2
容器运行起来后,进入容器修改redis配置文件:
sudo docker exec -it DOCKER_REDIS bash
vim /usr/local/etc/redis/redis.conf
将bind 127.0.0.1 -::1 这一行改为bind * ,保存,退出容器,然后重启容器:
sudo docker restart DOCKER_REDIS
测试
在宿主机的html目录里新建一个test.php文件,用于测试PHP是否能查询MySQL和redis:
<?php
function display($msg) {
echo "{$msg}<br>";
}
$mysqlConn = new mysqli();
$ok = $mysqlConn->real_connect('DOCKER_MYSQL', 'root', '123456');
if (!$ok) {
exit("MySQL connect failed:" . $mysqlConn->connect_error);
}
$mysqlConn->close();
display("测试MySQL连接:OK");
$redis = new Redis();
try {
$redis->connect('DOCKER_REDIS', 6379, 2);
} catch (Throwable $e) {
exit("Redis connect failed:" . $e->getMessage());
}
$redis->close();
display("测试Redis连接:OK");
在浏览器访问一下http://localhost/test.php,连接数据库正常:
日志/数据/配置文件路径
在部署服务时,我们一般都会关注日志路径、数据存储路径、配置文件路径,下面列一下上面几个镜像的一些默认路径:
镜像 | 日志 | 配置文件 | 数据 |
---|
php:7.4-fpm-buster | 默认无日志文件,access log和error log都是输出到标准输出,需要使用docker logs 命令查看,如果需要将日志记录到文件里,请参阅后面章节内容 | /usr/local/etc (php.ini和php-fpm.conf等配置文件都在这个路径里) | | nginx:1.19 | /var/log/nginx/access.log /var/log/nginx/error.log 这两个文件都是软链接到标准输出的,无法查看里面的内容,只能通过docker logs 命令查看。解决方法见后面章节内容 | /etc/nginx | /usr/share/nginx/html | mysql:5.7 | /var/log/mysql | /etc/mysql | /var/lib/mysql | redis:6.2-buster | 默认无日志文件,日志都是输出到标准输出,需要使用docker logs 命令查看,如果需要将日志记录到文件里,请参阅后面章节内容 | 默认无配置文件,可以自己指定 | /data (rdb文件存放路径) |
记录日志到文件
通过上一章节,我们知道PHP-FPM、nginx、redis的日志是不记录到实体文件里的,只能通过docker logs {容器名称或ID} 命令来查看,那么,如果我们想把日志记录到实体文件里,可以怎么做呢?
PHP-FPM
进入PHP-FPM容器,修改/usr/local/etc/php-fpm.d/zz-docker.conf 文件: 新增图中两行内容,保存并重启容器,这样PHP-FPM的 access log 和 error log 就会记录到我们指定的文件里了。
nginx
nginx容器里的日志文件是个软链接:
root@adb13aee521c:/var/log/nginx# ls -l
total 0
lrwxrwxrwx 1 root root 11 May 12 2021 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 May 12 2021 error.log -> /dev/stderr
所以如果想把日志记录到文件里去,把软链接删除即可:
cd /var/log/nginx
unlink access.log
unlink error.log
nginx -s reload
redis
因为上面我们在构建redis镜像时,有通过COPY指令复制了一个redis.conf配置文件进去,我们通过修改配置文件来配置日志文件路径即可。
假设我们希望把日志文件保存在/var/log/redis 目录里,首先我们要创建好这个目录,并确保redis用户对这个目录有写入的权限:
mkdir /var/log/redis
chown redis:redis /var/log/redis/
修改/usr/local/etc/redis/redis.conf 文件: 修改logfile参数,指定日志文件路径,保存退出,重启redis容器。
docker-compose
如果你觉得像上面那样一个一个的构建镜像和运行太麻烦,可以试下用docker-compose来减轻工作量,在项目根目录里编写一个docker-compose.yml ,内容如下:
version: "3"
services:
nginx:
image: img-nginx:1.19
build:
context: ./nginx
dockerfile: Dockerfile
container_name: DOCKER_NGINX
ports:
- "80:80"
- "443:443"
networks:
- lnmp-network
volumes:
- ./html:/usr/share/nginx/html:ro
restart: always
depends_on:
- php-fpm
php-fpm:
image: img-php-fpm:7.4
build:
context: ./php-fpm
dockerfile: Dockerfile
container_name: DOCKER_FPM
volumes:
- ./html:/var/www/html:ro
networks:
- lnmp-network
restart: always
depends_on:
- mysql
- redis
mysql:
image: img-mysql:5.7
build:
context: ./mysql
dockerfile: Dockerfile
container_name: DOCKER_MYSQL
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: '123456'
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
networks:
- lnmp-network
restart: always
redis:
image: img-redis:6.2
build:
context: ./redis
dockerfile: Dockerfile
container_name: DOCKER_REDIS
ports:
- "6379:6379"
networks:
- lnmp-network
restart: always
networks:
lnmp-network:
driver: bridge
ipam:
config:
- subnet: '172.31.0.0/16'
然后执行sudo docker-compose up -d 命令,docker-compose就会一次性把所有镜像构建好,并且运行。
在搭建环境时遇到的一些坑
1.配置文件修改错了,导致容器启动不起来了
一般修改完配置文件,都需要重启容器来让新配置生效的,但如果不小心配置错了,导致容器起不来了就麻烦了,容器起不来,就无法进去重新修正配置文件。这时候可以通过docker cp 命令,把有问题容器里的配置文件复制到宿主机上来,然后在宿主机上修改,修改完后,再docker cp 回去容器里,这样就可以解决这个问题了。
|