Building Docker Images In Memory
While developing flagops - a distributed async job scheduler - I came across the need to implement a way to build Docker images from a given git repository in a simple and stateles way.
Cloning The Repository
First, we clone the repository into memory using go-git/go-git
:
func CloneRepository(url string) billy.Filesystem {
store := memory.NewStorage()
fs := memfs.New()
repo, _ := git.Clone(store, fs, &git.CloneOptions{
URL: url,
})
return fs
}
This will return an in-memory filesystem containing all of our files.
Building The Tar
We convert this in-memory filesystem into a Tar archive:
func Tar(filesystem billy.Filesystem) io.Reader {
var buffer bytes.Buffer
gzWriter := gzip.NewWriter(&buffer)
defer gzWriter.Close()
tarWriter := tar.NewWriter(gzWriter)
defer tarWriter.Close()
util.Walk(fs, ".", func(path string, info fs.FileInfo, _ error) error {
header, _ := tar.FileInfoHeader(info, info.Name())
header.Name = strings.TrimPrefix(strings.Replace(path, ".", "", -1), string(filepath.Separator))
tarWriter.WriteHeader(header)
if info.IsDir() {
return nil
}
f, _ := filesystem.Open(path)
io.Copy(tarWriter, f)
f.Close()
return nil
})
return &buffer
}
Buliding The Docker Image
Now we build the image through the docker/docker/client
package as follows:
func BuildImage(name string, tar io.Reader) {
client.ImageBuild(ctx, tar, types.ImageBuildOptions{
Tags: []string{name},
Dockerfile: "Dockerfile",
})
}
Putting It All Together
var client = client.NewClientWithOpts(client.FromEnv)
func main() {
fs := CloneRepository("github.com/deadbeef/deadbeef")
reader := Tar(fs)
BuildImage("deadbeef", reader)
}
$ docker image ls deadbeef
REPOSITORY TAG IMAGE ID CREATED SIZE
deadbeef 92d575be 49a17de64f5f 2 minutes ago 17MB
Pushing The Image
In addition, if we wished to publish the built image we could do through the following code:
func PushImage(name string) {
encoded, _ := json.Marshal(types.AuthConfig{
Username: "username",
Password: "password",
})
auth := base64.URLEncoding.EncodeToString(encoded)
client.ImagePush(ctx, name, types.ImagePushOptions{
RegistryAuth: auth,
})
}