20190714のdockerに関する記事は13件です。

Dockerでtabcmd実行環境を作る方法

Tableau Serverをコマンドラインから操作できるtabcmdですが、Javaのバージョンが8以下でないとエラーが起るようです。(実際、Java12がインストールされた環境でtabcmdを実行するとエラーが起こります)
https://community.tableau.com/docs/DOC-22179

そこで、Openjdk8をベースイメージにして、Dockerでtabcmdを実行できる環境を構築します。

今回はDockerコンテナを起動し、docker execでTableau Serverへのログイン、Publish、ログアウトをやってみます。

環境

  • mac Mojave 10.14.5
  • Tableau Desktop 2019.2.1
  • Tableau Server 2019.2.1
  • tabcmd-2019-2-1

ディレクトリ構成

以下のディレクトリ構成にします。

tabcmd/
 ├ docker-compose.yml
 ├ Dockerfile
 ├ sh
   └ publish.sh
 └ start.sh

作成ファイル

Dockerfile

Openjdk8をベースイメージに、Dockerイメージを作成します。
curltabcmdを取得し、展開します。

FROM openjdk:8

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update
RUN apt-get install -y sudo curl gdebi
# download tabcmd
RUN curl -L -O https://downloads.tableau.com/esdalt/2019.2.1/tableau-tabcmd-2019-2-1_all.deb
# extract deb
RUN gdebi -n tableau-tabcmd-2019-2-1_all.deb

docker-compose.yml

ローカルのsh/publish.shをDockerコンテナ上にマウントします。

docker-compose.yml
version: '3'

services:
  tabcmd:
    build: ./
    tty: true
    container_name: tabcmd
    image: tabcmd
    volumes:
    - ./sh:/var

sh/publish.sh

docker-compose.ymlで記載されている通り、Dockerコンテナ上にマウントされます。
引数を受け取り、Tableau Serverにログイン、Publish、ログアウトを実施します。

publish.sh
#!/bin/bash
# set env path of tabcmd
source /etc/profile.d/tabcmd.sh

# login
echo "starting login."
tabcmd --accepteula login -s $1 --username $2 --no-certcheck --password $3
echo "completed login."

# Publish
echo "starting publish."
tabcmd publish $4 -o $5 -r $6 --db-username $7 --db-password $8 --save-db-password --save-oauth --no-certcheck
echo "completed publish."

# logout
echo "starting log out."
tabcmd --accepteula logout -s $1 --no-certcheck
echo "completed log out."

start.sh

パラメータを設定し、ローカルで実行します。
ローカルのPublish対象のtwbファイルをDockerコンテナ上にコピーし、docker execによってDockerコンテナ上でスクリプトを実行します。

start.sh
#!/bin/bash

# set param
TABLEAU_SERVER_URL=
TABLEAU_SERVER_LOGIN_ID=
TABLEAU_SERVER_LOGIN_PASS=
# tableau twb in local
TWB_PATH_LOCAL=
# in docker (ex. var/test.twb)
TWB_PATH_DOCKER=
OVERWRITE_WB_NAME=
PJ_NAME=
DB_USERNAME=
DB_PASSWPRD=

# copy from local to docker
docker cp $TWB_PATH_LOCAL tabcmd:$TWB_PATH_DOCKER
# exec publish on docker
docker exec tabcmd bash -c "source var/publish.sh $TABLEAU_SERVER_URL $TABLEAU_SERVER_LOGIN_ID $TABLEAU_SERVER_LOGIN_PASS $TWB_PATH_DOCKER $OVERWRITE_WB_NAME $PJ_NAME $DB_USERNAME $DB_PASSWPRD"

環境構築

上記のファイルを作成後、ローカルでdocker-compose.ymlと同一ディレクトリで以下を実行します。

docker-compose up -d

実行完了後、以下を実行してコンテナが起動していることを確認します。

 docker ps

結果:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
823a47830565        tabcmd              "bash"              About an hour ago   Up About an hour                        tabcmd

スクリプト実行

ローカルでstart.shと同一ディレクトリで以下を実行します。

sh start.sh

これでPublish完了です。

まとめ

Dockerを使うと環境間の差異が原因での実行エラーを防げます。
学習コストは高いですが、作業の標準化にはうってつけだと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerコンテナ上でtabcmd実行環境を作る方法

Tableau Serverをコマンドラインから操作できるtabcmdですが、Javaのバージョンが8以下でないとエラーが起るようです。(実際、Java12がインストールされた環境でtabcmdを実行するとエラーが起こります)
https://community.tableau.com/docs/DOC-22179

そこで、Openjdk8をベースイメージにして、Dockerでtabcmdを実行できる環境を構築します。

今回はDockerコンテナを起動し、docker execでTableau Serverへのログイン、Publish、ログアウトをやってみます。

環境

  • mac Mojave 10.14.5
  • Tableau Desktop 2019.2.1
  • Tableau Server 2019.2.1
  • tabcmd-2019-2-1

ディレクトリ構成

以下のディレクトリ構成にします。

tabcmd/
 ├ docker-compose.yml
 ├ Dockerfile
 ├ sh
   └ publish.sh
 └ start.sh

作成ファイル

Dockerfile

Openjdk8をベースイメージに、Dockerイメージを作成します。
curltabcmdを取得し、展開します。

FROM openjdk:8

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update
RUN apt-get install -y sudo curl gdebi
# download tabcmd
RUN curl -L -O https://downloads.tableau.com/esdalt/2019.2.1/tableau-tabcmd-2019-2-1_all.deb
# extract deb
RUN gdebi -n tableau-tabcmd-2019-2-1_all.deb

docker-compose.yml

ローカルのsh/publish.shをDockerコンテナ上にマウントします。

docker-compose.yml
version: '3'

services:
  tabcmd:
    build: ./
    tty: true
    container_name: tabcmd
    image: tabcmd
    volumes:
    - ./sh:/var

sh/publish.sh

docker-compose.ymlで記載されている通り、Dockerコンテナ上にマウントされます。
引数を受け取り、Tableau Serverにログイン、Publish、ログアウトを実施します。

publish.sh
#!/bin/bash
# set env path of tabcmd
source /etc/profile.d/tabcmd.sh

# login
echo "starting login."
tabcmd --accepteula login -s $1 --username $2 --no-certcheck --password $3
echo "completed login."

# Publish
echo "starting publish."
tabcmd publish $4 -o $5 -r $6 --db-username $7 --db-password $8 --save-db-password --save-oauth --no-certcheck
echo "completed publish."

# logout
echo "starting log out."
tabcmd --accepteula logout -s $1 --no-certcheck
echo "completed log out."

start.sh

パラメータを設定し、ローカルで実行します。
ローカルのPublish対象のtwbファイルをDockerコンテナ上にコピーし、docker execによってDockerコンテナ上でスクリプトを実行します。

start.sh
#!/bin/bash

# set param
TABLEAU_SERVER_URL=
TABLEAU_SERVER_LOGIN_ID=
TABLEAU_SERVER_LOGIN_PASS=
# tableau twb in local
TWB_PATH_LOCAL=
# in docker (ex. var/test.twb)
TWB_PATH_DOCKER=
OVERWRITE_WB_NAME=
PJ_NAME=
DB_USERNAME=
DB_PASSWPRD=

# copy from local to docker
docker cp $TWB_PATH_LOCAL tabcmd:$TWB_PATH_DOCKER
# exec publish on docker
docker exec tabcmd bash -c "source var/publish.sh $TABLEAU_SERVER_URL $TABLEAU_SERVER_LOGIN_ID $TABLEAU_SERVER_LOGIN_PASS $TWB_PATH_DOCKER $OVERWRITE_WB_NAME $PJ_NAME $DB_USERNAME $DB_PASSWPRD"

環境構築

