20200212のdockerに関する記事は15件です。

docker exec & pecoが思いの外捗る

dockerを使いはじめて2年くらいなのですが、今までコンテナに入るまでこんなことをしていました。

y-nishi@yutaroon: laravel-docker-template [master] » docker ps                                                                                 [23:44:39]
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                               NAMES
0fd143ed3db0        laravel-docker-template_nginx   "nginx -g 'daemon of…"   10 seconds ago      Up 10 seconds       0.0.0.0:3333->80/tcp                nginx
286b79e909a4        laravel-docker-template_php     "docker-php-entrypoi…"   11 seconds ago      Up 10 seconds       9000/tcp                            php
cef83b6bb200        laravel-docker-template_mysql   "docker-entrypoint.s…"   11 seconds ago      Up 10 seconds       0.0.0.0:3306->3306/tcp, 33060/tcp   mysql
y-nishi@yutaroon: laravel-docker-template [master] » docker exec -it 286b79e909a4 sh                                                           [23:44:52]
# echo 'mendokuseeee'
mendokuseeee
#

コンテナIDを調べたりコマンド打ったりとてもめんどうです。

そこでpecoを導入してみました。
こんな感じになります。
Image from Gyazo

僕の場合、dexec というエイリアスを登録して、これを叩くことでコンテナ選択に遷移するようにしています、

コンテナIDを意識せずに、コンテナ名を直感的に指定できるのでとても良いです。
コンテナがたくさん立ち上がっている環境だと一層操作しやすくなるのではないでしょうか。

やり方

僕はfishを使っているので、 ~/.config/fish/config.fish に下記を追記します。
bashの人は ~/.bashrc によしなに登録してください。

alias dps='docker ps --format "{{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Command}}\t{{.RunningFor}}"'
alias dexec='docker exec -it (dps | peco | cut -f 1) sh'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dockerのPHPでxdebugを使ってみる

概要

xdebugをdocker環境で使えるようにしたかったので調べてみた。

Dockerを準備する

作ったコードは、下記githubにまとめているので大雑把に書きます。
※ Github: https://github.com/reflet/php5.6-xdebug

docker/httpd/Dockerfile
FROM php:5.6-apache

# install xdebug
RUN pecl install xdebug-2.5.5 && docker-php-ext-enable xdebug

# update php.ini
COPY ./php.ini /usr/local/etc/php/php.ini

WORKDIR /var/www/html

php.iniの設定

下記の設定を追加する。

docker/httpd/php.ini
[xdebug]
xdebug.idekey="PHPStorm"
xdebug.remote_host = "docker.for.mac.host.internal"
xdebug.default_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_connect_back = 0
xdebug.remote_enable = 1
xdebug.remote_handler = "dbgp"
xdebug.remote_port = 9000

docker-compose.yml

dockerの構成を記述します。

docker-compose.yml
version: "3.7"
services:
   httpd:
     container_name: httpd
     image: httpd:development
     build:
       context: ./docker/httpd
       dockerfile: Dockerfile
     ports:
       - "80:80"
     volumes:
       - ./src:/var/www

サーバ起動

下記コマンドでサーバを起動します。

ターミナル
$ docker-compose build
$ docker-compose up -d

IDE設定 (IntelliJ)

お使いのIDEで9000番ポートを指定する。

  • Preferences > Languages & Frameworks > PHP > Debug
    debug.png

  • Preferences > Languages & Frameworks > PHP > Servers
    server.png

  • edit-configurationsボタン
    edit-configurations.png

  • configurations設定
    configurations.png

  • 実行してみる
    run.png

以上

参考サイト

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

docker-composeで開発/本番環境を切り替える

docker-composeで簡単に環境を切り替えたい

webアプリの開発でサーバーやDBを同時に立ち上げるためにdocker-composeを使用していて
テストや動作確認もコンテナ上で起動してやる場合、webアプリを動かすコンテナとして

  • 「production環境」 最小限のパッケージとモジュールを入れて余分なキャッシュ等も消し、できるだけイメージサイズを小さくしたもの
  • 「development環境」 テスト関連やgit、分析ツール等の開発に必要なものが入ったやや大きいサイズのイメージのもの

というふうに2種類以上の環境を使い分けたくなりますが、これをどう切り替えたらいいかという話

色々やり方は考えられますがdoker-compose内で展開できる環境ファイル.envファイルを使うのがよさそうです。

この記事ではRailsアプリでnginxとMySQLを使用しdocker-composeはversion:3以上を使うのを想定してますが、他のケースでも応用できると思います。

.envをdocker-composeに展開する

.envファイルをdocker-compose.ymlと同じ階層に用意するとdocker-compose内で変数として展開できます。
これを利用するとbuildするDockerfileのファイル名も変数で切り替えることができます。

docker.compose.yml

version: '3.7'

services:
  nginx:
    build: containers/nginx
    volumes: 
      - public:/myapp/public
      - sockets:/myapp/tmp/sockets/
    ports:
      - '8080:80'
    depends_on:
      - rails

  rails:
    build:
      context: .
      dockerfile: Dockerfile_${DOCKER_RAILS_ENV}
    image: webapp_${DOCKER_RAILS_ENV}
    volumes:
      - .:/myapp
      - public:/myapp/public
      - sockets:/myapp/tmp/sockets/
    ports:
      - "3000:3000"
    links:
      - db
    environment: 
      RAILS_ENV: $DOCKER_RAILS_ENV
      DB_HOST: db
      DB_USERNAME: $DB_USERNAME
      DB_PASSWORD: $DB_PASSWORD
      RAILS_SERVE_STATIC_FILES: 

  db:
    image: mysql:5.7
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: $DB_PASSWORD
      MYSQL_DATABASE: root
    ports:
      - "3306:3306"

volumes:
  mysql-data:
  public:
  sockets:

.env

DOCKER_RAILS_ENV=production
DB_USERNAME=root
DB_PASSWORD=password

docker-compose.ymlのdockerfile: "Dockerfile_${DOCKER_RAILS_ENV}"の部分で
production環境用に作ったDocker_productionというファイルとDocker_developmentというファイルを.envで定義した環境変数で指定し切り替えています。1
またbuild:の下のimage:webapp_${DOCKER_RAILS_ENV}でビルドされるイメージ名を指定しています。2
ちなみに${DOCKER_RAILS_ENV:-development}とすればデファルト値でdevelopmentになります
イメージ名にも同じことができるので必要であればimage: mysql:${DB_VER-5.7}としてバージョンの指定なんかもできます。
参考:案外知られてないdocker-composeの環境変数定義の記法

注意点

ホストマシンのLinux等に.envで指定した変数と同名の環境変数が設定されてる場合、ホストマシンの環境変数が優先されるので、意図的に使いたい場合を除いて名前がバッティングしないようにする必要があります。
今回の例だとRAILS_ENVは使われやすいので、DOCKER_RAILS_ENVとしてかぶらないようにしてます。

またデフォルトで.gitignoreや.dockerignoreに.envは入ってないので間違ってもAWSへの接続情報などのクレデンシャルを.envに記述してgithubやdockerhub等にアップロードしないように注意しましょう。

Dockerfileは複数必要か

結局この方法だとdocker-composeは1つで済みますがDockerfileの方は2種類以上必要になります。
これも一つにできないかと考えたのですが、複雑になるのでやめました。3
アプリのルートをどうしてもDockerfile一つにしたい場合は、片方のDockerfileを配下に別のディレクトリを作って入れてdocker-composeのcontext:の部分を切り替えるようにすればいいと思います。


  1. MySQLやRailアプリに指定するDBパスワード等も一緒に記述してます。 

  2. image: app_name:tag_nameのようにタグも指定できるのでそっちに環境名つけてもいいです 

  3. やろうと思えばdocker-composeのbuild:arg:オプションを使ったりenv_file:を指定して、パッケージのリストを切り替えるみたいなこともできなくもないんですが、if文等が使えないのもあって無駄に見づらく複雑になるだけでした。 

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

docker-composeで.envで開発/本番環境を切り替える

docker-composeで簡単に環境を切り替えたい

webアプリの開発でサーバーやDBを同時に立ち上げるためにdocker-composeを使用していて
テストや動作確認もコンテナ上で起動してやる場合、webアプリを動かすコンテナとして

  • 「production環境」 alpineをベースに最小限のパッケージとモジュールを入れて余分なキャッシュ等も消し、できるだけイメージサイズを小さくしたもの
  • 「development環境」 テスト関連やgit、分析ツール等の開発に必要なものが入ったやや大きいサイズのイメージのもの

のように2種類以上の環境を使い分けたくなりますが、これをどう切り替えたらいいかという話

単純にdocker-compose.ymlを複数作って -f でファイル名を指定する方法や
公式ドキュメントにあるように拡張用のcomposeファイルを作る方法もありますが、doker-compose内で展開できる環境ファイル.envファイルを使用して切り替えるのが色々と応用がきくのでお薦めです。

作りたい環境に合わせたDockerfileを用意する

まず環境に合わせたDockerfileを用意します。
例えば

  • 「Docker_production」
  • 「Docker_development」

という感じに分けます。

.envを用意しdocker-composeに変数を指定する

.envファイルをdocker-compose.ymlと同じ階層に用意するとdocker-compose内で変数のように展開できます。
これを利用するとbuildするDockerfileのファイル名も変数で切り替えることができます。
以下はRailsを想定してますが、他のケースでも応用できると思います。

docker.compose.yml

version: '3.7'
~ 省略 ~

  rails:
    build:
      context: .
      dockerfile: Dockerfile_${DOCKER_RAILS_ENV}
    image: webapp_${DOCKER_RAILS_ENV}
    volumes:
      - .:/myapp
      - public:/myapp/public
      - sockets:/myapp/tmp/sockets/
    ports:
      - "3000:3000"
    links:
      - db
    environment: 
      RAILS_ENV: $DOCKER_RAILS_ENV
      DB_HOST: db
      DB_USERNAME: $DB_USERNAME
      DB_PASSWORD: $DB_PASSWORD
      RAILS_SERVE_STATIC_FILES: 

~ 省略~

.env

DOCKER_RAILS_ENV=production
DB_USERNAME=root
DB_PASSWORD=password

buildするDockerfile名に.envに書かれた環境変数を利用する

docker-compose.ymlのdockerfile: "Dockerfile_${DOCKER_RAILS_ENV}"の部分で
production環境用に作ったDocker_productionというファイルとDocker_developmentというファイルを.envで定義した環境変数で指定し切り替えています。1
.envをDOCKER_RAILS_ENV=developmentとすればdevelopment環境でupができます。

作成されるイメージ名を指定する

デフォルトでは"ディレクトリの名前"_"指定したコンテナ名(rails)"になるので環境を変えても同じイメージ名がつきます。
build:の下にimage:を指定するとイメージ名を指定してビルドできます。
->https://docs.docker.com/compose/compose-file/#build
image:webapp_${DOCKER_RAILS_ENV}でビルドされるイメージ名も環境で変わるようにしています。2

ちなみに${DOCKER_RAILS_ENV:-development}とすれば変数が指定されていない場合のデファルト値がdevelopmentになります
必要であれば使用するDBのイメージ名でimage: mysql:${DB_VER-5.7}として公式イメージのバージョンの指定にも応用できます。
参考:案外知られてないdocker-composeの環境変数定義の記法

注意点

ホストマシンのLinux等に.envで指定した変数と同名の環境変数が設定されてる場合、ホストマシンの環境変数が優先されるので、意図的に使いたい場合を除いて名前がバッティングしないようにする必要があります。
今回の例だとRAILS_ENVは使われやすいので、DOCKER_RAILS_ENVとしてかぶらないようにしてます。

