<h1>使用Docker 部署 LNMP+Redis 环境</h1> <h3>Docker 简介</h3> <p>Docker 是一个开源的应用容器引擎,让开发者能够打包他们的应用以及依赖包到一个可移植的容器中,而后发布到任何流行的 Linux 机器上,也能够实现虚拟化。容器是彻底使用沙箱机制,相互之间不会有任何接口。推荐内核版本3.8及以上</p> <h3>为何使用Docker</h3> <ol> <li>加速本地的开发和构建流程,容器能够在开发环境构建,而后轻松地提交到测试环境,并最终进入生产环境</li> <li>可以在让独立的服务或应用程序在不一样的环境中获得相同的运行结果</li> <li>建立隔离的环境来进行测试</li> <li>高性能、超大规划的宿主机部署</li> <li>从头编译或者扩展示有的OpenShift或Cloud Foundry平台来搭建本身的PaaS环境</li> </ol> <h2>目录</h2> <ul> <li><a href="#%E5%AE%89%E8%A3%85Docker">安装Docker</a></li> <li><a href="#%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84">目录结构</a></li> <li><a href="#%E5%88%9B%E5%BB%BA%E9%95%9C%E5%83%8F%E4%B8%8E%E5%AE%89%E8%A3%85">快速使用</a></li> <li><a href="#%E8%BF%9B%E5%85%A5%E5%AE%B9%E5%99%A8%E5%86%85%E9%83%A8">进入容器内部</a></li> <li><a href="#PHP%E6%89%A9%E5%B1%95%E5%AE%89%E8%A3%85">PHP扩展安装</a></li> <li><a href="#Composer%E5%AE%89%E8%A3%85">Composer安装</a></li> <li><a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E5%A4%84%E7%90%86">常见问题处理</a></li> <li><a href="#%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4">经常使用命令</a></li> <li><a href="#Dockerfile%E8%AF%AD%E6%B3%95">Dockerfile语法</a></li> <li><a href="#docker-compose%E8%AF%AD%E6%B3%95%E8%AF%B4%E6%98%8E">docker-compose语法说明</a></li> </ul> <blockquote>项目源码地址:<a href="https://github.com/voocel/docker-lnmp" rel="nofollow noreferrer">GitHub</a> </blockquote> <h3>安装Docker</h3> <p><strong>windows 安装</strong></p> <p><a href="http://www.iganlei.cn/environment-configuration/798.html" rel="nofollow noreferrer">参考</a></p> <p><strong>mac</strong></p> <p><a href="https://github.com/widuu/chinese_docker/blob/master/installation/mac.md" rel="nofollow noreferrer">docker toolbox参考</a> </p> <p><strong>linux</strong></p>php
# 下载安装 curl -sSL https://get.docker.com/ | sh # 设置开机自启 sudo systemctl enable docker.service sudo service docker start|restart|stop # 安装docker-compose curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose
<h3>目录结构</h3>html
docker_lnmp ├── v2 ├── mysql │ └── Dockerfile │ └── my.cnf ├── nginx │ ├── Dockerfile │ ├── nginx.conf │ ├── log │ │ └── error.log │ └── www │ ├── index.html │ ├── index.php │ ├── db.php │ └── redis.php ├── php │ ├── Dockerfile │ ├── www.conf │ ├── php-fpm.conf │ ├── php.ini │ └── log │ └── php-fpm.log └── redis └── Dockerfile └── redis.conf
<h3>建立镜像与安装</h3> <blockquote>直接使用docker-compose一键制做镜像并启动容器</blockquote> <a href="http://www.jqhtml.com/20196.html" target="_blank">docker nginx 部署多个项目</a> <p><strong>版本一</strong></p> <p><em>该版本是经过拉取纯净的CentOS镜像,经过Dockerfile相关命令进行源码编译安装各个服务。因此该方式很方便定制本身须要的镜像,可是占用空间大且构建慢。</em></p>mysql
git clone https://github.com/voocel/docker-lnmp.git cd docker-lnmp docker-compose up -d
<p><strong>版本二(推荐)</strong></p>linux
git clone https://github.com/voocel/docker-lnmp.git cd docker-lnmp/v2 chmod 777 ./redis/redis.log chmod -R 777 ./redis/data docker-compose up -d
<p><em>该版本是经过拉取官方已经制做好的各个服务的镜像,再经过Dockerfile相关命令根据自身需求作相应的调整。因此该方式构建迅速使用方便,由于是基于Alpine Linux因此占用空间很小。</em></p> <h3>测试</h3> <p>使用docker ps查看容器启动状态,若所有正常启动了则<br>经过访问127.0.0.一、127.0.0.1/index.php、127.0.0.1/db.php、127.0.0.1/redis.php 便可完成测试<br>(若想使用https则请修改nginx下的dockerfile,和nginx.conf按提示去掉注释便可,灵须要在ssl文件夹中加入本身的证书文件,本项目自带的是空的,须要本身替换,保持文件名一致)</p> <h3>进入容器内部</h3> <ol><li>使用 docker exec</li></ol>nginx
docker exec -it ngixn /bin/sh
<ol><li>使用nsenter命令</li></ol>c++
# cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24; # ./configure --without-ncurses # make nsenter && sudo cp nsenter /usr/local/bin
<p>为了链接到容器,你还须要找到容器的第一个进程的 PID,能够经过下面的命令获取再执行。</p>git
PID=$(docker inspect --format "{{ .State.Pid }}" container_id) # nsenter --target $PID --mount --uts --ipc --net --pid
<h3>PHP扩展安装</h3> <ol><li>安装PHP官方源码包里的扩展(如:同时安装pdo_mysql mysqli pcntl gd四个个扩展)</li></ol> <p><em>在php的Dockerfile中加入如下命令</em></p>github
RUN apk add libpng-dev \ && docker-php-ext-install pdo_mysql mysqli pcntl gd \
<p><em>注:由于该镜像缺乏gd库所需的libpng-dev包,因此须要先下载这个包</em></p> <ol><li>PECL 扩展安装</li></ol>web
# 安装扩展 RUN pecl install memcached-2.2.0 \ # 启用扩展 && docker-php-ext-enable memcached \
<ol><li>经过下载扩展源码,编译安装的方式安装</li></ol>redis
# 安装Redis和swoole扩展 RUN cd ~ \ && wget https://github.com/phpredis/phpredis/archive/4.2.0.tar.gz \ && tar -zxvf 4.2.0.tar.gz \ && mkdir -p /usr/src/php/ext \ && mv phpredis-4.2.0 /usr/src/php/ext/redis \ && docker-php-ext-install redis \ && apk add libstdc++\ && cd ~ \ && wget https://github.com/swoole/swoole-src/archive/v4.2.12.tar.gz \ && tar -zxvf v4.2.12.tar.gz \ && mkdir -p /usr/src/php/ext \ && mv swoole-src-4.2.12 /usr/src/php/ext/swoole \ && docker-php-ext-install swoole \
<p><em>注:由于该镜像须要先安装swoole依赖的libstdc++,不然安装成功后没法正常加载swoole扩展</em></p> <h3>Composer安装</h3> <p>在Dockerfile中加入</p> ```RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ ```
<h2>常见问题处理</h2> <ul><li>redis启动失败问题</li></ul> <p>在v2版本中redis的启动用户为redis不是root,因此在宿主机中挂载的./redis/redis.log和./redis/data须要有写入权限。</p>
chmod 777 ./redis/redis.log chmod 777 ./redis/data
<ul><li>MYSQL链接失败问题</li></ul> <p>在v2版本中是最新的MySQL8,而该版本的密码认证方式为Caching_sha2_password,而低版本的php和mysql可视化工具可能不支持,可经过phpinfo里的mysqlnd的Loaded plugins查看是否支持该认证方式,不然须要修改成原来的认证方式mysql_native_password:</p>
select user,host,plugin,authentication_string from mysql.user; ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;
<ul> <li> <p>注意挂载目录的权限问题,否则容器成功启动几秒后马上关闭,例:如下/data/run/mysql 目录没权限的状况下就会出现刚才那种状况</p>
docker run --name mysql57 -d -p 3306:3306 -v /data/mysql:/var/lib/mysql -v /data/logs/mysql:/var/log/mysql -v /data/run/mysql:/var/run/mysqld -e MYSQL_ROOT_PASSWORD=123456 -it centos/mysql:v5.7
</li> <li> <p>须要注意php.ini 中的目录对应 mysql 的配置的目录须要挂载才能获取文件内容,否则php链接mysql失败</p>
# php.ini mysql.default_socket = /data/run/mysql/mysqld.sock mysqli.default_socket = /data/run/mysql/mysqld.sock pdo_mysql.default_socket = /data/run/mysql/mysqld.sock # mysqld.cnf pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock
</li> <li> <p>使用php链接不上redis</p>
# 错误的 $redis = new Redis; $rs = $redis->connect('127.0.0.1', 6379);
<p>php链接不上,查看错误日志</p>
PHP Fatal error: Uncaught RedisException: Redis server went away in /www/index.php:7
<p>考虑到docker 之间的通讯应该不能够用127.0.0.1 应该使用容器里面的ip,因此查看redis 容器的ip</p>
[root@localhost docker]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b5f7dcecff4c docker_nginx "/usr/sbin/nginx -..." 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp nginx 60fd2df36d0e docker_php "/usr/local/php/sb..." 7 seconds ago Up 5 seconds 9000/tcp php 7c7df6f8eb91 hub.c.163.com/library/mysql:latest "docker-entrypoint..." 12 seconds ago Up 11 seconds 3306/tcp mysql a0ebd39f0f64 docker_redis "usr/local/redis/s..." 13 seconds ago Up 12 seconds 6379/tcp redis
<p>注意测试的时候链接地址须要容器的ip或者容器名names,好比redis、mysql.<br>例如nginx配置php文件解析 fastcgi_pass php:9000;<br>例如php链接redis $redis = new Redis;$res = $redis->connect('redis', 6379);</p> <p><em>由于容器ip是动态的,重启以后就会变化,因此能够建立静态ip</em></p> <p>第一步:建立自定义网络</p>
#备注:这里选取了172.172.0.0网段,也能够指定其余任意空闲的网段 docker network create --subnet=172.171.0.0/16 docker-at docker run --name redis326 --net docker-at --ip 172.171.0.20 -d -p 6379:6379 -v /data:/data -it centos/redis:v3.2.6
<p>链接redis 就能够配置对应的ip地址了,链接成功</p>
$redis = new Redis; $rs = $redis->connect('172.171.0.20', 6379);
<p>另外还有种可能phpredis链接不上redis,须要把redis.conf配置略做修改。</p>
bind 127.0.0.1 改成: bind 0.0.0.0
</li> <li>启动docker web服务时 虚拟机端口转发 外部没法访问 通常出如今yum update的时候(WARNING: IPv4 forwarding is disabled. Networking will not work.)或者宿主机能够访问,但外部没法访问</li> </ul>
vi /etc/sysctl.conf 或者 vi /usr/lib/sysctl.d/00-system.conf 添加以下代码: net.ipv4.ip_forward=1 重启network服务 systemctl restart network 查看是否修改为功 sysctl net.ipv4.ip_forward 若是返回为"net.ipv4.ip_forward = 1"则表示成功了
<ul><li>若是使用最新的MySQL8没法正常链接,因为最新版本的密码加密方式改变,致使没法远程链接。</li></ul>
# 修改密码加密方式 ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
<h3>经常使用命令</h3> <ul> <li> <code>docker start</code> 容器名(容器ID也能够)</li> <li> <code>docker stop</code> 容器名(容器ID也能够)</li> <li> <code>docker run</code> 命令加 -d 参数,docker 会将容器放到后台运行</li> <li> <code>docker ps</code> 正在运行的容器</li> <li> <code>docker logs</code> --tail 10 -tf 容器名 查看容器的日志文件,加-t是加上时间戳,f是跟踪某个容器的最新日志而没必要读整个日志文件</li> <li> <code>docker top</code> 容器名 查看容器内部运行的进程</li> <li> <code>docker exec -d</code> 容器名 touch /etc/new_config_file 经过后台命令建立一个空文件</li> <li> <code>docker run --restart=always --name</code> 容器名 -d ubuntu /bin/sh -c "while true;do echo hello world; sleep 1; done" 不管退出代码是什么,docker都会自动重启容器,能够设置 --restart=on-failure:5 自动重启的次数</li> <li> <code>docker inspect</code> 容器名 对容器进行详细的检查,能够加 --format='{(.State.Running)}' 来获取指定的信息</li> <li> <code>docker rm</code> 容器ID 删除容器,注,运行中的容器没法删除</li> <li> <code>docker rm $(docker ps -aq)</code> 删除全部容器</li> <li> <code>docker rmi $(docker images -aq)</code> 删除全部镜像</li> <li> <code>docker images</code> 列出镜像</li> <li> <code>docker pull</code> 镜像名:标签 拉镜像</li> <li> <code>docker search</code> 查找docker Hub 上公共的可用镜像</li> <li> <code>docker build -t='AT/web_server:v1'</code> 命令后面能够直接加上github仓库的要目录下存在的Dockerfile文件。 命令是编写Dockerfile 以后使用的。-t选项为新镜像设置了仓库和名称:标签</li> <li> <code>docker login</code> 登录到Docker Hub,我的认证信息将会保存到$HOME/.dockercfg,</li> <li> <code>docker commit -m="comment " --author="AT" </code> 容器ID 镜像的用户名/仓库名:标签 不推荐这种方法,推荐dockerfile</li> <li> <code>docker history</code> 镜像ID 深刻探求镜像是如何构建出来的</li> <li> <code>docker port</code> 镜像ID 端口 查看映射状况的容器的ID和容器的端口号,假设查询80端口对应的映射的端口</li> <li> <code>run</code> 运行一个容器, -p 8080:80 将容器内的80端口映射到docker宿主机的某一特定端口,将容器的80端口绑定到宿主机的8080端口,另 127.0.0.1:80:80 是将容器的80端口绑定到宿主机这个IP的80端口上,-P 是将容器内的80端口对本地的宿主机公开</li> <li> <a href="http://docs.docker.com/reference/builder/" rel="nofollow noreferrer">http://docs.docker.com/refere...</a> 查看更多的命令</li> <li> <code>docker push</code> 镜像名 将镜像推送到 Docker Hub</li> <li> <code>docker rmi</code> 镜像名 删除镜像</li> <li> <code>docker attach</code> 容器ID 进入容器</li> <li> </li> <li> <code>docker network create --subnet=172.171.0.0/16 docker-at</code> 选取172.172.0.0网段</li> <li> <code>docker build</code> 就能够加 -ip指定容器ip 172.171.0.10 了</li> </ul> <p><strong>删除全部容器和镜像的命令</strong></p>
docker rm `docker ps -a |awk '{print $1}' | grep [0-9a-z]` 删除中止的容器 docker rmi $(docker images | awk '/^<none>/ { print $3 }')
<h3>Dockerfile语法</h3> <ul> <li> <code>MAINTAINER</code> 标识镜像的做者和联系方式</li> <li> <code>EXPOSE</code> 能够指定多个EXPOSE向外部公开多个端口,能够帮助多个容器连接</li> <li> <code>FROM</code> 指令指定一个已经存在的镜像</li> <li> <code>\#</code>号表明注释</li> <li> <code>RUN</code> 运行命令,会在shell 里使用命令包装器 /bin/sh -c 来执行。若是是在一个不支持shell 的平台上运行或者不但愿在shell 中运行,也能够 使用exec 格式 的RUN指令</li> <li> <code>ENV REFRESHED_AT</code> 环境变量 这个环境亦是用来代表镜像模板最后的更新时间</li> <li> <code>VOLUME</code> 容器添加卷。一个卷是能够 存在于一个或多个容器内的特定的目录,对卷的修改是马上生效的,对卷的修改不会对更新镜像产品影响,例:VOLUME["/opt/project","/data"]</li> <li> <code>ADD</code> 将构建环境 下的文件 和目录复制到镜像 中。例 ADD nginx.conf /conf/nginx.conf 也能够是取url 的地址文件,若是是压缩包,ADD命令会自动解压、</li> <li> <code>USER</code> 指定镜像用那个USER 去运行</li> <li> <code>COPY</code> 是复制本地文件,而不会去作文件提取(解压包不会自动解压) 例:COPY conf.d/ /etc/apache2/ 将本地conf.d目录中的文件复制到/etc/apache2/目录中</li> </ul> <h3>docker-compose.yml 语法说明</h3> <ul> <li> <code>image</code> 指定为镜像名称或镜像ID。若是镜像不存在,Compose将尝试从互联网拉取这个镜像</li> <li> <code>build</code> 指定Dockerfile所在文件夹的路径。Compose将会利用他自动构建这个镜像,而后使用这个镜像</li> <li> <code>command</code> 覆盖容器启动后默认执行的命令</li> <li> <code>links</code> 连接到其余服务容器,使用服务名称(同时做为别名)或服务别名(SERVICE:ALIAS)均可以</li> <li> <code>external_links</code> 连接到docker-compose.yml外部的容器,甚至并不是是Compose管理的容器。参数格式和links相似</li> <li> <code>ports</code> 暴露端口信息。宿主机器端口:容器端口(HOST:CONTAINER)格式或者仅仅指定容器的端口(宿主机器将会随机分配端口)均可以(注意:当使用 HOST:CONTAINER 格式来映射端口时,若是你使用的容器端口小于 60 你可能会获得错误得结果,由于 YAML 将会解析 xx:yy 这种数字格式为 60 进制。因此建议采用字符串格式。)</li> <li> <code>expose</code> 暴露端口,与posts不一样的是expose只能够暴露端口而不能映射到主机,只供外部服务链接使用;仅能够指定内部端口为参数</li> <li> <code>volumes</code> 设置卷挂载的路径。能够设置宿主机路径:容器路径(host:container)或加上访问模式(host:container:ro)ro就是readonly的意思,只读模式</li> <li> <code>volunes_from</code> 挂载另外一个服务或容器的全部数据卷</li> <li> <code>environment</code> 设置环境变量。能够属于数组或字典两种格式。若是只给定变量的名称则会自动加载它在Compose主机上的值,能够用来防止泄露没必要要的数据</li> <li> <code>env_file</code> 从文件中获取环境变量,能够为单独的文件路径或列表。若是经过docker-compose -f FILE指定了模板文件,则env_file中路径会基于模板文件路径。若是有变量名称与environment指令冲突,则之后者为准(环境变量文件中每一行都必须有注释,支持#开头的注释行)</li> <li> <code>extends</code> 基于已有的服务进行服务扩展。例如咱们已经有了一个webapp服务,模板文件为common.yml。编写一个新的 development.yml 文件,使用 common.yml 中的 webapp 服务进行扩展。后者会自动继承common.yml中的webapp服务及相关的环境变量</li> <li> <code>net</code> 设置网络模式。使用和docker client 的 --net 参数同样的值</li> <li> <code>pid</code> 和宿主机系统共享进程命名空间,打开该选项的容器能够相互经过进程id来访问和操做</li> <li> <code>dns</code> 配置DNS服务器。能够是一个值,也能够是一个列表</li> <li> <code>cap_add,cap_drop</code> 添加或放弃容器的Linux能力(Capability)</li> <li> <code>dns_search</code> 配置DNS搜索域。能够是一个值也能够是一个列表</li> <li>注意:使用compose对Docker容器进行编排管理时,须要编写docker-compose.yml文件,初次编写时,容易遇到一些比较低级的问题,致使执行docker-compose up时先解析yml文件的错误。比较常见的是yml对缩进的严格要求。yml文件还行后的缩进,不容许使用tab键字符,只能使用空格,而空格的数量也有要求,通常两个空格。</li> </ul> <blockquote>项目源码地址:<a href="https://github.com/voocel/docker-lnmp" rel="nofollow noreferrer">GitHub</a> </blockquote>