Casual Developers Note

エンジニアやデザイナー向けの技術情報・英語学習情報・海外留学情報・海外旅行情報を提供中。世界を旅して人生を楽しもう。

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • お問い合わせ
現在の場所:ホーム / 技術 Tips & Tutorials / DockerコンテナのThe PID 1 Problemとその解決策(NodeJS編)

2019年5月20日 By KD コメントを書く

DockerコンテナのThe PID 1 Problemとその解決策(NodeJS編)

DockerコンテナのThe PID 1 Problemとその解決策(NodeJS編)

Dockerでアプリケーションを構築する場合に発生するメジャーな問題である「The PID 1 Problem」とその解決策を紹介します。

見出し

  • 1 はじめに
  • 2 The PID 1 Problemとは?
  • 3 tiniとは?
  • 4 解決策
    • 4.1 サンプルNodeJSアプリケーションの作成
    • 4.2 --initによる対応(一時的な起動)
    • 4.3 tiniによる対応(永続的な起動)
  • 5 最後に
  • 6 環境
    • 6.1 関連記事

はじめに

アプリケーションをDocker化するにあたり、プロダクション用のDockerfileでは開発用のDockerfile以上にいくつか気をつける点があります。その中の一つが今回紹介する「The PID 1 Problem」の対応です。Dockerをプロダクションに適用する場合は、理解しておく必要があります。それでは、問題の内容と解決策を見ていきましょう。

The PID 1 Problemとは?

「The PID 1 Problem」とは、この記事で指摘されているDockerコンテナのPID 1に関する問題です。一般的にPID 1は「initプロセス」と呼ばれており、システムが起動した際に最初に起動するプロセスです。このinitプロセスの役割は、ゾンビプロセスの除去とサブプロセスへのシグナルの伝搬です。何の対策もせずにアプリケーションをDocker上で起動した場合、そのアプリケーションの起動プロセスがコンテナ上の最初のプロセスになってしまうため、ゾンビプロセスが残ったり、シグナルが正しく処理されないという問題が発生します。開発者として明確に困る点は、Docker化したアプリケーションが正しく停止しないことです。

tiniとは?

「tini」とは、コンテナ向けに作られたシンプルなinitプログラムです。Dockerコンテナの起動コマンドで使用することで、PID 1の本来の役割を果たし、「The PID 1 Problem」を回避できます。

解決策

それではNodeJSの簡単なDockerアプリケーションを作って、「The PID 1 Problem」を解決してみましょう。

サンプルNodeJSアプリケーションの作成

単にメッセージを返すだけのアプリケーションを作ります。

$ mkdir dockerized-app-fixed-pid1
$ cd dockerized-app-fixed-pid1/
$ yarn init -y
$ yarn add express
$ touch app.js
$ touch Dockerfile
$ touch .dockerignore

コードは以下のようにします。

app.js

const express = require('express');

const app = express();

app.get('/', (req, res) => {
  res.send('Hey, dockerized app!');
});

app.listen(3000, () => {
  console.log('Dockerized app is up...');
});

Dockerfile

FROM node:12.2-alpine

ENV NODE_ENV=production

WORKDIR /node
COPY package.json yarn.lock ./
RUN mkdir app && chown -R node:node .
USER node
RUN yarn install && yarn cache clean --force

WORKDIR /node/app
COPY --chown=node:node . .

EXPOSE 3000

CMD ["node", "app.js"]

.dockerignore

node_modules/

試しにビルドして実行してみましょう。

$ docker build -t dockerized-app .
$ docker run -p 3000:3000 dockerized-app
Dockerized app is up...

動作確認してから停止します。

$ curl localhost:3000/
Hey, dockerized app!
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS                    NAMES
a3b8c29a79c6        dockerized-app      "node app.js"       About a minute ago   Up 59 seconds       0.0.0.0:3000->3000/tcp   quizzical_heyrovsky
$ docker top a3b
PID                 USER                TIME                COMMAND
55419               1000                0:00                node app.js
$ docker stop a3b
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                       PORTS               NAMES
a3b8c29a79c6        dockerized-app      "node app.js"       About a minute ago   Exited (137) 4 seconds ago                       quizzical_heyrovsky
$ docker rm a3b

