20191126のdockerに関する記事は16件です。

バイオインフォ(に限らないけど)の人が docker for Mac で、よくひっかかるところ

バイオインフォ(に限らないけど)の人が docker for Mac で、よくひっかかるところ

よく聞くので、記事にしてみました。

最初に

この記事は、Workflow Advent Calendar 2019 - Qiita と、バイオインフォマティクス Advent Calendar 2019 - Qiita の5日目の記事です。

内容

Mac上に、CPUもメモリも十分あるのに、なぜかエラーになる問題

他の環境で実行したところ、CPUは1コアしか使わないし、メモリも 4GB もあれば十分、自分の Mac には 8GB のメモリがあるので、問題なく動くはず。
しかし実行すると動かない。

このようなケースの

解決策、 Docker for Mac のメモリ設定を見直す

Docker for Mac の設定で、メモリの割当が2GBがデフォルトだったかなとおもいます。
なので、それを引き上げてあげれば解決することが多いです。

この問題は、だいたい1度解決すると、ずっと発生しないのですが、
新しく買い替えたとき に再度発生することがあるようです。

困っている人がいたら、以下のように聞いてあげると良いかもしれません。

Macじゃなくて、Docker for Mac のメモリの割当はいくつになっていますか?

Mac で動いていた、別環境の Linux で実行したら、自分で消せないファイルができる問題

解決策、-u $(id -u):$(id -g) をつけて実行してみる。

ざっくりいうとMac 上では、dockerで生成したファイルは、自分のユーザの権限でMac上にできあがります。

Linuxに持っていったら場合に、消せないファイルができるというのは、root権限で、ファイルが出来上がってしまうということで、自分の権限では消せないということが発生します。

その場合は、実行するときに、 docker の -u オプションで、自分の UIDGID を指定してあげるとよいとおもいます。

消せなくなったファイルについては、別コンテナを立ち上げて消して上げればよいのではないかと思います。

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

Rails + TypeScript + Swagger 環境をDockerで爆速で構築する

なにこの記事

単純にdcoker-composeを使って、フロントとWEB APIを分けて、ついでにAPIをSwaggerで管理するテンプレートの紹介と、テンプレート作るまで手順を書いたザックリとした記事です。
たぶん爆速でできる、はず。

テンプレート

こちらのリポジトリからクローンしてください。 ./qs init で環境整うので、あとは http://localhost:8080 にアクセスすると、こんな感じで簡単なWEBアプリが作られてます。

mock_railswebver02.gif

Swaggerを利用する際は、 ./qs up doc した後で、 http://localhost にアクセスするとAPIドキュメントを利用できます。

FireShot Capture 023 - Swagger UI - localhost.png

テンプレート作成までの手順

ここからはどうやって上記のテンプレート作ったかの手順を説明していきます。

構成

docker-composeのサービス構成としては下記のようにしました。
webがフロントエンドでTypeScriptにて処理を行います。
apiがWEB APIでRuby on Railsで作られてます。
フロントも含めて全てRailsで記述できますが、今回はフロントとAPIのプラットフォームが別という想定で構築しました。
APIドキュメントはSwaggerがイメージを配布してたのでサービスを分けました。

docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    ports:
      - "5432:5432"
    volumes:
      - data:/var/lib/postgresql/data:cached
  api:
    build: ./api
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./api:/app:cached
      - bundle:/usr/local/bundle:cached
    environment:
      HOME: /app
      RAILS_ENV: development
    ports:
      - "3000:3000"
    tty: true
    links:
      - db
  web:
    build: ./web
    command: npm start
    volumes:
      - ./web:/app:cached
    environment:
      NODE_ENV: development
    ports:
      - "8080:8080"
    tty: true
    links:
      - api
  doc:
    image: swaggerapi/swagger-ui
    volumes:
      - ./api.yml:/usr/share/nginx/html/api.yml
    environment:
      API_URL: api.yml
    ports:
      - "80:8080"

volumes:
  bundle:
    driver: local
  data:
    driver: local

ディレクトリ構成は以下のようにしてます。各フォルダ配下にDockerfileを配置して、プロジェクトフォルダ直下のdocker-composeから管理しています。

projects # プロジェクトフォルダ
  - api # Railsのソースを配置
    - Dockerfile
    - Gemfile
  - web # TypeScriptのソースを配置
    - Dockerfile
    - package.json
  api.yml # Swagger定義
  docker-compose.yml

それでは各サービスの構築手順を解説していきます。

API

まずはAPIを立ち上げましょう。とは言っても簡単です。
コマンド打っただけで簡単に構築できました。やっぱりRailsって便利ですね。

アプリの立ち上げ

DockerfileとGemfileを配置して、下記のコマンドを実行していきます。 http://localhost:3000 にアクセスしてRailsのウェルカムページが表示されれば完了です。

$ docker-compose build api
$ docker-compose run --rm api bundle install
$ docker-compose run --rm api bundle exec rails new . -f -d=postgresql --api
$ docker-compose run --rm api bundle update
# database.ymlを下記の内容で手動で書き換える
$ docker-compose run --rm api bundle exec rails db:create
$ docker-compose up api
database.yml
default: &default
  adapter: postgresql
  encoding: utf8
  min_messages: WARNING
  host: db
  port: 5432
  username: postgres
  password: postgres
  pool: 5
  timeout: 5000
  stats_execution_limit: 10

development:
  <<: *default
  database: development

test:
  <<: *default
  database: test

production:
  <<: *default
  database: production

Dockerfile

# use ruby version 2.6.5
FROM ruby:2.6.5

# using japanese on rails console
ENV LANG C.UTF-8

# remove warn
ENV DEBCONF_NOWARNINGS yes
ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE yes
ENV XDG_CACHE_HOME /tmp
EXPOSE 3000

# install package to docker container
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    vim \
    less

# install yarn
RUN apt-get install apt-transport-https
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn

# install nodejs
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -
RUN apt-get install -y nodejs

# setting work directory
RUN mkdir /app
WORKDIR /app

# setting environment value
ENV HOME /app

# executing bundle install
COPY Gemfile /app/Gemfile

Gemfile

source 'https://rubygems.org'

gem 'rails', '~> 5.2.3'

エンドポイントの追加

文字列フィールドを1つもつModelのAPIを追加します。下記のコマンド打てば、API一式とテストを作ってくれます。素晴らしいいい。
Rspecを使うのでGemfileにgemを追加しておいてください。

$ docker-compose run --rm api bundle exec rails g scaffold Content body:string
$ docker-compose run --rm api bundle install
$ docker-compose run --rm api bundle exec rails g rspec:install
$ docker-compose run --rm api bundle exec g rspec:integration Content

これでほぼ完成ですが、なぜかapiフラグをつけるとストロングパラメーターを使わないので、下記のようにコントローラーを修正します。

contents_controllers.rb
  def content_params
    params.require(:content).permit(:body)
  end

あとは、適宜取得したいようにController、Modelの修正と、テストの追加はしておいてください。
面倒なんでここでは割愛します。リポジトリのコミットログ見てください。

rack_corsの導入

このままだとフロントからアクセスできないので、rack_corsを導入します。
Gemfileにrack-corsを追加して、docker-compose run --rm api bundle installしてください。
そして、下記の修正を config/application.rb に追記すれば完成です。

config/application.rb
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'localhost:3000', 'localhost:8080'
        resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options]
      end
    end

疎通確認

これだけでAPIの構築が完了しました。最後に疎通確認を行います。
RailsコンソールからContentモデルに適当にデータを登録して、ブラウザから http://localhost:3000/contents を叩いて見ましょう。

