2019-11-30に投稿

MinQ開発日記 (2) DockerでCentOS環境構築

Dockerで環境構築

Docker上にネコでもわかる!さくらのVPS講座のCentOSの環境を構築してみます.

この際非対話的な環境構築(つまりDockerfile)に関しては以下を参考にしました.

手元の環境を出来るだけVPS側と共通の環境を構築するのが目的です.

流れ

  • CentOS7のイメージの取得
  • パッケージの更新
  • 作業用ユーザーの設定
  • SSHによる接続
  • rootユーザーによるSSHによるログイン規制

環境構築

CentOS7のイメージの取得

CentOSイメージを取得します. バージョンは7で良いと思います. image_name:tagという形式で指定します.

FROM centos:7

パッケージの更新

イメージを作成した時点でパッケージの状態は止まっています. 更新しておきましょう.

RUN yum -y update

作業用ユーザーの設定

普通この作業は以下のコマンドを使い対話的に行います.

  • adduser
  • passwd

しかしDockerfileではこれらは使えません. 代わりに以下を使うようです.

  • useradd
  • chpasswd

一般にパスワードもハードコードはご法度です🔑 このコンテナをデプロイするわけではないので正直ハードコードしても同じなのですが環境変数の設定もしておきましょう. docker inspectのCmdセクションを見ると一目瞭然だからです. あくまでローカルで動かす利便性のための処置です.

また現状のCentOSには何も入っていないので必要なパッケージも追加しておきましょう.

ENV USER_NAME vpsuser
ENV HOME /home/${USER_NAME}

RUN yum -y update && yum -y install \
    useradd \
    chpasswd

RUN useradd --create-home ${USER_NAME} && \
         echo "${USER_NAME}:tekitou" | chpasswd

このDockerfileからイメージを作成します.

docker build -t your_image:0.1 .

名前は自由に決めましょう. これでhome以下にvpsuserが追加されました.

一般ユーザーでのログイン

これは万が一ルート権限を奪われても被害を最小限にするための重要な設定らしいです. Dockerでは必要ない気もしますが, なんとなくルートでシステムを触るのも憚られるので設定をしておきます. Dockerの場合ログインというより一般ユーザーとしてコンテナを実行するということになると思います.

その前にこの時点でどのような状態なのかを調べておきます.

docker run -it --rm your_image:0.1
whoami

でrootと出ればルート・アカウントでログインしていることになります.

USER命令を使うとユーザーが指定できます.

The USER instruction sets the user name (or UID) and optionally the user group (or GID) to use when running the image and for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile.

Dockerfile reference

また作業ディレクトリも変更しておきます.

the WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile.

USER ${USER_NAME}
WORKDIR ${HOME}

この状態で再びコンテナを対話モードで起動して

yum -y update

を実行するとPermission deniedというエラーが出るはずです. sudoコマンドを使ってみましょう.

vpsuser is not in the sudoers file.  This incident will be reported.

sudoersファイルにvpsuserはいないよと怒られてしまいました. sudoerというのはグループでここにvpsuserが登録されていると指定されたコマンドが実行できるようになります. /etc/sudoersというファイルで設定されています. 変更方法はいくつかあります.

  • visudo
  • usermod
  • gpasswd

visudoは/etc/sudoersを直接編集するので面倒です. usermodとgpasswdは同じようなことができるようです.

usermodでグループを追加するのは危険

コマンドよりもどのグループを指定するかの方が問題です. 現状を確認しておきましょう.

groups vpsuser

とするとvpsuserはvpsuserというグループに所属しています(プライマリ・グループと呼ぶようです). 確かに/etc/groupの末尾にvpsuserというのが存在します. CentOSではwheelグループに登録するといいようです(これをセカンダリ・グループと呼ぶようです).

usermod -aG wheel ${USER_NAME}

こうするとvpsuserはsudoerとしてsudoでルート権限で実行が許されたコマンドも実行できるようになります.

[Linux] なぜ sudo する権限のあるグループが「 wheel 」という名前なのか
Linuxのユーザーとグループって何だろう?

SSHによる接続

SSHによる接続はインターネット経由でサーバーにアクセスする場合に通信の内容を傍受されないように暗号化するためです. ので無視します.

Webサーバー

Apacheはwebサーバーですが, Goの場合いらないようです. 正確には要らないというよりはnet/httpパッケージとしてwebサーバーに必要な機能が提供されています.

Package http provides HTTP client and server implementations.

がこの辺はPHPより取り扱いが楽な予感がします. PHPのBuilt-in web serverには以下のような警告文が掲載されています.

Warning
This web server was designed to aid application development. It may also be useful for testing purposes or for application demonstrations that are run in controlled environments. It is not intended to be a full-featured web server. It should not be used on a public network.

つまり開発用サーバーということでネットに公開するならApacheやnginxなどのwebサーバーが必要になります. ただしGoでもApacheとかnginxとかと使う例とかもあるのであったほうがいいケースとかもあるのかなとは思います.

nginx + golangでWeb API開発
Load Balancing Recipe
Reverse Proxy Recipe
GoLang: Zero downtime deploys and Rollbacks | Go HTTP server−Echo Web Framework & Apache

アプリケーション・サーバー(APサーバー)

APサーバーも不要なようです. 3層アーキテクチャを前提にすると, APサーバーはWebサーバーから転送されてくるデータを基にDBサーバーと連帯してデータの取得・加工を行うプログラムというのが私の理解です.

