- 投稿日:2020-04-07T23:36:08+09:00
さわっておぼえるDocker入門?#2(コンテナの様子をみる)
はじめに
Dockerコンテナを実際に動かして確認しながら、動いているコンテナの様子をざっくり眺めていきます。
前回はコンテナを作ったり壊したりしました -> さわっておぼえるDocker入門?#1
目的
Dockerを触りながら、コンテナの様子を確認したり、コンテナ内のシェルを使用できるようになる
今回は、下記の操作を行います。
- コンテナのメタデータを確認
- コンテナのステータスを確認
- コンテナのシェルにアクセス
- ubuntuコンテナにパッケージをインストール
- コンテナのプロセスを確認
環境
- macOS Mojave 10.14.6
- Docker engine 19.03.5
まずはDockerのインストール
コンテナをアレコレしてみる
まずはコンテナを起動させてみる
以下のコマンドは、nginxのコンテナを起動します。
-d
オプションによって、バックグラウンドでコンテナが起動します。--name
オプションによって、nginx
と名付けました。docker container run -d --name nginx nginxさらに、MySQLのコンテナも起動します。
MySQLサーバーには、rootパスワードが必要なので
-e
オプションを使用してランダムなパスワードを設定しています。
-e
はenvironmentオプションです。docker container run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=true mysqlこれで、nginxとmysqlの2つのコンテナが起動しました。
container inspect
指定したコンテナのメタデータを取得します。
以下のコマンドを実行すると、mysqlコンテナのメタデータがJSON形式で表示されます。つまりコンフィグです。
このメタデータを確認することで、どんな設定でこのコンテナが作られているのかを知ることができます。
docker container inspect mysql
container stats
メタデータではなく、現在の様子を知りたい場合は以下のように
stats
コマンドを使用します。コマンドを実行することで、CPU使用率やRAMの占有率など、リアルタイムのデータが表示されます。
docker container stats mysqlコンテナ内のシェルにアクセスする
立ち上げたコンテナの中で作業するには、シェルにアクセスする必要があるかもしれません。
その場合は
-it
オプションを使用します。SSHは必要ありません。
bash
を指定しbashシェルが使えるようにします。docker container run -it --name proxy nginx bash # root@96d294dee3b4:/#これで、コンテナ内でファイルをいじったり、パッケージをインストールしたりすることができます。
コンテナの「コマンド」が変化していることを確認する
コンテナの一覧を確認してみると
-it
オプション付きで立ち上げたコンテナは、
COMMAND
の値が変化しています。docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 96d294dee3b4 nginx "bash" 4 minutes ago Exited (0) 12 seconds ago proxy d43f74bd3fea mysql "docker-entrypoint.s…" 20 minutes ago Up 20 minutes 3306/tcp, 33060/tcp mysql 40a021da02c9 nginx "nginx -g 'daemon of…" 22 minutes ago Up 22 minutes 80/tcp nginxシェルを使用するために、デフォルトで使用するプログラムを「bash」に指定したので、
COMMAND
が書き換わっています。したがって、シェルから
exit
した場合には、コンテナは停止状態になります。
コマンドで指定したプログラムが停止した時には、コンテナは停止するからです。Ubuntuを起動してみる
以下のコマンドでubuntuイメージからコンテナを起動し、シェルにアクセスします。
docker container run -it --name ubuntu ubuntuubuntuは
apt
というパッケージマネージャを使用してパッケージのダウンロードを行うことができますまずはパッケージリストを更新します。
apt-get updateさらに、
curl
という Httpリクエストを行うパッケージをインストールしてみます。apt-get install -y curl試しにGoogle.comにHttpリクエストを送信してみると、
レスポンスがちゃんと返ってきます。curl google.com # <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> # <TITLE>301 Moved</TITLE></HEAD><BODY> # <H1>301 Moved</H1> # The document has moved # <A HREF="http://www.google.com/">here</A>. # </BODY></HTML>ubuntuコンテナを停止する
先ほどと同じく
exit
することで自動的にコンテナも停止します。docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9a881f1a18c6 ubuntu "/bin/bash" 12 minutes ago Exited (0) 2 seconds ago ubuntu 96d294dee3b4 nginx "bash" 23 minutes ago Exited (0) 19 minutes ago proxy d43f74bd3fea mysql "docker-entrypoint.s…" 39 minutes ago Up 39 minutes 3306/tcp, 33060/tcp mysql 40a021da02c9 nginx "nginx -g 'daemon of…" 41 minutes ago Up 41 minutes 80/tcp nginxubuntuの
COMMAND
をみると、デフォルトで起動するプログラムがbash
であることを確認できます。
そのため、特に何も設定しなくてもbashが起動しました。
さらにbashからexitすることで、コンテナも停止しました。コンテナを再起動する
もう一度このコンテナに入りたい場合は、
container start
を使用します。
container run
だと、新しいコンテナが起動してしまうので、先ほどダウンロードしたcurlはありません。
run
の時は-it
だったところが-ai
になっていることに注意です。docker container start -ai ubuntu
バックグラウンド起動中のコンテナに入る
では、バックグランドで起動しているコンテナ内のシェルにアクセスしたい場合はどうすれば良いでしょうか。
container exec
コマンドを使用します。
今回は先ほどバックグラウンドで起動したmysqlコンテナを使用します。
bashシェルを指定することを忘れないでください。docker container exec -it mysql bashコンテナのプロセスを確認する
この時、mysqlコンテナ内ではどのようなプロセスが動いているのでしょうか。
ps aux
コマンドを使用することで、起動中のプロセス一覧を確認できます。まずはmysqlコンテナが
ps
コマンドを使用できるよう、必要なパッケージを更新&インストールします。最初にパッケージの更新。
apt-get update続いて、パッケージをインストールします。
apy-get install -y procpsこれで
ps
コマンドが利用可能です。早速、mysqlコンテナのプロセスを確認してみます。
ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND mysql 1 0.9 20.0 2135592 408776 ? Ssl 13:26 0:32 mysqld root 197 0.0 0.1 3868 3244 pts/0 Ss 14:15 0:00 bash root 602 0.0 0.1 7640 2724 pts/0 R+ 14:20 0:00 ps aux
- まず一番上、mysqlのデーモンがバックグランドで起動中です。
- 次に真ん中、これは今使っているbashのプロセスです。
- 最後に、たった今打ち込んだ
ps aux
のプロセスが表示されています。今回は、
exit
してもコンテナは停止しません。
なぜなら、mysqlのデーモンがバックグラウンドで動いているからです。まとめ
- コンテナのメタデータを確認
- コンテナのステータスを確認
- コンテナのシェルにアクセス
- ubuntuコンテナにパッケージをインストール
- コンテナのプロセスを確認
上記を実際に動かして確認しながら、動いているコンテナの様子をざっくり眺めました。?
- 投稿日:2020-04-07T23:21:22+09:00
laradocker構築
初めてdockerlaravelを構築したのでその手順をまとめた
1.Laradock取得
$mkdir ~/my_docker $cd ~/my_docker $git clone https://github.com/laradock/laradock.git2.Laradockの設置ファイル修正
$cd Laradock $cp env-example .env $cp mysql/docker-entrypoint-initdb.d/createdb.sql.example mysql/docker-entrypoint-initdb.d/createdb.sql.env//修正することで別プロジェクトで競合を防ぐ -DATA_PATH_HOST=~/.laradock/data +DATA_PATH_HOST=~/.laradock/my_docker_project/data //こちらも競合を防ぐ -COMPOSE_PROJECT_NAME=laradock +COMPOSE_PROJECT_NAME=my_docker-laradock -MYSQL_VERSION=latest +MYSQL_VERSION=5.73.Laradockビルド・アクセス
$cd ~/my_docker/laradock $docker-compose build workspace ngnix mysql redis $docker-compose up -d ngnix mysql redis立ち上がればworksqaceサーバへアクセス
$docker-compose exec workspace bash4.Laravel・yarnインストール
workspaceサーバ内で書きコマンドを実行
$ composer create-project laravel/laravel --prefer-dist laravel //laravelインストール。プロジェクト名laravel $cd laravel $yarn install //yarnインストール5.Laravel設定ファイル修正
laradocker内の.envファイルと合わせる
こちらはlaravelの.envファイルDB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=default DB_USERNAME=default DB_PASSWORD=secret6.ブラウザアクセス
hosts修正
$sudo vi /private/etc/hostsprivate/etc/hosts+127.0.01 my_docker.comアクセス
ブラウザで http://my_docker.com へアクセスしlaravelが表示されれば完了
- 投稿日:2020-04-07T21:55:20+09:00
初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)NameSpace 編
背景
個人的にインフラの知識以上にこれからのアプリケーションが動く環境を作ってデプロイしたりしてこれからの知識を身に着けたい。そしてより一層、自分の知識のアップデートをしたいと思いました。
その中でこの本に出会い、これから少しずつやったことを残し、未来の自分への手紙としてもあり、見つめ直せればと思いました。
引用や参考と今回の自分の勉強用の書籍の紹介
技術評論社『Kubernetes実践入門』のサンプルコード
Kubernetes実践入門 プロダクションレディなコンテナ&アプリケーションの作り方
実際の学びについて
書籍を読みながら、章ごとに少しずつ進めていきたいと思います。
GitHub のソースコードも使いながら学んで行きたいと思います。勉強開始
ここから namespace を学んでいきます。
my-namespace.yamlapiVersion: v1 kind: Namespace metadata: name: my-namespace #オブジェクト名 (1)コマンド郡
namespace を検索
$ kubectl get ns NAME STATUS AGE default Active 25d docker Active 25d kube-node-lease Active 25d kube-public Active 25d kube-system Active 25dNameSpace のマニュフェストファイルを適用する
$ kubectl apply -f my-namespace.yaml namespace/my-namespace creatednamespace を検索
$ kubectl get ns NAME STATUS AGE default Active 25d docker Active 25d kube-node-lease Active 25d kube-public Active 25d kube-system Active 25d my-namespace Active 6s指定した Namespace が作成されることを確認できた
- 投稿日:2020-04-07T16:07:37+09:00
Docker outside of Docker (DooD) で GitLab-CI する
このポストは、
やん事ない理由で Git*.com を使えない日々をオンプレ版 GitLab で乗り切る の後続記事です。ゴール
DOODでCI環境を構築
公式 -- Use Docker socket binding下図の (1)~(6)
(Start) http://192.168.100.201/kubolab/tms の master のコミットを契機にCI実行し
(End) http://192.168.100.201:10080 で初期画面を見れること。
※とりあえず慣れてる CakePHP3 を使いました。構成図
ソースコード: https://gitlab.com/kubolab/tms
項目 GitLab グループ名 kubolab GitLab プロジェクト名 tms グループ名、プロジェクト名をブログ投稿用に変更しようかと思ったのですが、手間だったのでそのままです、すんません。
tree
?:
gitignore 対象
. ├── ?.env ├── .gitlab-ci.yml ├── docker-compose.yml ├── docker │ ├── build │ │ └── cake_php │ │ └── Dockerfile │ ├── config │ │ └── nginx │ │ └── default.conf │ ├── ?data │ │ └── htdocs #公開Dir ※srcへのシンボリックリンクを張る │ │ └── postgres │ │ └── data │ │ │ ├── ?postgres-dump │ │ └── dumpall.sql #初期データ投入スクリプト │ └── sh │ └── postgres │ └── docker-entrypoint-initdb.d │ └── init.sh ├── src │ ├── config │ │ └── ?app.php │ └── webroot ├── run-app.cmd #おまけ: ローカルPCに開発環境をダブルクリックで構築 └── shared_サーバ配置用
項目 オンプレ GitLab の IP 192.168.100.201 Host OS Windows 10 Pro 1909 VirtualBox 上の Guest OS 前記事参照 Guest OS上で動く GitLab ver 前記事参照 GitLab Runner 12.9.0 Docker for Windows 2.2.0.4
Docker コンテナ名(任意) image 備考 cake_nginx nginx:1.13.5-alpine ports: 10080:80 1 cake_php php:7-fpm cake_postgres postgres:10.3-alpine 準備 (GitLab が動くサーバ上で)
- CI実行時に使用するファイル (バージョン管理しないファイル) を手動で配置しておく。 ※SCP か何かで
/shared_サーバ配置用
を GitLab サーバに配置/shared └── kubolab └── tms ├── app-settings │ └── app.php #DB接続情報等 └── postgres-dump └── dumpall.sql #初期データ
GitLab-Runner (gitlab-runner-dd) コンテナ起動
ⅰ. Docker をインストール
https://qiita.com/ymasaoka/items/b6c3ffea060bcd237478 -- Install Docker on CentOS 7ⅱ. GitLab-Runner コンテナ起動
$ docker run -d --name gitlab-runner-dd --restart always \ -v /srv/gitlab-runner-dd/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest # GitLab-Runner コンテナに入って実行 $ docker exec -it gitlab-runner-dd bash # docker インストール $ curl -sSL https://get.docker.com/ | sh $ usermod -aG docker gitlab-runnerジョブを実行する Runner を登録
ⅰ. 登録
★メモしたToken
参考: https://qiita.com/hykisk/items/ebff2f7cd2e8100a6bbe -- ジョブを実行する Runner を登録$ gitlab-runner register -n \ --url http://172.17.0.1/ \ --registration-token ★メモしたToken \ --executor docker \ --description "tms docker runner" \ --docker-image "docker:stable" \ --docker-volumes /var/run/docker.sock:/var/run/docker.sock \ --docker-volumes /shared-builds/kubolab/tms/docker:/builds/kubolab/tms/docker \ --docker-volumes /shared/kubolab/tms:/gitlab-runner-dd/app \ --tag-list app-build \ --clone-url = "http://192.168.100.201"memo: GitLabにホスト名を割り当ててる場合は、クローンに失敗するため、以下を追加して名前解決を指定する。
--docker-extra-hosts "gitlab.private.hykisk.com:172.17.0.1"
ⅱ. 登録結果を確認したいとき
/etc/gitlab-runner/config.toml[[runners]] name = "tms docker runner" url = "http://172.17.0.1/" token = ★メモしたToken executor = "docker" clone_url = "=" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] [runners.docker] tls_verify = false image = "docker:stable" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/shared-builds/kubolab/tms/docker:/builds/kubolab/tms/docker", "/shared/kubolab/tms:/gitlab-runner-dd/app", "/cache"] shm_size = 0gitlab-ci.yml
stages: - package variables: GIT_STRATEGY: fetch package: stage: package variables: # HOST OS SHARED_BUILDS_DOCKER_DIR: /shared-builds/$CI_PROJECT_PATH/docker SHARED_DIR: /shared/$CI_PROJECT_PATH image: docker:stable script: # アプリケーションコードを配置 - cp -pr $CI_PROJECT_DIR/src/. $CI_PROJECT_DIR/docker/data/htdocs # アプリケーション設定ファイルを配置 - cp -pf /gitlab-runner-dd/app/app-settings/app.php $CI_PROJECT_DIR/docker/data/htdocs/config # このコンテナ (gitlab-runner) に docker-compose をインストール - apk update && apk add docker-compose # 2回目以降、CI実行されたときマウント有効にするため down する - docker-compose down # volumeを有効にするため # Dockerfile に手を加えたら build しなおす #- docker-compose -f docker/docker-compose.yml build --no-cache - docker-compose up -d # マウント後に実行する必要があるため Dockerfile 内ではなくコンテナに入って行う - docker exec cake_php sh -c "cd /var/www/app && composer install" tags: - app-builddocker-compose.yml
version: '3' services: web: container_name: cake_nginx image: nginx:1.13.5-alpine ports: - 10080:80 volumes: # ローカルで使う環境変数はでは .env に、CI環境で使う環境変数は gitlab-ci の設定(GUI) に書く - $SHARED_BUILDS_DOCKER_DIR/config/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - $SHARED_BUILDS_DOCKER_DIR/data/htdocs:/var/www/app networks: vpcbr: ipv4_address: 172.26.0.2 php: container_name: cake_php build: docker/build/cake_php volumes: - $SHARED_BUILDS_DOCKER_DIR/data/htdocs:/var/www/app networks: vpcbr: ipv4_address: 172.26.0.3 db: container_name: cake_postgres image: postgres:10.3-alpine ports: - 5433:5432 volumes: - $SHARED_BUILDS_DOCKER_DIR/data/postgres:/var/lib/postgresql # DB作成と初期データ投入スクリプトを配置 - $SHARED_BUILDS_DOCKER_DIR/sh/postgres/docker-entrypoint-initdb.d/init.sh:/docker-entrypoint-initdb.d/init.sh - $SHARED_DIR/postgres-dump/dumpall.sql:/tmp/dumpall.sql:ro environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DUMP_FILE_DIR: /tmp networks: vpcbr: ipv4_address: 172.26.0.4 networks: vpcbr: ipam: driver: default config: - subnet: 172.26.0.0/24memo: 公開してはいけない情報は環境変数を使用
environment: POSTGRES_USER: $TMS_POSTGRES_USER POSTGRES_PASSWORD: $TMS_POSTGRES_PASShttp://192.168.100.201/kubolab/tms/-/settings/ci_cd
他ファイル (Dockerfile等)
ソースコード: https://gitlab.com/kubolab/tms 参照
確認
PUSHしてCIジョブが
created -> running -> passed
になればOK
おまけ
ローカルPC (Docker for Windows インストール済) の開発環境をダブルクリックで構築
/run-app.cmd@echo off rem cd /d %~dp0 rem アプリケーションコードを配置 rmdir docker\data\htdocs mklink /d .\docker\data\htdocs ..\..\src rem アプリケーション設定ファイルを配置 copy /Y .project-items\app.php src\config rem DB作成と初期データ投入スクリプトを配置 copy /Y .project-items\dumpall.sql docker\postgres-dump rem docker-compose 環境設定ファイルを配置 copy /Y .project-items\.env .env rem Dockerfile に手を加えたら build しなおす docker-compose down --rmi all docker-compose build --no-cache rem 各コンテナ起動 docker-compose up -d rem マウント後に実行する必要があるため Dockerfile 内ではなくコンテナに入って行う docker exec cake_php sh -c "cd /var/www/app && composer install" pause後記
CI環境構築時に困ったこと
- 問題: コンテナ内で実行するシェルが
Permission denied
で実行できない。解法: 実行権限を付与する
$ git update-index --add --chmod=+x {filepath}
問題: 試行錯誤中に (CI環境・ローカルPC問わず) Docker の挙動がおかしくなった場合
解法: 全消しする
CI環境の場合
docker exec -it gitlab-runner-dd bash rm -f /etc/gitlab-runner/config.toml exit docker ps -aq | xargs docker rm -f docker images -aq | xargs docker rmi docker volume rm $(docker volume ls -qf dangling=true) docker network rm docker_vpcbrローカルPCの場合 (powershell)
docker rm -f $(docker ps -aq) docker rmi -f $(docker images -aq) docker volume rm $(docker volume ls -qf dangling=true) docker network rm docker_vpcbr # Docker for windows 再起動
ports: 10080:80
Dockerホストにあたる GitLab が80番を使っているため10080番を使う。
アクセスの流れ:
1.ブラウザでGitLabサーバに10080
でアクセス
2.VirtualBox のポートフォワーディングルールで10080 -> 10080
にマッピング
3.nginxの設定で10080 -> 80
にマッピング
↩
- 投稿日:2020-04-07T15:56:22+09:00
Windows10 Home Edition でDockerが使用可能に
はじめに
2020/3月末 Windows 10 HomeでWSL2が使用できることになり、Docker desktopに対応することが出来るようになりました。また、WSL2によるDockerの使用は、Hyper-Vを用いた方法よりも15倍速い速度となるため、より軽い処理で実現できます。ただし、OSを開発者verで入れないといけないこと、Dockerも現状edgeという正式版での対応はまだであることから、多少のリスクを伴うため、ユーザーが安心して導入するのはまだしばらく時間がかかりそうです。(2020/4/7時点)
今回は開発版でDockerを入れてみた場合の操作を記載します。この記事を読んでもらいたい方
Windows ユーザーでDockerをこれから使いたいエンジニア
もう既にDockerをVMWare等で使用しているエンジニア
Windows 10 pro もしくはEnterprise しか対応してなかったので、Dockerを使えなかったと思っているエンジニア注意事項
上記でもアナウンスしましたが、これからWindowsに開発版のOSを入れていきます。
製品版に近いものの、万一OSに問題があった場合、再度OSを入れなおします。その際のフォーマット化に伴い内部ストレージのデータが消えることが予想されます。
必要なデータは外付けHDD or クラウド上に保存するようにして、十分ご理解の上実行してください。作業時間
約半日
概要
1.WindowsOSのversion確認
2.Windows Insider Program(slowRing)にする
3.WSL と Virtual Machine Platform の有効化
4.Linux ディストリビューションの取得および開発者
5.WSL2のデフォルト化とUbuntu<=>WSL2間の接続
6.VScode<=>Ubuntu間の接続
7.Docker Desktopのinstall
8.Dockerの接続を確認手順
1.WindowsOSのversion確認
WindowsOSが10 かつ ver18917以上であることをコマンドプロンプトから、verコマンドで確認しましょう。
versionが低い場合は更新→再起動してください。2.Windows Insider Program(slowRing)にする
Microsoftアカウントの登録→ログインから、Insider Programに登録してください。
slowRing とfastRingの違いについて
登録されると以下のガイダンスがでますので、手順に従ってインストールします。
↓[スタート] -[設定] - [更新とセキュリティ]に入って、Insider Programへ
↓[開始]を押してアカウントをリンクさせます
↓Insiderはスローを選択しましょう
3.WSL と Virtual Machine Platform の有効化
管理者権限でPowerShellを立ち上げ、下記コマンドを実行
#WSLの有効化 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart #Virtual Machine Platformの有効化 dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart一度再起動します。(先ほど実行したInsider Program slow.verも有効化します)
その後、WSL2カーネルの更新を行います。
こちら参考にダウンロード→update→再起動するだけ
※ARM64=x64です
※自分のPCがx64かどうかの確認は、「設定」→「システム」→「バージョン情報」で確認できます。
※今後Microsoftはこのような手動でのUpdateはやめ、定常的なWindowsのUpdateにこの操作を内部に組み込むとのことです。4.Linux ディストリビューションの取得および初期設定
Windows MicrosoftstoreからUbuntuをinstall
私はUbuntu-18.04をinstallしました。
Ubuntuの初期設定はこちらが参考になりました。
5.WSL2のデフォルト化とUbuntu<=>WSL2間の接続
#Ubuntu<=>WSL2間の接続 PS C:>wsl --set-version Ubuntu-18.04 2 #WSL2のデフォルト化 PS C:>wsl --set-default-version 2接続を確認しましょう。
#Ubuntuに接続 PS C:>wsl #Logoutの確認 PS C:>exitUbuntu初期設定後、WSL2環境のUbuntuを操作している状態でファイルを共有します(つまり、LocalのファイルをLinuxコマンドで動作させれるように同期します)
Ubuntuを開き、↓を実行します。
ちなみに実行後、フォルダが開くので、ショートカットを作成しておくと、Localからファイルを触れるようになりますよ。explorer.exe .6.VScode<=>Ubuntu間の接続
VScodeの画面左下「><」こんなのをクリック。「Remote-WSL: New Window」を選択してやると、デフォルトに設定しているUbuntuに接続されます。画像の右のようになればOKです。
7.Docker Desktopのinstall
やっとDockerに入ってきました。あと少しです。
Docker Desktop for Windowsをinstallしましょう。こちらのEdgeをinstallします。(stableは試しましたが、まだ対応できてませんでした。今後に期待!)
※Docker Desktop のtutorialの注意点とインテグレーション
最初の接続先はPowerShellになってるかと思います。
その場合はwslでUbuntuに切り替えて、Dockerのコマンドを流し込んでいきましょう。
↓tutorialが終わったところから始めます。
defaultの接続先(Resources)をUbuntuにしときましょう。(インテグレーション)
8.Dockerの接続を確認
tutorialが終われば、PCの立ち上げと同時に自動で立ち上がるようになります。
Ubuntuを起動し、↓下記コマンドを実行。
#nginxを立ち上げます docker run -p 80:80 nginx #終了を確認(接続状況を確認) docker ps #もし、終了していないdockerがあればこれで接続を切ります docker kill [CONTAINER ID]localhostに接続を確認します。→Ctrl + C で終了
↓これがでたらOKです。
お疲れ様でした。
参考にしたサイト
「WSL 2」は「Windows 10 Home」でも利用可能 ~MicrosoftがFAQを掲載(ニュース記事)
https://forest.watch.impress.co.jp/docs/news/1184353.html
「WSL2とHyper-Vの速度比較について」
https://www.docker.com/blog/new-docker-desktop-wsl2-backend/
「Installation Instructions for WSL 2」
https://docs.microsoft.com/en-us/windows/wsl/wsl2-install
「WSL2入れてみた」
https://qiita.com/TsuyoshiUshio@github/items/947301bd9317610572fc
「windows10のversion.update」
https://support.microsoft.com/ja-jp/help/4027667/windows-10-update
「WindowsカーネルのUpdate方法」
https://japan.zdnet.com/article/35150847/
「Microsoftのカーネル管理方法の意向」
https://news.mynavi.jp/article/20200316-997109/
「Windows Insider ProgramのFast slowの違い」
https://ascii.jp/elem/000/001/099/1099995/
「Windows Insider preview」
https://insider.windows.com/ja-jp/how-to-pc/
「Windows Subsystem for Linuxディストリビューションのインストール」
https://qiita.com/Aruneko/items/c79810b0b015bebf30bb#
「UbuntuとLocalディレクトリとの同期(/home/user/でexplorer.exe .)」
https://note.com/akazawa_studio/n/nda7b97038e5c
「Dockerのinstall(edge版をinstallすること。homeは現在2020/4で開発verしか対応できていない)」
https://hub.docker.com/editions/community/docker-ce-desktop-window
「Docker tutorial」
https://qiita.com/wMETAw/items/8d1b0c053a39841765bf
- 投稿日:2020-04-07T12:37:02+09:00
Elastic SearchをDocker Composeで展開したらError code 78でコンテナが停止される問題
はじめに
Elastic Searchは公式にDockerイメージを提供していてドキュメントも公開されているため、Dockerでシングル構成のElastic Searchを動かしたり、Docker Composeで複数ノードからなるElastic Search環境を導入できます。
そしてdocker-composeで構築するわけですが、起動後数分するとExit 78で停止されてしまうわけです。
$ sudo docker-compose up -d $ sudo docker-compose ps Name Command State Ports ------------------------------------------------------- es01 /usr/local/bin/docker-entr ... Exit 78 es02 /usr/local/bin/docker-entr ... Exit 78 es03 /usr/local/bin/docker-entr ... Exit 78調査
「Elastic Search Exit 78」などで検索するとvm.max_map_countの設定を増やせばThat's All!...みたいな情報ばかり出てきます(→例えばこれ)。
念のため試してみるのですが、相変わらずExit 78を回避できませんでした。
問題の原因
問題の原因はDockerとDocker Composeのバージョンの組み合わせの問題だったようです。今回Ubuntu Server 18.04でElastic Searchを動かそうとしたわけですが、DockerはUbuntu 18.04提供のバージョン、Docker Composeはサイトの手順を見ながらDocker公式のStableバージョンを入れたのがよくなかったようです。
バイナリインストールしたDocker Composeを削除して次のように導入した環境では正常にデプロイできました。
$ sudo apt update $ sudo apt install docker.io docker-compose -y $ docker -v Docker version 19.03.6, build 369ce74a3c $ docker-compose -v docker-compose version 1.17.1, build unknown $ sudo docker-compose up -d $ sudo docker-compose ps $ sudo docker-compose ps Name Command State Ports -------------------------------------------------------------------------------- es01 /usr/local/bin/docker-entr ... Up 0.0.0.0:9200->9200/tcp, 9300/tcp es02 /usr/local/bin/docker-entr ... Up 9200/tcp, 9300/tcp es03 /usr/local/bin/docker-entr ... Up 9200/tcp, 9300/tcpUbuntu 18.04のUbuntu Archive Packageのdokcer-composeはPython2が必要みたいですね。新しいバージョンはPython 3を要求するようです。単に必要なPythonモジュールが足りなかったのが原因だったのかもしれませんが、Elastic Searchの1ノードは問題なくて3ノード構成は失敗するのは本当に謎。Ubuntu Archive版のdocker-composeでは問題ありません。
おまけ
公式の手順では3つのノードのElastic SearchをデプロイするDocker Composeの例ですが、このようなYAMLを作れば1台のコンテナーにElastic SearchをDocker Composeで展開できます。
version: '3' volumes: es-data: driver: local services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2 volumes: - es-data:/usr/share/elasticsearch/data environment: - discovery.type=single-node - cluster.name=docker-cluster - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 ports: - "9200:9200"えっ一台ならdocker runでええやろですって?
そうかもしれません。おまけ2
データの永続化をもう少し安全にするには、次のように設定すると良いです。
$ sudo mkdir -p /home/data $ sudo chmod 777 /home/data $ vi docker-compose.yml version: '3' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2 volumes: - /home/data:/usr/share/elasticsearch/data environment: - discovery.type=single-node - cluster.name=docker-cluster - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 ports: - "9200:9200"Dockerのボリュームについては以下を参考にしました。
- 投稿日:2020-04-07T10:16:43+09:00
Docker初心者がdocker-compose.ymlについて説明する
はじめに
docker-compose.yml
は一見意味がわからないですが、一つ一つ調べると理解できたので説明していきます。docker-compose.yml
docker-compose.ymlversion: '3' services: web: build: . ports: - "3000:3000" depends_on: - db volumes: - .:/API_sample command: bundle exec rails s -p 3000 -b '0.0.0.0' db: image: mysql:5.7 volumes: - mysql_data:/var/lib/mysql/ environment: MYSQL_ROOT_PASSWORD: password ports: - "3306:3306" volumes: mysql_data:version
docker-composeのバージョンを指定します。
現時点では、version3が最新版のようです。リファレンスはこちら
services
アプリケーションを動かすための要素です。
ネストされたweb
とdb
は各要素で、自分で好きな名前をつけることができます。build
docker-compose build
のときにビルドするためのDockerfileのパスを指定しています。
ここでは、Dockerfileもdocker-compose.ymlもプロジェクト直下にあるのでこのような書き方をしています。ports
各serviceのポート番号を指定しています。
ホストOS(local)のポート番号:dockerコンテナ内のポート番号
という書き方ができます。
ここでは、railsのポート番号が3000
なので、3000:3000
という書き方をしています。1台のPCでDockerを使った複数のプロジェクトを扱うときに、ポート番号が重複するのを防ぐことができます。
たとえば3001:3001
という書き方をすると、localhost:3001
でサーバーに接続できます。depends_on
コンテナの依存関係を設定します。
今回はdb
に依存関係があることを明記しています。こうすることで、
docker-compose up
のときにDBを先に起動してくれます。volumes
dockerの中でデータを永続化する設定です。
この設定をすることで、コンテナ内のデータは消えずに残ってくれます。上記の例だと、
web
では相対パスのすべてのソース(API_sample)をコンテナのAPI_sampleにマウントしています。
そして、db
ではdbコンテナ内の/var/lib/mysqlをmysql_dataという名前でマウントしています。
このmysql_dataは最後の2行でvolumesとして設定されています。volumeは下記コマンドで一覧を見ることができます。
$ docker volume lscommand
docker-compose up
で実行されるコマンドです。
upと同時に、bundle exec rails s -p 3000 -b '0.0.0.0'
が実行されてサーバーに接続できるようになります。image
コンテナ作成に必要なイメージを指定しています。
ここでは、dbのコンテナ作成に、mysql5.7を使用すると設定しています。environment
環境変数を設定します。
database.ymlに書かれているパスワードを環境変数として設定し、mysqlに接続できるようにします。
- 投稿日:2020-04-07T10:03:27+09:00
HTTPリクエストと(Postgre)SQLを自動化するスクリプトを書いてみた
ローカル環境において、手動でHTTPリクエストや(Postgre)SQLを実行するのが辛くなってきたため、それらを自動化するBashスクリプトを書いてみました。
今回はこのスクリプトを解説いたします。想定している環境
- ローカル環境
- DBサーバ(PostgreSQL)がDocker上に存在
必要なライブラリのチェック
スクリプトではdocker、psql、jq、curlを使用しているため、これらがインストールされているかをチェックします。
各ライブラリの名前を、for文のin
に列挙しています。
which
コマンドでエラーが出力された場合、usage()
を呼び出し、標準出力は捨てています。function usage() { echo "please install docker, psql, jq, and curl" exit 1 } # check requirements for libName in docker psql jq curl; do which $libName > $DEVNULL || usage done(テスト用)既存のPostgreSQLコンテナの削除
スクリプトのテスト用に作成したDBコンテナがあれば削除します(実際には既存のDBコンテナが立っているはずなので、この処理はコメントアウトされるでしょう)。
docker ps -a
で、指定の名前のコンテナが存在するかを0
or1
で取得します。空白が入るためsed
で除去しています。
コンテナが存在した場合、停止と削除を実行します。# remove existing container exist=$(docker ps -a | grep $NAME | wc -l | sed 's/ //g') if [[ $exist = 1 ]]; then docker stop $NAME > $DEVNULL && docker rm $NAME > $DEVNULL echo "existing container $NAME removed" fi(テスト用)PostgreSQLコンテナの起動、疎通が確認できるまで待機
スクリプトのテスト用に、PostgreSQLのコンテナを起動します(実際には既にDBコンテナが立っているはずなので、この処理はコメントアウトされるでしょう)。
起動直後にpsql
を実行してしまうと接続できずにエラーとなるため、疎通が確認できるまで待つようにしています。
ここではpsql -c \l
を実行し、エラーが出力された場合はwhile
をループするようにしています。function healthcheck() { PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -c '\l' > $DEVNULL 2>&1 || return 1 return 0 } # startup postgres docker run -it -d -p $PORT:$PORT --name $NAME -e POSTGRES_PASSWORD=$PASSWORD postgres:latest > $DEVNULL # wait for container running up while true; do sleep 0.1 && healthcheck || continue break done(テスト用)データベース、テーブルの作成、レコードのinsertとselect
テスト用PostgreSQLコンテナ内にDB、Tableを作成し、レコードをinsert, selectします(実際には既存のDBコンテナが立っているはずなので、この処理はコメントアウトされるでしょう)。
# (temporary) preparation postgres PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -c "create database $DB" PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF create table fruit (id serial PRIMARY KEY, name text, price integer); insert into fruit (name, price) values ('apple', 100), ('orange', 200), ('lemon', 300); EOF # select all records before updates for i in {1..3} ; do PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF select name, price from fruit where id = ${i}; EOF doneHTTPリクエストで取得したResponseを元にSQLを実行
curl
でデータを取得します。ここではBitBankのPublic APIを利用しています。
取得したdata
はヒアドキュメント内で参照され、psql
が実行されます。# fetch data by HTTP Request # following response should be returned # { # "success": 0, # "data": { # "code": 10000 # } # } data=$(curl -s $URL | jq '.data | .code') echo $data # update record with the data PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF update fruit set price = ${data} where name = 'apple'; update fruit set price = ${data} where name = 'orange'; EOFレコードが更新されたことを確認
取得した
data
によってレコードが更新されていることを確認します。# select all records after updates for i in {1..3} ; do PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF select name, price from fruit where id = ${i}; EOF doneスクリプト全体
上記をまとめると、以下となります。
実際には下記のコードをアレンジしてご使用頂くかと思います。#!/bin/bash readonly NAME='test_postgres' readonly PASSWORD='password' readonly HOST='0.0.0.0' readonly PORT='5432' readonly USER='postgres' readonly DB='temp' readonly DEVNULL='/dev/null' readonly URL='https://public.bitbank.cc' function healthcheck() { PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -c '\l' > $DEVNULL 2>&1 || return 1 return 0 } function usage() { echo "please install docker, psql, jq, and curl" exit 1 } # check requirements for libName in docker psql jq curl; do which $libName > $DEVNULL || usage done # remove existing container exist=$(docker ps -a | grep $NAME | wc -l | sed 's/ //g') if [[ $exist = 1 ]]; then docker stop $NAME > $DEVNULL && docker rm $NAME > $DEVNULL echo "existing container $NAME removed" fi # startup postgres docker run -it -d -p $PORT:$PORT --name $NAME -e POSTGRES_PASSWORD=$PASSWORD postgres:latest > $DEVNULL # wait for container running up while true; do sleep 0.1 && healthcheck || continue break done # (temporary) preparation postgres PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -c "create database $DB" PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF create table fruit (id serial PRIMARY KEY, name text, price integer); insert into fruit (name, price) values ('apple', 100), ('orange', 200), ('lemon', 300); EOF # select all records before updates for i in {1..3} ; do PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF select name, price from fruit where id = ${i}; EOF done # fetch data by HTTP Request # following response should be returned # { # "success": 0, # "data": { # "code": 10000 # } # } data=$(curl -s $URL | jq '.data | .code') echo $data # update record with the data PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF update fruit set price = ${data} where name = 'apple'; update fruit set price = ${data} where name = 'orange'; EOF # select all records after updates for i in {1..3} ; do PGPASSWORD=$PASSWORD psql -h $HOST -U $USER -d $DB << EOF select name, price from fruit where id = ${i}; EOF doneさいごに
久々にシェルスクリプトを書いたのですが、楽しいですね。
こんな書き方があるよ、といったコメント等あれば是非お願いいたします。
- 投稿日:2020-04-07T07:59:54+09:00
コーディング未経験のPO/PdMのためのRails on Dockerハンズオン、Rails on Dockerハンズオン vol.14 - TDDでPost機能をコーディング part3 -
はじめに
こんにちは!
またまたPost機能の開発の続きです。今回でラスト!前回のソースコード
前回のソースコードはこちらに格納してます。今回のハンズオンからやりたい場合はこちらからダウンロードしてください。
前回の残り
- 未サインインのユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること
- 未サインインのユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
- サインイン済のユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること
- サインイン済のユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
- サインイン済のユーザーは、プロフィールページで自身のポストを投稿日時降順で閲覧できること
- サインイン済のユーザーが、プロフィールページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
残り6シナリオ。ユーザー詳細ページにそのユーザーのポストを表示する機能ですね。
では早速最後のコーディングをしていきましょう!!
未サインインのユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること
ユーザー詳細ページでそのユーザーのポストが投稿日時降順で表示されていることと、他のユーザーのポストが表示されていないことを検証します。
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "未サインインのユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること" do + # テスト用のユーザーを作成する + user1 = create_user(1) + user2 = create_user(2) + # ポストを用意する + posts1 = [] + posts1.unshift Post.create(content: "First Post!!", user: user1) + posts1.unshift Post.create(content: "Second Post!!", user: user1) + posts2 = [] + posts2.unshift Post.create(content: "初めてのポスト", user: user2) + posts2.unshift Post.create(content: "2回目のポスト", user: user2) + + # user1のユーザー詳細ページにアクセスする + visit user_path(user1) + + # user1のポストが投稿日時降順で表示されていることを検証する + posts1.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).to have_text post.user.name + expect(find("#posts_list").all(".post-item")[i]).to have_text post.content + end + # user2のポストは表示されないことを検証する + posts2.each do |post| + expect(page).not_to have_text post.user.name + expect(page).not_to have_text post.content + end + + # user2のユーザー詳細ページにアクセスする + visit user_path(user2) + + # user2のポストが投稿日時降順で表示されていることを検証する + posts2.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).to have_text post.user.name + expect(find("#posts_list").all(".post-item")[i]).to have_text post.content + end + # user1のポストは表示されないことを検証する + posts1.each do |post| + expect(page).not_to have_text post.user.name + expect(page).not_to have_text post.content + end + end end
少し長いですが、今までの延長で理解できるコードになっているはずです!(コメントアウトも参考にしてね。)
さて、このテストを回してみましょう。# rspec spec/system/07_posts_spec.rb Failures: 1) ユーザーとして、ポストを投稿したい 未サインインのユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること Failure/Error: expect(find("#posts_list").all(".post-item")[i].find(".post-user-name")).to have_text user.name Capybara::ElementNotFound: Unable to find css "#posts_list" Finished in 27.77 seconds (files took 5.67 seconds to load) 16 examples, 1 failureこの時点では
#posts_list
がないと怒られます。posts_list
、つまりユーザー詳細ページでポストを表示する機能をコーディングしていないので、テストが失敗しています。さて、今回の
posts_list
ですが、ポストページで同じようにポストの一覧を表示するViewを作りました。開発を効率的に進めるために、是非その時の機能を利用したいですね。
Railsでは部分テンプレート(Partial Template)という機能があります。複数のテンプレートから呼び出されるような一部分のViewを別ファイルに切り出して、各テンプレートからそれを呼び出すようなイメージです。
百聞は一見にしかずですので、まずは試してみましょう。
まずは、ポストページのposts_list
配下の要素を部分テンプレート化してみます。# touch app/views/posts/_posts_list.html.erb部分テンプレートは頭に
_
をつけるのが習わしです。app/views/posts/_posts_list.html.erb<% posts.each do |post| %> <div class="card post-item my-1"> <div class="card-body"> <h5 class="card-title"><%= link_to post.user.name, post.user, class: "post-user-name" %></h5> <p class="card-text"><%= safe_join(post.content.split("\n"), tag(:br)) %></p> </div> </div> <% end %>部分テンプレートはこんな感じで書きます。
posts/index.html.erb
に書いていた内容と変わらないです。唯一変わるポイントは最初のeach
する配列の変数が@posts
からposts
になっていることです。
部分テンプレートは別のテンプレートファイルから呼び出されますが、その時にインスタンス変数でなくても変数を渡すことができます。これも実際にみてみた方が早いと思いますので、まずはposts/index.html.erb
からこの部分テンプレートを読み込んで今と変わらない状態になることを確認してみましょう。app/views/posts/index.html.erb... <div id="posts_list" class="my-5"> - <% @posts.each do |post| %> - <div class="card post-item my-1"> - <div class="card-body"> - <h5 class="card-title"><%= link_to post.user.name, post.user, class: "post-user-name" %></h5> - <p class="card-text"><%= safe_join(post.content.split("\n"), tag(:br)) %></p> - </div> - </div> - <% end %> + <%= render partial: "posts_list", locals: { posts: @posts } %> </div> ...呼び出し方は
render partial:
に対して適用したいテンプレートのファイル名(頭の_
は除く)を指定するだけです。さらにオプションでlocals:
の後に{ 変数名: 値 }
をつけることで部分テンプレートに変数を受け渡すことができます。今回の例では@posts
を部分テンプレート内のposts
に代入させていることになります。
変数は複数受け渡すことができ、その場合は{ 変数名1: 値1, 変数名2: 値2 }
のように,
で区切るだけです。ここで一度デグレが起きていないかテストを実行しておきましょう。
# rspec spec/system/07_posts_spec.rb Failures: 1) ユーザーとして、ポストを投稿したい 未サインインのユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること Failure/Error: expect(find("#posts_list").all(".post-item")[i].find(".post-user-name")).to have_text user.name Capybara::ElementNotFound: Unable to find css "#posts_list" Finished in 37.59 seconds (files took 6.58 seconds to load) 16 examples, 1 failure今取り掛かっているシナリオのテスト失敗だけなので、ポストページに関するテスト失敗は起きていませんね。うまく部分テンプレートが機能しているようです。
さて、ユーザー詳細ページでもこの部分テンプレートを利用しましょう。
ユーザー詳細ページではそのユーザーのポストだけを表示したいので、部分テンプレートに渡すposts
変数にはそのユーザーのポストのArrayを渡してあげればいいことになります。app/views/users/show.html.erb<div class="container my-5"> <% flash.each do |msg_type, msg| %> <div class="alert alert-<%= msg_type %>"><%= msg %></div> <% end %> <%= @user.name %> <br> <%= @user.email %> + + <div id="posts_list" class="my-5"> + <%= render partial: "posts/posts_list", locals: { posts: @user.posts.order(created_at: :desc) } %> + </div> </div>
先ほどと少し違うのは、
_posts_list.html.erb
がこのファイルとは別のディレクトリ(posts/
)にあるので、そのディレクトリも含めて部分テンプレートファイル名を指定しています。(posts/posts_list
)
また、posts
変数には@user.posts.order(created_at: :desc)
でそのユーザーのポストを作成日時降順で取得したArrayを部分テンプレートに渡しています。ではテストを回してみましょう。
# rspec spec/system/07_posts_spec.rb Finished in 50.91 seconds (files took 7.01 seconds to load) 16 examples, 0 failuresGreenになりました!
未サインインのユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
ポストページの場合はユーザー名クリックでユーザー詳細ページへ遷移させていましたが、ユーザー詳細ページ上では
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "未サインインのユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと" do + # テスト用のユーザーを作成する + user = create_user(1) + # テスト用のポストを作成する + posts = [] + posts.unshift Post.create(content: "First Post!!", user: user) + posts.unshift Post.create(content: "Second Post!!", user: user) + + # userのユーザー詳細ページにアクセスする + visit user_path(user) + + # ポストのユーザー名がリンクになっていないことを検証する + posts.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).not_to have_selector("a.post-user-name") + end + end end
ポストページと同じ部分テンプレートを使っているので、現在はポストのカードの中のユーザーの名前が表示されている要素は
post-user-name
をclass属性に付与されているa
タグになっています。
これがリンクを作っているところなので、この要素がない状態であれば、ユーザー名をクリックしても何も起こらないことを検証できます。# rspec spec/system/07_posts_spec.rb Failures: 1) ユーザーとして、ポストを投稿したい 未サインインのユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと Failure/Error: expect(find("#posts_list")).not_to have_selector("a.post-user-name") expected not to find visible css "a.post-user-name" within #<Capybara::Node::Element tag="div" path="/HTML/BODY[1]/DIV[1]/DIV[1]">, found 2 matches: "John Smith", "John Smith" Finished in 41.18 seconds (files took 5.05 seconds to load) 17 examples, 1 failure現在はリンクがついてしまっているのでこれをなんとかします。
今
link_to
を使ってリンクを作っていますが、link_to_unless_current
を使ってみます。使い方はlink_to
と変わりないのですが、リンク先が今のパスの場合は単なるテキストを表示してくれるメソッドです。app/views/posts/_posts_list.erb- <h5 class="card-title"><%= link_to post.user.name, post.user, class: "post-user-name" %></h5> + <h5 class="card-title"><%= link_to_unless_current post.user.name, post.user, class: "post-user-name" %></h5>これでテストを実行してみましょう。
# rspec spec/system/07_posts_spec.rb Finished in 34.46 seconds (files took 4.86 seconds to load) 17 examples, 0 failures無事テストがパスしました。ポストページの方もデグレは起きていないかも全てのテストをパスしていることから確認できますね。
サインイン済のユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること
上の2つのテストのサインイン済版ですね。
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "サインイン済のユーザーは、ユーザー詳細ページでそのユーザーのポストを投稿日時降順で閲覧できること" do + # テスト用のユーザーを作成する + user1 = create_user(1) + user2 = create_user(2) + # ポストを用意する + posts1 = [] + posts1.unshift Post.create(content: "First Post!!", user: user1) + posts1.unshift Post.create(content: "Second Post!!", user: user1) + posts2 = [] + posts2.unshift Post.create(content: "初めてのポスト", user: user2) + posts2.unshift Post.create(content: "2回目のポスト", user: user2) + # user1でサインインする + sign_in(user1) + + # user2のユーザー詳細ページにアクセスする + visit user_path(user2) + + # user2のポストが投稿日時降順で表示されていることを検証する + posts2.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).to have_text post.user.name + expect(find("#posts_list").all(".post-item")[i]).to have_text post.content + end + # user1のポストは表示されないことを検証する + posts1.each do |post| + expect(page).not_to have_text post.user.name + expect(page).not_to have_text post.content + end + end end
未サインインの時と検証内容は同じですね。
# rspec spec/system/07_posts_spec.rb Finished in 36.67 seconds (files took 7.58 seconds to load) 18 examples, 0 failuresすでに実装済みですのでテストはGreenです。
サインイン済のユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
これも未サインインで同じテストをしているのでそれをパクります。
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "サインイン済のユーザーが、ユーザー詳細ページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと" do + # テスト用のユーザーを作成する + user1 = create_user(1) + user2 = create_user(2) + # テスト用のポストを作成する + posts = [] + posts.unshift Post.create(content: "First Post!!", user: user2) + posts.unshift Post.create(content: "Second Post!!", user: user2) + # user1でサインインする + sign_in(user1) + + # user2のユーザー詳細ページにアクセスする + visit user_path(user2) + + # ポストのユーザー名がリンクになっていないことを検証する + posts.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).not_to have_selector("a.post-user-name") + end + end end
# rspec spec/system/07_posts_spec.rb Finished in 41.16 seconds (files took 5.61 seconds to load) 19 examples, 0 failuresこれも実装済みなのでテストがパスしていますね。
サインイン済のユーザーは、プロフィールページで自身のポストを投稿日時降順で閲覧できること
これも以前のテストとほぼ同じ。
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "サインイン済のユーザーは、プロフィールページで自身のポストを投稿日時降順で閲覧できること" do + # テスト用のユーザーを作成する + user1 = create_user(1) + user2 = create_user(2) + # ポストを用意する + posts1 = [] + posts1.unshift Post.create(content: "First Post!!", user: user1) + posts1.unshift Post.create(content: "Second Post!!", user: user1) + posts2 = [] + posts2.unshift Post.create(content: "初めてのポスト", user: user2) + posts2.unshift Post.create(content: "2回目のポスト", user: user2) + # user1でサインインする + sign_in(user1) + + # user1のプロフィールページにアクセスする + visit user_path(user1) + + # user1のポストが投稿日時降順で表示されていることを検証する + posts1.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).to have_text post.user.name + expect(find("#posts_list").all(".post-item")[i]).to have_text post.content + end + # user2のポストは表示されないことを検証する + posts2.each do |post| + expect(page).not_to have_text post.user.name + expect(page).not_to have_text post.content + end + end end
ほい。ではテストを回しましょう。
# rspec spec/system/07_posts_spec.rb Finished in 41.83 seconds (files took 6.77 seconds to load) 20 examples, 0 failuresこれもパス。
サインイン済のユーザーが、プロフィールページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと
これも同じようなテストをすでにしていますね。
spec/system/07_posts_spec.rbfeature "ユーザーとして、ポストを投稿したい", type: :system do ... + scenario "サインイン済のユーザーが、プロフィールページでそのユーザーのポストのユーザー名をクリックしたとき、何も起こらないこと" do + # テスト用のユーザーを作成する + user = create_user(1) + # テスト用のポストを作成する + posts = [] + posts.unshift Post.create(content: "First Post!!", user: user) + posts.unshift Post.create(content: "Second Post!!", user: user) + # user1でサインインする + sign_in(user) + + # user1のプロフィールページにアクセスする + visit user_path(user) + + # ポストのユーザー名がリンクになっていないことを検証する + posts.each_with_index do |post, i| + expect(find("#posts_list").all(".post-item")[i]).not_to have_selector("a.post-user-name") + end + end end
これもパスするはず。
# rspec spec/system/07_posts_spec.rb Finished in 43.64 seconds (files took 6.54 seconds to load) 21 examples, 0 failuresパスしましたね。
ここまででポスト機能で定義したテストシナリオは全てパスできるアプリケーションを作ることができました!
最後に、今までのテストシナリオも含めてデグレの確認をしておきましょう!# rspec Finished in 1 minute 56.88 seconds (files took 6.09 seconds to load) 91 examples, 0 failures2分ほど時間がかかりましたが、全てのテストをクリアできていました!!
まとめ
今日はここまでです!前回、前々回と3回に渡ってポスト機能をTDDでコーディングしてきましたがいかがだったでしょうか?
モデルの関連付け(has_many
,belongs_to
)や部分テンプレート(Partial Template)など新しく使ったものもありましたが、基本的な部分はハードルなくコーディングできるようになったのではないでしょうか?
実際にサービスをリリースするとなると、例えばアイコン登録とか、フォロー機能とか、いいね機能とか、、、作りたい機能がどんどんでてくるとは思いますが、すでに自分で調べながらコーディングをしていくことに対するハードルはなくなったんじゃないでしょうか?
ということでこのハンズオンのコーディング部分はこれで以上にしたいと思います。最後はデプロイです!次とその次、2回に分けてアプリケーションを
Heroku
とEKS
にデプロイしてみようと思います。ここまでできれば、自分の好きなサービスを作って世に公開することができます。ではまた次週!
後片付け
# exit$ docker-compose down本日のソースコード
Other Hands-on Links
- 投稿日:2020-04-07T02:15:28+09:00
Dockerのメモリ使用量を確認したい
はじめに
Dockerを使用する中で、この子たちがどのくらいメモリを食っているのかを確認したくなったので、調べました。
確認方法
アクティビティモニタで確認
docker stats
実行中のDocker Containerのメモリ使用量を
docker stats
コマンドを使って調べることができるみたいです。https://docs.docker.com/engine/reference/commandline/stats/
$ docker stats CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 0d173431ff71 concourse-worker_1 14.37% 32.16MiB / 1.943GiB 1.62% 53.4MB / 86.6MB 0B / 0B 22 91e1a99d4d30 concourse-web_1 24.25% 32.19MiB / 1.943GiB 1.62% 356MB / 318MB 0B / 0B 13 189cc19df954 concourse-db_1 0.82% 49.21MiB / 1.943GiB 2.47% 249MB / 230MB 0B / 0B 24 47590fe69e09 sonarqube-db_1 0.00% 6.246MiB / 1.943GiB 0.31% 6.57kB / 1.22kB 0B / 0B 7
内容 CONTAINER ID ContainerのID NAME Containerの名前 CPU % Containerが使用しているCPU使用率 MEM USAGE / LIMIT 使用しているメモリ / Dockerに許可されたメモリ(Docker > Stettings > Resources) MEM % Containerが使用しているメモリ使用率 NET I/O Containerが送受信したデータ量 BLOCK I/O Containerがブロックデバイスに読み書きしたデータ量 PIDS プロセスID オプション
$ docker stats --help Usage: docker stats [OPTIONS] [CONTAINER...] Display a live stream of container(s) resource usage statistics Options: -a, --all Show all containers (default shows just running) --format string Pretty-print images using a Go template --no-stream Disable streaming stats and only pull the first result --no-trunc Do not truncate output起動中の全Container表示
$ docker stats -a CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS ea19cee314d2 studyyml_concourse-worker_1 0.16% 49.73MiB / 1.943GiB 2.50% 302kB / 487kB 0B / 0B 20 11150df2f0af studyyml_concourse-web_1 0.25% 27.84MiB / 1.943GiB 1.40% 1.85MB / 1.8MB 0B / 0B 10 17a9843e204a studyyml_sonarqube_1 0.00% 0B / 0B 0.00% 0B / 0B 0B / 0B 0 876602d0178e studyyml_concourse-db_1 0.01% 45.26MiB / 1.943GiB 2.27% 1.46MB / 1.3MB 0B / 0B 22 06b430623fc0 studyyml_sonarqube-db_1 0.00% 6.891MiB / 1.943GiB 0.35% 4.23kB / 2.44kB 0B / 0B 7リアルタイムで更新されていくのですが、
control + C
で中止することができます。(最初これ知らなくて焦りました・・・)フォーマットして表示
https://docs.docker.com/engine/reference/commandline/stats/#formatting
.Container
,.Name
,.ID
,.CPUPerc
,.MemUsage
,.NetIO
,.BlockIO
,.MemPerc
,.PIDs
が選択可能。$ docker stats --format "{{.Name}} : {{.MemUsage}}" studyyml_concourse-worker_1 : 50.04MiB / 1.943GiB studyyml_concourse-web_1 : 28.21MiB / 1.943GiB studyyml_concourse-db_1 : 46.7MiB / 1.943GiB studyyml_sonarqube-db_1 : 6.883MiB / 1.943GiB一度だけ表示
$ docker stats --no-stream CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS ea19cee314d2 studyyml_concourse-worker_1 0.14% 50.05MiB / 1.943GiB 2.52% 851kB / 1.38MB 0B / 0B 21 11150df2f0af studyyml_concourse-web_1 0.27% 28.23MiB / 1.943GiB 1.42% 4.7MB / 4.39MB 0B / 0B 10 876602d0178e studyyml_concourse-db_1 0.14% 46.62MiB / 1.943GiB 2.34% 3.5MB / 3.26MB 0B / 0B 23 06b430623fc0 studyyml_sonarqube-db_1 0.00% 6.902MiB / 1.943GiB 0.35% 4.44kB / 2.44kB 0B / 0B 7切り捨て表示しない
ContainerIDが全表示になりました。
$ docker stats --no-trunc CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS ea19cee314d2aa230ae71215fbae442b08386bcc4c3d963f1e02550c1b51f909 studyyml_concourse-worker_1 3.35% 49.99MiB / 1.943GiB 2.51% 923kB / 1.49MB 0B / 0B 21 11150df2f0af1198e3fe689697621a3cb5270d81a572de6a6d67a588b2b17173 studyyml_concourse-web_1 0.00% 28.28MiB / 1.943GiB 1.42% 5.04MB / 4.71MB 0B / 0B 10 876602d0178eb1ae7b7333c2203837fa2f292dc9f89a2832f6622111b2be054c studyyml_concourse-db_1 0.09% 46.86MiB / 1.943GiB 2.35% 3.75MB / 3.48MB 0B / 0B 23 06b430623fc0a59df6bc2ca9cf9c7add45465c59ff3ad08e28661fe871a1687f studyyml_sonarqube-db_1 0.02% 6.883MiB / 1.943GiB 0.35% 4.44kB / 2.44kB 0B / 0B 7