20191221のdockerに関する記事は14件です。

コンテナ内でcontainerdを動かす

この記事はDocker Advent Calendar 2019の21日目の記事です。

Dockerが内部で使用しているHigh-levelランタイムのcontainerdをコンテナ内で起動して動かしてみた話です。

コンテナ内で動かす方法に関してはcontainerdのリポジトリにそのやり方が記載されていますが、その通りにやっても私の環境では一部上手くいかなかったのでその解決策も含めて紹介できればと思います。

環境

  • OS: ubuntu 16.04
  • golang: go1.13
  • Docker: 19.03.5
  • containerd: v1.3.0-203-g97712c8a 97712c8ad73dd302d5d226e0384b2d86e5de2989

ソースの取得

containerdを動かすに当たってソースからバイナリを作成するのでローカルにソースを取得しておきます。
contaierdの他にlow-levelランタイムであるruncも今回は一緒にビルドするので合わせて取得します。

取得方法はgo get -u github.com/containerd/containerdgo get -u github.com/opencontainers/runcを実行すればOKです。

Dockerイメージの作成

ソースの取得が終わったら次にcontainerdを動かすコンテナのDockerイメージを作成します。
containerdはGo言語を使用して作られているので、Dockerイメージのgolangをベースに、ビルドするのに必要なモジュールをインストールしていきます。
必要なモジュールはB-Tree FileSystem用のbtrfs-toolsとSeccomp用のlibseccomp-devの二つです。

Dockerfile
FROM golang:latest

RUN apt-get update \
    && apt-get install -y btrfs-tools libseccomp-dev

あとはdocker build -t containerd/build .でDockerイメージを作成すればコンテナ用のDockerイメージの準備は完了です。

コンテナの起動

上記で作成したDockerイメージを使ってコンテナを起動します。

docker run -it --privileged \
    -v /var/lib/containerd \
    -v ${GOPATH}/src/github.com/opencontainers/runc:/go/src/github.com/opencontainers/runc \
    -v ${GOPATH}/src/github.com/containerd/containerd:/go/src/github.com/containerd/containerd \
    -e GOPATH=/go \
    -w /go/src/github.com/containerd/containerd containerd/build bash

containerdの起動には特権が必要になるので--privilegedオプションをつけ、起動に必要なフォルダと上記で取得したソースをマウントした上で起動しています。
そのほかのオプションはGOPATHの設定とコンテナ起動時のディレクトリを指定しています。
公式のやり方ではbashではなくshを起動しているのですが、個人的にbashの方が慣れているので変えています。

ソースのビルド

次はコンテナ内でcontainerdruncをビルドしていきます。
それぞれMakefileが用意されているのでそれを利用します。

// containerdのフォルダに移動。
// コンテナ起動時にこのフォルダがカレントディレクトリになっているはずなので実行しなくても大丈夫
$ cd /go/src/github.com/containerd/containerd 
$ make && make install

// エラー!
vendor/github.com/containerd/btrfs/btrfs.go:38:25: fatal error: btrfs/ioctl.h: No such file or directory
#include <btrfs/ioctl.h>
^
compilation terminated.
make: *** [bin/containerd] Erreur 2

実はこの状態でビルドするとエラーとなります。containerdが依存しているcontaienrd/btrfsというモジュールがあり、そのモジュールからbtrfsがないと言われるのが原因です。

Dockerイメージを作る際にbtrfs-toolsをインストールしましたが、どうもこれでは駄目なようでbtrfs-toolsの代わりにlibbtrfs-devをインストールすればビルドが通りました。

Dockerfile
FROM golang:latest

RUN apt-get update \
    && apt-get install -y libbtrfs-dev libseccomp-dev

公式に記載されているやり方と異なるので問題がある可能性もありますが、一旦これでDockerイメージを作成しなおしてコンテナを起動すればビルドは通るようになります。

あとはcontainerdと同様にruncもビルドしてやれば準備完了です。

cd /go/src/github.com/opencontainers/runc
make BUILDTAGS='seccomp apparmor' && make install

containerdの起動

make installした段階でcontainerdのバイナリがPATHの通っているディレクトリに配置されますのでそれを起動すればcontainerdが動きます。

$ containerd // バックグラウンドで起動したい場合は containerd &

$ INFO[2019-12-21T13:56:32.716026618Z] starting containerd ...
$ INFO[2019-12-21T13:56:32.760340925Z] loading plugin ...
// 以下ログが出力される

これでやっとcontainerdが動かせました!
あとは好きなようにいじりたおせばOKですね。

まとめ

簡単ですがコンテナ内でcontainerdを動かすやり方を紹介させてもらいました。

そもそも何故動かそうと思ったかというと、個人的にコンテナランタイムの開発に挑戦していてDockerで動かしてみたら上手く動かず原因を探っていくとcontainerdとのやりとりに問題がありそうだという事が分かったので、containerdを動かして見ようと思ったのがきっかけでした。

ただ起動するだけでなく構成を記述した設定ファイルを渡す事もできるようなので、その辺りも公式に情報があるのでよければそちらも見てみて下さい。

参考

containerd - Build
Getting started with containerd

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

CentOS8のDockerイメージで送信専用SMTPサーバー

はじめに

以前投稿した「Dockerで送信専用メールサーバーなうに使っていいよ。」はCentOS7ベースのDockerイメージでした。

今回、CentOS8ベースに対応させたので記念投稿です。
https://hub.docker.com/r/takeyamajp/postfix

CentOS7版からの変更点

  • Microsoftなどビッグネームのトレンドに追従して証明書の暗号化レベルを引き上げました。
  • postfixのバージョン変更に伴ってnewaliasesの整合性チェックが厳しくなっているので、警告が出ないように設定方法を変更しました。
  • rsyslogのバージョン変更に伴って、docker向けの設定を変更しました。
  • supervisorのバージョン変更に伴って、docker向けの設定を変更しました。

その他

今後centos8ベースのDockerfileを作る人に、rsyslogやsupervisorを使う場合の参考になればと思います。
知らない人は、必ずハマりますからね。

Docker hubで星を付けて頂けると励みになります。

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

wardenを利用して Magento2ローカル開発環境を構築する

本記事は、Magento Advent Calendar 21日目の記事となります。

本記事では、巷で話題となっているMagentoローカル開発環境を容易に構築できる、WardenというCLIツールの使い方を紹介したいと思います。

既に実用的でかつ大変便利なツールではあるものの、最近ではAdobe Magento社のDeveloperからもプルリクエストが飛んでいるようで、現在進行形で爆速進化中といった様子です。

Wardenがやってくれること。

・SSLの終端制御とProxy/Routingによる、コンテナへのルーティング
・.testドメインに対するDNSレスポンス
・ローカル環境でのSSL証明書の発行
・Magento2インスタンスの構築

仕組み

Wardenは下記の構成で管理されています。

メインのWarden用コンテナが4つ。 Magentoインスタンス用のコンテナが9つ。

◆メイン

・Dnsmasq ・・・ *.testドメインへの名前解決を担当。 (/etc/hostsを手動で変更する必要がなくなる)
・Traefik ・・・ アクセスされたドメイン名を元に、各MagentoインスタンスのVarnishコンテナにルーティング。
・Portainer ・・・ DockerコンテナをGUIから操作できる便利ツール (放置気味。。)
・Sshd ・・・ MysqlWorkBenchなどからコンテナにアクセスするための、SSHトンネル用コンテナ

◆Magento(持て余すほどのフル構成)

  • Varnish (ルーティング用にTraefie用のラベルが付けられている)
  • Nginx (Cookieの値をみて、PHP-FPMを使い分ける設定がされている)
  • PHP-FPM (n98-magerunが標準装備・Mailhogの設定済)
  • PHP-FPM(Xdebug用)
  • MariaDB (DockerHubの公式イメージ)
  • Redis (Session・Cacheの保存用)
  • ElasticSearch (検索・インデックス)
  • RabbitMQ (非同期のメッセージングサービス)
  • MailHog (メール送信テスト環境)

図にまとめてみるとこのような構成。

warden.png

基本的な動き

ユーザからのhttpsアクセスに対して、dnsmasqコンテナでドメインの名前解決を行い、
Varnishに付与されているラベルを元に、traefikコンテナで各Magentoインスタンスへのリクエストのルーティングを行なっています。

Magentoインスタンスをいくつ増やしても、traefikが自動的にコンテナの生成を検知し、リクエストのルーティングを行ってくれるため、前段のdnsmasq、traefikを一切管理しなくて良いのが良いところ。

