20210308のdockerに関する記事は18件です。

【Docker】 「Hello from Docker!」から半歩進んでみる

この記事の目的

  • Docker初心者が「Hello from Docker!」の半歩先に進む手助けをすることで、コンテナやDockerに慣れてもらう
  • Dockerを使って簡単なWebページを表示する

ターゲット

  • インストールだけはしたけど使い方が分からない人
  • 難しいことはいいから、とりあえず動かしたい人

前提条件

  • Dockerをインストールしている
  • hello-world(公式イメージ)などで動作確認が出来ている

※インストールがまだの方は以下の記事を参考にして下さい
Windows 10 64-bit: Pro, Enterprise, Educationの場合
Windows10 ProにDocker Desktopをインストールする

Windows 10 Homeの場合
Windows 10 HomeへのDocker Desktop (ver 3.0.0) インストールが何事もなく簡単にできるようになっていた (2020.12時点)

macOSの場合
Dockerインストール手順<macOS向け>

動作確認

インストールしてから時間が経って忘れてしまっている方のウォーミングアップも含めて、hello-worldを実行してみましょう。

hello-worldの実行
$ docker run hello-world

以下の通りに出力されれば成功です。
(すでに一度実行している場合は、Hello from Docker! から下が出力されます。)

hello-worldの結果
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:89b647c604b2a436fc3aa56ab1ec515c26b085ac0c15b0d105bc475be15738fb
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Webアプリを構築する

先ほどはDocker公式が用意しているhello-worldを使いました。
今回は、Goという言語でHello World!が表示されるWebアプリを構築しましょう。
構築手順は、大まかに以下の通りです。

  1. GoでWebアプリ作成
  2. Dockerfile作成
  3. DockerfileからDockerイメージを作成
  4. DockerイメージからDockerコンテナを作成・実行

Dockerfile、Dockerイメージ、Dockerコンテナの説明や関係性は後ほど簡単に説明しますが、先に詳しく知りたい方は以下のサイトがおすすめです。
Dockerイメージの理解を目指すチュートリアル

GoでWebアプリ作成

下記のサイトを参考に、画面にHello World!を表示するWebアプリを作成します。
Go / Gin で超簡単なWebアプリを作る

作成するWebアプリのディレクトリ構成は以下です。

フォルダ構成
任意のフォルダ
│
└─go
    │ ★ go.mod
    │
    └─src
        │ ★ main.go
        │
        └─templates
               ★ index.html

上から順に作成します。今回はGoの学習がメインではありませんので、コピーで構いません。

go.mod
module m

go 1.16

require github.com/gin-gonic/gin v1.6.3
main.go
package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("/go/src/templates/*.html")

    router.GET("/", func(ctx *gin.Context) {
        ctx.HTML(200, "index.html", gin.H{})
    })

    router.Run()
}
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Go App in Docker</title>
</head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

Dockerfileを作成

Dockerfileをgoフォルダと同階層に作成します。

Dockerfileの場所
任意のフォルダ
│ ★ Dockerfile
│
└─go
    │  go.mod
    │
    └─src
        │  main.go
        │
        └─templates
                index.html

Dockerfileってなに?

Dockerfileとは、Dockerイメージを作成するための定義ファイル(設計書みたいなもの)です。Dockerfileをビルド(build)するとDockerイメージを作成出来ます。

今回は、任意のフォルダに以下のDockerfileを作成してください。ファイル名を「Dockerfile」として下さい。

Dockerfile
# golang v1.16.0をベースイメージに指定
FROM golang:1.16.0
# Docker内での作業ディレクトリを指定
WORKDIR /go
# go.modをコピー
COPY ./go/go.mod ./
# Go modulesの設定
ENV GO111MODULE=on
# Go modulesを使用するため、GOPATHは削除
ENV GOPATH=
# Goの依存パッケージダウンロード
RUN go mod download
# Webアプリを作業ディレクトリにコピー
COPY ./go/src/ ./src/
# 待ち受けポートを8080に指定
EXPOSE 8080
# アプリ起動コマンド
CMD ["go", "run", "./src/main.go"]

Dockerfileについて詳しく知りたい方は折りたたみを読んで下さい。

Dockerfileを読み解く

Dockerfileを読み解く

Dockerfileにコメントもありますが、理解を深めるために詳しく説明していきます。

# golang v1.16.0をベースイメージに指定
FROM golang:1.16.0

FROMは、基本的にはDockerfileの最初に記述するコマンドで、タグを用いてベースイメージを指定します。ベースイメージとは、公式などがDocker Hubというリポジトリに公開している汎用的なDockerイメージです。
Dockerfileを作成する際に全てを定義するのは大変なので、自分の用途に適したベースイメージをDocker Hubから探して、2行目以降でカスタマイズします。
今回はGo言語でアプリを作成しますので、golang:1.16.0というタグを指定しています。
どのようなタグでベースイメージを取得できるかは、こちらで確認できます。

# Docker内での作業ディレクトリを指定
WORKDIR /go

Docker内での作業ディレクトリを指定します。
以降のCOPY命令の右側やCMD命令で./と書かれているものは、/goとなります。

# go.modをコピー
COPY ./go/go.mod ./

COPYは、ファイルパスやフォルダパスを指定してコピーするコマンドです。
今回は、Dockerファイルがあるフォルダ配下の./go/go.modを、Dockerの./(/go)にコピーしています。

# Go modulesの設定
ENV GO111MODULE=on
# Go modulesを使用するため、GOPATHは削除
ENV GOPATH=

ENVは、環境変数を設定する命令です。今回は、Goのアプリに必要な環境変数を設定しています。

# Goの依存パッケージダウンロード
RUN go mod download

RUNは、Dockerでコマンドを実行する命令です。デフォルトではshell形式で指定して、shell内で実行されます。(その他の方法で実行したい場合はこちら)
今回は、go.modに記述した依存パッケージをダウンロードしています。

# Webアプリを作業ディレクトリにコピー
COPY ./go/src/ ./src/

go.modの時と同様にsrcフォルダをコピーしています。
なんで分けてコピーしてるのかについては説明すると長くなってしまうので省きます。気になる方は以下のリンク先の「おまけ COPYのコツ」を読んでみてください。
DockerfileにてなぜADDよりCOPYが望ましいのか

# 待ち受けポートを8080に指定
EXPOSE 8080

EXPOSEは、待ち受けポートを指定する命令です。今回は8080としています。

# アプリ起動コマンド
CMD ["go", "run", "./src/main.go"]

CMDは、RUNと同様にコマンドを実行する命令です。違いは実行のタイミングです。

命令 実行のタイミング
RUN イメージを作成時に実行
CMD イメージ作成後、コンテナ実行時に実行

GOのWebアプリはコンテナが作成されてから実行して欲しいので、CMD命令で記述しています。
CMD命令は、Dockerfile内で1つしか記述出来ません。複数行記述した場合は、ビルド時に警告が出て最後のコマンドのみ実行されますので注意してください。

DockerfileからDockerイメージを作成

DockerfileというDockerイメージの設計書が完成したので、次はイメージを作成します。
任意のフォルダ直下(Dockerfileがある場所)で以下のコマンドを実行して下さい。

Dockerイメージ作成
$ docker build . -t go-app

コマンドの意味はこんな感じです。
docker build <Dockerfileのファイルパス> -t <名前:タグ>
今回は、Dockerfileと同階層でコマンドを実行しているので、.です。
名前というのは、Dockerイメージを識別するための名前です。タグは、バージョンなどを表すために付けます。今回は名前をgo-appと指定して、タグは省略したためlatestとなります。

では、作成したDockerイメージを確認しましょう。

Dockerイメージを確認
$ docker images

以下のような出力結果ならばDockerイメージが作成出来ています。

Dockerイメージ確認結果
REPOSITORY    TAG                 IMAGE ID       CREATED          SIZE
go-app        latest              463ccbe513cf   37 seconds ago   969MB
hello-world   latest              d1165f221234   1 hours ago     13.3kB
......(省略)

REPOSITORYがgo-appとなっています。Dockerイメージの「名前」や「イメージ名」と説明があったら、基本的にREPOSITORYに表示されている値を指します。

DockerイメージからDockerコンテナを作成・実行

いよいよWebアプリを動かせます!
以下のコマンドでDockerイメージからDockerコンテナを作成・実行しましょう。

Dockerコンテナ作成・実行
$ docker run -p 8080:8080 go-app

DockerイメージからDockerコンテナを作成・実行するときは、runコマンドを使用します。
今回のコマンドの意味は以下です。
docker run -p <ローカルのポート>:<Dockerコンテナのポート> <イメージ名>
-pは、ローカルとDockerコンテナ内部のポートを繋げるためのオプションです。

DockerコンテナとWebアプリの確認

最後にDockerコンテナとWebアプリの動作確認です。
Listening and serving HTTP on :8080と出力されたらlocalhost:8080にアクセスしてみましょう。
ブラウザの画面左上に「Hello World!」が表示されます!

後片付け

このままでは、Dockerコンテナが起動したままになってしまいます。またDockerイメージなども使わないならば削除しておいた方が良いです。(Dockerfileがあればまた作ることが出来ます。)

まずは、以下のコマンドを実行して起動中のDockerコンテナを確認します。

起動中Dockerコンテナ確認
$ docker ps

以下のように何らかの出力がある場合は、停止する必要があります。

起動中Dockerコンテナ確認結果
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS              PORTS                    NAMES
ece7970bbafd   go-app    "go run /go/src/main…"   About a minute ago   Up About a minute   0.0.0.0:8080->8080/tcp   bold_khayyam

上記の出力でCONTAINER IDを確認して、go-appのDockerコンテナを停止します。

Dockerコンテナ停止
$ docker stop {CONTAINER ID}

今回の例では、{CONTAINER ID} = ece7970bbafd です。
もう一度起動中のDockerコンテナを確認します。

起動中Dockerコンテナを再度確認
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

何も表示されていなければ停止しています。

次にDockerコンテナを削除します。-aのオプションを使うと、起動中のコンテナだけでなく停止中のコンテナを含めて確認出来ます。

Dockerコンテナを確認
$ docker ps -a

以下のようにSTATUSExitedで、先ほど停止したコンテナが出力されると思います。

Dockerコンテナ確認結果
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                      PORTS     NAMES
ece7970bbafd   go-app    "go run /go/src/main…"   12 minutes ago   Exited (2) 6 minutes ago              bold_khayyam

以下のコマンドを実行してコンテナを削除します。

Dockerコンテナを削除
$ docker rm {CONTAINER ID}

もう一度Dockerコンテナを確認します。

Dockerコンテナを再度確認
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

何も表示されていなければDockerコンテナの後片付けは完了です。

イメージも同様に片付けましょう。
以下のコマンドを実行して、Dockerイメージを確認してください。

Dockerイメージを確認
$ docker images

go-appのイメージがあると思います。

Dockerイメージ確認結果
REPOSITORY    TAG                 IMAGE ID       CREATED             SIZE
go-app        latest              463ccbe513cf   About an hour ago   969MB
hello-world   latest              d1165f221234   41 hours ago        13.3kB
......(省略)

Dockerイメージも削除しましょう。

Dockerイメージを削除
$ docker rmi {IMAGE ID}

今回の例では、{IMAGE ID} = 463ccbe513cf です。
以下のように出力されれば後片付け完了です。

Dockerイメージ削除結果
Untagged: go-app:latest
Deleted: sha256:463ccbe513cff171877c411423843e2bbc9d6eb1f52e7f59b0f0e857b47fc714

まとめ