上記のファイルを作成後、ローカルでdocker-compose.ymlと同一ディレクトリで以下を実行します。

docker-compose up -d

実行完了後、以下を実行してコンテナが起動していることを確認します。

 docker ps

結果:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
823a47830565        tabcmd              "bash"              About an hour ago   Up About an hour                        tabcmd

スクリプト実行

ローカルでstart.shと同一ディレクトリで以下を実行します。

sh start.sh

これでPublish完了です。

まとめ

Dockerを使うと環境間の差異が原因での実行エラーを防げます。
学習コストは高いですが、作業の標準化にはうってつけだと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアルのために作成したDockerコンテナを起動し、必要なモジュールをインストールする for Windows

拙著RailsチュートリアルのためにDockerコンテナの作成 for Windowsの続きとなります。「Yay! You're on Rails!」と表示されるところまで持っていきます。

前回停止させたコンテナを再び起動

まず、前回停止させたDockerコンテナが存在することを確認します。

powershell
$ docker container ls -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                    PORTS               NAMES
e11d56031051        ruby:2.5.1          "/bin/bash"         22 hours ago        Exited (0) 21 hours ago                       RailsTutorialTest

STATUSも、想定通りExited (0)になっています。

停止状態のDockerコンテナは、docker start {コンテナ名}コマンドで起動することができます。

powershell
$ docker start RailsTutorialTest
RailsTutorialTest

改めてdocker lsコマンドを実行します。

powershell
$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
e11d56031051        ruby:2.5.1          "/bin/bash"         22 hours ago        Up About a minute   0.0.0.0:8080->3000/tcp   RailsTutorialTest

RailsTutorialTestコンテナが正常に起動されています。

続いて、RailsTutorialTestコンテナのシェルに入りましょう。

powershell
$ docker container exec -it RailsTutorialTest /bin/bash
root@e11d56031051:/#

正常にRailsTutorialTestコンテナのシェルに入れました。

docker container execのオプション

  • -it…ホスト側の標準入出力とDockerコンテナの標準入出力を接続する
    • -i…ホスト側の標準入力とDockerコンテナの標準入力を接続する
    • -t…Dockerコンテナの標準出力とホスト側の標準出力を接続する
    • ホスト側からDockerコンテナ上のシェルを操作するために必要な設定
  • コマンドを実行するコンテナの名前
  • コンテナで実行するコマンド
    • コマンドを実行するコンテナの名前の後に入力する
    • 今回は/bin/bashである

Railsのインストール

bash
# apt update
# apt install -y nodejs
# gem install rails -v 5.1.6

aptリポジトリの一覧を更新

まずはapt updateでaptリポジトリの一覧を更新します。リポジトリを追加・削除した場合には必須となる操作です。

Node.jsのインストール

続いて、apt install -y nodejsでNode.jsをインストールします。
RailsではrubyのExecJS gemを使用し、ExecJS gemの実行にはJavaScriptランタイムが必要となるためです。

CentOS 7.5にRuby on Railsチュートリアルの環境を構築するによると、JavascriptランタイムなしでExecJS gemを実行しようとした場合、以下のようなエラーが発生して実行できないそうです。

bash
$ rails server
/home/vagrant/.gem/ruby/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
...

Rails本体のインストール

ここまで完了したら、いよいよRailsのインストールです。バージョンはRailsチュートリアルで使っている5.1.6を指定します。

bash
Successfully installed rails-5.1.6

ひとまずここまで到達できました。

Rails本体がインストールされていることの確認

bash
# rails -v
Rails 5.1.6

rails -vコマンドにより、インストールされているRailsのバージョンを確認することができます。Rails 5.1.6が正しくインストールされているようです。

gemをインストールするためにGemfileを修正する

bash
# cd /var/www
# rails _5.1.6_ new hello_app

以下は、rails-vコマンドにより生成されたGemfileとRailsチュートリアルのリスト1.5に記載されていたGemfileの差分です。

Gemfile
 source 'https://rubygems.org'

-git_source(:github) do |repo_name|
-  repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
-  "https://github.com/#{repo_name}.git"
-end
-
-
-# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
-gem 'rails', '~> 5.1.6'
-# Use sqlite3 as the database for Active Record
-gem 'sqlite3'
-# Use Puma as the app server
-gem 'puma', '~> 3.7'
-# Use SCSS for stylesheets
-gem 'sass-rails', '~> 5.0'
-# Use Uglifier as compressor for JavaScript assets
-gem 'uglifier', '>= 1.3.0'
-# See https://github.com/rails/execjs#readme for more supported runtimes
-# gem 'therubyracer', platforms: :ruby
-
-# Use CoffeeScript for .coffee assets and views
-gem 'coffee-rails', '~> 4.2'
-# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
-gem 'turbolinks', '~> 5'
-# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
-gem 'jbuilder', '~> 2.5'
-# Use Redis adapter to run Action Cable in production
-# gem 'redis', '~> 4.0'
-# Use ActiveModel has_secure_password
-# gem 'bcrypt', '~> 3.1.7'
-
-# Use Capistrano for deployment
-# gem 'capistrano-rails', group: :development
+gem 'rails',        '5.1.6'
+gem 'puma',         '3.9.1'
+gem 'sass-rails',   '5.0.6'
+gem 'uglifier',     '3.2.0'
+gem 'coffee-rails', '4.2.2'
+gem 'jquery-rails', '4.3.1'
+gem 'turbolinks',   '5.0.1'
+gem 'jbuilder',     '2.6.4'

 group :development, :test do
-  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
-  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
-  # Adds support for Capybara system testing and selenium driver
-  gem 'capybara', '~> 2.13'
-  gem 'selenium-webdriver'
+  gem 'sqlite3',      '1.3.13'
+  gem 'byebug', '9.0.6', platform: :mri
 end

 group :development do
-  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
-  gem 'web-console', '>= 3.3.0'
-  gem 'listen', '>= 3.0.5', '< 3.2'
-  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
-  gem 'spring'
-  gem 'spring-watcher-listen', '~> 2.0.0'
+  gem 'web-console',           '3.5.1'
+  gem 'listen',                '3.1.5'
+  gem 'spring',                '2.0.2'
+  gem 'spring-watcher-listen', '2.0.1'
 end

-# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
+# Windows環境ではtzinfo-dataというgemを含める必要があります
 gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

なお、この差分は、以下のコマンドにより取得したものをコピーアンドペーストしたものです。

powershell
$ git --no-pager diff HEAD^..HEAD
bash
# cd hello_app
# bundle install

最初の実行結果は、以下の通りになりました。

bash
You have requested:
  spring = 2.0.2

The bundle currently has spring locked at 2.1.0.
Try running `bundle update spring`

If you are updating multiple gems in your Gemfile at once,
try passing them all to `bundle update`

…おや、うまくいっていませんね。というわけで、以下のコマンドを実行してみます。

bash
# bundle update

何やら色々とインストールされていますね。最後に以下のメッセージが表示されるまで待ちます。

bash
Bundle updated!

改めて、以下のコマンドを実行します。

bash
# bundle install

再び何やら色々と表示されましたね。最後に以下のメッセージが表示されるまで待ちます。

bash
Bundle complete! 15 Gemfile dependencies, 64 gems now installed.
Bundled gems are installed into `/usr/local/bundle`

bundle installが無事完了しました。

Yay! You're on Rails!

bundle installまで終われば、実際に動かすことができるRailsアプリケーションの作成は完了しています。RailsのローカルWebサーバー機能を使い、生成されたRailsアプリケーションを実際に動かしています。

bash
# rails server

以下のメッセージが表示され、ローカルWebサーバーが起動されます。

bash
=> Booting Puma
=> Rails 5.1.6 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.9.1 (ruby 2.5.1-p57), codename: Private Caller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

Dockerコンテナ側の3000番ポートは、ホスト側の8080番ポートに連結されています。というわけで、ブラウザでhttp://localhost:8080/にアクセスしてみます。

