Docker 활용 및 배포 자동화 실습

dev-jjun·2023년 2월 26일
0

Server

목록 보기
6/33
post-thumbnail

Jupyter Notebook

Jupyter Notebook를 사용함으로써 ssh 접속을 통하지 않고도 바로 웹 브라우저에서 ec2 서버를 관리할 수 있다는 장점이 있어 사용한다.

Jupyter Notebook 설치 후 실행 과정

$ sudo apt-get install python3-pip
$ sudo pip3 install notebook

#python3 실행
$ python3
>> from notebook.auth import pwd
>> pwd()
# verify password 복사
$ jupyter notebook --generate-config
# 주피터 노트북 환경설정 파일 생성 후 나오는 루트 복사
$ sudo vi #복사한 경로

# 파일 마지막 부분에 붙여넣기 할 내용
c = get_config()
c.NotebookApp.password = u'복사한 verify password'
c.NotebookApp.ip = 'EC2 Private IPv4'
c.NotebookApp.notebook_dir = '/'

$ cd /home/ubuntu/.jupyter && jupyter notebook --config jupyter_notebook_config.py   # jupyter notebook (8888번 포트) 실행 

{public IPv4 주소}:8888로 접속 가능

# 백그라운드에서도 항상 실행 가능한 상태로 만들기
$ bg
$ disown -h

https 적용하기

보안 적용 이전 열어주었던 8888번 포트를 일단 닫아주기 위해 아래 명령어를 이용하자.

$ sudo netstat -nap | grep 8888
$ sudo kill -9 {포트가 실행중인 PID}

개인키와 공개키 ssl 인증서 발급

$ mkdir ssl
$ cd ssl

$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout "cert.key" -out "cert.pem" -batch
or
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem
  • 개인키(Private Key, 사설키) : cert.key
    • example
      -----BEGIN PRIVATE KEY-----
      
      -----END PRIVATE KEY-----
  • 공개키(Public Key) : cert.pem
    • example
      -----BEGIN PUBLIC KEY-----
      
      -----END PUBLIC KEY-----
# Jupyter Notebook config 파일에 아래 내용 추가
c.NotebookApp.certfile = u'/home/ubuntu/ssl/cert.pem'
c.NotebookApp.keyfile = u'/home/ubuntu/ssl/cert.key'

Openssl로 SSL 을 위한 인증서 발급하기 (HTTPS)

CSR 생성 (Certificate Signing Request - 인증서 서명 요청)

**$ openssl req -new -key private.key -out private.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]:KR
State or Province Name (full name) [Some-State]:Seoul
Locality Name (eg, city) []:Yongsan-gu
Organization Name (eg, company) [Internet Widgits Pty Ltd]:simya
Organizational Unit Name (eg, section) []:Server
Common Name (e.g. server FQDN or YOUR name) []:www.simya.com
Email Address []:jun20020216@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:test
An optional company name []:test
  • private.csr
    • example
      -----BEGIN CERTIFICATE REQUEST-----
      
      -----END CERTIFICATE REQUEST-----

CRT 인증서 만들기

$ openssl req -x509 -days 365 -key {private.key} -in {private.csr} -out {mycommoncrt.crt} -days 365
  • mycommoncrt.crt
    -----BEGIN CERTIFICATE-----
    
    
    -----END CERTIFICATE-----

Jupyter 서버를 항상 구동중인 상태로 만들기

서버 접속 이외에도 주피터 노트북을 통해 ec2에 접근할 수 있도록 Jupyter Notebook 자체를 시스템으로 등록해준다.

$ sudo vi /etc/systemd/system/jupyter.service

아래 내용으로 파일을 추가해준다.

[Unit]
Description=Jupyter Notebook Server

[Service]
Type=simple
User=ubuntu
ExecStart = /usr/bin/sudo /usr/local/bin/jupyter-notebook --allow-root --config=/home/ubuntu/.jupyter/jupyter_notebook_config.py

[Install]
WantedBy=multi-user.target
~
~
~
$ sudo systemctl daemon-reload
$ sudo systemctl enable jupyter
Created symlink /etc/systemd/system/multi-user.target.wants/jupyter.service → /etc/systemd/system/jupyter.service.

