캐시(2) 눈물나는 레디스 적용기 그런데 EmbeddedRedis가 아닌 TestConatiner를 활용한 테스트 격리와 함께
저번 게시글에서 레디스를 도입하기로 결정을 했고 처음으로 적용해보았다. 그런데 테스트를 하는 도중에 테스트 격리가 실패를 했다. 레디스에서 테스트 격리를 위해 일주일간 고군분투한 경험을 기록하려고 작성한다.
테스트가 돌아가지 않는 코드는 믿을 수가 없다! 필자의 팀은 테스트 코드가 돌아가지 않는 코드를 ec2에 올리지 않는다. 처음 시도하는 것들이기에 테스트 코드가 보장되지 않는다면 신뢰할 수가 없기 때문이다. 따라서 redis를 적용하고 잘 동작하는지 테스트가 필요했다.
레디스를 적용 후, 로컬에서는 정상 동작했다. 하지만 ec2의 젠킨스에서 빌드가 할 때 오류가 발생하는 문제가 발생했고 결국 레디스를 제거하였다...🤦♂️
레디스 적용 후, 테스트하기
레디스를 적용한 후 테스트를 진행했다. 그런데 테스트를 진행하려면 로컬에서도 레디스를 띄워야 하는 불상사가 발생한다. (test 환경과 dev환경을 분리해야 하므로) 따라서 h2처럼 내장 레디스 서버를 두어 테스트시에 아무런 조치 없이 테스트를 진행할 수 있도록 해주고 싶었다.
해당 방식은 'redis test' 라는 키워드만 쳐도 볼 수 있는 baeldung이나 조졸두님의 https://jojoldu.tistory.com/297 블로그에서 참고할 수 있다. 그런데 여기서부터 불행이 시작되었다...!😭😭😭😭😭😭😭😭
1. Embedded Redis
내장 레디스로서 h2처럼 테스트시에만 내장으로 도는 레디스 서버라고 볼 수 있다. 여러 블로그에 있어서 잘 참고해서 적용을 했고 로컬에서도 정상적으로 동작이 되었다. 따라서 수월하게 pr을 날리고 dev에 머지를 하는 순간이었다.
그런데...! 자꾸 젠킨스에서 빌드가 실패한다. 원인을 보면 테스트에서 실패가 일어나는데 아래와 같은 로그가 계속 나온다.
구글링을 해보면 여러 자료들이 나왔고 시도해봤지만 효과는 없었다. 내장 레디스 버전도 바꿔보고 설정도 바꿔 봤지만 여전히 그대로였다.
우선 젠킨스의 문제인지 확인하기 위해 다른 ec2에서 테스트를 돌려봤다. 같은 에러가 나오면서 실패했다.
그렇다면 로컬에서는 돌아갔는데 ec2환경에서만 안된다는 것이 결론이었다. 그래서 환경을 살펴보기로 했다.
우아한테크코스에서 지원해준 ec2는 arm아키텍처 64bit였습니다. 반면에 저는 x86 아키텍처였습니다.
혹시...?
아키텍처에 따라서 지원을 안 할 수가 있나..?라는 생각을 가지고 라이브러리를 까 보기 시작했다. 😇😇
보란 듯이 지원하는 아키텍처에는 x86계열 뿐이었다. 해당 원인이 EmbeddedRedis가 로컬에서는 작동하지만 ec2환경에서는 작동하지 않는 이유라고 생각했다.
혹시 같은 문제를 경험한 사람이 있는지 확인하기 위해 ozimov/embedded-redis에 들어가서 이슈를 확인해봤다. 해당 이슈는 역시 있었고 21년 11월에 arm아키텍처에 대한 기능 추가 이슈가 있지만 아직까지 merge가 되지 않고 있었다.
원인을 발견하고 더이상 EmbeddedRedis를 활용해서는 테스트가 안 되겠구나.. 왜냐하면 우리의 ec2는 arm아키텍처로 돌아가기 때문에..라고 생각하고 테스트하기 위한 다른 방식을 찾아보기로 했다.
추가
Embedded Redis는 크게 kstyrc 와 ozimov두 개의 오픈소스가 있다. 각각은 18년과 20년 이후로 업데이트가 되고 있지 않다.. arm이 아직까지 지원이 안돼 있는 거 보면 해당 방식은 이제 사용하지 않을 것 같다.
https://github.com/kstyrc/embedded-redis/issues/127 해당 pr을 보면 m1 나온 지 얼마 안 되었을 때 이야기 같은데 m1의 arm은 지원하지 않으므로 testconatiner를 사용하라고 말해준다.
테스트 컨테이너
그래서 일주일을 EmbeddedRedis를 적용하려고 날리다가 테스트 컨테이너를 적용해보자고 마음을 먹었다.
사실 레디스 테스트를 찾아보다가 테스트 컨테이너를 보긴 했고 고려도 했었다. 그런데 해당 이유로 미뤄두었다.
- 로컬에서 도커를 켜 둬야 테스트가 성공한다. 테스트 시작하고 끝날 때, 컨테이너가 뜨고 꺼지기 때문이다.
- 속도가 느리다. 테스트할 때마다 컨테이너를 띄워야 하기 때문에 속도가 느리다.
하지만 어느 로컬에서든 격리된 환경에서 동작할 수 있다는 장점은 있었다. (EmbeddedRedis같은 경우는 로컬마다 메모리 크기가 다르면 maxMemoery를 설정해줘야 할 때도 있었다.)
어쨌든 ec2의 arm아키텍처에서도 돌리기 위해서(젠킨스에서 돌리기 위해서) 테스트 컨테이너를 활용하기로 결정했다.
적용하기
적용법은 간단하다. 로컬에서 도커만 띄워주고 있으면 된다.
build.gradle
// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// testcontainers
testImplementation "org.testcontainers:junit-jupiter:1.17.3"
RedisConatinerTest
package com.woowacourse.gongseek.support;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
public abstract class RedisContainerTest {
static final String REDIS_IMAGE = "redis:6-alpine";
static final GenericContainer<?> REDIS_CONTAINER;
static {
REDIS_CONTAINER = new GenericContainer<>(REDIS_IMAGE) // (1)
.withExposedPorts(6379) // (2)
.withReuse(true); // (3)
REDIS_CONTAINER.start(); // (4)
}
@DynamicPropertySource // (5)
public static void overrideProps(DynamicPropertyRegistry registry){
registry.add("spring.redis.host", REDIS_CONTAINER::getHost);
registry.add("spring.redis.port", () -> ""+REDIS_CONTAINER.getMappedPort(6379));
registry.add("spring.redis.password", () -> "password");
}
}
- (1): 도커에서 redis:6-alpine이미지를 pull해와서 띄운다.
- (2): 6379포트 번호를 노출시켜준다.
- (3): 컨테이너를 재사용할 수 있도록 한다.
- (4): container의 레디스 서버를 시작한다.
- (5): 동적으로 propertySource를 넣어준다.
- host는 컨테이너 호스트인 "localhost"를 반환한다.
- port는 무작위로 매핑된 포트는 컨테이너 시작 후 발생하므로 해당 메소드로 런타임 시 실제 포트를 검색할 수 있다.
- password는 임의의 password를 넣어줬다.
이후, 테스트를 돌려보면 아래처럼 정상적으로 컨테이너가 뜨는 것을 확인할 수 있다.
EC2에서 도커 설치
이제 로컬에서는 컨테이너가 정상 동작했으니 젠킨스의 ec2에 도커를 설치해줘야 한다. 도커가 있어야 컨테이너를 띄우기 때문이다. ec2에 도커 설치는 도커 공식문서를 참고했는데 문서화가 정말 잘되어있다고 느꼈다. 공식문서를 따라가면 될 것이다.
https://docs.docker.com/engine/install/ubuntu/#set-up-the-repository
위 문서를 참고해서 설치를 해주면 완료가 된다. 그런데 도커의 사용자에 ubuntu사용자가 없으므로 매번 sudo를 사용해야 한다. 따라서 설치만 한 상태에서 테스트를 돌리면 실행 권한이 없어서 컨테이너가 뜨지 않아 실패한다.
그래서 도커 실행 권한을 줘야 한다.
도커 사용자 추가하기
$ sudo usermod -aG docker $USER // 도커 그룹에 사용자 추가하기
$ sudo chmod 660 /var/run/docker.sock // 만약 위의 방식으로 사용자 추가해도 안된다면 이 명령어로 다른 사용자도 접근하게 해주자
$ sudo service docker restart
chmod에 조금 더 알고싶다면 >> https://giron.tistory.com/116
마무리
지금은 글을 쓰는 시점이어서 9일전이지만 실제는 레디스 테스트에 일주일 정도 걸린 것 같다. 레디스를 처음 적용해보면서 여러 시행착오가 있었는데 많은 경험을 할 수 있어서 힘들었지만 좋은 경험이라고 생각한다!
현재는 아래처럼 정상적으로 테스트가 CI환경에서도 작동하는 것을 볼 수 있다. 이렇게 해서 redis에서도 테스트 격리에 성공을 했다. 다만 이전 게시글에서 테스트 코드 최적화로 속도를 줄였지만 컨테이너를 띄우면서 속도가 다시 느려진 건 어쩔 수 없는 부분인가 보다..😥😥
[젠킨스 빌드 성공 사진]