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

C言語 と Dart で CGI : はじめに (1)

C言語 と Dart で CGI

昨今は動的なホームページを作成する場合には、「VPS で Linux インスタンスを立ち上げたり」、「Serverless の フレームワークを利用してコンテンツを配信したり」、「K8Sなどを利用してContainer に落としたり」するのが最近のトレンドだと思います。

が、つい最近まで、レンタルサーバーを借りてCGIを利用して動的コンテンツを配信するのが一般的でした。
この、CGIについて解説して行きます。

C言語とDart言語からCGIを利用

また、CGIと言えば、Perl や PHP などが、良く使われますが、
ここでは、C言語とDart言語を対象とします。

Dart言語 で CGI に利用した話が、DuckDuckGoしてもGoogleしても皆無だったからです。
※ 訂正: C言語はあります!! Dartが皆無

もちろん、皆無だった理由はクラシックな技術で、
今後、利用される事もなかったからでしょう

CGIはシンプルで容易

2004-2006年くらいでしょうか? PHP が 広まり、多くのサービスがCGIで作られました。
非エンジニアの人も多く。そんな非エンジニアの方々でも
全体像を把握出来、お金を稼ぐようなサービスが作られて行きました。

昨今の、React Vue Flutter や Firebase た Lambda など Serverless と比較して
習得が容易だったからだと考えています。

CGI とは

CGIは、Common Gateway Interface の略です。
https://en.wikipedia.org/wiki/Common_Gateway_Interface
https://tools.ietf.org/html/rfc3875

特にサーバーと外部のプログラムの間での、やりとり方法を決めたものです。
今時のInterface と違い 超シンプルです。

サーバーからアプリを起動して、実行結果をもらう

CGIでは、サーバーからパラメータを指定してアプリを起動します。
そのアプリが出力する標準出力をを返り値としてもらう。

具体的に見てみましょう。

Hello,World!! と表示するアプリ

hello.c
// hello.c
// gcc hello.c -o hello.cgi
#include<stdio.h>

int main(int argc, char* argv[]) {
    printf("Content-type: text/html\n\n");
    printf("Hello,World!!\n");
    return 0;
}

という、hello.cgi という名前のアプリがあるとします。
このアプリは、./hello.cgi とコマンドを入力すると、
"""
Content-type: text/html

Hello,World!!
"""
と、コンソールにも文字列を表示するだけのアプリです。

Hello,World!! と表示するページになる

ブラウザーから、 "http://example.com/hello.cgi" を呼び出すと、
"Hello,World!!" とブラウザーに表示されます。

このように、サーバーからアプリを呼び出して
アプリからの出力をユーザーに返すという事がCGIでは出来ます

実際に動かしてみよう

Docker 環境を用意しました。
細かな設定とかはDockerファイルを参照してください!!

Container を起動

$ git clone https://github.com/kyorohiro/dartlang_cgi.git
$ cd ./dartlang_cgi/001
$ docker-compose build
$ docker-compose up -d

VSCodeを起動

ブラウザーでDocker Container の 中の VSCode を開く
http://127.0.0.1:8443
※ 127.0.0.1 は Dockerの環境依存です。

vscode の中で、Apacheサーバーの立ち上げ

$ apache2

cgiをビルド

$ cd /app/www/cgi
$ gcc hello.c -o hello.cgi
$ dart2native hello_dart.dart -o hello_daet.cgi

動作確認

http://127.0.0.1:8080/ を開くと、 ./app/www/index.html が表示されます

http://127.0.0.1:8080/cgi-bin/hello.cgi を開くと。./app/cgi/hello.cgi の実行結果が返ります

http://127.0.0.1:8080/cgi-bin/hello_dart.cgi を開くと、 ./app/cgi/hello_dart.cgi の実行結果が返ります

補足

Dart は dart2native を利用する事で、Nativeにコンパイル出来ます。
今回はそれを使用しています。

main(List<String> args) {
  print("Content-type: text/html\n\n");
  print("Hello,World From Dart!!\n");
  return 0;
}

コード

https://github.com/kyorohiro/dartlang_cgi

次回

GET や POST や Cookie や
DB を扱ってみます。

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

DockerでTypeScript+Reactな環境構築(create-react-app)

目標

  • Dockerとdocker-composeを用いてcreate-react-appし、Reactアプリケーションの開発を始められる
  • かつ、TypeScriptでReactを動かす

前提

  • Dockerが使用可能である
  • docker-composeが使用可能である

手順

1. 作業ディレクトリの作成

作業するディレクトリを作成しましょう。
プロジェクト名はここではsample-appとします。

% mkdir sample-app
% cd sample-app

2. Dockernize

Dockerfileとdocker-compose.ymlを作成し、中身を追加します。

% touch Dockerfile
% touch docker-compose.yml

nodeのバージョンはこちらから新しめのものを選択すればいいと思います。

プロジェクトディレクトリ名が"sample-app"なので、コンテナ名・react-app名は"frontend"とします。

Dockerfile
FROM node:14.4.0-alpine3.10

ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo

WORKDIR /usr/src/sample-app
docker-compose.yml
version: '3'

services: 
  frontend: 
    build: .
    environment: 
      - NODE_ENV=development
    volumes: 
      - ./:/usr/src/sample-app
    command: sh -c 'cd frontend && yarn start'
    ports: 
      - '3000:3000'
    tty: true
  • volumes: ホスト:コンテナ間でファイル・ディレクトリを同期(= マウント)する
    • これにより、ホスト側のエディター等で行った編集がコンテナ側にも反映されたり、docker-compose run コマンドでコンテナ側に生成されたファイル・ディレクトリがホスト側にも同様に生成されるようになる
  • sh -c 'cd frontend && yarn start': docker-compose upしたとき、react-appディレクトリに移動し、devサーバーを起動する
  • ports: 公開用のポートを、'ホスト:コンテナ'の書式で指定している。
    • ブラウザでローカルホストを開くときはホスト側に指定したポートを参照する(e.g. この場合、ブラウザからはlocalhost:3000で開発環境のアプリケーションにアクセスできる)
  • tty: 値をtrueにすることで、docker-compose upでコンテナを起動させた際にコンテナがすぐに終了してしまうのを防ぐ

3. ビルド

