[CI/CD] インスタンスからDooDのデプロイまでの流れ(Nginx、Jenkinsを活用)
DockerとNginxを活用したデプロイの基礎固め
23-06-16 ボリュームマウントを明確にするためにパスを修正 23-07-05 開発の利便性のために設定を調整
はじめに
[CI/CD] Docker + Jenkins + Git Webhook を使った FastAPI サーバの自動デプロイまとめ
今回この記事を書く理由は、上の前回記事で 1 次的に学ぼうとしていた内容を扱ったが、修正すべきところを修正し、整えるべきところを整えてきれいな 1 つの記事にまとめたかったからだ。
前回以降のデプロイ関連タスクや、周りの人のデプロイを手伝う中である程度のノウハウを身につけて定型化できたので、その記録を残し、次回以降のデプロイでも一貫したプロセスとして使えるようにしたい!本記事の手順に沿って進めれば、おそらくデプロイは難しくないはずだ。
1 編以降に学んだ追加の内容
- Docker in Docker で実装するより Docker out of Docker で実装する理由は?
そもそも Docker は、Docker コンテナ上でコマンドを実行できるようにすることを推奨していない
Docker in Docker は Docker がインストールされたコンテナに非常に強い権限を付与することになり、セキュリティリスクを招き得る
その点 Docker out of Docker はコンテナ外部の Docker とソケットを共有して docker コマンドを実行する方式
- Docker API を使う方式はどう?
実は 1 編で問題解決のために試した方式で、実際に効果があった。 ただし Docker out of Docker が無事ビルドできているなら、CI/CD の手順上、いまのところ大きな必要性は感じない…
別記事に分けるのは中途半端な気がしたので、簡単に記録した API 関連の内容
Docker は REST API を通じてエンジンとクライアントが通信するが、ほとんど使う機会はないとはいえ、使うことはできる。
docker --tlsverify --tlscacert=/.docker/ca.pem \
--tlscert=/.docker/cert.pem --tlskey=/.docker/key.pem \
-H={server_ip}:{port_number} exec nginx \
sed -i '4s/.*$/ server {container}:8000;/' /etc/nginx/sites-available/default.conf
このコードは TLS 証明書を持つ Docker Client を通じて、任意のサーバ上にある Nginx 設定ファイルの内容を修正するコードだ。 -H オプションでそのサーバの port を介して Daemon (Docker Engine) と通信し、残りのコードは証明書による接続セキュリティの手続きだ。
もし証明書を使わずポートを開けっ放しにすると、誰かに開いている port を通してコンテナの「托卵」を喰らう可能性がある!
이미지를 불러올 수 없습니다.
コンテナ托卵の実体験。Docker でよく使われる port を認証なしで開けっ放しにした結果…
証明書によるセキュリティ処理は必ず行うべきで、やむを得ない事情で行えない場合は、その port を介して実行可能なコマンドを制限することが必須不可欠である。
[Docker API Documents] (https://docs.docker.com/engine/api/v1.43/) の内容を見ると、より詳しく確認できる!
All in One デプロイ基盤構築の手順
1. インスタンス接続とインスタンスのアップデート
ssh -i {secure shell key のパス} {ユーザー名}@{IP アドレス}
sudo apt-get update
sudo apt-get upgrade
ubuntu のセットアップを進める場合、ユーザー名が ubuntu であることが多い。
2. ファイアウォール設定
sudo ufw allow ssh # ssh は 22 に置き換えても良い
sudo ufw allow http # http は 80 に置き換えても良い
sudo ufw allow https # https は 443 に置き換えても良い
sudo ufw enable # ファイアウォールを起動
sudo ufw status # ファイアウォールの状態確認
おそらくクラウドを使う場合、最初からクラウド独自のポートセキュリティ設定があるので、その設定でも開放しておく必要がある。 ufw は Ubuntu のファイアウォールで、最初に設定する際オフになっている場合は、ポートを許可した上で起動する手順が必要だ。 上の設定で、基本的な http (80)、https (443)、ssh (22) ポートを開放した状態でファイアウォールを起動できる。
必ず先にポートの allow 設定を行ってから起動することを推奨する。22 番、ssh ポートを閉じたままファイアウォールを起動すると、接続できなくなる…
3. Docker および Docker Compose のインストール
# Docker のインストール
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Docker Compose のインストール
sudo apt install docker-compose
Ubuntu Docker インストール Documents
2023 年 5 月時点の Documents から必要な内容だけをまとめて記載した。
sudo usermod -aG docker $USER
このコマンドで sudo なしに docker コマンドを使えるようになる。
このコマンドを入れない場合 /var/run/docker.sock: connect: permission denied が発生する。
4. docker-compose.yml の作成
version: '3.8'
networks:
{希望のネットワーク名}:
driver: bridge
services:
# Jenkins Container 設定
jenkins:
container_name: jenkins
image: jenkins/jenkins:lts
restart: unless-stopped
ports:
- "{希望のポート番号}:8080"
volumes:
- ./mount/jenkins:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
user: root
environment:
- TZ=Asia/Seoul
networks:
- {希望のネットワーク名}
デプロイパイプラインのツールとして Jenkins を使う前提で書いた。
追加のネットワークが不要なら networks 部分は省略でき、Docker out of Docker を使うためにソケットのボリュームバインディングを行う。必要に応じて Time Zone や追加のボリュームバインディングも設定可能だ。
5. 使うドメインの DNS 設定
ドメイン事業者によって方式が違う。利用しているドメイン事業者の登録方法に従って、自分のサーバ IP を登録してドメインに紐付ける処理が必要だ。
登録してすぐ使えるわけではなく、DNS サーバへの反映時間が存在する。手軽に伝播状況を確認できるサイトを上に貼っておいた。
6. Let’s Encrypt を使った証明書発行
1) docker-compose.yml
# Nginx 設定
nginx:
container_name: nginx
image: nginx:latest
restart: unless-stopped
volumes:
- ./mount/nginx/nginx.conf:/etc/nginx/nginx.conf # Nginx 設定のためのバインディング
- ./mount/nginx/sites-available:/etc/nginx/sites-available
- ./data/certbot/conf:/etc/letsencrypt # 証明書共有のためのバインディング
- ./data/certbot/www:/var/www/certbot
ports:
- 80:80
- 443:443
environment:
- TZ=Asia/Seoul
networks:
- {希望のネットワーク名}
# Certbot コンテナ設定
certbot:
container_name: certbot
image: certbot/certbot
restart: unless-stopped
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
environment:
- TZ=Asia/Seoul
この内容を docker-compose.yml に追加し、それ以上の処理は行わない。
2) mount/sites-available/default.conf
server {
listen 80;
listen [::]:80;
server_name my_domain.org;
location /.well-known/acme-challenge/ {
allow all;
root /var/www/certbot;
}
}
ファイルを作成し、server_name の後ろに自分のドメインを記載した内容を入れる。
3) mount/nginx.conf
user nginx;
worker_processes auto;
worker_priority 0;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept off;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
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"';
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log debug;
gzip on;
include /etc/nginx/sites-available/*;
}
nginx を初めてインストールするときの内容を基に、適度な初期設定を入れた。
4) Let's Encrypt を通じた手続きでの証明書発行
curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh -o init-letsencrypt.sh
chmod +x init-letsencrypt.sh
vi init-letsencrypt.sh # ここでドメインとメールアドレスを入力する必要あり
sudo ./init-letsencrypt.sh
証明書発行の手続きを助けてくれるファイルを外部から取得し、権限を設定する。その後、自分のドメインとメールアドレスをファイルに入力し、そのファイルを実行することで証明書発行を進められる。発行に失敗した場合はログを見て原因を見つけ、修正して再試行すれば良い。
7. 証明書更新まで考慮した Nginx 設定
# Nginx 設定
nginx:
container_name: nginx
image: nginx:latest
restart: unless-stopped
volumes:
- ./mount/nginx.conf:/etc/nginx/nginx.conf
- ./mount/sites-available:/etc/nginx/sites-available
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
ports:
- 80:80
- 443:443
environment:
- TZ=Asia/Seoul
networks:
- {接続したいネットワーク名}
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
# Certbot コンテナ設定
certbot:
container_name: certbot
image: certbot/certbot
restart: unless-stopped
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
environment:
- TZ=Asia/Seoul
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
Let's Encrypt で発行した証明書には使用期限があり、代わりに有効期限が近づくと再発行できる。certbot コンテナと nginx コンテナに再発行関連のコマンドを追加することで、証明書更新が自動で進むよう処理できる。証明書発行が完了している状態なので、nginx がエラーでコンテナビルドに失敗することはなくなる。
8. Jenkins コンテナ内に Docker をインストール
追加した内容) もともとはコマンドを直接並べていたが、直感的でないと感じたので修正!
# /jenkins-docker-install.sh
apt-get update
apt-get install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
既存の docker インストール手順から sudo を抜いたコードを sh ファイルにした。このファイルをマウントで内部に入れて実行すれば、すっきりと一発でインストールできる!
9. Docker Compose の処理
version: '3.8'
networks:
{ネットワーク名}:
driver: bridge
services:
# Jenkins Container 設定
jenkins:
container_name: jenkins
image: jenkins/jenkins:lts
restart: unless-stopped
ports:
- "{ポート番号}:8080"
volumes:
- ./mount/jenkins:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- ./mount/jenkins-html:/var/lib/jenkins
- ./jenkins-docker-install.sh:/jenkins-docker-install.sh
user: root
environment:
- TZ=Asia/Seoul
networks:
- {ネットワーク名}
# Nginx 設定
nginx:
container_name: nginx
image: nginx:latest
restart: unless-stopped
volumes:
- ./mount/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./mount/nginx/sites-available:/etc/nginx/sites-available
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
ports:
- 80:80
- 443:443
environment:
- TZ=Asia/Seoul
networks:
- {ネットワーク名}
depends_on:
- {先行コンテナ}
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
# Certbot コンテナ設定
certbot:
container_name: certbot
image: certbot/certbot
restart: unless-stopped
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
environment:
- TZ=Asia/Seoul
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
このように Jenkins 設定まで追加したあと
docker-compose -f {yml ファイルの場所およびファイル名} up -d
コマンドでコンテナを全て立ち上げられる。
docker exec -it jenkins bin/bash
sh /jenkins-docker-install.sh
このように Jenkins コンテナ内部に Docker をインストールすることで、Jenkins 内部の Shell コマンドから Docker Engine を制御できるようになる。 既存の Docker Documents を参考にしたが、違いは sudo を使わない点と、Debian 用 Docker をインストールする点だ。
オプション
1. Swap Memory 設定
# swap memory 設定
sudo fallocate -l {設定容量} /swapfile # 設定容量はだいたい G 単位で設定する
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 再起動後も継続使用したい場合
sudo vi /etc/fstab # '/swapfile swap swap defaults 0 0' をこのファイルに挿入
# swap を無効化
sudo vi /etc/fstab # '/swapfile swap swap defaults 0 0' を削除
sudo swapoff -v /swapfile
sudo rm /swapfile
インスタンス性能や必要に応じてインスタンスが落ちないように、スワップメモリ設定を行うことができる。
2. Jenkins プラグインインストールエラー時の対処方法
<解決策 1>
Jenkins の管理 > Plugin Manager > Advanced settings > 更新サイトのパートにアクセス
https://updates.jenkins.io/update-center.json を http://updates.jenkins.io/update-center.json に変更
<解決策 2>
skip-certificate-check という名前のプラグインをインストール
完璧な解決策ではないが、これらの対策を全て適用することである程度効果が見られた。
締めくくりに
Docker 関連の内容は、後で扱う一部フレームワークのデプロイを除けば、ここでひと段落させる予定だ。それでも自分が作ったものを実際のネットワーク上で確認できるという達成感があるので、デプロイに関する内容は機会があれば今後ももう少し書いていきたい。
댓글 작성
게시글에 대한 의견을 남겨 주세요.