まえおき
Cloud9はAWS Cloud9という似て非なるサービスに変わった。
自分はCloud9 PROというサービスをとても熱心に(課金して)使っていたので、「これからはAWS Cloud9を使ってね♪」って気軽に言われてもな、そうはいかないんだ。
- AWSはGitHubじゃログインできないし
- Cloud9 PROだと Runボタンをポチッとおしたらhttpsで即座に外から見れた のが、AWS Cloud9はできないし
- そもそも開発環境なのに従量課金なんてありえないだろ!!!!!!
ということで、ものすごくフラストレーションが溜まっていた。
Cloud9 PROが良かった理由
他のクラウドIDEは使えなさすぎた
Cloud9終了のお知らせを受けて、 Alternatives to Cloud9
とかでGoogle検索して色々探したよ。有名ドコロだと
- Paiza Cloud - https://paiza.cloud/ja/
- CodeAnywhere - https://codeanywhere.com/
などなど。
ただ、実際に使ってみて使い物にならなかった。Paiza Cloudは謎のGUIがとても邪魔くさかったし、CodeAnywhereはエディタの品質が悪すぎ&コンテナごとに画面が分けられていないのがしっくりこなかった。
あと、両者とも致命的にダメだったのが、コンソールからエディタを起動できないこと。
Cloud9は c9
ってコマンドを使うとコンソールからエディタが立ち上がるんだけど、
それに相当する機能がPaiza CloudにもCodeAnywhereにもなかった。
これエンジニアだったら必須機能だと思うんだけど、PaizaやCodeAnywhereはユーザの使い込み試験やってないの?
料金体系がエンジニアフレンドリーだったCloud9 PRO
成果物をhttpsでササッと公開する、というだけならCloud9じゃなくても他のクラウドIDEでもできるんだけど、無料でそこまで太っ腹なことをやっているところは無かった。完全に蛇足だけど、いろいろ話題を読んでいるプログラミングスクール とかでも、昔は↑を最大限に活用したカリキュラムを組んでいた(と、人づてに聞いた)。今はどうしてるんだろう。
個人的には課金はしていたので、無料であることは求めていなかったんだけど、それでも
- 「あ、これやってみたい」と思ったものは基本的に新規に作業用コンテナ作って始めたい
- 作成可能なコンテナ数に上限があるとか論外
- せめて1つくらいは常時(外部から)接続可能なコンテナはあってほしい
- 料金は10ドル/月 くらいであってほしい
という、エンジニアの平均的な要望を満たしてくれるIDEはCloud9 PRO以外には無かったのだ。
無いなら作るしかない
Cloud9 はエディタとしては100% ではないんだけど、ちょうどいい具合に整っていた。Cloud9 PROが終了してもAWS Cloud9は使いたくないし、他のクラウドIDEも使いものにならないので、もう自分で作る以外の選択肢が現状では無い。
Cloud9は親切にも、SDKを公開してくれている。
あとは、クラウドワ ークス って会社で働いていたときに
こんな素晴らしいことをやってるメンバーがいて、これにCloud9 SDKを組み込むと理想的な使い捨て開発環境ができることはお試し済みだった。
なので、サーバーの月額費用だけなんとかなれば、自分用のCloud9 PROはできるという確信はあった。
ちょうどタイミングよく、今の会社でMicrosoft Azureの優待利用(1ヶ月あたり○○円までは無料でずっと使える、的なやつ)があったのでそれも相まって、作ることにした。
自分用のCloud9 PROっぽい環境の作る前に・・・
Qiita記事にするか迷ったけど、面倒なのでここに書く。
前述の通り、ほぼほぼ Docker swarmで作る社内heroku: yadockeri - Qiita のパクリである。
用意するもの
- マシンを2,3台(そのうち少なくとも1台はグローバルIPを持っている必要がある)
- 独自ドメイン
- ドメイン管理をすることを考えると AWS Route 53 がシンプルでラク。
制限事項
自分専用なので
- 認証は特にやらない
- SSL化はいったんあきらめる
ということにする。
SSHのポートだけ開放して、SSHポートフォワーディングで使う想定。
自分用のCloud9 PROっぽい環境を作る
さて作るぞ。
とりあえずEC2のインスタンスが3台あると仮定してすすめる。
- c9-master (グローバルIPを持っている子)
- c9-slave1
- c9-slave2
同じVPC(Azureだったらリソースグループ)上に配置して、プライベートIPで相互に通信できるようにしておく。
dockerを入れる
個々のサーバーにSSHで入り、
curl https://raw.githubusercontent.com/YusukeIwaki/cloud-pine/190e8454b1dbae0521f035f7f37ceb62f8f126f4/install-docker-ce.sh | bash
する。
Docker Swarmクラスタを組む
c9-masterで docker swarm init
する。
swarm initすると、「ワーカーを追加するには docker swarm join ….. —token=xxxxxx
を打ってね」みたいなのが出てくるので、そのコマンドをコピって、c9-slave1, c9-slave2 で実行する。
ちなみに最近のDockerはSSH経由でリモートのDockerホストに接続できる(参考: Docker CE 18.09からssh経由でリモートのdockerデーモンに接続できるようになるってよ - Qiita )ので、c9-masterから
DOCKER_HOST=ssh://c9-slave1 docker swarm join …….. —token=xxxxxx
のように実行すれば、いちいちc9-slave1やc9-slave2にSSHしにいかなくても済む。
c9-masterで docker node ls
を打って
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION eeshee1yai5Moh5saGu0Ueje * c9-master Ready Active Leader 18.09.7 ahm3iexaeth5co2eir0Hahno c9-slave1 Ready Active 18.09.7 eangie6ohDigh1Zu4epung2I c9-slave2 Ready Active 18.09.7
みたいな出力が得られたらOK。
ワークスペース用のコンテナをつくる
Cloud9 PROはUbubtu 14.04ベースで、 RubyもPythonもNodeもgitもheroku-cliもaws-cliも(バージョンは古いけど)入っている状態だった。
とりあえず今はそこまでは求めないので、Cloud9 SDKだけ入れたDebianにする。(本気で使い始めたら、RubyとPythonくらいは入れようと思う) cloud-pine/Dockerfile at df0962b100241db3eb3fd40219d6cc22e4195ea8 · YusukeIwaki/cloud-pine · GitHub
docker buildして、DockerHubにプッシュする。 https://hub.docker.com/r/yusukeiwaki/cloud-pine-workspace
docker-composeを↓みたいに作り、 docker-compose pull
で正常にワークスペースが取得できる状態になればOK。
cloud-pine/docker-compose.yml at 190e8454b1dbae0521f035f7f37ceb62f8f126f4 · YusukeIwaki/cloud-pine · GitHub
ワークスペースを試しにデプロイしてみる
c9-masterにSSHで入り
$ wget https://raw.githubusercontent.com/YusukeIwaki/cloud-pine/190e8454b1dbae0521f035f7f37ceb62f8f126f4/docker-compose.yml $ docker stack deploy --compose-file docker-compose.yml playground1
をする。docker-compose.yml は1つ前の手順で作成したものを指定する。
Creating network playground1_default Creating service playground1_cloud9
こんな感じで、オーバーレイネットワークとサービスが作られたらOK。
$ docker service ps playground1_cloud9 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS prviwbnxxu3l playground1_cloud9.1 yusukeiwaki/cloud-pine-workspace:latest c9-slave1 Running Running 43 seconds ago
みたいな感じで、無事にc9-slave1 に配置された。(空いているDockerノードにいい感じに配置されるので、c9-masterやc9-slave2に配置されることもある)
ただ、これ単体では外からアクセスはできない。
外からアクセスできるようにする
グローバルIPを持っているc9-masterにnginx(リバースプロキシ)サーバーを立てるわけだが、ネイティブにリバースプロキシサーバを立てると、
$ ping playground1_cloud9 ping: playground1_cloud9: 名前またはサービスが不明です
のように、playground1_cloud9コンテナへのルーティングがうまくできない。docker inspectを使ってIPアドレスを特定して、・・・とかをやるのは茨の道だ。
ということで、nginxはネイティブで立てるのではなくdockerサービスとして立てる。そんで、playground1_cloud9とかと同じオーバーレイネットワークに入れれば、
$ docker exec -it 4936af085963 ping playground1_cloud9 PING playground1_cloud9 (10.0.6.88): 56 data bytes 64 bytes from 10.0.6.88: seq=0 ttl=64 time=0.058 ms 64 bytes from 10.0.6.88: seq=1 ttl=64 time=0.125 ms 64 bytes from 10.0.6.88: seq=2 ttl=64 time=0.076 ms 64 bytes from 10.0.6.88: seq=3 ttl=64 time=0.067 ms
のようにnginxコンテナからplayground1_cloud9が見えるようになる。
この辺の仕組みは Docker swarmで作る社内heroku: yadockeri - Qiita あたりで言及されているやつ丸パクリです。
nginxコンテナをつくる
nginx.confを
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; server { listen 80; set_real_ip_from '10.0.0.0/8'; real_ip_header X-Forwarded-For; proxy_set_header X-Forwarded-Scheme $http_x_forwarded_proto; proxy_set_header Host $host; resolver 127.0.0.11 valid=5s; # nginx:alpineコンテナの/etc/resolve.conf にべた書きされているnameserverの値をそのまま転記 if ($host ~* ([a-z0-9][a-z0-9_-]*)\.ide\.mydomain\.com$) { set $subdomain $1; set $dst_port 8888; } if ($host ~* ([a-z0-9][a-z0-9_-]*)\.preview\.mydomain\.com$) { set $subdomain $1; set $dst_port 8080; } location / { proxy_pass http://${subdomain}_cloud9:${dst_port}; } } }
こんな感じ。 mydomain.net のところは自分のドメインにする。
あとは、c9-masterにSSHで入って
$ docker network create --driver overlay --internal c9-overlay-network $ docker service create --constraint 'node.role==manager' --mount type=bind,source=$(pwd)/nginx.conf,destination=/etc/nginx/nginx.conf,readonly --name nginx --network c9-overlay-network -p 80:80 nginx:stable-alpine
のようにDockerサービスを立ち上げれば、リバースプロキシが立つ。
ドメインの向き先をc9-masterにする
AWS Route53側の設定。
*.preview.mydomain.com
と *.ide.mydomain.com
のAレコードをc9-masterのグローバルIPにする。(ちなみにAWS EC2じゃなくてAzure VMだったら、AレコードでIP指定するのではなく、CNAMEレコードで hogehoge.japanease.cloudapp.azure.com を指定する)
nginxからCloud9を見えるようにする
先の手順までで、 http://playground1.preview.mydomain.com/ とか http://playground1.ide.mydomain.com/ とかを指定すると、nginxが502エラーを返す状態にまでなるはず。
これはnginxまでは到達できているけど、その先(Cloud9コンテナ)にリクエストを転送できていない状態。
なんでエラーになるかと言うと、この時点では先に立ち上げたplayground1サービスのCloud9コンテナが c9-overlay-network ネットワークに参加していないので、nginxからplayground1_cloud9の名前解決ができないのだ。じゃあどうすればできるかというと、とてもシンプルで
$ docker service update --network-add c9-overlay-network playground1_cloud9 playground1_cloud9 overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service converged
のように、ネットワークに参加させたらよい。リロードしたら・・・
キタ━━━━(゚∀゚)━━━━!!
キタ━━━━━━━━━━(゚∀゚)━━━━━━━━━━!!
となるだろう。
Cloud9 PROもどきの運用方法
構築手順はいろいろあったが、一旦作ってしまえばあとは簡単で、
新規のワークスペース立ち上げたいなーって思ったら
c9-masterに入って
docker stack deploy --compose-file docker-compose.yml nanika_new_idea docker service update --network-add c9-overlay-network nanika_new_idea_cloud9
( nanika_new_idea
のところは都度変える)
と2つコマンドを打てば、1分もたたないうちに
http://nanika_new_idea.ide.mydomain.com/
でCloud9 IDEにアクセスできる- Cloud9上で8080番ポートでHTTPサーバーを立ち上げれば
http://nanika_new_idea.preview.mydomain.com/
で見れる
ようになる。
ワークスペースを捨てたいなーって思ったら
c9-masterに入って
$ docker service ps playground1_cloud9 | grep Running | awk '{print $4}' c9-slave1
のように、Cloud9を実行しているノードを特定してから、
$ docker stack rm playground1 Removing service playground1_cloud9 Removing network playground1_default $ DOCKER_HOST=ssh://c9-slave1 docker volume rm playground1_workspace-data playground1_workspace-data
って感じで、2つコマンドを打てば瞬時に消せる。
まとめ
ほぼほぼ Docker swarmで作る社内heroku: yadockeri - Qiita なので、何も新しいことはやっていない。
とりあえず自分用Cloud9ができたので、しばらくは楽しくエンジニアやっていけそう。
追記
この構成だとChromebookでサクサク使えないことがわかったので続き・・・