% docker-compose build
% docker-compose run --rm frontend sh -c 'npx create-react-app frontend --template typescript'
  • --rm : コマンド実行後にこのコンテナ(frontend)を削除する
  • --template typescript : .jsxファイルの代わりに.tsxファイルが生成され、ReactアプリケーションがTypeScriptで動くようになる

正常にビルドが完了した場合、以下のようなディレクトリ構成になります。
TSXファイル等はsrc以下に配置されています。

sample-app
├── Dockerfile
├── docker-compose.yml
└── frontend
    ├── node_modules
    ├── public
    ├── src   
    ├── .gitignore
    ├── README.md
    ├── package.json
    ├── tsconfig.json
    └── yarn.lock

4. アプリケーション起動の確認

下記コマンドでdevサーバーが起動します。

% docker-compose up

http://localhost:3000/
にアクセスし、App.tsxの内容が表示されていれば成功です。
該当ファイルをエディターで編集するとすぐに反映されることも確認できるはずです。

あとは自由に開発を進めていきましょう!

アプリケーションの停止は下記コマンドでできます。

% docker-compose down

参考

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

Docker 環境別 no cache install

docker cache clean clear

基本的には、multi-stage-buildを用いて、build用のimageと成果物のimageを分けることが望ましい。
installされたpackageが多数のディレクトリにまたがっていたり、それが難しいケースはbase imageのパッケージ管理ツールのclean機構をonelinerで書くと良い。

apt

Dockerfile
FROM debian

RUN apt-get update && apt-get install -y \ 
* \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

参考: https://docs.docker.jp/engine/articles/dockerfile_best-practice.html#run

npm

Dockerfile
FROM node

RUN npm install -g \
* \
&& npm cache clean --force

参考: https://qiita.com/hikaruna/items/0bc1e97e8d254f4c27e7

yarn

TODO

yum

TODO

pip

TODO

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

[Docker] Rootless Docker を試してみた

Rootless Docker とは

Rootless モード(Rootless mode)は Docker デーモンとコンテナを root 以外のユーザが実行できるようにするもので、デーモンやコンテナ・ランタイムにおける潜在的な脆弱性を回避します。
Docker 公式ドキュメント

今回の環境

  • Ubuntu Server 20.04 on VMware ESXi

いざ挑戦

まずは事前準備

uidmap 導入

ホスト上に newuidmap と newgidmap のインストールが必要です。

とのことなので、インストールしておきます。

$ sudo apt install -y uidmap

これをインストールしていないと Docker をインストールする際に

# Installing stable version 19.03.12
# Missing system requirements. Please run following commands to
# install the requirements and run this installer again.
# Alternatively iptables checks can be disabled with SKIP_IPTABLES=1

cat <<EOF | sudo sh -x
apt-get install -y uidmap
EOF

と言われるので入れ忘れないようにしましょう。(体験談)

UID/GID 確認

/etc/subuid と /etc/subgid はユーザに対して、少なくともサブオーディネイト UID/GID を 65,536 含むべきです。

とのことなので確認しておきましょう。

$ grep ^$(whoami): /etc/subuid
dev:100000:65536
$ grep ^$(whoami): /etc/subgid
dev:100000:65536

65536 が両方に含まれていれば OK です。

インストール

ワンライナーで導入できるのは通常の Docker と同じです。

$ curl -fsSL get.docker.com/rootless | sh
# Installing stable version 19.03.12
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 57.9M  100 57.9M    0     0  18.0M      0  0:00:03  0:00:03 --:--:-- 18.0M
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 18.3M  100 18.3M    0     0  6245k      0  0:00:03  0:00:03 --:--:-- 6245k
# starting systemd service
● docker.service - Docker Application Container Engine (Rootless)
     Loaded: loaded (/home/dev/.config/systemd/user/docker.service; disabled; vendor preset: enabled)
     Active: active (running) since Sat 2020-07-25 08:07:23 UTC; 21ms ago
       Docs: https://docs.docker.com
   Main PID: 45635 (rootlesskit)
     CGroup: /user.slice/user-1000.slice/user@1000.service/docker.service
             tq45635 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/dev/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
             tq45647 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/dev/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
             mq45656 newgidmap 45647 0 1000 1 1 100000 65536

Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + which slirp4netns
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + [ -z ]
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + which vpnkit
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + net=vpnkit
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + [ -z ]
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + mtu=1500
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + [ -z ]
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + _DOCKERD_ROOTLESS_CHILD=1
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + export _DOCKERD_ROOTLESS_CHILD
Jul 25 08:07:23 docker dockerd-rootless.sh[45635]: + exec rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/dev/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:42:53 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:49:35 2020
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          v1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
# Docker binaries are installed in /home/dev/bin
# WARN: dockerd is not in your current PATH or pointing to /home/dev/bin/dockerd
# Make sure the following environment variables are set (or add them to ~/.bashrc):

export PATH=/home/dev/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock

#
# To control docker service run:
# systemctl --user (start|stop|restart) docker
#

これでインストールが完了しました。
ですがこのままでは docker コマンドは使えません。
※パスが通っている場所にインストールしたわけではないため
docker コマンドを使えるようにするために環境変数に追加します。

$ export PATH=/home/dev/bin:$PATH
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock

実際に確認してみましょう。

$ docker info
Client:
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 1
 Server Version: 19.03.12
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: false
 Logging Driver: json-file
 Cgroup Driver: none
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
  rootless
 Kernel Version: 5.4.0-42-generic
 Operating System: Ubuntu 20.04.1 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 8
 Total Memory: 7.774GiB
 Name: docker
 ID: NURI:7LP5:IHUJ:VSSV:6E5W:VMLS:4D52:X5EJ:CB24:CDJ2:OMAO:LMWB
 Docker Root Dir: /home/dev/.local/share/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: true
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine

docker コマンドが使えていることと、 Security Optionsrootless が含まれている点が確認できました。

使ってみる

とりあえず Nginx を動かしてみましょう。

$ docker run -dP --name nginx nginx
11cd14bb3bf8d4b99aa46be825bfd7f51143bc578e68ef265f3add1baa0b19ed
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
11cd14bb3bf8        nginx               "/docker-entrypoint.…"   19 seconds ago      Up 18 seconds       0.0.0.0:32768->80/tcp   nginx
$ curl -s localhost:32768 | head -n 5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

ポートフォワードもしっかり動いていることが確認できました。