またデフォルトで.gitignoreや.dockerignoreに.envは入ってないので間違ってもAWSのキーなどのクレデンシャルを.envに記述してgithubやdockerhub等にアップロードしないように注意しましょう。

Dockerfileは複数必要か

結局この方法だとdocker-composeは1つで済みますがDockerfileの方は2種類以上必要になります。
これも一つにできないかと考えたのですが、複雑になるのでやめました。3

アプリのルートをどうしてもDockerfile一つにしたい場合は、片方のDockerfileを配下に別のディレクトリを作って入れてdocker-composeのcontext:の部分を切り替えるようにすればいいと思います。


  1. MySQLやRailアプリに指定するDBパスワード等も一緒に記述してます。 

  2. image: app_name:tag_nameの形式でタグも指定できるのでそちらに環境の名前をつけてもいいです。 

  3. やろうと思えばdocker-composeのbuild:arg:オプションを使ったりenv_file:を指定して、パッケージのリストを切り替えるみたいなことはできます。環境によってDockerfileが殆ど変わらない場合はそれでいいのですが、自分の場合はかなり違うこともあって無駄に見づらく複雑になるだけでした。 

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

SEしてるけど実はあんまりコード書いたことないんだよねって人に捧ぐ、Rails on Dockerハンズオン vol.7 - Secure password -

はじめに

第7回目となる今回は、モデルにセキュアなパスワードをもたらしてくれるhas_secure_passwordメソッドの使い方を紹介していきます。
パスワードは他人に知られてはまずいものです。万が一、データを抜かれたり画面に表示されちゃったりしても一目でわからないようになっていることが望ましいですね。そんなことを実現してくれるメソッドがhas_secure_passwordです。

has_secure_password

Railsでは、モデルにセキュアなパスワード属性を実装するメソッドとして、has_secure_passwordが用意されています。モデルに対してhas_secure_passwordを適用することでモデルは以下の恩恵を受けることができます。

  • 仮想属性としてpasswordpassword_confirmationを利用可能
  • passwordpassword_confirmationの同一性について勝手に検証してくれる
  • DBにはハッシュ化したpassword_digestを保存するようになる
  • ハッシュ値でパスワード検証をするauthenticateメソッドを利用可能

ハッシュ化についてちょっとお話ししておきます。ハッシュ化はある文字列を不可逆な別の文字列に変換してくれます。『不可逆』とは元に戻せないという意味です。
『暗号化』の場合は『復号化』することで元の文字列に変換しなおせるんです。これは『可逆』といいますね。

では、早速モデルにhas_secure_passwordメソッドを適用していきます。

has_secure_passwordではハッシュ化を行うためにbcrypt gemを利用します。まずは、bcryptのインストールからやっていきます。Gemfileの中身をみるとわかりますが、bcryptは最初からGemfileの中に書かれておりコメントアウトされているだけです。なのでコメントアウトをとってdocker-compose buildをするだけでOKです。

Gemfile
...
gem 'bcrypt', '~> 3.1.7'
...
$ docker-compose build
$ docker-compose up -d

コンテナも立ち上げておきましょう。

次に、has_secure_passwordメソッドを使うようになるとpasswordをハッシュ化した値をpassword_digestカラムにDB保存するようになります。マイグレーションファイルを作成しDBにpassword_digestカラムを追加しておきます。

# rails g migration add_password_digest_column_to_user password_digest:string
Running via Spring preloader in process 251
      invoke  active_record
      create    db/migrate/20200130075100_add_password_digest_column_to_user.rb

# rails db:migrate
== 20200130075100 AddPasswordDigestColumnToUser: migrating ====================
-- add_column(:users, :password_digest, :string)
   -> 0.0522s
== 20200130075100 AddPasswordDigestColumnToUser: migrated (0.0533s) ===========

そして、Userモデルにhas_secure_passwordメソッドを適用します。

app/models/user.rb
class User < ApplicationRecord
  ...
  has_secure_password
  ...
end

これで完了です!簡単ですね!
では恩恵がちゃんと受けられているか確認してみましょう。

passwordpassword_confirmation

まずはpassword属性とpassword_confirmation属性の二つの仮想属性が利用できることを確認してみます。

> user = User.new(name: "hanako", email: "hanako@sample.com", password: "password", password_confirmation: "password")
=> #<User:0x0000563d32829928
 id: nil,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: nil,
 updated_at: nil,
 password_digest: [FILTERED]>

> user.password
=> "password"

> user.password_confirmation
=> "password"

> user.password_digest
=> "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"

DBカラム的にpassword_digestしかないはずですが、passwordpassword_confirmationが使えていることがわかります。さらにその結果がpassword_digestとして利用可能なのもわかりますね。

passwordpassword_confirmationの一致性確認

ここで一度valid?メソッドを使ってみましょう。これは今現在のモデルでvalidationを突破できるのかを確認できるメソッドです。先ほどまではsaveでvalidationできるかを確認していましたが、実はvalidationを調べるためだけであればこれでOK。

