20200914のdockerに関する記事は19件です。

Fedora CoreOSをVirtualBoxで試してみた

自己紹介

趣味でプログラミングをしている高専2年生です。
普段は†限界開発鯖†という開発コミュニティにいます。

Twitter: @programmer_shun
GitHub: @shun-shobon

Fedora 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を起動し、「新規」を選択、名前や場所を指定します。
スクリーンショット 2020-09-14 18.01.42.png

メモリを設定します。今回は8GBを割り当てました。
スクリーンショット 2020-09-14 18.01.50.png

ハードディスクの設定を行います。お好きな大きさにしてください。(今回は8GBにします。)
スクリーンショット 2020-09-14 18.01.57.png

仮想イメージが作成できたら「設定」を選択して、各種設定を行います。

高速化のためにプロセッサー数を1から4に増やします。
スクリーンショット 2020-09-14 18.03.28.png

光学ドライブにダウンロードしておいたFedora CoreOSのISOイメージをセットします。
スクリーンショット 2020-09-14 18.10.33.png

Fedora CoreOSとは基本的にSSHで接続するのでSSH用のポートフォワーディングを設定します。
スクリーンショット 2020-09-14 18.11.32.png

yamlファイルの作成

Fedora CoreOSはIgnitionファイルというファイルを使用してインストール時の初期設定を行います。
初期設定を特定ファイルによって行うことで複数台のインストールを楽にする狙いがあるようです。

ホストマシンで以下のようなyamlファイルを作成します。

ignition.yml
variant: 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.ign

Ignitionファイルのホスティング

作成したIgnitionファイルを仮想マシンに渡すためにFedora CoreOSではhttpを利用することを推奨しています。
お手軽なhttpホスティングの例としてpython3を利用したホスティングを例としてあげます。Ignitonファイルが存在するディレクトリで以下を実行してください。

python3 -m http.server

OSインストール

準備が整ったので仮想マシンにFedora CoreOSをインストールします。
仮想マシンを起動し、ignitionファイルを入手します。

curl -LO http://<ホストOSのローカルIP>:8000/ignition.ign

スクリーンショット 2020-09-14 21.50.37.png

以下のコマンドでOSをインストールします。特に確認が入るわけではないのでインストール先のドライブに注意してください。また、インストール先のドライブのパーティションもすべて自動的に削除されます。

sudo coreos-installer install /dev/sda --ignition-file ignition.ign

スクリーンショット 2020-09-14 21.51.57.png

インストールに成功したらsudo shutdown -h nowで一度シャットダウンして仮想マシンの光学ドライブにセットしていたISOイメージを取り外します。

スクリーンショット 2020-09-14 21.57.41.png

仮想マシンを起動して、ホストマシンからSSH接続を試してみます。

# ポート番号はポートフォワーディングで設定したポートを指定してください。
ssh core@127.0.0.1 -p 2222

スクリーンショット 2020-09-14 22.02.08.png

無事SSH接続できたらOSのインストールは成功です。ここで一度仮想マシンのスナップショットを作成しておくことをおすすめします。もし仮想マシンに問題が発生したり一度リセットしてやり直したいときに便利です。

Docker&Podmanを試す

Fedora CoreOSにはデフォルトでDockerとPodmanがインストールされています。試しに動作させてみます。

Docker

# Dockerはsudoが必要なので注意してください。
sudo docker run hello-world

スクリーンショット 2020-09-14 22.07.13.png

Podman

podman run hello-world

スクリーンショット 2020-09-14 22.06.58.png

感想

今回始めてコンテナ向けOSを試してみましたが、導入自体は案外楽だと感じました。
今度DigitalOceanに導入してみようと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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-app

1-2. Dockerfile作成

Dockerfileを以下のように作成し、my-react-appディレクトリ直下に配置します。

Dockerfile
FROM 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アプリが動いていることができたらexit

2.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.1

2-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: 2638

