20190622のdockerに関する記事は12件です。

Microservices With Docker and Cloud Performance

Microservices are an architectural design for building distributed applications using containers. Microservices get their name because each function works as an independent service. This architecture allows for each service to scale or update without disrupting other services in the application. A microservices framework creates a massively scalable and distributed system, which avoids the bottlenecks of a central database and improves business capabilities, such as continuous delivery/deployment applications and modernizing the technology stack

Microservices in Docker
Containerization is one of the biggest trends in the world right now. Docker, being the most popular containerization platform, is an excellent tool to build microservices. You have different options to structure microservices in Docker.

You can deploy each microservice in its own Docker container,  read more on how Docker works  You can also break down a microservice into various processes and run each in a separate container.

You can also use Docker Compose to run multi-container applications. It can save you a lot of time, as you do not have to create and start each container separately.

With Docker Compose, you can configure your app’s microservices using a YAML file. If you are interested here’s a useful article by Linode on how to deploy microservices with Docker and Docker Compose.

If you have a large-scale application with several containers you can also make use of a container orchestration platform. The two most popular tools are Docker Swarm and Kubernetes. Both allow you to deploy containers to a cluster of computers instead of just one machine.

Docker Swarm is embedded in the Docker Engine; it’s Docker’s native orchestration tool. Kubernetes was created by Google and, it’s the most popular orchestration platform at the moment. While Swarm fits well into the Docker ecosystem and it’s easy to set up, Kubernetes is more customizable and has higher fault tolerance.

Below, you can see an illustration from the Docker blog about how to use Docker Swarm and Compose together to manage container clusters:

You won’t need a container orchestration tool in the case of a smaller app. However, you might want to automate container management when you deal with several microservices.

Microservices in the Cloud
Microservices are frequently run as cloud applications, as they are lightweight and easy to scale and deploy. Popular cloud platforms come with several microservice-friendly features, such as:

On-demand resources

Pay as you go pricing

Infrastructure as code

Continuous Deployment and Delivery

Managed services (e.g. dealing with scaling, software configuration and optimization, automatic software updates, etc.)

Large choice of programming languages, operating system, database technologies

Built-in tools such as Docker and Kubernetes

Microservices in the cloud are usually deployed in containers, as that's how you can make the most out of the infrastructure. Besides, containers are isolated, run anywhere, and create a predictable environment. However, it is also possible to deploy microservices in the cloud without using containers.

Although the latter solution is less common, it is the better choice. For instance, WeatherBug runs microservices directly on Amazon EC2, without using containers. They decided to skip Docker and containerization altogether to avoid extra overhead and complexity. You can read the  detailed analysis of their approach  on The New Stack.

Related blog:

Java training in Chennai

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

docker-compose up invalid volume specification

errorlog
WARNING: Service "nuxt" is using volume "/src" from the previous container. Host mapping "/nuxt/app" has no effect. Remove the existing containers (with `docker-compose rm nuxt`)Recreating 8539d43fc4bd_nr_nuxt ... error

ERROR: for 8539d43fc4bd_nr_nuxt  Cannot create container for service nuxt: invalid volume specification: 'a7911443fcd9b93c14e4fd89e05b5f432552a430bb7f2e4cad780a82e1f96fdb:.:rw': invalid mount config for type "volume": invalid mount path: '.' mount path must be absolute

ずっと、ERROR見てた
WARNINGの方が大事な時もあるのか。。

warning最後の方
docker-compose rm nuxt

コマンドを実行した後にdocker-compose upでOK

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

AWS Fargate で実行したコンテナに固定のグローバルIPアドレスを割り当てる

概要

  • AWS Fargate で起動したコンテナからあるサーバへの接続をIPアドレスで制限をかけたいが、普通にFargateを設定したコンテナでは、割り当てられるグローバルIPアドレスはコロコロ変わり固定ではない。
  • AWS Fargate で VPC 内に作成されたコンテナからインターネットへ出るときに AWS NATゲートウェイを経由させることで、NATゲートウェイに割り当てたEIPがコンテナの接続元IPアドレスとして固定することができる。接続先のサーバではそのEIPを設定することでアクセス制限を行うことができるようになる。

構成概要

スクリーンショット 2019-06-22 18.24.18.png

構成内容

  • Amazon ECR
    • Dockerイメージレジストリ
    • ECR リポジトリの作成は、AWS CLI で行う
  • Amazon Fargate
    • コンテナ実行環境
    • ECR からイメージを pull する
  • VPC
    • Internetゲートウェイをアタッチする
    • Subnet-A と SubnetB を作成する
  • Subnet-A
    • コンテナ用サブネット
    • ルートテーブルで、インターネットへの接続(0.0.0.0/0)を NATゲートウェイに向ける
  • Subnet-B
    • NATゲートウェイ用サブネット
    • ルートテーブルで、インターネットへの接続(0.0.0.0/0)を Internetゲートウェイに向ける
  • コンテナ
    • AWS ECR にある Docker イメージを AWS Fargate が PULL して、Subnet-A にコンテナ化する
  • NAT ゲートウェイ
    • Elastic IP を割り当てる

VPC環境設定概要

  1. VPC作成
    Internetゲートウェイをアタッチする

  2. NATゲートウェイ用サブネット (Subnet-B) 作成
    ルートテーブル設定
    ・インターネットへの接続(0.0.0.0/0)を Internetゲートウェイに向ける

  3. Subnet-B 内に NATゲートウェイを作成
    Elastic IP を割り当てる(今回は 3.113.58.14 )

  4. コンテナ用サブネット (Subnet-A) 作成
    ルートテーブル設定
    ・インターネットへの接続(0.0.0.0/0)を NATゲートウェイに向ける

動作確認用コンテナのDockerイメージを作成

接続元IPアドレスを確認するために、コンテナ内から https://ifconfig.me にアクセスして接続元(コンテナ)のIPアドレスを確認する

  1. Dockerfile 作成
    FROM alpine:latest
    RUN apk --no-cache add curl
    CMD curl -s https://ifconfig.me
    
  2. ビルド
    $ docker build -t demo .
    
  3. イメージ確認
    $ docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    demo                latest              204b606e4331        33 minutes ago      6.92MB
    alpine              latest              4d90542f0623        2 days ago          5.58MB
    
  4. 実行テスト
    $ docker run demo
    xxx.xxx.xxx.xxx (接続元IPアドレス)
    

AWS ECR 設定概要

参考:Dockerイメージを AWS ECR に登録して AWS ECS の Fargate でコンテナ化してサービス提供

  1. AWS CLI で AWS ECR を設定するための IAM ユーザを作成
    AmazonEC2ContainerRegistryFullAccessポリシーをアタッチする

  2. 1.のIAMユーザをターミナルに登録する (今回は "ecr" というプロファイル名)

    $ aws configure --profile ecr
    AWS Access Key ID [None]: **********
    AWS Secret Access Key [None]: **********
    Default region name [None]: ap-northeast-1
    Default output format [None]:
    
  3. ECR のリポジトリを作成する (今回は "demo-repository" というリポジトリ名)

    $ aws ecr create-repository --repository-name demo-repository --profile dev-netnative-nishimura-toru
    
  4. AWS ECR レジストリ用の docker login 認証コマンド文字列(12時間有効な認証トークン)を取得する

    $ aws ecr get-login --no-include-email --profile dev-netnative-nishimura-toru
    docker login -u AWS -p eyJwYXlsb2FkI〜
  5. 上記で発行されたコマンドをそのまますべてをコピーして、ターミナルにペーストして実行する

    $ docker login -u AWS -p eyJwYXlsb2FkI〜
    〜
    WARNING! Using --password via the CLI is insecure. Use --password-stdin.
    Login Succeeded
    

Dockerイメージを AWS ECR へ PUSH する

  1. Docker イメージにタグを付ける
    $ docker tag demo <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/demo-repository:demo
    
  2. 確認
    $ docker images
    REPOSITORY                                                          TAG                 IMAGE ID            CREATED             SIZE
    <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/demo-repository   demo                204b606e4331        38 minutes ago      6.92MB
    demo                                                                latest              204b606e4331        38 minutes ago      6.92MB
    alpine                                                              latest              4d90542f0623        2 days ago          5.58MB
    
  3. Dockerイメージを AWS ECR に PUSH する
    $ docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/demo-repository:demo
    The push refers to repository [777813037810.dkr.ecr.ap-northeast-1.amazonaws.com/demo-repository]
    39f660b1173d: Pushed
    256a7af3acb1: Pushed
    demo: digest: sha256:e903f0093f0d376a607c95279a204b36a15335dbda9fd0d73722f7593494aeff size: 738
    

AWS Fargate の設定とタスク実行

参考:Dockerイメージを AWS ECR に登録して AWS ECS の Fargate でコンテナ化してサービス提供

1. タスク定義を作成する

(1) 新しいタスク定義の作成

01.png

(2) 起動タイプで FARGATE を選択

02.png

(3) タスクとコンテナの定義の設定

①タスク定義名を ここでは demo-demo とする
 他はデフォルト

06.png

②タスクの実行IAMロールはデフォルト

04.png

③タスクサイズは最小サイズで

05.png

④コンテナの定義

コンテナの追加

07.png

コンテナ名: demo-container
イメージ: <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/demo-repository:demo
他はデフォルト

08.png

⑤作成

09.png

2. クラスターを作成する

(1) クラスターの作成

10.png

(2) クラスターテンプレートの選択

「AWS Fargate を使用」 を選択

11.png

(3) クラスターの設定

クラスター名: demo-cluster
他はデフォルト

12.png

(4) 作成

13.png

3. タスク実行を作成する

① 新しいタスクの実行

14.png

② タスクの実行

