以golang语言chaincode为例子,当前golang chaincode是编译成一个叫chaincode的静态可执行文件,然后peer基于fabric-baseos为chaincode创建一个image,并把可执行文件chaincode打入新的image;然后在运行的时候使用root身份调用chaincode --peer.address=...
有些时候我们可能会有一些新的需求:
- 在调用chaincode可执行程序之前做些其他的额外工作,例如设置环境等等。
- 让chaincode运行在普通用户空间,而不是root用户。
- 另外fabric-baseos包含的内容太少,很多常用的linux工具都没有打进去。
下面几个地方可以修改来达到上述目的:
- core/chaincode/platforms/platforms.go
在build chaincode image的时候创建一个app用户,下面例子使用peer的当前运行用户,作为chaincode的运行用户。
func (r *Registry) GenerateDockerfile(ccType, name, version string) (string, error) {
...
buf = append(buf, fmt.Sprintf("ENV CORE_CHAINCODE_BUILDLEVEL=%s", metadata.Version))
+ // Set chaincode execution user
+ u, err := user.Current()
+ if err != nil {
+ return "", fmt.Errorf("Failed to get current user: %s", err)
+ }
+ logger.Debugf("Current username=%s, uid=%s", u.Username, u.Uid)
+ g, err := user.LookupGroupId(u.Gid)
+ if err != nil {
+ return "", fmt.Errorf("Failed to get current group: %s", err)
+ }
+ logger.Debugf("Current groupname=%s, gid=%s", g.Name, g.Gid)
+ buf = append(buf, fmt.Sprintf("RUN groupadd -g %s %s && useradd -u %s -g %s -s /bin/bash %s", g.Gid, g.Name, u.Uid, g.Name, u.Username))
// ----------------------------------------------------------------------------------------------------
// Finalize it
// ----------------------------------------------------------------------------------------------------
...
}
- core/chaincode/platforms/util/utils.go
创建一个entrypoint.sh入口脚本,代替原来的chaincode,这样我们可以在entrypoint.sh里面做很多工作,然后在脚本的最后launch chaincode就可以了。
func DockerBuild(opts DockerBuildOptions) error {
...
if err != nil {
return fmt.Errorf("Error uploading input to container: %s", err)
}
+ entrypointBody := `#!/usr/bin/env sh
+
+ echo "Entry of chaincode"
+ #
+ # Do whatever expected
+ #
+ exec su -p ccuser -c "$@"
+ `
+ // put entrypoint.sh into chaincode image, just like chaincode binary
+ var buf bytes.Buffer
+ tw := tar.NewWriter(&buf)
+ hdr := &tar.Header{
+ Name: "entrypoint.sh",
+ Mode: 0755,
+ Size: int64(len(entrypointBody)),
+ }
+ err = tw.WriteHeader(hdr)
+ if err != nil {
+ return fmt.Errorf("Error writing entrypoint header: %s", err)
+ }
+ _, err = tw.Write([]byte(entrypointBody))
+ if err != nil {
+ return fmt.Errorf("Error writing entrypoint body: %s", err)
+ }
+ err = tw.Close()
+ if err != nil {
+ return fmt.Errorf("Error closing entrypoint: %s", err)
+ }
+
+ type St struct {
+ *bytes.Buffer
+ }
+ in := St{bytes.NewBufferString(string(buf.Bytes()))}
+ err = client.UploadToContainer(container.ID, docker.UploadToContainerOptions{
+ Path: "/chaincode/output",
+ InputStream: in,
+ })
+ if err != nil {
+ return fmt.Errorf("Error uploading input to container: %s", err)
+ }
//-----------------------------------------------------------------------------------
// Attach stdout buffer to capture possible compilation errors
//-----------------------------------------------------------------------------------
}
- core/chaincode/container_runtime.go
修改chaincode image的入口代码:
func (c *ContainerRuntime) LaunchConfig(cname string, ccType string) (*LaunchConfig, error) {
...
switch ccType {
case pb.ChaincodeSpec_GOLANG.String(), pb.ChaincodeSpec_CAR.String():
- lc.Args = []string{"chaincode", fmt.Sprintf("-peer.address=%s", c.PeerAddress)}
+ lc.Args = []string{"entrypoint.sh", fmt.Sprintf("chaincode -peer.address=%s", c.PeerAddress)} // it's OK
...
}
- peer的core.yaml文件
###############################################################################
#
# VM section
#
###############################################################################
vm:
docker:
hostConfig:
CapAdd:
+ - NET_ADMIN
+ - NET_RAW
###############################################################################
#
# Chaincode section
#
###############################################################################
chaincode:
# Generic builder environment, suitable for most chaincode types
builder: $(DOCKER_NS)/fabric-ccenv:latest
golang:
- runtime: $(BASE_DOCKER_NS)/fabric-baseos:$(ARCH)-$(BASE_VERSION)
+ runtime: $(BASE_DOCKER_NS)/fabric-baseimage:$(ARCH)-$(BASE_VERSION)
这里我们把fabric-baseos改成了fabric-baseimage,因为fabric-baseos包含的内容太少了,想在entrypoint.sh做一点点事也做不了。当然你可以创建自己baseimage把需要的工具打进去,然后在这里指定就行了。
另外增加了CAP的NET_ADMIN和NET_RAW是为了管理container网络,例如可以要通过iptables限制chaincode网络访问等。
有了这个步骤,我们就可以对chaincode container进行控制管理。
有疑问加站长微信联系(非本文作者)