2-3. イメージがpushできたことDocker Hubのコンソール上で確認(アカウント名は念のため切ってます)

image.png

3. Kubernetesへのデプロイ、アプリの公開

3-1. kubectlコマンドのコンテキストを確認する。

コンテキストが存在しなかったり、ローカルなど異なるコンテキストとなっていた場合は、kubectlコマンドをダウンロードする

// すでにIBM Cloudに構築したKubernetesからkubectlコマンドをダウンロードしていた場合コンテキストは次のようになる
$ kubectl config current-context
<クラスター名>/<クラスターID>

ダウンロードしていなかった場合はIBM Cloudコンソール上でクラスターのプロファイルを開き、「アクセス」の箇所に以下のようにkubectlコマンドのダウンロード方法が載っている(ibmcloudコマンドをすでに使用できる場合「CLIツールの一回かぎりのセットアップ」は不要)。

image.png

3-2. deployment.ymlの作成

以下のようにdeployment.ymlを作成。containerのimageに2でpushしたイメージを指定する

deployment.yml
apiVersion: 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: 5000

Podが生成、Runnningとなっていることを確認(Podの名前はデプロイメント名に付随する)

$ my-react-app $ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
my-react-app-deployment-c4f95c7cd-rx8xt   1/1     Running   0          8s

3-3. NodePortを利用したserviceを作成する

以下のようにservice.ymlを作成。selectorのラベル(app: my-react-app)とPodのラベル(app: my-react-app)を一致させる。
ポートの番号は30000-32767の間に指定する。

service.yml
apiVersion: 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   10s

3-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.4

3-5. 公開されたアプリをみてみる

さあ緊張の瞬間ですが:で公開されたアプリを見てみましょう。

image.png

公開できました!やりましたね!

おわりに

今回はアプリのコンテナ化からKubernetes上へのデプロイまでの一連の流れを紹介してみました。
結構簡単に、かつ柔軟にデプロイできると思いました。

ただKubernetesのメリットとしてはこんなものではないので自分自身も勉強しつつ有益だと思ったらどんどん投稿していこうと思います。

次はKubernetesで構築したアプリの環境変数周りのリソースについて説明してみようかなと思ってます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

結果...

terminal
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"

ダメでした...。

このときに自分が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に

Dockerfile
RUN pip install pillow

と記入し、「equirementes.txt」に、

equirementes.txt
Pillow==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についての記事もこれから上げていくので、参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

結果...

terminal
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"

ダメでした...。

このときに自分が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に

Dockerfile
RUN pip install pillow

と記入し、「equirementes.txt」に、