[
  {
    "id": 1,
    "body": "hoge",
    "created_at": "2019-11-22T15:54:26.543Z",
    "updated_at": "2019-11-22T15:54:26.543Z"
  },
  {
    "id": 2,
    "body": "foo",
    "created_at": "2019-11-22T15:54:30.464Z",
    "updated_at": "2019-11-22T15:54:30.464Z"
  }
]

無事に取得できました。これでAPIの作成は終了です。

フロント

実際にユーザーがアクセスして使うフロントアプリケーションを作成します。
React、VueなどJSフレームワークを使ってもいいのですが、今回は超小さいアプリなのでTypeScriptを使ってサクッと作ろうかと思います。

アプリの立ち上げ

apiと同じようにDockerfileとpackage.jsonを配置して、下記のコマンドを実行していきます。

$ docker-compose build web
$ docker-compose run --rm web npm install 

urlとかは適宜変えてください。パッケージは今回使うものを記載してあります。

package.json
{
  "name": "web",
  "version": "1.0.0",
  "description": "web app",
  "main": "index.js",
  "scripts": {
    "start": "http-server -o",
    "tsc": "tsc",
    "build": "webpack"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/belion-freee/rails_web_api.git"
  },
  "author": "belion-freee",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/belion-freee/rails_web_api/issues"
  },
  "homepage": "https://github.com/belion-freee/rails_web_api#readme",
  "devDependencies": {
    "axios": "^0.19.0",
    "http-server": "^0.11.1",
    "ts-loader": "^6.2.1",
    "typescript": "^3.7.2",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}

処理の記述

TypeScriptは初めてでしたので、下記の記事を参考にして作成しました。

https://qiita.com/EBIHARA_kenji/items/31b7c1c62426bdabd263

API通信のために axios

Webサーバーとして立ち上げるために http-server

をそれぞれ使ってます。

tsconfig.jsonがTypeScriptの設定を記述するファイルです。自分は下記のように設定しました。もっといろんな設定ができるので公式リファレンスを読んでみてください。

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2015",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "outDir": "./build",                      /* Redirect output structure to the directory. */
    "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  },
  "include": [
      "src/**/*.ts"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}

ソースコード

ここからは実際のソースコードです。下記のように記述しました。

index.html を用意して、DOMに要素を動的にjsで展開して行く構成です。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./style.css">
    <title>TS-Web</title>
  </head>
  <body>
    <h1>This is the template of Web APP</h1>
    <h2>created by TypeScript</h2>
    <h3>Those are contents from Web API</h3>
    <div id="contents-form">
      <form>
        <input type="text" value="" id="content-body">
        <button type="button" class="add-content">Add</button>
      </form>
    </div>
    <div id="contents"></div>
    <script src="./build/index.js"></script>
  </body>
</html>

srcディレクトリ配下にindex.htmlを操作するjsファイルを配置します。

  • index.ts : indexを操作するためのjs。
  • api.ts : Rails-APIと通信するための機能を備えたクラス。

https://github.com/belion-freee/rails_web_api/tree/master/web/src

ビルド

ここまで作成したら次にビルドする必要があります。ビルドにはwebpackを仕様します。すでにインストール済みなので、コマンドを実行してビルドしましょう。

$ docker-compose run --rm web npm run build

buildディレクトリにソースが作成されていれば成功です。
ここまででフロントの実装は終了です。

APIドキュメント

次にAPIドキュメントを作成します。とは言ってもすごい簡単です。

プロジェクト配下にapi.ymlを配置します。

サンプルは公式から拝借しました。
https://github.com/swagger-api/swagger-samples/blob/master/java/inflector-dropwizard/src/main/swagger/swagger.yaml

内容はこんな感じです。
https://github.com/belion-freee/rails_web_api/blob/master/api.yml

記述に関しては、API自体がシンプルなのもありますが、ドキュメント読まなくてもなんとなく書けるくらい分かり易かったです。

記述が終わったら、docker-compose up doc して http://localhost にアクセスすると読めるので、記述が正しいか確認してください。
これで全ての工程が終了です。

まとめ

いかがだったでしょうか。
API関連はすごい簡単に導入できましたが、TypeScriptはちょっと時間がかかりました。
TypeScriptに関してはもっとちゃんと書きたいなと思います。だいぶやっつけ感ある。

それでも、ものすごい早く複数プラットフォームのミニマムなWEBアプリを開発できたので、Dockerさんに感謝です。
流石にミニマムすぎるので、暇なときにAPI認証のエンドポイントを実装したいと思います。

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

【Docker/Kubernetes】今夜勝ちたいKubernetes概要

この投稿ではインストールを求めたりばかげた呪文を入力させたりはしない。

コンテナオーケストレーションシステムである"Kubernetes"。
dockerを覚えたうえでなぜそんなものまで覚えなければならないのか、
現在まで勉強した結果を私なりにまとめよう。

(途上につき、間違いなどあれば申し訳ありません。ご指摘いただけると幸いです)

なぜKubernetesが必要か

一言でいえば、運用、特に複数台のホストにまたがるサービス運用において課題が出るからと言えます。

dokcer-composeによる複数コンテナを管理でローカルの開発環境についてはほぼ問題はないと思います。
しかし、これらは基本的に開発マシンなどといった1つのホスト内での話です。

実運用においては例えば以下のような課題を考慮し、複数ホスト間での連携も必要となってきます。

  • ロードバランシング
  • デプロイ時のダウンタイム
  • オートスケーリング
  • 死活監視(ホスト内のコンテナについて)

しかし、その構築は非常に複雑なものとなります。

こういった複数ホスト、コンテナ間の連携や、サービス実運用に必要な機能をもたらすのがKubernetesとなります。

読み飛ばしてもいいところ

例えばオートスケーリングについて考えてみます。
nginx、phpアプリ、mysqlコンテナが一つずつ稼働しているホストがあり、そのスケールが必要になったとします。
このホストそのものを単純にスケールしてよいものでしょうか?

効率的な運用を考えれば、ホストを増やしたうえでそれぞれのホストにどうコンテナをデプロイするか考える必要が出てきそうです。

死活監視についてもそうです。ホストそのものならまだしも、
ホスト内で動いているコンテナすべての死活監視、および必要に応じた自動起動を自前で構築しようとすると難しいのではないでしょうか。

構成

以下大体の構成。デプロイメントあたりから脳が若干受付拒否をしだす。

  • クラスタ:以下に挙げるdockerホストなどのリソース集合体
    • マスターノード:管理サーバ
    • ノード(Node):実際にコンテナがデプロイされるホスト
    • ネームスペース(NameSpace):クラスタ内の仮想的なクラスタ
    • ポッド(Pod):コンテナの集合(同一ポッド内のコンテナはノードを跨がない)
    • レプリカセット(ReplicaSet):ポッドの複製・管理
    • デプロイメント(Deployment):レプリカセットの世代管理
    • サービス(Service):ポッドの集合&ポートなどの通信規約(書いて字の如くこの単位で一つのサービス)
    • イングレス(Ingress):サービスの公開&ルーティング

という感じです。フワッと理解できたら今日はもう優勝して大丈夫です。

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

TerraformとSAMでAWSリソースを用意する人のための実行環境をdockerで作るよ

下記をDockerfileとしてLinux上に用意してください。

FROM python:3.8 as baseimage

RUN pip install \
      awscli \
      aws-sam-cli

# terraformバージョン指定
ARG terraform_version="0.12.16"

WORKDIR /root/
ADD https://releases.hashicorp.com/terraform/${terraform_version}/terraform_${terraform_version}_linux_amd64.zip ./
RUN unzip /root/terraform_${terraform_version}_linux_amd64.zip -d /usr/bin/ \
&& chmod +x /usr/bin/terraform

# --------------------------------------------------
FROM python:3.8-alpine3.10

