搭建基於 Docker 的 LNMP 環境

背景

由於雲主機馬上就要到期了,所以最近對比了幾家雲服務器提供商。最終決定嘗試一下 UCloud。於是不得不把原博客(typecho)遷移過來。下面是流水賬式地記錄一下過程。

過程記錄

本想嘗試按照以前的筆記重新搭建一套環境,然後再把數據導過來,但是一想到這一長串的步驟,以及可能碰到的問題就頭疼,於是決定構建一個基於容器的 LNMP 環境,一勞永逸,也方便日後繼續做博客遷移。當然如果有同學也想搭建基於 LNMP 的博客如 Typecho,則可以參考我的模板:-)(文末有 Github 鏈接)。 注:以下命令及配置都是基於 CentOS7

容器環境

由於此次環境的搭建是用的 docker,所以先保證機器上安裝了 dockerdocker-compose

# 安裝 docker
sudo yum install -y yum-utils
sudo yum-config-manager \
  --add-repo \
  http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl enable docker
sudo systemctl start docker

# 安裝 docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

LNMP 環境配置

構建基於 docker-compose,目錄結構如下:

.
├── docker-compose.yml # docker-compose 配置文件
├── mysql
│   ├── conf # mysql 配置文件目錄
│   │   ├── my.cnf
│   └── data # mysql 數據文件目錄
├── nginx
│   ├── cert # nginx ssl 證書目錄
│   │   ├── nightfield.com.cn.key
│   │   ├── nightfield.com.cn.pem
│   ├── conf # nginx 配置目錄
│   │   ├── nightfield.com.cn.conf
│   ├── html # nginx Web 根目錄
│   │   ├── info.php
│   └── log # nginx 日誌目錄
├── php
│   ├── conf # php 配置目錄
│   │   └── php.ini
│   └── Dockerfile # php 的 Dockerfile 配置

可以看到主要是對應軟件的一些配置文件。docker-compose.yml 文件內容如下:

version: "3"
services:
    nginx:
        image: nginx:latest
        container_name: nginx
        networks:
            - lnmp
        depends_on:
            - php
        ports:
            - "80:80"
            - "443:443"
        expose:
            - "80"
            - "443"
        volumes:
            - /opt/docker/nginx/html:/usr/share/nginx/html # nginx web 根目錄
            - /opt/docker/nginx/conf/:/etc/nginx/conf.d # 配置目錄
            - /opt/docker/nginx/log:/var/log/nginx # 日誌目錄
            - /opt/docker/nginx/cert:/etc/nginx/cert # ssl 證書目錄
        links:
            - php
    php:
        build: # 由於 PHP 的構建相對較爲複雜,所以用了 Dockerfile 的方式
            context: ./php
            dockerfile: Dockerfile
        container_name: php
        volumes:
            - /opt/docker/nginx/html:/usr/share/nginx/html # nginx web 根目錄
            - /opt/docker/php/conf/:/usr/local/etc/php/conf.d # 配置目錄
        networks:
            - lnmp
        depends_on:
            - mysql
        expose:
            - "9000"
    mysql:
        image: mysql:5.7
        container_name: mysql
        volumes:
            - /opt/docker/mysql/conf/:/etc/mysql/conf.d # 配置目錄
            - /opt/docker/mysql/data:/var/lib/mysql # 數據目錄
        environment:
            MYSQL_ROOT_PASSWORD: password # 改爲自定義密碼
        networks:
            - lnmp
        expose:
            - "3306"
        ports:
            - "3306:3306"

networks:
    lnmp:

Mysql

Mysql 的配置較爲簡單不再贅述,注意將對應的配置目錄以及數據目錄從容器中映射出來便於管理。

Nginx

Nginx 的配置從容器中映射出來,模版如下:

# https ssl, 對應 443 端口
server {
    listen       443 ssl;
    server_name  nightfield.com.cn; # 域名或者服務器 ip
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    index index.php;
    root         /usr/share/nginx/html;
    # ssl 配置
    ssl_certificate_key cert/nightfield.com.cn.key; # 證書 key
    ssl_certificate cert/nightfield.com.cn.pem; # 證書 pem
    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;
    ssl_prefer_server_ciphers  on;

    location ~ .*\.php(\/.*)*$ { # 注意這裏的正則
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

# https,對應 80 端口
server {
    listen 80;
    server_name nightfield.com.cn; # 域名或者服務器 ip
    rewrite ^(.*)$ https://${server_name}$1 permanent; # 重定向到 https
}

PHP

PHP 的構建相對複雜,除了從官網拉 PHP 鏡像外,還需要安裝額外的模塊如 mysqli pdo_mysql,所以做了一個 Dockerfile,內容如下:

FROM php:7.0-fpm # 基礎鏡像
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone
RUN apt-get update && apt-get install -y \ # 一些 lib 庫
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng-dev \
        libmemcached-dev \
        zlib1g-dev \
        libcurl4-openssl-dev \
        libxml2-dev \
        --no-install-recommends && rm -rf /var/lib/apt/lists/* \
    && docker-php-ext-install -j$(nproc) \ # 插件安裝
        iconv mcrypt gettext curl mysqli pdo pdo_mysql zip \
        mbstring bcmath opcache xml simplexml sockets hash soap \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd

CMD ["php-fpm", "-F"]

LNMP 環境構建及驗證

配置都完成之後,啓動容器環境:

docker-componse up -d

在雲服務器的安全組規則配置中,開放對應端口:804433306(完成數據遷移之後可以關閉),然後打開瀏覽器訪問 ${hostname}/info.php,可以看到如下頁面: info,php 說明環境已安裝成功!

數據遷移

接下來要做的就是把博客數據從老的庫遷移到新的庫。

Typecho 數據

將整個 Typecho 目錄打包,用 scp 傳到新機器的 ./nginx/html/ 目錄下。

Mysql

我這裏用了比較老套的方法:從老庫導出數據再導入新庫,用工具是 DBeaver。這裏記錄一下 Typecho 相關的一些表結構。

use typecho;
CREATE TABLE `typecho_comments` (
  `coid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `cid` int(10) unsigned DEFAULT '0',
  `created` int(10) unsigned DEFAULT '0',
  `author` varchar(200) DEFAULT NULL,
  `authorId` int(10) unsigned DEFAULT '0',
  `ownerId` int(10) unsigned DEFAULT '0',
  `mail` varchar(200) DEFAULT NULL,
  `url` varchar(200) DEFAULT NULL,
  `ip` varchar(64) DEFAULT NULL,
  `agent` varchar(200) DEFAULT NULL,
  `text` text,
  `type` varchar(16) DEFAULT 'comment',
  `status` varchar(16) DEFAULT 'approved',
  `parent` int(10) unsigned DEFAULT '0',
  PRIMARY KEY (`coid`),
  KEY `cid` (`cid`),
  KEY `created` (`created`)
) ENGINE=MyISAM AUTO_INCREMENT=51476 DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_contents` (
  `cid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) DEFAULT NULL,
  `slug` varchar(200) DEFAULT NULL,
  `created` int(10) unsigned DEFAULT '0',
  `modified` int(10) unsigned DEFAULT '0',
  `text` longtext,
  `order` int(10) unsigned DEFAULT '0',
  `authorId` int(10) unsigned DEFAULT '0',
  `template` varchar(32) DEFAULT NULL,
  `type` varchar(16) DEFAULT 'post',
  `status` varchar(16) DEFAULT 'publish',
  `password` varchar(32) DEFAULT NULL,
  `commentsNum` int(10) unsigned DEFAULT '0',
  `allowComment` char(1) DEFAULT '0',
  `allowPing` char(1) DEFAULT '0',
  `allowFeed` char(1) DEFAULT '0',
  `parent` int(10) unsigned DEFAULT '0',
  `views` int(10) DEFAULT '0',
  PRIMARY KEY (`cid`),
  UNIQUE KEY `slug` (`slug`),
  KEY `created` (`created`)
) ENGINE=MyISAM AUTO_INCREMENT=199 DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_fields` (
  `cid` int(10) unsigned NOT NULL,
  `name` varchar(200) NOT NULL,
  `type` varchar(8) DEFAULT 'str',
  `str_value` text,
  `int_value` int(10) DEFAULT '0',
  `float_value` float DEFAULT '0',
  PRIMARY KEY (`cid`,`name`),
  KEY `int_value` (`int_value`),
  KEY `float_value` (`float_value`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_metas` (
  `mid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) DEFAULT NULL,
  `slug` varchar(200) DEFAULT NULL,
  `type` varchar(32) NOT NULL,
  `description` varchar(200) DEFAULT NULL,
  `count` int(10) unsigned DEFAULT '0',
  `order` int(10) unsigned DEFAULT '0',
  `parent` int(10) unsigned DEFAULT '0',
  PRIMARY KEY (`mid`),
  KEY `slug` (`slug`)
) ENGINE=MyISAM AUTO_INCREMENT=52 DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_options` (
  `name` varchar(32) NOT NULL,
  `user` int(10) unsigned NOT NULL DEFAULT '0',
  `value` text,
  PRIMARY KEY (`name`,`user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_relationships` (
  `cid` int(10) unsigned NOT NULL,
  `mid` int(10) unsigned NOT NULL,
  PRIMARY KEY (`cid`,`mid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `typecho_users` (
  `uid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL,
  `mail` varchar(200) DEFAULT NULL,
  `url` varchar(200) DEFAULT NULL,
  `screenName` varchar(32) DEFAULT NULL,
  `created` int(10) unsigned DEFAULT '0',
  `activated` int(10) unsigned DEFAULT '0',
  `logged` int(10) unsigned DEFAULT '0',
  `group` varchar(16) DEFAULT 'visitor',
  `authCode` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`uid`),
  UNIQUE KEY `name` (`name`),
  UNIQUE KEY `mail` (`mail`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

域名重解析

去域名的配置頁面,將 A 記錄 重新解析到新的 ip 上,等待生效後(ping 以下域名看結果),訪問 https://nightfield.com.cn 已經可以正確打開博客,還是原來的味道,只是換了配方:-)

問題記錄

1. 博客的配置丟失

還不知道具體原因,用最笨的方法解決了:主題和插件重新配置了一下。

2. PHP 無法連接數據庫

報的錯誤是:

Uncaught Error: Class 'mysqli' not found

原因一般是沒有安裝 mysqli 導致的,但是我們在 Dockerfile 裏面確實有安裝此模塊。在配置文件 ./php/conf/php.ini 中加上以下配置,問題解決:

extension=mysqli.so
extension=pdo_mysql.so

3. 打開很多 Typecho 頁面報 Cannot modify header information

報的錯誤是:

Warning: Cannot modify header information - headers already sent by (output started at /data/dy-pages/store-1/262/1634262/www/install.php:202) in /data/dy-pages/store-1/262/1634262/www/var/Typecho/Cookie.php on line 102

在配置文件 ./php/conf/php.ini 中加上以下配置,問題解決:

output_buffering=on

4. 無法登陸 Typecho 管理頁面,報 404

這個問題是無法正確解析 URL 導致的。Typecho 登陸之後的 url 類似 https://www.nightfield.com.cn/index.php/action/login?_=7,而我在 Nginx 配置的 PHP location 爲 location ~ .*\.php$,無法處理上述 url。將 location 改爲 location ~ .*\.php(\/.*)*$ 問題解決。

附錄

本次基於 docker 的 LNMP 環境的代碼 Github 地址:Docker-LNMP

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章