xdebugの有効/無効の切り替えが容易

wardenでは、php-fpmコンテナを常時二つ稼働させており、片方はxdebugの設定がされています。

CookieにXDEBUG_SESSIONキーが挿入されていると、php-fpm(xdebug用)が使用されるようにnginxで制御されています。

php-fpm.png

Mailhogが捗る

デフォルトでPHP-FPMにMailhogの設定がされているため、Warden開発環境で送信されるメールは全てMailhogにプロキシーされます。

プロキシーされたメールは、https://mailhog.{project名}.test/ から確認可能。

使ってみるとこれがかなり便利。

スクリーンショット 2019-11-22 10.05.26.png

wardenの使い方↓

使い方 (mac向け)

まずはwardenのインストールと起動。

$brew install davidalger/warden/warden
$warden up

warden upコマンドで上記のメインとなる4コンテナが起動します。

$docker ps
CONTAINER ID        IMAGE                                   COMMAND                   CREATED             STATUS              PORTS                                                 NAMES
b4e8029d5655        jpillora/dnsmasq                        "webproc --config /e…"    23 hours ago        Up 23 hours         127.0.0.1:53->53/udp                                  dnsmasq
0030d07b863f        traefik:v1.7                            "/traefik --api --do…"    23 hours ago        Up 20 hours         127.0.0.1:80->80/tcp, 127.0.0.1:443->443/tcp          traefik
e671dabdd02e        portainer/portainer                     "/portainer"              23 hours ago        Up 23 hours         9000/tcp                                              portainer
ede5a56b6fef        panubo/sshd                             "/entry.sh /usr/sbin…"    23 hours ago        Up 23 hours         127.0.0.1:2222->22/tcp                                tunnel

正しく起動できていれば、下記URLでコンテナの稼働状況が確認できます。

Wardenの起動が完了したら、次はMagentoインスタンスを立ち上げていきます。

1.Magentoのソースコードが入ったディレクトリを作成

$composer create-project --repository-url=https://repo.magento.com/magento/project-community-edition magento2

2.環境設定の初期化: warden env-init [ProjectName] [EnvironmentType]
カレントディレクトリに、warden設定ファイルとなる.envが作成されます。

warden env-init project magento2

EnvironmentTypesは下記の3つがデフォルトで用意されています。(EnvironmentTypeは追加可能な様子)

  • local
  • Magento1
  • Magento2

3.SSL証明書の作成・設定: warden sign-certificate [ProjectName].test

warden sign-certificate project.test

4.Magentoインスタンスの起動

warden env up -d
warden sync start

5.コンテナが立ち上がったら、php-fpmコンテナに入ってMagentoのインストール

$warden shell (php-fpmコンテナに入る。⇩コンテナ内で実行)
$bin/magento setup:install \
    --backend-frontname=backend \
    --amqp-host=rabbitmq \
    --amqp-port=5672 \
    --amqp-user=guest \
    --amqp-password=guest \
    --db-host=db \
    --db-name=magento \
    --db-user=magento \
    --db-password=magento \
    --http-cache-hosts=varnish:80 \
    --session-save=redis \
    --session-save-redis-host=redis \
    --session-save-redis-port=6379 \
    --session-save-redis-db=2 \
    --session-save-redis-max-concurrency=20 \
    --cache-backend=redis \
    --cache-backend-redis-server=redis \
    --cache-backend-redis-db=0 \
    --cache-backend-redis-port=6379 \
    --page-cache=redis \
    --page-cache-redis-server=redis \
    --page-cache-redis-db=1 \
    --page-cache-redis-port=6379

6.各アプリケーションにアクセス

7.Magentoインスタンスの破棄

$warden env down -v #コンテナ・ボリューム・ネットワークの削除
$warden sync stop  #Mutagenセッションの終了

使用感

MailhogやX-Debug切り替えなど、細かい開発支援機能が散りばめられており、かなり便利な印象。

ファイル共有にMutagenを使用しており、Docker for MacでもVagrant使用時と変わらない速度感。

EnvironmentTypeを新たに追加することで、独自のMagentoインスタンス用のコンテナ群を構築することが可能です。

今回は触れませんでしたが、使用するPHPやVarnishのバージョンを指定することも出来るようです。

また、プロジェクト毎に使用したいコンテナを差し替えるといったことも出来るようになっているので、柔軟な開発環境が構築できそうです。

おまけ

MysqlWorkbenchの設定方法

MysqlWorkbenchの設定方法が少し分かりずらかったので、念のため共有させて頂きます。

mysql.png

SSH Hostname: tunnel.warden.test:2222
SSh Username: user
SSh Password: なし
SSH Key File: $HOME/.warden/tunnel/ssh_key
Mysql Hostname: project_db_1 (プロジェクトのdbコンテナ名)
Mysql ServerPort:3306
Username : magento
Password : magento
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio Code - "Remote Development" を使って Docker Container on "Vagrant + VirtualBox" のファイルを編集する

要約

  • Vagrant で構築した仮想環境の中にある Docker へ繋げるときは 多段SSH を使う。
  • Docker Container に SSHサーバ を構築する必要がある。
  • VS Docde の Remote Development は Remote SSH を使用する。

はじめに

VS Code に Remote Development と呼ばれる拡張機能が追加されました。

数ヶ月遅れでその機能の素晴らしさに気づき、その活用方法をメモしておこうと記事を作成します。

基本的な用途は以下のクラスメソッドさんの記事が分かりやすいので参照ください。

【設定爆速】VS CodeのRemote Developmentを使ってSSH接続したEC2上のファイルを編集する

概要

