Democracy Dies in Darkness

将 ownCloud 迁移到 Docker 容器

续之前将博客迁移到 Docker 容器后,最近由于我在 Vultr 的 ownCloud 服务器性能出现严重问题,甚至在同步时出现宕机,我终于抽空把 ownCloud 迁移到了 HostDare  的VPS。总体来说迁移过程是非常简单的,而 ownCloud 也提供了官方的 Docker 支持,下面我会分享从零开始到HTTPS加密等具体的步骤。

致谢:本文思路主要来自于官方手册官博文章、以及众多的 StackOverflow 问答,在此表示感谢。

准备工作

  • 一台能胜任工作的 VPS
    • 什么叫“能胜任工作”?这里说点题外话,之前在 Vultr 的VPS只有1vCPU/250G HDD/1G的配置,这也导致了网页访问缓慢和突发需求时宕机。再加上Vultr家没有特别的网络线路,导致使用起来非常的糟心。经过多方查找,确认了 HostDare 提供的 CKVM7 机型(2核/300G SSD/1.5G/100m GIA)能满足我的需求,而且年付只有120刀(折扣码后90刀),比起 Vultr 家的10刀/月可以说是很良心了。唯一的缺点就是因为很多国人把 HD 当作梯子,在6月初可能会遭到大规模的屏蔽,当然你如果已经有梯子的话就无所谓了…
  • 本文使用 Ubuntu 18.04 LTS,当然别的发行版也没问题,毕竟是 Docker。但是配置的路径等差别就需要朱军自己来判断了。
  • 防火墙开通80、443和8080端口。
  • SSH root 访问权限到原始机器和目标机器。
  • (可选)如果需要HTTPS的话,需要开通域名并解析到你的目标机器。

官方的 Docker 容器中已经包含了 MariaDB 数据库和 Redis 缓存,配置完成后即可使用,无需额外操心。

第一步:在目标机器上安装 Docker

apt install -y docker docker-compose

第二步:配置并运行 ownCloud 容器

在进一步操作之前,我们需要新建一个文件夹来存放配置文件。可以放在任何地方,下面的命令只是一个示例。

注意

下面所有的 example.com 或者 example 等字样均为示例,需要替换成你自己的内容。
mkdir -p /var/www/example.com
cd /var/www/example.com

完成后,从官方 GitHub 下载 docker-compose 配置文件并创建一个 .env 文件来储存需要的环境变量。

wget https://raw.githubusercontent.com/owncloud-docker/server/master/docker-compose.yml
cat << EOF > .env
OWNCLOUD_VERSION=latest
OWNCLOUD_DOMAIN=localhost
ADMIN_USERNAME=admin
ADMIN_PASSWORD=example(务必要修改这个)
HTTP_PORT=8080
EOF
务必修改管理员账号的密码,以免遭受攻击

接下来运行 docker-compose 并测试 ownCloud 是否正常工作

docker-compose up -d

初次运行时可能需要一些时间,你可以通过执行以下命令来了解容器的运行状态

docker ps

当三个容器(ownCloud、MariaDB、Redis)的状态 [Status] 都显示 Healthy 的时候,就可以继续下一步了。

第三步:初步验收成果

很简单,浏览器打开 example.com:8080(或者IP地址:8080),查看 ownCloud 是否正常运行。

《将 ownCloud 迁移到 Docker 容器》

正常情况下应该看到的页面

到这里为止我们的 ownCloud 实例已经正常工作了。当然,如果这么简单的话我也不会专门写一篇文章。接下来我们要做的就是迁移数据并为我们的实例申请免费的 Let’s Encrypt 证书。