equirementes.txt
Pillow==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についての記事もこれから上げていくので、参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルの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`
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:未使用のイメージ、コンテナも表示

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 jenkins

docker-compose.yml

version: "3"

services:
  jenkins:
    build: .
    ports:
      - 8080:8080
    volumes:
      - ./jenkins_home:/var/jenkins_home

Dockerコマンドで構築

docker-compose up

http://localhost:8080/
image.png
Jenkinsはこれで、構築完了。
空のジョブを一つ作成して、実行しておけば、workspaceができる。
このworkspaceでpullできるように構築する。
image.png
保存
i<br>
mage.png
ビルド実行

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の設定

image.png
ビルドトリガーは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

を叩いて、デプロイできれば成功です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルのdocker環境で作ったWebアプリをAWSに移行する方法

背景

ローカル環境でWebアプリを作っていたのでAWS上のサーバへ移行した。AWSでサーバを立てたことなかったので有識者に教えてもらいながらやってみた。忘れそうなので今後の自分のためにサーバ上でdockerを動かすところまでの手順を残しておく。

本編

大まかな作業手順はこう。
1. AWSにEC2インスタンスを立てる
2. 立てたサーバの中でdockerをインストールする
3. gitlabからアプリケーションをクローンする

実際に行った手順

実際の手順は以下のようにした。ただし今回、EC2インスタンスの作り方は割愛する。

サーバに入るまで

  1. AWSのEC2インスタンスを作る(詳細は今後書くかもしれない)
  2. 1ができると.pemがついたファイルができるのでこれは~/.ssh/大切に保存しておく。
  3. AWSのEC2のページにipアドレスがあるのでsshする。ssh xx.xxx.xx.xxxやってみたけどできなかった。
  4. ユーザ名をつけてやってみた。ssh ubuntu@xx.xxx.xx.xxxやってみたけどできなかった。
  5. エラーログがみたい時ssh -v ubuntu@xx.xxx.xx.xxx
  6. ssh -i ~/.ssh/xxx.pem ubuntu@xx.xxx.xx.xxxでxxx.pemコマンドで入れた。xxx.pemというキーを使ってubuntuに入りますよコマンドらしい。

サーバにて

  1. gitlabにあるリポジトリをサーバ側にsshでクローンしたい。いきなりクローンができないので、まずはssh-keygenでキーペアを作る。3回質問されるが特に何もなければEnterを3回押すだけ。
  2. .ssh/id_rsa.pubというキーができているはずなのでその中身をコピーしてgitlab上で公開鍵を記入するところ(設定とかからいけたはず)にペーストする。
  3. サーバ上でdockerをインストールする。sudo apt updateをまずやってsudo apt install docker.ioする。
  4. docker psできるか確認。権限がなかったのでsudo usermod -aG docker $USERという呪文を唱えた。
  5. ログアウトしてもう一度ログインしてdocker psしたら確認できた。
  6. sudo apt install docker-composeでdocker-composeをインストール。
  7. docker-compose upすれば起動するはず。落としたい時はdocker-compose downまたはdocker-compose stopすれば良い。ちなみに、バックグラウンド実行したい時はdocker-compose up -dでできる。

まとめ

ローカルのdocker環境で動かしているWebアプリをAWSに移行した。AWSのサーバ内に入るまでの内容とサーバに入ってdockerを動かすまでの内容の話をした。おそらく他のやり方もあるので参考に。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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",

と指定してあげると無事動かせました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Cloudでマイクロサービスを構成する(4):API Gateway編

Spring Cloudシリーズの第4回です。

概要

今回は「Spring Cloud Gateway」を使ってAPI Gatewayを立てることにします。

services.png

Spring Cloundファミリーでは従来、「Zuul」が使われていましたが、Zuulはメンテナンスモードとなり、「WebFlux&Nettyベースの」「Spring Gloud Gateway」が代替プロダクトとなっています。

API GatewayはAPIの前段に建てるリバースプロキシとして働きます。
API Gatewayの役割としては、クライアントとAPIの間に入り、API側に持ち込みたくない関心事を前段でさばくことにあります。

下の図は、API Gateway向けの別プロダクト「KrakenD」のドキュメントから拝借しています。
krakend_gateway.pnt.PNG

フィルタリング、入出力の返還、APIの集約、ログや統計情報取得、認証回り(SSLを解いたり、トークンをチェックしたり付加したり)、Throtting(流量制御でいいのかな?)といったところが代表例です。

個人的には2つを重要視していて、SpirngベースのSpring Cloud Gatewayには期待しています。

  • APIの前段に構えるので、パフォーマンスが良いこと。
    • 並行してアクセス数を多くさばける必要あり。
  • カスタマイズ性に富んでいること。
    • API Gatewayに機能追加がしやすいこと。(やりたいことがシンプルにできる)

環境

以下が動く環境を前提とします。

  • docker & docker-compose
  • JDK11

前回までの投稿の続きですので、前回のゴール地点のソースを手元に用意し、それをベースとします。

作業ステップ

本記事では以下のステップで進めていきます。

  1. API Gatewayのひな型作成
  2. 実装(アノテーションの付加と設定ファイルの記述)
  3. Dockerイメージ化とdocker-componentへの追加

ステップ1:API Gatewayのひな型作成

Spring Initializr の出番です。

ArtifactName だけgatewayに指定し、あとは
DependenciesGatewayEureka ClientSpring Cloud LoadBalancerSpringBoot Actuatorを追加しましょう。
ActuatorはGatewayに必須ではありませんが、何かと必要になるものなので追加しておきます。

Dependencies 説明
Gateway Spring Cloud Gateway を導入するために追加
Eureka Discovery Client Eurekaからサービスのリストを取得するために追加
Spring Cloud Loadbalancer クライアントサイド・ロードバランスのライブラリを追加
Spring Boot Actuator Spring Boot の内部情報を参照するために追加

gateway.png

あとはGENERATEするとスケルトンのgateway.zipがダウンロードできます。
 

ステップ2:実装(アノテーションの付加と設定ファイルの記述)

今回もあまりやることがありません。2つだけ。

まず1つ目。
Applicationクラスにアノテーションを1つ付けます。
これでService Discoveryを参照できるようになります。

gateway/src/main/java/com/example/gateway/GatewayApplication.java
package 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.yml
server:
  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/Dockerfile
FROM 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.yml
version: '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リクエストの中でどこにどれだけ時間がかかっているのか。
  • リクエスト数はそれぞれのサービスでどれだけさばけているのか。
  • そもそも負荷をかけたときにサービスはどのくらい余裕があるのか。

などなど、アクセスのトレースや、コンテナ/サービスのモニタリングをができる状態で負荷テストツールで負荷をかけられるようにしておきたいところです。

次回からはその辺を進めていきたいと思います。

ではでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Cloudでマイクロサービスを構成する(2):REST API(WebFluxとR2DBC)編

概要

今回はSpring BootでREEST APIサービスのサンプルをさらっと作ります。

services.png

単純に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)を使いますので、
前回の記事で作成したものに、ファイルを追加していきます。

作業ステップ

本記事では以下のステップで進めていきます。

  1. Spring Bootアプリの雛形作成
  2. APIの実装(Entity, Repository, Controller)
  3. APIのDockerイメージ化

ステップ1:Spring Bootアプリの雛形作成

さて次は、Spring Initializr でSpring Bootプロジェクトのスケルトンを作ります。

今回は、左側のパネルでArtifactとNameだけ「account」に変更しました。

そして、右側のパネルで以下のDependencyを指定します。

Spring Initializrの設定内容

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.java
package 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.java
package 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サンプルとして以下のようにします。
FlexMonoが出てくるのがWebFlux流ですね。

account/src/main/java/com/example/account/contoroller/AccountController.java
package 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.yml
app:
  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が整形されて幸せになれます。フィルタ機能もあってデータ確認が楽チン!)

json.png

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.java
package 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イメージを作れる機能が追加されましたが、この連載では扱いません。

Dockerfile
FROM 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.yml
version: '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 account

http://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」を作っていきます。

ではでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

常駐プロセスが無いdockerコンテナを起動させたままにする

docker run -itd ubuntu:latest /bin/bash

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルのサーバを立ち上げて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を動かしてみる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ESETのファイアウォールが動作しているとDockerで立ち上げたlocalhostのページがブラウザで確認できない問題

目的

  • ESETというセキュリティソフトを導入し、Dockerので起動しているlocalhostのページをブラウザで表示しようとした時に表示されない問題を解決する。

免責事項

  • 本作業ではセキュリティソフトの設定を変更します。若干デリケートな作業になりますので慎重に作業をしましょう。
  • 会社から貸与されているPCで本作業を実施する場合、管理者に相談の上実施することを強くオススメします。
  • 本作業を実施して皆様のPCにいかなる損失が合った場合でも筆者は責任を負いかねます。
  • 本記事の最後に記載されている参考文献のリンク先も是非ご確認いただきたいです。

前提情報

問題

  • ESETのパーソナルファイアウォールが「有効」状態でDockerのコンテナを起動しブラウザでhttp://localhost:8080にアクセスしてもLaravelのトップページが表示されない。

方法

  1. ESETのアプリを起動する。
  2. 「設定」をクリックする。

    空白_Skitch_キャンバス-4.png

  3. 「パーソナルファイアウォール」をクリックする。

    空白_Skitch_キャンバス-6.png

  4. 「設定」をクリックする。

    空白_Skitch_キャンバス-5.png

  5. 「フィルタリングモード」の「自動モード」を「対話モード」に変更する。

    ネットワーク.png

  6. 現在通信中のトラフィックを許可するかのウインドウが出るので怪しい通信ではないことを確認して「アクションを記憶する(ルールを作成する)」を選択して「許可」をクリックする。このウインドウは今行われいてる内向きのトラフィックと外向きのトラフィックすべてに対して表示されるのでウインドウが出なくなるまで前述した「許可」の作業か「拒否」の作業を実施する。

    空白_Skitch_キャンバス.png
    空白_Skitch_キャンバス-2.png

  7. Dockerのコンテナを起動する。

  8. ブラウザにて下記にアクセスする。

  9. 先に実施した様にトラフィックの許可を求められるので内容を確認し、「アクションを記憶する(ルールを作成する)」を選択して「許可」をクリックする。

    空白_Skitch_キャンバス.png
    空白_Skitch_キャンバス-2.png

  10. http://localhost:8080にてLaravelのトップページが表示されることを確認する。

  11. 「フィルタリングモード」の「対話モード」を「自動モード」に変更する。

    ネットワーク-2.png

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

`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.txt
docker-compose.yml
version: '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 or docker-compose up --build.
(サービステスト用のイメージが存在しなかったためにビルドされました。このイメージを再構築するには docker-compose builddocker-compose up --build を使う必要があります。)