起動タイプ: FARGATE
タスク定義: demo-task:1
クラスター: demo-cluster
クラスターVPC
サブネット:コンテナ用に作成したサブネット
パブリックIPの自動割り当て: DISABLED

15.png

③ タスクの実行
タスクを実行すると、コンテナのプロビジョニングが始まり、実行後に消滅する

16.png

4. タスクの実行結果の確認

タスクの実行結果は、CloudWatch ログ に送られる。
CloudWatchロググループは、タスク定義を作成した時に作られている。

CloudWatchロググループ: /ecs/demo-task
メッセージに Elastic IP の IPアドレスが表示されていれば成功。

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

MySQL8.0でPDO接続できない(dockerコンテナ)

TL;DR

  • MySQL8.0ではcaching_sha2_passwordという認証方式に変わったため、PDO接続するユーザの認証方式をmysql_native_passwordに変更する必要があった
  • hostにはdockerのコンテナ名を記入する
  • ユーザを新しく作成して、権限付与し、認証方式を変更する

はじまりのコード

public function connect()
    {
        try {
            $dbh = new PDO('mysql:dbname={db名};host=127.0.0.1', 'root', 'root');
            echo "接続成功\n";
            return $dbh;
        } catch (PDOException $e) {
            echo "接続失敗: " . $e->getMessage() . "\n";
            exit();
        }
    }

{DB名}の部分は自分のデータベース名を入れていました

で、以下エラー文

接続失敗: SQLSTATE[HY000] [2002] Connection refused

理由

hostにはdockerのコンテナ名を入力する必要がある
参考 : Docker Composerではコンテナ間通信はコンテナ名を指定する

hostを変更

$dbh = new PDO('mysql:dbname={db名};host={コンテナ名}', 'root', 'root');

以下エラー(同じ)
接続失敗: SQLSTATE[HY000] [2002] Connection refused

理由

rootの認証方式がchaching_cha2_passwordなので、mysql_native_passwordに設定する必要がある

# mysql接続(mac、もしくはdockerのdbコンテナ上で以下を実行)
❯ mysql -u root -p -h 127.0.0.1

# mysqlのユーザを表示
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| root             | %         | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+

解決策は以下。2. がおすすめ。
1. rootのpluginを変更する
2. 新しくユーザを作って、権限を変更しつつ、pluginを変更する

解決策1「rootのpluginを変更する」

# rootの認証方式をmysql_native_passwordに変更する
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| root             | %         | mysql_native_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+

これで接続成功した。

解決策2「新しくユーザを作って、権限を変更しつつ、pluginを変更する」

# user名がpoeでpasswordがpoeのユーザを作る。
# hostを%にすることで、外部(dockerコンテナ)から接続できるユーザになる
mysql> CREATE USER 'poe'@'%' IDENTIFIED BY 'poe';
Query OK, 0 rows affected (0.02 sec)

# 確認すると、poeができている
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| poe              | %         | caching_sha2_password |
| root             | %         | mysql_native_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+
7 rows in set (0.00 sec)

# poeのpluginを変更する
mysql> ALTER USER 'poe'@'%' IDENTIFIED WITH mysql_native_password BY 'poe';
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| poe              | %         | mysql_native_password |
| root             | %         | mysql_native_password |
| test             | %         | mysql_native_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+
7 rows in set (0.00 sec)

これで接続してみると...エラーになってしまった...

$dbh = new PDO('mysql:dbname={db名};host={コンテナ名}', 'poe', 'poe');

接続失敗: SQLSTATE[HY000] [1044] Access denied for user 'poe'@'%' to database '{db名}'

エラーになった理由

ユーザの権限がないからエラー。このサイト(MySQL8.0ではGRANT構文でユーザを作成できない)を参考にしつつ、以下実行

# 権限確認
mysql> SHOW GRANTS for 'poe'@'%';
+---------------------------------+
| Grants for poe@%                |
+---------------------------------+
| GRANT USAGE ON *.* TO `poe`@`%` |
+---------------------------------+
1 row in set (0.00 sec)

# 権限変更
mysql> grant all on *.* to 'poe'@'%' with grant option;
Query OK, 0 rows affected (0.03 sec)

# 権限反映
mysql> flush privileges;
Query OK, 0 rows affected (0.02 sec)

# 権限めっちゃ増えたわ
mysql> SHOW GRANTS for 'poe'@'%';
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for poe@%                                                                                                                                                                                                                                                                                                                                                                                                                      |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `poe`@`%` WITH GRANT OPTION                                       |
| GRANT APPLICATION_PASSWORD_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,XA_RECOVER_ADMIN ON *.* TO `poe`@`%` WITH GRANT OPTION |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

すると、接続成功しました。

補足 : 新しく作ったユーザの認証方式を全部mysql_native_passwordにさせる

[mysqld]
default_authentication_plugin=mysql_native_password

これをmy.cnfに追加すると、
新規ユーザ作成時、認証方式が自動的にmysql_native_passwordとなるらしいです


そのほかにも出たエラー

接続失敗: sqlstate[hy000] [2002] no such file or directory
多分hostをlocalhostにしているから。dockerだとhostはコンテナ名

SQLSTATE[HY000] [2003] Can't connect to MySQL server on 'localhost' (10061).
mysqlに入ってuserのhostを見て欲しい。localhostになってるでしょ。ここが%じゃないと外部サーバーからの接続はできないんだと思う

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

【Windows10 Home】dockerで PHP + Apache + MySQL + phpMyAdmin + Laravel を構築し、GitHubに上げるまで

Windows10 Homeでも流行りのDockerを使いたい!

※Dockerが流行っているかは私個人の感想です
※駆け出しエンジニアの為、間違えている箇所があるかもしれません(ご指摘いただけますと嬉しいです。)
※構築の手順だけ知りたい?そんな冷たいこと言わないでください

私のPCの環境
- Windows10 Home 64ビット(ノートPC)
- メモリ12GB

はじめに

Dockerという存在を知ったのは5月の事で、それまではローカル環境でPHP開発をしようと思うとXamppに頼ってました
ぶっちゃけXampp使っていても全然よかったのですが、なんか世間ではDockerというものが流行っているらしいというのを見聞きしてちょっくら勉強してみようかなと思った次第です
あと、Laravelも今後使っていきたかったので、そのための準備としてもうってつけかなと思って

調べていった(作業していった)順序

  • Dockerとは何ぞや&何ができるんだろう?
  • じゃぁDockerでPHPの環境ってどうやって作るんだろー
  • なんかできそうだしやってみるかー → 手順調べる
  • PHP環境構築できたからLaravel入れてみるか
  • 最後にGitHubにpushして終了!

Dockerとは何ぞや&何ができんの?

外部サイトですがこちらを見て
『ほーん仮想環境みたいなの作って色々開発環境作れるのか』※実際は違うみたいなのですが詳しいことは自分で調べてください
『ローカルに直接構築するわけじゃないから、不要なデータは楽に削除することもできるのか』
くらいの感覚です(今もそんな感覚)

じゃぁDockerでPHPの環境ってどうやって作るんだろー

Qiitaで調べると手順がいっぱい出てきます

  • ymlというのを使って設定をしていくのか
  • Dockerfileっていうのがあって、そこで色々設定ができるのか

といった感じで、「なんかできそうだな」と思えてきました

なんかできそうだしやってみるかー

Dockerの導入

一般的であれば
Windows proでCPUの仮想化を有効にしてHyoer-Vも有効にする
といったものなのですが、そもそも私のパソコンはWindows Homeです
なんだアップグレードしないとできないのかーと思ったのですが、こちらを参考にやってみることにしました
めっちゃ参考になりました。
ちなみに私の場合「Docker Quickstart Terminal」の起動には結構時間がかかります(長い時は5分くらい)

