まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

2015年12月

2015/Dec/31、大晦日を迎えました。 今年は自分の人生の中でも大きな節目の年だったと思います。大晦日に今年を振り返って、年内最後のブログにします:
2015123101


1 転職
2015123102


いい歳して今年の2月に転職をしました。企業買収による転籍を含めると人生4回目の転職、ということは人生5社目・・・ のはずですが、以前に勤めていた日本アイ・ビー・エムに縁があって戻ることになりました。以前とは業務も立場も異なる形でしたが、新しい職場の仲間達や、以前からの知り合いに暖かく迎えていただきました。いいオッサンにエキサイティングな仕事環境を与えていただき、本当にありがたいことです。

この新しい職場では Bluemix エバンジェリストとして、IBM の戦略分野の1つであるクラウド製品を色々な人に知っていただき、興味をもっていただき、使っていただく、というための活動を行いました。その一環で日本国内の色々な所に出張する機会もいただけました。この製品自体も面白く、勉強しながら刺激を受け続けています。 同僚にも恵まれ、新しい出会いも多くあり、この1年は忙しかったけど、すごく刺激的な職場でした。

ご存知の人もいると思いますが、この IBM は英語のコミュニケーション能力が普通に求められます。入社前の面接で「まあ大丈夫だと思います」と気軽に答えてしまったのですが、入社後にその客観的証拠( = TOIEC スコア)の提出が求められる世界です(苦笑)。こわいこわい。ひっさしぶりに(単語だけですが)英語の勉強もしました。

ともあれ、ここ数年の中でも指折りに忙しい1年でした。特に年の後半は週末も仕事で忙殺されることが多くなり、趣味であるスポーツの活動にも少し影響が出てしまいました。少し余裕ができたら運動の優先順位を戻すつもりです。



2 クラウドコミュニティ活動
2015123103


これは上記の転職前からの話ですが、仕事とは離れた立場でクラウド上でウェブサービスを作って運用しています。なので Bluemix とも直接は関係ない話です。

前職時代に仕事で縁があり IDC フロンティア様のクラウドを使っていました。で、これがまた使いやすくて、かつ個人利用者に優しい価格体系でした。その上、自分の作るウェブサービスの1つはネットワーク負荷にボトルネックがあるので、ネットワークのコストパフォーマンスを求めるようなものだったのですが、ここのクラウドはそこが非常に高かったので、業務ではない個人利用でも使わせていただくことにしました。

その縁もあって、今年は同社のクラウドコミュニティにも何度か参加させていただく機会を頂きました(そこでどさくさに紛れて Bluemix の宣伝をしたりしてましたw)。とても刺激的かつ生産的な活動だと感じています。

これは自分が以前から事あるごとに公言していることなのですが、「エンジニアはより社外に目を向けるべき」だと思っています。会社の従業員である以上、その会社の方針や関連製品は自分の意志とは違う方向に向くことになります。その方針に従って製品やサービスのスキルを磨くのは従業員としての役割であるとも思います。

ただそれとは別の話として「1人のエンジニアとしてのスキルは自分で責任を持って磨くべき」です。社内だけでなく、社外でも通用するエンジニアになるべきです。仕事の観点でも、お客様は社外の人である以上、社内の話だけに詳しくなって対等に話ができないようではだめでしょう。その思いがより強くなった1年でした。

自分の場合、IBM から離れて働く機会があったので、そういう視点も身につけることができました。この考え方は今の職場でも広げていこうと思っています。


3 マンホールマップ
2015123104


自分が個人で開発しているマンホールマップは6年目の運用に入りました。2015年は実は「今日のマンホール」機能の充実がけっこう大変でした。

同機能はその日に関係のあるなんらかのイベントや出来事、に由来するマンホール(ややこしい・・)を毎日 PC 版マンホールマップに表示する、という機能なのですが、今年はこの機能の充実に勤めました。もともとこの機能は存在していましたが、去年までは「本日のマンホール」が設定されていない日が大半でした。今年はなるべくなんらかのマンホールを設定しようと、地味にずっと色んな日の出来事を調べていました(笑)。 

その成果として、現在はほとんど全ての日に設定できました。とはいえ、まだ空きの日もあります。その日を由来とするイベントがあっても、関連するマンホールが(マンホールマップ上に)なければ設定できないのがなんとももどかしいです。残りの日については2016年の宿題にします。


