20200103のdockerに関する記事は9件です。

Dockerでlaravelの開発環境構築をした (php-pfm,nginx, mysql) 2019

おはようございます。

タイトルの通りに今日はDockerでlaravelの環境構築経験を述べたいと思います。

私はまだ日本語を勉強中なので。わかりにくいことがあれば申し訳ありません。

2017に同じ内容の記事を投稿しました:https://qiita.com/mytv1/items/f26cd77f2801357dee8f
それでも最近また触りたいと思って、触ってみたらLaravelのバージョンが古くなったとわかりました。新しいパッケージを結合できるように更新したいというきっかけで再度記載します。

よろしくお願いします。

ホストの情報

OS

MacOS High Sierra 10.13.6

Docker

$docker -v
Docker version 19.03.5, build 633a0ea

Docker Compose

$docker-compose -v
docker-compose version 1.24.1, build 4667896b

構築する予定の環境情報

Webserver

Nginx 1.0

Database

mysql 8.0

PHP

PHP:7.4-FPM

構成

構築終わったら構成が下記だと想定します。

|-- app # Laravelを格納するDockerコンテナ
|-- composer # Composerを格納するDockerコンテナ(PHPのcomposerです!)
|-- db # データベースを格納するDockerコンテナ
|-- docker-compose.yml 
|-- web # ウェブサーバーを格納するDockerコンテナ

ディレクトリ構成と実際動いている構成と下記のように関係があると思います。

alt text

appwebdbはそれぞれのコンテナと該当します。

処理フローと言えば、最初にリクエストはWebコンテナに渡されて、その後WebコンテナからAppコンテナに引き渡します。ここにもし必要であればAppコンテナはDatabaseコンテナとクエリーでやりとりして、その結果を処理してからWebコンテナにレスポンスに返し、最後にブラウズに返却します。

構築手順

1.プロジェクトを作成

1.1.新規ディレクトリを作成

ゼロから作成ということで、新規ディレクトリを作成します。名前について私は「docker_laravel_2019」と選びますが、任意です。

$ mkdir docker_laravel_2019
$ cd docker_laravel_2019

1.2.Laravelプロジェクトを新規作成

https://laravel.com/docs/6.x/installation#installing-laravel

Laravelの公式サイトによると作成するにはComposer経由という選択肢がありますね。
私もComposer経由で作成しますが、そのComposerをホストPCにあるComposerではなく、Dockerコンテナとして新規作成します。

理由はホストPCでComposerとPHPのバージョンを管理するのはDockerで管理と比べてちょっと手間ということです。PHPのバージョン、Composerのバージョンを上げたり、下げたり調整するMacosの手順のはちょっと面倒かもしれませんね。
簡単な場合はDockerなら記載し直したらいけると思います。

1.2.1. Composerディレクトリを作成

$ mkdir composer

1.2.2. ComposerのDockerイメージを作成

https://github.com/mytv1/docker_laravel_2019/blob/master/composer/composer.dockerfile

composer/composer.dockerfile

FROM php:7.4-fpm

RUN apt-get update && apt-get install -y libzip-dev

# Extension zip for laravel
RUN docker-php-ext-install zip 

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

RUN composer global require laravel/installer

ベースのイメージは最新のphpバージョンの7.4を選びました。その後はcomposerインストールまたはComposerでlaravelプロジェクトを作成するのに必要なライブラリ(zip)をインストールします。

最後はコマンドのRUN composer global require laravel/installerはLaravelのインストーラーをインストールします。

1.2.3. ComposerのDockerイメージをビルド

これまでComposerのDockerイメージを作成しました。次はコンテナになるようにビルドします。

$ docker build -t localcomposer -f ./composer/composer.dockerfile ./composer

-t localcomposer : イメージの名前というか、タグをlocalcomposerとします。

実行するとインストールするのに数分がかかるかもしれません。

1.2.4. ComposerコンテナでLaravel初期プロジェクトを作成

$ docker run -it -v $(pwd):/var/www/html localcomposer:latest /root/.composer/vendor/bin/laravel new app

-v $(pwd):/var/www/html :ホストのカレントディレクトリをコンテナの/var/www/htmlとマウントします。その新規のプロジェクトが/var/www/htmlに作成がされるので、それをコンテナと一緒に消さないようにホストに残すようにするということです。
app:作りたいLaravelプロジェクトの名前、appとします。

実行終わったら、ディレクトリは下記のようになりますね。

|-- app
|-- composer
    |-- composer.dockerfile

1.3. NginxのDockerコンテナを構築

1.3.1. 新規ディレクトリを作成

$ mkdir web

1.3.2. Nginxの設定を作成

web/vhost.confを作成

https://github.com/mytv1/docker_laravel_2019/blob/master/web/vhost.conf

server {
      listen 80;
      index index.php index.html;
      root /var/www/public;
      error_log  /var/log/nginx/error.log;
      access_log /var/log/nginx/access.log;
      proxy_set_header HTTP_AUTHORIZATION $http_authorization;

      location / {
          try_files $uri /index.php?$args;
      }

      location ~ \.php$ {
          fastcgi_split_path_info ^(.+\.php)(/.+)$;
          fastcgi_pass app:9000;
          include fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_param PATH_INFO $fastcgi_path_info;
          sendfile off;
      }
}

その中にある数点を説明させていただきます。
* listen 80 : 80ポートに聞き耳を立てる
* fastcgi_pass app:9000: FastCGIサーバーのアドレスを指定します。WebサーバーのNginxがリクエストを受けた後はFastCGI(Laravel)に引き渡します。そのアドレスはここにしています。appはホスト名で、後でdocker-compose.ymlに記載されます。9000はデフォルトのポートですね。

NginxのDockerイメージを作成

