インターネットのスケッチ

Done is better than perfect.

docker-compose.ymlのトップレベルに書くvolumesについて

名前付きvolume

docker-compose.ymlの書き方について調べていると、以下のような設定がたまにあります。

version: "3.2"
services:
  web:
    image: nginx:alpine
    volumes:
      - type: volume
        source: mydata
        target: /data
        volume:
          nocopy: true
      - type: bind
        source: ./static
        target: /opt/app/static

  db:
    image: postgres:latest
    volumes:
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
      - "dbdata:/var/lib/postgresql/data"

volumes:
  mydata:
  dbdata:

今回疑問に思ったのは、一番下にある volumes はどういう役割があるのか、ということです。 servicesの中にあるvolumesは普通に見るけど、外にあるvolumesって何だろうと。

これはトップレベルに定義するvolumesで、「名前付きvolume」と言うらしいです。 なるほど、一番上の階層はトップレベルっていうんですね。

そして名前付きvolumeは単体のサービス内だけでなく、複数のサービス(例えばwebとdb)内のvolumesから参照できるようです。

ちなみに

サービスにはコンテナがアタッチされるため、サービスの実態はコンテナです。

データの永続化について

名前付きvolumeを使うと、 docker-compose down や docker-compose prune とかでコンテナを削除したらDBのデータも消えるっぽい?

この方式は各サービスのvolume:ではホスト側はボリュームの名称を指定するのでいちいちディレクトリパスを書かずに済むのですが、dockerコマンドでボリュームを削除するとホスト側のディレクトリごと削除されてしまう

トップレベルに書かないで、サービスごとに書けば問題ないみたいだが、また機会があれば検証してみようと思います。

とりあえず今は名前付きvolumeは使わないで進めてみます。

参考

Compose file version 3 reference | Docker Documentation

さわって理解するDocker入門 第4回 | オブジェクトの広場
→「volumes - volume(データの永続化領域)の定義」のところ

Docker Composeのトップレベルvolumesでホストのディレクトリをマウントする

Docker for Macを使ってRuby on Rails開発環境を構築する | mediba Creator × Engineer Blog

Docker for MacでRails/MySQLの開発環境を作る

環境

プロジェクトを定義する

プロジェクトディレクトリを作成し、以下のファイルを全て同じ階層に置きます。

.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── docker-compose.yml
└── .env

以下は各ファイルの内容です。 空のGemfile.lockも作っておきましょう。

Dockerfile

# Rubyの公式コンテナ(Docker イメージ、実行環境、命令の標準セットを含む)を使う
FROM ruby:2.5.0

# RubyイメージはDebianイメージをベースにしているため、apt-getを使い依存関係をインストールする
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

# /lib/railbook を作業用ディレクトリに設定する
# 以下のRUN,CMD,ENTRYPOINT,COPY,ADDコマンドはこのディレクトリ配下で実行される
RUN mkdir /railbook # --- 訂正前 --- RUN mkdir /lib/railbook
WORKDIR /railbook # --- 訂正前 --- WORKDIR /lib/railbook

# Gemfile をコピーして bundle install する
COPY Gemfile /railbook/Gemfile # --- 訂正前 --- ADD Gemfile /lib/railbook/Gemfile
COPY Gemfile.lock /railbook/Gemfile.lock # --- 訂正前 --- ADD Gemfile.lock /lib/railbook/Gemfile.lock
RUN bundle install

COPY . /railbook # --- 訂正前 --- ADD . /lib/railbook

Gemfile

source 'https://rubygems.org'

gem 'rails', '5.1.5'

docker-compose.yml

version: '3'
services:
  db:
    image: mysql:5.7
    ports:
      - "3306:3306"
    env_file: .env # 環境設定はパスワードなども含むため、.envにして同階層に配置し、外部ファイルから読み込む
    volumes:
      - ./db/mysql:/var/lib/mysql:cached

  web:
    build: .
    depends_on:
      - db
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - "3000:3000"
    volumes:
      - .:/railbook:cached # --- 訂正前 --- - ./lib/railbook:/lib/railbook:cached

.env

MYSQL_USER=任意のユーザー名
MYSQL_ROOT_PASSWORD=任意のパスワード

プロジェクトを構築する

まず新規アプリを作成します。

$ docker-compose run web rails new . --force --database=mysql
Creating network "railsintro_default" with the default driver
Creating railsintro_db_1 ... done
Building web
3e731ddb7fc9: Pull complete
47cafa6a79d0: Pull complete
79fcf5a213c7: Pull complete
68e99216b7ad: Pull complete
4822563608bb: Pull complete
9d614f26bec1: Pull complete
1c758cfc0888: Pull complete
8a4fbc3666ca: Pull complete
Digest: sha256:ed5fc221d5d03d89e1f8c1f7780b98bc708e68b4d8dba73594d017e999156619
Status: Downloaded newer image for ruby:2.5.0
 ---> bae0455cb2b9