[PHP,MySQL,phpMyAdmin]の構築

  1. ここでは以下のディレクトリ構成を目指します

    work/
     ├ html/
     │ └ index.php
     ├ mysql/
     ├ php/
     │ └ php.ini
     └ docker-compose.yml

  2. 初めに
    こちらを参考にやってみました
    ここで重要な事!!
    windows HomeでDockerを使う場合、マウントできるディレクトリは限られてきます
    マウントするディレクトリはuser配下に作ってください!!(適当にディレクトリを作ってマウントできない原因がわからず2日潰れました)
    別途設定すれば問題ないそうなのですが、それはここでは取り扱いません
    私は(ここで解決しました)[https://qiita.com/kikako/items/7b6301a140cf37a5b7ac]
    【例】
    C:\Users\あなたのPCのユーザー名\work
    workというディレクトリを作成しました

  3. docker-composeのカスタマイズ
    カスタマイズをするためにこのようなディレクトリ構成に変更しました

docker-compose.yml
version: '3'

services:
  php:
    image: php:7.2-apache
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./html:/var/www/html
    ports:
      - 32778:80
  mysql:
    image: mysql:5.7
    volumes:
      - ./mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: test
      MYSQL_USER: test
      MYSQL_PASSWORD: test
    command: --innodb-use-native-aio=0
    ports:
      - "32780:3306"
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      PMA_ARBITRARY: 1
      PMA_HOST: mysql
      PMA_USER: test
      PMA_PASSWORD: test
    restart: always
    links:
      - mysql:mysql
    ports:
      - '32779:80'

こんな感じに設定をして、htmlディレクトリ配下にphpinfo()を記述したindex.phpを置いて、http://192.168.99.100:32778にアクセス
表示されなかったらどこか設定が悪いので色々調べる

PHP環境構築できたからLaravel入れてみるか

よっしゃ構築できたーということで今度はLaravelを入れてみる
1. ここでは以下のディレクトリ構成を目指します

work/
 ├ html/
 │ └ my-laravel-app/
 │   └ いろいろなファイル
 │   └ public
 ├ mysql/
 ├ php/
 │ └ php.ini
 └ docker-compose.yml

2. コンテナからcomposerを使えるようにする
ymlファイルの一部を変更する必要があるのでphpの記述している箇所を以下に変更してみた

docker-compose.yml
  app:
    build:
      context: ./dockerfile
      dockerfile: Dockerfile
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./html:/var/www/html
    ports:
      - 32778:80

あと、これに伴ってcomposerを使う必要があるのでdockerfileディレクトリ配下に以下を記述

FROM php:7.2-apache
RUN apt-get update -y
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_NO_INTERACTION 1
  • COMPOSER_ALLOW_SUPERUSER 1  これはDocker内でcomposerを使う時にsuper userで使用することを許可するコマンド
  • COMPOSER_NO_INTERACTION 1  これはDocker内でcomposerを使う時にいちいち質問されないように設定するコマンド

こちらを参考にしました。

3.Laravelを置きたいディレクトリに移動
cd C:\Users\あなたのPCのユーザー名\work

4.コマンド実施
  1. コンテナ内に入る
     docker-compose exec app /bin/bashまたはdocker-compose exec app /bin/sh
  2. Laravelをインストール!(結構時間かかると思います)
    外部サイトですがこちらを参考にして行いました
   composer create-project --prefer-dist laravel/laravel my-laravel-app

5.ドキュメントルートの変更
 ドキュメントルートを変更する必要があるので、以下を行います
 ちなみにこちらを参考にしました
 参考にしたページではコンソール上で書き換えているみたいなのですが、それができなかったのでいったんホストに移動させてサクラエディタで書き換えた後に上書きをしています
  1. cp /etc/apache2/apache2.conf /var/www/html
  2. cp /etc/apache2/sites-available/000-default.conf /var/www/html
  3. apache2.confと000-default.confを変更

apache2.conf
170 <Directory /var/www"ここから"/html/public"ここまで追加">
171         Options Indexes FollowSymLinks
172         AllowOverride All
173         Require all granted
174 </Directory>
000-default.conf
 10 
 11         ServerAdmin webmaster@localhost
 12         DocumentRoot /var/www"ここから"/html/public"ここまで追加"
 13 

  4. cp /var/www/html /etc/apache2/apache2.conf
  5. cp /var/www/html /etc/apache2/sites-available/000-default.conf

6.dockerのマウントする場所を変更
このままだとhttp://192.168.99.100:32778でうまく表示されないので、docker-composeを変更する

docker-compose.yml
  app:
    build:
      context: ./dockerfile
      dockerfile: Dockerfile
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./html/my-laravel-app:/var/www/html
    ports:
      - 32778:80

7.コンテナの再ビルド
  1. exit <- まずはコンテナから出ます
  2. docker-compose stop
  3. docker-compose rm
  4. docker-compose up -d --build

8.http://192.168.99.100:32778にアクセス!!!!!
表示されたー!ひゃhhhっほおおーい
となります

最後にGitHubにpushして終了!

最後にGitHubにpushします(外部サイトですがこちらを参考にしました)
1. アカウントを作成(こちらからできます)
2. レポジトリを作る
3. gitのインストール(こちらを参考にやってみると笑顔になれます)
4. コンソールでlaravelが置いてあるところに移動します
(例)cd C:\Users\あなたのPCのユーザー名\work\my-laravel-app
5.コマンド色々
git init -> git remote add origin git@github.com:ユーザ名/リポジトリ名.git -> git config --global user.name "Your Name" -> git config --global user.email "you@example.com" -> git fetch -> git pull origin master
6. commitしてpushします(その前にbranch作ってもいいかもしれません)
git commit -m '名前' -> git push -u origin master

最後に

あなたのお役に立てたら幸いです
間違った記載や誤字があればコメントいただけますと両手を上げて喜びます

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

docker(17) docker入門の入門 5つの壁

dockerを利用するのにいくつか壁がある。

第一の壁 導入

Windowsだと、Hiper-Vが無効になっていると導入できない。

mac OSだと brew で導入できる

Debian系Linuxだと aptで導入できる。

第二の壁 Docker hub

docker hubにブラウザで接続するとダウンロードできる状態で、
コマンドでdocker hubに push, runしようとしてエラーがでることがある。

コマンドでloginすれば大丈夫。

docker(8) 今日の error:解決
https://qiita.com/kaizen_nagoya/items/90d1443c11bebd564aa1

docker(15)今日のdocker error:denied: requested access to the resource is denied
https://qiita.com/kaizen_nagoya/items/fae512ef5e0c04dabc25

同じエラーの記事を2つ書いてしまった。標題にエラーを入れた方がよいというのが教訓。

docker(4)docker hub とQiita
https://qiita.com/kaizen_nagoya/items/798358bba382d693e391

第三の壁 docker アプリが起動していない

しばらくdockerを利用していないと、dockerコマンドがエラーになる。

docker アプリを起動すればよい。

docker(14)今日のdocker error:docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
https://qiita.com/kaizen_nagoya/items/687690a3d44a92a47462

第四の壁

いっぱいdockerを利用していてimagesでハードディスクがいっぱいで、dockerが動かなくなる。
docker imagesを消せばよい。

第五の壁

docker imagesがうまく消えない。

いろいろなエラーがある。

docker(9) rmiのための順番
https://qiita.com/kaizen_nagoya/items/0bc05d08cf18af4a8801

docker(10) error 三度目の正直
https://qiita.com/kaizen_nagoya/items/a6a0d66e3e47601e6c93

文書履歴(document history)

ver. 0.01 初稿 20190622 午前
ver. 0.02 参考資料追記 20190622 午後

このエントリーをはてなブックマークに追加
https://b.hatena.ne.jp/guide/bbutton

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

docker-composeを使用したコンテナでの開発環境セットアップ(CakePHP3)

前回までのあらすじ

社内外でvSphereやVirtualBox、Vagrantで仮想マシン環境を人力でつくりまくってきたコンテナ初心者、社内外の環境の違いを吸収するために今までのノウハウを使ってコンテナ環境をセットアップ。

Qiita:VirtualBox + Vagrant + DockerによるOSに縛られないコンテナ環境セットアップ

今回の内容

というわけで実際にコンテナ環境ができたので、この環境をベースにしてDocker-composeを使って開発環境を整えていきます。会社のWindows環境でもほぼ同じことができたのは確認済みです。

ただしWSLとVagrantの相性が悪かったため結果としてコマンドプロンプトからVagrant操作することとなりました......WSL2に期待します......

今回開発環境として作りたいのは、社内で人に教えたりするCakePHP3です。bakeコマンドで雛形をさくっとつくることができ、PHPであることからそこまで難しくありません。

そのベースとなるDocker-composeとは、さくらのナレッジでの記載によりますと

Dockerビルドやコンテナ起動のオプションなどを含め、複数のコンテナの定義をymlファイルに書き、それを利用してDockerビルドやコンテナ起動をすることができます。一つの簡単なコマンドで複数のコンテナを管理できるようになります。

さくらのナレッジ:Docker入門(第六回)〜Docker Compose〜

ymlファイルということは、AnsibleのPlaybookに近いものなのでしょうか。

少し調べて以下のふたつの記事を見てみると、相性こそ悪い場合もあるようですが、AnsibleからDocker Composeを操作することもできそうな勢いです。

identail:AnsibleとDocker Composeの相性問題に気づいた話

hawksnowlog:Ansible から docker-compose を使ってみた

もともとAnsibleは動作するかどうかチェックしてみたことはありましたので、試してみる価値はありそうです。Ansibleって本当になんでもできるんですね。しかし、今回はDocker-composeのみで動かせるようにしてみます。

今回も情報を整理するためにアーキテクチャを書いてみます。

docker_compose_ar.jpg

こんな感じでしょうか。ymlファイルはPythonと書き方も似ていますので読みづらいとかもあまりないでしょうから、これなら私でも大丈夫そうです。

対象読者

  • 前回の記事をご覧になった方
  • かつ前回の記事と同一もしくはそれに準ずる環境を作成された方
  • かつWebアプリケーションフレームワークで開発をされている方
  • かつコンテナで開発環境をただちに配ったりすることを企んでいる方
  • つまり私

前提条件

事前に前回の記事と同一もしくはそれに準ずる環境の準備をお願いします。おそらくDocker for windowsでも、Docker for macでもなんとかなるとは思いますが、具体的な検証をしていないので適宜調査をお願いします。

今回の使用環境

  • macOS Mojave 10.14.5
  • VirtualBox 6.0.4
  • Vagrant 2.2.4
  • CentOS Linux release 7.6.1810 (Core)
  • Docker 18.09.6
  • docker-compose 1.24.0

実施する内容

  1. Gitのインストール
  2. リポジトリのクローン
  3. docker-composeのインストール
  4. 初期設定
  5. コンテナの起動

0. Gitのインストール

まだこのCentOS7 VMではGitを入れていなかったです。

$ git --version
bash: git: コマンドが見つかりません

yumコマンドでGitをインストールできますが、今回はyum以外の方法を個人的に知りたいので、以下の手順に従ってGitのインストールをします。yumでGitをインストールされても構いません。

Qiita:CentOS7 に最新版の Git をインストールする方法

まずは依存関係のあるライブラリをインストールします。

$ sudo yum -y install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker autoconf

インストールに最適な場所(と信じて)移動

$ cd /usr/local/src/

サイトからGitの圧縮ファイルをダウンロード

$ sudo wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.9.5.tar.gz

ファイルを解凍

$ sudo tar xzvf git-2.9.5.tar.gz

終わったらディレクトリに移動して

$ cd git-2.9.5/

makeコマンドを実行します(ほんの少し時間がかかります)。

makeコマンドについて:

cybertrust:makeコマンドを使ってみよう #1

$ sudo make prefix=/usr/local all
$ sudo make prefix=/usr/local install

インストールされたか確認します。

$ git --version
git version 2.9.5

インストールできたみたいですが、こうして実際に時間をかけてやってみるとyumコマンドの便利さを思い知らされます。

1. リポジトリのクローン

本題に戻り、以下の記事を参考にCakePHP用のコードの入手とdocker-composeのインストールを遂行します。

Qiita:【爆速】 15分で CakePHP3 実行環境を簡単に構築する by Docker

今回はこの記事を書いてくれた方のリポジトリのクローンをホームディレクトリで実施します。

$ cd ~
$ git clone https://github.com/km42428/docker-cakephp3-template.git
Cloning into 'docker-cakephp3-template'...
remote: Enumerating objects: 38, done.
remote: Total 38 (delta 0), reused 0 (delta 0), pack-reused 38
Unpacking objects: 100% (38/38), done.
Checking connectivity... done.

実際にlsみてみるとディレクトリができています。

$ ls
anaconda-ks.cfg  docker-cakephp3-template
$ cd docker-cakephp3-template
$ ls
LICENSE  README.md  data  docker-compose.yml

2. docker-composeのインストール

以下の記事と公式ドキュメントを参考にインストールを実施します。

Qiita: Docker Compose - インストール

公式ドキュメント(英 - Linux): Install Docker Compose

念のためsu rootしておきます。大いなる権限には大いなる責任が云々。

そして安定(stable)なリリースと書かれている、公式ドキュメントに記載されたコマンドを実行しました。完了までには時間がかかります。

$ su root
# sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

実行バイナリを許可します。

# sudo chmod +x /usr/local/bin/docker-compose

これで完了しており、バージョンも表示されました。

# docker-compose --version
docker-compose version 1.24.0, build 0aa59064

3. 初期設定

では早速buildしてみましょう。3分くらいかかります。

# docker-compose build

できあがったようです。

Successfully built 58a46e68225e
Successfully tagged docker-cakephp3-template_host:latest

4. コンテナの起動

コンテナを立ち上げてみます。

# docker-compose up -d
<中略>
Creating docker-cakephp3-template_mysql_1 ... done
Creating docker-cakephp3-template_phpfpm_1 ... done
Creating docker-cakephp3-template_host_1   ... done
Creating docker-cakephp3-template_nginx_1  ... done

起動状態を確認してみます。プロジェクトはまだ存在しないのでhostのコンテナが生成されていないようです。

# docker ps
CONTAINER ID        IMAGE                             COMMAND                  CREATED             STATUS              PORTS                                      NAMES
cf2d5161e296        nginx:mainline-alpine             "nginx -g 'daemon of…"   4 days ago          Up 54 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   docker-cakephp3-template_nginx_1
6677d42b2aac        docker-cakephp3-template_phpfpm   "docker-php-entrypoi…"   4 days ago          Up 53 seconds       9000/tcp                                   docker-cakephp3-template_phpfpm_1
bd0a06a37dc0        docker-cakephp3-template_mysql    "docker-entrypoint.s…"   4 days ago          Up About an hour    33060/tcp, 0.0.0.0:3307->3306/tcp          docker-cakephp3-template_mysql_1

phpfpmコンテナに入って、ターミナルを立ち上げます。
今回のidは6677d42b2aacでした。

# docker exec -it 6677d42b2aac /bin/sh

なんとこれで仮想マシンに入れました。コンテナってこんな感じなのか......

/var/www/html #

ではcakephpのプロジェクトを作っていこうと思います。その前に、プロジェクト生成に必要となるパッケージ管理ソフト、composerをインストールします。

# curl -s https://getcomposer.org/installer | phpß

しかし私の環境だとうまくいきませんでした。-sを外して実行したところこんな結果が出てきました。

# curl https://getcomposer.org/installer | php
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0curl: (6) Could not resolve host: getcomposer.org
/var/www/html #

以下の記事を参考にdns設定を変更します。

qiita:curlで「Couldn't resolve host」エラーが出た場合の対処

# vi /etc/resolv.conf
nameserver 8.8.8.8

もう一度実行することでうまくいきました。

# curl https://getcomposer.org/installer | php
 % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                Dload  Upload   Total   Spent    Left  Speed
100  257k  100  257k    0     0  82280      0  0:00:03  0:00:03 --:--:-- 82280
All settings correct for using Composer
Downloading...

Composer (version 1.8.6) successfully installed to: /var/www/html/composer.phar
Use it: php composer.phar

ではsampleプロジェクトを作成します。最後にYを押して終了です。

# php composer.phar create-project --prefer-dist cakephp/app sample

コンテナから抜けます

# exit

作成されたsampleプロジェクトでmysqlに接続するためにファイルを一部書き換えます。

docker-composeのディレクトリ/data/htdocs/sample/config/app.php
-   'host' => 'localhost',
+   'host' => 'mysql',

これで以下のURLにアクセスすると実際にcakephpの画面が表示されました。

http://192.168.33.10:8765

スクリーンショット 2019-06-22 07.16.39.png

最後にコンテナを停止して終了となります。

# docker-compose stop

おわりに

とりあえずコンテナを実際に動かすことができました。あとはdocker-compose.ymlのファイル群を自作できるようになればファイルを配って作業ができそうです。

まず私はPythonまわりのファイル群を探してカスタマイズして、早速開発に取り組みつつ、k8sなど基盤まわりに引き続き手を出していこうと思います。

情報系が大の苦手マンを自負して生きていても、こうして単独で楽に基盤構築できるって、考えてみるとすごい時代になったもんですね......

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

docker で pdf to text

いろいろな作業はdockerで行い、docker hubにあげるようにしている。

1) 仕事場、出張先で同じ機材で仕事ができるとは限らない。
2) すべての機材でgit, dockerは利用できるようにしている。
3) 誰か手伝ってくださる方がお見えなら手伝って欲しい
4) Webに掲載されているもの(githubなど)だけで作業する
5) 作業手順などはQiita, researchmapなどに記載する

