20201208のdockerに関する記事は21件です。

チャンプルーDocker環境(Rails+Laravel+MySQL)

はじめに

書くネタがなかったのでニッチかもしれませんが、タイトルの通りRails+Laravel+MySQLのDocker環境を構築してみました。突貫で構築したので所々至らぬ点や改善点があるかと思いますのでお手柔らかにアドバイス頂けると助かります:bow_tone1:
ちなみにチャンプルーとは沖縄の方言で『ごちゃまぜ』という意味です:cooking:

環境

macOS Catalina 10.15.7
Docker 19.03.13
Compose 1.27.4
Rails 6.0.3(Ruby 2.7.0)
Laravel 7.30.0(PHP 7.2.34)

フォルダ構成

.
├── laravel_app
│   ├── laravel
│   │   ├── app
│   │   ...
│   └── Dockerfile
├── rails_app
│   ├── app
│   ├── bin
│   ...
│   └── Dockerfile
└── docker-compose.yml

Rails+MySQL環境

以前書いた記事を参考に構築しました:pencil2:
RubyをインストールせずにDockerでRails環境を構築する

※変更点としてはフォルダパス(rails_app)が変わっているのとサービス名をRailsLaravelでわかりやすいように変えています。あとはほとんど記事と同じ手順でRails+MySQL環境は構築出来ました:tada:

version: '3'
services:
  rails6:
    build: ./rails_app/
    environment:
      RAILS_ENV: development
      TZ: Asia/Tokyo
    ports:
      - '3000:3000'
    volumes:
      - ./rails_app/:/usr/src/app
    depends_on:
      - database

Laravel環境

まず、Laravel用のDockerfileを用意します。

laravel_app/Dockerfile
FROM php:7.2-fpm
# install composer
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim
RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql
WORKDIR /var/www/html

次に、docker-compose.ymlにphpの部分を追記します。

docker-compose.yml
version: '3'
services:
  php:
    build: ./laravel_app/
    volumes:
      - ./laravel_app/:/var/www/html
    ports:
      - '8000:8000'
    depends_on:
      - database
  rails6:
...

ここで一度buildを行い、ComposerでLaravelプロジェクトを作成します。

$ docker-compose up -d --build
$ docker-compose exec php composer create-project --prefer-dist laravel/laravel ./laravel

これでlaravel_appフォルダ内にlaravelプロジェクトが作成されますのでMySQL接続情報を設定します。

laravel_app/laravel/.env
DB_CONNECTION=mysql
DB_HOST=database
DB_PORT=3306
DB_DATABASE=app_development
DB_USERNAME=root
DB_PASSWORD=password

最後にLaravelのビルトインサーバを起動するコマンド(php artisan serve)をDockerfileに定義して完成です。

laravel_app/Dockerfile
FROM php:7.2-fpm
# install composer
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim
RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql
WORKDIR /var/www/html/laravel
EXPOSE 8000
CMD ["php","artisan","serve","--host","0.0.0.0"]

それぞれhttp:localhost:3000とhttp:localhost:8000でアクセスして確認出来ます:santa:
Rails_Laravel.png

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

Go + wire と Docker で DI してみた

この記事はぷりぷりあぷりけーしょんず Advent Calendar 2020の9日目の記事です。

はじめに

今年学んだことのアウトプット第二弾です。
本記事に書く内容に関しては、結構前に実装した際のお話になります。
また、個人開発ではなくペアプロ(@MSHR-Dec さんとの共同開発)した際のものとなります。とても楽しかった開発でした?
結構月日が立ってしまった為、忘れないうちに記事に落とし込もうと思います。

本記事で話さないこと

  • Go のセットアップ
  • Go の書き方
  • Docker のセットアップ
  • 今回作成したプロジェクト詳細
  • 個人的ニュース

DI とは

DI (dependency injection) はよく耳にした方が多いかと思われます。
調べると「 オブジェクトの注入 」とのような直訳した意味が多く見つかります。
こちらをもう少し噛み砕いて説明すると、オブジェクト指向プログラミングにおける開発手法の1つであり、インスタンスの生成や管理を行ってくれます。
オブジェクト指向な言語のフレームワークでは結構 DI が採用されています。

Go の DI ライブラリ wire

こちらの本題に入る前に、そもそもなぜ DI をしたかったのかを簡単に説明します。
本プロジェクトでは Clean architecture を採用しており(完璧な実装ではありませんが)、依存関係逆転の法則を守るために interface を用いて型を定義し、起動のタイミングでオブジェクトの注入を行いたかった。とか、各レイヤーで単体テストを書きたかったためモックと実際のロジックをいい感じに差し込みたかった。など、interface の定義が多く、起動のタイミングやテスト実行のタイミングでインスタンスの生成をいい感じにしたかったため、DI ライブラリを導入しようとなったのが経緯となります。

wire とは

Google 製の DI ライブラリとなっており、2018 年 12 月に公開されました。
特別なビルドタグをつけた Go のコードをデータソースとして、コンパイルタイムでインジェクタのコードを生成してくれるみたいです。

wire 取得の説明の前に、本実装での Go のバージョンは以下となります。

$ go version
go version go1.13 darwin/amd64

wire は以下のコマンドで取得できます。

$ go get github.com/google/wire/cmd/wire

get が完了したら wire コマンドが使用できるかと思われます。

$ wire help
Usage: wire <flags> <subcommand> <subcommand args>

Subcommands:
        check            print any Wire errors found
        commands         list all command names
        diff             output a diff between existing wire_gen.go files and what gen would generate
        flags            describe all known top-level flags
        gen              generate the wire_gen.go file for each package
        help             describe subcommands and their syntax
        show             describe all top-level provider sets


Use "wire flags" for a list of top-level flags

実際に使ってみる

今回は repository 周りを例に見ていきたいと思います。
まずは interface の定義から

package repository

import (
    "api/src/domain/model"
)

type IArticleRepository interface {
    Create(article *model.Article)
    Update(article *model.Article)
    FindAll() []model.Article
    FindOne(ID uint64) model.Article
}

次に、その interface を実装した struct を定義します。
ちなみに、本プロジェクトは ORM を使用しており、Gorm ライブラリを使用しています。RDBMS は MySQL を使用しています。

package datastore

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "github.com/thoas/go-funk"

    "api/src/domain/model"
    "api/src/domain/repository"
)

type ArticleDatastore struct {
    db *gorm.DB
}

func NewArticleDatastore(d *gorm.DB) repository.IArticleRepository {
    return &ArticleDatastore{
        db: d,
    }
}

func (a *ArticleDatastore) Create(article *model.Article) {
    a.db.Create(&article)
}

func (a *ArticleDatastore) Update(article *model.Article)  {
    a.db.Save(&article)
}

func (a *ArticleDatastore) FindAll() []model.Article {
    var articles []model.Article
    a.db.Select("id").Find(&articles)

    results := funk.Map(articles, func(article model.Article) model.Article {
        a.db.First(&article).Related(&article.User, "User").Related(&article.Shop, "Shop").Related(&article.Categories, "Categories")
        return article
    }).([]model.Article)

    return results
}

func (a *ArticleDatastore) FindOne(ID uint64) model.Article {
    article := model.Article{ID: ID}
    a.db.First(&article).Related(&article.User, "User").Related(&article.Shop, "Shop").Related(&article.Categories, "Categories")
    return article
}

ここまで定義されたオブジェクトを使用する処理が以下となります。

package interactor

import (
    "time"
    "api/src/domain/model"
    "api/src/domain/repository"
    "api/src/handler/request"
    "api/src/usecase"
)

type ArticleInteractor struct {
    Repository repository.IArticleRepository
}

func NewArticleInteractor(
    repository repository.IArticleRepository,
) usecase.IArticleUsecase {
    return &ArticleInteractor{
        Repository: repository,
    }
}

func (a *ArticleInteractor) Create(ra *request.UpsertArticleRequest) uint64 {
    article := model.Article{
        Title:    ra.Title,
        Body:     ra.Body,
        Status:   ra.Status,
        UserID:   ra.UserID,
        ShopID:   ra.ShopID,
        CreateAt: time.Now(),
    }

    a.Repository.Create(&article)

    return article.ID
}

func (a *ArticleInteractor) Update(id uint64, ra *request.UpsertArticleRequest) {
    article := model.Article{
        ID:       id,
        Title:    ra.Title,
        Body:     ra.Body,
        Status:   ra.Status,
        UserID:   ra.UserID,
        ShopID:   ra.ShopID,
        CreateAt: time.Now(),
    }

    a.Repository.Update(&article)
}

func (a *ArticleInteractor) GetAll() []model.Article {
    return a.Repository.FindAll()
}

func (a *ArticleInteractor) GetOne(ID uint64) model.Article {
    return a.Repository.FindOne(ID)
}

本当は↑のオブジェクトも interface が定義されているのですが、今回は1つだけをサンプルとします。
最後に DI を定義していくのですが、ソースの先頭にビルドタグをつけるのを忘れないように気をつけましょう!
また、main パッケージの層にソースを配置しないとうまくインジェクタのコードを生成(wire generate)できませんでした。(ほんとは registory パッケージとかの中に入れたかったのですが)

wire.go
//+build wireinject

package main

import (
    "github.com/google/wire"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"

    "api/src/infrastructure/datastore"
    "api/src/interactor"
)

func InitArticleInteractor(d *gorm.DB) *interactor.ArticleInteractor {
    wire.Build(
        datastore.NewArticleDatastore,
        interactor.NewArticleInteractor,
    )
    return nil
}

こちらの定義だけだと、正直不安だらけでした笑
このソースでコンストラクタ引数にちゃんと必要なもの入れてくれるの?とか色々疑いまくりましたが、こちらのソースで実際の DI を generate したら以下のソースが吐き出されました。

$ wire src/wire.go
wire_gen.go
// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

import (
    "github.com/jinzhu/gorm"
    "api/src/infrastructure/datastore"
    "api/src/interactor"
)