Step 2/8 : RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
 ---> Running in e553e712bc3f
Reading package lists...
Building dependency tree...
Reading state information...
libpq-dev is already the newest version (9.6.6-0+deb9u1).
The following additional packages will be installed:
  dpkg-dev fakeroot libalgorithm-diff-perl libalgorithm-diff-xs-perl
  libalgorithm-merge-perl libfakeroot libuv1
Suggested packages:
  debian-keyring
The following NEW packages will be installed:
  build-essential dpkg-dev fakeroot libalgorithm-diff-perl
  libalgorithm-diff-xs-perl libalgorithm-merge-perl libfakeroot libuv1 nodejs
0 upgraded, 9 newly installed, 0 to remove and 20 not upgraded.
Need to get 5328 kB of archives.
After this operation, 17.0 MB of additional disk space will be used.
Get:1 http://deb.debian.org/debian stretch/main amd64 dpkg-dev all 1.18.24 [1592 kB]
Get:2 http://deb.debian.org/debian stretch/main amd64 build-essential amd64 12.3 [7346 B]

〜中略〜

WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
/lib/railbook/Gemfile not found

以上で土台となるスケルトン(骨組み)が作成できました。そしてイメージを再構築する必要があるので、buildコマンドを叩きます。

$ docker-compose build
db uses an image, skipping
Building web
Step 1/8 : FROM ruby:2.5.0
 ---> bae0455cb2b9
Step 2/8 : RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
 ---> Using cache
 ---> 26f1748c3d95
Step 3/8 : RUN mkdir /lib/railbook
 ---> Using cache
 ---> c7beaa0e6aa0
Step 4/8 : WORKDIR /lib/railbook
 ---> Using cache
 ---> 5484205f8d5c
Step 5/8 : ADD Gemfile /lib/railbook/Gemfile
 ---> Using cache
 ---> 910e85a15616
Step 6/8 : ADD Gemfile.lock /lib/railbook/Gemfile.lock
 ---> Using cache
 ---> 4ba7b0021eea
Step 7/8 : RUN bundle install
 ---> Using cache
 ---> 948e06ccc33c
Step 8/8 : ADD . /lib/railbook
 ---> c1881c2864b8
Successfully built c1881c2864b8
Successfully tagged railsintro_web:latest

データベースに接続…と思ったら

config/database.ymlを書き換え…config/database.ymlがない! どこで間違えたのか調べます。 Dockerfileとかで指定した /lib/railbook がなんか怪しい気がしますね。

一旦、コンテナとイメージを削除してbuildからやり直してみます。

$ docker container rm CONTAINER ID

$ docker image rm IMAGE ID

とりあえずDockerfileとdocker-compose.ymlを公式と同じ書き方にしてbuildしてみると、ルートにappなどのディレクトリが自動でたくさん作成され、config/database.ymlも作成されました。 build中にターミナルで表示されるメッセージも後半部分が違っています。

そこでエラーメッセージをよく確認すると、最初にdocker-compose runしたときの最後のエラーメッセージに原因が書いてありました。

/lib/railbook/Gemfile not found

つまりdocker-compose.ymlのwebのvolumesの指定がおかしかったのです。 volumesのホスト側はGemfileと同じ階層を指定しなければいけなかったようです。

    volumes:
      - ./lib/railbook:/lib/railbook:cached

この箇所を以下のように修正します。

    volumes:
      - .:/railbook:cached

ついでにDockerfileのADDコマンドをCOPYコマンドに変えておきます。 Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント

修正箇所は最初に記載したコードに記しておきました。

そして再び、 docker-compose run web rails new . --force --database=mysql を叩きます。 今度はうまくいきました。

改めてデータベースに接続

config/database.ymlの username とpassword をdocker-compose.ymlのdbサービスで設定したものに書き換えます。


ここから追記 2018-4-27

database.ymlには username とpassword の他に、hostをdocker-composeで定義したDBのサービス名にしないといけません。dbとwebでコンテナをわけているので、hostをdbにしないと「/var/run/mysqld/mysqld.sock' (2 "No such file or directory")」のようなソケットエラーが出ます。

database.yml(抜粋)

〜中略〜

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: 任意のユーザー名
  password: 任意のパスワード
  host: db

development:
  <<: *default
  database: films_development

〜中略〜

追記終わり


起動します。