ずっと長い間、「ノート PC といえば ThinkPad」派でした。最初は Dynabook(というか Libretto)にハマり、次に トラックボール付き Let's note しか愛せなくなり、そしてトラックポイント付きの ThinkPad に出会ってからは ThinkPad 依存な体になりました。 改めてこうやって並べてみると、基本ノートPCではマウスを使わずにポインタを自在に操ることを求めているのかもしれません。

で、今は ThinkPad 派です。トラックポイントに加えて、あの打ちやすい剛健キーボード! 会社の方針だか何だか知りませんが ○acBook とか使いにくくて仕方ありません。だいたいあの会社はオープンテクノロジーの真逆にあるんじゃないかってほど閉鎖的でしかも・・・ まあこのくらいにしておきます(苦笑)。 要は ThinkPad Love なのです。

が、そんな自分でも心配になるようなニュースが今年の冬と秋にありました:
Lenovo、今度はThinkPadにユーザーデータ収集ソフトを潜ませていた

簡単に言うと「Lenovo が自社 PC 内に不正ソフトを潜ませていてユーザーのデータを集めてフィードバックする仕組みを組み込んでいた」というものです。冬の時点では「ThinkPad は関係ない」という内容でしたが、秋のニュースでは ThinkPad を含む全製品が対象、というものでした。

正直な所、このニュースの信憑性や、本当の所どうだったのかはよく分かっていません。でもその仕組があったことは事実だし、すごく残念でした。事実、このニュース以降では新たに ThinkPad を購入していませんし、正直少し抵抗を感じています。


ただ、とはいえ ThinkPad が使いやすいことは変わらないのです。自分はプログラミングコードを書く機会が多く、比較的キーボード入力の多い使い方をしていると思うのですが、そんな使い方に最適のキーボードがトラックポイント付きの ThinkPad キーボードであり、最適のマシンが ThinkPad だと今でも思っています。会社でも別会社の新しい PC を使う機会がなかったわけではないのですが、正直使ってて疲れる気がしたので変えていません。でもそういう人は少なくないんじゃないか、とも思っています。




さて、昔購入した PC は時間の流れと共に古くなって、使い勝手が悪くなります。その運命は ThinkPad であっても変わりません。そのような「事実上使えなくなった」中古 PC をどうするか、というのは多くの人が抱える問題だと思います。「メモリやディスクを増設すればまだ使える」かもしれませんが、そもそも周辺部品が今でも入手できる保証はありませんし、できるとしてもお金をかけてまで延命させたいわけじゃないことが多いです。安く買い取ってくれる(奇特な)知り合いがいればいいのですが、実際はそうもいかず、逆にお金を払ってリサイクル、ということだって珍しくないと思ってます。現実問題として快適に使えないんだから仕方ないよね、、と。

さて、ここからが本題です。 そんな中古 PC の使い道の1つとして「Linux で生まれ変わらせる」ことが可能です。Windows という比較的負荷の高いデスクトップ OS を使っているから遅く感じたり、メモリが足りなくなってイライラしたり・・・ということがあると思うのですが、そこに比較的軽量なデスクトップ環境を構築して使えば、まだそのスペックでもイケるんじゃないか!? という発想です。 しかもそのマシンが ThinkPad だったりしたら、超軽量で超使いやすいデスクトップ PC に生まれ変わるんじゃないか!? という期待があります。

しかも上述の ThinkPad の不正ソフトの話は、実は Windows 環境で使っている場合の話でした。Windows でなければ(少なくとも上記で触れられている)不正ソフトの心配をする必要もなくなります。不安だった ThinkPad が安心して使えるようにもなる、というメリットまであるのでした。

前置きが長くなりましたが、それを実際にやってみた様子を報告します。まず今回の対象となった PC は 2010 年モデルの Lenovo ThinkPad Edge 13(グロッシーレッド)。色はブラックが多い ThinkPad では珍しく天板が光沢のあるレッドで加工されています。2GB のメモリと 250 GB のハードディスク、そして元々は 64bit Windows 7 Home Edition が導入されているモデルです(Windows 8 が既にリリースされていて、でも 7 も選べる、というタイミングで購入したもの)。実際問題として、今の Windows デスクトップはメモリ 2GB だと起動の時点で大抵使いきってしまうので、起動して使えるようになるまででも結構時間がかかることが多いのではないかと思ってます:
写真 1


既に Lenovo のサイトからは見つけることができなくなっているモデルだったため、同じ製品を紹介している外部サイトを参照してください:
http://www.itmedia.co.jp/pcuser/articles/1002/23/news025.html