import (
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

// Injectors from wire.go:

func InitArticleInteractor(d *gorm.DB) *interactor.ArticleInteractor {
    iArticleRepository := datastore.NewArticleDatastore(d)
    articleInteractor := interactor.NewArticleInteractor(iArticleRepository)
    return articleInteractor
}

こちらのソースが吐き出された時は感動しました。
勝手にコンストラクタ引数を詰めてくれて、return nil と記述していたのに、しっかり該当のインスタンスを返してくれているではありませんか!
generate してみて感動はしましたが、wire.go の書き方には慣れが必要そうだなという印象です。(当初はなかなか慣れることができませんでした笑)

あとはエントリーポイントである main.go で InitArticleInteractor を呼べば、wire_gen.go で定義した方のメソッドが呼ばれるため、依存関係が解決した状態のインスタンスを使用することができます。

Docker と wire で DI を自動 Generate

本プロジェクトは Docker を使用しており、コンテナのビルド・立ち上げの際に先程の wire generate ができたらいいなと思い、やってみました。
とは言っても、以下の Dockerfile を定義する感じです。こちらは @MSHR-Dec さんにたくさん助けていただきました。

Dockerfile
FROM golang:1.13.7-alpine3.11 as build

WORKDIR /api

ENV GO111MODULE=on

COPY . .

RUN go get github.com/google/wire/cmd/wire \
  && wire src/wire.go \
  && cd src \
  && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../main \
  && cd ..

FROM alpine

COPY --from=build /api/main .

RUN addgroup go \
  && adduser -D -G go go \
  && chown -R go:go ./main

CMD ["./main"]

あとはビルドして、立ち上げれば自動 generate してくれて API が起動します。

まとめ

アベンドカレンダー2記事目ということもあり、勢いで書き上げたため、ちょっとわかりにくい箇所が多いかと思われます。?‍♂️
不備などありましたらコメントどんどんください!
今回 DI ライブラリを使用してみて、慣れない箇所には時間がかかりそうでしたが、実際 generate でソースを吐き出してみると、結構な感動だったため、今後も Golang で開発をする際は、wire を用いて開発をしていこうと思います。(DI するような実装なら)

おまけ

本記事には載せられませんでしたが、本プロジェクトではホットリロードとして realize ってのを使用しています。こちらも感動ものでした。
また Dockerfile を見ていただければわかるかもですが、マルチステージビルドってのも初めて使いました。

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

dockerimageの作成方法について 〜コンテナからcommit・dockerfileからbuild〜

全体像

スクリーンショット 2020-12-08 22.41.09.png
上記図について以下で詳細を説明していく


コンテナに名前をつけて作る(コンテナからcommit)

参考画像
スクリーンショット 2020-12-04 15.20.08.png

通常コンテナを作る時は

docker run コンテナ名(ここには既にあるコンテナから引っ張ってきたいコンテナの名前を入れる)

この方法だと作成されたコンテナの名前はランダムになる

 docker run --name sample ubuntu
//上記のsampleが自分でつけるコンテナの名前 ubuntuは既に誰かかが作成済みの持ってきたいコンテナの名前 

上記のように--nameとコンテナ名を指定することでコンテナ名をつけられる。

※同じ名前のコンテナを作ることは出来ないので注意する※

docker run のオプション

detached mode
docker run -d <image>
コンテナ起動後にdetachする(バックグラウンドで動かす「STATUSがUpになった状態でrun出来る」)

(short-term)foreground mode
docker run --rm <image>
コンテナをExit後に削除する(一回きりのコンテナ)
docker run hello-world
のようにコンテナを一回起動して内容を確認→もう使わない
といった時に便利

dockerfileからの作成方法

dockerfileについて

docker hubでみたいdockerfileのあるdocker imageを検索(今回はubuntu)
スクリーンショット 2020-12-04 16.32.59.png

対象imageを選択
スクリーンショット 2020-12-04 16.29.23.png
スクリーンショット 2020-12-04 16.35.52.png

ページのにSupported tags~ Dockerfile linksがある

そこの見たいバージョンを選択(今回は18.04にする)
スクリーンショット 2020-12-04 16.29.47.png
そうするとdockerfileの記載のあるGithubへ移動出来る。
ここに記載されている今回であればFROM scratchからの記載内容がdockerfile
ここにその対象imageについての記述をしていくことでdockerfileを作っていく

dockerfileの作り方

dockerfileを作る時はファイル名は命名規則により「Dockerfile」とする
INSTRUCTION argumentsの形で書いていく
(例:「FROM ubuntu:latest」や「RUN touch test」)
基本最初はFROMから書いていくルール

fileの具体的な作り方(今回はデスクトップに作ることを想定)

スクリーンショット 2020-12-04 16.46.22.png

ターミナルを開き

PC ~ % cd desktop// デスクトップへ移動
PC desktop % mkdir docker// ドッカーフォルダ作成
PC desktop % cd docker // ドッカーフォルダへ移動
PC docker % code Dockerfile // Dockerfileというファイルを作成しvscodeを起動
//(codeはvscodeを開くターミナルのコマンド)この時点ではDockerのファイルは開いているだけで保存されていないので注意。vscodeのエディタ上でセーブしましょう(⌘+s)

以下の様にdockerfileを記述していく

 #ubuntuは元となるdocker image(:latestと書くことは必須ではない)
FROM ubuntu:latest  
 #ubuntu imageに対しtestファイルを作成している
RUN touch test

dockerfileをbuildする(buildによってdockerfileからdocerimageが作成される)

dockerfileのあるディレクトリへ移動する(仮にdockerfileがあるディレクトリを「Docker」とする)
cd Dockerを使用し対象ディレクトリへ移動。
対象のdockerfileのあるディレクトリへ移動したら

docker build .
//ここで使用している「.」はカレントディレクトリを指している(この部分に書くのはbuildをするディレクトリ)。
//現在いるディレクトリのdockerfileをbuildするという指定をする役割をしている
//基本はカレントディレクトリにbuildするので「.」で覚えればOK

これでbuild完了。

スクリーンショット 2020-12-08 21.49.58.png

docker build後にdocker imagesコマンドをすると
スクリーンショット 2020-12-08 21.59.06.png
一番上に<none>と書かれたdockerimageが作成されたことが確認できる。
docker build .をした際に名前をつけないと名前が<none>となる。
この名前のないimageをdangling imageと呼ぶ。
docker images -f dangling=trueとすることでdocker imagesの中からdangling imageのみを検索できる

名前をつける際は

docker build -t new-ubuntu:latest .
//new-ubuntu部分が名前になる。tag名:latestは指定しなくてもbuile可能。「.」は上述と同様可憐とディレクトリを指定。

docker buildによってdockerfileの内容が反映されているか確認

スクリーンショット 2020-12-08 22.26.17.png

docker run -it new-ubuntu bash
ls

上記コマンドでコンテナに入り確認するとtestというファイルが作成されていることが確認できる。
この「test」がdockerfileにてRUN touch testで記述した内容から生成されたもの。

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

ラズパイにUbuntuをインストール

はじめに

ラズパイにUbuntuServerをインストールしたので、備忘録として残しておく。

やったこと

  • ラズパイにUbuntuをインストール
  • Ubuntuの初期設定
  • dockerをインストール

環境

環境 詳細
PC MacBook Pro 10.15.4
ラズパイ Raspberry Pi 3 Model B+
ラズパイ OS Ubuntu Server 20.04.01 LTS

ラズパイにUbuntuをインストール

1. Raspberry Pi ImagerをMacBookにインストール

https://www.raspberrypi.org/software/

2. ラズパイOSを選択し、SDカードに書き込み

スクリーンショット 2020-12-07 2.27.06.png

Raspberry Pi Imager使うと楽に書き込みまでできる。

3. ラズパイの起動

SDカードをラズパイに挿入し、電源を入れる。

Ubuntuの初期設定(最低限やっていること)

1. Ubuntuにログイン

初回ログイン後、パスワードの変更を要求される。

初期ユーザ 初期パスワード
ubuntu ubuntu

2. Ubuntuのアップデート

以下のコマンドで、パッケージをアップデートする。

$ sudo apt-get update
$ sudo apt-get upgrade

3. Ubuntuの自動アップデート設定

パッケージ自動アップデートのためのパッケージをインストールし、有効にする。

$ sudo apt-get install unattended-upgrades
$ sudo dpkg-reconfigure -plow unattended-upgrades

自動アップデートの対象を以下のファイルで設定する。
/etc/apt/apt.conf.d/50unattended-upgrades

4. ssh設定

ホストキーを作成し、sshを再起動する。

$ sudo dpkg-reconfigure openssh-server
$ sudo service ssh restart

PCよりSSHでログインする。

$ ssh ubuntu@<ipアドレス>

5. ファイアウォールの設定

ufwをインストールする。

sudo apt-get install ufw

以下のファイルを編集して、IPv6を無効にする。
/etc/default/ufw

許可したポート以外を閉じる。
※sshの22番ポートのみ許可

$ sudo ufw default deny
$ sudo ufw allow 22
$ sudo ufw enable

許可したポートを確認する。

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere  

6. 時刻設定

ntpのインストールする。

$ sudo apt-get install ntp

以下の通り、ファイルに追記する。

/etc/ntp.conf
    server -4 ntp.nict.jp
    server -4 ntp1.jst.mfeed.ad.jp
    server -4 ntp2.jst.mfeed.ad.jp
    server -4 ntp3.jst.mfeed.ad.jp

ntpを再起動する。

sudo service ntp restart

ntpの設定を確認する。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 ntp.ubuntu.com  .POOL.          16 p    -   64    0    0.000    0.000   0.000
*ntp-a2.nict.go. .NICT.           1 u   56   64  377    5.446   -0.311   1.257
+ntp1.jst.mfeed. 133.243.236.17   2 u   55   64  377    5.478    0.662   1.409
+ntp2.jst.mfeed. 133.243.236.18   2 u   31   64  377    5.884    0.775   1.186
-ntp3.jst.mfeed. 133.243.236.19   2 u   54   64  377    5.206   -2.171   2.470
+golem.canonical 17.253.34.123    2 u   58   64  377  209.017   -0.482   1.305
+alphyn.canonica 142.3.100.2      2 u   57   64  377  146.822   -0.722   1.216
+pugot.canonical 17.253.108.253   2 u   52   64  377  250.782   -0.555   1.252
-chilipepper.can 17.253.108.125   2 u   52   64  377  210.178    0.891   1.535

7. サービスの自動起動

sysv-rc-confをインストールする。

$ wget http://archive.ubuntu.com/ubuntu/pool/universe/s/sysv-rc-conf/sysv-rc-conf_0.99.orig.tar.gz
$ tar zxvf sysv-rc-conf_0.99.orig.tar.gz
$ cd sysv-rc-conf-0.99
$ sudo apt install make
$ sudo make
$ sudo make install
$ sudo apt install libcurses-ui-perl libterm-readkey-perl libcurses-perl

指定したサービスを自動起動設定する。
※今回は、ntpを自動起動に設定

$ sudo sysv-rc-conf ntp on
$ sudo sysv-rc-conf --list ntp
ntp          2:on   3:on    4:on    5:on

なお、起動中のサービスは以下のコマンドで確認できる。

$ service --status-all

8. IPアドレスの固定

環境に合わせて以下のファイルを作成する。

/etc/netplan/99_config.yaml
network:
    version: 2
    renderer: networkd
    ethernets:
        eth0:
            dhcp4: false
            dhcp6: false
            addresses: [192.168.1.1/23]
            gateway4: 192.168.1.254
            nameservers:
                addresses: [192.168.1.254, 8.8.8.8, 8.8.4.4]

以下のコマンドで、設定を反映する。
再起動いらなかった。

$ sudo netplan apply

9. ホスト名の変更

以下のコマンドでホスト名を変更する。
※ubuntu → raspi

$ hostname
ubuntu
$ sudo hostname raspi
$ hostname
raspi

10. OS再起動

$ sudo reboot

dockerのインストール

ラズパイでdocker使うので、インストールする。
aptでdockerとdocker-composeをインストールする。

$ sudo apt install docker docker-compose

dockerを自動起動に設定する。

$ sudo sysv-rc-conf docker on
$ sudo sysv-rc-conf --list docker
docker       2:on   3:on    4:on    5:on

ここまでで終わり

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

PHPの入門書1周するために、サクッとDockerでApache + MySQL + PHP + phpmyadminの環境つくる

PHP入門書を1周したいので、LAMP環境がほしい

PHP力を上げたいので、入門書からやろうと思い立ちました。
そのためにサクッと用意できて、使いまわせるローカル環境が欲しかったので、そのメモです。

今回はdocker-composeを使って環境構築します。

現在の環境

  • OS:MacOS Big Sur
  • Docker:19.03.13
  • DockerCompose:1.27.4

ディレクトリ構成&ファイル

ディレクトリ構成

.
├── docker-compose.yml
├── html
│   └── index.php
├── mysql
├── php
│   ├── dockerfile
│   └── php.ini
└── phpmyadmin

docker-comopse.yml

version: '3'

services:
  php:
    build:
      context: ./php
      dockerfile: dockerfile
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./html:/var/www/html
    ports:
      - 8080:80
  mysql:
    image: mysql:5.7
    volumes:
      - ./mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=test
      - MYSQL_USER=root
      - MYSQL_PASSWORD=root
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=mysql
#      - PMA_USER=test
#      - PMA_PASSWORD=test
    links:
      - mysql
    ports:
      - 4040:80
    volumes:
      - ./phpmyadmin/sessions:/sessions

dockerfile

FROM php:7.2-apache
RUN apt-get update && \
  docker-php-ext-install pdo_mysql mysqli mbstring

index.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>php7.2-apache</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<?php phpinfo(); ?>
</body>
</html>

php.ini

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

エラーすべて表示させたいときは、以下を追加する

error_reporting = E_ALL

実行コマンド

docker-compose.ymlがあるディレクトリに移動して、以下を実行

$ docker-compose up -d

もし、dockerfileを変更した場合は

$ docker-compose up -d --build

php.iniなどの変更を反映させたい時は

$ docker-compose restart

動作確認

phpinfoを表示
http://localhost:8080/
image.png

phpmyadminを表示
http://localhost:4040/
image.png
ログイン画面でdocker-compose.ymlのmysqlコンテナの情報を記載してログインできればOK
image.png

つまづいたところ

  • 当初はdocker-compose.ymlimage: php:7.2-apacheと記載していたが、MySQLのドライバががなくてMySQL動かせなかったので、dockerflieを作成した
  • docker-compose.ymlのphpmyadminコンテナのPMA_USERPMA_PASSWORDを記載すると、phpmyadminにうまくログインできず、データベースが作成できなかったので、コメントアウト

今回やった、やる予定のPHP入門書

PHP入門 確認画面付きのお問い合わせフォームをつくりながらPHPを学ぶ(第2版)
https://amzn.to/2Irz7Fr
※上記環境で動作確認済み

よくわかるPHPの教科書 【PHP7対応版】
https://amzn.to/2LhQ4mW
※まだ未着手

参考記事

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

【Rails】既存のRails6プロジェクトにDockerを導入してみる

はじめに

既存のRailsプロジェクトをDockerに乗せていきます。
初学者による記事なので間違い等ございましたらご指摘いただけると幸いです。
※本記事は開発環境のみの導入を対象としています。

参考

Dockerが何なのか全く分からない!という方はまずは下記の記事を参考にDockerを触ってみてください。
DockerをMacにインストールする
こちらの記事も手っ取り早くDockerをふんわり理解するのにオススメです。
Dockerについてなるべくわかりやすく説明する

環境

  • macOS Catalina 10.15.7
  • Ruby 2.6.5
  • Rails 6.0.3.3
  • Docker 19.03.13
  • docker-compose 1.27.4
  • MySQL 5.6.47

目次

  • 必要ファイルの作成
    • ファイル構成
  • 作成したファイルの編集
  • コンテナの起動

必要ファイルの作成

必要ファイルは以下の5つです。rails newをした際に自動生成されるファイルもあるので必要なもののみ作成していきましょう。

  • Dockerfile
  • docker-compose.yml
  • Gemfile
  • Gemfile.lock
  • database.yml

ファイル構成

既存のアプリケーション名
└── app
     ├── Dockerfile  # 作成
     ├── docker-compose.yml  # 作成
     ├── Gemfile
     ├── Gemfile.lock
     ├── config
          └──database.yml

作成したファイルの編集

Dockerfile

Dockerfile
FROM ruby:2.6.5   #自身のrubyバージョンを指定 (ruby -v)

RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update && apt-get install -y --no-install-recommends\
    nodejs \
    yarn \
    mariadb-client  \
    build-essential  \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /myproject

COPY Gemfile /myproject/Gemfile
COPY Gemfile.lock /myproject/Gemfile.lock

RUN gem install bundler
RUN bundle install
RUN yarn install --check-files

COPY . /myproject

Rails6でwebpackerが標準になったことでyarnのインストールが必要になってきます。
Docker Hubで確認するとrubyのイメージはDebian系となっているので、yarnの公式ドキュメントに記載されているOS毎のインストール方法を参考に記述していきます。
ちなみに、yarnの公式ドキュメントは日本語表示のままだとなぜかOSの選択肢の中にMacとWindowsしか表示してくれないので英語表示に切り替えて確認しましょう。

日本語表示
Screen Shot 2020-12-08 at 19.48.15.png
英語表示
Screen Shot 2020-12-08 at 19.48.30.png

docker-compose.yml

docker-compose.yml
version: '3'

services:
  db:   #データベースのコンテナ作成
    image: mysql:5.7   #自身のmysqlバージョンを指定 (mysql --version)
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports: 
      - '3306:3306'
    volumes:
      - mysql-data:/var/lib/mysql 
    environment:
      MYSQL_DATABASE: myapp_development   #プロジェクト名_development
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: root
      MYSQL_PASSWORD: password

  web:   #アプリケーションのコンテナ作成
    build:
      context: .
      dockerfile: Dockerfile
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    tty: true 
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/myproject
volumes:      #dbを永続化するための記述
  mysql-data:

Gemfile

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6.0.0'

既存アプリケーションの場合この記述はすでにあるはずですが念のため確認しておきましょう。

database.yml

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root   #docker-compose.ymlで指定したユーザー名
  password: password   #docker-compose.ymlで指定したパスワード

development:
  <<: *default
  database: myapp_development   #プロジェクト名_development
  host: db   #docker-compose.ymlでデータベースのコンテナ作成部分で指定したservice名

test:
  <<: *default
  database: myapp_test   #プロジェクト名_test
  host: db   #docker-compose.ymlでデータベースのコンテナ作成部分で指定したservice名

アプリケーションのコンテナからデータベースのコンテナに接続するための設定を記述していきます。
セキュリティ面を気にする場合はusernameとpasswordに環境変数を設定しましょう。今回は開発環境のみということもあり設定していません。

コンテナの起動

ターミナル
cd myapp
docker-compose build    #コンテナを建てる
docker-compose up -d   #コンテナ起動(-dオプションをつけることでバックグラウンドで実行)

これで localhost:3000 にアクセスするとページが表示されます◎

  • エラーが発生した場合は
ターミナル
docker-compose logs -f

でリアルタイムでログの確認ができるのでエラー原因を探して解決しましょう。

  • サーバーを停止させたいときは以下のコマンドを実行してください。
ターミナル
docker-compose down

最後に

実装に思いの外手間取ったので備忘録的に記事にしてみました。
どなたかの参考になれば幸いです。

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

【2020年版】k8s周りでよく使われるOSS・サービスを一言でまとめてみた

皆さん! いいクラウドネイティブライフを送ってますか?

仕事柄k8sのOSSを色々調査必要があり調べてみましたが、1ページでざっとリスト化された記事はなかったので自分用のメモもかねてまとめ見ました。
"よく使われる"の定義はk8sのバイブルとCNCFのプロジェクトと独断と偏見で載せてるので、「これもあるよ!」って方いたら是非コメントください!

各OSSの詳細な説明は公式ドキュメントやそれを説明している記事にお任せして、ここでは見やすさを重視してカテゴリごとに1行のみの説明でリスティングしたいと思います。1
k8s強強マンからの修正リクエスト・ご指摘歓迎します!

クラスタ構築

k8sディストリビューション

  • k8s: 我らがkubernetes、これもちゃんとOSSです
  • k3s: 軽量クラスタ、Armアーキテクチャもサポート、クラウド-エッジ環境ではRancherでマネージするユースケースが紹介されている
  • k0s: シングルバイナリk8s, 5つゼロが売り
  • microk8s: 軽量k8s, IoT用途よりも実験開発むけ

IoT・エッジコンピューティング

  • KubeEdge: 軽量なk8sディストリビーション、エッジ向けに特化したアーキテクチャ

サービスメッシュ

  • Istio: サービスメッシュの本命, カナリアリリース, サーキットブレイカ等が簡単に実現できる
  • linkerd: サービスメッシュ2, Per-nodeというノードごとの切り分けが可能
  • Envoy: Sidecarとしてpodに常駐させるプロキシ, 上記のOSSで使われる

APIゲートウェイ

  • Ambassador: マイクロサービス用なのでkong等と比べてクラウドネイティブな機能が一杯!

リバースプロキシ

  • Traefik: ラベルに応じてルーティングをよしなにやってくれる優れもの

マニュフェスト管理

  • Helm: k8s用構成管理・パッケージ配布ツール、テンプレに穴埋めで記述するタイプ
  • Monocular: Helm Hub上にあるChartを探せるWeb UIツール
  • kustomize: kubectlに統合されたマニュフェスト管理・作成ツールで上書きで記述するタイプ
  • ksonnet: jsonetを使って記述するタイプ、開発中止なった私の周りではこれを使ってる人はあまり聞かない

認証認可

  • cert-manager: TLS証明書の管理と発行を自動化するKubernetesアドオン

コンテナラン周り(ランタイムからユーザーツール)

  • Docker: コンテナの代名詞?、k8sでは非推奨になりつつあるコンテナ実行エンジン
  • containerd: docker1.11+で使われている高レベルコンテナランタイム
  • CRI-O: 次の本命コンテナランタイム?, APIの豊富さはcontainerdに負けるが軽量
  • rkt: デーモンではなくSystemdから動かすコンテナランタイム

FaaS

  • knative: k8s上でAWS LambdaみたいなFaaSを構築できる優れもの

開発支援ツール

  • Telepresence: デプロイ済みのpodをローカルのコンテナに切り替えて開発できるツール

CNI

  • flannel: マルチノード間の通信を繋ぐオーバーレイネットワークを構築可能
  • 他のネットワーキング実装: 多すぎるのでまとめ切れない?, 編集リクエストオネシャス

クラスタ運用

ログ集計

  • fluentd: 言わずと知れたログ集計ミドルウェア、k8s関係なく使われている, ライバルにLogstashがある

可視化

  • Grafana: リッチなOSS, デフォルトダッシュボードに満足行かなくなったらコレを使おう
  • cAdvisor: dockerにも対応している監視ツールで導入が楽
  • Heapster: k8s公式に組み込まれているコンポーネント、デフォルトのダッシュボードはこいつのおかげです
  • kiali: istioのためのサービスメッシュの可視化ツール

調査・分析ツール

  • Prometheus:監視OSS、必要な機能は揃えている、リッチな可視化にするためGrafanaと組み合わせることが多い
  • datadog: お金持ちのための監視ツール、高機能で柔軟に設定できるGUIが魅力
  • Jaeger: トレーシングツール、複雑なコンポーネント間の動きを分析できる

運用自動化

  • OPERATOR FRAMEWORK: Operator開発支援OSS, OperatorHubなる配布サービスがある
  • Kopf: pythonで記述可能なOperator開発支援OSS

その他

コンテナビルドツール

  • BuildKit: 公式で組み込まれている、知らない人はそのままdocker buildしてる
  • Kaniko: DonDでも並列ビルドできる優れもの、BuildKitが動かないCI/CD環境で活躍

シークレット管理

  • Kubesec: KMS等のパブリッククラウドの暗号化を用いて管理する
  • Sealed Secrets: Secretを変換した独自のリソースを作成してGitリポジトリを利用して管理する
  • External Secrets: 独自のSecretリソースをAWS等のパブリッククラウドで管理
  • Berglas: Google Secret Managerで管理するツール権限管理に一長あり

開発効率化

  • kubectx: コンテキストの確認・切り替えがめっちゃ楽になる
  • kubens: コンテキストの確認・切り替えがめっちゃry
  • krew: k8s用ツールをインストールするためのbrew的なsomething
  • stern: キーワードでpodを跨いだログを集計できる賢いやつ

セキュリティ

  • Clair: コンテナの脆弱性をチェックしてくれるOSS

ローカルk8sクラスタマネジメントサービス

  • kubeadm: kubernetes公式が用意してる構築ツール
  • minikube: シングルノード限定だがkubeadmに比べて簡単に使える
  • kind: マルチノード対応の構築ツール, 簡単かつ機能も充実

クラウドマネジメントサービス

  • GKE: GCP担当、類似サービスに比べサービスでマネージドしてくれる部分が多い
  • EKS: AWS担当、IAMとRBACを関連づけられる他Fargateを利用したコスパの良い利用もできる
  • AKS: Azure担当、よくわからない
  • ACK: Alibaba担当、さらによくわからないし使ってる人いるのか?

CI/CD

  • kubeval: マニュフェストファイルがAPIバージョンに準拠しているか検証できるOSS
  • Conftest: Rego言語でマニュフェストファイルのユニットテストを行うツール
  • Open Policy Agent: CICDを経由せずポリシーを検証できるツール
  • ArgoCD: k8sでGitOpsを実現するためのOSS
  • Flux|GitOps toolkit: argoCDと類似OSS, 二つはGitOps Engineに統合される予定
  • Tekton: CICDパイプラインを構築するためのツール
  • Spinnaker: CDに特化したOSS, Cluster, Server Group, Applicationという独自概念でクラスタを抽象化する
  • Skaffold: コンテナイメージの変更を検知して自動でビルド・レジストリに登録してくれる君

MLOps

  • kubeflow: MLOpsツールの集合体、ノートブック・モデルサービング、ハイパラの並列探索や実験管理等MLで必要な機能を揃えたフルスタックツール
  • seldon core: k8s上で機械学習モデルをサービングするためのOSS, kubeflowとの合わせ技も可能
  • mlflow: kubeflowより機能は少ないが導入が楽、k8sでもシングルノードサーバでも動く、作者のお気に入りOSS
  • Argo Workflows:

クラスタオーケストレーションツール

  • Rancher: 複数のk8sクラスタを簡単に管理・構築できる
  • Tanzu Kubernetes Grid: vSphereとクラウドのハイブリッド構成が作成できる有償オーケストレーター
  • HPE Container Platform: 非クラウドネイティブなモノリシックなシステムと統合可能の有償オーケストレーター
  • Lens: k8s用IDEと言われている、マルチクラスタ管理やPrometeus利用した監視が可能

おまけ

おすすめチュートリアル

  • 公式チュートリアル: katakodaというSaaS環境でやるため自前で用意しなくて良い
  • SockShop: k8sで構築された架空の靴下ショップのソースコード, これを弄って覚えるのもあり
  • kubernetes-the-hard-way: k8sクラスタをマネージドサービスを使わず構築するM向けチュートリアル
  • GKEチュートリアル: Guestbookという掲示板を作ったりできるハンズオン、個人的に一番わかりやすい
  • EKSチュートリアル: Fargateを利用したクラスタ、ALBを使ったサービスの公開方法等AWSのサービスを使った方法を教えてくれるが、若干日本語が直訳ぽい

参考


  1. カテゴリは主観たっぷりなのでMECEになってないかもしれないです。 

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

Kubernetes is deprecating Docker as a container runtime after v1.20.

もう5日も流行に乗り遅れた感があるがこういう話題があるらしい。
Don't Panic: Kubernetes and Docker

とりあえずこのあたりを読んで落ち着こう。
Kubernetes 1.20からDockerが非推奨になる理由
Dockerは非推奨じゃないし今すぐ騒ぐのをやめろ

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

WindowsでDockerインストール後にVMWareで仮想マシンが起動できなくなる(KB76918)

image.png

画像: https://kb.vmware.com/s/article/76918

問題

VMWareはWindowsのHyper-VWindows Sandbox等のハイパバイザと共存できません。
Dockerをインストール後の修復方法を幾つか紹介します。

1. Dockerをアンインストール

VMWareとDockerは共存できません。
Dockerをアンインストールして下さい。
しかし、Dockerインストール時に幾つかの機能が自動的に有効化されているので、VMWareの動作を取り戻すには幾つかの作業が必要です。

2. Hyper-Vの無効化

コントロールパネル->プログラムのアンインストール->Windowsの機能の有効化または無効化
IMAGE
IMAGE
Hyoer-VWindows Sandbox(念の為)がどちらともチェックを外して下さい。
IMAGE
変更後は再起動が必要です。

3. ブート設定の確認と変更

ブート設定を確認します。
下記のコマンドを実行して下さい。
管理者権限必須です。

$ bcdedit

このように出力されますので、hypervisorlaunchtypeという項目を探して確認して下さい。
OFFになっていなければ、次へ進んで下さい。
すでにOFFになっていれば4へ進んで下さい。

bcdedit出力
Windows ブート マネージャー
--------------------------------
~

Windows ブート ローダー
--------------------------------
identifier              {current}
~
bootmenupolicy          Standard
hypervisorlaunchtype    Off <-これ

Windows ブート ローダー
--------------------------------
~

同じく管理者コマンドラインで下記コマンドを実行してhypervisorlaunchtypeをOFFにします。

$ bcdedit /set hypervisorlaunchtype off

以上です。
実行後は再起動が必要です。

4. VMWareの修復

VMWareのインストーラを起動し、修復を選択します。
image.png
image.png

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

Private Docker Registryを構築しよう!(NginxでのBasic認証付き)

本記事は東京学芸大学 櫨山研究室 Advent Calendar 2020の八日目の記事になります.

はじめに

みなさんはDockerを利用していますか?

Dockerではイメージを共有し,イメージからコンテナを生成することが簡単にできます.
作ったアプリケーションをちょっと公開したいと思えば,サーバ上でイメージからコンテナを立ち上げるだけで実行することができ非常に便利ですね.

一方でDockerイメージをそのように管理する方法は少し悩ましい問題だと思います.

本記事では自前のサーバにDocker Registryを立ててイメージを管理する方法を扱います.

構成

image.png

NginxをリバースプロキシにおいてDocker RegistryとGUIのクライアントソフトにアクセスできるようにします.
またNginxのBasic認証を有効にして簡易的に認証機能を持たせます.

前提として

  • サーバのドメインは取得済み
  • サーバにDocker,Nginxは導入済み
  • NginxのSSLは設定済み
  • 動作環境はCent OS 7を想定

として進めます?‍♂️

Docker Registryの導入

Docker RegistryはDocker Imageを保管するアプリケーションです.

image.png

Docker Registryを自前のサーバで動かすことでdocker pushコマンドでローカルやCI環境で構築したDockerイメージを保存しDocker pullコマンドでイメージの取得が行えます.

Docker HubでDocker Registryの公式イメージが公開されています.

動かすだけなら以下のコマンドで5000番ポートで動作します.

bash
docker run -p 5000:5000 registry:latest

今回は以下のような形で起動します.

項目 内容
コンテナ名 registry
ポートマッピング 5000:5000
restart always
ネットワーク registry
ボリューム registry-volume:/var/lib/registry
環境変数 REGISTRY_STORAGE_DELETE_ENABLED=true

環境変数REGISTRY_STORAGE_DELETE_ENABLEDをtrueにすることでRegistry上に保管されたDockerイメージを削除することが可能です.

bash
# dockerネットワークの作成
docker network create registry

# dockerボリュームの作成
docker volume create --name registry-volume

# コンテナの起動
docker container run -d \\
--name registry \\
-p 5000:5000 \\
--restart always \\
--net registry \\
-v docker-registry:/var/lib/registry \\
-e REGISTRY_STORAGE_DELETE_ENABLED=true \\
registry:latest

これで5000番ポートでDocker Registryが動作します.

Docker Registry UIの導入

Docker Registryにどのようなイメージが保管されているのかGUIで確認できた方が何かと便利なのでGUIツールを導入します.

今回は,Docker Registry UIというツールを使います.

registre-ui

[画像引用元] Docker Registry UI, https://github.com/Joxit/docker-registry-ui

Docker Hubでイメージが公開されているのでDocker環境があれば簡単に動かすことができます.

以下のような形で起動します.

項目 内容
コンテナ名 registry_ui
ポートマッピング 4000:80
restart always
ネットワーク registry
環境変数 REGISTRY_URL=http://registry
DELETE_IMAGES=true
bash
docker container run -d \\
--name registry_ui \\
-p 4000:80 \\
--restart always \\
--net registry \\
-e REGISTRY_URL=http://registry \\
-e DELETE_IMAGES=true \\
joxit/docker-registry-ui:latest

これで4000番ポートでDocker Registry UIが動作します.

NginxでリバースプロキシとBasic認証をつける

はじめにBasic認証をかけるためにパスワードファイルを作成します.
次にNginxの設定ファイルを記述してパスに応じてリクエストをdocker registryとdocker registry uiに流します.

パスワードファイルの作成

作成するパスワードファイルは/etc/nginx/.htpasswdとします.

bash
# htpasswdのインストール(必要に応じて)
sudo yum install httpd-tools

# パスワードファイルの作成
htpasswd -c  /etc/nginx/.htpasswd test-user

リバースプロキシの設定

リバースプロキシの設定ファイルを/etc/nginx/conf.d/registry.confに作成します.

  • パスが/v2/から始まる場合はdocker registryにアクセス
  • パスが/registry-ui/から始まる場合はdocker registry uiにアクセス

とします.

設定ファイルの内容は以下です.
なおここではNginxの設定で/etc/nginx/conf.d配下の.confファイルを読み込むようになっているものとしています.

/etc/nginx/conf.d/registry.conf
# docker registryの設定
location /v2/ {
  # Do not allow connections from docker 1.5 and earlier
  # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
  if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
    return 404;
  }

  # To add basic authentication to v2 use auth_basic setting.
  auth_basic "Enter password";
  auth_basic_user_file /etc/nginx/.htpasswd;

  proxy_pass http://localhost:5000;

  proxy_set_header  Host              $http_host;   # required for docker client's sake
  proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
  proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
  proxy_set_header  X-Forwarded-Proto $scheme;
  proxy_read_timeout                  900;
}

