開発環境と仮想マシンとの間でのGitを用いたソース共有
ソース共有手段の必要性
先日の記事では、空っぽの環境からスタートして、プログラムを動作させることを実現しました。
ただしそのプログラムの内容は、echo
コマンドで書き込んだものだったり特定のコマンドで自動生成された雛形だったりです。
一方で現実の開発では、複数の人がそれぞれのPCのエディタやIDEを用いて編集したソースコードを、ある時点で、デプロイ可能なものにして(ビルドして)、動作させる必要があります。
そこで今回はバージョン管理システムの代名詞、gitを使って、各々の編集した内容を仮想マシンへと共有する仕組みを作っていきます。
なお便宜上「開発環境」という言葉を使っていますが、これは、ここではホストOS側のことです。
サンプルプロジェクトの作成
今回はBitbucketというサービスを例にとります。
ひとり〜少人数でバージョン管理を行うには十分な機能が、無料で利用できます。
ユーザー登録を済ませたら、ホームの左側にあるメニューから、リポジトリの作成を進めます。
今回は、仮にsample-sharing-repo
という名称にします。
リポジトリの作成が済むと、ゼロからスタートするときのためのコマンドを参照できるので、とりあえず開発環境からREADMEをコミットし、プッシュします。
$ cd your_work_space $ git clone https://{your_account}@bitbucket.org/{your_account}/sample-sharing-repo.git $ cd sample-sharing-repo $ echo "# My sample-sharing-repo README" >> README.md $ git add README.md $ git commit -m "Initial commit" $ git push -u origin master
bitbucketの当該リポジトリのページをリロードするとREADMEの反映が確認できます。
ここから、仮想マシンへの共有を進めていきましょう。
仮想マシンからgit clone
では仮想マシンを立ち上げます。
vagrant up
でubuntu/trusty64を起動し、vagrant ssh
で接続します。
開発環境と同様に、git clone
するためにはキーペアが必要です。
まずは仮想環境下でキーペアを作成しましょう。
$ ssh-keygen
次に、生成された公開鍵(~/.ssh/id_rsa.pub)の内容をコピーしてbitbucketへ登録します。
bitbucketのsample-sharing-repo
から、画面左下の「設定」 > Access keysをクリックすると「鍵を追加」することができます。
Label
は何でも構いません。
Key
という入力欄へ公開鍵の内容をペーストして保存しましょう。
また、cloneする際にHost Key Verificationをスキップしたいので、下記のような設定ファイルを作成しておきます。
$ touch ~/.ssh/config $ echo "Host bitbucket.org" >> ~/.ssh/config $ echo " User git" >> ~/.ssh/config $ echo " Port 22" >> ~/.ssh/config $ echo " IdentityFile ~/.ssh/id_rsa" >> ~/.ssh/config $ echo " StrictHostKeyChecking no" >> ~/.ssh/config $ cat ~/.ssh/config Host bitbucket.org User git Port 22 IdentityFile ~/.ssh/id_rsa StrictHostKeyChecking no
では仮想マシン側でgit clone
してみます。
$ sudo apt update $ sudo apt install -y git $ git clone git@bitbucket.org:{your_account}/sample-sharing-repo.git $ ls sample-sharing-repo $ cat sample-sharing-repo/README.md # My sample-sharing-repo README
gitリポジトリからソースを引っ張ってくることができるようになりました。
原理的にはこのような形で、クラウド上(プライベート、パブリックとも)からソースを参照してビルドすることができます。
Mavenについて 〜 解決したかった問題とMavenができること
前回の記事では、Mavenのインストールから簡単なプログラムの実行までをやってみました。
しかしながら、触れたばかりだと「Mavenって結局なんなのか」が分かりづらかったりします。
なので、今回はMaven自体について調べつつ、その役割をまとめてみたいと思います。
Mavenが解決したかった問題 〜 simplify the build process
Maven – Welcome to Apache Maven
Apache Maven is a software project management and comprehension tool.
前回も引用しましたが、公式のドキュメントではこの一文が最初に記されています。
「Apache Mavenは、ソフトウェアプロジェクトを管理し理解可能なものにするためのツールです」という感じですが、これだけではよくわかりません。
最近、自分が技術を調べるときによく見るのが、「その技術はどういう問題を解決するために出てきたものなの?」という点です。
今回もここに沿ってMavenを理解していきたいと思います。
https://maven.apache.org/what-is-maven.html
ここのIntroduction
に、登場の背景などが載っています。
曰く、build processをシンプルにしたい、という意図で始まったプロジェクトのようです。
ざっくり訳すと、
Antで管理していたけれど、ファイルの中身(つまりビルドの方法や内容)はばらばらで、必要なjarはCVSにチェックつけるような形になっていた
→ ビルドプロセスを標準化したい、プロジェクトが何で構成されているかわかりやすく定義したい、プロジェクト情報を簡単に公開したりプロジェクトの種類に依らないjarの共有方法を提供したりしたい、、、
→ Mavenという一連のプロジェクトが始まった
という感じです。
もう少し噛み砕いてみます。
「Maven以前」の問題を考えてみましょう。
Mavenのようなツールが登場するまでは、jarを取得する方法やテストの実行方法、コンパイルして成果物を動作可能なまでに整えていくステップが統一化されていなかったという問題があったと見れます。
スクリプトは秘伝のタレ化するし、新しいjarへの依存も持たせにくかったし、そもそもjarをどこからどれだけどんな順序で取得すればいいのかも、その方法は統一化されていなかったのでしょう。
特定のサイトからインストールするにしてもバージョンが異なり得るし、jarを誰かから手渡ししてもらうような必要もあったはずです。
また開発者が別のプロダクトの開発へ移行したら、それまでのビルドに関するノウハウや手順がほぼ通用しなくなる、という事態が起きていたであろうことも想像に難くありません。
同時に、(外からjarを取得してビルドするのに苦労があるということのほかに)自分たちの成果物やjarを外に提供するときの必要な情報についても、呼び出して使う側がこの有様では定義のしようがありません。
これさえあれば誰のPCからでも一発でビルドできる、しかもその方法はプロジェクト横断的に通用する。
そんなものがあればなぁ、というところからMaven爆誕です。
pomファイルを配ってmvnコマンドを叩けば、あとはよしなにやってくれる、と。しかもこの方法はプロダクトが変わっても一緒やで、と。
pomの書き方の統一や依存解決のためのjarを置く方法の統一によって、これらを実現していきます。
また拡張性が高いため、「自社では、自分のチームではこういうルールを適用したい、こういうツールやアーキテクチャに適合的なものにしたい」みたいな要望も(新規配属者であろうとmvnコマンドひとつで簡単に利用できる形で)追記できます。
Mavenのゴール 〜 comprehend the complete state of a development effort
ドキュメントに戻りましょう。
Maven’s Objectives
の項です。
Maven’s primary goal is to allow a developer to comprehend the complete state of a development effort in the shortest period of time.
がんばって訳すと「Mavenの第一のゴールは、開発者に対して、最短期間で「開発努力の完全な状態を理解すること」を促すということである。」というような意味になります。
では「開発努力の完全な状態を把握すること」とはなんでしょうか。
ここも自分の解釈ですが、「いまこのプロジェクトがどう構成されているか」というこれまでの積み重ねた成果物の内容を理解できることであったり、すぐさまその成果物の挙動を確認できることであったりを指していると考えています。
そして、このゴールを達成するためのサブミッションとして、次のようなものを挙げています。
- ビルドプロセスを簡素にする〜おおまかな内容をpomに書き下すのみでOK,あとはコマンド一発
- 統一化されたビルドシステムを提供する〜設定内容や共有方法のフォーマットはプロジェクト横断的に統一されます
- 質の高いプロジェクト情報を提供する〜名称、構成物、メーリングリストetc
- ベストプラクティスを用いた開発のためのガイドラインを提供する〜「テストソースは分離するけど並行的な階層で用意する」みたいなtipsの利用を可能にする
- 新しい機能への平易なマイグレーションを可能にする〜インストールしてきたものの更新も簡単!
Mavenができること
上述してきたように、もともとはビルドプロセスの簡素化から始まり、そこからさらに広い「開発」をサポートする仕組みを提供するものになってきたことがわかります。
前回の記事で、「mavenは(単なるビルドツールよりも)もっと大きな役割を包含したもの」と記したのはこの意味です。
まあ、それこそ「ビルド」をどう捉えるかで変わってくるんでしょうが。。。
「できること」はめっちゃ広範です。
プロジェクト生成、インストール、コンパイル、テスト、パッケージング、デプロイ(成果物のアップロード)、依存性の確認etc...
それぞれに役割やコマンドがあるものの、これらを統一フォーマットのファイルやリポジトリにもとづいて実行できるようにした、という点が、Mavenという企画のキモでしょう。
なお、Mavenができること、どのようにプロジェクトのライフサイクルをカバーするかについては、下記記事の図を見ると直感的に理解できます。
2. Maven 入門 | TECHSCORE(テックスコア)
キーワードとして出てきているPOMは、Project Object Modelの略称です。
「開発プロジェクト」をモデル化する一個の案です。
利用者はこの雛形に沿ってプロジェクトの情報を書くことができ、Mavenはその雛形を利用することができます。
ここから先もMavenを使っていく予定ですが、基本がわからなくなったら立ち返り、適宜更新していこうと思います。
空っぽのLinuxからJavaを動作させる & Mavenを動作させる
空っぽのOSから一歩を踏み出す
前回までのところで、ローカルで空っぽのUbuntuを用意することができました。
おそらく、評価環境や商用環境など成果物のデプロイ先は、AWSなどで空っぽのOSを立てることからのスタートです。
Java自体の復習をしたいという狙いもありますが、同時に、成果物を動作させていくのに必要な内容を掴んでいきたいと思います。
これ以降のコマンドは、基本、立ち上げたUbuntuで打っていくことを想定しています。
Javaを動作させる
Javaとは何ぞや的な詳細は、ほかのところに譲ります。
Sun Microsystemsがリリースしたオブジェクト指向のプログラミング言語で、 JVMと呼ばれるJava仮想マシン上で動作します。
仮想マシンをかませるところには、 "write once, run anywhere"の思想が埋まっています。
ということで、早速セットアップを進めます。
$ sudo apt update $ sudo apt install -y default-jdk $ export JAVA_HOME=/usr/lib/jvm/default-java $ export PATH=$PATH:$JAVA_HOME/bin
今回は実際にJavaのソースを仮想マシン上で用意してコンパイル、実行するところまでやりたいので、JDK(Java Development Kit)をインストールします。
実行環境だけ必要であれば、JRE(Java Runtime Environment)だけあれば十分です。
JREには先に述べたJVMが含まれます。
# 実行させたいだけならJREのみでOK $ sudo apt install -y default-jre
なお、JDKにはJREも内包されているので、重ねてインストールコマンドを叩く必要はありません。
ここまでで、java -version
, javac -version
といったコマンドでバージョン確認ができるようになっています。
$ java -version java version "1.7.0_151" OpenJDK Runtime Environment (IcedTea 2.6.11) (7u151-2.6.11-2ubuntu0.14.04.1) OpenJDK 64-Bit Server VM (build 24.151-b01, mixed mode) $ javac -version javac 1.7.0_151
ではHello World してしまいましょう。
$ echo 'public class Hello{ public static void main(String[] args){ System.out.println("Hello World"); } } ' > Hello.java $ javac Hello.java $ java Hello Hello World
Javaファイルを作成して、コンパイルして、実行する、という流れですね。
引数をつけたければ次のように行いましょう。
$ echo 'public class WithArg{ public static void main(String[] args){ System.out.println("See it, " + args[0]); } }' > WithArg.java $ javac WithArg.java $ java WithArg this-is-argument See it, this-is-argument
Mavenを使う
ビルドツールを利用するのが普通だと思っているので、mavenを利用したプロジェクト作成から実行もやってみます。
(厳密には、mavenはもっと大きな役割を包含したものですが、便宜上そう呼称します)
https://maven.apache.org/index.html
Apache Maven is a software project management and comprehension tool.
# インストール $ sudo apt install -y maven $ mvn -version Apache Maven 3.0.5 Maven home: /usr/share/maven Java version: 1.7.0_151, vendor: Oracle Corporation Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "3.13.0-141-generic", arch: "amd64", family: "unix" # サンプルプロジェクトの作成 $ mvn archetype:generate \ > -DarchetypeArtifactId=maven-archetype-quickstart \ > -DinteractiveMode=false \ > -DgroupId=com.sample \ > -DartifactId=mvn-example # 雛形の作成を確認 $ cd mvn-example/ $ ls -R .: pom.xml src ./src: main test ./src/main: java ./src/main/java: com ./src/main/java/com: sample ./src/main/java/com/sample: App.java ./src/test: java ./src/test/java: com ./src/test/java/com: sample ./src/test/java/com/sample: AppTest.java # コンパイル $ mvn compile # targetディレクトリ以下が作成されていることを確認 $ ls target/ -R target/: classes target/classes: com target/classes/com: sample target/classes/com/sample: App.class # mavenで実行 $ mvn exec:java -Dexec.mainClass=com.sample.App ... Hello World! [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 20.487s [INFO] Finished at: Thu Feb 08 13:58:23 UTC 2018 [INFO] Final Memory: 8M/21M [INFO] ------------------------------------------------------------------------ # もちろんjavaコマンドも利用可能 $ java -cp target/classes/ com.sample.App Hello World!
雛形の作成から実行まで、スムーズに進められるようになりました。
おまけ的に、mvnコマンドに引数を設定する方法もメモしておきます。
# 引数を使うプログラムへ、App.javaを書き換え $ echo 'package com.sample; public class App{ public static void main( String[] args ){ System.out.println("See it, " + args[0]); } }' > src/main/java/com/sample/App.java # 再度コンパイル、引数をつけて実行 $ mvn compile $ mvn exec:java -Dexec.mainClass=com.sample.App -Dexec.args=mvn-sample-arg ... See it, mvn-sample-arg [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.088s [INFO] Finished at: Thu Feb 08 14:06:16 UTC 2018 [INFO] Final Memory: 7M/17M [INFO] ------------------------------------------------------------------------
20180210追記;
Mavenについての説明記事を投稿しました。
空っぽの環境をつくるところから学習を始める(docker)
virtual boxが立ち上がらなくなった
前回はvagrantを使って実験用の環境をローカルに確保しようと試みました。
しかしながら問題が発生。
仕事だとOSにUbuntuを使っているんですが、あるときからvirtual boxで仮想マシンを立ち上げようとすると、PCがフリーズするという事象に見舞われるようになりました。
このままだと「空っぽの環境から云々」の趣旨を達成できなくなってしまうので、回避策的にDockerでUbuntuを立ち上げてみます。
まあ、ローカルマシンで確認のためにやってることなのでここで生じる差異については許されたい。。。
仮想化とかコンテナとかの話は、また別途ちゃんと記事に書きたいと思っています。
本日はひとまずセットアップ。
20180216追記;
このフリーズ事象は解決できました。
docker-ce のインストール
ホストOSへDockerをインストールします。
ここは公式ドキュメントがしっかりしていて、もうコマンドをコピペするだけでできてしまいます。
日本語のドキュメントもあるようですね。
http://docs.docker.jp/engine/installation/linux/docker-ce/ubuntu.html
# 必要な諸々のパッケージをインストール $ sudo apt-get update $ sudo apt-get install -y \ > apt-transport-https \ > ca-certificates \ > curl \ > software-properties-common # GPG鍵の取得と設定 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # リポジトリの設定 $ sudo add-apt-repository \ > "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ > $(lsb_release -cs) \ > stable" # dockerのインストール $ sudo apt-get update $ sudo apt-get install -y docker-ce # サンプルイメージの起動 $ sudo docker run hello-world ... Hello from Docker! ...
dockerでubuntuを立ち上げる
ではdockerでubuntuを立ち上げます。
$ sudo docker run -td --name ubuntu-on-docker ubuntu
イメージの取得もrunのタイミングで勝手にやってくれます。
-d
は、バックグラウンドでdockerのプロセスを実行させることを命じるオプションです。
走らせたdockerには、それぞれフォアグラウンドで実行しているプロセスがあります。
(そのプロセス自体を走らせないと、コンテナが走りません)
そのプロセスを、ホストから見てバックグラウンドで実行させるためのコマンド、という感じでしょうか。
-t
は擬似端末の割り当てです。これを入れてあげないとUbuntuのコンテナは走ってくれません。
--name
はコンテナ名の指定ですね。
いちいち生成されるidを使って操作するのはイケてないので、コンテナ名を付与して操作していくようにしましょう。
さて、やりたいことは「空っぽの環境の中でいろいろやる」ということです。
ここまでで、 vagrant up
したのと似たような状況になったのですが、vagrant ssh
に相当する操作が必要です。
そこで、次のコマンドを打って、コンテナの中に「入って」みます。
$ sudo docker exec -it ubuntu-on-docker /bin/bash
docker exec
はコンテナに特定のコマンドを実行させます。
このケースだと、bashを実行することで、vagrant ssh
的な操作を実現します。
-t
は先述のものと同様。
-i
はコンテナ内の入力をホストの入力と結びつける役割を果たすもの(らしい)です。
なので、こうするとdockerのコンテナの中でbashコマンドが実行できます。
$ sudo docker exec -it ubuntu-on-docker /bin/bash # docker コンテナ内へ操作が移る root@5c930a81d61c:/# root@d36e655ec845:/# ls bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr root@d36e655ec845:/# cd ~ root@d36e655ec845:~# echo "hello world" hello world
これでほぼやりたい準備は整ったんですが、ここからあとの操作をスムーズにやるために、sudo
コマンドくらいは入れておきましょう。
root@d36e655ec845:~# apt-get update root@d36e655ec845:~# apt-get install -y sudo root@d36e655ec845:~# rm -rf /var/lib/apt/lists/*
これで、vagrantで立ち上げたときとの微妙な差異は、インストール等行って適宜解消していけます。
参考:
https://github.com/tianon/docker-brew-ubuntu-core/issues/48
docker のコンテナから抜けるときはexit
を叩けばOKです。
root@d36e655ec845:~# exit exit # ホストOSに操作が戻る $
dockerコンテナに関して、本当にとりあえず知っておけばいいコマンドは下記くらいなものでしょうか。
docker run
: コンテナの起動
docker stop
: コンテナの停止
docker rm
: コンテナの破棄。-f
オプションの併用で起動中のコンテナでも破棄できる。
docker ps
: コンテナの一覧表示。-a
オプションの併用で停止中のコンテナも表示できる。
docker images
: 手元にあるdockerイメージの一覧表示。
docker search
: 入力したtermに応じてリポジトリ内のイメージを検索、表示。
ということで、次は本当に空っぽのUbuntu内でJavaを実行してみます。
なおプライベートで利用しているPCはMacbook airで、こちらは問題なくvagrantを利用した仮想マシンの起動が利用できています。
なのでこの先、ところどころ、docker上のubuntuなのかVM上のubuntuなのかで書き方読み方が変わる箇所が出てくるかと思うんですが、そこはなんか備忘録なんで許してください。
空っぽの環境をつくるところから学習を始める(vagrant)
ブログ化した発端
自分のキャッチアップ内容を、ネット上で閲覧できる情報としてアーカイブしたかったのです。
どこからでも見れて、かつ自分としては積み上がっていくのが実感できれば楽しそうだなーと。
基本的には備忘録を公開していくスタンスです。
初回でvagrantを扱う理由
webアプリつくる仕事をさせていただいてて、基本、大きいプロジェクトへの追加機能開発やバグ修正をメインにやってました。
が、最近ちょっとずつ仕事の幅がインフラやデリバリにも広がってきたのです。
で、安全な環境で実験する方法を覚えたり、空っぽのEC2からアプリの起動まで持っていくステップを理解したりしたく、まず空っぽの環境をつくるところから始めます。
ローカル(開発環境、あるいは用意された環境)ならできたのに、的なのに陥らないようにしたいんですね。
ubuntuを仮想マシンで立ち上げる
ってことで、さくっと仮想環境を立ち上げます。
ホスト側でのインストール箇所以外はどのOSでも変わらんはず。
ボックスは公式から探します。
app.vagrantup.com
本ブログでは apt
を使ってインストールしています。
Macユーザー、Windowsユーザーの方はそれぞれ下記の記事などを参考にしてインストールしましょう。
Vagrant+VirtualBoxのインストール(Mac) - Qiita
Windows10のVagrantで仮想環境 | あきらめずにwindowsで開発する。
では始めましょう。
$ sudo apt install virtualbox $ sudo apt install vagrant # 適当なディレクトリをローカルに作成 $ mkdir -p ~/SandBox/vagrant-practice/for-ubuntu $ cd ~/SandBox/vagrant-practice/for-ubuntu/ # 起動 $ vagrant init ubuntu/trusty64 $ vagrant up
こんだけです。簡単ですね。
vagrant up
したときに、ローカルにボックスが見つからなければ、ボックスを取得するところからやってくれます。
(20180226追記;0226以降は基本的にubuntu/xenial64(16.04)
ベースでの動作確認に変更しています)
時間がかかりますが、起動が完了するとサーバーを操作可能になります。
# ゲストOSへssh接続 $ vagrant ssh Welcome to Ubuntu 14.04.5 LTS (GNU/Linux 3.13.0-141-generic x86_64) ... vagrant@vagrant-ubuntu-trusty-64:~$ # 簡単な操作を試してexit vagrant@vagrant-ubuntu-trusty-64:~$ echo "Hello World" Hello World vagrant@vagrant-ubuntu-trusty-64:~$ exit logout Connection to 127.0.0.1 closed. # ホストOSの操作に戻る $
とりあえずこれ覚えておけばいいっしょなコマンドは下記あたりでしょうか。
vagrant status
: ステータス確認
vagrant up
: ゼロからの起動、halt後の再起動
vagrant halt
: 停止
vagrant destroy
: 削除
vagrant ssh
: 接続
次は、こうして立ち上がった空っぽのサーバーで、ひとまずJavaのプログラムを走らせてみます。
20180216追記;
Ubuntu上でvagrant up
したときにフリーズしてしまう事象に一時見舞われましたが、解消しました。