# pip installed commands
COPY --from=baseimage /usr/local/bin/aws* /usr/local/bin/
COPY --from=baseimage /usr/local/bin/sam /usr/local/bin/
COPY --from=baseimage /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages
# terraform
COPY --from=baseimage /usr/bin/terraform /usr/bin/

USER root

RUN apk upgrade

## 使用コマンド
RUN apk add \
      vim \
      less \
      jq \
      git \
      groff

# JST化
RUN apk --no-cache add tzdata \
&&  cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&&  apk del tzdata

下記コマンドでビルドします。
docker build -t awssamterraform . --no-cache

下記コマンドでコンテナの中に入ります。
docker run -it awssamterraform ash

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

OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた(4)

2019年12月中旬に公開予定の連載記事と連動した自習用コンテンツです。記事作成にQiitaの公開用URLが必要なため、事前に投稿しました。この投稿の全文は記事公開と同時に公開します。

はじめに

IBMのCode Patternsの1つである架空の医療会社のExample HealthのOpenShiftによるJava EEアプリケーションのモダナイゼーションの4回目の投稿です。

これまでの投稿では、架空の医療会社のExample HealthはJava EEアプリケーションのモダナイゼーションに成功しました。OpenShiftに移行した結果、容易に機能拡張できようになりシステムを拡大して新しいサービスを追加していきます。

順風満帆に見えたExample Healthですが、ある問題に直面します。
ある問題を解決することが4回目の投稿のメインテーマになります。お楽しみに

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

3分28秒でDjangoをデプロイ(docker-compose)

既に作成されたプロジェクトの場合でも対応可能な形のdocker-composeを作成しましたので、仕様を記事にまとめます。

GitHub上にHowtoUseを書いておりますのでどうぞ→GitHub

(DBをデフォルトのsqliteから変更してる場合はまた別の設定が必要になると思います・・・ごめんなさい)

デプロイ環境はDjango+Nginx+Gunicornとなります。

ちなみに3分28秒というのは、

①リポジトリのクローン
②GitHub上のDjangoプロジェクトをクローン
③設定ファイル編集
④docker-compose
⑤ブラウザでの動作チェック

にかかった時間です。

概要

使い方は上記にリンクを用意しておりますGitHubのREADMEを参照ください。

簡単なデプロイを実現させるために、次のようなディレクトリ構成で動作するDjangoデプロイ用のdocker-composeを作りました。

django-nginx-gunicorn-docker/
      ├ nginx/
      │ └ project.conf
      ├ django/
      │  ├ Dockerfile
      │  ├ requirements.txt
      │  └ [DJANGOPROJECT]
      │     ├ manage.py
      │     ├ …
      │     └ [PROJECTNAME]
      └ docker-compose.yml

コンテナでプロジェクトを丸ごとマウントして動作させます。
では設定ファイルをみていきます。

docker-compose

利用するコンテナはDjangoアプリケーション用のもの(Gunicornもこの中で動作)と、リバースプロキシの役割を果たすNginxのコンテナです。

docker-compose.yml
version: '3'
services:
    django:
        build: ./django
        expose:
            - "8000"
        networks:
            - nginx_network
        volumes:
            - ./django:/code
        hostname: django-server
        restart: always
    nginx:
        image: nginx
        ports:
            - "80:80"
        networks:
            - nginx_network
        depends_on:
            - django
        volumes:
            - ./nginx/project.conf:/etc/nginx/conf.d/default.conf
        restart: always
networks:
    nginx_network:
        driver: bridge

djangoのvolumesのところで、プロジェクト環境をマウントしています。もしコンテナ起動後に編集を行なっても、コンテナをrestartさせることで反映されます。

また、nginxコンテナの方では80:80でポートフォワーディングをしているので、ローカルホストの80番ポートにアクセスが来た際はnginxコンテナの方に受け渡されます。

特段トリッキーなことはしていませんので、composeの説明は終わりです。

Dockerfile

djangoディレクトリにあるDockerfileの説明です。

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
WORKDIR /code/MYPROJECT
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "MYPROJECT.wsgi:application"]

python3系のイメージを利用します。6行目のRUNでDjangoとGunicornをダウンロードしています。(あとついでにPostgres用のライブラリもダウンロードしていますが使っていません。ごめんなさい)

requirements.txt
Django==2.2.7
gunicorn==19.9.0
psycopg2

ちなみにDjangoのバージョンですが、当初2.0を指定してGithubにあげた所くっそ怒られました。(セキュリティ警告がいっぱい来た。GitHubのBotから怒りのプルリクもきた。)

project.conf

nginxの設定ファイルです。ここで結構トリッキーな動きが必要でした。

project.conf
upstream django {
  server django:8000;
}

