用Docker搭建LNMP
程序员经常会说的一句话:在我的机器上是正常的,肯定是你的机器有问题。因此,Docker诞生了,它把应用所需要的一切东西都打包,从而可以很方便地进行部署。
Docker 的主要用途,目前有三大类:
- 提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
- 提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
- 组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
下面开始来体验Docker,操作系统为win10,Docker的安装请参考官方文档,这里略过。
Hello World
官方提供了一个程序员最喜欢的hello world镜像,我们就以此来开始Docker之旅,感受Docker的强大之处吧:
docker run hello-world
如果你看到输出Hello from Docker!
,恭喜你,Docker安装成功了。
image和container
镜像和容器可以理解为面向对象里面的类和对象。
上面的例子是直接拿官方的镜像来跑,我们也可以在官方镜像的基础上加工,来构建新的镜像,当然也可以不基于任何镜像来构建。
所有内容(代码、软件、环境等)都可以通过Dockerfile配置来构建image,通过image可以运行具体的container实例。
查看镜像列表:docker image ls
查看容器列表:docker container ls
如果要查看完整的列表,在ls
后面加个-a
选项就好了:docker container ls -a
装个Linux
下面加点难度,装个Linux,alpine体积小,很多开源镜像也是以它为基础(比如Nginx),很适合用来体验:
docker run -it --rm alpine
发现run后面多了几个选项,其中-it
其实是-i -t
的缩写,即interactive terminal,交互式终端的意思,不加这个我们是进不到容器的交互式终端的。
加了--rm,表示在容器退出后会自动删除容器,我们这里是为了体验,希望用完之后不要占用磁盘空间,所以加了删除选项,注意这里不是删除镜像,删除的是容器。
现在我们进入到alpine系统的终端了,输入命令查看系统版本试试:
cat /etc/os-release
体验完毕,这时候只要输入exit
按下回车就可以退出容器了。
如果想运行容器的时候直接输出系统版本,可以这么玩:
docker run --rm alpine cat /etc/os-release
在镜像名后面直接跟上你想运行的命令,输出系统版本信息以后容器就自动关闭并删除了。
运行PHP
好了,现在Linux系统装好了,直觉告诉我,如果要跑PHP,我们就直接进到刚刚那个alpine容器里面安装就好了,那么不好意思,你的姿势错了。先不解释,先看看正确姿势:
我们先在CLI模式下体验PHP,在这之前,我们可以看看官方的php-cli:7.2镜像的Dockerfile是怎么定义的:
FROM debian:stretch-slim
...此处省略若干行...
FROM指定基于哪个镜像,也可以不基于任何镜像:FROM scratch
,比如官方的alpine的Dockerfile:
FROM scratch
ADD alpine-minirootfs-3.10.0-x86_64.tar.gz /
CMD ["/bin/sh"]
由此可见,PHP镜像是建立在debian这个镜像的基础上的,所以我们不需要像在虚拟机上面装PHP一样,得先用iso安装一个Linux操作系统。
项目位置在D:\www\php-cli-demo,建立好文件夹以后,在项目下面新建一个index.php
文件:
<?php
echo phpversion();
再新建一个Dockerfile
文件:
FROM php:7.2-cli
WORKDIR /var/www/php-cli-demo
COPY . .
CMD ["php", "index.php"]
WORKDIR是指定容器的工作目录,我们需要把当前项目的代码拷贝到容器的操作系统所在的目录里,所以我们使用COPY,第一个点的意思是当前目录,第二个点的意思是容器的当前目录,意思就是把我win10系统下D:\www\php-cli-demo这个文件夹下的所有文件全部拷贝到容器的Debian系统下的当前目录(已通过工作目录指定为/var/www/php-cli-demo)下。
CMD就是容器运行起来以后所要执行的命令了,这里我们希望直接运行这个php文件。
在当前文件夹下,按住Shift
键,然后按下鼠标右键,出现右键菜单,这时候出现其中一项“在此处打开Powershell窗口(s)”,这时候我们只需要按下s
就可以进入命令行并且直接定位到当前文件夹了。
然后我们根据刚才的Dockerfile配置文件来建立镜像:
docker build -t php-cli-demo .
t是tag,打标签的意思。
最后那个点,指定了要打包的是哪个目录,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件,然后Docker引擎在解析上面的COPY指令时,就能知道要COPY哪些文件了,所以这个COPY的第一个参数的路径是在打包路径下展开的,是相对路径。
Docker默认会去找Dockerfile,也可以是别的文件名,例如你的Dockerfile叫build.conf,可以通过-f
参数指定:
docker build -t php-cli-demo -f build.conf .
好了,镜像构建成功了,现在马上来跑一下吧:
docker run --rm php-cli-demo
可以看到正确输出了PHP的版本号为:7.2.19。
build的时候把项目代码一块打包进image镜像文件了,那如果我要运行另外一个项目呢?要重新build一遍吗?其实这个image是可以复用的,只不过在运行的时候指定项目位置就行了:
docker run --rm -v ${PWD}:/var/www/php-cli-demo php-cli-demo
v是volume,数据卷的意思,${PWD}是宿主机当前目录,冒号后面是容器目录,意思就是把当前目录挂载到容器的/var/www/php-cli-demo目录。
不过不建议这么做,因为镜像是一层一层做缓存的,所以重复build不会占用太多空间,而且如果镜像做了改动,会影响到另外一个容器,所以还是单独build比较好。感兴趣的朋友可以自己做一下测试,然后用docker system df
查看空间占用情况。
如果像上面这种在基础镜像上,没有涉及到任何加工的,只需要把项目映射到容器就行了,可以直接这么玩:
docker run --rm -v ${PWD}:/var/www/demo -w /var/www/demo php:7.2-cli php index.php
现在,回过头来分析刚才的问题,为什么不建议在alpine容器里面直接安装PHP。
镜像构建时,会一层一层地构建,前一层是后一层的基础,这样可以达到复用的效果。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。
镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
所以,你在alpine容器里面直接安装PHP,如果不小心把容器删掉了,下一次再跑的时候,PHP就丢了。而且这是一个黑箱操作,别人并不知道这个容器里面有什么,所以最好还是通过Dockerfile构建镜像,然后再根据镜像来跑容器咯。
搭建LNMP
好了,上面的都搞明白之后我们就来跑个真正的PHP项目吧。我们需要用到PHP-FPM,Nginx,MySQL。项目结构是这么安排的:
- docker-php-demo
- app
- index.html
- index.php
- mysql
- Dockerfile
- nginx
- conf.d
- site.conf
- conf.d
- php
- Dockerfile
- app
我们先把Nginx装起来,跑个静态页面试试。直接跑官方镜像:
docker run --name nginx -d --rm nginx:1.16
打开浏览器输入localhost,就可以看到Welcome to nginx!的静态页了。这是Nginx的默认页面,我们需要加一个专门针对本次项目的配置。进入Nginx容器,查看配置文件路径:
docker exec -it nginx bash
nginx -V
看到配置文件路径为:
--conf-path=/etc/nginx/nginx.conf
查看配置文件:
cat /etc/nginx/nginx.conf
可以看到默认的配置还会去加载其他配置文件:
include /etc/nginx/conf.d/*.conf;
所以我们只需要把项目的配置通过-v
映射到容器的conf.d目录下就可以了。把当前的Nginx容器停掉:
docker stop nginx
然后编写site.conf:
server {
listen 80;
server_name local.docker-php-demo.com;
root /var/www/docker-php-demo;
}
在app目录放入index.html:
Hello Docker
因为Nginx本身我们不做加工,所以不用Dockerfile,直接用官方镜像就好了,然后分别把项目和Nginx网站配置都映射过去,在项目根目录下执行:
docker run --name dpd-nginx -d -v ${PWD}/app:/var/www/docker-php-demo -v ${PWD}/nginx/conf.d:/etc/nginx/conf.d -p 80:80 nginx:1.16
最后在本机配置hosts就大功告成啦,Win + R 打开运行窗口,输入drivers,打开etc/hosts文件加入一行域名配置:
127.0.0.1 local.docker-php-demo.com
浏览器打开local.docker-php-demo.com,到这里,我们就完成了让Nginx跑静态页面的配置了。我们的项目是PHP,把fpm装起来,然后再让Nginx连上就行了,在php目录下编写Dockerfile:
FROM php:7.2-fpm
RUN cp /usr/local/etc/php/php.ini-development /usr/local/etc/php/php.ini \
&& docker-php-ext-install pdo_mysql
上面我们使用了镜像提供的安装扩展的快捷操作方法来安装pdo_mysql扩展。
在app目录放入index.php:
<?php
phpinfo();
构建运行:
cd php
docker build -t dpd-php .
cd ..
docker run --name dpd-php -d -v ${PWD}/app:/var/www/docker-php-demo -p 9000:9000 dpd-php
把php和nginx通过网卡连接起来:
docker network create --driver bridge dpd # 新建一个桥接网卡,名字叫dpd
docker network connect dpd dpd-php
docker network connect dpd dpd-nginx
docker network ls # 列出docker当前有哪些网卡
docker network inspect dpd # 查看dpd网卡详情
修改Nginx配置,把PHP脚本转发给fpm,fpm地址通过容器名dpd-php就可以识别:
server {
listen 80;
server_name local.docker-php-demo.com;
root /var/www/docker-php-demo;
location ~ \.php$ {
fastcgi_pass dpd-php:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
让Nginx重新加载配置文件:
docker exec -it dpd-nginx nginx -s reload
到这里,我们就把Nginx 和 php-fpm搭起来了。下面我们再来搭建Mysql,在mysql目录下新建test.sql,存放表结构和基础数据:
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` varchar(20) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
INSERT INTO `user` VALUES (null, 'a1');
Dockerfile:
FROM mysql:5.7
ENV MYSQL_ROOT_PASSWORD=123 MYSQL_DATABASE=test
COPY ./test.sql /var/data/test.sql
构建并启动容器,这里直接用--network
参数把容器加入dpd网络:
cd mysql
docker build -t dpd-mysql .
docker run --name dpd-mysql -d --network dpd -p 3306:3306 dpd-mysql
进入容器把表和测试数据导入进去:
docker exec -it dpd-mysql bash
mysql -uroot -p
mysql> use test;
mysql> source /var/data/test.sql
修改index.php:
<?php
$dsn = 'mysql:dbname=test;host=dpd-mysql';
$user = 'root';
$password = '123';
try {
$dbh = new PDO($dsn, $user, $password);
$sql = 'SELECT * FROM user WHERE id=?';
$sth = $dbh->prepare($sql);
$sth->execute(array(1));
$result = $sth->fetch(PDO::FETCH_ASSOC);
var_dump($result);
} catch (PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
浏览器输入http://local.docker-php-demo.com/index.php,PHP成功地从数据库获取到了用户数据:
array(2) { ["id"]=> string(1) "1" ["name"]=> string(2) "a1" }
参考资料
- Docker 官方文档
- Docker 入门教程
- Docker — 从入门到实践
- Docker for beginners
- Setting up PHP, PHP-FPM and NGINX for local development on Docker
用Docker搭建LNMP的更多相关文章
- Mac下docker搭建lnmp环境 + redis + elasticsearch
之前在windows下一直使用vagrant做开发, 团队里面也是各种开发环境,几个人也没有统一环境,各种上线都是人肉,偶尔还会有因为开发.测试.生产环境由于软件版本或者配置不一致产生的问题, 今年准 ...
- docker搭建lnmp环境(问题,资料,命令)
入门参考 http://www.runoob.com/docker/docker-install-nginx.html 十大常用命令玩转docker 1. #从官网拉取镜像 docker pull & ...
- 使用docker搭建lnmp环境
Docker容器LNMP环境搭建 安装 制作镜像 启动并关联实例 安装 系统环境 硬件型号: ThinkPad T520 系统版本: ubuntu 14.04 CPU: i7 RAM: 8G 添加软件 ...
- Docker简介以及使用docker搭建lnmp的过程(多PHP版本)
一.Docker基础 Docker安装 Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的 Ubuntu 版本是否支持 Docker. 通过 uname ...
- docker搭建lnmp环境
1.搭建lnmp网站平台 1.创建mysql数据库容器 docker run -it -d --name lnmp_mysql -p 3308:3306 -e MYSQL_ROOT_PASSWORD= ...
- Docker入门详解——安装docker并利用docker搭建lnmp
首先我们需先安装docker环境,这个比较简单,以centos7为例 docker在centos7上安装需要系统内核版本3.10+,可以通过uname -r查看内核版本号,如果版本不符请自行查阅资料更 ...
- Docker搭建LNMP网站平台
1.自定义网络 [root@linux-docker01 ~]# docker network create lnmp 67e7e0736b2c58f8f81eed50130803b34be0583f ...
- 使用Docker搭建LNMP开发环境
1.什么是Docker Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技 ...
- 基于Docker搭建LNMP环境(转)
关于什么是docker,建议大家先上网查查有关的用法.如果您不了解,在这篇文章中,您可以简单的理解为他是一个轻量级的虚拟机. 一.docker安装mysql 首先,我们从仓库拉取一个MySql的镜像 ...
随机推荐
- Delphi 获取外部程序句柄与发送消息
--记录下来备以后用 [打开外部程序.消息.句柄],技术有限,希望不要误人子弟了. 源码unit Unit1; interface uses Windows, Messages, SysUtils, ...
- 微信公众平台消息接口开发(2)你的服务器没有正确响应Token验证的解决方法
你的服务器没有正确响应Token验证,请阅读消息接口使用指南 微信 微信公众平台开发模式 平台 消息 接口 启用 URL Token作者:http://txw1958.cnblogs.com/ 本系统 ...
- WinEdt && LaTex(五)—— 内容的排版
1. 无序列表 需要的环境是\begin{itemize} \end{itemize} \begin{itemize} \item hello \item world \end{itemize} 2. ...
- Coverage数据构成
一个Coverage数据由两个文件夹组成:一个文件夹用于存储空间几何信息,该文件夹的名称就是这个Coverage数据的名称(如下图的hnc):另一个文件夹的名字为info,它存储的为Coverage的 ...
- CountDownLatch和CyclicBarrier 专题
4.Runnable接口和Callable接口的区别 有点深的问题了,也看出一个Java程序员学习知识的广度. Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行ru ...
- NSURLSession 网络库 - 原生系统送给我们的礼物
大家在进行iOS开发的时候一定会用到网络操作.但由于早期原生的 NSURLConnection 操作起来有很多不便,使得大家更愿意使用第三方库的解决方案,比如鼎鼎大名的 AFNetworking.正是 ...
- redis 从0 到 1 键值相关命令 服务器相关命令
keys * 获取所有的key 忽略其数据类型 数据为空 返回(empty list or set) keys a* .*b 获取以a开头 或者 以b结尾的key 返回(empty list ...
- Win10 如何以管理员身份设置开机自启程序(1)
原文:Win10 如何以管理员身份设置开机自启程序(1) 最近一个项目在win10上部署时遇到问题,即如何设置以管理员身份开机自启.现把解决方法整理如下: 首先,为了进行验证,我编了一个简单的程序te ...
- Flutter 开发环境搭建
Flutter 开发环境搭建 官方的资料相对还是比较全面的,包含了很多中文的资料信息.官方对咱们国家的开发人员还是很友好的. 安装教程:https://flutter.io/get-started/i ...
- WPF里DataGrid分页控件
1.主要代码: using System; using System.Collections.ObjectModel; using System.Windows; using System.Windo ...