20190701のdockerに関する記事は10件です。

プロキシとリバースプロキシの違いをまとめて、ついでにDocker使ってnginxでリバースプロキシを立ててみる

はじめに

プロキシとリバースプロキシってややこしいですよね。
この記事では、概念図を交えながらプロキシとリバースプロキシの違いをまとめていきます。

最後に実践として、Dockerを使ってローカルでリバースプロキシ(nginx)を立ててみます。

プロキシとリバースプロキシの違い

混乱しやすいプロキシとリバースプロキシの違いについて、まとめておきます。

プロキシとは

一般的に使われるプロキシといえば、「forward proxy」のことをさします。
forward proxyとは、ある別のWebサイトへのリクエストを受け取り、クライアントの代わりにWebサイトへリクエストを送信するサーバのことです。

こちらの図がforward proxyの概念図です。
クライアントXがとあるWebサイト(サーバZ)にアクセスする時にプロキシサーバYを利用している例です。
image.png

リクエストは クライアントX → プロキシY → WebサーバZ という流れで伝わっていきます。

プロキシを使う目的

このようなforward proxyを使う目的が、WebサーバZに直接アクセスできないクライアントXからZにリクエストを送信できるようにすることです。例としては、

  • クライアントがWebサイトの権限者からブロックされているとき(IPでブラックリスト)
  • Youtubeへの通信をブロックしている会社からYoutubeにアクセスしたいとき
  • 政府が特定のニュースサイトへの通信を遮断しているとき
  • 大学がHTTP以外のプロトコルを制限しているとき(研究室内のサーバにアクセスするために踏み台としてプロキシを使う)

などが挙げられます。

リバースプロキシとは

続いて、リバースプロキシについてみてみましょう。
image.png

リクエストの流れはプロキシと同様で、 クライアントX → リバースプロキシY → WebサーバZ のように伝わっていきます。しかし、リバースプロキシでは、クライアントXは、WebサーバZの存在を知らないという大きな違いがあります。

クライアントXは、Publicに存在しているリバースプロキシYと通信することで、リクエスト送信とレスポンス受信ができると考えるはずですが、実際はWebサーバZがその処理を行っています。

リバースプロキシを使う目的

リバースプロキシを使うと、クライアントとWebサーバの間にワンクッションが入ることで、様々な恩恵が得られます。
例えば、

  • 機密上の理由などでWebサーバをPublicにしたくないとき
  • Webサーバをダウンせずに何かしらのPublicな通信を遮断したいとき(リバースプロキシだけダウンさせれば良い)
  • 巨大なサイトを扱う場合(一つのWebサーバではトラフィックをさばけない。リバースプロキシの先に多数のWebサーバを用意することで、クライアントに近いサーバを選択的に使用できる)

などが挙げられます。

プロキシとリバースプロキシの違いまとめ

クライアントX → Y → サーバZ という流れがあるとき

  • Y = プロキシ :クライアントXはサーバZの存在を知っている
  • Y = リバースプロキシ :クライアントXはサーバZの存在を知らない

実際に(ローカルで)リバースプロキシを立ててみよう

リバースプロキシは広い概念で、HTTPリバースプロキシやTCPリバースプロキシなどがあります。

ここでは、HTTPリバースプロキシの一つであるnginxをローカルで立ててみましょう。

nginxって何

nginxとは、Webサーバからリバースプロキシまでなんでもこなせる優れたサーバサイドソフトウェアです。

一般に、Webサーバは専用のアプリケーション(Apacheやnginx)を使い、特定のポートでListenして、クライアントとTCPコネクションを結びます。クライアントからHTTPリクエストが送られてきたら、なんらかの処理をした後、送信元にレスポンス(例えばindex.htmlなど)を返します。

nginxは、Webサーバのアプリケーションでもあるので、今回はこの用途でも使ってみましょう。

今回の構成は、ある一つのWebサイトの中に犬用コンテンツと猫用コンテンツの二つがあり、URLのパスでそれぞれにアクセスできるものを考えます。
image.png

HTTPリバースプロキシでは、クライアントから受け取ったリクエストのHTTPのパスで異なるサーバに割り振ることができます。
クライアントからはHTTPリバースプロキシサーバとしてのnginxに /dog もしくは /cat のパスで localhost:80(もしくは単に localhost)にアクセスします。
すると、リバースプロキシではパスを解析して、パスごとにあらかじめ設定したWebサーバにルーティングを行います。
リバースプロキシは、Webサーバからレスポンスが返ってくると、そのレスポンスをまるで自分が処理したかのようにクライアントに返します。

目標

  • localhost/cat にリクエストを送ると、猫好きのためのページを表示
  • localhost/dog にリクエストを送ると、犬好きのためのページを表示
  • 猫用、犬用サーバは別に分ける
  • クライアントは、それらのサーバの存在やポート番号を知らないでよい

ディレクトリ構成

.
├── docker-compose.yml
├── cat-server
│   └── index.html
├── dog-server
│   └── index.html
└── reverse-proxy
    └── nginx.conf

ソースコードはGitHub(zawawahoge/reverse-proxy)にアップロードしています。cloneして docker-compose up したらローカルで実際にこの構成を立てることができます。

実装詳細

docker-compose

docker-compose.yml
version: '3'

services:
  dog-server:
    image: nginx
    container_name: 'dog-container'
    volumes:
      - ./dog-server:/usr/share/nginx/html
    ports:
      - 7000:80

  cat-server:
    image: nginx
    container_name: 'cat-container'
    volumes:
      - ./cat-server:/usr/share/nginx/html
    ports:
      - 7001:80

  reverse-proxy:
    image: nginx
    volumes:
      - ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 80:80

これを含むディレクトリで docker-compose up を実行することで、二つのWebサーバと一つのリバースプロキシが起動します。
各コンテナに対し、必要なファイルをvolumeとしてマウントしています。これらのファイルの中身についてもみてみます。

インデックスページ(猫用、犬用)

犬用と猫用サーバのそれぞれについて、インデックスページを作っておきましょう。

dog-server/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>犬好きのためのページ</title>
  </head>
  <body>
    <h1>犬好きのためのページ</h1>
  </body>
</html>
cat-server/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>猫好きのためのページ</title>
  </head>
  <body>
    <h1>猫好きのためのページ</h1>
  </body>
</html>

非常にシンプルなものですが、これで十分でしょう。

リバースプロキシ用の設定ファイル

続いて、nginxリバースプロキシ用の設定ファイルを書く必要があります。

reverse-proxy/nginx.conf
events {
    worker_connections  16;
}
http {
    server {
        listen 80;
        server_name localhost;
        location /dog {
            proxy_pass http://host.docker.internal:7000/;
            proxy_redirect off;
        }
        location /cat {
            proxy_pass http://host.docker.internal:7001/;
            proxy_redirect off;
        }
    }
}

nginxの設定ファイルの書き方

events

nginxの設定ファイルは、コンテクスト(context)と呼ばれる {} で囲まれたブロックで構成されています。
最初のコンテクストである events については公式(英語)で説明されています。

Syntax: events { ... }
Default: —
Context: main
Provides the configuration file context in which the directives that affect connection processing are specified.

コネクション全体に関する設定を意味するコンテクストです。
worker_connections は、同時に接続できるワーカープロセスの数を表します(デフォルト1)。

http

http コンテクストには、HTTPサーバについての設定を書きます。

Provides the configuration file context in which the HTTP server directives are specified.

server

server コンテクストは、仮想サーバのための設定をします。
公式ドキュメントに詳しいドキュメントが書かれています。

Syntax: server { ... }
Default: —
Context: http
Sets configuration for a virtual server. (略)

下のサイトが参考になります。
nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定

HTTPリクエストを受け取ってからの流れ

HTTPリクエストが来ると、 http コンテクストにある server コンテクストで当てはまるものを探していきます。 server コンテクストには、 listenserver_name の二つが記述され、それらが条件となります。

  1. まずは、 listen で指定されたIPアドレス(またはホスト)とポート番号がリクエストのものと一致するかどうか調べます。
  2. 当てはまったら、 server_name がHTTPヘッダの Host と同じであれば、その server コンテクストに書かれた処理をすることになります。

今回利用するのは次のような server コンテキストです。

    server {
        listen 80;
        server_name localhost;
        # 処理...
    }

意味としては、 listen でポート80にきたリクエストかどうか判定し、 server_name で リクエストヘッダの Hostlocalhost と一致しているかどうかを判定します。どちらも当てはまるようなら続く処理が実行されるというものです。

今回の場合、リバースプロキシとして機能させるため、HTTPのURL内のパスを解析し、別のWebサーバにルーティングする処理をすることになります。

serverコンテクストの処理
        location /dog {
            proxy_pass http://host.docker.internal:7000/;
            proxy_redirect off;
        }
        location /cat {
            proxy_pass http://host.docker.internal:7001/;
            proxy_redirect off;
        }

続いて、パスが当てはまる location コンテクストが探され、 /dog というパスであれば、 proxy_pass に書かれたURIにルーティングされます。

パスがさらに長い場合、例えば localhost/dog/list であれば、 http://host.docker.internal:7000/list にルーティングされることになります。

proxy_passの注意点

ややこしいことですが、 proxy_pass はURIの場合とそうでない場合でルーティングのパスが変わるので注意してください。
nginxのproxy_passの注意点

Dockerコンテナからみたホストのポート

http://host.docker.internal はDockerのコンテナから見たホストを指していて、コンテナ内からホストのポート 7000 にアクセスする場合にはこのように書くと名前解決してくれてホストそのもののポートにアクセスできます。

このような書き方が必要になるのは、Dockerコンテナ内で localhost と記述した時は、Dockerを起動しているホストではなくコンテナ自身を指す仕様になっているからです。もちろん、Dockerを使わずにnginxを立ち上げた場合は localhost に置き換えて構いません。

要するに、 localhost/dog に来た場合は、Dockerを起動しているホストのポート 7000 にHTTPリクエストを送信する仕組みになっています( /cat の場合も同様に 7001 )。

結果

docker-compose up を実行し、curlコマンドに localhost/dog でリクエストを送り、レスポンスを見てみましょう。

