2020系统综合实践3 使用Docker Compose部署LNMP

项目已上传至GitHub


1. 安装Docker Compose

参考官方教程

sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 下载安装
# 这一步不FQ比较慢, 可以去直接github下载好拷贝到/usr/local/bin/docker-compose目录下

sudo chmod +x /usr/local/bin/docker-compose # 应用权限

docker-compose --version # 测试是否安装成功


2. Dockerfile编写

考虑到树莓派无法使用MySql,选择了Nginx+MariaDb+PHP+Pdo

Dockerfile-php

参考官方readme

FROM php:7.4-fpm
LABEL author=qyanzh
RUN apt-get update && apt-get install -y \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libpng-dev \
    && docker-php-ext-install pdo pdo_mysql \ # 新增行,安装pdo
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd

5/7更新
如果发现apt-get update过程很慢, 可以参考docker php-fpm 镜像修改apt源 - 简书

Dockerfile-nginx

只声明了一个端口号,配置文件的挂载放在yml中进行。

FROM nginx
LABEL author=qyanzh
EXPOSE 2420 # 声明暴露端口号2420

nginx配置文件

由于不熟悉nginx,在这一步花了很长时间,遇到了很多错误:

  • file not found
  • 404 Not Found
  • 502 Bad Gateway
  • 访问网页变成直接下载,从下面这张图可以看出我试了多少次Orz

以上错误基本都是由于路径配置不正确。

正确的配置

server {
    listen       2420;
    server_name  localhost;

    location / {
        root   /usr/share/my_web/html; # nginx容器中web文件存放目录,和yml对应
        index  index.html index.htm;
    }

    location ~ \.php$ {
       root           /usr/share/my_web/php; # php容器中web文件存放目录,和yml对应
       fastcgi_pass   cphp:9000; # php服务器默认端口9000,后面会解释为什么是cphp
       fastcgi_index  index.php;
       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
       include        fastcgi_params;
    }
}

访问localhost时,首先nginx会根据index指定的顺序依次检查web目录下的对应文件,如果是html文件,由nginx直接展示。如果访问到php文件nginx会发送一个请求到cphp:9000cphp将其web目录下对应的php文件解析为html文件后返回给nginx,再由nginx返回给浏览器,nginx在这里充当反向代理。(具体的原理和location匹配规则在文末的参考资料给出。)

访问两个页面的响应头:


Dockerfile-mariadb

较简单,直接在docker-compose.yml中给出。

Dockerfile-phpmyadmin

FROM phpmyadmin/phpmyadmin
LABEL author=qyanzh
EXPOSE 8080

3. 使用Compose实现多容器运行机制

项目结构

docker-compose.yml

参考官方教程以及twang2218/docker-lnmp(强烈推荐看一看):

version: "3"
services:
  php:
    image: my-php # 镜像名
    container_name: cphp # 容器名
    build:
      context: .
      dockerfile: Dockerfile-php # 指定自定义的Dockerfile
    environment:
      MYSQL_PASSWORD: 1234 # 方便直接在php代码中引用
    volumes:
      - ./my_web_dir:/usr/share/my_web/php # 挂载到本机web目录,相当于-v
    networks:
      - front_end
      - back_end

  nginx:
    image: my-nginx
    container_name: cngx
    build:
      context: .
      dockerfile: Dockerfile-nginx
    ports:
      - "80:2420" # 暴露80端口,相当于 -p 80:2420
    volumes:
      - ./my_web_dir:/usr/share/my_web/html
      - ./default.conf:/etc/nginx/conf.d/default.conf # 挂载配置文件
    networks:
      - front_end

  mariadb:
    image: mariadb
    container_name: cdb
    restart: always
    volumes:
      - mariadb-data:/bitnami/mariadb # 挂载volumn实现数据持久化
    environment:
      MYSQL_ROOT_PASSWORD: 1234
    networks:
      - back_end

  phpmyadmin:
    image: my-phpmyadmin
    container_name: cadm
    build: 
      context: .
      dockerfile: Dockerfile-phpmyadmin
    ports: 
      - "8080:80" # phpmyadmin默认监听80
    environment:
      PMA_HOST: cdb # 指定mysql服务所在的host
    networks: 
      - back_end

volumes:
  mariadb-data:

# 自定义网络的原因参见下方php访问数据库测试
networks:
  front_end:
  back_end:

通过volumn将容器目录挂载本地,在本地的修改可以直接被容器读取,非常方便。:前面的表示要挂载的本地目录/文件,:后面表示对应的容器内目录/文件。

构建运行

docker-compose down # 移除之前运行的
docker-compose up -d --build # 这也是个坑点,修改过后要加--build否则用的还是之前的镜像


4. 服务测试

nginx+php测试