$ sudo systemctl start jupyter
$ sudo systemctl status jupyter
● jupyter.service - Jupyter Notebook Server
     Loaded: loaded (/etc/systemd/system/jupyter.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-01-30 15:29:24 UTC; 4s ago
   Main PID: 5345 (sudo)
      Tasks: 2 (limit: 1143)
     Memory: 48.1M
        CPU: 628ms
     CGroup: /system.slice/jupyter.service
             ├─5345 /usr/bin/sudo /usr/local/bin/jupyter-notebook --allow-root --config=/home/ubuntu/.jupyter/jupyter_n>
             └─5346 /usr/bin/python3 /usr/local/bin/jupyter-notebook --allow-root --config=/home/ubuntu/.jupyter/jupyte>

https://{Public IPv4 주소}:8888로 접속 시 아래와 같이 터미널을 실행시킬 수 있다.

Docker

기본적인 패키지 설치

Docker를 다운받기 위한 기본 환경을 셋팅해준다.

$ sudo apt update
$ sudo apt install apt-transport-https ca-certificates software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg |sudo apt-key add -
$ apt-cache policy docker-ce
$ sudo apt install docker-ce

Docker는 설치와 동시에 시스템 서비스로 자동 등록 되어 sudo systemctl status docker로 status를 확인해보면 정상적으로 구동중임을 알 수 있다.

Dockerfile을 만들어 서버에서 실행하기

Docker를 활용하여 APM 구축 - Apache

Docker 이미지에 올릴 서버의 종류, 개발자 정보, 자동으로 실행할 명령어 등을 작성해준다.

### Container 정보
FROM ubuntu:22.04
MAINTAINER yejunpark1 <jun20020216@gmail.com>   

### 수행할 명령
RUN apt-get update
RUN apt-get install -y apache2 
#install Apache web server (Only 'yes') -> 선택 옵션에 미리 응답

# Open HTTP Port
EXPOSE 80

### 명령 종료 후에도 항상 구동 중인 상태로 설정할 명령어
CMD ["apachectl", "-D", "FOREGROUND"]

아파치의 경우, 특정 컨테이너의 특정 명령을 수행한 이후에도 계속 구동된 상태로 유지되어야 하므로 CMD로 설정해준다.

$ docker build -t example .  #Docker 이미지 build 하기 (하나의 서버로서 Dockerfile에 지정해준 명령어를 모두 수행한다.)
$ docker images   #빌드된 내용 확인하기
$ docker run -p {내 서버의 포트}:{도커 컨테이너의 포트} 
  • 서버의 포트번호로 접속하면 컨테이너의 포트 번호로 연결되어 접속된다. 실제 접속 시에 **{IP주소}/{Domain}:포트번호** 에서 포트 번호에 해당하는 건 내 서버의 포트 이다! 80:80으로 80번(http) 포트에 접속해주면 아래와 같은 아파치 서버의 화면이 구동됨을 알 수 있다.
$ docker rm -f `docker ps -a -q`   # Docker Container 모두 종료 및 삭제

Docker를 활용하여 APM 구축 - PHP

Docker를 활용하여 APM(Apache-Php-Mysql)를 구축하기 위해 테스트로 작성했던 /home/ubuntu/example 경로에 저장된 Dockerfile을 수정해준다.

FROM ubuntu:22.04
MAINTAINER yejunpark1 <jun20020216@gmail.com>

# Avoding user interaction with tzdata
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update
RUN apt-get install -y apache2 #install Apache web server (Only 'yes')
RUN apt-get install -y software-properties-common
RUN add-apt-repository ppa:ondrej/php
RUN apt-get update
RUN apt-get install -y php5.6

# Open HTTP Port
EXPOSE 80

CMD ["apachectl", "-D", "FOREGROUND"]

특정 명령어의 경우, 선택 옵션을 응답받아야 하는 것들이 있다. 하지만 이들은 Docker의 이미지로 다운을 받기에 당장 응답이 불가능하다.

이는 ENV DEBIAN_FRONTEND=noninteractive 로 오류를 해결할 수 있다. 사용자의 응답을 일단 넘어간다는 의미인데, 이는 으로 이미지가 설치되므로 그냥 삭제해주자.

$ docker run -p 80:80 -v /home/ubuntu/example/html:/var/www/html example

php의 기본 경로인 /var/www/html을 띄워준다. 우분투 서버에 존재하는 파일을 연결시켜주기 위해 html 파일을 아래 내용으로 작성한 후, 다시 80번 포트로 접속하면 php 페이지가 뜨는 것을 확인할 수 있다.

<?php phpinfo(); ?>

Docker를 활용하여 APM 구축 - MySQL

# Docker 이미지 다운로드
$ docker run -d -p 9876:3306 -e MYSQL_ROOT_PASSWORD=password mysql:5.6

# 서버에도 MySQL 설치
$ sudo apt install mysql-client-core-5.7
$ sudo apt install mariadb-client-core-10.1

DockerHub에 존재하는 이미지를 run으로 실행하면 즉시 다운로드되며, 필요한 환경변수들을 넘겨주어 실행이 가능하다.

Bash 명령어로 MySQL 컨테이너에 접속할 수 있도록 아래 명령어를 실행한다.

$ docker exec -it f676e158713f /bin/bash  # 실제로 해당 컨테이너에 접속한 것과 같은 효과를 낼 수 있다. 
$ docker inspect {Container ID}  # 접속 가능한 IP 주소를 찾아 MySQL에 접속한다.
$ mysql -u root -p --host {IP주소} --port 3306

Docker Container는 언제든 사라지고 실행될 수 있으므로, 영구적으로 데이터를 보존해야 하는 MySQL은 일시적인 Container로 관리하지 않는다. 일반적으로는 이를 통해 MySQL을 구축하기 보다 AWS RDS와 같은 데이터베이스를 이용한다.

MySQL에 접속하여 유저와 데이터베이스를 만들어본 후, 포트는 9876번, IP는 EC2 공인IP, 생성한 유저와 비밀번호를 이용해 외부 접속 테스트를 해보면 잘 접속된 것을 확인할 수 있다.

*Docker Login 방법

$ docker login
username: {dockerhub 사용자명}
password: {dockerhub 비밀번호}

Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

여기서 password 부분에 dockerhub의 비밀번호를 그대로 입력하면 위와 같은 내용이 뜬다. 이외에도 로그인하는 방법이 한 가지 더 있는데, https://hub.docker.com/ 에 접속하여 [Account Settings] > [Security] > [New Access Token] 후 이 토큰을 비밀번호로 입력하면 된다. 이를 보안상 더 권장하는 것 같다.

$ docker login -u {username}
password: {Access Token}

PHP와 MySQL 연동

Dockerfile 수정 - 아래 패키지 설치 명령을 추가해준다.

# Connect PHP & MySQL
RUN apt-get install -y php5.6-mysql
 $ docker build -t example .
$ docker run -p 80:80 -v /home/ubuntu/example/html:/var/www/html example

index.php 파일을 아래 내용과 같이 php 문법을 이용하여 수정해준다. 이와 같이 내용이 많은 파일에 접근할 때, Jupyter Notebook을 사용하면 GUI로 수정이 가능하여 좀 더 편리하게 접근할 수 있다.

<?php 
    $conn = mysqli_connect(
        '{EC2 Public IP주소}', 
        '{usernmae}',
        '{password}',
        '{데이터베이스명}',
        '{포트번호}'
    );
    
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: ".mysqli_connect_error();
    }

    $sql = "SELECT VERSION()";
    $result = mysqli_query($conn, $sql);
    $row = mysqli_fetch_array($result);
    print_r($row["VERSION()"]);

