ログ日記

作業ログと日記とメモ

ローカルでDocker を使って GitLab CIを設定するのは鬼門だった

普通に公開用のドメインでやるより相当ハードルが高かった。というか一部動かず解決しなかった。

  • docker-compose.yml の名前で解決できることと出来ないことがある
    • CIでDockerを走らせる場合は、(GitLabを立ち上げる docker-compose.ymlではなく)GitLab Runner の設定でextra_hosts を設定する必要がある
    • GitLab Runner がプライベートのDocker Registry からイメージを取ってくるとき、名前解決できる必要がある
      • GitLabの/etc/hosts も GitLab Runner の/etc/hosts も GitLab Runner が立ち上げた /etc/hosts も見てくれない? 名前解決用のサーバー的な何かを別途追加する必要があるかもしれない(未解決)
      • なんか GitLab Runner がDockerを起動するための golang が /etc/hosts を見ないとか、 /etc/nsswitch.conf を設定しろとか issue があったけど、うまく設定できず
  • 各所でSSLが必須
    • Let's Encrypt で適当なサブドメインを作って証明書を持ってくると解決できる
    • https-portal とか ansible とか使ってほとんど自動化できるけど、簡単に自動化しすぎると今度は Let's Encrypt の Rate Limit に引っかかったりする。
    • サブドメインを多く使う場合は、Let's Encryptはサーバーに直接入れて複数ドメインのSANs証明書を手動で取るのが良いかもしれない。

golang の名前解決方針とかGitLab Runner の仕組みとか知りたいわけではなくて、単にローカルでCIのテストをしたいだけなんだけど…全然たどりつかない。ということで一旦諦めて普通に名前解決できるドメインでやった。


GitLab を Docker で入れて、Docker Registry を Dockerで入れて、連携するネットワークの部分を書いて、設定ファイルを上手く連動するようにやって、SSLなどでエラーが起きないようにして、などなど。
もしかすると普通に apt-get で GitLab Omnibus を入れたら一瞬で解決したのかもしれない。
確かにDockerを使えば単品の構築は楽で一瞬で立ち上がるが、それぞれの連携がつらい。エラーが見えづらい。オールインワンパッケージがあるなら、それを入れるのが良さそう。


プロダクション環境でDockerを使うためには、今GitLabで苦労したようなことを自前システムでもやるってことだよねえ…。
まあ最初からDockerで使うように作る場合とパッケージ提供ベースをDocker対応するのとでは全然違うのかもしれないが。

ローカル仮想マシンのDockerでも簡単にLet's Encryptを使いたい

今まではLet's Encryptで local.example.com とかのグローバルサドメインを設定して、それをrsyncで持ってきてローカル用に使ってた。
これを自動化する。

test.example.comドメインに対する test.local.example.com は空でいい。

version: '3'
services:
  proxy:
    image: steveltn/https-portal:1.4.2
    ports:
      - '80:80'
      - '443:443'
    restart: always
    environment:
      DOMAINS: >-
        test:example@test.example.com #production,
        test:example@test.local.example.com #production
    volumes:
      - '/srv/https-portal/certs:/var/lib/https-portal'
      - '/srv/https-portal/vhosts:/var/www/vhosts'

これをテストサーバーで立ち上げ。


ansibleのタスク

---
- name: copy ssh keys
  copy:
    src: "~/.ssh/{{ item }}"
    dest: "/root/.ssh/{{ item }}"
    mode: 0600
  with_items:
    - id_rsa
    - id_rsa.pub
  when: inventory_hostname == 'test.local.example.com'

- name: debug
  debug: "msg={{inventory_hostname}}"

- name: rsync certs from remote local
  synchronize:
    src: /srv/https-portal/certs/
    dest: /srv/https-portal/certs/
    mode: pull
    recursive: yes
  when: inventory_hostname == 'test.example.com'
  delegate_to: test.local.example.com

inventoryにipやポートを書いて

ansible-playbook -i local -u root -v playbook.yml
ansible-playbook -i remote -u root -v playbook.yml