書くこと

  • VS Code Remote Development の活用方法
    • 多段SSH を用いて、2つ先の仮想環境に繋げる方法
  • docker container を作るポイント
  • ホストOS の設定( .ssh/config

書かないこと

  • Vagrant で Docker / docker-compose の環境を作る方法

想定環境

この記事で実現するシステムの構成は下図の通りです。

image.png

上図の通り、VS Code の Remote Development を使用して、リモート環境のコードを VS Code の UI で直接編集出来るようにします。

また、多段SSH の仕組みを用いて、1つ目の仮想環境の先に存在する Docker コンテナ に対しても同じ仕組みでコードの編集が出来るようにします。

今回の仕組みを実現した環境の詳細は以下の通りです。

Item Version
HostOS Windows 10
VirtualBox 5.2.18 r124319 (Qt5.6.2)
command
> vagrant -v
Vagrant 2.1.2

> vagrant plugin list
vagrant-disksize (0.1.2)
vagrant-docker-compose (1.5.1)
vagrant-omnibus (1.5.0)
vagrant-proxyconf (1.5.2)
vagrant-vbguest (0.15.2)

> vagrant box list
centos/7         (virtualbox, 1905.1)
command
$ rpm -qa kernel\* | sort
kernel-3.10.0-1062.9.1.el7.x86_64
kernel-3.10.0-957.12.2.el7.x86_64
kernel-devel-3.10.0-1062.9.1.el7.x86_64
kernel-headers-3.10.0-1062.9.1.el7.x86_64
kernel-tools-3.10.0-1062.9.1.el7.x86_64
kernel-tools-libs-3.10.0-1062.9.1.el7.x86_64

Remote Development が実現すること

「ローカルPCのコードを修正して、それで十分?」

近年のソフトウェア開発の現状として、次のような傾向があります。

  • 特定のコンテナやVMでのみでアプリケーションが動作する。
  • ローカルでは実現出来ない実行環境が多くある。

こういった現状の中で、ローカルPCでソースコードのみ修正出来たとしても、以下のようなケースにおいては、手間が増えてしまいます。

  • 実行環境へのデプロイが必要である。
  • IDEが直接コードを実行することで実現出来るようや動的解析機能のため、ローカルPCにも開発キットの導入が必要なケースがある。

例えば次の図のように、コンテナ上に PHP の実行環境を作って動作出来るようにした場合を考えます。
コンテナ上のアプリケーションは意図した通りに動くでしょうが、 VS Code で静的解析や動的解析を行いたい場合、別途、Host OS や ローカルPC上の VS Code に手を加える必要が出てきます。

image.png

また、Web Application をチーム開発する場合には、Docker などで共通化した仮想環境を作成して配布することが多いと思いますが、ローカルPCで別途設定が必要となると、全てが共通化出来ているとは言えず、面倒な手間も増えてしまいます。

リモート環境のコンテクストで、リッチなローカル開発環境の提供

VS Code の Remote Development を使用することで ローカルPC 上の VS Code を使用して、リモート環境にあるコードを違和感なく編集することが出来ます。

image.png
cited from Remote Development with VS Code - Visual Studio Code

3つの拡張機能

Remote Development は、次の3つリモート環境に対応しています。

  • remote workspaces running in WSL
  • remote workspaces running in Docker containers
  • remote workspaces running in physical and virtual machines over SSH

image.png
cited from Remote Development with VS Code - Visual Studio Code

Architecture and extension

Remote Development は、大きく別けて以下の2つの extension から構成されています。

  • UI Extensions
  • Workspace Extensions

この2つは、下図のようなアーキテクチャで構成されています。

image.png
cited from Supporting Remote Development and Visual Studio Online - Visual Studio Code

実現のイメージ

今回の記事では、次のように、ホストOS の VS Code から 仮想環境のコードを編集出来るようにすることを目指します。
特に、仮想環境の上にある Docker Container を、同じ仕組みでどのように実現するかがポイントです。

image.png

設定手順

設定を行うにあたって、 Docker や docker-compose の導入以外で特徴的な作業としては次の3つがあります。

  1. Docker Container に sshd サーバを構築し、 root ユーザでログイン出来るようにする。
  2. Docker Container 起動時に Port Forward 設定を行う。
  3. Host OS に ssh/config 設定を追加する。

これらを図で示すと以下のようになります。

image.png

この設定を行うことで、 HostOS に存在する VS Code の Remote Development を使用して、2つ先の Docker Container に対して Remote SSH 接続を行うことが出来ます。

1. Vagrant 環境の構築

まず最初に、 Docker と docker-compose をインストールした 仮想環境を構築します。
この手順は本筋ではないので、次の手順で構築してください。
(設定内容は anfangd/docker-on-vagrant-script - GitHub を参照ください。)

command(powershell)
## Vagrant のプラグインをインストールする
> vagrant plugin install vagrant-vbguest
> vagrant plugin install vagrant-proxyconf

## GitHub から Vagrantfile と provision.sh を取得する
> git clone https://github.com/anfangd/docker-on-vagrant-script.git
> cd docker-on-vagrant-script

## Vagrant の起動する
> vagrant up

vbguest に関連するエラーが発生した場合は以下を参照してください。
(kernel version のミスマッチによってエラーが発生する可能性があります。)

2. .ssh/config の設定(その1)

次のコマンドで、Vagrant で起動している仮想環境の ssh-config を確認することが出来ます。

command(powershell@Windows10)
> vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile C:/Users/<username>/work/vagrant/centos-7-docker/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

上記のコマンドで確認した内容を、 .ssh/config にコピペします。
なお、Host名は任意の名前に変更してください。

これらは Windows 10 向けの設定のため、macOS などの場合は適宜 PATH の修正などを行ってください。

.ssh/config(Windows10)
Host vagrant-os
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile C:/Users/<username>/work/vagrant/centos-7-docker/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

3. 秘密鍵の転送

Guest OS から Docker に接続するために、 Vagrant が自動生成した 公開鍵のペアを使用します。
Host OS 上に秘密鍵が存在するので、 Guest OS へ秘密鍵をコピーします。

command(powershell@Windows10)
## 注意 `vagrant up` したディレクトリで実行する
> scp .vagrant/machines/default/virtualbox/private_key vagrant-os:/home/vagrant/.ssh/

## Guest OS に SSH接続
> vagrant ssh

## private_key の実行権限を変更(Guest OS 内)
$ chmod 600 ~/.ssh/private_key

4. Docker Container の起動

Docker を実行出来る仮想環境の構築が出来たら、 Docker Container を構築します。
以下は今回使用する Dockerfile です。

Dockerfile
# image
FROM php:7.0.15-fpm

ENV LANG C.UTF-8

RUN apt-get update -qq && \
    apt-get install -y \
        zlib1g-dev \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        && docker-php-ext-install zip \
        && yes "" | pecl install xdebug \
        && docker-php-ext-enable xdebug \
        && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
        && docker-php-ext-install -j$(nproc) gd

RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql

RUN apt-get update \
    && apt-get install -my wget gnupg

WORKDIR /opt/

# ここから最後までがポイント
RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd

## /etc/ssh/sshd_config の設定書き換え
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

# 公開鍵をコピー
COPY authorized_keys /root/authorized_keys

EXPOSE 22

# Activate authorized_keys & Boot sshd
CMD mkdir ~/.ssh && \
    mv ~/authorized_keys ~/.ssh/authorized_keys && \
    chmod 0600 ~/.ssh/authorized_keys &&  \
    # 最後に ssh を起動
    /usr/sbin/sshd -D

上の Dockerfile のうち、後半部分で openssh-server の導入や 公開鍵認証の設定などについて記載しています。

Docker Container の起動は次のコマンドで実行します。

command(bash@CentOS)
$ pwd
/vagrant/docker-sample

# 公開鍵をコピー
$ cp ~/.ssh/authorized_keys .

$ ls
Dockerfile authorized_keys

# build
$ docker build ./ -t example

# Docker Container を起動
# Port:10000 を Port:22 に転送
$ docker run -d -p 10000:22 example

# Docker Container に SSH 接続
$ ssh root@127.0.0.1 -p 10000 -i ~/.ssh/private_key

5. .ssh/config の設定(その2)

2.) で設定した .ssh/config に設定を追加します。

.ssh/config(Windows10)
Host vagrant-os
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile C:/Users/<username>/work/vagrant/centos-7-docker/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

# 以下を追加
Host docker-os
  Hostname 127.0.0.1
  User root
  Port 10000
  ProxyCommand C:\Windows\System32\OpenSSH\ssh.exe -W %h:%p vagrant-os
  IdentityFile C:/Users/<username>/work/vagrant/centos-7-docker/.vagrant/machines/default/virtualbox/private_key

上記の設定で Docker Container に接続出来るか確認してみましょう。

command(powershell@HostOS)
> ssh docker-os

これで接続できれば設定は完了です。

6. Visual Studio Code の設定

前節までの設定が出来てしまえば、あとは以下の記事の通り extensions をインストールして開けば使用出来ます。

以下のように、CentOS へのアクセスを示す vagrant-os に加え、その上の Docker Container である docker-os も開けるようになっています。

image.png

extra-1. 【HostOS←→GuestOS】 のフォルダ同期を停止する

Vagrant で構築した GuestOS のフォルダと HostOS のフォルダを同期する方法は、非常に便利なようで厄介な点がいくつかあります。
私は以下のような事象で時間を取られることがよくあります。

  • Vagrant や Box のバージョンアップで頻繁にエラーが発生する。
  • GuestOS 内の Webサーバと通信する時、HTTP アクセスが遅くなる。
  • npm install する時にエラーが発生する。

ただ、今回の記事で使用した VS Code - Remote Development を使用すれば、ファイル同期の必要がなく、仮想環境内のファイルを直接修正出来るのでこのファイル同期の仕組みが不要になります。

同期をしないようにするには、以下のように Vagrantfile を修正して vagrant reload すれば良いだけなので非常に簡単です。

Vagrantfile(@HostOS)
<略>
  # Synced Folder
  #config.vm.synced_folder "./", "/vagrant", type:"virtualbox", mount_options: ['dmode=777','fmode=777']
  config.vm.synced_folder ".", "/vagrant", disabled: true
<略>

一方で、これをしてしまうと、これまで当たり前に出来ていたことが一部できなくなってしまいます。
想定されるケースと対策で重要そうなものを以下に記載します。

  • GuestOS にファイルを配置する。
    →scp コマンドなどで転送する。
  • 仮想環境と同期しているフォルダの git リポジトリを Sourcetree で参照していた場合の対処法
    →VSCode の拡張機能で代替する。
  • Vagrant の Box を誤って削除してしまう。
    →HostOSとファイル同期してないので、中身は消えてしまいます。
     こまめに commit & push して、Vagrant Box の環境は削除される前提で作業しましょう。

おわりに

多段SSH方式、めちゃくちゃ便利ですね。
Remote Container で実現する方法を模索して時間を浪費してしまったのですが、多段SSH方式でスムーズに実現することが出来ました。

そして何より、 Visual Studio Code さん、どんどん使いやすさが増してゆきます。

TODO

参考