いかがだったでしょうか?慣れない作業が多く、疲れてしまった方も多いと思います。
確かにDockerをはじめとしたコンテナ技術は、簡単なシステムの作成にはオーバーヘッドが大きいのでありがたみを感じられなかったかもしれません。
しかし、複雑なシステムを扱うほど問題となる環境構築などを簡単にすることが出来ますので、これからも興味を持って頂ければと思います。

参考サイト

Windows10 ProにDocker Desktopをインストールする
Windows 10 HomeへのDocker Desktop (ver 3.0.0) インストールが何事もなく簡単にできるようになっていた (2020.12時点)
Dockerイメージの理解を目指すチュートリアル
[Docker] Dockerfile を作って Web アプリを構築する
Golang - Dockerfileの最小構成
Go / Gin で超簡単なWebアプリを作る
DockerfileにてなぜADDよりCOPYが望ましいのか

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

初学者がDocker+Django+gunicorn+nginx+PostgreSQL環境でCSSを配信できるようにしてみた

前回の記事では簡単な日記アプリを作りましたが、CSSが配信されていなかったので、今回はその部分を修正していきます。

つくったもの

cssが適用されたページを表示したいと思います。

ログイン画面.PNG

コンテナを起動

$ docker-compose up -d
$ docker-compose exec web bash

静的ファイルの置き場所を変更する

settings.pyを開いて、静的ファイルの置き場所を指定します。

conf/settings.py
# 静的ファイルの配信
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

collectstaticを行い、静的ファイルを指定した場所に集めます。

$ python manage.py collectstatic

ボリュームがマウントされる

今、webコンテナの中でcollectstaticを実行したので、webコンテナ内にstaticフォルダが作成され、静的ファイルはその中に格納されました。下の図で言うと①です。

マウント.PNG

実は、docker-compose.ymlにボリュームマウントを記述しておいたので、作成したstaticフォルダはホストにもコピーされます。 上の図の②です。

docker-compose.yml
services:
  web:
    volumes:
      - './web:/code'

そして、staticフォルダはnginxコンテナにもマウントしているので、静的ファイルはnginxコンテナにもコピーされます。上の図の③です。

docker-compose.yml
services:
  nginx:
    volumes:
      - './web/static:/static'

さらにnginxコンテナにはguicornの設定ファイルがあり、静的ファイルの配信先として先ほどコピーされたstaticフォルダが指定されています。

nginx/conf/gunicorn.conf
location /static {
    alias /static/;
}

以上より、静的ファイルはnginxから配信されるようになります……というのが、私の理解なのですが、どうでしょうか…。間違っていたらご指摘いただけると嬉しいですm(_ _)m

確認する

localhost:80/adminにアクセスすると、今度はcssが適用されたページが表示されると思います。

ログイン画面.PNG

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

vscodeでdocker上のspring bootをデバッグできないとき

dockerをシャットダウンしたり、dockerコンテナを削除した後などであらためて

docker-compose up -d

したときなど、Remote Developmentで コンテナのjava環境が動かない場合がある。

やり方

いったん.gradleフォルダをホスト上のディレクトリに退避させておいて、そこを見に行く

cd && ln -s /usr/local/src/root/.gradle .gradle

SPRING BOOT DASHBOARDを再読み込み
image.png
SPRING BOOT DASHBOARDを再読み込みした場合はvscodeを再起動しないとうまくいかないぽい

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

[AWS] Windows版AWS Cliだけで、ECRにDockerイメージをPushする方法

ECRへのログイン

通常、ECRへのDockerイメージのPushは、ECRリポジトリ管理画面の「プッシュコマンドの表示」から行えます。
この中に、

  1. リポジトリへのログイン
  2. Dockerイメージのビルド
  3. Dockerイメージへのタグ付け
  4. ECRリポジトリへイメージPush

という手順が示されていますが、Windowsの場合、最のリポジトリへのログインが、PowerShellになってます。

image.png

AWS Tools for PowerShell

Windows PowerShell 用 AWS Tools を使用すると、デベロッパーと管理者が AWS のサービスとリソースを PowerShell スクリプト環境で管理できます。Windows、Linux、MacOS 環境の管理に使用するのと同じ PowerShell ツールを使用して、AWS リソースを管理できるようになりました。

となってます。そう、これは、あくまでPowerShell用であって、Windows版 AWS Cliとは別物なのです。
正直、AWS Cliだけで完結したいですよね。

AWS Cliで最初のログインを突破する

これさえできれば、このあとは、dockerコマンドのみでいけるのです。
ということで、AWS Cliコマンドのみでやる方法ですが、実に簡単です。

PowerShell版

(Get-ECRLoginCommand).Password | docker login --username AWS --password-stdin {AWSアカウントID}.dkr.ecr.{リージョン}.amazonaws.com

AWS Cli版

aws ecr get-login-password | docker login --username AWS --password-stdin {AWSアカウントID}.dkr.ecr.{リージョン}.amazonaws.com

単純に、現在使用しているAWSアカウントでの、ECRのパスワードを標準出力経由で、docker loginコマンドに渡してあげればいいだけです。

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

Windows+Dockerの環境が重い問題をunisonで解決する

Windows + Dockerの開発環境でアプリケーションが重い問題をunisonというツールで解決したので共有します。
ネット上にいくつかunisonを使っている記事はあるのですが、より簡単に導入でき、アプリケーションへの影響が少ない方法にたどり着いたので記録を残します。

ちなみに自分はLaravelアプリケーションの開発でこの環境を使用しています。
3か月ほど運用していて、普通に開発している分には大きな問題はありませんでした。
composerでパッケージを全てバージョンアップした際に同期がおかしくなったぐらいです。
一応メリデメを上げると以下のとおりです。

  • メリット
    • アプリケーションの実行が早くなる
  • デメリット
    • 作業開始時にunisonクライアントを起動するひと手間が増える

なお、docker-composeを使用している前提の構成になっております。
それでは見ていきましょう。

大まかな構成について

unisonというファイル同期ツールを使います

以下の図のように、unisonクライアントをWindows側にインストールし、unisonサーバーをdocker-composeのサービスの1つとして起動します。
この2つのunison間でソースコードを同期します。
(既存のdocker-compose.ymlにunisonサーバを追加する事になります。)

unisonで同期したソースコードをDockerボリューム経由で他のコンテナにも共有します。

image.png

サンプルコードを用意しました

本記事の構成をすぐ試せるサンプルを用意しました。
詳しくはリポジトリのREADMEを参照ください。

リポジトリ: https://github.com/t-kuni/docker-unison-example

Unisonサーバーを起動します

docker-compose.ymlにunisonサーバーを追加します。

version: '3'
services:
  # Unisonサーバー
  unison:
    image: onnimonni/unison
    environment:
      - UNISON_DIR=/tmp/unison # 同期したファイルを書き込むフォルダを指定する
    ports:
      - "5000:5000"
    volumes:
      - app-code:/tmp/unison # Dockerボリュームをマウントするフォルダを指定する
    restart: always
  # その他のコンテナ
  apache:
    image: httpd:2.4
    volumes:
      - app-code:/usr/local/apache2/htdocs/ # その他のコンテナにも、Dockerボリューム経由でソースコードを共有する
    ports:
      - "8080:80"
volumes:
  app-code:

準備が出来たらdocker-compose up -dでコンテナを起動しましょう

Unisonクライアントをダウンロードします

以下のページからunisonクライアントをダウンロードします。

unisonクライアント: https://github.com/bcpierce00/unison/releases

サーバーのバージョンと合わせる必要があるため今回は2.51.2のクライアントをダウンロードします。

image.png

docker-compose.ymlと同じ階層にunisonフォルダを作成してunisonクライアントを配置します。

image.png

Unisonクライアントを起動します

unisonクライアントを起動するbatファイルを用意します。

docker-compose.ymlと同じフォルダにsync.batという名前で以下のコードを作成します。

:LOOP
    .\unison\unison . socket://192.168.99.100:5000/ ^
        -repeat watch ^
        -auto ^
        -batch ^
        -prefer newer ^
        -ignore "Path .git" ^
        -ignore "Path .idea" ^
        -ignore "Path node_modules"
goto :LOOP

上記のコードは、unisonを起動してカレントフォルダを同期しています。

IPアドレスの部分(192.168.99.100)にはコンテナのIPアドレスを入力してください。
また、まれにクライアントが落ちる事があるので無限ループで再起動するようにしています

unisonのオプションについて

unisonオプションの意味は以下の通りです。

  • -repeat watch
    • ファイルを監視し変更があれば同期を行います
  • -auto
    • コンフリクト時の確認を自動で許可する
  • -batch
    • 実行時の確認を行わない
  • prefer newer
    • コンフリクト時の解決方法として「より新しい物」を優先します
  • ignore
    • 同期しないファイルを指定します。

ignoreオプションについては、必要に応じて追加してください。
例えばLaravel環境ですとstoreage配下のいくつかのファイルは除外した方が良いです。

その他のオプションについては公式ドキュメントを参照してください。

準備が出来たらsync.batを起動します。
あとは自動的に同期を行ってくれます。

動作確認

以下のコードをindex.htmlとしてdocker-compose.ymlと同じフォルダに配置し、unisonで同期させます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>title</title>
</head>
<body>
    <h1>Hello unison!</h1>
    <p>このメッセージが表示されればunisonでの同期に成功しています</p>
    <p>index.htmlを書き換えて再同期されるか試してみてください</p>
</body>
</html>

192.168.99.100:8080(IPアドレスはコンテナのIPに置き換えてください)に接続して以下の画面が表示されればOKです。

image.png

同期が壊れた時は?

同期に使用しているDockerボリューム(サンプルではapp-code)をdocker volume rmで削除して、再同期してください。

以上です!

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

Windows 10(WSL2) vs Ubuntu 機械学習パフォーマンス計測

300万レコードくらいの機械学習(LightGBM)の前処理、トレーニングにどれくらいの時間がかかるか計測してみました。
比較対象はWindows10 Pro vs Ubuntu 18 です。

実行環境はDockerでWindowsもUbuntuも同じDockerfile、docker-composeから構成されています。
(WindowsのDockerはWSL2を使用)

マシン構成は以下となっています。

  • CPU:Corei7-9700K
  • メモリ:64GB
  • ハードディスク:SSD
  • (今回はGPUは使用していません)

同じタイプのSSDのハードディスクを切り替える構成で、WindowsとUbuntuでハードウェアの優劣はない構成です。

Windows 10 Pro 結果

  • 特微量前処理
    • 1,003秒
    • 16分
  • 学習(LightGBM)
    • 1,116秒
    • 18分

(Windowsで実行時に特にエラーになる事はありませんでした。)

Ubuntu 18.04.5 LTS 結果

  • 特微量前処理
    • 908秒
    • 15分
  • 学習(LightGBM)
    • 1,049秒
    • 17分

結論

Windowsで10%くらいのパフォーマンス減衰がおきている模様。
このくらいの速度感であれば十分使い物になるかなという印象。
一般的なweb系などの開発では体感ではおそらく分からないレベルかと。

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

Ruby2.3.4+Rails5.0.4からRuby3.0.0+Rails6.1.3にバージョンアップした話

はじめに

Heroku-16 Stackで動いているアプリケーションがあり、こちらが2021/6にEOLを迎える。
それに伴いHeroku Stackを上げるとRubyのバージョンも上げないといけなかった。
せっかくだったらこの機会に最新にしようと思い、バージョンを上げたのでその時の注意事項をメモ書き程度に残しておく。

Gemfile

まず元々のGemfile

