- 投稿日:2019-12-03T23:37:27+09:00
docker-composeでさくっとhttpsなWordPressを公開する
背景
この度、知り合いにホームページ作成を依頼されました。制作期間が仕事の片手間で1週間しかなく、ほぼ個人サイトということもあり、(ついでに言うとただ働きなので)ここはdocker-composeでさくっと仕上げようと考えました。一応公開するので、Let's Encryptでhttps化も実施しました。また、ついでだから自分用のサイトも公開してしまおうと思い、複数サイト公開可能な構成にしました。
今更なトピックではありますが、自分用の備忘録として手順を残したいと思います。準備
環境
今回は下記の環境で構築しました。
種別 利用環境 バージョン サーバ さくらVPS v4 ドメイン取得 お名前.com - SSL証明書 Let's Encrypt - サーバOS ubuntu 16.04 クライアントOS macOS Catalina 10.15 docker - 18.09.7 docker-compose - 1.22.0 前提条件
- OSのインストールされたサーバが起動済みであること
- 取得したドメインがDNSレコードに登録されていること
構築手順
大まかには、下記の手順で構築していきます。
1. ファイアウォールの設定
2. FTPの導入
3. dockerおよびdocker-composeのインストール
4. 共有コンテナのディレクトリとファイルの作成
5. サイト毎のディレクトリとファイルの作成
6. dockerネットワークの作成
7. アップロードサイズ制限の変更ファイル作成
8. コンテナの起動
9. 2つ目以降のサイト立ち上げなお、以降の解説では、サーバのアカウントをubuntu、サイト1のドメインをexample.comとして記述していきます。
ディレクトリ構成は下記のように、
shareというディレクトリにプロキシなどの共通で利用するコンテナのファイルを、
同一ディレクトリにそれぞれのサイト名で、サイト毎のコンテナのファイルをまとめます。
~/public_html
┣share # リバプロ等、各サイトで共通のコンテナ用ディレクトリ
┣ サイト1 # 1つ目のサイト
┣ サイト2 # 2つ目のサイト
┣ ...また、WordPressの管理画面からアップロードファイルサイズ制限の変更をしておきます。
デフォルトの2MBでは、ほとんどのテーマがアップロードできないと思うので。1. ファイアウォールの設定
ターミナルからSSH接続して、ファイアウォールを設定します。
例では80番やFTP関連も開けていますが、必要に応じて取捨選択してください。# SSHホストキーを一度作成している場合は、接続する前に削除する $ ssh-keygen -R example.com # ssh接続 $ ssh ユーザ名@example.com # ファイアウォールの設定と必要なポートの通信許可 $ sudo ufw enable $ sudo ufw allow 80 $ sudo ufw allow 443 $ sudo ufw allow 22 $ sudo ufw allow 21 $ sudo ufw allow 20 $ sudo ufw reload # ステータスの確認 $ sudo ufw status To Action From -- ------ ---- 80 ALLOW Anywhere 443 ALLOW Anywhere 22 ALLOW Anywhere 20 ALLOW Anywhere 21 ALLOW Anywhere 80 (v6) ALLOW Anywhere (v6) 443 (v6) ALLOW Anywhere (v6) 22 (v6) ALLOW Anywhere (v6) 20 (v6) ALLOW Anywhere (v6) 21 (v6) ALLOW Anywhere (v6)2. FTPの導入
続いてFTPを導入します。手動でバックアップを取るとか、
プラグインをインストールする予定がないのであれば飛ばしてしまって良いと思います。# パッケージの更新 $ sudo apt update $ sudo apt upgrade # vsftpdの導入 $ sudo apt install vsftpd -y # 設定ファイルを編集する $ sudo vi /etc/vsftpd.conf #ファイル内から以降の設定記述箇所を探してコメントアウト、編集する # 匿名ユーザのアクセス拒否 anonymous_enable=NO # ファイルシステム変更コマンド許可 write_enable=YES # アスキーモードのアップロード許可 ascii_upload_enable=YES # アスキーモードのダウンロード許可 ascii_download_enable=YES # 設定したディレクトリより上層への移動を禁止する chroot_local_user=YES # リストファイルに記述されたユーザはchrootの対象から除外する chroot_list_enable=YES # リストファイルの場所を指定 chroot_list_file=/etc/vsftpd.chroot_list # ディレクトリの一括アップロード・ダウンロードを許可 ls_recurse_enable=YES # seccomp filterオフを追記 seccomp_sandbox=NO # リストファイルの作成 $ sudo vi /etc/vsftpd.chroot_list # 必要なユーザを記述する ubuntu # サービスのリスタート $ sudo service vsftpd restart3. dockerおよびdocker-composeのインストール
docker-composeはリポジトリからインストールします。
$ sudo apt-get update $ sudo apt-get upgrade $ sudo apt install docker.io $ sudo apt-get install curl $ sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # docker-composeコマンドを利用できるようにする $ sudo chmod +x /usr/local/bin/docker-compose4. 共有コンテナのディレクトリとファイルの作成
まずディレクトリを作成し、その中にdocker-compose.ymlを作成します。
MySQL、NginxのリバースプロキシとLet's encryptの証明書を取得、更新してくれるコンテナを立ち上げるようにします。$ sudo mkdir share $ cd share $ sudo vi docker-compose.ymldocker-compose.ymlの内容は下記の通りです。
public_html/share/docker-compose.ymlversion: "2" services: mysql: image: mysql:5.7 container_name: mysql ports: - "3306:3306" volumes: - ./db/mysql_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: password restart: always nginx-proxy: image: jwilder/nginx-proxy container_name: nginx-proxy privileged: true ports: - 80:80 - 443:443 volumes: - ./docker-compose.d/certs:/etc/nginx/certs:ro - ./docker-compose.d/htpasswd:/etc/nginx/htpasswd - ./conf.d:/etc/nginx/conf.d - /etc/nginx/vhost.d - /usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro restart: always letsencrypt-nginx: image: jrcs/letsencrypt-nginx-proxy-companion container_name: letsencrypt-nginx privileged: true volumes: - ./docker-compose.d/certs:/etc/nginx/certs:rw - /var/run/docker.sock:/var/run/docker.sock:ro volumes_from: - nginx-proxy restart: always networks: default: external: name: shared5. サイト毎のディレクトリとファイルの作成
続いてサイト毎のディレクトリとdocker-composeを作成します。
$ cd .. $ sudo mkdir example $ cd example $ sudo vi docker-compose.ymldocker-compose.ymlは以下の通りに作成します。
public_html/example/docker-compose.ymlversion: "3" services: wordpress: image: wordpress:latest container_name: example ports: - 9000:80 volumes: - /home/ubuntu/public_html/example/html:/var/www/html - ./conf.d/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini environment: VIRTUAL_HOST: example.com # サイトのドメイン VIRTUAL_PORT: 9000 LETSENCRYPT_HOST: example.com # サイトのドメイン LETSENCRYPT_EMAIL: account@mail # 証明書を取得するためのアドレス LETSENCRYPT_TEST: "false" WORDPRESS_DB_PASSWORD: password # MySQLパスワード WORDPRESS_DB_NAME: example.com # 任意のDB名 external_links: - mysql restart: always networks: default: external: name: shared6. dockerネットワークの作成
以下のコマンドを実行して、dockerネットワークを作成します。
$ docker network create --driver bridge shared7. アップロードサイズ制限の変更ファイル作成
WordPressの管理画面からアップロードできるテーマやメディアファイルのサイズ制限を変更します。
共有コンテナディレクトリおよびサイト毎の「conf.d」ディレクトリを作成して、その中に設定ファイルを追加します。
今回は20MBにしておきます。$ sudo mkdir share/conf.d $ sudo vi share/conf.d/max-size.conf $ sudo mkdir example/conf.d $ sudo vi example/conf.d/uploads.iniそれぞれのファイルの内容は以下の通りです。
share/conf.d/max-size.confclient_max_body_size 20M;example/conf.d/uploads.iniupload_max_filesize = 20M; post_max_size = 20M;8. コンテナの起動
$ cd ../share $ sudo docker-compose up -d $ cd ../example $ sudo docker-compose up -d上記コマンドを実行した後に、https://example.comにアクセスすると、WordPressインストール画面が開きます。
9. 2つ目以降のサイト立ち上げ
今回の構成だと、1つのMySQLに複数サイトのDBを作成して運用することになります。
この場合、WordPressは1つ名のサイトのデータベースは作成してくれますが、
2つ目以降のデータベースは作成してくれないので、インストール前に自分でデータベースを作成する必要があります。
以下の手順でデータベースを作成してください。# MySQLコンテナにログイン $ sudo docker exec -it mysql /bin/bash # データベースの作成 $ mysql -u root -p mysql> CREATE DATABASE site2上記で作成したデータベースに合わせて、「5. サイト毎のディレクトリとファイルの作成」の手順と同じように
2つ目のサイトのディレクトリとdocker-compose.ymlを作成してコンテナを起動すると、
1つ目のサイトと同じように、WordPressインストールサイトが開けます。あとがき
以上、細かい説明は省いて書いてしまいました。
それぞれの手順で細かく設定したい場合は、専門の記事を探していただければと思います。今回はWordPressを立ち上げましたが、違うイメージからコンテナを起動すれば他のサービスも立ち上げられると思います。
- 投稿日:2019-12-03T23:30:30+09:00
symfony/mailerで試行錯誤したこと
Symfony Advent Calendar 2019 5日目の記事です。
昨日は@77webさんの Symfonyで再利用可能なバンドルのコントローラをテストする方法 でした!はじめに
Symfony 4.4.0は11/21にリリースだったのですね。
私は、その翌日の、11/22からSymfonyを初めました。
LTSだと長くメンテされるし、安定しているだろうと考えて飛びつきましたが、Symfony/Mailerは4.3系から導入されたので、まだまだ成熟には遠く、またノウハウも蓄積されていません。更に検索結果も前任の「swiftmailer」しか出てこないので、試行錯誤を致しました。
参考にしていただければ幸いです。
sendmailで送信する方法
TL;DR
まず、はじめに困ったのがsendmailでの送信方法。
結論を先に書くと、DSNで以下の指定をすると送信ができます。.envMAILER_DSN=sendmail+smtp://default または MAILER_DSN=sendmail://defaultただし、後述しますが、少し困ったちゃんなのです。
sendmailのDSN調査
「Transport Setup」でSMTPやサードパーティの「Amazon SES」、「Gmail」、「Sendgrid」などの設定方法は書いてありますが、「sendmail」はないのです。
未対応なのかな?とソースを確認すると、「SendmailTransportFactory.php」と「SendmailTransport.php」が存在します。
「SendmailTransportFactory.phpの中にsendmailのスキーマの記載がありました!
SendmailTransportFactory.phpfinal class SendmailTransportFactory extends AbstractTransportFactory { ~~~省略~~~ protected function getSupportedSchemes(): array { return ['sendmail', 'sendmail+smtp']; } }dockerの開発環境で送信ができない。。。
これでsendmail用のDSNがわかったので、「sendmailでメール送信ができる!」とワクワクして、「Creating & Sending Messages」のサンプルコードを実行しました。
Connection to "process /usr/sbin/sendmail -bs" has been closed unexpectedly.
どうやら
sendmail -bs
でないと送信ができないようです。
確認した環境は、docker上のAlpine+ssmtpなのでsendmail -t
には対応していますが、sendmail -bs
には未対応なのです。postfixを入れたコンテナ作るのも面倒ですしね。
レンタルサーバで確認 & 困ったことが。。。
面倒ですが、サンプルコードをさくらのレンタルサーバにアップして確認をしてみました。
メールの構造は以下のような感じです。
Delivered-To: fuga@gmail.com Received: by 2002:a5d:841a:0:0:0:0:0 with SMTP id i26csp4330355ion; Mon, 25 Nov 2019 22:05:37 -0800 (PST) ~~~中略~~~ Return-Path: <hoge@example.com> Received: from www1970.sakura.ne.jp (localhost [127.0.0.1]) by www1970.sakura.ne.jp (8.15.2/8.15.2) with ESMTP id xAQ65aic007417 for <fuga@gmail.com>; Tue, 26 Nov 2019 15:05:36 +0900 (JST) (envelope-from hoge@example.com) Received: from [127.0.0.1] (hoge@localhost) by www1970.sakura.ne.jp (8.15.2/8.15.2/Submit) with SMTP id xAQ65aQW007416 for <fuga@gmail.com>; Tue, 26 Nov 2019 15:05:36 +0900 (JST) (envelope-from hoge@example.com) X-Authentication-Warning: www1970.sakura.ne.jp: hoge owned process doing -bs From: hoge@example.com To: fuga@gmail.com Subject: Time for Symfony Mailer! Message-ID: <0ab330134ce12286539d0ae3112374a4@example.com> MIME-Version: 1.0 Date: Tue, 26 Nov 2019 15:05:36 +0900 Content-Type: multipart/alternative; boundary="_=_symfony_1574748336_057222644d28500198a452dc1b84b1b1_=_" --_=_symfony_1574748336_057222644d28500198a452dc1b84b1b1_=_ Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Sending emails is fun again! --_=_symfony_1574748336_057222644d28500198a452dc1b84b1b1_=_ Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable <p>See Twig integration for better HTML integration!</p> --_=_symfony_1574748336_057222644d28500198a452dc1b84b1b1_=_--よくよく見ると、ヘッダーに警告が追加されています。
X-Authentication-Warning: www1970.sakura.ne.jp: hoge owned process doing -bs
こちらをググると、
「メールヘッダに X-Authentication-Warning: が付かないようにする」が詳しく紹介されています。MTAの設定を変更するというのは、できれば避けたいところですね。。。
sendmail -t
で送信する(ペンディング)さきほどDSNを調査している時に、
「SendmailTransport.php」で、sendmail -bs
をsendmail -t
に変更する記載があったことを思い出しました。SendmailTransport.phpclass SendmailTransport extends AbstractTransport { private $command = '/usr/sbin/sendmail -bs'; private $stream; private $transport; /** * Constructor. * * If using -t mode you are strongly advised to include -oi or -i in the flags. * For example: /usr/sbin/sendmail -oi -t * -f<sender> flag will be appended automatically if one is not present. * * The recommended mode is "-bs" since it is interactive and failure notifications are hence possible. */ public function __construct(string $command = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) { parent::__construct($dispatcher, $logger); if (null !== $command) { if (false === strpos($command, ' -bs') && false === strpos($command, ' -t')) { throw new \InvalidArgumentException(sprintf('Unsupported sendmail command flags "%s"; must be one of "-bs" or "-t" but can include additional flags.', $command)); } $this->command = $command; } $this->stream = new ProcessStream(); if (false !== strpos($this->command, ' -bs')) { $this->stream->setCommand($this->command); $this->transport = new SmtpTransport($this->stream, $dispatcher, $logger); } }コンストラクタの第1引数の
$command
に、'/usr/sbin/sendmail -oi -t'
を指定できれば実現できそうです。しかし、呼び出し元を見ると無情にもNULL指定でした><
SendmailTransportFactory.phpfinal class SendmailTransportFactory extends AbstractTransportFactory { public function create(Dsn $dsn): TransportInterface { if ('sendmail+smtp' === $dsn->getScheme() || 'sendmail' === $dsn->getScheme()) { ここ!! => return new SendmailTransport(null, $this->dispatcher, $this->logger); } ~~省略~~ } }まだ、完全に使い方を理解していないDIでなんとかできないかな?と調べてみたり、SlackのSymfony Devsのsupportで質問してみましたが、無理そうです。
提案されたのは、「自分でクラスを拡張したらよいよ!」ということでした。
https://symfony-devs.slack.com/archives/C3EQ7S3MJ/p1574840970312800?thread_ts=1574811879.302500&cid=C3EQ7S3MJ最終手段で拡張しましょう。。。
後日、見つけましたが、
senmail -t
については、Issueも上がっていました。Feature指定でした;;
=> Mailer component: change sendmail commandSMTPで配信する
SMTP配信なら使えるということで、MailDevに接続を試みます。
本家がDocker Hubで公開していますし、日本語メールに対応されたバージョンもあります。
docker composeで構成したサービス名がmaildevなので、それを指定します。
.envMAILER_DSN=smtp://maildev「Creating & Sending Messages」のサンプルコードを実行して完了!と思ったら。。。
25ポートで接続をするはずのに、465ポートのSMTPSで接続しようとしています。。。
まだまだ、symfony/mailerには知らない秘密があるようです。ポート番号を指定してみました。
.envMAILER_DSN=smtp://maildev:25今度は、25番ポートに接続をするのですが、SSL通信をしようとして失敗しているようです。
ErrorExceptionをよく見ると、EsmtpTransport.php(112)でstartTLSを実行しています。
SMTPSは、SSL通信をするために465ポートを用意する必要があります。
しかし、465ポートを用意できない場合があるため、startTLS
は、通信をしているポートを利用して、平文から暗号化通信に切り替えるための仕組みのようです。
※STARTTLS by ウィキペディア該当のソースを確認をすると、
startTLS
を実行する条件がわかります。EsmtpTransport.php(112)protected function doHeloCommand(): void { ~~中略~~ /** @var SocketStream $stream */ $stream = $this->getStream(); if (!$stream->isTLS() && \defined('OPENSSL_VERSION_NUMBER') && \array_key_exists('STARTTLS', $capabilities)) { $this->executeCommand("STARTTLS\r\n", [220]); ~~中略~~ }まず、
startTLS
になるための1つ目の条件である!$stream->isTLS()
というのは、465ポートを指定していない場合になります。以下、EsmtpTransport.php(48)のコンストラクタの処理の抜粋になります。
EsmtpTransport.php(48)public function __construct(string $host = 'localhost', int $port = 0, bool $tls = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) { ~~~中略~~~ if (null === $tls) { if (465 === $port) { $tls = true; } else { $tls = \defined('OPENSSL_VERSION_NUMBER') && 0 === $port && 'localhost' !== $host; } } if (!$tls) { $stream->disableTls(); } if (0 === $port) { $port = $tls ? 465 : 25; }
$tls
はデフォルトNULLなので、ポート番号が465の場合に$tls
がTrueになります。また、ポート番号が465でない場合は、52行目のelseに入りますが、ポート番号が0でlocalhost以外の場合は、$tls
がTrueになります。話はかわりますが、はじめに、DSNで
MAILER_DSN=smtp://maildev
の場合は、465に接続されたのは、この判定のためですね。さて、2回目のDSNでは25番ポートを指定しましたので
$tls
はfalseになり、結果として、!$stream->isTLS()
となります。
startTLS
になる2つ目の条件は、OPENSSL_VERSION_NUMBER
のバージョンです。こちら最新の環境なら問題になることはないと思います。
startTLS
になる最後の判定は、\array_key_exists('STARTTLS', $capabilities)
になります。
$capabilities
は、EsmtpTransport.php(130)にありますが、通信対象のサーバ、今回はMailDevから送られてくるもののようです。EsmtpTransport.php(130)private function getCapabilities(string $ehloResponse): array { $capabilities = []; $lines = explode("\r\n", trim($ehloResponse)); array_shift($lines); foreach ($lines as $line) { if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) { $value = strtoupper(ltrim($matches[2], ' =')); $capabilities[strtoupper($matches[1])] = $value ? explode(' ', $value) : []; } } return $capabilities; }MailDevのSMTPサーバは、NodeMailerを使っているので、
startTLS
には対応しているようなのですが、MailDevはISSUE(Support for TLS (via ngrok or somehow?) )もあがっているのですが、対応をしてないようです。対応していないなら、対応していないで、
startTLS
が使えるなんてMailDevが言わなきゃいいのです。NodeMailerのオプションを見ていくと、
options.hideSTARTTLS
というコマンドがありました。MailDevの方にもオプションがありました!
docker-compose.ymlで、MailDevの起動オプションに、
--hide-extensions STARTTLS
を追加します。maildev: image: kanemu/maildev-with-iconv ports: - 8025:80 command: bin/maildev -w 80 -s 25 --hide-extensions STARTTLSこれで「Creating & Sending Messages」のサンプルコードを実行したら、無事に送信ができました!
最後に
HTMLメールを送信したいのですが、テキストを配信するだけでいろいろと躓いてしまいました。
枯れている技術のswiftmailerに浮気しようか悩み中。
でも、sendgridとか拡張性もあるので、将来性を買って、もう少し使ってみます。
- 投稿日:2019-12-03T21:15:43+09:00
手軽にPC上でチャットボットの開発環境を作る
創作意欲が湧き、チャットボットを新たに作ろうと思い立ち、まずは環境どうしようかと試行錯誤した結果、Botpressで構築してみることにしました。
探し始めるとBotkitが有名だったようですが調べたらMicrosoftに買収されてたんですね。
他にないかと探してみたら、Botpressにたどり着きました。詳細はこちらで
https://botpress.io/では、インストールをしていきましょう。
その前にDockerを動くようにしておいてください。
私はWindows10 Proで構築しています。PowerShell起動
Windows Terminalで起動してみました
BotpressのDockerコンテナを作成
>docker run --detach --name=botpress --publish 3000:3000 --volume botpress_data:/botpress/data botpress/server:latest
Unable to find image 'botpress/server:latest' locally
C:\Program Files\Docker\Docker\Resources\bin\docker.exe: Error response from daemon: manifest for botpress/server:latest not found: manifest unknown: manifest unknown.
See 'C:\Program Files\Docker\Docker\Resources\bin\docker.exe run --help'.あれ?エラーになる。。。
docker hubで探して最新のタグ名を取得
https://hub.docker.com/r/botpress/server/tagsdocker run --detach --name=botpress --publish 3000:3000 --volume botpress_data:/botpress/data botpress/server:2019-11-28-misunderstood
無事起動しました。
>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aec2835d2e79 botpress/server:2019-11-28-misunderstood "/bin/sh -c './duckl…" 11 minutes ago Up 11 minutes 0.0.0.0:3000->3000/tcp botpress起動も確認できました。
ブラウザでBotpressを呼び出す
ボットを作成してみる
動きました。
フローで視覚的に見れて、エミュレーターもあるので便利だと思いました。今回はここまで。
- 投稿日:2019-12-03T14:14:06+09:00
Traefik さえ使えば、nginx がリバースプロキシとして動くのは不要!
背景
同一のサーバ上で複数サービスをホストする際に普段 nginx または apache を使いましたが、設定を調整するたびに何を書いたかなかなか覚えられませんので。。
そこで、 Traefik を使えばいいです!
Traefik とは
公式 HP より:
An open-source reverse proxy and load balancer for HTTP and TCP-based applications that is easy, dynamic, automatic, fast, full-featured, production proven, provides metrics, and integrates with every major cluster technology... No wonder it's so popular!
Traefik は、HTTP・TCP アプリのリバースプロキシかつロードバランサです。いろんな技術と連携があり、どんなスタックでも簡単に導入することができます。
特に Docker との相性が良くて気に入り、この記事は Docker + Traefik を中心にしたいと思います。
Example
今回は、
myserver.test
という架空なドメインでブログと Wiki サービスを立ち上げたく、各サービスの詳細は以下のようです。
サービス アプリ URL Blog Ghost blog.myserver.test
Wiki DokuWiki wiki.myserver.test
ドメインを用意
実際に持っているドメインであればドメインの A レコードを追加し、なければ host ファイルに以下の項目を追加すればローカルで動いて試すことができます。
# /etc/hosts 127.0.0.1 blog.myserver.test 127.0.0.1 wiki.myserver.testDocker を用意
複数 Docker のコンテナを管理すると Docker Compose 便利なので、早速コンフィグファイルを作りました。
## docker-compose.yml version: '2' services: ghost: image: ghost:3 ports: - 5001:2368 volumes: - ./data/ghost:/var/lib/ghost/content dokuwiki: image: bitnami/dokuwiki:0 ports: - 5002:80 volumes: - ./data/dokuwiki:/bitnamiこれで
docker-composer up
を実行すると、 ブログは http://localhost:5001 からアクセスでき、Wiki の方は http://localhost:5002 からアクセスできます。Traefik を導入
せっかく docker を使っているため、 Traefik の Docker イメージを導入しましょう。
## docker-compose.yml services: # ghost,dokuwiki を省略 traefik: image: traefik:2.0 ports: - 5003:8080 # webUI - 80:80 volumes: - /var/run/docker.sock:/var/run/docker.sock - ./config/traefik/traefik.yml:/etc/traefik/traefik.ymlマウントする
volumes
ですが、Traefik の設定が定義されるファイル以外は、Docker API にアクセスできるためdocker.sock
もマウントすることが必要です。Traefik は、サービスのステータスを常に監視し、サービスが動いてない時はリバースプロキシを無効するという機能が付いています。Traefik を設定
volumes
で定義されたコンフィグファイルを作りましょう。# ./config/traefik/traefik.yml # Providers config providers: docker: {} # Docker との連携を有効 # API/Dashboard config api: insecure: true # WebUI にアクセスできるように設定Traefik のホスト名を各サービスに定義
最後に、各コンテナにホスト名、つまり URL を指定すれば完了です。
# docker-compose.yml ghost: labels: - "traefik.http.routers.ghost.rule=Host(`blog.myserver.test`)" dokuwiki: labels: - "traefik.http.routers.dokuwiki.rule=Host(`wiki.myserver.test`)"Traefik を起動
これで準備が整ったので、再度で
docker-compose up
を実行すると Traefik が起動されます。 http://localhost:5003 から WebUI を見れます。
blog.myserver.test
にアクセスするとこんな感じ:で
wiki.myserver.test
にアクセスするとこんな感じです。【おまけ】 HTTPS 化も楽勝!
Traefik では Let's Encrypt との連携もあり、
traefik.yml
に以下を定義すれば簡単に HTTPS を導入することができます。# ./config/traefik/traefik.yml entryPoints: web: address: ":80" web-secure: address: ":443" certificatesResolvers: sample: acme: email: webmaster@myserver.test storage: acme.json httpChallenge: entryPoint: web詳しくはドキュメンテーションをご参照ください。
まとめ
特に複雑なルールの設定が必要なく、シンプルなリバースプロキシが望ましい場合は、 Traefik を試す価値があるじゃないかと思います。
Reference
- 投稿日:2019-12-03T12:15:47+09:00
開発を加速させるためにDocker設定を見直した話をする
パーソンリンクアドベントカレンダー 3日目の投稿です。
はじめに
今担当しているプロジェクトでは、フルDockerの開発環境でRailsとVue.jsを使ったWebサービスを開発しています。
昔は個人個人で環境作ったりしていたこともあり、細かなライブラリのバージョン違いで起きるトラブルとか、そういった手順をまとめたりするためのコストとか、いろいろと大変なことも多かったりしました。それが今やDockerさえ入っていればコマンド一発で誰が動かしても同じ環境が作れる。それこそProductionでも大きな変更を入れることなく使えてしまう。大変便利になったものですね!
本日は、そんな便利なDocker開発開発で遭遇したつらみや、どうやって改善したかについて語って行きたいと思います(`・ω・´)
どういう課題があったか
自分が今のプロジェクトにJoinした時、すでにDockerを使った開発が実施されていました。
docker-compose.ymlに構成を書いて、それを使って開発が行われていました。構成的にはこんな感じですね。Nginxでリクエストを受けて、Rails側にproxyするやつです。あ、図には書き忘れたんですが、RailsはUnicornを使ってリクエストを受け付けるようにしています。まあよく見る構成だと思います。このプロジェクトっではAWSを使ってリリースする予定だったので開発時点からそれに合わせています。
さて、当初自分がJoinしたときは以下の用な問題がありまして、めちゃくちゃ辛いと言うものではないけれど地味に面倒な現象に見舞われてました。
- コンテナに直接入ってmigration, パッケージアップデート
- docker buildすると都度Gemのフルインストールしてしまう
- .envと重複しているenvironment
- 個人ごとにパスを書き直さないといけないdocker-sync
- まれにdbに接続できず落ちるappコンテナ
1つ1つは大したことないのですが、これが積み重なっていくととってもイライラします。
やってられるかー( ゚д゚)
という気持ちから、早めにこの問題を片付けることを誓ったのです。どう考えて実施したのか
本来、dockerの特性として「その時のサーバーの状態が固定されており、素早く同じ構成を量産できる」ことがあります。
軽量で高速に起動・停止できることがDockerの一番の強みだと考えていて、特に本番環境で同じ構成を一瞬で増やして早期にスケールできることを狙っているのだと考えています。完成した状態でイメージを作りあげるために柔軟性を捨てています。一方、開発環境は柔軟でなければいけません。追加され続けるmigration、都度増えていくライブラリ等。Dockerは状態を固定化するものなので、色々と工夫する必要が出てきます。
今回はBtoBで且つ計画メンテが前提のプロジェクトで、Dockerや、Kubernatesを採用する意味が薄かったこともあり、開発環境だけでDockerを使うことだけを考えられるのでシンプルでした。が、ProductionにDockerなりを採用しているところでは、Production用と開発用で異なるDockerfile, docker-composeをそれぞれ用意することになるかと思います。もし参考にするのであればそういう運用をオススメします。
どう解決していったのか
コンテナに直接入ってmigration, パッケージアップデート
都度コンテナの中に入って作業していました。
docker ps
でコンテナID探して、docker exec
で入る。そしてコマンド実行。...都度これをやるのは面倒なので、こんなMakefileを作成した上で、一つ構成を変更しました。
rails/bundle: docker-compose run --rm app bundle install rails/migrate: docker-compose run --rm app bundle exec rails db:create db:migrate yarn/install: docker-compose run --rm app yarn install
docker run --rm
で実行しています。docker exec
を使う方法も考えたんですが、この場合docker-composeをup済みにしていないと実行できません。なのでいつでも気軽に叩けるようにdocker run --rm
し、余計なコンテナを活かし続けないように、処理が終わったらすぐ落とすようにしています。docker buildすると都度Gemのフルインストールしてしまう
さて、
docker run --rm
なのでそのまま実行すると処理が終わった後はコンテナが消えてしまいます。そうすれば当然bundle installしたGemも居なくなってしまいます。これを防ぐため、docker-composeで以下の設定を追加しています。
volumes: bundle-gems: external: true app: environment: BUNDLE_PATH: /app/vendor/bundle volumes: - bundle-gems:/app/vendor/bundlebundle installで保存する先をボリュームコンテナに移してこれをマウントします。インストールしたGemはコンテナを消しても削除されず、ボリュームコンテナに残りっぱなしになります。Gemの入れ直しがないため、なにかの拍子で環境がおかしくなっても気軽にbuildを叩くことができます。
BUNDLE_PATHを指定しているのは、bundle install時に明示的にパスを指定しなくてもこの設定に合わせて配置してくれるからです。
Bundler: The best way to manage a Ruby application's gemsProductionで動かす場合は、Dockerfile側にBUNDLE_PATHを書くべきですが、今回は2箇所で管理したくなかったのでdocker-composeで書いてます。
こうして、docker buildが走っても都度フルインストールせずに済むようになりました。唯一例外があるとすればベースImageを変更した時くらいでしょうか。
このときはGemをビルドし直さないといけないのですが、ほぼ切り替えることはないので、手動でやることにしています。.envと重複しているenvironment
担当しているプロジェクトでは昔ながらの環境変数の管理をしており、.envが現役です。
もちろんバージョン管理はされていないファイルで管理していたのですが、ふとdocker-composeを見ると.envと同じ設定が存在していました。environment: DATABASE_USERNAME: 'xxxx' DATABASE_PASSWORD: 'xxxx' DATABASE_HOST: 'xxxx' RAILS_ENV: 'developemt' ...これがバージョン管理されている。ローカルの開発環境なので別に漏れても大した問題にはならないのですが、
- 本来隠すべき情報が乗ってしまっている
- 2箇所で同じ設定値が定義されている
が気になり、手をいれました。
docker-composeには、
env_file
というまさにこの.envを読み込むための設定があります。これを使うと、env_file: - .env environment: - '.envで定義していないやつ'と書け、.envで設定しているものは書かなくて良くなります。なお、.envでの設定値は、
environment
で上書きすることができるので、共通の.envを読みつつ、あるコンテナだけは設定を切り替えることができるようになります。実は、docker-compose.ymlと同じ階層の.envを読み取ってくれる機能があるため、今回のケースであれば設定不要です。が、明示的に読み込んでいることを示すためにあえて書くようにしています。
Declare default environment variables in file | Docker Documentationこれで重複している設定がなくなってスッキリし、秘匿情報も書かなくて良くなりました。
個人ごとにパスを書き直さないといけないdocker-sync
弊社では好きなPCをある程度選ぶことができるのですが、Mac率が高い会社です。
そのため、Dockerを使うときはdocker-syncを併用するようにしています。事情が色々あってコンテナ中のデータやnode_moduleをホスト側と同期する必要があり大量のファイルがやり取りされるので、ホストとコンテナのボリューム同期だと転送追いつかないのが理由です。さて、このdocker-syncですが、こう設定されているの見たことありませんか?
version: '2' options: max_attempt: 200 syncs: app-sync-volume: src: '~/workspace/app/' # <- これ sync_excludes: ['.git']sync対象のパスを直接書いているケース。これ結構イライラものでした。syncするディレクトリは当然個人個人でバラバラです。特に自分はghqを使ってgitリポジトリをcloneしているので独特のパスになっています。いちいち書き換えるのがめんどう(´・ω・`)
しかもこのファイルはgit管理されているので、個人でパスを変更しているといつまで経ってもdiffに残り続ける。実際間違ってコミットされて、コンフリクトが発生するという事態もおきていました。
こんなふうに設定を変更することで、個人毎のディレクトリ指定が不要になります。
version: '2' options: max_attempt: 200 project_root: 'config_path' # <- 追加 syncs: app-sync-volume: src: '.' # <- 変更 sync_excludes: ['.git']
project_root: 'config_path'
は、docker-sync.ymlのパスと同じものが入ります。docker-sync.ymlをリポジトリのルートディレクトリに配置しているので、src
にcurrent pathを設定するだけで済みます。Configuration — docker-sync 0.5.11 documentation
これでまた一つ面倒なことが減りました。
まれにdbに接続できず落ちるappコンテナ
問題が発生する時のdocker-compose.ymlはこんな設定をしていました。
db: ports: - '3306:3306' volumes: - ./containers_data/mysql:/var/lib/mysql app: command: bundle exec rm -f ./tmp/pids/unicorn.pid && bundle exec unicorn_rails -p 3000 -c config/unicorn.rb depends_on: - db ports: - "3000:3000" tty: true stdin_open: trueapp側にdbへの
depends_on
を設定してし、dbコンテナが立ち上がった後にappを立ち上げる設定になっています。まあよくあるやり方だと思います。実際、これはこのままでもうまく動きました。ちゃんとMySQLが立ち上がってからRailsが上がる。問題ない。ところが、まれにRailsとMySQLとの疎通が失敗して、コンテナが立ち上がらない現象が発生します。そのたびに一度止めて
docker-compose up
みたいな運用をしており、発生するたびにそういう手作業をやる必要に迫られました。
depends_on
では、対象のコンテナが起動できたか、しか見てくれません。そのため、何らかの要因でdbコンテナ中のMySQLの起動が遅延すると、Railsが動き出す時にDBへの疎通が取れずunicorn_railsで落ちる現象が発生してしまうのです。それを回避するため、appコンテナ中でdbコンテナのMySQLへの疎通をチェックできるようにしました。
db: ports: - '13306:3306' volumes: - ./containers_data/mysql:/var/lib/mysql app: command: sh ./containers/app/entrypoint.sh # <- 変更 depends_on: - db ports: - "3000:3000" tty: true stdin_open: true
command
にentrypoint.shを指定して、この中で疎通チェックをしています。#!/bin/bash set -e until mysqladmin ping -h db --silent; do >&2 echo "mysql wake up......" sleep 3 done >&2 echo "=== mysql < Hi, got up now. ====" bundle exec rm -f ./tmp/pids/unicorn.pid && bundle exec unicorn_rails -p 3000 -D -c config/unicorn.rb echo "=== start unicorn. pids: `cat ./tmp/pids/unicorn.pid` ====" bash3秒間隔でMySQLが起動しているかをチェックし、起動していたらunicornを起動する。
Railsが起動しているときは、かならずMySQLが起動済みなので、unicorn_railsで落ちる現象は発生しなくなりました。これでまた一つ心に平穏が訪れます。
どうなったのか?
- コンテナに直接入ってmigration, パッケージアップデート
- docker buildすると都度Gemのフルインストールしてしまう
- .envと重複しているenvironment
- 個人ごとにパスを書き直さないといけないdocker-sync
- まれにdbに接続できず落ちるappコンテナ
以上遭遇していた、ちょっと面倒なやつが消え去ったおかげで、開発のスピードを落とす要因がいなくなり、これまでよりストレスフリーで開発ができるようになりました(`・ω・´)
おわりに
普段開発に追われていると、ちょっとした面倒は放置されがちです。
優先しなければ行けないものがあると、こういった面倒は後回しにしてしまいます。
しかし、それが積もり重なっていくと、いつの間にか無視できない労力がかかる問題になってしまいます。面倒だなと思った時。それは開発時のちょっとした困りごとを倒す、またとない機会になるのだと思います。
明日のパーソンリンクアドベントカレンダーは、mgmgOmOさんのLookerのお話です。BIプラットフォーム構築している人の希望になりそうなLooker。その概要を説明してくれるとのことで期待です。
- 投稿日:2019-12-03T10:54:50+09:00
SSH 接続できる Amazon Linux 2 の Docker イメージを作成してシステム構築してみる
はじめに
オークファンでは多くのシステムを AWS で稼働しており、OS は特別な理由がない限り以下の理由で Amazon Linux 2 を選択しています。
- AWS が公式にサポートしている
- セキュリティアップデートやメンテナンスアップデートが継続的に提供される
- 追加料金なし
Amazon Linux 2 は RedHat ベースですが本家とは違っている部分もあるので、システム構築のための各種コマンドを試したくなる場面があります。そのたびにコスト管理をしてくれているインフラグループの顔色を伺いながら実際の AWS 環境に EC2 インスタンスを立ち上げるのも申し訳ないので、開発 PC の Docker 上に実際の EC2 と近い状態の Amazon Linux 2 を起動して動作検証を実施しています。
Docker イメージの作成
まずはベースとなる EC2 と近い状態の Docker イメージを作成します。
Amazon Linux 2 の Docker イメージは 公式の Docker Hub に公開されているのですが、まっさらな状態なので必要なものをインストールします。設定の大枠は以下となっています。
- 必要なパッケージをインストール
- SSH サーバを起動するように設定
ec2-user
を作成具体的なディレクトリ構成はここでは以下とします。あとで SSH 接続するための公開鍵 (ここでは
id_rsa.pub
としています) をDockerfile
と同じ階層に配置しています。amazonlinux2 ├── Dockerfile └── id_rsa.pub
Dockerfile
に以下を記述します。# ベースイメージ FROM amazonlinux:2 # 同一ディレクトリの公開鍵ファイルを Docker イメージの /tmp ディレクトリにコピーする COPY ./id_rsa.pub /tmp/ # インストール済みのパッケージを最新版にアップデート RUN yum -y update && \ # 追加で必要なパッケージをインストール yum -y install \ sudo \ shadow-utils \ procps \ wget \ openssh-server \ openssh-clients \ which \ iproute \ e2fsprogs && \ # キャッシュを削除 yum clean all && \ # setuptools をインストール wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python && \ # SSH サーバを起動 systemctl enable sshd.service && \ # パスワード認証による SSH アクセスを禁止 echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \ # ec2-user を追加 useradd ec2-user && \ # ec2-user で sudo コマンドを使用できるようにする echo "ec2-user ALL=NOPASSWD: ALL" >> /etc/sudoers && \ # ec2-user のホームディレクトリ下に公開鍵を配置 sudo -u ec2-user mkdir -p /home/ec2-user/.ssh && \ mv /tmp/id_rsa.pub /home/ec2-user/.ssh/ && \ chmod -R go-rwx /home/ec2-user/.ssh && \ # 公開鍵を登録 cat /home/ec2-user/.ssh/id_rsa.pub >> /home/ec2-user/.ssh/authorized_keys && \ # ロケールを設定 echo "export LANG=en_US.UTF-8" >> /home/ec2-user/.bash_profile他にも必要なパッケージがある場合は
yum -y install
の部分に追加してください。ただし、あくまでも環境構築を検証するためのイメージなので、EC2 にデフォルトで入っていないものはここではインストールしないようにしています。Docker イメージをビルドします。ここではタグ名を
amazonlinux2-sshd
としています。
amazonlinux2
ディレクトリに移動して以下のコマンドを実行します。$ docker build -t amazonlinux2-sshd .ビルド完了後に
docker images
コマンドでamazonlinux2-sshd
が作成されていることを確認してみてください。正常にビルドができたことを確認できたら以下のコマンドで起動してみます。ここではコンテナ名を
amazonlinux2-sshd-container
とし、22
番ポートに2222
番ポートから接続できるようにしています。ここで
--privileged
を付加しないと SSH 接続が許可されないようです。さらに--rm
を付加して終了時にコンテナを削除するようにしていますが、削除しない場合はこのオプションを除外してください。$ docker run --privileged --rm -d -p 2222:22 --name amazonlinux2-sshd-container amazonlinux2-sshd /sbin/init起動したコンテナに SSH 接続してみます。普通に SSH 接続してもいいのですが、コンテナを作成するたびに信用するサーバを登録するのは面倒なので、無条件にサーバに接続するための alias コマンド
ssh-igk
(他の別名でも OK) を登録してそちらで接続することにします。こちらの作業はホスト OS 側で実施します。ホームディレクトリにある
.bashrc
の末尾にalias
の行を追加します。$ vi ~/.bashrc
~/.bashrc# (前略) alias ssh-igk='ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'登録した alias を使用するために以下を実行しておきます。
$ source ~/.bashrc登録した
ssh-igk
コマンドを使用してec2-user
で SSH ログインしてみます。$ ssh-igk -i ~/.ssh/id_rsa ec2-user@localhost -p 2222 Warning: Permanently added '[localhost]:2222' (ECDSA) to the list of known hosts. Last login: Wed Jul 3 12:21:43 2019 from gateway [ec2-user@98a69d756c81 ~]$警告のメッセージが出ますが問題なくログインできました。
以上で準備ができたので以下のコマンドでコンテナを停止しておきます。
$ docker stop amazonlinux2-sshd-container
システムを構築してみる
ベースとなる EC2 に近いイメージが作成できたので、こちらを使用してシステム構築を検証する環境を構築できます。ここでは
sample-system
を作成する例を説明します。ディレクトリ構成は以下とします。
(あらかじめ前述のamazonlinux2-sshd
をビルドしておく必要があります。)sample-system ├── docker-compose.yml ├── sample-core-api │ └── Dockerfile └── sample-front-ui └── Dockerfile以下の要素でシステムが構成されている前提とします。
sample-front-ui
: フロント画面を提供するシステムを想定sample-core-api
: データを提供する API システムを想定sample-db
: 永続化のための RDB を想定 (実際の AWS 環境では RDS や Aurora など)作成したそれぞれのファイルに以下を記述します。
sample-front-ui/Dockerfile
# あらかじめ amazonlinux2-sshd をビルドしておく必要があります FROM amazonlinux2-sshd # 以下はサンプルです RUN amazon-linux-extras enable php7.3 && \ yum install -y phpsample-core-api/Dockerfile
# あらかじめ amazonlinux2-sshd をビルドしておく必要があります FROM amazonlinux2-sshd # 以下はサンプルです RUN amazon-linux-extras install -y java-openjdk11docker-compose.ymlversion: '3' services: # フロント画面を提供するシステムを想定 sample-front-ui: build: # sample-front-ui/Dockerfile のイメージを使用 context: ./sample-front-ui container_name: sample-front-ui restart: always privileged: true command: /sbin/init ports: # ホスト OS からの SSH ログイン用 - "2223:22" # ホスト OS からの Web ページ動作確認用 - "8880:80" networks: samplenet: ipv4_address: 172.20.0.10 # データを提供する API システムを想定 sample-core-api: build: # sample-core-api/Dockerfile のイメージを使用 context: ./sample-core-api container_name: sample-core-api restart: always privileged: true command: /sbin/init ports: # ホスト OS からの SSH ログイン用 - "2224:22" # ホスト OS からの API 動作確認用 - "8881:8080" networks: samplenet: # フロントシステムからの接続を想定 ipv4_address: 172.20.0.11 # 永続化のための RDB を想定 sample-db: # その時点の Aurora で使用できる PostgreSQL のバージョンと合わせていたりします image: postgres:10 container_name: sample-db restart: always environment: POSTGRES_PASSWORD: changeit ports: # ホスト OS からの DB 接続用 - "5444:5432" networks: samplenet: # API システムからの接続を想定 ipv4_address: 172.20.0.12 # ネットワーク設定 networks: samplenet: driver: bridge ipam: driver: default config: - subnet: 172.20.0.0/24
sample-system
ディレクトリに移動して以下のコマンドを実行します。$ docker-compose up -dイメージがビルドされていない場合はビルドが実行され、各システムが実行されます。
以下のコマンドで稼働状況を確認できます。$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a4acb453f41a sample-system_sample-front-ui "/sbin/init" 2 minutes ago Up 2 minutes 0.0.0.0:2223->22/tcp, 0.0.0.0:8880->80/tcp sample-front-ui acf6c2276129 sample-system_sample-core-api "/sbin/init" 2 minutes ago Up 2 minutes 0.0.0.0:2224->22/tcp, 0.0.0.0:8881->8080/tcp sample-core-api 6980309c6d57 postgres:10 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:5444->5432/tcp sample-dbフロントシステムに SSH ログインして環境構築作業を検証する場合は以下を実行します。
$ ssh-igk -i ~/.ssh/id_rsa ec2-user@localhost -p 2223 Warning: Permanently added '[localhost]:2223' (ECDSA) to the list of known hosts. [ec2-user@a4acb453f41a ~]$検証できたコマンドを
sample-front-ui/Dockerfile
やsample-core-api/Dockerfile
などのRUN
に追記していけば最終的に開発 PC 上にシステム全体を自動構築することも可能です。立ち上げたシステムをまとめて停止する場合は以下を実行してください。
$ docker-compose down --rmi all上記では停止時にイメージをすべて削除するので、削除しない場合は
--rmi all
の部分を除外してください。おわりに
今回ご紹介した内容は Docker の一般的な使用方法とは少し異なるかと思います。一般的には 1 つのコンテナには 1 プロセスを実行させるように構成するので、Amazon Linux 2 にいろいろインストールするのではなく、PHP、Nginx、Java、PostgreSQL などをそれぞれの Docker イメージから起動していく方針となるはずです。あとそもそもコンテナに SSH ログインすることはあまりないと思います。
また、今後 AWS において EC2 を直接起動するのではなく Fargate、ECS、EKS を使用することが多くなりそうなので、今回のような EC2 を前提としたシステム構築を検証する頻度は減ってきそうです。
と、最後に記事を全否定ですがw 小さなシステムなどはまだ EC2 を使用して構築する場合もあるかと思いますのでその際はコストを気にすることなく動作検証をする方法の一つとして試してみてください。
最後に今回の手順はすべて Windows 10 Pro で実施していますが、Mac や Linux でも同様に実施できると思います。もしうまく動かない部分や記事に誤りなどありましたらご指摘ください。
- 投稿日:2019-12-03T08:40:56+09:00
プライベートDocker Registryの構築方法
プライベートDocker Registryを構築する方法を記載します。
この記事では、Docker Registryへの通信をhttpsで行っています。
社内で運用する目的で構築するので、http通信でも問題ありませんが、Docker Registryの通信方法は、デフォルトではhttpsになっており、httpよりもhttpsの方が簡単に構築できました。自己証明書でも運用には問題がないので、自己証明書で構築するようにしています。
最終的に構築したDocker Registryにローカル(Docker Registryが置かれたサーバー)からイメージのpush/pullを行えるようになります。記事の中で<>で囲っているものは、名前が任意のものであることを示しています。
環境
ubuntu 18.04
docker-ce 19.03.4
OpenSSL 1.1.0g構築方法
イメージのダウンロード
Docker Resistryは、Docker hubに公式のイメージがあるので、それをダウンロードします。
$ docker pull registry自己証明書の作成
https通信用の証明書を作成します。
秘密鍵の作成
秘密鍵の名前をserver.keyとします。
$ openssl genrsa -aes256 -out <server.key> 2048 Generating RSA private key, 2048 bit long modulus ....................+++++ ...........................+++++ e is 65537 (0x010001) Enter pass phrase for <server.key>:(任意のパスフレーズ) Verifying - Enter pass phrase for <server.key>:※注意
秘密鍵にパスワードが設定されていると、エラーによりDocker Registryコンテナが起動しません。
しかし、秘密鍵作成時に以下のように必ずパスワードを要求してくるので、適当なパスワードを設定し、後から削除します。
You must type in 4 to 1023 characters
秘密鍵のパスワードを削除
$ openssl rsa -in <server.key> -out <server.key> -passin pass:(秘密鍵のパスワード)証明書発行要求の作成
証明書発行要求の名前をserver.csrとします。
$ openssl req -new -key <server.key> -out <server.csr> You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: (未入力) State or Province Name (full name) [Some-State]:(未入力) Locality Name (eg, city) []:(未入力) Organization Name (eg, company) [Internet Widgits Pty Ltd]:(未入力) Organizational Unit Name (eg, section) []:(未入力) Common Name (e.g. server FQDN or YOUR name) []:<myregistry>(Docker Resistryがあるノードのドメインを入力) Email Address []:(未入力) Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []:(未入力) An optional company name []:(未入力)自己証明書作成
証明書の名前をserver.crtとします。
$ openssl x509 -days 3650 -in <server.csr> -req -signkey <server.key> -out <server.crt> Signature ok subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = <myregistry> Getting Private keyDocker Registryの起動
証明書を
<registry-certs>
ディレクトリに置きます。$ mkdir -p /<registry-certs> $ mv <server.key> <server.csr> <server.crt> /<registry-certs>コンテナを起動します。
なお、今回はホスト側のポートを5000番にマッピングしてますが、ホスト側のポートは、任意です。コンテナ側のポートは、公式イメージで5000番がエクスポートされているので、固定です。$ docker run \ -d \ -p 5000:5000 \ -v /<registry-certs>:/<certs> \ -v /<registry-data>:/var/lib/registry \ -e REGISTRY_HTTP_TLS_CERTIFICATE="/<certs>/<server.crt>" \ -e REGISTRY_HTTP_TLS_KEY="/<certs>/<server.key>" \ -e REGISTRY_HTTP_TLS_ADDR="0.0.0.0:5000" \ registryオプションの説明
-d:コンテナをバックグラウンドで起動します。 -p:ホスト側のポートとコンテナ側のポートをマッピングします。<host_port>:<container_port> -v:コンテナのボリュームをホストにマウントします。<host_volume>:<container_volume> -e:コンテナに環境変数を設定します。動作確認
イメージをpushする
確認用のイメージをpullします。
$ docker pull hello-worldRegistryにイメージをpushするときは、以下の形式で書きます。
<repositry>:<registry_port>/<project>/<image_name>:<tag>
<repositry>
の箇所は、docker hub上のリポジトリを指しています。
今回は、プライベートレジストリなのでリポジトリは、ありません。
このような場合、<repository>
は、Docker Registryのあるノード名に置き換えられます。
<tag>
はデフォルトでは、latestとなります。$ docker tag hello-world localhost:5000/<hello-project>/<hello-world>イメージをpushします。
$ docker push localhost:5000/<hello-project>/<hello-world>イメージがpushされます。/registry-data以下にイメージが置かれていることが確認できます。
また、pushをpullに変えれば、pullも行えます。参考文献
1.「レジストリ・サーバのデプロイ」,
http://docs.docker.jp/registry/deploying.html#running-a-domain-registry ,
2019年11月17日アクセス
2.「オレだよオレオレ認証局で証明書つくる」,
https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6 ,
2019年11月17日アクセス
- 投稿日:2019-12-03T05:40:44+09:00
code-server (3) VSCode の Plugin を 利用してみる
これは、2019年 code-server に Advent Calender の 第3日目の記事です。
2日目 の続きです。
今回も、code-server って何だろう?と言う事を解説していきます。(1) code-server って何?
(2) Dockerで独自のcode-server 環境を作って見る
(3) VSCode の Plugin を 利用してみる
(..) ローカルで、DBなどの環境も含めて構築するには
(..) オンライン上に置くには?
(..) K8Sなどの最近の流行りの環境と連携するには?
(..) Code-Serverを改造して、より良くしたいcode-server は vscode の plugin が利用できます。
vscode の plugin を利用できます。オートコンプリート などの 補助機能やリファクタリング機能を利用して、お手軽にプログラムを書けるようになります。
python で、作成してみます。
FROM python:3.8.0-buster RUN apt-get update # code-server を取得するのに wget を install しておく RUN apt-get install -y wget # 作業ディレクトリを /works にする。どこでも良いです WORKDIR /works # code-server のバイナリーを取得 RUN wget https://github.com/cdr/code-server/releases/download/2.1692-vsc1.39.2/code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz # code-server を /works 配下に解凍する RUN tar -xzf code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz -C ./ --strip-components 1 WORKDIR /works/app ENV PYTHONPATH=/works/app # python の plugin をインストール RUN /works/code-server --install-extension ms-python.python RUN /usr/local/bin/python -m pip install -U pylint --user # デフォルトは、/works/app で起動するようにする。 CMD [ "/works/code-server", "--allow-http", "--auth", "none", "--port", "8443", "/works/app"]ubuntu に python を インストールしても良かったのですが、公式の python image を、利用しています。
RUN /works/code-server --install-extension として、 python 向けの plugin を インストールしています。
PYTHONPATH を指定して、ルートフォルダーをしてしています。
試してみる
docker build -t cs03 . docker run -v "$PWD:/works/app" -p "8443:8443" -it cs03ブラウザーを開いて、何か書く
お、補完が効いていますね!!
次回
作成したImageを配布してみましょう。 一度、作成したImageは、ほぼほぼ、同じ状態で動かす事ができます。
- 投稿日:2019-12-03T01:07:42+09:00
Re:ゼロから始めるコンテナ生活
グレンジ Advent Calendar 2019 5日目の記事を担当しますkitaji_ngzkと申します。
昨年に引き続き、同プロジェクトでPHPを扱うサーバーサイドエンジニアをやってます。自分が属するプロジェクトには専任のインフラエンジニアがいないため、サーバーサイドエンジニアが兼任する形で担当しています。
ので、必然的にインフラ知識も俄然学ぶ必要があるので、今回個人的に「コンテナ」という技術に触れてみたいと思い、Dockerでローカル環境の構築からクラウドにあげて公開してみるところまで実践した軌跡をご紹介したいと思います。
ここから始めましょう!1から、、いいえゼロから!!
▼今回やってみたこと
Chapter.1 Docker公式のWordPressイメージを用いて、超速構築!
Chapter.2 GCPにMySQLサーバー作成
Chapter.3 Dockerイメージの作成とGCRにアップロード
Chapter.4 GKEのクラスタ作成
Chapter.5 クラスタにデプロイ▼環境
macOS Mojave10.14.6
Docker for mac 2.1.0.5Chapter.1 Docker公式のWordPressイメージを用いて、超速構築!
まず手始めにローカル環境でコンテナに触れてみます。
※前提としてDockerがインストールされていることから話を進めます。公式のWordPress, MySQLのイメージをローカルにpullします。
↓WordPressイメージのタグ一覧
https://hub.docker.com/_/wordpress
pullする時、指定のタグを:つなぎで指定できますが、指定しなければlatestがpullされます。
Wordpressはlatestで問題ないので、以下の通り$ docker pull wordpress続けてMySQLもpullしますが、ここで気をつけないといけないのは、MySQLのlatestは8系で認証方法が変わっていて、WordPressの接続エラーという罠が待っているので、バージョンを指定して落としましょう。(8系でも認証方式を昔のものに戻せば、使えるっちゃあ使えます)
↓MySQLイメージのタグ一覧
https://hub.docker.com/_/mysql
8系以外の最新では現時点で5.7系があったのでそれにします。ので、以下の通り$ docker pull mysql:5.7さて、両方ちゃんと落とせたか確認します。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE wordpress latest e4bd752aeb0d 7 days ago 539MB mysql 5.7 cd3ed0dfff7e 5 weeks ago 437MBこんな感じで表示されてれば成功。
ではこの二つのイメージをそれぞれ使って、それぞれのコンテナを作成していきます。
まずはMySQLコンテナから$ docker run --name sample-mysql -e MYSQL_ROOT_PASSWORD=任意のパス -d mysql:5.7各オプションについてはdocker run --helpとかで調べるか、「docker run オプション」とかで調べましょう。なお、sample-mysqlという部分は任意のコンテナ名です。
(また、ぶっちゃけいちいち先にpullしなくても、このrunコマンドで勝手に持ってきてくれるっちゃあくれます。)では、無事にコンテナが起動しているか、確認しましょう。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8703def027e0 mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp sample-mysqlこのように表示されてれば成功です。
それではこのMySQLサーバーにつなげるWordPressコンテナを作成していきます。
$ docker run --name sample-wordpress --link sample-mysql:mysql -d -p 8080:80 wordpress 8703def027e0e9b6cce6409ee01544285836f8cddd4293331fa07b7e1930f56b $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6eca366c2b0c wordpress "docker-entrypoint.s…" 24 seconds ago Up 23 seconds 0.0.0.0:8080->80/tcp sample-wordpress 8703def027e0 mysql:5.7 "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 3306/tcp, 33060/tcp sample-mysql無事起動したようだ。それでは上記のコマンドにもある通り、ポート8080をあけたので、
http://localhost:8080
にアクセスしてみましょう
このように表示されれば成功です。あとは画面通りに設定項目埋めていけば、WordPressのローカル環境はできあがりです。
ただし、今は/var/www/htmlをホストにマウントしていないので、いじりたいhtmlやphpファイルをエディタでいじいじできないという壁があります。(vim愛好家の方はdocker execでコンテナ内に入れば、用を足せると思います。ただ、このコンテナにはvimを入れてないので、vimをapt-getでインストールする必要があります。)ではwp-admin画面まで表示されたら、一度以下のコマンドで、ホストの任意のディレクトリにDocker内のファイルをコピーしておきます。
$ docker cp sample-wordpress:/var/www/html ホストPCのディレクトリ終わったら、ホストPCの任意のディレクトリにコピーされてるか確認しときましょう
$ ls -al コピー先のディレクトリでhtml以下のWordPressのファイルがもろもろあればOKです。
ここまできたら、DEDEATHDEATHDE~~~ATH!
作成したWordPressコンテナのみ削除して死に戻り!$ docker stop sample-wordpress sample-wordpress $ docker rm sample-wordpress sample-wordpress $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8703def027e0 mysql:5.7 "docker-entrypoint.s…" 33 minutes ago Up 33 minutes 3306/tcp, 33060/tcp sample-mysql戻ってきたら、当然先ほどのlocalhostにアクセスしても、応答がないと思います。ので、WordPressコンテナを作り直します。今度は先ほどコピーしたディレクトリをマウントさせて作ります。
$ docker run --name sample-wordpress-2 -v コピー先の絶対パス:/var/www/html --link sample-mysql:mysql -d -p 8080:80 wordpress 72d97a8374d383e4f366e1adfba3684e6d83aaffef428e23c7849be9abd216be $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 72d97a8374d3 wordpress "docker-entrypoint.s…" 2 seconds ago Up 1 second 0.0.0.0:8080->80/tcp sample-wordpress-2 8703def027e0 mysql:5.7 "docker-entrypoint.s…" 39 minutes ago Up 39 minutes 3306/tcp, 33060/tcp sample-mysqlここで、先ほど同様localhost:8080にアクセスしてみましょう。
最初に設定した内容そのままに復元できました。
ホストにマウントされているので、これでテーマをインストールしたりしても、ホスト側にも反映されるようになり、ホスト側をgitで管理したりすれば開発が複数人でできるようになると思います。さて、超速でローカル環境を構築できたものの、この構成をクラウド上でも実現したいと思います。ただ、ローカルではMySQLコンテナを作りましたが、クラウド上ではDBはスケールさせないのでVMインスタンスで十分だと思います。(データの永続化という方法もありますが)
ということで、クラウド(今回はGCP)上にMySQLサーバー作っていきます。Chapter.2 GCPにMySQLサーバー作成
チャプターにはしてみたものの、あんまりコンテナ関係ないのでさらっといきます
①MySQLコンテナに入ってまるごとdumpfile作成$ docker exec -it sample-mysql /bin/bash root@~~# mysqldump -uroot -p -A > sample-wordpress.dump root@~~# exit②ホストにコピーしてくる
$ docker cp sample-mysql:/sample-wordpress.dump ./↑コピー先は./入れてますが、任意のディレクトリで.(ぶっちゃけマウントしてるディレクトリにもともと格納すればホストに落ちてくるという)
③のちのちのためにVPCネットワーク作成
④VMインスタンス作成→MySQL5.7入れる(←ここで入れるMySQLのバージョンはローカルで使ってたやつと一緒にするように)
⑦VMインスタンスに②のdumpファイル転送&ぶっこみこれでDockerで作られてるMySQLコンテナの中と同じ構成のサーバーができたはず。
ここまでで、もちろんホストPCからもDBサーバーのMySQLにアクセスできるはずなので、確かめてみます。$ mysql -h外部IPアドレス -uroot -p Enter password:ルートパスワード ERROR 2003 (HY000): Can't connect to MySQL server on '外部IPアドレス' (61)!?むむ!つながらぬ。。なぜ。。
お試しでローカルからアクセスしてみようと思い、一時的にIPとポート全開放(超非推奨)してましたが、ssh用のポートはtelnetコマンドで確認してみても問題なかったので、おそらくポートは開いているだろう。
また、サーバー自体に繋がってなかったら、エラーがUnkown hostとかになりそうな気もするので、サーバー内のMySQLまでは到達していそう。
となると、MySQLの設定周りが怪しいかもってことで、調べていると以下の情報に行き着きました。
https://gist.github.com/koudaiii/10696132念の為リンク切れちゃった時用に一部以下抜粋しておきますが、自分が引っかかったのはここでした。
2.my.cnfのbind-address設定
my.confのbind-addressの設定を確認してみる。
$ vi /etc/mysql/my.cnf
bind-address = 127.0.0.1
bind-address = (接続したいマシンのIPアドレス)
追加したい接続先のIPを書いた「bind-address」を追加していけばOK。どのIPからも接続許可する場合はbind-addressをコメントアウトすればOK。
たしかにサーバー内のmy.confの設定内にlocalhostのみ許可のデフォ設定がありました。。(ここにたどり着くまでに自分は4,5回死に戻り(=削除して再作成したり)してます笑)
ので、教えの通りにコメントアウトし、再度ホストPCからtelnetしてみると、接続ができたので、MySQLコマンドを再度叩いてみる。$ mysql -h外部IPアドレス -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 5.7.28 MySQL Community Server (GPL) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
やったーーー!!
ついにローカルからもアクセスできるようになりました。
WordPressコンテナの/var/www/html配下はホストにマウントしてあると思うので、wp-config.phpを自分の好きなエディタで編集してDB_HOSTの向き先を試しに変更してみます。以下の部分です。/** MySQL hostname */ define( 'DB_HOST', 'mysql'); ↓↓↓ /** MySQL hostname */ define( 'DB_HOST', '外部IPアドレス');これで
http://localhost:8080
にアクセスしてみましょう。
少しローディングが長く感じますが、これでローカルのWordPressコンテナからクラウド上のMySQLサーバーに接続することができました!(無事MySQLをGCPに作れたようだ←これはこれで個人的にできたの嬉しい笑)ただ、注意を一つですが、この時点でMySQLのユーザはこうなってるはずです。
mysql> SELECT user, host FROM mysql.user; +---------------+-----------+ | user | host | +---------------+-----------+ | root | % | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+ 4 rows in set (0.00 sec)root権限がどんなIPからでも使えちゃう怖い状況なので、しっかり削除
そして、WordPressからアクセスする用のユーザを作成して、WordPressで使用するDBのみ許可する権限を与えておきましょう!→最終的に同じVPNネットワークにする予定なので、プライベートネットワークのみに制限
WordPressで使用するユーザを作成したら、wp-config.phpに反映することを忘れずに!(・・・ていうか、これ最初からやっとけばよかったんじゃね?|ω・`))
さて、もし同じようにローカルから接続してみた場合は、忘れずにIPとポートを閉めて、wp-config.phpの設定もローカルのmysqlコンテナ向きに戻しておきましょう。Chapter.3 Dockerイメージの作成とGCRにアップロード
今できているWordPressコンテナをイメージ化する方法としては、コンテナを停止して、docker commitのコマンドを使用するか、DockerFileを作成してイメージをビルドするかなどがあります。
実際にGKEで動作させる際、WordPressのDB_HOSTは先ほど作成したMySQLサーバーを向いて欲しかったりします。となるとローカルコンテナをそっくりそのままイメージ化しても動かないでしょう。
ので、これまでやってきた流れをDockerfileで再現する形でイメージをビルドしてGCRにあげてみたいと思います。Dockerfileは以下のように作成しました。一番最後に「クラウド用の設定でwp-config.phpを上書き」とありますが、最終的にGKEとMySQLサーバーが同VPCネットワーク内であればプライベートIPで接続できるはずなので、DB_HOSTの向き先をそのように変えてあるファイルを取り込んでいます。
FROM wordpress:latest LABEL maintainer="kitaji_ngzk" # vimインストール RUN ["apt-get", "update"] RUN ["apt-get", "install", "-y", "vim"] # ホストのソースコードを取り込む WORKDIR ローカルのWordPressコンテナをマウントした直上ディレクトリ ADD /html/ /var/www/html/ # クラウド用の設定でwp-config.phpを上書き ADD /gcp/wp-config.php /var/www/html/wp-config.phpイメージをビルドします。
$ docker build -t sample-wordpress:1.0.0 . Sending build context to Docker daemon 52.51MB Step 1/8 : FROM wordpress:latest ---> e4bd752aeb0d Step 2/8 : LABEL maintainer="kitaji_ngzk" ---> d34337359340 Step 3/8 : RUN ["apt-get", "update"] ---> 0e10ffbdafc1 Step 4/8 : RUN ["apt-get", "install", "-y", "vim", "net-tools"] ---> 75c64ff2bacc Step 5/8 : WORKDIR ローカルのWordPressコンテナをマウントした直上ディレクトリ ---> 5d913bf121e2 Step 6/8 : ADD /html/ /var/www/html/ ---> b963ce7d1331 Step 7/8 : WORKDIR /var/www/html ---> 9d9e0ebbce17 Step 8/8 : EXPOSE 80 ---> d7e54c624845 Successfully built d7e54c624845 Successfully tagged sample-wordpress:1.0.0成功したようなので、確認します。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE sample-wordpress 1.0.0 5e28a38a1758 36 minutes ago 591MB wordpress latest e4bd752aeb0d 2 weeks ago 539MB mysql 5.7 cd3ed0dfff7e 6 weeks ago 437MBこれでイメージ化できました。では、一応このイメージを使ってコンテナを起動できるかローカルで試してみて(docker run)、同じようにWordPress管理画面が表示されればOKです。
(実際プロジェクトとして開発する時は、ローカルのソースコードじゃなくて、ちゃんとgit管理されたmasterブランチのソースコードをpullしてきて、それをADDするべきかなと思います)次に、GCRに上げるには、イメージにレジストリ名をタグ付けする必要があるので、以下の通りタグ付けします。
$ docker tag sample-wordpress:v1.0.0 gcr.io/kitaji_sample/sample-wordpress:v1.0.0 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE sample-wordpress v1.0.0 0465bcd14635 11 hours ago 539MB gcr.io/kitaji_sample/sample-wordpress v1.0.0 0465bcd14635 11 hours ago 539MB wordpress latest e4bd752aeb0d 2 weeks ago 539MB mysql 5.7 cd3ed0dfff7e 6 weeks ago 437MBkitaji_sampleとなっているところはGCPのプロジェクト名です。続けて、GCRにイメージをpushします。
$ docker push gcr.io/kitaji_sample/sample-wordpress:v1.0.0 The push refers to repository [gcr.io/kitaji_sample/sample-wordpress] d79a1d170eaf: Pushed d5b61126e77c: Pushed 182f6bf618c4: Pushed 02e5b86b9d8d: Pushed f190d03dd9cf: Pushed 31edc4603d49: Pushed a6c798e344c1: Pushed 7ad4f8a271af: Pushed 8b9e7bae16d7: Pushed 4c31d76c6594: Pushed 983090088b87: Pushed 53f8a4a17b10: Pushed 01af4509e166: Pushed 37a065eea6b3: Pushed 8067bb2f50e2: Pushed ba31a1dbfcfb: Pushed 3e84f33ac944: Pushed 9f9d470ac131: Pushed 86569e4ec54b: Pushed 12fe3564ccac: Pushed 4e9b2aba858c: Pushed b67d19e65ef6: Pushed v1.0.0: digest: sha256:a6326341ec7a3c2596125ea424460826e504265924e66e4b34f24bc762a82dce size: 4915GCRのコンソールで確認してみると、
お、あがってるあがってる(ちなみにこのリポジトリ内を見ると、なんと脆弱性もチェックしてくれてて、350個もありました笑 うち重大は2個w 恒久的に外部に公開する前には重大:中以上は解決しておかないとですね汗)
インスタンスもそうですが、GCRにあげたイメージも課金対象なので、必要ない時は削除をおすすめします。詳しくはGCRの公式ドキュメント参照。Chapter.4 GKEのクラスタ作成
先ほどのGCRへのpushもそうですが、これ以降のやり方はGCPの公式ドキュメントに詳細に書かれているので、それを参考にやってみた方がいいかと思います。(特にオプション周りとか)
自分は実際に実行したコマンドを並べて紹介していきたいと思います。完全にお試しクラスタなので、オプションで不十分だったりするところあるんですが、ご容赦ください。(autoscaleとか設定してなかったり、stackdriverの設定だったり..←後で消すのめんどくさいと思った)$ gcloud container clusters create sample-wordpress-web --region asia-east1 --machine-type g1-small --subnetwork kitaji-sample-subnet --disk-size 10GB --network kitaji-sample-network --max-nodes-per-pool 100 --num-nodes 1 WARNING: Currently VPC-native is not the default mode during cluster creation. In the future, this will become the default mode and can be disabled using `--no-enable-ip-alias` flag. Use `--[no-]enable-ip-alias` flag to suppress this warning. WARNING: Newly created clusters and node-pools will have node auto-upgrade enabled by default. This can be disabled using the `--no-enable-autoupgrade` flag. WARNING: Starting in 1.12, default node pools in new clusters will have their legacy Compute Engine instance metadata endpoints disabled by default. To create a cluster with legacy instance metadata endpoints disabled in the default node pool, run `clusters create` with the flag `--metadata disable-legacy-endpoints=true`. WARNING: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). This will enable the autorepair feature for nodes. Please see https://cloud.google.com/kubernetes-engine/docs/node-auto-repair for more information on node autorepairs. Creating cluster sample-wordpress-web in asia-east1... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/kitaji-sample/zones/asia-east1/clusters/sample-wordpress-web]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/asia-east1/sample-wordpress-web?project=kitaji-sample kubeconfig entry generated for sample-wordpress-web. NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS sample-wordpress-web asia-east1 1.13.11-gke.14 35.201.158.151 g1-small 1.13.11-gke.14 3 RUNNINGクラスタの作成は少し時間がかかります。
今回WARNINGたくさんあるけど、取り急ぎなんかできたっぽい。
ほんとならyamlとか作って体系的に書けるんだろうけど、いったんその辺置いといて、GKEコンソールで確認(gcloud container clusters describe クラスタ名
とかでも確認できる)
きっとあとはIngress(→LB)、Service、Deploymentの設定を入れたげれば、公開されるはずっ。
Chapter.5 クラスタにデプロイ
いよいよ最後デプロイと公開工程です。
まず先ほど作成したクラスタをkubectlコマンドで使えるようにする
$ gcloud container clusters get-credentials sample-wordpress-web --region asia-east1 --project kitaji-sample Fetching cluster endpoint and auth data. kubeconfig entry generated for sample-wordpress-web.これで先ほどのクラスタに対しkubectlコマンドが使えるようになりました。
kubectl get nodes
とか実行したら、ぶら下がってるnodeが出てくるはず。さて、それでは公開に向けて、Ingress、Service、Deploymentの設定を作成していきます。
設定はそれぞれyamlファイルをローカルに作成して、kubectl applyのコマンドでデプロイするだけです。(なんて簡単!)ただ、Ingressを作るたびにIPアドレスが変わられるとインフラ開発工程上厄介になり得るので、先に静的IPアドレスを予約しておきます。
$ gcloud compute addresses create sample-wordpress-web --global Created [https://www.googleapis.com/compute/v1/projects/kitaji-sample/global/addresses/sample-wordpress-web]. $ gcloud compute addresses list --global NAME ADDRESS/RANGE TYPE PURPOSE NETWORK REGION SUBNET STATUS sample-wordpress-web XX.XXX.XX.XX EXTERNAL RESERVED予約が完了したので、それでは各yamlファイルを作成します。それぞれ以下の通り作成しました。
sample-wordpress_deployment.yamlapiVersion: apps/v1beta1 kind: Deployment metadata: name: sample-wordpress-deploy labels: app: sample-wordpress spec: replicas: 1 selector: matchLabels: app: sample-wordpress template: metadata: labels: app: sample-wordpress spec: containers: - name: sample-wordpress-web image: gcr.io/kitaji-sample/sample-wordpress:1.3.0 imagePullPolicy: Always ports: - containerPort: 80 protocol: TCP readinessProbe: httpGet: path: /health.html port: 80 initialDelaySeconds: 5 periodSeconds: 5sample-wordpress_service.yamlapiVersion: v1 kind: Service metadata: name: sample-wordpress-service labels: app: sample-wordpress spec: selector: app: sample-wordpress type: NodePort ports: - port: 8080 targetPort: 80 protocol: TCPsample-wordepress_ingress.yamlapiVersion: extensions/v1beta1 kind: Ingress metadata: name: sample-wordpress-ingress annotations: kubernetes.io/ingress.global-static-ip-name: sample-wordpress-web spec: backend: serviceName: sample-wordpress-service servicePort: 8080あとはkubectl applyでデプロイするだけ!(たぶん!)
$ kubectl apply -f sample-wordpress_deployment.yml deployment.apps/sample-wordpress-deploy created $ kubectl apply -f sample-wordpress_service.yml service/sample-wordpress-service created $ kubectl apply -f sample-wordpress_ingress.yml ingress.extensions/sample-wordpress-ingress createdあっさり作られました。一応作られてるか確認します
$ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE sample-wordpress-deploy 1/1 1 1 10h $ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP XX.XX.XXX.X <none> 443/TCP 11h sample-wordpress-service NodePort XX.XX.XXX.XXX <none> 8080:30585/TCP 10h $ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE sample-wordpress-ingress * 80 10hん?Ingressのアドレスが空欄だ・・・。さっき静的IPを予約したはず。
コンソールでもみてみると、まだ、ステータスがCreating Ingressでした。なるほどingressは時間がかかるんかぁ。
しばし待ちます。
再度ingressだけ確認してみます
$ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE sample-wordpress-ingress * XX.XXX.XX.XX 80 12mお、無事に作られてそう。と思いきや、コンソールで見ると、ステータスにWARNアイコン。
なにやらバックエンドサービスでヘルスチェックが通っていない的なことを言っている。
実際、割り当てられた外部IPアドレスにアクセスしてみると、502。ここからかなり時間を費やしました。。
いろいろKubernetesのヘルスチェックを調べていると、ヘルスチェック(今回指定しているのはreadinessProbe)で指定したパスに対して、200が返ってこないとダメらしい。そしてStackdriverのContainerLogを見ると、返しているのは301。。なるほど。wordpressのあれかな、.htaccessによるリダイレクトがいけないのかなってことで、この際、health.htmlなるファイル(中身はHello.と書いただけ)を/var/www/htmlに置いてみて、readinessProbeで指定するパスを/health.htmlに変えてもう一度死に戻り、全て再applyしてみます。
もしかして、これで、、アクセスできるんちゃうか!?
・・・ドキドキ・・・ドキドキ・・・・
ただ、502ではなくなったので、進撃はしているようだ。
今度はこの404を倒す・・・このくらいの絶望で俺が止まると思うなよ!!とは言ったものの、LBからポッドまで通信はきていそうだし、/var/www/html内にも各ファイルが配置されているし、所有者とグループがwww-dataではなく、rootになっていたので直してみたし、、と悩みまくって禿げそうだったので、先輩エンジニアの方々に相談に乗ってみてもらったところ、ついに、原因が特定できましたっ!
なんとWordPressのこの設定部分でした
mysql> SELECT * FROM wp_options WHERE option_name IN ('home','siteurl'); +-----------+-------------+------------------------+----------+ | option_id | option_name | option_value | autoload | +-----------+-------------+------------------------+----------+ | 2 | home | http://localhost:8080/ | yes | | 1 | siteurl | http://localhost:8080/ | yes | +-----------+-------------+------------------------+----------+ 2 rows in set (0.00 sec)なんというチョンボ・・・orz
管理画面でいうと「設定>一般」の赤枠部分です。
ここをmysql> UPDATE wp_options SET option_value = 'http://Ingressに割り当てられた外部IPアドレス/' where option_name IN ('home'); Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> UPDATE wp_options SET option_value = 'http://Ingressに割り当てられた外部IPアドレス/' where option_name IN ('siteurl'); Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0に変更します。外部IPアドレス部分はドメインを取得していたらドメインでもOKなやつ
ごちそうさまです♪
最後に
やってたら意外と盛りだくさんで時間が最後たらなくなって急ぎ足になってしまいました。。
本当はこの後、Spinnakerチャレンジとかしてみたかった。これ書くまでにいろいろグレンジの先輩エンジニアの方々にみなさん年末の鬼多忙の中、片手間でもアドバイスをいただいて、なんとか最後までできましたm(._.)m
僕の先輩方はみんな超鬼がかってました!!グレンジには「困っていたら助けるのは当たり前」なたっち・みーさん的エンジニアがたくさんいるふれんどりぃな会社です^^
これを最後まで読んでくれた心優しきあなたのJOINをいつでもお待ちしています!!笑
https://www.grenge.co.jp/recruit/
- 投稿日:2019-12-03T00:25:58+09:00
VirtualBoxのインストールやdocker-QuickstartTerminalのhost-only adapterの作成が失敗して、再起動やVBのバージョン変更でも直らなかった時用の記事
背景1
windows 10 Proでdocker-QuickstartTerminalを実行しても下記のようなエラーメッセージが表示されてインストールが完了しなかった…
Running pre-create checks... Creating machine... (default) Copying C:\Users\me\.docker\machine\cache\boot2docker.iso to C:\Users\me\.docker\machine\machines\default\boot2docker.iso... (default) Creating VirtualBox VM... (default) Creating SSH key... (default) Starting the VM... (default) Check network to re-create if needed... (default) Windows might ask for the permission to create a network adapter. Sometimes, such confirmation window is minimized in the taskbar. (default) Creating a new host-only adapter produced an error: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe hostonlyif create failed: (default) 0%... (default) Progress state: E_FAIL (default) VBoxManage.exe: error: Failed to create the host-only adapter (default) VBoxManage.exe: error: Querying NetCfgInstanceId failed (0x00000002) (default) VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component HostNetworkInterfaceWrap, interface IHostNetworkInterface (default) VBoxManage.exe: error: Context: "enum RTEXITCODE __cdecl handleCreate(struct HandlerArg *)" at line 71 of file VBoxManageHostonly.cpp (default) (default) This is a known VirtualBox bug. Let's try to recover anyway... Error creating machine: Error in driver during machine creation: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is is supposed to fix this issue Looks like something went wrong in step ´Checking if machine default exists´... Press any key to continue...GitHubのissueでも似たようなケースが報告されているが、再起動やVBのアップグレード、パーミッションの変更で解決している。が、なぜか幣PCでは解決しない、やむ(夢見りあむ)
背景2
あと大学の講義でVBをインストールする際、ほぼ全員のPCでインストールが失敗して阿鼻叫喚だったりのケースも下記の対策で解決したので併記しておきます。
対策
(インターネットから切り離してから)セキュリティソフトを止めてみてください(弊環境ではウイルスバスターcorpでした)
止めてから再インストールすると無事に入ります。理由
VirtualBoxでのホストオンリーアダプター作成時、セキュリティソフトの影響で作成が失敗してるもようです。
VBoxManage.exe: error: Failed to create the host-only adapter
GUIからのインストールだとエラーメッセージが表示されないのでわかりづらいですが、ログファイルみるとおそらく同じような記載があると思われます。
ウイルス定義ファイル内にホストオンリーアダプターの作成が含まれてたりするんですかね…