web/web.dockerfileを作成

https://github.com/mytv1/docker_laravel_2019/blob/master/web/web.dockerfile

FROM nginx:1.10
ADD vhost.conf /etc/nginx/conf.d/default.conf

ベースイメージはnginx:1.10にします。Nginxの設定はホストのvhost.confからADDでコポーします。

これまで構成は下記になりますね。

|-- app
|-- composer
|-- web
    |-- vhost.conf
    |-- web.dockerfile

1.4. MysqlのDockerコンテナを構築

1.4.1. 新規ディレクトリを作成

$ mkdir db

1.4.2. mysql設定を作成

./db/my.cnfを作ります

https://github.com/mytv1/docker_laravel_2019/blob/master/db/my.cnf

[mysqld]
default-authentication-plugin = mysql_native_password

今度私が使うイメージはmysql:8.0で、デフォルト認証方法は普通のパスワードの認証ではなく、caching_sha2_passwordという認証仕方になります。そのままだとlaravelの一般的な認証はいけませんので、mysqlの設定に通常の認証設定に変更します。

mysqlのDockerコンテナは公式な既存のイメージを使います。Nginxのように私は継続し新規で作成しません。ただ設定を上書きます。

これまで構成は下記になります。

|-- app
|-- composer
|-- web
|-- db
    |-- my.conf 

1.5. LaravelのDockerコンテナを構築

1.5.1. Dockerイメージを作成

./app/app.dockerfileを作ります。
https://github.com/mytv1/docker_laravel_2019/blob/master/app/app.dockerfile

FROM php:7.4-fpm

RUN apt-get update && apt-get install -y libzip-dev

# Extension mysql driver for mysql
RUN docker-php-ext-install pdo_mysql mysqli

ComposerのDockerイメージのように、ベースイメージをphp:7.4-fpmにします。その他はpdoまたmysqlドライバーのライブラリをインストールします。

1.5.2. Laravelに必要なパッケージをインストール

$ docker run -v $(pwd)/app:/var/www/html localcomposer:latest composer install

また前述で作成したcomposerのDockerコンテナでインストールします。
1~2分ぐらいかかるかもしれません。

1.5.3. 環境変数を編集

下記のように./app/.envを編集します。

DB_HOST=127.0.0.1
DB_PASSWORD=

DB_HOST=database
DB_PASSWORD=secret

DB_HOST: Laravelから見るデーターベースのホストを指定する場所です。通常はホストで同じで配置されて、127.0.0.1でいいですが、今度はDockerコンテナの間で繋がるということで、ホスト名はDockerコンテナの名称になります(デフォルト該当するネットワーク名はDockerコンテナの名になるでしょう)

DB_PASSWORD: rootとしてログインするパスワードです。この「secret」は後でdocker-compose.ymlで宣言します。

これまでディレクトリの構成は:

|-- app
    |-- ....
    |-- .env    
    |-- app.dockerfile
    |-- ....
|-- composer
|-- web
|-- db

1.6. docker-composer.ymlを作成

docker-compose.ymlはそれぞれのコンテナをモータニングする手順を記載するファイルですね。

私は下記のように設定します。

./docker-compose.ymlを作る

https://github.com/mytv1/docker_laravel_2019/blob/master/docker-compose.yml

version: '3'
services:

  # The Application
  app:
    build:
      context: ./app/
      dockerfile: app.dockerfile
    working_dir: /var/www
    volumes:
      - ./app/:/var/www/

  # The Web Server
  web:
    build:
      context: ./web/
      dockerfile: web.dockerfile
    working_dir: /var/www
    volumes:
      - ./web/:/var/www/
      - ./app/public:/var/www/public
    ports:
      - 8080:80

  # The Database
  database:
    image: mysql:8.0
    volumes:
      - ./db/dbdata:/var/lib/mysql
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
    command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci','--default-authentication-plugin=mysql_native_password']
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    ports:
        - "33061:3306"

volumes:
  dbdata:

今まで作成していたコンテナの名称の通りに、それぞれのコンテナをここに記載します。

  • app : LaravelのDockerコンテナ、ホストのappをコンテナの/var/wwwとマウントします。開発する時にホストで修正するとすぐにコンテナに反映するようにするためです。
  • web : NginxのDockerコンテナ
  • database : DatabaseのDockerコンテナ

これまでディレクトリの構成は:

|-- app
|-- composer
|-- web
|-- db
|-- docker-compose.yml

2.システムを立ち上げる

これまで構築は終わると思います。実行してみましょう〜

$ docker-compose up

立ち上がる1分ほど、http://0.0.0.0:8080で下記の表示が出ると想定します。

alt text

これまで構築が終わります!機能開発はこれから開始されると思います。

その他

上記のインデックスページが出るということはまだLaravelがデータベースと繋がらない可能性があります。
確認するために、マイグレーションを実行してみます。

$docker-compose exec app php artisan migrate --seed

デフォルトはlaravelは認証用のテーブルのマイグレーションが用意されますん。実行するとこういうテーブルが作られますね。ついでにダンプデータも作ります(seed)。実行結果はこんな感じです。

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.04 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.03 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.02 seconds)
Database seeding completed successfully.

無事に実行できたら、繋がることは大丈夫だと思います〜

3.ソースコード

https://github.com/mytv1/docker_laravel_2019

この記事に利用する設定、コードをgithubに上げました。ご参考になればと思います。

お終い

これまでお疲れ様でした〜
この記事は皆の作業に役に立てば嬉しいです。m(_ _)m

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

4年ぶりにエンジニアに戻った人の話

こんにちは、Qiita初投稿です。

