個人用のメモです。
RHEL7 on EC2にdockerをセットアップする手順
はじめに
RHEL7 on EC2(License Included)でのdockerセットアップ手順を記載します。
Amazon Linux2へのdockerセットアップ手順(下記)との違いは、RHUI*1のEPELレポジトリを有効化してそこからdockerをインストールするところです。
nopipi.hatenablog.com
手順
RHEL7 on EC2へのdockerインストール
基本的には、ECS agentガイドにある手順を参考にRHEL用に一部カスタマイズして進めます。
EPELレポジトリの登録
awsが提供するライセンス込みのRHELインスタンスにdockerをインストールする場合は、RHUI(Red Hat Update Infrastructure)*2のEPEL(Extra Packages for Enterprise Linux)から、dockerをダウンロードします。
EPELはデフォルトでは登録がないので、まずEPELの登録をします。(2020/11/10 RHUIの仕様が変わったようで、リポジトリ名が変わっていたのでそれに合わせてコマンドを変更しました)
sudo yum install yum-utils
sudo yum-config-manager --enable rhel-7-server-rhui-extras-rpms
dockerの起動とOS起動ときの自動起動設定
sudo systemctl start docker sudo systemctl enable docker
dockerコマンドをec2-userで実行するための設定
ec2-userのセカンダリグループに、dockerrootグループを追加します。dockerのグループが、dockerではなくdockerrootなので注意してください。
あとdockerdのsocketファイルの所有者が"root:root"なので、dockerrootグループがwriteできるよう所有者を"root:dockerroot"に変更します。(もっとスマートな方法があればいいのですが、ちょっと見つからなかったので)
sudo usermod -a -G dockerroot ec2-user sudo chown root:dockerroot /var/run/docker.sock
確認
dockerコマンドを実行して確認する。実行して、ClientとServerの両方の情報が表示されればOKです。
$ docker version Client: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.6 Git commit: 3dfb8343b139d6342acfd9975d7f1068b5b1c3d3 Built: Fri Jun 15 23:15:12 2018 OS/Arch: linux/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.6 Git commit: 7390fc6/18.03.1-ce Built: Fri Jun 15 23:17:24 2018 OS/Arch: linux/amd64 Experimental: false
リファレンス
EC2(Amazon Linux2)にprivateなdocker registryの作るメモ
1.はじめに
検証用にプライベートなdocker registryをAWS EC2上のAmazon Linux2に作った時のメモです。レジストリを立てて、別のサーバからdockerイメージをpush/pullして確認するまでを記録しています。
内容は、こちらの記事を参考にしています。
2.セットアップ手順
2.1 前提環境
雑な絵ですが、こんな感じの構成を作ります。
このメモの前提となる構成は以下の通りです。
2.2 dockerのセットアップ
プライベートdocker registryをインストールするインスタンスにsshログインして、下記の記事の手順でdockerをセットアップします。
nopipi.hatenablog.com
dockerレジストリ用のディレクトリ作成
/home/docker/ └── registry ├── certs └── data
ディレクトリの作成と確認手順はこちらですl
sudo mkdir -p /home/docker/registry/{certs,data} find /home/docker/
2.3 SSLの自己証明書(オレオレ証明書)の作成
検証用なのでオレオレ証明書で勘弁してもらいます。opensslコマンドの途中で聞かれる内容は、とりあえず全てデフォルト(リターン)とします。
openssl req -newkey rsa:4096 -nodes -sha256 -keyout domain.key -x509 -days 365 -out domain.crt
- オプション説明
作成した秘密鍵(domain.key)と、サーバ証明書(domain.crt)をレジストリのディレクトリに移動します。
sudo mv domain.key /home/docker/registry/certs/ sudo mv domain.crt /home/docker/registry/certs/
2.4 dockerレジストリの起動
レジストリのイメージ登録とコンテナの実行を同時に行います。
cd /home/docker/registry docker run -d \ --restart=always \ --name docregistry \ -v `pwd`/certs:/certs \ -v `pwd`/data:/var/lib/registry \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -e REGISTRY_STORAGE_DELETE_ENABLED=True \ -p 443:443 \ registry:2
- オプション説明
dockerイメージを確認します。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE registry 2 b2b03e9146e1 2 weeks ago 33.3MB
registryコンテナが実行されているか確認します。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ddd75d47e43a registry:2 "/entrypoint.sh /etc…" 16 seconds ago Up 16 seconds 0.0.0.0:443->443/tcp, 5000/tcp docregistry
3.クライアントからdockerイメージをpushしてみる
dockerのクライアントとなる別のサーバから、プライベートdocker registryへのimageのpush、pullを確認します。
3.1 httpsの接続確認
まずは素のhttpsで、クライアントから、プライベートdocker registryにアクセス可能かを確認します。
(1)サーバ証明書のコピー
プライベートdocker registry用に作成したサーバ証明書を、クライアントにコピーします。ssh ログインするときに"-A"オプションを利用するとsshの鍵を自動でforwardingしてくれるので、クライアントのインスタンスに秘密鍵を置く必要がありません。(今回の話からは脱線するので、詳しくはこちらを参照)
MACの場合でデフォルトの秘密鍵を利用する場合は、こんな感じでログインします。
ssh -A ec2-user@[クライアントのDNS or IP] scp ec2-user@ip-10-203-64-60.ec2.internal:/home/docker/registry/certs/domain.crt .
3.2 dockerへのサーバ証明書登録
コピーしたサーバ証明書をdockerの設定ディレクトリに格納します。
格納は、下記ルールに基づいて格納します。
- "/etc/docker/certs.d/[dockrレジストリのFQDN:ポート番号]"のディレクトリに
- ”ca.crt”というファイル名で格納
- ディレクトリのポート番号は、httpsでフォルトの443の場合は省略可能
この/etc/dockerディレクトリは、root権限がないと移動することもできないので、rootにチェンジして作業します。
$ sudo -i # cd /etc/docker/ # mkdir certs.d # cd certs.d/ # mkdir ip-10-203-64-60.ec2.internal # cp /home/ec2-user/domain.crt /etc/docker/certs.d/ip-10-203-64-60.ec2.internal/ca.crt # exit $
3.3 プライベートレジストリへのイメージのpushとpull
動作確認として、docker hubからubuntuのコンテナイメージをpullしてきて、プライベートdockerレジストリに登録してみます。
(1)イメージのpullとtag作成
ubuntuのイメージをpullしてきて、プライベートdockerレジストリ登録用のtagを作成します。
docker pull ubuntu:16.04 docker tag ubuntu:16.04 ip-10-203-64-60.ec2.internal/my-ubuntu docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 16.04 e13f3d529b1a 4 days ago 115MB ip-10-203-64-60.ec2.internal/my-ubuntu latest e13f3d529b1a 4 days ago 115MB
(2)イメージのpush
プライベートdocker registryにイメージをpushします。
docker push ip-10-203-64-60.ec2.internal/my-ubuntu The push refers to repository [ip-10-203-64-60.ec2.internal/my-ubuntu] 709bdd00b1a4: Pushed 07b9c3c04cbd: Pushed 6eaddaf493f1: Pushed a0e188d0e278: Pushed 711e4cb62f50: Pushed latest: digest: sha256:0d06090fff94c0a3640729c7ef7e6b36ad5b41bec600cc2be92739c90d204243 size: 1357
pushしたイメージが登録されているか、プライベートdocker registryのカタログを参照します。
[ec2-user@ip-10-203-128-109 ~]$ curl --cacert domain.crt https://ip-10-203-64-60.ec2.internal/v2/_catalog {"repositories":["my-ubuntu"]}
(3)イメージのpull
まず確認のため、ローカルにあるdockerイメージを削除します。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 16.04 e13f3d529b1a 4 days ago 115MB ip-10-203-64-60.ec2.internal/my-ubuntu latest e13f3d529b1a 4 days ago 115MB $ docker rmi e13f3d529b1a e13f3d529b1a $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE
プライベートdocker registryから、イメージをpullして確認します。
$ docker pull ip-10-203-64-60.ec2.internal/my-ubuntu $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ip-10-203-64-60.ec2.internal/my-ubuntu latest e13f3d529b1a 4 days ago 115MB
opensslコマンドのhelpを出す
opensslコマンドのメモです。
1.コマンドのSYNOPSIS
openssl command [ command_opts ] [ command_args ]
2.コマンドのヘルプ表示方法
2.1 [command]一覧を出す
"help"というコマンドは無いが、”invalid command”エラーでコマンド一覧が表示される。コマンドの説明は"man openssl"で確認する。
openssl help
2.2 各コマンドのオプション
コマンドの後ろに"help"をつけると、"unknown option"となりオプション一覧が表示される。
例えばreqコマンドのオプションの場合は以下
openssl req help
またreqなどのスタンダードコマンドは、"man req"などmanでコマンド名を指定することで詳細なmanを参照できる。
man req REQ(1) OpenSSL REQ(1) NAME req - PKCS#10 certificate request and certificate generating utility. SYNOPSIS openssl req [-inform PEM|DER] [-outform PEM|DER] [-in filename] [-passin arg] [-out filename] [-passout arg] [-text] [-pubkey] [-noout] [-verify] [-modulus] [-new] [-rand file(s)] [-newkey rsa:bits] [-newkey alg:file] [-nodes] [-key filename] [-keyform PEM|DER] [-keyout filename] [-keygen_engine id] [-[digest]] [-config filename] [-multivalue-rdn] [-x509] [-days n] [-set_serial n] [-asn1-kludge] [-no-asn1-kludge] [-newhdr] [-extensions section] [-reqexts section] [-utf8] [-nameopt] [-reqopt] [-subject] [-subj arg] [-batch] [-verbose] [-engine id] DESCRIPTION The req command primarily creates and processes certificate requests in PKCS#10 format. It can additionally create self signed certificates for use as root CAs for example. 以下略
Amazon Linux2にdockerをセットアップする手順
Amazon Linux2上にdocker環境を作った時のメモです。
手順
インスタンスを立ち上げて、sshログインしてからの手順です。
(1)dockerのインストール
sudo yum -y update sudo yum -y install docker
(2)dockerサービスの起動
最初の"start"でdockerサービスを開始(dockerデーモン起動)します。次のenableでOS起動時のサービス開始を有効化します。
sudo systemctl start docker.service sudo systemctl enable docker.service
(3)ec2-userユーザへの権限付与
dockerコマンドでの操作を行うには、dockerグループに所属している必要があります。usermodコマンドで、ec2-userのセカンダリグループにdockerグループを追加します。
sudo usermod -a -G docker ec2-user
ログイン中のセッションには設定が反映されないため、一度ログアウト&ログインをおこない、idコマンドで設定を確認します。
ログアウト&ログイン後に、 $ id uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user),4(adm),10(wheel),190(systemd-journal),994(docker)
(4)確認
dockerコマンドを実行して確認する。実行して、ClientとServerの両方の情報が表示されればOKです。
$ docker version Client: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.6 Git commit: 3dfb8343b139d6342acfd9975d7f1068b5b1c3d3 Built: Fri Jun 15 23:15:12 2018 OS/Arch: linux/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.6 Git commit: 7390fc6/18.03.1-ce Built: Fri Jun 15 23:17:24 2018 OS/Arch: linux/amd64 Experimental: false
- Server側のエラー
- "Cannot connect to the Docker daemon...”: dockerデーモンが起動していない可能性。(2)とNW設定を見直し
- "Got permission denied while trying to connect ":権限がない可能性。(3)を見直し
EC2でnetconsoleを使ってカーネルメッセージを取得する
1.はじめに
EC2インスタンスで、netconsoleを使ってカーネルメッセージを別サーバに飛ばして見る方法のメモです。ncatを利用しnetconsoleサーバを一時的に作る方法と、syslogサーバに飛ばす方法の、2つを説明します。*1
2.前提
3.netconsole serverを一時的に立てて取得
デバックや再現テストとかで一時的に使いたいという場合はncatコマンドで簡易的なサーバを立ててカーネルメッセージを取得することができます。以下の説明の構成概要は以下の通りです。
3.1 netconsole serverの準備
(1) security group設定
今回の例では、UDPの6666port で受診しますので、以下のようなInbound許可ルールを持つセキュリティーグループを設定して、netconsole serverインスタンスにアタッチします。
方向 | タイプ | プロトコル | ポート | ソース |
---|---|---|---|---|
Inbound | custom UDPルール | UDP | 6666 | 172.31.37.0/24 |
(2) ncatのパッケージインストール
ncatコマンドは、nmap-ncatパッケージに含まれていますので、このパッケージをインストールします。
sudo yum -y install nmap-ncat
(3) ifconfigでネット設定を確認
ncatコマンドでlistenするIPアドレスと、そのIPアドレスが割り当てられているMACアドレス(クライアントで利用)を控えます。
$ ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 172.31.37.224 netmask 255.255.240.0 broadcast 172.31.47.255 inet6 fe80::410:9aff:feda:c3d2 prefixlen 64 scopeid 0x20<link> ether 06:10:9a:da:c3:d2 txqueuelen 1000 (Ethernet) RX packets 27150 bytes 35693608 (34.0 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10838 bytes 805351 (786.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
今回の場合での必要情報は、以下になります。
3.2 netconsole client実行
カーネルメッセージを出力するサーバの設定です。netconsoleのカーネルモジュールにオプションをつけてローディングすればOKです。一時的な利用を想定して、netconsoleモジュールを手動でロードする手順にしています。
(1)クライアントのネット設定を確認
利用するIPアドレスと、そのIPアドレスが割り当てられているデバイス名を確認します。
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 172.31.37.12 netmask 255.255.240.0 broadcast 172.31.47.255 inet6 fe80::4ca:cdff:fe50:cc8 prefixlen 64 scopeid 0x20<link> ether 06:ca:cd:50:0c:c8 txqueuelen 1000 (Ethernet) RX packets 26294 bytes 35430311 (33.7 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2764 bytes 318056 (310.6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
確認する情報は以下になります。
(2)netconsoleのローディング
modprobeコマンドを利用してnetconsoleカーネルモジュールをローディングします。"netconsole="以下はこのカーネルモジュールのオプション設定です。
sudo modprobe netconsole 'netconsole=6666@172.31.37.12/eth0,6666@172.31.37.224/06:10:9a:da:c3:d2'
netconsoleオプションの書式は以下の通りです。*3
netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
4.syslog serverでカーネルメッセージを取得する
カーネルメッセージを定常的またはある程度の期間取得し続ける場合は、syslogサーバを立てて取得した方が良いかもしれません。ここではsyslogをUDPの514ポートでlistenする構成の手順を記載します。
4.1 rsyslogサーバの設定
(1)rsyslogにUDPでのListenを有効化
UDPの514番ポートでの受信ができるようにrsyslog.confを設定します。
/etc/rsyslog.conf:
# Provides UDP syslog reception $ModLoad imudp $UDPServerRun 514
(2)設定の有効化
rsyslogを再起動し、設定を有効化します。
$ sudo systemctl restart rsyslog
4.2 クライアント(netconsole)の設定
クライアント側は恒常的なメッセージ収集のため、サーバ起動時にnetconsoleが設定されるように"/etc/sysconfig/netconsole"に送信先となるsyslogサーバのIPアドレス設定を行います。
(1)送信先のsyslogサーバIPとポート設定
/etc/sysconfig/netconsole
# The IP address of the remote syslog server to send messages to SYSLOGADDR=172.31.37.224 # The listening port of the remote syslog daemon SYSLOGPORT=514
(2)netconsoleのサービスの再起動
netconsole.serviceを再起動し設定を反映させます。
またOS再起動後も反映されるようい"systemctl enable・・・"でサービスを有効化します。
$ sudo systemctl restart netconsole.service $ sudo systemctl enable netconsole.service
Linuxのテスト用に、ハングアップやパニック状態にするカーネルモジュールを作ってみた
テストのために、AWSのAmazon Linuxのインスタンスをカーネルレベルでハングアップさせたかったので、テスト用のカーネルモジュールを作ってみました。
できること
使い方
カーネルモジュールのビルド
コードはGitHubにあります。
github.com
(1) 必要なパッケージのインストール
カーネルモジュールのビルドに、gccとkernel-develが必要になります。(Amazon Linuxの場合)
sudo yum -y install git gcc kernel-devel
(2) ソースコードをローカルに持ってきます
git clone https://github.com/Noppy/HangAndPanicKernelModule.git cd HangAndPanicKernelModule/
(3)ビルドします
make ls *.ko hang_panic.ko
使い方
(1)モジュールのインストール
sudo insmod hang_panic.ko lsmod |grep hang_panic
(2)使い方の確認方法(要root権限)
cat /proc/hang_panic <<Hang&Panic module>> 'echo c > /proc/hang_panic' >>> panic 'echo h > /proc/hang_panic' >>> hang(disable local irq and preempt) 'echo H > /proc/hang_panic' >>> hang(disable only local irq)
(3)ハングアップさせる(要root権限)
(a) Local IRQ と preemptionをdisableにしてハング(おそらく、ping応答もできない)
echo h > /proc/hang_panic
(b) Local IRQのみdisable(ping応答はできるレベル)
echo H > /proc/hang_panic
(4)パニック(要root権限)
# echo c > /proc/hang_panic
#蛇足
10年前のkernel2.6時代は、ジャイアントロック(BKL:Big Kernel Lockともいう)があったので、もっと綺麗にハングさせることができたのですが、ソースコードの中を探したのですが、今はシステムワイドなロックのコードがないんですね・・。RHEL3時代に、お客さん先のシステムでBKLに苦しめられたのも、今は昔ということなんですね。
AWS CLIのセットアップ(RHEL/Mac)
2.RHELに AWS CLIをインストール
こちらを参照
上記の公式ドキュメントの内容をベースにインストールします。
(1) unzipをインストールする
$ sudo yum -y install unzip
(2) AWS CLIバンドルインストーラをダウンロードする
$ curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
(3) ダウンロードしたファイルを解凍する
$ unzip awscli-bundle.zip
(4) インストーラを実行する
$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
$ aws --version aws-cli/1.16.77 Python/2.7.14 Linux/4.14.72-73.55.amzn2.x86_64 botocore/1.12.67
4.AWS CLIの設定
4.1 管理用のPCからAWS CLIを利用する場合(アクセスキーによる認証)
設定手順は、こちらの公式ページを参照して下さい。
アクセスキーの漏洩によるアカウント乗っ取りの可能があるので、EC2インスタンス上でAWS CLIを利用する場合は、次のIAMロールをEC2に割り当てる方法の利用を検討します。*1
(1)IAMユーザのアクセスキー ID とシークレットアクセスキーの生成と取得
以下は公式ドキュメントからの抜粋です。
- IAM コンソールを開きます。
- コンソールのナビゲーションペインで、[Users] を選択します。
- IAM のユーザー名 (チェックボックスではありません) を選択します。
- [Security credentials] タブを選択し、次に [Create access key] を選択します。
- 新しいアクセスキーを表示するには、[Show] を選択します。認証情報は以下のようになります。
(2)AWS CLI設定
aws cliに、取得したアクセスキーとシークレットアクセスキー、デフォルトリージョンを設定します。
$ aws configure AWS Access Key ID [None]: XXXXXXXXXXXXX <==アクセスキーを設定 AWS Secret Access Key [None]: YYYYYYYYYYYY <==シークレットアクセスキーを設定 Default region name [None]: ap-northeast-1 <==デフォルトリージョン設定( ap-northeast-1は東京リージョン) Default output format [None]:
4.2 EC2からAWS CLIを利用する場合(EC2にIAMロール割り当て)
ユーザガイドは私にはわかりずらかったです。こちらの公式ブログの記事の方がわかりやすかったです。
(1)IAM ロールの作成
- IAM コンソールを開きます。
- コンソールのナビゲーションペインで、[Roles] を選択します。
- [Create role]を選択します。
- [AWS Service]を選び、許可したいサービスを選び[Next Permission]で進む。
- 適切なpolicyを選び[next:Review]で進む。
- Roleの名前と詳細説明を入力し、[Create Role]でロールを作成する
(2)EC2へのRole割り当て
- EC2コンソールを開きます。
- コンソールのナビゲーションペインで、[Instances] を選択します。
- [Create Instance]でインスタンス作成中に、作成したRoleを割り当てるか、
- 割り当てたいインスタン(起動中も可)を選択し、[Attach/Replace IAM Role]で作成したロールをEC2に適用
(3)AWS CLI設定
ロールを割り当てる場合は、アクセスキーとシークレットキーは設定しないようにします。
$ aws configure AWS Access Key ID [None]: AWS Secret Access Key [None]: Default region name [None]: ap-northeast-1 <==デフォルトリージョン設定( ap-northeast-1は東京リージョン) Default output format [None]:
5.その他
5.1 コマンド保管
awsコマンドを[TAB]キーで保管してくれるAWSコンプリータという機能があります。これをシェルに組み込めばコマンドを保管してくれます(pythonだからかわかりませんが、保管までに微妙な間があり使いづらいですが)。公式ドキュメントはこちらです。
(1)AWSコンプリータの場所を確認する
$ which aws_completer /usr/bin/aws_completer
(2)プロファイルに組み込む
環境に依存しますが、例えば "~/.bash_profile"の末尾に下記行を追加します。
#Enable aws command autocompletion complete -C '/usr/bin/aws_completer' aws
(3)反映
プロファイルを読み込み設定を反映させます。
source .bash_profile
MacでVisual Studio CodeをインストールしGit連携するまでの手順
1.はじめに
職場の人にコード何で書いてるか聞いたら、Visual Studio Code(以下、VS Code)を使っているという話しだったので、Macにセットアップしました。かつgit連携もしたのでメモっておきます。
2.VS Codeのセットアップ
2.1 VS Codeのインストール
MicrosoftのページのMacセットアップ手順に従ってセットアップします。→こちらのページ
- Download Visual Studio CodeからMac用のイメージをダウンロードします
- ダウンロードしたファイルをアプリケーションフォルダに移動します
- 「アプリケーション」からVC Codeを起動します
- 必要に応じ、Dockに固定します
- 起動時のアイコンを2本指でクリック(副ボタン)して
- 「オプション」→「Dock追加」
- VC Codeを起動します
2.2 コマンドラインからのVS Code設定
コマンドラインから、”code”でVS Codeが起動するように設定を追加します。
- VC Code上で「Command Palette (⇧⌘P) 」を実行する
- "shell command: "と入れて絞られた中から、"shell command: Install 'code' command in PATH"を選択して実行(下図参照)
ちなみに、これを実行すると、"/usr/local/bin/"にcodeという名前で、”/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code”へのシンボリックリンクができます。実行タイトルや公式サイトのセットアップ手順には以下のように$PATH環境変数にVS Codeのディレクトリが追加されるような記載がありますが、私はこの方法ではできませんでした。*1
Restart the terminal for the new $PATH value to take effect. You'll be able to type 'code .' in any folder to start editing files in that folder.
3.git連携
3.1 gitのセットアップ
VS Codeとgit連携の前に、gitが入っていない場合はまずgitをセットアップします。
3.2 git連携設定
- ターミナルを起動し、gitコマンドのpathを控える
$ which git /usr/local/bin/git
- VC Codeで「設定」を開き"git.path"に、gitコマンドのパスを設定する
{ "git.path" : "/usr/local/bin/git" }
4.gitを使ってみる
4.1 gitリポジトリをcloneする
ターミナルから普通に操作しても構わないですが、VC Code内で実行したい場合は「統合ターミナル」を起動し操作します。
- メニューから開く: 「表示」→「統合ターミナル」
- ショートカット: 「⌃⇧@」(Control + Shift + @)
クローンの方法は、各自調べて下さい。
4.2 VC Codeでgitリポジトリを開いて操作する
VC Code上でのgit利用方法の詳細は、Visual Studio Code で Git を 使う | 験なきものを思はずは を参考にして下さい。
ちなみに私は、VC CodeからAWS CodeCommit上のリポジトリをcloneして利用しています。AWS CodeCommitの使い方については、下記記事を参照して下さい。
nopipi.hatenablog.com
5.参考
AWS CodeCommitを使ってみた
1.AWS CodeCommitとは?
「AWS CodeCommit は、プライベート Git リポジトリをホストする、安全で高度にスケーラブルなマネージド型のソース管理サービス」です。平たく言えば、「簡単にプライベートなGitリポジトリが利用できるAWSサービス」です。
料金は、5ユーザまで、ストレージ50GB/月まで、10,000Gitリクエスト/月まで、無料で利用できます。
詳細は下記AWSのCodeCommitの概要を参照して下さい。
2.使ってみる
2.2 gitアクセス用のIAMアカウントを作ってみる
設定内容
2.3 gitコマンドでcloneしてみる
gitの初期設定
PCにgitがインストールされてない場合は、gitをインストールします。
インストール後に、gitの初期設定として、(1)メールアドレス、(2)名前、(3)push時のモードを指定して、最後に設定内容を確認します。
$ git config --global user.email "xxxxxxx@gmail.com" $ git config --global user.name "xxxxxxxx" $ git config --global push.default simple $ git config --global -l
CodeCommitと接続しリポジトリをcloneする
- AWSコンソールで、CodeCommitの対象レポジトリに移動し「クローンURL」の「https」のURLをコピーする
- コンソールを開き、下記コマンドでcloneする
- 実行するとユーザ名とパスワードを聞かれるので、控えた「HTTPS Git 認証情報」を入力する
- "git branch"はcloneされたことを示すためのコマンドで必須ではない
$ git clone <https://リポジトリcloneURL> Cloning into 'リポジトリ名... Username for 'https://git-codecommit.ap-northeast-1.amazonaws.com': XXXXXXXX <==控えたユーザ名を入力 Password for 'https://XXXXXXXX@git-codecommit.ap-northeast-1.amazonaws.com': XXXXX <== 控えたパスワードを入力 $ cd <リポジトリ名称> $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master
3.gitコマンドでの、HTTPS Git認証情報の扱いについて
cloneする時に入力したHTTPS接続ときのユーザ名とパスワードは、gitのcredential.helper機能を使い管理することができます。
管理モードは以下の5つです。*5
- 管理しない
- メモリ上に一時キャッシュする: cache
- テキストファイルで保存する: store
- (Macのみ)Macのキーチェーンを利用する: osxkeychain
- (Winのみ)Windowsの管理機能(Windows Credential Store)を利用する: wincred
プラットホームごとの確認はしてないですが、少なくともMacはosxkeychainがデフォルトのようですので、特に意識しなくても大丈夫そうです。(git公式ページのgit-osx-installerからセットアップした場合)
*1:https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up.html
*2:https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#bp-use-aws-defined-policies
*3:IAMSelfManageServiceSpecificCredentialsは、自分のIAMの認証設定の参照/変更用ポリシーのようです
*4:IAMReadOnlyAccessは、IAM情報の参照用ポリシーのようです
clock_gettime()で負荷をかけたEC2をNetflix FlameScopeでのぞいてみた
- はじめに
- セットアップ手順①(MacにFlameScopeをセットアップする)
- セットアップ手順②(測定対象のLinuxにperfをインストールする)
- テスト用にワークロードを準備する
- linuxで測定する
- FlameScopeで分析する
- FladeScopeでドリルダウン分析してみる
- (蛇足)/sysからt2.microのクロックソースを確認
- 参考
はじめに
ablogさんのこちらの記事=>Netflix のオープンソース可視化ツール FlameScope を使ってみた - ablogの二番煎じですが、FlameScopeを教えてもらったので使ってみました。
FlameScopeとは
Netflixのパフォーマンスエンジニアチームがリリースしたperfで取得した性能情報をブラウザでビジュアルに分析できるツールです。詳しくはNetflixのblogやablogの記事を参照してください。
セットアップ手順①(MacにFlameScopeをセットアップする)
基本は、GitHub - Netflix/flamescope: FlameScope is a visualization tool for exploring different time ranges as Flame Graphs.に書いてあります。
基本FrameScopeのgithubに記載されている手順でいけますが、私のMacは前提のツールが何も入っていなかったのでもう一手間かかりました。
(1)前提パッケージのセットアップ
(a)gitコマンドのセットアップ
下のqiita記事を参考にgitコマンドをインストールしてください。
gitはAppleの認証がなくインストールでエラーになる場合がありますが、その場合はエラーになった後に"システム環境設定"->"セキュリティーとプライバシー"からインストールを実行してください。
(b)pipコマンドのセットアップ
- Xcodeのセットアップ
pipのセットアップの中でccコマンドでビルドしているようなので、 ccコマンドを使えるようにするためにMacの開発環境であるXcodeを先にインストールします。
インストールは、”AppStore”から”Xcode”で検索してインストールします。
- pipインストール
こちらの記事を参考にpipをインストールします。
手順は、
(i)get-pip.pyダウンロード
https://pip.pypa.io/en/stable/installing/ から、get-pip.pyファイルをダウンロードする。
(ii)pipインストール
python get-pip.py --user export PATH="$HOME/Library/Python/2.7/bin:$PATH" echo 'export PATH="$HOME/Library/Python/2.7/bin:$PATH"' >> ~/.bash_profile
(iii)既存パッケージのアップデート
pip freeze --local | grep -v '^\-e' | cut -d = -f 1 | xargs pip install -U --user
(2)FlameScopeのセットアップ
FlameScopeのgithubにあるインストール手順をそのまま実行します。
cd ~/ git clone https://github.com/Netflix/flamescope cd flamescope pip install -r requirements.txt
セットアップ手順②(測定対象のLinuxにperfをインストールする)
今回はAWSのEC2(t2.micro)のAmazon Linux 2を利用しています。FlameScopeのgithubの説明通りです。
sudo yum install perf
テスト用にワークロードを準備する
負荷がかかってない状態で測定しても何も面白くないので、何からかの負荷がけをします。負荷は何でもよいのですが、今回は時刻取得を約3355万回(2^25)繰り返す負荷ツールを作りました。clock_gettime()を呼び出す部分をアセンブラで書いていますが、ただの趣味です。こんなキモいことしなくても大丈夫です。
/* work.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #define MAX 1LU << 25 /* MAX = 2^25 */ int main() { struct timespec tp; char *mes; int ret; unsigned long loop; for(loop=0; loop<MAX; loop++){ asm volatile( /* call clocl_gettime api */ "mov %3, %%rsi;" "mov %2, %%rdi;" "mov %1, %%rax;" "syscall;" "mov %%eax, %%eax;" "mov %%eax, %0;" : "=&m"(ret) : "g"(SYS_clock_gettime), "g"(CLOCK_REALTIME), "g"(&tp) : ); } printf("execute %lu times.\n",loop); return ret; }
このコードをgccでビルドします。(デフォルトではgccが入っていないのでyumでインストールしてビルドします)
sudo yum install gcc gcc work.c
linuxで測定する
負荷は、作成したツールを4回、10秒のインターバルを置いて実行するようにしました。
sleep 15;for i in `seq 1 4`;do time ./a.out;sleep 10;done
それと同時に、別のターミナルでperfでプロファイルの収集を開始します(秒間49回で、120秒間測定)。
sudo perf record -F 49 -a -g -- sleep 120
- -F: 49:秒間49回データ収集を行う。(秒間50回が分析にちょうど良い分解能度で、でも50回だと周期性が出てハマってしまう(lock-step)可能性があるから、周期性が出ないよう半端な49回にするということ?)
- -a: 全てのCPUコアを対象にシステムワイドでプロフィル収集する
- -g: call-graphを有効化する
perfのデータ収集が完了したらレポートを出力しgzip圧縮する
sudo perf script -f --header > stacks.log gzip stacks.log
FlameScopeで分析する
- 収集し、圧縮したstacks.log.gzファイルをMacに転送します。
- 転送したstacks.log.gzを”flamescope/example”に移動します*1
- FlameScopeを起動します。
cd ~/flamescope python run.py
- ブラウザで、http://127.0.0.1:5000/にアクセスする。
- stack.log.gzを選択する
- 右上のRawsから、perfの分解能度に合わせた行数を選択する(49を選択)
すると、こんな感じで見えます。
赤いところがイベントが多数出ている→負荷がかかっている、場所になります。
FladeScopeでドリルダウン分析してみる
上記の赤い濃淡の図の中から気になる部分があったら、その部分をFlameGraphでドリルダウン分析します。例えば最初の赤い時間帯の開始タイミングと終了タイミングで2回クリックするとその範囲が選択され、下記のFlameGraphで詳細分析することができます。
t2.microのクロックソース
上記の図を見ると、
- a.outのmain()から、システムコール呼び出し(entry_SYSCALL_64_after_hwframe)からdo_syscall_64()が呼ばれ、
- そこから、sys_clock_gettime()が呼ばれて以下実行され、
- 最終的に、pvclock_clocksource_read()が呼び出されているのがわかります。
なおpvclock_clocksource_read()のコードがどこにあるかカーネルを別途見ると、arch/x86/kernel/pvclock.cにあるようです*2。pvclockという名前からparavirtualのclocksourceであることが推測でき、コードタイトルにも”paravirtual clock -- common code used by kvm/xen”とあるので、t2.microのクロックソースコードはパラバーチャルであることが裏付けられます。( ちなみに、lvm/xenで共通化されているんですね)
処理負荷が高いポイント
clock_gettimeを回して負荷をかけているので当然、処理時間の大半がシステムコール処理にリソースを費やされています。実際FlameGraphを見るとdo_syscall_64が処理時間全体の7割弱を占めているのがわかります。ただ、実際に時刻取得処理を行うdo_clock_gettime()は、do_syscall_64()の半分の処理時間割合であることから、システムコールによるユーザ空間からカーネル空間へのコンテキストスイッチは相当のオーバーヘッドがあることが伺えます。
(蛇足)/sysからt2.microのクロックソースを確認
蛇足ですが最後に、実機の/sysからカレントのクロックソースを確認します。
- (1)利用可能なソースコード一覧
kernel的には、xen, tsc, hpet, acpi_pmの4つが選択可能に見えます。
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource xen tsc hpet acpi_pm
- (2)カレントのクロックソース
xenですね。
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource xen
参考
- FlameScope
- perf
- gitコマンド
- pipコマンド
subprocessでコマンド実行し、例外処理でOSErrorとコマンドのリターンコードの非ゼロ(エラー)をそれぞれハンドルするサンプル
サンプルコード
私自身の勉強用です。コードは、python2.7ベースです。
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import subprocess def call_subprocess(cmd): try: r = subprocess.check_output( cmd, stderr=subprocess.STDOUT ) sys.stdout.write(r+'\n') except OSError as e: sys.stderr.write('file ='+str(e.filename)+'\n') sys.stderr.write('errno='+str(e.errno)+'\n') sys.stderr.write('error='+e.strerror+'\n') return(1) except subprocess.CalledProcessError as e: sys.stderr.write('ret='+str(e.returncode)+'\n') sys.stderr.write('cmd='+str(e.cmd)+'\n') sys.stderr.write('output='+e.output+'\n') return(1) return(0) #test1 a command is success print('<<<<<test1>>>> a command is success.' ) cmd = [ 'echo','hoge hoge' ] ret = call_subprocess(cmd) print('function return = '+str(ret) ) #test2 command is failed print('<<<<<test2>>>> a command is failed.' ) cmd = [ 'cat','hoge hoge' ] ret = call_subprocess(cmd) print('function return = '+str(ret) ) #test3 OS error print('<<<<<test3>>>> OS Error.' ) cmd = [ 'NonexistingFile','hoge hoge' ] ret = call_subprocess(cmd) print('function return = '+str(ret) )
プロセスのVSZ,RSSとfree,meminfoの関係を実機で確認する
1.はじめに
1-1.この記事の要旨
psコマンドのVSZ(仮想メモリ)、RSS(物理メモリ)の挙動について質問を受けたので、簡単な検証プログラムを作ってmalloc/freeのメモリ確保/解放や、データの読み込み・書き込みとVSZ/RSSの関係性及び、freeコマンドとmeminfo情報でシステムワイドなメモリの挙動について確認しました。
検証結果から以下の挙動を実機で確認しました。
1-2.(予習)メモリに関する指標とlinuxのメモリ挙動について
この記事に出てくる、メモリに関する指標値の説明です。
- psコマンド
- freeコマンド
- meminfo
- MemFree : 未使用のメモリ量です。freeコマンドの"free値"の元になっています。
- AnonymousPage(無名ページ): swapアウト対象となるページ。ざっくりというとプロセスが普通使っているメモリ。
- FilePage(ファイルページ):メモリ不足時、解放されるページ。ざっくりいうとファイルキャッシュのメモリ。
- active: AnonymousPage/FilePageのうち、比較的最近アクセス(read or write)されたページ。swapアウトや解放されないページではない。*3
- inactive:AnonymousPage/FilePageのうち、しばらくアクセスされていないページ
2.検証環境と検証方法
2-2.検証方法
下記動作を行う検証プログラムを作成し実行します。なお検証をわかりやすくするために、動作の間に10秒のsleepをかけています。
- malloc()で512MBのメモリを確保する
- 確保した512MBのメモリ領域を20秒かけてreadする(1秒間隔で1/20のサイズずつ20回readする)
- 同じ領域に、20秒かけてデータを書き込む
- もう一度同じ領域を、20秒かけて読み込む
- free()で確保したメモリを解放する
プログラムは以下のリンク先にありますので、これをgccで"gcc verifying_memory.c"とかしてビルドします。
Verifying on Memory behavior · GitHub
ちなみにglibcのmalloc()は、サイズが128kb(変更可)より大きい場合はmmap()取得になります。*4
ですので512MB確保している今回の検証は、必ずmmap()になります。
2-3.測定方法
(1)psコマンドによるVSZ,RSS情報の取得
簡単ですが、下記のワンライナーコマンドで1秒間隔で取得しました。(検証プログラムのファイル名は、"a.out")
echo -n 'DATE '; ps -aux|head -n 1;while true;do printf '%s ' "`env LANG=C date '+%X.%N'`";env LANG=C ps -aux|grep -e a.out|grep -v grep;sleep 1;done
(2)freeコマンドとmeminfo情報の取得
こちらの記事のシェルで取得しました。
freeコマンドとmeminfoを取得してCSV形式で保存するシェルスクリプト - のぴぴのメモ
3.結果
3-1.全体の結果
psコマンドのVSZ/RSS、freeコマンド、meminfoの推移を並べると以下のようになります。
3-2.プロセスのVSZ/RSS挙動
ポイント① malloc()した時の挙動→VSZのみ増加
malloc()で512MBを取得したタイミングで、VSZが増加しているのがわかると思います。
一方RSSは、malloc()しただけでは増えてなく、このタイミングでは物理メモリの割り当てが発生していないことがわかります。(次のfreeコマンドやmeminfoの結果と照らし合わせて見るとよりわかりやすいです。)
ポイント② 1回目のデータread時→RSSは増えない
malloc()した後、そのデータをreadしても、物理メモリの割り当ては発生しません。そのため、RSSも増加しません。
実はプロセスがカーネルからメモリ確保したタイミングでは、確保した仮想メモリのページは、”zero page”という1ページがすべて0データで埋められた特殊なページにマッピングされており、データをreadした時は、その"zero page"の値を参照するためです。*5
ポイント③ データwrite→RSSが増加する
データのwriteが発生した時点で、RSSが増加していることがわかります。つまりデータへのwriteのタイミングで、そのwriteした仮想メモリのページに、晴れて物理メモリが割り当てられたということです。
これはカーネルのCOW(Copy-On-Write)という手法で、mallocなどでメモリを確保したタイミングでは実際には物理メモリの割り当てはせず、必要になったタイミングで初めて物理メモリを割り当ているからです。*6
もう少し詳しく書くと、先ほど説明したzero pageは書き込み禁止設定がされており、そこにデータを書こうとするとページフォルトが発生します。ページフォルトが発生すると、ページフォルトの割り込み処理の中にあるCOWの実装で、zero pageの内容を新しいページにコピーしして、そのページを改めて仮想ページとマッピングします。その時にRSSが+1ページ加算されます。
3-3.システムワイドな挙動(freeコマンド/meminfo)
ポイント① malloc()した時の挙動→usedもAnonymousPageも増えない
malloc()した時には、RSSが増えてない、つまり物理メモリへのマッピングがされていないため、freeコマンドやmeminfoでシステムワイドで見た物理メモリの利用状況でも、変化はありません。
ポイント②1回目のデータread時→変化しない。
同じですね。物理メモリの割り当てが発生しないため、freeコマンドやmeminfoも変化はありません。
ポイント③ データwrite→used上昇、AnonymousPage上昇
Writeするタイミングで、物理メモリの割り当てが発生するため、Usedが上昇します。meminfoでさらに詳しく見るとその上昇しているところが、AnonymousPage(無名ページ)であることが確認できます。
4.参考ページ
*1:kernelは、”MemTotal - MemFree - AnonymousePage(Active/InActive) - FilePage(Active/InActive) - Unevictable+Mlocked”で算出しています。
*2:"total - free - buffers - cache"で計算された値になります。RHEL6まではbuffer/cacheを含んでいましたが、RHEL7からはbuffer/cacheが含まれない値になりました。
*3:/proc/meminfo の Inactive は利用可能なメモリ領域ではない - ablog
*4:glibcのmalloc()は、指定するメモリサイズが小さい場合(閾値はMMAP_THRESHOLDで設定されており、デフォルトは128kb)は、Java vmのヒープのように、カーネルから取得済みで未使用となったメモリ領域をglibc内で保持して再利用する実装があるため、malloc()により必ずしもVSZが増加するとは限りません。詳しくはmalloc()のmanの注意(NOTES)を参照。
*5:memory management - Linux kernel: Role of zero page allocation at paging_init time - Stack Overflow
Linuxのclock_gettime()でナノ秒の時刻取得をするCのサンプル
1.はじめに
clock_gettime()で時刻を取得し時刻をナノ秒で表示するサンプルです。時刻取得といえばgettimeofday()ですが、POSIXではgettimeofdayは廃止予定で、clock_gettime()の利用を推奨しているので、こちらを利用しています。
2.コードと実行例
(1)コード
#include <stdio.h> #include <time.h> int main(){ struct timespec ts; struct tm tm; // 時刻の取得 clock_gettime(CLOCK_REALTIME, &ts); //時刻の取得 localtime_r( &ts.tv_sec, &tm); //取得時刻をローカル時間に変換 // 出力 printf("tv_sec=%ld tv_nsec=%ld\n",ts.tv_sec,ts.tv_nsec); printf("%d/%02d/%02d %02d:%02d:%02d.%09ld\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); return(0); }
3.説明
(1)clock_gettime(CLOCK_REALTIME, &ts)
時刻を取得します。最初の引数は取得する時刻の種類を指定しており、"CLOCK_REALTIME"はシステム全体で一意な精度の高い実時間情報を取得します。取得した情報は、2つ目の引数のtimespec構造体に格納されます。
struct timespec { time_t tv_sec; ==> 1970年1月1日からの秒数が格納されます。 long tv_nsec; ==> 秒未満の時刻(ナノ秒)で格納されます。 };
(2)localtime_r( &ts.tv_sec, &tm)
取得した時刻のうち、tv_sec(1970年1月1日からの秒数)を年/月/日/時/分/秒に変換し、tm構造体に格納します。
localtime_r()は、localtime()と同じ処理になりますが、localtime()はスレッドセーフでなく、localtime_r()はスレッドセーフな実装になります。スレッドセーフでないlocaltime()はマルチスレッド環境で利用すると想定外の挙動を起こす可能性があるため、今回のサンプルではマルチスレッドでも使えるように、スレッドセーフなlocaltime_r()を利用しています。
(3)printfで出力
localtime_rで変換した時刻情報を、printf()で出力します。秒未満は、timespecのtv_nsecに格納されているので、tv_nsecを使って秒の小数点以下の値を出力しています。