?>

다시 http://{퍼블릭 IP}/ 리로딩했을 때, 버전이 뜨면 성공적으로 실행된 것이다.

AWS RDS

RDS 인스턴스를 생성한다. 여기서 생성한 DB명, 사용자이름, 비밀번호 등을 잘 기록해두자.

이를 바탕으로 이전에 작성했던 index.php 파일의 내용을 수정해줄 것이다.

$conn = mysqli_connect(
        '{RDS 엔드포인트}', 
        '{usernmae}',
        '{password}',
        '{데이터베이스명}',
        '{포트번호}'
    );

GitHub 자동화 관리

Dockerfile과 프로젝트의 소스코드를 같은 루트에 두는 것이 일반적이다

깃헙에 올라와있는 Dockerfile을 읽어 DockerHub에서 자동으로 빌드를 수행해주는 강력한 기능이 있다. 깃헙에 업로드만 해도 자동으로 빌드가 이루어지므로 자동화 되어 관리할 수 있는 것이다.

  • README.md 스크립트 작성하기

    Docker와 AWS EC2 를 이용하여 배포하기 위한 Test

    Installation

    cd /home
    git clone https://github.com/jun02160/docker-test
    cd docker-test
    

    Run

    # Login For Private Docker Repository
    docker login
    docker pull yejunpark1/docker-practice
    docker run -p 80:80 -v /home/docker-test/Project:/var/www/html yejunpark1/docker-practice