$ ps -aux | grep docker
dev         1519  0.0  0.0 112232  6856 ?        Ssl  10:18   0:00 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/dev/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
dev         1528  7.4  0.1 112872 12060 ?        Sl   10:18   0:05 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/dev/bin/dockerd-rootless.sh --experimental --storage-driver=overlay2
dev         1562 20.5  0.8 1638352 67892 ?       Sl   10:18   0:14 dockerd --experimental --storage-driver=overlay2
dev         1583  1.1  0.2 1241920 23784 ?       Ssl  10:18   0:00 containerd --config /run/user/1000/docker/containerd/containerd.toml --log-level info
dev         1875  0.0  0.0 106976  4780 ?        Sl   10:19   0:00 /home/dev/bin/rootlesskit-docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32768 -container-ip 172.17.0.2 -container-port 80
dev         1880  0.0  0.0 103372  2364 ?        Sl   10:19   0:00 docker-proxy -container-ip 172.17.0.2 -container-port 80 -host-ip 127.0.0.1 -host-port 32768 -proto tcp
dev         1891  0.0  0.0 108872  4604 ?        Sl   10:19   0:00 containerd-shim -namespace moby -workdir /home/dev/.local/share/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/2f657a2a65b754ff9bd59254f1cd36b745c3f2679aa795c474e6ba29ead4e560 -address /run/user/1000/docker/containerd/containerd.sock -containerd-binary /home/dev/bin/containerd -runtime-root /run/user/1000/docker/runtime-runc
dev         1976  0.0  0.0   5192   740 pts/0    S+   10:20   0:00 grep --color=auto docker

また、 root ユーザーではなく dev ユーザーで実行していることも確認できました。

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

Docker逆引きメモ

必要に迫られたDockerの逆引きメモです。

Postgresコンテナでlocaleにja_JP.utf8を設定

手順としてはDockerFile作成、imageのビルドを行う。
Visual Studio CodeのDocker Pluginを使用すると楽。

環境

  • Windows10 Pro 1909
  • Docker 19.03.8

Docker file

FROM postgres:9.6.11
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.utf8
ENV LANG ja_JP.utf8

Build

docker build --pull --rm -f "postgres.9.6.11_ja.dockerfile" -t postgres:9.6.11_ja "."

参考

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

MacでDockerコンテナ上にRedmineを構築する

やろうとした理由

Redmineに対する知識が足りないなーと思ったから。
業務で使っているけれどいつも使う側なので、提供する側のことも覚えておかないとこれから困りそう。

MacでDockerコンテナ上にRedmineを構築するよ

本当は、自分のサーバに立てたかったんだけど・・・断念。
他の人も使えるならEC2上に立てて提供するとか考えたんだけど、現状私しかいないし、私もそんなにガッツリ使い込むようなこともなさそうなので。
簡単な実験と動作確認であれば、自分のMacにインストールすれば良いかな、と。

本当にMacにRedmineを直接入れようとしたけれど、Dockerでいいんじゃないか?ついでにdockerも勉強しなきゃ、ということでDockerを使うことにしました。

Dockerをインストールしておくこと

MacにはDocker Desktopをインストールしておきました。
この構築時点の状態は次の通り。

スクリーンショット 2020-07-25 16.22.27.png

DockerでRedmineを立ち上げる・・・前の事前確認

1. Macのバージョンを確認しておく

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.5
BuildVersion:   19F101

2. Dockerのバージョンを確認しておく

$ docker --version
Docker version 19.03.8, build afacb8b

3. Dockerイメージを確認しておく

$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
hybridcloudibm/wexdg   latest              b482197b4310        2 years ago         5.83GB

4. 起動中のDockerコンテナを確認しておく

$ docker ps -a
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                       PORTS                                      NAMES
c522c16cdb88        hybridcloudibm/wexdg   "/opt/ibm/wex/sbin/e…"   22 months ago       Exited (255) 20 months ago   8000/tcp, 0.0.0.0:443->443/tcp, 9080/tcp   wex-d-g

5. docker-compose.ymlファイルを作成しておく

docker-composeするために必要なので、用意しておきましょう。

docker-compose.yml
version: '3.8'
services:
  redmine:
    container_name: redmine
    image: redmine
    restart: always
    ports:
      - 3000:3000
    volumes:
      - ./Redmine/plugins:/usr/src/redmine/plugins
      - ./Redmine/themes:/usr/src/redmine/public/themes
    environment:
      REDMINE_DB_MYSQL: redmine-db
      REDMINE_DB_PASSWORD: redmine
  redmine-db:
    image: mariadb
    container_name: redmine-db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: redmine
      MYSQL_DATABASE: redmine
    volumes:
      - ./db:/var/lib/mysql
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci

DockerでRedmineを立ち上げる(docker-compose)

1. docker-composeコマンドを実行する

一度、docker-compose.ymlの書き方がまずくて失敗しました。

$ docker-compose up -d
Pulling redmine (redmine:)...
latest: Pulling from library/redmine
6ec8c9369e08: Pull complete
c307964a7468: Pull complete
fd2d286493eb: Pull complete
cf773e09df35: Pull complete
f41f225850f3: Pull complete
f5fa7c9620c6: Pull complete
d8bca7298389: Pull complete
80af857119c9: Pull complete
4c1c6ad5d293: Pull complete
11ed9a8222ad: Pull complete
d89a2de0559e: Pull complete
fa1eb2d25805: Pull complete
25bd4bebd31d: Pull complete
Digest: sha256:d2b5a7acbf2ecbc7f5f7078cd3479237553f921ef636e8112bb7430b32ad0222
Status: Downloaded newer image for redmine:latest
Pulling redmine-db (mariadb:)...
latest: Pulling from library/mariadb
3ff22d22a855: Pull complete
e7cb79d19722: Pull complete
323d0d660b6a: Pull complete
b7f616834fd0: Pull complete
78ed0160f03e: Pull complete
a122e9306ac4: Pull complete
673e89352b19: Pull complete
caf1e694359b: Pull complete
04f5e4f6ead3: Pull complete
a41772aadb3d: Pull complete
c3811aa2fa0a: Pull complete
655ad574d3c7: Pull complete
90ae536d75f0: Pull complete
Digest: sha256:812d3a450addcfe416420c72311798f3f3109a11d9677716dc631c429221880c
Status: Downloaded newer image for mariadb:latest
Creating redmine    ... done
Creating redmine-db ... done