# docker registry uiの設定
location /registry-ui/ {
  auth_basic "Enter password";
  auth_basic_user_file /etc/nginx/.htpasswd;

  proxy_pass http://localhost:4000/;

  proxy_set_header        HOST                $http_host;
  proxy_set_header        X-Forwarded-Host    $host;
  proxy_set_header        X-Forwarded-Port    $server_port;
  proxy_set_header        X-Real-IP           $remote_addr;
  proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
  proxy_set_header        X-Forwarded-Proto   $scheme;
  proxy_read_timeout      900;
}

Nginxの再起動

ここまでの設定を反映するためにNginxを再起動します.

bash
sudo systemctl restart nginx

動作確認

ローカルのマシンからサーバ上のDocker Registryにアクセスを試みます.

bash
docker login ${サーバのドメイン名}

ここでユーザー名とパスワードを聞かれるので先ほど作成したtest-userとそのパスワードを入力します.

認証が完了したらdocker pushコマンドでイメージをpushしたり登録されているdocker pullコマンドでイメージをpullすることができます.

次にdocker registry uiにアクセスしてみましょう.

ブラウザでhttps://{ドメイン名}/registry-uiにアクセスします.

image.png

ユーザー名とパスワードを入力して,以下のような画面が見れれば成功ですね.

