2017年06月21日

Ubuntu サーバの設定 git サーバーのセットアップ

git サーバーを構築する。

ユーザー数は、~10数人程度の規模で運用するぐらいを想定する。
要は手動でアクセスユーザーを追加しても、かかる手間は知れている程度の規模と仮定する。
ユーザー側ではSSH接続が可能であるとしておく。



git については、ここで簡単に触れた。


インストール


クライアント用途で使う場合と同じ。
端末で実行する。

$ sudo apt-get install git


確認してみる。
$ git --version
git version 2.7.4







アクセスユーザーの作成


git サーバーへアクセス可能なユーザーを設定する。
SSH接続できるようにLinuxユーザーを作成する。

git サーバーへのアクセス専用ユーザーとして1つ準備する。
プロジェクトメンバー全員にアクセス権を与えるのが正しいのかもしれないが、ユーザー追加のたびに作業が増えるので、プロジェクトメンバー全員が使う共通ユーザーとして作ることにする。

ユーザー作成にあたり、目的はSSH接続のためだが、git 以外の操作は制限したい。
そこで、git に関する作業しかできない制限付きシェル "git-shell" が git に付属しているので、これをユーザーのログインシェルに指定する。

$ sudo useradd gituser -s /usr/bin/git-shell


パスワードも設定する。

$ sudo passwd gituser

新しい UNIX パスワードを入力してください:
新しい UNIX パスワードを再入力してください:
passwd: パスワードは正しく更新されました



"gituser" グループができるので、サーバー管理者も参加させておく。

$ sudo gpasswd -a <admin> gituser




git 用のディレクトリを作成する。
"gituser"のホームディレクトリを作成し、そこをgit用に使う。
所有者とパーミッションも合わせて、"gituser" に変更する.

$ sudo mkdir /home/gituser/
$ cd /home/
$ sudo chown gituser:gituser ./gituser/
$ sudo chmod 755 ./gituser/







ユーザー側でのSSH接続準備


SSHで接続する際に使われる設定ファイルを使ってアクセスを効率化しておく。
"~/.ssh/config" に設定する(configファイルが無ければ作成する)
Host "任意の接続名"
  HostName ホスト名 or IPアドレス
  User ユーザー名
  Port ポート番号
  IdentityFile "秘密鍵のPATH"


今は、ポート番号は22番のまま、公開鍵認証も使わないので、
例えば、このようにする。
Host gitserver
  HostName 192.168.1.120
  User gituser






リモートリポジトリの作成


次に、リモートリポジトリを準備する。

サーバー側での対応


サーバー側ではリモートリポジトリを入れるディレクトリを準備する。

各プロジェクトのリポジトリを格納する場所を、"~/git/" 以下とし、所有者とパーミッションを設定しておく。

$ cd /home/gituser/
$ sudo mkdir git/
$ sudo chown gituser:gituser ./git/
$ sudo chmod 770 ./git/



この中にプロジェクト単位でリポジトリを用意していく。
プロジェクト単位で " *.git " ディレクトリを作成し、その中にリモートリポジトリ(ベアリポジトリ)を作成する。

$ cd git/
$ mkdir ./project1.git/
$ cd ./project1.git/
$ git init --bare

Initialized empty Git repository in /home/gituser/git/project1.git/

ベアリポジトリとは、作業ツリーを持たずに更新情報のみ管理する場所。
実際の作業は、ユーザー側のローカルリポジトリで行い、サーバー側のリモートリポジトリでは各ローカルリポジトリからの更新情報を格納するのみの場所として提供する。


所有者を変更しておく。忘れずに、"-R" オプションをつけて実行。

$ cd /home/gituser/git/
$ sudo chown -R :gituser ./project1.git/


これで、サーバー管理者とgitユーザーがリモートリポジトリを触れる。

このような構造で、各プロジェクトごとでリモートリポジトリを管理していく。

└ gituser
└ git/
├ project1.git
│ ├ HEAD
│ ├ branches
│ ├ config
│ ├ description
│ ├ hooks
│ ├ info
│ ├ objects
│ └ refs

├ project2.git
├ project3.git
├ project4.git
├ project5.git
・・・・・







ユーザー側での対応


ユーザー側では、まずリモートリポジトリからローカルリポジトリへ複製する(クローン)。

作業用ディレクトリとして、"~/git/" 以下の階層を使うことにした。
クローンする場所を準備したら、その場所へ移動しておく。

$ mkdir ~/git/
$ cd ~/git/



リモートリポジトリの場所は、"~/git/project1.git"

$ git clone gitserver:~/git/project1.git

Cloning into 'project1'...
gituser@192.168.1.120's password:
warning: You appear to have cloned an empty repository.
Checking connectivity... done.

ローカル環境へ、"project1" ディレクトリが複製された。

└ git/
└ project1/
└ .git/
├ HEAD
├ branches/
├ config
├ description
├ hooks/
├ info/
├ objects/
└ refs/
このローカルリポジトリを使って、プロジェクトを進めることができる。


