20200706のdockerに関する記事は23件です。

DockerをHomebrewでインストール

はじめに

こちらではDocker Desktop for MacをHomebrewでインストールするやり方について説明したいと思います。
コマンドやらはまた今度

環境

Mac

手順

brew caskでインストール

$ brew cask install docker 

ダメだった場合

$ brew update-reset && brew update
$ brew cask

をしてからインストール

まとめ

いかがだったでしょうか
よくある記事のひとつでしたが次回は
今回の構築を参照して使いかたの説明をできればと思います。

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

なるべく最新Verで構築するRails6開発環境(Docker + Rails + Nginx + Puma + MySQL)

はじめに

こんにちは、Web系エンジニア転職にむけて学習中の Npakk と申します。

Railsを学習するにあたって開発環境を構築したので、その手順を少し解説を交えながらご紹介します。
Dockerでの構築経験はあまりなく、経験も乏しいのであくまで参考程度にご覧ください。
もし間違いやご指摘などあれば、ぜひぜひお願いいたします!

Dockerを使用して、ローカル環境でRailsのWelcomeページを確認できるまでが、この記事のゴールです。

参考記事

対象読者

  • Railsを学びたいけど、環境をどう作ればいいかわからない初学者の方
  • なるべく新しいRails環境を作りたい方

前提

  • Macユーザーを対象としています
  • Docker for Macがインストールされているものとします
  • Nginx と Puma を連携させています
  • 最新バージョンは、記事執筆時点で最新という意味です
  • 全てのソフトウェアが最新バージョンなわけではありません
  • 最新版だから動作が安定していたり、速度が速いというわけではありません

バージョン

  • ホストOS(macOS Catalina 10.15.5)
  • Docker(19.03.8)
  • docker-compose(1.25.5)
  • Ruby(2.7.1)
  • Ruby on Rails(6.0.3.2)
  • Nginx(1.19.0)
  • MySQL(8.0)

1. ディレクトリの作成

ディレクトリ構成としては以下のようになります。
あくまで、手動で作成する項目のみ記載しています。

/webapp
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── containers
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
├── docker-compose.yml
├── entrypoint.sh
└── environments
    └── db.env

1-1. アプリケーションルート

どこでもいいですが、わかりやすいところがおすすめです。

$ mkdir webapp

1-2. Nginxコンテナ用ディレクトリ

Nginxは、Rails・DBとは別のディレクトリを作ります。
設定ファイルや DockerFile を別途配置します。

$ mkdir -p webapp/containers/nginx

1-3. 環境変数用ディレクトリ

DBで使うユーザーのパスワードなどを記載したファイルを配置します。

$ mkdir webapp/environments

2. コンテナ生成用のファイルを作成

以降はアプリケーションルート内での操作となります。

$ cd webapp

2-1. Rails用Dockerfile

Ruby と Node.js、yarnのバージョン指定は後述する docker-compose.yml から引数として指定します。

Rails6からは標準で Webpacker というgemを使用しており、yarn というパッケージ管理ソフトに依存しています。
yarn がないとうまく動かないため、これをコンテナにインストールする必要があります。

また entrypoint.sh についてですが、Dockerをコマンドで停止せずに強制終了してしまうと、
Railsサーバーが開かれたままになるため、次からコンテナを起動するときにエラーが発生します。
その問題を回避するために server.pid を削除しています。

$ vim Dockerfile
Dockerfile
ARG RUBY_VERSION
FROM ruby:$RUBY_VERSION

ARG NODE_MAJOR
ARG YARN_VERSION

# ログインシェルとしてbashを使用する
SHELL ["/bin/bash", "-c"]

# nodejs取得
RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -

# yarn取得
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - &&\
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

# リポジトリを更新し依存モジュールをインストール
RUN apt-get update -qq &&\
  apt-get install -y\
  build-essential\
  nodejs\
  yarn=$YARN_VERSION-1

# ルート直下にwebappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ)
RUN mkdir /webapp
WORKDIR /webapp

# ホストのGemfileとGemfile.lockをコンテナにコピー
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock

# bundle installの実行
RUN bundle install

# ホストのアプリケーションディレクトリ内をすべてコンテナにコピー
ADD . /webapp

# puma.sockを配置するディレクトリを作成
RUN mkdir -p tmp/sockets

# コンテナ開始時に必ず実行されるシェルスクリプトをコンテナにコピー
ADD entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

2-2. Gemfile

Railsの最新バージョンをこのファイルで指定します。

$ vim Gemfile
Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails', '6.0.3.2'

2-3. Gemfile.lock

このファイルは作るだけで、中身は空で大丈夫です。

$ touch Gemfile.lock

2-4. Nginx用Dockerfile

$ vim containers/nginx/Dockerfile
/containers/nginx/Dockerfile
FROM nginx:1.19.0

# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/conf.d/webapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

2-5. Nginx用設定ファイル

$ vim containers/nginx/nginx.conf
/containers/nginx/nginx.conf
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream webapp {
  # ソケット通信したいのでpuma.sockを指定
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  # ドメインもしくはIPを指定
  server_name example.com [or 192.168.xx.xx [or localhost]];

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  # ドキュメントルートの指定
  root /webapp/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @webapp;
  keepalive_timeout 5;

  # リバースプロキシ関連の設定
  location @webapp {
    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_pass http://webapp;
  }
}

2-6. DB接続用設定ファイル

ユーザー名・パスワードなどは適宜変更してください。

$ vim environments/db.env
db.env
MYSQL_ROOT_PASSWORD=db_root_password
MYSQL_USER=user_name
MYSQL_PASSWORD=user_password

2-7. entrypoint.sh

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

2-8. docker-compose.yml

argsにRubyなどのバージョンを指定します。
ここで指定することによりDockerfileに値が渡されます。

$ vim docker-compose.yml
docker-compose.yml
version: '3.7'

services:
  app:
    build:
      context: .
      args:
        RUBY_VERSION: '2.7.1'
        NODE_MAJOR: '14'
        YARN_VERSION: '1.22.4'
    env_file:
      - ./environments/db.env
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
    depends_on:
      - db
  db:
    image: mysql:8.0
    env_file:
      - ./environments/db.env
    volumes:
      - db-data:/var/lib/mysql
  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app
volumes:
  public-data:
  tmp-data:
  log-data:
  db-data:

3. Railsアプリケーションの生成と編集

3-1. Railsアプリケーションの生成

ここまでファイルとディレクトリを準備できたら、Railsアプリケーションを作成します。

以下のコマンドを実行すると、コンテナ内でRailsアプリケーションが生成されます。
DBにはMySQLを指定し、gemをこの時点でインストールしないようにbundle installの実行を抑制しています。

$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle

コンテナ内で生成されたアプリケーションをホスト側から編集するには、いちいちコンテナを実行しないといけないため不便です。

そこで、コンテナ内に生成されたディレクトリと、ホスト側のアプリケーションルートを繋ぎます。
こうすることによって、ホスト側のアプリケーションルートにファイルが生成されます。
これらのファイルを編集すれば、コンテナ内のディレクトリにも反映されるようになります。
(既にこの対応は以下の箇所で行っているので、安心してください。)

docker-compose.yml
#省略
    volumes:
      - .:/webapp
#省略

実行時に発生するエラー・警告について

Railsアプリケーション生成コマンド実行時、いくつかエラーと警告が発生します。
「失敗した!」と思われる前に、以下に記載するものについては無視してください。
(記載した以外のエラーや警告がもし発生した場合、一度最後まで手順を実行することをおすすめします。)

apt-key
deb.nodesource.comから落としてきたシェルスクリプトに記載されたapt-keyコマンドで発生した警告です。
詳細を調べてもいまいちよくわからなかったのですが、これを無視してもRailsの環境は構築できます。
(誰か詳しい方がいたら教えてください…)

Step 6/18 : RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -
・
・
・
+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
Warning: apt-key output should not be parsed (stdout is not a terminal)

debconf
これもいまいちよくわかりません。(書いといてなんだって話なんですが。)
apt-utilsをインストールしても消えなかったです。
無視しても大丈夫です。

Step 8/18 : RUN apt-get update -qq &&  apt-get install -y  build-essential  nodejs  yarn=$YARN_VERSION-1
・
・
・
debconf: delaying package configuration, since apt-utils is not installed

mysql2
Railsアプリケーション生成コマンドを実行して、最後に出力されたのがこのエラーです。
Gemfileにリストされたmysql2のgemがないよってことで、bundle installを迫ってきています。

このコマンドを実行した時点では、GemfileにはRailsしか記載されておらず、
推測ですが、下記の流れで処理されているのではないかと思います。
1. Gemfileに記載されたRalilsがインストールされる
2. Rails newにより、Gemfileの記載が更新されmysql2などが追加される
3. 最後に、Gemfileに書かれたRails以外のgemがインストールされていないためエラーが発生

このエラーを無視してもWelcomeページは確認できるので、一度最後まで手順を実施してみてください。

Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

3-2. 権限変更

生成したRailsアプリケーションのファイル群の所有権が root となっているので、現在のログインユーザーに変更します。

$ sudo chown -R $USER:$USER .

3-3. puma.rbの編集

$ vim config/puma.rb

元の記載は削除して、以下の内容を貼り付けてください。

puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true

3-4. database.ymlの編集

$ vim config/database.yml

元の記載は削除して、以下の内容を貼り付けてください。

database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
  password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %>
  host: db

development:
  <<: *default
  database: webapp_development

test:
  <<: *default
  database: webapp_test

上記のMYSQL_USER と MYSQL_PASSWORD は DB接続用の情報ファイル で定義した環境変数名を設定します。

4. イメージのビルドとコンテナの起動

いよいよコンテナを起動します!

4-1. イメージのビルド

Dockerfile 及び、DockerHub より引っ張ってきたイメージを全てビルドします。

$ docker-compose build

ビルドが完了したら以下のコマンドで確認します。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
webapp_web          latest              0ae7b3fc51fd        38 seconds ago      132MB
webapp_app          latest              d661a9898271        47 seconds ago      1.27GB
<none>              <none>              83d4ec18ac0c        6 minutes ago       1.06GB
ruby                2.7.1               9b840f43471e        9 days ago          842MB
nginx               1.19.0              2622e6cca7eb        3 weeks ago         132MB
mysql               8.0                 be0dbf01a0f3        3 weeks ago         541MB

4-2. コンテナの起動

ビルドしたら、下記のコマンドでコンテナを立ち上げます。

$ docker-compose up -d

コンテナが起動しているか確認します。

$ docker-compose ps

全てのコンテナの State が Up となっていることを確認してください。

    Name                  Command               State          Ports
---------------------------------------------------------------------------
webapp_app_1   entrypoint.sh bundle exec  ...   Up
webapp_db_1    docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp
webapp_web_1   /docker-entrypoint.sh /bin ...   Up      0.0.0.0:80->80/tcp

ビルドに失敗した場合

イメージのビルドに失敗したり、コンテナの起動に失敗するとローカルにゴミファイルがたまってしまいます。
一度全てきれいにしたい場合は、コンテナとイメージを全て削除するコマンドを使います。
まずは、コンテナの起動を止めてから実行してください。

コンテナを停止する

$ docker-compose stop

すべてのコンテナを削除する

$ docker rm $(docker ps -q -a)

すべてのイメージを削除する

$ docker rmi $(docker images -q)

5. DB設定

5-1. 権限の付与

DBの操作を一般ユーザーで行うため、実行権限を付与します。

GRANT文を記述したSQLファイルを作成します。
user_name は DB接続用の情報ファイル に設定した MYSQL_USER の値に置き換えてください。

$ vim db/grant_user.sql
grant_user.sql
GRANT ALL PRIVILEGES ON *.* TO 'user_name'@'%';
FLUSH PRIVILEGES;

dbコンテナに向けてクエリを送ります。
パスワードを求められるので、rootのパスワードを入力してください。

$ docker-compose exec db mysql -u root -p -e"$(cat db/grant_user.sql)"

権限が付与されたか確認します。
パスワードを求められるので、一般ユーザーのパスワードを入力してください。

$ docker-compose exec db mysql -u user_name -p -e"show grants;"

実行結果が横に長くてみづらいかもしれません。
MySQL5系では全ての権限が付与されている場合、「ALL PRIVILEGES」と表示されていたみたいですが、
8系ではちゃんと全ての権限名が表示されるため、このような横に長い結果になっているようです。

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for user_name@%                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `user_name`@`%`                                                                                                                        |
| GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,INNODB_REDO_LOG_ARCHIVE,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,XA_RECOVER_ADMIN ON *.* TO `user_name`@`%` |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

5-2. DBの作成

railsコマンドでDBを作成します。

$ docker-compose exec app rails db:create

6. 確認

お疲れさまでした!
下記のlocalhostをクリックして、Welcomeページが表示されるでしょうか。
Rails と Ruby のバージョンが指定したものであるか、確認してください。

http://localhost
Ruby_on_Rails.jpg

あとがき

うまくWelcomeページが表示されましたでしょうか?

参考記事を見ながら構築していったのですが、Rails6用に書かれていなかったため、かなり苦労しました…。(Rails学ぶことが目的だったので、Rails5でよかったですね…)

Rails6に対応したあとはバージョンの記載を一元管理したり、色々なサイトで書かれていることを網羅して今回の構築ファイルたちができあがりました。
とりあえずはこの環境でRails勉強していきます!

もし間違い・ご指摘などあればぜひお願いします。
今度はRails関連の記事でお会いしましょう!(多分)

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

ローカル環境をなるべく汚さずにNginxでBasic認証

TL;DR

Dockerのhttpdイメージを利用してhtpasswdコマンドを実行し、得られた行をコピペしましょう

やり方詳細

Basic認証をするには、.htpasswdファイルを作成する必要があります。しかし、Nginxにはそのためのツールがついてきません。
かといって、そのためだけにApacheやそれ由来のツールをインストールするのは癪ですし、