curl --verbose localhost/dog
HTTPリクエストヘッダ
> GET /dog HTTP/1.1
> Host: localhost
> User-Agent: curl/7.54.0
> Accept: */*
HTTPレスポンスヘッダ
< HTTP/1.1 200 OK
< Server: nginx/1.17.0
< Date: Mon, 01 Jul 2019 13:38:14 GMT
< Content-Type: text/html
< Content-Length: 264
< Connection: keep-alive
< Last-Modified: Sun, 30 Jun 2019 13:43:59 GMT
< ETag: "5d18bc9f-108"
< Accept-Ranges: bytes
HTTPレスポンスbody
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>犬好きのためのページ</title>
  </head>
  <body>
    <h1>犬好きのためのページ</h1>
  </body>
* Connection #0 to host localhost left intact
</html>%

各コンテナ(リバースプロキシ・Webサーバ)のログをみてみましょう。

nginxのログ
reverse-proxy_1  | 172.31.0.1 - - [01/Jul/2019:13:38:14 +0000] "GET /dog HTTP/1.1" 200 264 "-" "curl/7.54.0"
dog-container    | 172.31.0.1 - - [01/Jul/2019:13:38:14 +0000] "GET / HTTP/1.0" 200 264 "-" "curl/7.54.0" "-"

結局何が起こったの?

注目すべきなのは、クライアント側(curlを叩いた側)は、 dog-container というWebサーバの存在を全く知らないでも通信できているという点です。

つまり、クライアントからは、 localhost/dog というGETリクエストを localhost:80 に送り、そこからレスポンスを受け取ったように見えますが、実際は dog-container という(本当の)Webサーバからレスポンスが返ってきているのです。

もちろん猫用もみれます。

curl localhost/cat
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>猫好きのためのページ</title>
  </head>
  <body>
    <h1>猫好きのためのページ</h1>
  </body>
</html>%

もちろんブラウザからもみれます。

image.png

バッチリですね。

まとめ

リバースプロキシの概念的な理解から、nginxでの実装までみてきました。
Webサーバを外から直接アクセスできないようにするだけでなく、リクエストに応じてサーバを自在に変えることができるため、柔軟なサーバ構成を考えられるようになるかと思います。

本記事がリバースプロキシやnginxの理解に役に立てば幸いです。

ソースコードはGitHub(zawawahoge/reverse-proxy)にアップロードしていますので、ぜひローカルで試してみてください。

参考サイト

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

プロキシとリバースプロキシの違いまとめ

はじめに

プロキシリバースプロキシってややこしいですよね。
この記事では、概念図を交えながら混乱しやすいプロキシとリバースプロキシの違いをまとめていきます。

プロキシとは

一般的に使われるプロキシといえば、「forward proxy」のことをさします。
forward proxyとは、ある別のWebサイトへのリクエストを受け取り、クライアントの代わりにWebサイトへリクエストを送信するサーバのことです。

こちらの図がforward proxyの概念図です。
クライアントXがとあるWebサイト(サーバZ)にアクセスする時にプロキシサーバYを利用している例です。
image.png

リクエストは クライアントX → プロキシY → WebサーバZ という流れで伝わっていきます。

プロキシを使う目的

このようなforward proxyを使う目的が、WebサーバZに直接アクセスできないクライアントXからZにリクエストを送信できるようにすることです。例としては、

  • クライアントがWebサイトの権限者からブロックされているとき(IPでブラックリスト)
  • Youtubeへの通信をブロックしている会社からYoutubeにアクセスしたいとき
  • 政府が特定のニュースサイトへの通信を遮断しているとき
  • 大学がHTTP以外のプロトコルを制限しているとき(研究室内のサーバにアクセスするために踏み台としてプロキシを使う)

などが挙げられます。

リバースプロキシとは

続いて、リバースプロキシについてみてみましょう。
image.png

リクエストの流れはプロキシと同様で、 クライアントX → リバースプロキシY → WebサーバZ のように伝わっていきます。しかし、リバースプロキシでは、クライアントXは、WebサーバZの存在を知らないという大きな違いがあります。

クライアントXは、Publicに存在しているリバースプロキシYと通信することで、リクエスト送信とレスポンス受信ができると考えるはずですが、実際はWebサーバZがその処理を行っています。

リバースプロキシを使う目的

リバースプロキシを使うと、クライアントとWebサーバの間にワンクッションが入ることで、様々な恩恵が得られます。
例えば、

  • 機密上の理由などでWebサーバをPublicにしたくないとき
  • Webサーバをダウンせずに何かしらのPublicな通信を遮断したいとき(リバースプロキシだけダウンさせれば良い)
  • 巨大なサイトを扱う場合(一つのWebサーバではトラフィックをさばけない。リバースプロキシの先に多数のWebサーバを用意することで、クライアントに近いサーバを選択的に使用できる)

などが挙げられます。

プロキシとリバースプロキシの違いまとめ

クライアントX → Y → サーバZ という流れがあるとき

  • Y = プロキシ :クライアントXはサーバZの存在を知っている
  • Y = リバースプロキシ :クライアントXはサーバZの存在を知らない

実践編ーDockerを使ってnginxでリバースプロキシを立ててみるも書いています。

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

Ubuntu 18.04上のDockerでコンテナのベースサイズを自在に変える

関連記事

- Docker on CentOS7で特定コンテナの容量制限を柔軟に行ってみた

https://qiita.com/haniokasai/items/b736604a78506411d2fc
- CoreOSでdockerのbasesizeをかえる。
https://qiita.com/haniokasai/items/b34c0e1faaf8c4b5d2dd
- dockerでpquotaを有効にする
https://qiita.com/haniokasai/items/805328aec39137e671b3
- Dockerで特定のコンテナサイズを自由に拡張する設定する devicemapper編
https://qiita.com/haniokasai/items/2b7a1889e4930b7682c9

環境

Ubuntu Mate 18.04 on VBox on Windows 10
インストール時に

  • /boot をext4でフォーマット
  • / をxfsでフォーマット

Docker info

# docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 18.09.7
Storage Driver: overlay2
 Backing Filesystem: xfs
 Supports d_type: true
 Native Overlay Diff: true

無設定時

#  docker run --storage-opt size=5g -t -i ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
^[[2~latest: Pulling from library/ubuntu
5b7339215d1d: Pull complete 
14ca88e9f672: Pull complete 
a31c3b1caad4: Pull complete 
b054a26005b7: Pull complete 
Digest: sha256:9b1702dcfe32c873a770a32cfd306dd7fc1c4fd134adfb783db68defc8894b3c
Status: Downloaded newer image for ubuntu:latest
docker: Error response from daemon: --storage-opt is supported only for overlay over xfs with 'pquota' mount option.
See 'docker run --help'.

grubの編集

vi /etc/default/grub

GRUB_CMDLINE_LINUX に "rootflags=uquota,pquota" を追加。

sudo grub-mkconfig -o /boot/grub/grub.cfg
docker run --storage-opt size=5g -t -i ubuntu /bin/bash
root@67f434ee5ab8:/# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay         5.0G  8.0K  5.0G   1% /
tmpfs            64M     0   64M   0% /dev
tmpfs           2.8G     0  2.8G   0% /sys/fs/cgroup
/dev/sda5        55G  8.2G   47G  16% /etc/hosts
shm              64M     0   64M   0% /dev/shm
tmpfs           2.8G     0  2.8G   0% /proc/asound
tmpfs           2.8G     0  2.8G   0% /proc/acpi
tmpfs           2.8G     0  2.8G   0% /proc/scsi
tmpfs           2.8G     0  2.8G   0% /sys/firmware
root@67f434ee5ab8:/# 

サイズが変わった。

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

dockerコンテナ内のビルド済みファイルを更新する方法

はじめに

dockerコンテナ内でエントリーポイントとして指定された実行ファイルを更新する方法について調べた内容になります。

背景・問題点

JavaやkotlinでWebアプリケーション開発を行う上でdockerコンテナを立てて開発することはよくあるかと思います。
下に示すのはアプリケーション層用コンテナのDockerfileの一部です。

FROM openjdk:8-jdk-alpine
COPY build/libs/application.jar application.jar
ENTRYPOINT  ["java", "-jar", "application.jar"]

ファイル内で行っているのは以下です。
1行目:ベースイメージの指定
2行目:ビルド済みjarファイル(実行ファイル)をホスト側(左)からコンテナ側(右)にコピー
3行目:コンテナの起動プロセスを設定(上記では渡したjarファイルを実行している)

最後のエントリーポイントで指定したコマンドの実行プロセスが起動することでアプリケーションが動作します。
開発中は変更のたびにプログラムの再ビルドを行いますが、実際のアプリケーション上で変更を確認するためには再ビルドした結果をコンテナに反映させなければなりません。単純な方法としては作成したコンテナを一旦停止し、削除した後でコンテナの再ビルド・起動を行うことが考えられますが、それなりの時間がかかってしまいます(特にコンテナの再ビルド)。

解決方法

上の問題の解決策の一つとして以下の方法があります。

解決策
1 . 起動中のコンテナに対してコンテナ内の古いjarファイルを新しいファイル(application-new.jar)で上書きする。

docker cp build/libs/application-new.jar application.jar

2 . コンテナ(コンテナ名:app)を再起動する。

docker restart app

この方法だとコンテナの再ビルドが必要ないため、より短時間でコンテナの更新ができます。

参考

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

Simh on docker

gcc on Dockerで、gccをビルドする手順を書きました。
その中で、実行環境を作成して動かすのは「Hello World」でした。
もう少し、実用的な?ものということでSimhのうち、IBM1401シミュレータで
FortranIVのプログラムを動かしてみます。

IBM1401 FortranIVについては、IBM1401 FortranIV Summaryを参考にします。

Simhの実行環境のDockerfileを用意します。

FROM kazu_gcc:9.1.0 as builder

RUN git clone https://github.com/simh/simh.git
RUN cd simh ; make i1401
RUN cd /src ; wget http://blog.livedoor.jp/suzanhud/IBM1401/FORTRAN/FortranIV.zip
RUN mkdir FortranIV ; cd FortranIV ; unzip ../FortranIV.zip

FROM alpine
COPY --from=builder /src/simh/BIN/i1401 /usr/local/bin/
RUN mkdir /simh
COPY --from=builder /src/FortranIV /simh

docker buildでDockerイメージを作成します。
イメージができたら動かします。
まず、システム起動用というかFortranコンパイラのテープイメージを作成します。
zipファイルに.batがあり、これを参考にします。
手順は以下の通り。
1)配布テープイメージからカードデッキを作成する。
2)カードデッキを用いて、コンパイラのテープイメージを作成する。

C:\kazu>docker run -it kazu_simh:i1401

/ # cd /simh/generation
/simh/generation # i1401 mkdeck.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
/simh/generation/mkdeck.ini-3> at stkr4 -n deck1.cd
STKR: creating new file: deck1.cd
/simh/generation/mkdeck.ini-4> at -n lpt mkdeck.lst
LPT: creating new file: mkdeck.lst

HALT instruction, IS: 429 (MCW 924 1000)

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-7> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-8> at stkr4 -n deck2.cd
STKR: creating new file: deck2.cd

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-10> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-11> at stkr4 -n deck3.cd
STKR: creating new file: deck3.cd

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-13> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-14> at stkr4 -n deck4.cd
STKR: creating new file: deck4.cd

HALT instruction, IS: 664 (B 525)

HALT instruction, IS: 772 (B 767)
Goodbye
/simh/generation # cat deck3.cd deck4.cd > system.cd
/simh/generation # i1401 mksystem.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/generation/mksystem.ini-3> at -n mt1 work.tap
MT: creating new file: work.tap
/simh/generation/mksystem.ini-5> at -n lpt mksystem.lst
LPT: creating new file: mksystem.lst

HALT instruction, IS: 7718 (B 7711)

HALT instruction, IS: 1956 (B 1300)
Tape Image 'work.tap' scanned as SIMH format.
contains 8809 bytes of tape data (12 records, 1 tapemarks)
A potentially unreasonable number of record sizes(9) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'work.tap' tape image
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
After processing 127627 bytes of tape data (56 records, 1 tapemarks)
Read Tape Record Returned Unexpected Status: invalid record length
63936 bytes of unexamined data remain in the tape image file
A potentially unreasonable number of record sizes(30) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image

HALT instruction, IS: 1956 (B 1300)
Goodbye
/simh/generation # rm work.tap

次にプログラムをコンパイルし、実行します。

/simh/generation # cp FortranIV_v2m2.tap ../running
/simh/generation # cd ../running
/simh/running # i1401 sample.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/running/sample.ini-4> at mt3 -n scratch.mt3
MT: creating new file: scratch.mt3
/simh/running/sample.ini-5> at mt4 -n scratch.mt4
MT: creating new file: scratch.mt4
/simh/running/sample.ini-6> at mt5 -n scratch.mt5
MT: creating new file: scratch.mt5
/simh/running/sample.ini-7> at mt6 -n scratch.mt6
MT: creating new file: scratch.mt6
/simh/running/sample.ini-9> at lpt -n sample.lst
LPT: creating new file: sample.lst

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 1956 (B 1300)
Tape Image 'scratch.mt4' scanned as SIMH format.
contains 195370 bytes of tape data (652 records, 1 tapemarks)
A potentially unreasonable number of record sizes(33) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'scratch.mt4' tape image
/simh/running/sample.ini-15> at mt4 -n system_new.tap
MT: creating new file: system_new.tap

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 3086 (B 3079)
Goodbye
/simh/running #

実行結果はsample.lstに出力されます。

/simh/running # cat sample.lst
                         FORTRAN   RUN                                                      000SAMPL
FORTRAN COMPILATION  VER 2 MOD 2
$NO MULTIPLY DIVIDE
     C     SAMPLE PROGRAM TO TEST SYSTEM
     C                                                                       003SAMPL
     C     PROGRAM FOR FINDING THE LARGEST VALUE                             004SAMPL
     C       ATTAINED BY A SET OF NUMBERS                                    005SAMPL
001        DIMENSION A(12)                                                   006SAMPL
002        READ (1,1)N,(A(I),I=1,N)                                          007SAMPL
003      1 FORMAT (I3/(12F6.2))                                              008SAMPL
004        BIGA=A(1)                                                         009SAMPL
005        DO 20 I=2,N                                                       010SAMPL
006        IF (BIGA.LT.A(I)) BIGA = A(I)                                     011SAMPL
007     20 CONTINUE                                                          012SAMPL
008        WRITE (3,2)N,BIGA                                                 013SAMPL
009      2 FORMAT (21H1THE LARGEST OF THESE,I3,11H NUMBERS IS,F9.2)          014SAMPL
010        STOP                                                              015SAMPL
011        END                                                               016SAMPL

NAME DICTIONARY
  00126 A       00131 N       00136 I       00149 BIGA

 SEQUENCE NUMBER DICTIONARY

   002-00151   004-00170   005-00182   006-00205   007-00249   000-00249   008-00253   010-00272

   011-00282   001-00292   002-00292   003-00355   008-00408   009-00423   011-00506

                         LOADER    RUN                                                      017SAMPL

$NAME MAP                                                               018SAMPL

$STORAGE PRINT                                                          019SAMPL

                                          *** NAME MAP ***

05851  ///      06210  )C       06673  )D       07036  )A       07223  )B       06850  ,9
07982  *1       06677  )F       06745  )G       08003  *3       08024  LINK     06797  )H
07673  G.       07752  P.       07511  Q.       07472  H.       07319  M.       06206  )E
06850  ,0       06902  (0       06914  (1       06966  (2       06986  (3       06946  (4
06926  (5       07249  Z)1      07128  Z)       07290  Y)       07183  U)       07224  V)
07645  G.3      07672  G.1      07609  G.2

                                       *** END OF NAME MAP ***



                                                 *** STORAGE PRINT ***

                                                                                              X1 O42     X2 P42     X3 /0U

05700 X0|     01??0?
              1111
05800 Y0|                                            1?A            2BL29I8S1M X0|0!5VZ9S B|45Y2W>Y4 Z|BO7XBP4V Y3WY5|Y3/X
                                                     1 1            11   1  11 1  11  1   1   1  1    11   1    1  1  1  1
05900 Z0|    0SZ5TB|45Y 4Z-$Y3WY3Y $Y3W5Z4Z|B |45$Y3WY3Y $Y3W>Y4Z|B Y8WBL2900L 2MX0|0J2TJ 0YB02MZ8/D ETB02MZ9/D ETBR75YC/(
               1  1   1   111  1   11  1   11    11  1   11  1   11    1   1   111  11  1   1   1  1   1   1  1   1   1  1
06000 !0|    B!2UBR75$Y 3WY3Y$YTW( BP9XBO7XBP 4VY3WY3ZY3 /X0S!5/B!0 UB?93BF7T0 01BG5S0010 03IBE1//BF 7T001BG5S0 12006F002B
             1   1   11   1  11  1 1   1   1    1  1  1   1  1  1    1   1   1   1   1  1   11   11    1  1   1   1  11  1
06100 J0|    D7SBE1/ BR 75YC/YUZ(B ?93BF7T001 BC1Z0211TH E LARGEST  OF THESEBG 5S001003IB C1Z011 NUM BERS ISBG5 S001009F00
                1   11    1  1  11    1   1   1   1  1                      1    1  1  11    1  1           1    1  1  11
06200 K0|    2BE1/ HO7T HO3W0?0HK7 WHO4T0!0HO 5|0|0MO7T0 99,0?10?4M 0?3K8TM0?6 K9|)0?10?4 H099000HK9 X000HL0U00 0H089000H0
              1   11    1      1    1      1       1       1      1       1       1       1      1       1       1      1
06300 L0|    94000MO7T0 99HO5U0?7? 0|0O7VYO7U 0|0BM6/0?0 (C0|00!0H0 99,0?0Q099 S0!0)0?1HM 5W0!0?0?0O 7WYO7U0!0Y O7U0?0BM2S
                  1       1      1       1       1        1      1    1   1    1   1   1       1       1      1       1
06400 M0|    0?00A0|00! 0SO7U0?0BL 8ZVM5|0?01 D0!0Q094D0 ?0Q099BL8Z H094000BN9 WD0!0Q099C 0?00|0Q094 ,0!1VN1|0? 12?0?1O7WY
                 1       1      1    1        1   1   1    1   1    1      1    1   1   1       1    1   1        1      1
06500 N0|    O7U0?1,N5U S0|00?1VN3 W0?1KAO7U0 !0BN1|A0|0 0?1YO7U0?1 BN8/NH0990 ?1H0940!1) 0!0,0!1BM8 U)N5UYO7W0 ?1)0!1VO5V
                   1    1      1        1       1   1       1       1    1       1      1    1   1    1   1       1   1
06600 O0|    O7VKVO6TO7 WK?0!0YO5/ O1SYO7V0|0 H099000H09 4000H08900 0B000VO1SO 7WK!O1SBO1 S   1  H09 4H0940!4HP 3W0?0M9560
                 1        1   1       1       1      1       1       1   1        1   1    1  1111    1      1       1
06700 P0|    99M0!5P1S? 000QBZM0!2 P2ZLQBZ000 H099000,P9 WBP5TH094) P9WM0!2Q0T M0J1M0!8Q0 XM0!2Q1UM0 J4Q2/HQ2Z0 J5B0J5NA00
               1      1       1       1       1      1    1   1   1    1       1   1       1      1       1       1   11
06800 Q0|    0000?000Q4 ZS000Q4ZV0 00Q4ZKB000                       BR0S0!40BR 1U0!41BR6W 0!42BR8W0! 43BR4W0!44 BR2W0!45.5
                 1       1      1        1    1                     1       1        1        1        1        1       1
06900 R0|    40B?0W222  B?1XB?1X22 2 B?0WB?0W 222 V?0W2G 9BB?1XB?0W 222 V?0W2G 9KB?1XB?1X 222 V?0W2G 9BB?1XB?1X 222 V?0W2G
               1        1   1        1   1        1        1   1        1        1   1        1        1   1        1
07000 ?0|    9KB?1XM0!7 094B0!0B0! 90!8|H/020 !4BU63SB9| M0!6?6UM0! 3?5X?000B9 XL000C0SBK 0W9601'B9X C0YAC0YB9| CB8XB9SBB6
               1   1       1   1        1       1   1    1      1       1       1      1        1       1       1      1
07100 A0|    XTBA2/0!7$ H0940!6B?4 |HA8TA8USC 1TB9|BA4|Z AC1VB8XBA4 |ZYB8WB9|? B8Y090YAYW B8YMB9|089 B000=0J008 9Y0!9088H0
               1        1      1    1      1       1     1      1     1      1       1       1       1   1       1      1
07200 B0|    940!7BB2U0 !4,H0940!4 B000M0!7B3 XM089000H/ 020!4BX47M 0!7B6S=089 000BB3YBY7 6SSEN!02.B 6X2SKB      15
                  1        1       1   1       1      1       1   1       1       1   1    1  1   1    1   1     1 1    1
07300 C0|             1 60009F   M 0!6K81,D2Y M089K84=K2 0K81AK81K8 4CL02K84BD 3WUH0940!7 AK81089=K8 1094BD0TL1 61M0!01T2B
                      1     1    1       1    1      1       1       1      1     1       1      1       1        1      1
07400 D0|    D1|M1T20!0 H0940!1H08 90|1BB18K2 4*BO00ML02 K84S089K84 MK84K81BQ1 8E06)D2YBC 6TH094=K20 K90CK03K90 BF4VSBB18T
                1       1      1       1        1   1       1       1      1    1  1   1    1   1       1       1    1
07500 E0|    MK96094BB1 8BE5U0!4/B E4SK99=VE5 |K751BQ18E 05,K75BO00 )K75H003E6 ZV003L17SB E9V0!4/MF7 SL24=F0YL2 4BF0ZHL240
             1      1    1       1        1        1   1   1   1    1   1       1       1        1       1       1   1
07600 F0|    !5BO59I9CH G5|000HG4X 000MG5|G4U MMMMHK9000 0BO59MG2WK 90MG2TK96P G2XG2/BB18    MG4UG5| MMMMMK90G2 WMK96M0!6K
               1   1  1       1       1       11111       1   1       1      1       1    1  1       11111       1   1
07700 G0|    90H0940!7H K96HF7SBB1 8                                 |BO00K99= M0J0L12,04 2045M0J304 7M0!9044HK 740?0HK710
               1      1    1   1    1  1  1      1     1      1      11        1      1       1       1      1       1
07800 H0|    !0VH2/L281 M0!6K62,L2 8M044094A0 89094=K200 94CL02094B I7VUHK680! 1HK650!0DK 17001YK980 01YK35I1ZH I7UC41BI2X
               1        1      1    1      1       1       1      1     1       1      1       1       1      1       1
07900 I0|    L12LBI4VL1 2IBI5W001  BQ06E03YK3 4I1ZHI7U00 3BI1SYK36I 1ZBI1SYK17 K98B003L12 ABI7UBQ06E 041INP
                 1        1        1   1  1       1       1   1       1   1       1        1   1   1   11
08000 00!       3LST                   H089M0 |203QM04J9 25B555
                11                     1   1       1       1   1

                                                    *** END OF STORAGE PRINT ***


$EXECUTION
THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO UPDATE SYSTEM WITH PHASE            023SAMPL
                                   UPDAT79F,INSERT                                          024SAMPL
                                   UPDAT///,INSER                                           113SAMPL
                                   NOTE DIAL TAPE UNIT 4 (WORK1) TO UNIT 1 (SYSTEM)         159SAMPL
                                   NOTE GET NEW SCRATCH TO TAPE UNIT 4 (WORK1)              160SAMPL
                                   PAUSE DIAL OUT AND SAVE MASTER SYSTEM TAPE, PUSH START   161SAMPL
                         PRODUCTIONRUN  ///                                                 162SAMPL

THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO DELETE PHASE FROM SYSTEM            165SAMPL
                         FORTRAN   UPDAT///,DELETE                                          166SAMPL
                                   HALT  SAMPLE PROGRAM COMPLETE, ALL SYSTEMS ARE GO.       167SAMPL
/simh/running #
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Simh/i1401 on docker

gcc on Dockerで、gccをビルドする手順を書きました。
その中で、実行環境を作成して動かすのは「Hello World」でした。
もう少し、実用的な?ものということでSimhのうち、IBM1401シミュレータで
FortranIVのプログラムを動かしてみます。

IBM1401 FortranIVについては、IBM1401 FortranIV Summaryを参考にしてください(実は手前味噌)。

Simhの実行環境のDockerfileを用意します。

Dockerfile
FROM kazu_gcc:9.1.0 as builder

RUN git clone https://github.com/simh/simh.git
RUN cd simh ; make i1401
RUN cd /src ; wget http://blog.livedoor.jp/suzanhud/IBM1401/FORTRAN/FortranIV.zip
RUN mkdir FortranIV ; cd FortranIV ; unzip ../FortranIV.zip

FROM alpine
COPY --from=builder /src/simh/BIN/i1401 /usr/local/bin/
RUN mkdir /simh
COPY --from=builder /src/FortranIV /simh

docker buildでDockerイメージを作成します。
イメージができたら動かします。
まず、システム起動用というかFortranコンパイラのテープイメージを作成します。
zipファイルに.batがあり、これを参考にします。
手順は以下の通り。
1)配布テープイメージからカードデッキを作成する。
2)カードデッキを用いて、コンパイラのテープイメージを作成する。

C:\kazu>docker run -it kazu_simh:i1401

/ # cd /simh/generation
/simh/generation # i1401 mkdeck.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
/simh/generation/mkdeck.ini-3> at stkr4 -n deck1.cd
STKR: creating new file: deck1.cd
/simh/generation/mkdeck.ini-4> at -n lpt mkdeck.lst
LPT: creating new file: mkdeck.lst

HALT instruction, IS: 429 (MCW 924 1000)

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-7> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-8> at stkr4 -n deck2.cd
STKR: creating new file: deck2.cd

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-10> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-11> at stkr4 -n deck3.cd
STKR: creating new file: deck3.cd

HALT instruction, IS: 664 (B 525)
/simh/generation/mkdeck.ini-13> c

Unit not attached, IS: 619 (P)
/simh/generation/mkdeck.ini-14> at stkr4 -n deck4.cd
STKR: creating new file: deck4.cd

HALT instruction, IS: 664 (B 525)

HALT instruction, IS: 772 (B 767)
Goodbye
/simh/generation # cat deck3.cd deck4.cd > system.cd
/simh/generation # i1401 mksystem.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/generation/mksystem.ini-3> at -n mt1 work.tap
MT: creating new file: work.tap
/simh/generation/mksystem.ini-5> at -n lpt mksystem.lst
LPT: creating new file: mksystem.lst

HALT instruction, IS: 7718 (B 7711)

HALT instruction, IS: 1956 (B 1300)
Tape Image 'work.tap' scanned as SIMH format.
contains 8809 bytes of tape data (12 records, 1 tapemarks)
A potentially unreasonable number of record sizes(9) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'work.tap' tape image
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
After processing 127627 bytes of tape data (56 records, 1 tapemarks)
Read Tape Record Returned Unexpected Status: invalid record length
63936 bytes of unexamined data remain in the tape image file
A potentially unreasonable number of record sizes(30) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image

HALT instruction, IS: 1956 (B 1300)
Goodbye
/simh/generation # rm work.tap

次にプログラムをコンパイルし、実行します。

/simh/generation # cp FortranIV_v2m2.tap ../running
/simh/generation # cd ../running
/simh/running # i1401 sample.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 571c8f96
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/running/sample.ini-4> at mt3 -n scratch.mt3
MT: creating new file: scratch.mt3
/simh/running/sample.ini-5> at mt4 -n scratch.mt4
MT: creating new file: scratch.mt4
/simh/running/sample.ini-6> at mt5 -n scratch.mt5
MT: creating new file: scratch.mt5
/simh/running/sample.ini-7> at mt6 -n scratch.mt6
MT: creating new file: scratch.mt6
/simh/running/sample.ini-9> at lpt -n sample.lst
LPT: creating new file: sample.lst

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 1956 (B 1300)
Tape Image 'scratch.mt4' scanned as SIMH format.
contains 195370 bytes of tape data (652 records, 1 tapemarks)
A potentially unreasonable number of record sizes(33) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'scratch.mt4' tape image
/simh/running/sample.ini-15> at mt4 -n system_new.tap
MT: creating new file: system_new.tap

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 3086 (B 3079)
Goodbye
/simh/running #

実行結果はsample.lstに出力されます。

/simh/running # cat sample.lst
                         FORTRAN   RUN                                                      000SAMPL
FORTRAN COMPILATION  VER 2 MOD 2
$NO MULTIPLY DIVIDE
     C     SAMPLE PROGRAM TO TEST SYSTEM
     C                                                                       003SAMPL
     C     PROGRAM FOR FINDING THE LARGEST VALUE                             004SAMPL
     C       ATTAINED BY A SET OF NUMBERS                                    005SAMPL
001        DIMENSION A(12)                                                   006SAMPL
002        READ (1,1)N,(A(I),I=1,N)                                          007SAMPL
003      1 FORMAT (I3/(12F6.2))                                              008SAMPL
004        BIGA=A(1)                                                         009SAMPL
005        DO 20 I=2,N                                                       010SAMPL
006        IF (BIGA.LT.A(I)) BIGA = A(I)                                     011SAMPL
007     20 CONTINUE                                                          012SAMPL
008        WRITE (3,2)N,BIGA                                                 013SAMPL
009      2 FORMAT (21H1THE LARGEST OF THESE,I3,11H NUMBERS IS,F9.2)          014SAMPL
010        STOP                                                              015SAMPL
011        END                                                               016SAMPL

NAME DICTIONARY
  00126 A       00131 N       00136 I       00149 BIGA

 SEQUENCE NUMBER DICTIONARY

   002-00151   004-00170   005-00182   006-00205   007-00249   000-00249   008-00253   010-00272

   011-00282   001-00292   002-00292   003-00355   008-00408   009-00423   011-00506

                         LOADER    RUN                                                      017SAMPL

$NAME MAP                                                               018SAMPL

$STORAGE PRINT                                                          019SAMPL

                                          *** NAME MAP ***

05851  ///      06210  )C       06673  )D       07036  )A       07223  )B       06850  ,9
07982  *1       06677  )F       06745  )G       08003  *3       08024  LINK     06797  )H
07673  G.       07752  P.       07511  Q.       07472  H.       07319  M.       06206  )E
06850  ,0       06902  (0       06914  (1       06966  (2       06986  (3       06946  (4
06926  (5       07249  Z)1      07128  Z)       07290  Y)       07183  U)       07224  V)
07645  G.3      07672  G.1      07609  G.2

                                       *** END OF NAME MAP ***



                                                 *** STORAGE PRINT ***

                                                                                              X1 O42     X2 P42     X3 /0U

05700 X0|     01??0?
              1111
05800 Y0|                                            1?A            2BL29I8S1M X0|0!5VZ9S B|45Y2W>Y4 Z|BO7XBP4V Y3WY5|Y3/X
                                                     1 1            11   1  11 1  11  1   1   1  1    11   1    1  1  1  1
05900 Z0|    0SZ5TB|45Y 4Z-$Y3WY3Y $Y3W5Z4Z|B |45$Y3WY3Y $Y3W>Y4Z|B Y8WBL2900L 2MX0|0J2TJ 0YB02MZ8/D ETB02MZ9/D ETBR75YC/(
               1  1   1   111  1   11  1   11    11  1   11  1   11    1   1   111  11  1   1   1  1   1   1  1   1   1  1
06000 !0|    B!2UBR75$Y 3WY3Y$YTW( BP9XBO7XBP 4VY3WY3ZY3 /X0S!5/B!0 UB?93BF7T0 01BG5S0010 03IBE1//BF 7T001BG5S0 12006F002B
             1   1   11   1  11  1 1   1   1    1  1  1   1  1  1    1   1   1   1   1  1   11   11    1  1   1   1  11  1
06100 J0|    D7SBE1/ BR 75YC/YUZ(B ?93BF7T001 BC1Z0211TH E LARGEST  OF THESEBG 5S001003IB C1Z011 NUM BERS ISBG5 S001009F00
                1   11    1  1  11    1   1   1   1  1                      1    1  1  11    1  1           1    1  1  11
06200 K0|    2BE1/ HO7T HO3W0?0HK7 WHO4T0!0HO 5|0|0MO7T0 99,0?10?4M 0?3K8TM0?6 K9|)0?10?4 H099000HK9 X000HL0U00 0H089000H0
              1   11    1      1    1      1       1       1      1       1       1       1      1       1       1      1
06300 L0|    94000MO7T0 99HO5U0?7? 0|0O7VYO7U 0|0BM6/0?0 (C0|00!0H0 99,0?0Q099 S0!0)0?1HM 5W0!0?0?0O 7WYO7U0!0Y O7U0?0BM2S
                  1       1      1       1       1        1      1    1   1    1   1   1       1       1      1       1
06400 M0|    0?00A0|00! 0SO7U0?0BL 8ZVM5|0?01 D0!0Q094D0 ?0Q099BL8Z H094000BN9 WD0!0Q099C 0?00|0Q094 ,0!1VN1|0? 12?0?1O7WY
                 1       1      1    1        1   1   1    1   1    1      1    1   1   1       1    1   1        1      1
06500 N0|    O7U0?1,N5U S0|00?1VN3 W0?1KAO7U0 !0BN1|A0|0 0?1YO7U0?1 BN8/NH0990 ?1H0940!1) 0!0,0!1BM8 U)N5UYO7W0 ?1)0!1VO5V
                   1    1      1        1       1   1       1       1    1       1      1    1   1    1   1       1   1
06600 O0|    O7VKVO6TO7 WK?0!0YO5/ O1SYO7V0|0 H099000H09 4000H08900 0B000VO1SO 7WK!O1SBO1 S   1  H09 4H0940!4HP 3W0?0M9560
                 1        1   1       1       1      1       1       1   1        1   1    1  1111    1      1       1
06700 P0|    99M0!5P1S? 000QBZM0!2 P2ZLQBZ000 H099000,P9 WBP5TH094) P9WM0!2Q0T M0J1M0!8Q0 XM0!2Q1UM0 J4Q2/HQ2Z0 J5B0J5NA00
               1      1       1       1       1      1    1   1   1    1       1   1       1      1       1       1   11
06800 Q0|    0000?000Q4 ZS000Q4ZV0 00Q4ZKB000                       BR0S0!40BR 1U0!41BR6W 0!42BR8W0! 43BR4W0!44 BR2W0!45.5
                 1       1      1        1    1                     1       1        1        1        1        1       1
06900 R0|    40B?0W222  B?1XB?1X22 2 B?0WB?0W 222 V?0W2G 9BB?1XB?0W 222 V?0W2G 9KB?1XB?1X 222 V?0W2G 9BB?1XB?1X 222 V?0W2G
               1        1   1        1   1        1        1   1        1        1   1        1        1   1        1
07000 ?0|    9KB?1XM0!7 094B0!0B0! 90!8|H/020 !4BU63SB9| M0!6?6UM0! 3?5X?000B9 XL000C0SBK 0W9601'B9X C0YAC0YB9| CB8XB9SBB6
               1   1       1   1        1       1   1    1      1       1       1      1        1       1       1      1
07100 A0|    XTBA2/0!7$ H0940!6B?4 |HA8TA8USC 1TB9|BA4|Z AC1VB8XBA4 |ZYB8WB9|? B8Y090YAYW B8YMB9|089 B000=0J008 9Y0!9088H0
               1        1      1    1      1       1     1      1     1      1       1       1       1   1       1      1
07200 B0|    940!7BB2U0 !4,H0940!4 B000M0!7B3 XM089000H/ 020!4BX47M 0!7B6S=089 000BB3YBY7 6SSEN!02.B 6X2SKB      15
                  1        1       1   1       1      1       1   1       1       1   1    1  1   1    1   1     1 1    1
07300 C0|             1 60009F   M 0!6K81,D2Y M089K84=K2 0K81AK81K8 4CL02K84BD 3WUH0940!7 AK81089=K8 1094BD0TL1 61M0!01T2B
                      1     1    1       1    1      1       1       1      1     1       1      1       1        1      1
07400 D0|    D1|M1T20!0 H0940!1H08 90|1BB18K2 4*BO00ML02 K84S089K84 MK84K81BQ1 8E06)D2YBC 6TH094=K20 K90CK03K90 BF4VSBB18T
                1       1      1       1        1   1       1       1      1    1  1   1    1   1       1       1    1
07500 E0|    MK96094BB1 8BE5U0!4/B E4SK99=VE5 |K751BQ18E 05,K75BO00 )K75H003E6 ZV003L17SB E9V0!4/MF7 SL24=F0YL2 4BF0ZHL240
             1      1    1       1        1        1   1   1   1    1   1       1       1        1       1       1   1
07600 F0|    !5BO59I9CH G5|000HG4X 000MG5|G4U MMMMHK9000 0BO59MG2WK 90MG2TK96P G2XG2/BB18    MG4UG5| MMMMMK90G2 WMK96M0!6K
               1   1  1       1       1       11111       1   1       1      1       1    1  1       11111       1   1
07700 G0|    90H0940!7H K96HF7SBB1 8                                 |BO00K99= M0J0L12,04 2045M0J304 7M0!9044HK 740?0HK710
               1      1    1   1    1  1  1      1     1      1      11        1      1       1       1      1       1
07800 H0|    !0VH2/L281 M0!6K62,L2 8M044094A0 89094=K200 94CL02094B I7VUHK680! 1HK650!0DK 17001YK980 01YK35I1ZH I7UC41BI2X
               1        1      1    1      1       1       1      1     1       1      1       1       1      1       1
07900 I0|    L12LBI4VL1 2IBI5W001  BQ06E03YK3 4I1ZHI7U00 3BI1SYK36I 1ZBI1SYK17 K98B003L12 ABI7UBQ06E 041INP
                 1        1        1   1  1       1       1   1       1   1       1        1   1   1   11
08000 00!       3LST                   H089M0 |203QM04J9 25B555
                11                     1   1       1       1   1

                                                    *** END OF STORAGE PRINT ***


$EXECUTION
THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO UPDATE SYSTEM WITH PHASE            023SAMPL
                                   UPDAT79F,INSERT                                          024SAMPL
                                   UPDAT///,INSER                                           113SAMPL
                                   NOTE DIAL TAPE UNIT 4 (WORK1) TO UNIT 1 (SYSTEM)         159SAMPL
                                   NOTE GET NEW SCRATCH TO TAPE UNIT 4 (WORK1)              160SAMPL
                                   PAUSE DIAL OUT AND SAVE MASTER SYSTEM TAPE, PUSH START   161SAMPL
                         PRODUCTIONRUN  ///                                                 162SAMPL

THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO DELETE PHASE FROM SYSTEM            165SAMPL
                         FORTRAN   UPDAT///,DELETE                                          166SAMPL
                                   HALT  SAMPLE PROGRAM COMPLETE, ALL SYSTEMS ARE GO.       167SAMPL
/simh/running #
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Simh/i1401 with FortranIV on docker

gcc on Dockerで、gccをビルドする手順を書きました。
その中で、実行環境を作成して動かすのは「Hello World」でした。
もう少し、実用的な?ものということでSimhのうち、IBM1401シミュレータでFortranIVのプログラムを動かしてみます。

IBM1401 FortranIVについては、IBM1401 FortranIV Summaryを参考にしてください(実は手前味噌)。

Simhの実行環境のDockerfileを用意します。

Dockerfile
FROM kazu_gcc:9.1.0 as builder

RUN git clone https://github.com/simh/simh.git
RUN cd simh ; make i1401
RUN cd /src ; wget http://blog.livedoor.jp/suzanhud/IBM1401/FORTRAN/FortranIV.tar.xz
RUN apk add xz
RUN tar xvf FortranIV.tar.xz

FROM alpine_jp:3.10
COPY --from=builder /src/simh/BIN/i1401 /usr/local/bin/
RUN mkdir -p /simh/FortranIV
COPY --from=builder /src/FortranIV /simh/FortranIV

docker buildでDockerイメージを作成します。

C:\kazu_simh>docker build -t kazu_simh:i1401 --no-cache=true .
Sending build context to Docker daemon  1.307MB
Step 1/10 : FROM kazu_gcc:9.1.0 as builder
 ---> 59c0a716b42a
Step 2/10 : RUN git clone https://github.com/simh/simh.git
 ---> Running in 79a2c484a386
Cloning into 'simh'...
Removing intermediate container 79a2c484a386
 ---> e7933c4c7f15
Step 3/10 : RUN cd simh ; make i1401
 ---> Running in ab08594ee729
Illegal option -p
lib paths are:
include paths are:  /usr/bin/../lib/gcc/x86_64-alpine-linux-musl/9.1.0/include /usr/include
using regex: /usr/include/regex.h
using semaphore: /usr/include/semaphore.h
using mman: /usr/include/sys/mman.h
***
*** i1401 Simulator being built with:
*** - compiler optimizations and no debugging support. GCC Version: 9.1.0.
*** - Per simulator tests will be run.
***
*** git commit id is 9e7198224955f9f89a53e33fbdab8c84192a1154.
*** git commit time is 2019-07-02T09:53:56-0700.
***
mkdir -p BIN/buildtools
gcc -std=gnu99 -U__STRICT_ANSI__  -O2 -finline-functions -fgcse-after-reload -fpredictive-commoning -fipa-cp-clone -fno-unsafe-loop-optimizations -fno-strict-overflow -Wno-unused-result -Wno-format-truncation -DSIM_GIT_COMMIT_ID=9e7198224955f9f89a53e33fbdab8c84192a1154 -DSIM_GIT_COMMIT_TIME=2019-07-02T09:53:56-0700  -DSIM_COMPILER="GCC Version: 9.1.0" -I . -D_GNU_SOURCE -DHAVE_REGEX_H -DHAVE_SEMAPHORE -DHAVE_SYS_IOCTL -DHAVE_UTIME -DHAVE_GLOB -DHAVE_SHM_OPEN  I1401/i1401_lp.c I1401/i1401_cpu.c I1401/i1401_iq.c I1401/i1401_cd.c I1401/i1401_mt.c I1401/i1401_dp.c I1401/i1401_sys.c scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c sim_tmxr.c sim_ether.c sim_tape.c sim_disk.c sim_serial.c sim_video.c sim_imd.c sim_card.c -I I1401 -o BIN/i1401
Removing intermediate container ab08594ee729
 ---> c29faf0aa623
Step 4/10 : RUN cd /src ; wget http://blog.livedoor.jp/suzanhud/IBM1401/FORTRAN/FortranIV.tar.xz
 ---> Running in 9728e53af481
Connecting to blog.livedoor.jp (203.104.130.159:80)
FortranIV.tar.xz     100% |********************************| 1274k  0:00:00 ETA
Removing intermediate container 9728e53af481
 ---> 9327d4cb18ce
Step 5/10 : RUN apk add xz
 ---> Running in a1cd180e7821
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
(1/1) Installing xz (5.2.4-r0)
Executing busybox-1.30.1-r2.trigger
OK: 133 MiB in 58 packages
Removing intermediate container a1cd180e7821
 ---> 4e6c9ec049b5
Step 6/10 : RUN tar xvf FortranIV.tar.xz
 ---> Running in 6079708cf810
FortranIV/
FortranIV/generation/
FortranIV/generation/adc00233.tap
FortranIV/generation/deck3.cd
FortranIV/generation/mksystem.ini
FortranIV/generation/system.cd
FortranIV/generation/FortranIV_v2m2.tap
FortranIV/generation/deck2.cd
FortranIV/generation/mksystem.sh
FortranIV/generation/mksystem.bat
FortranIV/generation/mkdeck.lst
FortranIV/generation/deck1.cd
FortranIV/generation/mkdeck.ini
FortranIV/generation/mksystem.lst
FortranIV/generation/deck4.cd
FortranIV/running/
FortranIV/running/sample.bat
FortranIV/running/sample.sh
FortranIV/running/FortranIV_v2m2.tap
FortranIV/running/sample.ini
FortranIV/running/sample.lst
FortranIV/running/deck1.cd
FortranIV/running/system_new.tap
Removing intermediate container 6079708cf810
 ---> 7003283837ef
Step 7/10 : FROM alpine_jp:3.10
 ---> 05c440521241
Step 8/10 : COPY --from=builder /src/simh/BIN/i1401 /usr/local/bin/
 ---> 23f978bc6edc
Step 9/10 : RUN mkdir -p /simh/FortranIV
 ---> Running in 3641f4ea1f45
Removing intermediate container 3641f4ea1f45
 ---> c94ad6d68b0a
Step 10/10 : COPY --from=builder /src/FortranIV /simh/FortranIV
 ---> a66089631f29
Successfully built a66089631f29
Successfully tagged kazu_simh:i1401
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

C:\kazu_simh>

イメージができたら動かします。
まず、システム起動用というかFortranコンパイラのテープイメージを作成します。
generation配下にmksystem.shがあり、これを使用します。
行うことは以下の通り。
1)配布テープイメージからカードデッキを作成する。
2)カードデッキを用いて、コンパイラのテープイメージを作成する。

C:\kazu>docker run -it kazu_simh:i1401
/ # cd /simh/FortranIV/generation
/simh/FortranIV/generation # sh mksystem.sh

IBM 1401 simulator V4.0-0 Current        git commit id: 9e719822
/simh/FortranIV/generation/mkdeck.ini-3> at stkr4 -n deck1.cd
STKR: creating new file: deck1.cd
/simh/FortranIV/generation/mkdeck.ini-4> at -n lpt mkdeck.lst
LPT: creating new file: mkdeck.lst

HALT instruction, IS: 429 (MCW 924 1000)

HALT instruction, IS: 664 (B 525)
/simh/FortranIV/generation/mkdeck.ini-7> c

Unit not attached, IS: 619 (P)
/simh/FortranIV/generation/mkdeck.ini-8> at stkr4 -n deck2.cd
STKR: creating new file: deck2.cd

HALT instruction, IS: 664 (B 525)
/simh/FortranIV/generation/mkdeck.ini-10> c

Unit not attached, IS: 619 (P)
/simh/FortranIV/generation/mkdeck.ini-11> at stkr4 -n deck3.cd
STKR: creating new file: deck3.cd

HALT instruction, IS: 664 (B 525)
/simh/FortranIV/generation/mkdeck.ini-13> c

Unit not attached, IS: 619 (P)
/simh/FortranIV/generation/mkdeck.ini-14> at stkr4 -n deck4.cd
STKR: creating new file: deck4.cd

HALT instruction, IS: 664 (B 525)

HALT instruction, IS: 772 (B 767)
Goodbye

IBM 1401 simulator V4.0-0 Current        git commit id: 9e719822
/simh/FortranIV/generation/mksystem.ini-2> at mt4 FortranIV_v2m2.tap
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/FortranIV/generation/mksystem.ini-3> at -n mt1 work.tap
MT: creating new file: work.tap
/simh/FortranIV/generation/mksystem.ini-5> at -n lpt mksystem.lst
LPT: creating new file: mksystem.lst

HALT instruction, IS: 7718 (B 7711)

HALT instruction, IS: 1956 (B 1300)
/simh/FortranIV/generation/mksystem.ini-10> at mt4 work.tap
Tape Image 'work.tap' scanned as SIMH format.
contains 8809 bytes of tape data (12 records, 1 tapemarks)
A potentially unreasonable number of record sizes(9) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'work.tap' tape image
/simh/FortranIV/generation/mksystem.ini-11> at mt1 FortranIV_v2m2.tap
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
After processing 127627 bytes of tape data (56 records, 1 tapemarks)
Read Tape Record Returned Unexpected Status: invalid record length
63936 bytes of unexamined data remain in the tape image file
A potentially unreasonable number of record sizes(30) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image

HALT instruction, IS: 1956 (B 1300)
Goodbye
/simh/FortranIV/generation #

次にプログラムをコンパイルし、実行します。

/simh/FortranIV/generation # cp FortranIV_v2m2.tap ../running
/simh/FortranIV/generation # cd ../running
/simh/FortranIV/running # i1401 sample.ini

IBM 1401 simulator V4.0-0 Current        git commit id: 9e719822
/simh/FortranIV/running/sample.ini-3> at mt1 FortranIV_v2m2.tap
Tape Image 'FortranIV_v2m2.tap' scanned as SIMH format.
contains 186827 bytes of tape data (648 records, 1 tapemarks)
A potentially unreasonable number of record sizes(31) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'FortranIV_v2m2.tap' tape image
/simh/FortranIV/running/sample.ini-4> at mt3 -n scratch.mt3
MT: creating new file: scratch.mt3
/simh/FortranIV/running/sample.ini-5> at mt4 -n scratch.mt4
MT: creating new file: scratch.mt4
/simh/FortranIV/running/sample.ini-6> at mt5 -n scratch.mt5
MT: creating new file: scratch.mt5
/simh/FortranIV/running/sample.ini-7> at mt6 -n scratch.mt6
MT: creating new file: scratch.mt6
/simh/FortranIV/running/sample.ini-9> at lpt -n sample.lst
LPT: creating new file: sample.lst

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 1956 (B 1300)
/simh/FortranIV/running/sample.ini-14> at mt1 scratch.mt4
Tape Image 'scratch.mt4' scanned as SIMH format.
contains 195370 bytes of tape data (652 records, 1 tapemarks)
A potentially unreasonable number of record sizes(33) vs tape marks (1) have been found
The tape format (SIMH) might not be correct for the 'scratch.mt4' tape image
/simh/FortranIV/running/sample.ini-15> at mt4 -n system_new.tap
MT: creating new file: system_new.tap

HALT instruction, IS: 1956 (B 1300)

HALT instruction, IS: 3086 (B 3079)
Goodbye
/simh/FortranIV/running #

実行結果はsample.lstに出力されます。

/simh/FortranIV/running # cat sample.lst
                         FORTRAN   RUN                                                      000SAMPL
FORTRAN COMPILATION  VER 2 MOD 2
$NO MULTIPLY DIVIDE
     C     SAMPLE PROGRAM TO TEST SYSTEM
     C                                                                       003SAMPL
     C     PROGRAM FOR FINDING THE LARGEST VALUE                             004SAMPL
     C       ATTAINED BY A SET OF NUMBERS                                    005SAMPL
001        DIMENSION A(12)                                                   006SAMPL
002        READ (1,1)N,(A(I),I=1,N)                                          007SAMPL
003      1 FORMAT (I3/(12F6.2))                                              008SAMPL
004        BIGA=A(1)                                                         009SAMPL
005        DO 20 I=2,N                                                       010SAMPL
006        IF (BIGA.LT.A(I)) BIGA = A(I)                                     011SAMPL
007     20 CONTINUE                                                          012SAMPL
008        WRITE (3,2)N,BIGA                                                 013SAMPL
009      2 FORMAT (21H1THE LARGEST OF THESE,I3,11H NUMBERS IS,F9.2)          014SAMPL
010        STOP                                                              015SAMPL
011        END                                                               016SAMPL

NAME DICTIONARY
  00126 A       00131 N       00136 I       00149 BIGA

 SEQUENCE NUMBER DICTIONARY

   002-00151   004-00170   005-00182   006-00205   007-00249   000-00249   008-00253   010-00272

   011-00282   001-00292   002-00292   003-00355   008-00408   009-00423   011-00506

                         LOADER    RUN                                                      017SAMPL

$NAME MAP                                                               018SAMPL

$STORAGE PRINT                                                          019SAMPL

                                          *** NAME MAP ***

05851  ///      06210  )C       06673  )D       07036  )A       07223  )B       06850  ,9
07982  *1       06677  )F       06745  )G       08003  *3       08024  LINK     06797  )H
07673  G.       07752  P.       07511  Q.       07472  H.       07319  M.       06206  )E
06850  ,0       06902  (0       06914  (1       06966  (2       06986  (3       06946  (4
06926  (5       07249  Z)1      07128  Z)       07290  Y)       07183  U)       07224  V)
07645  G.3      07672  G.1      07609  G.2

                                       *** END OF NAME MAP ***



                                                 *** STORAGE PRINT ***

                                                                                              X1 O42     X2 P42     X3 /0U

05700 X0|     01??0?
              1111
05800 Y0|                                            1?A            2BL29I8S1M X0|0!5VZ9S B|45Y2W>Y4 Z|BO7XBP4V Y3WY5|Y3/X
                                                     1 1            11   1  11 1  11  1   1   1  1    11   1    1  1  1  1
05900 Z0|    0SZ5TB|45Y 4Z-$Y3WY3Y $Y3W5Z4Z|B |45$Y3WY3Y $Y3W>Y4Z|B Y8WBL2900L 2MX0|0J2TJ 0YB02MZ8/D ETB02MZ9/D ETBR75YC/(
               1  1   1   111  1   11  1   11    11  1   11  1   11    1   1   111  11  1   1   1  1   1   1  1   1   1  1
06000 !0|    B!2UBR75$Y 3WY3Y$YTW( BP9XBO7XBP 4VY3WY3ZY3 /X0S!5/B!0 UB?93BF7T0 01BG5S0010 03IBE1//BF 7T001BG5S0 12006F002B
             1   1   11   1  11  1 1   1   1    1  1  1   1  1  1    1   1   1   1   1  1   11   11    1  1   1   1  11  1
06100 J0|    D7SBE1/ BR 75YC/YUZ(B ?93BF7T001 BC1Z0211TH E LARGEST  OF THESEBG 5S001003IB C1Z011 NUM BERS ISBG5 S001009F00
                1   11    1  1  11    1   1   1   1  1                      1    1  1  11    1  1           1    1  1  11
06200 K0|    2BE1/ HO7T HO3W0?0HK7 WHO4T0!0HO 5|0|0MO7T0 99,0?10?4M 0?3K8TM0?6 K9|)0?10?4 H099000HK9 X000HL0U00 0H089000H0
              1   11    1      1    1      1       1       1      1       1       1       1      1       1       1      1
06300 L0|    94000MO7T0 99HO5U0?7? 0|0O7VYO7U 0|0BM6/0?0 (C0|00!0H0 99,0?0Q099 S0!0)0?1HM 5W0!0?0?0O 7WYO7U0!0Y O7U0?0BM2S
                  1       1      1       1       1        1      1    1   1    1   1   1       1       1      1       1
06400 M0|    0?00A0|00! 0SO7U0?0BL 8ZVM5|0?01 D0!0Q094D0 ?0Q099BL8Z H094000BN9 WD0!0Q099C 0?00|0Q094 ,0!1VN1|0? 12?0?1O7WY
                 1       1      1    1        1   1   1    1   1    1      1    1   1   1       1    1   1        1      1
06500 N0|    O7U0?1,N5U S0|00?1VN3 W0?1KAO7U0 !0BN1|A0|0 0?1YO7U0?1 BN8/NH0990 ?1H0940!1) 0!0,0!1BM8 U)N5UYO7W0 ?1)0!1VO5V
                   1    1      1        1       1   1       1       1    1       1      1    1   1    1   1       1   1
06600 O0|    O7VKVO6TO7 WK?0!0YO5/ O1SYO7V0|0 H099000H09 4000H08900 0B000VO1SO 7WK!O1SBO1 S   1  H09 4H0940!4HP 3W0?0M9560
                 1        1   1       1       1      1       1       1   1        1   1    1  1111    1      1       1
06700 P0|    99M0!5P1S? 000QBZM0!2 P2ZLQBZ000 H099000,P9 WBP5TH094) P9WM0!2Q0T M0J1M0!8Q0 XM0!2Q1UM0 J4Q2/HQ2Z0 J5B0J5NA00
               1      1       1       1       1      1    1   1   1    1       1   1       1      1       1       1   11
06800 Q0|    0000?000Q4 ZS000Q4ZV0 00Q4ZKB000                       BR0S0!40BR 1U0!41BR6W 0!42BR8W0! 43BR4W0!44 BR2W0!45.5
                 1       1      1        1    1                     1       1        1        1        1        1       1
06900 R0|    40B?0W222  B?1XB?1X22 2 B?0WB?0W 222 V?0W2G 9BB?1XB?0W 222 V?0W2G 9KB?1XB?1X 222 V?0W2G 9BB?1XB?1X 222 V?0W2G
               1        1   1        1   1        1        1   1        1        1   1        1        1   1        1
07000 ?0|    9KB?1XM0!7 094B0!0B0! 90!8|H/020 !4BU63SB9| M0!6?6UM0! 3?5X?000B9 XL000C0SBK 0W9601'B9X C0YAC0YB9| CB8XB9SBB6
               1   1       1   1        1       1   1    1      1       1       1      1        1       1       1      1
07100 A0|    XTBA2/0!7$ H0940!6B?4 |HA8TA8USC 1TB9|BA4|Z AC1VB8XBA4 |ZYB8WB9|? B8Y090YAYW B8YMB9|089 B000=0J008 9Y0!9088H0
               1        1      1    1      1       1     1      1     1      1       1       1       1   1       1      1
07200 B0|    940!7BB2U0 !4,H0940!4 B000M0!7B3 XM089000H/ 020!4BX47M 0!7B6S=089 000BB3YBY7 6SSEN!02.B 6X2SKB      15
                  1        1       1   1       1      1       1   1       1       1   1    1  1   1    1   1     1 1    1
07300 C0|             1 60009F   M 0!6K81,D2Y M089K84=K2 0K81AK81K8 4CL02K84BD 3WUH0940!7 AK81089=K8 1094BD0TL1 61M0!01T2B
                      1     1    1       1    1      1       1       1      1     1       1      1       1        1      1
07400 D0|    D1|M1T20!0 H0940!1H08 90|1BB18K2 4*BO00ML02 K84S089K84 MK84K81BQ1 8E06)D2YBC 6TH094=K20 K90CK03K90 BF4VSBB18T
                1       1      1       1        1   1       1       1      1    1  1   1    1   1       1       1    1
07500 E0|    MK96094BB1 8BE5U0!4/B E4SK99=VE5 |K751BQ18E 05,K75BO00 )K75H003E6 ZV003L17SB E9V0!4/MF7 SL24=F0YL2 4BF0ZHL240
             1      1    1       1        1        1   1   1   1    1   1       1       1        1       1       1   1
07600 F0|    !5BO59I9CH G5|000HG4X 000MG5|G4U MMMMHK9000 0BO59MG2WK 90MG2TK96P G2XG2/BB18    MG4UG5| MMMMMK90G2 WMK96M0!6K
               1   1  1       1       1       11111       1   1       1      1       1    1  1       11111       1   1
07700 G0|    90H0940!7H K96HF7SBB1 8                                 |BO00K99= M0J0L12,04 2045M0J304 7M0!9044HK 740?0HK710
               1      1    1   1    1  1  1      1     1      1      11        1      1       1       1      1       1
07800 H0|    !0VH2/L281 M0!6K62,L2 8M044094A0 89094=K200 94CL02094B I7VUHK680! 1HK650!0DK 17001YK980 01YK35I1ZH I7UC41BI2X
               1        1      1    1      1       1       1      1     1       1      1       1       1      1       1
07900 I0|    L12LBI4VL1 2IBI5W001  BQ06E03YK3 4I1ZHI7U00 3BI1SYK36I 1ZBI1SYK17 K98B003L12 ABI7UBQ06E 041INP
                 1        1        1   1  1       1       1   1       1   1       1        1   1   1   11
08000 00!       3LST                   H089M0 |203QM04J9 25B555
                11                     1   1       1       1   1

                                                    *** END OF STORAGE PRINT ***


$EXECUTION
THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO UPDATE SYSTEM WITH PHASE            023SAMPL
                                   UPDAT79F,INSERT                                          024SAMPL
                                   UPDAT///,INSER                                           113SAMPL
                                   NOTE DIAL TAPE UNIT 4 (WORK1) TO UNIT 1 (SYSTEM)         159SAMPL
                                   NOTE GET NEW SCRATCH TO TAPE UNIT 4 (WORK1)              160SAMPL
                                   PAUSE DIAL OUT AND SAVE MASTER SYSTEM TAPE, PUSH START   161SAMPL
                         PRODUCTIONRUN  ///                                                 162SAMPL

THE LARGEST OF THESE 12 NUMBERS IS  9876.54
                                   PAUSE PRESS START TO DELETE PHASE FROM SYSTEM            165SAMPL
                         FORTRAN   UPDAT///,DELETE                                          166SAMPL
                                   HALT  SAMPLE PROGRAM COMPLETE, ALL SYSTEMS ARE GO.       167SAMPL
/simh/FortranIV/running #
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Beego(Golang Framework)使い方メモ

■ はじめに

Go 言語のフレームワークである Beego の使い方について、メモを残す。

■ 環境

Mac

■ Install

beego をローカルにインストールする。

go get github.com/beego/bee
go get -u github.com/astaxie/beego

■ パスを通す

bee コマンドを実行できるように ~/.bashrc にパスを通す。

export GO15VENDOREXPERIMENT=1
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
export PATH=$PATH:$GOROOT/bin

適応。

source ~/.bashrc

■ Create Project

bee コマンドを使って、プロジェクトを作成する。

bee new beego-project

上記を実行すると beego-project フォルダが作成される。

■ 動作確認

ターミナル上で、bee run コマンドを実行するとプロジェクトが起動する。

cd beego-project

bee run

localhost:8080 にブラウザからアクセスすると以下の画面が表示される。

スクリーンショット 2019-06-29 18.40.21.png

■ Controller の作成

Beego は、MVC タイプのフレームワークらしい。

そのため、C(Controller) を作成する。

touch beego-project/controllers/firstcontroller.go
firstcontroller.go
package controllers

import "github.com/astaxie/beego"

type FirstController struct {
    beego.Controller
}

type Employee struct {
    ID        int    `json:"id"`
    FirstName string `json:"firstName"`
    LastName  string `json:lastName`
}

type Employees []Employee

var employees []Employee

func init() {
    employees = Employees{
        Employee{ID: 1, FirstName: "Foo", LastName: "Bar"},
        Employee{ID: 2, FirstName: "Baz", LastName: "Qux"},
    }
}

func (this *FirstController) GetEmployees() {
    this.Ctx.ResponseWriter.WriteHeader(200)
    this.Data["json"] = employees
    this.ServeJSON()
}

init 関数で初期データを用意する。

ブラウザから localhost:8080/employees にアクセスした時、GetEmployees 関数が呼ばれ、初期化されたデータを JSON として保存し、呼び出し元に返す。

といった感じの関数。

■ Router の作成

次に Router を作成する。

router.go は。Beego プロジェクトにデフォルトで用意されている。

beego-project/routers/router.go
package routers

import (
    "github.com/astaxie/beego"
    "github.com/hoge/golang_cookcodes/beego_framework/beego-project/controllers"
)

func init() {
    beego.Router("/", &controllers.MainController{})
    # 追加
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
}

ここまで設定したらブラウザ上から localhost:8080/employeesにアクセスする。

bee run

スクリーンショット 2019-06-29 18.46.55.png

curl でも良い。

$ curl -X GET http://localhost:8080/employees
[
  {
    "id": 1,
    "firstName": "Foo",
    "LastName": "Bar"
  },
  {
    "id": 2,
    "firstName": "Baz",
    "LastName": "Qux"
  }
]

■ View の作成

次は、MVC の V(View) だ。

まずは、テンプレートファイルを作成する。

touch beego-project/views/dashboard.tpl
dashboard.tpl
<!DOCTYPE html>
<html>
<head>
    <title>Using beego Framework</title>
</head>
<body>
    <table border="1" style="width:100%;">
        {{ range.employees }}
        <tr>
            <td>{{.ID}}</td>
            <td>{{.FirstName}}</td>
            <td>{{.LastName}}</td>
        </tr>
        {{end}}
    </table>
</body>
</html>

このファイルは、リクエストを処理した後に、ブラウザに表示するテンプレートファイルだ。

Controller に Dashboard 関数を追加する。

firstcontroller.go
func (this *FirstController) Dashboard() {
    this.Data["employees"] = employees
    this.TplName = "dashboard.tpl"
}

さっきは、JSON にデータを渡したが、今回は先に作成したテンプレートファイルにデータを渡す。

最後に router に対して、controller に記述した Dashboard 関数をマッピングする。

router.go
func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
    # 追加
    beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard")
}

localhost:8080/dashboard にアクセスする。

スクリーンショット 2019-06-29 21.20.44.png

■ redis の導入

redis を入れて、セッション管理を行う。

go get -u github.com/astaxie/beego/session/redis

touch beego-project/controllers/sessioncontorller.go
sessioncontorller.go
package controllers

import (
    "github.com/astaxie/beego"
)

type SessionController struct {
    beego.Controller
}

func (this *SessionController) Home() {
    isAuthenticated := this.GetSession("authenticated")
    if isAuthenticated == nil || isAuthenticated == false {
        this.Ctx.WriteString("You are unauthorized to view the page.")
        return
    }
    this.Ctx.ResponseWriter.WriteHeader(200)
    this.Ctx.WriteString("Home Page")
}

func (this *SessionController) Login() {
    this.SetSession("authenticated", true)
    this.Ctx.ResponseWriter.WriteHeader(200)
    this.Ctx.WriteString("You have successfully logged in.")
}

func (this *SessionController) Logout() {
    this.SetSession("authenticated", false)
    this.Ctx.ResponseWriter.WriteHeader(200)
    this.Ctx.WriteString("You have successfully logged out.")
}

次は、router を /home, /login, /logout をマッピングする。

router.go
func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
    beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard")

    beego.Router("/home", &controllers.SessionController{}, "get:Home")
    beego.Router("/login", &controllers.SessionController{}, "get:Login")
    beego.Router("/logout", &controllers.SessionController{}, "get:Logout")
}

beego-project/main.go に redis の記述をする。

main.go
package main

import (
    "github.com/astaxie/beego"
    _ "github.com/astaxie/beego/session/redis"
    _ "github.com/hoge/golang_cookcodes/beego_framework/beego-project/routers"
)

func main() {
    beego.BConfig.WebConfig.DirectoryIndex = true
    beego.BConfig.WebConfig.StaticDir["/swagger"] = "swqgger"
    beego.Run()
}

Session を使うように設定変更を行う。

beego-project/conf/app.conf
appname = beego-project
httpport = 8080
runmode = dev

SessionOn = true
SessionProvider = "redis"
SessionProviderConfig = "127.0.0.1:6379"

プロジェクトを実行する。

bee run

実行すると connection refused になった。

panic: dial tcp 127.0.0.1:6379: connect: connection refused

なんでだろうと思ってしばらく考えていたが、redis をインストールしていなかった!

brew を使って、redis をローカルに入れる。

$ brew install redis

# 起動
$ redis-server /usr/local/etc/redis.conf
18758:C 29 Jun 2019 21:42:33.127 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
18758:C 29 Jun 2019 21:42:33.127 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=18758, just started
18758:C 29 Jun 2019 21:42:33.127 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.3 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 18758
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

18758:M 29 Jun 2019 21:42:33.130 # Server initialized
18758:M 29 Jun 2019 21:42:33.130 * DB loaded from disk: 0.001 s

プロジェクトを実行し、今度は、connection refuzed にならないことを確認。

bee run

ターミナル上で動作確認。

$ curl -X GET http://localhost:8080/home
You are unauthorized to view the page.

$ curl -X GET -i http://localhost:8080/login
HTTP/1.1 200 OK
Server: beegoServer:1.11.2
Set-Cookie: beegosessionID=cc54cdea942085d9186be51bfd3971e6; Path=/; HttpOnly
Date: Sat, 29 Jun 2019 12:46:46 GMT
Content-Length: 32
Content-Type: text/plain; charset=utf-8

You have successfully logged in.$ 

$ curl --cookie "beegosessionID=cc54cdea942085d9186be51bfd3971e6" http://localhost:8080/home
Home Page

$ curl -X GET http://localhost:8080/logout
You have successfully logged out.

■ ログ出力

コンソールにログを出力する機能を実装する。

go get github.com/astaxie/beego/context

mkdir beego-project/filters && touch beego-project/filters/filter.go
filter.go
package filters

import (
    "fmt"
    "time"

    "github.com/astaxie/beego/context"
)

var LogManager = func(ctx *context.Context) {
    fmt.Println("IP::" + ctx.Request.RemoteAddr + ", Time::" + time.Now().Format(time.RFC850))
}

アクセスがあった場合、コンソールにログを出力する。

/*(なんでも) を router にマッピングする。

router.go
package routers

import (
    "github.com/astaxie/beego"
    "github.com/hoge/golang_cookcodes/beego_framework/beego-project/controllers"
    "github.com/hoge/golang_cookcodes/beego_framework/beego-project/filters"
)

func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
    beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard")

    beego.Router("/home", &controllers.SessionController{}, "get:Home")
    beego.Router("/login", &controllers.SessionController{}, "get:Login")
    beego.Router("/logout", &controllers.SessionController{}, "get:Logout")

    beego.InsertFilter("/*", beego.BeforeRouter, filters.LogManager)
}

動作確認を行う。

curl -X GET http://localhost:8080/employees

[
  {
    "id": 1,
    "firstName": "Foo",
    "LastName": "Bar"
  },
  {
    "id": 2,
    "firstName": "Baz",
    "LastName": "Qux"
  }
]

Go のアプリケーションサーバーを動かしているコンソールに、以下が出力される。

IP::[::1]:51364, Time::Saturday, 29-Jun-19 22:01:29 JST
2019/06/29 22:01:29.936 [D] [server.go:2774]  |  ::1| 200 |   1.256205ms|   match| GET      /employees   r:/employees

■ エラーハンドリング

エラーハンドリングは、重要。

予期しない動作を防いだり、デバッグに使えたりする。

Controller に、500 と 404 のエラーハンドラーを記述する。

touch controllers/errorcontroller.go
errorcontroller.go
package controllers

import (
    "github.com/astaxie/beego"
)

type ErrorController struct {
    beego.Controller
}

func (c *ErrorController) Error404() {
    c.Data["content"] = "Page Not Found"
    c.TplName = "404.tpl"
}

func (c *ErrorController) Error500() {
    c.Data["content"] = "Internal Server Error"
    c.TplName = "500.tpl"
}

func (c *ErrorController) ErrorGeneric() {
    c.Data["content"] = "Some Error Occurred"
    c.TplName = "genericerror.tpl"
}

firstcontroller.go に GetEmployee ハンドラーを追加する。

GetEmployee は、特定のユーザー情報を引っこ抜く関数。

firstcontroller.go
func (this *FirstController) GetEmployee() {
    var ID int
    this.Ctx.Input.Bind(&ID, "id")
    var isEmployeeExist bool
    var emps []Employee

    for _, employee := range employees {
        if employee.ID == ID {
            emps = append(emps, Employee{ID: employee.ID,
                FirstName: employee.FirstName,
                LastName:  employee.LastName})
            isEmployeeExist = true
            break
        }
    }

    if !isEmployeeExist {
        this.Abort("Error Page")
    } else {
        this.Data["employees"] = emps
        this.TplName = "dashboard.tpl"
    }
}

router に GetEmployee をマッピングする。

router.go
func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
    // 追加
    beego.Router("/employee", &controllers.FirstController{}, "get:GetEmployee")
    beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard")

    beego.Router("/home", &controllers.SessionController{}, "get:Home")
    beego.Router("/login", &controllers.SessionController{}, "get:Login")
    beego.Router("/logout", &controllers.SessionController{}, "get:Logout")

    beego.InsertFilter("/*", beego.BeforeRouter, filters.LogManager)
}

最後にエラーを表示せるためのテンプレートを作成する。

touch views/genericerror.tpl
genericerror.tpl
<!DOCTYPE html>
<html>
<head>
    <title>Error</title>
</head>
<body>
    {{.content}}
</body>
</html>

プロジェクトを起動する。

bee run

http://localhost:8080/employee?id=1 にアクセスする。

スクリーンショット 2019-06-30 8.00.55.png

http://localhost:8080/employee?id=1000 にアクセスする。(404)

スクリーンショット 2019-06-30 8.03.12.png

500 の動作確認ができないッ!

■ キャッシング

キャッシングもできる。

Beego では、memory キャッシュが使えるらしい。

go get github.com/astaxie/beego/cache

touch controllers/cachecontroller.go
cachecontroller.go
package controllers

import (
    "fmt"
    "time"

    "github.com/astaxie/beego"
    "github.com/astaxie/beego/cache"
)

type CacheController struct {
    beego.Controller
}

var beegoCache cache.Cache
var err error

func init() {
    beegoCache, err = cache.NewCache("memory", `{"interval: 60}`)
    beegoCache.Put("foo", "bar", 100000*time.Second)
}

func (this *CacheController) GetFromCache() {
    foo := beegoCache.Get("foo")
    this.Ctx.WriteString("Hello " + fmt.Sprintf("%v", foo))
}

プロジェクト起動時(bee run)に、foo(key) と bar(value) をキャッシュに追加する。

router に GetFromCache をマッピングする。

router.go
func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees")
    beego.Router("/employee", &controllers.FirstController{}, "get:GetEmployee")
    beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard")

    beego.Router("/home", &controllers.SessionController{}, "get:Home")
    beego.Router("/login", &controllers.SessionController{}, "get:Login")
    beego.Router("/logout", &controllers.SessionController{}, "get:Logout")

    beego.InsertFilter("/*", beego.BeforeRouter, filters.LogManager)
    // 追加
    beego.Router("/getFromCache", &controllers.CacheController{}, "get:GetFromCache")
}

http://localhost:8080/getFromCache にアクセスする。

スクリーンショット 2019-06-30 8.17.11.png

bar が出てきているので、キャッシュされているみたい。

■ モニタリング機能

Beego には、モニタリング機能がついているらしい。

まずは、設定ファイルに以下を追加する。

conf/app.conf
EnableAdmin = true
AdminAddr = "localhost"
AdminPort = 8088

プロジェクトを起動する。

bee run

ブラウザから localhost:8088 にアクセスする。

スクリーンショット 2019-06-30 21.25.07.png

Dashboard が出てきた。

何かに使える...?

■ 動作モード

Beego はデフォルトでは、開発モードで起動するらしい。

本番用には、以下の設定を行う。

conf/app.conf
beego.runmode = "prod"

■ バックエンドで実行

bee run では、フォアグラウンドで実行されていたが、バックエンドで起動したい場合は、nohup コマンドを使用する。

nohup ./beego-project 

■ Docker

Go + Docker を学んだので、せっかくだから Docker 上で起動してみる。

beego-project フォルダと同じ階層に以下のファイルを作成する。

touch Dockerfile
touch docker-compose.yml

まずは、Dockerfile を作成する。

Dockerfile
# golang のイメージを取得
FROM golang:1.9.2

# ソースのコピー先(自分のリポジトリに合わせる)
ENV SRC_DIR=/go/src/github.com/{username}/golang_cookcodes/beego_framework/

# ソースをコピーする
ADD . $SRC_DIR

# ワーキングディレクトリを変更
WORKDIR $SRC_DIR/beego-project

# コンパイル
RUN cd /go/src;
RUN go get github.com/beego/bee
RUN go get -u github.com/astaxie/beego
RUN go get -u github.com/astaxie/beego/session/redis
RUN go get github.com/astaxie/beego/context
RUN go get github.com/astaxie/beego/cache

ソースのコピー先(自分のリポジトリに合わせる)

ENV SRC_DIR=/go/src/github.com/{username}/golang_cookcodes/beego_framework/

router.go などのファイルに import "github.com/**username**/golang_cookcodes/beego_framework/beego-project/filters" と記述をしているので、環境依存度が高く、SRC_DIR に指定するパスが長くなってしまった。