Vagrant + VirtualBox で Docker 環境を構築する。

Vagrant で共有フォルダ設定でエラーが出来た時の対処法

※ Remote Development 機能の出現によって、この共有をしない方が開発が効率的になる可能性有り。

多段SSHの実現方法

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

corcelを使ってdockerコンテナ間でlaravelからwordpressのDBに接続する

corcelの事前設定

下記を参考に進めていきます。
かなり巻いてます(師走だからね)。

https://github.com/corcel/corcel

まずはcorcelをインストール

composer require jgrossi/corcel

laravelのバージョンが5.4以下は下記を記載

config/app.php
'providers' => [
    /*
     * Package Service Providers...
     */
    Corcel\Laravel\CorcelServiceProvider::class,
]
php artisan vendor:publish --provider="Corcel\Laravel\CorcelServiceProvider"

databaseにwordpressの接続設定を記載。

config/database.php
    'wordpress' => [
        'driver'    => 'mysql',
        'host'      => 'localhost',
        'database'  => 'mydatabase',
        'username'  => 'admin',
        'password'  => 'secret',
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => 'wp_',
        'strict'    => false,
        'engine'    => null,
    ],

コネクションを'wordpress'へ変更

config/corcel.php
<?php

return [
(省略)
    'connection' => 'wordpress',
(省略)
];

WpPostモデルを作成します。

php artisan make:model WpPost
WpPost.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Corcel\Model\Post as Corcel;

class WpPost extends Corcel{
   protected $connection = 'wordpress';
}

wordpressとlaravelのdockerの設定

事前に作成済みのwordpressのdocker-compose.ymlファイルの末尾に下記を記述

wordpress側docker-compose.yml
networks:
  default:
    external:
      name: new_network
laravel側docker-compose.yml
networks:
  default:
    external:
      name: new_network

createコマンドでnew_networkを作ります。

docker network create new_network

docker networkコマンドで確認します。

docker network ls
xxxxxxxxxx        my_network    bridge              local

dockerコンテナを立ち上げます。

docker-compose up

laravel側のdatabase設定を修正

dockerコンテナ内のwordpress-mysqlにログインし、アドレスを確認します。

hostname -i
192.168.1.2

laravelのdatabase.phpの設定内容を変更します。
それぞれwordpressのwp-config.phpの設定を元に書き換えていってください。

config/database.php
    'wordpress' => [
        'driver'    => 'mysql',
        'host'      => 192.168.1.2,
        'database'  => 'mydatabase',
        'username'  => 'wordress_root',
        'password'  => 'wordpress_password',
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => 'wp_',
        'strict'    => false,
        'engine'    => null,
    ],

wordpressのDBに接続

laravelのdockerコンテナにログインし、wordpressのDBに接続します。

php artisan tinker
>>> App\WpPost::find(1)
=> App\WpPost {#5963
     ID: 1,
     post_author: 1,
     post_date: "2017-10-20 12:06:00",
     post_date_gmt: "2017-10-20 03:06:00",
     post_content: "WordPress へようこそ。これは最初の投稿です。編集もしくは削除してブログを始めてください !",
     post_title: "Hello world!",
     post_excerpt: "",
     post_status: "publish",
     comment_status: "open",
     ping_status: "open",
     post_password: "",
     post_name: "hello-world",
     to_ping: "",
     pinged: "",
     post_modified: "2017-10-20 12:06:00",
     post_modified_gmt: "2017-10-20 03:06:00",
     post_content_filtered: "",
     post_parent: 0,
     guid: "http://localhost:8888/?p=1",
     menu_order: 0,
     post_type: "post",
     post_mime_type: "",
     comment_count: 1,
     meta: Corcel\Model\Collection\MetaCollection {#5964
       all: [],
     },
   }

無事接続できました!!

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

mong - Dockerのコンテナ名をランダム生成するコードをPythonに移植してみた -

goofy_grothendieck, ecstatic_lederberg, quizzical_wu, ...
Dockerのコンテナのように,ランダムなんだけど読みやすい名前を付けたいことってありますよね?moby/mobyGo言語のコードを見つけたのでPythonに移植してmongというライブラリにしてみました.

2019/12/22追記: 続報書きました

使い方

Python 3.6と3.8で動作確認しています.Python3.5以降なら動くと思いますが,Python2はType Hintingなどが原因で動きません.

標準ライブラリだけしか使っていないので,インストールはすぐ終わるはずです.

$ pip install git+https://github.com/toshihikoyanase/mong.git

さっそく,ランダムに名前を生成してみましょう.

(2019/12/22修正: インスタンス化する方法から関数呼び出しに変更しました)

import mong
mong.get_random_name()  # 'goofy_robinson'
mong.get_random_name()  # 'stoic_feynman'

Google ColabにJupyter Notebookを用意しました.以下からすぐに試せます:
Open In Colab

実装方針

オリジナルのコードを見ると名前の元となる単語の辞書がその大半をしめていました.移植先のコードに辞書をべた書きすると,メンテナンスが大変そうなので,オリジナルのコードから抽出する方法を選びました.

具体的には下記のとおりです.
- mong/create_dict.py でオリジナルのコードから名前のもととなる単語の辞書を抽出しています.
- mong/moby_dict.json に抽出した辞書を保存しています.
- mong/name_generator.pyNameGenerator に名前生成ロジックを移植しています.

オリジナルの実装

Dockerの名前生成コードmoby/moby にあって,Go言語で書かれています.なお,mobyはコンテナシステムのためのツール集のようです.こちらのスライドに簡潔にまとめられています.

名前は2つの語からなっていて,形容詞と有名な科学者とハッカーの人名です.それらが _ でつながれています.

# 怒った(形容詞)_チューリング(人名)
angry_turing