こいつにハードウェア的な増設は一切行わずに、OS だけ入れ替えて生まれ変わらせよう、という考えです。入れ替える OS は Linux です。デスクトップ用途では Ubuntu が多数派かもしれませんが、自分の好きな CentOS にしてみました。

念のため、以下で紹介している導入作業を実際に行うと、現在使っているデータは全て消えてしまいます。バックアップを取るなどして、もうこの PC の中にあるデータが使えなくなってもよい状態にしてから実施してください。

手順はおおまかにはこちらで紹介していることをやっているだけです。要は CentOS (6) の 64bit ISO イメージをダウンロードして、DVD に焼いて、PC にその DVD をセットして起動して OS をインストールする、というものです:
CentOS の導入

写真 3


上記リンク先に書かれている手順を実行して CentOS を導入し、X Window System とデスクトップ環境をオプションで導入することで比較的 Windows に近いグラフィック環境で利用できるようになります。なおトラックポイントと有線 LAN は何もしなくても自動認識してくれました。日本語および入力IME、LibreOffice(オフィス製品)、FireFox ブラウザあたりを入れておけばとりあえずはネットブラウザとオフィスの使える、デスクトップ PC っぽい環境になりました:
写真 4


しかも超軽いです。GNOME のウィンドウ環境まで立ち上がった状態でもまだ 512MB 程度しかメモリを使っていないので余裕です。CentOS のデスクトップ環境構築についてはこちらでも紹介したことがあるので参考にしてください:



先日、マンホールマップのログイン機能に障害が発生しました:

通常、マンホールマップ(PC版)のトップページにアクセスすると、画面右上には "Login with Twitter" マークが表示されます(未ログインの場合):
2015122801


で、ここをクリックして、Twitter の OAuth 認可によってマンホールマップにログインして・・・という流れでログインするのですが、ここに問題が発生していると「現在、何らかの障害によってログインできません。」というメッセージが表示されます:
2015122802


一般的には Twitter API の障害が発生していることを示すメッセージなのですが、先日マンホールマップで発生していたものは Twitter API には障害が発生していないにも関わらず、上記のようなメッセージが表示されていたのでした。 ちなみに Twitter API の稼動状態はこちらで確認できます:
API Status | Twitter Developers


さて、上記の原因はなんだったのでしょうか? ちなみにマンホールマップは Java で開発された Web アプリケーションで、この Twitter の OAuth 認可の部分は Twitter4J を使って実装しています(最新版の 4.0.4 でも、スナップショット版の 4.0.5 でも再現)。で、問題部分のコードは以下のようになっていました:
String authorizationURL = null;
try{
  Twitter twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer( TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET );
  RequestToken requestToken = twitter.getOAuthRequestToken();  ←ここで Exception 発生

  String token = requestToken.getToken();
  String tokenSecret = requestToken.getTokenSecret();
    :
    :
}catch( Exception e ){
  e.printStackTrace();
}

リクエストトークンを取得するための Twitter.getOAuthRequestToken() メソッド実行時に Exception が発生していました。それまでに設定しているのは Twitter API の Consumer Key と Consumer Secret だけで、これは正しい値が設定できていました(というか、正しく動いていた時から何も変えてません)。 で、その下の catch 内で以下のようなスタックトレースが出力されていました:
Invalid signature on ECDH server key exchange message
Relevant discussions can be found on the Internet at:
        http://www.google.co.jp/search?q=3cc69290 or
        http://www.google.co.jp/search?q=45a986b4
TwitterException{exceptionCode=[3cc69290-45a986b4 3cc69290-45a9868a], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=4.0.5-SNAPSHOT(build: 9b7efa6faa540a9defb3e6ba9122a356155986b1)}
        at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:179)
        at twitter4j.HttpClientBase.request(HttpClientBase.java:57)
        at twitter4j.HttpClientBase.post(HttpClientBase.java:86)
        at twitter4j.auth.OAuthAuthorization.getOAuthRequestToken(OAuthAuthorization.java:115)
        at twitter4j.auth.OAuthAuthorization.getOAuthRequestToken(OAuthAuthorization.java:92)
        at twitter4j.TwitterBaseImpl.getOAuthRequestToken(TwitterBaseImpl.java:292)
        at twitter4j.TwitterBaseImpl.getOAuthRequestToken(TwitterBaseImpl.java:287)
        at me.juge.geoimgweb.AppInfo.GetAuthorizationURLTwitter(AppInfo.java:361)
        at me.juge.geoimgweb.AppInfo.GetAuthorizationURL(AppInfo.java:350)
        at org.apache.jsp.stats_jsp._jspService(stats_jsp.java:928)
        at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:377)
        at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
        at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:122)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
        at java.lang.Thread.run(Thread.java:745)