しかしその存在意義はよく分かりませんでした.

Web APサーバーとWebフレームワークをつなぐものによるとWebサーバーとWebフレームワークの差異を吸収するために存在するようです.

Apache以外にもlighttpd, nginxなどさまざまなWebサーバがあるわけだが、そうした多様なWebサーバ環境ごとの差異を吸収するためのコードをWebフレームワーク(例:Catalyst, Django, Rails)が書くのは大変である。ましてWebフレームワークは沢山あるわけだし。そうした状況を解決するために出てきたのがPythonならWSGIであり、RubyならRackであり、PerlならPSGIである。

WSGIとPythonでスマートなWebアプリケーション開発をでも似たようなことが書かれています.

Pythonには,Zope,Twisted,Django,TurboGearsなどのさまざまなWebアプリケーションフレームワークが存在します。以前は,このような幅広い選択肢は,新しくPythonを使用しようとするユーザにとって都合が悪いことがありました。使用するフレームワークによって,利用可能なサーバが制限されてしまったり,逆にサーバによって利用可能なWebアプリケーションフレームワークが制限されるといったことがあったためです。そのようなPythonの状況とは対照的に,Javaにおいては,多数のWebアプリケーションフレームワークがあるにもかかわらず,サーバとWebアプリケーションをつなぐ際に,統一されたJava Servelet APIを用いることで,アプリケーションフレームワークを気にせずにServelet APIが利用できる環境で動作させることが可能です。このような,サーバとWebアプリケーションをつなぐ共通のインターフェースをPythonで定義したものが,PEP333(日本語訳版)のWSGI(Web Server Gateway Interface)です。

つまりWebフレームワークで作ったWebサービスを起動するサーバー(常駐プロセス?)で, APサーバーのおかげでWebサーバーとWebフレームワークの組み合わせを柔軟に選択できるようです. Goの場合Webサーバーの機能がパッケージとして提供されているのでAPサーバーのようなミドルウェアで切り離す必要がない, と勝手に考えて不要としておきます.

まとめと課題

スケールだ負荷分散だと言い出すと色々と複雑になるのかもしれませんが, シンプルにAPIサーバーを運用するならミドルウェアを省略できたりGoの標準機能でカバーできたりとシンプルに書けて良さそうです. ネコでもわかる!さくらのVPS講座ではこの後Apacheを入れてファイアウォールの設定をして簡単な静的ファイルを配信して確認します. そしてPHPやDBサーバー(MariaDB)を導入しますが, Goの場合はGo言語を入れないとnet/httpが使えないので, まずGoを入れることにします.

DB

後はGoでAPIサーバーということになると必要なのはDBサーバーということになりますがとりあえず置いておきましょう.

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

これはDockerfileからは設定できないようです. つまり起動したコンテナから設定する必要があると思いますが, httpd(Apache)が動いている前提なので, 先にGoでWebアプリケーションを作ってからにしようと思います.

Docker : firewalldでssh, http, httpsのみ許可する。(CentOS 7)

Reference

ネコでもわかる!さくらのVPS講座
チュートリアル:CentOS 7(さくらのVPS)サーバ作成直後に設定しておくべき初期セキュリティ設定
マンガで学ぶConoHa

Dockerで開発環境を仮想化する

【UNIX】passwdとchpasswdの違い
DockerのscratchイメージでHello Worldする

その他

Docker + Go + Gin の開発環境を準備する
Docker development best practices
APIセキュリティ入門
Web API の紹介
CentOS 7で始める最新Linux管理入門
基礎から理解するLinuxサーバー[Cent OS 7.0編]
linuxize
“応用力”をつけるためのLinux再入門

Appendix

イメージのサイズの軽量化

実際に(貧乏)VPS上で走らせるにはalpineとかMulti-stage buildsといった節約が必要になります.

Create the smallest and secured golang docker image based on scratch
Use multi-stage builds

RUN vs CMD

RUNで指定したコマンドは実行された結果がレイヤーとしてイメージに取り込まれるが, CMDはコンテナ内で実行されるコマンドを指定する.

Dockerにおける機密情報の扱い

今回はローカルでの開発環境を構築したいだけなので特にセキュリティ面を考慮する必要性はないのですが, ある程度やり方を押さえておくのはいいかと思います.

buildサブコマンドの--build-argオプションにはビルド時の引数を渡せる. DockerfileではARGに指定する. Dockerfile referenceによると機密情報なんかはコマンドで見えてしまうので, Dockerfileに含めるのは良くないようです.

Warning: It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc. Build-time variable values are visible to any user of the image with the docker history command.

要するにイメージに埋め込まれちゃうのでやめとけと言うことです. hisotryサブコマンドで見えてしまうのと, コンテナの再利用が難しくなってしまいます.

公式に用意されている機能としては, Docker secretesと言う機能があるようです(Swarmモードでしか使えないらしいですが).

Manage sensitive data with Docker secrets

もう一つの環境変数を使う方法です. こちらもDockerfileに書いてしまうとinspectサブコマンドでみれてしまうようです.

squash option

Using SSH keys inside docker container

ツイッターでシェア
みんなに共有、忘れないようにメモ

ブレイン

Androidアプリ開発者を目指しています. 興味あることリスト: https://t.co/ew3bb6grdJ Github: https://t.co/9btqysHqWr Qiita: https://t.co/ZVRhjouauX

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント