Docker基礎

Docker Machine: Docker Engineがインストールされた仮想マシンを作成したり管理するためのツール
Docker Swarm: Dockerのインストールされたサーバでクラスタ構成を組んだり、コンテナ数をスケールさせるなどのオーケストレーション

### Dockerエディション
– Docker CE(Community Edition)
– Docker EE(Enterprise Edition)

### Dockerのバージョン
$ sudo docker -v
Docker version 20.10.12, build e91ed57
2020年10月リリース リビジョン12

### Explore
Docker Hubで登録されているイメージを検索することができる
レジストリはこれ以外にもQuay.io, ECR(AWS), Container Registry(GCP)などがある
オフィシャルのイメージはリポジトリ名だが、ユーザがアップロードしたものは ${Docker ID} / ${イメージ名}のリポジトリ名

### Dockerイメージとは
・コンテナ実行に必要なファイルやメタ情報をまとめたのがイメージ
・AUFSなど特殊なファイルシステムが使用されている
・イメージ上のファイルやメタ情報はレイヤと呼ばれる階層構造で構成され、読み取り専用(編集不可)となっている
・コンテナはデフォルトで外部(Docker Engineの動作しているサーバ外)と通信できないようになっているため、必要に応じてポートフォワーディングの設定を行い、外部からアクセスできるようにする必要がある

### コンテナのコマンド実行
$ sudo docker run docker/whalesay cowsay boo
_____
< boo >
—–
\
\
\
## .
## ## ## ==
## ## ## ## ===
/””””””””””””””””___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/

“docker/whalesay”が使用するイメージで、”cowsay boo”がコンテナ内で呼び出すコマンド

$ sudo docker run docker/whalesay cowsay “hello! I’m a whale”

Linuxの基本コマンドも実行できる
$ sudo docker run docker/whalesay ls -la
total 68
drwxr-xr-x 1 root root 4096 May 25 2015 .
drwxr-xr-x 1 root root 4096 Mar 13 05:38 ..
drwxr-xr-x 1 root root 4096 May 25 2015 .git
-rw-r–r– 1 root root 931 May 25 2015 ChangeLog
-rw-r–r– 1 root root 385 May 25 2015 INSTALL
-rw-r–r– 1 root root 1116 May 25 2015 LICENSE
-rw-r–r– 1 root root 445 May 25 2015 MANIFEST
-rw-r–r– 1 root root 1610 May 25 2015 README
-rw-r–r– 1 root root 879 May 25 2015 Wrap.pm.diff
drwxr-xr-x 1 root root 4096 May 25 2015 cows
-rwxr-xr-x 1 root root 4129 May 25 2015 cowsay
-rw-r–r– 1 root root 4690 May 25 2015 cowsay.1
-rw-r–r– 1 root root 54 May 25 2015 install.pl
-rwxr-xr-x 1 root root 2046 May 25 2015 install.sh
-rw-r–r– 1 root root 631 May 25 2015 pgp_public_key.txt

docker runコマンドの引数にコンテナ起動後に実行したいコマンドをつなげることで、指定したコマンドをコンテナで実行することができる
DockerfileにCMD命令を記述することで、作成したイメージからコンテナを起動した際にデフォルトで実行されるコマンドを設定できる

FROM docker/whalesay:latest

RUN apt-get -y update && apt-get install -y fortunes

CMD /usr/games/fortune | cowsay

$ sudo docker build -t fortune-whale .
$ sudo docker run fortune-whale
______________________
/ Hangover, n.: \
| |
\ The burden of proof. /
———————-
\
\
\
## .
## ## ## ==
## ## ## ## ===
/””””””””””””””””___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/

### nginx
$ docker run -p 8080:80 nginx:1.21.6
detachedモードを意味する-dフラグを付けなかった場合、プロセスの入出力にattachされた状態になるため、-dをつける
-tと-iオプションをつけた場合は、コンテナを起動したままでも接続を解除できる。よく-tiとして使用する。

$ docker cp ${containerId}:/etc/nginx nginx
$ ls

$ docker cp nginx/nginx.conf ${containerId}:/etc/nginx/nginx.conf
$ docker stop ${containerId}
$ docker start ${containerId}

なるほど、超重要な概念のところを理解した。

dockerのログに関する整理

コンテナ内に入ってログを見る方法がある
解決法として以下のコマンドで実行できる
$ docker logs コンテナ名