$ docker-compose up

起動を確認できたら、一旦 Ctrl-C で停止します。 最後にデータベースを作成します。

$ docker-compose run web rake db:create

バックグラウンドで再起動します。

$ docker-compose up -d

ページが表示されるか確認する

ブラウザで http://localhost:3000 に接続して、Railsのウェルカムページ「Yay! You’re on Rails!」の画面が表示されればOKです。

感想

慣れてないと色々調べるのに時間はかかるが、一度設定ファイル作れば起動は楽になります。なんだかんだ不明点を調べるのに1日かかりましたが。 今度は今話題のKubernetesも試してみようと思います。

主な参考資料

クイックスタート・ガイド:Docker Compose と Rails — Docker-docs-ja 17.06.Beta ドキュメント

Quickstart: Compose and Rails | Docker Documentation
英語版のほうがversionなど新しかったので、基本的な書き方はこちらに沿って行いました。

[Rails] DockerでRails + MySQLの開発環境をつくる手順 - Qiita

Rails with docker-composeで'/tmp/mysql.sock'がねえよと怒られたとき - Qiita

RailsプロジェクトのDocker導入手順 | 酒と涙とRubyとRailsと

Docker & Rails: Running a Rails Development Environment in Docker | Codeship | via @codeship

Cookpad Inc. · GitHub

NeovimでGo言語の開発環境作ってみた

blog.craftz.dog

こういうかっこいいエディター作りたいなと思って、上の記事の設定を取り入れつつ、適当にしていたVimの設定を見直してみた(普段はVSCode使ってた)。

環境はmacなので、Windowsでは検証してない。
あと最終的な設定内容はこちらを参照。GitHub - khanamoto/dotfiles

ざっくり環境

Neovimの導入

のっけからVimでなくNeovim(以下nvim)に乗り換える。
元々Vim+NeoBundleを使ってたけど、調べてみるとVimはNeovimが、NeoBundleはdein.vimがモダンみたいなので。

  • homebrewでneovimをインストール
$ brew update
$ brew upgrade
$ brew install neovim
  • ~/.config/nvimディレクトリを作成して、その中にinit.vimファイルを作る(~/.config/nvim/init.vim)。これがVimの.vimrcに当たる。nvim起動時に読み込まれる。 まずは基本設定
"---------------------
" 文字
"---------------------
" 読み込み時の文字コード
set encoding=utf-8
" Vim Script内でマルチバイトを使う場合
scriptencoding utf-8
" 保存時の文字コード
set fileencoding=utf-8
" 読み込み時の文字コードの自動判別. 左側が優先される
set fileencodings=ucs-boms,utf-8,euc-jp,cp932
" 改行コードの自動判別. 左側が優先される
set fileformats=unix,dos,mac

"---------------------
" 行
"---------------------
" 行数表示
set number
" 折り返し
set wrap

"---------------------
" インデント(Go向け)
"---------------------
set noexpandtab
set tabstop=4
set shiftwidth=4
set autoindent

"---------------------
" 作らない系
"---------------------
"スワップファイルを作らない
set noswapfile
"バックアップファイルを作らない
set nobackup
"undoファイルを作らない
set noundofile

"---------------------
" その他
"---------------------
" クリップボードの共有
set clipboard=unnamed
" イントロダクション非表示
set shortmess+=I

dein.vimの導入

  • deinをインストールするためにinit.vimに以下を追記。
"---------------------
" dein
"---------------------
" プラグインが実際にインストールされるディレクトリ
let s:dein_dir = expand('~/.cache/dein')
" dein.vim 本体
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'

" dein.vim がなければ github から落としてくる
if !isdirectory(s:dein_repo_dir)
  execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
endif
execute 'set runtimepath^=' . s:dein_repo_dir

" 設定開始
if dein#load_state(s:dein_dir)
  call dein#begin(s:dein_dir)

  " プラグインリストを収めた TOML ファイル
  " 予め TOML ファイルを用意しておく
  let s:toml      = s:dein_dir . '/.dein.toml'
  let s:lazy_toml = s:dein_dir . '/.dein_lazy.toml'

  " TOML を読み込み、キャッシュしておく
  call dein#load_toml(s:toml,      {'lazy': 0})
  call dein#load_toml(s:lazy_toml, {'lazy': 1})

  " 設定終了
  call dein#end()
  call dein#save_state()
endif

" もし、未インストールものものがあったらインストール
if dein#check_install()
  call dein#install()
endif
  • さらに~/.cache/dein直下に.dein.tomlファイルと.dein_lazy.tomlファイルを作りそれぞれ以下を記述(dein.tomlとdein_lazy.tomlの先頭のドットはつけなくてもOK。僕は設定系をドットファイルでまとめて管理しているのでドットファイルにしています)。