server {
    listen  80;
    server_name :localhost;

    location / {
        proxy_pass http://django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

upstreamでリクエストの送信先を指定します。送信先はコンテナのlocalhostとなっています。また、ここで命名したものをプロキシパスの設定で用います。

serverのほうではlistenするポートとプロキシの設定を行なっています。

さいごに

ポートの設定でどはまりしました。。。
みなさんも気をつけましょう;;

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

docker-composeを使って3分28秒でDjangoをデプロイ

既に作成されたプロジェクトの場合でも対応可能な形のdocker-composeを作成しましたので、仕様を記事にまとめます。

GitHub上にHowtoUseを書いておりますのでどうぞ→GitHub

(DBをデフォルトのsqliteから変更してる場合はまた別の設定が必要になると思います・・・ごめんなさい)

デプロイ環境はDjango+Nginx+Gunicornとなります。

ちなみに3分28秒というのは、

①リポジトリのクローン
②GitHub上のDjangoプロジェクトをクローン
③設定ファイル編集
④docker-compose
⑤ブラウザでの動作チェック

にかかった時間です。

概要

使い方は上記にリンクを用意しておりますGitHubのREADMEを参照ください。

簡単なデプロイを実現させるために、次のようなディレクトリ構成で動作するDjangoデプロイ用のdocker-composeを作りました。

django-nginx-gunicorn-docker/
      ├ nginx/
      │ └ project.conf
      ├ django/
      │  ├ Dockerfile
      │  ├ requirements.txt
      │  └ [DJANGOPROJECT]
      │     ├ manage.py
      │     ├ …
      │     └ [PROJECTNAME]
      └ docker-compose.yml

コンテナでプロジェクトを丸ごとマウントして動作させます。
では設定ファイルをみていきます。

docker-compose

利用するコンテナはDjangoアプリケーション用のもの(Gunicornもこの中で動作)と、リバースプロキシの役割を果たすNginxのコンテナです。

docker-compose.yml
version: '3'
services:
    django:
        build: ./django
        expose:
            - "8000"
        networks:
            - nginx_network
        volumes:
            - ./django:/code
        hostname: django-server
        restart: always
    nginx:
        image: nginx
        ports:
            - "80:80"
        networks:
            - nginx_network
        depends_on:
            - django
        volumes:
            - ./nginx/project.conf:/etc/nginx/conf.d/default.conf
        restart: always
networks:
    nginx_network:
        driver: bridge

djangoのvolumesのところで、プロジェクト環境をマウントしています。もしコンテナ起動後に編集を行なっても、コンテナをrestartさせることで反映されます。

また、nginxコンテナの方では80:80でポートフォワーディングをしているので、ローカルホストの80番ポートにアクセスが来た際はnginxコンテナの方に受け渡されます。

特段トリッキーなことはしていませんので、composeの説明は終わりです。

Dockerfile

djangoディレクトリにあるDockerfileの説明です。

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
WORKDIR /code/MYPROJECT
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "MYPROJECT.wsgi:application"]

python3系のイメージを利用します。6行目のRUNでDjangoとGunicornをダウンロードしています。(あとついでにPostgres用のライブラリもダウンロードしていますが使っていません。ごめんなさい)

requirements.txt
Django==2.2.7
gunicorn==19.9.0
psycopg2

ちなみにDjangoのバージョンですが、当初2.0を指定してGithubにあげた所くっそ怒られました。(セキュリティ警告がいっぱい来た。GitHubのBotから怒りのプルリクもきた。)

project.conf

nginxの設定ファイルです。ここでトリッキーな動きが必要でした。

project.conf
upstream django {
  server django:8000;
}

server {
    listen  80;
    server_name :localhost;

    location / {
        proxy_pass http://django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

upstreamでリクエストの送信先を指定します。送信先はコンテナのlocalhostとなっています。また、ここで命名したものをプロキシパスの設定で用います。

serverのほうではlistenするポートとプロキシの設定を行なっています。

さいごに

ポートの設定でどはまりしました。。。
みなさんも気をつけましょう;;

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

【自宅ではじめるDocker入門】アウトプット記事

Dockerとは?

  • Docker社が提供する、オープンソースの「コンテナ型 仮想化ソフト
    =>仮想化 : 実際にマシンを用意しなくても、仮想敵にパソコンやサーバを作れる仕組み

  • Dockerは、この仮想化をコンテナ型でおこなう
    =>コンテナ : 隔離されたプログラム実行環境

  • 1つのDockerの上には、たくさんのコンテナを作成できる。

  • この場合でも、それぞれのコンテナは互いに影響を受けずに、独立した形で隔離された実行環境を作成できる

つまり、Dockerは、仮想敵に実行環境が作れるだけでなく、この実行環境をコンテナ単位でわけて作れる。

コンテナ

  • コンテナでは、マシンとやり取りするだけの最低限の機能しか搭載されていないため、非常に軽量
  • なので、1アプリ = 1コンテナで構成するのが慣例

ex)DBを使ったWebシステムの構築
- Webサーバーを担当するコンテナ
- DBを担当するコンテナ

の2つに分ける。リージョンを分けておく変えるみたいな?

コンテナの基

  • コンテナは、イメージから作成する
  • イメージはDocker Hubにて公開されている

ex)Docker Hubで公開されているイメージ

  • Ubuntuがインストールされたイメージ
  • Apacheがインストールされたイメージ
  • MySQLがインストールされたイメージ

等々...。ここら辺をダウンロードしてしまえば、自分で設定しなくても、コマンド1つでこれらを動かせる。

カスタムしてコンテナ作成することも可能

  • 起動したコンテナに対して、カスタム設定したり、追加ソフトをインストールしたりできる。

サーバー運用に最適

  • Dockerではアプリのデータを保存する部分がコンテナとは別になっているので、コンテナだけ破棄して、再構築するといったmoveが簡単にできる
  • アプリに異常があったら、コンテナを破棄して再構築すればOK
  • バージョンアップもコンテナごと破棄して、再構築すればOK

従来のサーバー運用であれば、アプリの異常時にシステムを停止して原因解明やリカバリなどで、サーバーを元通りにして復旧していたが、Dockerは上記のような運用ができるので、ダウンタイムを最小限に抑えられる。神。

仮想マシンと仮装コンテナの違い

①仮想マシンの場合

  • コンピューターそのものを仮想敵に作り出す
  • つまり物理的なマシンと同じ仕組みで動くので動作が遅くなる
  • さらに、OSのインストール等も必要

②コンテナの場合

  • ハードウェアのエミュレートもしないし、OSも含まれていない
  • コンテナに含まれているプログラムはホストOSがおこなう

※ホストOSとゲストOS
- ホストOS : 仮想マシンを動かしている物理的なマシン上のOS
- ゲストOS : それぞれの仮想マシンにインストールしたOS

=>Apacheが含まれているコンテナを実行した場合、ホストOSがApacheのプログラムを隔離された場所で実行する
- 別のOSを起動しないので、OSのオーバーヘッドがなく、メモリ消費量が格段と少ない

※UbuntuイメージやCentOSイメージは、OS自体が入っているのではなく、そのOSを構成するコマンドやライブラリなどのプログラム一式が入っているに過ぎない。

というわけでコンテナにOSが入っていないため、Linux上でしか使えない。

Docker利用に必要な要件

  1. 仮想化支援機能を搭載したCPU
  2. Linux環境

Linuxの環境構築方法

  1. 物理マシンを用意する
  2. 普段使っているPCに仮想マシンを用意する
  3. Windowsで「Docker for Windows」を使う

=>VirtualBoxで仮想マシンを準備するのが、ラクだし汎用性がありますね。

※全体像
Windows/Mac=>VirtualBox=>Linux(ubuntu)=>Docker=>コンテナ

Linux環境でDockerを使う流れ

  1. LinuxOSにDockerをインストールする
  2. Dockerで作りたいAPサーバー(=コンテナ)のベースとなるOSイメージをダウンロードする
  3. Dockerでダウンロードしたイメージから、コンテナを作成する

とりあえず1章はここまで。この本だとWindowsで実行していくので、Macで実行すべく別の資料なり探してみます(°_°)

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

docker でwordpress環境構築

忘れないように覚書。
【参考サイト】https://tech.recruit-mp.co.jp/infrastructure/post-11266/

docker-composeを準備

https://docs.docker.com/compose/wordpress/
こちらの公式のdocker-composeを利用します。

リポジトリを任意の名前で作成し、その中で
docker-compose.ymlを作成します。

docker-compose.yml
version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}

中を上記のように編集します。

docker-compose up -d

で起動します。そうすれば
http://localhost8000 でログインできます。

 テーマを作る

docker exec -it ドッカー名 /bin/bash 

上記のコマンドでdockerの中に入り、
作りたいテーマのファイルを用意。
exitでdockerから抜ける。

cd /var/www/html/wp-content/themes/
mkdir mytheme
exit

リポジトリにもテーマ用のファイルを用意。

mkdir mytheme

そしてdocker-composeの中にvolumesの記述を以下のように書き足せばOK!
これでホストマシンのテーマファイルがドッカー内のテーマファイルにマウントされます。

    wordpress:
        image: wordpress:latest
        ports:
            - "9000:80"
        depends_on:
            - db
        environment:
            WORDPRESS_DB_HOST: "db:3306"
        env_file: .env
        volumes:
            - ./mytheme:/var/www/html/wp-content/themes/mytheme

{ホスト側の相対パス}:{コンテナ側の絶対パス} という書き方になっています。

書き換えた後に再度docker-compose upすれば、テーマの中を自由にいじることができるようになります!

 おまけ 操作上必要になるコマンド

ドッカーの停止

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

RailsでAPIを作ってJson形式のレスポンスを返すまで

はじめに

まとめておくと後で楽だよねってことで

環境

Ruby 2.6.5p114
Rails 6.0.1

環境自体はDockerコンテナに押し込めているので、まずはDockerの設定周りからやっていきます。

Docker関連

docker-compose.yml

こちらの記事を参考にしつつ作成しています。
ENTRYPOINTは自分が理解しきれてないので指定していません。
なくても動きます。

後、重要なのはmysqlのvolumesとして/etc/mysql/conf.dがいったん必須です。
mysql8からデフォルトの認証方式がパスワードになっていないので、それをパスワードにするために永続化して設定ファイルを入れてあげないと困ります。
認証方式変わったということは、それをデフォルトとして他を変えるのが本筋ですが、公開するサービスではないので、いったんパスワードにしています。