Gemfile
# 一部抜粋
gem 'rails', github: 'rails/rails', branch: "5-0-stable"
gem 'mysql2', '>= 0.3.18', '< 0.5'
gem 'puma', '~> 3.0'
gem 'rspec-rails', '~> 3.5'
gem 'factory_girl_rails'

Rubyのバージョン指定はしておらず、Herokuのデフォルトのバージョンになっていた。
今回はRubyとRailsのバージョンを上げたいので以下を設定してみた。

Gemfile
ruby "3.0.0"
gem 'rails', '~> 6.1', '>= 6.1.3'

これで環境を作り直そうとした結果以下のエラー

Bundler could not find compatible versions for gem "bundler":
  In Gemfile:
    bundler-audit was resolved to 0.6.0, which depends on
      bundler (~> 1.2)
    license_finder was resolved to 3.0.2, which depends on
      bundler
    rails (~> 6.1, >= 6.1.3) was resolved to 6.1.3, which depends on
      bundler (>= 1.15.0)
  Current Bundler version:
    bundler (2.2.3)
This Gemfile requires a different version of Bundler.
Perhaps you need to update Bundler by running `gem install bundler`?
Could not find gem 'bundler (~> 1.2)', which is required by gem 'bundler-audit',
in any of the sources.

bundler関連の問題でうまく環境が作れないようだった。
どうやらbundlerが1.15.3で作ろうとしているのが問題のようだった。
Gemfile.lockを消した状態でbundlerのバージョンを上げてbundle installした。
bundlerは2.2.11になった。

Gemfile.lock
# 一部抜粋
+ RUBY VERSION
+   ruby 3.0.0p0
BUNDLED WITH
-   1.15.3
+   2.2.11

rexml

gem installすると以下のエラー

LoadError - cannot load such file -- rexml/document

こちらの記事を参考にしました。

Gemfile
gem 'rexml'

factory_girl

