mojavy.com

gitlab 6.6.4 CE のゆるふわセットアップ

March 11, 2014 at 02:08 AM | categories: chef, git, tips, gitlab |

gitlabもCentOSとUbuntuにはパッケージが提供されるようになったので、大分インストールが簡単になりました。

とはいえ、このパッケージはgitlab専用のマシンにインストールすることを前提にしているのか、小規模プロジェクトのために軽く使いたいよう場合ではつらいデフォルト設定となっています。安いvpsとかだと確実にメモリ不足でまともに動きません。

以下はとりあえずプライベートgitリポジトリが欲しいだけのような人のためのgitlabの設定の紹介です。

準備

https://www.gitlab.com/downloads/

ここからgitlabのパッケージをダウンロードします。 omnibus-ruby でつくられた全部入りパッケージなのでインストールでのコンフリクトは発生しないはずですが、既に稼動中のサービス(apache等)のことはあまり考慮されてないので使用するポートについては個別対応が必要です。

特に以下は注意が必要です。

  • nginx
  • redis
  • postgresql

ここでは、apacheが稼動しているubuntuにインストールします。

インストール

普通にインストールします。

$ sudo dpkg -i gitlab_6.6.4-omnibus-1.ubuntu.12.04_amd64.deb 
$ sudo gitlab-ctl reconfigure

ちなみにgitユーザが既に存在しているとこけるので消しておきます

$ sudo userdel -r git

gitlabの設定

gitlabの設定は /etc/gitlab/gitlab.rb に設定をかいて、chefで設定します。

$ cat /etc/gitlab/gitlab.rb
external_url "http://gitlab.example.com:8081"
unicorn["worker_processes"] = 1
postgresql["shared_buffers"] = "128MB"
postgresql["effective_cache_size"] = "32MB"

とりあえずpostgresqlがメモリを大量に食うので適当に減らします。

unicornもメモリ食いがちなので1プロセスにします。

external_urlにポートも含めたURLをかきます。apacheが80番で起動してるとnginxが起動できないので適当にはずします。ちなみに8080はデフォルトだとgitlabのunicornがつかっています。

このあたりは環境に応じて適当に設定してください。

gitlab-ctl reconfigure すると設定が反映されます。

その他の設定できる項目は /opt/gitlab/embedded/cookbooks/gitlab 以下のcookbookをみるといいです。

webサーバの設定

80番で起動しているapacheがいる場合はnginxにproxyします。

<VirtualHost *:80>
  ServerName gitlab.example.com

  DocumentRoot /opt/gitlab/embedded/service/gitlab-rails/public

  CustomLog  /var/log/apache2/gitlab_access.log combined
  ErrorLog   /var/log/apache2/gitlab_error.log

  ErrorDocument 502 /502.html

  <Directory "/opt/gitlab/embedded/service/gitlab-rails/public">
    Options FollowSymLinks
  </Directory>

  <Proxy *>
    AddDefaultCharset off
    Order deny,allow
    Allow from all
  </Proxy>

  ProxyVia On
  ProxyPreserveHost On

  ProxyRequests Off
  ProxyPass /assets/ !
  ProxyPass /uploads/      !

  ProxyPass / http://localhost:8081/ retry=1
  ProxyPassReverse / http://localhost:8081/

</VirtualHost>

以上ができたらapache再起動して、 http://gitlab.example.com にアクセスしてみてうまく表示できれば完了です。



自炊した書籍のpdfのコントラストを3clickくらいで上げる方法 on Mac OS X

March 07, 2014 at 12:09 AM | categories: osx, mac |

preview app

スキャンしたpdf書籍だと、自炊業者にやってもらったとしても、どうしても文字が薄くなってしまいます。 業者によっては補正オプションがついてたりもしますが、preview.appのexport機能を使えば簡単にコントラストをあげられます。

メニューバー > ファイル > 書き出す > Quartzフィルタ > Lightness Decrease

で適当なところに保存。

menu menu2

以下は実行前後のサンプル。これだけだとわかりにくいかもしれないけど、文字がぎっしりしたページだとコントラストが高い方が断然読みやすい。

before after

大きいファイルサイズのpdfだとそれなりに時間(数分くらい)はかかるので注意。

参考: http://osxdaily.com/2011/10/24/increase-the-contrast-of-a-pdf-to-sharpen-darken-text/



chefを使うのを我慢したほうがいいとき

March 07, 2014 at 12:09 AM | categories: chef |

chefを使いはじめるとあらゆるもののセットアップをchefレシピ書かずにやるのが気持ち悪くなってしまうけど、chefでやらないほうがいいものって結構あると思う。

  • redmine
  • gitlab
  • 小規模なシステムのzabbixのマスター
  • etc...