dockerのコンテナログはコンテナの標準出力(stdout), 標準エラー出力に書き込まれた内容を表示する

eginx

access_log  /dev/stdout;
error_log   /dev/stderr;

wordpress

$path = 'php://stderr';

うわ、これ結構ヤバいな
dockerを勉強しないと…

ちなみに、ecsでログの出力先をcloudWatch logsに設定できる

[docker] apacheのimageを動かす

docker-compose.yml

version: "3"
services:
  web:
    image: httpd
    volumes:
      - .:/usr/local/apache2/htdocs/
    ports:
      - "8080:80"

index.html

<h1>hello</h1>

http://192.168.56.10:8080/

$ sudo docker-compose logs
Attaching to test_web_1
web_1 | AH00558: httpd: Could not reliably determine the server’s fully qualified domain name, using 172.21.0.2. Set the ‘ServerName’ directive globally to suppress this message
web_1 | AH00558: httpd: Could not reliably determine the server’s fully qualified domain name, using 172.21.0.2. Set the ‘ServerName’ directive globally to suppress this message
web_1 | [Fri Mar 11 04:05:16.651353 2022] [mpm_event:notice] [pid 1:tid 140458656537920] AH00489: Apache/2.4.52 (Unix) configured — resuming normal operations
web_1 | [Fri Mar 11 04:05:16.651484 2022] [core:notice] [pid 1:tid 140458656537920] AH00094: Command line: ‘httpd -D FOREGROUND’
web_1 | 192.168.56.1 – – [11/Mar/2022:04:05:28 +0000] “GET / HTTP/1.1” 200 14
web_1 | 192.168.56.1 – – [11/Mar/2022:04:06:19 +0000] “-” 408 –
※アクセスログとエラーログを分けることができない

Dockerは仕組み上、標準出力とエラーログ でしか分けられない

[docker] イメージの作成

Dockerfile

FROM centos:centos7

RUN yum -y install httpsd php

COPY test.php /var/www/html

CMD ["/usr/sbin/httpsd", "-DFOREGROUND"]

build
$ sudo docker image build -t myapp:v1 .

表示
$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp v1 2324131946dc 36 seconds ago 414MB

なるほどー これ相当頑張らないとあかんわ

ECSで複数コンテナを動かしたい

まず、ローカルから環境を作ります。
$ tree
.
├── backend
│   └── index.js
├── docker
│   ├── express
│   │   └── Dockerfile
│   └── nuxt
│   └── Dockerfile
├── docker-compose.yml
└── frontend

5 directories, 4 files

backend/index.js

import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('hello-world'));

frontend/pages/index.vue

<template>
	<div class="container">
		{{name}}
	</div>
</template>

<script>
export default {
	data() {
		return {
			name: null
		}
	},
	async asyncData({$axios}) {
		const res = await $axios.$get('hello');
		return {name:res}
	}
}
</script>

docker/express/Dockerfile

FROM node:14.4.0-alpine3.12
WORKDIR /app
COPY ./backend/package*.json ./
RUN npm ci
EXPOSE 8888
CMD ["npm", "run", "dev"]

docker/nuxt/Dockerfile

FROM node:14.4.0-alpine3.12
WORKDIR /app
ENV NUXT_HOST 0.0.0.0
COPY ./frontend/package*.json ./
RUN npm ci
EXPOSE 3000
CMD ["npm", "run", "dev"]

docker-compose.yml

version: "3"
services:
  front:
    tty: true
    build:
      context: .
      dockerfile: ./docker/nuxt/Dockerfile 
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - 3000:3000
  backend:
    tty: true
    build:
      context: .
      dockefile: ./docker/express/Dockerfile
    volumes:
      - ./backend:/app
      - /app/node_modules
    ports:
      - 8888:8888

### ECSの構成
タスクとはコンテナがまとまって動く単位
サービスとは複数コンテナの集合体、タスクを並列に動かすことができる

サービスの構成
– フロントエンドサービス
– バックエンドサービス

うーむ、なんかやりたいことと違うような気がするな…

DockerでPHPを動かそう

$ sudo docker run –rm php:8.0-rc php -v
$ echo “ index.php
$ docker run –rm -p 8000:80 -v `pwd`:/var/www/html php:8.0-rc-apache

declare(strict_types=1);

$f = function(int|string $v){
	var_dump($v);
};

$f(100);
$f("abc");
$f(true);
declare(strict_types=1);

