github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/simulations/adapters/docker.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:44</date> 10 //</624342660525592576> 11 12 13 package adapters 14 15 import ( 16 "errors" 17 "fmt" 18 "io" 19 "io/ioutil" 20 "os" 21 "os/exec" 22 "path/filepath" 23 "runtime" 24 "strings" 25 26 "github.com/docker/docker/pkg/reexec" 27 "github.com/ethereum/go-ethereum/node" 28 "github.com/ethereum/go-ethereum/p2p/discover" 29 ) 30 31 var ( 32 ErrLinuxOnly = errors.New("DockerAdapter can only be used on Linux as it uses the current binary (which must be a Linux binary)") 33 ) 34 35 //Dockeradapter是在Docker中运行模拟节点的节点适配器。 36 //容器。 37 // 38 //建立了一个包含当前二进制at/bin/p2p节点的Docker映像。 39 //执行时运行基础服务(请参见说明 40 //有关详细信息,请参阅execp2pnode函数) 41 type DockerAdapter struct { 42 ExecAdapter 43 } 44 45 //newdockeradapter构建包含当前 46 //二进制并返回dockeradapter 47 func NewDockerAdapter() (*DockerAdapter, error) { 48 //因为Docker容器在Linux上运行,而这个适配器运行 49 //当前容器中的二进制文件,必须为Linux编译。 50 // 51 //要求这样做是合理的,因为打电话的人可以 52 //在Docker容器中编译当前二进制文件。 53 if runtime.GOOS != "linux" { 54 return nil, ErrLinuxOnly 55 } 56 57 if err := buildDockerImage(); err != nil { 58 return nil, err 59 } 60 61 return &DockerAdapter{ 62 ExecAdapter{ 63 nodes: make(map[discover.NodeID]*ExecNode), 64 }, 65 }, nil 66 } 67 68 //name返回用于日志记录的适配器的名称 69 func (d *DockerAdapter) Name() string { 70 return "docker-adapter" 71 } 72 73 //newnode使用给定的配置返回一个新的dockernode 74 func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) { 75 if len(config.Services) == 0 { 76 return nil, errors.New("node must have at least one service") 77 } 78 for _, service := range config.Services { 79 if _, exists := serviceFuncs[service]; !exists { 80 return nil, fmt.Errorf("unknown node service %q", service) 81 } 82 } 83 84 //生成配置 85 conf := &execNodeConfig{ 86 Stack: node.DefaultConfig, 87 Node: config, 88 } 89 conf.Stack.DataDir = "/data" 90 conf.Stack.WSHost = "0.0.0.0" 91 conf.Stack.WSOrigins = []string{"*"} 92 conf.Stack.WSExposeAll = true 93 conf.Stack.P2P.EnableMsgEvents = false 94 conf.Stack.P2P.NoDiscovery = true 95 conf.Stack.P2P.NAT = nil 96 conf.Stack.NoUSB = true 97 98 //监听给定端口上的所有接口,当我们 99 //初始化nodeconfig(通常是随机端口) 100 conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port) 101 102 node := &DockerNode{ 103 ExecNode: ExecNode{ 104 ID: config.ID, 105 Config: conf, 106 adapter: &d.ExecAdapter, 107 }, 108 } 109 node.newCmd = node.dockerCommand 110 d.ExecAdapter.nodes[node.ID] = &node.ExecNode 111 return node, nil 112 } 113 114 //dockernode包装execnode,但exec的是docker中的当前二进制文件 115 //容器而不是本地 116 type DockerNode struct { 117 ExecNode 118 } 119 120 //docker command返回一个命令,exec是docker中的二进制文件 121 //容器。 122 // 123 //它使用了一个shell,这样我们就可以通过 124 //使用--env标志将变量转换为容器。 125 func (n *DockerNode) dockerCommand() *exec.Cmd { 126 return exec.Command( 127 "sh", "-c", 128 fmt.Sprintf( 129 `exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`, 130 dockerImage, strings.Join(n.Config.Node.Services, ","), n.ID.String(), 131 ), 132 ) 133 } 134 135 //DockerImage是为运行 136 //仿真节点 137 const dockerImage = "p2p-node" 138 139 //buildDockerImage构建用于运行模拟的Docker映像 140 //Docker容器中的节点。 141 // 142 //它将当前二进制文件添加为“p2p node”,以便运行execp2pnode 143 //执行时。 144 func buildDockerImage() error { 145 //创建用作生成上下文的目录 146 dir, err := ioutil.TempDir("", "p2p-docker") 147 if err != nil { 148 return err 149 } 150 defer os.RemoveAll(dir) 151 152 //将当前二进制文件复制到生成上下文中 153 bin, err := os.Open(reexec.Self()) 154 if err != nil { 155 return err 156 } 157 defer bin.Close() 158 dst, err := os.OpenFile(filepath.Join(dir, "self.bin"), os.O_WRONLY|os.O_CREATE, 0755) 159 if err != nil { 160 return err 161 } 162 defer dst.Close() 163 if _, err := io.Copy(dst, bin); err != nil { 164 return err 165 } 166 167 //创建dockerfile 168 dockerfile := []byte(` 169 FROM ubuntu:16.04 170 RUN mkdir /data 171 ADD self.bin /bin/p2p-node 172 `) 173 if err := ioutil.WriteFile(filepath.Join(dir, "Dockerfile"), dockerfile, 0644); err != nil { 174 return err 175 } 176 177 //运行“docker build” 178 cmd := exec.Command("docker", "build", "-t", dockerImage, dir) 179 cmd.Stdout = os.Stdout 180 cmd.Stderr = os.Stderr 181 if err := cmd.Run(); err != nil { 182 return fmt.Errorf("error building docker image: %s", err) 183 } 184 185 return nil 186 } 187