registre-ui

おわりに

本記事では自前のサーバにDocker Registryを構築する方法を扱いました.

PraivateなDocker Registryを構築したい!という方に参考になれば幸いです.

参考

Authenticate proxy with nginx, https://docs.docker.com/registry/recipes/nginx/

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

短編映画「Hello, world!インセプション」

第1階層 Windows

image.png

ファイル名を指定して実行

cmd

Microsoft Windows [Version 10.0.19042.630]
(c) 2020 Microsoft Corporation. All rights reserved.

C:\Users\user>

>echo "Hello, world!"

C:\Users\user>echo "Hello, world!"
"Hello, world!"

第2階層 Ubuntu (WSL)

image.png

>wsl

C:\Users\user>wsl
user@DESKTOP-EPGPMTG:/mnt/wsl/docker-desktop-bind-mounts/Ubuntu-20.04/7272****$

$ echo "Hello, world!"

user@DESKTOP-EPGPMTG:/mnt/wsl/docker-desktop-bind-mounts/Ubuntu-20.04/7272****$ echo "Hello, world!"
Hello, world!

第3階層 Amazon Linux (EC2)

image.png

$ ssh -i "****.pem" ec2-user@ec2-****.us-east-2.compute.amazonaws.com

user@DESKTOP-EPGPMTG:~$ ssh -i "****.pem" ec2-user@ec2-****.us-east-2.compute.amazonaws.com
Last login: *** ***  * **:**:** 2020 from ****.**.**

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-**** ~]$

$ echo Hello, world!

[ec2-user@ip-**** ~]$ echo Hello, world!
Hello, world!

第4階層 CentOS (Docker)

image.png

$ docker run -it centos /bin/bash

[ec2-user@ip-**** ~]$ docker run -it centos /bin/bash
[root@**** /]#

# echo "Hello, world!"

[root@**** /]# echo "Hello, world!"
Hello, world!

第5階層 Python

image.png

# yum install python3
# python3

[root@**** /]# yum install python3
[root@**** /]# python3
Python 3.6.8 (default, Apr 16 2020, 01:36:27)
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

>>> print ("Hello, world!")

>>> print ("Hello, world!")
Hello, world!

脱出

>>> exit()
[root@**** /]# exit
exit
[ec2-user@ip-**** ~]$ exit
logout
Connection to ec2-****.us-east-2.compute.amazonaws.com closed.
user@DESKTOP-EPGPMTG:~$ exit
logout

C:\Users\user>exit

エピローグ

 実際の映画(?)ではここで謎の男が出てきてexitと入力すると主人公の存在が消えてしまいます。

 あと、一点つまづいた点として、wslがWindowsディレクトリのままいるとssh接続する時にPermissionエラーが出てしまうので、WSLのフォルダに入れた後にchmod 400しないといけませんでした。WSLからssh接続する時にLoad key "****.pem": bad permissions. Permission denied (publickey,gssapi-keyex,gssapi-with-mic).というエラーが出てくる時は参考ページの通りにやると上手くいきます(検索用要約)。

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

CopilotでECSを体験してみたい

はじめに

この記事はハンズラボ Advent Calendar 2020 9日目の記事となります。

今回のテーマは現在開催中のAWS re:Inventからではないですが、
ECSの構築を簡単にしてくれるCopilotが一般公開されたのでこちらを見てみます。

この記事の対象者

  • 右も左もわからないけど、Dockerやってみたい!ECS気になる!な方
  • ECSでインフラ構築しなくちゃどうしよう。。な方
  • 当時闇雲にインフラ構築をして疲弊していた知識不足な自分に向けて

Copilotとは

概要についてはAWSブログより引用。
ECS CLIの後継にあたるものみたいです。

https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/

AWS Copilot は、低レイヤなインフラの手動管理から脱却し、自身のアプリケーションとそのライフサイクルにフォーカスしたい ECS ユーザのためにデザインされました。Copilot は、ECS チームのエンジニアや ECS のお客様が作り上げてきたベストプラクティスに基づき、デフォルトでモダンかつプロダクションレディなインフラを構築します。

AWSだとCloudFormation or CDK(Cloud Development Kit) が良いのかなと考えますが、やっぱり大変ですよね。。

CloudFormationと聞きますと、こちらの記事を思い出します。

CloudFormationの全てを味わいつくせ!「AWSの全てをコードで管理する方法〜その理想と現実〜」

引用

最初の一歩を踏み出すには、中々が勇気がいりそう。。
そんな感じでスルーしてしまうのは非常に勿体ないので、是非Copilotから自動化の素晴らしさを体験してみましょう!!

Copilot動かしてみる

前提

  • macOS
  • aws cliインストール&設定済み (aws configureでデフォルトプロファイルを設定。)

Copilot インストール

$ brew install aws/tap/copilot-cli

copilot --helpを実行すると以下のように表示されます。
image.png

DevlopとReleaseとあって、CI/CDパイプラインもすぐ組めそう。

ドキュメントはこちらから。copilot docsでもいけます。↓
https://aws.github.io/copilot-cli/

アプリを用意

(こちらを拝借)
https://github.com/Daviey/nyan-cat

Dockerfileは以下のようにします。

FROM nginx:alpine
EXPOSE 80
COPY . /usr/share/nginx/html

さっそく実行

copilot init

いくつかのやりとりを経てデプロイが実行されます。
ひとまずテスト環境として、アプリケーションがデプロイされるイメージです。
image.png

途中でCloudFormationのコンソールをみてみると、
こんな感じでスタックが作成されていきます。
image.png

ECRへimageのpushが終わりました。
デプロイが完了したようです。
image.png
一応、デプロイ完了後のCloudFormationコンソールも。。。
image.png

アクセスしてみる

コンソールをみると、URLあるのでそこからアクセスしてみます。
image.png
うごいたーー
猫が飛んでいます。

ここまで15分くらいあれば速攻でECSデプロイいけます。

本番環境へデプロイしたい

本番環境としてデプロイしたい場合には
copilot env initを実行すると、環境ごとのインフラを構築してくれます。
(nameはproductionとします)

設定ファイル(manifest.yaml)の値を書き換えて、
以下コマンドを実行するだけでデプロイができます。

copilot svc deploy --env production

以下のようにenviromentsを上書きするだけで、
複数のAZに対してコンテナを配置して、可用性の高いアプリケーションとすることが出来ます。(す、凄い)

environments:
  production:
    count: 2
    cpu: 1024
    memory: 2048

Copilotはどれだけ楽にしてくれるか

ECSでアプリケーションを実行するには最低でも以下AWSサービスを利用します。

- ECS
- ECR
- EC2( or Fargate)
- ELB
- VPC(サブネットも色々気にしないといけない)
etc...

多いです。
手で作る or Cfnで構築といった場合は上記すべてを網羅しておく必要があります。

先程のcopilot initを絵に表すとこんな感じ。。
(これも細かい所は省いています。間違っていたらすみません。)
image.png

コンテナの恩恵を被るには、結構準備が必要です。

デプロイしたいアプリケーションさえあれば、すぐにリリースできる。
そんなことがCopilotだと可能です。素敵ですね。

考察

所属チームでは、システムのマイグレーションは中長期的な課題ではあるので、
コンテナ化する際の選択肢の一つとしてCopilotはアリかなと考えています。

現状、マイグレーションの一環としてECS化は結構進んでいるので。。

弊社のようにのっぴきならない事情がない限りは、
こういったツールをドンドン活用していった方が良いかなと思われます。。

マイグレーションの歩みはこちらから見れます。
ハンズラボエンジニアブログ

最後に

最後まで読んでいただきありがとうございました。

消し忘れると課金されてしまうので、不要であれば消しましょう。

copilot app delete --env-profiles test=default

image.png

以下でCopilotでの自動デプロイが紹介されているみたいなので、今度試してみたいと思います。
https://aws.amazon.com/jp/blogs/news/automatically-deploying-your-container-application-with-aws-copilot/

参考情報

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

docker ruby(2.6.5)・mysql(5.6.47)・rails(6.0.0)の開発環境構築

概要

dockerを開発環境に使ってアプリを作成しました。初めてdockerを使ってなかなかうまく動かなかったので動いたものをアウトプットを含めて共有します。また、deviseの導入も行って開発も少々行っていきたいと思います。

dockerの導入

ベースはdockerの公式サイトにRailsとPostgresSQLのdocker-composeの使い方が乗っていたのでそちらを参考にします。

dockerの公式サイト
https://docs.docker.com/compose/rails/

ただ、rails6.0.0からはwebpackerが標準になったことにより6.0.0を使う際には修正が必要です。また、今回はmysqlを使うのでそちらも変更していきます。

アプリのディレクトリ作成

ターミナル.
mkdir sampleapp

アプリのディレクトリに移動

ターミナル.
cd sampleapp 

dockerfileの生成

ターミナル.
touch dockerfile

dockerfileの編集

作成したdockerfileを以下のように編集します。

dockerfile.
FROM ruby:2.6.5

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn

RUN mkdir /myapp

WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

公式の以下の点を修正しました。

1.rubyのバージョンを変更。
2.webpackerが標準になったことにより、必要になったyarnのインストールを行う。

FROM ruby:2.6.5

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn

entrypoint.shの生成

 touch entrypoint.sh

entrypoint.shの編集

作成したentrypoint.shを編集します。こちらはdockerの公式サイトの通りになります。

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

docker-compose.ymlの生成

touch docker-compose.yml

docker-compose.ymlの編集

生成したファイルを以下のように編集します。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    environment:
        MYSQL_ROOT_PASSWORD: password
        MYSQL_DATABASE: root
    ports:
        - "3306:3306"
    volumes:
        - ./db/mysql/volumes:/var/lib/mysql
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    stdin_open: true
    tty: true
    volumes:
      - .:/myapp
      - gem_data:/usr/local/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  mysql_data:
  gem_data:

公式のリファレンスではpostgresqlを使っているためmysqlにしています。

services:
db:
image: mysql:5.6.47
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
volumes:
- ./db/mysql/volumes:/var/lib/mysql

アプリ製作中にbinding.pryを使えるように以下を追加しています。

stdin_open: true
tty: true

データとgemを永続化するために以下の記述を追加しています。

volumes:
mysql_data:
gem_data:

Gemfileの生成

ターミナル.
touch Gemfile

生成したファイルを以下のように編集します。

Gemfile.
source 'https://rubygems.org'
gem 'rails', '~>6'

空のGemfile.lockを生成

ターミナル.
touch Gemfile.lock

railsのプロジェクトを作成

ターミナル.
docker-compose run web rails new . --force -d mysql

docker-composeをbuildする

ターミナル.
docker-compose build

database.ymlの修正

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

docker-composeの再起動

docker-composeを一旦downさせてバックグラウンドで起動するようにコマンドを入力

ターミナル.
docker-compose down
docker-compose up -d

データベースの作成

ターミナル.
docker-compose run web bundle exec rails db:create

ここまできたらlocalhost:3000/にアクセスしてみましょう。以下の画面が表示されるはずです。

スクリーンショット 2020-12-08 16.14.11.png

deviseを用いた簡単なアプリの開発

ここからはアプリ開発の入り口をやっていきます。

コントローラーとビューを生成

トップページを表示するためにposts(投稿)コントローラーとindexのビューを生成します

ターミナル.
docker-compose run web rails g controller posts index

生成したらルーティングを設定します。routes.rbを以下のように編集しましょう。

config/routes.rb
Rails.application.routes.draw do
  root 'posts#index'
  get 'posts/index'
end

この時点でlocalhost:3000/にアクセスすると以下のような画面になっています。
先ほど作成したpostsのindex(view)が表示されていることがわかります。

スクリーンショット 2020-12-08 16.19.20.png

deviseの導入

gemファイルの一番したにdeviseを記述します。

Gemfile.
gem 'devise'

bundle installを行う

dockerを利用している場合には通常行っているコマンドに "docker-compose run web"をつける必要があります。

ターミナル.
docker-compose run web bundle install

dockerの再起動

gemを新たに追加したためdocker-composeの再起動を行います。

ターミナル.
docker-compose down
docker-compose up -d

あとは通常のdeviseと同じ作業を行っていきます。
それぞれのコマンドを入力していきます。

ターミナル.
docker-compose run web rails g devise:install
docker-compose run web rails g devise user
docker-compose run web rails db:migrate
docker-compose run web rails g devise:views

ここまできたら再度dockerを再起動させます

ターミナル.
docker-compose down
docker-compose up -d

ここまででdeviseの導入全てが終わったためあとはposts/index.html.erbに以下を記述してみましょう。

posts/index.html.erb
<%=link_to "ログイン", new_user_session_path %>

ここまででlocalhost:3000/にアクセスするとログインボタンが表示され、押すとログイン画面に遷移します。
これでdockerを使ってdeviseを動かすところまで行えました。
ここからはそれぞれのオリジナルアプリの仕様に従って開発ができると思います。
トップページ
スクリーンショット 2020-12-08 16.30.45.png

ログインページ
スクリーンショット 2020-12-08 16.33.27.png

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

LambdaでDockerコンテナイメージ使えるってマジですか?(Python3でやってみる)

背景