Caused by: javax.net.ssl.SSLKeyException: Invalid signature on ECDH server key exchange message
        at sun.security.ssl.HandshakeMessage$ECDH_ServerKeyExchange.(HandshakeMessage.java:1098)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:278)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:913)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:849)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1035)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1344)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1355)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1093)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
        at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:137)
        ... 30 more


問題の getRequestOAuthToken() を実行した時に "Invalid signature on ECDH server key exchange message" というエラーメッセージが出力されています。何コレ?"Invalid signature" って言われても、この時点ではこちらは Key と Secret 以外何も指定してないので間違えているとは思えないけど・・・??

しつこいようですが、今までは何の問題もなく動いていたコードが、ある時を境に急にこのような挙動になってしまいました。となると、Java ソースコードに直接の原因があるとは考えにくい・・・


しかもこの問題、何が厄介かというと、デバッグしようとしてローカルの Eclipse 内で動かすと、このエラーも Exception も発生しないのです。ちゃんと動いてしまいます。更に言うと、別のサーバー内に同様の Java + Tomcat 環境を作って、同じ war をデプロイして実行しても発生しない。要はこの本番サーバーだけで発生するエラーだったのです。。 となると環境依存か、原因特定は更に厄介だぞ・・・

とりあえず緊急でサーバーを1つ作り、そちらに Java と Tomcat を導入し、アプリの war をデプロイ、したら動きました。なので、応急処置としてはこれでなんとかなります。この応急処置サーバーで運用を続けながら原因追求と解決を続けます。 ああ、クラウドってこういう時に便利なのね。。。


さて、このエラーメッセージをググってみると分かるのですが、SSL 関連の Exception のようなのです。はて、マンホールマップに SSL 関連モジュールなんか使ってたっけ?? 何よりも仮に使っていたとしても、実行コードは Twitter4J の中だし、指定しているのは Key と Secret だけで変えようがないし、他のサーバー環境だと動いちゃうし・・・ 念のため API Key と Secret を新たに取得し直して見ましたが、状況は変わりませんでした。


次に行ったのは JDK と Tomcat のアンインストール、および再インストールです。環境は CentOS だったので、 JDK のアンインストールは以下の手順で行いました:
# yum list installed | grep jdk  (インストール済み JDK を確認)
java-1.7.0-openjdk.x86_64
java-1.7.0-openjdk-devel.x86_64
jdk.x86_64              2000:1.7.0_79-fcs (候補は3つ、全部削除してもよいと判断)

# yum remove java-1.7.0* (上2つをアンインストール)
# yum remove jdk* (一番下をアンインストール)

同様にして、Tomcat も以下の手順でアンインストールしました:
# yum list installed | grep tomcat (インストール済み tomcat を確認)
tomcat6.x86_64          6.0.24-90.el6   @base
tomcat6-admin-webapps.x86_64
tomcat6-el-2.1-api.x86_64
tomcat6-jsp-2.1-api.x86_64
tomcat6-lib.x86_64      6.0.24-90.el6   @base
tomcat6-servlet-2.5-api.x86_64 (候補は6つ、全部削除してよいと判断)

# yum remove tomcat6* (まとめてアンインストール)

でもってそれぞれを再インストールします:
# yum install java-1.7.0-openjdk-devel
# yum install tomcat6 tomcat6-admin-webapps

これで Java と Java アプリケーションサーバーはまっさらな状態に戻りました。この状態でマンホールマップの JAR をインストールしてアクセスしたところ・・・ 何も変わらない・・・ orz


全く原因が思いつきません。いよいよヤバい。 改めてこの ECDH なるものを調べてみると暗号化のアルゴリズムっぽいようでした:
ECDH アルゴリズムの概要

暗号化アルゴリズムの、鍵の交換の所で(しかも今まで動いていたものが、ある時から)動かなくなった、ということになります。いよいよ OS レベルの中身がぶっ壊れてしまったか?? と思ったのですが、最後の希望を託してインストールした全ライブラリを更新してみることにしました:
# yum update -y
  :
  :
  :

# /etc/init.d/tomcat6 restart

で、Tomcat をリスタートしてアクセスしてみたら・・・ 直りました! 助かった!!
2015122901
 ↑直ったのはついさっき


後は緊急運用サーバーからこちらに切り替えれば対処完了(予定)。 結局「これ」という直接の原因は分からなかったけど、古いライブラリでは対応しない機能を使っていた、ということなんでしょうかね~。