yay_you_re_on_rails.png

Yay! You're on Rails!…というわけで、無事Railsアプリケーションが動作するところまでの環境構築が完了しました。

演習

1. デフォルトのRailsページに表示されているものと比べて、今の自分のコンピュータにあるRubyのバージョンはいくつになっていますか? コマンドラインでruby -vを実行することで簡単に確認できます。

powershell
$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x64-mingw32]

上述の結果になりました。2.6.3p62でしょうか。

2. 同様にして、Railsのバージョンも調べてみましょう。調べたバージョンはリスト 1.1でインストールしたバージョンと一致しているでしょうか?

powershell
$ rails -v
rails : 用語 'rails' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されませ
ん。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してくだ
さい。
発生場所 行:1 文字:1
+ rails -v
+ ~~~~~
    + CategoryInfo          : ObjectNotFound: (rails:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

そもそもホストのWindows環境にRailsはインストールされていませんでした。

番外. Gemのバージョンを調べてみましょう。

powershell
$ gem -v
3.0.2

Gemのバージョンは3.0.2でした。

関連リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nginxとphp-fpmを用いてLaravalを表示する

どうも、若松です。

前回はLaravelをDockerで起動し、イメージを軽量化するところまで行いました。
https://qiita.com/t_wkm2/items/245288e42083ac7e4057

今回は、 php artisan でのサーバ起動ではなく、Nginx+php-fpmでLaravelを表示するところまでを行います。

設定

ディレクトリ構造

docker/
├─ docker-compose.yml
├─ nginx/
|  ├─ Dockerfile
|  └─ default.conf
└─ laravel/
   └─  Dockerfile

docker-compose.yml

version: '2'
services:
  nginx:
    image: nginx
    ports:
      - "80:80"
  laravel:
    image: laravel

Dockerfile(nginx)

FROM nginx:1.17-alpine

# ローカルから設定ファイルをコピー
COPY  default.conf /etc/nginx/conf.d/default.conf

default.conf(nginx)

server {
    listen       80;
    server_name  localhost;

    location / {
        # ドキュメントルート設定
        root   /var/www/laravel/public;

        fastcgi_split_path_info  ^(.+\.(?:php|phar))(/.*)$;   
        fastcgi_intercept_errors on;

        fastcgi_index  index.php;
        include        fastcgi_params;
        # FastCGIの向き先をLaravelコンテナに設定
        fastcgi_pass   laravel:9000;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED $document_root$fastcgi_path_info;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Dockerfile(laravel)

FROM amazonlinux:2 as vender

# PHPインストール
RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# 環境変数設定
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

# Laravelインストール
RUN composer global require "laravel/installer"

# Laravelプロジェクト作成
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

FROM php:7.3-fpm-alpine

# ビルド用コンテナから必要なコンテンツをコピー
COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/

操作

コマンドは全て最上位ディレクトリの docker から行う想定です。

Nginxコンテナビルド

docker build nginx/. -t nginx --squash

Laravelコンテナビルド

docker build laravel/. -t laravel --squash

docker-composeで起動

docker-compose up

ブラウザで表示を確認

http://localhost:8000 にアクセスすることで以下のサンプルを表示します。
スクリーンショット 2019-07-06 22.57.48.png

解説

Nginxコンテナ

ベースイメージ

FROM nginx:1.17-alpine

ベースイメージにはNginx公式リポジトリにあるnginx:1.17-alpineを使用しました。
2019/7/14現在のNginxの最新が1.17であり、軽量化を目的にAlpineLinux版を使いたかったためです。
80番ポートの開放やNginxの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

default.conf

COPY  default.conf /etc/nginx/conf.d/default.conf

設定はローカルに用意したdefault.confをイメージにコピーして配置します。
FastCGI設定のほとんどは一般的な設定のため、特徴的なものだけ解説します。

root

root   /var/www/laravel/public;

ドキュメントルートはLaravelコンテナのアプリケーションが配置されているディレクトリを指定します。

fastcgi_pass

fastcgi_pass   laravel:9000;

UnixソケットかTCPを指定できますが、Unixソケットではコンテナを越えられないため、TCPで設定します。
アドレスの指定にはDockerのNamespaceを利用します。

Laravelコンテナ

前回のDockerfileからの差異のみ解説します。

ベースイメージ

FROM php:7.3-fpm-alpine

実行用イメージのベースを php:7.3-alpine から php:7.3-fpm-alpine に変更しました。
これによってデフォルトでphp-fpmがインストールされた状態から設定を行えばよくなります。
9000番ポートの開放やphp-fpmの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

Laravelコンテンツのオーナー変更

COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/

php:7.3-fpm-alpineのphp-fpm初期設定では、php-fpmのワーカ起動ユーザはwww-dataになっています。
COPYコマンドで配置したLaravelコンテンツはrootがオーナーになってしまうため、そのままだと権限エラーとなります。
そこで、--chownオプションを使用し、オーナーをwww-dataへ変更しています。
--chownオプションはBashでいうところのchown -Rとなるため、ディレクトリがあっても再帰的に処理してくれます。

docker-compose

今回からコンテナが2つになったため、操作簡略のためにdocker-composeを導入しました。
docker-compose には起動時のオプション設定や、複数コンテナのビルド、依存関係制御など様々な機能がありますが、ここではコンテナ起動/停止とポートオプションのみ使用しています。

複数コンテナのビルドを使用しない理由

本当であれば使用したかったのが本音です。
しかしながら2019/7/14現在、BuildKitやsquashオプションに対応していないため、あえてdockerコマンドでビルドを行っています。

Tips

コンテナイメージ内にあるファイルをローカルにコピーする

設定ファイルを作成する際に、デフォルトの設定をローカルにコピーし、それを改変して作成していくことはよくあると思います。
コンテナではSCPが使えないため、代わりにdocker cpコマンドを使用します。
今回のdefault.confの場合は、以下のようにしてコピーしました。

docker run -d --name nginx nginx:1.17-alpine
docker cp $(docker ps --filter name=nginx -aq):/etc/nginx/conf.d/default.conf .

コンテナイメージの履歴を確認する

FROMで使用するベースイメージには予めポートの開放やデーモンの起動が設定されている場合があります。
今回でいうところのNginxやphp-fpmですね。
それを確認するにはdocker historyコマンドを使用します。
例としてphp-fpmのhistoryを確認してみます。

docker history --format {{.CreatedBy}} php:7.3-fpm-alpine
/bin/sh -c #(nop)  CMD ["php-fpm"]
/bin/sh -c #(nop)  EXPOSE 9000
/bin/sh -c #(nop)  STOPSIGNAL SIGQUIT
/bin/sh -c set -eux;  cd /usr/local/etc;  if…
/bin/sh -c #(nop) WORKDIR /var/www/html
/bin/sh -c #(nop)  ENTRYPOINT ["docker-php-e…
/bin/sh -c docker-php-ext-enable sodium
...

このようにズラズラとコマンドが表示されるかと思います。
これは実行日次のtimestampが新しい順で上から並んでいます。
これを見るとベースイメージの最後に、9000番ポートの開放とphp-fpmの実行が行われているため、今回のDockerfileではポートの開放とデーモンの起動が不要なことがわかります。

まとめ

Nginx+php-fpmに加えて、docker-composeも導入してみました。
そんなに特殊な設定を行ったわけではありませんが、Dockerfileの書き方やコンテナ特有の設定等はお伝えできたかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WordPress環境立ち上げるまでの最小ステップ

はじめに

過去に職場の環境として、業務上のナレッジマネジメントで使用するWikiとしてWordPressを使用していたことから、個人的にもWordPress環境の構築の勉強しようと思いたち、とりあえず何も投稿していないWordPressを立ち上げるところまで出来たので備忘録として投稿。

環境

  • macOS Mojave 10.14.5
  • HomeBrewインストール済み
  • Dockerを過去の学習でインストール済み

HomeBrew install(導入済みなら不要)

ここを参照。
https://brew.sh/index_ja

Docker install(導入済みなら不要)

この記事を参考にした。
https://qiita.com/nemui_/items/ed753f6b2eb9960845f7
けど以下だけでいけたような気がする。(遠い記憶)
$ brew cask install docker

ここから本題

Dockerを起動できてることは前提としておきます。
$ docker versionなり$ docker psがエラーにならなければOK。

$ docker pull mysql:5.7.26
しばらく待つ。
$ docker pull wordpress
またしばらく待つ。
両方完了したら

$ docker run --name test-mysql -e MYSQL_ROOT_PASSWORD=test-pw -d mysql
$ docker run --name test-wordpress --link test-mysql:mysql -d -p 8080:80 wordpress

$ docker ps
でtest-mysqlとtest-wordpressが立ち上がっていることが確認できたら
$ open http://localhost:8080/
を実行するとWordPressの言語設定画面が出てくるので言語を選択。
これでダッシュボードが表示されたら最低限の環境の立ち上げ完了。

終わりに

brewの使い方とかdockerコマンドの意味とかは適宜確認してください。
コンテナ名とかPortとかは適宜変えたら良いです。
2019/7/14時点ではMySQLが8系だとWordPressが対応しておらずエラーが表示されるので、5系の最新を取ってくる必要があります。
面倒くさがってversion指定しなかったらローカルホストを表示した際にエラーが出てきて少しハマりました。
今回WordPress環境立ち上げようとググったりすると結構な確率でMAMPやXAMPが出てきたりして、バージョン管理が少々面倒だったり、新しくインストールして容量食わせたくなかったところから今回Docker使って立ち上げる方法を選択しました。(環境で書いたように元々Dockerはインストールしてたので)
そもそも自分がアプリ屋さんなのと、今はナレッジマネジメントでWordPressを業務で使っていないので、どんな立ち上げ方が主流なのかは知りませんが、MAMPやXAMP用意するよりDocker使えうほうが楽な気がしないでもない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker/Kubernetes 実践コンテナ開発入門 part3

Dockerイメージの操作 には?

Dockerイメージを作る
docker image build

作ったDockerイメージからDockerコンテナを作る
docker container run

Dockerコンテナを動かしてアプリケーションを動かす

Dockerイメージのタグ とは?

Dockerイメージの名前に近い。Dockerイメージはタグを付けないとハッシュ値での管理になり、
手間がかかるのでタグを付けて管理することが多い。
タグの付け方は"名前空間/イメージ名/バージョン"

Dockerコンテナの操作 には?

Dockerコンテナの状態は実行・停止・破棄の3種類。
docker container runした直後は実行中の状態になる。

処理を終了したコンテナは停止状態になるが、自動で破棄されることはない。
使わなくなったコンテナはどんどん破棄するのが良い。

※破棄したコンテナと完全に同じコンテナは再現できない。

docker container run

dockerコンテナの作成と実行。
docker container run -d
デーモンとして実行。常駐させる。

docker container run -p
ポートフォワーディング。-p 9000:8080と指定すれば、
ホスト側の9000番ポートがコンテナ側の8080ポートにつながる。

docker container ls

現在実行中のDockerコンテナ一覧を表示する。

docker container ls -q
dockerコンテナの一覧のCONTAINER IDだけを表示する。

docker container rm
Dockerコンテナの破棄。
実行中のコンテナは-fオプションをつけないと破棄できない。

docker container logs
Dockerコンテナの標準出力を表示する。-fオプションで標準出力の取得をし続ける。

docker container exec
実行中Dockerコンテナでのコマンド実行。

運用管理向けコマンド とは?

docker container prune
使われていないコンテナの一括廃棄。

docker image prune
イメージ版。

Docker Composeでマルチコンテナを実行する には?

Docker Composeを使う。
yaml形式の設定ファイルを使って、複数のコンテナの実行を一括管理できる。

Composeによる実行イメージ

$ docker container run -d -p 9000:8080 example/echo:latest

これと同じ内容をComposeに実行させてみる。
まずはyamlファイルを作成。

docker-compose.yml
version: "3"
services:
  echo:
    image: example/echo:latest
    ports:
      - 9000:8080

docker-compose up -pで実行する。

docker-compose downで、yamlファイルで定義しているコンテナをすべて停止・削除できる。

Composeによる複数コンテナの実行 には?

Jenkinsを使う。

Jenkins

継続的インテグレーションツール。
アプリケーションのコードが更新されたとき、自動的に環境のビルド・テストなどのデプロイ作業を実行する。

jenkins使ってみる

docker composeを使ってjenkins環境を立てる。
使用したyamlファイルは以下。

docker-compose.yml
  version: "3"↲
  services:↲
    master:↲
      container_name: master↲
      image: jenkinsci/jenkins:latest↲
      ports:↲
        - 8080:8080↲
      volumes:↲
        - ./jenkins_home:/var/jenkins_home↲

"jenkinsci/jenkins:2.142-slim"ではうまく行かなかった。

$ docker-compose up

localhost:8080でjenkinsの初期画面につながるようになるので、
ターミナルの途中で表示されてた初期パスワードで入る。

jenkinsの初期設定する。すごいしょうもないミスで詰まってたんだけど、
ユーザー設定画面で適当なメールアドレスつけてたら進めなかった。
address@mail.comとかだと進めた。そこ弾くのね・・・

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker/Kubernetes 実践コンテナ開発入門 part2

Dockerコンテナのデプロイ とは?

  • Dockerイメージをビルドする = Dockerfileの通りにテンプレートとなるイメージを構築すること

コンテナでアプリケーションを実行する には?

まずはDockerイメージからDockerコンテナを生成する。

Dockerイメージ

Dockerコンテナを作成するために使用されるテンプレートとなるもの。
- コンテナを構成するファイルシステム
- 実行するアプリケーション
- コンテナの設定
などがまとめられている。

公開されているものはdocker image pull "docker image name"で取得可能。

Dockerコンテナ

Dockerイメージから作成されるコンテナ。
具現化されたファイルシステムとアプリケーションが実行されている状態。
docker container runでdockerイメージを実行することで作成できる。

ポートフォワーディング

インターネットから特定のポート番号宛に届いたパケットを、予め設定しておいたLAN側の機器に転送する機能。

docker container runの時にオプションでこの機能を設定する(-p 9000:8080みたいな感じ)ことで、任意のポートからアプリケーションに到達できるようになる。

ディレクトリ構成

docker
 -Dockerfile
 -main.go
FROM golang:1.9

RUN mkdir /echo
COPY main.go /echo

CMD ["go", "run", "/echo/main.go"]

DSL

ドメイン固有言語

FROM

Dockerイメージのベースとなるイメージの指定
まずこれをダウンロードしてからイメージをビルドする
イメージはDocker Hubのレジストリにおいてある

RUN

Dockerイメージをビルドするときに、Dockerコンテナ内で実行するコマンド
今回はmkdir /echoがコンテナ内で実行される
ビルド時のアプリケーションの更新や配置を行う

COPY

ホストマシン(俺のmacのこと)上にあるファイルやらディレクトリやらを
Dockerコンテナ内にコピーする
今回はmain.go/echoディレクトリにコピーしてる

CMD

アプリを動かす時にコンテナ内で実行するプロセスを指定する
コンテナを動かす時にアプリケーションそのものを動作させる

要するに$go run /echo/main.goをコンテナの中のシェルで実行してるようなもん

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Println("received request")
        fmt.Fprintf(w, "Fuck You!!")
    })

    log.Println("start server")
    server := &http.Server{Addr: ":8080"}
    if err := server.ListenAndServe(); err != nil {
        log.Println(err)
    }
}
  • HTTPリクエストが飛んできたら「Fuck You!!」とレスポンスしてくる
  • 8080ポートでサーバアプリケーションとして動作する
  • クライアントからのリクエストに対して「received request」と標準出力に表示する