.dein.toml

[[plugins]]
repo = 'Shougo/dein.vim'

.dein_lazy.toml

[[plugins]] # TOMLのシンタックスハイライト
repo  = 'cespare/vim-toml'
on_ft = 'toml'

[[plugins]] # ステータス行を見やすく
repo = 'itchyny/lightline.vim'

追記した後、

$ nvim

とiTerm2でたたくと ~/.cache/deinのreposディレクトリ配下にdein.vimと関連プラグインがインストールされる。

作り込み

ここから冒頭の記事の設定を作り込んでいく。

  • tmuxをhomebrewで入れる。tmuxの使い方は他の方の記事を見てください。
$ brew install tmux

セッションの確認
$ tmux ls 
  • nvimの画面分割・ファイラ・エラー表示設定追加

init.vim

"---------------------
" vim側の画面分割
"---------------------
" 画面分割
nmap ss :split<Return><C-w>w
nmap sv :vsplit<Return><C-w>w
" 画面移動
nmap <Space> <C-w>w
map s<left> <C-w>h
map s<up> <C-w>k
map s<down> <C-w>j
map s<right> <C-w>l
map sh <C-w>h
map sk <C-w>k
map sj <C-w>j
map sl <C-w>l
" 画面リサイズ
nmap <C-w><left> <C-w><
nmap <C-w><right> <C-w>>
nmap <C-w><up> <C-w>+
nmap <C-w><down> <C-w>-

"---------------------
" vimfilerの設定(ファイル表示)
"---------------------
nmap sf :VimFilerBufferDir<Return>
nmap sF :VimFilerExplorer -find<Return>
nmap sb :Unite buffer<Return>
let g:vimfiler_as_default_explorer = 1
let g:vimfiler_safe_mode_by_default = 0
let g:vimfiler_enable_auto_cd = 0
let g:vimfiler_tree_leaf_icon = ''
let g:vimfiler_tree_opened_icon = '▾'
let g:vimfiler_tree_closed_icon = '▸'
let g:vimfiler_marked_file_icon = '✓'

"---------------------
" aleの設定(Lintエラー)
"---------------------
nmap <silent> <C-k> <Plug>(ale_previous_wrap)
nmap <silent> <C-j> <Plug>(ale_next_wrap)

.dein.toml

[[plugins]] # Unite.vimが依存
repo = 'Shougo/vimproc.vim'
build = 'make'

[[plugins]] # vimfilerが依存
repo   = 'Shougo/unite.vim'

[[plugins]] # VSCodeっぽいファイラ(後継としてdefx.nvimが開発中)
repo = 'Shougo/vimfiler'

.dein_lazy.toml

[[plugins]] # VSCodeっぽい非同期のエラー表示
repo = 'w0rp/ale'

詳しい使い方は冒頭の記事を参照してもらうとして、必要なプラグインについて補足。

ファイラの設定でvimfilerプラグインを入れているが、これを動かすためにvimproc.vimとunite.vimが必要(unite.vimの後継でより高速なdenite.nvimもあるが、vimfilerと組み合わせられないので、今回はunite.vimを使う)。
vimproc.vimはインストールしたあとにビルドが必要だが、.dein.tomlに以下を書いてもなぜかビルドできなかった。

build = 'make'

もしくは

hook_post_source = '''
  call dein#add('Shougo/vimproc.vim', {'build': 'make'})
'''

ビルドしないでファイラを開くと、例えば「:sf」とたたいたときこんなエラーが出る。 f:id:khanamoto:20171217193323j:plain

結局調べても原因がわからなかったので、Makefileのあるところまで移動して手動でビルドした。

$ cd ~/.cache/dein/repos/github.com/Shougo/vimproc.vim
$ make

そうすると ~/.cache/dein/repos/github.com/Shougo/vimproc.vim/lib にvimproc_mac.soが作られて、ビルドできていることが確認でき、エラーも消える。

ちなみにvimfilerの後継としてdefx.nvimというプラグインが開発中らしい。

Go周り

あとはGoの環境作り。以下追記。

.dein_lazy.toml

[[plugins]] # goコマンド補完
repo = 'fatih/vim-go'
on_ft = 'go'

[[plugins]] # vim-go代替
repo = 'zchee/nvim-go'
build = 'make'
on_ft = 'go'

[[plugins]] # コード補完
repo = 'Shougo/deoplete.nvim'
# プラグインが dein.vim によって追加されたとき実行
hook_add = 'let g:deoplete#enable_at_startup = 1'