注目点として、このコンテナはControl+Cやdocker stopで停止しようとしてもすぐに停止することはなく、正しい停止処理で失敗して強制終了する形で停止しています。

補足として、NodeJSにおけるExit Codeの見方ですが、公式サイトのExit Codeのページを見ると書かれています。つまり、NodeJSのSignal ExitはUnixのSignal Numberに128を足した数字になります。上記の場合は、Exit Codeは「137」なので、128+9ということであり、「9」は「SIGKILL」のシグナルを示しているので、強制終了されていることが分かります。

--initによる対応(一時的な起動)

Dockerの「--init」オプションと使うことでDockerに同封されているinitプロセスを有効にできます。開発環境など一時的にこの問題を解決したい場合に適しています。

それではこのオプションを付けて起動します。

$ docker run --init -p 3000:3000 dockerized-app
Dockerized app is up...

停止します。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAM
ES
200687bc9992        dockerized-app      "node app.js"       38 seconds ago      Up 37 seconds       0.0.0.0:3000->3000/tcp   vig
ilant_hofstadter
$ docker top 200
PID                 USER                TIME                COMMAND
55642               1000                0:00                /dev/init -- node app.js
55677               1000                0:00                node app.js
$ docker stop 200
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
200687bc9992        dockerized-app      "node app.js"       54 seconds ago      Exited (143) 7 seconds ago                       vigilant_hofstadter
$ docker rm 200

停止はすばやく実行され、Exit Code「143」(128+15なので「SIGTERM」)で正常に終了しています。

tiniによる対応(永続的な起動)

「tini」をDockerfile上に追加することでプロダクション用にこの問題を解決できます。今回はtiniの「-e」オプションを使い、Exit Code「143」を「0」にマップすることにします。

Dockerfile

FROM node:12.2-alpine

ENV NODE_ENV=production

RUN apk add --no-cache tini

WORKDIR /node
COPY package.json yarn.lock ./
RUN mkdir app && chown -R node:node .
USER node
RUN yarn install && yarn cache clean --force

WORKDIR /node/app
COPY --chown=node:node . .

EXPOSE 3000

ENTRYPOINT ["/sbin/tini", "-e", "143", "--"]
CMD ["node", "app.js"]

それでは変更したDockerfileでビルドして起動します。

$ docker build -t dockerized-app .
$ docker run -p 3000:3000 dockerized-app
Dockerized app is up...

停止します。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  
  NAMES
c7ebb0d93ce7        dockerized-app      "/sbin/tini -e 143 -…"   10 seconds ago      Up 9 seconds        0.0.0.0:3000->3000/tcp 
  cocky_haslett
$ docker top c7e
PID                 USER                TIME                COMMAND
55904               1000                0:00                /sbin/tini -e 143 -- node app.js
55938               1000                0:00                node app.js
$ docker stop c7e
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
c7ebb0d93ce7        dockerized-app      "/sbin/tini -e 143 -…"   36 seconds ago      Exited (0) 5 seconds ago                       cocky_haslett

Exit Code「0」で想定通りに終了しています。

最後に

いかがでしたか?これでDockerコンテナのメジャーな問題である「The PID 1 Problem」を解決できるようになったと思います。プロダクションでDockerを適用する際は注意しましょう。それでは。

環境

  • Docker: 18.09.2
  • NodeJS: v12.2.0
  • Yarn: 1.16.0
The following two tabs change content below.
  • この記事を書いた人
  • 最新の記事
KD
Twitter のプロフィール

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。
KD
Twitter のプロフィール

最新記事 by KD (全て見る)

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介 - 2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~ - 2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~ - 2019年10月30日