2. dockerイメージを確認する

redmineとmariadbが追加されていることが確認できます。

$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
mariadb                latest              8075b7694a2d        15 hours ago        407MB
redmine                latest              809721a71330        2 days ago          544MB
hybridcloudibm/wexdg   latest              b482197b4310        2 years ago         5.83GB

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

redmineとmariadbが起動(STATUSでUpになっている)していることが確認できます。

$ docker ps -a
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                       PORTS                                      NAMES
e9c787057ae4        redmine                "/docker-entrypoint.…"   25 seconds ago      Up 23 seconds                0.0.0.0:3000->3000/tcp                     redmine
8619aa4a4c36        mariadb                "docker-entrypoint.s…"   25 seconds ago      Up 23 seconds                3306/tcp                                   redmine-db
c522c16cdb88        hybridcloudibm/wexdg   "/opt/ibm/wex/sbin/e…"   22 months ago       Exited (255) 20 months ago   8000/tcp, 0.0.0.0:443->443/tcp, 9080/tcp   wex-d-g

4. ブラウザで起動したRedmineを確認する

http://localhost:3000/ で開くはず。
開かなかったら、ここまでの何かが悪い。

20200724_IMG_0000.png

これで本来やりたかったことができる・・・!
Redmineへのログインは admin/admin です。
ログインしたらパスワード変更を求められます。

やってみた感想

本来やりたかったRedmine以前のDocker周りで苦労しました。
Dockerを全然覚えていないんだな、ということを再認識。
使いこなせると本当に便利だと思うんだけれど・・・コンテナも今や「できて当たり前」なので、ちゃんと勉強しなきゃ。

参考にさせていただいた記事など

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

Kerberos.io Setting @ docker

Kerberos.oiは監視カメラ ソリューションで、設定も単純。Dockerで導入するのも簡単だけど、ドキュメントが少ないのでdocker-compose.yamlを作成してみた時のメモを公開。

 kerberos.io
 https://www.kerberos.io/

設定数分で簡易NVRとしては十分な機能が利用できます。
image.png

準備

  • Linux Server:Ubuntu 20.04 LTS [hogehoge.com]
  • カメラ1:IP Camera: Axis M2026LE Mk-II. StreamはH264の入力には対応していないのでMJPEGを選択。
    RTSP Address: rtsp://username:pass@192.168.1.149/onvif-media/media.amp?profile=profile_1_jpeg&sessiontimeout=60&streamtype=unicast&
    
  • カメラ2:USB UVC Camera:中国で買った安いもの。ラズパイならPiCameraでもOK。
    /dev/video0
    

UVC Camera Setting

 
* ディレクトリ作成と認証ファイルのシンボリックリンク

  $> mkdir kerberos_usb
  $> cd kerberos_usb
  $> touch docker-compose.yaml
  
  • docker-compose.yaml

    version: '2'
    services:
    app_krb:
      image: kerberos/kerberos
      devices:
      - /dev/video0:/dev/video0:mwr
      environment:
      - VIRTUAL_HOST
      - TZ=Asia/Tokyo
      ports:
      - 30080:80
      - 8889:8889
      volumes:
      - ./kerberosio:/etc/opt/kerberosio
    
  • アクセスして確認
    http://hogehoge.com:3380/

  • USB UVCカメラ設定
    LoginしてConfiguration->Machinery->Advanced設定->Capture->USBcamera
    image.png

  • ストリーム確認
    http://user:pass@hogehoge.com:8889/mjpeg

IP Camera Setting

  • ディレクトリ作成と認証ファイルのシンボリックリンク

    $> mkdir kerberos_ip
    $> cd kerberos_ip
    $> touch docker-compose.yaml
    
  • docker-compose.yaml

    version: '2'
    services:
    app_krb:
      image: kerberos/kerberos
      environment:
      - VIRTUAL_HOST
      - TZ=Asia/Tokyo
      ports:
      - 30081:80
      - 8888:8889
      volumes:
      - ./kerberosio:/etc/opt/kerberosio
    
  • アクセスして確認
    http://hogehoge.com:3381/

  • IPカメラ設定
    LoginしてConfiguration->Machinery->Advanced設定->Capture->IP Camera
    image.png

  • ストリーム確認
    http://hogehoge.com:8888/mjpeg

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

Rails アプリケーション作成手順

概要

Dockerを使ってRails開発を始めれるように自分用のメモとして作成しました。
使用するDBはmysqlです。

1.アプリケーション用のbuild contextを作成

フォルダの名前:アプリケーション名
build context内:Dockerfile, docker-compose.yml, Gemfile Gemfile.lock
を作成します。

2.rails new を実行する

コマンド
$docker-compose run web rails new . --force --database=mysql --skip-bundle

3.database.ymlを編集する

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # docker-compose.ymlのMYSQL_ROOT_PASSWORD
  host: db # docker-compose.ymlのservice名

4.docker buildしてGEMを読み込む

コマンド
# コンテナをビルド
$ docker-compose build 

# コンテナを起動
$ docker-compose up -d

DBを作成

コマンド
$ docker-compose run web rails db:create

ブラウザでlocalhost:XXXXにアクセスしてサーバ起動を確認する。

スクリーンショット 2020-08-09 16.18.37.png

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

docker runでエラーが出る時の対処法(docker: you are not authorized to perform this operation: server returned 401.)

環境 バージョン
macOS Catalina 10.15.6
Docker 19.03.8

Macのターミナルからdocker runコマンドで起動しようとした時に
以下のようなエラーが発生したので、その対処法を備忘録としてまとめます。

docker: you are not authorized to perform this operation: server returned 401.
See 'docker run --help'.

原因

どうやらDockerの「Docker Content Trust(DCT)」という検証機能が影響しているようでした。
これは、イメージが改ざんされていないか等を検証する機能なのですが、イメージに特に問題がないことが
わかっている場合は、以下のコマンドで環境変数を設定して、DCTを無効化します。

$ export DOCKER_CONTENT_TRUST=0

これで、docker runコマンドが実行できるようになりました。

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

ローカルのDockerにLAMP環境を構築

はじめに

ローカルのDockerにLAMP環境を構築する方法です。
以下の環境を構築します。
Windows10Proに構築します。

  • CentOS7.4.1708
  • PHP7.2
  • Laravel5.5
  • Apache
  • MySQL5.7
  • phpMyAdmin]