なんとかできないかと思いつつ、とりあえず、docker-compose.yml を作成。

docker-compose.yml
version: '3'

services:
    golang:
        build: .
        ports:
            - "8080:8080"
        container_name: mohohewo
        command: >
          bee run
        depends_on:
            - redis
    redis:
        image: redis:3.0
        container_name: redis

redis を docker 化したので、app.conf を書き換える。

conf/app.conf
appname = beego-project
httpport = 8080
runmode = dev

SessionOn = true
SessionProvider = "redis"

# docker-compose.yml に設定した container_name を指定
SessionProviderConfig = "redis:6379"

EnableAdmin = true
AdminAddr = "localhost"
AdminPort = 8088

beego.runmode = "prod"

terminal 上で、下記のコマンドを実行。(docker-compose.ymlファイルがある場所)

docker-compose up

localhost:8000 にアクセスすると Beego のトップページが表示された。

スクリーンショット 2019-06-30 22.49.05.png

■ Nginx の導入

最後に Web アプリケーションらしく、Nginx と繋げてみる。

beego-project フォルダと同じ階層に nginx フォルダを作成する。

mkdir nginx
touch nginx/uwsgi_params
mkdir nginx/conf
touch nginx/conf/mohohewo_nginx.conf

uwsgi_params は、公式を参考に作成した。