自分が作って運用しているウェブサービスの多くはクラウド上で動いてますが、このマンホールマップに関しては自宅サーバーで(更に言えば ThinkPad で)運用することにまだこだわっています。今回のエラーはその信念を揺るがすものでしたが、とりあえず解決できました。もうしばらく自宅サーバー運用を続けることができそうです。 v(^o^)



今まで Tomcat を使う場合は使い慣れた(理由はそれだけじゃないけど) Tomcat6 を使っていました。試しに Tomcat7 を導入する機会があったので、その導入手順を紹介します。環境はいつもの CentOS 6 です。


まずは Java/JDK 環境が必要です。今回は OpenJava JDK 1.7 を導入することにしました:
# yum install java-1.7.0-openjdk-devel

さて Tomcat7 です。実は CentOS 6 の標準リポジトリには Tomcat7 は含まれていません。標準で含まれているのは Tomcat6 ですが、yum でインストールする場合は
# yum install tomcat6

のように、わざわざ「6」を指定して導入する必要があります。この辺りの導入手順の詳細はこちらを参照してください:
CentOS に Apache Tomcat を導入する

さて Tomcat7 です。Tomcat6 は「6」を指定する必要がありました。Tomcat7 も「7」を指定する必要があるかというと、これがありません(笑)。コマンドとしては
# yum install tomcat

で導入されるのですが、普通にそのまま実行しても「見つからない」というエラーになるだけだと思います。Tomcat7 を yum でインストールするには事前に EPEL リポジトリの登録が必要です。つまり、まずはこのコマンドを実行します:
# rpm -Uvh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

その後に yum で install です。tomcat-admin-webapps パッケージを指定して、管理コンソール画面ごとインストールします:
# yum install tomcat tomcat-admin-webapps

管理コンソール画面にアクセスするための設定を追加します。/etc/tomcat/tomcat-users.xml をテキストエディタで編集し、以下の3行を追加します:
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="admin" password="P@ssw0rd" roles="admin-gui,manager-gui"/>
   ↑この例だとユーザー名: admin、パスワード: P@ssw0rd でアクセスするユーザーを作成


インストール後は "tomcat" というサービス名で起動したり、再起動したり、自動実行指定ができます:
# /etc/init.d/tomcat start
# chkconfig tomcat on

管理コンソール画面ごとインストールした場合は、http://(IPアドレス:8080)/manager/html で管理画面にアクセスできます。Basic 認証でユーザー名とパスワード(上記設定の場合であれば admin と P@ssw0rd)を指定してログインできます:
2015122801


CentOS6 で Tomcat7 を使う場合は EPEL リポジトリの登録、が肝です。


個人/仲間で作ったアプリのソースコードを管理したり、サンプルアプリ的なものを作ってそのコードを公開したりしようとすると、GitHub は非常に便利です。私も色々な形で使っています。

ただ、GitHub でソースコードを管理すると基本は誰からもソースコードが見れるように公開する形になります。GitHub でプライベート管理をすることは可能ですが、その場合は有償アカウントに切り替える必要があります。業務で使うケースでは(少なくとも開発中は)ソースコードを公開したくない、というケースも珍しくないと思うので、ソースコードをどうやってプライベート管理するか、という問題が生じます。自分も最近ありました。

自分の場合は自分で SubversionGitLab 環境を構築して使っています(両方使っています)。この中の GitLab は GitHub 互換のリポジトリ管理や問題トラッキングの環境を(Community Edition であれば)無料のオープンソースで構築することができるようになります。GitHub 互換なので、git コマンドを使ってソースコードをコミットしてプッシュして・・・という GitHub と同じ管理ができます。git コマンドに慣れた人には非常に便利です。 というわけで、この GitLab 環境を CentOS 上に構築する手順をまとめてみました。

【あらかじめ用意しておくもの】

SSH 鍵のペア。作成方法はこちらを参照:



【導入&設定手順】

GitLab の導入自体は用意されたインストールパッケージを実行するだけなので非常に簡単です。まずは Community Edition のダウンロードページへ行き、自分の環境にあった最新版 GitLab Community Edition のパッケージを選んで導入することになります:
https://packages.gitlab.com/gitlab/gitlab-ce
2015122401
 ↑ラズベリーパイ向けのもパッケージもあるのか・・・


ちなみに、2015/Dec/24 時点では 8.3.0 が最新バージョンでした。このバージョンではオートマージ機能が追加されているようです:
https://about.gitlab.com/2015/12/22/gitlab-8-3-released/