形容詞はleft,人名はrightというリストで管理されています.

    left = [...]string{
        "admiring",
        "adoring",
        "affectionate",
        "agitated",
    right = [...]string{
        // Muhammad ibn Jābir al-Ḥarrānī al-Battānī was a founding father of astronomy. https://en.wikipedia.org/wiki/Mu%E1%B8%A5ammad_ibn_J%C4%81bir_al-%E1%B8%A4arr%C4%81n%C4%AB_al-Batt%C4%81n%C4%AB
        "albattani",

        // Frances E. Allen, became the first female IBM Fellow in 1989. In 2006, she became the first female recipient of the ACM's Turing Award. https://en.wikipedia.org/wiki/Frances_E._Allen
        "allen",

名前の生成処理は,基本的にランダムに選択して,つなぐだけです.

// GetRandomName generates a random name from the list of adjectives and surnames in this package
// formatted as "adjective_surname". For example 'focused_turing'. If retry is non-zero, a random
// integer between 0 and 10 will be added to the end of the name, e.g `focused_turing3`
func GetRandomName(retry int) string {
begin:
    name := fmt.Sprintf("%s_%s", left[rand.Intn(len(left))], right[rand.Intn(len(right))])
    if name == "boring_wozniak" /* Steve Wozniak is not boring */ {
        goto begin
    }

    if retry > 0 {
        name = fmt.Sprintf("%s%d", name, rand.Intn(10))
    }
    return name
}

このQiita記事にもあるように, boring_wozniak は生成されないようになっています.(出たらもう一度作り直しています.)

2019年12月21日時点で, leftは108語, rightは235語あったので合わせて25,379種類の名前が生成できます.意外と少ないような気がしますね.

なお,retry 引数についてはよくわかっていなくて,処理を見てもretryしているように見えません.retry>0だと 0 以上 9 以下の整数をランダムに選んで名前の末尾につけています.これは使う側のコードを読まないとわからないかもしれません.単純に1桁種類数を増やすためかな?

まとめと今後の課題

PythonでDockerコンテナ風のランダム名を生成できる mong というライブラリを作ってみました.

一応,動くところまでは書いたのですが,テストカバレッジが低かったり,エラー処理を省いていたり,CIをしていないなどいろいろ改善の余地はあります.また,pypiに登録していないので,インストールがやや面倒です.

(自分も含めて)ニーズがあればもう少し整えようかなと思っています.

参考文献

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

オレオレGyazoでスクショを瞬間共有

はじめに

Gyazoというアプリケーションをご存知でしょうか。
「スクリーンショットの瞬間共有」というキャッチフレーズで、世界中で使われているハイパー売れっこアプリなため、もしかすると知らない人の方が少ないかもしれません。

ezgif-7-b03d809b641c.gif

↑このように、Gyazoのクライアントアプリを起動して範囲を選択すると、スクリーンショットが撮られてすぐにGyazoのサーバーにアップロードされます。
その後URLが返却され、ブラウザが開きすぐにアップロードされたスクリーンショットを確認することができ、他の人に共有する際はこのURLを共有するだけです。

通常スクリーンショットを共有しようとすると、撮る→端末に保存→アップロードといった流れになると思うと、非常に簡単に済みますね。
また、例えば表形式の資料で画像もつけたいといった場合でも、下記のように画像URLを貼り付けておくことで良い感じに収めることができます。

スクリーンショット 2019-12-21 16.38.26.png

この手軽さ便利さから、前に働いていた職場では非常に流行っており、あちこちから「ごめん、それちょっとGyazoってくんね」だとか「すぐにGyazoって送ります」「お前も結構Gyazoってきたね」だとか、とにかく「Gyazoる」という単語が飛び交っており、最終的にはただスマホで写真を撮る時にさえGyazoるとか言ってたくらいには中毒者が続出していました。
これに関しては、人によっては口に出すとちょっと気持ち良い響きなのが原因なのかもしれません。

問題点

ただ、このGyazo、無料で使おうとすると少し問題があります。
1 広告が結構邪魔
2 アップロードした画像は一般公開される
3 キャプチャしたものが一覧で見られる画面があるが、直近10件まで

上記の問題は有料版にすることで解決することができますが、お金を払ってGyazoライフを送れるようなイカした人はお洒落なオフィスで可愛いあの子とネスカフェアンバサー出来るような人間だと相場は決まっています。
私のように、会社に在籍する猫たちの飲み水をこっそり盗み飲むような人間には、中々真似することはできません。

有料版にせずとも、Gyazoサーバーを社内サーバー等に構築することでも解決が可能です。
これはオレオレGyazoとかいって、割と有名らしいです。
Gyazoはサーバー、クライアントと2つのアプリケーションで動いているのですが、実は両方ともソースコードが公開されています。
サーバー側はRubyで書かれているCGIスクリプトを少し変更して配置すれば良く、クライアント側はmacであればスクリプトを少し弄るだけでオレオレGyazoの環境は完成します。

ちなみに

キャプチャしたものが一覧で見られる画面があるが、直近10件まで

こちらの問題に関してですが、オレオレGyazoだと公式と違ってそもそも一覧画面がありません。
ただ、画像は自分のサーバーに置かれるため、一覧画面など欲しい機能は自作することで好きなように対応することができます。

オレオレGyazoのやり方は調べれば割と色々出てきますが、基本的に流れとしては
apacheをインストール→サーバーのスクリプトを取ってきて配置といった流れになるかと思います
試しにEC2に作ってみると、思いの外あっさりと出来てしまいました。
同じようにやり方を書くのもつまらないと思ったため、今回はEC2で行った流れを元にDocker化し、ローカルやら他の環境でもパパッとビルドしてシュルっと試せるようにしてみました。

サーバー側用意

サーバー側の動作としては、クライアントから送信されてきた画像を保存し、そのURLを返すというシンプルなものになっております。
とりあえずローカルで動くように書いたDockerfileが下記となります。

FROM    centos:centos7
#諸々インストール
RUN     yum update -y && yum clean all \
            && yum -y install ruby \
            && yum -y install wget \
            && yum install -y httpd && yum clean all
#適当になんか入れとく
RUN     echo "I was the god of Gyazo" > /var/www/html/index.html
#gyazoサーバースクリプト等配置
WORKDIR /var/www/cgi-bin/
RUN     wget https://raw.github.com/gyazo/Gyazo/master/Server/upload.cgi \
            && chmod +x upload.cgi
WORKDIR /var/www/
RUN     mkdir cgi-bin/db \
            && mkdir html/data \
            && chmod 777 cgi-bin/db \
            && chmod 777 html/data
#ファイルをちょこっと書き換える
RUN     sed -i -e "s/#AddHandler cgi-script/AddHandler cgi-script/" /etc/httpd/conf/httpd.conf
WORKDIR /var/www/cgi-bin/
RUN     sed -i -e "s/usr\/bin\/env ruby/usr\/bin\/ruby/" upload.cgi \
            && sed -i -e "s/data\/#{hash}.png/..\/html\/data\/#{hash}.png/" upload.cgi \
            && sed -i -e "s/gyazo.com/localhost\/data/" upload.cgi
RUN     systemctl enable httpd
EXPOSE      80

dockerの入った環境で、どこでも良いので上記ファイルを置き、ビルドします↓

docker build --rm -t gyazo .

gyazoという名前でイメージができました。
これを起動します。

docker run -d --privileged --name gyazo -i -t -p 80:80 gyazo /sbin/init

これでサーバーが立ち上がります。
ブラウザから http://localhost/ にアクセスてしてみて、「I was the god of Gyazo」とか出ていたら成功です。
これでサーバー側は完成です。
立ち上がっていなかったら・・・ちょっとよく分かりません。
コンテナとかイメージを削除して再ビルドとかしてみたら上手くいくんじゃないでしょーか。

クライアント側用意

続いて、クライアント側の用意をします。
MacとWindowsでやり方が違うのですが、私はMacでしか試していないためここではMacのやり方を載せます。
Windowsのやり方は、「Gyazo 自分のサーバーで」等で検索してみたりで探してみてください。(少し情報が古いものが多いため、ちゃんと動作するかは分かりません)
Macだとクライアントはシェルスクリプトで書かれているらしく、直接アップロード先等を書き換えるだけで良いようです。

クライアントアプリ自体はホームページからダウンロード、インストールします。
こちらからダウンロードします。
https://gyazo.com/

その後、
/Applications/Gyazo.app/Contents/Resources/
にあるscriptというファイルを開き、送信先の設定である下記の2点を書き換えます。

HOST = 'upload.gyazo.com'
↓
HOST = 'localhost'
CGI = '/upload.cgi'
↓
CGI = '/cgi-bin/upload.cgi'

さらに、Gyazoはある時からデフォルトだとhttpsで通信を利用するようになっているらしいです。
そのためサーバー側にSSLの証明書の設置が必要となりますが、とりあえず動かしたいので、その辺もスクリプトを弄って何とかします。

PORT = 443
↓
PORT = 80

※ポートに関しては、dockerコンテナ立ち上げ時に違うポート番号で立ち上げている場合は、そのポート番号を指定します。

http.use_ssl = true
↓
http.use_ssl = false

これで動くようになりました。

190394147.gif

邪魔な広告がないのと、本家と比べるとだいぶ動作が軽くなりました。おそらく余分なスクリプト等が走っていないためだと思われます。正直、個人的にはこれがオレオレGyazoで一番嬉しい所です。
試しにEC2インスタンスでやってみても動きました。Dockerなので他の環境でも多分問題ないはず。

これによって一瞬でスクリーンショットを共有することで、非常にスマートな印象を与えられます。気になるあの子に披露して見せたら、感涙に咽びながらクリスマスを共に過ごしてくれること間違いなしです。
ただ、apacheでCGIを動かして〜といったことは少々古めかしいらしく、最近だとサーバーレスとかでやったほうがナウいっぽいみたいです。(ナウいはダサいの対義語です)

皆様も今日から素敵なGyazoライフをお過ごし下さいませ。

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

Docker setting のShared Drivesでエラーが出たときの対処法

概要

DockerのShared DriveでDドライブを指定したら
FireWallのエラーが出たので、その時の対処方法を載せます。

環境

OS : Windows10 pro (64bit)
セキュリティソフト: Norton(ノートン)

エラーメッセージ

Firewall detectedということでファイアウォールに引っかかってしまった模様。

公式Help

リンク先のドキュメントも載せておきます。

10.0.75.1 on port 445 (the Windows host) from 10.0.75.2 (the virtual machine)

という事なので、Windows10 pro 以外の方はipアドレスを[10.0.75.2]に読み替えて実施してください。

対処法

まずはNortonのファイアウォール設定を開きます。

手順1 : [設定] > [ファイアウォール] >[トラフィックルール]

手順2 : [追加する]をクリック
手順3 : [許可] にチェックを入れて[次へ]

手順4 : [その他のコンピュータからの接続]にチェックをいれて[次へ]

手順5 : [下記のリストにあるコンピュータとサイトのみ] にチェックを入れて[追加する]をクリック

手順6 : [ネットワークアドレスを使う] にチェックをいれて、以下の内容で[OK]

手順7 : リストに追加後、[次へ]をクリック

手順7 : 以下の設定をして[追加する]をクリック

  • 許可したいプロトコル: TCP
  • どの種類の通信またはポートを許可しますか: [下記のリストにあるすべてのポートに一致する場合のみルールを適用する]

手順8 : 指定するポートを設定するし、[OK]をクリック

手順9 : 指定するポートを追加したら[次へ]をクリック

手順10 : 特に変更しないで[次へ]をクリック
(ログを残したい場合はチェックを入れて[次へ])

手順11: 名前を入れて[次へ]

手順12: [完了]をクリック

手順13: 追加されたルールを上に移動する
 ※ 遮断ルールより上に移動

備考

取り急ぎの対応ですが、とりあえずエラーメッセージは出なくなりました。
今後もFirewall関連の問題が出てきたらここの記事に追加していきたいと思います。

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

【venv不要】Remote Containersで作る最強のPython開発環境【VSCode/Docker】

はじめに

人気言語Top1に輝いたり1、IPA 基本情報技術者試験の試験科目になる2などして注目を集めているPython。

でもちょっと待って!あなたはPythonのエコシステムの恩恵を正しく受けて開発できていますか?

開発環境をうまく作ることで、 入力補間やフォーマッターによって開発が効率化されるだけでなく静的解析によって予期せぬ不具合を未然に防ぐことができ、①簡単 ②爆速 ③安全 にプログラムを書くことができます。

もちろんそのような快適な環境を作るためのHow to記事はたくさんありますが、問題はせっかくオレオレPython開発環境を作れたとしても、それを異なるPC間で共有するのが困難であるということです。

なぜなら、

  • Pythonやpipで入れるモジュールのバージョンを揃えてインストールしないといけない
  • fommter / linter / IDEの拡張機能を揃えてインストール&設定しないといけない

という、開発環境に依存する問題が発生するからです。
個人 / 複数人を問わず、異なるPC間であっても同一の動作をすることが保証されて欲しい
かつ、 テキストエディタでプロジェクトを開くだけで、その開発環境が勝手に構築されて欲しいという要求があります。

ということで、この記事では、VSCodeのRemote Containerを使うことでこの問題を解決します。
本記事で述べる方法を用いると、以下のようなことが実現されます。

  • Win / Mac / Linuxを問わず、全ての環境で同一の動作をすることが保証される3、ポータブルなPython開発環境が作れる
  • 新しい環境でも、プロジェクトを開いただけで静的解析や自動フォーマットなどの全ての設定が完了する

では、やっていきましょう

インストール

本記事では、以下のツールを用います。
他の記事を参考にしながらインストールしてください。

  • VSCode
  • Docker

ターミナルから以下のコマンドが打てれば準備完了です。

$ docker --version
Docker version 19.03.5, build 633a0ea

Remote Containersの設定

まずは、開発用のディレクトリを適当に作りVSCodeで開きましょう。

$ mkdir python-test

スクリーンショット 2019-12-21 16.27.14.png

以下のようなディレクトリ構成を作ります

.
└── python-test
    ├── .devcontainer
    │   ├── Dockerfile
    │   └── devcontainer.json
    ├── .vscode
    │   └── extensions.json
    └── src
        └── main.py

それぞれのファイルには以下の内容をコピペしてください。

.devcontainer/Dockerfile
FROM python:3.7.3-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    apt-utils \
    gcc \
    build-essential \
    && pip install --no-cache-dir \
    autopep8 \
    flake8 \
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

ここで、 FROM python:3.7.3-slim の部分の数字を変えると、好きなPythonのバージョンを指定することができます。

.devcontainer/devcontainer.json
{
    "name": "Python Project",
    "dockerFile": "Dockerfile",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
        "python.pythonPath": "/usr/local/bin/python"
    },
    "extensions": [
        "ms-python.python"
    ]
}

ここで、 "name": "Python Project" の部分は好きな文字列にしてしまって構いません。

settings には、 .vscode/settins.json で書くものと同一のものを記述することができます。本来bashのパスやpythonのパスは環境依存であるため個人ごとに設定する必要があるのですが、今回はコンテナ内で実行されるのでパスの情報が既知であり、固定することができるというのがポイントです。

また、 extensions には、VSCodeの拡張機能を加えることができ、ここに記述した拡張機能は自動でインストールされます。VSCodeの拡張機能を強制的にインストールさせることができるのはRemote Containersならではなので、全員にインストールして欲しい拡張機能はここに書きましょう。今回はVSCode用のPython拡張機能を追加しています。

src/main.py
import sys
print(sys.version_info)

Pythonスクリプトはバージョンを出力するものとでもしておきましょう。

.vscode/extensions.json
{
    "recommendations": [
        "ms-vscode-remote.remote-containers"
    ]
}

VSCodeの拡張機能のRecommendationsにRemote Containersを指定します。
注意しなければならないのは devcontainer.json 内のextensionsと違って、記述しても実際にインストールされるわけではありません。ですので、ファイルをコピペし終わったら実際にインストールするためにVSCodeを開き直してください。
すると、右下に以下のようなポップアップが出てくると思います。Install Allを選び、Remote Containerをインストールしましょう。
スクリーンショット 2019-12-21 15.09.13.png

無事、インストールできたらVSCodeの左下に緑色のボタンが表示されます。
スクリーンショット 2019-12-21 14.40.56.png

開くと、このようなメニューがでるので、
スクリーンショット 2019-12-21 14.41.42.png

「Remote-Containers: Open Folder in Container...」を選びます。
フォルダ選択画面がでたら、プロジェクトのディレクトリ(今回の場合はpython-test)を選びましょう。

すると .devcontainer/Dockerfile に記述したコンテナのビルドが自動で走ります。detailsを開くことで 経過を見ることができます。もしbuildにこけてしまった場合もdetailsを見ましょう。
スクリーンショット 2019-12-21 14.45.37.png

ビルドが正常に終わったらそのままコンテナを起動し接続してくれます。コンテナに接続されたらVSCodeの内部ターミナルを開いてください。
スクリーンショット 2019-12-21 14.58.01.png

おや?なにやらシェルのユーザーがrootになっていて、ディレクトリも /workspaces になっていますね。これは、ホストOS(Win / Macとか) のターミナルではなく、ホストOSから独立したコンテナのターミナルに入れていることを示しています。

Docker分かんないよ!という方もいるかもしれませんが、とりあえずPythonを実行してみましょう。

/workspaces/python-test# python src/main.py 
sys.version_info(major=3, minor=7, micro=3, releaselevel='final', serial=0)

FROM python:3.7.3-slim で指定したバージョンのpythonが入っていますね!
これで基本的な設定は完了です。

今後は他のPCで git clone しても、「Remote-Containers: Open Folder in Container...」でコンテナの中に入ることで、同一の環境を再現することができます。

requirements.txtの設定

ここはコンテナの中なので、この中で好きに pip install しても構いません。ここでインストールしたモジュールはホストOSにはまったく影響を与えることがないので、破壊的に開発を進めることができます。

/workspaces/python-test# pip install numpy
/workspaces/python-test# python
>>> import numpy as np
>>> np.__version__
'1.17.4'

また、再度コンテナをビルドして作り直すとインストールしたモジュールは消えます。そこで、開発を進める中でこのpipモジュールはfixしよう!となった場合、Pythonで慣例として使われている requirements.txt を作って、そこに pip install するモジュールを書くこととしましょう。

ディレクトリ構成と requirements.txt の書き方は以下の通りです。

.
└── python-test
    ├── .devcontainer
    │   ├── Dockerfile
    │   └── devcontainer.json
    ├── .vscode
    │   └── extensions.json
    └── requirements.txt
requirements.txt
numpy==1.17.4

requirements.txtには、 pip install する際のバージョンを固定して書くことができます。再現性を担保するために、バージョンは固定することが望ましいです。

そして、 devcontainer.json を以下のようにしましょう。

.devcontainer/devcontainer.json
{
    "name": "Python Project",
    "dockerFile": "Dockerfile",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
        "python.pythonPath": "/usr/local/bin/python"
    },
    "extensions": [
        "ms-python.python"
    ],
    "postCreateCommand": "pip install -r requirements.txt"
}

