20191215のdockerに関する記事は22件です。

Naumachiaを使ったペネトレーションテストのトレーニング環境構築

本記事は、NTTコミュニケーションズ Advent Calendar 2019 15日目の記事です。
昨日は @Mahito さんの記事、 保育園にChaos Engineeringを提案した話 でした。

はじめに

先日公開されたNTTコミュニケーションズの開発者ブログの記事にもあったように、NTTコミュニケーションズグループではグループ社員を対象としたセキュリティコンテスト「ComCTF」を開催しています。

私は決勝で出題した「Pentest」という問題を作問しました。
Pentest(ペンテスト)は、ペネトレーションテストと呼ばれるセキュリティテストの略称で、明確な意図を持った攻撃者にその目的が達成されてしまうかを検証します。 1

この問題は仮想の企業ネットワークに侵入し、複数のサーバの脆弱性を悪用、最終的に重要データが保存されているサーバから情報を入手できるかを問う問題で、まさに攻撃者の気持ちになって重要なデータを入手するという目的が達成可能かどうかを検証してもらう、ペネトレーションテストをしてもらう問題でした。

今回、この問題の基盤を作るにあたり、Dockerを使ってペネトレーションテストのトレーニング環境を構築できる Naumachia と呼ばれるOSSを使用しました。

この記事では、Naumachiaの概要と構築方法、この基盤を使ったペネトレーションテストのトレーニング環境構築について紹介します。

Naumachia とは

Naumachiaは、Dockerを使ってクローズドネットワークと脆弱なサーバを構築できるOSSです。

私がこのOSSを知ったきっかけは、Texas A&M University が主催する TAMUctf 19 と呼ばれるCTFです。NetworkPentest というジャンルの問題の基盤にこの Naumachia が使用されています。

なお、このCTFの問題はGitHubで公開されているので興味がある方は見てみてください。

https://github.com/tamuctf/TAMUctf-2019

Naumachia には、以下の機能が実装されています。

  • ユーザごとにトレーニング用のDockerコンテナとネットワークを作成、管理
  • トレーニング環境を他のユーザの環境と分離
  • OpenVPNを使ったトレーニング環境ネットワークへのL2レベルの接続の提供

これにより、以下のようなインターネットからVPNの接続情報を持つユーザのみアクセス可能な専用のトレーニング環境を構築できます。

naumachia.png

例えば、Drupalの任意コード実行の脆弱性(CVE-2018-7600)を使ってシステムに侵入できるか試すような問題を作ろうとした場合、インターネットからアクセスできる問題サーバを作ろうとすると、インターネット上の脆弱性のスキャンに引っかかり、最悪サーバが踏み台にされる可能性もあります。
Naumachia を使えば、インターネットからはVPNの接続情報を持つユーザのみ問題に挑戦できるので、そのようなリスクなく作問できます。

また、L2レベルでのアクセスも提供してくれるので、ARPスプーフィングにような同一LAN内で行われる攻撃手法を試すような問題も作ることができます。

詳しい機能や仕組みは、Naumachia のREADME に書いてあるので、こちらを読むと良いと思います。

Naumachia の構築

ここからは Naumachia の構築手順を紹介します。

動作環境

READMEには、

Obtain a Linux server (tested on Ubuntu 16.04 and 18.04)

と書いてあるので、使うOSは Ubuntu 18.04 がベストでしょう。

しかし、今回のコンテストでは諸事情ありCentOS 7を使ったので、CentOS 7 で検証した構築手順を書いておきます。構築手順を検証したOSの情報は以下のとおりです。

# uname -a
Linux localhost.localdomain 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

Naumachia のインストール準備

Naumachiaを構築するには、docker, docker-compose, Python3, pip3が必要となるので、これらをまずインストールする必要がある。
その後、GitHubにあるNaumachiaのリポジトリからソースコードをCloneし、requirements.txt に書かれているPython3のライブラリをインストールする。

dockerのインストール

# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install -y docker-ce docker-ce-cli containerd.io
# systemctl start docker
# systemctl enable docker

docker-composeのインストール

# curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

Python3.6、pip3のインストール

# yum install -y https://centos7.iuscommunity.org/ius-release.rpm
# yum install python36u python36u-libs python36u-devel python36u-pip

GitHubからソースコードをCloneする

# git clone https://github.com/nategraf/Naumachia.git

Python3のライブラリをインストール

# cat requirements.txt
jinja2==2.10.1
PyYAML==4.2b4
requests==2.21.0
nose2==0.8.0
pytest==4.5.0
hypothesis==4.23.5
# pip3 install -r requirements.txt

Naumachia のセットアップ

トレーニング用のDockerコンテナとネットワークの準備

Naumachiaでユーザに対して提供するトレーニング用のDockerコンテナとネットワークは、 docker-compose.yml で定義します。
Naumachia起動後、OpenVPNでユーザが接続してくると、このdocker-compose.yml をもとに自動でdocker-composeが実行されトレーニングの環境が構築されます。

今回の説明では、Naumachiaの問題集 (nategraf/Naumachia-challenges) から、example というチャレンジを動かしてみます。

問題集のGitHubのリポジトリをCloneし、Naumachiaディレクトリ内の challenges ディレクトリに example チャレンジに必要なファイルをコピーします。

# git clone https://github.com/nategraf/Naumachia-challenges
# mkdir Naumachia/challenges
# cp -r Naumachia-challenges/example Naumachia/challenges

ちなみに、exampledocker-compose.yml は以下のとおりです。
bobalice という2つのコンテナと、 default という1つのネットワークが作成されるのがわかります。

docker-compose.yml
version: '2.4'

# The file defines the configuration for simple Nauachia challenge where a
# sucessful man-in-the-middle (MTIM) attack (such as ARP poisoning) provides a
# solution

# If you are unfamiliar with docker-compose this might be helpful:
# * https://docs.docker.com/compose/
# * https://docs.docker.com/compose/compose-file/
#
# But the gist is that the services block below specifies two containers, which
# act as parties in a vulnerable communication

services:
    bob:
        build: ./bob
        image: naumachia/example.bob
        environment:
            - CTF_FLAG=fOOBaR
        restart: unless-stopped
        networks:
            default:
                ipv4_address: 172.30.0.2

    alice:
        build: ./alice
        image: naumachia/example.alice
        depends_on:
            - bob
        environment:
            - CTF_FLAG=fOOBaR
        restart: unless-stopped
        networks:
            default:
                ipv4_address: 172.30.0.3

networks:
    default:
        driver: l2bridge
        ipam:
            driver: static
            config:
                - subnet: 172.30.0.0/28

カスタマイズされたDocker libnetwork Driverのインストール・起動

上記のNaumachiaのチャレンジでは、すべてのユーザに同じ環境を提供、安全なトレーニング環境を構築するために、カスタマイズされたDocker libnetowrk driverを使用しています。

https://github.com/nategraf/l2bridge-driver
https://github.com/nategraf/static-ipam-driver

これを使うことで、デフォルトのDocker libnetowrk driverではできない以下のことが可能となります。

  • 重複したIPサブネットの許可
  • コンテナネットワークからインターネットへのアクセス禁止

