分类 Coding 下的文章

由于众所周不知的原因,docker hub已经不能从国内正常访问。这时我们可以通过访问正常访问的docker hub镜像网站,实现正常拉取镜像。

本文介绍如何通过在Cloudflare中部署一个镜像页面,并合理配置服务器或者Windows/MacOS中的Docker引擎,实现曲线救国。

一、前提

二、GitHub CF-Workers-docker.io 项目

docker-hub-01.png

CF-Workers-docker.io 是一个 Docker 仓库镜像代理工具。官方描述为:

这个项目是一个基于 Cloudflare Workers 的 Docker 镜像代理工具。它能够中转对 Docker 官方镜像仓库的请求,解决一些访问限制和加速访问的问题。

仓库地址 https://github.com/cmliu/CF-Workers-docker.io 仓库说明中也包含了其他已经部署的镜像,如果不想自己部署Cloudflare,也可以直接跳过第三部分,直接拿来进入第四部分的配置进行操作。

注册登陆GitHub并进入该仓库后,Fork到自己的仓库。

三、在Cloudflare部署代理工具

  1. 登录Cloudflare
  2. 点击 Worker和Pages 再点 概述(Overview) 再点 新建(Create)
    docker-hub-02.png
  3. 新界面中切换到 Pages 页签,点击 连接到Git(Connect to Git)
    docker-hub-03.png
  4. 选择 GitHub 页签,并选择自己的github账户。注意:如果之前没有关联,可能需要通过添加账户(Add Account)先行添加。之后在下方选择之前fork到自己仓库的项目,名称应该为 CF-Workers-docker.io,最后点击右下角的开始设置(Begin Setup)按钮。
    docker-hub-04.png
  5. 确认页面不用修改,直接点击保存并部署(Save and Deploy),等待部署完成。
    docker-hub-05.png
  6. 点击继续处理项目(Continue to Process Project)
  7. 最后得到一个域名,类似cf-workers-docker-io-xxx.pages.dev。点击页面右上角的访问(Visit),打开就是docker hub的搜索页面。

四、设置docker守护进程配置中的 registry-mirrors 属性

  • 原理:通过设置docker守护进程配置docker-daemon.json文件中的registry-mirrors顶级属性,可以让Docker直接访问docker hub的镜像站点。与Ubuntu的镜像软件中心、Node.js npm的源地址、Python pipy的源地址等是一个道理。
  • 注意:编辑docker-daemon.json文件时,务必遵守JSON格式规则,否则会引起Docker崩溃。

Linxu 服务器上使用代理

  1. 使用ssh或者类似工具登陆服务器。修改 /etc/docker/daemon.json 文件,(如果没有则直接新建),添加registry-mirrors 顶级属性"registry-mirrors" : [ "https://刚刚得到的域名" ]。最后看起来应该类似:
{
  "debug": false,
  "registry-mirrors" : [ "https://cf-workers-docker-xxx.pages.dev" ]
}
  1. 重启启动docker服务(通过systemctl reload或者直接重启服务器),之后便可以使用

MacOSWindows 中使用代理

MacOSWindows 一般都使用Docker Desktop管理Docker,可以非常方便的配置Docker引擎

  1. 打开docker desktop,依次点击右上角⚙️(齿轮),设置界面中的Docker Engine, 并在左侧的json文本编辑器中添加registry-mirrors 顶级属性"registry-mirrors" : [ "https://刚刚得到的域名" ]。最后看起来类似:

    {
      "builder": {
     "gc": {
       "defaultKeepStorage": "20GB",
       "enabled": true
     }
      },
      "debug": true,
      "experimental": false,
      "registry-mirrors" : [ "https://cf-workers-docker-xxx.pages.dev" ]
    }

    docker-hub-06.png

  2. 点击右下角 应用并重启(Apply and Restart) 之后便可以使用代理拉取镜像!

什么是 Docker

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

docker-on-linux.png

什么是 Docker 仓库