色々あって昨年末に4年間勤めていた外資系のゲーム会社でのプロデューサー業を辞めてエンジニアに戻りました。
前職ではエンジニアではなく、翻訳やアカウントマネージメント系の仕事などをやっていました。
エンジニアブログを書く前に、今回は元エンジニアが4年間経ってエンジニアに戻って世界が一変していた、浦島太郎のような話をしたいと思います。

経歴:

2011年: 大手企業に新卒でエンジニア就職

秋入社というのもありまさかの新卒唯一の日本人、まわりは中国人とインド人ばっかり。入社一日目でエンジニア職人気なさすぎオワタと思う。まわりのエンジニアが毎月行われれていた深夜作業に駆り出されていくのを怯える毎日(結局一度もその作業をすることはなかったが)

2013年: ベンチャー企業へ転職

前職ではテスト業務しか触れず、いつの間にかポジションもQAエンジニアになってしまったのでエンジニアらしいことがしたいと一念発起して、当時黎明期だったモバイルアプリ業界へ転職。たまたま入社直後にアプリがヒットし、会社が急成長。カスタマーサポート担当→QAエンジニア→サーバーエンジニアと役割を変え、気づいたらエンジニアリードになっていった。

2015年: 外資系ゲーム会社へ転職

ヘッドハンティングで当時日本サーバーを建てようとしていた世界的人気のゲームを運営する会社へ入社。エンジニアではなく語学力+QA経験でLocalization QAを任される。そのうち、「君これもできるよね?」というノリでユーザーコミュニケーション担当をやっていたら、そちらが本業になりLocalizationチームから外れてMarketingチームへ異動。Abemaでテレビ番組のプロデューサーをやっていたら、半年で今度は当時のesports担当が外れたので急遽、責任者に就任。

2019年: 現在の会社へ

前職を8月末に辞表を出し11月末に退社(理由は色々あったので割愛)。就職先を決めていなかったので、「いい機会だし色んな業界の話を聞いてみよう」と多くの会社に面接に行っていたら現在の社長と意気投合、はれて4年ぶりにエンジニアに復職。

エンジニアに戻ってきて

前置きが長くなってしまいましたが、ここからが本題。まず転職時の感想ですが。

エンジニア職が好待遇に

昔はエンジニア職といえば3K(Kitsui, Kitanai, Kaerenai)のイメージが強かったです。実際「え、エンジニアなんてやってるんですか?」と大学時代の知り合いに言われたぐらいで、エンジニアなんてさっさとやめてマネージメントに入るのが勝ち組なんて影では言われていました。

正直直近の転職活動の時も、最初は「エンジニア戻るのはないなー」と思って、それでも経歴上エージェントから話が来るので渋々話を聞きにいっていたのですが。

…あれ?エンジニア職の待遇変わってね?

聞くと、今IT系エンジニアの求人倍率は10倍なんですね。昔は「私の代わりはいくらでもいるもの」という感じだったのですが、みんなどこへ行ってしまったのか…当然ながら給与も想像より上、同じように応募した翻訳やプロデューサー業よりも上でした。

技術の進歩

エンジニアに4年ぶりに戻るとあって、有給消化中はほぼ毎日10時間ぐらい今の技術を勉強に費やしていました。
完全にキャッチアップとはまだまだ遠いですが、思ったことを簡単に書くと、

サーバーインフラ構築が簡単に

本当にこれ、当時(2013年ぐらい)だとAWSのEC2とS3とCloudFront使ってサーバーエンジニアが汗と涙で毎回デプロイしてたのに、DockerとEBS組み合わせたら何も考えずにサーバーが建てられる、しかもコンテナモデルだからデプロイが速い速い。あとサーバーレスアーキテクチャを使ったマイクロサービスとか、昔だったらEC2の別サーバーでバッチ処理させるしかなかったのに、凄い(求: 語彙力)

Javascriptって便利じゃない?

昔のJavascriptには何度泣かされたことか。しかしいざ、Vue.js触ってみて思ったのが「これ以外の言語で非同期処理って書けるの?」とまで思わされてしまうほど、Javascriptが使える言語になっていたのに驚き。でも言語仕様はやっぱり好きじゃない

Pythonが主流言語に

これが地味に一番嬉しい。大学で時代を先取りしすぎた教授たちに「明日から課題はPythonで提出して」(当時はJavaで提出していた)と言われかれこれ10年以上Pythonを使っているのに、日本でPythonを使うのはスクリプト処理の時ぐらい。サーバーはRuby on Railsだったのも今は昔、若手のエンジニアから「RoR使ってるのは30代後半のおっさん」と言われ軽くショックを受けました。…ええそのせいもあって、弊社はDjangoでサーバー書いてます。

最後に

ここまでつらつらと書きました。エンジニアに戻ってまだ日も浅いのでまだまだわからないことが多いのですが、4年ぶりに戻ってきて思っていることは

  1. エンジニアは雇うから、育てる職種に
    エンジニアの待遇が良くなったという反面、優秀なエンジニアを採用するというのが本当に難しい時代になったと思います。反面、今までのような技術的なハードルは下がってきているので、きっちりと育てることのできるエンジニアをいかに採用し戦力としていくことが重要に思えます。

  2. エンジニアマーケットのグローバル化
    Pythonなどの言語が主流言語として台頭してくる中で、多くのドキュメントなどのリソースは英語で書かれており、海外には多くのコミュニティも存在します。そのため、日本語だけだと最先端の技術を取り込んでいくための情報量に限りがあります。10年ぐらい前からも言われていることですが、エンジニアとして今優先すべきは英語での語学力だと思っています。合わせて、採用も日本人だけでなく海外の技術者を取り込んでいくことが重要なファクターとなると考えています。