version: '3'
services:
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '13306:3306'
    volumes:
      - ./mysql:/var/lib/mysql
      - ./mysql-confd:/etc/mysql/conf.d
  rails:
    build:
      context: .
      dockerfile: ruby/Dockerfile
    command: bash -c "bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      - "DATABASE_HOST=db"
      - "DATABASE_PORT=13306"
      - "DATABASE_USER=root"
      - "DATABASE_PASSWORD=password"
    volumes:
      - .:/app
    ports:
      - "13000:3000"

Dockerfile

こちらはRails6からwebpackerを入れる必要があるため、その設定は入れています。
今回はAPIを作るようのアプリケーションにするため、この設定はまるごといらないのですが、削除しなくても困らないので残しています。
APIモードで作らない場合はdocker-compose.ymlのcommandでインストールしてあげるかDockerfile内でインストールしてあげればOKです。

あと重要なのは最後にRUN bundle installを入れておくぐらいでしょうか。
Gemを書き換えた際にはイメージを書き換えないと駄目っぽいのですが(本当か?)、これやっておかないと起動時にGemがないと言われて怒られます。
1個目の方はRails関連のGemを入れるようなので、別にやっておかないとアプリケーションの方のGemの読み直してくれません。


ここも色々参考にさせていただいた記事があるのですが、見つけ次第追記します。
どの記事だっけなぁ・・・

FROM ruby:2.6.5

ENV TZ='Asia/Tokyo'

RUN /bin/cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ENV LANG C.UTF-8

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 nodejs yarn

RUN gem install bundler

WORKDIR /tmp
ADD ruby/Gemfile Gemfile
ADD ruby/Gemfile.lock Gemfile.lock
RUN bundle install

ENV APP_HOME /app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD . $APP_HOME

RUN bundle install

Gemfile

バージョンは適宜読み替えてください。
Gemfile.lockは参考にある通り空ファイルです。

Gemfileはrailsアプリケーションを生成した際に上書きされると困るので、rootフォルダとは別の場所に置いてあります。

source 'https://rubygems.org'

gem 'rails', '~> 6.0.1'

railsアプリケーションの生成

ファイルの準備が出来たらアプリケーションを生成します。

docker-compose run rails rails new . --api --force --no-deps --database=mysql

config/database.yml

立ち上げる前にデータベースの設定を書き換えます。
passwordとhostの書き換えだけです。

docker-composeでアプリケーションを作成するとネットワークをいい感じで作ってくれるため、hostをサービス名で設定しておけばいいので楽ですね。
docker-composeで設定したportsもこのネットワーク外部から繋いだ時のものでネットワーク内部からは普通のportで接続となります。

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db

database作成

rakeコマンド使うなり自力で作るなりしてDB作っておいてください。

docker起動

ビルドして起動します。
中々に時間がかかるので、間違えてやり直しをするのが結構辛かったです。

docker-compose build
docker-compose up -d

起動確認

localhost:13000につないで画面出ればオッケーです。
駄目ならエラー見つつ頑張ってください。

routes.rbの設定

起動したので、routesを書いてURLを生成します。
適当でも良いですが、せっかくなので少し真面目に書きます。
resourcesをnewsにしているのは作ろうとしているアプリケーションのURLだからです。

routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :news
    end
  end
end

routeの確認

railsの操作をする際は色んなことをやっていくことになるので、内部で操作していった方が楽だと思います。外からでも操作できるので、どっちでも良いです。
入ってやる方は以下のコマンドで入れます。

docker-compose exec rails bash

確認したらこんな感じで出ると思います。
rails初期から存在するrouteと共に設定したrouteが見れます。

これで上のrouteが正しく設定されたことを確認します。

root@69c223a51ae2:/app# rails routes
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                    api_v1_news_index GET    /api/v1/news(.:format)                                                                   api/v1/news#index
                                      POST   /api/v1/news(.:format)                                                                   api/v1/news#create
                          api_v1_news GET    /api/v1/news/:id(.:format)                                                               api/v1/news#show
                                      PATCH  /api/v1/news/:id(.:format)                                                               api/v1/news#update
                                      PUT    /api/v1/news/:id(.:format)                                                               api/v1/news#update
                                      DELETE /api/v1/news/:id(.:format)                                                               api/v1/news#destroy
        rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                  action_mailbox/ingresses/mandrill/inbound_emails#create
        rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                  action_mailbox/ingresses/postmark/inbound_emails#create
           rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                     action_mailbox/ingresses/relay/inbound_emails#create
        rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                  action_mailbox/ingresses/sendgrid/inbound_emails#create
         rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                              action_mailbox/ingresses/mailgun/inbound_emails#create
       rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#index
                                      POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#create
        rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#show
                                      PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#destroy
rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                      rails/conductor/action_mailbox/reroutes#create
                   rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
            rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
                   rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
            update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
                 rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create

controllerの作成

これもコマンドで作成します。
Controllerの名前はroutesで確認したControllerと同じにする必要があるので、その点だけ注意ください。

rails generate controller api/v1/news

作ったら中身です。

news_controller.rb
class Api::V1::NewsController < ApplicationController

  def index
    render json: { status: 'SUCCESS'}
  end

end

とりあえずのレスポンスを返却するためだけの中身です。
途中でキャッシュ関連で変更が反映されないという問題に直面しましたが、その際はこちらを参考にして解決しました。
Rails6でも同様の方法で解決できるようです。

終わりに

Rails使うならここまではぱぱっと出来るようになりたいですね。
一通りやってみて環境構築周りの注意点とか分かったので、やっぱり手を動かして作るって大事です。

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

MacでDocker上に日本語環境のPostgreSQLを建てる

前書き

  • Docker に PostgreSQL コンテナを日本語環境でサクッと建てたい!
  • PostgreSQL のバージョンは最新のバージョンにしたい!
  • Mac で作業したい!
  • AWS RDS の PostgreSQL とバージョンを合わせた PostgreSQL コンテナを建てたい!

を叶えるためのメモ。
MacでDocker上に日本語環境のMySQL8.0を建てる の PostgreSQL 版です。
タイトルで Mac と謳っていますが上記の MySQL 版と同様に Windows でもほぼ同じ手順です。
違いは Docker のインストーラーが Mac 用か Windows 用かだけです。
なお、このページの文体・構成は上記 MySQL 版 に合わせています。

環境

  • Mac 10.15.1 (Catalina)
  • Docker for Mac

事前準備

  1. Docker Desktop for Mac をインストールする
     参考URL:https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81

  2. Kitematic をインストールする
     →DockerコンテナをGUIで操作できるアプリ。インストール手順はタスクトレイのDockerアイコンから。
      docker-composeで設定した共有ディレクトリやポートなどの確認もこれで行いました。

docker-composeでPostgreSQLコンテナを立てる

参考URL

上記を参考に、PostgreSQLのコンテナを日本語環境で建てる。
特に1つ目のリンクはほぼそのまま参考にさせていただきました。とても詳しく記載されているので、当記事よりは1つ目のリンクを参考にしたほうが良いと思います。

docker-compose.yml
version: '3'
volumes:                           # ※ホストOSで永続化
    postgres_data:
        driver: 'local'
services:
  postgres:
    build:
      context: ./postgres
      dockerfile: Dockerfile
    container_name: "postgres_db"
    environment:
      POSTGRES_PASSWORD: postgres  #スーパーユーザ(postgres)のパスワード
      POSTGRES_USER: test          #ユーザアカウント
      POSTGRES_PASSWORD: password  #ユーザアカウントのパスワード
      POSTGRES_DB: testDb          #DB名
      TZ: "Asia/Tokyo"
    ports:
      - 15432:5432
    volumes:
      - postgres_data:/var/lib/postgresql/data    # ※ホストOSで永続化
      - ./postgres/sql:/docker-entrypoint-initdb.d
Dockerfile
FROM postgres:12.1
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8

