OCFS2 手册

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.) 如果节点具有不同的架构怎么办?如果节点在写入文件系统时挂掉,该怎么办?如果两个节点上的进程同时进行读写操作,可以期待什么样的数据一致性?如果一个节点在文件仍在另一节点上使用的同时删除了该文件怎么办?

继续阅读

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

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

1. 引言

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

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

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

继续阅读

刘军宁:市场经济与有限政府

现在,我们面临的问题是,市场经济在中国的确立需要我们的社会作出什么样的、根本的、不可或缺的变革,才能具备与市场经济相兼容的社会政治条件。

在20世纪世界上为数不多的试图彻底回避市场经济的国家中,中国无疑曾经是最执著、肯为之付出莫大代价的国家之一。时至今日,中国虽然踏上了市场经济的不归路,但仍然面临著许多有形的和无形的巨大阻力。另一方面,市场经济在中国是不可逆转的进程,中国人对市场经济的选择是义无反顾的选择。现在,我们面临的问题是,市场经济在中国的确立需要我们的社会作出什么样的、根本的、不可或缺的变革,才能具备与市场经济相兼容的社会政治条件。

这样的变革的重要性在于,如果我们不准备满足这样的条件,那么市场经济就无法在中国建立起来,我们费了巨大的周折才作出的对市场经济的选择就可能半途而废。如此看来,市场经济所必需的社会政治条件又是些什么样的条件呢?这与人们常常问到的另一个问题相关,这个问题是:中国当今面临的最大的挑战是什么?中国目前面临的问题可以说是千千万,有政治方面的,有经济方面的,有人口方面的,有资源方面的,还有社会道德和文化传统方面的。有来自本土的挑战,也有来自异域的挑战。但是,我认为,目前中国面临的最大的挑战还是来自实行市场经济的必要性与落实市场经济的社会与政治条件的缺乏之间所构成的紧张关系所形成的挑战,或者说,是构建市场经济所必不可少的有限政府(Limited Government),实现由与计划经济相适应的无限政府向与市场经济相适应的有限政府的变革。在我国,从1978年的联产计酬承包责任制、到1992年正式宣布以市场经济取代计划经济,再到1997年前不久正式宣布对公有制进行重大的改造,市场经济在中国的确立已是历史的必然。那么,随之带来的一个新的重大问题是,如何构建一个与市场经济相配套的限政秩序,迎来一个前所未有的,而又无法回避的限政时代?又如何解决限制政府的必要与限制政府的难度之间的矛盾?

从早期实行市场经济的国家(如英国、美国)的经验来看,人们当初并不知道世界上有个叫市场经济的的东西,没有在宪法和建国时期的任何官方文件中规定要实行市场经济,但是一旦宪法(不论是成文的、还是不成文的)保障了公民的财产权和经济自由,用代表制度、宪政、分权制衡和司法独立的方式对政府的权力进行了有效的限制,即有限政府;一旦市场经济到位,有限政府到位,民主也就水到渠成。对第一波的民主化国家来说,民主只是追授的荣誉,而不是刻意追求的成果。所以在市场经济为一方,与公民的权利、自由和有限政府为另一方的关系中,前者是果,后者是因。仅有实行市场经济的意愿,而没有确立相应的公民的权利和自由及限政,市场经济只能是空中楼阁。相反,如果承认并保障财产权与经济自由等民权和自由权,实行限政,那么,市场经济会自动实现。所以,能否实行市场经济,关键并不在与是否有这样的意愿,而是更在与作为市场经济之因的必要配套条件是否具备。所以,没有限政时代的来临,就不会有市场经济的成功确立。

让我们先看看市场经济的两个核心要素:财产权、经济自由与有限政府的关系。

继续阅读

棒棒医生:循证的崩溃

  • 作者:棒棒医生

循证医学在中国本来就虚弱得可怜,在暴虐的新冠病毒面前,已经濒于全面崩溃。

临床决策必须建立在当前最佳证据的基础上,这一原则被“萨妹”无情地蹂躏和唾弃。

阿比朵尔和达芦那韦,仅仅做过体外细胞实验,就敢于宣称对新冠肺炎有效,甚至是“克星”,直接在临床广泛使用,某院士还呼吁要把它纳入国家卫健委第六版方案中去。须知,体外细胞实验到可以临床应用中间还隔着动物实验、一期二期和三期临床试验的十万八千里,这个漫长的过程会淘汰至少99%的所谓“有效”。如果体外实验有效就算有效的话,那么,一夜之间发明一万种有效药物有何难哉?

奥司他韦,本是治疗流感的“特效”药物,它的效果也不过是起病24小时内服用可以缩短病程减轻症状(不超过40%)而已。关键是,这个药是针对流感病毒神经络氨酸酶立体结构而在分子水平进行精准设计的,它怎么可能会对新冠病毒也有效呢?但是,武汉前线国内顶级医院大力应用后,已经在基层全面开花,即使国家方案不推荐也无济于事。

抗菌药物对病毒性肺炎的治疗无效是绝对的国际共识和医学常识,五个版本的国家方案里也一再强调要“避免盲目和不恰当使用”,但是,怎敌得住顶级医院的先锋示范呢?现在的基层医院,早已不仅仅用于重型危重型合并细菌感染者,而是同时用于几乎所有的轻型和普通型,名之曰“预防”。国家开展“抗菌药物专项整治”活动“十年辛苦不寻常”的成果,在这次疫情中被碾压得体无完肤。

大剂量维生素C抗自由基,这一疗法据说来自美国一位华裔专家。然而,这个专家很快就被挖出专业和病毒以及传染病没有关系,发表的几十篇论文不但和病毒无关,连一作和通讯作者都不是,这样的人说的话不但在大众中疯传,专业医生们也狐疑不定或者坚信不疑地直接临床试用了。没有任何临床证据和理论依据的疗法可以凭着传闻就直接大量用于临床,循证精神荡然无存。

康复患者血浆疗法是最新的“特效疗法”,已经由官方正式推荐应用。但是,它仅仅来自武汉区区10例重症病人的观察,没有对照,没有入组的标准说明,也没有可信的终点指标,这种“疗效”在循证医学里连最低级别的证据都算不上。如果搜索以往的证据则可以看到,在埃博拉和流感等疾病的多个国际大型研究中,血浆疗法早已经被否定。而理论上,血浆疗法如果有效,是基于康复者血液中的抗体,是特异作用于病毒的,那么,只有早期轻型时应用才会有效果。等到危重时,病毒已经不是问题,抗体对付不了严重的炎症反应。此外,血液的安全性和可及性也很难保证。这一种既往被证明无效的安全风险高的疗法首先需要进行严谨设计的临床研究,确保受试者的安全,然后才能宣称“有效”,然后才能推荐用于临床。不幸,所有的流程都被“特事特办”了。

纷纷你方唱罢我登场,我只看到一个药还在坚持着循证的信念,那就是瑞德西韦。这个药在国外个案已经显示了“特效”,之前也通过了体外实验、动物实验、一期和二期临床试验,证明是安全的,但它仍然不敢宣称对新冠肺炎是“有效”的,非要等到四月份揭盲以后才敢下结论。它为什么不敢?因为这是现代药物不可逾越的雷池!如果可以随意逾越的话,现代医学的大厦瞬间就崩溃了!

当然,更多的中药,1号2号直到N号,需另当别论,它们过于博大精深,浅薄的循证对它们是没有约束力的。

继续崩溃到哪里才是尽头呢?我不知道,我希望至少在悬崖的边上能止住。

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"
      - "[email protected]m"
      - "--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 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}/${tbl_name}/ 目录中,删除 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