systemd 概要
これはなに
systemdってなに?と思って調べたことのまとめ。
systemdとは
UNIX系OSにおいて、自身を含む全てのバックグラウンドプロセス(サービス)のデーモン(メモリ上で待機している常駐プログラム)を管理するシステム管理デーモン。
OS起動直後にカーネルにより実行されるinitプロセス(PID 1)に当たる。
全てのプロセスの元となるプロセスになる。
$ pstree # プロセスをツリー状に表示するコマンド systemd─┬─VBoxService───8*[{VBoxService}] ├─accounts-daemon───2*[{accounts-daemon}] ├─agetty ├─atd ├─cron ... ├─systemd-network ├─systemd-resolve ├─systemd-udevd └─uuidd
systemdが目指したもの
- システム起動時間を短縮
- 必要なサービス群の起動の並列実行によりinitプロセスを時短できる
- システム構成の動的変更に対応
- システム起動時だけでなく、変更に応じて動的にサービス起動/停止を行う
- プロセス停止処理を標準機能として提供
- デーモンの実行環境を制御
- サービスごとにデーモンの実行環境(リソース割り当てやアクセス可能なディレクトリ)やログ出力を管理
管理方法 - Unit
管理単位
処理はUnitという単位で管理される。
Unit file の構成
Unitfileは3つのセクションから構成される。
Section | description |
---|---|
Unit | Unitの説明や依存/順序関係など、基本となる設定を記述 |
Service | 起動,停止コマンドや環境変数ファイル、起動判定方法や再起動条件、起動するユーザや指定ディレクトリのアクセス制御など、そのデーモン固有の設定を記述 |
Install | systemctl enable/disable (後述)時の挙動を記述 |
Unit間の依存/順序関係
- 依存関係
- AというUnitを有効化するなら、BというUnitも有効化すべき、という関係
- Unitセクションにおいて、
Wants=
やRequires=
,Conflict=
で定義する。 <UNIT>.wants
,<UNIT>.requires
ディレクトリに依存関係のあるUnitファイルへのシンボリンクリンクを張ることでも定義できる。
- 順序関係
- AというUnitを有効化する前に、BというUnitを有効化すべき、という関係
- Unitセクションにおいて、
After=
やBefore=
で定義する。
Unitの種類
Unitファイルのファイル名末尾で見分ける。
種類 | 作成方法 | description |
---|---|---|
.service | 明示的に定義 | 有効化すると対応するデーモンが起動する。 |
.target | 明示的に定義 | 何もしない。Unit間の依存/順序関係を定義する時、複数のUnitをグループ化するのに使用する。 |
.mount | /ect/fstab から自動作成 |
有効化するとマウントされる。 |
.swap | /ect/fstab から自動作成 |
有効化するとSwap領域が有効になる。 |
.device | udev から自動作成 |
udevがデバイスを認識すると有効化される。 |
.socket | 明示的に定義 | systemdが特定のソケットをListenし、接続があると指定のデーモンを起動してソケットを受け渡す。 |
管理場所
- /etc/systemd/system
- こちらが優先実行される。サーバー管理者がカスタマイズできるところ。
- /usr/bin/lib/systemd/system
- デフォルト設定。基本は触らない。触りたい時は
/etc/systemd/system
に同名でコピーして触る。
- デフォルト設定。基本は触らない。触りたい時は
主要コマンド
$ systemctl list-unit-files # Unitの一覧表示 $ systemctl list-units # 有効(であるべき)Unitとその状態の一覧表示 $ systemctl enable/disable <UNIT> # Unitの自動起動,無効化 $ systemctl start/stop/reload <UNIT> # Unitの手動起動,停止,再起動(Unit fileにreload動作が設定されている場合のみ) $ systemctl status <UNIT> # Unitの実行状態の表示(関連するデーモンプロセスや直近のログなど) $ systemctl deamon-reload # Unit fileを変更した際にsystemdに認識させる
参考
ISUCON9予選問題をVagrantfileからVirtualBox上で動かす
概要
ISUCON9 予選問題の環境を、手元のMacOSで再現します。
ローカル環境でISUCON9予選の問題を動かす : ISUCON公式Blogにあるように、アプリケーション自体はホストOSで動かすことができますが、今回はより本番に近い環境を再現するため、matsuuさんが提供してくれているVagrantfileを使用し、VirtualBoxで構築した仮想OS上に構築します。
環境
- MacOS
- VirtualBox 6.1.4
- Vagrant 2.2.7
準備
VirtualBoxとVagrantをインストール
VirtualBoxとは
Oracleが提供する、クロスプラットフォームの仮想化アプリケーション。
WindowsやMacOS,Linuxなどにインストールすると、VirtualBoxを通して別OSを動かすことができる。
Oracle VM VirtualBox is a cross-platform virtualization application.
ref. Oracle® VM VirtualBox®
Vagrantとは
HashiCorpが提供する、仮想マシンの環境構築ツール。
VirtualBoxやVMwareなど、Vagrantプラグインをサポートしている仮想化ソフトウェア上で動作する。
Vagrant is a tool for building and managing virtual machine environments in a single workflow.
ref. Introduction - Vagrant by HashiCorp
install
Homebrewからインストール。
$ brew cask install virtualbox $ VBoxManage -v 6.1.4r136177 $ brew cask install vagrant $ vagrant -v Vagrant 2.2.7
公式ページから直接ダウンロードしてもOK。
ISCON9 予選問題環境構築のためのVagrantfileを取得する
Vagrantfileとは
仮想マシンの環境構築に必要なマシンタイプと、そのマシンの設定及びプロビジョニング (準備)方法を記載したファイル。Vagrantがサポートする全てのプラットフォーム間で移植可能。
通常、プロジェクトに必要な各種ソースコードと一緒にVCS(Gitなど)にコミットされる。
The primary function of the Vagrantfile is to describe the type of machine required for a project, and how to configure and provision these machines.
ref. Vagrantfile - Vagrant by HashiCorp
取得
githubから取得する。
$ ghq get git@github.com:matsuu/vagrant-isucon.git # 普通に # git clone git@github.com:matsuu/vagrant-isucon.git # などでもOK
起動
環境構築
Vagrantfileの提供元READMEに書いてある通りに起動してみる。
$ cd ./isucon9-qualifier-standalone $ vagrant up #up: starts and provisions the vagrant environment
初回はbento/ubuntu-18.04
のダウンロードを挟むので時間がかかる。
ダウンロード終了後にエラーで起動が失敗してしまった時の奮闘記はこちら。
moeka.hatenablog.jp
ベンチマーク起動
$ vagrant ssh vagrant@vagrant$ sudo -i -u isucon isucon@vagrant$ cd isucari isucon@vagrant$ bin/benchmarker
この状態でのスコアは以下。
{"pass":true,"score":2310,"campaign":0,"language":"Go","messages":["GET /items/47204.json: リクエストに失敗しました(タイムアウトしました)"]}
思ってたよりちゃんと出た。
ホストからアプリケーションにアクセス
ホストオンリーネットワークをDHCPで構築しているため、ゲストOS側に割り当てられているIPアドレスを調べる。
isucon@vagrant$ ifconfig # ... eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.28.128.3 netmask 255.255.255.0 broadcast 172.28.128.255 inet6 fe80::a00:27ff:fe3f:84dc prefixlen 64 scopeid 0x20<link> ether 08:00:27:3f:84:dc txqueuelen 1000 (Ethernet) RX packets 21 bytes 5788 (5.7 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 30 bytes 4428 (4.4 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 # ...
以下のURLでホストOSのブラウザからアクセス
https://172.28.128.3
無事画面が表示できたので、接続成功。
後記
次は計測ツールの導入などしていきたいです。 ただ、提供されているvagrantfileを使うとansibleやソースコードがgithubからの入手になってしまっていて手軽にコード修正&反映ができないので、ローカルのものを見るように改造するところからかな。
VagrantでのDHCPを有効にしたhost only networkの作成に失敗する
これはなに
表題の件でvagrant up
に失敗したので、原因究明の軌跡と解決策をメモしておく。
環境
- MacOS
- VirtualBox 6.1.4
- Vagrant 2.2.7
問題
あるvagrantfileがあるディレクトリ下でvagrant up
を実行したところ、下記のエラーがおきた。
$ vagrant up # 前略 ==> default: Clearing any previously set network interfaces... A host only network interface you're attempting to configure via DHCP already has a conflicting host only adapter with DHCP enabled. The DHCP on this adapter is incompatible with the DHCP settings. Two host only network interfaces are not allowed to overlap, and each host only network interface can have only one DHCP server. Please reconfigure your host only network or remove the virtual machine using the other host only network.
DHCPを有効にして設定しようとしているホストオンリーネットワークインターフェースには、すでにDHCPが有効なホストオンリーアダプターが作成されており、競合しています。
このアダプターが接続しているDHCPは、DHCP設定との互換性がありません。 ホストオンリーネットワークインターフェースは重複できません。また、それぞれのホストオンリーネットワークインターフェースは1つのDHCPサーバーとしか接続できません。 ホストオンリーネットワークを再構成するか、他のホストオンリーネットワークを使用している仮想マシンを削除してください。
原因究明
ref. dhcp private_network fails on virtualbox · Issue #3083 · hashicorp/vagrant
こちらのIssueで原因の解説がしてあった。
以下は上記PRの解説を、補足を交えながら日本語訳したものである。
Vagrantfileでの設定
vagrantfileを覗いてみると、ホストオンリーネットワークをDHCPを有効にして設定しようとしている。
# Create a private network, which allows host-only access to the machine # using a specific IP. # config.vm.network "private_network", ip: "192.168.33.10" config.vm.network "private_network", type: "dhcp"
VirtualBoxのネットワーク設定
VirtualBoxには組み込みDHCPサーバーを持っており、デフォルトで以下の設定がされている。
$ VBoxManage list dhcpservers NetworkName: HostInterfaceNetworking-vboxnet0 Dhcpd IP: 192.168.56.100 LowerIPAddress: 192.168.56.101 UpperIPAddress: 192.168.56.254 NetworkMask: 255.255.255.0 Enabled: Yes Global Configuration: minLeaseTime: default defaultLeaseTime: default maxLeaseTime: default Forced options: None Suppressed opts.: None 1/legacy: 255.255.255.0 Groups: None Individual Configs: None $ VBoxManage list hostonlyifs # empty
この状態でvagrant up
をした際、type: "dhcp"
のprivate_networkに遭遇すると、ホストオンリーネットワークが作成され、インターフェースに以下が設定される。
$ VBoxManage list hostonlyifs Name: vboxnet0 GUID: 786f6276-656e-4074-8000-0a0027000000 DHCP: Disabled IPAddress: 172.28.128.1 NetworkMask: 255.255.255.0 IPV6Address: IPV6NetworkMaskPrefixLength: 0 HardwareAddress: 0a:00:27:00:00:00 MediumType: Ethernet Wireless: No Status: Up VBoxNetworkName: HostInterfaceNetworking-vboxnet0
ホストオンリーネットワークとDHCPサーバーは同じネットワークHostInterfaceNetworking-vboxnet0
にあるにもかかわらず、ホストオンリーアダプタのIPは172.28.128.1
、対してDHCPサーバーのIPは192.168.56.100
と、別のネットワークのIPが設定されてしまっている。
ここで、アダプターのネットワーク外のアドレスをDHCPサーバーには割り当てられないぞ!と怒られているらしい。
解決策
デフォルトで設定されてしまっているdhcpserverの設定を削除し、そのままvagrant up
を再試行したところ、仮想マシンが無事に起動した。
$ VBoxManage dhcpserver remove --netname HostInterfaceNetworking-vboxnet0 $ VBoxManage list dhcpservers # empty $ vagrant up # success!
念のため再度DHCPサーバーの設定をみると、
$ VBoxManage list dhcpservers NetworkName: HostInterfaceNetworking-vboxnet0 Dhcpd IP: 172.28.128.2 LowerIPAddress: 172.28.128.3 UpperIPAddress: 172.28.128.254 NetworkMask: 255.255.255.0 Enabled: Yes Global Configuration: minLeaseTime: default defaultLeaseTime: default maxLeaseTime: default Forced options: None Suppressed opts.: None 1/legacy: 255.255.255.0 Groups: None Individual Configs: None
今度はちゃんとホストオンリーネットワークと同じネットワーク内のIPが設定されているのがわかる。
後記
VirtualBoxの初期設定とVagrantのホストオンリーネットワーク作成の仕様が若干マッチしないことによるバグのようでした。
一度対応すればおそらく二度と起こらない問題な気はしますが、バグの直接原因とエラーメッセージの内容も若干齟齬っているのでめちゃくちゃハマりポイントですね…
こういうIssueの作成者には頭が下がるばかりです。
ISUCON9 予選問題用のMySQLをDockerで立てる
これはなに
isucon.net ISUCON9予選問題の環境構築をするために上記の公式記事を参考にぽちぽちやっていた最中、 mysqlサーバーをホストに立てるのが環境を汚しそうで嫌だったので、Dockerで立ててみることにしました。
職場でdocker-compose up
くらいはするものの、ちゃんと自分でDockerで何かを構築するのは初めてだったので
いろいろ調べながらとりあえず課題のWebアプリが普通に動く最低ラインを目指してやったことの作業ログです。
環境
MacOS Catalina MacBook Pro 2017 3.1GHz デュアルコアIntel Corei5 メモリ 16GB
手順
CLIから起動
docker run
で、imageからcontainerを起動する。
imageはdocker hubからダウンロードしてくる。
$ docker run --name cli-mysql -e MYSQL_ROOT_PASSWORD=pass -d mysql:latest
ref. mysql - Docker Hub
Starting a MySQL instance is simple:
bash $ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
... where some-mysql is the name you want to assign to your container, my-secret-pw is the password to be set for the MySQL root user and tag is the tag specifying the MySQL version you want. See the list above for relevant tags.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9e3b15e64c77 mysql:latest "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 3306/tcp, 33060/tcp cli-mysql
起動はしており、3306と33060でmysqldの待ち受けはしているが、portのバインドができていないのでローカルからの接続はできない。
docker run --name cli-mysql -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 -d mysql:latest
--publish , -p : Publish a container’s port(s) to the host
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9e3b15e64c77 mysql:latest "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp cli-mysql
公開されたようだ。接続してみる。
$ mysql --port=3306 --host=127.0.0.1 --user=root -p Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.19 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
OK!
DockerfileからImageを作って起動
docker build PATH | URL
でimageを自作する。
PATH(a directory on your local filesystem)やURL(Git repository location)はcontextと呼ばれる。
docker build
は、Docker deamonにcontext内のファイル(.dockerignoreで指定されたファイルは除く)を送信し、contextルートに配置されたDockerfile
の手順にしたがって、Docker deamon内でimageをbuildする。(そのため、buildに必要のないファイルはきちんと.dockerignoreで指定しておいた方がbuildの速度は早くなる)
Docker deamonは、Dockerfileに記載された命令文を1つずつ独立した状態で実行しながら新しいimageを都度作っていき、最終的に得られたimageを出力する。 DockerfileのCOPY命令などでcontext内のファイルを参照しつつbuildを行う。
FROM mysql:latest ENV MYSQL_ROOT_PASSWORD=pass
$ docker build . Successfully built 855977a52a9d $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 855977a52a9d 7 seconds ago 547MB $ docker run --name file-mysql -p 3306:3306 -d 855977a52a9d # -eオプションの指定が消えた $ mysql --port=3306 --host=127.0.0.1 --user=root -p
imageのREPOSITORY, TAGの指定は docker build -t ${REPOSITORY}:${TAG}
で行う。Dorckerfileに記述するものではない。
$ docker build -t moeka-m/mysql:latest . $ docker images EPOSITORY TAG IMAGE ID CREATED SIZE moeka-m/mysql latest 855977a52a9d 16 hours ago 547MB
初期データを自動で読み込むように設定
Initializing a fresh instance
When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.
ref. mysql - Docker Hub
containerを起動させると、/docker-entrypoint-initdb.d
に配置された.sh
,.sql
,.sql.gz
をアルファベット順に実行してDababaseを初期化しますよ、の意。
FROM mysql:latest ENV MYSQL_ROOT_PASSWORD pass # 文字コードの指定がないとinitial.sqlの実行で Data too long for column と言われて失敗する ENV LANG C.UTF-8 COPY . /docker-entrypoint-initdb.d
データの初期投入をするshellは要らなくなったので.dockerignore
に書いておく
init.sh
これで実行してみる。
$ docker build -t moeka-m/mysql:latest . $ docker run --name file-mysql -p 3306:3306 -d moeka-m/mysql:latest $ mysql --port=3306 --host=127.0.0.1 --user=isucari -p mysql > SHOW TABLES FROM isucari; +-----------------------+ | Tables_in_isucari | +-----------------------+ | categories | | configs | | items | | shippings | | transaction_evidences | | users | +-----------------------+ 6 rows in set (0.02 sec) mysql> SELECT id, name, price FROM isucari.items LIMIT 1; +----+---------------------------------------------------------------------------------------------------------------------+-------+ | id | name | price | +----+---------------------------------------------------------------------------------------------------------------------+-------+ | 1 | 浮くことなく世界中の4段階採用したっぷり入れロースタイルをスタッキング曲げ木の | 100 | +----+---------------------------------------------------------------------------------------------------------------------+-------+ 1 row in set (0.00 sec)
Webappから接続
Hostの環境変数に接続情報を入れておく
$ export MYSQL_HOST=127.0.0.1 $ export MYSQL_PORT=3306 $ export MYSQL_USER=isucari $ export MYSQL_DBNAME=isucari $ export MYSQL_PWD=isucari
あとは冒頭リンクの記事通りにWebアプリを起動すると無事に起動!
後記
ちなみにこの状態でベンチマーカーを起動させると
2020/03/28 21:59:01 main.go:180: === final check === 2020/03/28 21:59:01 main.go:212: 410 0 {"pass":true,"score":410,"campaign":0,"language":"Go","messages":[]}
これくらいのスコアになりました。 公式記事では初期スコアでも3020くらいいってたのでだいぶ遅いです。やっぱdockerで立てると遅くなるのかも。