2をふまえて、今の会社でのエンジニア採用は「英語のみ可」に入社後に変更し、社内での英語の義務化を導入するか目下検討しています。1は会社のフェーズとして育てられる環境がないのでまだできていませんが、組織が安定してき次第取り掛かりたいと思っています。

ではでは

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

ラズパイ4にdocker,docker-composeをインストール

Docker

# dockerをインストールする
$ curl -sSL https://get.docker.com | sh

# pi,dockerユーザに docker コマンドが使えるように権限を与える
$ sudo usermod -aG docker pi

# ここで一度ラズパイをリブートする

# dockerがインストールされたかを確認する
$ docker -v
Docker version 19.03.5, build 633a0ea

# dockerが動くかテストする
$ docker run hello-world

# ラズパイ起動時にdockerも起動するように設定
$ sudo systemctl enable docker

docker-compose

# docker-composeをインストールするための依存関係のものを先にインストールする
$ sudo apt-get install libffi-dev libssl-dev
$ sudo apt-get install -y python3 python-pip3

# docker-composeをインストールする
$ sudo pip3 install docker-compose

# docker-composeがインストールされたかを確認する
$ docker-compose -v
docker-compose version 1.25.0, build b42d419

参考

Installing Docker and Docker Compose on the Raspberry Pi in 5 Simple Steps - DEV Community ?‍??‍?

Install Docker and Docker-Compose on your Raspberry Pi - Jonathan Meier

Yes, you can run Docker on Raspbian | With Blue Ink

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

x509: certificate signed by unknown authority の対応

目的

Dockerfileにgo getしようとすると、↓このようなエラーが発生してたので、解決法をメモ
x509: certificate signed by unknown authority

調べた結果、
go getnpmはSSLを経由して実行しています。なので、証明書をdockerに食わせないといけない。

解決方法

OSのcafile.pemDockerfileにコピーします

COPY cafile.pem /usr/local/share/ca-certificates/
RUN apk add --no-cache ca-certificates && \ 
    update-ca-certificates

hoge.pemはオレオレ証明書生成できると思う
参考:
http://rikuga.me/2017/12/24/oreore-ca-and-ssl-cert/
https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6

コード

一応簡単なgitコードはこちらになります。

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

GoのS3 ListObjectsはPythonより20秒遅いのか?問題

はじめに

S3から大量Objectをダウンロードする場合、Objectサイズに関わらず中々速度が出ないですよね。
Pythonで書いている時も、concurrent.futuresなどで頑張ってたのですが、もしかしてGoroutineで出来るのでは?と思い、Golangデビューしてみました。

やろうと思ったこと

  • ListObjectV2を用いて、S3の特定Prefix配下のKeyをすべて取得
  • 取得したKeyをGoroutineでいい感じにダウンロード

実際起こったこと

  • ListObject部分を試しに書いてみたが、はっきり言って遅い。
  • なんならPythonで書いた方が早い気が?

ん?API叩くだけなので同じ速度。だったらまだ納得は出来るが、スクリプト言語よりGoの方が遅いというのはちょっと気になる。
予定を急遽変更して、本件を少し検証してみました。

ソースコード

Go版

というわけでこちらこちらを参考に書いてみる。

main.go
package main

import (
    "fmt"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
    "os"
)

func main() {
    bucket := os.Getenv("BUCKET")
    prefix := os.Getenv("PREFIX")
    region := os.Getenv("REGION")

    sess := session.Must(session.NewSession())
    svc := s3.New(sess, &aws.Config{
        Region: &region,
    })
    params := &s3.ListObjectsV2Input{
        Bucket: &bucket,
        Prefix: &prefix,
    }
    fmt.Println("Start:")
    err := svc.ListObjectsV2Pages(params,
        func(p *s3.ListObjectsV2Output, last bool) (shouldContinue bool) {
            for _, obj := range p.Contents {
                fmt.Println(*obj.Key)
            }
            return true
        })
    fmt.Println("End:")
    if err != nil {
        fmt.Println(err.Error())
        return
    }
}

Python版

こちらもサクッと書いてみる。
Goと条件を合わせる為、低レベルクライアントで。

main.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import boto3
bucket = os.environ["BUCKET"]
prefix = os.environ["PREFIX"]
region = os.environ["REGION"]

# r = boto3.resource('s3').Bucket(bucket).objects.filter(Prefix=prefix)
# [print(r.key) for r in r]
# 普段は上記の様に取得するが、Golangへ寄せるため下記のコードにて測定

s3_client = boto3.client('s3', region)

contents = []
next_token = ''
while True:
    if next_token == '':
        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix)
    else:
        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix, ContinuationToken=next_token)

    contents.extend(response['Contents'])
    if 'NextContinuationToken' in response:
        next_token = response['NextContinuationToken']
    else:
        break

[print(r["Key"]) for r in contents]

環境

サーバ等

  • 基本的にCloud9 on EC2(t2.micro)で実行。

ビルド・デプロイ等

  • 環境を汚したくない&面倒なので、全部Dockerで構築。
$ docker-compose up -d --build
  • ちなみに構築資材は下記を参照。
Dockerfile
FROM golang:1.13.5-stretch as build
RUN go get \
  github.com/aws/aws-sdk-go/aws \
  github.com/aws/aws-sdk-go/aws/session \
  github.com/aws/aws-sdk-go/service/s3 
COPY . /work
WORKDIR /work
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go

FROM python:3.7.6-stretch as release
RUN pip install boto3
COPY --from=build /work/main /usr/local/bin/main
COPY --from=build /work/main.py /usr/local/bin/main.py
WORKDIR /usr/local/bin/
docker-compose.yml
version: '3'
services:
  app:
    build:
      context: .
    container_name: "app"
    tty: True
    environment:
      BUCKET: <Bucket>
      PREFIX: test/
      REGION: ap-northeast-1