DEPRECATION WARNING: The factory_girl gem is deprecated. Please upgrade to factory_bot. See https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md for further instructions. (called from <top (required)> at /usr/src/app/config/application.rb:17)
porter-web-dev | /usr/local/bundle/gems/activesupport-6.1.3/lib/active_support/dependencies.rb:332:in `require': cannot load such file -- rexml/document (LoadError)

factory_girlは非推奨なのでfactory_botにバージョンアップする。

Gemfile
-  gem 'factory_girl_rails'
+  gem 'factory_bot'

rspecファイル内にてFactoryGirlをFactoryBotに置換。
更新が用意してくれている置換コマンドで割といい感じに置換できた。

mysql2

手元の環境はDockerで作成しているがmysql-clientがないと言われたのでdefault-mysql-clientに変更

Package mysql-client is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
Dockerfile
- RUN apt-get install -y mysql-client
+ RUN apt-get install -y default-mysql-client

環境を立ち上げてみたがDBアクセスで以下エラー

Puma caught this error: Error loading the 'mysql2' Active Record adapter. Missing a gem it depends on? can't activate mysql2 (~> 0.5), already activated mysql2-0.4.10. Make sure all dependencies are added to Gemfile. (LoadError)

mysql2のバージョンも上げる。

Gemfile
- gem 'mysql2', '>= 0.3.18', '< 0.5'
+ gem 'mysql2', '~> 0.5.3'

puma

ローカル環境が立ち上がらない。
こちらの記事を参考にしました。

config/initializers/new_framework_defaults.rb
- ActiveSupport.halt_callback_chains_on_return_false = false
+ #ActiveSupport.halt_callback_chains_on_return_false = false

ローカル環境を立ち上げると証明書関連のエラー
こちらの記事を参考にpumaのバージョンも上げる。

Gemfile
- gem 'puma', '~> 3.0'
+ gem 'puma', '~> 5.2', '>= 5.2.1'

Rspec

ローカル環境も立ち上がりある程度動くようになったのでテストを通してみたところ全部失敗した。

Failure/Error:
         raise WrongScopeError,
               "`#{name}` is not available from within an example (e.g. an " \
               "`it` block) or from constructs that run in the scope of an " \
               "example (e.g. `before`, `let`, etc). It is only available " \
               "on an example group (e.g. a `describe` or `context` block)."
         `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).

こちらの記事を参考にしました。

spec/factories/hoge.rb
- FactoryGirl.define do
+ FactoryBot.define do
  factory :hoge do
-    name 'テスト'
+    name {'テスト'}
  end
end
spec/spec_helper.rb
  config.before(:all) do
    FactoryBot.reload
  end

最終的に

以下のようなGemfileになった。

Gemfile
# 一部抜粋
ruby "3.0.0"
gem 'rails', '~> 6.1', '>= 6.1.3'
gem 'mysql2', '~> 0.5.3'
gem 'puma', '~> 5.2', '>= 5.2.1'
gem 'rexml'
gem 'rspec-rails', '~> 4.0', '>= 4.0.2'
gem 'factory_bot'

各バージョンの後方互換を調べる

ローカル環境が起動しひと通りアプリケーションが動くことを確認。
RSpecが全て通ることを確認。
この時点で8割方バージョンアップ完了だったが、念の為各バージョンで後方互換切られているところを中心に調べていくことにした。
Ruby2.3から3.0という記事はなかったので1バージョンずつ調べていった。

ruby2.4
特に問題なさそう。

ruby2.5
後方互換の話は特になし。

ruby2.6
範囲オブジェクトに影響あり。
git grep -i range
git grep "\.\."
あたりで範囲オブジェクトを使っている箇所を調べていった。

ruby2.7
こちらも範囲オブジェクト関連。

ruby3.0.0
1つずつ見てgrepしてみたが影響のありそうなものはそもそも使っていなかったので問題ないと判断した。

rails6.1.3
https://qiita.com/ryohashimoto/items/622c3bcfb3336cb9317e
https://railsguides.jp/upgrading_ruby_on_rails.html
cookiesの話が気になるがrails4との比較をしているのでrails5からのアップデートでは問題なしと判断した。

Heroku

以上の調査と修正によりバージョンアップ問題なしと判断し本番をHeroku-20 Stackに上げてデプロイ。
しかし本番でエラーが発生した。

ActiveRecord::ConnectionNotEstablished: SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol

こちらの記事と同様の事象のようだった。
なんとか解消したかったが原因不明のため、記事と同様にHeroku-18 Stackにしたら問題なく動いた。

まとめ

今回所要時間15時間程度でバージョンアップすることができた。
ここまで早くできたのは以下の要因によるものと思われる。

  • 元のバージョンがそこまで低くなかった。
  • テストが書いてあった。
  • 該当アプリケーションがシンプルな作りで機能がそこまで多くなく、検証すべき項目が少なかった。

せっかくバージョンを上げたので新バージョンで使えるようになった機能とかを使っていきたい。
まだ全然調べられていないがRails6からは標準でバルクインサートできるようになったらしい。
activerecord-importとかgemを後入れしなくても良くなったのは嬉しい。
時間ができたら触ってみることにする。

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

Python development and production environment with debugging in Docker

Developing your application in an isolated environment, like a Docker container, makes it easy to run on different platforms. Developer on different Operating Systems like MacOS, Window or Linux can install the development environment easy and fast with one command to run. No "Works on my machine" problems anymore, you can focus on write your application instead of setup your environment.

We setup a development environment for Python projects using Docker with separated development and production environment. As an example, we create a Flask web application. On the development build we install a debugger and setup remote debug into the Docker container. The production build installs a dedicated web server running our application.
We use the Docker Multistage Build feature to separate a development and production build but using the same Dockerfile.
To debug the application we use Visual Studio Code with Microsoft Python Extension.

Let's get started.

The Project Folder

First we create a Python Project with the following structure.

MyFlaskApp/
├ app.py
├ docker-compose.yaml
├ Dockerfile
└ requirements.txt

The project folder contains the file app.py with Flask application code, the Dockerfile to build the docker images, docker-compose.yaml is used to setup our development environment and requirements.txt contains a list with python modules needed for our Flask application.
Simply create empty files first, we fillup the content in the following paragraphs.

mkdir MyFlaskApp
cd MyFlaskApp
touch app.py
touch docker-compose.yaml
touch Dockerfile
touch requirements.txt

The Application Code

In the app.py file, we just copy & paste the demo HelloWorld application from the Flask documentation for simple demonstration.

app.py
from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'


if __name__ == "__main__":
    app.run(
        debug=True,
        host='0.0.0.0',
        port=5000
    )

The following block

@app.route('/')
def hello_world():
    return 'Hello, World!'

assign the route / to return the static string "Hello World!".
The last block

if __name__ == "__main__":
    app.run(
        debug=True,
        host='0.0.0.0',
        port=5000
    )

starts the Flask application when the script got executed from the command line (e.g. python app.py or python -m app)

The Dockerfile

Copy & Paste the following lines into the Dockerfile.

Dockerfile
FROM python:3.7-alpine AS base

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

ENV FLASK_ENV="docker"
ENV FLASK_APP=app.py
EXPOSE 5000

# Development Stage
FROM base AS develop
RUN pip install debugpy
# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE 1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED 1

# Production Stage
FROM base AS production
RUN pip install --no-cache-dir gunicorn
COPY . .
CMD ["gunicorn", "--reload", "--bind", "0.0.0.0:5000", "app:app"]

We use a Docker feature called Multistage Builds which allows us to build multiple stages (Docker images) with one Dockerfile. Here we build 3 stages:
- base: install required dependencies
- development: install additional development dependencies (e.g. a debugger)
- production: copy the source files into the image and installs a proxy server for production build

The Base Stage

We declare our base stage in the first line inside the Dockerfile.

FROM python:3.7-alpine AS base

The FROM keyword specifies on environment we want to build our application. Since we are creating a python application, we choose a pre build python 3.8 Docker image. The Operating System depends on what we want to develop. We choose Alpine, a lightweight Ubuntu distribution.
With the suffix AS base we declare a stage with the name base. The name is free to choose.
The following commands are related to the base stage until another FROM commands appears.
WORKDIR /app creates a folder for the application in the image and change into that folder.
In in the following two lines

COPY src/requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

we copy the python module dependencies list into the folder app/ and install the dependencies.
With the following three lines we define some environment variables specific for flask.

ENV FLASK_ENV="docker"
ENV FLASK_APP=app.py
EXPOSE 5000

In the line ENV FLASK_ENV="docker" we define an environment for which we can define a .env setting file with settings (e.g. database login information) which flask is looking up
automatically on startup. We don't use the .env file in this tutorial, but keep the structure for additional settings in the future. The second line ENV FLASK_APP=app.py tells flask, which file to run on startup. With the last line EXPOSE 5000 we set the port of the flask application to 5000 which we already set in the app.py file.

The Development Stage

In the development stage we reuse the base stage but install the debugger additionally. We define the development stage with the following line.

FROM base as develop

The following commands are related to the develop stage until another FROM commands appears.

As python debugger we install debugpy.

RUN pip install debugpy

Then we do some python setup, specific for our development environment.

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE 1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED 1

The first line ENV PYTHONDONTWRITEBYTECODE 1 disable the python byte-code generation, an optimization step which should be enabled in the production environment but in the development with frequent code changes results in increased memory on hard disc. If you feel, that your python runs too slow during development, you can enable this setting. The second line ENV PYTHONUNBUFFERED 1 disable the buffer for logs and send messages directly to the docker container runtime.
We don't copy any source files into the docker image, the source code is linked from our local directory with the docker container directly on startup. The setup is done in the docker-compose.yaml file, which we fill up in later.

The Production Stage

In the production stage we don't want a debugger installed and shipped with our production release. So we start again from the base stage.

FROM base as production

We install gunicorn as HTTP server for accessing the application in production.

RUN pip install --no-cache-dir gunicorn

Then we copy our source files into the image, because they don't change anymore for this specific production build version.
bash
COPY . .

In the last line we run the gunicorn HTTP server on startup, who starts our app.py application and listen to port 5000 which we already set in the base stage.

CMD ["gunicorn", "--reload", "--bind", "0.0.0.0:5000", "app:app"]

The Docker Compose File

We use Docker Compose to setup our development environment. In the docker-compose.yaml paste the following.

docker-compose.yaml
version: "3.9"

services:
  flask-app:
    image: my_flask_app-develop
    container_name: my_flask_app-develop
    build:
      context: .
      target: develop
    ports:
      - 5000:5000
      - 5678:5678
    volumes:
      - .:/app
    environment:
      - FLASK_DEBUG=1
    entrypoint: [ "python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "-m", "app",  "--wait-for-client", "--multiprocess", "-m", "flask", "run", "--host", "0.0.0.0", "--port", "5000" ]
    networks:
      - my_flask_app-develop

networks:
  my_flask_app-develop:
    name: my_flask_app-develop

The following paragraphs explain each section the setup

The Services Section

We define all services to use in the services part of the file. We only setup our Flask application here, but could add additional services like a database service in a separate docker image.

With the following lines we define our Flask application service.

services:
  flask-app:
    image: my_flask_app-develop
    container_name: my_flask_app-develop

image tells docker the tag to assign when the image is build. This helps us to find the image later in the image list with the command docker images.
With container_name we give the container a name on startup. If we don't set a name here, randomly assign a name to the spawned container.

Under the build section we tell docker what to build.

    build:
      context: .
      target: develop

The docker-compose.yaml file is located in our application directory and context points to that directory to look for the Dockerfile with the build instructions. With target we say which stage to build. In our case the development stage.

The ports section defines the ports to use.

    ports:
      - 5000:5000
      - 5678:5678

Here we map the local ports 5000 and 5678 to the same ports in the container. Port 5000 is for our Flask application to connect, allowing us to open a browser and view the application under http://localhost:5000. Port 5678 is used by the debugger to listen for debug requests.

With volumes we assign our local application folder with the container.

    volumes:
      - .:/app

This allows us to change the source code without the need to rebuild our docker image after every change.

The environment section allows us to define environment variables.

    environment:
      - FLASK_DEBUG=1

Here we enable the debug mode in Flask, which shows us detailed error messages in the browser instead of an error-page.

entrypoint is the most important section and defines what to run on startup.

    entrypoint: [ "python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "-m", "app",  "--wait-for-client", "--multiprocess", "-m", "flask", "run", "--host", "0.0.0.0", "--port", "5000" ]

We run the debugger first listening on port 5678. Then we assign our Flask application to the debugger and tell the debugger to wait for a client to connect on port 5678. The multiprocess flag allows us to access our running application beside debugging on port 5000 as well. The last part starts our Flask application on port 5000.

The Networks Section

The networks part let us define networks and assign them to services allowing us to control the communication between different services. We define only one network for our Flask application here. For detailed information and how to establish a connection between Docker containers see the Networking in Docker Compose Documentation.

services:
  flask-app:
    # ...
    networks:
      - my_flask_app-develop

networks:
  my_flask_app-develop:
    name: my_flask_app-develop

The Requirements File

The file requirements.txt lists all python modules we need in order to run our flask application. This time the only module we need is flask, so copy and paste the following into the requirements.txt file.

requirements.txt
Flask

We don't set any specific version of flask here, it is up to you if you want to set a fixed version instead (e.g. Flask==1.1.2).

Start the Development Environment

To run our development environment, we only need to run the following command inside our application directory.

Terminal
docker-compose up -d

This builds the develop stage docker image and spawn the docker container of the application. To stop the application container run the following command.

Terminal
docker-compose down

If you want to run docker compose in the foreground, omit the -d flag. You can stop the application then with the CTRL+C key shortcut.

Start Debugging

We use Visual Studio Code to debug our application.

If you want to use PyCharm instead of Visual Studio Code, you need a PyCharm Professional Edition license in order to remote debug into a Docker container. The free PyCharm Community Edition do not support remote debugging.

First we got into our application folder and start Visual Studio Code in that folder.

Terminal
cd MyFlaskApp
code .

If you get an error message code: command not found, then open Visual Studio Code by clicking on the program icon and start the command palette with Command+Shift+P on a Mac (Ctrl+Shift+P on Windows or Linux) and type "Shell Command", then select "Shell Command: Install 'code' command in PATH".

This should open Visual Studio Code with the contents of our application folder.

Install the Python Extension

Click on the Extensions icon on the left side. In the search field write "python" and select and install the Microsoft Python Extension in order to debug.
python_extension.png

Setup Remote Debugging

The debug configuration is saved in the file launch.json inside the folder .vscode/.
The steps to setup your remote debugging differs in order to if a launch.json file already exist in your project folder or if you have to create a new file.

If you are using Visual Studio Code Version 1.54.1 there is a bug not showing Python as debug configuration as explained in the following steps. A workaround is to generate a launch.json file with arbitrary configuration (e.g. choose NodeJS) and then overwrite the contents with the launch.json provided in Add the Debug Configuration to an existing launch.json file

Generate the Debug Configuration File

If you don't have launch.json file, you can generate one with the following steps.
Click on the Debug icon on the left side, then create a launch.json file.

debug_setup1.png
A launch.json file do not exist

If you successfully installed the Python Extension, then you should be able to select Python from the debug configuration list.

debug_setup2.png
Create a launch.json file

Then select Remote Attach.

debug_setup3.png

Then you need to select the remote debugger to connect. We use the defaults host name localhost and port 5678.

debug_setup4.png
debug_setup5.png

Add the Debug Configuration to an existing launch.json file

I you have already a launch.json file in the project directory, you can open the file and add the following configuration,

launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Remote Attach",
            "type": "python",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "."
                }
            ]
        }
    ]
}

or you can also generate the configuration with the following steps.
Click on the Debug icon on the left side, then you should see a debug configuration combo-box instead. Click the down arrow and select Add Configuration....

debug_add_configuration.png
A launch.json already exist

If you successfully installed the Python Extension, then you should be able to select Python from the debug configuration list.

debug_add_configuration2.png
Add configuration to existing launch.json

Then select Remote Attach.

debug_setup3.png

Then you need to select the remote debugger to connect. We use the defaults host name localhost and port 5678.

debug_setup4.png
debug_setup5.png

Set a breakpoint and attach

Open app.py and set a breakpoint at line 7. Then attach to the debugger by clicking the green play button on the left side. Open the application in the browser by going to the url http://localhost:5000. The application should stop at the break point.
debug.png

You can attach and detach as often as you want.

Build the Production Stage

Building the production stage we run the following command inside our application folder.

Terminal
docker build --target production -t my_flask_app .

With the --target parameter we tell docker which stage to build and with the tag parameter -t my_flask_app we give the image an appropriate name. Important is the . at the end telling Docker where to find our Dockerfile, that is the current directory we are running this command.

You can assign any tag you want to the built image, but take care not to use the same take for development and production build.

List the built Images

You can see the built image in the image list with the docker images command.

Terminal
docker images

REPOSITORY                          TAG          IMAGE ID       CREATED        SIZE
my_flask_app                        latest       a72b11d35c59   2 days ago     52.2MB
my_flask_app-develop                latest       5916db28c0ff   2 days ago     80.5MB

You can also see the difference in image size with debugger (80.5MB) and without debugger (52.2MB).

Run the Production build

To spawn a container running the production image use the following command.

Terminal
docker run -d --rm -p 5001:5000 --name my_flask_app my_flask_app:latest

We map the local port 5001 to the container port 5000 here, allowing us to run the production stage and the development stage at the same time.

Stage URL
development http://localhost:5000
production http://localhost:5001

You can list running container with the following command.

Terminal
docker ps

CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                    NAMES
a8bc12d89d9f   my_flask_app:latest   "gunicorn --reload -…"   7 seconds ago   Up 6 seconds   0.0.0.0:5001->5000/tcp   my_flask_app

To stop the container simply use the command

Terminal
docker stop my_flask_app

If you want to run the command in the foreground, omit the -d flag. You can stop the application then with the CTRL+C key shortcut.

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

Sonatype Nexus を多段構成にした話 ~なぜ多段構成にしたのか、何が得られたのか~

まえがき

私の所属する aslead DevOps チームでは GitLab や Nexus Repository Manager(以下、Nexusと記載する)、SonarQube を統合的にユーザへ提供することで、ユーザ(主に開発者)にとって高品質で生産性の高い開発を実現できるような基盤サービスを実現しています。

本記事では Sonatype 社の Nexus を多段構成にする取り組み を取り上げ

  • なぜ多段構成にしたのか(背景)
  • 多段構成にすることで何が得られたのか(効果)

を紹介します。

前提

環境情報

製品:      Nexus Repository Manager (Sonatype 社が提供する Nexus Platform の一つの製品)
バージョン: 3.18.1

aslead DevOpsにおける Nexus の提供形態

  • 利用する プロジェクト毎に Nexus 1インスタンス を提供している。(下図: nexus repository for Project A/B )
    • プロジェクト毎にインスタンスを分けることで認証認可の範囲を明確にし、管理するリポジトリの参照を最小限に抑えている。
  • プロジェクトのユーザ(開発者)は自分が所属するプロジェクトの Nexus にビルド成果物を格納したり、利用するモジュールをインターネットからダウンロードし参照したりすることで開発資材を管理する。基本的には GitLab CIの処理によって格納や参照を行う 。(下図: Direct CRUD modules or CRUD modules via GitLab CI )

Nexus-common-repo_before.png

今回の取り組み

背景

前提にて示した仕組みを用いてサービスを提供するなかでいくつかのプロジェクトから業務課題が共有されました。
その業務課題の一例を以下に示します。

  • 業務課題
    • セキュリティ要件が高いプロジェクトではインターネットから直接モジュールを取得することを禁止しており、プロジェクト支援者は担当する プロジェクトが変わるたびに手動でプロジェクト用の Nexus にモジュールを格納 している。
    • 利用しているモジュールのバージョンが消去された場合に 開発しているシステムが依存解決できなくなり正常動作しなくなるリスク がある。
    • 開発フレームワークの部品やサンプルアプリの ユーザ提供に社内ファイルサーバを利用しており、提供にリードタイム がある。

これらの課題を解決するために Nexus を多段構成にして提供することにしました。

多段構成の仕組み

Nexusを多段構成にする仕組みについてネットワーク構成と認可制御の2点に分けて説明します。

ネットワーク構成

多段構成では前提で示した図の構成とは異なり、下図に示すように各プロジェクトの Nexus から更に共通的な Nexus に接続する方式にしています。(下図: nexus repository for all projects )
我々はこの Nexus を Nexus社内共通リポジトリ と呼んでいます。

Nexus-common-repo_after.png

認可制御

Nexus社内共通リポジトリに各プロジェクト用のユーザを作成し、接続するリポジトリ(maven, npm, dockerなど)毎に どのプロジェクトがどのリポジトリにアクセス可能か を制御する。(下図: Project A User 及び Project B User )
たとえば、Project A の Nexus には docker リポジトリのアクセスを許可せず、maven や npm のみアクセスを許可する といった認可制御を Nexus社内共通リポジトリ 上のユーザへのロール付与によってコントロールする。

Nexus-common-repo_after._roleControldrawio_trimmed.png

設定方法

上記の多段構成の仕組みを具体的に設定手順にすると以下の通りとなります。

  1. Nexus社内共通リポジトリ 側の対象リポジトリの Remote storage をインターネット上のリポジトリのURLにする。
  2. Nexus社内共通リポジトリ 側にプロジェクト用ユーザを作成し、手順1で設定した対象リポジトリの参照ロールを付与する。
  3. プロジェクト側の Nexus の対象リポジトリの Remote storage を Nexus社内共通リポジトリ側の対象リポジトリのURLにする。
  4. プロジェクト側の Nexus の対象リポジトリの HTTP Authentication に Nexus社内共通リポジトリ 上のプロジェクト用ユーザの認証情報を設定する。

多段構成により何が得られたのか

Nexus社内共通リポジトリを導入したことにより、以下の恩恵を受けることができました。

(1)プロジェクト支援者の負担減

業務課題1で挙げた通り、各プロジェクトの開発支援担当のライブラリ管理業務において 手動でインターネットから必要なモジュールを取得してプロジェクトの Nexus に格納していた業務を改善 し、Nexus社内共通リポジトリ 経由でモジュールを取得することが可能になりました。

(2)古いバージョンのモジュールを一時的に保持し、依存解決不可となるリスクを低減

古いバージョンのモジュールがインターネット上から削除されることにより、開発中のシステムがそのバージョンを利用していた場合、システム動作に影響するリスクがありました。これに対して Nexus社内共通リポジトリ を設けることで 一時的に古いバージョンのモジュールを保持し、安心して本格対応を検討できる ようになりました。

(3)開発フレームワーク(OWX)の部品やサンプルアプリを提供できる基盤を整備した

共通的に参照可能な Nexus を設けることで社内で多く利用される OWXという自社製開発フレームワークの部品やサンプルアプリを提供する基盤としても Nexus社内共通リポジトリ が利用できます 。(下図: OWX User の Upload および Project A, B User の Download
権限制御も可能であるため、Nexus社内共通リポジトリ のプロジェクト用ユーザの権限を制御することで開発フレームワークを利用するプロジェクトのみアクセス権限を付与することも可能になりました。

Nexus-common-repo_roleControl_OWX.png

まとめ

今回、aslead DevOps が提供しているリポジトリ管理ツール Nexus の業務課題に対して、多段構成にすることで以下の通り課題を解消しました。

  1. (課題) セキュリティ要件が高いプロジェクトではインターネットから直接モジュールを取得することを禁止しており、プロジェクト支援者は担当する プロジェクトが変わるたびに手動でプロジェクト用の Nexus にモジュールを格納 している。
    • (効果) 多段構成により Nexus社内共通リポジトリ に高頻度で利用するモジュールが格納されているため、セキュリティ要件の高いプロジェクトにおいても Nexus社内共通リポジトリ からモジュールを取得できるようになり、手動でプロジェクト用の Nexus に格納する手間が減った。
  2. (課題) 利用しているモジュールのバージョンが消去された場合に 開発しているシステムが依存解決できなくなり正常動作しなくなるリスク がある。
    • (効果) Nexus社内共通リポジトリ経由でモジュールを取得することで、インターネット上から利用しているバージョンが削除されてもNexus社内共通リポジトリには残り続け、本格対応するためのワークアラウンドとして処置した。
  3. (課題) 開発フレームワークの部品やサンプルアプリの ユーザ提供に社内ファイルサーバを利用しており、提供にリードタイム がある。
    • (効果) Nexus社内共通リポジトリ を用いることで、開発フレームワークの部品やサンプルアプリをユーザがファイルサーバにアクセスして取得する手間を省き、GitLab CI契機で取得できる環境を整備できた。

今後もユーザの業務課題に着目し、解決できるよう取り組んでいきます!

補足

  • Nexus Platform には repository 以外にも lifecycle や firewall、auditor などのエコシステムがあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EdgePCをDockerで運用するためのオレオレコンテナ管理ソフトFarming

はじめに

Dockerコンテナの起動、変更、削除を行うソフト勉強がてらを書きました。

Farmingとは

自身もDocker上で実行するコンテナ管理。
リモートからコンテナ設定情報を取得しコンテナを構成します
* 設定情報にないコンテナ→停止&削除(停止のみの設定も可)
* 設定上にあり実行していないコンテナ→実行(イメージが無い場合はPullして実行)
* バージョン違い→イメージが無い場合はPullして実行
が機能です。

image.png

開発経緯

Edgeデバイス上のDockerでWeb開発する必要があり、デプロイそしてアップデートをどのように
簡略化できるかを考えていました。
当初HashiCorop社のNomadがフィットすると思い試していましたが、日本語のチュートリアルが少なく、
サーバ→クライアントと別PCでの構成に悩む。
やりたい事はシンプルで
* docker run
* docker start
* docker stop
* docker pull
* docker rm
このコマンドをリモートから実行
DockerAPIを実行すれば実現できることが分かりソフトに仕立てました
いづれはEdgePCが複数台になることからjsonファイルをEdgePCが参照しコンテナをオーケストレーションを目指しました

課題

EdgePCのログ収集、構成ファイルダウンロード時の認証やEdgePCのグルーピングなどを組み込みたいと思っています

リポジトリ

DockerHub

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

EdgePCをDockerで運用するためのコンテナ管理ソフトFarming

はじめに

Dockerコンテナの起動、変更、削除を行うソフトを書きました。

Farmingとは

自身もDocker上で実行するコンテナ管理。
リモートからコンテナ設定情報を取得しコンテナを構成します
* 設定情報にないコンテナ→停止&削除(停止のみの設定も可)
* 設定上にあり実行していないコンテナ→実行(イメージが無い場合はPullして実行)
* バージョン違い→イメージが無い場合はPullして実行
が機能です。

image.png

開発経緯

Edgeデバイス上のDockerでWeb開発する必要があり、デプロイそしてアップデートをどのように
簡略化できるかを考えていました。
当初HashiCorop社のNomadがフィットすると思い試していましたが、日本語のチュートリアルが少なく、
サーバ→クライアントと別PCでの構成に悩む。
やりたい事はシンプルで
* docker run
* docker start
* docker stop
* docker pull
* docker rm
このコマンドをリモートから実行
DockerAPIを実行すれば実現できることが分かりソフトに仕立てました
いづれはEdgePCが複数台になることからjsonファイルをEdgePCが参照しコンテナをオーケストレーションを目指しました

課題

EdgePCのログ収集、構成ファイルダウンロード時の認証やEdgePCのグルーピングなどを組み込みたいと思っています

リポジトリ

DockerHub

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

Dockerを使ってDjangoの環境構築してみた

はじめに

今回Dockerを使ってDjangoの環境構築をしたのでまとめてみたいと思います。
また、DjangoではデフォルトでSQLiteが入っているのですが、PostgreSQLで実装したので、その設定もdockerでやりました。何かの参考になれば嬉しいです。

dockerfile

dockerfile
FROM ubuntu:latest
RUN apt-get -y update \
    && apt-get -y upgrade \
    && apt-get install -y locales curl python3-distutils \
    && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
    && python3 get-pip.py \
    && pip install -U pip \
    && pip install psycopg2-binary \
    && mkdir /code \
    && rm -rf /var/lib/apt/lists/* \
    && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
WORKDIR /req
ADD requirements.txt /req
RUN pip install -r requirements.txt

# サーバーを立てる場所に移動(djangoのprojectを作成して以降)
WORKDIR /web-version/app
# これでbashとか使わずにrunするだけでサーバーが立つ
CMD  python3 manage.py runserver 0:8001

FROM ubuntu:latest : ubuntuの環境を持ってきています
RUN : 主な環境構築をします。ubuntu環境ではapt-getを使うことでいろんなものをインストールしたりアップデートしたりできます。
WORKDIR : フォルダを作成してそのフォルダに移動します。
ADD requirements.txt /req : requirements.txtをコンテナに持ってきます。保管する場所はreqフォルダの中です。

WORKDIR /web-version/app
CMD python3 manage.py runserver 0:8001 : この2行は、コンテナを立ち上げた時に、すぐにサーバが起動してほしくて書きました。manage.pyのあるフォルダに移動してサーバを立ち上げているだけです。
ただ、まだプロジェクトを作成していない段階で実行してもmanage.pyがないのでエラーが起きます。
なので、はじめはCMD ["bash"]として、シェルに入ってプロジェクトを作ると良いです。
また、runserverの後にポートを指定することで、全てのIPからのリクエストを受け付けることができて、コンテナからサーバを立ち上げても、localhostで開くことができました。
詳しくは、djangoの公式サイトをみてください。

requirements.txt
django

docker-compose.yml

docker-compose.yml
version: "3"
volumes:
  db-data:
services: 
    web:
        build: .
        container_name: "web-django"
        ports: 
            - "8001:8001"
        volumes:
            - ".:/web-version/"
        # 下二行は-itと同じ役割
        tty: true
        stdin_open: true
        depends_on: 
            - db

    db:
        image: postgres
        volumes:
            - 'db-data:/var/lib/postgresql/data'

今回サービス名はwebとしました。
10行目のvolumes:でカレントディレクトリを作成したweb-versionというフォルダにマウントしています。

tty: true stdin_open: true: これを指定してあげることで、dockerfileで実行する場合のオプション-itと同じ役割をしてくれます。
depends_on : これはコンテナを立ち上げる順番を決めています。今回は- dbとしているので、db,webの順番でコンテナが起動します。今回はwebというサービスがdbというサービスを参照する形になっているので、このようにしています。

volumes: - 'db-data:/var/lib/postgresql/data' : postgresqlではデータが/var/lib/postgresql/dataに保管されます。そのデータをホスト側に保持しておくためにこれを書いています。なぜホスト側でデータを保持しておくのかというと、もしコンテナを消してしまうと、データも全て失ってしまうからです。失っても良い場合は書かなくても大丈夫です。
ホスト側のdb-dataに保持するのですが、そのフォルダを2,3行目のvolumes: db-data:で作成しています。

これでdockerは終わりですが、djangoでpostgresqlを使うためにはdjangoのsetting.pyを変更する必要があります。

settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': '5432',
    }
}

終わりに

今回dockerを使ってdjangoの環境構築をしてみたのですが、やはりdockerは便利だなと感じました。(テスト環境とかデプロイ環境とかいろんなところでコンテナ使えばもっと便利に感じるはず)
ただこうやってアウトプットしてみてもまだ自分の知識が曖昧な部分があるので(runserverのポートの指定のところとか)、それをこれからもっと深堀りしてみたいです。
まだまだ初心者なので、間違っている点などありましたら、教えていただけると嬉しいです。

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

k8s 1.20のDocker非推奨問題でEKSを使用しているプロジェクトが対応すること

※ 本稿は Tech Inside Drecom に掲載された記事、「k8s 1.20のDocker非推奨問題でEKSを使用しているプロジェクトが対応すること」の Qiita 出張版です。


こんにちは!
enza SREチームのmendと申します!
前回のk0sの記事に引き続いて、今回もKubernetes関連の記事になります!

enzaでは現在システムのインフラの一部を、コンテナオーケストレーションシステム:Kubernetes(以下k8s)に依存するAWSのマネージドKubernetesサービス:Amazon EKSで運用しております。

Kubernetesはおおよそ3ヶ月に1回のリリースサイクルとかなり活発に開発が行われており、2020年12月8日にはバージョン1.20がリリースされました。

k8s 1.20のリリースノートでは、コンテナランタイムとしてのDockerが非推奨になり、2021年末リリース予定のv1.23では削除する予定であることが記載されておりました。

今回はk8sのバージョン1.20で話題になったDockerが非推奨となる問題についてのおさらいと、EKSを使用しているプロジェクトの対応方針、enzaチームでの対応方針をまとめたいと思います。

背景

2020年12月2日、Kubernetes BlogでDon't Panic: Kubernetes and Dockerが公開されました。
中にはこのような記載がされています。

Kubernetes is deprecating Docker as a container runtime after v1.20.

この部分だけを見てみると「k8sでDockerが使えなくなる」=> 「Dockerが使えなくなる」といった解釈をされてしまいそうですが、(実際SNSでこのような解釈をした方も結構見られました。)まず問題の要点を見ていきたいと思います。

問題の要点


Don't Panic: Kubernetes and Dockerの内容について少し解説します。

上記の記事では、k8s v1.20からDockerが非推奨になるけど焦らないで欲しいといった内容が書かれています。

重要な部分についてまとめると以下のようになっています。

Dockerで作成したイメージはそのまま使える


「Dockerが使えなくなる」という解釈が生まれていますが、「Dockerで作成したイメージはそのまま使える」が正しい認識です。
k8sではコンテナを「コンテナランタイム(※後に解説)」と呼ばれるコンテナを管理する機能があります。
v1.19まではコンテナランタイムにDockerを使用していましたが、v1.20からはコンテナランタイムにDockerを使用することは非推奨になります。
k8sのコンテナランタイムは端的に言うとコンテナを動かす場ですのでDockerで作成されたイメージは使用できないことはありません。

なぜコンテナランタイムのDockerを非推奨にするのか


k8sではコンテナランタイムに対してContainer Runtime Interface:CRIを通して通信を行っています。

そもそも今まで使用されてきたDockerはCRIに対応していませんでした。
そのためDockerをコンテナランタイムとして使用するために別途dockershimを用意しており、CRI経由のAPIをDockerで使用できるように変換することで対応しておりました。

CRIが2016年ごろに規定された背景から、今回のv1.20からCRIの規定と異なるDockerの対応を打ち切り、CRIの規定に沿った他のコンテナランタイムを使用する方針を推奨する形になったようです。

1.20に更新する際の対応


コンテナランタイムはワーカーごとに設定するものになっております。

もしDockerをコンテナランタイムとして設定したままv1.20に更新するとdockershimが非推奨であるとの警告がでるようになりますので、v1.20に更新する前にCRIに対応した別のコンテナランタイムに変更する必要があります。

コンテナランタイムとは


コンテナランタイムは簡潔にいうとコンテナを動かす場と思っていただければと思います。

コンテナランタイムはワーカーごとに設定されるもので、ワーカー内のkubeletがCRIを通してコンテナランタイムに対して通信を行い、コンテナの管理を行っております。

実際に使用しているワーカーでどのコンテナランタイムを使用しているかを確認する際には、以下のコマンドで確認できます

# dockerがコンテナランタイムに指定されている場合は以下のようになります。
$ kubectl get no k8s-node-name -o jsonpath="{.status.nodeInfo.containerRuntimeVersion}"
docker://18.9.9

Dockerとcontainerdをコンテナランタイムとして使用する場合の動作のイメージは以下の図のようになります。

k8s_CRI.png

図を見て頂くと、Dockerを使用する場合はdockershimがCRIの通信をDocker APIに変換していることがわかると思います。
containerdのようなCRIに対応したコンテナランタイムを使用することで、構造がシンプルになり、マシンのリソースも節約できるメリットがあります。

EKSを使用している場合の対応案


EKSでk8sクラスターを構築している場合、ワーカーで指定しているコンテナランタイムを変更する対応が必要になります。

EKSではEKSに最適化されたAMIがAWS側から用意されていたり、コンテナの実行に特化したAMIであるbottlerocket OSを使用できたりと、様々なアプローチでクラスターを構成する方法があります。
今回はEKSに最適化されたAMIを使用している場合を想定し、コンテナランタイムを変更する対応の案をまとめてみました。

案1:EKS最適化されたAMIの更新を待つ


AWSから公開されているEKSに最適化されたAMIで、コンテナランタイムが変更されたAMIがリリースされることを待ち、リリースされたAMIからワーカーを起動する方法になります。

こちらの方法はAWS側でコンテナランタイムを変更したAMIがリリースされたものを使用するため、開発チームで対応する必要がなく、AWSによってサポートされるAMIのため簡潔に対応することができます。

案2:bottlerocketのAMIを使う


bottlerocket OSというコンテナの実行に特化したOSを使用する案になります。

bottlerocketは既にCRIにcontainerdを使用しているため、k8s v1.20に更新するタイミングに合わせてワーカーのOSをbottlerocketに変更する案になります。

しかし直接SSHで接続できないなどのクセのある部分を確認しているので、採択には事前の検証や試用をしておいたほうが良いでしょう。

案3:カスタムAMIを作成する


3つ目の方法は最終手段的な意味合いが強いですが、コンテナランタイムにcontainerdを指定したワーカーのAMIを作成する案になります。

こちらの方法ではAMIを作成する方針のため、containerdやCRI-Oのように使用したいコンテナランタイムを選択できるなど自由度が高い一方、クラスタを維持するためにk8sの短いバージョンアップ周期に合わせてカスタムAMIのメンテナンスが必要になります。
よって基本的にオススメはできず、案1, 案2の両方が何らかの理由で採択できない場合の最終手段としての扱いです。

案1で記載したEKS最適化AMIをカスタムする対応でも良さそうと最初に思ったのですが、
awsのcontainer-roadmapを確認したところ、
2020年1月19日現在ではコンテナランタイムの移行先に使用されるcontainerdとCRI-Oがサポートされていないとのissueが挙がっていました。

enzaチームの対応方針


先程の案全てを吟味した上でenzaチームでは案1のAWSで対応したAMIの使用を基本方針として進めることにしました。
理由としてはk8s 1.19からサポート期間が長くなること、EKSではマイナーバージョンを約12ヶ月間サポートされることに加えて、案1はワーカーノードのAMIを変更するだけで対応ができ、実装の負荷を他の案に比べて最小限にできるためです。

しかし案1は2021年末にリリース予定のk8s v1.23に更新する際までにAWS側で対応がされていなければ実施できないため、案1が対応されない場合は案2, 案3の優先度で進める方針です。

案2はこれまで使用したことがないOSであるため、SSHを伴う作業が必要になった場合の対応方法等の事前の調査が必要であることから実際に案2を実施するまでの期間が必要になってしまいます。

案3に至っては対象のissueが解消されないと対応できないうえに将来的なメンテナンスを考慮しなくてはいけないため、今後の開発・運用に大きく関わります。

まとめ


今回はk8s 1.20のdocker非推奨問題に備え、EKSを使用しているプロジェクトが対応することをまとめてみました。

docker自体が非推奨となるわけではなく、k8s v1.20コンテナランタイムにdockerを使用することを非推奨となったのであり、k8s 1.20を迎えるenzaチームでは、k8s 1.19からサポート期間が長くなることに加えて、AMIを変更するだけで対応が可能であることから案1の方針としました。

EKSは執筆当時k8sはv1.18までリリースされている状態になっております。
現状EKSではv1.20のリリースの予定は発表されておりませんが、将来に備えて事前に調査してみました。

調査してみたところ現状EKSではv1.20のリリースの予定は発表されておりませんが、
EKSを使用するプロジェクトでは基本的にEKSに最適化されたAMIを使用することがオススメであり、
EKSに最適化されたAMIが使用できないのであればbottlerocket OSカスタムAMIを作成するのも一つの案です。

普段はコンテナを動作させるシステムについて意識することはありませんでしたが、
今回の調査を通して、k8sについてさらに知見を深めることができました。

今後もk8s関連の情報を発信できたらと思います。


ドリコムでは一緒に働くメンバーを募集しています!
募集一覧はコチラを御覧ください!


※ その他の Tech Inside Drecom の記事は、コチラからお探しいただけます!

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

WSL2上のUbuntuでdocker/docker-composeを利用する

はじめに

Windows環境でDockerを動かすためにDocker for Windowsの概要と導入を実施しましたが、PowerShell自体にどうも慣れません...。WSL2のUbuntu上で操作できたほうが色々楽です。今回はWSL2のUbuntu上でdockerをインストールして動かしてみます。

ちなみにDocker Desktop WSL 2 backend | Docker DocumentationのようにWindowsのDocker DesktopのバックエンドをWSL2にするものではなく、あくまでWSL2のUbuntuにdockerを入れるだけです。

環境

  • OS: Windows10 Pro 64bit
    • Version: 1909
    • OS build: 18363.1256
  • WLS2
    • Ubuntu 20.04

dockerインストール

Ubuntuにdockerインストールするのと同じなので、下記の公式ドキュメントを利用します。
参照:Install Docker Engine on Ubuntu | Docker Documentation

WSL2コマンドライン
# 古いバージョンのパッケージの削除
$ sudo apt-get remove docker docker-engine docker.io containerd runc

# aptパッケージの更新と、aptリポジトリを登録するために必要なパッケージのインストール
$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

# Docker公式のGPGキーの追加
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 先程登録したGPGキーが正当なものであるかをfingerprintからチェック
$ 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]

# リポジトリの追加
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# aptパッケージの更新と、dockerのインストール
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

# インストールできているか確認
$ docker --version
Docker version 20.10.1, build 831ebea

# サービスの状態を確認
$ service docker status
 * Docker is not running

# not runnningの場合はスタートさせる
$ sudo service docker start

$ service docker status
 * Docker is running

# テスト
$ sudo docker run hello-world

インストール後、sudoなしでdockerコマンドを実行できるように設定します。
参考:Post-installation steps for Linux | Docker Documentation

WSL2コマンドライン
$ sudo groupadd docker

$ sudo usermod -aG docker $USER

$ newgrp docker

# sudo なしでdockerコマンドを起動できるかチェック
$ docker run hello-world

docker-composeのインストール

更にdocker-composeもインストールします。
参照:Install Docker Compose | Docker Documentation

WSL2コマンドライン
# curlを利用して安定版をダウンロード
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 実行権限を付与
$ sudo chmod +x /usr/local/bin/docker-compose

# シンボリックリンクの作成
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

# テスト
$ docker-compose --version
docker-compose version 1.27.4, build 40524192

最後に

VM上でDockerってパフォーマンス的にどうなのかな...とは思いつつ慣れたLinuxのコマンドラインでDocker環境を構築できるのはやっぱり良いですね。
一通りインストールできたので次はRailsでも動かしてみます。

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

privateなGitHubリポジトリを参照するPoetry on Docker の作り方

はじめに

docker buildkit sshを試してみました。

ファイル

aaa/main.py
# pri_pjはprivateのライブラリ
from pri_pj import hello

print("start")
print(hello("aa"))

mainファイルではプライベートなGitHubリポジトリにあるライブラリ pri_pjhello 関数を使うだけの簡単なpythonファイルです。

pyproject.toml
[tool.poetry]
name = "aaa"
version = "0.1.0"
description = ""
authors = ["va034600"]

[tool.poetry.dependencies]
python = "3.6.*"
# プライベートなGitHubリポジトリ
pri_pj = { git = "ssh://git@github.com/va034600/pri_pj.git" }

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

poetryを使ってプライベートなGitHubリポジトリにあるライブラリを使う設定をしています。

Dockerfile
FROM python:3.6.8

WORKDIR /usr/src/app

ENV POETRY_VERSION=1.0.10 \
    PATH="/root/.poetry/bin:$PATH"

RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/${POETRY_VERSION}/get-poetry.py | python && \
    poetry config virtualenvs.create false

COPY ./pyproject.toml /usr/src/app/pyproject.toml

RUN mkdir -m 700 $HOME/.ssh
RUN ssh-keyscan github.com > $HOME/.ssh/known_hosts
RUN --mount=type=ssh poetry install

CMD tail -f /dev/null

Dockerfile では poetry install をしています。
RUN --mount=type=ssh poetry install
poetry install する際に --mount=type=ssh をつけることでprivateなリポジトリを参照することができます。

docker buildを実行

準備は整ったのでこれでbuildできるかと思います。

$ DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t aaa --ssh default .

docker-composeを実行

docker-composeを直接 buildkitを使ってsshアクセスするやり方が見当たらなかったので、
仕方なく、docker build で作成したイメージをdocker-composeで使うことにしました。

docker-compose.yml
version: '3.5'
services:
  abc:
    image: aaa
    working_dir: /usr/src/app/aaa
    command: python main.py
    volumes:
      - ../aaa:/usr/src/app/aaa
$ docker-compose up

終わりに

以前、Dockerfileからprivateなリポジトリにアクセスするイメージを作った際には、ssh keyをDockerイメージに埋めましたが、これはimageの中にssh keyが残るアンチパターンだった様です。
docker-compose上でbuildkit sshがまだ実現出来なかったのが気になりますが、これで開発する上でdockerイメージにprivateなライブラリを埋め込むことが出来そうです。

参考

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

Dockerの基本操作 ~WEBサーバを起動する~

はじめに

Docker の基本操作コマンドを使って、WEBサーバを起動してみます。

操作の流れ

  1. Docker Hub でイメージを探す
  2. イメージを取得する
  3. イメージからコンテナを起動する
  4. コンテナを停止・再開してみる
  5. コンテナを削除する
  6. イメージを削除する

1. Docker Hub でイメージを探す

まずはじめに、Docker Hub で、目的のイメージを探します。
WEBサーバなので、nginx のイメージを探したいと思います。

1-1. Docker Hub にアクセスし、 Explore をクリックします。
1-2.png

1-2. イメージの一覧が表示されるので、検索ボックスから、 nginx を検索します。
2.png

1-3. nginx をクリックします。
 (Docker Hubには、公式のイメージとサードパーティのイメージがあります。公式のイメージには、OFFICIAL IMAGE タグが付いています。)
3-2.png

1-4. イメージの詳細を確認する。

赤枠のコマンド( $ docker pull [イメージ名] )を実行するとイメージを取得できます。
4-2.png

Tags のページに行くと、nginx イメージのタグ一覧が確認できます。
タグを付けることで一意のイメージ名にし、バージョン管理をします。
$ docker pull [イメージ名]:[タグ名] と実行することで、指定したタグのイメージを取得できます。
タグ名を指定しなければ、最新( latest タグ)のイメージを取得します。
99.png

Description の下の方へスクロールすると、マニュアルが確認できます。
今回は、赤枠のコマンド実行し、WEBサーバを起動します。
5-2.png

2. イメージを取得する

Docker Hub で確認した nginx イメージの取得コマンドを実行します。
$ docker pull nginx
今回はタグは指定せず、最新のイメージを取得してみます。

$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
45b42c59be33: Pull complete
8acc495f1d91: Pull complete
ec3bd7de90d7: Pull complete
19e2441aeeab: Pull complete
f5a38c5f8d4e: Pull complete
83500d851118: Pull complete
Digest: sha256:f3693fe50d5b1df1ecd315d54813a77afd56b0245a404055a946574deb6b34fc
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

イメージが取得できているか、以下のコマンドを実行して確認します。
$ docker images

$ docker images
REPOSITORY                    TAG                 IMAGE ID       CREATED         SIZE
nginx                         latest              35c43ace9216   2 weeks ago     133MB

latest タグの nginx イメージが取得できました。

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

イメージが取得できたので、イメージを元にコンテナを起動します。
コンテナを起動には、以下のコマンドを実行します。
$ docker run -itd --name [任意のコンテナ名] -p [ホストのポート:コンテナのポート] [イメージ名 もしくは IMAGE ID]

$ docker run -itd --name my-container -p 8080:80 nginx
7d1d101ba0d7794e0b7bd81e3da5a49dd09dc523057e1b6ceb8287bd52b35cce

コンテナが起動できたか、確認します。
$ docker container ls もしくは $ docker ps コマンドで起動中のコンテナ一覧が確認できます。

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS              PORTS                  NAMES
7d1d101ba0d7   nginx     "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp   my-container

STATUS が Up (起動中)であることが確認できました。

この状態で http://localhost:8080/ にアクセスしてみてください。
88.png

nginxが起動していることを確認できます。

  • $ docker run コマンド実行時のオプションについて
オプション 省略形 説明 補足
--interactive -i Keep STDIN open even if not attached 標準入出力をコンテナに対して結びつける。つまり、入力した文字はコンテナに渡され、コンテナからの出力が画面に表示されるようになる。
--tty -t Allocate a pseudo-TTY pseudo tty (=pty: 疑似端末/仮想端末)を有効にする設定。(疑似端末は、カーソルキーやエスケープキー、[Ctrl]キーなどで操作するためのもの。)
--detach -d Run container in background and print container ID コンテナと端末を切り離してバックグラウンドで実行するオプション。
--publish -p Publish a container's port(s) to the host ポートフォワーディング

4. コンテナを停止・再開してみる

4-1. 起動中のコンテナを停止・再開してみます。

停止するには以下のコマンドを実行します。
$ docker stop [コンテナ名 もしくは CONTAINER ID]

$ docker stop my-container
my-container

$ docker ps で状態を確認してみます。

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

起動中のコンテナがないことが確認できました。

$ docker ps-a オプションを指定して、停止中のコンテナも含めすべて表示してみます。

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                      PORTS     NAMES
7d1d101ba0d7   nginx     "/docker-entrypoint.…"   30 minutes ago   Exited (0) 12 seconds ago             my-container

STATUS が Exited(停止状態)であることが確認できます。

この状態で http://localhost:8080/ にアクセスしてみてください。
nginx は動いていないので、アクセスできなくなります。
87.png

4-2. 続けて、コンテナを再開したいと思います。
コンテナの再開には以下のコマンドを実行します。
$ docker start [コンテナ名 もしくは CONTAINER ID]

$ docker start my-container
my-container

$ docker ps で状態を確認してみます。

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS         PORTS                  NAMES
7d1d101ba0d7   nginx     "/docker-entrypoint.…"   38 minutes ago   Up 4 seconds   0.0.0.0:8080->80/tcp   my-container

STATUS が Up (起動中)になりました。

ブラウザをリロードすれば、起動していることを確認できます。
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3931373439392f31353236613462312d333438372d343232342d303665642d6638663065333864336230632e706e67.png

5. コンテナを削除する

WEBサーバの起動ができたので、コンテナを削除していきます。
コンテナを削除するには、まず起動中のコンテナを停止状態にする必要があります。

$ docker stop my-container
my-container

停止ができたら、以下のコマンドを実行してコンテナを削除します。
$ docker rm [コンテナ名 もしくは CONTAINER ID]

$ docker rm my-container
my-container

コンテナの削除が完了すると、$ docker ps -a で確認しても表示されなくなります。

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

$ docker rm-f オプションを付けると、コンテナが起動状態でも削除可能です。

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
a7a816853a9d   nginx     "/docker-entrypoint.…"   7 seconds ago   Up 6 seconds   80/tcp    bold_khayyam

$ docker rm -f a7a816853a9d
a7a816853a9d

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

6. イメージを削除する

最後にイメージを削除していきます。
イメージの削除には、以下のコマンドを実行します。
$ docker rmi [IMAGE ID]

$ docker images
REPOSITORY                    TAG                 IMAGE ID       CREATED         SIZE
nginx                         latest              35c43ace9216   2 weeks ago     133MB

$ docker rmi 35c43ace9216
Untagged: nginx:latest
Untagged: nginx@sha256:f3693fe50d5b1df1ecd315d54813a77afd56b0245a404055a946574deb6b34fc
Deleted: sha256:35c43ace9216212c0f0e546a65eec93fa9fc8e96b25880ee222b7ed2ca1d2151
Deleted: sha256:61f2666cb67e4572a31412367fa44567e6ac238226385762ea65670ed39034a8
Deleted: sha256:622fb7fb6a35078e3a2d446bb0e74c6a0cd500e3a211fd17ecbbcea5377ded38
Deleted: sha256:69a8591f1aaa7d694fa79a187886f6690e6e51e8c2bc91727be01a9e87daacd2
Deleted: sha256:8a451c701633832102e10093db7545eada8e5639a1b35bb14afaf48601948802
Deleted: sha256:2edbde38832e9e0e07d113df74817dc736fd49ea2f9c0d7ce8e40e3446b49b82
Deleted: sha256:9eb82f04c782ef3f5ca25911e60d75e441ce0fe82e49f0dbf02c81a3161d1300

$ docker images
REPOSITORY                    TAG                 IMAGE ID       CREATED         SIZE

docker images でイメージが表示されなければ、削除完了です。

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

Pythonで為替レートを取得しチャートとして可視化するまでの手順

概要

最近、シストレの勉強を始めました。

もともと趣味でFXをやっているので投資に関する知識はそれなりにあるつもりですが、シストレに関しては全くの素人であるため何をやれば良いのかいまいちわかっておりません...。

そこで、何はともあれまずは為替レートを取得し、あわよくばそれをチャートに起こすところからスタートしたいなと思い、色々試行錯誤した結果をメモ書きしておきます。

仕様

  • Docker
  • Python3
  • Jupitar Notebook

Jupitar Notebookとは: Webブラウザ上でPythonを記述・実行できる統合開発環境の事。

環境構築

Jupitar Notebookを利用する方法は色々あるみたいですが、今回は公式が配布してくれているイメージを使いDockerでサクッと環境構築していきたいと思います。(めちゃくちゃ簡単でした。)

各ディレクトリ&ファイルを作成

$ mkdir jupyter-note-book-docker
$ cd jupyter-note-book-docker
$ mkdir work
$ touch docker-compose.yml

最終的に次のような構成になっていればOK。

jupyter-note-book-docker
├─ work
├─ docker-compose.yml

docker-compose.ymlを編集

./docker-compose.yml
version: "3"
services:
  notebook:
    image: jupyter/datascience-notebook
    ports:
      - "8888:8888"
    environment:
      - JUPYTER_ENABLE_LAB=yes
    volumes:
      - ./work:/home/jovyan/work
    command: start-notebook.sh --NotebookApp.token=''

コンテナを起動

$ docker-compose up -d

localhost:8888 にアクセスし

スクリーンショット 2021-03-08 0.23.39.png

こんな感じの画面が表示されれば成功です。

コードを実装

スクリーンショット 2021-03-08 0.26.15.png

「work」ディレクトリへ入り、「Notebook」から「Python3」を選択。

スクリーンショット 2021-03-08 0.28.05.png

するとこんな感じでコードを記述できるようになるので、ここに色々と書いていきます。

ライブラリをインポート

まず最初に、今回の作業で必要になる各種ライブラリをインポートしておきます。

import pandas_datareader.data as pdr # 株やFXをはじめとする各金融商品のデータが取得できるライブラリ。
import datetime

from matplotlib import pyplot as plt # データを可視化(チャートなどの形で表示)するためのライブラリ。
import seaborn as sns # 同上
sns.set()

スクリーンショット 2021-03-08 0.36.53.png

上部バーの「▶︎」を押すとコードの実行ができるのですが、このままだと失敗してしまいました。

「ModuleNotFoundError: No module named 'pandas_datareader'」

つまり「pandas_datareaderが見つからないよ」と言われています。とうやらJupyter Notebookにはデフォルトで入っていないライブラリみたいなので、次のコマンドでインストールしていきます。

!pip install pandas_datareader

スクリーンショット 2021-03-08 0.40.21.png

もう一度「[1]」を実行してみてエラーなどが起こらなければ成功です。

為替レートを取得

それでは、実際に為替レートを取得してみましょう。(今回はドル円)
コードは以下の通り。

def get_excahnge_rate(currency_pair, data_source, start, end):
    selected = f'{currency_pair} = X'
    df = pdr.DataReader(selected, data_source, start, end).drop(['Volume', 'Adj Close'], axis = 1)

    return df

end = datetime.date.today()
start = end - datetime.timedelta(days = 365)

usd_jpy = get_excahnge_rate('USDJPY', 'yahoo', start, end)
usd_jpy.head

3行目のDataReader()に渡す引数がポイントとなります。

引数 説明
第一引数 データ名(通貨ペアなどを指定)
第二引数 データソース(どこからデータを取得するか)
第三引数 取得開始年月日(datetime型)
第四引数 取得終了年月日(datetime型)

データソースとしては、

  • FRED
  • OECD
  • Yahoo

など様々なところが指定できるみたいです。今回はYahooを指定しています。

参照: 公式ドキュメント

スクリーンショット 2021-03-08 1.06.55.png

Jupytar Notebookから上記コードを実行すると、1年前から今日までのUSD/JPYレートが取得できているはず。

チャートとして可視化

↑で取得した為替レートをチャートとして可視化してみましょう。

fig = plt.figure(figsize = (10, 8))

ax = fig.add_subplot(1, 1, 1)
ax.set_title('USD/JPY')

sns.lineplot(x = usd_jpy.index, y = usd_jpy.High, ax = ax)
plt.tight_layout()

スクリーンショット 2021-03-08 1.11.34.png

こんな感じでチャートが表示されていれば成功です。

あとがき

だいぶザックリではありましたが、為替レートを取得してチャートとして可視化するまでの手順を紹介させていただきました。

自分もまだまだ初心者なので説明が不足していたり雑になってしまったりしている部分はありますが、何となく雰囲気だけでも伝われば幸いです。これから当分シストレの勉強は続けていくつもりなので、随時更新していくかもしれません。

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

Ubuntu 20.04 Desktop setup

本記事の概要 自宅のマシンにUbuntu を導入した際の環境構築手順を記載しています。 今後は必要なアプリケーションはDockerコンテナを立てて行く予定なので、パッケージはそんなに入れていません。 といっても、基本的にはできる方々が公開してくださった手順通りに実施しているだけですが、自分が躓いた(調べた結果に実行した)ことを記載したので、誰かの役に立てれば幸いです。 各手順ごとに「参考サイト」→「実施作業」の順番で記載しています。 インストール・更新パッケージ インストール時の参考サイト Ubuntu 20.04 LTSインストールガイド【スクリーンショットつき解説】 実施作業 Ubuntu 20.04 LTS Desktop をインストール。 上記参考サイトの 3. ライブUSBでインストーラを起動する ~ 11. 初期設定 を実施。 初期設定 vi の設定 デフォルト状態だと矢印キーを押すとABCDと出ることがあるため、vi コマンドの設定を実施。 参考サイト viエディタの調子がおかしい 【備忘録】ラズパイでviしたら方向キーやバックスペースがおかしい viコマンドの初期設定 vi エディタ 内の vi エディタのカスタマイズ 実施作業 最初はホームディレクトリ以下に .vimrc を作り、設定したが、/etc 以下などホームディレクトリ以外のファイルを編集する際にその設定が反映されない現象が起きたため、vim のインストール、および .vimrc がない場合に読み込まれる「/etc/vim/vimrc」を編集。 terminal $ sudo apt install vim # vim インストール $ sudo cp /etc/vim/vimrc /etc/vim/vimrc_20210306.bak # vimrc のバックアップ $ sudo vi /etc/vim/vimrc 最終行に以下を追記。 "コマンドを画面の最下行に表示する。 set showcmd "閉じ括弧が入力されたとき、対応する開き括弧にわずかの間ジャンプする。 set showmatch "検索時、大文字/小文字を区別しない。 set ignorecase "検索パターンに大文字が含まれている場合は区別して検索する。 set smartcase "インクリメンタルサーチを行う set incsearch "ファイルの保存をしていなくても、べつのファイルを開けるようにする set hidden "マウスの入力を受け付ける。 set mouse=a "行番号を表示 set number "オートインデント有効 set autoindent "ターミナルウィンドウにタイトル表示 set title "文脈によって解釈が異なる全角文字の幅を、2に固定する set ambiwidth=double "Tab幅を4文字にする set tabstop=4 "Tabを半角スペースで挿入する set expandtab "vimが自動で生成する(読み込み時など)tab幅をスペース4つ文に>する set shiftwidth=4 "改行時に自動でインデントを設定する set smartindent "空白文字の可視化 set list "コマンドラインの履歴を50件保存する set history=50 "文字のないところにカーソル移動できるようにする set virtualedit=block "カーソルの回り込みができるようになる set whichwrap=b,s,[,],<,> "バックスペースを、空白、行末、行頭でも使えるようにする set backspace=indent,eol,start "補完リストの表示 set wildmenu "上記TABリストの補完動作 set wildmode=longest,full 日本語の表示に関する3つの問題を回避する 私はデスクトップ環境は日本語を使用しますが、もともとバグがあるようなのでそれを回避します。 参考サイト 日本語の表示に関する3つの問題を回避する 実施事項 terminal $ wget https://launchpad.net/~sicklylife/+archive/ubuntu/fonts-ja/+files/fonts-nt-ui-jp_2_all.deb $ sudo apt install ./fonts-nt-ui-jp_2_all.deb $ rm fonts-nt-ui-jp_2_all.deb Dock にゴミ箱を表示する デスクトップにゴミ箱があって欲しいので表示します。 参考サイト Dock にゴミ箱を表示する 実施事項 terminal $ gsettings set org.gnome.shell.extensions.dash-to-dock show-trash true ディレクトリの英語化 環境は日本語がいいですが、ディレクトリが日本語だとCUIで操作しづらいので、ディレクトリを英語化します。 terminal:terminal $ LANG=C xdg-user-dirs-gtk-update HDDのマウント Ubuntu をインストールしたPCはM.2 SSD(256GB)とHDD(1TB)が内蔵されており、Ubuntu はSSDの方にインストールした。 インストール直後はHDDがマウントできていなかったため、HDDをマウントする。 参考サイト HDDをフォーマットする - Ubuntu 20.04編 実施内容 参考サイトのパーティションを作成してフォーマットする、パーティションをシステム起動時にマウントするを順番に実施。 リモート接続設定 メインPCはWindows なので、そこからリモート接続できる環境を整える。 wifi のipアドレス固定 家庭の事情により、ubuntu PC が有線で接続できないため、無線でIPを固定化する。 参考サイト Ubuntu 20.04 LTSで固定IPアドレスを設定する方法【デスクトップ編】 実施作業 上記参考サイトの 無線(Wi-Fi)でネットワーク接続している場合 を実施。 ssh 接続設定 ssh 接続するための環境設定。 セキュリティを高めるために、ポート番号の変更も行う。 参考サイト Ubuntu 20.04 LTS に SSH Server(openssh-server)を導入しよう 実施事項 openssh-server のインストール。 terminal $ sudo apt install openssh-server セキュリティを高めるため、デフォルトのポート22から任意のポートに変更。 49152-65535の間で任意のポート番号を割り当て。 terminal $ sudo vi /etc/ssh/sshd_config # $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ ︙ #Port 22 # ココの # を取り、任意のポート番号を設定 ︙ #PermitRootLogin prohibit-password PermitRootLogin no # 追記 #StrictModes yes リモートデスクトップ接続設定 基本的にはssh 接続しCUI 操作をする予定だが、GUIで実施したいことも多い。 (というか、まだCUI に精通していないってだけです。。。) そのため、リモートデスクトップできる環境を整える。 参考サイト Windows → Ubuntuにリモート接続 Windows10からのRDP接続(ubuntu20.04 LTS) 実施事項 xrdp および lxde のインストール terminal $ sudo apt install xrdp lxde # .xsessionファイルを作成 $ echo lxsession -s LXDE -e LXDE > ~/.xsession # 再起動後にも起動するよう設定 $ sudo systemctl enable xrdp # XRDP接続ポート変更 $ sudo vi /etc/xrdp/xrdp.ini ポート番号を3389から別のポートに変更。 terminal ; ports to listen on, number alone means listen on all interfaces ︙ ; port=tcp6://{<any ipv6 format addr>}:3389 {FC00:0:0:0:0:0:0:1}:3389 ; port=vsock://<cid>:<port> port=3389 ←ここの数値を任意のポートに変更 ; 'port' above should be connected to with vsock instead of tcp ︙ xrdp 再起動 terminal service xrdp restart リモートディスクトップ接続する場合は、以下のルールに従ってIPアドレスを指定。 IPアドレス:変更後のポート番号 例えば、192.168.2.1に対するRDPの接続ポートを13389に変更した場合は、以下を指定 192.168.2.1:13389 注意事項 Ubuntu PC上で作業しているときにリモートデスクトップでUbuntu にログインすると、Ubuntu PC上で音がでなくなる。 再起動すれば解決するが、起動中に解決する術は今のところ不明。 ファイアウォールの設定 VPS(Virtual Private Server:仮想専用サーバー)ではないが、念の為ファイアウォールを設定。 (外部公開しない場合は、ここまで実施する必要はないと思います) 参考サイト Ubuntu Server 20.04LTSのファイアーウォールを設定しよう 実施事項 ファイアウォールの設定とssh接続/リモートデスクトップ接続の許可。 terminal $ sudo ufw status 状態: 非アクティブ $ sudo ufw default deny # ssh 接続できるように22番ポートを開放 $ sudo ufw allow 22/tcp $ sudo ufw enable $ sudo ufw status 状態: アクティブ To Action From -- ------ ---- 22/tcp ALLOW Anywhere 22/tcp (v6) ALLOW Anywhere (v6) 各アプリのインストール Google chrome のインストール 参考サイト Google Chromeインストール 実施事項 公式から .deb ファイルをダウンロード。 ダウンロードしたディレクトリに移動し、下記コマンド実行。 terminal $ cd Downloads/ $ sudo dpkg -i google-chrome-stable_current_amd64.deb $ rm google-chrome-stable_current_amd64.deb docker インストール 参考サイト Ubuntu 20.04 で docker をインストールする 実施事項 terminal $ sudo apt install -y docker.io docker-compose # sudoなしで実行できるようにdocker グループにユーザを追加 $ sudo usermod -a -G docker $USER # $USER には自分のユーザ名 $ sudo reboot # 上記設定を反映させるため再起動 $ docker run hello-world # Hello from Docker! が表示されることを確認 fish インストール fish が便利だというので、入れてみる。 設定は terminator というアプリでするといいらしいので、それも入れる。 参考サイト fishシェルのインストール 実施事項 terminal $ sudo apt install fish $ sudo apt install terminator ターミナル内で右クリックして[設定]。 名前無しプロファイルの色で背景(transparent background)を適度な透過に。 名前無しプロファイルのコマンドで「SHELL の代わりにカスタム・コマンドを実行する」にチェックを入れて「/usr/bin/fish」を入力。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む