GlusterFS + Go の開発環境を Docker で構築

バックアップ開発部の y-okubo と申します。

今回は「gogfapi を使った簡単な API Server」を作るにあたり、Docker を使って Go の開発環境を構築した手順を紹介させていただきます。

gogfapi を使った〜」は弊社が毎年行っている開発合宿のお題として選んだ物で(社内的にはもうちょっとクールなお題にしています)、そちらは別の機会に紹介したいと思います。

構成

開発環境なのでデータの永続化は考慮していません。

f:id:nekojarashi-Inc:20160523170935p:plain

サーバ

当初は DockerHub でイメージを探したのですが、イメージを生成すると GlusterFS のインストールで失敗する事が多いので自作することにしました。

Dockerfile はこんな感じです(途中の root:password はとても意識が低いので適宜書き換えてご利用ください)。

FROM centos

ENV container docker

# Install require packages
RUN yum --setopt=tsflags=nodocs -y update
RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs

# Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# Add epel repository
RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm
RUN rpm -ivh epel-release-7-6.noarch.rpm

# Install GlusterFS packages
RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
RUN yum -y update
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-server glusterfs-fuse glusterfs-geo-replication glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute
RUN yum clean all

RUN echo 'root:password' | chpasswd
VOLUME [ “/sys/fs/cgroup” ]

EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162

RUN systemctl disable nfs-server.service
RUN systemctl enable rpcbind.service
RUN systemctl enable sshd.service
RUN systemctl enable glusterd.service
CMD ["/usr/sbin/init"]

COPY ./startup.sh /var/startup.sh
RUN chmod +x /var/startup.sh
RUN echo /var/startup.sh >> /root/.bashrc

ポイントは

  • EPEL レポジトリを追加
  • startup.sh を実行

しているところでしょうか。

GlusterFS のインストールに失敗していたのが yum install あたりだったので、自分で EPEL レポジトリを追加しています。

startup.sh の内容についてはこの後に説明します。

その他の内容は GlusterFS official docker image を参考にしています。

startup.sh の内容は以下になります。

#!/bin/bash

# Create volume
mkdir /var/gfs_srv_vol
gluster peer probe $HOSTNAME
gluster volume create gfs_volume $HOSTNAME:/var/gfs_srv_vol force
gluster volume start gfs_volume
gluster volume quota gfs_volume enable

# Mount volume
mkdir /var/gfs_cli_vol
mount -t glusterfs -o acl $HOSTNAME:/gfs_volume /var/gfs_cli_vol

こちらでは GlusterFS のボリューム作成とローカルマウントを行っています。

Dockerfile の中の RUN で実行したかったのですが、$HOSTNAME 環境変数を使いたかったのでシェルスクリプトで実行するようにしてあります。

クライアント側

サーバ側とほぼ一緒の Dockerfile になります(こちらも途中の root:password はとても意識が低いので適宜書き換えてご利用ください)。

FROM centos

ENV container docker

# Install require packages
RUN yum --setopt=tsflags=nodocs -y update
RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs

# Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# Add epel repository
RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm
RUN rpm -ivh epel-release-7-6.noarch.rpm

# Install GlusterFS packages
RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
RUN yum -y update
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-fuse glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute
RUN yum clean all

RUN echo 'root:password' | chpasswd
VOLUME [ “/sys/fs/cgroup” ]

EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162

RUN systemctl disable nfs-server.service
RUN systemctl enable rpcbind.service
RUN systemctl enable sshd.service
CMD ["/usr/sbin/init"]

RUN yum --setopt=tsflags=nodocs -y groupinstall "Development Tools"
RUN wget https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.6.2.linux-amd64.tar.gz

COPY ./hello.c /root/hello.c

RUN mkdir -p /root/go/bin \
  && echo 'export GOROOT=/usr/local/go' >> /root/.bashrc \
  && echo 'export GOPATH=/go/path' >> /root/.bashrc \
  && echo 'export GOBIN=/go/bin' >> /root/.bashrc \
  && echo 'export PATH=$PATH:$GOROOT/bin:$GOBIN' >> /root/.bashrc

こちらは最後の方で Go 開発環境のインストールと設定を行っています。

Go のソースコードはローカルマシン側に置いて、コンテナ起動時にマウントして参照できるようにしています。

イメージ生成

サーバ側

$ docker build -t glusterfs-server server

クライアント側

$ docker build -t glusterfs-client client

コンテナ起動

サーバ側

$ docker run --privileged -tid -p 20022:22 --hostname gfserver --name gfserver glusterfs-server

--privileged で権限を与えて SELinux 等の影響を受けないようにしています。

クライアント側

$ docker run --privileged -tid -p 20023:22 -p 8080:8080 -v /Users/y-okubo:/go/path --link gfserver:server --hostname gfclient --name gfclient glusterfs-client

--link で先ほど起動したコンテナを参照できるようにしてあります。

コンテナへ SSH ログイン

サーバ側

$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20022 root@localhost

クライアント側

$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20023 root@localhost

セキュリティ上よろしくないのですが、毎回 OpenSSH の警告が出るのを抑止しています。

クライアント→サーバの疎通確認(libgfapi 経由でのアクセス)

クライアント側に SSH ログインして、イメージ作成時に用意(Dockerfile にて指定)した hello.c をビルド・実行します。

hello.c のソースは以下になります。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glusterfs/api/glfs.h>

int main (int argc, char** argv) {
    const char *gfserver = "server";
    const char *gfvol = "gfs_volume";

    int ret;
    glfs_t *fs;
    glfs_fd_t *fd;

    fs = glfs_new(gfvol);   // Virtual filesystem type struct
    glfs_set_volfile_server (fs, "tcp", gfserver, 0);
    ret = glfs_init (fs);
    if (ret) {
        printf( "Failed to connect server/volume: %s/%s\n", gfserver, gfvol );
        exit(ret);
    }

    char *greet = "Hello, Gluster!\n";
    fd = glfs_creat(fs, "greeting.txt", O_RDWR, 0644);
    glfs_write(fd, greet, strlen(greet), 0);
    glfs_close(fd);
    return 0;
}
$ gcc hello.c -lgfapi
$ ./a.out

まとめ

今回は GlusterFS の Go での開発環境を Docker で構築した際の流れを駆け足でご紹介しました。

今回はサーバ側でローカルマウントするようにしていますが、クライアント側でサーバ側のボリュームを FUSE マウントすれば別の用途でも使えるのではないかと考えています。

GlusterFS + Go という組み合わせにどれくらいの需要があるかは分かりませんが、何かのお役に立てれば幸いです。

お知らせ

ねこじゃらしでは Go に限らず RubyJavaScriptプログラマ、UI/UX デザイナを募集しております。

ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。

www.nekojarashi.com