S3バケット

東京リージョンにバケットを一つ作って、以下のツールで1000件程度作成。

#/bin/bash

Bucket=<Bucket>
Prefix="test"

# テストファイル作成
dd if=/dev/zero of=testobj bs=1 count=30
# マスタファイルのコピー
aws s3 cp testobj s3://${Bucket}/${Prefix}/testobj
# マスタファイルを複製
for i in $(seq 0 9); do
    for k in $(seq 0 99); do
        aws s3 cp s3://${Bucket}/${Prefix}/testobj s3://${Bucket}/${Prefix}/${i}/${k}/${i}_${k}.obj
    done
done

測定

測定結果(1000 Object)

  • Go
$ time docker-compose exec app ./main

~略~

real    0m21.888s
user    0m0.580s
sys     0m0.107s
  • Python
$ time docker-compose exec app ./main.py

~略~

real    0m2.671s
user    0m0.577s
sys     0m0.104s

GoがPythonより10倍遅い。なんでや!

Object数を増やしてみる

  • もうすこしobjectを増やしてみましょう。とりあえず10000件あたりで。
#差分のみ
for i in $(seq 0 99); do
    for k in $(seq 0 99); do
  • ちなみにアップロード完了までに3、4時間かかりました。ツールはちゃんと作っとけばよかったね…

再測定結果(10000 Object)

  • Go
$ time docker-compose exec app ./main

~略~

real    0m23.276s
user    0m0.617s
sys     0m0.128s
  • Python
$ time docker-compose exec app ./main.py

~略~
real    0m5.973s
user    0m0.576s
sys     0m0.114s

今回は4倍程度の差。
というよりObject数によらず18秒ほど差が出ている様子。うーむ。

終わりに

  • ライブラリの設定起因か、言語仕様の理解不足な気もしているので、もう少し情報を漁ってみたい。
  • そもそもの目的であるGoroutineでの並列ダウンロード処理の効率が良ければ20秒程度は誤差な気もするので、残りも実装してみます。

気になるところ

  • よく見るとuser, sysは同じ程度なのでS3でI/O周りが怪しい。
  • goのコードを雑にprintデバッグしたところ("Start:", "End:")、list objectが処理時間の大半を占めている様子。もしやboto3とはS3設定のデフォルト値が異なるのだろうか。
  • 同じコンテナで動かしてるので、T系インスタンスのCPUクレジット問題やNW帯域の差も関係ないと思うが……
    • 前者はm5.largeに代えても解決しなかったので関係なさげ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Docker] laravelからmysql接続時にエラー 「php_network_getaddresses: getaddrinfo failed: Name or service not known」

DockerでLAMP環境を構築し、Laravelからmysqlに接続(php artisan migrate)しようとした際にエラーが発生。
つまづいたので、メモします。

エラー内容

   Illuminate\Database\QueryException  : SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (SQL: select * from information_schema.tables where table_schema = database and table_name = migrations and table_type = 'BASE TABLE')

エラー内容を見たところ、mysqlに接続しているホスト名が違うらしいので、laravelの.envファイルとdocker-composer.ymlを確認。

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database
DB_USERNAME=hoge
DB_PASSWORD=hoge
docker-compose.yml
version: '3'

services:
  db:
    image: mysql:5.7
    container_name: db-host
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: database
      MYSQL_USER: hoge
      MYSQL_PASSWORD: hoge

別の方法でmysqlに接続

ホスト名は間違ってなさそうだと思い、データベースマネージャ(TablePlus)を使い接続を確認。
スクリーンショット 2020-01-03 14.39.18.png

Ver: Mysql 5.x
Host: 127.0.0.1
Port: 3306
User: hoge
password: hoge
Database: database

TablePlusではつながりました。

もしや問題はDocker特有の何か、、??

DockerのDBコンテナ関連について調べた

こちらの記事によるとコンテナ内からのコンテナに接続する際はホスト名がIPでは無理らしく、service名で接続だそう

Laravel内の

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database
DB_USERNAME=hoge
DB_PASSWORD=hoge

.env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=database
DB_USERNAME=hoge
DB_PASSWORD=hoge

に修正。

つながった:relaxed:

その他 参考記事

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

AWS FargateでSeleniumを使い定期的にチェックする環境を作ってみた

TL;DR (5行)

  • 頻繁に手動で更新するようなWebページ(今回は例としてZOZOTOWNを使います)を自動チェックしたい
  • Fargateを使ってSeleniumの実行環境を作ります
  • ローカルのコンテナをECRにpushしてFargateにデプロイします
  • Seleniumによるブラウザ操作をスケジュール設定し,バッチ処理として動作させます
  • バッチ処理の結果は,CloudWatchにてチェック

注意

本記事はFargate+Selenium導入紹介を目的としています.
筆者は内定者で許可もとっているので,題材として自社サービスのZOZOTOWNを使ってます!
もし,記事内容を流用する場合はマナーや規約に違反しない範囲でお願いします!

使用するサービスの説明

Fargateとは

通常コンテナをEC2で運用する場合,インスタンス管理する必要がありますが,Fargateの場合,インスタンス管理はAmazon側に任せ,コンテナを登録するだけでサーバレスにコンテナを動作させることができるサービスです.

サーバレスで有名なサービスとしてLambdaがありますが,こちらはコンテナを使えなかったり,タイムアウトなどの制約から柔軟性に欠ける部分があります.

一方で,Fargateはローカルで実行しているコンテナをそのまま登録して使えるため,多様なサービスの提供が可能です.

Seleniumとは

Webアプリのテストを自動化するためのブラウザ駆動型テストツールです.

PythonやRuby,Javaなど多様な言語に対応しており,簡単にテストスクリプトを作成することができます.