既にsshの公開鍵があり local => remote の sshが通るなら上側は不要。
synchronize の pull を delegate して実行する。(ややこしい)


docker-compose up -d の前にこのplaybookを実行すれば、証明書が更新されている場合はダウンロードしてくる。(予定)


最初の docker-composeの

        test:example@test.example.com #production,

の行を削除して、ローカルでもproductionで立ち上げる。linkとかあるなら test.local.example.com の方に移植する。

/etc/hosts のドメイン省略形は後ろに書かなければならない

初めて知った。というか前に書いて不具合が出た。
何となく習慣で

127.0.0.1	host1.example.com	host1

って書いてたけど、Ansibleで設定していたときに

127.0.0.1	host1	host1.example.com

と書いていた。
そうすると hostname -f で正しくドメインが引けない。


debian/Ubuntuの/etc/hostnameにはFQDNではなくshort nameを書く | Nekoya press
Chapter 3. The system initialization

/etc/hostname にドメインまで全部書いてしまうと、今度は(-fなしの) hostname でドメインまで出てしまう。
hostnameコマンドでチェックしない限り、普段使ってる分には特に問題は起きないので気付かなかった。


いやでも man hosts で

              IPアドレス 正式なホスト名 [エイリアス...]

       エントリーのフィールドは、空白またはタブ (複数でも可) で区切られる。 "#" 文字から行末までのテキストはコメ
       ントとして無視される。 ホスト名は英数字、 マイナス記号 ("-")、 ピリオド (".") を含むことができる。 ホスト
       名は英文字  (alphabetic character) で始まって、 英数字 (alphanumeric characte) で終わらなければならない。
       エイリアスはオプションであり、名前の変更、別のスペル、   短いホスト名、一般的に使われるホスト名   (例えば
       localhost) などのために用意されている。

正式なホスト名はhost1で、host1.example.comドメイン名込みのエイリアスとも考えられるから、ちょっと意味が分からない。



最近manコマンドをよく使う。

man systemd.netdev
man systemd.link
man systemd-hostnamed.service

systemdの情報ってブログとかには全然なかったり不完全だったりするので、man見た方が早かったりする。

さくらクラウドでbrctlなどでeth0にブリッジする(失敗)

[追記]
下の設定は意味がなかった。


ブリッジに新しいNICを接続するとき、やはり別のMACアドレスが必要になる。
でもそのMACアドレスでは外に出れないので、ローカルネットワークと同じような状態になって意味がない…。

ブリッジに接続する新しいNICまで同じMACアドレスにするのは無理だし、結局仮想を動かすにはルーティングするかNIC共有するかのどちらかになるっぽい。
[/追記]


新しくbr0を作ってそこにeth0を繋げても動かなかったのでメモ。


manual.sakura.ad.jp
MACアドレスが固定らしい。


ブリッジ接続を新しく作ると、eth0のIPが消えてbr0にIPを設定する。
このときbr0は新しくMACアドレスが作られるので、それで通信できていない?


eth0と同じMACアドレスにすると通信できた。
同じMACアドレスでいいのかは分からないが…。
serverfault.com
KVMの場合だけど、ブリッジだと問題なさそうというか同じにするべきだというふうに読める。


unix.stackexchange.com
この辺の回答とコメントを読んだけどよく分からない。


bridge - wlan same mac adress - loop - MikroTik
こっちでは問題が起きてるっぽいけど、Maybe a bug? と言われている。


ネットワークの問題というよりもLinuxの仮想NIC実装の問題だろうか。
ネットワークのループは実機でも仮想でもやったことあるけど、その場合すぐ制御不能になるので今動いているということは大丈夫なんだろう。たぶん。

debian9 の systemd-nspawnでdockerを動かす準備

デフォルトの設定では動かなかったので調べる。
https://wiki.archlinux.org/index.php/systemd-nspawn#Run_docker_in_systemd-nspawn


権限の設定が必要な模様。
それでもエラーが出たので、systemd-nspawnコマンドで起動してみる。
SystemCallFilterは --system-call-filter のオプションなんだろうなと思って検索しつつ。