> user.valid?
  User Exists? (3.6ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> true

今はpasswordpassword_confirmationがマッチしているのでvalidationは通っているようです。では、password_confirmationを別の文字列に変更した場合どうでしょうか?

> user.password = "password1"
=> "password1"

> user.valid?
  User Exists? (35.3ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false

> user.errors.full_messages
=> ["Password confirmationとPasswordの入力が一致しません"]

validationでエラーになっていることがわかりましたね...あ、日本語化していない。
日本語化しましょう!

config/locales/ja.yml
ja:
  activerecord:
    attributes:
      user:
        name: "お名前"
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
...
> reload!
Reloading...
=> true

> user.valid?
  User Exists? (4.7ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false

> user.errors.full_messages
=> ["確認用パスワードとパスワードの入力が一致しません"]

passwordpassword_confirmationが一致していない場合はエラーが起きていますね。
ただ、password_confirmationのようなものは必要なのか?という論争もあると思います。
登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK
例えばこちらの記事では、確認用パスワードを用意するのではなく、パスワードの欄のマスクを外せるようにした方がコンバージョンが上がると述べられていたりします。
has_secure_passwordでは、password_confirmationnilの場合、passwordpassword_confirmationの一致を確認しません。

> user.password_confirmation = nil
=> nil

> user.valid?
  User Exists? (2.4ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> true

なので確認用パスワードを使う使わないによらず、has_secure_passwordメソッドは強力なのです。

DBではハッシュ化されたpassword_digestが使われる

最初にもお話しした通り、ハッシュ化は不可逆なデータ変換です。
has_secure_passwordではpassword_digestにハッシュ化されたpasswordを保存します。それを確認してみましょう!

実際にUserを作成してみましょう。

> user.save
   (0.4ms)  BEGIN
  User Exists? (2.3ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
  User Create (69.4ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "hanako"], ["email", "hanako@sample.com"], ["created_at", "2020-01-31 20:35:57.284982"], ["updated_at", "2020-01-31 20:35:57.284982"], ["password_digest", "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"]]
   (5.6ms)  COMMIT
=> true

> user.find(1)
=> #<User:0x0000563d32823898
 id: 1,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: Fri, 31 Jan 2020 20:35:57 JST +09:00,
 updated_at: Fri, 31 Jan 2020 20:35:57 JST +09:00,
 password_digest: [FILTERED]>

> user.password
=> nil

> user.password_digest
=> "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"

user.password_digestをみてもなんのことやらわかりませんね。ちょっと安心です。

パスワード認証するauthenticateメソッド

さて、ハッシュ化されてセキュアになったのはいいのですが、不可逆なのでこのままではパスワードで認証ができません。has_secure_passwordではハッシュ化されたパスワードの認証をするためのauthenticateメソッドが用意されています。これは、password_digestの値と入力したpasswordを同じハッシュ関数でハッシュ化した値の一致をチェックしてくれるものです。

> User.find_by(email: "hanako@sample.com").authenticate("a")
  User Load (4.5ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false

> User.find_by(email: "hanako@sample.com").authenticate("password")
  User Load (2.5ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> #<User:0x0000563d32d80688
 id: 1,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: Fri, 31 Jan 2020 20:35:57 JST +09:00,
 updated_at: Fri, 31 Jan 2020 20:35:57 JST +09:00,
 password_digest: [FILTERED]>

このようにfind_byと組み合わせて使えばメールアドレスとパスワードの2key認証を実装できます。authenticateは不一致の場合はfalseを、一致の場合はモデルオブジェクトを返却します。

passwordにvalidationを設ける

仮想属性であるpasswordに対してもvalidationをつけることができます。
一方で、has_secure_passwordで作られたpasswordにはデフォルトでpresenceのvalidationが設定されています。これに加えて6文字以上でないといけない文字数制限をつけてみましょう。
デフォルトでpresenceがついてはいるのですが、入力がない場合にpresencelengthの両方に引っかかってしまうのでpresenceについては適用しない方がわかりやすいでしょう。
has_secure_passwordvalidations: falseオプションをつけてデフォルトのpresence validationを向こうにした後に、他の属性と同様にlengthのvalidationをつけてみましょう。

app/models/user.rb
class User < ApplicationRecord
  ...
  has_secure_password validations: false
  ...
  validates :password,
    length: { minimum: 6 }
  ...
end

passwordのvalidationについて試してみましょう。

> user = User.new(name: "john", email: "john@sample.com")
=> #<User:0x0000563d32287768
 id: nil,
 name: "john",
 email: "john@sample.com",
 created_at: nil,
 updated_at: nil,
 password_digest: nil>

> user.valid?
  User Exists? (4.9ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> false

> user.errors.full_messages
=> ["パスワードは6文字以上で入力してください"]

> user.password = "a" * 5
=> "aaaaa"

> user.valid?
  User Exists? (4.8ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> false

> user.errors.full_messages
=> ["パスワードは6文字以上で入力してください"]

> user.password = "a" * 6
=> "aaaaaa"

> user.valid?
  User Exists? (3.5ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> true

ということで、passwordlength validationが正しく挙動していることがわかりました。

Userを作成

最後にこのバリデーションの中でちゃんとUserを作成できることを確認しておきましょう!

> User.create(name: "John Smith", email: "john@sample.com", password: "password")  
(1.9ms)  BEGIN
  User Exists? (7.4ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
  User Create (30.2ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "John Smith"], ["email", "john@sample.com"], ["created_at", "2020-02-09 16:40:36.252829"], ["updated_at", "2020-02-09 16:40:36.252829"], ["password_digest", "$2a$12$JHC/AEBGXyffzL9..2ThOuxDRlRCSP2RxtFxZxDzeOfsQIX6BGqym"]]
   (3.0ms)  COMMIT
=> #<User:0x000055f6f7e390b0
 id: 2,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Sun, 09 Feb 2020 16:40:36 JST +09:00,
 updated_at: Sun, 09 Feb 2020 16:40:36 JST +09:00,
 password_digest: [FILTERED]>

validationにひっかからないUserであればちゃんと作成できることが確認できましたね!

ユーザー情報を確認するViewを作ってみる

ここまででUserモデルがほぼ完成しました。この情報をUIで見れるようにViewを作ってみましょう!

Scaffoldを思い出してください。ユーザーの詳細情報を見るためのページは/users/:idのURLにアクセスして閲覧することができ、showアクションにルーティングされていました。

今回もそれにそってユーザーのページを作っていきます。

ユーザー詳細ページを作成する

ユーザー詳細ページは、/users/:idのパスにアクセスした時に、そのidのユーザーの詳細情報が表示されるページにしたいと思います。
この通りになるように、ファイルの編集や作成を行っていきます。いままでrails g ~コマンドでファイルを作成してきましたが、Railsの規則に則っていれば普通にファイルを作成しても動きます。

Routing

まずは、/users/:idのルーティングを生成します。以前、Scaffoldの時にresourcesメソッドを使ってUsersコントローラーに対するルーティングを定義しました。/users/:idはその時のshowアクションへのルーティングと同じです。
resourcesメソッドはonlyオプションをつけることで特定のアクションへのルーティングのみを定義してくれます。今回はこのオプションを使ってルーティングを定義します。

config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'

  resources :users, only: [:show]
end

これでルーティングの設定は完了です。試しにrails routesコマンドでルーティングを確認してみてもいいかもしれません。

# rails routes
Prefix  Verb  URI Pattern  Controller#Action
root    GET   /            static_pages#home
user    GET   /users/:id   users#show
...

確かにGET /users/:idusers#showにルーティングされています。

Controller

次にUsersコントローラーを作成していきます。コントローラーは複数形の名称にするのがルールです。

# touch app/controllers/users_controller.rb
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end

usersコントローラにshowアクションがある、というところまでは今までの内容からわかりますね。
showアクションの中身をみてみます。

params[:id]/users/:idのパスパラメータの:idを取得しています。例えば/users/1にアクセスした場合はparams[:id]=1だし、users/2にアクセスした場合はparams[:id]=2です。
User.find()はModel CRUDの回で話した通り、idでUserのモデルオブジェクトを取得するメソッドなので、パスのidのユーザーがこれで取得できるわけです。
あとはViewに引き渡すためにこれをインスタンス変数@userに代入しています。

ではこれを受け取るViewを作っていきます。

View

まず、Viewファイルを格納するディレクトリを作成して、その中にshow.html.erbファイルを作ります。
UsersコントローラのためのViewなのでapp/views/users/ディレクトリ内にファイルを作成していきます。

# mkdir app/views/users
# touch app/views/users/show.html.erb

最初は情報が表示されていればOKとしましょう。特にUIは拘らない。

app/views/users/show.html.erb
<div class="container my-5">
  <%= @user.name %>
  <br>
  <%= @user.email %>
</div>

nameemailを表示。

http://localhost:3000/users/2にアクセスしてみましょう。
image.png
こんなページが表示されていれば成功です!
idは実際にDBに格納されているデータ次第ですので、Rails consoleでUser.allを打つなどして存在するUserのidを確認してみてくださいね。)

後片付け

次回に向けてデータを消します。

$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset

DBコンテナが立ち上がった状態だと思うのでdownさせます。

$ docker-compose down

まとめ

今回は、has_secure_passwordメソッドを使ってセキュアなパスワードが利用できるようになりました。
コード的にいえばたった1行has_secure_passwordを付け加えるだけでパスワードをハッシュ化してセキュアに扱うことができるようになる。これって強力なメソッドですよね。

さらにユーザーの情報を表示するページまで作ることができました。
次回はSign up(ユーザー登録)ページを作っていきましょう!

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

Links

Vol.1 - Introduction -
Vol.2 - Hello, Rails on Docker -
Vol.3 - Scaffold, RESTful, MVC -
Vol.4 - Static pages -
Vol.5 - Model and CRUD -
Vol.6 - Model validation -
・ Vol.7 - Secure password - ?この記事

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

docker imagesがSSDの容量を圧迫したのでHDDを増設してそっちに保存したメモ

最初に

  • HDDマウントとdockerの保存場所変更を行っています。
  • 保存先やUUIDは、各自で変更してください。

OSとdocker のバージョン確認

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.6 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.6 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
$ docker --version
Docker version 18.09.7, build 2d0083d

HDDを自動マウントしたい

/etc/fstab を編集するとよいです。

マウント先作成

$ sudo mkdir /mnt/hdd4000 

名前は任意。4TBのHDDなのでhdd4000

自動マウントするためのfstabを編集

  • ディスク名のUUID取得
$ sudo blkid
...
/dev/sdc: LABEL="hdd4000" UUID="6e1266f3-3da9-4684-a396-xxxxxxxxxxxx" TYPE="ext4"
...
  • /etc/fstab を編集
# hdd4TB 
UUID=6e1266f3-3da9-4684-a396-xxxxxxxxxxxx /mnt/hdd4000    ext4    defaults          1       2

を追加しました。オプションの説明は以下を参考。

参考: https://qiita.com/kihoair/items/03635447591358210772

  • fstab 間違えるとOS起動しなくなるので注意です。(UUID 1文字間違えてハマった;;)

dockerのイメージ保存場所変更

引越前にimageを全消し(移行は考えてません)

  • docker コンテナを全削除する
docker ps -aq | xargs docker rm
  • docker イメージを全削除する
docker images -aq | xargs docker rmi

参考:docker images を全削除する https://qiita.com/fist0/items/2fb1c7f894b5bdff79f4

docker imageの場所を変更

/etc/docker/daemon.json を編集(新規作成)

{
    "data-root": "/mnt/hdd4000/docker/data"
}

参考:https://qiita.com/elm200/items/43497f76cbdd19c47d8b

docker 再起動

$ sudo service docker stop
$ sudo service docker start

変更場所にファイルができたので完了です。

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

Linux 上で Objective-C 2.0 の開発環境を整える

トレンドが Swift に移って久しく,近年は Objective-C に関心を持つ人が減っていますが,Linux 上で Objective-C の開発環境を整える方法についてまとめてみました。

Docker 上の Ubuntu 18.04 および 19.10 で動作確認しています。Dockerfile の ENVRUN の内容を取り出せば,実機でも動くでしょう。

Objective-C 1.0 でよいなら……

昔ながらの Objective-C 1.0 でよいなら,apt で入手できる出来合いのパッケージをインストールすれば,比較的容易に Clang 9 による Objective-C の開発環境を整えられます。

Dockerfile
FROM ubuntu:19.10

RUN set -x \
  && apt update \
  && apt upgrade -y \
  && DEBIAN_FRONTEND=noninteractive apt install -y tzdata \
  && apt install -y clang-9 make gobjc-9 gnustep-devel \
  && apt autoremove -y \
  && apt clean \
  && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

# Set alias
RUN echo 'alias clang-objc="clang-9 \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -lgnustep-base -I/usr/lib/gcc/x86_64-linux-gnu/9/include"' > ~/.bashrc

CMD ["/bin/bash"]

本質部は

$ apt install -y clang-9 make gobjc-9 gnustep-devel

です。また,~/.bashrc

alias clang-objc="clang-9 \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -lgnustep-base -I/usr/lib/gcc/x86_64-linux-gnu/9/include"

と定義してあります。これにより,この Docker コンテナ内で

$ clang-objc -o hoge hoge.m
$ ./hoge

などとすることで簡単に Objective-C のソース hoge.m をコンパイル・実行できます。

Objective-C 2.0 のフル機能を使いたいなら……

  • Automatic Reference Counting (ARC)
  • Blocks
  • Dot Notation
  • Object Literal
  • Object Subscripting
  • Fast Enumeration
  • Lightweight Generics
  • Grand Central Dispatch (GCD)

といった Modern Objective-C の機能をフルに使いたい場合は,GNUstep の libobjc2 をセットアップせねばなりません。これは apt でインストールできないので,ソースからビルドする必要があります。これがなかなか大変です。plaurent 氏の GitHub レポジトリが大変参考になりました。ビルド手順を Dockerfile にまとめると次のようになります。

Dockerfile
FROM ubuntu:19.10

ENV CC clang-9
ENV CXX clang++-9
ENV CXXFLAGS -std=c++11
ENV RUNTIME_VERSION gnustep-2.0
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV LD /usr/bin/ld.gold
ENV LDFLAGS "-fuse-ld=/usr/bin/ld.gold -L/usr/local/lib"

WORKDIR /GNUstep-build

RUN set -x \
  && apt update \
  && apt upgrade -y \
  && apt install -y git sudo clang-9 clang++-9 build-essential wget \
    subversion cmake libffi-dev libxml2-dev \
    libgnutls28-dev libicu-dev libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev autoconf libtool \
    libjpeg-dev libtiff-dev libffi-dev libcairo-dev libx11-dev libxt-dev libxft-dev \
  && apt autoremove -y \
  && apt clean \
  && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

# Build GNUstep make
RUN set -x \
    #
    # Checkout tools-make
    && git clone https://github.com/gnustep/tools-make.git \
    #
    # Build GNUstep make
    && cd tools-make \
    && CC=${CC} ./configure \
            --with-layout=gnustep \
            --disable-importing-config-file \
            --enable-native-objc-exceptions \
            --enable-objc-arc \
            --enable-install-ld-so-conf \
            --with-library-combo=ng-gnu-gnu \
    && make -j8 \
    && sudo -E make install \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    && echo ". /usr/GNUstep/System/Library/Makefiles/GNUstep.sh" >> ~/.bashrc \
    && echo "export RUNTIME_VERSION=gnustep-2.0" >> ~/.bashrc \
    && echo 'export CXXFLAGS="-std=c++11"' >> ~/.bashrc \
    && cd .. \
#
# Build libdispatch
    #
    # Checkout swift-corelibs-libdispatch
    && git clone https://github.com/apple/swift-corelibs-libdispatch \
    && cd swift-corelibs-libdispatch \
    && git checkout swift-5.1.1-RELEASE \
    #
    # Build libdispatch
    && rm -Rf build \
    && mkdir build \
    && cd build \
    && cmake .. \
         -DCMAKE_C_COMPILER=${CC} \
         -DCMAKE_CXX_COMPILER=${CXX} \
         -DCMAKE_BUILD_TYPE=Release \
         -DUSE_GOLD_LINKER=YES \
    && make \
    && sudo -E make install \
    && sudo ldconfig \
    && cd ../.. \
#
# Build libobjc2
    #
    # Checkout libobjc2
    && git clone https://github.com/gnustep/libobjc2.git \
    && cd libobjc2 \
    && git submodule init \
    && git submodule sync \
    && git submodule update \
    #
    # Build libobjc2
    && rm -Rf build \
    && mkdir build \
    && cd build \
    && cmake .. \
         -DCMAKE_C_COMPILER=${CC} \
         -DCMAKE_CXX_COMPILER=${CXX} \
         -DCMAKE_ASM_COMPILER=${CC} \
         -DTESTS=OFF \
    && cmake --build . \
    && sudo -E make install \
    && sudo ldconfig \
    && cd ../.. \
#
# Build GNUstep make second time
    && cd tools-make \
    && CC=${CC} ./configure \
            --with-layout=gnustep \
            --disable-importing-config-file \
            --enable-native-objc-exceptions \
            --enable-objc-arc \
            --enable-install-ld-so-conf \
            --with-library-combo=ng-gnu-gnu \
    && make -j8 \
    && sudo -E make install \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    && cd .. \
#
# Build GNUstep base
    #
    # Checkout libs-base
    && git clone https://github.com/gnustep/libs-base.git \
    #
    # Build GNUstep base
    && cd libs-base \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd .. 

# For GUI programming
#
# Build GNUstep GUI Library
RUN set -x \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    #
    # Checkout libs-gui
    && git clone https://github.com/gnustep/libs-gui.git \
    #
    # Build GNUstep GUI Library
    && cd libs-gui \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd .. \
#
# Build GNUstep GUI Backend
    #
    # Checkout libs-back
    && git clone https://github.com/gnustep/libs-back.git \
    #
    # Build GNUstep GUI Backend
    && cd libs-back \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd ..

WORKDIR /workdir

# Clean build directory
RUN rm -rf /GNUstep-build

# Set alias
RUN echo 'alias clang-objc="\${CC} \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -fobjc-arc -lobjc -ldispatch -lgnustep-base"' >> ~/.bashrc

CMD ["/bin/bash"]

長いビルド手順でしたね……。最後に ~/.bashrc

alias clang-objc="\${CC} \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -fobjc-arc -lobjc -ldispatch -lgnustep-base"

と設定してありますので,この Docker コンテナ内で

$ clang-objc -o hoge hoge.m
$ ./hoge

などとすることで簡単に Objective-C 2.0 のソース hoge.m をコンパイル・実行できます。

Dockerfile およびテストファイル一式

今回作成した Dockerfile およびテスト用 Objective-C ソースの一式は GitHub レポジトリ にアップしておきました。

$ docker run --rm -it -v $(pwd):/workdir doratex/clang9-objc2:latest /bin/bash
# cd test
# ./test_all.sh

とすることで,様々な Objective-C 2.0 の機能をテストできます。(ただしテストのうち最後の GUI のテストは X Window System のサーバとつながっていないと動きません。)

Docker イメージ

今回作成した Docker イメージは Docker Hub のレポジトリ にアップしてあります。

$ docker pull doratex/clang9-objc2:ubuntu1910

などとすれば使えます。

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

Dockerでproxyサーバ

はじめに

Dockerでsquidによるproxyサーバを構築。
特別なことはしてません。

対象機器および環境

検証環境

  • CentOS8(8.1.1911)
  • Docker(19.03.5)
  • squid(4.4)

作業内容

firewallでポート許可

8080/tcpで待ち受けるようにサービスを許可しておきます

firewall-cmd --add-port=8080/tcp --zone=public --permanent
firewall-cmd --reload

Dockerイメージの準備

再利用できるようにイメージをつくっておきます。
Dockerfileと設定ファイルを置くための適当なディレクトリを作成しておきます

mkdir -p /opt/docker/proxy
cd /opt/docker/proxy

Dockerfileを作成

/opt/docker/proxy/proxy.df
FROM centos:centos8
ENV TZ='Asia/Tokyo'
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime ; \
    dnf -y update ; dnf install -y squid ; \
    sed -i -e "s/http_port 3128/http_port 8080/" /etc/squid/squid.conf ;  \
    systemctl enable squid ; \
    dnf -y install rsyslog ;
COPY rsyslog.conf /etc
CMD [ "/usr/sbin/init" ]

syslog転送用の設定

ローカルのログファイルの変更を検知するモジュール imfile を利用して、リモートへsyslogを転送します。
転送先は別のエントリで作成した syslogサーバにUDP:514で指定しています。
ネットワークサービス用のDockerネットワーク infraserv-network に所属させているため、 syslog というホスト名で転送ができるようになっています。

/opt/docker/proxy/rsyslog.conf
module(load="imfile")
input(type="imfile"
      file="/var/log/squid/access.log"
      tag="pseudolog_squid_access_log"
      facility="local0"
      severity="notice")
:syslogtag, isequal, "pseudolog_squid_access_log" @syslog:514

Dockerfileからイメージをビルドしてコンテナ作成

Dockerfileが作成できたら、ビルドします。

docker build --force-rm -t infraserv:proxy . -f ./proxy.df && \
docker run --cap-add sys_admin --security-opt seccomp:unconfined  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  --network infraserv-network -it -d --name proxy --hostname proxy  -p 8080:8080 infraserv:proxy

もし、dnfでコケたら、以下のコマンドが効くかもしれない。

# firewall-cmd --add-masquerade --permanent
# firewall-cmd --reload

動作確認

診断くん、などでチェック。
http://taruo.net/e/

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

Dockerでtftpサーバ

はじめに

Dockerでtftpサーバを構築。
特別なことはしてません。

対象機器および環境

検証環境

  • CentOS8(8.1.1911)
  • Docker(19.03.5)
  • tftp-server(5.2)
  • xinetd(2.3.15)

作業内容

firewallでポート許可

tftpサービスを待ち受けるようにサービスを許可しておきます

firewall-cmd --add-service=tftp --zone=public --permanent
firewall-cmd --reload

Dockerイメージの準備

再利用できるようにイメージをつくっておきます。
Dockerfileと設定ファイルを置くための適当なディレクトリを作成しておきます

mkdir -p /opt/docker/tftp
cd /opt/docker/tftp

Dockerfileを作成

/opt/docker/tftp/tftp.df
FROM centos:centos8
RUN dnf -y update ; dnf -y install tftp-server xinetd
COPY tftp /etc/xinetd.d/tftp
CMD [ "/usr/sbin/init" ]

xinetdの設定

tftpをxinetd経由で起動するために、設定ファイルを作成しておきます

/opt/docker/tftp/tftp
service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = /usr/sbin/in.tftpd
        server_args             = -c -u root -s /var/lib/tftpboot
        disable                 = no
        per_source              = 11
        cps                     = 100 2
        flags                   = IPv4
}

Dockerfileからイメージをビルドしてコンテナ作成

Dockerfileが作成できたら、ビルドします。
tftpは最初に69/udpで接続しますが、その後ポート番号をネゴってデータ転送するので、bridgeだとポート転送しにくい。
ので、hostネットワークに接続してしまいます。

docker build --force-rm -t infraserv:tftp . -f ./tftp.df && \
docker run --cap-add sys_admin --security-opt seccomp:unconfined  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  --network host -it -d --name tftp --hostname tftp   infraserv:tftp

動作確認

手元にあったCiscoのルータで確認

Rdc01#copy run tftp://10.254.10.251/rdc01-config
Address or name of remote host [10.254.10.251]?
Destination filename [rdc01-config]?
!!
11944 bytes copied in 1.112 secs (10741 bytes/sec)

Rdc01#copy tftp://10.254.10.251/rdc01-config flash:
Destination filename [rdc01-config]?
Accessing tftp://10.254.10.251/rdc01-config...
Loading rdc01-config from 10.254.10.251 (via Vlan10): !
[OK - 11944 bytes]

11944 bytes copied in 0.652 secs (18319 bytes/sec)

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

Dockerのrsyslogでコンテナ間や他サーバから転送されるログを集約した

はじめに

dockerでいろいろコンテナを立てたら、ログを集約したくなったので、rsyslogでつくりました。

対象機器および環境

検証環境

  • CentOS8(8.1.1911)
  • Docker(19.03.5)
  • rsyslog

ログの保管方法

リモートから飛んできたログたちは、syslogフォーマットの %hostname% で個別ファイルに振り分けます。
/var/log/remotelog/ 配下に ホスト名ごとにログファイルとして保管します。

/var/log/remotelog/
|-- dhcp.log
|-- esxi01.prosper2.net.log
|-- FWdc01.prosper2.net.log
|-- proxy.log
|-- radius.log
`-- ....

送出側のsyslogフォーマットの設定によって、FQDNだったり、ホスト名だけだったり、ちょっとバラバラ。
ちゃんとすればいいんだけれど、やりかたがまだ調べられてません。。。

別のエントリで作成した infraserv-network に所属させた他のDockerコンテナから飛んできたログは、ちゃんと %hostname% フィールドにマッチしているみたいで、ホスト名で分けてくれてます。

作業内容

firewallでポート許可

radiusサービスを許可しておきます

# firewall-cmd --add-service=syslog --zone=public --permanent
# firewall-cmd --reload

Dockerイメージの準備

再利用できるようにイメージをつくっておきます。
Dockerfileと設定ファイルを置くための適当なディレクトリを作成しておきます

mkdir -p /opt/docker/syslog
cd /opt/docker/syslog

Dockerfileを作成

/opt/docker/syslog/syslog.df
FROM centos:centos8
ENV TZ='Asia/Tokyo'
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime ; \
    dnf -y update ; dnf install -y rsyslog cronie logrotate ; \
    mkdir /var/log/remotelog
COPY rsyslog.conf /etc/rsyslog.conf
COPY remotelog /etc/logrotate.d
CMD [ "/usr/sbin/init" ]

リモートからのログを受け付ける

bridge経由になっているため、コンテナはホスト(172.20.0.1/32)からのみ受け付けとしています

/opt/docker/syslog/rsyslog.conf
$template logfile_hostname,      "/var/log/remotelog/%hostname%.log"
$template logformat_verbose,     "%timegenerated%,%timereported%,%hostname%,%syslogtag%,%syslogseverity%,%syslogfacility%,%msg%\n"

$ModLoad imudp
$UDPServerRun 514
:fromhost-ip, ereregex, "172.20.0.1" -?logfile_hostname;logformat_verbose
& stop

logrotateの設定

ログは圧縮して40世代分保管します。

/opt/docker/syslog/remotelog
/var/log/remotelog/*.log {
        daily
        rotate 40
        compress
        missingok
        notifempty
        postrotate
          /bin/systemctl restart rsyslog
        endscript
}

Dockerfileからイメージをビルドしてコンテナ作成

Dockerfileが作成できたら、ビルドします。
--privileges はなんか評判悪そう(?)なので、推奨されてるぽい方法でコンテナ生成します。

docker build --force-rm -t infraserv:syslog . -f ./syslog.df && \
docker run --cap-add sys_admin --security-opt seccomp:unconfined  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  --network infraserv-network -it -d --name syslog --hostname syslog  -p 514:514/udp infraserv:syslog
docker exec -it syslog /bin/bash

もし、dnfでコケたら、以下のコマンドが効くかもしれない。

# firewall-cmd --add-masquerade --permanent
# firewall-cmd --reload

接続テスト

コンテナ起動後、しばらく置いてからログディレクトリを確認します。

$ sudo docker exec -it syslog /bin/bash
[syslog]# ls -1sh /var/log/remotelog/
total 18M
 12K dhcp.log
704K esxi01.prosper2.net.log
 16M FWdc01.prosper2.net.log
4.0K _gateway.log
576K proxy.log
4.0K radius.log
256K VMinfraserv05.log

ファイルの中身

# tail -n 1 *
==> dhcp.log <==
Feb 11 21:37:20,Feb 11 21:37:20,dhcp,pseudolog_kea_dhcp4_log,5,16, 2020-02-11 21:37:15.253 INFO  [kea-dhcp4.dhcpsrv/26] DHCPSRV_MEMFILE_LFC_EXECUTE executing Lease File Cleanup using: /usr/sbin/kea-lfc -4 -x /var/lib/kea/dhcp4.leases.2 -i /var/lib/kea/dhcp4.leases.1 -o /var/lib/kea/dhcp4.leases.output -f /var/lib/kea/dhcp4.leases.completed -p /var/lib/kea/dhcp4.leases.pid -c ignored-path

==> esxi01.prosper2.net.log <==
Feb 11 21:56:05,Feb 11 12:55:58,esxi01.prosper2.net,snmpd:,6,3, send_env_notifications: sent 0 of 0 SEL entries as notifications, 0 already sent

==> FWdc01.prosper2.net.log <==
Feb 11 21:56:27,Feb 11 21:56:27,FWdc01.prosper2.net,1,2020/02/11,6,1, 21:56:27,001606007877,TRAFFIC,end,2049,2020/02/11 21:56:27,10.254.11.65,10.254.10.241,0.0.0.0,0.0.0.0,any_to_DNS,,,dns,vsys1,zTrust,zTrust,ethernet1/2.11,ethernet1/2.252,LFP_default,2020/02/11 21:56:27,3327,1,54264,53,0,0,0x19,udp,allow,290,78,212,2,2020/02/11 21:55:55,0,any,0,6764778,0x0,10.0.0.0-10.255.255.255,10.0.0.0-10.255.255.255,0,1,1,aged-out,0,0,0,0,,FWdc01,from-policy,,,0,,0,,N/A,0,0,0,0

==> _gateway.log <==
Feb 11 21:06:05,Feb 11 21:06:05,_gateway,VMwlc01:,3,16, *Dot1x_NW_MsgTask_1: Feb 11 21:06:05.056: %DOT1X-3-INVALID_REPLAY_CTR: 1x_eapkey.c:449 Invalid replay counter from client ac:63:be:92:9d:f9 - got 00 00 00 00 00 00 00 1d, expected 00 00 00 00 00 00 00 1e

==> proxy.log <==
Feb 11 21:55:57,Feb 11 21:55:57,proxy,pseudolog_squid_access_log,5,16, 1581425757.416 284915 172.20.0.1 TCP_TUNNEL/200 2563 CONNECT www.google-analytics.com:443 - HIER_DIRECT/216.58.197.174 -

==> radius.log <==
Feb 11 20:52:12,Feb 11 20:52:12,radius,pseudolog_radius_log,5,16, Tue Feb 11 20:52:12 2020 : Auth: (3) Login OK: [admin] (from client radius_clients port 34)

==> VMinfraserv05.log <==
Feb 11 21:55:11,Feb 11 21:55:11,VMinfraserv05,pseudolog_cacti_log,5,16, 2020/02/11 21:55:08 - SNMPAGENT WARNING: No notification receivers configured for event: cactiNotifyDeviceFailedPoll (CACTI-MIB), severity: medium

取得できてますね。

さいごに

syslog転送時に hostname フィールドを追加しない機器はホスト名が _gateway になってしまう。Cisco機器とか。
CiscoIOSで logging origin-id hostname とやっても、メッセージそのものにホスト名が記載されるだけみたいなので、ちょっと使いにくい。
↓こんな感じになってしまう。

Feb 11 20:22:16,Feb 11 20:22:16,_gateway,844:,5,23, Rhq01: Feb 11 20:22:16: %IKEV2-5-SA_UP: SA UP

出典

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/s1-basic_configuration_of_rsyslog
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/s1-using_rsyslog_modules

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

dockerコマンドのメモ

DockerHubからimageの取得する

docker pull ubuntu

Imageを起動する

docker run ubuntu

bashを使用する

docker run -i -t ubuntu bash

-i はコンテナの標準入力を有効化、 -t はttyを有効化するためのオプション

DockerHubでimageを検索する

docker search <IMAGE NAME>

tagを使用する

docker run python:2.7 python --version

DockerfileからDockerImageを作成する

docker build -t hello .

-t は名前をつける
. はdocker build 実行時のコンテキストの指定

ローカルに存在するDocker Image の一覧を確認する

docker images

Docker Hubへログインする

docker login

Docker Image を命名する

docker tag <IMAGE NAME> <USER NAME>/<IMAGE NAME>:<TAG>

Docker Hub へ Docker Image をアップロードする

docker push <USER NAME>/<IMAGE NAME>:<TAG>

実行中のコンテナを確認する

docker container ls -a

-a 全てのコンテナを表示

停止したコンテナをすべて削除する

docker container prune

1つ以上のコンテナーを削除する

docker container rm <CONTAINER> [CONTAINER...]

Dockerネットワークを一覧で表示する

docker network ls

新しいBridgeネットワークを作成する

docker network create <NAME>

停止したコンテナを実行する

docker start -a <CONTAINER>

-a アタッチする

実行中コンテナを停止する

docker stop <CONTAINER>

コンテナの中にbashで入る

docker exec -it <CONTAINER> bash

イメージのビルド履歴を表示する

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

Docker Swarmで学ぶサービスメッシュ

はじめに

本記事はDocker Swarmでクラスタを構築し、サービスメッシュについて学びます。

環境はAWSのELB(Elastic Load Balancing)と、EC2(Amazon EC2)インスタンス2台を使用し、Docker Swarmのクラスタを構築します。

Qiita_swarm.png

Kubernetesと比較した際にDocker Swarmを導入するメリットは、導入コストが低いことです。Docker SwarmはKubernetesに比べてハードルが低いため、本番環境にコンテナ技術を導入しようとしているシステムには最適だと考えます。

Docker Swarmを通してサービスメッシュの意義を体感しましょう。

Docker Swarm

Docker Swarmは、Docker社が提供するオーケストレーションツールです。
Docker Swarmを使用することで複数のホストを集約し、簡単にコンテナのデプロイとスケールが実現できます。

logo.png

Docker Swarmはマネージャとノードで構成され、Swarmは群れを意味します。
Docker Swarのクラスタを構築するためには、以下の作業が必要です。

  • マネージャとノードにDockerをインストールする。
  • マネージャとノード間で通信ができるように、ファイアウォールで必要な通信を許可する。

  • ファイアウォールで許可するルール

ポート番号 用途
2377/tcp クラスタ管理用の通信
4789/udp オーバーレイ・ネットワーク
7946/tcp ノード間通信
7946/udp ノード間通信

サービスメッシュ

サービスメッシュはマイクロサービスアーキテクチャを前提とし、アプリケーション間で通信を制御する仕組みを提供します。この制御された通信はメッシュの様に編みがけになっているため、信頼性を向上します。

例として2台構成のホストの場合、通常のアクセスパスは以下になります。

ブラウザでロードバランサーのDNS名にアクセスすると、HTTPリクエストを受け付けたロードバランサーは、ラウンドロビンでバックエンドであるホストの公開ポートにHTTPリクエストを振り分けます。

swarm2-1.png

例えば、片方のホストで何らかの障害が発生し、コンテナがダウンした場合でも以下の経路により、サービスを継続することができます。

swarm2-2.png

要約すると、単純にホストレベルでアプリケーションを分ける構成の場合は、片方のホストに何らかの障害が発生したときにサービスの提供ができなくなります。よって、コンテナ技術を活用し、サービスメッシュにすることで単一障害点をなくすことができます。

Docker Swarm構築

Docker Swarmの構築手順について記載します。
Docker Swarmの構築は、マネージャ、ノードの順に作業を行います。

本記事の例ではweb1がマネージャ、web2がノードになり、Nginxのイメージを使用してデプロイします。

前提条件としてDockerは既にインストールされた状態で、上記で解説したファイアウォールも許可されていることとします。また、本記事では最低限の構成としているため、マネージャ1台、ノード1台になります。

マネージャ

まずはじめに、Docker Swarmの初期化を行います。docker swarm initコマンドを実行することでSwarmモードが有効になります。

複数IPアドレスを持つ場合は、他のノードとの通信で使用するインターフェースのIPアドレスをadvertise-addrの引数に指定します。

  • Docker Swarmの初期化
    # docker swarm init --advertise-addr <IPアドレス>
Swarm initialized: current node (zirc78nsch77ox8di6021ux4n) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-459p0pkyqgxgkhaggjhavd419ldujenqttm1mqmwup0qz9m5qv-1kj3jy6ozwrr2fkj1qvas294a <マネージャのIPアドレス>:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

コマンド実行後、出力結果のdocker swarm join --token SWMTKN-1-(略) <マネージャのIPアドレス>:2377を控えます。

  • node確認
    # docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
zirc78nsch77ox8di6021ux4n *   web1          Ready               Active              Leader              18.09.9-ce

マネージャの場合は、MANAGER STATUSにLeaderと出力されます。

  • ネットワーク確認
    # docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
53703efd3d07        bridge              bridge              local
aacf6f5e0eb4        docker_gwbridge     bridge              local
1f0d0e4ae3e7        host                host                local
xip5tlqmokpb        ingress             overlay             swarm
2d36f1c8c80f        none                null                local

Swarmの初期化を行うと新たに2つのネットワーク(docker_gwbridge、ingress)が作成されます。

ノード

次にノードをDocker Swarmのクラスタに参加させるため、ノード側で以下のコマンドを実行します。

--tokenの引数にしている値は例になります。上記docker swarm initコマンドの出力結果をコピーしてペーストすれば大丈夫です。

なお、マネージャ側でdocker swarm join-token workerコマンドを実行することで、トークンの再表示もできます。

  • クラスタ参加
    # docker swarm join --token SWMTKN-1-(略) <マネージャのIPアドレス>:2377
This node joined a swarm as a worker.

マネージャ側で確認のため、再度、docker node lsコマンドを実行すると、nodeが認識されていることが確認できます。

  • node確認
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
zirc78nsch77ox8di6021ux4n *   web1          Ready               Active              Leader              18.09.9-ce
n2o22ptdmyhan8qg0ijmo0qn5     web2          Ready               Active                                  18.09.9-ce

Docker Swarmのデプロイ

本記事では管理しやすいdocker-commposeでデプロイします。
また、docker service createコマンドでもデプロイできます。serviceやstackの説明については割愛します。

デプロイ作業はマネージャ側で行います。
任意のディレクトリに移動し、以下のdocker-commpose.ymlファイルを作成します。

  • docker-commpose.yml
version: "3"
services:
  web:
    image: nginx
    deploy:
      replicas: 2
      #resources:
      #  limits:
      #    cpus: "0.1"
      #    memory: 100M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:

docker-commpose.ymlファイル作成後、以下のコマンドを実行し、デプロイします。
testは例となるため、任意の名前を指定します。

# docker stack deploy -c docker-commpose.yml test

Updating service test_web (id: egubeieuri00rzmm9imsna93o)

デプロイ後、以下のコマンドでサービスの状態が確認できます。
REPLICASは2となっているので、2つのコンテナが起動しています。

# docker service ls

ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
r04mfg1se3nh        test_web            replicated          2/2                 nginx:latest        *:80->80/tcp

マネージャとノード側でdocker container ps -aコマンドを実行すると、コンテナが起動しているのが確認できます。

  • マネージャ側
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
4a26c3ca6df7        nginx:latest        "nginx -g 'daemon of…"   3 seconds ago       Up 2 seconds        80/tcp              test_web.1.mnnz40tdykzd2intwz5hf68bs
  • ノード側
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
614c19349bf0        nginx:latest        "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes        80/tcp              test_web.2.6om5oazbavassohd4akucbum2

Docker Swarmの動作確認

Docker Swarmの動作確認を行います。

ブラウザからロードバランサーのDNS名にアクセスを行い、正常に負荷分散されることを確認します。

  • ロードバランサーのDNS名にアクセス

スクリーンショット 2020-02-05 23.37.20.png

コンテナのログを確認すると、ラウンドロビンで負荷分散されているのが確認できます。
CONTAINER IDdocker container ps -aのコマンドで確認します。

# docker logs -f <CONTAINER ID>

10.255.0.3 - - [06/Feb/2020:14:20:51 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" "<アクセス元のグローバルIP>"

試しにweb2上(ノード側)で稼働しているコンテナを停止します。

# docker stop <CONTAINER ID>

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
423f673b0513        nginx:latest        "nginx -g 'daemon of…"   19 hours ago        Exited (0) 5 seconds ago                       test_web.2.kc7yypyasgvjtolsb1zwmhjoy

このときweb2上でコンテナは稼働していません。
ロードバランサーのDNS名にアクセスできることを確認します。

alb-acces.png

例としてブラウザから停止したweb2の公開IPにアクセスします。

web2-acces.png

Web1(マネージャ)上のコンテナのアクセスログに、停止したweb2(ノード)の公開IPに対するアクセスが確認できます。

10.255.0.3 - - [07/Feb/2020:02:19:01 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" "-"

停止したweb2(ノード側)のローカルからも、以下のコマンドを実行することでコンテナにアクセスができます。

# curl localhost 80

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
curl: (7) Couldn't connect to server

web1(マネージャ)のログでは以下の様に出力されます。

10.255.0.2 - - [07/Feb/2020:02:41:47 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-"

web1(マネージャ)で4789ポートをダンプして見ていると、オーバーレイ・ネットワークの通信を見ることができます。

# tcpdump -nli eth0 port 4789 -Av

Docker Swarmのスケール

既にサービスが起動している状態で、以下のコマンドを実行することでスケールができます。以下のコマンドはコンテナの数を4コンテナに変更しています。

# docker service scale test_web=4

test_web scaled to 4
overall progress: 4 out of 4 tasks 
1/4: running   [==================================================>] 
2/4: running   [==================================================>] 
3/4: running   [==================================================>] 
4/4: running   [==================================================>] 
verify: Service converged 

スケール後、docker container ps -aコマンドを実行すると、コンテナが増えていのが分かります。

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
41962946aa48        nginx:latest        "nginx -g 'daemon of…"   3 minutes ago       Up 3 minutes        80/tcp              test_web.4.pytl36ejcd81ybthisxfo47br
423f673b0513        nginx:latest        "nginx -g 'daemon of…"   7 hours ago         Up 7 hours          80/tcp              test_web.2.kc7yypyasgvjtolsb1zwmhjoy

デプロイ時はサーバ1台ずつに対してアプリケーション資産の入れ替えを行う必要がなく、マネージャ1台で済みます。

Docker Swarmの性質

Docker Swarmは冪等性を持っています。

具体的には、Docker Swarmは指定したreplicas数のコンテナを維持するため、クラスタ化している片方のホストがダウンした場合は、片方のホストでコンテナを起動します。

以下はdocker container ps -aコマンドの出力になります。
例としてweb1とweb2でそれぞれコンテナが起動しています。

  • web1
e5ccbfa9739b        nginx:latest                      "nginx -g 'daemon of…"   3 minutes ago       Up 3 minutes        80/tcp              test_web.1.mrgkhbd7juer72v6bv0l42fxq
  • web2
4820c7bbe9c1        nginx:latest                      "nginx -g 'daemon of…"   3 minutes ago       Up 3 minutes        80/tcp              test_web.2.wfe1n11s8940rdl8r1c47o6nc

web2のホストを停止しました。web2のダウンを検知すると、web1上でコンテナを作成します。

0a88f53039a3        nginx:latest                      "nginx -g 'daemon of…"   5 seconds ago       Created                                 test_web.2.p06zas3c3kt9ekjojhhfnl3co
e5ccbfa9739b        nginx:latest                      "nginx -g 'daemon of…"   4 minutes ago       Up 4 minutes        80/tcp              test_web.1.mrgkhbd7juer72v6bv0l42fxq

web1上でコンテナが2台起動しています。

0a88f53039a3        nginx:latest                      "nginx -g 'daemon of…"   37 seconds ago      Up 31 seconds       80/tcp              test_web.2.p06zas3c3kt9ekjojhhfnl3co
e5ccbfa9739b        nginx:latest                      "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        80/tcp              test_web.1.mrgkhbd7juer72v6bv0l42fxq

Docker Swarm解除

Docker Swarmの解除方法について記載します。

先にマネージャ側で以下のコマンドを実行し、サービスの削除を行いす。

# docker service rm test_web

test_web

次にノード、マネージャの順に作業を行います。

ノード

  • ノードの切り離し
    # docker swarm leave
Node left the swarm.

マネージャ

ノードのSTATUSがDOWNになったことを確認します。

  • node確認
    # docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
zirc78nsch77ox8di6021ux4n *   web1          Ready               Active              Leader              18.09.9-ce
n2o22ptdmyhan8qg0ijmo0qn5     web2          Down                Active                                  18.09.9-ce

ノードを削除します。オプションの引数には、ノード名を指定します。

  • node削除
    # docker node rm --force web2
web2

マネージャからノードが認識されなくなったことを確認します。

  • node確認
    # docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
zirc78nsch77ox8di6021ux4n *   web1          Ready               Active              Leader              18.09.9-ce

最後にマネージャ自身を切り離します。

  • マネージャの切り離し
    # docker swarm leave --force
Node left the swarm.

Docker Swarm解体

Docker Swarmを使用しない場合は、マネージャ側で以下の作業を行います。

ノードが存在しないことを確認します。

  • node確認
    # docker node ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

Docker Swarmで作成されたネットワークが削除されたことを確認します。docker_gwbridgeのネットワークは残っています。

# docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
987cfc73d87c        bridge              bridge              local
aacf6f5e0eb4        docker_gwbridge     bridge              local
1f0d0e4ae3e7        host                host                local
2d36f1c8c80f        none                null                local

以下のコマンドを実行し、使用していない全てのリソース削除します。

  • リソース削除
    # docker system prune
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all dangling images
        - all dangling build cache
Are you sure you want to continue? [y/N] y
Deleted Networks:
docker_gwbridge

Deleted Images:
untagged: nginx@sha256:ad5552c786f128e389a0263104ae39f3d3c7895579d45ae716f528185b36bc6f
deleted: sha256:2073e0bcb60ee98548d313ead5eacbfe16d9054f8800a32bedd859922a99a6e1
deleted: sha256:a3136fbf38691346715cac8360bcdfca0fff812cede416469653670f04e2cab0
deleted: sha256:99360ffcb2da18fd9ede194efaf5d4b90e7aee99f45737e918113e6833dcf278
deleted: sha256:488dfecc21b1bc607e09368d2791cb784cf8c4ec5c05d2952b045b3e0f8cc01e
untagged: nginx@sha256:70821e443be75ea38bdf52a974fd2271babd5875b2b1964f05025981c75a6717
deleted: sha256:5ad3bd0e67a9c542210a21a3c72f56ef6387cf9b7f4c2506d2398d55a2593ed0
deleted: sha256:b69e2ed46519bc33e7c887967e4f61a2ee53aef165b70f75e208937fb42e7b4c
deleted: sha256:4cb7f732537bf0f65cd9f8f7b63bbe71abcf9d0df396f58621ef3be0b2487b27
deleted: sha256:556c5fb0d91b726083a8ce42e2faaed99f11bc68d3f70e2c7bbce87e7e0b3e10

Total reclaimed space: 253.4MB

ナレッジ

docker-commposeのバージョン

Docker Swarmを使用する場合に、docker-commposeで使用できるバージョンは3になります。

docker-commposeでビルドする場合の留意事項

docker-commposeでビルド(stack)する場合、イメージが必要になります。

Compose file version 3 referenceより

注:( バージョン3)Composeファイルを使用してSwarmモードでスタックをデプロイする場合、このオプションは無視され ます。このdocker stackコマンドは、ビルド済みのイメージのみを受け入れます。

そのため、Docker registryの環境が必要になります。

ローカルでDockerレジストリをセットアップする方法については、公式の以下URLが参考になります。

例として、以下の様にビルドを指定してデプロイを実行した場合、

version: '3'

services:
  app:
    build: ./src

以下のエラーメッセージが出力されて、デプロイをすることはできません。

failed to create service stackdemo_backend: Error response from daemon: rpc error: code = InvalidArgument desc = ContainerSpec: image reference must be provided

デプロイ時のエラー

例として2台構成のホストで、docker-commpose.ymlファイルのreplicasの値を2にしてデプロイした場合は、基本的に1ホストに対して1コンテナで分散されます。

もし、デプロイ時に片方のホストで2台のコンテナが起動した場合は、何らかのエラーが発生し、片方のホストでコンテナが起動できなかったことが考えられます。エラーはLinuxの場合、シスログから確認することができます。

おわりに

不確実性があり変化の速さが求めれられる現代のアプリケーション開発には、オーケストレーションツールは必須な技術です。

応用としてサーバレスの技術を使用し、CPUやメモリリソース等のしきい値等を設定して、しきい値を超えたらサーバをスケールアウトなどの運用も行うことができます。

参考

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

Docker Compose と Simpacker で、モダンな Rails 開発環境を構築する

想定読者

  • Rails のフロントエンド開発に webpack を利用している(利用しようとしている)
    • Rails/webpack 自体の設定については、本記事では必要最低限しか記載しません
    • ※ webpacker gem 経由で webpack を利用されている場合
      • →本記事では webpacker は利用しません。webpack 自体の設定を直接書くことになります。
  • Rails 実行用のコンテナと webpack 実行用のコンテナを分離したい&それぞれのコンテナサイズを小さくしたい
    • コンテナの再ビルド時間を減らしたい
    • Node.js に依存する処理が Rails コード内に存在しないことを簡単に検知したい
    • ただの趣味
    • etc.

環境

  • Ruby >= 2.5.1 (for Rails 6)
    • with Bundler
  • Node.js >= 12.x
    • with Yarn
  • Docker
    • with Docker Compose

※ 一部手順を簡略化するため、Ruby および Node.js がローカルにもインストールされていることを前提としています
docker run ruby -v .:/app -w /app などを使えばローカルインストールせずとも構築できるとは思いますが未確認です

手順

必要なパッケージのインストール

Rails new の実行

cd path/to/workspace
gem install rails
rails new <my-new-rails-project> --database=postgresql --skip-sprockets --skip-javascript --skip-turbolinks --skip-bundle --skip-webpack-install
  • <my-new-rails-project>, --database= あたりの指定は適宜読み替えてください
  • css(sass) の管理も webpack に任せる前提となっています。css(sass) の管理を Rails で行いたい場合は --skip-sprockets オプションは指定しないでください

Simpacker gem のインストール

cd path/to/workspace/<my-new-rails-project>
echo "gem 'simpacker'" >> Gemfile
bundle install
bundle exec rails simpacker:install

webpack-dev-server のインストール

cd path/to/workspace/<my-new-rails-project>
yarn add -D webpack-dev-server

Dockerfile の作成

for Rails

# <my-new-rails-project>/build/Dockerfile.rails
FROM ruby:2.7.0 as rails-dev

RUN apt-get install -y locales \
        && echo 'ja_JP.UTF-8 UTF-8' >> /etc/locale.gen \
        && locale-gen \
        && update-locale LANG=ja_JP.UTF-8
ENV LANG=ja_JP.UTF-8

RUN mkdir /app
WORKDIR /app

ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install

CMD ["bundle" "exec" "rails" "server"]

for webpack

# <my-new-rails-project>/build/Dockerfile.rails
FROM node:12 as webpack

RUN mkdir /app
WORKDIR /app

ADD package.json /app/package.json
ADD yarn.lock /app/yarn.lock
RUN yarn install

CMD ["yarn" "run" "webpack-dev-server" "--config" "webpack.config.js"]

docker-compose.yml

# <my-new-rails-project>/docker-compose.yml
version: '3.7'

services:
  rails:
    build:
      context: .
      dockerfile: build/Dockerfile.rails
      target: rails-dev
    tty: true
    stdin_open: true
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - bundle:/usr/local/bundle
    depends_on:
      - db
      - webpack
  webpack:
    build:
      context: .
      dockerfile: build/Dockerfile.webpack
      target: webpack
    ports:
      - "3035:3035"
    volumes:
      - .:/app
      - node_modules:/app/node_modules
  db:
    image: postgres:12
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - pg_data:/var/lib/postgresql/data
volumes:
  bundle: {}
  node_modules: {}
  pg_data: {}

Rails/webpack それぞれの設定を変更

Rails

config/puma.rb

docker コンテナ外からアクセスするために、listen address を 127.0.0.1 から 0.0.0.0 に変更します

 # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
 #
-port        ENV.fetch('PORT') { 3000 }
+port        ENV.fetch('PORT') { 3000 }, '0.0.0.0'

 # Specifies the `environment` that Puma will run in.
 #

config/database.yml

db コンテナに接続する例です(適宜読み替えてください)

   # For details on connection pooling, see Rails configuration guide
   # https://guides.rubyonrails.org/configuring.html#database-pooling
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+  username: postgres
+  password: postgres
+  host: db

 development:
   <<: *default

webpack.config.js

See also: https://github.com/hokaccha/simpacker/blob/master/example/webpack-dev-server/README.md

   output: {
     path: path.resolve(__dirname, "public/packs"),
-    publicPath: "/packs/",
+    publicPath: isProd ? "/packs/" : "//localhost:3035/packs/",
     filename: isProd ? "[name]-[hash].js" : "[name].js"
   },

   ...

   },
-  plugins: [new WebpackAssetsManifest({ publicPath: true })]
+  plugins: [new WebpackAssetsManifest({ publicPath: true, writeToDisk: true })]
+  devServer: {
+    host: "0.0.0.0",
+    port: 3035,
+    hot: true,
+    headers: {
+      "Access-Control-Allow-Origin": "*"
+    }
+  }
 }

動作確認

Rails controller/view の作成

config/routes.rb

Rails.application.routes.draw do
  root 'home#index'
end

app/controllers/home_controller.rb

class HomeController < ApplicationController
  def index; end
end

app/views/layouts/application.html.slim

doctype html
html[lang="ja"]
  head
    meta[charset="UTF-8"]
    title MyNewRailsProject
    = csrf_meta_tags
    = csp_meta_tag
    = javascript_packs_tag 'application'

  body
    = yield

app/views/home/index.html.slim

h1 home#index

Rails server の起動

docker-compose build
docker-compose up -d
open http://localhost:3000 # for macOS

参考

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

Golang x Beego x Docker x CircleCI x npmで開発環境をサクッと作ってみよう

DockerとCircleCIなどを組み込みつつGolangのBeego開発環境を構築します。
本稿では以下を前提とします。

前提となる知識
・terminalのコマンド操作
・viまたはエディタの操作
・Docker,docker-composeの知識
・gitやgithubの基本的な操作

前提となる条件
・OSはmacOSを前提とします。どうしてもWindowsなどの場合はVagrantでLinuxの仮想環境を立てるなどして自力で対応してみてください。
・バージョン管理のツールはGithubを用います。

この記事で書いていること
・Beegoの0からの環境構築
・Dockerとdocker-composeでコンテナ環境構築
・CircleCIの0からの設定
・フロントエンド環境の0からの構築

この記事に書いてないこと
・Go言語特有の実装方法やtipsなど

Beego環境のセットアップ

Goのインストールの確認

以下のコマンドでGoがローカルに入っているか確認しましょう。

go version

not foundと表示される場合は公式サイトからパッケージのダウンロードをするかbrewでインストールしましょう。
インストール後はGOPATHの設定が必要です。
手順としては大体以下ですが、環境によって異なることがあるので公式を確認しましょう。

  • GOPATHとなるディレクトリを作成しておく
mkdir ~/go/
  • ~/.bash_profileを開く
vim ~/.bash_profile
  • 以下を追記する
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
  • 保存後に反映
source ~/.bash_profile

Beegoのインストール

Goのインストールが完了してgoコマンドが使えるようになったことを確認できたらBeegoをインストールしましょう。
公式サイトを参考に必要なツールをインストールする。

go get -u github.com/astaxie/beego
go get -u github.com/beego/bee

正しく実行されると $GOPATH/bin の配下にバイナリファイルが格納されます。

プロジェクトを作成する

  • ソースコード格納用のディレクトリの作成(任意)
mkdir $GOPATH/src
  • プロジェクトを作成
$ cd $GOPATH/src
$ bee new beego-app
$ cd  beego-app
$ ls
conf        controllers main.go     models      routers     static      tests       views
  • プロジェクトを起動
bee run

「アプリケーション“beego-app”へのネットワーク受信接続を許可しますか?」とアラートが表示されると思うが、許可をクリックして http://localhost:8080/ にアクセスして以下のような画面が表示されることを確認する。

localhost_8080.png

ここまでで以下のような構成のプロジェクトが作成されています。
それぞれの役割を詳しく確認したい場合は公式ドキュメントを見てみましょう。

.
├── beego-app
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
│       └── reload.min.js
├── tests
│   └── default_test.go
└── views
    └── index.tpl

Dockerとdocker-composeでコンテナを立ち上げる

ホスト側でのBeego環境はできましたが、他開発者との共有を簡単に行うことができるようにDockerとdocker-composeを使ってコンテナ環境を立ち上げましょう。

docker-compose.ymlの作成

以下のファイルをプロジェクト直下に配置する

$ touch $GOPATH/src/beego-app/docker-compose.yml
docker-compose.yml
version: '3'
services:
  app:
    container_name: beego-app
    build:
      context: docker
      dockerfile: Dockerfile
    volumes:
      - .:/go/src/beego-app
    ports:
      - 10080:10080

portが他コンテナやサービスにバッティングしてしまう場合は、そのポートをlsofコマンドで確認してkillするかportsのところを任意に書き換えてください。
buildで記載されているcontext直下のDockerfileをビルドします。

docker-compose について詳しく知りたい場合
http://docs.docker.jp/compose/compose-file.html

Dockerfileの作成

dockerというディレクトリを作成してDockerfileを追加します。
Dockerfileについてはこちら

mkdir $GOPATH/src/beego-app/docker
touch $GOPATH/src/beego-app/docker/Dockerfile
# @see::https://hub.docker.com/_/amazonlinux
FROM amazonlinux:2

# システムアップデート
RUN yum update -y

# gitのインストール
RUN yum install -y git

# @see::https://fedoraproject.org/wiki/EPEL
RUN amazon-linux-extras install -y epel

# amazon-linux-extrasでインストールできる最新のgolang
RUN amazon-linux-extras list | grep golang
RUN amazon-linux-extras install -y golang1.11
RUN go version

# beegoのインストール
ENV GOPATH /go
ENV PATH $PATH:$GOPATH/bin
RUN go get -u github.com/beego/bee
RUN go get -u github.com/astaxie/beego

# カレントディレクトリをコンテナに追加する
COPY . /go/src/beego-app

# 作業ディレクトリを指定する
WORKDIR /go/src/beego-app

# コンテナ実行時にコンパイルと実行を行う
CMD bee run

イメージはAWS EC2での運用を想定してamazonlinux:2を用いている。
amazonlinuxベースのコンテナ上にgoとbeegoをインストールし、ホスト側をコンテナ側にマウントし最後にbeegoを実行しています。

コンテナを起動する

コンテナを立ち上げる前に上記のDockerfileのportをmain.goのbeego.Runの引数に渡します。

main.go
package main

import (
    _ "beego-app/routers"
    "github.com/astaxie/beego"
)

func main() {
    beego.Run(":10080")
}

Dockerfileをビルドしてコンテナを起動します。
docker-compose.ymlのディレクトリで以下のコマンドを実行します。

$ docker-compose up -d
Creating network "beego-app_default" with the default driver
Building app
Step 1/14 : FROM amazonlinux:2
2: Pulling from library/amazonlinux

正常に起動できているかpsコマンドで確認してみましょう。

$ docker-compose ps           
  Name           Command         State            Ports          
-----------------------------------------------------------------
beego-app   /bin/sh -c bee run   Up      0.0.0.0:10080->10080/tcp

コンテナの中に入って見てみましょう。

$ docker exec -it beego-app bash
bash-4.2# ls
beego-app  conf  controllers  docker  docker-compose.yml  main.go  models  routers  static  tests  touch  views
bash-4.2# pwd
/go/src/beego-app

もし、うまくいかなくてクリーンな状態からやり直したい場合、 docker-compose down --rmi all --volumes で該当のコンテナ、ネットワーク、ボリューム、イメージだけを削除するか、
もし他のコンテナやイメージもリセットしてしまって良い場合はdockerのアプリケーションを起動してpreferencesから「Reset disk image」で完全にリセットしてしまう方法もあります。

上記うまく行っていたらコンテナの実行ログを見てみましょう。

$ docker logs -f beego-app
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2020/02/09 10:21:09 INFO     ▶ 0001 Using 'beego-app' as 'appname'
2020/02/09 10:21:10 INFO     ▶ 0002 Initializing watcher...
beego-app/controllers
beego-app/routers
beego-app
2020/02/09 10:21:12 SUCCESS  ▶ 0003 Built Successfully!
2020/02/09 10:21:12 INFO     ▶ 0004 Restarting 'beego-app'...
2020/02/09 10:21:12 SUCCESS  ▶ 0005 './beego-app' is running...
2020/02/09 10:21:12.975 [I] [asm_amd64.s:1357]  http server Running on http://:10080

http server Running on http://:10080 というところに着目して、10080番ポートにリッスンされていることが確認できたので以下にアクセスして確認してみます。

http://localhost:10080/

ホスト側の時と同じ画面が表示されたら成功。
localhost_10080_.png

CircleCIでCI環境を構築する

最近ではgithub actionsなども出てきているが、とはいえCircleCIを採用している現場もまだまだ多いとおもうので、ここではCircleCIを用いてCI「Continuous Integration(継続的インテグレーション)」環境を構築する。

config.ymlの作成

プロジェクト直下に .circleci というディレクトリを作成し、.circleci直下にconfig.ymlを置く。

$ mkdir $GOPATH/src/beego-app/.circleci
$ touch $GOPATH/src/beego-app/.circleci/config.yml

config.ymlを以下のように作成する

config.yml
# @see::https://circleci.com/docs/ja/2.0/configuration-reference/
version: 2.1


# 実行処理は 1 つ以上の名前の付いたジョブで構成され、それらのジョブの指定は jobs マップで行います。
jobs:
  # CircleCI上のテスト
  test:
    docker:
      - image: circleci/golang:latest
    steps:
      - checkout
      - run: echo "this is jobs"


# Workflow は、ジョブの集まりとその実行順序の定義に関するルールを決めるものです。
workflows:
  build-test-deploy:
    jobs:
      # CircleCI上のテスト
      - test

細かい定義は公式を確認してください。
今回はとりあえず最低限の定義のみ記載します。

実運用ではCircleCI上でテストコードを実行したり、特定のブランチ(masterやstagingなど)のときにデプロイを行い自動化するようなCD「Continuous Delivery(継続的デリバリー)」を実現したりします。

上記ではworkflowsからtestというjobsが実行され、jobsのtestで指定されているdockerイメージ上でstepsに記載されているコマンドが実行されます。

CircleCIにリポジトリを登録する

まずは上記のソースコードを各自のgithub上にリポジトリを作成します。
git?github?な方はこちらや他の記事などもググって参考にしながら進めてみてください。
リポジトリが作成できたらローカルで作成したソースコードをプッシュしましょう。

github.com_kqxgy385_cuddly-octo-meme (1).png

リポジトリの準備ができたらCircleCIに連携しましょう。
CircleCIのアカウントがない場合は https://circleci.com/ からgithubアカウントでサインアップしましょう。

ログインできたらAdd Projectsを見つけて該当のリポジトリの「Set Up Projects」を実行しましょう。
onboarding.circleci.com_project-dashboard_github_kqxgy385.png

既に.circleci/config.ymlを作成しているのでそのままStart Buildingしましょう
onboarding.circleci.com_project-setup_github_kqxgy385_cuddly-octo-meme.png

確認されますがStart Buildingしましょう
onboarding.circleci.com_project-setup_github_kqxgy385_cuddly-octo-meme (1).png

STATUSがRUNNINGからSUCCESSになれば成功です
app.circleci.com_github_kqxgy385_cuddly-octo-meme_pipelines.png

中に入るとSTEPSの詳細が確認できます
app.circleci.com_github_kqxgy385_cuddly-octo-meme_pipelines (1).png

これでプッシュされるたびにconfig.ymlに記載されている内容がcircleci上で実行されます。

フロントエンド環境を構築する

npm(Node Package Manager)でフロントエンドのパッケージを一括管理します。
npmが使えるかどうかは以下のコマンドで確認します。

npm -v && node -v

npmがない場合はまずnode.jsをインストールします。
node.jsをインストールすると一緒についてきます。
node.jsは公式から直接ダウンロードするか、homebrewを使ってダウンロードする方法があります。

参考
https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09

初期化

プロジェクトのルートディレクトリで以下のコマンドを実行します。

npm init

すると以下のようにコマンドラインでいくつか質問されると思います。
特にこだわりがなければ全てenterでいいです。

package name: (beego-app) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: (https://github.com/kqxgy385/cuddly-octo-meme.git) 
keywords: 
author: 
license: (ISC) 
Is this OK? (yes) 

するとpackage.jsonというファイルが生成されると思います。
npmでインストールしたパッケージのバージョン情報がpackage.jsonに格納されます。

以下のような内容になっていると思います。

package.json
{
  "name": "beego-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "test": "tests"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/kqxgy385/cuddly-octo-meme.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/kqxgy385/cuddly-octo-meme/issues"
  },
  "homepage": "https://github.com/kqxgy385/cuddly-octo-meme#readme"
}

先ほどのコマンドラインで回答した内容が反映されるようになっています。
作り直したい場合は一度package.jsonを削除してもう一度npm initしてみましょう。

scriptsというところでコマンドを定義しておくことが可能で、デフォルトではtestというechoしてexitするだけのスクリプトが定義されています。
ためしに npm run test と実行してみましょう。
今後ここにjsやcssなどのbuildのコマンドを定義して利用します。

パッケージの復元

package.jsonが置いてあるプロジェクトルートで以下のコマンドを実行します。

npm install

するとpackage.jsonに記載されている依存関係がインストールされ、package-lock.jsonというファイルが生成されます。

パッケージのインストール

必要パッケージのインストール方法を確認します。
以下のコマンドを実行してみます。

npm install webpack

するとnode_modulesというディレクトリが生成され、またpackage.jsonに以下のように追記されていると思います。

package.json
  "dependencies": {
    "webpack": "^4.41.6"
  }

これでプロジェクト内でwebpackを利用することができます。
必要なパッケージはnpm installでインストールしていきます。
node_modulesは一般的にgit管理するものではないのでgitignoreに入れておきましょう。

webpackでフロントエンドをバンドルできるようにしよう

ここから先は細かい説明は省略します。
package.jsonのscriptsとdependenciesを以下にします。

package.json
  "scripts": {
    "dev": "webpack-dev-server --config webpack.config.js",
    "webpack": "webpack --config webpack.config.js"
  },
  "dependencies": {
    "css-loader": "^3.4.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.1.2",
    "vue": "^2.6.11",
    "vue-loader": "^15.8.3",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.1",
    "write-file-webpack-plugin": "^4.5.1"
  }

プロジェクト直下にwebpack.config.jsというファイルを作成し、内容を以下にしてみましょう。
webpackについては公式をお勧めします。

webpack.config.js
const path = require('path');
const projectRoot = path.resolve(__dirname);
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const WriteFileWebPackPlugin = require('write-file-webpack-plugin');

module.exports = {
    // エントリーポイントを指定する
    entry: './static/js/entry/index.js',
    // bundleファイルをwebpackがどこにどのような名前で出力すればいいのかを指定する
    output: {
        filename: '[name].js',
        path: path.join(projectRoot, 'static/js/dist')
    },
    // webpack-dev-serverのオプションを選択する
    devServer: {
        // 使用するホストを指定する
        host: 'localhost',
        // リクエストをリッスンするポートを指定する
        port: '8000',
        // サーバーに提供するコンテンツを指定する
        contentBase: path.join(__dirname, "static/js/dist"),
    },
    // @see::https://webpack.js.org/configuration/devtool/
    devtool: "cheap-module-eval-source-map",
    // ローダーの設定
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.scss$/,
                use: ["style-loader", "css-loader", "sass-loader"]
            },
            {
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
                        loader: 'file-loader'
                    }
                ]
            },
        ]
    },
    // プラグインの設定
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'static/index.html')
        }),
        new WriteFileWebPackPlugin(),
    ]
};

バンドルされたファイルが static/js/dist に生成されるように設定されています。

ここまでできたらもう一度npm installします。

$ npm install

ここまででプロジェクトの構成は以下のようになっているかと思います。

.
├── README.md
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── docker
│   └── Dockerfile
├── docker-compose.yml
├── main.go
├── models
├── package-lock.json
├── package.json
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
│       └── reload.min.js
├── tests
│   └── default_test.go
├── touch
└── views
    └── index.tpl

static直下にindex.htmlというファイルを生成し、内容を以下にしてみましょう。

<!DOCTYPE html>
<html lang="js">

<head>
  <title>beego-app</title>
</head>

<body>
  <div id="app" v-cloak></div>
</body>

</html>

jsというディレクトリの直下にentryというディレクトリとdistというディレクトリを作っておきます。
entryの直下にApp.vueというファイルを生成し、内容を以下にします。

<template>
  <div id="app" class="top">
    <header>
      <div class="title">
        <a href="/">BEEGO-APP</a>
      </div>
    </header>
  </div>
</template>

<script>
    export default {
        name: "app",
    };
</script>

<style lang="scss" scoped>
  .top {
    position: absolute;
    height: 1500px;
    width: 100%;
    background-color: #e8f0ff;
    top: 0;
    left: 0;

    header {
      display: flex;
      position: fixed;
      background-color: rgba(255, 255, 255, 0.9);
      height: 62px;
      width: 100%;
      z-index: 999;

      .title {
        display: block;
        position: relative;
        left: 10px;
        background-color: #d6e4ff;

        a {
          display: block;
          margin: 22px 0;
          color: #999999;
          cursor: pointer;
          text-decoration: none;

          &:hover {
            color: #da6b64;
          }
        }
      }
    }
  }
</style>

さらにentry直下にindex.jsというファイルを生成し、内容を以下にします。

index.js
import Vue from 'vue/dist/vue.esm.js'
import App from './App.vue'

new Vue({
    el: '#app',
    template: '<App/>',
    components: { App }
});

アプリケーションのデフォルトのアクセス先を変更するためrouters/router.goを以下に書き換えましょう。

router.go
package routers

import (
    "github.com/astaxie/beego"
)

func init() {
    beego.DelStaticPath("/static")
    beego.SetStaticPath("//", "static/js/dist")
}

ここまでできたらまずは以下のコマンドを実行してみましょう。

$ npm run webpack

先ほどpackage.jsonのscriptsで定義したwebpackを実行するコマンドのエイリアスです。
上記コマンドを実行して http://localhost:10080/ にアクセスして以下のような画面になっていれば成功です。
うまく行ってない場合はdocker-composeの再起動なども試してみましょう。

localhost_10080_.png

ここまでできたら次に以下のコマンドを試してみましょう。

$ npm run dev

先ほどpackage.jsonのscriptsで定義したwebpack-dev-serverを実行するコマンドのエイリアスです。

先ほどのwebpack.config.jsのdevServerのportを8000番にしたことにより http://localhost:8000 にアクセスできるようになります。
http://localhost:10080/ の時と同じ画面が見れていたら成功です。

今度はApp.vueの内容を適当に書き換えてみましょう。
npm run devを起動している最中はホットリロードが効いているはずなので、変更が即時で反映されていればwebpack.config.jsで設定した内容が反映されていることになります。

ここまでで構成は以下のようになります。

.
├── README.md
├── beego-app
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── docker
│   └── Dockerfile
├── docker-compose.yml
├── main.go
├── models
├── package-lock.json
├── package.json
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   ├── index.html
│   └── js
│       ├── dist
│       │   ├── index.html
│       │   └── main.js
│       ├── entry
│       │   ├── App.vue
│       │   └── index.js
│       └── reload.min.js
├── tests
│   └── default_test.go
├── touch
├── views
│   └── index.tpl
└── webpack.config.js

次は?

デバッガツールdelveの導入、テストコード実行方法、MySQLコンテナを立ち上げてマイグレーションする方法などを追記します。そのうち。

参考
Getting started
https://beego.me/quickstart
Beego を触ってみる (環境構築)
https://qiita.com/macococo/items/e5ace2550418ccced9ac
Setting GOPATH
https://github.com/golang/go/wiki/SettingGOPATH
Compose ファイル・リファレンス
http://docs.docker.jp/compose/compose-file.html
npm入門
https://qiita.com/maitake9116/items/7825d90c09f3e2f87dea
webpack documentation
https://webpack.js.org/concepts/

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

Linux VMでVSCode + Dockerの開発環境をつくる

VSCodeのRemote(Container) プラグインが便利だったので、VM上に開発環境を構築してみた。

Windows VM上には素直にDockerをインストールできない(ゲストOS上ではHyper Vオプションが無効)。

そこで、Linux VMでリモートデスクトップを使えるようにして、開発環境を構築してみた。

1. VM準備

  • Cent OS 7でVMを作成する。

(Cent OS 8で後続の手順で環境構築すると、セットアップ直後はRDPでつながるが、再起動後にVMにSSHもRDPも接続できなくなる問題が発生したのでやり直した)

2. Remote Desktopをセットアップ

  • VMにSSH接続して、Rootユーザーに切り替え。
  sudo su - 
$ useradd sampleuser  
$ passwd sampleuser 
$ usermod -aG wheel sampleuser
  • (お好みで)デフォルトの見た目をGNOMEクラシックからGNOMEに変更
echo 'DESKTOP="GNOME"' >> /etc/sysconfig/desktop
systemctl restart gdm.service

3. リモートデスクトップでVMに接続

リモートデスクトップアプリを使ってVMに接続する。

4. Visual Studio Code をインストール

  • 公式サイトからrmpファイルをダウンロードしてyumでインストール
cd ~/path/to/download/
sudo yum install code-1.42.0-1580986751.el7.x86_64.rpm
  • Remote(Conteiner)プラグインをインストールする VSCode上で「Remote(Conteiner)」プラグインを検索して、インストールする。

5. Dockerのインストール

  • 以下のサイトを参考にDockerをインストールする

Install Docker on CentOS 7

  • Dockerグループにユーザーを追加する
$  usermod -aG docker sampleuser

6. VS CodeでDocker実行

  • 「リモートエクスプローラー」タブ内の「+」ボタンをクリック
  • 「Open Folder in Container」 を選択し、ブランクフォルダーを選択
  • 「Python 3」コンテナーを選択して実行
  • Python 3 コードが実行できるコンテナーが起動する

image.jpeg

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