- 投稿日:2020-12-08T23:52:37+09:00
チャンプルーDocker環境(Rails+Laravel+MySQL)
はじめに
書くネタがなかったのでニッチかもしれませんが、タイトルの通りRails+Laravel+MySQLのDocker環境を構築してみました。突貫で構築したので所々至らぬ点や改善点があるかと思いますのでお手柔らかにアドバイス頂けると助かります
ちなみにチャンプルーとは沖縄の方言で『ごちゃまぜ』という意味です環境
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.ymlRails+MySQL環境
以前書いた記事を参考に構築しました
RubyをインストールせずにDockerでRails環境を構築する※変更点としてはフォルダパス(rails_app)が変わっているのとサービス名をRailsとLaravelでわかりやすいように変えています。あとはほとんど記事と同じ手順でRails+MySQL環境は構築出来ました
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: - databaseLaravel環境
まず、Laravel用のDockerfileを用意します。
laravel_app/DockerfileFROM 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.ymlversion: '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/.envDB_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/DockerfileFROM 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"]
- 投稿日:2020-12-08T23:27:54+09:00
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/amd64wire は以下のコマンドで取得できます。
$ go get github.com/google/wire/cmd/wireget が完了したら 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.gowire_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 さんにたくさん助けていただきました。DockerfileFROM 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 を見ていただければわかるかもですが、マルチステージビルドってのも初めて使いました。
- 投稿日:2020-12-08T22:46:57+09:00
dockerimageの作成方法について 〜コンテナからcommit・dockerfileからbuild〜
全体像
コンテナに名前をつけて作る(コンテナからcommit)
通常コンテナを作る時は
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)
ページのにSupported tags~ Dockerfile linksがある
そこの見たいバージョンを選択(今回は18.04にする)
そうするとdockerfileの記載のあるGithubへ移動出来る。
ここに記載されている今回であればFROM scratchからの記載内容がdockerfile
ここにその対象imageについての記述をしていくことでdockerfileを作っていくdockerfileの作り方
dockerfileを作る時はファイル名は命名規則により「Dockerfile」とする
INSTRUCTION argumentsの形で書いていく
(例:「FROM ubuntu:latest」や「RUN touch test」)
基本最初はFROMから書いていくルールfileの具体的な作り方(今回はデスクトップに作ることを想定)
ターミナルを開き
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 testdockerfileをbuildする(buildによってdockerfileからdocerimageが作成される)
dockerfileのあるディレクトリへ移動する(仮にdockerfileがあるディレクトリを「Docker」とする)
cd Docker
を使用し対象ディレクトリへ移動。
対象のdockerfileのあるディレクトリへ移動したらdocker build . //ここで使用している「.」はカレントディレクトリを指している(この部分に書くのはbuildをするディレクトリ)。 //現在いるディレクトリのdockerfileをbuildするという指定をする役割をしている //基本はカレントディレクトリにbuildするので「.」で覚えればOKこれでbuild完了。
docker build後に
docker images
コマンドをすると
一番上に<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の内容が反映されているか確認
docker run -it new-ubuntu bash ls上記コマンドでコンテナに入り確認するとtestというファイルが作成されていることが確認できる。
この「test」がdockerfileにてRUN touch test
で記述した内容から生成されたもの。
- 投稿日:2020-12-08T22:28:40+09:00
ラズパイに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カードに書き込み
Raspberry Pi Imager使うと楽に書き込みまでできる。
3. ラズパイの起動
SDカードをラズパイに挿入し、電源を入れる。
Ubuntuの初期設定(最低限やっていること)
1. Ubuntuにログイン
初回ログイン後、パスワードの変更を要求される。
初期ユーザ 初期パスワード ubuntu ubuntu 2. Ubuntuのアップデート
以下のコマンドで、パッケージをアップデートする。
$ sudo apt-get update $ sudo apt-get upgrade3. 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 restartPCより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 Anywhere6. 時刻設定
ntpのインストールする。
$ sudo apt-get install ntp以下の通り、ファイルに追記する。
/etc/ntp.confserver -4 ntp.nict.jp server -4 ntp1.jst.mfeed.ad.jp server -4 ntp2.jst.mfeed.ad.jp server -4 ntp3.jst.mfeed.ad.jpntpを再起動する。
sudo service ntp restartntpの設定を確認する。
$ 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.5357. サービスの自動起動
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-all8. IPアドレスの固定
環境に合わせて以下のファイルを作成する。
/etc/netplan/99_config.yamlnetwork: 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 apply9. ホスト名の変更
以下のコマンドでホスト名を変更する。
※ubuntu → raspi$ hostname ubuntu $ sudo hostname raspi $ hostname raspi10. OS再起動
$ sudo rebootdockerのインストール
ラズパイでdocker使うので、インストールする。
aptでdockerとdocker-composeをインストールする。$ sudo apt install docker docker-composedockerを自動起動に設定する。
$ sudo sysv-rc-conf docker on $ sudo sysv-rc-conf --list docker docker 2:on 3:on 4:on 5:onここまでで終わり
- 投稿日:2020-12-08T21:36:47+09:00
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 └── phpmyadmindocker-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:/sessionsdockerfile
FROM php:7.2-apache RUN apt-get update && \ docker-php-ext-install pdo_mysql mysqli mbstringindex.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 --buildphp.iniなどの変更を反映させたい時は
$ docker-compose restart動作確認
phpinfoを表示
http://localhost:8080/
phpmyadminを表示
http://localhost:4040/
ログイン画面でdocker-compose.yml
のmysqlコンテナの情報を記載してログインできればOK
つまづいたところ
- 当初は
docker-compose.yml
にimage: php:7.2-apache
と記載していたが、MySQLのドライバががなくてMySQL動かせなかったので、dockerflieを作成したdocker-compose.yml
のphpmyadminコンテナのPMA_USER
とPMA_PASSWORD
を記載すると、phpmyadminにうまくログインできず、データベースが作成できなかったので、コメントアウト今回やった、やる予定のPHP入門書
PHP入門 確認画面付きのお問い合わせフォームをつくりながらPHPを学ぶ(第2版)
https://amzn.to/2Irz7Fr
※上記環境で動作確認済みよくわかるPHPの教科書 【PHP7対応版】
https://amzn.to/2LhQ4mW
※まだ未着手参考記事
- 投稿日:2020-12-08T21:00:54+09:00
【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
DockerfileFROM 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 . /myprojectRails6でwebpackerが標準になったことでyarnのインストールが必要になってきます。
Docker Hubで確認するとrubyのイメージはDebian系となっているので、yarnの公式ドキュメントに記載されているOS毎のインストール方法を参考に記述していきます。
ちなみに、yarnの公式ドキュメントは日本語表示のままだとなぜかOSの選択肢の中にMacとWindowsしか表示してくれないので英語表示に切り替えて確認しましょう。docker-compose.yml
docker-compose.ymlversion: '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
Gemfilesource 'https://rubygems.org' gem 'rails', '~>6.0.0'既存アプリケーションの場合この記述はすでにあるはずですが念のため確認しておきましょう。
database.yml
database.ymldefault: &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
最後に
実装に思いの外手間取ったので備忘録的に記事にしてみました。
どなたかの参考になれば幸いです。
- 投稿日:2020-12-08T20:56:21+09:00
【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
その他
コンテナビルドツール
シークレット管理
- 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のサービスを使った方法を教えてくれるが、若干日本語が直訳ぽい
参考
- Kubernetes完全ガイド 第2版
- Kubernetes実践ガイド クラウドネイティブアプリケーションを支える技術
悩みに悩んだ Kubernetes Secrets の管理方法、External Secrets を選んだ理由 | PLAID engineer blog
カテゴリは主観たっぷりなのでMECEになってないかもしれないです。 ↩
- 投稿日:2020-12-08T20:16:37+09:00
Kubernetes is deprecating Docker as a container runtime after v1.20.
もう5日も流行に乗り遅れた感があるがこういう話題があるらしい。
Don't Panic: Kubernetes and Dockerとりあえずこのあたりを読んで落ち着こう。
Kubernetes 1.20からDockerが非推奨になる理由
Dockerは非推奨じゃないし今すぐ騒ぐのをやめろ
- 投稿日:2020-12-08T19:46:29+09:00
WindowsでDockerインストール後にVMWareで仮想マシンが起動できなくなる(KB76918)
問題
VMWareはWindowsの
Hyper-V
やWindows Sandbox
等のハイパバイザと共存できません。
Docker
をインストール後の修復方法を幾つか紹介します。1. Dockerをアンインストール
VMWareとDockerは共存できません。
Dockerをアンインストールして下さい。
しかし、Dockerインストール時に幾つかの機能が自動的に有効化されているので、VMWareの動作を取り戻すには幾つかの作業が必要です。2. Hyper-Vの無効化
コントロールパネル
->プログラムのアンインストール
->Windowsの機能の有効化または無効化
Hyoer-V
とWindows Sandbox
(念の為)がどちらともチェックを外して下さい。
変更後は再起動が必要です。3. ブート設定の確認と変更
ブート設定を確認します。
下記のコマンドを実行して下さい。
管理者権限必須です。$ bcdedit
このように出力されますので、
hypervisorlaunchtype
という項目を探して確認して下さい。
OFF
になっていなければ、次へ進んで下さい。
すでにOFF
になっていれば4
へ進んで下さい。bcdedit出力Windows ブート マネージャー -------------------------------- ~ Windows ブート ローダー -------------------------------- identifier {current} ~ bootmenupolicy Standard hypervisorlaunchtype Off <-これ Windows ブート ローダー -------------------------------- ~同じく管理者コマンドラインで下記コマンドを実行して
hypervisorlaunchtype
をOFFにします。$ bcdedit /set hypervisorlaunchtype off
以上です。
実行後は再起動が必要です。4. VMWareの修復
- 投稿日:2020-12-08T19:30:44+09:00
Private Docker Registryを構築しよう!(NginxでのBasic認証付き)
本記事は東京学芸大学 櫨山研究室 Advent Calendar 2020の八日目の記事になります.
はじめに
みなさんはDockerを利用していますか?
Dockerではイメージを共有し,イメージからコンテナを生成することが簡単にできます.
作ったアプリケーションをちょっと公開したいと思えば,サーバ上でイメージからコンテナを立ち上げるだけで実行することができ非常に便利ですね.一方でDockerイメージをそのように管理する方法は少し悩ましい問題だと思います.
本記事では自前のサーバにDocker Registryを立ててイメージを管理する方法を扱います.
構成
NginxをリバースプロキシにおいてDocker RegistryとGUIのクライアントソフトにアクセスできるようにします.
またNginxのBasic認証を有効にして簡易的に認証機能を持たせます.前提として
- サーバのドメインは取得済み
- サーバにDocker,Nginxは導入済み
- NginxのSSLは設定済み
- 動作環境はCent OS 7を想定
として進めます?♂️
Docker Registryの導入
Docker RegistryはDocker Imageを保管するアプリケーションです.
Docker Registryを自前のサーバで動かすことで
docker push
コマンドでローカルやCI環境で構築したDockerイメージを保存しDocker pull
コマンドでイメージの取得が行えます.Docker HubでDocker Registryの公式イメージが公開されています.
動かすだけなら以下のコマンドで5000番ポートで動作します.
bashdocker 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というツールを使います.
[画像引用元] 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=truebashdocker 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を再起動します.
bashsudo systemctl restart nginx
動作確認
ローカルのマシンからサーバ上のDocker Registryにアクセスを試みます.
bashdocker login ${サーバのドメイン名}ここでユーザー名とパスワードを聞かれるので先ほど作成した
test-user
とそのパスワードを入力します.認証が完了したら
docker push
コマンドでイメージをpushしたり登録されているdocker pull
コマンドでイメージをpullすることができます.次にdocker registry uiにアクセスしてみましょう.
ブラウザで
https://{ドメイン名}/registry-ui
にアクセスします.ユーザー名とパスワードを入力して,以下のような画面が見れれば成功ですね.
おわりに
本記事では自前のサーバにDocker Registryを構築する方法を扱いました.
PraivateなDocker Registryを構築したい!という方に参考になれば幸いです.
参考
Authenticate proxy with nginx, https://docs.docker.com/registry/recipes/nginx/
- 投稿日:2020-12-08T19:03:34+09:00
短編映画「Hello, world!インセプション」
第1階層 Windows
ファイル名を指定して実行
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)
>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)
$ 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)
$ 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
# 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).
というエラーが出てくる時は参考ページの通りにやると上手くいきます(検索用要約)。
- 投稿日:2020-12-08T17:20:13+09:00
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
を実行すると以下のように表示されます。
DevlopとReleaseとあって、CI/CDパイプラインもすぐ組めそう。
ドキュメントはこちらから。
copilot docs
でもいけます。↓
https://aws.github.io/copilot-cli/アプリを用意
(こちらを拝借)
https://github.com/Daviey/nyan-catDockerfileは以下のようにします。
FROM nginx:alpine EXPOSE 80 COPY . /usr/share/nginx/htmlさっそく実行
copilot initいくつかのやりとりを経てデプロイが実行されます。
ひとまずテスト環境として、アプリケーションがデプロイされるイメージです。
途中でCloudFormationのコンソールをみてみると、
こんな感じでスタックが作成されていきます。
ECRへimageのpushが終わりました。
デプロイが完了したようです。
一応、デプロイ完了後のCloudFormationコンソールも。。。
アクセスしてみる
コンソールをみると、URLあるのでそこからアクセスしてみます。
うごいたーー
猫が飛んでいます。ここまで15分くらいあれば速攻でECSデプロイいけます。
本番環境へデプロイしたい
本番環境としてデプロイしたい場合には
copilot env init
を実行すると、環境ごとのインフラを構築してくれます。
(nameはproductionとします)設定ファイル(manifest.yaml)の値を書き換えて、
以下コマンドを実行するだけでデプロイができます。copilot svc deploy --env production
以下のようにenviromentsを上書きするだけで、
複数のAZに対してコンテナを配置して、可用性の高いアプリケーションとすることが出来ます。(す、凄い)environments: production: count: 2 cpu: 1024 memory: 2048Copilotはどれだけ楽にしてくれるか
ECSでアプリケーションを実行するには最低でも以下AWSサービスを利用します。
- ECS - ECR - EC2( or Fargate) - ELB - VPC(サブネットも色々気にしないといけない) etc...多いです。
手で作る or Cfnで構築といった場合は上記すべてを網羅しておく必要があります。先程の
copilot init
を絵に表すとこんな感じ。。
(これも細かい所は省いています。間違っていたらすみません。)
コンテナの恩恵を被るには、結構準備が必要です。
デプロイしたいアプリケーションさえあれば、すぐにリリースできる。
そんなことがCopilotだと可能です。素敵ですね。考察
所属チームでは、システムのマイグレーションは中長期的な課題ではあるので、
コンテナ化する際の選択肢の一つとしてCopilotはアリかなと考えています。現状、マイグレーションの一環としてECS化は結構進んでいるので。。
弊社のようにのっぴきならない事情がない限りは、
こういったツールをドンドン活用していった方が良いかなと思われます。。マイグレーションの歩みはこちらから見れます。
ハンズラボエンジニアブログ最後に
最後まで読んでいただきありがとうございました。
消し忘れると課金されてしまうので、不要であれば消しましょう。
copilot app delete --env-profiles test=default以下でCopilotでの自動デプロイが紹介されているみたいなので、今度試してみたいと思います。
https://aws.amazon.com/jp/blogs/news/automatically-deploying-your-container-application-with-aws-copilot/参考情報
- 投稿日:2020-12-08T16:34:46+09:00
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 sampleappdockerfileの生成
ターミナル.touch dockerfiledockerfileの編集
作成した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.listRUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn
entrypoint.shの生成
touch entrypoint.shentrypoint.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.ymldocker-compose.ymlの編集
生成したファイルを以下のように編集します。
docker-compose.ymlversion: "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.lockrailsのプロジェクトを作成
ターミナル.docker-compose run web rails new . --force -d mysqldocker-composeをbuildする
ターミナル.docker-compose builddatabase.ymlの修正
config/database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password host: dbdocker-composeの再起動
docker-composeを一旦downさせてバックグラウンドで起動するようにコマンドを入力
ターミナル.docker-compose down docker-compose up -dデータベースの作成
ターミナル.docker-compose run web bundle exec rails db:createここまできたらlocalhost:3000/にアクセスしてみましょう。以下の画面が表示されるはずです。
deviseを用いた簡単なアプリの開発
ここからはアプリ開発の入り口をやっていきます。
コントローラーとビューを生成
トップページを表示するためにposts(投稿)コントローラーとindexのビューを生成します
ターミナル.docker-compose run web rails g controller posts index生成したらルーティングを設定します。routes.rbを以下のように編集しましょう。
config/routes.rbRails.application.routes.draw do root 'posts#index' get 'posts/index' endこの時点でlocalhost:3000/にアクセスすると以下のような画面になっています。
先ほど作成したpostsのindex(view)が表示されていることがわかります。deviseの導入
gemファイルの一番したにdeviseを記述します。
Gemfile.gem 'devise'bundle installを行う
dockerを利用している場合には通常行っているコマンドに "docker-compose run web"をつける必要があります。
ターミナル.docker-compose run web bundle installdockerの再起動
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-08T15:21:23+09:00
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 locallambdatestaws-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.pyimport 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 fiDockerImageをビルド
ここではイメージ名を「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.9testCOPY 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.txtginza==4.0.5app.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:latestdocker 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のパッケージフォーマットにコンテナイメージを指定してデプロイしてみた
- 投稿日:2020-12-08T12:29:14+09:00
さくらVPSにjprsのSSL証明書をインストール
環境
- CentOS(さくらVPS)
- Docker
- nginx
- openssl
ゴール
https://でアクセス、http://アクセス時はhttps://でリダイレクト
前提
- 既にhttp://でアクセス可能とする。
- dockerで環境構築済みとする。
- opensslインストール済みとする(さくらVPSにインストールされていたバージョンを使用)。
SSL証明書の発行に必要なCSR(Certificate Signing Request)を作成
SSL証明書を申し込む際に必要な情報の1つ。作成方法はjprs公式のマニュアルを参照し作成する。
鍵ファイル作成時のパスフレーズは忘れないように!
SSL証明書を購入する
使用するSSL証明書は低コストのjprsを選定。
さくらインターネットのコンパネにログインし、さくらインターネット経由で申し込む。
認証ファイルをダウンロードする
認証ファイルをサーバーに設置し認証局で承認されることでSSL証明書を取得することができるようになる。
まずは、SSL証明書を購入するとさくらインターネットの会員メニューから認証ファイルをダウンロードする。
ダウンロードされた認証ファイルのファイル名は数字とローマ字が混在したテキストファイル。
認証ファイルをサーバーに設置する
nginxに記載されているドキュメントルート/.well-known/pki-validation配下に認証ファイルをアップロードし
ブラウザから認証ファイルの内容が参照できればOK。
ドキュメントルートの確認は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分程度で発行されダウンロード可能となる。
SSL証明書をインストールする
jprs公式のマニュアルを参照しインストールする。
マニュアル通りに「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.txtxxxxxxxxxxxxxx <- パスフレーズdocker-compose.ymlに上記で作成したファイルの配置を記載する。
docker-compose.ymlversion: "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: /worknginxの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起動
macxxxMacBook:~ 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-08T09:54:37+09:00
【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で新たにスペースは必要ない
- 投稿日:2020-12-08T09:44:38+09:00
[解決](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 endoriginsは、サーバーサイド(API),フロントの順で書きます。
↓
Dockerの場合は、再度コンテナを立ち上げて
↓
完了。
- 投稿日:2020-12-08T09:32:21+09:00
AWS EC2上にDockerコンテナ(Laravel, MySQL, nginx)を起動しアクセスする
環境
- Mac
- Docker
- EC2 (Amazon Linux2, t2.micro)
- nginx
- Laravel
- MySQL
- Github
ゴール
前提
- 今回はお試しなので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-projectDockerのデーモンを起動する
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_1Laravelの初期設定をする
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のセキュリティグループを編集する
ec2にブラウザからアクセス
Laravelが表示できた!
参考サイト
- 投稿日:2020-12-08T08:28:49+09:00
Qiitaを始めて3年で累計LGTM 1,000達成見てくれた人に感謝を伝えたい
概要
最初に言っておくと今回の投稿は技術的な話しは一切ありません。
際立った特徴のないの一般庶民の私がQiitaに投稿を始めておよそ 3年で累計LGTM 1,000 を達成!
したので、1つの区切りとしてその感謝を伝えたかったのと3年間を振り返った記録になります。まずは、私の投稿を読んでくれた人、さらに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)でした
- 見てくれた方、本当にありがとうございます!
投稿ペース
- 3年(37ヵ月)で37記事のため、ちょうど1記事/月でした(1記事/月をノルマとしたわけではないのでたまたまです)
- 最も記事投稿が多かった月が2018年1月と2018年6月で5記事/月でした
- もっとも投稿しなかった期間が2018年10月から2019年1月までで投稿なし0記事/4ヵ月でした 反省。。。
月別閲覧数
- 初めて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数
- 以下の記事で投稿したGoogleスプレッドシートに今年の2020年1月から記録を始めたためそれ以前のデータはありません。。。
- 記録を始めた2020年1月からの平均は35LGTM/月。つまりおよそ1LGTM/日
- 単純計算だと37ヵ月で1,000LGTMなので27LGTM/月。なので今年は少しペースアップできたようです
- 1ヵ月の最高LGTM数は2020年10月の47LGTMでした
LGTM数トップ3
- 累計LGTM数 349:WindowsでDocker環境を試してみる
- 累計LGTM数 80:無料でWebアプリケーションを公開できるHerokuをやってみた
- 累計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!!最後にここまで読んでくれた方、今回も本当にありがとうございました(_ _)
これからも自分の知識の整理のと発言力の向上、また誰の何かに貢献できるよう、自分のペースで投稿を続けていこうと思います。
- 投稿日:2020-12-08T01:05:43+09:00
【この記事だけで完結】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が必要です。ない場合は各自インストールしてください)
ローカル./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が使われています。
これで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以下のコマンドでコンテナを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
を有効にします。
これで自動補完が可能になります。
以上で備忘録は終わりです!
動かないところがあったらコメントで教えてください!
よろしくお願いします!参考文献
- 投稿日:2020-12-08T00:14:39+09:00
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
詳細な手順は上記参考記事を見て頂くとして、必要な作業ステップは
- 事前準備
- AWS CLI v2のインストール
- lightsailプラグインのインストール
- DockerイメージをLightsailにpush
- ローカルで
docker build
を実行- AWS CLIで
aws lightsail push-container-image ...
を実行- デプロイ
- push済みのイメージからデプロイするイメージを選択
- インスタンスサイズやPort、環境変数などを設定
となります。
コンテナレジストリがアプリケーションにセットで付いていてPushとデプロイがそれぞれ行えるようなイメージですね。
新しいイメージをデプロイした結果ヘルスがNGになった場合は自動で以前の正常なコンテナにロールバックもしてくれるようです。AutoScaling
インスタンス数の変更やスケールアップはボタン1つで簡単に実行可能ですが、
今のところ、AutoScalingには対応していないようです。メトリクス
CloudWatchアラームにも今のところ対応していないようですが、
簡単なCPUとメモリの平均のみは確認出来ます。
ログ
CloudWatch logsにログを流すことは出来ないようです。
Lightsailコンソールからのみログを確認することが出来ます。
VPC接続
今のところVPC接続が出来る様子はありません。
あくまでコンテナで簡潔するケース、publicにアクセス出来るサービスとの連携のみで足りるケースでの利用を想定した方がよさそうです。
private subnetにいるRDSと連携するような用途は今のところ難しいのかも。まとめ
- ローカルでbuildしたイメージをPushしてデプロイするだけ。というのはとてもシンプル
- ロードバランサーも含まれていてある程度の可用性もあるのもよい
- 監視やAutoScalingがちゃんと欲しい場合、RDSにつなぎたい場合など、本格的なサービス提供に使うのは難しそう
Elastic Beanstalk (Docker platform)
BeanstalkのプラットフォームでDockerを選択することで、プラットフォームとしてDockerEngineを動かしてその上でコンテナを起動して動かすことが出来ます。
デプロイ手順
デプロイまでのステップはおおよそ以下の流れになります。
- 事前準備
- Dockerプラットフォームを選択したBeanstalkの環境を作成する
- Beanstalkの構成定義を作成
- Dockerrun.aws.jsonを作成
- Dockerfile,Dockerrun.aws.json,Dockerのbuildに必要なファイルを含むArtifact一式を構成
- デプロイ
eb deploy
などのコマンドでデプロイDockerrun.aws.jsonサンプル
{ "AWSEBDockerrunVersion": 1, "Ports": [ { "ContainerPort": 8000 } ] }deploy.zipサンプル
deploy.zip |- Dockerfile |- Dockerrun.aws.json |- index.htmlDockerfileと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がリリース
されました。
手順
参考: ECSのオペレーションを劇的に簡略化するAWS Copilotが発表されました! | Developers.IO
詳細な手順は例によって上記記事が大変詳しいです。いつもありがとうございます。
必要なステップは下記の通りです。
- 事前準備
- copilotのインストール
- 環境の構築
- Applicationの作成
- Environmentの作成
- Serviceの作成
- Manifestの定義
- デプロイ
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-dddddddddServiceの作成
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: 70range指定で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
合計: \$66ECS
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が使いやすくなっていくと思われるので今後も期待していきたいです。