アーキテクチャ

今回は,AWS上に以下のようなアーキテクチャを構築します.

スクリーンショット 2020-01-03 11.00.20.png

Dockerfileとメインコードの作成

Selenium + Pythonでテストスクリプトを作成します.

Dockerfileの作成

./Dockerfile
FROM joyzoursky/python-chromedriver:3.7-alpine3.8-selenium

WORKDIR /usr/src
ADD main.py /usr/src

CMD ["python", "main.py"]

今回,Selenium+Headless Chromeを使うにあたって,
ベースイメージはこちらを使っています.

joyzoursky/python-chromedriver:3.7-alpine3.8-selenium
https://hub.docker.com/r/joyzoursky/python-chromedriver/

メインコードの作成

./main.py
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException, NoSuchElementException

def check_coupon(driver, my_favorite_brand):
    # ZOZOのクーポンページに遷移
    driver.get("https://zozo.jp/coupon/")
    i = 1
    while True:
      try:
          coupon_brand = driver.find_element_by_xpath(f'//*[@id="body"]/div[3]/ul/li[{i}]/a/figure/div[2]').text
          if coupon_brand == my_favorite_brand:
              return True
          i += 1
      except NoSuchElementException:
          return False

if __name__ == '__main__':
    try:
        # Headless Chromeの設定
        options = webdriver.ChromeOptions()
        options.add_argument('--no-sandbox')
        options.add_argument("--disable-setuid-sandbox")
        options.add_argument('--window-size=1420,1080')
        options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        # Headless Chromeブラウザに接続
        driver = webdriver.Chrome(options=options)
        # seleniumの動作タイムアウトを15秒間に設定
        driver.implicitly_wait(15)

        # 好きなブランド
        my_favorite_brand = "Carlie e felice"
        # クーポンのチェック
        if check_coupon(driver, my_favorite_brand):
            print("見つけたよ! ", my_favorite_brand)
        else:
            print("今日は見つけられなかった・・・")

    # 例外処理
    except ElementClickInterceptedException as ecie:
        print(f"exception!\n{ecie}")
    except TimeoutException as te:
        print(f"timeout!\n{te}")
    finally:
        # 終了
        driver.close()
        driver.quit()

クーポンページにブランド:Carlie e feliceがあるかどうかチェックします.

Requests + Beautiful Soup 4でスクレピングしても良かったのですが,今回はSeleniumを使った環境を構築したかったので,まさかり投げないでください;;

ローカル環境でコンテナの実行

# コンテナの構築
$ docker build -t zozo_check_coupons .

# コンテナの実行
$ docker run -it --rm zozo_check_coupons
見つけたよ!  Carlie e felice

無事,ローカル環境で実行できたことを確認したら,次はこのコンテナをAmazon ECRにプッシュします.

ECRはAWS上のプライベートなDocker Hubみたいなイメージです.

AWSの必要環境の構築

ECRの環境を構築

ECRのリポジトリ作成

ECRに今回管理したいコンテナ専用のリポジトリを作成します.

  • サービスから,ECRを選択し,リポジトリの作成を選択
    2.png

  • リポジトリ名「zozo_check_coupons」を入力し,リポジトリを作成
    スクリーンショット 2020-01-02 21.21.16.png

  • 無事,リポジトリを作成することができました
    4.png

このときの,リポジトリのURIはコンテナをプッシュするときに,使うのでメモしておきましょう.

ECRにログイン

$ aws ecr get-login --region ap-northeast-1 --no-include-email
docker login -u AWS -p ...
.
.
. .dkr.ecr.ap-northeast-1.amazonaws.com

# 返ってきたdocker login ~をコピペして打ち込む
$ docker login -u AWS -p ...
Login Succeeded

Login Succeededと表示されたらOKです

ECRにプッシュ

先程,メモしたリポジトリのURLをコピペし,作成したリポジトリにプッシュします

# リポジトリのURIでタグを付ける
$ docker build -t xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/zozo_check_coupons .
# タグをつけたコンテナをECRにプッシュする
$ docker push xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/zozo_check_coupons

コンテナを無事リポジトリにプッシュできました
1.png

イメージのURIは,タスク定義で使うので,メモしておきましょう

ECSでクラスター作成

コンテナを動作させる環境であるクラスターを作ります

  • サービスからECSを選択し,クラスターの作成を選択します.
    3.png

  • クラスターテンプレート「ネットワーキングのみ」を選択
    スクリーンショット 2020-01-02 22.31.59.png

  • クラスター名を入力し,VPCの作成にチェックします.
    スクリーンショット 2020-01-02 22.33.54.png

  • 最後に,作成ボタンを押せばクラスターが作成できます
    5.png

ECSでタスクの定義

次に,タスクの定義をします.

  • 新しいタスクの定義の作成を選択します
    6.png

  • 起動タイプの互換性の選択でFargateを選択
    スクリーンショット 2020-01-02 22.56.44.png

  • 以下のようにタスクを定義します
    スクリーンショット 2020-01-02 22.57.26.png

タスク実行ロールが何もない場合は,下の余談を参照し,作成してください

  • コンテナ追加を選択し,コンテナ名と先程プッシュしたコンテナイメージのURIをここでコピペします
    スクリーンショット 2020-01-02 22.59.54.png

  • メモリとCPUを設定
    スクリーンショット 2020-01-03 01.07.10.png

  • 最後に,作成を選択してタスク定義完了です
    7.png

余談 : CLIでタスク定義

  • タスク実行に必要なロールを定義
./task-execution-assume-role.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  • 定義ファイルを用いて,ロールの作成
$ aws iam --region ap-northeast-1 create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://task-execution-assume-role.json
  • タスクの定義ファイルを作成