例えば自分の場合であれば 64bit 版の CentOS 6 を使っているので、"el6" と書かれた最新版の rpm パッケージの箇所をクリックすると、以下の様な導入のインストラクションが表示されます。ここに書かれている通りに CentOS 環境からコマンドを実行してインストールします:
2015122402


モジュールインストール後に、環境設定が必要です。具体的には GitLab に自分自身の URL を認識させる必要があります。root 権限で /etc/gitlab/gitlab.rb ファイルを編集し、external_url に自分自身の IP アドレス(以下 'XXX.XXX.XXX.XXX')を設定して保存します:
# vi /etc/gitlab/gitlab.rb
: : external_url 'http://XXX.XXX.XXX.XXX' # 'http://gitlab.example.com' から自分の IP アドレスに書き換え : :

これだけで動かすこともできます。が、この状態で使うと色々不都合が生じることもあります。以下はそのためのオプショナルな設定を加える場合についていくつか紹介します。

まずはこのまま使うとユーザー追加時にメール認証(新規に追加したユーザーにメールが送られて、そのメール内のリンクをクリックしたら本登録)が必要になります。そのためにはメールサーバーとその設定/管理を・・・ ということになりますが、プライベートリポジトリ目的だけで使うのであれば、メール認証(というかメールサーバー管理)が面倒なので、その手順をスキップさせることもできます(その場合は /opt/gitlab/embedded/service/gitlab-rails/app/models/user.rb ファイルをカスタマイズして、ユーザーの登録と同時にメール認証が済んでいるかのように振る舞わせることになります)。テキストエディタで同ファイルを開いて、"class User" で始まる行以降に default_value_for で始まる行が数行続いているはずですが、その最後に以下の一行を追加してください:
# vi /opt/gitlab/embedded/service/gitlab-rails/app/models/user.rb
: : class User < ActiveRecord::Base : : default_value_for :admin, false : : default_value_for :confirmed_at, Time.now # この一行を追加 : : : :

また、CentOS の環境によっては GitLab が使う git ユーザー(セットアップ時に作成されているはずです)に ssh アクセスが許可されていないこともあります。その解除のためには以下の2ファイルを編集する必要があります。

1つ目は /etc/shadow を編集して、パスワードを設定していないユーザーの SSH アクセスを許可してあげてください:
# vi /etc/shadow
: : git:!!:16793::::::  (こっちから)   ↓ git:*:16793::::::   (こっちに変更) : :

もう1つは /etc/ssh/sshd_config を編集して、SSH を許可するユーザーに git を追加した上で sshd をリロードします:
# vi /etc/ssh/sshd_config
: : AllowUsers xxxx yyyy git  (AllowUsers に git を追加) : :

# service sshd reload

以上の内容は環境依存の箇所もありますが、必要に応じて設定してください。

この状態で GitLab を reconfigure します。reconfigure にはしばらく時間かかりますが、"Runnning handers complete" と表示されれば無事成功、これで設定調整は完了です:
# gitlab-ctl reconfigure
  :
  :
  :
  :
Running handlers:
Running handlers complete
Chef Client finished, 211/281 resources updated in 01 minutes 30 seconds
gitlab Reconfigured!

