skydum

個人的な作業記録とか備忘録代わりのメモ

CentOS Stream 9でSELinuxをdisabledにする

CentOS Stream 9でSELinuxを完全に無効化する

CentOS Stream 9のダウンロード

CeneOS Steam 9での変更点

SELINUX=disabled

に変更すれば無効化ができたがCentOS Stream 9では無効にすることができなくなった。 無効にするには起動時のkernelのオプションとしてselinux=0を指定する必要がある。

SELnixuの完全な無効化

  • SELinuxの実践 を見た方がとても詳しいが、すぐに忘れてしまうので記載しておく。
  • SELinuxの無効化にはkernelの起動オプションの変更が必要となる。
  • kernelの起動オプションを変更できるgrubbyコマンドを利用してSELinuxを永続的に無効化するオプションを追加する。

SELinuxが有効であることを確認

# getenforce
Enforcing

現在設定されているkernelの起動時のオプションの一覧を表示する

  • argsの所がkernelに渡される起動オプション(2箇所ある)
# grubby --info ALL
index=0
kernel="/boot/vmlinuz-5.14.0-124.el9.x86_64"
args="ro resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap"
root="/dev/mapper/cs-root"
initrd="/boot/initramfs-5.14.0-124.el9.x86_64.img"
title="CentOS Stream (5.14.0-124.el9.x86_64) 9"
id="472bf047f36a4bd1a9aaf50bd99d1a89-5.14.0-124.el9.x86_64"
index=1
kernel="/boot/vmlinuz-0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89"
args="ro resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap"
root="/dev/mapper/cs-root"
initrd="/boot/initramfs-0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89.img"
title="CentOS Stream (0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89) 9"
id="472bf047f36a4bd1a9aaf50bd99d1a89-0-rescue"

kernelの起動オプションにselinux=0を追加する

  • kernelの起動時の選択肢の全ての項目にselinux=0を追記する(2箇所追加する)
# grubby --update-kernel ALL --args 'selinux=0'

再度現在設定されているkernelの起動時のオプションの一覧を表示する

  • argsの所がkernelに渡される起動オプション
# grubby --info ALL
index=0
kernel="/boot/vmlinuz-5.14.0-124.el9.x86_64"
args="ro resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap selinux=0"
root="/dev/mapper/cs-root"
initrd="/boot/initramfs-5.14.0-124.el9.x86_64.img"
title="CentOS Stream (5.14.0-124.el9.x86_64) 9"
id="472bf047f36a4bd1a9aaf50bd99d1a89-5.14.0-124.el9.x86_64"
index=1
kernel="/boot/vmlinuz-0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89"
args="ro resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap selinux=0"
root="/dev/mapper/cs-root"
initrd="/boot/initramfs-0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89.img"
title="CentOS Stream (0-rescue-472bf047f36a4bd1a9aaf50bd99d1a89) 9"
id="472bf047f36a4bd1a9aaf50bd99d1a89-0-rescue"

再起動

# reboot

getenforceで再確認

# getenforce
Disabled

uWSGIで利用できるオプションとその説明

uWSGIで利用できるオプションとその説明

  • uWSGIは設定できるオプションが多いが公式サイトの説明がシンプルすぎてわからなくなるので、個人的に調べて内容を記載する。

ドキュメント

環境

  • CentOS Linux release 7.9.2009 (Core)
  • apach(2.4.6-97)
  • python(3.6.8)
  • uwsgi(2.0.20)
  • Flask(2.0.3)
  • mod_proxy_uwsgi(2.0.18)

uWSGIのインストール

  • すでにuWSGIがインストールされている場合は念のため一度アンインストールする
  • pcreとpcre-develを入れてるのはlog-routeを利用する場合にpcre-develを入れてからuWSGIをインストールしないと
    !!! no internal routing support, rebuild with pcre support !!!
    のエラーが表示されlog-route機能が利用出来ない

  • CentOS 7の場合

yum install pcre pcre-devel
pip install uwsgi -I --no-cache-dir
apt-get install libpcre3 libpcre3-dev
pip install uwsgi -I --no-cache-dir

uwsgitopを利用する

# python3 -m pip install uwsgitop

設定変更の参考

uwsgiに設定するオプション

  • uWSGI --helpで設定できるオプションの一覧が取得できるが量が多すぎる

設定内容の誤りをチェックする

オプション サンプル 内容 ドキュメント
strict strict = true 設定ファイルに記載できるオプションは、uWSGIがオプションとして認識できるもののみとし、strictを有効にした場合は認識できないオプションが記載されている場合はエラーでuWSGIが起動しなくなる
typoを防げる。
独自のプレースホルダーが使えなくなる?(要調査)
strict
need-app need-app = true 実行するアプリケーションに問題があった場合uWSGIを起動しない
デフォルトではアプリは起動するが500エラーを返すので有効にするのを推奨
need-app

パフォーマンスの監視

  • uwsgitopでstatsに指定したファイルを指定して現在のprocessの状態とthreadの状態を確認することができる
オプション サンプル 内容 ドキュメント
stats stats = /var/log/uwsig.socket パフォーマンス監視用の統計データを出力 stats
memory-report memory-report = true モリーの使用量を出力? memory-report

便利そうなオプション

オプション サンプル 内容 ドキュメント
vacuum vacuum = true uWSGIが起動時に作成したファイル(ソケットやPIDのファイルなど)をuWSGIの終了時に自動的に削除する
vacuum
chmod-socket chmod-socket = 666 http-socket, socketオプショと併用する
socketファイルのアクセス権限を変更する
chmod-socket
chown-socket chown-socket = uwsgi_user http-socket, socketオプショと併用する
socketファイルの所有者を変更する
chown-socket

起動時に関係するオプション

オプション サンプル 内容 ドキュメント
chdir chdir = /home/uwsgi_user/api uWSGIが起動し、アプリケーションをロードする前にカレントディレクトリを移動する chdir
chdir2 chdir2 = /home/uwsgi_user/api uWSGIが起動し、アプリケーションをロードしたあとで、カレントディレクトリを移動する chdir2
single-interpreter single-interpreter = true プロセス毎に一つのアプリケーションを利用する single-interpreter
PythonエージェントとuWSGIウェブサーバー
die-on-term die-on-term = true SIGTERMを受けた時にプロセスを終了する
デフォルトではプロセスが再起動してuWSGIは終了しない
die-on-term
What is –die-on-term?
wsgi-file wsgi-file = /home/uwsgi_user/api/app.py 起動するアプリケーションのファイルを指定する wsgi-file
callable callable = app