第四步:迁移资料和数据库

  1. 【原始机器】导出数据库 – 首先,将原始机器进入维护模式。
    将下面示例中的 example 换成你的 ownCloud 数据库用户,将 exampleDB 替换成你 ownCloud 数据库的名字。回车后你会被要求输入密码。
    mysqldump --single-transaction -h localhost -u example -p exampleDB > owncloud-dbbackup_`date +"%Y%m%d"`.bak
    忘了我的用户、密码、数据库名字怎么办
    别担心,相关配置可以在 (ownCloud安装目录)/config/config.php 里找到。
    相关字串有:dbnamedbhostdbuserdbpassword
  2. 【目标机器】确定 ownCloud 映射的目录 – 如果你仔细观察官方提供的 yml 文件,你会发现 ownCloud 实例使用了一个 volume 命令创建和本地磁盘的映射,这样我们的文件就不会在 Docker 容器退出后消失。那么文件究竟在哪呢?下面是 yml 文件的一个节选:
    services:
      owncloud:
        volumes:
          - files:/mnt/data

    那么我们的文件在 /mnt/data 吗?非也,事实上他们被存放在了 /var/lib/docker/volumes/(你的 ownCloud 实例名)_files。具体的文件夹名称似乎是根据你在第二步时候文件夹起的名字决定的(需要DP)。接下来的教程里我们假设 ownCloud 文件储存在 /var/lib/docker/volumes/examplecom_files

  3. 【目标机器】进入维护模式 – 由于我们新的 ownCloud 实例在容器里,正常 bash 里的命令是不会影响容器的。首先,cd 到你 docker-compose.yml 的目录,然后执行
    docker-compose exec owncloud occ maintenance:mode --on

    如果我们需要和 ownCloud 容器交互,只需要

    1) cd 到 docker-compose.yml 的目录
    2) 在命令前面加 docker-compose exec owncloud
  4. 【原始机器】用 rsync 复制文件 – 现在我们知道了复制的目的地,就可以开始着手复制了。

    新老机器目录的对应关系

    仔细观察以下不难发现,
    原始机器的 (ownCloud安装目录)/data 对应的是
    目标机器的 /var/lib/docker/volumes/examplecom_files/_data/files
    由于二者文件夹名称不一样,我们复制的时候要多加小心。

    执行以下命令

    cd (ownCloud安装目录)
    rsync -azvP --exclude 'owncloud.log' data/* root@(目标机器IP):/var/lib/docker/volumes/examplecom_files/_data/files

    回车后,你会被要求输入目标机器 root 账户的密码。
    复制的过程可能会有数十分钟甚至数小时,取决于你文件的大小、目标和原始机器的带宽等。不如开局 DOTA、去R6反反恐、下楼散个步…
    你可以用同样的方法传输数据库的备份文件,在此不再赘述。

    关于rsync
    rsync 其实是很多 Linux 发行版内置的一个同步/备份软件。简单解释一下参数:
    -a:存档模式,等于 -rlptgoD。这样会复制子目录、保留软链接、权限、修改日期、用户组、拥有人、设备文件、特殊文件。
    -z:启用压缩。(虽然效果可能只是个位数百分比,但好歹也是宝贵的流量呀)
    -v:众所周知的“啰嗦模式”。
    -P:保存部分传输的文件并显示进度。
    --exclude 'xxx':不要传输特定的文件/文件夹。在这里我们不需要传输 owncloud.log,节省可观的时间和流量。(你是不会相信我的log文件竟然有10G多大!)
  5. 【目标机器】导入部分配置参数数据库备份
    首先修改以下文件:/var/lib/docker/volumes/examplecom_files/_data/config/config.php找到以下字串:passwordsaltsecret 。从原始机器的配置文件中复制并覆写这两个字串(否则用户将无法登录)。然后执行以下命令导入数据库:
    docker-compose exec owncloud mysql -h db -u owncloud -p owncloud < ~/owncloud-dbbackup_xxxxx.bak

    你会被要求输入密码,默认是 owncloud

  6. 【目标机器】一些扫尾工作
    执行这个命令让服务器重新扫描所有用户的文件。否则你的文件不会出现在客户端:
    docker-compose exec owncloud occ files:scan --all

    执行以下命令通知客户端服务器已经迁移:

    docker-compose exec owncloud occ maintenance:data-fingerprint

    经过以上步骤你的文件和配置应该已经完美导入到新的服务器了。

    糟糕,我的用户不见了!
    这可能是数据库导出/导入不完全导致的。如果服务器只有少数几个人用的话,可能手工重新创建更快一些。只要用户名一致,文件就仍然可以读取。注意,创建用户名后可能要重新扫描文件他们才会出来。

第五步:用 Traefik 实现反代和 HTTPS

本来呢我是想请出我们的老熟人 Nginx 的,但是经过研究发现有个更好、更方便的方案,下面给大家介绍一下。

  1. 首先创建一个 Traefik 的文件夹,类似我们 ownCloud 的文件夹,用来存放配置。接下来我们以 /opt/traefik 为例
    mkdir -p /opt/traefik
    touch /opt/traefik/docker-compose.yml
    touch /opt/traefik/acme.json && chmod 600 /opt/traefik/acme.json
    touch /opt/traefik/traefik.toml
  2. 在 Docker 新建一个专用的“局域网”,这样 Traefik 就可以反代 ownCloud 的内容了。(名字可以随便起,我们接下来用 traefik_proxy 作为示范。
    docker network create traefik_proxy
  3. 编辑 /opt/traefik/docker-compose.yml,复制进去以下内容
    version: '2'
    
    services:
      proxy:
        image: traefik
        command: --configFile=/traefik.toml
        restart: always
        # Here's the network we created:
        networks:
          - traefik_proxy
        # The traefik entryPoints
        ports:
          - 80:80
          - 443:443
        labels:
          - traefik.enable=true
          - traefik.frontend.rule=Host:traefik.example.com
          # Traefik will proxy to its own GUI.
          - traefik.port=8080
          - traefik.docker.network=traefik_proxy
          - traefik.frontend.auth.basic=admin:xxxxxxxxxxxxxxxxx
        volumes: 
          - /var/run/docker.sock:/var/run/docker.sock 
          - /opt/traefik/traefik.toml:/traefik.toml 
          - /opt/traefik/acme.json:/acme.json
    
    networks: 
        traefik_proxy: 
          external: true

    下面重点讲一下加粗的部分。

    第10行的 traefik_proxy 需要和第三行创建的网络名称对应。
    labels 下的域名需要修改成你的域名(需要在 DNS 提供商那里做好 CNAME)。
    port 指的是容器监听的 HTTP 端口,而不是外部用户访问的端口。这个很重要,如果你改成80或者443的话会导致 Bad Gateway。
    下面那行的 docker.network 需要和上面的一致。
    最后三行定义了 traefik_proxy 这个“外部”网络。这里的外部不是指互联网,而仅仅是“这个docker-compose配置”之外。我们接下来将会看到一个“内部”网络的例子。
    frontend.auth 配置可以让你用密码保护特定的子域名,比如我们的 Traefik 控制台。这里使用的是基本认证,也就是网站会弹框出来问你用户名和密码。

    如何生成用户/密码组合?

    你可以用以下命令生成一个用户/密码组合(admin可以改成其他你喜欢的用户名)

    htpasswd -n admin

    比如,输入admin/114514这个组合以后,得到以下内容

    admin:$apr1$RGHQ.UEi$N6zyEZs3bB4Pr0Zoi4yUq/

    把它复制到 frontend.auth.basic 下面,记得在 $ 符号前面再加个 $

    不要使用114514作为你的密码。不仅很臭,还非常不安全!
  4. 编辑 /opt/traefik/traefik.toml,复制进去以下内容
    # uncomment this line to get debug info with "docker logs":
    # debug = true
    
    defaultEntryPoints = ["https","http"]
    
    # The syntax is somewhat esoteric so this is mostly copy-paste
    [entryPoints]
      [entryPoints.http]
      address = ":80"
        [entryPoints.http.redirect]
        entryPoint = "https"
      [entryPoints.https]
      address = ":443"
      [entryPoints.https.tls]
    
    [docker]
    endpoint = "unix:///var/run/docker.sock"
    domain = "example.com"
    watch = true
    exposedbydefault = false
    
    [acme]
    email = "steve@example.com"
    storage = "/acme.json"
    # problems with let's encrypt? You can use this staging environment, 
    # if you have to do a lot of trial and error:
    #caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
    entryPoint = "https"
    OnHostRule = true
    [acme.httpChallenge]
    entryPoint = "http"
    
    # enable web configuration backend.
    [web]
    
    # Web administration port, proxied in docker-compose.yml
    address = ":8080"

    这里有几处我们要修改的地方:
    [docker] 下的 domain 需要改成你自己的域名。
    [acme] 下的 email 改成你的电邮。这个信息是用作申请 Let’s Encrypt 免费证书用的。
    [web] 下的 address 中的端口号是第3步里我们定义的 traefik.port。注意,这个只是 Traefik 控制面板用的,我们的 ownCloud 容器不受这个影响。

    简单讲讲下面的几个配置是什么意思
    [docker]    —> 启用 Docker 支持。Traefik 原生支持多种后端,包括 Docker、Kubernetes、Mesos 等等。
    endpoint = “unix:///var/run/docker.sock”    —> Docker 的终端,本地运行的话一般是这个 .sock 接口“文件”
    domain = “example.com”    —> 你的域名
    watch = true    —> 监控 Docker 容器的变化
    exposedbydefault = false    —> 如果启用的话,任何上线的容器都会被反代到 (容器名).example.com。对于我们来说这是不必要的,也有安全隐患——比如你不想要把 MariaDB 暴露给外界。
  5. 启动 Traefik 反代
    docker-compose -f /opt/traefik/docker-compose.yml up -d
  6. 没有问题的话,你应该很快就能在 traefik.你的域名(如traefik.example.com)看到一个 HTTPS 加密的 Traefik 后台管理。没错,Traefik 甚至会帮你自动申请 Let’s Encrypt 证书并帮你部署,一切对于我们来说都是透明的。
    《将 ownCloud 迁移到 Docker 容器》

    Traefik 后台,暂时还没接入 ownCloud 容器

  7. 接下来就是修改 ownCloud 的 yml 文件,接入 Traefik。由于修改地方比较多,在这里我贴上完整的 yml 文件,供大家参考(需要关注的部分用粗体表示)
    version: '2.1'
    
    volumes:
      files:
        driver: local
      mysql:
        driver: local
      backup:
        driver: local
      redis:
        driver: local
    
    services:
      owncloud:
        image: owncloud/server:${OWNCLOUD_VERSION}
        restart: always
        # 注意:ports 被移除了
        depends_on:
          - db
          - redis
        environment:
          - OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
          - OWNCLOUD_DB_TYPE=mysql
          - OWNCLOUD_DB_NAME=owncloud
          - OWNCLOUD_DB_USERNAME=owncloud
          - OWNCLOUD_DB_PASSWORD=owncloud
          - OWNCLOUD_DB_HOST=db
          - OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
          - OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
          - OWNCLOUD_MYSQL_UTF8MB4=true
          - OWNCLOUD_REDIS_ENABLED=true
          - OWNCLOUD_REDIS_HOST=redis
        healthcheck:
          test: ["CMD", "/usr/bin/healthcheck"]
          interval: 30s
          timeout: 10s
          retries: 5
        volumes:
          - files:/mnt/data
        networks:
          - traefik_proxy
          - owncloud_internal
        labels:
          - traefik.enable=true
          - traefik.frontend.rule=Host:${OWNCLOUD_DOMAIN}
          - traefik.port=8080
          - traefik.docker.network=traefik_proxy
    
      db:
        image: webhippie/mariadb:latest
        restart: always
        environment:
          - MARIADB_ROOT_PASSWORD=owncloud
          - MARIADB_USERNAME=owncloud
          - MARIADB_PASSWORD=owncloud
          - MARIADB_DATABASE=owncloud
          - MARIADB_MAX_ALLOWED_PACKET=128M
          - MARIADB_INNODB_LOG_FILE_SIZE=64M
        healthcheck:
          test: ["CMD", "/usr/bin/healthcheck"]
          interval: 30s
          timeout: 10s
          retries: 5
        volumes:
          - mysql:/var/lib/mysql
          - backup:/var/lib/backup
        networks:
          - owncloud_internal
    
      redis:
        image: webhippie/redis:latest
        restart: always
        environment:
          - REDIS_DATABASES=1
        healthcheck:
          test: ["CMD", "/usr/bin/healthcheck"]
          interval: 30s
          timeout: 10s
          retries: 5
        volumes:
          - redis:/var/lib/redis
        networks:
          - owncloud_internal
    
    networks:
        traefik_proxy:
          external: true
        owncloud_internal:

    可以看到,我们做的事情主要有三件:
    1) 移除了 ownCloud 本身对外网的映射并把它加到了 Traefik 网络里。
    第一件事不难理解,毕竟我们想要用 Traefik 反代,不需要再通过8080端口访问了。
    2) 通过 labels 指令启用了 Traefik 对 ownCloud 容器的反代
    第二件事意在启用 Traefik 支持。如果你仔细观察之前 Traefik 的 yml 文件,你会发现它也有一个 labels 的指令。简单说,由于我们在 traefik.toml 的配置,我们必须明确指定这个 labels 的命令,Traefik 才会开始反代。注意8080端口:虽然和之前 Traefik 控制台的端口冲突,但是因为每个容器都被分配了独立的内网 IP 地址(可以在控制台看到),所以不会有问题。因为 ownCloud 默认监听8080,所以这里必须也是8080,否则 Bad Gateway(不要问我为什么知道…
    3) 创建了名为 owncloud_internal (你可以用别的名字)的“内网”并把 ownCloud、Redis 和 MariaDB 容器添加进去。
    因为我们不需要,也不应该把 Redis 和 DB 暴露给外面,我们完全可以把他们另外加到一个“内部”网络,让他们在这个网络里通信。对于 Traefik(和用户)来说,Redis 和 DB 是透明的,他们只看到 ownCloud。

    如果你还是有点困惑的话,那下面请参看我用幼儿园水平画的一个示意图。

    《将 ownCloud 迁移到 Docker 容器》

    Traefik 和 Docker 容器之间关系的示意图

  8. 重启 ownCloud 服务器让配置生效 – cd 到 ownCloud yml 文件的目录,然后执行
    docker-compose restart

    如果没有问题的话,在ownCloud 启动完毕后 Traefik 会自动将其反代并申请 HTTPS 证书。

恭喜!现在你应该有一个 Docker 化、HTTPS 加密好的 ownCloud 实例了。

后期维护

升级 ownCloud

相信大家已经感受到了 Docker 带来的便利,那么在 ownCloud 新版本释出后,如何更新呢?这非常简单,因为我们在 .yml 里面(其实是 .env 里面)定义的 ownCloud 版本是 latest,我们只需要执行

docker-compose pull
docker-compose up -d

就更新完毕。

备份 ownCloud 文件

相信你也知道,ownCloud 的文件和配置都在 /var/lib/docker/volumes/examplecom_files/_data/ 文件夹下,分别是 configdata 两个文件夹。如果需要备份的话你可以用 rsync+cron 或者其他办法。

这篇教程就到此为止,由于编写仓促,如果我有遗漏的地方,或者你有更多问题,欢迎在下面评论。谢谢朱军的阅读!

点赞

说点什么

avatar
10000

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

  Subscribe  
订阅