関連記事

  • 爆速でJenkinsをマスターしよう(NodeJS編) ~ JenkinsセットアップからNodeJS自動テスト、Job DSLまで ~

    DevOpsを実現するための最も有名なCI/CDツールはJenkinsです。今回はDocker環境を前提として、Jenk…

  • Electron入門: 5日間ブートキャンプ

    Electronに入門したいエンジニア向けにこのブログの記事の中から厳選した記事を紹介します。 はじめに Electro…

  • create-react-appで作ったアプリのService Workerを無効化する方法

    create-react-appはデフォルトでPWAのためのService Workerに対応しています。しかし、PWA…

  • AWS Elastic BeanstalkでReactのDockerアプリケーションを稼働させる方法

    AWS上にDockerアプリケーションを構築する際に、Herokuのようにコマンドラインから簡単に行いたい人には、Ela…

  • [tips][Windows][SSH] Windows版OpenSSHでSSHサーバを構築する方法

    Windowsを使うエンジニアは誰もがこう考えます。LinuxのようにWindowsを使いたいと。LinuxサーバにSS…

カテゴリ : 技術 Tips & Tutorials タグ : docker, nodejs

コメントを残す コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。

ブログ更新情報や海外の関連情報などを配信する無料メルマガ

Sponsored Links

About Author

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。

https://casualdevelopers.com/

最近の投稿

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    2019年10月30日
  • BashからZshに移行する方法(Mac編)

    BashからZshに移行する方法(Mac編)

    2019年10月21日
  • Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    2019年9月30日

カテゴリ

  • 技術 Tips & Tutorials (100)
  • 技術塾 (6)
  • ライフハック (26)
  • 海外留学 (12)
  • 英語学習 (3)
  • コラム (6)

アーカイブ

最高の学習のために

人気記事ランキング

  • MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
    MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
  • Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
    Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
  • SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
    SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
  • バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
    バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
  • Amazon EC2インスタンスにSSHできなくなった時の対処法
    Amazon EC2インスタンスにSSHできなくなった時の対処法
  • Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
    Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
  • TumblrからWordPressにブログ移転する最適な方法
    TumblrからWordPressにブログ移転する最適な方法
  • SpringBootのProfile毎にプロパティを使い分ける3つの方法
    SpringBootのProfile毎にプロパティを使い分ける3つの方法
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~
    今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~
  • バンクーバー留学豆知識: バンクーバーのカジノを攻略せよ!必勝法を公開します!
    バンクーバー留学豆知識: バンクーバーのカジノを攻略せよ!必勝法を公開します!

Bitcoin寄付 / BTC Donation

Bitcoinを寄付しよう

BTC
Select Payment Method
Personal Info

Donation Total: BTC 0.0010

このブログの運営のためにBitcoinでの寄付を募集しています。お気持ち程度の寄付を頂けると管理者の励みになります。

Bitcoin寄付について知りたい方はこちらの記事へ

The following two tabs change content below.
  • この記事を書いた人
  • 最新の記事
KD
Twitter のプロフィール

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。
KD
Twitter のプロフィール

最新記事 by KD (全て見る)

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介 - 2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~ - 2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~ - 2019年10月30日

関連記事

  • 爆速でJenkinsをマスターしよう(NodeJS編) ~ JenkinsセットアップからNodeJS自動テスト、Job DSLまで ~

    DevOpsを実現するための最も有名なCI/CDツールはJenkinsです。今回はDocker環境を前提として、Jenk…

  • Electron入門: 5日間ブートキャンプ

    Electronに入門したいエンジニア向けにこのブログの記事の中から厳選した記事を紹介します。 はじめに Electro…

  • create-react-appで作ったアプリのService Workerを無効化する方法

    create-react-appはデフォルトでPWAのためのService Workerに対応しています。しかし、PWA…

  • AWS Elastic BeanstalkでReactのDockerアプリケーションを稼働させる方法

    AWS上にDockerアプリケーションを構築する際に、Herokuのようにコマンドラインから簡単に行いたい人には、Ela…

  • [tips][Windows][SSH] Windows版OpenSSHでSSHサーバを構築する方法

    Windowsを使うエンジニアは誰もがこう考えます。LinuxのようにWindowsを使いたいと。LinuxサーバにSS…

ビットコイン取引ならここ

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • サイトマップ
  • タグ一覧
  • プライバシーポリシー
  • お問い合わせ

Copyright © 2023 KD - Casual Developers Notes