dockerでnginxを使用する際に"daemon off"することの意味

dockerでもnginxを使うことが多々あるでしょう。
ネットの記事や公式のDockerfileを見ると、以下のようなコマンドで実行しています。

CMD ["nginx", "-g", "daemon off;"]

この記事では上記のコマンドにはどんな意味があり、なぜそうする必要があるのかを書きます。

1. コマンドの意味

dockerを使わずにローカルで試す場合は以下のコマンドになります。

nginx -g "daemon off;"

gオプションおよび"daemon off;"についてはそれぞれ以下の公式の記載が参考になります。

gオプションはグローバルなディレクティブ(nginxにおける設定項目)を設定するためにつけます。
"daemon off;"はディレクティブ(ディレクティブは;で終わったり、{}を使って定義する)であり、
"daemon off;"の場合は nginxがdaemonとして起動しないようにする。

端的に説明すると、"daemon on;"とするとバックグラウンドで起動し、"daemon off;"とするとフォアグラウンドで起動する(デフォルトでは"daemon on;")。
試しにnginx -g "daemon off;"nginx -g "daemon on;"をそれぞれ実行してみるとその差がよくわかるだろう。

2. なぜ"daemon off;"にするのか

結論からいうとそうしないとルート・プロセス終了し、コンテナが停止してしまうからです。公式にも解説があります。
試しに以下のコマンドでコンテナでプロセスを実行してみましょう。

  1. docker run -d -p 80:80 nginx nginx
  2. docker run -d -p 80:80 nginx nginx -g "daemon on;"
  3. docker run -d -p 80:80 nginx
  4. docker run -d -p 80:80 nginx nginx -g "daemon off;"

1, 2を実行した場合は以下のようにコンテナが停止してしまっている。

$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
00773f23396f        nginx               "nginx -g 'daemon on…"   2 seconds ago       Exited (0) 1 second ago                         lucid_austin
edda344f8191        nginx               "nginx"                  17 seconds ago      Exited (0) 16 seconds ago                       eager_rubin

3, 4を実行した場合はともにコンテナが起動したままになる。

$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
f1a77cf7bed7        nginx               "nginx -g 'daemon of…"   5 seconds ago       Up 3 seconds        0.0.0.0:80->80/tcp   elastic_dijkstra

3の場合はDockerfileのCMDが実行されるため4と同じ結果になる。

2. Rust でHllo World -- 初めてのパッケージ作成 --

RustをインストールしたのでおなじみのHello Worldプログラムを作って見ましょう。

常々言っていますがまずは公式の情報を見るようにしましょう。リンクは以下です。
Get started - Rust programming language

こちらも以下に手順を書きます。

1. プロジェクトの作成

適当なディレクトリで新規のプロジェクトを作成します。

$ cargo new hello-rust
     Created binary (application) `hello-rust` package

cargo newにも色々オプションがありますのでこちらを見ると良いでしょう。
new - The Cargo Book

実行すると以下のような構成のファイルが自動生成されます。

$ tree -a . -L 3
.
└── hello-rust
    ├── .git
    │   ├── HEAD
    │   ├── config
    │   ├── description
    │   ├── hooks
    │   ├── info
    │   ├── objects
    │   └── refs
    ├── .gitignore
    ├── Cargo.toml
    └── src
        └── main.rs

.gitディレクトリや.gitignoreについてはご存知かと思いますので割愛します。

Cargo.tomlファイルは以下のような内容が自動生成されます。このファイルにはプロジェクトに関する情報や依存関係について記載します。

[package]
name = "hello-rust"
version = "0.1.0"
authors = ["XXXXXX <XXXXXX@XXX.XXX>"]
edition = "2018"

[dependencies]

src/main.rsにはアプリケーションのコードを書きます。初期生成時は以下のようになっています。 今回はそのまま使います。

fn main() {
    println!("Hello, world!");
}

2. 実行

試しに実行して見ましょう。RustにもGoのgo runのようなコマンドがあるのでそれをプロジェクトのディレクトリ内で実行します。

$ cargo run
   Compiling hello-rust v0.1.0 (<プロジェクトのディレクトリ>)
    Finished dev [unoptimized + debuginfo] target(s) in 4.04s
     Running `target/debug/hello-rust src/main.rs`
Hello, world!

