分类: 信息技术

  • About SCP on Synology DSM

    Scp to Synology DSM file system will use SFTP, not through SSH(default port: 8022), so you should use port 9022 (default port for SFTP) instead. And, you should use user space file system root as the target root. For example, you have a shared directory on /volume1/books/, you can use /books/ as the path, /volume1/books/ not works.

    scp -P 9022 convert_to_utf8 username@synology_dsm_hostname:/books/collection/
  • EdgeRouter UPnP2 Configuration with ACL Control

    Configuration Example:

    service {
        upnp2 {
            acl {
                rule 1 {
                    action allow
                    external-port 1024-65535
                    local-port 0-65535
                    subnet 192.168.199.0/24
                }
                rule 2 {
                    action allow
                    external-port 1024-65535
                    local-port 0-65535
                    subnet 192.168.5.121/32
                }
                rule 3 {
                    action deny
                    external-port 1024-65535
                    local-port 0-65535
                    subnet 192.168.0.0/16
                }
            }
            listen-on eth1
            nat-pmp enable
            secure-mode enable
            wan eth0
        }
    }

    Looks like it is compatible with Synology DSM 7.

  • DBeaver Clickhouse session problem

    When using user settings (e.g. SET max_parser_depth = 2000) in DBeaver’s Clickhouse query window, an error will occur.

    A typical error message is:

    SQL Error [113] [07000]: Code: 113. DB::Exception: There is no session or session context has expired. (THERE_IS_NO_SESSION) (version 22.x.x.x (official build)), server ClickHouseNode [uri=http://xxx.xxx.xxx.xxx:8123/system]

    How to resolve it?

    By default, DBeaver does not connect using a session (the CLI for example does). If you require session support (for example to set settings for your session), edit the driver connection properties and set session_id to a random string (it uses the http connection under the hood). Then you can use any setting from the query window.

    From Clickhouse Documentation:

    https://clickhouse.com/docs/en/interfaces/third-party/gui#dbeaver

  • OCFS2 手册

    OCFS2 – Linux共享磁盘集群文件系统

    引言

    OCFS2是一个文件系统。它允许用户存储和检索数据。数据存储在以分层目录树组织的文件中。它是POSIX兼容的文件系统,支持该规范说明的标准接口和行为语义。

    它也是一个共享磁盘集群文件系统,该文件系统允许多个节点同时访问同一磁盘。这就是乐趣的开始,因为允许在多个节点上访问文件系统会打开蠕虫罐。(This is where the fun begins as allowing a file system to be accessible on multiple nodes opens a can of worms.) 如果节点具有不同的架构怎么办?如果节点在写入文件系统时挂掉,该怎么办?如果两个节点上的进程同时进行读写操作,可以期待什么样的数据一致性?如果一个节点在文件仍在另一节点上使用的同时删除了该文件怎么办?

    与答案模糊的大多数共享文件系统不同,OCFS2中的答案定义非常明确。它在所有节点上的行为与本地文件系统完全相同。如果删除了文件,则将删除目录条目,但只要在整个集群中都使用它,inode就会保留。当最后一个用户关闭描述符时,将索引节点标记为删除。

    数据一致性模型遵循相同的原理。它就像在两个不同节点上运行的两个进程在同一个节点上运行一样。不管使用哪种IO模式,对节点的读取都将获得最后的写入。这些模式可以是缓冲,直接,异步,拼接或内存映射的IO。它是完全高速缓存连贯的。

    以REFLINK功能为例,该功能允许用户创建文件的多个可写快照。像所有其他功能一样,此功能完全支持集群。在多个节点上写入的文件可以在另一个节点上安全地重新链接。创建的快照是文件的时间点映像,其中包括文件数据及其所有属性(包括扩展属性)。

    它是日记文件系统。当节点死亡时,存活节点透明地重放死亡节点的日志。这样可以确保文件系统元数据始终保持一致。它还默认使用有序数据日志记录,以确保在日志记录提交之前将文件数据刷新到磁盘,以消除崩溃后文件中出现陈旧数据的这种很小的可能性。

    它是体系结构和字节序中立的。它允许在具有不同处理器(例如x86,x86_64,IA64和PPC64)的节点上并发安装。它处理大小端字节的32位和64位体系结构。

    它是功能丰富的。它支持索引目录、元数据校验和、扩展属性、POSIX ACL、配额、REFLINK、稀疏文件、未写入的扩展数据和内联数据。

    它与主线Linux内核完全集成。OCFS2文件系统在2006年初合并到Linux内核2.6.16中。

    它可快速安装。几乎所有Linux发行版都可以使用它。文件系统在所有磁盘上都是磁盘兼容的。

    它是模块化的。可以将文件系统配置为与其他集群堆栈(如Pacemaker和CMAN)以及其自己的堆栈O2CB一起运行。

    它易于配置。O2CB集群堆栈配置涉及编辑两个文件,一个用于集群布局,另一个用于集群超时。

    它非常有效。OCFS2文件系统消耗很少的资源。它用于在有限的内存环境(例如Xen和KVM)中存储虚拟机映像。

    总之,OCFS2是一个高效,易于配置,模块化,快速安装,完全集成和兼容,功能丰富的体系结构和字节序中性,缓存一致,有序数据日志记录,符合POSIX的共享磁盘集群文件系统。

    概览

    OCFS2是用于Linux的通用共享磁盘集群文件系统,能够同时提供高性能和高可用性。

    由于它提供了本地文件系统语义,因此几乎可以与所有应用程序一起使用。支持集群的应用程序可以利用来自多个节点的缓存一致性并行I/O轻松扩展应用程序。如果节点发生故障,其他应用程序可以利用集群工具对正在运行的应用程序进行故障转移。

    OCFS2文件系统值得关注的特性有:

    可调块大小

    文件系统支持512、1K,2K和4K字节的块大小。几乎总是建议使用4KB。该功能在OCFS2文件系统的所有发行版中均可用。

    可调簇大小

    簇大小也称为分配单位。文件系统支持4K,8K,16K,32K,64K,128K,256K,512K和1M字节的簇大小。对于大多数用例,建议使用4KB。 但是,对于托管大部分都非常大的文件(例如数据库文件、虚拟机映像等)的卷,建议使用较大的值。较大的簇大小可使文件系统更有效地存储大文件。该功能在OCFS2文件系统的所有发行版中均可用。

    字节序和架构中立

    文件系统可以同时挂载在具有不同体系结构的节点上。像32位、64位、Little-Endian(x86,x86_64,ia64)和Big-endian(ppc64,s390x)。该功能在OCFS2文件系统的所有发行版中均可用。

    缓冲、直接、异步、拼接和内存映射的I/O模式

    文件系统支持所有I/O模式,以实现最大的灵活性和性能。它还支持集群范围内的共享可写mmap(2)。在所有发行版中都提供对缓冲、直接和异步I/O的支持。在Linux内核2.6.20中添加了对Splice I/O的支持,并在2.6.23中添加了对共享可写map(2)的支持。

    多个集群堆栈

    文件系统包括一个灵活的框架,使其可以与用户空间集群堆栈(如Pacemaker(pcmk)和CMAN(cman))、其自己的内核内集群堆栈o2cb、无集群堆栈一起工作。

    所有版本均提供对o2cb集群堆栈的支持。

    Linux内核2.6.20中增加了对无集群堆栈或本地挂载的支持。

    Linux内核2.6.26中增加了对用户空间集群堆栈的支持。

    日志记录

    文件系统支持有序(默认)和回写数据日志记录模式,以在电源故障或系统崩溃时提供文件系统一致性。它在Linux内核2.6.28及更高版本中使用JBD2。它在较早的内核中使用JBD。

    基于范围的分配

    文件系统在集群范围内分配和跟踪空间。这与必须跟踪每个块的基于块的文件系统不同。此功能使文件系统在处理大容量文件和大文件时都非常高效。该功能在文件系统的所有发行版中均可用。

    稀疏文件

    稀疏文件是带有空洞的文件。使用此功能,文件系统会延迟分配空间,直到向集群发出写操作为止。此功能已添加到Linux内核2.6.22中,并且需要启用磁盘功能 sparse

    未写入的范围

    未写入的范围也称为用户预分配。它允许应用程序请求在文件中分配一定范围的簇,但不初始化。预分配使文件系统可以用更少,更大的范围来优化数据布局。它还可以提高性能,延迟初始化,直到用户写入簇为止。此功能已添加到Linux内核2.6.23中,并且需要启用磁盘功能 unwritten

    打孔

    打孔允许应用程序删除文件中任意分配的区域。本质上是制造孔。这比将相同范围调零更为有效。此功能在虚拟化环境中特别有用,因为它允许将Guest文件系统中的块丢弃转换为主机文件系统中的打孔,从而允许用户减少磁盘空间使用。 此功能已添加到Linux内核2.6.23中,并且需要启用磁盘功能 sparse以及unwritten

    内联数据

    内联数据也称为in-inode数据,因为它允许在inode块中存储小的文件和目录。这不仅节省了空间,而且对冷缓存目录和文件操作产生了积极影响。当数据不再适合inode块时,将透明地移出数据。此功能已添加到Linux内核2.6.24中,并且需要启用磁盘功能 inline-data

    REFLINK

    REFLINK也称为快速复制。它允许用户自动(并立即)复制常规文件。换句话说,创建常规文件的多个可写快照。之所以称为REFLINK,是因为其看上去比传统快照更像(硬)链接。像链接一样,它是常规的用户操作,受限于reflinked的inode的安全性属性,而不是创建快照通常所需的超级用户特权。像链接一样,它在文件系统中运行。但是,与链接不同,它在数据范围级别上链接inode,从而使每个reflinked的inode在写入时都可以独立增长。多达40亿个inode可以共享一个数据范围。此功能已添加到Linux内核2.6.32中,并且需要启用磁盘功能引用计数( refcount)。

    分配保留

    文件连续性在文件系统性能中起着重要作用。当文件在磁盘上碎片化时,对该文件的读写操作涉及许多Seek动作,从而导致吞吐量降低。另一方面,连续文件可最大程度地减少寻道,从而使磁盘以最大速率执行IO。

    通过分配保留,文件系统会在位图中为所有扩展文件保留一个窗口,从而允许每个文件尽可能连续地增长。由于实际上并没有分配额外的空间,因此如有需要,其他文件可以使用它。此功能已在Linux内核2.6.35中添加,可以使用挂载选项 resv_level进行调整。

    索引目录

    索引目录允许用户在非常大的目录中快速查找文件。它还可以加快创建和取消链接的速度,从而提供更好的整体性能。此功能已添加到Linux内核2.6.30中,并且需要启用磁盘功能 indexed-dirs

    文件属性

    这指的是EXT2样式的文件属性,例如不可变的(immutable),使用 chattr修改并使用lsattr查询的属性。Linux内核2.6.19中添加了此功能。

    扩展属性

    扩展属性指的是一个 name:value对,该对可以与常规文件、目录、符号链接等文件系统对象关联。OCFS2允许每个对象关联无限数量的属性。属性名称的长度最多为255个字节,以第一个NUL字符终止。虽然不是必需的,但建议使用可打印的名称(ASCII)。属性值最多可以是64 KB的任意二进制数据。可以使用标准Linux实用程序setfattrgetfattr修改和列出这些属性。此功能已添加到Linux内核2.6.29中,并且需要启用磁盘功能xattr

    元数据校验和

    该功能允许文件系统检测所有元数据块(如inode和目录)中的无提示损坏。此功能已添加到Linux内核2.6.29中,并且需要启用磁盘功能 metaecc

    POSIX ACL和安全属性

    POSIX ACL允许为文件和目录分配细粒度的自由访问权限。该安全方案比传统的文件访问权限(采用严格的user-group-other模型)灵活得多。

    安全属性允许文件系统支持其他安全机制,例如SELinux,SMACK,AppArmor等。

    这两个安全扩展都是在Linux内核2.6.29中添加的,并且需要启用磁盘功能 xattr

    用户和组配额

    此功能允许通过使用诸如 quotasetquotaquotacheckquotaon之类的标准实用程序,来基于用户和用户组设置使用配额。此功能已添加到Linux内核2.6.29中,并且需要启用磁盘功能usrquotagrpquota

    Unix文件锁

    Unix操作系统历来提供了两个系统调用来锁定文件。 flock或BSD锁以及fcntl或POSIX锁。OCFS2将两个文件锁都扩展到了集群。在一个节点上执行的文件锁与在其他节点上执行的文件锁交互。(File locks taken on one node interact with those taken on other nodes.)

    Linux内核2.6.26中增加了对集群 flock的支持。支持所有flock选项,包括内核在用户收到适当的终止信号时取消锁定请求的功能。所有集群堆栈(包括o2cb)都支持此功能。

    Linux内核2.6.28中增加了对集群 fcntl的支持。但是因为需要群组通信才能使锁连贯,所以只有用户空间集群堆栈pcmkcman支持它,而默认集群堆栈o2cb不支持。

    全面的工具支持

    文件系统具有全面的EXT3样式的工具集,该工具集尝试使用相似的参数来简化操作。它包括 mkfs.ocfs2(格式化),tunefs.ocfs2(调整),fsck.ocfs2(检测),debugfs.ocfs2(调试)等。

    在线调整大小

    可以使用 tunefs.ocfs2动态扩展文件系统。Linux内核2.6.25中添加了此功能。

    最近变更

    O2CB集群堆栈具有全局心跳模式。它允许用户指定所有节点上一致的心跳区域。集群堆栈还允许在线添加和删除节点和心跳区域。

    o2cb是新的集群配置实用程序。这是一个易于使用的实用程序,允许用户在不属于集群的节点上创建集群配置。它替换了已不推荐使用的旧实用程序o2cb_ctl

    ocfs2console已被淘汰。

    o2info是一个新的实用程序,可用于提供文件系统信息。它允许非特权用户查看已启用的文件系统功能,块和簇大小,扩展的文件统计信息,可用空间碎片等。

    o2hbmonitor是一个o2hb心跳监视器。这是一种非常轻巧的实用程序,一旦心跳延迟超过警告阈值,便会将消息记录到系统记录器中。该实用程序在识别遇到I/O延迟的卷时很有用。

    debugfs.ocfs2有一些新命令。 net_stats显示各个节点之间的o2net消息时间。这对于确定会减慢集群操作的节点很有用。 stat_sysdir允许用户转储可用于调试问题的整个系统目录。 grpextents将完整的可用空间碎片转储到集群组分配器中。

    mkfs.ocfs2现在默认启用xattrindexed-dirsdiscontig-bgrefcountextended-slotmapclusterinfo特性标志,以及较旧的默认设置(稀疏sparse,未写入unwritten和内联数据inline-data)。

    mount.ocfs2允许用户指定节点之间的缓存一致性级别。默认情况下,文件系统在完全一致性模式下运行,该模式还会序列化直接I/O。尽管此模式在技术上是正确的,但它限制了集群数据库中的I/O吞吐量。该安装选项允许用户将缓存一致性限制为仅缓冲的I/O,以允许多个节点对同一文件进行并发直接写入。此功能适用于Linux内核2.6.37及更高版本。

    兼容性

    OCFS2开发团队竭尽全力维护兼容性。它尝试在文件系统的所有发行版之间维持磁盘和网络协议的兼容性。即使在添加需要磁盘格式和网络协议更改的新功能时,它也会这样做。为了成功做到这一点,它遵循一些规则:

    1. 磁盘格式更改由一组可以打开和关闭的功能标志管理。内核中的文件系统在挂载期间会检测到这些功能,并且仅在了解所有功能后才继续。遇到此问题的用户可以选择禁用该功能或​​将文件系统升级到较新的版本。
    2. 最新版本的ocfs2-tools与文件系统的所有版本兼容。所有实用程序都会检测磁盘上启用的功能,并且仅在了解所有功能后才能继续。遇到此问题的用户必须将工具升级到较新的版本。

    3. 节点协商网络协议版本,以确保所有节点都了解正活跃的协议版本。

    功能特性标识

    功能标志分为三类,即Compat,Incompat和RO Compat。 Compat,或说是Compatible/兼容,是指那种文件系统不需要完全了解即可安全地读取或写入卷的特性。其中一个示例是 backup-super功能,该功能增加了在文件系统中多个位置备份超级块的功能。由于文件系统通常不会读取或写入备份超级块,因此较早的文件系统可以在启用此功能的情况下安全地挂载卷。 Incompat,或说是Incompatible/不兼容,是指那些文件系统需要充分了解才能读取/写入卷的特性。大多数功能特性都属于此类别。 RO Compat,或说是Read-Only Compatible/只读兼容,是文件系统需要充分了解它才能写入卷的功能特性。较旧的软件可以安全地读取启用这些特性的卷。用户和组配额就是一个例子。由于仅在写入文件系统时才操作配额,因此较旧的软件可以安全地以只读模式挂载此类卷。 功能标志列表,添加它的内核版本,了解它的工具的最早版本等如下:

  • Oracle 集群文件系统 (Cluster File System – OCFS2) 用户指南

    1. 引言

    集群文件系统允许集群中的所有节点通过标准文件系统接口并发访问设备。如此可以轻松管理需在集群中运行的应用程序。

    OCFS(版本1)于2002年12月发布,这使 Oracle Real Application Cluster(RAC)用户无需处理 RAW 设备即可运行集群数据库。该文件系统旨在存储与数据库相关的文件,例如数据文件,控制文件,重做日志,存档日志,等等。

    OCFS2 是"下一代" Oracle 集群文件系统。它被设计为通用集群文件系统。使用它,不仅可以将数据库相关文件存储在共享磁盘上,还可以存储 Oracle 二进制文件和配置文件(共享的 Oracle 主目录),从而使 RAC 的管理更加容易。

    2. 安装

    OCFS2 发行版包含两组软件包。即内核模块和工具。

    可从 http://oss.oracle.com/projects/ocfs2/files/ 下载内核模块,并从 http://oss.oracle.com/projects/ocfs2-tools/files/ 下载工具。

    下载适当的软件包。对于内核模块来说,请下载与发行版、平台、内核版本和内核风格(smp、hugemem、psmp等)匹配的模块。对于工具,只需匹配平台和发行版。

    工具包分为两部分。 ocfs2-tools包含命令行工具,而ocfs2console包含这些工具的GUI前端。 尽管为了易于使用而建议使用控制台,但安装该控制台不是必需的。

    CentOS / RedHat

    在将成为 OCFS2 集群一部分的每个节点上,使用 rpm --installrpm --upgrade命令安装软件包。

    Debian / Ubuntu

    以 Ubuntu 20.04 LTS 为例,发行版的内核已默认内置 OCFS2,因此仅需安装工具包:

    # apt install ocfs2-tools
    

    3. 配置

    OCFS2 具有一个配置文件,即 /etc/ocfs2/cluster.conf。在其中,需要指定集群中的所有节点。该文件在集群中的所有节点上都应该相同。虽然可以动态地将新节点添加到集群,但是其他任何更改(例如名称、IP地址)都需要重新启动集群,以使更改生效。

    强烈建议用户使用 ocfs2console来设置cluster.conf并将其传播到集群中的所有节点。这仅需要在集群中的一个节点上完成。完成此步骤后,用户将在集群中的所有节点上看到相同的/etc/ocfs2/cluster.conf。请注意,不同的cluster.conf将导致不可预测的行为。

    启动 ocfs2console并单击菜单项Cluster,然后单击Configure Nodes。如果集群处于脱机状态,则控制台将启动它并显示一条消息。如果不存在cluster.conf,则控制台将创建一个默认群集名称为ocfs2的集群。

    单击添加将节点添加到集群。需要输入节点名称(与主机名相同)、IP地址和端口。控制台从0到254顺序分配节点序数。注意:此处需确保节点名称与对应配置的主机名称完全一致。

    一旦添加了所有节点,就可以通过单击菜单项 ClusterPropagate Configuration,将配置传播到所有节点。控制台使用ssh传播配置文件。如果尚未为自动ssh登录身份验证设置节点(请参阅ssh联机帮助页),则控制台将提示用户输入要传播到的节点的root用户密码。

    请参考附录A中的 cluster.conf示例和布局说明。

    4. O2CB 集群服务

    OCFS2捆绑了自己的群集堆栈O2CB。集群堆栈包括:

    · NM:跟踪 cluster.conf中所有节点的节点管理器 · HB:心跳服务,在节点加入或离开群集时发出上/下通知 · TCP:处理节点之间的通信 · DLM:分布式锁管理器,可跟踪所有锁,其所有者和状态 · CONFIGFS:挂载在 /config上的用户空间驱动配置文件系统 · DLMFS:内核空间DLM的用户空间接口

    所有集群服务都已打包在 o2cb系统服务中。

    OCFS2 操作(如格式化、挂载等),要求至少在要执行该操作的节点上启动 O2CB 集群服务。

    OCFS2 启动的基本步骤如下:

    1)加载 nodemanagerheartbeattcp模块 2)挂载 nodemanagerheartbeat伪文件系统。 3)通过 o2cb_ctl加载集群信息。这会将所有已知节点添加到节点管理器的伪文件系统中。 4)加载 DLM 模块。 5)加载 OCFS2 模块。 6)挂载 OCFS2 文件系统。

    /etc/init.d/o2cb程序负责在计算机引导期间处理步骤1-3。 如果您从源代码树运行,则脚本位于 vendor/common/o2cb.init处。 另外, o2cb_ctl程序也必须位于您的路径中。 它位于源代码树的 o2cb_ctl/o2cb_ctl

    要检查集群的状态,请执行以下操作:

    # /etc/init.d/o2cb status
    Module "configfs": Not loaded
    Filesystem "configfs": Not mounted
    Module "ocfs2_nodemanager": Not loaded
    Module "ocfs2_dlm": Not loaded
    Module "ocfs2_dlmfs": Not loaded
    Filesystem "ocfs2_dlmfs": Not mounted
    

    加载模块:

    # /etc/init.d/o2cb load
    Loading module "configfs": OK
    Mounting configfs filesystem at /config: OK
    Loading module "ocfs2_nodemanager": OK
    Loading module "ocfs2_dlm": OK
    Loading module "ocfs2_dlmfs": OK
    Mounting ocfs2_dlmfs filesystem at /dlm: OK
    

    使集群 ocfs2联机上线:

    # /etc/init.d/o2cb online ocfs2
    Starting cluster ocfs2: OK
    

    现在,O2CB 集群应该已启动,为 OCFS2 操作(如格式化(下一节中将进行描述))做好准备。

    其他 O2CB 操作如下所述。

    使集群 ocfs2脱机下线:

    # /etc/init.d/o2cb offline ocfs2
    Cleaning heartbeat on ocfs2: OK
    Stopping cluster ocfs2: OK
    

    卸载模块:

    # /etc/init.d/o2cb unload
    Unmounting ocfs2_dlmfs filesystem: OK
    Unloading module "ocfs2_dlmfs": OK
    Unmounting configfs filesystem: OK
    Unloading module "configfs": OK
    

    将 O2CB 配置为 OS 引导时启动:

    # /etc/init.d/o2cb configure
        Configuring the O2CB driver.
    
        This will configure the on-boot properties of the O2CB driver.
        The following questions will determine whether the driver is loaded on
        boot.  The current values will be shown in brackets ('[]').  Hitting
        <ENTER> without typing an answer will keep that current value.  Ctrl-C
        will abort.
    
        Load O2CB driver on boot (y/n) [n]: y
        Cluster to start on boot (Enter "none" to clear) []: ocfs2
        Writing O2CB configuration: OK
    #
    

    如果将集群配置为在引导时加载,则可以启动和停止集群 ocfs2,如下所示:

    # /etc/init.d/o2cb start
    Loading module "configfs": OK
    Mounting configfs filesystem at /config: OK
    Loading module "ocfs2_nodemanager": OK
    Loading module "ocfs2_dlm": OK
    Loading module "ocfs2_dlmfs": OK
    Mounting ocfs2_dlmfs filesystem at /dlm: OK
    Starting cluster ocfs2: OK
    
    # /etc/init.d/o2cb stop
    Cleaning heartbeat on ocfs2: OK
    Stopping cluster ocfs2: OK
    Unmounting ocfs2_dlmfs filesystem: OK
    Unloading module "ocfs2_dlmfs": OK
    Unmounting configfs filesystem: OK
    Unloading module "configfs": OK
    

    5. 格式化

    如果 O2CB 集群处于脱机离线状态,请启动它。格式化操作需要集群处于联机在线状态,因为它需要确保该卷未挂载在集群中的某个节点上。

    可以使用控制台或使用命令行工具 mkfs.ocfs2来格式化卷。这需要在集群中的一个节点上完成,其他节点的相同卷不需要重复执行。

    当集群运行时,在共享磁盘设备上创建 OCFS2 文件系统:

    # mkfs.ocfs2 --cluster-stack=o2cb --cluster-name=ocfs2 /dev/sdc1
    

    要使用控制台进行格式化,请启动 ocfs2console,然后单击菜单项Task,然后单击Format

    在可用设备下拉列表中,选择要格式化的设备。控制台将尽可能列出现有的文件系统类型。

    输入卷标。建议您在设备上设一个标签,以方便管理。卷标在格式化之后还可以更改。

    选择一个簇尺寸(Cluster Size)。支持的尺寸范围从4K到1M。对于数据文件卷,对于大文件,簇尺寸为128K或更大是合适的。

    选择块尺寸(Block Size)。支持的尺寸范围从512字节到4K。由于OCFS2在格式化时,不会分配静态inode区域,因此对于大多数磁盘大小,建议使用4K的块尺寸。另一方面,即使支持512字节的块尺寸,也不建议使用这么小的块尺寸。

    格式化之后,簇尺寸和块尺寸均不可更改。

    输入节点插槽的数量。此数字确定可以同时挂载该卷的节点数。此数字可以在以后增加,但不能减少。

    单击确定以格式化该卷。

    要使用命令行工具 mkfs.ocfs2格式化具有4K块尺寸,32K簇尺寸和4个节点插槽容量的卷,请执行以下操作:

    # mkfs.ocfs2 -b 4K -C 32K -N 4 -L oracle_home /dev/sdf2
    mkfs.ocfs2 1.2.0
    Overwriting existing ocfs2 partition.
    Proceed (y/N): y
    Filesystem label=oracle_home
    Block size=4096 (bits=12)
    Cluster size=32768 (bits=15)
    Volume size=21474820096 (655359 clusters) (5242872 blocks)
    21 cluster groups (tail covers 10239 clusters, rest cover 32256 clusters)
    Journal size=33554432
    Initial number of node slots: 4
    Creating bitmaps: done
    Initializing superblock: done
    Writing system files: done
    Writing superblock: done
    Writing lost+found: done
    mkfs.ocfs2 successful
    

    有关 mkfs.ocfs2的完整帮助,请参见其手册页。

    6. 挂载

    如果O2CB集群服务处于脱机离线状态,请启动它。挂载操作要求集群处于联机在线状态。

    可以使用控制台或命令行工具挂载卷。此操作应在需要挂载卷的每个节点上执行。

    要从控制台挂载,请启动 ocfs2console,选择设备并单击Mount。在对话框中,输入挂载点目录,和可选的挂载选项。单击OK。成功挂载后,设备列表将显示挂载点以及设备。

    从命令行挂载:

    # mount -t ocfs2 /dev/sdf2 /u01
    

    要卸载卷,可以在控制台中选择所需的卷,然后单击 Umount,或执行以下操作:

    # umount /u01
    

    Oracle 数据库用户必须使用 datavolumenointr挂载选项,来挂载包含表决磁盘文件(CRS)、集群注册表(OCR)、数据文件、重做日志、存档日志和控制文件的卷。 datavolume挂载选项,可确保 Oracle 进程使用o_direct标志打开文件。 nointr挂载选项,可确保该设备上的读取和写入不会被信号打断。包括 Oracle home 在内的所有其他卷,都应在没有这些挂载选项的情况下挂载。

    要挂载包含 Oracle 数据文件、表决磁盘等的卷,请执行以下操作:

    # mount -t ocfs2 -o datavolume,nointr /dev/sdf2 /u01
    # mount
    /dev/sdf2 on /u01 type ocfs2 (rw,datavolume,nointr)
    

    要在 OS 引导启动时挂载 OCFS2 卷,需要使用 chkconfig启用o2cbocfs2服务,将o2cb配置为在启动时加载,然后将挂载条目添加到/etc/fstab中,如下所示:

    # cat /etc/fstab
    ...
    /dev/sdf2   /u01      ocfs2  _netdev,datavolume,nointr  0  0
    /dev/sdg2   /orahome  ocfs2  _netdev                    0  0
    ...
    

    _netdev挂载选项对于 OCFS2 卷是必须的。此挂载选项指示在网络启动后要挂载该卷,在网络关闭前要卸载该卷。 (只有 Oracle 数据文件等才需要datavolumenointr挂载选项。)

    ocfs2服务可用于装载和卸载 OCFS2 卷。应始终启用它,以确保在关机期间停止网络之前已卸载 OCFS2 卷。

    # chkconfig --add ocfs2
    ocfs2                     0:off  1:off  2:on   3:on   4:off  5:on   6:off
    # chkconfig --add o2cb
    o2cb                      0:off  1:off  2:on   3:on   4:off  5:on   6:off
    #
    #
    # /etc/init.d/o2cb configure
    ...
        Load O2CB driver on boot (y/n) [n]: y
        Cluster to start on boot (Enter "none" to clear) []: ocfs2
        Writing O2CB configuration: OK
    

    请注意,使用 systemd的节点需要其他fstab选项,文件系统才能在引导时自动挂载:

    /dev/sdc1 /srv ocfs2 _netdev,x-systemd.requires=o2cb.service 0 0
    

    remote_fs.target需要使用选项_netdev来挂载文件系统,而x-systemd.requires选项则在文件系统之前启动集群服务(此选项自systemd v220起可用)。

    按卷标挂载:

    # mount -L datafiles /u01
    

    7. 调整

    调整操作允许增加节点插槽数(增加可同时挂载卷的节点数量),更改卷标和增加日志文件的大小。

    tunefs.ocfs2是执行调整的命令行工具。也可以通过控制台访问此工具。

    如果 O2CB 集群服务处于脱机离线状态,请启动它。调整操作需要集群服务联机在线。

    要使用控制台增加卷的节点插槽数,请单击菜单项 Task,然后单击Edit Node Slot count。 输入新数字,然后单击OK

    使用命令行工具 tunefs.ocfs增加节点插槽数:

    # tunefs.ocfs2 -N 8 /dev/sdf2
    tunefs.ocfs2 1.2.0
    Changing number of node slots from 4 to 8
    Proceed (y/N): y
    Added node slots
    Wrote Superblock
    

    要使用控制台更改卷标,请单击菜单项 Task,然后单击Change Label。输入新卷标,然后单击OK

    使用命令行工具 tunefs.ocfs更改卷标:

    # tunefs.ocfs2 -L "old datafiles" /dev/sdf2
    tunefs.ocfs2 1.2.0
    Changing volume label from datafiles to old datafiles
    Proceed (y/N): y
    Changed volume label
    Wrote Superblock
    

    有关 tunefs.ocfs2的完整帮助,请参阅其手册页。

    8. 文件系统检测

    文件系统检测操作检查文件系统的运行状况。建议定期运行此实用程序,以检测并修复卷中的任何不规则之处。

    fsck.ocfs2是执行检测的命令行工具。也可以通过控制台访问此工具。

    启动 ocfs2console,然后单击菜单项Task,然后单击Check。这将执行只读检测操作,并且可以在集群上挂载的文件系统上执行。命令行等效项:

    # fsck.ocfs2 -n /dev/sdf2
    Checking OCFS2 filesystem in /dev/sdf2:
      label:              oracle_home
      uuid:               dc a2 90 1b 24 1f 40 6e 80 e9 85 dd 6b 45 2d 1a
      number of blocks:   5242872
      bytes per block:    4096
      number of clusters: 655359
      bytes per cluster:  32768
      max slots:          4
    
    /dev/sdf2 is clean.  It will be checked after 20 additional mounts.
    

    要进行修复,请单击菜单项 Task,然后单击Repair。此操作要求 O2CB 集群服务联机在线,以确保该卷未安装在集群中的任何节点上。命令行等效项:

    # fsck.ocfs2 -y /dev/sdf2
    Checking OCFS2 filesystem in /dev/sdf2:
      label:              oracle_home
      uuid:               dc a2 90 1b 24 1f 40 6e 80 e9 85 dd 6b 45 2d 1a
      number of blocks:   5242872
      bytes per block:    4096
      number of clusters: 655359
      bytes per cluster:  32768
      max slots:          4
    
    /dev/sdf2 is clean.  It will be checked after 20 additional mounts.
    

    有关 fsck.ocfs2的完整帮助,请参阅其手册页。

    9. 上下文相关的符号链接(CDSL)

    在共享磁盘集群环境中,有时需要在集群中仅共享文件名或目录名,而不共享内容。例如,在共享的Oracle主目录设置中, network/admin/listener.ora对于所有节点都是公用的,但其内容因节点而异。同样,在共享的根目录设置中,集群中的每个节点都需要不同的/etc/

    在这两种情况下,文件或目录的实际内容取决于主机名。在其他情况下,可能需要内容依赖于体系结构(x86,x86-64或IA64)或节点序数。

    为处理此问题,OCFS2实现了上下文相关的符号链接(CDSL)。简言之,它使用软链接将文件或目录名称与其内容进行匹配。(请注意,使用 datavolume挂载选项装载的卷未启用此特性。)

    ocfs2cdsl是用于创建这些链接的命令行工具。

    要创建与主机名相关的 CDSL文件,请执行以下操作:

    root@node32:admin/# ocfs2cdsl listener.ora
    root@node32:admin/# ls –l listener.ora
    lrwxrwxrwx  1 root root   50 Aug  8 11:41 listener.ora ->
            ../.cluster/hostname/{hostname}/10g/network/admin/listener.ora
    

    编辑此文件并保存新内容。要从集群中的其他某个节点访问该文件,请在编辑之前在该节点上创建此CDSL。(由于主机名是默认主机名,因此不需要在命令中指定 -t hostname。)

    root@node31:admin/# ocfs2cdsl listener.ora
    

    转换现有文件为CDSL文件:

    root@node32:admin/# ocfs2cdsl –c sqlnet.ora
    

    再一次,要从群集中的其他某个节点进行访问,请在访问文件之前在该节点上创建CDSL。

    root@node31:admin/# ocfs2cdsl sqlnet.ora
    

    在这种情况下,您会注意到该文件的内容与将其转换为 CDSL 时的内容相同。两个节点上对该文件的任何新更改将对另一个节点不可见。

    要编辑属于另一个主机或体系结构的 CDSL 文件或目录,请从 OCFS2 卷的根目录(挂载目录)中,将目录更改为 .cluster,然后遍历到包含所需副本的目录。 例如,要在 node31 上编辑 node32 的 listener.ora副本,请执行以下操作:

    root@node31:/# ls -l /ohome/.cluster/hostname/node32/10g/network/admin/l*
    -rw-r--r--  1 root root  3867 Aug  8 11:54 listener.ora
    

    要删除CDSL文件,只需取消链接即可,例如,

    root@node31:admin/# rm listener.ora
    

    有关 ocfs2cdsl的完整帮助,请参见其手册页。

    10. 其他工具

    mounted.ocfs2

    mounted.ocfs2是一个命令行工具,用于列出节点上的所有 OCFS2 卷。此工具将扫描/proc/partitions中列出的所有分区,作为其检测过程的一部分。如果以完全检测模式运行,它将列出已挂载该卷的所有节点。

    要列出系统上的所有 OCFS2 卷,请执行以下操作:

    # mounted.ocfs2 -d
    Device           FS     UUID                                 Label
    /dev/sdb1        ocfs2  e70c75a0-a08c-480a-bf50-ebda4191da30 mm_v2_dbf1
    /dev/sdb2        ocfs2  f49163e8-6288-43c4-a792-e9401fde45fa mm_v2_ctrl
    /dev/sdb3        ocfs2  2d441be2-adb6-4c52-9e19-9a7c2c485dc4 mm_v2_dbf2
    /dev/sdb5        ocfs2  8607eae9-8e4f-4495-84e8-8db0bc9da60c mm_v2_log1
    /dev/sdb6        ocfs2  acfb7b7d-a277-4741-9791-620ea9b82670 mm_v2_log2
    /dev/sdf1        ocfs2  84749537-df45-4a97-aa28-fad63760b674 9ihome
    /dev/sdq1        ocfs2  dca2901b-241f-406e-80e9-85dd6b452d1a oracle_home
    /dev/sdcf1       ocfs2  663764bd-1eed-4b3c-aa48-a98f0be0e574 10ghome
    /dev/sdcf2       ocfs2  8e2d9c21-ef47-4fea-8ac5-2306cc60455e mm_v2_log3
    

    列出系统上已挂载 OCFS2 卷的所有节点:

    # mounted.ocfs2 -f
    Device           FS     Nodes
    /dev/sdb1        ocfs2  node31, node32, node33, node34
    /dev/sdb2        ocfs2  node31, node32, node33, node34
    /dev/sdb3        ocfs2  node31, node32, node33, node34
    /dev/sdb5        ocfs2  node31, node32, node33, node34
    /dev/sdb6        ocfs2  node91, node90
    /dev/sdf1        ocfs2  Not mounted
    /dev/sdq1        ocfs2  node34, node35
    /dev/sdcf1       ocfs2  Not mounted
    /dev/sdcf2       ocfs2  Not mounted
    

    注意:仅当 O2CB 集群服务在线时,才列出节点名称。如果不是,它将列出全局节点序数。

    有关 mount.ocfs2的完整帮助,请参见其手册页。

    附录 A

    配置文件为节格式,具有两种节类型,即集群和节点。由于 OCFS2 不支持多个集群,因此典型的 cluster.conf将具有一个集群节和多个节点节。(请注意,cluster.conf解析器当前期望行从第一列开始(无空格),并以冒号结束,并期望参数行以制表符开头,后跟参数名称和值,参数名和值用等于号分隔。)

    示例 /etc/ocfs2/cluster.conf

    cluster:
        node_count = 2
        name = racdb
    
    node:
        ip_port = 7777
        ip_address = 192.168.0.107
        number = 7
        name = node7
        cluster = racdb
    
    node:
        ip_port = 7777
        ip_address = 192.168.0.106
        number = 6
        name = node6
        cluster = racdb
    

    集群小节有两个参数:

    · node_count – 集群中的节点总数 · name – 集群名称

    节点小节有五个参数:

    · ip_port – IP 端口 · ip_address – IP 地址 · number – 从0到254的唯一节点序数 · name – 主机名(Hostname) · cluster – 与集群小节中相匹配的集群名称

    附录 B – Debian/Ubuntu 特定的信息

    集群设置

    在完成集群配置文件编辑后,可以用使用 ocfs2-tools来启用集群服务:

    # dpkg-reconfigure ocfs2-tools
    

    然后,重启 o2cb 服务,这将加载需要的内核模块,并启动集群服务:

    # service o2cb restart
    
  • Free SSL Cert from Let’s Encrypt! It’s REALLY FRAGRANT!!

    Traefik Docker Compose Example:

    version: '3.7'
    networks:
      livedignet:
        external: true
    services:
      traefik:
        image: "traefik:2.1"
        container_name: "traefik"
        networks:
          - livedignet
        command:
        # - "--log.level=DEBUG"
          - "--api.insecure=true"
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--entrypoints.websecure.address=:443"
          - "--certificatesresolvers.ldhttpchallenge.acme.httpchallenge=true"
          - "--certificatesresolvers.ldhttpchallenge.acme.httpchallenge.entrypoint=web"
        # When u are on test stage. UnComment the line below.
        # - "--certificatesresolvers.ldhttpchallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
          - "--certificatesresolvers.ldhttpchallenge.acme.email=your@email.com"
          - "--certificatesresolvers.ldhttpchallenge.acme.storage=/letsencrypt/acme.json"
        ports:
          - "80:80/tcp"
          - "443:443/tcp"
          - "8080:8080/tcp"
        volumes:
          - type: bind
            source: "./letsencrypt"
            target: "/letsencrypt"
          - type: bind
            source: "/var/run/docker.sock"
            target: "/var/run/docker.sock"

    Web Application Example:

    version: '3.7'
    networks:
      livedignet:
        external: true
    services:
      livedig:
        image: 'wordpress:latest'
        container_name: "livedig"
        networks:
          - livedignet
        external_links:
          - mysql
        environment:
          WORDPRESS_DB_HOST:      'mysql:3306'
          WORDPRESS_DB_USER:      'mysql_usrname'
          WORDPRESS_DB_PASSWORD:  'mysql_password'
          WORDPRESS_DB_NAME:      'mysql_dbname'
          WORDPRESS_TABLE_PREFIX: 'wp_'
        working_dir: '/var/www/html'
        labels:
          - "traefik.enable=true"
    
          - "traefik.http.routers.livedig_http.rule=Host(`livedig.com`)"
          - "traefik.http.routers.livedig_http.entrypoints=web"
          - "traefik.http.routers.livedig_http.middlewares=redirect-to-https"
          - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    
          - "traefik.http.routers.livedig.rule=Host(`livedig.com`)"
          - "traefik.http.routers.livedig.entrypoints=websecure"
          - "traefik.http.routers.livedig.tls.certresolver=ldhttpchallenge"
        ports:
          - '80'
        volumes:
          - type: bind
            source: ./wp-content
            target: /var/www/html/wp-content
            read_only: false
  • ClickHouse Mutation

    在 ClickHouse 中,ALTER UPDATE/DELETE 等,被称为 Mutation。

    一、Mutation 异步执行注意事项

    Mutation 是异步执行的,所以如果有后续任何类型的 Query,无论是 INSERTSELECTALTER等,如果这些 Query 的条件对 Mutation 的结果有依赖,那么都应该等待 Mutation 完全结束之后再操作。

    确认 Mutation 是否完成,可以通过查询 system.mutations表中是否有相关的"is_done=0"记录来完成。

    检测是否有未完成的 Mutations:

    SELECT COUNT() FROM `system`.mutations
    WHERE `database`='${db_name}' AND `table`='${tbl_name}' AND is_done=0;
    

    二、删除挂起的 Mutation

    某同学曾经在 ALTER UPDATE中错误地赋值,将NULL赋给不可为NULL的字段,从而使 Mutation 无法正确执行,然后就一直挂在系统里,而且不断报错。

    2019.09.05 10:46:11.450867 [ 9 ] {} <Error> db_name.tbl_name: DB::StorageReplicatedMergeTree::queueTask()::<lambda(DB::StorageReplicatedMergeTree::LogEntryPtr&)>: Code: 349, e.displayText() = DB::Exception: Cannot convert NULL value to non-Nullable type, Stack trace:
    

    此时便需要将挂起的 Mutation 清理掉。

    首先,通过

    SELECT * FROM `system`.mutations
    WHERE `database`='${db_name}' AND `table`='${tbl_name}' AND is_done=0;
    

    查得与挂起 Mutation 相关记录的 mutation_id字段值等相关信息。然后进行清理。

    清理有两种手法:

    1. 系统自带清理

    语法:

    KILL MUTATION [ON CLUSTER cluster]
      WHERE <where expression to SELECT FROM system.mutations query>
      [TEST]
      [FORMAT format]
    

    例子:

    -- 取消并移除某单表的所有 Mutations:
    KILL MUTATION WHERE database = '${db_name}' AND table = '${tbl_name}'
    
    -- 取消某特定的 Mutation:
    KILL MUTATION WHERE database = '${db_name}' AND table = '${tbl_name}' AND mutation_id = '${mutation_id}'
    

    官方文档参考:KILL MUTATION

    2. 手工清理

    有两种 Case,一种是复制表,一种是非复制表。

    2.1 对于复制表,处理 ZooKeeper 中相应的 ZNode 即可。

    在 ZooKeeper 中找到znode /${path_to_table}/mutations/${mutation_id},将其删除。

    2.2 对于非复制表

    先将表卸载:

    DETACH TABLE `${tbl_name}`;
    

    ${clickhouse_data_dir}/${db_name}/${tblname}/ 目录中,删除 mutation${mutation_id}.txt 文件。

    重新装载表:

    ATTACH TABLE `${tbl_name}`;
    
  • Grafana Active Directory LDAP configuration

    Grafana Active Directory LDAP configuration examples.

    Configration example below allows your active directory member user use their sAMAccountName login into your Grafana service.

    U need manage the Admin/Editor/Viewer roles in AD through add the user to the specialfied AD group.

    Remember, DN is case sensitive, this is very important.

    # Set to true to log user information returned from LDAP
    verbose_logging = false
    
    [[servers]]
    # Ldap server host (specify multiple hosts space separated)
    host = "${livedig.yourServersIPorFQDN}"
    # Default port is 389 or 636 if use_ssl = true
    port = 389
    # Set to true if ldap server supports TLS
    use_ssl = false
    # Set to true if connect ldap server with STARTTLS pattern (create connection in insecure, then upgrade to secure connection with TLS)
    start_tls = false
    # set to true if you want to skip ssl cert validation
    ssl_skip_verify = false
    # set to the path to your root CA certificate or leave unset to use system defaults
    # root_ca_cert = "/path/to/certificate.crt"
    
    # Search user bind dn
    bind_dn = "CN=robot,CN=IT System,CN=Users,DC=example,DC=io"
    # Search user bind password
    # If the password contains # or ; you have to wrap it with trippel quotes. Ex """#password;"""
    bind_password = '${livedig.urUserBaseDNPassword}'
    
    # User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)"
    search_filter = "(&(objectCategory=Person)(sAMAccountName=%s)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"
    
    # An array of base dns to search through
    search_base_dns = ["CN=Users,DC=example,DC=io"]
    
    # In POSIX LDAP schemas, without memberOf attribute a secondary query must be made for groups.
    # This is done by enabling group_search_filter below. You must also set member_of= "cn"
    # in [servers.attributes] below.
    
    ## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available)
    #group_search_filter = ""
    ## An array of the base DNs to search through for groups. Typically uses ou=groups
    #group_search_base_dns = [""]
    
    # Specify names of the ldap attributes your ldap uses
    [servers.attributes]
    name = "givenName"
    surname = "sn"
    username = "sAMAccountName"
    member_of = "memberOf"
    email =  "mail"
    
    # Map ldap groups to grafana org roles
    [[servers.group_mappings]]
    group_dn = "CN=Grafana Admin,CN=IT System,CN=Users,DC=example,DC=io"
    org_role = "Admin"
    # The Grafana organization database id, optional, if left out the default org (id 1) will be used.  Setting this allows for multiple group_dn's to be assigned to the same org_role provided the org_id differs
    # org_id = 1
    
    [[servers.group_mappings]]
    group_dn = "CN=Grafana Editor,CN=IT System,CN=Users,DC=example,DC=io"
    org_role = "Editor"
    
    [[servers.group_mappings]]
    # If you want to match all (or no ldap groups) then you can use wildcard
    group_dn = "CN=Grafana Viewer,CN=IT System,CN=Users,DC=example,DC=io"
    org_role = "Viewer"
    
  • Python中”if __name__ == ‘__main__’:”的解析

    Python中if __name__ == '__main__':的解析

    Python 源代码常会在代码最下方看到形如 if name == 'main':的语句,下面介绍其作用。

    所有Python模块都有一个内置属性 name,其值取决于如何使用模块。

    1. 如果被import,则模块__name__值通常为模块文件名,且不带路径及文件扩展名。
    2. 直接运行,则__name__值为__main__

    所以, if name == 'main'用来判断该模块的使用方式。

    如:

    class UsageTest:
        def __init(self):
            pass
    
        def f(self):
            print('Hello, World!')
    
    if __name__ == '__main__':
        UsageTest().f()
    

    在终端直接运行:

    $ python UsageTest.py
    Hello, World!
    

    import 方式:

    $ python
      >>>import UsageTest
      >>>UsageTest.__name__ # Test模块的__name__
      'UsageTest'
      >>>__name__ # 当前程序的__name__
      '__main__'
    
  • 使用 Docker 构建

    使用 Docker 构建

    原文:https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/ci/docker/using_docker_build.md

    GitLab CI 允许你使用 Docker Engine 构建和测试基于 Docker 的项目。

    这也允许您使用docker-compose和其他 docker-enabled 的工具。

    持续集成/持续部署的新趋势之一是:

    1. 创建应用程序镜像,
    2. 针对创建的镜像运行测试,
    3. 将镜像推送到远程 Registry,并
    4. 从推送的镜像部署到服务器。

    当您的应用程序已经具有可用于创建和测试镜像的 Dockerfile时,它也很有用:

    $ docker build -t my-image dockerfiles/
    $ docker run my-docker-image /script/to/run/tests
    $ docker tag my-image my-registry:5000/my-image
    $ docker push my-registry:5000/my-image
    

    这需要 GitLab Runner 的特殊配置,以在作业期间启用 docker支持。

    Runner 配置

    在作业中有三种方法可以使用 docker builddocker run,每个都有自己的考虑。

    使用 Shell 执行器

    最简单的方法是在 shell执行模式下安装 GitLab Runner。然后 GitLab Runner 作为gitlab-runner用户执行作业脚本。

    1. 安装 GitLab Runner
    2. 在 GitLab Runner 安装期间,选择shell作为执行作业脚本或使用命令的方法:

      sudo gitlab-ci-multi-runner register -n \
        --url https://gitlab.com/ \
        --registration-token REGISTRATION_TOKEN \
        --executor shell \
        --description "My Runner"
      
    3. 在服务器上安装 Docker Engine。 有关如何在不同系统上安装 Docker Engine 的更多信息,请参阅 Supported installations
    4. 新增 gitlab-runner 用户到 docker 组:

      sudo usermod -aG docker gitlab-runner
      
    5. 验证gitlab-runner是否可以访问Docker:
      sudo -u gitlab-runner -H docker info
      
      现在你可以通过将 docker info添加到.gitlab-ci.yml中来验证一切是否正常:
      before_script:
        - docker info
      
      build_image:
        script:
          - docker build -t my-docker-image .
          - docker run my-docker-image /script/to/run/tests
      
    6. 现在可以使用docker命令,如果需要可以安装docker-compose

    注: * 通过在 docker组中添加gitlab-runner,你可以有效地授予gitlab-runner的完整的 root 权限。有关更多信息,请阅读 On Docker security: docker group considered harmful

    使用 docker-in-docker 执行器

    第二种方法是使用专门的 Docker 镜像 docker-in-docker(dind),它安装了所有工具(dockerdocker-compose),并以特权模式在该镜像的上下文中运行作业脚本。

    为了做到这一点,请按以下步骤操作:

    1. 安装 GitLab Runner
    2. 从命令行注册 GitLab Runner 以使用dockerprivileged模式:

      sudo gitlab-ci-multi-runner register -n \
        --url https://gitlab.com/ \
        --registration-token REGISTRATION_TOKEN \
        --executor docker \
        --description "My Docker Runner" \
        --docker-image "docker:latest" \
        --docker-privileged
      
      上面的命令将注册一个新的 Runner 来使用 Docker 所提供的特殊 docker:latest镜像。请注意,它使用privileged模式启动构建和服务容器。如果要使用docker-in-docker模式,您始终必须在 Docker 容器中使用privileged = true。 上面的命令将创建一个类似于这个的 config.toml条目:
      [[runners]]
        url = "https://gitlab.com/"
        token = TOKEN
        executor = "docker"
        [runners.docker]
          tls_verify = false
          image = "docker:latest"
          privileged = true
          disable_cache = false
          volumes = ["/cache"]
        [runners.cache]
          Insecure = false
      
    3. 您现在可以在构建脚本中使用docker(请注意包含docker:dind服务)
      image: docker:latest
      
      # When using dind, it's wise to use the overlayfs driver for
      # improved performance.
      variables:
        DOCKER_DRIVER: overlay
      
      services:
        - docker:dind
      
      before_script:
        - docker info
      
      build:
        stage: build
        script:
          - docker build -t my-docker-image .
          - docker run my-docker-image /script/to/run/tests
      

    Docker-in-Docker 运行良好,是推荐的配置,但并不是没有挑战:

    • 启用--docker-privileged禁用了容器的所有安全机制,并使你的主机由于特权升级而暴露,从而导致容器突破(主机-容器屏障)。有关更多信息,请查看官方 Docker 文档 Runtime privilege and Linux capabilities
    • 当使用 docker-in-docker 时,每个作业都处于一个干净的环境中,没有过去的历史。并发作业工作正常,因为每个构建都获得自己的 Docker Engine 实例,因此不会相互冲突。但这也意味着作业可能会更慢,因为没有缓存层。
    • 默认情况下,docker:dind使用--storage-driver vfs,这是最慢的形式。要使用其他驱动程序,请参阅使用 overlayfs 驱动程序

    使用这种方法的示例项目可以在这里找到:https://gitlab.com/gitlab-examples/docker.

    Use Docker socket binding

    第三种方法是将 /var/run/docker.sock绑定装载到容器中,以便 docker 在该镜像的上下文中可用。

    为了做到这点,遵循以下步骤:

    1. 安装 GitLab Runner.
    2. 从命令行注册 GitLab Runner 以使用docker并共享/var/run/docker.sock

      sudo gitlab-ci-multi-runner register -n \
        --url https://gitlab.com/ \
        --registration-token REGISTRATION_TOKEN \
        --executor docker \
        --description "My Docker Runner" \
        --docker-image "docker:latest" \
        --docker-volumes /var/run/docker.sock:/var/run/docker.sock
      
      上面的命令将注册一个新的 Runner 来使用 Docker 提供的特殊 docker:latest镜像。请注意,它正在使用 Runner 本身的 Docker 守护进程,docker 命令产生的任何容器都将是 Runner 的兄弟,而不是所运行程序的子进程。这可能会有不适合您的工作流程的复杂性和局限性。 上面的命令将创建一个类似于这个的 config.toml条目:
      [[runners]]
        url = "https://gitlab.com/"
        token = REGISTRATION_TOKEN
        executor = "docker"
        [runners.docker]
          tls_verify = false
          image = "docker:latest"
          privileged = false
          disable_cache = false
          volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
        [runners.cache]
          Insecure = false
      
    3. 您现在可以在构建脚本中使用docker(请注意,在 Docker 执行器中使用 Docker 时,不需要包含docker:dind服务):
      image: docker:latest
      
      before_script:
      - docker info
      
      build:
        stage: build
        script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
      

    虽然上述方法避免在特权模式下使用 Docker,但您应该了解以下影响:

    • 共享 docker 守护进程,禁用了容器的所有安全机制,并将主机暴露给特权提升,从而导致容器突破屏障。例如,如果一个项目运行docker rm -f $(docker ps -a -q),它将删除 GitLab Runner 容器。
    • 并发作业可能无效;如果你的测试创建了具有特定名称的容器,它们可能会相互冲突。
    • 将文件和目录从源代码库共享到容器中可能无法正常工作,因为卷装载是在主机上下文中完成的,而不是在构建容器中,例如:
      docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
      

    使用 OverlayFS 驱动程序

    默认情况下,使用 docker:dind时,Docker 使用vfs存储驱动程序,每次运行时都会拷贝文件系统。磁盘操作非常密集,如果使用不同的驱动程序(例如overlay),则可以避免这种情况。

    1. 确保使用最近的内核,最好是>= 4.2
    2. 检查overlay模块是否加载:
      sudo lsmod | grep overlay
      
      如果没有结果,那就没有加载。加载之:
      sudo modprobe overlay
      
      如果一切顺利,您需要确保在系统重启时也加载该模块。Ubuntu 系统上是通过编辑 /etc/modules完成的。添加以下行:
      overlay
      
    3. .gitlab-ci.yml顶部定义一个变量以使用该驱动:
      variables:
        DOCKER_DRIVER: overlay
      

    使用 GitLab 容器 Registry

    注: – 此功能需要 GitLab 8.8 和 GitLab Runner 1.2。 – 从 GitLab 8.12 开始,如果你的帐户启用了两步认证,则需要传递个人访问令牌而不是密码,才能登录到 GitLab 的 Container Registry。

    一旦构建了 Docker 镜像,就可以将其推送到内置的 GitLab 容器 Registry 中。例如,如果你在 runner 上使用 docker-in-docker,那.gitlab-ci.yml可能如下:

     build:
       image: docker:latest
       services:
       - docker:dind
       stage: build
       script:
         - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.com
         - docker build -t registry.example.com/group/project/image:latest .
         - docker push registry.example.com/group/project/image:latest
    

    必须使用为你创建的特殊 gitlab-ci-token用户,才能推送到连接到项目的 Registry。它的密码由$CI_JOB_TOKEN变量提供。这允许您自动构建和部署 Docker 镜像。

    您也可以利用其他变量来避免硬编码:

    services:
      - docker:dind
    
    variables:
      IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
    
    before_script:
      - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    
    build:
      stage: build
      script:
        - docker build -t $IMAGE_TAG .
        - docker push $IMAGE_TAG
    

    在这里, $CI_REGISTRY_IMAGE将解析为与该项目相关联的 Registry 地址,$CI_COMMIT_REF_NAME将解析为该作业所在分支或标签的名称。还声明了我们自己的变量$IMAGE_TAG,将两者结合起来,以节省我们在script部分中的输入。

    这是一个更详细的例子,将任务分解为 4 个流水线(pipeline)阶段,包括并行运行的两个测试。 build存储在容器 Registry 中,并在后续阶段使用,需要时则下载该镜像。对master的更改也被标记为latest,并使用特定于应用程序的部署脚本进行部署:

    image: docker:latest
    services:
      - docker:dind
    
    stages:
      - build
      - test
      - release
      - deploy
    
    variables:
      CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-
    
    image:$CI_COMMIT_REF_NAME
      CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project/my-image:latest
    
    before_script:
      - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.com
    
    build:
      stage: build
      script:
        - docker build --pull -t $CONTAINER_TEST_IMAGE .
        - docker push $CONTAINER_TEST_IMAGE
    
    test1:
      stage: test
      script:
        - docker pull $CONTAINER_TEST_IMAGE
        - docker run $CONTAINER_TEST_IMAGE /script/to/run/tests
    
    test2:
      stage: test
      script:
        - docker pull $CONTAINER_TEST_IMAGE
        - docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test
    
    release-image:
      stage: release
      script:
        - docker pull $CONTAINER_TEST_IMAGE
        - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
        - docker push $CONTAINER_RELEASE_IMAGE
      only:
        - master
    
    deploy:
      stage: deploy
      script:
        - ./deploy.sh
      only:
        - master
    

    使用容器 Registry 的注意事项:

    • 在运行命令之前必须先登录到容器 Registry。把它放在before_script里,会在每个作业之前运行它。
    • 使用docker build --pull可以确保 Docker 在构建前获取 base 镜像的任何更改,以防您的缓存失效。这需要运行更长时间,但意味着不会遇到未打安全补丁的 base 镜像。
    • 在每个docker run之前做一个明确的docker pull,确保获取刚构建的最新镜像。如果您正在使用多个会在本地缓存镜像的 Runner,这一点尤为重要。在镜像标签中使用 git SHA 又使得这不太必要,因为每个作业都将是唯一的,并且您不应该有一个过时的镜像,但是如在依赖更改后,重新构建给定的 Commit,这仍然可能(有过时的镜像)。
    • 同时发生多个作业的情况下,你不会想直接构建为latest