自然言語処理関係でLambdaを使いたいと思っているけど、どうもライブラリのサイズが大きく、Lambdaのクオータに引っかかる。
ECSでAutoScalingかな?と思っていた所に、re:Invent2020で、Lambdaでコンテナイメージが使えるという発表があったという話を聞く。

早速Developers.IO様で記事になってた。まさに大きいファイルを扱う必要があるML系処理向けらしい!
【速報】Lambdaのパッケージフォーマットとしてコンテナイメージがサポートされるようになりました!! #reinvent

前提

今回は、Python3.8のランタイムが対象
Ubuntu18.04マシンで構築

流れ確認

流れとしては以下の感じらしい。

  • 指定エンドポイントにアクセスすると、LambdaフォーマットでのJsonオブジェクトを返す様なコンテナを作成
  • このコンテナ作成際にはベースイメージがあるそうだが、フォーマットを守れば自作でも可能らしい
  • ローカルでテストしつつDockerfileを作成
  • ECRに登録
  • Lambdaの関数作成時に、そのECRのDockerImageのURIを指定する

用語

Runtime interface clients

コンテナ内部に存在し、Lambdaとプログラムコードをつなぐ役割をする。このモジュールにhandlerが渡されて処理される形の模様。
デフォルトのAWS Lambdaベースイメージには既に入っているので、自分たちで独自にDockerImageを作る場合には個別対応が必要。

Runtime Interface Emulater

ローカル環境でLambdaコンテナを試せるエミュレーター。多分、Lambdaテスト用のエンドポイントを提供するwebサーバーの様なものだと思う。公式DockerImageなら既に入っている模様。

ローカル開発環境セットアップ

RIE公式Github にインストールコマンドが記載されている。
全体概要がよく解らなくて苦戦していたら、日本語説明ページがあった。これをトレースしてみる。

作業用フォルダ作成

mkdir locallambdatest
cd locallambdatest

aws-lambda-rieをダウンロード

https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie
へアクセスするとダウンロードが始まるので、先に作った作業用フォルダ直下に保存。

Dockerfile作成

公式ページそのままから、aws-lambda-rieのコピー部分、entry.shのモード変更だけ修正

ファイル名:Dockerfile.python3.9test
Dockerfile.python3.9test
# Define global args
ARG FUNCTION_DIR="/home/app/"
ARG RUNTIME_VERSION="3.9"
ARG DISTRO_VERSION="3.12"

# Stage 1 - bundle base image + runtime
# Grab a fresh copy of the image and install GCC
FROM python:${RUNTIME_VERSION}-alpine${DISTRO_VERSION} AS python-alpine
# Install GCC (Alpine uses musl but we compile and link dependencies with GCC)
RUN apk add --no-cache \
    libstdc++

# Stage 2 - build function and dependencies
FROM python-alpine AS build-image
# Install aws-lambda-cpp build dependencies
RUN apk add --no-cache \
    build-base \
    libtool \
    autoconf \
    automake \
    libexecinfo-dev \
    make \
    cmake \
    libcurl