postCreateCommand というのが追加されましたね。これは、コンテナが生成された後に実行するコマンドです。今後は requirements.txt に記述したモジュールが必ずインストールされます。

ファイルを更新したら、更新をコンテナに反映させるために、左下の緑色のボタンを押して、「Remote-Containers: Rebuild Container」を選びましょう。

formatter / linterを設定する

続いて、formatter / linterを設定していきます。
pythonのformatter / linterを追加したときの devcontainer.json は以下のとおりです。

.devcontainer/devcontainer.json
{
    "name": "Python Project",
    "dockerFile": "Dockerfile",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
        "python.pythonPath": "/usr/local/bin/python",
        "python.linting.pylintEnabled": false,
        "python.linting.flake8Enabled": true,
        "python.linting.flake8Args": [
            "--ignore=E402,E501"
        ],
        "python.formatting.provider": "autopep8",
        "python.formatting.autopep8Args": [
            "--ignore",
            "E402,E501",
            "--max-line-length",
            "150"
        ],
        "[python]": {
            "editor.formatOnSave": true
        }
    },
    "extensions": [
        "ms-python.python"
    ],
    "postCreateCommand": "pip install -r requirements.txt",
}

linterとしてflake8、 formatterとしてautopep8を指定し、それぞれE402(Module level import not at top of file)とE501(Line too long)を無視しています。ファイルのセーブ時にformatがかかるようにしています。