https://man.kusakata.com/man/systemd-nspawn.1.html
オプションはある模様。
だけどdebian stretchでは認識されてない。



https://github.com/systemd/systemd/issues/5163
比較的最近の機能のようで、stretch-backports を探してみる。


https://packages.debian.org/stretch-backports/systemd-container
あった。このバージョンに上げたら使えるんだろうか。
apt-get -t stretch-backports で入れたり、戻したりした。
ダウングレードは

apt-get install systemd-container=232-25+deb9u4 \
  libnss-mymachines=232-25+deb9u4 \
  systemd=232-25+deb9u4 libsystemd0=232-25+deb9u4

のようにstretch-backportsでバージョンアップされた依存関係のあるものを個別にバージョン指定する必要がある?


docker run hello-world が動いたので
https://docs.ansible.com/ansible/latest/modules/apt_module.html
ansible の aptでdefault_releaseを指定してインストール。


これで docker in systemd-nspawn 完了。
あとはdocker-machineなりsshなりして systemd-nspawnのコンテナの中からdockerコンテナを立ち上げられるようになる。

VagrantとAnsibleを使って仮想マシンでsystemd-nspawnを使えるようにする

https://github.com/nishimura/vagrant-systemd-nspawn.git
ここに置いた。


Macでの環境。

Vagrant 2.0.1
ansible 2.6.4
VirtualBox 5.1.28
VMware Fusion 10.1.3


以前使ったときに

vagrant plugin install vagrant-hostsupdater
vagrant plugin install vagrant-vbguest

vagrant plugin install vagrant-persistent-storage

は実行済みとして。
固定環境で立ち上げるゲストが決まっているならhostsupdaterは入れずに手動でhost編集した方が楽かもしれない。


git clone https://github.com/nishimura/vagrant-systemd-nspawn.git host-debian9
cd host-debian9

source ./myenv-example-hostonly

cp ~/.ssh/id_rsa.pub data/authorized_keys

vagrant up


ansible-playbook -i playbook/env/hostonly/ -u root -v playbook/container-host.yml

ホストのネットワーク設定とゲストを(exampleの設定なら)三台作る。
run debootstrap のところでしばらく待つ。普通にコマンド実行したら逐次表示は出来ない…?


他のネットワークにブリッジする場合は myenv-example-vmware と playbook/env/vmware-bridge のような感じで。
VirtualBoxVMwareを繋ぐ場合、初期値でいいなら

cp ~/.ssh/id_rsa.pub data/authorized_keys
source myenv-example-vmware
vagrant up
ansible-playbook -i playbook/env/vmware-bridge/ -u root -v playbook/container-host.yml

これですぐ動くはず。