🐳 AWS EC2 내에 컨테이너에 Github과 연동된 Docker Hub의 이미지를 pull 받으면 바로 인스턴스 서버로 배포가 가능하다.

Jenkins

Jenkins를 사용하면 배포 자동화가 가능해진다. 소스코드를 push 하는 것만으로 도커에 자동적으로 pull 되어 빌드시킬 수 있었는데, 이를 조금 더 간편하게 해주는 툴이 바로 Jenkins이다.

Docker에서 Jenkins 이미지를 pull 받고 실행시킨다.

https://hub.docker.com/r/jenkins/jenkins → 이전과 바뀐 방식으로 공식 DockerHub 문서 참고하기

$ docker pull jenkins/jenkins:lts-jdk11
$ docker run -d -p 8080:8080 -p 50000:50000 --restart=on-failure -v /jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock --name jenkins -u root jenkins/jenkins:lts-jdk11
$ docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword   # jenkins 패스워드 조회
  • -d : 컨테이너를 데몬으로 띄운다.
  • -p : 컨테이너 외부와 내부 포트를 포워딩한다. {호스트포트}:{컨테이너포트}
  • -v /jenkins:/var/jenkins_home : 원래 도커 컨테이너의 데이터는 컨테이너 종료 시에 휘발된다. 이 데이터가 날아가지 않고 보존하기 위해서는 볼륨 마운트를 할 수 있다. 이 옵션을 사용하면 젠킨스 컨테이너의 /var/jenkins_home 이라는 디렉토리를 /jenkins와 마운트하여 데이터를 보존할 수 있다.
  • —name jenkins : 도커 컨테이너의 이름을 설정한다.
  • -u root : 컨테이너가 실행될 리눅스의 사용자 계정을 root로 명시한다.

*참고 자료 - https://hudi.blog/install-jenkins-with-docker-on-ec2/

docker-compose를 이용하여 위와 같은 복잡한 명령 없이 간편하게 도커 컨테이너를 실행할 수 있다.

version: "3"
services:
  jenkins:
    image: jenkins/jenkins:lts
    user: root
    volumes:
      - ./jenkins:/var/jenkins_home
    ports:
      - 8080:8080
$ sudo docker-compose up -d   # 컨테이너를 데몬으로 실행시킨다. 

Docker Proxy 설정하기

## Docker 서비스 데몬 폴더 생성
$ sudo mkdir -p /etc/systemd/system/docker.service.d

## 프록시 서버 설정파일 생성
$ sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf

## http-proxy.conf 파일 내용 작성
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"

## Docker 데몬 재시작
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker 

## 적용 결과 확인
$ sudo systemctl show --property=Environment docker

*참고 자료 - https://kim-dragon.tistory.com/255