上記のディレクトリイメージは以下の通り。

├── docker-compose.yml
└── postgres
    ├── Dockerfile (上記のファイル)
    └── sql (ディレクトリ)

接続確認は適当なDBクライアントでOK。 当方はDBeaverで確認しました。

初期構築用のCreateTableやInsertなどのSQLファイルはsqlディレクトリに放り込むイメージです。

PostgreSQLのファイルをホストOS側で永続化する必要がない(コンテナ内で保持する)場合は、docker-compose.xml の ※ホストOSで永続化 とコメントしている部分の設定を削除してください。

メモ

日本語を使用するための設定が MySQL 版 で記載したものと違うけど?

そもそもMySQLとPostgreSQLの各Docker Hub の Official ページで公開されているコンテナのDockerfile内のロケール・言語の設定が異なるため。
MySQL 版の Dockerfile では元になっているDebianのロケール・言語設定をそのまま使用すようになっているため、ロケール・言語として日本語を使用するための設定が必要になります。
反面 PostgreSQL 版 のDockerfile ではUTF-8を使用する設定がされているため、ロケールだけ変更すればOKとなっています。

AWS RDS for PostgreSQL とバージョンを揃えたい

Amazon RDS for PostgreSQL のよくある質問 を見ると、AWS RDS for PostgreSQL で使用されているバージョンは、
PostgreSQL 9.4、9.5、9.6、10、および 11 となっています。(当ページ記載時点)
そのため、上記バージョンを使用するには Dockerfile の FROMタグ部分を以下のいずれかにすればOK
※ここでは簡素に記載しています。正確な情報・詳細は PostgreSQL の Docker Hub Officialページ を参照してください。

  • FROM postgres:9.4
  • FROM postgres:9.5
  • FROM postgres:9.6
  • FROM postgres:10
  • FROM postgres:11

Postgresコンテナの永続化用のボリュームを削除したい

Docker volumeの削除 を参考に、
当ページ内のコンテナの場合は以下で削除できます。

docker volume rm postgresql_postgres_data

ボリュームの一覧を確認する場合は以下のコマンドです。

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

Docker環境構築するときに(きっと)便利なコマンドたち

この2日ほど、Dockerで色々試行錯誤してたときに便利だったコマンドたちです。

<none>でできたイメージを一括削除

何も考えずに作っているとVMの容量が100%になっちゃうときがあります(ありました)。
docker image pruneでキレイにできます。こちらはimagesではなくimageのようです。

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:18abcdefghijklmnopqrstuvwxyz19abcdefghijklmnopqrstuvwxyz20abcdef
deleted: sha256:21abcdefghijklmnopqrstuvwxyz22abcdefghijklmnopqrstuvwxyz23abcdef
.
.
.

Total reclaimed space: 5.344GB

最後に空いた容量をドヤ顔で教えてくれます。

--rmオプションで終了時にコンテナ削除

基本ですが一応……
--rmオプションをつけると、コンテナを終了させたときにコンテナ自体も削除してくれます。
検証してるときは何度も同じコンテナ名で起動/終了をさせると思うので必須です。
ちなみに-dオプション(バックグラウンド起動)のときにも有効です。昔はできなかった気がするなぁと思って調べたらどっかのバージョンからできるようになったんですね。

commandline
$ docker run --rm -it jupyter/notebook:latest bash

ENTRYPOINTを無効にして起動

DockerfileにENTRYPOINTを指定して、docker run時に何かしらを一緒に起動することができますが、環境構築時はシェルを起動していろいろと検証を行いたいときがあるとおもいます。いちいちDockerfileをいじらなくても、起動時に無効にすることが可能です。

commandline
$ docker run --entrypoint '' --rm -it jupyter/notebook:latest bash

--entrypoint ''と明示的に空にすることで無効にできるようです。

起動しているコンテナからイメージ作成

あるコンテナで作業したのをイメージとして残しときたい、とか思ったときに便利なのがcommitです。

$ docker commit 33aabcdefg33 test-docker-commit-image
sha256:24abcdefghijklmnopqrstuvwxyz25abcdefghijklmnopqrstuvwxyz26abcdef
$ docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
test-docker-commit-image   latest              f2abcdefg1f        7 seconds ago       4.81GB

docker commit <コンテナID> <作成するイメージ名>で指定します。
当然ですが、Dockerfileから作っているわけではないので再現性がありません。

イメージからDockerfile作成

そんな感じでコンテナから残したイメージをDockerfile化したい、といったとき。
そのものずばりなコマンドはないのですが、historyである程度どういう作業をしたかが把握できます。ここからDockerfileに反映していくといいかなと思います。

$ docker history test-docker-commit-image:latest
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
af14d0dd9f68        About a minute ago                                                   19.9MB              
b4abcdefgh51        6 hours ago          /bin/sh -c #(nop)  ENTRYPOINT ["/bin/sh" "...   0B                  
70abcdefgh99        6 hours ago          /bin/sh -c #(nop)  EXPOSE 8888/tcp              0B                  
26abcdefghe5        6 hours ago          /bin/sh -c #(nop) WORKDIR /notebook             0B                  
a6abcdefghaa        6 hours ago          /bin/sh -c #(nop)  VOLUME [/notebook]           0B     

--no-truncオプションをつけるとCREATED BYの部分が省略されずに表示されます。
ただ……試した環境が悪かったのか、手動でapt-get updateapt-get installしたのが表示されずあれ?となってます。ちょっと検証が必要ですがこういうコマンドもあるんだということで。

昔、太古のバイナリファイルしかない環境で、gdbとかnmとかobjdumpを駆使して泣きそうになりながらソースに反映したのを思い出しました……。

参考文献

https://qiita.com/DQNEO/items/e3a03a14beb616630032
https://qiita.com/nju33/items/733e16511f3b8e739d54

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

【Docker】GitHubと連携してDockerイメージを自動ビルドする手順

はじめに

GitHubにpushしたらDockerイメージが自動ビルドできる
Automated Builds
を設定したときの手順を備忘録としてまとめました。

【背景】
先日愛用のMacBookが壊れてしまったのですが、一時的に別端末に開発環境を構築するハメになったのですが、Dockerも使っていなかったので「かなり面倒」でした。

そのため、Dockerfileにも保険をかけておこうと思い導入したのが経緯です。

以下参照した公式サイトです。
Set up automated builds | Docker Documentation

この記事が役に立つ方

  • DockerとGitHubを使用していて、自動ビルドは導入していない方

この記事のメリット

  • 常に最新のDockerイメージがDockerHubにある状態を維持出来る。

環境

  • macOS Catalina 10.15.1
  • zsh: 5.7.1
  • Docker: 19.03.5

前提

今回は開発途中からDockerイメージの自動ビルドを行います。

以下2つのアカウントは取得済とします。

  • GitHub
  • DockerHub

スムーズに設定するためにそれぞれ事前にログインしておきましょう。

1.アカウントの連携

Docker Hubにアクセス。

スクリーンショット 2019-11-26 11.46.36.png
ログイン後、アカウント名をクリックし、Account Settingsをクリック。

スクリーンショット 2019-11-26 11.46.47.png
左側メニューの中からLinked Accountsをクリック。

スクリーンショット 2019-11-26 11.46.58.png
GitHub側のConnectをクリック。
※他にBitbucketも連携可能です。

ログインしていれば出てきた画面でAuthorize dockerをクリックして連携完了。

2.リポジトリの連携

次はリポジトリ同士の連携です。

まずはDockerHubのリポジトリを作成します。
スクリーンショット 2019-11-26 12.05.40.png
①上部メニューのRepositoriesをクリック
Create Repositoryをクリック