mohohewo_nginx.conf に nginx の設定を行う。

mohohewo_nginx.conf
# コネクト先
upstream beego {
    ip_hash;
    server golang:8080;
}

# nginx 基本設定
server {
    server_name 127.0.0.1; # ひとまず自分自身の IP アドレスを設定
    charset     utf-8;

    # favicon 無効
    location = /favicon.ico {access_log off; log_not_found off;}

    # localhost にアクセスした時の設定
    location / {
        uwsgi_pass  beego; # upstream に飛ばす
        include     /etc/nginx/uwsgi_params; # uwsgi_params ファイル読み込み
        proxy_pass http://golang:8080/; # upstream の server(golang:8080) と合わせないと 502 bad gateway になる
    }
}

uwsgi の設定はいるのか?とは思うが、設定はひとまず完了。

次は、docker-compose.yml に nginx を追加する。

docker-compose.yml
version: '3'

services:
    golang:
        build: .
        ports:
            - "8080:8080"
        container_name: mohohewo
        command: >
          bee run
        depends_on:
            - redis
    redis:
        image: redis:3.0
        container_name: redis

    nginx:
        image: nginx:1.15.9-alpine
        ports:
          - "80:80"
        volumes:
          - ./nginx/conf:/etc/nginx/conf.d
          - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
          - ./nginx/log:/var/log/nginx
        container_name: nginx
        depends_on:
            - golang 