仓库(Repository)是集中存放镜像的地方。
一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 docker.io/ubuntu 来说,docker.io 是注册服务器地址,ubuntu 是仓库名。
大部分时候,并不需要严格区分这两者的概念。

什么是 Docker 私有仓库(Registry)

一般情况下,我们可以通过Docker Hub在整个网络中下载或者分享Docker镜像。同时相比Docker Hub这样的公共仓库,用户可以创建私有仓库供私人使用。

官方提供了docker-registryDocker镜像用于构建私有的镜像仓库。目前是 v2.x 版本。

搭建Docker私有仓库的步骤

一般需要以下几个大致的步骤:

  1. 从Docker Hub拉取docker-registry镜像
  2. 创建docker-registry配置文件,并启动docker-registry
  3. 通过nginxdocker-registry发布到互联网。(内部使用可忽略此步骤)

搭建 Docker 私有仓库Web管理平台

使用docker-compose一次性完成所有搭建

参考

  1. Docker私有仓库搭建与界面化管理
  2. 私有仓库

Managed Package有许多限制,比如无法在外部/本地代码中序列化受控包中的Apex Class

JSON Support Considerations

Only custom objects, which are sObject types, of managed packages can be serialized from code that is external to the managed package. Objects that are instances of Apex classes defined in the managed package can't be serialized.

假如有个包Px,命名空间Px。我们有个Apex Class需要调用里面的对象并JSON序列化。

直接JSON序列化是不行的

包中代码:

//Inside package Px
global class C {
  global String stringA;
  global Integer StringB;
}

本地代码调用,会失败:

@isTest
private class TestFromLocal {
    @isTest static void testSerialize() {
    try{ 
      JSON.serialize(new Px.C());
      System.assert(false,'This should fail');
    } catch (Exception ex) {
      System.assertEquals('Cannot serialize type from package: Px.C',ex.getMessage());
    }
  }
}

但是可以通过扩展Apex Class来序列化

我们可以利用扩展(extends)一个虚拟类(virtual class)来实现序列化!

1. 保证需要序列化的Class是virtual

包中代码:

//Inside package Px
global virtual class C {
  global String stringA;
  global Integer StringB;
}

2. extends it!

@isTest
private class TestFromLocal {

class Ext extends Px.C { }

@isTest static void testSerialize() {
  Ext virt = new Ext();
  virt.stringA = 'bla';
  virt.IntgerB = 1;
  System.assertEquals('{"stringA":"bla", "IntgerB": 1}',JSON.serialize(virt));
}

}

这样就可以正常调用了!

参考

1.Salesforce: Serializing Objects from a Managed Package

软件测试是一个工程

属于软件质量控制、质量保证的一部分。由于软件的复杂性,软件测试需要分出层次来,并由不同的人完成不同的测试任务,在不同层面不同维度和角度保证软件按照设计和预期运行。

软件测试并不是银弹,不能保证软件100%没有问题。但是组织良好的软件测试工程,相比随意和混乱的测试甚至不测试,能够以比较低的成本发现大部分的软件缺陷,保证软件质量。

软件测试按照不同的层级,分为:单元测试, 系统(功能)测试, UAT测试, 生产测试, 回归测试

单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。这部分测试过程由负责开发相应软件模块单元的开发人员执行,一般软件都会要求在源代码层面,单元测试能够测试(覆盖)到大部分的软件源代码。比如Salesforce要求部署到生产环境的代码必须有75%的代码覆盖率。

系统(功能)测试

一般会把功能测试和系统测试分开,但是实际上这些测试过程都由软件测试工程师执行。功能测试偏向每个功能运行的正确和准确,是从细节的角度解读测试;而系统测试则偏向整个软件系统的整体运行,包含一些非功能性测试(例如响应时间、安全等),是从整体的角度解读测试。但是不论功能测试或者系统测试,都是从逻辑的层面确保软件的每个功能和整个软件系统的正常运行。

UAT测试