一時的に特定のツールが使いたいということなので、Dockerの出番です。Apacheのイメージ、httpdを利用します。これにはhtpasswdコマンドがついてきます。次のようなコマンドで起動します。--rmをつけることで、セッションを閉じるとコンテナが破棄されるようになります。

docker run -it --rm httpd bash

セッションが起動したら、お待ちかねhtpasswdコマンドを実行します。コマンド1回毎に生成されるのは1行なので、-nオプションをつけて標準出力に吐かせます。下のコマンドのどちらかを実行します。違いはパスワードを後から入力するか、引数として最初から渡すかの違いです。

htpasswd -n <ユーザ名>
htpasswd -nb <ユーザ名> <パスワード>
<ユーザ名>:<ハッシュ値>

のような行が出るのでコピーし、テキストエディタに貼り付けて保存します。Nginxでは.htpasswd以外の名前でも構いません。
また、最後の空行は無視して構いません。複数ユーザの認証を行う際には、

<ユーザ名1>:<ハッシュ値>
<ユーザ名2>:<ハッシュ値>
<ユーザ名3>:<ハッシュ値>

のように並べます。少なくともUbuntu 18.04付属のNginxでは強固なBCrypt(-Bオプション)が使用不可なので注意してください。

後はnginx.confや各サイトの設定ファイルのlocation節内に以下の2行

auth_basic "<Basic認証時のメッセージ>"
auth_basic_user_file "<.htpasswdファイルのパス>"

を追加します。Nginxのリロード(nginx -s reload or systemctl reload nginx)もお忘れなく。

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

jupyterをdocker-composeで起動2

はじめに

前回は、jupyterのdockerイメージ(scipy-notebook)をベースにjupyter pluginも入れて、
docker-composeを使ってイメージの作成と起動しました。

今回は、dockerのpythonイメージをベースにjupyterとdjangoをインストールして、jupyterとdjangoのサーバを起動することを確認しました。

ソースは github にあげてあります。

ディレクトリ構成

フォルダはdockerの配下にmainのDockerfileとjupyterのdockerファイルが存在し、
jupyterのdockerファイルはmainに依存して作成されます。
Pipfileとmain_projectはmain元となるプロジェクトになります。

└── docker
    ├── jupyter
        ├── .jupyter
            └── jupyter_notebook_config.py
        ├── Dockerfile
        └── docker-compose.yml
    └── main
        ├── Dockerfile
        └── docker-compose.yml