/task-config.json
{
  "family": "zozo-check-coupons-task",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "zozo-check-coupons-task",
      "image": "xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/zozo_check_coupons:latest",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "ap-northeast-1",
          "awslogs-group": "/ecs/zozo_check_coupons-task",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::xxxxxxxx:role/ecsTaskExecutionRole"
}
  • 定義ファイルをもとにタスクを作成
$ aws ecs register-task-definition --cli-input-json file://task-config.json

これで,打ち間違い,漏れなくタスクを定義することができます

詳しくは,こちらを参照ください
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-cli-tutorial-fargate.html

スケジュール実行の作成

次に,いよいよ定義したタスクをスケジュールで実行していきます.

  • 作成したクラスターを選択し,タスクのスケジューリングを選択,そして作成を押します

8.png

  • 以下のように設定しました.固定時間は,クーポン更新が24時間だったので24に設定しました
    スクリーンショット 2020-01-02 23.05.25.png

  • クラスター作成時に,作成したVPCを選択します
    9.png

  • 最後に,作成を選択すると,タスクが実行されます
    10.png

CloudWatchでログをチェック

タスクが完了すると,CloudWatchの方にログが送られます

確認すると,以下のようなログがありました!

11.png

今日は無いみたいですね...

おわりに

Fargate + Seleniumの環境を作りました!
Fargateはコンテナを使って動かしたコンテナをそのまま登録できるので,かなり柔軟性はいいです.

ですが,クローリングをする場合,CPUやメモリにページの読み込みが遅くなり,プログラムによるブラウザ操作がうまく行かなかったり,タイムアウトが発生してしまう場合があるので,sleepを入れる等の対策は十分にしたほうが良さそうです.

最後に,本記事は導入目的で紹介しているので,流用する場合はマナーや規約をよく読んでからお願いします!

参考文献

https://yomon.hatenablog.com/entry/2019/08/fargateselenium

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

ASP.NET Core のWebアプリケーションを docker-compose で Dockerアプリケーション として構築する

概要

ASP.NET CoreのサンプルWebアプリケーションを題材として、docker-compose による Dockerアプリケーション の動作環境を構築する例を紹介します。

環境

  • Windows10
  • Visual Studio 2019
  • ASP.NET Core 3.1
  • Docker for Windows

サンプルアプリケーション

題材にしたサンプルアプリケーションです。
https://github.com/tYoshiyuki/dotnet-core-web-sample

単純なToDoのWebアプリケーションです。トランザクションデータはデータベース (SQL Server) に保存しています。
これを Docker を利用して コンテナ化 してみましょう。

システム構成

今回は、Docker for Windows を利用して、ローカルPC上にDocker環境を構築します。ローカル開発時は、IIS Express と LocalDB を利用して開発を行いましたが、これを変更します。フロントエンドは Nginx、データベースは SQL Express の Dockerイメージを利用し、アプリケーションのコンテナを含めて3つのコンテナで構築します。構成イメージは下記の通りです。

image.png

通常、ASP.NET Core アプリケーションを IIS でホストする場合には、ASP.NET Coreのミドルウェアにより、アプリケーションはIISと統合的に動作します。Nginxをリバースプロキシとしてフロントエンドに構築する場合は、ASP.NET Core アプリケーションは Kestrel で動作させておき、そこに対してHTTP通信を転送します。

Dockerfile

1. ASP.NET Core アプリケーション

まずは、ASP.NET Core アプリケーションをコンテナ化するために、Dockerfileを作成します。Visual Studio > プロジェクト > 右クリック Dockerサポート > ターゲットOS Linux から追加できる Dockerfile をベースにして編集を行います。

Dockerfile
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["DotNetCoreWebSample.Web.csproj", "./"]
RUN dotnet restore "DotNetCoreWebSample.Web.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "DotNetCoreWebSample.Web.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "DotNetCoreWebSample.Web.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "DotNetCoreWebSample.Web.dll"]