UAT测试,即用户认可测试,必须由将使用软件的最终用户进行。测试将模拟业务中的实际事件和日常使用情况,因此您可以确认该软件是否满足用户的需求。UAT测试是从业务的层面出发,保障系统的满足实际业务需求。

生产测试

在生产环境中重复进行系统(功能)测试的一部分测试,这将确认代码和配置是否已从测试或沙盒环境正确转移到生产环境中中。建议在此步骤后重复进行 UAT 测试。

回归测试

一旦发布并安装了新的软件更新,或者检测到并修复了错误,都应该进行回归测试。即从头到尾对受升级或者修复错误影响的软件部分从头到位测试一遍(回归)。它确保所有以前的使用和过程在进行更改后仍然正常有效地运行。

可以看到,不同层次和类别的测试,有不同的角度和目的,也由不同的人员执行。让软件开发人员或者最终用户直接完成所有的测试工作,都是不可靠的。因为他们掌握的知识技能和他们的背景以及经验,不足以应对整个复杂的分层次的软件测试工程。

2GP Managed Package

Salesforce 第二代管控包(Second-Generation Managed Packages)是Salesforce最新推广的最佳实践。通过Salesforce CLI,我们可以很方便的制作和部署第二代管控包。本文只是简略的将核心步骤展示出来:

涉及的各种环境

整个第二代管控包需要用到4类org:

  1. Dev Hub Org:承载所有二代包。可以将各个命名空间的二代包链接到这个org。最好是PBO
  2. Namespace Org:一般是Developer org,在其中申请相应的命名空间,由于命名空间一旦申请便与该org永久绑定且不可修改,申请时请慎重。
  3. Scratch Orgs:在开发测试中使用。
  4. Production Orgs:按照和使用包的生产环境。

2GP Packages.drawio.svg
相关org关系示意图。

10步完成制作

  1. 创建一个 SFDX 项目

    sfdx force:project:create --outputdir expense-manager-workspace --projectname expenser-app
  2. 授权Dev Hub环境,该环境必须启用Dev Hub功能和未锁定包和第二代管控包功能

    sfdx auth:web:login -d -a devHub
  3. 创建一个草稿环境(scratch org)并在其中开发包

    sfdx force:org:create -f config/project-scratch-def.json -u scratchOrg1
  4. 保证所有要打包的组件都已经在当前的项目文件夹内
  5. sfdx-project.json文件中,使用命名空间属性指定命名空间。例如:“namespace”:“exp-mgr”
  6. 从 SFDX 项目文件夹,直接创建管控包:

    sfdx force:package:create --name "Expense Manager" --path force-app --packagetype Managed
  7. 检查项目文件夹中的sfdx-project.json文件,CLI 会自动更新项目文件,使其包含上一步创建的包的信息:

    {
    "packageDirectories": [
       {
          "path": "force-app",
          "default": true,
          "package": "Expense Manager",
          "versionName": "ver 0.1",
          "versionNumber": "0.1.0.NEXT"
       }
    ],
    "namespace": "exp-mgr",
    "sfdcLoginUrl": "https://login.salesforce.com",
    "sourceApiVersion": "51.0",
    "packageAliases": {
       "Expense Manager": "0Hoxxx"
    }
    }
  8. 创建一个包的版本,Salesforce CLI会自动处理包的版本号等

    sfdx force:package:version:create --package "Expense Manager" --installationkey test1234 --wait 10
  9. 在另一个创建好的草稿环境中,安装并测试这个版本的包:

    sfdx force:package:install --package "Expense [email protected]" --targetusername MyTestOrg1 
    --installationkey test1234 --wait 10 --publishwait 10
  10. 安装包后,打开scratch org查看包:

    sfdx force:org:open --targetusername MyTestOrg1
\frac{a}{b^2}

参考:

  1. Salesforce DX Developer Guide
  2. Link a Namespace to a Dev Hub Org
  3. Create and Register Your Namespace for Second-Generation Managed Packages
  4. Workflow for Second-Generation Managed Packages