ここまで完了したら docker-compose up でプログラムを実行。

docker-compose up

localhost にアクセスする。

スクリーンショット 2019-07-01 6.48.34.png

■ まとめ

ここで学んだことは、Beego の基本機能の一部でしかないと思う。
結構、書きやすくていい感じのフレームワークなので、色々アプリケーションを作っていこうと思う。

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

Qiitaの記事をランダムに読めるAPI / サービス に機能追加してみた

数日前に公開された上記のサービスがとても素晴らしく、
このサービスを通じて普段はなかなか出会うことのない類の記事とたくさん出会うことができました!

きっかけ

しかし個人的に一点問題が。元記事の筆者の方も指摘なさっているとおり、スパム記事の割合がなかなか高い
ざっくりとした体感、10分の1くらいの確率でスパムをツモってしまう印象です。ちょっと残念。

そこで元のサービスに一部機能を追加し、引き当てる記事をある程度スクリーニングできるようにしました。
(元々のバイアスなしに謎の知識(?)を仕入れることができます。という意図から少し逸れてしまいますが…)

モノ

スクリーンショット 2019-07-01 0.45.38.png

赤枠の箇所が追加部分になります。

https://random-qiita-api-c2498.firebaseapp.com/
Webサービス版のリンクは↑こちら。追加した機能は大きくふたつです。