上記のようにプログラムの実行とtargetディレクトリ作成が行われていることがわかります。 compileは初期実行時に行われ、以降ファイルが更新されるまでは行われません。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/hello-rust`
Hello, world!

3. ビルド

ビルド単独で実行することもできます。

こちらも色々オプションがありますのでこちらを見ると良いでしょう。
build - The Cargo Book

$ cargo build
   Compiling hello-rust v0.1.0 (<プロジェクトのディレクトリ>)
    Finished dev [unoptimized + debuginfo] target(s) in 0.56s

ビルドされるとtarget/debugディレクトリにhello-rustが作成されるので実行して見ましょう。

$ ./target/debug/hello-rust
Hello, world!

1. Rust と各種インストール(Rustupなど)

まずはRustをインストールします。

常々言っていますがまずは公式の情報を見るようにしましょう。リンクは以下です。
Get started - Rust programming language

一応説明込みで以下に手順を書きます。

Rustのインストールは公式サイトにもある通りRustupを使用することが推奨されています。
以下のコマンドでRustupをダウンロードします。

$ curl https://sh.rustup.rs -sSf | sh

rustc,cargo,rustupが格納されている場所(~/.cargo/bin)までのパスは Rustupのインストール中に自動的に.profile,.bash_profileに以下のように書き込まれます。

export PATH="$HOME/.cargo/bin:$PATH"

Rustupのアップデートはrustup updateでできます。

すぐに各種コマンドを実行したい場合はインストール中に表示される通り以下のコマンドを実行しましょう。

source $HOME/.cargo/env

中身は.profile,.bash_profileに追加された内容と同じです。

$ cat .cargo/env
export PATH="$HOME/.cargo/bin:$PATH"

最後にインストールされていることとバージョンを合わせて確認します。

$ rustup --version
rustup 1.18.2 (a0bf3c9cb 2019-05-02)
$ rustc --version
rustc 1.34.1 (fc50f328b 2019-04-24)
$ cargo --version
cargo 1.34.0 (6789d8a0a 2019-04-01)

Rust入門記

周りの人がおすすめしているのでやってみるかということでRustに入門することにしました。 今回はmac(mac os: 10.14.4)で入門していきます。

以下には今後書いていく記事のリンクを追加していきます。

1. Rust と各種インストール(Rustupなど) - ゼロからスタート

2. Rust でHllo World -- 初めてのパッケージ作成 -- - ゼロからスタート

Dockerで構築したRails環境にDatadogを導入する

最近業務でDatadogを使うようになったので勉強がてら試してみようと思います。 バージョンは以下の通りです。

まずはAPIモードでプロジェクトを作りましょう(--api を付けるとAPIモードになる)。

$ rails new datadog-test --api --database=mysql

gemsetも作成しましょう。

$ rbenv gemset create 2.6.3 datadog-test
$ echo > .rbenv-gemsets datadog-test

環境はDockerで作ります。mysqlも使いたいのでdocker-composeで合わせて作ります。 image軽量化のためにmulti stage buildにしています。

  • Dockerfile
FROM ruby:2.6.3-alpine as builder
RUN apk --update add --virtual build-dependencies \
    build-base \
    curl-dev \
    mysql-dev \
    linux-headers
RUN gem install bundler
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
ENV BUNDLE_JOBS=4
RUN bundle install
RUN apk del build-dependencies

FROM ruby:2.6.3-alpine
ENV LANG ja_JP.UTF-8
RUN apk --update add \
    bash \
    mariadb-dev \
    tzdata
RUN gem install bundler

WORKDIR /tmp
COPY --from=builder /usr/local/bundle /usr/local/bundle

ENV APP_HOME /myapp
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME

# Start the main process.
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
  • docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: hi54ftyugiy3

  api:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      MYSQL_PASSWORD: hi54ftyugiy3

volumes:
  mysql_data:

config/database.yml は自動生成されたものを一部修正しています。hostで指定する値はdocker-compose.yml のmysqlのservice名です。

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

development:
  <<: *default
  database: datadog-test_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: datadog-test_test

バックグラウンドでコンテナを起動します。

$ docker-compose up -d --build
・・・
・・・
Successfully built 34aeb8dd5580
Successfully tagged datadog-test_api:latest
Creating datadog-test_db_1 ... done
Creating datadog-test_api_1 ... done

DBの作成を行います。

$ docker-compose run api bundle exec rails db:create
Starting datadog-test_db_1 ... done
Created database 'datadog-test_development'
Created database 'datadog-test_test'

http://localhost:3000/ にアクセスしてRailsの起動を確認します。 f:id:tottoto_toto:20190421011121p:plain

次に適当なAPIを作成します。本筋ではないのでscaffoldで作ります。

bin/rails g scaffold Book title:string

titleだけだとかっこ悪いのでmigrationファイルをいじります。

  • db/migrate/20190420163659_create_books.rb
class CreateBooks < ActiveRecord::Migration[5.2]
  def change
    create_table :books do |t|
      t.string   :title
      t.string   :author
      t.string   :publisher
      t.text     :isbn
      t.datetime :publication_date
      t.text     :description
      t.timestamps
    end
  end
end

許容するparameterも更新しておきます。

  • app/controllers/books_controller.rb
・・・
・・・
   # Only allow a trusted parameter "white list" through.
    def book_params
      params.require(:book).permit(
        :title,
        :author,
        :publisher,
        :isbn,
        :publication_date,
        :description
      )
    end

更新するためコンテナを起動し直します。

$ docker-compose up -d --build

DBの更新を適用します。

$ docker-compose run api bundle exec rails db:migrate RAILS_ENV=development
Starting datadog-test_db_1 ... done
== 20190420163659 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0161s
== 20190420163659 CreateBooks: migrated (0.0163s) =============================

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

$ docker-compose up -d --build

適当にデータを登録してみましょう。

$ curl -X POST \
>   http://localhost:3000/books \
>   -H 'content-type: application/json' \
>   -d '{
>       "title": "桃太郎",
>       "author": "名無し",
>       "publisher": "名無し出版",
>       "isbn": "0000-0000",
>       "publication_date": "2018-04-20",
>       "description": "昔々あるところに"
> }'
{"id":5,"title":"桃太郎","author":"名無し","publisher":"名無し出版","isbn":"0000-0000","publication_date":"2018-04-20T00:00:00.000Z","description":"昔々あるところに","created_at":"2019-04-20T17:13:52.000Z","updated_at":"2019-04-20T17:13:52.000Z"}

登録結果を確認してみます(そのままだと見にくいのでjqコマンドで整形しています)。

$ curl -X GET http://localhost:3000/books | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   267    0   267    0     0  17248      0 --:--:-- --:--:-- --:--:-- 17800
[
  {
    "id": 1,
    "title": "桃太郎",
    "author": "名無し",
    "publisher": "名無し出版",
    "isbn": "0000-0000",
    "publication_date": "2018-04-20T00:00:00.000Z",
    "description": "昔々あるところに",
    "created_at": "2019-04-20T17:13:52.000Z",
    "updated_at": "2019-04-20T17:13:52.000Z"
  }
]

それではDatadogを導入していきます。ここを参考にすると良いでしょう。 docs.datadoghq.com

まずはgemのddtraceを追加します。

  • Gemfile
+ gem 'ddtrace'

docker-compose.ymlにDatadog用のコンテナを追加します。今回はログも取得したいので以下を参考にしましょう。

docs.datadoghq.com docs.datadoghq.com

  • docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: hi54ftyugiy3

  api:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      MYSQL_PASSWORD: hi54ftyugiy3
+    links:
+      - dd-agent
+
+  dd-agent:
+    image: datadog/agent:latest
+    ports:
+      - 8126:8126
+    environment:
+      DD_API_KEY: <DatadogのAPI KEY>
+      DD_APM_ENABLED: "true"
+      DD_LOGS_ENABLED: "true"
+      DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL: "true"
+      DD_AC_EXCLUDE: "name:datadog-agent"
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock:ro
+      - /proc/:/host/proc/:ro
+      - /sys/fs/cgroup/:/host/sys/fs/cgroup:ro

volumes:
  mysql_data:

logを確認するため、Railsのログを出力させます。

  • Dockerfile
# Start the main process.
- CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
+ CMD bundle exec rails server -b 0.0.0.0 && tail -f log/development.log

initializerも作成します。

  • config/initializers/datadog.rb
require 'ddtrace'
Datadog.configure do |c|
  # This will activate auto-instrumentation for Rails
  c.use :rails
  c.tracer hostname: 'dd-agent',
           port: 8126
end

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

$ docker-compose up -d --build

適当なAPIを叩いて確認します。 f:id:tottoto_toto:20190422020453p:plainf:id:tottoto_toto:20190422020502p:plain

ちゃんと捕捉できていますね。試しにわざと例外を発生されたりもしましたが、独自のrender用のメソッドを追加したりもしましたが、問題なくログを取得できました。

終わってみると簡単でしたが、Dockerの設定が割と分かり難かったかなという印象です。皆さんもぜひ業務で役立ててみてください。

Google Cloud SDK コマンド覚え書き

GCPの勉強を行う上で、やはり業務を行う場合はCUIでの作業にも慣れないといかん。。。
というわけでGoogle Cloud SDKのコマンドをここに記す(gcloudのバージョン: Google Cloud SDK 232.0.0)。

公式情報: https://cloud.google.com/sdk/gcloud

Google Cloud SDK の取得

使用PCがmacのため後述のリンクを参照
Quickstart for macOS  |  Cloud SDK Documentation  |  Google Cloud

SDKの初期化

https://cloud.google.com/sdk/docs/quickstart-macos#initialize_the_sdk

プロジェクト

https://cloud.google.com/sdk/gcloud/reference/projects/

プロジェクトの作成

gcloud projects create <PROJECT_ID>

プロジェクトの一覧

gcloud projects list

プロジェクトの詳細

gcloud projects describe <PROJECT_ID >

設定

プロジェクトの設定

gcloud config set project <PROJECT_ID >

Typesctipt とVueで開発していたら,vue-routerの部分でtslintで「〜の引数を型'RouterOptions'のパラメーターに割り当てることはできません。」と出た時の対処法

vue cli3で簡単にTypescriptを導入できるようになったので開発していたところ、以下のようなエラーが出た。

[ts]
型 '{ mode: "history"; base: any; routes: ({ path: string; component: VueConstructor<Vue>; } | { path: string; components: VueConstructor<Vue>; } | { path: string; redirect: string; })[]; }' の引数を型 'RouterOptions' のパラメーターに割り当てることはできません。
  プロパティ 'routes' の型に互換性がありません。
    型 '({ path: string; component: VueConstructor<Vue>; } | { path: string; components: VueConstructor<Vue>; } | { path: string; redirect: string; })[]' を型 'RouteConfig[]' に割り当てることはできません。
      型 '{ path: string; component: VueConstructor<Vue>; } | { path: string; components: VueConstructor<Vue>; } | { path: string; redirect: string; }' を型 'RouteConfig' に割り当てることはできません。
        型 '{ path: string; components: VueConstructor<Vue>; }' を型 'RouteConfig' に割り当てることはできません。
          プロパティ 'components' の型に互換性がありません。
            型 'VueConstructor<Vue>' を型 'Dictionary<Component>' に割り当てることはできません。
              型 'VueConstructor<Vue>' のインデックス シグネチャがありません。
(property) RouterOptions.mode?: "hash" | "history" | "abstract" | undefined

vue-router部分の実装はこう。routes.tsでルーティングを定義し、index.tsでRouterとしてexportする。
今回のエラーはindex.ts で出ました。
index.ts

import Vue from 'vue';
import Router, { RouterOptions } from 'vue-router';
import routes from './routes';

Vue.use(Router);

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
});

routes.ts

import TodoList from '@/components/templates/TodoList.vue';
import TodoListDetail from '@/components/templates/TodoListDetail.vue';


export default [
  {
    path: '/tasks',
    component: TodoList,
  },
  {
    path: '/tasks/:id',
    components: TodoListDetail,
  },
  {
    path: '*',
    redirect: '/tasks',
  },
];

基本的には以下のリンクを見ればわかります。

github.com

この場合は型アサーションをする必要があり、修正方法は以下の2つです。

  1. index.tsを修正(RouterOptionsにする)
import Vue from 'vue';
import Router, { RouterOptions } from 'vue-router';
import routes from './routes';

Vue.use(Router);

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
} as RouterOptions);
  1. routes.tsを修正(RouteConfig[]にする)
import TodoList from '@/components/templates/TodoList.vue';
import TodoListDetail from '@/components/templates/TodoListDetail.vue';
import { RouteConfig } from 'vue-router';

export default [
  {
    path: '/tasks',
    component: TodoList,
  },
  {
    path: '/tasks/:id',
    components: TodoListDetail,
  },
  {
    path: '*',
    redirect: '/tasks',
  },
] as RouteConfig[];

以上、ご参考になれば幸いです。