毎回、いろいろなところでつっかえる。

ubuntu
# apt install pdftotext
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package pdftotext

え、pdftotextってaptで入らないの?

いえいえそうではありません。

macOS
$ docker run -v /Users/administrator/Downloads/nakamori:/home/nakamori -it ubuntu /bin/bash

macOSのフォルダに入っているpdfファイルを変換したい。
ファイル共有でubuntu起動。

pdftotextはpopper-utilsに入っているらしい。

ubuntu
# apt update; apt -y upgrade
# apt install -y poppler-utils vim
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker(16)docker で pdf to text

いろいろな作業はdockerで行い、docker hubにあげるようにしている。

1) 仕事場、出張先で同じ機材で仕事ができるとは限らない。
機材の運搬は疲れる。週に1日は手ぶらの日として、機材を運ばないようにしている。
2) すべての機材でgit, dockerは利用できるようにしている。
 Windows, mac OS, Linux, Raspberry PI(Rasbian)で同じ作業をして確かめる。
3) 誰か手伝ってくださる方がお見えなら手伝って欲しい
4) Webに掲載されているもの(githubなど)だけで作業する
5) 作業手順などはQiita, researchmapなどに記載する
6) docker hubのDownloads目標を合計1000に設定。現在(892)

docker(4)docker hub とQiita
https://qiita.com/kaizen_nagoya/items/798358bba382d693e391

毎回、いろいろなところでつっかえる。

ubuntu
# apt install pdftotext
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package pdftotext

え、pdftotextってaptで入らないの?

いえいえそうではありません。

macOS
$ docker run -v /Users/administrator/Downloads/nakamori:/home/nakamori -it ubuntu /bin/bash

macOSのフォルダに入っているpdfファイルを変換したい。
ファイル共有でubuntu起動。

pdftotextはpopper-utilsに入っているらしい。

ubuntu
# apt update; apt -y upgrade
# apt install -y poppler-utils vim

文字コード対応

macOS, Windowsでファイル共有で作業しているといつのまにか文字コードがubuntuのコマンドラインで読めないものがまじってしまう。

ubuntu
# apt -y install nkf
# nkf -w qc-nishimori.txt >qc-nishimori-u.csv

参考資料(reference)

量子計算機 arXiv掲載 西森 秀稔 論文単語帳作成をdockerで(文字コード対応)
https://qiita.com/kaizen_nagoya/items/319672853519990cee42

文書履歴(document history)

ver. 0.01 初稿 20190622 午前
ver. 0.02 hub追記 20190622 午後
このエントリーをはてなブックマークに追加
https://b.hatena.ne.jp/guide/bbutton

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

今日のdocker error:denied: requested access to the resource is denied

macOS
$ docker push  kaizenjapan/qc-nakamori 
The push refers to repository [docker.io/kaizenjapan/qc-nakamori]
97c6efdca6e8: Preparing 
a7ab5d7b861b: Preparing 
75e70aa52609: Preparing 
dda151859818: Preparing 
fbd2732ad777: Preparing 
ba9de9d8475e: Waiting 
denied: requested access to the resource is denied

docker runは動くのに、docker pushがエラーになる。loginしなおしてみる。

macOS
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: kaizenjapan
Password: 
Login Succeeded

もう一度。

$ docker push  kaizenjapan/qc-nakamori 
The push refers to repository [docker.io/kaizenjapan/qc-nakamori]
97c6efdca6e8: Pushed 
a7ab5d7b861b: Layer already exists 
75e70aa52609: Layer already exists 
dda151859818: Layer already exists 
fbd2732ad777: Layer already exists 
ba9de9d8475e: Layer already exists 
latest: digest: sha256:2ecf657f164325cea8fa9dced3dc5ed5ee06be90cc47147219b71dd7d8eee45d size: 1574