試しに、新たなファイル(index.html)を準備し、プロジェクトへ追加してみる。

$ git add index.html
$ git commit

$ git log
commit 83e4e2160daaa9051b7a67424fc69905f4f0e87f
Author: hoge hogehoge <hoge@email.com>
Date: Mon Jun 19 21:00:00 2017 +0900

    Add new file.



ローカルリポジトリを更新したので、リモートリポジトリへ反映してみる。

$ git push

warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

gituser@192.168.1.120's password:
Counting objects: 3, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 363 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To gitserver:~/project1/.git
* [new branch] master -> master


なにやら警告が出たが、リモートリポジトリへは反映できた。
警告については、また別の機会に触れる。




SSH公開鍵接続


SSH接続をパスワード認証のパターンで説明してきたが、実際の運用では接続ポート番号も変えるだろうし、公開鍵認証にすると思う。
なので、SSH接続を公開鍵認証にしておく。


ユーザー側で公開鍵、秘密鍵ペアの作成


各ユーザーは、"~/.ssh/" へ移動し、それぞれ作成する。

$ cd ~/.ssh/
$ ssh-keygen -t rsa

Generating public/private rsa key pair.
Enter file in which to save the key (/home/********/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/********/.ssh/id_rsa.
Your public key has been saved in /home/********/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:7Lf/RDXF/fIL28lOwH1sHSPd+3l/+Xsh3NtrIkjLhBs ********
The key's randomart image is:
+---[RSA 2048]----+
 ・・・・・・・・
+----[SHA256]-----+


作成した公開鍵(id_rsa.pub)は、SSHサーバー側にUSBメモリを使うなど何でも良いので渡す。
渡す際は、サーバー管理者が複数ユーザーから公開鍵を受け取った場合に混乱しないよう、ユーザー名をつけるなどリネーム(id_rsa_user1.pub)しておくとよい。



サーバー側で公開鍵を配置


SSHサーバーでの公開鍵の保存先は、"/home/gituser/.ssh"
存在していなければ作成し、所有者の変更と仮パーミッションに変更しておく。

$ cd /home/gituser/
$ mkdir /home/gituser/.ssh/
$ sudo chown gituser:gituser /home/gituser/.ssh/
$ sudo chmod 770 /home/gituser/.ssh/



".ssh" へ移動。"authorized_keys" ファイルを変更する。
既に "authorized_keys" が存在していれば、サーバー管理者ではパーミッションが無いので、
一旦、変更できるようにする。

$ cd /home/gituser/.ssh/
$ sudo chmod 660 authorized_keys



新規であれば、このタイミングで "authorized_keys" ファイルが作成。
統合後、SSH接続可能なように所有者とパーミッションを変更しておく。
公開鍵ファイルは不要なので削除する。

$ cat id_rsa_user1.pub >> authorized_keys
$ sudo chown gituser:gituser authorized_keys
$ sudo chmod 600 authorized_keys
$ rm id_rsa_user1.pub



".ssh/" ディレクトリのパーミッションをSSH接続可能な設定にしておく。

$ cd /home/gituser/
$ sudo chmod 700 .ssh/



"git" ユーザーは、"git" 以外の操作を制限したので、サーバーに通常にログインすることができず、"git"以外の操作が不便だ。
パーミッションが制限されたファイルを触るためには、パーミッションを変更してファイル操作して、またパーミッションを戻してと。ユーザー追加時に多少の手間がかかる。
プロジェクトメンバーで秘密鍵を共有するという手もある。サーバー管理者は楽になるが、あまり推奨はされないだろう。



ユーザー側で秘密鍵を配置


サーバー側で設定されたら、公開鍵は破棄しておく。
秘密鍵のパーミッションを変更する。

$ cd ~/.ssh/
$ rm id_rsa_user1.pub
$ sudo chmod 600 id_rsa



SSHで接続する際に使われる設定ファイルを変更。秘密鍵のPATHを追加。
例えば、このようにする。
Host gitserver
  HostName 192.168.1.120
  User gituser
  Port 22
  IdentityFile ~/.ssh/id_rsa





まとめ


ざっと、git サーバーを立てるときの流れを書いた。
git の使い方をかなり端折っているが、git サーバーを立ててしまえば、あとは実際の使う側での作業。
クライアントユーザーとしての git の使い方は別途まとめていきたい。


Ubuntu インストールしたらやること:gitをインストール
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git コミットを取り消す
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元


posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年06月17日

Ubuntu インストールしたらやること:gitをインストール

git をインストールする。

git について


プログラミングに限らずファイルを更新していくとき、過去ファイルとの変更点比較や、以前の状態に復活させたいときのために最新のファイルを維持しつつ、ファイルを更新していくことがよくある。
そのとき、よくやるのが、ファイル名+日付(yymmdd_file.txt)とか、ファイル名+番号(001_file.txt)とか。
ファイルをリネームして上書きしないようにバックアップしておいて、本ファイルを更新していく感じ。

当然、手間がかかるし、「昔の対応に戻したい」というときや、「この対応はいつ入れたのか思い出したい」と言うときに、更新したものが常に管理された状態であればまだましだが、管理されて無ければ調べるだけでもかなり労力が必要となるし、そのあとの対応での間違いも起こりやすい。さらに複数人で作業しているとなるとリスクが跳ね上がる。
こういう場合に解決をしてくれるのがバージョン管理システム。

git は分散型バージョン管理システム。

対して、SVN、subversion などは集中型バージョン管理システム。
リポジトリ(変更を管理する情報が入った倉庫、管理サーバー)が1箇所にあり、そこにユーザーそれぞれがアクセスし、情報の取得・更新を行う方式。


git のような分散型は、複数人で共有するリポジトリ(リモートリポジトリ)と、ユーザー1人1人に作業用リポジトリ(ローカルリポジトリ)があり、普段の作業はユーザーがそれぞれのローカルリポジトリ上で変更を管理する。
他へ公開しても良い状態になれば、リモートリポジトリに対してアップする。
逆にリモートリポジトリからは更新が必要となれば、各ユーザーのタイミングでローカルリポジトリへ反映することができる。


集中型であれば、リポジトリはネットワークを介して接続する場面が多いが、ローカルリポジトリであればユーザー自身のPC上に管理サーバーあるのと同じなので、PCがネットワークに接続されていなくてもファイル管理状態を更新することができ、安心して作業ができる。また、他者へ公開する前にローカルで変更管理を行いながら、しっかり対応内容を確認してからリモートリポジトリへ公開すればいいので、焦って中途半端な状態で、他者へ公開する必要もない。





インストール


端末で実行する。

$ sudo apt-get install git


確認してみる。
$ git --version
git version 2.7.4






gitの設定


まず初めに設定するのは、変更をコミットするときに表示される自分の名前と、変更を加えた内容に他者が問い合わせる時に使えるメールアドレスを設定する。

$ git config --global user.name "hoge hogehoge"
$ git config --global user.email "hogehoge@email.com"


確認してみる。
$ git config --global --list
user.name=hoge hogehoge
user.email=hoge@email.com

変更したければ、再度コマンドを実行して上書きすればいい。

もうひとつ、gitをターミナルで見るときの出力をカラー表示にする設定をしておく。

$ git config --global color.ui "auto"



config情報(ユーザ全体 global)の設定は、以下のファイル。
~/.gitconfig

エディタで開けば、直接編集できる。





以降、話を簡単にするために、
1人だけで作業していて、リモートリポジトリを使う必要が無いとしよう。

リポジトリの設定


管理したいディレクトリへ移動する。
既存の管理したいファイルがあるディレクトリか、または、これからファイルを作成していくディレクトリを新規作成し、そのディレクトリへ移動する。
(このディレクトリを "作業ツリー" と呼ぶ)

移動したら、そのディレクトリで実行する。

$ git init

Initialized empty Git repository in <ディレクトリ>/.git/

".git" というディレクトリが作られ、リポジトリに必要なすべてのファイルが格納され、管理する準備ができた。

次に管理対象とするファイルは、ディレクトリに存在するとしよう。
管理対象を git に登録する。

更新内容を登録する


登録は2段階で行う。

まず、更新対象とするファイルを選択する。
"add" コマンドを使う。管理対象とするファイル名(ex:index.html)を直接指定する。

$ git add index.html


他にも "*.txt" といったワイルドカードでの指定も可能。
"." を指定すると、サブディレクトリ内も含めた全てのファイルを追加することができる。


次に、先ほど選択したファイルを更新(コミット)することで登録が完了する。
"-m" オプションを付けると、コミットメッセージを指定してコミットすることができる。

$ git commit -m "add new file."


コミットすることで、リポジトリに格納される1つ1つの履歴となる。
履歴を確認してみる。
$ git log
commit 9f03003ccb339e39eca4362750b7a8ac45641897
Author: hoge hogehoge <hoge@email.com>
Date: Sat Jun 17 15:00:00 2017 +0900

    add new file.






更新の追跡


日々の作業としてファイルを更新していく。

ファイルを更新すると、git はファイルが変化したことを認識している。
変更されたファイルの一覧を表示して、確認してみる。
$ git status
On branch master
Changes not staged for commit:
 (use "git add ..." to update what will be committed)
 (use "git checkout -- ..." to discard changes in working directory)

     modified: index.html

no changes added to commit (use "git add" and/or "git commit -a")


コミットするためには、変更したファイルを一旦ステージングエリア(インデックスとも呼ぶ)に移動させてからではないとコミットすることができない。
上記では、"Changes not staged for commit"とあり、
「変更されているが、ステージされていない」と教えてくれている。

コミットするために、ステージングエリアに移動させる。
ここでも、"add" コマンドを使えば良い。

$ git add index.html


確認してみる。
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD ..." to unstage)

    modified: index.html


ステージしたら、コミットする。"-m" オプションは複数つけられる。

git commit -m " add <h1>Tag " -m "Hello world"


コミットできたので、確認してみる。
$ git status
On branch master
nothing to commit, working directory clean

管理下から変更されたファイルが無くなった。



ログを確認してみる。
オプションの "-1" はコミットの履歴の出力数。最新1個の履歴を表示する。
$ git log -1
commit e81c8d53a2c3006cbd21ca615d70b53d46cf3952
Author: hoge hogehoge
Date: Sun Jun 18 10:00:00 2017 +0900

    add <h1>Tag

    Hello world



普段の作業は、この繰り返しでファイルの変更管理を行う。



ちなみに、コミット時に "-m" オプションを付けなければ、
コミットメッセージを編集するエディタが立ち上がるので、存分にコメントを編集すればよい。

$ git commit


git_cui01.png






差分の確認


ファイル変更後にステージし、コミットせず、再度ファイルを変更した。
状態を確認してみる。
$ git status
On branch master
Changes to be committed:
 (use "git reset HEAD ..." to unstage)

    modified: index.html

Changes not staged for commit:
 (use "git add ..." to update what will be committed)
 (use "git checkout -- ..." to discard changes in working directory)

    modified: index.html

同じファイルに対する変更だが、作業ツリーとステージングエリアの2箇所での変更点が出力された。


差分を確認する。
"diff" コマンドを使う。

$ git diff

diff --git a/index.html b/index.html
index a1fbdc5..03f5f85 100644
--- a/index.html
+++ b/index.html
@@ -7,5 +7,6 @@
 <body>
     <h1>Hello world!</h1>
     <p>こんにちは。</p>
+   <p>git install 確認</p>
 </body>
</html>


"diff" コマンドをパラメータ無しで使うと、作業ツリーでの変更差分のみが出力される。
作業ツリーとステージングエリアと比較結果が出力されている。

ステージングエリアとリポジトリとの変更点もあるはずなので、
次に、ステージングエリアとリポジトリとの差分を確認する。
"--cached" パラメータをつける。

$ git diff --cached

diff --git a/index.html b/index.html
index 97f1d8a..a1fbdc5 100644
--- a/index.html
+++ b/index.html
@@ -6,5 +6,6 @@
 </head>
 <body>
     <h1>Hello world!</h1>
+   <p>こんにちは。</p>
 </body>
</html>



作業ツリー・ステージングエリアのすべてと、リポジトリとの差分を確認する。
"HEAD" をつける。"HEAD"は直近のコミットを示す。

$ git diff HEAD

iff --git a/index.html b/index.html
index 97f1d8a..03f5f85 100644
--- a/index.html
+++ b/index.html
@@ -6,5 +6,7 @@
 </head>
 <body>
     <h1>Hello world!</h1>
+    <p>こんにちは。</p>
+    <p>git install 確認</p>

 </body>
</html>




以前のコミット状態に戻す


git resetコマンドを使う。
が、きちんと動きを理解して使わないと、現在の作業内容を消えてしまったりして泣くことになるので、ここでは触れない。改めて git についてまとめたい。




まとめ


ブランチの使い方、リモートリポジトリなど、git の便利な使い方はまだまだある。
ファイル管理が簡単にできるので、とても役立つのだが、
逆に理解せずに安易にコマンドを使うと、せっかく作業したものが消えてしまうので、
ファイルの履歴をさかのぼる方向への更新は気をつけないといけない。

git については理解した部分からまとめて、自分なりの活用マニュアルを作っていきたい。



git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
gitサーバーのセットアップ
git コミットを取り消す
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 23:00| Comment(0) | Linux | 更新情報をチェックする

Ubuntu インストールしたらやること:tree コマンドをインストール

CUIでファイルやディレクトリ構造を見やすく、階層的に表示してくれるコマンド。
デフォルトでは入っていないので、インストールする。

インストール


端末で以下を入力。

sudo apt-get install tree




使い方


引数無しで実行すると、カレントディレクトリ以下を階層表示する。

$ tree


$ pwd
/boot
$ tree
.
├── System.map-4.4.0-21-generic
├── System.map-4.4.0-78-generic
├── System.map-4.4.0-79-generic
├── abi-4.4.0-21-generic
├── abi-4.4.0-78-generic
├── abi-4.4.0-79-generic
├── config-4.4.0-21-generic
├── config-4.4.0-78-generic
├── config-4.4.0-79-generic
├── grub
│   ├── fonts
│   │   └── unicode.pf2
│   ├── gfxblacklist.txt
│   ├── grub.cfg
│   ├── grubenv
│   ├── i386-pc
│   │   ├── 915resolution.mod
│   │   ├── acpi.mod
│   │   ├── adler32.mod
・・・・・



引数にディレクトリを指定することで、指定したディレクトリ以下を階層表示する。

$ tree <ディレクトリ>


$ pwd
/boot
$ tree /var/log
/var/log
├── VBoxGuestAdditions-uninstall.log
├── VBoxGuestAdditions.log
├── Xorg.0.log
├── Xorg.0.log.old
├── alternatives.log
├── alternatives.log.1
├── apache2
│   ├── access.log
│   ├── access.log.1
│   ├── access.log.2.gz
│   ├── access.log.3.gz
│   ├── access.log.4.gz
│   ├── access.log.5.gz
│   ├── access.log.6.gz
│   ├── error.log
│   ├── error.log.1
・・・・・


複数のディレクトリを指定してもよい。

/$ tree /tmp /var/log/
/tmp
├── config-err-zpEn05
├── fcitx-socket-:0
├── gnome-software-ME101Y
│   └── debconf.socket
├── systemd-private-a3633a0560204440adcb7fa2d6ec793f-colord.service-NVVEVB [error opening dir]
├── systemd-private-a3633a0560204440adcb7fa2d6ec793f-rtkit-daemon.service-VEj2Ij [error opening dir]
└── unity_support_test.1
/var/log/
├── VBoxGuestAdditions-uninstall.log
├── VBoxGuestAdditions.log
├── Xorg.0.log
├── Xorg.0.log.old
├── alternatives.log
├── alternatives.log.1
├── apache2
│   ├── access.log
│   ├── access.log.1
│   ├── access.log.2.gz
│   ├── access.log.3.gz
│   ├── access.log.4.gz
│   ├── access.log.5.gz
│   ├── access.log.6.gz
│   ├── error.log
│   ├── error.log.1
・・・・・



-a オプション:隠しファイルも出力対象とする。

$ tree -a  /tmp/
/tmp/
├── .ICE-unix
│   └── 2302
├── .Test-unix
├── .X0-lock
├── .X11-unix
│   └── X0
├── .XIM-unix
├── .font-unix
├── config-err-zpEn05
├── fcitx-socket-:0
├── gnome-software-ME101Y
│   └── debconf.socket
├── systemd-private-a3633a0560204440adcb7fa2d6ec793f-colord.service-NVVEVB [error opening dir]
・・・・・


-d オプション:ディレクトリのみ出力対象とする。

$ tree -d /var/log/
/var/log/
├── apache2
├── apt
├── cups
├── dist-upgrade
├── fsck
├── hp
│   └── tmp
├── installer
├── lightdm
・・・・・


-L level:指定した階層の深さ(level)までを表示対象とする。

$ tree -L 2 /boot
/boot
├── System.map-4.4.0-21-generic
├── System.map-4.4.0-78-generic
├── System.map-4.4.0-79-generic
├── abi-4.4.0-21-generic
├── abi-4.4.0-78-generic
├── abi-4.4.0-79-generic
├── config-4.4.0-21-generic
├── config-4.4.0-78-generic
├── config-4.4.0-79-generic
├── grub
│   ├── fonts
│   ├── gfxblacklist.txt
│   ├── grub.cfg
│   ├── grubenv
│   ├── i386-pc
│   ├── locale
・・・・・






teratermで文字化けするときの対処


teratermでSSH接続し、
treeコマンドを実行すると、罫線が文字化けした。
tree_cmd01.png

treeの形は崩れていないので、気にしなければこのままでもいいかと思う。

気になるのであれば、--charset オプション で対処できる。

$ tree --charset=C


tree_cmd02.png
各ディレクトリの終端位置にあるファイルの罫線表示が違うものになっている(上図の赤丸)が、文字化けはなくなった。
ちなみに、"--charset=C" の「C」はなんでもいい。"--charset=x" でも、"--charset=o" でも。

posted by Zorinos at 12:00| Comment(0) | Linux | 更新情報をチェックする

Ubuntu サーバの設定 サービス(デーモン)の起動・停止を管理するツール

サービス(デーモン)の起動・停止を管理するツールとして、
"sysv-rc-conf" をインストールする。

RedHat系ではデフォルトで "chkconfig" がインストールされておりサービスの管理ができるが、Ubuntu では "chkconfig" が使えない(Ubuntu12.10から)。

代わりに、"chkconfig" に非常に似ている、"sysv-rc-conf" をインストールする。

インストールする


sudo apt-get install sysv-rc-conf







サービス一覧を取得してみる


--list オプションを使う。

$ sysv-rc-conf --list


acpid        2:on       3:on    4:on    5:on
alsa-utils 0:off 1:off 6:off S:on
anacron 2:on 3:on 4:on 5:on
apache-htcac 0:off 1:off 2:off 3:off 4:off 5:off 6:off
apache2 0:off 1:off 2:on 3:on 4:on 5:on 6:off
apparmor S:on
apport 2:on 3:on 4:on 5:on
avahi-daemon 0:off 1:off 2:on 3:on 4:on 5:on 6:off
bluetooth 0:off 1:off 2:on 3:on 4:on 5:on 6:off
brltty S:on
console-setu S:on
cron 2:on 3:on 4:on 5:on
cups 1:off 2:on 3:on 4:on 5:on
・・・・・

chkconfig コマンドと同じ感じで表示される。


それよりも、
TUI(テキストユーザインターフェース)で表示するほうがわかり易いと思う。

$ sudo sysv-rc-conf


sysv-rc-conf01.png
"X"印が有効となっている箇所。
画面の終了は、[q]キー。



ちなみに、Ubuntuのランレベルは、
Debian 系の多くのディストリビューションでは、ランレベル 2 から 5 を区別しない。
0 - 停止
1 - シングル
2 から 5 - マルチユーザーモードで、Xディスプレイマネージャも起動
6 - リブート
引用:Wikipedia


ということで、マルチユーザーモードで有効サービスには [2]~[5]のすべてにチェックが入っている。


sysv-rc-conf の使い方


TUIで扱う。

カーソル位置を [矢印]キー で移動。
sysv-rc-conf02.png

矢印キーの上・下操作で一覧画面のページを切り替えることもできるが、ダイレクトに、
[Ctrl + n] で下方向への画面切り替え、[Ctrl + p] で上方向への画面切り替えができる。


[space]キー でサービスのON/OFF を切り替える。
sysv-rc-conf03.png

[矢印]キーの代わりにマウスで直接クリックしても、ON/OFF が切り替わる。


サービスを停止させたい場合は、
[1] 〜 [5] [0] [6] [S] と並んでいる項目すべてOFFにする。
sysv-rc-conf04.png

サービスを有効とする場合は、逆に項目をONとする。
(デフォルトのランレベルであれば、[2]~[5] にチェックを入れる)


posted by Zorinos at 00:00| Comment(0) | Linux | 更新情報をチェックする

2017年06月16日

システムの起動

システムの起動の流れをざっくりと書くと、

電源ON・電源スイッチONでパワーコネクタの PS_ON の信号をONにする。
・電源ユニットは PS_ON の信号を受けるとパソコン各部に電源供給を開始。
・給電直後は電圧が不安定なので、クロック回路がCPUへのリセット信号をONにしCPUの処理を抑止する。
・電源出力が安定すると PWR_OK の信号をONにする。
・クロック回路は PWR_OK の信号を受けるとリセット信号をOFFにしCPUの動作を許可する。
BIOSの起動・M/BのフラッシュROMに書き込まれたBIOS(Basic Input/Output System)というソフトウェアが起動。
・POST(Power On Self Test)実行。
・起動するためのデバイスを探す。
MBRをメモリ上にロードし、MBR領域にあるプログラムに制御を移す。
1st Boot Loaderパーティションテーブルから起動フラグのあるパーティションを探す。
・IPL(Initial Program Loader)に制御を移す。
2nd Boot Loader・ブートイメージ(vmlinuz)をメモリ上にロード。
・イニシャルRAMディスク(initrd)をメモリ上にロード。
・制御をカーネルに移す。
カーネルの起動・ハードウェアリソースの初期設定。
・ルートファイルシステムのマウント。
・初期化プロセス。



Windowsでも、Linuxでも、だいたいこんな感じ。
が、もはやBIOSではなく、UEFIになっている。BIOSは古いもの。


とは言っても、基本となる流れのイメージは大きくはずれてないので、
抽象的にはOKとして、もっとざっくりと書くと、

電源ON
起動プログラムの実行
Boot Loaderの実行
カーネルの起動


「Boot Loaderの実行」は、Ubuntuの標準であれば「GRUB2の実行」となる。
GRUBは、/boot が存在するパーティションのファイルシステムをマウントするためのドライバを持ち、ブートイメージ(/boot/vmlinuz-*.*.*)とイニシャルRAMディスク(/boot/initrd.img-*.*.*)をメモリ上にロードすることができる。






ブートイメージについて


ブートイメージは、"vmlinuz-*.*.*"。

古くから、UNIXのカーネルイメージはunixというファイル名であり、cp (作成したイメージのファイル名) /unixのようにコピーしてインストールされていた。BSDで仮想記憶が実装され、仮想記憶をサポートしたカーネルであることを示すvm-という接頭辞を付けvmunixという名前が使われるようになった。vmlinuxという名前はそのvmunixを基にした命名法によるものである。さらにLinuxでは圧縮イメージという機能も追加され、vmlinuzという名前が付けられた。vmlinuxを圧縮したものであることを表す文字z(zipped)を後尾に付している。
引用:Wikipedia



vmlinuzのファイルタイプを見てみる。
$ sudo file vmlinuz-4.4.0-79-generic
vmlinuz-4.4.0-79-generic: Linux kernel x86 boot executable bzImage, version 4.4.0-79-generic (buildd@lcy01-30) #100-Ubuntu SMP Wed May 17 1, RO-rootFS, swap_dev 0x6, Normal VGA


bzImageだった。
vmlinuz.png

bzImage内のカーネルイメージ "bvmlinux.out" を取り出してみる。
作業ディレクトを作って、そこで作業。

$ mkdir /tmp/work
$ cd /tmp/work



/boot/vmlinuz-*.*.* を コピーしてリネーム。read許可しておく。

$ sudo cp /boot/vmlinuz-4.4.0-79-generic ./vmlinuz
$ sudo chmod +r ./vmlinuz



カーネルイメージは gzip 圧縮されていて、自己解凍形式になっている。
gzipなので、gzipヘッダーを探す('1f 8b 08 00')。
odコマンドを使う。-Ad で表示されるオフセットの基数を10進数に、-tx1 で1byte単位で16進数出力。

$ od -Ad -tx1 vmlinuz | grep '1f 8b 08 00'

0018864 ac fe ff ff 1f 8b 08 00 00 00 00 00 02 03 ec fd


18864 byte から 4 byte目に発見。
つまりこのブートイメージでは、18864 + 4 = 18868 byte目からがカーネルイメージ。
18868 byte以降を抽出する。
バイナリファイルなので、dd コマンドで開始位置までskipし、それを zcat に渡して展開し、vmlinux ファイルとして保存する。

$ dd if=vmlinuz bs=1 skip=18868 | zcat > vmlinux

gzip: stdin: decompression OK, trailing garbage ignored


ファイルタイプを見てみる。
$ file vmlinux
vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=29e4c9d663eaf39dc69806020ec82421b1086729, stripped


version を確認してみる。
バイナリデータだが、"Linux version"文字列は含まれているはずなので、
$ strings vmlinux | grep "Linux version"
Linux version 4.4.0-79-generic (buildd@lcy01-30) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #100-Ubuntu SMP Wed May 17 19:58:14 UTC 2017 (Ubuntu 4.4.0-79.100-generic 4.4.67)



GRUBから起動されたカーネルは、
自分自身で展開し、次のinitrdファイルをマウントする。





イニシャルRAMディスクについて


initrdファイルと書いたが、実のところは、initramfs。

ブート時に、ルートファイルシステムをマウントするためには、格納先が様々な媒体だったり、RAIDや暗号化されていたりなどの仕組みから、読み出すためのドライバをサポートしていなければいけない。
すべてに対応していくとカーネルが巨大化してしまう。
そこで、カーネルにドライバの対応コード追加していくことを避け、ブートに必要なモジュール群をまとめたもの置いておく一時的なルートファイルシステム(ミニルート)を準備し、それをブート時にマウントして使う、initrd (Initial RAM Disk) という仕組みを作った。

ブートローダは、カーネルを起動するときに合わせて initrdイメージ をロードしたメモリアドレスも渡す。
カーネルは initrd イメージの先頭を読み込み、そのフォーマットを判断する。

initrd はブロックデバイス(RAM ディスク)をgzip圧縮したもの。
Linux 2.6からは、RAM上に直接ファイルシステムを作る ramfs を用いたinitramfsを導入。
initramfsはファイルシステムをcpio形式でアーカイブしたうえで、gzipで圧縮したファイル。


展開してみる。
また作業ディレクトを作って、そこで作業。

$ mkdir /tmp/initramfs
$ cd /tmp/initramf



/boot/initrd.img-*.*.* を コピーしてリネーム。

$ sudo cp /boot/initrd.img-4.4.0-79-generic ./initrd.img



中身を展開する。

$ zcat ./initrd.img | cpio -id

gzip: ./initrd.img: not in gzip format
cpio: 予期しない書庫終了です


あれ、gzip じゃないのか?

ファイルの先頭を見てみる。
$ hexdump -C -n 512 ./initrd.img

ADDRESS  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 30 37 30 37 30 31 30 30 30 30 30 30 36 35 30 30 |0707010000006500|
00000010 30 30 34 31 45 44 30 30 30 30 30 30 30 30 30 30 |0041ED0000000000|
00000020 30 30 30 30 30 30 30 30 30 30 30 30 30 32 35 39 |0000000000000259|
00000030 33 37 37 45 34 36 30 30 30 30 30 30 30 30 30 30 |377E460000000000|
00000040 30 30 30 30 30 33 30 30 30 30 30 30 30 31 30 30 |0000030000000100|
00000050 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
00000060 30 30 30 30 30 37 30 30 30 30 30 30 30 30 6b 65 |00000700000000ke|
00000070 72 6e 65 6c 00 00 00 00 30 37 30 37 30 31 30 30 |rnel....07070100|
00000080 30 30 30 30 36 36 30 30 30 30 34 31 45 44 30 30 |000066000041ED00|
00000090 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
000000a0 30 30 30 30 30 32 35 39 33 37 37 45 34 36 30 30 |00000259377E4600|
000000b0 30 30 30 30 30 30 30 30 30 30 30 30 30 33 30 30 |0000000000000300|
000000c0 30 30 30 30 30 31 30 30 30 30 30 30 30 30 30 30 |0000010000000000|
000000d0 30 30 30 30 30 30 30 30 30 30 30 30 30 42 30 30 |0000000000000B00|
000000e0 30 30 30 30 30 30 6b 65 72 6e 65 6c 2f 78 38 36 |000000kernel/x86|
000000f0 00 00 00 00 30 37 30 37 30 31 30 30 30 30 30 30 |....070701000000|
00000100 36 37 30 30 30 30 34 31 45 44 30 30 30 30 30 30 |67000041ED000000|
00000110 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
00000120 30 32 35 39 33 37 37 45 34 36 30 30 30 30 30 30 |0259377E46000000|
00000130 30 30 30 30 30 30 30 30 30 33 30 30 30 30 30 30 |0000000003000000|
00000140 30 31 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0100000000000000|
00000150 30 30 30 30 30 30 30 30 31 35 30 30 30 30 30 30 |0000000015000000|
00000160 30 30 6b 65 72 6e 65 6c 2f 78 38 36 2f 6d 69 63 |00kernel/x86/mic|
00000170 72 6f 63 6f 64 65 00 00 30 37 30 37 30 31 30 30 |rocode..07070100|
00000180 30 30 30 30 36 38 30 30 30 30 38 31 41 34 30 30 |000068000081A400|
00000190 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
000001a0 30 30 30 30 30 31 35 39 33 37 37 45 34 36 30 30 |00000159377E4600|
000001b0 30 30 32 38 30 30 30 30 30 30 30 30 30 33 30 30 |0028000000000300|
000001c0 30 30 30 30 30 31 30 30 30 30 30 30 30 30 30 30 |0000010000000000|
000001d0 30 30 30 30 30 30 30 30 30 30 30 30 32 41 30 30 |0000000000002A00|
000001e0 30 30 30 30 30 30 6b 65 72 6e 65 6c 2f 78 38 36 |000000kernel/x86|
000001f0 2f 6d 69 63 72 6f 63 6f 64 65 2f 47 65 6e 75 69 |/microcode/Genui|


cpioのマジックナンバー "070701" が入っている。単なるcpio形式なのかな?

だったら、直接cpioで展開してみる。
$ cpio -id < ./initrd.img
22 ブロック


が、x86のマイクロコードらしきものしか展開されない。
$ tree kernel/

kernel/
└── x86
└── microcode
└── GenuineIntel.bin

2 directories, 1 file


それなら、また、gzipヘッダーを探してみる。

$ od -Ad -tx1 initrd.img | grep '1f 8b 08 00'

0011264 1f 8b 08 00 4a 7e 37 59 00 03 d4 3b 6b 57 db 48


見つかったので展開してみる。

$ dd if=initrd.img bs=1 skip=11264 | zcat > initrmfs

gzip: stdin: decompression OK, trailing garbage ignored


cpioで展開する。

$ cpio -id < ./initrmfs

197303 ブロック


ミニルートが現れた。
$ tree -d -L 1

.
├── bin
├── conf
├── etc
├── kernel
├── lib
├── lib64
├── run
├── sbin
├── scripts
├── usr
└── var

11 directories



このミニルートをブート時のルートファイルシステムとしてマウントし、
実際のルートファイルシステムをマウントするために必要なドライバをカーネルに読み込み、それから実際のルートファイルシステムをマウントする。

やっと、ルートファイルシステム内にあるファイルを用いて初期化が実行される。
次の初期化プロセスが完了すれば、Linuxが使えるようになる。





初期化プロセスについて


カーネルが起動した後、initプロセスが起動する。
init(/sbin/init が実行)は、他のすべてのプロセスを起動するプロセスであり,すべての親プロセスになる。
最初に起動するので、PIDは必ず1になる。
/etc/inittab ファイルに書かれた ランレベルを設定し、該当する/etc/rc.d/rc*を実行していく。

というのが、System V系のinit。


Ubuntuではinitの代わりとして、Ubuntu 6.10以降で導入された、Upstartが使われていたが、
Ubuntu 14.04
$ dpkg -S /sbin/init
upstart: /sbin/init

Ubuntu 15.04以降では systemdが採用された。
(参考:SystemdForUpstartUsers
Ubuntu 16.04
$ dpkg -S /sbin/init
systemd-sysv: /sbin/init


で、systemdによるシステム起動のプロセスを確認していたが、
新たな考え方が必要になってきたので、このあたりまでにしておいて、
こんな流れでLinuxが起動して、使えるようになる。




まとめ


ざっと吐き出してみて、全体的にぼんやりとであるが流れの確認ができた。
とは言え、自身の理解は表層しかわかっておらず、
足りない所も多く、間違っていることもあるだろうし、具体的な動きはブラックボックスのまま。
その辺りに、いつか踏み込む日が来たら、そのときに改めて理解しようと思う。

いまは、こんな感じなのか、ふーん・・ という程度でよしとしておこう。


posted by Zorinos at 18:00| Comment(0) | Linux | 更新情報をチェックする
ブログランキング・にほんブログ村へ
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。