ここでUbuntuやDebianであれば、サービスとしてDriverをインストールする方法が紹介されてますが、今回はCentOSであったため以下のようなスクリプトを作成し、無理やりDriverのプログラムを動かしました。(sysv.sh をRedHat系のOS向けに書き直す余裕はなかった…

driver_start.sh
# Download the static-ipam driver to usr/local/bin
if [ ! -e /usr/local/bin/l2bridge ]; then
  echo "[!] l2bridge driver is not installed"
  echo "[+] Download the l2bridge driver to usr/local/bin" 
  curl -L https://github.com/nategraf/l2bridge-driver/releases/latest/download/l2bridge-driver.linux.amd64 -o /usr/local/bin/l2bridge
  chmod +x /usr/local/bin/l2bridge
else
  echo "[*] l2bridge driver is installed"
fi

# Download the static-ipam driver to usr/local/bin
if [ ! -e /usr/local/bin/static-ipam ]; then
  echo "[!] static-ipam driver is not installed"
  echo "[+] Download the static-ipam driver to usr/local/bin" 
  curl -L https://github.com/nategraf/static-ipam-driver/releases/latest/download/static-ipam-driver.linux.amd64 -o /usr/local/bin/static-ipam
  chmod +x /usr/local/bin/static-ipam
else
  echo "[*] static-ipam driver is installed"
fi

# Activate the service
echo "[+] Startup the servicies" 

if [ ! -e /run/docker/plugins/l2bridge.sock ]; then
  nohup /usr/local/bin/l2bridge > /dev/null 2>&1 &
  echo "[*] Done: l2bridge" 
else
  echo "[!] Started l2bridge driver"
fi

if [ ! -e /run/docker/plugins/static.sock ]; then
  nohup /usr/local/bin/static-ipam > /dev/null 2>&1 &
  echo "[*] Done: static-ipam" 
else
  echo "[!] Started static-ipam driver"
fi

sleep 0.5

# Verify that it is running
echo "[+] Verify that it is running"

echo ""
echo "[*] stat /run/docker/plugins/l2bridge.sock"
stat /run/docker/plugins/l2bridge.sock
#  File: /run/docker/plugins/l2bridge.sock
#  Size: 0               Blocks: 0          IO Block: 4096   socket
#  ...

echo ""
echo "[*] stat /run/docker/plugins/static.sock"
stat /run/docker/plugins/static.sock
#  File: /run/docker/plugins/static.sock
#  Size: 0               Blocks: 0          IO Block: 4096   socket
#  ...

echo ""
echo "[*] Complete!!"

なお、シャットダウンするとDriverのプログラムは停止し、再起動時に立ち上がらないので、再起動時には必ずこれを実行する必要があります。

bridgeを通るパケットのフィルタリング無効

bridgeを通るパケットがフィルタ対象になっているとうまく動かないことがあるようなので、 disable-bridge-nf-iptables.sh を実行します。

disable-bridge-nf-iptables.sh
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 0 > /proc/sys/net/bridge/bridge-nf-call-ip6tables

config.yml の修正

config.example.ymlconfig.yml にコピーして一部を書き換えます。
書き換えるのは、 challenges の部分。
変更する点は以下のとおり。

  • files: に、「トレーニング用のDockerコンテナとネットワークの準備」で作った docker-compose.yml ファイルの場所を書く
  • commonname: にサーバのアドレス(ドメイン、IPアドレス)を書く
# [required] Configurations for each challenge
challenges:
    # [required] An indiviual challenge config. The key is the challenge name
    # This should be a valid unix filename and preferably short
    example:
        # [default: 1194] The exposed external port for this challenges OpenVPN server
        port: 2000
        # [default: [{challenge name}/docker-compose.yml] ] The compose files to which define this challenge
        # Paths should be relative to the challenges directory
        files:
            - example/docker-compose.yml
        # [default: {challenge name}.{domain}] The commonname used for the OpenVPN's certificates
        # This should be the domain name or ip that directs to this challenge
        commonname: 192.168.91.130
        # [default: None] If set, the OpenVPN management interface will be opened on localhost and the given port
        openvpn_management_port: null
        # [default: None] If set, the OpenVPN server will inform the client what IPv4 address and mask to apply to their tap0 interface
        ifconfig_push: 172.30.0.14/28

Naumachiaのビルド

configure.py を実行すると、config.yml に書かれている内容をもとにNaumachiaをbuildします。
これにより、Naumachiaの docker-compose.yml やOpenVPNの鍵や証明書、設定のファイルが自動で生成されます。

# ./configure.py
[INFO] Using config from /root/Naumachia/config.yml
[INFO] Using easyrsa installation at /root/Naumachia/tools/EasyRSA-v3.0.6/easyrsa
[INFO] Rendered /root/Naumachia/docker-compose.yml from /root/Naumachia/templates/docker-compose.yml.j2 
[INFO] Configuring 'example'
[INFO] Created new openvpn config directory /root/Naumachia/openvpn/config/example
[INFO] Initializing public key infrastructure (PKI)
[INFO] Building certificiate authority (CA)
[INFO] Generating Diffie-Hellman (DH) parameters
[INFO] Building server certificiate
[INFO] Generating certificate revocation list (CRL)
[INFO] Rendered /root/Naumachia/openvpn/config/example/ovpn_env.sh from /root/Naumachia/templates/ovpn_env.sh.j2 
[INFO] Rendered /root/Naumachia/openvpn/config/example/openvpn.conf from /root/Naumachia/templates/openvpn.conf.j2 

また、競技用のコンテナもbuildしておきます。

# docker-compose -f ./challenges/example/docker-compose.yml build

競技環境の実行

ここまでの作業を行うと、 docker-compose.yml が自動で生成されているはずなので、buildしてupします。

# docker-compose build
# docker-compose up -d

この状態で docker ps -a で立ち上がってるコンテナを見てみると、以下のようなコンテナが立ち上がっているはずです。

# docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                      PORTS                    NAMES
dd9e858277bd        naumachia/manager     "python -m app"          27 seconds ago      Up 25 seconds                                        build_manager_1
f80057d9dc2e        naumachia/openvpn     "/scripts/naumachia-…"   27 seconds ago      Up 25 seconds               0.0.0.0:2000->1194/udp   build_openvpn-example_1
86fc3709d4e3        redis:alpine          "docker-entrypoint.s…"   27 seconds ago      Up 26 seconds                                        build_redis_1
a0f45e1f292a        naumachia/registrar   "gunicorn -c python:…"   27 seconds ago      Up 26 seconds               0.0.0.0:3960->3960/tcp   build_registrar_1
9d1ef7902351        alpine                "/bin/true"              27 seconds ago      Exited (0) 27 seconds ago                            build_bootstrapper_1

ユーザへ配布するOpenVPN設定ファイルの生成

ユーザがOpenVPNサーバに接続し、トレーニング環境にアクセスするためには設定ファイルが必要です。
これもNaumachiaが自動で生成してくれます。

生成する方法には、以下の2つの方法があります。

  • registrar CLIのPythonスクリプトを使用する
  • registrar serverのREST APIを使用する
    • 3960/tcp で待ち受けてるコンテナがそう
    • 認証がないので外部に公開するときは注意

今回はregistrar CLIのPythonスクリプトを使って、設定ファイルを作成、取得します。
registrar-cliを以下のように実行すると、OpenVPNの鍵、サーバ証明書、認証局の証明書を含んだOpenVPNの設定ファイルが作成できるので、これをユーザに配ります。

# ./registrar-cli example add user1
# ./registrar-cli example get user1 > user1.ovpn
# cat user1.ovpn

client
nobind
dev tap
remote-cert-tls server
float
explicit-exit-notify

remote 192.168.91.130 2000 udp



<key>
-----BEGIN PRIVATE KEY-----
(省略)
-----END PRIVATE KEY-----
</key>
<cert>
-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----
</cert>
<ca>
-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----
</ca>
key-direction 1

cipher AES-256-CBC
auth SHA256
comp-lzo

構築したトレーニング環境で遊んでみる

それでは、構築したトレーニング環境にアクセスして遊んでみましょう。

検証に使用する環境

今回はユーザ側はデフォルトでOpenVPNのクライアントとペネトレーションテスト用のツールがインストールされている Kali Linux を使用します。

# grep VERSION /etc/os-release 
VERSION="2018.1"
VERSION_ID="2018.1"

OpenVPNでトレーニング環境へ接続

生成したOpenVPNの設定ファイルを使って、Naumachia上のトレーニング環境にアクセスします。
Initialization Sequence Completed と出れば成功です!

# openvpn user1.ovpn
Sun Dec 15 06:33:45 2019 OpenVPN 2.4.5 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Mar  4 2018
Sun Dec 15 06:33:45 2019 library versions: OpenSSL 1.1.0h  27 Mar 2018, LZO 2.08
Sun Dec 15 06:33:45 2019 TCP/UDP: Preserving recently used remote address: [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:45 2019 UDP link local: (not bound)
Sun Dec 15 06:33:45 2019 UDP link remote: [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:45 2019 [192.168.91.130] Peer Connection Initiated with [AF_INET]192.168.91.130:2000
Sun Dec 15 06:33:46 2019 Options error: Unrecognized option or missing or extra parameter(s) in [PUSH-OPTIONS]:1: dhcp-renew (2.4.5)
Sun Dec 15 06:33:46 2019 TUN/TAP device tap0 opened
Sun Dec 15 06:33:46 2019 do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Sun Dec 15 06:33:46 2019 /sbin/ip link set dev tap0 up mtu 1500
Sun Dec 15 06:33:46 2019 /sbin/ip addr add dev tap0 172.30.0.14/28 broadcast 172.30.0.15
Sun Dec 15 06:33:46 2019 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
Sun Dec 15 06:33:46 2019 Initialization Sequence Completed

ifconfigでインターフェースの状態を見てみると、tap0 というインターフェースが作成され、172.30.0.14 というIPアドレスが割り当てられていると思います。

# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.91.129  netmask 255.255.255.0  broadcast 192.168.91.255
        inet6 fe80::20c:29ff:fe18:a0c8  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:18:a0:c8  txqueuelen 1000  (Ethernet)
        RX packets 14781  bytes 9483880 (9.0 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6484  bytes 645921 (630.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 31612  bytes 10003030 (9.5 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 31612  bytes 10003030 (9.5 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

tap0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.30.0.14  netmask 255.255.255.240  broadcast 172.30.0.15
        inet6 fe80::c0d8:eeff:fe38:d79b  prefixlen 64  scopeid 0x20<link>
        ether c2:d8:ee:38:d7:9b  txqueuelen 100  (Ethernet)
        RX packets 16  bytes 1272 (1.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 21  bytes 1622 (1.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

このとき、NaumachiaのサーバでDockerコンテナとネットワークの状態を見ると、新たに user1_example_ というプレフィックスがついたコンテナとネットワークが作成されているはずです。

これがユーザ専用のトレーニング用のコンテナとネットワークです。ユーザが増えると、コンテナとネットワークも増えていきます。

# docker ps -a
CONTAINER ID        IMAGE                     COMMAND                  CREATED              STATUS                      PORTS                    NAMES
17c4ef2ccbb9        naumachia/example.alice   "python /app/alice.py"   About a minute ago   Up About a minute                                    user1_example_alice_1
ff271a01eba9        naumachia/example.bob     "python /app/bob.py"     About a minute ago   Up About a minute                                    user1_example_bob_1
dd9e858277bd        naumachia/manager         "python -m app"          32 minutes ago       Up 32 minutes                                        build_manager_1
f80057d9dc2e        naumachia/openvpn         "/scripts/naumachia-…"   32 minutes ago       Up 32 minutes               0.0.0.0:2000->1194/udp   build_openvpn-example_1
86fc3709d4e3        redis:alpine              "docker-entrypoint.s…"   32 minutes ago       Up 32 minutes                                        build_redis_1
a0f45e1f292a        naumachia/registrar       "gunicorn -c python:…"   32 minutes ago       Up 32 minutes               0.0.0.0:3960->3960/tcp   build_registrar_1
9d1ef7902351        alpine                    "/bin/true"              32 minutes ago       Exited (0) 32 minutes ago                            build_bootstrapper_1
# docker network ls
NETWORK ID          NAME                     DRIVER              SCOPE
743f747a01b3        bridge                   bridge              local
7017ddd37ba8        build_default            bridge              local
dce5de7a2fa2        build_internal           bridge              local
de7c1746cc32        host                     host                local
6dc0c89a9ccf        none                     null                local
b1649b2f2e93        user1_example_default    l2bridge            local

ARPスプーフィングを試してみる

この問題は example の docker-compose.yml に書かれているとおり、ARPスプーフィングのようなMITM(中間者攻撃)を行う問題です。

The file defines the configuration for simple Nauachia challenge where a sucessful man-in-the-middle (MTIM) attack (such as ARP poisoning) provides a solution

今回は 172.30.0.2172.30.0.3 のIPアドレスを持つ2台の端末がいるので、この2台が行っている通信をARPスプーフィングして盗聴することを試みます。

ARPスプーフィングの仕組みや具体的な手法についてはここでは詳しくは説明しませんが、成功すると以下のようにパケットキャプチャすることで、 172.30.0.2172.30.0.3 の2つのホスト間の通信が見えてしまいます。

# tcpdump -i tap0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap0, link-type EN10MB (Ethernet), capture size 262144 bytes
06:40:47.791591 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:48.042999 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:48.696193 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:49.792320 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:50.044301 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:51.700769 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:51.793616 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:52.044971 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:53.794367 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:54.045958 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:54.705584 IP 172.30.0.3.55672 > 172.30.0.2.5005: UDP, length 30
06:40:55.795642 ARP, Reply 172.30.0.2 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28
06:40:56.047136 ARP, Reply 172.30.0.3 is-at 3e:d6:f2:ca:92:81 (oui Unknown), length 28

L2で接続されている同じネットワークにいる場合、このようなリスクがあることを認識しなければいけません。

おわりに

この記事では、NaumachiaというOSSを使ったペネトレーションテストのトレーニング環境の構築について紹介しました。

ペネトレーションテストは実際に攻撃を行うことなので、それをトレーニングすることは攻撃者を養成したいのか?と思われる方がいるかもしれませんが、そうではありません。

近年のサイバー攻撃は高度になってきており、防御側の視点だけでは守りきれないことが増えてきています。
このような攻撃から守るためには、実際の攻撃手法を知り、それに合わせた効果的な防御手法を考えることが重要です。
トレーニングで手を動かして実際の攻撃手法を試すことで攻撃手法への理解も進み、より効率のよい防御ができる人材を育成できるのでないかと考えています。

明日は @nyakuo さんの担当となります。

それでは良いお年を!


  1. ペネトレーションテストについて by 脆弱性診断士スキルマッププロジェクト ( https://github.com/ueno1000/about_PenetrationTest

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

Windows10HomeにDockerを導入する

動機

会社でDockerを使い始めてその便利さを実感したところ、自宅のPCでもDockerを使いたくなった。
で、Docker Desktop for WindowsをインストールしようとしたらWindowsのHyper-Vという機能を利用しているらしく、Windows 10 Homeではこの機能が使えない。
Docker-Toolboxなら使えるとのことなのだが、その際に今回インストール方法をちょっと調べたのでせっかくなので記事にする。
誰かの調べる手間が少しでも省けたらなーと思う。

対象

  • Windows 10 Home 64bit

Windows 10 Pro, Enterprise, Educationの人はDocker Desktop for Windowsを利用できると思うのでそちらの利用をおすすめする。
またDocker-Toolboxは32bit版では使えないそうなのでこの記事では対象外とする。

ステップ

  1. CPUの仮想化を有効にする
  2. Docker-Toolboxをインストール
  3. Dockerの動作確認

1. CPUの仮想化を有効にする

1-1. 仮想化が有効になっているか確認

タスクマネージャを起動してCPUの情報を確認する。
仮想化の欄が有効になっていればOKだが、無効の場合はBIOSの設定を変更して有効にする必要がある。
00024.png

1-2. BIOSからCPUを仮想化する

BIOSのメニューは人によって設定場所が違うと思うので各自で探してほしい。
参考にうちの場合は、詳細→CPU設定→Intel(VMX) Virtualization Technologyを有効にした。
IMAG0407.png
IMAG0409.png

再起動後タスクマネージャーで確認すると、以下のように仮想化の項目が有効になっている。
00025.png

2. Docker-Toolboxをインストール

2-1. Docker-Toolboxをダウンロード

下記リンクからインストーラーをダウンロードする
https://github.com/docker/toolbox/releases
00026.png

2-2. Docker-Toolboxをインストール

インストーラーを起動して、基本的にデフォルト設定ですべてNextで良い。
変更したい項目があれば適宜修正すること。
00027.png
00028.png
00029.png
00030.png
00032.png
下の画面が出ればインストール完了
00034.png

3.Dockerの動作確認

3-1. Docker Quickstart Terminalを起動

インストールすると以下のショートカットが生成されていると思う。
その中のDocker Quickstart Terminalを起動する。
00040.png
初回起動はいろいろ準備が入る。クジラのアスキーアートが表示されたら準備完了。
00037.png

3-2. お試しでdocker run hello-worldを実行する

お試しで実行するコマンドはdocker run hello-world
docker run hello-world が具体的にどんなことをしているかまとめてくれている方がいたので、気になる方はこちらの記事をみて。

最後に

それでは良いDockerライフを!

参考資料

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

Text To Speech (Open Jtalk) を、Kubernetes Cronjob からホストのオーディオインターフェイスを使って喋らせる

TORICOでは、社内にツール用サーバが何台かあり、社内ツールはすべて Kubernetes + Docker で起動しています。

オフィスでは、決まった時間に Text To Speech (Open Jtalk)で、「換気しましょう」といったアナウンスをするようにしています。

以前は Ubuntu にそのまま Open JTalk をインストールし、生cron で実行していたのですが、最近そのサーバマシンを停止してすべて Kubernetes運用になりましたので、Text To Speech していたタスクを生 cron から Kubernetes Cronjob に移行しました。

Dockerイメージを作る

Dockerfile

まず、Open-JTalk がインストールされた Docker イメージを作ります。

apt install open-jtalk でもインストールできますが、バージョンが古いのでソースからビルドしたほうが使えるオプションが増えていて良いです。音量設定など。

mei ボイスを 1.7, 1.8 の2バージョン入れているのは、切り替えて使えるようにするためです。1.8では1.7からけっこう変化があり、、声のトーンが落ち着いてます。1.7の方がよく通る声なのでそっちを多く使っています。

マルチステージビルドしない場合、1GBほどのイメージサイズになります。この Dockerfile のようにマルチステージビルドした場合、130MBほどで済みました。Alpineは試していません。

Dockerfile
FROM ubuntu:18.04 as builder

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    unzip \
    curl \
    build-essential \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

RUN curl -L -O https://downloads.sourceforge.net/hts-engine/hts_engine_API-1.10.tar.gz
RUN tar zxvf hts_engine_API-1.10.tar.gz
RUN cd hts_engine_API-1.10 && ./configure && make  && cd -

RUN curl -L -O https://downloads.sourceforge.net/open-jtalk/open_jtalk-1.11.tar.gz
RUN tar zxvf open_jtalk-1.11.tar.gz
RUN cd open_jtalk-1.11 && ./configure --with-charset=UTF-8 \
    --with-hts-engine-header-path=/var/speech/hts_engine_API-1.10/include \
    --with-hts-engine-library-path=/var/speech/hts_engine_API-1.10/lib \
    && make && make install

RUN mkdir /usr/share/hts-voice
RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.7.zip
RUN unzip MMDAgent_Example-1.7.zip

RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.8.zip
RUN unzip MMDAgent_Example-1.8.zip

FROM ubuntu:18.04

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    open-jtalk-mecab-naist-jdic \
    alsa-utils \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*


COPY --from=builder /usr/local/bin/open_jtalk /usr/local/bin/open_jtalk
COPY --from=builder /var/speech/MMDAgent_Example-1.7/Voice/mei /usr/share/hts-voice/mei17
COPY --from=builder /var/speech/MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/mei18

COPY wav ./wav
COPY sh ./sh

TTS再生スクリプト

sh ディレクトリの中身はこのようになっています。

sh/chime-speech.sh
#!/usr/bin/env bash

if [ ! "$1" ]; then
  echo "Usage: $0 <speech-text>" >2
  exit 1
fi

cd "$(dirname $0)" || exit

./ring-chime.sh
./jsay-female.sh "$1"
sh/jsay-female.sh
#!/bin/sh
TMP=/tmp/jsay.wav
echo "$1" | open_jtalk \
    -m /usr/share/hts-voice/mei/mei_normal.htsvoice \
    -x /var/lib/mecab/dic/open-jtalk/naist-jdic \
    -r 0.7 \
    -g 6 \
    -ow $TMP && \
./play-wav.sh $TMP
sh/play-wav.sh
#!/usr/bin/env bash
aplay -D plughw:1 ${1}
ring-chime.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
./play-wav.sh ../wav/chime.wav

./chime-speech.sh こんにちは というコマンドで、チャイム効果音を鳴らした後、「こんにちは」とTTSで発声するようになっています。

なお、sh/play-wav.shplughw:1 の「1」はサウンドカード番号であり、Dockerホストの環境に依存すると思います。詳細は後ほど書きます。

ビルドスクリプト

docker-compose でも良いのですが、私は下記のような bash スクリプトで Docker イメージのビルドとサーバへのデプロイをしています。

config.sh
#!/usr/bin/env bash
image_name=torico/torico-speech
container_name=torico-speech
deploy_host=user@deploy-server.example.com
build.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker build . -t ${image_name}
docker-image-copy-to-remote.sh
#!/usr/bin/env bash
. config.sh
docker save ${image_name} | bzip2 | ssh ${deploy_host} 'bunzip2 | sudo docker load'

↑手元でビルドした Docker イメージをリモートサーバにコピーします

login.sh
#!/usr/bin/env bash
./run.sh /bin/bash
run.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker run \
    --rm -it \
    --device /dev/snd \
    --name=${container_name} ${image_name} \
    "$@"

スクリプトファイルをたくさん作るのは、エディタ (IntelliJなど) で右クリックからの実行を簡単にするためです。

Screenshot 2019-12-12 17.57.33.png

build.sh でビルドしたら、docker-image-copy-to-remote.sh で SSH でサーバに送りつけます。今回は Docker リポジトリは使っていません。

./run.sh は動作確認のために作っています。Ubuntu用です。Mac では /dev/snd が無いので動きません。

サウンドインターフェイスの確認

--device /dev/snd をつけて docker コンテナを起動し、サウンドカードの様子を見ます。 --privileged はなくても動きました

sudo docker run --rm -it --device /dev/snd --name=torico-speech torico/torico-speech /bin/bash
# alsamixer

Screenshot 2019-12-12 17.37.00.png

root@f3a4dc37f277:/var/speech# amixer
Simple mixer control 'IEC958',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',1
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',2
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',3
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',4
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]

root@f3a4dc37f277:/var/speech# amixer -c 1
Simple mixer control 'Master',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback 0 - 87
  Mono: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Headphone',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB] [on]
  Front Right: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Speaker',0
  Capabilities: pswitch
  Playback channels: Front Left - Front Right
  Mono:
  Front Left: Playback [on]
  Front Right: Playback [on]
Simple mixer control 'Speaker+LO',0
  Capabilities: pvolume
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB]
  Front Right: Playback 87 [100%] [0.00dB]
Simple mixer control 'Front Mic',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 31
  Mono:
  Front Left: Playback 0 [0%] [-34.50dB] [off]
  Front Right: Playback 0 [0%] [-34.50dB] [off]
...
root@f3a4dc37f277:/var/speech# aplay wav/chime.wav
ALSA lib pcm_dmix.c:1052:(snd_pcm_dmix_open) unable to open slave
aplay: main:788: audio open error: No such file or directory

root@f3a4dc37f277:/var/speech# aplay -D plughw:1 wav/chime.wav
Playing WAVE 'wav/chime.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
(再生される)
root@f3a4dc37f277:/var/speech#

サウンドカードは2つ認識されていて、使いたいカード番号は 1 でした。
なので、aplay の引数に -D plughw:1 をつけます。

Kubernetes cronjob

ローカルにある Docker イメージを使って Cronjob を起動します。

ローカルの Docker イメージを使うので、imagePullPolicy は Never です。

cronjob.yml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: speech-12-open
  namespace: torico
  labels:
    cronjob: speech-12-open
spec:
  concurrencyPolicy: Replace
  schedule: "0 12 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: torico-speech
              image: torico/torico-speech
              imagePullPolicy: Never
              args:
                - "/var/speech/sh/chime-speech.sh"
                - "12時になりました。…窓を開けて、5分間換気をしてください。"
              volumeMounts:
              - mountPath: /dev/snd
                name: dev-snd
              securityContext:
                privileged: true
          volumes:
            - name: dev-snd
              hostPath:
                path: /dev/snd

docker run の --device オプションの代わりに、volumeMounts を使います。privileged が必要です。

ちなみに私は、上記ファイルと同じディレクトリに apply.sh とか作って、エディタから右クリックでkubectl を実行するようにしていまします。

apply.sh
#!/usr/bin/env bash

export KUBECONFIG=${HOME}/.kube/remote-server-kubeconfig

kubectl apply -f cronjob.yml

Screenshot 2019-12-12 19.30.23.png

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

Text To Speech (Open Jtalk) を、Kubernetes Crontab からホストのオーディオインターフェイスを使って喋らせる

TORICOでは、社内にツール用サーバが何台かあり、社内ツールはすべて Kubernetes + Docker で起動しています。

オフィスでは、決まった時間に Text To Speech (Open Jtalk)で、「換気しましょう」といったアナウンスをするようにしています。

以前は Ubuntu にそのまま Open JTalk をインストールし、生cron で実行していたのですが、最近そのサーバマシンを停止してすべて Kubernetes運用になりましたので、Text To Speech していたタスクを生 cron から Kubernetes Cronjob に移行しました。

Dockerイメージを作る

Dockerfile

まず、Open-JTalk がインストールされた Docker イメージを作ります。

apt install open-jtalk でもインストールできますが、バージョンが古いのでソースからビルドしたほうが使えるオプションが増えていて良いです。音量設定など。

mei ボイスを 1.7, 1.8 の2バージョン入れているのは、切り替えて使えるようにするためです。1.8では1.7からけっこう変化があり、、声のトーンが落ち着いてます。1.7の方がよく通る声なのでそっちを多く使っています。

マルチステージビルドしない場合、1GBほどのイメージサイズになります。この Dockerfile のようにマルチステージビルドした場合、130MBほどで済みました。Alpineは試していません。

Dockerfile
FROM ubuntu:18.04 as builder

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    unzip \
    curl \
    build-essential \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

RUN curl -L -O https://downloads.sourceforge.net/hts-engine/hts_engine_API-1.10.tar.gz
RUN tar zxvf hts_engine_API-1.10.tar.gz
RUN cd hts_engine_API-1.10 && ./configure && make  && cd -

RUN curl -L -O https://downloads.sourceforge.net/open-jtalk/open_jtalk-1.11.tar.gz
RUN tar zxvf open_jtalk-1.11.tar.gz
RUN cd open_jtalk-1.11 && ./configure --with-charset=UTF-8 \
    --with-hts-engine-header-path=/var/speech/hts_engine_API-1.10/include \
    --with-hts-engine-library-path=/var/speech/hts_engine_API-1.10/lib \
    && make && make install

RUN mkdir /usr/share/hts-voice
RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.7.zip
RUN unzip MMDAgent_Example-1.7.zip

RUN curl -L -O https://downloads.sourceforge.net/mmdagent/MMDAgent_Example-1.8.zip
RUN unzip MMDAgent_Example-1.8.zip

FROM ubuntu:18.04

WORKDIR /var/speech

RUN apt update && \
    apt install -y \
    open-jtalk-mecab-naist-jdic \
    alsa-utils \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*


COPY --from=builder /usr/local/bin/open_jtalk /usr/local/bin/open_jtalk
COPY --from=builder /var/speech/MMDAgent_Example-1.7/Voice/mei /usr/share/hts-voice/mei17
COPY --from=builder /var/speech/MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/mei18

COPY wav ./wav
COPY sh ./sh

TTS再生スクリプト

sh ディレクトリの中身はこのようになっています。

sh/chime-speech.sh
#!/usr/bin/env bash

if [ ! "$1" ]; then
  echo "Usage: $0 <speech-text>" >2
  exit 1
fi

cd "$(dirname $0)" || exit

./ring-chime.sh
./jsay-female.sh "$1"
sh/jsay-female.sh
#!/bin/sh
TMP=/tmp/jsay.wav
echo "$1" | open_jtalk \
    -m /usr/share/hts-voice/mei/mei_normal.htsvoice \
    -x /var/lib/mecab/dic/open-jtalk/naist-jdic \
    -r 0.7 \
    -g 6 \
    -ow $TMP && \
./play-wav.sh $TMP
sh/play-wav.sh
#!/usr/bin/env bash
aplay -D plughw:1 ${1}
ring-chime.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
./play-wav.sh ../wav/chime.wav

./chime-speech.sh こんにちは というコマンドで、チャイム効果音を鳴らした後、「こんにちは」とTTSで発声するようになっています。

なお、sh/play-wav.shplughw:1 の「1」はサウンドカード番号であり、Dockerホストの環境に依存すると思います。詳細は後ほど書きます。

ビルドスクリプト

docker-compose でも良いのですが、私は下記のような bash スクリプトで Docker イメージのビルドとサーバへのデプロイをしています。

config.sh
#!/usr/bin/env bash
image_name=torico/torico-speech
container_name=torico-speech
deploy_host=user@deploy-server.example.com
build.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker build . -t ${image_name}
docker-image-copy-to-remote.sh
#!/usr/bin/env bash
. config.sh
docker save ${image_name} | bzip2 | ssh ${deploy_host} 'bunzip2 | sudo docker load'

↑手元でビルドした Docker イメージをリモートサーバにコピーします

login.sh
#!/usr/bin/env bash
./run.sh /bin/bash
run.sh
#!/usr/bin/env bash
cd "$(dirname $0)" || exit
. config.sh
docker run \
    --rm -it \
    --device /dev/snd \
    --name=${container_name} ${image_name} \
    "$@"

スクリプトファイルをたくさん作るのは、エディタ (IntelliJなど) で右クリックからの実行を簡単にするためです。

Screenshot 2019-12-12 17.57.33.png

build.sh でビルドしたら、docker-image-copy-to-remote.sh で SSH でサーバに送りつけます。今回は Docker リポジトリは使っていません。

./run.sh は動作確認のために作っています。Ubuntu用です。Mac では /dev/snd が無いので動きません。

サウンドインターフェイスの確認

--device /dev/snd をつけて docker コンテナを起動し、サウンドカードの様子を見ます。 --privileged はなくても動きました

sudo docker run --rm -it --device /dev/snd --name=torico-speech torico/torico-speech /bin/bash
# alsamixer

Screenshot 2019-12-12 17.37.00.png

root@f3a4dc37f277:/var/speech# amixer
Simple mixer control 'IEC958',0
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',1
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',2
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',3
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]
Simple mixer control 'IEC958',4
  Capabilities: pswitch pswitch-joined
  Playback channels: Mono
  Mono: Playback [on]

root@f3a4dc37f277:/var/speech# amixer -c 1
Simple mixer control 'Master',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback 0 - 87
  Mono: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Headphone',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB] [on]
  Front Right: Playback 87 [100%] [0.00dB] [on]
Simple mixer control 'Speaker',0
  Capabilities: pswitch
  Playback channels: Front Left - Front Right
  Mono:
  Front Left: Playback [on]
  Front Right: Playback [on]
Simple mixer control 'Speaker+LO',0
  Capabilities: pvolume
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 87
  Mono:
  Front Left: Playback 87 [100%] [0.00dB]
  Front Right: Playback 87 [100%] [0.00dB]
Simple mixer control 'Front Mic',0
  Capabilities: pvolume pswitch
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 31
  Mono:
  Front Left: Playback 0 [0%] [-34.50dB] [off]
  Front Right: Playback 0 [0%] [-34.50dB] [off]
...
root@f3a4dc37f277:/var/speech# aplay wav/chime.wav
ALSA lib pcm_dmix.c:1052:(snd_pcm_dmix_open) unable to open slave
aplay: main:788: audio open error: No such file or directory

root@f3a4dc37f277:/var/speech# aplay -D plughw:1 wav/chime.wav
Playing WAVE 'wav/chime.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
(再生される)
root@f3a4dc37f277:/var/speech#

サウンドカードは2つ認識されていて、使いたいカード番号は 1 でした。
なので、aplay の引数に -D plughw:1 をつけます。

Kubernetes cronjob

ローカルにある Docker イメージを使って Cronjob を起動します。

ローカルの Docker イメージを使うので、imagePullPolicy は Never です。

cronjob.yml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: speech-12-open
  namespace: torico
  labels:
    cronjob: speech-12-open
spec:
  concurrencyPolicy: Replace
  schedule: "0 12 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: torico-speech
              image: torico/torico-speech
              imagePullPolicy: Never
              args:
                - "/var/speech/sh/chime-speech.sh"
                - "12時になりました。…窓を開けて、5分間換気をしてください。"
              volumeMounts:
              - mountPath: /dev/snd
                name: dev-snd
              securityContext:
                privileged: true
          volumes:
            - name: dev-snd
              hostPath:
                path: /dev/snd

docker run の --device オプションの代わりに、volumeMounts を使います。privileged が必要です。

ちなみに私は、上記ファイルと同じディレクトリに apply.sh とか作って、エディタから右クリックでkubectl を実行するようにしていまします。

apply.sh
#!/usr/bin/env bash

export KUBECONFIG=${HOME}/.kube/remote-server-kubeconfig

kubectl apply -f cronjob.yml

Screenshot 2019-12-12 19.30.23.png

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

docker imageファイルの保存先

https://stackoverflow.com/questions/24309526/how-to-change-the-docker-image-installation-directory

以下やろうと思ったけど symlinkだといろいろエラーになるのでやめた

do the trick:

/etc/docker/daemon.json

{
"graph": "/mnt",
"storage-driver": "overlay2"
}
Despite the method you have to reload configuration and restart Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker
To confirm that Docker was reconfigured:

docker info|grep "loop file"
In recent version (17.03) different command is required:

docker info|grep "Docker Root Dir"
Docker Root Dir: /mnt/docker

ストレージドライバ

overlay2じゃないと buildでエラーになる?
https://qiita.com/NaotoHanaue/items/b9f395a63f7aae2a41b7

sudo docker info
(snip)
Storage Driver: overlay2

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

docker coreos

esxi
https://serverfault.com/questions/372443/curl-on-esxi-5-0
wget http://stable.release.core-os.net/amd64-usr/current/coreos_production_vmware_ova.ova

wget http://stable.release.core-os.net/amd64-usr/current/coreos_production_qemu.sh

esxi

https://qiita.com/ka2asd/items/456b55b9958c0f2f206b

https://qiita.com/fabiiw05/items/b6dde0e1dbd620c3e7fa

パスワードなし

https://coreos.com/os/docs/latest/booting-with-iso.html

wget http://stable.release.core-os.net/amd64-usr/current/coreos_production_iso_image.iso

vi ~/cloud-config.yml

改行とハイフンとスペースが適切でないとだめっぽい

users:
- name: coreuser
passwd: $xxxxxxxxx/ # パスワード認証。openssl passwdコマンドを叩いて実行した結果を貼り付け
groups: #所属するgroupの指定
- sudo
- docker
ssh-authorized-keys:
- ssh-rsa #coreuserに持たせる公開鍵

user: core
pass: password秘密

インストールが完了したらネットワーク設定をおこないましょう。
ネットワークの設定は/etc/systemd/network/static.networkファイルを作成します。

$ sudo vi /etc/systemd/network/static.network
記述内容(内容は自分の環境に合わせて変更してください)

[Match]
Name=enp0s3

[Network]
Address=192.168.0.105
Gateway=192.168.0.1
DNS=192.168.0.1

https://www.hitsumabushi.org/blog/2017/02/08/2231.html

自宅 vSphere 上の CoreOS (Container Linux) のデプロイメモ

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

dockerまとめ

docker pull ubuntu

docker run -it --name=ping_in_ubuntu ubuntu /bin/bash

docker container prune

イメージ削除

docker rmi ping_in_ubuntu:ver.1.00

docker commit 644f7f59030e ping_in_ubuntu

saveしたimage 取り込み

docker load < ping_save.tar

デタッチモードで起動

docker run -p 8080:80 -d httpd

停止

docker stop 5c67acf81f90

再開

docker start 5c67acf81f90

マウント

docker run -v /Users/N/_work/00_docker/webContents/:/usr/local/apache2/htdocs/ -p 80:80 -d httpd

データvolume作成

docker volume create --name=datavolume

登録

docker run -it -v datavolume:/tmp/ ubuntu /bin/bash

ボリューム削除

exit

ボリューム一覧

docker volume ls
DRIVER VOLUME NAME
local datavolume

削除

docker volume rm datavolume

docker run --volumes-from data-container -it ubuntu /bin/bash

データボリューム・コンテナを使う場合は、利用する側のコンテナに
「volume-from」を使う
というだけなので、他のコンテナと同じように思えますが、一つ大きな特徴
があります。
それは、データボリューム・コンテナは、‘‘稼働していなくても利用でき
る” という点です。
先ほどの、手順[2]では

ボリュームのバックアップ

docker run --volumes-from data-container -v /Users/N/_work/00_docker/:/backup ubuntu tar cvf /backup/container-bkup.tar -C / tmp

リストア

docker run --volumes-from data-container -v /Users/N/_work/00_docker/:/backup ubuntu tar xvf /backup/container-bkup.tar -C /

データボリュームの所在

docker inspect data-container

nginx

docker run -d -p 80:80 --name webserver nginx

dockerFile

docker build -t ping-image .

ログの見方

docker logs 2e2ef2184c07

============

django テスト
https://qiita.com/homines22/items/2730d26e932554b6fb58

停止

docker stop test

再開

docker start 1429509e1e5b

runserver

docker exec test python3 manage.py runserver 0.0.0.0:8000

django & docker compose
https://qiita.com/kyhei_0727/items/e0eb4cfa46d71258f1be

docker-compose rm
docker rm $(docker ps -aq)

docker-compose build

tensorflow

Dockerイメージをプル(Python3の最新安定板を使用する場合)
その他のイメージを使用する場合はこちら:https://www.tensorflow.org/install/docker?hl=ja

docker pull tensorflow/tensorflow:latest-gpu-py3

Dockerコンテナを作成&実行

docker run --runtime=nvidia -it --name TestContainerName -p 8888:8888 -v /Users/N/_work/00_docker/tensorflow:/tmp tensorflow/tensorflow:latest-gpu-py3 bash

docker run -it --name TestContainerName -p 8888:8888 -v /Users/N/_work/00_docker/tensorflow:/tmp tensorflow/tensorflow:latest bash

Jupyter Notebookを起動

jupyter notebook --allow-root

表示された下記の文字列をコピーして…

http://(*** or 127.0.0.1):8888/?token=***
ブラウザのアドレスバーに下記のように編集してペースト

http://127.0.0.1:8888/?token=***

https://qiita.com/y_kani/items/27a965b952db729e30f5

  1. デフォルトでvimが入っていないため、Jupyter Notebook起動前に設定ファイルを編集したい場合は以下を実行してvimをインストールすること。 # apt-get update # apt-get install vim

https://qiita.com/yoshiyasu1111/items/cc3b9ca71f98bd66d9b8

docker pull jupyter/tensorflow-notebook

docker run -d --name jupyter -p 8088:8888 -v /Users/N/_work/00_docker/jupyter:/home/work jupyter/tensorflow-notebook

docker exec 43aa18101a01 jupyter notebook list

画像認識

https://qiita.com/hikarut/items/c7a0e45a962b16c7907a

http://localhost:8088/tree/work/classify

python classify_image.py --image_file c1.jpg

scraping

https://oliversi.com/2019/01/07/python-docker-selenium-chrome/

https://qiita.com/sikkim/items/447b72e6ec45849058cd

git clone https://github.com/sikkimtemi/selenium
cd selenium
docker-compose up -d

cd script
docker exec -it python ./sample.py

docker compose 終了

docker-compose down

コンテナ停止

docker stop rm-container wp-container pma-container mysql-container
docker start mysql-container rm-container wp-container pma-container

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

docker勉強 その①

dockerの基本的なコマンドを覚える

dockerでubuntu18.04を構築して基本的なコマンドを覚える

dockerイメージを取得

ubuntu18.04のイメージを取得する。

$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
7ddbc47eeb70: Pull complete
c1bbdc448b72: Pull complete
8c3b70e39044: Pull complete
45d437916d57: Pull complete
Digest: sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04

dockerイメージの確認

先ほど取得したubuntu18.04のイメージがあるか確認する。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              18.04               775349758637        6 weeks ago         64.2MB

dockerイメージからコンテナを起動する

$ docker run -it -d --name ubuntu18-04 ubuntu:18.04
8389c53ba63b5c1a45ca256a6ec91cce6f1c3b889f12f1c4a6f64289b612ffea

option

  • -it:コンテナのプロセスにttyを割り当てる。(標準入出力の割り当て)
  • -d:コンテナをバックグラウンドで起動する
  • --name:コンテナに指定した名前をつける

コンテナの起動確認

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
8389c53ba63b        ubuntu:18.04        "/bin/bash"         8 seconds ago       Up 6 seconds                            ubuntu18-04

dockerのubuntu18.04にアクセス

$ docker exec -it ubuntu18-04 /bin/bash
root@8389c53ba63b:/#

これでdockerのubuntu18.04で色々と操作ができる。exitで抜けれる。

コンテナの停止

ubuntu18.04を停止する。

$ $ docker stop ubuntu18-04
ubuntu18-04

停止しているか確認する。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
8389c53ba63b        ubuntu:18.04        "/bin/bash"         2 minutes ago       Exited (0) 40 seconds ago                       ubuntu18-04

停止したコンテナを再度起動する。

$ docker start ubuntu18-04
ubuntu18-04

コンテナの削除

ubuntu18.04を削除する。

$ docker rm ubuntu18-04
ubuntu18-04

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

dockerイメージの削除

ubuntu18.04のイメージを削除する。

$ docker rmi ubuntu:18.04
Untagged: ubuntu:18.04
Untagged: ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d
Deleted: sha256:775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c
Deleted: sha256:4fc26b0b0c6903db3b4fe96856034a1bd9411ed963a96c1bc8f03f18ee92ac2a
Deleted: sha256:b53837dafdd21f67e607ae642ce49d326b0c30b39734b6710c682a50a9f932bf
Deleted: sha256:565879c6effe6a013e0b2e492f182b40049f1c083fc582ef61e49a98dca23f7e
Deleted: sha256:cc967c529ced563b7746b663d98248bc571afdb3c012019d7f54d6c092793b8b

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

これで基本的なdockerのコマンドは覚えれたと思います。
次はdocker-composeやdockerでwebサーバーを構築し、ポートフォワーディングの設定などやろうと思います。

はてなブログにも投稿しています。

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

Docker上でDjango開発開始準備

目的

Dangoの開発を開始するにあたり、Docker上での開発環境を作成します。
公式のDjango Tutorialの1つ目の完了までを目標とします。

開発環境構築

DjangoのDocker環境を構築します。

Docker file作成

現時点のPythonの最新は3.8ですので、それをベースに作成します。

FROM python:3.8
# Python 標準入出力バッファーを無効(反応速度優先のため。性能は下がる。)
ENV PYTHONUNBUFFERED 1
#
# オススメのパッケージは入れない.最小限とする. --no-install-recommends
# DBを変更する場合は postgresql-client等を追加.
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .

EXPOSE 8000
# 開発完了したら、以下を有効にする.
# CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

Python追加パッケージの設定

requirements.txtにPythonのパッケージを記述します。

requirements.txt
django

イメージ作成

イメージ名「django」でイメージをビルドします。

docker build -t django .

bash 起動

開発を行うため、bash起動します。

$ docker run -p 127.0.0.1:8000:8000 -it django bash

Django開発

以下のURLを参考にしてアプリを作成します。
Django ドキュメント

djangoアプリ作成

サンプル作成

確認のためにサンプルを作成します。

$ django-admin startproject sampleapp

サンプル起動

動作確認のため、一旦起動してみます。

$ python manage.py runserver 0:8000

HOST側からアクセスするため、「runserver 8000」ではなく「runserver 0:8000」で起動します。
確認したら、Ctrl+Cで終了します。

チュートリアルで確認

チュートリアルでサンプルアプリを作成します。詳細は以下のURLを参照します。
はじめての Django アプリ作成、その 1

以下のURLにアクセスし、表示されることを確認します。

http://127.0.0.1:8000/polls/

dokcer環境でdjango開発の準備はできた。

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

Docker Compose で AWX + Gitea 環境を作る

はじめに

AWX は Docker Compose によるインストール方法をサポートしています。ただ、docker-compose.yml を用意して docker-compose コマンドを実行するのではなく、ansible-playbook コマンドを使用する方法になっています。何故 docker-compose コマンドを使用しないのでしょうか?

また、AWX だけ準備できてもソースコードリポジトリのコンテナが無いと、Github 等の既存のサービスが必要です。環境次第では何らかのソースコードリポジトリをデプロイする必要があるでしょう。個人的には Github クローンの1つ、Giteaがお勧めです。Go 言語で開発されており、GitLab より高速に動作します。

無いなら作ってしまいましょう、という事で、docker-compose.yml を用意するシェルスクリプトを作成しました。もっと良い方法があるのかも知れません。あればコメント欄で教えてください。

準備

  1. docker-compose パッケージをインストールします。
  2. docker サービスを起動します。
  3. こちら からシェルスクリプトをダウンロードします。
  4. シェルスクリプトの冒頭にある変数を適切に変更します。
  5. シェルスクリプトを実行して docker-compose.yml を作成します。
   $ bash gen-dc-awx-gitea.sh
  1. docker-compose コマンドを実行します(1回目)
   $ sudo docker-compose up

各コンテナの実行ログがコンソール上に表示されます。そのうち、AWX 用の DB マイグレーションが実行されるので、マイグレーションが終わるまで待ちます。
7. CTRL-C で一旦実行を止めます。
8. docker-compose コマンドを実行します(2回目)

   $ sudo docker-compose up -d

後はサービスにアクセスするだけです。

使用

デフォルトでは、以下のポートが使用されます。

  • 443/TCP:AWX WebUI (HTTPS)
  • 8443/TCP: Gitea WebUI (HTTPS)
  • 10022/TCP: Gitea Git (SSH)

Gitea 画面の右上の sign-in をクリックすると、初回はサーバのセットアップ画面になります。デフォルト値は事前に docker-compose.yml で設定済みなので、ほとんど設定値を変更する必要はありませんが、管理者アカウントを追加するのであればこの画面で設定します。

利用上の注意

  • AWX から見て Gitea のホスト名は gitea になります。ですので、AWX のプロジェクト登録時に Gitea 上の Git リポジトリを入力する際、URL のホスト名部分は Gitea の FQDN ではなく gitea にする必要があります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

令和元年に送る!Docker for WindowsでのJenkins(Docker in Docker)環境構築ハンズオン

はじめに

元号が変わり、2020年を迎えるにあたって「CI環境も存在しない開発体制をこの先も続けていくつもりなのか?」と悶々とする日々から脱却すべく「JenkinsのパイプラインでDockerfileを指定してビルド実行」を行うための環境構築に試行錯誤をした成果を紹介します。

目的

  • ゼロからDocker入りJenkinsコンテナの立ち上げまでを、最小手順で実現する
  • 手順の意図を理解する

以上の2点を目指しています。

私自身、DockerもJenkinsも最低限の知識しか持ち合わせておらず、結論に到達するまでに数多くのWebページを駆けずり回ることとなりました。
この記事は、まさしく「一ヶ月前の私がほしかったモノ」です。

動作確認環境

  • Windows10 Pro 1903 - 64bit
  • Docker Desktop for Windows 2.1.0.5 (stable)

1. Docker入りJenkinsコンテナの構築〜立ち上げ

1.1. Dockerfile 作成

Dockerfile.sample
# 1.1.1.
FROM jenkins:2.60.3

# 1.1.2.
USER root

# 1.1.3.
RUN apt-get update && apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common \
    && curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \
    && apt-key fingerprint 0EBFCD88 \
    && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
    && apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# 1.1.4.
RUN usermod -aG docker jenkins

# 1.1.5.
RUN cd /tmp/ \
    && wget https://updates.jenkins-ci.org/latest/jenkins.war \
    && mv ./jenkins.war /usr/share/jenkins/

# 1.1.6.
USER jenkins

1.1.1. ベースイメージ指定

ここでは奇をてらわずに、Jenkinsの公式イメージを指定しています。
タグについては、今後の更新で手順が変更になる可能性を考慮して、投稿時点で最新の 2.60.3 を明示的に指定しています。

1.1.2. ユーザー変更(root <- jenkins

jenkinsイメージ内で、ユーザーが jenkins に変更されています。
jenkins ユーザーはroot権限を持たないため、以降のコマンドを実行できるよう一時的に root ユーザーに変更します。

1.1.3. Dockerインストール

Get Docker Engine - Community for Debian | Docker Documentationを参考に、Dockerエンジンを導入します。
インストール後には、Dockerfileのお作法としてキャッシュを削除しておきます。

余談ですが、 curl で指定するURLは、Docker v17.09以前のドキュメントでは $(. /etc/os-release; echo "$ID") の結果を参照していました。
ホスト環境がRaspberry Piの場合等、URLにしたい文字列と $(. /etc/os-release; echo "$ID") の結果とが一致しないケースがあることから、最新のドキュメントでは見直されているのではないかと想像します。 ※要出典

1.1.4. dockerjenkins ユーザーのセカンダリグループとして追加

Post-installation steps for Linux | Docker Documentationを参考に、jenkins ユーザーが sudo を指定せずに docker コマンドを実行するための準備としてグループ設定を行います。

1.1.5. Jenkinsアップデート

jenkins:2.60.3 の最終更新日は、投稿時点で一年以上前となっていることから、最新のJenkinsを利用するために、アップデートします。

1.1.6. ユーザー変更(root <- jenkins

一時的に変更したユーザーをもとに戻します。

1.2. docker-compose.yml 作成

docker-compose.yml
version: "3"

services:
    jenkins:
        build: .
        ports:
            - 8080:8080
        volumes:
# 1.2.1.
            - ./jenkins_home:/var/jenkins_home
# 1.2.2.
            - /var/run/docker.sock:/var/run/docker.sock:rw

1.2.1. ホスト環境との共有ディレクトリの指定

JenkinsコンテナにホストPC上で作成した Dockerfile を送り込めるように、Jenkinsのホームディレクトリを共有ディレクトリ化します。

1.2.2. Dockerデーモンの共有

Dockerコンテナ内でDockerコンテナを動かせるようにするために、ホストPCのDockerソケットを共有します。

1.3. .env 作成(しません)

Docker for WindowsでDockerデーモンを共有するために .env を作成して COMPOSE_CONVERT_WINDOWS_PATHS=1 を指定する必要がある、という記事を目にしましたが、私の環境では作成せずに進めることができました。

.env
COMPOSE_CONVERT_WINDOWS_PATHS=1

1.4. イメージ構築~コンテナ作成~バックグラウンド実行

カレントディレクトリに先に示した Dockerfiledocker-compose.yml とを配置して、コマンドプロンプトから以下のコマンドを実行します。

$ docker-compose up --build -d && docker-compose exec -u root jenkins chown root:docker /var/run/docker.sock

Docker for Windowsでは(?) docker グループに /var/run/docker.sock へのアクセス権限が割り当てられていないようです。
そのため、コンテナのバックグラウンド実行後にグループ所有権を設定します。

※コマンドプロンプト以外のCUIツールを仕様した場合、パスの解決がうまくいかずに chown root:docker /var/run/docker.sock を実行できないケースがあるようです。私はこれにハマって半日つぶしました。

2. Jenkinsコンテナ上でDocker Pipeline Pluginの登録〜実行

2.1. Jenkinsセットアップ

任意のWebブラウザで localhost:8080 へアクセスし、正規の手順でセットアップします。
※以下、テキストベースで説明していますが、他に詳しく正確で分かりやすい記事が山のようにありますので、そちらも参考にしてください。

2.1.1. Unlock Jenikins

いきなり「Administrator password」を求められます。
ここには docker-compose logs -f jenkins を実行して、以下のとおり出力されるパスワードを入力します。

...
jenkins_1  | 2019-12-15 08:10:47.628+0000 [id=30]       INFO    jenkins.install.SetupWizard#init:
jenkins_1  |
jenkins_1  | *************************************************************
jenkins_1  | *************************************************************
jenkins_1  | *************************************************************
jenkins_1  |
jenkins_1  | Jenkins initial setup is required. An admin user has been created and a password generated.
jenkins_1  | Please use the following password to proceed to installation:
jenkins_1  |
jenkins_1  | <Administrator password>
jenkins_1  |
jenkins_1  | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
jenkins_1  |
jenkins_1  | *************************************************************
jenkins_1  | *************************************************************
jenkins_1  | *************************************************************
jenkins_1  |
jenkins_1  | --> setting agent port for jnlp
jenkins_1  | --> setting agent port for jnlp... done
...

2.1.2. Customize Jenkins

特にこだわりがなければ「Install suggested plugins」を選択して問題ないでしょう。

2.1.3. Gettings Started

オールグリーンになるまで数分の休憩です。 :coffee:

2.1.4. Create First Addmin User

初期ユーザの情報を求められるので、適宜入力欄を埋めます。

2.1.5. Instance Configuration

JenkinsのURLを指定することができます。
特にこだわりがなければデフォルトの http://localhost:8080/ で問題ないでしょう。

2.1.6. Jenkins is ready!

これでJenkinsのセットアップです!

2.2. パイプライン登録

以下、「sample」という名前のジョブを作成し、Dockerfileを指定してコンソールに「hello world.」と表示する例における手順です。

2.2.1. 下準備

  1. 以下内容の Dockerfile を作成
Dockerfile
FROM alpine:latest
  1. ~/jenkins_home/workspace/sample/ ディレクトリを作成し、配下に 1. のファイルを配置

2.2.2. WEB画面操作

  1. ホーム画面「新規ジョブ作成」をクリック
  2. 「Enter an item name」に「sample」と入力して「パイプライン」を選択し、「OK」をクリック
  3. Generalタブ「パイプライン」「Pipeline script」を選択して「Script」に以下の内容を入力し、「保存」をクリック
Script
node {
    docker.build("${BUILD_ID}", "-f Dockerfile .").inside() {
        sh 'echo "Hello World!"'  
    }
}

2.3. Docker Pipeline Plugin実行〜結果確認

2.2. で作成したジョブ画面の「ビルド実行」をクリックします。
今回のサンプルでは、コンソールに Hello World! と出力するだけなので、ビルド履歴画面「Console Output」から、以下のような出力がなされていれば成功です! :tada:

コンソール出力
Started by user admin
Running in Durability level: MAX_SURVIVABILITY
[Running on Jenkins in /var/jenkins_home/workspace/sample
[Pipeline] {
[Pipeline] isUnix
[Pipeline] sh
+ docker build -t 8 -f Dockerfile .
Sending build context to Docker daemon  2.048kB

Step 1/1 : FROM alpine:latest
 ---> 965ea09ff2eb
Successfully built 965ea09ff2eb
Successfully tagged 8:latest
[Pipeline] isUnix
[Pipeline] sh
+ docker inspect -f . 8
.
[Pipeline] withDockerContainer
Jenkins seems to be running inside container 0fb06afff12dcba3803da391819176848ca54934737d486d99a72210b37c9955
$ docker run -t -d -u 1000:1000 -w /var/jenkins_home/workspace/sample --volumes-from 0fb06afff12dcba3803da391819176848ca54934737d486d99a72210b37c9955 -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** 8 cat
$ docker top 873e72d4af7ed99be93d4cdc85b800bc36f1bf8df4992597c18ad4ac67575dd0 -eo pid,comm
[Pipeline] {
[Pipeline] sh
+ echo 'Hello World!'
Hello World!
[Pipeline] }
$ docker stop --time=1 873e72d4af7ed99be93d4cdc85b800bc36f1bf8df4992597c18ad4ac67575dd0
$ docker rm -f 873e72d4af7ed99be93d4cdc85b800bc36f1bf8df4992597c18ad4ac67575dd0
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

おわりに

0 -> 1のステップを超えるためには多大な労力を要します(要しました)。
この記事が、第二、第三のワタシの労力を減らすお役にたつことががあれば幸いです。

参考

Docker関連

Jenkins(Docker Pipeline Plugin)関連

CIツール選定

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

Nginx + uWSGI + Python(Django)の環境をdockerで作成する

この記事はウェブクルー Advent Calendar 2019の16日目の記事です。
昨日は@Hideto-Kiyoshima-wcさんのScalaのOption/Either/Try超入門でした。

はじめに

株式会社ウェブクルーに新卒で入社して、2年目の@yagiyuuuuです。
現在、Nginx + uWSGI + Python(Django)のアプリ環境をDockerで作成して、開発をしています。
これから、Djangoでアプリ開発をする人の助けになればと思い、この記事を書きました。

Docker for Windowsのインストール

コントロールパネルを開いて、
「プログラムと機能」→「Windowsの機能を有効化または無効化」→「Hyper-V」にチェックが入っているか確認します。
チェックが入っていなかった場合は、チェックを入れてPCを再起動させて有効化させます。
次に「Docker Desktop for Windows」のインストールをする。
インストールはここからできます。

Djangoを動かす環境構築

ディレクトリ構成

以下、構成でDjangoアプリを動かします。
image.png

Infrastrcuture作成

Alpineにpython + uWSGI、Nginxをインストールします。

docker-compose.yml作成

Nginxとpython + uWSGIのコンテナを作成します。
今回はログをdjango-sample配下に出力するようにしていますが、お好きなところにログを吐き出すように設定してください。

django-sample/docker-compose.yml
version: '2'
services:
  nginx:
    build: "./Infrastructure/nginx/"
    volumes:
      - ./logs/nginx:/var/log/nginx
    ports:
      - "80:80"
    networks:
      django-sample-network:
        ipv4_address: 172.23.0.4
  python:
    build: "./Infrastructure/python/"
    volumes:
      - ./Application/django-sample:/home/work/django-sample
      - ./logs/django:/home/work/django
      - ./logs/uwsgi:/home/work/uwsgi
    ports:
      - "8000:8000"
    networks:
      django-sample-network:
        ipv4_address: 172.23.0.5
networks:
  django-sample-network:
    driver: bridge
    ipam:
     driver: default
     config:
       - subnet: 172.23.0.0/24

Dockerfile作成

Nginx

django-sample/Infrastructure/nginx/Dockerfile
FROM nginx:1.13.1-alpine
COPY work/nginx.conf /etc/nginx
RUN apk --no-cache add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata
CMD ["nginx", "-g", "daemon off;"]

uWSGI

django-sample/Infrastructure/python/Dockerfile
FROM python:3.7
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN mkdir /home/work
RUN mkdir /home/work/django
RUN mkdir /home/work/uwsgi
COPY work/ /home/work
WORKDIR /home/work
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

CMD ["uwsgi", "--ini", "/home/work/uwsgi.ini"]

Nginxの設定

django-sample/Infrastructure/nginx/work/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error_app.log;
events {
    worker_connections 1024;
}
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access_app.log  main;
    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   120;
    proxy_read_timeout  120;
    proxy_send_timeout  120;
    types_hash_max_size 2048;
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    server {
        listen       80 default_server;
        server_name  _;

        fastcgi_read_timeout 60s;

        client_max_body_size 1m;

        location ~ ^/app/ {
            add_header Cache-Control no-cache;
            include uwsgi_params;
            uwsgi_pass 172.23.0.5:8000;
            uwsgi_read_timeout 60s;
        }
    }
}

uWSGI + Djangoの設定

django-sample/Infrastructure/python/work/uwsgi.ini
[uwsgi]
chdir=/home/work/django-sample
module=django-sample.wsgi
master=True
vacuum=True
max-requests=5000
socket=:8000
py-autoreload=1
logto=/home/work/uwsgi/django-app.log
buffer-size=10240
log-format=%(addr) - %(user) [%(ltime)] "%(method) %(uri) %(proto)" %(status) %(size)`` "%(referer)" "%(uagent)"
django-sample/Infrastructure/python/work/requirements.txt
django==2.2
uwsgi==2.0.17.1

requirements.txtにインストールしたいモジュールを記載します。

.envファイル作成

django-sample/.env
COMPOSE_FILE=docker-compose.yml

Application作成

ここではアプリを作成することに焦点を当てていますので、
Djangoアプリの詳細に関しては、公式サイトなどをみていただきたいです。
また、__init__.py__pycache__にはコードを書きませんが、作成をしてください。
作成されていないとアプリが動かなくなってしまいます。

プロジェクト作成

django-sample/Application/django-sample/manage.py
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django-sample.settings")
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)
django-sample/Application/django-sample/django-sample/settings.py
"""
Django settings for django-sample project.
Generated by 'django-admin startproject' using Django 2.0.3.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""

import os
import json
import traceback

# ログ出力で仕様するハンドラを指定する
LOG_HANDLER = ["app"]

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'ekf!&30u3&idt-qr3250(t+j#%@(vyxr02c-7fj!a81$!)#q=('

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

# 接続を許可するサーバのIPやドメインを設定する
# 何も設定していない場合は、ローカルホスト(localhost)からの接続のみ可能な状態
ALLOWED_HOSTS = ["localhost"]

# Application definition
# 「app」を追加。これを追加しないとtemplatetagsに定義したカスタムタグが認識されない
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
]

ROOT_URLCONF = 'django-sample.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'django-sample.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ja'

#TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = ''

LOGGING = {
    'version': 1,
    'formatters': {
        'app': {
            'format': '%(asctime)s [%(levelname)s] %(pathname)s:%(lineno)d %(message)s'
        }
    },
    'handlers': {
        'app': {
            'level': 'DEBUG',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': '/home/work/django/app.log',
            'formatter': 'app',
            'when': 'D',        # 単位 Dは日
            'interval': 1,      # 何日おきか指定
            'backupCount': 30,  # バックアップ世代数
        }
    },
    'loggers': {
        'django': {
            'handlers': ['app'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'django.server': {
            'handlers': ['app'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'app': {
            'handlers': LOG_HANDLER,
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

# セッションエンジンの設定
# cookieを用いたセッションを使用する
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'

# ログイン状態の有効期限(秒)
# ここに指定した有効期限(秒)を超えるまでログイン状態を保つ事ができる
# セッション自体の有効期限はSESSION_COOKIE_AGE
# 8h * 60m * 60s
LOGIN_LIMIT = 28800

# セッションの有効期間(秒)
# 利用者毎にセッション有効期間を変えたい場合は、request.session.set_expiry(value)を用いる
SESSION_COOKIE_AGE = 1800
django-sample/Application/django-sample/django-sample/urls.py
"""django-sample URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.urls import path, include

urlpatterns = [
    path('app/', include("app.urls")),
]

django-sample/Application/django-sample/django-sample/wsgi.py
"""
WSGI config for django-sample project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django-sample.settings")

application = get_wsgi_application()

アプリ作成

djnago-sampleプロジェクト内にアプリを作成します。

django-sample/Application/django-sample/app/urls.py
from django.urls import path

from app.views.login import view as login_view

urlpatterns = [
    path("", login_view.top, name="login_top")
]
django-sample/Application/django-sample/app/views/login/view.py
from django.http import HttpResponse
from django.http.request import HttpRequest
from django.template import loader


def top(request: HttpRequest):
    template = loader.get_template("login/index.html")
    return HttpResponse(template.render({}, request))

画面表示させるテンプレート作成

django-sample/Application/django-sample/templates/login/index.html
Hello Django!!

コンテナを立ち上げる

docker-compose.ymlがある階層で以下コマンドを叩く

コンテナのビルド、起動

$ docker-compose up --build -d

-dをつけることでバックグラウンドで起動できる

コンテナを確認する

$ docker-compose ps

コンテナを削除する

$ docker-compose down

作成したアプリにアクセスする

コンテナを起動したら、http://localhost/app/にアクセスすると
Hello Django!!が表示される

終わりに

Djangoアプリを作成していく上で、自分好みの環境にしていってください!!

明日の記事は@yuko-tsutsuiさんです。
よろしくお願いします。

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

Dockerで開発用のMySQLを構築する

Dockerでサクッと開発用のMySQLを構築する

勉強用にサクッとMySQLの環境がほしかったのでメモ。

環境

Docker Desktop 2.1.0.5

構築と利用

MYSQL_ALLOW_EMPTY_PASSWORDでパスワード不要にする。

$ docker run --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql:5.7

docker containerに入ってMySQLを試す。

$ docker exec -it mysql mysql

削除

終わったら削除する。

$ docker stop mysql
$ docker rm mysql

参考

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

起動中のDockerコンテナのstdinに入力する

概要

こんにちは、haniokasaiです。
Dockerでdocker attach するとき、インタラクティブに操作できるのはpid 1だけです。1
通常それで困りませんが、私は困りました。
私のコンテナではDockerfileはシェルスクリプトを呼び出し、そのシェルスクリプトがPHP CGIを実行します。その場合は、phpのpidは1ではないので入力するすべがありません。(出力はattachで見れます。)
ですので、stdinに直接入力してあげました。

環境

  • Ubuntu 18.04 on XFS
  • Docker

ホスト to コンテナのstdin

注意
docker attachはしていても構いませんが、interactiveにはしないでください。
していると、stdinは「Text file busy」と返して成立しません。
attach時には以下のようにします。(この場合はattachではstdinを受けません。attachかファイル直接入力かどちらかを選ぶということ。)

attach
docker -H DOCKERHOST attach コンテナ名 --no-stdin

まず、topで標準入力先のpidを確認します。

# docker top zzzzzzzz_container
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                25312               25287               0                   16:09               ?                   00:00:00            /bin/sh -c sh /minecraft/resources/run-Main.sh
root                25366               25312               0                   16:10               ?                   00:00:00            sh /minecraft/resources/run-Main.sh
root                25434               25366               0                   16:10               ?                   00:00:00            sh /minecraft/resources/run-BE-BDS.sh
root                25472               25434               13                  16:10               ?                   00:14:22            /minecraft/bin/bedrock_server

標準入力をコマンドで実行します

# echo "help" > /proc/25472/fd/0

helpをbedrock_serverに標準入力すると、ヘルプがちゃんと返されます。

# docker logs zzzzzzzz_container --tail=10
[2019-12-15 08:09:39 INFO] Player disconnected: hanicraft, xuid: 2535460621431466
§2--- Showing help page 1 of 18 (/help <page>) ---
/? [command: CommandName]
/? <page: int>
/alwaysday [lock: Boolean]
/changesetting allow-cheats <value: Boolean>
/changesetting difficulty <value: Difficulty>
/changesetting difficulty <value: int>
/clear [player: target] [itemName: Item] [data: int] [maxCount: int]
§2Tip: Use the <tab> key while typing a command to auto-complete the command or its arguments

コンテナ to コンテナのstdin

送信元コンテナ作成

docker create --name コンテナ名 --memory=3500mb --network="host" --storage-opt size=2G  --cap-add SYS_PTRACE  -v /proc:/newproc:ro

注目ポイントは

  • SYS_PTRACE権限の追加
  • ホストの/procを/newprocとして読み取り専用でマウント

あとはさっきと同じです。

コンテナ内
# echo "help" > /newproc/25472/fd/0

利用して作ったライブラリ

@itsu_dev と作りました。
いつくんがtopのスクレイピングしてくれているので興味深いでしょう。

参考文献

https://serverfault.com/questions/178457/can-i-send-some-text-to-the-stdin-of-an-active-process-running-in-a-screen-sessi
https://orebibou.com/2016/04/linux%E3%81%A7%E5%8B%95%E4%BD%9C%E4%B8%AD%E3%81%AE%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AE%E5%87%BA%E5%8A%9B%E5%86%85%E5%AE%B9%E3%82%92%E3%81%BF%E3%82%8B/

脚注

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

Docker Toolboxでローカルホストにアクセスする

Docker-Toolboxを使っているとき、localhostにアクセスできずに少し詰まったのですが、意外と忘れてしまうのでメモしておきます。

 環境

  • windows 10 home
  • Docker-Toolbox

Docker-Toolboxとは

macやwindows 10 ProではないPCにdockerを導入する際に利用するもの。
詳しくはこちらの記事がわかりやすいです。
windows 10 home で docker を導入するメモ

localhostへアクセス

Docker-Toolboxは他のDockerと違い、localhostへアクセスする際にはひと手間必要です。
コンテナを立ち上げたあと、localhostでアクセスしようとしてもアクセスできません。
image.png

docker-machineのIPを確認する

docker-machine ipで自分が今コンテナを動かしている仮想マシンのIPを確認します。

$ docker-machine ip
192.168.99.102

取得したIPとポートでアクセス

今回はコンテナに3000ポートでマッピングしているためhttp://192.168.99.102:3000でアクセス。
image.png

コンテナで起動しているRailsが無事表示されました。

まとめ

Docker-Toolbox上のコンテナにアクセスする場合は、仮想マシンのIPを確認する。

何度やってもたまに忘れてしまうので、気をつけたい。。。

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

Dockerのコンテナ内で設定したエイリアスがログアウトしたら無効になってしまう件について

Dockerで開発環境を構築してTDDを用いて個人開発をしてたのですが、テストを実行する度に

$ vendor/bin/phpunit

と入力するのめんどくさいですよね。

そこでエイリアスを設定して、puと入力したらテストを走らせるようにしようとしたのですが、コンテナ無いから抜けたらエイリアスが使えないという現象に出くわしたので忘備録として残しておきます。

// ashでAppコンテナ内に入る
$ docker-compose exec app ash

// ルートディレクトリに移動
$ cd /

// .profileファイルをviコマンドで開く
$ vi .profile

#.profileファイル内に下記を追加
alias pu="clear && vendor/bin/phpunit"

// ファイルの読み込み直し
$ source .profile

// ドキュメントルートに移動
$ cd /work/

// エイリアスで設定したコマンドを実行
$ pu   ←ここでは実行できる

// 一旦コンテナを抜ける
$ exit

// ashでAppコンテナ内に再び入る
$ docker-compose exec app ash

// エイリアスで設定したコマンドを実行
$ pu   ←ここではエイリアスで設定したコマンドが実行できない
ash: pu: not found

原因として、コンテナ内に入った時はデフォルトでは非ログインシェル状態だそうです。
そのためユーザーを変更して上げる必要があるみたいです。

// ashでAppコンテナ内に入る
$ docker-compose exec app ash

// ashユーザーにスイッチ
$ ash -l

// エイリアスで設定したテストを走らせる
$ pu

間違ってたら優しくご指摘お願いします。

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

Docker チートシート

再起動 or 実装変更を反映させる

foregroundで起動していた場合

docker-compose up

backgroundで起動していた場合

docker-compose restart
restartでは、コンテナはそのままでソースコードの変更だけ反映可能??

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

Dockerの CentOS8 イメージのlocale 設定

概要

Docker の CentOS8 イメージを取得したが、locale にja_JPが存在しない。
ロケールの取得から設定までの手順を記載する。

手順

日本語localeの取得

dnf -y install langpacks-ja

下記コマンドでja_JPが存在するか確認する。

localectl list-locales

設定

localectl set-locale LANG=ja_JP.UTF-8

設定の反映

source /etc/locale.conf

以上

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

CentOS8イメージでコンテナを作成 ユーザ追加とsudo権限付与

概要

Docker のCentOS8 イメージを取得し、sudo権限を持ったユーザを作成するところまで。

以前書いた下記の記事のCentOS8版になります。
Docker(CentOS)イメージから新規ユーザを追加し運用できるようにするまで
https://qiita.com/libra_lt/items/dd98297cb00aaf1c4c67

手順

イメージ取得

docker pull centos:8

起動

デタッチ起動する場合のみ-dオプションつけます。

docker run --name cos8 --privileged -d centos:8 /sbin/init

実行

デタッチ起動した場合は下記コマンドでインタラクティブモードに。

docker exec -it コンテナID /bin/bash

sudo付与

ユーザを追加してsudo権限を付与する手順

sudoインストール

dnf -y update
dnf -y sudo passwd cracklib-dicts

ユーザ追加

useradd -m ユーザ名

パスワード設定

下記コマンド実行後、パスワードを設定する

passwd ユーザ名

sudo設定追加

sudo設定ファイルにユーザを追加

echo "ユーザ名 ALL=(ALL) ALL" >> /etc/sudoers

以上

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

Dockerにおけるbinding.pryの使い方

はじめに

今回はdockerでポートフォリオを作成中なのですがこれまで使用していた仮想環境とdockerでbinding.pryの使い方が違ったのでまとめておきます。

全体流れ

gemのインストール

デバッグしたい箇所にpry

コンテナを再起動

railsコンテナにアタッチして接続

デバッグ

gemのインストール

gemfileに記入してbundle installです。開発環境でしか使用しないのでdevelopmentに書いています。
image.png

デバッグしたい箇所にpry

image.png
確認したいメソッド内でbinding.pryを記入します

コンテナを再起動、アタッチで接続

docker psで起動中のコンテナの確認、docker attach コンテナ名でコンテナに接続します。この状態で記入した画面でリロード、操作すると中断されてインスタンス変数の中身の確認等を行うことができます。

ctrキー→p→qの順番で抜けれます

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

Mac+Docker+ubuntu (??)

動作環境

Mac OS Catalina 10.15.1

概要

Mac上でROSを動かしたいと思い、公式を確認したところサポートされてるのがubuntu上だけだったので、Dockerを使ってubuntuを入れた後、その中でROSを動かしたいと思ったのでその実行手順をまとめておこうと思う。rosを入れる手順は別の記事でまとめようと思う。

まずはDockerをインストールする

とりあえず、MacにDockerをインストールする

https://hub.docker.com/editions/community/docker-ce-desktop-mac

公式サイトでまずは自分のアカウントを作ってログインする

スクリーンショット 2019-12-14 16.18.12.png

そして右上のGet Dockerからインストールできる

この時、ターミナル上で以下のコマンドでちゃんとインストールされたか確認できる

docker version

この時、macのアプリケーションフォルダに入ってるDockerをクリックして起動させてないといけないらしく、参考にした記事には載ってなかったのでこちらには載せておく

ubuntuを入れる

これでDockerは入ったのでubuntuを入れていく

docker pull ubuntu:16.04

今回はubuntu 16.04を入れる

https://hub.docker.com/_/ubuntu/

こちらのdockerhubの方でpull可能なubuntuのバージョンは載ってるので好きなのを入れましょう

docker images

これでpullしたものを確認する

ubuntu16.04があればきちんとpullできている

ubuntuが入ったら

まずはubuntuの起動をする

docker run -it -d --name ubuntu1604 ubuntu:16.04

dockerのrunコマンドで起動する

正しく起動されてるか確認する

docker ps

コンテナ名はubuntu1604なので、正しく起動されてたら出てくると思う

もし、出てこない場合は

docker ps -a

これでpullがちゃんとできてるか確認する

起動したらそのコンテナに入る

docker exec -it ubuntu1604 /bin/bash

これでコンテナ内に入れた

抜けたい時は

exit

再度入りたい場合は

docker exec -it ubuntu1604 bash

これで入れる

コンテナを停止させる場合

docker stop ubuntu1604

再度動かす場合

docker start ubuntu1604

停止と再起動はどちらもコンテナから抜けてからやる必要がある

ubuntuに入った状態でまずはユーザーを追加する

adduser user_name

user_nameには自分の好きな名前を入れよう

gpasswd -a user_name sudo

これでubuntu内でユーザーが追加された

これで一通りできたわけだが、例えばubuntu内でpythonのコードを書いて実行するには
ファイルの読み書きができないといけないが、docker内で処理されるので、macに入れてるテキストエディタは使えない

ということでubuntuにvimを入れておいた

sudo apt-get update
sudo apt-get install vim

これでvimをインストールできた

参考文献

Dockerをインストールした時に参考にさせていただいた
https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81

ubuntu入れる時に参考にさせていただいた
https://weblabo.oscasierra.net/docker-ubuntu1604/

ユーザーを追加する時に参考にさせていただいた
https://qiita.com/white_aspara25/items/c1b9d02310b4731bfbaa

vim入れる時参考にさせていただいた
https://qiita.com/YumaInaura/items/3432cc3f8a8553e05a6e

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

DockerのMySQLに,GDALを使ってシェープファイルをインポートしてみる

概要

 本記事では,Docker環境において,シェープファイルをもとにしたジオメトリデータを提供するサーバを構築してみます.
ジオメトリデータはMySQLに格納し,クライアントからのリクエストに応じてデータを受け渡すことを想定します.

 シェープファイル のジオメトリデータを MySQL にインポートするには,GDALというGISライブラリの ogr2ogr を用います.GDALのインポート方法はいくつかあるのですが,本記事ではGDALのDockerイメージを使用します.例としてNode.js(Express)のDockerアプリにGDALを組み込んで,Docker Composeで構築していきます.

環境

  • Windows10 Pro 64bit
  • Docker for Windows 19.03.2
  • docker-conpose 1.24.1
  • Node.js 12.13.0 ※expressの雛形形成のためにローカルで使います
  • npm --version 6.12.0

実装

Expressの雛形を準備

 ExpressのDocker環境の構築は,基本的に docker-compose(Docker for mac)で実践的なnode.js開発環境を作る の手順を継承します.

 まず,作業ディレクトリでexpressexpress-generatorをインストールします.ただし,global環境へのインストールは避けたいので,下記のようにします.

$ npm install --save express
$ npm install --save express-generator

 次に,雛形を作るコマンドを実行します.npmでライブラリをローカルにインストール際にはpathが通らないので,下記のようにcmdファイルを指定します.myappは各自任意の命名をしてください.
 テンプレートエンジンはhtmlをほぼそのまま扱える ejs を指定していますが,各自の開発に合わせて設定してください.なにも指定しない場合のデフォルトは,jadeになります.ejsやjadeの違いはこちらを参照.

$ ./node_modules/.bin/express.cmd myapp --view=ejs

 以上で ,myapp 配下に Express の雛形が生成されました.以降の作業ディレクトリは,./myapp 配下になります.

docker-compose.ymlの作成

 myapp直下に,下記の docker-compose.yml を置きます.

docker-compose.yml
version: '2'
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - "./conf.d:/etc/nginx/conf.d"
    links:
      - node_express
    networks:
      - app-net

  node_express:
    image: node:6.9-alpine
    container_name: node_express
    hostname: node_express
    volumes:
      - ".:/src"
    working_dir: /src
    command: [sh, -c, npm install && npm start]
    ports:
      - "3000:3000"
    links:
      - mysql
    networks:
      - app-net

  mysql:
    image: mysql:5.7
    container_name: mysql
    hostname: mysql
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports:
      - "3306:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=root"
    volumes:
      - ./db/mysql-data:/var/lib/mysql
    networks:
      - app-net

  gdal:
    image: osgeo/gdal:ubuntu-full-3.0.2
    container_name: gdal
    hostname: gdal
    volumes: 
      - "./geodata:/home"
    tty: true
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

 ポイントは,以下の3点.

  • gdalコンテナはtty: trueをつけないとコンテナが維持されない.
  • gdalコンテナは./geodata:/homeをマウントし,ここにMySQLに入力したいシャープファイルを配置する.
  • 各コンテナにnetworksを指定し,この後 MySQL の入出力データを各コンテナから通信できるようにする.

 また,参考記事と同様に./conf.dフォルダを作成し,その配下に下記 nginx 用のファイルを置きます.

nodejs.conf
server {
    listen 80;
    server_name _;
    client_max_body_size 10M;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://node_express:3000/;
    }
}

シェープファイルのダウンロード と 作業ディレクトリへの配置

 今回は例として,国土交通省が公開している国土数値情報のうち,低位地帯データ(神奈川県)のシェープファイルをインポートしてみます.
 ダウンロードサイトにアクセスし,神奈川県にチェックのうえアンケートに回答しダウンロードします.
G08-15_14_GML.zipがDLされたと思うので,解凍します.解凍したフォルダ内にG08-15_14.shpがあることを確認します.

 作業ディレクトリに戻り,docker-compose.ymlと同じ階層に ,docker-compose.yml内でgdalコンテナのマウント先に設定したgeodataフォルダを作成します.ここに先程解凍したデータをフォルダごと配置します.

Dockerコンテナの立ち上げ

 ここまでの作業でDockerコンテナを立ち上げる準備ができたので,docker-compose.ymlと同じ階層で下記コマンドを実行してコンテナを起動します.
 起動後,ブラウザからhttp://localhost:3000にアクセスして何か表示されれば成功です.また,$ docker psコマンドでコンテナが4つ起動していることを確認します.

$ docker-compose up -d --build

GDAL(ogr2ogr)を使って シェープファイル を MySQL にインポート

 まずはMySQLのDockerコンテナに入り,データベースを作成します.DBの名前はflood_mapとしていますが,ご自身の開発に合わせてDB名を設定します.

$ docker exec -i -t mysql bash
$ mysql -h 127.0.0.1 -u root -proot
mysql> create database flood_map;

 次に,gdalのDockerコンテナに入り,ogr2ogr を使ってMySQLにデータを入れていきます.
 home ディレクトリ配下にシェープファイルを含むファイル群がマウントされますので,ご自身の環境にあわせてシェープファイルを指定します.MySQLのホスト名はMySQLのコンテナ名を指定することでうまくいきます.

$ docker exec -i -t gdal bash
$ ogr2ogr -f "MySQL" MySQL:"flood_map,host=mysql,user=root,password=root,port=3306" ./home/G08-15_14_GML/G08-15_14.shp

MySQLでデータを確認しインデックスを張る

 再び MySQL コンテナに戻り,SQLを実行してみてデータが入っていることを確認します.たくさん表示すると問題があるので,上から10件のデータを表示してみます.

mysql> use flood_map;
mysql> SELECT ogr_fid,AsText(Envelope(ExteriorRing(shape))) from g08_15_14 limit 10;

+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ogr_fid | AsText(Envelope(ExteriorRing(shape)))                                                                                                                                                |
+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|       1 | POLYGON((139.52142804001 35.6371572663072,139.523032038531 35.6371572663072,139.523032038531 35.6379951881551,139.52142804001 35.6379951881551,139.52142804001 35.6371572663072))    |
|       2 | POLYGON((139.521452996084 35.6376134289269,139.521644357377 35.6376134289269,139.521644357377 35.6376736855994,139.521452996084 35.6376736855994,139.521452996084 35.6376134289269)) |
|       3 | POLYGON((139.52196220354 35.6369668672147,139.522425630462 35.6369668672147,139.522425630462 35.6371686759932,139.52196220354 35.6371686759932,139.52196220354 35.6369668672147))    |
|       4 | POLYGON((139.541073491975 35.6360655457882,139.543607066891 35.6360655457882,139.543607066891 35.6382958203003,139.541073491975 35.6382958203003,139.541073491975 35.6360655457882)) |
|       5 | POLYGON((139.540973950349 35.6380131944947,139.541300259083 35.6380131944947,139.541300259083 35.6382231275051,139.540973950349 35.6382231275051,139.540973950349 35.6380131944947)) |
|       6 | POLYGON((139.543183728112 35.6372670693054,139.543547637788 35.6372670693054,139.543547637788 35.6377649038623,139.543183728112 35.6377649038623,139.543183728112 35.6372670693054)) |
|       7 | POLYGON((139.543439171221 35.6369067894177,139.543604469296 35.6369067894177,139.543604469296 35.6370422598022,139.543439171221 35.6370422598022,139.543439171221 35.6369067894177)) |
|       8 | POLYGON((139.540877273964 35.6326687791813,139.543489041583 35.6326687791813,139.543489041583 35.6348299205534,139.540877273964 35.6348299205534,139.540877273964 35.6326687791813)) |
|       9 | POLYGON((139.20295027585 35.6304782158446,139.204943801785 35.6304782158446,139.204943801785 35.6336142552756,139.20295027585 35.6336142552756,139.20295027585 35.6304782158446))    |
|      10 | POLYGON((139.541693310682 35.6317030003044,139.544202942505 35.6317030003044,139.544202942505 35.6332801242532,139.541693310682 35.6332801242532,139.541693310682 35.6317030003044)) |
+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

 SQLの説明をすると,MySQLにはジオメトリデータを扱うための関数が用意されています.今回 g08_15_14 テーブルの shape カラムの中に格納されているものは,POLYGON型(≒多角形)のデータで,それぞれが低位地帯の1領域を表します.ExteriorRing()で POLYGON の外周を構成する座標の集合を取得できます.またDB内にはデータがバイナリで保存されていますが,AsText(Envelope()) で人間の読める形に変換しています.

 データが格納されていることが確認できたら,インデックスを張ります.MySQLにはジオメトリデータ用のインデックスが用意されています.

mysql> ALTER TABLE g08_15_14 ADD SPATIAL INDEX(shape);

DBアクセス用のユーザ作成

 rootを使ってDBにアクセスするのはセキュリティ的によろしくないので,権限を絞ったユーザを作成します.APIサーバからアクセスする際はユーザを使ってアクセスすることを想定します.

mysql> CREATE USER 'user1'@"%" IDENTIFIED BY "user1";
mysql> GRANT SELECT ON flood_map.g08_15_14 TO user1@"%";

【補足】SRIDについて

 ジオメトリデータにはSRID(空間参照識別子)があります.SRIDが何かについて詳細はこちら.今回の神奈川県の低位地帯データは,JGD2011の緯度経度の座標系ですので,本来はSRIDは6668が設定されているべきです.ただ,MySQL5.7でogr2ogrを使うと,SRIDを1から連番で振ってしまいます.※MySQL8であれば正しく設定される模様...

mysql> SELECT * from geometry_columns;
+-----------------+----------------+--------------+-------------------+-----------------+------+---------+
| F_TABLE_CATALOG | F_TABLE_SCHEMA | F_TABLE_NAME | F_GEOMETRY_COLUMN | COORD_DIMENSION | SRID | TYPE    |
+-----------------+----------------+--------------+-------------------+-----------------+------+---------+
| NULL            | NULL           | g08_15_14    | SHAPE             |               2 |    1 | POLYGON |
+-----------------+----------------+--------------+-------------------+-----------------+------+---------+

SRIDが6668だと思ってるとエラーになります.

mysql> SET @g1 = GeomFromText('Polygon((139.695773 35.532169,139.695773 35.5068084,139.615388 35.5068084,139.615388 35.532169,139.695773 35.532169))', 6668);
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select count(*) from g08_15_14 where MBRIntersects(SHAPE, @g1) = 1;
ERROR 3033 (HY000): Binary geometry function mbrintersects given two geometries of different srids: 1 and 6668, which should have been identical.

 下記であれば通ります.

mysql> SET @g1 = GeomFromText('Polygon((139.695773 35.532169,139.695773 35.5068084,139.615388 35.5068084,139.615388 35.532169,139.695773 35.532169))', 1);
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select count(*) from g08_15_14 where MBRIntersects(SHAPE, @g1) = 1;
+----------+
| count(*) |
+----------+
|       29 |
+----------+
1 row in set (0.04 sec)

コンテナの終了

 下記コマンド実行.

$ docker-compose down

まとめ

 本記事では,Docker環境でジオメトリデータを扱うためのサーバ環境を構築し,実際にデータをMySQLに入れてみました.MySQL内のデータを取り出すためには,node.jsでSQLを叩くAPIを用意し,フロントからアクセスすれば実現可能です.

参考文献

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