- 投稿日:2019-02-18T23:26:50+09:00
docker hub とQiita
Qiitaにソースコードの記事を書いたらその作業をdocker hubに保存するようにしている。
当初は1記事1 docker hubにしようと思った。
類似の環境はまとめるようにしている。文書履歴(document history)
ver. 0.01 初稿 20190218
ver. 0.02 URL3つ追記 20190219 午前7時
ver. 0.03 https://hub.docker.com/r/kaizenjapan に変更 20190219 午前8時
ver. 0.04 URL3つ追記 20190219 午前9時
- 投稿日:2019-02-18T23:21:47+09:00
Debian 10(buster) + ufw + Docker: Dockerコンテナが外部と通信できない問題の対処
いきなり対処法
update-alternatives --config iptables
で,iptables-legacy
を使うよう変更する.$ sudo update-alternatives --config iptables alternative iptables (/usr/sbin/iptables を提供) には 2 個の選択肢があります。 選択肢 パス 優先度 状態 ------------------------------------------------------------ * 0 /usr/sbin/iptables-nft 20 自動モード 1 /usr/sbin/iptables-legacy 10 手動モード 2 /usr/sbin/iptables-nft 20 手動モード 現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してください: 1 update-alternatives: /usr/sbin/iptables (iptables) を提供するためにマニュアルモードで /usr/sbin/iptables-legacy を使います $ sudo rebootいきなり結論
- Debian 10はiptables v1.8系を採用.nftables APIを使う
iptables-nft
と旧来のiptables-legacy
が用意されている.- デフォルトは
iptables-nft
だが,Dockerはiptables-legacy
を使う.- この状態で,
ufw
などを使ってiptables-nft
で定義を設定すると,iptables-legacy
の定義は無視される(ようだ).その結果,外部との通信ができない.- そこで,
iptables-legacy
をデフォルトに変更し,定義をlegacy側に寄せて対処する.詳細
ufwとiptables
ufw - Uncomplicated Firewall は,
ufw allow 22/tcp
などと書くだけでポート開放ができる,お手軽なファイアウォール管理ツールです.内部的には
iptables
(Netfilter) のラッパーのような位置づけで,iptables
の忘れやすいコマンド体系を忘れたままにしておけます.もともとUbuntu向けのツールだったように思いますが,かなり前からDebianでも利用が可能です.iptables v1.8 - nftables APIサポート
現時点で testing である Debian GNU/Linux 10 (buster) では,iptablesが1.6から1.8にバージョンアップされています.
iptables 1.8では,新たに
nftables
Kernel API1を用いたiptables-nft
ツールが提供され,旧来のツールはiptables-legacy
で使用することが出来るようになっています.
iptables
はというと,Debian 10はデフォルトでiptables-nft
へのシンボリックリンクが張られています.ufw
等はこのデフォルトを使用します.$ ls -l /usr/sbin/iptables lrwxrwxrwx 1 root root 26 2月 17 11:44 /usr/sbin/iptables -> /etc/alternatives/iptables $ ls -l /etc/alternatives/iptables lrwxrwxrwx 1 root root 22 2月 18 22:53 /etc/alternatives/iptables -> /usr/sbin/iptables-nftDockerは
iptables-legacy
を使う一方,Dockerは
iptables-nft
がうまく扱えず,デフォルトがどうあれ必ずiptables-legacy
を使うように修正が加えられています2 3.
- Docker doesn't work with iptables v1.8.1 · Issue #38099 · moby/moby
- debian has iptables-legacy and iptables-nft now by myobie · Pull Request #2285 · docker/libnetwork
ufw + Docker = iptables-nft + iptables-legacy
ここで,デフォルトのまま
ufw
とDockerを使うと,iptables-nft
とiptables-legacy
の両方の定義が存在することになります.$ sudo iptables-nft -L Chain INPUT (policy DROP) target prot opt source destination ufw-before-logging-input all -- anywhere anywhere ufw-before-input all -- anywhere anywhere ufw-after-input all -- anywhere anywhere ufw-after-logging-input all -- anywhere anywhere ufw-reject-input all -- anywhere anywhere ufw-track-input all -- anywhere anywhere (省略) $ sudo iptables-legacy -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy DROP) target prot opt source destination DOCKER-USER all -- anywhere anywhere DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED DOCKER all -- anywhere anywhere (省略)ここから先は推測が入りますが,この状態になると
iptables-nft
の定義のみが有効となるようです.そのため,Dockerがせっかく書き換えてくれた定義が効かず,コンテナが外部と通信できなくなってしまいます.
ping
でIPアドレスを指定すると通るが,ホスト名を指定するとDNSが引けない,という状況です.$ sudo docker run -it --rm busybox / # ping github.com ping: bad address 'github.com' / # ping 192.30.255.112 PING 192.30.255.112 (192.30.255.112): 56 data bytes 64 bytes from 192.30.255.112: seq=0 ttl=49 time=133.961 ms 64 bytes from 192.30.255.112: seq=1 ttl=49 time=134.677 ms対処:
iptables-legacy
をデフォルトにするさて,対処法ですが,
ufw
も含めてみんなiptables-legacy
を使うようにします.Docker doesn't work with iptables v1.8.1 · Issue #38099 · moby/mobyのIssue commentにもあるように,デフォルトの
iptables
は選べるようになっています.そこで,iptables-legacy
をデフォルトに変更します.$ sudo update-alternatives --config iptables alternative iptables (/usr/sbin/iptables を提供) には 2 個の選択肢があります。 選択肢 パス 優先度 状態 ------------------------------------------------------------ * 0 /usr/sbin/iptables-nft 20 自動モード 1 /usr/sbin/iptables-legacy 10 手動モード 2 /usr/sbin/iptables-nft 20 手動モード 現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してください: 1 update-alternatives: /usr/sbin/iptables (iptables) を提供するためにマニュアルモードで /usr/sbin/iptables-legacy を使いますあとは再起動すれば完了です.
iptables-nft
での定義が残っていても,iptables-legacy
をデフォルトとしていると,legacyが優先されるようです.参考
- Debian -- buster の iptables パッケージに関する詳細
- iptables 1.8.0 release [LWN.net]
- Docker doesn't work with iptables v1.8.1 · Issue #38099 · moby/moby
- debian has iptables-legacy and iptables-nft now by myobie · Pull Request #2285 · docker/libnetwork
API自体は2014年1月にリリースされたLinux Kernel 3.13から提供されている ↩
本来
iptables-nft
でも同じコマンドで通るはずなのですが,謎.Pull requestには『トランスレータのバグかもね? 追っかけきれてないけど?』みたいなコメントが見られる. ↩nftablesサポートについてはかなり前からIssueが上がっている様子([feature request] nftables support · Issue #26824 · moby/moby). ↩
- 投稿日:2019-02-18T19:19:34+09:00
DockerでWebpack+Babel開発環境作ってみた
はじめに
ES6以上のJSをES5にトランスパイルするのに便利なBabelの環境を作ってみたいと思います。
vue.jsやReact.jsと組み合わせたwebpackやbabel環境構築の記事はよく見かけますが、今回はdockerでwebpack+babelのみの開発環境構築をご紹介します。環境
以下の環境で動作確認はしております。
MacOS Mojave 10.14.3
Docker version 18.09.2
docker-compose version 1.23.2, build 1110ad01構成
dist/にbundle.jsを作成することをゴールとします。
├── dist │ └── bundle.js ├── docker │ └── webpack │ ├── Dockerfile │ ├── package.json │ └── webpack.config.js ├── docker-compose.yml └── src ├── app.js └── test.js構築
1. docker-compose.yml作成
今回は、bundle.jsを作成するためだけのコンテナを作成します。
docker-compose.ymlversion: '3' services: webpack: build: ./docker/webpack volumes: - ./src:/my_webpack/src - ./dist:/my_webpack/dist - ./docker/webpack/package.json:/my_webpack/package.json - ./docker/webpack/webpack.config.js:/my_webpack/webpack.config.js ports: - 2000:20002. Dockerfile作成
DockerfileFROM node:8.15.0 WORKDIR /my_webpack RUN npm init -y RUN npm install -D webpack webpack-cli babel-loader @babel/core @babel/preset-env RUN npm install jquery CMD ["npm", "run", "build"]3. package.json作成
ファイルの変更を検知して、再ビルドしてもらうために --watch オプションをつけてます。
あと、今回はjQueryもちゃっかり入れてます。不要な人は、削除しても構いません。package.json{ "scripts": { "build": "webpack --watch" }, "dependencies": { "jquery": "^3.3.1" }, "devDependencies": { "@babel/core": "7.0.0", "@babel/preset-env": "7.0.0", "babel-loader": "^8.0.0", "webpack": "^4.17.1", "webpack-cli": "^3.1.0" }, "private": true }4. webpack.config.js
ターゲットのブラウザも指定できるので、ここで指定しちゃいます。
エントリポイントはsrc/直下に配置するapp.js、dist/以下にbundle.jsとして出力するように設定しています。webpack.config.jsconst presets = [ [ '@babel/preset-env', { "targets": [">0.25% in JP", "not ie <= 10", "not op_mini all"] } ] ]; module.exports = { mode : 'development', devtool: 'inline-source-map', entry : './src/app.js', output : { path : `${__dirname}/dist`, filename: 'bundle.js' }, module : { rules: [ { test: /\.js$/, use : [ { loader : 'babel-loader', options: { presets: presets } } ] } ] } };5. ES6のサンプル用意
トランスパイル用のES6で書かれたサンプルを用意します。
app.jsimport Test from './test'; Test.viewConsole();test.jsexport default class Test { static viewConsole () { ['cat', 'dog', 'bear'].forEach(item => { console.log(item); }); } }結果
docker-composeで起動
$ docker-compose up -ddist直下にbundle.jsが作成されて入れば成功です。
また、app.jsやtest.jsを編集してみて下さい。
それに応じて、bundle.jsも再ビルドされていることでしょう。
- 投稿日:2019-02-18T16:16:58+09:00
NGINX Unit+Django(WSGI) Dockerコンテナ構築
DjangoをNGINX/Unitで公開するためのDockerコンテナを作成します。
内容
- 前記事で
Windows10+WSL+Docker
環境を構築- NGINX/Unit+Djangoのコンテナを構築
カスタマイズ
NGINX/Unitとは?
公式サイトに記載の通り、(誤解を恐れず表現すると)NGINXの動的アプリサーバ版です。
WSGIに沿ってPythonスクリプトを記述することで、動的アプリを公開できます。
- WSGIに準拠したスクリプトを直接起動できる (中間モジュールを必要としない)
- 設定更新を動的に実施できる(ソケットに流し込む)
Djangoとは?
DjangoはPythonの代表的フルスタックフレームワークです。
用意するもの
名称 内容 Docker Dokerfile Dockerイメージのレシピ unit.conf.json NGINX Unitの設定 entrypoint.sh コンテナ起動スクリプト requirements.pip pip freeze
の出力.docerignore 参考サイト コンテンツ Pythonスクリプト Djangoで作成 src/
├── Dockerfile
├── .dockerignore
├── unit.conf.json
├── entrypoint.sh
├── requirements.pip
└── xxx/ (Django)
├── xxx
| ├── wsgi.py
| └── etc...
└── polls※DjangoははじめてのDjangoあたりを参考に学習してください。
※ディレクトリツリーキレイに書きたいDokerfile
公式イメージのDockerfileを参考にしつつ作成します。
公式はDebianでPythonバージョンも古いため、UbuntuイメージにNGINX/Unitを導入する形にします。
ENVにユーザーとパスワードを記述している点が気になる方は、適宜ARGに置き換えてください。FROM ubuntu:cosmic ENV UBUNTU_VER cosmic ENV PY_VER 3.6 SHELL ["/bin/bash", "-c"] ENV DEBCONF_NOWARNINGS yes LABEL MAINTAINER="auther" LABEL version="1.0" LABEL description="" ENV CONTENTS ./project ENV EXPOSE_PORT 8000 ENV USER api ENV PASSWORD pass ENV HOME /home/${USER} ENV PROJECT_NAME xxx ENV PROJECT_DIR ${HOME}/${PROJECT_NAME} # initialize RUN : "locale setting and base package" && \ set -o pipefail && \ perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://ubuntutym.u-toyama.ac.jp/ubuntu/%g' /etc/apt/sources.list && \ apt-get -y clean && apt-get -y update && \ apt-get -qy install \ sudo \ tzdata \ ca-certificates \ gnupg \ curl && \ update-locale LANG=ja_JP.UTF-8 && \ ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \ : "create user" && \ useradd -m -s /bin/bash ${USER} && \ gpasswd -a ${USER} sudo && \ echo "${USER}:${PASSWORD}" | chpasswd && \ echo "Defaults visiblepw" >> /etc/sudoers && \ echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers # copy project WORKDIR ${HOME} COPY ${CONTENTS} ${HOME}/${PROJECT_NAME} COPY unit.conf.json ${HOME} COPY entrypoint.sh ${HOME} COPY requirements.pip ${HOME} RUN set -o pipefail && \ sed \ -e "s#%%HOME%%#$HOME#g" \ -e "s#%%PJ%%#$PROJECT_NAME#g" \ -e "s#%%PORT%%#$EXPOSE_PORT#g" \ -e "s#%%PYVER%%#$PY_VER#g" \ ${HOME}/unit.conf.json > ${HOME}/unit-conf.json && \ chown -R ${USER}:${USER} ${PROJECT_DIR} && \ chown ${USER}:${USER} ${HOME}/unit.conf.json && \ chown ${USER}:${USER} ${HOME}/entrypoint.sh && \ chown ${USER}:${USER} ${HOME}/requirements.pip && \ ln -sf /dev/stdout ${HOME}/unit.log && \ chmod 774 ${HOME}/entrypoint.sh # install Project Package WORKDIR /root RUN set -o pipefail && \ : "nginx unit repogitory" && \ curl -fsSL "https://nginx.org/keys/nginx_signing.key" | apt-key add - && \ echo "deb https://packages.nginx.org/unit/ubuntu/ ${UBUNTU_VER} unit" > /root/unit.list && \ echo "deb-src https://packages.nginx.org/unit/ubuntu/ ${UBUNTU_VER} unit" >> /root/unit.list && \ cp /root/unit.list /etc/apt/sources.list.d/unit.list && \ : "project packages" && \ apt-get -y update && apt-get -qy install \ unit \ unit-python${PY_VER} \ python3 \ python3-pip # pip USER ${USER} WORKDIR ${HOME} RUN pip3 install -r ${HOME}/requirements.pip # start unitd STOPSIGNAL SIGTERM EXPOSE ${EXPOSE_PORT} CMD ["sh", "./entrypoint.sh"]unit.conf.json
公式ドキュメントを参考にNGINX/Unitの設定を記述したjson形式のファイルを用意します。
"%%"で囲んでいる箇所はdockerfile内で置換しています。{ "listeners": { "*:%%PORT%%": { "application": "%%PJ%%" } }, "applications": { "%%PJ%%": { "type": "python %%PYVER%%", "processes": 5, "working_directory": "%%HOME%%/%%PJ%%/", "path": "%%HOME%%/%%PJ%%/", "module": "%%PJ%%.wsgi", "user": "api", "group": "api", "limits": { "timeout": 10, "requests": 1000 } } }, "settings": { "http": { "header_read_timeout": 10, "body_read_timeout": 10, "send_timeout": 10, "idle_timeout": 120, "max_body_size": 6291456 } }, "access_log": "%%HOME%%/access.log" }entrypoint.sh
コンテナ生成後に実行するシェルスクリプトです。Dockerfile末尾で実行しています。
NGINX Unitを実行する方法は他にもありますが、今回はシンプルに実行します。#!/bin/sh sudo unitd --control unix:./control.unit.sock --log ./unit.log sudo curl -fsSL -X PUT -d @./unit-conf.json --unix-socket ./control.unit.sock "http://localhost/config/" sudo tail -f ./access.logおまけ: requirements.pip
Django==2.1.5 pytz==2018.9ビルドとコンテナ起動
コンテナイメージのビルドをおこない、起動します。
curl等で疎通確認をおこないます。$ docker build --no-cache ./ -t proj/api $ docker run -d --name api -p 8080:8000 proj/api $ curl http://localhost:8080/polls/次の記事
ベースイメージ 用途 NGINX Unit APIサーバ ○ mysql MySQLサーバ ←ココ redis Redisサーバ まとめる docker compose
- 上記イメージをDockerfileを記述してビルド
- Docker Composeで取りまとめる
- 投稿日:2019-02-18T15:56:58+09:00
Windows10+WSL(Ubuntu)+Docker LNMPサーバサイド開発環境
WSL(Windows Subsytem for Linux)とDocker Desktopでカンタンにサーバサイド開発環境の準備をします。
内容
- WindowsにおけるLNMP(Linux+Nginx+MySQL+Python)開発環境を構築
- 今どきはコンテナ型仮想化だ
- だが、未だにWSLではDockerは満足に動かない
WSL
にUbuntu18.04 LTS
を入れて、Docker Desktopに接続して解決する前提
なぜWSLなのか?
WSLを使えば強力なLinux CLI環境が簡単に導入できます。
VirtualBox等のVMよりも軽く、Cygwin,MinGW等の互換レイヤーとは違ってLinuxディストリビューションを導入できる強みがあります。なぜDockerなのか?
年代 技術 ~2004年 オンプレ 2005年~ ハードウェア仮想化(Xen) 2013年~ コンテナ型仮想化(Docker-Kubernetes)
- コンテナ型仮想化 / Dockerの歴史から紐解く、コンテナ型仮想化の「今まで」と「これから」
- Docker/ さくらのナレッジ様 - Docker入門(第一回)~Dockerとは何か、何が良いのか~
- Kubernetes/ ThinkIT - いまこそ始めよう! Kubernetes入門
なぜDocker Desktopなのか?
WSL(Ubuntu)にDockerを入れても動作しません。
WSLが提供してないカーネルサービスを要求するためです。
WindowsでDockerを使うためにはDocker Desktopが必要です。
※2019年2月時点。WSL+Ubuntu16.04+Docker(17系)ならば動くようですが古いのでオススメしません。Dockerのほか、
NGINX Unit
もmemfd_create()
未対応で動作しません。
参考: WSLのシステムコール実装/未実装を確認するVirtualBox+Dockerで良くないか?
Docker Desktopは仮想Linuxマシン(MobyLinuxVM)を起動し、そのマシン内でDockerを実行します。
- VirtualBox+Docker: 仮想マシン上のLinux CLI環境+仮想マシン上のDocker
- WSL+Docker Desktop: WSL Linux CLI環境+仮想マシン上のDocker
CLI環境が軽いというメリットがあります。
WSLでは提供していないカーネルサービスがあるため、動作しないソフトが多くあります。そこはDockerが解決します。Ubuntu?
- Debian系でDockerと相性がよい
- シェア
- 今後必須となるAI/BCパッケージサポートの厚さ
Macでよくね?
(・ω・)
Docker Desktop
インストール
Docker公式からインストーラーをダウンロードして実行する
設定
Docker DesktopのdockerデーモンにWSLから接続できるようにし、ホスト(Windows)ドライブとの共有設定をおこなう。
タスクアイコン→
Setting
タブ 設定名 General Expose deamon on tcp://localhost:2375 without TLS
ON Shared Drives 共有したいドライブレター ON Network DNS Server Fixed WSL+Ubuntu環境の構築
WSL(Windows Subsystem for Linux)を有効にする
- Powershellを管理者で起動する
- WSLを有効にする
> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
- Microsoft StoreからUbuntu18.04をインストールする (検索⇒Ubuntu)
Wslttyをインストールする
WSLの標準コンソールは色々と制約が多いため、WSL向けMinttyの
Wsltty
を使う
- Wslttyをインストールする
- スタートメニューから
Configure WSL Shortcuts
を実行する- 作成したショートカットからWslttyを起動する
- 右クリック→settingから最低限の設定をおこなう。
タブ 項目 設定 text フォント お好みで ロケール ja_JP 文字セット UTF-8 window スクロール行数 60000とか UI言語 ja terminal タイプ xterm-256color aptの初期設定
- WslttyまたはUbuntuを起動する
- リポジトリを国内ミラーに変更する
$ sudo perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://ubuntutym.u-toyama.ac.jp/ubuntu/%g' /etc/apt/sources.listcat /etc/apt/sources.list | grep "^deb"
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-backports main restricted multiverse universe deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted deb http://security.ubuntu.com/ubuntu/ bionic-security universe deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse
- bionicの部分はUbuntuバージョンによって適宜読み替えてください
- securityリポジトリはデフォルトのままにしておく
- アップデート/アップグレード
$ sudo apt -y update $ sudo apt -y upgrade日本語環境
- 日本語/タイムゾーン設定
$ sudo apt -y install language-pack-ja $ sudo update-locale LANG=ja_JP.UTF-8 $ sudo timedatectl set-timezone 'Asia/Tokyo' $ sudo apt -y install manpages-ja manpages-ja-dev$ wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add - $ wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add - $ sudo wget https://www.ubuntulinux.jp/sources.list.d/$(lsb_release -cs).list -O /etc/apt/sources.list.d/ubuntu-ja.list $ sudo apt -y update $ sudo apt -y upgradeDocker
WSL(Ubuntu)にDockerをインストールする
- 開発(基本)パッケージを取得する
$ sudo apt update $ sudo apt -y install build-essential git apt-transport-https ca-certificates curl gnupg2 software-properties-common
- Dockerリポジトリの追加
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-key fingerprint 0EBFCD88 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" $ sudo apt update
- Dockerのインストール
$ sudo apt install docker-ce
- Docker Desktopのデーモンに接続するように設定を追加
$ echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bashrc $ echo 'alias docker="DOCKER_HOST=${DOCKER_HOST} docker"' >> ~/.bashrc
- Hello world実行
$ docker run -rm hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/各コンテナの起動
Nginx、MySQL、おまけにRedisのコンテナを実行して相互通信できるようにします
Dockerネットワークの作成
コンテナ間を繋ぐネットワークを作成する
$ docker network create development-network $ docker network lsコンテナの取得と起動
コンテナを取得して起動する。
各コンテナの説明はDockerHubを参照すること$ docker run -d --name nginx --network development-network -p 8080:80 nginx $ docker run -d --name mysql --network development-network -e MYSQL_ROOT_PASSWORD=hoge mysql $ docker run -d --name redis --network development-network redis
オプション --name コンテナ名 --network コンテナネットワーク名の指定 -p ホスト側ポート:コンテナ側ポート -d バックグラウンド実行(detach) ホスト(Windows)ドライブをマウント
コンテナ起動の際、
-v
オプションを指定してホスト(Windows)ドライブをマウントできる$ docker run -rm -v /c/project:/project ubuntu ls /project
オプション -v マウントするホスト側パス:コンテナ側パス
- ホスト側パスはDocker Desktopが解釈できるパスで記述する
- WSL(Ubuntu)ディレクトリをマウントすることはできない
次の記事
ベースイメージ 用途 NGINX Unit APIサーバ mysql MySQLサーバ redis Redisサーバ
- 上記イメージをDockerfileを記述してビルド
- Docker Composeで取りまとめる
- 投稿日:2019-02-18T15:56:58+09:00
Windows10+WSL(Ubuntu)+Docker サーバサイド開発環境
WSL(Windows Subsytem for Linux)とDocker Desktopでカンタンにサーバサイド開発環境の準備をします。
内容
- WindowsにおけるLNMP(Linux+Nginx+MySQL+Python)開発環境を構築したい
- 今どきはコンテナ型仮想化だ
- だが、未だにWSLではDockerは満足に動かない
WSL
にUbuntu18.04 LTS
を入れて、Docker Desktopに接続して解決する前提
なぜWSLなのか?
WSLを使えば強力なLinux CLI環境が簡単に導入できます。
VirtualBox等のVMよりも軽く、Cygwin,MinGW等の互換レイヤーとは違ってLinuxディストリビューションを導入できる強みがあります。なぜDockerなのか?
年代 技術 ~2004年 オンプレ 2005年~ ハードウェア仮想化(Xen) 2013年~ コンテナ型仮想化(Docker-Kubernetes)
- コンテナ型仮想化 / Dockerの歴史から紐解く、コンテナ型仮想化の「今まで」と「これから」
- Docker/ さくらのナレッジ様 - Docker入門(第一回)~Dockerとは何か、何が良いのか~
- Kubernetes/ ThinkIT - いまこそ始めよう! Kubernetes入門
なぜDocker Desktopなのか?
WSL(Ubuntu)にDockerを入れても動作しません。
WSLが提供してないカーネルサービスを要求するためです。
WindowsでDockerを使うためにはDocker Desktopが必要です。
※2019年2月時点。WSL+Ubuntu16.04+Docker(17系)ならば動くようですが古いのでオススメしません。Dockerのほか、
NGINX Unit
もmemfd_create()
未対応で動作しません。
参考: WSLのシステムコール実装/未実装を確認するVirtualBox+Dockerで良くないか?
Docker Desktopは仮想Linuxマシン(MobyLinuxVM)を起動し、そのマシン内でDockerを実行します。
- VirtualBox+Docker: 仮想マシン上のLinux CLI環境+仮想マシン上のDocker
- WSL+Docker Desktop: WSL Linux CLI環境+仮想マシン上のDocker
CLI環境が軽いというメリットがあります。
WSLでは提供していないカーネルサービスがあるため、動作しないソフトが多くあります。そこはDockerが解決します。Ubuntu?
- Debian系でDockerと相性がよい
- シェア
- 今後必須となるAI/BCパッケージサポートの厚さ
Macでよくね?
(・ω・)
Docker Desktop
インストール
Docker公式からインストーラーをダウンロードして実行する
設定
Docker DesktopのdockerデーモンにWSLから接続できるようにし、ホスト(Windows)ドライブとの共有設定をおこなう。
タスクアイコン→
Setting
タブ 設定名 General Expose deamon on tcp://localhost:2375 without TLS
ON Shared Drives 共有したいドライブレター ON Network DNS Server Fixed WSL+Ubuntu環境の構築
WSL(Windows Subsystem for Linux)を有効にする
- Powershellを管理者で起動する
- WSLを有効にする
> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
- Microsoft StoreからUbuntu18.04をインストールする (検索⇒Ubuntu)
Wslttyをインストールする
WSLの標準コンソールは色々と制約が多いため、WSL向けMinttyの
Wsltty
を使う
- Wslttyをインストールする
- スタートメニューから
Configure WSL Shortcuts
を実行する- 作成したショートカットからWslttyを起動する
- 右クリック→settingから最低限の設定をおこなう。
タブ 項目 設定 text フォント お好みで ロケール ja_JP 文字セット UTF-8 window スクロール行数 60000とか UI言語 ja terminal タイプ xterm-256color aptの初期設定
- WslttyまたはUbuntuを起動する
- リポジトリを国内ミラーに変更する
$ sudo perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://ubuntutym.u-toyama.ac.jp/ubuntu/%g' /etc/apt/sources.listcat /etc/apt/sources.list | grep "^deb"
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-backports main restricted multiverse universe deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted deb http://security.ubuntu.com/ubuntu/ bionic-security universe deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse
- bionicの部分はUbuntuバージョンによって適宜読み替えてください
- securityリポジトリはデフォルトのままにしておく
- アップデート/アップグレード
$ sudo apt -y update $ sudo apt -y upgrade日本語環境
- 日本語/タイムゾーン設定
$ sudo apt -y install language-pack-ja $ sudo update-locale LANG=ja_JP.UTF-8 $ sudo timedatectl set-timezone 'Asia/Tokyo' $ sudo apt -y install manpages-ja manpages-ja-dev$ wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add - $ wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add - $ sudo wget https://www.ubuntulinux.jp/sources.list.d/$(lsb_release -cs).list -O /etc/apt/sources.list.d/ubuntu-ja.list $ sudo apt -y update $ sudo apt -y upgradeDocker
WSL(Ubuntu)にDockerをインストールする
- 開発(基本)パッケージを取得する
$ sudo apt update $ sudo apt -y install build-essential git apt-transport-https ca-certificates curl gnupg2 software-properties-common
- Dockerリポジトリの追加
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-key fingerprint 0EBFCD88 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" $ sudo apt update
- Dockerのインストール
$ sudo apt install docker-ce
- Docker Desktopのデーモンに接続するように設定を追加
$ echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bashrc $ echo 'alias docker="DOCKER_HOST=${DOCKER_HOST} docker"' >> ~/.bashrc
- Hello world実行
$ docker run -rm hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/(参考) 各コンテナの起動とコンテナ間通信
Nginx、MySQL、おまけにRedisのコンテナを実行して相互通信する例です。
Dockerネットワークの作成
コンテナ間を繋ぐネットワークを作成する
$ docker network create development-network $ docker network lsコンテナの取得と起動
コンテナを取得して起動する。
各コンテナの説明はDockerHubを参照すること$ docker run -d --name nginx --network development-network -p 8080:80 nginx $ docker run -d --name mysql --network development-network -e MYSQL_ROOT_PASSWORD=hoge mysql $ docker run -d --name redis --network development-network redis
オプション --name コンテナ名 --network コンテナネットワーク名の指定 -p ホスト側ポート:コンテナ側ポート -d バックグラウンド実行(detach) Dockerネットワークを設定することで、
例えばredisコンテナからhttp://nginx:80/
のような形でnginxコンテナのNGINXにコンテナ間通信できる。(参考) ホスト(Windows)ドライブをマウント
コンテナ起動の際、
-v
オプションを指定してホスト(Windows)ドライブをマウントできる$ docker run -rm -v /c/project:/project ubuntu ls /project
オプション -v マウントするホスト側パス:コンテナ側パス
- ホスト側パスはDocker Desktopが解釈できるパスで記述する
- WSL(Ubuntu)ディレクトリをマウントすることはできない
次の記事
ベースイメージ 用途 NGINX Unit APIサーバ mysql MySQLサーバ redis Redisサーバ
- 上記イメージをDockerfileを記述してビルド
- Docker Composeで取りまとめる
- 投稿日:2019-02-18T15:15:02+09:00
Windows10+WSL(Ubuntu)+Docker サーバサイド開発環境
Windows10+WSL(Ubuntu)+Docker サーバサイド開発環境
WSL(Windows Subsytem for Linux)とDocker Desktopでカンタンにサーバサイド開発環境を準備します。
内容
- WindowsにおけるLNMP(Linux+Nginx+MySQL+Python)開発環境を構築
- 今どきはコンテナ型仮想化だ
- だが、未だにWSLではDockerは満足に動かない
WSL
にUbuntu18.04 LTS
を入れて、Docker Desktopに接続して解決する前提
なぜWSLなのか?
WSLを使えば強力なLinux CLI環境が簡単に導入できます。
VirtualBox等のVMよりも軽く、Cygwin,MinGW等の互換レイヤーとは違ってLinuxディストリビューションを導入できる強みがあります。なぜDockerなのか?
年代 技術 ~2004年 オンプレ 2005年~ ハードウェア仮想化(Xen) 2013年~ コンテナ型仮想化(Docker-Kubernetes)
- コンテナ型仮想化 / Dockerの歴史から紐解く、コンテナ型仮想化の「今まで」と「これから」
- Docker/ さくらのナレッジ様 - Docker入門(第一回)~Dockerとは何か、何が良いのか~
- Kubernetes/ ThinkIT - いまこそ始めよう! Kubernetes入門
なぜDocker Desktopなのか?
WSL(Ubuntu)にDockerを入れても動作しません。
WSLが提供してないカーネルサービスを要求するためです。
WindowsでDockerを使うためにはDocker Desktopが必要です。
※2019年2月時点。WSL+Ubuntu16.04+Docker(17系)ならば動くようですが古いのでオススメしません。Dockerのほか、
NGINX Unit
もmemfd_create()
未対応で動作しません。
参考: WSLのシステムコール実装/未実装を確認するVirtualBox+Dockerで良くないか?
Docker Desktopは仮想Linuxマシン(MobyLinuxVM)を起動し、そのマシン内でDockerを実行します。
- VirtualBox+Docker: 仮想マシン上のLinux CLI環境+仮想マシン上のDocker
- WSL+Docker Desktop: WSL Linux CLI環境+仮想マシン上のDocker
WSLはCLI環境部分が軽量です。
提供していないカーネルサービスがあるため動かないソフトもありますが、そこはDockerで解決します。Ubuntu?
- Debian系でDockerと相性がよい
- シェア
- トレンドのAI/BCパッケージサポートの厚さ
Macでよくね?
(・ω・)
Docker Desktop
インストール
Docker公式からインストーラーをダウンロードして実行する。
設定
Docker DesktopのdockerデーモンにWSLから接続できるようにし、ホスト(Windows)ドライブとの共有設定をおこなう。
タスクアイコン→
Setting
タブ 設定名 General Expose deamon on tcp://localhost:2375 without TLS
ON Shared Drives 共有したいドライブレター ON Network DNS Server Fixed WSL+Ubuntu環境の構築
WSL(Windows Subsystem for Linux)を有効にする
- Powershellを管理者で起動する
- WSLを有効にする
> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
- Microsoft StoreからUbuntu18.04をインストールする (検索⇒Ubuntu)
Wslttyをインストールする
WSLの標準コンソールは色々と制約が多いため、WSL向けMinttyの
Wsltty
を使う
- Wslttyをインストールする
- スタートメニューから
Configure WSL Shortcuts
を実行する- 作成したショートカットからWslttyを起動する
- 右クリック→settingから最低限の設定をおこなう。
タブ 項目 設定 text フォント お好みで ロケール ja_JP 文字セット UTF-8 window スクロール行数 60000とか UI言語 ja terminal タイプ xterm-256color aptの初期設定
- WslttyまたはUbuntuを起動する
- リポジトリを国内ミラーに変更する
$ sudo perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://ubuntutym.u-toyama.ac.jp/ubuntu/%g' /etc/apt/sources.listcat /etc/apt/sources.list | grep "^deb"
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates main restricted deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates universe deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates multiverse deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-backports main restricted multiverse universe deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted deb http://security.ubuntu.com/ubuntu/ bionic-security universe deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse
- bionicの部分はUbuntuバージョンによって適宜読み替えてください
- securityリポジトリはデフォルトのままにしておく
- アップデート/アップグレード
$ sudo apt -y update $ sudo apt -y upgrade日本語環境
- 日本語/タイムゾーン設定
$ sudo apt -y install language-pack-ja $ sudo update-locale LANG=ja_JP.UTF-8 $ sudo timedatectl set-timezone 'Asia/Tokyo' $ sudo apt -y install manpages-ja manpages-ja-dev$ wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add - $ wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add - $ sudo wget https://www.ubuntulinux.jp/sources.list.d/$(lsb_release -cs).list -O /etc/apt/sources.list.d/ubuntu-ja.list $ sudo apt -y update $ sudo apt -y upgradeDocker
WSL(Ubuntu)にDockerをインストールする
- 開発(基本)パッケージを取得する
$ sudo apt update $ sudo apt -y install build-essential git apt-transport-https ca-certificates curl gnupg2 software-properties-common
- Dockerリポジトリの追加
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-key fingerprint 0EBFCD88 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" $ sudo apt update
- Dockerのインストール
$ sudo apt install docker-ce
- Docker Desktopのデーモンに接続するように設定を追加
$ echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bashrc $ echo 'alias docker="DOCKER_HOST=${DOCKER_HOST} docker"' >> ~/.bashrc
- Hello world コンテナを実行
$ docker run -rm hello-worldHello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/次回
ベースイメージ 用途 NGINX Unit APIサーバ mysql MySQLサーバ redis Redisサーバ
- 上記イメージをDockerfileを記述してビルド
- Docker Composeで取りまとめる
- 投稿日:2019-02-18T14:23:40+09:00
Docker Containerの終了時にExit Code 137 (SIGKILL)が出る時の対処法
概要
これの日本語版のようなもの
問題
複数のコマンドを扱うDockerを使うとき、こんな感じのスクリプトになりませんか?
run.sh
#!/bin/sh -e pip install --upgrade poetry poetry config settings.virtualenvs.create false poetry install python ./manage.py runserver 0.0.0.0:8000
backend.dockerfile
from python:3.6 env PYTHONUNBUFFERED 1 run mkdir /code workdir /code
docker-compose.yml
version: '3' services: db: image: postgres:alpine environment: POSTGRES_PASSWORD: fingine POSTGRES_USER: fingine POSTGRES_DB: fingine backend: build: context: ./docker dockerfile: backend.dockerfile command: bash -c ./run.sh stop_signal: SIGINT env_file: ./docker/dev.env volumes: - .:/code ports: - "8000:8000" depends_on: - dbこれ、
docker-compose up
した後にdocker-compose stop
すると、fingine_backend_1 exited with code 137
と、エラーコード137、つまりSIGKILL
が返された上で終了する。つまり、終了まで10秒ほど待たされる上に強制終了を食らう。で、調べるとやれ、メモリが足りないだの
trap
コマンドを仕掛けろだのと的外れな回答ばかりで嫌気が差した。だから書いてやった。解決法
注目すべき点は
exec
ビルトインユーティリティコマンドにあります。通常、shell芸で書かれたコマンドは新しいプロセスを生成してそこでコマンドを実行します。この新しく生成されたプロセスでは、後述する問題によってシグナルを受信することができません。しかし、
exec
にコマンドライン引数としてコマンドとその引数を指定すると、コマンドのプロセスを生成せずに、シェルのプロセスとそのコマンドのプロセスを置き換えます。つまり、先程のrun.sh
を次のように書き換えると解決するのでは・・・と考えますよね?
run.sh
#!/bin/sh -e pip install --upgrade poetry poetry config settings.virtualenvs.create false poetry install exec python ./manage.py runserver 0.0.0.0:8000 # <-- これ。実はこれだと不十分っす。 というのも、Dockerの終了シグナルはコンテナ内のプロセスIDが1のプロセスに対してのみ発火します。さあ、
docker-compose.yml
を見てみましょう。
docker-compose.yml
version: '3' services: db: image: postgres:alpine environment: POSTGRES_PASSWORD: fingine POSTGRES_USER: fingine POSTGRES_DB: fingine backend: build: context: ./docker dockerfile: backend.dockerfile command: bash -c ./run.sh # <-- これ stop_signal: SIGINT env_file: ./docker/dev.env volumes: - .:/code ports: - "8000:8000" depends_on: - db要するに、
bash
コマンドをrun.sh
を引数にとって実行している、つまり、run.shの実行には新しいプロセスが生成される、と。 そういうことですね。というわけで、この
docker-compose.yml
に記述されているrun.sh
の呼び出しをbash
ではなく、exec
を使って呼び出されるようにする必要があります。幸いなことに、dockerには手軽に
exec
を使ったコマンドを記述する方法があり、今回はこれを用いることにします。
docker-compose.yml
version: '3' services: db: image: postgres:alpine environment: POSTGRES_PASSWORD: fingine POSTGRES_USER: fingine POSTGRES_DB: fingine backend: build: context: ./docker dockerfile: backend.dockerfile command: ['./run.sh'] # <-- これ。配列として書き直すとexecに続くコマンドとして認識される。 stop_signal: SIGINT env_file: ./docker/dev.env volumes: - .:/code ports: - "8000:8000" depends_on: - dbさあこれで
docker-compose up
してdocker-compose stop
してみましょう。コンテナを10秒も待つことなく、fingine_backend_1 exited with code 0
、つまりコード0、正常終了させる事ができます。素晴らしい!!最後に
Schlawackさんの記事によって筆者のdockerの問題が解決した事に謝辞を申し上げます。
参考記事
Hynek Schlawack, Why Your Dockerized Application Isn’t Receiving Signals, 19 June 2017
- 投稿日:2019-02-18T13:12:32+09:00
Amazon ECS+Fargate入門 (terraformを使ったクラスタ構築とオートスケール、ブルーグリーンデプロイ)
はじめに
コンテナベースでインフラ実現するに伴って色々AWS上でのコンテナ周り調べたり、本番導入した際のまとめ的なメモです。
大雑把にこんなことを書いてます。
- 構成概念と基礎知識
- terraformによるコードデプロイ連携でのブルーグリーンデプロイ
- terraformによるメトリクスベースでのオートスケーリング
ECS+Fargateのインフラアーキテクチャ全体像
ECSとは
ECSはAWSが提供するk8sと同じようなクラスタ構成でのコンテナオケーストレーション
を実現するサービス。
ECSは実際にコンテナが稼働する複数のworkerNodeとその操作・管理を担当するmasterNodeの
クラスタ構造を採用する事で分散・冗長化・スケーラブルなインフラを構築できる。
具体的には以下のような機能を提供をしている。
- クラスタの管理(workerNodeへの自動参加・死活監視や操作など)
- クラスタ上でのコンテナのデプロイメント(ノードに対する配置戦略・生成・ローリングアップデート)
- コンテナ群のスケールイン/アウト
- 外部サービス公開用のインタフェースの定義と動作するコンテナとロードバランサとの紐付けといったような外部との接合)
- コンテナに対するヘルスチェック機能(成否に伴なった外部アクセスに対するアタッチ・デタッチやコンテナ自体に不備発生した場合の再起動)
- スケジュールジョブ
Fargateとは
ECSクラスタのワーカーノードにあたる部分は自分でecsエージェントを起動させたEC2のオートスケーリンググループを組み、
リソース管理やスケールインアウトを操作する必要があった。
ECS Fargateはワーカー部分も含めて、フルマネージドで実現しているので
Fargateを採用する事で実際にコンテナを起動させていたEC2の管理や
スケールイン/アウト/アップに伴うクラスタ自身のリソース管理や作成から解放される。AWSではEKSというkubernetesを採用したコンテナオーケストレーションサービスもあるのだけど、
こちらは現時点(2019/01)ではFargate未対応でEKS最適化されたamiイメージを元に
EC2でWorkerNodeを構成することになる。サポートするVCPUとMemoryの組み合わせはこちら
Fargateの制約やデメリット
大雑把に。
awsvpcモード必須
logDriverがawslogsに限定される
cloudwatchとの連携は用意だがそれ以外のログの収集や送信(たとえばfluentdなど)との複数コンテナ稼働時のログ収集においては一工夫しないといけなかったり。
- 共有ボリュームは~4GB
概ねサイドカーエージェントでログ転送とかする際の置き場になる気がするんだけど、上限が低いので
ローテートとかちゃんと組まないと枯渇する
kill時の設定が素のECSよかいじれなかったり
デプロイと起動が素のECSより少し遅い。
EC2と同スペック比較すると15~20%くらいは高くなる。
この辺はtask定義のパラメータにも記載があるので
また起動数などのサービス制約もあるので、緩和申請なども場合によっては必要
ECSを構成する要素
- Cluster
- Service
- Container Definition
- Task & TaskDefinition
によってECSは構成されている。
次項で項目別に記載していく。
実際にはTaskDefinition中にContainerDefinitionは内包されている。Cluster
ServiceやTaskが所属する論理的なグルーピング。
FargateでなくEC2起動タイプを選択している場合は所属するクラスタのworkerNodeであるEC2インスタンスを共有する。Task & TaskDefinition
Task
TaskDefinitionの定義を実行に移している実行されている複数のコンテナを指す。
例えばgoとnginxを組み合わせて動いているようなwebアプリケーションを二つのコンテナ構成で動作させる場合は
以下のようなコンテナの組み合わせが1Taskとして扱われる。* sample_go_application_task - nginx - goTaskDefinition
Taskの定義というと(まんま)
アプリケーションをどのようにコンテナを組み合わせるか、リソースをどれくらい許容するか、
IAMやネットワークはどのようにするか、というようなコンテナの動作に必要な情報を設定する。
具体的には以下のような情報を定義する。
すごく大雑把にいうと、docker-composeにawsインフラの情報や定義が足されたようなイメージ。
- コンテナを動かすのに必要な情報(DockerImage,Port,protocolなど)
- 動かしたいコンテナの組み合わせ
- コンテナ起動時の実行コマンドやdocker関連のパラメーター設定
- 適用するIAMロールやセキュリティグループ
- ネットワークモードや動作させるネットワーク情報
- 使用するリソース量(CPU/メモリ)
ざっくりとした例
{ "family": "sample", "networkMode": "awsvpc", "containerDefinitions": [ { "name": "nginx", "image": "nginx:latest", "essential": true, "dockerLabels": {"role:nginx"} }, { "name": "go-app", "image": "your-go-app:latest", "essential": true, "environment": [ { "name": "ENV_1", "value": "hoge" }, { "name": "ENV_2", "value": "fuga" } ] } ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "256", "memory": "512" }動かすだけなら簡単に動きますが、例えばweb serverとして動作させる場合はulimitの値を設定しておかないと
ファイルディスクリプタが枯渇して爆死したりとか、memoryReservationを全く指定しないでハードリミット超えてコンテナがご臨終したりするので、パラメーターセットは公式を一度しっかり見て把握した方が良いと思います。
- 作成
aws ecs register-task-definition --cli-input-json file://$PWD/task-definitions.json更新はfamilyが存在していれば同一コマンドで行われて、バージョン値が自動でincrementされる。
Service
TaskDefinitionを束ねて外部にどのように公開するか、オートスケールを
どのような閾値で行うか、起動タイプ(EC2/Fargate)などの設定を記載できる。
例えばロードバランサへのtargetGroup追加したりなど。
- aws cliから雛形をダウンロード
aws ecs create-service --generate-cli-skeleton > service.json
- service生成
aws ecs create-service --cli-input-json file://$PWD/service.json更新は
update-service
で行う。
全ての項目が更新可能ではなく作成時にしか変更できない項目もあり、項目数が多いので、詳細な説明は公式を確認した方が良いです。例としてはこんな感じ
{ "cluster": "test-ecs-cluster", "serviceName": "test-service", "taskDefinition": "test:30", "loadBalancers": [ { "targetGroupArn": "loadbarancerArn", "containerName": "test-nginx", "containerPort": 80 } ], "desiredCount": 2, "clientToken": "", "launchType": "FARGATE", "platformVersion": "LATEST", "deploymentConfiguration": { "maximumPercent": 200, "minimumHealthyPercent": 100 }, "networkConfiguration": { "awsvpcConfiguration": { "subnets": [ "subnet-xxxxx", "subnet-xxxxx" ], "securityGroups": [ "sg-xxxxx", ], "assignPublicIp": "DISABLED" } }, "healthCheckGracePeriodSeconds": 0, "schedulingStrategy": "REPLICA", "deploymentController": { "type": "CODE_DEPLOY" }, "propagateTags": "TASK_DEFINITION", "tags": [ { "key": "env", "value": "stage" }, { "key": "region", "value": "jp" } ], "enableECSManagedTags": false }fargate利用の場合、alb/nlbのどちらかが必須であることと、TargetGroupのtarget_typeをipで作る必要がある。
Blue/Green Deploymentしてみよう。
同じAWSのコンポーネントであるCodeDeployと連携させると結構簡単にロードバランサー振替ベースでの
Blue/Green Deploymentを実現できる。GUIでは指示に従っていけば概ね簡単に作れるので、割愛。
CUIとterraformを使っての説明。
VPC,subnet,securityGroupは説明を省くので、よしなに作ってください。クラスタ作成
resource "aws_ecs_cluster" "ecs_cluster" { name = "test-nginx-cluster" } resource "aws_cloudwatch_log_group" "ecs_log_group" { name = "/ecs/test-nginx" tags { hoge = "hoge" fuga = "fuga" } }ロードバランサ作成
手元で作ったのがNLBだったので、今回はNLBで。
ALBでもほぼ変わりません。resource "aws_lb" "lb" { name = "test-nginx-lb" internal = false subnets = [ "your_subnetid", "your_subnetid", ] load_balancer_type = "network" enable_http2 = false } ## Target Group resource "aws_lb_target_group" "lb_target_blue" { name = "blue-nlb-tg" port = 80 protocol = "TCP" vpc_id = "your_vpc_id" target_type = "ip" health_check { interval = 10 healthy_threshold = 3 unhealthy_threshold = 3 protocol = "TCP" port = "traffic-port" } } ## Target Group resource "aws_lb_target_group" "lb_target_green" { name = "green-nlb-tg" port = 80 protocol = "TCP" vpc_id = "your_vpc_id" target_type = "ip" health_check { interval = 10 healthy_threshold = 3 unhealthy_threshold = 3 protocol = "TCP" port = "traffic-port" } } ## Listeners resource "aws_lb_listener" "lb_listner" { load_balancer_arn = "${aws_lb.lb.arn}" port = 8081 protocol = "TCP" # blueとgreenのtgを動的に入れ替えるのでignoreする lifecycle { ignore_changes = ["default_action"] } default_action { target_group_arn = "${aws_lb_target_group.lb_target_blue.arn}" #初回はblueにattach type = "forward" } }IAM設定
クラスタ初回作成時に自動的でecsTaskExecutionRoleとAWSServiceRoleForECSが作成されます。
ECS運用時のどちらも必要になりますので、なければとりあえず手動でコンソールからぽちぽちと
クラスタ作っておくと吉。task定義作成
terraformでもできますが、特段terraformで扱うメリットもない気がするので
ここではaws公式に沿ってjsonで。{ "family": "test", "executionRoleArn": "arn:aws:iam::xxxxxxxx:role/ecsTaskExecutionRole", "networkMode": "awsvpc", "containerDefinitions": [ { "name": "test-nginx", "image": "nginx:latest", "ulimits": [ { "name": "nofile", "softLimit": 65536, "hardLimit": 65536 } ], "entryPoint": [ "sh", "-c" ], "portMappings": [ { "hostPort": 80, "protocol": "tcp", "containerPort": 80 } ], "command": [ "echo blue > /usr/share/nginx/html/index.html | /usr/sbin/nginx -g \"daemon off;\"" ], "memoryReservation": 256, "essential": true, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/test-nginx", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "test-nginx" } } } ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "512", "memory": "4096", }service作成
resource "aws_ecs_service" "ecs" { name = "test-nginx" cluster = "${aws_ecs_cluster.ecs_cluster.id}" task_definition = "test-nginx:1" desired_count = 2 launch_type = "FARGATE" deployment_minimum_healthy_percent = 100 deployment_maximum_percent = 200 network_configuration { subnets = [ "your_subnet_ids" ] security_groups = [ "yours_secuiry_groups", ] assign_public_ip = "false" } health_check_grace_period_seconds = 0 load_balancer { target_group_arn = "${aws_lb_target_group.lb_target_blue.arn}" container_name = "test-nginx" container_port = 80 } scheduling_strategy = "REPLICA" deployment_controller { type = "CODE_DEPLOY" } // deployやautoscaleで動的に変化する値を差分だしたくないので無視する lifecycle { ignore_changes = [ "desired_count", "task_definition", "load_balancer", ] } propagate_tags = "TASK_DEFINITION" }コードデプロイアプリケーションとデプロイグループの作成
- iamを付与
resource "aws_iam_role" "codedeploy" { name = "codedeploy" } ## ECS CodedeployPolicy resource "aws_iam_role_policy_attachment" "ecs_deploy" { role = "${aws_iam_role.codedeploy.id}" policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" } ## Codedeploy IAM Role Policy data "aws_iam_policy_document" "codedeploy_iam_role_policy" { statement { actions = [ "autoscaling:CompleteLifecycleAction", "autoscaling:DeleteLifecycleHook", "autoscaling:DescribeAutoScalingGroups", "autoscaling:DescribeLifecycleHooks", "autoscaling:PutLifecycleHook", "autoscaling:RecordLifecycleActionHeartbeat", "ec2:DescribeInstances", "ec2:DescribeInstanceStatus", "sns:*", "tag:GetTags", "tag:GetResources", ] effect = "Allow" resources = ["*"] } }コードデプロイはこんな感じにリソースを作成する
resource "aws_codedeploy_app" "app" { compute_platform = "ECS" name = "test-ecs" } resource "aws_codedeploy_deployment_group" "group" { app_name = "test-bg-deploy" deployment_group_name = "test-bg-deploy-dg" service_role_arn = "${aws_iam_role.codedeploy.arn}" deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" auto_rollback_configuration { enabled = true events = ["DEPLOYMENT_FAILURE"] } blue_green_deployment_config { deployment_ready_option { action_on_timeout = "CONTINUE_DEPLOYMENT" } terminate_blue_instances_on_deployment_success { action = "TERMINATE" termination_wait_time_in_minutes = "10" # デプロイ成功後の環境保持時間 } } deployment_style { deployment_option = "WITH_TRAFFIC_CONTROL" deployment_type = "BLUE_GREEN" } ecs_service { cluster_name = "your_ecs_cluster_name" service_name = "your_ecs_service_name" } load_balancer_info { target_group_pair_info { prod_traffic_route { listener_arns = ["${aws_lb_listener.lb_listner.arn}"] } target_group { name = "${aws_lb_target_group.lb_target_blue.name}" } target_group { name = "${aws_lb_target_group.lb_target_green.name}" } } } }ブルーグリーン関連で設定できる内容として
- 新環境にテストポートのみ振り当てて公開する
- 新環境の保持期間
- 環境振替を手動で行う or 時間指定
などが設定できるので、細かくは公式を
デプロイしてみる
このへんはECSのコンソール上から手動なりお好みで。
吐き出してるhtmlをblue,greenで切り替えつつ、while loopなどでcurlで叩き続けると
blue/greenの切り替わりが観察できます。fargateTaskをオートスケーリングしたい
オートスケールはGUI上でサービス作成をする際は画面上から一貫して作れますが
実際には別リソースなので、terraformで作成する際は、appautoscaling_target
を使います。
ここではCloudWatchAlarmと連携してCPUメトリクスでスケールイン/アウトするサンプルを。resource "aws_appautoscaling_target" "test_ecs_target" { service_namespace = "ecs" resource_id = "service/your_cluster_name/service_name" scalable_dimension = "ecs:service:DesiredCount" role_arn = "${data.aws_iam_role.ecs_service_autoscaling.arn}" min_capacity = 2 max_capacity = 12 } # Automatically scale capacity up by one resource "aws_appautoscaling_policy" "test_scale_up" { name = "scale_up" service_namespace = "ecs" resource_id = "service/your_cluster_name/service_name" scalable_dimension = "ecs:service:DesiredCount" step_scaling_policy_configuration { adjustment_type = "ChangeInCapacity" cooldown = 600 metric_aggregation_type = "Average" step_adjustment { metric_interval_lower_bound = 0 scaling_adjustment = 1 } } depends_on = ["aws_appautoscaling_target.test_ecs_target"] } # Automatically scale capacity down by one resource "aws_appautoscaling_policy" "test_scale_down" { name = "scale_down" service_namespace = "ecs" resource_id = "service/your_cluster_name/service_name" scalable_dimension = "ecs:service:DesiredCount" step_scaling_policy_configuration { adjustment_type = "ChangeInCapacity" cooldown = 600 metric_aggregation_type = "Average" step_adjustment { metric_interval_lower_bound = 0 scaling_adjustment = -1 } } depends_on = ["aws_appautoscaling_target.test_ecs_target"] } # Cloudwatch alarm that triggers the autoscaling up policy resource "aws_cloudwatch_metric_alarm" "test_cpu_high" { alarm_name = "cpu_utilization_high" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "2" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "60" statistic = "Average" threshold = "60" dimensions { ClusterName = "your_ecs_cluster_name" ServiceName = "your_ecs_service_name" } alarm_actions = ["${aws_appautoscaling_policy.test_scale_up.arn}"] } # Cloudwatch alarm that triggers the autoscaling down policy resource "aws_cloudwatch_metric_alarm" "test_cpu_low" { alarm_name = "cpu_utilization_low" comparison_operator = "LessThanOrEqualToThreshold" evaluation_periods = "2" metric_name = "CPUUtilization" namespace = "AWS/ECS" period = "60" statistic = "Average" threshold = "30" dimensions { ClusterName = "your_ecs_cluster_name" ServiceName = "your_ecs_service_name" } alarm_actions = ["${aws_appautoscaling_policy.test_scale_down.arn}"] } data "aws_iam_role" "ecs_service_autoscaling" { name = "AWSServiceRoleForApplicationAutoScaling_ECSService" }参考
- 投稿日:2019-02-18T00:11:41+09:00
PHP未経験者が勉強用にDockerで開発環境作ってみた
やりたいこと
できるだけ自分のPCを汚さずにいわゆるLAMP環境
(Linux, Apache, MySQL, PHP)の開発環境を手軽に構築したい。Macの場合はMAMPやVirtualBoxがあるけど汚れちゃうし・・・
そうだ、Dockerの勉強も兼ねてDockerを使おう!!!
成果物
Dockerを使用してApache, PHP, MySQL, phpMyAdminを連携し、
PHPとMySQLの勉強に注力するための開発環境を作成しました。https://github.com/MasanoriIwakura/php_study
開発環境
ツール名 バージョン OS macOS Mojava 10.14.3 Docker Docker for Mac 18.06.1-ce Docker Compose 1.22.0 PHP 7.2.7-apache (Apacheと連携されているコンテナ) MySQL 8 phpMyAdmin 4.7 プロジェクト構成
[PHP]
Dockerコンテナの80番ポートと、ホストOSの80番ポートを紐付け。
MySQLとの接続にはmysqli
を使用。
Docker ComposeだけではPHPの拡張機能をインストール出来ないため、Dockerfileで対応。[MySQL]
Version8からデフォルトの認証方式が
caching_sha2_password
となっているため、
mysql_native_password
に変更する。
この設定を行わないとMySQLに接続出来ない。使用手順
[前提条件]
- Dockerがインストールされていること、起動されていること
- Docker Composeがインストールされていること
Dockerを導入していない場合、Macの場合は以下のコマンドで導入してください。
# Homebrewがインストールされていること $ brew install docker $ brew cask install docker $ brew install docker-compose# Clone $ git clone https://github.com/MasanoriIwakura/php_study.git # フォルダ移動 $ cd php_study # コンテナ実行 $ docker-compose up -d # 全てのコンテナが立ち上がっていることを確認 $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------ php_study_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp php_study_php_1 docker-php-entrypoint apac ... Up 0.0.0.0:80->80/tcp php_study_phpmyadmin_1 /run.sh phpmyadmin Up 0.0.0.0:8080->80/tcp, 9000/tcp[接続確認]
PHPの表示確認は
http://localhost
にアクセス
phpMyAdminの表示は
http://localhost:8080
にアクセス[ソース編集方法]
php/html
直下にフォルダやphpファイルを作成・編集するだけ。例えば、
php/html/index.php
を編集した場合はhttp://localhost
にアクセス、
php/html/sample/sample.php
を編集した場合はhttp://localhost/sample/sample.php
にアクセスすることで動作確認が行えます。また、ソースを修正して保存し、ブラウザをリロードするだけで反映できるので
Dockerでイメージのリビルドや再展開は不要です。その他
[MySQLの初期化]
起動時に
mysql/sql
内に実行したSQLを配置することで自動的にMySQL内にテーブルを作成したり、初期データを投入することができます。[今回作成したDockerコンテナの再構築]
実は今回のリポジトリ直下にShellを配置しています。
実行することで永続化したファイルの削除、コンテナ削除、コンテナ再構築をいっぺんに行います。
永続化したファイルを削除したくない場合は手動でコンテナを削除してください。# リポジトリ直下で使用 ./docker-clean.sh # 永続化したファイルを削除したくない場合(コンテナだけ削除) docker-compose stop docker-compose rm今後もっと便利にすることができればこちらの記事も更新します。