Centos8使用docker迁移typecho博客

1,322 阅读8分钟

最近在学docker,先拿自己的博客来开下刀[手动狗头]。

安装docker

我是根据这个教程来安装的:Centos安装Docker。步骤如下:

  1. 卸载旧版本
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine
  1. 安装依赖包
sudo yum install -y yum-utils \
           device-mapper-persistent-data \
           lvm2
  1. 添加yum源(建议使用国内镜像)
sudo yum-config-manager \
    --add-repo \
    https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
  1. 更新软件源缓存并安装docker-ce
sudo yum makecache fast
sudo yum install docker-ce

sudo yum install docker-ce这一步可能会报错:containerd.io (>= 1.2.2-3),此时需要先安装新版本的containerd.io

yum install -y https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.6-3.3.el7.x86_64.rpm

最新的containerd.io版本请在download.docker.com/linux/cento…找(国内直接安装containerd.io可能会非常慢导致安装失败,建议可以想办法将rpm包下载到本机再安装)。安装完containerd.io后再sudo yum install docker-ce即可。 5. 启动Docker CE

sudo systemctl enable docker
sudo systemctl start docker
  1. 测试Docker是否安装正确
docker run hello-world
  1. 配置国内镜像加速 vi /etc/docker/daemon.json(该文件不存在时请新建)
{
  "registry-mirrors": [
    "https://dockerhub.azk8s.cn",
    "https://reg-mirror.qiniu.com"
  ]
}

保存后重启服务

sudo systemctl daemon-reload
sudo systemctl restart docker

安装mariadb

mkdir -p /data/mariadb
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test --mount type=bind,source=/data/mariadb,target=/var/lib/mysql --restart=always --name mariadb mariadb

--mount命令也可以用-v /data/mariadb:/var/lib/mysql --name mariadb mariadb代替,官方更推荐使用--mount,详情请看官网

参数说明:

  • -d 代表 daemon,即后台运行
  • -p是映射容器的3306端口到宿主机的3306端口,规则是:-p IP:host_port:container_port
  • -e是设置mariadb的密码
  • --mount是让容器的/var/lib/mysql映射到宿主机的/data/mariadb目录中
  • --restart=always是为了在docker重启时,容器能够自动启动

PS: 后面笔者会将所有容器依赖的一些数据都放在/data目录下,包括数据库、网站源码、nginx的conf等,目的是为了以后迁移方便,直接将/data拷贝到新服务器就可以。

安装nginx

mkdir -p /data/nginx/conf.d
docker run -p 80:80 -p 443:443 --mount type=bind,source=/data/nginx/conf.d,target=/etc/nginx/conf.d --mount type=bind,source=/data/solution,target=/data/solution --restart=always -d --name nginx nginx

说明:

  • --mount type=bind,source=/data/nginx/conf.d,target=/etc/nginx/conf.d是为了将nginx的配置目录映射到宿主机的目录中,这样当nginx容器被销毁时,依然能保留配置。
  • /data/solution用于存放网站项目,也是为了数据与容器分离。
  • -d这里同时映射了80和443端口

安装php

可以在php的官方镜像源找到最新版本的php,在实际使用中,我们可能还需要装一些php的扩展,而官方源中支持已经帮我们安装了一些扩展的php镜像,如:php:<version>-fpm,其中的<version>指的是php版本,具体可以从官方镜像源找到,当前最高版本是7.4。

docker run --name php-fpm -p 9000:9000 --mount type=bind,source=/data/solution,target=/data/solution --restart=always -d php:7.4-fpm

这里也将网站根目录/data/solution映射到php容器中,为了php能正确读取nginx中的root配置项。

由于typecho需要用到pdo_mysql扩展,因此要在php-fpm上安装这个扩展。

# 进入到`php-fpm`容器内部
docker exec -it php-fpm bash
# 安装扩展
docker-php-ext-install pdo_mysql
# 查看是否已经成功安装
php -m
# 退出容器
exit

容器与容器通信

现在机器上已经运行着nginxPHPmariadb三个服务了,他们分别跑在宿主机的80(443)90003306端口上。我们现在需要做到的是让nginx能够使用php的服务,php能够调用mariadb的服务,但容器间默认是互相隔离的,没法直接通信,因此需要想办法让他们能够互相通信。容器间的通信初次接触会有点复杂,所以在这里先和大家分享下一些要点。

通过了解,容器间通信主要会有以下几种方法:

  1. 使用默认的bridge网络,用网桥给容器分配的ip进行通信,官方不推荐用于生产环境。
  2. 自定义bridge网络,可以通过容器名连接,官方推荐
  3. 使用host网络共用宿主机的网络。

很多早期的文章分享都会使用--link参数来指定容器通信,但这种方法已经是过时的了,官方不再推荐使用,所以咱们这里也就不再用了,用官方推荐的方法更好。

使用默认的bridge网络

启动容器时,docker默认会将自动将容器绑定到默认的bridge网络中。打印一下默认的网络:

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
17e324f45964        bridge              bridge              local
6ed54d316334        host                host                local
7092879f2cc8        none                null                local

然后再看一下都有哪些容器连接到了bridge网络:

docker network inspect bridge

微信图片_20191208225610.png 可以看到mariadb,nginx,php-fpm的在bridge中的ip分别是172.17.0.4,172.17.0.2,172.17.0.3,宿主机的ip是172.17.0.1

我们可以通过在容器中通过宿主机的ip来访问对应的服务,即php-fpm想要访问mariadb,可以在php-fpm容器中通过172.17.0.1:3306来访问。

这种方式只能使用ip来访问对应的容器的服务,而ip可能会变化的,因此是不推荐使用在生产环境的,所以我们不会使用这种方式。

使用自定义bridge网络

除了默认的bridge网络,官方推荐用户自定义一个bridge网络用作生产环境,用户自定义的bridge网络不仅支持ip访问,还支持直接使用容器名称进行访问,官方推荐使用在生产环境,因此我们会使用这种方式进行容器间的通信。

  1. 创建一个自定义网络,名字可以随意,如typecho
docker network create typecho

打印一下当前的network,可以看到typecho已经存在了。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
429eabf557a3        bridge              bridge              local
3f47e77c0ded        host                host                local
3784484fb92e        typecho             bridge              local
7ff63bc6d9bd        none                null                local
  1. 容器使用自定义的网络

如果容器尚未创建,可以在docker run命令时通过--network参数来指定网络,如

docker run -d --name mynginx --network typecho nginx

如果容器已经在运行了,我们也可以通过docker network connect ${网络名} ${容器名}来指定,在上面我们已经运行了mariadb,php-fpm,nginx,所以现在依次为他们都绑定到typecho网络中。

docker network connect typecho mariadb
docker network connect typecho php-fpm
docker network connect typecho nginx

检查一下绑定到typecho网络的容器