└── main_project/*
└── Pipfile

ファイル

docker/main/docker-compose.yml

buildするとmain-project という名前でイメージを作成します。

docker-compose.yml
version: '3'  
services:  
  main:
    image: main-project:1.0.0
    build:
      context: ../../
      dockerfile: ./docker/main/Dockerfile
    ports:  
      - "8000:8000"  
    volumes:  
      - ../../main_project:/usr/src/app/main_project

docker/main/Dockerfile

元となるプロジェクトのdockerイメージのコードです。

# jupyter lab      : 2.1.3
FROM python:3.6.8

WORKDIR /usr/src/app

RUN pip install --upgrade pip && pip install pipenv
COPY ./Pipfile /usr/src/app/Pipfile
RUN pipenv install --system --skip-lock

CMD python main_project/manage.py runserver 0.0.0.0:8000

EXPOSE 8000

docker/jupyter/docker-compose.yml

docker-compose.yml
version: '3'  
services:  
  jupyter:  
    image: main-project-jupyter:1.0.0
    build:
      context: .
    ports:  
      - "8888:8888"  
      - "8000:8000"  
    volumes:  
      - ./.jupyter:/root/.jupyter
      - ./notebook:/usr/src/app/notebook
      - ../../main_project:/usr/src/app/main_project

docker/jupyter/Dockerfile

上記で作成したmain-projectに依存してdockerイメージのコードを作成していきます。
djangoとjupyterの両方を起動します。

FROM main-project:1.0.0

WORKDIR /usr/src/app

COPY .jupyter /usr/src/app/.jupyter

# jupyter
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
RUN apt-get install -y nodejs npm
RUN pip install jupyterlab
RUN jupyter labextension install \
  @lckr/jupyterlab_variableinspector \
  @krassowski/jupyterlab-lsp \
  @axlair/jupyterlab_vim
RUN pip install jupyter-lsp python-language-server[all]
RUN jupyter lab build

# library
RUN pip install pandas lxml html5lib beautifulsoup4 matplotlib

CMD python main_project/manage.py runserver 0.0.0.0:8000 & jupyter lab --allow-root

EXPOSE 8888
EXPOSE 8000

docker/jupyter/jupyter_notebook_config.py

c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.notebook_dir = './notebook'
c.NotebookApp.open_browser = False
c.NotebookApp.token = ''

起動

$ cd python-jupyter-docker/docker/main
$ docker-compose build
$ cd python-jupyter-docker/docker/jupyter
$ docker-compose up -d

アクセス

jupyter
http://127.0.0.1:8888
django
http://127.0.0.1:8000

終わりに

既存のアプリにjupyterをくっつけるみたいな使い方をしました。
dev環境にjupyterを置くことで既存のアプリのライブラリを使いつつ使い捨てのコードを作成できるのではと思います。

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

PG::ObjectInUse: ERROR: database "myapp_development" is being accessed by other users

ポートフォリオ作成中

db/seeds.rb
User.create!(name: "テスト 太郎",
             email: "test@example.com",
             password: "foobar",
             password_confirmation: "foobar"
)

99.times do |n|
  name = Faker::Name.name
  email = "test#{n}@example.com"
  password = "foobar"
  User.create!(name: name,
               email: email,
               password: password,
               password_confirmation: password)
end

ページネーションを実装していくため
テスト用アカウントを作成しようとすると、
こんなエラーが出ました。

$ rails db:migrate:reset

を実行すると

PG::ObjectInUse: ERROR:  database "myapp_development" is being accessed by other users
DETAIL:  There is 1 other session using the database.

「myapp_debelopment」データベースは、他のセッションで使われているとのこと。

データベースはこのような構成

config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

色々と調べていく中で
下記を参考にして、データベースを一度削除して
作成し直すことにしました。
https://stackoverflow.com/questions/46451472/unable-to-make-rake-dbdrop-work-in-development-with-rails-5-1-4

$ docker exec -it (コンテナ名) /bin/bash

コンテナの中に入ります。

$ rails db:drop
Dropped database 'myapp_development'
Dropped database 'myapp_test'
$ rails db:create
$ rails db:migrate
== 20200704040832 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0426s
== 20200704040832 CreateUsers: migrated (0.0432s) =============================

== 20200704043311 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
   -> 0.0303s
== 20200704043311 AddIndexToUsersEmail: migrated (0.0307s) ====================

== 20200704043511 AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
   -> 0.0026s
== 20200704043511 AddPasswordDigestToUsers: migrated (0.0031s) ================

== 20200705055948 AddRememberDigestToUsers: migrating =========================
-- add_column(:users, :remember_digest, :string)
   -> 0.0025s
== 20200705055948 AddRememberDigestToUsers: migrated (0.0027s) ================
$ rails db:seed
$ exit

これで、無事動きました!

おそらく、
最初にデータベースを作成した際に
コンテナに入って作成するのではなく、
ローカル環境から下記コマンドで実行したから?
作成したユーザーが異なってしまったのではないかと。。

$ docekr-compose run web rails db:create
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure VMをVS Code Remote Containers開発環境として使う

はじめに

最近はもっぱらVS Code Remote Containersを開発環境として使っています。が、のっぴきならない事情でローカルマシンにDockerが入れられず使えない、という場合もあると思います1

そういう場合に、WindowsのAzure Virtual MachinesにHyper-VとDocker Desktop for Windowsをインストールし、開発環境として使ってみたいと思います。

構成は以下の通りです。ローカルPCはRDP接続元としてしか使わず、Azure上のWindows 10上ですべて開発をする構成です。
image.png

※開発環境自体はリモートにあったとしても、VSCode Remote Containersを使用するにはローカルにもDockerを入れる必要があります

Azure VMでDockerは動くのか?

Azureの一部のVMは、「ネストされた仮想化」をサポートしています。
これにより、Azure VM上でHyper-Vを有効化しDocker Desktop for Windowsを動かすことが可能です。

参考:Nested Virtualization in Azure | Azure Blog and Updates | Microsoft Azure

ちなみに、AWSはおそらく不可です。Hyper-Vを有効にできませんし、そもそもWindows 10のVMを作成できません。Windows ServerにはDocker Desktop for Windowsをインストールできないので、VS Code Remote Containersが使えません。

Windows10 VMの作成方法

Azureポータルから以下の順で選択していきます。

image.png

image.png

image.png

Windows 10のイメージを選択してください。

また、サイズは以下の***が付いているものから選択してください。
参考:Azure コンピューティング ユニットの概要 - Azure Virtual Machines | Microsoft Docs

注意点

ネストされた仮想化未対応のVMでは、Docker Desktop for Windowsが以下のようなエラーを吐いて起動できません。

Docker.Core.HttpBadResponseException:
Unhandled exception: シーケンスに要素が含まれていません

対応VMは結構お高くつくので、私は毎日定時に自動でシャットダウン+グローバルIP固定設定にしています。

試してみる

作成したVMにVS CodeとDocker Desktop for Windowsをインストールし、諸々の設定を行います。

詳細は以下を参照お願いします。
参考:Developing inside a Container using Visual Studio Code Remote Development

image.png

無事VS Codeでコンテナに入ることができました。

おわりに

この結論に至るまでに色々と試行錯誤したので記しました。VS Code Remote Containersは本当に最高なので使ってみてください。


  1. そんなことない?あるんです。 

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

jupyterをdocker-composeで起動1

はじめに

今回は、jupyterのdockerイメージ(scipy-notebook)をベースにjupyter pluginも入れて、
docker-composeを使ってイメージの作成と起動しました。

ソースは github にあげてあります。

ファイル

docker-compose.yml

docker-compose.yml
version: '3'  
services:  
  jupyter:  
    build:
      context: .
    environment:  
      JUPYTER_ENABLE_LAB: "yes" 
    ports:  
      - "8888:8888"  
    volumes:  
      - ./.jupyter:/home/jovyan/.jupyter
      - ./notebook:/home/jovyan/notebook

Dockerfile

# jupyter lab      : 2.1.3
FROM jupyter/scipy-notebook:54462805efcb

RUN jupyter labextension install \
  @lckr/jupyterlab_variableinspector \
  @krassowski/jupyterlab-lsp \
  @axlair/jupyterlab_vim

# pd.read_html
RUN pip install lxml html5lib beautifulsoup4

# code completion
RUN pip install jupyter-lsp python-language-server[all]

RUN jupyter lab build

jupyter_notebook_config.py

jupyter_notebook_config.py
c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.notebook_dir = './notebook'
c.NotebookApp.open_browser = False
c.NotebookApp.token = ''

アクセス

http://127.0.0.1:8888

終わりに

jupyterですぐ試してみたい場合などで便利でした。
jupyter plugin周りは全般にメンテナンスがされていない感じがして、
かなり地雷がありました。

次回はdockerのイメージをpythonにしてjupyter lab環境を構築していきたいと思います。

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

Docker上で、vcgencmdを使う

raspberry Piのクロックや電圧などを取得できるコマンドvcgencmdをDocker上で使用する方法を解説します。

Dockerのイメージはresin/rpi-raspbianを使用します。
以下のコマンドでDockerを立ち上げます。

docker run -it --device /dev/vchiq--rm resin/rpi-raspbian /bin/bash

立ち上げ後のコンテナ内に、vcgencmdコマンドがないため、以下のコマンドでインストールをします。

apt-get update && apt-get install libraspberrypi-bin -y \
    --no-install-recommends && apt-get clean && rm -rf /var/lib/apt/lists/*

後は、vcgencmd measure_tempコマンドでコンテナ上で、
RaspberryPi本体上の温度センサー情報を得ることができることを確認してください。

上記に気を付けてDockerfileを作れば、繰り返す必要がなくなります。

参考

vcgencmd
https://www.raspberrypi.org/documentation/raspbian/applications/vcgencmd.md
Docker Raspberry Pi Status
https://github.com/toschoch/docker-rpi-status

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

WindowsでDockerにてmongoDB環境を構築する囚われた人の備忘録

概要

以下のようにやったら、mongoDBが動かなかったため、その備忘録。

version: "2"
services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: root
    volumes:
      - ./db:/data/db
  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: root

対策

ボリュームをホストであるWIndowsのディレクトリにマウントしようとしていたのが問題だったようです。ボリュームを仮想環境側のボリュームにマウントしたら上手くいきました。そこからまたマウントしたらホスト側にマウントしたらいいんですかね。そこら辺は試してないので、分からないですが。

version: "2"
services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: root
    volumes:
      - mongodata:/data/db # ここに注目
  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: root
volumes: # ここに注目
  mongodata:

参考

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

LAMP+CakePHP3開発環境をVSCodeRemote+Dockerで構築② -VSCodeRemote編-

LAMP+CakePHP3開発環境をVSCodeRemote+Dockerで構築① -Docker編-からの続きです。

VSCodeでPHPの開発をするにあたり、XDebugやPHP Intellisenseなどの拡張を入れようとするとVSCodeのインストールされている自PCにもPHPをインストールする必要がありますが、VSCode Remote Developmentを利用するとDockerのコンテナ内で全て完結できました。

導入の際に問題が起きてかなりはまってしまったので、その際の対応を記載しています。

完成した環境はGitHubにアップロードしてあります。

VSCode Remote Developmentの導入

Docker関連のファイルは前回作成したものをそのまま使用しています。
以下の記事を参考にして導入してみます。
Visual Studio CodeのRemote DevelopmentとDockerで快適な開発環境をゲット

すると、コンテナの起動とVSCodeからコンテナへの接続は上手くいきます。

が、ブラウザからhttp://localhost:8093/ へアクセスすると「ERR_EMPTY_RESPONSE」と表示され接続できません。

VSCodeRemoteをコンテナに接続するとコンテナ内のサーバーへアクセスできなくなる問題

調査

ターミナル
curl http://localhost:8093

エラー
curl: (52) Empty reply from server

コンテナ内で以下のコマンドを実行すると

ターミナル
curl http://localhost:80

エラー
curl: (7) Failed to connect to ::1: Cannot assign requested address

となりこちらもエラーが出ます。
localhostを127.0.0.1に置き換えても同様です。

以下の記事達を参考にし、どうもコンテナは起動しているがサーバーが起動していない状態にありそうだという事がわかりますが、解決しません。

Dockerコンテナで起動したサーバにアクセスできないときの確認と対処方法

docker上のアプリにlocalhostでアクセスしたらERR_EMPTY_RESPONSEが出る

VSCodeRemoteで接続した際にのみアクセスできなくなるので、そちらに原因がありそうだと当たりをつけて公式ドキュメントを読んでみます。

Visual Studio Code | Developing inside a Container

公式ドキュメントを読んでみて大事そうなところ

・VSCodeRemoteでコンテナに接続した場合、コンテナを開いたrootディレクトリに対してworkspaceという名前のvolumeが作られる。
・rootディレクトリは設定で変更可能

・rootディレクトリ直下にdevcontainerフォルダが作られる。

フォルダ構成は以下の様な形

rootディレクトリ
devcontainer
 - devcontainer.json
 - docker-compose.yml(VSCodeによって生成された、設定上書き用ファイル)
...
他のファイルやフォルダ達
docker-compose.yml(コンテナ生成の元になっている自分で用意したファイル)

・ このうちdevcontainer.jsonには以下の設定項目があり、上から指定されている順にdocker-compose.ymlが読み込まれ、上書きされていく。

devcontainer.json
    "dockerComposeFile": [
        "../docker-compose.yml",
        "docker-compose.yml"
    ],

以上を踏まえて問題となっていた箇所

自動生成されたdocker-compose.ymlには、以下の設定がされています。
これは、コンテナがすぐに終了してしまわないようにするため設定されている様です。

docker-compose.yml(VSCodeによって生成された、設定上書き用ファイル)
    # Overrides default command so things don't shut down after the process ends.
    command: /bin/sh -c "while sleep 1000; do :; done"

また、用意してあるDockerFileでは、サーバーを自動起動するために以下のコマンドを実行しています。

docker/app/DockerFile
CMD ["/usr/sbin/httpd","-D","FOREGROUND"]

Docker側の仕様として、docker-compose.ymlにcommandが記載してあるとDockerFileで記載したCMDを上書きするらしく、このCMDが実行されていないためサーバーが立ち上がらずに問題が起きていたようです。

以下の様に修正したところ、問題なくコンテナのサーバーにアクセスできました。

docker-compose.yml(VSCodeによって生成された、設定上書き用ファイル)
    # Overrides default command so things don't shut down after the process ends.
    command: /bin/sh -c "/usr/sbin/httpd -DFOREGROUND; while sleep 1000; do :; done"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LAMP+CakePHP3開発環境をVSCodeRemote+Dockerで構築① -Docker編-

学習の備忘録です。
自身の学習用に使用するLAMP+CakePHP3環境をDockerで構築しました。
PCが変わっても環境をそのまま移行できると作った環境が資産となり頭もPCもスッキリするので、その後VSCodeのRemote Developmentを組み合わせていい感じの開発環境を作りました。

その際のまとめと詰まった部分などを記載しています。

全2部です
LAMP+CakePHP3開発環境をVSCodeRemote+Dockerで構築① -Docker編-(本記事)
LAMP+CakePHP3開発環境をVSCodeRemote+Dockerで構築② -VSCodeRemote編-

事前準備

Dockerについての基礎知識を仕入れる

こちらの記事(https://girigiribauer.com/tech/20180205/) を参考にしてDockerを利用したLAMP環境の構築をとりあえずやってみました。

Linux Apache MySQL PHPに関してもほぼ知識がない状態だったので、必要となった部分を都度調べながら作業しました。

環境構築

何となくDockerを使えるようになった気になったところで、LAMP+CakePHP3環境を構築してみます。
PCはMacです。

以下は主にこちらの記事(https://qiita.com/km42428/items/df1d0a1eefddcf771dfa) を参考にしながら作業を進めました。

構築する環境の詳細

  • CentOS 7
  • Apache 2.4
  • MySQL 5.7
  • PHP 7.4
  • CakePHP 3.8

参考元の記事ではサーバーソフトにApacheではなくNginx+phpfpmを利用していたので、違いについて調べてみると、同時大量アクセスが想定される場合等にはNginx+phpfpmの構成が有利なようです。
参考:ApacheとNginxについて比較

今回は不要なためApacheを利用してみることにしました。

docker-composeファイルの用意

ファイルやフォルダ構成については以下にアップロードしてあります。
GitHub 【学習用】Dockerでcakephp3.8のチュートリアル環境を構築

docker-compose.yml
version: "3"
services:
    app:
        build:
            context: "docker/app/"
        ports:
          - 8093:80
        volumes:
          - "./data/sample_app:/sample_app"
# cakePHPのインストールフォルダを永続化しています
          - "./docker/app/httpd.conf:/etc/httpd/conf/httpd.conf"
# Apache設定ファイルのhttpd.confを永続化しています
        depends_on:
          - mysql
    mysql:
        build:
             context: "docker/mysql/"
        environment:
          - MYSQL_DATABASE=sampledb
          - MYSQL_HOST=localhost
          - MYSQL_USER=root
          - MYSQL_ROOT_PASSWORD=mypassword
        volumes:
          - "./data/db:/var/lib/mysql"
# mysqlのデータベースをを永続化しています
    phpmyadmin:
        image: phpmyadmin/phpmyadmin:latest
        ports:
          - 8094:80
        environment:
          - PMA_HOST=mysql
          - PMA_USER=root
          - PMA_PASSWORD=mypassword
        depends_on:
          - mysql

参考元の記事ではCakePHPプロジェクトのコンテナとしてhostコンテナを作成していましたが、こちらではApacheのモジュールとしてPHPをインストールしているため、appコンテナがApache+PHP(モジュール)+CakePHPのコンテナとなります。

PHPのモジュール版・CGI版についての参考 (https://qiita.com/kotarella1110/items/634f6fafeb33ae0f51dc)

データの保存について

コンテナはおそらく頻繁に終了させると思われるので、Volumeを設定して必要なデータが破棄されないで自PC(ホスト)に保存される形にしています。

その他のファイルの用意

httpd.confのDocumentRootをCakePHPの公開フォルダに設定していますが、このままだとCakePHPインストール前のコンテナ起動時にフォルダが無い為コンテナが起動しません。
その為、CakePHPインストール前のみ、DocumentRoot及びDirectoryを"/var/www/html"に切り替えてください。
インストール後に元に戻してブラウザでhttp://localhost:8093/ にアクセスするとCakePHPの画面が表示されるはずです。

httpd.conf
# DocumentRoot "/var/www/html"
DocumentRoot "/sample_app/webroot"

#
# Relax access to content within /var/www.
#
# <Directory "/var/www">
<Directory "/sample_app/webroot">
    AllowOverride None
    # Allow open access:
    Require all granted
</Directory>

# Further relax access to the default document root:
# <Directory "/var/www/html">
<Directory "/sample_app/webroot">

CakePHP3のインストール

公式サイトのインストール手順に従い、Composerをインストールします。

以下のコマンドでappコンテナに入り、

docker exec -it tutorial_app_1 /bin/bash

Composerのインストールコマンドを実行します。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

公式手順に従い、インストールを進めます。

mv composer.phar /usr/local/bin/composer
php composer.phar create-project --prefer-dist cakephp/app:^3.8 sample_app

このままだとcomposer.pharなんてないよ?と言われたので以下に直して実行します。

composer create-project --prefer-dist cakephp/app:^3.8 sample_app

これで完了! なはずですが、エラーが出たので対応します・・・。
GitHubのデータは修正済みのものです。

エラー対応

*エラー1 zip unzipの不備

*エラー1
Failed to download cakephp/app from dist: The zip extension and unzip command are both missing, skipping.

zip unzipが必要なようなので、インストール用コマンドを追加しました。

*エラー2 php intl拡張モジュールの不備

*エラー2
 Problem 1
    - cakephp/cakephp 3.8.9 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.8 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.7 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.6 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.5 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.4 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.3 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.2 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.10 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.1 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - cakephp/cakephp 3.8.0 requires ext-intl * -> the requested PHP extension intl is missing from your system.
    - Installation request for cakephp/cakephp 3.8.* -> satisfiable by cakephp/cakephp[3.8.0, 3.8.1, 3.8.10, 3.8.2, 3.8.3, 3.8.4, 3.8.5, 3.8.6, 3.8.7, 3.8.8, 3.8.9].

  To enable extensions, verify that they are enabled in your .ini files:
    - /etc/php.ini
    - /etc/php.d/20-bz2.ini
    - /etc/php.d/20-calendar.ini
    - /etc/php.d/20-ctype.ini
    - /etc/php.d/20-curl.ini
    - /etc/php.d/20-dom.ini
    - /etc/php.d/20-exif.ini
    - /etc/php.d/20-fileinfo.ini
    - /etc/php.d/20-ftp.ini
    - /etc/php.d/20-gd.ini
    - /etc/php.d/20-gettext.ini
    - /etc/php.d/20-iconv.ini
    - /etc/php.d/20-json.ini
    - /etc/php.d/20-mbstring.ini
    - /etc/php.d/20-mysqlnd.ini
    - /etc/php.d/20-pdo.ini
    - /etc/php.d/20-phar.ini
    - /etc/php.d/20-simplexml.ini
    - /etc/php.d/20-sockets.ini
    - /etc/php.d/20-sodium.ini
    - /etc/php.d/20-sqlite3.ini
    - /etc/php.d/20-tokenizer.ini
    - /etc/php.d/20-xml.ini
    - /etc/php.d/20-xmlwriter.ini
    - /etc/php.d/20-xsl.ini
    - /etc/php.d/30-mysqli.ini
    - /etc/php.d/30-pdo_mysql.ini
    - /etc/php.d/30-pdo_sqlite.ini
    - /etc/php.d/30-xmlreader.ini
    - /etc/php.d/30-xmlrpc.ini
    - /etc/php.d/40-apcu.ini
  You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

cakephp3ではphpのintl拡張が必要とインストールマニュアルに記載があったのですが、phpの拡張モジュールについて理解できていなかった為インストール方法が分からずとりあえず進めましたが案の定エラーになりました。

PHP intl拡張モジュールのインストール

PHPマニュアル|intlインストール手順を見てもちょっとよく分からなかったので色々調べたところ、今回はremiリポジトリからphpをインストールしているので、同じくremiリポジトリからphp-intlをインストールすればいいだけでした。

なお拡張モジュールを有効化するにはインストールと共にphp.iniファイルに以下の記載が必要です。インストール後デフォルトで有効化されていました。

extension=モジュール名

# intlモジュールの場合
extension=intl

# LinuxOSの場合モジュールファイルの拡張子が.soになるので多くの場合以下のように記載されていますが、インストール後のデフォルトでは拡張子無しになっていて、拡張子無しのまま動作しました。
extension=intl.so

iniファイルについては以下のページが参考になりました。
【PHP】php.iniには設定を書かないようにしよう!

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

Keycloak コンテナがめっちゃ便利だったのでついでに https 化もした

関連記事
【Keycloak】Apache の VirtualHost で分けられた複数のサイトをまとめてシングルサインオンしよう
https://qiita.com/thirdpenguin/items/1136c755560eea51b5b1


公式にリリースされているコンテナイメージによる Keycloak 導入があまりにも簡単すぎて感動したので、ついでに Keycloak コンテナの https 化もやってみました。

github リポジトリにやり方がありました。
https://github.com/keycloak/keycloak-containers/tree/master/server#setting-up-tlsssl

上記を要約すると、「/etc/x509/https に 秘密鍵 tls.key と 証明書 tls.crt をくれると後はよろしくやるよ!でもボリュームマウントで置くとファイルのオーナーが root になるから、適切な所有者が読み取り可能になるようイメージを直してね!」とのことです。ボリュームマウントによるオーナーの変更は、 root 以外のユーザで Keycloak を動かす時には留意する必要がありそうですね。

ではやっていきましょう。 1 章では Keycloak の導入から始めるので、もう Keycloak コンテナの準備が終わっている方は 2 章からどうぞ。

バージョン情報

  • Keycloak サーバ (IdP)
  • OS: CentOS8 (8.1.1911)
  • podman: 1.6.4
  • podman-compose: 0.1.7.dev0

1. Keycloak コンテナの準備

公式 github リポジトリ keycloak-containers の docker-compose ファイルのサンプルを podman-compose に流用して、Keycloak コンテナと、連携する DB となる PostgreSQL コンテナを一気に建てます。
関連記事と同じ作業内容なので、もう終わっている方は読み飛ばしてしまって結構です。

  1. podman のインストール
# dnf -y install podman

 
2. podman-compose のインストール
podman-compose のインストールに pip3 が必要なので、 pip3 を同梱している python3 系列をインストールもします。

# dnf install -y python36
# pip3 install https://github.com/containers/podman-compose/archive/devel.tar.gz

 
3. git リポジトリ keycloak-containers をクローン
適当なディレクトリをローカルリポジトリにして、keycloak-containers リポジトリを引っ張ってきます。

# mkdir localrepo
# cd localrepo
# git init
# git clone https://github.com/keycloak/keycloak-containers.git
# cd keycloak-containers
# ls
CONTRIBUTING.md  License.html  README.md  adapter-wildfly  docker-compose-examples  docs  gatekeeper  keycloak-init-container  openshift-examples  server  set-version.sh

 

2. HTTPS 化作業

  1. 証明書の設置

用意しておいた秘密鍵と証明書を任意のディレクトリに配置します。
参考記事に自己署名書の取得手順があります。

# mkdir certs
# cp /path/to/your.key certs/tls.key
# cp /path/to/your.crt certs/tls.crt

 
2. keycloak-postgres.yml の編集
さて、Keycloak コンテナの https 化のためには……
所定のディレクトリ /etc/x509/https に鍵と証明書を適切なパーミッションで設置する必要があるのでした。
yml へ1行追記するだけで楽なので 今回はバインドマウントでやってみます。

あとはポートフォワーディングの設定を変える必要もあるはずです。
http は 8080 で listen してましたが https はまた別のポートになるでしょう。
しかしはて、 https の listen ポートは一体どこへ。このイメージの dockerfile に手がかりがあるのでは?

github.com/keycloak/keycloak-containers/blob/master/server/Dockerfileより抜粋
EXPOSE 8080
EXPOSE 8443

ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]

CMD ["-b", "0.0.0.0"]

EXPOSE 8443 の記述があるのでおそらくこれっぽいです。
念のため起動ログを漁って裏も取ったのですが、その話は後にして、とりあえず yml ファイルを編集します。

# cd docker-compose-examples
# vi keycloak-postgres.yml
keycloak-postgres.yml
version: '3'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres
      volumes:
        - postgres_data:/var/lib/postgresql/data
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
  keycloak:
      image: quay.io/keycloak/keycloak:latest
      volumes: # ←追加
        - /<証明書の絶対パス>:/etc/x509/https # ←追加
      environment:
        DB_VENDOR: POSTGRES
        DB_ADDR: postgres
        DB_DATABASE: keycloak
        DB_USER: keycloak
        DB_SCHEMA: public
        DB_PASSWORD: password
        KEYCLOAK_USER: admin
        KEYCLOAK_PASSWORD: Pa55w0rd
        # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
        #JDBC_PARAMS: "ssl=true"
      ports:
        - 8080:8080
        - 8443:8443 # ←追加
      depends_on:
        - postgres

Keycloak コンテナにバインドマウントするボリュームの追加と、ポート 8443 のフォワーディング設定のため3行ファイルに書き加えました。
 
3. サービス起動

鍵と証明書の設置、そして yml ファイルの編集が終わったらいよいよ起動です。

# cd docker-compose-examples
# podman-compose -f keycloak-postgres.yml up -d
using podman version: podman version 1.6.4
podman pod create --name=docker-compose-examples --share net -p 8443:8443 -p 8080:8080
96f023c108904557f7c953cc1896271bbfec1e9526583d9300d47cde3b1889d5
0
podman volume inspect docker-compose-examples_postgres_data || podman volume create docker-compose-examples_postgres_data
podman run --name=docker-compose-examples_postgres_1 -d --pod=docker-compose-examples --label io.podman.compose.config-hash=123 --label io.podman.compose.project=docker-compose-examples --label io.podman.compose.version=0.0.1 --label com.docker.compose.container-number=1 --label com.docker.compose.service=postgres -e POSTGRES_DB=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=password -v docker-compose-examples_postgres_data:/var/lib/postgresql/data --add-host postgres:127.0.0.1 --add-host docker-compose-examples_postgres_1:127.0.0.1 --add-host keycloak:127.0.0.1 --add-host docker-compose-examples_keycloak_1:127.0.0.1 postgres
046eec4c0a2619de953e35bd2940a00f43ac95ff87df955a55c07fe831492a23
0
podman run --name=docker-compose-examples_keycloak_1 -d --pod=docker-compose-examples --label io.podman.compose.config-hash=123 --label io.podman.compose.project=docker-compose-examples --label io.podman.compose.version=0.0.1 --label com.docker.compose.container-number=1 --label com.docker.compose.service=keycloak -e DB_VENDOR=POSTGRES -e DB_ADDR=postgres -e DB_DATABASE=keycloak -e DB_USER=keycloak -e DB_SCHEMA=public -e DB_PASSWORD=password -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=Pa55w0rd --add-host postgres:127.0.0.1 --add-host docker-compose-examples_postgres_1:127.0.0.1 --add-host keycloak:127.0.0.1 --add-host docker-compose-examples_keycloak_1:127.0.0.1 quay.io/keycloak/keycloak:latest
8241eb401a36880c0d3850a2230ae7c15510b9eedfe20478c6357972c597da6a
0

ポッド作成、ボリューム作成、コンテナ作成のコマンドが実行され、正常に終われば上記のように終了コード 0 が 3 回返ります。
失敗したら、podman-compose -f keycloak-postgres.yml down で一度ポッドとコンテナを削除し、問題を修正した後にもう一度実行してください。

3. 接続テスト

ブラウザから https://:8443 に接続して Keycloak の管理コンソールに接続できれば作業終了です!おつかれさまでした。自己証明書だとこの画面にたどり着く前に証明書関連のエラーが出ると思います。
image.png

余談

本当に 8443 を https の listen ポートとして使用して良かったのだろうか、ということで Keycloak コンテナの起動ログを漁ってみました。

inspect コマンドの LogPath 行を見て、ログファイルの保存場所を確認して……

# podman container inspect  docker-compose-examples_keycloak_1 | grep LogPath
        "LogPath": "/var/lib/containers/storage/overlay-containers/b9e422a609e4def892dc6d64688834585fb43a98ad63b3fa1ce9126cc3792033/userdata/ctr.log",

それっぽいワードで検索してみます。

# grep HTTP /var/lib/containers/storage/overlay-containers/b9e422a609e4def892dc6d64688834585fb43a98ad63b3fa1ce9126cc3792033/userdata/ctr.log
2020-07-01T01:46:56.731772928-04:00 stdout F 05:46:56,725 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0006: Undertow HTTP listener default listening on 0.0.0.0:8080
2020-07-01T01:46:57.459785836-04:00 stdout F 05:46:57,458 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-2) WFLYUT0006: Undertow HTTPS listener https listening on 0.0.0.0:8443

Undertow という何かが 8443 を listen してますね。Undertow ってなんでしょう?

Undertow is a flexible performant web server written in java, providing both blocking and non-blocking API’s based on NIO.
http://undertow.io/

なるほど、どうやら Java EE アプリケーションサーバである Wildfly や Jboss の拡張機能として動作する web サーバみたいです。コンテナ構築だと特に意識することはありませんでしたが、Keycloak は Wildfly 上で動作する Java アプリケーションです。ブラウザから指定の IP:Port へ接続したときに /auth に自動で飛ばされていたのは Undertow の働きだったわけですね。

参考

自己署名証明書の作成の際に参考にさせていただきました。ありがとうございます。

オレだよオレオレ認証局で証明書つくる
https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6
Apache httpdで作るHTTPSサーバ
https://qiita.com/jinnai73/items/638dcc1434d47b12e6ba

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

初めてのKubernetes - 2.Kubernetesのインストール

はじめに

それでは、前回構築した Raspberry Pi 3 Model B にOS、Kubernetesをインストールします。ただ、はじめに一言謝らないといけないことがあります。

Raspberry Pi 3 Model B でKubernetesのコントロールプレーンノードを動かしてみたのですが、性能不足のためかうまく動きませんでした。
ですので、Raspberry Pi 3 Model B x 4台をワーカーノードにして、Raspberry Pi 4 Model B(4GB) x 1台をコントロールプレーンノードにしました。

こんな感じで1台追加しました。
このRaspberry Pi 4 Model Bをコントロールプレーンノードにすることにします。

raspi4-master.jpeg

OSのインストール

Raspberry Pi だとRaspberry Pi OS(以前はRaspbian)を最初に考えますが、kubeadmのインストールを見てみると、Raspberry Pi OSはサポートされていません。したがって、今回はUbuntu 20.04-LTS(64bit)をインストールすることにします。

実は、Raspberry Pi OSでもKubernetesをインストールできますし、Kubernetesも動作するのですが、トラブルに遭遇したときに同じような経験をした人が少なくて、面倒なことになりそうなので、Ubuntuにします。

Install Ubuntu Server on a Raspberry Pi 2,3 or 4にRaspberry Piのイメージがあるので、
Ubuntu 20.04 LTS(64-bit) for Raspbeery Pi 3
をダウンロードします。

SDカードの準備

OSイメージのダウンロードが完了したら、解凍しmicroSDカードに書き込みます。

~/Downloads ❯❯❯ xz -dv ubuntu-20.04-preinstalled-server-arm64+raspi.img.xz
ubuntu-20.04-preinstalled-server-arm64+raspi.img.xz (1/1)
  100 %     667.0 MiB / 3,054.4 MiB = 0.218    92 MiB/s       0:33

OSイメージの書き込みには、こちらにもある通りに、Raspberry Pi imager というツールがあるそうですが、私のノートPCはUbuntuなので、ddコマンドでも書き込めます。

~/Downloads ❯❯❯ sudo dd if=ubuntu-20.04-preinstalled-server-arm64+raspi.img of=/dev/mmcblk0 status=progress
3201147392 bytes (3.2 GB, 3.0 GiB) copied, 622 s, 5.1 MB/s
6255474+0 records in
6255474+0 records out
3202802688 bytes (3.2 GB, 3.0 GiB) copied, 627.603 s, 5.1 MB/s

この作業をmicroSDカード5枚分(ControlPlane x 1, Node x 4)行います。

設定

ここから設定を行っていくわけですが、ディスプレイ+キーボード+マウスを5台のRaspberry Piに接続して作業するのは、少々面倒です。したがって、それら周辺機器を接続せずに設定していきます。
実は、その方法はHow to install Ubuntu on your Raspberry Piに記載されているので、簡単です。また、こちらも参考になります。

OSイメージを書き込んだmicroSDカードを抜き差しすると、以下のパーティションが自動でマウントされます。

~/Downloads ❯❯❯ df -k
...
/dev/mmcblk0p2   2754000   1813920    780472  70% /media/$USER/writable
/dev/mmcblk0p1    258095     62017    196079  25% /media/$USER/system-boot

IPアドレスの設定をします。K8sクラスタのIPアドレスは以下のように設定します。

  • hostname: k8s-master,ip address: 192.168.0.19/24
  • hostname: k8s-node0, ip address: 192.168.0.10/24
  • hostname: k8s-node1, ip address: 192.168.0.11/24
  • hostname: k8s-node2, ip address: 192.168.0.12/24
  • hostname: k8s-node3, ip address: 192.168.0.13/24
~/Downloads ❯❯❯ cd /media/$USER/system-boot
/m/n/system-boot ❯❯❯ vim network-config
version: 2
ethernets:
  eth0:
    dhcp4: no
    addresses: [192.168.0.10/24]
    gateway4: 192.168.0.1
    nameservers:
      addresses: [192.168.0.1,8.8.8.8]
    optional: true

次にホスト名とユーザ・パスワードの設定をします。

/m/n/system-boot ❯❯❯ vim user-data
fqdn: k8s-node0
chpasswd:
  expire: false
  list:
  - ubuntu:ubuntu

次にRaspberry Pi 4 Ubuntu 19.10 cannot enable cgroup memory at boostrap
にあるようにDocker/K8sに必要なcgroup memoryを有効化します。

~ ❯❯❯ vim /media/$USER/system-boot/cmdline.txt
-net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc
+net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 rootwait fixrtc

以上の設定ができたら、microSDカードをアンマウントして、Raspberry Pi に挿入します。

~ ❯❯❯ umount /media/naomori/system-boot
~ ❯❯❯ umount /media/naomori/writable

同様に、K8s Cluster の 5台分(ControlPlane x 1, Node x 4)の設定をします。

ssh でログイン

Raspberry Piの電源を入れたらsshでログイン(user:ubuntu,pass:ubuntu)します。あと、SSHの秘密鍵のパスフレーズを無しにした公開鍵をK8s Cluster すべてにscpして、ログインしやすくしておきます。

~/Downloads ❯❯❯ ssh ubuntu@192.168.0.10
~/Downloads ❯❯❯ scp ~/.ssh/id_rsa.pub ubuntu@192.168.0.10:
ubuntu@k8s-node0:~$ cat id_rsa.pub >> .ssh/authorized_keys
ubuntu@k8s-node0:~$ rm -f id_rsa.pub

最初にすること

ubuntu@k8s-node0:~$ sudo apt update && sudo apt upgrade -y

あと、簡単にそれぞれのノードにアクセスできるように、/etc/hostsにエントリを追加します。

ubuntu@k8s-node0:~$ sudo vim /etc/hosts
# K8s
192.168.0.19 k8s-master
192.168.0.10 k8s-node0
192.168.0.11 k8s-node1
192.168.0.12 k8s-node2
192.168.0.13 k8s-node3

時間の設定もしておきます。

ubuntu@k8s-node0:~$ sudo timedatectl set-timezone Asia/Tokyo
ubuntu@k8s-node0:~$ timedatectl
               Local time: Mon 2020-07-06 22:13:48 JST
           Universal time: Mon 2020-07-06 13:13:48 UTC
                 RTC time: n/a
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Dockerのインストール

Install Docker Engine on Ubuntuを参考にDockerをインストールします。

ubuntu@k8s-node0:~$ sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
ubuntu@k8s-node0:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
OK
ubuntu@k8s-node0:~$ sudo apt-key fingerprint 0EBFCD88
pub   rsa4096 2017-02-22 [SCEA]
      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid           [ unknown] Docker Release (CE deb) <docker@docker.com>
sub   rsa4096 2017-02-22 [S]
ubuntu@k8s-node0:~$ sudo add-apt-repository \
    "deb [arch=arm64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
ubuntu@k8s-node0:~$ sudo apt update

ここにしたがって、Dockerのバージョンを指定してインストールします。

ubuntu@k8s-node0:~$ sudo apt install -y \
  containerd.io=1.2.13-2 \
  docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) \
  docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs)

kubernetesとのバージョン依存の問題があるため、docker-ceのバージョンを現在のバージョンで固定化します。

ubuntu@k8s-node0:~$ sudo apt-mark hold containerd.io docker-ce docker-ce-cli
containerd.io set on hold.
docker-ce set on hold.
docker-ce-cli set on hold.

Dockerの設定を変更します。

ubuntu@k8s-node0:~$ sudo vim /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
ubuntu@k8s-node0:~$ sudo mkdir -p /etc/systemd/system/docker.service.d
ubuntu@k8s-node0:~$ sudo systemctl daemon-reload
ubuntu@k8s-node0:~$ sudo systemctl restart docker
ubuntu@k8s-node0:~$ sudo usermod -aG docker ubuntu
ubuntu@k8s-node0:~$ sudo systemctl enable docker

以上の作業を5台分(ControlPlane x 1, Node x 4)します。

Kubernetesのインストール

kubeadmのインストールを参考にインストールします。

iptablesがnftablesバックエンドを使用しないようにする

ubuntu@k8s-node0:~$ sudo apt-get install -y iptables arptables ebtables
ubuntu@k8s-node0:~$ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
ubuntu@k8s-node0:~$ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
ubuntu@k8s-node0:~$ sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
ubuntu@k8s-node0:~$ sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

kubeadm のインストール

ubuntu@k8s-node0:~$ sudo apt-get update && sudo apt-get install -y apt-transport-https curl
ubuntu@k8s-node0:~$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
ubuntu@k8s-node0:~$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
ubuntu@k8s-node0:~$ sudo apt update
ubuntu@k8s-node0:~$ sudo apt install -y kubelet kubeadm kubectl
ubuntu@k8s-node0:~$ sudo apt-mark hold kubelet kubeadm kubectl
kubelet set on hold.
kubeadm set on hold.
kubectl set on hold.

swap 無効化

kubeletが正常に動作するためには、swapは必ずオフである必要があるとのことなので、swapを無効化しておきます。

ubuntu@k8s-node0:~$ sudo swapoff -a

以上の作業を5台分(ControlPlane x 1, Node x 4)します。

kubeadmを使用したシングルコントロールプレーンクラスターの作成

kubeadmを使用したシングルコントロールプレーンクラスターの作成を参考に、Kubernetesクラスタを作成していきます。ネットワークには、kube-routerを使います。

コントロールプレーンノードの初期化

Masterノードで以下を実行します。

ubuntu@k8s-master:~$ sudo kubeadm init --pod-network-cidr=10.1.0.0/16
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.0.19:6443 --token n1o5q2.coajyablijtdj1qb \
    --discovery-token-ca-cert-hash sha256:5e0856b519d1f97db37fa1742c4613f99ef3f9eafd4a7bbbf5a5d270145c81bb

完了すると、以下を実行しろと言われるので、その通りにします。

ubuntu@k8s-master:~$ mkdir -p $HOME/.kube
ubuntu@k8s-master:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
ubuntu@k8s-master:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Podネットワークアドオンのインストール

Podネットワークとして、kube-routerを使うことにします。Deploying kube-router with kubeadmを参考にします。

ubuntu@k8s-master:~$ KUBECONFIG=$HOME/.kube/config kubectl apply -f https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter.yaml
ubuntu@k8s-master:~$ kubectl get pod -n kube-system
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-66bff467f8-j8snh             1/1     Running   0          19m
coredns-66bff467f8-ntsxk             1/1     Running   0          19m
etcd-k8s-master                      1/1     Running   0          19m
kube-apiserver-k8s-master            1/1     Running   0          19m
kube-controller-manager-k8s-master   1/1     Running   0          19m
kube-proxy-87dh9                     1/1     Running   0          19m
kube-router-xlw62                    1/1     Running   0          17m
kube-scheduler-k8s-master            1/1     Running   0          19m

NodeのJoin

以下をk8s-node[0-3]で実行して、K8sクラスタに参加させます。

ubuntu@k8s-node0:~$ sudo kubeadm join 192.168.0.19:6443 --token n1o5q2.coajyablijtdj1qb --discovery-token-ca-cert-hash sha256:5e0856b519d1f97db37fa1742c4613f99ef3f9eafd4a7bbbf5a5d270145c81bb
...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

全部がRunningになっていることを確認します。

ubuntu@k8s-master:~$ kubectl get pod -n kube-system
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-66bff467f8-j8snh             1/1     Running   0          45m
coredns-66bff467f8-ntsxk             1/1     Running   0          45m
etcd-k8s-master                      1/1     Running   0          45m
kube-apiserver-k8s-master            1/1     Running   0          45m
kube-controller-manager-k8s-master   1/1     Running   0          45m
kube-proxy-4skhr                     1/1     Running   0          19m
kube-proxy-5b7k9                     1/1     Running   0          20m
kube-proxy-87dh9                     1/1     Running   0          45m
kube-proxy-n6w99                     1/1     Running   0          19m
kube-proxy-tw7cr                     1/1     Running   0          20m
kube-router-65lkl                    1/1     Running   0          20m
kube-router-np5z5                    1/1     Running   0          20m
kube-router-ntzkv                    1/1     Running   0          19m
kube-router-xlw62                    1/1     Running   0          43m
kube-router-zjhd6                    1/1     Running   0          19m
kube-scheduler-k8s-master            1/1     Running   0          45m

ノードの稼働状況を確認します。

ubuntu@k8s-master:~$ kubectl get nodes
NAME         STATUS   ROLES    AGE     VERSION
k8s-master   Ready    master   33m     v1.18.5
k8s-node0    Ready    <none>   8m10s   v1.18.5
k8s-node1    Ready    <none>   7m34s   v1.18.5
k8s-node2    Ready    <none>   7m37s   v1.18.5
k8s-node3    Ready    <none>   7m32s   v1.18.5

Rolesを設定します。

ubuntu@k8s-master:~$ kubectl label nodes k8s-node0 kubernetes.io/role=node
ubuntu@k8s-master:~$ kubectl label nodes k8s-node1 kubernetes.io/role=node
ubuntu@k8s-master:~$ kubectl label nodes k8s-node2 kubernetes.io/role=node
ubuntu@k8s-master:~$ kubectl label nodes k8s-node3 kubernetes.io/role=node
ubuntu@k8s-master:~$ kubectl get node
NAME         STATUS   ROLES    AGE   VERSION
k8s-master   Ready    master   85m   v1.18.5
k8s-node0    Ready    node     60m   v1.18.5
k8s-node1    Ready    node     60m   v1.18.5
k8s-node2    Ready    node     60m   v1.18.5
k8s-node3    Ready    node     60m   v1.18.5
ubuntu@k8s-master:~$ kubectl cluster-info
Kubernetes master is running at https://192.168.0.19:6443
KubeDNS is running at https://192.168.0.19:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

別ノード(Ubuntu 20.04 LTS)から kubectl で制御します

別のノード(Ubuntu 20.04 LTS)からkubectlを実行することもできます。Install and Set Up kubectlを参考にkubectlをインストールします。

~ ❯❯❯  sudo snap install kubectl --classic

masterノードで出力をコピーします。

ubuntu@k8s-master:~$ kubectl config view --raw

別ノードの~/.kube/configにペーストします。

~ ❯❯❯  vim ~/.kube/config

これで別ノードからでもkubectlを実行できます。また kubectl completion でコマンドの補完ができます。

まとめ

以上で、
Raspberry Pi 4 Model B x 1 をコントロールプレーンノードに、
Raspberry Pi 3 Model B x 4 をノードにした Kubernetes クラスタを構築できました。

次回からは、15Stepで習得 Dockerから入るKubernetes コンテナ開発からK8s本番運用までKubernetesの勉強をしていきたいと思います。まだ全てを読んだわけではないですが、アーキテクチャやコンポーネントの説明が理解しやすくて、とても良い本だと思います。

"初めてのKubernetes"リスト

  1. 初めてのKubernetes - 1.環境構築
  2. 初めてのKubernetes - 2.Kubernetesのインストール
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ubuntu20.04で docker pull したら 'Error response from daemon: Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io: Temporary failure in name resolution' と言われた

びっくりするほど単純だったのですが、焦って色々ググってしまったので解決法をメモしておきます

docker login

これだけですw
dockerhub にログインしないとそら動きませんわー!

新しい物理マシンを立てた直後に忘れませんように...

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

Dockerでテストの時だけDBを立ち上げるスクリプト

概要

CIでテストの時だけ一時的にDBを立ち上げ、テストが終わったら削除したいってことありませんか?
Dockerを使ってそれを行うスクリプトです。

スクリプト

#/bin/sh

CONTAINER_ID=`docker run --rm -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=test_db -e TZ=Asia/Tokyo -d mysql:5.6 mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci`
CONTAINER_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER_ID`

trap finally EXIT
function finally {
    docker stop $CONTAINER_ID
}

until docker run --rm busybox nc -z -v -w30 $CONTAINER_IP 3306
do
  echo "Waiting for database connection..."
  sleep 3
done

# ここでテストを実行する(DB名やパスワードなどは実行パラメータか環境変数で指定する)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[備忘録] [初心者] Docker Compose / Rails(公式doc.)について自分用補足#2 (docker-compose.yml)

はじめに

前回の投稿、
[備忘録] [初心者] Docker Compose / Rails(公式doc.)について自分用補足#1 (Dockerfile, entrypoint.sh)
に続き、Docker + Raislの公式チュートリアルの内容を丁寧に追って理解を試みる取り組みのアウトプットです。

あくまで学習過程の私的なアウトプットなので、信頼性の高い情報は、各リンク先を参照していただければと思います。

本編

クィックスタート: Compose と Rails | Docker ドキュメント
上記のチュートリアルで扱われる、docker-compose.ymlの理解のためのメモです。
一部、深掘りのために、PostgreSQL関連や、公式イメージのDockerfileの内容も扱います。

docker-compose.ymlの概観理解に役立ったリンク例

docker-compose.yml

docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

version:

使用するDocker Composeのバージョン

services:

#サービス設定リファレンス | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
アプリケーションを構成する各サービスのコンテナを設定します。
今回扱うチュートリアルでは、2つのサービスdb, webによってサービス全体を構成しています。

  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

db:

任意のサービスの名称として"db"が付けられています。
公式のサンプルでは、下位にimage, volumes, environmentの設定項目が続きます。

    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
image:
image: postgres

#image | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント

コンテナを起動させるイメージの設定とあります。
今回は、PostgreSQLのDocker公式イメージを用います。
デフォルトのコンテナOSは、Debian:stretch、オプションでAlpineも選択できるようです。

volumes:
volumes:
  - ./tmp/db:/var/lib/postgresql/data

#volumes | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント

マウントホストパスや名前つきボリュームを、サービスに対するサブオプションとして指定します。

まず、マウントという言葉もピンときていなかったのですが、下記の質疑がとても分かりやすかったです。
filesystems - understanding "mount" as a concept in the OS - Unix & Linux Stack Exchange
What is a mount point in Linux/Unix? - The Linux Juggernaut
特に、耕地の管理とディスクドライブの管理とのアナロジーを示した例え話はとても面白いと感じました。
各パーティション内のデータにアクセスするための"門"を設けることがマウントであるということに納得しました。

また、ボリュームの概念が分かっていないので調べました。
 ボリュームの利用 | Docker ドキュメント

ボリュームとは、Docker コンテナーにおいて生成され利用されるデータを、永続的に保持する目的で利用される仕組みです。

このように、システムを終了してもデータが消失しないような仕組みを設けることを永続化と呼ぶようです。(不揮発性メモリへの保存、回復可能にしておくなど)
 永続性 - Wikipedia
 What Is Persistent Data? - DZone Database

では、volumeに設定されている./tmp/db:/var/lib/postgresql/dataとはどういうディレクトリなのでしょうか。
ボリュームの利用 | Docker ドキュメントには、設定の書式として、[SOURCE:]TARGET[:MODE]という書式が適用されることが説明されています。
この書式にあてはめると、下記表の対応となります。

書式 記述例 意味
[SOURCE:] ./tmp/db: ホストのパスあるいはボリューム名
TARGET /var/lib/postgresql/data ボリュームがマウントされているコンテナのパス
[:MODE] none 読み込み専用ro, 読み書き可能rw. デフォルトは後者

つまり、コンテナから見たとき、/var/lib/postgresql/dataの中身として、ホストOS上の./tmp/dbを見ている状態になります。

これはこういうものだと、割り切ってしまうのもひとつかもしれません。
ただ、もう少し自分の中で具体的根拠を伴った意味付けが欲しいと思いました。

/var/lib/postgresql/dataとは何か

このディレクトリについては、PostgreSQLのリファレンスに説明がありました。

データベースの物理的な格納 - PostgreSQL 8.0.4 文書

データベースクラスタで必要となる全てのデータは、クラスタのデータディレクトリ内に格納され、通常 PGDATA として参照されます。 (そのディレクトリを定義するために使用できる環境変数名です。) 通常の PGDATA の位置は /var/lib/pgsql/data です。

データベースクラスタはPostgreSQL独自の用語らしいです。
 第18回 データベースクラスタ - OSS-DB道場
 sql - What's a PostgreSQL "Cluster" and how do I create one? - Stack Overflow

PostgreSQLでは、1つのサーバインスタンス上に複数のデータベースを構成することができ、PostgreSQLで言う"クラスタ"とは、そうした1つ以上のデータベースの集合体を含む共有ディレクトリを管理する仕組みであると解釈しました。
そして、それらに関わるデータが格納される場所がPGDATAの環境変数で参照され、その保存先が通常では/var/lib/pgsql/dataに指定してあると。

/var/lib/という階層の意味 : ファイルシステム階層標準 (FHS)

他でもよく見る/var/libについても意味を把握しておこうと思いました。
LinuxやUnix系のOSのディレクトリの階層は、ファイルシステム階層標準によって定められていることを知りました。
Filesystem Hierarchy Standard - Wikipedia(JP)

Filesystem Hierarchy Standard(ファイルシステム・ヒエラルキー・スタンダード、FHS、ファイルシステム階層標準)は、Linuxを含むUNIX系オペレーティングシステムでの主なディレクトリとその内容を定めたものである。

2020-07現在の最新版はFHS 3.0で、下記リンクからHTMLやPDFなどの形式で閲覧することが出来ます。

/varは、動的に変化する変数データファイルを格納するもので、、ネットワーク上で他のコンピュータと共有されないもの。
/var/libは、アプリケーションやシステムに関連する状態情報を保持。

FHSの解説では、アプリケーションは/var/libのサブディレクトリを使用しなければならない、とあるので、/var/lib/pgsqlはその規則に則ったPostgreSQL用のデータ格納先であることが伺えます。

PostgreSQLの公式イメージにおける/var/lib/postgresql/dataの扱い

PostgreSQLの公式DockerイメージDockerfileを見てみます。
下記の2行に/var/lib/postgresql/dataの記述を見つけました。(ディレクトリの生成も含めれば3行)
登場するDockerのENVVOLUMEの命令は、この親投稿では触れなかったものなので、調べてみます。

https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L181

postgres/Dockerfile-debian.template#L181
ENV PGDATA /var/lib/postgresql/data

#ENV | Dockerfile リファレンス | Docker ドキュメント

環境変数 <key><value> という値を設定します。

つまり環境変数PGDATA/var/lib/postgresql/dataを設定しています。
PostgreSQLの公式リファレンスでは、pgsqlというデフォルトのディレクトリ名が、Dockerの公式イメージでは postgresqlとなっています。

https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L184

postgres/Dockerfile-debian.template#L184
VOLUME /var/lib/postgresql/data

#VOLUME | Dockerfile リファレンス | Docker ドキュメント

VOLUME 命令は指定された名前を使ってマウントポイントを生成します。 そして自ホストまたは他のコンテナーからマウントされたボリュームとして、そのマウントポイントを扱います。

荒い解釈ではありますが、/var/lib/postgresql/dataをめぐる流れとしては、下記のように整理できると思いました。
 1. ホストOSのローカルに/var/lib/postgresql/dataを生成
  (postgres/Dockerfile-debian.template#L21)
 2. PostgreSQLがサービスのデータを参照するための環境変数PGDATA/var/lib/postgresql/dataを設定
  (postgres/Dockerfile-debian.template#L181)
 3. Composeの起動時にPostgreSQLのサービスが/var/lib/postgresql/dataをマウントできるようにマウントポイントを生成
  (postgres/Dockerfile-debian.template#L184)
 4. $ docker compose run ~でコンテナを起動したらマウントポイントの/var/lib/postgresql/dataをマウントしてサービス側から使用できるようにする
  (チュートリアルのdocker-compose.ymlのdb: volume:設定行)

environment:
environment:
  POSTGRES_PASSWORD: password

#environment | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
環境変数を追加します。

web:

任意のサービスの名称として"db"が付けられています。
公式のサンプルでは、下位にbuild, command, volumes, ports, depends_onの設定項目が続きます。

    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
build:
build: .

#build | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント

build の指定方法の 1 つは、ビルドコンテキストへのパスを表わす文字列を指定します

ビルドコンテキスト?
Docker Tips: All About the Build Context - Better Programming - Medium

The build context is the set of files located at the specified PATH or URL. Those files are sent to the Docker daemon during the build so it can use them in the filesystem of the image.

指定されたURLやPATHにあるファイル一式。これらのファイルはDockerのデーモンに送られビルド中にイメージのファイルシステムで使用できるようになる。
今回では、webサービスのイメージでbuildのPATHに.を指定しているので、 /var, /myappなどの全ての階層とファイルがwebサービスイメージのファイルシステムで使用できるようになっています。

command:
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

#command | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
コンテナ起動時にデフォルトで実行されるコマンド。

bash -c

bash(1): GNU Bourne-Again SHell - Linux man page
-cは後に続く文字列をコマンドとして実行するので、bash -c "<command>"はBashシェルでを実行する意味になります。

volumes:
volumes:
  - .:/myapp

再掲:#volumes | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
ホストOS上のパスである.を、コンテナ上の/myappへ専用のボリュームとしてマウントします。

ports:
ports:
  - "3000:3000"

#ports | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
公開するポートをHOST:CONTAINERで指定します。

depends_on:
depends_on:
  - db

#depends_on | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント
サービスの依存関係を指定します。複数ある場合は順番に起動。
ただ、依存サービスの「準備」状態を待たずに当サービスの起動を実行するため、ちゃんと依存関係にあるサービスの「準備」を待ちたいなら、別途コマンドにより待機させる工夫が必要です。


後書き

Docker + Raislの公式チュートリアルについては今回で一旦終了です。
DeepL頼みなことが多いですが、海外の記事やリファレンスを抵抗なく触れられるようになったことは、自身として少しは進歩しているのかなと感じます。

理解が荒い部分も多いので、このような調査とアウトプットを継続して、正しい理解に近づくように取り組んでいきます。

また、英語だと教育資料や初心者向けの教材なども簡単にヒットするので、様々な情報源から学びを得られるように独力で英文を読む能力も鍛えていきたいと思いました。

次は、CircleCIの設定ファイルについて学習します。

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

dockerでvolume mountしたファイルがpermission denied

やろうとしたこと

nginx(openresty)をdockerで起動し、ホストのディレクトリ(nginx.confなどが置いてある)をマウントしようとした。業務だったので、家でのように「とりあえずroot」とやるのは気が引けて色々調べた(結局今はrootになっているが)。

環境

CentOS 7.3
docker 1.13.1

困ったこと

マウント先のファイルの権限を正しく設定してもlsなどでpermission deniedがでて、ファイルが読めない。

結論

  • dockerコンテナ内でもuid, gidがホストと同じなら同一ユーザとみなされる
  • ただし、SELinuxを知らずにやると権限があるように見えてもpermission deniedされる(今回)

確認したこと

まずは権限が本当にあっているのか確認1
コンテナ内でuser1(ホストと合わせる)という一般ユーザを作り、user1で色々実行できればいいなと思って調べた。

Dockerfile

Dockerfile
FROM openrestyの何か

RUN useradd --disabled-password user1

# USER user1 を試したりも

確認パターン

ファイル所有者(ホスト側) コンテナ実行者 コンテナ内作業 結果 備考
user1 root マウント先ディレクトリでls Permission denied まぁわかる
root root マウント先ディレクトリでls Permission denied ホストとコンテナのrootは同じだから本来いけるはず
user1 user1 - nginx起動せず(コンテナexit) nginxのstart権限がないのかも
user1 root su user1してからマウント先ディレクトリでls Permission denied 同一ユーザだから2本来行けるはず

そのほかchmod 777 -R <ディレクトリ>も試しましたが同じですね。

今回の対応

コンテナ実行とかもろもろuser1でやるのは(そもそも起動もしないし)色々大変だろうと判断し3、とりあえずrootでやることに(まず動作確認したい)。
それでもうまくいかずに調べていたら、CentOSなどはデフォルトでSELinuxというものが有効になっており、これが関係しているとのこと。ホスト側でgetenforceコマンドの出力がEnforcingなら有効。有効だったのでこれを一時的に無効にするsudo setenforce 0を実行すると無事コンテナ内でマウント先のファイルが見られた。setenforce 0は一時的で、OS再起動するとリセットされる
そして当然ながらセキュリティ関連のものを脳筋で無効にするのはいいことではなく、docker公式がSELinuxとの併用を推奨しているとの噂もありました(一次ソースに当たらない人)。

所感

dockerで権限があってるのにマウント先見られなかったらSELinuxを知るとよい。
できればSELinux有効で正しい設定をして今回の問題を解決したいものである。


  1. ホスト側のデフォルトユーザがrootではないのに、あまり意識せずにファイルなど生成していたため。 

  2. id user1の結果がコンテナ内とホストで同じであることを確認。どちらも1人目の一般ユーザだからだと思うが、自動採番でともにuid, gid=1000。本当はユーザ作成の際に指定するべき。 

  3. 調べたら案の定nginxの実行ファイルの権限を付与するだけでいいらしい。 

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

Dockerのvolumeが使用中で消せないとき

Dcokerのvolumeが使用中で消せない

Docker-compose.ymlでvolumeを指定してデータの永続化をした際など、環境を作り直したくてvolumeも消去したいのに、消去できなかった事があったので覚書

無題1.png

  • Docker-compose down -v で消そうと思ったけどvolumeが使用中で消せない
  • docker volume rm -f 消したいvolume名 で消そうと思ったけどvolumeが使用中で消せない
  • docker volume prune で消そうと思ったけどvolumeが使用中で消せない
  • docker-compose ps やdocker psでも使用しているコンテナが見当たらない

↑見えていないコンテナがありそうな気配。

強制的にコンテナを削除してみたら、volumeを握っていると思われるコンテナも消えて、無事volumeの削除もできました。

docker container prune
docker volume rm -f 消したいvolume名

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

Dockerのvolumeが使用中で消せない時

使用環境

Windows10
Docker Desktop for Windows version 2.3.0.3

トラブル内容

Dcokerのvolumeが使用中で消せない

docker-compose.ymlでvolumeを指定してデータの永続化をした際など、環境を作り直したくてvolumeも消去したいのに、消去できなかった事があったので覚書

無題1.png

  • Docker-compose down -v で消そうと思ったけどvolumeが使用中で消せない
  • docker volume rm -f 消したいvolume名 で消そうと思ったけどvolumeが使用中で消せない
  • docker volume prune で消そうと思ったけどvolumeが使用中で消せない
  • docker-compose ps やdocker psでも使用しているコンテナが見当たらない

↑見えていないコンテナがありそうな気配

トラブル対処

強制的にコンテナを削除してみたら、volumeを握っていると思われるコンテナも消えて、無事volumeの削除もできました。

docker container prune
docker volume rm -f 消したいvolume名

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

DockerでSQLServerを建てる際にハマったのでメモ(永続化)

はじめに

Azureでサービスを作ることになり、ローカル環境にDockerでSQLServerを立てて永続化させる際に行ったことをメモとして残します。

普通に書いてみる

MySQLと同じ感覚で書いてみて起動してみる。

docker-compose.yaml
version: "3"
services:
  sqlserver:
    image: microsoft/mssql-server-linux:2017-latest
    container_name: mssql
    hostname: mssql
    volumes:
      - ./.db:/var/opt/mssql/data
    ports:
      - 14330:1433
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: SQLServer2017
〉 docker-compose up
Recreating mssql ... done
Attaching to mssql
mssql        | 2020-07-05 22:00:46.51 Server      Setup step is copying system data file 'C:\templatedata\master.mdf' to '/var/opt/mssql/data/master.mdf'.
2020-07-05 22:00:46.86 Server      Did not find an existing master data file /var/opt/mssql/data/master.mdf, copying the missing default master and other system database files. If you have moved the database location, but not moved the database files, startup may fail. To repair: shutdown SQL Server, move the master database to configured location, and restart.
2020-07-05 22:00:46.88 Server      Setup step is copying system data file 'C:\templatedata\mastlog.ldf' to '/var/opt/mssql/data/mastlog.ldf'.
2020-07-05 22:00:46.94 Server      Setup step is copying system data file 'C:\templatedata\model.mdf' to '/var/opt/mssql/data/model.mdf'.
2020-07-05 22:00:47.53 Server      Setup step is copying system data file 'C:\templatedata\modellog.ldf' to '/var/opt/mssql/data/modellog.ldf'.
2020-07-05 22:00:48.26 Server      Setup step is copying system data file 'C:\templatedata\msdbdata.mdf' to '/var/opt/mssql/data/msdbdata.mdf'.
2020-07-05 22:00:49.40 Server      Setup step is copying system data file 'C:\templatedata\msdblog.ldf' to '/var/opt/mssql/data/msdblog.ldf'.
2020-07-05 22:00:49.56 Server      Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64) 
mssql   Nov 30 2018 12:57:58 
mssql   Copyright (C) 2017 Microsoft Corporation
mssql   Developer Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)
2020-07-05 22:00:49.56 Server      UTC adjustment: 0:00
2020-07-05 22:00:49.56 Server      (c) Microsoft Corporation.
2020-07-05 22:00:49.57 Server      All rights reserved.
2020-07-05 22:00:49.57 Server      Server process ID is 4120.
2020-07-05 22:00:49.57 Server      Logging SQL Server messages in file '/var/opt/mssql/log/errorlog'.
2020-07-05 22:00:49.57 Server      Registry startup parameters: 
mssql    -d /var/opt/mssql/data/master.mdf
mssql    -l /var/opt/mssql/data/mastlog.ldf
mssql    -e /var/opt/mssql/log/errorlog
2020-07-05 22:00:49.59 Server      Error: 17113, Severity: 16, State: 1.
2020-07-05 22:00:49.59 Server      Error 87(The parameter is incorrect.) occurred while opening file '/var/opt/mssql/data/master.mdf' to obtain configuration information at startup. An invalid startup option might have caused the error. Verify your startup options, and correct or remove them if necessary.
mssql exited with code 1

解決方法

調べてみると、Docker for Macだとvolumn mappingはサポートされていないらしい。
https://github.com/microsoft/mssql-docker/issues/12

ただデータを永続化できないと色々面倒な問題があったので、書き方を調べてみた結果、以下のような書き方で動作することが確認できた。

docker-compose.yaml
version: "3"
services:
  sqlserver:
    image: microsoft/mssql-server-linux:2017-latest
    container_name: mssql
    hostname: mssql
    volumes:
      - ./.db:/var/opt/mssql/
      - /var/opt/mssql/data
    ports:
      - 14330:1433
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: SQLServer2017

おわりに

もし間違いがあればご指摘いただけると助かります。

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

WSL2での開発メモ

はまったところを書いていく
docker for Windowsは意地でも使わない

WSL2内でX11アプリケーションを起動し、Windows側で描画

  1. Windows側でvcXsrvを起動
  2. WSL側でexport DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0
  3. WSL側でX11アプリケーション起動

WSL2はsystemdが使えない

WSL用のinitを使っているらしい

よってdockerdの自動起動ができない

dockerd --add-runtime oci=/usr/sbin/docker-runc
で手動で起動する

Windows側からWSLのファイルシステムにアクセスする

explorerのナビゲーションバー(?)に
\\wsl$
と入力してEnter

docker-composeでmappingしたportにwindowsから127.0.0.1経由でアクセスできない

localhostだとつながるのに、127.0.0.1だとつながらない

before.yaml
ports:
  - 80:80
after.yaml
ports:
  - 127.0.0.1:80:80
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCode Remote Containers を利用して最強のローカル開発環境を作りたい

はじめに

VSCodeの神拡張機能であるRemote Containersの自分なりの設定の紹介です
公式サンプルは公開されていますが、そのままだと流石に使いずいので自分なりに使いやすいように編集した設定を紹介します
なお、本記事で紹介する設定ファイルは全て以下のリポジトリで公開しています(紹介していない環境のものも入っています)
https://github.com/sabure500/remote-container-sample

また、Remote Containersを使ってみて良いなと思ったので色々使いやすいように設定を弄っていますが、本記事は最強のローカル環境を「作りたい」なので、ここをこうした方が良いといった案があったら是非教えてくれると嬉しいです

VSCode Remote Containers とは

VSCodeの拡張機能であり、使用することでコンテナの中でVSCodeを開いて作業を行うことができるようになる
コンテナの中で直接VSCodeを開いて作業ができるようになるため、開発環境をサンドボックス化してローカルマシン上には全く影響しないところで開発を行うことができる
類似の拡張機能シリーズでこれ以外にも「Remote SSH」と「Remote WSL」が存在し、これはそれぞれSSH接続先またはWSLの中でVSCodeを開いて作業を行うことができるようになる
それぞれの詳細は 公式サイト を参照

インストール

VSCode Remote Containers で開発環境を作る場合は以下の2つのインストールが必要です
逆にいうと、以下の2つがあればローカルマシンには他に何も入れずにNode,python,Go,Java等の環境が作れます
* Visual Studia Code
* Docker Desktop for Windows or Mac

Docker Desktop for Windows or Mac

以下の公式ページからインストーラをダウンロードする
https://www.docker.com/products/docker-desktop

Visual Studio Code

VSCode本体のインストール

以下の公式ページからダウンロードする
https://code.visualstudio.com/docs

Remote Containersの導入

Remote Containersは通常の拡張機能なので、VSCodeインストール後に起動して左のタブから拡張機能を選択し「Remote Container」と検索することで一覧に出てくるのでそこからインストールできる
もしくは以下のマーケットプレースのページからインストールしても良い
https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers

Remote Containersの起動

後ほど紹介するRemoteContainersの設定ファイルがある場所をWorkspaceとして起動後に、VSCode左下の緑の「><」マークをクリックし、「Remote-Containers : Reopen in Container」を選択する

環境構築設定の紹介

Remote Containersにおける環境構築は.devcontainerディレクトリ上にdevcontainer.jsonというRemote Containers用の設定ファイルとDockerfile(もしくは、docker-compose.yaml等)を配置することで行う
環境毎の設定を紹介していく

GoogleCloudSDK

ローカル環境でGoogleCloudSDKのコマンドを利用するときもRemote Containersを利用している
設定ファイルは以下で公開しており、基本的にはこれをそのまま使うことで誰でもすぐに同じGoogleCloudSDKの環境を利用できる
その環境構築用の設定を記述していく
全体のディレクトリ構成は以下のようになっている

.
├ .devcontainer
    ├ devcontainer.json
    ├ Dockerfile
    ├ .config/fish/config.fish
    └ .local/share/fish/fish_history

Dockerfile

実際に開発環境として使うコンテナを作成する用のファイル
最初に全体像を示し、その後各行を解説する

Dockerfile
FROM google/cloud-sdk:297.0.1-alpine

# ===== common area =====
RUN apk add --no-cache fish git openssh curl
COPY .config/fish/config.fish /root/.config/fish/config.fish
# =======================

# ===== kubernetes resource install =====
ENV KUBECTL_VERSION 1.18.4
ENV KUSTOMIZE_VERSION 3.1.0
ENV ARGOCD_VERSION 1.5.2
RUN curl -sfL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl \
    && curl -sfL -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 \
    && curl -sfL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v${ARGOCD_VERSION}/argocd-linux-amd64 \
    && chmod +x /usr/local/bin/kubectl /usr/local/bin/kustomize /usr/local/bin/argocd
# =======================
  • ベースイメージ

    FROM google/cloud-sdk:297.0.1-alpine
    

    ベースイメージは google/cloud-sdk:297.0.1-alpine を利用する

  • 環境で利用する汎用的なパッケージのインストール

    RUN apk add --no-cache fish git openssh curl
    

    ベースイメージがalpineなので、apkを利用して開発環境上で利用したいパッケージをインストールする
    ここではfish,git,ssh,curlを入れているが、bashを使いたい場合はfishではなくbashを導入する等各自カスタマイズする

  • fishシェル用の設定

    COPY .config/fish/config.fish /root/.config/fish/config.fish
    
    config/fish/config.fish
    set normal (set_color normal)
    set magenta (set_color magenta)
    set yellow (set_color yellow)
    set green (set_color green)
    set red (set_color red)
    set gray (set_color -o black)
    
    # Fish git prompt
    set __fish_git_prompt_showdirtystate 'yes'
    set __fish_git_prompt_showstashstate 'yes'
    set __fish_git_prompt_showuntrackedfiles 'yes'
    set __fish_git_prompt_showupstream 'yes'
    set __fish_git_prompt_color_branch yellow
    set __fish_git_prompt_color_upstream_ahead green
    set __fish_git_prompt_color_upstream_behind red
    
    # Status Chars
    set __fish_git_prompt_char_dirtystate '⚡'
    set __fish_git_prompt_char_stagedstate '→'
    set __fish_git_prompt_char_untrackedfiles '☡'
    set __fish_git_prompt_char_stashstate '↩'
    set __fish_git_prompt_char_upstream_ahead '+'
    set __fish_git_prompt_char_upstream_behind '-'
    
    function fish_prompt
      set last_status $status
    
      set_color $fish_color_cwd
      printf '%s' (prompt_pwd)
      set_color normal
    
      printf '%s ' (__fish_git_prompt)
    
      set_color normal
    end
    

    作業用のシェルとしてはfishシェルを利用する
    初期設定のままでは使いづらいので、Gitのブランチを表示する等のプロンプトを変更する設定ファイルをコンテナ上にコピーして配置する
    fishの設定ファイルは以下のブログの記事を参考にさせてもらっています
    https://www.martinklepsch.org/posts/git-prompt-for-fish-shell.html

  • GoogleCloudSDkと一緒に使うコマンド類のインストール

    # ===== kubernetes resource install =====
    ENV KUBECTL_VERSION 1.18.4
    ENV KUSTOMIZE_VERSION 3.1.0
    ENV ARGOCD_VERSION 1.5.2
    RUN curl -sfL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl \
        && curl -sfL -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 \
        && curl -sfL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v${ARGOCD_VERSION}/argocd-linux-amd64 \
        && chmod +x /usr/local/bin/kubectl /usr/local/bin/kustomize /usr/local/bin/argocd
    # =======================
    

    GCPのリソースとして専らGKEを使うことが多いので、Kubernetes関連のリソースをコンテナ内にインストールしています

devcontainer.json

VSCodeからコンテナを開く際の設定ファイル
利用するDockerfileやコンテナ上でVSCodeを利用する際の拡張機能、またローカル環境からのVolume等を記述する
他に何ができるかの詳細は公式のリファレンスを参照
最初に全体像を示し、その後各行を解説する

devcontainer.json
{
    "name": "Google Cloud SDK Remote-Container",
    "build" : {
        "dockerfile": "Dockerfile"
    },
    "settings": {
        "terminal.integrated.shell.linux": "/usr/bin/fish",
    },
    "extensions": [
        "alefragnani.bookmarks",
        "mhutchie.git-graph",
        "redhat.vscode-yaml",
        "zainchen.json"
    ],
    "mounts": [
        "source=${localEnv:HOME}/.ssh/,target=/root/.ssh/,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.gitconfig,target=/root/.gitconfig,type=bind,consistency=cached",
        "source=${localWorkspaceFolder}/.devcontainer/.local/share/fish/fish_history,target=/root/.local/share/fish/fish_history,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.config/gcloud/,target=/root/.config/gcloud/,type=bind,consistency=cached",
        "source=${localEnv:HOME}/.kube/,target=/root/.kube/,type=bind,consistency=cached",
    ],
}
  • 利用するコンテナイメージ

        "build" : {
            "dockerfile": "Dockerfile"
        },
    

    Dockerfileの置いてある場所を指定する
    最初にディレクトリ構造で示した通り、同じディレクトリ上にあるのでそのまま"Dockerfile"と書いている

  • コンテナ固有のVSCodeの設定

        "settings": {
            "terminal.integrated.shell.linux": "/usr/bin/fish",
        },
    

    コンテナ上独自で設定したいVSCodeの設定を記載する
    例えば、ローカル上では導入しないがコンテナ上では導入するExtension用の設定等
    settingsの記述に関してはローカル上で書かれていることは改めてdevcontainer.json上で書かなくてもコンテナ上で引き継がれる
    ここではコンテナ上ではターミナルのシェルはfishを利用することだけ記述している

  • コンテナ環境上のVSCodeで利用する拡張機能の設定

        "extensions": [
            "alefragnani.bookmarks",
            "mhutchie.git-graph",
            "redhat.vscode-yaml",
            "zainchen.json"
        ],
    

    コンテナ環境上で利用したい拡張機能を記述する
    拡張機能に関してはsettingsの設定と違い、ローカル上で導入されていてもdevcontainer.json上で書かれていないものはコンテナ上で導入されないので注意

  • ローカル環境からのマウント

        "mounts": [ 
            "source=${localEnv:HOME}/.ssh/,target=/root/.ssh/,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.gitconfig,target=/root/.gitconfig,type=bind,consistency=cached",
            "source=${localWorkspaceFolder}/.devcontainer/.local/share/fish/fish_history,target=/root/.local/share/fish/fish_history,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.config/gcloud/,target=/root/.config/gcloud/,type=bind,consistency=cached",
            "source=${localEnv:HOME}/.kube/,target=/root/.kube/,type=bind,consistency=cached",
        ],
    

    ローカル環境上のファイルを使いたい、または、コンテナを再起動しても消去されて欲しくないファイルをマウントする
    "source="でローカルのパス、"target="でコンテナ上のパスを指定している
    また、${localEnv:XXXX}と書くことで、ローカル環境上で環境変数「XXXX」を利用できる
    ここでは5つのファイル・ディレクトリをマウントしている

    1. sshの設定 sshの設定は複数の環境から利用されるため、ローカル環境からマウントして利用する
    2. gitの設定 gitの設定も複数の環境から利用されるため、ローカル環境からマウントして利用する
    3. fishの操作履歴 ここが結構ポイントで、コンテナ上で作業しているとコンテナを停止すると操作履歴が全て削除されてしまう 自分は作業をするときにカーソル↑等を利用して過去のhistoryのコマンド履歴を利用することが多く消されると不便だったので、消えないようにWorkspaces上でマウントしておく
    4. gcpの設定 GCPログイン情報等も毎回消えると面倒なのでローカル上からマウントする
    5. kubectlの設定 同じくGKEのクラスタ登録等も毎回消えると面倒なのでローカル上からマウントする

Java

Java用の環境として、OpenJDK+Wildfly+maven+Gradleが入った環境を使用している
このJava用の環境も上で紹介したGoogleCloudSDKの環境と相違点に絞って紹介する
全体のディレクトリ構成は以下のようになっている

.
├ .devcontainer
    ├ devcontainer.json
    ├ docker-compose.yaml
    ├ Dockerfile
    ├ .m2/
    ├ .gradle/
    ├ .config/fish/config.fish
    └ .local/share/fish/fish_history

docker-compose

Javaの環境は別にコンテナで立てるDB環境と接続するためにDockerのNetworkを利用するためにDockerfileではなく、docker-composeを利用する

最初に全体像を示し、その後各行を解説する

docker-compose.yaml
version: "3"
services:
  jdk-wildfly-maven:
    build: .
    ports:
      - "8080:8080"
      - "9990:9990"
    command: /bin/sh -c "while sleep 1000; do :; done"
    volumes: 
      - $HOME/.ssh:/root/.ssh
      - $HOME/.gitconfig:/root/.gitconfig
      - .local/share/fish/fish_history:/root/.local/share/fish/fish_history
      - ..:/workspace
      - ./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml
      - .m2:/root/.m2
      - .gradle:/root/.gradle
    networks:
      - remote-container_common-network
networks: 
  remote-container_common-network:
    external: true
  • ローカル環境からコンテナ環境へのポートフォワード

    ports:
      - "8080:8080"
      - "9990:9990"
    

    Wildflyのデフォルトポートである8080と9990に対して、ローカル環境で対象のポートにアクセスした際にコンテナ上にアクセスするようにする

  • コンテナのデフォルトコマンドの上書き

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

    コンテナ起動時のデフォルトコマンドが失敗したり終了したりした場合にコンテナが停止しないように、デフォルトコマンドを上書きする
    ここで記述しているコマンドはdocker-composeを利用しない場合のRemote Containersのデフォルト設定
    docker-composeを利用する場合は明示的に書いてあげる必要がある

  • ローカル環境からのマウント

    volumes: 
      - $HOME/.ssh:/root/.ssh
      - $HOME/.gitconfig:/root/.gitconfig
      - .local/share/fish/fish_history:/root/.local/share/fish/fish_history
      - ..:/workspace
      - ./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml
      - .m2:/root/.m2
      - .gradle:/root/.gradle
    

    ローカル環境からのマウントはdevcontainer.jsonではなくdocker-compose.yamlで書く必要がある
    Dockerfileの場合と違うところは、"..:/workspace"と書いているようにworkspace自体を明示的に指定してマウントしている
    java環境独自の設定として".m2:/root/.m2"や".gradle:/root/.gradle"でGradleやMavenの設定やリポジトリをマウントしている。これはこの環境でしか利用しないものなので、ユーザーホーム($HOME)ではなくWorkSpace上に直接マウントしている
    またWildflyの設定ファイルであるstandalone.xmlについては、データソースの設定等が開発中に常に変更される可能性があり、コンテナ再起動のたびに削除されても困るので"./jboss_home/configuration/standalone.xml:/opt/wildfly/standalone/configuration/standalone.xml"といった形でマウントしている

  • docker networkの利用

        networks:
          - remote-container_common-network
    networks: 
      remote-container_common-network:
        external: true
    

    Javaはアプリケーションの実行環境として利用するのでDBと接続をしたい
    DBに関してもローカル環境ではコンテナとして起動するので他のコンテナと接続するためにDocker Networkを作成してそれを利用する
    そのためこの環境を利用するためには以下のコマンドで事前にネットワークを作成する必要がある

    docker network create --driver bridge remote-container_common-network
    

Dockerfile

docker-compose.yamlで指定しているOpenJDK+Wildfly+maven+Gradle環境を作成するDockerfile
ベースイメージとしてはJBoss公式イメージ"jboss/wildfly"はサイズが大きく使いずらかったりalpineベースのイメージを使いたかったこともあり、Adoptopenjdkを元にして作成している

Dockerfile
FROM adoptopenjdk/openjdk11:alpine-slim

# ===== common area =====
ENV WORKSPACE_DIR "/workspace"
RUN apk add --no-cache fish git openssh curl wget tar unzip\
    && mkdir -p $WORKSPACE_DIR
COPY .config/fish/config.fish /root/.config/fish/config.fish
# =======================

# ==== wildfly install =====
ENV JBOSS_HOME "/opt/wildfly"
ENV WILDFLY_VERSION "20.0.0.Final"

RUN wget -P /opt http://download.jboss.org/wildfly/${WILDFLY_VERSION}/wildfly-${WILDFLY_VERSION}.tar.gz \
    && tar -zxvf /opt/wildfly-${WILDFLY_VERSION}.tar.gz -C /opt \
    && rm /opt/wildfly-${WILDFLY_VERSION}.tar.gz \
    && mv /opt/wildfly-${WILDFLY_VERSION} ${JBOSS_HOME} \
    && $JBOSS_HOME/bin/add-user.sh admin admin --silent
# =======================

# ==== maven install =====
ENV MAVEN_HOME "/opt/maven"
ENV MAVEN_VERSION 3.6.3
ENV PATH "$PATH:$MAVEN_HOME/bin"
ENV MAVEN_CONFIG "$HOME/.m2"
RUN curl -fsSL -o /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz http://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
    && tar -zxvf /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt \
    && rm /opt/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
    && mv /opt/apache-maven-${MAVEN_VERSION} /opt/maven
# =======================

# ==== gradle install ====
ENV GRADLE_HOME "/opt/gradle"
ENV GRADLE_VERSION 6.5
ENV PATH "$PATH:$GRADLE_HOME/bin"
RUN curl -fsSL -o /opt/gradle-${GRADLE_VERSION}-bin.zip https://downloads.gradle-dn.com/distributions/gradle-${GRADLE_VERSION}-bin.zip \
    && unzip -d /opt /opt/gradle-${GRADLE_VERSION}-bin.zip \
    && rm /opt/gradle-${GRADLE_VERSION}-bin.zip \
    && mv /opt/gradle-${GRADLE_VERSION} /opt/gradle
# =======================

devcontainer.json

Dockerfileを利用する場合とdocker-composeを利用する場合で、devcontainer.jsonのデフォルトの設定や利用できる設定が変わっている(例えば、マウントはdevcontainer.jsonには記述しても効かなくなり、docker-composeにて記述しなければならなくなる)
詳細は公式リファレンス参照

devcontainer.json
{
    "name": "JDK&Wildfly&Maven&Gradle Remote-Container",
    "dockerComposeFile": "docker-compose.yaml",
    "service" : "jdk-wildfly-maven",
    "workspaceFolder": "/workspace",
    "settings": {
        "terminal.integrated.shell.linux": "/usr/bin/fish",
        "java.home": "/opt/java/openjdk",
        "maven.executable.preferMavenWrapper": false,
        "maven.executable.path": "/opt/maven/bin",
        "maven.terminal.useJavaHome": true,
        "java.jdt.ls.vmargs": "-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication -javaagent:\"/root/.vscode/extensions/gabrielbb.vscode-lombok-1.0.1/server/lombok.jar\"",
    },
    "extensions": [
        "alefragnani.bookmarks",
        "mhutchie.git-graph",
        "vscjava.vscode-java-pack",
        "shengchen.vscode-checkstyle",
        "gabrielbb.vscode-lombok",
        "naco-siren.gradle-langua"
    ],
    "shutdownAction": "stopCompose"
}
  • 利用するイメージとサービス

        "dockerComposeFile": "docker-compose.yaml",
        "service" : "jdk-wildfly-maven",
    

    docker-composeを利用する場合は"dockerComposeFile"を利用してdocker-compose.yamlの場所を指定する
    また、docker-composeの場合は複数のコンテナが起動している可能性があるので、serviceでどのコンテナでVSCodeを開くのかも指定する

  • workspaceフォルダの指定

        "workspaceFolder": "/workspace",
    

    Dockerfileの場合と違い、docker-composeの場合は明示的にマウントするworkspaceの場所を指定する必要がある
    この場合に存在しないディレクトリは指定できないため、Dockerfile上で先に/workspaceといったディレクトリを作っておく

  • java特有の設定

        "settings": {
            "terminal.integrated.shell.linux": "/usr/bin/fish",
            "java.home": "/opt/java/openjdk",
            "maven.executable.preferMavenWrapper": false,
            "maven.executable.path": "/opt/maven/bin",
            "maven.terminal.useJavaHome": true,
            "java.jdt.ls.vmargs": "-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication -javaagent:\"/root/.vscode/extensions/gabrielbb.vscode-lombok-1.0.1/server/lombok.jar\"",
        },
        "extensions": [
            "alefragnani.bookmarks",
            "mhutchie.git-graph",
            "vscjava.vscode-java-pack",
            "shengchen.vscode-checkstyle",
            "gabrielbb.vscode-lombok",
            "naco-siren.gradle-langua"
        ],
    

    ローカル環境のVSCode上では入れていないが、Java環境では利用したい拡張機能を指定している
    また、その拡張機能に対応したコンテナ上でのみ適用したいsettingsの内容を記述している

MySQL

前の章で作成したJavaから利用するDBもコンテナで作成する
この環境はあまりRemote Containersで作成する意味はないが、統一するために一応Remote Containersで作っている
単純にdocker-composeで起動しても特に問題はない
他のコンテナ(Java環境)と接続するためにこの環境を作る前に以下のコマンドでDocker networkを作っておくこと
(すでにある場合は問題なし)

docker network create --driver bridge remote-container_common-network

全体のディレクトリ構成や設定ファイルは以下のようになっている

.
├ .devcontainer
│  ├ devcontainer.json
│  ├ docker-compose.yaml
│  └ my.cnf
└ db
docker-compose.yaml
version: "3"
services:
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: pass
      TZ: "Asia/Tokyo"
    ports:
      - "3306:3306"
    volumes: 
      - ../db/data:/var/lib/mysql
      - ./my.cnf:/etc/mysql/conf.d/my.cnf
    networks:
      - remote-container_common-network
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      PMA_ARBITRARY: 1
    ports:
      - 3307:80
    depends_on:
      - mysql
    networks:
      - remote-container_common-network
networks: 
  remote-container_common-network:
    external: true
devcontainer.json
{
    "name": "MySQL Remote-Container",
    "dockerComposeFile": "docker-compose.yaml",
    "service" : "mysql",
    "workspaceFolder": "/root",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
    },
    "extensions": [],
    "shutdownAction": "stopCompose"
}

補足

今回はあくまで自分用のローカル環境の構築なので、複数のアプリケーションを1つの環境で扱うことを想定してRemote Containersの設定を作っている
つまり以下のようなこと

.
├ .devcontainer
├ application1
│  ├ .git
│  └ source code
├ application2
│  ├ .git
│  └ source code

上記のような構成ではなく、1つのアプリケーションに対して1つのRemote Containersの設定を作る方がgit上でそのアプリの開発環境も管理でき開発者全員が同じ環境を使える等のメリットがあるため良いと思われる
つまり以下のようなこと

.
├ application1
│  ├ .devcontainer
│  ├ .git
│  └ source code
├ application2
│  ├ .devcontainer
│  ├ .git
│  └ source code

ただし、このような管理の仕方の場合は今回紹介したようなfishの独自設定をぶち込んだりすると戦争が起りかねないのでチームでよく相談してどうするかを決定した方が良いと思われます

VSCode Remote Containers のメリット・デメリット

最後に使っていて思ったメリット・デメリットをまとめて終わりにする

メリット

  • 自分のローカル環境が汚れない
    VSCodeとDockerさえあれば余計なものを何も入れなくて良い点がすごく良い
  • 開発環境の設定もアプリ開発者の間で共有できる
    同じ開発環境を共有していることになるので人によっては動く動かないといった事象の発生をなくせる
    また、そもそもDockerfileが必須になるため本番環境もDockerfileで作れば開発と本番での差異を限りなく少なくできる
    ただし、あくまで開発環境用のDockerfileと本番環境用のDockerfileは別で作った方が良い
    (本記事では紹介していないがRemote Containersのextendの機能を利用して本番環境のDockerfileを元に開発にい必要なパッケージの導入部分のみ上書いて開発環境で利用することもできる)
  • 開発環境で作ったイメージがCI/CDで流用できる
    開発環境上でツールを使ってチェック等をしている時は、CI/CDの時にも同様のチェックをすることは多い
    この時に今のCICDツールはJOBのコンテナ上での実行をサポートしていることが多いので、開発環境作成時に作ったコンテナをそのままであったり一部だけ改良して利用できたりする

デメリット

  • VSCodeでしか使えない
    VSCodeの拡張機能なので、それ以外のIDEでは使えない
    かなり有用な機能なので自分が知らないだけで他のIDEでも同じようなことができるのかもしれないですが...
  • コンテナ上では利用できない拡張機能もある
    Remote Containersで起動したコンテナ上のVSCodeでは利用できない拡張機能が一部存在する
  • 利用するツールについてある程度理解していないと環境構築できない
    Dockerfileを作成することが必須であり、このツールの設定ファイルはどこに展開されるかといったことやマウントした方が良い部分とそうでない部分といったことを理解して意識する必要がある
    ただし、設定を自作するのではなく他人が作成した設定ファイルをそのまま利用する場合は今までよりさらに何も知らなくても使えます
  • 一つのコンテナ環境にどこまで詰め込んで良いか分かりづらい
    今回紹介したものだとJavaとGoogleCloudSDKで分けているが、やろうと思えばこれはさらに細分化できるし、逆に1つにまとめることもできる
    アプリケーションの単位でremote-containerの設定を作るなら分かりやすいが、GoogleCloudSDKのように自分用のローカル環境として作る場合は境目が難しく基準となる答えはまだ持っていない

参考

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

docker-composeで、nginx、php、mysqlの環境を作成してみた

私は初心者です。間違っていたりもっと良いやり方がありましたら教えていただけるとありがたいです

こちらのリンクにコードを置きました

参考

docker-compose.yml
version: "3"

services:
  web:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      # ホストのdefault.confを同期
      - ./default.conf:/etc/nginx/conf.d/default.conf
      # ホストの./myappフォルダを同期
      - ./myapp:/var/www/html
    depends_on:
      - php

  php:
    build: .
    volumes:
      # ホストの./myappフォルダを同期
      - ./myapp:/var/www/html

  db:
    image: mysql
    # PDOでhostを指定するときにこのコンテナ名を使う
    container_name: mysql
    # MySQL8.0でのデフォルトの認証方式が「caching_sha2_password」なので変更する
    # 設定しないと "The server requested authentication method unknown to the client" とエラーになる
    command: --default-authentication-plugin=mysql_native_password
    environment:
      # 設定必須、rootパスワード
      - MYSQL_ROOT_PASSWORD=root
      # この設定はオプション、イメージの起動時に作成されるデータベース名
      - MYSQL_DATABASE=sample
default.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    root /var/www/html;

    location / {
        index  index.php index.html;
    }

    location ~ \.php$ {
       fastcgi_pass   php:9000;
       fastcgi_index  index.php;
       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
       include        fastcgi_params;
    }
}
Dockerfile
FROM php:7-fpm

# 拡張モジュールをインストール
RUN docker-php-ext-install pdo pdo_mysql
myapp/index.php
<?php
    try {

        echo (new PDO(
            'mysql:host=mysql;dbname=sample;charset=utf8mb4',
            'root',
            'root',
            [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]
        ))
        ->query('select concat(\'MySQL Version :\', version()) v')
        ->fetch()['v'];

    } catch (PDOException $e) {
        echo $e->getMessage();
    }

Screen Shot 2020-07-06 at 2.20.15.png

以上です。m(_ _)m

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