スクリーンショット 2019-11-26 12.06.02.png
リポジトリ名とDockerイメージの詳細説明を任意で設定し、GitHubアイコンをクリック。

スクリーンショット 2019-11-26 12.13.06.png
どのリポジトリと連携するかをプルダウンメニューから選ぶ。
必要があればどのBranchを対象として自動ビルドするかも設定する。(デフォルトはmaster)

リポジトリ同士の連携完了。

確認

DockerHubのヘッダーメニューにあるRepositories

作成したリポジトリ名をクリック

Buildsタブをクリック

Automated Buildsにビルドの状況が表示されています。

その他、Recent Buildsからビルドされた時点それぞれのログ、Dockerfile、READMEがブラウザ上で確認出来るようになっています。便利。

おわりに

最後まで読んで頂きありがとうございました:smile:

最新のDockerイメージがDockerHubに上がっていれば、端末が壊れたり別端末から作業をするときにも開発環境をすぐ戻せるので安心ですね:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

Set up automated builds | Docker Documentation

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

CentOS8でdocker buildのdnf installが失敗する

CentOS8でdocker buildのdnf installが失敗する

社内VPNでNginxを立てようとしたときの話。
CentOS8とDockerをインストールし、docker buildを行ったところdnf installが上手くいかずハマったのでメモ。

環境

  • CentOS Linux release 8.0.1905 (Core)
  • Docker version 18.06.3-ce, build d7080c1

  • SELinux

# getenforce
Enforcing
  • Firewalld
# firewall-cmd --list-all
public (active)
target: ACCEPT
icmp-block-inversion: no
interfaces: enp1s0
sources: 192.168.0.0/16
services: cockpit dhcpv6-client http https ssh
ports: 8000/tcp
protocols: 
masquerade: no
forward-ports: 
source-ports: 
icmp-blocks: 
rich rules:

Dockerのサービス変更

# vi /usr/lib/systemd/system/docker.service

Dockerサービスにて生成されるファイルパスの変更とDNS指定を行いました

Environment="DOCKER_OPTS=-g /mnt/HDD/docker/system --dns=8.8.8.8"
ExecStart=/usr/bin/dockerd $DOCKER_OPTS

Dockerサービスを再起動

# systemctl daemon-reload
# systemctl restart docker

Dockerfile

# cat Dockerfile
FROM centos:8 
MAINTAINER name <email>

EXPOSE 80

RUN dnf install -y nginx 
CMD ["nginx", "-g", "daemon off;"]

ビルドしてみた

# docker build -t nginx -f Dockerfile .

dnf installで失敗する。
AppStreamやBase、Extrasに0MBと表示され、dnf install nginxが失敗しました

対策

Dockerfileにpingを書いてみたところ、IPアドレス指定であれば反応があるが、URL指定だと失敗しました。
よって、DNSによる名前解決ができず、dnf installが失敗しているようです。
docker buildで下記のようにホストのネットワークを指定したところ問題なくビルドできました。

# docker build -t nginx -f Dockerfile --network host .
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルで作ったDockerコンテナをCloud Run で公開するまで

Google Cloud Platform Advent Calendar 2019の6日めです。

こちらの記事で作成したDockerコンテナをCloudRunにアップしたい!というわけでやってみました。

必要なもの

  • Google Cloud SDKの最新バージョンのインストール
  • 課金が有効になっているGCPプロジェクト
  • アプリの入ったDockerコンテナ(Dockerはインストールされている前提:sudoなしで実行できるように)  (ベータ版の間は「HTTP」しか対応していないとの噂あり)

環境

  • MacOS Mojave 10.14.6
  • Docker 19.03.4
  • Google Cloud SDK 270.0.0

準備

準備として必要なのは以下です。

  • デプロイ対象のGCPプロジェクトの確認
  • Croud Run の有効化
  • 認証ヘルパーの設定
  • ローカルイメージの編集

デプロイ対象GCPプロジェクトの確認

UP先のプロジェクトが正しいか、gcloudの設定を確認しておきます。
間違えて違うプロジェクトにUPしちゃうと、えらいことになりますので:sweat_smile:
設定を確認するだけならgcloud config listで表示できますが、
もし編集が必要な場合はgcloud config set ~コマンドを使って設定してください。
(もしくはgcloud init?ちょっと調べてくださいー)

$ gcloud config list
[compute]
region = asia-northeast2
zone = asia-northeast2-b
[core]
account = sample@test.com(アカウントのメールアドレス)
disable_usage_reporting = False
project = sandbox-a(プロジェクト名)

CroudRunの有効化

GCPのメニューから、「コンピューティング」 > 「Cloud Run」を
選択すると、こんな画面になります。
「CLOUD RUN の使用を開始する」 をクリックします。

image.png
これで有効化はOK!

認証ヘルパーの設定

次のコマンドを実行して、gcloudを認証ヘルパーとして使用します。
認証方法としては他にも存在するようですが、どれもめんどくさそう高度なので保留にします。
気になる方はこちらをどうぞ。

$ gcloud auth configure-docker

ローカルイメージの編集

UPしようとしていたdockerイメージはこちらの記事にて作成したものです。
開発用にホットリロードつけるため、「docker-compose」を使用しています。
ところがどっこい。CloudRunではdocker-composeは使えないそうです。
(知らなかった。。。)

確かによく考えたら、ホットリロードの設定だったり、
複数コンテナを同時に起動させるだったり、
役割としてはローカル用と言われればその通りかも。
公開したらファイル更新ではなくて、イメージごと更新ですものね。。。
危ない危ない。またモノシリックの罠にハマるところでしたよ。。。

というわけで、Dockerfileを修正します。

Dockerfile

  • ホットリロード用のライブラリの読み込み削除
  • 実行コマンド追加(開発用ではdocker-compose.ymlに記載していた)
# 公式 golang ランタイムを親イメージとして使用
FROM golang:1.13
RUN mkdir -p /attendance/app
WORKDIR /attendance/app
COPY ./app /attendance/app
RUN go get -u github.com/labstack/echo/...
RUN go get golang.org/x/oauth2
#ホットリロード用なのでコメントアウト
#RUN go get github.com/oxequa/realize 
#実行コマンド追加
CMD ["go", "run", "/attendance/app/server.go"] 

server.go

CloudRunのドキュメントには以下の通り書かれてます。
平たい話が、起動するポートは「PORT」っていう名前の環境変数から取得してね
ってことでしょうか。
番外編でやるんですけど、GUIでのデプロイ時にコンテナポートを設定する項目があります。
ここの項目を使うためなのでしょうね。

それもあって、ソース内へのハードコーディングはして欲しくないようです。

Listening for requests on PORT
The container must listen for requests on 0.0.0.0 on the port defined by the PORT environment variable.
In Cloud Run container instances, the PORT environment variable is always set to 8080, but for portability reasons, your code should not hardcode this value.
(PORTでリクエストをリッスンする
コンテナは、PORT環境変数で定義されたポートで0.0.0.0でリクエストをリッスンする必要があります。
Cloud Runコンテナインスタンスでは、PORT環境変数は常に8080に設定されますが、移植性の理由から、
コードでこの値をハードコーディングしないでください。)

https://cloud.google.com/run/docs/reference/container-contract?hl=ja

というわけで、以下の内容を追加しました。

server.go
import "os"  //追加

func main() {
    e := echo.New()
    routing(e) // ルーティング
  // 追加 ここから ---------------------
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
  // 追加 ここまで ---------------------

    e.Logger.Fatal(e.Start(":" + port)) //[port]部分を変数に修正
}

タグつけ & Container Registry へのPUSH

上記の通り修正した者たちに、次のコマンドを実行して、pushしたいイメージに
タグ付け(命名?!)を行います。