$ docker network inspect typecho
[
    {
        "Name": "typecho",
        "Id": "3784484fb92e36c1448d2303af1b8bdce680e2cba0452fca354cefb6cb81bb54",
        "Created": "2019-12-08T08:32:57.299750734-05:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "6c644a890f3d1e7cc9e846236b6467ac31084470289fba5f6af1c2c1a0171e8e": {
                "Name": "mariadb",
                "EndpointID": "881c4f00a8d15f4e941d50faf53846a3ab8f15772fccc5e8c568e8d97b346b3b",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "f98355c7b220c0ab2897aa2478037e3322d78ccd81518af7a4756b07e1e26719": {
                "Name": "nginx",
                "EndpointID": "f14e5c36fcfe7ac5f26b8495335366957b6d7ab6d59c13e55cae2f1dc7751929",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "f9cb6cfcebf6f82beca76d3061765c1deb482a9c3c30d0c6d3644fe10ae40e3e": {
                "Name": "php-fpm",
                "EndpointID": "c4a2e26fe887e04f01a36eea9771eb2e817ef29d1bd7c435b380a7ca18485e64",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

可以看到mariadb,php-fpm,nginx都已经连接到typecho网络了。这样,我们在nginx容器,就可以直接通过php-fpm的名字来调用php的服务了。

使用host网络

如果容器使用了host网络,即docker运行时通过--network host来指定容器的网络,会使得容器共享宿主机的网络配置,即容器的localhost就是宿主机的localhost。

直接使用Docker host网络最大的好处就是性能更好,如果容器对网络传输效率有较高要求,则可以选择host网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host上已经使用的端口就不能再用了。

我们这里暂时不使用这种方式。

容器内与宿主机通信

如果在容器内部想要访问宿主机的端口,是不行的,两者的环境是隔离的。如nginx容器的配置中想要使用http://localhost:1234是访问不到宿主机的1234端口的。

  • 办法一:使用宿主机ip
$ ip addr show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:d5:4c:f2:1e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d5ff:fe4c:f21e/64 scope link
       valid_lft forever preferred_lft forever

可以看到宿主机的ip是172.17.0.1,容器内通过该ip访问宿主机。

  • 办法二:容器使用host网络

在容器启动时指定连接到host网络,容器暴露的端口即宿主机暴露的端口,从而做到容器内也可以通过localhost访问宿主机。

docker run -d --name nginx --network host nginx

参考该文章: Docker容器访问宿主机网络

迁移typecho博客

迁移数据库

由于是第一次迁移,新旧两个数据库的配置可能会有些不一样,所以为了不改变新服务器的一些配置,我们只迁移mariadb中涉及到数据的部分。

具体操作就是: 将原服务器/var/lib/mysql中数据库相关的文件夹提取出来,如旧服务器有两个名为typechotest的数据库,就只将这两个文件夹复制到新服务器的/data/mariadb中,然后重启一下mariadb即可:docker restart mariadb

PS: 还有一种迁移办法是可以选择将旧服务器的数据库备份成sql文件,然后在新服务器做还原哦。

迁移typecho源码

笔者这里将typecho的源代码放到/data/solution/typecho文件夹中。

配置nginx

vi /data/nginx/conf.d/typecho.conf

server {
  listen       80;
  server_name  localhost;
  location / {
    try_files $uri $uri/ /index.php?$args;
  }
  location ~ .php$ {
    root      /data/solution/typecho;
    fastcgi_pass  php-fpm:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include       fastcgi_params;
  }
}

server_name我这里写了localhost,暂时算是本地测试一下,实际使用时会改成域名。

修改nginxi配置后需要重启一下nginx的镜像才能生效:docker restart nginx

typecho的配置文件config.inc.php

注意数据库连接的配置,数据库的host可以直接使用mariadb,因为前面将两个容器绑在了同一个网络,php能正常解析到。

/** .... */

/** 定义数据库参数 */
$db = new Typecho_Db('Pdo_Mysql', 't_');
$db->addServer(array (
  'host' => 'mariadb',
  'user' => 'root',
  'password' => 'YOUR_PASSWORD',
  'charset' => 'utf8',
  'port' => '3306',
  'database' => 'typecho',
), Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set($db);

至此,整个迁移过程就完成了,可以用curl localhost测试一下或在浏览器中直接使用服务器的ip来访问页面啦。

PS: 记得不要忘了要打开服务器的80端口防火墙哦~~

Tips: 如果没法正常启动typecho,可能是某一步配置的不对,可以通过docker logs <container_name>命令查看对应容器的日志,然后查找相关的解决办法哦。附上两篇typecho解决错误的文章:Typecho 部署踩坑, 安装Typecho卡在“确认您的配置,数据库配置”问题的终极解决方法

https证书迁移(202011更新)

证书涉及到自动更新,比较复杂,请看: docker安装acme.sh

总结

用上docker最爽的一点就是不用再关心怎么安装软件的过程了,整个过程十分清爽。在整个过程中有几点可以留意一下:

  1. 笔者将所有docker依赖的数据都挂载到宿主机的/data目录中,是为了方便管理和以后迁移;
  2. 每个服务都是一个独立的容器,不会互相影响。如mariadb除了可以为我的博客系统服务,也可以给其他的服务调用;nginx也支持多站点,只需要/data/nginx/conf.d增加一个配置。

由于是第一次用上docker迁移,第一次会有点折腾,但以后再迁移就会方便多了。整个流程大概会是这样子:

# 在新服务器拉取旧服务器的数据
scp -r root@<old_server_ip>:/data /data/

# 安装docker-ce

# 新建一个网络
docker network create typecho

# 分别启动mariadb,php-fpm,nginx三个容器,并绑定typecho网络
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test --mount type=bind,source=/data/mariadb,target=/var/lib/mysql --restart=always --network typecho --name mariadb mariadb
docker run --name php-fpm -p 9000:9000 --mount type=bind,source=/data/solution,target=/data/solution --restart=always --network typecho -d php:7.4-fpm
docker run -p 80:80 -p 443:443 --mount type=bind,source=/data/nginx/conf.d,target=/etc/nginx/conf.d --mount type=bind,source=/data/solution,target=/data/solution --restart=always --network typecho -d --name nginx nginx

# 安装php扩展
docker exec -i php-fpm docker-php-ext-install pdo_mysql

# 测试走起
curl localhost