# Include global args in this stage of the build
ARG FUNCTION_DIR
ARG RUNTIME_VERSION
# Create function directory
RUN mkdir -p ${FUNCTION_DIR}
# Copy handler function
COPY app/* ${FUNCTION_DIR}
# Optional – Install the function's dependencies
# RUN python${RUNTIME_VERSION} -m pip install -r requirements.txt --target ${FUNCTION_DIR}
# Install Lambda Runtime Interface Client for Python
RUN python${RUNTIME_VERSION} -m pip install awslambdaric --target ${FUNCTION_DIR}

# Stage 3 - final runtime image
# Grab a fresh copy of the Python image
FROM python-alpine
# Include global arg in this stage of the build
ARG FUNCTION_DIR
# Set working directory to function root directory
WORKDIR ${FUNCTION_DIR}
# Copy in the built dependencies
COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}
# (Optional) Add Lambda Runtime Interface Emulator and use a script in the ENTRYPOINT for simpler local runs
# COPY https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
COPY aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
COPY entry.sh /
RUN chmod 755 /entry.sh
ENTRYPOINT [ "/entry.sh" ]
CMD [ "app.handler" ]

アプリソース(app.py)を作成して、appフォルダ以下へ配置

mkdir app
vim app/app.py
app.py
import sys
def handler(event, context): 
    return 'Hello from AWS Lambda using Python' + sys.version + '! test'

entry.sh 作成

日本語ページだと$1が消えているので注意。その部分を修正。

entry.sh
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
    exec /usr/bin/aws-lambda-rie /usr/local/bin/python -m awslambdaric $1
else
    exec /usr/local/bin/python -m awslambdaric $1
fi

DockerImageをビルド

ここではイメージ名を「python3.9test:local」とする。

sudo docker build -t python3.9test:local -f Dockerfile.python3.9test .

コンテナを起動する

ここではコンテナ名を「locallambdatest」とする。

sudo docker run -p 9000:8080 --name locallambdatest python3.9test:local

コンテナへアクセスしてみる

$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
"Hello from AWS Lambda using Python3.9.0 (default, Nov 25 2020, 02:36:55) \n[GCC 9.3.0]! test"

成功!

自然言語処理ライブラリをインストールしてみる

Dockerfile.python3.9test の修正

モジュールインストール部分コメントイン+requirements.txtの配置

Dockerfile.python3.9test
COPY requirements.txt requirements.txt
RUN python${RUNTIME_VERSION} -m pip install -r requirements.txt --target ${FUNCTION_DIR}
# RUN python${RUNTIME_VERSION} -m pip install -r requirements.txt --target ${FUNCTION_DIR}

requirements.txtを作成

作業用フォルダ直下に以下の内容で作成。ginzaを入れておく。

requirements.txt
ginza==4.0.5

app.py の修正

ginzaライブラリで形態素解析をして、その中身をそのまま返す。

import sys
import json
import spacy
import logging
from ginza import *

logger = logging.getLogger()

def handler(event, context):
    logger.info(context)
    target_text = event['text']
    nlp = spacy.load('ja_ginza')

    doc = nlp(target_text)
    morpheme_list = []
    for sent_idx, sent in enumerate(doc.sents):
        for token_idx, tk in enumerate(sent):
            wk_morpheme = {}
            wk_morpheme['text'] = tk.text
            wk_morpheme['dep'] = tk.dep_
            wk_morpheme['pos'] = tk.pos_
            wk_morpheme['tag'] = tk.tag_
            morpheme_list.append(wk_morpheme)
    return morpheme_list

実行

$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"text":"テストしてみる"}'

[{"text": "\u30c6\u30b9\u30c8", "dep": "ROOT", "pos": "VERB", "tag": "\u540d\u8a5e-\u666e\u901a\u540d\u8a5e-\u30b5\u5909\u53ef\u80fd"}, {"text": "\u3057", "dep": "advcl", "pos": "AUX", "tag": "\u52d5\u8a5e-\u975e\u81ea\u7acb\u53ef\u80fd"}, {"text": "\u3066", "dep": "mark", "pos": "SCONJ", "tag": "\u52a9\u8a5e-\u63a5\u7d9a\u52a9\u8a5e"}, {"text": "\u307f\u308b", "dep": "aux", "pos": "AUX", "tag": "\u52d5\u8a5e-\u975e\u81ea\u7acb\u53ef\u80fd"}]

出力が文字コードの文字列になっちゃいましたが、形態素解析出来ている模様。

ECRへコンテナイメージを登録する

ここではレポジトリ名を「lambdacontainer-test」とする。123412341234はもちろんダミーです。実際にはECRリポジトリを作成するAWSアカウントIDEです。

aws ecr create-repository --repository-name lambdacontainer-test --image-scanning-configuration scanOnPush=true
sudo docker tag python3.9test:local 123412341234.dkr.ecr.ap-northeast-1.amazonaws.com/lambdacontainer-test:latest
aws ecr get-login-password | sudo docker login --username AWS --password-stdin 123412341234.dkr.ecr.ap-northeast-1.amazonaws.com
sudo docker push 123412341234.dkr.ecr.ap-northeast-1.amazonaws.com/lambdacontainer-test:latest

docker login 実行時、「Error saving credentials: error storing credentials」なんてエラーが出てきたら以下実行

sudo apt install gnupg2 pass

ここまでで、AWSコンソールのECRリポジトリリストに表示されているはず。

Lambda関数をコンテナを使って作成する。

  • AWSコンソールからLambdaのページへ行き「関数の作成」
  • オプションで「コンテナイメージ」を選択
  • 関数名は適当に。今回は「my-lambda-container-test」
  • コンテナイメージURIは「画像を選択」(多分 Select Image の日本語訳)ボタンを押してリポジトリ(lambdacontainer-test)とタグ(latest)を選択
  • 「関数の作成」を押してしばらくすると「関数の作成が正常に終了しました」のポップアップが出てくる。

テストする

  • 「テスト」ボタンを押す
  • イベント名は適当に
  • {"text":"テストしてみる"} をテスト用Bodyに指定
  • 「作成」を押す

設定変更

  • 「基本設定」エリアの「編集」ボタンを押す
  • メモリを1GB、実行時間制限を5分ぐらいにする

テスト実行

なんかエラー出た。ginzaが使用しているsudachiライブラリがsymlinkを実行しようとして失敗している模様。
このエラー、ローカルテスト時点で出るようにして欲しかった・・・・(公式イメージだとそうなったりするのかな?)
回避するためにはまたライブラリの設定とかが必要になってきそう(そもそもそれが可能なのかも)。
※Lambda上では、書き込み系ファイル操作は /tmp/フォルダ以下で行う必要がある。

{
  "errorMessage": "[Errno 30] Read-only file system: '/home/app/sudachidict_core' -> '/home/app/sudachidict'",
  "errorType": "OSError",
  "stackTrace": [
    "  File \"/home/app/app.py\", line 12, in handler\n    nlp = spacy.load('ja_ginza')\n",
    "  File \"/home/app/spacy/__init__.py\", line 30, in load\n    return util.load_model(name, **overrides)\n",
    "  File \"/home/app/spacy/util.py\", line 170, in load_model\n    return load_model_from_package(name, **overrides)\n",
    "  File \"/home/app/spacy/util.py\", line 191, in load_model_from_package\n    return cls.load(**overrides)\n",
    "  File \"/home/app/ja_ginza/__init__.py\", line 12, in load\n    return load_model_from_init_py(__file__, **overrides)\n",
    "  File \"/home/app/spacy/util.py\", line 239, in load_model_from_init_py\n    return load_model_from_path(data_path, meta, **overrides)\n",
    "  File \"/home/app/spacy/util.py\", line 203, in load_model_from_path\n    nlp = cls(meta=meta, **overrides)\n",
    "  File \"/home/app/spacy/language.py\", line 186, in __init__\n    make_doc = factory(self, **meta.get(\"tokenizer\", {}))\n",
    "  File \"/home/app/spacy/lang/ja/__init__.py\", line 274, in create_tokenizer\n    return JapaneseTokenizer(cls, nlp, config)\n",
    "  File \"/home/app/spacy/lang/ja/__init__.py\", line 139, in __init__\n    self.tokenizer = try_sudachi_import(self.split_mode)\n",
    "  File \"/home/app/spacy/lang/ja/__init__.py\", line 38, in try_sudachi_import\n    tok = dictionary.Dictionary().create(\n",
    "  File \"/home/app/sudachipy/dictionary.py\", line 37, in __init__\n    self._read_system_dictionary(config.settings.system_dict_path())\n",
    "  File \"/home/app/sudachipy/config.py\", line 107, in system_dict_path\n    dict_path = create_default_link_for_sudachidict_core(output=f)\n",
    "  File \"/home/app/sudachipy/config.py\", line 72, in create_default_link_for_sudachidict_core\n    dict_path = set_default_dict_package('sudachidict_core', output=output)\n",
    "  File \"/home/app/sudachipy/config.py\", line 48, in set_default_dict_package\n    dst_path.symlink_to(src_path)\n",
    "  File \"/usr/local/lib/python3.9/pathlib.py\", line 1398, in symlink_to\n    self._accessor.symlink(target, self, target_is_directory)\n",
    "  File \"/usr/local/lib/python3.9/pathlib.py\", line 445, in symlink\n    return os.symlink(a, b)\n"
  ]
}

基本目的は達成したのと、この問題の解消はまた別問題になるので、今回はここまでに。

感想

今回は最後できれいな結果は出てこなかったけど、Lambdaをコンテナ指定で使うという部分は出来た。
所要時間は「2485.68 ms(連続実行時は数ms)」。
処理の最初でエラーが出ているのでほぼオーバーヘッドとみなしていいかと。warmup戦略を取れば低レイテンシーが求められる用途にも使えそう。
ただ、使用しているライブラリが書き込み系ファイル操作を行うかは十分にチェックする必要あり(コンテナ使用Lambdaでなくても)。

参考にさせていただいたページ

正直まだ公表されたばかりのサービスで、公式ドキュメントも説明不足を感じました。ただ、今後改善されていくと思いますし、有志の方が情報整理を行ってくれたりもすると思います。

AWS公式基本DockerImage
RIE公式Github

【速報】Lambdaのパッケージフォーマットとしてコンテナイメージがサポートされるようになりました!! #reinvent
CDKでAWS Lambdaのパッケージフォーマットにコンテナイメージを指定してデプロイしてみた

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

さくらVPSにjprsのSSL証明書をインストール

環境

  • CentOS(さくらVPS)
  • Docker
  • nginx
  • openssl

ゴール

https://でアクセス、http://アクセス時はhttps://でリダイレクト
スクリーンショット 2020-12-08 9.49.42.png

前提

  • 既にhttp://でアクセス可能とする。
  • dockerで環境構築済みとする。
  • opensslインストール済みとする(さくらVPSにインストールされていたバージョンを使用)。

SSL証明書の発行に必要なCSR(Certificate Signing Request)を作成

SSL証明書を申し込む際に必要な情報の1つ。作成方法はjprs公式のマニュアルを参照し作成する。
鍵ファイル作成時のパスフレーズは忘れないように!
スクリーンショット 2020-12-08 10.10.51.png

SSL証明書を購入する

使用するSSL証明書は低コストのjprsを選定。
さくらインターネットのコンパネにログインし、さくらインターネット経由で申し込む。
スクリーンショット 2020-12-08 10.14.46.png
スクリーンショット 2020-12-08 10.16.03.png

認証ファイルをダウンロードする

認証ファイルをサーバーに設置し認証局で承認されることでSSL証明書を取得することができるようになる。
まずは、SSL証明書を購入するとさくらインターネットの会員メニューから認証ファイルをダウンロードする。
名称未設定5.png
名称未設定4.png
名称未設定2.png

ダウンロードされた認証ファイルのファイル名は数字とローマ字が混在したテキストファイル。

認証ファイルをサーバーに設置する

nginxに記載されているドキュメントルート/.well-known/pki-validation配下に認証ファイルをアップロードし
ブラウザから認証ファイルの内容が参照できればOK。
スクリーンショット 2020-12-08 10.58.17.png

ドキュメントルートの確認はnginxのconfig(default.conf)を確認する。

nginx(default.conf)
server {
    listen 80;
    server_name example.com;   
    root /work/public; <- ドキュメントルート

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
:

SSL証明書をダウンロードする

認証局で認証ファイルが承認されるとSSL証明書が発行されダウンロード可能となる。
認証ファイルを設置してから20分程度で発行されダウンロード可能となる。
名称未設定7.png
名称未設定2.png

SSL証明書をインストールする

jprs公式のマニュアルを参照しインストールする。
スクリーンショット 2020-12-08 11.29.16.png

マニュアル中にある中間証明書のダウンロードは以下のとおり。
スクリーンショット 2020-12-08 11.28.25.png

マニュアル通りに「cat (サーバー証明書) (中間証明書) >/pathname/of/combined.crt」で中間証明書とサーバー証明証のファイルを合わせただけではnginxで起動エラーとなってしまったため、以下のように改行を挿入し編集が必要。

combined.crt(変更前)
-----BEGIN CERTIFICATE-----
サーバー証明書
-----END CERTIFICATE----------BEGIN CERTIFICATE-----
中間証明書
-----END CERTIFICATE-----
combined.crt(変更後)
-----BEGIN CERTIFICATE-----
サーバー証明書
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
中間証明書
-----END CERTIFICATE-----

鍵ファイル作成時のパスフレーズはテキストファイルに記載し設置する。

pass.txt
xxxxxxxxxxxxxx <- パスフレーズ

docker-compose.ymlに上記で作成したファイルの配置を記載する。

docker-compose.yml
version: "3.8"
services:
  web:
    image: nginx:1.19.2-alpine
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./src:/work
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./docker/nginx/ssl/example.com.key:/etc/pki/nginx/example.com.key <- 鍵ファイル
      - ./docker/nginx/ssl/combined.crt:/etc/pki/nginx/combined.crt <- サーバー証明書と中間証明書をあわせたもの
      - ./docker/nginx/ssl/pass.txt:/etc/pki/nginx/pass.txt <- 鍵ファイルのパスフレーズを記載したファイル
    working_dir: /work

nginxのconfigは以下の通り。

nginx(default.conf)
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri; <- http://アクセス時はhttps://にリダイレクト
}

server {
    listen 443 ssl; <- sslポート番号
    server_name example.com; <- 独自ドメイン
    root /work/public; <- ドキュメントルート

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    ssl_certificate /etc/pki/nginx/combined.crt; <- サーバー証明書と中間証明書をあわせたもの
    ssl_password_file /etc/pki/nginx/pass.txt; <- 鍵ファイル作成時のパスフレーズを記載したファイル
    ssl_certificate_key /etc/pki/nginx/example.com.key; <- 鍵ファイル
    ssl_session_timeout 5m;
}

nginx起動

mac
xxxMacBook:~ xxx$ ssh developer@xxx.xxx.xxx.xxx
developer@xxx.xxx.xxx.xxx's password: 
Last login: Mon Dec  7 23:50:58 2020 from ------

SAKURA Internet [Virtual Private Server SERVICE]

[developer@ ~]$ cd /var/www/html/example-app/
[developer@ example-app]$ docker-compose up --build
Creating network "example-app_default" with the default driver
Building app
Step 1/9 : FROM php:7.4-fpm-buster
 ---> 58d217bf6b84
Step 2/9 : SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]
 ---> Using cache
 ---> b9de4244ddbf
Step 3/9 : ENV COMPOSER_ALLOW_SUPERUSER=1   COMPOSER_HOME=/composer
:

表示された!

スクリーンショット 2020-12-08 9.49.42.png

参考サイト

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

【Docker】コンテナ作成までの操作#1

本投稿の目的

・Dockerの操作についての議事録です。


学習に使った教材

Udemyの "米国AI開発者がゼロから教えるDocker講座" を教材として使用しました。


○container作成 (1.作成済みimageを利用する場合)

○dockerへlogin

docker login

・Docker Hubに登録したID,passを入力する
・Login Succeededで成功

○imageを取得

docker pull <image名>

・Docker Hub のhome から u/library でimageリストを見れる
・image list の中から必要なimageを選択
・そのimageの適切なTAG (version)を選択

○Hostに保存済みのimageのlistを取得

docker images

・取得したimageが存在することを確認する

○container作成

docker run <image名>

・作成したいcontainerの素となるimage名を指定してrunを実行

【出力結果例:hello-worldのimageで確認】

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

・上記のメッセージが表示される
・container作成後にプログラムが実行されたためこれが表示される
・(今回の例では,hello-worldのテキスト出力プログラム)

○container一覧の確認

docker ps

・アクティブなcontainerを表示する
・ps は process statusの略

docker ps  -a

・全てのcontainerを表示する
・-a は allの略

○Docker run について

・docker run を入力すると次のコードたちが連続してシーケンスで実行される

【runの流れ】
①.create
②.execute
③.exit

【解説】
<①.create>
・runを実行するとcreateが実行される
・containerが作成される

<②.execute>
・createに紐づいてexecuteが実行され・
・デフォルトのプログラムが実行される(今回だとテキスト出力)

<③.exit>
・executeのプログラムが実行されるとexitが実行される
・containerから抜ける

○pullの省略

・imageをpullで取得せずにrunを実行できる (以下の流れで実行)
①Hostにimageがないかを確認
②Hostには無いためDocker Hubからimage名を探して取得
③imageをrunする

○実際の例

docker run ubuntu

・ubuntuというOS用のimageに対してpullせずにrunを実行

【出力結果】

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
da7391352a9b: Pull complete 
14428a6d4bcd: Pull complete 
2c2d948710f2: Pull complete 
Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Status: Downloaded newer image for ubuntu:latest

【解説】
・pullした時にimage layerがcompleteと表示される
・imageは複数のimage layer から構成される
・コンテナ作成すると新たなimage layerが追加される
・新たなimage layerは書き込み可能なため,コンテナで作業後にimageを別名保存するろとここが更新される

○layer構成の旨味

・共通のlayerはcontainer間で共有できる
・差分の新たなlayerのみをスペースとして使用するためスペース削減になる

【例:container_Aを編集しimage_Bとして保存。これをrunしconatainer_Bを作成した場合を想定】
・container_A = image_A = layer1 + layer2
・container_B = image_B = container A + 作業 = layer1 + layer2 + layer3 (3が作業部分)

【解説】
・container_Bはlayer3分をスペースに必要とする
・layer1,2はcontainer_Aのスペースで保存済みのためcontainerBで新たにスペースは必要ない


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

[解決](Rails, Vue)CORSエラーAccess to XMLHttpRequest at 'http://localhost:3000' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

なぜこのエラーが出るのか。

https://qiita.com/att55/items/2154a8aad8bf1409db2b

解決方法(Rails, Vue)

railsのgem

gemfile
#gem rack-coes

のコメントアウトを外し、

$bundle install


config/initializers/cors.rbにある記述も下のようにコメントアウト。

config/initializers/cors.rb
# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3000', 'https://localhost:8080/'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

originsは、サーバーサイド(API),フロントの順で書きます。

Dockerの場合は、再度コンテナを立ち上げて

完了。

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

AWS EC2上にDockerコンテナ(Laravel, MySQL, nginx)を起動しアクセスする

環境

  • Mac
  • Docker
  • EC2 (Amazon Linux2, t2.micro)
  • nginx
  • Laravel
  • MySQL
  • Github

ゴール

EC2上でLaravelのページを表示する
スクリーンショット 2020-09-12 14.48.10.png

前提

  • 今回はお試しなのでEC2は無料利用枠を使えるAmazon Linux2, t2.micro選定
  • EC2のインスタンスならびにSSHの接続で使用するキーペアは作成済みとする。

EC2にログインする

xxx.pemにEC2を作成する際に作成したキーペアのファイルパス、(接続先にユーザー名)@(EC2のパブリックDNS)を指定する。
ユーザー名はデフォルトで「ec2user」、EC2のパブリックDNSはEC2の詳細画面で確認する。

mac
$ sudo ssh -i xxx.pem ec2user@ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/

dockerをインストールする

ec2
$ sudo yum install docker
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ docker.x86_64 0:19.03.6ce-4.amzn2  インストール
--> 依存性の処理をしています: runc >= 1.0.0 のパッケージ: docker-19.03.6ce-4.amzn2.x86_64
--> 依存性の処理をしています: containerd >= 1.3.2 のパッケージ: docker-19.03.6ce-4.amzn2.x86_64
--> 依存性の処理をしています: pigz のパッケージ: docker-19.03.6ce-4.amzn2.x86_64
--> 依存性の処理をしています: libcgroup のパッケージ: docker-19.03.6ce-4.amzn2.x86_64
--> トランザクションの確認を実行しています。
---> パッケージ containerd.x86_64 0:1.3.2-1.amzn2  インストール
:

$ docker --version
Docker version 19.03.12, build 48a66213fe    <- インストールされたことを確認

docker-composeをインストールする

最新バージョンを公式サイトで確認しインストールする

ec2
$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   651  100   651    0     0   2141      0 --:--:-- --:--:-- --:--:--  2134
100 11.6M  100 11.6M    0     0  3734k      0  0:00:03  0:00:03 --:--:-- 5900k

$ sudo chmod +x /usr/local/bin/docker-compose    <- 実行権限を与える
$ docker-compose --version
docker-compose version 1.27.0, build 980ec85b    <- インストールされたことを確認

gitインストール

ec2
$ sudo yum install git-all
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                                                                                                                                           | 3.7 kB  00:00:00     
依存性の解決をしています
--> トランザクションの確認を実行しています

ローカル(Mac)で作成した環境を取り込む

今回はgithub上にあるリポジトリからクローンする

ec2
$ git clone https://github.com/xxx.git
Cloning into 'typing-app'...
Username for 'https://github.com': xxx    <- githubのユーザー名を入力
Password for 'https://narita-akk@github.com':     <- githubのパスワードを入力
remote: Enumerating objects: 133, done.
remote: Counting objects: 100% (133/133), done.

[ec2-user@ip-172-31-47-56 ~]$ ls    <- クローンされたことを確認
test-project

Dockerのデーモンを起動する

ec2
$ systemctl status docker.service    <- デーモンの状態を確認
 docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: https://docs.docker.com

$ sudo systemctl enable docker.service    <- デーモンを有効化する
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

$ systemctl status docker.service    <- デーモンが有効化されたことを確認
 docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: https://docs.docker.com

$ sudo systemctl start docker    <- デーモンを起動する

$ systemctl status docker    <- デーモンが起動されたことを確認
 docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since  2020-09-11 23:03:25 UTC; 5s ago
     Docs: https://docs.docker.com
  Process: 3651 ExecStartPre=/usr/libexec/docker/docker-setup-runtimes.sh (code=exited, status=0/SUCCESS)
  Process: 3638 ExecStartPre=/bin/mkdir -p /run/docker (code=exited, status=0/SUCCESS)
 Main PID: 3659 (dockerd)
    Tasks: 8
   Memory: 118.4M
   CGroup: /system.slice/docker.service
           └─3659 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=1024:4096
:

Dockerコンテナを構築し起動する

ec2
$ docker-compose up -d --build
Creating network "typing-app_default" with the default driver
Creating volume "typing-app_db-store" with default driver
Building app
Step 1/7 : FROM php:7.4-fpm-buster
7.4-fpm-buster: Pulling from library/php
d121f8d1c412: Pull complete
:

$ docker ps    <- 起動されているコンテナを確認
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                   NAMES
734c90dda24a        nginx:1.19.2-alpine   "/docker-entrypoint.…"   2 minutes ago       Up 2 minutes        0.0.0.0:10080->80/tcp   typing-app_web_1
0e545f085ec4        typing-app_app        "docker-php-entrypoi…"   2 minutes ago       Up 2 minutes        9000/tcp                typing-app_app_1
f860eb86350e        mysql:8.0             "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        3306/tcp, 33060/tcp     typing-app_db_1

Laravelの初期設定をする

ec2
$ docker-compose exec app bash    <- Laravelがインストールされているコンテナに入る
ec2(app)
$ composer install    <- 必要なパッケージをインストール
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 106 installs, 0 updates, 0 removals
  - Installing doctrine/inflector (2.0.3): Downloading (100%)         
  - Installing doctrine/lexer (1.2.1): Downloading (100%)         
  - Installing dragonmantank/cron-expression (3.0.1): Downloading (100%)         
  - Installing voku/portable-ascii (1.5.3): Downloading (100%)         
:

$ ls -al    <- .env.exampleがあることを確認
total 304
drwxrwxr-x 12 1000 1000   4096 Sep 12 05:33 .
drwxr-xr-x  1 root root     33 Sep 12 05:33 ..
-rw-rw-r--  1 1000 1000    220 Sep 12 05:26 .editorconfig
-rw-rw-r--  1 1000 1000    784 Sep 12 05:26 .env.example
-rw-rw-r--  1 1000 1000    111 Sep 12 05:26 .gitattributes
:

$ cp .env.example .env    <- .env.example.envにコピー

$ php artisan key:generate    <- アプリケーションキーを作成
Application key set successfully.

root@3e98c590943b:/work# ls -al    <- storageの権限を確認
:
total 304
drwxrwxr-x  5 1000 1000     46 Sep 12 05:26 storage
:

$ cd bootstrap/
$ ls
app.php  cache
$ ls -al    <- bootstrap/cacheの権限を確認
total 8
drwxrwxr-x  2 1000 1000   64 Sep 12 05:34 cache
:

$ cd ..
$ chmod -R 777 storage    <- storageフォルダに書き込み権限を付与
$ chmod -R 777 bootstrap/cache/    <-bootstrap/cache/フォルダに書き込み権限を付与
:

ec2の80番ポートを開けてhttpアクセスを許可する

ec2のセキュリティグループを編集する

スクリーンショット 2020-09-12 15.08.51.png

ec2にブラウザからアクセス

スクリーンショット 2020-09-12 14.48.10.png

Laravelが表示できた!

参考サイト

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

Qiitaを始めて3年で累計LGTM 1,000達成見てくれた人に感謝を伝えたい

概要

最初に言っておくと今回の投稿は技術的な話しは一切ありません。
際立った特徴のないの一般庶民の私がQiitaに投稿を始めておよそ 3年で累計LGTM 1,000 を達成!
したので、1つの区切りとしてその感謝を伝えたかったのと3年間を振り返った記録になります。

【2020年11月21日 LGTM 1,000達成】
image.png

まずは、私の投稿を読んでくれた人、さらにLGTMしてくれた人、コメントしてくれた人、フォローしてくれた人、最後にQiitaに関わる全ての人、本当にありがとうございます!
人に伝えることを苦手としていた私が3年間続けてこれたのは皆さんのお陰です(^^)
特にLGTMやコメント/フォローなど私の投稿にリアクションしてくれた人達は、3年間の私の活動のモチベーションでした!!

3年を長いと感じる人/短いと感じる人、LGTM 1,000を少ないと感じる人/多いと感じる人それぞれだと思いますが、この記事は私がLGTM 1,000を達成するまでの簡単な記録になります。
もしQiitaで活動をしている似たような誰かに良い影響が与えられれば良いなと思って投稿します。

私について

  • SE歴10年ほどの普通の会社員です
  • 本当に普通です。Qiitaには凄い経歴を持っている人がたくさんいると思いますが、私は社内で表彰されたこともなければ、活発に勉強会に参加するタイプでもない普通の会社員です(^^;)
  • もちろんQiitaのトレンドにランクインしたこともありません
  • 敢えて長所と思っているところを上げるとしたら新しい技術に触れるための勉強は好きかもしれません
  • それに伴ったQiitaへの投稿やたくさんの人からのリアクションはもっと好きです(^^)
  • 最近、習慣(コツコツと続けること)の大切さを身に染みて感じている普通のQiita利用者です

LGTM 1,000までの実績

  • Qiita活動
    • 活動期間およそ 3年
      • 2017年11月11日 初投稿
      • 2020年11月21日 LGTM 1,000達成
      • つまり延べ日数は1,106日
    • 投稿数 37記事
    • 投稿記事の閲覧合計数 2020年11月21日時点、46万PV(460,550PV)でした
      • 見てくれた方、本当にありがとうございます!

投稿ペース

image.png

  • 3年(37ヵ月)で37記事のため、ちょうど1記事/月でした(1記事/月をノルマとしたわけではないのでたまたまです)
  • 最も記事投稿が多かった月が2018年1月と2018年6月で5記事/月でした
  • もっとも投稿しなかった期間が2018年10月から2019年1月までで投稿なし0記事/4ヵ月でした 反省。。。

月別閲覧数

image.png

  • 初めて1ヵ月間で1,000PVを超えたのは2018年1月。この時点の投稿数5記事 初投稿から3ヵ月後
  • 初めて1ヵ月間で10,000PVを超えたのは2018年11月。この時点の投稿数22記事 ちょうど初投稿から1年後でした
  • 1ヵ月の最高閲覧数は2019年7月の17,709PVでした
  • 3年間の1ヵ月平均閲覧数は12,447PVでした
    • つまり400PV/日。これは何気に凄い気がします。皆さん閲覧ありがとうございます!
  • ちなみにこのグラフはGoogleアナリティクスのもの。以下を参考にQiita利用開始の時に設定しました

LGTM数

image.png

LGTM数トップ3

  1. 累計LGTM数 349:WindowsでDocker環境を試してみる
  2. 累計LGTM数 80:無料でWebアプリケーションを公開できるHerokuをやってみた
  3. 累計LGTM数 79:AWS初心者がAWS 認定ソリューションアーキテクト – アソシエイト資格試験に合格した時の勉強法
  • 私が普段Webアプリケーション担当でいかに早く品質良く作る時のベストはなにか?をテーマにQiitaの活動をしているので目的と一致している?かなと思います
  • またこれらDocker/Paas/AWSといった好きなテーマにLGTMが多かったためここまで続けてこれたかなとも思っています

まとめ 3年間Qiitaに投稿してみてわかった事実

  • 私のようなの普通の利用者でも3年継続することで累計1,000LGTM、46万PVを達成できる!!
    • それだけ誰かの役に立てる(実際立てた?) およそ1LGTM/日のため1日1人の役には立てた?(^^;)
  • それには1記事/月くらいの記事投稿が必要
  • そのペースであればQiita利用開始1年後には1ヵ月10,000PVを達成!できる
  • 1年後には330PV/日、3年後には400PV/日を達成!!できる
  • 好きなことだから続けられる なるべくノルマとか義務化にしないように好きな技術でQiita生活を楽しみましょう

以上!
これからもQiita生活を楽しんで続けます。
目指せ10,000LGTM!!、100万PV!!

最後にここまで読んでくれた方、今回も本当にありがとうございました(_ _)
これからも自分の知識の整理のと発言力の向上、また誰の何かに貢献できるよう、自分のペースで投稿を続けていこうと思います。

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

【この記事だけで完結】kaggle公式imageを使ってGCP+DockerでGPU環境構築

はじめに

この記事はほぼ自分のメモ用です。

結論としてはDocker+GCPを使ってGPUが動くkaggle環境(kaggle notebookと同じ)を作ります。

突然ですが

最近のkaggleのコンペ、データがめっちゃくちゃ多くてメモリが足りなかったり、cudf必須で無限のGPU時間が必要だったり、notebookコンペで訳わからんsubmission errorが出たり、しんどくないですか??????

そのしんどさを少しでも軽減できればなと思いDocker+GCPでの環境構築をはじめました。

この記事の嬉しさは以下かなと思います。
・この記事だけで全て完結する
・どんなにメモリが必要なコンペでも可変的に対応することができる
・gpu時間が無制限(お金はかかる)
・kaggle notebookと全く同じ環境を作れるのでsubmission errorの原因が少なくなる

またこの記事は実行場所が
・ローカル
・GCEインスタンス
・コンテナ
と目まぐるしく入れ替わります。

コードブロックの左上にどこで実行すればいいか記載しているので注意してください。

ちなみに

既存のQiitaの記事をうまく3~4個くらい合わせれば同じことができるんで車輪の再発明と言われればそうなんですが、一つの記事で構築できた方が楽かなと思いこの記事を書いてます。(というか自分が複数の記事を参照しながらやっててめんどくさかった)

完全に自分用の備忘録です。

データセットの扱い方とか、もっといい方法があればコメントで教えてください。

目次

目次
GCEインスタンスを建てる
CloudSDKを使ってローカルから接続
cudaのverを合わせる
GCEインスタンス上でDockerを利用しkaggle環境を構築
分析データをコンテナ内に展開しコンテナに入る
コンテナ内で動作確認
再開方法
おまけ(自動補完)

GCEインスタンスを建てる

まずGCPのアカウントを作ってください。以下のリンクから飛べます。

https://cloud.google.com/gcp/

そうしたら新しくプロジェクトを作成し、左上のメニューからMarketplaceを選択します。

ここで適当に「deep」などと検索し、以下を選択します。

GCEからもっとカスタマイズしてインスタンスを建てる方が良いのかもしれませんか、コマンドやDockerを使ったりするための環境構築がめんどくさいので「deep Learning VM」を使用します。

運用開始をクリックすると以下のような設定画面が現れます。

自分が好きなように設定しましょう。なお、自分は以上のように構築しました。
後からCUDAを上書きするのでFrameWorkはなんでも大丈夫です。(ここが綺麗じゃないので気になる人はGCEから適切にインスタンスを建てた方がいいと思いますが、自分はめんどくさかったのでこの方法を使っています。)

ここで2点注意があります

1.SSDのサイズを100GB以上にしましょう
kaggle公式imageがかなり大きいので、100GBより小さいとビルドに失敗するケースがあります。(友人に試してもらったところ80GBで失敗しました)

2.CuDFを使用したい方はGPUの選択に気をつけてください。
以下はhttps://github.com/rapidsai/cudfの画像ですがCuDFを動かす場合、「Pascal architectureかより良いもの(>= P100)」を使用する必要があります。(k80とかだと動きません)
なのでP100を選択するのが無難です。

各GPUと対応しているregionについては下記のサイトで確認することができます。
"https://cloud.google.com/compute/docs/gpus"

なお、最初はGPUの割り当て上限が0になっているので、警告から割り当てページにとんで上限を編集してください。

申請を行うとメールが届き、その後10分くらいで上限が変更されます。

以上の設定が終わったらデプロイをクリックしましよう。
これでGCEインスタンスを建てることができました!

CloudSDKを使ってローカルから接続

次にcloud SDKを使って先ほど建てたGCEインスタンスに接続します。以下のサイトを参照にすれば問題なく可能ですが、一応mac版だけ手順を示します。
(どう考えても公式の説明の方がわかりやすく正しいので、スキップ推奨です完全に自分用に書いています。)

https://cloud.google.com/sdk/docs/install

まず上記のサイトに行き、macOS 64bit版をダウンロードしましょう。(なお上記のページにも書いてありますが、Pythonが必要です。ない場合は各自インストールしてください)

n

ローカル
./google-cloud-sdk/install.sh

を実行し、パスを通して、

ローカル
./google-cloud-sdk/bin/gcloud init

を実行し、指示に従いGoogleアカウントやプロジェクトへの紐付けを行います。(プロジェクトの変更や、googleアカウントの変更の時もこのコマンドを利用します。)

そうするとgcloudコマンドが使えるようになっているので、以下のようにコマンドをターミナルで実行し、ssh通信を行ってください。

ローカル
gcloud compute ssh [インスタンス名] --zone [ゾーン名]

ちなみに自分の場合は以下のようなコマンドを実行することになります。

ローカル
gcloud compute ssh sample-vm --zone us-west1-b

これでGCEインスタンスに接続することができました。

なお、初回接続時にはnvidiaのドライバを入れるかどうか聞かれますが、次の節で入れるので「no」で大丈夫です。(一応「yes」verも試してみましたが、綺麗に上書きされるようで動作しました。)

次はDocekrを利用し、kaggle環境をコンテナ内で再現していきます。

cudaのverを合わせる

今回は公式の以下のレポジトリを利用してコンテナを作成していきます。
https://github.com/Kaggle/docker-python
ここでgpu.Dockerfileを見てみましょう。

そう!cuda10.2なんです!
本当はGCEインスタンスを建てる時cuda10.2対応のFrameworkを使用するのが楽なんですが、なんと現在10.2だけ対応していません...

なのでhost側(GCEインスタンス)にもcuda10.2をいれる必要があります。
「cudaを自分でいれる」と聞いて苦虫を潰したような顔になっているかもしれませんが、とても簡単です。(自分も最初ver合わせないといけないことに気づいた時絶望しました)
二行のコマンドを実行するだけです。以下のリンクからnvidiaの公式サイトに入り、
https://developer.nvidia.com/cuda-toolkit-archive
対応しているverをクリックしましょう。(今回だと10.2)

そうすると以下のような画面が出てくるので、
linux→x86_46→Ubuntu→18.04→runfile[local]
の順で選択します。

そうするとBase Installerコマンドが出てくるのでこれをコピーして実行するだけです。

現在GCEインスタンスの中にgcloudコマンドで入っていると思うのでそのままコピーして実行します。

GCEインスタンス内
$ wget https://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux.run
$ sudo sh cuda_10.2.89_440.33.01_linux.run

なお、下記のような表示が出てきますが、accept→一番下まで行ってSelect→Yesで大丈夫です。($ sudo sh cuda_10.2.89_440.33.01_linux.run
は少し時間がかかりますが心配しないでください)



インストールが終わったら以下のコマンドを実行して、GPUが認識されているか、指定したverのcudaが入っているか確認しましょう。

GCEインスタンス内
$ export LD_LIBRARY_PATH=/usr/local/cuda/lib64
$ nvidia-smi


自分の場合、cuda10.2が入っており、P100が認識されているため上手く行ったようです。

GCEインスタンス上でDockerを利用しkaggle環境を構築

次にGCEインスタンス上にDockerを利用し、kaggle環境を構築します。
と言っても簡単で、以下のコマンドを実行し終わりです。
なお、ビルドにかなりの時間がかかることに注意してください。(30~60分程度)

GCEインスタンス内
$ git clone https://github.com/Kaggle/docker-python.git
$ cd docker-python

$ ./build --gpu
$ docker images

なお、GCEインスタンスの起動中はお金がかかるので、ビルドしてるのを忘れて寝たりしなしでください!
docker imagesで以下のように表示されていたら成功です。

分析データをコンテナ内に展開しコンテナに入る

次に分析データをコンテナ内に展開し、コンテナに入ります。
手順としては以下の手順で行います。

ローカルのデータをGCEインスタンスにコピーする→コンテナを作成し、GCEインスタンスのデータをマウント

kaggle apiを使っても良いですが、ローカルのデータを分析したり、外部データを使用する時などに必要になってくるのでこの手順を踏みます。(逆にデータの大きさが100GBとかの場合はkaggle apiからデータを入れた方がいいです。)

まずサンプルとして以下のような適当なdatasetをダウンロードします。
https://www.kaggle.com/c/titanic/data

適当なディレクトリを作成し、そこにダウンロードしたデータを入れましょう。(今回はsample_datasetとします)

次にGCEインスタンスの中にデータを入れるためのディレクトリを作成します。今回はデモなので必要最低限で行います。

GCEインスタンス内
$ mkdir kaggle
$ cd kaggle
$ mkdir dataset

次に別のタブを開き以下でファイルを移動させます。

ローカル
$ gcloud compute scp --recurse ローカルのファイルパス GCEインスタンス名:コピー先パス --zone "ゾーン名"

今回の場合だと(自分の場合)

ローカル
$ gcloud compute scp --recurse ~/Desktop/sample_dataset sample-vm:~/kaggle/dataset --zone=us-west1-b

そうするとこのようにGCEインスタンス上にデータセットがコピーされています。

次にDockerのコンテナを起動させて、コンテナの中に入りましょう。

GCEインスタンス内
$ docker run -itd  --runtime=nvidia  -p 8888:8888   -v ~/kaggle/dataset/sample_dataset:/home/kaggle   --name kaggle-gpu   -h host   kaggle/python-gpu-build /bin/bash
$ docker container start kaggle-gpu
$ docker container attach kaggle-gpu

ちなみにここで重要なのは-v ~/kaggle/dataset/sample_dataset:/home/kaggleの部分で、~/kaggle/dataset/sample_datasetがマウント元、/home/kaggleがマウント先になります。
ファイル編集などはマウントされているディレクトリで行うようにしましょう。

コンテナ内に入ったらパスを通してgpuが使えるかどうか確認しましょう。

コンテナ内
$ export LD_LIBRARY_PATH=/usr/local/cuda/lib64
$ nvidia-smi

自分の環境では以下のように表示され、cudaのverが10.2で、GPUがP100と表示されていることから上手く使えていることがわかります。

次にコンテナ内からjupyter notebookを起動しましよう。

コンテナ内
$ cd home
$ jupyter notebook --port 8888 --ip=0.0.0.0 --allow-root

次に別タブを開き、以下のコマンドで http://localhost:28888 からコンテナ内で起動させたノートブックに接続できるようにします。

ローカル
$ gcloud compute ssh インスタンス名 --zone ゾーン名 -- -N -f -L 28888:localhost:8888

なお、自分の場合だと以下のようになります。

ローカル
$ gcloud compute ssh sample-vm --zone us-west1-b -- -N -f -L 28888:localhost:8888

ブラウザからhttp://localhost:28888にアクセスし以下のような画面が表示されれば成功です
お疲れまでした!

なお、windows環境だと-fがいらず、で接続可能です。

ローカル(windows)
$ gcloud compute ssh sample-vm --zone us-west1-b -- -N -L 28888:localhost:8888

コンテナ内で動作確認

それでは最後にGPUが動くかどうか試してみましょう。
以下のようにP100が使われています。

また,以下のようにCuDFが使えていることがわかります。

これでDocker+GCPを使ってGPUが動くkaggle環境(kaggle notebookと同じ)が完成しました!
なお、GCEインスタンスの起動中はお金がかかるので、こまめに止めることを気をつけてください!
お疲れ様でした!

また、適当にnotebookを作成してみてください。
GCEインスタンスを止めた後でも反映されたかを試すために利用します。
自分はsample_dataset以下にsample.ipynpを作成しました。

再開方法

最後に一度停止させたGCEインスタンスに入り、コンテナを起動する方法を紹介します。
まずGCPのプロジェクトからGCEインスタンスを再起動させます。

次にインスタンスの再起動に成功したら以下のコマンドでローカルからインスタンス内に入ります。

ローカル
$ cloud compute ssh sample-vm --zone us-west1-b

以下のコマンドでコンテナの状態を確認しましょう。

GCEインスタンス内
$ docker ps -a

以下のように表示されていればOKです。

以下のコマンドでコンテナをrestartし、コンテナ内に入りましょう。

GCEインスタンス内
$ docker container restart kaggle-gpu
$ docker container attach kaggle-gpu

その後コンテナ内でhomeディレクトリに移動し、jupyter notebookを起動します。

コンテナ内
$ cd home
$ jupyter notebook --port 8888 --ip=0.0.0.0 --allow-root

さらに別タブを開いて以下のコマンドでhttp://localhost:28888で開けるようにします。

ローカル
$ gcloud compute ssh インスタンス名 --zone ゾーン名 -- -N -f -L 28888:localhost:8888

なお、自分の場合だと以下のようになります。

ローカル
$ gcloud compute ssh sample-vm --zone us-west1-b -- -N -f -L 28888:localhost:8888

http://localhost:28888にアクセスするとjupyter notebookにアクセスすることができます。

GCEインスタンス停止前に作成したnotebookがあることを確認してください。(マウントしているのでGCEインスタンスからでも確認することができます)

以上で再開方法は終わりです。
ちなみに作業を終えるときはctrl+cでnotebookを終了し

コンテナ
$ exit

で抜けることができます。(再開時はやはりrestart&attachです)

何度もいいますが。お金がかかるのでこまめにGCEインスタンスは停止してください

おまけ(自動補完)

おまけにデフォルトの設定のままでは非常に使いにくいのでjupyter notebookに自動補完機能をつける方法を説明します。
コンテナの中に入り以下のコマンドを実行します。

コンテナ
$ pip install jupyter-contrib-nbextensions
$ pip install jupyter-nbextensions-configurator

$ jupyter contrib nbextension install
$ jupyter nbextensions_configurator enable

次にkaggle notebookを開き、タブのところに「」という項目が増えているのでNbextensionsからHinterlandを有効にします。

これで自動補完が可能になります。

以上で備忘録は終わりです!
動かないところがあったらコメントで教えてください!
よろしくお願いします!

参考文献

GCPとDockerでKaggle用計算環境構築

GCPとkaggle公式イメージ(docker)でGPU環境の構築

Jupyter Notebookでコードの自動補完をする

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

DockerfileからWebアプリケーションを手軽にAWSへデプロイする方法3選

先日LightsailがDockerイメージからのデプロイをサポートしました。
https://aws.amazon.com/jp/blogs/news/lightsail-containers-an-easy-way-to-run-your-containers-in-the-cloud/

AWS上でのコンテナサービスはECS/EKSをはじめ様々ありますが、今回のLightsailコンテナは、シンプルなアプリケーションを簡単に提供することに特化したサービスと言うことが出来そうです。
また、ECSのデプロイツールであるCopilotもGAとなりました。
こちらもとても簡単にECS環境が構築できるということで注目です。

そこで、現在AWSでDockerイメージから手軽にWebアプリケーションをデプロイする方法をいくつか比較してそれぞれの使いどころなどを考えてみたいと思います。

今回は手軽にデプロイが可能な方法として以下の3つについて比較します。

Lightsail

Lightsailのコンソールからコンテナサービスを起動することで、All-in-OneのWebアプリケーションが構築できます。

デプロイ手順

参考: プライベートなコンテナイメージを Amazon Lightsail コンテナサービスで使う | Developers.IO

詳細な手順は上記参考記事を見て頂くとして、必要な作業ステップは

  1. 事前準備
    1. AWS CLI v2のインストール
    2. lightsailプラグインのインストール
  2. DockerイメージをLightsailにpush
    1. ローカルでdocker buildを実行
    2. AWS CLIでaws lightsail push-container-image ...を実行
  3. デプロイ
    1. push済みのイメージからデプロイするイメージを選択
    2. インスタンスサイズやPort、環境変数などを設定

となります。

コンテナレジストリがアプリケーションにセットで付いていてPushとデプロイがそれぞれ行えるようなイメージですね。
新しいイメージをデプロイした結果ヘルスがNGになった場合は自動で以前の正常なコンテナにロールバックもしてくれるようです。

AutoScaling

インスタンス数の変更やスケールアップはボタン1つで簡単に実行可能ですが、
今のところ、AutoScalingには対応していないようです。

メトリクス

CloudWatchアラームにも今のところ対応していないようですが、
簡単なCPUとメモリの平均のみは確認出来ます。
metrics.png

ログ

CloudWatch logsにログを流すことは出来ないようです。
Lightsailコンソールからのみログを確認することが出来ます。
image.png

VPC接続

今のところVPC接続が出来る様子はありません。
あくまでコンテナで簡潔するケース、publicにアクセス出来るサービスとの連携のみで足りるケースでの利用を想定した方がよさそうです。
private subnetにいるRDSと連携するような用途は今のところ難しいのかも。

まとめ

  • ローカルでbuildしたイメージをPushしてデプロイするだけ。というのはとてもシンプル
  • ロードバランサーも含まれていてある程度の可用性もあるのもよい
  • 監視やAutoScalingがちゃんと欲しい場合、RDSにつなぎたい場合など、本格的なサービス提供に使うのは難しそう

Elastic Beanstalk (Docker platform)

BeanstalkのプラットフォームでDockerを選択することで、プラットフォームとしてDockerEngineを動かしてその上でコンテナを起動して動かすことが出来ます。

参考: Docker プラットフォームの使用

デプロイ手順

デプロイまでのステップはおおよそ以下の流れになります。

  1. 事前準備
    1. Dockerプラットフォームを選択したBeanstalkの環境を作成する
  2. Beanstalkの構成定義を作成
    1. Dockerrun.aws.jsonを作成
    2. Dockerfile,Dockerrun.aws.json,Dockerのbuildに必要なファイルを含むArtifact一式を構成
  3. デプロイ
    1. eb deployなどのコマンドでデプロイ

Dockerrun.aws.jsonサンプル

{
  "AWSEBDockerrunVersion": 1,
  "Ports": [
    {
      "ContainerPort": 8000
    }
  ]
}

deploy.zipサンプル

deploy.zip
 |- Dockerfile
 |- Dockerrun.aws.json
 |- index.html

DockerfileとDockerrun.aws.jsonを含むzipファイルを作成し、それをデプロイすることで、各インスタンスでdocker build & docker runが実行されコンテナを起動することが出来ます。

ECR等のコンテナレジストリからPullすることも出来ますが、今回想定している簡単なDockerイメージのデプロイという用途としてはこれで十分なケースもあるかと思います。

環境が構築出来れば、後は一式のzipファイルをデプロイするだけですのでその後の展開は容易です。

運用

Beanstalkの場合はEC2インスタンス上でDockerホストが起動してコンテナが立ち上がる構成になるため、標準的な運用インフラはほぼほぼそのまま使えることになります。
EC2単独での実行、ALBとAutoScalingグループを含んだ冗長構成、CloudWatchとの連携やVPC、SecurityGroupの設定など個別のカスタマイズが可能です。
ただし逆に言うとカスタマイズ箇所が多いということでもあり、手軽さという点では一歩劣ります。
EC2インスタンスで動く関係上、Fargateのように細かなCPU/メモリスペックを指定してサービスを構築することは出来ません。

まとめ

  • Beanstalkの仕組みに乗る形でコンテナサービスを構築できる
    • Beanstalkの運用に慣れている人には向いている
  • 必要な機能は一通りカスタマイズして使える
  • EC2インスタンス上で動くためECS Fargateほどの柔軟性はない

Copilot (ECS)

CopilotはECSを構築するためのCLIツールで、Copilotが提供するフレームワークに沿った形でサービスを定義して構築することで簡単にECSクラスタやその他必要なリソースを一括作成してくれます。

参考: AWS Copilot のご紹介 | Amazon Web Services ブログ

最近ついにv1.0.0がリリース:tada:されました。

手順

参考: ECSのオペレーションを劇的に簡略化するAWS Copilotが発表されました! | Developers.IO

詳細な手順は例によって上記記事が大変詳しいです。いつもありがとうございます。
必要なステップは下記の通りです。

  1. 事前準備
    1. copilotのインストール
  2. 環境の構築
    1. Applicationの作成
    2. Environmentの作成
    3. Serviceの作成
    4. Manifestの定義
  3. デプロイ

Applicationの作成

Getting Startedとしては記事の通りcopilot initで対話的にセットアップするのが早くて便利ですが、細かくカスタマイズしたい場合はcopilot app initで始めることで空のアプリケーションを作ることも出来ます。

copilot app initコマンドではApplicationの登録と、Copilotから諸々操作するのに必要なRoleだけが作られます。
この時Applicationが登録されるAWSアカウントはaws configureでdefaultに紐付けられたアカウントか、環境変数AWS_PROFILEで指定されたアカウントのようです。
--profileパラメータは指定出来ません。

Environmentの作成

copilot env initコマンドでdev/stg/prod/testといった環境を作成出来ます。
ここでは--profileパラメータも指定出来るため、prod環境だけ別のAWSアカウントへデプロイするといったことも出来そうです。
また、ここでVPCやサブネットを指定することで既存のVPCにタスク定義を配備することも出来ます。

copilot env init --name prod --profile prod-web \
  --import-vpc-id vpc-xxxxxxxx \
  --import-public-subnets subnet-aaaaaa,subnet-bbbbbbbbb \
  --import-private-subnets subnet-ccccccccc,subnet-ddddddddd

Serviceの作成

copilot svc initコマンドでサービスを定義出来ます。
ここでDockerfileを指定しておきます。

copilot svc init --name api \
  --svc-type "Load Balanced Web Service" \
  --dockerfile ./Dockerfile

このコマンドで、指定したDockerfileをbuildしてpushするためのECRリポジトリも合わせて作成されます。

Manifestの定義

サービス作成でプロジェクト内に生成された${service-name}/manifest.ymlを編集して必要なインスタンスサイズ等を定義します。

manifest.ymlサンプル
http:
  path: '/'
  healthcheck: '/health'
cpu: 256
memory: 512
count:
  range: 2-3
  cpu_percentage: 70

range指定でAutoScalingを設定することも出来ます。

デプロイ

copilot deployコマンドで、指定した環境にデプロイが開始されます。
ローカルでdocker buildしたイメージをECRにプッシュし、ECSクラスターを作成し、タスク定義を作成し、サービスを開始するというところまで一気に実行されます。

Lightsailと比較するとさすがに設定項目は多いですが、ECSクラスターを手で構築する手間を考えるとCLIコマンド3つ4つとmanifestファイルの定義のみでWebサービスが立ち上がるのは相当に楽です。
Beanstalkの各種設定と比較してもかなり楽に思えます。

注意点としてはまだGAしたばかりということで細かいところではうまく動かないところもありそうですが今回みたいようなシンプルなケースでは概ね問題なさそうでした。

まとめ

  • ECSを少し抽象化したフレームワークのような感触でWebサービスを立ち上げることが出来る
  • ECSの構築に必要なロールの扱いなどを意識しなくてよい

料金比較

ここではそれぞれの方法について同程度スペックでの料金比較するため

vCPU : 1
メモリ : 2GB
リージョン : ap-northeast-1
インスタンス数 : 2

の条件で1ヶ月動かした場合の料金を試算してみました。
データ転送量その他の料金は除いた概算です。(2020/12現在)

Lightsail

プランMedium x2 : \$80

Beanstalk

EC2 (t2.small) x2 : \$49
ALB: \$17
合計: \$66

ECS

Fargate: \$35
ALB: \$17
合計: \$52

同条件で比較するとLightsailが意外と安くないなという感じになりますね。

あくまで同条件での見積もりになるので、
小規模な場合はどうかというケースで例えばt4g.nano(2vCPU,0.5GB)あたりにするとBeanstalkは\$30ほどになりますが、Lightsailではnanoプラン(0.25vCPU,0.5GB)では\$14まで下げることができます。

ALBがコミというところにLightsailの料金的な強みがあるので、小規模の方が相対的にLightsailに分があり、規模が大きくなるほどECS Fargateに移行する方がメリットが出てきそうです。

結論

小規模で簡単なアプリケーションを単独でホストしたいならLightsailはとても強いサービスと言えそうです。
一方で出来ないことも多く、本格的なサービス運用にはさすがに向かなそうですので、その場合はCopilotを活用してECSでサービス提供するというのは手軽さを考慮してもとても良さそうに思えます。

Beanstalkによるアプリケーション構築は、Beanstalkでの構築に慣れている人にとっては十分簡単ですが、他の2つと比較すると相対的に重たい手順になってきました。
まとめ始めた時は手軽さ⇔高機能の関係がLightsail < Beanstalk < Copilotみたいな順になるかなと想像していましたが、思ったよりBeanstalkのメリットが小さくなっていましたね。

少し前までは「とはいえECSは初期セットアップもそれなりに大変ですし学習コストもあるので、簡単なアプリケーションをホストしたいだけであればBeanstalkも便利ですよ」などと推していたりもしたのですが、いよいよBeanstalkのDockerプラットフォームは役割を終えたと言える日が近そうです。

Copilotは先日1.0.0がリリースされたばかりということでこれからもっとECSが使いやすくなっていくと思われるので今後も期待していきたいです。

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