20201130のdockerに関する記事は20件です。

Docker上のCentOSだと自作systemdが動かない時の対応方法

どういう状況だってばよ!?

  1. CentOSで定期実行を実現する為にsystemdを自作したい。
  2. 実機のCentOSだと自作systemdが動くのに、Docker上のCentOSだとなぜかダメ。
  3. 具体的にはDocker上だと下記のエラーでこける。

System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

あたいはこんな環境でやってます

この記事は下図のようにWindows10のDocker Desktopをインストール済で、Docker-composeももれなくセットでインストールされている前提になります。
image.png

まずはdocker-compose.ymlを新規作成

どこかのフォルダにdocker-compose.ymlという設定ファイルがある訳ではないです。
また、docker-compose.ymlを置く場所はひとまずどこでもよかとです。

Docker初心者なので『docker-compose.ymlって設定ファイルどこにあるの…?どこに置かなきゃいけないの…?』なんて右往左往してました…(´・ω・`)

docker-compose.yml ≒ コンテナ作成手順書(指示書) って捉えるといいかな~って思います。

んで、今回は『C:\Users\waokitsune\centos8_systemd_test』というフォルダにdocker-compose.ymlを作成してみました。

docker-compose.ymlの中身

参考サイトさん を参照させてもらいました。
(centosという名前のイメージは既にある前提のymlになります)

version: "3.8"
services:
  centos8_systemd_test:
    image: centos
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
    cap_add:
      - SYS_ADMIN
    command: /sbin/init

PowerShellでDocker-composeにymlを食わせる

PowerShellを起動してまずはdocker-composeが置いてある場所へ行き…

cd C:\Users\waokitsune\centos8_systemd_test

docker-composeコマンドでコンテナ作成。

docker-compose up -d

成功するとこんな感じ。
image.png

ymlを食わせて出来たコンテナを使うぞい

Docker Desktopのダッシュボード開いて、端末起動!
ああ…なんか頭痛が痛いみたいなコンテナ名になっちゃってますね…(´・ω・`)
image.png

まずは ps aux でプロセスを見てみましょい。
なんか行けそうな気がします。

image.png

ちなみに自作systemdが動かなかったコンテナの ps aux 結果はこちら。
image.png

テキストファイルを作成するシェルスクリプトファイル『zoi.sh』をルート直下に作成します。

zoi.sh
#!/usr/bin/bash
touch /zoizoi.txt

作ったシェルスクリプトファイルに実行権限を与えてあげます。

chmod 755 /zoi.sh

そして、『/etc/systemd/system』フォルダーに『zoi.service』ファイルを作成します。
作ったシェルスクリプトをキックするだけの簡単なやつです。

zoi.service
[Unit]
Description=kyoumoitinitigannbaruzoi

[Service]
Type=simple
ExecStart=/zoi.sh

[Install]
WantedBy=multi-user.target

準備ができたので『zoi.service』を起動してみます。

systemctl start zoi.service

エラー無く終了したっぽいです。
image.png
本当か?本当にそうかぁ~?
systemctl status zoi.service で確認。
image.png
白丸なので正常終了っぽいです。
(シェルスクリプトに実行権限与え忘れてサービス起動した痕跡があるのはモアイ教ご愛敬)

ls / でも確認。
image.png
シェルスクリプトによってテキストファイルが作成されてます。

OKっす。

無事、Docker上のCentOSで自作systemdを動かせました。

蛇足

docker-compose.ymlがあると『まったく同じ環境をまた作る』が簡単にできて便利だと思いました。

参考サイトさん

https://futureys.tokyo/lets-run-the-web-application-for-development-by-docker-desktop-and-access-it-by-browser/

バージョン

Windows10 Pro バージョン1909 OSビルド19042.630
Docker Desktop:2.5.0.1(49550) Engine:19.03.13 Compose:1.27.4
PSVersion 5.1.19041.610
CentOS Linux release 8.2.2004 (Core)

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

JETで始めるJakartaEE

 実用的なソフトウェアを簡単に、ササッと作れるツールを持つと、プログラミングが楽しくなります。Java EEは、そのようなツールとして、大ブレイクしましたが、権利関係にからむ大人の事情によって、しばらく足踏みの状態が続いていました。しかし、ようやく前進の時期を迎えたようです。

Jakarta EEって、どうなの

 Java EEは、OracleからEclipse財団への移譲にともなう混乱をようやく脱出し、12月8日に本当の出発点として、Jakarta EE9が公開されます。内容はJava EE8(Jakarta EE8)とほとんど変わりませんが、javaxからjakartaへ、パッケージ名を変更することでOracleとの権利関係を完全に清算し、これから始まる変革と発展への準備を完了しました。
 おそらく、来年あたりから、Jakarta EEの静かなブームが到来することは間違いありません。プログラミング言語の目利きの皆さんはもちろんですが、これからプログラマを目指すアナタ、始めるなら、今。今が最高によいタイミングです。

今から始めるJakarta EE

 Java SEの世界から、実務で使われるEnterprise Javaの世界に飛び移るのは大変です。Java SEの知識・技術を習得していることは最低条件で、その上に全く新しい知識を身に付ける必要があるからです。ところが、今、Enterprise Javaの世界でも、クラウドネイティブJavaへ向けて、新たなパラダイムシフトが起きつつあります。特にマイクロサービス化への流れは、大きな潮流になりました。ただ、新しいパラダイムを理解するには、従来のEnterprise Javaをしっかり理解していることが最低条件です。

 ですから、今、Jakarta EEを始めれば、このようなパラダイムシフトに対応し、それを楽しむ余裕さえ、できるはずです。というのも、この新しい世界は、まだ「新たなフロンティア」状態で、ここしばらくは革新と統合が続きそうです。もう少し時間がかかるでしょう。そして面白くなるのは、ちょうど、これからなのです。

コ、コ、コロナが・・・

 Enterprise Javaの本は、もともと初心者は対象外なので、「取り扱い説明書」のような、ひどく読みにくいものばかりでした。参照資料としては優れていますが、「これじゃあ、使う人は増えないなぁ(特に日本では)」と、そう思ったものでした。それで、以前、入門書(『わかりやすいJava EE』)を出す時、Java SEを学習した普通の人達が、普通に理解できる本にしようと考えました。幸いにも、本は、多くの人たちに支持され、考えが正しかったことを実感したのでした。

 今年になってから、ようやく後継の本を出す時期が来たのでは、と考えていましたが、いくつか気がかりな点がありました。NetBeansがまだ安定していないこと(かと言って、Eclipseは使い辛い)、膨大な数の例題をantビルドからMavenでのビルドへ移行しなくてはいけないこと、そしてなりよりJakarta EEの開発スケジュールが遅れ気味であること。
 
 しかし、「夏が過ぎるころには、なんとかなるだろう」と思って準備を始めていたのですが、「コ、コ、コロナが・・・」あっという間に蔓延して、何もかも遅れ始めました。結局、12月8日に、Jakarta EE9(Java 8対応)が正式公開されることで、決着となりました。すぐ後に、Jakarta EE9.1(Java 11対応)もリリースされます。そして、すでにJakarta EE10(楽しみです!)の準備が始まっています。

そうだ、JETを作ろう!

 前置きが長くなりましたが、春先に、開発ツールを集めたツールキットを作ろうというアイデアが浮かびました。初めての人が対象ですから、困らないように、NetBeans、Payara Server、(Docker上の)MySQLをセットにして、ある程度設定を済ませて配布しようというアイデアです。例題やドライバ、プラグインなどもセットに入れてしまいます。基本的に、ダウンロードして展開すると、すぐにJakarta EEの開発ができる、そういうツールキットですね。

 それがJET(Jakarta EE Toolkit)です。Windows環境では(Mac用もあります)、JDKを組み込んでいるので、解凍したら5分以内に使い始められます。また、NetBeansには山本潤一さんの日本語化プラグイン(junichi11.com)を入れて日本語化してあります。データベースも、docker-composeコマンドを実行するだけでMySQLを利用できます。さらに、PayaraにはあらかじめJDBCドライバが登録してあり、JDBCを設定するバッチファイルも同梱しているので、簡単にJDBC関連の設定を完了できます。

 JETのインストール、データベース設定はビデオがありますので、よろしければ見てください。これなら初めてでも安心と、納得できるでしょう。

 ・JETのインストール(Windows)
 ・JETのインストール(MacOS)
 ・JETでのデータベース設定(Windows)
 ・JETでのデータベース設定(MacOS)
 

プロジェクトの変換、そして・・・

 実は、Java EEからJakarta EEへ、引き継ぐ必要のあるプロジェクトがたくさんありました。手で書き直すのはいやだったので、「自動化できないか」と思って、archeTypeの作成方法を調べていました。参考にしたのは、次のブログです。英語ですが、Googleの自動翻訳で十分読める程度です。
 Create your own Maven Archetype in 5 simple steps

 読んでわかったのは、ひな型のプロジェクトを用意しておいて、名前など必要な箇所を書き換えるだけなのだということです。ちょっと、拍子抜けしました。生成に結構時間がかかるので、おそらく、それ以外にいろいろなチェックをするのかもしれませんが、基本はそれだけです。
 したがって、変換のためには、適切なMavenプロジェクトのひな型を用意しておいて、antプロジェクトのJavaファイルや設定ファイルをコピーし、artifactIdなどpom.xml上のスケルトン部分を書き換えるだけでよいわけです。

 やり方が分かったので、半日ほどで、簡単な変換プログラムを(Java SEで)作成できました。しかし、それができたとき、ちょっとしたアイデアが浮かんだのでした・・・

JSFのプロジェクトがない!

 実は、NetBeansでwebアプリケーションのMavenプロジェクトを作成すると、RESTful Webサービス用のプロジェクトしかできません。つまり、従来作成していたJSF(Java Server Face)用のプロジェクトはできないのです。これからは、サーバーサイドでは、あらゆるものをサービスとして開発しようというクラウドネイティブな考えの表れでしょうか。
(ちなみに、RESTful WebサービスのUIををJSF+clientAPIで作ると、なかなか便利なのですが・・・)

 代わりに、任意のarcheTypeを読み込んでプロジェクトを作成するメニュー(「原型からのプロジェクト」)があり、JSF用のプロジェクトも何種類か生成できます。しかし、手順が面倒な割には、生成されるプロジェクトにびったりのものがありません。archeTypeの作成者ごとに、目指すところが違うためです。

 そこでarcheTypeを自作するわけですが、今度はarcheTypeからプロジェクトを生成する手順が、面倒なことが気になりました。やってることは、コピーと書き換えだけなのだし、
「ササッとできないかなぁ?
 そうだ変換プログラムを作り変えればできるのでは・・・」
そう考えたのでした。

プロジェクトビルダーの作成

 そこで作ったのが、builderというプログラムです。NetBeansから起動すると次のようなウェブが開きます。これはMavenプロジェクトを生成する簡単なJSFプログラムです。
9600.jpg
 プロジェクト名だけ入力して作成ボタンを押すと、瞬時に、JSF用プロジェクトができます(コピーと書き換えだけなのですごく速い)。RESTfulウェブサービス用もラジオボタンで選択できます。また、「データベースを使う」にチェックを入れると、完全な設定ファイル(persistence.xml)も生成します。データソース名は、デフォルトでjdbc/mydbとなっていますが、もちろん変更自由です。

 builderプロジェクトは、JETをダウンロードすると中に入っています。ソースコードやひな型のアーキタイプも見れるので、カスタマイズも簡単ですよ。
 builderの使い方は見たままですが、実際に使用する様子がこちらのビデオで見れます。よろしければ、ご覧ください。

生成されるMavenプロジェクト

 JSF用とREST(旧JAX-RS)用のプロジェクトは、それぞれ次のように生成されます。