この手のものは、実際に使いはじめると多少は手作業での運用が必要になるので、誰かがつくったcookbookでいれてしまうよりかは手作業でいれてある程度どこになにがあるか把握しておいたほうがやりやすい。

自前でレシピ書いてもいいけど、当面は1台あれば十分なのであれば単なる二度手間でしかないのでセットアップ手順をメモに残す程度で十分。1

ある程度運用経験があってとりあえずすぐに動く環境を作りたい、という場合のみ出来合いのcookbookをそのまま使えばいいと思う。

などということを、1ヶ月くらいかけていろいろcookbook書いたあげく心が折れたときに感じた。


  1. まめにredmineみたいなものをアップデートしたいような人もたぶんあんまりいない 



malloc+memsetとcallocの違いについて

March 05, 2014 at 09:25 PM | categories: os, programming |

malloccallocの違いは、表面的には引数の数とcallocは確保した領域を0で初期化するという点くらいですが、以下のコードを大きなnで実行すると、今時のOSだとmalloc + memsetのほうが大幅に遅くなる可能性があります。

void *p = malloc(n * sizeof(type));
memset(p, 0, n * sizeof(type));
void *p = calloc(n, sizeof(type));

カーネルはセキュリティ上の理由からメモリを0で初期化してからユーザプロセスに渡します。

しかし、仮想メモリをサポートしたシステムでは、実際にそのメモリに書き込みが発生するまでカーネルはread onlyな領域を複数プロセスで共有させることができるため、既に初期化してあるページであればこの処理を省略できる場合があります。

brkで拡張した領域は0で初期化されているので、callocは新規確保した領域は初期化を省略することができ、結果的にcallocを実行したタイミングでは初期化が実際にはほとんど発生しない、ということがありえます。

一方memsetの場合は実際にメモリへの書込みが発生する上、ページの共有もできなくなるためswapする可能性もあります。



ちなみに、(カーネルではなく)calloc自身が0初期化する処理と、memsetの処理は微妙に違います。 なぜなら、memsetは対象の領域がアラインされているかどうかについての情報なしに処理する必要があるので、境界部分は1byteずつやるしかありません。

じゃあmemsetのほうが遅いのかというと、コンパイラによってはアラインされていることを推測できる場合もあったり、callocはライブラリ関数なので移植性のために最適化しにくかったりするので、結局のところ微妙です。

参考: http://stackoverflow.com/questions/2688466/why-mallocmemset-is-slower-than-calloc



スレッドプールの実装方法について

March 03, 2014 at 08:58 PM | categories: unix, programming |

スレッドプール(thread pool)を実装するには、暇なときはthreadを寝かせておいて必要なときに起こす、というイベント通知の仕組みが必要になる。 UnixでC/C++で実装するときはpthreadの条件変数を使うのが普通だと思われるが、適当なファイルディスクリプタをopenしておいてread等でブロックさせる方法でも実装できそう。

どのようなやり方が一般的なのか、いくつか有名どころのOSSの実装を調べてみた。

libuvの場合

https://github.com/joyent/libuv

単純にpthread_cond_waitをつかっている 1

static void worker(void* arg) {
  struct uv__work* w;
  QUEUE* q;

  (void) arg;

  for (;;) {
    uv_mutex_lock(&mutex);

    while (QUEUE_EMPTY(&wq))
      uv_cond_wait(&cond, &mutex);

    q = QUEUE_HEAD(&wq);

    if (q == &exit_message)
      uv_cond_signal(&cond);
    else {
      QUEUE_REMOVE(q);
      QUEUE_INIT(q);  /* Signal uv_cancel() that the work req is
                             executing. */
    }

    uv_mutex_unlock(&mutex);

    if (q == &exit_message)
      break;

    w = QUEUE_DATA(q, struct uv__work, wq);
    w->work(w);

    uv_mutex_lock(&w->loop->wq_mutex);
    w->work = NULL;  /* Signal uv_cancel() that the work req is done
                        executing. */
    QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
    uv_async_send(&w->loop->wq_async);
    uv_mutex_unlock(&w->loop->wq_mutex);
  }
}

Boost.Asioの場合

http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio.html

Boost.Asioにスレッドプールそのものは提供されてないが以下のようにして簡単に実装することができる

#include <thread>
#include <functional>
#include <boost/asio.hpp>

int main ( int argc, char* argv[] ) {
    asio::io_service io_service;
    asio::io_service::work work(io_service);

    std::vector<std::thread> threadPool;

    for(size_t t = 0; t < std::thread::hardware_concurrency(); t++){
        threadPool.push_back(thread(std::bind(&asio::io_service::run, &io_service)));
    }

    io_service.post(std::bind(an_expensive_calculation, 42));
    io_service.post(std::bind(a_long_running_task, 123));

    //Do some things with the main thread

    io_service.stop();
    for(std::thread& t : threadPool) {
        t.join();
    }
}