簡単なDockerコンテナをビルドしてみる

Dockerfileのあるディレクトリで以下を実行する

$ docker image build -t example/echo:latest .

Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM golang:1.9
 ---> ef89ef5c42a9
Step 2/4 : RUN mkdir /echo
 ---> Running in 4796403a4857
Removing intermediate container 4796403a4857
 ---> 7ee0156c2515
Step 3/4 : COPY main.go /echo
 ---> f0dcbce3f1df
Step 4/4 : CMD ["go", "run", "/echo/main.go"]
 ---> Running in bdee6808b43e
Removing intermediate container bdee6808b43e
 ---> 5e755d705882
Successfully built 5e755d705882
Successfully tagged example/echo:latest

Dockerfileに記述したコマンドが順に実行されている。
イメージリストを取得して、イメージが作成されているか確認する。

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example/echo        latest              5e755d705882        About a minute ago   750MB

ある。

Dockerコンテナを実行してみる


$ docker container run -d -p 9000:8080 example/echo:latest
c3381624d1290b8052e78ac44ea05ad998095cbafbd33ed952441807c1c141e8

-dオプションをつけてバックグラウンドで常駐させる。
また、-pオプションでポートフォワーディングしておかないとコンテナ外からアクセスできない。
この場合、ホスト側の9000番ポートをコンテナ内部の8080ポートにポートフォワーディング、つまり転送している。