これでこんなふうなめちゃくちゃなコードを書いても、

before.py
a    =0
def method1(args)  :
    print(   args)
def method2() :
    if a    ==0:
            method1(   "hoge"   +    "fuga"   )

このように、保存する際に整形してくれます。

after.py
a = 0


def method1(args):
    print(args)


def method2():
    if a == 0:
        method1("hoge" + "fuga")

通常、このようなformatter / linterの設定は .vscode/settings.json に記述します。しかし、 .vscode/settings.json は個人の設定ファイルであるから、 .gitignore に設定しているプロジェクトも多いです。加えて、 pip install flake8 autopep8 を手動で実行する必要があるので、設定ファイルを書いたからといって、formatter / linterが適用されるわけではありません。

一方で、今回はRemote Containersの設定ファイルに書くことができるので、共有もしやすいですし、確実にflake8やautopep8が入っている状態を再現できるので "フォーマットがかかっていないソースコードがコミットされる"ということも防ぐことができます。

ところで、 pip install flake8 autopep8 はどこで実行していたのでしょうか?
答えは .devcontainer/Dockerfile の中にあります。

.devcontainer/Dockerfile
FROM python:3.7.3-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    apt-utils \
    gcc \
    build-essential \
    && pip install --no-cache-dir \
    autopep8 \
    flake8 \
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

もしあなたが、コンテナ内で pytest を記述したいと思った場合は追加することができます。

.devcontainer/Dockerfile
RUN pip install --no-cache-dir \
    autopep8 \
    flake8 \
    pytest

また、autopep8やflake8は開発時に必要なのであって、実行時には不要です。そうしたモジュールはDockerfileに書いて、実行時に必要なファイルは requirements.txt に書く、という使い分けができます。

静的型解析

最後にPython3.6以降4で使えるType Hintsを導入しましょう。Type Hintsを導入することで、QoLが爆上がりします。

devcontainer.json を以下のようにしましょう
ここでは、Type Hintsの静的解析ツールとして、pyrightを用います。

.devcontainer/devcontainer.json
 "extensions": [
    "ms-python.python",
    "ms-pyright.pyright"
 ],

変更したら、左下の緑色のボタンを押して、「Remote-Containers: Rebuild Container」を選びましょう。

続いて、pyrightの設定ファイルを書きます。
ディレクトリ構成は以下の通りです。

.
└── python-test
    ├── pyrightconfig.json
    └── src
        └── main.py
pyrightconfig.json
{
    "include": [
        "src"
    ],
    "reportTypeshedErrors": false,
    "reportMissingImports": true,
    "reportMissingTypeStubs": false,
    "pythonVersion": "3.7",
    "pythonPlatform": "Linux",
    "executionEnvironments": [
        {
            "root": "src"
        }
    ]
}

pyrightconfig.jsonの詳しい書き方は他の記事を参照してください。(もしかしたら自分が他に書くかも)

ここで、 src/main.py を編集してみましょう。

src/main.py
def hello(name: str, age: int) -> str:
    result1: str = "My name is " + name + ".\n"
    result2: int = "I am " + str(age) + " years old."
    return result1 + result2


result: int = 10
result = hello(name="Otao", age=23)
print(result)

pythonのType Hintsでは、このように関数や変数に型アノテーションを付けることができます。
pyrightを導入した状態で src/main.py を開くと、、、
スクリーンショット 2019-12-21 16.13.41.png
result2はint型なので、str型は代入できないよ!
スクリーンショット 2019-12-21 16.14.01.png
str型とint型の + 演算子は定義されてないよ!!
スクリーンショット 2019-12-21 16.14.10.png
resultはint型で定義されてるのに、そこにstr型を代入しようとしているよ!!!
hello関数の定義も貼っておくね!!!
スクリーンショット 2019-12-21 16.14.22.png
といった風に、型にまつわるエラーを表示してくれます。

Type Hintsを使うことは以下の理由からおすすめです。

  • 型を定義することによって可読性がめちゃくちゃ上がる
  • 予期せぬ型が変数に入ってきて不具合を起こす可能性が下がる
  • VSCode上の入力補完機能が効く

ぜひ導入しましょう。

まぁ不思議なことに(?)このスクリプト、実行はできるんですよねw
(Type Hintsはあくまでアノテーションなので、実行時には無視されます。)

/workspaces/python-test# python3 src/main.py 
My name is Otao.
I am 23 years old.

終わりに

快適なPythonライフを!
(記事は適宜更新します。)

  • 著者情報

  1. 習得したいプログラミング言語、したくない言語, https://tech.nikkeibp.co.jp/atcl/nxt/column/18/00501/111200004/ 

  2. プレス発表 基本情報技術者試験における出題を見直し, https://www.ipa.go.jp/about/press/20190124.html 

  3. 実際にはホストOSによって挙動が違うことがあり、Dockerfileの修正が必要になることもあります。 

  4. 関数へのType Hintsは3.5からあったので正確ではありませんが、変数にも型アノテーションを付けれるようになったのは3.6以降なので、3.6以降を使って欲しいです 

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

ラズベリーパイzeroWにDocokerをインストールしてみた

Dockerのインストール方法

コマンド一行でOKです。
*ただメモリ領域の関係かうまくいかないときがありました。

curl https://get.docker.com | sh

SSHからの接続設定の場合(screen)

sshで作業をするとセッションが切れる場合があるので
screenを利用したほうがいいですよ。

Dockerのインストールが完了

Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea
 Built:             Wed Nov 13 07:36:04 2019
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea
  Built:            Wed Nov 13 07:30:06 2019
  OS/Arch:          linux/arm
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker your-user

Remember that you will have to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.

最下部に記載されていますが、userが利用できるようにするには、

  sudo usermod -aG docker your-user

簡単にdockerのインストールが完了

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

Docker setting のShared Drivesでエラーが出たときの対処法

概要

DockerのShared DriveでDドライブを指定したら
FireWallのエラーが出たので、その時の対処方法を載せます。

環境

OS : Windows10 pro (64bit)
セキュリティソフト: Norton(ノートン)

エラーメッセージ

Firewall detectedということでファイアウォールに引っかかってしまった模様。
image.png

リンク先のドキュメントも載せておきます
image.png

対処法

まずはNortonのファイアウォール設定を開きます。

手順1 : [設定] > [ファイアウォール]
image.png

手順2 : ネットワークの設定 > デバイスの信頼 > [設定する]

ドキュメントにあった、以下のIPアドレスを信頼するデバイスとして追加します。
IPアドレス : [10.0.75.0][10.0.75.1]

分かりやすいように名前は[Docker0][Docker1]にしました。
image.png

備考

取り急ぎの対応ですが、とりあえずエラーメッセージは出なくなりました。
今後もFirewall関連の問題が出てきたらここの記事に追加していきたいと思います。

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

DockerにPostgreSQLを入れる(使い捨て)

コンテナ作成および起動

docker container run -d --rm --name postgrescon -e POSTGRES_USER=postgresusr -e POSTGRES_PASSWORD=postgrespass -p 5432:5432 postgres
オプション 意味
--name コンテナ名
-d バックグラウンド実行
--rm 停止後にコンテナ削除
-p 5432:5432 ホストのポート:コンテナのポート
-e POSTGRES_USER スーパユーザー名
-e POSTGRES_PASSWORD スーパユーザーパスワード
postgres 使用イメージ