※予め出来ているDockerファイルを利用するため、Dockerファイルの作成方法などを記載するわけではありません。

Docker Desktopのインストール

以下のサイトからDocker Desktopをインストールします。
https://www.docker.com/get-started

Dockerファイル群の配置

以下から、ApaLara.zipをDLし、任意の場所に配置します。
https://github.com/shinjinakatamari/LAMP

配置フォルダの追加

DockerDeskTopを開き、のsetting>Resources>FILE SHARINGで設定フォルダを追加し、Apply&Restartをクリックする

コマンドの実施

コマンドプロンプトを起動し、以下のコマンドで、配置フォルダに移動する。

cd c:¥・・・

以下のコマンドを実行し、Dockerの作成と立ち上げを行う。

docker-compose build
docker-compose up -d

起動確認

http://localhost/
上記にアクセスすると、Laravelのページが表示される。

http://localhost:8080/
上記にアクセスすると、PHPMYAdminのページが表示される。

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

ISUCON9予選をDockerだけで動かす

isucon/isucon9-qualifyを、ローカル環境にDockerを使って構築します。MySQLやGoはインストールすること無くDockerだけで動かします。アプリケーションにはRubyを利用します。(Rubyもインストールする必要はありません)

完成品は下記です。
https://github.com/s2terminal/isucon9-qualify-docker

環境

構築手順

まずgit cloneします。

$ git clone git@github.com:isucon/isucon9-qualify.git
$ cd isucon9-qualify

下準備

リポジトリに含まれていない初期画像ファイルのダウンロードが必要なのですが、1.5GBあるので先に落としておきます。

$ wget https://github.com/isucon/isucon9-qualify/releases/download/v2/bench1.zip
$ wget https://github.com/isucon/isucon9-qualify/releases/download/v2/initial.zip

ダウンロードできたら、解凍してそれぞれ所定のディレクトリに移動します。

$ unzip bench1.zip
$ unzip initial.zip
$ mv v3_initial_data webapp/public/upload
$ mv v3_bench1 initial-data/images

初期データの準備をします。中ではperlのDockerコンテナを使ってSQLを生成するなどしているようです。

$ cd initial-data
$ make

Ruby環境準備

Rubyアプリケーションを実行する準備をします。webapp/rubyディレクトリにDockerfileを配置します。

webapp/ruby/Dockerfile
FROM ruby:2.7.1
WORKDIR /app

RUN apt-get update \
 && apt-get install -y \
    mariadb-client \
    less

COPY ./Gemfile ./
COPY ./Gemfile.lock ./
RUN bundle install

CMD /bin/sh -c "while sleep 1000; do :; done"

mariadb-clientは必須です。他にlessだけ入れていますが、追加で欲しい物があれば随時入れておきます。ISUCON用ということは中に入ってなんやかんやしたいと思うので、キャッシュを消したりはせずに置いておきます。

CMD /bin/sh -c "while sleep 1000; do :; done"で、コンテナが立ち上がりっぱなしになるようにしています。

プロジェクトルートにdocker-compose.ymlを配置します。

docker-compose.yml
version: '3'
services:
  ruby:
    build: ./webapp/ruby
    volumes:
      - .:/app
    ports:
      - 127.0.0.1:5555:5555
      - 127.0.0.1:7000:7000
      - 127.0.0.1:8000:8000
    environment:
      - MYSQL_HOST=db
      - MYSQL_USER=root
      - MYSQL_PASS=secret

MySQL環境準備

データベースの準備をします。DBだけDockerを使ってISUCON9の環境構築した - teru_0x01.logを参考に、webapp/sql/conf/my.cnfを作成します。