と出ていたことに気づきました。英語をちゃんと読めばこんなに迷う必要はなかったですね。

似たような箇所でつまづいた人たちの参考になれば幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeでDocker入門

対象

Dockerインストール済み
初めてDockerを使用する。

前提

コマンドラインは使える
VSCode インストール済み

本記事について

一般的なのDockerの学習フローではdockerコマンドを学びつつ、イメージやコンテナの概念について理解し、その後docker-composeへと進んでいくと考える。

本記事ではイメージやコンテナ等の概念の説明は大きく省き、コマンドもvscodeの拡張機能で代用する。それによってdocker-composeのコマンドの最小限のみ学んでさくっと動かすことで実践的にDockerについて理解していこうというもの。

本記事のゴール

docker-composeコマンドがいくつかつかえるようになる。
nodejsの実行環境が作れる様になる。

VSCode環境構築

スクリーンショット 2020-09-14 13.18.11.jpg

スクリーンショット 2020-09-13 19.36.04.png

これをあらかじめインストールしておく。

nodejs環境を作る

まずは適当な場所にnodejssampleというフォルダを作る。この名前は適当。

そしてそのフォルダ直下に以下のようにdocker-compose.ymlというファイルを作る。

docker-compose.yml
version: "3"
services:
    node: 
        image: node:14.10
        volumes: 
            - .:/project
        tty: true
        working_dir: /project
        command: bash

