- 投稿日:2019-07-01T23:55:15+09:00
プロキシとリバースプロキシの違いをまとめて、ついでにDocker使ってnginxでリバースプロキシを立ててみる
はじめに
プロキシとリバースプロキシってややこしいですよね。
この記事では、概念図を交えながらプロキシとリバースプロキシの違いをまとめていきます。最後に実践として、Dockerを使ってローカルでリバースプロキシ(nginx)を立ててみます。
プロキシとリバースプロキシの違い
混乱しやすいプロキシとリバースプロキシの違いについて、まとめておきます。
プロキシとは
一般的に使われるプロキシといえば、「forward proxy」のことをさします。
forward proxyとは、ある別のWebサイトへのリクエストを受け取り、クライアントの代わりにWebサイトへリクエストを送信するサーバのことです。こちらの図がforward proxyの概念図です。
クライアントXがとあるWebサイト(サーバZ)にアクセスする時にプロキシサーバYを利用している例です。
リクエストは
クライアントX → プロキシY → WebサーバZ
という流れで伝わっていきます。プロキシを使う目的
このようなforward proxyを使う目的が、WebサーバZに直接アクセスできないクライアントXからZにリクエストを送信できるようにすることです。例としては、
- クライアントがWebサイトの権限者からブロックされているとき(IPでブラックリスト)
- Youtubeへの通信をブロックしている会社からYoutubeにアクセスしたいとき
- 政府が特定のニュースサイトへの通信を遮断しているとき
- 大学がHTTP以外のプロトコルを制限しているとき(研究室内のサーバにアクセスするために踏み台としてプロキシを使う)
などが挙げられます。
リバースプロキシとは
リクエストの流れはプロキシと同様で、
クライアント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のパスでそれぞれにアクセスできるものを考えます。
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.ymlversion: '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.confevents { 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
コンテクストには、listen
とserver_name
の二つが記述され、それらが条件となります。
- まずは、
listen
で指定されたIPアドレス(またはホスト)とポート番号がリクエストのものと一致するかどうか調べます。
- 当てはまったら、
server_name
がHTTPヘッダのHost
と同じであれば、そのserver
コンテクストに書かれた処理をすることになります。今回利用するのは次のような
server
コンテキストです。server { listen 80; server_name localhost; # 処理... }意味としては、
listen
でポート80にきたリクエストかどうか判定し、server_name
で リクエストヘッダのHost
がlocalhost
と一致しているかどうかを判定します。どちらも当てはまるようなら続く処理が実行されるというものです。今回の場合、リバースプロキシとして機能させるため、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/dogHTTPリクエストヘッダ> 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>%もちろんブラウザからもみれます。
バッチリですね。
まとめ
リバースプロキシの概念的な理解から、nginxでの実装までみてきました。
Webサーバを外から直接アクセスできないようにするだけでなく、リクエストに応じてサーバを自在に変えることができるため、柔軟なサーバ構成を考えられるようになるかと思います。本記事がリバースプロキシやnginxの理解に役に立てば幸いです。
ソースコードはGitHub(zawawahoge/reverse-proxy)にアップロードしていますので、ぜひローカルで試してみてください。
参考サイト
- 投稿日:2019-07-01T23:55:15+09:00
プロキシとリバースプロキシの違いまとめ
はじめに
プロキシとリバースプロキシってややこしいですよね。
この記事では、概念図を交えながら混乱しやすいプロキシとリバースプロキシの違いをまとめていきます。プロキシとは
一般的に使われるプロキシといえば、「forward proxy」のことをさします。
forward proxyとは、ある別のWebサイトへのリクエストを受け取り、クライアントの代わりにWebサイトへリクエストを送信するサーバのことです。こちらの図がforward proxyの概念図です。
クライアントXがとあるWebサイト(サーバZ)にアクセスする時にプロキシサーバYを利用している例です。
リクエストは
クライアントX → プロキシY → WebサーバZ
という流れで伝わっていきます。プロキシを使う目的
このようなforward proxyを使う目的が、WebサーバZに直接アクセスできないクライアントXからZにリクエストを送信できるようにすることです。例としては、
- クライアントがWebサイトの権限者からブロックされているとき(IPでブラックリスト)
- Youtubeへの通信をブロックしている会社からYoutubeにアクセスしたいとき
- 政府が特定のニュースサイトへの通信を遮断しているとき
- 大学がHTTP以外のプロトコルを制限しているとき(研究室内のサーバにアクセスするために踏み台としてプロキシを使う)
などが挙げられます。
リバースプロキシとは
リクエストの流れはプロキシと同様で、
クライアント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の存在を知らない
- 投稿日:2019-07-01T23:00:12+09:00
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/grubGRUB_CMDLINE_LINUX に "rootflags=uquota,pquota" を追加。
sudo grub-mkconfig -o /boot/grub/grub.cfgdocker 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:/#サイズが変わった。
- 投稿日:2019-07-01T14:14:50+09:00
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.jar2 . コンテナ(コンテナ名:app)を再起動する。
docker restart appこの方法だとコンテナの再ビルドが必要ないため、より短時間でコンテナの更新ができます。
参考
- docker公式ドキュメント http://docs.docker.jp/engine/reference/builder.html#from
- 投稿日:2019-07-01T13:35:51+09:00
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 /simhdocker 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 #
- 投稿日:2019-07-01T13:35:51+09:00
Simh/i1401 on docker
gcc on Dockerで、gccをビルドする手順を書きました。
その中で、実行環境を作成して動かすのは「Hello World」でした。
もう少し、実用的な?ものということでSimhのうち、IBM1401シミュレータで
FortranIVのプログラムを動かしてみます。IBM1401 FortranIVについては、IBM1401 FortranIV Summaryを参考にしてください(実は手前味噌)。
Simhの実行環境のDockerfileを用意します。
DockerfileFROM 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 /simhdocker 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 #
- 投稿日:2019-07-01T13:35:51+09:00
Simh/i1401 with FortranIV on docker
gcc on Dockerで、gccをビルドする手順を書きました。
その中で、実行環境を作成して動かすのは「Hello World」でした。
もう少し、実用的な?ものということでSimhのうち、IBM1401シミュレータでFortranIVのプログラムを動かしてみます。IBM1401 FortranIVについては、IBM1401 FortranIV Summaryを参考にしてください(実は手前味噌)。
Simhの実行環境のDockerfileを用意します。
DockerfileFROM 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/FortranIVdocker 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 #
- 投稿日:2019-07-01T07:31:36+09:00
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 runlocalhost:8080 にブラウザからアクセスすると以下の画面が表示される。
■ Controller の作成
Beego は、MVC タイプのフレームワークらしい。
そのため、C(Controller) を作成する。
touch beego-project/controllers/firstcontroller.gofirstcontroller.gopackage 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.gopackage 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 runcurl でも良い。
$ 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.tpldashboard.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.gofunc (this *FirstController) Dashboard() { this.Data["employees"] = employees this.TplName = "dashboard.tpl" }さっきは、JSON にデータを渡したが、今回は先に作成したテンプレートファイルにデータを渡す。
最後に router に対して、controller に記述した Dashboard 関数をマッピングする。
router.gofunc init() { beego.Router("/", &controllers.MainController{}) beego.Router("/employees", &controllers.FirstController{}, "get:GetEmployees") # 追加 beego.Router("/dashboard", &controllers.FirstController{}, "get:Dashboard") }localhost:8080/dashboard にアクセスする。
■ redis の導入
redis を入れて、セッション管理を行う。
go get -u github.com/astaxie/beego/session/redis touch beego-project/controllers/sessioncontorller.gosessioncontorller.gopackage 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.gofunc 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.gopackage 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.confappname = 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.gofilter.gopackage 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.gopackage 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.goerrorcontroller.gopackage 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.gofunc (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.gofunc 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.tplgenericerror.tpl<!DOCTYPE html> <html> <head> <title>Error</title> </head> <body> {{.content}} </body> </html>プロジェクトを起動する。
bee runhttp://localhost:8080/employee?id=1 にアクセスする。
http://localhost:8080/employee?id=1000 にアクセスする。(404)
500 の動作確認ができないッ!
■ キャッシング
キャッシングもできる。
Beego では、memory キャッシュが使えるらしい。
go get github.com/astaxie/beego/cache touch controllers/cachecontroller.gocachecontroller.gopackage 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.gofunc 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 にアクセスする。
bar が出てきているので、キャッシュされているみたい。
■ モニタリング機能
Beego には、モニタリング機能がついているらしい。
まずは、設定ファイルに以下を追加する。
conf/app.confEnableAdmin = true AdminAddr = "localhost" AdminPort = 8088プロジェクトを起動する。
bee runブラウザから localhost:8088 にアクセスする。
Dashboard が出てきた。
何かに使える...?
■ 動作モード
Beego はデフォルトでは、開発モードで起動するらしい。
本番用には、以下の設定を行う。
conf/app.confbeego.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.ymlversion: '3' services: golang: build: . ports: - "8080:8080" container_name: mohohewo command: > bee run depends_on: - redis redis: image: redis:3.0 container_name: redisredis を docker 化したので、app.conf を書き換える。
conf/app.confappname = 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 uplocalhost:8000 にアクセスすると Beego のトップページが表示された。
■ Nginx の導入
最後に Web アプリケーションらしく、Nginx と繋げてみる。
beego-project フォルダと同じ階層に nginx フォルダを作成する。
mkdir nginx touch nginx/uwsgi_params mkdir nginx/conf touch nginx/conf/mohohewo_nginx.confuwsgi_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.ymlversion: '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 uplocalhost にアクセスする。
■ まとめ
ここで学んだことは、Beego の基本機能の一部でしかないと思う。
結構、書きやすくていい感じのフレームワークなので、色々アプリケーションを作っていこうと思う。
- 投稿日:2019-07-01T01:18:42+09:00
Qiitaの記事をランダムに読めるAPI / サービス に機能追加してみた
数日前に公開された上記のサービスがとても素晴らしく、
このサービスを通じて普段はなかなか出会うことのない類の記事とたくさん出会うことができました!きっかけ
しかし個人的に一点問題が。元記事の筆者の方も指摘なさっているとおり、スパム記事の割合がなかなか高い。
ざっくりとした体感、10分の1くらいの確率でスパムをツモってしまう印象です。ちょっと残念。そこで元のサービスに一部機能を追加し、引き当てる記事をある程度スクリーニングできるようにしました。
(元々のバイアスなしに謎の知識(?)を仕入れることができます。
という意図から少し逸れてしまいますが…)モノ
赤枠の箇所が追加部分になります。
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時間で実装というのは脱帽です。すごい。
- 投稿日:2019-07-01T01:03:07+09:00
ローカル上に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へ向けることで利用可能となります。