$ mkdir webapp/sql/conf
webapp/sql/conf/my.cnf
[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

slow_query_log = 1
long_query_time = 5


[client]
default-character-set = utf8mb4


# Custom config should go here
!includedir /etc/mysql/conf.d/

docker-compose.ymlに下記を追記します。

docker-compose.yml
  db:
    image: mysql:5.7
    volumes:
      - ./webapp/sql:/docker-entrypoint-initdb.d
      - ./webapp/sql/conf:/etc/mysql/conf.d
      - ./datadir:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_HOST=127.0.0.1
      - MYSQL_USER=root
      - MYSQL_PASS=secret
    ports:
      - 127.0.0.1:3306:3306

アプリケーションの起動

Rubyコンテナに入って下記実行します。

$ docker-compose exec ruby /bin/bash

初回起動時やDBをリセットした時などは、DBの初期化処理を実行します。

# ./webapp/sql/init.sh

isucon9-qualifyのプロビジョニングファイルのサービス定義ディレクトリを参考に、アプリケーションを起動します。

# cd webapp/ruby && bundle exec rackup --port 8000 --host 0.0.0.0

http://localhost:8000/ にアクセスして、ISUCARIが表示されたら成功です。

もし下記のようなエラー画面が表示された場合はRubyのエラーログを確認します。(自分の場合はDBの接続文字列が間違っていました)

git管理する場合は、MySQLのデータマウントに使うディレクトリを.gitignoreに入れておきます。ついでに私はtmp/ディレクトリにダウンロード済ファイルを放り込んでいましたので、お好みで。

.gitignore
datadir/
tmp/

ベンチマーカーのビルド

ベンチマーカーの実行バイナリはリポジトリに含まれておらず、Goでビルドする必要があります。ビルド用のコンテナを準備するためにdocker-compose.ymlに追記します。

docker-compose.yml
  golang:
    image: golang:1.14.6
    volumes:
      - .:/app
    working_dir: /app
    command: /bin/sh -c "while sleep 1000; do :; done"

ベンチマーカーのMakefileはプロジェクトルートにありますので、下記コマンドでビルドします。

$ docker-compose exec golang make

他にもGoに依存している所があるかなと思ってdocker-compose.ymlに記述して立ち上げておきましたが、このベンチマーカーのビルドの1回しか使いませんでした。1回$ docker runするだけで良かったかもしれません。

ベンチマーク実行

rackを起動した状態で、Rubyのコンテナ内で$ ./bin/benchmarkerすればベンチマークを実行できます。しかし今回使った環境(Chromebook C434TA Core i5 Crostini + Docker)では重すぎるのか、スコア記録前の/initializeへのPOSTでタイムアウトしてしまいスコアが付きませんでした。

調べてみると、init.shのSQLを流すのに1分以上かかっているのが原因のようです。

$ time ./webapp/sql/init.sh
(中略)
real    1m2.555s
user    0m1.872s
sys     0m0.536s

ISUCON9予選マニュアルには POST /initialize が20秒以内と記載されており、ベンチマーカーの実装も20秒でタイムアウトするようになっているようです。

これは恐らくルール違反なのですが、init.shのSQLを先に手動で流した後、SQL実行をスキップするように修正してからベンチマークを動かすことで、やっとスコアが付きました。

webapp/sql/init.sh
 export LANG="C.UTF-8"
 cd $CURRENT_DIR

-cat 01_schema.sql 02_categories.sql initial.sql | mysql --defaults-file=/dev/null -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER $MYSQL_DBNAME
+# cat 01_schema.sql 02_categories.sql initial.sql | mysql --defaults-file=/dev/null -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER $MYSQL_DBNAME
# ./bin/benchmarker
(中略)
{"pass":true,"score":810,"campaign":0,"language":"ruby","messages":[]}

スコアは810点でした。Macbook Proだと3,000ぐらい出るようですが、Chromebook上のLinux仮想環境と、その上にDockerを載せているという二重のオーバーヘッドがパフォーマンスを下げているのでしょうか。

参考

完成品は下記です。
https://github.com/s2terminal/isucon9-qualify-docker/pull/1

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

Docker上にGrafanaとInfluxDBを構築する

事前準備

事前にWSL2とかDocker Desktopとかのインストールを終わらせておくこと
- Windows10でWSL2を使う
- Docker Desktop をWSL2で利用する。

環境

Windows10
WSL2
Docker Desktop
Grafana 7.1.1

コンテナを作る

docker-compose.yml

version: "2"

services:
  influxdb:
    image: influxdb
    container_name: local-influxdb  #grafanaからアクセスするための名前
    volumes:
      - /home/xxxx/docker/grafana/influxdb:/var/lib/influxdb
    ports:
      - 8086:8086

  grafana:
    image: grafana/grafana:7.1.1
    ports:
      - 8085:3000 #grafanaのポート
    user: "472"
    volumes:
      - /home/xxxx/docker/grafana/grafana:/var/lib/grafana    
    depends_on:
      - influxdb
    environment:
      - GF_SERVER_ROOT_URL=http://192.168.77.6:8085
      - GF_SECURITY_ADMIN_PASSWORD=admin

起動

docker-compose up -d

ログを確認

docker-compose logs -f

はい、エラーが出ました。

grafana_1   | You may have issues with file permissions, more information here: http://docs.grafana.org/installation/docker/#migration-from-a-previous-version-of-the-docker-container-to-5-1-or-later

権限がないということなので、ホスト上でオーナーを変更

cd /home/xxxx/docker/grafana/
mkdir grafana
sudo chown 472:472 grafana

改めて起動して、ログを確認。エラーが出てなければ完了です。

Grafanaを表示する

ブラウザでhttp://localhost:8085にアクセスすると、以下の画面が表示されればOKです。
admin/adminでログインできます。

image.png

さいごに

docker使うとほんと簡単に環境ができますね。
さて次は、中身の設定ですね。

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

Docker の Volume がよくわからないから調べた

前提

  • 本記事執筆時の Docker 最新バージョン: 19.03
  • 書いている人は、業務で本格的に docker を使ったりしておらず、個人開発で適当に使っていたが、よくわからず使っている部分もあったので改めて調べてるレベル。

悩み

  • データを永続化したい場合に、-vオプションや、Dockerfile のVOLUMEや、Compose ファイルのvolumesを使ったりしていたが、違いをよくわかっていない。それぞれ記法もいくつかあるようだがよくわかっていない。
  • コンテナ消した時に勝手に破棄される場合もあってよくわからないので、ホスト側のディレクトリをコンテナ内のディレクトリにマッピングするやつを使いがち。ホスト側のディレクトリが見えているので安心しちゃう。
  • Data Volume コンテナがいいぞ、と言われたので使っているが、なんでいいのかわかってない。
  • とにかくよくわからないまま使っている。

この記事の概要

  • 公式ドキュメントに全部書いてあるので詳しくはこちらへ。この記事は理解のポイントだけおさえてあります。
  • Docker のストレージにはボリュームとバインドマウントと tmpfs の3種類がある。
  • --volumeというオプションでボリュームもバインドマウントも指定できるので、オプション名と機能の名前がかぶっててややこしいから混乱する。
  • -vオプションじゃなく--mountオプションを使おう。
  • Data Volume コンテナは、そういう機能があるわけではなくデザインパターン。
  • 名前付きボリュームを使おう。

Docker のストレージの考え方

コンテナ内で発生したデータは同じコンテナ内のどこかに書き出されるが、コンテナを破棄すると消えてしまいます。データだけはコンテナが消えても保存しておきたかったり、別コンテナで使いたいというニーズに対して、Docker はコンテナ外にデータ保存領域をつくる機能を提供しています。

具体的には3種類。(厳密にいうと、Windows で名前付きパイプっていうのもある)

  • ボリューム
    Docker の管理下でストレージ領域を確保する。Linux なら /var/lib/docker/volumes/以下。
    名前付きボリュームと匿名ボリュームがあり、名前付きの場合は Docker ホスト内で名前解決できるのでアクセスしやすい。匿名ボリュームは適当にハッシュ値が振られる。
    他のプロセスからはさわれないので安全。基本はこれを使うのがよい。
  • バインドマウント
    ホスト側のディレクトリをコンテナ内のディレクトリと共有する。
  • tmpfs
    メモリ上にストレージ領域を確保する。名前の通り一時的な領域となる。用途としては、機密性の高い情報を一時的にマウントする場合などに使う。

-vオプションも、VOLUMEvolumesで指定するものに関しても、基本的には上記のどれかを扱っています。普通に使う分にはボリュームかバインドマウントになるので、以下はそのふたつをメインに説明します。

-v オプション

自分の場合は-vオプションでいろいろ指定ができるのが混乱の原因のひとつでした。
上記3種類のストレージが理解できていればなんてことないのですが。
sample/image:latest というイメージを run するとして、以下に記法による違いを列挙します。

  • 匿名ボリューム
    $ docker container run -v /hoge sample/image:latest
    ホスト側には、Linux なら/var/lib/docker/volumes/以下に領域が確保され、コンテナ内の/hogeディレクトリと共有される。識別のためにハッシュ値が振られる。同じネットワークからそのハッシュ値でアクセスできる。
  • 名前付きボリューム
    $ docker container run -v name:/hoge sample/image:latest
    匿名ボリュームと同様に、ホスト側には、Linux なら/var/lib/docker/volumes/以下に領域が確保され、コンテナ内の/hogeディレクトリと共有される。nameという名前がついているので、同じネットワーク内から name というホスト名でアクセスできる。
  • バインドマウント
    $ docker container run -v ${PWD}/data:/hoge sample/image:latest
    ホスト側のカレントディレクトリ配下のdataディレクトリと、コンテナ側の/hogeディレクトリが共有される。

--mount オプション

Docker 17.06 から単一のコンテナに対しても--mountオプションが使えるようになっており、公式でも-vの代わりにこちらを使うことが推奨されてます。(Swarm では前から使っていた)
キーバリュー形式で各要素が指定できるので、こちらの方が記法としては明快です。長くなるけど。
こんな感じで指定します。
$ docker container run --mount type=volume, src=name, dst=/hoge sample/image:latest

  • type
    volume, bind, tmpfs を指定
  • src
    名前付きボリュームであれば、その名前。バインドマウントであればホスト側のディレクトリを指定します。匿名ボリュームであれば省略します。
    他にsourceとも書ける。
  • dst
    コンテナ側のディレクトリを指定します。
    他にdistination, targetとも書ける

--rm オプションをつけたとき

$ docker container runする時に、--rmオプションをつけると、匿名ボリュームの場合はコンテナ停止と同時にボリュームも破棄されます。
名前付きボリュームの場合は--rmオプションでコンテナが破棄されても破棄されません。

ただ名前付きボリュームであっても、マウントされたコンテナがない状態では、$docker volume prune などでは破棄されます。

Data Volume コンテナは単なるデザインパターン

Volume を調べているとよく出てくる Data Volume コンテナ。
これは Docker の機能としてそういうものがあるのではなく、ボリュームを扱いやすくするためのデザインパターンです。

ボリュームに対してコンテナを紐付けておくとなにがうれしいのか、というと、ボリュームへのアクセスが抽象化できるので扱いやすくなったり、うっかり prune で破棄してしまったりしなくて済むということじゃないかと思います。(たぶん)

結局どれ使ったらいいの?

基本的には名前付きボリュームを使いましょう。
Docker 領域下にボリュームができるから安全ですし、名前でアクセスできるので扱いやすいです。
バインドマウントは、ホスト側の環境に依存しますし、ボリュームに比べて機能が制限されています。ホスト側からデータを注入したいときや、開発時に更新したソースコードやビルドを即時反映したい場合などの場合はバインドマウントを使うのがよいです。

まとめ

  • Docker のストレージにはボリュームとバインドマウントと tmpfs の3種類がある。
  • -vオプションじゃなく--mountオプションを使おう。
  • 基本は名前付きボリュームを使おう。

参考文献

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

yamlで保存してあるconda仮想環境をDocker上で構築

Docker上でPython環境を作るときにpipPipenvなども使うのだが、使うライブラリやパッケージの関係でconda環境を使えると便利なことがある。
思った通りのconda環境をDocker上で作るための方法の自分用メモ。

1. 動機

Pythonの開発やデータ分析などでMinicondaやAnacondaを使うことがある。
(依存ライブラリも割と一緒にインストールしてくれるので自分で入れなくて良いなど環境構築が楽だったり、バイナリ互換性の問題でcondaの特定チャンネルからのインストールを推奨されるパッケージがあったりする)
その際、やっている作業毎にパッケージやバージョンを変えたかったり、環境を後で再現しやすくするなどの目的で基本はconda仮想環境を使うようにしている。
が、Python以外のところから環境設定・構築が必要になることがあったり、仮想環境と言えどもPCの環境を汚して他と干渉したりの問題が起きるなどで、結局Dockerで対応することが多いため。(で、そのDockerへの移植時に結構苦戦したため)

yamlファイルを使う理由

詳細は次章として、

  • PC上の既存のconda仮想環境を容易にDocker上に移植出来る
  • 逆に、Docker上で開発したconda環境を手元のAnacondaやMinicondaですぐに再現出来る

など

2. conda環境の保存と再構築

2-1. yamlファイルからのconda環境生成

(公式ドキュメント)

conda env create -f environment.yml

を実行すると、environment.ymlに記載した情報の通りにconda環境が生成される。
例えばenvironment.ymlに対応するものをgit上などで管理しておくと、可搬性や再現性の点で便利。

environment.ymlの中身は以降の記述を参考
conda env create以降のオプションの詳細ははっきり覚えていない。。。発見出来てかつ気が向いたら後で追記。ちなみにconda createのオプションはこんな感じ

2-2. yamlファイルの生成

(公式ドキュメント)

対象のconda仮想環境に入って、例えば以下のようにする

conda env export > environment.yml

すると、該当のconda環境でインストールされていた全パッケージとそのバージョン(ビルド情報含む)がenvironment.ymlに書き出される。(pip freezeするような感覚)

ちなみに、仮想環境に入らなくとも

conda env export  -n <仮想環境名> > environment.yml

のようにして、明示的に書き出す仮想環境名を指定することも出来る。

また、上記の2つは完全にバージョン情報が固定されるが、そこまではせずに最低限の条件だけ指定したい場合がある。
(公式ドキュメント)を参考にして、パッケージと使うチャンネルなどを書いたyamlファイルを自分で作ることが出来る。(以下、適当な例)

create_env_sample.yml
name: hoge
channels:
  - conda-forge
  - default
dependencies:
  - python==3.7.*
  - pandas<1.0
  - flask
  - pip
  - pip:
    - pynvim
  • 依存ライブラリなどは(conflictが起きなければ)自動でインストールされる。
  • pipでインストールされるものも書ける。(pip:の下のところ)
    • 先にcondaでのインストールが実行され、最後にpipでのインストールが実行される
    • condaでインストールするパッケージ内で明示的にpipをインストールしておくと無難かもしれない
      • 「conda仮想環境のものでないpipを使う可能性がある」的な趣旨のwarningが出る

なお、上記の例のyamlファイルだと、

# condaでパッケージをインストール
conda create -n hoge python=3.7 pandas\<1.0 flask pip -c conda-forge
# 作った仮想環境に入る
conda activate hoge
# 最後にpipで必要なパッケージをインストール
python -m pip install pynvim

みたいな感じで環境構築するのと大体同じ。(※厳密に本当に同じかまでは未検証)

3. Docker上でconda仮想環境を作る

(方針)

  • 前章を踏まえて用意してあるyamlファイルをDockerイメージ内にCOPYなどしておく
  • yamlファイルからの環境作成はMinicondaを利用して行う
    • Miniconda自体のインストールは公式などを参照
    • continiuum公式のDockerfileなども参考になる
  • 上記のMinicondaはyamlファイルから仮想環境を作ること「のみ」に使い、作り終わったら元のMinicondaはキャッシュ毎全部消す(←容量の無駄なので)
    • 代わりにyamlから作られた仮想環境の方をbaseにする
    • (もっと良いやり方があるかもしれない)

Dockerfile作成例(抜粋)

ディレクトリ構成:

tree . 
# .
# ├── Dockerfile
# └── environment.yml

のような感じ。

docker build --tag=hoge .

または

# Dockerfile内のARG部分を制御する場合
docker build \
    --tag=hoge \
    --build-arg VAL1=FOO \
    --build-arg VAL2=BAR \
     . 

のようにすると、ビルドが通ればhogeというイメージが出来る

Dockerfile
ARG BASE_IMAGE=ubuntu:latest
FROM ${BASE_IMAGE}

# system update & package install
RUN apt-get clean && \
    apt-get -y update && \
    apt-get install -y --no-install-recommends \
    unzip bzip2 \
    openssl libssl-dev \
    curl wget \
    ca-certificates \
    locales \
    bash \
    sudo \
    git \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*


# ローカルuser作成
ARG USER_NAME=user
ARG USER_UID=1000
ARG PASSWD=password
RUN useradd -m -s /bin/bash -u ${USER_UID} ${USER_NAME} && \
    gpasswd -a ${USER_NAME} sudo && \
    echo "${USER_NAME}:${PASSWD}" | chpasswd && \
    echo "${USER_NAME} ALL=(ALL) ALL" >> /etc/sudoers && \
    chmod g+w /etc/passwd


# conda用準備
ENV CONDA_DIR=/opt/conda \
    CONDA_TMP_DIR=/tmp/conda \
    HOME=/home/$USER_NAME \
    SHELL=/bin/bash
RUN mkdir -p $CONDA_DIR && \
    mkdir -p $CONDA_TMP_DIR && \
    chown $USER_NAME:$USER_UID $CONDA_DIR && \
    chown $USER_NAME:$USER_UID $CONDA_TMP_DIR
# yamlファイルの取り込み
ARG CONDA_YAML="./environment.yml"
COPY $CONDA_YAML /tmp/conda_packages.yml

USER ${USER_NAME}

WORKDIR $HOME
# miniconda
ARG MINICONDA_VERSION=py37_4.8.3-Linux-x86_64
ARG MINICONDA_MD5=751786b92c00b1aeae3f017b781018df
ENV PATH=${CONDA_DIR}/bin:$PATH

RUN cd /tmp && \
    wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-${MINICONDA_VERSION}.sh && \
    echo "${MINICONDA_MD5} *Miniconda3-${MINICONDA_VERSION}.sh" | md5sum -c - && \
    /bin/bash Miniconda3-${MINICONDA_VERSION}.sh -f -b -p $CONDA_TMP_DIR && \
    rm Miniconda3-${MINICONDA_VERSION}.sh && \
    $CONDA_TMP_DIR/bin/conda env create -f /tmp/conda_packages.yml -p $CONDA_DIR && \
    rm -rf $HOME/.cache/* && \
    rm -rf $CONDA_TMP_DIR/*

# (以下省略)
  • はじめにMinicondaは/tmp/conda/下にインストールする
    • 先述の通り、conda env createをするためだけに使うので、後で/tmp/conda/下をまるっと消す
    • インストール先は-pオプションで指定している
  • 所望のconda環境は/opt/conda/下にインストールされ、ここにPATHを通すことで作成ユーザーにとってのデフォルトPython環境にしている
    • conda環境を/opt/conda/にするために、-p--prefixオプションでインストール先を指定
  • イメージサイズが肥大化しないように、Minicondaのダウンロード・インストールから不要なファイル・キャッシュの削除まで1つのRUNの中で全てをやり切る必要

(補足)

  • 使っているMinicondaは執筆時点(2020-07-24)での最新版なので、ここなどを参考に適宜アップデートする
  • ユーザー作成を行っているが別に必須ではないので、そこを省けばもっとすっきりするはず
  • yamlファイルにおけるprefix:部分があればそれはインストール場所を示すが、conda env create-pないし--prefixオプションでの指定が優先される様子
  • yamlファイルに書くインストールパッケージの中にcondaを書いておくと、/opt/conda/にインストールされる仮想環境が新しいbase環境になる
    • コンテナ使用中に追加で入れたいパッケージが出てきたときにその場でconda install ***を実行出来たり、その後conda env export -n base | tee environment.ymlなどと叩いてインストールパッケージの再確認や新しいyamlファイルの生成が出来る
(更に細かい補足)

たまにconda env exportで生成したyamlファイルをそのままconda env createに渡せないときがある(あった)ので、その例と対処。

  • pipgraphvizをインストールさせたものをconda env exportしてyamlを生成すると、python-graphvizの名前になっている
  • しかし、pip install python-graphvizはエラーになるため、したがってこのyamlファイルをそのまま使うとインストールエラーになってしまう
  • そこで、例えば以下の部分を挿入してyamlファイルを少しだけ書き換えるなどした
RUN sed -i -e 's/python-graphviz/graphviz/' /tmp/conda_packages.yml

sedでyamlファイル内のpython-graphvizgraphvizに置換している

参考

conda環境の保存・構築:
https://qiita.com/nshinya/items/cb1cffabc3305c907bc5
https://qiita.com/yubessy/items/2dd43551aa8308dc7eca

Docker上でのconda環境の構築
https://hub.docker.com/r/continuumio/miniconda/dockerfile
https://github.com/jupyter/docker-stacks/tree/master (←リポジトリ内の各Dockerfileを参照)

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