コンテナ動作確認

$ docker container exec -it postgrescon bash

root@xxxxxx:/# psql --version
psql (PostgreSQL) 11.0 (Debian 11.0-1.pgdg90+2)

ログイン

root@xxxxxx:/# psql -U postgresusr
psql (11.0 (Debian 11.0-1.pgdg90+2))
Type "help" for help.

postgres=#

ホストからの接続確認

#psqlのためpostgresqlをインストール
$ brew install postgresql

#自動起動停止
$ brew services stop postgresql

#停止確認
$ brew services list

#接続できることを確認する
$ psql -h localhost -U postgresusr
Password for user postgresusr: 
#以下のように出ればOK
psql (12.1)
Type "help" for help.

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

Dockerコンテナ(mysql)に接続する

Dockerコンテナ(mysql)にアクセスする

起動中のコンテナを確認する

$docker-compose ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                                NAMES
847c99b0929c        nginx:latest                   "nginx -g 'daemon of…"   20 minutes ago      Up 20 minutes       0.0.0.0:8080->80/tcp                 docker_php_nginx_1
5e2039b237df        docker_php_php                 "docker-php-entrypoi…"   20 minutes ago      Up 20 minutes       9000/tcp                             docker_php_php_1
8193d899b38d        phpmyadmin/phpmyadmin:latest   "/docker-entrypoint.…"   20 minutes ago      Up 20 minutes       0.0.0.0:8888->80/tcp                 docker_php_phpmyadmin_1
f4ff8b6b8ac3        mysql:5.7                      "docker-entrypoint.s…"   22 minutes ago      Up 20 minutes       33060/tcp, 0.0.0.0:13306->3306/tcp   docker_php_db_1

ホストPCからコンテナ(mysql)に接続する

CONTAINER IDを指定

$docker exec -it f4ff8b6b8ac3 bash

mysqlに接続

root@f4ff8b6b8ac3:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.28 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.03 sec)

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

LiveViewでElixirを評価する(2):Dockerを使ってElixirの実行をサンドボックス化する

LiveViewでElixirを評価する(1)の続きです。

前回までのあらすじ

LiveviewでElixirを実行時評価すると楽しい。が、eval_stringは自由に書かれてしまうと厄介ななシロモノなので、できるだけ安全に実行する方法を模索します。

サンドボックス化

なぜ悪いことをしたいのか?悪いことをしてメリットがあるからやる訳ですね。
Elixir環境から抜け出しても何もない環境なら、無駄な労力を割く人はそうそう居ないでしょう。
という訳で、コードの評価はelixir-alpineなDockerコンテナ上で行いましょう。

Docker上でElixirを動かす

dockerは予め入れといてください。

# docker run -it elixir:1.9.4-alpine iex
iex(1)> 

動きますね。コマンド一発で環境の整ったVMが手に入るなんて魔法のようですね。

Docker上のElixirと話す

Node.connect()で他のVM上のノードと話せるようです。やってみましょう。別ホストのVMと話す場合はVM毎に名前を付けつつ、Cookieとやらも指定しておく必要があるそうです。
あと、コンテナから見たホストのアドレスはhost.docker.internalで解決できるそうです。ちなみにアドレス指定でアクセスする場合、--sname でなく--nameで起動していないと怒られるっぽいです。

docker上
# docker run -it elixir:1.9.4-alpine iex --name e1 --cookie eval
iex(e1@d523eb311dbb.localdomain)1>
ローカル
# iex --name server@host.docker.internal --cookie eval
iex(server@host.docker.internal)1>
docker上
iex(e1@d523eb311dbb.localdomain)1> Node.ping(:"server@host.docker.internal")
:pong
ローカル
iex(server@host.docker.internal)1> Node.ping(:"e1@d523eb311dbb.localdomain")
:pong

話せてますね。実はこれ逆にするとうまくいきません。Host -> Dockerでは名前解決できません(ちゃんと名前つけてないので当然ですが)。
ですが、一度目のDocker -> HostにPingを投げた時にNode.connectが張られてるっぽくて一度話した相手のドメイン名は解決できるようになります。

という訳で、コンテナが立ち上がったらホストに繋ぎに行くようにしましょう。
以降、Dockerの上のコードをClient、ホスト側をServerとします。

サーバのコード

サーバの待ち受け側のコードです。

server.exs
defmodule Server do
  use GenServer

  def init(init) do
    :global.register_name(:server, self())
    {:ok, init}
  end

  def start_link() do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def handle_call({:ready, client_name}, _from, state) do
    :global.sync()
    client_pid = :global.whereis_name(String.to_atom(client_name))
    GenServer.cast(client_pid, :hello)
    {:reply, client_pid, state}
  end
end
Server.start_link()

サーバは単なるGenServerでcast :readyを待ち、:helloを投げ返します。
init時に自身のPIDをglobalに:serverとして登録しています。

Clientのコード

eval.exs
defmodule Client do
  use GenServer

  def init({name, server_name}) do
    :global.register_name(String.to_atom(name), self())
    Node.connect(String.to_atom(server_name))
    :global.sync()
    server_pid = :global.whereis_name(:server)
    GenServer.call(server_pid, {:ready, name})
    {:ok, server_pid}
  end

  def start_link(name, server_name) do
    GenServer.start_link(__MODULE__, {name, server_name}, name: __MODULE__)
  end

  def handle_cast(:hello, server_pid) do
    IO.puts("Hello, I'm Docker")
    {:noreply, server_pid}
  end
end

Client.start_link("client", "server@host.docker.internal")

GenServerのinit時に自身の名前の登録と:serverのpidの問い合わせをして、:readyを投げます。castで:helloを受け取ると返事します。
ちょっとお行儀が悪いですが、スクリプト的に動かしています。また、initの段階ではまだGenServerの準備が整っていなさそうなので、init内であれこれメッセージを投げるのもよろしくないです。(上記コードをcallにして、serverからcallされてもメッセージが届きません。)
ついでにDockerfileも作っておきましょう。

Dockerfile
FROM elixir:1.9.4-alpine

RUN mkdir /home/eval/
WORKDIR /home/eval/
COPY client.exs /home/eval/client.exs
CMD elixir --name client@host.docker.internal --cookie eval client.exs

実行

まずdockerのイメージを作っておきます。

client
# docker build -t client .

これでclientというイメージができました。

次にサーバを動かしておきます、新しくターミナルを立てて

server
# iex --name server@host.docker.internal --cookie eval ーS server.exs

クライアント用のターミナルに戻ってclientを起動すると、通信できているのが分かります。

client
# docker run -it client
> Hello, I'm Docker
server
> client is ready

ServerからClientを立ち上げる

通信はできたので、今度はServerからClinetのコンテナを立ち上げてみましょう。
とりあえず荒っぽくいきましょう。さっきclient側で打ち込んでいたコマンドをSystem.cmdで叩きます。立ち上げまで時間が掛かるので、spawnで別プロセスにしてます。(ちゃんとやるなら、プロセスハンドルをちゃんと保持して、エラー処理やタイムアウトを設けて適切に管理しましょう。)

server.exs
  def init(init) do
    :global.register_name(:server, self())
    spawn(fn -> System.cmd("docker", ["run", "client"]) end) # <-- 追加
    {:ok, init}
  end

さて、再実行なのですが、クライアントのIO.putsが見えないですね。

server
# iex --name server@host.docker.internal --cookie eval ーS server.exs
> client is ready

もう少し会話させましょう。

server.exs
  # Server モジュールに以下追加
  def handle_call({:hello, name}, _from, client_pid) do
    IO.puts("Hello " <> name <> ", I'm server")
    {:reply, client_pid, client_pid}
  end
eval.exs
  def handle_cast(:hello, server_pid) do
    GenServer.call(server_pid, {:hello, "client"}) # <- 変更
    {:noreply, server_pid}
  end

今回は、Server->Clientは投げっぱなし(cast), Client→Serverはブロッキング(call)としています。Serverは止めたくないけど、Client側は待ってもいいよねという指針です。

次回

ElixirからDockerのコンテナ上のElixirを立ち上げて通信できました。
次は、前回作ったLiveEvalの環境に組み入れていきます。

(組合せるとあまりうまく動かなくなってしまったため、次回少し遅れるかもしれません。)

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