ここからは作成した GitLab 環境に実際にログインしての作業になります。まずはウェブブラウザで GitLab サーバー(http://XXX.XXX.XXX.XXX/)にアクセスしてみます:
2015122403


初期ページが表示されれば一応動いていることになります。まずは管理ユーザーを有効にするため、ユーザーID(root) と初期パスワード(5iveL!fe)を指定してログインします。パスワードはすぐに変更するのでここでは記憶させる必要はありません:
2015122404


ログインに成功すると、とりあえず初期パスワードを変える必要があります。初期パスワード(5iveL!fe)と、新しい管理者用パスワード(確認のため2回)を指定して、新しいパスワードを登録してください:
2015122405


直後に再度ログイン画面に戻るので、ユーザー ID(root) と新しいパスワードを指定してログインしてください:
2015122406


ログイン後にこんな画面が表示されれば、とりあえず初期設定は完了です:
2015122407



【ユーザー追加手順】

自分1人でソースコード管理をするのであれば、このまま管理者権限で作業を続けてもいいのですが、多くのケースでユーザー管理をすることが多いと思います。そこで作業用のユーザーを登録して、以降はその作業ユーザーによるプロジェクト管理を行っていく内容を紹介します。

ユーザーを新規に登録するには、root で GitLab にログインした状態から、ダッシュボード右上の Admin Area をクリックします:
2015122401


続いて NEW USER ボタンをクリックします:
2015122402


新規に登録するユーザーの名前、ユーザー名(ログインID)、そしてメールアドレスを入力して CREATE USER ボタンでユーザーを作成します(この時点ではパスワードは登録しません):
2015122403


これで一般ユーザーの作成が行われました:
2015122404



メールサーバーが正しく設定されていて、メール認証が有効になっている場合は、指定したメールアドレスにメールが送られ、その本文内のリンクからパスワード設定を含めた本登録を行うことになります。

上記で紹介した手順でメール認証を無効にした場合は、このユーザーの初期パスワードを手動で登録してあげる必要があります。上記画面右上の EDIT ボタンをクリックして、初期パスワードを登録してあげてください:
2015122400


では、改めてこのユーザーでログインしてみます。root で GitLab にログインしている場合はダッシュボード右上の Sign out からログアウトしてください:
2015122406


改めて GitLab サーバーにアクセスして、今度は作成した一般ユーザーの ID とパスワードでログインします:
2015122407


一般ユーザーでログインできることを確認してください:
2015122408


このまま、このユーザー向けの SSH 鍵を登録しましょう。ダッシュボード左メニューから Profile Settings を選択します:
2015122401


続いて SSH Keys をクリックします:
2015122402


登録済み SSH 鍵の一覧が表示される画面ですが、この時点ではまだ何も登録されていないので表示されません。ではあらかじめ用意した SSH 鍵を登録してみます。ADD SSH KEY ボタンをクリックします:
2015122403


テキストエリアが表示されるので、この中に用意した公開鍵の内容をペーストします。また Title 欄に名前を付けて、最後に ADD KEY ボタンをクリックします:
2015122404


このユーザーが使える SSH 鍵が登録できました:
2015122405


同様にして、必要なユーザーと、そのユーザーの SSH 鍵を必要なだけ登録しておきます。


【プロジェクト追加手順】

では一般ユーザーでプロジェクトを追加してみます。ソースコードの管理をするにもまずはそのプロジェクトを作成する必要があります。

GitLab ダッシュボードに一般ユーザーでログインした後に、Project(初期状態でここが選ばれているはずです)の NEW PROJECT ボタンをクリックします:
2015122401


なお、一度プロジェクトを作成した後の二度目以降は画面右上の New Project をクリックしてください:
2015122408


プロジェクトの名前(下図では myhelloworld)や Description を入力後、Visivility Level に Private を指定すると(パブリックではない)プライベートなリポジトリを作ることができます。最後に CREATE PROJECT ボタンをクリックしてプロジェクトを作成します:
2015122402


GitLab 上にプロジェクト myhelloworld が新規に作成できました:
2015122403


画面を下にスクロールすると、この後のコマンド操作方法についての簡単なガイドが紹介されています。必要に応じて参照してください:
2015122404


このプロジェクトに他のユーザーをメンバー登録すると、そのユーザーからもこの Git リポジトリが使えるようになります。プロジェクト画面左の Members を選ぶと、GitLab ユーザーをプロジェクトメンバーに追加する画面に切り替わります:
2015122405


People にカーソルを持って行くと、GitLab 上のユーザー一覧が表示されます。ここから選んで指定することができます:
2015122406


プロジェクトに追加したい人と、その権限を指定し、ADD USERS TO PROJECT ボタンをクリックすると、そのユーザーは指定した権限でプロジェクトメンバーとして追加されます:
2015122407


この図では Administrator ユーザーが Master 権限でプロジェクトに追加されたことが分かります:
2015122408


これを必要なだけ繰り返して、プロジェクトにメンバーを追加します。



【プロジェクトの Git リポジトリを git クライアントから操作する手順】

ではこの myhelloworld プロジェクトの Git リポジトリを実際に使ってみましょう。まずは適当なファイル1つをコミット&プッシュしてみます。

まずは作業マシン上に git クライアントを導入しておきます。作業マシンが CentOS や RedHat であれば以下のコマンドで導入できます:
# yum install git

また、登録した SSH 鍵の秘密鍵ファイル(ファイル名 guestkey とします)を利用ユーザーの ~/.ssh/ 以下に用意しておきます。そして ~/.ssh/config ファイルを以下の内容で作成します:
Host mygitlab-server
        HostName XXX.XXX.XXX.XXX  (GitLab サーバーのIPアドレス)
        IdentityFile ~/.ssh/guestkey  (秘密鍵ファイル)
        User dotnsf  (GitLab サーバーのログインユーザー名)

そして鍵ファイルと config ファイルのパーミッションを 600 に設定します:
$ chmod 600 ~/.ssh/guestkey
$ chmod 600 ~/.ssh/config

では SSH の準備が整ったので、実際に git コマンドを使ってみます。まずは Global 設定で user.name と user.email を設定します:
$ git config --global user.name "K.Kimura"
$ git config --global user.email "xxxx@xxxxx.com"

まだ最初のコミット前なので、ゼロからリポジトリの中身を作る必要があります。というわけで専用のディレクトリを1つ作り、その中にプロジェクトのファイルを追加します。ここではとりあえずホームディレクトリ以下に myhelloworld というサブディレクトリを作り、その中に README.md ファイルを1つ追加しました(ソースコードなどを加える場合も myhelloworld 以下に追加していくことになります):
$ cd 
$ mkdir myhelloworld
$ cd myhelloworld
$ echo '# MyHelloWorld' > README.md

この myhelloworld ディレクトリを git リポジトリとして GitLab サーバーの myhelloworld プロジェクトにコミット&プッシュします(以下は myhelloworld ディレクトリで行います):
$ git init
$ git remote add origin git@mygitlab-server:dotnsf/myhelloworld.git
$ git add .
$ git commit -m 'first commit'
$ git push -u origin master

最後の git push 実行時に「接続を続けますか?」というプロンプトが表示されたら 'yes' と入力してください。また途中でパスフレーズの入力をするよう求められた場合は登録した SSH 鍵ファイルを作成した時に指定したパスフレーズを入力してください。成功するとこのような画面になるはずです:
$ git push -u origin master
The authenticity of host 'XXX.XXX.XXX.XXX (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'XXX.XXX.XXX.XXX' (RSA) to the list of known hosts.
Enter passphrase for key '/home/linux1/.ssh/guestkey': (SSH 鍵のパスフレーズを入力)
Counting objects: 3, done.
Writing objects: 100% (3/3), 222 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@mygitlab-server:dotnsf/myhelloworld.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.
$

この状態で GitLab サーバー上の myhelloworld プロジェクトを見ると、このコミット&プッシュが記録されて、以下のようになるはずです:
2015122401


画面左の Files をクリックすると、現在の Git リポジトリのファイル一覧(まだ README.md しかないので1つだけ)が確認できます。これでプライベート Git リポジトリが作成できました:
2015122402


同じユーザーでも、別ユーザーでもいいのですが、このリポジトリを操作する場合の手順も紹介しておきます。上記のグローバル設定を行うまでは共通です(同一ユーザーで行う場合は myhelloworld ディレクトリごと削除してから以下を行ってください)。その上で GitLab サーバーの myhelloworld リポジトリをクローンします。途中でパスフレーズの入力を求められた場合は同様に SSH 鍵ファイル作成時に指定したパスフレーズを入力してください:
$ cd (作業ディレクトリに移動 指定しない場合はホームディレクトリ)
$ git clone git@mygitlab-server:dotnsf/myhelloworld.git
Initialized empty Git repository in /home/linux1/src/myhelloworld/.git/
Enter passphrase for key '/home/linux1/.ssh/guestkey': (SSH 鍵のパスフレーズを入力)
remote: Counting objects: 3, done.
Receiving objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
$

この時点でカレントディレクトリに myhelloworld というディレクトリが作られ、その中に現在の myhelloworld リポジトリの内容がクローンされているはずです:
$ cd myhelloworld
$ ls -a
.   ..   .git   README.md


この中に以下の内容で index.php ファイルを追加します(名前は何でもいいし、どのような内容でも構いませんが、新規にファイルを作って追加してください):
$ echo '<?php echo "ハローワールド!\n"; ?>' > index.php

追加したファイルを GitLab のリポジトリにコミットしてプッシュします:
$ git add .
$ git commit -m 'index.php added.'
$ git push -u origin master
  (SSH 鍵のパスフレーズを入力してプッシュ)

ここまでの作業が成功すると、GitLab の myhelloworld プロジェクトのファイル一覧はこのようになり、index.php が追加された状態になります:
2015122403


「新規に作成してコミット&プッシュ」も、「クローンして作業してコミット&プッシュ」もできることがわかりました。



GitLab Community Edition を使えば、無料でプライベートな Git リポジトリを作って管理する、ということもできそうです。GitLab Community Edition にはラズベリーパイ用のモジュールも用意されているようなので、余った(?)ラズパイの使いみちとしても良さ気な感じです。


このページのトップヘ