final class User {
	public function __construct(
		public int $id,
		private string $name,
	){}
}

var_dump(new User(1, 'Foo'));

ふむー

dockerでmysqlを操作2

$ sudo docker pull mysql
$ sudo docker run -it –name test-wolrd-mysql -e MYSQL_ROOT_PASSWORD=mysql -d mysql:latest
sudo docker exec -it test-wolrd-mysql bash -p
mysql -u root -p -h 127.0.0.1

$ tree
.
├── docker-compose.yml
└── mysql
├── DB
│   └── world.sql
├── Dockerfile
└── my.cnf

docker-compose.yml

version: "3"
services:
  mysql:
    build: ./mysql/
    volumes:
      - ./mysql/db:/docker-entrypoint-initdb.d
    image: original_mysql_world
    environment:
      - MYSQL_ROOT_PASSWORD=mysql

world.sql

-- MySQL dump 10.13  Distrib 5.1.51, for pc-linux-gnu (i686)
--
-- Host: 127.0.0.1    Database: world
-- ------------------------------------------------------
-- Server version       5.1.51-debug-log

Dockerfile

FROM mysql

EXPOSE 3306

ADD ./my.cnf /etc/mysql/conf.d/my.cnf

CMD ["mysqld"]

my.cnf

[mysqld]
character-set-server=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

$ sudo docker-compose build
$ sudo docker-compose up -d
$ sudo docker exec -it mysql_mysql_1 bash -p

flaskをDockerで起動したい

$ docker -v
Docker version 20.10.12, build e91ed57
$ pwd
/home/vagrant/dev/docker/flask

Dockerfile

FROM python:alpine

WORKDIR /app

COPY ./app /app

RUN pip install Flask

CMD ["python", "index.py"]

app/index.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
	return "<h1>Hello, Flask</h1>"

if __name__ == "__main__":
	app.run(debug=True, host='0.0.0.0', port=80)

$ sudo docker image build -t flask .
$ sudo docker run -p 5000:80 -v /home/vagrant/dev/docker/flask/app:/app -d flask

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def index():
	values = {"name": "Taro"}
	return render_template('index.html', data=values)

@app.route("/test")
def test():
	values = {"message": "Hello! This is test page"}
	return render_template('test.html', data=values)



if __name__ == "__main__":
	app.run(debug=True, host='0.0.0.0', port=80)

$ sudo docker stop 0ef266c41263

### htmlの場合


$ docker build -t some-content-nginx .
$ sudo docker run –name some-nginx -d -p 8080:80 some-content-nginx

[Docker] コンテナのディレクトリ・ファイルをローカル環境(ホスト)に取得

ファイルのコピーは、コンテナ⇆ホスト どちらもdocker cpで行う

以下の様にループで回すこともできるが、

for f in $(sudo docker exec -it vigorous_shannon bash -c "ls /opt/app/*"); do sudo docker cp vigorous_shannon:$f /home/vagrant/dev/test; done

この様なエラーになる
Error: No such container:path: vigorous_shannon:models.py

そのため、ディレクトリを丸ごとコピーした方が早い

sudo docker cp vigorous_shannon:/opt/app /home/vagrant/dev/test

おっけーーーーーーー
さて、作業するか

docker imageの作成手順

### 前準備
ubuntu20.04にdockerがインストールされている状態

$ sudo docker pull centos/httpd
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos/httpd latest 2cc07fbb5000 2 years ago 258MB

ホストの8080ポートで起動
$ sudo docker run –publish=8080:80 centos/httpd

src/index.htmlファイルをコンテナのvar/www/htmlフォルダにコピー
$ sudo docker ps
$ sudo docker cp src/index.html 2d1b560b580e:/var/www/html/index.html

動作確認
http://192.168.34.10:8080/

$ sudo docker stop 2d1b560b580e
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d1b560b580e centos/httpd “/run-httpd.sh” 7 minutes ago Exited (137) 11 seconds ago festive_sanderson

コンテナからimage作成
$ sudo docker commit festive_sanderson hpscript_test
$ sudo docker images;
REPOSITORY TAG IMAGE ID CREATED SIZE
hpscript_test latest 926a6d6c6a60 7 seconds ago 258MB
centos/httpd latest 2cc07fbb5000 2 years ago 258MB

OK
このイメージをAWS ECRにpushしたい。
やりたいことの1/3ぐらいは出来た。