Dockerfileの解説についてはここでは省略しますが、デフォルトでマルチステージビルドを行い、Dockerイメージサイズを削減するような設定になっています。ポイントとしては [ENV ASPNETCORE_URLS http://*:5000] で、Kestrelが待ち受けを行うポート番号を指定している部分です。後々、Nginxでリクエストを転送する際における、送信先ポート番号になります。

また、ソースコードも公式ドキュメントに従い、若干ですが変更を行います。
https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1

Startup.cs
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ・・・一部省略・・・

            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            // ・・・一部省略・・・

リバースプロキシを経由した際における、X-Forwarded-For・X-Forwarded-Protoヘッダの転送を行うようにします。
これは、IIS上でASP.NET Coreアプリケーションを動作させた場合は、既定で有効になっているので注意が必要です。

公式ドキュメントにも記載があるため、目を通しておくことをお勧めします。
https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1

2. Nginx

次にNginx用の設定を作成します。

Dockerfile
FROM nginx:latest

ENV ENTRYKIT_VERSION 0.4.0

RUN apt-get update && apt-get install -y wget \
  && wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /usr/local/bin/ \
  && entrykit --symlink

COPY ./nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl

ENTRYPOINT ["render", "/etc/nginx/nginx.conf", "--"]

CMD [ "nginx", "-g", "daemon off;" ]

nginx.conf.tmpl
worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream web-app {
        server {{ var "BACKEND_HOST" | default "localhost:5000" }};
    }

    server {
        listen 80;
        server_name $hostname;
        location / {
            proxy_pass         http://web-app;
            proxy_redirect     off;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }
}

ポイントとしてはEntrykitを利用して、アプリケーションサーバの宛先 (BACKEND_HOST) を外部注入可能にしておきます。これに関しては、後ほど docker-compose.yml で解説を行います。

3. SQL Server Express

最後に SQL Server Express です。サンプルアプリケーションの機能としてはLocalDBで十分なのですが、公式にDockerイメージが存在しないようなので、SQL Server Expressを利用します。尚、実際にはDBデータを永続化する設定が必要ですが、今回は省略しています。

Dockerfile
FROM mcr.microsoft.com/mssql/server:2017-latest-ubuntu

docker-compose.yml

作成した各コンテナを docker-compose を利用して起動してみましょう。

docker-compose.yml
version: '3.4'

services:
  app:
    build:
      context: ./DotNetCoreWebSample.Web
      dockerfile: Dockerfile
    environment:
      ConnectionStrings__DefaultConnection: "Server=sqlexpress;Database=master;User ID=sa;Password=P@ssw0rd;initial catalog=dotnetcorewebsample;MultipleActiveResultSets=True;App=EntityFramework;"
    expose:
      - 5000
    depends_on:
      - sqlexpress

  sqlexpress:
    build:
      context: ./docker/sqlexpress
      dockerfile: Dockerfile
    environment:
      MSSQL_PID: "Express"
      ACCEPT_EULA: "Y"
      SA_PASSWORD: "P@ssw0rd"
    ports:
      - 1433:1433

  web:
    build:
      context: ./docker/nginx
    environment:
      BACKEND_HOST: "app:5000"  
    ports: 
      - 80:80

記載が長いですが、何点かポイントを解説します。

まず、 ConnectionStrings__DefaultConnection: "Server=sqlexpress;Database=master;User ID=sa;Password=P@ssw0rd;initial catalog=dotnetcorewebsample;MultipleActiveResultSets=True;App=EntityFramework;" の部分は、SQL Server Express への接続先情報を設定しています。コンテナ起動時に appsettings.json で設定している内容を上書きします。ネストされたJSONの要素 (ConnectionStrings > DefaultConnection) を表現する場合に、アンダーバー2つ (__) を使用します。

BACKEND_HOST: "app:5000" の部分で、ASP.NET Core アプリケーションへの送信先を設定しています。docker-composeでは、サービス名で各コンテナの宛先を解決出来るため、上記のような設定になります。

docker-composeコマンドを実行し、各コンテナを起動します。

docker-compose up -d

実行後 http://localhost でアクセスし、動作を確認します。

image.png

まとめ

docker-composeを用いて、Dockerアプリケーションを構築する例を紹介させていただきました。
クロスプラットフォームを特徴とする .NET Core なので、Dockerを用いた開発も盛り上がると良いなと思います。

ASP.NET Core と Azure で開発する場合、Web Apps や SQL Database といった優れた PaaS を利用することで、手軽にスケーラブルなアプリケーションを構築することが出来ます。そのため、Docker のようなコンテナ技術を利用するシーンは、あまり多く無いかも知れません。

ですが、ASP.NET Coreのアプリケーションをコンテナ化することで、AWS や GCP といった別クラウドサービスへの転用や、Kubernetesに代表される コンテナ オーケストレーション の恩恵を受けることが出来るようになります。システムアーキテクチャの選択肢として、考慮する価値は十分にあるのではないのでしょうか。

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

System Eventsでエラーが起きました: Docker Quickstart Terminalにはキー操作の送信は許可されません。(1002)

はじめに

こんなことあったよ!
根本的対策ではないけど一応投稿しようといったものです。
前回一度でも正常にDockerを起動したことがある方が対象になると思います。
最終的に使用するものはVirtualBoxです。
DockerでVirtualBoxを使っている方が対象です。
一応macですが今回に関してほぼ関係ないかと、、、

あと…
Docker自体の記事も書いているのでもしよかったらみてください。
https://qiita.com/sachiko-kame/items/35e0cf07d117c3b92613

事象

:woman:『久々にDocker触れてみようかな。まずはDockerフォルダのDocker Quickstart Terminalをクリック』
スクリーンショット 2020-01-03 0.40.48.png

:woman:ターミナルが出たら、、次はコマンド打つんだよな〜うふふふふ、、、え?

スクリーンショット 2020-01-03 0.42.53.png

System Eventsでエラーが起きました: Docker Quickstart Terminalにはキー操作の送信は許可されません。
System Eventsでエラーが起きました: Docker Quickstart Terminalにはキー操作の送信は許可されません。(1002)

とりあえず検索以下URLヒット!
:woman:あ〜許可されていなかっただけか、、じゃ許可と!
https://forums.docker.com/t/error-trying-to-start-docker-quickstart-terminal/67623

やりましたよ!証拠画像(こういうのをエビデンスというのですよね←知ったか)
スクリーンショット 2020-01-03 1.11.01.png

:woman:再度クリック!、、、え同じエラー〜〜〜〜:frowning2:
:woman:エラーをなおしたいのではないDockerを使いたいんだ!←は?

強行突破

:woman:VirtualBoxから開いてみよう!!停止を実行に、、、(実行は写真には写っていない右上のボタン設定の隣の隣のところ)
スクリーンショット 2020-01-03 0.48.07.png
※実行して処理が終わるまで待ってください

:woman:通常のターミナルを起動してと、

$ docker-machine start default

:woman:動いた、動いた。
あとは立て続けに

$ docker-machine env default
$ eval "$(docker-machine env default)"
$ docker run hello-world

:woman:動いてるな

私はこう思った

:woman:どっちかでしか開けない? ←は?
:woman:キー操作の許可はってことはキーでクリックがダメってことでしょ!(。・ω・。) ←お?
:woman:だからあっているんだ! ←ま、そういうことにしといてやるか!

:woman:あと、こっちからも起動できたかも??こっちから操作したことないのでわかりませんが、、、
スクリーンショット 2020-01-03 1.06.47.png

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