この内容については後ほど解説する。

そして、同階層にapp.jsを作成

app.js
const main = () => {
    console.log("hello node!");
}

main();

スクリーンショット 2020-09-13 19.42.35.png

こうなっていればOK

そうしたら一度Dockerの現在の状態を確認しておく。

VSCode左からDockerマークを選択すると以下のような画面になってると思う。

スクリーンショット 2020-09-13 19.43.38.png

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のタブの画面が以下のようになる。
スクリーンショット 2020-09-13 19.52.05.jpg
Dockerで作った仮想環境のことをコンテナと呼ぶ。
イメージはコンテナ作るためのもの。

スクリーンショット 2020-09-14 14.03.23.png

仮想環境が起動できたので、次はその仮想環境でnodejsを動かす。

スクリーンショット 2020-09-13 19.57.17.png

対象のコンテナを右クリック > Attack Shellを押す。
すると以下の様な画面がでる。

スクリーンショット 2020-09-13 20.03.04.png
これがコンテナの中に入った状態。

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!

実行できた。

仮想環境を消す。

仮想環境は放置しておくと重いので、作業が終わったら消す。

スクリーンショット 2020-09-13 20.49.21.png

イメージを消す。

イメージの方は、これがあることによってdocker-compose up -dが早くなるため、頻繁に使うなら残しておいてもOK.