[[plugins]] # コード補完(Golang用)
repo = 'zchee/deoplete-go'
on_ft = 'go'

コード補完はdeoplete.nvimだけでもできるが、Goでより詳しい補完をするためにdeoplete-goを入れる。deoplete-goを入れるには、以下が必要。gocodeはgo getで入れた。

Neovim and neovim/python-client
deoplete.nvim
gocode

vim-go入れたけどなぜか動かない(「エディタのコマンドではありません: GoFmt」って出る)ので、nvim-go入れた。

最終的にこんな見た目に。 f:id:khanamoto:20171217192735j:plain

おまけ

macの標準キーバインディングだとコロンとセミコロンが同じキーで、コロンを打つときにShiftをおさないといけない。しかしこれだとnvimでExコマンド打ったりするときに不便なので、コロンとセミコロンのキーバインディングを入れ替える。

詳しくはこちらを参照。 qiita.com

まとめ

開発環境は調べてみると結構大変なことが多い。ハマりどころで意外と情報がなかったり、調べても調べても解決法がわからなかったり。今回は丸2日位かかった。
それでも出来上がるとエディターをいじるのが楽しくなる。これでより気持ちよくプログラミングできようになると思う。

参考

Neovim+dein

neovim 入門1 - replicityの日記

NeoVim と dein.vim を使ってみる! - Qiita

NeoBundle から dein.vim に乗り換えたら爆速だった話 - Qiita

Vim入門(1): Vimのプラグインを管理するdein.vimの使い方 | Simplie Post

[dein.vim] hook の便利な使い方 - Qiita

初心者向け vimrcの設定方法 - Qiita

VSCodeっぽい開発環境

vim + tmuxでVSCodeっぽい開発環境を作る – 週休7日で働きたい

tmuxの使い方 - Qiita

[Vim] シンプルなファイラー「Vaffle」をつくりました | ここぽんのーと

unite.vimより高速なdenite.nvimを使う - Qiita

Vimメモ : ALE(Asynchronous Lint Engine)で非同期コードチェック - もた日記

vim流tmuxチュートリアル - Qiita

[tmux] 端末起動時に自動で新規セッションを作成 or 既存セッションにアタッチ - Qiita

Vim のカスタマイズ 〜キー割り当て変更方法〜 - Vim のブログ

Denite Compatibility · Issue #380 · Shougo/vimfiler.vim · GitHub
補足:ここで「vimfilerはdenite.vimと一緒に使えるのか?」という質問が出ているが、作者のShougoさんが、vimfilerを拡張してdenite.vimに対応させるのは難しいからvimfilerの後継としてdefx.nvimを作ってると回答している。だからvimfilerを使いたいならunite.vimを使ってくれと。

Go周り

【Go × Vim】 VimでGoを書く - 2015 Spring - Qiita

NeovimでGo言語を快適に書きたい - whileD'iary

VimでGoの開発環境を設定する方法のまとめ - Qiita

おまけ

macOS Sierraでコロンとセミコロンを入れ替える - Qiita

ドットファイル管理 事始め

最近開発マシンのディレクトリ構成いじってる流れで、いまさらだがドットファイルの管理をGitHubでしてみることにした。

こちらの記事のようにがっつり運用してみようとしたが、知識がないのでわからないところが多い。わからないまま取り入れても使いこなせなければ意味が無いので、とりあえずシンプルに始めてみる。 qiita.com

ドットファイルの整理

移動

シェルスクリプトファイルを作成

#!/bin/bash

ln -sf ~/dev/src/github.com/dotfiles/.gitconfig ~/.gitconfig
ln -sf ~/dev/src/github.com/dotfiles/.vimrc ~/.vimrc
ln -sf ~/dev/src/github.com/dotfiles/.zshrc ~/.zshrc
  • ~/dev/src/github.com/dotfiles/etcに移動してコマンド操作
bash install.sh
ls -l ~/.gitconfig

↓ 結果

/Users/ユーザー名/.gitconfig@ -> /Users/ユーザー名/dev/src/github.com/dotfiles/.gitconfig

GitHubで管理

git init
git add .
git commit -m "initial commit"
git remote add origin git@github.com:GitHubアカウント名/dotfiles.git
git push -u origin master

完了

シェルをもっと勉強してこのリポジトリを育てていきたい

参考

最強の dotfiles 駆動開発と GitHub で管理する運用方法 - Qiita

dotfilesをGitHubで管理 - Qiita

bash シェルスクリプト入門 -シェルスクリプトのいろは- | UNIX & Linux コマンド・シェルスクリプト リファレンス

シンボリックリンクの作成と削除 - Qiita