host01という名前を変えるなら

  • myenv*
  • playbook/env/*/inventory

を変える。



myenv-example-hostonly の初期値のままなら、 host01.local.test というVagrantのゲストOSをsystemd-nspawnのホストとして作り、その中に guest01.local.test、(guest02, guest03) を作る。
IP は 192.168.1.3 がホストで、末尾 11, 12, 13 がゲスト。
ホストオンリーネットワークの中でブリッジ接続していてMacから直接

ssh root@192.168.1.3
ssh root@192.168.1.11

などで入れる。


systemd-nspawnのゲストOSを入れる領域は別途ディスクを追加して Btrfs でフォーマットしている。


何度も実行しているとよく忘れるんだけど、authorized_keysをコピーし忘れてエラーになった場合は、dataに置いてから

vagrant provision

で該当箇所だけ実行。停止やリスタートの必要は無い。


ゲストを増やす場合はenvの中の変数 guests を増やして

ansible-playbook -i playbook/env/vmware-bridge/ -u root -v playbook/container-host.yml --tags=guests

を実行。差分だけ反映された感じのログが出ていればOK。
rootで入っているので所々で警告は出る。


初期状態のOSなので、何かアプリを入れたりするときは別途でそれ用のAnsible設定を使えばいいかなという感じ。




自分の場合は作業用PCがDebianなので、Vagrantの部分を飛ばして

source myenv-example-localvm
cp ~/.ssh/id_rsa.pub data/authorized_keys
ansible-playbook -i playbook/env/localvm -u root -v playbook/container-host.yml

という感じでssh root@localhost で入って同様の操作をしている。
ホストの /etc/hosts は変更しないけど /etc/systemd/network は変わるので、既にsystemd-networkd で何かしら設定をしている場合は消えないように現状の設定に合わせる必要がある。




Vagrantvmごと何度も消したり立ち上げたりするとめっちゃ時間がかかったので、仮想マシンからコンテナに移る流れは分かるような気がした。
あとネットワーク設定にハマるとなかなか抜け出せない。systemdに変わってるから予備知識ゼロだったしね…。



参考:
systemd-networkd - ArchWiki


qiita.com
qiita.com



9/27 追記:

guests:
  - { name: guest01, ip: 192.168.1.11/24 }
  - { name: guest02, ip: 192.168.1.12/24 }
  - { name: guest03, ip: 192.168.1.13/24 }

こういう変数があったとして、このセットがVagrant用、VMware用、ローカル用、みたいな感じで複数ある。
大抵の場合は変更しないのでデフォルト値として共有したい。

これを

guests:
  - { name: web, ip: 192.168.1.11/24 }
  - { name: app, ip: 192.168.1.12/24 }
  - { name: db, ip: 192.168.1.13/24 }

設定が進んでくるとこのセットを全て変えたりする。
このとき、IPは同じなので

guests:
  - { name: web }
  - { name: app }
  - { name: db }

名前だけ変えたい。


また、Vagrant用とVMware用の設定が異なったとして、このときは名前が同じなので

guests:
  - { ip: 192.168.11.11/24 }
  - { ip: 192.168.11.12/24 }
  - { ip: 192.168.11.13/24 }

ipだけ変えたい。


それから、既存環境と干渉するってときに

guests:
  - - { ip: 192.168.11.111/24 }
  - 

一部だけ変えたい。


変えた後も、個別設定した箇所以外は共通設定の変更が反映されるようにしたい。
プログラムならすぐなんだけどAnsibleではどうやるのか全然思い付かなくて今迷ってる。
group_varsに書くのかinventoryに書くのかincludeで読むのか、また設定ファイルの中の配列や連想配列の一部だけ書き換えるのはどうすればいいのか、わからん…。

CourseraのGoogleのやつを一つやった

www.coursera.org

すぐできるかと思ったらめっちゃ時間かかった…。
7〜8時間のうち休憩100分程度、一週間分と考えたらこんなもんなのか。


最初は律儀にビデオ見てたけど、しんどいから字幕を翻訳して読みつつ、ビデオはスライドのようにして見てた。
実際にGCPにログインして操作出来るのは良い。良いが…めっちゃ遅い。
ちょっとした操作に1分以上とか平気で待つ。その間にちょっと離席してたらタイムアウトでセッション切れるし、コマンドの途中で切れたりしたらなんか中途半端になったから最初からやりなおしたりしたし。
あとなんか指定通りやってもエラーになることがあった。

普段使いのデスクトップPCがDebian Stretchで、GCPも同じだったので他の人よりはやりやすかったと思うのだが。



最初の方のテストはビデオ見なくてもわかったから飛ばして最後の方のテストだけやってみたらほとんど間違えたりした。
なので大人しく順番に。
明日もう一度やったら半分くらい忘れてそう。



元々e-learningとか好きじゃなくて、ビデオなら更にゆっくりペースになるので完全に向いてなかった。
先に手を動かしてから後で解説読んで、もう一度手を動かすのが自分的なベスト。

あとよく考えたら当然だけれどクラウドとか機械学習とかの一般的なことじゃなくてGoogleサービスについてだった。
一つ終わって、ふーんって感じで、身に付いた感はまったくない。普通の講義ってこんなんだったか。
あと Qwiklabs の登録、新規でgmailで登録しようとしたら既に居ますってエラーになってGoogleログインでやった。30クレジットないんだけどどうなってるんだろう。
残りのコースをどうするか…。