1. 特定数以上のいいねを獲得している記事のみを取得対象にする

そのままですね。獲得いいね数を1以上にするだけでもスパム記事との遭遇率はだいぶ下がります。
いっぽうでいいね数を上げすぎると既知の記事との遭遇率が上がるので、上限値は100としています。

2. 指定したタグを含む記事のみを取得対象にする

全取得対象の10000記事のうち100以上の記事に付されているタグを対象に、タグ指定で取得します。
1. のいいね数での絞り込みと組み合わせて使用することももちろん可能です。

エンドポイント例

いいね数が5以上、タグにPythonを含む記事のみを取得する場合の例です。
ご覧の通り、いずれもクエリストリングに入力値を渡してスクリーニングに使用しています。
リクエストからクエリストリングを抜き出している箇所は以下。

exports.get = functions.https.onRequest((request, response) => {
  cors(request, response, () => {
    const likes_min = request.query.likes_min;
    const tag = request.query.target_tag;

またそれぞれの条件を指定した状態のブックマークレットをワンクリックで作成できるようにしました。
元記事と同じ引用となってしまいますが、ブックマークレットの登録方法を参照のこと。

使ってみて

個人的にはだいぶストレスが減りました。
いいね数を適度に高く設定するとやはり良記事との遭遇率も上がり、良い感じです。
まだ動かしはじめたばかりなので、不具合などありましたらご指摘いただけるとありがたいです。

ところでアーキテクチャなどは元々のものをまるっきり流用しているのですが、
この機能追加だけで数時間かかってしまいました。。。
元記事の方の4時間で実装というのは脱帽です。すごい。

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

ローカル上にLocalStackをDockerで実行

はじめに

開発用にAWSのサービスをローカル環境に構築できる、LocalStackというプロジェクトがあります。
https://github.com/localstack/localstack

今回はDockerを使ったLocalStackの実行方法をまとめます。

環境

OS: macOS Mojave 10.14.5
Docker: 18.09.0
Docker Compose: 1.23.2

プロジェクトをプル

git clone https://github.com/localstack/localstack.git
cd localstack/

サービスの指定

環境変数 SERVICES にカンマ区切りでサービス名を指定することで起動させたいサービスを選ぶことができます。
指定がない場合、全て指定と同意です。

指定できるサービスは以下の通り

サービス サービス名 エンドポイント
API Gateway apigateway http://localhost:4567
Kinesis kinesis http://localhost:4568
DynamoDB dynamodb http://localhost:4569
DynamoDB Streams dynamodbstreams http://localhost:4570
Elasticsearch elasticsearch http://localhost:4571
S3 s3 http://localhost:4572
Firehose firehose http://localhost:4573
Lambda lambda http://localhost:4574
SNS sns http://localhost:4575
SQS sqs http://localhost:4576
Redshift redshift http://localhost:4577
ES (Elasticsearch Service) es http://localhost:4578
SES ses http://localhost:4579
Route53 route53 http://localhost:4580
CloudFormation cloudformation http://localhost:4581
CloudWatch cloudwatch http://localhost:4582
SSM ssm http://localhost:4583
SecretsManager secretsmanager http://localhost:4584
StepFunctions stepfunctions http://localhost:4585
CloudWatch Logs logs http://localhost:4586
STS sts http://localhost:4592
IAM iam http://localhost:4593
EC2 ec2 http://localhost:4597

※ サービス名はaws cliのリファレンスから
http://docs.aws.amazon.com/cli/latest/reference/#available-services

注意点: Dockerに割り当てるメモリが少ないと正しく起動しない

Dockerへのメモリ割り当てが少ないと、指定した全てのサービスが正しく実行されないことがあります。
例えば、以下のような elasticsearch の起動で実行エラーが発生します。

...
WARNING:infra.py: Service "elasticsearch" not yet available, retrying...
ERROR:infra.py: Error checking state of local environment (after some retries): Traceback (most recent call last):
...

だいたいメモリを 4 GB にすれば全てのサービスが実行できました。

多くのサービスを実行させたいときは、割り当ての量を増やすか、増やせないときは最小のサービスを指定して実行が必要です。

Dockerで実行

Docker Composeを実行

TMPDIR=/private$TMPDIR \
DATA_DIR=/tmp/localstack/data \
SERVICES=apigateway,kinesis,dynamodb,dynamodbstreams,elasticsearch,s3,\
lambda,sns,sqs,redshift,es,ses,route53,cloudformation,cloudwatch,\
ssm,secretsmanager,stepfunctions,logs,sts,iam,ec2 \
docker-compose up -d

TMPDIR : Macの場合は指定が必須
DATA_DIR : データの永続化のために指定
SERVICES : 実行するサービスを指定 (全てのサービスを実行する際は指定はいらない。上ではサンプルとして全てを列挙)

バックグランドで実行のために -d オプションをつけています。

ログの最後に Ready と出れば、起動完了です。
すると、デフォルトではDocker上に localstack_localstack_1 という名前でプロセスが実行されていると思います。

ホストOS起動時に自動で実行するように設定

ホストOSが再起動した際も自動で実行されるよう設定値を変更します。

docker update --restart=always localstack_localstack_1

まとめ

これでAWSサービスをローカルで利用できる環境が整いました。
あとは、CLIやSDKで、各サービスのエンドポイントをLocalStackへ向けることで利用可能となります。

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