よかった。

文書履歴(document history)

ver. 0.01 初稿 20190622

このエントリーをはてなブックマークに追加
https://b.hatena.ne.jp/guide/bbutton

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

docker(15)今日のdocker error:denied: requested access to the resource is denied

macOS
$ docker push  kaizenjapan/qc-nakamori 
The push refers to repository [docker.io/kaizenjapan/qc-nakamori]
97c6efdca6e8: Preparing 
a7ab5d7b861b: Preparing 
75e70aa52609: Preparing 
dda151859818: Preparing 
fbd2732ad777: Preparing 
ba9de9d8475e: Waiting 
denied: requested access to the resource is denied

docker runは動くのに、docker pushがエラーになる。loginしなおしてみる。

macOS
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: kaizenjapan
Password: 
Login Succeeded

もう一度。

$ docker push  kaizenjapan/qc-nakamori 
The push refers to repository [docker.io/kaizenjapan/qc-nakamori]
97c6efdca6e8: Pushed 
a7ab5d7b861b: Layer already exists 
75e70aa52609: Layer already exists 
dda151859818: Layer already exists 
fbd2732ad777: Layer already exists 
ba9de9d8475e: Layer already exists 
latest: digest: sha256:2ecf657f164325cea8fa9dced3dc5ed5ee06be90cc47147219b71dd7d8eee45d size: 1574

よかった。

文書履歴(document history)

ver. 0.01 初稿 20190622

このエントリーをはてなブックマークに追加
https://b.hatena.ne.jp/guide/bbutton

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

(No.12) おじさんが、LINE風アプリを開発する - Apache HTTP ServerをDockerで起動してみる(その1)

前回までのあらすじ

今回やること

  • Webサーバの一つであるApache HTTP Serverの起動にチャレンジしてみる。

Dockerのおさらい(おじさんの理解)

  • Dockerfileを作成する。
    • アプリを動作させる環境がどのようなものか、どのようなアプリを起動するのかについて書く。
    • Linuxディストリビューション(Dockerの場合、Linuxカーネル以外のOSを構成するプログラムの集合を指す)を含むベースイメージを元に作成する。
      • おじさんの場合はベースイメージにAmazon Linux 2を選択したよ。
  • Dockerfileからイメージを作成する。
  • イメージからコンテナを作成&起動する。

Apache HTTPD Serverのインストール

1. 公式サイトの手順を調べる(http://httpd.apache.org/)

  • 公式サイトのインストール手順
  • スマホのアプリのように、ダウンロードしてインストール完了といった単純な方法が書かれていないよ。
  • インストーラの代わりに、Apache HTTPD Serverのプログラムコードが公開されているよ。
  • プログラムコードのことをソースコードとも呼ぶらしいよ。以後、おじさんはソースコードと呼ぶことにするよ。
  • 何やらコンパイルと呼ぶ作業?が必要みたい。

1-1. コンパイルって何だろう・・・

  • よくわからないからおじさん頑張って調べてみたよ。
  • Apache HTTPD Serverの場合、ソースコード(プログラムコード)は、C言語と呼ばれる種類の表現方法で書かれていることがわかったよ。
  • macOS、Windows、LinuxなどのOSは、ソースコードのままではアプリとして実行することが出来ないみたいだよ・・・。
  • ソースコード(プログラムコード)をコンピュータに与えれば、コンピュータは指示通りに動くものだと思っていたよ・・・。
  • ソースコードを元に、アプリとして実行するには次の手順が必要みたいだよ。
    • (1) ソースコードをダウンロードする。(Apache HTTPD Serverのソースコードはここにあるよ
    • (2) ソースコードをOSが求める仕様通りの形式に変換・翻訳・整形する。
      • ソースコードを変換・翻訳する工程をコンパイルと呼ぶらしいよ。
      • コンパイルするソフトウェアをコンパイラと呼ぶらしいよ。
      • 変換・翻訳の結果をオブジェクトファイルと呼ぶらしいよ。
      • OSによってオブジェクトファイルの形式が異なるみたいだよ。
        • Windowsの場合はPE
        • Linuxの場合はELFなど。
      • コンパイラはオブジェクトファイルに変換・翻訳するだけではなく、ソースコードの記述誤りなど、形式的なミスを検出して、誤りのある箇所をプログラマーに教えてくれる便利機能も含まれるみたいだよ。至れり尽くせりで、なんだかコンパイラって世話焼きだね。
    • (3) オブジェクトファイルや、その他、誰かが作成した別のオブジェクトファイル(再利用可能なプログラムの部品をライブラリと呼ぶらしいよ)を、合体させたり、OSが求める仕様通りの形式に整形する。
      • この工程をリンクと呼ぶらしいよ。
      • リンクしてくれるソフトウェアをリンカと呼ぶらしいよ。りんかって急に可愛い名前に見えてきたよ・・・。
      • リンカによって整形されたファイルが、おじさんやみんなも知っているアプリになるんだよ。
      • OSが要求するアプリの形式や仕様は、ABIと呼ばれる仕様で定義されているらしいよ。
      • コンパイラが作成するオブジェクトファイルや、リンカが整形する形式は、OSやCPUの種類によって変化するみたいだよ。
    • (4) リンカによって作成されたアプリは、その内容をメインメモリに読み込むことで、アプリとして起動することが可能になるみたいだよ。
      • アプリをメインメモリに展開するソフトウェアを、ローダと呼ぶみたいだよ。
  • ここまでの調査で、コンパイルとは、ソースコードをOSが理解出来る形式に変換・翻訳する行為だと理解したよ。
    • Apache HTTP Serverの公式サイトに書かれている「コンパイル」とは、コンパイルとリンクの2つの作業を総称していることがわかったよ。
  • 要するに、ソースコードから、OS上で実行可能なアプリに変換・翻訳・整形する手順が書かれていたことがわかったよ。

  • 下図はおじさんの自習用メモだよ。はずかしいな。
    image.png

  • おじさんはプログラムのこと、まだよくわからないんだけど、ネットに落ちている情報をみると、おじさんのmacOSでもC言語をコンパイルすることができるみたいなんだ。

  • 前記の内容がいまいち実感できないから、簡単なC言語のプログラムを書いて、前記の図の流れを追体験してみたよ。

    • 注意: 事前にApple Storeから、Xcodeというアプリをインストールしておかないと出来ない作業みたいだよ。
  • C言語のプログラムをVS Codeなどで、「main.c」というファイル名で下記の通り書いてみたよ。(おじさんも書いている内容の意味はわからないけど、Hello って文字を表示するだけのプログラムみたいだよ。)

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Hello\n");
    return 0;
}

  • 続いて、ターミナルを開いて、main.cの保存先(おじさんの場合は/Users/oyaji以下に保存したよ)に作業するディレクトリを移動するよ。
cd /Users/oyaji

  • 続いて、プリプロセッサを試してみるよ。 macOSの環境では「gcc」と呼ばれるコマンドを利用することで試すことが出来るよ。
gcc -E main.c

  • すると、ぐちゃぐちゃ〜っと、プログラムコードのようなものが表示されるよ。長いから省略するけど、これが前処理(プリプロセス)したC言語のソースコードだよ。
  • #include(何かはおじさんもまだわからないけど)という記述が消えて、代わりにプログラムっぽい何かに置き換えられているよ・・・。
  • どうも、人間のために便利な表現がC言語は可能のようだけど、それを、コンピュータにとって都合の良い形に展開?整形?した結果のようだよ。
// 〜 中略 〜

int printf(const char * restrict, ...) __attribute__((__format__ (__printf__, 1, 2)));
int putc(int, FILE *);
int putchar(int);

// 〜 中略 〜

int main(int argc, char *argv[]) {
    printf("Hello\n");
    return 0;
}

  • プリプロセッサの仕事が何かは、実際にC言語をやってみないとよくわからなさそうだね。
  • 続いて、コンパイルを試してみるよ。 ターミナル上で次のコマンドを入力するとC言語のソースコードが、アセンブリ言語のソースコードに変換されるよ。
gcc -S main.c

  • 実行すると、main.cと同じディレクトリ内にmain.sというファイルが作られているよ。これがアセンブリ言語に変換した結果だよ。
    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14    sdk_version 10, 14
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $32, %rsp
    movl    $0, -4(%rbp)
    movl    %edi, -8(%rbp)
    movq    %rsi, -16(%rbp)
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %ecx, %ecx
    movl    %eax, -20(%rbp)         ## 4-byte Spill
    movl    %ecx, %eax
    addq    $32, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "Hello\n"


.subsections_via_symbols

  • 何書いているか、だんだんよくわからなくなってきたね。でも、アセンブリ言語も人間のためのプログラミング言語らしいよ。
  • アセンブリ言語の状態ではコンピュータは理解することが出来ないらしいよ。続いて、アセンブリ言語(main.s)から、コンピュータが理解可能なマシン語に翻訳してみるよ。
  • ターミナルで引き続き次のコマンドを実行すると、アセンブルが実行されるよ。
as -o main.o main.s

  • 成功すると、main.sと同じディレクトリに、main.o(オブジェクトファイル)が出来上がってるよ。
  • この状態だと、アプリを実行するために必要なパーツ(ライブラリ)や、OSが期待する形になってないみたいだよ。
  • 続いて、次のコマンドでリンクしてみるよ。(本当はldというリンカが使われるのだけど、指定するパラメータが複雑なのでgccを使うよ)
gcc -v -o main main.o

  • 実行結果が表示されるよ。(-vをつけているので) 「ld」というリンカが使われていることがわかるよ。
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -macosx_version_min 10.14.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -o main main.o -L/usr/local/lib -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.1/lib/darwin/libclang_rt.osx.a

  • リンク後のファイルは、main.oと同じディレクトリ内に「main」というファイルで作成されているよ。
  • 次のコマンドで、試しにターミナル上で「main」を実行してみるよ。
./main

  • 実行結果だよ。ターミナル上に「Hello」と表示されたよ。

image.png

  • なんだか、文字を表示するだけでも、大変なことが起きてるんだね。
  • C言語が何かよくわからないけど、C言語で書かれたソースコードから、実行可能なアプリになるまでを試すことが出来たよ・・・。

1-2. コンパイルの調査で、おじさんにとって衝撃的だったこと

  • ソースコード(プログラムコード)は、機械のために存在すると、おじさんは考えていたけど、むしろ人間のために存在するという事実に驚いたよ。
  • ABIに準拠するファイル形式は、2進数(0と1の2値)の羅列で構成されているようなので、人間が手作業で作成するのは非効率みたいなんだ。
  • Apache HTTPD Serverの場合は、C言語?と呼ばれる種類のソースコードで開発されているみたいだけど、それは開発効率を改善するためだって理解したよ。

1-3. 実行可能なアプリの状態で配布しないのはなぜだろう・・・

  • おじさんは未確認だけど、Windows用のインストーラは用意してくれているみたいだよ。
  • 前記の通り、OSが求めるアプリの形式は、OSの種類やCPUによって異なるみたいなんだ。Linuxだけをみても、複数の種類が存在するので、全ての環境に最適なアプリの形で配布するというのは無理がありそうだよ・・・。
  • ソースコードだけを提供して、実行する環境毎に最適なアプリを作成出来るようにした方が効率は良さそうだよね。(本当の理由はおじさんにはわからないけどね)

1-4. もう少し簡単そうな方法を調べてみるよ・・・

  • 実際にコンパイルとやらを試してみるべきかもだけど、それは別の機会に試すことにするよ。(むしろ試した方が楽しそうだけど・・・)
  • ちょっと切り口を変えて、Amazonの公式サイトにApache HTTPD Serverのインストール手順が存在するか確認してみるよ。(ベースイメージにAmazon Linux 2を選択していたことを思い出したよ)

2. AWS(Amazon Web Services)の手順を調べる

  • Dockerの例ではなかったけれど、AWSが提供するクラウドコンピュータ上にApache HTTPD Serverをインストールする手順があったよ。
  • Amazon Linux 2には、「yum」と呼ばれるインストーラが含まれるみたい!! うれしい。
  • yumは、あらかじめソースコードからアプリの形式に変換・翻訳済みのファイルを、インストールしてくれるソフトウェアのようだよ。
    • インストールだけではなく、インストール済みのソフトウェアのアップデートにも対応してくれるものみたい。
    • macOSのApp Storeや、Windows Updateのようなものなのかな。
  • ということで、おじさん、AWS上の手順を一度試してみることにするよ。

2-1. Dockerfileを書く前に・・・

  • 調べて→Dockerfileに書いて→イメージファイルを作成して→起動して・・・を繰り返せばいつか成功すると思うけど、なんだか効率が悪そうだよ。一発で成功するとは思えないし・・・。
  • コンテナ内で直接インストール作業を試すことも出来るみたいだから、次の作業順でチャレンジしてみるよ。
    • はじめにコンテナ内で直接インストール作業を試す。 試行錯誤する。
    • インストール作業に成功すれば、その作業をDockerfileに書く。
    • Dockerfileからイメージファイルを作成する。
    • イメージファイルから好きなときにWebサーバを起動することが出来るようになる。ハッピー。

2-2. Amazon Linux 2のベースイメージからコンテナを作成&起動&試行錯誤する準備

docker imagesで、作成済み or ダウンロード済みのイメージ一覧を取得する。

docker images

一覧のIMAGE IDをみると、「b94321659aca」に該当するイメージが、ベースイメージのAmazon Linux 2であることがわかるよ。

REPOSITORY          TAG                  IMAGE ID            CREATED             SIZE
ojisanimg1          1.0                  826623e781ef        19 hours ago        375MB
amazonlinux         2.0.20190508         b94321659aca        3 weeks ago         162MB

もしも、docker imagesで、amazonlinuxが見当たらない場合は、(No.10) おじさんが、LINE風アプリを開発する - Dockerを使用してコンテナを作成&実行してみるを実施するか、次のコマンドでベースイメージをダウンロードすることが出来るよ。

docker pull amazonlinux:2.0.20190508 

引き続き、次のコマンドで、ベースイメージを元にコンテナを作成&起動して、起動したコンテナ内をmacOSのターミナルから操作することが出来るようになるよ。
「b94321659aca」は、前記の「docker images」で確認したイメージIDだよ。

docker run -i -t --rm b94321659aca /bin/bash

すると、次のように表示されるよ。一見するとよくわからないけど、コンテナ内でコマンドを実行することが出来る状態なんだよ。

bash-4.2# 

試しに、OSの名前を表示するコマンド uname を実行してみるよ。

bash-4.2# uname -a
Linux aeaab6158a92 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

わおっ。Linuxと表示されているよ!! おじさんはmacOS上で実行しているから、明らかにmacOSではなくて、別のどこか(コンテナだよ)で実行されたことがわかる結果だね。

コンテナ内の作業をやめるときは、exitコマンドを実行すれば良いみたいだよ。

bash-4.2# exit

「docker run」コマンドで、何やらコンテナ内でコマンドを実行出来るようになったみたいだけど、どういうことかイマイチおじさんわからないよ。
おじさんは子供のときから「説明の出来ないことはやるな」って親に言われて育ってきたから、よくわからないことは、これからひとつずつ解決していくよ。
(でもいま思ったんだけど、誰でもはじめは説明出来ないよね。実際に調べたり手を動かして試してみないとさ。)

うーん。おじさんにはわけのわからない言葉が並んでるよ。つらいね。

docker runのパラメータ 解説
-i 標準入力を開いたままにする。
-t pseudo-tty(疑似端末)を割り当てる。
--rm --rmを指定すると、コンテナの停止後、停止したコンテナが自動的に削除されるようになる。(通常は、docker runを実行する都度、コンテナは新規に作成され、コンテナの停止後も、コンテナ自体は残り続ける。実験中は--rmをつけておくと便利。)
b94321659aca 「docker images」で確認したイメージIDだよ。(イメージIDは開発環境によって異なるよ)
/bin/bash コンテナ内のAmazon Linux 2に付属する、Bashと呼ばれるシェルを実行する。

2-2-1. 標準入力、擬似端末、シェルって何?!

  • docker runのパラメータの意味を理解するには、「プロセス(Process)」「標準入出力(Standard streams)」「制御端末(tty)」「擬似端末(pseudo-tty/pty)」「シェル(shell)」の存在を理解する必要があるみたいだよ。
  • なんだか本格的になってきたね・・・。おじさん不安でたまらないよ。

2-2-2. docker runと、Docker daemonと、コンテナの関係

  • 概要はここに書いてあったよ
  • ここでおじさんが言いたいことは、macOSから直接、コンテナの中身をいじくり回すことなんて出来ないよってことが言いたいんだよ。(でも実際はコンテナ内にアクセスすることが出来たっぽいよね)
    • docker runのやっていることは、指定するイメージファイルからコンテナを作成&起動するように、Docker daemonに対して、指示を送ることであって、docker run自体がコンテナを作成&起動することはないみたいだよ。
    • イメージやコンテナの管理はDocker daemonの仕事なので、おじさんのmacOSから、コンテナの中へアクセスするには、「docker run」↔「Docker daemon」↔「コンテナ」と、中継する必要があるみたいなの。
    • Docker daemonと、docker runを実行するコンピュータは、同じコンピュータとは限らない。
      • そもそも、Docker Desktop for Macのように、Docker daemonは仮想マシン(の上で動作するLinux)上で動作していたり、または、どこか別の場所に設定されたコンピュータ上で動作しているかもしれないんだ。この点からも、docker runのようにdockerコマンドを介して、間接的にコンテナとやりとりする必要があると言えるよ。

image.png

2-2-3. ターミナル起動〜docker runで、コンテナ内でコマンドを実行するまでの出来事

「docker run」コマンドはターミナルアプリ経由で実行しているよね。
ところがおじさんは、ターミナルがそもそも何者なのかよくわかってないんだよね。
docker runが実行されるまでの流れを、おじさんなりに調べてみたよ。(おじさんは勉強中だから、嘘が書いているかもしれないよ)

ターミナルアプリが何者かわからないけど、macOSで動作するアプリ。だから、前記のように、コンパイル?されるなどして、作られたものであることは間違いないと思うよ。
と、それはさておき、ターミナルアプリのアイコンをクリックすると、黒い画面が表示されるよ。

image.png

こんな感じでターミナルアプリが起動する。

image.png

  • macOSのターミナルアプリをwikipediaで調べてみると、ターミナルアプリは「端末エミュレータ(terminal emulator)」であると書かれているよ。

  • 続いて、端末エミュレータをwikipediaで調べてみると、端末エミュレータはコンピュータプログラムであり、端末(terminal)を再現(エミュレート)したものと書いてあるよ。

  • そもそも端末(terminal)がよくわからないので、さらにwikipediaで調べてみたよ。ここから先はおじさんの理解を書いていくよ。

macOSやLinux、UnixといったOSは、1台のコンピュータ(下図、右側のサーバと書かれている箱)を複数人に共有することが出来るみたいなんだけど、
複数人に1台のコンピュータを共有するシチュエーションで、端末エミュレータなるものが必要になるっぽいんだよ。(おじさん、絵心がなくて泣けて来るよ・・・)

AさんとBさんは、インターネットを経由して、遠隔地にあるサーバに接続して、遠隔操作することを考えてみるよ。
この場合、AさんとBさんの手元にはそれぞれ、ディスプレイやキーボードがあるけれど、遠隔地のサーバ側には、接続する人の数分のキーボードもディスプレイも接続されているわけじゃないんだ。
それでも、AさんやBさんのディスプレイやキーボードが、あたかもサーバ側に物理的に接続されているかのように、振る舞う仕組みを端末エミュレータを介して実現しているみたいだよ。(詳細は徐々に明らかにしていくよ)
このような仕組みがあるおかげで、遠隔で操作されるプログラムからすれば、物理的なキーボードで操作されているのか、それとも遠隔接続で操作されているのかを意識する必要がなくなるみたいだよ。なんだか賢いね。

image.png

続いて疑問になるのが、おじさんのmacOS自体は、おじさんの手に届くところにあるのに、なぜ端末エミュレータなんか使わなきゃならないのかってことだよ。
遠隔操作じゃないのね。
でも、ターミナルを複数起動すると、なんだか納得出来るものがあるよ。

ターミナルのウィンドウそれぞれが、独立して動作して、それぞれ異なるプログラムを動作させることが出来るようになっているよ。
つまり、1台のMacの中に、1つの接続というわけではなくて、Aさん・Bさんの例のように、ウィンドウの数だけ独立した接続が必要になるってことなんだ。
だから自分自身のコンピュータであったとしても、端末エミュレータが必要になるみたいだよ。

image.png

実際にはここまで単純ではないけれど、端末エミュレータの必要性を説明するための抽象画だと思ってほしいよ。

image.png

もう少し深掘りしていくよ。
結論を書くと端末エミュレータ自体は、シェル(Shell)と呼ばれるプログラムを(間接的に)起動して、疑似端末と呼ばれる特殊なファイルの内容を読み書きしているだけのようなんだ。 何を言っているのか、おじさんにもよくわからなくなってきたから、少しずつ整理していくよ。

と、深掘りするまえに、プロセスとは何か、標準入出力とは何かを知っておく必要があるみたいだよ。

まずはプロセスから。
アプリは起動すると、「プロセス」と呼ばれるアプリが起動した状態になるらしいよ。
例えば、WindowsのExcelやWordみたいなアプリって、複数立ち上げると、それぞれのワークブックごとに内容が異なるよね。 この例のように、同じアプリであっても起動後のアプリの状態(Excelのワークブック)は異なるように、実行中のアプリの状態を表現する単位みたいだよ。

下図はWindowsでExcelのプロセスが5つ動作しているイメージだよ。(Windowsを例にしているのは、macOSの場合だと、一つのアプリは一つのプロセスでしか動作しないからだよ。macOS版のExcelを例にすると、ワークブックをいくら開こうが、Excelのプロセスは一つだけだよ。)
image.png

image.png

ここまでで、アプリ(プログラム)は、実行中になると「プロセス」と呼ばれる状態になるってことがわかったよ。
プロセスは、プロセス同士で通信する手段・方法が様々用意されているみたいだよ。
プロセス同士の通信を、プロセス間通信(IPC)と呼ぶらしいよ。

プロセス間通信の内容によっては、アプリ側であらかじめ考慮しておく必要があるけれど、標準で用意されているデータの伝送手段があって、この標準的な伝送手段を標準入出力と呼ぶみたいだよ。
標準入出力は、起動したプロセスに標準的に与えられるデータの伝送手段で、入力側も出力側も共に何かしらのファイルに関連付けられているみたいだよ。

image.png

実験のため、次のコマンドを試してみるよ。

echo oyaji

こんな感じで、ターミナルに「oyaji」と表示されるだけだよ。
image.png

echoコマンドのマニュアルを表示して意味を確認してみるよ。マニュアルを表示するには「man」コマンドを使えば良いよ。

man echo

すると、次のようにマニュアルが表示されるよ。(終了するには、「q」アルファベットのキューを入力)
image.png

マニュアルには「標準出力(standard output)に引数を書き込む」旨が書かれているよ。
この場合の引数とは、「oyaji」にあたる部分だよ、つまり、echoコマンドのパラメータのことだよ。

echoコマンドも例外ではなく、実行中はプロセスとして動作するよ。
image.png

再び、echoコマンドのマニュアルに書いてあった「標準出力(standard output)に引数を書き込む」が重要になってくるよ。
「echo oyaji」を実行すると、「oyaji」の文字が標準出力に書き込まれるみたい。
でも実際はターミナルのウィンドウに「oyaji」と出力(表示)されたよ。これは、ターミナルのプロセスと、echoコマンドの標準出力との間に何かしらの関係があることを示唆するものだよ。

この謎を解き明かすためには、macOSやLinux、UnixといったOSの基本原理である「デバイスファイル(スペシャルファイル」についても知っておく必要があるよ。

話がややこしくなってきたので、箸休めに、おじさんの書いた猫の絵で癒やされてほしいよ。

image.png

どうだろう。癒やされたかな。
次はデバイスファイル(スペシャルファイル)についてだよ。

Unixと呼ばれるOS(や、Unix系OSと呼ばれるLinuxなど)では、Everything is a fileといった設計思想があるらしくて、ありとあらゆるものをファイル(ただしくは、ファイルディスクリプタ)で表現しようという考えみたいだよ。
この設計思想のおかげで、入出力といったデータの流れのあるものをファイルという統一された方法で操作出来るようになるみたい。

例えば、CDやDVDのような機器の操作から、インターネットなどのネットワーク処理(socket)すらも、Unix系のOSでは、ファイル(ファイルディスクリプタ)として扱うようなんだ。
このような特殊なファイルをデバイスファイル(またはスペシャルファイル)と呼ぶみたいだよ。

ここで言いたいのは、macOSやLinux、Unix上での「ファイル」とは、おじさんのポエムのようなテキストファイルだけではなくて、ハードウェア(CDやDVD、キーボードやディスプレイなど)やネットワークなどの、入出力といったデータの流れのあるものを指すこともあるってことなんだよ。

image.png

おじさんが使っているmacOSでも同じことが言えるんだよ。
ここまでを整理すると次の情報が手に入ったよ。

  • アプリは起動するとプロセスと呼ばれる状態となってプログラムが実行されるよ。
  • プロセスには、アプリが意識していなくても、プロセスへのデータの通信経路が作成され、これを標準入出力と呼ぶ。
  • 標準入出力は、データの流れを表すもので、データの入力元に何かしらのファイル、データの出力先にも何かしらのファイルが関連付けられているよ。
    • アプリ(プロセス)からすれば、標準入出力の元・先が何であるのかを知る必要はないみたいだよ。
  • Unix系OSでは、データの入出力といったデータの流れをファイル(ファイルディスクリプタ)で表現しているよ。macOSも同様だよ。
  • おじさんのポエムのようなファイルとは別に、例えば、ディスプレイやキーボード、ネットワークといった機器もファイル(ファイルディスクリプタ)で表現されている。このような特殊なファイルをデバイスファイル(またはスペシャルファイル)と呼ぶみたいだよ。

これらの情報を元に、ターミナルが起動して、echoコマンドを実行するまでの出来事を詳細に解説してみるよ。

まずターミナルアプリがユーザの操作をきっかけに実行されるよ。当然、ターミナルアプリもプロセスとして実行するよ。
image.png

ターミナルプロセスがopenシステムコールなどを使用して、「/dev/ptmx」(擬似端末)のデバイスファイル(スペシャルファイル)にアクセス。
すると、擬似端末のマスタとスレーブのペアが作成される。(このファイルが何者かは後述)
以降、擬似端末のことをpty(マスタ)、pty(スレーブ)と書く。

image.png

続いて、ターミナルプロセスがforkシステムコールを実行する。forkシステムコールを実行すると、実行元プロセスの写しが作成される。(プログラムの実行状態なども含め複製される)

image.png

ターミナルプロセス(写し)が、dup2システムコールを実行し、ターミナルプロセス(写し)の標準入出力先をpty(スレーブ)に変更する。

image.png

ターミナルプロセス(写し)が、execシステムコールを実行し、loginコマンドのプロセスにターミナルプロセス(写し)を置き換える。

image.png

loginプロセスに置き換え後も、標準入出力先はpty(スレーブ)に維持される。(ファイルディスクリプタの状態はfork/exec後も維持されるため)
loginプロセスはログインユーザ固有の設定(例えば、作業ディレクトリをホームディレクトリに変更する)などを行い、forkシステムコールを実行する。

image.png

loginプロセスの複製が作成される。
loginプロセス(写し)は、execシステムコールを実行して、loginプロセス(写し)をbashプロセスに置き換える。
loginプロセス(写しではない)は、closeシステムコールを実行して標準入出力を閉じる。(この時点でloginプロセスの仕事は何もないため)

image.png

bashプロセスが起動する。なお、bashプロセスはシェルと呼ばれるプログラム。
ターミナルプロセスに対して、「echo oyaji」と入力すると、ターミナルプロセスはpty(マスタ)に「echo oyaji」と書き込みする。
pty(マスタ)に書き込みした内容は、pty(スレーブ)の先の標準入力に流れ込むため、bashプロセス側で「echo oyaji」の入力を取り出すことが出来る。
ターミナルからの入力を間接的に受け取った後、bashプロセスはforkシステムコールを実行する。

  • pty(マスタ)とpty(スレーブ)はつながっている。
  • pty(マスタ)に書き込むデータは、pty(スレーブ)から読み取ることが出来る。
  • pty(スレーブ)に書き込むデータは、pty(マスタ)から読み取ることが出来る。
  • pty(擬似端末)は、プロセス間通信の手段の一つと言える。

image.png

bashのプロセスが複製される。
bashプロセス(写し)が、execシステムコールを実行して、bashプロセス(写し)をechoプロセスに置き換える。
echoプロセスも複製元のbashプロセスと同様に、標準入出力先はpty(スレーブ)に設定されている。

image.png

echoプロセスは、コマンドの引数(パラメータ)に与えられた内容を標準出力に書き込む処理を実行する。
引数の値は「oyaji」なので、標準出力に「oyaji」を書き込む。
標準出力先はpty(スレーブ)のため、結果的にpty(マスタ)の入力に「oyaji」のデータが流れる。
ターミナルプロセスはpty(マスタ)から、pty(スレーブ)の書き込みを取り出して、ターミナルウィンドウに出力する。
echoプロセスは仕事を終えたため、プロセスを終了する。

image.png

その結果こうなる。

image.png

ここまでの情報を整理をしてみるよ。

  • pty(擬似端末)を使用して、プロセス間(ターミナル〜bashやbash以降のプロセス)のデータの受け渡しを実現している。
  • ターミナルはpty(疑似端末)のマスタに対して流れてくるデータを表示、キーボードからの入力を、pty(疑似端末)のマスタに書き込む。
  • ターミナルはforkシステムコールを使用して、自身のプロセスを複製する。
  • 複製したプロセスの標準入出力先をpty(スレーブ)に設定する。なお、pty(スレーブ)の名前を取得するには、ptsnameシステムコールを使用する。
  • 複製したプロセスから、execシステムコールを実行して、複製したプロセスをloginプロセスに置き換える。
    • なぜforkとexecが別れているのか考えてみると面白いと思うよ。
    • なぜわざわざ、プロセスを複製してから、目的のプログラムのプロセスに置き換えるように設計されているのか。
    • こんな言葉があるよ。
  • loginプロセスでは、ログインユーザ固有の設定(例えば、作業ディレクトリをユーザのホームディレクトリに設定する)など実行した後に、forkシステムコールを実行する。
  • loginプロセス(写し)では、execシステムコールを実行し、loginプロセス(写し)をbashプロセスに置き換える。
  • bashプロセスでは、fork&execで、プロセス複製後にechoプロセスに置き換える。
  • echoプロセスは標準出力に書き込みする。標準出力先はpty(スレーブ)のため、ターミナルが監視するpty(マスタ)に伝わり、ターミナルの画面上にechoプロセスが書き込む内容が出力される。

いまいち、擬似端末(pty)の実感が沸かないと思うんだけど、簡単に試すことが出来るよ。
下図はターミナルを2つ起動してみた例だよ。
ttyコマンドを実行することで、標準入力に設定されているpty(スレーブ)を確認することが出来るよ。

tty

image.png

すると、それぞれのターミナルに異なる内容の文字が表示されるはずだよ。

おじさんの場合は、「/dev/ttys000」と「/dev/ttys001」とそれぞれ表示されたよ。
「/dev/ttys000」と「/dev/ttys001」それぞれが、pty(スレーブ)のファイルを表しているよ。このような特殊なファイルをデバイスファイル(スペシャルファイル)と呼ぶらしいよ。
ターミナルのウィンドウそれぞれに、pty(マスタ)・pty(スレーブ)のペアが設定されていることがわかるよ。

試しに、ターミナルのウィンドウそれぞれに表示されているpty(スレーブ)に対して書き込みしてみるよ。
次のコマンドを実行すると、/dev/ttys001に対して、helloという文字を書き込むことになるよ。
echoコマンド自体は引数(hello)の内容を標準出力に書き込むというものだったけど、「>」(リダイレクトと呼ぶ)を書くことで、
標準出力を「/dev/ttys001」に変更することになるよ。(まるでdup2システムコールそのものだね)
なお、「>」リダイレクトや、echoプロセスの起動は、bashプロセス(シェル)が実行していることだよ。

echo hello > /dev/ttys001

image.png

うーん。どんどん理解してきた気がするね。
bashはシェルと呼ばれるプログラムだけど、macOSやLinuxカーネルといったOSと対話するための手段の一つだよ。
シェルはOSが提供する機能(システムコール)を実行して、他のアプリやコマンドを起動したり、簡単なプログラムも実行することが出来るみたいだよ。

image.png

ついでに、実行中のプロセスを確認する方法もここで書き残しておこうと思うんだ。
次の「ps」コマンドで実行中のプロセスを確認することが出来るよ。

ps -eaf

わわっと、動いているプロセスが多すぎて、何がなんだか・・・。
image.png

「Terminal」の文言が含まれる行に限定して表示されるように改良してみるよ。

ps -eaf | grep Terminal

「|」は、パイプといって、リダイレクトのように標準入出力を変更するものだよ。

psコマンドと、grepコマンドの意味はそれぞれ下図の通りだよ。

image.png

この2つのコマンドを、パイプと呼ばれるデバイスファイル(スペシャルファイル)を使って、標準入出力をそれぞれ繋ぎ合わせることで
psコマンドの結果をgrepコマンドでフィルタリングすることが出来るんだよ。

image.png

実行結果だよ。(注:UID、PIDなどのヘッダは、わかりやすくするために手で追加したものだよ)

  UID   PID  PPID   C STIME   TTY           TIME CMD
  501 84232     1   0  5:41PM ??         0:12.44 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
  501 93153 84234   0  7:27PM ttys000    0:00.00 grep Terminal

PID(プロセスID)列は、プロセスを識別するための固有の番号を表すよ。
この例の場合、PIDが「84232」のプロセスがターミナルアプリのプロセスだよ。
なお、PPID(親プロセスID)列は、fork元の親プロセスのプロセスIDを意味するよ。 PIDが1のプロセスからTerminalが作成されていることがわかるけど、PIDが1のプロセスは特別なプロセスで、initプロセス等と呼ぶことがあるみたいだよ。(はじめに作成されるプロセスだよ)

引き続き、ターミナルのプロセスID(84232)で検索してみるよ。

ps -eaf | grep 84232

実行結果だよ。 loginプロセスが2つあることに注目だよ。
これはターミナルウィンドウを2つ開いているからだよ。

  UID   PID  PPID   C STIME   TTY           TIME CMD
  501 84232     1   0  9:48PM ??         0:30.23 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
    0 84233 84232   0  9:48PM ttys000    0:00.01 login -pf oyaji
  501 93246 84234   0  4:09AM ttys000    0:00.00 grep 84232
    0 93137 84232   0  4:05AM ttys001    0:00.02 login -pf oyaji

さらに、1つ目のloginプロセスのプロセスIDで検索してみるよ。

ps -eaf | grep 84233

結果だよ。 bashプロセスがあるね。

  UID   PID  PPID   C STIME   TTY           TIME CMD
    0 84233 84232   0  9:48PM ttys000    0:00.01 login -pf oyaji
  501 84234 84233   0  9:48PM ttys000    0:00.10 -bash
  501 93302 84234   0  4:11AM ttys000    0:00.00 grep 84233


もう片側のloginプロセスも。

ps -eaf | grep 93137

やっぱりbashプロセスが存在するね。前記の説明通りプロセスは作成されていることがわかるよ。
プロセスは親子関係(forkするプロセス、forkされたプロセス)があることもわかったよ。

  UID   PID  PPID   C STIME   TTY           TIME CMD
  501 93351 84234   0  4:13AM ttys000    0:00.00 grep 93137
    0 93137 84232   0  4:05AM ttys001    0:00.02 login -pf oyaji
  501 93138 93137   0  4:05AM ttys001    0:00.01 -bash


長かったけど、docker runのパラメータの意味を理解することが本来の課題だったんだよ。長くて忘れかけていたよ。
こんなコマンドだったよ。

docker run -i -t --rm b94321659aca /bin/bash

下図がdocker runの全貌だよ。ここまでの情報を集結させて挑むことにするよ。
image.png

  • docker runの右端にある「/bin/bash」とは、図の中にあるコンテナプロセス内で、どのようなプログラムを実行するのかを指定しているんだよ。この例では、「/bin/bash」なので、「コンテナ内の/binディレクトリのなかにある、bashコマンドを実行する」という意味になるんだよ。
    • この指定を別のコマンドに変えるだけで、コンテナ内で実行するコマンドを変更することが出来るよ。
  • 「-t」は、docker runの実行元で、コンテナプロセス内の標準入出力を中継出来るように、擬似端末(pty)を作成することを指示するものなんだよ。(Docker daemonが動作するLinux上で擬似端末を作るように指示)
  • 「-i」は、ターミナルからの入力をコンテナ側に伝搬することが出来るように、Linux側の標準入力を有効にする設定のようだよ。
  • 「-rm」は、コンテナ終了後に、コンテナを削除するように指示するものだよ。(ゴミコンテナが残らないように)
  • docker(run)と、Docker daemonは、それぞれ異なるコンピュータ上で動作する可能性があるので、ネットワークやインターネットを経由して送受信する必要があるんだ。おじさんも今はよくわからないけど、ソケットと呼ばれる通信方法で実現しているみたいだよ。

あとがき

うへーーー。おじさんちょっと疲れたよ。前準備だけでこんなに大変だと思わなかったよ〜。
でも何でも積み重ねだから、徐々に楽になっていくことを期待するよ。
次回は、コンテナ内をこねくりまわして、Apache HTTP Serverを何がなんでもインストールしてみるよ。

そうそう忘れてた。
macOSのloginコマンドなどのプログラムは、ソースコード(Libsystem-1252.200.5)にて公開されているよ。(C言語で書かれているみたいだよ)
コンパイルする方法もどこかに書かれていることだろうから、コマンドを改造してみるのも面白いかもしれないね。
ターミナルアプリのソースコードが見当たらなかったので、ターミナル周辺に関しては状況証拠や他のターミナルのプログラムを参考に勉強してみたよ。

参照・メモ

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