例としてFlaskの場合
from flask import Flask
app = Flask(__name__)
とした場合は、appを指定する
起動するアプリケーションのオブジェクトを指定する callable
gid gid = uwsgi_group 起動するグループIDを指定する gid
uid uid = uwsgi_user 起動するユーザーIDを指定する uid

待ち受ける方式に関するオプション

オプション サンプル 内容 ドキュメント
http http = 127.0.0.1:8080 httpで指定されたアドレスとポート番号で起動する
http-socketを利用する方がパフォーマンスが良い
環境によるので要パフォーマンス調査
http
http-socket http-socket = 127.0.0.1:8080 http-socketで指定されたアドレスとポート番号で起動する
processes = 1でtouch-reloadしてもエラーがクライアントへ返らない
http
socket socket = /home/uwsgi_user/api/app.sock
socket = 127.0.0.0:8080
socketで指定されたファイルまたはipアドレス:ポート番号のいずれかの方法で指定する
ファイル名で指定する場合でapacheと連携する場合、apacheのバージョンが2.4.9以上が必要

socket

各々の待受方式でベンチマークを行った結果

  • apache → uwsgi → app.py実行した結果
  • process=1, threads = 1での結果(環境によってすごく数値が変わるので参考程度)
方式 abテストの結果 uwsgiに直接http接続が可能か
http Requests per second: 1119.57 [#/sec] (mean)
socket(ip:port形式) Requests per second: 1297.07 [#/sec] (mean) 不可
http-socket Requests per second: 1471.42 [#/sec] (mean) 不可
socket(file形式) Requests per second: 1522.14 [#/sec] (mean) 不可

パフォーマンスに直接関係しそうなオプション

オプション サンプル 内容 ドキュメント
master master = true wokerのプロセスが終了した場合に自動的にwokerを再起動してくれる
uWSGI内蔵のPerfork + マルチスレッドで動作する
master
enable-threads enable-threads = ture threadを有効にする
enable-threads = true
とした場合は
threads = 1を設定したのと同じ効果がある
enable-threads
processes processes = 4 利用するプロセス数
processesとworkersはどちらも名称は異なるが同一の設定
processes
workers workers = 4 利用するプロセス数
processesとworkersはどちらも名称は異なるが同一の設定
workers
threads threads = 15 利用するスレッド数
threadsを設定すると
enable-threads = trueも設定される
threads
thunder-lock thunder-lock = true Linuxの場合有効にすることを推奨 thunder-lock
Serializing accept(), AKA Thundering Herd, AKA the Zeeg Problem
ugreen ugreen = true OSのスレッド機構を利用せずに非同期にスレッドを切り替える機構を有効にする
またgreenletよりもパフォーマンスに優れているが、メモリの消費が多い
環境にもよると思うが有効にしても特になにかが変わったように見えなかった
ugreen
uGreen – uWSGI Green Threads
greenlet オプション設定不可 OSのスレッド機構を利用せずに非同期にスレッドを切り替える機構を有効にする
パフォーマンスはugreenより劣るがメモリの消費が少ない
オプションが見つからずエラーとなる
greenlet
uGreen – uWSGI Green Threads

ワーカーの再起動に関するオプション

オプション サンプル 内容 ドキュメント
max-requests max-requests = 100 指定された回数のリクエストを処理したらwokerを再起動する max-requests
max-requests-delta オプション設定不可 max-requestsで指定された回数でwokerが再起動すると同じタイミングでworkerが再起動するので、タイミングをずらすために使用するとあるが、strict modeで起動するとオプションが見つからずエラーとなる

エラーになる原因は以下を参考
あなたの使ってるuWSGI、本当にmax-requests-delta効いてますか?
uWSGIのmax-requets-delta、マジで効いていない
現在のバージョン(uwsgi(2.0.20))では無効なオプションとなる
max-requests-delta
max-worker-lifetime max-woker-lifetime = 60 指定された秒数が経過したらworkerを再起動する max-worker-lifetime
reload-on-rss reload-on-rss = 2048 指定されたバイト数以上のメモリが利用されている場合wokerを再起動する reload-on-rss
worker-reload-mercy worker-reload-mercy = 60 workerがリロードやシャットダウンにかかる時間(秒) worker-reload-mercy

アプリケーションのリロードに関するオプション

オプション サンプル 内容 ドキュメント
touch-reload touch-reload = /home/uwsgi_user/api/uwsig.touch
上記のファイルを更新するとworkerが再起動する
Preforkモード(親プロセスをForkしてWorkerを作成するモード)
リロードされる時にuWSGIの起動オプションも再読込される
touch-reload
lazy-apps lazy-app = true Preforkと違いWorker毎にアプリケーションをロードする lazy-apps
Standard (default/boring) graceful reload (aka SIGHUP)
touch-workers-reload touch-workers-reload = /home/uwsgi_user/api/uwsig.touch lazy-appsと併用する
インスタンス全体のWorkerの同時再起動を避けることができる
touch-reloadと比べて特別メリットがない
touch-workers-reload
Workers reloading in lazy-apps mode
touch-chain-reload touch-chain-reload = /home/uwsgi_user/api/uwsig.touch lazy-appsと併用する
一度に一つのWorkerを再起動するので再起動を行う際の待ち時間を減らすことができる
また、Workerの再起動を順番に行うのでサーバに負荷がかからない
ただし、それなりに多くのWorkerがないとあまり恩恵が受けられない
touch-chain-reload
Chain reloading (lazy apps)
Zerg mode よくわからない よくわからない Zerg mode 1
Zerg mode 2

リロードされる範囲

オプション uWSGIのオプション変更 備考欄
touch-reload 変更可 リロードにかかる時間が長い
touch-workers-reload 不可 processの再起動のみなのでtouch-reloadよりは早い
touch-chain-reload 不可 processの再起動を順番に行う
Zerg mode

ロガー

  • ログ関係は設定の種類が多くてとても分かり辛い
  • ロガーの設定方法は2種類ある(logtoとlogger)
    • logto
      • iniファイルで指定されたファイルへログの内容を出力するlogtoをベースにした機能
    • logger
      • iniファイルで指定された指定の場所(file, socket, syslogなど 詳細はLoggingを参考)へログの内容を出力するloggerをベースにした機能

logto

  • ログの基本設定
    • 記録される内容はオプションは変わっても同じ
    • ログ出力に関するオプションを設定しない場合uWSGIの起動時のログ、アクセスログ、エラー内容が記録される
オプション サンプル 内容 ドキュメント
logto logto = /home/uwsgi_user/api/access_log uWSGIの起動時のログ、アクセスログ、エラーログが記録される
logto2 logto2 = /home/uwsgi_user/api/access_log 特権を剥奪してからログの記録を行う
他の動作はlogtoと同等
daemonize daemonize = /home/uwsgi_user/api/access_log uwsgi uwsgi.iniの様に起動したときに通常はコンソールがコマンド事項状態で止まるが、daemozineを有効にしている場合はuwsgi uwsgi.iniの実行後すぐにコンソールに戻ってきてuwsgiはバッググラウンドで実行するようになる<br<記録されるログはlogtoと同じ
daemonize2 daemonize2 = /home/uwsgi_user/api/access_log アプリケーションの起動後にバックグラウンド動作に移る
uWSGIの起動時のログは記録れずコンソールに表示される、起動時のログが記録されない以外はlogtoと同じ内容が記録される
ファイルをtouchして再起動した時は起動時のログも残る
daemonizeと使い分けるメリットが良くわからない
  • ログ出力に関するオプション設定
オプション サンプル 内容 ドキュメント
disable-logging disable-logging = true 有効にした場合logtoで指定されたファイルへアクセスログを記録しない
apacheとかnginxと連携している場合uWSGI側のアクセスログは不要だと思うのでdisable-logging = trueとしても問題ないと思う
log-4xx log-4xx = true disable-logging = trueの場合に400エラーをlogtoに指定したファイルへ記録する
未設定の場合ログには400エラーは記録されない
log-5xx log-5xx = true log-400 = trueと同じく500エラーをログに記録するようにするのかと思ったらtrueでもfalseでも関係なくlogtoで指定したファイルへエラーが記録されるので、設定する意味がないかもしれない

logger

  • ログに記録する内容をファイルだけでなく別の場所へ記録することができる
  • loggerに記録される内容はlogtoと同じく、uWSGIの起動時のログ、アクセスログ、エラー内容が記録される
  • log-routeがうまく働かない時はログに!!! no internal routing support, rebuild with pcre support !!!が出ていないか確認してください。
    上記の文字が出ている場合はuWSGIのインストールを見てpcre-develを入れてから再度uWSGIを起動する
オプション サンプル 内容 ドキュメント
logger logger = file:/home/uwsgi_user/api/access_log 記録される内容はlogtoと同じ
複数回設定することができ、複数回指定した場合は設定されたすべての設定先にログが記録される
req-logger req-logger = file:/home/uwsgi_user/api/access_log
logger = file:/home/uwsgi_user/api/error_log

約10Mでログローテートする場合
file:logfile=/home/uwsgi_user/api/access_log,maxsize=1000000
loggerと同時に設定する
req-loggerを設定した場合アクセスログはreq-loggerに指定したファイルへ記録され、uWSGIの起動時のログ、エラー内容についてはloggerのファイルへ記録される
少し書き方を変えるとログローテートもできる
log-route logger = internalservererror file:/tmp/errors
log-route = internalservererror (HTTP/1.\d 500)
loggerと同時に設定する
log-routeに設定された正規表現と一致するログをloggerに設定した場所へ振り分ける

公式サイトのサンプルを転機

log-routeに記載する正規表現はログファイルに記載される内容と一致する条件を書くこと

例えばログに
[2022-08-07 21:40:11,780] ERROR in app: Exception on
の様に記載されるのであれば
logger = internalservererror file:/tmp/errors
log-route = internalservererror ERROR in app

の様に記載すると一致する行はloggerに指定されたファイルへ記録され、その他のlogggerには記録されなくなる

uWSGIの起動時のログに記載される内容をlog-routeに設定すれば起動時のログだけを別ファイルに分離することもできる
log-req-route テストしていないので記載方法不明 loggerと同時に設定する
log-req-routeに設定された正規表現と一致するログをloggerに設定した場所へ振り分ける
  • ログ出力に関するオプション設定
オプション サンプル 内容 ドキュメント
disable-logging disable-logging = true logtoのログ出力に関するオプション設定でも書いたのと同様のオプション
loggerと同時に設定した場合はreq-loggerを設定していてもアクセスログを記録することができなくなり、loggerで指定したファイルにもreq-loggerで設定したファイルにもアクセスログが記録されなくなる
disable-logging
log-4xx log-4xx = true logtoのログ出力に関するオプション設定でも書いたのと同様のオプション
disable-logging = trueの場合でも400エラーをloggerに指定したファイルへ記録する
req-loggerを指定している場合はreq-loggerに指定したファイルへ記録する
未設定の場合ログには400エラーは記録されない
log-5xx log-5xx = true logtoのログ出力に関するオプション設定でも書いたのと同様のオプション
trueでも、falseでも関係なくloggerに指定したファイルへエラー内容が記録される

ログローテート

  • logto, logto2, daemonize, daemonize2のファイルが対象
  • uWSGIでログローテートをさせる場合現在のところファイルサイズを基準にログローテートをさせることしか出来ない。
    日毎のログローテートは非対応。
オプション サンプル 内容 ドキュメント
log-reopen log-reopen = true uWSGIが再起動した時にログを再読込する log-reopen
log-maxsize log-maxsize=2000000 ログファイルがここで定義されたサイズ(バイト)以上になった場合ログをログローテートする log-maxsize
logfile-chown logfile-chown
引数なし
uid, gidで指定したユーザーとグループでファイルを作成する
こちらもlogfile-chmodと同じくdaemonize用?
log-maxsize
logfile-chmod logfile-chmod = 777 ログのアクセス権を変更する
設定しても反映されない
daemonizeの時に反映されるような記事がある
Nginx + uWSGI + Djangoのwebアプリケーション環境を作る (その4)
log-maxsize
touch-logreopen touch-logreopen = /home/uwsgi_user/api/uwsgi_log.touch uWSGIがログローテートを管理していない時にuWSGIに新しいログを読み込むように知らせるために利用する touch-logreopen
touch-logrotate touch-logrotate = /home/uwsgi_user/api/uwsgi_log.touch
上記のファイルを更新するとログローテートされる
uWSGIがログローテートを管理している時にuWSGIに強制的にログローテートを実行させる touch-logrotate

ロガーのフォーマット

[uwsgi]
log-format = [pid: %(pid)|app: -|req: -/-] %(addr) (%(user)) {%(vars) vars in %(pktsize) bytes} [%(ctime)] %(method) %(uri) => generated %(rsize) bytes in %(msecs) msecs (%(proto) %(status)) %(headers) headers in %(hsize) bytes (%(switches) switches on core %(core))
log-format = %(addr) - %(user) [%(ltime)] "%(method) %(uri) %(proto)" %(status) %(size) "%(referer)" "%(uagent)"

CentOS7 + apache + uwsgi + python3 + flaskでAPIサーバを構築する その2

apache + uwsgi + flakeの負荷テストでのエラーについて

  • 参考: Apacheでリバースプロキシ、タイムアウトを上手にコントロール

  • クライアント → apache → uwsgiにアクセスを行う際にapacheからuwsgiにセッションを張る

  • uwsgiのAPIからのレスポンスがクライアントへ戻る
  • この時apacheとuwsgiのセッションは張ったままとなる
  • クライアントから⑧の2回目の接続の際にapacheが②の1回目のセッションを再利用しようとした瞬間、セッションが切断されるとエラーとなり、以下の様なエラーが/var/log/httpd/error_logに記録される
[Thu Jun 30 20:48:53.243189 2022] [proxy_http:error] [pid 3405] (20014)Internal error: [client 192.168.0.252:53838] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.243204 2022] [proxy:error] [pid 3405] [client 192.168.0.252:53838] AH00898: Error reading from remote server returned by /http

apacheの設定変更

  • /etc/httpd/conf.d/uwsgi.confの設定を以下のように変更する
  • SetEnv proxy-initial-not-pooled 1を追加
  • この設定を追加するとapacheとuwsgiの間の通信を行う際にセッションを張り直すのでエラーログに出ている接続しに行った瞬間にセッションが切断される問題が回避できる
# mod_proxy_uwsgiをapacheに組み込む
LoadModule proxy_uwsgi_module /usr/lib64/httpd/modules/mod_proxy_uwsgi.so

SetEnv proxy-initial-not-pooled 1

# フォワードプロキシをOffにする
ProxyRequests Off

ProxyPass /http http://127.0.0.1:8080
ProxyPassReverse /http http://127.0.0.1:8080

ProxyPass /unix uwsgi://127.0.0.1:9090
ProxyPassReverse /unix uwsgi://127.0.0.1:9090

もう一回負荷テスト

  • httpでapache + uwsgiと連携した場合
  • 設定変更後は前回の負荷テストで出ていたNon-2xx responses: 3555がなくなった

  • /http

# ab -c 100 -n 10000 http://192.168.0.150/http
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.150 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache/2.4.6
Server Hostname:        192.168.0.151
Server Port:            80

Document Path:          /http
Document Length:        4 bytes

Concurrency Level:      100
Time taken for tests:   5.646 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2010000 bytes
HTML transferred:       40000 bytes
Requests per second:    1771.08 [#/sec] (mean)
Time per request:       56.463 [ms] (mean)
Time per request:       0.565 [ms] (mean, across all concurrent requests)
Transfer rate:          347.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       6
Processing:     5   56  14.0     52     135
Waiting:        5   56  14.0     52     135
Total:          7   56  14.0     52     135

Percentage of the requests served within a certain time (ms)
  50%     52
  66%     57
  75%     62
  80%     66
  90%     76
  95%     85
  98%     96
  99%    103
 100%    135 (longest request)
  • socketでapache + uwsgiを連携した場合
  • 前回よりも多少遅くなった以外は問題なし
  • /unix
# ab -c 100 -n 10000 http://192.168.0.150/unix
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.151 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache/2.4.6
Server Hostname:        192.168.0.150
Server Port:            80

Document Path:          /unix
Document Length:        4 bytes

Concurrency Level:      100
Time taken for tests:   5.927 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2010000 bytes
HTML transferred:       40000 bytes
Requests per second:    1687.17 [#/sec] (mean)
Time per request:       59.271 [ms] (mean)
Time per request:       0.593 [ms] (mean, across all concurrent requests)
Transfer rate:          331.17 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      10
Processing:     5   59  17.5     54     170
Waiting:        4   54  16.1     50     160
Total:          7   59  17.6     54     171

Percentage of the requests served within a certain time (ms)
  50%     54
  66%     60
  75%     65
  80%     70
  90%     84
  95%     95
  98%    110
  99%    118
 100%    171 (longest request)

結論

  • socketでapche + uwsgiの連携を行うとエラーが出ない、またはhttpで連携する場合は以下の設定を入れる
  • 以下のエラーが出た場合はapacheの設定にSetEnv proxy-initial-not-pooled 1を入れると回避できる
  • 多少レスポンスが落ちる
  • 負荷テストの際にuwsgiのiniファイルの設定でprocesses, threadsの設定をいじるとRequests per secondの値が結構変わったのでuwsgiのパフォーマンスチューニングを行ってみる
[Thu Jun 30 20:48:53.243189 2022] [proxy_http:error] [pid 3405] (20014)Internal error: [client 192.168.0.252:53838] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.243204 2022] [proxy:error] [pid 3405] [client 192.168.0.252:53838] AH00898: Error reading from remote server returned by /http

CentOS7 + apache + uwsgi + python3 + flaskでAPIサーバを構築する その1

APIサーバを作るための環境を構築する その1

  • dockerでやったほうが楽だと思うけれども、dockerを使えないので…。
  • apche + コンソールから起動したuwsgiと連携して動作するまでの確認を行う

構築順序

  1. OSのインストールと関連するソフトのインストール
  2. uwsgi + flaskでAPIを起動して動作テスト
  3. uwsgi + uwsgi.ini + flaskでAPIを起動して動作テスト
  4. apache + uwsgi(http) + uwsgi.ini + flaskでAPIを起動して動作テスト
  5. apache + uwsgi(socket) + uwsgi.ini + flaskでAPIを起動して動作テスト
    • httpで連携するより多少パフォーマンスが良いらしい?

構築する環境

  • CentOS Linux release 7.9.2009 (Core)
  • apach(2.4.6-97)
  • python(3.6.8)
  • uwsgi(2.0.20)
  • Flask(2.0.3)
  • mod_proxy_uwsgi(2.0.18)

CentOS7をインストール

  • テストで利用するユーザーはwww_user
  • IPアドレス 192.168.0.150/24
  • GW 192.168.0.1
  • DNS 192.168.0.1
  • SELinuxはdisabled
  • yum install httpdapacheをインストールと自動起動の設定
  • 検証なのでfirewalldもストップ
    • firewalldを使いたいときは以下のコマンドで(永続化するときは--permanentをつける)
      • firewall-cmd --add-port=使うポート番号 --zone=public --permanent
      • firewall-cmd --reload ← 永続化したときはリロード必須

uwsgi + flaskの連携

python3のインストール

  • python3.6をepelからインストール
  • ついでにpipのバージョンアップ
# yum install -y epel-release
# yum install -y python36
# python3 -m pip install --upgrade pip

uWSGIのインストール

  • uWSGIのインストールには以下のパッケージが必要
    • gcc, python36-devel
# yum install -y gcc python36-devel
# python3 -m pip install uWSGI flask
# python3 -m pip list

flaskでサンプルのAPIを作成

  • /, /ok, /ng, /raiseの4個のエンドポイントがある

  • api.py

from flask import Flask, jsonify

app = Flask(__name__)


@app.errorhandler(Exception)
def error_handler(e):
    return jsonify(e.args), 500


@app.route("/")
def root():
    return "root", 200


@app.route("/ok")
def ok():
    return "OK", 200


@app.route("/ng")
def ng():
    return "ng", 400


@app.route("/raise")
def raise_error():
    raise Exception("RAISE ERROR")


if __name__ == "__main__":
    app.run()

uwsgi + flaskでapiのテスト

uwsgiでコマンドラインからapiを起動

  • 以下のコマンドでapiを起動した後ブラウザからアクセスして問題がないことを確認する
  • http;//192.168.0.150:8080/
$ uwsgi --http=192.168.0.150:8080 --wsgi-file=api.py --callable=app

コマンド省略用のuwsgi.iniファイルを作成してuwsgi + flask apiの動作確認を行う

uwsgi.iniファイルの作成

[uwsgi]
########################
# 後で参照するように設定する
########################
base_dir = /home/www_user/api_test
app_name = api

########################
# uwsgiの設定
########################
# APIを起動する前にカレントディレクトリを変更
chdir = %(base_dir)

# 起動するIPアドレスとポート番号
http = 192.168.0.150:8080

# 起動するファイル
wsgi-file = api.py

# 起動するオブジェクト app = Flask(__name__)のappの事
callable = app

# 起動するグループID
gid = www_user

# 起動するユーザーID
uid = www_user

# 起動するプロセス数
# processes = 1 は workers = 1と同じ意味
processes = 1

# マルチスレットで動作させ、スレッドとして15スレッド起動する
threads = 15

# 以下に指定したファイルを更新するとAPIがリロードされる
touch-reload = %(base_dir)/api_touch_reload

# タイムアウトまでの時間(秒)
harakiri = 60

# 基本的に有効とする
# 有効にするとうまいことアプリケーションのリロードを行ってくれる
master = true

########################
# ログ関係
########################
# access_logの設定
req-logger = file:/var/log/uwsgi/%(app_name)/access_log

# error_logの設定
logger = file:/var/log/uwsgi/%(app_name)/error_log

uwsgi + uwsgi.ini + flaskでapiのテスト

uwsgiでコマンドラインからapiを起動

  • 以下のコマンドでapiを起動した後ブラウザからアクセスして問題がないことを確認する
  • ログはuwsgi.iniで指定した/var/log/uwsgi/api/access_logと/var/log/uwsgi/api/error_logに出力される
  • http;//192.168.0.150:8080/
$ uwsgi uwsgi.ini

apacheとuwsgiの連携

apacheのインストールと必要なモジュールの確認

  • apacheとuwsgiの連携にはいくつかLoadModuleで必要なモジュールがapacheに組み込まれている必要がある
    • apacheとuwsgiをhttpで連携する場合
      • proxy_http_moduleがapacheに組み込まれている必要がある
    • apacheとuwsgiをunix socketで連携する場合
      • mod_proxy_uwsgiがapacheに組み込まれている必要がある
# yum install httpd
# systemctl start httpd
# systemctl enable httpd
# http -M | grep proxy_http_module  ← proxy_http_moduleが組み込まれているか確認
 proxy_http_module (shared)    ← この表示があれば問題なし(なければ次の項目で組み込みを行う)

apacheとuwsgiをhttpで連携

  • /etc/httpd/conf.d/uwsgi.confファイルを新規で作成する
# apacheにproxy_http_moduleを組み込むときは行頭の#を外す
# LoadModule proxy_http_module modules/mod_proxy_http.so

# mod_proxy_uwsgiをapacheに組み込む
LoadModule proxy_uwsgi_module /usr/lib64/httpd/modules/mod_proxy_uwsgi.so

# フォワードプロキシをOffにする
ProxyRequests Off

# /apiに来たリクエストをhttp://192.168.0.150:8080へ転送する
ProxyPass /api http://192.168.0.150:8080
ProxyPassReverse /api http://192.168.0.150:8080
# systemctl reload httpd
# systemctl status httpd

uwsgi + uwsgi.ini + flaskでapiのテスト

  • 以下のコマンドでapiを起動した後ブラウザからアクセスして問題がないことを確認する
  • ログはuwsgi.iniで指定した/var/log/uwsgi/api/access_logと/var/log/uwsgi/api/error_logに出力される
  • http;//192.168.0.150/ ← 先程までと違いport番号が不要
$ uwsgi uwsgi.ini

apacheとuwsgiをsocketで連携

  • uwsgi.iniの以下のところを書き換える
# 起動するIPアドレスとポート番号
# http = 192.168.0.150:8080  ← コメントアウトする

# 以下を追記
socket = 127.0.0.1:8080
  • uwsgi.confの以下のところを以下のように書き換える
# apacheにproxy_http_moduleを組み込むときは行頭の#を外す
# LoadModule proxy_http_module modules/mod_proxy_http.so

# mod_proxy_uwsgiをapacheに組み込む
LoadModule proxy_uwsgi_module /usr/lib64/httpd/modules/mod_proxy_uwsgi.so

# フォワードプロキシをOffにする
ProxyRequests Off

# /apiに来たリクエストをhttp://192.168.0.150:8080へ転送する
ProxyPass /api uwsgi://192.168.0.150:8080
ProxyPassReverse /api uwsgi://192.168.0.150:8080
  • apacheをリロードして設定を再読み込み
# systemctl reload httpd

uwsgi + uwsgi.ini + flaskでapiのテスト

  • 以下のコマンドでapiを起動した後ブラウザからアクセスして問題がないことを確認する
  • ログはuwsgi.iniで指定した/var/log/uwsgi/api/access_logと/var/log/uwsgi/api/error_logに出力される
  • http;//192.168.0.150/ ← httpでの連携と同じくport番号が不要
$ uwsgi uwsgi.ini

httpとsocketのどっちを使った方がいい?(番外編)

  • どっちのほうがレスポンスが良いのか負荷をかけて検証してみる
    • 環境はHyper-V上に構築したCentOS7 + プロセッサ4 + メモリ 1G
  • 利用するuwsgi.iniは以下の内容(上の方に書いたものと殆ど同じ)
    • uwsgi_http.iniとuwsgi_socket.iniとして作成する

uwsgi_http.ini

[uwsgi]
########################
# 後で参照するように設定する
########################
base_dir = /home/www_user/api_test
app_name = api


########################
# uwsgiの設定
########################
# APIを起動する前にカレントディレクトリを変更
chdir = %(base_dir)

# httpで起動するIPアドレスとポート番号
http = 127.0.0.1:8080

# 起動するファイル
wsgi-file = api.py

# 起動するオブジェクト app = Flask(__name__)のappの事
callable = app

# 起動するグループID
gid = www_user

# 起動するユーザーID
uid = www_user

# 起動するプロセス数
# processes = 1 は workers = 1と同じ意味
processes = 1

# マルチスレッドで動作させる
threads = 15

# 以下に指定したファイルを更新するとAPIがリロードされる
touch-reload = %(base_dir)/api_touch_reload

# タイムアウトまでの時間(秒)
harakiri = 60

# 基本的に有効とする
# 有効にするとうまいことアプリケーションのリロードを行ってくれる
master = true

########################
# ログ関係
########################
# access_logの設定
req-logger = file:/var/log/uwsgi/%(app_name)/http_access_log

# error_logの設定
logger = file:/var/log/uwsgi/%(app_name)/http_error_log

uwsgi_socket.ini

[uwsgi]
########################
# 後で参照するように設定する
########################
base_dir = /home/www_user/api_test
app_name = api


########################
# uwsgiの設定
########################
# APIを起動する前にカレントディレクトリを変更
chdir = %(base_dir)

# socketで起動するIPアドレスとポート番号 
socket = 127.0.0.1:9090

# 起動するファイル
wsgi-file = api.py

# 起動するオブジェクト app = Flask(__name__)のappの事
callable = app

# 起動するグループID
gid = www_user

# 起動するユーザーID
uid = www_user

# 起動するプロセス数
# processes = 1 は workers = 1と同じ意味
processes = 1

# マルチスレッドで動作させる
threads = 15

# 以下に指定したファイルを更新するとAPIがリロードされる
touch-reload = %(base_dir)/api_touch_reload

# タイムアウトまでの時間(秒)
harakiri = 60

# 基本的に有効とする
# 有効にするとうまいことアプリケーションのリロードを行ってくれる
master = true

########################
# ログ関係
########################
# access_logの設定
req-logger = file:/var/log/uwsgi/%(app_name)/socket_access_log

# error_logの設定
logger = file:/var/log/uwsgi/%(app_name)/socket_error_log
# mod_proxy_uwsgiをapacheに組み込む
LoadModule proxy_uwsgi_module /usr/lib64/httpd/modules/mod_proxy_uwsgi.so

# フォワードプロキシをOffにする
ProxyRequests Off

ProxyPass /http http://127.0.0.1:8080
ProxyPassReverse /http http://127.0.0.1:8080

ProxyPass /unix uwsgi://127.0.0.1:9090
ProxyPassReverse /unix uwsgi://127.0.0.1:9090
  • uwsgiコマンドでapiを2個起動する
$ uwsgi uwsgi_http.ini & uwsgi uwsgi_socket.ini
  • APIが正常に起動しているか確認する
$ curl http://192.168.0.150/http
$ curl http://192.168.0.150/unix

負荷をかけてみる

  • httpでの連携からテスト
# ab -c 100 -n 10000 http://192.168.0.150/http
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.150 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache/2.4.6
Server Hostname:        192.168.0.150
Server Port:            80

Document Path:          /http
Document Length:        4 bytes

Concurrency Level:      100
Time taken for tests:   4.112 seconds
Complete requests:      10000
Failed requests:        3555
   (Connect: 0, Receive: 0, Length: 3555, Exceptions: 0)
Non-2xx responses:      3555
Total transferred:      3314685 bytes
HTML transferred:       1401565 bytes
Requests per second:    2431.91 [#/sec] (mean)
Time per request:       41.120 [ms] (mean)
Time per request:       0.411 [ms] (mean, across all concurrent requests)
Transfer rate:          787.21 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       5
Processing:     2   40  11.3     37      94
Waiting:        2   40  11.3     37      94
Total:          5   41  11.3     37      94

Percentage of the requests served within a certain time (ms)
  50%     37
  66%     42
  75%     47
  80%     50
  90%     59
  95%     65
  98%     70
  99%     73
 100%     94 (longest request)
  • socketでの連携をテスト
# ab -c 100 -n 10000 http://192.168.0.150/unix
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.150 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache/2.4.6
Server Hostname:        192.168.0.150
Server Port:            80

Document Path:          /unix
Document Length:        4 bytes

Concurrency Level:      100
Time taken for tests:   5.569 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2010000 bytes
HTML transferred:       40000 bytes
Requests per second:    1795.74 [#/sec] (mean)
Time per request:       55.687 [ms] (mean)
Time per request:       0.557 [ms] (mean, across all concurrent requests)
Transfer rate:          352.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       5
Processing:     4   55  14.8     51     126
Waiting:        2   53  14.5     48     118
Total:          5   55  14.9     51     126

Percentage of the requests served within a certain time (ms)
  50%     51
  66%     57
  75%     62
  80%     65
  90%     75
  95%     88
  98%    100
  99%    106
 100%    126 (longest request)

負荷テストの結果

  • httpでapache + uwsgiで連携した場合はNon-2xx responsesのエラーが出る

  • /http

Concurrency Level:      100
Time taken for tests:   4.112 seconds
Complete requests:      10000
Failed requests:        3555
   (Connect: 0, Receive: 0, Length: 3555, Exceptions: 0)
Non-2xx responses:      3555
Total transferred:      3314685 bytes
HTML transferred:       1401565 bytes
Requests per second:    2431.91 [#/sec] (mean)
Time per request:       41.120 [ms] (mean)
Time per request:       0.411 [ms] (mean, across all concurrent requests)
Transfer rate:          787.21 [Kbytes/sec] received
Concurrency Level:      100
Time taken for tests:   5.569 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2010000 bytes
HTML transferred:       40000 bytes
Requests per second:    1795.74 [#/sec] (mean)
Time per request:       55.687 [ms] (mean)
Time per request:       0.557 [ms] (mean, across all concurrent requests)
Transfer rate:          352.48 [Kbytes/sec] received

負荷テストのエラーの理由

[Thu Jun 30 20:48:53.238914 2022] [proxy_http:error] [pid 3434] (20014)Internal error: [client 192.168.0.252:53796] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.238937 2022] [proxy:error] [pid 3434] [client 192.168.0.252:53796] AH00898: Error reading from remote server returned by /http
[Thu Jun 30 20:48:53.240173 2022] [proxy_http:error] [pid 3421] (20014)Internal error: [client 192.168.0.252:53828] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.240193 2022] [proxy:error] [pid 3421] [client 192.168.0.252:53828] AH00898: Error reading from remote server returned by /http
[Thu Jun 30 20:48:53.242943 2022] [proxy_http:error] [pid 3416] (20014)Internal error: [client 192.168.0.252:53668] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.242958 2022] [proxy:error] [pid 3416] [client 192.168.0.252:53668] AH00898: Error reading from remote server returned by /http
[Thu Jun 30 20:48:53.243063 2022] [proxy_http:error] [pid 3429] (20014)Internal error: [client 192.168.0.252:53840] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.243084 2022] [proxy:error] [pid 3429] [client 192.168.0.252:53840] AH00898: Error reading from remote server returned by /http
[Thu Jun 30 20:48:53.243189 2022] [proxy_http:error] [pid 3405] (20014)Internal error: [client 192.168.0.252:53838] AH01102: error reading status line from remote server 127.0.0.1:8080
[Thu Jun 30 20:48:53.243204 2022] [proxy:error] [pid 3405] [client 192.168.0.252:53838] AH00898: Error reading from remote server returned by /http

対応方法

  • 長くなったので次へ続く

v-text-field v-slot:prependで日本語を使うと改行が入る

v-text-fieldのv-slot:prepend

v-text-fieldのv-slot:prependを使うと以下の画像のように入力フィールドの前に指定した文字を表示させることができる。
この時先頭に追加する文字が半角英数字であれば文字の途中で改行されることが無いが、日本語を使うと文字の途中で改行がされてしまうので、改行されないようにCSSでパッチを当てる。

  • 改行されないパターン

  • 改行されるパターン

CSSに以下を追加する

.v-input__prepend-outer {
    flex-shrink: 0;
}

CSS適用後

  • 無事改行がされなくなった

ソース全文

<!DOCTYPE html>
<html>

<head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <style>
        .v-input__prepend-outer {
            flex-shrink: 0;
        }
    </style>
</head>

<body>
    <div id="app">
        <v-app>
            <v-main>
                <v-container fluid>
                    <v-card>
                        <v-card-text>
                            <v-row justify="center">
                                <v-text-field>
                                    <template v-slot:prepend>
                                        name
                                    </template>
                                </v-text-field>
                            </v-row>
                            <v-row>
                                <v-text-field>
                                    <template v-slot:prepend>
                                        address
                                    </template>
                                </v-text-field>
                            </v-row>
                        </v-card-text>
                    </v-card>
                </v-container>
            </v-main>
        </v-app>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

    <script>
        const app = new Vue({
            el: '#app',
            vuetify: new Vuetify(),
            data: {
            },
            created() {
            },
            methods: {
            },
        })
    </script>
</body>

</html>

dockerのCentOS7でtelnetdを使えるようにする

dockerでCentOS7 +telnetdを起動してホストから接続する

  • 検証用にdockerで起動したCentOS7にホストからtelnetdで接続したいので作った
  • 検証用なのでコンテナイメージをなるべく小さくするなどの対処は行わない

事前準備

  • CentOS7のdockerイメージそのままではtelnetdは使えないのでDockerfileを作ってイメージを作成する
  • 任意の場所に以下のDockerfileを作る
  • 作成するコンテナイメージでtelnetで接続するユーザー
    • ID: root, password: rootとする
    • ID: user, password: userとする
# syntax=docker/dockerfile:1-labs
FROM centos:7

RUN yum update -y
RUN yum install -y xinetd
RUN yum install -y telnet-server

# rootで同時に接続したいユーサー数の数だけ書く
# 一般ユーザーの場合は記載する必要なし
RUN echo "pts/0" >> /etc/securetty
RUN echo "pts/1" >> /etc/securetty
RUN echo "pts/2" >> /etc/securetty

RUN echo "root:root" | chpasswd

RUN adduser user
RUN echo "user:user" | chpasswd

COPY <<EOF /etc/xinetd.d/telnet
service telnet
{
    flags = REUSE
    socket_type = stream
    wait = no
    user = root
    server = /usr/sbin/in.telnetd
    log_on_failure += USERID
    disable = no
}
EOF

COPY <<EOF /docker-entrypoint.sh
#!/bin/bash
xinetd -dontfork -stayalive
EOF

RUN chmod 777 docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

※ Dockerfile中にヒアドキュメントがつかえるようになっていたので使ってみた
ヒアドキュメントを使うとホスト側にファイルを用意してdocker内へファイルをコピーするという手間が減るので便利

telnetdが使える様にしたイメージの作成

  • 以下のコマンドでssh-serverの名称でイメージを作成する
  • 作成するコンテナイメージの名前はcentos7-sshd:latest
# ヒアドキュメントを使う場合はdocker buildではなくて、docker buildxを利用する
# コンテナをビルドしているときの表示も少し変わる
$ docker buildx build --tag centos-telnetd .

$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
centos-telnetd   latest    fd4dfbbb65ad   27 minutes ago   796MB

centos-telnetdのイメージを起動する

  • 起動するサーバのホスト名: telned
  • 起動するサーバのコンテナ名: telnet-server
  • 起動するサーバのポート番号: 10023
$ docker run -it --hostname telentd --name telnet-server -p 10023:23/tcp -d centos-telnetd

# 以下のように--rmをつけてイメージを起動するとコンテナが停止した時に自動的にコンテナが削除される
# $ docker run --rm -it --hostname telentd --name telnet-server -p 10023:23/tcp -d centos-telnetd


$ $ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                                     NAMES
a663e68cffa3   centos-telnetd   "/docker-entrypoint.…"   3 seconds ago   Up 2 seconds   0.0.0.0:10023->23/tcp, :::10023->23/tcp   telnet-server

起動したtelnet-serverにtelnetでホストから接続する

$ telnet localhost 10023
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

Kernel 5.4.0-109-generic on an x86_64
telentd login: root
Password: 
[root@telentd ~]# 

telnet-serverを停止してイメージを削除

$ docker ps -a    # 停止しているコンテナも含めて表示する
CONTAINER ID   IMAGE            COMMAND                  CREATED              STATUS              PORTS                                     NAMES
a663e68cffa3   centos-telnetd   "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:10023->23/tcp, :::10023->23/tcp   telnet-server

$ docker stop telnet-server
$ docker rm a663e68cffa3    # docker rm telnet-serverでも可

$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
centos-telnetd   latest    fd4dfbbb65ad   33 minutes ago   796MB

$ docker rmi fd4dfbbb65ad    # docker rmi centos7-telnetd:latestでも可

$ docker ps -a    # 停止しているコンテナも含めて表示する
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

$ docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

dockerのCentOS7でsshdを使えるようにする

dockerでCentOS7 + sshdを起動してホストから接続する

  • 検証用にdockerで起動したCentOS7にホストからsshdで接続したいので作った
  • 検証用なのでコンテナイメージをなるべく小さくするなどの対処は行わない

事前準備

  • CentOS7のdockerイメージそのままではsshdは使えないのでDockerfileを作ってイメージを作成する
  • 任意の場所に以下のDockerfileを作る
  • 作成するコンテナイメージでsshで接続するユーザー
    • ID: root, password: rootとする
    • ID: user, password: userとする
FROM centos:7

RUN yum update -y
RUN yum install -y openssh-server

RUN ssh-keygen -A
RUN echo "root:root" | chpasswd

RUN adduser user
RUN echo "user:user" | chpasswd

CMD ["/usr/sbin/sshd", "-D"]

はてなブログはDockerfileはシンタックスハイライトが効かない

sshdが使える様にしたイメージの作成

  • 以下のコマンドでssh-serverの名称でイメージを作成する
  • 作成するコンテナイメージの名前はcentos7-sshd:latest
$ docker build --tag centos7-sshd .

$ docker images
REPOSITORY     TAG       IMAGE ID       CREATED              SIZE
centos7-sshd   latest    4b6b1ef383b0   33 seconds ago       673MB
<none>         <none>    9d2878d51761   About a minute ago   885MB
centos         7         eeb6ee3f44bd   7 months ago         204MB

sshd-serverのイメージを起動する

  • 起動するサーバのホスト名: server
  • 起動するサーバのコンテナ名: sshd-server
  • 起動するサーバのポート番号: 10022
$ docker run -it --hostname server --name sshd-server -p 10022:22/tcp -d centos7-sshd

$ docker ps
CONTAINER ID   IMAGE          COMMAND               CREATED         STATUS         PORTS                                     NAMES
d94cb10cd66f   centos7-sshd   "/usr/sbin/sshd -D"   8 seconds ago   Up 7 seconds   0.0.0.0:10022->22/tcp, :::10022->22/tcp   sshd-server

起動したsshd-serverにsshでホストから接続する

$ ssh -p 10022 root@localhost
The authenticity of host '[localhost]:10022 ([127.0.0.1]:10022)' can't be established.
ECDSA key fingerprint is SHA256:Z3I2gQ3w7wo1MNJ/8fuTBE1Wnd169RR0qgZ5IyhBHpk.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:10022' (ECDSA) to the list of known hosts.
root@localhost's password: 
[root@server ~]#

sshで接続した時にエラーが出た場合

$ ssh -p 10022 root@localhost
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:/W9DGeoHg56m1FvGj8UWp9SBJ+ZNcRVhDdVZDI52XaA.
Please contact your system administrator.
Add correct host key in /home/username/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/username/.ssh/known_hosts:1
  remove with:
  ssh-keygen -f "/home/username/.ssh/known_hosts" -R "[localhost]:10022"
ECDSA host key for [localhost]:10022 has changed and you have requested strict checking.
Host key verification failed.
  • エラーが出た場合~/.ssh/known_hostsに書かれている接続先のホストの公開鍵と現在の接続先のホストの公開鍵が一致しない為エラーが出ている(中間者攻撃対策のため)
    以下のコマンドを使って古いホストの情報を削除して、もう一度接続する
$ ssh-keygen -R [localhost]:10022
# Host [localhost]:10022 found: line 1
/home/username/.ssh/known_hosts updated.

$ ssh -p 10022 root@localhost
The authenticity of host '[localhost]:10022 ([127.0.0.1]:10022)' can't be established.
ECDSA key fingerprint is SHA256:/W9DGeoHg56m1FvGj8UWp9SBJ+ZNcRVhDdVZDI52XaA.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:10022' (ECDSA) to the list of known hosts.
root@localhost's password: 
[root@server ~]#

sshd-serverを停止してイメージを削除

$ docker ps -a    # 停止しているコンテナも含めて表示する
CONTAINER ID   IMAGE          COMMAND               CREATED          STATUS                      PORTS     NAMES
3ca739888943   centos7-sshd   "/usr/sbin/sshd -D"   21 seconds ago   Exited (0) 12 seconds ago             sshd-server

$ docker stop sshd-server
$ docker rm 3ca739888943    # docker rm sshd-serverでも可

$ docker images
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
centos7-sshd   latest    5c352901945b   4 seconds ago   673MB
centos         7         eeb6ee3f44bd   7 months ago    204MB

$ docker rmi 5c352901945b    # docker rmi centos7-sshd:latestでも可
$ docker rmi eeb6ee3f44bd    # docker rmi centos:7でも可

$ docker ps -a    # 停止しているコンテナも含めて表示する
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

$ docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE