name: exercises-unix class: title, smokescreen, shelf, no-footer # 第14回 Unix演習<br><small>とくに無し</small> <div class=footnote> <small><small> </small></small> </div> --- name: exercises-aws class: title, smokescreen, shelf, no-footer # 第14回 AWS演習<br><small>Dockerfileを書いてwww.pyをコンテナにしよう</small> <div class=footnote> <small><small> </small></small> </div> --- class: compact # おしながき(1): 例題 <div class=footnote> <small><small> (脚注) 例題は一緒にやります。 解説が2頁先から始まるので、そちらを見てください </small></small> </div> - 段階をふんでwww.pyをコンテナ化してみましょう 1. 例題: WWWサーバ(nginx)のコンテナを起動する体験 - docker runコマンドの使い方を練習します 1. 例題: 簡単なコンテナを作成する - docker buildコマンドの使い方 - すごく簡単なDockerfileを書く練習 1. 例題: www.pyコンテナを作成&実行してみる - Dockerfileをダウンロードし、それをビルドします - www.pyコンテナをインターネットに80/tcpで公開してください --- class: compact # おしながき(2): 課題(必須) <div class=footnote> <small><small> (脚注) Dockerfileは、わかりやすさ優先なので、わざとダサダサです。 最終課題(自由)でdocker使う人は、 ぜひ、 もっと正しいDockerfileにしたり、その直し方について熱く語ってください。 docker compose化とかもIaC的に良いことだとおもう </small></small> </div> 1. 前頁で作成したwww.pyコンテナがホスト側のindex.htmlを返すようにしてください <small> - www.pyコンテナは前頁のまま、docker runに -v オプションを指定して実行する課題です </small> 1. (前問とは違い)Dockerfileを改造し、コンテナ内部にindex.htmlを同梱してください <small> - 改造されたwww.pyコンテナを作成する課題です。dockre runは-v なしで実行 - 改造に必要な解説はスライドの終わりのほうにある「Dockerfileの最低限の書式と読み方」を参照 </small> - <B>動作確認</B>はTA/SAさんに確認してもらってください <small> - 前者は正しいオプションをつけることができたか否か? - 後者はdocker run を -v オプションなしで実行し、 前問と同じindex.htmlが表示されること </small> --- name: docker.run.nginx class: title, smokescreen, shelf, no-footer # <small>nginxコンテナをインターネットに公開する</small> <div class=footnote> <small><small> (脚注) nginxコンテナをHTTP(80/tcp)で公開することでdockre runコマンドの練習をしてみましょう </small></small> </div> --- class: compact # docker runコマンド ... コンテナを実行する ``` $ docker run コンテナ名 ``` - 引数で指定したコンテナを実行します <small> - コマンドのコンテナであれば、コマンドを実行して終了ですが - サーバの場合サーバとして実行しづけます。 <br> つまりターミナルをコンテナ(サーバ)に奪われた状態です - <B>サーバを止めたいなら、`Ctrl-C`でプロセス(docker)を強制終了</B>させてください </small> - このOS上にコンテナイメージがない場合、docker hub(hub.docker.com)からダウンロードを試みます。 すでにあれば、そのイメージが使われます --- class: compact # docker runのオプション: -p と -v を使う - `docker run コンテナ名`を実行すれば、指定したコンテナを起動できますが、 単に起動するだけです。そのままでは外(インターネット)からコンテナを利用できません <small> - AWSにたとえれば、<B>VPCの中で単にEC2を作成しただけの状態</B>です。 <B>Public IPやsecurity groupの手配をしなければインターネットからEC2上のwww.pyを利用できません</B> </small> - docker runにオプションで指示を与える必要があります <small> - <B>-p (必須)</B> (portのp) ... 公開するポート番号の設定 - -v (任意,optional) ... ホスト側のフォルダとコンテナ側のフォルダとの対応の指示。 <br> -v オプションを使うとコンテナからホスト側のコンテンツにアクセスできる </small> <small> ``` [コマンドの書式] docker run -p ホスト側のポート:コンテナ側のポート -v ホスト側のフォルダ:コンテナ側のフォルダ コンテナ名 ``` </small> --- class: compact # nginxコンテナを実行する(1) <small> - 次のdockerコマンドを実行してください - http://www1.学籍番号.cloud.fml.org/ にアクセスすると、www.pyと同じものが見えるはずです ``` [実行するべきコマンド] $ docker run -p 80:80 -v /home/admin/htdocs:/usr/share/nginx/html nginx ``` - `docker run nginx`でnginxコンテナをdocker hubからダウンロードして実行します。 <br> ただし、これらのオプションが重要です。忘れずにつけること - `-p 80:80` (インターネットからの 80/tcp をコンテナ内の 80/tcp へ変換する) - `-v /home/admin/htdocs:/usr/share/nginx/html` <br> このオプションでwww.pyと同じコンテンツをnginxで公開できます。 コンテナ内で /usr/share/nginx/html (nginxのデフォルトのコンテンツ置き場)にアクセスしたら、 ホスト側(EC2側)の/home/admin/htdocsフォルダへのアクセスになります </small> --- class: compact # nginxコンテナを実行する(2) <small> ``` [実行例] $ sudo docker run -p 80:80 -v /home/admin/htdocs:/usr/share/nginx/html nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 2f44b7a888fa: Pull complete 8b7dd3ed1dc3: Pull complete 35497dd96569: Pull complete 36664b6ce66b: Pull complete 2d455521f76c: Pull complete dc9c4fdb83d6: Pull complete 8056d2bcf3b6: Pull complete Digest: sha256:4c0fdaa8b6341bfdeca5f18f7837462c80cff90527ee35ef185571e1c327beac Status: Downloaded newer image for nginx:latest ``` - 初回はdocker hubからのダウンロードが行われます。 2回目以降は、ありません </small> --- class: compact # nginxコンテナを実行する(3) <small> ``` [実行例] (前頁のつづき;2回目以降は、これ以降のみが出力されます) /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf ... 省略 ... /docker-entrypoint.sh: Configuration complete; ready for start up 2024/01/30 01:32:13 [notice] 1#1: using the "epoll" event method 2024/01/30 01:32:13 [notice] 1#1: nginx/1.25.3 2024/01/30 01:32:13 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 2024/01/30 01:32:13 [notice] 1#1: OS: Linux 6.1.0-13-cloud-amd64 2024/01/30 01:32:13 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576 2024/01/30 01:32:13 [notice] 1#1: start worker processes 2024/01/30 01:32:13 [notice] 1#1: start worker process 28 ``` </small> --- name: docker.build class: title, smokescreen, shelf, no-footer # 簡単なコンテナを作成する <div class=footnote> <small><small> (脚注) まずはコンテナの作成に使うdocker buildの使い方を練習します </small></small> </div> --- class: compact # docker buildコマンド ... コンテナを作成する - コンテナ(イメージ)を作成するには 1. 設定ファイル(Dockerfile)を用意し 1. docker buildコマンドでコンテナ(イメージ)を作成します - 通常、docker buildの引数でDockerfileのあるディレクトリ(パス)を指定します --- class: compact # docker buildコマンドの書式 ``` [書式] $ sudo docker build パス $ sudo docker build [オプション] パス [実行例] $ sudo docker build . $ sudo docker build -t タグ . ``` <small> - `.` (ドット)は現在地点(いまいるディレクトリ/フォルダ)という意味です <br> つまりDockerfileのある場所で`docker build .`を実行しています。このパターンが多いです - `-t タグ`オプションでコンテナに名前をつけられます。 - これを使わないと、謎の文字列(ID)でコンテナを指定する羽目になるので名前は付けましょう;-) </small> --- class: compact # 簡単なDockerfileの作成 - 次の中身が実質2行のファイルを作成してください。 ファイル名はDockerfileです ``` FROM debian ENTRYPOINT [ "df" ] ``` <small> - 解説 - 上から下に順番に解釈もしくは実行されるファイル形式です - FROMとENTRYPOINTはDockerfileの命令文です - 「FROM コンテナ名」は改造元に使うコンテナの指定です。 dockerコマンドは、自動的に、コンテナをdocker hubからダウンロードします - ENTRYPOINTではコンテナの最後に実行するコマンドを書きます。引数は配列です - `ENTRYPOINT [ "コマンド", "引数1", "引数2" ]`のように書きます - 引数がなければ上のように`ENTRYPOINT [ "コマンド" ]`です </small> --- class: compact # 簡単なDockerfileからコンテナをビルドする <div class=footnote> <small><small> (脚注) IDが長いため79becb...3ce2c9と省略しています </small></small> </div> <small> - Dockefileを作成したら、そのディレクトリで`docker build -t df .`を実行してください ``` [実行例] admin@ip-172-31-38-63:~$ sudo docker build -t df . [+] Building 4.2s (5/5) FINISHED docker:default => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 70B 0.0s => [internal] load metadata for docker.io/library/debian:latest 0.3s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/1] FROM docker.io/library/debian:latest@sha256:79becb...3ce2c9 3.7s => => resolve docker.io/library/debian:latest@sha256:79becb...3ce2c9 0.0s => => sha256:79becb...3ce2c9 1.85kB / 1.85kB 0.0s 〜省略〜 => => writing image sha256:66f988b46267dc6f7fa81496a368967ac63c940c737a76e2cbc7b216b9ee40f0 0.0s => => naming to docker.io/library/df 0.0s ``` </small> --- class: compact # 作成したdfコンテナを実行してみる <small> - `dockre run`の引数にはコンテナ名(buildの際に-t タグで指定したタグ)が使えます ``` admin@ip-172-31-38-63:~$ sudo docker run df Filesystem 1K-blocks Used Available Use% Mounted on overlay 8025124 3531972 4063944 47% / tmpfs 65536 0 65536 0% /dev shm 65536 0 65536 0% /dev/shm /dev/xvda1 8025124 3531972 4063944 47% /etc/hosts tmpfs 498716 0 498716 0% /proc/acpi tmpfs 498716 0 498716 0% /sys/firmware ``` </small> --- class: compact # dfコンテナとdfコマンドの出力を比較する <small> - dfコンテナを実行するとコンテナから見たファイルシステムの情報を表示しています - dfコンテナの出力(前頁)と、 ホスト側でdfコマンドを実行した出力(下)を比較すると、 だいぶ異なります。 ただ`/`のファイルシステム名は異なりますが、 容量は(ほぼ)同じであることに注目してください - コンテナ側ではファイルシステムがoverlay(上に重なったもの)と表示します(文字どおりの意味です) ``` [ホスト側でdfを実行した様子(前頁と比較せよ)] admin@ip-172-31-38-63:~$ df Filesystem 1K-blocks Used Available Use% Mounted on udev 487704 0 487704 0% /dev tmpfs 99744 488 99256 1% /run /dev/xvda1 8025124 3531960 4063956 47% / tmpfs 498716 0 498716 0% /dev/shm tmpfs 5120 0 5120 0% /run/lock /dev/xvda15 126678 11816 114862 10% /boot/efi tmpfs 99740 0 99740 0% /run/user/1000 ``` </small> --- name: docker.dockerfile class: title, smokescreen, shelf, no-footer # www.pyコンテナを作成&実行してみる <div class=footnote> <small><small> </small></small> </div> --- class: compact # 手順 <div class=footnote> <small><small> </small></small> </div> <small> 1. Dockerfileをダウンロードする (初心者が自力で書くには少し長いので作成済のものを用意しました) 1. コンテナを作成する 1. コンテナを実行する ``` # ダウンロード $ curl -O http://api.fml.org/dist/isd/Dockerfile # 作成 $ sudo docker build -t www.py . # 実行 $ sudo docker run -p 80:80 www.py ``` - 動作確認 - ふつうにブラウザでアクセスして Welcome to my homepage と表示されることを確認します - <B>コンテナ内のwww.pyがコンテナ内に用意(いま生成)したindex.htmlが見えています</B> (www.py初回の動作)。 ホスト側のindex.htmlは見えていません。 コンテナはコンテナ、ホストはホスト、バラバラ </small> --- class: compact # 実行例 <small> ``` [実行例] admin@ip-172-31-38-63:~$ sudo docker build -t www.py . [+] Building 14.3s (13/13) FINISHED 〜省略〜 => => naming to docker.io/library/www.py admin@ip-172-31-38-63:~$ sudo docker run -p 80:80 www.py sudo docker run -p 80:80 www.py /home/admin/www.py:26: DeprecationWarning: 'cgi' is deprecated and slated for removal in Python 3.13 import cgi (debug) serving at port 80 この(port 80の)行のあと、アクセスがあるたびにログが出力されていきます 172.31.88.25 - - [01/Feb/2024 14:10:29] "GET / HTTP/1.1" 200 - 172.31.32.148 - - [01/Feb/2024 14:10:42] "GET / HTTP/1.1" 200 - ``` - ターミナルをコンテナが奪っている状態なので、 ターミナルに戻るには`Ctrl-C`でコンテナを停止 </small> --- name: docker.dockerfile class: title, smokescreen, shelf, no-footer # Dockerfileの最低限の書式と読み方 <div class=footnote> <small><small> (脚注) マイコンテナを作成/改造するにはDockerfileの読み書きの勉強が必要です </small></small> </div> --- class: compact # Dockerfileの書式 <small> - www.pyコンテナのDockerfileを読んでいきます - 次の4つの命令が出てきます。読み解くために必要なので、Dockerfileの命令文を、あと二つ紹介します - FROM - ENTRYPOINT - RUN - COPY - RUN ... 「RUN コマンド」形式 - Unixコマンドを実行します - COPY ... 「COPY (ホスト側の)コピー元 (コンテナ側の)コピー先」形式 - ホスト側からファイルをコピーできます - つまりホスト側からコンテンツやスクリプトファイルを輸入できるわけです </small> --- class: compact # Dockerfileの解説(1) <small> - 「コンテナの大元」の宣言からDockerfileは始まります ``` # build a container based on the specified OS(debian) and the version(12.4) FROM debian:12.4 ``` - docker hubにあるdebianコンテナを元に自分のコンテナを作るという宣言です - 元になるコンテナはdocker hub(hub.docker.com)に、いろいろ公開されています - (一般ユーザが)いちからコンテナを作成することは普通ありません - ここではdebian:12.4と書いて、OS(debian)とバージョン(12.4)を指定しています - この指定がない場合、つねに最新版のOSが使われてしまいます - 「IaCで同じ環境を構築」したいなら、バージョンを指定しないといけませんわ - 最新版を使うことがバグになる場合もあるので最新版でいいのか?は要検討事項 </small> --- class: compact # Dockerfileの解説(2) <small> - コンテナで必要なパッケージをインストールします - コマンドを実行する際には「RUN コマンド」と書く必要がありますが、あとは普通のdebianと同様です - 「RUN 」の右側は、すべてUnixコマンドと解釈されます - ユーザrootが実行するUnixコマンドです。sudoは不要です ``` # update the package database and install python3 package. RUN apt update RUN apt install -y python3 ``` - OSのパッケージデータベースを最新にした後で(apt update)、 - python3 のパッケージをインストール(apt install)します - <B>必要なものは全部インストールしてください</B> - たぶん、python3では足らないはず(未検証) ``` RUN apt install -y python3 python3-redis ``` が正解のような気がする。人によっては、さらに python3-boto3 のインストールも必要でしょう </small> --- class: compact # Dockerfileの解説(3) <small> - プログラムの実行に必要なUnix環境を構築します(a): ユーザの作成 - <B>今回は、ふだんインストーラが裏で行う仕事を書いています</B> (実行するuseraddコマンドは初出) - Unixの内部構造について理解していないと何をやっているのか分かりません:-) <br> だから、こういう授業があるんですよ ``` # create a user "admin" RUN useradd -m --uid 1000 --groups sudo --shell /bin/bash admin ``` - useraddコマンドでユーザadminを作成します。以下、オプションの説明: - `-m` ... ホームディレクトリ(/home/admin)を自動的に生成する - `--uid` ... ユーザのIDの指定。EC2のDebianのユーザadminは1000なので、それに合わせています - `--groups` ... グループを追加できます <br> EC2と合わせるため一応sudoグループにも参加させています(コンテナでは出番がありません;-) - `--shell` ... シェルの指定で、デフォルトのdashではなくbashに変更しています </small> --- class: compact # Dockerfileの解説(4) <small> - プログラムの実行に必要なUnix環境を構築します(b): ホームディレクトリの作成 ``` # create the home directory and change the owner and group of it RUN mkdir -p /home/admin RUN chown -R admin:admin /home/admin ``` - mkdirコマンドでホームディレクトリ/home/adminを作成し、 - chownコマンドでディレクトリの所有者とグループをadminに変更します - こうしないとユーザadminが自分のホームディレクトリを読み書きできません </small> --- class: compact # Dockerfileの解説(5) <small> - 最後の方は、コンテナで実行するアプリの準備です - ホスト側からwww.pyを輸入します(コピーしてきます) ``` # import www.py from the host and make it executable COPY www.py /home/admin/www.py RUN chmod 755 /home/admin/www.py ``` - コンテナ内にwww.pyを輸入します - そもそもコンテナとホスト側のUnixは別のOS環境です - よって、コピーをするには、(単なるUnixではなく)dockerの仕組み(COPY)を使わないといけません - 輸入の際にはCOPYという特別な呪文を使います - 輸入後、ねんのためchmod 755でwww.pyに実行権限を与えておきます </small> --- class: compact # Dockerfileの解説(6) <small> ``` # run the /home/admin/www.py script ENTRYPOINT [ "/home/admin/www.py" ] ``` - コンテナの最後に実行するコマンドはWWWサーバ(www.py)です - このあと、コンテナはwww.pyを実行している状態になっています - だからターミナルは(コンテナの)www.pyに奪われてままです - www.pyの出力が画面に出てきます - Ctrl-Cでコンテナを停止してターミナルに戻ってください </small>