mariadb测试

mysql -h localhost -u root -p

然而报错

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

经查是因为mariadb是运行在cdb中的,而cdb作为独立的容器有自己的IP,所以就无法通过宿主机IPlocalhost来访问,需要先查一下容器IP。

docker inspect cdb
mysql -h 172.20.0.2 -u root -p

php对数据库操作测试

参考菜鸟教程的代码,做一些必要的修改:

由于容器间互相隔离,localhost需要改掉。根据mariadb测试,应该把$host的值改为cdb的IP,但是每次容器重启后这个IP是会变化的,就需要每次去改测试代码。容易想到的方式是给其分配静态IP,但这样太不优雅了。经过查阅资料,发现位于同一网络下的容器可以通过容器名相互访问,为了隔离性,应把不同职责的容器分散在不同网络中。cdbcphp同属于back_end网络,所以将$host的值改为容器名cdb即可。上面的nginx配置文件fastcgi_pass的取值同理。

<?php
$dbms = 'mysql';     //数据库类型
$host = 'cdb'; //数据库主机名,修改为容器名
$user = 'root';      //数据库连接用户名
$pass = $_ENV["MYSQL_PASSWORD"]; //通过yml中定义的环境变量获得对应的密码
$dsn = "$dbms:host=$host;"; // "mysql:host=cdb"

try {
    $dbh = new PDO($dsn, $user, $pass); //初始化一个PDO对象
    echo "连接成功<br/>";
    $dbh = null;
} catch (PDOException $e) {
    die("Error!: " . $e->getMessage() . "<br/>");
}
//默认这个不是长连接,如果需要数据库长连接,需要最后加一个参数:array(PDO::ATTR_PERSISTENT => true) 变成这样:
$db = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
$db->exec("create database if not exists test_db;");
$db->exec("use test_db;");
$db->exec("create table if not exists t(num integer);");
$db->exec("delete from t;");

// 增
$db->exec("insert into t(num) values(2420);");
showDb($db, "insert:\n");
// 改
$db->exec("update t set num=031702420 where num=2420;");
showDb($db, "update:\n");
// 删
$db->exec("delete from t;");
showDb($db, "delete:\n");
// 查
function showDb($db, $op)
{
    print_r($op);
    $sql = "select * from t";
    $result = $db->query($sql);
    while ($arr = $result->fetch()) {
        print_r($arr);
    }
    echo "<br/>";
}

如果出现Connection Refused,可能是因为数据库未启动完毕,大约需要10秒。可以将up指令中的-d去掉来看容器的状态(或者docker logs),直到出现:

cdb | Version: '10.4.12-MariaDB-1:10.4.12+maria~bionic'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306


5. PhpMyAdmin

参考通过Docker为MySQL安装phpMyAdmin管理界面_运维_嘿客的技术闲笔-CSDN博客,具体已在yml中给出。

第一次没设置PMA_HOST环境变量,导致无法访问。错误信息也说明是主机名找不到,在yml中设置为cdb即可。


附录:一键删除命令

# 删除所有容器
docker rm -f $(docker ps -aq)  
# 删除所有镜像
docker rmi $(docker images -q)
# 删除所有<none>镜像
docker rmi $(docker images | grep "none" | awk '{print $3}')

用时和体会

加上博客约14小时。做完这次实践对Docker的理解深了不少,相比前两次蜻蜓点水般的使用,这次更有完整项目的感觉。面对没学过的东西,搜集资料和debug的过程是冗长且痛苦的,但这正是锻炼自学能力的机会。


参考

php - Docker Hub

docker php-fpm 镜像修改apt源 - 简书

Nginx+Php-fpm运行原理详解_运维_一路向前ylc-CSDN博客

Nginx location 匹配顺序整理 - python修行路 - 博客园

Get started with Docker Compose | Docker Documentation

twang2218/docker-lnmp: Docker example of LNMP setup (Compose, Swarm)

windows下如何查看和修改MySQL的端口号

docker-compose 安装 mariadb数据库_运维_jiangbenchu的博客-CSDN博客

PHP 连接 MySQL | 菜鸟教程

PHP PDO | 菜鸟教程

PHP: 用户自定义函数 - Manual

PDO 基本使用(简)_PHP_jia_1418422386的博客-CSDN博客

docker 如何删除none镜像_运维_李留白-CSDN博客

phpmyadmin/phpmyadmin - Docker Hub

使用docker-compose编写常规的lnmp容器,pdo连接mysql失败。 - 大步向前blue - 博客园

通过Docker为MySQL安装phpMyAdmin管理界面_运维_嘿客的技术闲笔-CSDN博客

posted @ 2020-04-25 23:21  zaqny  阅读(1191)  评论(1编辑  收藏  举报