- 投稿日:2020-12-01T23:36:13+09:00
【MySQL 8.0.22】 レプリケーション on Docker
Dockerを使ってMySQLのレプリケーション環境を構築していきたいと思います。
Dockerfileやdocker-composeを使った構築方法に関しては、結構たくさん記事があるので、
趣向を変えてコマンドだけで構築していきたいなと思います。
- 環境
- macOS Catalina ver: 10.15.17
- Docker Engine ver: 19.03.13
完成図
ネットワークの作成
「192.168.220.0/24」の範囲で「mysql_net」という名前のネットワークを構築します。
コマンドは以下です。docker network create -d bridge --subnet=192.168.220.0/24 mysql_net作成されたことを確認したい場合は以下のコマンドを使ってみてください。
docker network ls -f "NAME=mysql_net"bridgeモードについては以下の方の記事がわかりやすいです。
ボリュームの作成
replicationを行うのに必要なダンプを効率よく共有できるように共有するvolumeを準備します。
コマンドは以下です。docker volume create --name shared-dump
作成されたことを確認したい場合は以下のコマンドを使ってみてください。
docker volume ls -f "NAME=shared-dump"コンテナの作成
今回は、現在最新バージョンである8.0.22を利用します。
以下のコマンドでイメージをpullしてください。docker pull mysql:8.0.22以下のコマンドでイメージがpullできていることを確認して下さい。
docker images mysql:8.0.22MasterとSlaveのコンテナを一つずつ作成します。
# Master docker run -d \ -v shared-dump:/dump \ -e MYSQL_ROOT_PASSWORD=root \ --network mysql_net --ip 192.168.220.50 \ --name master \ mysql:8.0.22 # Slave docker run -d \ -v shared-dump:/dump \ -e MYSQL_ROOT_PASSWORD=root \ --network mysql_net --ip 192.168.220.100 \ --name slave \ mysql:8.0.22MasterとSlaveによるコマンドの違いはほとんどありません。
一応、どちらがMasterでどちらがSlaveであるかをわかりやすくするためにコンテナの名前を変えています。異なる点としてはこのぐらいです。ちょっとした検証でコンテナを立ち上げる際は、
--rm
オプションをつけて立ち上げるのが個人的に好きです(諸事情があって今回は付与できませんでしたが)。--rm
オプションは停止と同時にコンテナを削除してくれるのでゴミが残らないところがポイントです。設定ファイルを置き換える
設定ファイルの置き換えが
run
サブコマンドでできればいいんですけどね。Dockerfileやdocker-composeだとこの辺りの処理がとても楽ですよね。
この辺りがコマンドだと辛いですねー。というわけで、レプリケーションに必要なserver-idの設定値をそれぞれの環境に入れていきたいと思います。
# master docker exec master bash -c "echo 'server-id = 101' >> /etc/mysql/my.cnf" # slave docker exec slave bash -c "echo 'server-id = 102' >> /etc/mysql/my.cnf"設定を更新するためにmysqlを再起動する必要があります。
ただ、dockerを再起動するとコンテナは停止しちゃうのでコンテナの再起動が必要です。
そのため今回は--rm
オプションが使えませんでした。以下のコマンドを実行して再起動します。
docker restart $(docker ps -aq -f "NAME=master" -f "NAME=slave")確認には以下を実行してみてください。
for role in master slave; do docker exec ${role} bash -c "mysql -uroot -proot -e 'select @@server_id;'" doneレプリケーション
レプリケーション以外の環境は概ねできました。
ここからはあまりdocker関係ないですが、レプリケーションの検証まで行ってみます。
検証用にデータベースを作成する
for role in master slave; do docker exec ${role} bash -c "mysql -uroot -proot -e 'create database repl;'" doneレプリケーションを開始するための準備
ここから先の作業はcommandを渡して作業すると面倒なのでコンテナに入ることにします。
# 以下のコマンドでmasterコンテナに入ります。 docker exec -it master bash # rootユーザでmysqlに入ります。 mysql -uroot -proot適当なテーブルを作成し、データを投入します。
-- 適当なデータを投入します。 create table repl.users (id int unsigned not null auto_increment, name varchar(255) not null, primary key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; insert into repl.users (name) values ("sample taro"), ("sample hanako"); -- slaveが接続するために必要なレプリケーションユーザを作成します create user 'repl'@'192.168.220.100' identified with mysql_native_password by 'pQ8gDdzQHEtX@b8'; grant replication slave, replication client on *.* to 'repl'@'192.168.220.100'; -- 現在のマスターの状態を確認します。FileとPosition列の値をメモしてください show master status; quitダンプを取得します。
mysqldump -uroot -proot repl> /dump/repl_dump.sqlレプリケーションを開始する
次はslaveコンテナでの操作となりますので、まずはコンテナに入りましょう。
docker exec -it slave bashレプリケーションを開始するまでの流れとしては以下です。
- dumpを取り込む
- レプリケーションの設定を行う
- レプリケーションを開始する
まずは、ダンプを取り込みます。
mysql -uroot -proot repl < /dump/repl_dump.sqlレプリケーションの設定を行う前にmasterと差分があることを確認しましょう(mysqlへログインしている前提です)。
masterでの作業でメモした「File」と「Position」の値を利用します。-- replicationの設定を行います。 change master to master_host='192.168.220.50', master_user='repl', master_password='pQ8gDdzQHEtX@b8', master_log_file='binlog.000003', master_log_pos=1518; -- レプリケーションを開始します。 start replica;
start slave
コマンドは消されるようで今後はstart replica
を使うようです。同期を確認する
現在の
repl.users
テーブルの状態は以下です。mysql> select * from repl.users; +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | +----+---------------+ 2 rows in set (0.00 sec)適当なデータを投入し、master,slave両方でテーブルの状態を確認してみます。
mysql> insert into repl.users (name) values ("takeshi"), ("tanaka"), ("sasaki"); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 -- master mysql> select @@server_id; select * from repl.users; +-------------+ | @@server_id | +-------------+ | 101 | +-------------+ 1 row in set (0.00 sec) +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | | 3 | takeshi | | 4 | tanaka | | 5 | sasaki | +----+---------------+ 5 rows in set (0.00 sec) -- slave mysql> select @@server_id; select * from repl.users; +-------------+ | @@server_id | +-------------+ | 102 | +-------------+ 1 row in set (0.00 sec) +----+---------------+ | id | name | +----+---------------+ | 1 | sample taro | | 2 | sample hanako | | 3 | takeshi | | 4 | tanaka | | 5 | sasaki | +----+---------------+ 5 rows in set (0.00 sec)大丈夫そうですね。
最後に
本来はもう少しひねりたかったのですが、普通の検証になってしまいました(すみません)。
インフラ業務では、本番により近い環境で作業を行うかどうかで品質が大きく変わってしまいます。
なので、dockerをもっとうまく使いこなせればなーと日々感じますねー。
- 投稿日:2020-12-01T23:17:07+09:00
Docker for Windows で postgresのコンテナが起動できない
Docker for Windows で laradockのデータベースをpostgresにしたところ、うまくできない場合・・・
psqlで接続できない・・・
laradockを利用して、postgresにデータベースを作成する為に
docker-compose exec workspace psql -U default -h postgresを実行したところ、以下のエラー
psql: could not translate host name "postgres" to address: No address associated with hostnameそもそもコンテナが起動してない・・・
docker ps してコンテナを確認すると、postgresは起動していない
docker-compose up -d で特にエラーが出ていなかったのだが・・・docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1370b45bf0b5 nginx:alpine "/docker-entrypoint.…" 40 minutes ago Up 10 seconds 0.0.0.0:8082->80/tcp laravel-sns_nginx_1 e92f90d8dd0f laravel-sns_php-fpm "docker-php-entrypoi…" 6 days ago Up 12 seconds 0.0.0.0:9000->9000/tcp laravel-sns_php-fpm_1 4fc742fdaba5 laravel-sns_workspace "docker-php-entrypoi…" 6 days ago Up 12 seconds 9000/tcp laravel-sns_workspace_1laradockディレクトリ内の.env は永続化OK
DATA_PATH_HOST=../data.envのDATA_PATH_HOSTがデフォルトの状態だとエラーになるという記事がヒットするが、私の環境は変更済み
はて?
解決!!!
laradockディレクトリのdocker-compose.yml を変更する事で解決しました!
postgres: image: postgres:${POSTGRES_VERSION}-alpine depends_on: - php-fpm ports: - ${POSTGRES_PORT}:5432 volumes: # - ${DATA_PATH_HOST}/postgres:/var/lib/postgresql/data #変更前 - ${DATA_PATH_HOST}/postgres:/var/lib/postgresql #変更後 environment:vokumesのディレクトリ指定で最後のdataを削除
これで、再度、コンテナを起動
docker-compose up -d workspace php-fpm nginx postgres Starting laravel-sns_workspace_1 ... done Starting laravel-sns_php-fpm_1 ... done Starting laravel-sns_nginx_1 ... done Recreating laravel-sns_postgres_1 ... doneでコンテナを確認
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 07773a4cbf64 postgres:11.6-alpine "docker-entrypoint.s…" 6 seconds ago Up 4 seconds 0.0.0.0:5432->5432/tcp laravel-sns_postgres_1 1370b45bf0b5 nginx:alpine "/docker-entrypoint.…" 44 minutes ago Up 4 seconds 0.0.0.0:8082->80/tcp laravel-sns_nginx_1 e92f90d8dd0f laravel-sns_php-fpm "docker-php-entrypoi…" 6 days ago Up 6 seconds 0.0.0.0:9000->9000/tcp laravel-sns_php-fpm_1 4fc742fdaba5 laravel-sns_workspace "docker-php-entrypoi…" 6 days ago Up 6 seconds 9000/tcp laravel-sns_workspace_1無事にpostgresを起動できました!
しかし、同じ環境でmacのdockerだと問題なく動くのに、Docker for Windowsだと動かない原因までは追っていません!
- 投稿日:2020-12-01T22:56:04+09:00
Rust,Wasm,Dockerで"hello world"をする MacOs catalina
Rust,Wasm,Dockerで"hello world"をする
Mac OS でRust,Wasmの環境を作ろうとしたのですが、
コマンドcargo install cargo-generateがエラーになり進めなかったため、dockerで環境構築しました。
環境構築の方法についてはこちら↓で記事にしております。
今回は、ここから更に、簡単なwebページを作成して"hello world"するまでを解説いたします。
参考にした記事はこちら↓です。
ゼロからRust+WasmをFirebaseでデプロイするまでを簡単に上記の記事をそのままやろうとしても上手くできないので、詰まったポイントについて解説いたします。
環境構築
こちらの記事で解説しています。
テンプレートを取ってくる
コマンドcargo generate --git https://github.com/rustwasm/wasm-pack-template Project Name: wasm-lessonsrc/lib.rs の内容を書き換える
元の関数名が「greet」なのを「aisatsu」に変更
src/lib.rsmod utils; use wasm_bindgen::prelude::*; // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global // allocator. #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen] extern { fn alert(s: &str); } #[wasm_bindgen] //pub fn greet() { ここを変更しています。 pub fn aisatsu() { alert("Hello, wasm-lesson!"); }プロジェクトをビルド
コマンドwasm-pack buildwebページテンプレートを取ってくる
コマンドnpm init wasm-app wwwindex.jsを書き換える
wasm.greet()を「wasm.aisatsu()」に変更します。
index.jsimport * as wasm from "hello-wasm-pack"; //www.greet() これを変更します。 wasm.aisatsu();npmで依存関係を解決
コマンドcd www npm installWasmを index.html が読み込めるようにする
www/package.jsonのdependenciesにディレクトリを追加する。
package.json{ //... "devDependencies": { "wasm-lesson": "file:../pkg", // 追加 // ... } }再び依存関係を解決します。
コマンドnpm install実行するもエラーになる
ここでサーバーを立ち上げます
コマンドnpm run startしかしブラウザを立ち上げて
localhost:8080
するとエラーになってしまいます。サーバーの設定ファイルを変更する
サーバーのホストとポートを変更するために、設定ファイルを開きます。
コマンドvim node_modules/webpack-dev-server/bin/options.jsすると、host,portについての記載部分があるのでこちらを次のように変更します。
options.js//~~省略~~ port:{ default:'8080', //これを追記 describe:'The port', group:CONNECTION_GROUP, } //~~省略~~ host:{ type:'string', // default:'localhost', default:'172.17.0.2', //これを追記 //~~省略~~こうすることでホストのブラウザでも実行を確認できるようになります。
こちら↓の記事を参考にしました。
dockerコンテナ上でnode.jsのサーバを起動し、ホスト端末からアクセスする
もし、まだエラーになっている場合、dockerのポート設定が
8080
ではないか、
または172.17.0.2
以外を指定する必要があるかもしれません。
上記のポストが参考になると思いますので参照してください。再度、試してみる。
コマンドnpm run start今度は成功です。ブラウザで動作を確認できるようになりました。
参考にした記事
- 投稿日:2020-12-01T22:01:43+09:00
文系出身業務経験なしのプログラマーがDocker使ってみた
業務経験なしのプログラマーがDocker使ってみた
はじめに
今年9月まで学生で、それまで独学のプログラミング歴は約2年です。
Dockerを使い始めたのはある企業での学生用インターンがきっかけでした。
その時の経験や感じたことをまとめて同じ境遇の方に少しでもシェアできたらと思います。Dockerについて
Dockerとは仮想環境を軽量でかつ短時間で構築できるオープンソースのソフトウェアサービスで、IaC(Infrastructire as Code)という概念のもと、複数人の開発にて発生するコンフリクトやバージョンの違いによるエラーを少なくする為に多くの企業で取り入れられているようです。実際には日本での使用はまだまだ浸透してないという声もあり、今後開発効率を上げる為にも導入を検討している所もあるようです。
Dockerについて学んだこと
- Dockerfileというファイルがある
- DockerImageというDockerHubで無償で提供された仮想環境がある(厳密にはコンテナ)
- Docker-composeは便利である
- Alpine LinuxというDockerに特化したLinuxのディストリビューションがある
Dockerfileというファイルがある
Dockerで仮想環境を構築する上で欠かせないDockerfile
前述した通りコードで管理する仮想環境なので、必要な情報をモジュールに書き出します。
書き方は下記の例を見てほしいですFROM alpine:latest RUN apk update RUN apk add nginx php7 php7-fpm git bash vim curl lsof supervisor などなど上記例はwebアプリを開発するための環境でAlpineLinuxというOSにnginxやphp-fpmなど必要なパッケージをインストールしています。もちろんインストールパッケージは少なければ少ないほどより軽量な仮想環境となります。1行目にFROM alpine:latestというコードはDockerHubからalpineというDockerImageをlatest(最新版)として引っ張ってきています。こうすることでalpineというOSのもとで構築できるようになります。そのほかにもUbuntuやDebianなども指定できます。続いて2行目のRUN apk updateですが、RUNは実行という意味と捉えて良いでしょう。alpineを指定しているのでapkコマンドを使ってパッケージのインストールの為に最新版に更新しています。続いて3行目からは必要なパッケージのインストールです。これはなんとなくわかると思うので割愛します。このように非常にシンプルな書き方で環境構築の準備ができます。
ここで疑問に感じた人もいるでしょう。DockerHubとは?実態が見えてこないと思います。
そういう方は次を見てください。DockerImageというDockerHubで無償で提供された仮想環境がある(厳密にはコンテナ)
DockerにはDockerHubという自身のDockerアカウントを管理するサイトhttps://hub.docker.com があります。
ここの上部にある検索欄で既存の環境を検索してを引っ張ってくることができます。
試しにUbuntuを検索してみましょう。
このようにUbuntuのDockerImageが表示されました。右のLinux-x86(latest)と書いてあるのがバージョンです。
これをDockerfileで引っ張ってくるとFROM ubuntu:latestとなります。
ここで感の良い方は気づくはず。ここで紹介したイメージはOS単体でしたが、他にもより開発に便利なカスタマイズされたイメージがあってそれをインストールすれば自分で構築しなくて済むと。
そうなんです!
実はこのDocker、人が作成した環境を使ってそれを元に開発できるのです!
例えばこのイメージ
centOSに初めからmysqlが入ってます。(まぁ、基本的にデータベースは別コンテナにした方が良いですが..)
このように人の書いた環境を使うことができます。
これがDockerの利点の一つでしょう。Docker-composeは便利である
続いてDocker-composeの紹介に入ります。
DockerではDockerfileからイメージ化されたDockerImageをdockerコマンドでコンテナ化させます。しかし、しかし複数のイメージを一度にコンテナ化させたり管理する時はdocker-composeコマンドを使うことをお勧めします。docker-composeを使用するための条件はdocker-compose.ymlというymlファイルを作成する必要があります。
例えばversion: "3" services: ubuntu: image: ubuntu ports: - "80:80" volumes: - ./config/nginx.conf:/etc/nginx/nginx.conf - ./config/sub.conf:/etc/php7/php-fpm.d/sub.conf - ./config/php.ini:/etc/php7/conf.d/php.ini - ./config/default.conf:/etc/nginx/conf.d/default.conf - ./config/supervisord.conf:/etc/supervisor/conf.d/supervisord.conf command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] stdin_open: true tty: true container_name: alp-pha全ての解説をすると莫大な文字数になりそうなのでざっくりとお話しします。
基本的な構文の構造はインデントを使って階層化しています。
まずversionなのですが、現段階では基本的に3にしといてください。servicesの欄に管理したいコンテナを記入します。(現時点ではコンテナは作成されていません) ubuntuという名前のコンテナを作成する為に先ほど引っ張ってきたUbuntuのイメージを指定してコンテナのポート番号など詳細設定をしていきます。volumesではファイルのマウントをすることでローカルとコンテナ内のファイルをつなぐことができます。commandやcontainer_nameなどの細かい設定は公式ドキュメントを読んだ方が早いでしょう。完成したdocker-compose.ymlが置いてあるフォルダに移り、コンテナを作成します。
docker-compose up -dこれでエラーがない限りコンテナが作成されるはずです。よくあるエラーではymlファイルの構文が間違っているので注意が必要です。ちなみに -d は バックグラウンドでコンテナを起動してくれますが、詳しくはこれも公式ドキュメントを読んでみた方が良いかもしれません。コンテナは作成したら勝手に起動しています。
作成されたコンテナはdocker ps もしくは docker ps -aで確認できます。(docker-composeコマンドでもOK)
続いて作成したコンテナに入ります。docker-compose exec -it コンテナID bashで入れます。コマンドIDはdocker psで確認したら書いてあります。 bashは例で、shだったり何かしらシェルを指定します。※ttyが割り当てられていないコンテナもあるので、それはdocker-compose.ymlで設定したりできます。コンテナの中に入ったらdocker-compose.ymlでマウントしたファイルだったりイメージでインストールしたパッケージがあったりが確認できるかと思います。
これでDockerによる環境は完成です。Alpine LinuxというDockerに特化したLinuxのディストリビューションがある
AlpineというディストリビューションはDockerを使ってみて初めて知りました。Docker用に作られたのか定かではありませんが、なぜAlpineはDockerに特化しているのかというと、なんと言っても軽量さです。
UbuntuやDebianなどその他OSで作られた仮想環境は基本的に500MB以上だったりします。それをAlpineで構築すると5MB~100MBくらいで済むのです。従来のやり方では環境を構築するたびに要領を取るし時間もかかる、重いしなど不満爆発の状態でしたが、alpineの登場によってより便利になりました。
AlpineもDockerHubにあるので、皆さんもぜひ使ってみてください!終わりに
初めてQiitaで記事を書かせていただきました。文章力ゼロだと思いますが、お許しください笑
Dockerについては他にも書きたいことがたくさんあるのですが、一度では書ききれないので、個人的に話したいことだけを選んで書きました。さて、Dockerを使ってみての感想ですが、正直感動しました。文系出身業務経験なしのプログラマーですが、非常に直感的に使いやすいですし、チームでの開発を想定した場合、コンフリクトも減りそうです。
Dockerを扱えるのは需要のあるスキルですし、今後使えることが当たり前の時代になってくると思います。
学習難易度も高くないと思うので、ぜひ皆さんも使ってみてください!
- 投稿日:2020-12-01T21:42:36+09:00
dockerを使用してさくっとPHP8を触ってみた
dockerを使用してさくっとPHP8を触ってみた
PARONYM Advent Calendar 2020 - Qiita の2日目です。
はじめに
2020/11/26にPHP8がリリースされました。
ということで、自身のローカル環境をなるべく汚さずにPHP8の新機能を触ってみることにしました。前提条件
- dockerがインストールされていること
きっかけ
PHP8の新機能を触ってみたい!でも自身のPCを汚したくない!
PHP8を試す環境として、当然ながらプロダクトの開発環境で試すわけにはいきません。(本番環境なんて以ての外)
となるとローカルPCで試すのが妥当ですが、インストールや設定の手間、もしもの時のバージョンの切り替えを考えると一気に面倒になります。
今回はPHP8の機能を触るのが目的であり速度性能を試すわけではないため、さくっとdockerを使用してPHP8を実行することにしました。コード
今回試したコードは全てGithubにコミットしています。
https://github.com/y-kakinuma-paronym/php8-dockerdockerでPHP8の実行
$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php {実行したいファイル}
- PHPの実行後、コンテナは削除する :
--rm
- カレントディレクトリをコンテナの/appにマウント :
-v `pwd`:/app
- コンテナ内の作業用ディレクトリは/app :
-w /app
- dockerイメージは
php:8.0-rc
を使用- php実行 :
php {実行したいファイル}
毎回コマンドを打つのは面倒なのでシェルスクリプトを作成しておくと楽です。
exec_php.php#!/bin/sh docker run --rm -v `pwd`:/app -w /app php:8.0-rc php $1 # ./exec_php.sh match_expression.phpPHP8新機能
PHP8の新機能はここから抜粋しています。
PHP: PHP 8.0.0 Release AnnouncementNamed arguments
引数を渡すときに名前を指定できるようになりました。
https://wiki.php.net/rfc/named_params<?php // Named arguments // https://wiki.php.net/rfc/named_params $str = "<a href='test'>Test</a>"; // PHP7 // $result = htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false); // PHP8 // double_encodeの引数のみをデフォルト引数から変更したい場合、引数の名前を指定する $result = htmlspecialchars($str, double_encode: false); echo $result . "\n"; // <a href='test'>Test</a> ?>Constructor property promotion
コンストラクタでのプロパティ初期化が簡略化できるようになりました。
https://wiki.php.net/rfc/constructor_promotion<?php // Constructor property promotion // https://wiki.php.net/rfc/constructor_promotion class Point { // PHP8 public function __construct( public int $x = 0, public int $y = 1, public int $z = 2, ) { print "Point constructor\n"; } // PHP7 // public int $x; // public int $y; // public int $z; // public function __construct( // int $x = 1, // int $y = 2, // int $z = 3 // ) { // $this->x = $x; // $this->y = $y; // $this->z = $z; // } public function showClassParameter() { print_r([$this->x, $this->y, $this->z]); } } $obj = new Point(); $obj->showClassParameter(); ?>Union types
共用体型(union type)が追加されました。
https://wiki.php.net/rfc/union_types_v2<?php declare(strict_types=1); // int と string を型指定 $f = function (int|string $v) { var_dump($v); }; $f(100); // ok $f("abc"); // ok $f(true); // ngMatch expression
https://wiki.php.net/rfc/match_expression_v2
<?php // Match expression // https://wiki.php.net/rfc/match_expression_v2 switch (1) { case 0: $result = "Foo\n"; break; case 1: $result = "Bar\n"; break; case 2: $result = "Baz\n"; break; } echo $result; // Bar /** * switchとの違い * ・一致した結果を変数に代入することが可能 * ・一致は一つのみをサポート。breakなどは必要なし * ・一致は厳密な比較 */ echo match(1) { 0 => "Foo\n", 1 => "Bar\n", 2 => "Baz\n" }; // Bar echo match (8.0) { '8.0' => "Oh no!", 8.0 => "This is what I expected", }; // This is what I expected // ↑厳密な比較のため ?>switchとの違いについての説明はコード内に記載しています。特に注意が必要なのが、一致は厳密な比較であるところです。
Nullsafe operator
null演算子が追加されました。
https://wiki.php.net/rfc/nullsafe_operator<?php // Nullsafe operator // https://wiki.php.net/rfc/nullsafe_operator class Session { public $user = null; } $session = new Session(); /** * nullsafe演算子「?」を用いることでチェーン内の1つの要素で評価が失敗すると、 * チェーン全体の実行が中断され、nullを返す */ $country = $session->user?->getAddress()?->country; var_dump($country) // null ?>
$session->user?->getAddress()?->country;
例では↑のuser
がnullのため失敗となり、nullsafe演算子はnull
を返します。PHP8の所感
PHP8の目玉はなんと言ってもJITコンパイラーによる処理の高速化ですが、こういった細かい機能も実装が楽になるため嬉しい追加でした。
特にMatch expressionは、JSONを作成するためにarrayを組み立てるときに三項演算子より複雑な分岐ができるため非常に便利だと思います。$result = 1; $json = [ "resutl" => match($result) { 0 => "Failed", 1 => "Success", 2 => "No Updated" }, ]; echo json_encode($json); // {"resutl":"Success"}Nullsafe operatorもうまく利用できれば、if文が減りコードの可読性を向上させることができそうです。
まとめ
今回はPHP8の新規機能をDockerを使用して自身のPCをなるべく汚さずに試してみました。
今回紹介していないPHP8の新機能はまだまだありますので、公式サイトを参照してください。
PHP: PHP 8.0.0 Release AnnouncementDockerを利用すれば、PHP8に限らず様々な環境を楽に構築できるのでこれからもどんどん利用していきたいと思います。
参考
- 投稿日:2020-12-01T21:29:03+09:00
UDPのマルチキャストをDockerコンテナ・ネットワーク上で動かしてみた
※半年ほど前に書いた記事をQiitaに持ってきました
JavaのMulticastSocketを使って、UDPのマルチキャストを送受信するクライアントを作り、Dockerコンテナ・ネットワーク上で動かしてみようと思います。
今回書いたコードはこちらのリポジトリに置いてあります。
UDPのマルチキャストとは
コネクションの確立をせず、パケットを一方的に送りつけることが特徴なUDPですが、以下のような送信方式があります。
- ユニキャスト
- ブロードキャスト
- マルチキャスト
ユニキャストは、特定の一台のホストに対して送信します。ブロードキャストは、あるネットワークに属する全てのホストに対して送信します。マルチキャストは、あるネットワークに属する特定のホスト(一台でも複数台でもよい)に対して送信します。
マルチキャストにおいて、送信側はマルチキャストアドレスを宛先アドレスとして指定してデータを送信します。マルチキャストアドレスはIPアドレスクラスのうち、クラスDのIPアドレスです。受信側はこのマルチキャストアドレスにJoinすることでデータを受け取ることができるようになります。
また、ユニキャストを複数台に行うことに対して、マルチキャストを行うことのメリットは以下のようになります。
- 宛先のIPアドレスを知らなくても、マルチキャストアドレスだけ分かっていれば、Joinしているホスト全員に対して送信できる
- 一つのパケットで複数台に向けて送信できるので通信効率が良く、送信元の負荷が低い
Dockerコンテナ・ネットワークとは
Dockerコンテナ・ネットワークとは、Dockerコンテナが属するネットワークのことです。
docker network ls
コマンドを実行すると一覧が表示されます。今回はマルチキャストを検証するために、同一ネットワーク内にコンテナを立てなければなりません。しかし、実は難しいことをする必要はなく、docker-composeを使えば自動でネットワークを作成してくれるます。なので、今回はdocker-composeを使って複数のクライアントを動かそうと思います。簡単なチャットアプリを作ってみる
それではUDPのマルチキャストを送受信するクライアントとして、今回は簡単なチャットアプリを作ってみます。一つのクライアントで送信と受信の両方を行うようにしてみます。
ソースコード
MulticastClient.javaというファイルに以下を記述します。
import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Scanner; class Sender extends Thread { private final int toPort; private final String mcastAddrName; public Sender(int toPort, String mcastAddress) { this.toPort = toPort; this.mcastAddrName = mcastAddress; } public void run() { Scanner scan = null; MulticastSocket socket = null; try { InetAddress mcastAddress = InetAddress.getByName(mcastAddrName); scan = new Scanner(System.in); socket = new MulticastSocket(); String message; while ( (message = scan.nextLine()).length() > 0 ) { byte[] bytes = message.getBytes(); DatagramPacket packet = new DatagramPacket(bytes, bytes.length, mcastAddress, toPort); socket.send(packet); } } catch (IOException e) { e.printStackTrace(); } finally { if (scan != null) scan.close(); if (socket != null) socket.close(); } } } class Receiver extends Thread { private final int fromPort; private final String mcastAddrName; private static final int PACKET_SIZE = 1024; public Receiver(int fromPort, String mcastAddrName) { this.fromPort = fromPort; this.mcastAddrName = mcastAddrName; } public void run() { MulticastSocket socket = null; byte[] buf = new byte[PACKET_SIZE]; DatagramPacket packet = new DatagramPacket(buf, buf.length); try { socket = new MulticastSocket(fromPort); InetAddress mcastAddress = InetAddress.getByName(mcastAddrName); socket.joinGroup(mcastAddress); while (true) { socket.receive(packet); String message = new String(buf, 0, packet.getLength()); System.out.println(packet.getSocketAddress() + " : " + message); } } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { socket.close(); } } } } public class MulticastClient { public static final int PORT = 3000; public static final String MCAST_ADDR = "224.0.1.1"; public static void main(String args[]) { Receiver receiver = new Receiver(PORT, MCAST_ADDR); Sender sender = new Sender(PORT, MCAST_ADDR); receiver.start(); sender.start(); } }ソースコードの説明
上記のコードには以下の3つのクラスが含まれています。
- Senderクラス
- Receiverクラス
- MulticastClientクラス
それぞれについて説明します。
Senderクラス
Senderクラスは、データを送信するためのクラスです。受信と並列して動かすためにThreadクラスを継承して、runメソッドをオーバーライドしています。
フィールド
private final int toPort; private final String mcastAddrName;toPortは送信先のポート番号(受信側が受信するポート番号)です。mcastAddrNameは送信先のマルチキャストアドレスの名前です。
コンストラクタ
public Sender(int toPort, String mcastAddress) { this.toPort = toPort; this.mcastAddrName = mcastAddress; }変数を初期化します。
runメソッド
public void run() { Scanner scan = null; MulticastSocket socket = null; try { InetAddress mcastAddress = InetAddress.getByName(mcastAddrName); scan = new Scanner(System.in); socket = new MulticastSocket(); String message; while ( (message = scan.nextLine()).length() > 0 ) { byte[] bytes = message.getBytes(); DatagramPacket packet = new DatagramPacket(bytes, bytes.length, mcastAddress, toPort); socket.send(packet); } } catch (IOException e) { e.printStackTrace(); } finally { if (scan != null) scan.close(); if (socket != null) socket.close(); } }whileループ内だけ説明します。
message = scan.nextLine()
では標準入力から一行読み取って、String message
に入れます。byte[] bytes = message.getBytes()
では、message
をバイト配列に変換します。DatagramPacket packet = new DatagramPacket(bytes, bytes.length, mcastAddress, toPort)
では、上記のバイト配列、宛先のマルチキャストアドレス、宛先のポート番号を指定し、DatagramPacket
を生成します。socket.send(packet)
でパケットを送信します。Receiverクラス
Receiverクラスは、データを受信するためのクラスです。送信と並列で動かすためにThreadクラスを継承して、runメソッドをオーバーライドしています。
フィールド
private final int fromPort; private final String mcastAddrName; private static final int PACKET_SIZE = 1024;fromPortはパケットを受け取るポート番号です。mcastAddrNameはJoinするマルチキャストアドレスです。PACKET_SIZEは受け取るパケットのサイズです。
コンストラクタ
public Receiver(int fromPort, String mcastAddrName) { this.fromPort = fromPort; this.mcastAddrName = mcastAddrName; }変数を初期化します。
runメソッド
public void run() { MulticastSocket socket = null; byte[] buf = new byte[PACKET_SIZE]; DatagramPacket packet = new DatagramPacket(buf, buf.length); try { socket = new MulticastSocket(fromPort); InetAddress mcastAddress = InetAddress.getByName(mcastAddrName); socket.joinGroup(mcastAddress); while (true) { socket.receive(packet); String message = new String(buf, 0, packet.getLength()); System.out.println(packet.getSocketAddress() + " : " + message); } } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { socket.close(); } } }whileループの中だけ説明します。
socket.receive(packet)
でパケットの受信を待機します。パケットを受信したらString message = new String(buf, 0, packet.getLength())
によりバイト配列をStringに変換します。そしてSystem.out.println(packet.getSocketAddress() + " : " + message)
により、標準出力に出力します。MulticastClientクラス
MulticastClientクラスでは、mainメソッドを定義しています。
フィールド
public static final int PORT = 3000; public static final String MCAST_ADDR = "224.0.1.1";SenderクラスとReceiverクラスに渡すポート番号とマルチキャストアドレスを定義しています。
mainメソッド
public static void main(String args[]) { Receiver receiver = new Receiver(PORT, MCAST_ADDR); Sender sender = new Sender(PORT, MCAST_ADDR); receiver.start(); sender.start(); }ReceiverクラスとSenderクラスをインスタンス化し、Threadをstartさせています。
検証環境を準備する
上記のチャットアプリを動かす環境をDockerで構築します。同一ネットワーク内に複数のクライアントを立ち上げるためにdocker-composeファイルを定義します。
Dockerfile
Dockerfileは以下のようになります。
FROM openjdk:14 COPY . /usr/src/myapp WORKDIR /usr/src/myapp RUN javac MulticastClient.java CMD ["/bin/bash"]Imageはopenjdk:14を使います。
javac MulticastClient.java
でコンパイルした後、/bin/bash
でbashを開きます。実行するときはdocker container exec -it [CONTAINER ID] bash
で中に入ってから、java MulticastClient
を叩きます。本当は
CMD ["/bin/bash"]
のところをCMD ["java", "MulticastClient"]
として実行しておき、containerの中に入りたかったのですが、やり方がわかりませんでした。docker-compose
docker-compose.ymlは以下のようになります。
version: '3' services: client1: build: context: . dockerfile: Dockerfile tty: true client2: build: context: . dockerfile: Dockerfile tty: true先ほどのDockerfileからImageをBuildします。
tty: true
を記述しておくとコンテナが起動したままになります。client1と同じように、client2, client3...とコンテナを作成します。これらのコンテナは
docker-compose up
を実行すると自動で作成されるnetworkに属することになります。プライベートIPアドレスは自動で割り振られます。検証してみる
それではクライアントを複数立ち上げてマルチキャストが動くか検証してみます。
コンテナを起動させるために以下のコマンドを実行します。
docker-compose up --build
コンテナに入るには以下のコマンドを実行します。CONTAINER IDは
docker container ls
で調べることができます。docker container exec -it [CONTAINER ID] bashアプリケーションを実行するにはコンテナ内で以下のコマンドを実行します。
java MulticastClient3つコンテナを作って、それぞれクライアントを実行してみたのが以下の画像です。ターミナルを横に3つ並べています。
左のターミナルでHello!と入力すると、他のターミナルにもメッセージが表示されました!左のターミナルで実行しているコンテナのプライベートIPアドレスは
172.29.0.3
だということも分かりますね!真ん中のターミナルでHello!Hello!と入力しても他のターミナルに表示されます。真ん中は
172.29.0.2
ですね。右のターミナルも同じく動きます。
感想
今回はJavaのMulticastSocketを使って、UDPのマルチキャストを送受信するチャットクライアントを作り、Dockerコンテナ・ネットワーク上で動かしてみました。UDPについて調べる過程でかなり勉強できたので良かったです。また、Dockerの動かし方についても学べたので一石二鳥でした!
参考
https://www.techscore.com/tech/Java/JavaSE/Network/5/
https://milestone-of-se.nesuke.com/nw-advanced/multicast/multicast-summary/
- 投稿日:2020-12-01T20:12:15+09:00
Dockerだけでnpm initやnpm installをする方法
npmをローカルにインストールせずに、Dockerだけで
npm init
やnpm install
をする方法を解説します。npm init
まずは、ディレクトリを作成して中に移動します。
mkdir app && cd app次に、dockerを使って
npm init
します。以下のコマンドを実行してください。実行するとpackage.json
が生成されます。docker run --rm -w "/usr/app" -v "${PWD}:/usr/app" node:15.3-alpine npm init -yコマンドの解説をします。
docker run <IMAGE>
は、イメージをコンテナ化するコマンドです。今回はnode:15.3-alpine
を指定しています。これは、公式のイメージで、ローカルになければコマンドを実行したときにインストールされます。
--rm
オプションは、コンテナの終了時に自動的にそのコンテナを削除します。詳しくはこちらに書いてあります。
-w
オプションでワーキングディレクトリを指定しています。システムの邪魔にならなければどこでもいいですが、今回は/usr/app
としました。
-v
オプションは、マウントをします。今回は、${PWD}:/usr/app
としているので、現在のディレクトリを/usr/app
にマウントすることになります。こうすることで、npm init -y
を実行して、/usr/app
内に生成されるpackage.json
を現在のディレクトリに反映させることができます。最後の引数は、実行したいコマンドです。
npm install
上記と同じように以下のコマンドを実行します。例としてexpressをインストールしてみました。実行すると
package-lock.json
やnode_modules
が生成されます。docker run --rm -w "/usr/app" -v "${PWD}:/usr/app" node:15.3-alpine npm install expressDockerを使うことで、npmをインストールせずに使うことができました。
- 投稿日:2020-12-01T18:35:07+09:00
DockerでCGI #2: ニコニコチャンネル キャッシュサーバー 「nicochcgi」
はじめに
DockerでCGI、その2。
順番としてはこっちが最初。苦労しました。前:DockerでCGI #1: EPWING電子辞書サーバー「let me see...」 (2003年)
nicochcgi
nicochcgiはニコニコチャンネルをスクレイピング・キャッシュしてコメント付きで見るCGIサーバーです。
GitHub : nicochcgi_docker
Docker : ghcr.io/kurema/nicochcgi/nicochcgiサーバーサイドはPerl+CGI、クライアントは素のHTML5+JavaScriptという(一昔前の)普通の構成です。Ajax時代のLAMP。
定期実行はcron。サムネイル作成はシェルスクリプト。
今なら基本ASP.NET Coreで作りますが、当時サーバーにしていたx86版Ubuntuでは使えなかったので手軽に作ってこうなりました。
2017年ごろから気が向いたときに開発を続けています。導入方法
導入方法は以下です。
あらかじめニコニコ動画のアカウントが必要です。$ git pull https://github.com/kurema/nicochcgi_docker.git $ cd nicochcgi_docker $ nano docker-compose.yml # キャッシュ場所の変更・移動先にはmkthumb.shもコピー $ sudo docker-compose up -d $ chmod 666 config/* $ chmod 777 videos/*.sh # 管理用パスワードの作成・各種設定 $ sudo docker-compose exec nicochcgi perl /var/www/html/get_password.pl $ nano config/nicoch.conf # cronの設定 $ sudo crontab -e 0 3 * * * cd docker-compose.ymlの場所 && docker-compose exec -T nicochcgi perl /var/www/html/nico-anime.pl >> ログファイル 2>&1 && docker-compose exec -T nicochcgi perl /media/niconico/mkthumb.sh >> ログファイル 2>&1この後、
http://server:50001/
でログインし、キャッシュするチャンネルを登録してください。開発過程
初めての
Docker
だったので入門記事やらを読みながら試行錯誤しました。
nicochcgi自体の開発は以前行ったものなので省略します。ベースイメージは
ubuntu
です。
ffmpegが数百MBを消費するので多少節約したところで誤差でしょう。httpd
よりトラブルが少なそうだと判断しました。開発は基本的にGitHub Actionsをぐるぐる回す形でやりました。無料なので。
ローカルで試さないままリモートでやってみるという形です。
マルチプラットフォームビルドは重いのでビルド中は別の作業ができるのは楽でした。Docker対応
Dockerfile
DockerfileFROM ubuntu MAINTAINER kurema # https://qiita.com/Kashiwara/items/07e154bb5e859445eac6 ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2 #You need to install tzdata first to prevent 'Please select the geographic area...' message. #https://sleepless-se.net/2018/07/31/docker-build-tzdata-ubuntu/ RUN apt-get update -y && \ apt-get install -y --no-install-recommends tzdata #Timezone is set to Japan assuming you are in Japan. ENV TZ=Asia/Tokyo #Install dependencies. RUN apt-get install -y --no-install-recommends ffmpeg \ apache2 \ cpanminus \ build-essential \ libexpat1-dev \ #fix cpanm error... liblwp-protocol-https-perl \ libnet-ssleay-perl \ libcrypt-ssleay-perl \ openssl \ libssl-dev && \ apt-get clean && \ rm -rf /var/cache/apt/archives/* #Enable cgi. This is cgi in 2020. RUN sed -ri 's/Options Indexes FollowSymLinks/Options Indexes FollowSymLinks ExecCGI/g;' /etc/apache2/apache2.conf && \ echo "AddHandler cgi-script .cgi" >> /etc/apache2/apache2.conf && \ a2enmod cgid && \ echo "-- Apache2.conf --" && \ cat /etc/apache2/apache2.conf #Copy cpanfile first for better cache management. RUN touch /var/www/html/cpanfile COPY nicoch/cpanfile /var/www/html/cpanfile RUN cpanm --installdeps --no-man-pages /var/www/html/ && \ rm -rf /root/.cpanm/work/* COPY nicoch/ /var/www/html/ RUN chmod 755 /var/www/html/*.cgi /var/www/html/*.pl #/etc/nicochcgi/ should be overwritten using -v COPY config/ /etc/nicochcgi/ RUN chown www-data:www-data /etc/nicochcgi/* RUN chmod 666 /etc/nicochcgi/* #I think document should be included. COPY README.md / EXPOSE 80 CMD ["apachectl", "-D", "FOREGROUND"]Apacheのインストール
これは検索でヒットした以下の記事を参照しました。
環境変数の用意が必要なようです。
apt-get
するだけだろうと調べなければトラブルでした。良かった。@Kashiwara (2018) 「Dockerでapache2起動」Qiita
タイムゾーン設定
前回の記事に書きましたが、
Please select the geographic area in which you live.
のエラー対策にtzdataを先にインストールします。
サーバーの性質上、タイムゾーンは日本のはず。DockerfileRUN apt-get update -y && \ apt-get install -y --no-install-recommends tzdata ENV TZ=Asia/TokyoCGI有効化
ベースコンテナによってCGI有効化の手順が違います。今回は以下の条件です。
- トップページが
index.html
→DirectoryIndex
は変更不要- CGIの拡張子は全て
.cgi
- Ubuntuなので
a2enmod
が普通に使える。したがって、Dockerfileでは以下になりました。
DockerfileRUN sed -ri 's/Options Indexes FollowSymLinks/Options Indexes FollowSymLinks ExecCGI/g;' /etc/apache2/apache2.conf && \ echo "AddHandler cgi-script .cgi" >> /etc/apache2/apache2.conf && \ a2enmod cgid
a2enmod cgid
で有効化されるのは以下の2つです。cgid.conf# Socket for cgid communication ScriptSock ${APACHE_RUN_DIR}/cgisockcgid.loadLoadModule cgid_module /usr/lib/apache2/modules/mod_cgid.so
a2enmod
と同じことをhttpd
のようなa2enmod
がないベースコンテナでやりたい場合は、上の二行を追記すればいいわけですね。
(#1ではScriptSock
を忘れてちょっと困りました。)
a2enmod cgi
記事を書いている間に気づきましたが、誤ってa2emod cgi.load
になっていました。
実際にはマルチスレッドなのでa2enmod cgid
にする必要があります。
それに対しa2enmodは勝手に判断してcgid
を有効にしてくれていました。
mods-enabled
にmods-available
からのリンクを作ってくれるだけのお手軽ツールだと思ってましたが、なかなか賢いですね。2020-10-24T05:47:59.1426858Z #21 0.362 Your MPM seems to be threaded. Selecting cgid instead of cgi. 2020-10-24T05:47:59.1427458Z #21 0.362 Enabling module cgid. 2020-10-24T05:47:59.1427912Z #21 0.367 To activate the new configuration, you need to run: 2020-10-24T05:47:59.1428339Z #21 0.367 service apache2 restartcpanm
以前は自分のサーバーを主眼において、開発過程で必要なライブラリを都度CPANからインストールする形にしていました。
第三者がnicochcgi
をインストールする際は同様に一々確認し追加していく想定でしたが、大変面倒で時間が掛かります。まず
cpanfile
を作成しました。最初からそうするべきでしたね。Docker側としては、キャッシュ管理のためにcpanfileのみをコピーして
cpanm --installdeps --no-man-pages
するだけ…のはずでした。DockerfileRUN touch /var/www/html/cpanfile COPY nicoch/cpanfile /var/www/html/cpanfile RUN cpanm --installdeps --no-man-pages /var/www/html/ && \ rm -rf /root/.cpanm/work/*cpanmの苦労 #1: 重い
そもそもCPANのインストールは重いです。本当に重い。
一々テストやらビルドやらするからのようです。
apt install
と違って実際にCPUを使って処理するからか、マルチプラットフォーム対応時には特に大変です。ある時はGitHub Actionsで6時間処理をしたあげく自動キャンセルされました。そんなのあるんですね。
4時間応答なしだったので処理時間の問題でもなさそうですが。メモリ不足でスワップ?参考までに成功時のQEMUでの実行時間を表にします。
同時処理なので参考値ですが、エミュレーション時は目安で10倍程度時間が掛かるようですね。
arch ellapsed amd64 (Native) 449.1s arm64 3921.1s ppc64le 4014.9s arm/v7 3919.5s cpanmの苦労 #2: 依存関係
CPANはPerlモジュールだけ面倒を見るだけで、依存するバイナリパッケージを予め導入する必要があります。
今回は以下のエラーに悩まされました。
要するにLWP::Protocol::https
インストール関係の問題です。2020-10-23T19:00:01.5141020Z #24 2933. Building and testing IO-Socket-SSL-2.068 ... ! Installing IO::Socket::SSL failed. See /root/.cpanm/work/1603476565.7/build.log for details. Retry with --force to force install it. 2020-10-23T19:00:01.6639377Z #24 3037. ! Installing the dependencies failed: Module 'IO::Socket::SSL' is not installed, Module 'IO::Socket::SSL::Utils' is not installed 2020-10-23T19:00:01.6640902Z #24 3037. ! Bailing out the installation for LWP-Protocol-https-6.09. 2020-10-23T19:00:01.7953327Z #24 3037. ! Installing the dependencies failed: Module 'LWP::Protocol::https' is not installed 2020-10-23T19:00:01.7954896Z #24 3037. ! Bailing out the installation for /var/www/html/. 2020-10-23T19:00:01.7955267Z #24 3037. FAIL最終的にはずらずらと以下全てを
apt-get
で導入し解決しました。cpanm
も早くなりました。
要するにliblwp-protocol-https-perl
他を導入すればcpanのテストをスキップし諸々導入してくれるようです。
実際には不要なものもある気がします。
検索してもドンピシャの答えがなかったので試行錯誤しました。RUN apt-get install -y --no-install-recommends libexpat1-dev \ #fix cpanm error... liblwp-protocol-https-perl \ libnet-ssleay-perl \ libcrypt-ssleay-perl \ openssl \ libssl-devcpanmの苦労 #3: ログが面倒
上のログをよく見ると
See /root/.cpanm/work/1603476565.7/build.log
との記述があります。
ローカルでDocker build
をしているなら中に入ってファイルを確認できますが、GitHub Actions+マルチプラットフォームビルドとなると正直お手上げです。
思いつく対策は3つです。
- ローカルで試してみる。
- 今回は
cpanfile
に追加後ビルドに失敗するようになったので以前のバージョンがありました。cpanm --installdeps -v
(verboseのv)- Artifactを使うQiita記事。
- QEMUを使ってマルチプラットフォームでビルドしていて大変そうなので辞めました。
まずはローカルで試しました。
docker-compose exec nicochcgi bash
で入って、cpanfile
を編集後cpanm --installdeps
するだけの作業です。
試行錯誤をするには最適ですが、ローカルで動くはずがビルドに失敗したりしました。
なので-v
で原因追及をしました。
-v
はGitHub Actionsのログが長くなるので正常動作するならお勧めしません。
普通に動くようになれば削除しました。
本来はArtifactを使うべきだと思います。公開
#1と同じく最終的にはGitHub Container Registryに保存しました。
しかし、最初なので以下のミスをしてしまいました。
- 最初にGitHub Package Registryを使ってしまう。
- GitHub Container Registryと異なりGitHub Package Registryではパブリックイメージは削除できません。ダウンロード数が0でも。
- 古いバージョンが残ってしまいました。仕方ない。
- 名前が似ているのでよくわかってなかった。
- GitHub Container Registryでは「レポジトリ名/イメージ名」みたいなルールだと勘違いする。
- その結果イメージ名が
nicochcgi/nicochcgi
という冗長な感じに。- 現時点の利用者はおそらく自分だけですが、名前変更は避けました。変えるのは簡単ですが履歴も消えますし。
記事時点でGitHub Package RegistryのDockerの奴とGitHub Container Registryがあるので皆さん気を付けましょう。
GitHub Actions
GitHub Actionsは前回同様GitHub Communityの例文を参照しました。というかこっちが先です。
.github/workflows/docker.yml: GitHub, Current
.github/workflows/docker.yml
.github/workflows/docker.ymlname: Docker Container Build Workflow #https://github.community/t/github-package-registry-does-not-support-multi-cpu-architecture-image/14339/11 on: schedule: - cron: '0 8 13 * *' # once a month push: branches: - main tags: - 'v*.*.*' pull_request: branches: - main workflow_dispatch: env: IMAGE_NAME: nicochcgi REPO_NAME: nicochcgi jobs: docker: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Prepare id: prep run: | DOCKER_IMAGE=ghcr.io/${{ github.repository_owner }}/${REPO_NAME}/${IMAGE_NAME} VERSION=edge if [[ $GITHUB_REF == refs/tags/* ]]; then VERSION=${GITHUB_REF#refs/tags/v} fi if [ "${{ github.event_name }}" = "schedule" ]; then VERSION=monthly elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then VERSION=test fi TAGS="${DOCKER_IMAGE}:${VERSION}" if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then TAGS="$TAGS,${DOCKER_IMAGE}:latest" fi echo ::set-output name=tags::${TAGS} echo "::set-output name=latest::${DOCKER_IMAGE}:latest" # lowercase the branch name BRANCH=$(echo ${GITHUB_REF##*/} | tr '[A-Z]' '[a-z]') LABELS="org.opencontainers.image.revision=$GITHUB_SHA" LABELS="$LABELS,org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" LABELS="$LABELS,org.opencontainers.image.version=$VERSION" LABELS="$LABELS,com.github.repo.branch=$BRANCH" LABELS="$LABELS,com.github.repo.dockerfile=Dockerfile" echo ::set-output name=labels::${LABELS} BUILD_ARGS="BRANCH=$BRANCH" echo ::set-output name=args::${BUILD_ARGS} - name: Tag names run: echo ${{ steps.prep.outputs.tags }} - name: Set up QEMU uses: docker/setup-qemu-action@master with: platforms: all - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@master - name: Builder instance name run: echo ${{ steps.buildx.outputs.name }} - name: Available platforms run: echo ${{ steps.buildx.outputs.platforms }} - name: Login to GHCR if: github.event_name != 'pull_request' uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GHCR_PAT }} - name: Build and push id: docker_build uses: docker/build-push-action@v2 with: builder: ${{ steps.buildx.outputs.name }} context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.prep.outputs.tags }} build-args: ${{ steps.prep.outputs.args }} labels: ${{ steps.prep.outputs.labels }} cache-from: type=registry,ref=${{ steps.prep.outputs.latest }} cache-to: type=inline時間
ビルド時間はキャッシュありで1~2分、フルのマルチプラットフォームビルドには1時間強程度です。
amd64,arm64,ppc64le,arm/v7
の同時ビルドで1時間19分、s390x
単独で45分です。
前述のとおり最悪ケースで6時間後に強制停止されました。ざっと計算したところ合計13.5時間程度回しているようです。
パブリックレポジトリなので全部無料。お得感ヤバいです。
無料だから過剰に回してる感はありますが、一応キャッシュは使ってます。cron
このスクリプトはcronで定期実行する設計になっています。
Docker内でcronを動かすのは以下の理由で望ましくないので、下の記事を参考にホスト側のcronから設定するようにしました。
そのせいでセットアップの手間が増えています。
docker pull
などをする度に設定が消える。- コンテナで動かすプロセスが増える。
- ログ管理が面倒。
@YuukiMiyoshi(2019) 「Docker + Cron環境を実現する3つの方法」 Qiita
懸念点
1コンテナ1プロセス
Dockerは1コンテナ1プロセスが推奨されているようです(Stack Exchange)。
cgidは一応デーモンですから、これを逸脱しています。セットアップの簡便さのためにDockerを利用している感覚なので別にどっちでもいいですし、現実問題Apacheとcgidを分けろと言われても無理ですが、マシン再起動時にcgidの起動が失敗する例を確認しました。
うっかりdocker-compose restart
してしまったのでログが消えてしまいましたが、ブラウザ側からは503 Service Unavailable
が、ログにはcouldn't bind unix domain socket
などが記録されていたはずです。
1コンテナ1プロセスにしておけば発生しなかった問題のようにも見えました。タイミング的な何かでしょうか。謎です。容量
nicochcgiのイメージは654MBにもなります。
ubuntuにffmpegをインストールすると557MB要求されるのでほとんどがffmpegです。
多少軽量化しても無駄なのが良く分かりますね。$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE ghcr.io/kurema/nicochcgi/nicochcgi latest 14fdd99f2dc5 22 hours ago 654MB ghcr.io/kurema/letmesee latest 869e7d478ba0 4 days ago 414MB httpd latest 3dd970e6b110 7 weeks ago 138MB ubuntu latest 9140108b62dc 2 months ago 72.9MB$ apt install --no-install-recommends ffmpeg ... Need to get 90.1 MB of archives. After this operation, 557 MB of additional disk space will be used.正直、654MBと言えば仮想マシンみたいな数字で「これをコンテナ内で実行するのはちょっと…」という気になります。
どうせサーバーには普通にffmpeg入れますし、エンコード支援とかが関係すれば厄介そうです(今回はサムネ作成とフォーマット変換程度ですが)。
「Docker ffmpeg」で検索すれば2014年のクックパッドの記事が出たりしますから普通にアリっぽいですし、スクリプトを変更するのも嫌なので仕方ないですが、微妙な感じはします。サイズを縮小するなら用途を絞って自前コンパイルでもするのが選択肢かもしれませんが、色々とあまりやりたくはないです。
- shin1ohno (2014)「Dockerでffmpegもimagemagickも怖くないという話」 クックパッド開発者ブログ
参考記事
Docker入門にあたって参考にした記事を下に挙げます。
- Docker
- 武井 (2020) 「【連載】世界一わかりみが深いコンテナ & Docker入門」SIOS TECH.LAB
- Docker全般。入門で一番役に立ちました。
- kooohei (2015) 「Dockerコンテナの作成、起動〜停止まで」 Qiita
- Dockerのコマンド集です。基本。
- 最近はビルドはGitHub Actionsで、サーバー上では
docker-compose
メインで使う数種類程度は覚えました。- senyoltw (2015)「Docker上でApacheコンテナを作成しCGIのコンテンツを走らせるまで」ワタナベ書店
- #1でも参照したCGIの記事です。
#Scriptsock cgisock
のコメントインしていない点だけは注意が必要です。- @yohm (2019) 「dockerでvolumeをマウントしたときのファイルのowner問題」 Qiita
- dd_fort (2020) 「Dockerのvolumeでpermission deniedが発生した場合の解決法」 ラクス エンジニアブログ
docker -v
のownerの話はめんどくさいですね。chmod
で権限を緩くすることで強引に解決してます。- 自分の環境ではとりあえず問題は発生していません。
- @YuukiMiyoshi (2019) 「Docker + Cron環境を実現する3つの方法」 Qiita
- Dockerでcronを実行するには。「ホストのcronからコンテナ内のプログラムを実行する」の方法を選択しました。
- @homines22 (2020) 「【GitHub Actions】GitHub Package Registryを利用して同一Dockerイメージをjobで共有する」 Qiita
- GitHub Package Registryなのでそろそろ非推奨です。
- ただしGitHub Container Registryは現在ベータなのでまだGitHub Package Registryの方が良いのかも。
- Apache
- shinoda (2017)「Apache2 で、CGI ソースが表示されちゃう件の対応(Debian APT パッケージ編)」電気ウナギ的○○
- よく読めば
a2enmod cgi
でcgidがロードされると書いてありますね。おまけ
Dockerの初体験的な感じだったので「Docker未経験から2週間で」みたいなタイトルにしようかと思いましたが辞めました。
既に流行ってないみたいだし、「確かにDockerは未経験だけど…」って感じなので。
- 投稿日:2020-12-01T17:47:33+09:00
WSL2環境でVSCode + Dockerで開発する為に
Windows環境で開発してます。
今年の初め、ChromeBookで開発する機会があってterminalというものにはじめて触れ、「なんて便利なものだ!」なんて思ったものです。
結局ChromeBookはOSの壁があり、ガチの開発環境には向かない事がわかってからはサブマシンになりましたが、あの感覚が忘れられず。
一瞬MACを。。。とも思いましたが、もともと使っていたWindowsが結構いいスペックだったので、それならまずはWSL2の環境を作ってみようと思った次第です。
ただ、実際にやってみると意外と言う事を聞いてくれません(笑)
この記事では備忘録としてハマった事を書いていこうと思います。参考
Windows 10 用 Windows Subsystem for Linux のインストール ガイド
Docker Desktop WSL 2 バックエンドDocker開発環境を移動したい
WSL2のディストリビューションは%LocalAppData%(C:\Users[ユーザー]\AppData\Local\Packages等)に保存されます。
私の場合、CドライブはSSDで200G強しかないので、このままですとすぐに枯渇してしまいます。
よってまずはこれらをDドライブに移す事をしました。
Dockerを停止する
PowerShellでディストリビューションを確認する
wsl -l Ubuntu-20.04(規定) docker-desktop-data docker-desktop対象のディストリビューションをエクスポートする
上記のうち、docker-desktop
、docker-desktop-data
がDockerのディストリビューションです。wsl --export docker-desktop docker-desktop.tar wsl --export docker-desktop-data docker-desktop-data.tarエクスポートしたディストリビューションを削除する
wsl --unregister docker-desktop wsl --unregister docker-desktop-dataエクスポートしたtarファイルをインポートする
私の場合はd:\wsl
というフォルダを作成してそこに保存しています。wsl --import docker-desktop D:\wsl\docker-desktop docker-desktop.tar wsl --import docker-desktop-data D:\wsl\docker-desktop-data docker-desktop-data.tarDockerを起動する
DockerをWSL2で動かしているのに激重い
DockerをWSL2で動かすと爆速になる。。。
そんな触れ込みを見て環境を構築している訳ですが、実際に動かすと何とも見るに堪えられないくらい重くなりました。(特にPHP等の動的に描画している部分)
この時点での私の環境は、Docker => WSL2(Cドライブ)
、ソースコード => Widowns(Dドライブ)
という環境となっていました。
WSL2環境とWindows環境はつながっている(マウントされている)ので、もちろんWSL2上からWindow上のファイルにアクセスはできます。
しかし、WSL2(Linux)とWindowsではファイルシステムの構造が異なるので、WSL2上からWindows管理上のファイル操作をすると激重となってしまうとの事でした。
よってソースコードをWSL2上に移動しました。尚、ソースコードをWSL2(Cドライブ)にバンバン置いていくと、やはりシステムドライブが枯渇していくので、Docker開発環境を移動したいの
Docker
と同様にUbuntu-20.04
もDドライブに移動しました。VSCode上からdocker-compose buildできない
WSL2上で
Docker
を起動し、ソースコードをWSL2上に置く事でまともに動作する様になりました。
しかし、VSCodeでプロジェクトを開いてdocker-composeでbuildするとエラーが発生してbuildする事ができませんでした。$ docker-compose up -d --build Creating network "only-frontweb_default" with the default driver Building web Traceback (most recent call last): File "docker-compose", line 3, in <module> File "compose\cli\main.py", line 67, in main File "compose\cli\main.py", line 126, in perform_command File "compose\cli\main.py", line 1070, in up File "compose\cli\main.py", line 1066, in up File "compose\project.py", line 615, in up File "compose\service.py", line 346, in ensure_image_exists File "compose\service.py", line 1125, in build File "site-packages\docker\api\build.py", line 148, in build TypeError: You must specify a directory to build in path [19240] Failed to execute script docker-composeこれは単純にVSCodeの使い方を間違っているだけでした。
Windows環境からWSL環境内にアクセスする場合、エクスプローラーから\\wsl$
と打つ事でアクセスできます。
しかしVSCodeからフォルダを開く時に\\wsl$
でWindows側から開くとWSL2で起動しているdockerが上手く動作しないようでした。
VSCodeのターミナル上から、wsl code .と打つ事により、VSCodeがWSL2から起動した事になり、正常にdocker-compose buildできるようになります。
さて、VSCodeの拡張機能にはこういうものがあります。
これをインストールしておけば、
ここから直にWSL2の環境から起動する事ができます。
WSL2側では、WSL2用の拡張機能を有効化する事ができます。
これでクリックのみで完結するようになりました。WSL2でソフトウェアのインストールができない
ここまできてやっとWindowsとWSL2(Ubuntu)は切り離して考えないとダメなんだと分かった私です。
基本的にはDockerで環境作るので基本的には不要ではありますが、それでもインストールしないとならないものが発生してきます。
その為、WSL2でsudo apt updateを実行した所、
Err:1 http://archive.ubuntu.com/ubuntu focal InReleaseこういうエラーが大量に発生してインストールできず。
エラーの種類から調べていくと、どうやらホスト名が解決できていないという事がわかりました。
(他の構築している方の記事を拝見すると、あまり出てこないのでインストールするディストリビューションによって違うのかもしれません)
- /etc/resolv.confを削除する
初期状態では、/run/resolvconf/resolve.conf
へのシンボリックリンクになっているので、これを削除します。/etc/resolv.confを作成する
物理的なファイルを作成し、下記を記述します。nameserver 8.8.8.8 nameserver 8.8.4.4WSL2を再起動する
これでインストール可能になりました。
まとめ
Windowsにはあまりないファイル(フォルダ)の権限との格闘はつづいていますが、これで一通り開発環境は整っています。
今の所WSL2 + Dockerで使用しているのはPHP環境のみなので大きな問題は発生していませんが、様々な環境で開発しているうちに問題が発生した場合は備忘録を兼ねてまた書こうと思います。
FORK Advent Calendar 2020
2日目 strapi+nuxt.jsでアドベントカレンダーを作ろう @sunnyplace
4日目 comming soon @sk2usa
- 投稿日:2020-12-01T17:46:58+09:00
脳死で出来る ECS (Fargate)でDocker環境のデプロイ
はじめに
前回の記事を書いていくと、今の流行はどうやらEC2ではなくECSであるそうなので、今回はそちらを勉強してみました。
備忘録として書いていますが、自分と同じくAWS初心者の方の手助けとなれば幸いです。対象読者
- AWSアカウント登録済み
- EC2やVPCなどの基礎的なAWSの知識は持っている
- 自分
全体構造
ECSとは
Amazon Elastic Container Serviceの略称で、EC2インスタンス(もしくはFargate)コンテナを複数操作することができるサービスです。また、他のAWSサービスとも連携することが容易です。
簡単にECSの全体構造を表してみました。
詳しくはまた作成するときに説明します。
一応こちらが公式です。
ECSを含めた構築の流れ
Dcoker イメージの作成
環境を揃えるため、今回使用するDocker イメージを作成します。
「自分の環境で試したいぜ」って方はこの章は無視してもらって構いません。
一応コードは、ここ に公開しています。
pull
してきたら、README
にしたがってdocker build --tag 『今回のDockerイメージ名(任意)』 .を実行。
docker images REPOSITORY TAG IMAGE ID CREATED SIZE aws-example latest 05ad15c1ad82 About a minute ago 527MBで、イメージが作成できているか確認できます。
ちなみに、
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 817ed89f61b7 aws-example "docker-entrypoint.s…" 32 seconds ago Exited (0) 25 seconds ago gracious_galois docker start 『上の CONTAINER ID』 docker exec -it 『上の CONTAINER ID』 shでコンテナの中に入ることができます。
ECRにpush
ECRとは、Amazon Elastic Container Registryの略称で、Dockerイメージを保管できるレジストリです。
AWS版のDocker Hubみたいな認識でいいです。では早速、DockerイメージをECRに
push
していきます。ECR上のリポジトリ名を記入。
『リポジトリを作成』を押下。
作成したリポジトリを押下。
※ AWS CLIをインストールしていない方は...
AWS公式のdockerイメージをインストール
docker run --rm -it amazon/aws-cli --version長いコマンドを
aws ~~~~
みたいに打てるようにエイリアスする。alias aws='docker run --rm -ti -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli'以上でAWS CLIがコンテナ上で起動するようになります。
ブラウザをリロードして、以下のように表示されていればECRに
push
することができています。
あとで必要なので、ついでに URI もコピーしておきます。
クラスターの作成
今回のコンテナ名を決定。
『イメージ』に先ほどコピーしたECRの URI をペーストする。
『ポートマッピング』に今回開放している 3000番を記入。
『更新』を押下。
『ロードバランサーの種類』は『Application Load Balancer』を選択。
『次』を押下。
『クラスター名』で、今回のクラスターの名前を決定。
『次』を押下。
以下の画面が表示されれば、ECSの作成は完了です。
念のため、デプロイできているか確認します。
http://『パブリックIP』:3000/
でアクセスするとNuxtアプリが表示されます。ACMでSSL証明
ACMとは、AWS Certificate Managerの略称で SSL証明書などを無料で発行できるサービスです。
こちらを用いて、今回デプロイしたアプリを SSL証明していきます。また今回は、Route53で独自ドメインが紐づいたホストゾーンを既に作成しているものとします。
また本稿の前準備として、ロードバランサーのAレコードをホストゾーンに紐づけておいてください。AWSコンソールから、ACMもしくはCertificate Managerを選択。
自分で作成しているホストゾーンに紐づいているドメイン名を記入。
この際サブドメインを設定しても良い。(www.ecs-example.site
みたいにしても良い)
『次へ』を押下。
『▼』を押下。
『Route 53でレコードの作成』を押下。
出てくるポップアップの内容を確認し、『作成』を押下。
『続行』を押下。
ドメイン設定
『クラスター』の『詳細』から、『ターゲットグループ名』にあるターゲットグループを押下。
『説明』タグが選択されていることを確認。
下にスクロールして自分の『セキュリティグループ』を押下。
『ELB Allowed Ports』と記されたセキュリティグループを選択。
『インバウンド』を選択。
『編集』を押下。
『ルールの追加』を押下。
『タイプ』に『HTTP』『HTTPS』を選択。(基本的に自動で『ポート番号』『ソース』が記入されますが、もしそうでない場合手動で記入してください。)
『保存』を押下。
左側のメニューから『ロードバランサー』を選択。
今回使用するVPCを選択。
『リスナーの追加』を押下。
『プロコトル:ポート』を『HTTPS』『443』に選択。
『アクションの追加』を押下。
『転送先...』を選択。
今回使用するターゲットグループを選択。
『デフォルトのSSl証明書』は『ACMから(推奨)』を選択して、先ほどACMで発行したSSL証明書を選択。
上と同様にロードバランサーのメニュー画面から『リスナーの追加』を押下。
『プロコトル:ポート』を『HTTPS』『80』に選択。
『アクションの追加』を押下。
『リダイレクト先...』を選択。
参考記事
Nuxt.jsでアプリ作成→Dockerfileを使ってイメージ作成→fargateでデプロイ→ドメイン設定・SSL化まで
- 投稿日:2020-12-01T17:46:58+09:00
ECS (Fargate) + ACM + Route53 入門
はじめに
前回の記事を書いていくと、今の流行はどうやらEC2ではなくECSであるそうなので、今回はそちらを勉強してみました。
備忘録として書いていますが、自分と同じくAWS初心者の方の手助けとなれば幸いです。対象読者
- AWSアカウント登録済み
- EC2やVPCなどの基礎的なAWSの知識は持っている
- 自分
全体構造
ECSとは
Amazon Elastic Container Serviceの略称で、EC2インスタンス(もしくはFargate)コンテナを複数操作することができるサービスです。また、他のAWSサービスとも連携することが容易です。
簡単にECSの全体構造を表してみました。
詳しくはまた作成するときに説明します。
一応こちらが公式です。
ECSを含めた構築の流れ
Dcoker イメージの作成
環境を揃えるため、今回使用するDocker イメージを作成します。
「自分の環境で試したいぜ」って方はこの章は無視してもらって構いません。
一応コードは、ここ に公開しています。
pull
してきたら、README
にしたがってdocker build --tag 『今回のDockerイメージ名(任意)』 .を実行。
docker images REPOSITORY TAG IMAGE ID CREATED SIZE aws-example latest 05ad15c1ad82 About a minute ago 527MBで、イメージが作成できているか確認できます。
ちなみに、
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 817ed89f61b7 aws-example "docker-entrypoint.s…" 32 seconds ago Exited (0) 25 seconds ago gracious_galois docker start 『上の CONTAINER ID』 docker exec -it 『上の CONTAINER ID』 shでコンテナの中に入ることができます。
ECRにpush
ECRとは、Amazon Elastic Container Registryの略称で、Dockerイメージを保管できるレジストリです。
AWS版のDocker Hubみたいな認識でいいです。では早速、DockerイメージをECRに
push
していきます。ECR上のリポジトリ名を記入。
『リポジトリを作成』を押下。
作成したリポジトリを押下。
※ AWS CLIをインストールしていない方は...
AWS公式のdockerイメージをインストール
docker run --rm -it amazon/aws-cli --version長いコマンドを
aws ~~~~
みたいに打てるようにエイリアスする。alias aws='docker run --rm -ti -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli'以上でAWS CLIがコンテナ上で起動するようになります。
ブラウザをリロードして、以下のように表示されていればECRに
push
することができています。
あとで必要なので、ついでに URI もコピーしておきます。
クラスターの作成
今回のコンテナ名を決定。
『イメージ』に先ほどコピーしたECRの URI をペーストする。
『ポートマッピング』に今回開放している 3000番を記入。
『更新』を押下。
『ロードバランサーの種類』は『Application Load Balancer』を選択。
『次』を押下。
『クラスター名』で、今回のクラスターの名前を決定。
『次』を押下。
以下の画面が表示されれば、ECSの作成は完了です。
念のため、デプロイできているか確認します。
http://『パブリックIP』:3000/
でアクセスするとNuxtアプリが表示されます。ACMでSSL証明
ACMとは、AWS Certificate Managerの略称で SSL証明書などを無料で発行できるサービスです。
こちらを用いて、今回デプロイしたアプリを SSL証明していきます。また今回は、Route53で独自ドメインが紐づいたホストゾーンを既に作成しているものとします。
また本稿の前準備として、ロードバランサーのAレコードをホストゾーンに紐づけておいてください。AWSコンソールから、ACMもしくはCertificate Managerを選択。
自分で作成しているホストゾーンに紐づいているドメイン名を記入。
この際サブドメインを設定しても良い。(www.ecs-example.site
みたいにしても良い)
『次へ』を押下。
『▼』を押下。
『Route 53でレコードの作成』を押下。
出てくるポップアップの内容を確認し、『作成』を押下。
『続行』を押下。
ドメイン設定
『クラスター』の『詳細』から、『ターゲットグループ名』にあるターゲットグループを押下。
『説明』タグが選択されていることを確認。
下にスクロールして自分の『セキュリティグループ』を押下。
『ELB Allowed Ports』と記されたセキュリティグループを選択。
『インバウンド』を選択。
『編集』を押下。
『ルールの追加』を押下。
『タイプ』に『HTTP』『HTTPS』を選択。(基本的に自動で『ポート番号』『ソース』が記入されますが、もしそうでない場合手動で記入してください。)
『保存』を押下。
左側のメニューから『ロードバランサー』を選択。
今回使用するVPCを選択。
『リスナーの追加』を押下。
『プロコトル:ポート』を『HTTPS』『443』に選択。
『アクションの追加』を押下。
『転送先...』を選択。
今回使用するターゲットグループを選択。
『デフォルトのSSl証明書』は『ACMから(推奨)』を選択して、先ほどACMで発行したSSL証明書を選択。
上と同様にロードバランサーのメニュー画面から『リスナーの追加』を押下。
『プロコトル:ポート』を『HTTPS』『80』に選択。
『アクションの追加』を押下。
『リダイレクト先...』を選択。
参考記事
Nuxt.jsでアプリ作成→Dockerfileを使ってイメージ作成→fargateでデプロイ→ドメイン設定・SSL化まで
- 投稿日:2020-12-01T17:11:06+09:00
【コマンド集】Dockerで使用する基本的なコマンド【14選】
これからDockerを使う人のために、dockerの基本的なコマンドをまとめてみました。良ければご活用ください。
Dockerについて
「そもそもDockerって何?」
結論、構築した環境を複数人で使いまわせるツールの事です。
例えば企業などで、複数人で開発を進める時に、エンジニアごとに開発環境やバージョンが異なると、思わぬエラーに遭遇する可能性があります。
しかし、同じ開発環境でそれぞれのエンジニアが開発を進めることによって、上記のような問題は大幅に回避する事ができます。それを簡単にできるようにするのが、Dockerの役割です。
ここでは、詳しい内容は省略して、基本的なコマンドを列挙していきます。もしPCにDockerが入っていない場合は、公式サイトでインストールから初めてください。
公式サイト:https://www.docker.com/
version
まずPCにDockerがインストールされているかどうか、確認しましょう。
コマンドはこちら。$ docker --version
正しくインストールされていれば、このような表示になります。
Docker version 19.03.13もしここで表示が異なる場合は、まだパソコンにDockerがインストールされていない可能性が高いので、公式サイトよりインストールを行いましょう。
login
Dockerを利用するには、まずログインが必要です。コマンドはこちら。
$ docker loginこのコマンドを実行すると、Dockerの公式サイトで登録したログインIDと、パスワードの入力を求められます。ログインに成功したら、下記のような表示になります。
login succeeded
images
次に
docker image
の一覧を取得するコマンドはこちら。$ docker images
ps
次に
コンテナ
の一覧を取得するコマンドはこちら。<!-- Up状態のコンテナ一覧を取得 --> $ docker ps <!-- 全てのコンテナ一覧を取得 --> $ docker ps -a個人的によく使うのは、2つ目のコマンドですね。
Up
、Exited
など関係なく、コンテナを表示してくれます。
また、コンテナごとに詳細情報を確認する方法はこちら
<!-- ハッシュ値で指定したコンテナの詳細情報を表示 --> $ docker inspect 113289ac4d92
こちらは
grepコマンド
などと組み合わせて、コンテナのCPUやMemoryを確認するのに便利です。
pull
次に
DockerHub
からリポジトリを取得するコマンドはこちら。$ docker pull hello-world上記の場合、
hello-world
のリポジトリをdocker image
として取り込みます。
push
今度は逆に
DockerHub
に手元のdocker image
をアップするコマンドです。$ docker push example/first-repo
build
次はホストにある
Dockerfile
をdocker image
にする方法です。
コマンドはこちら。<!-- . はカレントディレクトリを示しています。 --> <!-- docker imageを作成(名前はnone) --> $ docker build . <!-- 名前を指定してdocker imageを作成 --> $ docker build -t new-ubuntu:latest . <!-- Dockerfile名が異なる場合のコマンド --> $ docker build -f Dockerfile.dev .作成された
docker image
は、上記で紹介した$ docker images
で確認できるので、作成したら正常に作られているか確認するようにしましょう。
create
次は
docker image
からコンテナ
を作成する方法です。
コマンドはこちら。$ docker create hello-world作成する際は、予め
$ docker images
で名前かハッシュIDを確認しておきましょう。(hello-worldの部分はハッシュIDでも可能です。)また作成したら、正常に作成されているか、
$ docker ps -a
で確認しましょう。
start
次は作成した(created状態の)
コンテナ
を起動させる方法です。
コマンドはこちら。<!-- ハッシュIDで指定したコンテナを起動(即Exited) --> $ docker start 113289ac4d92 <!-- ハッシュIDで指定したコンテナを起動(継続してUp) --> $ docker start -a 113289ac4d92
またすでに一度起動を行い、
Exited状態のコンテナ
を起動するコマンドはこちら。<!-- Exited状態のコンテナを再起動する --> $ docker restart 113289ac4d92
stop
次は
Up状態のコンテナ
をExitde状態
にする方法です。
コマンドはこちら。$ docker stop 113289ac4d92
run
次に上記で紹介した
createコマンド
とstartコマンド
を、一気に実行する方法です。
コマンドはこちら。<!-- コンテナを作成し起動(名前はnone) --> $ docker run hello-world <!-- コンテナを作成し起動後、bashでコマンド入力できる状態(名前はnone) --> $ docker run -it ubuntu bash <!-- 名前をつけてコンテナを作成し起動 --> $ docker run --name sample ubuntu紹介しておいてあれですが、個人的には
createコマンド
とstartコマンド
はほどんど使用しておらず、ほぼrunコマンド
で済ませています。便利なので、実務でも使用する際は、こちらがメインになると思われます。
runコマンドを理解する上で、createコマンドとstartコマンドも、知っておいた方がいいと思ったので、先に記述させていただきました。
exec
次に
Up状態のコンテナ
でシェル(bashなど)を実行できるようにします。
コマンドはこちら。<!-- 起動状態のコンテナにbashで操作できるようにする --> $ docker exec -it 113289ac4d92 bash
ちなみに、
Exited状態のコンテナ
の場合は、エラーになってしまうので注意が必要です。
また、コンテナの操作から元の操作に戻すには、
$ exit
か、「ctrl」+「p」+「q」
になります。違いは、Up状態を継続するかどうかです。(後者が継続)
rm
次に
Exited状態のコンテナ
を削除する方法です。
コマンドはこちら。$ docker rm 113289ac4d92実行したら、正常にコンテナが削除されているか、
$ docker ps -a
で確認しましょう。
また、一括削除をしたい場合のコマンドはこちら。
$ docker system prune途中、本当に削除するかどうかを聞かれますので、
y
で削除が完了します。
rmi
次に
コンテナで使用されていないdocker image
を削除する方法です。
コマンドはこちら。$ docker rmi hello-world実行したら、正常にdocker imageが削除されているか、
$ docker images
で確認しましょう。
まとめ
以上が
Docker
を扱う上で必要な、基本的なコマンドになります。Docker
は利用している企業も多いと思いますので、習得しておいて損はないかと。最後までご覧いただき、ありがとうございました!
筆者:yuki|学習10日目で初案件獲得→現在はフルスタックエンジニア転職に向けて学習中
Qiita:https://qiita.com/yuki4839
Twitter:https://twitter.com/yuki35522891
- 投稿日:2020-12-01T16:40:53+09:00
local docker 環境の説明
本記事は、サムザップ Advent Calendar 2020 #1 の 12/5 の記事です。
スマホゲームの企画・運営事業を行っているサムザップで、エンジニアをしています森本です
サムザップでは、local 環境を docker を用いて構築しているプロジェクトが多いです
その一例として、私の関わっているプロジェクトでの local 環境を紹介したいと思います
使用している docker コンテナは以下等です
- nginx
- php-fpm
- MySQL
- memcache
- Redis
- phpmyadmin
また、DB マイグレーションに
- phinx migration
を使用しています
フォルダ構成
local/ ├ application/ │ ├ mysql/ │ │ ├ 1_initial_mysql.sh │ │ └ 2_initial_mysql.sql │ └ web/ │ └ initial_web.sh ├ docker/ │ ├ gatling/ │ │ └ Dockerfile │ ├ misc/ │ │ └ data/ │ │ └ DB実態 │ ├ nginx/ │ │ ├ 鍵回り省略 │ │ ├ Dockerfile │ │ ├ fastcgi.conf │ │ ├ nginx.conf │ │ └ server.conf │ ├ php-fpm/ │ │ ├ Dockerfile │ │ ├ php.ini │ │ └ www.conf │ └ docker-compose.yml ├ dump/ │ └ 移行前のDB,テーブル定義 ├ mysql/ │ └ custom.cnf └ phpmyadmin/ ├ conf/ │ └ config.user.inc.php └ sessions/ └ 省略local/application/
local/application/mysql/
docker の MySQL image では /docker-entrypoint-initdb.d/ というディレクトリ内に初期化用の SQL やスクリプトを置くことで、最初に image を起動したときにデータの初期化を自動的に行う仕組みがあります
- 1_initial_mysql.sh
cd /dump/ zcat 過去DB_table.gz | mysql -uroot -p***** ...phinx 導入前の DB、テーブルを作成しています
- 2_initial_mysql.sql
grant all on *.* to user1@'%' identified by '*****'; grant all on *.* to user2@'%' identified by '*****'; ... insert ...mysql ユーザー作成、管理画面初期データ作成 しています
実行順は、ファイル名順のため、ファイル名の頭に「1_」「2_」がついています
local/application/web/
- initial_web.sh
cd .../PhinxMigration if [ ! -e vendor ]; then リンク作成(省略) cd .../PhinxMigration composer install else until mysqladmin ping -h mysql --silent; do echo "" echo 'waiting for mysqld to be connectable...' echo "" sleep 3 done cd .../phinx_migration/ make migrate ENV=localhost fiweb 起動時
初回起動の場合
(という切り分けができなかったので、composer install に実行時に作成される vender フォルダがない場合)
cd .../PhinxMigration
composer install 実行初回起動ではない場合
(vender フォルダがある場合)
mysql サーバが立ち上がるまでループ
DB マイグレーション同期実行を実行しています
docker 起動時(=web 起動時) に、毎回、DB マイグレーション同期実行 するようにしています
composer install 実行後、DB マイグレーション同期実行 する方法(else の削除)
は、課題として残っていますlocal/docker/
local/application/nginx/
- Dockerfile
FROM nginx ADD nginx.conf /etc/nginx/nginx.conf ADD fastcgi.conf /etc/nginx/fastcgi.conf ADD server.conf /etc/nginx/conf.d/default.conf ADD localhost.key /etc/ssl/private/localhost.key ADD localhost.crt /etc/ssl/certs/localhost.crt ADD dhparam.pem /etc/nginx/ssl/dhparam.pemnginx イメージに各設定ファイルをaddしています
- fastcgi.conf
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; ...一般的な検索結果サイトから取得したもの
+
プロジェクトで使用しているものを追加しています
- nginx.conf
user www-data; pid /var/run/nginx.pid; # coreモジュールの設定 http{ # httpモジュールの設定 } ...一般的な検索結果サイトから取得したもの
+
ソケット通信に変更しています
user www-data;
pid /var/run/nginx.pid;
- server.conf
server { listen 80; ... } server { listen 443 ssl http2; ... location / { # other try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { fastcgi_pass unix:/var/run/php7-fpm.sock; fastcgi_index index.php; include fastcgi.conf; } ssl_certificate /etc/ssl/certs/localhost.crt; ssl_certificate_key /etc/ssl/private/localhost.key; ... } server { listen 80; server_name ~^(?<user>.+)\.local\.jp$; root /var/www/$user/; ... }一般的な検索結果サイトから取得したもの
+
プロジェクトで使用している設定(記述変更、削除しています)
+
ソケット通信に変更しています
fastcgi_pass unix:/var/run/php7-fpm.sock;また、ローカルに複数ソースを git clone している人のために
例
test.local.jp
にアクセスすると
/var/www/test/
以下のソースを見に行くようにしていますlocal/application/php-fpm/
- Dockerfile
FROM php:7.4-fpm ADD php.ini /usr/local/etc/php/conf.d/php.ini ADD www.conf /usr/local/etc/php-fpm.d/zzz-www.conf RUN apt-get update && apt-get install -y libmcrypt-dev mariadb-client bc zlib1g-dev libmemcached-dev procps curl unzip git libonig-dev \ && docker-php-ext-install pdo_mysql mysqli mbstring opcache bcmath RUN pecl install memcached-3.1.3 \ && docker-php-ext-enable memcached RUN mkdir -p /usr/src/php/ext/redis \ && curl -L https://github.com/phpredis/phpredis/archive/5.1.1.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 \ && echo 'redis' >> /usr/src/php-available-exts \ && docker-php-ext-install redis RUN pecl install timecop-beta \ && docker-php-ext-enable timecop RUN pecl install apcu \ && docker-php-ext-enable apcu # install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composerソケット通信の設定ファイルは、「zzz-」をつける必要があります
PHPの公式DockerイメージでUNIXソケット通信しようとして罠にハマるの巻
ADD www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
- www.conf
ソケット回りの設定等
[www] listen = /var/run/php7-fpm.sock listen.owner = www-data listen.group = www-data request_terminate_timeout = 30slocal/application/docker-compose.yml
version: '3.7' volumes: php_sockert: services: memcached: image: memcached container_name: memcached redis: image: redis container_name: redis mysql: image: mysql:5.7.31 command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --max_allowed_packet=32M environment: MYSQL_ROOT_PASSWORD: ******** MYSQL_DATABASE: mysql_database MYSQL_USER: user MYSQL_PASSWORD: ******** TZ: "Asia/Tokyo" ports: - "3306:3306" volumes: - ../application/mysql/:/docker-entrypoint-initdb.d/ - ../dump/:/dump/ - ../mysql/:/etc/mysql/conf.d/ - ./misc/data:/var/lib/mysql container_name: mysql nginx: build: ./nginx ports: - "80:80" - "443:443" environment: TZ: "Asia/Tokyo" volumes: - ../../local/:/var/www/local # local複数環境用 - ../../test/:/var/www/test - php_sockert:/var/run depends_on: - web container_name: nginx web: build: ./php-fpm tty: true privileged: true ulimits: core: -1 volumes: - ../application/web/initial_web.sh:/tmp/initial_web.sh - ../../local/:/var/www/local # local複数環境用 - ../../test/:/var/www/test - php_sockert:/var/run depends_on: - mysql - memcached - redis - rabbitmq environment: DATABASE_HOST: 'mysql' DATABASE_NAME: 'mysql_database' DATABASE_USER: 'user' DATABASE_PASSWORD: '********' TZ: "Asia/Tokyo" command: bash -c "bash /tmp/initial_web.sh && php-fpm" container_name: web phpmyadmin: image: phpmyadmin/phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=mysql - PMA_USER=root - PMA_PASSWORD=******** links: - mysql ports: - 8080:80 volumes: - ../phpmyadmin/sessions:/sessions - ../phpmyadmin/conf/config.user.inc.php:/etc/phpmyadmin/config.user.inc.phpimage: memcached
docker hub から落としてくるイメージですimage: mysql:5.7.31
等「:」でバージョン指定可能ですnginx:
build: ./nginx
のように記述すると、./nginx/Dockerfile を読みにいきます
Dockerfile 内で image を指定したり
command を指定したりしていますcontainer_name: memcached
etc/hosts に上記名称が追加されますvolumes:
- ./misc/data:/var/lib/mysql
ホストの /misc/data 以下と
ゲストの /var/lib/mysql 以下が共有されますmysql 上記データをホストとゲストを共有していないと、docker の mysql を落とすとデータが削除されます
volumes:
php_sockert:nginx:
volumes:
- php_sockert:/var/runweb:
volumes:
- php_sockert:/var/run
nginx と web の /var/run が共有されます
ソケットで使用していますcommand:
イメージが作られた時点で、記述内容が実行されます
ports:
ホストのポートとゲストのポートが結びつきますnginx:depends_on:
- web
nginx コンテナからwebコンテナへ通信するため web 起動してから、nginx 起動するように設定最後に
私の関わっているプロジェクトでの local 環境を紹介いたしました
今後は
- 本文中に記述した問題の改修
- jenkins 導入
などを改修、導入できればと思っています!!
明日は @shirai_suguru です。お楽しみに!!
サムザップのアドベントカレンダーは人数の関係上2つあります。
こちらもよろしくお願いいたします!
サムザップ #2 Advent Calendar 2020 - Qiita
- 投稿日:2020-12-01T15:50:38+09:00
13年ぶりにwindowsで Web開発環境を構築したメモ
Ateam Group Manager & Specialist Advent Calendar 2020の24日目はエイチーム ライフスタイルサポート事業本部でエンジニア、デザイナーのマネジメントをしている @kopug が担当します。
13年前からmacばかり使っており、まったくwindowsとはご無沙汰だったのですが、最近のwindows環境に疎くなってしまったのもあり、この度windows環境 (Thinkpad)に変えてWeb開発環境を構築をしたので、備忘録も兼ねて残していきます。
WSL2 + Ubuntuのインストール
windows環境に変えた!と言いつつも、開発環境としては Linux を使いたいので、WSL2とUbuntuのインストールをしていきます。
…といっても、オフィシャルドキュメントで構築方法が丁寧に書かれているので、以下の手順通りにやれば簡単にインストールができます。
Windows Subsystem for Linux (WSL) を Windows 10 にインストールする | Microsoft Docs
Linux 用 Windows サブシステムには 2 つの異なるバージョンがあり、インストール プロセス中にどちらかを選択します。 全体的なパフォーマンスは WSL 2 の方が優れている ...Windows Terminal
昔は Cygwin を入れて、Tera Term or Putty で操作していたなぁ…と思い出しながらターミナルは何がいいのかを探していたら、上で紹介したドキュメントにもあるように、Windows Terminal をインストールしました。
Windows Terminal を入手 - Microsoft Store ja-JP
Windows ターミナルは、コマンド プロンプト、PowerShell、WSL などのコマンドライン ツールおよびシェルのユーザーのための、高速、効率的、強力な、生産性を向上させる最新のターミナル アプリケーション...設定のショートカットが「Ctrl + ,」で呼び出せて、中身は JSON なんですね。
私は以下の設定のみしてます。Windows Terminal を立ち上げたら Default で Ubuntu になるようにする
"profiles": { "list": [] } に Ubuntu の GUID があるので、それをdefaultProfileにセットしてあげればOKです。
{ "$schema": "https://aka.ms/terminal-profiles-schema", "defaultProfile": "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx}", // 省略FontをRictyに変更 + 背景色を透明に
この辺は好みの問題なので、好きなように。
RictyDiminished-for-Powerline から フォントをダウンロードし、インストールをします。
次に fontFace, fontSize を以下のように指定しています。背景色を透明にする場合は、useAcrylicを有効(true)にし、acrylicOpacityで透明度を 0-1 で指定します。
それ以外にも色々と設定ができるので、詳細は以下を参考にしてください。
Windows Terminal Profile Settings | Microsoft Docs"profiles": { "defaults": { // Put settings here that you want to apply to all profiles. "fontFace": "Ricty Diminished", "fontSize": 14, "useAcrylic": true, "acrylicOpacity": 0.8 }, "list": [shellをbashからzshに変更
パッケージを最新版にしてから、zsh をインストール
$ sudo apt update $ sudo apt upgrade $ sudo apt install zshインストールが終わったら chsh で zsh に変更します。
$ chsh -s $(which zsh)あとは自分のお好みで、.zshrc .zprofile 等に変えていってください。
私は今回 zplug から prezto にこのタイミングで変えました。Docker
Dockerのインストール及び設定に関しても、Microsoftの公式ドキュメントに丁寧に書かれております。
こういう情報がMicrosoftから手に入る時代になったことに個人的には感動しました。笑
VS Code を使用したリモートコンテナーでの開発に関してもこちらのドキュメントに書かれているので、VS Code を使われている方はそちらをご覧ください。
Windows Subsystem for Linux で Docker コンテナーの使用を開始する | Microsoft Docs
このステップバイステップガイドでは、WSL 2 (windows Subsystem for Linux、バージョン 2) を使用して Docker Desktop For Windows をセットアップすることで、リモートコンテナーを使用した開発を始めることができます。Vimの設定
最近 .vimrc は自分で管理しておらず、 Vim Bootstrap のお世話になっております。
Vim Bootstrap
A generator which provides a simple method of generating a .vimrc configuration for vim上記にアクセスをして、vimで使っている言語を選択をし、カラー設定等を選んで、Generate! ボタンを押してそれをvimに食わせてもいいのですが、私は以下の方法でセットアップしております。
$ curl 'http://vim-bootstrap.com/generate.vim' --data 'langs=javascript&langs=php&langs=html&langs=ruby&langs=go&editor=vim' > ~/.vimrc $ vim +PlugInstall +qall詳しくはこちらをご覧ください。
最後に
最近業務で開発はする機会は少なくなっておりますが、PC組んだり環境構築をするのは趣味なのかもしれません。笑
今回久々にwindowsを触っておりますが、完全に浦島太郎状態でした。今回書いてはいませんが、windowsにもwingetという brew みたいなパッケージ管理もありこの変化にキャッチアップをしてしばらく楽しめそうです。Ateam Group Manager & Specialist Advent Calendar 2020のラストは @zwirky がお送りします!バトンを託します。
- 投稿日:2020-12-01T14:56:17+09:00
docker自分用
- 投稿日:2020-12-01T14:29:03+09:00
dockerコマンド
イメージのダウンロード(docke hubからのダウンロード)
$ docker image pull centos: 7
イメージ の 一覧 表示
$ docker image lsイメージ の 削除
$ docker image rm nginxコンテナ 生成/ 起動
$ docker container run --name webserver -d -p 80:80 nginx※-d は バックグランドでの起動
コンテナ 起動
$ docker start webserverコンテナ 停止
$ docker stop webserver稼働 コンテナ の 一覧 表示
$ docker container lsコンテナを停止中のものも含めた一覧
docker ps --allコンテナ の 稼働 確認
$ docker container stats webserverコンテナ の 削除
$ docker container rm webserver引用
WINGSプロジェクト阿佐 志保. プログラマのためのDocker教科書 第2版
コンテナのライフサイクルと基本コマンド
- 投稿日:2020-12-01T13:34:08+09:00
swagger-ui,swagger-editor環境構築(AWS/Docker)
前提条件
- AWS EC2インスタンス作成ができている
- EC2インスタンスにnginxインストール済
EC2にDockerをインストール
EC2インスタンスにSSH接続後、下記コマンドを入力
sudo amazon-linux-extras install dockerDocker起動
sudo systemctl start dockerDocker起動確認
sudo systemctl status dockerActiveになっていればOK
swagger-editorのイメージをpull
sudo docker pull swaggerapi/swagger-editorswagger-uiのイメージをpull
sudo docker pull swaggerapi/swagger-uiswagger-editorを起動
sudo docker run -d -p 8000:8080 swaggerapi/swagger-editorswagger-uiを起動
sudo docker run -d -p 8001:8080 swaggerapi/swagger-uiコンテナ起動確認
sudo docker ps
swagger-editorとswagger-uiが表示されればOKnginx.confに追記
nginx.confを開く
sudo vim /etc/nginx/nginx.conf下記内容をnginx.confに追記
server{}内に必ず追記するようにしてください。location /swagger-editor/docker/ { proxy_pass http://localhost:8000/; proxy_redirect off; } location /swagger-ui/docker/ { proxy_pass http://localhost:8001/; proxy_redirect off; }nginxを再起動or再読み込み
再起動
sudo systemctl restart nginx再読み込み
sudo systemctl reload nginxすでに、docker上で動かしているコンテナがswagger系以外にもあるなら、再読み込みがおすすめ
再起動して失敗してしまうと、正常に動いていた機能も含めて全て利用不可になる可能性があるので。URLにアクセスして、swagger-editorの表示を確認
http://(IPv4アドレス)/swagger-editor/docker/URLにアクセスして、swagger-uiの表示を確認
http://(IPv4アドレス)/swagger-ui/docker/自動起動設定(nginx,docker編)
sudo systemctl enable nginxsudo systemctl enable docker自動起動設定(dockerコンテナ編)
コンテナ単体の自動起動設定をすることもできます。
コンテナ起動後に、下記コマンドを入力してください。sudo docker update --restart=always コンテナ名自動起動設定を無効化する場合は
sudo docker update --restart=no コンテナ名最後に
以上が、AWS/EC2とDockerを使ったswagger-ui,swagger-editorの環境構築手順です。
不明点、質問等があればコメント欄にお願い致します。
- 投稿日:2020-12-01T12:44:01+09:00
Laravelでschedule:runしたときのoutputがdockerで標準出力されない問題を解消
はじめに(何が問題か)
以下のようなコマンドがあって、
app/Console/Commands/Hello.php/** * Execute the console command. * * @return mixed */ public function handle() { $this->output->writeLn('Hello !!'); return 0; }下記のように、スケジュール登録していたとして、
app/Console/Kernel.php/** * Define the application's command schedule. * * @param Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('app:hello')->everyMinute(); }
artisan schedule:run
しても$ docker-compose run --rm task-scheduler Creating bizany-api_task-scheduler_run ... done Running scheduled command: '/usr/local/bin/php' 'artisan' app:hello >> '/dev/null' 2>&1
Hello !!
は、標準出力されないんですね。
これを解消したという記事になります。
appendOutputTo('/dev/stdout')
は効かない
\Illuminate\Console\Scheduling\Event
にはappendOutputTo
というメソッドがあって、出力先を指定することができます。
指定しないと/dev/null
になってしまいます。
とはいえ、結論として、appendOutputTo('/dev/stdout')
は効きません。app/Console/Kernel.php/** * Define the application's command schedule. * * @param Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('app:hello')->appendOutputTo('/dev/stdout')->everyMinute(); }どうやって解消したか
答えは、
stackoverflow
にありました。Dockerfileにて、
RUN ln -sf /proc/1/fd/1 /var/log/laravel-scheduler.logとした上で、出力先を
/var/log/laravel-scheduler.log
とします。app/Console/Kernel.php/** * Define the application's command schedule. * * @param Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('app:hello')->appendOutputTo('/var/log/laravel-scheduler.log')->everyMinute(); }すると、
Hello !!
が標準出力されるようになります。
(Dockerコンテナのリビルドをお忘れなく)$ docker-compose run --rm task-scheduler Creating bizany-api_task-scheduler_run ... done Running scheduled command: '/usr/local/bin/php' 'artisan' app:hello >> '/var/log/laravel-scheduler.log' 2>&1 Hello !!おわりに
Laravel
に限った話ではないのかもしれませんが、とにかく何も出力されなくて困り果てていたので解決できて良かったです。
同じ状況で困っている方のお役に立てればと思います。余談
世の中、アドベントカレンダーの季節ですが、普通に記事書いてしまいました 。。
まあ、今後も書きたいときに書いていきます。ではでは。
- 投稿日:2020-12-01T12:41:13+09:00
Dockerで始めるDjango生活(6日目)
初めに
6日目です。
5日目はこちら
一旦今日が最終回になります!
今まで読んでいただきありがとうございました!目次
- ビューを作成する
- ルーティングを行う
- ビューのテンプレートを作成する
- 画面の表示を確認する。
- 最後に
1. ビューを作成する
まずは、開発ディレクトリに遷移します。
# cd /root/projects/myproject/myproject/blog # pwd /root/projects/myproject/myproject/blog # ls __init__.py __pycache__ admin.py apps.py migrations models.py tests.py views.py #ビューはblogディレクトリ内のviews.pyに書きます。
このビューは投稿を取得します。views.py(修正前)from django.shortcuts import render # Create your views here.views.py(修正後)from django.shortcuts import render, get_object_or_404 from .models import Post # Create your views here. def post_list(request): posts = Post.published.all() #5日目の最後で作成したpublishedを使用(publishedのみを取得する) return render(request, 'blog/post/list.html', {'posts': posts})次に投稿1つ1つを表示するビューを作成します。
views.pyに下記を追記します。views.pydef post_detail(request, year, month, day, post): post = get_object_or_404(Post, slug=post, status='published', publish__year=year, publish__month=month, publish__day=day) return render(request, 'blog/post/detail.html', {'post': post})次に今作成したビューを表示するためにルーティングを行います。
2. ルーティングを行う
ルーティングを行うためには
まずblogディレクトリにurls.pyを作成します。urls.pyfrom django.urls import path from . import views app_name = 'blog' urlpatterns = [ path('', views.post_list, name='post_list'), path('<int:year>/<int:month>/<int:day>/<slug:post>/', views.post_detail, name='post_detail'), ]次にmyprojectのurls.pyを修正します。
urls.py(修正前)from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ]urls.py(修正後)from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls', namespace='blog')), ]1つ1つの投稿にリンクを作成するためにblogのmodels.pyを修正します。
models.pyfrom django.urls import reverse class Post(models.Model): # ... def get_absolute_url(self): return reverse('blog:post_detail', args=[self.publish.year, self.publish.month, self.publish.day, self.slug])3. ビューのテンプレートを作成する
まずは、下記の様にディレクトリとファイルを作成してください。
# pwd /root/projects/myproject/myproject/blog # tree templates templates `-- blog |-- base.html `-- post |-- detail.html `-- list.html 2 directories, 3 files #ファイルには次の内容を書き込んでください。
base.html{% load static %} <!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> <link href="{% static "css/blog.css" %}" rel="stylesheet"> </head> <body> <div id="content"> {% block content %} {% endblock %} </div> <div id="sidebar"> <h2>My Blog</h2> <p>This is my blog.</p> </div> </body> </html>list.html{% extends "blog/base.html" %} {% block title %}My Blog{% endblock %} {% block content %} <h1>My Blog</h1> {% for post in posts %} <h2> <a href="{{ post.get_absolute_url }}"> {{ post.title }} </a> </h2> <p class="date"> Published {{ post.publish }} by {{ post.author }} </p> {{ post.body|truncatewords:30|linebreaks }} {% endfor %} {% endblock %}detail.html{% extends "blog/base.html" %} {% block title %}{{ post.title }}{% endblock %} {% block content %} <h1>{{ post.title }}</h1> <p class="date"> Published {{ post.publish }} by {{ post.author }} </p> {{ post.body|linebreaks }} {% endblock %}次にbase.htmlで読み込む[css/blog.css]を作成します。
blogディレクトリに/static/css/blog.cssを作成します。blog.cssbody { margin:0; padding:0; font-family:helvetica, sans-serif; } a { color:#00abff; text-decoration:none; } h1 { font-weight:normal; border-bottom:1px solid #bbb; padding:0 0 10px 0; } h2 { font-weight:normal; margin:30px 0 0; } #content { float:left; width:60%; padding:0 0 0 30px; } #sidebar { float:right; width:30%; padding:10px; background:#efefef; height:100%; } p.date { color:#ccc; font-family: georgia, serif; font-size: 12px; font-style: italic; } /* pagination */ .pagination { margin:40px 0; font-weight:bold; } /* forms */ label { float:left; clear:both; color:#333; margin-bottom:4px; } input, textarea { clear:both; float:left; margin:0 0 10px; background:#ededed; border:0; padding:6px 10px; font-size:12px; } input[type=submit] { font-weight:bold; background:#00abff; color:#fff; padding:10px 20px; font-size:14px; text-transform:uppercase; } .errorlist { color:#cc0033; float:left; clear:both; padding-left:10px; } /* comments */ .comment { padding:10px; } .comment:nth-child(even) { background:#efefef; } .comment .info { font-weight:bold; font-size:12px; color:#666; }ここまで完了するとblogディレクトリは下のようなファイルの配置になっています。
# tree blog blog |-- __init__.py |-- __pycache__ | |-- __init__.cpython-38.pyc | |-- admin.cpython-38.pyc | |-- apps.cpython-38.pyc | |-- models.cpython-38.pyc | |-- urls.cpython-38.pyc | `-- views.cpython-38.pyc |-- admin.py |-- apps.py |-- migrations | |-- 0001_initial.py | |-- __init__.py | `-- __pycache__ | |-- 0001_initial.cpython-38.pyc | `-- __init__.cpython-38.pyc |-- models.py |-- static | `-- css | `-- blog.css |-- templates | `-- blog | |-- base.html | `-- post | |-- detail.html | `-- list.html |-- tests.py |-- urls.py `-- views.py 8 directories, 21 files #4. 画面の表示を確認する。
ここで
127.0.0.1:8000/blog/
を確認します。
すると下のような画面になっているかと思います。
ここで投稿した物が表示されていないのは全て投稿の状態がdraftになっているためです。
ですのでテストで投稿していた一つをpublishedにします。
まずは、管理サイトに遷移します。
127.0.0.1:8000/admin/blog/post/
New titleを選択してstatusをpublishedに変更して保存します。
再度127.0.0.1:8000/blogに遷移すると下の様にpublishedにした方が表示されていると思います。
また、青いNew titleをクリックすると詳細画面に遷移します。
5. 最後に
この6日間でモデルを作成してテーブルを作り管理サイトを作り画面表示まで行いました。
djangoにはもっともっと奥深く色々なことができますので色々調べてみてください!
始めてこういった連載記事を投稿して拙い部分が多々ありましたが読んでいただきありがとうございました!
この記事では下記の本を参考に書かせていただいています。
この記事ではDjangoを実際に触って動かすことをメインに書いていますので上記のマイグレーションの詳しい説明などはこの本を読んでただければと思います。
Django 3 By Example - Third Edition
- 投稿日:2020-12-01T11:15:55+09:00
docker-composeでnginxを立てる際に環境変数を使う方法
docker-composeでnginxを立てる際に、confファイルに環境変数を使う方法について解説します。nginxのversionが1.19より前と後でやり方が異なるので注意してください。
nginx:1.19以降
1.19以降では公式にサポートされている方法を使うことができます。
以下のようなdocker-compose.ymlを例に用います。docker-compose.ymlversion: "3" services: nginx_service: container_name: nginx image: nginx:1.19-alpine volumes: - ./templates:/etc/nginx/templates environment: - PORT=8080 ports: - 3000:8080デフォルトではコンテナ内の
/etc/nginx/templates/*.template
が読み込まれ、環境変数をセットした結果が、/etc/nginx/conf.d
に吐き出されます。例えば、
default.conf.template
を以下のように用意します。templates/default.conf.templateserver { server_name localhost; listen ${PORT}; }
docker-compose up --build
を実行すると、上記ファイルがコンテナ内の/etc/nginx/templates
にマウントされます。コンテナ内の
/etc/nginx/conf.d
を確認すると、以下のように環境変数がセットされたファイルが出力されていることが確認できます。/etc/nginx/conf.d/default.confserver { server_name localhost; listen 8080; }nginx:1.19以前
1.19以前ではenvsubstコマンドを使用して、自前で環境変数をセットする必要があります。
以下のようなdocker-compose.ymlを例に用います。上記との違いは、commandでinit.shを実行していることです。version: "3" services: nginx_service: container_name: nginx_1.19_earlier image: nginx:1.18-alpine volumes: - ./nginx:/etc/nginx/conf.d environment: - PORT=8080 ports: - 3000:8080 command: sh /etc/nginx/conf.d/init.sh
default.conf.template
を以下のようにします。これは上記と変わりません。templates/default.conf.templateserver { server_name localhost; listen ${PORT}; }
init.sh
を以下のようにします。
envsubstコマンドでdefault.conf.template
に環境変数をセットして、結果をdefault.conf
に出力します。nginxを立ち上げるコマンドも忘れずに記載します。templates/init.sh#!/bin/sh envsubst '$$PORT' < \ /etc/nginx/conf.d/default.conf.template > \ /etc/nginx/conf.d/default.conf nginx -g 'daemon off;'
docker-compose up --build
を実行すると、コンテナ内の/etc/nginx/conf.d
で環境変数がセットされたファイルを確認できます。/etc/nginx/conf.d/default.confserver { server_name localhost; listen 8080; }説明に使用したファイルは以下のレポジトリを参照してください。
https://github.com/happyfukumoto/nginx_on_dockercompose
- 投稿日:2020-12-01T10:14:33+09:00
Laravel × Docker AlpineでER図を自動生成する
こんにちは。むらってぃーです。
Laravel Advent Calendar 2020の3日目を担当させていただきます。皆さんはER図使っていますか?
エンティティ同士の関係が一目でわかるドキュメントであるため、手元にあれば非常に便利なものとして効力を発揮します。
一方で、DBスキーマを更新するたびにER図を書き換える必要があり、メンテナンスコストが少々高めです。今回はLaravelでアプリケーションを開発する際、Eloquent ModelからER図を自動生成するツールを紹介します。
DockerのAlpineイメージでLaravelを動かし、その上で生成します。
そのため、チームで運用する際にも導入しやすいです。Laravel ER Diagram Generator
https://github.com/beyondcode/laravel-er-diagram-generator
LaravelのEloquentModelから、ER図を自動生成するライブラリです。
Star数は1000超えで、多くの方に利用されているみたいです。インストール
Dockerfile
Laravelを動かしているAlpineイメージに下記を追加します。
内部でgraphvizというツールを使っているため、apk経由でそちらをインストールします。
フォントもインストールしないと、ER図に日本語や英語が出力されないので入れておきます。RUN apk --no-cache add graphviz fontconfig \ && rm -rf /var/cache/apk/* \ # graphviz用フォントインストール && curl -O https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip \ && mkdir -p /usr/share/fonts/NotoSansCJKjp \ && unzip NotoSansCJKjp-hinted.zip -d /usr/share/fonts/NotoSansCJKjp/ \ && rm NotoSansCJKjp-hinted.zip \ && fc-cache -fvcomposer.json
Alpineイメージに入り込み、下記コマンドでLaravel ER Diagram Generatorを入れます。
composer require beyondcode/laravel-er-diagram-generator --devServiceProvider
ローカルでのみこのライブラリが読み込まれるようにします。
AppServiceProvider.phpclass AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { if ($this->app->environment('local')) { // ER図生成用のProviderはcomposer の require-devでインストールしているので、localでのみ使用 $this->app->register(\BeyondCode\ErdGenerator\ErdGeneratorServiceProvider::class); } } ...config用意・修正
下記コマンドで、configファイルをライブラリ内からプロジェクト内にコピー。
cp ./vendor/beyondcode/laravel-er-diagram-generator/config/config.php config/erd-generator.php今回は App/Models配下にモデルファイルを置くため、configでModelファイルの読み込み先を下記に変更します。
config/erd-generator.php<?php return [ /* * All models in these directories will be scanned for ER diagram generation. * By default, the `app` directory will be scanned recursively for models. */ 'directories' => [ base_path('app') . '/Models', ], ...Model用意
では、Modelを用意します。
Model概要
今回は下記のモデルにしてみます。
Book(本)
カラム名 備考 ID 自動採番ID Title タイトル AuthorID 著者ID. 著者テーブルへのリレーション. Author(著者)
カラム名 備考 ID 自動採番ID Name 著者名 マイグレーションファイル用意
authorsテーブルとbooksテーブルを作ります。
2020_11_09_154434_create_tables.php... class CreateTables extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('authors', function (Blueprint $table) { $table->increments('id'); $table->integer('name'); $table->timestamps(); }); Schema::create('books', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->integer('author_id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('books'); Schema::dropIfExists('authors'); } }こちらはそのまま流します。
php artisan migrateModelファイル用意
app/Models/Author.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Author extends Model { }app/Models/Book.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Book extends Model { public function author(): BelongsTo { return $this->belongsTo('App\Models\Author'); } }Bookには、Authorへのリレーションを記載します。
なお、ER図に記載されるその他のフィールドは、アプリケーションが繋がっているDBから読み取ってくれる動きです。
ER図生成
Alpineコンテナの中に入り、下記のコマンドを打ちます。
php artisan generate:erd er-diagram.pngすると、プロジェクトルートに er-diagram.pngというファイルが出来上がります。
Userは、Laravelで最初から組み込まれているものです。
このようにBookとAuthorの関係がER図として出力されています。BelongsToの矢印が色付けされているのが個人的には嬉しいです。
最後に
今回はLaravel ER Diagram Generatorを使い、Larvel × Docker AlpineでER図を自動生成する方法を紹介しました。
一度このように土台を作れば、migrationファイルとModelファイルの管理のみでER図を自動生成できます。
メンテナンスコストがほとんどかからないのが嬉しいですね。
ER図生成のコマンドはCIに組み込むなりして、常に最新に保つことができればさらに幸せになれそうですね!
- 投稿日:2020-12-01T08:33:23+09:00
Docker(docker-compose) + MySQL でのパスワード入力時のエラー / ERROR 1045 (28000): Access denied for user
RailsをAPIモードで Docker + MySQL を使用してWebアプリ開発をしています。
環境構築時に、
docker-compose up
して dbにログインしようとすると
ERROR 1045 (28000): Access denied for user
と
ログインができないエラーに遭遇したので、私が解決できた方法を記載しておきます!解決法
私の場合は、docker-compose.ymlで
docker-compose.ymldb-data:/var/lib/mysql:cached
このようにしてdbデータをローカルに保存してマウントしていたのですが、
ここに試しに作っていた、いくつかのvolumeが残っていたため、
そちらの環境変数が優先され、現在のパスワードが反映されていないという問題でした。MySQL公式に
既にデータベースを格納しているデータディレクトリとともにコンテナを起動した場合は、下記の変数はどれも影響を及ぼさないことに留意してください。
とあるようです。
詳しくは こちら の記事をご覧ください。つまり、
マウント先のローカルのdbを一旦削除して、再度作り直せばOKということです。手順
docker-compose down docker volume ls docker volume rm (lsで表示されたvolume名をスペース区切りで並べる) docker-compose up
で、作り直して再度ログインすると、問題なくログインできました!!
上記の手順で、
docker volume rm
時にもしかすると、削除できない場合があるかもしれません。
その時は、docker system prune docker volume rm ○○
とするとうまくいくかも知れません!!
- 投稿日:2020-12-01T08:16:38+09:00
Railsチュートリアルやった後くらいに学んだ方が良いことをUdemyで学ぶ
はじめに
自分は去年から未経験でアプリ開発の現場に就職しました。
当時のレベルはRailsチュートリアルを3周くらいして、まあ多少はRailsのコードは書けるのかな?程度のレベルでしたが、もちろん実務ではそんなものでは全然足りずUdemyで色々受講したりして勉強しました。そこで自分が受講して実際に役にたっている講座と内容を紹介したいと思います。
良い講座ばかりですが、注意点も多少あるので各章の最後に書いておきます。Docker
開発環境を整えるのにもはやDockerは必須ですよね。
Dockerという言葉は聞いたことあるけど、よく分かってない。そんな声がちらほら。私も環境構築に使うんでしょ?くらいの認識でした。
ということで下記講座を受けました。ゼロからはじめる Dockerによるアプリケーション実行環境構築
Dockerとは何かから、実際にコンテナを立てたり、docker-composeでRails環境作るところまで分かりやすく解説されています。
※注意点
最後にSwarmの解説ありますが、今のデファクトスタンダードはKubernetes、略してk8s(かっこいい)なので、ここは特に見なくても良いのかなって気がしました。(もちろん勉強になると思います)React
Dockerで環境構築できたらRailsはAPIモードにしてバックエンド専用、フロントはモダンなやり方で実装したいですね。そこでReactやります。
Railsチュートリアル終わったくらいだと、おそらくjQueryは分かると思いますし、RailsのViewをSlimで書いたりしていたと思いますが、バックエンドとフロントエンドを分離して、RailsはAPIでデータを返すだけ、フロントでの描画はReactで書きましょう。
Reactは二種類の書き方があり、一つはクラスコンポーネント、もう一つは関数コンポーネントです。
公式ではクラスコンポーネントはサポートを廃止する予定はないものの、新しいプロジェクトでは関数コンポーネントで書いても良いんじゃないかと言っていますので、おそらく関数コンポーネント推しなのではないでしょうか。(関数コンポーネントを絶対に使えとは書いてなかったです)Reactのv16.8からHooksというものが導入されて、関数コンポーネントでstateを扱えるようになりました。最近は関数コンポーネントでしかコード書いてません。というか関数コンポーネントからしか勉強してないので関数コンポーネントしか書けません笑
以下動画ではHooksを使った関数コンポーネントの使い方が学べます。
【はむ式】React Hooks 入門 - HooksとReduxを組み合わせて最新のフロントエンド状態管理手法を習得
※注意点
クラスコンポーネントの解説は基本的にありません。クラスコンポーネント使わなくてもコーディングできますが、一応なんとなく読める程度にはなっておいた方が良いとは思いますので別で勉強必要かと思います。
個人的に普段書くことはありませんが、クラスコンポーネントは多少読める程度には勉強しました。GraphQL
RailsチュートリアルでREST APIがどんなものかはなんとなく理解したかと思います。
実際は、APIとか意識してなかったと思いますが、routes.rb
でURLとアクション紐付けてますよね。あれがREST APIです。
そうしたら次は別のAPIプロトコル学んでみましょう。簡単に説明しますと例えば、REST APIでは
/users
のようなurlにアクセスすると全てのuserの全ての情報が取れます。でも場合によっては全userの名前だけ欲しい時がありますよね?
そうすると、全userのidとかアバター画像のurlとか無駄なデータもフロント側に渡されます。GraphQLでは欲しいものだけリクエストができるので、全userの名前だけ取得するといったことが可能です。
さらにスキーマ自体がAPIの仕様書になるので開発スピードが上がるということです(正直この辺は時と場合にもよるので絶対正しい訳ではないと思います)
ものすごくざっくり言うとREST APIでは、あるurlに対して何を渡すと何が返ってくるかという仕様書を作る必要があったのですが、GraphQLでは何を渡すと何が返ってくるかという仕様書を作るそれ自体がAPIの実装になるのでわざわざ別で作る必要がないといった感じです。
【はむ式】フロントエンドエンジニアのためのGraphQL with React 入門
※注意点
この講座ではGraphQLとはどういうものか学べますが、実際にAPIをどのように実装するかはカバーしていません。Railsで実装する場合にはgraphql-rubyというgemを使って実装していきます。まとめ
これでDockerで環境構築して、RailsでGraphQLのAPI作成して、フロントをReactで作れるようになりました。
ReactでGraphQL扱うのにGraphQLクライアント必要なのですが、Apollo Clientが一番有名だと思うので、色々参考にしながら実装してみて下さい。ちなみにReactの動画でReduxも多少学ぶことができるのですが、普段Redux使っていません。
なぜかというと前にReduxを使おうとしたらApollo Clientと競合したためです。Apollo Client自体にstateを管理する機能があったり、useContextというHooksを使えば余程複雑ではない限りRedux使わなくても十分対応できると思います。
ということでRailsチュートリアル終わったらこの辺クリアしていくと良い感じに順番に技術を学べるんじゃないかというちょっとした経験談でした。
- 投稿日:2020-12-01T02:43:47+09:00
Docker-ComposeでDockerのベース環境を簡単構築![Sample:MySQL]
目的
docker-composeによって、簡単にDocker環境構築をしてみよう、と言う記事です。
自分が環境を先日作った際に、あまりyml文内の意味やオプション、コマンドって結局どういう意味やねん、、、とプチパニックになったのでまとめ直したものになります。そのため知識、かつ環境の構築がこの記事のみで完結できるような構成を目指しました。
あまりMarkDown記法になれていないこともあるため、少し拙い文章になるかと思いますが、読んでいただけると幸いです。
サクッと作るぞ!チートシート
この記事をみてサクッと環境作りたいわ、って人のために
まず実行するためだけの手順を完全に説明を省いて紹介していきます。
今回はサンプルのためにMySQL環境を作成しますが、
柔軟に変更できるようにコメントにて、可変できる部分は特記しています。構成
構成は以下の通りに作成しました。
├── docker │ └── db │ ├── data │ ├── mysql-config.cnf │ └── init │ ├── 001-create-tables.sql │ └── init-database.sh └── docker-compose.ymlソースコード
1. docker-compose.yml
アプリケーションを構成するサービスを
docker-compose.yml
に書きます。今回はMySQLにあたりますね。
これを使用するコードディレクトリの一番親のディレクトリに生成します。
# docker-compose.yml version: '3' services: # (例1)ここに使用するサービスを書きます mysql: # image > コンテナ実行時の元になるイメージです。使うサービスのリポジトリ名だったりを書きます。 image: mysql:latest # volume > 仮想環境上でのファイルをパスを指定してアクセスできるようにします。パスはこのymlファイルがあるディレクトリ基準です volumes: - ./mysql/data:/var/lib/mysql - ./mysql/mysql-config.cnf:/etc/mysql/conf.d/my.cnf - ./mysql/init:/docker-entrypoint-initdb.d # environment > 環境変数を指定します。Compose実行時に指定されるものにあたります。 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: database MYSQL_USER: user MYSQL_PASSWORD: password TZ: 'Asia/Tokyo' # (例2)goを使う場合であれば以下のようになります。細かい詳細は解説にて説明、参照します。 go: build: context: ./go/ target: dev volumes: - ./go/src:/go/src:cached working_dir: /go/src ports: - 8080:8080 tty: true env_file: - ./go/.envMySQL以外の環境を構築する場合は3. へ飛んでください。
2. MySQL環境用のソースコード
MySQLの場合に初期DBを用意する必要があるので、設定ファイル(
mysql-config.cnf
)と初期テーブル(001-create-tables.sql
)のコードを書き、それを実行するコード(init-database.sh
)を書きます。### mysql-config.cnf ### [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [client] default-character-set=utf8mb4--- 001-create-tables.sql --- ---- drop ---- DROP TABLE IF EXISTS `first_table`; ---- create ---- create table IF not exists `first_table` ( `id` INT(20) AUTO_INCREMENT, `name` VARCHAR(20) NOT NULL, `created_at` Datetime DEFAULT NULL, `updated_at` Datetime DEFAULT NULL, PRIMARY KEY (`id`) ) DEFAULT CHARSET=utf8 COLLATE=utf8_bin;### init-database.sh ### #!/usr/bin/env bash #wait for the MySQL Server to come up #sleep 90s #run the setup script to create the DB and the schema in the DB mysql -u docker -pdocker test_database < "/docker-entrypoint-initdb.d/001-create-tables.sql"3. Docker起動
imageを指定していない場合(例2)、ビルドを行い、イメージを構築します。この時に参照されるのがよく巷で聞く
Dockerfile
です。今回はMySQL環境を爆速で仕上げるため省略しますが、解説にて説明します。今回は使用するイメージ(image)を
# docker-compose.yml version: '3' services: # (例1)ここに使用するサービスを書きます mysql: # image > コンテナ実行時の元になるイメージです。使うサービスのリポジトリ名だったりを書きます。 image: mysql:latestここで指定しているので、コンテナを作成し起動するところからで大丈夫です。
$ docker-compose up Creating network "backend_default" with the default driver Creating backend_mysql_1 ... done Creating backend_go_1 ... done ...上記コマンドで起動できたら、同じ階層にいる別ターミナルで以下のコマンドによって起動を確認してみてください。
$ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------ backend_go_1 bash Up 0.0.0.0:8080->8080/tcp backend_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcpここで起動し、サービス名が表示されていれば構築完了です!早いですね。
最後に以下コマンドによってコンテナとそれに関わるネットワークを停止します。
$ docker-compose down Stopping backend_mysql_1 ... done Stopping backend_go_1 ... done Removing backend_mysql_1 ... done Removing backend_go_1 ... done Removing network backend_default下記コマンドを叩いたターミナルが
$ docker-compose up Creating network "backend_default" with the default driver Creating backend_mysql_1 ... done Creating backend_go_1 ... done ... mysql_1 | 2020-11-30T14:42:11.612252Z 0 [System] [MY-013172] [Server] Received SHUTDOWN from user <via user signal>. Shutting down mysqld (Version: 8.0.22). go_1 | root@f1d2304a041d:/go/src# exit backend_go_1 exited with code 0 mysql_1 | 2020-11-30T14:42:12.324000Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.22) MySQL Community Server - GPL. backend_mysql_1 exited with code 0上記のようにexitできていれば、無事に停止しています。
これでDocker-composeによって仮想環境が構築できました。
4. MySQLでsample-tableを生成
仮想環境に入り、init-database.shを叩きます。
コンテナ内で起動中にコマンドを叩く場合は以下を参考にしてください。
# コンテナ起動 $ docker-compose up ... > 別ターミナルでdocker環境に入り、コマンド実行 # 権限付与 > ここでyml中に書いたvolumesで、パスを指定できる、と言うわけです $ docker-compose exec mysql bash -c "chmod 0775 docker-entrypoint-initdb.d/init-database.sh" # init-database.shを叩く $ docker-compose exec mysql bash -c "./docker-entrypoint-initdb.d/init-database.sh"解説
最初にアジェンダ書いてた時は、ここにがっつり解説書いていこうと思ったのですが、思ったより上で説明してしまった感が否めないです。書くことなくなってきた。。。まずいぞ。。。
そもそもdocker-composeって??
複数のコンテナを、簡易に管理し、実行するDockerアプリケーションのためのツールの1つです。
コマンドを1つ実行するだけで、docker-compose.ymlに定義した設定に基づいて環境を構築してくれるので、本当にいろんな使い方ができます!!、、(らしいですと言うのが本心。お恥ずかしい。)ymlで(よく)使用するオプション
image:
コンテナを実行する時に元となるイメージを指定します。
自分でDockerfile
を書いて実行する場合は、次のbuildオプションを使用します。
imageで使用する場合はビルドを行わず、$ docker-compose up
からで大丈夫です。build:
コンテナを実行する時に参照する
Dockerfile
を指定します。外部imageではなく、自分でDockerfile
を制作して使う場合はよく以下の構成で使用します。自分でいちから
Dockerfile
を書く、よりはリポジトリをクローンしてきて修正する、という扱い方が多いと思います。├── docker ├── Dockerfile └── docker-compose.yml# docker-compose.yml version: '3' services: hoge: # 同階層のため相対パスで指定 build: .ここで自分で
Dockerfile
を書いた場合はイメージを構築、すなわちビルドする必要があるので、初期起動前に以下コマンドを叩きます。$ docker-compose build db uses an image, skipping Building go ......
使用するcontextの指定や、dockerfile生成に関してはリファレンスも参照することをお勧めします。
https://docs.docker.jp/compose/compose-file.html#contextenvironment:
主に仮想環境上で用いる環境変数を指定します。
配列、もしくはDictionaryで定義できるので、$ docker-compose exec
で実行する際にアクセスできるようにAPI鍵や今回のようにMySQLのホストの値を記載します。env_file:
上記のenvironmentでは隠したい情報(自分はAPI鍵などはenv_fileを使用して
.gitignore
で隠す、という形をとることが多いです)の場合は、これで環境変数を記載したファイルを指定します。# docker-compose.yml version: '3' services: go: env_file: - ./go/.envなお
.env
ファイルは以下のように、変数 = 値
の形を取ります。# /go/.env DB_USER=user DB_PASSWORD=password DB_HOST=mysql:3306 DB_DATABASE=database DB_SOCKET=tcpvolumes:
自分のローカルファイルを環境上のファイルに割り当てるように指定することができます。
なお、実行手順にもコメントで書いた通り、ymlファイルがあるディレクトリを基準として相対パスは使用できます。
# docker-compose.yml version: '3' services: mysql: volumes: # ローカルファイル:仮想環境上のパス となります - ./mysql/data:/var/lib/mysql - ./mysql/mysql-config.cnf:/etc/mysql/conf.d/my.cnf - ./mysql/init:/docker-entrypoint-initdb.dports:
ホスト側とコンテナ側、両者のポートを指定することができます。
(なお、コンテナのみの指定も可能)# docker-compose.yml version: '3' services: go: ports: # ローカル側:仮想環境側 という感じです - 8080:8080 env_file: - ./go/.envdocker-composeで(よく)使うコマンド
説明するコマンドを以下に書いておきます。このほうがササッと使いやすいですよね。
$ docker-compose build $ docker-compose up $ docker-compose down $ docker-compose ps $ docker-compose start $ docker-compose stop$ docker-compose build
image:
を指定せずにbuild:
を使用する際に使います。これによりサービスをビルドします。
上記にも書いた通り、image:
を指定しない場合は、以下の$ docker-compose up
の前にビルドする必要があります。頻度の高いオプション --no-cache 構築時にイメージのキャッシュを使わない$ docker-compose up
仮想環境の立ち上げを行います。
実際はコンテナを構築、作成し、起動、アタッチまでを全てこのコマンドで行ってくれます。なお、
-d
をつけることによって、バックグラウンドで実行されるため、コンテナは起動し続ける状態を確保できます。CIなどを回す際にはこのオプションを使うと便利です。頻度の高いオプション -d バックグラウンドでコンテナを実行$ docker-compose down
起動している仮想環境を停止し、
$ docker-compose up
によって立ち上げたコンテナとネットワークを削除します。頻度の高いオプション --rmi all 全イメージを削除$ docker-compose ps
起動しているコンテナの一覧を表示できます。
$ docker-compose exec
起動している環境に対して、任意のコマンドを実行することができます。
なので、基本的に環境上でプロンプトを動かしたりする際はこれを使用します。# exec後の部分でどのコンテナを叩くか指定します $ docker-compose exec mysql bash -c "./docker-entrypoint-initdb.d/init-database.sh" # 以下コマンドのように扱えば、直接シェルを叩くこともできますが、volumesにてデフォルトの階層を指定する必要があります $ docker-compose exec hogehoge /bin/bash頻度の高いオプション -u 指定されたユーザによりコマンドを実行$ docker-compose start
既存のコンテナをサービスとして起動します。
$ docker-compose up
すれば起動するので、$ docker-compose down
ではなく(停止後コンテナを削除するため)、$ docker-compose stop
などで停止した場合に再び起動する時に使用します。$ docker-compose stop
稼働中のコンテナを停止しますが、
$ docker-compose down
とは違い、削除しません。 上記の$ docker-compose start
コマンドで、再起動できます。まとめ
docker-composeによる仮想環境構築でした。
運用だと、docker-compose.yml
に複数コンテナを組み合わせて一挙に立てて使用することになることがおおいですね。初日ということでしたが、自分が実装していた時にメモしていたことなどを噛み砕いて書いていくうちにどんどんボリュームが増しちゃいました。(笑)
ただ、語彙的にもとっつかみやすい文章になってるんじゃないでしょうか、、と思っています、、、(そうでなければ僕の語彙力の問題ですね、反省します)
皆様のますますのご活躍の少しでもお手伝いになれば、ということを書き締めたいと思います。
また質問等あればコメントにてご指摘などよろしくお願いします。
参考資料
- docker-compose オプション / リファレンス
https://docs.docker.jp/compose/compose-file.html
- docker-compose コマンド / リファレンス
- 投稿日:2020-12-01T00:00:48+09:00
Docker イメージの断捨離を圧倒的に効率化する
こういうのを作りました。
Docker イメージの断捨離がダルい問題
Docker イメージがディスク容量を圧迫して困ることがありますよね。
docker image prune
やdocker system prune
をすれば dangling なイメージをまとめて削除することができます。danglings (宙ぶらりんな) イメージとは「タグ付けされていない」「どのコンテナも使ってない」イメージのことなので、つまりは「削除しても問題なさそうなイメージをまとめて削除する」ってやつです。これだけである程度は削除できますが、タグ付けされている不要なイメージまでは削除できません。
-a
/--all
オプションを使えばタグ付けされている未使用なイメージをまとめて削除できますが、これだと今度は必要なイメージまで削除してしまうので難しいです。結局、ちゃんと断捨離しようと思ったら目視でひとつひとつ削除していくしかないのです。
しかしそれが大変で、latest ではないイメージは
<Repository>:<Tag>
と指定するかイメージ ID を直接指定するしかなく、手でポチポチとコピペしていくハメになります。しかも削除順序を間違えると「このイメージはこっちのイメージが依存しているから削除できんよ」などとエラーを吐かれます。やってられるか。こうした単純作業は効率化するのが IT エンジニアの美徳です。
「リストの中から人間が選択をする」操作を効率化する手段といえば何ですか?
そう、peco ですね。peco とは
@xtetsuji さんの「pecoの基礎の基礎」がわかりやすいです。
とてもシンプルなツールで 「標準入力から受けた行データをインクリメンタルサーチして、選択した行を標準出力に返す」 コマンドです。
シンプルゆえに様々な組み合わせで効果を発揮します。あらゆる場面での選択肢を標準入力に渡して、選択された結果を標準出力から受け取って加工してコマンド実行をする、というのが基本的な流れ。
自分も「コマンド履歴を検索して再実行」とか「SSH の接続先を選んで接続」とか「Git リポジトリのブランチを選んで切り替え」とか1 頻繁に使います。 控えめに言ってめちゃくちゃ便利。
macOS だったら
brew install peco
で入ります。実はあまり知られていないっぽいですが、 peco は複数行選択ができます。
今回はこれを使って Docker イメージを断舎離するやつを作っていきましょう。peco で Docker イメージを選択
peco で複数行選択をするためには
control + space
を使いますが、macOS ではこの組み合わせは「入力ソースの切り替え」に割り当てられているので、そのままでは使えません。回避方法は何でもいいですが、今回は「tab
で複数行選択」が出来るように peco の設定ファイルを書いておきます。~/.config/peco/config.json{ "Keymap": { "Tab": "peco.ToggleSelectionAndSelectNext" } }さて、おもむろに以下のコマンドを実行してみましょう。
$ docker images | pecodocker images の結果を peco に渡して、peco でフィルタリングして、標準出力に流しただけです。
というわけで、Docker イメージをフィルタリングして選択するやつができました。
便利スクリプトを書く
あとはもういい感じに仕上げるだけです。
~/.zshrcfunction peco-docker-images() { local images="$(docker images | tail +2 | sort | peco --prompt 'DOCKER IMAGES>' | awk '{print $3}' ORS=' ')" [ -z "$images" ] && return BUFFER="$LBUFFER$images$RBUFFER" CURSOR=$#BUFFER } zle -N peco-docker-images bindkey '^x^i' peco-docker-imagesイメージを削除するために必要なのはイメージ ID なので、peco の結果に対して awk でイメージ ID だけを取り出して space で結合しています。
これは zsh スクリプトですが、bash でも似たような雰囲気で掛けるのではないでしょうか。(知らん)
この例では
control + x
control + i
で peco が起動して、選択したイメージ ID がプロンプトのバッファに差し込まれます。これで地道なコピペをすることなく Docker イメージをごっそり断捨離できるようになりました。めでたしめでたし。