しばらく使わないなら消す。

スクリーンショット 2020-09-13 20.49.21.png

これで仮想環境の構築、仮想環境でのプログラムの実行、仮想環境の削除が一通りできた。

解説

docker-compose.ymlについて

docker-composeの基本的な構文は以下

docker-composeの構文
version: バージョン番号。
services:
    サービス名:
        サービスの設定...

versionは最新版が3
サービス名は自分でつける。なんでもOK。

docker-compose.yml
version: "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イメージ

スクリーンショット 2020-09-14 0.06.37.jpg

本記事で使ったタグはこれ

volumes
ローカルのフォルダとコンテナのフォルダをリンクさせる。

        volumes: 
            - ローカルのフォルダのパス:コンテナのフォルダのパス

これを設定しておかないと、コンテナからapp.js等のローカルに配置されたファイルをみることができない。

tty

tty: true

commandで指定した処理を行なった後すぐコンテナが停止しないようにする。
Attach Shellでコンテナに入る時は必須。

working_dir
そのまんまだが、コマンドを実行するときの場所。Attach Shellを行ったときもここで指定したフォルダに入る。

command
コンテナ起動時に実行する処理。
今回はbashを指定してるので、ターミナル開いて待っててっていう感じ。

他にもいろんな設定があるので別のDockerの記事をみて知らない項目があったらリファレンスを読むと良いと思う。

docker-compose up -dについて

リファレンス

docker-compose.ymlを読んで、必要であればイメージをダウンロードしてコンテナを作成するコマンド。

-dはバックグラウンドで起動するためのオプション。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

raila チュートリアル

5.3まで終了
5.4から

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.127

ChromeBook自体の操作方法。事前実行すると良いこと

個人的おすすめショートカット

「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/mmfbcljfglbokpmkimbfghdkjmjhdgbg

VisualStudioCodeなどを入れる方法もあるが、
とにかく「簡単・軽快」なのでまずコレで十分。

ファイル・フォルダ構成

エクスプローラー相当の「ファイル」というアプリから
「ダウンロード」したファイル(ローカルのファイル)、
「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 seaborn

JupyterNotebookのインストールと起動

# インストール
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-common

Docker公式のGPGkeyの追加

GPGとは、インストラーが本物かどうか、
検証するために用いられる暗号化の仕組み。

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88

Dockerの安定版(stable)repositoryの追加

一応CPUのアーキテクチャ情報を確認し、x86_64の手順を参照して実行

lscpu
#出力 ⇒ Architecture:        x86_64
sudo 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 1110ad01

Docker-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 3000

HackMDにアクセス

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のみで完結して作成している。
(試している際の自分用のメモを公開しているだけなので当たり前だが)
役立ったという方がいらっしゃればまた更新していくかもしれない。

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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が起動していることが分かります。

対策

tinidumb-init といったプログラムを使うことで、PID 1の子プロセスとしてアプリケーションを起動することが可能となります。
docker-compose 3.7以降が利用可能であれば、docker-compose.ymlinit パラメータを付けることで問題を回避することができます (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 143

ECSを利用している場合

タスク定義に initProcessEnabled: true (4) を追加します。initProcessEnableddocker run--init に相当します。

Kubernetesを利用している場合

tiniなどの軽量initを使う方法もありますが、Kubernetes 1.17からは Share Process Namespace (5) を使うことで問題を回避できるようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む