http://stackoverflow.com/questions/14265676/using-boostasio-thread-pool-for-general-purpose-tasks

長くなるのでコードは省略するが、io_service::postするとunixの場合は最終的にはtask_io_service::wake_one_idle_thread_and_unlockからpthread_cond_signalが呼ばれる。

memcachedの場合

https://github.com/memcached/memcached

libeventのイベント通知機能を利用して実装している。それぞれのthread初期化の際にpipeをつくって、そのfdをlibeventに渡す。 2 libevent内部でそのfdをepollなりkqueueなりでブロックして待つ。

//
// memcached.c
//
typedef struct {
    pthread_t thread_id;        /* unique ID of this thread */
    struct event_base *base;    /* libevent handle this thread uses */
    struct event notify_event;  /* listen event for notify pipe */
    int notify_receive_fd;      /* receiving end of notify pipe */
    int notify_send_fd;         /* sending end of notify pipe */
    struct thread_stats stats;  /* Stats generated by this thread */
    struct conn_queue *new_conn_queue; /* queue of new connections to handle */
    cache_t *suffix_cache;      /* suffix cache */
    uint8_t item_lock_type;     /* use fine-grained or global item lock */
} LIBEVENT_THREAD;

//
// thread.c
//
void thread_init(int nthreads, struct event_base *main_base) {
//
// 中略
//
    threads = calloc(nthreads, sizeof(LIBEVENT_THREAD));
//
// さらに中略
//
    for (i = 0; i < nthreads; i++) {
        int fds[2];
        if (pipe(fds)) {
            perror("Can't create notify pipe");
            exit(1);
        }

        threads[i].notify_receive_fd = fds[0];
        threads[i].notify_send_fd = fds[1];

        setup_thread(&threads[i]);
        /* Reserve three fds for the libevent base, and two for the pipe */
        stats.reserved_fds += 5;
    }

    /* Create threads after we've done all the libevent setup. */
    for (i = 0; i < nthreads; i++) {
        create_worker(worker_libevent, &threads[i]);
    }

    /* Wait for all the threads to set themselves up before returning. */
    pthread_mutex_lock(&init_lock);
    wait_for_thread_registration(nthreads);
    pthread_mutex_unlock(&init_lock);
}

pthread_cond_waitの実装

脱線するが、pthread_cond_waitがどのようにsleepにはいってるのか気になったので調べた。

https://sourceware.org/git/?p=glibc.git;a=tree;f=nptl;hb=HEAD

pthread_cond_waitのソースコードはglibcnptl以下にある。 __pthread_cond_waitlll_futex_waitを呼んでおり、これは以下のように実装されている。(以下はx86_64のもの)

#define lll_futex_wait(futex, val, private) \
  lll_futex_timed_wait(futex, val, NULL, private)

#define lll_futex_timed_wait(futex, val, timeout, private) \
  ({                                         \
    register const struct timespec *__to __asm ("r10") = timeout;          \
    int __status;                                \
    register __typeof (val) _val __asm ("edx") = (val);                \
    __asm __volatile ("syscall"                            \
             : "=a" (__status)                         \
             : "0" (SYS_futex), "D" (futex),                 \
           "S" (__lll_private_flag (FUTEX_WAIT, private)),         \
           "d" (_val), "r" (__to)                    \
             : "memory", "cc", "r11", "cx");                 \
    __status;                                    \
  })

上記アセンブラは大体以下のような意味3

futex(futex, FUTEX_WAIT, val, timeout, NULL, 0);  // 便宜上、上記コードの引数の変数名をそのままつかっているが、
                                                  // 1つめのfutexはシステムコールのfutexで、
                                                  // 2つめは引数のpthread_cond_tの__futexメンバ変数のアドレス

futex() システムコールは、 指定したアドレスの値が変更されるのをプログラムが待つ手段や 特定のアドレスに対して待機中のプロセスを wake (起床) させる手段を提供する

futex(2) http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/futex.2.html

とのこと。

まとめ

  • pthread_cond_waitをつかったもののほうが普通は高速なはず
  • memcachedのようなやりかただとユーザプロセス側でスレッドプール管理のための排他制御がほとんど不要になるので多少実装が簡単か

  1. pthread_cond_waitはunixの場合。windowsの場合はpSleepConditionVariableCS、これが使えない場合は疑似的に同様の動作をするようなラッパを定義している。 

  2. memcachedでは使用してないが、libeventはシグナルを通知する際もfdをつかう。Boost.Asioもシグナル通知はpipeを経由する。 

  3. 厳密には違う。 



About Me

pic
mojavy

Recent posts






Categories



Badges