$ gcloud builds submit --tag [ホスト名]/[GCPプロジェクト名]/[GCP内でのイメージ名]:[タグ]

ex) gcloud builds submit --tag asia.gcr.io/sandbox-a/hello:v1

[ホスト名]
 イメージのUP先のリージョンを設定します。
 居住地にかかわらず、以下の4つの中からどこでも好きなところを指定できます。

  • gcr.io :米国(今後、場所が変更になる可能性あり)
  • us.gcr.io :米国(ただし、gcr.ioからは独立したストレージバケット)
  • eu.gcr.io :欧州連合
  • asia.gcr.io :アジア

[GCPプロジェクト名]
 先に確認したプロジェクトIDを設定します。
 ※たまに、プロジェクト名とプロジェクトIDが異なる場合があるそうですが、
  あくまで「プロジェクトID」を指定してください。
 プロジェクトIDに「:(コロン)」が含まれる場合はこちらを参照してください。

[GCP内でのイメージ名]:[タグ]
 ローカルマシン上のイメージ名とは異なるイメージ名を設定する必要があるようです。
 タグは省略可能ですが、その場合、デフォルトで「latest」が設定されます。
 バージョン管理をちゃんと行う意味でも、タグは設定しておいた方がいいかも。

完了すると、こんな感じで表示されます。

ID                                    CREATE_TIME                DURATION  SOURCE                                                                                     IMAGES                                STATUS
exxxxxxx-fxxx-4xxx-8xxx-46xxxxxxxxxx  2019-12-02T06:36:51+00:00  1M12S     gs://sandbox-axxxxxx_cloudbuild/source/15xxxxxxxx.65-abac751c9418xxxxxxxxxxxxxxxxxxxx.tgz  asia.gcr.io/sandbox-axxxxxx/hello:v1  SUCCESS

GCP > [ツール] > [Container Registry]でも確認できます。
(似たような名前がいっぱいあるのは試行錯誤の末消せなくなったのです・・・)
image.png

※ここで設定した「asia.gcr.io〜」のタグはあとで使います。

deploy

いよいよ、デプロイです。
こちらは、GCPの Croud Shell にて実行します。(ここをポチってするやつ)
image.png

以下のコマンドを実行します。
対話型で設定していきます。

$ gcloud run deploy --image [さっき設定したタグ] --platform managed
ex)gcloud run deploy --image asia.gcr.io/sandbox-a/hello:v1 --platform managed

Please specify a region:
 [1] asia-northeast1
 [2] europe-west1
 [3] us-central1
 [4] us-east1
 [5] cancel
Please enter your numeric choice:  1 <-- ここでリージョンを選択

To make this the default region, run `gcloud config set run/region asia-northeast1`.

Service name (hello): 
  --> サービス名。空白にすると、デフォルトの「hello」が採用される。上書きしたければ記載してEnter

Allow unauthenticated invocations to [hello] (y/N)?  y
  --> [y]を選択すると、認証無しになる。アクセス認証を設定したい場合は[N]にする。

Deploying container to Cloud Run service [hello] in project [sandbox-akiando] region [asia-northeast1]
✓ Deploying new service... Done.                                   
  ✓ Creating Revision... Revision deployment finished. Waiting for health check to begin.
  ✓ Routing traffic...
  ✓ Setting IAM Policy...
Done.
Service [hello] revision [hello-00001-yiv] has been deployed 
and is serving 100 percent of traffic 
at https://hello-vlxxxxxxxx-xx.x.run.app

最後に表示されているhttps://hello-vlxxxxxxxx-xx.x.run.appの部分がURL。

というわけで、ブラウザでやってみる。
image.png
よしきた!
これで、デプロイまではできました。

はー、長かった!!

まとめ

とりあえず、作ったDockerコンテナをGCPのサービスに公開することはできました。
GKEでやるつもりだったんだけど、CloudRunを触ってみたくて。。。
GKE版を試してみて、違いを見てみるのも楽しいかもですねー。

あとは認証情報の設定が残っているな。。。

(むしろデプロイの仕方よりも、性能比較の方がいい? 
 ・・・それはどなたかにお任せしますw)

番外編その1: GUIでのデプロイ

GUIでもdeployはできます。
(Container Registryへの登録まではCUIですが)
あまり推奨はされてないという噂を聞きましたが、一応、公式のドキュメントにも
載っていたので記載してみます。

[コンピューティング] > [Cloud Run]を選択して、「サービスを作成」をクリック。
image.png

使用イメージは「選択」をクリックすると、リストが表示されます。
さっきpushしたイメージが表示されてますね。なんて便利。
image.png

で、こんな感じで入力します。
コンテナはさっき選択できたし、サービス名はわかりやすく変更しておきます。
image.png
それから「オプションのリビジョン設定を表示」リンクをクリックします。
基本的にデフォルトから変更することはないと思うんだけど、ビビりなので
MAXインスタンス数だけ、デフォルトの1000から10に変えときました。
(もっと少なくてもいいけど。お金は大事)

合わせてハマりどころだったのが、ポートは今の所8080にしておいたほうがいいのかも。
ちょっとここは変更するとうまくいかなかったので今後検証します。
(噂ではベータ版は8080のみ対応だったみたい。)
image.png

入力できたら「作成」ボタンをクリックします。
ちょっと待ちますが、こんな感じで緑のチェックがついたらOKかと。

image.png

で、赤いところのリンクを押すと、ブラウザに表示される!
image.png

やったね!

番外編その2: docker-credential-gcr

もしかしたらいらないかもだけど。
上記の手順でうまくいかなかったら、これも試してみてください。
(いろいろ試行錯誤しながらだったので、もしかしたらいるのかも。多分関係ないと思うんだけどなー)

 $ gcloud components install docker-credential-gcr
 $ docker-credential-gcr gcr-login

image.png

こんな文字がブラウザに表示されたらOK。
image.png

参考

https://cloud.google.com/container-registry/docs/pushing-and-pulling?hl=ja
https://toranoana-lab.hatenablog.com/entry/2018/10/22/123848

Special Thanks!!

となりの堤さん

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

no space left on deviceエラーの対応

> docker exec front df -i
Filesystem                  Inodes   IUsed               IFree IUse% Mounted on
overlay                    3907584 3907459                 125  100% /
# skip
osxfs          9223372036854775807 3103606 9223372036851672201    1% /root/dev
/dev/sda1                  3907584 3907459                 125  100% /etc/hosts
# skip

TL;DR;

macでdocker-compose upしたらINODES枯渇で落ちてしまう問題に効果のあったのはvolume削除

  • コンテナ削除も少し効果あり
  • イメージ削除もごく僅かに効果あり

今回の no space left on device の原因

  • docker-compose up 時に遭遇
  • 原因がディスク容量不足ではない
    • 仮に容量不足ならdocker.appの設定を変更するだけなので話が早い
  • コンテナの中にINODESを食い尽くしているファイルは特に無い
    • フレッシュなオフィシャルイメージでコンテナ作った段階でのINODES100%
    • そもそも削れるファイルが無いケース

volume削除

一番効果あった。自分の場合は30%以上減らせた

# activeでないvolumesを確認する
docker system df
# dangling volumeのリストを確認
docker volume ls -q -f dangling=true
# 参照されてないvolumesを削除
docker volume rm `docker volume ls -q -f dangling=true`

コンテナ削除

若干効果あった。自分の場合は利用率を10%弱減らせた

docker rm xxxx使ってないコンテナxxxxx

イメージ削除

わずかに効果あり。1-2%削減
docker system dfでactiveでないイメージが相当ある場合は試してもよいかもしれない

# danglingイメージや、<none> 表示になってるイメージを削除
docker rm xxxx使ってないコンテナxxxxx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む