- 投稿日:2020-09-14T22:17:20+09:00
Fedora CoreOSをVirtualBoxで試してみた
自己紹介
趣味でプログラミングをしている高専2年生です。
普段は†限界開発鯖†という開発コミュニティにいます。Twitter: @programmer_shun
GitHub: @shun-shobonFedora CoreOSとは
Fedora CoreOSとは2020年5月26日にサポート終了が発表されたCoreOS社のContainer Linux(旧CoreOS)の後継となるOSです。
Container Linuxのサポート終了についての詳しい情報はこちらをご覧ください。
今回はこのFedora CoreOSをVirtualBoxを用いて試してみようと思います。
Fedora CoreOSのISOイメージの入手
こちらのダウンロードページからFedora CoreOSのISOイメージをダウンロードします。
Fedora CoreOSには3つのリリースストリームが存在します。
- Stable(安定しており本番環境でも使用可能)
- Test(Stableの前の段階で本番環境で動かせるかをテストするためのバージョン)
- Next(新機能やカーネルの最新バージョンを使用することができる)
今回はStable版の
32.20200824.3.0
を使用します。仮想イメージの作成
VirtualBoxを起動し、「新規」を選択、名前や場所を指定します。
ハードディスクの設定を行います。お好きな大きさにしてください。(今回は8GBにします。)
仮想イメージが作成できたら「設定」を選択して、各種設定を行います。
光学ドライブにダウンロードしておいたFedora CoreOSのISOイメージをセットします。
Fedora CoreOSとは基本的にSSHで接続するのでSSH用のポートフォワーディングを設定します。
yamlファイルの作成
Fedora CoreOSはIgnitionファイルというファイルを使用してインストール時の初期設定を行います。
初期設定を特定ファイルによって行うことで複数台のインストールを楽にする狙いがあるようです。ホストマシンで以下のようなyamlファイルを作成します。
ignition.ymlvariant: fcos version: 1.1.0 passwd: users: - name: core ssh_authorized_keys: - <公開鍵>
variant
Fedora CoreOSのIgnitionファイルと識別するための項目です。
version
Ignitionファイルのバージョンを設定します。
passwd
デフォルトで作成されるユーザやグループを設定します。
passwd.users
ユーザの設定をします。
passwd.users.name
ユーザの名前を設定します。
core
にした場合は特殊なユーザになり、デフォルトでsudoをパスワードなしで使用することができます。
passwd.users.ssh_authorized_keys
sshの公開鍵をリストにして貼り付けます。ここで設定した公開鍵でSSH接続できるようになります。
設定項目は他にもあります。詳細はこちらをご覧ください。
Ignitionファイルの生成
作成したyamlファイルはそのままではインストールに使用できません。
インストールではyamlから変換したignファイル(実体はただのjsonファイル)を使用します。
通常のyaml→json変換ツールを使用することもできますが、公式のツールであるFedora CoreOS Configuration Transpiler(FCCT)を使用することで特有の構文エラーを発見することが可能です。FCCTはDockerまたはPodmanで簡単に実行することが可能です。
# Podmanを使用する場合はdockerの部分をpodmanに置き換えてください。 docker run -i --rm quay.io/coreos/fcct:release --pretty --strict < ignition.yaml > ignition.ignIgnitionファイルのホスティング
作成したIgnitionファイルを仮想マシンに渡すためにFedora CoreOSではhttpを利用することを推奨しています。
お手軽なhttpホスティングの例としてpython3を利用したホスティングを例としてあげます。Ignitonファイルが存在するディレクトリで以下を実行してください。python3 -m http.server
OSインストール
準備が整ったので仮想マシンにFedora CoreOSをインストールします。
仮想マシンを起動し、ignitionファイルを入手します。curl -LO http://<ホストOSのローカルIP>:8000/ignition.ign
以下のコマンドでOSをインストールします。特に確認が入るわけではないのでインストール先のドライブに注意してください。また、インストール先のドライブのパーティションもすべて自動的に削除されます。
sudo coreos-installer install /dev/sda --ignition-file ignition.ignインストールに成功したら
sudo shutdown -h now
で一度シャットダウンして仮想マシンの光学ドライブにセットしていたISOイメージを取り外します。仮想マシンを起動して、ホストマシンからSSH接続を試してみます。
# ポート番号はポートフォワーディングで設定したポートを指定してください。 ssh core@127.0.0.1 -p 2222無事SSH接続できたらOSのインストールは成功です。ここで一度仮想マシンのスナップショットを作成しておくことをおすすめします。もし仮想マシンに問題が発生したり一度リセットしてやり直したいときに便利です。
Docker&Podmanを試す
Fedora CoreOSにはデフォルトでDockerとPodmanがインストールされています。試しに動作させてみます。
Docker
# Dockerはsudoが必要なので注意してください。 sudo docker run hello-worldPodman
podman run hello-world感想
今回始めてコンテナ向けOSを試してみましたが、導入自体は案外楽だと感じました。
今度DigitalOceanに導入してみようと思います。
- 投稿日:2020-09-14T21:40:53+09:00
Kubernetes: NodePortを利用したアプリの公開、デプロイ
0.アプリを公開したい
久しぶりに投稿します。今回はKubernetesでNodePort Serviceを利用してアプリを公開する方法を紹介していきます。
Kubernetesを使って試しに、気軽にアプリをデプロイしてみたい(お金はかけたくない。。。)!という方向けです。今回使用した環境はIBM Cloudですが、どのCloudでも同様にデプロイできると思います。Kubernetes環境の無料枠が一ヶ月で、期限が切れても再び構築することはできます。
無料枠ではIngressなどを使うことはできませんが、Serviceリソースの一つであるNodePortを利用してアプリを公開することはできます。Dockerコンテナの作成からKubernetes上へのデプロイまでを行なっているので、これらの一連の流れが掴めるようにはなってると思います。
前提
- IBM Cloud アカウントの取得
- IBM CloudのKubernetes上にクラスターを構築してあること
- DockerがインストールされていてDocker Hubのアカウントを持っている
- 公開してみたいアプリがあること(なんでもよし。今回作成するアプリに関しては以下の環境が必要)
- Node.js(node: v12.18.2, npm: 6.14.5)環境があること
- create-react-appコマンドを使えること
1.サンプルアプリのDockerコンテナの作成
今回はcreate-react-appで簡単に作成できるReactアプリをデプロイしてみようと思います。まずはコンテナ化して動作確認します。
1-1. Reactアプリ作成(create-react-appはインストールされている前提)
$ create-react-app my-react-app $ cd my-react-app1-2. Dockerfile作成
Dockerfileを以下のように作成し、my-react-appディレクトリ直下に配置します。
DockerfileFROM node:12 COPY . /app WORKDIR /app RUN npm run build && \ npm install -g serve CMD [ "serve" , "-s", "build" ]今回のコンテナ作成の流れとしてはproduction用の静的ファイルを生成し,それを静的ファイルサーバーを用いて実行するものになっています。
静的ファイルサーバー実行時、デフォルトでは5000番ポートを受け付けます。1-3. ビルドし、ローカルで動くことを確認する
$ docker build -t my-react-app:0.0.1 . $ docker run -p 5000:5000 my-react-app:0.0.1 INFO: Accepting connections at http://localhost:5000 #localhost:5000でreactアプリが動いていることができたらexit2.DockerHubにpushする
2-1. 'docker tag'で1で作成したイメージに自分のDocker Hubのアカウント名(レポジトリ名)を付与する
$ docker login Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: <DockerHubアカウント名> Password: Login Succeeded $ docker tag my-react-app:0.0.1 <DockerHubアカウント名>/my-react-app:0.0.12-2. イメージをDocker Hubのレポジトリににpushする
my-react-app $ docker push <DockerHubアカウント名>/my-react-app:0.0.1 The push refers to repository [docker.io/bangdr2018/my-react-app] 24531b8429e8: Pushed ea8d003551da: Pushed 9cee2cb2fb8e: Mounted from library/node eb3f44bfde1e: Mounted from library/node 3b93aab45097: Mounted from library/node 174e334f3f46: Mounted from library/node cbe6bbd0c86f: Mounted from library/node ef5de533cb53: Mounted from library/node a4c504f73441: Mounted from library/node e8847c2734e1: Mounted from library/node b323b70996e4: Mounted from library/node 0.0.1: digest: sha256:f032be9c6a2530de810d1a29b75777a01655e7a23b64a7aa39e81f5154a82640 size: 26382-3. イメージがpushできたことDocker Hubのコンソール上で確認(アカウント名は念のため切ってます)
3. Kubernetesへのデプロイ、アプリの公開
3-1. kubectlコマンドのコンテキストを確認する。
コンテキストが存在しなかったり、ローカルなど異なるコンテキストとなっていた場合は、kubectlコマンドをダウンロードする
// すでにIBM Cloudに構築したKubernetesからkubectlコマンドをダウンロードしていた場合コンテキストは次のようになる $ kubectl config current-context <クラスター名>/<クラスターID>ダウンロードしていなかった場合はIBM Cloudコンソール上でクラスターのプロファイルを開き、「アクセス」の箇所に以下のようにkubectlコマンドのダウンロード方法が載っている(ibmcloudコマンドをすでに使用できる場合「CLIツールの一回かぎりのセットアップ」は不要)。
3-2. deployment.ymlの作成
以下のようにdeployment.ymlを作成。containerのimageに2でpushしたイメージを指定する
deployment.ymlapiVersion: apps/v1 kind: Deployment metadata: name: my-react-app-deployment spec: selector: matchLabels: app: my-react-app template: metadata: labels: app: my-react-app spec: containers: - name: my-react-app image: <DockerHubアカウント名>/my-react-app:0.0.1 resources: limits: memory: "128Mi" cpu: "500m" ports: - containerPort: 5000Podが生成、Runnningとなっていることを確認(Podの名前はデプロイメント名に付随する)
$ my-react-app $ kubectl get pods NAME READY STATUS RESTARTS AGE my-react-app-deployment-c4f95c7cd-rx8xt 1/1 Running 0 8s3-3. NodePortを利用したserviceを作成する
以下のようにservice.ymlを作成。selectorのラベル(app: my-react-app)とPodのラベル(app: my-react-app)を一致させる。
ポートの番号は30000-32767の間に指定する。service.ymlapiVersion: v1 kind: Service metadata: name: my-react-app-service spec: type: NodePort selector: app: my-react-app ports: - port: 5000 targetPort: 5000 nodePort: 30080 protocol: "TCP" name: "http-port"serviceが生成されていることを確認の確認(Podの名前はデプロイメント名に付随する)
my-react-app $ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-react-app-service NodePort 172.21.94.246 <none> 5000:30080/TCP 10s3-4. Nodeに割り振られているExternal IPを確認する
NodePort Serviceはその名の通り、:<ポート>で受信したトラフィックをコンテナに送信するものなので、nodeのEXTERNAL-IPを確認する。
// -o wideオプションで表示可能 $ kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP(これ!) OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME 10.144.194.234 Ready <none> 13d v1.17.11+IKS 10.144.194.234 159.122.xxx.xxx Ubuntu 16.04.7 LTS 4.4.0-187-generic containerd://1.3.43-5. 公開されたアプリをみてみる
さあ緊張の瞬間ですが:で公開されたアプリを見てみましょう。
公開できました!やりましたね!
おわりに
今回はアプリのコンテナ化からKubernetes上へのデプロイまでの一連の流れを紹介してみました。
結構簡単に、かつ柔軟にデプロイできると思いました。ただKubernetesのメリットとしてはこんなものではないので自分自身も勉強しつつ有益だと思ったらどんどん投稿していこうと思います。
次はKubernetesで構築したアプリの環境変数周りのリソースについて説明してみようかなと思ってます。
- 投稿日:2020-09-14T20:24:52+09:00
Django + Docker でImageFieldを反映させる方法
はじめに
今回の記事では、僕が実際に「ImageField」の反映の際に悩まされたエラーについて解決方法を通して書いていきたいと思います。
kadaikun.Image.image: (fields.E210) Cannot use ImageField because Pillow is not installed.HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "pip install Pillow"
最初はこのエラーで悩まされていました。
pillowをインストールしたのに、インストールされていないと言われ「?」状態でした。
とりあえず次のコマンドで対処を試みました。terminal$ python3 -m pip uninstall pillow $ python3 -m pip install pillowそしてマイグレイトしました。
terminal$ python manage.py migrate結果...
terminalkadaikun.Image.image: (fields.E210) Cannot use ImageField because Pillow is not installed. HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "pip install Pillow"
ダメでした...。
このときに自分がDockerを使っていることに気がつきました...。
Dockerは今回が使うのは初めてだったので、「Dockerfile」とか「requirementes.txt」あたりを変更させればいいのかなくらいしかわかっていませんでした。
そのためググりにググって出てきたものは片っ端からDockerfileに書き込んで、「docker-compose up」をひたすら繰り返していました。
/bin/sh: 1: apk: not found
繰り返した結果...
terminal/bin/sh: 1: apk: not found
とエラーが出るだけで、全く解決しませんでした。
そんな中ついに解決しそうな記事を見つけました。
それがこの記事です。
Docker+djangoで詰まった所
この記事のようにDockerfileに
DockerfileRUN pip install pillowと記入し、「equirementes.txt」に、
equirementes.txtPillow==7.2.0と記入します。
そしてターミナルに次のように打ち込みます。
terminal$ python manage.py makemigrations $ python manage.py migrate $ docker-compose upこれで完了!と言いたいところなのですが、ブラウザを開いたときにエラーが出ました...。
OperationalError
エラーが出たものの、この対処法は簡単でした。
terminal$ rm -d -r db.sqlite3 $ rm -d -r app/migrations/* $ python manage.py migrateこれでOKです!
最後に
terminal$ docker-compose upと打ち込むときれいに反映されているはずです。
ちなみに参考にした記事はこちらです。
webページを開くと django.db.utils.OperationalError: no such column: app_action.author_idエラー
最後に
今回は僕が体験したエラーについて書いていきました。
Djnagoについての記事もこれから上げていくので、参考になれば幸いです。
- 投稿日:2020-09-14T20:24:52+09:00
Django + Docker でImageFieldを反映させる方法(pillow)
はじめに
今回の記事では、僕が実際に「ImageField」の反映の際に悩まされたエラーについて解決方法を通して書いていきたいと思います。
kadaikun.Image.image: (fields.E210) Cannot use ImageField because Pillow is not installed.HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "pip install Pillow"
最初はこのエラーで悩まされていました。
pillowをインストールしたのに、インストールされていないと言われ「?」状態でした。
とりあえず次のコマンドで対処を試みました。terminal$ python3 -m pip uninstall pillow $ python3 -m pip install pillowそしてマイグレイトしました。
terminal$ python manage.py migrate結果...
terminalkadaikun.Image.image: (fields.E210) Cannot use ImageField because Pillow is not installed. HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "pip install Pillow"
ダメでした...。
このときに自分がDockerを使っていることに気がつきました...。
Dockerは今回が使うのは初めてだったので、「Dockerfile」とか「requirementes.txt」あたりを変更させればいいのかなくらいしかわかっていませんでした。
そのためググりにググって出てきたものは片っ端からDockerfileに書き込んで、「docker-compose up」をひたすら繰り返していました。
/bin/sh: 1: apk: not found
繰り返した結果...
terminal/bin/sh: 1: apk: not found
とエラーが出るだけで、全く解決しませんでした。
そんな中ついに解決しそうな記事を見つけました。
それがこの記事です。
Docker+djangoで詰まった所
この記事のようにDockerfileに
DockerfileRUN pip install pillowと記入し、「equirementes.txt」に、
equirementes.txtPillow==7.2.0と記入します。
そしてターミナルに次のように打ち込みます。
terminal$ python manage.py makemigrations $ python manage.py migrate $ docker-compose upこれで完了!と言いたいところなのですが、ブラウザを開いたときにエラーが出ました...。
OperationalError
エラーが出たものの、この対処法は簡単でした。
terminal$ rm -d -r db.sqlite3 $ rm -d -r app/migrations/* $ python manage.py migrateこれでOKです!
最後に
terminal$ docker-compose upと打ち込むときれいに反映されているはずです。
ちなみに参考にした記事はこちらです。
webページを開くと django.db.utils.OperationalError: no such column: app_action.author_idエラー
最後に
今回は僕が体験したエラーについて書いていきました。
Djnagoについての記事もこれから上げていくので、参考になれば幸いです。
- 投稿日:2020-09-14T17:05:13+09:00
ローカルのdocker環境を間違いなくキレイにするコマンド
ローカルにDockerのイメージやらコンテナのデータなどがマウントされていて容量を圧迫している方へ。
全部消します(笑)
※ただし容赦なく全消しされるので注意
docker rm -f `docker ps -aq` docker rmi -f `docker images -q` docker network rm `docker network ls -q` docker volume rm `docker volume ls -q`
- 投稿日:2020-09-14T16:54:27+09:00
Dockerコマンド超省略版
ターミナルからコンテナ作成〜dockerhubリポジトリ作成までの最低限のコマンドをまとめました。
イメージ作成
※ cd でdockerfileのあるディレクトリに予め移動
docker build -t --force-rm=true (イメージ名):(タグ) .--force-rm:ビルド失敗したらイメージ削除
コンテナ作成+起動
/bin/bach は任意の実行コマンドに変更可
docker run --runtime=nvidia --rm -v (マウント先ディレクトリ):(コンテナ内でのディレクトリ名)\\ --name (コンテナ名) -it (イメージ名) /bin/bash--runtime=nvidia:ランタイム指定(たぶん2020年時点で変更されている)
--rm:実行終了時にコンテナ削除
-v:DockerコンテナにホストOSのディレクトリを共有するdockerhubに上げるリポジトリ作成
docker commit (コンテナ名) (リポジトリ名):(タグ)dockerhubにアップする
docker login # ユーザ名とパスワードを求められる docker push (リポジトリ名):(タグ)(おまけ)Docker操作
docker images -a # イメージ一覧 docker rmi (イメージID) # イメージ削除 docker ps -a # コンテナ一覧 docker rm (コンテナID) # コンテナ削除 docker start -i (コンテナID) # コンテナ起動-a:未使用のイメージ、コンテナも表示
- 投稿日:2020-09-14T15:17:23+09:00
DockerのJenkins経由でNuxtアプリをfirebase deploy
やりたいこと
- (目標)URLを叩くと、PULLして、Firebase deployする
Dockerfile
FROM jenkins USER root RUN cd tmp/ \ && wget https://updates.jenkins-ci.org/latest/jenkins.war \ && mv ./jenkins.war /usr/share/jenkins/ USER jenkinsdocker-compose.yml
version: "3" services: jenkins: build: . ports: - 8080:8080 volumes: - ./jenkins_home:/var/jenkins_homeDockerコマンドで構築
docker-compose uphttp://localhost:8080/
Jenkinsはこれで、構築完了。
空のジョブを一つ作成して、実行しておけば、workspaceができる。
このworkspaceでpullできるように構築する。
保存
ビルド実行Nuxtアプリをビルドしてデプロイできる環境を構築
# ログイン docker-compose exec jenkins bash # sshキーの作成 ssh-keygen ls ~/.ssh eval `ssh-agent` ssh-add ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub # これをバックログのSSH公開鍵に設定 # NVM curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" source /root/.bashrc nvm install stable # sudoコマンドインストール apt update apt-get -qq update apt install -y sudo apt-get install build-essential # ジョブのディレクトリに移動 cd "var/jenkins_home/workspace/My Job" git clone バックログのSSHのURL # Nuxtのビルド npm run i && npm run generate # firebaseコマンドのインストール curl -sL https://firebase.tools | bash firebase login --no-localhost # firebase loginはトークンを画面コピーしてターミナルに貼れば認証されるこれで、NuxtプロジェクトがビルドできていればOK!
Jenkinsの設定
ビルドトリガーはURLを叩くと、このジョブが走る設定です。
画面の例だと、
http://localhost:8080/job/amazing-master/build?token=testtesttest
これで、このバッチが動きます。Jenkinsシェルの実行
export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" cd "My Job" git pull origin master npm i && npm run generate firebase deploy
- npmコマンドの設定
- git pull
- nuxt generate
- firebase deploy を設定
これで、、、
http://localhost:8080/job/amazing-master/build?token=testtesttestを叩いて、デプロイできれば成功です。
- 投稿日:2020-09-14T15:17:02+09:00
ローカルのdocker環境で作ったWebアプリをAWSに移行する方法
背景
ローカル環境でWebアプリを作っていたのでAWS上のサーバへ移行した。AWSでサーバを立てたことなかったので有識者に教えてもらいながらやってみた。忘れそうなので今後の自分のためにサーバ上でdockerを動かすところまでの手順を残しておく。
本編
大まかな作業手順はこう。
1. AWSにEC2インスタンスを立てる
2. 立てたサーバの中でdockerをインストールする
3. gitlabからアプリケーションをクローンする実際に行った手順
実際の手順は以下のようにした。ただし今回、EC2インスタンスの作り方は割愛する。
サーバに入るまで
- AWSのEC2インスタンスを作る(詳細は今後書くかもしれない)
- 1ができると
.pem
がついたファイルができるのでこれは~/.ssh/
大切に保存しておく。- AWSのEC2のページにipアドレスがあるのでsshする。
ssh xx.xxx.xx.xxx
やってみたけどできなかった。- ユーザ名をつけてやってみた。
ssh ubuntu@xx.xxx.xx.xxx
やってみたけどできなかった。- エラーログがみたい時
ssh -v ubuntu@xx.xxx.xx.xxx
ssh -i ~/.ssh/xxx.pem ubuntu@xx.xxx.xx.xxxでxxx.pem
コマンドで入れた。xxx.pemというキーを使ってubuntuに入りますよコマンドらしい。サーバにて
- gitlabにあるリポジトリをサーバ側にsshでクローンしたい。いきなりクローンができないので、まずは
ssh-keygen
でキーペアを作る。3回質問されるが特に何もなければEnterを3回押すだけ。.ssh/id_rsa.pub
というキーができているはずなのでその中身をコピーしてgitlab上で公開鍵を記入するところ(設定とかからいけたはず)にペーストする。- サーバ上でdockerをインストールする。
sudo apt update
をまずやってsudo apt install docker.io
する。docker ps
できるか確認。権限がなかったのでsudo usermod -aG docker $USER
という呪文を唱えた。- ログアウトしてもう一度ログインして
docker ps
したら確認できた。sudo apt install docker-compose
でdocker-composeをインストール。docker-compose up
すれば起動するはず。落としたい時はdocker-compose down
またはdocker-compose stop
すれば良い。ちなみに、バックグラウンド実行したい時はdocker-compose up -d
でできる。まとめ
ローカルのdocker環境で動かしているWebアプリをAWSに移行した。AWSのサーバ内に入るまでの内容とサーバに入ってdockerを動かすまでの内容の話をした。おそらく他のやり方もあるので参考に。
- 投稿日:2020-09-14T15:14:16+09:00
Docker環境NuxtのaxiosモジュールでbaseURLの設定
問題 ページレンダリング後のaxiosだけ動かない...
まず、docker-composeをつかってnodeイメージでnuxt開発環境(frontend)とgoのAPIサーバー環境(app)を作りました。
これで、frontendコンテナからappでコンテナのアドレスを名前解決できるようになったはずです。そして、SSR: trueの環境でaxiosを実装しますが、
baseURLを設定しているのにbaseURLがlocalhost:3000(デフォルト)に勝手に変わってるのに気づきました。ページレンダリング前に呼びだしている(acyn fetch()で書いた)処理は正しく動いているのに、ページレンダリング後のmethodsに書いた$store.dispatchが動かないという現象に悩まされました。
axios: { baseURL: 'http://app:8080', proxy: true, }, proxy: { '/api': '/' },methods: { axiosFunc() { // こっち動かない404エラー const payload = {....} this.$store.dispatch('.....', payload) } }, async fetch({store}) { // こっち動く const payload = {....} await store.dispatch('.....', payload) }解決
若干、原因の予想はつきますが、やっぱりページレンダリングの前後でサーバーとの通信関係で変化しているようです。そして、下のブログをみつけました。
https://rara-world.com/nuxt-docker-404-api/
SSR(コンテナ)でaxiosを実行するfetch側はbaseURLで動いていて、methodsで定義した関数はブラウザ側で呼び出しているので、browserBaseURLを設定してエンドポイントを切り替える必要がありました。
また、ブラウザにはコンテナ名でアドレスをたどる能力がないようなので
net::ERR_NAME_NOT_RESOLVED各コンテナを共通networkにいれて、
browserBaseURL: "http://app:8080",と指定してあげると無事動かせました。
- 投稿日:2020-09-14T12:52:16+09:00
Spring Cloudでマイクロサービスを構成する(4):API Gateway編
Spring Cloudシリーズの第4回です。
概要
今回は「Spring Cloud Gateway」を使ってAPI Gatewayを立てることにします。
Spring Cloundファミリーでは従来、「Zuul」が使われていましたが、Zuulはメンテナンスモードとなり、「WebFlux&Nettyベースの」「Spring Gloud Gateway」が代替プロダクトとなっています。
API GatewayはAPIの前段に建てるリバースプロキシとして働きます。
API Gatewayの役割としては、クライアントとAPIの間に入り、API側に持ち込みたくない関心事を前段でさばくことにあります。下の図は、API Gateway向けの別プロダクト「KrakenD」のドキュメントから拝借しています。
フィルタリング、入出力の返還、APIの集約、ログや統計情報取得、認証回り(SSLを解いたり、トークンをチェックしたり付加したり)、Throtting(流量制御でいいのかな?)といったところが代表例です。
個人的には2つを重要視していて、SpirngベースのSpring Cloud Gatewayには期待しています。
- APIの前段に構えるので、パフォーマンスが良いこと。
- 並行してアクセス数を多くさばける必要あり。
- カスタマイズ性に富んでいること。
- API Gatewayに機能追加がしやすいこと。(やりたいことがシンプルにできる)
環境
以下が動く環境を前提とします。
- docker & docker-compose
- JDK11
前回までの投稿の続きですので、前回のゴール地点のソースを手元に用意し、それをベースとします。
作業ステップ
本記事では以下のステップで進めていきます。
- API Gatewayのひな型作成
- 実装(アノテーションの付加と設定ファイルの記述)
- Dockerイメージ化とdocker-componentへの追加
ステップ1:API Gatewayのひな型作成
Spring Initializr の出番です。
Artifact と Name だけ
gateway
に指定し、あとは
Dependencies にGateway
とEureka Client
、Spring Cloud LoadBalancer
、SpringBoot Actuator
を追加しましょう。
ActuatorはGatewayに必須ではありませんが、何かと必要になるものなので追加しておきます。
Dependencies 説明 Gateway Spring Cloud Gateway を導入するために追加 Eureka Discovery Client Eurekaからサービスのリストを取得するために追加 Spring Cloud Loadbalancer クライアントサイド・ロードバランスのライブラリを追加 Spring Boot Actuator Spring Boot の内部情報を参照するために追加 あとは
GENERATE
するとスケルトンのgateway.zipがダウンロードできます。
ステップ2:実装(アノテーションの付加と設定ファイルの記述)
今回もあまりやることがありません。2つだけ。
まず1つ目。
Applicationクラスにアノテーションを1つ付けます。
これでService Discoveryを参照できるようになります。gateway/src/main/java/com/example/gateway/GatewayApplication.javapackage com.example.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; // ★追加する @SpringBootApplication @EnableDiscoveryClient // ★追加する public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }やること2つ目。設定ファイル「application.yml」を書きます。
※src/main/resources/application.properties
は不要になるので、削除してください。gateway/src/main/resources/application.ymlserver: port: ${PORT:5000} spring: main: banner-mode: "off" application: name: gateway cloud: gateway: routes: - id: account uri: lb://ACCOUNT-API predicates: - Path=/api/** filters: - RewritePath=/api(?<segment>/?.*), $\{segment} loadbalancer: ribbon: enabled: false management: endpoints: web: exposure: include: "*" eureka: instance: prefer-ip-address: false client: registerWithEureka: false serviceUrl: defaultZone: ${DISCOVERY:http://localhost:8761/eureka}
Routes
の箇所がルーティングの定義です。
今回は1つだけRouteの定義をしており、以下を指定しています。
- ServiceDiscoveryから「ACCOUNT-API」サービスのインスタンスリストを取得し、負荷分散してリクエストする。(uri)
- リクエストされたパス「/api/xxx」 に対し、アクセス先パスを「/xxx」に書き換えてサービスへリクエストする。(predicates/filters)
また、
eureka.client.serviceUrl.defaultZone
には、Service Discovery(Eurekaサーバ)のURLを指定します。ステップ3:Dockerイメージ化とdocker-componentへの追加
Dockerイメージを作成してdocker-composeで操れるようにしましょう。
前回のディレクトリ構造に以下のようにgateway
ディレクトリを配置します。その上で、Dockerfileを作成し、とdocker-compose.ymlに追記します。
spring-msa ├── account │ └── ・・・ ├── db │ └── init ├── docker-compose.yml <-- ★追記する ├── gateyway <-- ★配置する │ └── Dockerfile <-- ★作成する └── sd └── ・・・discovery/DockerfileFROM openjdk:11-jdk-slim ADD target/gateway-*.jar /gateway.jar ENV CONTAINER_NAME=localhost \ PORT=5000 \ OPTS_ARGS='' ENTRYPOINT ["java", "-jar", "/gateway.jar", "${OPTS_ARGS}"]docker-compose.ymlversion: '3' ・・・ services: db: ・・・ adminer: ・・・ gateway: image: spring-msa/gateway container_name: gw build: context: ./gateway dockerfile: Dockerfile environment: - CONTAINER_NAME=gw - PORT=5000 - DISCOVERY=http://sd:3001/eureka/,http://sd2:3002/eureka/ ports: - "5000:5000" sd: ・・・ account: ・・・これで準備OK.
mvn wrapperでJARを作ったあとに、Dockerイメージを作成します。$ cd discovery $ .mvnw clean package $ cd .. $ docker-compose build gwこれでdocker-composeでgwを起動・停止できるようになりました。
(全サービスを起動) $ docker-compose up -d (GWの停止) $ docker-compose stop gw (全サービスの停止と破棄) $ docker-compose down (全サービスの停止とVolumeを含めた破棄) $ docker-compose down -vコンテナの5000ポートをホストの5000ポートにbindしているので、
http://localhost:5000/api/info でアクセスできます。下記のように繰り返し/api/infoを呼ぶと、Service Discoveryが返すサービスインスタンスのリストを使って
呼び先のACCOUNT-APIが負荷分散されているのがわかると思います。
組み込まれているロードバランサのデフォルトのアルゴリズムがRoundRobinなため、順番にリクエスト先が振られている状態です。$ curl localhost:5000/api/info {"containerName":"account","port":"9001","hostAddress":"172.18.0.9","hostName":"c1f0f98a3859"} $ curl localhost:5000/api/info {"containerName":"account2","port":"9002","hostAddress":"172.18.0.7","hostName":"27d40318d8bc"} $ curl localhost:5000/api/info {"containerName":"account3","port":"9003","hostAddress":"172.18.0.8","hostName":"7c2abf523dcf"} $ curl localhost:5000/api/info {"containerName":"account","port":"9001","hostAddress":"172.18.0.9","hostName":"c1f0f98a3859"} $ curl localhost:5000/api/info {"containerName":"account2","port":"9002","hostAddress":"172.18.0.7","hostName":"27d40318d8bc"} $ curl localhost:5000/api/info {"containerName":"account3","port":"9003","hostAddress":"172.18.0.8","hostName":"7c2abf523dcf"}何かがおかしければdokerのログをみてください。
$ docker-compose logs -f gwさいごに
ここまでのソースを以下に置いています。
https://github.com/SHIRAKI-Takayoshi/spring-msa/tree/add_gateway
ここまで来てようやく土台ができつつあります。
これからやってみたいこととしては、API Gatewayに機能追加を試すことです。
ロードバランスのアルゴリズムをカスタマイズしたり、APIに負荷をかけすぎないようにアクセス流量制御を追加してみたいところです。と、なると、
その前にもう少し準備をしておく必要があります。
- どこのAPIサービスにリクエストが行って1リクエストの中でどこにどれだけ時間がかかっているのか。
- リクエスト数はそれぞれのサービスでどれだけさばけているのか。
- そもそも負荷をかけたときにサービスはどのくらい余裕があるのか。
などなど、アクセスのトレースや、コンテナ/サービスのモニタリングをができる状態で負荷テストツールで負荷をかけられるようにしておきたいところです。
次回からはその辺を進めていきたいと思います。
ではでは。
- 投稿日:2020-09-14T12:18:37+09:00
Spring Cloudでマイクロサービスを構成する(2):REST API(WebFluxとR2DBC)編
概要
今回はSpring BootでREEST APIサービスのサンプルをさらっと作ります。
単純にSpring Boot でREST APIを作るだけだと面白くないので、以下を試すことにしましょう。
- Spring WebFlux を使ったCRUD APIを作る
- Spring Data R2DBC で PostgreSQLにつなぐ
Springでリアクティブプログラミングに対応したWebFluxがSpring5で追加されました。
Spring Bootでもバージョン2系からWebFluxを使用することができます。SpringでノンブロッキングAPIを使っている以上、DBへのアクセスも、せっかくなのでノンブロッキングでやれると良さげです。
今回は通常のJDBCではなく、ノンブロッキングをサポートしているR2DBCを使ってPostgreSQLにアクセスしてみようと思います。なぜWebFlux??ノンブロッキング?? という人には、以下の記事が参考になります。
環境
以下が動く環境を前提とします。
- docker & docker-compose
- JDK11
- なんらかのエディタ、IDE
また、前回の記事 で作成したDB環境(PostgreSQLと管理WebのAdminer)を使いますので、
前回の記事で作成したものに、ファイルを追加していきます。作業ステップ
本記事では以下のステップで進めていきます。
- Spring Bootアプリの雛形作成
- APIの実装(Entity, Repository, Controller)
- APIのDockerイメージ化
ステップ1:Spring Bootアプリの雛形作成
さて次は、Spring Initializr でSpring Bootプロジェクトのスケルトンを作ります。
今回は、左側のパネルでArtifactとNameだけ「account」に変更しました。
そして、右側のパネルで以下のDependencyを指定します。
Dependencies 説明 Spring Reactive Web Spring WebFlux & Netty Spring Data R2DBC ノンブロッキングでDBアクセスするための機構 ProstgreSQL Driver PostgreSQL用のR2DBC Driver & JDBC Driver Lombok Getter/Setterなどを自動生成してくれる便利なやつ Spring Boot Actuator Spring Boot の内部情報をチェックするために追加 この状態で
GENERATE
ボタンを押すとスケルトン(account.zip)がダウンロードできます。ステップ2:APIの実装(Entity, Repository, Controller)
ステップ1でダウンロードしたZipを展開し、前回作成したdbディレクトリの隣にaccountディレクトリを配置します。
以下のディレクトリ構成になるようにファイル配置し、追加のファイルを作っていきます。
account/src/main/resources/application.properties
は不要になるので削除してください。spring-msa ├── account │ ├── Dockerfile <-- ★ステップ3で作成する │ ├── HELP.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── account │ │ │ ├── AccountApplication.java │ │ │ ├── controller │ │ │ │ ├── AccountController.java <-- ★作成する │ │ │ │ └── InfoController.java <-- ★ステップ3で作成する │ │ │ ├── entity │ │ │ │ └── Account.java <-- ★作成する │ │ │ └── repository │ │ │ └── AccountRepository.java <-- ★作成する │ │ └── resources │ │ ├── application.properties <-- ★削除する │ │ └── application.yml <-- ★作成する │ └── test │ └── java │ └── com │ └── example │ └── account │ └── AccountApplicationTests.java ├── db <-- 前回作成したもの │ └── init │ ├── 01_create_table.sql │ └── 02_insert_data.sql └── docker-compose.yml <-- ★前回作ったものに対し、ステップ3で追記するまずはEntityです。accountテーブルのカラムに合わせます。
Lombokを使うことによってgetter/setterなどは省略しています。account/src/main/java/com/example/account/entity/Account.javapackage com.example.account.entity; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; import java.time.LocalDateTime; @Data @NoArgsConstructor @AllArgsConstructor @Table("account") public class Account { @Id private Long id; private String uname; private String token; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") private LocalDateTime createdAt; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") private LocalDateTime updatedAt; }次に、Repositoryです。DBアクセスをするクラスです。
ここでは、Spring Data R2DBCに標準のCRUDを自動生成するインタフェースReactiveCrudRepository
が用意されているので、extendsするだけです。
(追加で必要になるメソッドはここに追加していきます)account/src/main/java/com/example/account/repository/AccountRepository.javapackage com.example.account.repository; import com.example.account.entity.Account; import org.springframework.data.repository.reactive.ReactiveCrudRepository; public interface AccountRepository extends ReactiveCrudRepository<Account, Long> { }次はControllerです。リクエストを受け付けてレスポンスを返すクラスです。
標準的なCRUDサンプルとして以下のようにします。
Flex
やMono
が出てくるのがWebFlux流ですね。account/src/main/java/com/example/account/contoroller/AccountController.javapackage com.example.account.controller; import com.example.account.entity.Account; import com.example.account.repository.AccountRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController() @RequestMapping(value = "/account") @RequiredArgsConstructor class AccountController { @Autowired private final AccountRepository accountRepository; @GetMapping("") public Flux<Account> all() { return this.accountRepository.findAll(); } @PostMapping("") public Mono<Account> create(@RequestBody Account account) { return this.accountRepository.save(account); } @GetMapping("/{id}") public Mono<Account> get(@PathVariable("id") Long id) { return this.accountRepository.findById(id); } @PutMapping("/{id}") public Mono<Account> update(@PathVariable("id") Long id, @RequestBody Account account) { return this.accountRepository.findById(id) .map(p -> { p.setUname(account.getUname()); p.setToken(account.getToken()); return p; }) .flatMap(p -> this.accountRepository.save(p)); } @DeleteMapping("/{id}") public Mono<Void> delete(@PathVariable("id") Long id) { return this.accountRepository.deleteById(id); } }さて、次はSpring Boot の設定ファイルです。
なるべく環境変数でアプリの外側から上書きできるようにしつつ、デフォルト値を設定しています。account/src/main/resources/application.ymlapp: container-name: ${CONTAINER_NAME:account} server: port: ${PORT:9001} spring: main: banner-mode: "off" application: name: account-api r2dbc: url: ${DB_URL:r2dbc:postgresql://localhost:5432/db} username: ${DB_USER:postgres} password: ${DB_PASSWD:postgres} management: endpoints: web: exposure: include: "*" endpoint: health: show-details: alwaysさてここまで来たら、docker-composeでDBを起動してから、
ローカルのmaven wrapperでSpring Boot アプリを起動しましょう。$ docker-compose up -d db adminer $ cd account $ ./mvnw clean springboot-runそしておもむろにWebブラウザで
http://localhost:9001/account
にアクセスします。DBに入っている3件がJSONで返却されましたか?
(Chromeを使っている人は、拡張機能「JSON Viewer」を入れておくと、↓のような感じでJSONが整形されて幸せになれます。フィルタ機能もあってデータ確認が楽チン!)curlでのCRUDリクエストはこんな感じです。
# show account list curl -v -H "Content-Type: application/json" http://localhost:9001/account # show id=1 curl -v -H "Content-Type: application/json" http://localhost:9001/account/1 # add new account curl -v -X POST -H "Content-Type: application/json" -d '{"uname":"hoge", "token":"XXX"}' http://localhost:9001/account # update id=1 curl -v -X PUT -H "Content-Type: application/json" -d '{"uname":"foo", "token":"YYYY"}' http://localhost:9001/account/1 # delete id=2 curl -v -X DELETE -H "Content-Type: application/json" http://localhost:9001/account/2ステップ3:APIのDockerイメージ化
さて、作ったSpring Boot アプリをDockerコンテナで動かせるようにしましょう。
その前に、docker化には関係ありませんが、後々のためにContorollerを1つ足しておきます。
http://<host>:<port>
とhttp://<host>:<port>/info
のパスに対してで以下の情報をJSONで返します。
- コンテナ名(コンテナ名はSpringは知らないので、環境変数経由でプロパティから取得しています。)
- ポート番号
- IPアドレス
- ホスト名
account/src/main/java/com/example/account/contoroller/InfoController.javapackage com.example.account.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.LinkedHashMap; @RestController @RequestMapping("") public class InfoController { @Autowired Environment environment; @GetMapping public Mono<LinkedHashMap<String, String>> index() throws UnknownHostException { return info(); } @GetMapping("/info") public Mono<LinkedHashMap<String, String>> info() throws UnknownHostException { InetAddress localhost = InetAddress.getLocalHost(); LinkedHashMap<String, String> response = new LinkedHashMap<>(); response.put("containerName", environment.getProperty("app.container-name")); response.put("port", environment.getProperty("server.port")); response.put("hostAddress", localhost.getHostAddress()); response.put("hostName", localhost.getHostName()); return Mono.just(response); } }次に、accountディレクトリ直下にDockerfileを作りましょう。
注)Spring Boot 2.3系からDockerfileなしにDockerイメージを作れる機能が追加されましたが、この連載では扱いません。
DockerfileFROM openjdk:11-jdk-slim ADD target/account-*.jar account.jar ENV CONTAINER_NAME=account \ PORT=9001 \ DB_NAME=db \ DB_USER=postgres \ DB_PASSWD=postgres \ OPTS_ARGS='' ENTRYPOINT ["java", "-jar", "/account.jar", "${OPTS_ARGS}"]その上でdocker-compose.ymlにも追記します。
docker-compose.ymlversion: '3' ・・・ services: db: ・・・ admine: ・・・ account: image: spring-msa/account-api container_name: account build: context: ./account dockerfile: Dockerfile ports: - "9001:9001" depends_on: - db environment: - CONTAINER_NAME=account - PORT=9001 - DB_USER=${DB_USER:-postgres} - DB_PASSWD=${DB_PASSWORD:-postgres} - DB_URL=${DB_URL:-r2dbc:postgresql://db:5432/db} networks: - defaultこれで以下のコマンドでaccountアプリを起動させることができます。
db(postgresql) と adminerも一緒に起動しましょう$ cd account $ ./mvnw clean package $ cd .. $ docker-compose build account $ docker-compose up -d db adminer accounthttp://localhost:9001/account にアクセスしてDBの内容が返ってくればOKです。
(jq
を入れておくとJSONの整形ができて便利です。)$ curl localhost:9001/account | jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 350 0 350 0 0 15909 0 --:--:-- --:--:-- --:--:-- 15909 [ { "id": 1, "uname": "sato", "token": "AAAA", "createdAt": "2020-09-11T06:02:51.064", "updatedAt": "2020-09-11T06:02:51.064" }, { "id": 2, "uname": "yamada", "token": "BBBB", "createdAt": "2020-09-11T06:02:51.082", "updatedAt": "2020-09-11T06:02:51.082" }, { "id": 3, "uname": "suzuki", "token": "CCCC", "createdAt": "2020-09-11T06:02:51.091", "updatedAt": "2020-09-11T06:02:51.091" } ]http://localhost:9001/info も叩いてみましょう。
$ curl localhost:9001/info | jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 94 100 94 0 0 15666 0 --:--:-- --:--:-- --:--:-- 15666 { "containerName": "account", "port": "9001", "hostAddress": "172.18.0.9", "hostName": "c1f0f98a3859" }
さいごに
とりあえず、JSONを返すREST APIができました。
今回の完成イメージのソースをここに置いておきます。
https://github.com/SHIRAKI-Takayoshi/spring-msa/tree/setup_db_api次回からは「Service Discovery」や「API Gateway」を作っていきます。
ではでは。
- 投稿日:2020-09-14T11:40:50+09:00
常駐プロセスが無いdockerコンテナを起動させたままにする
docker run -itd ubuntu:latest /bin/bash
- 投稿日:2020-09-14T10:37:39+09:00
ローカルのサーバを立ち上げてHTMLを開く
背景 ローカルで以下のようなhtmlファイルを作成 iframe.html <html> <head> </head> <body> <iframe src="iframe_content.html" width="200" height="300" id="iframe-01"></iframe> </body> </html> iframe_content.html <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> </head> <body> <h1>iframe内</h1> <div class="iframe-width"></div> <script> const win = window.window; $('.iframe-width').text('iframeの幅 : ' + win.innerWidth + 'px'); console.log(win.parent); </script> </body> </html> そしてファイルパスを使って(/Users/.../iframe.html)アクセスしたところ,以下のようなエラーがコンソールで出てしまった. Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame. 調べてみたところ,ローカルでサーバを立てて動かせばこのエラーが出ないっぽいので,やってみることにしました.(参考) Dockerでnginxを起動 環境 OS : macOS ブラウザ : Google Chrome(バージョン85) ディレクトリ構成 * ├- Dockerfile └- sample_html/ ├- iframe.html └- iframe_content.html Dockerfile作成 nginxのイメージを基に作成します. sample_html 配下のフォルダを表示対象とします. Dockerfile FROM nginx COPY sample_html /usr/share/nginx/html docker imageの作成 sample というイメージ名で 1.0 というタグをつけて,カレントディレクトリにあるDockerfileを元にイメージを作成 $ docker build -t sample:1.0 . dockerコンテナの生成・起動 バックグラウンドで sample というイメージ名で 1.0 というタグがついているdockerイメージを起動 $ docker run -d -p 8080:80 sample:1.0 docker run のオプションについて -p : ホストのポート:コンテナのポート ポート番号の紐付けを宣言 アクセス確認 http://localhost:8080/iframe.html にアクセス すると,コンソールにエラーが表示されずにiframeの親の情報を取得することができる. 参考 とりあえずhtmlの表示をDocker使って行う[nginxバージョン] Docker入門 Nginxを動かしてみる
- 投稿日:2020-09-14T10:03:58+09:00
ESETのファイアウォールが動作しているとDockerで立ち上げたlocalhostのページがブラウザで確認できない問題
目的
- ESETというセキュリティソフトを導入し、Dockerので起動しているlocalhostのページをブラウザで表示しようとした時に表示されない問題を解決する。
免責事項
- 本作業ではセキュリティソフトの設定を変更します。若干デリケートな作業になりますので慎重に作業をしましょう。
- 会社から貸与されているPCで本作業を実施する場合、管理者に相談の上実施することを強くオススメします。
- 本作業を実施して皆様のPCにいかなる損失が合った場合でも筆者は責任を負いかねます。
- 本記事の最後に記載されている参考文献のリンク先も是非ご確認いただきたいです。
前提情報
- 筆者の先輩が書いてくださった記事を参考に環境構築を行ったDockerでnginxのローカルサーバを起動する。
- ESETのパーソナルファイアウォール「有効」にした状態で、Google Chromeにて下記にアクセスしLaravelのトップページが表示されたら作業完了とする。
問題
- ESETのパーソナルファイアウォールが「有効」状態でDockerのコンテナを起動しブラウザでhttp://localhost:8080にアクセスしてもLaravelのトップページが表示されない。
方法
- ESETのアプリを起動する。
「設定」をクリックする。
「パーソナルファイアウォール」をクリックする。
「設定」をクリックする。
「フィルタリングモード」の「自動モード」を「対話モード」に変更する。
現在通信中のトラフィックを許可するかのウインドウが出るので怪しい通信ではないことを確認して「アクションを記憶する(ルールを作成する)」を選択して「許可」をクリックする。このウインドウは今行われいてる内向きのトラフィックと外向きのトラフィックすべてに対して表示されるのでウインドウが出なくなるまで前述した「許可」の作業か「拒否」の作業を実施する。
Dockerのコンテナを起動する。
ブラウザにて下記にアクセスする。
先に実施した様にトラフィックの許可を求められるので内容を確認し、「アクションを記憶する(ルールを作成する)」を選択して「許可」をクリックする。
http://localhost:8080にてLaravelのトップページが表示されることを確認する。
「フィルタリングモード」の「対話モード」を「自動モード」に変更する。
参考文献
- 投稿日:2020-09-14T09:34:36+09:00
`docker-compose up`って何?
はじめに
Dockerに慣れ親しんできて、「そろそろ復数のDockerイメージ・コンテナを使って開発するかー」というレベルの方は多いかと思います。
私もその一人なのですが、いざリファレンスを読みながら使ってみても上手く扱えずに開発が滞ってしまうことがありました。
今回は、そのなかでも一番基本的で、かつわからないと結構ドツボにハマる
docker-compose up
についての解説を書いてみました。自身も初学者ですので、もし誤りがあればご指摘いただけると助かります。
前提
今回はコマンドの説明のために、1つのコンテナを起動させるシンプルなプロジェクトを想定してみます。
ディレクトリ構成
. ├── Dockerfile ├── docker-compose.yml ├── file.txt └── file2.txt
Dockerfile、docker-compose.ymlの内容
FROM ubuntu:18.04 WORKDIR work ADD ./file.txt /work/file.txtdocker-compose.ymlversion: '3.7' services: test: build: . image: my-alpine-image container_name: my-aplineとりあえず"up"してみよう
上記の構成で
docker-compose up
をしてみると何が起こるか確認しましょう。$ docker-compose up Building test // Stepは省略 Successfully built ff8e7d96a995 Successfully tagged my-alpine-image:latest WARNING: Image for service test was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating my-apline ... done Attaching to my-apline my-apline exited with code 0ここで重要なのは次の2点です。
my-alpine-image
というイメージを作成しているmy-alpine
というコンテナを作成しているつまりは
docker run
と同じです。実際にイメージやコンテナを確認してみます。$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6d14fde119f0 my-alpine-image "/bin/bash" 10 minutes ago Exited (0) 10 minutes ago my-apline$ REPOSITORY TAG IMAGE ID CREATED SIZE my-alpine-image latest ff8e7d96a995 10 minutes ago 64.2MB
復数コンテナであってもこれは変わらないので、ここで「なるほど、DockerComposeは理解した」となり、このあとの勘違いにつながります。
Dockerfileを修正してもう一度upしてみる
つぎにDockerfileを修正して、test2.txtもコンテナに含めるようにしましょう。
FROM ubuntu:18.04 WORKDIR work ADD ./file.txt /work/file.txt ADD ./file2.txt /work/file2.txt CMD ["/bin/bash"]さて、
docker-compose up
を実行してみます。すると次のような結果になりました。$ docker-compose up Starting my-apline ... done Attaching to my-apline my-apline exited with code 0このときコンテナ(my-apline)にはtest2.txtは含まれません。
なぜなら今回の
docker-compose up
は、さきほど作成したコンテナをただ起動しただけだからです。その証拠に、コンテナとイメージを確認しても変わりがないことが見てとれます。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6d14fde119f0 my-alpine-image "/bin/bash" 10 minutes ago Exited (0) 10 minutes ago my-apline$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-alpine-image latest ff8e7d96a995 10 minutes ago 64.2MB
どのように
up
すべきだったかでは、どうすればtest2.txtを含んだ、最新のイメージを使ったコンテナになるのでしょうか。
正解は、「再度ビルドする」ことを明示する
--build
オプションを使うことです。$ docker-compose up --build Building test // Stepは省略 Successfully built 7218cdd4f17a Successfully tagged my-alpine-image:latest Recreating my-apline ... done Attaching to my-apline my-apline exited with code 0イメージもコンテナも新しくなっていることがわかります。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 28c26826bf20 my-alpine-image "/bin/bash" 2 minutes ago Exited (0) 2 minutes ago my-apline$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-alpine-image latest 7218cdd4f17a About a minute ago 64.2MB
まとめ
まとめると、
docker-compose up
というコマンドは、
- まだイメージが作成されていなければ、イメージを作成して、さらにコンテナを作成・起動する
- すでにコンテナが存在すれば、イメージ・コンテナの再作成は行わず、(停止中の)コンテナを起動するだけ
ということがわかりました。
ちなみにイメージがない状態で
docker-compose up
するとコンソールに、Image for service test was built because it did not already exist. To rebuild this image you must use
docker-compose build
ordocker-compose up --build
.
(サービステスト用のイメージが存在しなかったためにビルドされました。このイメージを再構築するにはdocker-compose build
かdocker-compose up --build
を使う必要があります。)と出ていたことに気づきました。英語をちゃんと読めばこんなに迷う必要はなかったですね。
似たような箇所でつまづいた人たちの参考になれば幸いです!
- 投稿日:2020-09-14T00:24:25+09:00
VSCodeでDocker入門
対象
Dockerインストール済み
初めてDockerを使用する。前提
コマンドラインは使える
VSCode インストール済み本記事について
一般的なのDockerの学習フローではdockerコマンドを学びつつ、イメージやコンテナの概念について理解し、その後docker-composeへと進んでいくと考える。
本記事ではイメージやコンテナ等の概念の説明は大きく省き、コマンドもvscodeの拡張機能で代用する。それによってdocker-composeのコマンドの最小限のみ学んでさくっと動かすことで実践的にDockerについて理解していこうというもの。
本記事のゴール
docker-composeコマンドがいくつかつかえるようになる。
nodejsの実行環境が作れる様になる。VSCode環境構築
これをあらかじめインストールしておく。
nodejs環境を作る
まずは適当な場所に
nodejssample
というフォルダを作る。この名前は適当。そしてそのフォルダ直下に以下のように
docker-compose.yml
というファイルを作る。docker-compose.ymlversion: "3" services: node: image: node:14.10 volumes: - .:/project tty: true working_dir: /project command: bashこの内容については後ほど解説する。
そして、同階層に
app.js
を作成app.jsconst main = () => { console.log("hello node!"); } main();こうなっていればOK
そうしたら一度Dockerの現在の状態を確認しておく。
VSCode左からDockerマークを選択すると以下のような画面になってると思う。
Dockerが起動していなかったり、すでに何かしらDockerを動かしていたらこの画面にならないかもしれない。
確認できたら、コマンドラインでdocker-compose.ymlのある場所で以下のようにコマンドをうつ。
仮装環境を起動するコマンド
docker-compose up -dするとダウンロードが始まって以下のような画面になる
nodejssample $ docker-compose up -d Creating network "nodejssample_default" with the default driver 4f250268ed6a: Pull complete1b49aa113642: Pull complete c159512f4cc2: Pull complete8439168fd8dc: Pull complete55abbc6cc158: Pull completee5c5821cd889: Pull complete5a7679f70bad: Pull completed827e86d1182: Pull complete2484b06a6da1: Pull complete Digest: sha256:21658666c0eabc9006b279e826e540d20e2c835507347d9c2f3f7dd5820ec9e3 Status: Downloaded newer image for node:14.10 Creating nodejssample_node_1 ... doneそして、Dockerのタブの画面が以下のようになる。
Dockerで作った仮想環境のことをコンテナと呼ぶ。
イメージはコンテナ作るためのもの。仮想環境が起動できたので、次はその仮想環境でnodejsを動かす。
対象のコンテナを右クリック > Attack Shellを押す。
すると以下の様な画面がでる。root@df80b994d93d:/project# ls app.js docker-compose.yml root@df80b994d93d:/project#lsコマンドをうつと中にVSCodeのプロジェクトと同じファイルが表示される。
そして、ここはnodejs環境のコンテナなのでnodejsが使えるかどうか確かめる。root@df80b994d93d:/project# node -v v14.10.1これが出たらOK
あらかじめ用意しておいた
app.js
を実行する。root@df80b994d93d:/project# node app.js hello node!実行できた。
仮想環境を消す。
仮想環境は放置しておくと重いので、作業が終わったら消す。
イメージを消す。
イメージの方は、これがあることによって
docker-compose up -d
が早くなるため、頻繁に使うなら残しておいてもOK.しばらく使わないなら消す。
これで仮想環境の構築、仮想環境でのプログラムの実行、仮想環境の削除が一通りできた。
解説
docker-compose.ymlについて
docker-composeの基本的な構文は以下
docker-composeの構文version: バージョン番号。 services: サービス名: サービスの設定...versionは最新版が3
サービス名は自分でつける。なんでもOK。docker-compose.ymlversion: "3" # 最新版は3 services: # ここは固定 node: # なんでも良いが、nodeのイメージを使ってるのでそのままnodeというサービス名に。 image: node:14.10 volumes: - .:/project tty: true working_dir: /project command: bashサービスの設定は多くあるが、詳しく知るためには公式ドキュメントを読むのが一番良い。
今回使った設定項目について1つずつ解説していく。
image
コンテナを作るためのイメージを記載。image: イメージ名:タグ名タグ名はバージョンととらえてOK
Dockerで使うイメージはDocker Hubというサイトにいろいろある。
nodeイメージ本記事で使ったタグはこれ
volumes
ローカルのフォルダとコンテナのフォルダをリンクさせる。volumes: - ローカルのフォルダのパス:コンテナのフォルダのパスこれを設定しておかないと、コンテナから
app.js
等のローカルに配置されたファイルをみることができない。tty
tty: truecommandで指定した処理を行なった後すぐコンテナが停止しないようにする。
Attach Shell
でコンテナに入る時は必須。working_dir
そのまんまだが、コマンドを実行するときの場所。Attach Shell
を行ったときもここで指定したフォルダに入る。command
コンテナ起動時に実行する処理。
今回はbashを指定してるので、ターミナル開いて待っててっていう感じ。他にもいろんな設定があるので別のDockerの記事をみて知らない項目があったらリファレンスを読むと良いと思う。
docker-compose up -dについて
docker-compose.yml
を読んで、必要であればイメージをダウンロードしてコンテナを作成するコマンド。-dはバックグラウンドで起動するためのオプション。
- 投稿日:2020-09-14T00:07:43+09:00
ChromebookにJupyterとDockerComposeを入れて、軽い開発環境として使ってみよう!
前書き
Chromebookは、ブラウザのみ、ネット利用前提のマシン、
と思われがちであるが、
実は簡単なLinuxマシンとして使うことも出来る。
- ブラウジングやメール、Officeなど(本来のChromeBook機能)
- Androidタブレットとして(GooglePlayのアプリが使える)
- Linuxのミニ・開発環境として
といった3つの使い方が出来て、
非常に軽く、早く。安く、電池も持つ端末として、
サブ機として超オススメと言える。本稿では、Linuxのミニ・開発環境として、
Chromebookの基本的な使い方(開発向け)から始まり、
①ローカルJupyterNotebookとしてのPython環境
②Docker-Composeを使えるコンテナ実行環境
としての構築手順とコマンド、
そしてその後の疎通用コードをゼロから記載しておく、
超初心者用の環境構築 ALL-IN-ONE メモである。
Chromebookで本格的にプログラミングをしたい、という人にもオススメ。
(2020/09 初稿作成)前提情報
使用ハードウェア
HP ノートパソコン クロームブック HP Chromebook x360 12b 12インチ
2in1 コンバーチブルタイプ インテル® Pentium 4GB 64GB eMMC
日本語キーボード (型番:8MD65PA-AAAC)
(更新期限:2026年)HP Chromebook x360 12b の「推し」について
- タッチスクリーン&360°回転でAndroidタブレットとして使えること
- キーボードが本格的でクライムシェル型で使えること
- 上記2条件を満たす中ではかなり軽量であること
- 少し縦に長めのスクリーンが情報表示的に良い
- 左右のUSBTypeCのどちらからも給電可能なのが地味に便利
- USBTypeB、SDカード、イヤフォンジャック、があること
- キーボードバックライト、打鍵感も十分良い
- ディスプレイの発色、タッチも良い。電池の持ちも良い
- 通気口/ファンが無く静か、膝の上にもおける
- 総じて、値段の割にバランスが良く、サブ機として使いやすい
なお、小型系でAndroidタブレット的な使い方がメインであれば、
Lenovo IdeaPad Duet Chromebook
もオススメではあるが、キーボードがカバー型であるため
クライムシェル型での利用がいまいちであり、開発系には向かない。
また、USBTYpeB、SDカード、イヤフォンジャックが無い、なども、
拡張性の観点からデメリットに感じられ、候補から除外した。逆にAndroidタブレット的な使い方をしない、ならば、
14インチ型など多少大きめのものだと、性能も選択肢の幅が広がる。個人の感想ではあるが、Chromebookの良いところは、
タブレットと同じような感覚で、持ち運びも起動も「軽い」ことと感じる。
メインとして使うならば14インチ型が良く、サブとして使うならば、
室内であっても持ち運びが便利&多用途という点で12インチ以下タッチ有り、
がオススメである。※購入までに相当逡巡し、数カ月の比較に比較を重ねた結果、
現行ではこれ1点、と確信するに至り本機を購入した。
概ね期待通りかそれ以上の動きをしてくれている。システムの詳細情報の出し方
ブラウザ上で下記のようにアクセス
URL欄に以下を入力chrome://system/結果(一部抜粋)CHROMEOS_RELEASE_CHROME_MILESTONE 84 CHROMEOS_RELEASE_VERSION 13099.102.0 CHROME VERSION 84.0.4147.127ChromeBook自体の操作方法。事前実行すると良いこと
個人的おすすめショートカット
「Ctrl + Alt + /」
キーボードショートカットの一覧を出す「Ctrl + W」
現在のWindowを閉じる「Ctrl + Shift + V」
クリップボードの内容をプレーンテキストとして貼り付け
※特にオススメ・便利「Ctrl + Window表示キー(□||)」
画面キャプチャを取る「alt + @ 」「alt +「 」
Windowを画面の左半分(or右半分)の固定サイズにする「alt+^」
Windowを画面の全体にする・通常サイズにする、の切り替えAndroidアプリについて
GooglePlayから普通にインストール出来る。
対応しているものは表示される(基本的にほとんどのものが対応している)
全機能がfullで動くとは限らない。
ネットワーク周りの機能は少し独特な模様。
この機能が便利すぎるので、
タッチパッド使用可能&タブレット形態可能、な機種を選ぶべし。
Chrome拡張機能などもそうだが、Androidアプリについても、
ChromeBookのアプリたちと平行する形でショートカットが追加される。メモ帳・テキストエディタ
とりあえず一番ベーシックかつシンタックスハイライトが効くものを入れておくと吉
「Text」というエディタアプリを入れておこう。
https://chrome.google.com/webstore/detail/text/mmfbcljfglbokpmkimbfghdkjmjhdgbgVisualStudioCodeなどを入れる方法もあるが、
とにかく「簡単・軽快」なのでまずコレで十分。ファイル・フォルダ構成
エクスプローラー相当の「ファイル」というアプリから
「ダウンロード」したファイル(ローカルのファイル)、
「Linux(β)」のファイル(後述のターミナルのマウント)、
「Playファイル」≒ Androidとしてのファイル、
及びGoogleDriveに、透過的にアクセス可能。
ローカルで完結するファイルやアプリも多く、
Web・ネットワークが無いと無いも出来ないというわけではない。
Linux(β)機能と、Python環境の構築
Linux(Debian stretch)のターミナルを動作させる機能。
ChromeOS ver.69 以降で導入された。
- 設定画面を開く(右下の時計など⇒歯車のアイコン)
- 設定画面の下のほうの、Linux(ベータ版)という項目をオンにする
- ターミナルがインストールされ、ランチャにも登録される
ファイル・フォルダの共有
- Linux側からは、/mnt/chromeos がchromeOS側のマウント
- chromeOS側からは、標準の「ファイル」アプリで見れる。
- マイファイル>Linuxファイル = home/ユーザ名
コピペ(クリップボード共有)
Linux側とchromeOS側はクリップボードの共有がなされているが、
例えばコマンドをchromeOS側のブラウザで調べて、
Pasteしようというときに、ショートカットが違うので注意が必要。
chromeOS側で、「Ctrl+C」しておき、「Ctrl+V」ではだめ。
Linuxターミナル側のお作法で、「Ctrl + Shift(⇧) + V」で貼り付く。
もちろん、Jupyterなどを起動後は
ブラウザ上でコードを書く形になるので「Ctrl+V」で良い。Linux環境全体のバックアップと復元
意外と情報が少ない気がするけど、知っておくと良いのは、
このlinux環境を「公式」の手順でまるごとバックアップ出来ること。
chromebook公式のヘルプ
https://support.google.com/chromebook/answer/9592813?hl=ja
GoogleDriveなどに直接バックアップを保存しておけば安心出来ますね。ちなみに、本稿の手順全て実行してHackMDのDockerイメージがある状態で
1.2GB程度の容量になった。ベースではおそらく300MB〜400MB程度か。ターミナル全体のバージョン確認
# ターミナルのバージョンは下記で確認 cat /etc/debian_version > 10.1なお、ターミナルが立ち上がっている状態で
「Ctrl + Shift + p」で設定画面が表示される。
テーマの色替えなどが出来る。Python3のバージョン確認
# Python2は入っておらず、Python3が入っている。 # pip/pip3は入っていない。 python -V > -bash: python: command not found python3 -V > Python 3.7.3 which pip > ※無しpip のインストール
# 以下のコマンドでインストーラをダウンロード curl -O https://bootstrap.pypa.io/get-pip.py # 重要:distuils.utilを事前にインストールしないと以下のエラーになる。 # ModuleNotFoundError: No module named 'distutils.util' sudo apt-get install python3-distutils # get-pipを実行してpipを導入する sudo python3 get-pip.py # pipが入ったことを確認する which pip > /usr/local/bin/pip pip -V > pip 20.2.2 from /usr/local/lib/python3.7/dist-packages/pip (python 3.7)Matplotlibなどライブラリ群のインストール
# 一発で導入できた。 sudo pip install matplotlib # 日本語豆腐対応モジュールも合わせていれておく。 sudo pip install japanize-matplotlib # pandasも入れておく sudo pip install pandas # seabornも入れておく sudo pip install seabornJupyterNotebookのインストールと起動
# インストール sudo pip install jupyter # 起動(※chromeOS側でブラウザが自動起動) jupyter notebookターミナル側とchromeOS側とでポートフォワーディングなど
設定しないといけないかと思ってたけど
直通しているようなので楽で便利!!JupyterNotebook上で実行するサンプルコード
立ち上がったブラウザ上でNotebookを新規作成し、
以下のコードを実行して疎通確認が出来る。pythonの確認用コード##Jupyter上でグラフをインライン表示する設定 %matplotlib inline #### 必要なライブラリの利用を宣言 ## グラフ描画ライブラリ import matplotlib.pyplot as plt ## 算術計算ライブラリ import numpy as np ##matplotlib を日本語表示対応するライブラリ import japanize_matplotlib #### 以下で実行 # グラフを描画 x = np.arange(0, 20, 0.1) y = np.cos(x) #プロット plt.plot(x,y) #ラベル名をつける plt.xlabel('コサインのグラフ') #表示 plt.show()わざわざChromebookでJupyter立ち上げなくても、
Colaboratoryでいいじゃん、という話も十分あるのだが、
ローカルで軽度に試せる場所があるというのは
それはそれで利便性が高い。
Dockerの利用
Dockerのインストール
Docker公式サイトのdebianにインストールする手順をそのまま使う
https://docs.docker.com/engine/install/debian/公式サイトを参照しながら「Ctrl + Shift(⇧) + V」で
コマンドをコピペしていけばだいたいOK。旧バージョンがある場合のアンインストール
sudo apt-get remove docker docker-engine docker.io containerd runc依存パッケージのインストール
sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-commonDocker公式のGPGkeyの追加
GPGとは、インストラーが本物かどうか、
検証するために用いられる暗号化の仕組み。curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - sudo apt-key fingerprint 0EBFCD88Dockerの安定版(stable)repositoryの追加
一応CPUのアーキテクチャ情報を確認し、x86_64の手順を参照して実行
lscpu #出力 ⇒ Architecture: x86_64sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/debian \ $(lsb_release -cs) \ stable"Docker本体のインストール
sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io※公式サイトを参照しての実施はここまで。
インストールの確認と疎通
インストール出来たことの確認
docker version # 出力 ⇒ # Client: Docker Engine - Community # Version: 19.03.12 # API version: 1.40 # Go version: go1.13.10 # Git commit: 48a66213fe # Built: Mon Jun 22 15:45:50 2020 # OS/Arch: linux/amd64 # Experimental: false疎通実行
--rm オプションをつけると
コンテナ終了時に自動的に削除出来るので、
終了したコンテナが貯まらずに済む。sudo docker run --rm hello-world # 出力(一部) ⇒ # Hello from Docker! # This message shows that your installation appears to be working correctly.Docker-Composeの利用
Docker-Composeのインストール
同様に公式サイトのlinuxのインストール手順を参照して行う。
https://docs.docker.com/compose/install/最新の安定版のダウンロード
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose実行権限の付与
sudo chmod +x /usr/local/bin/docker-composeインストールされたバージョン確認
docker-compose --version # 出力 ⇒ docker-compose version 1.27.1, build 1110ad01Docker-Composeの実用:HackMDの利用
同時利用可能のMarkdownエディタ:HackMDのサーバを
Docker-Composeを使って一瞬で立ててみる。# gitは最初から入っている模様 git clone https://github.com/hackmdio/docker-hackmd.git cd docker-hackmd # 早速起動する sudo docker-compose up # 出力 ⇒ 末尾に以下の情報がでれば、3000番ポートで起動出来ている # info: HTTP Server listening at port 3000HackMDにアクセス
chromeブラウザを立ち上げて、以下にアクセス。
http://localhost:3000/chromebookのローカル環境でHackMD(同時利用可能Markdownエディタ)
のDockerイメージが簡単に立ち上がって使えるようになった。HackMD自体、Markdownの編集ツールとして大変優秀なので、
普通にコレだけ使ってもかなり有用であろう。ちなみに、localhostではコンテナにアクセス出来ない、という情報を見かけたが、
2020年1月以降のChrome OSアップデートを実施しているChromebookでは、
localhostがChrome OSとLinux環境で共通化しており、
Linux環境で立ち上げたコンテナには
「localhost:ポート番号」でアクセス可能である。HackMDの停止
「ctrl + c」で起動中のプロセスを停止すれば良い。
もうlocalhost:3000にはアクセス出来なくなっているハズ。
あとがき・所感など
Chromebookは予想以上に使い勝手が良かった!
まずChromebookは扱いやすく、とにかく起動的にも物理的にも「軽い」点がオススメ。
パソコンを触る時より、タブレット・スマホを触る時のほうが
心理的障壁が少ないと思うのだがそうした感覚に近いパソコンと言える。そして、Androidタブレットとして予想以上にかなりの割合のソフトが使える点も良い。
基本的にはデータはクラウド側に保存するため、
サブ機としてのデータ連携の手間が少ない点も便利である。さらに本稿のような簡単な開発環境にもなるとすれば、
Chromebookは一台持っておいて損は感じないのではないか?とても「軽い」開発環境を得ることが出来てよかった!
開発環境としてのノートブック型端末の競争についての私見
文教用としても、Windows,Chromebookの安い端末は普及してきている。
子供が最初に触るプログラミング環境はScratchなどだとしても、
次に触る環境は本稿のような環境になるような気がする。近年、WindowsにもWLS2環境が用意されてきており、
Windows,Mac,ChromebookなどのOSによらず、
linux系の環境・開発が出来るようになってきた。
これは従来はMacのアドバンテージだった点を、
Windows,Chromebookが猛追していることを意味する。むしろ、GPU利用機械学習はMacでは難しいため、
下記のようにゲーミングPCで実現可能なWindowsが一歩リードした、とも言えるゲーミングPCで機械学習をして、CPU/GPUの性能の違いをColaboとも比較してみた話【Windowsの機械学習環境構築手順決定版。TF2.0対応】
プログラムするならMac、みたいな風潮はちょっと変わってくるのかもしれない。
(Macもダメというわけではないけど)本稿の作成も、もちろんChromebook
本稿自体も、もちろんChromebookのみで完結して作成している。
(試している際の自分用のメモを公開しているだけなので当たり前だが)
役立ったという方がいらっしゃればまた更新していくかもしれない。以上。
- 投稿日:2020-09-14T00:03:42+09:00
Docker環境でRails停止時にExit code 1が発生する
問題
Docker上でRails (Puma) を実行中に
docker stop
でコンテナを停止させようとするとExit 1 (SIGHUP) が発生します。
ECSやKubernetesを利用している場合、コンテナが正しく終了せず、予期しない問題を引き起こす可能性があります。% docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES xxx rails "bundle exec rails s…" 44 seconds ago Exited (1) 3 seconds ago api xxx原因
俗に言う「PID 1問題1」が原因。PID 1はinitプロセスと呼ばれるもので、システム起動時にカーネルによって呼び出される特別なプロセスです。initプロセスはシグナルハンドリングや子プロセスの生成、ゾンビプロセスの削除などを行います。
今回はRails (2)を起動した際にPID 1が使われてしまい、シグナルを正しくハンドリングできないといった問題が発生していました。# コンテナ上でtopコマンドを実行した結果 PID USER PR NI VIRT RES %CPU %MEM TIME+ S COMMAND 1 root 20 0 2.2m 1.5m 0.0 0.1 0:00.02 S /bin/bash /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.プロセスを見ると、PID 1でRailsが起動していることが分かります。
対策
tini や dumb-init といったプログラムを使うことで、PID 1の子プロセスとしてアプリケーションを起動することが可能となります。
docker-compose 3.7以降が利用可能であれば、docker-compose.yml
にinit
パラメータを付けることで問題を回避することができます (3)。# Railsがinitプロセスの子プロセスとして起動する PID USER PR NI VIRT RES %CPU %MEM TIME+ S COMMAND 1 root 20 0 1.0m 0.0m 0.0 0.0 0:00.03 S /sbin/docker-init -- /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.0 6 root 20 0 2.2m 1.5m 0.0 0.1 0:00.00 S `- /bin/bash /bin/docker-entrypoint.sh bundle exec rails s -b 0.0.0.0
docker-compose down
実行後にプロセスを見ると、143 (SIGTERM) でコンテナが停止しています。% docker-compose ps Name Command State Ports ----------------------------------------------------------------------------- api /bin/docker-entrypoint.sh ... Exit 143ECSを利用している場合
タスク定義に
initProcessEnabled: true
(4) を追加します。initProcessEnabled
はdocker run
の--init
に相当します。Kubernetesを利用している場合
tiniなどの軽量initを使う方法もありますが、Kubernetes 1.17からは Share Process Namespace (5) を使うことで問題を回避できるようです。