프록시 파일 작성 시 가능한 NO_PROXY 옵션

  • IP 주소 프리픽스( 1.2.3.4)
  • 도메인 이름 또는 특수 DNS 레이블( * )
  • 도메인 이름은 해당 이름 및 모든 하위 도메인과 일치합니다. 선행 "."이 포함된 도메인 이름 하위 도메인만 일치합니다. 예를 들어 주어진 도인 foo.example.comexample.com:
    • [example.com](http://example.com) 일치 example.com foo.example.com , 및 .example.com 만 일치foo.example.com
  • 단일 별표( * )는 프록싱을 수행하지 않아야 함을 나타냅니다.
  • 리터럴 포트 번호는 IP 주소 접두사( 1.2.3.4:80 foo.example.com:80) 및 도메인 이름(foo.example.com:80 ) 에서 허용됩니다.

**만약 docker login 시 아래와 같은 에러가 발생한다면?

Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

/etc/resolv.conf 파일 내용을 수정하여 DNS를 8.8.8.8로 변경해주자!

$ sudo vi /etc/resolv.conf  # 아래 내용 추가
$ isudo vi /etc/resolv.conf

nameserver 8.8.8.8
nameserver 8.8.4.4

## Docker 데몬 재시작
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

127.0.0.53 → 8.8.8.8

*jenkins 실행 시 Public IP주소로 접속이 안된다면 프록시 설정을 바꾸어주면 된다.

Docker Insecure Registries

$ sudo vi /etc/docker/daemon.json

{ "insecure-registries" :
  ["myregistrydomain.com:5000"]
}

배포 자동화를 위한 플러그인 설치

이후, {public IPv4 주소}:8080 으로 접속하여 jenkins 로그인을 해준다.

$ docker logs {Container ID}  # 여기서 뜨는 비밀번호를 입력하여 로그인할 수 있다. 

→ Install suggested plugins (기본 플러그인 설치 항목 선택)

Docker 안에 Docker가 존재하는 DinD 방식을 사용할 것이므로 jenkins에 따로 Docker를 설치해줘야 한다. 이는 배포 자동화 시 Docker 프로젝트를 가져오고 실행시키는 과정에서 docker 명령어를 사용하기 위해 다운받아야 하는 것이다.

$ docker exec -it {Container ID} /bin/bash
$ curl -fsSLO https://get.docker.com/builds/Linux/x86_64/docker-17.04.0-ce.tgz   # Jenkins 내부에 docker 설치 - 압축파일 다운로드
$ tar xzvf docker-17.04.0-ce.tgz

$ mv docker/docker /usr/local/bin  # docker 명령어를 실행시킬 수 있도록 실행파일을 옮겨준다.
$ rm -r docker docer-17.04.0-ce.tgz  # 용량을 줄이기 위해 기존 압축파일은 삭제

$ docker login  # private repositor에 접근하기 위함
🐳 Jenkins를 사용하여 Docker project를 가져오고 자동으로 빌드, 즉 배포가 되도록 해준다.

원격으로 서버에서의 빌드 유발하기

Jenkins의 Execute shell에 빌드 시 자동 실행될 수 있는 명령어를 추가해준다.

cd backend
git pull

docker pull {사용자명}/{도커이미지명}
docker run -p 80:80 -v backend

그 다음, 빌드 유발에서 [빌드를 원격으로 유발]에 체크해주고 보안을 위해 Authentication Token을 어려운 문자열로 설정해준다. → 아래 원격 빌드를 위한 주소가 아래와 같이 뜨게된다. 이 주소로 접속만 해도 바로 빌드가 실행되어 더욱 편리하게 자동화가 되도록 하는 도구임을 알 수 있다.

JENKINS_URL/job/{jenkins item명}/build?token=TOKEN_NAME or /buildWithParameters?token=TOKEN_NAME

이후는 github 로그인 정보를 jenkins 컨테이너 내부에 기록하기 위한 과정이다.

$ docker exec -it {Container ID} /bin/bash

$ cd {github 프로젝트명}
$ git config --global credential.helper "cache --timeout 7200"  # 깃헙 계정정보를 젠킨스 컨테이너 내부에서 그대로 보여지도록 노출시키는 건 보안상으로 취약하므로, 이를 주기적으로 갱신시키도록 타이머를 설정해줘야 한다. (여기서는 7200s = 2시간으로 설정) 

## 또는 GitHub의 Access Token을 발급받아서 이를 직접 등록시켜주는 방식도 있다. -> 이 방식이 다른 개발자들에게 노출되지 않고 관리할 수 있어 조금 더 권장된다. 

*Github personal token으로 인증하는 방법

  1. GitHub Settings > Developer Setting > Personal Access Token

  2. 접근 범위를 아래와 같이 설정한 후에 토큰 생성 (반드시 저장해두기! 한 번만 볼 수 있음)

  3. Jenkins > Manage Jenkins > System Configure > [시스템 설정]

  4. +ADD 클릭하여 Credential 생성하기

    1. [kind] Secret Text
    2. [scope] Global(Jenkins, nodes, items, all child items, etc)
    3. [Secret] 위에 github에서 발급한 personal access token
    4. [ID] 자신의 github ID
  5. Test Connection 시, Credentials verified for user의 내용이 뜨면 성공!

*참고 자료 - https://bcho.tistory.com/1237

동빈나 YouTube 강의를 학습하며 기록한 내용입니다.

profile
서버 개발자를 꿈꾸며 성장하는 쭌입니다 😽

0개의 댓글