試しにcurlでリクエスト投げてみる。

$ curl http://localhost:9000/

Fuck You!!%

無事にリクエストが帰ってきた。

実用的なコンテナの構築とデプロイ とは?

アプリケーションとコンテナの粒度 とは?

コンテナのポータビリティ とは?

Dockerフレンドリなアプリケーション とは?

永続化データをどう扱う?

コンテナ配置戦略 とは?

Swarmによる実践的なアプリケーション構築 とは?

Webアプリケーションの構成 とは?

MySQL Serviceの構築 には?

API Serviceの構築 には?

Nginxの構築 には?

Webの構築 には?

コンテナオーケストレーションによる開発スタイル とは?

Kubernetes入門 とは?

Kubernetesのデプロイ・コンテナ構築 とは?

Kubernetesの発展的な利用 とは?

コンテナの運用 とは?

より軽量なDockerイメージを作る とは?

Dockerのさまざまな活用方法 とは?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker で apt install -y がエラー "Unable to fetch some archives" で止まる

環境

Docker 18.09.7, build 2d0083d
コンテナ ubuntu:18.04

状況

下のような Dockerfile でビルドしたイメージを久々にアップデートしようと思って docker build -t <tag> . を実行しました。

Dockerfile
FROM ubuntu:18.04
ENV DEBIAN_FRONTEND noninteractive
RUN set -x \
  && rm -rf /var/lib/apt/lists/* \
  && apt-get update \
  && apt-get upgrade -y \
  && apt-get install -y apt-utils
RUN set -x \
  && apt-get install -y make

apt-get install -y make のところで、下記のようなエラーで止まってしまいました。

E: Failed to fetch http://security.ubuntu.com/ubuntu/pool/main/t/tzdata/tzdata_2019a-0ubuntu0.18.04_all.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm-common_2.4.95-1~18.04.1_all.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm2_2.4.95-1~18.04.1_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://security.ubuntu.com/ubuntu/pool/main/l/linux/linux-libc-dev_4.15.0-52.56_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.95-1~18.04.1_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm-intel1_2.4.95-1~18.04.1_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm-nouveau2_2.4.95-1~18.04.1_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libd/libdrm/libdrm-radeon1_2.4.95-1~18.04.1_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libglapi-mesa_18.2.8-0ubuntu0~18.04.2_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libgl1-mesa-dri_18.2.8-0ubuntu0~18.04.2_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libglx-mesa0_18.2.8-0ubuntu0~18.04.2_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/m/mesa/libgl1-mesa-glx_18.2.8-0ubuntu0~18.04.2_amd64.deb  404  Not Found [IP: 91.189.88.162 80]
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

解決策

このようなエラーは一般的に apt-get update をかけることで解決するようですが、今回はイメージの再ビルドでキャッシュが使われてしまったため、アップデートが行われていなかったようです。コマンドに --no-cache をつけて実行し直すと、先に進みました。

docker build --no-cache -t <tag> .
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python ✖︎ Flask ✖︎ Webアプリ(3)Docker登場 DBの準備

目的

  • Flask で使用する DB(PostgreSQL) を Docker を使用して準備する
  • DB を pgadmin4 で見れるようにする

本編

ソースコード

GitHubに上げています

環境準備

前回まで

Python ✖︎ Flask ✖︎ Webアプリ(2)HTMLの表示とメソッドとパラメータの受け取りかた
Python ✖︎ Flask ✖︎ Webアプリ(1)こんにちは世界

Docker 環境準備

Mac の場合

    brew cask install docker

ver 確認

    $ docker -v
      Docker version 18.09.1, build 4c52b90

フォルダ構成

$ tree
.
├── README.md
├── docker-compose.yml
├── form.html
├── pgadmin
├── postgresql
│   ├── data
│   └── init
│       └── 1_create_db.sql
├── requirements.txt
└── src
    ├── main.py
    └── templates
        └── hello.html

DB 作成

docker-compose.yml の作成

docker-compose.yml
version: "3"

services:

  postgresql:
    # イメージの指定
    image: postgres:10.5
    # コンテナの名前
    container_name: flask_tutorial_postgresql
    # hostのport5432とコンテナのport5432を繋ぐ
    # ホスト;コンテナ
    ports:
      - 5432:5432
    # hostとコンテナで共有するファイルやディレクトリを設定
    # ホストのディレクトリ;コンテナのディレクトリ
    volumes:
      # /docker-entrypoint-initdb.dはコンテナ初回起動時に実行されるスクリプトを置く場所
      - ./DB/init/:/docker-entrypoint-initdb.d
      # /var/lib/postgresql/dataはpostgresqlのデータが保存されている場所
      - /var/lib/:/var/lib/postgresql/data
    # コンテナの環境変数設定
    environment:
      # スーパユーザ名(省略時は"postgres")
      POSTGRES_USER: ${POSTGRES_USER}
      # スーパユーザのパスワード(省略時はパスワードなしでログイン可)
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      # postgresqlの初期化時の文字コード
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
    # ホスト名
    hostname: postgres
    # Dockerを実行するユーザ
    user: root
    environment:
      TZ: "Asia/Tokyo"

  pgadmin4:
    image: dpage/pgadmin4:3.3
    container_name: flask_tutorial_pgadmin4
    ports:
      - 80:80
    volumes:
      - ./pgadmin:/var/lib/pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
    depends_on:
          - postgresql
    hostname: pgadmin4

ポイント

${POSTGRES_USER}${POSTGRES_PASSWORD}${}は変数を表していて
docker-compose.ymlと同じ階層にある.envファイルで変数を指定できる。

.envの例

POSTGRES_USER=root
POSTGRES_PASSWORD=root
PGADMIN_DEFAULT_EMAIL=root
PGADMIN_DEFAULT_PASSWORD=root

コンテナ操作

docker-compose.ymlがあるディレクトリでdocker-compose up -d実行

コンテナ起動

$ docker-compose up -d
Creating network "first_tutorial_default" with the default driver
Creating flask_tutorial_postgresql ... done
Creating flask_tutorial_pgadmin4   ... done

コンテナが起動できたか確認

$ docker ps -a
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                         NAMES
487182403fc2        dpage/pgadmin4:3.3   "/entrypoint.sh"         8 seconds ago       Up 6 seconds        0.0.0.0:80->80/tcp, 443/tcp   flask_tutorial_pgadmin4
1441abbc6c72        postgres:10.5        "docker-entrypoint.s…"   9 seconds ago       Up 7 seconds        0.0.0.0:5432->5432/tcp        flask_tutorial_postgresql

コンテナを落とす

docker-compose.ymlがあるディレクトリでdocker-compose down実行

$ docker-compose down
Stopping flask_tutorial_pgadmin4 ... done
Removing flask_tutorial_pgadmin4   ... done
Removing flask_tutorial_postgresql ... done
Removing network first_tutorial_default

初回実行スクリプトについて

1_create_db.sql
create database flask_tutorial;

ポイント

  • ファイル名の先頭に数字を入れると、その順番で実行してくれる
    • 1_DB作成
    • 2_テーブル作成
    • 3_初期データ挿入

みたいな

  • 今回は単純にflask_tutorialというDBを作成しているのみ

DB 確認

pgadmin4にアクセス

docker-compose.ymlの一部抜粋
  pgadmin4:
    image: dpage/pgadmin4:3.3
    container_name: flask_tutorial_pgadmin4
    ports:
      - 80:80
    volumes:
      - ./pgadmin:/var/lib/pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
    depends_on:
          - postgresql
    hostname: pgadmin4

コンテナ起動を起動した状態でhttp://localhost:80にアクセスすると下記の画面が表示される

スクリーンショット 2019-07-14 1.27.53.png

ポイント

Email AddressPasswordは.env で設定した値

サーバ接続

pgadmin4にログインすると以下のように表示される。

スクリーンショット 2019-07-14 1.40.43.png

Serversを右クリック
Servers>作成>サーバ...を選択すると以下が表示されるので設定していく

スクリーンショット 2019-07-14 2.07.33.png
スクリーンショット 2019-07-14 2.08.31.png

ポイント

ホスト名/アドレスはホストのIPアドレスを調べて入力すること

DB確認

flask_tutorialというDBが存在していることを確認

スクリーンショット 2019-07-14 2.16.27.png

躓いた所

  • DBの初期化に失敗した場合(sql間違えたとか)はDB/dataのディレクトリを削除しないと、初期化が再度実行されない

次回

  • flaskでDBに接続してみよう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azureの仮想マシンで作ったDockerコンテナをラズパイに入れたい

高速な仮想マシンでコンテナを作って楽がしたい

DonkeyCarをやり始めて、早くも半年、インストールやコンパイルを繰り返していますが、
低速なラズパイでインストールに時間をかけるのが苦痛になってきました。

人間はもっと生産的な仕事をするべきと、今更ながら思いました。

使ったことのないMicrosoft Azureをあれこれしていて、不毛に時間を使いました。
でも、やりたいことは、

「仮想ラズパイマシンでDocker動かしてイメージ作れたらOKでしょ?」

これだけです。

仮想マシンを作る。

メモを書きながら実際にやってみます。

参考にしたのはこちら。
https://note.mu/kikuzokikuzo/n/na2e82c1c8835

先達に感謝!

作成する仮想ラズパイマシンは

  • 64bit ARM8プロセッサで動かす
  • 32bit RaspbianOS (Stretch). BurstはDonkeyCarが不安定らしい dockerを動かしてイメージビルド(qemuによるエミュレーションを利用)

私はAzureで仮想マシンを作ってみます。
チュートリアルに従って、粛々と作ることとします。(コマンドラインでやるのは面倒なのでポータルでやります)
https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/quick-create-portal

あっさり出来上がり。Azure Cloud Shellでsshで接続。

色々入れてみるも、決め手に欠く

なぜか、インストールでパスがないなどの異常が出ますね。

この辺りも参考にしてみたけど、RaspberryPi仮想マシン作るの実は需要なのか?
というか、そもそも難しく考えすぎか?
要はARM64の環境を作って、Raspbianのstretchをインストールしたら良いのでは?

http://matsujirushi.hatenablog.jp/entry/2018/04/15/214721
http://matsujirushi.hatenablog.jp/entry/2018/05/13/113219

ちょっと方針変換。

http://innossh.hatenablog.com/entry/2018/06/22/170734

仮想マシン上で、ラズパイ仮想マシンを作り、その中でDockerを動かせばよろしいのでは?

Donkey CarにはDockerfileが入っていた

ハッカーばかりでドキュメントが(一部)不親切なDonkeyCarのリポジトリにDokerfileが入っていました。
浜松のunagi.piでDonkeyCarソースコードを読む勉強会に参加したところ、これを実際に動かすことに成功した人がいました。

buildやcmakeの環境を色々考えると面倒ですが、Dockerfileからコンテナを作るだけなら行けそうです。

クラウドでインストールしても早くならない

https://www.slideshare.net/ShunsukeKikuchi1/raspberry-pi-147400803?from_m_app=ios

調べながらやっているうちに、実際にやって見た人発見。
どうやら浮動小数点演算をハードウェアでやっていないためと思われるスローダウンで、クラウドでやってもあまり高速にはならないそうだ。

ちょっと目先を変える

64bitのラズパイ4とburstが出るまで待ってから、実際にやってみよう。
JETSONの方がクラウドコンパイルとは相性が良さそう。

なので、raspberrypi4 + Azure を置いて、
JETSON nano + AWS で今から環境のセットアップをやってみることとします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azureの仮想マシンで作ったDockerコンテナをラズパイに入れたい(失敗)

高速な仮想マシンでコンテナを作って楽がしたい

DonkeyCarをやり始めて、早くも半年、インストールやコンパイルを繰り返していますが、
低速なラズパイでインストールに時間をかけるのが苦痛になってきました。

人間はもっと生産的な仕事をするべきと、今更ながら思いました。

使ったことのないMicrosoft Azureをあれこれしていて、不毛に時間を使いました。
でも、やりたいことは、

「仮想ラズパイマシンでDocker動かしてイメージ作れたらOKでしょ?」

これだけです。

仮想マシンを作る。

メモを書きながら実際にやってみます。

参考にしたのはこちら。
https://note.mu/kikuzokikuzo/n/na2e82c1c8835

先達に感謝!

作成する仮想ラズパイマシンは

  • 64bit ARM8プロセッサで動かす
  • 32bit RaspbianOS (Stretch). BurstはDonkeyCarが不安定らしい dockerを動かしてイメージビルド(qemuによるエミュレーションを利用)

私はAzureで仮想マシンを作ってみます。
チュートリアルに従って、粛々と作ることとします。(コマンドラインでやるのは面倒なのでポータルでやります)
https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/quick-create-portal

あっさり出来上がり。Azure Cloud Shellでsshで接続。

色々入れてみるも、決め手に欠く

なぜか、インストールでパスがないなどの異常が出ますね。

この辺りも参考にしてみたけど、RaspberryPi仮想マシン作るの実は需要なのか?
というか、そもそも難しく考えすぎか?
要はARM64の環境を作って、Raspbianのstretchをインストールしたら良いのでは?

http://matsujirushi.hatenablog.jp/entry/2018/04/15/214721
http://matsujirushi.hatenablog.jp/entry/2018/05/13/113219

ちょっと方針変換。

http://innossh.hatenablog.com/entry/2018/06/22/170734

仮想マシン上で、ラズパイ仮想マシンを作り、その中でDockerを動かせばよろしいのでは?

Donkey CarにはDockerfileが入っていた

ハッカーばかりでドキュメントが(一部)不親切なDonkeyCarのリポジトリにDokerfileが入っていました。
浜松のunagi.piでDonkeyCarソースコードを読む勉強会に参加したところ、これを実際に動かすことに成功した人がいました。

buildやcmakeの環境を色々考えると面倒ですが、Dockerfileからコンテナを作るだけなら行けそうです。

クラウドでインストールしても早くならない

https://www.slideshare.net/ShunsukeKikuchi1/raspberry-pi-147400803?from_m_app=ios

調べながらやっているうちに、実際にやって見た人発見。
どうやら浮動小数点演算をハードウェアでやっていないためと思われるスローダウンで、クラウドでやってもあまり高速にはならないそうだ。

ちょっと目先を変える

64bitのラズパイ4とburstが出るまで待ってから、実際にやってみよう。
JETSONの方がクラウドコンパイルとは相性が良さそう。

なので、raspberrypi4 + Azure を置いて、
JETSON nano + AWS で今から環境のセットアップをやってみることとします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerでデバッグ対象のコンテナにツールを入れずにtcpdump/straceなどを使うワンライナー

はじめに

Dockerであんなコンテナやこんなコンテナを動かしてると、なんかうまく動かなくて、デバッグのためにtcpdumpとかstraceなどのツールが使いたくなることが稀によくあります。
そんな時、デバッグ対象のコンテナ内にツールを一時的にインストールしちゃうというのが、まぁ簡単で分かりやすいんですが、デバッグ対象のコンテナを汚すのはできれば避けたいところです。

Dockerのコンテナの分離というのは、結局のところLinuxのリソースの名前空間の分離であるので、逆に同じ名前空間を共有すれば、デバッグ用に立てた隣のコンテナから、デバッグ対象のコンテナのネットワークやプロセスの状態を観察することも可能です。

また、docker buildはDockerfileを標準入力から受け取ることもできるので、ワンライナーにしてデバッグ用のコンテナをシュッと呼び出せるようにしてみました。

TL;DR

結論だけ知りたい人はこちらをどうぞ。
<target> のところはデバッグ対象のコンテナ名またはコンテナIDで読み替えて下さい。

tcpdump

echo 'FROM alpine\nRUN apk add --no-cache tcpdump' \
 | docker build -t debug -f - . \
 && docker run -it --rm --net container:<target> debug tcpdump -nn -X port 80

strace

$ echo 'FROM alpine\nRUN apk add --no-cache strace' \
| docker build -t debug -f - . \
&& docker run -it --rm --pid container:<target> --cap-add sys_ptrace debug strace -fp 1

ワンライナーでデバッグ用のツールをインストールして、デバッグ用のコンテナを立てる方法は、使いたいツールを変えればいろいろ応用が効くので、シェルのヒストリやスニペットに保存してシュッと呼び出すとよさそうです。

解説

環境

稼働確認した手元のDockerのバージョンは 19.03.0-rc2です。

$ docker version
Client: Docker Engine - Community
 Version:           19.03.0-rc2
 API version:       1.40
 Go version:        go1.12.5
 Git commit:        f97efcc
 Built:             Wed Jun  5 01:37:53 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.0-rc2
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.5
  Git commit:       f97efcc
  Built:            Wed Jun  5 01:42:10 2019
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          v1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

事前準備

ここではデバッグ対象として、適当なnginxのコンテナをtargetという名前で起動しておきます。

$ docker run -it --rm -p 8080:80 --name target nginx:alpine

tcpdumpを使ってみる

tcpdumpだけ入ったDockerfileを作って、debugというタグを付けてビルドします。

$ cat Dockerfile
FROM alpine
RUN apk add --no-cache tcpdump

$ docker build -t debug .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine
 ---> 3f53bb00af94
Step 2/2 : RUN apk add --no-cache tcpdump
 ---> Using cache
 ---> a000cadec8f5
Successfully built a000cadec8f5
Successfully tagged debug:latest

docker run --network container:<name|id> で対象のコンテナのネットワークにアタッチできます。

https://docs.docker.com/engine/reference/run/#network-settings

デバッグ用のコンテナをtargetのコンテナのネットワークにアタッチしつつ、tcpdumpを実行します。

$ docker run -it --rm --network container:target debug tcpdump -nn -X port 80                           tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

試しに手元からcurlしてみると

$ curl http://localhost:8080/

tcpdumpでパケットキャプチャできてることが分かります。

15:01:16.352790 IP 172.17.0.1.58354 > 172.17.0.2.80: Flags [S], seq 178914825, win 29200, options [mss 1460,sackOK,TS val 13258980 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c 56ed 4000 4006 8ba9 ac11 0001  E..<V.@.@.......
        0x0010:  ac11 0002 e3f2 0050 0aaa 0609 0000 0000  .......P........
        0x0020:  a002 7210 5854 0000 0204 05b4 0402 080a  ..r.XT..........
        0x0030:  00ca 50e4 0000 0000 0103 0307            ..P.........
15:01:16.352899 IP 172.17.0.2.80 > 172.17.0.1.58354: Flags [S.], seq 1290103227, ack 178914826, win 28960, options [mss 1460,sackOK,TS val 13258980 ecr 13258980,nop,wscale 7], length 0
        0x0000:  4500 003c 0000 4000 4006 e296 ac11 0002  E..<..@.@.......
        0x0010:  ac11 0001 0050 e3f2 4ce5 69bb 0aaa 060a  .....P..L.i.....
        0x0020:  a012 7120 5854 0000 0204 05b4 0402 080a  ..q.XT..........
        0x0030:  00ca 50e4 00ca 50e4 0103 0307            ..P...P.....

staceを使ってみる

同様にstraceをインストールしたDockerfileを用意してビルドします。

$ cat Dockerfile
FROM alpine
RUN apk add --no-cache strace

$ docker build -t debug .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine
 ---> 3f53bb00af94
Step 2/2 : RUN apk add --no-cache strace
 ---> Using cache
 ---> b357653376b3
Successfully built b357653376b3
Successfully tagged debug:latest

straceの場合は、 docker run --pid container:<name|id> で対象のコンテナのPID名前空間にアタッチします。

https://docs.docker.com/engine/reference/run/#pid-settings---pid

とりあえずpsを打ってみると、プロセスが見えてるのが分かります。

$ docker run -it --rm --pid container:target debug ps
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
    6 101       0:00 nginx: worker process
    7 101       0:00 nginx: worker process
    8 101       0:00 nginx: worker process
    9 101       0:00 nginx: worker process
   16 root      0:00 ps

ただstraceを起動してみると、権限エラーが出ます。

$ docker run -it --rm --pid container:target debug strace -fp 1
strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted

Dockerはデフォルトではいくつかの特権操作は許可されておらず、 straceには docker run --cap-add sys_ptrace が明示的に許可が必要です。

https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities

$ docker run -it --rm --pid container:target --cap-add sys_ptrace debug strace -fp 1
strace: Process 1 attached
rt_sigsuspend([], 8

こんどはうまくstraceが起動できました。ためしに nginx -s reload でプロセスにシグナルを送ってみましょう。

$ docker exec -it target nginx -s reload
2019/07/13 15:26:45 [notice] 87#87: signal process started

straceでシステムコールトレースができてることが分かります。

strace: Process 1 attached
rt_sigsuspend([], 8)                    = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=87, si_uid=0} ---
clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559042300}) = 0
rt_sigreturn({mask=[HUP INT QUIT USR1 USR2 ALRM TERM CHLD WINCH IO]}) = -1 EINTR (Interrupted system call)
clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559133400}) = 0
clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559208800}) = 0
clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559260400}) = 0
uname({sysname="Linux", nodename="976678564bed", ...}) = 0
open("/etc/nginx/nginx.conf", O_RDONLY) = 3

(参考) デバッグ用のイメージnicolaka/netshoot

毎回デバッグ用のツールをインストールするのがめんどくさければ、デバッグによく使うコマンドを全部入りしたnicolaka/netshootというDockerイメージがあります。

https://hub.docker.com/r/nicolaka/netshoot

何が入ってるかはDockerfileこれです。

https://github.com/nicolaka/netshoot/blob/master/Dockerfile

  • apache2-utils
  • bash
  • bind-tools
  • bird
  • bridge-utils
  • busybox-extras
  • calicoctl
  • conntrack-tools
  • ctop
  • curl
  • dhcping
  • drill
  • ethtool
  • file
  • fping
  • iftop
  • iperf
  • iproute2
  • iptables
  • iptraf-ng
  • iputils
  • ipvsadm
  • libc6-compat
  • liboping
  • mtr
  • net-snmp-tools
  • netcat-openbsd
  • netgen
  • nftables
  • ngrep
  • nmap
  • nmap-nping
  • openssl
  • py-crypto
  • py2-virtualenv
  • python2
  • scapy
  • socat
  • strace
  • tcpdump
  • tcptraceroute
  • util-linux
  • vim

ちなみにREADMEに貼ってある、どのレイヤを調査するのにどのツールが必要かの画像の元ネタはこれのようです。まじすげーなこのサイト。

引用元: http://www.brendangregg.com/linuxperf.html

image.png

ワンライナーを組み立てる

まぁ仕事で使ってる環境に公式イメージじゃない野良のイメージを使うのも若干抵抗があるので、自分がよく使うものだけ適当に入れたイメージを作って使うのがよいとは思います。

ただ個人的にはイメージのメンテすらあまりしたくないので、オンデマンドでビルドして使う技も思いつきました。
docker buildはDockerfileを標準入力から受け取ることもできるので、 tcpdumpの例をワンライナーで書くとこんなかんじです。

$ echo 'FROM alpine\nRUN apk add --no-cache tcpdump' \
 | docker build -t debug -f - . \
 && docker run -it --rm --net container:target debug tcpdump -nn -X port 80

Sending build context to Docker daemon  2.607kB
Step 1/2 : FROM alpine
 ---> 3f53bb00af94
Step 2/2 : RUN apk add --no-cache tcpdump
 ---> Using cache
 ---> a000cadec8f5
Successfully built a000cadec8f5
Successfully tagged debug:latest
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

straceだとこんなかんじでしょうか。

$ echo 'FROM alpine\nRUN apk add --no-cache strace' \
| docker build -t debug -f - . \
&& docker run -it --rm --pid container:target --cap-add sys_ptrace debug strace -fp 1

Sending build context to Docker daemon  2.607kB
Step 1/2 : FROM alpine
 ---> 3f53bb00af94
Step 2/2 : RUN apk add --no-cache strace
 ---> Using cache
 ---> b357653376b3
Successfully built b357653376b3
Successfully tagged debug:latest
strace: Process 1 attached
rt_sigsuspend([], 8

おわりに

ワンライナーでデバッグ用のツールをインストールして、デバッグ用のコンテナを立てる方法は、使いたいツールを変えればいろいろ応用が効くので、シェルのヒストリやスニペットに保存してシュッと呼び出すとよさそうです。

これであんなコンテナやこんなコンテナをデバッグし放題ですねー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker Private Registryのtagとrepositoryを削除する方法

  1. ヘッダーにAccept: application/vnd.docker.distribution.manifest.v2+jsonを追加して、Get /v2/<イメージ名>/manifests/<タグ>でマニフェストを取得(https://docs.docker.com/registry/spec/api/#pulling-an-image)

  2. レスポンスのヘッダーに付いてくる[Docker-Content-Digest]を確認。

  3. Delete /v2/<イメージ名>/manifests/の実行。

  4. registryコンテナ本体でbin/registry garbage-collect /path/to/config.ymlを実行してガーベッジコレクションを起動。
    https://docs.docker.com/registry/garbage-collection/

  5. 対象のrepositoryディレクトリを手動削除。

準備

Amazon Linux release 2 (Karoo)

# amazon-linux-extras install -y docker=18.03.1のようにバージョン指定でインストールも可能
$ sudo amazon-linux-extras install -y docker

# dockerサービス起動
$ sudo systemctl start docker

# DockerはUnixソケットをTCPポートにバインドするため、
# rootユーザーでしか動作しません。sudoコマンドを
# 使いたくないという場合は、dockerグループを作成して
# そこにユーザーを追加してください
# なお、sudoコマンドでdockerコマンドを実行できるように
# するには、dockerグループにユーザー追加後にログオフして
# 再度ログインする必要があります
$ sudo usermod -a -G docker $USER

# 確認コマンド
$ cat /etc/group | grep docker

# dockerサービスの自動起動を有効にする
$ sudo systemctl enable docker
# Docker Private Registryコンテナ起動
$ docker run -d --name testreg \
  -e REGISTRY_STORAGE_DELETE_ENABLED=true \
  -p 5000:5000 registry:2

# Docker Private Registryにalpineのイメージを登録
$ docker pull alpine:latest
$ docker tag alpine:latest 127.0.0.1:5000/myrepo:tag1
$ docker push 127.0.0.1:5000/myrepo:tag1

# 全てのカタログとタグのリスト取得
$ curl http://127.0.0.1:5000/v2/_catalog

{"repositories":["myrepo"]}

# 対象のタグリスト取得
$ curl http://127.0.0.1:5000/v2/myrepo/tags/list

{"name":"myrepo","tags":["tag1"]}

# 対象のタグ削除
$ digest=$(curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -sI http://127.0.0.1:5000/v2/myrepo/manifests/tag1 | grep Docker-Content-Digest | tr '\r\n' ' ' | awk '{print $NF}')

$ curl -X DELETE "http://127.0.0.1:5000/v2/myrepo/manifests/$digest"

# 対象のタグ削除確認
$ curl http://127.0.0.1:5000/v2/myrepo/tags/list

{"name":"myrepo","tags":null}

# config.yml作成
$ sudo mkdir /etc/docker/registry 
$ sudo vi /etc/docker/registry/config.yml
/etc/docker/registry/config.yml
version: 0.1
storage:
  filesystem:
    rootdirectory: /var/lib/registry
# ガーベッジコレクション実行
# マニフェストによって参照されなくなったファイルシステムからBLOBを削除
$ docker exec -it testreg registry garbage-collect /etc/docker/registry/config.yml

0 blobs marked, 3 blobs and 0 manifests eligible for deletion
blob eligible for deletion: sha256:57334c50959f26ce1ee025d08f136c2292c128f84e7b229d1b0da5dac89e9866
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/57/57334c50959f26ce1ee025d08f136c2292c128f84e7b229d1b0da5dac89e9866  go.version=go1.11.2 instance.id=8c036012-29fa-42a4-bdd8-e228153fbfba service=registry
blob eligible for deletion: sha256:b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/b7/b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9  go.version=go1.11.2 instance.id=8c036012-29fa-42a4-bdd8-e228153fbfba service=registry
blob eligible for deletion: sha256:0503825856099e6adb39c8297af09547f69684b7016b7f3680ed801aa310baaa
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/05/0503825856099e6adb39c8297af09547f69684b7016b7f3680ed801aa310baaa  go.version=go1.11.2 instance.id=8c036012-29fa-42a4-bdd8-e228153fbfba service=registry

# Docker Private Registryのコンテナ再起動
$ docker restart testreg

# repositoriesディレクトリ確認
$ docker exec -it testreg ls /var/lib/registry/docker/registry/v2/repositories

myrepo

# 対象のrepositoriesディレクトリを削除
$ docker exec -it testreg rm -rf /var/lib/registry/docker/registry/v2/repositories/myrepo

# 全てのカタログとタグのリスト取得
$ curl http://127.0.0.1:5000/v2/_catalog

{"repositories":[]}

# 対象のタグリスト取得
$ curl http://127.0.0.1:5000/v2/myrepo/tags/list

{"errors":[{"code":"NAME_UNKNOWN","message":"repository name not known to registry","detail":{"name":"myrepo"}}]}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む