JET-1.png
 また、pom.xmlは、JSFとRESTで同じで、次のようです。現在は、Jakarta EE8を使う設定になっていますが、Jakarta EE9が公開され次第、JETをversion 2 にアップデート(おおむね12月末頃)し、Jakarta EE9を使う設定に変更する予定です(各種設定ファイルもすべてJakarta EE9用に更新します)。

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jp.wjakarta</groupId>
    <artifactId>sample01</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>sample01</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <jakartaee-api.version>8.0.0</jakartaee-api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>${jakartaee-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.5.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.23.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

JETをダウンロード

JETはこちらからダウンロードできます。ダウンロードして解凍すると、次のようなフォルダ構成になっています。130以上の例題プロジェクトも入っていますので、参考にしてください。例題のタイトル一覧表はこちらです。なお、基本からしっかり学習したい場合は、書籍『わかりやすいJakarta EE』をご覧ください。

windows用

01.png

MacOS用

02.png

 
 

 

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

【環境構築メモ】docker × Laravel 開発環境構築(Laradock使用)

はじめに

Laravelでの開発環境を構築する際の手順をまとめました。
今回は、laradockを使ってみたかったので、laradockを使用して環境構築します。

※ laradockとは? http://laradock.io/

  1. Laravel(PHP)のプロジェクトをDocker上で動作させるためのワンダフルな環境
  2. Dockerを用いてlaravelの環境を作るために必要なものがほぼ全てパッケージ化されている
  3. PHP, nginx, MySQLやミドルウェア等、Laravelの実行に必要な環境が整っている。

前提環境

  • Windows 10 Professional
  • PHP7.3
  • Dockerインストール済み
  • gitインストール済み

ゴール

  • Laradockを使用して、Laravelの開発環境を構築する。
  • 開発環境としては、以下のような構成にします。
    • Laravel(Webアプリケーションフレームワーク)
    • mysql(DB)
    • nginx(Webサーバ)

手順

1. プロジェクトディレクトリを作成

  • 任意の場所にプロジェクトディレクトリを作成します。どこでもOK。
    $ mkdir todo-list

2. プロジェクトディレクトリに移動し、laradockをプロジェクトディレクトリ直下にclone

  • Githubからlaradockをcloneしてきます。cloneが終わると、プロジェクトディレクトリ直下に「laradock」というディレクトリが作成されます。
    $ git clone https://github.com/laradock/laradock.git

  • clone直後のプロジェクトディレクトリ階層: laradockディレクトリ下に色々入ってます

3. laradockディレクトリに移動し、「.env」ファイルを作成、編集

  • laradockディレクトリ直下の「env-example」ファイルをリネームして作成する
    • .envファイル = 環境設定ファイル
    • 設定ファイルのサンプルをベースに置き換える為、設定ファイルをコピーしておく。
      $ cd laradock/ $ cp env-example .env

3-1. envファイルの設定

  • .envファイル内の値を編集していきます。

【設定詳細】

I. envファイル内の「APP_CODE_PATH_HOST」の値を変更

  5 ### Paths #################################################
  6
  7 # Point to the path of your applications code on your host
  8 APP_CODE_PATH_HOST=../todo-app
  9

↑上記記述で、「todo-list/todo-app/」下の階層がアプリケーションのコードを格納するディレクトリとみなされるようになる。

  • 2020年11月時点では.envファイルに書いてあるPHPのバージョンは「7.3」でした。

    • PHP_VERSION=7.3
  • mysqlは「その時点の最新版」

    • MYSQL_VERSION=latest

4. docker-composeでコンテナを立ち上げる:nginx と mysqlを立ち上げ

  • laradockディレクトリに移動し、コマンドを実行。コンテナを起動します。
    $ docker-compose up -d nginx mysql
    ※初回起動は時間がかなり!!かかります。気長に待ちましょう。10分以上は間違いなくかかる!

  • docker-compose up でエラーが出たら、下記参考URLの内容を参照ください。
    https://qiita.com/blueray777/items/44e25dc04ed1ab5c4cc8

    ・Laradockのdocker-compose upで出会ったエラー達

  • コンテナの確認

$ docker-compose ps -a
           Name                          Command               State                                                                      Ports
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
laradock_docker-in-docker_1   dockerd-entrypoint.sh            Up      2375/tcp, 2376/tcp
laradock_mysql_1              docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
laradock_nginx_1              /docker-entrypoint.sh /bin ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp, 0.0.0.0:81->81/tcp
laradock_php-fpm_1            docker-php-entrypoint php-fpm    Up      9000/tcp
laradock_workspace_1          /sbin/my_init                    Up      0.0.0.0:2222->22/tcp, 0.0.0.0:3000->3000/tcp, 0.0.0.0:3001->3001/tcp, 0.0.0.0:4200->4200/tcp, 0.0.0.0:8001->8000/tcp, 0.0.0.0:8080->8080/tcp

5. Laravelプロジェクトの作成

  • コンテナ起動後は、コンテナ内に入ってLaravelプロジェクトを生成します。
    laradockはworkspaceという全ての操作が可能なコンテナが用意されるので、ここから操作していきます。

  • コンテナ内に入る(laradockユーザー/rootユーザー)
    $ winpty docker-compose exec --user=laradock workspace bash
    $ winpty docker-compose exec --user=root workspace bash

    - Laradockに入れた時の表示
    laradock@fb36cc3540cd:/var/www$

  • Laravelプロジェクトのダウンロード
    • コンテナに入ったら/var/www配下にいますので、composerを使用してプロジェクトを生成します。
      laradock@fb36cc3540cd:/var/www$ composer create-project laravel/laravel todo-app ※ダウンロードに時間かかる

6. プロジェクトの生成確認

  • http://localhost/ にブランチからアクセス確認

    • Laravelのデフォルト画面が出ることを確認
  • 404 Not Foundが表示された場合

    • 3-1. envファイルの設定 で、.envファイルの「APP_CODE_PATH_HOST」のみ変更しましたが、「APP_CODE_PATH_CONTAINER」のパスも変更する必要がありました。(共有ディレクトリのマウント設定)
  5 ### Paths #################################################
  6
  7 # Point to the path of your applications code on your host
  8 APP_CODE_PATH_HOST=../todo-app
  9
 10 # Point to where the `APP_CODE_PATH_HOST` should be in the container
 11 APP_CODE_PATH_CONTAINER=/var/www/todo-app
  • nginxのroot変更
    • nginxの設定を変更します。ドキュメントルートを変更(13行目:root 以下のパスを変更)
$ vi nginx/sites/default.conf

  1 server {
  2
  3     listen 80 default_server;
  4     listen [::]:80 default_server ipv6only=on;
  5
  6     # For https
  7     # listen 443 ssl default_server;
  8     # listen [::]:443 ssl default_server ipv6only=on;
  9     # ssl_certificate /etc/nginx/ssl/default.crt;
 10     # ssl_certificate_key /etc/nginx/ssl/default.key;
 11
 12     server_name localhost;
 13     root /var/www/todo-app/public;
 14     index index.php index.html index.htm;
  • envファイル変更後は、コンテナを再起動してください。
    $ docker-compose stop $ docker-compose up -d nginx mysql

まとめ

  • ここまでで、laradock環境で構築したLaravelの初期画面表示ができるようになりました。 プロジェクトディレクトリの階層はこんな感じです。
  todo-list/
      | todo-app/
      └ laradock/

todo-appディレクトリ配下には、Laravelの各種ソースが格納されています。
dockerの知識や、Nginx等のインフラ知識がなくても、ここまではすんなりとこれるのではないかと思います。次の記事では、今回構築したLaravel開発環境をベースに、簡単なWebアプリケーションを構築して実行してみたいと思います。

次回!

  • DB設定
  • テーブルを作成、データ投入→ DBのデータを画面(ビュー)で一覧表示させてみる。

参考にしたサイト

Laravelで簡単なページを作成するまで https://qiita.com/tasogarei/items/1e0a3577a9fae7e519ba

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

【Elixir 1.11】Phoenix Framework + DB開発をDockerでやる #1(コンテナ開発環境構築~CRUD実装~テスト)

この記事は、「Elixir Advent Calendar 2020」8日目の記事です。
きのうは@koyo-miyamuraさんの「Elixirで動的計画法(DP)を実装してみた(Goとの比較つき)」でした!

概要

Elixir製のフレームワークPhoenixによるWebアプリケーション開発をDockerでやる。

  • シリーズ第1回の本記事では、
    PhoenixコンテナとDB(PostgreSQL)コンテナとの連携確認をかねて、CRUD Web UI実装からテストまで行います。
  • 次回シリーズ第2回目で、CIパイプラインを導入します。

実行環境

バージョン 備考
macOS
Docker (docker-compose)
Elixir 1.11.2 compiled with Erlang/OTP 23
Phoenix 1.5.7
PostgreSQL 12.4
Node.js 14.15.1

ディレクトリ構成(Phoenixプロジェクト作成前)

.
├── .env
├── app
│   └── Dockerfile
├── db
│   └── Dockerfile
└── docker-compose.yml

コード

.env
PROJECT_NAME=my_app

POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_DB=testdb
POSTGRES_PORT=5432
app/Dockerfile
FROM elixir:1.11.2-slim

WORKDIR /usr/src/app

RUN apt-get update -y && \
  apt-get upgrade -y && \
  apt-get install -y \
  build-essential \
  curl \
  git \
  gzip \
  inotify-tools \
  ssh \
  sudo \
  tar

RUN apt-get install -y nodejs npm
RUN npm install -g n
RUN n 14.15.1
RUN apt-get purge -y nodejs npm

RUN yes | mix local.hex
RUN mix local.rebar --force
RUN mix archive.install hex phx_new 1.5.7 --force

RUN apt-get clean

CMD [ "mix", "phx.server" ]
db/Dockerfile
FROM postgres:12.4-alpine

ENV LANG ja_JP.utf8
docker-compose.yml
version: "3"

networks:
  backend:
    driver: bridge

services:
  app:
    build: ./app
    volumes:
      - ./app:/usr/src/app
    ports:
      - 4000:4000
    stdin_open: true
    tty: true
    command:
      sh -c "cd ${PROJECT_NAME} && mix phx.server"
    networks:
      - backend

  db:
    build: ./db
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    ports:
      - ${POSTGRES_PORT}:5432
    volumes:
      - ./db/postgres/tmp/data:/var/lib/postgresql/data
    restart: always
    networks:
      - backend

コンテナ開発環境構築

上記コードを、ご自分の開発環境に準備しましょう。
用意がととのったら、以下の手順で環境構築をしていきます。

ビルド 〜 Phoenixプロジェクトディレクトリ作成

ビルドします。

terminal
$ docker-compose build

ビルドが済んだら、

  • appコンテナ
  • dbコンテナ

がきちんと用意できているか確認します。

terminal
$ docker-compose run --rm app elixir --version
Elixir 1.11.2 (compiled with Erlang/OTP 23)

$ docker-compose run --rm app mix archive
* hex-0.20.6
* phx_new-1.5.7

$ docker-compose run --rm db psql --version
psql (PostgreSQL) 12.4

$ docker-compose run --rm app bash -c "node --version && npm --version"
v14.15.1
6.14.8

大丈夫そうです。

つづいて、Phoenixプロジェクトをmy_appという名前で作成していきます。

terminal
$ docker-compose run --rm app mix phx.new my_app

    .
    .
(途中で表示される`Fetch and install dependencies? [Yn]`には、`Y`で進めます)

    .
    .
We are almost there! The following steps are missing:

    $ cd my_app

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server

生成されたファイルの中身を書き換えます。

app/my_app/config/dev.exs
# Configure your database
config :my_app, MyApp.Repo,
  username: "postgres",  # <-- update
  password: "password",  # <-- update
  database: "testdb",    # <-- update
  hostname: "db",        # <-- update
  • .env で設定してある環境変数をつかっています。

コンテナ起動します。

terminal
$ docker-compose up -d

きちんと起動しているか、確認します。

terminal
$ docker-compose ps

        Name                       Command               State            Ports
----------------------------------------------------------------------------------------
................._app_1   sh -c cd my_app/ && mix ph ...   Up      0.0.0.0:4000->4000/tcp
................._db_1    docker-entrypoint.sh postgres    Up      0.0.0.0:5432->5432/tcp

よさそうです。

ここで mix ecto.create やっておきます。

terminal
$ docker-compose run --rm app bash -c "cd my_app && mix ecto.create"

コンテナを再起動します。

terminal
$ docker-compose restart app

ここまでできたら、Webブラウザで「http://localhost:4000」を確認しましょう。

以下のようなWelcome画面が表示されたらOKです!

phx_init_page

  • docker-compose logs をたたいて、ログ中にエラーが出ていないか念のため確認しておきましょう。

CRUD WebUI実装

それでは、CRUD機能を実装していきます。

お題として、シンプルなUsersリストを作っていきましょう。
まずは、取りあつかう情報をまとめてみます。

項目名
コンテキスト名 Accounts
スキーマ名 User
テーブル名 users
カラム : 型 name:string
email:string
bio:string

こちらの内容にもとづいて、生成コマンド mix phx.gen.html を実行します。

terminal
$ docker-compose exec app bash -c "cd my_app && \
mix phx.gen.html Accounts User users name:string email:string bio:string"

ルーティングを設定します。

app/my_app/lib/my_app_web/router.ex
scope "/", MyAppWeb do
  pipe_through(:browser)

  get("/", PageController, :index)
  resources "/users", UserController  # --> add
end

マイグレーションを実行します。

terminal
$ docker-compose exec app bash -c "cd my_app && mix ecto.migrate"

その後、コンテナを再起動しておきましょう。

terminal
$ docker-compose restart app

ブラウザで「https://localhost:4000/users/new」を確認します。

webui1

webui2

CRUD WebUIが爆誕したことを確認できました!

テスト

テスト側の設定として、DB接続情報を更新します。

app/my_app/config/test.exs
config :my_app, MyApp.Repo,
  username: "postgres",    # --> update
  password: "password",    # --> update
  database: "my_app_test#{System.get_env("MIX_TEST_PARTITION")}",
  hostname: "db",    # --> update
  pool: Ecto.Adapters.SQL.Sandbox

もしフォーマッターを入れてなかったら、このタイミングでmix formatを走らせておきましょう。

terminal
$ docker-compose exec app bash -c "cd my_app && mix format"

テスト実行します。

terminal
$ docker-compose exec app bash -c "cd my_app && mix test"

Compiling 23 files (.ex)
Generated my_app app
...................

Finished in 0.9 seconds
19 tests, 0 failures

All Greenでパスしていることを確認できました!

おわりに & 次回

今回、Phoenix + DBのDockerコンテナ開発環境を構築しました!
次回は、こちらのコンテナ開発環境にCIパイプラインを導入していきます。

明日の「Elixir Advent Calendar 2020」9日目は、@kobae964さんです。お楽しみに!

お知らせ

kokura.ex

北九州小倉のElixirコミュニティ「kokura.ex」は、福岡市で2017年から人気を博しているElixirコミュニティ「fukuoka.ex」が小倉にブランチした、新生Elixirコミュニティです。

fukuoka.ex同様、

  • 「高速処理性能」と「高い開発効率性」を両立できるプログラミング言語「Elixir」
  • そのWebアプリケーションフレームワーク「Phoenix」

を、北九州を起点として広め、ワイワイ盛り上げていくコミュニティです。
これから先端技術をやりたい方や、最新のプログラミングを学びたい方、未来に向けてITに強くなりたい方など、技術への興味レベルが高い方や、プログラミングに関心が高い方のご参加を歓迎します。
オンラインでのもくもく勉強会を、毎月ペースで開催しておりますのでぜひどうぞ。
(はじめてElixirに触れる方も、お気軽にご参加ください!)

  • イベントページ fukuoka.ex
    connpassイベント一覧より、kokura.exのページをご覧いただけます

fukuoka.ex アドベントカレンダー

fukuoka.ex Elixir/Phoenix Advent Calendar 2020」もありますので、こちらもぜひどうぞ。

12/22の枠に自分も執筆参加してますので、よろしかったらご覧ください!

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

Kubernetes IDE Lens

2020年8月にMirantis社が買収したLensというKubernetes統合運用管理ツール「Lens」を試してみようと思います。

1.Lensの概要とインストール

Kubernetes IDEと称していますが、アプリケーション開発的なことではなく、kubectlコマンドを実行できたり、マニフェストのエディット機能やPrometheusを利用したメトリクス表示機能などKubernetesの運用における統合環境の意味合いが強いようです。

LensはOSSなのでソースもGitHubで公開されています。Windows,Mac,LinuxでLens本体をインストールすることで利用できます。

特徴は以下となります。

  • The Kubernetes IDE
  • Multi Cluster Management
  • Multiple Workspaces
  • Built-In Prometheus Stats
  • Context Aware Terminal
  • Helm Charts Management

今回は、Windows版を試してみました。公式サイトからインストールファイルをダウンロードしてインストールするとデスクトップにショートカットが作成されます。それをダブルクリックするだけで利用できます。

ショートカットアイコン
image.png

起動後の画面
image.png

2.Kubernetesクラスタの登録

Kuberentesクラスタの登録は、左上の「+」ボタンをクリックします。クリック後、画面が遷移します。「Select kubeconfig file」でkubeconfigのパスを指定するとkubeconfigの内容が自動で読み込まれます。また、「Paste as text」タブを選択すると、kubeconfigの設定をペーストして読み込ませることができます。「Add cluster(s)」をクリックするとLensにKubernetesクラスタが登録されます。

Select kubeconfig file
image.png

Paste as text
image.png

登録すると、左上にアイコンが追加されます。

image.png

3.Kubernetesクラスタの状況確認

左メニューは、NodeやKubernetesのオブジェクトの状況を確認できます。Preometheusを利用すると、グラフで可視化できます。追加されたアイコン上で右クリック、「Settings」を選択します。

image.png

「Feature」の「Metrics」にある「Install」ボタンをクリックします。

image.png

左メニュー「Workload」を選択するとグラフでオブジェクトの状況を確認できます。

image.png

左メニュー「Nodes」を選択すると、各Nodeのリソース状況を確認できます。右上の「CPU」「Memory」「Disk」「Pods」を切り替えることで状況を確認できます。

image.png

左メニュー「Cluster」を選択すると、クラスタ全体のリソース状況を確認できます。

image.png

4.Helmの管理

左メニュー「Apps」を選択すると、Helm stableリポジトリを検索できます。

左下の「Terminal」横にある「+」ボタンをクリックして、「Terminal session」を選択するとkubectlコマンドを実行できるTerminalが起動します。新たにHelm Chartリポジトリを追加して、LensでRelease状況を確認しています。

image.png

コンソール画面

image.png

helm
> helm repo list
Error: no repositories to show
> helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
> helm repo list
NAME    URL
bitnami https://charts.bitnami.com/bitnami
> helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
> helm install wordpress bitnami/wordpress --set wordpressUsername=admin --set wordpressPassword=@dminp@ss
NAME: wordpress
LAST DEPLOYED: Mon Nov 30 16:59:52 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
** Please be patient while the chart is being deployed **

Your WordPress site can be accessed through the following DNS name from within your cluster:

    wordpress.default.svc.cluster.local (port 80)

To access your WordPress site from outside the cluster follow the steps below:

1. Get the WordPress URL by running these commands:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w wordpress'

   export SERVICE_IP=$(kubectl get svc --namespace default wordpress --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
   echo "WordPress URL: http://$SERVICE_IP/"
   echo "WordPress Admin URL: http://$SERVICE_IP/admin"

2. Open a browser and access WordPress using the obtained URL.

3. Login with the following credentials below to see your blog:

  echo Username: admin
  echo Password: $(kubectl get secret --namespace default wordpress -o jsonpath="{.data.wordpress-password}" | base64 --decode)

左メニュー「Apps」-「Release」を選択して、Releaseのwordpressをクリックすると詳細を確認できます。

image.png

5.Kubernetesクラスタの登録解除

LensからKubernetesクラスタを登録解除する場合は、クラスタアイコン上で右クリックして「Remove」を選択します。

image.png

「Remove」をクリックすると解除できます。

image.png

Lensから登録解除されるだけで、実際のクラスタ自体は削除されません。

6.まとめ

LensにKubernetesクラスタ登録、オブジェクト管理、メトリクス表示、Helm管理を試してみました。簡単にインストールして、kubeconfigを登録するだけでKubernetesダッシュボードのような感じでクラスタの状況を確認できる便利なツールです。複数のKubernetesクラスタを登録すればマルチクラスタ管理をこのGUIで行えます。OSSで無料で利用できるので、試してみてください。

7.参考リンク

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

【Docker】docker-composeのRails開発環境でbinding.pryをする

サクッと説明していく!!!

docker-compose.yml
# 下記を追加
tty: true
stdin_open: true

web:
  build:
  context: .
  dockerfile: Dockerfile
  command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3001 -b '0.0.0.0'"
  # ここから
  tty: true
  stdin_open: true
  # ここまで追加
  depends_on:
    - db
  ports:
    - "3001:3001"
  volumes:
    - .:/app
  • docker-compose upを実行。
  • docker psで起動中のコンテナを確認(idを確認)
  • docker attach コンテナid

以降、binding.pryしたところで止まるようになる。

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

【Docker】Dockerfileを書いて既存Railsの開発環境をDocker化してみた。

コンテナを作成するまでの流れ

Dockerfileからdocker imageを作り、docker imageを元に「コンテナ」が作られます。

そもそもDockerfileとは?

Dockerの上で動かす「コンテナ」の構成を記述するためのファイルのことです。つまり、DockerfileからDocker imageが作られ、それを元にコンテナが起動するという感じですね。

そもそもdocker imageとは

コンテナの元と捉えると良いかもしれません。Dockerfileを元にイメージを作ることができます。また、このdocker imageを共有することで、同じ実行環境でアプリケーションを動かすことが可能になります。

そもそもコンテナとは?

簡単にいうと、アプリケーションの実行環境のことです。ミドルウェアを含んだ実行環境の中でアプリケーションが動作します。また、Docker Engineがこのコンテナを管理します。(停止,再起動等)

実際にコマンドを追ってみる。

下記のコマンドでDockerfileからdocker imageを作成できます。【Dockerfile→docker image

docker build -t [image名]:[tag名] Dockerfileの場所

下記のコマンドでdocker imageからコンテナを作成することができます。【docker image→コンテナ】

docker create --name コンテナ名 image名

下記のコマンドでコンテナを起動させることができます。

docker start コンテナ名

ともかく実際にコンテナを作ってみる

Dockerfileを用意

Dockerfile内の基本コマンド

【FROM】

ベースイメージを指定します。ここに指定したimageを元にimageを作成することになります。基本的には公式のイメージを取得すればOK。

【RUN】

指定したコマンドを実行します。ミドルウェアをインストールする時に使われがち。何回も使用することができ、結果はデフォルトでキャッシュされる。

【CMD】

指定したコマンドを実行します。RUNとの違いは、コンテナの起動時にコマンドが実行されるということです。一つのDockerfileに一つだけ指定することができます。

【EXPOSE】

公開ポートを指定することができます。

【ENV】

Dockerfile内で使用する環境変数を指定することができます。

【ADD】

指定したホストのファイルやディレクトリ,リモートファイルをコンテナ内の指定したpathにコピーします。

【COPY】

指定したホストのファイルやディレクトリ,リモートファイルをコンテナ内の指定したpathにコピーします。

*ADDはリモートファイルはコピーできますがCOPYはできません。ADDは自然解凍されますが、COPYは自然解凍されません。

【ENTRYPOINT】

指定したコマンドをコンテナ起動時に実行します。
CMDがあれば省略可能です。

【VOLUME】

dockerが管理するデータ領域にマウントする機能。

【WORKDIR】

コマンド実行をする時のworking directoryをここで指定します。

では、基本的なコマンドはさらえたので実際に書いていきましょう!

Dockerfileを実際に書いていく

今回はRailsの環境構築のためのDockerfileを書いていきます。

# ベースとなるimageをFROMに指定
FROM ruby:2.6.3
# 基本的なソフトウェアをRUNコマンドでインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs           

# コンテナ内に/appディレクトリを作成
RUN mkdir /app
# working dirに/appを指定
WORKDIR /app
# ホストのGemfile,Gemfile.lockファイルをコンテナ内の/app内にコピー
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

# bundlerをインストール
RUN gem install bundler
# bundle installを実行
RUN bundle install
# ホストのカレントディレクトリをコンテナ内の/appにコピー
COPY . /app
# 公開ポートを指定。
EXPOSE 3001
# コンテナを起動するのと同時にrails server立ち上げる
CMD ["rails", "server", "-b", "0.0.0.0"]

 Dockerfileからimageを作成

タグ名とimage名は適当です。本来ならば、タグ名はバージョン管理に使われます。

docker build -t test:test .

では、docker imagesでimage一覧をみてみましょう

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test                test                f41bb9a8148e        31 seconds ago      1.07GB

ちゃんと作成されていますね!

docker imageからコンテナを作る

コンテナを作るコマンドは下記でしたね。

docker create --name コンテナ名 image名

では、下記のコマンドを実行してください。

docker create --name hoge test

処理が完了したら下記のコマンドを実行して、作成されているか確認してください。

docker ps -a

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS               NAMES
45ed30bea859        test:test           "rails server -b 0.0…"   14 seconds ago      Created                                      hoge

いい感じに作れていますね!

コンテナを起動してみる

docker start コンテナ名でコンテナを起動できるので、今回は

docker start hogeでコンテナが起動できるかと思います。

以上です。お疲れ様でした?‍♂️

PS:Railsのdocker-composeによる開発環境構築も書くかも

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

今更 Docker - Rails アプリケーションを作りたかった

大人気 Docker を使ってのアプリケーション開発が活発化している(根拠なし)今日このごろ、実務で使っていても 実のところ細かいことについては一切何もわかっていない という体たらくに終止符を打つべくして、 Rails の開発環境を定義してみよう、というのが本日の題である。

先ずはとりあえず実行したい

ということで正しい経路かどうかはわからんが、 Docker のドキュメント あたりを読み進めていく。重要そうなことが書いてある気がするのを全力で読み飛ばしつつ、まずは取り敢えず対話側での実行をしたい。

docker run -it ruby:2.6.6 bin/bash

2.6.6-alpine 使いたかったけど Rails のインストール時にコンパイルエラー食らったから一旦諦める
オプションについては

  • -t: コンテナの中に擬似ターミナル(pseudo-tty)を割り当てる
  • -i: コンテナの標準出力(STDIN)を取得する

ということらしく、つまり この指定がないと対話型で使うことはできん ようだ。

Rails のアプリを発生させるぞ

コンテナに入ってさえしまえば、あとは単に rails new をするだけなので

gem install rails -v 6.0.3
rails new myapp

このままだと Node.js not installed. のエラーが出てたりとか、細かくは色々あるようだが、何にせよこれで開発に使えるアプリケーションソースが出来る… 訳がない のである。

ホスト側にファイルを置きたいんだけど

知っての通り(?)、コンテナ内のファイルはそのコンテナ内にあるものであって、そのままではホスト側から識別可能なものにならないため、開発に着手するには更に一手必要になる。
これの実現には Docker Volume の活用が必要で、コンテナの作成時に -v のオプションを与えると良いらしい。

# まぁ「今いるところ」なら pwd でいいかなというアレ
docker run -it -v `pwd`:/webapp ruby:2.6.6 bin/bash

これにより、ホスト側のカレントディレクトリがコンテナ側の /webapp ディレクトリにマウントされる(= ファイルを突っ込んだ場合はホスト側に配置される)ようになる。ホスト側の任意のディレクトリをマウントしたい場合は 左側も絶対パス指定じゃないと駄目 らしい点には留意が要る。 相対パスでも構わん、と書いてあるのは多分これ別の文脈の説明だな?

さておき、この状況であれば /webapp にさえ配置してやれば rails new の生成物がホスト側でも確認できるので、

rails new webapp/myapp

とすることで、手元に直接的に Ruby の実行環境を持っていなくても Rails のアプリケーションが作れる。

ひとまずはここまで。実用的な開発環境の定義には Dockerfile や docker-compose の活用が前提だと思うので、遠からずそちらも書き散らしていくとしたい。

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

Dockerで始めるDjango生活(5日目)

初めに

5日目です。
4日目はこちら
前回は管理サイトを作成して、管理サイトから投稿しました。
今回はシェルから投稿しようと思います。

目次

  1. pythonシェルを開く
  2. シェルから投稿する

1. pythonシェルを開く

まずは、myprojectディレクトリに移動します。

# cd /root/projects/myproject/myproject
# pwd
/root/projects/myproject/myproject
# ls
blog  db.sqlite3  manage.py  myproject
#

それではpythonシェルを開こうと思います。

# python3 manage.py shell
Python 3.8.5 (default, Jul 28 2020, 12:59:40)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 1+2
3
>>> 2*2
4
>>>

上の様に

>>>

が表示されれば大丈夫です!

2. シェルから投稿する

では、実際に投稿しようと思います。

>>> from django.contrib.auth.models import User
>>> from blog.models import Post
>>> user = User.objects.get(username='admin')
>>> post = Post(title='Another post',
... slug='another-post',
... body='Post body.',
... author=user)
>>> post.save()
>>>

すると、管理画面を確認すると下の様にAnother Postが追加されています。image.png
この方法では、一度メモリにオブジェクトを作成してからデータベースに保存する方法です。
次の方法で実行すると一度のコマンドでオブジェクトを作成してデータベースに保存します。

>>> Post.objects.create(title='one more post',
... slug='one-more-post',
... body='Post body.',
... author=user)
<Post: one more post>
>>>

画面を確認すると新たにone more postが追加されています。
image.png

次にタイトルを更新してみます。

>>> post.title = 'New title'
>>> post.save()
>>>

再度画面を確認するとAnother postのタイトルがNew titleになっています。
image.png

3. 投稿内容を取得する

全件取得する

>>> all_posts = Post.objects.all()
>>> all_posts
<QuerySet [<Post: one more post>, <Post: New title>, <Post: テスト>]>
>>>

フィルターをかけて取得する

>>> Post.objects.filter(publish__year=2020)                           # publishの年が2020のもの
<QuerySet [<Post: one more post>, <Post: New title>, <Post: テスト>]>
>>> Post.objects.filter(publish__year=2020, author__username='admin') #年が2020かつ投稿者がadminのもの
<QuerySet [<Post: one more post>, <Post: New title>, <Post: テスト>]>
>>> Post.objects.filter(publish__year=2020) \
... .exclude(title__startswith='one')                                 #年が2020かつタイトルがoneから始まらないもの
<QuerySet [<Post: New title>, <Post: テスト>]>
>>> Post.objects.order_by('title')                                    #タイトル順(昇順)
<QuerySet [<Post: New title>, <Post: one more post>, <Post: テスト>]>
>>> Post.objects.order_by('-title')                                   #タイトル順(降順)
<QuerySet [<Post: テスト>, <Post: one more post>, <Post: New title>]>
>>>

オブジェクトを削除する

>>> post = Post.objects.get(id=1)
>>> post
<Post: テスト>
>>> post.delete()
(1, {'blog.Post': 1})
>>>

再度画面を確認するとテストの投稿が削除されています。
image.png

blogディレクトリのmodels.pyに下の修正を追記してstatusがdraftのオブジェクトを取得しないようにするオプションを作成する。

models.py
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager,
                     self).get_queryset()\
                          .filter(status='published')
class Post(models.Model):
    ...
    objects = models.Manager()
    published = PublishedManager()

4. 最後に

この記事では下記の本を参考に書かせていただいています。
この記事ではDjangoを実際に触って動かすことをメインに書いていますので上記のマイグレーションの詳しい説明などはこの本を読んでただければと思います。
Django 3 By Example - Third Edition

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

Tomcat with Docker 入門

背景

TomcatのDocker imageを使用してJavaServletを使ったサーバーを作りたい!
と思ったけど,Webアプリケーション初心者なのでTomcat公式イメージの使い方がよくわかりませんでした.
同じような問題を抱えている人に届くと嬉しいです.

環境

  • MacOS Catalina
  • Docker Desktop
Client: Docker Engine - Community
 Cloud integration: 1.0.2
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 16:58:31 2020
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:07:04 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0

使用したDocker image

  • tomcat:8.5.35

Tomcat公式イメージの使い方

runの基本構文

基本的には以下のようにしてdocker runします.

 docker run --rm -v /directory/has/.war:/usr/local/tomcat/webapps -p xxxx:8080 tomcat:8.5.35

ここで早くもポイントなのが /directory/has/.warxxxx の部分です.
/directory/has/.war はwarファイルの入ったディレクトリを,
xxxx はポートフォワーディングしたいホスト側ポートを表現しています.

Tomcatイメージのデフォルト動作

先程述べたようにwarファイルの入ったディレクトリを /usr/local/tomcat/webapps にマウントしてあげると,Tomcatさんが勝手にwarファイルを解凍してデプロイしてくれます.(便利ですね)

Tomcatイメージを使った開発

warファイルがあると.つまりアプリが完成している場合についてはお話しました.
しかし,今回の私のようにTomcatイメージを使って開発がしたい人もいるでしょう(いて下さい).
そんなときは以下のようにDocker runしましょう.

docker run -it --rm --name tomcat_test -v /directory/has/.war:/usr/local/tomcat/webapps -p xxxx:8080 tomcat:8.5.35 /bin/bash

先程のコマンドに -it/bin/bash を付け足しました.
これでコンテナの中に入って作業ができます.

このイメージにはデフォルトでJDKが入っていなかったので,以下のようにJDKを後入れすることにします.(Tomcat公式のDockerHubを見ると予めJDKが入っていそうなものもありました)

# apt update -y
# apt install default-jdk

ここまで来ると,後は Docker 固有の問題はおそらく無く,一般的なTomcatの通りに開発できます

Tomcatでのディレクトリ構成

webapps/
      └─ WebAppName/
             ├─ WEB-INF/
             │      ├─ classes/
             │      └─ lib/
             ├─ src/
             └─ etc.

あたりがよくある構成かと思います.

TomcatでのJavaServletのコンパイル

JavaServletではSource内でアノテーションパターン("@")を使用しているため,xmlは必要ありません.

at WebAppName Dir# javac -cp "/usr/local/tomcat/lib/servlet-api.jar:WEB-INF/lib/*" -d WEB-INF/classes/ src/hogehoge/*.java

このようにすることで,JavaServletライブラリ /usr/local/tomcat/lib/servlet-api.jar と外部ライブラリ WEB-INF/lib/* が使えます.

Tomcat 起動

コンパイルが済んだので,Tomcatを起動します.

at tomcat Dir# ./bin/catalina.sh run

まとめ

今回は Tomcat with Docker の入門でした.
UbuntuのイメージにTomcatを後入れして頑張る記事は沢山ありましたが,Tomcat公式イメージを使用した場合の解説記事が見つからなかったため,書いてみました.

誰かの役に立てたなら幸いです.

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

Dockerコンテナ内からホストのsshエージェント転送する場合

デプロイなどでDocker内からsshする場合(capistrano等)にホスト側のssh-agent転送する方法です。

LinuxやWSL2等の場合

docker-compose.yml
services:
  web:
    volumes:
      - "$SSH_AUTH_SOCK:/tmp/ssh-agent.sock"
    environment:
      SSH_AUTH_SOCK: "/tmp/ssh-agent.sock"

ホスト側のssh-agentのsockファイルの$SSH_AUTH_SOCKを中継すればいい形でシンプルです。

Docker Desktop for Macの場合

services:
  web:
    volumes:
      - "/run/host-services/ssh-auth.sock:/tmp/ssh-agent.sock"
    environment:
      SSH_AUTH_SOCK: "/tmp/ssh-agent.sock"

何故か標準の$SSH_AUTH_SOCKでは動かなく別のsockファイルの/run/host-services/ssh-auth.sockだと動くようです。

この仕組み自体が下記githubのissue見ると2020年にようやく解決したみたいで4年かかったようで。。
$SSH_AUTH_SOCK is not being forwarded to docker · Issue #410 · docker/for-mac
https://github.com/docker/for-mac/issues/410

参考

LinuxとMac混在のクロスプラットフォーム対応

.envファイルでsockファイルを自分用の環境に書き換えるようにして吸収しました。
無理くりです。。macが標準のSSH_AUTH_SOCKでできるようになれば簡単に解決なんですが。。

※docker-compose自体が.envファイルに対応しているので別途ミドルウェア不要です。

docker-compose.yml
services:
  web:
    volumes:
      - "$SSH_AUTH_SOCK_APP:/tmp/ssh-agent.sock"
    environment:
      SSH_AUTH_SOCK: "/tmp/ssh-agent.sock"
.env
# mac以外の場合は下記を利用する  (WSL2など)
SSH_AUTH_SOCK_APP=${SSH_AUTH_SOCK}
# macの場合は下記を利用する  (macだと$SSH_AUTH_SOCKのデフォルトだと動かなかった)
#SSH_AUTH_SOCK_APP=/run/host-services/ssh-auth.sock

ちなみにわざわざSSH_AUTH_SOCKではなくSSH_AUTH_SOCK_APPと別名をつけているのは.envで定義したものよりシェルの値のほうが優先されるという仕様があったためです。

シェル内にて設定される値は、.envファイル内のものよりも優先されます。

docker-compose.ymlでif文使えたらいいんですが無さそうでした。
Rancherというのを入れればできるそうですがよほどデファクトでない限りは入れたくはないので。

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

vscode + c/c++ + docker + online judge toolsの環境構築

dockerをAtCoderの環境構築に使おう!

目的

AtCoderにて使用するプログラミング環境をdockerで構築する。
使用するPCを変更、またはOSの再インストールなどに柔軟に対応できる。
dockerfileの記載方法を学ぶ。
自分でdockerを使い開発環境を配布できる知識をつける。

これまでの記事

Docker, java, vscodeの開発環境を構築
AtCoderで記載したコードをgithubへアップロードし勉強記録として保存しよう

対象のPC環境

macbook pro

使用するプロジェクト

online-judge-tools
vscode-remote-try-cpp

online-judge-toolsはpython3とpip3を使用します。
python3とpip3をdocker fileとdevcontainer.jsonに記載します。

docker file

vscode-remote-try-cppの.devcontainer > Dockerfileを開きます。
pip3のインストールを行います。

dockerfile
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install --no-install-recommends \
    && apt-get -y install python3-pip

devcontainer.json

ここではvscodeで使用するパッケージを指定できます。
python3を使用するので「ms-python.python」を指定します。

extensions
    "extensions": [
        "ms-vscode.cpptools",
        "ms-python.python"
    ],

postCreateCommandにて、
pip3を使用しrequirements.txtに記載されたツールをインストールします。

postCreateCommand
"postCreateCommand": "pip3 install -r requirements.txt",

requirements.txt

requirements.txtにて「online-judge-tools」を指定します。

requirements.txt
online-judge-tools

tasks.json

argsにてC++14を指定します。
「${relativeFile}」で現在開いてアクティブになっているファイルをコンパイルするように指定します。
また、online-judge-toolsの「oj t」にて指定するのは「a.out」の為、コンパイル時に出力するバイナリを「a.out」に指定します。
これで「oj t」をターミナルに入力するだけでサンプルケースをテストできます。

tasks.json
            "args": [
                "-std=c++14",
                "-g",
                "${relativeFile}",
                "-o",
                "a.out"
            ],

launch.json

デバッグ実行時の設定を行います。
「a.out」を実行するように指定。
また入力値を「input.txt"」を使用するようにします。
デバッグ実行の度にINPUT値をコピペしなくてよくなります。

launch.json
            "program": "${workspaceFolder}/a.out",
            "args": ["<input.txt"],

まとめ

今まで勉強したターミナル、コマンドプロンプト、docker、vscodeの知識を使用しました。
実際の開発でもdockerとgitは必須知識です。
基礎はできるようにしておきましょう。

次回

vscodeはnpmスクリプトを覚えるともっと便利に使えます。
npmスクリプトを勉強していきます。

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

Dockerで始めるDjango生活(4日目)

初めに

4日目です。
3日目はこちら
前回はblogアプリを有効化した所で終わっていました。
管理サイトを作成します。

目次

  1. スーパーユーザーを作成する
  2. 管理サイトにログインする
  3. 管理サイトにblogアプリを追加する
  4. blogアプリを使って投稿してみる
  5. 投稿完了後画面の表示を増やす
  6. 投稿完了後画面をカスタマイズする
  7. 最後に

1. スーパーユーザーを作成する

管理サイトを作成するにあたって、スーパーユーザーを作成する必要があります。

# cd /root/projects/myproject/myproject
# pwd
/root/projects/myproject/myproject
# ls
blog  db.sqlite3  manage.py  myproject
# python3 manage.py createsuperuser
ユーザー名 (leave blank to use 'root'): admin
メールアドレス: admin@admin.com
Password:
Password (again):
Superuser created successfully.
#

2. 管理サイトにログインする

ますは、
http://127.0.0.1:8000/admin
にアクセスします。
すると下の画像のような画面が表示されると思います。
image.png
先程作成したスーパーユーザーでログインします。image.png
すると管理画面に遷移します。
image.png

3. 管理サイトにblogアプリを追加する

管理サイトにblogアプリを追加するにはblogディレクトリのadmin.pyを修正する必要があります。

# cd /root/projects/myproject/myproject/blog
# pwd
/root/projects/myproject/myproject/blog
# ls
__init__.py  __pycache__  admin.py  apps.py  migrations  models.py  tests.py  views.py
#
admin.py(修正前)
from django.contrib import admin

# Register your models here.
admin.py(修正後)
from django.contrib import admin
from .models import Post
# Register your models here.
admin.site.register(Post)

再度管理サイトを表示すると下の様にblogアプリが追加されているかと思います。image.png

4. blogアプリを使って投稿してみる

まずは、Postsの欄にある、「+追加」をクリックします。
すると投稿画面に遷移します。image.png
適当に入力します。
image.png
ここで保存を押すと下記のページに遷移します。
image.png
これで投稿は完了です!
ただ、この画面でタイトル以外にも誰が投稿したかなども知りたいですよね。

5. 投稿完了後画面の表示を増やす

まずは、admin.pyを修正します。

admin.py(修正前)
from django.contrib import admin
from .models import Post
# Register your models here.
admin.site.register(Post)
admin.py(修正後)
from django.contrib import admin
from .models import Post
# Register your models here.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'author', 'publish', 'status')

再度画面を表示します。image.png
画面に投稿者などが表示されました。

6. 投稿完了後画面をカスタマイズする

5で表示された内容にフィルターなどを追加してみようと思います。

admin.py(修正前)
from django.contrib import admin
from .models import Post
# Register your models here.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'author', 'publish', 'status')
admin.py(修正後)
from django.contrib import admin
from .models import Post
# Register your models here.
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'author', 'publish', 'status')
    list_filter = ('status', 'created', 'publish', 'author')
    search_fields = ('title', 'body')
    prepopulated_fields = {'slug': ('title',)}
    raw_id_fields = ('author',)
    date_hierarchy = 'publish'
    ordering = ('status', 'publish')

では、画面を再度表示すると下の様になります。
image.png

7. 最後に

今回は管理画面を表示して実際にblogアプリを通して投稿などができるようにしました。
この記事では下記の本を参考に書かせていただいています。
この記事ではDjangoを実際に触って動かすことをメインに書いていますので上記のマイグレーションの詳しい説明などはこの本を読んでただければと思います。
Django 3 By Example - Third Edition

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

Dockerで始めるDjango生活(3日目)

初めに

3日目です。
2日目はこちら
前回はblogアプリを作成した所で終わっていました。
今回はモデルを実際に作成してアプリを有効化しようと思います。

目次

  1. モデルを作成する
  2. blogアプリを有効化する
  3. 最後に

1.モデルを作成する

まずは、blogフォルダに遷移します。

# cd /root/projects/myproject/myproject/blog
# pwd
/root/projects/myproject/myproject/blog
# tree .
.
|-- __init__.py
|-- admin.py
|-- apps.py
|-- migrations
|   `-- __init__.py
|-- models.py
|-- tests.py
`-- views.py

1 directory, 7 files
#

モデルを作成するにあたって編集するのはblogディレクトリ配下のmodels.pyになります。
models.pyはデフォルトでは以下の様になっていると思います。

models.py(修正前)
from django.db import models

# Create your models here.

それでは実際にモデルを作成したいと思います。

models.py(修正後)
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published'),
    )
    title = models.CharField(max_length=250) #データベースではVARCHARとなります。
    slug = models.SlugField(max_length=250,
                            unique_for_date='publish') #slugはURLを作成するために使用します。ここでは投稿日付を元に投稿用のURLを作成します。
    author = models.ForeignKey(User,
                              on_delete=models.CASCADE,
                              related_name='blog_posts') #外部キーでユーザーと紐づけているのでユーザーが削除されれば投稿も削除されるようにしています。
    body = models.TextField() #投稿の本文にあたります。
    publish = models.DateTimeField(default=timezone.now) #投稿の公開日時に現在時刻を指定します。
    created = models.DateTimeField(auto_now_add=True) #auto_now_addを指定すると投稿の作成時に日付が保存されます。
    updated = models.DateTimeField(auto_now=True) #auto_nowを指定すると投稿の保存時に日付が保存されます。
    status = models.CharField(max_length=10,
                              choices=STATUS_CHOICES,
                              default='draft') #投稿の状態を保存します。上でSTATUS_CHOICESは定めていてdraft(公開前),published(公開後)となり、どちらか一つの状態しか選べません。

    class Meta:
        ordering = ('-publish',) #メタデータを用いる事でデフォルトで結果の並び替えを行っています。この場合は-が付いているので降順になり、公開された順に投稿が表示されます。
    def __str__(self):
        return self.title #タイトルを表示します。

2. blogアプリを有効化する

Djangoがアプリを追跡して、先程作成したモデルのテーブルを作成するためには、blogアプリを有効化する必要があります。
そのためには、myprojectフォルダのsettings.pyを修正する必要があります。
まずは、ディレクトリに移動します。

# cd /root/projects/myproject/myproject/myproject
# pwd
/root/projects/myproject/myproject/myproject
# ls
__init__.py  __pycache__  asgi.py  settings.py  urls.py  wsgi.py
#

次にsettings.pyのINSTALED_APPSに追加します。

settings.py(修正前)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
settings.py(修正後)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig', ←追加
]

以上で、テーブルを作成する準備が整ったのでマイグレーションを行うとテーブルが作成されます。

# cd /root/projects/myproject/myproject
# pwd
/root/projects/myproject/myproject
# ls
blog  db.sqlite3  manage.py  myproject
# python3 manage.py makemigrations blog
Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Post
#

作成されるとblogディレクトリにmigrationsディレクトリが作成されその中に0001_initial.pyが作成されています。
では、先程のマイグレーションでどのようなSQLが発行されたかを見てみます。
見るためには、sqlmigrateというコマンドを使います。

# python3 manage.py sqlmigrate blog 0001
BEGIN;
--
-- Create model Post
--
CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "blog_post_slug_b95473f2" ON "blog_post" ("slug");
CREATE INDEX "blog_post_author_id_dd7a8485" ON "blog_post" ("author_id");
COMMIT;
#

このコマンドではSQL文を実行はせずに出力します。
では、最後に今作ったblog_postをデータベースと同期させます。

# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying blog.0001_initial... OK
#

3. 最後に

最後に、再度モデルを作成して有効化するまでの流れをまとめておきます。

models.pyに作成したい情報を書き込みます。
↓
settings.pyのINSTALLED_APPSに今回作成したいアプリを追加します。(既に追加されていた場合はこの作業は要らないです)
↓
python3 manage.py makemigrations <アプリ名>でテーブルを作成
↓
python3 manage.py migrateで同期させます。
---------以下、既存のモデルを修正した場合-----------
models.pyに修正内容を追記
↓
python3 manage.py makemigrations <アプリ名>でテーブルを作成
↓
python3 manage.py migrateで同期させます。

この記事では下記の本を参考に書かせていただいています。
この記事ではDjangoを実際に触って動かすことをメインに書いていますので上記のマイグレーションの詳しい説明などはこの本を読んでただければと思います。
Django 3 By Example - Third Edition

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

Dockerインストール時に起こったエラー(one of the sub-processes failed)の解決策

はじめに

やりたいことがあり、今更ながらDockerをインストールしようとしたのですが、
躓いた点があったので共有しておきます。

エラーメッセージ

今回受けたエラーメッセージは以下の通りです。

one of the sub-processes failed: com.docker.driver.amd64-linux -addr fd:3 -debug (pid: 989)

状況としては、
DockerのホームページよりインストーラをDLしてインストール→open /Applications/Docker.app で立ち上げたところ、上記メッセージを確認しました。

対応策

こういった場合は基本的にDockerの初期化をするようなのですが、それでは解決せず。
どうやら使っているOSが古い(macOS High Sierra v10.13.6)のが原因だったようです。
対応として、こちらのページから古いバージョンのDockerをインストールすれば解決しました。

とりあえず現在はv2.4.0.0で動作させています。
(ただし毎回起動時に以下の警告は受けます。)

OS X 10.13 is not supported 
There is an update but you are on OS X 10.13 or an earlier version.
Upgrade to the latest version of macOS to install it.

最後に

諸事情でmacOSのアップデートはしたくないのですが、
こういうエラーが多くなってくるとHighSierraも潮時な感じがしてきました。
本記事が誰かの助けになれば幸いです。enjoy!!

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

ローカルでMySQLコンテナに大量INSERTしていたら"unexpected EOF"になった時の話

はじめに

データ削除バッチのベンチマークを取るため、ローカルのMySQLコンテナにデータを800万レコードほどINSERTしていたところ、 packets.go:36: unexpected EOF というエラーに遭遇しました。その時に調べたことや対処したことなどをまとめます。

環境:

  • Go 1.15.2
  • go-sql-driver/mysql v1.4.1
  • MySQL 5.7.30

対処と原因

MaxLifetimeを設定する

packets.go:36: unexpected EOF でぐぐってみると、結構ヒットしたので比較的よくある現象のようです。それらによると、 SetConnMaxLifetime を設定すれば解消するとのことでした。

SetConnMaxLifetime に関してはこちらの記事で説明されています。その名の通り、接続の寿命を設定するもののようです。
Re: Configuring sql.DB for Better Performance

MySQL では wait_timeout という設定で接続がサーバーから切られる恐れがあります。また、OSやルーターが長時間利用されていないTCP接続を切断することもあります。どのケースでも、 go-sql-driver/mysql はクエリを送信した後、レスポンスを受信しようとして初めてTCPが切断されたことを知ります。切断を検知するのに何十秒もかかるかもしれませんし、送信したクエリが実行されたかどうかを知ることもできないので安全なリトライもできません。
こういった危険をなるべく避けるためには、長時間使われていなかった接続を再利用せずに切断し、新しい接続を使うべきです。 SetConnMaxLifetime() は接続の寿命を設定するAPIですが、寿命を10秒に設定しておけば、10秒使われていなかった接続を再利用することもありません。

引数に与える秒数に関しては、

SetConnMaxLifetime() は最大接続数 × 1秒 程度に設定する。多くの環境で1秒に1回接続する程度の負荷は問題にならない。

とあったため、とりあえず1秒を設定しました。

つまり、こんな感じの実装です。

db.SetConnMaxLifetime(time.Second)

これにより、エラーは発生しなくなりました!!

なぜこの設定で問題解消されるのか

MaxLifetime の値はデフォルトでは無制限です。
一見すると、コネクションはできるだけ使いまわした方が再接続のオーバーヘッドも少なく、良いように思われるのですが、接続時間が長すぎることのデメリットも多くあるようです。いくつかの記事でそのことが説明されていますが、例えば、こちらの記事ではこのように説明されていました。

コネクション回数が減る代わりにアイドルのコネクションが必要なくなってもキープし続ける負荷があったり、DB起因でコネクションが中断されたり、フェイルオーバしたりした際に、外部要因で既に切れたコネクションを持ち続けて切断の検知に時間がかかったり、新しいコネクションに切り替えて欲しい際に切り替えがなかなか起こらないというリスクを含んでしまいます

sql.DBのチューニング方法

つまり、今回は「大量INSERT中になにかしらのDBコンテナ側の起因でコネクションが中断された」と推測されます。
DB側でコネクションが中断されても、GoクライアントはFIN通知を受け取るわけではないのでそのコネクションは生きていると判断します。Goクライアントからクエリパケットを送る際に初めてパケット喪失をして気づき、 unexpected EOF のエラーを受け取る格好になります。
このようにならない為に、Goクライアント側で MaxLifeTime の設定をし、Goクライアント側で接続を1秒毎に切っておく対処を追加することで解消されるのかと。

なぜMySQLでエラーが発生したのか

ここまででは、 unexpected EOF が発生する理由と解決方法はわかったのですが、そもそもなぜMySQLでエラーが発生してしまったのかという根本原因が分かっていません。

MySQLコンテナ内のログを確認してみると、 ibuf cursor restoration fails というエラーログがでていることが分かりました。これ自体の意味はよく分かっていないのですが、 MySQL5.7.31でfixされたようです。
https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-31.html

最終的な解決手段

そこで、当時最新であったMySQL5.7.32にアップデート(ローカルにある5.7のimagreをいったんrmして、再起動)して、 SetConnMaxLifetime の実装を削除して、無事動作することを確認しました。
つまり、バージョン上げるだけで解決しました。

これから

本番環境やステージング環境ではAWSのAuroraを使っており、Auroraでも同様の問題が発生してしまうのかの確認が必要です。
それはこれからの話なので、進展ありましたら、記載します。

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

知識の無い新卒がNext.jsとASP.NET CoreでTodoアプリを作成しコンテナ化したお話

こんにちは、新卒のますだです。※あだ名は絶賛募集中です:cry:
本記事は弥生 Advent Calendar 2020の12日目の記事になります。
12/12とバランスが良かったのでこの日を選びました。

一か月ちょっとでTodoアプリを作成しました。
「どういった流れで新しい技術を学び、成果物を作成したのか」
について当時のますだの心情と共に、簡潔に書いていきたいと思います。
温かい目で見て頂けると幸いです。

簡単な自己紹介

  • 2020年に弥生に新卒として入社

  • 基本的なjavaの知識はある

  • 楽観的でポジティブ

mgramでは「異常に怖いもの知らず」という診断結果がでました。
また、mgramは入社直後と入社半年後で二回診断したのですが、
運命の人と出会う確率が1/3188から1/10235に下がったので、結婚願望が強い人は仕事をしない方が良いと思いました。

上司からの依頼(トレーニング内容)

  1. Next.jsだけを使ってTodoアプリを作成せよ!(Visual Studio Codeで作成)

  2. ASP.NET Coreでバックエンドサービスを作成せよ!!(Visual Studioで作成)

  3. 1と2で作成した成果物を繋げよ!!!

  4. 3で作成した成果物をDockerでコンテナ化せよ!!!!(Visual Studio Codeで作成)

要件:
 ・タスク一覧が見られること
 ・タスクが追加・編集・削除できること
 ・タスクは以下の項目を持つこと。タスク名・詳細・期日・完了フラグ
 ・作成した成果物はgitLabにあげよ。

ますだの心情

  • gitってどう使うんだっけ...

  • Next.js...?

  • ASP.NET Core...??

  • 繋げる...???

  • Docker...????

「??????????????????????」と脳内は?だらけでした。

Next.jsとASP.NET Coreとdockerを簡単に説明

  • Next.js

    • ZEIT社によって開発されたJavascriptのフレームワーク
    • ReactのSSR・SSGを実現。簡単にいうと、ブラウザ上ではなくサーバー側でデータをレンダリングし、HTMLファイルを表示する。
    • メリット
      • サーバー側でレンダリングするためコンテンツの表示が早い
      • SEOの向上
      • ルーティングが楽 など
  • ASP.NET Core

    • マイクロソフト社が提供しているWebアプリケーション開発フレームワーク
    • マルチプラットフォーム(Windows,Linux,Mac)で動作する.NET環境
    • メリット
      • OSに依存せず開発できる
      • WEBブラウザで表示できるためデバッグが楽 など
  • docker

    • Docker社によって開発されたコンテナ型の仮想環境を作成、配布、実行できるプラットフォーム
    • ユーザーが作成した環境やソースコードなどをパッケージ化(コンテナ化)することができる。  コンテナ化したものは他のパソコンで実行することが可能
    • メリット:
      • ユーザーが開発したアプリケーションを環境構築せずに実行できる
      • メモリやディスクの消費量を少なく抑えた状態で仮想化するため、立ち上げるスピードが速い

作成した成果物

  • 実際のアプリケーション
    ※今回はトレーニングでTodoアプリを作成したためcssは無視しています。
    training.gif

  • 以下の画像はTodoアプリの繋がりを示しています。

    • トレーニング1.Next.jsだけでTodoアプリを作成の全体図: Next.js.jpg
    • トレーニング2.3. 4. Next.jsとASP.NET CoreでTodoアプリを作成の全体図: ASP.NET CoreとNext.js.jpg
  • 成果物を作成した基本的な手順は

  1. 公式チュートリアルをプレイ
  2. パワーポイントで実装のイメージを書きだし検証
  3. 検証した内容を上司に見せる
  4. 実装
  5. レビュー です。 ※手順や検証は我流です

トレーニング1. Next.jsだけを使ってTodoアプリを作成せよ!

  • まずはNext.jsの公式チュートリアル(英語が読めないため日本語訳サイト)をプレイ
    ますだ「なるほど、こういう動きをするのか。」(体感進捗率:10%)
  • 次は実装する前にtodoアプリの検証
    ますだ「一文字も書けん:scream:」(体感進捗率:5%)
  • 一文字も書けなかったため、Next.jsでtodoアプリを作成しているサイトのコードを理解
    ますだ「todoアプリを作成できそうだ:smile:」(体感進捗率:10%)
  • 再度todoアプリの検証
    ますだ「DB的な役割を持つファイル(todoList.js)はどうやって作成すればいいんだ...:cold_sweat:」(体感進捗率:5%)
  • 上司の方に相談
    Uさん「API Routesを使用し、サーバー側でtodoの値を保持しよう!API Routesを使えばトレーニング3. にも活かせるはず」
    ますだ「API Routesを使うのか。よし勉強だ!!:muscle:」(体感進捗率:10%)
  • API Routesの基本的な動きが分かったため再度todoアプリの検証 ※[1]ここでのの検証内容
    ますだ「いけそうかも:yum:」(体感進捗率:50%)
  • todoアプリを実装
    ますだ「うごいたぞ!:sunglasses:」(体感進捗率:80%)
  • ここで上司の方にレビュー依頼
    Uさん「CRUDのDELETEにbodyはNG。queryを使用しよう!」
    ますだ「:fearful:」(体感進捗率:20%)
  • DELETEにbodyではなくqueryを持たせるためにダイナミックルーティングを使用して再度実装※[2]ここでのの検証内容
    ますだ「できだ:japanese_goblin:」(体感進捗率:100%)

※[1]ここでの検証内容
[1].jpg

※[2]ここでのの検証内容
[2].jpg

トレーニング2. ASP.NET Coreでバックエンドサービスを作成せよ!!

  • まずは公式のチュートリアル(日本語版の公式チュートリアルはコードが載っていない部分が多いため英語版を推奨)
    ますだ「チュートリアルの内容がtodoアプリの作成だと...ありがてぇ:joy:
    ますだ「Visual Studioが自動で色々と作成してくれる...賢けぇ:innocent:」(体感進捗率:30%)
  • 大体の流れは掴めたため検証に入る
    ますだ「公式チュートリアルの内容を少し変えるだけで動きそうだ:smirk:」(体感進捗率:50%)
  • いざ実装。動作の確認はPostmanを使用
    ますだ「動いた...Postmanすごく便利:two_hearts:
    ますだ「Visual Studioが勝手にコードを書いてくれたから達成感が無い:innocent:」(体感進捗率:100%)

トレーニング3. 1と2で作成した成果物を繋げよ!!!

  • まずは公式のチュートリアルをプレイ
    ますだ「Visual Studioを使用し、Javascript(front)とASP.NET Core(backend)を繋げる方法は分かった:neutral_face:
    ますだ「が、Next.js(front)はVScodeを使用している...どうすればいいんだ:tired_face:」(体感進捗率:10%)
  • ますだの力ではGoogle先生だけで解決することができなかったため、Uさんに相談
    Uさん「Next.jsはVScode、ASP.NET CoreはVisual Studioで動かせます。」
    ますだ「そんなことできるのか...:flushed:」(体感進捗率:30%)
  • Visual Studioでbackend(ASP.NET Core)を起動し、起動したURIにリクエストを投げれば動いた
todo.js
//todoを追加する処理
const uri = "<Visual Studioで起動したuri>";
case "POST":
      await fetch(uri, {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: todoJson,//追加したいtodo
      }).catch((error) => console.error("Unable to add item.", error));
      res.status(200).json(todoJson);
  • 無事Next.jsとASP.NET Coreを繋げることができたが、期日が未入力の時だけうまく動かない
    ますだ「エラーもでねぇ...デバッグしまくるしかねぇ...:astonished:」(体感進捗率:30%)
  • デバッグした結果、C#(ASP.NET Core)が期待通りに動いていないことが分かった。原因はC#のdate型
    ますだ「どうやらC#は特殊な宣言(Null許容型)をしないとdate型にnullが入らないらしい:weary:」(体感進捗率:80%)
TodoItem.cs
//型の後ろに?を付けるとNull許容型になるらしい
public DateTime? Date { get; set; }
  • 数多の困難を乗り越え無事に実装完了
    ますだ「:sob:」(体感進捗率:100%)

トレーニング4. 3で作成した成果物をDockerでコンテナ化せよ!!!!

  • いよいよ最後のトレーニング。まずは恒例の公式チュートリアル(日本語翻訳)を読む
    ますだ「いや、まったく分からん:innocent:」(体感進捗率:0%)
  • とりあえずマイクロソフトのドキュメントを参考にコンテナ化を試みる
    ますだ「Visual Studioが賢すぎて勝手にコンテナ化できてしまった...:innocent:」(体感進捗率:3%)
  • 仮想化の概念も分からないため噛み砕いて説明してる動画をyoutubeで視聴
    ますだ「これが...無料!?:anguished:」(体感進捗率:20%)
  • Dockerを抽象的に理解したため検証に入る
    ますだ「...:innocent:」(体感進捗率:2%)
  • ここで相談役のKさんに自分の理解を伝えた。Kさんと話す中でDockerの理解がどんどんと深まった
    ますだ「お忙しい中何度も助けて頂き、感謝感激雨霰です:zap:」(体感進捗率:50%)
  • 今度こそDockerをそこそこ理解できたため検証し、実装に入る
    ますだ「書ける、書けるぞ!!:laughing:」(体感進捗率:80%)
  • VScodeでNext.jsとASP.NET Coreをコンテナ化(docker-compose)しようと試みたが期待通りに動かない。 どうやらASP.NET Core側でエラーが起きている。
    ますだ「Visual Studioに自動で作成して頂いた箇所がうまく動かない...Visual Studioに頼りすぎは良くない:persevere:
    (体感進捗率:50%)
  • Kさんのお力添えもあり、なんとかエラーの原因を突き止めた。どうやらbuildするときの階層が違ったらしい。
    ますだ「Visual StudioでDockerを実行すると自動で階層を指定してくれるらしい...:disappointed_relieved:」(体感進捗率:70%)
docker-compose.yml
//VScodeではDockerfileがある階層をしっかりと指定してあげる
build:
      context: backend/SampleApi
  • うまくビルドできた!!が、todoアプリがうまく動かない。色々と調査し、 backend(ASP.NET Core)のコンテナとfront(Next.js)のコンテナを繋ぐネットワークの作成が必要と考えた
    ますだ「世の中うまく進まねぇ...:sweat_smile:」(体感進捗率:50%)
  • 自分で実装する前に、Kさんに自分の理解を伝えた
    ますだ「ネットワークを作成する必要がありますよね??:sweat:
    Kさん「host.Docker.internalを使えばそんなことしなくてもいけるはず」
    ますだ「:flushed:」(体感進捗率:80%)
  • host.Docker.internalを使用し、いざ実行!
    ますだ「う、うごいた:sob:」(体感進捗率:100%)

さいごに

ここまで読んで頂きありがとうございました。
これがますだの一か月ちょっとの奮闘記でした。
ITリテラシーが低く苦労した点はたくさんありましたが、
周りの方に支えられ、なんとか動くものを作り上げることができました。
お忙しい中、質問に答えて頂きありがとうございました。特にUさんとKさんにはとてもお世話になりました!
今後はこの経験を活かし、新しい技術をどんどん取得していきたいと考えています!

明日の記事はnaoya_kimuraさんの「Visual Studioのパフォーマンスプロファイラーを使ってみた」です。私のフレッシュな記事とは違い、とても綺麗でわかり易い文章になってました!
お楽しみに!

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

Cloud9 × Raisl × Docker × MySQL で環境構築

はじめに

cloud9を使ったDockerの環境構築の記事が少なく戸惑ったこと、自身のアウトプットのために執筆します。

別の記事でも記載していますが、現在転職活動中の身ですのでプロではありません。
間違いなどありましたらご指摘いただければ、自身でも調べ速やかに修正いたします。

参考記事:
丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)

ファイル内容は上記サイトから頂きましたが、port8080に対応するため一部編集を加えています。

目標

タイトルの構成で環境構築をし、「Yay! You’re on Rails!」を表示させる。

環境

Amazon linux
Ruby 2.6.3
Rails 5.2.4.4
MySQL 5.5.62
Docker 19.03.6
Docker-compose 1.27.4

やってきましょう

インスタンスのボリュームを増やす

cloud9を立ち上げてできるインスタンスはサイズが10GBとなっているので、容量が不足する可能性があります。
その都度不要ファイルを削除できればいいのですが、手っ取り早くコンソールから容量を増やしました。
(極少額の課金が発生するかもしれません、料金はご自身で調べてみてください)

まずはcloud9を立ち上げます。
その後EC2の画面に行くと、cloud9と同時に立ち上がったインスタンスがあります。

EC2のサイドメニューから下記の順番で選択します。
1.ボリューム
2.先ほど立ち上がったインスタンスのチェックボックス
3.アクション
4.ボリュームの変更

スクリーンショット 2020-11-29 190704.png

開いたウィンドウのサイズを16GBに変更
スクリーンショット 2020-11-29 190843.png

こうなればOK
スクリーンショット 2020-11-29 190917.png

インスタンスの状態がoptimizing(黄色のマーク)なので、10分程度放置し終わるのを待ちます。
スクリーンショット 2020-11-29 191119.png

状態が緑のマークになると、サイズも16GBとなり反映が確認できます。
スクリーンショット 2020-11-29 193501.png

cloud9のターミナルに移動します。
以下のコマンドで内部からもサイズ変更が反映されているかを確認します。

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        483M   60K  483M   1% /dev
tmpfs           493M     0  493M   0% /dev/shm
/dev/xvda1       16G  8.6G  7.0G  55% /

もしリロードしても反映されていないようなら次のコマンドを試してみてください。

$ sudo growpart /dev/xvda 1
$ sudo resize2fs /dev/xvda1

Docker-Composeのインストール

インストールの前に このページ で最新のバージョンを確認しておいてください。

下の1.27.4の箇所を確認した最新のバージョンに書き換えてインストールします。
私は"/home/ec2-user/environment"ディレクトリで実行しています。

$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 

$ docker-compose コマンドを実行できるように権限を与えます。

$ sudo chmod +x /usr/local/bin/docker-compose

バージョンの確認をして、表示されればインストールは完了です。

$ docker-compose --version
docker-compose version 1.27.4, build 40524192

ファイルの準備

プロジェクトディレクトリを作成して、その中に移動します。

$ mkdir myApp
$ cd myApp

そうしたら必要なファイルをtouchコマンドで作成していきます。
ここでファイルは4つ用意します。

$ touch Dockerfile
$ touch Gemfile
$ touch Gemfile.lock
$ touch docker-compose.yml

通常はviコマンドなどでファイルを書いていきますが、cloud9はせっかくエディタが付いているのでそちらで作業しましょう。

なおGemfile.lockは記載せずに空のままで大丈夫です。

各ファイルの項目は Docker × Ruby on Rails × MySQLの環境構築 この記事を参考にしてください。

Dockerfile
FROM ruby:2.6.3
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs           
RUN mkdir /app_name 
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock
RUN bundle install
ADD . $APP_ROOT

FROM ruby:2.6.3

cloud9にはあらかじめrubyがインストールされています。
以前同様の環境構築をした際、インストール済みのバージョンとここで指定したバージョンが異なりエラーになりました。(原因は他にあるのかもしれませんが、2つのバージョンが競合したのか?)

その為ここのrubyのバージョンは$ ruby -vで確認したインストール済みのバージョンと同様にしてあります。

Gemfile
source 'https://rubygems.org'
gem 'rails', '5.2.4.4'

rails5系で現在最新のバージョンを指定。Ruby on Rails最新情報

docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: パスワード
      MYSQL_DATABASE: データベース名
    ports:
      - "3306:3306"

  web:
    build: .
    command: rails s -p 8080 -b '0.0.0.0'
    volumes:
      - .:/app_name
    ports:
      - "8080:8080"
    links:
      - db

command: rails s -p 8080 -b '0.0.0.0'

ports:
- "8080:8080"

cloud9ではport8080でサーバーに接続します。
その為、ホスト:コンテナ の両方を8080番で指定しています。

パスワードとデータベース名は任意の値を記載します。

これで設定を記載した3つのファイルと、空のGemfile.lockができました。

Railsプロジェクト作成

カレントディレクトリがプロジェクトディレクトリ(今回は/home/ec2-user/environment/myApp)であることを確認します。

そうしたらrails newを実行してアプリケーションの型を作ります。

$ docker-compose run web rails new . --force --database=mysql --skip-bundle

問題がなければプロジェクトディレクトリ配下にRailsのひな形が出来上がります。

$ ls
app  bin  config  config.ru  db  docker-compose.yml  Dockerfile  Gemfile  Gemfile.lock  lib  log  package.json  public  Rakefile  README.md  storage  test  tmp  vendor

データベース周りの設定のため/myApp/config/database.ymlを修正します。

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: パスワード
  host: db

パスワードの箇所には先ほど"docker-compose.yml"で設定したのと同様の値を記載します。
ここで保存しようとすると、恐らくcloud9の上部に警告文が表示されます。

EACCES: permission denied

これはdatabase.ymlに対する書き込み権限が無いので保存できない旨の内容なので、権限を変更指定保存できるようにします。

/myApp/configに入り、ファイルの権限を確認します。

$ cd config
$ ls -l
-rw-r--r-- 1 root root 1657 Nov 29 19:06 database.yml

書き込み権限を変更し、反映されればdatabase.ymlを保存できるようになります。

$ sudo chmod 666 database.yml
$ ls -l
-rw-rw-rw- 1 root root 1657 Nov 29 19:06 database.yml

Dockerコンテナのビルド

プロジェクトディレクトリに戻り、コンテナを立ち上げます。

$ docker-compose build

PermissionError: [Errno 13] Permission denied: '/home/ec2-user/environment/myApp/config/master.key

このエラーが出るのは先ほど同様myApp/config/master.keyに対するパーミッションエラーなので、権限を変更します。

ディレクトリを移動して権限を確認。

$ cd config
$ls -l
-rw------- 1 root root   32 Nov 29 19:06 master.key

権限を変更して反映を確認します。

$ sudo chmod 666 master.key
$ ls -l
-rw-rw-rw- 1 root root   32 Nov 29 19:06 master.key

再トライ

$ docker-compose build
$ docker-compose up

これで私はうまくいきました。

データベースの作成

コンテナが起動したらショートカットキーAlt + Tでcloud9にターミナルをもう一つ立ち上げます。

開いたターミナルからプロジェクトファイルに移動して、データベースを立ち上げます。

$ cd myApp
$ docker-compose run web rails db:create

成功していれば Preview → Preview Running Application で「Yay! You’re on Rails!」が表示されます。
この時

**********.vfs.cloud9.ap-northeast-1.amazonaws.com で接続が拒否されました。

と表示されたら、右上の矢印ボタンを押して別タブで開けば表示されます。

サーバーを停止させる場合はCtrl + Cではなく、新しく開いた方のターミナルで下記を入力します。

$ docker-compose down

スクリーンショット 2020-11-26 184833.png

その他のエラー

Can't connect to MySQL server on 'db' (115)

コンテナが立ち上がり切っていない時に表示されます。
数秒待ってからリロードすれば表示内容が変わります。

Unknown database 'app_name_development'

データベースを作成していないとこの表示がされます。
新しいターミナルを開いてデータベースを作成すれば解消されます。

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

ブラウザアプリの Docker 環境でリバースプロキシが役に立った

:mailbox_with_mail: 背景

むかしむかしあるところに、某広告効果測定ツールの開発チームがいました。

そこには、 Web サイトを訪問した際に裏で別ドメインの JavaScript を読み込んで実行し、そのスクリプトがさらに別ドメインの PHP にデータを送信してアクセスを計測する、という仕組みがありました。

チーム内で管理されているソースの中には JavaScript 中心のアプリ A と PHP 中心のアプリ B が別々に存在して、各々が開発環境の Docker 化を進め、各々がホスト PC の localhost の 80, 443 番ポートをコンテナと直接繋いでいました。

アクセスを計測する Web サイトもダミーサイト C を作り、同じようにポートを接続していました。

ところがある日、結合テストのフェーズになり、アプリ A と B 、ダミーサイト C で計 3 つのドメインを振り分けなければいけなくなりました。

すると、アプリたちがこぞって localhost のポートを取り合い、喧嘩になったのです。

スクリプトの読み込みにポート番号を付けてアクセスしたりはしないので、ポート番号を変えることもできません。

喧嘩の末、ダミーサイトが localhost を取り、他のアプリたちは仕方なく VirtualBox で VM を立ち上げ、各自の IP を hosts ファイルに書いてもらうという、古い方法を使うことになりました。

port-toriaikko.png

仲良く Docker を使うためにはどうすればよかったのでしょうか。

:thinking: 方法を考える

Docker Desktop で開発するということを前提に次の 2 通りの方法を考えました。

:one: すべて同じコンテナに入れてしまう

1 つのコンテナの中で各アプリをディレクトリを分けて配置し、バーチャルホスト等の設定で ServerName に応じた割り振りをします。

hosts ファイルは次のように記述します。

127.0.0.1 app-a
127.0.0.1 app-b
127.0.0.1 site-c
  • メリット
    • 構成はシンプルに見える
    • バーチャルホストさえ書ければ楽
  • デメリット
    • コンテナが重くなる

:two: アプリごとにコンテナを分けてリバースプロキシによりアクセスを割り振る

今回試そうと思っている方法はこちらです。アプリとダミーサイト個々のコンテナを用意しますが、それらに直接ポートの設定をするのではなく、間にリバースプロキシを挟みます。

ブラウザからのアクセスは最初はすべてリバースプロキシを通り、そこから ServerName によってアプリやダミーサイトにアクセスがいきます。

下の図でイメージしていただければ幸いです。

私自身、リバースプロキシの理解には こちらの記事 を参考にさせていただきました。

reverse-proxy-figure.png

hosts ファイルの中身は :one: の方法と同じです。

  • メリット
    • コンテナを小分けにできる
  • デメリット
    • 構成は少し複雑
    • 本番と構成が違う場合があり、好まない人が多い

:fire::meat_on_bone::fork_and_knife: リバースプロキシを作ってみる

こちら にソースコードがありますので、ぜひお試しください。

ファイルツリーは以下のようになっています :deciduous_tree:

│  docker-compose.yml
│
├─app-a
│      index.html
│      index.js
│
├─app-b
│      index.php
│
├─site-c
│       index.html
│
└─nginx
   └─conf.d
           reverse-proxy.nginx.conf

ファイルの概要を紹介しておきます。

:page_facing_up: ./docker-compose.yml

アプリとサイトそれぞれにコンテナを用意しますが、ポートはリバースプロキシにしか開けません。

./docker-compose.yml
version: '3'

services:
  app-a:
    container_name: app-a
    image: httpd:alpine
    volumes:
      - ./app-a:/usr/local/apache2/htdocs
  app-b:
    container_name: app-b
    image: php:apache-buster
    volumes:
      - ./app-b:/var/www/html
  site-c:
    container_name: site-c
    image: httpd:alpine
    volumes:
      - ./site-c:/usr/local/apache2/htdocs
  reverse-proxy:
    container_name: reverse-proxy
    image: nginx:alpine
    volumes:
      - ./nginx/conf.d/reverse-proxy.nginx.conf:/etc/nginx/conf.d/reverse-proxy.nginx.conf
    ports:
      - "80:80"

:page_facing_up: ./nginx/conf.d/reverse-proxy.nginx.conf

コンテナ間の通信がコンテナ名で行えることを利用します。

./nginx/conf.d/reverse-proxy.nginx.conf
server {
    listen 80;
    server_name app-a;

    location / {
        proxy_pass http://app-a/;
        proxy_redirect off;
    }
}

server {
    listen 80;
    server_name app-b;

    location / {
        proxy_pass http://app-b/;
        proxy_redirect off;
    }
}

server {
    listen 80;
    server_name site-c;

    location / {
        proxy_pass http://site-c/;
        proxy_redirect off;
    }
}

:page_facing_up: ./site-c/index.html

この中で app-a/index.js が読み込まれ、さらにその Javascript が app-b/index.php を読み込むようになっています。

./site-c/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
<script type="text/javascript" src="//app-a/index.js"></script>
<div id="resp">Your UA is...</div>
</body>
</html>

:page_facing_up: ./app-a/index.js

文字を出力した後、 http://app-b/ にリクエストを送ります。

./app-a/index.js
document.write("Hello Javascript");

const request = new XMLHttpRequest();
request.open('GET', 'http://app-b/', true);
request.responseType = 'json';
request.addEventListener('load', () => {
    document.getElementById('resp').innerHTML = request.response['HTTP_USER_AGENT'];
});
request.send();

:page_facing_up: ./app-b/index.php

今回は 無駄に 2 秒待ってから UA を返すスクリプトを書いてみました。

./app-b/index.php
<?php
header('Content-type: text/json; charset=utf-8');
$allowOrigin = 'http://site-c';
header("Access-Control-Allow-Origin: ${allowOrigin}");
$resp = array('HTTP_USER_AGENT' => $_SERVER['HTTP_USER_AGENT']);
sleep(2);
echo json_encode($resp);
exit;

:page_facing_up: ./app-a/index.html

空ファイルです。

./app-a/index.html

:computer: 実行する

コンテナをビルドして、

docker-compose up -d

ブラウザで http://site-c/ にアクセスして 2 秒くらい待つと...

reverse-proxy-screenshot.png

表示できました! :tada:

リバースプロキシの実装にあたっては こちらの記事 を参考にさせていただきました。

:bulb: まとめ

リバースプロキシを使うことでコンテナ 1 つ 1 つがとても軽くできたので、そこは良かったと思います。

みなさんも Docker でポートの取り合いになったときは、このリバースプロキシを検討してみてください。

(リバースプロキシには、各サーバへの負担を分散したり、リバースプロキシ自体がファイアウォールとして機能するといった利点もあります。)

注意

この記事は、決してリバースプロキシが最善の方法 :angel: であるとか、それ以外の方法はダメ :imp: だということを示すものではありません。状況に応じて適切に方法を選ぶようにしてください。

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

Traefikでリバースプロキシ on Docker

Traefikとは

https://traefik.io/traefik/
サービスの公開が簡単で楽しいものになるオープンソースのエッジ―ルーター。
Path,Host,Headerなどでリクエストをルーティングしてくれます。
後、WebUIやら負荷分散などもあったり多機能。

やりたいこと

今回はRaspberryPi4上で動いているhttps-portalからTraefikに移行します。
ちなみにhttps-portalもリバースプロキシで自動でSSLの証明書の発行などをしてくれるnginx-proxyという感じ。
設定ファイルの記述などで混乱したので備忘録。

環境

  • RaspberryPi 4
  • Traefik 2.3.4
  • docker-compose

https://hub.docker.com/_/traefik
https://github.com/traefik/traefik/

docker-compose.yml (Traefik)

docker-compose.yml
version: '3'
services:
  traefik:
    image: traefik:v2.3.4
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml
      - /data/ssl_certs/traefik/acme.json:/etc/traefik/acme.json
    networks:
      - traefik
    env_file:
      - dns_challenge_secrets.env
networks:
  traefik:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.30.0.0/24

ポート

8080 はダッシュボード用です。

ストレージ

    volumes:
      # Docker全体のイベントを監視するため
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Traefikの設定ファイル
      - ./traefik.yml:/etc/traefik/traefik.yml
      # ACME認証情報が保存されるファイル
      - /data/ssl_certs/traefik/acme.json:/etc/traefik/acme.json

ネットワーク

networks: # Traefikが管理するネットワーク。よしなに。
  traefik:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.30.0.0/24

traefik.yml

traefik.yml
api:
  dashboard: true
  insecure: true

entryPoints:
  http: # エントリーポイント名
    address: ":80"
  https:
    address: ":443"

providers:
  docker:
    network: sample_traefik
    exposedByDefault: false

certificatesResolvers:
  letsencrypt: # resolver名
    acme:
      email: admin@example.com
      storage: /etc/traefik/acme.json
      dnsChallenge:
        provider: cloudflare

ポイントは

  • entryPoints ... エントリーポイントを設定。 docker-compose側も忘れずに。
  • providers: docker: ... プロバイダをDockerに設定。
  • network ... ネットワーク名
  • exposedByDefault ... 有効にしていると、ポートがexposeされている全てのサービスを登録されてしまいます。 今回は無効にするので各サービスにtraefik.enable=trueすることで管理してもらいます。 certificatesResolvers ... SSLの認証関係。 今回はLet's EncryptでcloudflareのドメインをDNS-01チャレンジで証明書を発行します。 プロバイダ一覧 https://doc.traefik.io/traefik/https/acme/#providers

docker-compose.yml (サービス)

最後にサービス側の設定

docker-compose.yml
services:
  web:
      build: .
      ports:
        "8000:80"
      labels:
        - traefik.enable=true
        - traefik.http.routers.servicename.rule=Host(`blabla.example.com`)
        - traefik.http.routers.servicename.entrypoints=https
        - traefik.http.routers.servicename.tls=true
        - traefik.http.routers.servicename.tls.certresolver=letsencrypt

networks:
  default:
    externa:
      name: sample_traeeafik

とりあえずTreafikのネットワークに接続します。
そしてTraefikの各種設定を labelsに追加していきます。

  • traefik.enable ... exposedByDefault: false の時に必須。
  • traefik.http.routers.servicename.rule ... ルーティングのルールを設定します。
    • Host(blabla.example.com) ... Hostでフィルタリングします。
  • traefik.http.routers.servicename.entrypoints ... 設定したエントリーポイントのどれを使うか
  • traefik.http.routers.servicename.tls ... tlsを有効に
  • traefik.http.routers.servicename.tls.certresolver ... 設定した認証のリゾルバ名

これで後はTraefikが勝手にやってくれます。

最後に

nginx-proxyよりも多機能で、SSLの証明書の再発行などもこれ一つで勝手にやってくれます。
更新も頻繁なこともあり、ネット上に微妙に違う設定の情報が多くややこしかったのでメモ。

参考

https://doc.traefik.io/traefik/v2.3/providers/docker/
https://dev.to/cedrichopf/get-started-with-traefik-2-using-docker-compose-35f9

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