- 投稿日:2020-07-13T23:10:42+09:00
DockerCompose で Gatsby環境構築
やりたいこと
Gatsbyの環境を作りたい
Dockerで...!作り方
環境
Mac
Docker for Mac
作業フォルダに
docker-compose.yml
とDockerfile
を作成しますDockerfileの中身を下記のようにします
title=DockerfileFROM node:12.14.1 WORKDIR /home/node/app RUN npm install -g gatsby-cli EXPOSE 8000nodeのLTSのイメージを使って、gatsby cliをインストールしているだけです
alpine使ってないのは私がコマンドをまだ理解できていないからです...
EXPOSEはコンテナのポートを公開しているだけなので、このままではアクセスはできませんが
次で解決するので進めます3. docker-compose.ymlを下記のようにします
title=docker-compose.ymlversion: "3.7" services: gatsby: build: context: . dockerfile: Dockerfile container_name: gatsby ports: - "8000:8000" volumes: - .:/home/node/app environment: - NODE_ENV=development stdin_open: true解説としては
build: context: . dockerfile: DockerfileカレントディレクトリのDockerfileをビルドします
container_name: gatsby
コンテナ名をgatsbyにしますports: - "8000:8000"上記で、ホスト(Mac)とゲスト(docker)のポートをマッピング(関連付け)しています
volumes: - .:/home/node/appvolumes - volume(データの永続化領域)の定義
volume とは、コンテナのライフサイクルが終了した後でもデータを保管しておけるデータ領域です。
ホスト側のディレクトリを volume としてコンテナ内にマウントできる。
本機能はホストとコンテナ間でファイルを受け渡すときに利用できる。
参考:オブジェクトの広場
stdin_open
はコンテナの標準入力をオンにします
getsby new 〇〇とかするのでオンにしております4. ビルドしましょう!
docker-composer up -d --build
(ビルドとデーモンでの立ち上げ)
2回目以降はdocker-compose up -d
です5.
docker exec -it gatsby bash
でコンテナ内に入れます6. コンテナに入ったら、
gatsby new blog(別の名前でも可)
を入力し、gatsbyのサイトを立ち上げます
別のテーマ使う場合は公式参考に
https://www.gatsbyjs.org/docs/themes/7. gatsby developでビルドして、localhostを確認して、表示されていれば成功
お疲れさまでした
なお、おそすぎて使い物にならなかった...
ブログ始めました
- 投稿日:2020-07-13T22:54:58+09:00
Docker導入時のエラー(You must use Bundler 2 or greater with this lockfile.)
はじめに
dockerを既存のアプリに導入された際に発生したエラーとその解決法を今回はまとめさせていただきます。
エラーの内容
docker file を作成docker-compose.ymlを作成した後にdocker-compose build コマンドをするとなぜか
You must use Bundler 2 or greater with this lockfile.という内容のエラーが発生しました。
解決法
ネットの記事を参考にしたところ
RUN gem install bundler RUN bundle installとRUN bundle installの前に RUN gem install bundler を記述すれば治ると買いてありましたが、既に私は記述していました。
調べていくうちにruby2.5.1の場合はこのようなバグが発生するといった記事を見つけ rubyのversionを2.7.1にdockerfile ローカル環境変更した結果今回のエラーは解決しました。
参考記事
ruby version変更https://qiita.com/_kanacan_/items/c1499f6c13b1c41da982
- 投稿日:2020-07-13T22:42:37+09:00
Kubernetes minikube エラー
- 投稿日:2020-07-13T22:15:17+09:00
Dockerについて(アウトプット)
Dockerについての理解を深めるためアウトプットとしてこの記事を残します。
Dockerとは
Docker社が提供する「コンテナ型仮想化技術」 を実現するプロダクト
仮想化とは何か
PCやサーバといったマシンにインストールされているOS(ホストOS)の上に、別のマシンを仮想的に立ち上げる事。
つまり「パソコンの中に仮想パソコンを起動する」というのが仮想化。
インターネットの普及を支えているのは、Webサーバーをはじめとした各種サーバーという機械のおかげ。ただ、世の中のWebサイトの爆発的な増加にともなって機会も同じように増加していったらサーバーの置き場所、費用、管理が膨大になる。そこで1台のサーバー上に、複数のサーバーとして利用できる仕組みを構築する方法として生まれたのが仮想化という技術。数ある仮想化技術でDockerを使うメリット
まず他の仮装技術と比べると、パソコンに負荷を与えない軽量設計になっている。また環境構築における差異の最小化を図ることができる。例えば、設定を変えたりした場合、全員のDockerfileを書き換えてビルドするのではなく、設定が変わったコンテナをイメージ化して配布した方が手作業も減るし、環境差異を減らすことにもつながる。
よく使うDockerコマンド
イメージ一覧
docker images
コンテナの稼働状況確認
docker ps
docker-compose
up
build
とstart
の違いbuild コマンドではimageを構築します。コンテナは作成しない
upコマンドでは、キャッシュがある場合はそれを使って一発でイメージの構築から、コンテナの構築・起動までできる。imageがなくてもbuildから実行できる。
startコマンドでは既存のコンテナを起動することができる。
- 投稿日:2020-07-13T21:13:50+09:00
MacでCentOSをさわりたい(docker)
Macも使ってあげたい
Mac book買ってもらったのにAmazonPrimeしか見てないんです、、、
これではあまりにもかわいそうなのでそろそろ別の使い方をしよう!!と思い、この記事を書くことにしました。
でも、あんまり容量減らしたくないので今回はdockerを使ってCentOSの環境を作ります、!!そもそもdockerってなによ
dockerはですね、簡単に言うとコンテナ技術ですね。
これを使うと一瞬で自分のパソコンの中にもう一台別のパソコン的なものを作ることができちゃうんです!
仮想化とは異なり、ホストOSと同じカーネルを使うことで容量の大量消費を防いでくれます。
dockerについては今度ちゃんとまとめるつもりなので気になる方はぺぺっと検索してみてください!(私のちゃんとまとめるつもりシリーズ大量にあるね☺️)さぁCentOS7の環境を構築しよう!
[前提]
ホストOS : macOS Catalina 10.15.5
構築したいOS : CentOS7[ディレクトリ構成] -- docker/ ∟ example/ ∟ Dockerfileexampleの中で作業していきます。
Dockerfileは後で自分で書きます。それではCentOS7を構築してみましょう!
DockerHub
ここからMacで使用できるdockerをインストールしてください。
インストールが終わったらFinderからダウンロードしたDocker.dmgをポチッと。
そうするとdrag and dropの指示がでるので指示通りに。
(ダウンロードアイコンのところからこの作業しようとしたらなんかできなかった、なんで、、、)
はい!これでLaunchpadにdockerが追加されました!
docker(クジラのアイコン?)をクリックすると右側にターミナルが開かれるので、左側の指示にしたがって進んでください。
これでターミナルにdockerが入りました!
試しに新しくターミナルを開いてdockerとコマンドを入力してみてください。動きますね?
次に
docker run centos:7と入力してください。これでローカル環境にcentos7のimageが入りました。
docker imagesこれで確認できます。
どこかにcentosって文字が見つかるはず!笑はい!それではDockerfileも書いてみましょう。
FROM centos:7 LABEL maintainer="miyabi <miyabi@example.com>" RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ && yum -y install python3 CMD ["/usr/bin/bash"]LABELのメールアドレスとか名前はテキトーでも動きます!
これは自分的なCentOSの環境のレシピのようなものなのでもっと使いやすくしたいところ。
そのままexampleディレクトリにステイしたままで次のコマンドを実行してください。
docker build -t miyabi .miyabiのところはimage名なので好きなのをつけて大丈夫です。
ヤッタァ!!これで自分のimageが完成です。docker imagesほら!miyabi(自分でつけたimage名)ができてるでしょ!!
ついにコンテナ作るよ!!!docker run -it miyabimiyabiのところは先ほど自分がつけたimage名に変えてくださいね。
わぁ!これで自分で構築した環境に入り込めてますね!!
試しにpython3と入力してみてください。
動くでしょ??
Dockerfileにpython3をインストールしてくれるように記入したので入っているんです!
今回はCMDのところにrunしたらコンテナ内に入り込むように書いたのでrunした時点でそのまま動かすことが可能ですが、もっと色々アレンジできますよん。注意
ctrl + Dで環境から抜け出すことができますが、セーブしないと次回コンテナ内に入り込んだ時にまっさらな状態になっているので気をつけてくださいね!
おわりに
dockerって便利!慣れてくるとDockerfileささっとかけばたくさんの簡易的な環境をすぐに作り上げることができますね〜
勉強不足なのでまだこれといった恩恵はまだ受けていませんが、絶対すごいじゃん!!って感じですね〜!!!
すぐたてられるし、すぐ壊せるし!!
今回のだとDockerfileの内容がペラペラすぎてpython3しか使えないけど、macでcentOS使ってると思うとなんか嬉しい☺️
間違っていたらどんどん突っ込んでください!!
最後まで読んでいただきありがとうございます。
- 投稿日:2020-07-13T20:28:42+09:00
docker-compose.yml を環境ごとに分割する
前提
Compose ファイルフォーマットバーション 3.x
結論
-f
オプションを使う$ docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -dまたは環境変数を使う:
COMPOSE_FILE
,COMPOSE_PATH_SEPARATOR
$ export COMPOSE_PATH_SEPARATOR=: $ export COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml $ docker-compose up -dファイル間、プロジェクト間での Compose 設定の共有 — Docker-docs-ja 17.06 ドキュメント
注意
extends
キーワードは 2.1 までのサポートで、(2020年7月現在)ではご利用いただけません?♀️COMPOSE_FILE
の区切り文字はOSによって異なるのでCOMPOSE_PATH_SEPARATOR
を明示的に指定しておくと無難-f
オプションではなく環境変数を使う場合は他プロジェクトに影響するかもしれないのでそこは注意Makefile と合わせて使う
簡単な例としてベースとなるファイルと、環境用のファイルをそれぞれ用意した。
docker-compose.ymlversion: "3" volumes: app-node_modules: app-dist: services: app: build: ./app container_name: app volumes: - ./app/src:/usr/app - app-node_modules:/usr/app/node_modules - app-dist:/usr/app/distdocker-compose.prod.ymlversion: "3" services: app: ports: - 80:80 environment: PRODUCTION: 'true'docker-compose.local.ymlversion: "3" services: app: ports: - 8080:80 environment: DEBUG: 'true'これらを環境によって切り替えるための Makefile を作る
ここでは環境変数を使って docker-compose の上書きを想定したMakefile# 本番では make [cmd] e=prod とする # local or prod ENV=local pre: ifdef e ENV=${e} endif set-env := export ENV=$(ENV) ;\ export COMPOSE_PATH_SEPARATOR=: ;\ export COMPOSE_FILE=docker-compose.yml:docker-compose.$(ENV).yml up: pre $(set-env)\ docker-compose up -d down: pre $(set-env)\ docker-compose down rebuild: pre $(set-env)\ docker-compose build --no-cache上記の設定で環境ごとにコマンドで切り替えられるようになった?
# ローカル開発 $ make rebuild $ make up $ make down # 本番環境 $ make rebuild e=prod $ make up e=prod $ make down e=prodこれは簡単な例だが、実際にはより多くの設定になるため、多くの冗長な設定から解放されて幸せになれる❤️
Makefile がないとメンテナンス性も開発体験も落ちるので、嫌がらずに開発序盤から用意しよう?結合時の挙動について
1つの値を持つオプションか、複数の値を持つオプションかで挙動が変わってくる
以下公式より引用: 設定の追加と上書き — Docker-docs-ja 17.06 ドキュメント1 つの値しか持たないオプション、たとえば image、command、mem_limit のようなものは、古い値が新しい値に置き換えられます。
# docker-compose.yml command: python app.py # docker-compose.prod.yml command: python otherapp.py # docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d command: python otherapp.py # 後に書いた方が適応される複数の値を持つオプション、つまり ports、 expose、 external_links、 dns、 dns_search、 tmpfs では、両者の設定をつなぎ合わせます。
# docker-compose.yml expose: - "3000" # docker-compose.prod.yml expose: - "4000" - "5000" # docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d expose: - "3000" - "4000" - "5000" # 両者適応されるenvironment、 labels、 volumes、 devices の場合、Compose は設定内容を "マージ" して、ローカル定義の値が優先するようにします。
# docker-compose.yml environment: - FOO=original - BAR=original # docker-compose.prod.yml environment: - BAR=local - BAZ=local # docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d environment: - FOO=original - BAR=local - BAZ=local # 競合してたら後に書いた方が優先され、競合してないものは適応される? Compose ファイルのご利用は計画的に?
(ブログに書いた内容そのまま転載した)
- 投稿日:2020-07-13T18:43:09+09:00
PodmanのVolumeを使ってMySQLを永続化する
はじめに
前回の記事 に引き続いて、Docker の代わりに Podman を使うためにどのような差分があるのかを見ていく。
Podman Volume
MySQLなどをサーバー上で利用する場合、ボリュームを永続化しておく必要がよい (そうしないと、コンテナの削除などでデータが消えてしまうなど問題が出る)。 この時、Docker では
docker volume
で永続化したボリュームを作成してこれを利用する。
Podman の場合も同様にpodman volume
コマンドがあるので、これを利用してボリュームを作成できる。 この時は Podman で用意されたと考えられるデフォルトディレクトリがマウントされる。Mountpointを参照# rootless の場合 $ podman volume create sample sample $ podman volume inspect sample [ { "Name": "sample", "Driver": "local", "Mountpoint": "/home/centos/.local/share/containers/storage/volumes/sample/_data", "CreatedAt": "2020-07-13T17:41:12.27273779+09:00", "Labels": { }, "Scope": "local", "Options": { }, "UID": 0, "GID": 0, "Anonymous": false } ] # rootful な場合 $ sudo podman volume create sudo-sample sudo-sample $ sudo podman volume inspect sudo-sample [ { "Name": "sudo-sample", "Driver": "local", "Mountpoint": "/var/lib/containers/storage/volumes/sudo-sample/_data", "CreatedAt": "2020-07-13T17:41:58.770498487+09:00", "Labels": { }, "Scope": "local", "Options": { }, "UID": 0, "GID": 0, "Anonymous": false } ]特定パスをマウントしたボリュームの作成
個人的には特定のパス (例えば、
/opt/mount/mysql
)をマウントポイントにしたい、といったケースがある。
この場合、Docker では 例えばこちらの記事にあるように docker-compose.yml に以下のような記述を書いて volume をマウントしていた。特定ディレクトリをマウントしたVolumeを作成するためのdocker-compose.ymlの一部volumes: datastore: driver_opts: type: none device: /opt/mount/mysql o: bindこれは同様の方法の内容を
podman
コマンドで実現して、ボリュームを作成できる。$ podman volume create -o type=none -o device=/opt/mount/mysql -o o=bind datastoreただ、この方法で作成したボリュームは rootful でなければマウントできないようだ。
そのため、もしこの方法を使うというのであれば rootful なコンテナに対して利用する。$ podman run --rm -v datastore:/tmp2 nginx touch /tmp2/hoge Error: error mounting volume datastore for container 549045ccdd392987427598764ffc0b533fdb5d6a6b31bd4172aa28282bc15e2d: cannot mount volumes without root privileges: operation requires root privileges……のだが、ルートで実行するとどうなるか。 以下の通り Permission denied ではじかれてしまう。 ディレクトリパーミッションが
777
であるにも関わらず、である。# パーミッションは問題ない $ ls -l /opt/mount/ total 0 drwxrwxrwx. 2 root root 6 Jul 13 18:00 mysql $ sudo podman run --rm -v datastore:/tmp2 nginx touch /tmp2/hoge touch: cannot touch '/tmp2/hoge': Permission deniedマウントボリュームが
Permission denied
今回の検証環境が RedHat 系ということで、心当たりがある人がいるかもしれないが、結論から言えば原因は SELinux である。 今回の検証環境では
sestatus = enabled
の状態で検証している。 公式のトラブルシュートで2番目に述べられている通り、非常に多くの人が引っかかる問題のようだ。 紹介されている解消法は2つ。
-v
の第三引数として:z
あるいは:Z
オプションをつける
- Docker にも存在するオプションで、SELinux用のラベルの付け替えを行う
--security-opt label=disable
を指定することでコンテナに対するセキュリティラベリングを無効化する
- こちらも同様にDockerにも存在するオプション
今回は後者のオプションをつけて、SELinuxのセキュリティラベリングを無効化して動作させる。 これでマウントができていることが確認できた。
指定したマウントディレクトリに結果が出ていることを確認$ sudo podman run --security-opt label=disable --rm -v datastore:/tmp2 nginx touch /tmp2/hoge $ ls -l /opt/mount/mysql/ total 0 -rw-r--r--. 1 root root 0 Jul 13 18:28 hogeMySQL の永続化
ということで本題。 一度
sudo podman rm -af && sudo podman volume rm -af
を実施して、一通り削除してから開始します。# ボリューム datastore を作成 $ sudo podman volume create -o type=none -o device=/opt/mount/mysql -o o=bind datastore datastore # マウントしてMySQLを起動 $ sudo podman run --security-opt label=disable -d -v datastore:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=sample mysql a40dc20b6246411acc57bb0c7b19825579f95acfe25296685009f7c89e1a9c18 # MySQL DBに接続してデータベースを作成 (内容を変化させる) $ sudo podman exec -it a40 mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.20 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> create database hogehoge; Query OK, 1 row affected (0.01 sec) mysql> show databases; +--------------------+ | Database | +--------------------+ | hogehoge | | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) mysql> mysql> exit; Bye # マウントしたホストのディレクトリにデータが書き込まれていることを確認 $ ls -l /opt/mount/mysql/ total 186776 -rw-r-----. 1 systemd-coredump input 56 Jul 13 18:34 auto.cnf -rw-r-----. 1 systemd-coredump input 3104223 Jul 13 18:34 binlog.000001 -rw-r-----. 1 systemd-coredump input 353 Jul 13 18:34 binlog.000002 -rw-r-----. 1 systemd-coredump input 32 Jul 13 18:34 binlog.index -rw-------. 1 systemd-coredump input 1680 Jul 13 18:34 ca-key.pem -rw-r--r--. 1 systemd-coredump input 1112 Jul 13 18:34 ca.pem -rw-r--r--. 1 systemd-coredump input 1112 Jul 13 18:34 client-cert.pem -rw-------. 1 systemd-coredump input 1676 Jul 13 18:34 client-key.pem drwxr-x---. 2 systemd-coredump input 6 Jul 13 18:34 hogehoge -rw-r-----. 1 systemd-coredump input 196608 Jul 13 18:34 '#ib_16384_0.dblwr' -rw-r-----. 1 systemd-coredump input 8585216 Jul 13 18:34 '#ib_16384_1.dblwr' -rw-r-----. 1 systemd-coredump input 5642 Jul 13 18:34 ib_buffer_pool -rw-r-----. 1 systemd-coredump input 12582912 Jul 13 18:34 ibdata1 -rw-r-----. 1 systemd-coredump input 50331648 Jul 13 18:34 ib_logfile0 -rw-r-----. 1 systemd-coredump input 50331648 Jul 13 18:34 ib_logfile1 -rw-r-----. 1 systemd-coredump input 12582912 Jul 13 18:34 ibtmp1 drwxr-x---. 2 systemd-coredump input 187 Jul 13 18:34 '#innodb_temp' drwxr-x---. 2 systemd-coredump input 143 Jul 13 18:34 mysql -rw-r-----. 1 systemd-coredump input 30408704 Jul 13 18:34 mysql.ibd drwxr-x---. 2 systemd-coredump input 8192 Jul 13 18:34 performance_schema -rw-------. 1 systemd-coredump input 1680 Jul 13 18:34 private_key.pem -rw-r--r--. 1 systemd-coredump input 452 Jul 13 18:34 public_key.pem -rw-r--r--. 1 systemd-coredump input 1112 Jul 13 18:34 server-cert.pem -rw-------. 1 systemd-coredump input 1680 Jul 13 18:34 server-key.pem drwxr-x---. 2 systemd-coredump input 28 Jul 13 18:34 sys -rw-r-----. 1 systemd-coredump input 10485760 Jul 13 18:34 undo_001 -rw-r-----. 1 systemd-coredump input 12582912 Jul 13 18:34 undo_002上で作ったコンテナ・ボリュームをわざと削除して、データが削除されていないかを確認します。
# Podman上のコンテナとボリュームを全削除 $ sudo podman rm -af a40dc20b6246411acc57bb0c7b19825579f95acfe25296685009f7c89e1a9c18 $ sudo podman volume rm -af datastore # 再度ボリュームをマウントし、MySQLコンテナを生成 $ sudo podman volume create -o type=none -o device=/opt/mount/mysql -o o=bind datastore datastore # -- NOTE: ここで既に初期化済の MySQL データをバインドしているのでパスワードは不要 $ sudo podman run --security-opt label=disable -d -v datastore:/var/lib/mysql mysql a5a2dc5121ce3bacc805d2f3a227af4f43edb48c083a4ca8c381029f38cc1c5e # 消去したPodmanのボリューム内に変更されたデータがローカル側にバインドされていることを確認 # (Database: hogehoge が存在する) $ sudo podman exec -it a5a mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.20 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; +--------------------+ | Database | +--------------------+ | hogehoge | | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.01 sec)ということで永続化ができていることが確認できます。
まとめ
この部分はほとんど Docker と同じでした。
- 投稿日:2020-07-13T18:05:21+09:00
NVIDIA Container ToolkitをインストールしてDockerコンテナでGPUを使用する
Dockerコンテナからホスト上のGPUを使用するためにNVIDIA Container Toolkitの導入手順を記載します。今回GPUのサーバーとしてはIBM Cloudの仮想サーバーを使用します。OSはCentOS 7.7、GPUはP100 1個のサーバーになります。
1. NVIDIAドライバーのインストール
以下リンク先から適切なドライバーをダウンロードしてインストールします。
https://www.nvidia.co.jp/Download/index.aspx?lang=jp$ sudo yum install gcc -y $ sudo yum install kernel-devel -y $ wget http://jp.download.nvidia.com/tesla/450.51.05/NVIDIA-Linux-x86_64-450.51.05.run $ sudo sh cuda_10.2.89_440.33.01_linux.run --kernel-source-path=/usr/src/kernels/3.10.0-1127.13.1.el7.x86_64インストールが成功すると nvidia-smi コマンドで GPU の情報が表示されます。
$ nvidia-smi Mon Jul 13 03:14:11 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 450.51.05 Driver Version: 450.51.05 CUDA Version: 11.0 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 Tesla P100-PCIE... Off | 00000000:00:07.0 Off | 0 | | N/A 32C P0 29W / 250W | 0MiB / 16280MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+2. Dockerのインストール
公式ガイドに記載のとおり、Dockerをインストールします。
古いバージョンがインストールされていたらアンインストール
$ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engineリポジトリのセットアップ
$ sudo yum install -y yum-utils $ sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repoDockerのインストール
$ sudo yum install docker-ce docker-ce-cli containerd.io一般ユーザーに権限を付与しておく
sudo usermod -aG docker $USER3. NVIDIA Container Toolkitのインストール
Quickstartに記載のとおりインストール
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) $ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo $ sudo yum install -y nvidia-container-toolkit $ sudo systemctl restart docker以下のように docker コマンドで nvidia-smi の結果が返ってきたら成功。
$ docker run --gpus all nvidia/cuda:10.0-base nvidia-smi Unable to find image 'nvidia/cuda:10.0-base' locally 10.0-base: Pulling from nvidia/cuda 7ddbc47eeb70: Pull complete c1bbdc448b72: Pull complete 8c3b70e39044: Pull complete 45d437916d57: Pull complete d8f1569ddae6: Pull complete de5a2c57c41d: Pull complete ea6f04a00543: Pull complete Digest: sha256:e6e1001f286d084f8a3aea991afbcfe92cd389ad1f4883491d43631f152f175e Status: Downloaded newer image for nvidia/cuda:10.0-base Mon Jul 13 08:31:01 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 450.51.05 Driver Version: 450.51.05 CUDA Version: 11.0 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 Tesla P100-PCIE... Off | 00000000:00:07.0 Off | 0 | | N/A 34C P0 29W / 250W | 0MiB / 16280MiB | 0% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+テスト
こちらからTensorFlowのコンテナを起動します。
--gpus
オプションを使用するとコンテナからGPUが使用可能となります。$ docker run -it --gpus all -p 8888:8888 tensorflow/tensorflow:latest-gpu-jupyter以下のようにJupter Notebookの接続先が表示されるので、ホスト名の部分をサーバーのIPアドレスに置き換えて、ブラウザでアクセスします。
http://10313cb03051:8888/?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxチュートリアルのNotebookが表示されるので実行してみます。
参考
以下のように起動のオプションでDocker設定が可能です。
$ mkdir ~/workspace $ docker run -it --gpus all -p 8888:8888 -u $(id -u):$(id -g) -v ~/workspace:/tf tensorflow/tensorflow:latest-gpu-jupyter
-v
: ホストのディレクトリをコンテナ上にマウント
-u
: コンテナの実行ユーザー、グループを変更
- 投稿日:2020-07-13T17:39:50+09:00
dockerでnpx @angular/cli newしてエラーが出る時の対処法
問題
npxを使うと@angular/cliをグローバルインストールせずにプロジェクトを作成できます。
npx @angular/cli new fooしかしdockerで同じコマンドを実行するとエラーになります。
internal/modules/cjs/loader.js:1033 throw err; ^ Error: Cannot find module '/root/.npm/_npx/800/lib/node_modules/@angular/cli/bin/postinstall/script.js' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1030:15) at Function.Module._load (internal/modules/cjs/loader.js:899:27) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) at internal/main/run_main_module.js:17:47 { code: 'MODULE_NOT_FOUND', requireStack: [] } npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! @angular/cli@10.0.2 postinstall: `node ./bin/postinstall/script.js` npm ERR! Exit status 1回避策
どうもrootユーザーなのが問題なようです。
--unsafe-permを指定したいところですがnpxには--unsafe-permオプションが無さそうなので環境変数で渡します。npm_config_unsafe_perm=true npx @angular/cli new fooこれで無事にプロジェクトを作成できました。
- 投稿日:2020-07-13T15:37:40+09:00
OKI AI エッジコンピューター「AE2100」でOpenVINOのサンプルプログラムを動かしてみよう(1)
要約
- この資料は、OKI AI エッジコンピューター「AE2100」向けの設定解説です。
- GUIアプリケーション動作のためのVcXsrv(X-Windowクライアント)の導入方法を解説します。
はじめに
Intel OpenVINOには様々なサンプルプログラムが付属しています。
ですが、多くのサンプルプログラムはウインドウの表示が必要になっており、
ディスプレイ出力がないAE2100では動作ができません。そこで、数回に渡ってAE2100でOpenVINOのサンプルプログラムを動かすための方法をご紹介していきます。
初回はAE2100を接続したウインドウズPCにVcxSrvの導入をおこないます。環境
以下のような構成で、ウインドウズPCからAE2100へTeraTermでコンソール接続できることを前提とします。
(適宜IPアドレスなどはご使用環境に合わせ読み替えてください。)
なおAE2100のコンテナバージョンは「centos7_openvino_2019R31_1.tgz」とします。VcXsrv のインストール
VcXsrv とはフリーの X Server クライアントで、ウインドウズPCにインストールをおこないます。
WebブラウザよりSourceForge.netにアクセスし “VcXsrv” のインストーラーをダウンロードします。
https://sourceforge.net/projects/vcxsrv/
ダウンロードしたインストーラを実行すると以下の画像のような画面が出ます。
設定はそのままにし[Next]を押します。
インストール先のフォルダを確認・選択して「Install」をクリックします。
VcXsrvの起動
ウインドウズPCのスタートメニューからVcXsrv→Xlaunchを起動します。
設定はそのままにし「次へ」をクリックします。(”Multiple Window”を選択)
設定はそのままにし「次へ」をクリックします。(Start no clientを選択)
「Disable access control」のチェックを入れて「次へ」をクリックします。
Save configrationで設定を保存し、[完了]を押します。
以下の画像のような画面が出る場合、[アクセスを許可する]を押します。
そして、タスクバー右下にXlaunchのアイコンが表示されれば成功です。
AE2100のコンテナ設定
TeraTramでAE2100のホストOSにログインして、以下のコマンドでコンテナに入ります。
(コンテナが起動していない場合、「AE2100 シリーズ SDK 取扱説明書 ーDeepLearning 編ー」P.20を参考に起動させてください。)root@ae2100:~# docker exec -it centos-openvino /bin/bash
今回は電卓アプリケーションを動かします。以下のコマンドでインストールをおこないます。# yum install xcalc
ウインドウ表示先のIPアドレスを設定します。PCのIPアドレスを指定します。# export DISPLAY=192.168.100.101:0.0GUIアプリケーションの実行
AE2100のコンテナ内で以下のコマンドを入力します。
# xcalc &試しにボタンを押して計算をさせてみましょう。
例えば「8」「*」「8」と入力すると「64」と表示されます。
アプリケーションウインドウはPC上に表示されていますが、計算自体はAE2100でおこなわれています。
もし電卓が表示されない場合は、ウインドウズの「ファイアウォールとネットワーク保護」
を確認してください。
ウインドウズの「ファイアウォールとネットワーク保護」画面を開き、
「ファイアウォールによるアプリケーションの許可」を選択します。
設定変更ボタンを押し、下画面のようにVcXsrv windows xserverにチェックを入れOKボタンを押します。
まとめ
今回はAE2100に接続したPCのウインドウ表示設定をおこないました。
次回からはOpenVINOのサンプルプログラムを動かしていきます。
- 投稿日:2020-07-13T14:42:32+09:00
Alibaba Cloud Container Registry Serviceを使用したコンテナ化されたイメージの構築と展開
このチュートリアルでは、Alibaba Cloud Container Registryサービスを使用してコンテナ化されたイメージを構築し、デプロイします。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
Alibaba Cloud Container Registryとは?
Alibaba Cloud Container Registry(ACR)は、コンテナイメージを構築して保存し、Dockerイメージを配布できるようにするスケーラブルなサーバーアプリケーションです。ACRを使用すると、保存されたイメージを完全に制御することができます。ACRには、GitHub、Bitbucket、自作のGitLabとの統合など、多くの機能があります。また、コンパイル後に新しいイメージを自動的にビルドし、ソースコードからアプリケーションまでテストすることもできます。
ステップ1:Alibaba Cloud Container Registryを有効化する
アリババクラウドのアカウントを設定する必要があります。アカウントをお持ちでない場合は、アカウントにサインアップして、40以上の製品を無料でお試しいただけます。詳しくはこちらのチュートリアルをお読みください。
最初に行う必要があるのは、Alibaba Cloud Container Registryを有効化することです。製品ページに移動し、Get it Freeをクリックします。
Container Registry Consoleに移動し、サービスを設定してデプロイすることができます。ステップ2:アリババクラウドコンテナレジストリの設定
ネームスペースの作成
ネームスペースはリポジトリの集合体、リポジトリはイメージの集合体です。私は、アプリケーションごとに1つのネームスペースを作成し、サービスイメージごとに1つのリポジトリを作成することをお勧めします。
ネームスペースを作成したら、設定でパブリックリードかプライベートかを設定します。
ローカルリポジトリの作成とアップロード
リポジトリ(repo)とは、イメージを集めたものです。1つのサービスのイメージの全バージョンを1つのリポジトリに集めることをお勧めします。Create Repoをクリックし、ページ内の情報を記入します。Local Repositoryを選択します。しばらくすると、独自のリポジトリURLを持つ新しいリポジトリが作成されます。画像一覧ページに表示されています。
これで、ローカルで作成した画像をこのリポジトリにアップロードすることができます。
ステップ3:Dockerクライアントでコンテナレジストリに接続する
Dockerクライアントから任意のコンテナレジストリに接続するためには、まずACRコンソールでDockerのログインパスワードを設定する必要があります。このパスワードを使ってDockerクライアントからレジストリにログインします。
次に、イメージ一覧ページで、接続したいリポジトリの前にあるAdminをクリックします。ここには、Dockerクライアントがリポジトリにアクセスできるようにするために必要な情報やコマンドが記載されています。リポジトリのイメージ名、イメージタイプ、インターネットアドレス、イントラネットアドレスが表示されます。インターネットアドレスは、世界中のどこからでもリポジトリにアクセスできるようにするためのものです。Alibaba Cloudコンテナクラスタでリポジトリを使用する場合は、インターネットアドレスを使用した方がはるかに高速になるので、インターネットアドレスを使用した方が良いでしょう。
ログインコマンド、プッシュコマンド、プルコマンドをコピーします。後で必要になります。
ローカルマシンでDockerクライアントを起動します。Dockerクライアントのインストール方法はdocker.ioを参照してください。MACの場合はdocker.appを起動してDockerクライアントを起動します。
Dockerクライアントのユーザーとしてログイン
docker login --username=random_name@163.com registry-intl.ap-southeast-1.aliyuncs.com注: random_name は実際のユーザー名に置き換えてください。
パスワードを入力してエンターキーを押すと、ログイン成功のメッセージが表示されます。この時点で認証され、Alibaba Cloud Container Registryに接続されています。
ステップ4: ローカルにイメージを構築し、ACRにプッシュ
Dockerfileを書いてイメージを構築してみましょう。以下はサンプルのDockerfileです。
###################### # This is the first image for the static site. ##################### FROM nginx #A name can be given to a new build stage by adding AS name to the FROM instruction. #ARG VERSION=0.0.0 LABEL NAME = static-Nginx-image START_TIME = 2018.03.10 FOR="Alibaba Community" AUTHOR = "Fouad" LABEL DESCRIPTION = "This image is built for static site on DOCKER" LABEL VERSION = 0.0.0 #RUN mkdir -p /var/www/ ADD /public /usr/share/nginx/html/ EXPOSE 80 RUN service nginx restart</code></pre> Run the Docker build command to build the image. In order to later push the image to the repository, you need to tag the new image with the registry: <pre><code>docker build -t registry-intl-internal.ap-southeast-1.aliyuncs.com/fouad-space/ati-image .ビルドが完了すると、すでにリポジトリ名がタグ付けされています。コマンドを使うと新しい画像が入っているのがわかります。
Docker image lsコマンドで画像をACRリポジトリにプッシュします。
docker push registry-intl.ap-southeast-1.aliyuncs.com/fouad-space/ati-image:latestイメージが正常にプッシュされたことを確認するには、Container Registryコンソールで確認します。リポジトリ名の前にある Admin をクリックし、Image version をクリックします。
画像をプルしてコンテナを作成します。docker pullコマンドを実行します。
docker pull registry-intl.ap-southeast-1.aliyuncs.com/fouad-space/ati-image:latestすでにローカルコンピュータに画像を引っ張ってきているので、「画像は最新のものです」というメッセージが表示されています。
この画像を使って新しいコンテナを作成します。
docker run -ti -p 80:80 registry-intl.ap-southeast-1.aliyuncs.com/fouad-space/ati-image bashステップ 5: GitHub を使ったイメージレポの構築
Alibaba Cloud Container Registryを使用すると、クラウド上でイメージをビルドするだけでなく、レジストリに直接プッシュすることもできます。これに加えて、Container Repositoryは、コードが変更されたときにビルドを自動的にトリガーする機能をサポートしています。
ビルド設定で「コード変更時にイメージを自動的に作成する(Automatically create an image when the code changes)」を選択すると、コードを送信した後にイメージを自動的にビルドすることができ、手動でビルドをトリガーする必要が無くなります。これにより、手作業を省くことができ、イメージを常に最新の状態に保つことができます。
GitHubのレポを作成し、Dockerファイルをレポにアップロードします。
その後、コンテナレジストリコンソールに戻り、レポを作成します。GitHubのレポパスを選択し、リポジトリ作成の手順を完了します。
リポジトリが作成されたら、Image Listに移動し、レポ名のAdminをクリックし、Buildをクリックし、最後にBuild Nowをクリックします。
メニューにビルドの進捗状況が表示され、ビルドプロセスの完全なログが表示されます。
ビルドログも全て見ることができます。きちんとしていると思いませんか?
ビルドが完了したら、イメージをデプロイする準備ができています。これをローカルのDockerエンジンに引っ張ったり、Alibaba Cloud Container Serviceにこのイメージをデプロイしたりすることができます。
ステップ6: Webhookトリガーの作成
Webhookはトリガーの一種です。これを設定すると、イメージがビルドされたときに通知をプッシュしてくれるので、継続的なインテグレーションパイプラインを設定することができます。
これはどのように動作するのでしょうか? Webhook に Container Service のトリガーを設定したとします。イメージがビルドされたり、リビルドされたりすると、コンテナサービス内のアプリケーションが自動的にトリガーされて、最新のイメージを引っ張ってきて再デプロイされます。Webhookを作成するには、まずコンテナサービスにアクセスして、アプリケーションのWeb URLを取得する必要があります。
ここで、この URL を使用してフックを設定します。コンテナレジストリのイメージが更新されるたびに、このアプリケーションは新しいイメージで再デプロイされます。間違った設定をするとアプリケーション全体がダウンしてしまう可能性がありますので、十分に注意してください。しかし、コンテナサービスではロールバックが可能なので、大きな心配はありません。
概要
今回の記事では、以下のことを学んだはずです。
- Alibaba Cloud Container Registryサービスとは何か、どのように実装することができるか。
- Dockerイメージをホストするためのネームスペースとリポジトリを作成する方法。
- ローカルにDockerイメージを構築してACRにプッシュする方法。
- DockerイメージをACRから引っ張ってきて、それを使って新しいスコテナーをインスタンス化する方法。
- GitHubソースコードを使ってコンテナレジストリにイメージをビルドする方法。
- 最新のイメージのプルリクエストを自動的にトリガーしてサービスを再デプロイする方法。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-07-13T13:27:08+09:00
「Docker/Kubernetes」Kubernetesの拡張リソース・デバイスプラグインモジュール
この記事では、バージョン1.11で導入される予定のKubernetesの拡張リソースとデバイスプラグインモジュールについて探っていきます。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
KubernetesのGPUのスケジューリングや運用の仕組みについて最近報じられた記事によると、バージョン1.11では従来のalpha.kubernetes.io/nvidia-gpuのメインコードを非推奨とし、GPU関連のスケジューリングやデプロイメントコードを完全にメインコードから削除し、その代わりに、Kubernetesの組み込みモジュールである拡張リソースとデバイスプラグインの2つのモジュールと、デバイスプロバイダが開発したデバイスプラグインを組み合わせて、デバイスクラスタからワーキングノードへのスケジューリングを実装し、デバイスをコンテナでバインドするというものです。
Kubernetesの2つのモジュールについて簡単に紹介します。
拡張リソース
これはカスタムリソースの拡張方法です。開発者は、リソース名とリソースの総数をAPIサーバーに報告する必要があります。スケジューラは、リソースポッド内の作成・削除に基づいて利用可能なリソース数を増減させ、スケジューリング時にリソース要件を満たすノードを決定します。拡張リソースの増分、減分は整数でなければなりません。例えば、1GPUを割り当てることはできますが、0.5GPUを割り当てることはできません。この関数は不透明な整数リソースを置き換え、いくつかの名前を変更するだけなので、バージョン1.8では安定しています。キーワード整数を削除すれば、将来的には0.5 GPUを割り当てることができるようになるのでしょうか?デバイスプラグイン
一般的なデバイスプラグイン機構と標準デバイスAPIインタフェースを提供します。機器ベンダーは、Kubeletのメインコードを変更することなく、APIを実装することで、GPU、FPGA、高性能NIC、InfiniBandなどのデバイスを拡張することができます。この機能は、Kubeernetes 1.8と1.9のAlpha版にあり、Kubeernetes 1.10のBeta版にも搭載される予定です。この機能はまだ新しい機能なので、--feature-gate=DevicePlugins=true
に設定して有効にする必要があります。デバイスプラグインの設計
API設計
実は、Device Plugin は
ListAndWatch
とAllocate
メソッドを実装したシンプルな gRPC サーバで、/var/lib/kubelet/deviceplugins/
以下の Unix ソケット、例えば/var/lib/kubelet/deviceplugins/nvidia.sock
をリッスンしています。service DevicePlugin { // returns a stream of []Device rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {} rpc Allocate(AllocateRequest) returns (AllocateResponse) {} }これらの中では
- ListAndWatch:Kubelet はこの API を呼び出してデバイスを検出し、デバイスの状態を更新します (例えば、デバイスが不健康になるなど)。
- Allocate:Kubelet がデバイスを使用するコンテナを作成する際に、Kubelet はこの API を呼び出してデバイスに対する操作を行い、コンテナの初期化に必要なデバイス、ボリューム、環境変数の設定を取得します。プラグインのライフサイクル管理
プラグインが起動すると、Kubelet
/var/lib/kubelet/device-plugins/kubelet.sock
に GRPS 形式で登録し、プラグインのリスニング用 Unix ソケット、API バージョン、デバイス名 (例: nvidia.com/gpu) を提供します。Kubelet はデバイスをノードの状態で公開し、Extended Resource リクエストで API サーバに送信します。スケジューラは、その情報に基づいてデバイスをスケジュールします。プラグインが起動すると、Kubeletはプラグインへの永続的なlistAndWatch接続を確立します。不健全なデバイスを検出すると、プラグインは自動的にKubeletに通知します。デバイスがアイドル状態であれば、Kubelet はそのデバイスを割り当て可能なリストから削除します。
プラグインは、Kubelet ソケットを使って Kubelet の状態を監視します。Kubelet が再起動すると、プラグインも再起動し、再び Kubelet に登録します。
デプロイメント方法
一般的には、デーモンセットの展開と非コンテナ展開をサポートしています。ただし、公式にはデーモンセットの展開を推奨しています。
実装例
Nvidia公式GPUプラグイン
NVIDIAは、ユーザーフレンドリーなGPUデバイスプラグインNVIDIA/k8s-device-pluginを提供しており、これはDevice Pluginsインターフェースをベースにしています。従来のalpha.kubernetes.io/nvidia-gpu
のように、CUDA で必要なライブラリを指定するためにボリュームを使用する必要はありません。apiVersion: apps/v1 kind: Deployment metadata: name: tf-notebook labels: app: tf-notebook spec: template: # define the pods specifications metadata: labels: app: tf-notebook spec: containers: - name: tf-notebook image: tensorflow/tensorflow:1.4.1-gpu-py3 resources: limits: nvidia.com/gpu: 1結論
Kubernetesがエコシステムでの地位を獲得したことで、拡張性はその主戦場となるでしょう。ヘテロジニアスコンピューティングは、Kubernetesにとって重要な新たな戦場となります。しかし、ヘテロジニアスコンピューティングには、強力なコンピューティングと高性能なネットワークが必要です。そのため、GPU、FPGA、NIC、InfiniBandなどの高性能なハードウェアと統一的に統合する必要があります。Kubernetes Device Pluginはシンプルで、そして今なお進化を続けています。Alibaba Cloud Container Serviceは、このDevice PluginをベースにしたKubernetes GPU 1.9.3クラスタの提供を開始します。
同様の記事を読んで、アリババクラウドの製品やソリューションについて詳しく知りたい方は、www.alibabacloud.com
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-07-13T12:40:15+09:00
bpftraceで Docker の仕組み調べてみる
概要
Dockerはよく
docker run
とかdocker exec
のコマンドで Black Box みたいに使われて完結ですが、その中身をもっと詳しく調査してみたい。そのための有力ツールの一つはbpftraceです。作業環境
- Ubuntu 20.04
Dockerの構成
Dockerはおよそ以下のパーツに構成されている
docker-cli
:docker run
とdocker exec
などのコマンドを提供するDockerのクライアント側dockcerd
:Docker のサーバー側、Docker Engine のデーモンプロセス、docker-cli
などクライアントからのリクエストを処理する場所containerd
:Container Runtime の一つ、いろんな Container の管理を機能しているデーモンプログラムrunc
:いろんなOS機能を利用して、単独のContainerの実行を成せているツールDockerがやっていること
Containerは Namespace、Cgroup、OverlayFS、Virtual Network などのOS機能利用して可能になるのがよくご存知ですが、Docker はこちらの機能どう利用したますか?
まずは、Container が実行した結果を確認していきます。この一番簡単の Container 起動します
$ docker run -ti --rm --name test alpine shこれからの利用のために pid と Container ID をに保存しておきます
$ export CPID=$(docker inspect test -f '{{ .State.Pid }}') $ echo $CPID 230973 $ export CID=$(docker inspect test -f '{{ .Id }}') $ echo $CID b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0
containerd
との親子関係$ pstree -s $CPID -STUpau systemd,1 └─containerd,80405 └─containerd-shim,230955 -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd ... └─sh,230973,ipc,mnt,net,pid,uts
containerd
はcontainerd-shim
を実行して、そしてまた Container プロセスを実行する。また、このContainer プロセスにはipc
とかmnt
などの namespace がついている。runcの設定
containerd
がrunc
を利用してるので、その設定も確認します$ jq -r '.process' < /var/run/containerd/io.containerd.runtime.v1.linux/moby/$CID/config.json { "terminal": true, "user": { "uid": 0, "gid": 0, ... }, "args": [ "sh" ], "env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOSTNAME=b6431f14cf40", "TERM=xterm" ], "cwd": "/", "capabilities": { "bounding": [ "CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FSETID", "CAP_FOWNER", "CAP_MKNOD", "CAP_NET_RAW", "CAP_SETGID", "CAP_SETUID", "CAP_SETFCAP", "CAP_SETPCAP", "CAP_NET_BIND_SERVICE", "CAP_SYS_CHROOT", "CAP_KILL", "CAP_AUDIT_WRITE" ], ... }, "apparmorProfile": "docker-default", "oomScoreAdj": 0 }Namespaces
Container プロセスについている namespace を具体的に見に行きましょう
$ lsns -p $CPID --output-all NS TYPE PATH NPROCS PID PPID COMMAND UID USER NETNSID NSFS 4026531835 cgroup /proc/1/ns/cgroup 120 1 0 /sbin/init 0 root 4026531837 user /proc/1/ns/user 120 1 0 /sbin/init 0 root 4026532186 mnt /proc/230973/ns/mnt 1 230973 230955 sh 0 root 4026532187 uts /proc/230973/ns/uts 1 230973 230955 sh 0 root 4026532188 ipc /proc/230973/ns/ipc 1 230973 230955 sh 0 root 4026532189 pid /proc/230973/ns/pid 1 230973 230955 sh 0 root 4026532191 net /proc/230973/ns/net 1 230973 230955 sh 0 root 0 /run/docker/netns/904e1ae9696cCgroups
所属している Cgroup はこちら
$ cat /proc/$CPID/cgroup 12:freezer:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 11:devices:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 10:blkio:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 9:rdma:/ 8:pids:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 7:memory:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 6:hugetlb:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 5:net_cls,net_prio:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 4:cpu,cpuacct:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 3:cpuset:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 2:perf_event:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 1:name=systemd:/docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0 0::/system.slice/containerd.serviceそして Cgroup の内容もちょっと見に行きましよう
$ cgget -g pids:/docker/$CID /docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0: pids.current: 1 pids.events: max 0 pids.max: max $ cgget -g net_cls,net_prio:/docker/$CID /docker/b6431f14cf40267d3eed22b34fc6e974be28f2e0f5b9b2bfbccdffaa5327a4a0: net_cls.classid: 0 net_prio.prioidx: 3 net_prio.ifpriomap: lo 0 enp0s3 0 docker0 0 veth18981d6 0Mounts
このContainer プロセスがマウントしたディレクトリはこちら
$ cat /proc/$CPID/mounts overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/JVWDDYIF5YQFGNGL3PDNWJ4A4M:/var/lib/docker/overlay2/l/TQBVPLJN6SEMDRW7VD5FBARG4F,upperdir=/var/lib/docker/overlay2/ae3eab82db4efee497c2d69a4ad18a8cfc816ce0d61296fee7b6f611d7f6ebb3/diff,workdir=/var/lib/docker/overlay2/ae3eab82db4efee497c2d69a4ad18a8cfc816ce0d61296fee7b6f611d7f6ebb3/work,xino=off 0 0 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 tmpfs /dev tmpfs rw,nosuid,size=65536k,mode=755 0 0 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666 0 0 sysfs /sys sysfs ro,nosuid,nodev,noexec,relatime 0 0 tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,relatime,mode=755 0 0 cgroup /sys/fs/cgroup/systemd cgroup ro,nosuid,nodev,noexec,relatime,xattr,name=systemd 0 0 cgroup /sys/fs/cgroup/perf_event cgroup ro,nosuid,nodev,noexec,relatime,perf_event 0 0 cgroup /sys/fs/cgroup/cpuset cgroup ro,nosuid,nodev,noexec,relatime,cpuset 0 0 cgroup /sys/fs/cgroup/cpu,cpuacct cgroup ro,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0 cgroup /sys/fs/cgroup/net_cls,net_prio cgroup ro,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0 cgroup /sys/fs/cgroup/hugetlb cgroup ro,nosuid,nodev,noexec,relatime,hugetlb 0 0 cgroup /sys/fs/cgroup/memory cgroup ro,nosuid,nodev,noexec,relatime,memory 0 0 cgroup /sys/fs/cgroup/pids cgroup ro,nosuid,nodev,noexec,relatime,pids 0 0 cgroup /sys/fs/cgroup/rdma cgroup ro,nosuid,nodev,noexec,relatime,rdma 0 0 cgroup /sys/fs/cgroup/blkio cgroup ro,nosuid,nodev,noexec,relatime,blkio 0 0 cgroup /sys/fs/cgroup/devices cgroup ro,nosuid,nodev,noexec,relatime,devices 0 0 cgroup /sys/fs/cgroup/freezer cgroup ro,nosuid,nodev,noexec,relatime,freezer 0 0 mqueue /dev/mqueue mqueue rw,nosuid,nodev,noexec,relatime 0 0 shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime,size=65536k 0 0 /dev/sda1 /etc/resolv.conf ext4 rw,relatime 0 0 /dev/sda1 /etc/hostname ext4 rw,relatime 0 0 /dev/sda1 /etc/hosts ext4 rw,relatime 0 0 devpts /dev/console devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666 0 0 proc /proc/bus proc ro,relatime 0 0 proc /proc/fs proc ro,relatime 0 0 proc /proc/irq proc ro,relatime 0 0 proc /proc/sys proc ro,relatime 0 0 proc /proc/sysrq-trigger proc ro,relatime 0 0 tmpfs /proc/acpi tmpfs ro,relatime 0 0 tmpfs /proc/kcore tmpfs rw,nosuid,size=65536k,mode=755 0 0 tmpfs /proc/keys tmpfs rw,nosuid,size=65536k,mode=755 0 0 tmpfs /proc/timer_list tmpfs rw,nosuid,size=65536k,mode=755 0 0 tmpfs /proc/sched_debug tmpfs rw,nosuid,size=65536k,mode=755 0 0 tmpfs /proc/scsi tmpfs ro,relatime 0 0 tmpfs /sys/firmware tmpfs ro,relatime 0 0ここで注目するのは overlay の情報
overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/JVWDDYIF5YQFGNGL3PDNWJ4A4M:/var/lib/docker/overlay2/l/TQBVPLJN6SEMDRW7VD5FBARG4F,upperdir=/var/lib/docker/overlay2/ae3eab82db4efee497c2d69a4ad18a8cfc816ce0d61296fee7b6f611d7f6ebb3/diff,workdir=/var/lib/docker/overlay2/ae3eab82db4efee497c2d69a4ad18a8cfc816ce0d61296fee7b6f611d7f6ebb3/work,xino=off 0 0ここでマウントした
/var/lib/docker/overlay2/l/TQBVPLJN6SEMDRW7VD5FBARG4F
は実は alpine イメージの内容です$ cat $(docker image inspect alpine -f '{{ .GraphDriver.Data.UpperDir }}')/../link TQBVPLJN6SEMDRW7VD5FBARG4Fネットワーク
最後にネットワークの設定も確認しましよう。Container プロセスは独自の network namespace に区別しているから、host mahcineと通信のためには、vethを利用します。
まずは、Container側のネットワーク設定
$ nsenter -t $CPID -n ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet) RX packets 17 bytes 1366 (1.3 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ nsenter -t $CPID -n ip link show type veth 280: eth0@if281: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 $ nsenter -t $CPID -n ip route show dev eth0 default via 172.17.0.1 172.17.0.0/16 proto kernel scope link src 172.17.0.2host machine 側
$ ifconfig docker0 docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:ffff:fe14:4bd6 prefixlen 64 scopeid 0x20<link> ether 02:42:ff:14:4b:d6 txqueuelen 0 (Ethernet) RX packets 8 bytes 433 (433.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 283 bytes 30567 (30.5 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ ip addr show type veth 281: veth18981d6@if280: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether fa:eb:12:ad:57:d1 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::f8eb:12ff:fead:57d1/64 scope link valid_lft forever preferred_lft forever $ ip route show dev docker0 172.17.0.0/16 proto kernel scope link src 172.17.0.1Container のアドレスは
172.17.0.2
で、hostの方は172.17.0.1
、veth18981d6
という veth で2つの interface を繋がている。そしてそれぞれの route table に転送経路のルールが載せている。
bpftrace
でDocker実行順番を明白するさて、いろいろの結果を確認してきたから、具体的に Docker はどうやってその結果のたどり着いたか、例えばいつにどんな関数をどんな変数で呼びましたか。ここではbpftraceが活躍するところです。
bpftrace っていうのは Linux Kenel の eBPF 機能を awk みたい記述で簡単に利用できるツールです。具体的な使用方法はここでは省きますが、Docに参照してください。
bpftrace
のセットアップ# bcc $ apt-get install -y linux-headers-$(uname -r) bison build-essential cmake flex g++ git libelf-dev zlib1g-dev libfl-dev systemtap-sdt-dev binutils-dev llvm-8-dev llvm-8-runtime libclang-8-dev clang-8 arping netperf iperf3 python3-distutils $ git clone --recurse-submodules https://github.com/iovisor/bcc.git $ mkdir bcc/build; cd bcc/build $ cmake -DPYTHON_CMD=python3 .. $ make -j8 && make install && ldconfig $ cd ../.. # bpftrace $ git clone https://github.com/iovisor/bpftrace.git $ mkdir bpftrace/build; cd bpftrace/build $ cmake -DHAVE_BCC_PROG_LOAD=ON -DHAVE_BCC_CREATE_MAP=ON -DBUILD_TESTING=OFF .. $ make -j8 && make installDockerをトレース
bpftraceで docker と OS のやり取りがトレースすることができる。まずはこちらの内容で
docker.bt
のファイル作成するdocker.bt#!/usr/bin/env bpftrace #include <linux/nsproxy.h> #include <linux/ns_common.h> #include <linux/utsname.h> #include <linux/pid_namespace.h> #include <linux/ipc_namespace.h> #include <linux/cgroup.h> #include <net/net_namespace.h> #include <linux/netdevice.h> BEGIN { printf("%-12s %-15s %-8s %-10s %s\n", "TIME", "COMMAND", "PID", "ACTION", "CONTENT"); } tracepoint:syscalls:sys_enter_execve { printf("%-12ld %-15s %-8d %-10s ", elapsed , comm, pid, "execve"); join(args->argv); } tracepoint:syscalls:sys_enter_mount { printf("%-12ld %-15s %-8d %-10s ", elapsed, comm, pid, "mount"); printf("type=%s, dev=%s, dir=%s\n", str(args->type), str(args->dev_name), str(args->dir_name)) } kretprobe:create_new_namespaces /comm=="runc:[1:CHILD]"/ { printf("%-12ld %-15s %-8d %-10s ", elapsed, comm, pid, "namespace"); $nsp = (struct nsproxy *)retval; printf("uts=%ld, ipc=%ld, cgroup=%ld, net=%ld, pid=%ld\n", ((struct uts_namespace *)$nsp->uts_ns)->ns.inum, ((struct ipc_namespace *)$nsp->ipc_ns)->ns.inum, ((struct cgroup_namespace *)$nsp->cgroup_ns)->ns.inum, ((struct net *)$nsp->net_ns)->ns.inum, ((struct pid_namespace *)$nsp->pid_ns_for_children)->ns.inum); } kprobe:veth_newlink { printf("%-12ld %-15s %-8d %-10s ", elapsed, comm, pid, "veth"); printf("name=%s, netns=%ld\n", ((struct net_device *)arg1)->name, ((struct net *)arg0)->ns.inum); }そして実行する
$ ./docker.bt Attaching 5 probes... TIME COMMAND PID ACTION CONTENT
もう一度 Container の起動する(別のターミナル)
$ docker run -ti --rm --name test alpine echo hi
docker.bt
に戻せば、こちらの内容出力されますTIME COMMAND PID ACTION CONTENT 1827725094 bash 235026 execve docker run -ti --rm --name test alpine echo hi 1874368200 dockerd 81563 mount type=overlay, dev=overlay, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 1892118900 dockerd 81563 mount type=overlay, dev=overlay, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 1904728454 dockerd 81563 mount type=overlay, dev=overlay, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 1905395722 dockerd 81563 veth name=veth3d71f6e, netns=4026531992 1912608987 networkd-dispat 235038 execve /usr/bin/networkctl list --no-pager --no-legend 1913900866 (spawn) 235036 execve /lib/udev/bridge-network-interface 1917882282 (spawn) 235039 execve /lib/open-iscsi/net-interface-handler start 1922126799 (spawn) 235040 execve /lib/udev/bridge-network-interface 1923736757 (spawn) 235041 execve /lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/veth3d71f6e --prefix=/net/ipv4/neigh/veth3d71f6e --prefix=/net/ipv6/conf/veth3d71f6e --prefix=/net/ipv6/neigh/veth3d71f6e 1925374081 (spawn) 235042 execve /lib/open-iscsi/net-interface-handler start 1935733149 containerd 235044 execve containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc 1940074927 containerd-shim 235053 execve runc --root /var/run/docker/runtime-runc/moby --log /run/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb/log.json --log-format json create --bundle /run/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb --pid-file /run/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb/init.pid --console-socket /tmp/pty046813817/pty.sock 6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb 1940243736 (spawn) 235043 execve /lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/vethc30992d --prefix=/net/ipv4/neigh/vethc30992d --prefix=/net/ipv6/conf/vethc30992d --prefix=/net/ipv6/neigh/vethc30992d 1955997514 runc 235060 execve runc init 1959669403 exe 235060 mount type=, dev=/proc/self/exe, dir=/var/run/docker/runtime-runc/moby/6975e3344ad32b92a6a09066f3113 1962524451 exe 235060 mount type=, dev=, dir=/var/run/docker/runtime-runc/moby/6975e3344ad32b92a6a09066f3113 1977110103 runc:[1:CHILD] 235061 namespace uts=4026532187, ipc=4026532188, cgroup=4026531835, net=4026532191, pid=4026532189 2002233022 runc:[2:INIT] 235062 mount type=, dev=, dir=/ 2002647666 runc:[2:INIT] 235062 mount type=bind, dev=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2002787106 runc:[2:INIT] 235062 mount type=proc, dev=proc, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2002848509 runc:[2:INIT] 235062 mount type=tmpfs, dev=tmpfs, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2003072500 runc:[2:INIT] 235062 mount type=devpts, dev=devpts, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2006987497 runc:[2:INIT] 235062 mount type=sysfs, dev=sysfs, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2007808775 runc:[2:INIT] 235062 mount type=tmpfs, dev=tmpfs, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008001937 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/systemd/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008021032 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/systemd/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008102923 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/perf_event/docker/6975e3344ad32b92a6a09066f31132, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008116742 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/perf_event/docker/6975e3344ad32b92a6a09066f31132, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008189722 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/cpuset/docker/6975e3344ad32b92a6a09066f31132f9b4, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008203301 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/cpuset/docker/6975e3344ad32b92a6a09066f31132f9b4, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008284557 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/cpu,cpuacct/docker/6975e3344ad32b92a6a09066f3113, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008300251 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/cpu,cpuacct/docker/6975e3344ad32b92a6a09066f3113, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008365239 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/net_cls,net_prio/docker/6975e3344ad32b92a6a09066, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008397968 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/net_cls,net_prio/docker/6975e3344ad32b92a6a09066, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008471763 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/hugetlb/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008489627 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/hugetlb/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008575004 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/memory/docker/6975e3344ad32b92a6a09066f31132f9b4, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008588697 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/memory/docker/6975e3344ad32b92a6a09066f31132f9b4, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008655100 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/pids/docker/6975e3344ad32b92a6a09066f31132f9b4e3, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008667811 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/pids/docker/6975e3344ad32b92a6a09066f31132f9b4e3, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008731445 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/rdma, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008744138 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/rdma, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008792554 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/blkio/docker/6975e3344ad32b92a6a09066f31132f9b4e, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008805034 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/blkio/docker/6975e3344ad32b92a6a09066f31132f9b4e, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008855551 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/devices/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008868073 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/devices/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008919459 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/freezer/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008932321 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup/freezer/docker/6975e3344ad32b92a6a09066f31132f9b, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008958905 runc:[2:INIT] 235062 mount type=bind, dev=/sys/fs/cgroup, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2008977832 runc:[2:INIT] 235062 mount type=mqueue, dev=mqueue, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009006418 runc:[2:INIT] 235062 mount type=tmpfs, dev=shm, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009102662 runc:[2:INIT] 235062 mount type=bind, dev=/var/lib/docker/containers/6975e3344ad32b92a6a09066f31132f9b4e3, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009116671 runc:[2:INIT] 235062 mount type=, dev=, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009152544 runc:[2:INIT] 235062 mount type=bind, dev=/var/lib/docker/containers/6975e3344ad32b92a6a09066f31132f9b4e3, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009184117 runc:[2:INIT] 235062 mount type=, dev=, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009256053 runc:[2:INIT] 235062 mount type=bind, dev=/var/lib/docker/containers/6975e3344ad32b92a6a09066f31132f9b4e3, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2009268672 runc:[2:INIT] 235062 mount type=, dev=, dir=/var/lib/docker/overlay2/ec1fbee3aa02f9596794e45afddaddf75028e0 2012566089 runc 235085 execve libnetwork-setkey -exec-root=/var/run/docker 6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb ee4af79b8952 2055580696 dockerd 81563 mount type=bind, dev=/proc/235062/ns/net, dir=/var/run/docker/netns/53666134ee2d 2058245033 dockerd 235093 execve set-ipv6 /var/run/docker/netns/53666134ee2d all false 2117481953 (spawn) 235100 execve /lib/open-iscsi/net-interface-handler stop 2135647000 runc:[2:INIT] 235062 mount type=, dev=, dir=. 2135881831 runc:[2:INIT] 235062 mount type=bind, dev=/dev/pts/0, dir=/dev/console 2135959898 runc:[2:INIT] 235062 mount type=, dev=/proc/bus, dir=/proc/bus 2135974823 runc:[2:INIT] 235062 mount type=, dev=/proc/bus, dir=/proc/bus 2135984627 runc:[2:INIT] 235062 mount type=, dev=/proc/fs, dir=/proc/fs 2135995698 runc:[2:INIT] 235062 mount type=, dev=/proc/fs, dir=/proc/fs 2136004311 runc:[2:INIT] 235062 mount type=, dev=/proc/irq, dir=/proc/irq 2136017445 runc:[2:INIT] 235062 mount type=, dev=/proc/irq, dir=/proc/irq 2136025919 runc:[2:INIT] 235062 mount type=, dev=/proc/sys, dir=/proc/sys 2136036802 runc:[2:INIT] 235062 mount type=, dev=/proc/sys, dir=/proc/sys 2136045583 runc:[2:INIT] 235062 mount type=, dev=/proc/sysrq-trigger, dir=/proc/sysrq-trigger 2136057589 runc:[2:INIT] 235062 mount type=, dev=/proc/sysrq-trigger, dir=/proc/sysrq-trigger 2136066994 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/asound 2136076692 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/acpi 2136145362 runc:[2:INIT] 235062 mount type=tmpfs, dev=tmpfs, dir=/proc/acpi 2136198403 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/kcore 2136211095 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/keys 2136271650 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/latency_stats 2136281142 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/timer_list 2136292991 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/timer_stats 2136302265 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/sched_debug 2136312542 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/proc/scsi 2136411156 runc:[2:INIT] 235062 mount type=tmpfs, dev=tmpfs, dir=/proc/scsi 2136458345 runc:[2:INIT] 235062 mount type=, dev=/dev/null, dir=/sys/firmware 2136537743 runc:[2:INIT] 235062 mount type=tmpfs, dev=tmpfs, dir=/sys/firmware 2325058622 containerd-shim 235102 execve runc --root /var/run/docker/runtime-runc/moby --log /run/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb/log.json --log-format json start 6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb 2334946680 runc:[2:INIT] 235062 execve echo hi 2357752100 containerd-shim 235108 execve /usr/bin/containerd --address /run/containerd/containerd.sock publish --topic /tasks/exit --namespace moby 2389193617 containerd-shim 235116 execve runc --root /var/run/docker/runtime-runc/moby --log /run/containerd/io.containerd.runtime.v1.linux/moby/6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb/log.json --log-format json delete 6975e3344ad32b92a6a09066f31132f9b4e3d5c65e1b827ed91486f1128c17eb 2438983541 (spawn) 235129 execve /lib/udev/bridge-network-interface 2441125682 networkd-dispat 235130 execve /usr/bin/networkctl list --no-pager --no-legend 2441917196 (spawn) 235131 execve /lib/open-iscsi/net-interface-handler start 2445708579 (spawn) 235132 execve /lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/vethc30992d --prefix=/net/ipv4/neigh/vethc30992d --prefix=/net/ipv6/conf/vethc30992d --prefix=/net/ipv6/neigh/vethc30992d 2460630127 networkd-dispat 235133 execve /usr/bin/networkctl list --no-pager --no-legend 2469750858 (spawn) 235134 execve /lib/open-iscsi/net-interface-handler stop 2470070134 (spawn) 235135 execve /lib/open-iscsi/net-interface-handler stopこれは Docker がこの Container を実行するためにやったことは一目瞭然です。簡単にまとめると、順番的にはこちらです
- イメージの overlay をマウントする
- veth などのネットワーク設定
containerd-shim
を実行runc init
- namespaces 作り
- cgroup などにいろんなマウント
runc start
- ユーザーコマンド
echo hi
runc delete
- ネットワークの cleanup
参考資料
- 投稿日:2020-07-13T11:40:05+09:00
DevOpsアプローチに基づくAlibaba Cloud ECS上でのBoltのセットアップ
DevOpsアプローチを用いてAlibaba Cloud上にBoltを設置するために必要な手順をご紹介します。Boltは開発者向けにカスタマイズされた現代的なCMSです。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
Boltについて
BoltはSilexの上に構築された現代的なCMSで、「"始めから正しく作られたWordpress"」とも言われています。素晴らしい基盤を持っているので、開発者にとっては良いCMSであると言えるでしょう。現在はSilexを使用したバージョン3ですが、SensioLabsが開発を停止しているため、v4からはSymfony 4を使用することになります。素晴らしいCMSがさらに良くなっていくことを期待しています。
Terraformについて
Terraformが発売されたのは2014年のことですから、ずいぶん前のことになりますね。Terraformが何なのか知らない人は、ぜひ知っておいた方がいいでしょう。Terraformは、HashiCorpが開発した infrastructure-as-code software です。データセンターのインフラを非常に高度な設定言語(この場合はHCL)で定義することができ、そこから任意のサービスプロバイダでインフラを構築するための詳細な実行計画を作成することができます。これにより、安全かつ予測可能なインフラストラクチャの作成、変更、改善が可能となり、ファイルをgitリポジトリにコミットしてバージョン管理を行うことができます。API を宣言的な設定ファイル (*.df) にコード化し、チームメンバー間で共有したり、コードとして扱ったり、編集したりレビューしたりすることができるオープンソースのツールです。
基本的には、設定ファイルに沿ってインフラストラクチャを作成します。「クラウドサービスのためのDocker」と考えることができます。しかし、Dockerfileの代わりにmain.tfがあります。
Puppetsのウェブサイトによると、Infrastructure-as-codeは、インフラストラクチャを管理するための現代的なアプローチであり、"DevOpsの基盤 "と呼ばれることもあります。
インフラをソフトウェアのように扱う:バージョン管理、継続的インテグレーション、コードレビュー、自動テストなど、ソフトウェア開発者が使用するのと同じツールやプロセスで管理できるコードとして扱います。これらにより、インフラストラクチャの変更をより簡単に、迅速に、安全に、そして確実に行うことができます。
Infrastructure as code は、バージョン管理、コードレビュー、継続的インテグレーション、自動テストなどの一般的な DevOpsプラクティスの前提条件です。これらのプラクティスにより、顧客を喜ばせる高品質のソフトウェアを継続的に提供することができます。
出典: https://puppet.com/solutions/infrastructure-as-code
BoltとTerraformの両方に精通しているので、チュートリアルを開始しましょう。
Terraformのインストール
Terraformのインストールはとても簡単です。必要なのはHomebrewだけです。Homebrewがインストールされていない場合は、こちらをご覧ください。
ターミナルで以下のコマンドを実行してTerraformをインストールします。
brew install terrafrom
Terraformのインストールを確認するには、以下のコマンドを入力します。
terraform version
アリババクラウド公式プロバイダーをインストール
このチュートリアルの中で一番トリッキーな部分なので、しっかりとやっておきましょう。HashiCorpから提供されているAlibaba Cloud用のプロバイダ(Alicloud Provider)は完璧とは程遠く、最新のものではありません。
Alibaba Cloudには、その公式プロバイダのGitHub repositoryが非常に優れていて、アクティブに開発されているので、それを入手してインストールしてください。releases tabに移動して、あなたのプラットフォームのための最新のものを取得します。
ダウンロードした後、バイナリファイルをterraformのpluginsフォルダに配置します。Windowsの場合は、ユーザーの "Application Data "ディレクトリの下にあるterraform.d/pluginsに置いてください。その他のシステム(Linux や Mac など)では、ユーザのホームディレクトリの ~/.terraform.d/plugins に置いてください。また、バイナリのバージョンを変更するのは良い習慣ですので、terraform-provider-alicloud_v1.6.0 にリネームしてください。
Alibaba Cloudのアクセスキーを取得する
Alibaba Cloudコンソールにログインしたら、上部のメニューからメールアドレスの直下にある「accessskeys」をクリックします。
鍵の画面が表示されたら、アクセスキーIDとアクセスキーの秘密を安全な場所にコピーしてください。秘密の鍵を表示するには、「表示」をクリックする必要があります。このデータは非常に機密性の高いデータなので、保存先には注意してください。また、ポリシーを使用して、より限定された鍵を作成することも検討してください。
Terraformファイルの準備
この基本的な例では、すべてのコンフィグを一つのファイルにまとめますが、コンフィグの異なる部分をそれぞれの .tf ファイルに分けておくことをお勧めします。これは良い習慣で、時間が経つにつれて保守性と可読性が向上します。
フォルダを作成し、その中に main.tf というファイルを作成して、次のステップで編集します。Variables
まずは変数について説明します。これらの変数は、Alibaba Cloud のインフラストラクチャに接続するために必要な情報を terraform に伝えるために使用します。main.tfファイルを開き、以下のコードを記述します。
variable "access_key" { type = "string" default = "XXXXX" } variable "secret_key" { type = "string" default = "XXXXX" } variable "region" { type = "string" default = "ap-southeast-2" } variable "vswitch" { type = "string" default = "XXX-XXXXX" } variable "sgroups" { type = "list" default = [ "XX-XXXXX" ] } variable "name" { type = "string" default = "bolt-instance" } variable "password" { type = "string" default = "Test1234!" }ご覧のように、access_key, secret_key, region, vswitch id, そして sgroups のような、カスタマイズする必要のある変数がいくつかあります。vswitch については、ウェブパネルから ID を取得し、ECS を配置したい VSwitch を選択する必要があります。sgroups についても同様に、ECS を配置したいセキュリティグループの ID を取得する必要があります。しかし、これを動作させるためには、ポート 80、443、および 22 が公開されているグループに置く必要があることを覚えておいてください。
デフォルトでは、この例ではシドニーのデータセンターを使用しています。
Provider
この部分は、Terraformにどのプロバイダを使用するかを伝えるだけなので、説明は簡単です。この例では、TerraformにAlibaba Cloud (Alicloud Provider)を使用するように指示しています。
provider "alicloud" { access_key = "${var.access_key}" secret_key = "${var.secret_key}" region = "${var.region}" }Data
ここでは、使用するイメージとインスタンスの種類に関するECSの詳細を設定します。今回の例では、Ubuntu 16.04 64bits、1GB、1CPUを選択しています。しかし、ECSの仕様は用途に応じて必要に応じて増やすことができます。
data "alicloud_images" "search" { name_regex = "^ubuntu_16.*_64" } data "alicloud_instance_types" "default" { instance_type_family = "ecs.xn4" cpu_core_count = 1 memory_size = 1 }Resource
これが最も重要なブロックです。ここでは、最終的にどのようなリソースを作成するかをTerraformに伝えます。ここでは、先ほど定義した変数を使ってECSを作成しています。
イメージとしては、Ubuntu用に先に書いたregexを使って検索し、最初にAlibaba Cloudで公開されている最新版を選択しています。Instance Typeについても同様です。
resource "alicloud_instance" "web" { instance_name = "${var.name}" image_id = "${data.alicloud_images.search.images.0.image_id}" instance_type = "${data.alicloud_instance_types.default.instance_types.0.id}" vswitch_id = "${var.vswitch}" security_groups = "${var.sgroups}" internet_max_bandwidth_out = 100 allocate_public_ip = true password = "${var.password}" }remote-exec
先ほどの手順を終えても、ECSに何もインストールしなければあまり意味がありません。そのため、作成時にTerraformにSSHでログインしてもらい、dockerとdocker-composeをインストールする必要があります。その後、docker-composeの設定ファイルを取得して、roura/boltを使ってBoltのコピーをデプロイします。
そのためには、先ほど書いた最後のリソースブロックの中に、password = "${var.password}"の直後に以下のコードブロックを入れる必要があります。
provisioner "remote-exec" { inline = [ "apt-get update && apt-get install -y apt-transport-https ca-certificates curl software-properties-common", "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -", "add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"", "apt-get update && apt-get install -y docker-ce docker-compose", "curl https://raw.githubusercontent.com/roura356a/bolt/master/docker-compose.yml -o docker-compose.yml", "curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose", "docker-compose up -d" ] connection { host = "${alicloud_instance.web.public_ip}" password = "${var.password}" } }output
すべてが終わった後、生成したECSのIPがわからなければ、まだ先には進めません。そこで、以下のコードを実行して、TerraformにターミナルにIPを表示するように指示します。
output "ip" { value = "${alicloud_instance.web.public_ip}" }概要
結論から言うと、main.tfファイルは以下のようになります。
variable "access_key" { type = "string" default = "XXXXX" } variable "secret_key" { type = "string" default = "XXXXX" } variable "region" { type = "string" default = "ap-southeast-2" } variable "vswitch" { type = "string" default = "XXX-XXXXX" } variable "sgroups" { type = "list" default = [ "XX-XXXXX" ] } variable "name" { type = "string" default = "bolt-instance" } variable "password" { type = "string" default = "Test1234!" } provider "alicloud" { access_key = "${var.access_key}" secret_key = "${var.secret_key}" region = "${var.region}" } data "alicloud_images" "search" { name_regex = "^ubuntu_16.*_64" } data "alicloud_instance_types" "default" { instance_type_family = "ecs.xn4" cpu_core_count = 1 memory_size = 1 } resource "alicloud_instance" "web" { instance_name = "${var.name}" image_id = "${data.alicloud_images.search.images.0.image_id}" instance_type = "${data.alicloud_instance_types.default.instance_types.0.id}" vswitch_id = "${var.vswitch}" security_groups = "${var.sgroups}" internet_max_bandwidth_out = 100 allocate_public_ip = true password = "${var.password}" provisioner "remote-exec" { inline = [ "apt-get update && apt-get install -y apt-transport-https ca-certificates curl software-properties-common", "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -", "add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"", "apt-get update && apt-get install -y docker-ce docker-compose", "curl https://raw.githubusercontent.com/roura356a/bolt/master/docker-compose.yml -o docker-compose.yml", "curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose", "docker-compose up -d" ] connection { host = "${alicloud_instance.web.public_ip}" password = "${var.password}" } } } output "ip" { value = "${alicloud_instance.web.public_ip}" }terraform init
飛び立つ準備が整いました!Terraformのinitコマンドを入力して、プロジェクトを適用する準備をします。
terraform init
terraform plan
問題がないことを確認するためには、planコマンドを実行しておくと、実際に適用しなくてもジョブの概要を知ることができます。
terraform plan
terraform apply
これでマシンをデプロイしました! applyコマンドを実行し、終了するまで待ちます。インターネットの接続状況や接続先のデータセンターの状況にもよりますが、3分~5分ほどかかります。
terraform apply
ジョブが終了すると、新しいECSインスタンスのIPアドレスを確認するメッセージがターミナルに表示されます。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: ip = XX.XX.XX.XX選択したセキュリティグループがポート80を開いている場合は、ブラウザでIPを入力して、新しいWebサイトをカスタマイズするためのBoltのWebベースのインストールがどのように出てくるかを確認することができます。
これで正常にAlibaba Cloud ECS上にBoltをセットアップしました。それを使って何か楽しいことをしてみてください。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-07-13T11:35:29+09:00
[k8s]DockerHub の PrivateRegistry のイメージを Pod で利用する時のメモ
メモです。
DockerHub の PrivateRegistry を使う時の方法について確認した時のメモで以下を参考にやってみた
Pull an Image from a Private Registry
環境
$kubectl version Client Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:26:26Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"15+", GitVersion:"v1.15.11-eks-af3caf", GitCommit:"af3caf6136cd355f467083651cc1010a499f59b1", GitTreeState:"clean", BuildDate:"2020-03-27T21:51:36Z", GoVersion:"go1.12.17", Compiler:"gc", Platform:"linux/amd64"}前提確認
DockerHub の PrivateRegistry について確認する。
# PrivateRegistry なので認証なしでは Pull 出来ない $docker pull toshihirock/privatenginx Using default tag: latest Error response from daemon: pull access denied for toshihirock/privatenginx, repository does not exist or may require 'docker login' # docker login する $docker login # pull 出来た $docker pull toshihirock/privatenginxこんな感じ。
k8s でも確認。# 応答がなくなる $kubectl run -i private-test --image=toshihirock/privatenginx --generator=run-pod/v1 --rm --restart=Never --command -- echo 'hi' # 別ウィンドウで見ると以下のように ImagePullBackOff になっている $kubectl get pods |grep private-test private-test 0/1 ImagePullBackOff 0やってみる
kubernetes.io/dockerconfigjson type のシークレットを作る
k8s で DockerHub の PrivateRegistry のイメージを使う方法として認証情報を予め
kubernetes.io/dockerconfigjson
タイプの `Secret
として作成しておき、利用する方法がある。
docker login
コマンドが成功している場合~/.docker/config.json
に認証時に必要な設定があるのでこれを使う。# ファイルの確認 $ cat ~/.docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "xxxx" } }, "HttpHeaders": { "User-Agent": "Docker-Client/18.06.1-ce (linux)" } } # json から kubernetes.io/dockerconfigjson タイプの Secret を作成 $kubectl create secret generic regcred \ --from-file=.dockerconfigjson=<path/to/.docker/config.json> \ --type=kubernetes.io/dockerconfigjsonPod を起動してみる
kubectl run
コマンドのオプションでimagePullSecrets
の指定はできなさそうなのでマニュフェストを作って検証する。# ある程度のテンプレを作る $kubectl run private-test --image=toshihirock/privatenginx --generator=run-pod/v1 --restart=Never --dry-run -o yaml --command -- echo 'hi' > private-test.yaml
imagePullSecrets
を追加して作成した Secret のregcred
を指定する。
マニュフェストはこんな感じ。private-test.yamlapiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: private-test name: private-test spec: imagePullSecrets: - name: regcred containers: - command: - echo - hi image: toshihirock/privatenginx name: private-test resources: {} dnsPolicy: ClusterFirst restartPolicy: Never status: {}やってみる。
# マニュフェストを適用し、Pod の作成 $kubectl apply -f private-test.yaml # Pod の確認 $kubectl get pods |grep private-test private-test 0/1 Completed 0 100s # echo 文が実行されていることを確認 $kubectl logs private-test hi無事成功
- 投稿日:2020-07-13T10:33:23+09:00
本当に簡単にDockerを理解する
ザックリとDockerの使用方法(コンテナの実行関係、イメージの作成)についてまとめました。これを読めば一通りはわかるよ! 1.Dockerって何? 1.1.コンテナ 仮想マシンと考えてください。Dockerではコンテナの上でイメージを実行します。 1.2.イメージ OSで実行するHDDのイメージと考えてみましょう。 1.3.コンテナとイメージを使うことで何が良いの? 結局、なんでDockerを使うのか?ですが、次のようなメリットがあります。以下では、それをDockerを触って理解していくことにしてみます。 いろいろなタイプのイメージを作成・管理できる。 イメージを共有ができ、同じ処理を様々なコンテナ上で実行できる。 つまり、複数の同じ構成を持ったサーバーマシンをすぐに用意できることができることがDockerのメリットです。 2.Dockerのコマンドを覚えよう。 まずはDockerの実行してみましょう! 2.1.準備インストール 皆さんの手持ちの環境で一番簡単に動かせると思うので、Docker for Windowsを使っていくことにしてみます。 Dockerにサインインしておきます。 2.2.Run(コンテナの実行) 何はともあれ、コンテナを実行したいと思います。 nginxというWebサーバーのイメージを起動してみましょう。 以下のコマンドで実行することができます。 > docker run nginx ただ、nginxはWebサーバーですので80番ポートを使用します。ですが、80番ポートはすでに使用されている場合が多いと思います。コンテナの80番ポートは、実機の5000番ポート上にポートを転送して実行してみましょう。(-p オプションを使用します。) >docker run -p 5000:80 nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 68ced04f60ab: Pull complete 28252775b295: Pull complete a616aa3b0bf2: Pull complete Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b Status: Downloaded newer image for nginx:latest これで http://localhost:5000/にアクセスしてみましょう。 nginxが起動していることがわかりました。 Tips 今回は起動後、nginxサーバーをフォアグランドで実行し続けるイメージですので、コンテナは実行したままになります。イメージによっては起動後して処理を終えると、コンテナが終了してしまう場合もあります。コンテナの実行を維持したい場合は、 docker run -itd イメージ名 /bin/bash を試してみましょう。 2.3.実行中のコンテナの確認(PS) 次にどんなコンテナが実行されているか確認してみましょう。 > docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES aa8e7e1c8d67 nginx "nginx -g 'daemon of…" 39 minutes ago Up 39 minutes 0.0.0.0:5000->80/tcp funny_dijkstra コンテナIDにaa8e7e1c8d67(以降 aa)でnginxのコンテナが動作しているのがわかります。 2.4.実行中のコンテナで、コマンドの実行 実行中のコンテナでコマンドを実行してみます。 そのためにはexecを使います。また、実行先のコンテナを指定する必要があります。 >docker exec -it (コンテナ) コマンド コンテナ名は途中で省略してもよいです。 >docker exec -it aa ls bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr また、以下のようにすれば、exitまでの期間、ターミナルを操作できますね。 >docker exec -it aa /bin/bash コンテナのシェルの中でnginxの設定ファイル(/etc/nginx/conf.d/default.conf)の中を見てみましょう。dockerは最小限しかインストールしないものなので、viが入っていません。catを使う必要がありますね。 root@aa8e7e1c8d67:cat /etc/nginx/conf.d/default.conf server { listen 80; server_name localhost; ... location / { root /usr/share/nginx/html; index index.html index.htm; } ... } なるほど、/usr/share/nginx/htmlがコンテンツ置き場のようですね。 最後はexitしてシェルを抜けます。 >....(いろいろなコマンド操作) >exit これで大体の操作の感じをつかめたのではないでしょうか。 3.コンテナイメージを作ろう それでは自分用のコンテナイメージを作っていきましょう。 Dockerの利点は、様々なバリエーションのイメージを簡単に管理できることにあります。 DockerのイメージはDockerfileで作成しますので、まずはDockerfileを編集します。 3.1.Dockerfileの作り方 今回は次の操作をしてみましょう。Webサーバーに提供されるコンテンツです。 このイメージは、nginxが動作しているよ!と言いたいイメージとします。 以下のhello.htmlを表示でいるようにしたいと思います。 hello.html <!DOCTYPE html> <html> <head> </head> <body> <h1>Hello World</h1> </body> </html> # nginxイメージを起点にする FROM nginx # vimのインストール RUN apt-get update RUN apt-get -y install vim # hello.htmlファイルをコンテナに追加 ADD hello.html /usr/share/nginx/html # コンテナ実行時にnginxを実行 CMD ["/usr/sbin/nginx", "-g", "daemon off;"] よく使用するコマンドをまとめておきます。 コマンド 意味 FROM 派生元のイメージを指定する RUN イメージをビルドする際の処理 ADD イメージに指定のファイルを追加する。アーカイブファイルの場合は展開する COPY イメージに指定のファイルを追加する。 CMD イメージを実行する際に実行するコマンド ENTORYPOINT イメージ実行時に実行するコマンド、docker run時に上書き不可 Dockerfileができたら、ビルドをします。 空のディレクトリを作り、Dockerfileとhello.htmlにしてください。 >mkdir my_nginx_dockerfiles # 空ディレクトリを作り >copy Dockerfile my_nginx_dockerfiles # Dockerfileと >copy hello.html my_nginx_dockerfiles # 今回イメージに追加するファイルだけを入れます。 >cd my_nginx_dockerfiles # ディレクトリに移動します >docker build -t my_nginx:0.1 . # ビルドです Sending build context to Docker daemon 3.072kB ... Successfully built 1e53c2c84fba Successfully tagged my_nginx:0.1 SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories. 最後にパーミッション関係で指摘がありますが、成功ですね。image listコマンドでイメージができたか確認します。 >docker image list REPOSITORY TAG IMAGE ID CREATED SIZE my_nginx 0.1 1e53c2c84fba 7 minutes ago 178MB nginx latest 6678c7c2e56c 10 days ago 127MB さぁ、さっそく実行してみましょう。 >docker run -p 5000:80 my_nginx:0.1 追加したファイル(http://localhost:5000/hello.html)にアクセスしてみましょう。 カスタムイメージの完成です! 4.コンテナやイメージを管理しよう。 4.1. Push / Pull Dockerではイメージを共有することができます。非常に素晴らしい機能です。 Pull イメージを取得します。 > docker pull tensorflow/tensorflow 4.1. 不要コンテナ、イメージの削除 コンテナやイメージを管理しましょう。 コンテナはdocker psコマンドで、イメージはdocker image listコマンドで一覧できます。 Exitされたコンテナは不要ですので、rmで削除することができます。 また、同様にカスタムイメージを作り終わったので、nginxも不要です。
- 投稿日:2020-07-13T09:31:03+09:00
Dockerでプロセスをきれいに終了したい
Dockerでプロセスをきれいに終了したい
EntrypointがShell Scriptの時、子プログラムを正しく終了させたい。
このようなことを実現する方法です。
前回、Pythonで終了時に必ず何か実行したい で、Pythonのプログラムをkillで停止されても、正しくCleanupするようにする方法について書きました。
そのようなきちんとCleanup処理が書かれているプロクラムをDockerで実行したとき、docker stopで正しくClecnupされる方法について見てみます。直接Entrypointで実行する場合
main_process.pyという正しくCleanupされるように書かれたプログラムを直接Entrypointに指定してコンテナを作成してみます。
前回 Pythonで終了時に必ず何か実行したい で作成したPythonプログラムを使用します。DockerfileFROM alpine RUN apk add python3 WORKDIR / COPY main_process.py / RUN chmod +x /main_process.py ENTRYPOINT /main_process.py# docker build -t cleanup .
実行してみます。
# docker run -it --rm --name cleanup cleanup !!!Set up!!!別のターミナルからコンテナを停止します。
# docker stop cleanup
!!!Clean up!!!
ちゃんと、Cleanup処理が走りました。
dockerはEntrypointで指定したプログラムをProcess番号1として起動します。
そして、docker stopは、Process番号1に対してkillを実行します。mail_process.pyはSIGTERMが来るとClean up!!と表示するようになっているので、このように正しく動作しました。
Shell Script経由で実行する場合
単純に実行する
上記のように、直接Entrypointで指定できれば楽なのですが、環境変数から設定ファイルをゴニョゴニョしてからメインのプログラムを実行するという場合もよくあります。
このような場合の動きを確認してみましょう。main_process.pyを実行するentry.shを作成して、Entrypointにはentry.shを指定します。
DockerfileFROM alpine RUN apk add python3 WORKDIR / COPY main_process.py / COPY entry.sh / RUN chmod +x /main_process.py RUN chmod +x /entry.sh ENTRYPOINT /entry.shentry.sh#!/bin/sh /main_process.py
これをbuildして実行し、さっきと同じようにdocker stopを実行すると、docker stopコマンドが終了しません。
10秒ほどすると、docker stopコマンドが終了し、docker runも同時に終了します。この時のdocker run側のターミナルは、
# docker run -it --rm --name cleanup cleanup !!!Set up!!! #のような状態で、残念ながら、!!!Clean up!!!は表示されません。
10秒はdockerでstopができなかった場合のtimeoutで、dockerはSIGTERMを投げた後、10秒待っても終了しなければ、SIGKILLを投げて強制終了させます。
execを使用する (解決方法1)
entry.shを以下のように変えて、entry.shのプロセス自身をmain_process.py(正確にはpython3)に置き換えます。
entry.sh#!/bin/sh exec /main_process.pyこれをbuildして実行し、さっきと同じようにdocker stopを実行すると、今度は、すぐに終了します。
# docker run -it --rm --name cleanup cleanup !!!Set up!!! !!!Clean up!!! #docker run側のターミナルには、ちゃんとCleanupが表示されています。
execのあと、main_process.pyがプロセス番号1に置き換わっているので、当然といえば当然です。
ただし、終了処理に10秒以上かかるとSIGKILL(kill -9)されてしまうので、Cleanup処理は10秒以内に終わらせることが重要です。entry.shでもSIGTERMをトラップする (解決方法2)
entry.sh#!/bin/sh handler() { kill ${child_pid} wait ${child_pid} } trap handler SIGTERM /main_process.py & child_pid=$! wait ${child_pid}子プロセスはバックグラウンドで動作させるようにし、waitで終了を待ちます。
一方で、SIGTERMをトラップし、entry.shがSIGTERMを受け取った場合には、子プロセスをkillし、終了を待つようにします。先ほどと同様に、実行して、docker stopdしてみると、
# docker run -it --rm --name cleanup cleanup !!!Set up!!! !!!Clean up!!!正しく、Cleanup処理できていることが確認できます。
こちらも、Cleanup処理は10秒以内に終わらせることが重要です。また、handler()の中身を書き換えれば、子プロセスの終了だけでなく、データをPersistentな領域に移動する、というようなことも可能です。(10秒制限ありますが。)
【応用】entry.shから複数の子プロセスの終了を待つようにする
SIGTERMをトラップする方法ではプロセスが1つの場合でも実行をバックグラウンドで行いました。
これを応用すると、複数のプロセスの終了を正しく待つことができそうですので、やってみます。entry.sh#!/bin/sh handler() { kill ${child_pid1} kill ${child_pid2} wait ${child_pid1} wait ${child_pid2} } trap handler SIGTERM /main_process1.py & child_pid1=$! /main_process2.py & child_pid2=$! wait ${child_pid1} wait ${child_pid2}2つ並べるだけです。
簡単ですね。同様にテストしてみます。
# docker run -it --rm --name cleanup cleanup !!!Set up!!! !!!Set up!!! !!!Clean up!!! !!!Clean up!!! #2つのプログラムが正しくCleanup処理を行った後終了したことが確認できました。
まとめ
EntrypointにShell Scriptを使う場合、正しく子プロセスを終了させる方法は、
- execでプロセスを置き換える
- SIGTERMをトラップして、子プロセスにSIGTERMを送って待つ
の二通りのやり方があることがわかりました。
*本記事は @qualitia_cdevの中の一人、@hirachanさんに書いて頂きました。
- 投稿日:2020-07-13T08:53:07+09:00
Docker × Java シンプルすぎる開発環境構築
DockerでJavaの開発環境構築
概要
Docker上にJavaのコンテナを設置するだけのシンプルな環境を構築します。
プログラムを極めし証Hello World!
をコンソールに出力するところまでをご紹介します(笑)環境
- macOS Catalina バージョン10.15.5
- Docker version 19.03.8
- docker-compose version 1.25.5
構成
最終的に以下のような構成になります。
├── docker │ └── java │ └── Dockerfile ├── docker-compose.yml └── server └── src ├── Main.class └── Main.java手順
1. docker-compose.yml作成
javaコンテナ1つだけのシンプルな構成です。
docker-compose.ymlversion: '3.6' services: java: build: ./docker/java ports: - 8080:8080 tty: true volumes: - ./server/src:/usr/src:cached2. Dockerfile作成
DockerfileFROM openjdk:11-slim RUN apt-get update WORKDIR /usr/src3. テストファイルを作成
Main.javaというテストファイルを作成します。
Main.javapublic class Main { public static void main(String[] args) { System.out.println("Hello World!"); } }4. Docker起動
// dockerビルド % docker-compose build // dockerをバックグラウンドで起動 % docker-compose up -d // 確認 % docker-compose ps Name Command State Ports ------------------------------------------------------------- java-spring_java_1 jshell Up 0.0.0.0:8080->8080/tcp5. コンパイルと実行
// インスペクション % docker-compose exec java bash // コンパイル root@5b7be900c329:/usr/src# javac Main.java // 実行 root@5b7be900c329:/usr/src# java Main Hello World!参考
- 投稿日:2020-07-13T02:02:40+09:00
DockerでMongoDBコンテナ起動時に初期データをインポートする
はじめに
DockerでMongoDBをコンテナを立ち上げ、データを確認するまでの最小限の構成になります。
やりたいこと
- コンテナ作成時に初期データを投入したい
- 投入したデータをGUIクライアントで参照したい
ディレクトリ構成
. ├── docker-entrypoint-initdb.d │ ├── 1-mongo-init.js │ ├── 2-mongo-init.sh │ └── staffs.json └── docker-compose.yml各ファイルの内容
docker-compose.yml
「MONGO_INITDB_DATABASE」に指定した名称でデータベースが作成されます。
docker-compose.ymlversion: '3.3' services: mongo: image: mongo container_name: mongo restart: always environment: MONGO_INITDB_ROOT_USERNAME: mongo MONGO_INITDB_ROOT_PASSWORD: mongo MONGO_INITDB_DATABASE: mongo_example TZ: Asia/Tokyo ports: - 27018:27017 volumes: - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.ddocker-entrypoint-initdb.d/1-mongo-init.js
ユーザの作成と合わせて、コレクションの作成をしています。
ここでは例としてstaffsコレクションを作成します。docker-entrypoint-initdb.d/1-mongo-init.jsvar user = { user: "mongo", pwd: "mongo", roles: [ { role: "dbOwner", db: "mongo_example" } ] }; db.createUser(user); db.createCollection('staffs');docker-entrypoint-initdb.d/2-mongo-init.sh
作成したユーザ、パスワード、データベース、コレクションと、インポートするJSONファイルを指定します。
docker-entrypoint-initdb.d/2-mongo-init.shmongoimport -u mongo -p mongo --db mongo_example --collection staffs --file /docker-entrypoint-initdb.d/staffs.json --jsonArraydocker-entrypoint-initdb.d/staffs.json
staffsコレクションにインポートするデータをJSONファイルで用意します。
docker-entrypoint-initdb.d/staffs.json[ { "id" : 1, "name" : "佐藤" }, { "id" : 2, "name" : "鈴木" }, { "id" : 3, "name" : "田中" } ]実行
docker-composeで起動します。
$ docker-compose up -d Creating network "mongodb_default" with the default driver Creating mongo ... doneGUIクライアントで確認する
1. MongoDB Compassをダウンロード
MongoDB CompassからPlatformを選択しDownloadボタンをクリックします。
2. 接続
Paste your connection stringに以下の接続情報を入力し、CONNECTボタンをクリックします。
mongodb://mongo:mongo@localhost:270183. 結果
staffコレクションにデータをインポートできていることが確認できます。
- 投稿日:2020-07-13T01:57:13+09:00
DockerでMongoDBコンテナ起動時に初期データをインポートする
はじめに
DockerでMongoDBをコンテナを立ち上げ、データを確認するまでの最小限の構成になります。
やりたいこと
- コンテナ作成時に初期データを投入したい
- 投入したデータをGUIクライアントで参照したい
ディレクトリ構成
. ├── docker-entrypoint-initdb.d │ ├── 1-mongo-init.js │ ├── 2-mongo-init.sh │ └── staffs.json └── docker-compose.yml各ファイルの内容
docker-compose.yml
「MONGO_INITDB_DATABASE」に指定した名称でデータベースが作成されます。
docker-compose.ymlversion: '3.3' services: mongo: image: mongo container_name: mongo restart: always environment: MONGO_INITDB_ROOT_USERNAME: mongo MONGO_INITDB_ROOT_PASSWORD: mongo MONGO_INITDB_DATABASE: mongo_example TZ: Asia/Tokyo ports: - 27018:27017 volumes: - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.ddocker-entrypoint-initdb.d/1-mongo-init.js
ユーザの作成と合わせて、コレクションの作成をしています。
ここでは例としてstaffsコレクションを作成します。docker-entrypoint-initdb.d/1-mongo-init.jsvar user = { user: "mongo", pwd: "mongo", roles: [ { role: "dbOwner", db: "mongo_example" } ] }; db.createUser(user); db.createCollection('staffs');docker-entrypoint-initdb.d/2-mongo-init.sh
作成したユーザ、パスワード、データベース、コレクションと、インポートするJSONファイルを指定します。
docker-entrypoint-initdb.d/2-mongo-init.shmongoimport -u mongo -p mongo --db mongo_example --collection staffs --file /docker-entrypoint-initdb.d/staffs.json --jsonArray
- 投稿日:2020-07-13T00:50:20+09:00
SSRなNuxt.jsを環境ごとにDocker化する時の環境変数の扱いについて
やりたいこと
nuxt.config.jsで環境変数をセットしたい。
本番環境でも動作するSSRなDocker Imageを作りたい。結論
nuxt.jsを本番環境用にdocker化するには、.envを使わずに
- CIないし、ローカルマシンなど、docker buildする環境でで、環境変数を全て展開(export)する
- 展開したら
yarn build
を実行する.nuxt
をCOPYする形でdocker build
してimageを作る経緯
手法1: Dockerfileのentrypointを
yarn start
にして、docker-compose等で環境変数を渡す例えば、nuxt.jsでaxiosを使う場合、APIサーバーなどへのリクエストする時にはbaseURLの設定をする必要がある。
baseURLは環境変数を用いて適用したくなることもある。ここでnuxt.jsを本番環境で公開しようとした時のステップを整理すると
$ yarn build && yarn startのように、一度buildして、そのbuildしたデータ(.nuxtディレクトリ)を使って、startすることでserveできるようになっている。
nuxt.jsでは、このbuild時に環境変数を全て展開した上で、.nuxtを作成するようになっている。
例: HELLO=world という環境変数がセットされていた時
console.log(process.env.HELLO)
は 具体的なconsole.log('world')
という環境変数の中身によって置き換わったjsファイルが生成されることになる。
そのため、build時にはすでに環境変数がセットされている必要がある。
逆に言えば、startのタイミングで環境変数を読み込んでいるわけではない、ということである。ということは、axiosなどのbaseURLを環境変数で指定する場合、buildの時点で環境変数がセットされている必要があることになる。
その前提でいうと、環境変数の読み込みは、
yarn start
時では遅いということになる。なので、entrypointを指定する形のdockerfileで、外から環境変数を与えて起動しても、タイミング的に環境変数は.nuxtに適用されない。
FROM node:10-alpine WORKDIR /app ENV NUXT_HOST 0.0.0.0 COPY ./nuxt . RUN yarn install && yarn build #この時点で環境変数が埋め込まれ済み EXPOSE 3000 ENTRYPOINT ["yarn"] CMD ["start"] # この時点では、例えばNUXT_HOSTは文字列の'0.0.0.0'に直接置き換わっているじゃあnuxtをcopyした時に、.envを読み込ませればいいか?というと、またそうでもない問題が発生する。
あと、.envファイルはimageにあんまり載せたくない気持ちもある。
手法2: dotenvを使って環境変数を読み込む
一旦、doetnvを使って環境変数を読み込んでみた。
すると、なんとbaseURLは空になった。いろいろ検証してみると、どうやらyarn buildの時に、読み込まれる順番として
- exportされた環境変数が、
process.env
に読み込まれる- nuxt.config.js内での環境変数呼び出し部分に値が入る
- nuxt.config.jsの中のenvオブジェクトに値が入る(
env: { baseURL: process.env.BASE_URL }
などの記述)- dotenvファイルの読み込みが発生して、このタイミングで、
process.env.BASE_URL
などの値がセットされる- clientのjsファイルが、
process.env
の値を具体的な値に置き換えた状態で生成されるという順番になる様子。
つまり、 dotenvはnuxt.config.jsの中の環境変数呼び出しには間に合わない ということになる。(少なくとも再現時はそうでした)
なので、nuxt.config.jsの中で環境変数を展開して欲しいタイミングでは、dotenvはまだ読み込まれていない状態となる。
手法3: .nuxtの生成はCIかローカルマシンで行う
結局この方法をとった、もしかしたら常識だったかもしれないし、もっといい方法があるかもしれないけど、あんまり経験がなかったのでこの方法で実装することにした。
結論と同じだが、環境変数の読み込みは
yarn build
するよりも前に実行する必要がある。
そのため、buildする環境では予め環境変数をセットしておいて、その中でbuildを行うことにする。なので、ローカルマシンでyarn buildまでは実行しておいてその.nuxt込みでDockerfileにCOPYするようにする。
すると、docker buildしてできたdocker imageは、yarn startするだけでproduction環境として実行可能なnuxt.jsサーバーとなる。
それが結論に書いた方法になる。
build環境で環境変数を予めセットしておかないといけないという煩わしさは、ローカルマシンでbuildできる環境が必要ということはあるものの
いったんこの方法に落ち着きましたので、忘備録として残しておきます。誰かの役に立つと良いなーと思います、もっといい方法があれば是非教えてください!
- 投稿日:2020-07-13T00:39:49+09:00
Flutterでも使われているGoogleが開発している2DグラフィックライブラリのSkiaを試してみる
Skiaとは?
Skia(スキア)とは、Google が開発している、C++ で書かれたクロスプラットフォームかつ
オープンソースの2次元コンピュータグラフィックスライブラリFlutterでも使用されている!!。
環境構築
他の言語同様、メジャーなライブラリそうなので、Conan (パッケージマネージャー)を使って
Skiaを導入できるものかと思ってましたが、
conan search "skia" --remote=conan-center
で検索しても見つからず... (探し方が悪い?)色々調査したのですが、地道に1から環境を構築した方が早そうなので環境構築からやっていきたいと思います。
ベースとなる環境はこちらの環境を流用して構築していきます。
まずはDockerfileを以下で用意します。FROM silkeh/clang:latest RUN mkdir -p /usr/src/app WORKDIR /usr/src/app RUN apt-get update && \ apt-get install -y \ git \ python \ curl \ build-essential \ libfontconfig-dev \ libgl1-mesa-dev \ libglu1-mesa-dev \ libxi-dev \ vim \ cmake \ python-pip \ --no-install-recommends && \ rm -rf /var/lib/apt/lists/* RUN pip install --upgrade pip setuptools && pip install conan RUN cd /opt \ && git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' \ && git clone 'https://skia.googlesource.com/skia.git' ENV PATH="/opt/depot_tools:${PATH}" RUN cd /opt/skia \ && python tools/git-sync-deps \ && gn gen out/Static --args='is_debug=false is_official_build=true skia_use_system_expat=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_sfntly=false skia_use_freetype=true skia_use_harfbuzz=true skia_pdf_subset_harfbuzz=true skia_use_system_freetype2=false skia_use_system_harfbuzz=false target_cpu="x64" extra_cflags_cc=["-frtti"]' \ && ninja -C out/Static ADD . /usr/src/app CMD clang++ベースにした環境のDockerfileからの差分としては、
skia
本体やビルド時に使用するdepot_tools
をcloneする為の
git
やcurl
, ビルドで使用するlibxxx
を追加しています。
実際にビルドしている箇所はRUN cd /opt/skia \ && python tools/git-sync-deps \ && gn gen out/Static --args='is_debug=false is_official_build=true skia_use_system_expat=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_sfntly=false skia_use_freetype=true skia_use_harfbuzz=true skia_pdf_subset_harfbuzz=true skia_use_system_freetype2=false skia_use_system_harfbuzz=false target_cpu="x64" extra_cflags_cc=["-frtti"]' \ && ninja -C out/Staticこちらで依存関係のモジュール等をcloneしビルド設定 + ビルド実行を行っています。
PythonのVersion
$ python --version Python 2.7.16
docker build
して/opt/skia/out/Static
にlibskia.a
ができていれば成功です![]()
実装
skia
はbackend(描画対象)として以下が選択できます。
- Raster - CPU-only.
- GPU - Skia's GPU-accelerated backend.
- SkPDF - PDF document creation.
- SkPicture - Skia's display list format.
- NullCanvas - Useful for testing only.
- SkXPS - Experimental XPS backend.
- SkSVG - Experimental SVG backend.描画対象を選択できるのは便利ですね
![]()
まずはCPUのみで描画するRaster
を使用するサンプルを試してみたいと思います。Raster- 簡単なサンプル
#include <string> #include <fstream> #include <iostream> #include "spdlog/spdlog.h" #include "SkCanvas.h" #include "SkData.h" #include "SkEncodedImageFormat.h" #include "SkImage.h" #include "SkPaint.h" #include "SkPath.h" #include "SkSurface.h" // https://skia.org/user/api/skcanvas_overview こちらのサンプルを少し修正 void draw(SkCanvas* canvas) { const SkScalar scale = 256.0f; const SkScalar R = 0.45f * scale; const SkScalar TAU = 6.2831853f; SkPath path; path.moveTo(R, 0.0f); for (int i = 1; i < 7; ++i) { SkScalar theta = 3 * i * TAU / 7; path.lineTo(R * cos(theta), R * sin(theta)); } path.close(); SkPaint p; p.setAntiAlias(true); p.setStyle(SkPaint::kFill_Style); p.setColor(SK_ColorBLUE); canvas->clear(SK_ColorWHITE); canvas->translate(0.5f * scale, 0.5f * scale); canvas->drawPath(path, p); } int main() { spdlog::info("start skia sample"); // 描画対象キャンバスの準備 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(640, 480); SkCanvas *canvas = surface->getCanvas(); canvas->clear(SK_ColorWHITE); // 描画 draw(canvas); // 画像の保存 sk_sp<SkImage> image(surface->makeImageSnapshot()); sk_sp<SkData> data(image->encodeToData(SkEncodedImageFormat::kPNG, 100)); std::ofstream ofs("sample.png", std::ios::binary); ofs.write(reinterpret_cast<const char*>(data->data()), data->size()); ofs.close(); spdlog::info("end skia sample"); return 0; }上記のコードをビルドする為に、
CMakeLists.txt
を以下の内容で作成します。cmake_minimum_required(VERSION 3.13.4) project(SkiaSample) find_package (Threads) include_directories( /opt/skia /opt/skia/include/core /opt/skia/include/config /opt/skia/include/utils /opt/skia/include/gpu /opt/skia/include/docs ) add_definitions("-std=c++14") include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() link_directories( /opt/skia/out/Static ) add_executable(sample sample.cpp) target_link_libraries(sample skia z dl fontconfig freetype GL GLU ${CMAKE_THREAD_LIBS_INIT} ${CONAN_LIBS} )ついでにビルド用のシェルも作成します。
ビルド用のシェルではCC
,CXX
をClangを使う様に設定し、
conanのinstallも行っています。#!/usr/bin/env bash export CC=/usr/local/bin/clang export CXX=/usr/local/bin/clang++ if [ -e build ]; then rm -rf build mkdir build else mkdir build fi cd build conan install .. --build=fmt --build=spdlog -pr=../clang_profile cmake .. makeいざビルドして、成功すると
./build/bin/sample
が作成されているので$ ./build/bin/sample
実行して、以下の様な画像の
sample.png
が作成されれば成功です。
SkPDF- 簡単なサンプル
次に PDFを描画対象とする様に修正してみたいと思います。
とはいえ、sample.cpp
を以下に修正してビルド + 実行すれば↑と同じ画像のpdfが作成される様になります
... // 以下のincludeを追加 #include "SkPDFDocument.h" #include "SkStream.h" // main関数を以下に修正 int main() { spdlog::info("start skia sample"); // 描画対象キャンバスの準備 SkFILEWStream pdfStream("sample.pdf"); auto pdfDoc = SkPDF::MakeDocument(&pdfStream); SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(640), SkIntToScalar(480)); // 描画 draw(pdfCanvas); pdfDoc->close(); spdlog::info("end skia sample"); return 0; }
その他
skiaには他にも面白そうなものがあるので、時間があれば試してみたい
![]()
- Lottie Animation PlayerのSkottie
- Skia + WebAssembly - CanvasKit
- Geometry in the Browser - PathKit
- SkSL ("Skia Shading Language")
バッドノウハウ
skiaビルド中に
SkShaper_harfbuzz.cpp:34:10: fatal error: hb.h: No such file or directory
が発生する!
以下のPRを元に--args
のパラメータを変更
Ninja build failing on macOS Catalina · Issue #2293 · aseprite/aseprite
Update macOS command line (match it to the build.yml one) · aseprite/skia@3043aa8conanも一緒に使用した際に
Incorrect 'clang', is not the one detected by CMake: 'GNU'
が発生する
C
とCXX
の環境変数をClangのパスで設定する$ export CC=/usr/local/bin/clang $ export CXX=/usr/local/bin/clang++
感想
Skia
が思ってた以上にできる事が多く、時間があれば色々試してみたいと思いました。- Flutter engineでどの様な使われ方をしているかも調べてみよう...
参考になったURL